Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
3dbb647
feat(cli-internal): add assess, refactor, and generate-new commands
iliapolo Mar 17, 2026
6873cfc
chore: fix test
iliapolo Mar 17, 2026
16846fa
test(cli-internal): replace null-as-any logger with noOpLogger helper
iliapolo Mar 17, 2026
317737f
feat(cli-internal): print validation report on failure
iliapolo Mar 17, 2026
c0a38cb
refactor(cli-internal): classify auth stacks by resource type
iliapolo Mar 17, 2026
ff63d5d
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 19, 2026
d309cc8
refactor(cli-internal): split auth refactorers and improve resource m…
iliapolo Mar 20, 2026
2d45f9e
revert(cli-internal): remove trailing-newline normalization from sani…
iliapolo Mar 20, 2026
e136a67
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 20, 2026
6a4bed3
chore: remove merge markers
iliapolo Mar 20, 2026
5cbbfa7
chore: cleanup
iliapolo Mar 20, 2026
db785ac
feat(cli-internal): group plan operations by resource
iliapolo Mar 21, 2026
8827079
fix(cli-internal): tweak plan resource group display
iliapolo Mar 21, 2026
5562032
feat(cli-internal): refine plan display and support UserPool Groups r…
iliapolo Mar 21, 2026
5811ef7
docs: add commit OOM prevention and scratch file cleanup
iliapolo Mar 21, 2026
28f8dd4
feat(cli-internal): add changeset preview and move table to refactor …
iliapolo Mar 21, 2026
9d2e090
fix(cli-internal): improve changeset report formatting
iliapolo Mar 21, 2026
9554e90
refactor(cli-internal): use cli-table3 for move table and fix changes…
iliapolo Mar 21, 2026
bee6cae
feat(cli-internal): validate stack updates via changeset during refac…
iliapolo Mar 21, 2026
e569cd2
test(cli-internal): fix gen2-migration refactor tests
iliapolo Mar 21, 2026
960e6e5
refactor(cli-internal): improve refactor workflow resilience
iliapolo Mar 22, 2026
c511745
refactor(cli-internal): add physicalResourceId to MoveMapping
iliapolo Mar 22, 2026
09d2be8
refactor(cli-internal): hoist computation out of callbacks and simpli…
iliapolo Mar 22, 2026
195f982
refactor(cli-internal): consolidate CFN operations into Cfn class
iliapolo Mar 22, 2026
e1a83f6
refactor(cli-internal): add SpinningLogger to Cfn class
iliapolo Mar 22, 2026
7d4374e
refactor(cli-internal): clean up refactorer operations
iliapolo Mar 22, 2026
5fd4d21
refactor(cli-internal): harden refactor workflow for multi-stack moves
iliapolo Mar 22, 2026
8e9b42b
refactor(cli-internal): improve refactor logging, remove caching, add…
iliapolo Mar 22, 2026
45878ff
fix(cli-internal): defer template resolution to execution time in ref…
iliapolo Mar 22, 2026
b2d1b05
refactor(cli-internal): move template manipulation into Cfn.refactor …
iliapolo Mar 22, 2026
2698a8f
refactor(cli-internal): use SDK ResourceMapping and strip all DependsOn
iliapolo Mar 22, 2026
aaaa376
fix(cli-internal): handle absent holding stacks and defer template fe…
iliapolo Mar 22, 2026
a41cf12
refactor(cli-internal): share Cfn instance, enable rollback resolutio…
iliapolo Mar 23, 2026
32091b1
test(cli-internal): update refactor tests for new interfaces
iliapolo Mar 23, 2026
3ba4939
fix(cli-internal): prevent test hangs with missing mocks and async fixes
iliapolo Mar 23, 2026
a66f7cc
test(cli-internal): fix all refactor tests — 17 suites, 92 tests pass
iliapolo Mar 23, 2026
ee86aa1
docs(cli-internal): update JSDoc for refactor workflow changes
iliapolo Mar 23, 2026
5545ea4
docs(cli-internal): fix remaining JSDoc inaccuracies
iliapolo Mar 23, 2026
cec05b7
test(cli-internal): make CloudFormation mock stateful with template map
iliapolo Mar 23, 2026
50ef166
test(cli-internal): reorganize refactor tests to mirror source structure
iliapolo Mar 23, 2026
8b3d080
chore: revert snapshots
iliapolo Mar 23, 2026
e5c5b13
fix(cli-internal): clone empty holding template to prevent cross-refa…
iliapolo Mar 23, 2026
1e72d2a
docs: add coding guideline for module-level mutable constants
iliapolo Mar 23, 2026
1525461
chore: geo codegen
Mar 23, 2026
98a01d3
chore(cli-internal): remove duplicate ResourceMapping, add dictionary…
iliapolo Mar 23, 2026
676324f
style(cli-internal): consistent property order in ResourceMapping obj…
iliapolo Mar 23, 2026
20a1610
chore: regen fitness tracker snapshots
iliapolo Mar 24, 2026
8c3aac1
chore: update snapshots
iliapolo Mar 24, 2026
8822035
refactor(cli-internal): simplify beforeMove/afterMove to take stack ID
iliapolo Mar 24, 2026
72fdab7
test(cli-internal): fix tests for beforeMove/afterMove signature change
iliapolo Mar 24, 2026
d398c77
chore: recapture snapshot
iliapolo Mar 24, 2026
6ffd821
chore: update snapshots
iliapolo Mar 24, 2026
7c2aedb
docs(cli-internal): update refactor.md for beforeMove/afterMove changes
iliapolo Mar 24, 2026
398db04
test(cli-internal): add buildResourceMappings edge case tests
iliapolo Mar 24, 2026
576a347
test(cli-internal): add targetLogicalId and match tests for all refac…
iliapolo Mar 24, 2026
b9e4ef7
docs(cli-internal): document resource mapping happy and unhappy paths
iliapolo Mar 24, 2026
e3d532d
fix(cli-internal): skip holding stack resources already present in be…
iliapolo Mar 24, 2026
c1f546f
feat(cli-internal): delete holding stack after rollback if only place…
iliapolo Mar 24, 2026
1733750
Merge remote-tracking branch 'origin/epolon/refactor-after-rollback-a…
Mar 24, 2026
7cc0ce0
chore: adding generate snapshots
Mar 24, 2026
7813ad6
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 24, 2026
0920d3e
chore: bring back discussions from gen2-migration
iliapolo Mar 24, 2026
84584cf
chore: update snapshots
iliapolo Mar 24, 2026
824c65f
chore: update package
Mar 24, 2026
d448d6b
fix(cli-internal): align tests with gen2-migration merge changes
iliapolo Mar 24, 2026
ca04573
Merge remote-tracking branch 'origin/epolon/refactor-after-rollback-a…
Mar 24, 2026
447de62
chore: pre-refactor snapshots
Mar 24, 2026
7b639f0
test(cli-internal): update store-locator snapshot for generate
Mar 24, 2026
79fdcd1
chore: update stack naming with geo prefix
Mar 25, 2026
b3b3ae7
fix(cli-internal): mark GeofenceCollection as unsupported for refactor
Mar 25, 2026
f3dc182
chore: update yarn.lock
Mar 25, 2026
9b28406
chore: update snapshot.ts
Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions .eslint-dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"captcha",
"cfn",
"cfnlambda",
"changeset",
"chatbot",
"chatbots",
"chdir",
Expand Down Expand Up @@ -330,9 +331,12 @@
"regenerator",
"regexes",
"rekognition",
"renderer",
"repo",
"reqheaders",
"resolvers",
"refactorer",
"refactorers",
"rethrow",
"retrier",
"rimraf",
Expand Down
12 changes: 9 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,24 @@ Verify your changes by following these guidelines:
- **Always** update the appropriate JSDoc strings in the code you change. Be concise.
- Do not create additional markdown files in the repository unless you are instructed explicitly to.
- Never commit `.ai-generated` files (`.commit-message.ai-generated.txt`, `.pr-body.ai-generated.md`, etc.) — they are gitignored and are only used as local scratch files.
- Commit your changes in git using a well-formed commit message following the Conventional Commits format. The message must start
with a type prefix (e.g., `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`) followed by a single sentence summary and no more
- Commit your changes in git using a well-formed commit message following the Conventional Commits format. The message must include
a scope when the change is scoped to a specific package: `type(scope): subject`. The scope is derived from the package's `name`
field in `package.json` with the `@aws-amplify/` prefix stripped. For example, `@aws-amplify/cli-internal` → `cli-internal`,
`@aws-amplify/amplify-prompts` → `amplify-prompts`. Valid scopes are enforced by commitlint via `commitlint.config.js`. The
message must start with a type prefix (e.g., `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`) followed by a single sentence summary and no more
than a few paragraphs explaining the change and your testing. After this explanation, place the prompt the user used to trigger this
work prefixed with a "Prompt: " after a single line consisting of '---'. Make sure there are no empty lines before or after this line.
Word wrap all paragraphs at 72 columns including the prompt. For the author of the commit, use the configured username in git with
' (AI)' appended and the user email. For example, `git commit --author="John Doe (AI) <john@bigco.com>" -m "docs: update configuration guide"`.
To avoid issues with multi-line commit messages, write the message to `.commit-message.ai-generated.txt` and use `-F`:

