@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.7 → 3.9.0-ultramodern.9

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.
@@ -38,15 +38,24 @@ var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
40
  configuredMicroVerticalApiStem: ()=>configuredMicroVerticalApiStem,
41
+ consumerParserPlugins: ()=>consumerParserPlugins,
41
42
  microVerticalApiBaselineViolation: ()=>microVerticalApiBaselineViolation
42
43
  });
43
44
  const external_node_fs_namespaceObject = require("node:fs");
44
45
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
46
+ const external_node_path_namespaceObject = require("node:path");
47
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
45
48
  const parser_namespaceObject = require("@babel/parser");
46
49
  const traverse_namespaceObject = require("@babel/traverse");
47
50
  var traverse_default = /*#__PURE__*/ __webpack_require__.n(traverse_namespaceObject);
48
51
  const types_namespaceObject = require("@babel/types");
49
52
  const external_microvertical_api_owner_cjs_namespaceObject = require("./microvertical-api-owner.cjs");
53
+ const consumerParserPlugins = (filePath)=>filePath.endsWith('.tsx') || filePath.endsWith('.jsx') ? [
54
+ "typescript",
55
+ 'jsx'
56
+ ] : [
57
+ "typescript"
58
+ ];
50
59
  class ConsumerSyntaxError extends Error {
51
60
  }
52
61
  class ConsumerValidationHub extends traverse_namespaceObject.Hub {
@@ -61,9 +70,7 @@ function parseConsumer(filePath) {
61
70
  file = (0, parser_namespaceObject.parse)(external_node_fs_default().readFileSync(filePath, 'utf8'), {
62
71
  sourceType: 'module',
63
72
  sourceFilename: filePath,
64
- plugins: [
65
- "typescript"
66
- ]
73
+ plugins: consumerParserPlugins(filePath)
67
74
  });
68
75
  } catch (error) {
69
76
  if ('object' == typeof error && null !== error && 'code' in error && 'BABEL_PARSER_SYNTAX_ERROR' === error.code) throw new ConsumerSyntaxError(error instanceof Error ? error.message : String(error));
@@ -157,6 +164,110 @@ const exportedConst = (sourceFile, name)=>{
157
164
  }))) return localConst(sourceFile, name);
158
165
  }
159
166
  };
167
+ const resolveRelativeModulePath = (fromPath, specifier)=>{
168
+ if (!/^\.\.?\//u.test(specifier)) return;
169
+ const resolved = external_node_path_default().resolve(external_node_path_default().dirname(fromPath), specifier);
170
+ const stem = resolved.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
171
+ for (const candidate of [
172
+ `${stem}.ts`,
173
+ `${stem}.tsx`,
174
+ external_node_path_default().join(stem, 'index.ts'),
175
+ external_node_path_default().join(stem, 'index.tsx')
176
+ ])if (external_node_fs_default().existsSync(candidate) && external_node_fs_default().statSync(candidate).isFile()) return candidate;
177
+ };
178
+ const createModuleReader = (root)=>{
179
+ const modules = new Map([
180
+ [
181
+ root.path,
182
+ root
183
+ ]
184
+ ]);
185
+ return (filePath)=>{
186
+ if (modules.has(filePath)) return modules.get(filePath);
187
+ if (modules.size >= 256) return;
188
+ let module;
189
+ try {
190
+ module = {
191
+ file: parseConsumer(filePath),
192
+ path: filePath
193
+ };
194
+ } catch {
195
+ module = void 0;
196
+ }
197
+ modules.set(filePath, module);
198
+ return module;
199
+ };
200
+ };
201
+ const importedBinding = (module, name)=>{
202
+ for (const statement of module.file.program.body)if (types_namespaceObject.isImportDeclaration(statement) && 'type' !== statement.importKind) {
203
+ for (const specifier of statement.specifiers)if (types_namespaceObject.isIdentifier(specifier.local, {
204
+ name
205
+ })) {
206
+ if (types_namespaceObject.isImportSpecifier(specifier) && 'type' !== specifier.importKind) return {
207
+ imported: propertyName(specifier.imported) ?? name,
208
+ specifier: statement.source.value
209
+ };
210
+ if (types_namespaceObject.isImportDefaultSpecifier(specifier)) return {
211
+ imported: 'default',
212
+ specifier: statement.source.value
213
+ };
214
+ }
215
+ }
216
+ };
217
+ const reexportedBinding = (module, name)=>{
218
+ for (const statement of module.file.program.body){
219
+ if (!types_namespaceObject.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
220
+ const specifier = statement.specifiers.find((value)=>types_namespaceObject.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
221
+ if (void 0 !== specifier && types_namespaceObject.isExportSpecifier(specifier)) return {
222
+ local: specifier.local.name,
223
+ ...statement.source ? {
224
+ specifier: statement.source.value
225
+ } : {}
226
+ };
227
+ }
228
+ };
229
+ const defaultExport = (module)=>{
230
+ for (const statement of module.file.program.body){
231
+ if (!types_namespaceObject.isExportDefaultDeclaration(statement)) continue;
232
+ const declaration = statement.declaration;
233
+ if (types_namespaceObject.isIdentifier(declaration)) return declaration.name;
234
+ return types_namespaceObject.isExpression(declaration) ? declaration : void 0;
235
+ }
236
+ };
237
+ const resolveBinding = (module, name, scope, readModule, seen)=>{
238
+ const key = `${scope}:${module.path}#${name}`;
239
+ if (seen.has(key) || seen.size >= 512) return;
240
+ seen.add(key);
241
+ const declaration = 'export' === scope ? exportedConst(module.file, name) : localConst(module.file, name);
242
+ if (declaration?.init) return {
243
+ expression: declaration.init,
244
+ module
245
+ };
246
+ const throughModule = (specifier, imported)=>{
247
+ const resolvedPath = resolveRelativeModulePath(module.path, specifier);
248
+ const target = void 0 === resolvedPath ? void 0 : readModule(resolvedPath);
249
+ return void 0 === target ? void 0 : resolveBinding(target, imported, 'export', readModule, seen);
250
+ };
251
+ if ('local' === scope) {
252
+ const imported = importedBinding(module, name);
253
+ return void 0 === imported ? void 0 : throughModule(imported.specifier, imported.imported);
254
+ }
255
+ if ('default' === name) {
256
+ const exported = defaultExport(module);
257
+ if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, seen);
258
+ return void 0 === exported ? void 0 : {
259
+ expression: exported,
260
+ module
261
+ };
262
+ }
263
+ const reexported = reexportedBinding(module, name);
264
+ if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', readModule, seen) : throughModule(reexported.specifier, reexported.local);
265
+ for (const statement of module.file.program.body){
266
+ if (!types_namespaceObject.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
267
+ const resolved = throughModule(statement.source.value, name);
268
+ if (void 0 !== resolved) return resolved;
269
+ }
270
+ };
160
271
  const objectLiteral = (expression)=>{
161
272
  if (null == expression) return;
162
273
  const unwrapped = unwrapExpression(expression);
@@ -288,7 +399,22 @@ const rootComposesFoundation = (sourceFile, declaration, stem, foundationName)=>
288
399
  'identity'
289
400
  ]))) && first?.name === 'addHttpApi' && identifierName(first.arguments[0]) === foundationName;
