@wairon/cli 5.1.1-dev.10 → 5.1.1-dev.12
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/dist/cli/index.js +1194 -1038
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +2668 -2520
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.12";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -186,11 +186,13 @@ __export(fs_exports, {
|
|
|
186
186
|
fromProjectRoot: () => fromProjectRoot,
|
|
187
187
|
getProjectRoot: () => getProjectRoot,
|
|
188
188
|
getProjectRootOverride: () => getProjectRootOverride,
|
|
189
|
+
getRequestParentReach: () => getRequestParentReach,
|
|
189
190
|
getRequestProjectRoot: () => getRequestProjectRoot,
|
|
190
191
|
listFiles: () => listFiles,
|
|
191
192
|
listFilesRecursive: () => listFilesRecursive,
|
|
192
193
|
pathExists: () => pathExists,
|
|
193
194
|
readFileOrNull: () => readFileOrNull,
|
|
195
|
+
runWithProjectBinding: () => runWithProjectBinding,
|
|
194
196
|
runWithProjectRoot: () => runWithProjectRoot,
|
|
195
197
|
setProjectRoot: () => setProjectRoot,
|
|
196
198
|
writeFile: () => writeFile,
|
|
@@ -241,10 +243,21 @@ function listFilesRecursive(dirPath, ext) {
|
|
|
241
243
|
return files;
|
|
242
244
|
}
|
|
243
245
|
function runWithProjectRoot(dir, fn) {
|
|
244
|
-
return requestRootStore.run(path4.resolve(dir), fn);
|
|
246
|
+
return requestRootStore.run({ ...requestRootStore.getStore(), root: path4.resolve(dir) }, fn);
|
|
247
|
+
}
|
|
248
|
+
function runWithProjectBinding(dir, reach, fn) {
|
|
249
|
+
return requestRootStore.run(
|
|
250
|
+
{ root: path4.resolve(dir), topRoot: path4.resolve(reach.topRoot), parentReach: reach.parentReach },
|
|
251
|
+
fn
|
|
252
|
+
);
|
|
245
253
|
}
|
|
246
254
|
function getRequestProjectRoot() {
|
|
247
|
-
return requestRootStore.getStore() ?? null;
|
|
255
|
+
return requestRootStore.getStore()?.root ?? null;
|
|
256
|
+
}
|
|
257
|
+
function getRequestParentReach() {
|
|
258
|
+
const scope = requestRootStore.getStore();
|
|
259
|
+
if (!scope || scope.parentReach === void 0) return null;
|
|
260
|
+
return { topRoot: scope.topRoot, parentReach: scope.parentReach };
|
|
248
261
|
}
|
|
249
262
|
function setProjectRoot(dir) {
|
|
250
263
|
projectRootOverride = dir === null ? null : path4.resolve(dir);
|
|
@@ -269,7 +282,7 @@ function findSystemRoot(startDir) {
|
|
|
269
282
|
}
|
|
270
283
|
}
|
|
271
284
|
function getProjectRoot() {
|
|
272
|
-
const scoped = requestRootStore.getStore();
|
|
285
|
+
const scoped = requestRootStore.getStore()?.root;
|
|
273
286
|
if (scoped) return scoped;
|
|
274
287
|
if (projectRootOverride) return projectRootOverride;
|
|
275
288
|
const systemRoot = findSystemRoot(process.cwd());
|
|
@@ -3534,733 +3547,6 @@ var init_type_references = __esm({
|
|
|
3534
3547
|
}
|
|
3535
3548
|
});
|
|
3536
3549
|
|
|
3537
|
-
// src/utils/filenames.ts
|
|
3538
|
-
function safeFilenamePart(value) {
|
|
3539
|
-
return value.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
3540
|
-
}
|
|
3541
|
-
var init_filenames = __esm({
|
|
3542
|
-
"src/utils/filenames.ts"() {
|
|
3543
|
-
"use strict";
|
|
3544
|
-
}
|
|
3545
|
-
});
|
|
3546
|
-
|
|
3547
|
-
// src/core/openapi.ts
|
|
3548
|
-
function schemaFor(typeRef, closureIds) {
|
|
3549
|
-
const trimmed = typeRef.trim().replace(/^promise\s*<(.+)>$/i, "$1").trim();
|
|
3550
|
-
const arrayMatch = /^(.+)\[\]$/.exec(trimmed);
|
|
3551
|
-
if (arrayMatch) {
|
|
3552
|
-
return { type: "array", items: schemaFor(arrayMatch[1], closureIds) };
|
|
3553
|
-
}
|
|
3554
|
-
const lower = trimmed.toLowerCase();
|
|
3555
|
-
if (lower in PRIMITIVES) {
|
|
3556
|
-
const p = PRIMITIVES[lower];
|
|
3557
|
-
return p.type ? { ...p } : {};
|
|
3558
|
-
}
|
|
3559
|
-
const local = trimmed.split(/::|\./).pop().toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
3560
|
-
const hit = [...closureIds].find((id) => id.toLowerCase() === local);
|
|
3561
|
-
if (hit) return { $ref: `#/components/schemas/${hit}` };
|
|
3562
|
-
return { type: "object", description: `Unresolved type: ${trimmed}` };
|
|
3563
|
-
}
|
|
3564
|
-
function operationFor(method2, closureIds) {
|
|
3565
|
-
const endpoint = method2.endpoint;
|
|
3566
|
-
const httpVerb = endpoint && endpoint.transport === "HTTP" ? endpoint.method.toLowerCase() : "post";
|
|
3567
|
-
const bodyVerbs = /* @__PURE__ */ new Set(["post", "put", "patch"]);
|
|
3568
|
-
const params = method2.params ?? [];
|
|
3569
|
-
const op = {
|
|
3570
|
-
operationId: method2.name,
|
|
3571
|
-
summary: method2.description,
|
|
3572
|
-
responses: {
|
|
3573
|
-
"200": {
|
|
3574
|
-
description: method2.returns || "Success",
|
|
3575
|
-
...method2.returns && method2.returns.toLowerCase() !== "void" ? { content: { "application/json": { schema: schemaFor(method2.returns, closureIds) } } } : {}
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
};
|
|
3579
|
-
if (method2.guarantees?.length) op["x-wairon-guarantees"] = method2.guarantees;
|
|
3580
|
-
if (method2.effect) op["x-wairon-effect"] = method2.effect;
|
|
3581
|
-
if (method2.ext && Object.keys(method2.ext).length) op["x-wairon-ext"] = method2.ext;
|
|
3582
|
-
if (params.length) {
|
|
3583
|
-
if (bodyVerbs.has(httpVerb)) {
|
|
3584
|
-
op.requestBody = {
|
|
3585
|
-
required: true,
|
|
3586
|
-
content: {
|
|
3587
|
-
"application/json": {
|
|
3588
|
-
schema: {
|
|
3589
|
-
type: "object",
|
|
3590
|
-
properties: Object.fromEntries(params.map((p) => [p.name, schemaFor(p.type, closureIds)])),
|
|
3591
|
-
required: params.filter((p) => !p.optional).map((p) => p.name)
|
|
3592
|
-
}
|
|
3593
|
-
}
|
|
3594
|
-
}
|
|
3595
|
-
};
|
|
3596
|
-
} else {
|
|
3597
|
-
op.parameters = params.map((p) => ({
|
|
3598
|
-
name: p.name,
|
|
3599
|
-
in: "query",
|
|
3600
|
-
required: !p.optional,
|
|
3601
|
-
...p.description ? { description: p.description } : {},
|
|
3602
|
-
schema: schemaFor(p.type, closureIds)
|
|
3603
|
-
}));
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
return op;
|
|
3607
|
-
}
|
|
3608
|
-
function securitySchemeObject(auth) {
|
|
3609
|
-
if (auth.scheme === "none") return null;
|
|
3610
|
-
const desc = auth.description ? { description: auth.description } : {};
|
|
3611
|
-
switch (auth.scheme) {
|
|
3612
|
-
case "apiKey":
|
|
3613
|
-
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "X-API-Key", ...desc };
|
|
3614
|
-
case "bearer":
|
|
3615
|
-
return { type: "http", scheme: "bearer", ...auth.bearerFormat ? { bearerFormat: auth.bearerFormat } : {}, ...desc };
|
|
3616
|
-
case "basic":
|
|
3617
|
-
return { type: "http", scheme: "basic", ...desc };
|
|
3618
|
-
case "oauth2": {
|
|
3619
|
-
const flow = { scopes: Object.fromEntries((auth.scopes ?? []).map((s) => [s.name, s.description])) };
|
|
3620
|
-
if (auth.authorizationUrl) flow.authorizationUrl = auth.authorizationUrl;
|
|
3621
|
-
if (auth.tokenUrl) flow.tokenUrl = auth.tokenUrl;
|
|
3622
|
-
if (auth.refreshUrl) flow.refreshUrl = auth.refreshUrl;
|
|
3623
|
-
return { type: "oauth2", flows: { [auth.flow ?? "authorizationCode"]: flow }, ...desc };
|
|
3624
|
-
}
|
|
3625
|
-
case "openIdConnect":
|
|
3626
|
-
return { type: "openIdConnect", openIdConnectUrl: auth.openIdConnectUrl ?? "", ...desc };
|
|
3627
|
-
case "custom":
|
|
3628
|
-
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "Authorization", description: auth.description ?? auth.example ?? "Custom authentication scheme." };
|
|
3629
|
-
default:
|
|
3630
|
-
return null;
|
|
3631
|
-
}
|
|
3632
|
-
}
|
|
3633
|
-
function schemeBaseName(scheme) {
|
|
3634
|
-
return { apiKey: "ApiKeyAuth", bearer: "BearerAuth", basic: "BasicAuth", oauth2: "OAuth2", openIdConnect: "OpenIdConnect", custom: "CustomAuth" }[scheme] ?? "Auth";
|
|
3635
|
-
}
|
|
3636
|
-
function buildSecurity(entries) {
|
|
3637
|
-
const schemes = {};
|
|
3638
|
-
const nameByContent = /* @__PURE__ */ new Map();
|
|
3639
|
-
const securityByEntry = /* @__PURE__ */ new Map();
|
|
3640
|
-
for (const entry of entries) {
|
|
3641
|
-
if (!entry.auth) continue;
|
|
3642
|
-
const obj = securitySchemeObject(entry.auth);
|
|
3643
|
-
if (!obj) continue;
|
|
3644
|
-
const content = JSON.stringify(obj);
|
|
3645
|
-
let name = nameByContent.get(content);
|
|
3646
|
-
if (!name) {
|
|
3647
|
-
name = schemeBaseName(entry.auth.scheme);
|
|
3648
|
-
for (let n = 2; schemes[name]; n++) name = schemeBaseName(entry.auth.scheme) + n;
|
|
3649
|
-
schemes[name] = obj;
|
|
3650
|
-
nameByContent.set(content, name);
|
|
3651
|
-
}
|
|
3652
|
-
const scopeNames = entry.auth.scheme === "oauth2" ? (entry.auth.scopes ?? []).map((s) => s.name) : [];
|
|
3653
|
-
securityByEntry.set(entry.id, { [name]: scopeNames });
|
|
3654
|
-
}
|
|
3655
|
-
return { schemes, securityByEntry };
|
|
3656
|
-
}
|
|
3657
|
-
function httpEntriesOf(snapshot) {
|
|
3658
|
-
return snapshot.interfaces.filter((e) => e.type === "REST" || e.methods.some((m) => m.endpoint?.transport === "HTTP"));
|
|
3659
|
-
}
|
|
3660
|
-
function renderDoc(snapshot, entries, closureIds, opts = {}) {
|
|
3661
|
-
const { schemes, securityByEntry } = buildSecurity(entries);
|
|
3662
|
-
const paths = {};
|
|
3663
|
-
for (const entry of entries) {
|
|
3664
|
-
const security = securityByEntry.get(entry.id);
|
|
3665
|
-
for (const method2 of entry.methods) {
|
|
3666
|
-
const endpoint = method2.endpoint;
|
|
3667
|
-
if (!endpoint || endpoint.transport !== "HTTP") continue;
|
|
3668
|
-
const p = endpoint.path.startsWith("/") ? endpoint.path : `/${endpoint.path}`;
|
|
3669
|
-
paths[p] = paths[p] ?? {};
|
|
3670
|
-
paths[p][endpoint.method.toLowerCase()] = {
|
|
3671
|
-
tags: [entry.id],
|
|
3672
|
-
...operationFor(method2, closureIds),
|
|
3673
|
-
...security ? { security: [security] } : {}
|
|
3674
|
-
};
|
|
3675
|
-
}
|
|
3676
|
-
}
|
|
3677
|
-
const schemas = {};
|
|
3678
|
-
for (const t of snapshot.types) {
|
|
3679
|
-
schemas[t.id] = {
|
|
3680
|
-
type: "object",
|
|
3681
|
-
title: t.name,
|
|
3682
|
-
properties: Object.fromEntries(t.fields.map((f) => [f.name, schemaFor(f.type, closureIds)])),
|
|
3683
|
-
required: t.fields.filter((f) => !f.optional).map((f) => f.name)
|
|
3684
|
-
};
|
|
3685
|
-
}
|
|
3686
|
-
const components = {};
|
|
3687
|
-
if (Object.keys(schemas).length) components.schemas = schemas;
|
|
3688
|
-
if (Object.keys(schemes).length) components.securitySchemes = schemes;
|
|
3689
|
-
const basePaths = [...new Set(entries.map((e) => e.basePath).filter((b) => !!b))];
|
|
3690
|
-
const servers = opts.servers && basePaths.length === 1 ? [{ url: basePaths[0] }] : void 0;
|
|
3691
|
-
return {
|
|
3692
|
-
openapi: "3.1.0",
|
|
3693
|
-
info: {
|
|
3694
|
-
title: opts.title ?? snapshot.projectName,
|
|
3695
|
-
version: snapshot.version ?? "0.0.0",
|
|
3696
|
-
...snapshot.stateId ? { "x-wairon-state-id": snapshot.stateId } : {},
|
|
3697
|
-
"x-wairon-origin": snapshot.origin,
|
|
3698
|
-
"x-wairon-generated-at": snapshot.generatedAt
|
|
3699
|
-
},
|
|
3700
|
-
...servers ? { servers } : {},
|
|
3701
|
-
paths,
|
|
3702
|
-
...Object.keys(components).length ? { components } : {}
|
|
3703
|
-
};
|
|
3704
|
-
}
|
|
3705
|
-
function toOpenApiSet(snapshot) {
|
|
3706
|
-
const closureIds = new Set(snapshot.types.map((t) => t.id));
|
|
3707
|
-
const byPortal = /* @__PURE__ */ new Map();
|
|
3708
|
-
const order = [];
|
|
3709
|
-
for (const entry of httpEntriesOf(snapshot)) {
|
|
3710
|
-
if (!byPortal.has(entry.component)) {
|
|
3711
|
-
byPortal.set(entry.component, []);
|
|
3712
|
-
order.push(entry.component);
|
|
3713
|
-
}
|
|
3714
|
-
byPortal.get(entry.component).push(entry);
|
|
3715
|
-
}
|
|
3716
|
-
return order.map((portalId) => {
|
|
3717
|
-
const entries = byPortal.get(portalId);
|
|
3718
|
-
const name = entries[0]?.name ?? portalId;
|
|
3719
|
-
return { portalId, name, document: JSON.stringify(renderDoc(snapshot, entries, closureIds, { title: name, servers: true }), null, 2) };
|
|
3720
|
-
});
|
|
3721
|
-
}
|
|
3722
|
-
function isOpenApiDocument(body) {
|
|
3723
|
-
try {
|
|
3724
|
-
const parsed = yaml2.load(body);
|
|
3725
|
-
return !!parsed && typeof parsed === "object" && typeof parsed.openapi === "string";
|
|
3726
|
-
} catch {
|
|
3727
|
-
return false;
|
|
3728
|
-
}
|
|
3729
|
-
}
|
|
3730
|
-
function typeRefFromSchema(schema) {
|
|
3731
|
-
if (!schema) return "json";
|
|
3732
|
-
const ref = schema.$ref;
|
|
3733
|
-
if (typeof ref === "string") return ref.split("/").pop() ?? "json";
|
|
3734
|
-
if (schema.type === "array") {
|
|
3735
|
-
return `${typeRefFromSchema(schema.items)}[]`;
|
|
3736
|
-
}
|
|
3737
|
-
const t = schema.type;
|
|
3738
|
-
if (t === "integer") return "int";
|
|
3739
|
-
if (typeof t === "string" && t !== "object") return t;
|
|
3740
|
-
return "json";
|
|
3741
|
-
}
|
|
3742
|
-
function authFromSecurityScheme(scheme) {
|
|
3743
|
-
const desc = typeof scheme.description === "string" ? { description: scheme.description } : {};
|
|
3744
|
-
if (scheme.type === "apiKey") {
|
|
3745
|
-
return {
|
|
3746
|
-
scheme: "apiKey",
|
|
3747
|
-
...scheme.in === "header" || scheme.in === "query" || scheme.in === "cookie" ? { in: scheme.in } : {},
|
|
3748
|
-
...typeof scheme.name === "string" ? { name: scheme.name } : {},
|
|
3749
|
-
...desc
|
|
3750
|
-
};
|
|
3751
|
-
}
|
|
3752
|
-
if (scheme.type === "http") {
|
|
3753
|
-
if (scheme.scheme === "bearer") return { scheme: "bearer", ...typeof scheme.bearerFormat === "string" ? { bearerFormat: scheme.bearerFormat } : {}, ...desc };
|
|
3754
|
-
if (scheme.scheme === "basic") return { scheme: "basic", ...desc };
|
|
3755
|
-
}
|
|
3756
|
-
if (scheme.type === "oauth2") {
|
|
3757
|
-
const flows = scheme.flows ?? {};
|
|
3758
|
-
const flowKey = Object.keys(flows)[0];
|
|
3759
|
-
const f = flows[flowKey] ?? {};
|
|
3760
|
-
return {
|
|
3761
|
-
scheme: "oauth2",
|
|
3762
|
-
...flowKey === "authorizationCode" || flowKey === "clientCredentials" || flowKey === "implicit" || flowKey === "password" ? { flow: flowKey } : {},
|
|
3763
|
-
...typeof f.authorizationUrl === "string" ? { authorizationUrl: f.authorizationUrl } : {},
|
|
3764
|
-
...typeof f.tokenUrl === "string" ? { tokenUrl: f.tokenUrl } : {},
|
|
3765
|
-
...typeof f.refreshUrl === "string" ? { refreshUrl: f.refreshUrl } : {},
|
|
3766
|
-
scopes: Object.entries(f.scopes ?? {}).map(([name, description]) => ({ name, description })),
|
|
3767
|
-
...desc
|
|
3768
|
-
};
|
|
3769
|
-
}
|
|
3770
|
-
if (scheme.type === "openIdConnect") {
|
|
3771
|
-
return { scheme: "openIdConnect", openIdConnectUrl: typeof scheme.openIdConnectUrl === "string" ? scheme.openIdConnectUrl : "", ...desc };
|
|
3772
|
-
}
|
|
3773
|
-
return void 0;
|
|
3774
|
-
}
|
|
3775
|
-
function fromOpenApi(document, projectName) {
|
|
3776
|
-
let parsed;
|
|
3777
|
-
try {
|
|
3778
|
-
parsed = yaml2.load(document);
|
|
3779
|
-
} catch (e) {
|
|
3780
|
-
throw new Error(`Invalid surface document: not parseable as JSON/YAML (${e instanceof Error ? e.message : String(e)})`);
|
|
3781
|
-
}
|
|
3782
|
-
if (!parsed || typeof parsed !== "object" || typeof parsed.openapi !== "string" || typeof parsed.paths !== "object") {
|
|
3783
|
-
throw new Error('Invalid surface document: missing OpenAPI "openapi"/"paths" structure.');
|
|
3784
|
-
}
|
|
3785
|
-
const info = parsed.info ?? {};
|
|
3786
|
-
const methods = [];
|
|
3787
|
-
for (const [rawPath, ops] of Object.entries(parsed.paths)) {
|
|
3788
|
-
for (const [verb, opRaw] of Object.entries(ops ?? {})) {
|
|
3789
|
-
if (!["get", "post", "put", "delete", "patch", "options", "head"].includes(verb)) continue;
|
|
3790
|
-
const op = opRaw ?? {};
|
|
3791
|
-
const name = typeof op.operationId === "string" && /^[a-zA-Z0-9_]+$/.test(op.operationId) ? op.operationId : `${verb}_${rawPath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
3792
|
-
const params = [];
|
|
3793
|
-
for (const p of op.parameters ?? []) {
|
|
3794
|
-
if (typeof p.name !== "string") continue;
|
|
3795
|
-
params.push({
|
|
3796
|
-
name: p.name,
|
|
3797
|
-
type: typeRefFromSchema(p.schema),
|
|
3798
|
-
...p.required === true ? {} : { optional: true },
|
|
3799
|
-
...typeof p.description === "string" ? { description: p.description } : {}
|
|
3800
|
-
});
|
|
3801
|
-
}
|
|
3802
|
-
const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
|
|
3803
|
-
if (bodySchema) {
|
|
3804
|
-
const props = bodySchema.properties ?? {};
|
|
3805
|
-
const required = new Set(bodySchema.required ?? []);
|
|
3806
|
-
if (Object.keys(props).length) {
|
|
3807
|
-
for (const [pname, pschema] of Object.entries(props)) {
|
|
3808
|
-
params.push({ name: pname, type: typeRefFromSchema(pschema), ...required.has(pname) ? {} : { optional: true } });
|
|
3809
|
-
}
|
|
3810
|
-
} else {
|
|
3811
|
-
params.push({ name: "body", type: typeRefFromSchema(bodySchema) });
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
const okResponse = op.responses?.["200"] ?? op.responses?.["201"];
|
|
3815
|
-
const responseSchema = okResponse?.content?.["application/json"]?.schema;
|
|
3816
|
-
const returns = responseSchema ? typeRefFromSchema(responseSchema) : "void";
|
|
3817
|
-
const rawGuarantees = op["x-wairon-guarantees"];
|
|
3818
|
-
const guarantees = Array.isArray(rawGuarantees) ? rawGuarantees.filter((g) => typeof g === "string" && g.length > 0) : [];
|
|
3819
|
-
const rawEffect = op["x-wairon-effect"];
|
|
3820
|
-
const effect = rawEffect === "read" || rawEffect === "write" ? rawEffect : void 0;
|
|
3821
|
-
const rawExt = op["x-wairon-ext"];
|
|
3822
|
-
const ext = rawExt && typeof rawExt === "object" && !Array.isArray(rawExt) ? rawExt : void 0;
|
|
3823
|
-
methods.push({
|
|
3824
|
-
name,
|
|
3825
|
-
description: typeof op.summary === "string" ? op.summary : typeof op.description === "string" ? op.description : name,
|
|
3826
|
-
signature: `${name}(${params.map((p) => `${p.name}: ${p.type}`).join(", ")}): ${returns}`,
|
|
3827
|
-
returns,
|
|
3828
|
-
params,
|
|
3829
|
-
endpoint: { transport: "HTTP", method: verb.toUpperCase(), path: rawPath },
|
|
3830
|
-
...guarantees.length ? { guarantees } : {},
|
|
3831
|
-
...effect ? { effect } : {},
|
|
3832
|
-
...ext ? { ext } : {}
|
|
3833
|
-
});
|
|
3834
|
-
}
|
|
3835
|
-
}
|
|
3836
|
-
const types = [];
|
|
3837
|
-
const schemas = parsed.components?.schemas ?? {};
|
|
3838
|
-
for (const [id, schema] of Object.entries(schemas)) {
|
|
3839
|
-
const props = schema.properties ?? {};
|
|
3840
|
-
const required = new Set(schema.required ?? []);
|
|
3841
|
-
types.push({
|
|
3842
|
-
id,
|
|
3843
|
-
name: typeof schema.title === "string" ? schema.title : id,
|
|
3844
|
-
kind: "value-object",
|
|
3845
|
-
fields: Object.entries(props).map(([fname, fschema]) => ({
|
|
3846
|
-
name: fname,
|
|
3847
|
-
type: typeRefFromSchema(fschema),
|
|
3848
|
-
...required.has(fname) ? {} : { optional: true }
|
|
3849
|
-
}))
|
|
3850
|
-
});
|
|
3851
|
-
}
|
|
3852
|
-
const securitySchemes = parsed.components?.securitySchemes ?? {};
|
|
3853
|
-
const firstScheme = Object.values(securitySchemes)[0];
|
|
3854
|
-
const importedAuth = firstScheme ? authFromSecurityScheme(firstScheme) : void 0;
|
|
3855
|
-
const entry = {
|
|
3856
|
-
id: `${projectName}-api`,
|
|
3857
|
-
name: typeof info.title === "string" ? info.title : projectName,
|
|
3858
|
-
audience: "external",
|
|
3859
|
-
type: "REST",
|
|
3860
|
-
component: `${projectName}-api`,
|
|
3861
|
-
methods,
|
|
3862
|
-
details: typeof info.description === "string" ? info.description : `Imported OpenAPI surface of ${projectName}.`,
|
|
3863
|
-
...typeof info.version === "string" ? { version: info.version } : {},
|
|
3864
|
-
...importedAuth ? { auth: importedAuth } : {}
|
|
3865
|
-
};
|
|
3866
|
-
return SurfaceSnapshotSchema.parse({
|
|
3867
|
-
projectName,
|
|
3868
|
-
origin: "authored",
|
|
3869
|
-
...typeof info.version === "string" ? { version: info.version } : {},
|
|
3870
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3871
|
-
interfaces: [entry],
|
|
3872
|
-
types
|
|
3873
|
-
});
|
|
3874
|
-
}
|
|
3875
|
-
var yaml2, PRIMITIVES;
|
|
3876
|
-
var init_openapi = __esm({
|
|
3877
|
-
"src/core/openapi.ts"() {
|
|
3878
|
-
"use strict";
|
|
3879
|
-
yaml2 = __toESM(require("js-yaml"));
|
|
3880
|
-
init_models();
|
|
3881
|
-
PRIMITIVES = {
|
|
3882
|
-
string: { type: "string" },
|
|
3883
|
-
number: { type: "number" },
|
|
3884
|
-
float: { type: "number" },
|
|
3885
|
-
decimal: { type: "number" },
|
|
3886
|
-
int: { type: "integer" },
|
|
3887
|
-
integer: { type: "integer" },
|
|
3888
|
-
boolean: { type: "boolean" },
|
|
3889
|
-
bool: { type: "boolean" },
|
|
3890
|
-
date: { type: "string", format: "date-time" },
|
|
3891
|
-
datetime: { type: "string", format: "date-time" },
|
|
3892
|
-
uuid: { type: "string", format: "uuid" },
|
|
3893
|
-
json: { type: "object" },
|
|
3894
|
-
object: { type: "object" },
|
|
3895
|
-
any: {},
|
|
3896
|
-
unknown: {},
|
|
3897
|
-
void: {}
|
|
3898
|
-
};
|
|
3899
|
-
}
|
|
3900
|
-
});
|
|
3901
|
-
|
|
3902
|
-
// src/core/surfaces.ts
|
|
3903
|
-
function surfacesDir(rootDir) {
|
|
3904
|
-
return path11.join(rootDir, ".wai", SURFACES_DIRNAME);
|
|
3905
|
-
}
|
|
3906
|
-
function audienceRank(audience) {
|
|
3907
|
-
const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
|
|
3908
|
-
return idx === -1 ? SURFACE_AUDIENCES.indexOf("instance") : idx;
|
|
3909
|
-
}
|
|
3910
|
-
function stateIdString() {
|
|
3911
|
-
const s = computeStateId();
|
|
3912
|
-
return `${s.algorithm}:${s.digest}`;
|
|
3913
|
-
}
|
|
3914
|
-
function computeTypeClosure(entries, types) {
|
|
3915
|
-
const included = /* @__PURE__ */ new Map();
|
|
3916
|
-
const queue = [];
|
|
3917
|
-
const enqueueRef = (ref) => {
|
|
3918
|
-
if (BUILTIN_TYPES.has(ref.toLowerCase())) return;
|
|
3919
|
-
for (const spec of types) {
|
|
3920
|
-
const qualifiedId2 = spec.subsystem && !spec.id.startsWith(`${spec.subsystem}::`) ? `${spec.subsystem}::${spec.id}` : spec.id;
|
|
3921
|
-
if (matchTypeRef(ref, qualifiedId2) && !included.has(spec.id)) {
|
|
3922
|
-
included.set(spec.id, spec);
|
|
3923
|
-
queue.push(spec.id);
|
|
3924
|
-
}
|
|
3925
|
-
}
|
|
3926
|
-
};
|
|
3927
|
-
for (const entry of entries) {
|
|
3928
|
-
for (const m of entry.methods) {
|
|
3929
|
-
for (const ref of methodTypeRefs(m)) enqueueRef(ref);
|
|
3930
|
-
}
|
|
3931
|
-
}
|
|
3932
|
-
while (queue.length) {
|
|
3933
|
-
const spec = included.get(queue.shift());
|
|
3934
|
-
for (const field of spec.fields) {
|
|
3935
|
-
for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3938
|
-
return [...included.values()].map((t) => ({
|
|
3939
|
-
id: t.id,
|
|
3940
|
-
name: t.name,
|
|
3941
|
-
kind: t.kind,
|
|
3942
|
-
fields: t.fields.map((f) => ({
|
|
3943
|
-
name: f.name,
|
|
3944
|
-
type: f.type,
|
|
3945
|
-
...f.description ? { description: f.description } : {},
|
|
3946
|
-
...f.optional ? { optional: true } : {}
|
|
3947
|
-
}))
|
|
3948
|
-
}));
|
|
3949
|
-
}
|
|
3950
|
-
function projectOwnSurface(maxAudience) {
|
|
3951
|
-
const system = loadSystemSpec();
|
|
3952
|
-
if (!system) {
|
|
3953
|
-
throw new Error("Cannot project a surface: the L0 system spec is missing.");
|
|
3954
|
-
}
|
|
3955
|
-
const subsystems = loadSubsystemSpecs();
|
|
3956
|
-
const components = loadComponentSpecs();
|
|
3957
|
-
const interfaces = loadInterfaceSpecs();
|
|
3958
|
-
const types = loadTypeSpecs();
|
|
3959
|
-
const floor = audienceRank(maxAudience);
|
|
3960
|
-
const rawEntries = system.publicInterfaces ?? [];
|
|
3961
|
-
const entries = [];
|
|
3962
|
-
for (const raw of rawEntries) {
|
|
3963
|
-
const audience = raw.audience ?? "instance";
|
|
3964
|
-
if (audienceRank(audience) < floor) continue;
|
|
3965
|
-
if (!raw.component) continue;
|
|
3966
|
-
const comp = components.find((c) => c.id === raw.component);
|
|
3967
|
-
if (!comp) continue;
|
|
3968
|
-
const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!raw.interface || i.id === raw.interface));
|
|
3969
|
-
const methods = compInterfaces.flatMap((i) => i.methods);
|
|
3970
|
-
const subsystemType = subsystems.find((s) => s.id === comp.subsystem)?.publicInterfaces.find((pi) => pi.component === comp.id)?.type;
|
|
3971
|
-
entries.push({
|
|
3972
|
-
id: raw.id ?? raw.interface ?? comp.id,
|
|
3973
|
-
name: raw.name ?? comp.name,
|
|
3974
|
-
audience,
|
|
3975
|
-
type: raw.type ?? subsystemType ?? "Custom",
|
|
3976
|
-
component: comp.id,
|
|
3977
|
-
methods,
|
|
3978
|
-
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
3979
|
-
// Project the backing Portal's auth + basePath so the codec can emit
|
|
3980
|
-
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
3981
|
-
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
3982
|
-
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
3983
|
-
details: raw.details ?? "",
|
|
3984
|
-
...raw.version ? { version: raw.version } : {},
|
|
3985
|
-
...raw.stability ? { stability: raw.stability } : {}
|
|
3986
|
-
});
|
|
3987
|
-
}
|
|
3988
|
-
return SurfaceSnapshotSchema.parse({
|
|
3989
|
-
projectName: system.name,
|
|
3990
|
-
origin: "generated",
|
|
3991
|
-
stateId: stateIdString(),
|
|
3992
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3993
|
-
interfaces: entries,
|
|
3994
|
-
types: computeTypeClosure(entries, types)
|
|
3995
|
-
});
|
|
3996
|
-
}
|
|
3997
|
-
function projectChildSurface() {
|
|
3998
|
-
return projectOwnSurface("project");
|
|
3999
|
-
}
|
|
4000
|
-
function localName(id) {
|
|
4001
|
-
return id.split("::").pop();
|
|
4002
|
-
}
|
|
4003
|
-
function projectSubsystemSurface(subsystemId) {
|
|
4004
|
-
const system = loadSystemSpec();
|
|
4005
|
-
if (!system) {
|
|
4006
|
-
throw new Error("Cannot project a subsystem surface: the L0 system spec is missing.");
|
|
4007
|
-
}
|
|
4008
|
-
const subsystems = loadSubsystemSpecs();
|
|
4009
|
-
const target = subsystems.find((s) => s.id === subsystemId);
|
|
4010
|
-
if (!target) {
|
|
4011
|
-
throw new Error(`Cannot project a subsystem surface: subsystem "${subsystemId}" does not exist.`);
|
|
4012
|
-
}
|
|
4013
|
-
const components = loadComponentSpecs();
|
|
4014
|
-
const interfaces = loadInterfaceSpecs();
|
|
4015
|
-
const types = loadTypeSpecs();
|
|
4016
|
-
const entries = [];
|
|
4017
|
-
const unprojectable = [];
|
|
4018
|
-
for (const pub of target.publicInterfaces ?? []) {
|
|
4019
|
-
if (!pub.component) continue;
|
|
4020
|
-
const comp = components.find((c) => c.id === pub.component || c.id === `${subsystemId}::${pub.component}`);
|
|
4021
|
-
if (!comp) continue;
|
|
4022
|
-
if (!CROSS_BOUNDARY_TARGETS.has(comp.componentType)) {
|
|
4023
|
-
unprojectable.push({ component: pub.component, componentType: comp.componentType });
|
|
4024
|
-
continue;
|
|
4025
|
-
}
|
|
4026
|
-
const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!pub.interface || i.id === pub.interface || i.id === `${subsystemId}::${pub.interface}`));
|
|
4027
|
-
const methods = compInterfaces.flatMap((i) => i.methods);
|
|
4028
|
-
entries.push({
|
|
4029
|
-
id: localName(pub.interface ?? comp.id),
|
|
4030
|
-
name: comp.name,
|
|
4031
|
-
// Family ceiling: a sibling surface is consumable by the system family only.
|
|
4032
|
-
audience: "project",
|
|
4033
|
-
type: pub.type ?? "Custom",
|
|
4034
|
-
// The snapshot carries the LOCAL portal name — consumers resolve cross-tree
|
|
4035
|
-
// refs by their final segment.
|
|
4036
|
-
component: localName(comp.id),
|
|
4037
|
-
methods,
|
|
4038
|
-
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
4039
|
-
// Project the backing component's auth + basePath so the codec can emit
|
|
4040
|
-
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
4041
|
-
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
4042
|
-
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
4043
|
-
details: pub.details ?? ""
|
|
4044
|
-
});
|
|
4045
|
-
}
|
|
4046
|
-
for (const skipped of unprojectable) {
|
|
4047
|
-
console.error(
|
|
4048
|
-
`[surfaces] skipped "${subsystemId}::${skipped.component}": a published ${skipped.componentType} can never serve a cross-boundary caller, so it stays out of every chained child's sibling surface \u2014 publish this surface through a Portal, a Gateway, or an Observer (for events).`
|
|
4049
|
-
);
|
|
4050
|
-
}
|
|
4051
|
-
return SurfaceSnapshotSchema.parse({
|
|
4052
|
-
projectName: `${system.name}::${subsystemId}`,
|
|
4053
|
-
origin: "generated",
|
|
4054
|
-
stateId: stateIdString(),
|
|
4055
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4056
|
-
interfaces: entries,
|
|
4057
|
-
types: computeTypeClosure(entries, types)
|
|
4058
|
-
});
|
|
4059
|
-
}
|
|
4060
|
-
function listSnapshots(rootDir = getProjectRoot()) {
|
|
4061
|
-
const dir = surfacesDir(rootDir);
|
|
4062
|
-
if (!fs9.existsSync(dir)) return [];
|
|
4063
|
-
const out = [];
|
|
4064
|
-
for (const file of fs9.readdirSync(dir)) {
|
|
4065
|
-
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
4066
|
-
try {
|
|
4067
|
-
out.push(SurfaceSnapshotSchema.parse(readYamlFile(path11.join(dir, file))));
|
|
4068
|
-
} catch {
|
|
4069
|
-
}
|
|
4070
|
-
}
|
|
4071
|
-
return out;
|
|
4072
|
-
}
|
|
4073
|
-
function getSnapshot(projectName, rootDir = getProjectRoot()) {
|
|
4074
|
-
return listSnapshots(rootDir).find((s) => s.projectName === projectName) ?? null;
|
|
4075
|
-
}
|
|
4076
|
-
function snapshotFilename(projectName) {
|
|
4077
|
-
return `${safeFilenamePart(projectName)}.yaml`;
|
|
4078
|
-
}
|
|
4079
|
-
function writeSnapshotIfChanged(snapshot, rootDir) {
|
|
4080
|
-
const dir = surfacesDir(rootDir);
|
|
4081
|
-
fs9.mkdirSync(dir, { recursive: true });
|
|
4082
|
-
const p = path11.join(dir, snapshotFilename(snapshot.projectName));
|
|
4083
|
-
const next = SurfaceSnapshotSchema.parse(snapshot);
|
|
4084
|
-
if (fs9.existsSync(p)) {
|
|
4085
|
-
try {
|
|
4086
|
-
const existing = SurfaceSnapshotSchema.parse(readYamlFile(p));
|
|
4087
|
-
if (surfaceContentKey(existing) === surfaceContentKey(next)) {
|
|
4088
|
-
return { path: p, changed: false };
|
|
4089
|
-
}
|
|
4090
|
-
} catch {
|
|
4091
|
-
}
|
|
4092
|
-
}
|
|
4093
|
-
writeYamlFile(p, next);
|
|
4094
|
-
return { path: p, changed: true };
|
|
4095
|
-
}
|
|
4096
|
-
function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
|
|
4097
|
-
return writeSnapshotIfChanged(snapshot, rootDir).path;
|
|
4098
|
-
}
|
|
4099
|
-
function loadSurfaceSnapshots() {
|
|
4100
|
-
return listSnapshots();
|
|
4101
|
-
}
|
|
4102
|
-
function selectPortalSpec(renderedSet, portalId) {
|
|
4103
|
-
const hit = renderedSet.find((spec) => spec.portalId === portalId);
|
|
4104
|
-
if (!hit) {
|
|
4105
|
-
const known = renderedSet.map((s) => s.portalId).join(", ");
|
|
4106
|
-
throw new Error(`Unknown portal "${portalId}" \u2014 this surface renders: ${known || "(no portals)"}.`);
|
|
4107
|
-
}
|
|
4108
|
-
return [hit];
|
|
4109
|
-
}
|
|
4110
|
-
function perPortalPath(resolvedOut, portalId) {
|
|
4111
|
-
const ext = path11.extname(resolvedOut);
|
|
4112
|
-
const stem2 = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
|
|
4113
|
-
return `${stem2}.${safeFilenamePart(portalId)}${ext}`;
|
|
4114
|
-
}
|
|
4115
|
-
function writeSurfaceFile(outPath, snapshot, renderedSet) {
|
|
4116
|
-
const resolved = path11.resolve(outPath);
|
|
4117
|
-
fs9.mkdirSync(path11.dirname(resolved), { recursive: true });
|
|
4118
|
-
if (!renderedSet || renderedSet.length === 0) {
|
|
4119
|
-
writeYamlFile(resolved, snapshot);
|
|
4120
|
-
return [resolved];
|
|
4121
|
-
}
|
|
4122
|
-
if (renderedSet.length === 1) {
|
|
4123
|
-
fs9.writeFileSync(resolved, renderedSet[0].document);
|
|
4124
|
-
return [resolved];
|
|
4125
|
-
}
|
|
4126
|
-
return renderedSet.map((spec) => {
|
|
4127
|
-
const target = perPortalPath(resolved, spec.portalId);
|
|
4128
|
-
fs9.writeFileSync(target, spec.document);
|
|
4129
|
-
return target;
|
|
4130
|
-
});
|
|
4131
|
-
}
|
|
4132
|
-
function exportResult(snapshot, renderedSet, writtenPaths) {
|
|
4133
|
-
const rendered = renderedSet?.length === 1 ? renderedSet[0].document : void 0;
|
|
4134
|
-
return {
|
|
4135
|
-
snapshot,
|
|
4136
|
-
...rendered !== void 0 ? { rendered } : {},
|
|
4137
|
-
...renderedSet ? { renderedSet } : {},
|
|
4138
|
-
...writtenPaths.length === 1 ? { writtenTo: writtenPaths[0] } : {},
|
|
4139
|
-
...writtenPaths.length ? { writtenPaths } : {}
|
|
4140
|
-
};
|
|
4141
|
-
}
|
|
4142
|
-
function exportSurface(maxAudience, format, outPath, portalId) {
|
|
4143
|
-
const snapshot = projectOwnSurface(maxAudience);
|
|
4144
|
-
let renderedSet = format === "openapi" ? toOpenApiSet(snapshot) : void 0;
|
|
4145
|
-
if (renderedSet && portalId) renderedSet = selectPortalSpec(renderedSet, portalId);
|
|
4146
|
-
const writtenPaths = outPath ? writeSurfaceFile(outPath, snapshot, renderedSet) : [];
|
|
4147
|
-
return exportResult(snapshot, renderedSet, writtenPaths);
|
|
4148
|
-
}
|
|
4149
|
-
function importSurface(sourcePath, origin) {
|
|
4150
|
-
const resolved = path11.resolve(sourcePath);
|
|
4151
|
-
if (!fs9.existsSync(resolved)) {
|
|
4152
|
-
throw new Error(`Surface document not found: ${resolved}`);
|
|
4153
|
-
}
|
|
4154
|
-
const body = fs9.readFileSync(resolved, "utf8");
|
|
4155
|
-
let snapshot;
|
|
4156
|
-
if (isOpenApiDocument(body)) {
|
|
4157
|
-
const projectName = path11.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
|
|
4158
|
-
snapshot = fromOpenApi(body, projectName);
|
|
4159
|
-
snapshot = { ...snapshot, origin };
|
|
4160
|
-
} else {
|
|
4161
|
-
snapshot = SurfaceSnapshotSchema.parse(readYamlFile(resolved));
|
|
4162
|
-
snapshot = { ...snapshot, origin };
|
|
4163
|
-
}
|
|
4164
|
-
saveSnapshot(snapshot);
|
|
4165
|
-
return snapshot;
|
|
4166
|
-
}
|
|
4167
|
-
function generateChildSnapshots(rootDir = getProjectRoot()) {
|
|
4168
|
-
const topLevel = loadSubsystemSpecs().filter((s) => !s.id.includes("::"));
|
|
4169
|
-
const children = topLevel.filter((s) => s.projectPath);
|
|
4170
|
-
if (!children.length) return [];
|
|
4171
|
-
const familySnapshot = projectChildSurface();
|
|
4172
|
-
const siblingSnapshots = /* @__PURE__ */ new Map();
|
|
4173
|
-
const siblingSurface = (subsystemId) => {
|
|
4174
|
-
let snap = siblingSnapshots.get(subsystemId);
|
|
4175
|
-
if (!snap) {
|
|
4176
|
-
snap = projectSubsystemSurface(subsystemId);
|
|
4177
|
-
siblingSnapshots.set(subsystemId, snap);
|
|
4178
|
-
}
|
|
4179
|
-
return snap;
|
|
4180
|
-
};
|
|
4181
|
-
const written = [];
|
|
4182
|
-
const record2 = (r) => {
|
|
4183
|
-
if (r.changed) written.push(r.path);
|
|
4184
|
-
};
|
|
4185
|
-
for (const child of children) {
|
|
4186
|
-
const childDir = path11.resolve(rootDir, child.projectPath);
|
|
4187
|
-
if (!fs9.existsSync(childDir)) continue;
|
|
4188
|
-
record2(writeSnapshotIfChanged(familySnapshot, childDir));
|
|
4189
|
-
for (const sibling of topLevel) {
|
|
4190
|
-
if (sibling.id === child.id) continue;
|
|
4191
|
-
record2(writeSnapshotIfChanged(siblingSurface(sibling.id), childDir));
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
return written;
|
|
4195
|
-
}
|
|
4196
|
-
function computeParentStateId(parentRoot) {
|
|
4197
|
-
return computeStateIdAt(parentRoot);
|
|
4198
|
-
}
|
|
4199
|
-
function listExternalInterfaces() {
|
|
4200
|
-
const snapshots = listSnapshots();
|
|
4201
|
-
const chainingParent = resolveChainingParent();
|
|
4202
|
-
const parentStateId = chainingParent ? computeParentStateId(chainingParent.parentRoot) : null;
|
|
4203
|
-
return snapshots.map((snapshot) => {
|
|
4204
|
-
const generated = snapshot.origin === "generated";
|
|
4205
|
-
const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
|
|
4206
|
-
const freshness = generated && parentStateId ? snapshot.stateId === parentStateId ? "fresh" : "stale" : "unverifiable";
|
|
4207
|
-
return {
|
|
4208
|
-
projectName: snapshot.projectName,
|
|
4209
|
-
origin: snapshot.origin,
|
|
4210
|
-
sourceKind,
|
|
4211
|
-
generatedAt: snapshot.generatedAt,
|
|
4212
|
-
...snapshot.stateId ? { stateId: snapshot.stateId } : {},
|
|
4213
|
-
...snapshot.version ? { version: snapshot.version } : {},
|
|
4214
|
-
freshness,
|
|
4215
|
-
interfaceIds: snapshot.interfaces.map((e) => e.id)
|
|
4216
|
-
};
|
|
4217
|
-
});
|
|
4218
|
-
}
|
|
4219
|
-
function surfaceContentKey(snapshot) {
|
|
4220
|
-
const { stateId, generatedAt, origin, ...content } = snapshot;
|
|
4221
|
-
return JSON.stringify(content);
|
|
4222
|
-
}
|
|
4223
|
-
function checkChildSurfaceFreshness(rootDir = getProjectRoot()) {
|
|
4224
|
-
const system = loadSystemSpec();
|
|
4225
|
-
if (!system) return [];
|
|
4226
|
-
const children = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::"));
|
|
4227
|
-
if (!children.length) return [];
|
|
4228
|
-
const current2 = surfaceContentKey(projectChildSurface());
|
|
4229
|
-
const issues = [];
|
|
4230
|
-
for (const child of children) {
|
|
4231
|
-
const childDir = path11.resolve(rootDir, child.projectPath);
|
|
4232
|
-
const held = getSnapshot(system.name, childDir);
|
|
4233
|
-
if (!held || held.origin !== "generated") continue;
|
|
4234
|
-
if (surfaceContentKey(held) !== current2) {
|
|
4235
|
-
issues.push({
|
|
4236
|
-
severity: "warning",
|
|
4237
|
-
code: "SURFACE_STALE",
|
|
4238
|
-
message: `Chained child "${child.id}" holds a parent surface snapshot whose contracts no longer match the current tree \u2014 rerun \`wairon surface generate-children\` so the child validates against the current surface.`,
|
|
4239
|
-
specId: child.id
|
|
4240
|
-
});
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
return issues;
|
|
4244
|
-
}
|
|
4245
|
-
var fs9, path11, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
|
|
4246
|
-
var init_surfaces = __esm({
|
|
4247
|
-
"src/core/surfaces.ts"() {
|
|
4248
|
-
"use strict";
|
|
4249
|
-
fs9 = __toESM(require("fs"));
|
|
4250
|
-
path11 = __toESM(require("path"));
|
|
4251
|
-
init_fs();
|
|
4252
|
-
init_yaml();
|
|
4253
|
-
init_filenames();
|
|
4254
|
-
init_models();
|
|
4255
|
-
init_specs2();
|
|
4256
|
-
init_statehash();
|
|
4257
|
-
init_type_analysis();
|
|
4258
|
-
init_openapi();
|
|
4259
|
-
SURFACES_DIRNAME = "surfaces";
|
|
4260
|
-
CROSS_BOUNDARY_TARGETS = /* @__PURE__ */ new Set(["Portal", "Gateway", "Observer"]);
|
|
4261
|
-
}
|
|
4262
|
-
});
|
|
4263
|
-
|
|
4264
3550
|
// src/core/rules/namespace.ts
|
|
4265
3551
|
function isExternalNamespaceRef(ctx, ref) {
|
|
4266
3552
|
if (ref.startsWith("::") || ref.startsWith("super::")) return true;
|
|
@@ -4268,21 +3554,36 @@ function isExternalNamespaceRef(ctx, ref) {
|
|
|
4268
3554
|
if (sep9 === -1) return false;
|
|
4269
3555
|
return !ctx.subsystemIds.has(ref.slice(0, sep9));
|
|
4270
3556
|
}
|
|
4271
|
-
function resolveSurfaceRef(ctx, ref) {
|
|
3557
|
+
function resolveSurfaceRef(ctx, ref, fromSubsystem) {
|
|
4272
3558
|
const local = ref.split("::").filter((seg) => seg && seg !== "super").pop();
|
|
4273
3559
|
if (!local) return null;
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
3560
|
+
const mountPools = fromSubsystem ? enclosingMounts(ctx, fromSubsystem).reverse().map((ns) => ctx.mountSurfaceSnapshots.find((m) => m.namespace === ns)?.snapshots ?? []) : [];
|
|
3561
|
+
for (const pool of [...mountPools, ctx.surfaceSnapshots]) {
|
|
3562
|
+
for (const snapshot of pool) {
|
|
3563
|
+
const entry = snapshot.interfaces.find((e) => e.component === local || e.id === local);
|
|
3564
|
+
if (entry) return { snapshot, entry };
|
|
3565
|
+
}
|
|
4277
3566
|
}
|
|
4278
3567
|
return null;
|
|
4279
3568
|
}
|
|
4280
|
-
|
|
3569
|
+
function enclosingMounts(ctx, subsystemId) {
|
|
3570
|
+
const mounts = [];
|
|
3571
|
+
let prefix = "";
|
|
3572
|
+
for (const segment of subsystemId.split("::")) {
|
|
3573
|
+
prefix = prefix ? `${prefix}::${segment}` : segment;
|
|
3574
|
+
if (ctx.subsystems.some((s) => s.id === prefix && s.projectPath)) mounts.push(prefix);
|
|
3575
|
+
}
|
|
3576
|
+
return mounts;
|
|
3577
|
+
}
|
|
3578
|
+
function isCollapsedCrossTreeRef(ctx, ref, fromSubsystem) {
|
|
3579
|
+
const nearest = enclosingMounts(ctx, fromSubsystem).pop();
|
|
3580
|
+
return nearest !== void 0 && ref !== nearest && !ref.startsWith(`${nearest}::`);
|
|
3581
|
+
}
|
|
3582
|
+
var roundtripRule, namespaceHygieneRule;
|
|
4281
3583
|
var init_namespace = __esm({
|
|
4282
3584
|
"src/core/rules/namespace.ts"() {
|
|
4283
3585
|
"use strict";
|
|
4284
3586
|
init_specs2();
|
|
4285
|
-
init_surfaces();
|
|
4286
3587
|
roundtripRule = {
|
|
4287
3588
|
name: "roundtrip-serialization",
|
|
4288
3589
|
description: "Every loaded spec must re-serialize through the exact writer pipeline (same relativization, same schema, no I/O) \u2014 validate must predict every refusal that lock's status promotion or any later save would otherwise raise mid-write.",
|
|
@@ -4295,18 +3596,6 @@ var init_namespace = __esm({
|
|
|
4295
3596
|
}
|
|
4296
3597
|
}
|
|
4297
3598
|
};
|
|
4298
|
-
surfaceFreshnessRule = {
|
|
4299
|
-
name: "surface-freshness",
|
|
4300
|
-
description: "Every chained child's stored parent-surface snapshot must match the parent's CURRENT exported contracts \u2014 a drifted snapshot means the child validates standalone against a stale truth. Regenerate with `wairon surface generate-children`.",
|
|
4301
|
-
codes: [
|
|
4302
|
-
{ code: "SURFACE_STALE", defaultSeverity: "warning", summary: "A chained child holds a parent surface snapshot whose contracts no longer match the current tree" }
|
|
4303
|
-
],
|
|
4304
|
-
check(ctx) {
|
|
4305
|
-
for (const issue2 of checkChildSurfaceFreshness()) {
|
|
4306
|
-
ctx.addIssue("warning", "SURFACE_STALE", issue2.message, issue2.specId);
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
};
|
|
4310
3599
|
namespaceHygieneRule = {
|
|
4311
3600
|
name: "namespace-hygiene",
|
|
4312
3601
|
description: 'Ids must stay resolvable in the :: namespace grammar: no id segment may be the reserved keyword "super", and a subproject-local name must not shadow a root-level subsystem id \u2014 a bare reference to a shadowed name silently anchors to the ROOT subsystem, so the local spec becomes unaddressable.',
|
|
@@ -4413,11 +3702,13 @@ var init_contracts = __esm({
|
|
|
4413
3702
|
continue;
|
|
4414
3703
|
}
|
|
4415
3704
|
const isCrossTreeForm = isExternalNamespaceRef(ctx, step.targetComponent);
|
|
3705
|
+
const fromSubsystem = ctx.componentMap.get(contract.component)?.subsystem;
|
|
3706
|
+
const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && isCollapsedCrossTreeRef(ctx, step.targetComponent, fromSubsystem);
|
|
4416
3707
|
if (step.type === "dispatch") {
|
|
4417
3708
|
const dispatchTarget = ctx.componentMap.get(step.targetComponent);
|
|
4418
3709
|
if (!dispatchTarget) {
|
|
4419
|
-
if (isCrossTreeForm) {
|
|
4420
|
-
const resolved = resolveSurfaceRef(ctx, step.targetComponent);
|
|
3710
|
+
if (isCrossTreeForm || isCollapsedForm) {
|
|
3711
|
+
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
4421
3712
|
if (resolved) {
|
|
4422
3713
|
if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
|
|
4423
3714
|
ctx.addIssue(
|
|
@@ -4431,10 +3722,12 @@ var init_contracts = __esm({
|
|
|
4431
3722
|
}
|
|
4432
3723
|
continue;
|
|
4433
3724
|
}
|
|
3725
|
+
}
|
|
3726
|
+
if (isCrossTreeForm) {
|
|
4434
3727
|
ctx.addIssue(
|
|
4435
3728
|
"warning",
|
|
4436
3729
|
"CROSS_TREE_REF_UNRESOLVED",
|
|
4437
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches through cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, or import
|
|
3730
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches through cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
4438
3731
|
impl.id,
|
|
4439
3732
|
isDraftCtx
|
|
4440
3733
|
);
|
|
@@ -4474,8 +3767,8 @@ var init_contracts = __esm({
|
|
|
4474
3767
|
}
|
|
4475
3768
|
const targetComp = ctx.componentMap.get(step.targetComponent);
|
|
4476
3769
|
if (!targetComp) {
|
|
4477
|
-
if (isCrossTreeForm) {
|
|
4478
|
-
const resolved = resolveSurfaceRef(ctx, step.targetComponent);
|
|
3770
|
+
if (isCrossTreeForm || isCollapsedForm) {
|
|
3771
|
+
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
4479
3772
|
if (resolved) {
|
|
4480
3773
|
const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
|
|
4481
3774
|
if (!surfaceMethod) {
|
|
@@ -4504,10 +3797,12 @@ var init_contracts = __esm({
|
|
|
4504
3797
|
}
|
|
4505
3798
|
continue;
|
|
4506
3799
|
}
|
|
3800
|
+
}
|
|
3801
|
+
if (isCrossTreeForm) {
|
|
4507
3802
|
ctx.addIssue(
|
|
4508
3803
|
"warning",
|
|
4509
3804
|
"CROSS_TREE_REF_UNRESOLVED",
|
|
4510
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, or import
|
|
3805
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
4511
3806
|
impl.id,
|
|
4512
3807
|
isDraftCtx
|
|
4513
3808
|
);
|
|
@@ -4995,7 +4290,7 @@ function resolveTypeScript(projectRoot2) {
|
|
|
4995
4290
|
return cached.ts;
|
|
4996
4291
|
}
|
|
4997
4292
|
let ts = null;
|
|
4998
|
-
const bases = [
|
|
4293
|
+
const bases = [path11.join(projectRoot2, "package.json"), __filename];
|
|
4999
4294
|
for (const base of bases) {
|
|
5000
4295
|
try {
|
|
5001
4296
|
const req = (0, import_module2.createRequire)(base);
|
|
@@ -5151,7 +4446,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
5151
4446
|
}
|
|
5152
4447
|
function resolveRelativeModule(fromFile, specifier) {
|
|
5153
4448
|
if (!specifier.startsWith(".")) return null;
|
|
5154
|
-
const base =
|
|
4449
|
+
const base = path11.resolve(path11.dirname(fromFile), specifier);
|
|
5155
4450
|
const candidates = [
|
|
5156
4451
|
base,
|
|
5157
4452
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -5159,12 +4454,12 @@ function resolveRelativeModule(fromFile, specifier) {
|
|
|
5159
4454
|
`${base}.ts`,
|
|
5160
4455
|
`${base}.tsx`,
|
|
5161
4456
|
`${base}.js`,
|
|
5162
|
-
|
|
5163
|
-
|
|
4457
|
+
path11.join(base, "index.ts"),
|
|
4458
|
+
path11.join(base, "index.js")
|
|
5164
4459
|
];
|
|
5165
4460
|
for (const c of candidates) {
|
|
5166
4461
|
try {
|
|
5167
|
-
if (
|
|
4462
|
+
if (fs9.statSync(c).isFile()) return c;
|
|
5168
4463
|
} catch {
|
|
5169
4464
|
}
|
|
5170
4465
|
}
|
|
@@ -5174,12 +4469,12 @@ function chaseStarExports(ts, facts, filePath, projectRoot2, visited, exactCache
|
|
|
5174
4469
|
for (const spec of facts.starExports) {
|
|
5175
4470
|
const target = resolveRelativeModule(filePath, spec);
|
|
5176
4471
|
if (!target || visited.has(target)) continue;
|
|
5177
|
-
if (
|
|
4472
|
+
if (path11.relative(projectRoot2, target).startsWith("..")) continue;
|
|
5178
4473
|
visited.add(target);
|
|
5179
4474
|
let targetFacts = exactCache.get(target);
|
|
5180
4475
|
if (targetFacts === void 0) {
|
|
5181
4476
|
try {
|
|
5182
|
-
targetFacts = walkExact(ts,
|
|
4477
|
+
targetFacts = walkExact(ts, fs9.readFileSync(target, "utf8"), target);
|
|
5183
4478
|
} catch {
|
|
5184
4479
|
targetFacts = null;
|
|
5185
4480
|
}
|
|
@@ -5219,23 +4514,23 @@ function buildCodeModel(implementations, projectRoot2) {
|
|
|
5219
4514
|
if (seen.has(sourcePath)) continue;
|
|
5220
4515
|
seen.add(sourcePath);
|
|
5221
4516
|
const empty = { declaredNames: [], anchoredNames: [], exportedNames: [], imports: [], reexports: [] };
|
|
5222
|
-
if (
|
|
4517
|
+
if (path11.isAbsolute(sourcePath) || path11.normalize(sourcePath).split(path11.sep)[0] === "..") {
|
|
5223
4518
|
files.push({ path: sourcePath, status: "escaped", ...empty });
|
|
5224
4519
|
continue;
|
|
5225
4520
|
}
|
|
5226
|
-
const absolute =
|
|
5227
|
-
if (
|
|
4521
|
+
const absolute = path11.resolve(projectRoot2, sourcePath);
|
|
4522
|
+
if (path11.relative(projectRoot2, absolute).startsWith("..")) {
|
|
5228
4523
|
files.push({ path: sourcePath, status: "escaped", ...empty });
|
|
5229
4524
|
continue;
|
|
5230
4525
|
}
|
|
5231
4526
|
let buffer;
|
|
5232
4527
|
try {
|
|
5233
|
-
const stat =
|
|
4528
|
+
const stat = fs9.statSync(absolute);
|
|
5234
4529
|
if (!stat.isFile()) {
|
|
5235
4530
|
files.push({ path: sourcePath, status: "missing", ...empty });
|
|
5236
4531
|
continue;
|
|
5237
4532
|
}
|
|
5238
|
-
buffer =
|
|
4533
|
+
buffer = fs9.readFileSync(absolute);
|
|
5239
4534
|
} catch {
|
|
5240
4535
|
files.push({ path: sourcePath, status: "missing", ...empty });
|
|
5241
4536
|
continue;
|
|
@@ -5245,7 +4540,7 @@ function buildCodeModel(implementations, projectRoot2) {
|
|
|
5245
4540
|
continue;
|
|
5246
4541
|
}
|
|
5247
4542
|
const text2 = buffer.toString("utf8");
|
|
5248
|
-
const language = EXTENSION_LANGUAGE[
|
|
4543
|
+
const language = EXTENSION_LANGUAGE[path11.extname(sourcePath).toLowerCase()];
|
|
5249
4544
|
let analyzed;
|
|
5250
4545
|
if (language === "typescript" || language === "javascript") {
|
|
5251
4546
|
const ts = resolveTypeScript(projectRoot2);
|
|
@@ -5279,12 +4574,12 @@ function buildCodeModel(implementations, projectRoot2) {
|
|
|
5279
4574
|
}
|
|
5280
4575
|
return { files, projectRoot: projectRoot2 };
|
|
5281
4576
|
}
|
|
5282
|
-
var
|
|
4577
|
+
var fs9, path11, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
|
|
5283
4578
|
var init_source_analysis = __esm({
|
|
5284
4579
|
"src/core/source-analysis.ts"() {
|
|
5285
4580
|
"use strict";
|
|
5286
|
-
|
|
5287
|
-
|
|
4581
|
+
fs9 = __toESM(require("fs"));
|
|
4582
|
+
path11 = __toESM(require("path"));
|
|
5288
4583
|
import_module2 = require("module");
|
|
5289
4584
|
EXTENSION_LANGUAGE = {
|
|
5290
4585
|
".ts": "typescript",
|
|
@@ -6009,8 +5304,9 @@ var init_stereotype_deps = __esm({
|
|
|
6009
5304
|
for (const depId of dependencies) {
|
|
6010
5305
|
const depComp = ctx.componentMap.get(depId);
|
|
6011
5306
|
if (!depComp) {
|
|
6012
|
-
|
|
6013
|
-
|
|
5307
|
+
const external = isExternalNamespaceRef(ctx, depId);
|
|
5308
|
+
if (external || isCollapsedCrossTreeRef(ctx, depId, comp.subsystem)) {
|
|
5309
|
+
const resolved = resolveSurfaceRef(ctx, depId, comp.subsystem);
|
|
6014
5310
|
if (resolved) {
|
|
6015
5311
|
if (comp.componentType !== "Adapter") {
|
|
6016
5312
|
ctx.addIssue(
|
|
@@ -6024,14 +5320,16 @@ var init_stereotype_deps = __esm({
|
|
|
6024
5320
|
}
|
|
6025
5321
|
continue;
|
|
6026
5322
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
5323
|
+
if (external) {
|
|
5324
|
+
ctx.addIssue(
|
|
5325
|
+
"warning",
|
|
5326
|
+
"CROSS_TREE_REF_UNRESOLVED",
|
|
5327
|
+
`Component "${comp.id}" depends on cross-tree component "${depId}", and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
5328
|
+
comp.id,
|
|
5329
|
+
isDraftCtx
|
|
5330
|
+
);
|
|
5331
|
+
continue;
|
|
5332
|
+
}
|
|
6035
5333
|
}
|
|
6036
5334
|
ctx.addIssue(
|
|
6037
5335
|
"error",
|
|
@@ -6101,12 +5399,12 @@ var init_stereotype_deps = __esm({
|
|
|
6101
5399
|
}
|
|
6102
5400
|
}
|
|
6103
5401
|
if (comp.componentType === "Specialist") {
|
|
6104
|
-
const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Supervisor"];
|
|
5402
|
+
const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Registry", "Supervisor", "Actor"];
|
|
6105
5403
|
if (forbiddenTypes.includes(depComp.componentType)) {
|
|
6106
5404
|
ctx.addIssue(
|
|
6107
5405
|
"error",
|
|
6108
5406
|
"ARCHITECTURE_VIOLATION_SPECIALIST_DEP",
|
|
6109
|
-
`Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are
|
|
5407
|
+
`Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are pure capabilities \u2014 they may use Repository facades, Indexes, and Adapters, but never workflow/runtime blocks (Orchestrators, Supervisors, Actors) and never persistence directly (Stores, Registries \u2014 all storage, even in-memory, is reached through a Repository facade), nor Portals or Observers.` + (depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : ""),
|
|
6110
5408
|
comp.id,
|
|
6111
5409
|
isDraftCtx || ctx.isComponentDraft(depComp.id)
|
|
6112
5410
|
);
|
|
@@ -8652,7 +7950,7 @@ var init_naming = __esm({
|
|
|
8652
7950
|
// src/core/rules/integration-conformance.ts
|
|
8653
7951
|
function resolveAgainst(mapped, fromFile, specifier) {
|
|
8654
7952
|
if (!specifier.startsWith(".")) return null;
|
|
8655
|
-
const joined = normalizePath(
|
|
7953
|
+
const joined = normalizePath(path12.posix.normalize(path12.posix.join(path12.posix.dirname(fromFile), specifier)));
|
|
8656
7954
|
const candidates = [
|
|
8657
7955
|
joined,
|
|
8658
7956
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -8668,11 +7966,11 @@ function resolveAgainst(mapped, fromFile, specifier) {
|
|
|
8668
7966
|
}
|
|
8669
7967
|
return null;
|
|
8670
7968
|
}
|
|
8671
|
-
var
|
|
7969
|
+
var path12, normalizePath, integrationConformanceRule;
|
|
8672
7970
|
var init_integration_conformance = __esm({
|
|
8673
7971
|
"src/core/rules/integration-conformance.ts"() {
|
|
8674
7972
|
"use strict";
|
|
8675
|
-
|
|
7973
|
+
path12 = __toESM(require("path"));
|
|
8676
7974
|
init_source_analysis();
|
|
8677
7975
|
init_conformance();
|
|
8678
7976
|
normalizePath = normalizeSourcePath;
|
|
@@ -8876,7 +8174,7 @@ var init_hidden_state = __esm({
|
|
|
8876
8174
|
// src/core/rules/dependency-conformance.ts
|
|
8877
8175
|
function resolveAgainst2(mapped, fromFile, specifier) {
|
|
8878
8176
|
if (!specifier.startsWith(".")) return null;
|
|
8879
|
-
const joined = normalizePath2(
|
|
8177
|
+
const joined = normalizePath2(path13.posix.normalize(path13.posix.join(path13.posix.dirname(fromFile), specifier)));
|
|
8880
8178
|
const candidates = [
|
|
8881
8179
|
joined,
|
|
8882
8180
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -8892,11 +8190,11 @@ function resolveAgainst2(mapped, fromFile, specifier) {
|
|
|
8892
8190
|
}
|
|
8893
8191
|
return null;
|
|
8894
8192
|
}
|
|
8895
|
-
var
|
|
8193
|
+
var path13, normalizePath2, dependencyConformanceRule;
|
|
8896
8194
|
var init_dependency_conformance = __esm({
|
|
8897
8195
|
"src/core/rules/dependency-conformance.ts"() {
|
|
8898
8196
|
"use strict";
|
|
8899
|
-
|
|
8197
|
+
path13 = __toESM(require("path"));
|
|
8900
8198
|
init_source_analysis();
|
|
8901
8199
|
init_conformance();
|
|
8902
8200
|
normalizePath2 = normalizeSourcePath;
|
|
@@ -9378,17 +8676,11 @@ function buildRuleContext(opts) {
|
|
|
9378
8676
|
for (const i of interfaces) collectAllows(i.id, i.lint);
|
|
9379
8677
|
for (const im of implementations) collectAllows(im.id, im.lint);
|
|
9380
8678
|
for (const t of types) collectAllows(t.id, t.lint);
|
|
9381
|
-
const
|
|
8679
|
+
const knownIssueCodes2 = /* @__PURE__ */ new Set([
|
|
9382
8680
|
...[...SDD_RULES, ...extensions.rules].flatMap((r) => r.codes.map((c) => c.code)),
|
|
9383
8681
|
// Declarative assertions bring their own namespaced codes — lint.allow
|
|
9384
8682
|
// and severity overrides treat them exactly like builtins.
|
|
9385
|
-
...extensions.assertions.map((a) => a.fullCode)
|
|
9386
|
-
// Entry-point emitted codes: validateSddTree's chained-subproject pass
|
|
9387
|
-
// raises these AFTER the rule run (it post-processes the aggregated issue
|
|
9388
|
-
// list), so no registered rule declares them — but lint.allow validation
|
|
9389
|
-
// must still recognize them as real codes.
|
|
9390
|
-
"CHAINED_SUBPROJECT_CONTEXT",
|
|
9391
|
-
"UNVERIFIED_EXTERNAL_REF"
|
|
8683
|
+
...extensions.assertions.map((a) => a.fullCode)
|
|
9392
8684
|
]);
|
|
9393
8685
|
const addIssue = (defaultSeverity, code, message, specId, isDraftContext, surfaceResolved) => {
|
|
9394
8686
|
if (scopeSubsystem && specId && !isSpecInScope(specId)) {
|
|
@@ -9444,9 +8736,10 @@ function buildRuleContext(opts) {
|
|
|
9444
8736
|
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [] },
|
|
9445
8737
|
variants: opts.variants ?? [],
|
|
9446
8738
|
surfaceSnapshots: opts.surfaceSnapshots ?? [],
|
|
8739
|
+
mountSurfaceSnapshots: opts.mountSurfaceSnapshots ?? [],
|
|
9447
8740
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
9448
8741
|
lintAllows,
|
|
9449
|
-
knownIssueCodes,
|
|
8742
|
+
knownIssueCodes: knownIssueCodes2,
|
|
9450
8743
|
addIssue
|
|
9451
8744
|
};
|
|
9452
8745
|
}
|
|
@@ -9501,7 +8794,6 @@ var init_rules = __esm({
|
|
|
9501
8794
|
// explain many downstream findings, so surface them early in the list.
|
|
9502
8795
|
namespaceHygieneRule,
|
|
9503
8796
|
roundtripRule,
|
|
9504
|
-
surfaceFreshnessRule,
|
|
9505
8797
|
typeReferencesRule,
|
|
9506
8798
|
contractsRule,
|
|
9507
8799
|
// Vocabulary check right after contracts: an unknown token explains why the
|
|
@@ -9771,14 +9063,14 @@ function describeApprover(who) {
|
|
|
9771
9063
|
}
|
|
9772
9064
|
function readLockRecord() {
|
|
9773
9065
|
try {
|
|
9774
|
-
return normalizeRecord(JSON.parse(
|
|
9066
|
+
return normalizeRecord(JSON.parse(fs10.readFileSync(lockPath(), "utf8")));
|
|
9775
9067
|
} catch {
|
|
9776
9068
|
return null;
|
|
9777
9069
|
}
|
|
9778
9070
|
}
|
|
9779
9071
|
function readLockRecordAt(root) {
|
|
9780
9072
|
try {
|
|
9781
|
-
return normalizeRecord(JSON.parse(
|
|
9073
|
+
return normalizeRecord(JSON.parse(fs10.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
|
|
9782
9074
|
} catch {
|
|
9783
9075
|
return null;
|
|
9784
9076
|
}
|
|
@@ -9789,10 +9081,10 @@ function normalizeRecord(raw) {
|
|
|
9789
9081
|
}
|
|
9790
9082
|
function writeLockRecord(record2) {
|
|
9791
9083
|
const p = lockPath();
|
|
9792
|
-
|
|
9084
|
+
fs10.mkdirSync(path14.dirname(p), { recursive: true });
|
|
9793
9085
|
const tmp = `${p}.tmp`;
|
|
9794
|
-
|
|
9795
|
-
|
|
9086
|
+
fs10.writeFileSync(tmp, JSON.stringify(withSortedMaps(record2), null, 2) + "\n");
|
|
9087
|
+
fs10.renameSync(tmp, p);
|
|
9796
9088
|
}
|
|
9797
9089
|
function withSortedMaps(record2) {
|
|
9798
9090
|
const sorted = (m) => {
|
|
@@ -9809,12 +9101,12 @@ function withSortedMaps(record2) {
|
|
|
9809
9101
|
...children ? { children } : {}
|
|
9810
9102
|
};
|
|
9811
9103
|
}
|
|
9812
|
-
var
|
|
9104
|
+
var fs10, path14;
|
|
9813
9105
|
var init_lockfile = __esm({
|
|
9814
9106
|
"src/core/lockfile.ts"() {
|
|
9815
9107
|
"use strict";
|
|
9816
|
-
|
|
9817
|
-
|
|
9108
|
+
fs10 = __toESM(require("fs"));
|
|
9109
|
+
path14 = __toESM(require("path"));
|
|
9818
9110
|
init_fs();
|
|
9819
9111
|
}
|
|
9820
9112
|
});
|
|
@@ -10547,14 +9839,14 @@ function embedJson(value) {
|
|
|
10547
9839
|
}
|
|
10548
9840
|
function loadCytoscapeLib() {
|
|
10549
9841
|
const candidates = [
|
|
10550
|
-
|
|
9842
|
+
path15.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
|
|
10551
9843
|
// src/core & dist/cli
|
|
10552
|
-
|
|
9844
|
+
path15.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
|
|
10553
9845
|
// dist (library entry)
|
|
10554
9846
|
];
|
|
10555
9847
|
for (const p of candidates) {
|
|
10556
|
-
if (
|
|
10557
|
-
return
|
|
9848
|
+
if (fs11.existsSync(p)) {
|
|
9849
|
+
return fs11.readFileSync(p, "utf-8").replace(/<\/script/gi, "<\\/script");
|
|
10558
9850
|
}
|
|
10559
9851
|
}
|
|
10560
9852
|
throw new Error("Vendored cytoscape bundle not found (templates/canvas/cytoscape.min.js) \u2014 the wairon installation is incomplete.");
|
|
@@ -10566,12 +9858,12 @@ function renderCanvasHtml(model) {
|
|
|
10566
9858
|
function escapeHtml(s) {
|
|
10567
9859
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
10568
9860
|
}
|
|
10569
|
-
var
|
|
9861
|
+
var fs11, path15, CANVAS_TEMPLATE;
|
|
10570
9862
|
var init_canvas = __esm({
|
|
10571
9863
|
"src/core/canvas.ts"() {
|
|
10572
9864
|
"use strict";
|
|
10573
|
-
|
|
10574
|
-
|
|
9865
|
+
fs11 = __toESM(require("fs"));
|
|
9866
|
+
path15 = __toESM(require("path"));
|
|
10575
9867
|
init_specs2();
|
|
10576
9868
|
init_type_analysis();
|
|
10577
9869
|
init_diagram_export();
|
|
@@ -14640,110 +13932,822 @@ var MODEL = __MODEL_JSON__;
|
|
|
14640
13932
|
}
|
|
14641
13933
|
panel.innerHTML = '<div class="head">' + head + '</div><div class="body">' + body + '</div>';
|
|
14642
13934
|
|
|
14643
|
-
var navs = panel.querySelectorAll('[data-kind]');
|
|
14644
|
-
for (var i = 0; i < navs.length; i++) {
|
|
14645
|
-
(function (n) {
|
|
14646
|
-
n.addEventListener('click', function () { select(n.getAttribute('data-kind'), n.getAttribute('data-id'), true); });
|
|
14647
|
-
n.addEventListener('mouseenter', function () {
|
|
14648
|
-
var node = nodeForRef(n.getAttribute('data-kind'), n.getAttribute('data-id'));
|
|
14649
|
-
if (node.length) node.addClass('hoverhl');
|
|
14650
|
-
});
|
|
14651
|
-
n.addEventListener('mouseleave', function () { cy.nodes().removeClass('hoverhl'); });
|
|
14652
|
-
})(navs[i]);
|
|
14653
|
-
}
|
|
14654
|
-
var opens = panel.querySelectorAll('[data-open-kind]');
|
|
14655
|
-
for (var k = 0; k < opens.length; k++) {
|
|
14656
|
-
(function (b) {
|
|
14657
|
-
b.addEventListener('click', function () { navigateTo(b.getAttribute('data-open-kind'), b.getAttribute('data-open-id')); });
|
|
14658
|
-
})(opens[k]);
|
|
14659
|
-
}
|
|
14660
|
-
var typesOpen = document.getElementById('openTypesBtn');
|
|
14661
|
-
if (typesOpen && typesOpen.addEventListener && panel.innerHTML.indexOf('openTypesBtn') >= 0) {
|
|
14662
|
-
typesOpen.addEventListener('click', function () { navigateTo('types', null); });
|
|
13935
|
+
var navs = panel.querySelectorAll('[data-kind]');
|
|
13936
|
+
for (var i = 0; i < navs.length; i++) {
|
|
13937
|
+
(function (n) {
|
|
13938
|
+
n.addEventListener('click', function () { select(n.getAttribute('data-kind'), n.getAttribute('data-id'), true); });
|
|
13939
|
+
n.addEventListener('mouseenter', function () {
|
|
13940
|
+
var node = nodeForRef(n.getAttribute('data-kind'), n.getAttribute('data-id'));
|
|
13941
|
+
if (node.length) node.addClass('hoverhl');
|
|
13942
|
+
});
|
|
13943
|
+
n.addEventListener('mouseleave', function () { cy.nodes().removeClass('hoverhl'); });
|
|
13944
|
+
})(navs[i]);
|
|
13945
|
+
}
|
|
13946
|
+
var opens = panel.querySelectorAll('[data-open-kind]');
|
|
13947
|
+
for (var k = 0; k < opens.length; k++) {
|
|
13948
|
+
(function (b) {
|
|
13949
|
+
b.addEventListener('click', function () { navigateTo(b.getAttribute('data-open-kind'), b.getAttribute('data-open-id')); });
|
|
13950
|
+
})(opens[k]);
|
|
13951
|
+
}
|
|
13952
|
+
var typesOpen = document.getElementById('openTypesBtn');
|
|
13953
|
+
if (typesOpen && typesOpen.addEventListener && panel.innerHTML.indexOf('openTypesBtn') >= 0) {
|
|
13954
|
+
typesOpen.addEventListener('click', function () { navigateTo('types', null); });
|
|
13955
|
+
}
|
|
13956
|
+
var oapis = panel.querySelectorAll('[data-openapi-tag]');
|
|
13957
|
+
for (var oi = 0; oi < oapis.length; oi++) {
|
|
13958
|
+
(function (b) {
|
|
13959
|
+
b.addEventListener('click', function () {
|
|
13960
|
+
var tag = b.getAttribute('data-openapi-tag') || '';
|
|
13961
|
+
if (typeof opts !== 'undefined' && opts && opts.onOpenApi) { opts.onOpenApi(tag); return; }
|
|
13962
|
+
var href = openApiSiblingHref();
|
|
13963
|
+
if (href) window.open(href + (tag ? '#/' + tag : ''), '_blank', 'noopener');
|
|
13964
|
+
});
|
|
13965
|
+
})(oapis[oi]);
|
|
13966
|
+
}
|
|
13967
|
+
var ospecs = panel.querySelectorAll('[data-openspec-kind]');
|
|
13968
|
+
for (var si = 0; si < ospecs.length; si++) {
|
|
13969
|
+
(function (b) {
|
|
13970
|
+
b.addEventListener('click', function () {
|
|
13971
|
+
if (typeof opts !== 'undefined' && opts && typeof opts.onOpenSpec === 'function') {
|
|
13972
|
+
opts.onOpenSpec(b.getAttribute('data-openspec-kind'), b.getAttribute('data-openspec-id') || '');
|
|
13973
|
+
}
|
|
13974
|
+
});
|
|
13975
|
+
})(ospecs[si]);
|
|
13976
|
+
}
|
|
13977
|
+
var flows = panel.querySelectorAll('[data-flow-comp]');
|
|
13978
|
+
for (var j = 0; j < flows.length; j++) {
|
|
13979
|
+
(function (b) {
|
|
13980
|
+
b.addEventListener('click', function (ev) {
|
|
13981
|
+
if (ev && ev.stopPropagation) ev.stopPropagation();
|
|
13982
|
+
openFlow(b.getAttribute('data-flow-comp'), b.getAttribute('data-flow-method'), b.getAttribute('data-flow-mode') || 'flow');
|
|
13983
|
+
});
|
|
13984
|
+
})(flows[j]);
|
|
13985
|
+
}
|
|
13986
|
+
}
|
|
13987
|
+
|
|
13988
|
+
renderPanel();
|
|
13989
|
+
// Stage G: a deep link may focus a component and/or open a method's narrative
|
|
13990
|
+
// modal once the seeded view + DOM + cy graph exist. The host parses the URL hash
|
|
13991
|
+
// into opts.initialSelect / opts.initialFlow \u2014 both carry the component id, so this
|
|
13992
|
+
// works whether the seeded view is the component itself or its parent subsystem
|
|
13993
|
+
// (a leaf component has no meaningful "inside", so Specs deep-links open the parent
|
|
13994
|
+
// and focus the component here).
|
|
13995
|
+
(function () {
|
|
13996
|
+
if (typeof opts === 'undefined' || !opts) return;
|
|
13997
|
+
var f = opts.initialFlow, s = opts.initialSelect;
|
|
13998
|
+
var focusComp = (f && f.comp) || (s && s.comp);
|
|
13999
|
+
if (focusComp && compById[focusComp]) {
|
|
14000
|
+
try { select('component', focusComp, true); } catch (e) { /* ignore */ }
|
|
14001
|
+
}
|
|
14002
|
+
if (f && f.comp && f.method && compById[f.comp]) {
|
|
14003
|
+
try { openFlow(f.comp, f.method, f.mode === 'steps' ? 'steps' : 'flow'); } catch (e) { /* ignore */ }
|
|
14004
|
+
}
|
|
14005
|
+
})();
|
|
14006
|
+
})();
|
|
14007
|
+
</script>
|
|
14008
|
+
</body>
|
|
14009
|
+
</html>
|
|
14010
|
+
`;
|
|
14011
|
+
}
|
|
14012
|
+
});
|
|
14013
|
+
|
|
14014
|
+
// src/core/rules/repository.ts
|
|
14015
|
+
function addRule(rule) {
|
|
14016
|
+
ruleSet.push(rule);
|
|
14017
|
+
}
|
|
14018
|
+
function listRules() {
|
|
14019
|
+
return ruleSet;
|
|
14020
|
+
}
|
|
14021
|
+
function registerBuiltinRules() {
|
|
14022
|
+
ruleSet = [];
|
|
14023
|
+
for (const rule of SDD_RULES) addRule(rule);
|
|
14024
|
+
}
|
|
14025
|
+
function registerPackRules(packRules) {
|
|
14026
|
+
for (const rule of packRules) addRule(rule);
|
|
14027
|
+
}
|
|
14028
|
+
function ruleSequence() {
|
|
14029
|
+
const base = ruleSet.filter((r) => r !== lintAllowsRule);
|
|
14030
|
+
return ruleSet.includes(lintAllowsRule) ? [...base, lintAllowsRule] : base;
|
|
14031
|
+
}
|
|
14032
|
+
function specScopedRules() {
|
|
14033
|
+
return ruleSequence().filter((r) => r.scope === "spec");
|
|
14034
|
+
}
|
|
14035
|
+
function knownIssueCodes() {
|
|
14036
|
+
return listRules().flatMap((r) => r.codes);
|
|
14037
|
+
}
|
|
14038
|
+
var ruleSet;
|
|
14039
|
+
var init_repository = __esm({
|
|
14040
|
+
"src/core/rules/repository.ts"() {
|
|
14041
|
+
"use strict";
|
|
14042
|
+
init_rules();
|
|
14043
|
+
init_lint_allows();
|
|
14044
|
+
ruleSet = [];
|
|
14045
|
+
}
|
|
14046
|
+
});
|
|
14047
|
+
|
|
14048
|
+
// src/utils/filenames.ts
|
|
14049
|
+
function safeFilenamePart(value) {
|
|
14050
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
14051
|
+
}
|
|
14052
|
+
var init_filenames = __esm({
|
|
14053
|
+
"src/utils/filenames.ts"() {
|
|
14054
|
+
"use strict";
|
|
14055
|
+
}
|
|
14056
|
+
});
|
|
14057
|
+
|
|
14058
|
+
// src/core/openapi.ts
|
|
14059
|
+
function schemaFor(typeRef, closureIds) {
|
|
14060
|
+
const trimmed = typeRef.trim().replace(/^promise\s*<(.+)>$/i, "$1").trim();
|
|
14061
|
+
const arrayMatch = /^(.+)\[\]$/.exec(trimmed);
|
|
14062
|
+
if (arrayMatch) {
|
|
14063
|
+
return { type: "array", items: schemaFor(arrayMatch[1], closureIds) };
|
|
14064
|
+
}
|
|
14065
|
+
const lower = trimmed.toLowerCase();
|
|
14066
|
+
if (lower in PRIMITIVES) {
|
|
14067
|
+
const p = PRIMITIVES[lower];
|
|
14068
|
+
return p.type ? { ...p } : {};
|
|
14069
|
+
}
|
|
14070
|
+
const local = trimmed.split(/::|\./).pop().toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
14071
|
+
const hit = [...closureIds].find((id) => id.toLowerCase() === local);
|
|
14072
|
+
if (hit) return { $ref: `#/components/schemas/${hit}` };
|
|
14073
|
+
return { type: "object", description: `Unresolved type: ${trimmed}` };
|
|
14074
|
+
}
|
|
14075
|
+
function operationFor(method2, closureIds) {
|
|
14076
|
+
const endpoint = method2.endpoint;
|
|
14077
|
+
const httpVerb = endpoint && endpoint.transport === "HTTP" ? endpoint.method.toLowerCase() : "post";
|
|
14078
|
+
const bodyVerbs = /* @__PURE__ */ new Set(["post", "put", "patch"]);
|
|
14079
|
+
const params = method2.params ?? [];
|
|
14080
|
+
const op = {
|
|
14081
|
+
operationId: method2.name,
|
|
14082
|
+
summary: method2.description,
|
|
14083
|
+
responses: {
|
|
14084
|
+
"200": {
|
|
14085
|
+
description: method2.returns || "Success",
|
|
14086
|
+
...method2.returns && method2.returns.toLowerCase() !== "void" ? { content: { "application/json": { schema: schemaFor(method2.returns, closureIds) } } } : {}
|
|
14087
|
+
}
|
|
14088
|
+
}
|
|
14089
|
+
};
|
|
14090
|
+
if (method2.guarantees?.length) op["x-wairon-guarantees"] = method2.guarantees;
|
|
14091
|
+
if (method2.effect) op["x-wairon-effect"] = method2.effect;
|
|
14092
|
+
if (method2.ext && Object.keys(method2.ext).length) op["x-wairon-ext"] = method2.ext;
|
|
14093
|
+
if (params.length) {
|
|
14094
|
+
if (bodyVerbs.has(httpVerb)) {
|
|
14095
|
+
op.requestBody = {
|
|
14096
|
+
required: true,
|
|
14097
|
+
content: {
|
|
14098
|
+
"application/json": {
|
|
14099
|
+
schema: {
|
|
14100
|
+
type: "object",
|
|
14101
|
+
properties: Object.fromEntries(params.map((p) => [p.name, schemaFor(p.type, closureIds)])),
|
|
14102
|
+
required: params.filter((p) => !p.optional).map((p) => p.name)
|
|
14103
|
+
}
|
|
14104
|
+
}
|
|
14105
|
+
}
|
|
14106
|
+
};
|
|
14107
|
+
} else {
|
|
14108
|
+
op.parameters = params.map((p) => ({
|
|
14109
|
+
name: p.name,
|
|
14110
|
+
in: "query",
|
|
14111
|
+
required: !p.optional,
|
|
14112
|
+
...p.description ? { description: p.description } : {},
|
|
14113
|
+
schema: schemaFor(p.type, closureIds)
|
|
14114
|
+
}));
|
|
14115
|
+
}
|
|
14116
|
+
}
|
|
14117
|
+
return op;
|
|
14118
|
+
}
|
|
14119
|
+
function securitySchemeObject(auth) {
|
|
14120
|
+
if (auth.scheme === "none") return null;
|
|
14121
|
+
const desc = auth.description ? { description: auth.description } : {};
|
|
14122
|
+
switch (auth.scheme) {
|
|
14123
|
+
case "apiKey":
|
|
14124
|
+
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "X-API-Key", ...desc };
|
|
14125
|
+
case "bearer":
|
|
14126
|
+
return { type: "http", scheme: "bearer", ...auth.bearerFormat ? { bearerFormat: auth.bearerFormat } : {}, ...desc };
|
|
14127
|
+
case "basic":
|
|
14128
|
+
return { type: "http", scheme: "basic", ...desc };
|
|
14129
|
+
case "oauth2": {
|
|
14130
|
+
const flow = { scopes: Object.fromEntries((auth.scopes ?? []).map((s) => [s.name, s.description])) };
|
|
14131
|
+
if (auth.authorizationUrl) flow.authorizationUrl = auth.authorizationUrl;
|
|
14132
|
+
if (auth.tokenUrl) flow.tokenUrl = auth.tokenUrl;
|
|
14133
|
+
if (auth.refreshUrl) flow.refreshUrl = auth.refreshUrl;
|
|
14134
|
+
return { type: "oauth2", flows: { [auth.flow ?? "authorizationCode"]: flow }, ...desc };
|
|
14135
|
+
}
|
|
14136
|
+
case "openIdConnect":
|
|
14137
|
+
return { type: "openIdConnect", openIdConnectUrl: auth.openIdConnectUrl ?? "", ...desc };
|
|
14138
|
+
case "custom":
|
|
14139
|
+
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "Authorization", description: auth.description ?? auth.example ?? "Custom authentication scheme." };
|
|
14140
|
+
default:
|
|
14141
|
+
return null;
|
|
14142
|
+
}
|
|
14143
|
+
}
|
|
14144
|
+
function schemeBaseName(scheme) {
|
|
14145
|
+
return { apiKey: "ApiKeyAuth", bearer: "BearerAuth", basic: "BasicAuth", oauth2: "OAuth2", openIdConnect: "OpenIdConnect", custom: "CustomAuth" }[scheme] ?? "Auth";
|
|
14146
|
+
}
|
|
14147
|
+
function buildSecurity(entries) {
|
|
14148
|
+
const schemes = {};
|
|
14149
|
+
const nameByContent = /* @__PURE__ */ new Map();
|
|
14150
|
+
const securityByEntry = /* @__PURE__ */ new Map();
|
|
14151
|
+
for (const entry of entries) {
|
|
14152
|
+
if (!entry.auth) continue;
|
|
14153
|
+
const obj = securitySchemeObject(entry.auth);
|
|
14154
|
+
if (!obj) continue;
|
|
14155
|
+
const content = JSON.stringify(obj);
|
|
14156
|
+
let name = nameByContent.get(content);
|
|
14157
|
+
if (!name) {
|
|
14158
|
+
name = schemeBaseName(entry.auth.scheme);
|
|
14159
|
+
for (let n = 2; schemes[name]; n++) name = schemeBaseName(entry.auth.scheme) + n;
|
|
14160
|
+
schemes[name] = obj;
|
|
14161
|
+
nameByContent.set(content, name);
|
|
14162
|
+
}
|
|
14163
|
+
const scopeNames = entry.auth.scheme === "oauth2" ? (entry.auth.scopes ?? []).map((s) => s.name) : [];
|
|
14164
|
+
securityByEntry.set(entry.id, { [name]: scopeNames });
|
|
14165
|
+
}
|
|
14166
|
+
return { schemes, securityByEntry };
|
|
14167
|
+
}
|
|
14168
|
+
function httpEntriesOf(snapshot) {
|
|
14169
|
+
return snapshot.interfaces.filter((e) => e.type === "REST" || e.methods.some((m) => m.endpoint?.transport === "HTTP"));
|
|
14170
|
+
}
|
|
14171
|
+
function renderDoc(snapshot, entries, closureIds, opts = {}) {
|
|
14172
|
+
const { schemes, securityByEntry } = buildSecurity(entries);
|
|
14173
|
+
const paths = {};
|
|
14174
|
+
for (const entry of entries) {
|
|
14175
|
+
const security = securityByEntry.get(entry.id);
|
|
14176
|
+
for (const method2 of entry.methods) {
|
|
14177
|
+
const endpoint = method2.endpoint;
|
|
14178
|
+
if (!endpoint || endpoint.transport !== "HTTP") continue;
|
|
14179
|
+
const p = endpoint.path.startsWith("/") ? endpoint.path : `/${endpoint.path}`;
|
|
14180
|
+
paths[p] = paths[p] ?? {};
|
|
14181
|
+
paths[p][endpoint.method.toLowerCase()] = {
|
|
14182
|
+
tags: [entry.id],
|
|
14183
|
+
...operationFor(method2, closureIds),
|
|
14184
|
+
...security ? { security: [security] } : {}
|
|
14185
|
+
};
|
|
14186
|
+
}
|
|
14187
|
+
}
|
|
14188
|
+
const schemas = {};
|
|
14189
|
+
for (const t of snapshot.types) {
|
|
14190
|
+
schemas[t.id] = {
|
|
14191
|
+
type: "object",
|
|
14192
|
+
title: t.name,
|
|
14193
|
+
properties: Object.fromEntries(t.fields.map((f) => [f.name, schemaFor(f.type, closureIds)])),
|
|
14194
|
+
required: t.fields.filter((f) => !f.optional).map((f) => f.name)
|
|
14195
|
+
};
|
|
14196
|
+
}
|
|
14197
|
+
const components = {};
|
|
14198
|
+
if (Object.keys(schemas).length) components.schemas = schemas;
|
|
14199
|
+
if (Object.keys(schemes).length) components.securitySchemes = schemes;
|
|
14200
|
+
const basePaths = [...new Set(entries.map((e) => e.basePath).filter((b) => !!b))];
|
|
14201
|
+
const servers = opts.servers && basePaths.length === 1 ? [{ url: basePaths[0] }] : void 0;
|
|
14202
|
+
return {
|
|
14203
|
+
openapi: "3.1.0",
|
|
14204
|
+
info: {
|
|
14205
|
+
title: opts.title ?? snapshot.projectName,
|
|
14206
|
+
version: snapshot.version ?? "0.0.0",
|
|
14207
|
+
...snapshot.stateId ? { "x-wairon-state-id": snapshot.stateId } : {},
|
|
14208
|
+
"x-wairon-origin": snapshot.origin,
|
|
14209
|
+
"x-wairon-generated-at": snapshot.generatedAt
|
|
14210
|
+
},
|
|
14211
|
+
...servers ? { servers } : {},
|
|
14212
|
+
paths,
|
|
14213
|
+
...Object.keys(components).length ? { components } : {}
|
|
14214
|
+
};
|
|
14215
|
+
}
|
|
14216
|
+
function toOpenApiSet(snapshot) {
|
|
14217
|
+
const closureIds = new Set(snapshot.types.map((t) => t.id));
|
|
14218
|
+
const byPortal = /* @__PURE__ */ new Map();
|
|
14219
|
+
const order = [];
|
|
14220
|
+
for (const entry of httpEntriesOf(snapshot)) {
|
|
14221
|
+
if (!byPortal.has(entry.component)) {
|
|
14222
|
+
byPortal.set(entry.component, []);
|
|
14223
|
+
order.push(entry.component);
|
|
14224
|
+
}
|
|
14225
|
+
byPortal.get(entry.component).push(entry);
|
|
14226
|
+
}
|
|
14227
|
+
return order.map((portalId) => {
|
|
14228
|
+
const entries = byPortal.get(portalId);
|
|
14229
|
+
const name = entries[0]?.name ?? portalId;
|
|
14230
|
+
return { portalId, name, document: JSON.stringify(renderDoc(snapshot, entries, closureIds, { title: name, servers: true }), null, 2) };
|
|
14231
|
+
});
|
|
14232
|
+
}
|
|
14233
|
+
function isOpenApiDocument(body) {
|
|
14234
|
+
try {
|
|
14235
|
+
const parsed = yaml2.load(body);
|
|
14236
|
+
return !!parsed && typeof parsed === "object" && typeof parsed.openapi === "string";
|
|
14237
|
+
} catch {
|
|
14238
|
+
return false;
|
|
14239
|
+
}
|
|
14240
|
+
}
|
|
14241
|
+
function typeRefFromSchema(schema) {
|
|
14242
|
+
if (!schema) return "json";
|
|
14243
|
+
const ref = schema.$ref;
|
|
14244
|
+
if (typeof ref === "string") return ref.split("/").pop() ?? "json";
|
|
14245
|
+
if (schema.type === "array") {
|
|
14246
|
+
return `${typeRefFromSchema(schema.items)}[]`;
|
|
14247
|
+
}
|
|
14248
|
+
const t = schema.type;
|
|
14249
|
+
if (t === "integer") return "int";
|
|
14250
|
+
if (typeof t === "string" && t !== "object") return t;
|
|
14251
|
+
return "json";
|
|
14252
|
+
}
|
|
14253
|
+
function authFromSecurityScheme(scheme) {
|
|
14254
|
+
const desc = typeof scheme.description === "string" ? { description: scheme.description } : {};
|
|
14255
|
+
if (scheme.type === "apiKey") {
|
|
14256
|
+
return {
|
|
14257
|
+
scheme: "apiKey",
|
|
14258
|
+
...scheme.in === "header" || scheme.in === "query" || scheme.in === "cookie" ? { in: scheme.in } : {},
|
|
14259
|
+
...typeof scheme.name === "string" ? { name: scheme.name } : {},
|
|
14260
|
+
...desc
|
|
14261
|
+
};
|
|
14262
|
+
}
|
|
14263
|
+
if (scheme.type === "http") {
|
|
14264
|
+
if (scheme.scheme === "bearer") return { scheme: "bearer", ...typeof scheme.bearerFormat === "string" ? { bearerFormat: scheme.bearerFormat } : {}, ...desc };
|
|
14265
|
+
if (scheme.scheme === "basic") return { scheme: "basic", ...desc };
|
|
14266
|
+
}
|
|
14267
|
+
if (scheme.type === "oauth2") {
|
|
14268
|
+
const flows = scheme.flows ?? {};
|
|
14269
|
+
const flowKey = Object.keys(flows)[0];
|
|
14270
|
+
const f = flows[flowKey] ?? {};
|
|
14271
|
+
return {
|
|
14272
|
+
scheme: "oauth2",
|
|
14273
|
+
...flowKey === "authorizationCode" || flowKey === "clientCredentials" || flowKey === "implicit" || flowKey === "password" ? { flow: flowKey } : {},
|
|
14274
|
+
...typeof f.authorizationUrl === "string" ? { authorizationUrl: f.authorizationUrl } : {},
|
|
14275
|
+
...typeof f.tokenUrl === "string" ? { tokenUrl: f.tokenUrl } : {},
|
|
14276
|
+
...typeof f.refreshUrl === "string" ? { refreshUrl: f.refreshUrl } : {},
|
|
14277
|
+
scopes: Object.entries(f.scopes ?? {}).map(([name, description]) => ({ name, description })),
|
|
14278
|
+
...desc
|
|
14279
|
+
};
|
|
14280
|
+
}
|
|
14281
|
+
if (scheme.type === "openIdConnect") {
|
|
14282
|
+
return { scheme: "openIdConnect", openIdConnectUrl: typeof scheme.openIdConnectUrl === "string" ? scheme.openIdConnectUrl : "", ...desc };
|
|
14283
|
+
}
|
|
14284
|
+
return void 0;
|
|
14285
|
+
}
|
|
14286
|
+
function fromOpenApi(document, projectName) {
|
|
14287
|
+
let parsed;
|
|
14288
|
+
try {
|
|
14289
|
+
parsed = yaml2.load(document);
|
|
14290
|
+
} catch (e) {
|
|
14291
|
+
throw new Error(`Invalid surface document: not parseable as JSON/YAML (${e instanceof Error ? e.message : String(e)})`);
|
|
14292
|
+
}
|
|
14293
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.openapi !== "string" || typeof parsed.paths !== "object") {
|
|
14294
|
+
throw new Error('Invalid surface document: missing OpenAPI "openapi"/"paths" structure.');
|
|
14295
|
+
}
|
|
14296
|
+
const info = parsed.info ?? {};
|
|
14297
|
+
const methods = [];
|
|
14298
|
+
for (const [rawPath, ops] of Object.entries(parsed.paths)) {
|
|
14299
|
+
for (const [verb, opRaw] of Object.entries(ops ?? {})) {
|
|
14300
|
+
if (!["get", "post", "put", "delete", "patch", "options", "head"].includes(verb)) continue;
|
|
14301
|
+
const op = opRaw ?? {};
|
|
14302
|
+
const name = typeof op.operationId === "string" && /^[a-zA-Z0-9_]+$/.test(op.operationId) ? op.operationId : `${verb}_${rawPath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
14303
|
+
const params = [];
|
|
14304
|
+
for (const p of op.parameters ?? []) {
|
|
14305
|
+
if (typeof p.name !== "string") continue;
|
|
14306
|
+
params.push({
|
|
14307
|
+
name: p.name,
|
|
14308
|
+
type: typeRefFromSchema(p.schema),
|
|
14309
|
+
...p.required === true ? {} : { optional: true },
|
|
14310
|
+
...typeof p.description === "string" ? { description: p.description } : {}
|
|
14311
|
+
});
|
|
14312
|
+
}
|
|
14313
|
+
const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
|
|
14314
|
+
if (bodySchema) {
|
|
14315
|
+
const props = bodySchema.properties ?? {};
|
|
14316
|
+
const required = new Set(bodySchema.required ?? []);
|
|
14317
|
+
if (Object.keys(props).length) {
|
|
14318
|
+
for (const [pname, pschema] of Object.entries(props)) {
|
|
14319
|
+
params.push({ name: pname, type: typeRefFromSchema(pschema), ...required.has(pname) ? {} : { optional: true } });
|
|
14320
|
+
}
|
|
14321
|
+
} else {
|
|
14322
|
+
params.push({ name: "body", type: typeRefFromSchema(bodySchema) });
|
|
14323
|
+
}
|
|
14324
|
+
}
|
|
14325
|
+
const okResponse = op.responses?.["200"] ?? op.responses?.["201"];
|
|
14326
|
+
const responseSchema = okResponse?.content?.["application/json"]?.schema;
|
|
14327
|
+
const returns = responseSchema ? typeRefFromSchema(responseSchema) : "void";
|
|
14328
|
+
const rawGuarantees = op["x-wairon-guarantees"];
|
|
14329
|
+
const guarantees = Array.isArray(rawGuarantees) ? rawGuarantees.filter((g) => typeof g === "string" && g.length > 0) : [];
|
|
14330
|
+
const rawEffect = op["x-wairon-effect"];
|
|
14331
|
+
const effect = rawEffect === "read" || rawEffect === "write" ? rawEffect : void 0;
|
|
14332
|
+
const rawExt = op["x-wairon-ext"];
|
|
14333
|
+
const ext = rawExt && typeof rawExt === "object" && !Array.isArray(rawExt) ? rawExt : void 0;
|
|
14334
|
+
methods.push({
|
|
14335
|
+
name,
|
|
14336
|
+
description: typeof op.summary === "string" ? op.summary : typeof op.description === "string" ? op.description : name,
|
|
14337
|
+
signature: `${name}(${params.map((p) => `${p.name}: ${p.type}`).join(", ")}): ${returns}`,
|
|
14338
|
+
returns,
|
|
14339
|
+
params,
|
|
14340
|
+
endpoint: { transport: "HTTP", method: verb.toUpperCase(), path: rawPath },
|
|
14341
|
+
...guarantees.length ? { guarantees } : {},
|
|
14342
|
+
...effect ? { effect } : {},
|
|
14343
|
+
...ext ? { ext } : {}
|
|
14344
|
+
});
|
|
14345
|
+
}
|
|
14346
|
+
}
|
|
14347
|
+
const types = [];
|
|
14348
|
+
const schemas = parsed.components?.schemas ?? {};
|
|
14349
|
+
for (const [id, schema] of Object.entries(schemas)) {
|
|
14350
|
+
const props = schema.properties ?? {};
|
|
14351
|
+
const required = new Set(schema.required ?? []);
|
|
14352
|
+
types.push({
|
|
14353
|
+
id,
|
|
14354
|
+
name: typeof schema.title === "string" ? schema.title : id,
|
|
14355
|
+
kind: "value-object",
|
|
14356
|
+
fields: Object.entries(props).map(([fname, fschema]) => ({
|
|
14357
|
+
name: fname,
|
|
14358
|
+
type: typeRefFromSchema(fschema),
|
|
14359
|
+
...required.has(fname) ? {} : { optional: true }
|
|
14360
|
+
}))
|
|
14361
|
+
});
|
|
14362
|
+
}
|
|
14363
|
+
const securitySchemes = parsed.components?.securitySchemes ?? {};
|
|
14364
|
+
const firstScheme = Object.values(securitySchemes)[0];
|
|
14365
|
+
const importedAuth = firstScheme ? authFromSecurityScheme(firstScheme) : void 0;
|
|
14366
|
+
const entry = {
|
|
14367
|
+
id: `${projectName}-api`,
|
|
14368
|
+
name: typeof info.title === "string" ? info.title : projectName,
|
|
14369
|
+
audience: "external",
|
|
14370
|
+
type: "REST",
|
|
14371
|
+
component: `${projectName}-api`,
|
|
14372
|
+
methods,
|
|
14373
|
+
details: typeof info.description === "string" ? info.description : `Imported OpenAPI surface of ${projectName}.`,
|
|
14374
|
+
...typeof info.version === "string" ? { version: info.version } : {},
|
|
14375
|
+
...importedAuth ? { auth: importedAuth } : {}
|
|
14376
|
+
};
|
|
14377
|
+
return SurfaceSnapshotSchema.parse({
|
|
14378
|
+
projectName,
|
|
14379
|
+
origin: "authored",
|
|
14380
|
+
...typeof info.version === "string" ? { version: info.version } : {},
|
|
14381
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14382
|
+
interfaces: [entry],
|
|
14383
|
+
types
|
|
14384
|
+
});
|
|
14385
|
+
}
|
|
14386
|
+
var yaml2, PRIMITIVES;
|
|
14387
|
+
var init_openapi = __esm({
|
|
14388
|
+
"src/core/openapi.ts"() {
|
|
14389
|
+
"use strict";
|
|
14390
|
+
yaml2 = __toESM(require("js-yaml"));
|
|
14391
|
+
init_models();
|
|
14392
|
+
PRIMITIVES = {
|
|
14393
|
+
string: { type: "string" },
|
|
14394
|
+
number: { type: "number" },
|
|
14395
|
+
float: { type: "number" },
|
|
14396
|
+
decimal: { type: "number" },
|
|
14397
|
+
int: { type: "integer" },
|
|
14398
|
+
integer: { type: "integer" },
|
|
14399
|
+
boolean: { type: "boolean" },
|
|
14400
|
+
bool: { type: "boolean" },
|
|
14401
|
+
date: { type: "string", format: "date-time" },
|
|
14402
|
+
datetime: { type: "string", format: "date-time" },
|
|
14403
|
+
uuid: { type: "string", format: "uuid" },
|
|
14404
|
+
json: { type: "object" },
|
|
14405
|
+
object: { type: "object" },
|
|
14406
|
+
any: {},
|
|
14407
|
+
unknown: {},
|
|
14408
|
+
void: {}
|
|
14409
|
+
};
|
|
14410
|
+
}
|
|
14411
|
+
});
|
|
14412
|
+
|
|
14413
|
+
// src/core/surfaces.ts
|
|
14414
|
+
function surfacesDir(rootDir) {
|
|
14415
|
+
return path16.join(rootDir, ".wai", SURFACES_DIRNAME);
|
|
14416
|
+
}
|
|
14417
|
+
function audienceRank(audience) {
|
|
14418
|
+
const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
|
|
14419
|
+
return idx === -1 ? SURFACE_AUDIENCES.indexOf("instance") : idx;
|
|
14420
|
+
}
|
|
14421
|
+
function stateIdString() {
|
|
14422
|
+
const s = computeStateId();
|
|
14423
|
+
return `${s.algorithm}:${s.digest}`;
|
|
14424
|
+
}
|
|
14425
|
+
function computeTypeClosure(entries, types) {
|
|
14426
|
+
const included = /* @__PURE__ */ new Map();
|
|
14427
|
+
const queue = [];
|
|
14428
|
+
const enqueueRef = (ref) => {
|
|
14429
|
+
if (BUILTIN_TYPES.has(ref.toLowerCase())) return;
|
|
14430
|
+
for (const spec of types) {
|
|
14431
|
+
const qualifiedId2 = spec.subsystem && !spec.id.startsWith(`${spec.subsystem}::`) ? `${spec.subsystem}::${spec.id}` : spec.id;
|
|
14432
|
+
if (matchTypeRef(ref, qualifiedId2) && !included.has(spec.id)) {
|
|
14433
|
+
included.set(spec.id, spec);
|
|
14434
|
+
queue.push(spec.id);
|
|
14435
|
+
}
|
|
14663
14436
|
}
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
var tag = b.getAttribute('data-openapi-tag') || '';
|
|
14669
|
-
if (typeof opts !== 'undefined' && opts && opts.onOpenApi) { opts.onOpenApi(tag); return; }
|
|
14670
|
-
var href = openApiSiblingHref();
|
|
14671
|
-
if (href) window.open(href + (tag ? '#/' + tag : ''), '_blank', 'noopener');
|
|
14672
|
-
});
|
|
14673
|
-
})(oapis[oi]);
|
|
14437
|
+
};
|
|
14438
|
+
for (const entry of entries) {
|
|
14439
|
+
for (const m of entry.methods) {
|
|
14440
|
+
for (const ref of methodTypeRefs(m)) enqueueRef(ref);
|
|
14674
14441
|
}
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
opts.onOpenSpec(b.getAttribute('data-openspec-kind'), b.getAttribute('data-openspec-id') || '');
|
|
14681
|
-
}
|
|
14682
|
-
});
|
|
14683
|
-
})(ospecs[si]);
|
|
14442
|
+
}
|
|
14443
|
+
while (queue.length) {
|
|
14444
|
+
const spec = included.get(queue.shift());
|
|
14445
|
+
for (const field of spec.fields) {
|
|
14446
|
+
for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
|
|
14684
14447
|
}
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14448
|
+
}
|
|
14449
|
+
return [...included.values()].map((t) => ({
|
|
14450
|
+
id: t.id,
|
|
14451
|
+
name: t.name,
|
|
14452
|
+
kind: t.kind,
|
|
14453
|
+
fields: t.fields.map((f) => ({
|
|
14454
|
+
name: f.name,
|
|
14455
|
+
type: f.type,
|
|
14456
|
+
...f.description ? { description: f.description } : {},
|
|
14457
|
+
...f.optional ? { optional: true } : {}
|
|
14458
|
+
}))
|
|
14459
|
+
}));
|
|
14460
|
+
}
|
|
14461
|
+
function projectOwnSurface(maxAudience) {
|
|
14462
|
+
const system = loadSystemSpec();
|
|
14463
|
+
if (!system) {
|
|
14464
|
+
throw new Error("Cannot project a surface: the L0 system spec is missing.");
|
|
14465
|
+
}
|
|
14466
|
+
const subsystems = loadSubsystemSpecs();
|
|
14467
|
+
const components = loadComponentSpecs();
|
|
14468
|
+
const interfaces = loadInterfaceSpecs();
|
|
14469
|
+
const types = loadTypeSpecs();
|
|
14470
|
+
const floor = audienceRank(maxAudience);
|
|
14471
|
+
const rawEntries = system.publicInterfaces ?? [];
|
|
14472
|
+
const entries = [];
|
|
14473
|
+
for (const raw of rawEntries) {
|
|
14474
|
+
const audience = raw.audience ?? "instance";
|
|
14475
|
+
if (audienceRank(audience) < floor) continue;
|
|
14476
|
+
if (!raw.component) continue;
|
|
14477
|
+
const comp = components.find((c) => c.id === raw.component);
|
|
14478
|
+
if (!comp) continue;
|
|
14479
|
+
const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!raw.interface || i.id === raw.interface));
|
|
14480
|
+
const methods = compInterfaces.flatMap((i) => i.methods);
|
|
14481
|
+
const subsystemType = subsystems.find((s) => s.id === comp.subsystem)?.publicInterfaces.find((pi) => pi.component === comp.id)?.type;
|
|
14482
|
+
entries.push({
|
|
14483
|
+
id: raw.id ?? raw.interface ?? comp.id,
|
|
14484
|
+
name: raw.name ?? comp.name,
|
|
14485
|
+
audience,
|
|
14486
|
+
type: raw.type ?? subsystemType ?? "Custom",
|
|
14487
|
+
component: comp.id,
|
|
14488
|
+
methods,
|
|
14489
|
+
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
14490
|
+
// Project the backing Portal's auth + basePath so the codec can emit
|
|
14491
|
+
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
14492
|
+
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
14493
|
+
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
14494
|
+
details: raw.details ?? "",
|
|
14495
|
+
...raw.version ? { version: raw.version } : {},
|
|
14496
|
+
...raw.stability ? { stability: raw.stability } : {}
|
|
14497
|
+
});
|
|
14498
|
+
}
|
|
14499
|
+
return SurfaceSnapshotSchema.parse({
|
|
14500
|
+
projectName: system.name,
|
|
14501
|
+
origin: "generated",
|
|
14502
|
+
stateId: stateIdString(),
|
|
14503
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14504
|
+
interfaces: entries,
|
|
14505
|
+
types: computeTypeClosure(entries, types)
|
|
14506
|
+
});
|
|
14507
|
+
}
|
|
14508
|
+
function projectChildSurface() {
|
|
14509
|
+
return projectOwnSurface("project");
|
|
14510
|
+
}
|
|
14511
|
+
function localName(id) {
|
|
14512
|
+
return id.split("::").pop();
|
|
14513
|
+
}
|
|
14514
|
+
function projectSubsystemSurface(subsystemId) {
|
|
14515
|
+
const system = loadSystemSpec();
|
|
14516
|
+
if (!system) {
|
|
14517
|
+
throw new Error("Cannot project a subsystem surface: the L0 system spec is missing.");
|
|
14518
|
+
}
|
|
14519
|
+
const subsystems = loadSubsystemSpecs();
|
|
14520
|
+
const target = subsystems.find((s) => s.id === subsystemId);
|
|
14521
|
+
if (!target) {
|
|
14522
|
+
throw new Error(`Cannot project a subsystem surface: subsystem "${subsystemId}" does not exist.`);
|
|
14523
|
+
}
|
|
14524
|
+
const components = loadComponentSpecs();
|
|
14525
|
+
const interfaces = loadInterfaceSpecs();
|
|
14526
|
+
const types = loadTypeSpecs();
|
|
14527
|
+
const entries = [];
|
|
14528
|
+
const unprojectable = [];
|
|
14529
|
+
for (const pub of target.publicInterfaces ?? []) {
|
|
14530
|
+
if (!pub.component) continue;
|
|
14531
|
+
const comp = components.find((c) => c.id === pub.component || c.id === `${subsystemId}::${pub.component}`);
|
|
14532
|
+
if (!comp) continue;
|
|
14533
|
+
if (!CROSS_BOUNDARY_TARGETS.has(comp.componentType)) {
|
|
14534
|
+
unprojectable.push({ component: pub.component, componentType: comp.componentType });
|
|
14535
|
+
continue;
|
|
14693
14536
|
}
|
|
14537
|
+
const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!pub.interface || i.id === pub.interface || i.id === `${subsystemId}::${pub.interface}`));
|
|
14538
|
+
const methods = compInterfaces.flatMap((i) => i.methods);
|
|
14539
|
+
entries.push({
|
|
14540
|
+
id: localName(pub.interface ?? comp.id),
|
|
14541
|
+
name: comp.name,
|
|
14542
|
+
// Family ceiling: a sibling surface is consumable by the system family only.
|
|
14543
|
+
audience: "project",
|
|
14544
|
+
type: pub.type ?? "Custom",
|
|
14545
|
+
// The snapshot carries the LOCAL portal name — consumers resolve cross-tree
|
|
14546
|
+
// refs by their final segment.
|
|
14547
|
+
component: localName(comp.id),
|
|
14548
|
+
methods,
|
|
14549
|
+
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
14550
|
+
// Project the backing component's auth + basePath so the codec can emit
|
|
14551
|
+
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
14552
|
+
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
14553
|
+
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
14554
|
+
details: pub.details ?? ""
|
|
14555
|
+
});
|
|
14694
14556
|
}
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14557
|
+
for (const skipped of unprojectable) {
|
|
14558
|
+
console.error(
|
|
14559
|
+
`[surfaces] skipped "${subsystemId}::${skipped.component}": a published ${skipped.componentType} can never serve a cross-boundary caller, so it stays out of every chained child's sibling surface \u2014 publish this surface through a Portal, a Gateway, or an Observer (for events).`
|
|
14560
|
+
);
|
|
14561
|
+
}
|
|
14562
|
+
return SurfaceSnapshotSchema.parse({
|
|
14563
|
+
projectName: `${system.name}::${subsystemId}`,
|
|
14564
|
+
origin: "generated",
|
|
14565
|
+
stateId: stateIdString(),
|
|
14566
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14567
|
+
interfaces: entries,
|
|
14568
|
+
types: computeTypeClosure(entries, types)
|
|
14569
|
+
});
|
|
14570
|
+
}
|
|
14571
|
+
function listSnapshots(rootDir = getProjectRoot()) {
|
|
14572
|
+
const dir = surfacesDir(rootDir);
|
|
14573
|
+
if (!fs12.existsSync(dir)) return [];
|
|
14574
|
+
const out = [];
|
|
14575
|
+
for (const file of fs12.readdirSync(dir)) {
|
|
14576
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
14577
|
+
try {
|
|
14578
|
+
out.push(SurfaceSnapshotSchema.parse(readYamlFile(path16.join(dir, file))));
|
|
14579
|
+
} catch {
|
|
14709
14580
|
}
|
|
14710
|
-
|
|
14711
|
-
|
|
14581
|
+
}
|
|
14582
|
+
return out;
|
|
14583
|
+
}
|
|
14584
|
+
function snapshotFilename(projectName) {
|
|
14585
|
+
return `${safeFilenamePart(projectName)}.yaml`;
|
|
14586
|
+
}
|
|
14587
|
+
function writeSnapshotIfChanged(snapshot, rootDir) {
|
|
14588
|
+
const dir = surfacesDir(rootDir);
|
|
14589
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
14590
|
+
const p = path16.join(dir, snapshotFilename(snapshot.projectName));
|
|
14591
|
+
const next = SurfaceSnapshotSchema.parse(snapshot);
|
|
14592
|
+
if (fs12.existsSync(p)) {
|
|
14593
|
+
try {
|
|
14594
|
+
const existing = SurfaceSnapshotSchema.parse(readYamlFile(p));
|
|
14595
|
+
if (surfaceContentKey(existing) === surfaceContentKey(next)) {
|
|
14596
|
+
return { path: p, changed: false };
|
|
14597
|
+
}
|
|
14598
|
+
} catch {
|
|
14712
14599
|
}
|
|
14713
|
-
})();
|
|
14714
|
-
})();
|
|
14715
|
-
</script>
|
|
14716
|
-
</body>
|
|
14717
|
-
</html>
|
|
14718
|
-
`;
|
|
14719
14600
|
}
|
|
14720
|
-
|
|
14721
|
-
|
|
14722
|
-
// src/core/rules/repository.ts
|
|
14723
|
-
function addRule(rule) {
|
|
14724
|
-
ruleSet.push(rule);
|
|
14601
|
+
writeYamlFile(p, next);
|
|
14602
|
+
return { path: p, changed: true };
|
|
14725
14603
|
}
|
|
14726
|
-
function
|
|
14727
|
-
|
|
14728
|
-
for (const rule of SDD_RULES) addRule(rule);
|
|
14604
|
+
function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
|
|
14605
|
+
return writeSnapshotIfChanged(snapshot, rootDir).path;
|
|
14729
14606
|
}
|
|
14730
|
-
function
|
|
14731
|
-
|
|
14607
|
+
function loadSurfaceSnapshots() {
|
|
14608
|
+
return listSnapshots();
|
|
14732
14609
|
}
|
|
14733
|
-
function
|
|
14734
|
-
const
|
|
14735
|
-
|
|
14610
|
+
function listMountSnapshots(mounts) {
|
|
14611
|
+
const bound = path16.resolve(getProjectRoot());
|
|
14612
|
+
const out = [];
|
|
14613
|
+
for (const namespace of mounts) {
|
|
14614
|
+
const dir = resolveSubprojectForNamespace(namespace);
|
|
14615
|
+
if (!dir || path16.resolve(dir) === bound) continue;
|
|
14616
|
+
const snapshots = listSnapshots(dir);
|
|
14617
|
+
if (snapshots.length > 0) out.push({ namespace, snapshots });
|
|
14618
|
+
}
|
|
14619
|
+
return out;
|
|
14736
14620
|
}
|
|
14737
|
-
function
|
|
14738
|
-
return
|
|
14621
|
+
function loadMountSurfaceSnapshots(mounts) {
|
|
14622
|
+
return listMountSnapshots(mounts);
|
|
14739
14623
|
}
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14624
|
+
function selectPortalSpec(renderedSet, portalId) {
|
|
14625
|
+
const hit = renderedSet.find((spec) => spec.portalId === portalId);
|
|
14626
|
+
if (!hit) {
|
|
14627
|
+
const known = renderedSet.map((s) => s.portalId).join(", ");
|
|
14628
|
+
throw new Error(`Unknown portal "${portalId}" \u2014 this surface renders: ${known || "(no portals)"}.`);
|
|
14629
|
+
}
|
|
14630
|
+
return [hit];
|
|
14631
|
+
}
|
|
14632
|
+
function perPortalPath(resolvedOut, portalId) {
|
|
14633
|
+
const ext = path16.extname(resolvedOut);
|
|
14634
|
+
const stem2 = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
|
|
14635
|
+
return `${stem2}.${safeFilenamePart(portalId)}${ext}`;
|
|
14636
|
+
}
|
|
14637
|
+
function writeSurfaceFile(outPath, snapshot, renderedSet) {
|
|
14638
|
+
const resolved = path16.resolve(outPath);
|
|
14639
|
+
fs12.mkdirSync(path16.dirname(resolved), { recursive: true });
|
|
14640
|
+
if (!renderedSet || renderedSet.length === 0) {
|
|
14641
|
+
writeYamlFile(resolved, snapshot);
|
|
14642
|
+
return [resolved];
|
|
14643
|
+
}
|
|
14644
|
+
if (renderedSet.length === 1) {
|
|
14645
|
+
fs12.writeFileSync(resolved, renderedSet[0].document);
|
|
14646
|
+
return [resolved];
|
|
14647
|
+
}
|
|
14648
|
+
return renderedSet.map((spec) => {
|
|
14649
|
+
const target = perPortalPath(resolved, spec.portalId);
|
|
14650
|
+
fs12.writeFileSync(target, spec.document);
|
|
14651
|
+
return target;
|
|
14652
|
+
});
|
|
14653
|
+
}
|
|
14654
|
+
function exportResult(snapshot, renderedSet, writtenPaths) {
|
|
14655
|
+
const rendered = renderedSet?.length === 1 ? renderedSet[0].document : void 0;
|
|
14656
|
+
return {
|
|
14657
|
+
snapshot,
|
|
14658
|
+
...rendered !== void 0 ? { rendered } : {},
|
|
14659
|
+
...renderedSet ? { renderedSet } : {},
|
|
14660
|
+
...writtenPaths.length === 1 ? { writtenTo: writtenPaths[0] } : {},
|
|
14661
|
+
...writtenPaths.length ? { writtenPaths } : {}
|
|
14662
|
+
};
|
|
14663
|
+
}
|
|
14664
|
+
function exportSurface(maxAudience, format, outPath, portalId) {
|
|
14665
|
+
const snapshot = projectOwnSurface(maxAudience);
|
|
14666
|
+
let renderedSet = format === "openapi" ? toOpenApiSet(snapshot) : void 0;
|
|
14667
|
+
if (renderedSet && portalId) renderedSet = selectPortalSpec(renderedSet, portalId);
|
|
14668
|
+
const writtenPaths = outPath ? writeSurfaceFile(outPath, snapshot, renderedSet) : [];
|
|
14669
|
+
return exportResult(snapshot, renderedSet, writtenPaths);
|
|
14670
|
+
}
|
|
14671
|
+
function importSurface(sourcePath, origin) {
|
|
14672
|
+
const resolved = path16.resolve(sourcePath);
|
|
14673
|
+
if (!fs12.existsSync(resolved)) {
|
|
14674
|
+
throw new Error(`Surface document not found: ${resolved}`);
|
|
14675
|
+
}
|
|
14676
|
+
const body = fs12.readFileSync(resolved, "utf8");
|
|
14677
|
+
let snapshot;
|
|
14678
|
+
if (isOpenApiDocument(body)) {
|
|
14679
|
+
const projectName = path16.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
|
|
14680
|
+
snapshot = fromOpenApi(body, projectName);
|
|
14681
|
+
snapshot = { ...snapshot, origin };
|
|
14682
|
+
} else {
|
|
14683
|
+
snapshot = SurfaceSnapshotSchema.parse(readYamlFile(resolved));
|
|
14684
|
+
snapshot = { ...snapshot, origin };
|
|
14685
|
+
}
|
|
14686
|
+
saveSnapshot(snapshot);
|
|
14687
|
+
return snapshot;
|
|
14688
|
+
}
|
|
14689
|
+
function pinFamilySurfaces() {
|
|
14690
|
+
const parent = resolveChainingParent();
|
|
14691
|
+
if (!parent) return null;
|
|
14692
|
+
const childRoot = getProjectRoot();
|
|
14693
|
+
const projected = runWithProjectRoot(parent.parentRoot, () => {
|
|
14694
|
+
invalidateSpecCache();
|
|
14695
|
+
const siblings = loadSubsystemSpecs().filter((s) => !s.id.includes("::") && s.id !== parent.subsystemId);
|
|
14696
|
+
return [projectChildSurface(), ...siblings.map((s) => projectSubsystemSurface(s.id))];
|
|
14697
|
+
});
|
|
14698
|
+
const before = new Map(listSnapshots(childRoot).map((s) => [s.projectName, surfaceContentKey(s)]));
|
|
14699
|
+
const changed = [];
|
|
14700
|
+
for (const snapshot of projected) {
|
|
14701
|
+
const stored = saveSnapshot(snapshot, childRoot);
|
|
14702
|
+
if (before.get(snapshot.projectName) !== surfaceContentKey(SurfaceSnapshotSchema.parse(snapshot))) {
|
|
14703
|
+
changed.push(stored);
|
|
14704
|
+
}
|
|
14705
|
+
}
|
|
14706
|
+
return changed;
|
|
14707
|
+
}
|
|
14708
|
+
function computeParentStateId(parentRoot) {
|
|
14709
|
+
return computeStateIdAt(parentRoot);
|
|
14710
|
+
}
|
|
14711
|
+
function listExternalInterfaces() {
|
|
14712
|
+
const snapshots = listSnapshots();
|
|
14713
|
+
const chainingParent = resolveChainingParent();
|
|
14714
|
+
const parentStateId = chainingParent ? computeParentStateId(chainingParent.parentRoot) : null;
|
|
14715
|
+
return snapshots.map((snapshot) => {
|
|
14716
|
+
const generated = snapshot.origin === "generated";
|
|
14717
|
+
const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
|
|
14718
|
+
const freshness = generated && parentStateId ? snapshot.stateId === parentStateId ? "fresh" : "stale" : "unverifiable";
|
|
14719
|
+
return {
|
|
14720
|
+
projectName: snapshot.projectName,
|
|
14721
|
+
origin: snapshot.origin,
|
|
14722
|
+
sourceKind,
|
|
14723
|
+
generatedAt: snapshot.generatedAt,
|
|
14724
|
+
...snapshot.stateId ? { stateId: snapshot.stateId } : {},
|
|
14725
|
+
...snapshot.version ? { version: snapshot.version } : {},
|
|
14726
|
+
freshness,
|
|
14727
|
+
interfaceIds: snapshot.interfaces.map((e) => e.id)
|
|
14728
|
+
};
|
|
14729
|
+
});
|
|
14730
|
+
}
|
|
14731
|
+
function surfaceContentKey(snapshot) {
|
|
14732
|
+
const { stateId, generatedAt, origin, ...content } = snapshot;
|
|
14733
|
+
return JSON.stringify(content);
|
|
14734
|
+
}
|
|
14735
|
+
var fs12, path16, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
|
|
14736
|
+
var init_surfaces = __esm({
|
|
14737
|
+
"src/core/surfaces.ts"() {
|
|
14743
14738
|
"use strict";
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14739
|
+
fs12 = __toESM(require("fs"));
|
|
14740
|
+
path16 = __toESM(require("path"));
|
|
14741
|
+
init_fs();
|
|
14742
|
+
init_yaml();
|
|
14743
|
+
init_filenames();
|
|
14744
|
+
init_models();
|
|
14745
|
+
init_specs2();
|
|
14746
|
+
init_statehash();
|
|
14747
|
+
init_type_analysis();
|
|
14748
|
+
init_openapi();
|
|
14749
|
+
SURFACES_DIRNAME = "surfaces";
|
|
14750
|
+
CROSS_BOUNDARY_TARGETS = /* @__PURE__ */ new Set(["Portal", "Gateway", "Observer"]);
|
|
14747
14751
|
}
|
|
14748
14752
|
});
|
|
14749
14753
|
|
|
@@ -14850,7 +14854,7 @@ var init_approval = __esm({
|
|
|
14850
14854
|
// src/core/validation.ts
|
|
14851
14855
|
var validation_exports = {};
|
|
14852
14856
|
__export(validation_exports, {
|
|
14853
|
-
listRules: () =>
|
|
14857
|
+
listRules: () => listRules2,
|
|
14854
14858
|
validateAsComplete: () => validateAsComplete,
|
|
14855
14859
|
validateProjectConfig: () => validateProjectConfig,
|
|
14856
14860
|
validateRegistry: () => validateRegistry,
|
|
@@ -14863,6 +14867,32 @@ function projectPackSelections() {
|
|
|
14863
14867
|
return [];
|
|
14864
14868
|
}
|
|
14865
14869
|
}
|
|
14870
|
+
function issueKey(issue2) {
|
|
14871
|
+
return `${issue2.code}|${issue2.specId ?? ""}`;
|
|
14872
|
+
}
|
|
14873
|
+
function worstByKey(issues) {
|
|
14874
|
+
const worst = /* @__PURE__ */ new Map();
|
|
14875
|
+
for (const issue2 of issues) {
|
|
14876
|
+
const key = issueKey(issue2);
|
|
14877
|
+
const seen = worst.get(key);
|
|
14878
|
+
if (!seen || SEVERITY_RANK[issue2.severity] > SEVERITY_RANK[seen]) worst.set(key, issue2.severity);
|
|
14879
|
+
}
|
|
14880
|
+
return worst;
|
|
14881
|
+
}
|
|
14882
|
+
function stricterSeverities(parent, child) {
|
|
14883
|
+
const fromParent = parent?.sddRuleSeverity ?? {};
|
|
14884
|
+
const fromChild = child?.sddRuleSeverity ?? {};
|
|
14885
|
+
const codes = /* @__PURE__ */ new Set([...Object.keys(fromParent), ...Object.keys(fromChild)]);
|
|
14886
|
+
if (codes.size === 0) return parent ?? child;
|
|
14887
|
+
const defaults = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
|
|
14888
|
+
const merged = {};
|
|
14889
|
+
for (const code of codes) {
|
|
14890
|
+
const p = fromParent[code] ?? defaults.get(code);
|
|
14891
|
+
const c = fromChild[code] ?? defaults.get(code);
|
|
14892
|
+
merged[code] = p === void 0 || c === void 0 ? p ?? c : SEVERITY_RANK[p] >= SEVERITY_RANK[c] ? p : c;
|
|
14893
|
+
}
|
|
14894
|
+
return { ...parent ?? child, sddRuleSeverity: merged };
|
|
14895
|
+
}
|
|
14866
14896
|
function issue(severity, code, message, agentId, specId) {
|
|
14867
14897
|
return { severity, code, message, agentId, specId };
|
|
14868
14898
|
}
|
|
@@ -14944,7 +14974,7 @@ function validateProjectConfig(config) {
|
|
|
14944
14974
|
issues
|
|
14945
14975
|
};
|
|
14946
14976
|
}
|
|
14947
|
-
function
|
|
14977
|
+
function listRules2() {
|
|
14948
14978
|
const extensions = loadProjectExtensions();
|
|
14949
14979
|
registerBuiltinRules();
|
|
14950
14980
|
registerPackRules(extensions.rules);
|
|
@@ -14956,7 +14986,8 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14956
14986
|
let recursive = true;
|
|
14957
14987
|
let extensions;
|
|
14958
14988
|
let treatAllAsComplete = false;
|
|
14959
|
-
|
|
14989
|
+
let crossTree;
|
|
14990
|
+
if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "recursive" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "crossTree" in rulesOrOptions)) {
|
|
14960
14991
|
const opts = rulesOrOptions;
|
|
14961
14992
|
rules = opts.rules;
|
|
14962
14993
|
projectType = opts.projectType ?? "backend";
|
|
@@ -14964,6 +14995,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14964
14995
|
recursive = opts.recursive ?? true;
|
|
14965
14996
|
extensions = opts.extensions;
|
|
14966
14997
|
treatAllAsComplete = opts.treatAllAsComplete ?? false;
|
|
14998
|
+
crossTree = opts.crossTree;
|
|
14967
14999
|
}
|
|
14968
15000
|
extensions ??= loadProjectExtensions();
|
|
14969
15001
|
scanAllSpecs({ recursive });
|
|
@@ -15015,6 +15047,9 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15015
15047
|
for (const err of extensions.errors) {
|
|
15016
15048
|
issues.push(issue("error", "EXTENSION_LOAD_ERROR", err));
|
|
15017
15049
|
}
|
|
15050
|
+
const mountSurfaceSnapshots = loadMountSurfaceSnapshots(
|
|
15051
|
+
subsystems.filter((s) => s.projectPath).map((s) => s.id)
|
|
15052
|
+
);
|
|
15018
15053
|
const ctx = buildRuleContext({
|
|
15019
15054
|
system,
|
|
15020
15055
|
subsystems,
|
|
@@ -15030,6 +15065,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15030
15065
|
// By-name selections only: a legacy path ref pins nothing to check.
|
|
15031
15066
|
packSelections: projectPackSelections(),
|
|
15032
15067
|
surfaceSnapshots,
|
|
15068
|
+
mountSurfaceSnapshots,
|
|
15033
15069
|
codeModel,
|
|
15034
15070
|
issues
|
|
15035
15071
|
});
|
|
@@ -15038,56 +15074,27 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15038
15074
|
for (const rule of ruleSequence()) {
|
|
15039
15075
|
rule.check(ctx);
|
|
15040
15076
|
}
|
|
15041
|
-
const
|
|
15042
|
-
|
|
15043
|
-
);
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
continue;
|
|
15063
|
-
}
|
|
15064
|
-
if (SUBPROJECT_CONFORMANCE_CODES.has(iss.code)) {
|
|
15065
|
-
if (iss.severity === "error") {
|
|
15066
|
-
iss.severity = "warning";
|
|
15067
|
-
downgraded++;
|
|
15068
|
-
}
|
|
15069
|
-
iss.crossTreeContext = true;
|
|
15070
|
-
}
|
|
15071
|
-
}
|
|
15072
|
-
if (unverified > 0 || downgraded > 0) {
|
|
15073
|
-
const notes = [];
|
|
15074
|
-
if (unverified > 0) {
|
|
15075
|
-
notes.push(
|
|
15076
|
-
`${unverified} cross-tree reference(s) have no vendored surface snapshot covering them and were reported as UNVERIFIED_EXTERNAL_REF warnings \u2014 re-lock the parent so fresh family/sibling snapshots ship, or inspect via \`wairon surface externals\` / sdd_list_external_interfaces.`
|
|
15077
|
-
);
|
|
15078
|
-
}
|
|
15079
|
-
if (downgraded > 0) {
|
|
15080
|
-
notes.push(
|
|
15081
|
-
`${downgraded} code\u2194spec conformance finding(s) (parent-root-relative source paths) were downgraded to warnings.`
|
|
15082
|
-
);
|
|
15083
|
-
}
|
|
15084
|
-
issues.unshift({
|
|
15085
|
-
severity: "warning",
|
|
15086
|
-
code: "CHAINED_SUBPROJECT_CONTEXT",
|
|
15087
|
-
crossTreeContext: true,
|
|
15088
|
-
message: `This project is a chained subproject ("${chainingParent.subsystemId}") of the parent project at "${chainingParent.parentRoot}". ${notes.join(" ")} Full cross-tree verification runs from the parent root.`
|
|
15089
|
-
});
|
|
15090
|
-
}
|
|
15077
|
+
const unresolved = (i) => RESOLUTION_FAILURE_CODES.has(i.code) && !i.surfaceResolved;
|
|
15078
|
+
const chainingParent = crossTree !== "off" && issues.some(unresolved) ? findChainingParent(getProjectRoot()) : null;
|
|
15079
|
+
const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete, rules) : null;
|
|
15080
|
+
if (resolution) {
|
|
15081
|
+
const judgedByParent = worstByKey(resolution.issues);
|
|
15082
|
+
const kept = issues.filter((i) => {
|
|
15083
|
+
const judged = judgedByParent.get(issueKey(i));
|
|
15084
|
+
if (judged !== void 0) return SEVERITY_RANK[judged] < SEVERITY_RANK[i.severity];
|
|
15085
|
+
return !unresolved(i);
|
|
15086
|
+
});
|
|
15087
|
+
const keptByChild = worstByKey(kept);
|
|
15088
|
+
const added = resolution.issues.filter((i) => {
|
|
15089
|
+
const own = keptByChild.get(issueKey(i));
|
|
15090
|
+
return own === void 0 || SEVERITY_RANK[own] <= SEVERITY_RANK[i.severity];
|
|
15091
|
+
});
|
|
15092
|
+
const merged = dedupeIssues([...kept, ...added]);
|
|
15093
|
+
return {
|
|
15094
|
+
valid: merged.every((i) => i.severity !== "error"),
|
|
15095
|
+
issues: merged,
|
|
15096
|
+
resolvedThrough: { root: resolution.root, scope: resolution.scope }
|
|
15097
|
+
};
|
|
15091
15098
|
}
|
|
15092
15099
|
return {
|
|
15093
15100
|
valid: issues.every((i) => i.severity !== "error"),
|
|
@@ -15099,6 +15106,74 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15099
15106
|
});
|
|
15100
15107
|
}
|
|
15101
15108
|
}
|
|
15109
|
+
function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
|
|
15110
|
+
const reach = getRequestParentReach();
|
|
15111
|
+
if (reach && !reach.parentReach) return null;
|
|
15112
|
+
const ceiling = reach?.topRoot ? path18.resolve(reach.topRoot) : void 0;
|
|
15113
|
+
const chain = [];
|
|
15114
|
+
let top = path18.resolve(boundRoot);
|
|
15115
|
+
while (top !== ceiling) {
|
|
15116
|
+
const hop = findChainingParent(top);
|
|
15117
|
+
if (!hop) break;
|
|
15118
|
+
const next = path18.resolve(hop.parentRoot);
|
|
15119
|
+
if (ceiling && !isWithinOrEqual(ceiling, next)) break;
|
|
15120
|
+
chain.unshift(hop.subsystemId);
|
|
15121
|
+
top = next;
|
|
15122
|
+
}
|
|
15123
|
+
if (chain.length === 0) return null;
|
|
15124
|
+
const scope = chain.join("::");
|
|
15125
|
+
const inner = runWithProjectRoot(top, () => {
|
|
15126
|
+
invalidateSpecCache();
|
|
15127
|
+
let governing = { rules: stricterSeverities(void 0, childRules) };
|
|
15128
|
+
try {
|
|
15129
|
+
const config = loadProjectConfig();
|
|
15130
|
+
governing = { rules: stricterSeverities(config.rules, childRules), projectType: config.projectType };
|
|
15131
|
+
} catch {
|
|
15132
|
+
}
|
|
15133
|
+
return validateSddTree({
|
|
15134
|
+
...governing,
|
|
15135
|
+
scopeSubsystem: scope,
|
|
15136
|
+
recursive: true,
|
|
15137
|
+
crossTree: "off",
|
|
15138
|
+
treatAllAsComplete
|
|
15139
|
+
});
|
|
15140
|
+
});
|
|
15141
|
+
if (inner.issues.some((i) => i.code === "MISSING_SYSTEM_SPEC" || i.code === "SUBSYSTEM_NOT_FOUND")) return null;
|
|
15142
|
+
const local = scope.split("::").pop();
|
|
15143
|
+
const issues = inner.issues.map((i) => ({
|
|
15144
|
+
...i,
|
|
15145
|
+
message: stripNamespace(i.message, scope),
|
|
15146
|
+
...i.specId !== void 0 ? { specId: i.specId === scope ? local : stripNamespace(i.specId, scope) } : {}
|
|
15147
|
+
}));
|
|
15148
|
+
return { root: top, scope, issues };
|
|
15149
|
+
}
|
|
15150
|
+
function stripNamespace(text2, scope) {
|
|
15151
|
+
const prefix = `${scope}::`;
|
|
15152
|
+
let out = "";
|
|
15153
|
+
let from = 0;
|
|
15154
|
+
for (let at = text2.indexOf(prefix); at !== -1; at = text2.indexOf(prefix, from)) {
|
|
15155
|
+
const atBoundary = at === 0 || !isIdChar(text2[at - 1]);
|
|
15156
|
+
out += text2.slice(from, at) + (atBoundary ? "" : prefix);
|
|
15157
|
+
from = at + prefix.length;
|
|
15158
|
+
}
|
|
15159
|
+
return out + text2.slice(from);
|
|
15160
|
+
}
|
|
15161
|
+
function isIdChar(ch) {
|
|
15162
|
+
return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_" || ch === "-" || ch === "." || ch === ":";
|
|
15163
|
+
}
|
|
15164
|
+
function isWithinOrEqual(dir, target) {
|
|
15165
|
+
const rel2 = path18.relative(dir, target);
|
|
15166
|
+
return rel2 === "" || !rel2.startsWith("..") && !path18.isAbsolute(rel2);
|
|
15167
|
+
}
|
|
15168
|
+
function dedupeIssues(list2) {
|
|
15169
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15170
|
+
return list2.filter((i) => {
|
|
15171
|
+
const key = `${i.code}|${i.specId ?? ""}|${i.message}`;
|
|
15172
|
+
if (seen.has(key)) return false;
|
|
15173
|
+
seen.add(key);
|
|
15174
|
+
return true;
|
|
15175
|
+
});
|
|
15176
|
+
}
|
|
15102
15177
|
function settledStatusBearing(loaded) {
|
|
15103
15178
|
let settled;
|
|
15104
15179
|
try {
|
|
@@ -15126,7 +15201,7 @@ function settledStatusBearing(loaded) {
|
|
|
15126
15201
|
function validateAsComplete(options) {
|
|
15127
15202
|
return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
|
|
15128
15203
|
}
|
|
15129
|
-
var path18,
|
|
15204
|
+
var path18, RESOLUTION_FAILURE_CODES, SEVERITY_RANK;
|
|
15130
15205
|
var init_validation = __esm({
|
|
15131
15206
|
"src/core/validation.ts"() {
|
|
15132
15207
|
"use strict";
|
|
@@ -15142,26 +15217,15 @@ var init_validation = __esm({
|
|
|
15142
15217
|
init_fs();
|
|
15143
15218
|
path18 = __toESM(require("path"));
|
|
15144
15219
|
init_approval();
|
|
15145
|
-
|
|
15220
|
+
RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
15146
15221
|
"UNDEFINED_TYPE_REFERENCE",
|
|
15147
15222
|
"INVALID_DEPENDENCY_REFERENCE",
|
|
15148
15223
|
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
15149
15224
|
"INVALID_SUBSYSTEM_REFERENCE",
|
|
15150
|
-
"UNDECLARED_DEPENDENCY_CALL",
|
|
15151
15225
|
"INVALID_TRUSTED_LINK",
|
|
15152
|
-
"CROSS_SUBSYSTEM_NON_ADAPTER",
|
|
15153
15226
|
"CROSS_TREE_REF_UNRESOLVED"
|
|
15154
15227
|
]);
|
|
15155
|
-
|
|
15156
|
-
"MISSING_SOURCE_FILE",
|
|
15157
|
-
"SOURCE_PATH_ESCAPES_ROOT",
|
|
15158
|
-
"MISSING_SOURCE_PATH",
|
|
15159
|
-
"UNREALIZED_METHOD",
|
|
15160
|
-
"CONFORMANCE_ANALYSIS_SKIPPED",
|
|
15161
|
-
"CONFORMANCE_DEGRADED",
|
|
15162
|
-
"UNDECLARED_DEPENDENCY",
|
|
15163
|
-
"UNREALIZED_DEPENDENCY"
|
|
15164
|
-
]);
|
|
15228
|
+
SEVERITY_RANK = { off: 0, warning: 1, error: 2 };
|
|
15165
15229
|
}
|
|
15166
15230
|
});
|
|
15167
15231
|
|
|
@@ -15796,6 +15860,20 @@ function qualifyId(id, prefix, rootSubsystems) {
|
|
|
15796
15860
|
}
|
|
15797
15861
|
return prefix ? `${prefix}::${id}` : id;
|
|
15798
15862
|
}
|
|
15863
|
+
function qualifyDeclaredId(id, prefix, mountRealization = false) {
|
|
15864
|
+
if (!id || !prefix) return id;
|
|
15865
|
+
if (id.startsWith("::") || id.startsWith("super::")) {
|
|
15866
|
+
return qualifyId(id, prefix, NO_ROOT_SUBSYSTEMS);
|
|
15867
|
+
}
|
|
15868
|
+
if (mountRealization && id === prefix.split("::").pop()) {
|
|
15869
|
+
return prefix;
|
|
15870
|
+
}
|
|
15871
|
+
return `${prefix}::${id}`;
|
|
15872
|
+
}
|
|
15873
|
+
function qualifySubsystemRef(id, prefix, rootSubsystems) {
|
|
15874
|
+
if (prefix && !id.includes("::") && id === prefix.split("::").pop()) return prefix;
|
|
15875
|
+
return qualifyId(id, prefix, rootSubsystems);
|
|
15876
|
+
}
|
|
15799
15877
|
function splitNamespace(qualifiedId2) {
|
|
15800
15878
|
if (!qualifiedId2.includes("::")) {
|
|
15801
15879
|
return { prefix: "", localId: qualifiedId2 };
|
|
@@ -15991,6 +16069,19 @@ function stripNamespaceFromImplementation(spec, prefix) {
|
|
|
15991
16069
|
}))
|
|
15992
16070
|
};
|
|
15993
16071
|
}
|
|
16072
|
+
function childRelativeFilePaths(spec, authoringRoot, childRoot) {
|
|
16073
|
+
const reexpress = (p) => {
|
|
16074
|
+
if (path19.isAbsolute(p)) return p;
|
|
16075
|
+
const rel2 = path19.relative(childRoot, path19.resolve(authoringRoot, p));
|
|
16076
|
+
const inside = rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path19.sep}`) && !path19.isAbsolute(rel2);
|
|
16077
|
+
return inside ? rel2.replace(/\\/g, "/") : p;
|
|
16078
|
+
};
|
|
16079
|
+
return {
|
|
16080
|
+
...spec,
|
|
16081
|
+
...spec.sourcePath ? { sourcePath: reexpress(spec.sourcePath) } : {},
|
|
16082
|
+
...spec.simPath ? { simPath: reexpress(spec.simPath) } : {}
|
|
16083
|
+
};
|
|
16084
|
+
}
|
|
15994
16085
|
function stripNamespaceFromType(spec, prefix) {
|
|
15995
16086
|
return {
|
|
15996
16087
|
...spec,
|
|
@@ -16183,7 +16274,14 @@ function buildProjectGraph(level) {
|
|
|
16183
16274
|
return buildGraphModel(level);
|
|
16184
16275
|
}
|
|
16185
16276
|
function resolveChainingParent() {
|
|
16186
|
-
|
|
16277
|
+
const reach = getRequestParentReach();
|
|
16278
|
+
if (reach && !reach.parentReach) return null;
|
|
16279
|
+
const parent = findChainingParent(getProjectRoot());
|
|
16280
|
+
if (parent && reach?.topRoot) {
|
|
16281
|
+
const fromTop = path19.relative(path19.resolve(reach.topRoot), path19.resolve(parent.parentRoot));
|
|
16282
|
+
if (fromTop === ".." || fromTop.startsWith(`..${path19.sep}`) || path19.isAbsolute(fromTop)) return null;
|
|
16283
|
+
}
|
|
16284
|
+
return parent;
|
|
16187
16285
|
}
|
|
16188
16286
|
function computeGateStateId() {
|
|
16189
16287
|
let gate = {};
|
|
@@ -16248,7 +16346,7 @@ function findLegacySpecFiles() {
|
|
|
16248
16346
|
function updateSpec(kind, id, delta, hooks) {
|
|
16249
16347
|
return current().updateSpec(kind, id, delta, hooks);
|
|
16250
16348
|
}
|
|
16251
|
-
var fs13, path19, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
16349
|
+
var fs13, path19, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
16252
16350
|
var init_specs2 = __esm({
|
|
16253
16351
|
"src/core/specs.ts"() {
|
|
16254
16352
|
"use strict";
|
|
@@ -16263,6 +16361,7 @@ var init_specs2 = __esm({
|
|
|
16263
16361
|
init_models();
|
|
16264
16362
|
init_narrative_labels();
|
|
16265
16363
|
init_diagram();
|
|
16364
|
+
NO_ROOT_SUBSYSTEMS = /* @__PURE__ */ new Set();
|
|
16266
16365
|
SIGNATURE_TTL_MS = 2e3;
|
|
16267
16366
|
SpecWorkspace = class {
|
|
16268
16367
|
constructor(rootDir) {
|
|
@@ -16309,6 +16408,18 @@ var init_specs2 = __esm({
|
|
|
16309
16408
|
this.lastSignatureCheckMs = Date.now();
|
|
16310
16409
|
return this.cachedIndex;
|
|
16311
16410
|
}
|
|
16411
|
+
/**
|
|
16412
|
+
* The id a scan-time loader issue is anchored to: the refused spec's own id
|
|
16413
|
+
* when the file got far enough to carry one, else its name on disk (the
|
|
16414
|
+
* containing directory in the nested layout, where every file is `.index` /
|
|
16415
|
+
* `.interface`), qualified into the mount's namespace exactly as the loader
|
|
16416
|
+
* qualifies the spec itself — so scoping a validate to a mount keeps it.
|
|
16417
|
+
*/
|
|
16418
|
+
loaderIssueSpecId(file, rawId, namespacePrefix) {
|
|
16419
|
+
const stem2 = path19.basename(file, ".yaml");
|
|
16420
|
+
const local = rawId ?? (stem2.startsWith(".") ? path19.basename(path19.dirname(file)) : stem2);
|
|
16421
|
+
return namespacePrefix ? qualifyId(local, namespacePrefix, this.rootSubsystems) : local;
|
|
16422
|
+
}
|
|
16312
16423
|
scanSpecsForProject(projectDir, namespacePrefix, visitedDirs, maxDepth, currentDepth) {
|
|
16313
16424
|
const index = emptyIndex();
|
|
16314
16425
|
const projectPaths = aiPathsAt(projectDir);
|
|
@@ -16322,6 +16433,7 @@ var init_specs2 = __esm({
|
|
|
16322
16433
|
const normFile = path19.normalize(file);
|
|
16323
16434
|
if (normFile === systemYaml) continue;
|
|
16324
16435
|
let detectedType = "spec";
|
|
16436
|
+
let rawId;
|
|
16325
16437
|
try {
|
|
16326
16438
|
const raw = readYamlFile(file);
|
|
16327
16439
|
if (raw === null || typeof raw !== "object") {
|
|
@@ -16329,10 +16441,12 @@ var init_specs2 = __esm({
|
|
|
16329
16441
|
severity: "error",
|
|
16330
16442
|
code: "INVALID_YAML",
|
|
16331
16443
|
message: `Spec file "${file}" is not a valid YAML object or is empty.`,
|
|
16332
|
-
specId:
|
|
16444
|
+
specId: this.loaderIssueSpecId(file, void 0, namespacePrefix)
|
|
16333
16445
|
});
|
|
16334
16446
|
continue;
|
|
16335
16447
|
}
|
|
16448
|
+
const idField = raw.id;
|
|
16449
|
+
if (typeof idField === "string" && idField) rawId = idField;
|
|
16336
16450
|
if ("parentSystem" in raw) {
|
|
16337
16451
|
detectedType = "subsystem";
|
|
16338
16452
|
const parsed = SubsystemSpecSchema.parse(raw);
|
|
@@ -16384,21 +16498,20 @@ var init_specs2 = __esm({
|
|
|
16384
16498
|
severity: "error",
|
|
16385
16499
|
code: "UNKNOWN_SPEC_TYPE",
|
|
16386
16500
|
message: `Spec file "${file}" does not match any recognized L1-L4 schema structure.`,
|
|
16387
|
-
specId:
|
|
16501
|
+
specId: this.loaderIssueSpecId(file, rawId, namespacePrefix)
|
|
16388
16502
|
});
|
|
16389
16503
|
}
|
|
16390
16504
|
} catch (e) {
|
|
16391
|
-
const filename = path19.basename(file, ".yaml");
|
|
16392
16505
|
this.loaderIssues.push({
|
|
16393
16506
|
severity: "error",
|
|
16394
16507
|
code: "SCHEMA_VALIDATION_ERROR",
|
|
16395
16508
|
message: `Failed to parse ${detectedType} spec "${file}": ${e.message || String(e)}`,
|
|
16396
|
-
specId:
|
|
16509
|
+
specId: this.loaderIssueSpecId(file, rawId, namespacePrefix)
|
|
16397
16510
|
});
|
|
16398
16511
|
}
|
|
16399
16512
|
}
|
|
16400
16513
|
index.subsystems = index.subsystems.map((sub) => {
|
|
16401
|
-
const qualifiedSubId = namespacePrefix ?
|
|
16514
|
+
const qualifiedSubId = namespacePrefix ? qualifyDeclaredId(sub.id, namespacePrefix, true) : sub.id;
|
|
16402
16515
|
const componentPrefix = sub.projectPath ? qualifiedSubId : namespacePrefix;
|
|
16403
16516
|
return {
|
|
16404
16517
|
...sub,
|
|
@@ -16417,14 +16530,14 @@ var init_specs2 = __esm({
|
|
|
16417
16530
|
const originalSubsystemPaths = index.paths.subsystem;
|
|
16418
16531
|
index.paths.subsystem = {};
|
|
16419
16532
|
for (const [k, v] of Object.entries(originalSubsystemPaths)) {
|
|
16420
|
-
const qualifiedK = namespacePrefix ?
|
|
16533
|
+
const qualifiedK = namespacePrefix ? qualifyDeclaredId(k, namespacePrefix, true) : k;
|
|
16421
16534
|
index.paths.subsystem[qualifiedK] = v;
|
|
16422
16535
|
}
|
|
16423
16536
|
if (namespacePrefix) {
|
|
16424
16537
|
index.components = index.components.map((comp) => ({
|
|
16425
16538
|
...comp,
|
|
16426
|
-
id:
|
|
16427
|
-
subsystem:
|
|
16539
|
+
id: qualifyDeclaredId(comp.id, namespacePrefix),
|
|
16540
|
+
subsystem: qualifySubsystemRef(comp.subsystem, namespacePrefix, this.rootSubsystems),
|
|
16428
16541
|
owns: comp.owns.map((o) => qualifyId(o, namespacePrefix, this.rootSubsystems)),
|
|
16429
16542
|
dependsOn: comp.dependsOn.map((d) => qualifyId(d, namespacePrefix, this.rootSubsystems)),
|
|
16430
16543
|
dispatch: comp.dispatch?.map((b) => ({
|
|
@@ -16434,12 +16547,12 @@ var init_specs2 = __esm({
|
|
|
16434
16547
|
}));
|
|
16435
16548
|
index.interfaces = index.interfaces.map((intf) => ({
|
|
16436
16549
|
...intf,
|
|
16437
|
-
id:
|
|
16550
|
+
id: qualifyDeclaredId(intf.id, namespacePrefix),
|
|
16438
16551
|
component: qualifyId(intf.component, namespacePrefix, this.rootSubsystems)
|
|
16439
16552
|
}));
|
|
16440
16553
|
index.implementations = index.implementations.map((impl) => ({
|
|
16441
16554
|
...impl,
|
|
16442
|
-
id:
|
|
16555
|
+
id: qualifyDeclaredId(impl.id, namespacePrefix),
|
|
16443
16556
|
contract: qualifyId(impl.contract, namespacePrefix, this.rootSubsystems),
|
|
16444
16557
|
methods: impl.methods.map((m) => ({
|
|
16445
16558
|
...m,
|
|
@@ -16451,13 +16564,13 @@ var init_specs2 = __esm({
|
|
|
16451
16564
|
}));
|
|
16452
16565
|
index.types = index.types.map((t) => ({
|
|
16453
16566
|
...t,
|
|
16454
|
-
id:
|
|
16455
|
-
subsystem: t.subsystem ?
|
|
16567
|
+
id: qualifyDeclaredId(t.id, namespacePrefix),
|
|
16568
|
+
subsystem: t.subsystem ? qualifySubsystemRef(t.subsystem, namespacePrefix, this.rootSubsystems) : void 0,
|
|
16456
16569
|
group: t.group ? qualifyId(t.group, namespacePrefix, this.rootSubsystems) : void 0
|
|
16457
16570
|
}));
|
|
16458
16571
|
index.groups = index.groups.map((g) => ({
|
|
16459
16572
|
...g,
|
|
16460
|
-
id:
|
|
16573
|
+
id: qualifyDeclaredId(g.id, namespacePrefix)
|
|
16461
16574
|
}));
|
|
16462
16575
|
const originalPaths = index.paths;
|
|
16463
16576
|
index.paths = {
|
|
@@ -16469,19 +16582,19 @@ var init_specs2 = __esm({
|
|
|
16469
16582
|
group: {}
|
|
16470
16583
|
};
|
|
16471
16584
|
for (const [k, v] of Object.entries(originalPaths.component)) {
|
|
16472
|
-
index.paths.component[
|
|
16585
|
+
index.paths.component[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16473
16586
|
}
|
|
16474
16587
|
for (const [k, v] of Object.entries(originalPaths.interface)) {
|
|
16475
|
-
index.paths.interface[
|
|
16588
|
+
index.paths.interface[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16476
16589
|
}
|
|
16477
16590
|
for (const [k, v] of Object.entries(originalPaths.implementation)) {
|
|
16478
|
-
index.paths.implementation[
|
|
16591
|
+
index.paths.implementation[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16479
16592
|
}
|
|
16480
16593
|
for (const [k, v] of Object.entries(originalPaths.type)) {
|
|
16481
|
-
index.paths.type[
|
|
16594
|
+
index.paths.type[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16482
16595
|
}
|
|
16483
16596
|
for (const [k, v] of Object.entries(originalPaths.group)) {
|
|
16484
|
-
index.paths.group[
|
|
16597
|
+
index.paths.group[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16485
16598
|
}
|
|
16486
16599
|
}
|
|
16487
16600
|
if (currentDepth < maxDepth) {
|
|
@@ -16875,7 +16988,11 @@ var init_specs2 = __esm({
|
|
|
16875
16988
|
}
|
|
16876
16989
|
prepareImplementationForWrite(spec) {
|
|
16877
16990
|
const prefix = this.writePrefixFor(spec.id);
|
|
16878
|
-
|
|
16991
|
+
if (!prefix) return spec;
|
|
16992
|
+
const stripped = stripNamespaceFromImplementation(spec, prefix);
|
|
16993
|
+
const mount = this.getSubprojectPrefix(spec.id);
|
|
16994
|
+
const childRoot = mount ? this.resolveSubprojectForNamespace(mount) : null;
|
|
16995
|
+
return childRoot ? childRelativeFilePaths(stripped, this.rootDir, childRoot) : stripped;
|
|
16879
16996
|
}
|
|
16880
16997
|
prepareTypeForWrite(spec) {
|
|
16881
16998
|
const prefix = this.writePrefixFor(spec.id);
|
|
@@ -18174,6 +18291,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
18174
18291
|
});
|
|
18175
18292
|
ensureDir(path20.dirname(childFooDir));
|
|
18176
18293
|
fs14.renameSync(fooDir, childFooDir);
|
|
18294
|
+
rebaseImplementationPaths(childFooDir, parentRoot, childDir);
|
|
18177
18295
|
patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
|
|
18178
18296
|
s.parentSystem = childSystemName;
|
|
18179
18297
|
delete s.projectPath;
|
|
@@ -18224,6 +18342,7 @@ function internalizeSubsystem(subsystemId) {
|
|
|
18224
18342
|
fs14.rmSync(fooDir, { recursive: true, force: true });
|
|
18225
18343
|
ensureDir(path20.dirname(fooDir));
|
|
18226
18344
|
fs14.renameSync(childFooDir, fooDir);
|
|
18345
|
+
rebaseImplementationPaths(fooDir, childDir, parentRoot);
|
|
18227
18346
|
patchSubsystemIndex(path20.join(fooDir, ".index.yaml"), (s) => {
|
|
18228
18347
|
s.parentSystem = parentSystemName;
|
|
18229
18348
|
delete s.projectPath;
|
|
@@ -18324,6 +18443,28 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18324
18443
|
if (changed) writeYamlFile(file, raw);
|
|
18325
18444
|
}
|
|
18326
18445
|
}
|
|
18446
|
+
function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
|
|
18447
|
+
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
18448
|
+
let raw;
|
|
18449
|
+
try {
|
|
18450
|
+
raw = readYamlFile(file);
|
|
18451
|
+
} catch {
|
|
18452
|
+
continue;
|
|
18453
|
+
}
|
|
18454
|
+
if (!raw || typeof raw !== "object" || !("contract" in raw)) continue;
|
|
18455
|
+
let changed = false;
|
|
18456
|
+
for (const key of ["sourcePath", "simPath"]) {
|
|
18457
|
+
const p = raw[key];
|
|
18458
|
+
if (typeof p !== "string" || p === "" || path20.isAbsolute(p)) continue;
|
|
18459
|
+
const next = toPosixPath(path20.relative(toRoot, path20.resolve(fromRoot, p)));
|
|
18460
|
+
if (next !== p) {
|
|
18461
|
+
raw[key] = next;
|
|
18462
|
+
changed = true;
|
|
18463
|
+
}
|
|
18464
|
+
}
|
|
18465
|
+
if (changed) writeYamlFile(file, raw);
|
|
18466
|
+
}
|
|
18467
|
+
}
|
|
18327
18468
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
18328
18469
|
if (!fs14.existsSync(indexPath)) return;
|
|
18329
18470
|
const raw = readYamlFile(indexPath);
|
|
@@ -23578,7 +23719,7 @@ NOTICE:
|
|
|
23578
23719
|
name: import_zod10.z.string().describe("Human-readable implementation name"),
|
|
23579
23720
|
description: import_zod10.z.string().describe("Implementation details"),
|
|
23580
23721
|
contract: import_zod10.z.string().describe("The L3 Interface contract ID this implements"),
|
|
23581
|
-
sourcePath: import_zod10.z.string().optional().describe("Optional: target source code file path relative to project root"),
|
|
23722
|
+
sourcePath: import_zod10.z.string().optional().describe("Optional: target source code file path relative to project root \u2014 for a chained subproject's implementation (qualified id), relative to that subproject's root; a path given relative to this root that lands inside the subproject is re-expressed for you"),
|
|
23582
23723
|
simPath: import_zod10.z.string().optional().describe("Optional: the committed integration-sim harness file (project-relative; N:1 sharing allowed). The validator proves it exists and its import graph wires the REAL modules (this component + each direct dependency; technology adapters may stay faked) \u2014 running it is CI's job. Declaring the first simPath in a subsystem activates MISSING_INTEGRATION_SIM for its other complete non-leaf implementations"),
|
|
23583
23724
|
technologies: import_zod10.z.array(import_zod10.z.string()).optional().describe(`External technologies this implementation binds to (e.g. ["mysql"]) \u2014 declares this component's ownership tree as the technology's home; references outside it are flagged (TECH_LEAKAGE) and contract identifiers must stay intent-language. Only for Adapter/Store/Registry/Index components.`),
|
|
23584
23725
|
detail: detailEnum.optional().describe("Spec-level narrative detail default for all methods"),
|
|
@@ -23767,7 +23908,8 @@ NOTICE:
|
|
|
23767
23908
|
return json({
|
|
23768
23909
|
valid: result.valid,
|
|
23769
23910
|
errors: result.issues.filter((i) => i.severity === "error"),
|
|
23770
|
-
warnings: result.issues.filter((i) => i.severity === "warning")
|
|
23911
|
+
warnings: result.issues.filter((i) => i.severity === "warning"),
|
|
23912
|
+
...result.resolvedThrough ? { resolvedThrough: result.resolvedThrough } : {}
|
|
23771
23913
|
});
|
|
23772
23914
|
} catch (e) {
|
|
23773
23915
|
return errText(String(e));
|
|
@@ -25189,19 +25331,19 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
25189
25331
|
const backupDir = path28.join(root, BACKUP_DIR, timestampSlug());
|
|
25190
25332
|
let backedUp = false;
|
|
25191
25333
|
for (const rootRel of roots) {
|
|
25192
|
-
const
|
|
25193
|
-
const stagedSpecDir = path28.join(stagingDir,
|
|
25334
|
+
const relative15 = rootRel === "." ? "" : rootRel;
|
|
25335
|
+
const stagedSpecDir = path28.join(stagingDir, relative15, ".wai");
|
|
25194
25336
|
if (!fs18.existsSync(stagedSpecDir)) continue;
|
|
25195
|
-
const liveSpecDir = liveSpecDirFor(path28.join(root,
|
|
25337
|
+
const liveSpecDir = liveSpecDirFor(path28.join(root, relative15));
|
|
25196
25338
|
if (backupExisting && liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
25197
|
-
const backupTarget = path28.join(backupDir,
|
|
25339
|
+
const backupTarget = path28.join(backupDir, relative15, path28.basename(liveSpecDir));
|
|
25198
25340
|
fs18.mkdirSync(path28.dirname(backupTarget), { recursive: true });
|
|
25199
25341
|
fs18.renameSync(liveSpecDir, backupTarget);
|
|
25200
25342
|
backedUp = true;
|
|
25201
25343
|
} else if (liveSpecDir && fs18.existsSync(liveSpecDir)) {
|
|
25202
25344
|
fs18.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
25203
25345
|
}
|
|
25204
|
-
const target = path28.join(root,
|
|
25346
|
+
const target = path28.join(root, relative15, ".wai");
|
|
25205
25347
|
fs18.mkdirSync(path28.dirname(target), { recursive: true });
|
|
25206
25348
|
if (fs18.existsSync(target)) fs18.rmSync(target, { recursive: true, force: true });
|
|
25207
25349
|
fs18.renameSync(stagedSpecDir, target);
|
|
@@ -26166,9 +26308,9 @@ init_loader();
|
|
|
26166
26308
|
init_validation();
|
|
26167
26309
|
function isCiDraftWaivable(issue2) {
|
|
26168
26310
|
if (issue2.severity !== "warning") return false;
|
|
26311
|
+
if (issue2.code === "DRAFT_SUBSYSTEM_WARNING") return true;
|
|
26169
26312
|
if (issue2.code === "DRAFT_COMPONENT_WARNING") return true;
|
|
26170
26313
|
if (issue2.code === "UNUSED_COMPONENT") return issue2.draftContext === true;
|
|
26171
|
-
if (issue2.crossTreeContext === true) return true;
|
|
26172
26314
|
return false;
|
|
26173
26315
|
}
|
|
26174
26316
|
async function runValidate(options = {}) {
|
|
@@ -26233,6 +26375,11 @@ async function runValidate(options = {}) {
|
|
|
26233
26375
|
scopeSubsystem: options.subsystem,
|
|
26234
26376
|
recursive: options.recursive ?? true
|
|
26235
26377
|
});
|
|
26378
|
+
if (sddResult.resolvedThrough) {
|
|
26379
|
+
logger.info(
|
|
26380
|
+
`Chained subproject \u2014 verified through the parent project at ${sddResult.resolvedThrough.root} (mount "${sddResult.resolvedThrough.scope}").`
|
|
26381
|
+
);
|
|
26382
|
+
}
|
|
26236
26383
|
if (sddResult.issues.length === 0) {
|
|
26237
26384
|
logger.success("Spec tree is valid and component type boundaries are enforced.");
|
|
26238
26385
|
} else {
|
|
@@ -26427,14 +26574,14 @@ var http = __toESM(require("http"));
|
|
|
26427
26574
|
var https = __toESM(require("https"));
|
|
26428
26575
|
init_defaults();
|
|
26429
26576
|
function downloadFile(url, dest) {
|
|
26430
|
-
return new Promise((
|
|
26577
|
+
return new Promise((resolve30, reject) => {
|
|
26431
26578
|
const file = fs22.createWriteStream(dest);
|
|
26432
26579
|
const get4 = url.startsWith("https://") ? https.get : http.get;
|
|
26433
26580
|
get4(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
|
|
26434
26581
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
26435
26582
|
file.close();
|
|
26436
26583
|
res.destroy();
|
|
26437
|
-
downloadFile(res.headers.location, dest).then(
|
|
26584
|
+
downloadFile(res.headers.location, dest).then(resolve30).catch(reject);
|
|
26438
26585
|
return;
|
|
26439
26586
|
}
|
|
26440
26587
|
if (res.statusCode !== 200) {
|
|
@@ -26446,7 +26593,7 @@ function downloadFile(url, dest) {
|
|
|
26446
26593
|
res.pipe(file);
|
|
26447
26594
|
file.on("finish", () => {
|
|
26448
26595
|
res.destroy();
|
|
26449
|
-
file.close(() =>
|
|
26596
|
+
file.close(() => resolve30());
|
|
26450
26597
|
});
|
|
26451
26598
|
file.on("error", (err) => {
|
|
26452
26599
|
res.destroy();
|
|
@@ -26623,7 +26770,7 @@ function releaseChannelLabel(tag) {
|
|
|
26623
26770
|
return version.slice(dash + 1).replace(/\.\d+$/, "") || "stable";
|
|
26624
26771
|
}
|
|
26625
26772
|
function fetchReleases(repo) {
|
|
26626
|
-
return new Promise((
|
|
26773
|
+
return new Promise((resolve30, reject) => {
|
|
26627
26774
|
const url = `https://api.github.com/repos/${repo}/releases?per_page=100`;
|
|
26628
26775
|
const options = {
|
|
26629
26776
|
headers: {
|
|
@@ -26643,7 +26790,7 @@ function fetchReleases(repo) {
|
|
|
26643
26790
|
return;
|
|
26644
26791
|
}
|
|
26645
26792
|
try {
|
|
26646
|
-
|
|
26793
|
+
resolve30(JSON.parse(data));
|
|
26647
26794
|
} catch {
|
|
26648
26795
|
reject(new Error("Failed to parse GitHub API response"));
|
|
26649
26796
|
}
|
|
@@ -26818,7 +26965,7 @@ async function filteredCheckbox(config) {
|
|
|
26818
26965
|
32,
|
|
26819
26966
|
Math.max(...items.map((i) => i.label.length))
|
|
26820
26967
|
);
|
|
26821
|
-
return new Promise((
|
|
26968
|
+
return new Promise((resolve30) => {
|
|
26822
26969
|
const checked = /* @__PURE__ */ new Set();
|
|
26823
26970
|
let cursor = 0;
|
|
26824
26971
|
let filterIdx = 0;
|
|
@@ -26885,7 +27032,7 @@ async function filteredCheckbox(config) {
|
|
|
26885
27032
|
);
|
|
26886
27033
|
teardown();
|
|
26887
27034
|
const result = items.filter((_, idx) => checked.has(idx)).map((i) => i.value);
|
|
26888
|
-
|
|
27035
|
+
resolve30(result);
|
|
26889
27036
|
}
|
|
26890
27037
|
function abort() {
|
|
26891
27038
|
process.stdout.write("\n");
|
|
@@ -27635,7 +27782,7 @@ init_logger();
|
|
|
27635
27782
|
init_validation();
|
|
27636
27783
|
init_extensions();
|
|
27637
27784
|
init_loader();
|
|
27638
|
-
async function
|
|
27785
|
+
async function listRules3() {
|
|
27639
27786
|
let overrides = {};
|
|
27640
27787
|
if (isProjectInitialized()) {
|
|
27641
27788
|
try {
|
|
@@ -27650,7 +27797,7 @@ async function listRules2() {
|
|
|
27650
27797
|
};
|
|
27651
27798
|
const ext = loadProjectExtensions();
|
|
27652
27799
|
const packRuleNames = new Set(ext.rules.map((r) => r.name));
|
|
27653
|
-
const active =
|
|
27800
|
+
const active = listRules2();
|
|
27654
27801
|
const builtin = active.filter((r) => !packRuleNames.has(r.name));
|
|
27655
27802
|
const packRules = active.filter((r) => packRuleNames.has(r.name));
|
|
27656
27803
|
const groups = [
|
|
@@ -35085,7 +35232,7 @@ async function awaitApproval(cfg, credential, requestId, timeoutSeconds) {
|
|
|
35085
35232
|
let current2 = req;
|
|
35086
35233
|
while (current2.status === "pending" && Date.now() < deadline) {
|
|
35087
35234
|
const remaining = deadline - Date.now();
|
|
35088
|
-
await new Promise((
|
|
35235
|
+
await new Promise((resolve30) => setTimeout(resolve30, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
|
|
35089
35236
|
expirePendingApprovals(cfg.dataDir, (/* @__PURE__ */ new Date()).toISOString());
|
|
35090
35237
|
current2 = getApprovalRequestById(cfg.dataDir, requestId) ?? current2;
|
|
35091
35238
|
}
|
|
@@ -36312,7 +36459,7 @@ var ShareSnapshotIndex = class {
|
|
|
36312
36459
|
function putSnapshot(dataDir, snapshot) {
|
|
36313
36460
|
return new ShareSnapshotRegistry(new ShareSnapshotStore(dataDir)).put(snapshot);
|
|
36314
36461
|
}
|
|
36315
|
-
function
|
|
36462
|
+
function getSnapshot(dataDir, snapshotId) {
|
|
36316
36463
|
return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).get(snapshotId);
|
|
36317
36464
|
}
|
|
36318
36465
|
function getSnapshotArtifact(dataDir, snapshotId, kind, portalId) {
|
|
@@ -39810,7 +39957,8 @@ var PROJECT_RECORD_TOOLS = /* @__PURE__ */ new Set([
|
|
|
39810
39957
|
"sdd_host_initialize_project",
|
|
39811
39958
|
"sdd_host_get_approval_status",
|
|
39812
39959
|
"sdd_host_await_approval",
|
|
39813
|
-
...PROJECT_OPS_TOOLS
|
|
39960
|
+
...PROJECT_OPS_TOOLS,
|
|
39961
|
+
...LANDSCAPE_DISCOVERY_TOOLS
|
|
39814
39962
|
]);
|
|
39815
39963
|
function jsonRpcRequests(body) {
|
|
39816
39964
|
const arr = Array.isArray(body) ? body : [body];
|
|
@@ -39838,8 +39986,16 @@ var WRITE_TOOL_PREFIXES = [
|
|
|
39838
39986
|
"sdd_move_"
|
|
39839
39987
|
];
|
|
39840
39988
|
var READ_TOOL_PREFIXES = ["sdd_get_", "sdd_validate_"];
|
|
39989
|
+
var READ_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
39990
|
+
"sdd_list_external_interfaces",
|
|
39991
|
+
"listAgents",
|
|
39992
|
+
"getAgent",
|
|
39993
|
+
"listDomains",
|
|
39994
|
+
"validateTopology",
|
|
39995
|
+
"getProjectConfig"
|
|
39996
|
+
]);
|
|
39841
39997
|
function requiredDataPlaneCapability(toolName) {
|
|
39842
|
-
if (READ_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:read";
|
|
39998
|
+
if (READ_TOOL_NAMES.has(toolName) || READ_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:read";
|
|
39843
39999
|
if (WRITE_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:write";
|
|
39844
40000
|
return "project:write";
|
|
39845
40001
|
}
|
|
@@ -40043,7 +40199,9 @@ async function handleMcpRequest(cfg, req, res, body, credential) {
|
|
|
40043
40199
|
});
|
|
40044
40200
|
return;
|
|
40045
40201
|
}
|
|
40046
|
-
|
|
40202
|
+
const topRoot = existingProjectRoot(cfg.dataDir, binding.projectId) ?? binding.rootPath;
|
|
40203
|
+
const parentReach = principal.projects.includes("*") || principal.projects.includes(binding.projectId);
|
|
40204
|
+
await runWithProjectBinding(binding.rootPath, { topRoot, parentReach }, async () => {
|
|
40047
40205
|
const projectId = binding.projectId;
|
|
40048
40206
|
const subproject = binding.subproject;
|
|
40049
40207
|
const confinementError = subprojectConfinementError(projectId, subproject, body);
|
|
@@ -40134,7 +40292,7 @@ function resolveSharedView(cfg, token, meta) {
|
|
|
40134
40292
|
record(cfg.dataDir, link?.id ?? "", meta, check.outcome);
|
|
40135
40293
|
return { found: false, outcome: check.outcome };
|
|
40136
40294
|
}
|
|
40137
|
-
const snapshot =
|
|
40295
|
+
const snapshot = getSnapshot(cfg.dataDir, check.link.snapshotId);
|
|
40138
40296
|
record(cfg.dataDir, check.link.id, meta, "served");
|
|
40139
40297
|
return {
|
|
40140
40298
|
found: true,
|
|
@@ -41343,11 +41501,11 @@ async function runServe(options = {}) {
|
|
|
41343
41501
|
logger.info(` data dir: ${import_chalk17.default.gray(cfg.dataDir)}`);
|
|
41344
41502
|
logger.blank();
|
|
41345
41503
|
logger.info("Press Ctrl+C to stop.");
|
|
41346
|
-
await new Promise((
|
|
41504
|
+
await new Promise((resolve30) => {
|
|
41347
41505
|
const shutdown = () => {
|
|
41348
41506
|
logger.info("Shutting down\u2026");
|
|
41349
41507
|
handle.close();
|
|
41350
|
-
|
|
41508
|
+
resolve30();
|
|
41351
41509
|
};
|
|
41352
41510
|
process.on("SIGINT", shutdown);
|
|
41353
41511
|
process.on("SIGTERM", shutdown);
|
|
@@ -41412,11 +41570,11 @@ async function runDev(options = {}) {
|
|
|
41412
41570
|
logger.blank();
|
|
41413
41571
|
logger.info("An agent edits specs; refresh the page to see the live graph. Press Ctrl+C to stop.");
|
|
41414
41572
|
if (options.open) openBrowser(url);
|
|
41415
|
-
await new Promise((
|
|
41573
|
+
await new Promise((resolve30) => {
|
|
41416
41574
|
const shutdown = () => {
|
|
41417
41575
|
logger.info("Shutting down\u2026");
|
|
41418
41576
|
handle.close();
|
|
41419
|
-
|
|
41577
|
+
resolve30();
|
|
41420
41578
|
};
|
|
41421
41579
|
process.on("SIGINT", shutdown);
|
|
41422
41580
|
process.on("SIGTERM", shutdown);
|
|
@@ -41967,16 +42125,6 @@ async function runSurface(action, options = {}) {
|
|
|
41967
42125
|
}
|
|
41968
42126
|
return;
|
|
41969
42127
|
}
|
|
41970
|
-
case "generate-children": {
|
|
41971
|
-
const written = generateChildSnapshots();
|
|
41972
|
-
if (!written.length) {
|
|
41973
|
-
logger.info("Delivered surfaces are already up to date \u2014 nothing rewritten.");
|
|
41974
|
-
return;
|
|
41975
|
-
}
|
|
41976
|
-
logger.success(`Updated ${written.length} delivered surface(s):`);
|
|
41977
|
-
for (const p of written) logger.info(` ${p}`);
|
|
41978
|
-
return;
|
|
41979
|
-
}
|
|
41980
42128
|
case "externals": {
|
|
41981
42129
|
const entries = listExternalInterfaces();
|
|
41982
42130
|
if (!entries.length) {
|
|
@@ -41991,8 +42139,22 @@ async function runSurface(action, options = {}) {
|
|
|
41991
42139
|
}
|
|
41992
42140
|
return;
|
|
41993
42141
|
}
|
|
42142
|
+
case "pin": {
|
|
42143
|
+
const written = pinFamilySurfaces();
|
|
42144
|
+
if (written === null) {
|
|
42145
|
+
logger.info("This project is not a chained subproject \u2014 there is no parent family to pin.");
|
|
42146
|
+
return;
|
|
42147
|
+
}
|
|
42148
|
+
if (!written.length) {
|
|
42149
|
+
logger.info("Pinned family surfaces are already up to date \u2014 nothing rewritten.");
|
|
42150
|
+
return;
|
|
42151
|
+
}
|
|
42152
|
+
logger.success(`Pinned ${written.length} family surface(s) from the parent:`);
|
|
42153
|
+
for (const p of written) logger.info(` ${p}`);
|
|
42154
|
+
return;
|
|
42155
|
+
}
|
|
41994
42156
|
default:
|
|
41995
|
-
throw new WaironError(`Unknown surface action "${action}" (supported: export, import, list,
|
|
42157
|
+
throw new WaironError(`Unknown surface action "${action}" (supported: export, import, list, externals, pin).`);
|
|
41996
42158
|
}
|
|
41997
42159
|
}
|
|
41998
42160
|
|
|
@@ -42732,12 +42894,6 @@ async function runLock2(options) {
|
|
|
42732
42894
|
logger.info("Cancelled. Nothing was changed.");
|
|
42733
42895
|
return;
|
|
42734
42896
|
}
|
|
42735
|
-
const childPaths = generateChildSnapshots();
|
|
42736
|
-
if (childPaths.length > 0) {
|
|
42737
|
-
logger.blank();
|
|
42738
|
-
logger.success(`Updated ${childPaths.length} delivered surface(s) in the chained children:`);
|
|
42739
|
-
for (const p of childPaths) logger.info(` ${p}`);
|
|
42740
|
-
}
|
|
42741
42897
|
logger.blank();
|
|
42742
42898
|
await runGenerate({ domain: options.subsystem });
|
|
42743
42899
|
logger.blank();
|
|
@@ -42793,7 +42949,7 @@ program.command("doctor").description("Health check: flags stale generated guide
|
|
|
42793
42949
|
await runDoctor({ fix: opts.fix });
|
|
42794
42950
|
});
|
|
42795
42951
|
async function runRules() {
|
|
42796
|
-
await
|
|
42952
|
+
await listRules3();
|
|
42797
42953
|
}
|
|
42798
42954
|
async function runPatterns() {
|
|
42799
42955
|
await listPatterns();
|
|
@@ -43000,7 +43156,7 @@ mcpCmd.command("status").description("Show whether the wairon MCP server is regi
|
|
|
43000
43156
|
program.command("produce <target>").description("project the local project's specs to a producer target (notion | miro)").option("--page <id>", "parent page/board id in the target").option("--token <token>", "integration token (else env, else interactive prompt)").action(async (target, opts) => {
|
|
43001
43157
|
await runProduce(target, { page: opts.page, token: opts.token });
|
|
43002
43158
|
});
|
|
43003
|
-
program.command("surface <action>").description("public surface exchange: export | import | list |
|
|
43159
|
+
program.command("surface <action>").description("public surface exchange: export | import | list | externals | pin").option("--audience <level>", "export ceiling: project | department | instance | partner | external (default instance)").option("--format <fmt>", "export format: native | openapi (default native)").option("--out <path>", "export output path (else print)").option("--portal <id>", "export: select one portal's OpenAPI spec (a multi-portal project renders one document per portal)").option("--source <path>", "import: the surface document (native snapshot YAML or OpenAPI)").option("--origin <origin>", "import provenance: exchanged | authored (default authored)").action(async (action, opts) => {
|
|
43004
43160
|
await runSurface(action, {
|
|
43005
43161
|
audience: opts.audience,
|
|
43006
43162
|
format: opts.format,
|