@aws-blocks/core 0.3.0 → 0.5.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.
Files changed (147) hide show
  1. package/README.md +25 -0
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +32 -0
  4. package/dist/api.test.js +22 -0
  5. package/dist/bb-utils.d.ts +1 -0
  6. package/dist/bb-utils.d.ts.map +1 -1
  7. package/dist/bb-utils.js +3 -0
  8. package/dist/cdk/apigateway-account.d.ts +33 -0
  9. package/dist/cdk/apigateway-account.d.ts.map +1 -0
  10. package/dist/cdk/apigateway-account.js +60 -0
  11. package/dist/cdk/blocks-backend.d.ts +11 -0
  12. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  13. package/dist/cdk/blocks-backend.js +72 -13
  14. package/dist/cdk/blocks-backend.test.js +88 -1
  15. package/dist/cdk/blocks-defaults.d.ts +76 -1
  16. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  17. package/dist/cdk/blocks-defaults.js +17 -1
  18. package/dist/cdk/blocks-defaults.test.js +17 -0
  19. package/dist/cdk/blocks-stack.test.js +44 -3
  20. package/dist/cdk/compute/compute-registry.d.ts +19 -0
  21. package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
  22. package/dist/cdk/compute/compute-registry.js +38 -0
  23. package/dist/cdk/compute/compute.d.ts +82 -2
  24. package/dist/cdk/compute/compute.d.ts.map +1 -1
  25. package/dist/cdk/compute/compute.js +64 -2
  26. package/dist/cdk/compute/default-compute-factory.d.ts +1 -0
  27. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -1
  28. package/dist/cdk/config-registry.d.ts +34 -4
  29. package/dist/cdk/config-registry.d.ts.map +1 -1
  30. package/dist/cdk/config-registry.js +83 -25
  31. package/dist/cdk/config-registry.test.d.ts +2 -0
  32. package/dist/cdk/config-registry.test.d.ts.map +1 -0
  33. package/dist/cdk/config-registry.test.js +127 -0
  34. package/dist/cdk/dashboard-registry.d.ts +41 -0
  35. package/dist/cdk/dashboard-registry.d.ts.map +1 -0
  36. package/dist/cdk/dashboard-registry.js +61 -0
  37. package/dist/cdk/index.d.ts +90 -6
  38. package/dist/cdk/index.d.ts.map +1 -1
  39. package/dist/cdk/index.js +135 -13
  40. package/dist/cdk/internal.d.ts +3 -1
  41. package/dist/cdk/internal.d.ts.map +1 -1
  42. package/dist/cdk/internal.js +4 -1
  43. package/dist/cdk/tracer-registry.d.ts +31 -0
  44. package/dist/cdk/tracer-registry.d.ts.map +1 -0
  45. package/dist/cdk/tracer-registry.js +49 -0
  46. package/dist/cdk/vpc-requirements-registry.d.ts +33 -0
  47. package/dist/cdk/vpc-requirements-registry.d.ts.map +1 -0
  48. package/dist/cdk/vpc-requirements-registry.js +46 -0
  49. package/dist/cdk/vpc-types.d.ts +151 -0
  50. package/dist/cdk/vpc-types.d.ts.map +1 -0
  51. package/dist/cdk/vpc-types.js +3 -0
  52. package/dist/cdk/vpc.d.ts +59 -0
  53. package/dist/cdk/vpc.d.ts.map +1 -0
  54. package/dist/cdk/vpc.js +298 -0
  55. package/dist/cdk/vpc.test.d.ts +2 -0
  56. package/dist/cdk/vpc.test.d.ts.map +1 -0
  57. package/dist/cdk/vpc.test.js +285 -0
  58. package/dist/client/index.d.ts +1 -1
  59. package/dist/client/index.d.ts.map +1 -1
  60. package/dist/client/index.js +1 -1
  61. package/dist/common/config.d.ts +34 -0
  62. package/dist/common/config.d.ts.map +1 -1
  63. package/dist/common/config.js +45 -3
  64. package/dist/common/config.test.js +19 -0
  65. package/dist/common/index.d.ts +8 -0
  66. package/dist/common/index.d.ts.map +1 -1
  67. package/dist/errors.d.ts +21 -0
  68. package/dist/errors.d.ts.map +1 -1
  69. package/dist/errors.js +25 -0
  70. package/dist/hosting.d.ts +9 -0
  71. package/dist/hosting.d.ts.map +1 -1
  72. package/dist/hosting.js +12 -1
  73. package/dist/hosting.test.js +63 -1
  74. package/dist/index.cdk.d.ts +3 -2
  75. package/dist/index.cdk.d.ts.map +1 -1
  76. package/dist/index.cdk.js +2 -2
  77. package/dist/index.d.ts +1 -1
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +1 -1
  80. package/dist/lambda-handler.d.ts +17 -0
  81. package/dist/lambda-handler.d.ts.map +1 -1
  82. package/dist/lambda-handler.js +61 -5
  83. package/dist/lambda-handler.test.js +191 -2
  84. package/dist/raw-route.d.ts +15 -1
  85. package/dist/raw-route.d.ts.map +1 -1
  86. package/dist/raw-route.js +96 -12
  87. package/dist/raw-route.test.js +332 -1
  88. package/dist/scripts/dev-server.d.ts.map +1 -1
  89. package/dist/scripts/dev-server.js +11 -0
  90. package/dist/scripts/extract-ts-types.d.ts.map +1 -1
  91. package/dist/scripts/extract-ts-types.js +107 -23
  92. package/dist/scripts/extract-ts-types.test.js +225 -26
  93. package/dist/scripts/generate-spec.d.ts.map +1 -1
  94. package/dist/scripts/generate-spec.js +14 -5
  95. package/dist/scripts/generate-spec.test.js +93 -0
  96. package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
  97. package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
  98. package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
  99. package/dist/scripts/sandbox.d.ts +54 -0
  100. package/dist/scripts/sandbox.d.ts.map +1 -1
  101. package/dist/scripts/sandbox.js +163 -25
  102. package/dist/version.d.ts +1 -1
  103. package/dist/version.js +1 -1
  104. package/package.json +10 -1
  105. package/src/api.test.ts +25 -0
  106. package/src/api.ts +39 -0
  107. package/src/bb-utils.ts +3 -0
  108. package/src/cdk/apigateway-account.ts +66 -0
  109. package/src/cdk/blocks-backend.test.ts +168 -60
  110. package/src/cdk/blocks-backend.ts +299 -227
  111. package/src/cdk/blocks-defaults.test.ts +21 -0
  112. package/src/cdk/blocks-defaults.ts +79 -1
  113. package/src/cdk/blocks-stack.test.ts +57 -13
  114. package/src/cdk/compute/compute-registry.ts +45 -0
  115. package/src/cdk/compute/compute.ts +114 -2
  116. package/src/cdk/compute/default-compute-factory.ts +1 -0
  117. package/src/cdk/config-registry.test.ts +149 -0
  118. package/src/cdk/config-registry.ts +92 -34
  119. package/src/cdk/dashboard-registry.ts +68 -0
  120. package/src/cdk/index.ts +427 -256
  121. package/src/cdk/internal.ts +6 -2
  122. package/src/cdk/tracer-registry.ts +54 -0
  123. package/src/cdk/vpc-requirements-registry.ts +63 -0
  124. package/src/cdk/vpc-types.ts +158 -0
  125. package/src/cdk/vpc.test.ts +348 -0
  126. package/src/cdk/vpc.ts +336 -0
  127. package/src/client/index.ts +1 -1
  128. package/src/common/config.test.ts +21 -0
  129. package/src/common/config.ts +47 -3
  130. package/src/common/index.ts +8 -0
  131. package/src/errors.ts +26 -0
  132. package/src/hosting.test.ts +87 -1
  133. package/src/hosting.ts +24 -1
  134. package/src/index.cdk.ts +11 -1
  135. package/src/index.ts +1 -1
  136. package/src/lambda-handler.test.ts +220 -2
  137. package/src/lambda-handler.ts +65 -4
  138. package/src/raw-route.test.ts +427 -1
  139. package/src/raw-route.ts +125 -12
  140. package/src/scripts/dev-server.ts +12 -1
  141. package/src/scripts/extract-ts-types.test.ts +228 -26
  142. package/src/scripts/extract-ts-types.ts +104 -20
  143. package/src/scripts/generate-spec.test.ts +101 -0
  144. package/src/scripts/generate-spec.ts +15 -5
  145. package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
  146. package/src/scripts/sandbox.ts +185 -24
  147. package/src/version.ts +1 -1
