@aws-blocks/core 0.4.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 (90) hide show
  1. package/README.md +12 -0
  2. package/dist/cdk/blocks-backend.d.ts +4 -1
  3. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  4. package/dist/cdk/blocks-backend.js +56 -10
  5. package/dist/cdk/blocks-backend.test.js +71 -1
  6. package/dist/cdk/blocks-defaults.d.ts +11 -0
  7. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  8. package/dist/cdk/blocks-stack.test.js +23 -3
  9. package/dist/cdk/compute/compute.d.ts +80 -2
  10. package/dist/cdk/compute/compute.d.ts.map +1 -1
  11. package/dist/cdk/compute/compute.js +57 -3
  12. package/dist/cdk/config-registry.test.js +12 -0
  13. package/dist/cdk/dashboard-registry.d.ts +41 -0
  14. package/dist/cdk/dashboard-registry.d.ts.map +1 -0
  15. package/dist/cdk/dashboard-registry.js +61 -0
  16. package/dist/cdk/index.d.ts +67 -11
  17. package/dist/cdk/index.d.ts.map +1 -1
  18. package/dist/cdk/index.js +100 -18
  19. package/dist/cdk/internal.d.ts +3 -1
  20. package/dist/cdk/internal.d.ts.map +1 -1
  21. package/dist/cdk/internal.js +4 -1
  22. package/dist/cdk/tracer-registry.d.ts +31 -0
  23. package/dist/cdk/tracer-registry.d.ts.map +1 -0
  24. package/dist/cdk/tracer-registry.js +49 -0
  25. package/dist/cdk/vpc-requirements-registry.d.ts +33 -0
  26. package/dist/cdk/vpc-requirements-registry.d.ts.map +1 -0
  27. package/dist/cdk/vpc-requirements-registry.js +46 -0
  28. package/dist/cdk/vpc-types.d.ts +151 -0
  29. package/dist/cdk/vpc-types.d.ts.map +1 -0
  30. package/dist/cdk/vpc-types.js +3 -0
  31. package/dist/cdk/vpc.d.ts +59 -0
  32. package/dist/cdk/vpc.d.ts.map +1 -0
  33. package/dist/cdk/vpc.js +298 -0
  34. package/dist/cdk/vpc.test.d.ts +2 -0
  35. package/dist/cdk/vpc.test.d.ts.map +1 -0
  36. package/dist/cdk/vpc.test.js +285 -0
  37. package/dist/errors.d.ts +5 -0
  38. package/dist/errors.d.ts.map +1 -1
  39. package/dist/errors.js +5 -0
  40. package/dist/hosting.d.ts.map +1 -1
  41. package/dist/hosting.js +2 -0
  42. package/dist/hosting.test.js +39 -1
  43. package/dist/index.cdk.d.ts +2 -1
  44. package/dist/index.cdk.d.ts.map +1 -1
  45. package/dist/index.cdk.js +1 -1
  46. package/dist/lambda-handler.js +9 -2
  47. package/dist/lambda-handler.test.js +61 -1
  48. package/dist/raw-route.d.ts +15 -1
  49. package/dist/raw-route.d.ts.map +1 -1
  50. package/dist/raw-route.js +96 -12
  51. package/dist/raw-route.test.js +332 -1
  52. package/dist/scripts/dev-server.d.ts.map +1 -1
  53. package/dist/scripts/dev-server.js +11 -0
  54. package/dist/scripts/extract-ts-types.d.ts.map +1 -1
  55. package/dist/scripts/extract-ts-types.js +107 -23
  56. package/dist/scripts/extract-ts-types.test.js +225 -26
  57. package/dist/scripts/generate-spec.d.ts.map +1 -1
  58. package/dist/scripts/generate-spec.js +14 -5
  59. package/dist/scripts/generate-spec.test.js +93 -0
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.js +1 -1
  62. package/package.json +8 -1
  63. package/src/cdk/blocks-backend.test.ts +144 -60
  64. package/src/cdk/blocks-backend.ts +298 -239
  65. package/src/cdk/blocks-defaults.ts +12 -0
  66. package/src/cdk/blocks-stack.test.ts +32 -13
  67. package/src/cdk/compute/compute.ts +105 -3
  68. package/src/cdk/config-registry.test.ts +14 -0
  69. package/src/cdk/dashboard-registry.ts +68 -0
  70. package/src/cdk/index.ts +426 -298
  71. package/src/cdk/internal.ts +6 -2
  72. package/src/cdk/tracer-registry.ts +54 -0
  73. package/src/cdk/vpc-requirements-registry.ts +63 -0
  74. package/src/cdk/vpc-types.ts +158 -0
  75. package/src/cdk/vpc.test.ts +348 -0
  76. package/src/cdk/vpc.ts +336 -0
  77. package/src/errors.ts +5 -0
  78. package/src/hosting.test.ts +59 -1
  79. package/src/hosting.ts +3 -0
  80. package/src/index.cdk.ts +7 -0
  81. package/src/lambda-handler.test.ts +79 -1
  82. package/src/lambda-handler.ts +11 -2
  83. package/src/raw-route.test.ts +427 -1
  84. package/src/raw-route.ts +125 -12
  85. package/src/scripts/dev-server.ts +12 -1
  86. package/src/scripts/extract-ts-types.test.ts +228 -26
  87. package/src/scripts/extract-ts-types.ts +104 -20
  88. package/src/scripts/generate-spec.test.ts +101 -0
  89. package/src/scripts/generate-spec.ts +15 -5
  90. package/src/version.ts +1 -1
