@bleedingdev/modern-js-code-tools 3.9.3-ultramodern.1 → 3.9.3-ultramodern.2

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.
@@ -64,6 +64,8 @@ const ignored = new Set([
64
64
  'node_modules',
65
65
  'repos'
66
66
  ]);
67
+ const MAX_CLOSURE_DEPTH = 128;
68
+ const MAX_CLOSURE_MODULES = 2048;
67
69
  const normalize = (file)=>file.split(external_node_path_default().sep).join('/').replace(/^\.\//u, '');
68
70
  const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
69
71
  const apiApp = (app)=>'vertical' === app.kind && 'ui-only' !== app.surfaceProfile && 'horizontal-remote' !== app.deliveryUnitKind;
@@ -129,7 +131,7 @@ function configuredApps(root, supplied) {
129
131
  };
130
132
  for (const field of Object.keys(allowedFields)){
131
133
  const value = app[field];
132
- if (void 0 !== value && !allowedFields[field].includes(value)) throw new Error(`${key}: invalid ${field}`);
134
+ if ((void 0 !== value || 'kind' === field) && ('string' != typeof value || !allowedFields[field].includes(value))) throw new Error(`${key}: invalid ${field}`);
133
135
  }
134
136
  if (void 0 !== app.api) {
135
137
  if (!app.api || 'object' != typeof app.api || Array.isArray(app.api)) throw new Error(`${key}: api must be an object`);
@@ -171,6 +173,9 @@ function check(options, topology) {
171
173
  };
172
174
  try {
173
175
  const apps = configuredApps(root, options.configuredApps);
176
+ const workspace = external_node_fs_default().realpathSync(root);
177
+ const realRelative = (file)=>normalize(external_node_path_default().relative(workspace, external_node_fs_default().realpathSync(absolute(file))));
178
+ const inside = (directory, real)=>real.startsWith(`${directory}${external_node_path_default().sep}`);
174
179
  const byPath = new Map(apps.map((app)=>[
175
180
  normalize(app.path),
176
181
  app
@@ -211,12 +216,19 @@ function check(options, topology) {
211
216
  const resolveRelative = (fromFile, specifier)=>{
212
217
  if (!/^\.\.?\//u.test(specifier)) return;
213
218
  const base = external_node_path_default().posix.join(external_node_path_default().posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
214
- return [
219
+ const candidate = [
215
220
  `${base}.ts`,
216
221
  `${base}.tsx`,
217
222
  `${base}/index.ts`,
218
223
  `${base}/index.tsx`
219
- ].find((candidate)=>exists(candidate));
224
+ ].find((file)=>exists(file));
225
+ return void 0 === candidate ? void 0 : realRelative(candidate);
226
+ };
227
+ const graphBinding = (graph, module, expression, seen = new Set())=>{
228
+ if (seen.has(expression) || seen.size >= 128) return;
229
+ seen.add(expression);
230
+ const binding = types_namespaceObject.isIdentifier(expression) || types_namespaceObject.isMemberExpression(expression) ? graph.resolveReference(module, expression) : void 0;
231
+ return binding?.kind === 'declaration' && (types_namespaceObject.isIdentifier(binding.expression) || types_namespaceObject.isMemberExpression(binding.expression)) ? graphBinding(graph, binding.module, binding.expression, seen) : binding;
220
232
  };
221
233
  const clientExportFile = (appPath, exported)=>{
222
234
  if ('string' != typeof exported || !exported.startsWith('./') || exported.split(/[\\/]/u).includes('..') || !/\.[cm]?[jt]sx?$/u.test(exported)) return;
@@ -233,29 +245,65 @@ function check(options, topology) {
233
245
  return normalize(external_node_path_default().relative(root, candidate));
234
246
  };
235
247
  const composedModules = (file, appPath)=>{
236
- const order = [];
237
- const seen = new Set();
248
+ const owner = external_node_fs_default().realpathSync(absolute(appPath));
249
+ if (!inside(owner, external_node_fs_default().realpathSync(absolute(file)))) throw new Error(`consumer module closure escapes its owner: ${file}`);
250
+ const entry = realRelative(file);
251
+ const discovered = new Set([
252
+ entry
253
+ ]);
238
254
  const queue = [
239
- file
255
+ {
256
+ file: entry,
257
+ depth: 0
258
+ }
240
259
  ];
241
- const owner = external_node_fs_default().realpathSync(absolute(appPath));
242
- while(queue.length > 0 && order.length < 128){
243
- const current = queue.shift();
244
- if (seen.has(current)) continue;
245
- seen.add(current);
246
- order.push(current);
260
+ let graph;
261
+ for (const { file: current, depth } of queue){
247
262
  const ast = parseFile(current);
248
263
  if (!ast) continue;
249
- for (const statement of ast.program.body){
250
- const source = (types_namespaceObject.isImportDeclaration(statement) || types_namespaceObject.isExportNamedDeclaration(statement) || types_namespaceObject.isExportAllDeclaration(statement)) && statement.source ? statement.source.value : void 0;
251
- const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
252
- if (void 0 !== resolved && !seen.has(resolved)) {
253
- const real = external_node_fs_default().realpathSync(absolute(resolved));
254
- if (real.startsWith(`${owner}${external_node_path_default().sep}`)) queue.push(resolved);
264
+ const sources = [];
265
+ let callsRequire = false;
266
+ for (const statement of ast.program.body)if (types_namespaceObject.isImportDeclaration(statement) && 'type' !== statement.importKind && 'typeof' !== statement.importKind || (types_namespaceObject.isExportNamedDeclaration(statement) || types_namespaceObject.isExportAllDeclaration(statement)) && 'type' !== statement.exportKind && statement.source) sources.push(statement.source.value);
267
+ types_namespaceObject.traverseFast(ast, (node)=>{
268
+ if (types_namespaceObject.isImportExpression(node)) {
269
+ if (!types_namespaceObject.isStringLiteral(node.source)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
270
+ sources.push(node.source.value);
271
+ }
272
+ if ((types_namespaceObject.isCallExpression(node) || types_namespaceObject.isOptionalCallExpression(node)) && types_namespaceObject.isIdentifier((0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(node.callee, true), {
273
+ name: 'require'
274
+ })) callsRequire = true;
275
+ });
276
+ if (callsRequire) {
277
+ graph ??= (0, external_module_graph_cjs_namespaceObject.createModuleGraph)();
278
+ const module = graph.module(absolute(current));
279
+ types_namespaceObject.traverseFast(module.file, (node)=>{
280
+ if (!types_namespaceObject.isCallExpression(node) && !types_namespaceObject.isOptionalCallExpression(node)) return;
281
+ const callee = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(node.callee, true);
282
+ if (!types_namespaceObject.isIdentifier(callee, {
283
+ name: 'require'
284
+ }) || !graph.isUnboundReference(module, callee)) return;
285
+ const [argument] = node.arguments;
286
+ if (1 !== node.arguments.length || !types_namespaceObject.isStringLiteral(argument)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
287
+ sources.push(argument.value);
288
+ });
289
+ }
290
+ for (const source of sources){
291
+ if (!/^\.\.?\//u.test(source)) continue;
292
+ const resolved = resolveRelative(current, source);
293
+ if (void 0 === resolved) throw new Error(`consumer module closure has unresolved relative dependency: ${current} -> ${source}`);
294
+ if (!inside(owner, external_node_path_default().join(workspace, resolved))) throw new Error(`consumer module closure escapes its owner: ${current} -> ${source}`);
295
+ if (!discovered.has(resolved)) {
296
+ if (depth + 1 >= MAX_CLOSURE_DEPTH) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_DEPTH} import-depth analysis budget`);
297
+ if (discovered.size >= MAX_CLOSURE_MODULES) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_MODULES}-module analysis budget`);
298
+ discovered.add(resolved);
299
+ queue.push({
300
+ file: resolved,
301
+ depth: depth + 1
302
+ });
255
303
  }
256
304
  }
257
305
  }
258
- return order;
306
+ return queue.map((item)=>item.file);
259
307
  };
260
308
  const moduleShape = (file, imports, calls, patterns = [], defaultExport, composedAppPath)=>{
261
309
  if (!exists(file)) return;
@@ -294,6 +342,10 @@ function check(options, topology) {
294
342
  })), `${file}: must default-export ${defaultExport}`);
295
343
  };
296
344
  const effect = '@modern-js/bff-effect/effect-client';
345
+ const clientFactories = new Set([
346
+ 'makeEffectHttpApiClient',
347
+ 'makeEffectRpcClient'
348
+ ]);
297
349
  const appDirectories = [
298
350
  ...new Set([
299
351
  ...directories('apps'),
@@ -514,8 +566,8 @@ function check(options, topology) {
514
566
  ]);
515
567
  });
516
568
  const sourceRules = options.sourceRules ?? [];
517
- const graph = (0, external_module_graph_cjs_namespaceObject.createModuleGraph)();
518
569
  for (const file of sourceRules.length ? sourceFiles : [])if (/\.[cm]?[jt]sx?$/u.test(file)) guarded(file, ()=>{
570
+ const graph = (0, external_module_graph_cjs_namespaceObject.createModuleGraph)();
519
571
  let module;
520
572
  try {
521
573
  module = graph.module(absolute(file));
@@ -534,20 +586,178 @@ function check(options, topology) {
534
586
  for (const message of messages)diagnostics.push(`${file}: ${message}`);
535
587
  }
536
588
  });
537
- const shell = 'apps/shell-super-app';
538
- const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
539
- if (exists(shell) && verticals.length) {
540
- const file = `${shell}/src/api/vertical-clients.ts`;
541
- assert(exists(file), `${file}: must aggregate vertical API clients`);
542
- if (exists(file)) guarded(file, ()=>{
543
- const ast = parseFile(file);
544
- for (const app of verticals){
545
- const specifier = `${app.package}/api/${app.api?.protocol === 'rpc' ? 'rpc-client' : 'client'}`;
546
- assert(ast?.program.body.some((item)=>types_namespaceObject.isExportNamedDeclaration(item) && item.source?.value === specifier), `${file}: must re-export ${specifier}`);
589
+ const checkShellExports = (shellPath)=>{
590
+ const packageFile = `${shellPath}/package.json`;
591
+ const manifest = JSON.parse(read(packageFile));
592
+ noPath(`${shellPath}/src/api/vertical-clients.ts`, 'Shell client registries are forbidden; import each owner client directly');
593
+ assert(!Object.hasOwn(manifest.exports ?? {}, './api/clients'), `${packageFile}: Shell ./api/clients exports are forbidden`);
594
+ const owner = external_node_fs_default().realpathSync(absolute(shellPath));
595
+ const invalidExport = ()=>assert(false, `${packageFile}: invalid Shell public source export`);
596
+ const targets = (exported)=>'string' == typeof exported ? [
597
+ exported
598
+ ] : exported && 'object' == typeof exported ? Object.values(exported).flatMap(targets) : [];
599
+ const filesUnder = (directory, seen = new Set())=>{
600
+ const real = external_node_fs_default().realpathSync(absolute(directory));
601
+ if (real !== owner && !inside(owner, real)) {
602
+ invalidExport();
603
+ return [];
547
604
  }
548
- });
605
+ if (seen.has(real)) return [];
606
+ seen.add(real);
607
+ return external_node_fs_default().readdirSync(absolute(directory), {
608
+ withFileTypes: true
609
+ }).flatMap((entry)=>{
610
+ if (ignored.has(entry.name)) return [];
611
+ const file = `${directory}/${entry.name}`;
612
+ const stat = external_node_fs_default().statSync(absolute(file));
613
+ return stat.isDirectory() ? filesUnder(file, seen) : stat.isFile() ? [
614
+ file
615
+ ] : [];
616
+ });
617
+ };
618
+ const exportedFiles = (target)=>{
619
+ if (!target.includes('*')) return [
620
+ `${shellPath}/${target.slice(2)}`
621
+ ];
622
+ const prefix = target.slice(0, target.indexOf('*'));
623
+ const pattern = new RegExp(`^${target.split('*').map((part)=>part.replaceAll(/[.*+?^${}()|[\]\\]/gu, '\\$&')).join('.*')}$`, 'u');
624
+ return filesUnder(`${shellPath}/${prefix.slice(2, prefix.lastIndexOf('/'))}`).filter((file)=>pattern.test(`./${external_node_path_default().posix.relative(shellPath, file)}`));
625
+ };
626
+ const ownClientPrefix = `${manifest.name}/api/`;
627
+ const foreignSpecifier = (specifier)=>/\/api\/(?:rpc-)?client$/u.test(specifier) && !specifier.startsWith(ownClientPrefix);
628
+ for (const target of targets(manifest.exports))if (/\.[cm]?[jt]sx?$/u.test(target)) {
629
+ if (!target.startsWith('./') || target.split(/[\\/]/u).includes('..')) {
630
+ invalidExport();
631
+ continue;
632
+ }
633
+ for (const candidate of exportedFiles(target)){
634
+ const file = clientExportFile(shellPath, `./${external_node_path_default().posix.relative(shellPath, candidate)}`);
635
+ if (!file) {
636
+ invalidExport();
637
+ continue;
638
+ }
639
+ assert(!exposesForeignClient(file, owner, foreignSpecifier), `${file}: Shell public exports must not expose another owner's API client`);
640
+ }
641
+ }
642
+ };
643
+ const exposesForeignClient = (entry, owner, foreignSpecifier)=>{
644
+ const graph = (0, external_module_graph_cjs_namespaceObject.createModuleGraph)();
645
+ const seenValues = new Set();
646
+ const seenExports = new Map();
647
+ const workspacePath = (module)=>normalize(external_node_path_default().relative(workspace, module.path));
648
+ const resolveSource = (from, specifier)=>resolveRelative(from, specifier) ?? ownerClients.get(specifier);
649
+ const returnExpressions = (node)=>{
650
+ if (types_namespaceObject.isReturnStatement(node)) return node.argument ? [
651
+ node.argument
652
+ ] : [];
653
+ if (types_namespaceObject.isFunction(node)) return [];
654
+ return (types_namespaceObject.VISITOR_KEYS[node.type] ?? []).flatMap((key)=>{
655
+ const value = Reflect.get(node, key);
656
+ return (Array.isArray(value) ? value : [
657
+ value
658
+ ]).flatMap((child)=>types_namespaceObject.isNode(child) ? returnExpressions(child) : []);
659
+ });
660
+ };
661
+ const nestedValues = (node)=>{
662
+ if (types_namespaceObject.isObjectExpression(node)) return node.properties.flatMap((property)=>types_namespaceObject.isObjectProperty(property) ? [
663
+ property.value
664
+ ] : types_namespaceObject.isObjectMethod(property) ? returnExpressions(property.body) : types_namespaceObject.isSpreadElement(property) ? [
665
+ property.argument
666
+ ] : []);
667
+ if (types_namespaceObject.isArrayExpression(node)) return node.elements.filter((element)=>null !== element);
668
+ if (types_namespaceObject.isFunction(node)) return types_namespaceObject.isBlockStatement(node.body) ? returnExpressions(node.body) : [
669
+ node.body
670
+ ];
671
+ return [];
672
+ };
673
+ const foreignValue = (value, from)=>{
674
+ if ('external' === value.kind) {
675
+ const source = resolveSource(workspacePath(from), value.specifier);
676
+ if (!source) return foreignSpecifier(value.specifier);
677
+ return '*' === value.name ? inspect(source) : exportedValue(source, value.name);
678
+ }
679
+ if ('unresolved' === value.kind && types_namespaceObject.isImportDeclaration(value.node) && value.node.specifiers.some((specifier)=>types_namespaceObject.isImportNamespaceSpecifier(specifier))) {
680
+ const specifier = value.node.source.value;
681
+ const source = resolveSource(workspacePath(value.module), specifier);
682
+ return source ? inspect(source) : foreignSpecifier(specifier);
683
+ }
684
+ if ('node' !== value.kind || seenValues.has(value.node)) return false;
685
+ seenValues.add(value.node);
686
+ const { node, module } = value;
687
+ if ((types_namespaceObject.isCallExpression(node) || types_namespaceObject.isOptionalCallExpression(node)) && graph.evaluate(module, node.callee).some((callee)=>'external' === callee.kind && callee.specifier === effect && clientFactories.has(callee.name))) {
688
+ const [argument] = node.arguments;
689
+ const binding = argument ? graphBinding(graph, module, argument) : void 0;
690
+ if (module.boundary !== owner || binding?.kind !== 'declaration' || binding.module.boundary !== owner) return true;
691
+ }
692
+ return nestedValues(node).some((expression)=>graph.evaluate(module, expression).some((nested)=>foreignValue(nested, module)));
693
+ };
694
+ const exportName = (node)=>types_namespaceObject.isIdentifier(node) ? node.name : node.value;
695
+ const names = (source, visited = new Set())=>{
696
+ if (visited.has(source)) return [];
697
+ visited.add(source);
698
+ return graph.module(absolute(source)).file.program.body.flatMap((statement)=>{
699
+ if (types_namespaceObject.isExportDefaultDeclaration(statement)) return [
700
+ 'default'
701
+ ];
702
+ if (!types_namespaceObject.isExportNamedDeclaration(statement) && !types_namespaceObject.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return [];
703
+ if (types_namespaceObject.isExportAllDeclaration(statement)) {
704
+ const target = resolveSource(source, statement.source.value);
705
+ return target ? names(target, visited).filter((name)=>'default' !== name) : [];
706
+ }
707
+ return statement.declaration ? Object.keys(types_namespaceObject.getOuterBindingIdentifiers(statement.declaration)) : statement.specifiers.flatMap((specifier)=>types_namespaceObject.isExportSpecifier(specifier) && 'type' === specifier.exportKind ? [] : [
708
+ exportName(specifier.exported)
709
+ ]);
710
+ });
711
+ };
712
+ const exportedValue = (source, name)=>{
713
+ const visited = seenExports.get(source) ?? new Set();
714
+ if (visited.has(name)) return false;
715
+ visited.add(name);
716
+ seenExports.set(source, visited);
717
+ const module = graph.module(absolute(source));
718
+ const evaluates = (from, node)=>graph.evaluate(from, node).some((value)=>foreignValue(value, from));
719
+ const binding = graph.resolve(module, name, 'export');
720
+ if (binding?.kind === 'external') return foreignValue({
721
+ ...binding,
722
+ members: []
723
+ }, module);
724
+ if (binding?.kind === 'declaration') return evaluates(binding.module, binding.expression);
725
+ for (const statement of module.file.program.body){
726
+ if (types_namespaceObject.isExportDefaultDeclaration(statement) && 'default' === name) return evaluates(module, statement.declaration);
727
+ if (!types_namespaceObject.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
728
+ if (types_namespaceObject.isFunctionDeclaration(statement.declaration) && statement.declaration.id?.name === name) return evaluates(module, statement.declaration);
729
+ const specifier = statement.specifiers.find((item)=>exportName(item.exported) === name);
730
+ if (!specifier) continue;
731
+ if (types_namespaceObject.isExportSpecifier(specifier) && 'type' === specifier.exportKind) return false;
732
+ if (!statement.source) return types_namespaceObject.isExportSpecifier(specifier) ? evaluates(module, specifier.local) : false;
733
+ const target = resolveSource(source, statement.source.value);
734
+ if (!target) return foreignSpecifier(statement.source.value);
735
+ if (types_namespaceObject.isExportNamespaceSpecifier(specifier)) return inspect(target);
736
+ if (types_namespaceObject.isExportSpecifier(specifier)) return exportedValue(target, exportName(specifier.local));
737
+ }
738
+ return module.file.program.body.some((statement)=>{
739
+ if (!types_namespaceObject.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return false;
740
+ const target = resolveSource(source, statement.source.value);
741
+ return !!target && names(target).includes(name) && exportedValue(target, name);
742
+ });
743
+ };
744
+ const inspect = (source)=>[
745
+ ...new Set(names(source))
746
+ ].some((name)=>exportedValue(source, name));
747
+ return inspect(entry);
748
+ };
749
+ const ownerClients = new Map();
750
+ for (const app of apps)if (exists(`${app.path}/package.json`)) {
751
+ const manifest = JSON.parse(read(`${app.path}/package.json`));
752
+ for (const key of [
753
+ './api/client',
754
+ './api/rpc-client'
755
+ ]){
756
+ const file = clientExportFile(app.path, manifest.exports?.[key]);
757
+ if (file) ownerClients.set(`${manifest.name}${key.slice(1)}`, file);
758
+ }
549
759
  }
550
- if (exists(`${shell}/package.json`)) guarded(`${shell}/package.json`, ()=>assert(JSON.parse(read(`${shell}/package.json`)).exports?.['./api/clients'] === './src/api/vertical-clients.ts', `${shell}/package.json: must export ./api/clients`));
760
+ for (const shell of apps.filter((app)=>'shell' === app.kind))guarded(shell.path, ()=>checkShellExports(normalize(shell.path)));
551
761
  if (exists('topology/reference-topology.json')) guarded('topology/reference-topology.json', ()=>{
552
762
  for (const vertical of JSON.parse(read('topology/reference-topology.json')).verticals ?? []){
553
763
  if (vertical.api?.runtime === 'effect') {
@@ -232,6 +232,7 @@ function parseModule(filePath, workspace) {
232
232
  const file = (0, external_source_analysis_cjs_namespaceObject.parseSource)(external_node_fs_default().readFileSync(filePath, 'utf8'), filePath);
233
233
  let scope;
234
234
  const references = new Map();
235
+ const declarations = new Map();
235
236
  const returns = new Map();
236
237
  const bareReturns = new Set();
237
238
  const functionScopes = new Map();
@@ -342,6 +343,11 @@ function parseModule(filePath, workspace) {
342
343
  ReferencedIdentifier (p) {
343
344
  references.set(p.node, p.scope.getBinding(p.node.name));
344
345
  },
346
+ BindingIdentifier (p) {
347
+ if (!types_namespaceObject.isIdentifier(p.node)) return;
348
+ const binding = p.scope.getBinding(p.node.name);
349
+ if (binding?.identifier === p.node) declarations.set(p.node, binding);
350
+ },
345
351
  Function (p) {
346
352
  functionScopes.set(p.node, p.scope);
347
353
  },
@@ -407,6 +413,7 @@ function parseModule(filePath, workspace) {
407
413
  scope,
408
414
  workspace,
409
415
  references,
416
+ declarations,
410
417
  returns,
411
418
  bareReturns,
412
419
  functionScopes,
@@ -1142,7 +1149,7 @@ function createModuleGraph() {
1142
1149
  }
1143
1150
  };
1144
1151
  const mutated = (module, binding)=>binding.constantViolations.length > 0 || factsOf(module).merged.has(binding.identifier.name) || (aliasesOf(binding, factsOf(module).writes)?.some((alias)=>factsOf(module).writes.has(alias.binding)) ?? true);
1145
- const resolveBinding = (module, name, scope, chain, seen)=>{
1152
+ const resolveBinding = (module, name, scope, chain, seen, lexical)=>{
1146
1153
  const key = `${scope}:${module.path}#${name}`;
1147
1154
  if (seen.has(key) || seen.size >= MAX_LOOKUPS) return;
1148
1155
  seen.add(key);
@@ -1165,7 +1172,7 @@ function createModuleGraph() {
1165
1172
  return void 0 === next ? void 0 : resolveBinding(next, imported, 'export', hops, seen);
1166
1173
  };
1167
1174
  if ('local' === scope) {
1168
- const binding = factsOf(module).scope.getBinding(name);
1175
+ const binding = lexical ?? factsOf(module).scope.getBinding(name);
1169
1176
  if (!binding || mutated(module, binding)) return;
1170
1177
  if ('module' === binding.kind) {
1171
1178
  const imported = importOf(binding);
@@ -1194,6 +1201,39 @@ function createModuleGraph() {
1194
1201
  if (void 0 !== resolved) return resolved;
1195
1202
  }
1196
1203
  };
1204
+ const resolveReference = (module, reference)=>{
1205
+ const node = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(reference, true);
1206
+ const moduleFacts = factsOf(module);
1207
+ const bindingAt = (identifier)=>moduleFacts.references.get(identifier) ?? moduleFacts.declarations.get(identifier);
1208
+ if (types_namespaceObject.isIdentifier(node)) {
1209
+ const binding = bindingAt(node);
1210
+ return binding ? resolveBinding(module, node.name, 'local', [], new Set(), binding) : void 0;
1211
+ }
1212
+ if (!types_namespaceObject.isMemberExpression(node) || node.computed || !types_namespaceObject.isIdentifier(node.object) || !types_namespaceObject.isIdentifier(node.property)) return;
1213
+ const binding = bindingAt(node.object);
1214
+ if (!binding || 'module' !== binding.kind || mutated(module, binding)) return;
1215
+ const imported = importOf(binding);
1216
+ if (imported?.name !== '*') return;
1217
+ const target = follow(module, imported.specifier);
1218
+ const chain = [
1219
+ {
1220
+ path: module.path,
1221
+ name: node.object.name
1222
+ }
1223
+ ];
1224
+ if ('external' === target) return {
1225
+ kind: 'external',
1226
+ specifier: imported.specifier,
1227
+ name: node.property.name,
1228
+ chain
1229
+ };
1230
+ return void 0 === target ? void 0 : resolveBinding(target, node.property.name, 'export', chain, new Set());
1231
+ };
1232
+ const isUnboundReference = (module, reference)=>{
1233
+ const node = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(reference, true);
1234
+ const { references } = factsOf(module);
1235
+ return types_namespaceObject.isIdentifier(node) && references.has(node) && void 0 === references.get(node);
1236
+ };
1197
1237
  const evaluation = (walking)=>{
1198
1238
  const active = new Set();
1199
1239
  let frames = 0;
@@ -1801,6 +1841,8 @@ function createModuleGraph() {
1801
1841
  return {
1802
1842
  module: (filePath)=>load(filePath),
1803
1843
  resolve: (module, name, scope)=>resolveBinding(module, name, scope, [], new Set()),
1844
+ resolveReference,
1845
+ isUnboundReference,
1804
1846
  evaluate: (module, expression)=>evaluation(false).values(module, expression, []),
1805
1847
  reachable: (module, expression)=>evaluation(true).reachable(module, expression)
1806
1848
  };
@@ -51,6 +51,19 @@ const nodeRuntime = '@modern-js/bff-effect/effect';
51
51
  const assembly = '@modern-js/bff-effect/assembly';
52
52
  const failure = 'Generated API entries must export defineEffectBff(...) or the native Effect BFF assembly helper with an explicitly composed handler Layer and an unshadowed executable root; entries must implement handlers through HttpApiBuilder.group.';
53
53
  const MAX_API_SOURCE_MODULES = 256;
54
+ const httpRouteMethods = new Set([
55
+ '*',
56
+ 'GET',
57
+ 'POST',
58
+ 'PUT',
59
+ 'PATCH',
60
+ 'DELETE',
61
+ 'HEAD',
62
+ 'OPTIONS',
63
+ 'TRACE',
64
+ 'CONNECT',
65
+ 'QUERY'
66
+ ]);
54
67
  function createEffectApiImportResolver(filename) {
55
68
  const directory = external_node_fs_default().realpathSync(external_node_path_default().dirname(filename));
56
69
  const owner = external_node_fs_default().realpathSync(external_node_path_default().resolve(directory, '..'));
@@ -98,9 +111,12 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
98
111
  })) {
99
112
  let target = nodePath.isAssignmentExpression() ? nodePath.node.left : nodePath.node.argument;
100
113
  while(types_namespaceObject.isMemberExpression(target))target = target.object;
101
- if (types_namespaceObject.isIdentifier(target)) {
102
- const binding = nodePath.scope.getBinding(target.name);
103
- if (binding) mutated.add(binding);
114
+ let binding = types_namespaceObject.isIdentifier(target) ? nodePath.scope.getBinding(target.name) : void 0;
115
+ while(binding && !mutated.has(binding)){
116
+ mutated.add(binding);
117
+ const declarator = binding.path.node;
118
+ const alias = 'const' === binding.kind && types_namespaceObject.isVariableDeclarator(declarator) && declarator.init ? (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(declarator.init, true) : void 0;
119
+ binding = types_namespaceObject.isIdentifier(alias) ? binding.path.scope.getBinding(alias.name) : void 0;
104
120
  }
105
121
  }
106
122
  if (nodePath.isTSModuleDeclaration() && types_namespaceObject.isIdentifier(nodePath.node.id)) {
@@ -275,11 +291,48 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
275
291
  if (method(node.callee, 'Layer', 'mergeAll', layerSources)) return node.arguments.length > 0 && node.arguments.every((argument)=>handlers(argument, api));
276
292
  return method(node.callee, 'HttpApiBuilder', 'group') && 3 === node.arguments.length && apiIdentity(node.arguments[0]) === api && handled(node.arguments[2]);
277
293
  });
278
- const transport = (node)=>guarded(node, (node)=>{
294
+ const effectSources = [
295
+ edge,
296
+ 'effect',
297
+ 'effect/Effect'
298
+ ];
299
+ const routeHandler = (node)=>{
300
+ node = unwrap(node);
301
+ if (!types_namespaceObject.isCallExpression(node) || !method(node.callee, 'HttpRouter', 'add') || 3 !== node.arguments.length) return;
302
+ const [verb, route, handler] = node.arguments;
303
+ return types_namespaceObject.isStringLiteral(verb) && httpRouteMethods.has(verb.value) && types_namespaceObject.isStringLiteral(route) && (route.value.startsWith('/') || '*' === route.value) && (types_namespaceObject.isArrowFunctionExpression(handler) || types_namespaceObject.isFunctionExpression(handler)) ? handler : void 0;
304
+ };
305
+ const effectReference = (node)=>guarded(node, (node)=>{
279
306
  const init = initialized(node);
280
- if (init) return transport(init);
307
+ if (init) return types_namespaceObject.isIdentifier(unwrap(init)) && effectReference(init);
308
+ const value = imported(node);
309
+ return !!value && !value.namespace;
310
+ });
311
+ const capturedRouteFactory = (node)=>guarded(node, (node)=>{
312
+ const init = initialized(node) ?? externalValue(node);
313
+ if (init) return capturedRouteFactory(init);
314
+ if (!types_namespaceObject.isArrowFunctionExpression(node) || node.async || 1 !== node.params.length || !types_namespaceObject.isIdentifier(node.params[0]) || types_namespaceObject.isBlockStatement(node.body)) return false;
315
+ const handler = routeHandler(node.body);
316
+ const parameter = binding(node.params[0]);
317
+ return !!handler && !!parameter && parameter.referencePaths.some((reference)=>!!reference.findParent((path)=>path.node === handler));
318
+ });
319
+ const capturedTransport = (node)=>guarded(node, (node)=>{
320
+ const init = initialized(node);
321
+ if (init) return capturedTransport(init);
322
+ if (!types_namespaceObject.isCallExpression(node)) return false;
323
+ if (method(node.callee, 'Effect', 'map', effectSources)) return 2 === node.arguments.length && effectReference(node.arguments[0]) && capturedRouteFactory(node.arguments[1]);
324
+ if (!types_namespaceObject.isMemberExpression(node.callee) || node.callee.computed || !types_namespaceObject.isIdentifier(node.callee.property, {
325
+ name: 'pipe'
326
+ }) || 1 !== node.arguments.length || !effectReference(node.callee.object)) return false;
327
+ const map = unwrap(node.arguments[0]);
328
+ return types_namespaceObject.isCallExpression(map) && method(map.callee, 'Effect', 'map', effectSources) && 1 === map.arguments.length && capturedRouteFactory(map.arguments[0]);
329
+ });
330
+ const transport = (node, provided = false)=>guarded(node, (node)=>{
331
+ const init = initialized(node) ?? externalValue(node);
332
+ if (init) return transport(init, provided);
281
333
  if (!types_namespaceObject.isCallExpression(node)) return method(node, 'Layer', 'empty', layerSources);
282
- return pipe(node, transport) || method(node.callee, 'HttpRouter', 'cors') || method(node.callee, 'Layer', 'mergeAll', layerSources) && node.arguments.length > 0 && node.arguments.every(transport);
334
+ const providedHere = provided || node.arguments.some((argument)=>types_namespaceObject.isCallExpression(argument) && method(argument.callee, 'Layer', 'provide', layerSources) && 1 === argument.arguments.length);
335
+ return pipe(node, (value)=>transport(value, providedHere)) || void 0 !== routeHandler(node) || provided && method(node.callee, 'Layer', 'unwrap', layerSources) && 1 === node.arguments.length && capturedTransport(node.arguments[0]) || method(node.callee, 'HttpRouter', 'cors') || method(node.callee, 'Layer', 'mergeAll', layerSources) && node.arguments.length > 0 && node.arguments.every((argument)=>transport(argument, provided));
283
336
  });
284
337
  const directLayer = (node, api)=>guarded(node, (node)=>{
285
338
  const init = initialized(node);