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