290
401
  };
291
- const reachableEndpoints = (sourceFile, declaration)=>{
402
+ const nonEndpointCombinators = {
403
+ api: {
404
+ annotate: 2,
405
+ annotateMerge: 1,
406
+ middleware: 1
407
+ },
408
+ group: {
409
+ annotate: 2,
410
+ annotateEndpoints: 2,
411
+ annotateEndpointsMerge: 1,
412
+ annotateMerge: 1,
413
+ middleware: 1
414
+ }
415
+ };
416
+ const reachableEndpoints = (rootModule, declaration)=>{
417
+ const readModule = createModuleReader(rootModule);
292
418
  const active = new Set();
293
419
  const endpoints = [];
294
420
  const identities = new Set();
@@ -303,13 +429,16 @@ const reachableEndpoints = (sourceFile, declaration)=>{
303
429
  options: 'OPTIONS'
304
430
  };
305
431
  let visits = 0;
306
- const visit = (expression, kind, group = '')=>{
432
+ const visit = (expression, module, kind, group = '')=>{
307
433
  if (!expression) return false;
308
434
  const node = unwrapExpression(expression);
309
435
  if (active.has(node) || active.size >= 128 || ++visits > 2048) return false;
310
436
  active.add(node);
311
437
  try {
312
- if (types_namespaceObject.isIdentifier(node)) return visit(localConst(sourceFile, node.name)?.init, kind, group);
438
+ if (types_namespaceObject.isIdentifier(node)) {
439
+ const resolved = resolveBinding(module, node.name, 'local', readModule, new Set());
440
+ return void 0 !== resolved && visit(resolved.expression, resolved.module, kind, group);
441
+ }
313
442
  if ('endpoint' === kind) {
314
443
  if (!types_namespaceObject.isCallExpression(node) || node.arguments.length < 2 || node.arguments.length > 3) return false;
315
444
  const callee = accessPath(node.callee);
@@ -335,19 +464,35 @@ const reachableEndpoints = (sourceFile, declaration)=>{
335
464
  ]) || 1 !== chain.base.arguments.length) return false;
336
465
  const name = stringLiteral(chain.base.arguments[0]);
337
466
  if (!name) return false;
467
+ const chainStart = endpoints.length;
468
+ const combinators = nonEndpointCombinators[kind];
338
469
  return chain.methods.every((method)=>{
470
+ if ('prefix' === method.name) {
471
+ const prefix = stringLiteral(method.arguments[0]);
472
+ if (1 !== method.arguments.length || !prefix?.startsWith('/')) return false;
473
+ for(let index = chainStart; index < endpoints.length; index += 1){
474
+ const endpoint = endpoints[index];
475
+ endpoints[index] = {
476
+ ...endpoint,
477
+ routePath: `${prefix}${endpoint.routePath}`
478
+ };
479
+ }
480
+ return true;
481
+ }
482
+ const combinatorArity = combinators[method.name];
483
+ if (void 0 !== combinatorArity) return method.arguments.length === combinatorArity;
339
484
  if (1 !== method.arguments.length) return false;
340
- if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(sourceFile, 'effect', [
485
+ if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(module.file, 'effect', [
341
486
  'identity'
342
487
  ]);
343
- if ('add' === method.name) return visit(method.arguments[0], 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
344
- return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], 'api');
488
+ if ('add' === method.name) return visit(method.arguments[0], module, 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
489
+ return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], module, 'api');
345
490
  });
346
491
  } finally{
347
492
  active.delete(node);
348
493
  }
349
494
  };
350
- return visit(declaration?.init, 'api') ? endpoints : void 0;
495
+ return visit(declaration?.init, rootModule, 'api') ? endpoints : void 0;
351
496
  };
352
497
  const operationContextFields = (property)=>{
353
498
  const constructorCall = exactCall(property.value, [
@@ -478,7 +623,8 @@ const readinessSchemaIsShared = (declaration, markerSchemaName, ownerMarkerIsIde
478
623
  return void 0 !== schema && (schema.identity && ownerMarkerIsIdentity || 1 === schema.assignments.size && identifierName(schema.assignments.get('marker')?.value) === markerSchemaName);
479
624
  };
480
625
  const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName(unwrapExpression(declaration.init)) === identifier;
481
- const validateParsedContract = (sourceFile, stem, expectation)=>{
626
+ const validateParsedContract = (module, stem, expectation)=>{
627
+ const sourceFile = module.file;
482
628
  const exportStem = camelCaseStem(stem);
483
629
  const foundationName = `${exportStem}FoundationApi`;
484
630
  const markerSchemaName = `${exportStem}MarkerSchema`;
@@ -495,7 +641,7 @@ const validateParsedContract = (sourceFile, stem, expectation)=>{
495
641
  if (!readinessSchemaIsShared(exportedConst(sourceFile, readinessSchemaName), markerSchemaName, declarationIsIdentifier(markerDeclaration, 'MicroVerticalBuildMarkerSchema'))) return 'MicroVertical readiness schema must consume the shared readiness schema without overriding shared fields';
496
642
  if (!foundationIsExact(exportedConst(sourceFile, foundationName), stem, readinessSchemaName)) return 'MicroVertical readiness foundation API must directly compose its exact readiness endpoint and foundation identity';
497
643
  if (!rootComposesFoundation(sourceFile, exportedConst(sourceFile, `${exportStem}Api`), stem, foundationName)) return 'MicroVertical root API must explicitly compose its readiness foundation API';
498
- const endpoints = reachableEndpoints(sourceFile, exportedConst(sourceFile, `${exportStem}Api`));
644
+ const endpoints = reachableEndpoints(module, exportedConst(sourceFile, `${exportStem}Api`));
499
645
  if (!endpoints) return 'MicroVertical root API must compose bounded native endpoint declarations without ambiguous identities';
500
646
  if (!operationMapIsConnected(exportedConst(sourceFile, `${exportStem}OperationContexts`), stem, expectation.operationPaths ?? {}, endpoints)) return 'MicroVertical operation map must construct every operation with the shared context constructor';
501
647
  if (!metadataIsExact(exportedConst(sourceFile, `${exportStem}ApiContract`), expectation, endpoints)) return 'MicroVertical root contract must keep exact owner and API path metadata without forbidden fields';
@@ -509,16 +655,21 @@ const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
509
655
  try {
510
656
  const sourceFile = parseConsumer(filePath);
511
657
  if (!(0, external_microvertical_api_owner_cjs_namespaceObject.baselinePublicIdentityIsExact)(filePath, expectation)) return 'MicroVertical baseline imports must resolve the exact framework owner public export and schema identity';
512
- return validateParsedContract(sourceFile, stem, expectation);
658
+ return validateParsedContract({
659
+ file: sourceFile,
660
+ path: filePath
661
+ }, stem, expectation);
513
662
  } catch (error) {
514
663
  if (error instanceof ConsumerSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
515
664
  throw error;
516
665
  }
517
666
  };
518
667
  exports.configuredMicroVerticalApiStem = __webpack_exports__.configuredMicroVerticalApiStem;
668
+ exports.consumerParserPlugins = __webpack_exports__.consumerParserPlugins;
519
669
  exports.microVerticalApiBaselineViolation = __webpack_exports__.microVerticalApiBaselineViolation;
520
670
  for(var __rspack_i in __webpack_exports__)if (-1 === [
521
671
  "configuredMicroVerticalApiStem",
672
+ "consumerParserPlugins",
522
673
  "microVerticalApiBaselineViolation"
523
674
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
524
675
  Object.defineProperty(exports, '__esModule', {
@@ -159,10 +159,7 @@ function check(options, topology) {
159
159
  const ast = (0, parser_namespaceObject.parse)(read(file), {
160
160
  sourceType: 'module',
161
161
  sourceFilename: file,
162
- plugins: [
163
- "typescript",
164
- 'jsx'
165
- ]
162
+ plugins: (0, external_microvertical_api_baseline_cjs_namespaceObject.consumerParserPlugins)(file)
166
163
  });
167
164
  parsed.set(file, ast);
168
165
  return ast;
@@ -175,13 +172,49 @@ function check(options, topology) {
175
172
  throw error;
176
173
  }
177
174
  };
178
- const moduleShape = (file, imports, calls, patterns = [], defaultExport)=>{
175
+ const resolveRelative = (fromFile, specifier)=>{
176
+ if (!/^\.\.?\//u.test(specifier)) return;
177
+ const base = external_node_path_default().posix.join(external_node_path_default().posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
178
+ return [
179
+ `${base}.ts`,
180
+ `${base}.tsx`,
181
+ `${base}/index.ts`,
182
+ `${base}/index.tsx`
183
+ ].find((candidate)=>exists(candidate));
184
+ };
185
+ const composedModules = (file)=>{
186
+ const order = [];
187
+ const seen = new Set();
188
+ const queue = [
189
+ file
190
+ ];
191
+ while(queue.length > 0 && order.length < 128){
192
+ const current = queue.shift();
193
+ if (seen.has(current)) continue;
194
+ seen.add(current);
195
+ order.push(current);
196
+ const ast = parseFile(current);
197
+ if (!ast) continue;
198
+ for (const statement of ast.program.body){
199
+ const source = (types_namespaceObject.isImportDeclaration(statement) || types_namespaceObject.isExportNamedDeclaration(statement) || types_namespaceObject.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
200
+ const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
201
+ if (void 0 !== resolved && !seen.has(resolved)) queue.push(resolved);
202
+ }
203
+ }
204
+ return order;
205
+ };
206
+ const moduleShape = (file, imports, calls, patterns = [], defaultExport, composed = false)=>{
179
207
  if (!exists(file)) return;
180
208
  const ast = parseFile(file);
181
209
  if (!ast) return;
182
- const body = ast.program.body;
210
+ const shapeFiles = composed ? composedModules(file) : [
211
+ file
212
+ ];
213
+ const body = shapeFiles.flatMap((shapeFile)=>parseFile(shapeFile)?.program.body ?? []);
183
214
  for (const [specifier, names] of imports){
184
- const imported = body.filter((item)=>types_namespaceObject.isImportDeclaration(item) && 'type' !== item.importKind && item.source.value === specifier);
215
+ const target = resolveRelative(file, specifier);
216
+ const matchesSpecifier = (item, from)=>void 0 === target ? item.source.value === specifier : resolveRelative(from, item.source.value) === target;
217
+ const imported = shapeFiles.flatMap((shapeFile)=>(parseFile(shapeFile)?.program.body ?? []).filter((item)=>types_namespaceObject.isImportDeclaration(item) && 'type' !== item.importKind && matchesSpecifier(item, shapeFile)));
185
218
  assert(imported.length, `${file}: must import from ${specifier}`);
186
219
  for (const name of names)assert(imported.some((item)=>types_namespaceObject.isImportDeclaration(item) && item.specifiers.some((value)=>types_namespaceObject.isImportSpecifier(value) && 'type' !== value.importKind && types_namespaceObject.isIdentifier(value.imported, {
187
220
  name
@@ -190,11 +223,15 @@ function check(options, topology) {
190
223
  }))), `${file}: must import ${name} from ${specifier}`);
191
224
  }
192
225
  const found = new Set();
193
- types_namespaceObject.traverseFast(ast, (node)=>{
226
+ const collectCalls = (node)=>{
194
227
  if (!types_namespaceObject.isCallExpression(node)) return;
195
228
  if (types_namespaceObject.isIdentifier(node.callee)) found.add(node.callee.name);
196
229
  if (types_namespaceObject.isMemberExpression(node.callee) && !node.callee.computed && types_namespaceObject.isIdentifier(node.callee.object) && types_namespaceObject.isIdentifier(node.callee.property)) found.add(`${node.callee.object.name}.${node.callee.property.name}`);
197
- });
230
+ };
231
+ for (const shapeFile of shapeFiles){
232
+ const shapeAst = parseFile(shapeFile);
233
+ if (shapeAst) types_namespaceObject.traverseFast(shapeAst, collectCalls);
234
+ }
198
235
  for (const call of calls)assert(found.has(call), `${file}: must call ${call}(...)`);
199
236
  const source = read(file).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/gu, '');
200
237
  for (const [pattern, reason] of patterns)assert(pattern.test(source), `${file}: ${reason}`);
@@ -372,7 +409,7 @@ function check(options, topology) {
372
409
  ]
373
410
  ], [
374
411
  rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
375
- ]);
412
+ ], [], void 0, true);
376
413
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
377
414
  [
378
415
  /\bbackendFederationContract\b/u,
@@ -1,8 +1,15 @@
1
1
  import node_fs from "node:fs";
2
+ import node_path from "node:path";
2
3
  import { parse } from "@babel/parser";
3
4
  import traverse, { Hub, NodePath } from "@babel/traverse";
4
5
  import { baselinePublicIdentityIsExact } from "./microvertical-api-owner.js";
5
6
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
7
+ const consumerParserPlugins = (filePath)=>filePath.endsWith('.tsx') || filePath.endsWith('.jsx') ? [
8
+ "typescript",
9
+ 'jsx'
10
+ ] : [
11
+ "typescript"
12
+ ];
6
13
  class ConsumerSyntaxError extends Error {
7
14
  }
8
15
  class ConsumerValidationHub extends Hub {
@@ -17,9 +24,7 @@ function parseConsumer(filePath) {
17
24
  file = parse(node_fs.readFileSync(filePath, 'utf8'), {
18
25
  sourceType: 'module',
19
26
  sourceFilename: filePath,
20
- plugins: [
21
- "typescript"
22
- ]
27
+ plugins: consumerParserPlugins(filePath)
23
28
  });
24
29
  } catch (error) {
25
30
  if ('object' == typeof error && null !== error && 'code' in error && 'BABEL_PARSER_SYNTAX_ERROR' === error.code) throw new ConsumerSyntaxError(error instanceof Error ? error.message : String(error));
@@ -113,6 +118,110 @@ const exportedConst = (sourceFile, name)=>{
113
118
  }))) return localConst(sourceFile, name);
114
119
  }
115
120
  };
121
+ const resolveRelativeModulePath = (fromPath, specifier)=>{
122
+ if (!/^\.\.?\//u.test(specifier)) return;
123
+ const resolved = node_path.resolve(node_path.dirname(fromPath), specifier);
124
+ const stem = resolved.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
125
+ for (const candidate of [
126
+ `${stem}.ts`,
127
+ `${stem}.tsx`,
128
+ node_path.join(stem, 'index.ts'),
129
+ node_path.join(stem, 'index.tsx')
130
+ ])if (node_fs.existsSync(candidate) && node_fs.statSync(candidate).isFile()) return candidate;
131
+ };
132
+ const createModuleReader = (root)=>{
133
+ const modules = new Map([
134
+ [
135
+ root.path,
136
+ root
137
+ ]
138
+ ]);
139
+ return (filePath)=>{
140
+ if (modules.has(filePath)) return modules.get(filePath);
141
+ if (modules.size >= 256) return;
142
+ let module;
143
+ try {
144
+ module = {
145
+ file: parseConsumer(filePath),
146
+ path: filePath
147
+ };
148
+ } catch {
149
+ module = void 0;
150
+ }
151
+ modules.set(filePath, module);
152
+ return module;
153
+ };
154
+ };
155
+ const importedBinding = (module, name)=>{
156
+ for (const statement of module.file.program.body)if (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) && 'type' !== statement.importKind) {
157
+ for (const specifier of statement.specifiers)if (__rspack_external__babel_types_6e8337dd.isIdentifier(specifier.local, {
158
+ name
159
+ })) {
160
+ if (__rspack_external__babel_types_6e8337dd.isImportSpecifier(specifier) && 'type' !== specifier.importKind) return {
161
+ imported: propertyName(specifier.imported) ?? name,
162
+ specifier: statement.source.value
163
+ };
164
+ if (__rspack_external__babel_types_6e8337dd.isImportDefaultSpecifier(specifier)) return {
165
+ imported: 'default',
166
+ specifier: statement.source.value
167
+ };
168
+ }
169
+ }
170
+ };
171
+ const reexportedBinding = (module, name)=>{
172
+ for (const statement of module.file.program.body){
173
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
174
+ const specifier = statement.specifiers.find((value)=>__rspack_external__babel_types_6e8337dd.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
175
+ if (void 0 !== specifier && __rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier)) return {
176
+ local: specifier.local.name,
177
+ ...statement.source ? {
178
+ specifier: statement.source.value
179
+ } : {}
180
+ };
181
+ }
182
+ };
183
+ const defaultExport = (module)=>{
184
+ for (const statement of module.file.program.body){
185
+ if (!__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement)) continue;
186
+ const declaration = statement.declaration;
187
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(declaration)) return declaration.name;
188
+ return __rspack_external__babel_types_6e8337dd.isExpression(declaration) ? declaration : void 0;
189
+ }
190
+ };
191
+ const resolveBinding = (module, name, scope, readModule, seen)=>{
192
+ const key = `${scope}:${module.path}#${name}`;
193
+ if (seen.has(key) || seen.size >= 512) return;
194
+ seen.add(key);
195
+ const declaration = 'export' === scope ? exportedConst(module.file, name) : localConst(module.file, name);
196
+ if (declaration?.init) return {
197
+ expression: declaration.init,
198
+ module
199
+ };
200
+ const throughModule = (specifier, imported)=>{
201
+ const resolvedPath = resolveRelativeModulePath(module.path, specifier);
202
+ const target = void 0 === resolvedPath ? void 0 : readModule(resolvedPath);
203
+ return void 0 === target ? void 0 : resolveBinding(target, imported, 'export', readModule, seen);
204
+ };
205
+ if ('local' === scope) {
206
+ const imported = importedBinding(module, name);
207
+ return void 0 === imported ? void 0 : throughModule(imported.specifier, imported.imported);
208
+ }
209
+ if ('default' === name) {
210
+ const exported = defaultExport(module);
211
+ if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, seen);
212
+ return void 0 === exported ? void 0 : {
213
+ expression: exported,
214
+ module
215
+ };
216
+ }
217
+ const reexported = reexportedBinding(module, name);
218
+ if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', readModule, seen) : throughModule(reexported.specifier, reexported.local);
219
+ for (const statement of module.file.program.body){
220
+ if (!__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
221
+ const resolved = throughModule(statement.source.value, name);
222
+ if (void 0 !== resolved) return resolved;
223
+ }
224
+ };
116
225
  const objectLiteral = (expression)=>{
117
226
  if (null == expression) return;
118
227
  const unwrapped = unwrapExpression(expression);
@@ -244,7 +353,22 @@ const rootComposesFoundation = (sourceFile, declaration, stem, foundationName)=>
244
353
  'identity'
245
354
  ]))) && first?.name === 'addHttpApi' && identifierName(first.arguments[0]) === foundationName;
246
355
  };
247
- const reachableEndpoints = (sourceFile, declaration)=>{
356
+ const nonEndpointCombinators = {
357
+ api: {
358
+ annotate: 2,
359
+ annotateMerge: 1,
360
+ middleware: 1
361
+ },
362
+ group: {
363
+ annotate: 2,
364
+ annotateEndpoints: 2,
365
+ annotateEndpointsMerge: 1,
366
+ annotateMerge: 1,
367
+ middleware: 1
368
+ }
369
+ };
370
+ const reachableEndpoints = (rootModule, declaration)=>{
371
+ const readModule = createModuleReader(rootModule);
248
372
  const active = new Set();
249
373
  const endpoints = [];
250
374
  const identities = new Set();
@@ -259,13 +383,16 @@ const reachableEndpoints = (sourceFile, declaration)=>{
259
383
  options: 'OPTIONS'
260
384
  };
261
385
  let visits = 0;
262
- const visit = (expression, kind, group = '')=>{
386
+ const visit = (expression, module, kind, group = '')=>{
263
387
  if (!expression) return false;
264
388
  const node = unwrapExpression(expression);
265
389
  if (active.has(node) || active.size >= 128 || ++visits > 2048) return false;
266
390
  active.add(node);
267
391
  try {
268
- if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) return visit(localConst(sourceFile, node.name)?.init, kind, group);
392
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) {
393
+ const resolved = resolveBinding(module, node.name, 'local', readModule, new Set());
394
+ return void 0 !== resolved && visit(resolved.expression, resolved.module, kind, group);
395
+ }
269
396
  if ('endpoint' === kind) {
270
397
  if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) || node.arguments.length < 2 || node.arguments.length > 3) return false;
271
398
  const callee = accessPath(node.callee);
@@ -291,19 +418,35 @@ const reachableEndpoints = (sourceFile, declaration)=>{
291
418
  ]) || 1 !== chain.base.arguments.length) return false;
292
419
  const name = stringLiteral(chain.base.arguments[0]);
293
420
  if (!name) return false;
421
+ const chainStart = endpoints.length;
422
+ const combinators = nonEndpointCombinators[kind];
294
423
  return chain.methods.every((method)=>{
424
+ if ('prefix' === method.name) {
425
+ const prefix = stringLiteral(method.arguments[0]);
426
+ if (1 !== method.arguments.length || !prefix?.startsWith('/')) return false;
427
+ for(let index = chainStart; index < endpoints.length; index += 1){
428
+ const endpoint = endpoints[index];
429
+ endpoints[index] = {
430
+ ...endpoint,
431
+ routePath: `${prefix}${endpoint.routePath}`
432
+ };
433
+ }
434
+ return true;
435
+ }
436
+ const combinatorArity = combinators[method.name];
437
+ if (void 0 !== combinatorArity) return method.arguments.length === combinatorArity;
295
438
  if (1 !== method.arguments.length) return false;
296
- if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(sourceFile, 'effect', [
439
+ if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(module.file, 'effect', [
297
440
  'identity'
298
441
  ]);
299
- if ('add' === method.name) return visit(method.arguments[0], 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
300
- return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], 'api');
442
+ if ('add' === method.name) return visit(method.arguments[0], module, 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
443
+ return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], module, 'api');
301
444
  });
302
445
  } finally{
303
446
  active.delete(node);
304
447
  }
305
448
  };
306
- return visit(declaration?.init, 'api') ? endpoints : void 0;
449
+ return visit(declaration?.init, rootModule, 'api') ? endpoints : void 0;
307
450
  };
308
451
  const operationContextFields = (property)=>{
309
452
  const constructorCall = exactCall(property.value, [
@@ -434,7 +577,8 @@ const readinessSchemaIsShared = (declaration, markerSchemaName, ownerMarkerIsIde
434
577
  return void 0 !== schema && (schema.identity && ownerMarkerIsIdentity || 1 === schema.assignments.size && identifierName(schema.assignments.get('marker')?.value) === markerSchemaName);
435
578
  };
436
579
  const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName(unwrapExpression(declaration.init)) === identifier;
437
- const validateParsedContract = (sourceFile, stem, expectation)=>{
580
+ const validateParsedContract = (module, stem, expectation)=>{
581
+ const sourceFile = module.file;
438
582
  const exportStem = camelCaseStem(stem);
439
583
  const foundationName = `${exportStem}FoundationApi`;
440
584
  const markerSchemaName = `${exportStem}MarkerSchema`;
@@ -451,7 +595,7 @@ const validateParsedContract = (sourceFile, stem, expectation)=>{
451
595
  if (!readinessSchemaIsShared(exportedConst(sourceFile, readinessSchemaName), markerSchemaName, declarationIsIdentifier(markerDeclaration, 'MicroVerticalBuildMarkerSchema'))) return 'MicroVertical readiness schema must consume the shared readiness schema without overriding shared fields';
452
596
  if (!foundationIsExact(exportedConst(sourceFile, foundationName), stem, readinessSchemaName)) return 'MicroVertical readiness foundation API must directly compose its exact readiness endpoint and foundation identity';
453
597
  if (!rootComposesFoundation(sourceFile, exportedConst(sourceFile, `${exportStem}Api`), stem, foundationName)) return 'MicroVertical root API must explicitly compose its readiness foundation API';
454
- const endpoints = reachableEndpoints(sourceFile, exportedConst(sourceFile, `${exportStem}Api`));
598
+ const endpoints = reachableEndpoints(module, exportedConst(sourceFile, `${exportStem}Api`));
455
599
  if (!endpoints) return 'MicroVertical root API must compose bounded native endpoint declarations without ambiguous identities';
456
600
  if (!operationMapIsConnected(exportedConst(sourceFile, `${exportStem}OperationContexts`), stem, expectation.operationPaths ?? {}, endpoints)) return 'MicroVertical operation map must construct every operation with the shared context constructor';
457
601
  if (!metadataIsExact(exportedConst(sourceFile, `${exportStem}ApiContract`), expectation, endpoints)) return 'MicroVertical root contract must keep exact owner and API path metadata without forbidden fields';
@@ -465,10 +609,13 @@ const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
465
609
  try {
466
610
  const sourceFile = parseConsumer(filePath);
467
611
  if (!baselinePublicIdentityIsExact(filePath, expectation)) return 'MicroVertical baseline imports must resolve the exact framework owner public export and schema identity';
468
- return validateParsedContract(sourceFile, stem, expectation);
612
+ return validateParsedContract({
613
+ file: sourceFile,
614
+ path: filePath
615
+ }, stem, expectation);
469
616
  } catch (error) {
470
617
  if (error instanceof ConsumerSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
471
618
  throw error;
472
619
  }
473
620
  };
474
- export { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation };
621
+ export { configuredMicroVerticalApiStem, consumerParserPlugins, microVerticalApiBaselineViolation };
@@ -1,7 +1,7 @@
1
1
  import node_fs from "node:fs";
2
2
  import node_path from "node:path";
3
3
  import { parse } from "@babel/parser";
4
- import { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
4
+ import { configuredMicroVerticalApiStem, consumerParserPlugins, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
5
5
  import { baselinePackage, resolveBaselinePackageDirectory } from "./microvertical-api-owner.js";
6
6
  import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "./strict-effect-runtime.js";
7
7
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
@@ -113,10 +113,7 @@ function check(options, topology) {
113
113
  const ast = parse(read(file), {
114
114
  sourceType: 'module',
115
115
  sourceFilename: file,
116
- plugins: [
117
- "typescript",
118
- 'jsx'
119
- ]
116
+ plugins: consumerParserPlugins(file)
120
117
  });
121
118
  parsed.set(file, ast);
122
119
  return ast;
@@ -129,13 +126,49 @@ function check(options, topology) {
129
126
  throw error;
130
127
  }
131
128
  };
132
- const moduleShape = (file, imports, calls, patterns = [], defaultExport)=>{
129
+ const resolveRelative = (fromFile, specifier)=>{
130
+ if (!/^\.\.?\//u.test(specifier)) return;
131
+ const base = node_path.posix.join(node_path.posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
132
+ return [
133
+ `${base}.ts`,
134
+ `${base}.tsx`,
135
+ `${base}/index.ts`,
136
+ `${base}/index.tsx`
137
+ ].find((candidate)=>exists(candidate));
138
+ };
139
+ const composedModules = (file)=>{
140
+ const order = [];
141
+ const seen = new Set();
142
+ const queue = [
143
+ file
144
+ ];
145
+ while(queue.length > 0 && order.length < 128){
146
+ const current = queue.shift();
147
+ if (seen.has(current)) continue;
148
+ seen.add(current);
149
+ order.push(current);
150
+ const ast = parseFile(current);
151
+ if (!ast) continue;
152
+ for (const statement of ast.program.body){
153
+ const source = (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
154
+ const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
155
+ if (void 0 !== resolved && !seen.has(resolved)) queue.push(resolved);
156
+ }
157
+ }
158
+ return order;
159
+ };
160
+ const moduleShape = (file, imports, calls, patterns = [], defaultExport, composed = false)=>{
133
161
  if (!exists(file)) return;
134
162
  const ast = parseFile(file);
135
163
  if (!ast) return;
136
- const body = ast.program.body;
164
+ const shapeFiles = composed ? composedModules(file) : [
165
+ file
166
+ ];
167
+ const body = shapeFiles.flatMap((shapeFile)=>parseFile(shapeFile)?.program.body ?? []);
137
168
  for (const [specifier, names] of imports){
138
- const imported = body.filter((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && 'type' !== item.importKind && item.source.value === specifier);
169
+ const target = resolveRelative(file, specifier);
170
+ const matchesSpecifier = (item, from)=>void 0 === target ? item.source.value === specifier : resolveRelative(from, item.source.value) === target;
171
+ const imported = shapeFiles.flatMap((shapeFile)=>(parseFile(shapeFile)?.program.body ?? []).filter((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && 'type' !== item.importKind && matchesSpecifier(item, shapeFile)));
139
172
  assert(imported.length, `${file}: must import from ${specifier}`);
140
173
  for (const name of names)assert(imported.some((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && item.specifiers.some((value)=>__rspack_external__babel_types_6e8337dd.isImportSpecifier(value) && 'type' !== value.importKind && __rspack_external__babel_types_6e8337dd.isIdentifier(value.imported, {
141
174
  name
@@ -144,11 +177,15 @@ function check(options, topology) {
144
177
  }))), `${file}: must import ${name} from ${specifier}`);
145
178
  }
146
179
  const found = new Set();
147
- __rspack_external__babel_types_6e8337dd.traverseFast(ast, (node)=>{
180
+ const collectCalls = (node)=>{
148
181
  if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return;
149
182
  if (__rspack_external__babel_types_6e8337dd.isIdentifier(node.callee)) found.add(node.callee.name);
150
183
  if (__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) && !node.callee.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.object) && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property)) found.add(`${node.callee.object.name}.${node.callee.property.name}`);
151
- });
184
+ };
185
+ for (const shapeFile of shapeFiles){
186
+ const shapeAst = parseFile(shapeFile);
187
+ if (shapeAst) __rspack_external__babel_types_6e8337dd.traverseFast(shapeAst, collectCalls);
188
+ }
152
189
  for (const call of calls)assert(found.has(call), `${file}: must call ${call}(...)`);
153
190
  const source = read(file).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/gu, '');
154
191
  for (const [pattern, reason] of patterns)assert(pattern.test(source), `${file}: ${reason}`);
@@ -326,7 +363,7 @@ function check(options, topology) {
326
363
  ]
327
364
  ], [
328
365
  rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
329
- ]);
366
+ ], [], void 0, true);
330
367
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
331
368
  [
332
369
  /\bbackendFederationContract\b/u,
@@ -1,9 +1,16 @@
1
1
  import "node:module";
2
2
  import node_fs from "node:fs";
3
+ import node_path from "node:path";
3
4
  import { parse } from "@babel/parser";
4
5
  import traverse, { Hub, NodePath } from "@babel/traverse";
5
6
  import { baselinePublicIdentityIsExact } from "./microvertical-api-owner.js";
6
7
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
8
+ const consumerParserPlugins = (filePath)=>filePath.endsWith('.tsx') || filePath.endsWith('.jsx') ? [
9
+ "typescript",
10
+ 'jsx'
11
+ ] : [
12
+ "typescript"
13
+ ];
7
14
  class ConsumerSyntaxError extends Error {
8
15
  }
9
16
  class ConsumerValidationHub extends Hub {
@@ -18,9 +25,7 @@ function parseConsumer(filePath) {
18
25
  file = parse(node_fs.readFileSync(filePath, 'utf8'), {
19
26
  sourceType: 'module',
20
27
  sourceFilename: filePath,
21
- plugins: [
22
- "typescript"
23
- ]
28
+ plugins: consumerParserPlugins(filePath)
24
29
  });
25
30
  } catch (error) {
26
31
  if ('object' == typeof error && null !== error && 'code' in error && 'BABEL_PARSER_SYNTAX_ERROR' === error.code) throw new ConsumerSyntaxError(error instanceof Error ? error.message : String(error));
@@ -114,6 +119,110 @@ const exportedConst = (sourceFile, name)=>{
114
119
  }))) return localConst(sourceFile, name);
115
120
  }
116
121
  };
122
+ const resolveRelativeModulePath = (fromPath, specifier)=>{
123
+ if (!/^\.\.?\//u.test(specifier)) return;
124
+ const resolved = node_path.resolve(node_path.dirname(fromPath), specifier);
125
+ const stem = resolved.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
126
+ for (const candidate of [
127
+ `${stem}.ts`,
128
+ `${stem}.tsx`,
129
+ node_path.join(stem, 'index.ts'),
130
+ node_path.join(stem, 'index.tsx')
131
+ ])if (node_fs.existsSync(candidate) && node_fs.statSync(candidate).isFile()) return candidate;
132
+ };
133
+ const createModuleReader = (root)=>{
134
+ const modules = new Map([
135
+ [
136
+ root.path,
137
+ root
138
+ ]
139
+ ]);
140
+ return (filePath)=>{
141
+ if (modules.has(filePath)) return modules.get(filePath);
142
+ if (modules.size >= 256) return;
143
+ let module;
144
+ try {
145
+ module = {
146
+ file: parseConsumer(filePath),
147
+ path: filePath
148
+ };
149
+ } catch {
150
+ module = void 0;
151
+ }
152
+ modules.set(filePath, module);
153
+ return module;
154
+ };
155
+ };
156
+ const importedBinding = (module, name)=>{
157
+ for (const statement of module.file.program.body)if (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) && 'type' !== statement.importKind) {
158
+ for (const specifier of statement.specifiers)if (__rspack_external__babel_types_6e8337dd.isIdentifier(specifier.local, {
159
+ name
160
+ })) {
161
+ if (__rspack_external__babel_types_6e8337dd.isImportSpecifier(specifier) && 'type' !== specifier.importKind) return {
162
+ imported: propertyName(specifier.imported) ?? name,
163
+ specifier: statement.source.value
164
+ };
165
+ if (__rspack_external__babel_types_6e8337dd.isImportDefaultSpecifier(specifier)) return {
166
+ imported: 'default',
167
+ specifier: statement.source.value
168
+ };
169
+ }
170
+ }
171
+ };
172
+ const reexportedBinding = (module, name)=>{
173
+ for (const statement of module.file.program.body){
174
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
175
+ const specifier = statement.specifiers.find((value)=>__rspack_external__babel_types_6e8337dd.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
176
+ if (void 0 !== specifier && __rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier)) return {
177
+ local: specifier.local.name,
178
+ ...statement.source ? {
179
+ specifier: statement.source.value
180
+ } : {}
181
+ };
182
+ }
183
+ };
184
+ const defaultExport = (module)=>{
185
+ for (const statement of module.file.program.body){
186
+ if (!__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement)) continue;
187
+ const declaration = statement.declaration;
188
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(declaration)) return declaration.name;
189
+ return __rspack_external__babel_types_6e8337dd.isExpression(declaration) ? declaration : void 0;
190
+ }
191
+ };
192
+ const resolveBinding = (module, name, scope, readModule, seen)=>{
193
+ const key = `${scope}:${module.path}#${name}`;
194
+ if (seen.has(key) || seen.size >= 512) return;
195
+ seen.add(key);
196
+ const declaration = 'export' === scope ? exportedConst(module.file, name) : localConst(module.file, name);
197
+ if (declaration?.init) return {
198
+ expression: declaration.init,
199
+ module
200
+ };
201
+ const throughModule = (specifier, imported)=>{
202
+ const resolvedPath = resolveRelativeModulePath(module.path, specifier);
203
+ const target = void 0 === resolvedPath ? void 0 : readModule(resolvedPath);
204
+ return void 0 === target ? void 0 : resolveBinding(target, imported, 'export', readModule, seen);
205
+ };
206
+ if ('local' === scope) {
207
+ const imported = importedBinding(module, name);
208
+ return void 0 === imported ? void 0 : throughModule(imported.specifier, imported.imported);
209
+ }
210
+ if ('default' === name) {
211
+ const exported = defaultExport(module);
212
+ if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, seen);
213
+ return void 0 === exported ? void 0 : {
214
+ expression: exported,
215
+ module
216
+ };
217
+ }
218
+ const reexported = reexportedBinding(module, name);
219
+ if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', readModule, seen) : throughModule(reexported.specifier, reexported.local);
220
+ for (const statement of module.file.program.body){
221
+ if (!__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
222
+ const resolved = throughModule(statement.source.value, name);
223
+ if (void 0 !== resolved) return resolved;
224
+ }
225
+ };
117
226
  const objectLiteral = (expression)=>{
118
227
  if (null == expression) return;
119
228
  const unwrapped = unwrapExpression(expression);
@@ -245,7 +354,22 @@ const rootComposesFoundation = (sourceFile, declaration, stem, foundationName)=>
245
354
  'identity'
246
355
  ]))) && first?.name === 'addHttpApi' && identifierName(first.arguments[0]) === foundationName;
247
356
  };
248
- const reachableEndpoints = (sourceFile, declaration)=>{
357
+ const nonEndpointCombinators = {
358
+ api: {
359
+ annotate: 2,
360
+ annotateMerge: 1,
361
+ middleware: 1
362
+ },
363
+ group: {
364
+ annotate: 2,
365
+ annotateEndpoints: 2,
366
+ annotateEndpointsMerge: 1,
367
+ annotateMerge: 1,
368
+ middleware: 1
369
+ }
370
+ };
371
+ const reachableEndpoints = (rootModule, declaration)=>{
372
+ const readModule = createModuleReader(rootModule);
249
373
  const active = new Set();
250
374
  const endpoints = [];
251
375
  const identities = new Set();
@@ -260,13 +384,16 @@ const reachableEndpoints = (sourceFile, declaration)=>{
260
384
  options: 'OPTIONS'
261
385
  };
262
386
  let visits = 0;
263
- const visit = (expression, kind, group = '')=>{
387
+ const visit = (expression, module, kind, group = '')=>{
264
388
  if (!expression) return false;
265
389
  const node = unwrapExpression(expression);
266
390
  if (active.has(node) || active.size >= 128 || ++visits > 2048) return false;
267
391
  active.add(node);
268
392
  try {
269
- if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) return visit(localConst(sourceFile, node.name)?.init, kind, group);
393
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) {
394
+ const resolved = resolveBinding(module, node.name, 'local', readModule, new Set());
395
+ return void 0 !== resolved && visit(resolved.expression, resolved.module, kind, group);
396
+ }
270
397
  if ('endpoint' === kind) {
271
398
  if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) || node.arguments.length < 2 || node.arguments.length > 3) return false;
272
399
  const callee = accessPath(node.callee);
@@ -292,19 +419,35 @@ const reachableEndpoints = (sourceFile, declaration)=>{
292
419
  ]) || 1 !== chain.base.arguments.length) return false;
293
420
  const name = stringLiteral(chain.base.arguments[0]);
294
421
  if (!name) return false;
422
+ const chainStart = endpoints.length;
423
+ const combinators = nonEndpointCombinators[kind];
295
424
  return chain.methods.every((method)=>{
425
+ if ('prefix' === method.name) {
426
+ const prefix = stringLiteral(method.arguments[0]);
427
+ if (1 !== method.arguments.length || !prefix?.startsWith('/')) return false;
428
+ for(let index = chainStart; index < endpoints.length; index += 1){
429
+ const endpoint = endpoints[index];
430
+ endpoints[index] = {
431
+ ...endpoint,
432
+ routePath: `${prefix}${endpoint.routePath}`
433
+ };
434
+ }
435
+ return true;
436
+ }
437
+ const combinatorArity = combinators[method.name];
438
+ if (void 0 !== combinatorArity) return method.arguments.length === combinatorArity;
296
439
  if (1 !== method.arguments.length) return false;
297
- if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(sourceFile, 'effect', [
440
+ if ('pipe' === method.name) return 'identity' === identifierName(method.arguments[0]) && importsExactBindings(module.file, 'effect', [
298
441
  'identity'
299
442
  ]);
300
- if ('add' === method.name) return visit(method.arguments[0], 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
301
- return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], 'api');
443
+ if ('add' === method.name) return visit(method.arguments[0], module, 'api' === kind ? 'group' : 'endpoint', 'group' === kind ? name : '');
444
+ return 'api' === kind && 'addHttpApi' === method.name && visit(method.arguments[0], module, 'api');
302
445
  });
303
446
  } finally{
304
447
  active.delete(node);
305
448
  }
306
449
  };
307
- return visit(declaration?.init, 'api') ? endpoints : void 0;
450
+ return visit(declaration?.init, rootModule, 'api') ? endpoints : void 0;
308
451
  };
