@aws-blocks/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +174 -0
- package/dist/api.d.ts +147 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +10 -0
- package/dist/api.test.d.ts +2 -0
- package/dist/api.test.d.ts.map +1 -0
- package/dist/api.test.js +19 -0
- package/dist/bb-utils.d.ts +11 -0
- package/dist/bb-utils.d.ts.map +1 -0
- package/dist/bb-utils.js +12 -0
- package/dist/builtin-routes.d.ts +6 -0
- package/dist/builtin-routes.d.ts.map +1 -0
- package/dist/builtin-routes.js +57 -0
- package/dist/cdk/__fixtures__/factory-backend.d.ts +3 -0
- package/dist/cdk/__fixtures__/factory-backend.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/factory-backend.js +6 -0
- package/dist/cdk/__fixtures__/fullid-construct-backend.d.ts +2 -0
- package/dist/cdk/__fixtures__/fullid-construct-backend.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/fullid-construct-backend.js +37 -0
- package/dist/cdk/__fixtures__/handler.d.ts +5 -0
- package/dist/cdk/__fixtures__/handler.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/handler.js +3 -0
- package/dist/cdk/__fixtures__/side-effect-backend.d.ts +2 -0
- package/dist/cdk/__fixtures__/side-effect-backend.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/side-effect-backend.js +5 -0
- package/dist/cdk/blocks-backend.d.ts +68 -0
- package/dist/cdk/blocks-backend.d.ts.map +1 -0
- package/dist/cdk/blocks-backend.js +214 -0
- package/dist/cdk/blocks-backend.test.d.ts +2 -0
- package/dist/cdk/blocks-backend.test.d.ts.map +1 -0
- package/dist/cdk/blocks-backend.test.js +151 -0
- package/dist/cdk/blocks-stack.test.d.ts +2 -0
- package/dist/cdk/blocks-stack.test.d.ts.map +1 -0
- package/dist/cdk/blocks-stack.test.js +78 -0
- package/dist/cdk/config-registry.d.ts +26 -0
- package/dist/cdk/config-registry.d.ts.map +1 -0
- package/dist/cdk/config-registry.js +80 -0
- package/dist/cdk/index.d.ts +35 -0
- package/dist/cdk/index.d.ts.map +1 -0
- package/dist/cdk/index.js +90 -0
- package/dist/cdk/mixins.d.ts +28 -0
- package/dist/cdk/mixins.d.ts.map +1 -0
- package/dist/cdk/mixins.js +37 -0
- package/dist/cdk/mixins.test.d.ts +2 -0
- package/dist/cdk/mixins.test.d.ts.map +1 -0
- package/dist/cdk/mixins.test.js +108 -0
- package/dist/cdk/node-version.d.ts +14 -0
- package/dist/cdk/node-version.d.ts.map +1 -0
- package/dist/cdk/node-version.js +15 -0
- package/dist/cdk/stack-metadata.d.ts +12 -0
- package/dist/cdk/stack-metadata.d.ts.map +1 -0
- package/dist/cdk/stack-metadata.js +22 -0
- package/dist/cdk/stack-metadata.test.d.ts +2 -0
- package/dist/cdk/stack-metadata.test.d.ts.map +1 -0
- package/dist/cdk/stack-metadata.test.js +56 -0
- package/dist/cdk/synth-guard.d.ts +14 -0
- package/dist/cdk/synth-guard.d.ts.map +1 -0
- package/dist/cdk/synth-guard.js +20 -0
- package/dist/cdk/synth-guard.test.d.ts +2 -0
- package/dist/cdk/synth-guard.test.d.ts.map +1 -0
- package/dist/cdk/synth-guard.test.js +15 -0
- package/dist/client/index.d.ts +97 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +238 -0
- package/dist/client/index.test.d.ts +2 -0
- package/dist/client/index.test.d.ts.map +1 -0
- package/dist/client/index.test.js +113 -0
- package/dist/common/config.d.ts +45 -0
- package/dist/common/config.d.ts.map +1 -0
- package/dist/common/config.js +154 -0
- package/dist/common/config.test.d.ts +2 -0
- package/dist/common/config.test.d.ts.map +1 -0
- package/dist/common/config.test.js +173 -0
- package/dist/common/constants.d.ts +11 -0
- package/dist/common/constants.d.ts.map +1 -0
- package/dist/common/constants.js +12 -0
- package/dist/common/crypto.d.ts +6 -0
- package/dist/common/crypto.d.ts.map +1 -0
- package/dist/common/crypto.js +14 -0
- package/dist/common/index.d.ts +220 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +180 -0
- package/dist/common/index.test.d.ts +2 -0
- package/dist/common/index.test.d.ts.map +1 -0
- package/dist/common/index.test.js +195 -0
- package/dist/common/mock-data.d.ts +40 -0
- package/dist/common/mock-data.d.ts.map +1 -0
- package/dist/common/mock-data.js +39 -0
- package/dist/common/official-bb-names.generated.d.ts +7 -0
- package/dist/common/official-bb-names.generated.d.ts.map +1 -0
- package/dist/common/official-bb-names.generated.js +28 -0
- package/dist/common/sdk-registry.d.ts +136 -0
- package/dist/common/sdk-registry.d.ts.map +1 -0
- package/dist/common/sdk-registry.js +125 -0
- package/dist/constants.d.ts +27 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +28 -0
- package/dist/cors.d.ts +39 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +85 -0
- package/dist/cors.test.d.ts +2 -0
- package/dist/cors.test.d.ts.map +1 -0
- package/dist/cors.test.js +241 -0
- package/dist/db-naming.d.ts +21 -0
- package/dist/db-naming.d.ts.map +1 -0
- package/dist/db-naming.js +38 -0
- package/dist/db-naming.test.d.ts +2 -0
- package/dist/db-naming.test.d.ts.map +1 -0
- package/dist/db-naming.test.js +29 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/hosting.d.ts +258 -0
- package/dist/hosting.d.ts.map +1 -0
- package/dist/hosting.js +340 -0
- package/dist/hosting.test.d.ts +2 -0
- package/dist/hosting.test.d.ts.map +1 -0
- package/dist/hosting.test.js +1077 -0
- package/dist/index.cdk.d.ts +13 -0
- package/dist/index.cdk.d.ts.map +1 -0
- package/dist/index.cdk.js +13 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/lambda-handler.d.ts +131 -0
- package/dist/lambda-handler.d.ts.map +1 -0
- package/dist/lambda-handler.js +610 -0
- package/dist/lambda-handler.test.d.ts +2 -0
- package/dist/lambda-handler.test.d.ts.map +1 -0
- package/dist/lambda-handler.test.js +835 -0
- package/dist/pipeline/index.d.ts +4 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +3 -0
- package/dist/pipeline/pipeline-construct.d.ts +130 -0
- package/dist/pipeline/pipeline-construct.d.ts.map +1 -0
- package/dist/pipeline/pipeline-construct.js +484 -0
- package/dist/pipeline/pipeline.test.d.ts +2 -0
- package/dist/pipeline/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline/pipeline.test.js +1231 -0
- package/dist/pipeline/types.d.ts +351 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +3 -0
- package/dist/raw-route.cdk.d.ts +47 -0
- package/dist/raw-route.cdk.d.ts.map +1 -0
- package/dist/raw-route.cdk.js +51 -0
- package/dist/raw-route.d.ts +119 -0
- package/dist/raw-route.d.ts.map +1 -0
- package/dist/raw-route.js +239 -0
- package/dist/raw-route.mock.d.ts +47 -0
- package/dist/raw-route.mock.d.ts.map +1 -0
- package/dist/raw-route.mock.js +51 -0
- package/dist/raw-route.test.d.ts +2 -0
- package/dist/raw-route.test.d.ts.map +1 -0
- package/dist/raw-route.test.js +597 -0
- package/dist/redact.d.ts +51 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +134 -0
- package/dist/redact.test.d.ts +2 -0
- package/dist/redact.test.d.ts.map +1 -0
- package/dist/redact.test.js +199 -0
- package/dist/rpc.d.ts +54 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +127 -0
- package/dist/rpc.test.d.ts +2 -0
- package/dist/rpc.test.d.ts.map +1 -0
- package/dist/rpc.test.js +60 -0
- package/dist/scripts/cdk-telemetry-env.d.ts +13 -0
- package/dist/scripts/cdk-telemetry-env.d.ts.map +1 -0
- package/dist/scripts/cdk-telemetry-env.js +21 -0
- package/dist/scripts/cdk-telemetry-env.test.d.ts +2 -0
- package/dist/scripts/cdk-telemetry-env.test.d.ts.map +1 -0
- package/dist/scripts/cdk-telemetry-env.test.js +56 -0
- package/dist/scripts/cleanup.d.ts +3 -0
- package/dist/scripts/cleanup.d.ts.map +1 -0
- package/dist/scripts/cleanup.js +35 -0
- package/dist/scripts/console.d.ts +6 -0
- package/dist/scripts/console.d.ts.map +1 -0
- package/dist/scripts/console.js +25 -0
- package/dist/scripts/deploy.d.ts +6 -0
- package/dist/scripts/deploy.d.ts.map +1 -0
- package/dist/scripts/deploy.js +82 -0
- package/dist/scripts/destroy.d.ts +6 -0
- package/dist/scripts/destroy.d.ts.map +1 -0
- package/dist/scripts/destroy.js +30 -0
- package/dist/scripts/dev-server-cors.test.d.ts +2 -0
- package/dist/scripts/dev-server-cors.test.d.ts.map +1 -0
- package/dist/scripts/dev-server-cors.test.js +23 -0
- package/dist/scripts/dev-server-telemetry.test.d.ts +2 -0
- package/dist/scripts/dev-server-telemetry.test.d.ts.map +1 -0
- package/dist/scripts/dev-server-telemetry.test.js +113 -0
- package/dist/scripts/dev-server.d.ts +21 -0
- package/dist/scripts/dev-server.d.ts.map +1 -0
- package/dist/scripts/dev-server.js +456 -0
- package/dist/scripts/ensure-secrets.d.ts +31 -0
- package/dist/scripts/ensure-secrets.d.ts.map +1 -0
- package/dist/scripts/ensure-secrets.js +100 -0
- package/dist/scripts/ensure-secrets.test.d.ts +2 -0
- package/dist/scripts/ensure-secrets.test.d.ts.map +1 -0
- package/dist/scripts/ensure-secrets.test.js +44 -0
- package/dist/scripts/external-migrations-step.d.ts +62 -0
- package/dist/scripts/external-migrations-step.d.ts.map +1 -0
- package/dist/scripts/external-migrations-step.js +243 -0
- package/dist/scripts/external-migrations-step.test.d.ts +2 -0
- package/dist/scripts/external-migrations-step.test.d.ts.map +1 -0
- package/dist/scripts/external-migrations-step.test.js +47 -0
- package/dist/scripts/extract-ts-types.d.ts +65 -0
- package/dist/scripts/extract-ts-types.d.ts.map +1 -0
- package/dist/scripts/extract-ts-types.js +664 -0
- package/dist/scripts/extract-ts-types.test.d.ts +2 -0
- package/dist/scripts/extract-ts-types.test.d.ts.map +1 -0
- package/dist/scripts/extract-ts-types.test.js +497 -0
- package/dist/scripts/generate-client-worker.d.ts +2 -0
- package/dist/scripts/generate-client-worker.d.ts.map +1 -0
- package/dist/scripts/generate-client-worker.js +13 -0
- package/dist/scripts/generate-client.d.ts +14 -0
- package/dist/scripts/generate-client.d.ts.map +1 -0
- package/dist/scripts/generate-client.js +73 -0
- package/dist/scripts/generate-client.test.d.ts +2 -0
- package/dist/scripts/generate-client.test.d.ts.map +1 -0
- package/dist/scripts/generate-client.test.js +48 -0
- package/dist/scripts/generate-spec-cli.d.ts +3 -0
- package/dist/scripts/generate-spec-cli.d.ts.map +1 -0
- package/dist/scripts/generate-spec-cli.js +77 -0
- package/dist/scripts/generate-spec-cli.test.d.ts +14 -0
- package/dist/scripts/generate-spec-cli.test.d.ts.map +1 -0
- package/dist/scripts/generate-spec-cli.test.js +90 -0
- package/dist/scripts/generate-spec.d.ts +61 -0
- package/dist/scripts/generate-spec.d.ts.map +1 -0
- package/dist/scripts/generate-spec.js +746 -0
- package/dist/scripts/generate-spec.test.d.ts +12 -0
- package/dist/scripts/generate-spec.test.d.ts.map +1 -0
- package/dist/scripts/generate-spec.test.js +88 -0
- package/dist/scripts/index.d.ts +12 -0
- package/dist/scripts/index.d.ts.map +1 -0
- package/dist/scripts/index.js +13 -0
- package/dist/scripts/sandbox.d.ts +11 -0
- package/dist/scripts/sandbox.d.ts.map +1 -0
- package/dist/scripts/sandbox.js +200 -0
- package/dist/scripts/telemetry-cli.d.ts +3 -0
- package/dist/scripts/telemetry-cli.d.ts.map +1 -0
- package/dist/scripts/telemetry-cli.js +8 -0
- package/dist/scripts/telemetry.d.ts +17 -0
- package/dist/scripts/telemetry.d.ts.map +1 -0
- package/dist/scripts/telemetry.js +150 -0
- package/dist/scripts/telemetry.test.d.ts +2 -0
- package/dist/scripts/telemetry.test.d.ts.map +1 -0
- package/dist/scripts/telemetry.test.js +203 -0
- package/dist/scripts/validate-spec.d.ts +22 -0
- package/dist/scripts/validate-spec.d.ts.map +1 -0
- package/dist/scripts/validate-spec.js +159 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/withAuth.d.ts +142 -0
- package/dist/server/withAuth.d.ts.map +1 -0
- package/dist/server/withAuth.js +261 -0
- package/dist/server/withAuth.test.d.ts +2 -0
- package/dist/server/withAuth.test.d.ts.map +1 -0
- package/dist/server/withAuth.test.js +278 -0
- package/dist/telemetry/client.d.ts +59 -0
- package/dist/telemetry/client.d.ts.map +1 -0
- package/dist/telemetry/client.js +215 -0
- package/dist/telemetry/config-writer.d.ts +16 -0
- package/dist/telemetry/config-writer.d.ts.map +1 -0
- package/dist/telemetry/config-writer.js +36 -0
- package/dist/telemetry/consent-commands.test.d.ts +2 -0
- package/dist/telemetry/consent-commands.test.d.ts.map +1 -0
- package/dist/telemetry/consent-commands.test.js +161 -0
- package/dist/telemetry/consent.d.ts +60 -0
- package/dist/telemetry/consent.d.ts.map +1 -0
- package/dist/telemetry/consent.js +122 -0
- package/dist/telemetry/environment.d.ts +33 -0
- package/dist/telemetry/environment.d.ts.map +1 -0
- package/dist/telemetry/environment.js +87 -0
- package/dist/telemetry/identifiers.d.ts +20 -0
- package/dist/telemetry/identifiers.d.ts.map +1 -0
- package/dist/telemetry/identifiers.js +86 -0
- package/dist/telemetry/index.d.ts +6 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +4 -0
- package/dist/telemetry/telemetry.test.d.ts +2 -0
- package/dist/telemetry/telemetry.test.d.ts.map +1 -0
- package/dist/telemetry/telemetry.test.js +1131 -0
- package/dist/telemetry/trackCommand.d.ts +32 -0
- package/dist/telemetry/trackCommand.d.ts.map +1 -0
- package/dist/telemetry/trackCommand.js +102 -0
- package/dist/telemetry/types.d.ts +92 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/package.json +83 -0
- package/src/api.test.ts +25 -0
- package/src/api.ts +163 -0
- package/src/bb-utils.ts +13 -0
- package/src/builtin-routes.ts +62 -0
- package/src/cdk/__fixtures__/factory-backend.ts +8 -0
- package/src/cdk/__fixtures__/fullid-construct-backend.ts +42 -0
- package/src/cdk/__fixtures__/handler.ts +4 -0
- package/src/cdk/__fixtures__/side-effect-backend.ts +7 -0
- package/src/cdk/blocks-backend.test.ts +207 -0
- package/src/cdk/blocks-backend.ts +249 -0
- package/src/cdk/blocks-stack.test.ts +106 -0
- package/src/cdk/config-registry.ts +106 -0
- package/src/cdk/index.ts +115 -0
- package/src/cdk/mixins.test.ts +129 -0
- package/src/cdk/mixins.ts +41 -0
- package/src/cdk/node-version.ts +17 -0
- package/src/cdk/stack-metadata.test.ts +71 -0
- package/src/cdk/stack-metadata.ts +29 -0
- package/src/cdk/synth-guard.test.ts +21 -0
- package/src/cdk/synth-guard.ts +23 -0
- package/src/client/index.test.ts +118 -0
- package/src/client/index.ts +320 -0
- package/src/common/config.test.ts +236 -0
- package/src/common/config.ts +169 -0
- package/src/common/constants.ts +15 -0
- package/src/common/crypto.ts +15 -0
- package/src/common/index.test.ts +242 -0
- package/src/common/index.ts +335 -0
- package/src/common/mock-data.ts +52 -0
- package/src/common/official-bb-names.generated.ts +29 -0
- package/src/common/sdk-registry.ts +150 -0
- package/src/constants.ts +31 -0
- package/src/cors.test.ts +279 -0
- package/src/cors.ts +91 -0
- package/src/db-naming.test.ts +48 -0
- package/src/db-naming.ts +41 -0
- package/src/errors.ts +72 -0
- package/src/hosting.test.ts +1431 -0
- package/src/hosting.ts +626 -0
- package/src/index.cdk.ts +45 -0
- package/src/index.ts +22 -0
- package/src/lambda-handler.test.ts +1005 -0
- package/src/lambda-handler.ts +706 -0
- package/src/pipeline/index.ts +12 -0
- package/src/pipeline/pipeline-construct.ts +662 -0
- package/src/pipeline/pipeline.test.ts +1565 -0
- package/src/pipeline/types.ts +384 -0
- package/src/raw-route.cdk.ts +56 -0
- package/src/raw-route.mock.ts +56 -0
- package/src/raw-route.test.ts +819 -0
- package/src/raw-route.ts +316 -0
- package/src/redact.test.ts +222 -0
- package/src/redact.ts +143 -0
- package/src/rpc.test.ts +73 -0
- package/src/rpc.ts +174 -0
- package/src/scripts/cdk-telemetry-env.test.ts +75 -0
- package/src/scripts/cdk-telemetry-env.ts +23 -0
- package/src/scripts/cleanup.ts +39 -0
- package/src/scripts/console.ts +34 -0
- package/src/scripts/deploy.ts +106 -0
- package/src/scripts/destroy.ts +42 -0
- package/src/scripts/dev-server-cors.test.ts +28 -0
- package/src/scripts/dev-server-telemetry.test.ts +127 -0
- package/src/scripts/dev-server.ts +515 -0
- package/src/scripts/ensure-secrets.test.ts +50 -0
- package/src/scripts/ensure-secrets.ts +113 -0
- package/src/scripts/external-migrations-step.test.ts +64 -0
- package/src/scripts/external-migrations-step.ts +269 -0
- package/src/scripts/extract-ts-types.test.ts +516 -0
- package/src/scripts/extract-ts-types.ts +773 -0
- package/src/scripts/generate-client-worker.ts +15 -0
- package/src/scripts/generate-client.test.ts +69 -0
- package/src/scripts/generate-client.ts +86 -0
- package/src/scripts/generate-spec-cli.test.ts +106 -0
- package/src/scripts/generate-spec-cli.ts +92 -0
- package/src/scripts/generate-spec.test.ts +99 -0
- package/src/scripts/generate-spec.ts +833 -0
- package/src/scripts/index.ts +21 -0
- package/src/scripts/sandbox.ts +230 -0
- package/src/scripts/telemetry-cli.ts +10 -0
- package/src/scripts/telemetry.test.ts +239 -0
- package/src/scripts/telemetry.ts +173 -0
- package/src/scripts/validate-spec.ts +199 -0
- package/src/server/index.ts +5 -0
- package/src/server/withAuth.test.ts +348 -0
- package/src/server/withAuth.ts +323 -0
- package/src/telemetry/client.ts +226 -0
- package/src/telemetry/config-writer.ts +52 -0
- package/src/telemetry/consent-commands.test.ts +196 -0
- package/src/telemetry/consent.ts +154 -0
- package/src/telemetry/environment.ts +100 -0
- package/src/telemetry/identifiers.ts +94 -0
- package/src/telemetry/index.ts +11 -0
- package/src/telemetry/telemetry.test.ts +1343 -0
- package/src/telemetry/trackCommand.ts +110 -0
- package/src/telemetry/types.ts +92 -0
- package/src/types.ts +33 -0
- package/src/version.ts +2 -0
package/dist/cors.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a comma-separated CORS origin string into anchored RegExp patterns.
|
|
3
|
+
*
|
|
4
|
+
* Each entry is treated as a regex pattern:
|
|
5
|
+
* - If it starts with `^`, it's used as-is (already anchored).
|
|
6
|
+
* - Otherwise it's wrapped with `^...$` anchors.
|
|
7
|
+
* - If the resulting regex is invalid, the entry is escaped and matched literally.
|
|
8
|
+
*
|
|
9
|
+
* @param raw - Comma-separated CORS patterns (e.g. `"https://example\\.com,^https?://localhost(:\\d+)?$"`)
|
|
10
|
+
* @returns Array of anchored RegExp patterns
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseCorsPatterns(raw: string): RegExp[];
|
|
13
|
+
/**
|
|
14
|
+
* Get the lazily-computed CORS patterns from environment variables.
|
|
15
|
+
*
|
|
16
|
+
* Merges `CORS_ALLOWED_ORIGINS` and `CORS_HOSTING_ORIGINS` on first call,
|
|
17
|
+
* then caches the result. Returns `null` if no patterns are configured.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getCorsPatterns(): RegExp[] | null;
|
|
20
|
+
/**
|
|
21
|
+
* Check whether the given origin is allowed by the configured CORS patterns.
|
|
22
|
+
*
|
|
23
|
+
* @param origin - The `Origin` header value from the request
|
|
24
|
+
* @returns `true` if the origin matches at least one pattern, `false` otherwise
|
|
25
|
+
*/
|
|
26
|
+
export declare function isOriginAllowed(origin: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Build a 403 Forbidden response for cross-origin requests from disallowed origins.
|
|
29
|
+
*/
|
|
30
|
+
export declare function corsRejection(): {
|
|
31
|
+
statusCode: number;
|
|
32
|
+
headers: Record<string, string>;
|
|
33
|
+
body: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Reset the lazy CORS pattern cache. **For testing only.**
|
|
37
|
+
*/
|
|
38
|
+
export declare function _resetCorsPatterns(): void;
|
|
39
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAYvD;AAcD;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,GAAG,IAAI,CAcjD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMrG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
|
package/dist/cors.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Parse a comma-separated CORS origin string into anchored RegExp patterns.
|
|
5
|
+
*
|
|
6
|
+
* Each entry is treated as a regex pattern:
|
|
7
|
+
* - If it starts with `^`, it's used as-is (already anchored).
|
|
8
|
+
* - Otherwise it's wrapped with `^...$` anchors.
|
|
9
|
+
* - If the resulting regex is invalid, the entry is escaped and matched literally.
|
|
10
|
+
*
|
|
11
|
+
* @param raw - Comma-separated CORS patterns (e.g. `"https://example\\.com,^https?://localhost(:\\d+)?$"`)
|
|
12
|
+
* @returns Array of anchored RegExp patterns
|
|
13
|
+
*/
|
|
14
|
+
export function parseCorsPatterns(raw) {
|
|
15
|
+
return raw.split(',').map(p => p.trim()).filter(Boolean).map(pattern => {
|
|
16
|
+
try {
|
|
17
|
+
if (pattern.startsWith('^')) {
|
|
18
|
+
return new RegExp(pattern);
|
|
19
|
+
}
|
|
20
|
+
return new RegExp(`^${pattern}$`);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
24
|
+
return new RegExp(`^${escaped}$`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Lazily-computed regex patterns for CORS origin validation.
|
|
30
|
+
*
|
|
31
|
+
* Computed on first access (not at module load) so that S3 config values
|
|
32
|
+
* injected by `loadConfigToProcessEnv()` are available. Combines:
|
|
33
|
+
* - `CORS_ALLOWED_ORIGINS` — set as Lambda env var by blocks-backend in sandbox mode
|
|
34
|
+
* - `CORS_HOSTING_ORIGINS` — set from S3 config by the Hosting construct (CloudFront domain)
|
|
35
|
+
*
|
|
36
|
+
* The sentinel value `undefined` means "not yet computed".
|
|
37
|
+
*/
|
|
38
|
+
let _corsPatterns;
|
|
39
|
+
/**
|
|
40
|
+
* Get the lazily-computed CORS patterns from environment variables.
|
|
41
|
+
*
|
|
42
|
+
* Merges `CORS_ALLOWED_ORIGINS` and `CORS_HOSTING_ORIGINS` on first call,
|
|
43
|
+
* then caches the result. Returns `null` if no patterns are configured.
|
|
44
|
+
*/
|
|
45
|
+
export function getCorsPatterns() {
|
|
46
|
+
if (_corsPatterns !== undefined)
|
|
47
|
+
return _corsPatterns;
|
|
48
|
+
const envOrigins = process.env.CORS_ALLOWED_ORIGINS ?? '';
|
|
49
|
+
const hostingOrigins = process.env.CORS_HOSTING_ORIGINS ?? '';
|
|
50
|
+
const combined = [envOrigins, hostingOrigins].filter(Boolean).join(',');
|
|
51
|
+
if (!combined) {
|
|
52
|
+
_corsPatterns = null;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
_corsPatterns = parseCorsPatterns(combined);
|
|
56
|
+
return _corsPatterns;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check whether the given origin is allowed by the configured CORS patterns.
|
|
60
|
+
*
|
|
61
|
+
* @param origin - The `Origin` header value from the request
|
|
62
|
+
* @returns `true` if the origin matches at least one pattern, `false` otherwise
|
|
63
|
+
*/
|
|
64
|
+
export function isOriginAllowed(origin) {
|
|
65
|
+
const patterns = getCorsPatterns();
|
|
66
|
+
if (!origin || !patterns)
|
|
67
|
+
return false;
|
|
68
|
+
return patterns.some(re => re.test(origin));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build a 403 Forbidden response for cross-origin requests from disallowed origins.
|
|
72
|
+
*/
|
|
73
|
+
export function corsRejection() {
|
|
74
|
+
return {
|
|
75
|
+
statusCode: 403,
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({ error: 'Forbidden: cross-origin request rejected' }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Reset the lazy CORS pattern cache. **For testing only.**
|
|
82
|
+
*/
|
|
83
|
+
export function _resetCorsPatterns() {
|
|
84
|
+
_corsPatterns = undefined;
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.test.d.ts","sourceRoot":"","sources":["../src/cors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { parseCorsPatterns, _resetCorsPatterns } from './cors.js';
|
|
6
|
+
import { createLambdaHandler } from './lambda-handler.js';
|
|
7
|
+
import { clearRouteRegistry } from './raw-route.js';
|
|
8
|
+
// ── parseCorsPatterns unit tests ────────────────────────────────────────────
|
|
9
|
+
describe('parseCorsPatterns', () => {
|
|
10
|
+
it('returns anchored patterns from plain strings', () => {
|
|
11
|
+
const patterns = parseCorsPatterns('https://example\\.com');
|
|
12
|
+
assert.strictEqual(patterns.length, 1);
|
|
13
|
+
assert.ok(patterns[0].test('https://example.com'));
|
|
14
|
+
assert.ok(!patterns[0].test('https://example.com.evil.org'));
|
|
15
|
+
});
|
|
16
|
+
it('handles already-anchored patterns (starting with ^)', () => {
|
|
17
|
+
const patterns = parseCorsPatterns('^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$');
|
|
18
|
+
assert.strictEqual(patterns.length, 1);
|
|
19
|
+
assert.ok(patterns[0].test('http://localhost:3000'));
|
|
20
|
+
assert.ok(patterns[0].test('https://127.0.0.1:8080'));
|
|
21
|
+
assert.ok(!patterns[0].test('https://evil.com'));
|
|
22
|
+
});
|
|
23
|
+
it('handles multiple comma-separated patterns', () => {
|
|
24
|
+
const patterns = parseCorsPatterns('https://a\\.com,https://b\\.com');
|
|
25
|
+
assert.strictEqual(patterns.length, 2);
|
|
26
|
+
assert.ok(patterns[0].test('https://a.com'));
|
|
27
|
+
assert.ok(patterns[1].test('https://b.com'));
|
|
28
|
+
});
|
|
29
|
+
it('trims whitespace from patterns', () => {
|
|
30
|
+
const patterns = parseCorsPatterns(' https://a\\.com , https://b\\.com ');
|
|
31
|
+
assert.strictEqual(patterns.length, 2);
|
|
32
|
+
assert.ok(patterns[0].test('https://a.com'));
|
|
33
|
+
assert.ok(patterns[1].test('https://b.com'));
|
|
34
|
+
});
|
|
35
|
+
it('skips empty entries', () => {
|
|
36
|
+
const patterns = parseCorsPatterns('https://a\\.com,,,,https://b\\.com');
|
|
37
|
+
assert.strictEqual(patterns.length, 2);
|
|
38
|
+
});
|
|
39
|
+
it('handles invalid regex by escaping and matching literally', () => {
|
|
40
|
+
const patterns = parseCorsPatterns('https://[invalid');
|
|
41
|
+
assert.strictEqual(patterns.length, 1);
|
|
42
|
+
assert.ok(patterns[0].test('https://[invalid'));
|
|
43
|
+
assert.ok(!patterns[0].test('https://valid'));
|
|
44
|
+
});
|
|
45
|
+
it('wildcard .* allows all origins', () => {
|
|
46
|
+
const patterns = parseCorsPatterns('.*');
|
|
47
|
+
assert.strictEqual(patterns.length, 1);
|
|
48
|
+
assert.ok(patterns[0].test('https://anything.example.org'));
|
|
49
|
+
assert.ok(patterns[0].test('http://localhost:9999'));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
// ── isOriginAllowed + getCorsPatterns integration via handler ────────────────
|
|
53
|
+
// These tests exercise the full CORS flow through createLambdaHandler to
|
|
54
|
+
// verify origin validation, header injection, and rejection work end-to-end.
|
|
55
|
+
describe('createLambdaHandler — CORS origin validation', () => {
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
process.env.CORS_ALLOWED_ORIGINS = [
|
|
58
|
+
'https://myapp\\.com',
|
|
59
|
+
'^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$',
|
|
60
|
+
].join(',');
|
|
61
|
+
delete process.env.CORS_HOSTING_ORIGINS;
|
|
62
|
+
_resetCorsPatterns();
|
|
63
|
+
clearRouteRegistry();
|
|
64
|
+
});
|
|
65
|
+
function makeEvent(overrides = {}) {
|
|
66
|
+
return {
|
|
67
|
+
httpMethod: 'POST',
|
|
68
|
+
path: '/aws-blocks/api',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'api.echo', params: ['hello'], id: 1 }),
|
|
71
|
+
isBase64Encoded: false,
|
|
72
|
+
...overrides,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function invoke(backend, event) {
|
|
76
|
+
const handler = createLambdaHandler(async () => backend);
|
|
77
|
+
return handler(event);
|
|
78
|
+
}
|
|
79
|
+
const echoBackend = {
|
|
80
|
+
api: (_ctx) => ({
|
|
81
|
+
async echo(msg) { return { msg }; },
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
it('allows an origin that matches an exact pattern', async () => {
|
|
85
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
86
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://myapp.com' },
|
|
87
|
+
}));
|
|
88
|
+
assert.strictEqual(result.statusCode, 200);
|
|
89
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'https://myapp.com');
|
|
90
|
+
assert.strictEqual(result.headers['access-control-allow-credentials'], 'true');
|
|
91
|
+
});
|
|
92
|
+
it('allows an origin that matches a regex pattern', async () => {
|
|
93
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
94
|
+
headers: { 'Content-Type': 'application/json', origin: 'http://localhost:5173' },
|
|
95
|
+
}));
|
|
96
|
+
assert.strictEqual(result.statusCode, 200);
|
|
97
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'http://localhost:5173');
|
|
98
|
+
});
|
|
99
|
+
it('rejects an origin that does not match any pattern with 403', async () => {
|
|
100
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
101
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://evil.com' },
|
|
102
|
+
}));
|
|
103
|
+
assert.strictEqual(result.statusCode, 403);
|
|
104
|
+
const body = JSON.parse(result.body);
|
|
105
|
+
assert.strictEqual(body.error, 'Forbidden: cross-origin request rejected');
|
|
106
|
+
});
|
|
107
|
+
it('passes through when no origin header is present (same-origin / server-side)', async () => {
|
|
108
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
110
|
+
}));
|
|
111
|
+
assert.strictEqual(result.statusCode, 200);
|
|
112
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], undefined);
|
|
113
|
+
});
|
|
114
|
+
it('anchored pattern prevents partial-match bypass', async () => {
|
|
115
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
116
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://myapp.com.evil.org' },
|
|
117
|
+
}));
|
|
118
|
+
assert.strictEqual(result.statusCode, 403);
|
|
119
|
+
});
|
|
120
|
+
it('sets ACAO and credentials headers on allowed origin', async () => {
|
|
121
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
122
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://myapp.com' },
|
|
123
|
+
}));
|
|
124
|
+
assert.strictEqual(result.statusCode, 200);
|
|
125
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'https://myapp.com');
|
|
126
|
+
assert.strictEqual(result.headers['access-control-allow-credentials'], 'true');
|
|
127
|
+
});
|
|
128
|
+
it('OPTIONS preflight with allowed origin returns 200 with CORS headers', async () => {
|
|
129
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
130
|
+
httpMethod: 'OPTIONS',
|
|
131
|
+
path: '/aws-blocks/api',
|
|
132
|
+
headers: { origin: 'http://localhost:3000' },
|
|
133
|
+
body: null,
|
|
134
|
+
}));
|
|
135
|
+
assert.strictEqual(result.statusCode, 200);
|
|
136
|
+
assert.strictEqual(result.headers['Access-Control-Allow-Origin'], 'http://localhost:3000');
|
|
137
|
+
assert.strictEqual(result.headers['Access-Control-Allow-Credentials'], 'true');
|
|
138
|
+
assert.ok(result.headers['Access-Control-Allow-Methods']);
|
|
139
|
+
assert.ok(result.headers['Access-Control-Allow-Headers']);
|
|
140
|
+
assert.strictEqual(result.headers['Access-Control-Max-Age'], '86400');
|
|
141
|
+
});
|
|
142
|
+
it('OPTIONS preflight with rejected origin returns 403', async () => {
|
|
143
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
144
|
+
httpMethod: 'OPTIONS',
|
|
145
|
+
path: '/aws-blocks/api',
|
|
146
|
+
headers: { origin: 'https://evil.com' },
|
|
147
|
+
body: null,
|
|
148
|
+
}));
|
|
149
|
+
assert.strictEqual(result.statusCode, 403);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
// ── CORS wildcard pattern ───────────────────────────────────────────────────
|
|
153
|
+
describe('createLambdaHandler — CORS wildcard pattern (.*)', () => {
|
|
154
|
+
beforeEach(() => {
|
|
155
|
+
process.env.CORS_ALLOWED_ORIGINS = '.*';
|
|
156
|
+
delete process.env.CORS_HOSTING_ORIGINS;
|
|
157
|
+
_resetCorsPatterns();
|
|
158
|
+
clearRouteRegistry();
|
|
159
|
+
});
|
|
160
|
+
function makeEvent(overrides = {}) {
|
|
161
|
+
return {
|
|
162
|
+
httpMethod: 'POST',
|
|
163
|
+
path: '/aws-blocks/api',
|
|
164
|
+
headers: { 'Content-Type': 'application/json' },
|
|
165
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'api.echo', params: ['hello'], id: 1 }),
|
|
166
|
+
isBase64Encoded: false,
|
|
167
|
+
...overrides,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function invoke(backend, event) {
|
|
171
|
+
const handler = createLambdaHandler(async () => backend);
|
|
172
|
+
return handler(event);
|
|
173
|
+
}
|
|
174
|
+
const echoBackend = {
|
|
175
|
+
api: (_ctx) => ({
|
|
176
|
+
async echo(msg) { return { msg }; },
|
|
177
|
+
}),
|
|
178
|
+
};
|
|
179
|
+
it('allows any origin when pattern is .* (catch-all)', async () => {
|
|
180
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
181
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://anything.example.org' },
|
|
182
|
+
}));
|
|
183
|
+
assert.strictEqual(result.statusCode, 200);
|
|
184
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'https://anything.example.org');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
// ── CORS hosting origin merge ───────────────────────────────────────────────
|
|
188
|
+
describe('createLambdaHandler — CORS hosting origin merge', () => {
|
|
189
|
+
beforeEach(() => {
|
|
190
|
+
process.env.CORS_ALLOWED_ORIGINS = '^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$';
|
|
191
|
+
process.env.CORS_HOSTING_ORIGINS = 'https://d111111abcdef8\\.cloudfront\\.net';
|
|
192
|
+
_resetCorsPatterns();
|
|
193
|
+
clearRouteRegistry();
|
|
194
|
+
});
|
|
195
|
+
function makeEvent(overrides = {}) {
|
|
196
|
+
return {
|
|
197
|
+
httpMethod: 'POST',
|
|
198
|
+
path: '/aws-blocks/api',
|
|
199
|
+
headers: { 'Content-Type': 'application/json' },
|
|
200
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'api.echo', params: ['hello'], id: 1 }),
|
|
201
|
+
isBase64Encoded: false,
|
|
202
|
+
...overrides,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async function invoke(backend, event) {
|
|
206
|
+
const handler = createLambdaHandler(async () => backend);
|
|
207
|
+
return handler(event);
|
|
208
|
+
}
|
|
209
|
+
const echoBackend = {
|
|
210
|
+
api: (_ctx) => ({
|
|
211
|
+
async echo(msg) { return { msg }; },
|
|
212
|
+
}),
|
|
213
|
+
};
|
|
214
|
+
it('allows localhost origin from CORS_ALLOWED_ORIGINS env var', async () => {
|
|
215
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
216
|
+
headers: { 'Content-Type': 'application/json', origin: 'http://localhost:5173' },
|
|
217
|
+
}));
|
|
218
|
+
assert.strictEqual(result.statusCode, 200);
|
|
219
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'http://localhost:5173');
|
|
220
|
+
});
|
|
221
|
+
it('allows CloudFront origin from CORS_HOSTING_ORIGINS (S3 config)', async () => {
|
|
222
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
223
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://d111111abcdef8.cloudfront.net' },
|
|
224
|
+
}));
|
|
225
|
+
assert.strictEqual(result.statusCode, 200);
|
|
226
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'https://d111111abcdef8.cloudfront.net');
|
|
227
|
+
});
|
|
228
|
+
it('rejects origins not matching either source', async () => {
|
|
229
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
230
|
+
headers: { 'Content-Type': 'application/json', origin: 'https://evil.com' },
|
|
231
|
+
}));
|
|
232
|
+
assert.strictEqual(result.statusCode, 403);
|
|
233
|
+
});
|
|
234
|
+
it('allows 127.0.0.1 from the combined patterns', async () => {
|
|
235
|
+
const result = await invoke(echoBackend, makeEvent({
|
|
236
|
+
headers: { 'Content-Type': 'application/json', origin: 'http://127.0.0.1:8080' },
|
|
237
|
+
}));
|
|
238
|
+
assert.strictEqual(result.statusCode, 200);
|
|
239
|
+
assert.strictEqual(result.headers['access-control-allow-origin'], 'http://127.0.0.1:8080');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the SSM parameter name that stores an external
|
|
3
|
+
* database connection string, and for the project ref derived from a Postgres
|
|
4
|
+
* connection string.
|
|
5
|
+
*
|
|
6
|
+
* Both the deploy-time provisioner (`ensure-secrets`) and the `db pull`
|
|
7
|
+
* generated code derive this name from the stage alone. Keep this the only
|
|
8
|
+
* place the name is constructed.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Extract a stable identifier from a Postgres connection string.
|
|
12
|
+
*
|
|
13
|
+
* Maps the Supabase pooler form (`postgres.{ref}@`) and the direct form
|
|
14
|
+
* (`db.{ref}.supabase.co`) to the same `{ref}`, so a project's connection string
|
|
15
|
+
* yields one identifier regardless of which form the customer pastes. Falls back
|
|
16
|
+
* to a sanitized hostname for non-Supabase hosts.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractDbRef(connectionString: string): string;
|
|
19
|
+
/** SSM parameter name storing the connection string for a given stage. */
|
|
20
|
+
export declare function dbConnectionParameterName(stage: string): string;
|
|
21
|
+
//# sourceMappingURL=db-naming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-naming.d.ts","sourceRoot":"","sources":["../src/db-naming.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAc7D;AAED,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Single source of truth for the SSM parameter name that stores an external
|
|
5
|
+
* database connection string, and for the project ref derived from a Postgres
|
|
6
|
+
* connection string.
|
|
7
|
+
*
|
|
8
|
+
* Both the deploy-time provisioner (`ensure-secrets`) and the `db pull`
|
|
9
|
+
* generated code derive this name from the stage alone. Keep this the only
|
|
10
|
+
* place the name is constructed.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Extract a stable identifier from a Postgres connection string.
|
|
14
|
+
*
|
|
15
|
+
* Maps the Supabase pooler form (`postgres.{ref}@`) and the direct form
|
|
16
|
+
* (`db.{ref}.supabase.co`) to the same `{ref}`, so a project's connection string
|
|
17
|
+
* yields one identifier regardless of which form the customer pastes. Falls back
|
|
18
|
+
* to a sanitized hostname for non-Supabase hosts.
|
|
19
|
+
*/
|
|
20
|
+
export function extractDbRef(connectionString) {
|
|
21
|
+
// Supabase pooler: postgres.{ref}:pass@... or postgres.{ref}@...
|
|
22
|
+
const pooler = connectionString.match(/postgres\.([a-z0-9]+)[:@]/i);
|
|
23
|
+
if (pooler)
|
|
24
|
+
return pooler[1];
|
|
25
|
+
// Supabase direct: @db.{ref}.supabase.co
|
|
26
|
+
const direct = connectionString.match(/@db\.([a-z0-9]+)\.supabase\.co/i);
|
|
27
|
+
if (direct)
|
|
28
|
+
return direct[1];
|
|
29
|
+
// Generic host fallback
|
|
30
|
+
const host = connectionString.match(/@([^:/?]+)/);
|
|
31
|
+
if (host)
|
|
32
|
+
return host[1].replace(/\./g, '-');
|
|
33
|
+
throw new Error('Cannot extract database identifier from connection string.');
|
|
34
|
+
}
|
|
35
|
+
/** SSM parameter name storing the connection string for a given stage. */
|
|
36
|
+
export function dbConnectionParameterName(stage) {
|
|
37
|
+
return `/blocks/${stage}/db-connection-string`;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-naming.test.d.ts","sourceRoot":"","sources":["../src/db-naming.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { test, describe } from 'node:test';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { extractDbRef, dbConnectionParameterName } from './db-naming.js';
|
|
6
|
+
describe('extractDbRef', () => {
|
|
7
|
+
test('pooler form (postgres.{ref}@) yields ref', () => {
|
|
8
|
+
assert.strictEqual(extractDbRef('postgresql://postgres.abcdef:pw@aws-0-us-east-1.pooler.supabase.com:6543/postgres'), 'abcdef');
|
|
9
|
+
});
|
|
10
|
+
test('direct form (db.{ref}.supabase.co) yields the same ref', () => {
|
|
11
|
+
assert.strictEqual(extractDbRef('postgresql://postgres:pw@db.abcdef.supabase.co:5432/postgres'), 'abcdef');
|
|
12
|
+
});
|
|
13
|
+
test('pooler and direct forms of one project agree', () => {
|
|
14
|
+
const pooler = extractDbRef('postgresql://postgres.proj123:pw@aws-0-eu-west-2.pooler.supabase.com:5432/postgres');
|
|
15
|
+
const direct = extractDbRef('postgresql://postgres:pw@db.proj123.supabase.co:5432/postgres');
|
|
16
|
+
assert.strictEqual(pooler, direct);
|
|
17
|
+
});
|
|
18
|
+
test('non-Supabase host falls back to sanitized hostname', () => {
|
|
19
|
+
assert.strictEqual(extractDbRef('postgresql://user:pw@my.db.example.com:5432/app'), 'my-db-example-com');
|
|
20
|
+
});
|
|
21
|
+
test('throws when no host is present', () => {
|
|
22
|
+
assert.throws(() => extractDbRef('not-a-connection-string'));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('dbConnectionParameterName', () => {
|
|
26
|
+
test('composes the stage into the SSM name', () => {
|
|
27
|
+
assert.strictEqual(dbConnectionParameterName('production'), '/blocks/production/db-connection-string');
|
|
28
|
+
});
|
|
29
|
+
});
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error subclass for errors that cross the wire between server and client.
|
|
3
|
+
*
|
|
4
|
+
* Carries a `status` (HTTP status code) and sets `name` to the BB-level
|
|
5
|
+
* error name (e.g., `'ConditionalCheckFailedException'`). Both are
|
|
6
|
+
* serialized to the client. `cause` stays server-side.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // Backend: catch a BB error and re-throw with status
|
|
11
|
+
* try {
|
|
12
|
+
* await store.put(key, value, { ifNotExists: true });
|
|
13
|
+
* } catch (e: unknown) {
|
|
14
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
15
|
+
* throw new ApiError('Username already taken', 409, { name: e.name, cause: e });
|
|
16
|
+
* }
|
|
17
|
+
* throw e;
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* // Frontend: same isBlocksError works
|
|
21
|
+
* try {
|
|
22
|
+
* await api.createUser('alice', 'pass');
|
|
23
|
+
* } catch (e: unknown) {
|
|
24
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
25
|
+
* showMessage('Username already taken');
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare class ApiError extends Error {
|
|
31
|
+
/** HTTP status code. */
|
|
32
|
+
readonly status: number;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the caller can retry the same action without restarting the
|
|
35
|
+
* broader flow. Semantically meaningful for multi-step state machines
|
|
36
|
+
* like auth challenges: the same session token / envelope can be reused
|
|
37
|
+
* with a corrected input (wrong MFA code, wrong password on re-prompt)
|
|
38
|
+
* when `retriable === true`; non-retriable errors (expired session,
|
|
39
|
+
* tampered envelope, too-many-attempts lockouts) require restarting the
|
|
40
|
+
* flow. Defaults to `false` when unspecified.
|
|
41
|
+
*/
|
|
42
|
+
readonly retriable: boolean;
|
|
43
|
+
constructor(message: string, status: number, options?: {
|
|
44
|
+
name?: string;
|
|
45
|
+
cause?: unknown;
|
|
46
|
+
retriable?: boolean;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Type guard for narrowing `unknown` catch variables against BB error constants.
|
|
51
|
+
*
|
|
52
|
+
* Checks `error.name` — works identically on both server and client because
|
|
53
|
+
* `ApiError` reconstructed from the wire preserves the error name.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* catch (e: unknown) {
|
|
58
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
59
|
+
* // e is narrowed to Error & { name: 'ConditionalCheckFailedException' }
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function isBlocksError<N extends string>(e: unknown, name: N): e is Error & {
|
|
65
|
+
name: N;
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAM9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAE7F"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Error subclass for errors that cross the wire between server and client.
|
|
5
|
+
*
|
|
6
|
+
* Carries a `status` (HTTP status code) and sets `name` to the BB-level
|
|
7
|
+
* error name (e.g., `'ConditionalCheckFailedException'`). Both are
|
|
8
|
+
* serialized to the client. `cause` stays server-side.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // Backend: catch a BB error and re-throw with status
|
|
13
|
+
* try {
|
|
14
|
+
* await store.put(key, value, { ifNotExists: true });
|
|
15
|
+
* } catch (e: unknown) {
|
|
16
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
17
|
+
* throw new ApiError('Username already taken', 409, { name: e.name, cause: e });
|
|
18
|
+
* }
|
|
19
|
+
* throw e;
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* // Frontend: same isBlocksError works
|
|
23
|
+
* try {
|
|
24
|
+
* await api.createUser('alice', 'pass');
|
|
25
|
+
* } catch (e: unknown) {
|
|
26
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
27
|
+
* showMessage('Username already taken');
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class ApiError extends Error {
|
|
33
|
+
/** HTTP status code. */
|
|
34
|
+
status;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the caller can retry the same action without restarting the
|
|
37
|
+
* broader flow. Semantically meaningful for multi-step state machines
|
|
38
|
+
* like auth challenges: the same session token / envelope can be reused
|
|
39
|
+
* with a corrected input (wrong MFA code, wrong password on re-prompt)
|
|
40
|
+
* when `retriable === true`; non-retriable errors (expired session,
|
|
41
|
+
* tampered envelope, too-many-attempts lockouts) require restarting the
|
|
42
|
+
* flow. Defaults to `false` when unspecified.
|
|
43
|
+
*/
|
|
44
|
+
retriable;
|
|
45
|
+
constructor(message, status, options) {
|
|
46
|
+
super(message, options?.cause ? { cause: options.cause } : undefined);
|
|
47
|
+
this.name = options?.name ?? 'ApiError';
|
|
48
|
+
this.status = status;
|
|
49
|
+
this.retriable = options?.retriable ?? false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Type guard for narrowing `unknown` catch variables against BB error constants.
|
|
54
|
+
*
|
|
55
|
+
* Checks `error.name` — works identically on both server and client because
|
|
56
|
+
* `ApiError` reconstructed from the wire preserves the error name.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* catch (e: unknown) {
|
|
61
|
+
* if (isBlocksError(e, KVStoreErrors.ConditionalCheckFailed)) {
|
|
62
|
+
* // e is narrowed to Error & { name: 'ConditionalCheckFailedException' }
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function isBlocksError(e, name) {
|
|
68
|
+
return e instanceof Error && e.name === name;
|
|
69
|
+
}
|