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

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.
@@ -2,10 +2,10 @@ import "node:module";
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
4
  import { parse } from "@babel/parser";
5
- import { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation } from "./microvertical-api-baseline.js";
5
+ import { camelCaseStem, configuredMicroVerticalApiStem, microVerticalApiBaselineViolation, pascalCaseStem } from "./microvertical-api-baseline.js";
6
6
  import { baselinePackage, resolveBaselinePackageDirectory } from "./microvertical-api-owner.js";
7
7
  import { createModuleGraph } from "./module-graph.js";
8
- import { SourceSyntaxError, consumerParserPlugins } from "./source-analysis.js";
8
+ import { SourceSyntaxError, consumerParserPlugins, unwrapExpression } from "./source-analysis.js";
9
9
  import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "./strict-effect-runtime.js";
10
10
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
11
11
  const ignored = new Set([
@@ -18,6 +18,14 @@ const ignored = new Set([
18
18
  'node_modules',
19
19
  'repos'
20
20
  ]);
21
+ const MAX_CLOSURE_DEPTH = 128;
22
+ const MAX_CLOSURE_MODULES = 2048;
23
+ const MAX_API_HOPS = 128;
24
+ const MAX_API_VISITS = 2048;
25
+ const identitySources = [
26
+ 'effect',
27
+ 'effect/Function'
28
+ ];
21
29
  const normalize = (file)=>file.split(node_path.sep).join('/').replace(/^\.\//u, '');
22
30
  const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
23
31
  const apiApp = (app)=>'vertical' === app.kind && 'ui-only' !== app.surfaceProfile && 'horizontal-remote' !== app.deliveryUnitKind;
@@ -83,7 +91,7 @@ function configuredApps(root, supplied) {
83
91
  };
84
92
  for (const field of Object.keys(allowedFields)){
85
93
  const value = app[field];
86
- if (void 0 !== value && !allowedFields[field].includes(value)) throw new Error(`${key}: invalid ${field}`);
94
+ if ((void 0 !== value || 'kind' === field) && ('string' != typeof value || !allowedFields[field].includes(value))) throw new Error(`${key}: invalid ${field}`);
87
95
  }
88
96
  if (void 0 !== app.api) {
89
97
  if (!app.api || 'object' != typeof app.api || Array.isArray(app.api)) throw new Error(`${key}: api must be an object`);
@@ -125,6 +133,9 @@ function check(options, topology) {
125
133
  };
126
134
  try {
127
135
  const apps = configuredApps(root, options.configuredApps);
136
+ const workspace = node_fs.realpathSync(root);
137
+ const realRelative = (file)=>normalize(node_path.relative(workspace, node_fs.realpathSync(absolute(file))));
138
+ const inside = (directory, real)=>real.startsWith(`${directory}${node_path.sep}`);
128
139
  const byPath = new Map(apps.map((app)=>[
129
140
  normalize(app.path),
130
141
  app
@@ -165,12 +176,19 @@ function check(options, topology) {
165
176
  const resolveRelative = (fromFile, specifier)=>{
166
177
  if (!/^\.\.?\//u.test(specifier)) return;
167
178
  const base = node_path.posix.join(node_path.posix.dirname(fromFile), specifier).replace(/\.(?:[cm]?[jt]sx?)$/u, '');
168
- return [
179
+ const candidate = [
169
180
  `${base}.ts`,
170
181
  `${base}.tsx`,
171
182
  `${base}/index.ts`,
172
183
  `${base}/index.tsx`
173
- ].find((candidate)=>exists(candidate));
184
+ ].find((file)=>exists(file));
185
+ return void 0 === candidate ? void 0 : realRelative(candidate);
186
+ };
187
+ const graphBinding = (graph, module, expression, seen = new Set())=>{
188
+ if (seen.has(expression) || seen.size >= 128) return;
189
+ seen.add(expression);
190
+ const binding = __rspack_external__babel_types_6e8337dd.isIdentifier(expression) || __rspack_external__babel_types_6e8337dd.isMemberExpression(expression) ? graph.resolveReference(module, expression) : void 0;
191
+ 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;
174
192
  };
175
193
  const clientExportFile = (appPath, exported)=>{
176
194
  if ('string' != typeof exported || !exported.startsWith('./') || exported.split(/[\\/]/u).includes('..') || !/\.[cm]?[jt]sx?$/u.test(exported)) return;
@@ -187,29 +205,209 @@ function check(options, topology) {
187
205
  return normalize(node_path.relative(root, candidate));
188
206
  };
189
207
  const composedModules = (file, appPath)=>{
190
- const order = [];
191
- const seen = new Set();
208
+ const owner = node_fs.realpathSync(absolute(appPath));
209
+ if (!inside(owner, node_fs.realpathSync(absolute(file)))) throw new Error(`consumer module closure escapes its owner: ${file}`);
210
+ const entry = realRelative(file);
211
+ const discovered = new Set([
212
+ entry
213
+ ]);
192
214
  const queue = [
193
- file
215
+ {
216
+ file: entry,
217
+ depth: 0
218
+ }
194
219
  ];
195
- const owner = node_fs.realpathSync(absolute(appPath));
196
- while(queue.length > 0 && order.length < 128){
197
- const current = queue.shift();
198
- if (seen.has(current)) continue;
199
- seen.add(current);
200
- order.push(current);
220
+ let graph;
221
+ for (const { file: current, depth } of queue){
201
222
  const ast = parseFile(current);
202
223
  if (!ast) continue;
203
- for (const statement of ast.program.body){
204
- 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;
205
- const resolved = void 0 === source ? void 0 : resolveRelative(current, source);
206
- if (void 0 !== resolved && !seen.has(resolved)) {
207
- const real = node_fs.realpathSync(absolute(resolved));
208
- if (real.startsWith(`${owner}${node_path.sep}`)) queue.push(resolved);
224
+ const sources = [];
225
+ let callsRequire = false;
226
+ 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);
227
+ __rspack_external__babel_types_6e8337dd.traverseFast(ast, (node)=>{
228
+ if (__rspack_external__babel_types_6e8337dd.isImportExpression(node)) {
229
+ if (!__rspack_external__babel_types_6e8337dd.isStringLiteral(node.source)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
230
+ sources.push(node.source.value);
231
+ }
232
+ 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), {
233
+ name: 'require'
234
+ })) callsRequire = true;
235
+ });
236
+ if (callsRequire) {
237
+ graph ??= createModuleGraph();
238
+ const module = graph.module(absolute(current));
239
+ __rspack_external__babel_types_6e8337dd.traverseFast(module.file, (node)=>{
240
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) && !__rspack_external__babel_types_6e8337dd.isOptionalCallExpression(node)) return;
241
+ const callee = unwrapExpression(node.callee, true);
242
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(callee, {
243
+ name: 'require'
244
+ }) || !graph.isUnboundReference(module, callee)) return;
245
+ const [argument] = node.arguments;
246
+ if (1 !== node.arguments.length || !__rspack_external__babel_types_6e8337dd.isStringLiteral(argument)) throw new Error(`consumer module closure has nonliteral runtime dependency: ${current}`);
247
+ sources.push(argument.value);
248
+ });
249
+ }
250
+ for (const source of sources){
251
+ if (!/^\.\.?\//u.test(source)) continue;
252
+ const resolved = resolveRelative(current, source);
253
+ if (void 0 === resolved) throw new Error(`consumer module closure has unresolved relative dependency: ${current} -> ${source}`);
254
+ if (!inside(owner, node_path.join(workspace, resolved))) throw new Error(`consumer module closure escapes its owner: ${current} -> ${source}`);
255
+ if (!discovered.has(resolved)) {
256
+ if (depth + 1 >= MAX_CLOSURE_DEPTH) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_DEPTH} import-depth analysis budget`);
257
+ if (discovered.size >= MAX_CLOSURE_MODULES) throw new Error(`consumer module closure exceeds ${MAX_CLOSURE_MODULES}-module analysis budget`);
258
+ discovered.add(resolved);
259
+ queue.push({
260
+ file: resolved,
261
+ depth: depth + 1
262
+ });
209
263
  }
210
264
  }
211
265
  }
212
- return order;
266
+ return queue.map((item)=>item.file);
267
+ };
268
+ const foreignClientConstructions = (client, appPath, contract, stem, rpc)=>{
269
+ const graph = createModuleGraph();
270
+ const contractModule = graph.module(absolute(contract));
271
+ const factory = rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient';
272
+ const ownExport = graph.resolve(contractModule, rpc ? `${pascalCaseStem(stem)}RpcGroup` : `${camelCaseStem(stem)}Api`, 'export');
273
+ const isIdentity = (value)=>'external' === value.kind && identitySources.includes(value.specifier) && 'identity' === value.name && 0 === value.members.length;
274
+ const identityPipe = (module, node)=>{
275
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) || !__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) || node.callee.computed || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property, {
276
+ name: 'pipe'
277
+ }) || 1 !== node.arguments.length) return false;
278
+ const values = graph.evaluate(module, node.arguments[0]);
279
+ return values.length > 0 && values.every(isIdentity);
280
+ };
281
+ const withoutIdentity = (module, value)=>{
282
+ let node = unwrapExpression(value, true);
283
+ for(let pipes = 0; identityPipe(module, node); pipes += 1){
284
+ if (pipes >= MAX_API_HOPS) return value;
285
+ node = unwrapExpression(node.callee.object, true);
286
+ }
287
+ return node;
288
+ };
289
+ const normalizedApi = (initial)=>{
290
+ let binding = initial;
291
+ const seen = new Set();
292
+ for(let hops = 0; hops < MAX_API_HOPS; hops += 1){
293
+ const expression = withoutIdentity(binding.module, binding.expression);
294
+ if (seen.has(expression)) break;
295
+ seen.add(expression);
296
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(expression) && !__rspack_external__babel_types_6e8337dd.isMemberExpression(expression)) return {
297
+ ...binding,
298
+ expression: expression
299
+ };
300
+ const next = graphBinding(graph, binding.module, expression);
301
+ if (next?.kind !== 'declaration') break;
302
+ binding = next;
303
+ }
304
+ };
305
+ const apiBinding = (module, value)=>{
306
+ const binding = graphBinding(graph, module, withoutIdentity(module, value));
307
+ return binding?.kind === 'declaration' ? normalizedApi(binding) : void 0;
308
+ };
309
+ const ownedApis = new Map();
310
+ const own = (binding)=>{
311
+ const identities = ownedApis.get(binding.module.path) ?? new Set();
312
+ identities.add(binding.expression);
313
+ ownedApis.set(binding.module.path, identities);
314
+ };
315
+ const proven = new Set();
316
+ const active = new Set();
317
+ let visits = 0;
318
+ const builtApi = (module, expression)=>{
319
+ const initial = withoutIdentity(module, expression);
320
+ if (active.has(initial) || active.size >= MAX_API_HOPS) return false;
321
+ active.add(initial);
322
+ try {
323
+ const children = [];
324
+ for(let node = initial;;){
325
+ if (++visits > MAX_API_VISITS) return false;
326
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(node) || __rspack_external__babel_types_6e8337dd.isMemberExpression(node)) {
327
+ const receiver = apiBinding(module, node);
328
+ if (!receiver || !builtApi(receiver.module, receiver.expression)) return false;
329
+ break;
330
+ }
331
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return false;
332
+ const callee = graph.evaluate(module, node.callee);
333
+ if (callee.length > 0 && callee.every((value)=>'external' === value.kind && httpApiSources.includes(value.specifier) && 'HttpApi' === value.name && 1 === value.members.length && 'make' === value.members[0])) {
334
+ if (1 !== node.arguments.length || !__rspack_external__babel_types_6e8337dd.isStringLiteral(node.arguments[0])) return false;
335
+ break;
336
+ }
337
+ if (!__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) || node.callee.computed || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property)) return false;
338
+ const method = node.callee.property.name;
339
+ const count = node.arguments.length;
340
+ const [first] = node.arguments;
341
+ if ('addHttpApi' === method && 1 === count) children.push(first);
342
+ else if (!('add' === method && count > 0 || 'annotate' === method && 2 === count || [
343
+ 'annotateMerge',
344
+ 'middleware'
345
+ ].includes(method) && 1 === count || 'prefix' === method && 1 === count && __rspack_external__babel_types_6e8337dd.isStringLiteral(first) && first.value.startsWith('/'))) return false;
346
+ node = withoutIdentity(module, node.callee.object);
347
+ }
348
+ return children.every((child)=>{
349
+ const binding = apiBinding(module, child);
350
+ return void 0 !== binding && collectApi(binding);
351
+ });
352
+ } finally{
353
+ active.delete(initial);
354
+ }
355
+ };
356
+ const collectApi = (binding)=>{
357
+ if (proven.has(binding.expression)) return true;
358
+ if (!builtApi(binding.module, binding.expression)) return false;
359
+ proven.add(binding.expression);
360
+ if (binding.module.boundary === contractModule.boundary) own(binding);
361
+ return true;
362
+ };
363
+ if (!rpc && ownExport?.kind === 'declaration') {
364
+ const root = normalizedApi(ownExport);
365
+ if (root && collectApi(root)) own(root);
366
+ else ownedApis.clear();
367
+ }
368
+ const immutablePipeAliases = (module, target)=>{
369
+ if (0 === module.mutations.length) return true;
370
+ let valid = true;
371
+ __rspack_external__babel_types_6e8337dd.traverseFast(module.file, (node)=>{
372
+ if (!__rspack_external__babel_types_6e8337dd.isVariableDeclarator(node) || !__rspack_external__babel_types_6e8337dd.isIdentifier(node.id) || !node.init) return;
373
+ let initializer = unwrapExpression(node.init, true);
374
+ let identityOnly = true;
375
+ let piped = false;
376
+ while(__rspack_external__babel_types_6e8337dd.isCallExpression(initializer) && __rspack_external__babel_types_6e8337dd.isMemberExpression(initializer.callee) && !initializer.callee.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(initializer.callee.property, {
377
+ name: 'pipe'
378
+ })){
379
+ piped = true;
380
+ identityOnly &&= identityPipe(module, initializer);
381
+ initializer = unwrapExpression(initializer.callee.object, true);
382
+ }
383
+ if (!piped) return;
384
+ const receiver = apiBinding(module, initializer);
385
+ if (receiver?.module.path !== target.module.path || receiver.expression !== target.expression) return;
386
+ if (!identityOnly || graph.resolveReference(module, node.id)?.kind !== 'declaration') valid = false;
387
+ });
388
+ return valid;
389
+ };
390
+ const ownedBy = (module, argument)=>{
391
+ if (!argument) return false;
392
+ if (rpc) {
393
+ const binding = graphBinding(graph, module, argument);
394
+ return binding?.kind === 'declaration' && ownExport?.kind === 'declaration' && binding.module.path === ownExport.module.path && binding.expression === ownExport.expression;
395
+ }
396
+ const binding = apiBinding(module, argument);
397
+ return void 0 !== binding && !!ownedApis.get(binding.module.path)?.has(binding.expression) && immutablePipeAliases(module, binding);
398
+ };
399
+ const violations = new Set();
400
+ for (const source of composedModules(client, appPath)){
401
+ const module = graph.module(absolute(source));
402
+ __rspack_external__babel_types_6e8337dd.traverseFast(module.file, (node)=>{
403
+ if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node) && !__rspack_external__babel_types_6e8337dd.isOptionalCallExpression(node)) return;
404
+ const native = graph.evaluate(module, node.callee).some((value)=>'external' === value.kind && value.specifier === effect && value.name === factory);
405
+ if (native && !ownedBy(module, node.arguments[0])) violations.add(`${source}: must construct the client from its own API contract`);
406
+ });
407
+ }
408
+ return [
409
+ ...violations
410
+ ];
213
411
  };
214
412
  const moduleShape = (file, imports, calls, patterns = [], defaultExport, composedAppPath)=>{
215
413
  if (!exists(file)) return;
@@ -248,6 +446,14 @@ function check(options, topology) {
248
446
  })), `${file}: must default-export ${defaultExport}`);
249
447
  };
250
448
  const effect = '@modern-js/bff-effect/effect-client';
449
+ const httpApiSources = [
450
+ effect,
451
+ 'effect/http-api'
452
+ ];
453
+ const clientFactories = new Set([
454
+ 'makeEffectHttpApiClient',
455
+ 'makeEffectRpcClient'
456
+ ]);
251
457
  const appDirectories = [
252
458
  ...new Set([
253
459
  ...directories('apps'),
@@ -425,6 +631,7 @@ function check(options, topology) {
425
631
  ], [
426
632
  rpc ? 'makeEffectRpcClient' : 'makeEffectHttpApiClient'
427
633
  ], [], void 0, appPath);
634
+ if (exists(contract)) diagnostics.push(...foreignClientConstructions(client, appPath, contract, stem, rpc));
428
635
  }
429
636
  moduleShape(`${appPath}/api/effect-api.ts`, [], [], [
430
637
  [
@@ -468,8 +675,8 @@ function check(options, topology) {
468
675
  ]);
469
676
  });
470
677
  const sourceRules = options.sourceRules ?? [];
471
- const graph = createModuleGraph();
472
678
  for (const file of sourceRules.length ? sourceFiles : [])if (/\.[cm]?[jt]sx?$/u.test(file)) guarded(file, ()=>{
679
+ const graph = createModuleGraph();
473
680
  let module;
474
681
  try {
475
682
  module = graph.module(absolute(file));
@@ -488,20 +695,178 @@ function check(options, topology) {
488
695
  for (const message of messages)diagnostics.push(`${file}: ${message}`);
489
696
  }
490
697
  });
491
- const shell = 'apps/shell-super-app';
492
- const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
493
- if (exists(shell) && verticals.length) {
494
- const file = `${shell}/src/api/vertical-clients.ts`;
495
- assert(exists(file), `${file}: must aggregate vertical API clients`);
496
- if (exists(file)) guarded(file, ()=>{
497
- const ast = parseFile(file);
498
- for (const app of verticals){
499
- const specifier = `${app.package}/api/${app.api?.protocol === 'rpc' ? 'rpc-client' : 'client'}`;
500
- assert(ast?.program.body.some((item)=>__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(item) && item.source?.value === specifier), `${file}: must re-export ${specifier}`);
698
+ const checkShellExports = (shellPath)=>{
699
+ const packageFile = `${shellPath}/package.json`;
700
+ const manifest = JSON.parse(read(packageFile));
701
+ noPath(`${shellPath}/src/api/vertical-clients.ts`, 'Shell client registries are forbidden; import each owner client directly');
702
+ assert(!Object.hasOwn(manifest.exports ?? {}, './api/clients'), `${packageFile}: Shell ./api/clients exports are forbidden`);
703
+ const owner = node_fs.realpathSync(absolute(shellPath));
704
+ const invalidExport = ()=>assert(false, `${packageFile}: invalid Shell public source export`);
705
+ const targets = (exported)=>'string' == typeof exported ? [
706
+ exported
707
+ ] : exported && 'object' == typeof exported ? Object.values(exported).flatMap(targets) : [];
708
+ const filesUnder = (directory, seen = new Set())=>{
709
+ const real = node_fs.realpathSync(absolute(directory));
710
+ if (real !== owner && !inside(owner, real)) {
711
+ invalidExport();
712
+ return [];
713
+ }
714
+ if (seen.has(real)) return [];
715
+ seen.add(real);
716
+ return node_fs.readdirSync(absolute(directory), {
717
+ withFileTypes: true
718
+ }).flatMap((entry)=>{
719
+ if (ignored.has(entry.name)) return [];
720
+ const file = `${directory}/${entry.name}`;
721
+ const stat = node_fs.statSync(absolute(file));
722
+ return stat.isDirectory() ? filesUnder(file, seen) : stat.isFile() ? [
723
+ file
724
+ ] : [];
725
+ });
726
+ };
727
+ const exportedFiles = (target)=>{
728
+ if (!target.includes('*')) return [
729
+ `${shellPath}/${target.slice(2)}`
730
+ ];
731
+ const prefix = target.slice(0, target.indexOf('*'));
732
+ const pattern = new RegExp(`^${target.split('*').map((part)=>part.replaceAll(/[.*+?^${}()|[\]\\]/gu, '\\$&')).join('.*')}$`, 'u');
733
+ return filesUnder(`${shellPath}/${prefix.slice(2, prefix.lastIndexOf('/'))}`).filter((file)=>pattern.test(`./${node_path.posix.relative(shellPath, file)}`));
734
+ };
735
+ const ownClientPrefix = `${manifest.name}/api/`;
736
+ const foreignSpecifier = (specifier)=>/\/api\/(?:rpc-)?client$/u.test(specifier) && !specifier.startsWith(ownClientPrefix);
737
+ for (const target of targets(manifest.exports))if (/\.[cm]?[jt]sx?$/u.test(target)) {
738
+ if (!target.startsWith('./') || target.split(/[\\/]/u).includes('..')) {
739
+ invalidExport();
740
+ continue;
501
741
  }
502
- });
742
+ for (const candidate of exportedFiles(target)){
743
+ const file = clientExportFile(shellPath, `./${node_path.posix.relative(shellPath, candidate)}`);
744
+ if (!file) {
745
+ invalidExport();
746
+ continue;
747
+ }
748
+ assert(!exposesForeignClient(file, owner, foreignSpecifier), `${file}: Shell public exports must not expose another owner's API client`);
749
+ }
750
+ }
751
+ };
752
+ const exposesForeignClient = (entry, owner, foreignSpecifier)=>{
753
+ const graph = createModuleGraph();
754
+ const seenValues = new Set();
755
+ const seenExports = new Map();
756
+ const workspacePath = (module)=>normalize(node_path.relative(workspace, module.path));
757
+ const resolveSource = (from, specifier)=>resolveRelative(from, specifier) ?? ownerClients.get(specifier);
758
+ const returnExpressions = (node)=>{
759
+ if (__rspack_external__babel_types_6e8337dd.isReturnStatement(node)) return node.argument ? [
760
+ node.argument
761
+ ] : [];
762
+ if (__rspack_external__babel_types_6e8337dd.isFunction(node)) return [];
763
+ return (__rspack_external__babel_types_6e8337dd.VISITOR_KEYS[node.type] ?? []).flatMap((key)=>{
764
+ const value = Reflect.get(node, key);
765
+ return (Array.isArray(value) ? value : [
766
+ value
767
+ ]).flatMap((child)=>__rspack_external__babel_types_6e8337dd.isNode(child) ? returnExpressions(child) : []);
768
+ });
769
+ };
770
+ const nestedValues = (node)=>{
771
+ if (__rspack_external__babel_types_6e8337dd.isObjectExpression(node)) return node.properties.flatMap((property)=>__rspack_external__babel_types_6e8337dd.isObjectProperty(property) ? [
772
+ property.value
773
+ ] : __rspack_external__babel_types_6e8337dd.isObjectMethod(property) ? returnExpressions(property.body) : __rspack_external__babel_types_6e8337dd.isSpreadElement(property) ? [
774
+ property.argument
775
+ ] : []);
776
+ if (__rspack_external__babel_types_6e8337dd.isArrayExpression(node)) return node.elements.filter((element)=>null !== element);
777
+ if (__rspack_external__babel_types_6e8337dd.isFunction(node)) return __rspack_external__babel_types_6e8337dd.isBlockStatement(node.body) ? returnExpressions(node.body) : [
778
+ node.body
779
+ ];
780
+ return [];
781
+ };
782
+ const foreignValue = (value, from)=>{
783
+ if ('external' === value.kind) {
784
+ const source = resolveSource(workspacePath(from), value.specifier);
785
+ if (!source) return foreignSpecifier(value.specifier);
786
+ return '*' === value.name ? inspect(source) : exportedValue(source, value.name);
787
+ }
788
+ if ('unresolved' === value.kind && __rspack_external__babel_types_6e8337dd.isImportDeclaration(value.node) && value.node.specifiers.some((specifier)=>__rspack_external__babel_types_6e8337dd.isImportNamespaceSpecifier(specifier))) {
789
+ const specifier = value.node.source.value;
790
+ const source = resolveSource(workspacePath(value.module), specifier);
791
+ return source ? inspect(source) : foreignSpecifier(specifier);
792
+ }
793
+ if ('node' !== value.kind || seenValues.has(value.node)) return false;
794
+ seenValues.add(value.node);
795
+ const { node, module } = value;
796
+ 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))) {
797
+ const [argument] = node.arguments;
798
+ const binding = argument ? graphBinding(graph, module, argument) : void 0;
799
+ if (module.boundary !== owner || binding?.kind !== 'declaration' || binding.module.boundary !== owner) return true;
800
+ }
801
+ return nestedValues(node).some((expression)=>graph.evaluate(module, expression).some((nested)=>foreignValue(nested, module)));
802
+ };
803
+ const exportName = (node)=>__rspack_external__babel_types_6e8337dd.isIdentifier(node) ? node.name : node.value;
804
+ const names = (source, visited = new Set())=>{
805
+ if (visited.has(source)) return [];
806
+ visited.add(source);
807
+ return graph.module(absolute(source)).file.program.body.flatMap((statement)=>{
808
+ if (__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement)) return [
809
+ 'default'
810
+ ];
811
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) && !__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return [];
812
+ if (__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement)) {
813
+ const target = resolveSource(source, statement.source.value);
814
+ return target ? names(target, visited).filter((name)=>'default' !== name) : [];
815
+ }
816
+ 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 ? [] : [
817
+ exportName(specifier.exported)
818
+ ]);
819
+ });
820
+ };
821
+ const exportedValue = (source, name)=>{
822
+ const visited = seenExports.get(source) ?? new Set();
823
+ if (visited.has(name)) return false;
824
+ visited.add(name);
825
+ seenExports.set(source, visited);
826
+ const module = graph.module(absolute(source));
827
+ const evaluates = (from, node)=>graph.evaluate(from, node).some((value)=>foreignValue(value, from));
828
+ const binding = graph.resolve(module, name, 'export');
829
+ if (binding?.kind === 'external') return foreignValue({
830
+ ...binding,
831
+ members: []
832
+ }, module);
833
+ if (binding?.kind === 'declaration') return evaluates(binding.module, binding.expression);
834
+ for (const statement of module.file.program.body){
835
+ if (__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement) && 'default' === name) return evaluates(module, statement.declaration);
836
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
837
+ if (__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(statement.declaration) && statement.declaration.id?.name === name) return evaluates(module, statement.declaration);
838
+ const specifier = statement.specifiers.find((item)=>exportName(item.exported) === name);
839
+ if (!specifier) continue;
840
+ if (__rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier) && 'type' === specifier.exportKind) return false;
841
+ if (!statement.source) return __rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier) ? evaluates(module, specifier.local) : false;
842
+ const target = resolveSource(source, statement.source.value);
843
+ if (!target) return foreignSpecifier(statement.source.value);
844
+ if (__rspack_external__babel_types_6e8337dd.isExportNamespaceSpecifier(specifier)) return inspect(target);
845
+ if (__rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier)) return exportedValue(target, exportName(specifier.local));
846
+ }
847
+ return module.file.program.body.some((statement)=>{
848
+ if (!__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) return false;
849
+ const target = resolveSource(source, statement.source.value);
850
+ return !!target && names(target).includes(name) && exportedValue(target, name);
851
+ });
852
+ };
853
+ const inspect = (source)=>[
854
+ ...new Set(names(source))
855
+ ].some((name)=>exportedValue(source, name));
856
+ return inspect(entry);
857
+ };
858
+ const ownerClients = new Map();
859
+ for (const app of apps)if (exists(`${app.path}/package.json`)) {
860
+ const manifest = JSON.parse(read(`${app.path}/package.json`));
861
+ for (const key of [
862
+ './api/client',
863
+ './api/rpc-client'
864
+ ]){
865
+ const file = clientExportFile(app.path, manifest.exports?.[key]);
866
+ if (file) ownerClients.set(`${manifest.name}${key.slice(1)}`, file);
867
+ }
503
868
  }
