@aws-blocks/core 0.2.0 → 0.4.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/README.md +13 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +32 -0
- package/dist/api.test.js +22 -0
- package/dist/bb-utils.d.ts +1 -0
- package/dist/bb-utils.d.ts.map +1 -1
- package/dist/bb-utils.js +3 -0
- package/dist/cdk/apigateway-account.d.ts +33 -0
- package/dist/cdk/apigateway-account.d.ts.map +1 -0
- package/dist/cdk/apigateway-account.js +60 -0
- package/dist/cdk/blocks-backend.d.ts +43 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +69 -73
- package/dist/cdk/blocks-backend.test.js +78 -69
- package/dist/cdk/blocks-defaults.d.ts +92 -2
- package/dist/cdk/blocks-defaults.d.ts.map +1 -1
- package/dist/cdk/blocks-defaults.js +22 -2
- package/dist/cdk/blocks-defaults.test.js +21 -2
- package/dist/cdk/blocks-stack.test.js +94 -37
- package/dist/cdk/compute/compute-registry.d.ts +19 -0
- package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
- package/dist/cdk/compute/compute-registry.js +38 -0
- package/dist/cdk/compute/compute.d.ts +2 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -1
- package/dist/cdk/compute/compute.js +8 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +34 -0
- package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
- package/dist/cdk/compute/default-compute-factory.js +3 -0
- package/dist/cdk/config-registry.d.ts +34 -4
- package/dist/cdk/config-registry.d.ts.map +1 -1
- package/dist/cdk/config-registry.js +83 -25
- package/dist/cdk/config-registry.test.d.ts +2 -0
- package/dist/cdk/config-registry.test.d.ts.map +1 -0
- package/dist/cdk/config-registry.test.js +115 -0
- package/dist/cdk/index.d.ts +76 -7
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +99 -9
- package/dist/cdk/internal.d.ts +1 -0
- package/dist/cdk/internal.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/common/config.d.ts +34 -0
- package/dist/common/config.d.ts.map +1 -1
- package/dist/common/config.js +45 -3
- package/dist/common/config.test.js +19 -0
- package/dist/common/index.d.ts +8 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -0
- package/dist/hosting-secrets.d.ts +48 -0
- package/dist/hosting-secrets.d.ts.map +1 -0
- package/dist/hosting-secrets.js +57 -0
- package/dist/hosting-secrets.test.d.ts +2 -0
- package/dist/hosting-secrets.test.d.ts.map +1 -0
- package/dist/hosting-secrets.test.js +81 -0
- package/dist/hosting.d.ts +97 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +91 -23
- package/dist/hosting.test.js +182 -10
- package/dist/index.cdk.d.ts +13 -12
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -9
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/lambda-handler.d.ts +17 -0
- package/dist/lambda-handler.d.ts.map +1 -1
- package/dist/lambda-handler.js +52 -3
- package/dist/lambda-handler.test.js +196 -1
- package/dist/pipeline/index.d.ts +24 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +41 -1
- package/dist/pipeline.test.d.ts +2 -0
- package/dist/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline.test.js +47 -0
- package/dist/rpc.d.ts +21 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +34 -0
- package/dist/rpc.test.js +38 -1
- package/dist/scripts/config.d.ts +15 -0
- package/dist/scripts/config.d.ts.map +1 -0
- package/dist/scripts/config.js +29 -0
- package/dist/scripts/deploy.d.ts.map +1 -1
- package/dist/scripts/deploy.js +4 -0
- package/dist/scripts/dev-server.d.ts +8 -0
- package/dist/scripts/dev-server.d.ts.map +1 -1
- package/dist/scripts/dev-server.js +31 -0
- package/dist/scripts/generate-client.d.ts.map +1 -1
- package/dist/scripts/generate-client.js +9 -4
- package/dist/scripts/generate-client.test.js +19 -4
- package/dist/scripts/index.d.ts +13 -10
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +11 -8
- package/dist/scripts/preflight-credentials.d.ts +30 -0
- package/dist/scripts/preflight-credentials.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.js +108 -0
- package/dist/scripts/preflight-credentials.test.d.ts +2 -0
- package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.test.js +77 -0
- package/dist/scripts/sandbox-args.test.d.ts +2 -0
- package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-args.test.js +77 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
- package/dist/scripts/sandbox.d.ts +106 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +255 -40
- package/dist/scripts/secret.d.ts +15 -0
- package/dist/scripts/secret.d.ts.map +1 -0
- package/dist/scripts/secret.js +29 -0
- package/dist/scripts/secret.test.d.ts +2 -0
- package/dist/scripts/secret.test.d.ts.map +1 -0
- package/dist/scripts/secret.test.js +28 -0
- package/dist/scripts/typegen.d.ts +15 -0
- package/dist/scripts/typegen.d.ts.map +1 -0
- package/dist/scripts/typegen.js +16 -0
- package/dist/secret-naming.d.ts +58 -0
- package/dist/secret-naming.d.ts.map +1 -0
- package/dist/secret-naming.js +67 -0
- package/dist/secret-naming.test.d.ts +2 -0
- package/dist/secret-naming.test.d.ts.map +1 -0
- package/dist/secret-naming.test.js +40 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/src/api.test.ts +25 -0
- package/src/api.ts +39 -0
- package/src/bb-utils.ts +3 -0
- package/src/cdk/apigateway-account.ts +66 -0
- package/src/cdk/blocks-backend.test.ts +291 -272
- package/src/cdk/blocks-backend.ts +88 -83
- package/src/cdk/blocks-defaults.test.ts +25 -2
- package/src/cdk/blocks-defaults.ts +94 -2
- package/src/cdk/blocks-stack.test.ts +186 -118
- package/src/cdk/compute/compute-registry.ts +45 -0
- package/src/cdk/compute/compute.ts +10 -0
- package/src/cdk/compute/default-compute-factory.ts +38 -0
- package/src/cdk/config-registry.test.ts +135 -0
- package/src/cdk/config-registry.ts +92 -34
- package/src/cdk/index.ts +129 -10
- package/src/cdk/internal.ts +1 -0
- package/src/client/index.ts +1 -1
- package/src/common/config.test.ts +21 -0
- package/src/common/config.ts +47 -3
- package/src/common/index.ts +8 -0
- package/src/errors.ts +21 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +272 -63
- package/src/hosting.ts +200 -68
- package/src/index.cdk.ts +80 -38
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +215 -1
- package/src/lambda-handler.ts +54 -2
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +44 -1
- package/src/rpc.ts +41 -0
- package/src/scripts/config.ts +35 -0
- package/src/scripts/deploy.ts +5 -0
- package/src/scripts/dev-server.ts +40 -0
- package/src/scripts/generate-client.test.ts +43 -8
- package/src/scripts/generate-client.ts +70 -65
- package/src/scripts/index.ts +20 -17
- package/src/scripts/preflight-credentials.test.ts +110 -0
- package/src/scripts/preflight-credentials.ts +131 -0
- package/src/scripts/sandbox-args.test.ts +92 -0
- package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
- package/src/scripts/sandbox.ts +287 -39
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -0
- package/src/scripts/typegen.ts +18 -0
- package/src/secret-naming.test.ts +59 -0
- package/src/secret-naming.ts +99 -0
- package/src/version.ts +1 -1
package/dist/hosting.test.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
4
3
|
import assert from 'node:assert';
|
|
5
4
|
import * as fs from 'node:fs';
|
|
6
|
-
import * as path from 'node:path';
|
|
7
5
|
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
8
|
+
import { config, secret } from '@aws-blocks/hosting';
|
|
9
|
+
import { _setSynthExistsChecker } from '@aws-blocks/hosting/constructs';
|
|
8
10
|
import * as cdk from 'aws-cdk-lib';
|
|
9
11
|
import { App, Duration, Stack } from 'aws-cdk-lib';
|
|
10
|
-
import {
|
|
12
|
+
import { Match, Template } from 'aws-cdk-lib/assertions';
|
|
11
13
|
import { BLOCKS_RPC_PREFIX } from './constants.js';
|
|
12
14
|
import { Hosting } from './hosting.js';
|
|
13
15
|
import { clearRouteRegistry, registerRoute } from './raw-route.js';
|
|
@@ -84,6 +86,31 @@ describe('Hosting', () => {
|
|
|
84
86
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
85
87
|
clearRouteRegistry();
|
|
86
88
|
});
|
|
89
|
+
// ── Deploy-time existence check (key change 4) ───────────────
|
|
90
|
+
describe('environment marker existence check (Hosting.create)', () => {
|
|
91
|
+
it('rejects at deploy when a declared environment secret has no value', async () => {
|
|
92
|
+
createSpaBuildOutput(tmpDir);
|
|
93
|
+
const app = new App();
|
|
94
|
+
const stack = new Stack(app, 'MissingEnvSecretStack');
|
|
95
|
+
// Stub the synth existence probe to report "not set" — rejects before any
|
|
96
|
+
// CDK construction, so this needs no assets and no AWS.
|
|
97
|
+
_setSynthExistsChecker(async () => {
|
|
98
|
+
const e = new Error('missing');
|
|
99
|
+
e.name = 'ResourceNotFoundException';
|
|
100
|
+
throw e;
|
|
101
|
+
});
|
|
102
|
+
try {
|
|
103
|
+
await assert.rejects(Hosting.create(stack, 'Hosting', {
|
|
104
|
+
root: tmpDir,
|
|
105
|
+
api: MOCK_API,
|
|
106
|
+
environment: { MISSING_SECRET: secret('MISSING_SECRET') },
|
|
107
|
+
}), /MISSING_SECRET.*not set/s);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
_setSynthExistsChecker(null);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
87
114
|
// ── SPA tests ────────────────────────────────────────────────
|
|
88
115
|
describe('SPA mode', () => {
|
|
89
116
|
it('creates S3 bucket and CloudFront distribution', () => {
|
|
@@ -345,6 +372,30 @@ describe('Hosting', () => {
|
|
|
345
372
|
}
|
|
346
373
|
assert.ok(foundRetain, 'At least one bucket should have Retain deletion policy');
|
|
347
374
|
});
|
|
375
|
+
it('forwards buildRetentionDays to the hosting bucket lifecycle rule (#480)', () => {
|
|
376
|
+
createSpaBuildOutput(tmpDir);
|
|
377
|
+
const app = new App();
|
|
378
|
+
const stack = new Stack(app, 'RetentionStack');
|
|
379
|
+
new Hosting(stack, 'Hosting', {
|
|
380
|
+
root: tmpDir,
|
|
381
|
+
api: MOCK_API,
|
|
382
|
+
buildRetentionDays: 90,
|
|
383
|
+
});
|
|
384
|
+
const template = Template.fromStack(stack);
|
|
385
|
+
// Before the fix, core dropped buildRetentionDays (only retainOnDelete was
|
|
386
|
+
// forwarded), so this asserted 30. It must now reach the DeleteOldBuilds
|
|
387
|
+
// rule as 90.
|
|
388
|
+
template.hasResourceProperties('AWS::S3::Bucket', {
|
|
389
|
+
LifecycleConfiguration: Match.objectLike({
|
|
390
|
+
Rules: Match.arrayWith([
|
|
391
|
+
Match.objectLike({
|
|
392
|
+
Id: 'DeleteOldBuilds',
|
|
393
|
+
ExpirationInDays: 90,
|
|
394
|
+
}),
|
|
395
|
+
]),
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
});
|
|
348
399
|
});
|
|
349
400
|
// ── API integration tests ────────────────────────────────────
|
|
350
401
|
describe('API integration', () => {
|
|
@@ -1092,8 +1143,7 @@ describe('Hosting', () => {
|
|
|
1092
1143
|
...(basePath !== undefined ? { basePath } : {}),
|
|
1093
1144
|
});
|
|
1094
1145
|
const tpl = Template.fromStack(stack).toJSON();
|
|
1095
|
-
const kvKeys = Object.entries(tpl.Resources).find(([id, r]) => r.Type === 'AWS::CloudFormation::CustomResource' &&
|
|
1096
|
-
/RouteStoreKeys/.test(id));
|
|
1146
|
+
const kvKeys = Object.entries(tpl.Resources).find(([id, r]) => r.Type === 'AWS::CloudFormation::CustomResource' && /RouteStoreKeys/.test(id));
|
|
1097
1147
|
assert.ok(kvKeys, 'expected a RouteStoreKeys custom resource');
|
|
1098
1148
|
const entries = JSON.parse(kvKeys[1].Properties.Entries);
|
|
1099
1149
|
const meta = JSON.parse(entries.meta);
|
|
@@ -1134,6 +1184,131 @@ describe('Hosting', () => {
|
|
|
1134
1184
|
`found DependsOn=${JSON.stringify(dependsOn)}`);
|
|
1135
1185
|
});
|
|
1136
1186
|
});
|
|
1187
|
+
// ── value() integration ─────────────────────────────────────────
|
|
1188
|
+
//
|
|
1189
|
+
// Runtime values: the store LOCATOR (never the value) is injected as a Lambda
|
|
1190
|
+
// env var, and the role is granted read + kms:Decrypt. The store is derived from
|
|
1191
|
+
// the marker's type — secret → Secrets Manager, config → SSM Parameter Store.
|
|
1192
|
+
// Domain markers resolve at synth time via Hosting.create().
|
|
1193
|
+
describe('secret() / config() integration', () => {
|
|
1194
|
+
it('secret() → Secrets Manager: injects the locator (not the value) + grants read+decrypt', () => {
|
|
1195
|
+
createNextjsBuildOutput(tmpDir);
|
|
1196
|
+
const app = new App();
|
|
1197
|
+
const stack = new Stack(app, 'SecretEnvStack');
|
|
1198
|
+
new Hosting(stack, 'Hosting', {
|
|
1199
|
+
root: tmpDir,
|
|
1200
|
+
customAdapter: createNextjsFixtureAdapter(tmpDir),
|
|
1201
|
+
api: MOCK_API,
|
|
1202
|
+
environment: { STRIPE_KEY: secret('STRIPE_KEY') },
|
|
1203
|
+
});
|
|
1204
|
+
const template = Template.fromStack(stack);
|
|
1205
|
+
// secret → Secrets Manager: slash-free locator under the Blocks /blocks/secrets namespace.
|
|
1206
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
1207
|
+
Environment: {
|
|
1208
|
+
Variables: Match.objectLike({ HOSTING_SECRET_PARAM_STRIPE_KEY: 'blocks/secrets/STRIPE_KEY' }),
|
|
1209
|
+
},
|
|
1210
|
+
});
|
|
1211
|
+
// The plaintext value must NOT appear anywhere in the template.
|
|
1212
|
+
const json = JSON.stringify(template.toJSON());
|
|
1213
|
+
assert.ok(!json.includes('STRIPE_KEY_VALUE'), 'no value should be in the template');
|
|
1214
|
+
// The role has secretsmanager:GetSecretValue scoped to the secret + kms:Decrypt.
|
|
1215
|
+
template.hasResourceProperties('AWS::IAM::Policy', {
|
|
1216
|
+
PolicyDocument: {
|
|
1217
|
+
Statement: Match.arrayWith([
|
|
1218
|
+
Match.objectLike({ Action: 'secretsmanager:GetSecretValue' }),
|
|
1219
|
+
Match.objectLike({
|
|
1220
|
+
Action: 'kms:Decrypt',
|
|
1221
|
+
Condition: { StringEquals: Match.objectLike({ 'kms:ViaService': Match.anyValue() }) },
|
|
1222
|
+
}),
|
|
1223
|
+
]),
|
|
1224
|
+
},
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
it('threads secretStore { prefix, stage, cacheTtlSeconds } from core HostingProps to the wiring (B3)', () => {
|
|
1228
|
+
createNextjsBuildOutput(tmpDir);
|
|
1229
|
+
const app = new App();
|
|
1230
|
+
const stack = new Stack(app, 'SecretStoreOverrideStack');
|
|
1231
|
+
new Hosting(stack, 'Hosting', {
|
|
1232
|
+
root: tmpDir,
|
|
1233
|
+
customAdapter: createNextjsFixtureAdapter(tmpDir),
|
|
1234
|
+
api: MOCK_API,
|
|
1235
|
+
environment: { STRIPE_KEY: secret('STRIPE_KEY') },
|
|
1236
|
+
// Custom prefix + stage + warm-cache TTL must all reach the leaf wiring.
|
|
1237
|
+
secretStore: { prefix: '/myapp/secrets', stage: 'prod', cacheTtlSeconds: 30 },
|
|
1238
|
+
});
|
|
1239
|
+
const template = Template.fromStack(stack);
|
|
1240
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
1241
|
+
Environment: {
|
|
1242
|
+
Variables: Match.objectLike({
|
|
1243
|
+
// custom prefix + stage (Secrets Manager → slash-free)
|
|
1244
|
+
HOSTING_SECRET_PARAM_STRIPE_KEY: 'myapp/secrets/prod/STRIPE_KEY',
|
|
1245
|
+
// shared fallback (no stage)
|
|
1246
|
+
HOSTING_SECRET_PARAM_STRIPE_KEY_FALLBACK: 'myapp/secrets/STRIPE_KEY',
|
|
1247
|
+
// warm-cache TTL for rotation without a cold start
|
|
1248
|
+
HOSTING_SECRET_CACHE_TTL: '30',
|
|
1249
|
+
}),
|
|
1250
|
+
},
|
|
1251
|
+
});
|
|
1252
|
+
});
|
|
1253
|
+
it('config() → SSM: HOSTING_CONFIG_PARAM_* (leading-slash) + ssm:GetParameter', () => {
|
|
1254
|
+
createNextjsBuildOutput(tmpDir);
|
|
1255
|
+
const app = new App();
|
|
1256
|
+
const stack = new Stack(app, 'ConfigEnvStack');
|
|
1257
|
+
new Hosting(stack, 'Hosting', {
|
|
1258
|
+
root: tmpDir,
|
|
1259
|
+
customAdapter: createNextjsFixtureAdapter(tmpDir),
|
|
1260
|
+
api: MOCK_API,
|
|
1261
|
+
environment: { FEATURE_FLAGS: config('FEATURE_FLAGS') },
|
|
1262
|
+
});
|
|
1263
|
+
const template = Template.fromStack(stack);
|
|
1264
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
1265
|
+
Environment: {
|
|
1266
|
+
Variables: Match.objectLike({ HOSTING_CONFIG_PARAM_FEATURE_FLAGS: '/blocks/config/FEATURE_FLAGS' }),
|
|
1267
|
+
},
|
|
1268
|
+
});
|
|
1269
|
+
template.hasResourceProperties('AWS::IAM::Policy', {
|
|
1270
|
+
PolicyDocument: { Statement: Match.arrayWith([Match.objectLike({ Action: 'ssm:GetParameter' })]) },
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
it('does not inject a plaintext env var for a runtime value', () => {
|
|
1274
|
+
createNextjsBuildOutput(tmpDir);
|
|
1275
|
+
const app = new App();
|
|
1276
|
+
const stack = new Stack(app, 'SecretNoPlaintextStack');
|
|
1277
|
+
new Hosting(stack, 'Hosting', {
|
|
1278
|
+
root: tmpDir,
|
|
1279
|
+
customAdapter: createNextjsFixtureAdapter(tmpDir),
|
|
1280
|
+
api: MOCK_API,
|
|
1281
|
+
environment: { FLAG: 'on', STRIPE_KEY: secret('STRIPE_KEY') },
|
|
1282
|
+
});
|
|
1283
|
+
const template = Template.fromStack(stack);
|
|
1284
|
+
const fns = template.findResources('AWS::Lambda::Function');
|
|
1285
|
+
for (const fn of Object.values(fns)) {
|
|
1286
|
+
const vars = (fn.Properties?.Environment?.Variables ?? {});
|
|
1287
|
+
assert.strictEqual(vars.STRIPE_KEY, undefined, 'value key must not be a plaintext env var');
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
it('rejects a runtime value whose key mismatches the env var name', () => {
|
|
1291
|
+
createNextjsBuildOutput(tmpDir);
|
|
1292
|
+
const app = new App();
|
|
1293
|
+
const stack = new Stack(app, 'SecretMismatchStack');
|
|
1294
|
+
assert.throws(() => new Hosting(stack, 'Hosting', {
|
|
1295
|
+
root: tmpDir,
|
|
1296
|
+
customAdapter: createNextjsFixtureAdapter(tmpDir),
|
|
1297
|
+
api: MOCK_API,
|
|
1298
|
+
environment: { STRIPE_KEY: secret('OTHER') },
|
|
1299
|
+
}), /must match the environment variable name/);
|
|
1300
|
+
});
|
|
1301
|
+
it('throws if a domain marker is used with the sync constructor', () => {
|
|
1302
|
+
createSpaBuildOutput(tmpDir);
|
|
1303
|
+
const app = new App();
|
|
1304
|
+
const stack = new Stack(app, 'SecretDomainSyncStack');
|
|
1305
|
+
assert.throws(() => new Hosting(stack, 'Hosting', {
|
|
1306
|
+
root: tmpDir,
|
|
1307
|
+
api: MOCK_API,
|
|
1308
|
+
domain: { domainName: config('DOMAIN_PROD') },
|
|
1309
|
+
}), /requires async resolution/);
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1137
1312
|
describe('config.json stale-placeholder guard (#173)', () => {
|
|
1138
1313
|
// Helper: pull every BucketDeployment custom resource's Properties.
|
|
1139
1314
|
const bucketDeployments = (stack) => {
|
|
@@ -1169,8 +1344,7 @@ describe('Hosting', () => {
|
|
|
1169
1344
|
directive.includes('s-maxage=31536000') &&
|
|
1170
1345
|
!excludes(p).includes('.blocks-sandbox/config.json'));
|
|
1171
1346
|
});
|
|
1172
|
-
assert.strictEqual(leaks.length, 0, 'no mutable-tier deployment may apply s-maxage=31536000 to ' +
|
|
1173
|
-
'.blocks-sandbox/config.json');
|
|
1347
|
+
assert.strictEqual(leaks.length, 0, 'no mutable-tier deployment may apply s-maxage=31536000 to ' + '.blocks-sandbox/config.json');
|
|
1174
1348
|
});
|
|
1175
1349
|
it('registers the placeholder as a no-cache path even for a static-only site (no api)', () => {
|
|
1176
1350
|
// The placeholder is always written (step 5), so its no-cache
|
|
@@ -1201,9 +1375,7 @@ describe('Hosting', () => {
|
|
|
1201
1375
|
// Assert via Template + Match so a CDK property rename fails loudly here
|
|
1202
1376
|
// rather than silently skipping a structural-heuristic lookup.
|
|
1203
1377
|
Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', Match.objectLike({
|
|
1204
|
-
DistributionPaths: Match.arrayWith([
|
|
1205
|
-
Match.stringLikeRegexp('^/builds/.+/\\.blocks-sandbox/\\*$'),
|
|
1206
|
-
]),
|
|
1378
|
+
DistributionPaths: Match.arrayWith([Match.stringLikeRegexp('^/builds/.+/\\.blocks-sandbox/\\*$')]),
|
|
1207
1379
|
}));
|
|
1208
1380
|
});
|
|
1209
1381
|
});
|
package/dist/index.cdk.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
1
|
+
export { type ConfigValue, config, DEFAULT_CONFIG_PARAMETER_PREFIX, DEFAULT_SECRET_PARAMETER_PREFIX, isConfig, isManagedValue, isSecret, type ManagedValue, type SecretValue, secret, type ValueKind, } from '@aws-blocks/hosting';
|
|
2
|
+
export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
|
|
3
|
+
export { BlocksBackend, type BlocksBackendProps, type BlocksDefaults, BlocksPresets, BlocksStack, type BlocksThrottling, blocksNodejsBundling, type CoreBlocksBackendProps, type CoreBlocksStackProps, DEFAULT_NODE_RUNTIME, ensureApiGatewayAccount, finalizeConfigRegistry, getConfigLocation, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
|
|
4
|
+
export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
|
|
5
5
|
export { BlocksStackProps } from './common/index.js';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export { type
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
6
|
+
export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
|
|
7
|
+
export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
|
|
8
|
+
export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
|
|
9
|
+
export { type BlocksStackApi, type ComputeConfig, type FrameworkType, Hosting, type HostingDomainConfig, type HostingProps, type HostingWafConfig, } from './hosting.js';
|
|
10
|
+
export { EventSourceMapping } from './lambda-handler.js';
|
|
11
|
+
export { __PIPELINE_STAGE_SCOPE__, type BranchConfig, DeployStage, type DeployStageProps, Pipeline, type PipelineProps, type PipelineSourceConfig, type PipelineStageConfig, type PipelineSynthConfig, } from './pipeline/index.js';
|
|
12
|
+
export { type HttpMethod, RawRoute, RawRouteErrors, type RawRouteOptions, } from './raw-route.cdk.js';
|
|
13
|
+
export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, type RegisteredRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
|
|
14
|
+
export { BLOCKS_CONFIG_PARAMETER_PREFIX, BLOCKS_SECRET_PARAMETER_PREFIX, blocksConfigParameterName, blocksSecretParameterName, } from './secret-naming.js';
|
|
14
15
|
//# sourceMappingURL=index.cdk.d.ts.map
|
package/dist/index.cdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,WAAW,EAChB,MAAM,EACN,+BAA+B,EAC/B,+BAA+B,EAC/B,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,EACN,KAAK,SAAS,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACN,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,aAAa,EACb,WAAW,EACX,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,gCAAgC,EAChC,KAAK,EACL,8BAA8B,EAC9B,UAAU,GACV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACN,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzG,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACN,wBAAwB,EACxB,KAAK,YAAY,EACjB,WAAW,EACX,KAAK,gBAAgB,EACrB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,KAAK,UAAU,EACf,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,KAAK,eAAe,EACpB,aAAa,EACb,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,8BAA8B,EAC9B,8BAA8B,EAC9B,yBAAyB,EACzB,yBAAyB,GACzB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.cdk.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// Declare functions for infra (secret/config). The runtime getters
|
|
4
|
+
// (getSecret/getConfig) are imported from '@aws-blocks/hosting' — core's own
|
|
5
|
+
// getConfig (backend config loader, above) owns that name here.
|
|
6
|
+
export { config, DEFAULT_CONFIG_PARAMETER_PREFIX, DEFAULT_SECRET_PARAMETER_PREFIX, isConfig, isManagedValue, isSecret, secret, } from '@aws-blocks/hosting';
|
|
3
7
|
export { ApiNamespace } from './api.js';
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling } from './cdk/index.js';
|
|
10
|
-
export { BlocksPresets, } from './cdk/index.js';
|
|
8
|
+
export { BlocksBackend, BlocksPresets, BlocksStack, blocksNodejsBundling, DEFAULT_NODE_RUNTIME, ensureApiGatewayAccount, finalizeConfigRegistry, getConfigLocation, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
|
|
9
|
+
export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
|
|
10
|
+
export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
|
|
11
|
+
export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
|
|
12
|
+
export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
|
|
11
13
|
export { Hosting, } from './hosting.js';
|
|
14
|
+
export { EventSourceMapping } from './lambda-handler.js';
|
|
15
|
+
export { __PIPELINE_STAGE_SCOPE__, DeployStage, Pipeline, } from './pipeline/index.js';
|
|
12
16
|
export { RawRoute, RawRouteErrors, } from './raw-route.cdk.js';
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
17
|
+
export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
|
|
18
|
+
export { BLOCKS_CONFIG_PARAMETER_PREFIX, BLOCKS_SECRET_PARAMETER_PREFIX, blocksConfigParameterName, blocksSecretParameterName, } from './secret-naming.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { Scope, type ScopeOptions, type ScopeParent
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
1
|
+
export { type ConfigValue, config, isConfig, isManagedValue, isSecret, type ManagedValue, type SecretValue, secret, type ValueKind, } from '@aws-blocks/hosting';
|
|
2
|
+
export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
|
|
3
|
+
export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
|
|
4
|
+
export { type BuildingBlockMeta, Scope, type ScopeOptions, type ScopeParent } from './common/index.js';
|
|
5
|
+
export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
|
|
6
|
+
export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
|
|
7
|
+
export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
|
|
8
|
+
export { clearRouteRegistry, getRegisteredRoutes, type HttpMethod, lockRouteRegistry, matchRoute, RawRouteErrors, type RawRouteOptions, type RegisteredRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
|
|
8
9
|
export { RawRoute } from './raw-route.mock.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,WAAW,EAChB,MAAM,EACN,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,EACN,KAAK,SAAS,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EACN,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzG,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,UAAU,EACf,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,aAAa,EACb,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// secret()/config() declare markers (CDK-free subpath). The runtime getters
|
|
4
|
+
// getSecret()/getConfig() are imported from '@aws-blocks/hosting' directly —
|
|
5
|
+
// core already exports a backend `getConfig` (config loader) under this name.
|
|
6
|
+
export { config, isConfig, isManagedValue, isSecret, secret, } from '@aws-blocks/hosting';
|
|
3
7
|
export { ApiNamespace } from './api.js';
|
|
4
|
-
export {
|
|
5
|
-
export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
|
|
8
|
+
export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
|
|
6
9
|
export { Scope } from './common/index.js';
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
+
export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
|
|
11
|
+
export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
|
|
12
|
+
export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
|
|
13
|
+
export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, RawRouteErrors, registerRoute, unlockRouteRegistry, } from './raw-route.js';
|
|
10
14
|
export { RawRoute } from './raw-route.mock.js';
|
package/dist/lambda-handler.d.ts
CHANGED
|
@@ -128,4 +128,21 @@ export interface LambdaContext {
|
|
|
128
128
|
* @param backendFactory - Async function returning the backend module (typically `() => import('./index.js')`)
|
|
129
129
|
*/
|
|
130
130
|
export declare function createLambdaHandler(backendFactory: () => Promise<any>): (event: any, context?: LambdaContext) => Promise<any>;
|
|
131
|
+
/**
|
|
132
|
+
* Thrown by `initialize()` when the app has config coordinates
|
|
133
|
+
* (BLOCKS_CONFIG_BUCKET/KEY) but the S3 config load resolved empty because
|
|
134
|
+
* blocks-config.json wasn't readable yet — the transient window right after a
|
|
135
|
+
* deploy, before the BucketDeployment settles.
|
|
136
|
+
*
|
|
137
|
+
* It flows through the `createLambdaHandler()` init catch, which resets
|
|
138
|
+
* `initPromise` so the NEXT request re-runs `initialize()` and picks up the
|
|
139
|
+
* now-present config (config.ts does not cache a not-found result, so the retry
|
|
140
|
+
* re-fetches). A distinct type keeps the recovery path greppable and testable
|
|
141
|
+
* and separates it from real init failures, which surface with their own error.
|
|
142
|
+
*
|
|
143
|
+
* @internal Exported for testing only.
|
|
144
|
+
*/
|
|
145
|
+
export declare class TransientConfigError extends Error {
|
|
146
|
+
constructor();
|
|
147
|
+
}
|
|
131
148
|
//# sourceMappingURL=lambda-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lambda-handler.d.ts","sourceRoot":"","sources":["../src/lambda-handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAerD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,2BAAkC,CAAC;AAG9D;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;CAErB,CAAC;AAqCX;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAWlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAwD7C;AAiBD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,UAAU,CAWpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAIrE;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,wBAAwB,IAAI,MAAM,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"lambda-handler.d.ts","sourceRoot":"","sources":["../src/lambda-handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAerD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,2BAAkC,CAAC;AAG9D;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;CAErB,CAAC;AAqCX;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAWlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAwD7C;AAiBD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,UAAU,CAWpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAIrE;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,wBAAwB,IAAI,MAAM,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IA8CtD,OAAO,GAAG,EAAE,UAAU,aAAa,kBA4ElD;AASD;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C"}
|
package/dist/lambda-handler.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ApiError } from './errors.js';
|
|
|
6
6
|
import { BLOCKS_RPC_PREFIX } from './constants.js';
|
|
7
7
|
import { matchRoute, lockRouteRegistry } from './raw-route.js';
|
|
8
8
|
import { registerBuiltinRoutes } from './builtin-routes.js';
|
|
9
|
-
import { loadConfigToProcessEnv } from './common/config.js';
|
|
9
|
+
import { loadConfigToProcessEnv, isConfigResolved } from './common/config.js';
|
|
10
10
|
import { parseRpcRequest, successResponse, errorResponse, errorResponseFromCatch, methodNotFoundResponse, } from './rpc.js';
|
|
11
11
|
import { getCorsPatterns, isOriginAllowed, corsRejection, buildCorsHeaders, CORS_MAX_AGE } from './cors.js';
|
|
12
12
|
export { parseCorsPatterns, _resetCorsPatterns } from './cors.js';
|
|
@@ -257,6 +257,25 @@ export function createLambdaHandler(backendFactory) {
|
|
|
257
257
|
let initPromise = null;
|
|
258
258
|
async function initialize() {
|
|
259
259
|
await loadConfigToProcessEnv();
|
|
260
|
+
// If the app has config coordinates (BLOCKS_CONFIG_BUCKET/KEY set) but the
|
|
261
|
+
// load didn't actually resolve the config, we're in the transient post-deploy
|
|
262
|
+
// S3 window where blocks-config.json isn't readable yet. loadConfigFromS3()
|
|
263
|
+
// deliberately does NOT cache that empty result, so throw a typed transient
|
|
264
|
+
// error here — BEFORE importing the backend — so the createLambdaHandler()
|
|
265
|
+
// catch resets initPromise and the next request re-runs initialize(), which
|
|
266
|
+
// re-fetches from S3 and picks up the now-present config. Without this throw,
|
|
267
|
+
// initialize() would succeed with empty config, `handler` would be assigned,
|
|
268
|
+
// and the `if (!handler)` guard below would never fire again, poisoning the
|
|
269
|
+
// container for its whole life (it would import the backend against empty
|
|
270
|
+
// process.env and serve "not configured" 500s forever).
|
|
271
|
+
//
|
|
272
|
+
// A genuinely config-less app does NOT throw: the no-bucket local-dev path
|
|
273
|
+
// and a real empty `{}` config both cache their result, so isConfigResolved()
|
|
274
|
+
// is true and the retry never spins. Once the blob is readable the successful
|
|
275
|
+
// load caches, so there is no unbounded per-request S3 re-fetch either.
|
|
276
|
+
if (process.env.BLOCKS_CONFIG_BUCKET && process.env.BLOCKS_CONFIG_KEY && !isConfigResolved()) {
|
|
277
|
+
throw new TransientConfigError();
|
|
278
|
+
}
|
|
260
279
|
// Merge hosting-provided CORS origins into the main env var so the lazy
|
|
261
280
|
// getCorsPatterns() sees a combined value on first access.
|
|
262
281
|
// loadConfigToProcessEnv() won't override CORS_ALLOWED_ORIGINS if it's
|
|
@@ -276,8 +295,18 @@ export function createLambdaHandler(backendFactory) {
|
|
|
276
295
|
}
|
|
277
296
|
return async (event, context) => {
|
|
278
297
|
if (!handler) {
|
|
279
|
-
|
|
280
|
-
|
|
298
|
+
// Retry init on failure instead of caching the rejection. If initialize() throws (most often
|
|
299
|
+
// because loadConfigToProcessEnv() couldn't read blocks-config.json during the brief
|
|
300
|
+
// post-deploy window before it's readable), a memoized rejected promise would poison this
|
|
301
|
+
// container for its whole lifetime — every later request re-awaits the same rejection and 500s.
|
|
302
|
+
// Resetting initPromise lets the next invocation re-run initialize() (config.ts re-fetches,
|
|
303
|
+
// since it doesn't cache failures), so the handler self-heals once config is available.
|
|
304
|
+
if (!initPromise) {
|
|
305
|
+
initPromise = initialize().catch((err) => {
|
|
306
|
+
initPromise = null;
|
|
307
|
+
throw err;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
281
310
|
await initPromise;
|
|
282
311
|
}
|
|
283
312
|
// Only apply the HTTP timeout guard to API Gateway requests; async event
|
|
@@ -344,6 +373,26 @@ class HandlerTimeoutError extends Error {
|
|
|
344
373
|
this.name = 'HandlerTimeoutError';
|
|
345
374
|
}
|
|
346
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Thrown by `initialize()` when the app has config coordinates
|
|
378
|
+
* (BLOCKS_CONFIG_BUCKET/KEY) but the S3 config load resolved empty because
|
|
379
|
+
* blocks-config.json wasn't readable yet — the transient window right after a
|
|
380
|
+
* deploy, before the BucketDeployment settles.
|
|
381
|
+
*
|
|
382
|
+
* It flows through the `createLambdaHandler()` init catch, which resets
|
|
383
|
+
* `initPromise` so the NEXT request re-runs `initialize()` and picks up the
|
|
384
|
+
* now-present config (config.ts does not cache a not-found result, so the retry
|
|
385
|
+
* re-fetches). A distinct type keeps the recovery path greppable and testable
|
|
386
|
+
* and separates it from real init failures, which surface with their own error.
|
|
387
|
+
*
|
|
388
|
+
* @internal Exported for testing only.
|
|
389
|
+
*/
|
|
390
|
+
export class TransientConfigError extends Error {
|
|
391
|
+
constructor() {
|
|
392
|
+
super('[Blocks] Config not readable yet (transient post-deploy S3 window); will retry on next request');
|
|
393
|
+
this.name = 'TransientConfigError';
|
|
394
|
+
}
|
|
395
|
+
}
|
|
347
396
|
/**
|
|
348
397
|
* Extract the request path from a Lambda event, normalizing between
|
|
349
398
|
* API Gateway v1 (REST) and v2 (HTTP API) event shapes.
|