@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13

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,63 +38,31 @@ var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
40
  configuredMicroVerticalApiStem: ()=>configuredMicroVerticalApiStem,
41
- consumerParserPlugins: ()=>consumerParserPlugins,
42
41
  microVerticalApiBaselineViolation: ()=>microVerticalApiBaselineViolation
43
42
  });
44
43
  const external_node_fs_namespaceObject = require("node:fs");
45
44
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
45
+ const external_node_module_namespaceObject = require("node:module");
46
46
  const external_node_path_namespaceObject = require("node:path");
47
47
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
48
- const parser_namespaceObject = require("@babel/parser");
49
- const traverse_namespaceObject = require("@babel/traverse");
50
- var traverse_default = /*#__PURE__*/ __webpack_require__.n(traverse_namespaceObject);
51
48
  const types_namespaceObject = require("@babel/types");
49
+ const external_resolve_exports_namespaceObject = require("resolve.exports");
52
50
  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
- ];
59
- class ConsumerSyntaxError extends Error {
60
- }
61
- class ConsumerValidationHub extends traverse_namespaceObject.Hub {
62
- buildError(_node, message) {
63
- return new ConsumerSyntaxError(message);
64
- }
65
- }
51
+ const external_source_analysis_cjs_namespaceObject = require("./source-analysis.cjs");
66
52
  function parseConsumer(filePath) {
67
53
  if (external_node_fs_default().statSync(filePath).size > 1000000) throw new Error(`${filePath}: consumer source exceeds 1 MB analysis budget`);
68
- let file;
69
- try {
70
- file = (0, parser_namespaceObject.parse)(external_node_fs_default().readFileSync(filePath, 'utf8'), {
71
- sourceType: 'module',
72
- sourceFilename: filePath,
73
- plugins: consumerParserPlugins(filePath)
74
- });
75
- } catch (error) {
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));
77
- throw error;
78
- }
79
- const program = traverse_namespaceObject.NodePath.get({
80
- hub: new ConsumerValidationHub(),
81
- parentPath: void 0,
82
- parent: file,
83
- container: file,
84
- key: 'program'
85
- });
86
- program.setContext();
87
- traverse_default()(file, {
54
+ const file = (0, external_source_analysis_cjs_namespaceObject.parseSource)(external_node_fs_default().readFileSync(filePath, 'utf8'), filePath);
55
+ (0, external_source_analysis_cjs_namespaceObject.traverseSource)(file, {
88
56
  AssignmentExpression (p) {
89
- throw new ConsumerSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
57
+ throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
90
58
  },
91
59
  UpdateExpression (p) {
92
- throw new ConsumerSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
60
+ throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
93
61
  },
94
62
  TSModuleDeclaration () {
95
- throw new ConsumerSyntaxError('contract bindings must not be merged with namespaces');
63
+ throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError('contract bindings must not be merged with namespaces');
96
64
  }
97
- }, program.scope, void 0, program);
65
+ });
98
66
  return file;
99
67
  }
100
68
  const camelCaseStem = (stem)=>stem.replaceAll(/-([a-z0-9])/gu, (_match, letter)=>letter.toUpperCase());
@@ -119,24 +87,19 @@ const accessPath = (node)=>{
119
87
  }
120
88
  };
121
89
  const isAccessPath = (node, expected)=>accessPath(node)?.join('.') === expected.join('.');
122
- const unwrapExpression = (expression)=>{
123
- let current = expression;
124
- while(types_namespaceObject.isTSAsExpression(current) || types_namespaceObject.isParenthesizedExpression(current) || types_namespaceObject.isTSSatisfiesExpression(current))current = current.expression;
125
- return current;
126
- };
127
90
  const stringLiteral = (expression)=>{
128
91
  if (null == expression) return;
129
- const unwrapped = unwrapExpression(expression);
92
+ const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
130
93
  return types_namespaceObject.isStringLiteral(unwrapped) ? unwrapped.value : void 0;
131
94
  };
