@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.16 → 3.9.0-ultramodern.18
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 -1
- package/bin/modern-api-check.mjs +1 -1
- package/bin/modern-i18n-check.mjs +4 -0
- package/dist/cjs/cli/microvertical-api-check.cjs +36 -7
- package/dist/cjs/cli/workspace-source-check.cjs +90 -17
- package/dist/cjs/microvertical-api-baseline.cjs +24 -255
- package/dist/cjs/microvertical-api-boundary.cjs +25 -0
- package/dist/cjs/module-graph.cjs +321 -0
- package/dist/esm/cli/microvertical-api-check.js +26 -7
- package/dist/esm/cli/workspace-source-check.js +86 -13
- package/dist/esm/microvertical-api-baseline.js +17 -237
- package/dist/esm/microvertical-api-boundary.js +24 -2
- package/dist/esm/module-graph.js +260 -0
- package/dist/esm-node/cli/microvertical-api-check.js +26 -7
- package/dist/esm-node/cli/workspace-source-check.js +86 -13
- package/dist/esm-node/microvertical-api-baseline.js +17 -237
- package/dist/esm-node/microvertical-api-boundary.js +24 -2
- package/dist/esm-node/module-graph.js +261 -0
- package/dist/types/cli/microvertical-api-check.d.ts +1 -1
- package/dist/types/cli/workspace-source-check.d.ts +1 -1
- package/dist/types/microvertical-api-boundary.d.ts +14 -0
- package/dist/types/module-graph.d.ts +52 -0
- package/package.json +4 -3
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runMicroVerticalApiCheckCli } from '../dist/esm-node/cli/microvertical-api-check.js';
|
|
3
3
|
|
|
4
|
-
process.exitCode = runMicroVerticalApiCheckCli(
|
|
4
|
+
process.exitCode = await runMicroVerticalApiCheckCli(
|
|
5
|
+
process.argv.slice(2),
|
|
6
|
+
true,
|
|
7
|
+
);
|
package/bin/modern-api-check.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
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
|
+
})();
|
|
3
12
|
(()=>{
|
|
4
13
|
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
14
|
var define = (defs, kind)=>{
|
|
@@ -30,30 +39,50 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
39
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
40
|
runMicroVerticalApiCheckCli: ()=>runMicroVerticalApiCheckCli
|
|
32
41
|
});
|
|
42
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
43
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
44
|
+
const external_node_url_namespaceObject = require("node:url");
|
|
33
45
|
const external_microvertical_api_boundary_cjs_namespaceObject = require("../microvertical-api-boundary.cjs");
|
|
34
|
-
|
|
46
|
+
const flags = [
|
|
47
|
+
'--workspace-root',
|
|
48
|
+
'--baseline-package-directory',
|
|
49
|
+
'--rules'
|
|
50
|
+
];
|
|
51
|
+
async function loadContractRules(file) {
|
|
52
|
+
const rules = (await import((0, external_node_url_namespaceObject.pathToFileURL)(external_node_path_default().resolve(file)).href)).default;
|
|
53
|
+
if (!Array.isArray(rules) || !rules.every((rule)=>'function' == typeof rule)) throw new Error(`${file}: default export must be an array of contract rule functions, e.g. \`export default [myRule]\``);
|
|
54
|
+
return rules;
|
|
55
|
+
}
|
|
56
|
+
async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOnly = false) {
|
|
35
57
|
let workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
|
|
36
58
|
let baselinePackageDirectory;
|
|
59
|
+
const ruleFiles = [];
|
|
37
60
|
for(let index = 0; index < args.length; index += 1){
|
|
38
61
|
const argument = args[index];
|
|
39
62
|
if ('--help' === argument || '-h' === argument) {
|
|
40
|
-
console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>]
|
|
63
|
+
console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>] [--rules <module>]...\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.'}\n--rules loads a module whose default export is an array of contract rules; each rule receives the module graph of every API contract.\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
|
|
41
64
|
return 0;
|
|
42
65
|
}
|
|
43
|
-
if (![
|
|
44
|
-
'--workspace-root',
|
|
45
|
-
'--baseline-package-directory'
|
|
46
|
-
].includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
|
|
66
|
+
if (!flags.includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
|
|
47
67
|
console.error(`Invalid argument: ${argument ?? ''}. Use --help.`);
|
|
48
68
|
return 2;
|
|
49
69
|
}
|
|
50
70
|
const value = args[++index];
|
|
51
71
|
if ('--workspace-root' === argument) workspaceRoot = value;
|
|
72
|
+
else if ('--rules' === argument) ruleFiles.push(value);
|
|
52
73
|
else baselinePackageDirectory = value;
|
|
53
74
|
}
|
|
75
|
+
const contractRules = [];
|
|
76
|
+
for (const file of ruleFiles)try {
|
|
77
|
+
contractRules.push(...await loadContractRules(file));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`API tool error: --rules ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
return 2;
|
|
81
|
+
}
|
|
54
82
|
const result = (filesOnly ? external_microvertical_api_boundary_cjs_namespaceObject.checkMicroVerticalApiConsumerFiles : external_microvertical_api_boundary_cjs_namespaceObject.checkMicroVerticalApiBoundaries)({
|
|
55
83
|
workspaceRoot,
|
|
56
|
-
baselinePackageDirectory
|
|
84
|
+
baselinePackageDirectory,
|
|
85
|
+
contractRules
|
|
57
86
|
});
|
|
58
87
|
for (const diagnostic of result.diagnostics)console.error(`API violation: ${diagnostic}`);
|
|
59
88
|
for (const error of result.toolErrors)console.error(`API tool error: ${error}`);
|
|
@@ -37,8 +37,8 @@ var __webpack_require__ = {};
|
|
|
37
37
|
var __webpack_exports__ = {};
|
|
38
38
|
__webpack_require__.r(__webpack_exports__);
|
|
39
39
|
__webpack_require__.d(__webpack_exports__, {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
runWorkspaceSourceCheck: ()=>runWorkspaceSourceCheck,
|
|
41
|
+
runWorkspaceSourceCheckCli: ()=>runWorkspaceSourceCheckCli
|
|
42
42
|
});
|
|
43
43
|
const external_node_fs_namespaceObject = require("node:fs");
|
|
44
44
|
var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
|
|
@@ -50,6 +50,11 @@ const DEFAULT_LOCALES = [
|
|
|
50
50
|
'en',
|
|
51
51
|
'cs'
|
|
52
52
|
];
|
|
53
|
+
const DEFAULT_SOURCE_ROOTS = [
|
|
54
|
+
'apps',
|
|
55
|
+
'verticals',
|
|
56
|
+
'packages'
|
|
57
|
+
];
|
|
53
58
|
const ignoredDirectories = new Set([
|
|
54
59
|
'.modern',
|
|
55
60
|
'.modernjs',
|
|
@@ -83,6 +88,8 @@ const createLocaleJsonMatcher = (locales)=>{
|
|
|
83
88
|
const resolvePluralCategories = (locale, overrides)=>overrides?.[locale] ?? new Intl.PluralRules(locale).resolvedOptions().pluralCategories;
|
|
84
89
|
const readText = (filePath)=>external_node_fs_default().readFileSync(filePath, 'utf-8');
|
|
85
90
|
const localeImportPattern = (locale)=>new RegExp(`import\\s+(?:\\*\\s+as\\s+)?[A-Za-z_$][\\w$]*\\s+from\\s+['"]\\.\\./locales/${escapeRegExp(locale)}/[^'"]+\\.json['"]`, 'u');
|
|
91
|
+
class SourceViolation extends Error {
|
|
92
|
+
}
|
|
86
93
|
const checkRuntimeResources = (root, filePath, text, locales)=>{
|
|
87
94
|
const relative = relativePath(root, filePath);
|
|
88
95
|
if (!relative.endsWith('/src/modern.runtime.ts')) return;
|
|
@@ -90,7 +97,7 @@ const checkRuntimeResources = (root, filePath, text, locales)=>{
|
|
|
90
97
|
const registersResources = /initOptions\s*:\s*\{[\s\S]*?\bresources\s*[,:}]/u.test(text);
|
|
91
98
|
if (missingLocales.length > 0 || !registersResources) {
|
|
92
99
|
const detail = missingLocales.length > 0 ? `missing locale JSON imports for: ${missingLocales.join(', ')}` : 'initOptions does not register a `resources` entry';
|
|
93
|
-
throw new
|
|
100
|
+
throw new SourceViolation(`${relative} must register locale JSON resources in modern.runtime.ts so Worker SSR and hydration use the same first-render translations (${detail}).`);
|
|
94
101
|
}
|
|
95
102
|
};
|
|
96
103
|
const visitLocaleKeys = (value, visitor, pathParts = [])=>{
|
|
@@ -110,7 +117,7 @@ const checkPluralResources = (root, filePath, json, requiredSuffixes, pluralSuff
|
|
|
110
117
|
visitLocaleKeys(json, (key, value, pathParts)=>{
|
|
111
118
|
if ('string' != typeof value || !value.includes('{{count}}')) return;
|
|
112
119
|
const suffixMatch = key.match(pluralSuffixPattern);
|
|
113
|
-
if (!suffixMatch) throw new
|
|
120
|
+
if (!suffixMatch) throw new SourceViolation(`${relative} key ${pathParts.join('.')} contains {{count}} but is not plural-suffixed.`);
|
|
114
121
|
const [, base = '', suffix = ''] = suffixMatch;
|
|
115
122
|
const parentPath = pathParts.slice(0, -1).join('.');
|
|
116
123
|
const groupKey = `${parentPath}.${base}`;
|
|
@@ -118,7 +125,7 @@ const checkPluralResources = (root, filePath, json, requiredSuffixes, pluralSuff
|
|
|
118
125
|
existing.add(suffix);
|
|
119
126
|
groups.set(groupKey, existing);
|
|
120
127
|
});
|
|
121
|
-
for (const [group, suffixes] of groups)for (const suffix of requiredSuffixes)if (!suffixes.has(suffix)) throw new
|
|
128
|
+
for (const [group, suffixes] of groups)for (const suffix of requiredSuffixes)if (!suffixes.has(suffix)) throw new SourceViolation(`${relative} plural group ${group} is missing _${suffix}.`);
|
|
122
129
|
};
|
|
123
130
|
const runRuntimeAndLocaleResourceChecks = (root, sourceRoots, locales, pluralCategories)=>{
|
|
124
131
|
if (0 === locales.length) return;
|
|
@@ -140,11 +147,7 @@ const runRuntimeAndLocaleResourceChecks = (root, sourceRoots, locales, pluralCat
|
|
|
140
147
|
checkPluralResources(root, filePath, JSON.parse(readText(filePath)), localeCategories.get(language) ?? [], pluralSuffixPattern);
|
|
141
148
|
}
|
|
142
149
|
};
|
|
143
|
-
const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots =
|
|
144
|
-
'apps',
|
|
145
|
-
'verticals',
|
|
146
|
-
'packages'
|
|
147
|
-
], locales = DEFAULT_LOCALES, pluralCategories } = {})=>{
|
|
150
|
+
const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots = DEFAULT_SOURCE_ROOTS, locales = DEFAULT_LOCALES, pluralCategories } = {})=>{
|
|
148
151
|
const oxlintResult = (0, external_oxlint_cjs_namespaceObject.runOxlintRules)({
|
|
149
152
|
cwd,
|
|
150
153
|
targets: sourceRoots,
|
|
@@ -177,20 +180,90 @@ const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots = [
|
|
|
177
180
|
try {
|
|
178
181
|
runRuntimeAndLocaleResourceChecks(cwd, sourceRoots, locales, pluralCategories);
|
|
179
182
|
} catch (error) {
|
|
180
|
-
console.error(error instanceof Error ? error.message :
|
|
181
|
-
return 1;
|
|
183
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
184
|
+
return error instanceof SourceViolation ? 1 : 2;
|
|
182
185
|
}
|
|
183
186
|
console.log(WORKSPACE_SOURCE_SUCCESS);
|
|
184
187
|
return 0;
|
|
185
188
|
};
|
|
186
|
-
const
|
|
187
|
-
|
|
189
|
+
const USAGE = `modern-i18n-check [--workspace-root <path>]
|
|
190
|
+
Runs the UltraModern i18n and boundary source checks for a workspace.
|
|
191
|
+
Workspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Options come from
|
|
192
|
+
package.json "modernjs.i18nCheck": { sourceRoots, locales, pluralCategories }.
|
|
193
|
+
Exit codes: 0 valid, 1 source violation, 2 tool/configuration failure.`;
|
|
194
|
+
const isStringArray = (value)=>Array.isArray(value) && value.every((entry)=>'string' == typeof entry && entry.length > 0);
|
|
195
|
+
const isLocaleArray = (value)=>{
|
|
196
|
+
if (!isStringArray(value)) return false;
|
|
197
|
+
try {
|
|
198
|
+
Intl.getCanonicalLocales(value);
|
|
199
|
+
return true;
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
205
|
+
const isWorkspaceDirectory = (root, entry)=>{
|
|
206
|
+
if (external_node_path_default().isAbsolute(entry)) return false;
|
|
207
|
+
const target = external_node_path_default().resolve(root, entry);
|
|
208
|
+
if (!external_node_fs_default().existsSync(target) || !external_node_fs_default().statSync(target).isDirectory()) return false;
|
|
209
|
+
const relative = external_node_path_default().relative(external_node_fs_default().realpathSync(root), external_node_fs_default().realpathSync(target));
|
|
210
|
+
return '' !== relative && '..' !== relative && !relative.startsWith(`..${external_node_path_default().sep}`) && !external_node_path_default().isAbsolute(relative);
|
|
211
|
+
};
|
|
212
|
+
const CLDR_PLURAL_CATEGORIES = new Set([
|
|
213
|
+
'zero',
|
|
214
|
+
'one',
|
|
215
|
+
'two',
|
|
216
|
+
'few',
|
|
217
|
+
'many',
|
|
218
|
+
'other'
|
|
219
|
+
]);
|
|
220
|
+
const isPluralCategories = (value)=>isRecord(value) && Object.values(value).every((categories)=>isStringArray(categories) && categories.includes('other') && categories.every((category)=>CLDR_PLURAL_CATEGORIES.has(category)));
|
|
221
|
+
const readWorkspaceCheckOptions = (root)=>{
|
|
222
|
+
const manifestPath = external_node_path_default().join(root, 'package.json');
|
|
223
|
+
const modernjs = JSON.parse(readText(manifestPath)).modernjs;
|
|
224
|
+
const config = isRecord(modernjs) && Object.hasOwn(modernjs, 'i18nCheck') ? modernjs.i18nCheck : {};
|
|
225
|
+
const invalid = (field, expected)=>new Error(`${manifestPath} "modernjs.i18nCheck${field}" must be ${expected}.`);
|
|
226
|
+
if (!isRecord(config)) throw invalid('', 'an object');
|
|
227
|
+
const { locales, pluralCategories } = config;
|
|
228
|
+
if (void 0 !== locales && !isLocaleArray(locales)) throw invalid('.locales', 'an array of BCP 47 locale codes');
|
|
229
|
+
if (void 0 !== pluralCategories && !isPluralCategories(pluralCategories)) throw invalid('.pluralCategories', 'an object of locale -> CLDR plural categories including "other"');
|
|
230
|
+
const sourceRoots = Object.hasOwn(config, 'sourceRoots') ? config.sourceRoots : DEFAULT_SOURCE_ROOTS.filter((entry)=>isWorkspaceDirectory(root, entry));
|
|
231
|
+
if (!isStringArray(sourceRoots) || 0 === sourceRoots.length || !sourceRoots.every((entry)=>isWorkspaceDirectory(root, entry))) throw invalid('.sourceRoots', `a non-empty array of existing workspace-relative directories (default: whichever of ${DEFAULT_SOURCE_ROOTS.join(', ')} exist)`);
|
|
232
|
+
return {
|
|
233
|
+
cwd: root,
|
|
234
|
+
sourceRoots,
|
|
235
|
+
locales,
|
|
236
|
+
pluralCategories
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
const runWorkspaceSourceCheckCli = (args = process.argv.slice(2))=>{
|
|
240
|
+
let workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
|
|
241
|
+
for(let index = 0; index < args.length; index += 1){
|
|
242
|
+
const argument = args[index];
|
|
243
|
+
if ('--help' === argument || '-h' === argument) {
|
|
244
|
+
console.log(USAGE);
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
const value = args[index + 1];
|
|
248
|
+
if ('--workspace-root' !== argument || !value || value.startsWith('--')) {
|
|
249
|
+
console.error(`Invalid argument: ${argument ?? ''}. Use --help.`);
|
|
250
|
+
return 2;
|
|
251
|
+
}
|
|
252
|
+
workspaceRoot = value;
|
|
253
|
+
index += 1;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
return runWorkspaceSourceCheck(readWorkspaceCheckOptions(external_node_path_default().resolve(workspaceRoot)));
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
259
|
+
return 2;
|
|
260
|
+
}
|
|
188
261
|
};
|
|
189
|
-
exports.main = __webpack_exports__.main;
|
|
190
262
|
exports.runWorkspaceSourceCheck = __webpack_exports__.runWorkspaceSourceCheck;
|
|
263
|
+
exports.runWorkspaceSourceCheckCli = __webpack_exports__.runWorkspaceSourceCheckCli;
|
|
191
264
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
192
|
-
"
|
|
193
|
-
"
|
|
265
|
+
"runWorkspaceSourceCheck",
|
|
266
|
+
"runWorkspaceSourceCheckCli"
|
|
194
267
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
195
268
|
Object.defineProperty(exports, '__esModule', {
|
|
196
269
|
value: true
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
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
3
|
(()=>{
|
|
13
4
|
__webpack_require__.d = (exports1, getters, values)=>{
|
|
14
5
|
var define = (defs, kind)=>{
|
|
@@ -40,40 +31,16 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
40
31
|
configuredMicroVerticalApiStem: ()=>configuredMicroVerticalApiStem,
|
|
41
32
|
microVerticalApiBaselineViolation: ()=>microVerticalApiBaselineViolation
|
|
42
33
|
});
|
|
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_module_namespaceObject = require("node:module");
|
|
46
|
-
const external_node_path_namespaceObject = require("node:path");
|
|
47
|
-
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
48
34
|
const types_namespaceObject = require("@babel/types");
|
|
49
|
-
const external_resolve_exports_namespaceObject = require("resolve.exports");
|
|
50
35
|
const external_microvertical_api_owner_cjs_namespaceObject = require("./microvertical-api-owner.cjs");
|
|
36
|
+
const external_module_graph_cjs_namespaceObject = require("./module-graph.cjs");
|
|
51
37
|
const external_source_analysis_cjs_namespaceObject = require("./source-analysis.cjs");
|
|
52
|
-
function parseConsumer(filePath) {
|
|
53
|
-
if (external_node_fs_default().statSync(filePath).size > 1000000) throw new Error(`${filePath}: consumer source exceeds 1 MB analysis budget`);
|
|
54
|
-
const file = (0, external_source_analysis_cjs_namespaceObject.parseSource)(external_node_fs_default().readFileSync(filePath, 'utf8'), filePath);
|
|
55
|
-
(0, external_source_analysis_cjs_namespaceObject.traverseSource)(file, {
|
|
56
|
-
AssignmentExpression (p) {
|
|
57
|
-
throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
|
|
58
|
-
},
|
|
59
|
-
UpdateExpression (p) {
|
|
60
|
-
throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError(`contract bindings must be immutable at ${p.node.start}`);
|
|
61
|
-
},
|
|
62
|
-
TSModuleDeclaration () {
|
|
63
|
-
throw new external_source_analysis_cjs_namespaceObject.SourceSyntaxError('contract bindings must not be merged with namespaces');
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
return file;
|
|
67
|
-
}
|
|
68
38
|
const camelCaseStem = (stem)=>stem.replaceAll(/-([a-z0-9])/gu, (_match, letter)=>letter.toUpperCase());
|
|
69
39
|
const pascalCaseStem = (stem)=>{
|
|
70
40
|
const camelStem = camelCaseStem(stem);
|
|
71
41
|
return `${camelStem.slice(0, 1).toUpperCase()}${camelStem.slice(1)}`;
|
|
72
42
|
};
|
|
73
43
|
const identifierName = (node)=>void 0 !== node && types_namespaceObject.isIdentifier(node) ? node.name : void 0;
|
|
74
|
-
const propertyName = (node)=>{
|
|
75
|
-
if (void 0 !== node && (types_namespaceObject.isIdentifier(node) || types_namespaceObject.isStringLiteral(node) || types_namespaceObject.isNumericLiteral(node))) return types_namespaceObject.isIdentifier(node) ? node.name : String(node.value);
|
|
76
|
-
};
|
|
77
44
|
const accessPath = (node)=>{
|
|
78
45
|
if (types_namespaceObject.isIdentifier(node)) return [
|
|
79
46
|
node.name
|
|
@@ -102,201 +69,6 @@ const callExpression = (expression, callee)=>{
|
|
|
102
69
|
const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
|
|
103
70
|
return types_namespaceObject.isCallExpression(unwrapped) && isAccessPath(unwrapped.callee, callee) ? unwrapped : void 0;
|
|
104
71
|
};
|
|
105
|
-
const localConst = (sourceFile, name)=>{
|
|
106
|
-
for (const item of sourceFile.program.body){
|
|
107
|
-
const statement = types_namespaceObject.isExportNamedDeclaration(item) ? item.declaration : item;
|
|
108
|
-
if (!types_namespaceObject.isVariableDeclaration(statement) || 'const' !== statement.kind) continue;
|
|
109
|
-
const matches = statement.declarations.filter((declaration)=>types_namespaceObject.isIdentifier(declaration.id, {
|
|
110
|
-
name
|
|
111
|
-
}));
|
|
112
|
-
if (1 === matches.length) return matches[0];
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const exportedConst = (sourceFile, name)=>{
|
|
116
|
-
for (const statement of sourceFile.program.body)if (types_namespaceObject.isExportNamedDeclaration(statement) && 'type' !== statement.exportKind) {
|
|
117
|
-
if (types_namespaceObject.isVariableDeclaration(statement.declaration) && 'const' === statement.declaration.kind) {
|
|
118
|
-
const declaration = statement.declaration.declarations.find((value)=>types_namespaceObject.isIdentifier(value.id, {
|
|
119
|
-
name
|
|
120
|
-
}));
|
|
121
|
-
if (declaration) return declaration;
|
|
122
|
-
}
|
|
123
|
-
if (!statement.source && statement.specifiers.some((value)=>types_namespaceObject.isExportSpecifier(value) && 'type' !== value.exportKind && types_namespaceObject.isIdentifier(value.local, {
|
|
124
|
-
name
|
|
125
|
-
}) && types_namespaceObject.isIdentifier(value.exported, {
|
|
126
|
-
name
|
|
127
|
-
}))) return localConst(sourceFile, name);
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
const inside = (file, boundary)=>file === boundary || file.startsWith(`${boundary}${external_node_path_default().sep}`);
|
|
131
|
-
const containingWorkspace = (file)=>{
|
|
132
|
-
for(let directory = external_node_path_default().dirname(file);; directory = external_node_path_default().dirname(directory)){
|
|
133
|
-
if (external_node_fs_default().existsSync(external_node_path_default().join(directory, 'pnpm-workspace.yaml'))) return external_node_fs_default().realpathSync(directory);
|
|
134
|
-
if (directory === external_node_path_default().dirname(directory)) return;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
const sourceBoundary = (file, workspace)=>{
|
|
138
|
-
for(let directory = external_node_path_default().dirname(file);; directory = external_node_path_default().dirname(directory)){
|
|
139
|
-
if (external_node_fs_default().existsSync(external_node_path_default().join(directory, 'package.json')) && directory !== workspace) return external_node_fs_default().realpathSync(directory);
|
|
140
|
-
if (directory === workspace || directory === external_node_path_default().dirname(directory)) return external_node_fs_default().realpathSync(external_node_path_default().dirname(file));
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
const sourceFileAt = (target, boundary)=>{
|
|
144
|
-
const stem = target.replace(/\.(?:[cm]?[jt]sx?)$/u, '');
|
|
145
|
-
for (const candidate of [
|
|
146
|
-
`${stem}.ts`,
|
|
147
|
-
`${stem}.tsx`,
|
|
148
|
-
`${stem}.mts`,
|
|
149
|
-
`${stem}.cts`,
|
|
150
|
-
target,
|
|
151
|
-
external_node_path_default().join(target, 'index.ts'),
|
|
152
|
-
external_node_path_default().join(target, 'index.tsx')
|
|
153
|
-
])try {
|
|
154
|
-
const real = external_node_fs_default().realpathSync(candidate);
|
|
155
|
-
if (inside(real, boundary) && external_node_fs_default().statSync(real).isFile()) return real;
|
|
156
|
-
} catch {}
|
|
157
|
-
};
|
|
158
|
-
const packageEntry = (specifier)=>{
|
|
159
|
-
if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#')) return;
|
|
160
|
-
const parts = specifier.split('/');
|
|
161
|
-
const count = specifier.startsWith('@') ? 2 : 1;
|
|
162
|
-
if (parts.length < count || parts.slice(0, count).some((part)=>!part)) return;
|
|
163
|
-
return {
|
|
164
|
-
name: parts.slice(0, count).join('/'),
|
|
165
|
-
exportKey: parts.length === count ? '.' : `./${parts.slice(count).join('/')}`
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
const resolveModulePath = (module, specifier, workspace)=>{
|
|
169
|
-
if (/^\.\.?\//u.test(specifier)) {
|
|
170
|
-
const file = sourceFileAt(external_node_path_default().resolve(external_node_path_default().dirname(module.path), specifier), module.boundary);
|
|
171
|
-
return void 0 === file ? void 0 : {
|
|
172
|
-
path: file,
|
|
173
|
-
boundary: module.boundary
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
const entry = packageEntry(specifier);
|
|
177
|
-
if (!entry || !workspace) return;
|
|
178
|
-
const paths = (0, external_node_module_namespaceObject.createRequire)(module.path).resolve.paths(entry.name) ?? [];
|
|
179
|
-
for (const lookup of paths){
|
|
180
|
-
const directory = external_node_path_default().join(lookup, entry.name);
|
|
181
|
-
const manifest = external_node_path_default().join(directory, 'package.json');
|
|
182
|
-
if (!external_node_fs_default().existsSync(manifest)) continue;
|
|
183
|
-
const boundary = external_node_fs_default().realpathSync(directory);
|
|
184
|
-
if (!inside(boundary, workspace) || boundary.split(external_node_path_default().sep).includes('node_modules')) break;
|
|
185
|
-
const pkg = JSON.parse(external_node_fs_default().readFileSync(manifest, 'utf8'));
|
|
186
|
-
if (pkg.name !== entry.name || void 0 === pkg.exports) break;
|
|
187
|
-
let target;
|
|
188
|
-
try {
|
|
189
|
-
const targets = (0, external_resolve_exports_namespaceObject.exports)(pkg, entry.exportKey, {
|
|
190
|
-
conditions: [
|
|
191
|
-
'modern:source'
|
|
192
|
-
]
|
|
193
|
-
});
|
|
194
|
-
target = Array.isArray(targets) ? targets[0] : void 0;
|
|
195
|
-
} catch {
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
if (!target || !target.startsWith('./')) break;
|
|
199
|
-
const file = sourceFileAt(external_node_path_default().resolve(boundary, target), boundary);
|
|
200
|
-
return void 0 === file ? void 0 : {
|
|
201
|
-
path: file,
|
|
202
|
-
boundary
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const createModuleReader = (root)=>{
|
|
207
|
-
const modules = new Map([
|
|
208
|
-
[
|
|
209
|
-
root.path,
|
|
210
|
-
root
|
|
211
|
-
]
|
|
212
|
-
]);
|
|
213
|
-
return (filePath, boundary)=>{
|
|
214
|
-
if (modules.has(filePath)) return modules.get(filePath);
|
|
215
|
-
if (modules.size >= 256) return;
|
|
216
|
-
let module;
|
|
217
|
-
try {
|
|
218
|
-
module = {
|
|
219
|
-
file: parseConsumer(filePath),
|
|
220
|
-
path: filePath,
|
|
221
|
-
boundary
|
|
222
|
-
};
|
|
223
|
-
} catch {
|
|
224
|
-
module = void 0;
|
|
225
|
-
}
|
|
226
|
-
modules.set(filePath, module);
|
|
227
|
-
return module;
|
|
228
|
-
};
|
|
229
|
-
};
|
|
230
|
-
const importedBinding = (module, name)=>{
|
|
231
|
-
for (const statement of module.file.program.body)if (types_namespaceObject.isImportDeclaration(statement) && 'type' !== statement.importKind) {
|
|
232
|
-
for (const specifier of statement.specifiers)if (types_namespaceObject.isIdentifier(specifier.local, {
|
|
233
|
-
name
|
|
234
|
-
})) {
|
|
235
|
-
if (types_namespaceObject.isImportSpecifier(specifier) && 'type' !== specifier.importKind) return {
|
|
236
|
-
imported: propertyName(specifier.imported) ?? name,
|
|
237
|
-
specifier: statement.source.value
|
|
238
|
-
};
|
|
239
|
-
if (types_namespaceObject.isImportDefaultSpecifier(specifier)) return {
|
|
240
|
-
imported: 'default',
|
|
241
|
-
specifier: statement.source.value
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
const reexportedBinding = (module, name)=>{
|
|
247
|
-
for (const statement of module.file.program.body){
|
|
248
|
-
if (!types_namespaceObject.isExportNamedDeclaration(statement) || 'type' === statement.exportKind) continue;
|
|
249
|
-
const specifier = statement.specifiers.find((value)=>types_namespaceObject.isExportSpecifier(value) && 'type' !== value.exportKind && propertyName(value.exported) === name);
|
|
250
|
-
if (void 0 !== specifier && types_namespaceObject.isExportSpecifier(specifier)) return {
|
|
251
|
-
local: types_namespaceObject.isIdentifier(specifier.local) ? specifier.local.name : specifier.local.value,
|
|
252
|
-
...statement.source ? {
|
|
253
|
-
specifier: statement.source.value
|
|
254
|
-
} : {}
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
const defaultExport = (module)=>{
|
|
259
|
-
for (const statement of module.file.program.body){
|
|
260
|
-
if (!types_namespaceObject.isExportDefaultDeclaration(statement)) continue;
|
|
261
|
-
const declaration = statement.declaration;
|
|
262
|
-
if (types_namespaceObject.isIdentifier(declaration)) return declaration.name;
|
|
263
|
-
return types_namespaceObject.isExpression(declaration) ? declaration : void 0;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
const resolveBinding = (module, name, scope, readModule, workspace, seen)=>{
|
|
267
|
-
const key = `${scope}:${module.path}#${name}`;
|
|
268
|
-
if (seen.has(key) || seen.size >= 512) return;
|
|
269
|
-
seen.add(key);
|
|
270
|
-
const declaration = 'export' === scope ? exportedConst(module.file, name) : localConst(module.file, name);
|
|
271
|
-
if (declaration?.init) return {
|
|
272
|
-
expression: declaration.init,
|
|
273
|
-
module
|
|
274
|
-
};
|
|
275
|
-
const throughModule = (specifier, imported)=>{
|
|
276
|
-
const resolved = resolveModulePath(module, specifier, workspace);
|
|
277
|
-
const target = resolved && readModule(resolved.path, resolved.boundary);
|
|
278
|
-
return void 0 === target ? void 0 : resolveBinding(target, imported, 'export', readModule, workspace, seen);
|
|
279
|
-
};
|
|
280
|
-
if ('local' === scope) {
|
|
281
|
-
const imported = importedBinding(module, name);
|
|
282
|
-
return void 0 === imported ? void 0 : throughModule(imported.specifier, imported.imported);
|
|
283
|
-
}
|
|
284
|
-
if ('default' === name) {
|
|
285
|
-
const exported = defaultExport(module);
|
|
286
|
-
if ('string' == typeof exported) return resolveBinding(module, exported, 'local', readModule, workspace, seen);
|
|
287
|
-
return void 0 === exported ? void 0 : {
|
|
288
|
-
expression: exported,
|
|
289
|
-
module
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
const reexported = reexportedBinding(module, name);
|
|
293
|
-
if (void 0 !== reexported) return void 0 === reexported.specifier ? resolveBinding(module, reexported.local, 'local', readModule, workspace, seen) : throughModule(reexported.specifier, reexported.local);
|
|
294
|
-
for (const statement of module.file.program.body){
|
|
295
|
-
if (!types_namespaceObject.isExportAllDeclaration(statement) || 'type' === statement.exportKind) continue;
|
|
296
|
-
const resolved = throughModule(statement.source.value, name);
|
|
297
|
-
if (void 0 !== resolved) return resolved;
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
72
|
const objectLiteral = (expression)=>{
|
|
301
73
|
if (null == expression) return;
|
|
302
74
|
const unwrapped = (0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(expression);
|
|
@@ -306,7 +78,7 @@ const propertyAssignments = (properties)=>{
|
|
|
306
78
|
const assignments = new Map();
|
|
307
79
|
for (const property of properties){
|
|
308
80
|
if (!types_namespaceObject.isObjectProperty(property) || property.computed || property.shorthand) return;
|
|
309
|
-
const name = propertyName(property.key);
|
|
81
|
+
const name = (0, external_module_graph_cjs_namespaceObject.propertyName)(property.key);
|
|
310
82
|
if (void 0 === name || assignments.has(name)) return;
|
|
311
83
|
assignments.set(name, property);
|
|
312
84
|
}
|
|
@@ -424,9 +196,13 @@ const rootComposesFoundation = (sourceFile, declaration, stem, foundationName)=>
|
|
|
424
196
|
return void 0 !== chain && isAccessPath(chain.base.callee, [
|
|
425
197
|
'HttpApi',
|
|
426
198
|
'make'
|
|
427
|
-
]) && 1 === chain.base.arguments.length && stringLiteral(chain.base.arguments[0]) === `${pascalCaseStem(stem)}Api` && chain.methods.length > 0 && chain.methods.every((method, index)=>
|
|
199
|
+
]) && 1 === chain.base.arguments.length && stringLiteral(chain.base.arguments[0]) === `${pascalCaseStem(stem)}Api` && chain.methods.length > 0 && chain.methods.every((method, index)=>{
|
|
200
|
+
const combinatorArity = nonEndpointCombinators.api[method.name];
|
|
201
|
+
if (void 0 !== combinatorArity) return method.arguments.length === combinatorArity;
|
|
202
|
+
return 1 === method.arguments.length && ('add' === method.name || 'addHttpApi' === method.name || index === chain.methods.length - 1 && 'pipe' === method.name && 'identity' === identifierName(method.arguments[0]) && importsExactBindings(sourceFile, 'effect', [
|
|
428
203
|
'identity'
|
|
429
|
-
]))
|
|
204
|
+
]));
|
|
205
|
+
}) && first?.name === 'addHttpApi' && identifierName(first.arguments[0]) === foundationName;
|
|
430
206
|
};
|
|
431
207
|
const nonEndpointCombinators = {
|
|
432
208
|
api: {
|
|
@@ -442,9 +218,7 @@ const nonEndpointCombinators = {
|
|
|
442
218
|
middleware: 1
|
|
443
219
|
}
|
|
444
220
|
};
|
|
445
|
-
const reachableEndpoints = (
|
|
446
|
-
const readModule = createModuleReader(rootModule);
|
|
447
|
-
const workspace = containingWorkspace(rootModule.path);
|
|
221
|
+
const reachableEndpoints = (graph, declaration)=>{
|
|
448
222
|
const active = new Set();
|
|
449
223
|
const endpoints = [];
|
|
450
224
|
const identities = new Set();
|
|
@@ -466,8 +240,8 @@ const reachableEndpoints = (rootModule, declaration)=>{
|
|
|
466
240
|
active.add(node);
|
|
467
241
|
try {
|
|
468
242
|
if (types_namespaceObject.isIdentifier(node)) {
|
|
469
|
-
const resolved =
|
|
470
|
-
return
|
|
243
|
+
const resolved = graph.resolve(module, node.name, 'local');
|
|
244
|
+
return resolved?.kind === 'declaration' && visit(resolved.expression, resolved.module, kind, group);
|
|
471
245
|
}
|
|
472
246
|
if ('endpoint' === kind) {
|
|
473
247
|
if (!types_namespaceObject.isCallExpression(node) || node.arguments.length < 2 || node.arguments.length > 3) return false;
|
|
@@ -522,7 +296,7 @@ const reachableEndpoints = (rootModule, declaration)=>{
|
|
|
522
296
|
active.delete(node);
|
|
523
297
|
}
|
|
524
298
|
};
|
|
525
|
-
return visit(declaration?.init,
|
|
299
|
+
return visit(declaration?.init, graph.root, 'api') ? endpoints : void 0;
|
|
526
300
|
};
|
|
527
301
|
const operationContextFields = (property)=>{
|
|
528
302
|
const constructorCall = exactCall(property.value, [
|
|
@@ -615,7 +389,7 @@ const markerSchemaIsShared = (sourceFile, declaration)=>{
|
|
|
615
389
|
'version'
|
|
616
390
|
]);
|
|
617
391
|
if (void 0 === schema || schema.identity) return schema?.identity === true;
|
|
618
|
-
const brandedField = (property, brand)=>identifierName(property.value) === `${brand}Schema` && brandedStringSchemaIsExact(localConst(sourceFile, `${brand}Schema`), brand);
|
|
392
|
+
const brandedField = (property, brand)=>identifierName(property.value) === `${brand}Schema` && brandedStringSchemaIsExact((0, external_module_graph_cjs_namespaceObject.localConst)(sourceFile, `${brand}Schema`), brand);
|
|
619
393
|
const validators = new Map([
|
|
620
394
|
[
|
|
621
395
|
'appId',
|
|
@@ -653,13 +427,13 @@ const readinessSchemaIsShared = (declaration, markerSchemaName, ownerMarkerIsIde
|
|
|
653
427
|
return void 0 !== schema && (schema.identity && ownerMarkerIsIdentity || 1 === schema.assignments.size && identifierName(schema.assignments.get('marker')?.value) === markerSchemaName);
|
|
654
428
|
};
|
|
655
429
|
const declarationIsIdentifier = (declaration, identifier)=>declaration?.init != null && identifierName((0, external_source_analysis_cjs_namespaceObject.unwrapExpression)(declaration.init)) === identifier;
|
|
656
|
-
const validateParsedContract = (
|
|
657
|
-
const sourceFile =
|
|
430
|
+
const validateParsedContract = (graph, stem, expectation)=>{
|
|
431
|
+
const sourceFile = graph.root.file;
|
|
658
432
|
const exportStem = camelCaseStem(stem);
|
|
659
433
|
const foundationName = `${exportStem}FoundationApi`;
|
|
660
434
|
const markerSchemaName = `${exportStem}MarkerSchema`;
|
|
661
435
|
const readinessSchemaName = `${exportStem}ReadinessSchema`;
|
|
662
|
-
const markerDeclaration = exportedConst(sourceFile, markerSchemaName);
|
|
436
|
+
const markerDeclaration = (0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, markerSchemaName);
|
|
663
437
|
if (!importsExactBindings(sourceFile, expectation.effectClientPackage, [
|
|
664
438
|
'HttpApi',
|
|
665
439
|
'HttpApiEndpoint',
|
|
@@ -668,13 +442,13 @@ const validateParsedContract = (module, stem, expectation)=>{
|
|
|
668
442
|
])) return 'MicroVertical root contract must import exact Effect API primitives from the framework client package';
|
|
669
443
|
if (!importsSharedBaselinePrimitives(sourceFile, expectation.baselinePackage)) return 'MicroVertical root contract must import exact baseline primitives from the framework baseline package';
|
|
670
444
|
if (!markerSchemaIsShared(sourceFile, markerDeclaration)) return 'MicroVertical root contract must consume the shared build marker schema without overriding shared fields';
|
|
671
|
-
if (!readinessSchemaIsShared(exportedConst(sourceFile, readinessSchemaName), markerSchemaName, declarationIsIdentifier(markerDeclaration, 'MicroVerticalBuildMarkerSchema'))) return 'MicroVertical readiness schema must consume the shared readiness schema without overriding shared fields';
|
|
672
|
-
if (!foundationIsExact(exportedConst(sourceFile, foundationName), stem, readinessSchemaName)) return 'MicroVertical readiness foundation API must directly compose its exact readiness endpoint and foundation identity';
|
|
673
|
-
if (!rootComposesFoundation(sourceFile, exportedConst(sourceFile, `${exportStem}Api`), stem, foundationName)) return 'MicroVertical root API must explicitly compose its readiness foundation API';
|
|
674
|
-
const endpoints = reachableEndpoints(
|
|
445
|
+
if (!readinessSchemaIsShared((0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, readinessSchemaName), markerSchemaName, declarationIsIdentifier(markerDeclaration, 'MicroVerticalBuildMarkerSchema'))) return 'MicroVertical readiness schema must consume the shared readiness schema without overriding shared fields';
|
|
446
|
+
if (!foundationIsExact((0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, foundationName), stem, readinessSchemaName)) return 'MicroVertical readiness foundation API must directly compose its exact readiness endpoint and foundation identity';
|
|
447
|
+
if (!rootComposesFoundation(sourceFile, (0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, `${exportStem}Api`), stem, foundationName)) return 'MicroVertical root API must explicitly compose its readiness foundation API';
|
|
448
|
+
const endpoints = reachableEndpoints(graph, (0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, `${exportStem}Api`));
|
|
675
449
|
if (!endpoints) return 'MicroVertical root API must compose bounded native endpoint declarations without ambiguous identities';
|
|
676
|
-
if (!operationMapIsConnected(exportedConst(sourceFile, `${exportStem}OperationContexts`), stem, expectation.operationPaths ?? {}, endpoints)) return 'MicroVertical operation map must construct every operation with the shared context constructor';
|
|
677
|
-
if (!metadataIsExact(exportedConst(sourceFile, `${exportStem}ApiContract`), expectation, endpoints)) return 'MicroVertical root contract must keep exact owner and API path metadata without forbidden fields';
|
|
450
|
+
if (!operationMapIsConnected((0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, `${exportStem}OperationContexts`), stem, expectation.operationPaths ?? {}, endpoints)) return 'MicroVertical operation map must construct every operation with the shared context constructor';
|
|
451
|
+
if (!metadataIsExact((0, external_module_graph_cjs_namespaceObject.exportedConst)(sourceFile, `${exportStem}ApiContract`), expectation, endpoints)) return 'MicroVertical root contract must keep exact owner and API path metadata without forbidden fields';
|
|
678
452
|
};
|
|
679
453
|
const configuredMicroVerticalApiStem = (verticalPath, verticals)=>{
|
|
680
454
|
const topologyVertical = verticals.find((vertical)=>(vertical.path ?? `verticals/${vertical.id}`) === verticalPath);
|
|
@@ -683,14 +457,9 @@ const configuredMicroVerticalApiStem = (verticalPath, verticals)=>{
|
|
|
683
457
|
};
|
|
684
458
|
const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
|
|
685
459
|
try {
|
|
686
|
-
const
|
|
687
|
-
const sourceFile = parseConsumer(realPath);
|
|
460
|
+
const graph = (0, external_module_graph_cjs_namespaceObject.createModuleGraph)(filePath);
|
|
688
461
|
if (!(0, external_microvertical_api_owner_cjs_namespaceObject.baselinePublicIdentityIsExact)(filePath, expectation)) return 'MicroVertical baseline imports must resolve the exact framework owner public export and schema identity';
|
|
689
|
-
return validateParsedContract(
|
|
690
|
-
file: sourceFile,
|
|
691
|
-
path: realPath,
|
|
692
|
-
boundary: sourceBoundary(realPath, containingWorkspace(realPath))
|
|
693
|
-
}, stem, expectation);
|
|
462
|
+
return validateParsedContract(graph, stem, expectation);
|
|
694
463
|
} catch (error) {
|
|
695
464
|
if (error instanceof external_source_analysis_cjs_namespaceObject.SourceSyntaxError) return `MicroVertical root contract must be valid TypeScript syntax (${error.message})`;
|
|
696
465
|
throw error;
|