@bleedingdev/modern-js-code-tools 3.8.3-ultramodern.6 → 3.9.0-ultramodern.10
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.
- package/bin/modern-api-check-files.mjs +4 -0
- package/bin/modern-api-check.mjs +4 -0
- package/dist/cjs/cli/microvertical-api-check.cjs +71 -0
- package/dist/cjs/cli/oxlint.cjs +9 -5
- package/dist/cjs/microvertical-api-baseline.cjs +677 -0
- package/dist/cjs/microvertical-api-boundary.cjs +507 -0
- package/dist/cjs/microvertical-api-owner.cjs +217 -0
- package/dist/cjs/oxlint-plugin/rules/strict-effect-api-boundaries.cjs +7 -5
- package/dist/cjs/strict-effect-runtime.cjs +411 -0
- package/dist/esm/cli/microvertical-api-check.js +33 -0
- package/dist/esm/cli/oxlint.js +10 -6
- package/dist/esm/microvertical-api-baseline.js +621 -0
- package/dist/esm/microvertical-api-boundary.js +449 -0
- package/dist/esm/microvertical-api-owner.js +162 -0
- package/dist/esm/oxlint-plugin/rules/strict-effect-api-boundaries.js +7 -5
- package/dist/esm/strict-effect-runtime.js +358 -0
- package/dist/esm-node/cli/microvertical-api-check.js +34 -0
- package/dist/esm-node/cli/oxlint.js +10 -6
- package/dist/esm-node/microvertical-api-baseline.js +622 -0
- package/dist/esm-node/microvertical-api-boundary.js +450 -0
- package/dist/esm-node/microvertical-api-owner.js +163 -0
- package/dist/esm-node/oxlint-plugin/rules/strict-effect-api-boundaries.js +7 -5
- package/dist/esm-node/strict-effect-runtime.js +359 -0
- package/dist/types/cli/microvertical-api-check.d.ts +1 -0
- package/dist/types/microvertical-api-baseline.d.ts +28 -0
- package/dist/types/microvertical-api-boundary.d.ts +32 -0
- package/dist/types/microvertical-api-owner.d.ts +6 -0
- package/dist/types/strict-effect-runtime.d.ts +10 -0
- package/package.json +26 -5
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
14
|
+
var define = (defs, kind)=>{
|
|
15
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
[kind]: defs[key]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
define(getters, "get");
|
|
21
|
+
define(values, "value");
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
(()=>{
|
|
25
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
26
|
+
})();
|
|
27
|
+
(()=>{
|
|
28
|
+
__webpack_require__.r = (exports1)=>{
|
|
29
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
30
|
+
value: 'Module'
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
33
|
+
value: true
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
var __webpack_exports__ = {};
|
|
38
|
+
__webpack_require__.r(__webpack_exports__);
|
|
39
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
40
|
+
baselinePackage: ()=>baselinePackage,
|
|
41
|
+
baselinePublicIdentityIsExact: ()=>baselinePublicIdentityIsExact,
|
|
42
|
+
resolveBaselinePackageDirectory: ()=>resolveBaselinePackageDirectory
|
|
43
|
+
});
|
|
44
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
45
|
+
var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
|
|
46
|
+
const external_node_module_namespaceObject = require("node:module");
|
|
47
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
48
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
49
|
+
const parser_namespaceObject = require("@babel/parser");
|
|
50
|
+
const types_namespaceObject = require("@babel/types");
|
|
51
|
+
const baselinePackage = '@modern-js/bff-effect/microvertical-api';
|
|
52
|
+
const ownerName = '@modern-js/bff-effect';
|
|
53
|
+
const within = (root, file)=>file.startsWith(`${root}${external_node_path_default().sep}`);
|
|
54
|
+
function resolveBaselinePackageDirectory(fromFile) {
|
|
55
|
+
const lookup = (0, external_node_module_namespaceObject.createRequire)(fromFile).resolve.paths(baselinePackage);
|
|
56
|
+
for (const directory of lookup ?? []){
|
|
57
|
+
const candidate = external_node_path_default().join(directory, ownerName);
|
|
58
|
+
if (!external_node_fs_default().existsSync(candidate)) continue;
|
|
59
|
+
const root = external_node_fs_default().realpathSync(candidate);
|
|
60
|
+
const manifest = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(root, 'package.json'), 'utf8'));
|
|
61
|
+
if (![
|
|
62
|
+
ownerName,
|
|
63
|
+
'@bleedingdev/modern-js-bff-effect'
|
|
64
|
+
].includes(manifest.name)) throw new Error(`${fromFile}: ${baselinePackage} resolved to unrelated package ${String(manifest.name)}`);
|
|
65
|
+
const target = importTarget(manifest.exports?.['./microvertical-api']);
|
|
66
|
+
if (!target?.startsWith('./')) throw new Error(`${fromFile}: ${baselinePackage} has no public Node import export`);
|
|
67
|
+
external_node_fs_default().statSync(external_node_path_default().resolve(root, target));
|
|
68
|
+
return root;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`${fromFile}: cannot resolve installed ${baselinePackage}`);
|
|
71
|
+
}
|
|
72
|
+
function importTarget(value) {
|
|
73
|
+
if ('string' == typeof value) return value;
|
|
74
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return;
|
|
75
|
+
for (const [condition, target] of Object.entries(value))if ([
|
|
76
|
+
'node',
|
|
77
|
+
'import',
|
|
78
|
+
'default'
|
|
79
|
+
].includes(condition)) {
|
|
80
|
+
const resolved = importTarget(target);
|
|
81
|
+
if (resolved) return resolved;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function baselinePublicIdentityIsExact(fromFile, expectation) {
|
|
85
|
+
const owner = external_node_fs_default().realpathSync(expectation.baselinePackageDirectory);
|
|
86
|
+
const manifest = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(owner, 'package.json'), 'utf8'));
|
|
87
|
+
if (expectation.baselinePackage !== baselinePackage || ![
|
|
88
|
+
ownerName,
|
|
89
|
+
'@bleedingdev/modern-js-bff-effect'
|
|
90
|
+
].includes(manifest.name)) return false;
|
|
91
|
+
let resolvedOwner;
|
|
92
|
+
try {
|
|
93
|
+
resolvedOwner = resolveBaselinePackageDirectory(fromFile);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error instanceof Error && error.message.includes('resolved to unrelated package')) return false;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
if (resolvedOwner !== owner) return false;
|
|
99
|
+
const target = importTarget(manifest.exports?.['./microvertical-api']);
|
|
100
|
+
if (!target?.startsWith('./')) return false;
|
|
101
|
+
const modules = new Map();
|
|
102
|
+
const load = (filename)=>{
|
|
103
|
+
const real = external_node_fs_default().realpathSync(filename);
|
|
104
|
+
if (!within(owner, real)) return;
|
|
105
|
+
let file = modules.get(real);
|
|
106
|
+
if (!file) {
|
|
107
|
+
if (modules.size >= 64 || external_node_fs_default().statSync(real).size > 1000000) throw new Error(`${real}: public export analysis budget exceeded`);
|
|
108
|
+
file = (0, parser_namespaceObject.parse)(external_node_fs_default().readFileSync(real, 'utf8'), {
|
|
109
|
+
sourceType: 'module',
|
|
110
|
+
sourceFilename: real,
|
|
111
|
+
plugins: [
|
|
112
|
+
"typescript"
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
modules.set(real, file);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
file,
|
|
119
|
+
real
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
const follow = (from, specifier)=>{
|
|
123
|
+
if (!specifier.startsWith('.')) return;
|
|
124
|
+
const candidate = external_node_path_default().resolve(external_node_path_default().dirname(from), specifier);
|
|
125
|
+
if (!within(owner, candidate)) return;
|
|
126
|
+
return [
|
|
127
|
+
candidate,
|
|
128
|
+
`${candidate}.ts`,
|
|
129
|
+
`${candidate}.js`,
|
|
130
|
+
external_node_path_default().join(candidate, 'index.ts'),
|
|
131
|
+
external_node_path_default().join(candidate, 'index.js')
|
|
132
|
+
].find((file)=>external_node_fs_default().existsSync(file) && external_node_fs_default().statSync(file).isFile());
|
|
133
|
+
};
|
|
134
|
+
const exportedOwner = (filename, name, seen = new Set())=>{
|
|
135
|
+
const loaded = load(filename);
|
|
136
|
+
if (!loaded) return;
|
|
137
|
+
const { file, real } = loaded;
|
|
138
|
+
const key = `${real}:${name}`;
|
|
139
|
+
if (seen.has(key)) return;
|
|
140
|
+
seen = new Set([
|
|
141
|
+
...seen,
|
|
142
|
+
key
|
|
143
|
+
]);
|
|
144
|
+
const explicit = [];
|
|
145
|
+
const stars = [];
|
|
146
|
+
for (const statement of file.program.body){
|
|
147
|
+
if (types_namespaceObject.isExportAllDeclaration(statement) && 'type' !== statement.exportKind) {
|
|
148
|
+
const next = follow(real, statement.source.value);
|
|
149
|
+
if (!next) return;
|
|
150
|
+
stars.push(next);
|
|
151
|
+
}
|
|
152
|
+
if (!types_namespaceObject.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
|
|
153
|
+
const declaration = statement.declaration;
|
|
154
|
+
if (types_namespaceObject.isVariableDeclaration(declaration) && declaration.declarations.some((value)=>types_namespaceObject.isIdentifier(value.id, {
|
|
155
|
+
name
|
|
156
|
+
}))) explicit.push(real);
|
|
157
|
+
if ((types_namespaceObject.isFunctionDeclaration(declaration) || types_namespaceObject.isClassDeclaration(declaration)) && types_namespaceObject.isIdentifier(declaration.id, {
|
|
158
|
+
name
|
|
159
|
+
})) explicit.push(real);
|
|
160
|
+
for (const value of statement.specifiers)if (types_namespaceObject.isExportSpecifier(value) && 'type' !== value.exportKind && types_namespaceObject.isIdentifier(value.exported, {
|
|
161
|
+
name
|
|
162
|
+
})) {
|
|
163
|
+
if (!types_namespaceObject.isIdentifier(value.local, {
|
|
164
|
+
name
|
|
165
|
+
})) {
|
|
166
|
+
explicit.push(void 0);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (statement.source) {
|
|
170
|
+
const next = follow(real, statement.source.value);
|
|
171
|
+
explicit.push(next ? exportedOwner(next, name, seen) : void 0);
|
|
172
|
+
} else {
|
|
173
|
+
const local = file.program.body.find((item)=>types_namespaceObject.isVariableDeclaration(item) && item.declarations.some((value)=>types_namespaceObject.isIdentifier(value.id, {
|
|
174
|
+
name
|
|
175
|
+
})) || (types_namespaceObject.isFunctionDeclaration(item) || types_namespaceObject.isClassDeclaration(item)) && types_namespaceObject.isIdentifier(item.id, {
|
|
176
|
+
name
|
|
177
|
+
}));
|
|
178
|
+
if (local) {
|
|
179
|
+
explicit.push(real);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const imported = file.program.body.find((item)=>types_namespaceObject.isImportDeclaration(item) && 'type' !== item.importKind && item.specifiers.some((value)=>types_namespaceObject.isImportSpecifier(value) && 'type' !== value.importKind && types_namespaceObject.isIdentifier(value.local, {
|
|
183
|
+
name
|
|
184
|
+
}) && types_namespaceObject.isIdentifier(value.imported, {
|
|
185
|
+
name
|
|
186
|
+
})));
|
|
187
|
+
const next = types_namespaceObject.isImportDeclaration(imported) ? follow(real, imported.source.value) : void 0;
|
|
188
|
+
explicit.push(next ? exportedOwner(next, name, seen) : void 0);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (explicit.length) return 1 === explicit.length ? explicit[0] : void 0;
|
|
193
|
+
const owners = stars.map((next)=>exportedOwner(next, name, seen));
|
|
194
|
+
const unique = new Set(owners.filter((value)=>void 0 !== value));
|
|
195
|
+
return 1 === unique.size ? [
|
|
196
|
+
...unique
|
|
197
|
+
][0] : void 0;
|
|
198
|
+
};
|
|
199
|
+
const entry = external_node_path_default().resolve(owner, target);
|
|
200
|
+
const owners = [
|
|
201
|
+
'MicroVerticalBuildMarkerSchema',
|
|
202
|
+
'MicroVerticalReadinessSchema',
|
|
203
|
+
'createMicroVerticalOperationContext'
|
|
204
|
+
].map((name)=>exportedOwner(entry, name));
|
|
205
|
+
return void 0 !== owners[0] && owners.every((value)=>value === owners[0]);
|
|
206
|
+
}
|
|
207
|
+
exports.baselinePackage = __webpack_exports__.baselinePackage;
|
|
208
|
+
exports.baselinePublicIdentityIsExact = __webpack_exports__.baselinePublicIdentityIsExact;
|
|
209
|
+
exports.resolveBaselinePackageDirectory = __webpack_exports__.resolveBaselinePackageDirectory;
|
|
210
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
211
|
+
"baselinePackage",
|
|
212
|
+
"baselinePublicIdentityIsExact",
|
|
213
|
+
"resolveBaselinePackageDirectory"
|
|
214
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
215
|
+
Object.defineProperty(exports, '__esModule', {
|
|
216
|
+
value: true
|
|
217
|
+
});
|
|
@@ -30,6 +30,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
31
|
createStrictEffectApiBoundariesRule: ()=>createStrictEffectApiBoundariesRule
|
|
32
32
|
});
|
|
33
|
+
const external_strict_effect_runtime_cjs_namespaceObject = require("../../strict-effect-runtime.cjs");
|
|
33
34
|
const external_ast_cjs_namespaceObject = require("../ast.cjs");
|
|
34
35
|
const createStrictEffectApiBoundariesRule = ()=>({
|
|
35
36
|
meta: {
|
|
@@ -49,15 +50,16 @@ const createStrictEffectApiBoundariesRule = ()=>({
|
|
|
49
50
|
node,
|
|
50
51
|
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.'
|
|
51
52
|
});
|
|
52
|
-
(0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /@modern-js\/plugin-bff\/hono-server/u, 'UltraModern API workspaces must not import Hono server helpers; use @modern-js/
|
|
53
|
+
(0, external_ast_cjs_namespaceObject.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.');
|
|
53
54
|
(0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bruntimeFramework\s*(?::|=)\s*['"]hono['"]/u, 'UltraModern API apps must use bff.runtimeFramework: "effect".');
|
|
54
55
|
(0, external_ast_cjs_namespaceObject.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.');
|
|
55
56
|
(0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bstrictEffectApproach\s*(?::|=)\s*false\b/u, 'UltraModern API apps must keep strictEffectApproach enabled.');
|
|
56
57
|
if ((0, external_ast_cjs_namespaceObject.isApiEntryFile)(filename)) {
|
|
57
|
-
(0,
|
|
58
|
-
(
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const violation = (0, external_strict_effect_runtime_cjs_namespaceObject.strictEffectRuntimeTopologyViolation)(source, (0, external_strict_effect_runtime_cjs_namespaceObject.createEffectApiImportResolver)(filename));
|
|
59
|
+
if (violation) context.report({
|
|
60
|
+
node,
|
|
61
|
+
message: violation
|
|
62
|
+
});
|
|
61
63
|
}
|
|
62
64
|
if ((0, external_ast_cjs_namespaceObject.isSharedApiContractFile)(filename)) {
|
|
63
65
|
(0, external_ast_cjs_namespaceObject.reportMissingProgramPattern)(context, node, source, /\bHttpApi\.make\b/u, 'Generated shared API contracts must declare an HttpApi contract.');
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
14
|
+
var define = (defs, kind)=>{
|
|
15
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
[kind]: defs[key]
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
define(getters, "get");
|
|
21
|
+
define(values, "value");
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
(()=>{
|
|
25
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
26
|
+
})();
|
|
27
|
+
(()=>{
|
|
28
|
+
__webpack_require__.r = (exports1)=>{
|
|
29
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
30
|
+
value: 'Module'
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
33
|
+
value: true
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
var __webpack_exports__ = {};
|
|
38
|
+
__webpack_require__.r(__webpack_exports__);
|
|
39
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
40
|
+
createEffectApiImportResolver: ()=>createEffectApiImportResolver,
|
|
41
|
+
strictEffectRuntimeTopologyViolation: ()=>strictEffectRuntimeTopologyViolation
|
|
42
|
+
});
|
|
43
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
44
|
+
var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
|
|
45
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
46
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
47
|
+
const parser_namespaceObject = require("@babel/parser");
|
|
48
|
+
const traverse_namespaceObject = require("@babel/traverse");
|
|
49
|
+
var traverse_default = /*#__PURE__*/ __webpack_require__.n(traverse_namespaceObject);
|
|
50
|
+
const types_namespaceObject = require("@babel/types");
|
|
51
|
+
const edge = '@modern-js/bff-effect/effect-edge';
|
|
52
|
+
const nodeRuntime = '@modern-js/bff-effect/effect';
|
|
53
|
+
const sharedRuntime = /^@[^/]+\/shared-contracts\/server\/effect-bff-runtime$/u;
|
|
54
|
+
const failure = 'Generated API entries must export defineEffectBff(...) or the server-only shared Effect BFF assembly helper with an explicitly composed handler Layer and an unshadowed executable root; entries must implement handlers through HttpApiBuilder.group.';
|
|
55
|
+
const invalidSources = new WeakSet();
|
|
56
|
+
function invalidSource(message) {
|
|
57
|
+
const error = new Error(message);
|
|
58
|
+
invalidSources.add(error);
|
|
59
|
+
return error;
|
|
60
|
+
}
|
|
61
|
+
class SourceValidationHub extends traverse_namespaceObject.Hub {
|
|
62
|
+
buildError(_node, message) {
|
|
63
|
+
return invalidSource(message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function createEffectApiImportResolver(filename) {
|
|
67
|
+
const directory = external_node_fs_default().realpathSync(external_node_path_default().dirname(filename));
|
|
68
|
+
const owner = external_node_fs_default().realpathSync(external_node_path_default().resolve(directory, '..'));
|
|
69
|
+
const resolve = (from)=>(specifier)=>{
|
|
70
|
+
if (!specifier.startsWith('.')) return;
|
|
71
|
+
const target = external_node_path_default().resolve(external_node_path_default().dirname(from), specifier);
|
|
72
|
+
if (!target.startsWith(`${owner}${external_node_path_default().sep}`)) return;
|
|
73
|
+
for (const candidate of [
|
|
74
|
+
target,
|
|
75
|
+
`${target}.ts`,
|
|
76
|
+
`${target}.mts`
|
|
77
|
+
])try {
|
|
78
|
+
const real = external_node_fs_default().realpathSync(candidate);
|
|
79
|
+
if (!real.startsWith(`${owner}${external_node_path_default().sep}`) || external_node_fs_default().statSync(real).size > 1000000) return;
|
|
80
|
+
return {
|
|
81
|
+
id: real,
|
|
82
|
+
source: external_node_fs_default().readFileSync(real, 'utf8'),
|
|
83
|
+
resolveImport: resolve(real)
|
|
84
|
+
};
|
|
85
|
+
} catch {}
|
|
86
|
+
};
|
|
87
|
+
return resolve(external_node_path_default().join(directory, external_node_path_default().basename(filename)));
|
|
88
|
+
}
|
|
89
|
+
function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
90
|
+
const modules = new Map();
|
|
91
|
+
const owners = new Map();
|
|
92
|
+
const paths = new Map();
|
|
93
|
+
const mutated = new Set();
|
|
94
|
+
const load = (input)=>{
|
|
95
|
+
const existing = modules.get(input.id);
|
|
96
|
+
if (existing) return existing;
|
|
97
|
+
if (modules.size >= 64 || input.source.length > 1000000) throw invalidSource('API source budget exceeded');
|
|
98
|
+
let file;
|
|
99
|
+
try {
|
|
100
|
+
file = (0, parser_namespaceObject.parse)(input.source, {
|
|
101
|
+
sourceType: 'module',
|
|
102
|
+
sourceFilename: input.id,
|
|
103
|
+
plugins: [
|
|
104
|
+
"typescript"
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
if ('object' == typeof cause && null !== cause && 'code' in cause && 'BABEL_PARSER_SYNTAX_ERROR' === cause.code && 'reasonCode' in cause && 'string' == typeof cause.reasonCode) throw invalidSource('Invalid API syntax');
|
|
109
|
+
throw cause;
|
|
110
|
+
}
|
|
111
|
+
const module = {
|
|
112
|
+
...input,
|
|
113
|
+
file
|
|
114
|
+
};
|
|
115
|
+
modules.set(input.id, module);
|
|
116
|
+
const program = traverse_namespaceObject.NodePath.get({
|
|
117
|
+
hub: new SourceValidationHub(),
|
|
118
|
+
parentPath: void 0,
|
|
119
|
+
parent: file,
|
|
120
|
+
container: file,
|
|
121
|
+
key: 'program'
|
|
122
|
+
});
|
|
123
|
+
program.setContext();
|
|
124
|
+
traverse_default()(file, {
|
|
125
|
+
enter (nodePath) {
|
|
126
|
+
paths.set(nodePath.node, nodePath);
|
|
127
|
+
owners.set(nodePath.node, module);
|
|
128
|
+
if (nodePath.isAssignmentExpression() || nodePath.isUpdateExpression() || nodePath.isUnaryExpression({
|
|
129
|
+
operator: 'delete'
|
|
130
|
+
})) {
|
|
131
|
+
let target = nodePath.isAssignmentExpression() ? nodePath.node.left : nodePath.node.argument;
|
|
132
|
+
while(types_namespaceObject.isMemberExpression(target))target = target.object;
|
|
133
|
+
if (types_namespaceObject.isIdentifier(target)) {
|
|
134
|
+
const binding = nodePath.scope.getBinding(target.name);
|
|
135
|
+
if (binding) mutated.add(binding);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (nodePath.isTSModuleDeclaration() && types_namespaceObject.isIdentifier(nodePath.node.id)) {
|
|
139
|
+
const binding = nodePath.scope.getBinding(nodePath.node.id.name);
|
|
140
|
+
if (binding) mutated.add(binding);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, program.scope, void 0, program);
|
|
144
|
+
return module;
|
|
145
|
+
};
|
|
146
|
+
try {
|
|
147
|
+
const entry = load({
|
|
148
|
+
id: '/entry.ts',
|
|
149
|
+
source,
|
|
150
|
+
resolveImport
|
|
151
|
+
});
|
|
152
|
+
const unwrap = (node)=>{
|
|
153
|
+
while(types_namespaceObject.isParenthesizedExpression(node) || types_namespaceObject.isTSSatisfiesExpression(node) || types_namespaceObject.isTSAsExpression(node) || types_namespaceObject.isTSTypeAssertion(node) || types_namespaceObject.isTSNonNullExpression(node))node = node.expression;
|
|
154
|
+
return node;
|
|
155
|
+
};
|
|
156
|
+
const binding = (node)=>{
|
|
157
|
+
node = unwrap(node);
|
|
158
|
+
if (!types_namespaceObject.isIdentifier(node)) return;
|
|
159
|
+
const nodePath = paths.get(node);
|
|
160
|
+
if (!nodePath) throw new Error('Missing API lexical scope');
|
|
161
|
+
const value = nodePath.scope.getBinding(node.name);
|
|
162
|
+
return value?.constant && !mutated.has(value) ? value : void 0;
|
|
163
|
+
};
|
|
164
|
+
const declaration = (node)=>binding(node)?.path.node;
|
|
165
|
+
const imported = (node)=>{
|
|
166
|
+
const value = binding(node);
|
|
167
|
+
const decl = value?.path.node;
|
|
168
|
+
if (!decl || !types_namespaceObject.isImportSpecifier(decl) && !types_namespaceObject.isImportNamespaceSpecifier(decl) || types_namespaceObject.isImportSpecifier(decl) && 'type' === decl.importKind) return;
|
|
169
|
+
const statement = value.path.parent;
|
|
170
|
+
if (!types_namespaceObject.isImportDeclaration(statement) || 'type' === statement.importKind) return;
|
|
171
|
+
return {
|
|
172
|
+
namespace: types_namespaceObject.isImportNamespaceSpecifier(decl),
|
|
173
|
+
name: types_namespaceObject.isImportNamespaceSpecifier(decl) ? '*' : types_namespaceObject.isIdentifier(decl.imported) ? decl.imported.name : decl.imported.value,
|
|
174
|
+
specifier: statement.source.value
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
const namespaceSources = {
|
|
178
|
+
HttpApi: [
|
|
179
|
+
edge,
|
|
180
|
+
'effect/unstable/httpapi'
|
|
181
|
+
],
|
|
182
|
+
HttpApiBuilder: [
|
|
183
|
+
edge,
|
|
184
|
+
'effect/unstable/httpapi'
|
|
185
|
+
],
|
|
186
|
+
HttpRouter: [
|
|
187
|
+
edge,
|
|
188
|
+
'effect/unstable/http'
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
const native = (node, name, sources)=>{
|
|
192
|
+
const value = imported(node);
|
|
193
|
+
if (!value) return false;
|
|
194
|
+
const importedNameMatches = value.namespace ? value.specifier === `effect/${name}` : value.name === name && value.specifier !== `effect/${name}`;
|
|
195
|
+
return importedNameMatches && (sources ?? namespaceSources[name] ?? [
|
|
196
|
+
edge
|
|
197
|
+
]).includes(value.specifier);
|
|
198
|
+
};
|
|
199
|
+
const method = (node, namespace, member, sources)=>{
|
|
200
|
+
node = unwrap(node);
|
|
201
|
+
return types_namespaceObject.isMemberExpression(node) && !node.computed && types_namespaceObject.isIdentifier(node.property, {
|
|
202
|
+
name: member
|
|
203
|
+
}) && native(node.object, namespace, sources);
|
|
204
|
+
};
|
|
205
|
+
const initialized = (node)=>{
|
|
206
|
+
const value = binding(node);
|
|
207
|
+
return value?.kind === 'const' && types_namespaceObject.isVariableDeclarator(value.path.node) ? value.path.node.init ?? void 0 : void 0;
|
|
208
|
+
};
|
|
209
|
+
const moduleFor = (node)=>{
|
|
210
|
+
const value = imported(node);
|
|
211
|
+
if (!value) return;
|
|
212
|
+
const input = owners.get(node)?.resolveImport?.(value.specifier);
|
|
213
|
+
return input ? {
|
|
214
|
+
module: load(input),
|
|
215
|
+
name: value.name
|
|
216
|
+
} : void 0;
|
|
217
|
+
};
|
|
218
|
+
const exported = (module, name)=>{
|
|
219
|
+
for (const statement of module.file.program.body)if (types_namespaceObject.isExportNamedDeclaration(statement) && 'type' !== statement.exportKind) {
|
|
220
|
+
if (types_namespaceObject.isVariableDeclaration(statement.declaration)) {
|
|
221
|
+
for (const decl of statement.declaration.declarations)if (types_namespaceObject.isIdentifier(decl.id, {
|
|
222
|
+
name
|
|
223
|
+
})) return decl.id;
|
|
224
|
+
}
|
|
225
|
+
if (!statement.source) {
|
|
226
|
+
const specifier = statement.specifiers.find((element)=>types_namespaceObject.isExportSpecifier(element) && 'type' !== element.exportKind && (types_namespaceObject.isIdentifier(element.exported) ? element.exported.name : element.exported.value) === name);
|
|
227
|
+
if (types_namespaceObject.isExportSpecifier(specifier)) return specifier.local;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const externalValue = (node)=>{
|
|
232
|
+
const value = moduleFor(node);
|
|
233
|
+
return value ? exported(value.module, value.name) : void 0;
|
|
234
|
+
};
|
|
235
|
+
const apiIdentity = (node, seen = new Set())=>{
|
|
236
|
+
node = unwrap(node);
|
|
237
|
+
if (seen.has(node)) return;
|
|
238
|
+
seen.add(node);
|
|
239
|
+
const value = imported(node);
|
|
240
|
+
if (value) {
|
|
241
|
+
if (!value.specifier.startsWith('.')) return;
|
|
242
|
+
const module = moduleFor(node);
|
|
243
|
+
const exportedValue = module && exported(module.module, module.name);
|
|
244
|
+
if (exportedValue) return apiIdentity(exportedValue, seen);
|
|
245
|
+
if (module && /(?:^|\/)shared\/(?:api|rpc)\.ts$/u.test(value.specifier)) return `${module.module.id}:import:${value.name}`;
|
|
246
|
+
if (!owners.get(node)?.resolveImport && /(?:^|\/)shared\/(?:api|rpc)\.ts$/u.test(value.specifier)) return `${value.specifier}:${value.name}`;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const init = initialized(node);
|
|
250
|
+
if (init && types_namespaceObject.isIdentifier(unwrap(init))) return apiIdentity(init, seen);
|
|
251
|
+
const valueBinding = binding(node);
|
|
252
|
+
return valueBinding ? `${owners.get(valueBinding.path.node)?.id}:${valueBinding.identifier.start}` : void 0;
|
|
253
|
+
};
|
|
254
|
+
const properties = (node)=>{
|
|
255
|
+
node = unwrap(node);
|
|
256
|
+
if (!types_namespaceObject.isObjectExpression(node)) return;
|
|
257
|
+
const result = new Map();
|
|
258
|
+
for (const prop of node.properties){
|
|
259
|
+
if (!types_namespaceObject.isObjectProperty(prop) || prop.computed || !types_namespaceObject.isIdentifier(prop.key) || result.has(prop.key.name)) return;
|
|
260
|
+
result.set(prop.key.name, prop.value);
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
};
|
|
264
|
+
const active = new Set();
|
|
265
|
+
const guarded = (node, check)=>{
|
|
266
|
+
node = unwrap(node);
|
|
267
|
+
if (active.has(node) || active.size > 128) return false;
|
|
268
|
+
active.add(node);
|
|
269
|
+
try {
|
|
270
|
+
return check(node);
|
|
271
|
+
} finally{
|
|
272
|
+
active.delete(node);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const layerSources = [
|
|
276
|
+
edge,
|
|
277
|
+
'effect',
|
|
278
|
+
'effect/Layer'
|
|
279
|
+
];
|
|
280
|
+
const returned = (body)=>{
|
|
281
|
+
if (!types_namespaceObject.isBlockStatement(body)) return body;
|
|
282
|
+
const statements = body.body;
|
|
283
|
+
const last = statements.at(-1);
|
|
284
|
+
return statements.slice(0, -1).every((statement)=>types_namespaceObject.isVariableDeclaration(statement)) && types_namespaceObject.isReturnStatement(last) ? last.argument : void 0;
|
|
285
|
+
};
|
|
286
|
+
const pipe = (node, check)=>types_namespaceObject.isMemberExpression(node.callee) && !node.callee.computed && types_namespaceObject.isIdentifier(node.callee.property, {
|
|
287
|
+
name: 'pipe'
|
|
288
|
+
}) && check(node.callee.object) && node.arguments.every((argument)=>method(argument, 'Layer', 'orDie', layerSources) || types_namespaceObject.isCallExpression(argument) && method(argument.callee, 'Layer', 'provide', layerSources) && argument.arguments.length > 0);
|
|
289
|
+
const handled = (node)=>{
|
|
290
|
+
node = unwrap(node);
|
|
291
|
+
if (!types_namespaceObject.isArrowFunctionExpression(node) && !types_namespaceObject.isFunctionExpression(node)) return false;
|
|
292
|
+
const parameter = node.params[0];
|
|
293
|
+
if (!types_namespaceObject.isIdentifier(parameter)) return false;
|
|
294
|
+
const body = returned(node.body);
|
|
295
|
+
const chain = (value, count = 0)=>{
|
|
296
|
+
value = unwrap(value);
|
|
297
|
+
if (types_namespaceObject.isIdentifier(value)) return count > 0 && declaration(value) === parameter;
|
|
298
|
+
return types_namespaceObject.isCallExpression(value) && types_namespaceObject.isMemberExpression(value.callee) && !value.callee.computed && types_namespaceObject.isIdentifier(value.callee.property) && [
|
|
299
|
+
'handle',
|
|
300
|
+
'handleRaw'
|
|
301
|
+
].includes(value.callee.property.name) && 2 === value.arguments.length && chain(value.callee.object, count + 1);
|
|
302
|
+
};
|
|
303
|
+
return !!body && chain(body);
|
|
304
|
+
};
|
|
305
|
+
const handlers = (node, api)=>guarded(node, (node)=>{
|
|
306
|
+
const init = initialized(node) ?? externalValue(node);
|
|
307
|
+
if (init) return handlers(init, api);
|
|
308
|
+
if (!types_namespaceObject.isCallExpression(node)) return false;
|
|
309
|
+
if (pipe(node, (value)=>handlers(value, api))) return true;
|
|
310
|
+
if (method(node.callee, 'Layer', 'mergeAll', layerSources)) return node.arguments.length > 0 && node.arguments.every((argument)=>handlers(argument, api));
|
|
311
|
+
return method(node.callee, 'HttpApiBuilder', 'group') && 3 === node.arguments.length && apiIdentity(node.arguments[0]) === api && handled(node.arguments[2]);
|
|
312
|
+
});
|
|
313
|
+
const transport = (node)=>guarded(node, (node)=>{
|
|
314
|
+
const init = initialized(node);
|
|
315
|
+
if (init) return transport(init);
|
|
316
|
+
if (!types_namespaceObject.isCallExpression(node)) return method(node, 'Layer', 'empty', layerSources);
|
|
317
|
+
return pipe(node, transport) || method(node.callee, 'HttpRouter', 'cors') || method(node.callee, 'Layer', 'mergeAll', layerSources) && node.arguments.length > 0 && node.arguments.every(transport);
|
|
318
|
+
});
|
|
319
|
+
const directLayer = (node, api)=>guarded(node, (node)=>{
|
|
320
|
+
const init = initialized(node);
|
|
321
|
+
if (init) return directLayer(init, api);
|
|
322
|
+
if (!types_namespaceObject.isCallExpression(node) || !types_namespaceObject.isMemberExpression(node.callee) || node.callee.computed || !types_namespaceObject.isIdentifier(node.callee.property, {
|
|
323
|
+
name: 'pipe'
|
|
324
|
+
})) return false;
|
|
325
|
+
const base = unwrap(node.callee.object);
|
|
326
|
+
return types_namespaceObject.isCallExpression(base) && method(base.callee, 'HttpApiBuilder', 'layer') && 1 === base.arguments.length && apiIdentity(base.arguments[0]) === api && node.arguments.length > 0 && node.arguments.every((argument)=>types_namespaceObject.isCallExpression(argument) && method(argument.callee, 'Layer', 'provide', layerSources) && 1 === argument.arguments.length && handlers(argument.arguments[0], api));
|
|
327
|
+
});
|
|
328
|
+
const rpc = (values)=>{
|
|
329
|
+
const api = values.get('api');
|
|
330
|
+
const apiLayer = values.get('layer');
|
|
331
|
+
const apiInit = api && (initialized(api) ?? unwrap(api));
|
|
332
|
+
if (!apiInit || !types_namespaceObject.isCallExpression(apiInit) || !method(apiInit.callee, 'HttpApi', 'make') || 1 !== apiInit.arguments.length || !apiLayer || !transport(apiLayer)) return false;
|
|
333
|
+
const config = values.get('rpc');
|
|
334
|
+
const fields = config && properties(config);
|
|
335
|
+
const route = fields?.get('path');
|
|
336
|
+
const serialization = fields?.get('serialization');
|
|
337
|
+
if (!types_namespaceObject.isStringLiteral(route, {
|
|
338
|
+
value: '/rpc'
|
|
339
|
+
}) || !types_namespaceObject.isStringLiteral(serialization, {
|
|
340
|
+
value: 'json'
|
|
341
|
+
})) return false;
|
|
342
|
+
const group = fields?.get('group');
|
|
343
|
+
const layer = fields?.get('layer');
|
|
344
|
+
const value = group && imported(group);
|
|
345
|
+
if (!group || !layer || value?.specifier !== '../shared/rpc.ts') return false;
|
|
346
|
+
const groupValue = externalValue(group);
|
|
347
|
+
const groupInit = groupValue && initialized(groupValue);
|
|
348
|
+
if (!groupInit || !types_namespaceObject.isCallExpression(groupInit) || !method(groupInit.callee, 'RpcGroup', 'make', [
|
|
349
|
+
'effect/unstable/rpc',
|
|
350
|
+
'@modern-js/bff-effect/effect-client'
|
|
351
|
+
])) return false;
|
|
352
|
+
const layerInit = initialized(layer) ?? unwrap(layer);
|
|
353
|
+
if (!types_namespaceObject.isCallExpression(layerInit) || !types_namespaceObject.isMemberExpression(layerInit.callee) || layerInit.callee.computed || !types_namespaceObject.isIdentifier(layerInit.callee.property, {
|
|
354
|
+
name: 'toLayer'
|
|
355
|
+
}) || apiIdentity(layerInit.callee.object) !== apiIdentity(group) || 1 !== layerInit.arguments.length) return false;
|
|
356
|
+
const implementation = unwrap(layerInit.arguments[0]);
|
|
357
|
+
if (!types_namespaceObject.isCallExpression(implementation) || !types_namespaceObject.isMemberExpression(implementation.callee) || implementation.callee.computed || !types_namespaceObject.isIdentifier(implementation.callee.property, {
|
|
358
|
+
name: 'of'
|
|
359
|
+
}) || apiIdentity(implementation.callee.object) !== apiIdentity(group) || 1 !== implementation.arguments.length) return false;
|
|
360
|
+
const entries = properties(implementation.arguments[0]);
|
|
361
|
+
return !!entries?.size && [
|
|
362
|
+
...entries.values()
|
|
363
|
+
].every((value)=>types_namespaceObject.isArrowFunctionExpression(value) || types_namespaceObject.isFunctionExpression(value));
|
|
364
|
+
};
|
|
365
|
+
const runtime = (node)=>guarded(node, (node)=>{
|
|
366
|
+
const init = initialized(node);
|
|
367
|
+
if (init) return runtime(init);
|
|
368
|
+
if (!types_namespaceObject.isCallExpression(node)) return false;
|
|
369
|
+
const value = imported(node.callee);
|
|
370
|
+
if (value?.name === 'defineEffectBff' && [
|
|
371
|
+
edge,
|
|
372
|
+
nodeRuntime
|
|
373
|
+
].includes(value.specifier) || value?.name === 'assembleEffectBffRuntime' && sharedRuntime.test(value.specifier)) {
|
|
374
|
+
if (1 !== node.arguments.length) return false;
|
|
375
|
+
const values = properties(node.arguments[0]);
|
|
376
|
+
if (!values) return false;
|
|
377
|
+
if ('defineEffectBff' === value.name && values.has('rpc')) return rpc(values);
|
|
378
|
+
const api = values.get('api');
|
|
379
|
+
const apiBinding = api && imported(api);
|
|
380
|
+
if (!api || apiBinding?.specifier !== '../shared/api.ts') return false;
|
|
381
|
+
const identity = apiIdentity(api);
|
|
382
|
+
if (!identity) return false;
|
|
383
|
+
const layer = values.get('defineEffectBff' === value.name ? 'layer' : 'handlers');
|
|
384
|
+
const extra = values.get('transport');
|
|
385
|
+
return void 0 !== layer && ('defineEffectBff' === value.name ? directLayer(layer, identity) : handlers(layer, identity)) && (!extra || transport(extra));
|
|
386
|
+
}
|
|
387
|
+
const decl = declaration(node.callee);
|
|
388
|
+
const factory = decl && types_namespaceObject.isFunctionDeclaration(decl) ? decl : initialized(node.callee);
|
|
389
|
+
if (!factory || !types_namespaceObject.isFunctionDeclaration(factory) && !types_namespaceObject.isArrowFunctionExpression(factory) && !types_namespaceObject.isFunctionExpression(factory) || !factory.body) return false;
|
|
390
|
+
const result = returned(factory.body);
|
|
391
|
+
return !!result && runtime(result);
|
|
392
|
+
});
|
|
393
|
+
const root = entry.file.program.body.find((statement)=>types_namespaceObject.isExportDefaultDeclaration(statement));
|
|
394
|
+
return root && runtime(root.declaration) ? void 0 : failure;
|
|
395
|
+
} catch (cause) {
|
|
396
|
+
if ('object' == typeof cause && null !== cause && invalidSources.has(cause)) return failure;
|
|
397
|
+
const message = 'object' == typeof cause && null !== cause && 'message' in cause && 'string' == typeof cause.message ? cause.message : String(cause);
|
|
398
|
+
throw new Error(`Strict Effect API analysis failed: ${message}`, {
|
|
399
|
+
cause
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
exports.createEffectApiImportResolver = __webpack_exports__.createEffectApiImportResolver;
|
|
404
|
+
exports.strictEffectRuntimeTopologyViolation = __webpack_exports__.strictEffectRuntimeTopologyViolation;
|
|
405
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
406
|
+
"createEffectApiImportResolver",
|
|
407
|
+
"strictEffectRuntimeTopologyViolation"
|
|
408
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
409
|
+
Object.defineProperty(exports, '__esModule', {
|
|
410
|
+
value: true
|
|
411
|
+
});
|