132
95
  const numericLiteral = (expression)=>{
133
96
  if (null == expression) return;
134
- const unwrapped = unwrapExpression(expression);
97
+ const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
135
98
  return types_namespaceObject.isNumericLiteral(unwrapped) ? Number(unwrapped.value) : void 0;
136
99
  };
137
100
  const callExpression = (expression, callee)=>{
138
101
  if (null == expression) return;
139
- const unwrapped = unwrapExpression(expression);
102
+ const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
140
103
  return types_namespaceObject.isCallExpression(unwrapped) && isAccessPath(unwrapped.callee, callee) ? unwrapped : void 0;
141
104
  };
142
105
  const localConst = (sourceFile, name)=>{
@@ -164,16 +127,81 @@ const exportedConst = (sourceFile, name)=>{
164
127
  }))) return localConst(sourceFile, name);
165
128
  }
166
129
  };
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, '');
130
+ const inside = (file, boundary)=>file === boundary || file.startsWith(`${boundary}${external_node_path_default().sep}`);
131
+ const containingWorkspace = (file)=>{
132
+ for(let directory = external_node_path_default().dirname(file);; directory = external_node_path_default().dirname(directory)){
133
+ if (external_node_fs_default().existsSync(external_node_path_default().join(directory, 'pnpm-workspace.yaml'))) return external_node_fs_default().realpathSync(directory);
134
+ if (directory === external_node_path_default().dirname(directory)) return;
135
+ }
136
+ };
137
+ const sourceBoundary = (file, workspace)=>{
138
+ for(let directory = external_node_path_default().dirname(file);; directory = external_node_path_default().dirname(directory)){
139
+ if (external_node_fs_default().existsSync(external_node_path_default().join(directory, 'package.json')) && directory !== workspace) return external_node_fs_default().realpathSync(directory);
140
+ if (directory === workspace || directory === external_node_path_default().dirname(directory)) return external_node_fs_default().realpathSync(external_node_path_default().dirname(file));
141
+ }
142
+ };
143
+ const sourceFileAt = (target, boundary)=>{
144
+ const stem = target.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
171
145
  for (const candidate of [
172
146
  `${stem}.ts`,
173
147
  `${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;
148
+ `${stem}.mts`,
149
+ `${stem}.cts`,
150
+ target,
151
+ external_node_path_default().join(target, 'index.ts'),
152
+ external_node_path_default().join(target, 'index.tsx')
153
+ ])try {
154
+ const real = external_node_fs_default().realpathSync(candidate);
155
+ if (inside(real, boundary) && external_node_fs_default().statSync(real).isFile()) return real;
156
+ } catch {}
157
+ };
158
+ const packageEntry = (specifier)=>{
159
+ if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#')) return;
160
+ const parts = specifier.split('/');
161
+ const count = specifier.startsWith('@') ? 2 : 1;
162
+ if (parts.length < count || parts.slice(0, count).some((part)=>!part)) return;
163
+ return {
164
+ name: parts.slice(0, count).join('/'),
165
+ exportKey: parts.length === count ? '.' : `./${parts.slice(count).join('/')}`
166
+ };
167
+ };
168
+ const resolveModulePath = (module, specifier, workspace)=>{
169
+ if (/^\.\.?\//u.test(specifier)) {
170
+ const file = sourceFileAt(external_node_path_default().resolve(external_node_path_default().dirname(module.path), specifier), module.boundary);
171
+ return void 0 === file ? void 0 : {
172
+ path: file,
173
+ boundary: module.boundary
174
+ };
175
+ }
176
+ const entry = packageEntry(specifier);
177
+ if (!entry || !workspace) return;
178
+ const paths = (0, external_node_module_namespaceObject.createRequire)(module.path).resolve.paths(entry.name) ?? [];
179
+ for (const lookup of paths){
180
+ const directory = external_node_path_default().join(lookup, entry.name);
181
+ const manifest = external_node_path_default().join(directory, 'package.json');
182
+ if (!external_node_fs_default().existsSync(manifest)) continue;
183
+ const boundary = external_node_fs_default().realpathSync(directory);
184
+ if (!inside(boundary, workspace) || boundary.split(external_node_path_default().sep).includes('node_modules')) break;
185
+ const pkg = JSON.parse(external_node_fs_default().readFileSync(manifest, 'utf8'));
186
+ if (pkg.name !== entry.name || void 0 === pkg.exports) break;
187
+ let target;
188
+ try {
189
+ const targets = (0, external_resolve_exports_namespaceObject.exports)(pkg, entry.exportKey, {
190
+ conditions: [
191
+ 'modern:source'
192
+ ]
193
+ });
194
+ target = Array.isArray(targets) ? targets[0] : void 0;
195
+ } catch {
196
+ break;
197
+ }
198
+ if (!target || !target.startsWith('./')) break;
199
+ const file = sourceFileAt(external_node_path_default().resolve(boundary, target), boundary);
200
+ return void 0 === file ? void 0 : {
201
+ path: file,
202
+ boundary
203
+ };
204
+ }
177
205
  };
178
206
  const createModuleReader = (root)=>{
179
207
  const modules = new Map([
@@ -182,14 +210,15 @@ const createModuleReader = (root)=>{
182
210
  root
183
211
  ]
184
212
  ]);
185
- return (filePath)=>{
213
+ return (filePath, boundary)=>{
186
214
  if (modules.has(filePath)) return modules.get(filePath);
187
215
  if (modules.size >= 256) return;
188
216
  let module;
189
217
  try {
190
218
  module = {
191
219
  file: parseConsumer(filePath),
192
- path: filePath
220
+ path: filePath,
221
+ boundary
193
222
  };
194
223
  } catch {
195
224
  module = void 0;
@@ -219,7 +248,7 @@ const reexportedBinding = (module, name)=>{
219
248
  if (!types_namespaceObject.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
220
249
  const specifier = statement.specifiers.find((value)=>types_namespaceObject.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
221
250
  if (void 0 !== specifier && types_namespaceObject.isExportSpecifier(specifier)) return {
222
- local: specifier.local.name,
251
+ local: types_namespaceObject.isIdentifier(specifier.local) ? specifier.local.name : specifier.local.value,
223
252
  ...statement.source ? {
224
253
  specifier: statement.source.value
225
254
  } : {}
@@ -234,7 +263,7 @@ const defaultExport = (module)=>{
234
263
  return types_namespaceObject.isExpression(declaration) ? declaration : void 0;
235
264
  }
236
265
  };
237
- const resolveBinding = (module, name, scope, readModule, seen)=>{
266
+ const resolveBinding = (module, name, scope, readModule, workspace, seen)=>{
238
267
  const key = `${scope}:${module.path}#${name}`;
239
268
  if (seen.has(key) || seen.size >= 512) return;
240
269
  seen.add(key);
@@ -244,9 +273,9 @@ const resolveBinding = (module, name, scope, readModule, seen)=>{
244
273
  module
245
274
  };
246
275
  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);
276
+ const resolved = resolveModulePath(module, specifier, workspace);
277
+ const target = resolved && readModule(resolved.path, resolved.boundary);
278
+ return void 0 === target ? void 0 : resolveBinding(target, imported, 'export', readModule, workspace, seen);
250
279
  };
251
280
  if ('local' === scope) {
252
281
  const imported = importedBinding(module, name);
@@ -254,14 +283,14 @@ const resolveBinding = (module, name, scope, readModule, seen)=>{
254
283
  }
255
284
  if ('default' === name) {
256
285
  const exported = defaultExport(module);
257
- if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, seen);
286
+ if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, workspace, seen);
258
287
  return void 0 === exported ? void 0 : {
259
288
  expression: exported,
260
289
  module
261
290
  };
262
291
  }