@@ -53,9 +53,13 @@ async function loadConfigFromS3() {
53
53
  || error?.Code === 'NoSuchKey'
54
54
  || error?.$metadata?.httpStatusCode === 404;
55
55
  if (isNotFound) {
56
- console.warn(`[Blocks] Config file not found in S3 (${bucket}/${key}), proceeding with empty config`);
57
- configCache = {};
58
- return configCache;
56
+ // Do NOT cache the empty result: a 404 right after deploy is usually the config file
57
+ // not being readable yet (the transient window before the BucketDeployment settles), not
58
+ // a genuinely config-less app. Caching {} here would poison the container for its whole
59
+ // lifetime. Returning without caching lets the next invocation re-fetch and pick up the
60
+ // real config once it's present. (Apps that truly have no config just re-check cheaply.)
61
+ console.warn(`[Blocks] Config file not found in S3 (${bucket}/${key}), proceeding with empty config (will retry on next request)`);
62
+ return {};
59
63
  }
60
64
  const msg = error instanceof Error ? error.message : String(error);
61
65
  throw new Error(`[Blocks] Failed to load config from S3 (${bucket}/${key}): ${msg}`);
@@ -138,6 +142,44 @@ export async function loadConfigToProcessEnv() {
138
142
  }
139
143
  }