309
452
  const operationContextFields = (property)=>{
310
453
  const constructorCall = exactCall(property.value, [
@@ -435,7 +578,8 @@ const readinessSchemaIsShared = (declaration, markerSchemaName, ownerMarkerIsIde
435
578
  return void 0 !== schema && (schema.identity && ownerMarkerIsIdentity || 1 === schema.assignments.size && identifierName(schema.assignments.get('marker')?.value) === markerSchemaName);
436
579
  };
437
580
  const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName(unwrapExpression(declaration.init)) === identifier;
438
- const validateParsedContract = (sourceFile, stem, expectation)=>{
581
+ const validateParsedContract = (module, stem, expectation)=>{
582
+ const sourceFile = module.file;
439
583
  const exportStem = camelCaseStem(stem);
440
584
  const foundationName = `${exportStem}FoundationApi`;
441
585
  const markerSchemaName = `${exportStem}MarkerSchema`;
@@ -452,7 +596,7 @@ const validateParsedContract = (sourceFile, stem, expectation)=>{
452
596
  if (!readinessSchemaIsShared(exportedConst(sourceFile, readinessSchemaName), markerSchemaName, declarationIsIdentifier(markerDeclaration, 'MicroVerticalBuildMarkerSchema'))) return 'MicroVertical readiness schema must consume the shared readiness schema without overriding shared fields';
453
597
  if (!foundationIsExact(exportedConst(sourceFile, foundationName), stem, readinessSchemaName)) return 'MicroVertical readiness foundation API must directly compose its exact readiness endpoint and foundation identity';
454
598
  if (!rootComposesFoundation(sourceFile, exportedConst(sourceFile, `${exportStem}Api`), stem, foundationName)) return 'MicroVertical root API must explicitly compose its readiness foundation API';
455
- const endpoints = reachableEndpoints(sourceFile, exportedConst(sourceFile, `${exportStem}Api`));
599
+ const endpoints = reachableEndpoints(module, exportedConst(sourceFile, `${exportStem}Api`));
456
600
  if (!endpoints) return 'MicroVertical root API must compose bounded native endpoint declarations without ambiguous identities';
457
601
  if (!operationMapIsConnected(exportedConst(sourceFile, `${exportStem}OperationContexts`), stem, expectation.operationPaths ?? {}, endpoints)) return 'MicroVertical operation map must construct every operation with the shared context constructor';
458
602
  if (!metadataIsExact(exportedConst(sourceFile, `${exportStem}ApiContract`), expectation, endpoints)) return 'MicroVertical root contract must keep exact owner and API path metadata without forbidden fields';
@@ -466,10 +610,13 @@ const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
466
610
  try {
467
611
  const sourceFile = parseConsumer(filePath);
468
612
  if (!baselinePublicIdentityIsExact(filePath, expectation)) return 'MicroVertical baseline imports must resolve the exact framework owner public export and schema identity';
469
- return validateParsedContract(sourceFile, stem, expectation);
613
+ return validateParsedContract({
614
+ file: sourceFile,
615
+ path: filePath
616
+ }, stem, expectation);
470
617
  } catch (error) {
471
618
  if (error instanceof ConsumerSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
472
619
  throw error;
473
620
  }
474
621
  };
475
- export { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation };
622
+ export { configuredMicroVerticalApiStem, consumerParserPlugins, microVerticalApiBaselineViolation };
@@ -2,7 +2,7 @@ import "node:module";
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
4
  import { parse } from "@babel/parser";
5
- import { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
5
+ import { configuredMicroVerticalApiStem, consumerParserPlugins, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
6
6
  import { baselinePackage, resolveBaselinePackageDirectory } from "./microvertical-api-owner.js";
7
7
  import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "./strict-effect-runtime.js";
8
8
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
@@ -114,10 +114,7 @@ function check(options, topology) {
114
114
  const ast = parse(read(file), {
115
115
  sourceType: 'module',
116
116
  sourceFilename: file,
117
- plugins: [
118
- "typescript",
119
- 'jsx'
120
- ]
117
+ plugins: consumerParserPlugins(file)
121
118
  });
122
119
  parsed.set(file, ast);
123
120
  return ast;
@@ -130,13 +127,49 @@ function check(options, topology) {
130
127
  throw error;
131
128
  }
132
129
  };
133
- const moduleShape = (file, imports, calls, patterns = [], defaultExport)=>{
130
+ const resolveRelative = (fromFile, specifier)=>{
131
+ if (!/^\.\.?\//u.test(specifier)) return;
132
+ const base = node_path.posix.join(node_path.posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
133
+ return [
134
+ `${base}.ts`,
135
+ `${base}.tsx`,
136
+ `${base}/index.ts`,
137
+ `${base}/index.tsx`
138
+ ].find((candidate)=>exists(candidate));
139
+ };
140
+ const composedModules = (file)=>{
141
+ const order = [];
142
+ const seen = new Set();
143
+ const queue = [
144
+ file
145
+ ];
146
+ while(queue.length > 0 && order.length < 128){
147
+ const current = queue.shift();
148
+ if (seen.has(current)) continue;
149
+ seen.add(current);
150
+ order.push(current);
151
+ const ast = parseFile(current);
152
+ if (!ast) continue;
153
+ for (const statement of ast.program.body){
154
+ const source = (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
155
+ const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
156
+ if (void 0 !== resolved && !seen.has(resolved)) queue.push(resolved);
157
+ }
158
+ }
159
+ return order;
160
+ };
161
+ const moduleShape = (file, imports, calls, patterns = [], defaultExport, composed = false)=>{
134
162
  if (!exists(file)) return;
135
163
  const ast = parseFile(file);
136
164
  if (!ast) return;
137
- const body = ast.program.body;
165
+ const shapeFiles = composed ? composedModules(file) : [
166
+ file
167
+ ];
168
+ const body = shapeFiles.flatMap((shapeFile)=>parseFile(shapeFile)?.program.body ?? []);
138
169
  for (const [specifier, names] of imports){
139
- const imported = body.filter((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && 'type' !== item.importKind && item.source.value === specifier);
170
+ const target = resolveRelative(file, specifier);
171
+ const matchesSpecifier = (item, from)=>void 0 === target ? item.source.value === specifier : resolveRelative(from, item.source.value) === target;
172
+ const imported = shapeFiles.flatMap((shapeFile)=>(parseFile(shapeFile)?.program.body ?? []).filter((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && 'type' !== item.importKind && matchesSpecifier(item, shapeFile)));
140
173
  assert(imported.length, `${file}: must import from ${specifier}`);
141
174
  for (const name of names)assert(imported.some((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && item.specifiers.some((value)=>__rspack_external__babel_types_6e8337dd.isImportSpecifier(value) && 'type' !== value.importKind && __rspack_external__babel_types_6e8337dd.isIdentifier(value.imported, {
142
175
  name
@@ -145,11 +178,15 @@ function check(options, topology) {
145
178
  }))), `${file}: must import ${name} from ${specifier}`);
146
179
  }
147
180
  const found = new Set();
148
- __rspack_external__babel_types_6e8337dd.traverseFast(ast, (node)=>{
181
+ const collectCalls = (node)=>{
149
182
  if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return;
150
183
  if (__rspack_external__babel_types_6e8337dd.isIdentifier(node.callee)) found.add(node.callee.name);
151
184
  if (__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) && !node.callee.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.object) && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property)) found.add(`${node.callee.object.name}.${node.callee.property.name}`);
152
- });
185
+ };
186
+ for (const shapeFile of shapeFiles){
187
+ const shapeAst = parseFile(shapeFile);
188
+ if (shapeAst) __rspack_external__babel_types_6e8337dd.traverseFast(shapeAst, collectCalls);
189
+ }
153
190
  for (const call of calls)assert(found.has(call), `${file}: must call ${call}(...)`);
154
191
  const source = read(file).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/gu, '');
155
192
  for (const [pattern, reason] of patterns)assert(pattern.test(source), `${file}: ${reason}`);
@@ -327,7 +364,7 @@ function check(options, topology) {
327
364
  ]
328
365
  ], [
329
366
  rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
330
- ]);
367
+ ], [], void 0, true);
331
368
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
332
369
  [
333
370
  /\bbackendFederationContract\b/u,
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Only `.tsx` is JSX. Enabling JSX for a `.ts` file misreads a generic arrow
3
+ * such as `const f = <T>(value: T) => value` as an unclosed JSX element.
4
+ */
5
+ export declare const consumerParserPlugins: (filePath: string) => ('typescript' | 'jsx')[];
1
6
  export interface MicroVerticalApiBaselineExpectation {
2
7
  /** Optional override for operation IDs outside reachable endpoint naming conventions. */
3
8
  readonly operationPaths?: Readonly<Record<string, string>>;
package/package.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "engines": {
24
24
  "node": ">=20"
25
25
  },
26
- "version": "3.9.0-ultramodern.7",
26
+ "version": "3.9.0-ultramodern.9",
27
27
  "types": "./dist/types/index.d.ts",
28
28
  "main": "./dist/esm-node/index.js",
29
29
  "typesVersions": {