@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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.
Files changed (42) hide show
  1. package/bin/modern-api-check-files.mjs +7 -0
  2. package/bin/modern-api-check.mjs +4 -0
  3. package/bin/modern-i18n-check.mjs +4 -0
  4. package/dist/cjs/cli/microvertical-api-check.cjs +100 -0
  5. package/dist/cjs/cli/oxlint.cjs +9 -5
  6. package/dist/cjs/cli/workspace-source-check.cjs +90 -17
  7. package/dist/cjs/microvertical-api-baseline.cjs +505 -0
  8. package/dist/cjs/microvertical-api-boundary.cjs +586 -0
  9. package/dist/cjs/microvertical-api-owner.cjs +217 -0
  10. package/dist/cjs/module-graph.cjs +321 -0
  11. package/dist/cjs/oxlint-plugin/rules/strict-effect-api-boundaries.cjs +8 -6
  12. package/dist/cjs/source-analysis.cjs +104 -0
  13. package/dist/cjs/strict-effect-runtime.cjs +376 -0
  14. package/dist/esm/cli/microvertical-api-check.js +52 -0
  15. package/dist/esm/cli/oxlint.js +10 -6
  16. package/dist/esm/cli/workspace-source-check.js +86 -13
  17. package/dist/esm/microvertical-api-baseline.js +464 -0
  18. package/dist/esm/microvertical-api-boundary.js +525 -0
  19. package/dist/esm/microvertical-api-owner.js +162 -0
  20. package/dist/esm/module-graph.js +260 -0
  21. package/dist/esm/oxlint-plugin/rules/strict-effect-api-boundaries.js +8 -6
  22. package/dist/esm/source-analysis.js +44 -0
  23. package/dist/esm/strict-effect-runtime.js +324 -0
  24. package/dist/esm-node/cli/microvertical-api-check.js +53 -0
  25. package/dist/esm-node/cli/oxlint.js +10 -6
  26. package/dist/esm-node/cli/workspace-source-check.js +86 -13
  27. package/dist/esm-node/microvertical-api-baseline.js +465 -0
  28. package/dist/esm-node/microvertical-api-boundary.js +526 -0
  29. package/dist/esm-node/microvertical-api-owner.js +163 -0
  30. package/dist/esm-node/module-graph.js +261 -0
  31. package/dist/esm-node/oxlint-plugin/rules/strict-effect-api-boundaries.js +8 -6
  32. package/dist/esm-node/source-analysis.js +45 -0
  33. package/dist/esm-node/strict-effect-runtime.js +325 -0
  34. package/dist/types/cli/microvertical-api-check.d.ts +1 -0
  35. package/dist/types/cli/workspace-source-check.d.ts +1 -1
  36. package/dist/types/microvertical-api-baseline.d.ts +23 -0
  37. package/dist/types/microvertical-api-boundary.d.ts +46 -0
  38. package/dist/types/microvertical-api-owner.d.ts +6 -0
  39. package/dist/types/module-graph.d.ts +52 -0
  40. package/dist/types/source-analysis.d.ts +10 -0
  41. package/dist/types/strict-effect-runtime.d.ts +10 -0
  42. package/package.json +29 -6