140
144
  }
145
+ /**
146
+ * Whether a config load has resolved and cached a result.
147
+ *
148
+ * Returns `true` once `loadConfigFromS3()` has cached a config object — either a
149
+ * successful S3 load, a genuinely-empty `{}` config, or the no-bucket local-dev
150
+ * path (both of which cache `{}`). Returns `false` in the initial state AND
151
+ * after a transient not-found miss: the 404 path deliberately does NOT cache its
152
+ * empty result (see `loadConfigFromS3`), so a `false` return immediately after
153
+ * awaiting a load uniquely identifies the post-deploy S3 window where
154
+ * blocks-config.json isn't readable yet.
155
+ *
156
+ * Callers use this to tell a transient-empty load (retry on the next request)
157
+ * apart from a legitimately-config-less app (do nothing) without reaching into
158
+ * the module's private cache.
159
+ */
160
+ export function isConfigResolved() {
161
+ return configCache !== null;
162
+ }
163
+ /**
164
+ * Sanitize a Blocks identifier into a valid config/env-var key segment:
165
+ * uppercase, with every non-alphanumeric character replaced by `_`.
166
+ *
167
+ * Config entries are loaded into `process.env` at runtime (see
168
+ * {@link loadConfigToProcessEnv}), so a key must be a valid env-var name. This
169
+ * is the single rule both sides of a key share: whoever *writes* a config key
170
+ * (`registerConfig(\`PREFIX_${sanitizeConfigKey(id)}\`, …)`) and whoever *reads*
171
+ * it (`getConfigSync(\`PREFIX_${sanitizeConfigKey(id)}\`)`) MUST go through this
172
+ * function so they reconstruct a byte-identical key — a hand-inlined variant
173
+ * that drifts silently misses the lookup at runtime.
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * registerConfig(this, `BLOCKS_QUEUE_URL_${sanitizeConfigKey(this.fullId)}`, url);
178
+ * ```
179
+ */
180
+ export function sanitizeConfigKey(id) {
181
+ return id.toUpperCase().replace(/[^A-Z0-9]/g, '_');
182
+ }
141
183
  /**
142
184
  * Reset the config cache. **For testing only.**
143
185
  */
@@ -101,6 +101,25 @@ describe('config — S3 errors', () => {
101
101
  const result = await getConfig('ANY_KEY');
102
102
  assert.strictEqual(result, '', 'Should return empty string (empty config) on 404');
103
103
  });
