@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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { encodeRpcRequest, decodeRpcResponse } from '../rpc.js';
|
|
4
|
+
const IS_SSR = typeof window === 'undefined';
|
|
5
|
+
// SSR cookie forwarding: reads from a global AsyncLocalStorage that SSR
|
|
6
|
+
// frameworks populate with the inbound request's cookies. The store is
|
|
7
|
+
// accessed via a well-known global key so the client bundle never imports
|
|
8
|
+
// node:async_hooks (which would break browser/webpack builds).
|
|
9
|
+
function getSsrCookies() {
|
|
10
|
+
if (!IS_SSR)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (typeof globalThis === 'undefined')
|
|
13
|
+
return undefined;
|
|
14
|
+
const store = globalThis.__BLOCKS_REQUEST_COOKIES_STORE__;
|
|
15
|
+
if (!store || typeof store.getStore !== 'function')
|
|
16
|
+
return undefined;
|
|
17
|
+
return store.getStore();
|
|
18
|
+
}
|
|
19
|
+
let API_URL = null;
|
|
20
|
+
let apiUrlPromise = null;
|
|
21
|
+
async function getApiUrl() {
|
|
22
|
+
if (API_URL)
|
|
23
|
+
return API_URL;
|
|
24
|
+
if (apiUrlPromise)
|
|
25
|
+
return apiUrlPromise;
|
|
26
|
+
apiUrlPromise = resolveApiUrl().catch((err) => {
|
|
27
|
+
// Don't cache failures — config.json may not exist yet during startup
|
|
28
|
+
apiUrlPromise = null;
|
|
29
|
+
throw err;
|
|
30
|
+
});
|
|
31
|
+
return apiUrlPromise;
|
|
32
|
+
}
|
|
33
|
+
async function resolveApiUrl() {
|
|
34
|
+
if (API_URL)
|
|
35
|
+
return API_URL;
|
|
36
|
+
function isInvalidUrl(url) {
|
|
37
|
+
if (!url || typeof url !== 'string' || !url.trim())
|
|
38
|
+
return true;
|
|
39
|
+
if (url === 'undefined' || url.startsWith('undefined'))
|
|
40
|
+
return true;
|
|
41
|
+
// Relative URLs (e.g. '/aws-blocks/api') are valid for browser fetch
|
|
42
|
+
if (url.startsWith('/'))
|
|
43
|
+
return false;
|
|
44
|
+
try {
|
|
45
|
+
const parsed = new URL(url);
|
|
46
|
+
return parsed.hostname === 'undefined' ||
|
|
47
|
+
parsed.pathname === '/undefined' ||
|
|
48
|
+
parsed.pathname.startsWith('/undefined/');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function validateAndCache(url, source) {
|
|
55
|
+
if (!url || typeof url !== 'string' || isInvalidUrl(url)) {
|
|
56
|
+
throw new Error(`Blocks API URL is not configured (source: ${source}). Ensure BLOCKS_API_URL environment variable is set ` +
|
|
57
|
+
'or config.json is deployed. Run with --conditions=cdk during CDK synthesis.');
|
|
58
|
+
}
|
|
59
|
+
API_URL = url;
|
|
60
|
+
return url;
|
|
61
|
+
}
|
|
62
|
+
// 1. SSR Lambda: env vars injected by Hosting construct
|
|
63
|
+
if (typeof process !== 'undefined' && process.env?.BLOCKS_API_URL) {
|
|
64
|
+
const url = process.env.BLOCKS_API_URL;
|
|
65
|
+
// CDK tokens (e.g. ${Token[TOKEN.1228]}) are present at build time but
|
|
66
|
+
// only resolve at CloudFormation deploy time. If we detect them, the
|
|
67
|
+
// build is running inside `cdk synth` and the URL is not usable yet.
|
|
68
|
+
if (/\$\{Token\[/.test(url)) {
|
|
69
|
+
throw new Error('Blocks API URL contains unresolved CDK tokens. This usually means a ' +
|
|
70
|
+
'Server Component is being statically prerendered during `next build` ' +
|
|
71
|
+
'inside `cdk deploy`.\n' +
|
|
72
|
+
'Fix: add `export const dynamic = \'force-dynamic\';` to any page ' +
|
|
73
|
+
'that calls the Blocks API so Next.js skips prerendering it.');
|
|
74
|
+
}
|
|
75
|
+
const validated = validateAndCache(url, 'env BLOCKS_API_URL');
|
|
76
|
+
console.log('[Blocks] Using API (env BLOCKS_API_URL):', validated);
|
|
77
|
+
return validated;
|
|
78
|
+
}
|
|
79
|
+
// 2. SSR Lambda: full config as JSON env var
|
|
80
|
+
if (typeof process !== 'undefined' && process.env?.BLOCKS_CONFIG) {
|
|
81
|
+
try {
|
|
82
|
+
const config = JSON.parse(process.env.BLOCKS_CONFIG);
|
|
83
|
+
const validated = validateAndCache(config.apiUrl, 'env BLOCKS_CONFIG');
|
|
84
|
+
console.log('[Blocks] Using API (env BLOCKS_CONFIG):', validated);
|
|
85
|
+
return validated;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Malformed BLOCKS_CONFIG — fall through
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 3. Node.js: read config file from filesystem
|
|
92
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
93
|
+
try {
|
|
94
|
+
const fs = await import(/* webpackIgnore: true */ 'node:fs');
|
|
95
|
+
const config = JSON.parse(fs.readFileSync('.blocks-sandbox/config.json', 'utf-8'));
|
|
96
|
+
const validated = validateAndCache(config.apiUrl, 'config.json file');
|
|
97
|
+
console.log('[Blocks] Using API (config.json file):', validated);
|
|
98
|
+
return validated;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Config doesn't exist or not in Node.js
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 4. Browser: fetch config.json from hosting origin
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch('/.blocks-sandbox/config.json');
|
|
107
|
+
if (response.ok) {
|
|
108
|
+
const config = await response.json();
|
|
109
|
+
const validated = validateAndCache(config.apiUrl, 'config.json fetch');
|
|
110
|
+
console.log('[Blocks] Using API (config.json fetch):', validated);
|
|
111
|
+
return validated;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Config doesn't exist
|
|
116
|
+
}
|
|
117
|
+
// Fallback — no config found
|
|
118
|
+
throw new Error('Blocks API URL not configured. Ensure:\n' +
|
|
119
|
+
'1. You ran `npm run deploy` (deploys config.json)\n' +
|
|
120
|
+
'2. SSR Lambda has BLOCKS_API_URL env var, OR\n' +
|
|
121
|
+
'3. config.json exists at /.blocks-sandbox/config.json');
|
|
122
|
+
}
|
|
123
|
+
const middlewares = [];
|
|
124
|
+
/**
|
|
125
|
+
* Register client middleware for request/response processing.
|
|
126
|
+
*
|
|
127
|
+
* Building Block middleware packages call this on import to register
|
|
128
|
+
* hooks that run on every API call. Request hooks can inject headers
|
|
129
|
+
* (e.g., auth tokens). Response hooks hydrate `{ __blocks: '...' }`
|
|
130
|
+
* descriptors into live client-side objects (e.g., WebSocket channels).
|
|
131
|
+
*
|
|
132
|
+
* Client middleware uses a **self-registering** pattern: the middleware
|
|
133
|
+
* module imports this function and calls it as a side effect of being
|
|
134
|
+
* imported. The generated `client.js` simply includes
|
|
135
|
+
* `import 'bb-realtime/mock-middleware'` — no explicit registration
|
|
136
|
+
* code is needed. This is idiomatic for browser/bundler contexts where
|
|
137
|
+
* side-effect imports are standard (similar to CSS imports or polyfills).
|
|
138
|
+
*
|
|
139
|
+
* This differs intentionally from **dev server attachments**, which use
|
|
140
|
+
* an explicit `attach(server)` pattern because they need the HTTP server
|
|
141
|
+
* instance passed to them — something unavailable at import time.
|
|
142
|
+
*/
|
|
143
|
+
export function registerMiddleware(middleware) {
|
|
144
|
+
middlewares.push(middleware);
|
|
145
|
+
}
|
|
146
|
+
async function processRequest(request) {
|
|
147
|
+
for (const mw of middlewares) {
|
|
148
|
+
if (mw.onRequest) {
|
|
149
|
+
const result = await mw.onRequest(request);
|
|
150
|
+
if (result)
|
|
151
|
+
request = result;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return request;
|
|
155
|
+
}
|
|
156
|
+
function processResponse(data) {
|
|
157
|
+
for (const mw of middlewares) {
|
|
158
|
+
if (mw.onResponse) {
|
|
159
|
+
data = mw.onResponse(data);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return data;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Client-side API proxy. Creates a typed proxy that sends method calls
|
|
166
|
+
* to the backend API over HTTP and hydrates Transferable descriptors
|
|
167
|
+
* in responses via registered middleware.
|
|
168
|
+
*
|
|
169
|
+
* This is the browser counterpart of the server-side `ApiNamespace` in
|
|
170
|
+
* `core/api.ts`. The generated `client.js` emits one of these per
|
|
171
|
+
* API namespace exported from the backend.
|
|
172
|
+
*
|
|
173
|
+
* ```typescript
|
|
174
|
+
* // Generated client.js
|
|
175
|
+
* import { ApiNamespaceClient } from '@aws-blocks/core/client';
|
|
176
|
+
* export const api = ApiNamespaceClient('api');
|
|
177
|
+
*
|
|
178
|
+
* // Frontend usage — fully typed via the 'aws-blocks' types export
|
|
179
|
+
* import { api } from 'aws-blocks';
|
|
180
|
+
* const user = await api.getUser('123');
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export function ApiNamespaceClient(name, options) {
|
|
184
|
+
const urlOverride = options?.url;
|
|
185
|
+
return new Proxy({}, {
|
|
186
|
+
get(target, method) {
|
|
187
|
+
if (typeof method === 'symbol')
|
|
188
|
+
return undefined;
|
|
189
|
+
return async (...args) => {
|
|
190
|
+
const apiUrl = urlOverride ?? await getApiUrl();
|
|
191
|
+
let request = {
|
|
192
|
+
apiNamespace: name,
|
|
193
|
+
method,
|
|
194
|
+
args,
|
|
195
|
+
headers: { 'Content-Type': 'application/json' },
|
|
196
|
+
};
|
|
197
|
+
request = await processRequest(request);
|
|
198
|
+
// SSR cookie forwarding: when running server-side, forward the
|
|
199
|
+
// inbound request cookies so auth sessions propagate to API calls.
|
|
200
|
+
// Deduplicates by cookie name — existing cookies (from middleware)
|
|
201
|
+
// take precedence over SSR-forwarded ones.
|
|
202
|
+
const ssrCookies = getSsrCookies();
|
|
203
|
+
if (ssrCookies) {
|
|
204
|
+
const existingKey = 'Cookie' in request.headers ? 'Cookie' : 'cookie' in request.headers ? 'cookie' : null;
|
|
205
|
+
const existing = existingKey ? request.headers[existingKey] : undefined;
|
|
206
|
+
if (existingKey && existingKey !== 'Cookie') {
|
|
207
|
+
delete request.headers[existingKey];
|
|
208
|
+
}
|
|
209
|
+
if (existing) {
|
|
210
|
+
const existingNames = new Set(existing.split(';').filter(Boolean).map((c) => c.trim().split('=')[0]));
|
|
211
|
+
const newCookies = ssrCookies
|
|
212
|
+
.split(';')
|
|
213
|
+
.filter(Boolean)
|
|
214
|
+
.filter((c) => !existingNames.has(c.trim().split('=')[0]))
|
|
215
|
+
.join('; ');
|
|
216
|
+
request.headers['Cookie'] = newCookies
|
|
217
|
+
? `${existing}; ${newCookies}`
|
|
218
|
+
: existing;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
request.headers['Cookie'] = ssrCookies;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const response = await fetch(apiUrl, {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
headers: request.headers,
|
|
227
|
+
credentials: 'include',
|
|
228
|
+
body: encodeRpcRequest(request.apiNamespace, request.method, request.args),
|
|
229
|
+
});
|
|
230
|
+
const rpcBody = await response.json();
|
|
231
|
+
const result = decodeRpcResponse(rpcBody); // throws ApiError on RPC error
|
|
232
|
+
return processResponse(result);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
export { ApiError, isBlocksError } from '../errors.js';
|
|
238
|
+
export { Scope } from '../common/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/client/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import { writeFileSync, unlinkSync, mkdtempSync } from 'node:fs';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
/**
|
|
11
|
+
* Tests for the client's URL resolution defensive guard (issue #730).
|
|
12
|
+
*
|
|
13
|
+
* The client module caches API_URL globally, so each test spawns a
|
|
14
|
+
* subprocess with appropriate env vars to get a fresh module instance.
|
|
15
|
+
*/
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const CLIENT_MODULE = join(__dirname, '..', 'client', 'index.js');
|
|
18
|
+
function runScript(scriptBody, env) {
|
|
19
|
+
const tmp = mkdtempSync(join(tmpdir(), 'blocks-client-test-'));
|
|
20
|
+
const scriptPath = join(tmp, 'test.mjs');
|
|
21
|
+
// Use absolute path to import the client module
|
|
22
|
+
const importPath = `file://${CLIENT_MODULE}`;
|
|
23
|
+
const fullScript = `import { ApiNamespaceClient } from '${importPath}';\n${scriptBody}`;
|
|
24
|
+
writeFileSync(scriptPath, fullScript);
|
|
25
|
+
try {
|
|
26
|
+
return execSync(`node ${scriptPath}`, {
|
|
27
|
+
encoding: 'utf-8',
|
|
28
|
+
env: { PATH: process.env.PATH, HOME: process.env.HOME, ...env },
|
|
29
|
+
timeout: 10000,
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
try {
|
|
34
|
+
unlinkSync(scriptPath);
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
describe('Client URL validation (issue #730)', () => {
|
|
40
|
+
it('ApiNamespaceClient with explicit url option containing "undefined" does NOT throw on creation', async () => {
|
|
41
|
+
const { ApiNamespaceClient } = await import('./index.js');
|
|
42
|
+
const client = ApiNamespaceClient('test', { url: 'https://undefined.example.com' });
|
|
43
|
+
assert.ok(client, 'Should create client proxy');
|
|
44
|
+
});
|
|
45
|
+
it('resolveApiUrl rejects when BLOCKS_API_URL contains "undefined"', () => {
|
|
46
|
+
const result = runScript(`
|
|
47
|
+
const api = ApiNamespaceClient('test');
|
|
48
|
+
try {
|
|
49
|
+
await api.hello();
|
|
50
|
+
console.log('FAIL:no_error');
|
|
51
|
+
} catch (e) {
|
|
52
|
+
if (e.message.includes('Blocks API URL is not configured')) {
|
|
53
|
+
console.log('PASS');
|
|
54
|
+
} else {
|
|
55
|
+
console.log('FAIL:' + e.message);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
`, { BLOCKS_API_URL: 'https://undefined/api' });
|
|
59
|
+
assert.ok(result.includes('PASS'), `Expected PASS, got: ${result}`);
|
|
60
|
+
});
|
|
61
|
+
it('resolveApiUrl rejects when BLOCKS_CONFIG has missing apiUrl', () => {
|
|
62
|
+
const result = runScript(`
|
|
63
|
+
const api = ApiNamespaceClient('test');
|
|
64
|
+
try {
|
|
65
|
+
await api.hello();
|
|
66
|
+
console.log('FAIL:no_error');
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (e.message.includes('Blocks API URL is not configured') || e.message.includes('Blocks API URL not configured')) {
|
|
69
|
+
console.log('PASS');
|
|
70
|
+
} else {
|
|
71
|
+
console.log('FAIL:' + e.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`, { BLOCKS_CONFIG: JSON.stringify({ region: 'us-east-1' }) });
|
|
75
|
+
assert.ok(result.includes('PASS'), `Expected PASS, got: ${result}`);
|
|
76
|
+
});
|
|
77
|
+
it('resolveApiUrl succeeds with valid BLOCKS_API_URL', () => {
|
|
78
|
+
const result = runScript(`
|
|
79
|
+
const api = ApiNamespaceClient('test');
|
|
80
|
+
try {
|
|
81
|
+
await api.hello();
|
|
82
|
+
console.log('FAIL:no_error');
|
|
83
|
+
} catch (e) {
|
|
84
|
+
if (e.message.includes('fetch') || e.message.includes('ENOTFOUND') || e.message.includes('getaddrinfo')) {
|
|
85
|
+
console.log('PASS:url_resolved_fetch_failed');
|
|
86
|
+
} else if (e.message.includes('Blocks API URL')) {
|
|
87
|
+
console.log('FAIL:url_rejected_valid');
|
|
88
|
+
} else {
|
|
89
|
+
console.log('PASS:other');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
`, { BLOCKS_API_URL: 'https://abc123.execute-api.us-east-1.amazonaws.com/prod/aws-blocks' });
|
|
93
|
+
assert.ok(result.includes('PASS'), `Expected PASS, got: ${result}`);
|
|
94
|
+
});
|
|
95
|
+
it('resolveApiUrl accepts relative URLs (e.g. /aws-blocks/api for SPA hosting)', () => {
|
|
96
|
+
const result = runScript(`
|
|
97
|
+
const api = ApiNamespaceClient('test');
|
|
98
|
+
try {
|
|
99
|
+
await api.hello();
|
|
100
|
+
console.log('FAIL:no_error');
|
|
101
|
+
} catch (e) {
|
|
102
|
+
if (e.message.includes('fetch') || e.message.includes('ENOTFOUND') || e.message.includes('getaddrinfo') || e.message.includes('Invalid URL')) {
|
|
103
|
+
console.log('PASS:url_resolved_fetch_failed');
|
|
104
|
+
} else if (e.message.includes('Blocks API URL')) {
|
|
105
|
+
console.log('FAIL:url_rejected_relative');
|
|
106
|
+
} else {
|
|
107
|
+
console.log('PASS:other');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`, { BLOCKS_API_URL: '/aws-blocks/api' });
|
|
111
|
+
assert.ok(result.includes('PASS'), `Expected PASS, got: ${result}`);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a config value by key.
|
|
3
|
+
*
|
|
4
|
+
* Checks `process.env[key]` first (local dev where env vars are set directly).
|
|
5
|
+
* If not found, reads from the cached S3 config file.
|
|
6
|
+
*
|
|
7
|
+
* @param key - The config key (e.g., `BLOCKS_AUTH_COGNITO_MYAPP_AUTHC_USER_POOL_ID`)
|
|
8
|
+
* @returns The config value, or empty string if not found anywhere
|
|
9
|
+
*/
|
|
10
|
+
export declare function getConfig(key: string): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Synchronous config access — returns the value if already cached, otherwise
|
|
13
|
+
* returns the process.env fallback. Use this only when you're certain the
|
|
14
|
+
* config has already been loaded (e.g., after an initial await getConfig() call).
|
|
15
|
+
*
|
|
16
|
+
* @param key - The config key
|
|
17
|
+
* @returns The config value, or empty string if not found
|
|
18
|
+
*/
|
|
19
|
+
export declare function getConfigSync(key: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Preload the S3 config. Call this early in Lambda cold start to ensure
|
|
22
|
+
* the config is cached before any BB constructors need it.
|
|
23
|
+
*
|
|
24
|
+
* This is a no-op if the config is already loaded or if S3 env vars aren't set.
|
|
25
|
+
*/
|
|
26
|
+
export declare function preloadConfig(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Load S3 config and inject all values into `process.env`.
|
|
29
|
+
*
|
|
30
|
+
* This enables the lazy-init handler pattern: the handler loads config into
|
|
31
|
+
* process.env BEFORE importing the backend module, so BB constructors can
|
|
32
|
+
* read their config via `process.env[key]` as before — no BB code changes needed.
|
|
33
|
+
*
|
|
34
|
+
* Only sets env vars that aren't already set (won't override explicit env vars).
|
|
35
|
+
*/
|
|
36
|
+
export declare function loadConfigToProcessEnv(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Reset the config cache. **For testing only.**
|
|
39
|
+
*/
|
|
40
|
+
export declare function _resetConfigCache(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Override the S3 fetcher function. **For testing only.**
|
|
43
|
+
*/
|
|
44
|
+
export declare function _setS3Fetcher(fetcher: ((bucket: string, key: string) => Promise<string>) | null): void;
|
|
45
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/common/config.ts"],"names":[],"mappings":"AA4FA;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnD;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAO5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAEtG"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Runtime config loader for Building Blocks.
|
|
5
|
+
*
|
|
6
|
+
* At deploy time, BB resource mappings (ARNs, URLs, paths) are stored in an
|
|
7
|
+
* S3 JSON file instead of Lambda environment variables (which have a 4KB limit).
|
|
8
|
+
* This module loads that config file once at cold start and caches it.
|
|
9
|
+
*
|
|
10
|
+
* Lookup order for each key:
|
|
11
|
+
* 1. `process.env[key]` — local dev where env vars are set directly
|
|
12
|
+
* 2. Cached S3 config — loaded once per cold start
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
let configCache = null;
|
|
17
|
+
let configLoadPromise = null;
|
|
18
|
+
let s3FetcherOverride = null;
|
|
19
|
+
async function defaultS3Fetcher(bucket, key) {
|
|
20
|
+
const { S3Client, GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
21
|
+
const client = new S3Client({
|
|
22
|
+
region: process.env.AWS_REGION,
|
|
23
|
+
requestHandler: { requestTimeout: 5000 },
|
|
24
|
+
maxAttempts: 3,
|
|
25
|
+
});
|
|
26
|
+
const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
27
|
+
const body = await response.Body?.transformToString('utf-8');
|
|
28
|
+
if (!body)
|
|
29
|
+
throw new Error('Empty config file');
|
|
30
|
+
return body;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load the config JSON from S3. Fetches once and caches forever (Lambda lifetime).
|
|
34
|
+
* Skips S3 entirely if BLOCKS_CONFIG_BUCKET/BLOCKS_CONFIG_KEY are not set (local dev).
|
|
35
|
+
* Throws if the env vars ARE set but the S3 fetch fails.
|
|
36
|
+
*/
|
|
37
|
+
async function loadConfigFromS3() {
|
|
38
|
+
if (configCache)
|
|
39
|
+
return configCache;
|
|
40
|
+
const bucket = process.env.BLOCKS_CONFIG_BUCKET;
|
|
41
|
+
const key = process.env.BLOCKS_CONFIG_KEY;
|
|
42
|
+
if (!bucket || !key) {
|
|
43
|
+
configCache = {};
|
|
44
|
+
return configCache;
|
|
45
|
+
}
|
|
46
|
+
const fetcher = s3FetcherOverride ?? defaultS3Fetcher;
|
|
47
|
+
try {
|
|
48
|
+
const body = await fetcher(bucket, key);
|
|
49
|
+
configCache = JSON.parse(body);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const isNotFound = error?.name === 'NoSuchKey'
|
|
53
|
+
|| error?.Code === 'NoSuchKey'
|
|
54
|
+
|| error?.$metadata?.httpStatusCode === 404;
|
|
55
|
+
if (isNotFound) {
|
|
56
|
+
console.warn(`[Blocks] Config file not found in S3 (${bucket}/${key}), proceeding with empty config`);
|
|
57
|
+
configCache = {};
|
|
58
|
+
return configCache;
|
|
59
|
+
}
|
|
60
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
61
|
+
throw new Error(`[Blocks] Failed to load config from S3 (${bucket}/${key}): ${msg}`);
|
|
62
|
+
}
|
|
63
|
+
if (typeof configCache !== 'object' || configCache === null || Array.isArray(configCache)) {
|
|
64
|
+
throw new Error('[Blocks] Config file must be a JSON object');
|
|
65
|
+
}
|
|
66
|
+
console.log(`[Blocks] Config loaded: ${Object.keys(configCache).length} keys`);
|
|
67
|
+
return configCache;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Ensure only one S3 fetch is in flight at a time.
|
|
71
|
+
* Multiple BB constructors calling getConfig() concurrently will share the same promise.
|
|
72
|
+
*/
|
|
73
|
+
function ensureConfigLoaded() {
|
|
74
|
+
if (configCache)
|
|
75
|
+
return Promise.resolve(configCache);
|
|
76
|
+
if (!configLoadPromise) {
|
|
77
|
+
configLoadPromise = loadConfigFromS3().finally(() => {
|
|
78
|
+
configLoadPromise = null;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return configLoadPromise;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get a config value by key.
|
|
85
|
+
*
|
|
86
|
+
* Checks `process.env[key]` first (local dev where env vars are set directly).
|
|
87
|
+
* If not found, reads from the cached S3 config file.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The config key (e.g., `BLOCKS_AUTH_COGNITO_MYAPP_AUTHC_USER_POOL_ID`)
|
|
90
|
+
* @returns The config value, or empty string if not found anywhere
|
|
91
|
+
*/
|
|
92
|
+
export async function getConfig(key) {
|
|
93
|
+
const envValue = process.env[key];
|
|
94
|
+
if (envValue !== undefined)
|
|
95
|
+
return envValue;
|
|
96
|
+
const config = await ensureConfigLoaded();
|
|
97
|
+
return config[key] ?? '';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Synchronous config access — returns the value if already cached, otherwise
|
|
101
|
+
* returns the process.env fallback. Use this only when you're certain the
|
|
102
|
+
* config has already been loaded (e.g., after an initial await getConfig() call).
|
|
103
|
+
*
|
|
104
|
+
* @param key - The config key
|
|
105
|
+
* @returns The config value, or empty string if not found
|
|
106
|
+
*/
|
|
107
|
+
export function getConfigSync(key) {
|
|
108
|
+
const envValue = process.env[key];
|
|
109
|
+
if (envValue !== undefined)
|
|
110
|
+
return envValue;
|
|
111
|
+
if (configCache)
|
|
112
|
+
return configCache[key] ?? '';
|
|
113
|
+
return '';
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Preload the S3 config. Call this early in Lambda cold start to ensure
|
|
117
|
+
* the config is cached before any BB constructors need it.
|
|
118
|
+
*
|
|
119
|
+
* This is a no-op if the config is already loaded or if S3 env vars aren't set.
|
|
120
|
+
*/
|
|
121
|
+
export async function preloadConfig() {
|
|
122
|
+
await ensureConfigLoaded();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Load S3 config and inject all values into `process.env`.
|
|
126
|
+
*
|
|
127
|
+
* This enables the lazy-init handler pattern: the handler loads config into
|
|
128
|
+
* process.env BEFORE importing the backend module, so BB constructors can
|
|
129
|
+
* read their config via `process.env[key]` as before — no BB code changes needed.
|
|
130
|
+
*
|
|
131
|
+
* Only sets env vars that aren't already set (won't override explicit env vars).
|
|
132
|
+
*/
|
|
133
|
+
export async function loadConfigToProcessEnv() {
|
|
134
|
+
const config = await ensureConfigLoaded();
|
|
135
|
+
for (const [key, value] of Object.entries(config)) {
|
|
136
|
+
if (process.env[key] === undefined) {
|
|
137
|
+
process.env[key] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Reset the config cache. **For testing only.**
|
|
143
|
+
*/
|
|
144
|
+
export function _resetConfigCache() {
|
|
145
|
+
configCache = null;
|
|
146
|
+
configLoadPromise = null;
|
|
147
|
+
s3FetcherOverride = null;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Override the S3 fetcher function. **For testing only.**
|
|
151
|
+
*/
|
|
152
|
+
export function _setS3Fetcher(fetcher) {
|
|
153
|
+
s3FetcherOverride = fetcher;
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/common/config.test.ts"],"names":[],"mappings":""}
|