504
- 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`));
869
+ for (const shell of apps.filter((app)=>'shell' === app.kind))guarded(shell.path, ()=>checkShellExports(normalize(shell.path)));
505
870
  if (exists('topology/reference-topology.json')) guarded('topology/reference-topology.json', ()=>{
506
871
  for (const vertical of JSON.parse(read('topology/reference-topology.json')).verticals ?? []){
507
872
  if (vertical.api?.runtime === 'effect') {
@@ -187,6 +187,7 @@ function parseModule(filePath, workspace) {
187
187
  const file = parseSource(node_fs.readFileSync(filePath, 'utf8'), filePath);
188
188
  let scope;
189
189
  const references = new Map();
190
+ const declarations = new Map();
190
191
  const returns = new Map();
191
192
  const bareReturns = new Set();
192
193
  const functionScopes = new Map();
@@ -297,6 +298,11 @@ function parseModule(filePath, workspace) {
297
298
  ReferencedIdentifier (p) {
298
299
  references.set(p.node, p.scope.getBinding(p.node.name));
299
300
  },
301
+ BindingIdentifier (p) {
302
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(p.node)) return;
303
+ const binding = p.scope.getBinding(p.node.name);
304
+ if (binding?.identifier === p.node) declarations.set(p.node, binding);
305
+ },
300
306
  Function (p) {
301
307
  functionScopes.set(p.node, p.scope);
302
308
  },
@@ -362,6 +368,7 @@ function parseModule(filePath, workspace) {
362
368
  scope,
363
369
  workspace,
364
370
  references,
371
+ declarations,
365
372
  returns,
366
373
  bareReturns,
367
374
  functionScopes,
@@ -1097,7 +1104,7 @@ function createModuleGraph() {
1097
1104
  }
1098
1105
  };
1099
1106
  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);
1100
- const resolveBinding = (module, name, scope, chain, seen)=>{
1107
+ const resolveBinding = (module, name, scope, chain, seen, lexical)=>{
1101
1108
  const key = `${scope}:${module.path}#${name}`;
