@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5
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/microvertical-api-baseline.cjs +526 -0
- package/dist/cjs/microvertical-api-boundary.cjs +470 -0
- package/dist/cjs/microvertical-api-owner.cjs +217 -0
- package/dist/cjs/oxlint-plugin/rules/strict-effect-api-boundaries.cjs +1 -1
- package/dist/cjs/strict-effect-runtime.cjs +43 -19
- package/dist/esm/cli/microvertical-api-check.js +33 -0
- package/dist/esm/microvertical-api-baseline.js +474 -0
- package/dist/esm/microvertical-api-boundary.js +412 -0
- package/dist/esm/microvertical-api-owner.js +162 -0
- package/dist/esm/oxlint-plugin/rules/strict-effect-api-boundaries.js +1 -1
- package/dist/esm/strict-effect-runtime.js +43 -19
- package/dist/esm-node/cli/microvertical-api-check.js +34 -0
- package/dist/esm-node/microvertical-api-baseline.js +475 -0
- package/dist/esm-node/microvertical-api-boundary.js +413 -0
- package/dist/esm-node/microvertical-api-owner.js +163 -0
- package/dist/esm-node/oxlint-plugin/rules/strict-effect-api-boundaries.js +1 -1
- package/dist/esm-node/strict-effect-runtime.js +43 -19
- package/dist/types/cli/microvertical-api-check.d.ts +1 -0
- package/dist/types/microvertical-api-baseline.d.ts +23 -0
- package/dist/types/microvertical-api-boundary.d.ts +32 -0
- package/dist/types/microvertical-api-owner.d.ts +6 -0
- package/package.json +15 -2
|
@@ -4,7 +4,8 @@ import node_path from "node:path";
|
|
|
4
4
|
import { parse } from "@babel/parser";
|
|
5
5
|
import traverse, { Hub, NodePath } from "@babel/traverse";
|
|
6
6
|
import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
|
|
7
|
-
const edge = '@modern-js/
|
|
7
|
+
const edge = '@modern-js/bff-effect/effect-edge';
|
|
8
|
+
const nodeRuntime = '@modern-js/bff-effect/effect';
|
|
8
9
|
const sharedRuntime = /^@[^/]+\/shared-contracts\/server\/effect-bff-runtime$/u;
|
|
9
10
|
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.';
|
|
10
11
|
const invalidSources = new WeakSet();
|
|
@@ -120,23 +121,38 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
120
121
|
const imported = (node)=>{
|
|
121
122
|
const value = binding(node);
|
|
122
123
|
const decl = value?.path.node;
|
|
123
|
-
if (!decl || !__rspack_external__babel_types_6e8337dd.isImportSpecifier(decl) || 'type' === decl.importKind) return;
|
|
124
|
+
if (!decl || !__rspack_external__babel_types_6e8337dd.isImportSpecifier(decl) && !__rspack_external__babel_types_6e8337dd.isImportNamespaceSpecifier(decl) || __rspack_external__babel_types_6e8337dd.isImportSpecifier(decl) && 'type' === decl.importKind) return;
|
|
124
125
|
const statement = value.path.parent;
|
|
125
126
|
if (!__rspack_external__babel_types_6e8337dd.isImportDeclaration(statement) || 'type' === statement.importKind) return;
|
|
126
127
|
return {
|
|
127
|
-
|
|
128
|
+
namespace: __rspack_external__babel_types_6e8337dd.isImportNamespaceSpecifier(decl),
|
|
129
|
+
name: __rspack_external__babel_types_6e8337dd.isImportNamespaceSpecifier(decl) ? '*' : __rspack_external__babel_types_6e8337dd.isIdentifier(decl.imported) ? decl.imported.name : decl.imported.value,
|
|
128
130
|
specifier: statement.source.value
|
|
129
131
|
};
|
|
130
132
|
};
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
const namespaceSources = {
|
|
134
|
+
HttpApi: [
|
|
135
|
+
edge,
|
|
136
|
+
'effect/unstable/httpapi'
|
|
137
|
+
],
|
|
138
|
+
HttpApiBuilder: [
|
|
139
|
+
edge,
|
|
140
|
+
'effect/unstable/httpapi'
|
|
141
|
+
],
|
|
142
|
+
HttpRouter: [
|
|
143
|
+
edge,
|
|
144
|
+
'effect/unstable/http'
|
|
145
|
+
]
|
|
146
|
+
};
|
|
147
|
+
const native = (node, name, sources)=>{
|
|
134
148
|
const value = imported(node);
|
|
135
|
-
|
|
149
|
+
if (!value) return false;
|
|
150
|
+
const importedNameMatches = value.namespace ? value.specifier === `effect/${name}` : value.name === name && value.specifier !== `effect/${name}`;
|
|
151
|
+
return importedNameMatches && (sources ?? namespaceSources[name] ?? [
|
|
152
|
+
edge
|
|
153
|
+
]).includes(value.specifier);
|
|
136
154
|
};
|
|
137
|
-
const method = (node, namespace, member, sources
|
|
138
|
-
edge
|
|
139
|
-
])=>{
|
|
155
|
+
const method = (node, namespace, member, sources)=>{
|
|
140
156
|
node = unwrap(node);
|
|
141
157
|
return __rspack_external__babel_types_6e8337dd.isMemberExpression(node) && !node.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(node.property, {
|
|
142
158
|
name: member
|
|
@@ -214,8 +230,15 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
214
230
|
};
|
|
215
231
|
const layerSources = [
|
|
216
232
|
edge,
|
|
217
|
-
'effect'
|
|
233
|
+
'effect',
|
|
234
|
+
'effect/Layer'
|
|
218
235
|
];
|
|
236
|
+
const returned = (body)=>{
|
|
237
|
+
if (!__rspack_external__babel_types_6e8337dd.isBlockStatement(body)) return body;
|
|
238
|
+
const statements = body.body;
|
|
239
|
+
const last = statements.at(-1);
|
|
240
|
+
return statements.slice(0, -1).every((statement)=>__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement)) && __rspack_external__babel_types_6e8337dd.isReturnStatement(last) ? last.argument : void 0;
|
|
241
|
+
};
|
|
219
242
|
const pipe = (node, check)=>__rspack_external__babel_types_6e8337dd.isMemberExpression(node.callee) && !node.callee.computed && __rspack_external__babel_types_6e8337dd.isIdentifier(node.callee.property, {
|
|
220
243
|
name: 'pipe'
|
|
221
244
|
}) && check(node.callee.object) && node.arguments.every((argument)=>method(argument, 'Layer', 'orDie', layerSources) || __rspack_external__babel_types_6e8337dd.isCallExpression(argument) && method(argument.callee, 'Layer', 'provide', layerSources) && argument.arguments.length > 0);
|
|
@@ -224,7 +247,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
224
247
|
if (!__rspack_external__babel_types_6e8337dd.isArrowFunctionExpression(node) && !__rspack_external__babel_types_6e8337dd.isFunctionExpression(node)) return false;
|
|
225
248
|
const parameter = node.params[0];
|
|
226
249
|
if (!__rspack_external__babel_types_6e8337dd.isIdentifier(parameter)) return false;
|
|
227
|
-
const body =
|
|
250
|
+
const body = returned(node.body);
|
|
228
251
|
const chain = (value, count = 0)=>{
|
|
229
252
|
value = unwrap(value);
|
|
230
253
|
if (__rspack_external__babel_types_6e8337dd.isIdentifier(value)) return count > 0 && declaration(value) === parameter;
|
|
@@ -280,7 +303,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
280
303
|
const groupInit = groupValue && initialized(groupValue);
|
|
281
304
|
if (!groupInit || !__rspack_external__babel_types_6e8337dd.isCallExpression(groupInit) || !method(groupInit.callee, 'RpcGroup', 'make', [
|
|
282
305
|
'effect/unstable/rpc',
|
|
283
|
-
'@modern-js/
|
|
306
|
+
'@modern-js/bff-effect/effect-client'
|
|
284
307
|
])) return false;
|
|
285
308
|
const layerInit = initialized(layer) ?? unwrap(layer);
|
|
286
309
|
if (!__rspack_external__babel_types_6e8337dd.isCallExpression(layerInit) || !__rspack_external__babel_types_6e8337dd.isMemberExpression(layerInit.callee) || layerInit.callee.computed || !__rspack_external__babel_types_6e8337dd.isIdentifier(layerInit.callee.property, {
|
|
@@ -300,7 +323,10 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
300
323
|
if (init) return runtime(init);
|
|
301
324
|
if (!__rspack_external__babel_types_6e8337dd.isCallExpression(node)) return false;
|
|
302
325
|
const value = imported(node.callee);
|
|
303
|
-
if (value?.name === 'defineEffectBff' &&
|
|
326
|
+
if (value?.name === 'defineEffectBff' && [
|
|
327
|
+
edge,
|
|
328
|
+
nodeRuntime
|
|
329
|
+
].includes(value.specifier) || value?.name === 'assembleEffectBffRuntime' && sharedRuntime.test(value.specifier)) {
|
|
304
330
|
if (1 !== node.arguments.length) return false;
|
|
305
331
|
const values = properties(node.arguments[0]);
|
|
306
332
|
if (!values) return false;
|
|
@@ -316,11 +342,9 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
316
342
|
}
|
|
317
343
|
const decl = declaration(node.callee);
|
|
318
344
|
const factory = decl && __rspack_external__babel_types_6e8337dd.isFunctionDeclaration(decl) ? decl : initialized(node.callee);
|
|
319
|
-
if (!factory || !__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(factory) && !__rspack_external__babel_types_6e8337dd.isArrowFunctionExpression(factory) && !__rspack_external__babel_types_6e8337dd.isFunctionExpression(factory) ||
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const last = statements.at(-1);
|
|
323
|
-
return statements.slice(0, -1).every((statement)=>__rspack_external__babel_types_6e8337dd.isVariableDeclaration(statement)) && !!last && __rspack_external__babel_types_6e8337dd.isReturnStatement(last) && !!last.argument && runtime(last.argument);
|
|
345
|
+
if (!factory || !__rspack_external__babel_types_6e8337dd.isFunctionDeclaration(factory) && !__rspack_external__babel_types_6e8337dd.isArrowFunctionExpression(factory) && !__rspack_external__babel_types_6e8337dd.isFunctionExpression(factory) || !factory.body) return false;
|
|
346
|
+
const result = returned(factory.body);
|
|
347
|
+
return !!result && runtime(result);
|
|
324
348
|
});
|
|
325
349
|
const root = entry.file.program.body.find((statement)=>__rspack_external__babel_types_6e8337dd.isExportDefaultDeclaration(statement));
|
|
326
350
|
return root && runtime(root.declaration) ? void 0 : failure;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runMicroVerticalApiCheckCli(args?: string[], filesOnly?: boolean): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface MicroVerticalApiBaselineExpectation {
|
|
2
|
+
/** Optional override for operation IDs outside reachable endpoint naming conventions. */
|
|
3
|
+
readonly operationPaths?: Readonly<Record<string, string>>;
|
|
4
|
+
readonly additionalPaths: Readonly<Record<string, string>>;
|
|
5
|
+
readonly apiPrefix: string;
|
|
6
|
+
readonly basePath: string;
|
|
7
|
+
readonly effectClientPackage: string;
|
|
8
|
+
readonly ownerId: string;
|
|
9
|
+
readonly readinessPath: string;
|
|
10
|
+
readonly baselinePackage: '@modern-js/bff-effect/microvertical-api';
|
|
11
|
+
readonly baselinePackageDirectory: string;
|
|
12
|
+
}
|
|
13
|
+
export interface MicroVerticalTopologyEntry {
|
|
14
|
+
readonly api?: {
|
|
15
|
+
readonly readiness?: {
|
|
16
|
+
readonly endpoint?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly path?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const configuredMicroVerticalApiStem: (verticalPath: string, verticals: readonly MicroVerticalTopologyEntry[]) => string | undefined;
|
|
23
|
+
export declare const microVerticalApiBaselineViolation: (stem: string, filePath: string, expectation: MicroVerticalApiBaselineExpectation) => string | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type { MicroVerticalApiBaselineExpectation, MicroVerticalTopologyEntry, } from './microvertical-api-baseline.js';
|
|
2
|
+
export { configuredMicroVerticalApiStem, microVerticalApiBaselineViolation, } from './microvertical-api-baseline.js';
|
|
3
|
+
export interface MicroVerticalConfiguredApp {
|
|
4
|
+
readonly path: string;
|
|
5
|
+
readonly id?: string;
|
|
6
|
+
readonly kind?: string;
|
|
7
|
+
readonly package?: string;
|
|
8
|
+
readonly surfaceProfile?: string;
|
|
9
|
+
readonly deliveryUnitKind?: string;
|
|
10
|
+
readonly api?: {
|
|
11
|
+
readonly stem?: string;
|
|
12
|
+
readonly prefix?: string;
|
|
13
|
+
readonly protocol?: 'rest' | 'rpc';
|
|
14
|
+
readonly operationPaths?: Readonly<Record<string, string>>;
|
|
15
|
+
readonly additionalPaths?: Readonly<Record<string, string>>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface MicroVerticalApiCheckOptions {
|
|
19
|
+
readonly workspaceRoot: string;
|
|
20
|
+
readonly configuredApps?: readonly MicroVerticalConfiguredApp[];
|
|
21
|
+
/** Explicit expected installed owner, useful for isolated/non-hoisted installations. */
|
|
22
|
+
readonly baselinePackageDirectory?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface MicroVerticalApiCheckResult {
|
|
25
|
+
readonly diagnostics: readonly string[];
|
|
26
|
+
readonly toolErrors: readonly string[];
|
|
27
|
+
readonly topologyFilesAnalyzed: number;
|
|
28
|
+
}
|
|
29
|
+
/** Full standalone check: source runtime topology is analyzed once per entry. */
|
|
30
|
+
export declare const checkMicroVerticalApiBoundaries: (options: MicroVerticalApiCheckOptions) => MicroVerticalApiCheckResult;
|
|
31
|
+
/** Consumer files/export/owner checks; caller must separately run runtime topology analysis. */
|
|
32
|
+
export declare const checkMicroVerticalApiConsumerFiles: (options: MicroVerticalApiCheckOptions) => MicroVerticalApiCheckResult;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MicroVerticalApiBaselineExpectation } from './microvertical-api-baseline.js';
|
|
2
|
+
export declare const baselinePackage = "@modern-js/bff-effect/microvertical-api";
|
|
3
|
+
/** Locate the installed package from this consumer's resolver, never this tool's installation. */
|
|
4
|
+
export declare function resolveBaselinePackageDirectory(fromFile: string): string;
|
|
5
|
+
/** Follow only public bindings and local barrels. Never examine schema implementation expressions. */
|
|
6
|
+
export declare function baselinePublicIdentityIsExact(fromFile: string, expectation: MicroVerticalApiBaselineExpectation): boolean;
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=20"
|
|
25
25
|
},
|
|
26
|
-
"version": "3.9.0-ultramodern.
|
|
26
|
+
"version": "3.9.0-ultramodern.5",
|
|
27
27
|
"types": "./dist/types/index.d.ts",
|
|
28
28
|
"main": "./dist/esm-node/index.js",
|
|
29
29
|
"typesVersions": {
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
],
|
|
34
34
|
"oxlint-plugin": [
|
|
35
35
|
"./dist/types/oxlint-plugin.d.ts"
|
|
36
|
+
],
|
|
37
|
+
"microvertical-api-boundary": [
|
|
38
|
+
"./dist/types/microvertical-api-boundary.d.ts"
|
|
36
39
|
]
|
|
37
40
|
}
|
|
38
41
|
},
|
|
@@ -57,10 +60,16 @@
|
|
|
57
60
|
"require": "./dist/cjs/oxlint-plugin.cjs"
|
|
58
61
|
},
|
|
59
62
|
"default": "./dist/esm-node/oxlint-plugin.js"
|
|
63
|
+
},
|
|
64
|
+
"./microvertical-api-boundary": {
|
|
65
|
+
"types": "./dist/types/microvertical-api-boundary.d.ts",
|
|
66
|
+
"import": "./dist/esm-node/microvertical-api-boundary.js",
|
|
67
|
+
"require": "./dist/cjs/microvertical-api-boundary.cjs"
|
|
60
68
|
}
|
|
61
69
|
},
|
|
62
70
|
"files": [
|
|
63
|
-
"dist"
|
|
71
|
+
"dist",
|
|
72
|
+
"bin"
|
|
64
73
|
],
|
|
65
74
|
"dependencies": {
|
|
66
75
|
"@babel/parser": "^8.0.4",
|
|
@@ -79,6 +88,10 @@
|
|
|
79
88
|
"access": "public"
|
|
80
89
|
},
|
|
81
90
|
"modern:source": "./src/index.ts",
|
|
91
|
+
"bin": {
|
|
92
|
+
"modern-api-check": "bin/modern-api-check.mjs",
|
|
93
|
+
"modern-api-check-files": "bin/modern-api-check-files.mjs"
|
|
94
|
+
},
|
|
82
95
|
"scripts": {
|
|
83
96
|
"build": "rm -rf dist && rslib build -c rslib.config.mts",
|
|
84
97
|
"dev": "rslib build -c rslib.config.mts -w",
|