263
292
  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);
293
+ if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', readModule, workspace, seen) : throughModule(reexported.specifier, reexported.local);
265
294
  for (const statement of module.file.program.body){
266
295
  if (!types_namespaceObject.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
267
296
  const resolved = throughModule(statement.source.value, name);
@@ -270,7 +299,7 @@ const resolveBinding = (module, name, scope, readModule, seen)=>{
270
299
  };
271
300
  const objectLiteral = (expression)=>{
272
301
  if (null == expression) return;
273
- const unwrapped = unwrapExpression(expression);
302
+ const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
274
303
  return types_namespaceObject.isObjectExpression(unwrapped) ? unwrapped : void 0;
275
304
  };
276
305
  const propertyAssignments = (properties)=>{
@@ -289,7 +318,7 @@ const exactCall = (expression, callee, argumentCount)=>{
289
318
  };
290
319
  const sharedSchemaObject = (declaration, sharedSchemaName, protectedFields)=>{
291
320
  if (declaration?.init == null) return;
292
- const initializer = unwrapExpression(declaration.init);
321
+ const initializer = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(declaration.init);
293
322
  if (types_namespaceObject.isIdentifier(initializer) && initializer.name === sharedSchemaName) return {
294
323
  assignments: new Map(),
295
324
  identity: true
@@ -313,14 +342,14 @@ const sharedSchemaObject = (declaration, sharedSchemaName, protectedFields)=>{
313
342
  };
314
343
  const directCallChain = (expression)=>{
315
344
  if (null == expression) return;
316
- let current = unwrapExpression(expression);
345
+ let current = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
317
346
  const methods = [];
318
347
  while(types_namespaceObject.isCallExpression(current) && types_namespaceObject.isMemberExpression(current.callee) && !current.callee.computed && types_namespaceObject.isIdentifier(current.callee.property) && !types_namespaceObject.isIdentifier(current.callee.object)){
319
348
  methods.unshift({
320
349
  arguments: current.arguments,
321
350
  name: current.callee.property.name
322
351
  });
323
- current = unwrapExpression(current.callee.object);
352
+ current = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(current.callee.object);
324
353
  }
325
354
  if (!types_namespaceObject.isCallExpression(current)) return;
326
355
  return {
@@ -331,7 +360,7 @@ const directCallChain = (expression)=>{
331
360
  const brandedStringSchemaIsExact = (declaration, brand)=>{
332
361
  const initializer = declaration?.init;
333
362
  if (null == initializer) return false;
334
- const pipeCall = unwrapExpression(initializer);
363
+ const pipeCall = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(initializer);
335
364
  if (!types_namespaceObject.isCallExpression(pipeCall) || !types_namespaceObject.isMemberExpression(pipeCall.callee) || pipeCall.callee.computed || !types_namespaceObject.isIdentifier(pipeCall.callee.property, {
336
365
  name: 'pipe'
337
366
  }) || !isAccessPath(pipeCall.callee.object, [
@@ -415,6 +444,7 @@ const nonEndpointCombinators = {
415
444
  };
416
445
  const reachableEndpoints = (rootModule, declaration)=>{
417
446
  const readModule = createModuleReader(rootModule);
447
+ const workspace = containingWorkspace(rootModule.path);
418
448
  const active = new Set();
419
449
  const endpoints = [];
420
450
  const identities = new Set();
@@ -431,12 +461,12 @@ const reachableEndpoints = (rootModule, declaration)=>{
431
461
  let visits = 0;
432
462
  const visit = (expression, module, kind, group = '')=>{
433
463
  if (!expression) return false;
434
- const node = unwrapExpression(expression);
464
+ const node = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
435
465
  if (active.has(node) || active.size >= 128 || ++visits > 2048) return false;
436
466
  active.add(node);
437
467
  try {
438
468
  if (types_namespaceObject.isIdentifier(node)) {
439
- const resolved = resolveBinding(module, node.name, 'local', readModule, new Set());
469
+ const resolved = resolveBinding(module, node.name, 'local', readModule, workspace, new Set());
440
470
  return void 0 !== resolved && visit(resolved.expression, resolved.module, kind, group);
441
471
  }
442
472
  if ('endpoint' === kind) {
@@ -622,7 +652,7 @@ const readinessSchemaIsShared = (declaration, markerSchemaName, ownerMarkerIsIde
622
652
  ]);
623
653
  return void 0 !== schema && (schema.identity && ownerMarkerIsIdentity || 1 === schema.assignments.size && identifierName(schema.assignments.get('marker')?.value) === markerSchemaName);
624
654
  };
625
- const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName(unwrapExpression(declaration.init)) === identifier;
655
+ const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName((0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(declaration.init)) === identifier;
626
656
  const validateParsedContract = (module, stem, expectation)=>{
627
657
  const sourceFile = module.file;
628
658
  const exportStem = camelCaseStem(stem);
@@ -653,23 +683,23 @@ const configuredMicroVerticalApiStem = (verticalPath, verticals)=>{
653
683
  };
654
684
  const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
655
685
  try {
656
- const sourceFile = parseConsumer(filePath);
686
+ const realPath = external_node_fs_default().realpathSync(filePath);
687
+ const sourceFile = parseConsumer(realPath);
657
688
  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';
658
689
  return validateParsedContract({
659
690
  file: sourceFile,
660
- path: filePath
691
+ path: realPath,
692
+ boundary: sourceBoundary(realPath, containingWorkspace(realPath))
661
693
  }, stem, expectation);
662
694
  } catch (error) {
663
- if (error instanceof ConsumerSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
695
+ if (error instanceof external_source_analysis_cjs_namespaceObject.SourceSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
664
696
  throw error;
665
697
  }
666
698
  };
667
699
  exports.configuredMicroVerticalApiStem = __webpack_exports__.configuredMicroVerticalApiStem;
668
- exports.consumerParserPlugins = __webpack_exports__.consumerParserPlugins;
669
700
  exports.microVerticalApiBaselineViolation = __webpack_exports__.microVerticalApiBaselineViolation;
670
701
  for(var __rspack_i in __webpack_exports__)if (-1 === [
671
702
  "configuredMicroVerticalApiStem",
672
- "consumerParserPlugins",
673
703
  "microVerticalApiBaselineViolation"
674
704
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
675
705
  Object.defineProperty(exports, '__esModule', {
@@ -50,6 +50,7 @@ const parser_namespaceObject = require("@babel/parser");
50
50
  const types_namespaceObject = require("@babel/types");
51
51
  const external_microvertical_api_baseline_cjs_namespaceObject = require("./microvertical-api-baseline.cjs");
52
52
  const external_microvertical_api_owner_cjs_namespaceObject = require("./microvertical-api-owner.cjs");
53
+ const external_source_analysis_cjs_namespaceObject = require("./source-analysis.cjs");
53
54
  const external_strict_effect_runtime_cjs_namespaceObject = require("./strict-effect-runtime.cjs");
54
55
  const ignored = new Set([
55
56
  '.git',
@@ -65,13 +66,42 @@ const normalize = (file)=>file.split(external_node_path_default().sep).join('/')
65
66
  const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
66
67
  const apiApp = (app)=>'vertical' === app.kind && 'ui-only' !== app.surfaceProfile && 'horizontal-remote' !== app.deliveryUnitKind;
67
68
  function configuredApps(root, supplied) {
68
- const source = supplied ?? JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(root, '.modernjs/ultramodern.json'), 'utf8')).topology?.apps;
69
- if (!Array.isArray(source)) throw new Error('.modernjs/ultramodern.json: topology.apps must be an array');
69
+ const topology = supplied ? void 0 : JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(root, 'topology/reference-topology.json'), 'utf8'));
70
+ if (topology && (!topology.shell || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells))) throw new Error('topology/reference-topology.json: shell and verticals must be present');
71
+ const entries = topology?.shell ? [
72
+ topology.shell,
73
+ ...topology.shells ?? [],
74
+ ...topology.verticals ?? []
75
+ ] : void 0;
76
+ const source = supplied ?? entries?.map((entry)=>{
77
+ if (!entry || 'string' != typeof entry.path || !entry.path || external_node_path_default().isAbsolute(entry.path) || entry.path.split(/[\\/]/u).includes('..')) throw new Error('topology/reference-topology.json: each app must have an explicit workspace path');
78
+ const packageFile = external_node_path_default().join(root, entry.path, 'package.json');
79
+ const manifest = JSON.parse(external_node_fs_default().readFileSync(packageFile, 'utf8'));
80
+ if ('string' != typeof manifest.name || !manifest.name || void 0 !== entry.package && entry.package !== manifest.name) throw new Error(`${entry.path}/package.json: package name must match topology`);
81
+ const api = entry.api;
82
+ const stem = api?.stem ?? api?.readiness?.endpoint?.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)\/readiness$/u)?.[1];
83
+ return {
84
+ id: entry.id,
85
+ kind: entry.kind,
86
+ path: entry.path,
87
+ package: manifest.name,
88
+ surfaceProfile: entry.surfaceProfile,
89
+ deliveryUnitKind: entry.deliveryUnitKind,
90
+ ...void 0 === api ? {} : {
91
+ api: {
92
+ stem,
93
+ prefix: api.bff?.prefix,
94
+ protocol: api.protocol
95
+ }
96
+ }
97
+ };
98
+ });
99
+ if (!Array.isArray(source)) throw new Error('topology/reference-topology.json: shell and verticals must describe apps');
70
100
  const paths = new Set();
71
101
  for (const app of source){
72
- if (!app || 'object' != typeof app || 'string' != typeof app.path || !app.path || external_node_path_default().isAbsolute(app.path) || app.path.split(/[\\/]/u).includes('..')) throw new Error('.modernjs/ultramodern.json: each app must have a relative workspace path');
102
+ if (!app || 'object' != typeof app || 'string' != typeof app.path || !app.path || external_node_path_default().isAbsolute(app.path) || app.path.split(/[\\/]/u).includes('..')) throw new Error('topology/reference-topology.json: each app must have a relative workspace path');
73
103
  const key = normalize(app.path);
74
- if (paths.has(key)) throw new Error(`.modernjs/ultramodern.json: duplicate app path ${key}`);
104
+ if (paths.has(key)) throw new Error(`topology/reference-topology.json: duplicate app path ${key}`);
75
105
  paths.add(key);
76
106
  for (const name of [
77
107
  'id',
@@ -80,7 +110,7 @@ function configuredApps(root, supplied) {
80
110
  'surfaceProfile',
81
111
  'deliveryUnitKind'
82
112
  ])if (void 0 !== app[name] && 'string' != typeof app[name]) throw new Error(`${key}: ${name} must be a string`);
83
- for (const [field, allowed] of Object.entries({
113
+ const allowedFields = {
84
114
  kind: [
85
115
  'shell',
86
116
  'vertical'
@@ -94,7 +124,11 @@ function configuredApps(root, supplied) {
94
124
  'microvertical',
95
125
  'horizontal-remote'
96
126
  ]
97
- }))if (void 0 !== app[field] && !allowed.includes(app[field])) throw new Error(`${key}: invalid ${field}`);
127
+ };
128
+ for (const field of Object.keys(allowedFields)){
129
+ const value = app[field];
130
+ if (void 0 !== value && !allowedFields[field].includes(value)) throw new Error(`${key}: invalid ${field}`);
131
+ }
98
132
  if (void 0 !== app.api) {
99
133
  if (!app.api || 'object' != typeof app.api || Array.isArray(app.api)) throw new Error(`${key}: api must be an object`);
100
134
  if (void 0 !== app.api.protocol && ![
@@ -159,7 +193,7 @@ function check(options, topology) {
159
193
  const ast = (0, parser_namespaceObject.parse)(read(file), {
160
194
  sourceType: 'module',
161
195
  sourceFilename: file,
162
- plugins: (0, external_microvertical_api_baseline_cjs_namespaceObject.consumerParserPlugins)(file)
196
+ plugins: (0, external_source_analysis_cjs_namespaceObject.consumerParserPlugins)(file)
163
197
  });
164
198
  parsed.set(file, ast);
165
199
  return ast;
@@ -182,12 +216,27 @@ function check(options, topology) {
182
216
  `${base}/index.tsx`
183
217
  ].find((candidate)=>exists(candidate));
184
218
  };
185
- const composedModules = (file)=>{
219
+ const clientExportFile = (appPath, exported)=>{
220
+ if ('string' != typeof exported || !exported.startsWith('./') || exported.split(/[\\/]/u).includes('..') || !/\.[cm]?[jt]sx?$/u.test(exported)) return;
221
+ const appDirectory = absolute(appPath);
222
+ const candidate = external_node_path_default().resolve(appDirectory, exported);
223
+ try {
224
+ const workspace = external_node_fs_default().realpathSync(root);
225
+ const owner = external_node_fs_default().realpathSync(appDirectory);
226
+ const real = external_node_fs_default().realpathSync(candidate);
227
+ if (!owner.startsWith(`${workspace}${external_node_path_default().sep}`) || !real.startsWith(`${owner}${external_node_path_default().sep}`) || !external_node_fs_default().statSync(real).isFile()) return;
228
+ } catch {
229
+ return;
230
+ }
231
+ return normalize(external_node_path_default().relative(root, candidate));
232
+ };
233
+ const composedModules = (file, appPath)=>{
186
234
  const order = [];
187
235
  const seen = new Set();
188
236
  const queue = [
189
237
  file
190
238
  ];
239
+ const owner = external_node_fs_default().realpathSync(absolute(appPath));
191
240
  while(queue.length > 0 && order.length < 128){
192
241
  const current = queue.shift();
193
242
  if (seen.has(current)) continue;
@@ -198,16 +247,19 @@ function check(options, topology) {
198
247
  for (const statement of ast.program.body){
199
248
  const source = (types_namespaceObject.isImportDeclaration(statement) || types_namespaceObject.isExportNamedDeclaration(statement) || types_namespaceObject.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
200
249
  const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
201
- if (void 0 !== resolved && !seen.has(resolved)) queue.push(resolved);
250
+ if (void 0 !== resolved && !seen.has(resolved)) {
251
+ const real = external_node_fs_default().realpathSync(absolute(resolved));
252
+ if (real.startsWith(`${owner}${external_node_path_default().sep}`)) queue.push(resolved);
253
+ }
202
254
  }
203
255
  }
204
256
  return order;
205
257
  };
206
- const moduleShape = (file, imports, calls, patterns = [], defaultExport, composed = false)=>{
258
+ const moduleShape = (file, imports, calls, patterns = [], defaultExport, composedAppPath)=>{
207
259
  if (!exists(file)) return;
208
260
  const ast = parseFile(file);
209
261
  if (!ast) return;
210
- const shapeFiles = composed ? composedModules(file) : [
262
+ const shapeFiles = composedAppPath ? composedModules(file, composedAppPath) : [
211
263
  file
212
264
  ];
213
265
  const body = shapeFiles.flatMap((shapeFile)=>parseFile(shapeFile)?.program.body ?? []);
@@ -317,19 +369,25 @@ function check(options, topology) {
317
369
  }
318
370
  return;
319
371
  }
320
- if (app?.kind === 'vertical') assert(void 0 !== app.api, `${appPath}: vertical must declare its Effect API in .modernjs/ultramodern.json`);
372
+ if (app?.kind === 'vertical') assert(void 0 !== app.api, `${appPath}: vertical must declare its Effect API in topology/reference-topology.json`);
321
373
  const stem = app?.api?.stem ?? external_node_path_default().posix.basename(appPath);
322
374
  const rpc = app?.api?.protocol === 'rpc';
323
375
  const contract = `${appPath}/shared/${rpc ? 'rpc' : 'api'}.ts`;
324
- const client = `${appPath}/src/api/${stem}-${rpc ? 'rpc-client' : 'client'}.ts`;
325
376
  const entry = `${appPath}/api/index.ts`;
326
377
  for (const file of [
327
378
  entry,
328
- contract,
329
- client
379
+ contract
330
380
  ])assert(exists(file), `${file}: required API surface is missing`);
381
+ const packageFile = `${appPath}/package.json`;
382
+ assert(exists(packageFile), `${packageFile}: required API package is missing`);
383
+ if (!exists(packageFile)) return;
384
+ const manifest = JSON.parse(read(packageFile));
385
+ assert(manifest.exports?.['./api'] === `./shared/${rpc ? 'rpc' : 'api'}.ts`, `${packageFile}: invalid ./api export`);
386
+ const clientKey = `./api/${rpc ? 'rpc-client' : 'client'}`;
387
+ const client = clientExportFile(appPath, manifest.exports?.[clientKey]);
388
+ assert(void 0 !== client, `${packageFile}: invalid API client export`);
389
+ assert(manifest.exports?.[`./api/${rpc ? 'client' : 'rpc-client'}`] === void 0, `${packageFile}: forbidden opposite protocol client export`);
331
390
  noPath(`${appPath}/shared/${rpc ? 'api' : 'rpc'}.ts`, `must not emit a ${rpc ? 'REST' : 'RPC'} contract`);
332
- noPath(`${appPath}/src/api/${stem}-${rpc ? 'client' : 'rpc-client'}.ts`, `must not emit a ${rpc ? 'REST' : 'RPC'} client`);
333
391
  if (rpc) {
334
392
  moduleShape(entry, [
335
393
  [
@@ -395,21 +453,24 @@ function check(options, topology) {
395
453
  const violation = (0, external_microvertical_api_baseline_cjs_namespaceObject.microVerticalApiBaselineViolation)(stem, absolute(contract), expectation);
396
454
  if (violation) diagnostics.push(`${contract}: ${violation}`);
397
455
  }
398
- moduleShape(client, [
399
- [
400
- effect,
456
+ if (client) {
457
+ const relativeContract = external_node_path_default().posix.relative(external_node_path_default().posix.dirname(client), contract);
458
+ moduleShape(client, [
459
+ [
460
+ effect,
461
+ [
462
+ 'Effect',
463
+ rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
464
+ ]
465
+ ],
401
466
  [
402
- 'Effect',
403
- rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
467
+ relativeContract.startsWith('.') ? relativeContract : `./${relativeContract}`,
468
+ []
404
469
  ]
405
- ],
406
- [
407
- rpc ? '../../shared/rpc.ts' : '../../shared/api',
408
- []
409
- ]
410
- ], [
411
- rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
412
- ], [], void 0, true);
470
+ ], [
471
+ rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
472
+ ], [], void 0, appPath);
473
+ }
413
474
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
414
475
  [
415
476
  /\bbackendFederationContract\b/u,
@@ -450,13 +511,6 @@ function check(options, topology) {
450
511
  'must enable strictEffectApproach'
451
512
  ]
452
513
  ]);
453
- const packageFile = `${appPath}/package.json`;
454
- if (exists(packageFile)) {
455
- const manifest = JSON.parse(read(packageFile));
456
- assert(manifest.exports?.['./api'] === `./shared/${rpc ? 'rpc' : 'api'}.ts`, `${packageFile}: invalid ./api export`);
457
- assert(manifest.exports?.[`./api/${rpc ? 'rpc-client' : 'client'}`] === `./src/api/${stem}-${rpc ? 'rpc-client' : 'client'}.ts`, `${packageFile}: invalid API client export`);
458
- assert(manifest.exports?.[`./api/${rpc ? 'client' : 'rpc-client'}`] === void 0, `${packageFile}: forbidden opposite protocol client export`);
459
- }
460
514
  });
461
515
  const shell = 'apps/shell-super-app';
462
516
  const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
@@ -52,7 +52,7 @@ const createStrictEffectApiBoundariesRule = ()=>({
52
52
  });
53
53
  (0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /@modern-js\/plugin-bff\/(?:server|hono-server)(?![\w-])/u, 'UltraModern API workspaces must not import Hono server helpers; use @modern-js/bff-effect/effect-edge and HttpApi.');
54
54
  (0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bruntimeFramework\s*(?::|=)\s*['"]hono['"]/u, 'UltraModern API apps must use bff.runtimeFramework: "effect".');
55
- (0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
55
+ (0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)(?=\/|\.[cm]?[jt]sx?['"]|['"])[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
56
56
  (0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bstrictEffectApproach\s*(?::|=)\s*false\b/u, 'UltraModern API apps must keep strictEffectApproach enabled.');
57
57
  if ((0, external_ast_cjs_namespaceObject.isApiEntryFile)(filename)) {
58
58
  const violation = (0, external_strict_effect_runtime_cjs_namespaceObject.strictEffectRuntimeTopologyViolation)(source, (0, external_strict_effect_runtime_cjs_namespaceObject.createEffectApiImportResolver)(filename));