@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,323 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
|
+
|
|
6
|
+
import { ApiError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
var __BLOCKS_REQUEST_COOKIES_STORE__: AsyncLocalStorage<string> | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getSsrCookies(): string | undefined {
|
|
13
|
+
const store = globalThis.__BLOCKS_REQUEST_COOKIES_STORE__;
|
|
14
|
+
if (!store || typeof store.getStore !== 'function') return undefined;
|
|
15
|
+
return store.getStore();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get or lazily create the request cookie AsyncLocalStorage.
|
|
20
|
+
*
|
|
21
|
+
* In the API Lambda, `lambda-handler.ts` creates the store and registers it
|
|
22
|
+
* on `globalThis.__BLOCKS_REQUEST_COOKIES_STORE__`. In the SSR Lambda (a separate
|
|
23
|
+
* process), the store may not exist yet — this function creates it on demand.
|
|
24
|
+
*
|
|
25
|
+
* This module is server-only (never bundled for the browser), so direct
|
|
26
|
+
* `node:async_hooks` import is safe — no `webpackIgnore` needed.
|
|
27
|
+
*/
|
|
28
|
+
function getRequestCookieStore(): AsyncLocalStorage<string> {
|
|
29
|
+
const existing = globalThis.__BLOCKS_REQUEST_COOKIES_STORE__;
|
|
30
|
+
if (existing) return existing;
|
|
31
|
+
|
|
32
|
+
const store = new AsyncLocalStorage<string>();
|
|
33
|
+
globalThis.__BLOCKS_REQUEST_COOKIES_STORE__ = store;
|
|
34
|
+
return store;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Cookie Provider Registry
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A function that attempts to detect cookies from a specific framework's
|
|
43
|
+
* request context. Returns the cookie string if available, or `undefined`
|
|
44
|
+
* if the framework is not active or no cookies are present.
|
|
45
|
+
*/
|
|
46
|
+
export type CookieProvider = () => Promise<string | undefined>;
|
|
47
|
+
|
|
48
|
+
const providers: Array<{ name: string; detect: CookieProvider }> = [];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Register a framework-specific cookie provider for `withAuth()`.
|
|
52
|
+
*
|
|
53
|
+
* Providers are tried in registration order when `withAuth()` is called
|
|
54
|
+
* without explicit cookies. The first provider that returns a non-undefined
|
|
55
|
+
* value wins.
|
|
56
|
+
*
|
|
57
|
+
* Two providers are registered by default:
|
|
58
|
+
*
|
|
59
|
+
* - **Next.js** — reads cookies from `next/headers`. Works out of the box.
|
|
60
|
+
* - **Nuxt/Nitro** — reads cookies from the active Nitro request event
|
|
61
|
+
* (via the `unctx` `'nitro-app'` async context). Requires
|
|
62
|
+
* `nitro.experimental.asyncContext: true` in `nuxt.config.ts` so Nitro
|
|
63
|
+
* wraps each request in an `AsyncLocalStorage` context. Without that
|
|
64
|
+
* flag the provider returns `undefined` and a warning is emitted on
|
|
65
|
+
* first miss to point at the missing config (see `withAuth` for the
|
|
66
|
+
* no-cookies error path).
|
|
67
|
+
*
|
|
68
|
+
* Use this to add support for additional frameworks (SvelteKit, Astro, etc.)
|
|
69
|
+
* or custom cookie sources.
|
|
70
|
+
*
|
|
71
|
+
* @param name - Human-readable provider name (for debugging).
|
|
72
|
+
* @param detect - Async function that returns a cookie string or `undefined`.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { registerCookieProvider } from '@aws-blocks/blocks/server';
|
|
77
|
+
*
|
|
78
|
+
* // SvelteKit provider
|
|
79
|
+
* registerCookieProvider('sveltekit', async () => {
|
|
80
|
+
* try {
|
|
81
|
+
* const { getRequestEvent } = await import('$app/server');
|
|
82
|
+
* const event = getRequestEvent();
|
|
83
|
+
* return event?.request.headers.get('cookie') ?? undefined;
|
|
84
|
+
* } catch {
|
|
85
|
+
* return undefined;
|
|
86
|
+
* }
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function registerCookieProvider(name: string, detect: CookieProvider): void {
|
|
91
|
+
const existingIdx = providers.findIndex(p => p.name === name);
|
|
92
|
+
if (existingIdx >= 0) {
|
|
93
|
+
providers[existingIdx] = { name, detect };
|
|
94
|
+
} else {
|
|
95
|
+
providers.push({ name, detect });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Built-in Next.js provider — registered by default
|
|
100
|
+
registerCookieProvider('nextjs', async () => {
|
|
101
|
+
try {
|
|
102
|
+
const mod = await import('next/headers');
|
|
103
|
+
const store = await mod.cookies();
|
|
104
|
+
const all = store.getAll();
|
|
105
|
+
if (all.length === 0) return undefined;
|
|
106
|
+
return all.map((c: { name: string; value: string }) => `${c.name}=${c.value}`).join('; ');
|
|
107
|
+
} catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Built-in Nuxt/Nitro provider — requires `nitro.experimental.asyncContext: true`
|
|
113
|
+
//
|
|
114
|
+
// Reads the active request event from Nitro's async context, keyed
|
|
115
|
+
// `'nitro-app'` in `unctx`. Nitro's *documented* entry point for this is
|
|
116
|
+
// `useEvent()` from `nitropack/runtime` — but we deliberately do NOT use
|
|
117
|
+
// it here, and reach into `unctx.getContext('nitro-app')` directly instead.
|
|
118
|
+
//
|
|
119
|
+
// WHY (do not "fix" this back to `nitropack/runtime`): `@aws-blocks/core`
|
|
120
|
+
// is framework-agnostic. `withAuth` is re-exported through
|
|
121
|
+
// `@aws-blocks/blocks/server` and gets transitively imported by Next.js
|
|
122
|
+
// apps (and any other framework). `nitropack/runtime`'s entrypoint pulls
|
|
123
|
+
// in `#nitro-internal-virtual/*` subpath imports that ONLY resolve inside
|
|
124
|
+
// a Nitro build — webpack/Next.js can't resolve them and the consumer's
|
|
125
|
+
// `next build` fails with "Module not found: Can't resolve
|
|
126
|
+
// '#nitro-internal-virtual/error-handler'". `unctx` is a standalone leaf
|
|
127
|
+
// utility with no virtual imports, so it bundles cleanly from any
|
|
128
|
+
// framework. The documented-vs-internal-key trade-off loses to the
|
|
129
|
+
// bundler-safety requirement for a cross-framework package.
|
|
130
|
+
//
|
|
131
|
+
// Both approaches ride the same experimental async-context machinery and
|
|
132
|
+
// both require `experimental.asyncContext: true`. `nitroApp.use()` throws
|
|
133
|
+
// when no context instance is bound (the flag-off signature), which we
|
|
134
|
+
// distinguish from "not a Nitro app at all" below.
|
|
135
|
+
/** Minimal shape of the h3 `H3Event` we read from (Nitro v2 / Nuxt 4). */
|
|
136
|
+
type NitroH3Event = { node?: { req?: { headers?: { cookie?: string } } } };
|
|
137
|
+
|
|
138
|
+
let nuxtAsyncContextWarned = false;
|
|
139
|
+
registerCookieProvider('nuxt', async () => {
|
|
140
|
+
let getContext: typeof import('unctx').getContext;
|
|
141
|
+
try {
|
|
142
|
+
({ getContext } = await import('unctx'));
|
|
143
|
+
} catch {
|
|
144
|
+
return undefined; // unctx not installed → not a Nuxt/Nitro app
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// `getContext('nitro-app')` itself never throws — it lazily creates the
|
|
148
|
+
// context registry. `.use()` is what throws when no instance is bound.
|
|
149
|
+
const nitroAsyncContext = getContext('nitro-app');
|
|
150
|
+
let bound: { event?: NitroH3Event; request?: Request } | undefined;
|
|
151
|
+
try {
|
|
152
|
+
// `.use()` returns the bound NitroApp or throws.
|
|
153
|
+
//
|
|
154
|
+
// TODO(nitro-v3 / nuxt-5): Nitro v3 swaps h3 for srvx, so the bound
|
|
155
|
+
// context shape changes from `{ event }` (h3 `H3Event`, read below via
|
|
156
|
+
// `event.node.req.headers.cookie`) to `{ request }` (a standard Web
|
|
157
|
+
// `Request`, read via `request.headers.get('cookie')`). We read both
|
|
158
|
+
// shapes below so the provider keeps working across the cutover; revisit
|
|
159
|
+
// and drop the h3 branch once Nuxt 5 / Nitro v3 is the floor.
|
|
160
|
+
bound = nitroAsyncContext.use() as typeof bound;
|
|
161
|
+
} catch {
|
|
162
|
+
// In a Nitro runtime but no context bound for this call — almost
|
|
163
|
+
// always `experimental.asyncContext: true` missing from the config.
|
|
164
|
+
// Surface a hint exactly once per process so the developer sees the
|
|
165
|
+
// root cause, not a downstream generic "no cookies" 401. Suppressible
|
|
166
|
+
// via env var for tests / intentional setups.
|
|
167
|
+
if (
|
|
168
|
+
!nuxtAsyncContextWarned &&
|
|
169
|
+
process.env.BLOCKS_SUPPRESS_NUXT_ASYNC_CONTEXT_WARN !== '1'
|
|
170
|
+
) {
|
|
171
|
+
nuxtAsyncContextWarned = true;
|
|
172
|
+
// eslint-disable-next-line no-console
|
|
173
|
+
console.warn(
|
|
174
|
+
'[@aws-blocks/core] withAuth: Nuxt/Nitro detected but the request context is not available. ' +
|
|
175
|
+
"Add `nitro: { experimental: { asyncContext: true } }` to nuxt.config.ts so withAuth() can read cookies from the active request. " +
|
|
176
|
+
'See https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext',
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// h3 (Nitro v2): cookie lives on the Node request headers.
|
|
183
|
+
// srvx (Nitro v3): cookie lives on the Web `Request` headers. See the
|
|
184
|
+
// TODO above — the `request` branch future-proofs this for Nuxt 5.
|
|
185
|
+
const cookie =
|
|
186
|
+
bound?.event?.node?.req?.headers?.cookie ??
|
|
187
|
+
bound?.request?.headers?.get('cookie') ??
|
|
188
|
+
undefined;
|
|
189
|
+
return typeof cookie === 'string' && cookie.length > 0 ? cookie : undefined;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Try each registered provider in order; return the first non-undefined result.
|
|
194
|
+
*/
|
|
195
|
+
async function detectFrameworkCookies(): Promise<string | undefined> {
|
|
196
|
+
for (const provider of providers) {
|
|
197
|
+
const result = await provider.detect();
|
|
198
|
+
if (result) return result;
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Clear all registered cookie providers, including the built-in Next.js one.
|
|
205
|
+
*
|
|
206
|
+
* Primarily useful for testing. After calling this, you must re-register any
|
|
207
|
+
* providers you need (including Next.js if desired).
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* import { clearCookieProviders, registerCookieProvider } from '@aws-blocks/blocks/server';
|
|
212
|
+
*
|
|
213
|
+
* // In test setup
|
|
214
|
+
* clearCookieProviders();
|
|
215
|
+
* registerCookieProvider('test', async () => 'session=test');
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function clearCookieProviders(): void {
|
|
219
|
+
providers.length = 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** @internal Read-only view of registered providers (for testing). */
|
|
223
|
+
export function _getProviders(): ReadonlyArray<{ name: string; detect: CookieProvider }> {
|
|
224
|
+
return providers;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Wraps an async function with authenticated cookie forwarding for SSR.
|
|
229
|
+
*
|
|
230
|
+
* In SSR server components, browser cookies aren't automatically forwarded
|
|
231
|
+
* to API calls. This wrapper reads cookies and makes them available to
|
|
232
|
+
* all Blocks API calls within the callback via `AsyncLocalStorage`.
|
|
233
|
+
*
|
|
234
|
+
* **Resolution order:**
|
|
235
|
+
* 1. Explicit `cookies` parameter (framework-agnostic)
|
|
236
|
+
* 2. Existing request context (Lambda handler already set it up)
|
|
237
|
+
* 3. Registered cookie providers — Next.js and Nuxt are built-in, plus any
|
|
238
|
+
* you've added via `registerCookieProvider`
|
|
239
|
+
* 4. Throws if no cookies found
|
|
240
|
+
*
|
|
241
|
+
* **Built-in providers:**
|
|
242
|
+
*
|
|
243
|
+
* | Framework | Source | Notes |
|
|
244
|
+
* |-----------|--------|-------|
|
|
245
|
+
* | Next.js | `next/headers` | Works out of the box. |
|
|
246
|
+
* | Nuxt/Nitro | Nitro async context (via `unctx`) | Requires `nitro: { experimental: { asyncContext: true } }` in `nuxt.config.ts`. With the flag off the provider returns `undefined` and emits a one-time warning naming the missing config. |
|
|
247
|
+
*
|
|
248
|
+
* @param fn - Async function containing Blocks API calls that need auth.
|
|
249
|
+
* @param cookies - Optional explicit cookie string. If omitted, auto-detects
|
|
250
|
+
* from the request context or registered providers. Pass this for frameworks
|
|
251
|
+
* without a registered provider (or use `registerCookieProvider` to add one).
|
|
252
|
+
* @returns The return value of `fn`.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* // Next.js — auto-detects cookies from next/headers (built-in provider)
|
|
257
|
+
* import { withAuth } from '@aws-blocks/blocks/server';
|
|
258
|
+
*
|
|
259
|
+
* const posts = await withAuth(() => api.listMyPosts());
|
|
260
|
+
*
|
|
261
|
+
* // Multiple calls in one scope
|
|
262
|
+
* const data = await withAuth(async () => {
|
|
263
|
+
* const posts = await api.listMyPosts();
|
|
264
|
+
* const profile = await api.getProfile();
|
|
265
|
+
* return { posts, profile };
|
|
266
|
+
* });
|
|
267
|
+
*
|
|
268
|
+
* // Explicit cookies for any framework
|
|
269
|
+
* const posts = await withAuth(
|
|
270
|
+
* () => api.listMyPosts(),
|
|
271
|
+
* request.headers.get('cookie'),
|
|
272
|
+
* );
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```typescript
|
|
277
|
+
* // Nuxt — auto-detects cookies via the active Nitro request event
|
|
278
|
+
* // (built-in provider). Requires the experimental.asyncContext flag.
|
|
279
|
+
* //
|
|
280
|
+
* // nuxt.config.ts
|
|
281
|
+
* // export default defineNuxtConfig({
|
|
282
|
+
* // nitro: { experimental: { asyncContext: true } },
|
|
283
|
+
* // });
|
|
284
|
+
* //
|
|
285
|
+
* // server/api/posts.get.ts
|
|
286
|
+
* import { withAuth } from '@aws-blocks/blocks/server';
|
|
287
|
+
*
|
|
288
|
+
* export default defineEventHandler(async () => {
|
|
289
|
+
* return withAuth(() => api.listMyPosts());
|
|
290
|
+
* });
|
|
291
|
+
* ```
|
|
292
|
+
*
|
|
293
|
+
* @throws {Error} If no cookies are found and no provider detected them.
|
|
294
|
+
*/
|
|
295
|
+
export async function withAuth<T>(
|
|
296
|
+
fn: () => Promise<T>,
|
|
297
|
+
cookies?: string | null,
|
|
298
|
+
): Promise<T> {
|
|
299
|
+
if (cookies != null && cookies.trim()) {
|
|
300
|
+
const store = getRequestCookieStore();
|
|
301
|
+
return store.run(cookies, fn);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const existing = getSsrCookies();
|
|
305
|
+
if (existing) {
|
|
306
|
+
return fn();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const detected = await detectFrameworkCookies();
|
|
310
|
+
if (detected) {
|
|
311
|
+
const store = getRequestCookieStore();
|
|
312
|
+
return store.run(detected, fn);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const providerNames = providers.map(p => p.name).join(', ');
|
|
316
|
+
throw new ApiError(
|
|
317
|
+
`withAuth: No authentication cookies found. ` +
|
|
318
|
+
`Tried providers: [${providerNames || 'none registered'}]. ` +
|
|
319
|
+
`Ensure the user is logged in and cookies are being forwarded, ` +
|
|
320
|
+
`or pass cookies explicitly as the second argument.`,
|
|
321
|
+
401,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { request as httpsRequest } from 'node:https';
|
|
2
|
+
import { request as httpRequest } from 'node:http';
|
|
3
|
+
import { existsSync, readFileSync, mkdirSync, openSync, writeSync, closeSync, writeFileSync, constants } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { debuglog } from 'node:util';
|
|
6
|
+
import { CORE_VERSION } from '../version.js';
|
|
7
|
+
import { Scope } from '../common/index.js';
|
|
8
|
+
import { isTelemetryEnabled } from './consent.js';
|
|
9
|
+
import { collectEnvironment } from './environment.js';
|
|
10
|
+
import { getInstallationId, getProjectId, generateEventId } from './identifiers.js';
|
|
11
|
+
import type { BlocksTelemetryEvent, BuildAndSendEventOptions } from './types.js';
|
|
12
|
+
|
|
13
|
+
// Debug log: writes to stderr via NODE_DEBUG=blocks-telemetry (developer-facing, for troubleshooting)
|
|
14
|
+
const debug = debuglog('blocks-telemetry');
|
|
15
|
+
|
|
16
|
+
const DEFAULT_ENDPOINT = 'https://blocks-telemetry.us-east-1.api.aws/metrics';
|
|
17
|
+
const TIMEOUT_MS = 500;
|
|
18
|
+
const TELEMETRY_VERSION = '1.0.0';
|
|
19
|
+
|
|
20
|
+
function getEndpoint(): string {
|
|
21
|
+
return process.env.BLOCKS_TELEMETRY_ENDPOINT || DEFAULT_ENDPOINT;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse `--telemetry-file=/path/to/file.json` from process.argv.
|
|
26
|
+
*
|
|
27
|
+
* Supports both `--telemetry-file=path` and `--telemetry-file path` forms.
|
|
28
|
+
*
|
|
29
|
+
* @returns The file path if the flag is present, undefined otherwise.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Capture telemetry to file (also sends HTTP when enabled):
|
|
33
|
+
* // npx tsx aws-blocks/scripts/server.ts --telemetry-file=/tmp/events.json
|
|
34
|
+
*
|
|
35
|
+
* // Capture WITHOUT sending (disable HTTP, file still writes):
|
|
36
|
+
* // AWS_BLOCKS_DISABLE_TELEMETRY=1 npx tsx aws-blocks/scripts/server.ts --telemetry-file=/tmp/events.json
|
|
37
|
+
*/
|
|
38
|
+
export function getTelemetryFilePath(): string | undefined {
|
|
39
|
+
const arg = process.argv.find(a => a.startsWith('--telemetry-file='));
|
|
40
|
+
if (arg) {
|
|
41
|
+
const p = arg.slice('--telemetry-file='.length);
|
|
42
|
+
return p.trim() === '' ? undefined : p;
|
|
43
|
+
}
|
|
44
|
+
const idx = process.argv.indexOf('--telemetry-file');
|
|
45
|
+
if (idx !== -1 && process.argv[idx + 1] && !process.argv[idx + 1].startsWith('-')) {
|
|
46
|
+
return process.argv[idx + 1];
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// File sink: writes to --telemetry-file path (user-facing, for debugging/testing)
|
|
52
|
+
function writeToTelemetryFile(event: BlocksTelemetryEvent): void {
|
|
53
|
+
const filePath = getTelemetryFilePath();
|
|
54
|
+
if (!filePath) return; // getTelemetryFilePath already rejects empty/whitespace
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const dir = path.dirname(filePath);
|
|
58
|
+
if (!existsSync(dir)) {
|
|
59
|
+
mkdirSync(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// O_CREAT | O_EXCL: atomic create — fails with EEXIST if file already exists
|
|
63
|
+
const fd = openSync(filePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o644);
|
|
64
|
+
const content = JSON.stringify([event], null, 2);
|
|
65
|
+
writeSync(fd, content);
|
|
66
|
+
closeSync(fd);
|
|
67
|
+
} catch (err: any) {
|
|
68
|
+
// EEXIST = file already existed → skip (protects user data)
|
|
69
|
+
// All other errors silently ignored — telemetry must never affect commands
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Constructs a telemetry event without sending it.
|
|
75
|
+
* Useful for testing event structure without I/O side effects.
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export function buildEvent(opts: BuildAndSendEventOptions): BlocksTelemetryEvent {
|
|
79
|
+
const { blocks, totalCount, customBlocksCount } = Scope.getRegisteredBlocks();
|
|
80
|
+
|
|
81
|
+
let templateName: string | undefined = opts.product?.template;
|
|
82
|
+
let templateVersion: string | undefined = opts.product?.templateVersion;
|
|
83
|
+
try {
|
|
84
|
+
const pkgPath = path.resolve(process.cwd(), 'package.json');
|
|
85
|
+
if (existsSync(pkgPath)) {
|
|
86
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
87
|
+
if (pkg.blocksTemplate) templateName = templateName || pkg.blocksTemplate;
|
|
88
|
+
if (pkg.blocksTemplateVersion) templateVersion = templateVersion || pkg.blocksTemplateVersion;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// template info is best-effort
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const buildingBlocks = blocks;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
telemetryVersion: TELEMETRY_VERSION,
|
|
98
|
+
identifiers: {
|
|
99
|
+
installationId: getInstallationId(),
|
|
100
|
+
projectId: getProjectId(),
|
|
101
|
+
eventId: generateEventId(),
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
},
|
|
104
|
+
event: {
|
|
105
|
+
command: opts.command,
|
|
106
|
+
state: opts.state,
|
|
107
|
+
duration: opts.duration,
|
|
108
|
+
...(opts.error && { error: opts.error }),
|
|
109
|
+
},
|
|
110
|
+
environment: collectEnvironment(),
|
|
111
|
+
product: {
|
|
112
|
+
blocksVersion: CORE_VERSION,
|
|
113
|
+
...(opts.product?.cdkVersion && { cdkVersion: opts.product.cdkVersion }),
|
|
114
|
+
...(opts.product?.framework && { framework: opts.product.framework }),
|
|
115
|
+
...(templateName && {
|
|
116
|
+
template: {
|
|
117
|
+
name: templateName,
|
|
118
|
+
...(templateVersion && { version: templateVersion }),
|
|
119
|
+
},
|
|
120
|
+
}),
|
|
121
|
+
...(buildingBlocks.length > 0 && { buildingBlocks }),
|
|
122
|
+
},
|
|
123
|
+
...(opts.counters || totalCount > 0 || customBlocksCount > 0
|
|
124
|
+
? {
|
|
125
|
+
counters: {
|
|
126
|
+
blocksCount: opts.counters?.blocksCount ?? totalCount,
|
|
127
|
+
...(opts.counters?.customBuildingBlocks !== undefined
|
|
128
|
+
? { customBuildingBlocks: opts.counters.customBuildingBlocks }
|
|
129
|
+
: customBlocksCount > 0 ? { customBuildingBlocks: customBlocksCount } : undefined),
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
: undefined),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build a complete telemetry event and send it to the collection endpoint.
|
|
138
|
+
*
|
|
139
|
+
* This is the single entry point for all telemetry emission. It handles:
|
|
140
|
+
* 1. Early exit — if telemetry is disabled AND no `--telemetry-file` is set,
|
|
141
|
+
* returns immediately with no side effects (no IDs persisted, no notice)
|
|
142
|
+
* 2. Full payload construction (identifiers, environment, product info)
|
|
143
|
+
* 3. File sink — writes to the file specified by `--telemetry-file` (if set),
|
|
144
|
+
* regardless of opt-out status (inspired by CDK CLI's --telemetry-file)
|
|
145
|
+
* 4. Privacy filtering — custom BB names are NEVER sent, only counted
|
|
146
|
+
* 5. HTTP sink — fire-and-forget POST (only when telemetry is enabled)
|
|
147
|
+
*
|
|
148
|
+
* The file sink fires unconditionally when `--telemetry-file` is set.
|
|
149
|
+
* The HTTP sink only fires when telemetry is enabled. Callers never need to
|
|
150
|
+
* import consent, identifier, or environment utilities directly.
|
|
151
|
+
*
|
|
152
|
+
* @param opts - Event metadata (command, state, duration, optional error/product/counters)
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* buildAndSendEvent({
|
|
157
|
+
* command: 'deploy',
|
|
158
|
+
* state: 'SUCCESS',
|
|
159
|
+
* duration: 4500,
|
|
160
|
+
* product: { cdkVersion: '2.150.0' },
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function buildAndSendEvent(opts: BuildAndSendEventOptions): void {
|
|
165
|
+
try {
|
|
166
|
+
const filePath = getTelemetryFilePath();
|
|
167
|
+
const enabled = isTelemetryEnabled();
|
|
168
|
+
if (!filePath && !enabled) return;
|
|
169
|
+
|
|
170
|
+
const event = buildEvent(opts);
|
|
171
|
+
|
|
172
|
+
if (filePath) writeToTelemetryFile(event);
|
|
173
|
+
if (enabled) sendEvent(event);
|
|
174
|
+
} catch {
|
|
175
|
+
// Telemetry must never throw or affect the user's command
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Send a pre-built telemetry event to the collection endpoint.
|
|
181
|
+
*
|
|
182
|
+
* Fire-and-forget: no retry, 500ms timeout, all errors silently swallowed.
|
|
183
|
+
* Debug output available via `NODE_DEBUG=blocks-telemetry`.
|
|
184
|
+
*/
|
|
185
|
+
export function sendEvent(event: BlocksTelemetryEvent): void {
|
|
186
|
+
try {
|
|
187
|
+
const endpoint = getEndpoint();
|
|
188
|
+
const payload = JSON.stringify(event);
|
|
189
|
+
|
|
190
|
+
// E2E test sink: writes to BLOCKS_TELEMETRY_FILE env path (test-facing, for assertions)
|
|
191
|
+
if (process.env.BLOCKS_TELEMETRY_FILE) {
|
|
192
|
+
try { writeFileSync(process.env.BLOCKS_TELEMETRY_FILE, payload); } catch {}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
debug('sending event to %s (%d bytes)', endpoint, Buffer.byteLength(payload));
|
|
196
|
+
|
|
197
|
+
const url = new URL(endpoint);
|
|
198
|
+
const isHttps = url.protocol === 'https:';
|
|
199
|
+
const requestFn = isHttps ? httpsRequest : httpRequest;
|
|
200
|
+
|
|
201
|
+
const req = requestFn(
|
|
202
|
+
{
|
|
203
|
+
hostname: url.hostname,
|
|
204
|
+
port: url.port || (isHttps ? '443' : '80'),
|
|
205
|
+
path: url.pathname + url.search,
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: {
|
|
208
|
+
'Content-Type': 'application/json',
|
|
209
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
210
|
+
},
|
|
211
|
+
timeout: TIMEOUT_MS,
|
|
212
|
+
},
|
|
213
|
+
(res) => {
|
|
214
|
+
debug('event sent (status=%d)', res.statusCode);
|
|
215
|
+
res.resume();
|
|
216
|
+
},
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
req.on('error', (err) => { debug('send failed: %s', err.message); });
|
|
220
|
+
req.on('timeout', () => { debug('send timed out'); req.destroy(); });
|
|
221
|
+
req.write(payload);
|
|
222
|
+
req.end();
|
|
223
|
+
} catch {
|
|
224
|
+
// Telemetry must never throw or affect the user's command
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
|
+
|
|
7
|
+
interface TelemetryConfig {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
projectId?: string;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface BlocksConfig {
|
|
14
|
+
telemetry?: TelemetryConfig;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Read an existing config file, merge the telemetry.enabled setting, and write it back.
|
|
20
|
+
* Creates the file and parent directories if they don't exist.
|
|
21
|
+
* Preserves all other keys in the config file and in the telemetry object
|
|
22
|
+
* (e.g., projectId).
|
|
23
|
+
*
|
|
24
|
+
* Config format:
|
|
25
|
+
* ```json
|
|
26
|
+
* { "telemetry": { "enabled": true, "projectId": "..." } }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param configPath - Absolute path to the config file
|
|
30
|
+
* @param telemetryEnabled - Whether telemetry should be enabled
|
|
31
|
+
*/
|
|
32
|
+
export function writeConfigTelemetry(configPath: string, telemetryEnabled: boolean): void {
|
|
33
|
+
let existing: BlocksConfig = {};
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
37
|
+
existing = JSON.parse(content) as BlocksConfig;
|
|
38
|
+
} catch {
|
|
39
|
+
// File doesn't exist or is invalid — start fresh
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const telemetry: TelemetryConfig = (existing.telemetry && typeof existing.telemetry === 'object')
|
|
43
|
+
? { ...existing.telemetry }
|
|
44
|
+
: {};
|
|
45
|
+
|
|
46
|
+
telemetry.enabled = telemetryEnabled;
|
|
47
|
+
existing.telemetry = telemetry;
|
|
48
|
+
|
|
49
|
+
const dir = dirname(configPath);
|
|
50
|
+
mkdirSync(dir, { recursive: true });
|
|
51
|
+
writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
|
|
52
|
+
}
|