```bash
git commit --author="John Doe (AI) <john@bigco.com>" -F .commit-message.ai-generated.txt
NODE_OPTIONS="--max-old-space-size=8192" git commit --author="John Doe (AI) <john@bigco.com>" -F .commit-message.ai-generated.txt
```

Always set `NODE_OPTIONS="--max-old-space-size=8192"` when committing to prevent OOM failures in the lint-staged hook.
After a successful commit, delete the scratch file: `rm -f .commit-message.ai-generated.txt`.

- Since this repo has a commit hook that takes quite a long time to run, don't immediately commit every
change you were asked to do. Apply your judgment, if the diff is still fairly small just keep going.
Otherwise, ask the user if they want to commit or keep going.
Expand Down
37 changes: 37 additions & 0 deletions CODING_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,43 @@ funcGenerator.setAuthGenerator(authGenerator);

---

### Don't mutate module-level constant objects

`const` prevents reassignment but does not prevent mutation of the object's properties. A module-level `const` object that gets passed to a function and mutated there silently becomes shared mutable state — every subsequent caller sees the mutations from previous calls.

This is especially dangerous when the constant looks like a template or default value that multiple callers use as a starting point. The first caller mutates it, and the second caller unknowingly starts with the first caller's leftovers.

```typescript
// Bad — shared object mutated by callers
const EMPTY_TEMPLATE = { Resources: {} };

function createStack(resources: Record<string, Resource>) {
const template = EMPTY_TEMPLATE; // not a copy!
for (const [id, r] of Object.entries(resources)) {
template.Resources[id] = r; // mutates the shared constant
}
return template;
}

// Second call sees resources from the first call
createStack({ BucketA: bucket });
createStack({ BucketB: bucket }); // template now has both BucketA and BucketB
```