104
+ it('does not cache a not-found result — a later call re-fetches once the config exists', async () => {
105
+ process.env.BLOCKS_CONFIG_BUCKET = 'test-bucket';
106
+ process.env.BLOCKS_CONFIG_KEY = 'blocks-config.json';
107
+ // Simulate the transient post-deploy window: the object isn't written yet on the first
108
+ // fetch, then becomes available. A not-found result must NOT be cached, or the container
109
+ // would be poisoned with empty config for its lifetime.
110
+ let call = 0;
111
+ const notFound = new Error('The specified key does not exist.');
112
+ notFound.name = 'NoSuchKey';
113
+ _setS3Fetcher(async () => {
114
+ call++;
115
+ if (call === 1)
116
+ throw notFound;
117
+ return JSON.stringify({ READY_KEY: 'ready-value' });
118
+ });
119
+ assert.strictEqual(await getConfig('READY_KEY'), '', 'first call sees the not-yet-written config as empty');
120
+ assert.strictEqual(await getConfig('READY_KEY'), 'ready-value', 'later call re-fetches once the config exists');
121
+ assert.strictEqual(call, 2, 'a not-found result must not be cached — S3 is retried');
122
+ });
104
123
  it('throws with clear message on S3 network error', async () => {
105
124
  process.env.BLOCKS_CONFIG_BUCKET = 'test-bucket';
106
125
  process.env.BLOCKS_CONFIG_KEY = 'blocks-config.json';
@@ -224,6 +224,14 @@ export interface BlocksStackProps extends StackProps {
224
224
  }
225
225
  export declare class BlocksStack {
226
226
  readonly id: string;
227
+ /**
228
+ * Path to the app's backend module (`props.backendCDKPath`). A typed contract for Building
229
+ * Blocks that co-bundle the backend at synth time and read it off
230
+ * `globalThis.CURRENT_BLOCKS_STACK` (the CDK `BlocksStack` / `BlocksBackend` set it). Declared
231
+ * here so those consumers can type against this shared `BlocksStack` contract — which the CDK
232
+ * `BlocksStack` / `BlocksBackend` implements — instead of `any`.
233
+ */
234
+ readonly backendModulePath: string;
227
235
  constructor(scope: Construct, id: string, props: BlocksStackProps);
228
236
  }
229
237
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsGG;AAEH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,KAAK;IAChB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,MAAM,EAAE,WAAW,CAAC;IAEpC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8D;gBAE5E,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;IAiB9C;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,IAAI;QAAE,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAejI,2CAA2C;IAC3C,MAAM,CAAC,cAAc,IAAI,IAAI;IAI7B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAOlH;;;;OAIG;IACH,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIxD;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIrD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;CAapD;AA0CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,UAUrE;AAED,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,WAAW;IACvB,SAAgB,EAAE,EAAE,MAAM,CAAC;gBACf,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB;CAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsGG;AAEH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,KAAK;IAChB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,MAAM,EAAE,WAAW,CAAC;IAEpC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8D;gBAE5E,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;IAiB9C;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,IAAI;QAAE,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAejI,2CAA2C;IAC3C,MAAM,CAAC,cAAc,IAAI,IAAI;IAI7B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAOlH;;;;OAIG;IACH,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIxD;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIrD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;CAapD;AA0CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,UAUrE;AAED,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,WAAW;IACvB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,SAAiB,iBAAiB,EAAE,MAAM,CAAC;gBAC/B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB;CAGjE"}
package/dist/errors.d.ts CHANGED
@@ -44,6 +44,11 @@ export declare class ApiError extends Error {
44
44
  * when `retriable === true`; non-retriable errors (expired session,
45
45
  * tampered envelope, too-many-attempts lockouts) require restarting the
46
46
  * flow. Defaults to `false` when unspecified.
47
+ *
48
+ * This marks whether the *kind* of failure is retriable in principle, not a
49
+ * guarantee that a given retry will succeed — e.g. an optimistic-lock
50
+ * conflict against a missing row is flagged retriable, yet a blind retry
51
+ * fails identically.
47
52
  */
48
53
  readonly retriable: boolean;
49
54
  constructor(message: string, status: number, options?: {
@@ -70,6 +75,22 @@ export declare class ApiError extends Error {
70
75
  export declare function isBlocksError<N extends string>(e: unknown, name: N): e is Error & {
71
76
  name: N;
72
77
  };
78
+ /**
79
+ * Build a named `Error` whose `name` is a BB error constant, so it is matchable
80
+ * with {@link isBlocksError} on both server and client. The name is also
81
+ * prefixed into the message for readable logs.
82
+ *
83
+ * This is the producer half of the {@link isBlocksError} contract: throw via
84
+ * this helper so the `name` a consumer matches on is set consistently. It has
85
+ * no runtime dependencies, so it is safe to use in every bundle — mock,
86
+ * aws-runtime, and CDK synth.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * throw blocksError(KVStoreErrors.ConditionalCheckFailed, 'Key already exists');
91
+ * ```
92
+ */
93
+ export declare function blocksError(name: string, message: string): Error;
73
94
  /**
74
95
  * Type guard for branching on a failed `AuthState` (the recommended
75
96
  * `setAuthState` client path) by its structured `errorName`.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAM9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAE7F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,SAAS,MAAM,EAC9E,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3B,IAAI,EAAE,CAAC,GACL,KAAK,IAAI,CAAC,GAAG;IAAE,SAAS,EAAE,CAAC,CAAA;CAAE,CAE/B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAM9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAE7F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAIhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,SAAS,MAAM,EAC9E,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3B,IAAI,EAAE,CAAC,GACL,KAAK,IAAI,CAAC,GAAG;IAAE,SAAS,EAAE,CAAC,CAAA;CAAE,CAE/B"}
package/dist/errors.js CHANGED
@@ -46,6 +46,11 @@ export class ApiError extends Error {
46
46
  * when `retriable === true`; non-retriable errors (expired session,
47
47
  * tampered envelope, too-many-attempts lockouts) require restarting the
48
48
  * flow. Defaults to `false` when unspecified.
49
+ *
50
+ * This marks whether the *kind* of failure is retriable in principle, not a
51
+ * guarantee that a given retry will succeed — e.g. an optimistic-lock
52
+ * conflict against a missing row is flagged retriable, yet a blind retry
53
+ * fails identically.
49
54
  */
50
55
  retriable;
51
56
  constructor(message, status, options) {
@@ -73,6 +78,26 @@ export class ApiError extends Error {
73
78
  export function isBlocksError(e, name) {
74
79
  return e instanceof Error && e.name === name;
75
80
  }
81
+ /**
82
+ * Build a named `Error` whose `name` is a BB error constant, so it is matchable
83
+ * with {@link isBlocksError} on both server and client. The name is also
84
+ * prefixed into the message for readable logs.
85
+ *
86
+ * This is the producer half of the {@link isBlocksError} contract: throw via
87
+ * this helper so the `name` a consumer matches on is set consistently. It has
88
+ * no runtime dependencies, so it is safe to use in every bundle — mock,
89
+ * aws-runtime, and CDK synth.
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * throw blocksError(KVStoreErrors.ConditionalCheckFailed, 'Key already exists');
94
+ * ```
95
+ */
96
+ export function blocksError(name, message) {
97
+ const err = new Error(`${name}: ${message}`);
98
+ err.name = name;
99
+ return err;
100
+ }
76
101
  /**
77
102
  * Type guard for branching on a failed `AuthState` (the recommended
78
103
  * `setAuthState` client path) by its structured `errorName`.
package/dist/hosting.d.ts CHANGED
@@ -209,6 +209,15 @@ export interface HostingProps {
209
209
  waf?: HostingWafConfig;
210
210
  /** Retain the S3 bucket when the stack is deleted. Default: false. */
211
211
  retainOnDelete?: boolean;
212
+ /**
213
+ * Days to retain SUPERSEDED build artifacts in S3 before they are expired.
214
+ * Default: 30. The build currently referenced by CloudFront KVS (`meta.b`)
215
+ * is NEVER expired regardless of this value — only builds that have been
216
+ * replaced by a newer deploy are cleaned up (issue #480). Raise this to keep
217
+ * a longer rollback window. Must be at least `skewProtection.maxAge`
218
+ * (converted to days), or synth throws `InvalidSkewProtectionMaxAgeError`.
219
+ */
220
+ buildRetentionDays?: number;
212
221
  /** Custom Content-Security-Policy header value. */
213
222
  contentSecurityPolicy?: string;
214
223
  /** CloudFront price class. Default: PRICE_CLASS_100. */
@@ -1 +1 @@
1
- {"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,aAAa,EAAE,gBAAgB,EAAiB,MAAM,gCAAgC,CAAC;AACrH,OAAO,EAAmB,KAAK,kBAAkB,EAAiC,MAAM,8BAA8B,CAAC;AACvH,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAInC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,QAAQ,EAMd,MAAM,sBAAsB,CAAC;AAK9B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;IAChC,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE;QAClB,2EAA2E;QAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IACF,uEAAuE;IACvE,YAAY,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;CAC3C,CAAC;AAEF,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;AAE3F;;;;;;;GAOG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,mBAAmB,EAAE,YAAY,CAAC;IAC5E,8FAA8F;IAC9F,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,mHAAmH;IACnH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAE3B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,mEAAmE;IACnE,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IAErB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGxC,uDAAuD;IACvD,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,oCAAoC;IACpC,GAAG,CAAC,EAAE,gBAAgB,CAAC;IAEvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,wDAAwD;IACxD,UAAU,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC;IAE3C;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;QAChC,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;;;;;WAQG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAEF;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,0EAA0E;QAC1E,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;KAC7B,CAAC;IAEF;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE;QACX,iEAAiE;QACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iEAAiE;QACjE,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,wCAAwC;QACxC,OAAO,EAAE,OAAO,CAAC;QACjB,+CAA+C;QAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,6DAA6D;QAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,2CAA2C;IAC3C,SAAgB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IAC1C,mCAAmC;IACnC,SAAgB,YAAY,EAAE,GAAG,CAAC,cAAc,CAAC,YAAY,CAAC;IAC9D,yDAAyD;IACzD,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,SAAgB,WAAW,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;IACtD,wFAAwF;IACxF,SAAgB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IACrD,2FAA2F;IAC3F,SAAgB,eAAe,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAErD;;;;;;;;;;;;;;;;;OAiBG;WACU,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAaxF;;;;;OAKG;gBACS,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,eAAe,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa;IA8U/G;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,eAAe;CAwDxB"}
package/dist/hosting.js CHANGED
@@ -267,7 +267,16 @@ export class Hosting extends Construct {
267
267
  environment: Object.keys(plainEnv).length ? plainEnv : undefined,
268
268
  domain: resolvedDomain,
269
269
  waf: props.waf,
270
- storage: props.retainOnDelete != null ? { retainOnDelete: props.retainOnDelete } : undefined,
270
+ storage: props.retainOnDelete != null || props.buildRetentionDays != null
271
+ ? {
272
+ ...(props.retainOnDelete != null
273
+ ? { retainOnDelete: props.retainOnDelete }
274
+ : {}),
275
+ ...(props.buildRetentionDays != null
276
+ ? { buildRetentionDays: props.buildRetentionDays }
277
+ : {}),
278
+ }
279
+ : undefined,
271
280
  cdn: props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas
272
281
  ? {
273
282
  contentSecurityPolicy: props.contentSecurityPolicy,
@@ -356,6 +365,8 @@ export class Hosting extends Construct {
356
365
  for (const dep of assetDeployments) {
357
366
  configDeployment.node.addDependency(dep);
358
367
  }
368
+ // Delay route cutover until resolved config replaces the static placeholder.
369
+ hosting.addBuildAssetDependency(configDeployment);
359
370
  }
360
371
  // ── 9. Register public origin + CORS hosting origin into S3 config ──
361
372
  // The Handler no longer depends on the BucketDeployment, so including
@@ -12,7 +12,7 @@ import { App, Duration, Stack } from 'aws-cdk-lib';
12
12
  import { Match, Template } from 'aws-cdk-lib/assertions';
13
13
  import { BLOCKS_RPC_PREFIX } from './constants.js';
14
14
  import { Hosting } from './hosting.js';
15
- import { clearRouteRegistry, registerRoute } from './raw-route.js';
15
+ import { clearRouteRegistry, compilePath, registerRoute } from './raw-route.js';
16
16
  // ================================================================
17
17
  // Hosting construct tests
18
18
  //
@@ -372,6 +372,30 @@ describe('Hosting', () => {
372
372
  }
373
373
  assert.ok(foundRetain, 'At least one bucket should have Retain deletion policy');
374
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
+ });
375
399
  });
376
400
  // ── API integration tests ────────────────────────────────────
377
401
  describe('API integration', () => {
@@ -528,6 +552,38 @@ describe('Hosting', () => {
528
552
  assert.ok(patterns.includes('/health'), 'Should have /health behavior for RawRoute');
529
553
  assert.ok(patterns.includes('/users/*'), 'Should have /users/* behavior for parameterized RawRoute');
530
554
  });
555
+ it('adds a CloudFront behavior for a route another core copy registered', () => {
556
+ createSpaBuildOutput(tmpDir);
557
+ // Synth has the same split as dispatch: a bundle can hold more than one
558
+ // copy of @aws-blocks/core, and this loop reads whichever route table
559
+ // its own copy owns. Register the way a second copy would — straight
560
+ // into the shared state, not through this copy's registerRoute() — and
561
+ // the behavior must still be emitted. With a module-local registry it
562
+ // never is, and the route 404s at CloudFront before Lambda is reached.
563
+ const REGISTRY_KEY = '__AWS_BLOCKS_RAW_ROUTE_REGISTRY_V1__';
564
+ const state = globalThis[REGISTRY_KEY];
565
+ assert.ok(state, `route registry state must live on globalThis['${REGISTRY_KEY}']`);
566
+ const { pattern, paramNames } = compilePath('/from-other-copy');
567
+ state.routes.push({
568
+ method: 'GET',
569
+ path: '/from-other-copy',
570
+ pattern,
571
+ paramNames,
572
+ handler: async () => { },
573
+ });
574
+ const app = new App();
575
+ const stack = new Stack(app, 'ForeignRouteBehaviorStack');
576
+ new Hosting(stack, 'Hosting', {
577
+ root: tmpDir,
578
+ api: MOCK_API,
579
+ });
580
+ const template = Template.fromStack(stack);
581
+ const distributions = template.findResources('AWS::CloudFront::Distribution');
582
+ const distKeys = Object.keys(distributions);
583
+ const distConfig = distributions[distKeys[0]].Properties.DistributionConfig;
584
+ const patterns = (distConfig.CacheBehaviors ?? []).map((b) => b.PathPattern);
585
+ assert.ok(patterns.includes('/from-other-copy'), `synth must see routes from every core copy, got: ${JSON.stringify(patterns)}`);
586
+ });
531
587
  it('proxies the reserved /aws-blocks/auth subtree with a single behavior', () => {
532
588
  createSpaBuildOutput(tmpDir);
533
589
  // A route under the auth subtree (as the auth runtime would mount) must
@@ -1158,6 +1214,12 @@ describe('Hosting', () => {
1158
1214
  const assetDeps = dependsOn.filter((d) => /AssetDeployment/.test(d));
1159
1215
  assert.ok(assetDeps.length >= 1, `BlocksConfigDeployment must DependsOn the asset deployment(s); ` +
1160
1216
  `found DependsOn=${JSON.stringify(dependsOn)}`);
1217
+ const routeCutoverId = Object.entries(tpl.Resources).find(([id, resource]) => resource.Type === 'AWS::CloudFormation::CustomResource' &&
1218
+ /RouteStoreKeys/.test(id))?.[0];
1219
+ assert.ok(routeCutoverId, 'expected a RouteStoreKeys cutover resource');
1220
+ const routeCutoverDeps = tpl.Resources[routeCutoverId].DependsOn ?? [];
1221
+ assert.ok(routeCutoverDeps.includes(configId), `RouteStoreKeys must DependsOn BlocksConfigDeployment; ` +
1222
+ `found DependsOn=${JSON.stringify(routeCutoverDeps)}`);
1161
1223
  });
1162
1224
  });
1163
1225
  // ── value() integration ─────────────────────────────────────────
@@ -1,11 +1,12 @@
1
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
2
  export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
3
- export { BlocksBackend, type BlocksBackendProps, type BlocksDefaults, BlocksPresets, BlocksStack, blocksNodejsBundling, type CoreBlocksBackendProps, type CoreBlocksStackProps, DEFAULT_NODE_RUNTIME, finalizeConfigRegistry, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
3
+ export type { BlocksVpcOptions, ScopeOptions, SubnetRole, VpcContext, VpcRequirements } from './cdk/index.js';
4
+ export { BlocksBackend, type BlocksBackendProps, type BlocksDefaults, BlocksPresets, BlocksStack, type BlocksThrottling, BuildingBlockScope, blocksNodejsBundling, type CoreBlocksBackendProps, type CoreBlocksStackProps, DEFAULT_NODE_RUNTIME, ensureApiGatewayAccount, finalizeConfigRegistry, finalizeDashboards, finalizeTracing, getConfigLocation, getVpcContext, registerConfig, registerDashboardFinalizer, registerTracer, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
4
5
  export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
5
6
  export { BlocksStackProps } from './common/index.js';
6
7
  export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
7
8
  export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
8
- export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
9
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
9
10
  export { type BlocksStackApi, type ComputeConfig, type FrameworkType, Hosting, type HostingDomainConfig, type HostingProps, type HostingWafConfig, } from './hosting.js';
10
11
  export { EventSourceMapping } from './lambda-handler.js';
11
12
  export { __PIPELINE_STAGE_SCOPE__, type BranchConfig, DeployStage, type DeployStageProps, Pipeline, type PipelineProps, type PipelineSourceConfig, type PipelineStageConfig, type PipelineSynthConfig, } from './pipeline/index.js';
@@ -1 +1 @@
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,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,sBAAsB,EACtB,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,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5F,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"}
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,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EACN,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,aAAa,EACb,WAAW,EACX,KAAK,gBAAgB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,0BAA0B,EAC1B,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
@@ -5,11 +5,11 @@
5
5
  // getConfig (backend config loader, above) owns that name here.
6
6
  export { config, DEFAULT_CONFIG_PARAMETER_PREFIX, DEFAULT_SECRET_PARAMETER_PREFIX, isConfig, isManagedValue, isSecret, secret, } from '@aws-blocks/hosting';
7
7
  export { ApiNamespace } from './api.js';
8
- export { BlocksBackend, BlocksPresets, BlocksStack, blocksNodejsBundling, DEFAULT_NODE_RUNTIME, finalizeConfigRegistry, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
8
+ export { BlocksBackend, BlocksPresets, BlocksStack, BuildingBlockScope, blocksNodejsBundling, DEFAULT_NODE_RUNTIME, ensureApiGatewayAccount, finalizeConfigRegistry, finalizeDashboards, finalizeTracing, getConfigLocation, getVpcContext, registerConfig, registerDashboardFinalizer, registerTracer, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
9
9
  export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
10
10
  export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
11
11
  export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
12
- export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
12
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
13
13
  export { Hosting, } from './hosting.js';
14
14
  export { EventSourceMapping } from './lambda-handler.js';
15
15
  export { __PIPELINE_STAGE_SCOPE__, DeployStage, Pipeline, } from './pipeline/index.js';
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, pr
4
4
  export { type BuildingBlockMeta, Scope, type ScopeOptions, type ScopeParent } from './common/index.js';
5
5
  export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
6
6
  export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
7
- export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
7
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
8
8
  export { clearRouteRegistry, getRegisteredRoutes, type HttpMethod, lockRouteRegistry, matchRoute, RawRouteErrors, type RawRouteOptions, type RegisteredRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
9
9
  export { RawRoute } from './raw-route.mock.js';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5F,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"}
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
@@ -9,6 +9,6 @@ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, pr
9
9
  export { Scope } from './common/index.js';
10
10
  export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
11
11
  export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
12
- export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
12
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
13
13
  export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, RawRouteErrors, registerRoute, unlockRouteRegistry, } from './raw-route.js';
14
14
  export { RawRoute } from './raw-route.mock.js';
@@ -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,IA0BtD,OAAO,GAAG,EAAE,UAAU,aAAa,kBAiElD"}
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"}
@@ -4,9 +4,9 @@
4
4
  import { AsyncLocalStorage } from 'node:async_hooks';
5
5
  import { ApiError } from './errors.js';
6
6
  import { BLOCKS_RPC_PREFIX } from './constants.js';
7
- import { matchRoute, lockRouteRegistry } from './raw-route.js';
7
+ import { matchRoute, lockRouteRegistry, getRegisteredRoutes, getLoadedCoreCopies } 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
- if (!initPromise)
280
- initPromise = initialize();
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.
@@ -420,8 +469,15 @@ function createHandler(backend) {
420
469
  if (matched) {
421
470
  return handleRawRoute(event, matched.route, matched.params, corsHeaders, signal);
422
471
  }
423
- // No RawRoute matched and path is not the RPC endpoint — return 404
472
+ // No RawRoute matched and path is not the RPC endpoint — return 404.
473
+ // Log it: an unmatched route used to be entirely silent, which is what
474
+ // made a split route registry (duplicate @aws-blocks/core copies)
475
+ // undiagnosable from CloudWatch alone. The path is already in the API
476
+ // Gateway access logs, so this adds no new category of data.
424
477
  if (!requestPath.startsWith(BLOCKS_RPC_PREFIX)) {
478
+ const copies = getLoadedCoreCopies();
479
+ const copiesNote = copies > 1 ? ` — ${copies} copies of @aws-blocks/core are loaded` : '';
480
+ console.error(`No RawRoute matched ${httpMethod} ${requestPath} (${getRegisteredRoutes().length} routes registered)${copiesNote}`);
425
481
  return {
426
482
  statusCode: 404,
427
483
  headers: {