@@ -0,0 +1,163 @@
1
+ import "node:module";
2
+ import node_fs from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import node_path from "node:path";
5
+ import { parse } from "@babel/parser";
6
+ import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
7
+ const baselinePackage = '@modern-js/bff-effect/microvertical-api';
8
+ const ownerName = '@modern-js/bff-effect';
9
+ const within = (root, file)=>file.startsWith(`${root}${node_path.sep}`);
10
+ function resolveBaselinePackageDirectory(fromFile) {
11
+ const lookup = createRequire(fromFile).resolve.paths(baselinePackage);
12
+ for (const directory of lookup ?? []){
13
+ const candidate = node_path.join(directory, ownerName);
14
+ if (!node_fs.existsSync(candidate)) continue;
15
+ const root = node_fs.realpathSync(candidate);
16
+ const manifest = JSON.parse(node_fs.readFileSync(node_path.join(root, 'package.json'), 'utf8'));
17
+ if (![
18
+ ownerName,
19
+ '@bleedingdev/modern-js-bff-effect'
20
+ ].includes(manifest.name)) throw new Error(`${fromFile}: ${baselinePackage} resolved to unrelated package ${String(manifest.name)}`);
21
+ const target = importTarget(manifest.exports?.['./microvertical-api']);
22
+ if (!target?.startsWith('./')) throw new Error(`${fromFile}: ${baselinePackage} has no public Node import export`);
23
+ node_fs.statSync(node_path.resolve(root, target));
24
+ return root;
25
+ }
26
+ throw new Error(`${fromFile}: cannot resolve installed ${baselinePackage}`);
27
+ }
28
+ function importTarget(value) {
29
+ if ('string' == typeof value) return value;
30
+ if (!value || 'object' != typeof value || Array.isArray(value)) return;
31
+ for (const [condition, target] of Object.entries(value))if ([
32
+ 'node',
33
+ 'import',
34
+ 'default'
35
+ ].includes(condition)) {
36
+ const resolved = importTarget(target);
37
+ if (resolved) return resolved;
38
+ }
39
+ }
40
+ function baselinePublicIdentityIsExact(fromFile, expectation) {
41
+ const owner = node_fs.realpathSync(expectation.baselinePackageDirectory);
42
+ const manifest = JSON.parse(node_fs.readFileSync(node_path.join(owner, 'package.json'), 'utf8'));
43
+ if (expectation.baselinePackage !== baselinePackage || ![
44
+ ownerName,
45
+ '@bleedingdev/modern-js-bff-effect'
46
+ ].includes(manifest.name)) return false;
47
+ let resolvedOwner;
48
+ try {
49
+ resolvedOwner = resolveBaselinePackageDirectory(fromFile);
50
+ } catch (error) {
51
+ if (error instanceof Error && error.message.includes('resolved to unrelated package')) return false;
52
+ throw error;
53
+ }
54
+ if (resolvedOwner !== owner) return false;
55
+ const target = importTarget(manifest.exports?.['./microvertical-api']);
56
+ if (!target?.startsWith('./')) return false;
57
+ const modules = new Map();
58
+ const load = (filename)=>{
59
+ const real = node_fs.realpathSync(filename);
60
+ if (!within(owner, real)) return;
61
+ let file = modules.get(real);
62
+ if (!file) {
63
+ if (modules.size >= 64 || node_fs.statSync(real).size > 1000000) throw new Error(`${real}: public export analysis budget exceeded`);
64
+ file = parse(node_fs.readFileSync(real, 'utf8'), {
65
+ sourceType: 'module',
66
+ sourceFilename: real,
67
+ plugins: [
68
+ "typescript"
69
+ ]
70
+ });
71
+ modules.set(real, file);
72
+ }
73
+ return {
74
+ file,
75
+ real
76
+ };
77
+ };
78
+ const follow = (from, specifier)=>{
79
+ if (!specifier.startsWith('.')) return;
80
+ const candidate = node_path.resolve(node_path.dirname(from), specifier);
81
+ if (!within(owner, candidate)) return;
82
+ return [
83
+ candidate,
84
+ `${candidate}.ts`,
85
+ `${candidate}.js`,
86
+ node_path.join(candidate, 'index.ts'),
87
+ node_path.join(candidate, 'index.js')
88
+ ].find((file)=>node_fs.existsSync(file) && node_fs.statSync(file).isFile());
89
+ };
90
+ const exportedOwner = (filename, name, seen = new Set())=>{
91
+ const loaded = load(filename);
92
+ if (!loaded) return;
93
+ const { file, real } = loaded;
94
+ const key = `${real}:${name}`;
95
+ if (seen.has(key)) return;
96
+ seen = new Set([
97
+ ...seen,
98
+ key
99
+ ]);
100
+ const explicit = [];
101
+ const stars = [];
102
+ for (const statement of file.program.body){
103
+ if (__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) && 'type' !== statement.exportKind) {
104
+ const next = follow(real, statement.source.value);
105
+ if (!next) return;
106
+ stars.push(next);
107
+ }
108
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
109
+ const declaration = statement.declaration;
110
+ if (__rspack_external__babel_types_6e8337dd.isVariableDeclaration(declaration) && declaration.declarations.some((value)=>__rspack_external__babel_types_6e8337dd.isIdentifier(value.id, {
111
+ name
112
+ }))) explicit.push(real);
113
+ if ((__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(declaration) || __rspack_external__babel_types_6e8337dd.isClassDeclaration(declaration)) && __rspack_external__babel_types_6e8337dd.isIdentifier(declaration.id, {
114
+ name
115
+ })) explicit.push(real);
116
+ for (const value of statement.specifiers)if (__rspack_external__babel_types_6e8337dd.isExportSpecifier(value) && 'type' !== value.exportKind && __rspack_external__babel_types_6e8337dd.isIdentifier(value.exported, {
117
+ name
118
+ })) {
119
+ if (!__rspack_external__babel_types_6e8337dd.isIdentifier(value.local, {
120
+ name
121
+ })) {
122
+ explicit.push(void 0);
123
+ continue;
124
+ }
125
+ if (statement.source) {
126
+ const next = follow(real, statement.source.value);
127
+ explicit.push(next ? exportedOwner(next, name, seen) : void 0);
128
+ } else {
129
+ const local = file.program.body.find((item)=>__rspack_external__babel_types_6e8337dd.isVariableDeclaration(item) && item.declarations.some((value)=>__rspack_external__babel_types_6e8337dd.isIdentifier(value.id, {
130
+ name
131
+ })) || (__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(item) || __rspack_external__babel_types_6e8337dd.isClassDeclaration(item)) && __rspack_external__babel_types_6e8337dd.isIdentifier(item.id, {
132
+ name
133
+ }));
134
+ if (local) {
135
+ explicit.push(real);
136
+ continue;
137
+ }
138
+ const imported = file.program.body.find((item)=>__rspack_external__babel_types_6e8337dd.isImportDeclaration(item) && 'type' !== item.importKind && item.specifiers.some((value)=>__rspack_external__babel_types_6e8337dd.isImportSpecifier(value) && 'type' !== value.importKind && __rspack_external__babel_types_6e8337dd.isIdentifier(value.local, {
139
+ name
140
+ }) && __rspack_external__babel_types_6e8337dd.isIdentifier(value.imported, {
141
+ name
142
+ })));
143
+ const next = __rspack_external__babel_types_6e8337dd.isImportDeclaration(imported) ? follow(real, imported.source.value) : void 0;
144
+ explicit.push(next ? exportedOwner(next, name, seen) : void 0);
145
+ }
146
+ }
147
+ }
148
+ if (explicit.length) return 1 === explicit.length ? explicit[0] : void 0;
149
+ const owners = stars.map((next)=>exportedOwner(next, name, seen));
150
+ const unique = new Set(owners.filter((value)=>void 0 !== value));
151
+ return 1 === unique.size ? [
152
+ ...unique
153
+ ][0] : void 0;
154
+ };
155
+ const entry = node_path.resolve(owner, target);
156
+ const owners = [
157
+ 'MicroVerticalBuildMarkerSchema',
158
+ 'MicroVerticalReadinessSchema',
159
+ 'createMicroVerticalOperationContext'
160
+ ].map((name)=>exportedOwner(entry, name));
161
+ return void 0 !== owners[0] && owners.every((value)=>value === owners[0]);
162
+ }
163
+ export { baselinePackage, baselinePublicIdentityIsExact, resolveBaselinePackageDirectory };
@@ -0,0 +1,261 @@
1
+ import "node:module";
2
+ import node_fs from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import node_path from "node:path";
5
+ import { exports as external_resolve_exports_exports } from "resolve.exports";
6
+ import { SourceSyntaxError, parseSource, traverseSource } from "./source-analysis.js";
7
+ import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
8
+ const MAX_MODULES = 256;
9
+ const MAX_LOOKUPS = 512;
10
+ function parseConsumer(filePath) {
11
+ if (node_fs.statSync(filePath).size > 1000000) throw new Error(`${filePath}: consumer source exceeds 1 MB analysis budget`);
12
+ const file = parseSource(node_fs.readFileSync(filePath, 'utf8'), filePath);
13
+ traverseSource(file, {
14
+ AssignmentExpression (p) {
15
+ throw new SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
16
+ },
17
+ UpdateExpression (p) {
18
+ throw new SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
19
+ },
20
+ TSModuleDeclaration () {
21
+ throw new SourceSyntaxError('contract bindings must not be merged with namespaces');
22
+ }
23
+ });
24
+ return file;
25
+ }
26
+ const propertyName = (node)=>{
27
+ if (void 0 !== node && (__rspack_external__babel_types_6e8337dd.isIdentifier(node) || __rspack_external__babel_types_6e8337dd.isStringLiteral(node) || __rspack_external__babel_types_6e8337dd.isNumericLiteral(node))) return __rspack_external__babel_types_6e8337dd.isIdentifier(node) ? node.name : String(node.value);
28
+ };
29
+ const localConst = (sourceFile, name)=>{
30
+ for (const item of sourceFile.program.body){
31
+ const statement = __rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(item) ? item.declaration : item;
32
+ if (!__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement) || 'const' !== statement.kind) continue;
33
+ const matches = statement.declarations.filter((declaration)=>__rspack_external__babel_types_6e8337dd.isIdentifier(declaration.id, {
34
+ name
35
+ }));
36
+ if (1 === matches.length) return matches[0];
37
+ }
38
+ };
39
+ const exportedConst = (sourceFile, name)=>{
40
+ for (const statement of sourceFile.program.body)if (__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) && 'type' !== statement.exportKind) {
41
+ if (__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement.declaration) && 'const' === statement.declaration.kind) {
42
+ const declaration = statement.declaration.declarations.find((value)=>__rspack_external__babel_types_6e8337dd.isIdentifier(value.id, {
43
+ name
44
+ }));
45
+ if (declaration) return declaration;
46
+ }
47
+ if (!statement.source && statement.specifiers.some((value)=>__rspack_external__babel_types_6e8337dd.isExportSpecifier(value) && 'type' !== value.exportKind && __rspack_external__babel_types_6e8337dd.isIdentifier(value.local, {
48
+ name
49
+ }) && __rspack_external__babel_types_6e8337dd.isIdentifier(value.exported, {
50
+ name
51
+ }))) return localConst(sourceFile, name);
52
+ }
53
+ };
54
+ const inside = (file, boundary)=>file === boundary || file.startsWith(`${boundary}${node_path.sep}`);
55
+ const containingWorkspace = (file)=>{
56
+ for(let directory = node_path.dirname(file);; directory = node_path.dirname(directory)){
57
+ if (node_fs.existsSync(node_path.join(directory, 'pnpm-workspace.yaml'))) return node_fs.realpathSync(directory);
58
+ if (directory === node_path.dirname(directory)) return;
59
+ }
60
+ };
61
+ const sourceBoundary = (file, workspace)=>{
62
+ for(let directory = node_path.dirname(file);; directory = node_path.dirname(directory)){
63
+ if (node_fs.existsSync(node_path.join(directory, 'package.json')) && directory !== workspace) return node_fs.realpathSync(directory);
64
+ if (directory === workspace || directory === node_path.dirname(directory)) return node_fs.realpathSync(node_path.dirname(file));
65
+ }
66
+ };
67
+ const sourceFileAt = (target, boundary)=>{
68
+ const stem = target.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
69
+ for (const candidate of [
70
+ `${stem}.ts`,
71
+ `${stem}.tsx`,
72
+ `${stem}.mts`,
73
+ `${stem}.cts`,
74
+ target,
75
+ node_path.join(target, 'index.ts'),
76
+ node_path.join(target, 'index.tsx')
77
+ ])try {
78
+ const real = node_fs.realpathSync(candidate);
79
+ if (inside(real, boundary) && node_fs.statSync(real).isFile()) return real;
80
+ } catch {}
81
+ };
82
+ const packageEntry = (specifier)=>{
83
+ if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#')) return;
84
+ const parts = specifier.split('/');
85
+ const count = specifier.startsWith('@') ? 2 : 1;
86
+ if (parts.length < count || parts.slice(0, count).some((part)=>!part)) return;
87
+ return {
88
+ name: parts.slice(0, count).join('/'),
89
+ exportKey: parts.length === count ? '.' : `./${parts.slice(count).join('/')}`
90
+ };
91
+ };
92
+ const resolveModulePath = (module, specifier, workspace)=>{
93
+ if (/^\.\.?\//u.test(specifier)) {
94
+ const file = sourceFileAt(node_path.resolve(node_path.dirname(module.path), specifier), module.boundary);
95
+ return void 0 === file ? void 0 : {
96
+ kind: 'module',
97
+ path: file,
98
+ boundary: module.boundary
99
+ };
100
+ }
101
+ const entry = packageEntry(specifier);
102
+ if (!entry) return;
103
+ if (!workspace) return {
104
+ kind: 'external'
105
+ };
106
+ const paths = createRequire(module.path).resolve.paths(entry.name) ?? [];
107
+ for (const lookup of paths){
108
+ const directory = node_path.join(lookup, entry.name);
109
+ const manifest = node_path.join(directory, 'package.json');
110
+ if (!node_fs.existsSync(manifest)) continue;
111
+ const boundary = node_fs.realpathSync(directory);
112
+ if (!inside(boundary, workspace) || boundary.split(node_path.sep).includes('node_modules')) break;
113
+ const pkg = JSON.parse(node_fs.readFileSync(manifest, 'utf8'));
114
+ if (pkg.name !== entry.name || void 0 === pkg.exports) return;
115
+ let target;
116
+ try {
117
+ const targets = external_resolve_exports_exports(pkg, entry.exportKey, {
118
+ conditions: [
119
+ 'modern:source'
120
+ ]
121
+ });
122
+ target = Array.isArray(targets) ? targets[0] : void 0;
123
+ } catch {
124
+ return;
125
+ }
126
+ if (!target || !target.startsWith('./')) return;
127
+ const file = sourceFileAt(node_path.resolve(boundary, target), boundary);
128
+ return void 0 === file ? void 0 : {
129
+ kind: 'module',
130
+ path: file,
131
+ boundary
132
+ };
133
+ }
134
+ return {
135
+ kind: 'external'
136
+ };
137
+ };
138
+ const importedBinding = (module, name)=>{
139
+ for (const statement of module.file.program.body)if (__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) && 'type' !== statement.importKind) {
140
+ for (const specifier of statement.specifiers)if (__rspack_external__babel_types_6e8337dd.isIdentifier(specifier.local, {
141
+ name
142
+ })) {
143
+ if (__rspack_external__babel_types_6e8337dd.isImportSpecifier(specifier) && 'type' !== specifier.importKind) return {
144
+ imported: propertyName(specifier.imported) ?? name,
145
+ specifier: statement.source.value
146
+ };
147
+ if (__rspack_external__babel_types_6e8337dd.isImportDefaultSpecifier(specifier)) return {
148
+ imported: 'default',
149
+ specifier: statement.source.value
150
+ };
151
+ }
152
+ }
153
+ };
154
+ const reexportedBinding = (module, name)=>{
155
+ for (const statement of module.file.program.body){
156
+ if (!__rspack_external__babel_types_6e8337dd.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
157
+ const specifier = statement.specifiers.find((value)=>__rspack_external__babel_types_6e8337dd.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
158
+ if (void 0 !== specifier && __rspack_external__babel_types_6e8337dd.isExportSpecifier(specifier)) return {
159
+ local: __rspack_external__babel_types_6e8337dd.isIdentifier(specifier.local) ? specifier.local.name : specifier.local.value,
160
+ ...statement.source ? {
161
+ specifier: statement.source.value
162
+ } : {}
163
+ };
164
+ }
165
+ };
166
+ const defaultExport = (module)=>{
167
+ for (const statement of module.file.program.body){
168
+ if (!__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement)) continue;
169
+ const declaration = statement.declaration;
170
+ if (__rspack_external__babel_types_6e8337dd.isIdentifier(declaration)) return declaration.name;
171
+ return __rspack_external__babel_types_6e8337dd.isExpression(declaration) ? declaration : void 0;
172
+ }
173
+ };
174
+ function createModuleGraph(filePath) {
175
+ const realPath = node_fs.realpathSync(filePath);
176
+ const workspace = containingWorkspace(realPath);
177
+ const root = {
178
+ file: parseConsumer(realPath),
179
+ path: realPath,
180
+ boundary: sourceBoundary(realPath, workspace)
181
+ };
182
+ const modules = new Map([
183
+ [
184
+ root.path,
185
+ root
186
+ ]
187
+ ]);
188
+ const readModule = (modulePath, boundary)=>{
189
+ if (modules.has(modulePath)) return modules.get(modulePath);
190
+ if (modules.size >= MAX_MODULES) return;
191
+ let module;
192
+ try {
193
+ module = {
194
+ file: parseConsumer(modulePath),
195
+ path: modulePath,
196
+ boundary
197
+ };
198
+ } catch {
199
+ module = void 0;
200
+ }
201
+ modules.set(modulePath, module);
202
+ return module;
203
+ };
204
+ const resolveBinding = (module, name, scope, chain, seen)=>{
205
+ const key = `${scope}:${module.path}#${name}`;
206
+ if (seen.has(key) || seen.size >= MAX_LOOKUPS) return;
207
+ seen.add(key);
208
+ const last = chain.at(-1);
209
+ const hops = last?.path === module.path && last.name === name ? chain : [
210
+ ...chain,
211
+ {
212
+ path: module.path,
213
+ name
214
+ }
215
+ ];
216
+ const declaration = 'export' === scope ? exportedConst(module.file, name) : localConst(module.file, name);
217
+ if (declaration?.init) return {
218
+ kind: 'declaration',
219
+ expression: declaration.init,
220
+ module,
221
+ chain: hops
222
+ };
223
+ const throughModule = (specifier, imported, allowExternal)=>{
224
+ const target = resolveModulePath(module, specifier, workspace);
225
+ if (target?.kind === 'external') return allowExternal ? {
226
+ kind: 'external',
227
+ specifier,
228
+ name: imported,
229
+ chain: hops
230
+ } : void 0;
231
+ const next = target && readModule(target.path, target.boundary);
232
+ return void 0 === next ? void 0 : resolveBinding(next, imported, 'export', hops, seen);
233
+ };
234
+ if ('local' === scope) {
235
+ const imported = importedBinding(module, name);
236
+ return void 0 === imported ? void 0 : throughModule(imported.specifier, imported.imported, true);
237
+ }
238
+ if ('default' === name) {
239
+ const exported = defaultExport(module);
240
+ if ('string' == typeof exported) return resolveBinding(module, exported, 'local', hops, seen);
241
+ return void 0 === exported ? void 0 : {
242
+ kind: 'declaration',
243
+ expression: exported,
244
+ module,
245
+ chain: hops
246
+ };
247
+ }
248
+ const reexported = reexportedBinding(module, name);
249
+ if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', hops, seen) : throughModule(reexported.specifier, reexported.local, true);
250
+ for (const statement of module.file.program.body){
251
+ if (!__rspack_external__babel_types_6e8337dd.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
252
+ const resolved = throughModule(statement.source.value, name, false);
253
+ if (void 0 !== resolved) return resolved;
254
+ }
255
+ };
256
+ return {
257
+ root,
258
+ resolve: (module, name, scope)=>resolveBinding(module, name, scope, [], new Set())
259
+ };
260
+ }
261
+ export { createModuleGraph, exportedConst, localConst, parseConsumer, propertyName };
@@ -1,4 +1,5 @@
1
1
  import "node:module";
2
+ import { createEffectApiImportResolver, strictEffectRuntimeTopologyViolation } from "../../strict-effect-runtime.js";
2
3
  import { getSourceText, isApiEntryFile, isApiSourceFile, isForbiddenGeneratedEffectPath, isSharedApiContractFile, normalizeFilename, reportMissingProgramPattern, reportProgramPattern } from "../ast.js";
3
4
  const createStrictEffectApiBoundariesRule = ()=>({
4
5
  meta: {
@@ -18,15 +19,16 @@ const createStrictEffectApiBoundariesRule = ()=>({
18
19
  node,
19
20
  message: 'Generated UltraModern workspaces use direct api/index.ts, shared/api.ts and src/api/* paths; api/effect, api/lambda, shared/effect and src/effect are forbidden.'
20
21
  });
21
- reportProgramPattern(context, node, source, /@modern-js\/plugin-bff\/hono-server/u, 'UltraModern API workspaces must not import Hono server helpers; use @modern-js/plugin-bff/effect-edge and HttpApi.');
22
+ reportProgramPattern(context, node, source, /@modern-js\/plugin-bff\/(?:server|hono-server)(?![\w-])/u, 'UltraModern API workspaces must not import Hono server helpers; use @modern-js/bff-effect/effect-edge and HttpApi.');
22
23
  reportProgramPattern(context, node, source, /\bruntimeFramework\s*(?::|=)\s*['"]hono['"]/u, 'UltraModern API apps must use bff.runtimeFramework: "effect".');
23
- reportProgramPattern(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
24
+ reportProgramPattern(context, node, source, /(?:from|import)\s*['"][^'"]*(?:api\/effect|shared\/effect)(?=\/|\.[cm]?[jt]sx?['"]|['"])[^'"]*['"]|shared-effect-api/u, 'Import API code from direct api/index.ts, shared/api.ts or src/api/* paths, not api/effect, shared/effect or shared Effect API packages.');
24
25
  reportProgramPattern(context, node, source, /\bstrictEffectApproach\s*(?::|=)\s*false\b/u, 'UltraModern API apps must keep strictEffectApproach enabled.');
25
26
  if (isApiEntryFile(filename)) {
26
- reportMissingProgramPattern(context, node, source, /\bdefineEffectBff\b/u, 'Generated API entries must export defineEffectBff(...).');
27
- reportMissingProgramPattern(context, node, source, /\bHttpApiBuilder\b/u, 'Generated API entries must implement handlers through HttpApiBuilder.');
28
- reportMissingProgramPattern(context, node, source, /\bLayer\b/u, 'Generated API entries must compose dependencies with Effect Layer.');
29
- reportMissingProgramPattern(context, node, source, /from\s+['"]\.\.\/shared\/api\.ts['"]/u, 'Generated API entries must import the contract from ../shared/api.ts.');
27
+ const violation = strictEffectRuntimeTopologyViolation(source, createEffectApiImportResolver(filename));
28
+ if (violation) context.report({
29
+ node,
30
+ message: violation
31
+ });
30
32
  }
31
33
  if (isSharedApiContractFile(filename)) {
32
34
  reportMissingProgramPattern(context, node, source, /\bHttpApi\.make\b/u, 'Generated shared API contracts must declare an HttpApi contract.');
@@ -0,0 +1,45 @@
1
+ import "node:module";
2
+ import { parse } from "@babel/parser";
3
+ import traverse, { Hub, NodePath } from "@babel/traverse";
4
+ import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
5
+ const consumerParserPlugins = (filePath)=>/\.[jt]sx$/u.test(filePath) ? [
6
+ "typescript",
7
+ 'jsx'
8
+ ] : [
9
+ "typescript"
10
+ ];
11
+ class SourceSyntaxError extends Error {
12
+ }
13
+ class SourceValidationHub extends Hub {
14
+ buildError(_node, message) {
15
+ return new SourceSyntaxError(message);
16
+ }
17
+ }
18
+ function parseSource(source, filename, syntaxMessage) {
19
+ try {
20
+ return parse(source, {
21
+ sourceType: 'module',
22
+ sourceFilename: filename,
23
+ plugins: consumerParserPlugins(filename)
24
+ });
25
+ } catch (cause) {
26
+ if ('object' == typeof cause && null !== cause && 'code' in cause && 'BABEL_PARSER_SYNTAX_ERROR' === cause.code && 'reasonCode' in cause && 'string' == typeof cause.reasonCode) throw new SourceSyntaxError(syntaxMessage ?? (cause instanceof Error ? cause.message : String(cause)));
27
+ throw cause;
28
+ }
29
+ }
30
+ function traverseSource(file, visitor) {
31
+ const program = NodePath.get({
32
+ hub: new SourceValidationHub(),
33
+ parentPath: void 0,
34
+ parent: file,
35
+ container: file,
36
+ key: 'program'
37
+ });
38
+ program.setContext();
39
+ traverse(file, visitor, program.scope, void 0, program);
40
+ }
41
+ function unwrapExpression(node, includeAssertions = false) {
42
+ while(__rspack_external__babel_types_6e8337dd.isTSAsExpression(node) || __rspack_external__babel_types_6e8337dd.isParenthesizedExpression(node) || __rspack_external__babel_types_6e8337dd.isTSSatisfiesExpression(node) || includeAssertions && (__rspack_external__babel_types_6e8337dd.isTSTypeAssertion(node) || __rspack_external__babel_types_6e8337dd.isTSNonNullExpression(node)))node = node.expression;
43
+ return node;
44
+ }
45
+ export { SourceSyntaxError, consumerParserPlugins, parseSource, traverseSource, unwrapExpression };