**Instead:** Clone the object before mutating it, or use a factory function that returns a fresh object each time.

```typescript
// Good — clone before use
const template = JSON.parse(JSON.stringify(EMPTY_TEMPLATE));

// Good — factory function
function emptyTemplate() {
return { Resources: {} };
}
```

---

### Prefer `const` over `let`

Avoid `let` when the code can be restructured to use `const` instead. Even when `let` is technically correct (the variable is reassigned), the reassignment pattern itself is often the problem — it usually means branching logic is being used to populate variables that are consumed later, making it hard to reason about what values they hold at any given point.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,6 @@
"authenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-ge-amplifyAuthauthenticatedU-NrnOsacJVeCr"
}
},
"DependsOn": [
"amplifyAuthIdentityPool3FDE84CC",
"amplifyAuthUserPoolAppClient2626C6F8"
],
"Metadata": {
"aws:cdk:path": "amplify-backendonly-gen2main-branch-8e0f260810/auth/amplifyAuth/IdentityPoolRoleAttachment"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@
"ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:amplify-backendonly-gen2main-branch-8e0f260810-storage0EC3F24A-1BTE7UH0TFE2I/CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
"BucketName": "amplify-backendonly-ge-backendonlycb1a13ab81664-hysxrjfwrstc"
},
"DependsOn": [
"backendonlycb1a13ab81664ecaa7d015068ab2d0165e0fagen2mainBucketPolicy14D90AB6"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete",
"Metadata": {
Expand Down Expand Up @@ -128,9 +125,6 @@
]
}
},
"DependsOn": [
"CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
],
"Metadata": {
"aws:cdk:path": "amplify-backendonly-gen2main-branch-8e0f260810/storage/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
"aws:asset:path": "asset.faa95a81ae7d7373f3e1f242268f904eb748d8d0fdd306e8a6fe515a1905a7d6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,7 @@
"RefreshToken": "days"
},
"UserPoolId": "us-east-1_1rvCNKN5B"
},
"DependsOn": [
"amplifyAuthUserPool4BA7F805"
]
}
},
"amplifyAuthUserPoolNativeAppClient79534448": {
"Type": "AWS::Cognito::UserPoolClient",
Expand All @@ -183,10 +180,7 @@
"RefreshToken": "days"
},
"UserPoolId": "us-east-1_1rvCNKN5B"
},
"DependsOn": [
"amplifyAuthUserPool4BA7F805"
]
}
},
"amplifyAuthIdentityPool3FDE84CC": {
"Type": "AWS::Cognito::IdentityPool",
Expand Down Expand Up @@ -241,10 +235,7 @@
"unauthenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-main-5e0fa-unauthRole",
"authenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-main-5e0fa-authRole"
}
},
"DependsOn": [
"amplifyAuthIdentityPool3FDE84CC"
]
}
}
},
"Conditions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthPublic"
},
"S3AuthProtectedPolicy": {
Expand Down Expand Up @@ -250,7 +249,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthProtected"
},
"S3AuthPrivatePolicy": {
Expand Down Expand Up @@ -287,7 +285,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthPrivate"
},
"S3AuthUploadPolicy": {
Expand Down Expand Up @@ -324,7 +321,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthUploads"
},
"S3GuestPublicPolicy": {
Expand Down Expand Up @@ -361,7 +357,6 @@
"amplify-backendonly-main-5e0fa-unauthRole"
]
},
"DependsOn": [],
"Condition": "CreateGuestPublic"
},
"S3AuthReadPolicy": {
Expand Down Expand Up @@ -416,7 +411,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "AuthReadAndList"
},
"S3GuestReadPolicy": {
Expand Down Expand Up @@ -469,7 +463,6 @@
"amplify-backendonly-main-5e0fa-unauthRole"
]
},
"DependsOn": [],
"Condition": "GuestReadAndList"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@
"ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:amplify-backendonly-gen2main-branch-8e0f260810-storage0EC3F24A-1BTE7UH0TFE2I/CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
"BucketName": "amplify-backendonly-ge-backendonlycb1a13ab81664-hysxrjfwrstc"
},
"DependsOn": [
"backendonlycb1a13ab81664ecaa7d015068ab2d0165e0fagen2mainBucketPolicy14D90AB6"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete",
"Metadata": {
Expand Down Expand Up @@ -128,9 +125,6 @@
]
}
},
"DependsOn": [
"CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
],
"Metadata": {
"aws:cdk:path": "amplify-backendonly-gen2main-branch-8e0f260810/storage/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
"aws:asset:path": "asset.faa95a81ae7d7373f3e1f242268f904eb748d8d0fdd306e8a6fe515a1905a7d6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,6 @@
"authenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-ge-amplifyAuthauthenticatedU-NrnOsacJVeCr"
}
},
"DependsOn": [
"amplifyAuthIdentityPool3FDE84CC",
"amplifyAuthUserPoolAppClient2626C6F8"
],
"Metadata": {
"aws:cdk:path": "amplify-backendonly-gen2main-branch-8e0f260810/auth/amplifyAuth/IdentityPoolRoleAttachment"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@
"ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:amplify-backendonly-gen2main-branch-8e0f260810-storage0EC3F24A-1BTE7UH0TFE2I/CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
"BucketName": "amplify-backendonly-ge-backendonlycb1a13ab81664-hysxrjfwrstc"
},
"DependsOn": [
"backendonlycb1a13ab81664ecaa7d015068ab2d0165e0fagen2mainBucketPolicy14D90AB6"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete",
"Metadata": {
Expand Down Expand Up @@ -200,9 +197,6 @@
]
}
},
"DependsOn": [
"CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
],
"Metadata": {
"aws:cdk:path": "amplify-backendonly-gen2main-branch-8e0f260810/storage/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
"aws:asset:path": "asset.faa95a81ae7d7373f3e1f242268f904eb748d8d0fdd306e8a6fe515a1905a7d6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,7 @@
"RefreshToken": "days"
},
"UserPoolId": "us-east-1_1rvCNKN5B"
},
"DependsOn": [
"UserPool"
]
}
},
"UserPoolClient": {
"Type": "AWS::Cognito::UserPoolClient",
Expand All @@ -192,10 +189,7 @@
"RefreshToken": "days"
},
"UserPoolId": "us-east-1_1rvCNKN5B"
},
"DependsOn": [
"UserPool"
]
}
},
"UserPoolClientRole": {
"Type": "AWS::IAM::Role",
Expand Down Expand Up @@ -288,10 +282,7 @@
"unauthenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-main-5e0fa-unauthRole",
"authenticated": "arn:aws:iam::123456789012:role/amplify-backendonly-main-5e0fa-authRole"
}
},
"DependsOn": [
"IdentityPool"
]
}
}
},
"Outputs": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthPublic"
},
"S3AuthProtectedPolicy": {
Expand Down Expand Up @@ -314,7 +313,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthProtected"
},
"S3AuthPrivatePolicy": {
Expand Down Expand Up @@ -351,7 +349,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthPrivate"
},
"S3AuthUploadPolicy": {
Expand Down Expand Up @@ -388,7 +385,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "CreateAuthUploads"
},
"S3GuestPublicPolicy": {
Expand Down Expand Up @@ -425,7 +421,6 @@
"amplify-backendonly-main-5e0fa-unauthRole"
]
},
"DependsOn": [],
"Condition": "CreateGuestPublic"
},
"S3AuthReadPolicy": {
Expand Down Expand Up @@ -480,7 +475,6 @@
"amplify-backendonly-main-5e0fa-authRole"
]
},
"DependsOn": [],
"Condition": "AuthReadAndList"
},
"S3GuestReadPolicy": {
Expand Down Expand Up @@ -533,7 +527,6 @@
"amplify-backendonly-main-5e0fa-unauthRole"
]
},
"DependsOn": [],
"Condition": "GuestReadAndList"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,6 @@
"authenticated": "arn:aws:iam::123456789012:role/amplify-discussions-ge-amplifyAuthauthenticatedU-fjzgnNdQ7Oe2"
}
},
"DependsOn": [
"amplifyAuthIdentityPool3FDE84CC",
"amplifyAuthUserPoolAppClient2626C6F8"
],
"Metadata": {
"aws:cdk:path": "amplify-discussions-gen2main-branch-a27e51c30a/auth/amplifyAuth/IdentityPoolRoleAttachment"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@
"ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:amplify-discussions-gen2main-branch-a27e51c30a-storage0EC3F24A-O1Q835F0QK5N/CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
"BucketName": "amplify-discussions-ge-discusavatarsc39a5gen2ma-tofeojsv2jx4"
},
"DependsOn": [
"discusavatarsc39a5gen2mainBucketPolicy73EB7DB2"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete",
"Metadata": {
Expand Down Expand Up @@ -128,9 +125,6 @@
]
}
},
"DependsOn": [
"CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
],
"Metadata": {
"aws:cdk:path": "amplify-discussions-gen2main-branch-a27e51c30a/storage/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler",
"aws:asset:path": "asset.faa95a81ae7d7373f3e1f242268f904eb748d8d0fdd306e8a6fe515a1905a7d6",
Expand Down
Loading
Loading