@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6
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 +33 -13
- 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 +33 -13
- 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 +33 -13
- 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
|
@@ -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
|
+
});
|
|
@@ -50,7 +50,7 @@ const createStrictEffectApiBoundariesRule = ()=>({
|
|
|
50
50
|
node,
|
|
51
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.'
|
|
52
52
|
});
|
|
53
|
-
(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.');
|
|
54
54
|
(0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bruntimeFramework\s*(?::|=)\s*['"]hono['"]/u, 'UltraModern API apps must use bff.runtimeFramework: "effect".');
|
|
55
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.');
|
|
56
56
|
(0, external_ast_cjs_namespaceObject.reportProgramPattern)(context, node, source, /\bstrictEffectApproach\s*(?::|=)\s*false\b/u, 'UltraModern API apps must keep strictEffectApproach enabled.');
|
|
@@ -48,7 +48,8 @@ const parser_namespaceObject = require("@babel/parser");
|
|
|
48
48
|
const traverse_namespaceObject = require("@babel/traverse");
|
|
49
49
|
var traverse_default = /*#__PURE__*/ __webpack_require__.n(traverse_namespaceObject);
|
|
50
50
|
const types_namespaceObject = require("@babel/types");
|
|
51
|
-
const edge = '@modern-js/
|
|
51
|
+
const edge = '@modern-js/bff-effect/effect-edge';
|
|
52
|
+
const nodeRuntime = '@modern-js/bff-effect/effect';
|
|
52
53
|
const sharedRuntime = /^@[^/]+\/shared-contracts\/server\/effect-bff-runtime$/u;
|
|
53
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.';
|
|
54
55
|
const invalidSources = new WeakSet();
|
|
@@ -164,23 +165,38 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
164
165
|
const imported = (node)=>{
|
|
165
166
|
const value = binding(node);
|
|
166
167
|
const decl = value?.path.node;
|
|
167
|
-
if (!decl || !types_namespaceObject.isImportSpecifier(decl) || 'type' === decl.importKind) return;
|
|
168
|
+
if (!decl || !types_namespaceObject.isImportSpecifier(decl) && !types_namespaceObject.isImportNamespaceSpecifier(decl) || types_namespaceObject.isImportSpecifier(decl) && 'type' === decl.importKind) return;
|
|
168
169
|
const statement = value.path.parent;
|
|
169
170
|
if (!types_namespaceObject.isImportDeclaration(statement) || 'type' === statement.importKind) return;
|
|
170
171
|
return {
|
|
171
|
-
|
|
172
|
+
namespace: types_namespaceObject.isImportNamespaceSpecifier(decl),
|
|
173
|
+
name: types_namespaceObject.isImportNamespaceSpecifier(decl) ? '*' : types_namespaceObject.isIdentifier(decl.imported) ? decl.imported.name : decl.imported.value,
|
|
172
174
|
specifier: statement.source.value
|
|
173
175
|
};
|
|
174
176
|
};
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
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)=>{
|
|
178
192
|
const value = imported(node);
|
|
179
|
-
|
|
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);
|
|
180
198
|
};
|
|
181
|
-
const method = (node, namespace, member, sources
|
|
182
|
-
edge
|
|
183
|
-
])=>{
|
|
199
|
+
const method = (node, namespace, member, sources)=>{
|
|
184
200
|
node = unwrap(node);
|
|
185
201
|
return types_namespaceObject.isMemberExpression(node) && !node.computed && types_namespaceObject.isIdentifier(node.property, {
|
|
186
202
|
name: member
|
|
@@ -258,7 +274,8 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
258
274
|
};
|
|
259
275
|
const layerSources = [
|
|
260
276
|
edge,
|
|
261
|
-
'effect'
|
|
277
|
+
'effect',
|
|
278
|
+
'effect/Layer'
|
|
262
279
|
];
|
|
263
280
|
const returned = (body)=>{
|
|
264
281
|
if (!types_namespaceObject.isBlockStatement(body)) return body;
|
|
@@ -330,7 +347,7 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
330
347
|
const groupInit = groupValue && initialized(groupValue);
|
|
331
348
|
if (!groupInit || !types_namespaceObject.isCallExpression(groupInit) || !method(groupInit.callee, 'RpcGroup', 'make', [
|
|
332
349
|
'effect/unstable/rpc',
|
|
333
|
-
'@modern-js/
|
|
350
|
+
'@modern-js/bff-effect/effect-client'
|
|
334
351
|
])) return false;
|
|
335
352
|
const layerInit = initialized(layer) ?? unwrap(layer);
|
|
336
353
|
if (!types_namespaceObject.isCallExpression(layerInit) || !types_namespaceObject.isMemberExpression(layerInit.callee) || layerInit.callee.computed || !types_namespaceObject.isIdentifier(layerInit.callee.property, {
|
|
@@ -350,7 +367,10 @@ function strictEffectRuntimeTopologyViolation(source, resolveImport) {
|
|
|
350
367
|
if (init) return runtime(init);
|
|
351
368
|
if (!types_namespaceObject.isCallExpression(node)) return false;
|
|
352
369
|
const value = imported(node.callee);
|
|
353
|
-
if (value?.name === 'defineEffectBff' &&
|
|
370
|
+
if (value?.name === 'defineEffectBff' && [
|
|
371
|
+
edge,
|
|
372
|
+
nodeRuntime
|
|
373
|
+
].includes(value.specifier) || value?.name === 'assembleEffectBffRuntime' && sharedRuntime.test(value.specifier)) {
|
|
354
374
|
if (1 !== node.arguments.length) return false;
|
|
355
375
|
const values = properties(node.arguments[0]);
|
|
356
376
|
if (!values) return false;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { checkMicroVerticalApiBoundaries, checkMicroVerticalApiConsumerFiles } from "../microvertical-api-boundary.js";
|
|
2
|
+
function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOnly = false) {
|
|
3
|
+
let workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
|
|
4
|
+
let baselinePackageDirectory;
|
|
5
|
+
for(let index = 0; index < args.length; index += 1){
|
|
6
|
+
const argument = args[index];
|
|
7
|
+
if ('--help' === argument || '-h' === argument) {
|
|
8
|
+
console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>]\n${filesOnly ? 'Checks consumer files, contracts, package exports and owner identity. Does not analyze runtime topology; run the strict Effect source phase separately.' : 'Checks consumer files, contracts, package exports, owner identity and runtime topology once per API entry.'}\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
|
|
9
|
+
return 0;
|
|
10
|
+
}
|
|
11
|
+
if (![
|
|
12
|
+
'--workspace-root',
|
|
13
|
+
'--baseline-package-directory'
|
|
14
|
+
].includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
|
|
15
|
+
console.error(`Invalid argument: ${argument ?? ''}. Use --help.`);
|
|
16
|
+
return 2;
|
|
17
|
+
}
|
|
18
|
+
const value = args[++index];
|
|
19
|
+
if ('--workspace-root' === argument) workspaceRoot = value;
|
|
20
|
+
else baselinePackageDirectory = value;
|
|
21
|
+
}
|
|
22
|
+
const result = (filesOnly ? checkMicroVerticalApiConsumerFiles : checkMicroVerticalApiBoundaries)({
|
|
23
|
+
workspaceRoot,
|
|
24
|
+
baselinePackageDirectory
|
|
25
|
+
});
|
|
26
|
+
for (const diagnostic of result.diagnostics)console.error(`API violation: ${diagnostic}`);
|
|
27
|
+
for (const error of result.toolErrors)console.error(`API tool error: ${error}`);
|
|
28
|
+
if (result.toolErrors.length) return 2;
|
|
29
|
+
if (result.diagnostics.length) return 1;
|
|
30
|
+
console.log(`UltraModern API ${filesOnly ? 'consumer files' : 'boundary'} check passed.`);
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
export { runMicroVerticalApiCheckCli };
|