@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.
@@ -4,7 +4,7 @@ import { parse } from "@babel/parser";
4
4
  import { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
5
5
  import { baselinePackage, resolveBaselinePackageDirectory } from "./microvertical-api-owner.js";
6
6
  import { createModuleGraph } from "./module-graph.js";
7
- import { SourceSyntaxError, consumerParserPlugins } from "./source-analysis.js";
7
+ import { SourceSyntaxError, consumerParserPlugins, unwrapExpression } from "./source-analysis.js";
8
8
  import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "./strict-effect-runtime.js";
9
9
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
10
10
  const ignored = new Set([
@@ -17,6 +17,8 @@ const ignored = new Set([
17
17
  'node_modules',
18
18
  'repos'
19
19
  ]);
20
+ const MAX_CLOSURE_DEPTH = 128;
21
+ const MAX_CLOSURE_MODULES = 2048;
20
22
  const normalize = (file)=>file.split(node_path.sep).join('/').replace(/^\.\//u, '');
21
23
  const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
22
24
  const apiApp = (app)=>'vertical' === app.kind && 'ui-only' !== app.surfaceProfile && 'horizontal-remote' !== app.deliveryUnitKind;
@@ -82,7 +84,7 @@ function configuredApps(root, supplied) {
82
84
  };
83
85
  for (const field of Object.keys(allowedFields)){
84
86
  const value = app[field];
85
- if (void 0 !== value && !allowedFields[field].includes(value)) throw new Error(`${key}: invalid ${field}`);
87
+ if ((void 0 !== value || 'kind' === field) && ('string' != typeof value || !allowedFields[field].includes(value))) throw new Error(`${key}: invalid ${field}`);
86
88
  }
87
89
  if (void 0 !== app.api) {
88
90
  if (!app.api || 'object' != typeof app.api || Array.isArray(app.api)) throw new Error(`${key}: api must be an object`);
@@ -124,6 +126,9 @@ function check(options, topology) {
124
126
  };
125
127
  try {
126
128
  const apps = configuredApps(root, options.configuredApps);
129
+ const workspace = node_fs.realpathSync(root);
130
+ const realRelative = (file)=>normalize(node_path.relative(workspace, node_fs.realpathSync(absolute(file))));
131
+ const inside = (directory, real)=>real.startsWith(`${directory}${node_path.sep}`);
127
132
  const byPath = new Map(apps.map((app)=>[
128
133
  normalize(app.path),
129
134
  app
@@ -164,12 +169,19 @@ function check(options, topology) {
164
169
  const resolveRelative = (fromFile, specifier)=>{
165
170
  if (!/^\.\.?\//u.test(specifier)) return;
166
171
  const base = node_path.posix.join(node_path.posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
167
- return [
172
+ const candidate = [
168
173
  `${base}.ts`,
169
174
  `${base}.tsx`,
170
175
  `${base}/index.ts`,
171
176
  `${base}/index.tsx`
172
- ].find((candidate)=>exists(candidate));
177
+ ].find((file)=>exists(file));
178
+ return void 0 === candidate ? void 0 : realRelative(candidate);
179
+ };
180
+ const graphBinding = (graph, module, expression, seen = new Set())=>{
181
+ if (seen.has(expression) || seen.size >= 128) return;
182
+ seen.add(expression);
183
+ const binding = __rspack_external__babel_types_6e8337dd.isIdentifier(expression) || __rspack_external__babel_types_6e8337dd.isMemberExpression(expression) ? graph.resolveReference(module, expression) : void 0;
184
+ return binding?.kind === 'declaration' && (__rspack_external__babel_types_6e8337dd.isIdentifier(binding.expression) || __rspack_external__babel_types_6e8337dd.isMemberExpression(binding.expression)) ? graphBinding(graph, binding.module, binding.expression, seen) : binding;
173
185
  };
174
186
  const clientExportFile = (appPath, exported)=>{
175
187
  if ('string' != typeof exported || !exported.startsWith('./') || exported.split(/[\\/]/u).includes('..') || !/\.[cm]?[jt]sx?$/u.test(exported)) return;
@@ -186,29 +198,65 @@ function check(options, topology) {
186
198
  return normalize(node_path.relative(root, candidate));
187
199
  };
188
200
  const composedModules = (file, appPath)=>{
189
- const order = [];
190
- const seen = new Set();
201
+ const owner = node_fs.realpathSync(absolute(appPath));
202
+ if (!inside(owner, node_fs.realpathSync(absolute(file)))) throw new Error(`consumer module closure escapes its owner: ${file}`);
203
+ const entry = realRelative(file);
204
+ const discovered = new Set([
205
+ entry
206
+ ]);
191
207
  const queue = [
192
- file
208
+ {
209
+ file: entry,
210
+ depth: 0
211
+ }
193
212
  ];
194
- const owner = node_fs.realpathSync(absolute(appPath));
195
- while(queue.length > 0 && order.length < 128){
196
- const current = queue.shift();
197
- if (seen.has(current)) continue;
198
- seen.add(current);
199
- order.push(current);
213
+ let graph;
214
+ for (const { file: current, depth } of queue){
200
215
  const ast = parseFile(current);
201
216
  if (!ast) continue;
202
- for (const statement of ast.program.body){
203
- 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;
204
- const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
205
- if (void 0 !== resolved && !seen.has(resolved)) {
206
- const real = node_fs.realpathSync(absolute(resolved));
207
- if (real.startsWith(`${owner}${node_path.sep}`)) queue.push(resolved);
217
+ const sources = [];
218
+ let callsRequire = false;
219
+ for (const statement of ast.program.body)if (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) && 'type' !== statement.importKind && 'typeof' !== statement.importKind || (__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || __rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) && 'type' !== statement.exportKind && statement.source) sources.push(statement.source.value);
220
+ __rspack_external__babel_types_6e8337dd.traverseFast(ast, (node)=>{
221
+ if (__rspack_external__babel_types_6e8337dd.isImportExpression(node)) {
222
+ if (!__rspack_external__babel_types_6e8337dd.isStringLiteral(node.source)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
223
+ sources.push(node.source.value);
224
+ }
225
+ if ((__rspack_external__babel_types_6e8337dd.isCallExpression(node) || __rspack_external__babel_types_6e8337dd.isOptionalCallExpression(node)) && __rspack_external__babel_types_6e8337dd.isIdentifier(unwrapExpression(node.callee, true), {
226
+ name: 'require'
227
+ })) callsRequire = true;
228
+ });
229
+ if (callsRequire) {
230
+ graph ??= createModuleGraph();
231
+ const module = graph.module(absolute(current));
232
+ __rspack_external__babel_types_6e8337dd.traverseFast(module.file, (node)=>{
233
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) && !__rspack_external__babel_types_6e8337dd.isOptionalCallExpression(node)) return;
234
+ const callee = unwrapExpression(node.callee, true);
235
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(callee, {
236
+ name: 'require'
237
+ }) || !graph.isUnboundReference(module, callee)) return;
238
+ const [argument] = node.arguments;
239
+ if (1 !== node.arguments.length || !__rspack_external__babel_types_6e8337dd.isStringLiteral(argument)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
240
+ sources.push(argument.value);
241
+ });
242
+ }
243
+ for (const source of sources){
244
+ if (!/^\.\.?\//u.test(source)) continue;
245
+ const resolved = resolveRelative(current, source);
246
+ if (void 0 === resolved) throw new Error(`consumer module closure has unresolved relative dependency: ${current} -> ${source}`);
247
+ if (!inside(owner, node_path.join(workspace, resolved))) throw new Error(`consumer module closure escapes its owner: ${current} -> ${source}`);
248
+ if (!discovered.has(resolved)) {
249
+ if (depth + 1 >= MAX_CLOSURE_DEPTH) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_DEPTH} import-depth analysis budget`);
250
+ if (discovered.size >= MAX_CLOSURE_MODULES) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_MODULES}-module analysis budget`);
251
+ discovered.add(resolved);
252
+ queue.push({
253
+ file: resolved,
254
+ depth: depth + 1
255
+ });
208
256
  }
209
257
  }
210
258
  }
211
- return order;
259
+ return queue.map((item)=>item.file);
212
260
  };
213
261
  const moduleShape = (file, imports, calls, patterns = [], defaultExport, composedAppPath)=>{
214
262
  if (!exists(file)) return;
@@ -247,6 +295,10 @@ function check(options, topology) {
247
295
  })), `${file}: must default-export ${defaultExport}`);
248
296
  };
249
297
  const effect = '@modern-js/bff-effect/effect-client';
298
+ const clientFactories = new Set([
299
+ 'makeEffectHttpApiClient',
300
+ 'makeEffectRpcClient'
301
+ ]);
250
302
  const appDirectories = [
251
303
  ...new Set([
252
304
  ...directories('apps'),
@@ -467,8 +519,8 @@ function check(options, topology) {
467
519
  ]);
468
520
  });
469
521
  const sourceRules = options.sourceRules ?? [];
470
- const graph = createModuleGraph();
471
522
  for (const file of sourceRules.length ? sourceFiles : [])if (/\.[cm]?[jt]sx?$/u.test(file)) guarded(file, ()=>{
523
+ const graph = createModuleGraph();
472
524
  let module;
473
525
  try {
474
526
  module = graph.module(absolute(file));
@@ -487,20 +539,178 @@ function check(options, topology) {
487
539
  for (const message of messages)diagnostics.push(`${file}: ${message}`);
488
540
  }
489
541
  });
490
- const shell = 'apps/shell-super-app';
491
- const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
492
- if (exists(shell) && verticals.length) {
493
- const file = `${shell}/src/api/vertical-clients.ts`;
494
- assert(exists(file), `${file}: must aggregate vertical API clients`);
495
- if (exists(file)) guarded(file, ()=>{
496
- const ast = parseFile(file);
497
- for (const app of verticals){
498
- const specifier = `${app.package}/api/${app.api?.protocol === 'rpc' ? 'rpc-client' : 'client'}`;
499
- assert(ast?.program.body.some((item)=>__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(item) && item.source?.value === specifier), `${file}: must re-export ${specifier}`);
542
+ const checkShellExports = (shellPath)=>{
543
+ const packageFile = `${shellPath}/package.json`;
544
+ const manifest = JSON.parse(read(packageFile));
545
+ noPath(`${shellPath}/src/api/vertical-clients.ts`, 'Shell client registries are forbidden; import each owner client directly');
546
+ assert(!Object.hasOwn(manifest.exports ?? {}, './api/clients'), `${packageFile}: Shell ./api/clients exports are forbidden`);
547
+ const owner = node_fs.realpathSync(absolute(shellPath));
548
+ const invalidExport = ()=>assert(false, `${packageFile}: invalid Shell public source export`);
549
+ const targets = (exported)=>'string' == typeof exported ? [
550
+ exported
551
+ ] : exported && 'object' == typeof exported ? Object.values(exported).flatMap(targets) : [];
552
+ const filesUnder = (directory, seen = new Set())=>{
553
+ const real = node_fs.realpathSync(absolute(directory));
554
+ if (real !== owner && !inside(owner, real)) {
555
+ invalidExport();
556
+ return [];
557
+ }
558
+ if (seen.has(real)) return [];
559
+ seen.add(real);
560
+ return node_fs.readdirSync(absolute(directory), {
561
+ withFileTypes: true
562
+ }).flatMap((entry)=>{
563
+ if (ignored.has(entry.name)) return [];
564
+ const file = `${directory}/${entry.name}`;
565
+ const stat = node_fs.statSync(absolute(file));
566
+ return stat.isDirectory() ? filesUnder(file, seen) : stat.isFile() ? [
567
+ file
568
+ ] : [];
569
+ });
570
+ };
571
+ const exportedFiles = (target)=>{
572
+ if (!target.includes('*')) return [
573
+ `${shellPath}/${target.slice(2)}`
574
+ ];
575
+ const prefix = target.slice(0, target.indexOf('*'));
576
+ const pattern = new RegExp(`^${target.split('*').map((part)=>part.replaceAll(/[.*+?^${}()|[\]\\]/gu, '\\$&')).join('.*')}$`, 'u');
577
+ return filesUnder(`${shellPath}/${prefix.slice(2, prefix.lastIndexOf('/'))}`).filter((file)=>pattern.test(`./${node_path.posix.relative(shellPath, file)}`));
578
+ };
579
+ const ownClientPrefix = `${manifest.name}/api/`;
580
+ const foreignSpecifier = (specifier)=>/\/api\/(?:rpc-)?client$/u.test(specifier) && !specifier.startsWith(ownClientPrefix);
581
+ for (const target of targets(manifest.exports))if (/\.[cm]?[jt]sx?$/u.test(target)) {
582
+ if (!target.startsWith('./') || target.split(/[\\/]/u).includes('..')) {
583
+ invalidExport();
584
+ continue;
500
585
  }
501
- });
586
+ for (const candidate of exportedFiles(target)){
587
+ const file = clientExportFile(shellPath, `./${node_path.posix.relative(shellPath, candidate)}`);
588
+ if (!file) {
589
+ invalidExport();
590
+ continue;
591
+ }
592
+ assert(!exposesForeignClient(file, owner, foreignSpecifier), `${file}: Shell public exports must not expose another owner's API client`);
593
+ }
594
+ }
595
+ };
596
+ const exposesForeignClient = (entry, owner, foreignSpecifier)=>{
597
+ const graph = createModuleGraph();
598
+ const seenValues = new Set();
599
+ const seenExports = new Map();
600
+ const workspacePath = (module)=>normalize(node_path.relative(workspace, module.path));
601
+ const resolveSource = (from, specifier)=>resolveRelative(from, specifier) ?? ownerClients.get(specifier);
602
+ const returnExpressions = (node)=>{
603
+ if (__rspack_external__babel_types_6e8337dd.isReturnStatement(node)) return node.argument ? [
604
+ node.argument
605
+ ] : [];
606
+ if (__rspack_external__babel_types_6e8337dd.isFunction(node)) return [];
607
+ return (__rspack_external__babel_types_6e8337dd.VISITOR_KEYS[node.type] ?? []).flatMap((key)=>{
608
+ const value = Reflect.get(node, key);
609
+ return (Array.isArray(value) ? value : [
610
+ value
611
+ ]).flatMap((child)=>__rspack_external__babel_types_6e8337dd.isNode(child) ? returnExpressions(child) : []);
612
+ });
613
+ };
614
+ const nestedValues = (node)=>{
615
+ if (__rspack_external__babel_types_6e8337dd.isObjectExpression(node)) return node.properties.flatMap((property)=>__rspack_external__babel_types_6e8337dd.isObjectProperty(property) ? [
616
+ property.value
617
+ ] : __rspack_external__babel_types_6e8337dd.isObjectMethod(property) ? returnExpressions(property.body) : __rspack_external__babel_types_6e8337dd.isSpreadElement(property) ? [
618
+ property.argument
619
+ ] : []);
620
+ if (__rspack_external__babel_types_6e8337dd.isArrayExpression(node)) return node.elements.filter((element)=>null !== element);
621
+ if (__rspack_external__babel_types_6e8337dd.isFunction(node)) return __rspack_external__babel_types_6e8337dd.isBlockStatement(node.body) ? returnExpressions(node.body) : [
622
+ node.body
623
+ ];
624
+ return [];
625
+ };
626
+ const foreignValue = (value, from)=>{
627
+ if ('external' === value.kind) {
628
+ const source = resolveSource(workspacePath(from), value.specifier);
629
+ if (!source) return foreignSpecifier(value.specifier);
630
+ return '*' === value.name ? inspect(source) : exportedValue(source, value.name);
631
+ }
632
+ if ('unresolved' === value.kind && __rspack_external__babel_types_6e8337dd.isImportDeclaration(value.node) && value.node.specifiers.some((specifier)=>__rspack_external__babel_types_6e8337dd.isImportNamespaceSpecifier(specifier))) {
633
+ const specifier = value.node.source.value;
634
+ const source = resolveSource(workspacePath(value.module), specifier);
635
+ return source ? inspect(source) : foreignSpecifier(specifier);
636
+ }
637
+ if ('node' !== value.kind || seenValues.has(value.node)) return false;
638
+ seenValues.add(value.node);
639
+ const { node, module } = value;
640
+ if ((__rspack_external__babel_types_6e8337dd.isCallExpression(node) || __rspack_external__babel_types_6e8337dd.isOptionalCallExpression(node)) && graph.evaluate(module, node.callee).some((callee)=>'external' === callee.kind && callee.specifier === effect && clientFactories.has(callee.name))) {
641
+ const [argument] = node.arguments;
642
+ const binding = argument ? graphBinding(graph, module, argument) : void 0;
643
+ if (module.boundary !== owner || binding?.kind !== 'declaration' || binding.module.boundary !== owner) return true;
644
+ }
645
+ return nestedValues(node).some((expression)=>graph.evaluate(module, expression).some((nested)=>foreignValue(nested, module)));
646
+ };
647
+ const exportName = (node)=>__rspack_external__babel_types_6e8337dd.isIdentifier(node) ? node.name : node.value;
648
+ const names = (source, visited = new Set())=>{
649
+ if (visited.has(source)) return [];
650
+ visited.add(source);
651
+ return graph.module(absolute(source)).file.program.body.flatMap((statement)=>{
652
+ if (__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement)) return [
653
+ 'default'
654
+ ];
655
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) && !__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return [];
656
+ if (__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) {
657
+ const target = resolveSource(source, statement.source.value);
658
+ return target ? names(target, visited).filter((name)=>'default' !== name) : [];
659
+ }
660
+ return statement.declaration ? Object.keys(__rspack_external__babel_types_6e8337dd.getOuterBindingIdentifiers(statement.declaration)) : statement.specifiers.flatMap((specifier)=>__rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier) && 'type' === specifier.exportKind ? [] : [
661
+ exportName(specifier.exported)
662
+ ]);
663
+ });
664
+ };
665
+ const exportedValue = (source, name)=>{
666
+ const visited = seenExports.get(source) ?? new Set();
667
+ if (visited.has(name)) return false;
668
+ visited.add(name);
669
+ seenExports.set(source, visited);
670
+ const module = graph.module(absolute(source));
671
+ const evaluates = (from, node)=>graph.evaluate(from, node).some((value)=>foreignValue(value, from));
672
+ const binding = graph.resolve(module, name, 'export');
673
+ if (binding?.kind === 'external') return foreignValue({
674
+ ...binding,
675
+ members: []
676
+ }, module);
677
+ if (binding?.kind === 'declaration') return evaluates(binding.module, binding.expression);
678
+ for (const statement of module.file.program.body){
679
+ if (__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement) && 'default' === name) return evaluates(module, statement.declaration);
680
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
681
+ if (__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(statement.declaration) && statement.declaration.id?.name === name) return evaluates(module, statement.declaration);
682
+ const specifier = statement.specifiers.find((item)=>exportName(item.exported) === name);
683
+ if (!specifier) continue;
684
+ if (__rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier) && 'type' === specifier.exportKind) return false;
685
+ if (!statement.source) return __rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier) ? evaluates(module, specifier.local) : false;
686
+ const target = resolveSource(source, statement.source.value);
687
+ if (!target) return foreignSpecifier(statement.source.value);
688
+ if (__rspack_external__babel_types_6e8337dd.isExportNamespaceSpecifier(specifier)) return inspect(target);
689
+ if (__rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier)) return exportedValue(target, exportName(specifier.local));
690
+ }
691
+ return module.file.program.body.some((statement)=>{
692
+ if (!__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return false;
693
+ const target = resolveSource(source, statement.source.value);
694
+ return !!target && names(target).includes(name) && exportedValue(target, name);
695
+ });
696
+ };
697
+ const inspect = (source)=>[
698
+ ...new Set(names(source))
699
+ ].some((name)=>exportedValue(source, name));
700
+ return inspect(entry);
701
+ };
702
+ const ownerClients = new Map();
703
+ for (const app of apps)if (exists(`${app.path}/package.json`)) {
704
+ const manifest = JSON.parse(read(`${app.path}/package.json`));
705
+ for (const key of [
706
+ './api/client',
707
+ './api/rpc-client'
708
+ ]){
709
+ const file = clientExportFile(app.path, manifest.exports?.[key]);
710
+ if (file) ownerClients.set(`${manifest.name}${key.slice(1)}`, file);
711
+ }
502
712
  }
503
- 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`));
713
+ for (const shell of apps.filter((app)=>'shell' === app.kind))guarded(shell.path, ()=>checkShellExports(normalize(shell.path)));
504
714
  if (exists('topology/reference-topology.json')) guarded('topology/reference-topology.json', ()=>{
505
715
  for (const vertical of JSON.parse(read('topology/reference-topology.json')).verticals ?? []){
506
716
  if (vertical.api?.runtime === 'effect') {
@@ -186,6 +186,7 @@ function parseModule(filePath, workspace) {
186
186
  const file = parseSource(node_fs.readFileSync(filePath, 'utf8'), filePath);
187
187
  let scope;
188
188
  const references = new Map();
189
+ const declarations = new Map();
189
190
  const returns = new Map();
190
191
  const bareReturns = new Set();
191
192
  const functionScopes = new Map();
@@ -296,6 +297,11 @@ function parseModule(filePath, workspace) {
296
297
  ReferencedIdentifier (p) {
297
298
  references.set(p.node, p.scope.getBinding(p.node.name));
298
299
  },
300
+ BindingIdentifier (p) {
301
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(p.node)) return;
302
+ const binding = p.scope.getBinding(p.node.name);
303
+ if (binding?.identifier === p.node) declarations.set(p.node, binding);
304
+ },
299
305
  Function (p) {
300
306
  functionScopes.set(p.node, p.scope);
301
307
  },
@@ -361,6 +367,7 @@ function parseModule(filePath, workspace) {
361
367
  scope,
362
368
  workspace,
363
369
  references,
370
+ declarations,
364
371
  returns,
365
372
  bareReturns,
366
373
  functionScopes,
@@ -1096,7 +1103,7 @@ function createModuleGraph() {
1096
1103
  }
1097
1104
  };
1098
1105
  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);
1099
- const resolveBinding = (module, name, scope, chain, seen)=>{
1106
+ const resolveBinding = (module, name, scope, chain, seen, lexical)=>{
1100
1107
  const key = `${scope}:${module.path}#${name}`;
1101
1108
  if (seen.has(key) || seen.size >= MAX_LOOKUPS) return;
1102
1109
  seen.add(key);
@@ -1119,7 +1126,7 @@ function createModuleGraph() {
1119
1126
  return void 0 === next ? void 0 : resolveBinding(next, imported, 'export', hops, seen);
1120
1127
  };
1121
1128
  if ('local' === scope) {
1122
- const binding = factsOf(module).scope.getBinding(name);
1129
+ const binding = lexical ?? factsOf(module).scope.getBinding(name);
1123
1130
  if (!binding || mutated(module, binding)) return;
1124
1131
  if ('module' === binding.kind) {
1125
1132
  const imported = importOf(binding);
@@ -1148,6 +1155,39 @@ function createModuleGraph() {
1148
1155
  if (void 0 !== resolved) return resolved;
1149
1156
  }
1150
1157
  };
1158
+ const resolveReference = (module, reference)=>{
1159
+ const node = unwrapExpression(reference, true);
1160
+ const moduleFacts = factsOf(module);
1161
+ const bindingAt = (identifier)=>moduleFacts.references.get(identifier) ?? moduleFacts.declarations.get(identifier);
1162
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) {
1163
+ const binding = bindingAt(node);
1164
+ return binding ? resolveBinding(module, node.name, 'local', [], new Set(), binding) : void 0;
1165
+ }
1166
+ if (!__rspack_external__babel_types_6e8337dd.isMemberExpression(node) || node.computed || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.object) || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.property)) return;
1167
+ const binding = bindingAt(node.object);
1168
+ if (!binding || 'module' !== binding.kind || mutated(module, binding)) return;
1169
+ const imported = importOf(binding);
1170
+ if (imported?.name !== '*') return;
1171
+ const target = follow(module, imported.specifier);
1172
+ const chain = [
1173
+ {
1174
+ path: module.path,
1175
+ name: node.object.name
1176
+ }
1177
+ ];
1178
+ if ('external' === target) return {
1179
+ kind: 'external',
1180
+ specifier: imported.specifier,
1181
+ name: node.property.name,
1182
+ chain
1183
+ };
1184
+ return void 0 === target ? void 0 : resolveBinding(target, node.property.name, 'export', chain, new Set());
1185
+ };
1186
+ const isUnboundReference = (module, reference)=>{
1187
+ const node = unwrapExpression(reference, true);
1188
+ const { references } = factsOf(module);
1189
+ return __rspack_external__babel_types_6e8337dd.isIdentifier(node) && references.has(node) && void 0 === references.get(node);
1190
+ };
1151
1191
  const evaluation = (walking)=>{
1152
1192
  const active = new Set();
1153
1193
  let frames = 0;
@@ -1755,6 +1795,8 @@ function createModuleGraph() {
1755
1795
  return {
1756
1796
  module: (filePath)=>load(filePath),
1757
1797
  resolve: (module, name, scope)=>resolveBinding(module, name, scope, [], new Set()),
1798
+ resolveReference,
1799
+ isUnboundReference,
1758
1800
  evaluate: (module, expression)=>evaluation(false).values(module, expression, []),
1759
1801
  reachable: (module, expression)=>evaluation(true).reachable(module, expression)
1760
1802
  };
@@ -7,6 +7,19 @@ const nodeRuntime = '@modern-js/bff-effect/effect';
7
7
  const assembly = '@modern-js/bff-effect/assembly';
8
8
  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.';
9
9
  const MAX_API_SOURCE_MODULES = 256;
10
+ const httpRouteMethods = new Set([
11
+ '*',
12
+ 'GET',
13
+ 'POST',
14
+ 'PUT',
15
+ 'PATCH',
16
+ 'DELETE',
17
+ 'HEAD',
18
+ 'OPTIONS',
19
+ 'TRACE',
20
+ 'CONNECT',
21
+ 'QUERY'
22
+ ]);
10
23
  function createEffectApiImportResolver(filename) {
11
24
  const directory = node_fs.realpathSync(node_path.dirname(filename));
12
25
  const owner = node_fs.realpathSync(node_path.resolve(directory, '..'));
@@ -54,9 +67,12 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
54
67
  })) {
55
68
  let target = nodePath.isAssignmentExpression() ? nodePath.node.left : nodePath.node.argument;
56
69
  while(__rspack_external__babel_types_6e8337dd.isMemberExpression(target))target = target.object;
57
- if (__rspack_external__babel_types_6e8337dd.isIdentifier(target)) {
58
- const binding = nodePath.scope.getBinding(target.name);
59
- if (binding) mutated.add(binding);
70
+ let binding = __rspack_external__babel_types_6e8337dd.isIdentifier(target) ? nodePath.scope.getBinding(target.name) : void 0;
71
+ while(binding && !mutated.has(binding)){
72
+ mutated.add(binding);
73
+ const declarator = binding.path.node;
74
+ const alias = 'const' === binding.kind && __rspack_external__babel_types_6e8337dd.isVariableDeclarator(declarator) && declarator.init ? unwrapExpression(declarator.init, true) : void 0;
75
+ binding = __rspack_external__babel_types_6e8337dd.isIdentifier(alias) ? binding.path.scope.getBinding(alias.name) : void 0;
60
76
  }
61
77
  }
62
78
  if (nodePath.isTSModuleDeclaration() && __rspack_external__babel_types_6e8337dd.isIdentifier(nodePath.node.id)) {
@@ -231,11 +247,48 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
231
247
  if (method(node.callee, 'Layer', 'mergeAll', layerSources)) return node.arguments.length > 0 && node.arguments.every((argument)=>handlers(argument, api));
232
248
  return method(node.callee, 'HttpApiBuilder', 'group') && 3 === node.arguments.length && apiIdentity(node.arguments[0]) === api && handled(node.arguments[2]);
233
249
  });
234
- const transport = (node)=>guarded(node, (node)=>{
250
+ const effectSources = [
251
+ edge,
252
+ 'effect',
253
+ 'effect/Effect'
254
+ ];
255
+ const routeHandler = (node)=>{
256
+ node = unwrap(node);
257
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) || !method(node.callee, 'HttpRouter', 'add') || 3 !== node.arguments.length) return;
258
+ const [verb, route, handler] = node.arguments;
259
+ return __rspack_external__babel_types_6e8337dd.isStringLiteral(verb) && httpRouteMethods.has(verb.value) && __rspack_external__babel_types_6e8337dd.isStringLiteral(route) && (route.value.startsWith('/') || '*' === route.value) && (__rspack_external__babel_types_6e8337dd.isArrowFunctionExpression(handler) || __rspack_external__babel_types_6e8337dd.isFunctionExpression(handler)) ? handler : void 0;
260
+ };
261
+ const effectReference = (node)=>guarded(node, (node)=>{
235
262
  const init = initialized(node);
236
- if (init) return transport(init);
263
+ if (init) return __rspack_external__babel_types_6e8337dd.isIdentifier(unwrap(init)) && effectReference(init);
264
+ const value = imported(node);
265
+ return !!value && !value.namespace;
266
+ });
267
+ const capturedRouteFactory = (node)=>guarded(node, (node)=>{
268
+ const init = initialized(node) ?? externalValue(node);
269
+ if (init) return capturedRouteFactory(init);
270
+ if (!__rspack_external__babel_types_6e8337dd.isArrowFunctionExpression(node) || node.async || 1 !== node.params.length || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.params[0]) || __rspack_external__babel_types_6e8337dd.isBlockStatement(node.body)) return false;
271
+ const handler = routeHandler(node.body);
272
+ const parameter = binding(node.params[0]);
273
+ return !!handler && !!parameter && parameter.referencePaths.some((reference)=>!!reference.findParent((path)=>path.node === handler));
274
+ });
275
+ const capturedTransport = (node)=>guarded(node, (node)=>{
276
+ const init = initialized(node);
277
+ if (init) return capturedTransport(init);
278
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return false;
279
+ if (method(node.callee, 'Effect', 'map', effectSources)) return 2 === node.arguments.length && effectReference(node.arguments[0]) && capturedRouteFactory(node.arguments[1]);
280
+ if (!__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) || node.callee.computed || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property, {
281
+ name: 'pipe'
282
+ }) || 1 !== node.arguments.length || !effectReference(node.callee.object)) return false;
283
+ const map = unwrap(node.arguments[0]);
284
+ return __rspack_external__babel_types_6e8337dd.isCallExpression(map) && method(map.callee, 'Effect', 'map', effectSources) && 1 === map.arguments.length && capturedRouteFactory(map.arguments[0]);
285
+ });
286
+ const transport = (node, provided = false)=>guarded(node, (node)=>{
287
+ const init = initialized(node) ?? externalValue(node);
288
+ if (init) return transport(init, provided);
237
289
  if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return method(node, 'Layer', 'empty', layerSources);
238
- return pipe(node, transport) || method(node.callee, 'HttpRouter', 'cors') || method(node.callee, 'Layer', 'mergeAll', layerSources) && node.arguments.length > 0 && node.arguments.every(transport);
290
+ const providedHere = provided || node.arguments.some((argument)=>__rspack_external__babel_types_6e8337dd.isCallExpression(argument) && method(argument.callee, 'Layer', 'provide', layerSources) && 1 === argument.arguments.length);
291
+ 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));
239
292
  });
240
293
  const directLayer = (node, api)=>guarded(node, (node)=>{
241
294
  const init = initialized(node);