1102
1109
  if (seen.has(key) || seen.size >= MAX_LOOKUPS) return;
1103
1110
  seen.add(key);
@@ -1120,7 +1127,7 @@ function createModuleGraph() {
1120
1127
  return void 0 === next ? void 0 : resolveBinding(next, imported, 'export', hops, seen);
1121
1128
  };
1122
1129
  if ('local' === scope) {
1123
- const binding = factsOf(module).scope.getBinding(name);
1130
+ const binding = lexical ?? factsOf(module).scope.getBinding(name);
1124
1131
  if (!binding || mutated(module, binding)) return;
1125
1132
  if ('module' === binding.kind) {
1126
1133
  const imported = importOf(binding);
@@ -1149,6 +1156,39 @@ function createModuleGraph() {
1149
1156
  if (void 0 !== resolved) return resolved;
1150
1157
  }
1151
1158
  };
1159
+ const resolveReference = (module, reference)=>{
1160
+ const node = unwrapExpression(reference, true);
1161
+ const moduleFacts = factsOf(module);
1162
+ const bindingAt = (identifier)=>moduleFacts.references.get(identifier) ?? moduleFacts.declarations.get(identifier);
1163
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(node)) {
1164
+ const binding = bindingAt(node);
1165
+ return binding ? resolveBinding(module, node.name, 'local', [], new Set(), binding) : void 0;
1166
+ }
1167
+ 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;
1168
+ const binding = bindingAt(node.object);
1169
+ if (!binding || 'module' !== binding.kind || mutated(module, binding)) return;
1170
+ const imported = importOf(binding);
1171
+ if (imported?.name !== '*') return;
1172
+ const target = follow(module, imported.specifier);
1173
+ const chain = [
1174
+ {
1175
+ path: module.path,
1176
+ name: node.object.name
1177
+ }
1178
+ ];
1179
+ if ('external' === target) return {
1180
+ kind: 'external',
1181
+ specifier: imported.specifier,
1182
+ name: node.property.name,
1183
+ chain
1184
+ };
1185
+ return void 0 === target ? void 0 : resolveBinding(target, node.property.name, 'export', chain, new Set());
1186
+ };
1187
+ const isUnboundReference = (module, reference)=>{
1188
+ const node = unwrapExpression(reference, true);
1189
+ const { references } = factsOf(module);
1190
+ return __rspack_external__babel_types_6e8337dd.isIdentifier(node) && references.has(node) && void 0 === references.get(node);
1191
+ };
1152
1192
  const evaluation = (walking)=>{
1153
1193
  const active = new Set();
1154
1194
  let frames = 0;
@@ -1756,6 +1796,8 @@ function createModuleGraph() {
1756
1796
  return {
1757
1797
  module: (filePath)=>load(filePath),
1758
1798
  resolve: (module, name, scope)=>resolveBinding(module, name, scope, [], new Set()),
1799
+ resolveReference,
1800
+ isUnboundReference,
1759
1801
  evaluate: (module, expression)=>evaluation(false).values(module, expression, []),
1760
1802
  reachable: (module, expression)=>evaluation(true).reachable(module, expression)
1761
1803
  };