@@ -16,6 +16,7 @@ import { join, dirname } from 'path';
16
16
  import { tmpdir } from 'os';
17
17
  import { fileURLToPath, pathToFileURL } from 'url';
18
18
  import { generateSpec } from './generate-spec.js';
19
+ import { ApiNamespace } from '../api.js';
19
20
 
20
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
21
22
  // `import.meta.url` lives in dist/scripts/, so `..` is dist/ and the built
@@ -97,3 +98,103 @@ describe('generateSpec — @blocksSkipCodegen', () => {
97
98
  }
98
99
  });
99
100
  });
101
+
102
+ describe('generateSpec — namespaces sharing a method name (regression: #445)', () => {
103
+ it('emits each namespace its OWN schema for a same-named method', async () => {
104
+ const dir = join(tmpdir(), `blocks-spec-test-${Date.now()}-collision`);
105
+ mkdirSync(dir, { recursive: true });
106
+ writeTsconfig(dir);
107
+
108
+ // Typed foundation for the TS type extractor. `create` has DIFFERENT params
109
+ // in each namespace; binding names (widgets/subscriptions) match the runtime
110
+ // module's export names below.
111
+ writeFileSync(join(dir, 'index.ts'), `
112
+ interface ApiNamespaceConstructor { new <T>(scope: any, name: string, handler: (ctx: any) => T): T; }
113
+ const ApiNamespace: ApiNamespaceConstructor = class {} as any;
114
+
115
+ export const widgets = new ApiNamespace(null, 'widgets', () => ({
116
+ async create(label: string): Promise<{ widgetId: string }> { return { widgetId: 'w' }; },
117
+ }));
118
+ export const subscriptions = new ApiNamespace(null, 'subscriptions', () => ({
119
+ async create(topicCount: number): Promise<{ subId: number }> { return { subId: 1 }; },
120
+ }));
121
+ `);
122
+
123
+ // Runtime module for namespace discovery (types come from the .ts above via
124
+ // the extractor). Use the real ApiNamespace so the marker symbol matches.
125
+ const widgets = new ApiNamespace(null as any, 'widgets', () => ({ async create(_label: string) { return { widgetId: 'w' }; } }));
126
+ const subscriptions = new ApiNamespace(null as any, 'subscriptions', () => ({ async create(_topicCount: number) { return { subId: 1 }; } }));
127
+ const loader = async () => ({ widgets, subscriptions }) as Record<string, unknown>;
128
+
129
+ try {
130
+ const doc = await generateSpec(join(dir, 'index.ts'), loader);
131
+ const byName = new Map(doc.methods.map((m) => [m.name, m]));
132
+
133
+ const w = byName.get('widgets.create');
134
+ const s = byName.get('subscriptions.create');
135
+ assert.ok(w && s, 'both namespace-qualified methods should be present');
136
+
137
+ // Each keeps its OWN param — no cross-assignment.
138
+ assert.strictEqual(w!.params[0]?.name, 'label');
139
+ assert.strictEqual((w!.params[0] as any)?.schema?.type, 'string');
140
+ assert.strictEqual(s!.params[0]?.name, 'topicCount');
141
+ assert.strictEqual((s!.params[0] as any)?.schema?.type, 'number');
142
+ } finally {
143
+ rmSync(dir, { recursive: true, force: true });
144
+ }
145
+ });
146
+ });
147
+
148
+ describe('generateSpec — namespace returned by a factory, then destructured (regression: #444)', () => {
149
+ it('emits real schemas (not unknown) for a factory/destructured namespace', async () => {
150
+ const dir = join(tmpdir(), `blocks-spec-test-${Date.now()}-factory`);
151
+ mkdirSync(dir, { recursive: true });
152
+ writeTsconfig(dir);
153
+
154
+ // Typed foundation: one directly-exported namespace and one built inside a
155
+ // factory, returned as a property, destructured, and re-exported. Both use
156
+ // `create` with DIFFERENT params so a regression would cross-assign or emit
157
+ // unknown.
158
+ writeFileSync(join(dir, 'index.ts'), `
159
+ interface ApiNamespaceConstructor { new <T>(scope: any, name: string, handler: (ctx: any) => T): T; }
160
+ const ApiNamespace: ApiNamespaceConstructor = class {} as any;
161
+ class Scope { constructor(id: string) {} }
162
+
163
+ class Factory {
164
+ constructor(private readonly scope: Scope) {}
165
+ build() {
166
+ return {
167
+ subscriptions: new ApiNamespace(this.scope, 'subscriptions', () => ({
168
+ async create(topicCount: number): Promise<{ subId: number }> { return { subId: 1 }; },
169
+ })),
170
+ };
171
+ }
172
+ }
173
+
174
+ const scope = new Scope('app');
175
+ export const widgets = new ApiNamespace(scope, 'widgets', () => ({
176
+ async create(label: string): Promise<{ widgetId: string }> { return { widgetId: 'w' }; },
177
+ }));
178
+ const { subscriptions } = new Factory(scope).build();
179
+ export { subscriptions };
180
+ `);
181
+
182
+ const widgets = new ApiNamespace(null as any, 'widgets', () => ({ async create(_label: string) { return { widgetId: 'w' }; } }));
183
+ const subscriptions = new ApiNamespace(null as any, 'subscriptions', () => ({ async create(_topicCount: number) { return { subId: 1 }; } }));
184
+ const loader = async () => ({ widgets, subscriptions }) as Record<string, unknown>;
185
+
186
+ try {
187
+ const doc = await generateSpec(join(dir, 'index.ts'), loader);
188
+ const byName = new Map(doc.methods.map((m) => [m.name, m]));
189
+ const w = byName.get('widgets.create');
190
+ const s = byName.get('subscriptions.create');
191
+ assert.ok(w && s, 'both qualified methods present');
192
+ // The factory-returned namespace keeps its own param, not `unknown` and
193
+ // not the direct namespace's `string`.
194
+ assert.strictEqual((s!.params[0] as any)?.schema?.type, 'number');
195
+ assert.strictEqual((w!.params[0] as any)?.schema?.type, 'string');
196
+ } finally {
197
+ rmSync(dir, { recursive: true, force: true });
198
+ }
199
+ });
200
+ });
@@ -324,6 +324,16 @@ export async function generateSpec(
324
324
  for (const [methodName, methodFn] of Object.entries(methodMap)) {
325
325
  if (typeof methodFn !== 'function') continue;
326
326
 
327
+ const qualifiedName = `${routingName}.${methodName}`;
328
+ // Prefer the namespace-qualified TS types; fall back to the bare method
329
+ // name for entries the extractor couldn't attribute to a namespace.
330
+ // Qualified keys stop two namespaces that share a method name (e.g.
331
+ // widgets.create / subscriptions.create) from cross-assigning schemas.
332
+ // Residual edge: namespaces the extractor can't attribute (default export,
333
+ // factory-wrapped, or an `export { x as y }` rename) still land on bare
334
+ // keys and could overwrite each other here — same class as #445, but rare.
335
+ const tsInfo = tsTypes.get(qualifiedName) ?? tsTypes.get(methodName);
336
+
327
337
  // `@blocksSkipCodegen` is a JSDoc tag the customer puts on a method
328
338
  // they want callable from the generated TS client (Proxy-based,
329
339
  // doesn't enumerate) but absent from the OpenRPC spec — so native
@@ -334,9 +344,11 @@ export async function generateSpec(
334
344
  // type-resolution errors and silently empty its Map — we always
335
345
  // want the JSDoc tag to be honored, so the AST-only scan acts as
336
346
  // a safety net.
337
- if (tsTypes.get(methodName)?.skipCodegen || skipCodegenNames.has(methodName)) continue;
347
+ // The skip set is namespace-qualified too (with a bare fallback), so a
348
+ // `@blocksSkipCodegen` tag on one namespace's method doesn't drop another
349
+ // namespace's same-named method.
350
+ if (tsInfo?.skipCodegen || skipCodegenNames.has(qualifiedName) || skipCodegenNames.has(methodName)) continue;
338
351
 
339
- const qualifiedName = `${routingName}.${methodName}`;
340
352
  const resultName = capitalize(methodName) + 'Result';
341
353
  const paramNames = extractParamNames(methodFn);
342
354
  const fnSource = methodFn.toString();
@@ -380,7 +392,6 @@ export async function generateSpec(
380
392
  }));
381
393
  } else {
382
394
  // No Zod schema — fall back to TypeScript AST types
383
- const tsInfo = tsTypes.get(methodName);
384
395
  if (tsInfo) {
385
396
  params = tsInfo.params.map((p) => ({
386
397
  name: p.name,
@@ -397,8 +408,7 @@ export async function generateSpec(
397
408
  }
398
409
  }
399
410
 
400
- // Return type: Zod doesn't cover this yet, so use TS types
401
- const tsInfo = tsTypes.get(methodName);
411
+ // Return type: Zod doesn't cover this yet, so use TS types (tsInfo resolved above)
402
412
  const rawResultSchema = tsInfo?.returnType && tsInfo.returnType.type !== 'unknown'
403
413
  ? tsInfo.returnType
404
414
  : { type: 'unknown' };
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
- export const CORE_VERSION = '0.4.0';
2
+ export const CORE_VERSION = '0.5.0';