@wairon/cli 5.1.1-dev.107 → 5.1.1-dev.108
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/README.md +5 -1
- package/dist/cli/index.js +2611 -1237
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +4498 -847
- package/dist/index.js +1768 -551
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/cli.md +114 -18
- package/docs/extending-wairon.md +5 -4
- package/docs/network.md +9 -4
- package/docs/roadmap.md +2 -2
- package/package.json +1 -1
- package/schemas/design-export-2.json +6 -0
package/dist/index.js
CHANGED
|
@@ -3059,8 +3059,8 @@ var require_dist = __commonJS({
|
|
|
3059
3059
|
function slug(name) {
|
|
3060
3060
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3061
3061
|
}
|
|
3062
|
-
var
|
|
3063
|
-
var
|
|
3062
|
+
var fs31 = __toESM2(require("fs"));
|
|
3063
|
+
var path50 = __toESM2(require("path"));
|
|
3064
3064
|
var import_fflate = require_node();
|
|
3065
3065
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
|
|
3066
3066
|
function listEntries(archive) {
|
|
@@ -3098,9 +3098,9 @@ var require_dist = __commonJS({
|
|
|
3098
3098
|
}
|
|
3099
3099
|
function writeTree(destDir, files) {
|
|
3100
3100
|
for (const file of files) {
|
|
3101
|
-
const absolute =
|
|
3102
|
-
|
|
3103
|
-
|
|
3101
|
+
const absolute = path50.join(destDir, file.path);
|
|
3102
|
+
fs31.mkdirSync(path50.dirname(absolute), { recursive: true });
|
|
3103
|
+
fs31.writeFileSync(absolute, file.contents);
|
|
3104
3104
|
}
|
|
3105
3105
|
}
|
|
3106
3106
|
function classifyKind(name, symlinks) {
|
|
@@ -3109,14 +3109,14 @@ var require_dist = __commonJS({
|
|
|
3109
3109
|
return "file";
|
|
3110
3110
|
}
|
|
3111
3111
|
function walkPackDir(root, current2, out) {
|
|
3112
|
-
for (const entry of
|
|
3112
|
+
for (const entry of fs31.readdirSync(current2, { withFileTypes: true })) {
|
|
3113
3113
|
if (entry.isDirectory()) {
|
|
3114
3114
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
3115
|
-
walkPackDir(root,
|
|
3115
|
+
walkPackDir(root, path50.join(current2, entry.name), out);
|
|
3116
3116
|
} else if (entry.isFile()) {
|
|
3117
|
-
const absolute =
|
|
3118
|
-
const relative22 =
|
|
3119
|
-
out.push({ path: relative22, contents:
|
|
3117
|
+
const absolute = path50.join(current2, entry.name);
|
|
3118
|
+
const relative22 = path50.relative(root, absolute).split(path50.sep).join("/");
|
|
3119
|
+
out.push({ path: relative22, contents: fs31.readFileSync(absolute) });
|
|
3120
3120
|
}
|
|
3121
3121
|
}
|
|
3122
3122
|
}
|
|
@@ -3316,8 +3316,8 @@ var require_dist = __commonJS({
|
|
|
3316
3316
|
return normalized === "." ? ".wai/" : `${normalized}/.wai/`;
|
|
3317
3317
|
}
|
|
3318
3318
|
function normalizeRootPath(relativePath) {
|
|
3319
|
-
const
|
|
3320
|
-
return
|
|
3319
|
+
const posix8 = relativePath.split(path210.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
3320
|
+
return posix8 === "" || posix8 === "." ? "." : posix8;
|
|
3321
3321
|
}
|
|
3322
3322
|
function slugify(projectName) {
|
|
3323
3323
|
const slug2 = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -3551,6 +3551,7 @@ __export(src_exports, {
|
|
|
3551
3551
|
SwitchCaseSchema: () => SwitchCaseSchema,
|
|
3552
3552
|
SystemPublicInterfaceSchema: () => SystemPublicInterfaceSchema,
|
|
3553
3553
|
SystemSpecSchema: () => SystemSpecSchema,
|
|
3554
|
+
TECHNOLOGY_PACKAGES: () => TECHNOLOGY_PACKAGES,
|
|
3554
3555
|
TargetConfigSchema: () => TargetConfigSchema,
|
|
3555
3556
|
TechnologySchema: () => TechnologySchema,
|
|
3556
3557
|
TemplateNotFoundError: () => TemplateNotFoundError,
|
|
@@ -3581,7 +3582,9 @@ __export(src_exports, {
|
|
|
3581
3582
|
approvalKeyIn: () => approvalKeyIn,
|
|
3582
3583
|
approvalRecord: () => approvalRecord,
|
|
3583
3584
|
approvalVerdict: () => approvalVerdict,
|
|
3585
|
+
approvedRevision: () => approvedRevision,
|
|
3584
3586
|
approvedStatuses: () => approvedStatuses,
|
|
3587
|
+
asSchemaReads: () => asSchemaReads,
|
|
3585
3588
|
assertContainedProjectPath: () => assertContainedProjectPath,
|
|
3586
3589
|
assertProjectInitialized: () => assertProjectInitialized,
|
|
3587
3590
|
attachRetiredMounts: () => attachRetiredMounts,
|
|
@@ -3829,6 +3832,7 @@ __export(src_exports, {
|
|
|
3829
3832
|
reexpress: () => reexpress,
|
|
3830
3833
|
registerExporter: () => registerExporter,
|
|
3831
3834
|
registerPackRef: () => registerPackRef,
|
|
3835
|
+
registerProjectServer: () => registerProjectServer,
|
|
3832
3836
|
reinjectLocalGuides: () => reinjectLocalGuides,
|
|
3833
3837
|
relationHealth: () => relationHealth,
|
|
3834
3838
|
removeDomain: () => removeDomain,
|
|
@@ -3841,6 +3845,7 @@ __export(src_exports, {
|
|
|
3841
3845
|
renameField: () => renameField,
|
|
3842
3846
|
renameId: () => renameProjectId,
|
|
3843
3847
|
renameMethod: () => renameMethod,
|
|
3848
|
+
renameParam: () => renameParam,
|
|
3844
3849
|
renameType: () => renameType,
|
|
3845
3850
|
renderDiagram: () => renderDiagram2,
|
|
3846
3851
|
repairForeignStepFields: () => repairForeignStepFields,
|
|
@@ -3897,8 +3902,10 @@ __export(src_exports, {
|
|
|
3897
3902
|
storedMethodSignature: () => storedMethodSignature,
|
|
3898
3903
|
storedTypeMethod: () => storedTypeMethod,
|
|
3899
3904
|
subsystemDesignView: () => subsystemDesignView,
|
|
3905
|
+
surfaceChanges: () => surfaceChanges,
|
|
3900
3906
|
syncContextFiles: () => syncContextFiles,
|
|
3901
3907
|
technologyName: () => technologyName,
|
|
3908
|
+
technologyPackages: () => technologyPackages,
|
|
3902
3909
|
technologyTokens: () => technologyTokens,
|
|
3903
3910
|
toMarkdown: () => toMarkdown,
|
|
3904
3911
|
transportExportKind: () => transportExportKind,
|
|
@@ -3989,22 +3996,22 @@ function sampleOf(file) {
|
|
|
3989
3996
|
if (fd !== void 0) fs.closeSync(fd);
|
|
3990
3997
|
}
|
|
3991
3998
|
}
|
|
3992
|
-
function attributeMatches(pattern,
|
|
3999
|
+
function attributeMatches(pattern, relative22) {
|
|
3993
4000
|
const glob = pattern.replace(/^\//, "");
|
|
3994
|
-
const target = glob.includes("/") ?
|
|
4001
|
+
const target = glob.includes("/") ? relative22 : path.posix.basename(relative22);
|
|
3995
4002
|
const source = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\u0000/g, "(?:.*/)?");
|
|
3996
4003
|
return new RegExp(`^${source}$`).test(target);
|
|
3997
4004
|
}
|
|
3998
4005
|
function attributeEol(attributesFile, file) {
|
|
3999
4006
|
const text3 = readFileOrNull(attributesFile);
|
|
4000
4007
|
if (text3 === null) return null;
|
|
4001
|
-
const
|
|
4008
|
+
const relative22 = path.relative(path.dirname(attributesFile), file).split(path.sep).join("/");
|
|
4002
4009
|
let eol = null;
|
|
4003
4010
|
for (const raw of text3.split(/\r?\n/)) {
|
|
4004
4011
|
const line = raw.trim();
|
|
4005
4012
|
if (line === "" || line.startsWith("#")) continue;
|
|
4006
4013
|
const [pattern, ...attrs] = line.split(/\s+/);
|
|
4007
|
-
if (!attributeMatches(pattern,
|
|
4014
|
+
if (!attributeMatches(pattern, relative22)) continue;
|
|
4008
4015
|
for (const attr of attrs) {
|
|
4009
4016
|
if (attr === "eol=crlf") eol = "\r\n";
|
|
4010
4017
|
else if (attr === "eol=lf") eol = "\n";
|
|
@@ -4285,7 +4292,7 @@ function defaultTargetConfig(type) {
|
|
|
4285
4292
|
enabled: true
|
|
4286
4293
|
};
|
|
4287
4294
|
}
|
|
4288
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
4295
|
+
var WAIRON_VERSION = "5.1.1-dev.108";
|
|
4289
4296
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
4290
4297
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
4291
4298
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -4406,7 +4413,7 @@ function cacheRoot() {
|
|
|
4406
4413
|
return root;
|
|
4407
4414
|
}
|
|
4408
4415
|
function git(args, cwd) {
|
|
4409
|
-
return (0, import_child_process.execFileSync)("git", args, {
|
|
4416
|
+
return (0, import_child_process.execFileSync)("git", ["-c", "core.longpaths=true", ...args], {
|
|
4410
4417
|
cwd,
|
|
4411
4418
|
encoding: "utf8",
|
|
4412
4419
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4504,6 +4511,20 @@ function repositoryRoot(directory) {
|
|
|
4504
4511
|
at = up;
|
|
4505
4512
|
}
|
|
4506
4513
|
}
|
|
4514
|
+
function lastCommitOf(directory, pathspec) {
|
|
4515
|
+
try {
|
|
4516
|
+
return git(["log", "-1", "--format=%H", "--", pathspec], directory).trim() || null;
|
|
4517
|
+
} catch {
|
|
4518
|
+
return null;
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
function commitOf(directory, ref) {
|
|
4522
|
+
try {
|
|
4523
|
+
return git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], directory).trim() || null;
|
|
4524
|
+
} catch {
|
|
4525
|
+
return null;
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4507
4528
|
|
|
4508
4529
|
// src/utils/yaml.ts
|
|
4509
4530
|
var yaml2 = __toESM(require("js-yaml"));
|
|
@@ -5838,23 +5859,23 @@ function childrenOf(tokens, index) {
|
|
|
5838
5859
|
}
|
|
5839
5860
|
return out;
|
|
5840
5861
|
}
|
|
5841
|
-
function locateScalar(tokens,
|
|
5862
|
+
function locateScalar(tokens, path50) {
|
|
5842
5863
|
let region = tokens;
|
|
5843
|
-
for (let k = 0; k <
|
|
5844
|
-
const segment =
|
|
5845
|
-
if (region.length === 0) return `nothing is nested at ${
|
|
5864
|
+
for (let k = 0; k < path50.length; k += 1) {
|
|
5865
|
+
const segment = path50[k];
|
|
5866
|
+
if (region.length === 0) return `nothing is nested at ${path50.slice(0, k).join(".")}`;
|
|
5846
5867
|
const base = Math.min(...region.map((t) => t.col));
|
|
5847
|
-
const last = k ===
|
|
5868
|
+
const last = k === path50.length - 1;
|
|
5848
5869
|
let at;
|
|
5849
5870
|
const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
|
|
5850
5871
|
if (items.length === 0) {
|
|
5851
5872
|
at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
|
|
5852
|
-
if (at < 0) return `no block key "${segment}" at ${
|
|
5873
|
+
if (at < 0) return `no block key "${segment}" at ${path50.slice(0, k).join(".") || "the document root"}`;
|
|
5853
5874
|
if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
|
|
5854
5875
|
} else {
|
|
5855
|
-
if (!/^\d+$/.test(segment)) return `"${segment}" is not a sequence index at ${
|
|
5876
|
+
if (!/^\d+$/.test(segment)) return `"${segment}" is not a sequence index at ${path50.slice(0, k).join(".") || "the document root"}`;
|
|
5856
5877
|
const index = Number(segment);
|
|
5857
|
-
if (index >= items.length) return `no block sequence item ${segment} at ${
|
|
5878
|
+
if (index >= items.length) return `no block sequence item ${segment} at ${path50.slice(0, k).join(".")}`;
|
|
5858
5879
|
at = items[index].i;
|
|
5859
5880
|
if (last) {
|
|
5860
5881
|
const value = region[at + 1];
|
|
@@ -5902,10 +5923,10 @@ function writeScalar(value, style) {
|
|
|
5902
5923
|
if (style === "plain" && /^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value)) return value;
|
|
5903
5924
|
return `'${value.replace(/'/g, "''")}'`;
|
|
5904
5925
|
}
|
|
5905
|
-
function setAt(document,
|
|
5926
|
+
function setAt(document, path50, value) {
|
|
5906
5927
|
let node = document;
|
|
5907
|
-
for (const segment of
|
|
5908
|
-
node[
|
|
5928
|
+
for (const segment of path50.slice(0, -1)) node = node[segment];
|
|
5929
|
+
node[path50[path50.length - 1]] = value;
|
|
5909
5930
|
}
|
|
5910
5931
|
function rewriteScalarsInText(text3, edits, root) {
|
|
5911
5932
|
const refuse4 = (why2) => {
|
|
@@ -6396,9 +6417,9 @@ function registryOver(store, root) {
|
|
|
6396
6417
|
const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
|
|
6397
6418
|
const base = ["rules", "conformance", "carried", String(g), "findings", String(f)];
|
|
6398
6419
|
const named2 = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
|
|
6399
|
-
const change = (
|
|
6420
|
+
const change = (path50, field, from, to) => {
|
|
6400
6421
|
if (from === to) return;
|
|
6401
|
-
edits.push({ path: [...base, ...
|
|
6422
|
+
edits.push({ path: [...base, ...path50], from, to });
|
|
6402
6423
|
rekeys.push({ ...named2, field, from, to });
|
|
6403
6424
|
};
|
|
6404
6425
|
change(["spec"], "spec", entry.spec, next.spec);
|
|
@@ -7472,7 +7493,12 @@ var MethodParamSchema = import_zod5.z.object({
|
|
|
7472
7493
|
*/
|
|
7473
7494
|
type: import_zod5.z.string(),
|
|
7474
7495
|
description: import_zod5.z.string().optional(),
|
|
7475
|
-
optional: import_zod5.z.boolean().optional()
|
|
7496
|
+
optional: import_zod5.z.boolean().optional(),
|
|
7497
|
+
/**
|
|
7498
|
+
* The parameter's rename trace: every name it held before, oldest first —
|
|
7499
|
+
* written by the rename-param tool, shown as `formerly` in the design export.
|
|
7500
|
+
*/
|
|
7501
|
+
previousNames: import_zod5.z.array(import_zod5.z.string().min(1)).optional()
|
|
7476
7502
|
});
|
|
7477
7503
|
var FindingDeclarationSchema = import_zod5.z.object({
|
|
7478
7504
|
/** UPPER_SNAKE and unique within the method; a pack's codes carry the pack prefix (<PACK>_<CODE>). */
|
|
@@ -8301,6 +8327,32 @@ function componentDesignView(component) {
|
|
|
8301
8327
|
function subsystemDesignView(subsystem) {
|
|
8302
8328
|
return withoutFields(subsystem, outOfApproval("subsystem"));
|
|
8303
8329
|
}
|
|
8330
|
+
function withoutUnknownKeys(raw, parsed) {
|
|
8331
|
+
if (Array.isArray(raw)) {
|
|
8332
|
+
if (!Array.isArray(parsed) || parsed.length !== raw.length) return raw;
|
|
8333
|
+
return raw.map((item, i) => withoutUnknownKeys(item, parsed[i]));
|
|
8334
|
+
}
|
|
8335
|
+
if (!raw || typeof raw !== "object" || !parsed || typeof parsed !== "object" || Array.isArray(parsed)) return raw;
|
|
8336
|
+
const out = {};
|
|
8337
|
+
for (const [key2, value] of Object.entries(raw)) {
|
|
8338
|
+
if (key2 in parsed) out[key2] = withoutUnknownKeys(value, parsed[key2]);
|
|
8339
|
+
}
|
|
8340
|
+
return out;
|
|
8341
|
+
}
|
|
8342
|
+
var SCHEMA_OF_KIND = {
|
|
8343
|
+
implementation: ImplementationSpecSchema,
|
|
8344
|
+
type: TypeSpecSchema,
|
|
8345
|
+
component: ComponentSpecSchema,
|
|
8346
|
+
subsystem: SubsystemSpecSchema,
|
|
8347
|
+
interface: InterfaceSpecSchema
|
|
8348
|
+
};
|
|
8349
|
+
function asSchemaReads(kind, doc) {
|
|
8350
|
+
const parsed = SCHEMA_OF_KIND[kind].safeParse(doc);
|
|
8351
|
+
if (!parsed.success) return doc;
|
|
8352
|
+
const kept = withoutUnknownKeys(doc, parsed.data);
|
|
8353
|
+
if (kind === "component" && "mounts" in doc) kept.mounts = doc.mounts;
|
|
8354
|
+
return kept;
|
|
8355
|
+
}
|
|
8304
8356
|
function interfaceDesignView(contract) {
|
|
8305
8357
|
return withoutFields(contract, outOfApproval("interface"));
|
|
8306
8358
|
}
|
|
@@ -8441,6 +8493,19 @@ var SurfaceTypeDefSchema = import_zod5.z.object({
|
|
|
8441
8493
|
values: import_zod5.z.array(EnumValueSchema).optional(),
|
|
8442
8494
|
/** A named scalar's one primitive, in place of fields; only on a value-object that declares it. */
|
|
8443
8495
|
holds: import_zod5.z.string().optional(),
|
|
8496
|
+
/**
|
|
8497
|
+
* The type's own methods (its checked constructors, its pure operations),
|
|
8498
|
+
* each with its signature, params and returns, so a binding written against
|
|
8499
|
+
* the pin knows them. Carried, never digested: a pin taken before they were
|
|
8500
|
+
* carried lacks them and reads as stale (re-pin), never as incompatible.
|
|
8501
|
+
*/
|
|
8502
|
+
methods: import_zod5.z.array(import_zod5.z.object({
|
|
8503
|
+
name: import_zod5.z.string(),
|
|
8504
|
+
signature: import_zod5.z.string(),
|
|
8505
|
+
params: import_zod5.z.array(import_zod5.z.object({ name: import_zod5.z.string(), type: import_zod5.z.string(), description: import_zod5.z.string().optional(), optional: import_zod5.z.boolean().optional() })).optional(),
|
|
8506
|
+
returns: import_zod5.z.string(),
|
|
8507
|
+
description: import_zod5.z.string().optional()
|
|
8508
|
+
})).optional(),
|
|
8444
8509
|
/**
|
|
8445
8510
|
* The type's rename trace as the producer recorded it (previousIds), so a
|
|
8446
8511
|
* consumer pinned to a former name reads a rename rather than a removal.
|
|
@@ -8875,6 +8940,29 @@ var TemplateSchema = import_zod8.z.object({
|
|
|
8875
8940
|
version: import_zod8.z.string().default("1.0.0")
|
|
8876
8941
|
});
|
|
8877
8942
|
|
|
8943
|
+
// src/models/technology-packages.ts
|
|
8944
|
+
var TECHNOLOGY_PACKAGES = [
|
|
8945
|
+
{ names: ["postgres", "postgresql", "pg"], packages: ["pg", "postgres", "pg-promise", "@neondatabase/serverless", "@vercel/postgres"] },
|
|
8946
|
+
{ names: ["mysql", "mariadb"], packages: ["mysql2", "mysql", "mariadb"] },
|
|
8947
|
+
{ names: ["redis", "valkey"], packages: ["redis", "ioredis", "@redis/client"] },
|
|
8948
|
+
{ names: ["mongodb", "mongo"], packages: ["mongodb", "mongoose"] },
|
|
8949
|
+
{ names: ["sqlite", "sqlite3"], packages: ["better-sqlite3", "sqlite3", "sqlite"] },
|
|
8950
|
+
{ names: ["kafka"], packages: ["kafkajs", "node-rdkafka"] },
|
|
8951
|
+
{ names: ["rabbitmq", "amqp"], packages: ["amqplib", "amqp-connection-manager"] }
|
|
8952
|
+
];
|
|
8953
|
+
var lookupKey = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
8954
|
+
function technologyPackages(tech, contributed = {}) {
|
|
8955
|
+
const key2 = lookupKey(technologyName(tech));
|
|
8956
|
+
const out = new Set(technologyTokens(tech).map((t) => t.toLowerCase()));
|
|
8957
|
+
for (const entry of TECHNOLOGY_PACKAGES) {
|
|
8958
|
+
if (entry.names.some((n) => lookupKey(n) === key2)) for (const p of entry.packages) out.add(p);
|
|
8959
|
+
}
|
|
8960
|
+
for (const [name, packages] of Object.entries(contributed)) {
|
|
8961
|
+
if (lookupKey(name) === key2) for (const p of packages) out.add(p.toLowerCase());
|
|
8962
|
+
}
|
|
8963
|
+
return [...out];
|
|
8964
|
+
}
|
|
8965
|
+
|
|
8878
8966
|
// src/models/type-grammar.ts
|
|
8879
8967
|
var PRIMITIVE_TYPES = ["string", "int", "float", "bool", "bytes", "date", "datetime", "duration", "void", "any"];
|
|
8880
8968
|
var PRIMITIVES = new Set(PRIMITIVE_TYPES);
|
|
@@ -9547,13 +9635,13 @@ var Canonicalizer = class {
|
|
|
9547
9635
|
this.problems = [];
|
|
9548
9636
|
}
|
|
9549
9637
|
/** One position: its canonical text when it has one, else the text as written, recorded either way. */
|
|
9550
|
-
read(text3, position,
|
|
9638
|
+
read(text3, position, path50, omittable2 = false) {
|
|
9551
9639
|
const parse = parseTypePosition(text3, position, omittable2);
|
|
9552
9640
|
if (parse.problem) {
|
|
9553
|
-
this.problems.push({ ...parse.problem, specId: this.specId, kind: this.kind, path:
|
|
9641
|
+
this.problems.push({ ...parse.problem, specId: this.specId, kind: this.kind, path: path50 });
|
|
9554
9642
|
return text3;
|
|
9555
9643
|
}
|
|
9556
|
-
if (parse.canonical !== text3) this.respellings.push({ specId: this.specId, kind: this.kind, path:
|
|
9644
|
+
if (parse.canonical !== text3) this.respellings.push({ specId: this.specId, kind: this.kind, path: path50, written: text3, stored: parse.canonical });
|
|
9557
9645
|
return parse.canonical;
|
|
9558
9646
|
}
|
|
9559
9647
|
params(params, position, prefix) {
|
|
@@ -9638,8 +9726,8 @@ function namesAnInteger(name) {
|
|
|
9638
9726
|
const words2 = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[\s_\-.]+/).filter(Boolean);
|
|
9639
9727
|
return words2.some((word) => INTEGER_WORDS.has(word));
|
|
9640
9728
|
}
|
|
9641
|
-
function positionNameOf(
|
|
9642
|
-
const segments =
|
|
9729
|
+
function positionNameOf(path50) {
|
|
9730
|
+
const segments = path50.split(".");
|
|
9643
9731
|
const last = segments[segments.length - 1];
|
|
9644
9732
|
return last === "returns" && segments.length >= 3 ? segments[segments.length - 2] : last;
|
|
9645
9733
|
}
|
|
@@ -10007,10 +10095,10 @@ function stepGraph(method) {
|
|
|
10007
10095
|
const s = byNum.get(n);
|
|
10008
10096
|
if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
|
|
10009
10097
|
const entries = s.branches.map((b) => b.step).sort((a, b) => a - b);
|
|
10010
|
-
const
|
|
10098
|
+
const join31 = fallNext(s.endStep);
|
|
10011
10099
|
for (let i = 0; i < entries.length; i++) {
|
|
10012
10100
|
const armEnd = i + 1 < entries.length ? prevOf(entries[i + 1]) : s.endStep;
|
|
10013
|
-
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd,
|
|
10101
|
+
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join31);
|
|
10014
10102
|
}
|
|
10015
10103
|
}
|
|
10016
10104
|
const successorsOf = (n) => {
|
|
@@ -10488,7 +10576,10 @@ function carriedFactChanges(pinned, live) {
|
|
|
10488
10576
|
const liveTypes = new Map(live.types.map((t) => [t.id, t]));
|
|
10489
10577
|
for (const def of pinned.types) {
|
|
10490
10578
|
const now = liveTypes.get(def.id);
|
|
10491
|
-
if (now)
|
|
10579
|
+
if (!now) continue;
|
|
10580
|
+
differs(`rename trace of ${def.id}`, def.formerly, now.formerly);
|
|
10581
|
+
const methods = (d) => (d.methods ?? []).map((m) => ({ name: m.name, params: (m.params ?? []).map((p) => ({ type: p.type, optional: p.optional === true })), returns: m.returns }));
|
|
10582
|
+
differs(`methods of ${def.id}`, def.methods?.length ? methods(def) : void 0, now.methods?.length ? methods(now) : void 0);
|
|
10492
10583
|
}
|
|
10493
10584
|
const unresolved = (snapshot) => [...snapshot.unresolvedExports ?? []].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
10494
10585
|
differs("the unresolved re-exports", unresolved(pinned), unresolved(live));
|
|
@@ -10518,6 +10609,77 @@ function memberDigest(snapshot, publicName, member) {
|
|
|
10518
10609
|
if (!method) return null;
|
|
10519
10610
|
return sha256(canonicalize({ method: methodShape(snapshot, method), closure: closureShapes(snapshot, methodTypeRefs(method)) }));
|
|
10520
10611
|
}
|
|
10612
|
+
function shownSignature(method) {
|
|
10613
|
+
return method.signature || `${method.name}(${(method.params ?? []).map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${method.returns}`;
|
|
10614
|
+
}
|
|
10615
|
+
function surfaceChanges(newer, older) {
|
|
10616
|
+
const out = [];
|
|
10617
|
+
const oldEntries = new Map(older.interfaces.map((e) => [e.id, e]));
|
|
10618
|
+
const oldTypes = new Map((older.exportedTypes ?? []).map((t) => [t.id, t]));
|
|
10619
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
10620
|
+
const formerOf = (formerly, has) => (formerly ?? []).find((f) => has(f) && !consumed.has(f));
|
|
10621
|
+
for (const entry of newer.interfaces) {
|
|
10622
|
+
let before = oldEntries.get(entry.id);
|
|
10623
|
+
if (!before) {
|
|
10624
|
+
const from = formerOf(entry.formerly, (n) => oldEntries.has(n) && !newer.interfaces.some((e) => e.id === n));
|
|
10625
|
+
if (from === void 0) {
|
|
10626
|
+
out.push({ kind: "added", name: entry.id, detail: `added: ${entry.type} contract with ${entry.methods.length} method(s), audience ${entry.audience}` });
|
|
10627
|
+
continue;
|
|
10628
|
+
}
|
|
10629
|
+
consumed.add(from);
|
|
10630
|
+
before = oldEntries.get(from);
|
|
10631
|
+
out.push({ kind: "renamed", name: entry.id, from, detail: `renamed from "${from}"` });
|
|
10632
|
+
}
|
|
10633
|
+
if (before.audience !== entry.audience) out.push({ kind: "changed", name: entry.id, detail: `audience ${before.audience} \u2192 ${entry.audience}` });
|
|
10634
|
+
const oldMethods = new Map(before.methods.map((m) => [m.name, m]));
|
|
10635
|
+
const usedOld = /* @__PURE__ */ new Set();
|
|
10636
|
+
for (const method of entry.methods) {
|
|
10637
|
+
let was = oldMethods.get(method.name);
|
|
10638
|
+
if (!was) {
|
|
10639
|
+
const from = (method.formerly ?? []).find((f) => oldMethods.has(f) && !entry.methods.some((m) => m.name === f));
|
|
10640
|
+
if (from === void 0) {
|
|
10641
|
+
out.push({ kind: "added", name: entry.id, member: method.name, detail: `method added: ${shownSignature(method)}` });
|
|
10642
|
+
continue;
|
|
10643
|
+
}
|
|
10644
|
+
was = oldMethods.get(from);
|
|
10645
|
+
out.push({ kind: "renamed", name: entry.id, member: method.name, from, detail: `method renamed from "${from}"` });
|
|
10646
|
+
}
|
|
10647
|
+
usedOld.add(was.name);
|
|
10648
|
+
const a = memberDigest(older, before.id, was.name);
|
|
10649
|
+
const b = memberDigest(newer, entry.id, method.name);
|
|
10650
|
+
const sameShape = a === b || memberDigest({ ...newer, interfaces: newer.interfaces.map((e) => e !== entry ? e : { ...e, id: before.id, methods: e.methods.map((m) => m === method ? { ...m, name: was.name } : m) }) }, before.id, was.name) === a;
|
|
10651
|
+
if (!sameShape) out.push({ kind: "changed", name: entry.id, member: method.name, detail: `signature ${shownSignature(was)} \u2192 ${shownSignature(method)}` });
|
|
10652
|
+
}
|
|
10653
|
+
for (const m of before.methods) {
|
|
10654
|
+
if (!usedOld.has(m.name)) out.push({ kind: "removed", name: entry.id, member: m.name, detail: `method removed: ${shownSignature(m)}` });
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10657
|
+
for (const t of newer.exportedTypes ?? []) {
|
|
10658
|
+
let before = oldTypes.get(t.id);
|
|
10659
|
+
if (!before) {
|
|
10660
|
+
const def = newer.types.find((d) => d.id === t.type);
|
|
10661
|
+
const from = formerOf(def?.formerly, (n) => oldTypes.has(n) && !(newer.exportedTypes ?? []).some((x) => x.id === n));
|
|
10662
|
+
if (from === void 0) {
|
|
10663
|
+
out.push({ kind: "added", name: t.id, detail: `type added (${def?.kind ?? "type"}), audience ${t.audience}` });
|
|
10664
|
+
continue;
|
|
10665
|
+
}
|
|
10666
|
+
consumed.add(from);
|
|
10667
|
+
before = oldTypes.get(from);
|
|
10668
|
+
out.push({ kind: "renamed", name: t.id, from, detail: `type renamed from "${from}"` });
|
|
10669
|
+
}
|
|
10670
|
+
if (before.audience !== t.audience) out.push({ kind: "changed", name: t.id, detail: `audience ${before.audience} \u2192 ${t.audience}` });
|
|
10671
|
+
const a = memberDigest(older, before.id, "type");
|
|
10672
|
+
const b = memberDigest(newer, t.id, "type");
|
|
10673
|
+
if (a !== b && before.id === t.id) out.push({ kind: "changed", name: t.id, detail: "type shape changed (fields, values, held primitive or a type it names)" });
|
|
10674
|
+
}
|
|
10675
|
+
for (const e of older.interfaces) {
|
|
10676
|
+
if (!newer.interfaces.some((x) => x.id === e.id) && !consumed.has(e.id)) out.push({ kind: "removed", name: e.id, detail: `removed: ${e.type} contract with ${e.methods.length} method(s)` });
|
|
10677
|
+
}
|
|
10678
|
+
for (const t of older.exportedTypes ?? []) {
|
|
10679
|
+
if (!(newer.exportedTypes ?? []).some((x) => x.id === t.id) && !consumed.has(t.id)) out.push({ kind: "removed", name: t.id, detail: "type removed" });
|
|
10680
|
+
}
|
|
10681
|
+
return out.sort((x, y) => x.name === y.name ? (x.member ?? "") < (y.member ?? "") ? -1 : (x.member ?? "") > (y.member ?? "") ? 1 : 0 : x.name < y.name ? -1 : 1);
|
|
10682
|
+
}
|
|
10521
10683
|
|
|
10522
10684
|
// src/models/exports.ts
|
|
10523
10685
|
var PUBLIC_NAME_RE = /^[a-z0-9_-]+$/;
|
|
@@ -10731,9 +10893,9 @@ function shortestPath(from, to, members, edges, rank) {
|
|
|
10731
10893
|
const v = queue.shift();
|
|
10732
10894
|
for (const w of [...edges.get(v) ?? []].filter((x) => members.has(x)).sort((a, b) => rank(a) - rank(b))) {
|
|
10733
10895
|
if (w === to) {
|
|
10734
|
-
const
|
|
10735
|
-
for (let at = v; at !== from; at = prev.get(at))
|
|
10736
|
-
return
|
|
10896
|
+
const path50 = [w];
|
|
10897
|
+
for (let at = v; at !== from; at = prev.get(at)) path50.unshift(at);
|
|
10898
|
+
return path50;
|
|
10737
10899
|
}
|
|
10738
10900
|
if (seen.has(w)) continue;
|
|
10739
10901
|
seen.add(w);
|
|
@@ -10774,7 +10936,9 @@ var DesignParamSchema = import_zod9.z.object({
|
|
|
10774
10936
|
name: import_zod9.z.string(),
|
|
10775
10937
|
type: DesignTypeRefSchema,
|
|
10776
10938
|
optional: import_zod9.z.boolean(),
|
|
10777
|
-
description: import_zod9.z.string().optional()
|
|
10939
|
+
description: import_zod9.z.string().optional(),
|
|
10940
|
+
/** The parameter's rename trace (its previousNames), oldest first; absent when never renamed. */
|
|
10941
|
+
formerly: import_zod9.z.array(import_zod9.z.string()).optional()
|
|
10778
10942
|
}).passthrough();
|
|
10779
10943
|
var DesignFieldSchema = import_zod9.z.object({
|
|
10780
10944
|
name: import_zod9.z.string(),
|
|
@@ -16220,7 +16384,12 @@ var externalsFileAdapter = {
|
|
|
16220
16384
|
return parsed.data;
|
|
16221
16385
|
},
|
|
16222
16386
|
writeLock(lock) {
|
|
16223
|
-
|
|
16387
|
+
const file = lockPath2(getProjectRoot());
|
|
16388
|
+
if (Object.keys(lock.externals).length === 0 && lock.parent === void 0) {
|
|
16389
|
+
if (fs8.existsSync(file)) fs8.unlinkSync(file);
|
|
16390
|
+
return;
|
|
16391
|
+
}
|
|
16392
|
+
writeAtomically(file, serializeYaml(sortedLock(lock)));
|
|
16224
16393
|
},
|
|
16225
16394
|
readSnapshot(alias) {
|
|
16226
16395
|
const file = snapshotPath(getProjectRoot(), alias);
|
|
@@ -16944,7 +17113,8 @@ function projectParams(params, position, keys, owner) {
|
|
|
16944
17113
|
name: p.name,
|
|
16945
17114
|
type: typeRef(p.type, position, keys, owner),
|
|
16946
17115
|
optional: p.optional === true,
|
|
16947
|
-
...p.description !== void 0 ? { description: p.description } : {}
|
|
17116
|
+
...p.description !== void 0 ? { description: p.description } : {},
|
|
17117
|
+
...p.previousNames && p.previousNames.length > 0 ? { formerly: [...p.previousNames] } : {}
|
|
16948
17118
|
}));
|
|
16949
17119
|
}
|
|
16950
17120
|
function exportEntry(e, keys) {
|
|
@@ -16993,7 +17163,8 @@ function memberDependency(member, lock, keys) {
|
|
|
16993
17163
|
projectId: member.projectId,
|
|
16994
17164
|
role: "member",
|
|
16995
17165
|
...digest3 !== void 0 ? { digest: digest3 } : {},
|
|
16996
|
-
|
|
17166
|
+
// Every public name a reference reaches (the graph's), and every one the projection wrote.
|
|
17167
|
+
uses: [.../* @__PURE__ */ new Set([...member.referenced ?? [], ...keys.used.get(member.alias) ?? []])].sort(compareOrdinal)
|
|
16997
17168
|
};
|
|
16998
17169
|
}
|
|
16999
17170
|
function directMembers(family) {
|
|
@@ -17014,7 +17185,8 @@ function directMembers(family) {
|
|
|
17014
17185
|
for (const key2 of bound2(target)) if (!names.has(key2)) names.set(key2, name);
|
|
17015
17186
|
}
|
|
17016
17187
|
}
|
|
17017
|
-
|
|
17188
|
+
const referenced = [...new Set(family.references.filter((r) => r.consumer === "" && r.producer === n.namespace && r.publicName !== void 0).map((r) => r.publicName))];
|
|
17189
|
+
return { alias, projectId: n.id ?? alias, names, referenced };
|
|
17018
17190
|
}).sort((a, b) => compareOrdinal(a.alias, b.alias));
|
|
17019
17191
|
}
|
|
17020
17192
|
function subsystemSection(s, table, keys) {
|
|
@@ -17324,6 +17496,8 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
17324
17496
|
...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
|
|
17325
17497
|
// A named scalar travels with the primitive it holds.
|
|
17326
17498
|
...t.holds !== void 0 ? { holds: t.holds } : {},
|
|
17499
|
+
// Its own methods (checked constructors, pure operations) travel with it — carried, never digested.
|
|
17500
|
+
...t.methods && t.methods.length ? { methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, ...m.params ? { params: m.params.map(describe2) } : {}, returns: m.returns, ...m.description ? { description: m.description } : {} })) } : {},
|
|
17327
17501
|
// A renamed type travels with its rename trace — provenance, outside every digest.
|
|
17328
17502
|
...formerlyOf(t.previousIds, t.id)
|
|
17329
17503
|
};
|
|
@@ -17438,7 +17612,8 @@ function canonicalReferences(snapshot) {
|
|
|
17438
17612
|
...def,
|
|
17439
17613
|
fields: def.fields.map((f) => ({ ...f, type: canon(f.type) })),
|
|
17440
17614
|
...def.params ? { params: def.params.map((p) => ({ ...p, type: canon(p.type) })) } : {},
|
|
17441
|
-
...def.returns !== void 0 ? { returns: canon(def.returns) } : {}
|
|
17615
|
+
...def.returns !== void 0 ? { returns: canon(def.returns) } : {},
|
|
17616
|
+
...def.methods ? { methods: def.methods.map((m) => ({ ...m, returns: canon(m.returns), ...m.params ? { params: m.params.map((p) => ({ ...p, type: canon(p.type) })) } : {} })) } : {}
|
|
17442
17617
|
}))
|
|
17443
17618
|
};
|
|
17444
17619
|
}
|
|
@@ -17608,9 +17783,17 @@ function exportResult(snapshot, renderedSet, writtenPaths) {
|
|
|
17608
17783
|
...writtenPaths.length ? { writtenPaths } : {}
|
|
17609
17784
|
};
|
|
17610
17785
|
}
|
|
17786
|
+
var OpenApiNotApplicableError = class extends Error {
|
|
17787
|
+
constructor(snapshot, maxAudience) {
|
|
17788
|
+
const kinds = [...new Set(snapshot.interfaces.map((e) => e.type))].sort();
|
|
17789
|
+
super(snapshot.interfaces.length ? `OpenAPI does not apply to "${snapshot.projectName}": it exposes no HTTP Portal at audience \u2265 ${maxAudience} \u2014 its ${snapshot.interfaces.length} exported interface(s) are ${kinds.join(", ")}${kinds.includes("InProcess") ? " (an in-process library is called, not requested over HTTP)" : ""}, which no OpenAPI document describes. Nothing was written; export the native snapshot (\`--format native\`) instead.` : `OpenAPI does not apply to "${snapshot.projectName}": it exports no interface at audience \u2265 ${maxAudience}. Nothing was written.`);
|
|
17790
|
+
this.name = "OpenApiNotApplicableError";
|
|
17791
|
+
}
|
|
17792
|
+
};
|
|
17611
17793
|
function exportSurface(maxAudience, format, outPath, portalId) {
|
|
17612
17794
|
const snapshot = projectOwnSurface(maxAudience);
|
|
17613
17795
|
let renderedSet = format === "openapi" ? toOpenApiSet(snapshot) : void 0;
|
|
17796
|
+
if (renderedSet && renderedSet.length === 0 && outPath) throw new OpenApiNotApplicableError(snapshot, maxAudience);
|
|
17614
17797
|
if (renderedSet && portalId) renderedSet = selectPortalSpec(renderedSet, portalId);
|
|
17615
17798
|
const writtenPaths = outPath ? writeSurfaceFile(outPath, snapshot, renderedSet) : [];
|
|
17616
17799
|
return exportResult(snapshot, renderedSet, writtenPaths);
|
|
@@ -17697,22 +17880,57 @@ function producerUnreadable(external) {
|
|
|
17697
17880
|
return null;
|
|
17698
17881
|
}
|
|
17699
17882
|
function projectProducer(external) {
|
|
17700
|
-
const snapshot = runWithProjectRoot(external.directory, () =>
|
|
17883
|
+
const snapshot = runWithProjectRoot(external.directory, () => {
|
|
17884
|
+
const projected = projectOwnSurface(external.audience);
|
|
17885
|
+
const broken = getLoaderIssues().find((i) => i.code === "SCHEMA_VALIDATION_ERROR");
|
|
17886
|
+
if (broken) throw new Error(unreadableReason(broken.message));
|
|
17887
|
+
return projected;
|
|
17888
|
+
});
|
|
17701
17889
|
return { ...snapshot, audience: external.audience };
|
|
17702
17890
|
}
|
|
17703
|
-
|
|
17891
|
+
var PRODUCER_UNREADABLE = "producer unreadable";
|
|
17892
|
+
function unreadableReason(message) {
|
|
17893
|
+
const parsed = /^Failed to parse (?:\w+ )?spec(?: "([^"]+)")?: ([\s\S]*)$/.exec(message);
|
|
17894
|
+
if (!parsed) return `${PRODUCER_UNREADABLE}: ${message}`;
|
|
17895
|
+
return `${PRODUCER_UNREADABLE}: ${parsed[1] ?? "its system spec"}: ${parsed[2]}`;
|
|
17896
|
+
}
|
|
17897
|
+
function readFailure(e) {
|
|
17898
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
17899
|
+
return detail.startsWith(PRODUCER_UNREADABLE) ? detail : `the producer could not be read: ${detail}`;
|
|
17900
|
+
}
|
|
17901
|
+
function usedDigests(usage, snapshot, alias) {
|
|
17704
17902
|
const used = {};
|
|
17705
17903
|
const missing = [];
|
|
17904
|
+
const uncarried = [];
|
|
17706
17905
|
for (const use of usage?.used ?? []) {
|
|
17707
17906
|
const members = {};
|
|
17708
17907
|
for (const member of use.members) {
|
|
17709
17908
|
const digest3 = memberDigest(snapshot, use.publicName, member);
|
|
17710
|
-
if (digest3
|
|
17711
|
-
|
|
17909
|
+
if (digest3 !== null) {
|
|
17910
|
+
members[member] = digest3;
|
|
17911
|
+
continue;
|
|
17912
|
+
}
|
|
17913
|
+
missing.push(`${use.publicName}.${member}`);
|
|
17914
|
+
uncarried.push({
|
|
17915
|
+
specId: use.specs?.[0] ?? "",
|
|
17916
|
+
position: "uncarried",
|
|
17917
|
+
target: `${alias}::${use.publicName}`,
|
|
17918
|
+
...member !== "type" ? { member } : {},
|
|
17919
|
+
consumer: usage?.consumer ?? "",
|
|
17920
|
+
producer: alias,
|
|
17921
|
+
authored: `${alias}::${use.publicName}`
|
|
17922
|
+
});
|
|
17712
17923
|
}
|
|
17713
17924
|
used[use.publicName] = members;
|
|
17714
17925
|
}
|
|
17715
|
-
return { used, missing };
|
|
17926
|
+
return { used, missing, uncarried };
|
|
17927
|
+
}
|
|
17928
|
+
function pinDetail(missing, usedNames, alias) {
|
|
17929
|
+
if (missing.length) return { detail: `used members the snapshot does not carry cannot be pinned: ${missing.join(", ")}` };
|
|
17930
|
+
if (usedNames === 0) {
|
|
17931
|
+
return { detail: `no spec references "${alias}" yet, so this pin records none of its uses \u2014 it fills on the next pin (\`wairon externals pin ${alias}\` / sdd_pin_externals) once a spec writes \`${alias}::<name>\`; until then each new reference reads "used now, but not in the lock"` };
|
|
17932
|
+
}
|
|
17933
|
+
return {};
|
|
17716
17934
|
}
|
|
17717
17935
|
function pinBinding(binding, lock) {
|
|
17718
17936
|
const { external } = binding;
|
|
@@ -17723,14 +17941,13 @@ function pinBinding(binding, lock) {
|
|
|
17723
17941
|
if (unreadable) throw new Error(unreadable);
|
|
17724
17942
|
snapshot = projectProducer(external);
|
|
17725
17943
|
} catch (e) {
|
|
17726
|
-
|
|
17727
|
-
return { pin: { alias: external.alias, outcome: "unreachable", project: external.project, usedNames: 0, unexported, detail: `the producer could not be read: ${detail}; its previous pin stays` }, changed: false };
|
|
17944
|
+
return { pin: { alias: external.alias, outcome: "unreachable", project: external.project, usedNames: 0, unexported, detail: `${readFailure(e)}; its previous pin stays` }, changed: false };
|
|
17728
17945
|
}
|
|
17729
17946
|
const digest3 = contentDigest(snapshot);
|
|
17730
17947
|
const pinned = externalsRepository.readSnapshot(external.alias);
|
|
17731
17948
|
const snapshotChanged = !pinned || contentDigest(pinned) !== digest3 || pinnedContentKey(pinned) !== pinnedContentKey(snapshot);
|
|
17732
17949
|
if (snapshotChanged) externalsRepository.saveSnapshot(external.alias, snapshot);
|
|
17733
|
-
const { used, missing } = usedDigests(binding.usage, snapshot);
|
|
17950
|
+
const { used, missing, uncarried } = usedDigests(binding.usage, snapshot, external.alias);
|
|
17734
17951
|
const entry = {
|
|
17735
17952
|
project: external.project,
|
|
17736
17953
|
snapshot: `.wai/externals/${external.alias}.yaml`,
|
|
@@ -17749,8 +17966,8 @@ function pinBinding(binding, lock) {
|
|
|
17749
17966
|
snapshot: entry.snapshot,
|
|
17750
17967
|
digest: digest3,
|
|
17751
17968
|
usedNames: Object.keys(used).length,
|
|
17752
|
-
unexported,
|
|
17753
|
-
...missing.
|
|
17969
|
+
unexported: [...unexported, ...uncarried],
|
|
17970
|
+
...pinDetail(missing, Object.keys(used).length, external.alias)
|
|
17754
17971
|
},
|
|
17755
17972
|
changed: entryChanged
|
|
17756
17973
|
};
|
|
@@ -17914,6 +18131,13 @@ function compareUses(entry, usage, live) {
|
|
|
17914
18131
|
}
|
|
17915
18132
|
}
|
|
17916
18133
|
for (const ref of usage?.unexported ?? []) {
|
|
18134
|
+
const spelled = ref.authored.split("::");
|
|
18135
|
+
const name = spelled.length === 2 ? spelled[1] : spelled.length === 1 ? spelled[0] : void 0;
|
|
18136
|
+
if (name !== void 0 && !liveHas(name) && !uses.some((u) => u.publicName === name && (u.member === ref.member || u.member === void 0))) {
|
|
18137
|
+
const gone = goneUse(live, name, ref.member, void 0);
|
|
18138
|
+
uses.push(gone);
|
|
18139
|
+
continue;
|
|
18140
|
+
}
|
|
17917
18141
|
uses.push({ member: ref.member, state: "unavailable", code: CHECK_UNAVAILABLE, detail: `"${ref.specId}" reaches "${ref.target}", which the producer does not export \u2014 nothing to compare` });
|
|
17918
18142
|
}
|
|
17919
18143
|
return uses;
|
|
@@ -17946,7 +18170,7 @@ function externalStatus(binding, lock) {
|
|
|
17946
18170
|
try {
|
|
17947
18171
|
live = projectProducer(external);
|
|
17948
18172
|
} catch (e) {
|
|
17949
|
-
reason =
|
|
18173
|
+
reason = readFailure(e);
|
|
17950
18174
|
}
|
|
17951
18175
|
}
|
|
17952
18176
|
if (!live) {
|
|
@@ -18011,6 +18235,23 @@ function listExternals() {
|
|
|
18011
18235
|
}));
|
|
18012
18236
|
return [...declared, ...orphans];
|
|
18013
18237
|
}
|
|
18238
|
+
function listConsumers2(search) {
|
|
18239
|
+
return listConsumers(search);
|
|
18240
|
+
}
|
|
18241
|
+
function diff(against) {
|
|
18242
|
+
const current2 = projectOwnSurface("project");
|
|
18243
|
+
let baseline;
|
|
18244
|
+
let label7;
|
|
18245
|
+
if (against !== void 0 && fs9.existsSync(against) && fs9.statSync(against).isFile()) {
|
|
18246
|
+
baseline = SurfaceSnapshotSchema.parse(parseYaml(fs9.readFileSync(against, "utf8"), against));
|
|
18247
|
+
label7 = `the saved surface ${path14.resolve(against)}`;
|
|
18248
|
+
} else {
|
|
18249
|
+
const revision = approvedRevision(against);
|
|
18250
|
+
baseline = runWithProjectRoot(revision.directory, () => projectOwnSurface("project"));
|
|
18251
|
+
label7 = revision.label;
|
|
18252
|
+
}
|
|
18253
|
+
return { project: current2.projectId ?? current2.projectName, against: label7, changes: surfaceChanges(current2, baseline) };
|
|
18254
|
+
}
|
|
18014
18255
|
function pinnedContentKey(snapshot) {
|
|
18015
18256
|
const { stateId: _s, generatedAt: _g, origin: _o, ...content } = SurfaceSnapshotSchema.parse(JSON.parse(JSON.stringify(snapshot)));
|
|
18016
18257
|
return canonicalize({ ...content, interfaces: content.interfaces.map((e) => ({ ...e, component: lastSegment2(e.component) })) });
|
|
@@ -18188,8 +18429,8 @@ function remove(alias, dryRun) {
|
|
|
18188
18429
|
}
|
|
18189
18430
|
if (dryRun) return { alias, removed: false, unpinned: pinned };
|
|
18190
18431
|
const removed = declared ? removeExternal(alias) : false;
|
|
18191
|
-
const
|
|
18192
|
-
return { alias, removed, unpinned };
|
|
18432
|
+
const unpinned2 = unpin(alias);
|
|
18433
|
+
return { alias, removed, unpinned: unpinned2 };
|
|
18193
18434
|
}
|
|
18194
18435
|
function currentUse(declaration2) {
|
|
18195
18436
|
const use = typeof declaration2 === "object" && declaration2 !== null ? declaration2.use : void 0;
|
|
@@ -18287,6 +18528,12 @@ function removeExternal2(alias, dryRun) {
|
|
|
18287
18528
|
function updateExternalUse(request) {
|
|
18288
18529
|
return updateUse(request);
|
|
18289
18530
|
}
|
|
18531
|
+
function listConsumers3(search) {
|
|
18532
|
+
return listConsumers2(search);
|
|
18533
|
+
}
|
|
18534
|
+
function diffSurface(against) {
|
|
18535
|
+
return diff(against);
|
|
18536
|
+
}
|
|
18290
18537
|
|
|
18291
18538
|
// src/core/family-validation.ts
|
|
18292
18539
|
var fs11 = __toESM(require("fs"));
|
|
@@ -18394,12 +18641,12 @@ function findingChanges(before, after) {
|
|
|
18394
18641
|
}
|
|
18395
18642
|
|
|
18396
18643
|
// src/core/rules/reach-model-projector.ts
|
|
18397
|
-
function underBase(basePath,
|
|
18644
|
+
function underBase(basePath, path50) {
|
|
18398
18645
|
const base = (basePath ?? "").trim().replace(/\/+$/, "");
|
|
18399
|
-
if (base === "" || base === "/") return
|
|
18646
|
+
if (base === "" || base === "/") return path50;
|
|
18400
18647
|
const head2 = base.startsWith("/") ? base : `/${base}`;
|
|
18401
|
-
if (
|
|
18402
|
-
return `${head2}${
|
|
18648
|
+
if (path50 === head2 || path50.startsWith(`${head2}/`)) return path50;
|
|
18649
|
+
return `${head2}${path50.startsWith("/") ? "" : "/"}${path50}`;
|
|
18403
18650
|
}
|
|
18404
18651
|
function bindingLine(endpoint, basePath) {
|
|
18405
18652
|
if (!endpoint) return void 0;
|
|
@@ -18521,6 +18768,7 @@ function project(ctx, network) {
|
|
|
18521
18768
|
...c.subsystem !== void 0 ? { subsystem: c.subsystem } : {},
|
|
18522
18769
|
componentType: c.componentType
|
|
18523
18770
|
}));
|
|
18771
|
+
const subsystems = ctx.subsystems.map((s) => s.id).filter((id) => !ctx.isInChainedSubproject(id)).sort();
|
|
18524
18772
|
const topics = [];
|
|
18525
18773
|
const addEnd = (component, topic, emits) => {
|
|
18526
18774
|
if (!topics.some((t) => t.component === component && t.topic === topic && t.emits === emits)) topics.push({ component, topic, emits });
|
|
@@ -18540,7 +18788,8 @@ function project(ctx, network) {
|
|
|
18540
18788
|
verbs: verbs.sort(byVerb),
|
|
18541
18789
|
calls: calls.sort(byCall),
|
|
18542
18790
|
placements: placements.sort(byPlacement),
|
|
18543
|
-
topics: topics.sort(byTopic)
|
|
18791
|
+
topics: topics.sort(byTopic),
|
|
18792
|
+
subsystems
|
|
18544
18793
|
};
|
|
18545
18794
|
}
|
|
18546
18795
|
function qualify(projectKey, local) {
|
|
@@ -18577,7 +18826,9 @@ function compose(models, family, networks) {
|
|
|
18577
18826
|
const calls = [];
|
|
18578
18827
|
const placements = [];
|
|
18579
18828
|
const topics = [];
|
|
18829
|
+
const subsystems = [];
|
|
18580
18830
|
for (const [key2, model] of models) {
|
|
18831
|
+
for (const s of model.subsystems ?? []) subsystems.push(qualify(key2, s));
|
|
18581
18832
|
for (const p of model.placements ?? []) {
|
|
18582
18833
|
placements.push({
|
|
18583
18834
|
...p,
|
|
@@ -18639,7 +18890,8 @@ function compose(models, family, networks) {
|
|
|
18639
18890
|
verbs: verbs.sort(byVerb),
|
|
18640
18891
|
calls: calls.sort(byCall),
|
|
18641
18892
|
placements: placements.sort(byPlacement),
|
|
18642
|
-
topics: topics.sort(byTopic)
|
|
18893
|
+
topics: topics.sort(byTopic),
|
|
18894
|
+
subsystems: subsystems.sort()
|
|
18643
18895
|
};
|
|
18644
18896
|
}
|
|
18645
18897
|
|
|
@@ -18668,6 +18920,7 @@ function judge(model) {
|
|
|
18668
18920
|
out.push({ severity, code, message, specId: v.portal, at: v.verb });
|
|
18669
18921
|
};
|
|
18670
18922
|
const family = model.scope === "family";
|
|
18923
|
+
const bypassed = /* @__PURE__ */ new Map();
|
|
18671
18924
|
const byPortal = /* @__PURE__ */ new Map();
|
|
18672
18925
|
for (const v of model.verbs) byPortal.set(v.portal, [...byPortal.get(v.portal) ?? [], v]);
|
|
18673
18926
|
for (const v of model.verbs) {
|
|
@@ -18676,12 +18929,7 @@ function judge(model) {
|
|
|
18676
18929
|
if (entry) {
|
|
18677
18930
|
const scope = entry.scope ?? "outside";
|
|
18678
18931
|
if (scope === "outside" && v.network !== void 0 && !v.gateway) {
|
|
18679
|
-
|
|
18680
|
-
"error",
|
|
18681
|
-
"GATEWAY_BYPASSED",
|
|
18682
|
-
v,
|
|
18683
|
-
`Portal verb "${verbName(v)}" sits inside ${networkName(v.network)} and is entered from outside it, but "${v.portal}" is not a gateway: callers from outside a network reach it only through its gateway. Make the Portal a gateway (variant: gateway), or route the outside callers through the network's gateway and narrow this entry to scope network.`
|
|
18684
|
-
);
|
|
18932
|
+
bypassed.set(v.portal, [...bypassed.get(v.portal) ?? [], v]);
|
|
18685
18933
|
}
|
|
18686
18934
|
if (scope === "network" && v.network === void 0 && family) {
|
|
18687
18935
|
verbFinding(
|
|
@@ -18735,9 +18983,27 @@ function judge(model) {
|
|
|
18735
18983
|
at: call.evidence
|
|
18736
18984
|
});
|
|
18737
18985
|
}
|
|
18986
|
+
for (const [portal, verbs] of bypassed) {
|
|
18987
|
+
const network = verbs[0].network;
|
|
18988
|
+
const gatewayless = (model.networks.find((n) => n.id === network)?.gateways.length ?? 0) === 0;
|
|
18989
|
+
const names = verbs.map((v) => `"${v.verb}"`).join(", ");
|
|
18990
|
+
const subject = verbs.length === 1 ? `Portal verb "${verbName(verbs[0])}"` : `Portal "${portal}" (${verbs.length} verbs: ${names})`;
|
|
18991
|
+
const way = gatewayless ? ` ${networkName(network).replace(/^t/, "T")} has no gateway yet: in a project that runs as one process, the Portal its callers enter IS the gateway \u2014 mark "${portal}" variant: gateway. Otherwise route the outside callers through the network's gateway and narrow ${verbs.length === 1 ? "this entry" : "these entries"} to scope network.` : ` Make the Portal a gateway (variant: gateway), or route the outside callers through the network's gateway and narrow ${verbs.length === 1 ? "this entry" : "these entries"} to scope network.`;
|
|
18992
|
+
out.push({
|
|
18993
|
+
severity: "error",
|
|
18994
|
+
code: "GATEWAY_BYPASSED",
|
|
18995
|
+
message: `${subject} sits inside ${networkName(network)} and is entered from outside it, but "${portal}" is not a gateway: callers from outside a network reach it only through its gateway.${way}`,
|
|
18996
|
+
specId: portal,
|
|
18997
|
+
...verbs.length === 1 ? { at: verbs[0].verb } : { covers: verbs.map((v) => v.verb) }
|
|
18998
|
+
});
|
|
18999
|
+
}
|
|
19000
|
+
const localName3 = (key2) => key2.includes("::") ? key2.slice(key2.lastIndexOf("::") + 2) : key2;
|
|
19001
|
+
const byLocal = (a, b) => localName3(a).localeCompare(localName3(b)) || a.localeCompare(b);
|
|
18738
19002
|
for (const n of model.networks) {
|
|
18739
19003
|
if (n.gateways.length < 2) continue;
|
|
18740
|
-
const
|
|
19004
|
+
const ownProject = (g) => model.verbs.some((v) => v.portal === g && v.project === n.id);
|
|
19005
|
+
const own2 = n.gateways.filter(ownProject);
|
|
19006
|
+
const first = [...own2.length > 0 ? own2 : n.gateways].sort(byLocal)[0];
|
|
18741
19007
|
out.push({
|
|
18742
19008
|
severity: "notice",
|
|
18743
19009
|
code: "MULTIPLE_GATEWAYS",
|
|
@@ -18753,6 +19019,8 @@ var FAMILY_SEVERITY = {
|
|
|
18753
19019
|
EXTERNAL_INCOMPATIBLE: "error",
|
|
18754
19020
|
EXTERNAL_DRIFTED: "notice",
|
|
18755
19021
|
EXTERNAL_CHECK_UNAVAILABLE: "warning",
|
|
19022
|
+
// A used external never pinned: nothing records what it is judged against, so the composed gate fails.
|
|
19023
|
+
EXTERNAL_UNPINNED: "error",
|
|
18756
19024
|
// The advisory live comparison (advise): printed and counted, never part of a failure decision.
|
|
18757
19025
|
EXTERNAL_LIVE_INCOMPATIBLE: "warning",
|
|
18758
19026
|
EXTERNAL_LIVE_UNCOMPARED: "notice",
|
|
@@ -18763,6 +19031,8 @@ var FAMILY_SEVERITY = {
|
|
|
18763
19031
|
MEMBER_DRIFTED: "warning",
|
|
18764
19032
|
PROJECT_ID_DEFAULTED: "notice",
|
|
18765
19033
|
PROJECT_ID_AMBIGUOUS: "warning",
|
|
19034
|
+
// A family-only re-export from another project that no project of the family reaches: dead.
|
|
19035
|
+
EXPORT_UNREACHED: "notice",
|
|
18766
19036
|
// Governance: a required pack not adopted blocks; a member's deviation from
|
|
18767
19037
|
// an adopted pack's settings is visible and never fails --ci.
|
|
18768
19038
|
POLICY_NOT_ADOPTED: "error",
|
|
@@ -18986,7 +19256,16 @@ function judgeExternal(status, project3, config) {
|
|
|
18986
19256
|
project3
|
|
18987
19257
|
));
|
|
18988
19258
|
}
|
|
18989
|
-
|
|
19259
|
+
const neverPinned = status.pinnedDigest === void 0 ? status.uses.filter((u) => u.state === "unlocked") : [];
|
|
19260
|
+
if (neverPinned.length > 0) {
|
|
19261
|
+
out.push(finding(
|
|
19262
|
+
config,
|
|
19263
|
+
"EXTERNAL_UNPINNED",
|
|
19264
|
+
`${named(project3)} uses ${neverPinned.map(useName).join(", ")} of ${who}, which was never pinned: nothing records what its design is judged against, so a producer change that breaks it cannot be told. Pin it (\`wairon externals pin ${status.alias}\`) \u2014 and \`wairon lock\` refuses until it is.`,
|
|
19265
|
+
project3
|
|
19266
|
+
));
|
|
19267
|
+
}
|
|
19268
|
+
for (const use of status.uses.filter((u) => u.state === "unavailable" || u.state === "unlocked" && !neverPinned.includes(u))) {
|
|
18990
19269
|
const why2 = use.detail ?? (use.state === "unlocked" ? "used now, but not in the lock" : "nothing to compare");
|
|
18991
19270
|
out.push(finding(
|
|
18992
19271
|
config,
|
|
@@ -19184,11 +19463,54 @@ function checkMembers(family) {
|
|
|
19184
19463
|
ref.parent
|
|
19185
19464
|
));
|
|
19186
19465
|
}
|
|
19466
|
+
out.push(...unreachedReexports(family, config));
|
|
19187
19467
|
for (const entry of projectApprovals()) {
|
|
19188
19468
|
if (present.has(entry.key) && entry.as !== "part") out.push(approvalFinding(entry, config));
|
|
19189
19469
|
}
|
|
19190
19470
|
return out.filter((f) => f !== null);
|
|
19191
19471
|
}
|
|
19472
|
+
function aliasTargets(node) {
|
|
19473
|
+
const out = new Map(node.aliases);
|
|
19474
|
+
for (const e of node.externals) if (e.sourceKind === "family" && e.producer !== void 0 && !out.has(e.alias)) out.set(e.alias, e.producer);
|
|
19475
|
+
return out;
|
|
19476
|
+
}
|
|
19477
|
+
function unreachedReexports(family, config) {
|
|
19478
|
+
const out = [];
|
|
19479
|
+
const keys = new Set(family.nodes.map((n) => n.namespace));
|
|
19480
|
+
const reached = /* @__PURE__ */ new Map();
|
|
19481
|
+
for (const ref of family.authoredReferences) {
|
|
19482
|
+
const owner = family.nodes.find((n) => n.namespace === (family.owners.get(ref.specId) ?? ""));
|
|
19483
|
+
if (!owner) continue;
|
|
19484
|
+
const targets = aliasTargets(owner);
|
|
19485
|
+
const via = ref.form === "import" ? (ref.importedVia ?? "").split(", ") : [ref.authored.split("::")[0]];
|
|
19486
|
+
const name = ref.form === "import" ? ref.authored : ref.authored.split("::")[1];
|
|
19487
|
+
for (const alias of via) {
|
|
19488
|
+
const project3 = targets.get(alias);
|
|
19489
|
+
if (project3 === void 0 || project3 === owner.namespace || name === void 0) continue;
|
|
19490
|
+
reached.set(project3, (reached.get(project3) ?? /* @__PURE__ */ new Set()).add(name));
|
|
19491
|
+
}
|
|
19492
|
+
}
|
|
19493
|
+
for (const node of family.nodes) {
|
|
19494
|
+
let entries;
|
|
19495
|
+
try {
|
|
19496
|
+
entries = resolveProjectExports(node.namespace || void 0).entries;
|
|
19497
|
+
} catch {
|
|
19498
|
+
continue;
|
|
19499
|
+
}
|
|
19500
|
+
for (const entry of entries) {
|
|
19501
|
+
const from = entry.via[0];
|
|
19502
|
+
if ((entry.audience ?? "instance") !== "project" || from === void 0 || from === node.namespace || !keys.has(from)) continue;
|
|
19503
|
+
if (reached.get(node.namespace)?.has(entry.publicName)) continue;
|
|
19504
|
+
out.push(finding(
|
|
19505
|
+
config,
|
|
19506
|
+
"EXPORT_UNREACHED",
|
|
19507
|
+
`${named(node.namespace)} re-exports "${entry.publicName}" from ${named(from)} at audience project \u2014 the family only \u2014 but no project of the family reaches it through ${named(node.namespace)}: a dead re-export. Drop the entry from its L0 export table (or widen its audience if a consumer outside the family uses it).`,
|
|
19508
|
+
node.namespace
|
|
19509
|
+
));
|
|
19510
|
+
}
|
|
19511
|
+
}
|
|
19512
|
+
return out;
|
|
19513
|
+
}
|
|
19192
19514
|
function subjectOf(record) {
|
|
19193
19515
|
return `${record.stateId.algorithm}:${record.stateId.digest}`;
|
|
19194
19516
|
}
|
|
@@ -19569,6 +19891,10 @@ function checkReach(family, models) {
|
|
|
19569
19891
|
return out;
|
|
19570
19892
|
}
|
|
19571
19893
|
function reachModel(options) {
|
|
19894
|
+
if (options.memberDepth !== 0) {
|
|
19895
|
+
const enclosing2 = enclosingFamilyModel(options);
|
|
19896
|
+
if (enclosing2) return enclosing2;
|
|
19897
|
+
}
|
|
19572
19898
|
const family = graph();
|
|
19573
19899
|
const root = path15.resolve(getProjectRoot());
|
|
19574
19900
|
const ceiling = reachCeiling(root, options.family);
|
|
@@ -19580,7 +19906,44 @@ function reachModel(options) {
|
|
|
19580
19906
|
const scoped = narrowed(family, selected);
|
|
19581
19907
|
return within2(root, ceiling, () => {
|
|
19582
19908
|
const model = compose(models, scoped, networksOf(scoped));
|
|
19583
|
-
|
|
19909
|
+
const config = configOrNull();
|
|
19910
|
+
const externals = Object.keys(config?.externals ?? {});
|
|
19911
|
+
return {
|
|
19912
|
+
...model,
|
|
19913
|
+
findings: judgedFindings(model, scoped),
|
|
19914
|
+
...config?.id !== void 0 ? { rootProject: config.id } : {},
|
|
19915
|
+
...externals.length > 0 ? { externals } : {},
|
|
19916
|
+
...formerNamesOf(scoped)
|
|
19917
|
+
};
|
|
19918
|
+
});
|
|
19919
|
+
}
|
|
19920
|
+
function formerNamesOf(family) {
|
|
19921
|
+
const out = {};
|
|
19922
|
+
for (const node of family.nodes) {
|
|
19923
|
+
if (node.namespace === "") continue;
|
|
19924
|
+
const previous = runWithProjectRoot(node.directory, configOrNull)?.previousIds ?? [];
|
|
19925
|
+
for (const id of previous) {
|
|
19926
|
+
const former = keyIn(node.parent ?? "", id);
|
|
19927
|
+
if (former !== node.namespace && !family.nodes.some((n) => n.namespace === former)) out[former] = node.namespace;
|
|
19928
|
+
}
|
|
19929
|
+
}
|
|
19930
|
+
return Object.keys(out).length > 0 ? { formerNames: out } : {};
|
|
19931
|
+
}
|
|
19932
|
+
function enclosingFamilyModel(options) {
|
|
19933
|
+
const bound2 = path15.resolve(getProjectRoot());
|
|
19934
|
+
let top = null;
|
|
19935
|
+
let parent = resolveChainingParent();
|
|
19936
|
+
for (let hops = 0; parent && hops < 32; hops++) {
|
|
19937
|
+
top = path15.resolve(parent.parentRoot);
|
|
19938
|
+
parent = runWithProjectRoot(top, resolveChainingParent);
|
|
19939
|
+
}
|
|
19940
|
+
if (top === null) return null;
|
|
19941
|
+
const topRoot = top;
|
|
19942
|
+
return runWithProjectRoot(topRoot, () => {
|
|
19943
|
+
const member = graph().nodes.find((n) => path15.resolve(n.directory) === bound2);
|
|
19944
|
+
if (!member || member.namespace === "") return null;
|
|
19945
|
+
const model = reachModel({ ...options, memberDepth: void 0 });
|
|
19946
|
+
return { ...model, focus: member.namespace, judgedAt: topRoot };
|
|
19584
19947
|
});
|
|
19585
19948
|
}
|
|
19586
19949
|
function judgedFindings(model, family) {
|
|
@@ -19603,7 +19966,8 @@ function judgedFindings(model, family) {
|
|
|
19603
19966
|
code: f.code,
|
|
19604
19967
|
message: f.message,
|
|
19605
19968
|
...f.specId !== void 0 ? { specId: f.specId } : {},
|
|
19606
|
-
...f.at !== void 0 ? { at: f.at } : {}
|
|
19969
|
+
...f.at !== void 0 ? { at: f.at } : {},
|
|
19970
|
+
...f.covers !== void 0 ? { covers: f.covers } : {}
|
|
19607
19971
|
});
|
|
19608
19972
|
}
|
|
19609
19973
|
return out;
|
|
@@ -19929,6 +20293,13 @@ var DeclarativePackSchema = import_zod10.z.object({
|
|
|
19929
20293
|
* builtin + declared are flagged UNKNOWN_GUARANTEE by the validator.
|
|
19930
20294
|
*/
|
|
19931
20295
|
guarantees: import_zod10.z.array(import_zod10.z.string().min(1)).default([]),
|
|
20296
|
+
/**
|
|
20297
|
+
* Package names a technology's code is imported by, keyed by the
|
|
20298
|
+
* technology's name — extending the built-in table the code twin of the
|
|
20299
|
+
* leakage rule (TECH_LEAKAGE_IN_CODE) compares imports with, for a driver or
|
|
20300
|
+
* SDK the table does not know. Data only.
|
|
20301
|
+
*/
|
|
20302
|
+
technologyPackages: import_zod10.z.record(import_zod10.z.array(import_zod10.z.string().min(1))).default({}),
|
|
19932
20303
|
/**
|
|
19933
20304
|
* Connecting-agent guidance appended to wairon's own MCP `initialize`
|
|
19934
20305
|
* instructions, attributed to this pack, in pack load order.
|
|
@@ -19963,7 +20334,7 @@ function globalPacksEnabled(config) {
|
|
|
19963
20334
|
return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
|
|
19964
20335
|
}
|
|
19965
20336
|
function emptyExtensions() {
|
|
19966
|
-
return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [], deprecations: [] };
|
|
20337
|
+
return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], technologyPackages: {}, assertions: [], instructions: [], errors: [], selectionFailures: [], deprecations: [] };
|
|
19967
20338
|
}
|
|
19968
20339
|
var FOREIGN_BUILTINS_RETIRED = "nothing replaces it: contracts are written in the neutral type grammar and a language is chosen at L4, so the foreign-builtin check it fed is retired";
|
|
19969
20340
|
function globalPacksDir() {
|
|
@@ -20035,6 +20406,10 @@ function mergePack(out, pack, ref, scope, packDir) {
|
|
|
20035
20406
|
for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path17.resolve(packDir, s.source) });
|
|
20036
20407
|
for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
|
|
20037
20408
|
out.guarantees = [.../* @__PURE__ */ new Set([...out.guarantees, ...pack.guarantees])];
|
|
20409
|
+
for (const [tech, packages] of Object.entries(pack.technologyPackages ?? {})) {
|
|
20410
|
+
const key2 = tech.toLowerCase();
|
|
20411
|
+
out.technologyPackages[key2] = [.../* @__PURE__ */ new Set([...out.technologyPackages[key2] ?? [], ...packages.map((p) => p.toLowerCase())])];
|
|
20412
|
+
}
|
|
20038
20413
|
for (const a of pack.assertions) out.assertions.push({ ...a, pack: pack.name, fullCode: assertionFullCode(pack.name, a.code) });
|
|
20039
20414
|
for (const b of pack.instructions) out.instructions.push({ ...b, pack: pack.name });
|
|
20040
20415
|
}
|
|
@@ -20067,6 +20442,7 @@ function readManifest(target, resolveFrom) {
|
|
|
20067
20442
|
skills: mod.skills ?? [],
|
|
20068
20443
|
patterns: mod.patterns ?? [],
|
|
20069
20444
|
guarantees: mod.guarantees ?? [],
|
|
20445
|
+
technologyPackages: mod.technologyPackages ?? {},
|
|
20070
20446
|
assertions: mod.assertions ?? [],
|
|
20071
20447
|
instructions: mod.instructions ?? [],
|
|
20072
20448
|
applyByDefault: mod.applyByDefault ?? false
|
|
@@ -20377,21 +20753,21 @@ function buildCodeIndex(model) {
|
|
|
20377
20753
|
const declarations = /* @__PURE__ */ new Map();
|
|
20378
20754
|
const anchors = /* @__PURE__ */ new Map();
|
|
20379
20755
|
const findingAnchors = /* @__PURE__ */ new Map();
|
|
20380
|
-
const namesAt = (cache,
|
|
20381
|
-
const cached = cache.get(
|
|
20756
|
+
const namesAt = (cache, path50, make) => {
|
|
20757
|
+
const cached = cache.get(path50);
|
|
20382
20758
|
if (cached) return cached;
|
|
20383
|
-
const f = facts.get(
|
|
20759
|
+
const f = facts.get(path50);
|
|
20384
20760
|
if (!f) return NO_NAMES;
|
|
20385
20761
|
const built = make(f);
|
|
20386
|
-
cache.set(
|
|
20762
|
+
cache.set(path50, built);
|
|
20387
20763
|
return built;
|
|
20388
20764
|
};
|
|
20389
20765
|
const republished = /* @__PURE__ */ new Map();
|
|
20390
|
-
const republishedFrom = (
|
|
20391
|
-
const cached = republished.get(
|
|
20766
|
+
const republishedFrom = (path50) => {
|
|
20767
|
+
const cached = republished.get(path50);
|
|
20392
20768
|
if (cached) return cached;
|
|
20393
|
-
const out = /* @__PURE__ */ new Set([
|
|
20394
|
-
const queue = [
|
|
20769
|
+
const out = /* @__PURE__ */ new Set([path50]);
|
|
20770
|
+
const queue = [path50];
|
|
20395
20771
|
while (queue.length) {
|
|
20396
20772
|
const from = queue.pop();
|
|
20397
20773
|
const f = facts.get(from);
|
|
@@ -20404,7 +20780,7 @@ function buildCodeIndex(model) {
|
|
|
20404
20780
|
}
|
|
20405
20781
|
}
|
|
20406
20782
|
}
|
|
20407
|
-
republished.set(
|
|
20783
|
+
republished.set(path50, out);
|
|
20408
20784
|
return out;
|
|
20409
20785
|
};
|
|
20410
20786
|
const NO_ORIGIN = /* @__PURE__ */ new Set();
|
|
@@ -20413,9 +20789,9 @@ function buildCodeIndex(model) {
|
|
|
20413
20789
|
return to ? republishedFrom(to) : NO_ORIGIN;
|
|
20414
20790
|
};
|
|
20415
20791
|
const scopeOf2 = (site, from) => {
|
|
20416
|
-
const
|
|
20417
|
-
const f = facts.get(
|
|
20418
|
-
return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path:
|
|
20792
|
+
const path50 = pathKey(site.from ?? from);
|
|
20793
|
+
const f = facts.get(path50);
|
|
20794
|
+
return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path50, facts: f } : void 0;
|
|
20419
20795
|
};
|
|
20420
20796
|
const originOf2 = (site, from) => {
|
|
20421
20797
|
const resolved = scopeOf2(site, from);
|
|
@@ -20457,6 +20833,8 @@ function buildCodeIndex(model) {
|
|
|
20457
20833
|
const target = aliasTargetOf(decl.facts, decl.name);
|
|
20458
20834
|
if (target !== void 0) widenThroughAt(decl.path, target, typeBindingOf(decl.facts, target));
|
|
20459
20835
|
}
|
|
20836
|
+
const declared = declaringModule(at, atFacts, typeName);
|
|
20837
|
+
if (declared) for (const implementor of implementorsOf(declared.path, declared.name)) out.add(implementor);
|
|
20460
20838
|
};
|
|
20461
20839
|
if (site.field) for (const typeName of fieldTypesOf(f, site.field)) widenThroughType(typeName);
|
|
20462
20840
|
if (site.receiverPath && site.receiverPath.length >= 2) {
|
|
@@ -20529,8 +20907,43 @@ function buildCodeIndex(model) {
|
|
|
20529
20907
|
}
|
|
20530
20908
|
return found;
|
|
20531
20909
|
};
|
|
20910
|
+
const moduleKey = (p) => pathKey(p).replace(/\.(d\.)?(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/, "").replace(/\/index$/, "");
|
|
20911
|
+
const declaringModule = (at, atFacts, typeName) => {
|
|
20912
|
+
const specifier = typeBindingOf(atFacts, typeName);
|
|
20913
|
+
if (specifier === void 0) return { path: at, name: typeName };
|
|
20914
|
+
const name = importBindingOf(atFacts, typeName)?.imported ?? typeName;
|
|
20915
|
+
const resolved = resolveImport(at, specifier, paths, packages);
|
|
20916
|
+
if (resolved) return { path: resolved, name };
|
|
20917
|
+
if (!specifier.startsWith(".")) return void 0;
|
|
20918
|
+
const dir = pathKey(at).split("/").slice(0, -1);
|
|
20919
|
+
for (const part of specifier.split("/")) {
|
|
20920
|
+
if (part === "..") dir.pop();
|
|
20921
|
+
else if (part !== "." && part !== "") dir.push(part);
|
|
20922
|
+
}
|
|
20923
|
+
return { path: dir.join("/"), name };
|
|
20924
|
+
};
|
|
20925
|
+
let implementors;
|
|
20926
|
+
const implementorsOf = (path50, typeName) => {
|
|
20927
|
+
if (!implementors) {
|
|
20928
|
+
implementors = /* @__PURE__ */ new Map();
|
|
20929
|
+
for (const [file, f] of facts) {
|
|
20930
|
+
if (f.status !== "analyzed" || f.analysisGrade !== "exact" || !f.implementsClauses) continue;
|
|
20931
|
+
for (const names of Object.values(f.implementsClauses)) {
|
|
20932
|
+
for (const name of names) {
|
|
20933
|
+
const declared = declaringModule(file, f, name);
|
|
20934
|
+
if (!declared) continue;
|
|
20935
|
+
const key2 = `${moduleKey(declared.path)}#${declared.name}`;
|
|
20936
|
+
const set = implementors.get(key2) ?? /* @__PURE__ */ new Set();
|
|
20937
|
+
set.add(file);
|
|
20938
|
+
implementors.set(key2, set);
|
|
20939
|
+
}
|
|
20940
|
+
}
|
|
20941
|
+
}
|
|
20942
|
+
}
|
|
20943
|
+
return implementors.get(`${moduleKey(path50)}#${typeName}`) ?? NO_NAMES;
|
|
20944
|
+
};
|
|
20532
20945
|
const holdsMemberBody = (f, name, container) => f.status === "analyzed" && f.analysisGrade === "exact" && !!f.functionBodies && Object.prototype.hasOwnProperty.call(f.functionBodies, name) && f.functionBodies[name].some((b) => b.container === container);
|
|
20533
|
-
const declarationsAt = (
|
|
20946
|
+
const declarationsAt = (path50) => namesAt(declarations, pathKey(path50), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
|
|
20534
20947
|
const forwardsOf = (start, name, container) => {
|
|
20535
20948
|
const out = [];
|
|
20536
20949
|
const listed = /* @__PURE__ */ new Set();
|
|
@@ -20574,12 +20987,13 @@ function buildCodeIndex(model) {
|
|
|
20574
20987
|
paths,
|
|
20575
20988
|
exactPaths,
|
|
20576
20989
|
packages,
|
|
20577
|
-
factsAt: (
|
|
20990
|
+
factsAt: (path50) => facts.get(pathKey(path50)),
|
|
20578
20991
|
declarationsAt,
|
|
20579
|
-
anchorsAt: (
|
|
20580
|
-
findingAnchorsAt: (
|
|
20992
|
+
anchorsAt: (path50) => namesAt(anchors, pathKey(path50), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
|
|
20993
|
+
findingAnchorsAt: (path50) => namesAt(findingAnchors, pathKey(path50), (f) => new Set(f.anchoredNames)),
|
|
20581
20994
|
originOf: originOf2,
|
|
20582
20995
|
possibleOriginsOf,
|
|
20996
|
+
implementorsOf,
|
|
20583
20997
|
forwardsOf,
|
|
20584
20998
|
// Realization has begun once any named file is one the run analyzed or
|
|
20585
20999
|
// could read: missing and escaped are the only statuses that hold nothing.
|
|
@@ -20622,10 +21036,10 @@ function buildRealizationIndex(ctx) {
|
|
|
20622
21036
|
return {
|
|
20623
21037
|
paths,
|
|
20624
21038
|
implementationsOf: (compId) => implsByComponent.get(compId) ?? NONE_IMPL,
|
|
20625
|
-
implementationsAt: (
|
|
21039
|
+
implementationsAt: (path50) => implsByPath.get(pathKey(path50)) ?? NONE_IMPL,
|
|
20626
21040
|
filesOf: (compId) => filesByComponent.get(compId) ?? NONE_PATH,
|
|
20627
21041
|
filesIn: (subsystemId) => filesBySubsystem.get(subsystemId) ?? NONE_PATH,
|
|
20628
|
-
componentsAt: (
|
|
21042
|
+
componentsAt: (path50) => componentsByPath.get(pathKey(path50)) ?? NONE_COMP
|
|
20629
21043
|
};
|
|
20630
21044
|
}
|
|
20631
21045
|
function buildImportGraph(index, universe) {
|
|
@@ -20637,22 +21051,22 @@ function buildImportGraph(index, universe) {
|
|
|
20637
21051
|
if (to && to !== from) into.add(to);
|
|
20638
21052
|
}
|
|
20639
21053
|
};
|
|
20640
|
-
const importsOf = (
|
|
20641
|
-
const cached = imports.get(
|
|
21054
|
+
const importsOf = (path50) => {
|
|
21055
|
+
const cached = imports.get(path50);
|
|
20642
21056
|
if (cached) return cached;
|
|
20643
|
-
const facts = index.factsAt(
|
|
21057
|
+
const facts = index.factsAt(path50);
|
|
20644
21058
|
const out = /* @__PURE__ */ new Set();
|
|
20645
|
-
if (facts?.status === "analyzed") resolveAll(
|
|
20646
|
-
imports.set(
|
|
21059
|
+
if (facts?.status === "analyzed") resolveAll(path50, facts.imports, out);
|
|
21060
|
+
imports.set(path50, out);
|
|
20647
21061
|
return out;
|
|
20648
21062
|
};
|
|
20649
|
-
const tracesOf = (
|
|
20650
|
-
const cached = traces.get(
|
|
21063
|
+
const tracesOf = (path50) => {
|
|
21064
|
+
const cached = traces.get(path50);
|
|
20651
21065
|
if (cached) return cached;
|
|
20652
|
-
const facts = index.factsAt(
|
|
20653
|
-
const out = new Set(importsOf(
|
|
20654
|
-
if (facts?.status === "analyzed") resolveAll(
|
|
20655
|
-
traces.set(
|
|
21066
|
+
const facts = index.factsAt(path50);
|
|
21067
|
+
const out = new Set(importsOf(path50));
|
|
21068
|
+
if (facts?.status === "analyzed") resolveAll(path50, facts.reexports, out);
|
|
21069
|
+
traces.set(path50, out);
|
|
20656
21070
|
return out;
|
|
20657
21071
|
};
|
|
20658
21072
|
return {
|
|
@@ -21139,6 +21553,105 @@ var signatureTypeReferencesRule = {
|
|
|
21139
21553
|
}
|
|
21140
21554
|
};
|
|
21141
21555
|
|
|
21556
|
+
// src/core/rules/integrity/implements-contracts.ts
|
|
21557
|
+
var implementsContractsRule = {
|
|
21558
|
+
name: "implements-contracts",
|
|
21559
|
+
judges: "design",
|
|
21560
|
+
description: "An interface that declares `implements: alias::name` realizes another project's extension point, so it must declare every method of that contract with the same params and returns, read from the export the reference resolves to (a contained member's live table, or a declared external's pin). Types are compared semantically, never by spelling: the producer's own bare name and the consumer's `alias::name` (or the bare name its `use` imports) are one type. Whether the reference resolves and names an entry exported with role implement is project-boundaries' finding (EXTERNAL_NOT_EXPORTED); this rule judges the shape once it resolves. Extra methods are the implementer's own and are not reported.",
|
|
21561
|
+
codes: [
|
|
21562
|
+
{ code: "IMPLEMENTS_MISMATCH", defaultSeverity: "error", summary: "An interface implementing an extension point lacks one of its methods, or declares one with different params or returns" }
|
|
21563
|
+
],
|
|
21564
|
+
check(ctx) {
|
|
21565
|
+
for (const intf of ctx.interfaces) {
|
|
21566
|
+
if (intf.implements === void 0 || !ctx.isSpecInScope(intf.id)) continue;
|
|
21567
|
+
const point = extensionPoint(ctx, intf.id, intf.implements);
|
|
21568
|
+
if (!point) continue;
|
|
21569
|
+
const { methods: contract, publicNames } = point;
|
|
21570
|
+
const alias = intf.implements.split("::")[0];
|
|
21571
|
+
const expectedType = (t) => respelled(t, (n) => publicNames.get(lastName(n)) ?? lastName(n));
|
|
21572
|
+
const ownType = (t) => respelled(t, (n) => n.startsWith(`${alias}::`) ? lastName(n.slice(alias.length + 2)) : n);
|
|
21573
|
+
const draft = intf.status === "draft" || intf.status === "design" || ctx.isComponentDraft(intf.component);
|
|
21574
|
+
for (const expected of contract) {
|
|
21575
|
+
const own2 = intf.methods.find((m) => m.name === expected.name);
|
|
21576
|
+
const differs = own2 ? difference(expected, own2, expectedType, ownType) : "is not declared";
|
|
21577
|
+
if (!differs) continue;
|
|
21578
|
+
const former = own2 ? void 0 : (expected.formerly ?? []).find((f) => intf.methods.some((m) => m.name === f));
|
|
21579
|
+
const renamedFrom = former ? ` It was renamed from "${former}" (the producer's rename trace), which this interface still declares \u2014 rename it to "${expected.name}".` : "";
|
|
21580
|
+
ctx.addIssue(
|
|
21581
|
+
"error",
|
|
21582
|
+
"IMPLEMENTS_MISMATCH",
|
|
21583
|
+
`Interface "${intf.id}" implements "${intf.implements}", whose method "${expected.name}" ${differs === "is not declared" ? "it does not declare" : `it declares with ${differs}`}.${renamedFrom} An implementation of an extension point declares every method of it with the same signature: ${expected.name}(${(expected.params ?? []).map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${expected.returns}.`,
|
|
21584
|
+
intf.id,
|
|
21585
|
+
draft,
|
|
21586
|
+
void 0,
|
|
21587
|
+
{ at: expected.name }
|
|
21588
|
+
);
|
|
21589
|
+
}
|
|
21590
|
+
}
|
|
21591
|
+
}
|
|
21592
|
+
};
|
|
21593
|
+
function lastName(name) {
|
|
21594
|
+
return name.split("::").pop().split(".").pop();
|
|
21595
|
+
}
|
|
21596
|
+
function renamed(expr, rename) {
|
|
21597
|
+
const args = expr.args.map((a) => renamed(a, rename));
|
|
21598
|
+
return expr.form === "named" || expr.form === "applied" ? { ...expr, name: rename(expr.name), args } : { ...expr, args };
|
|
21599
|
+
}
|
|
21600
|
+
function respelled(text3, rename) {
|
|
21601
|
+
const keep = (name) => isTypeVocabulary(name) ? name : rename(name);
|
|
21602
|
+
const parsed = parseTypeExpression(text3, "returns");
|
|
21603
|
+
if (parsed.expression) return canonicalTypeText(renamed(parsed.expression, keep));
|
|
21604
|
+
return text3.replace(/[A-Za-z_][A-Za-z0-9_-]*(?:(?:::|\.)[A-Za-z_][A-Za-z0-9_-]*)*/g, keep);
|
|
21605
|
+
}
|
|
21606
|
+
function difference(expected, own2, expectedType, ownType) {
|
|
21607
|
+
const shape = (m, as) => (m.params ?? []).map((p) => `${as(p.type)}${p.optional ? "?" : ""}`).join(", ");
|
|
21608
|
+
const written = (m) => (m.params ?? []).map((p) => `${p.type}${p.optional ? "?" : ""}`).join(", ");
|
|
21609
|
+
if (shape(expected, expectedType) !== shape(own2, ownType)) return `params (${written(own2)}) instead of (${written(expected)})`;
|
|
21610
|
+
if (expectedType(expected.returns) !== ownType(own2.returns)) return `returns ${own2.returns} instead of ${expected.returns}`;
|
|
21611
|
+
return void 0;
|
|
21612
|
+
}
|
|
21613
|
+
function extensionPoint(ctx, specId, ref) {
|
|
21614
|
+
const resolution = ctx.resolveCrossProject(specId, "implements", ref);
|
|
21615
|
+
if (resolution && resolution.outcome !== "resolved") return void 0;
|
|
21616
|
+
const surface = ctx.resolveSurfaceRef(ref);
|
|
21617
|
+
if (surface.kind === "resolved") {
|
|
21618
|
+
if (surface.entry.role !== "implement") return void 0;
|
|
21619
|
+
const publicNames2 = /* @__PURE__ */ new Map();
|
|
21620
|
+
for (const t of surface.snapshot.exportedTypes ?? []) publicNames2.set(lastName(t.type), t.id);
|
|
21621
|
+
return { methods: surface.entry.methods, publicNames: publicNames2 };
|
|
21622
|
+
}
|
|
21623
|
+
const [alias, name] = ref.split("::");
|
|
21624
|
+
if (name === void 0) return void 0;
|
|
21625
|
+
const bound2 = ctx.projectFamily?.nodes.find((n) => n.namespace === "");
|
|
21626
|
+
const key2 = bound2?.aliases.get(alias);
|
|
21627
|
+
if (key2 === void 0) return void 0;
|
|
21628
|
+
const table = (ctx.exportTables ?? []).find((t) => t.level === "project" && t.owner === key2);
|
|
21629
|
+
const entry = table?.entries.find((e) => e.kind === "component" && e.publicName === name);
|
|
21630
|
+
if (!entry || entry.role !== "implement" || !entry.component) return void 0;
|
|
21631
|
+
const methods = entry.interface ? ctx.interfaceMap.get(entry.interface)?.methods : ctx.interfaceMethodsOf(entry.component);
|
|
21632
|
+
if (!methods) return void 0;
|
|
21633
|
+
const traced2 = methods.map((m) => ({ ...m, formerly: (m.previousNames ?? []).map((k) => k.slice(k.lastIndexOf(".") + 1)) }));
|
|
21634
|
+
const publicNames = /* @__PURE__ */ new Map();
|
|
21635
|
+
for (const e of table?.entries ?? []) {
|
|
21636
|
+
if (e.kind === "type" && e.typeDef) publicNames.set(lastName(e.typeDef), e.publicName);
|
|
21637
|
+
}
|
|
21638
|
+
return { methods: traced2, publicNames };
|
|
21639
|
+
}
|
|
21640
|
+
function renamedExportMethod(ctx, source) {
|
|
21641
|
+
const dot = source.lastIndexOf(".");
|
|
21642
|
+
if (dot <= 0 || !source.includes("::")) return void 0;
|
|
21643
|
+
const [head2, method] = [source.slice(0, dot), source.slice(dot + 1)];
|
|
21644
|
+
const surface = ctx.resolveSurfaceRef(head2);
|
|
21645
|
+
if (surface.kind === "resolved") return surface.entry.methods.find((m) => (m.formerly ?? []).includes(method))?.name;
|
|
21646
|
+
const [alias, name] = head2.split("::");
|
|
21647
|
+
const key2 = ctx.projectFamily?.nodes.find((n) => n.namespace === "")?.aliases.get(alias);
|
|
21648
|
+
if (key2 === void 0 || name === void 0) return void 0;
|
|
21649
|
+
const entry = (ctx.exportTables ?? []).find((t) => t.level === "project" && t.owner === key2)?.entries.find((e) => e.kind === "component" && e.publicName === name);
|
|
21650
|
+
if (!entry?.component) return void 0;
|
|
21651
|
+
const methods = entry.interface ? ctx.interfaceMap.get(entry.interface)?.methods : ctx.interfaceMethodsOf(entry.component);
|
|
21652
|
+
return methods?.find((m) => (m.previousNames ?? []).some((k) => k.slice(k.lastIndexOf(".") + 1) === method))?.name;
|
|
21653
|
+
}
|
|
21654
|
+
|
|
21142
21655
|
// src/core/rules/integrity/signature-sources.ts
|
|
21143
21656
|
function draftContext(ctx, interfaceId, component) {
|
|
21144
21657
|
const intf = ctx.interfaceMap.get(interfaceId);
|
|
@@ -21165,17 +21678,20 @@ var signatureSourcesRule = {
|
|
|
21165
21678
|
const where = `Method "${fact.method}" on interface "${fact.interfaceId}"`;
|
|
21166
21679
|
const at = { at: fact.method };
|
|
21167
21680
|
switch (fact.outcome) {
|
|
21168
|
-
case "unresolved":
|
|
21681
|
+
case "unresolved": {
|
|
21682
|
+
const renamed2 = renamedExportMethod(ctx, fact.source);
|
|
21683
|
+
const rename = renamed2 ? ` It was renamed to "${renamed2}" (the producer's rename trace) \u2014 follow the rename: take it from "${fact.source.slice(0, fact.source.lastIndexOf("."))}.${renamed2}".` : "";
|
|
21169
21684
|
ctx.addIssue(
|
|
21170
21685
|
"error",
|
|
21171
21686
|
"SIGNATURE_SOURCE_UNRESOLVED",
|
|
21172
|
-
`${where} takes its signature from "${fact.source}", which names ${fact.detail ? `no contract method, and ${fact.detail}` : "no contract method and no signature type"}
|
|
21687
|
+
`${where} takes its signature from "${fact.source}", which names ${fact.detail ? `no contract method, and ${fact.detail}` : "no contract method and no signature type"}.${rename} A source is a \`component.method\` the component reaches, an exported contract's method (\`alias::name.method\`), or a type of kind signature.`,
|
|
21173
21688
|
fact.interfaceId,
|
|
21174
21689
|
isDraft,
|
|
21175
21690
|
void 0,
|
|
21176
21691
|
at
|
|
21177
21692
|
);
|
|
21178
21693
|
continue;
|
|
21694
|
+
}
|
|
21179
21695
|
case "ambiguous":
|
|
21180
21696
|
ctx.addIssue(
|
|
21181
21697
|
"error",
|
|
@@ -21330,12 +21846,12 @@ function draftContext3(ctx, specId, kind) {
|
|
|
21330
21846
|
const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
|
|
21331
21847
|
return !!sub && (sub.status === "draft" || sub.status === "design");
|
|
21332
21848
|
}
|
|
21333
|
-
function siteOf(
|
|
21334
|
-
const [head2, name] =
|
|
21335
|
-
return (head2 === "methods" || head2 === "fields") && name ? name :
|
|
21849
|
+
function siteOf(path50) {
|
|
21850
|
+
const [head2, name] = path50.split(".");
|
|
21851
|
+
return (head2 === "methods" || head2 === "fields") && name ? name : path50;
|
|
21336
21852
|
}
|
|
21337
|
-
function located(kind, specId,
|
|
21338
|
-
return `${
|
|
21853
|
+
function located(kind, specId, path50) {
|
|
21854
|
+
return `${path50 ? `"${path50}" of ` : ""}${kind ?? "spec"} "${specId}"`;
|
|
21339
21855
|
}
|
|
21340
21856
|
function problemMessage(problem, specId) {
|
|
21341
21857
|
const where = located(problem.kind, specId, problem.path);
|
|
@@ -21616,88 +22132,6 @@ var publicSurfaceConsumersRule = {
|
|
|
21616
22132
|
}
|
|
21617
22133
|
};
|
|
21618
22134
|
|
|
21619
|
-
// src/core/rules/integrity/implements-contracts.ts
|
|
21620
|
-
var implementsContractsRule = {
|
|
21621
|
-
name: "implements-contracts",
|
|
21622
|
-
judges: "design",
|
|
21623
|
-
description: "An interface that declares `implements: alias::name` realizes another project's extension point, so it must declare every method of that contract with the same params and returns, read from the export the reference resolves to (a contained member's live table, or a declared external's pin). Types are compared semantically, never by spelling: the producer's own bare name and the consumer's `alias::name` (or the bare name its `use` imports) are one type. Whether the reference resolves and names an entry exported with role implement is project-boundaries' finding (EXTERNAL_NOT_EXPORTED); this rule judges the shape once it resolves. Extra methods are the implementer's own and are not reported.",
|
|
21624
|
-
codes: [
|
|
21625
|
-
{ code: "IMPLEMENTS_MISMATCH", defaultSeverity: "error", summary: "An interface implementing an extension point lacks one of its methods, or declares one with different params or returns" }
|
|
21626
|
-
],
|
|
21627
|
-
check(ctx) {
|
|
21628
|
-
for (const intf of ctx.interfaces) {
|
|
21629
|
-
if (intf.implements === void 0 || !ctx.isSpecInScope(intf.id)) continue;
|
|
21630
|
-
const point = extensionPoint(ctx, intf.id, intf.implements);
|
|
21631
|
-
if (!point) continue;
|
|
21632
|
-
const { methods: contract, publicNames } = point;
|
|
21633
|
-
const alias = intf.implements.split("::")[0];
|
|
21634
|
-
const expectedType = (t) => respelled(t, (n) => publicNames.get(lastName(n)) ?? lastName(n));
|
|
21635
|
-
const ownType = (t) => respelled(t, (n) => n.startsWith(`${alias}::`) ? lastName(n.slice(alias.length + 2)) : n);
|
|
21636
|
-
const draft = intf.status === "draft" || intf.status === "design" || ctx.isComponentDraft(intf.component);
|
|
21637
|
-
for (const expected of contract) {
|
|
21638
|
-
const own2 = intf.methods.find((m) => m.name === expected.name);
|
|
21639
|
-
const differs = own2 ? difference(expected, own2, expectedType, ownType) : "is not declared";
|
|
21640
|
-
if (!differs) continue;
|
|
21641
|
-
ctx.addIssue(
|
|
21642
|
-
"error",
|
|
21643
|
-
"IMPLEMENTS_MISMATCH",
|
|
21644
|
-
`Interface "${intf.id}" implements "${intf.implements}", whose method "${expected.name}" ${differs === "is not declared" ? "it does not declare" : `it declares with ${differs}`}. An implementation of an extension point declares every method of it with the same signature: ${expected.name}(${(expected.params ?? []).map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${expected.returns}.`,
|
|
21645
|
-
intf.id,
|
|
21646
|
-
draft,
|
|
21647
|
-
void 0,
|
|
21648
|
-
{ at: expected.name }
|
|
21649
|
-
);
|
|
21650
|
-
}
|
|
21651
|
-
}
|
|
21652
|
-
}
|
|
21653
|
-
};
|
|
21654
|
-
function lastName(name) {
|
|
21655
|
-
return name.split("::").pop().split(".").pop();
|
|
21656
|
-
}
|
|
21657
|
-
function renamed(expr, rename) {
|
|
21658
|
-
const args = expr.args.map((a) => renamed(a, rename));
|
|
21659
|
-
return expr.form === "named" || expr.form === "applied" ? { ...expr, name: rename(expr.name), args } : { ...expr, args };
|
|
21660
|
-
}
|
|
21661
|
-
function respelled(text3, rename) {
|
|
21662
|
-
const keep = (name) => isTypeVocabulary(name) ? name : rename(name);
|
|
21663
|
-
const parsed = parseTypeExpression(text3, "returns");
|
|
21664
|
-
if (parsed.expression) return canonicalTypeText(renamed(parsed.expression, keep));
|
|
21665
|
-
return text3.replace(/[A-Za-z_][A-Za-z0-9_-]*(?:(?:::|\.)[A-Za-z_][A-Za-z0-9_-]*)*/g, keep);
|
|
21666
|
-
}
|
|
21667
|
-
function difference(expected, own2, expectedType, ownType) {
|
|
21668
|
-
const shape = (m, as) => (m.params ?? []).map((p) => `${as(p.type)}${p.optional ? "?" : ""}`).join(", ");
|
|
21669
|
-
const written = (m) => (m.params ?? []).map((p) => `${p.type}${p.optional ? "?" : ""}`).join(", ");
|
|
21670
|
-
if (shape(expected, expectedType) !== shape(own2, ownType)) return `params (${written(own2)}) instead of (${written(expected)})`;
|
|
21671
|
-
if (expectedType(expected.returns) !== ownType(own2.returns)) return `returns ${own2.returns} instead of ${expected.returns}`;
|
|
21672
|
-
return void 0;
|
|
21673
|
-
}
|
|
21674
|
-
function extensionPoint(ctx, specId, ref) {
|
|
21675
|
-
const resolution = ctx.resolveCrossProject(specId, "implements", ref);
|
|
21676
|
-
if (resolution && resolution.outcome !== "resolved") return void 0;
|
|
21677
|
-
const surface = ctx.resolveSurfaceRef(ref);
|
|
21678
|
-
if (surface.kind === "resolved") {
|
|
21679
|
-
if (surface.entry.role !== "implement") return void 0;
|
|
21680
|
-
const publicNames2 = /* @__PURE__ */ new Map();
|
|
21681
|
-
for (const t of surface.snapshot.exportedTypes ?? []) publicNames2.set(lastName(t.type), t.id);
|
|
21682
|
-
return { methods: surface.entry.methods, publicNames: publicNames2 };
|
|
21683
|
-
}
|
|
21684
|
-
const [alias, name] = ref.split("::");
|
|
21685
|
-
if (name === void 0) return void 0;
|
|
21686
|
-
const bound2 = ctx.projectFamily?.nodes.find((n) => n.namespace === "");
|
|
21687
|
-
const key2 = bound2?.aliases.get(alias);
|
|
21688
|
-
if (key2 === void 0) return void 0;
|
|
21689
|
-
const table = (ctx.exportTables ?? []).find((t) => t.level === "project" && t.owner === key2);
|
|
21690
|
-
const entry = table?.entries.find((e) => e.kind === "component" && e.publicName === name);
|
|
21691
|
-
if (!entry || entry.role !== "implement" || !entry.component) return void 0;
|
|
21692
|
-
const methods = entry.interface ? ctx.interfaceMap.get(entry.interface)?.methods : ctx.interfaceMethodsOf(entry.component);
|
|
21693
|
-
if (!methods) return void 0;
|
|
21694
|
-
const publicNames = /* @__PURE__ */ new Map();
|
|
21695
|
-
for (const e of table?.entries ?? []) {
|
|
21696
|
-
if (e.kind === "type" && e.typeDef) publicNames.set(lastName(e.typeDef), e.publicName);
|
|
21697
|
-
}
|
|
21698
|
-
return { methods, publicNames };
|
|
21699
|
-
}
|
|
21700
|
-
|
|
21701
22135
|
// src/core/rules/integrity/public-surface-bound-contract.ts
|
|
21702
22136
|
var publicSurfaceBoundContractRule = {
|
|
21703
22137
|
name: "public-surface-bound-contract",
|
|
@@ -23003,11 +23437,11 @@ var unconditionalCallCyclesRule = {
|
|
|
23003
23437
|
const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
|
|
23004
23438
|
if (memberEdges.length === 0) continue;
|
|
23005
23439
|
const anchor = [...memberEdges].sort((a, b) => byCodeUnit(a.fromKey, b.fromKey) || byCodeUnit(a.impl.id, b.impl.id) || a.stepNumber - b.stepNumber)[0];
|
|
23006
|
-
const
|
|
23440
|
+
const path50 = [...keys].sort(byCodeUnit).join(" \u2192 ");
|
|
23007
23441
|
ctx.addIssue(
|
|
23008
23442
|
"warning",
|
|
23009
23443
|
"UNCONDITIONAL_CALL_CYCLE",
|
|
23010
|
-
`Call cycle with no guard: ${
|
|
23444
|
+
`Call cycle with no guard: ${path50} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
|
|
23011
23445
|
anchor.impl.id,
|
|
23012
23446
|
memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
|
|
23013
23447
|
);
|
|
@@ -23306,7 +23740,8 @@ var portalsRule = {
|
|
|
23306
23740
|
"ENDPOINT_TRANSPORT_MISMATCH",
|
|
23307
23741
|
`Method "${m.name}" on interface "${intf.id}" declares a "${m.endpoint.transport}" endpoint, but its Portal "${comp.id}" has transport "${comp.transport}" (expects transport "${expected}").`,
|
|
23308
23742
|
intf.id,
|
|
23309
|
-
|
|
23743
|
+
// A contradiction, not unfinished work: never downgraded on a draft.
|
|
23744
|
+
false
|
|
23310
23745
|
);
|
|
23311
23746
|
}
|
|
23312
23747
|
}
|
|
@@ -25890,7 +26325,9 @@ var eventTopologyRule = {
|
|
|
25890
26325
|
"UNCONSUMED_TOPIC",
|
|
25891
26326
|
`Component "${e.compId}" emits topic "${e.topic}" (${e.vias.join(", ")}), but nothing in this tree subscribes to it \u2014 the event goes nowhere. Wire a subscriber (subscribesTo, or a MessageBus subscribe endpoint), fix the topic name, or lint.allow with the external consumer named.`,
|
|
25892
26327
|
e.compId,
|
|
25893
|
-
ctx.isComponentDraft(e.compId)
|
|
26328
|
+
ctx.isComponentDraft(e.compId),
|
|
26329
|
+
void 0,
|
|
26330
|
+
{ at: e.topic }
|
|
25894
26331
|
);
|
|
25895
26332
|
}
|
|
25896
26333
|
for (const s of subscribers) {
|
|
@@ -25900,7 +26337,9 @@ var eventTopologyRule = {
|
|
|
25900
26337
|
"UNSOURCED_SUBSCRIPTION",
|
|
25901
26338
|
`Component "${s.compId}" subscribes to topic "${s.topic}" (${s.vias.join(", ")}), but nothing in this tree emits it \u2014 the handler can never fire from inside this system. Wire the emitter (emits, or a MessageBus publish endpoint), fix the topic name, or lint.allow with the external source named.`,
|
|
25902
26339
|
s.compId,
|
|
25903
|
-
ctx.isComponentDraft(s.compId)
|
|
26340
|
+
ctx.isComponentDraft(s.compId),
|
|
26341
|
+
void 0,
|
|
26342
|
+
{ at: s.topic }
|
|
25904
26343
|
);
|
|
25905
26344
|
}
|
|
25906
26345
|
}
|
|
@@ -25914,13 +26353,13 @@ function plannedCode(ctx) {
|
|
|
25914
26353
|
var sourceFileLinkageRule = {
|
|
25915
26354
|
name: "source-file-linkage",
|
|
25916
26355
|
judges: "code",
|
|
25917
|
-
description: "Code\u2194spec Level 1: every source file an implementation names \u2014 its own sourcePath and each method's \u2014 is checked against the code on disk. A named file that is not on disk is PLANNED (SOURCE_FILE_PLANNED, a notice \u2014 planned, not written yet), at method level as at implementation level and whether or not the implementation's realization has begun: a design names the file a method will live in before the method is written, and code linkage, outside the approval since format 3, costs nothing to declare. Whether the realization has BEGUN (code_index.holdsAny over the files it names: at least one exists) decides only how an unlinked method is reported: before that, an implementation that names no file is unlinked design (MISSING_SOURCE_PATH, a notice), so a design-only tree passes `validate --ci`; once it has begun, a contract method that names no file while others do is unlinked (METHOD_SOURCE_PATH_MISSING, a warning), and a method its named, existing file does not hold is method-realization's UNREALIZED_METHOD. A team that wants CI to demand code for every designed implementation sets rules.conformance.requireCode, which reports the two notices at error, naming the setting. A file that escapes the root or cannot be analyzed is reported once and blocks only the methods realized in it. A run in which TypeScript/JavaScript files were analyzed below exact grade reports that once (CONFORMANCE_DEGRADED). Implementations under chained subsystems (projectPath) validate standalone in their own project run and are skipped here.",
|
|
26356
|
+
description: "Code\u2194spec Level 1: every source file an implementation names \u2014 its own sourcePath and each method's \u2014 is checked against the code on disk. A named file that is not on disk is PLANNED (SOURCE_FILE_PLANNED, a notice \u2014 planned, not written yet), at method level as at implementation level and whether or not the implementation's realization has begun: a design names the file a method will live in before the method is written, and code linkage, outside the approval since format 3, costs nothing to declare. The planned files of one implementation are one notice naming them all, so a planned design reads as one line per component, never one per file. Whether the realization has BEGUN (code_index.holdsAny over the files it names: at least one exists) decides only how an unlinked method is reported: before that, an implementation that names no file is unlinked design (MISSING_SOURCE_PATH, a notice), so a design-only tree passes `validate --ci`; once it has begun, a contract method that names no file while others do is unlinked (METHOD_SOURCE_PATH_MISSING, a warning), and a method its named, existing file does not hold is method-realization's UNREALIZED_METHOD. A team that wants CI to demand code for every designed implementation sets rules.conformance.requireCode, which reports the two notices at error, naming the setting. A file that escapes the root or cannot be analyzed is reported once and blocks only the methods realized in it. A run in which TypeScript/JavaScript files were analyzed below exact grade reports that once (CONFORMANCE_DEGRADED). Implementations under chained subsystems (projectPath) validate standalone in their own project run and are skipped here.",
|
|
25918
26357
|
codes: [
|
|
25919
26358
|
{ code: "MISSING_SOURCE_PATH", defaultSeverity: "notice", summary: "An implementation whose realization has not begun names no source file (for itself or for some contract methods) \u2014 designed, not linked to code yet; an error under rules.conformance.requireCode" },
|
|
25920
26359
|
{ code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file an implementation or one of its methods names is absolute or escapes the project root (containment refusal)" },
|
|
25921
26360
|
{ code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file an implementation or one of its methods names could not be analyzed (binary/unreadable) \u2014 realization of the methods in it was not checked" },
|
|
25922
26361
|
{ code: "CONFORMANCE_DEGRADED", defaultSeverity: "warning", summary: "TypeScript/JavaScript files were analyzed below exact grade (compiler not resolvable) \u2014 dependency conformance skips them" },
|
|
25923
|
-
{ code: "SOURCE_FILE_PLANNED", defaultSeverity: "notice", summary: "
|
|
26362
|
+
{ code: "SOURCE_FILE_PLANNED", defaultSeverity: "notice", summary: "Source files an implementation or its methods name are not on disk \u2014 planned, not written yet, whether or not another file it names exists; one notice per implementation naming every planned file, and method-realization judges the methods in each once it exists. An error under rules.conformance.requireCode" },
|
|
25924
26363
|
{ code: "METHOD_SOURCE_PATH_MISSING", defaultSeverity: "warning", summary: "An implementation whose realization has begun leaves contract methods without a source file \u2014 structural conformance cannot link them to code" }
|
|
25925
26364
|
],
|
|
25926
26365
|
check(ctx) {
|
|
@@ -25972,6 +26411,7 @@ var sourceFileLinkageRule = {
|
|
|
25972
26411
|
}
|
|
25973
26412
|
}
|
|
25974
26413
|
const reported = /* @__PURE__ */ new Set();
|
|
26414
|
+
const plannedFiles = [];
|
|
25975
26415
|
for (const file of implementationSourceFiles(impl)) {
|
|
25976
26416
|
const key2 = pathKey(file);
|
|
25977
26417
|
if (reported.has(key2)) continue;
|
|
@@ -25990,13 +26430,7 @@ var sourceFileLinkageRule = {
|
|
|
25990
26430
|
draft
|
|
25991
26431
|
);
|
|
25992
26432
|
} else if (facts.status === "missing") {
|
|
25993
|
-
|
|
25994
|
-
planned.severity,
|
|
25995
|
-
"SOURCE_FILE_PLANNED",
|
|
25996
|
-
`${owner} sourcePath "${file}" is planned, not written yet \u2014 method-realization judges the methods in it once it exists${planned.note}.`,
|
|
25997
|
-
impl.id,
|
|
25998
|
-
draft
|
|
25999
|
-
);
|
|
26433
|
+
plannedFiles.push(users.length === 0 ? `"${file}"` : `"${file}" (${users.length === 1 ? "method" : "methods"} ${quoteList(users)})`);
|
|
26000
26434
|
} else {
|
|
26001
26435
|
ctx.addIssue(
|
|
26002
26436
|
"warning",
|
|
@@ -26007,6 +26441,16 @@ var sourceFileLinkageRule = {
|
|
|
26007
26441
|
);
|
|
26008
26442
|
}
|
|
26009
26443
|
}
|
|
26444
|
+
if (plannedFiles.length > 0) {
|
|
26445
|
+
const one = plannedFiles.length === 1;
|
|
26446
|
+
ctx.addIssue(
|
|
26447
|
+
planned.severity,
|
|
26448
|
+
"SOURCE_FILE_PLANNED",
|
|
26449
|
+
`Implementation "${impl.id}" plans ${one ? "a source file" : `${plannedFiles.length} source files`} not written yet: ${plannedFiles.join(", ")} \u2014 method-realization judges the methods in ${one ? "it" : "each"} once it exists${planned.note}.`,
|
|
26450
|
+
impl.id,
|
|
26451
|
+
draft
|
|
26452
|
+
);
|
|
26453
|
+
}
|
|
26010
26454
|
}
|
|
26011
26455
|
}
|
|
26012
26456
|
};
|
|
@@ -26420,11 +26864,11 @@ var hiddenStateRule = {
|
|
|
26420
26864
|
byPath.set(key2, list);
|
|
26421
26865
|
}
|
|
26422
26866
|
}
|
|
26423
|
-
for (const
|
|
26424
|
-
const facts = code.factsAt(
|
|
26867
|
+
for (const path50 of code.exactPaths) {
|
|
26868
|
+
const facts = code.factsAt(path50);
|
|
26425
26869
|
const bindings = facts.topLevelMutableBindings ?? [];
|
|
26426
26870
|
if (bindings.length === 0) continue;
|
|
26427
|
-
const mapped = byPath.get(
|
|
26871
|
+
const mapped = byPath.get(path50) ?? [];
|
|
26428
26872
|
if (mapped.length === 0) continue;
|
|
26429
26873
|
if (!mapped.every((m) => isStatelessLogic(m.component))) continue;
|
|
26430
26874
|
const anchor = [...mapped].sort((a, b) => a.impl.id.localeCompare(b.impl.id))[0];
|
|
@@ -26441,15 +26885,17 @@ var hiddenStateRule = {
|
|
|
26441
26885
|
};
|
|
26442
26886
|
|
|
26443
26887
|
// src/core/rules/conformance/dependency-conformance.ts
|
|
26888
|
+
var import_path = require("path");
|
|
26444
26889
|
var dependencyConformanceRule = {
|
|
26445
26890
|
name: "dependency-conformance",
|
|
26446
26891
|
judges: "code",
|
|
26447
|
-
description: "Code\u2194spec Level 2: runtime import edges between component-mapped source files (a component maps to every file its implementations and their methods name) must be justified by declared relations \u2014 a direct dependsOn/owns pair, a shared component, membership in a depended-on pattern, or (across subsystems) a declared edge to the target subsystem's published surface (UNDECLARED_DEPENDENCY). A type-only import alone never accuses \u2014 type coupling is allowed \u2014 but a CALL of a modelled contract method through a collaborator whose declared type comes from another component's file is runtime collaboration whatever the import's form, and the same justification is owed (UNDECLARED_DEPENDENCY on that edge). Conversely, a declared dependsOn/owns edge between components realized in different files should be visible as an import between any file of the source and any file of the target (UNREALIZED_DEPENDENCY \u2014 DI indirection can defeat this, hence warning); a type-only import realizes it, since `import type` is what dependency injection writes down. Two twins of design rules are judged on the same code: a Portal file calling a write- or lifecycle-effect contract method of a Repository, Index, Store or Registry is the persistence shortcut the design rule PORTAL_WRITE_SHORTCUT refuses, unless the Portal's own narrative already claims that call (the design rule's finding then) (PORTAL_WRITE_SHORTCUT_IN_CODE)
|
|
26892
|
+
description: "Code\u2194spec Level 2: runtime import edges between component-mapped source files (a component maps to every file its implementations and their methods name) must be justified by declared relations \u2014 a direct dependsOn/owns pair, a shared component, membership in a depended-on pattern, or (across subsystems) a declared edge to the target subsystem's published surface (UNDECLARED_DEPENDENCY). A type-only import alone never accuses \u2014 type coupling is allowed \u2014 but a CALL of a modelled contract method through a collaborator whose declared type comes from another component's file is runtime collaboration whatever the import's form, and the same justification is owed (UNDECLARED_DEPENDENCY on that edge). Conversely, a declared dependsOn/owns edge between components realized in different files should be visible as an import between any file of the source and any file of the target (UNREALIZED_DEPENDENCY \u2014 DI indirection can defeat this, hence warning); a type-only import realizes it, since `import type` is what dependency injection writes down. An Adapter's edge to a Portal reached over an out-of-process transport (HTTP, gRPC, a bus, a CLI\u2026) is the link the design models: the Adapter's transport client realizes it, never an import, so it is never UNREALIZED_DEPENDENCY \u2014 and nothing asks for an import across that boundary, which would couple two deployables' builds. Two twins of design rules are judged on the same code: a Portal file calling a write- or lifecycle-effect contract method of a Repository, Index, Store or Registry is the persistence shortcut the design rule PORTAL_WRITE_SHORTCUT refuses, unless the Portal's own narrative already claims that call (the design rule's finding then) (PORTAL_WRITE_SHORTCUT_IN_CODE). A receiver is followed through a non-null assertion, parentheses, `satisfies`, a cast to a named type and a local alias or destructuring of a field exactly as through the field itself (a cast to any or unknown says nothing, and is not followed), and a call in a Portal's own file whose receiver the analysis cannot follow at all, under the name of a write- or lifecycle-effect method of a data component in the Portal's own subsystem or one it depends into, is said so rather than passed in silence (PORTAL_CALL_UNRESOLVED). And a file importing a technology's package while no component it realizes is that technology's home is the leak TECH_LEAKAGE refuses in the design (TECH_LEAKAGE_IN_CODE): a technology's packages are its declared tokens (its name, or its `matches`), the curated built-in table of the common packages its name is known by (postgres: pg, postgres, @neondatabase/serverless, @vercel/postgres; mysql: mysql2, mysql; redis: redis, ioredis; mongodb: mongodb, mongoose; sqlite: better-sqlite3, sqlite3; kafka: kafkajs; rabbitmq: amqplib \u2014 an HTTP client is never a technology leak), and the packages a loaded pack contributes for it, each compared exactly with an import's package name. Only exact-grade analyzed files participate; chained subprojects validate standalone.",
|
|
26448
26893
|
codes: [
|
|
26449
26894
|
{ code: "UNDECLARED_DEPENDENCY", defaultSeverity: "warning", summary: "A runtime import, or a call through a type-only-imported collaborator, between component-mapped files has no declared dependsOn/owns (or published-surface) justification" },
|
|
26450
26895
|
{ code: "PORTAL_WRITE_SHORTCUT_IN_CODE", defaultSeverity: "error", summary: "A Portal's code calls a write- or lifecycle-effect method of a data component (Repository, Index, Store, Registry) directly \u2014 the code twin of PORTAL_WRITE_SHORTCUT: mutations route through an Orchestrator" },
|
|
26451
26896
|
{ code: "TECH_LEAKAGE_IN_CODE", defaultSeverity: "warning", summary: "A source file imports a technology's package although no component it realizes binds that technology \u2014 the code twin of TECH_LEAKAGE" },
|
|
26452
|
-
{ code: "UNREALIZED_DEPENDENCY", defaultSeverity: "warning", summary: "A declared dependsOn/owns edge between components in different files is realized by no import between any of their files" }
|
|
26897
|
+
{ code: "UNREALIZED_DEPENDENCY", defaultSeverity: "warning", summary: "A declared dependsOn/owns edge between components in different files is realized by no import between any of their files" },
|
|
26898
|
+
{ code: "PORTAL_CALL_UNRESOLVED", defaultSeverity: "notice", summary: "A call in a Portal's own file, under the name of a data component's write- or lifecycle-effect method, goes through a receiver the analysis cannot follow at all \u2014 neither proven a shortcut nor cleared, and said so rather than passed in silence" }
|
|
26453
26899
|
],
|
|
26454
26900
|
check(ctx) {
|
|
26455
26901
|
const code = ctx.codeIndex();
|
|
@@ -26493,7 +26939,7 @@ var dependencyConformanceRule = {
|
|
|
26493
26939
|
}
|
|
26494
26940
|
return false;
|
|
26495
26941
|
};
|
|
26496
|
-
const draftAt = (
|
|
26942
|
+
const draftAt = (path50) => realization.implementationsAt(path50).some((impl) => ctx.isImplementationDraft(impl));
|
|
26497
26943
|
for (const fromPath of mappedPaths) {
|
|
26498
26944
|
const fromComponents = realization.componentsAt(fromPath);
|
|
26499
26945
|
for (const toPath of graph2.importsOf(fromPath)) {
|
|
@@ -26515,16 +26961,16 @@ var dependencyConformanceRule = {
|
|
|
26515
26961
|
}
|
|
26516
26962
|
}
|
|
26517
26963
|
const typeTraces = /* @__PURE__ */ new Map();
|
|
26518
|
-
const typeTracesOf = (
|
|
26519
|
-
let out = typeTraces.get(
|
|
26964
|
+
const typeTracesOf = (path50) => {
|
|
26965
|
+
let out = typeTraces.get(path50);
|
|
26520
26966
|
if (out) return out;
|
|
26521
26967
|
out = /* @__PURE__ */ new Set();
|
|
26522
|
-
const facts = code.factsAt(
|
|
26968
|
+
const facts = code.factsAt(path50);
|
|
26523
26969
|
for (const specifier of new Set(Object.values(facts?.typeOnlyBindings ?? {}))) {
|
|
26524
|
-
const to = resolveImport(
|
|
26525
|
-
if (to && to !==
|
|
26970
|
+
const to = resolveImport(path50, specifier, mappedPaths, code.packages);
|
|
26971
|
+
if (to && to !== path50) out.add(to);
|
|
26526
26972
|
}
|
|
26527
|
-
typeTraces.set(
|
|
26973
|
+
typeTraces.set(path50, out);
|
|
26528
26974
|
return out;
|
|
26529
26975
|
};
|
|
26530
26976
|
const typeConnects = (from, to) => from.some((f) => {
|
|
@@ -26539,6 +26985,7 @@ var dependencyConformanceRule = {
|
|
|
26539
26985
|
for (const targetId of declaredTargets) {
|
|
26540
26986
|
const target = ctx.componentMap.get(targetId);
|
|
26541
26987
|
if (!target) continue;
|
|
26988
|
+
if (component.componentType === "Adapter" && target.componentType === "Portal" && target.transport !== void 0 && target.transport !== "InProcess") continue;
|
|
26542
26989
|
const crossSubsystem = component.subsystem !== target.subsystem;
|
|
26543
26990
|
const toFiles = crossSubsystem ? realization.filesIn(target.subsystem).filter((p) => isExact(p) && !fromFiles.includes(p)) : filesOf(targetId);
|
|
26544
26991
|
if (toFiles.length === 0) continue;
|
|
@@ -26594,12 +27041,44 @@ var dependencyConformanceRule = {
|
|
|
26594
27041
|
if (!specifier) continue;
|
|
26595
27042
|
const toPath = resolveImport(scope, specifier, mappedPaths, code.packages);
|
|
26596
27043
|
if (toPath && toPath !== scope) out.push({ toPath, typeOnly: typeOnly !== void 0 });
|
|
27044
|
+
const declaredAt = resolveImport(scope, specifier, code.paths, code.packages) ?? (specifier.startsWith(".") ? import_path.posix.join(import_path.posix.dirname(scope), specifier) : void 0);
|
|
27045
|
+
const published = importBindingOf(facts, typeName)?.imported ?? typeName;
|
|
27046
|
+
if (declaredAt) {
|
|
27047
|
+
for (const implementor of code.implementorsOf(declaredAt, published)) {
|
|
27048
|
+
if (mappedPaths.has(implementor) && implementor !== scope) out.push({ toPath: implementor, typeOnly: typeOnly !== void 0 });
|
|
27049
|
+
}
|
|
27050
|
+
}
|
|
26597
27051
|
}
|
|
26598
27052
|
return out;
|
|
26599
27053
|
};
|
|
27054
|
+
const unfollowable = (site, file) => {
|
|
27055
|
+
const facts = code.factsAt(pathKey(site.from ?? file));
|
|
27056
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") return false;
|
|
27057
|
+
if (site.field) return fieldTypesOf(facts, site.field).length === 0;
|
|
27058
|
+
if (site.via) return !importBindingOf(facts, site.via) && localTypesOf(facts, site.via).length === 0;
|
|
27059
|
+
return !site.constructed && !site.returnedBy && !site.enclosingClass && !site.receiverPath;
|
|
27060
|
+
};
|
|
27061
|
+
const dataWrites = /* @__PURE__ */ new Map();
|
|
27062
|
+
for (const c of ctx.components) {
|
|
27063
|
+
if (!DATA_COMPONENTS2.has(c.componentType)) continue;
|
|
27064
|
+
for (const m of ctx.interfaceMethodsOf(c.id)) {
|
|
27065
|
+
if (m.effect !== "write" && m.effect !== "lifecycle") continue;
|
|
27066
|
+
const names = /* @__PURE__ */ new Set([m.name]);
|
|
27067
|
+
for (const impl of realization.implementationsOf(c.id)) {
|
|
27068
|
+
const bound2 = impl.methods.find((x) => x.name === m.name)?.symbol;
|
|
27069
|
+
if (bound2) names.add(bound2);
|
|
27070
|
+
}
|
|
27071
|
+
for (const name of names) {
|
|
27072
|
+
const list = dataWrites.get(name) ?? [];
|
|
27073
|
+
list.push({ component: c.id, method: m.name });
|
|
27074
|
+
dataWrites.set(name, list);
|
|
27075
|
+
}
|
|
27076
|
+
}
|
|
27077
|
+
}
|
|
26600
27078
|
const calledAt = (toPath, name) => realization.componentsAt(toPath).map((c) => ({ component: c, method: calledMethodOf(c.id, name) })).filter((c) => c.method !== void 0);
|
|
26601
27079
|
const reportedEdges = /* @__PURE__ */ new Set();
|
|
26602
27080
|
const reportedWrites = /* @__PURE__ */ new Set();
|
|
27081
|
+
const reportedUnresolved = /* @__PURE__ */ new Set();
|
|
26603
27082
|
for (const fromPath of mappedPaths) {
|
|
26604
27083
|
const facts = code.factsAt(fromPath);
|
|
26605
27084
|
const fromComponents = realization.componentsAt(fromPath);
|
|
@@ -26612,6 +27091,23 @@ var dependencyConformanceRule = {
|
|
|
26612
27091
|
for (const site of sites) {
|
|
26613
27092
|
if (!site.member || pathKey(site.from ?? fromPath) !== fromPath) continue;
|
|
26614
27093
|
const landings = /* @__PURE__ */ new Set([...typedLandings(site, fromPath).map((l) => l.toPath), ...code.originOf(site, fromPath)]);
|
|
27094
|
+
if (landings.size === 0 && unfollowable(site, fromPath)) {
|
|
27095
|
+
const near = /* @__PURE__ */ new Set([portal.subsystem, ...portal.dependsOn.map((d) => ctx.componentMap.get(d)?.subsystem)]);
|
|
27096
|
+
const candidates = (dataWrites.get(site.name) ?? []).filter((w) => near.has(ctx.componentMap.get(w.component)?.subsystem) && !claims(portal.id, w.component, w.method));
|
|
27097
|
+
const at = `${fromPath} -> ${site.name}`;
|
|
27098
|
+
if (candidates.length > 0 && !reportedUnresolved.has(at)) {
|
|
27099
|
+
reportedUnresolved.add(at);
|
|
27100
|
+
ctx.addIssue(
|
|
27101
|
+
"notice",
|
|
27102
|
+
"PORTAL_CALL_UNRESOLVED",
|
|
27103
|
+
`Portal "${portal.id}": its method "${method.name}" in "${fromPath}" calls \`${site.via ?? (site.field ? `this.${site.field}` : "<receiver>")}.${site.name}(\u2026)\` through a receiver this analysis cannot follow (nothing in the file declares its type), and ${candidates.map((w) => `${w.component}.${w.method}`).join(", ")} \u2014 a data component's write \u2014 carries that name. Neither proven a persistence shortcut nor cleared: give the receiver a declared type (a field or parameter annotation) so the call can be judged.`,
|
|
27104
|
+
impl.id,
|
|
27105
|
+
draftAt(fromPath),
|
|
27106
|
+
void 0,
|
|
27107
|
+
{ at }
|
|
27108
|
+
);
|
|
27109
|
+
}
|
|
27110
|
+
}
|
|
26615
27111
|
for (const toPath of landings) {
|
|
26616
27112
|
if (toPath === fromPath) continue;
|
|
26617
27113
|
for (const { component: target, method: called } of calledAt(toPath, site.name)) {
|
|
@@ -26672,7 +27168,7 @@ var dependencyConformanceRule = {
|
|
|
26672
27168
|
const facade = ownerOf4.get(owner.id);
|
|
26673
27169
|
if (facade) members.add(facade.id);
|
|
26674
27170
|
for (const technology of technologies) {
|
|
26675
|
-
for (const token of
|
|
27171
|
+
for (const token of technologyPackages(technology, ctx.ext.technologyPackages)) {
|
|
26676
27172
|
const key2 = token.toLowerCase();
|
|
26677
27173
|
const entry = homes.get(key2) ?? { label: technologyName(technology), home: /* @__PURE__ */ new Set() };
|
|
26678
27174
|
for (const id of members) entry.home.add(id);
|
|
@@ -26740,7 +27236,7 @@ var portalImportsRule = {
|
|
|
26740
27236
|
}
|
|
26741
27237
|
return files;
|
|
26742
27238
|
};
|
|
26743
|
-
const draftAt = (
|
|
27239
|
+
const draftAt = (path50) => realization.implementationsAt(path50).some((impl) => ctx.isImplementationDraft(impl));
|
|
26744
27240
|
for (const fromPath of mappedPaths) {
|
|
26745
27241
|
const fromSubsystems = new Set(realization.componentsAt(fromPath).map((c) => c.subsystem));
|
|
26746
27242
|
for (const toPath of graph2.importsOf(fromPath)) {
|
|
@@ -26983,20 +27479,48 @@ function ownerOf2(type, file) {
|
|
|
26983
27479
|
var typeRealizationRule = {
|
|
26984
27480
|
name: "type-realization",
|
|
26985
27481
|
judges: "code",
|
|
26986
|
-
description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code. Until that claim has BEGUN (code_index.holdsAny over the type's own file and its methods' files) a named file that is not on disk is PLANNED (SOURCE_FILE_PLANNED, a notice; an error under rules.conformance.requireCode). Once it has begun, every file it names must resolve to a real, readable file inside the project root (MISSING_SOURCE_FILE, an error, otherwise), its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath claims nothing and is never
|
|
27482
|
+
description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code. Until that claim has BEGUN (code_index.holdsAny over the type's own file and its methods' files) a named file that is not on disk is PLANNED (SOURCE_FILE_PLANNED, a notice; an error under rules.conformance.requireCode). Once it has begun, every file it names must resolve to a real, readable file inside the project root (MISSING_SOURCE_FILE, an error, otherwise), its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath of its own claims no code, so nothing ever compares its shape with the code \u2014 and that is said, never left silent: before the code of the type's subsystem has begun (no file an implementation of that subsystem names exists; for a type no subsystem owns, no file of any implementation) it is unlinked design (MISSING_TYPE_SOURCE_PATH, a notice \u2014 plan the type's sourcePath with the implementations'), and once that code exists it is a type no check reads (a warning). rules.conformance.requireCode reports the notice at error, naming the setting. Findings carry the analysis grade, a file that escapes the root or could not be analyzed is reported once and blocks only what it would have realized, and types under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
26987
27483
|
codes: [
|
|
26988
27484
|
{ code: "UNREALIZED_TYPE", defaultSeverity: "warning", summary: "A type names a sourcePath but its declaration is nowhere in that file \u2014 the claim points at code that does not hold it" },
|
|
26989
27485
|
{ code: "UNREALIZED_TYPE_METHOD", defaultSeverity: "warning", summary: "A pure method of a claimed type is nowhere in its own source file (the method's sourcePath, else the type's)" },
|
|
26990
27486
|
{ code: "MISSING_SOURCE_FILE", defaultSeverity: "error", summary: "A source file a type or one of its methods names does not resolve to a file on disk although the type's realization has begun (another file it names exists)" },
|
|
26991
27487
|
{ code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file a type or one of its methods names is absolute or escapes the project root (containment refusal)" },
|
|
26992
27488
|
{ code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file a type or one of its methods names could not be analyzed (binary/unreadable) \u2014 what it would have realized was not checked" },
|
|
26993
|
-
{ code: "SOURCE_FILE_PLANNED", defaultSeverity: "notice", summary: "A source file a type names is not on disk and none of its named files is \u2014 planned, not written yet; an error under rules.conformance.requireCode" }
|
|
27489
|
+
{ code: "SOURCE_FILE_PLANNED", defaultSeverity: "notice", summary: "A source file a type names is not on disk and none of its named files is \u2014 planned, not written yet; an error under rules.conformance.requireCode" },
|
|
27490
|
+
{ code: "MISSING_TYPE_SOURCE_PATH", defaultSeverity: "notice", summary: "A type names no sourcePath of its own, so its shape is never compared with code \u2014 a notice while its subsystem has no code yet (plan the path with the implementations'), a warning once it has; an error under rules.conformance.requireCode" }
|
|
26994
27491
|
],
|
|
26995
27492
|
check(ctx) {
|
|
26996
27493
|
const code = ctx.codeIndex();
|
|
27494
|
+
const begunIn = /* @__PURE__ */ new Map();
|
|
27495
|
+
const codeBegunFor = (subsystem) => {
|
|
27496
|
+
const key2 = subsystem ?? "";
|
|
27497
|
+
let begun = begunIn.get(key2);
|
|
27498
|
+
if (begun === void 0) {
|
|
27499
|
+
begun = ctx.implementations.some((impl) => {
|
|
27500
|
+
if (subsystem !== void 0) {
|
|
27501
|
+
const owner = ctx.componentMap.get(ctx.interfaceMap.get(impl.contract)?.component ?? "");
|
|
27502
|
+
if (owner?.subsystem !== subsystem) return false;
|
|
27503
|
+
}
|
|
27504
|
+
return code.holdsAny(implementationSourceFiles(impl));
|
|
27505
|
+
});
|
|
27506
|
+
begunIn.set(key2, begun);
|
|
27507
|
+
}
|
|
27508
|
+
return begun;
|
|
27509
|
+
};
|
|
26997
27510
|
for (const type of ctx.types) {
|
|
26998
|
-
if (!type.sourcePath) continue;
|
|
26999
27511
|
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
27512
|
+
if (!type.sourcePath) {
|
|
27513
|
+
const begunHere = codeBegunFor(type.subsystem);
|
|
27514
|
+
const planned = plannedCode(ctx);
|
|
27515
|
+
const methods = typeSourceFiles(type).length > 0 ? " (only its methods name files)" : "";
|
|
27516
|
+
ctx.addIssue(
|
|
27517
|
+
begunHere ? "warning" : planned.severity,
|
|
27518
|
+
"MISSING_TYPE_SOURCE_PATH",
|
|
27519
|
+
begunHere ? `Type "${type.id}" names no sourcePath of its own${methods}, and the code of ${type.subsystem ? `subsystem "${type.subsystem}"` : "this project"} exists \u2014 so nothing compares its fields with the code: it is data no check reads. Name the file that declares it (sourcePath, and symbol when the code name differs).` : `Type "${type.id}" names no sourcePath of its own${methods} \u2014 designed, not linked to code yet, so its shape will never be compared with the code. Plan its sourcePath now, with the implementations': code linkage is not part of the approval, so it costs no re-lock${planned.note}.`,
|
|
27520
|
+
type.id
|
|
27521
|
+
);
|
|
27522
|
+
continue;
|
|
27523
|
+
}
|
|
27000
27524
|
const blocked3 = /* @__PURE__ */ new Set();
|
|
27001
27525
|
const begun = code.holdsAny(typeSourceFiles(type));
|
|
27002
27526
|
for (const file of typeSourceFiles(type)) {
|
|
@@ -28203,7 +28727,7 @@ function contractIdentifiers(m) {
|
|
|
28203
28727
|
var technologyRule = {
|
|
28204
28728
|
name: "technology-boundaries",
|
|
28205
28729
|
judges: "design",
|
|
28206
|
-
description: "Technology stays behind its owning boundary: an L4 that declares `technologies` (e.g. [mysql]) makes its component's ownership tree the technology's home. References outside that tree are leakage, and L3 contract identifiers must stay intent-language \u2014 the contract is the swap seam, so the vendor name is wrong even on the owning component's own interface. A technology is matched by its name, or \u2014 when it declares `matches` because its name is also an ordinary word of the tree (a package named after the file format it reads) \u2014 by those tokens alone.",
|
|
28730
|
+
description: "Technology stays behind its owning boundary: an L4 that declares `technologies` (e.g. [mysql]) makes its component's ownership tree the technology's home. References outside that tree are leakage, and L3 contract identifiers must stay intent-language \u2014 the contract is the swap seam, so the vendor name is wrong even on the owning component's own interface. A technology is matched by its name, or \u2014 when it declares `matches` because its name is also an ordinary word of the tree (a package named after the file format it reads) \u2014 by those tokens alone. Two declarations of one technology are one home whichever notation each is written in: a component binding it as a bare name and one binding it as `{name, matches}` are both inside it, even when one form's tokens police nothing.",
|
|
28207
28731
|
codes: [
|
|
28208
28732
|
{ code: "TECH_LEAKAGE", defaultSeverity: "warning", summary: "Technology referenced outside its owning boundary" },
|
|
28209
28733
|
{ code: "VENDOR_NAME_IN_CONTRACT", defaultSeverity: "warning", summary: "Technology name in L3 contract identifiers" }
|
|
@@ -28268,7 +28792,6 @@ var technologyRule = {
|
|
|
28268
28792
|
const homes = /* @__PURE__ */ new Map();
|
|
28269
28793
|
for (const { comp, name, tokens } of declarations) {
|
|
28270
28794
|
const matchers = tokens.map(makeMatcher).filter((m) => m !== null);
|
|
28271
|
-
if (!matchers.length) continue;
|
|
28272
28795
|
const key2 = name.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).join("");
|
|
28273
28796
|
const home = homes.get(key2) ?? newHome(name);
|
|
28274
28797
|
home.matchers.push(...matchers);
|
|
@@ -28276,6 +28799,7 @@ var technologyRule = {
|
|
|
28276
28799
|
for (const id of scopeOf2(comp)) home.scope.add(id);
|
|
28277
28800
|
homes.set(key2, home);
|
|
28278
28801
|
}
|
|
28802
|
+
for (const [key2, home] of homes) if (home.matchers.length === 0) homes.delete(key2);
|
|
28279
28803
|
if (homes.size === 0) return;
|
|
28280
28804
|
const ownersDesc = (h) => [...h.ownerComponents].map((c) => `"${c}"`).join(", ");
|
|
28281
28805
|
for (const home of homes.values()) {
|
|
@@ -28421,10 +28945,25 @@ function getBaseId(id) {
|
|
|
28421
28945
|
return parts[parts.length - 1];
|
|
28422
28946
|
}
|
|
28423
28947
|
var isAllUppercase = (s) => /^[A-Z0-9_]+$/.test(s);
|
|
28948
|
+
function implementedNames(ctx, ref) {
|
|
28949
|
+
if (ref === void 0) return /* @__PURE__ */ new Set();
|
|
28950
|
+
const surface = ctx.resolveSurfaceRef(ref);
|
|
28951
|
+
if (surface.kind === "resolved") {
|
|
28952
|
+
return surface.entry.role === "implement" ? new Set(surface.entry.methods.map((m) => m.name)) : /* @__PURE__ */ new Set();
|
|
28953
|
+
}
|
|
28954
|
+
const [alias, name] = ref.split("::");
|
|
28955
|
+
if (name === void 0) return /* @__PURE__ */ new Set();
|
|
28956
|
+
const key2 = ctx.projectFamily?.nodes.find((n) => n.namespace === "")?.aliases.get(alias);
|
|
28957
|
+
if (key2 === void 0) return /* @__PURE__ */ new Set();
|
|
28958
|
+
const entry = (ctx.exportTables ?? []).find((t) => t.level === "project" && t.owner === key2)?.entries.find((e) => e.kind === "component" && e.publicName === name);
|
|
28959
|
+
if (!entry || entry.role !== "implement" || !entry.component) return /* @__PURE__ */ new Set();
|
|
28960
|
+
const methods = entry.interface ? ctx.interfaceMap.get(entry.interface)?.methods : ctx.interfaceMethodsOf(entry.component);
|
|
28961
|
+
return new Set((methods ?? []).map((m) => m.name));
|
|
28962
|
+
}
|
|
28424
28963
|
var namingRule = {
|
|
28425
28964
|
name: "naming-conventions",
|
|
28426
28965
|
judges: "design",
|
|
28427
|
-
description: "Enforces naming conventions (casing styles or regular expressions) for subsystem, component, interface, type (differentiating entities and value-objects), method, variables/parameters, fields, and constants names/IDs, plus stereotype-specific naming patterns. A method name is judged by naming_rule_config.methodCasingFor the subsystem's effective targetLanguage: the configured methods casing when set, else the language's own convention, so a Rust tree is snake_case without configuring anything, and the authoring and rename tools accept exactly what this rule accepts.",
|
|
28966
|
+
description: "Enforces naming conventions (casing styles or regular expressions) for subsystem, component, interface, type (differentiating entities and value-objects), method, variables/parameters, fields, and constants names/IDs, plus stereotype-specific naming patterns. A method name is judged by naming_rule_config.methodCasingFor the subsystem's effective targetLanguage: the configured methods casing when set, else the language's own convention, so a Rust tree is snake_case without configuring anything, and the authoring and rename tools accept exactly what this rule accepts. A contract that implements another project's extension point (`implements`) takes the names of the methods it implements \u2014 and of their parameters \u2014 from the producer, whose language they are spelled in: those methods, on the contract and on its implementations, are exempt from the casing rule, and the rename tools accept them.",
|
|
28428
28967
|
codes: [
|
|
28429
28968
|
{ code: "NAMING_CONVENTION_VIOLATION", defaultSeverity: "warning", summary: "Item name or ID does not match the configured casing pattern or regex" },
|
|
28430
28969
|
{ code: "STEREOTYPE_NAMING_VIOLATION", defaultSeverity: "warning", summary: "Component name or ID does not match stereotype suffix/prefix/regex rules" },
|
|
@@ -28508,7 +29047,9 @@ var namingRule = {
|
|
|
28508
29047
|
checkNamedValue(ctx, intf.name, namingConfig.interfaces, `Interface Name "${intf.name}" does not match naming convention "${namingConfig.interfaces}".`, intf.id, isDraft);
|
|
28509
29048
|
}
|
|
28510
29049
|
const methodCasing = methodCasingFor(namingConfig, ctx.targetLanguageFor(comp?.subsystem));
|
|
29050
|
+
const dictated = implementedNames(ctx, intf.implements);
|
|
28511
29051
|
for (const m of intf.methods) {
|
|
29052
|
+
if (dictated.has(m.name)) continue;
|
|
28512
29053
|
checkNamedValue(ctx, m.name, methodCasing, `Interface method "${m.name}" on "${intf.id}" does not match naming convention "${methodCasing}"${namingConfig?.methods ? "" : " (its target language's convention)"}.`, intf.id, isDraft);
|
|
28513
29054
|
if (namingConfig?.variables) {
|
|
28514
29055
|
for (const param of m.params ?? []) {
|
|
@@ -28523,7 +29064,9 @@ var namingRule = {
|
|
|
28523
29064
|
const namingConfig = ctx.namingConfigFor(comp?.subsystem);
|
|
28524
29065
|
const isDraft = ctx.isImplementationDraft(impl);
|
|
28525
29066
|
const methodCasing = methodCasingFor(namingConfig, ctx.targetLanguageFor(comp?.subsystem));
|
|
29067
|
+
const dictated = implementedNames(ctx, intf?.implements);
|
|
28526
29068
|
for (const m of impl.methods) {
|
|
29069
|
+
if (dictated.has(m.name)) continue;
|
|
28527
29070
|
checkNamedValue(ctx, m.name, methodCasing, `Implementation method "${m.name}" on "${impl.id}" does not match naming convention "${methodCasing}".`, impl.id, isDraft);
|
|
28528
29071
|
}
|
|
28529
29072
|
}
|
|
@@ -29502,7 +30045,8 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29502
30045
|
const fieldTypes = /* @__PURE__ */ new Map();
|
|
29503
30046
|
const localTypes = /* @__PURE__ */ new Map();
|
|
29504
30047
|
const memberTypes = /* @__PURE__ */ new Map();
|
|
29505
|
-
const
|
|
30048
|
+
const aliasTargets2 = /* @__PURE__ */ new Map();
|
|
30049
|
+
const implementsClauses = /* @__PURE__ */ new Map();
|
|
29506
30050
|
const typeShapes = /* @__PURE__ */ new Map();
|
|
29507
30051
|
const functionParams = /* @__PURE__ */ new Map();
|
|
29508
30052
|
const functionRoutes = /* @__PURE__ */ new Map();
|
|
@@ -29561,7 +30105,13 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29561
30105
|
const typeReferenceName = (type) => {
|
|
29562
30106
|
let at = type;
|
|
29563
30107
|
for (let depth = 0; at && depth < 4; depth++) {
|
|
29564
|
-
|
|
30108
|
+
while (at && ts.isParenthesizedTypeNode(at)) at = at.type;
|
|
30109
|
+
if (at && ts.isUnionTypeNode(at)) {
|
|
30110
|
+
const present = at.types.filter((t) => !(t.kind === ts.SyntaxKind.UndefinedKeyword || t.kind === ts.SyntaxKind.NullKeyword || ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
|
|
30111
|
+
if (present.length !== 1) return void 0;
|
|
30112
|
+
at = present[0];
|
|
30113
|
+
}
|
|
30114
|
+
if (!at || !ts.isTypeReferenceNode(at) || !ts.isIdentifier(at.typeName)) return void 0;
|
|
29565
30115
|
const name = at.typeName.text;
|
|
29566
30116
|
if (!MEMBER_PRESERVING.has(name) || !at.typeArguments?.length) return name;
|
|
29567
30117
|
at = at.typeArguments[0];
|
|
@@ -29756,6 +30306,29 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29756
30306
|
const addSite = (site) => {
|
|
29757
30307
|
callees.set(siteKey(site), site);
|
|
29758
30308
|
};
|
|
30309
|
+
const namedCast = (type) => ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName);
|
|
30310
|
+
const unwrapped = (expression) => {
|
|
30311
|
+
let at = expression;
|
|
30312
|
+
while (ts.isNonNullExpression(at) || ts.isParenthesizedExpression(at) || typeof ts.isSatisfiesExpression === "function" && ts.isSatisfiesExpression(at) || (ts.isAsExpression(at) || ts.isTypeAssertionExpression(at)) && namedCast(at.type)) at = at.expression;
|
|
30313
|
+
return at;
|
|
30314
|
+
};
|
|
30315
|
+
const fieldAliases = /* @__PURE__ */ new Map();
|
|
30316
|
+
const collectAliases = (node) => {
|
|
30317
|
+
if (ts.isVariableDeclaration(node) && node.initializer) {
|
|
30318
|
+
const init = unwrapped(node.initializer);
|
|
30319
|
+
if (ts.isIdentifier(node.name) && ts.isPropertyAccessExpression(init) && unwrapped(init.expression).kind === ts.SyntaxKind.ThisKeyword && ts.isIdentifier(init.name)) {
|
|
30320
|
+
fieldAliases.set(node.name.text, init.name.text);
|
|
30321
|
+
} else if (ts.isObjectBindingPattern(node.name) && init.kind === ts.SyntaxKind.ThisKeyword) {
|
|
30322
|
+
for (const element of node.name.elements) {
|
|
30323
|
+
if (element.dotDotDotToken || !ts.isIdentifier(element.name)) continue;
|
|
30324
|
+
const property = element.propertyName ? propertyNameText(element.propertyName) : element.name.text;
|
|
30325
|
+
if (property) fieldAliases.set(element.name.text, property);
|
|
30326
|
+
}
|
|
30327
|
+
}
|
|
30328
|
+
}
|
|
30329
|
+
ts.forEachChild(node, collectAliases);
|
|
30330
|
+
};
|
|
30331
|
+
if (fn.body) collectAliases(fn.body);
|
|
29759
30332
|
const count = (node) => {
|
|
29760
30333
|
if (namedFunctionName(node) !== void 0) return;
|
|
29761
30334
|
if (ts.isIfStatement(node) || ts.isConditionalExpression(node) || ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node) || ts.isCaseClause(node) || ts.isCatchClause(node)) {
|
|
@@ -29769,10 +30342,12 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29769
30342
|
const callee = node.expression;
|
|
29770
30343
|
if (ts.isIdentifier(callee)) addSite({ name: callee.text, member: false });
|
|
29771
30344
|
else if (ts.isPropertyAccessExpression(callee)) {
|
|
29772
|
-
const receiver = callee.expression;
|
|
30345
|
+
const receiver = unwrapped(callee.expression);
|
|
29773
30346
|
const name = callee.name.text;
|
|
29774
|
-
if (ts.isIdentifier(receiver)
|
|
29775
|
-
|
|
30347
|
+
if (ts.isIdentifier(receiver) && fieldAliases.has(receiver.text)) {
|
|
30348
|
+
addSite({ name, member: true, field: fieldAliases.get(receiver.text) });
|
|
30349
|
+
} else if (ts.isIdentifier(receiver)) addSite({ name, member: true, via: receiver.text });
|
|
30350
|
+
else if (ts.isPropertyAccessExpression(receiver) && unwrapped(receiver.expression).kind === ts.SyntaxKind.ThisKeyword) {
|
|
29776
30351
|
addSite({ name, member: true, field: receiver.name.text });
|
|
29777
30352
|
} else if (ts.isNewExpression(receiver) && ts.isIdentifier(receiver.expression)) {
|
|
29778
30353
|
addSite({ name, member: true, constructed: receiver.expression.text });
|
|
@@ -29786,7 +30361,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29786
30361
|
let at = receiver;
|
|
29787
30362
|
while (ts.isPropertyAccessExpression(at) && !at.questionDotToken && ts.isIdentifier(at.name)) {
|
|
29788
30363
|
chain.unshift(at.name.text);
|
|
29789
|
-
at = at.expression;
|
|
30364
|
+
at = unwrapped(at.expression);
|
|
29790
30365
|
}
|
|
29791
30366
|
if (ts.isIdentifier(at)) chain.unshift(at.text);
|
|
29792
30367
|
else if (at.kind === ts.SyntaxKind.ThisKeyword) chain.unshift("this");
|
|
@@ -29944,13 +30519,15 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
29944
30519
|
(clause) => clause.token === ts.SyntaxKind.ExtendsKeyword
|
|
29945
30520
|
);
|
|
29946
30521
|
recordDeclaredShape(node.name.text, node.members, extendsOther);
|
|
30522
|
+
const implemented = (node.heritageClauses ?? []).filter((clause) => clause.token === ts.SyntaxKind.ImplementsKeyword).flatMap((clause) => clause.types.map((t) => ts.isIdentifier(t.expression) ? t.expression.text : void 0)).filter((name) => name !== void 0);
|
|
30523
|
+
if (implemented.length > 0 && !implementsClauses.has(node.name.text)) implementsClauses.set(node.name.text, implemented);
|
|
29947
30524
|
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
29948
30525
|
if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
|
|
29949
30526
|
else {
|
|
29950
30527
|
if (!aliasTypes.has(node.name.text)) aliasTypes.set(node.name.text, node.type.getText(sf));
|
|
29951
30528
|
const named2 = typeReferenceName(node.type);
|
|
29952
|
-
if (named2 !== void 0 && named2 !== node.name.text && !
|
|
29953
|
-
|
|
30529
|
+
if (named2 !== void 0 && named2 !== node.name.text && !aliasTargets2.has(node.name.text)) {
|
|
30530
|
+
aliasTargets2.set(node.name.text, named2);
|
|
29954
30531
|
}
|
|
29955
30532
|
const constant = inferredSchemaConstant(node.type);
|
|
29956
30533
|
if (constant) derivedAliases.push({ name: node.name.text, constant });
|
|
@@ -30117,7 +30694,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
30117
30694
|
importBindings.set(name, fact);
|
|
30118
30695
|
}
|
|
30119
30696
|
for (const alias of propertyAliases) if (exported.has(alias.container)) exportAliases.push(alias);
|
|
30120
|
-
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, exportAliases, returnTypes, complexity, calls, bodies, importBindings, typeOnlyBindings, fieldTypes, localTypes, memberTypes, aliasTargets, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
|
|
30697
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, exportAliases, returnTypes, complexity, calls, bodies, importBindings, typeOnlyBindings, fieldTypes, localTypes, memberTypes, aliasTargets: aliasTargets2, implementsClauses, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
|
|
30121
30698
|
}
|
|
30122
30699
|
var NO_PACKAGES = /* @__PURE__ */ new Map();
|
|
30123
30700
|
function resolveModule(fromFile, specifier, packages) {
|
|
@@ -30491,6 +31068,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
30491
31068
|
localTypes: Object.fromEntries([...facts.localTypes].map(([k, v]) => [k, [...v]])),
|
|
30492
31069
|
memberTypes: Object.fromEntries([...facts.memberTypes].map(([k, v]) => [k, [...v]])),
|
|
30493
31070
|
aliasTargets: Object.fromEntries(facts.aliasTargets),
|
|
31071
|
+
...facts.implementsClauses.size > 0 ? { implementsClauses: Object.fromEntries(facts.implementsClauses) } : {},
|
|
30494
31072
|
typeShapes: Object.fromEntries(facts.typeShapes),
|
|
30495
31073
|
functionParams: Object.fromEntries(facts.functionParams),
|
|
30496
31074
|
functionRoutes: Object.fromEntries([...facts.functionRoutes].map(([k, v]) => [k, [...v.values()]])),
|
|
@@ -30674,6 +31252,8 @@ var DEPTH_GATED_CODES = {
|
|
|
30674
31252
|
// L4 expectations: implementations and their code linkage.
|
|
30675
31253
|
MISSING_IMPLEMENTATION_METHOD: "implementations",
|
|
30676
31254
|
MISSING_SOURCE_PATH: "implementations",
|
|
31255
|
+
MISSING_TYPE_SOURCE_PATH: "implementations",
|
|
31256
|
+
PORTAL_CALL_UNRESOLVED: "implementations",
|
|
30677
31257
|
METHOD_SOURCE_PATH_MISSING: "implementations",
|
|
30678
31258
|
SOURCE_FILE_PLANNED: "implementations",
|
|
30679
31259
|
PORTAL_AUTH_UNMET: "implementations",
|
|
@@ -30715,6 +31295,7 @@ var COMPLETENESS_RULES = /* @__PURE__ */ new Set([
|
|
|
30715
31295
|
// Structural conformance: a draft tree is allowed to name code that does
|
|
30716
31296
|
// not exist yet — the findings gate only once the specs claim completeness.
|
|
30717
31297
|
"MISSING_SOURCE_PATH",
|
|
31298
|
+
"MISSING_TYPE_SOURCE_PATH",
|
|
30718
31299
|
"METHOD_SOURCE_PATH_MISSING",
|
|
30719
31300
|
"MISSING_SOURCE_FILE",
|
|
30720
31301
|
"SOURCE_FILE_PLANNED",
|
|
@@ -31342,7 +31923,7 @@ function buildRuleContext(opts) {
|
|
|
31342
31923
|
documentationConfigFor,
|
|
31343
31924
|
namingConfigFor,
|
|
31344
31925
|
isBuiltinType,
|
|
31345
|
-
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [], deprecations: extensions.deprecations ?? [] },
|
|
31926
|
+
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, technologyPackages: extensions.technologyPackages ?? {}, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [], deprecations: extensions.deprecations ?? [] },
|
|
31346
31927
|
variants: opts.variants ?? [],
|
|
31347
31928
|
surfaceSnapshots,
|
|
31348
31929
|
...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
|
|
@@ -32905,11 +33486,22 @@ function crossReferences(family, specs) {
|
|
|
32905
33486
|
for (const ref of family.authoredReferences) {
|
|
32906
33487
|
if (ref.position === "type") addRaw(ref, "type");
|
|
32907
33488
|
else if (ref.position === "auth") addRaw(ref, "auth");
|
|
32908
|
-
else if (ref.position === "implements")
|
|
32909
|
-
|
|
33489
|
+
else if (ref.position === "implements") {
|
|
33490
|
+
for (const method of implementedMethods(ref.specId, ref.authored)) addRaw(ref, "implements", method);
|
|
33491
|
+
} else if (ref.position === "calls") addRaw(ref, "calls", calledMethod.get(`${ref.specId}|${ref.authored}`), callerOf2.get(ref.specId));
|
|
32910
33492
|
}
|
|
32911
33493
|
return out;
|
|
32912
33494
|
}
|
|
33495
|
+
function implementedMethods(interfaceId, implemented) {
|
|
33496
|
+
const intf = loadInterfaceSpecs().find((i) => i.id === interfaceId);
|
|
33497
|
+
const out = /* @__PURE__ */ new Set();
|
|
33498
|
+
for (const method of intf?.methods ?? []) {
|
|
33499
|
+
const source = method.signatureFrom;
|
|
33500
|
+
const dot = source?.lastIndexOf(".") ?? -1;
|
|
33501
|
+
out.add(source !== void 0 && dot > 0 && source.slice(0, dot) === implemented ? source.slice(dot + 1) : method.name);
|
|
33502
|
+
}
|
|
33503
|
+
return [...out];
|
|
33504
|
+
}
|
|
32913
33505
|
function projectFamilyGraph() {
|
|
32914
33506
|
const roots = listProjectRoots();
|
|
32915
33507
|
const cached = memo.get(roots);
|
|
@@ -32943,9 +33535,12 @@ function projectFamilyGraph() {
|
|
|
32943
33535
|
// src/core/exports.ts
|
|
32944
33536
|
var CONSUMABLE = /* @__PURE__ */ new Set(["Portal", "Observer"]);
|
|
32945
33537
|
var IMPLEMENTABLE = /* @__PURE__ */ new Set(["Portal", "Adapter"]);
|
|
32946
|
-
function exportKindOf(comp, authored) {
|
|
33538
|
+
function exportKindOf(comp, authored, role) {
|
|
32947
33539
|
if (comp.componentType === "Portal" && comp.transport === "InProcess") return "InProcess";
|
|
32948
33540
|
if (comp.componentType === "Portal" && comp.transport) return transportExportKind(comp.transport);
|
|
33541
|
+
if (role === "implement" && comp.componentType === "Adapter") {
|
|
33542
|
+
return !comp.transport || comp.transport === "InProcess" ? "InProcess" : transportExportKind(comp.transport);
|
|
33543
|
+
}
|
|
32949
33544
|
return authored;
|
|
32950
33545
|
}
|
|
32951
33546
|
function localName2(id) {
|
|
@@ -33117,7 +33712,7 @@ function bindOwn(world, sub, pi, problems) {
|
|
|
33117
33712
|
component: comp.id,
|
|
33118
33713
|
...narrowed2 ? { interface: narrowed2 } : {},
|
|
33119
33714
|
componentType: comp.componentType,
|
|
33120
|
-
...exportKindOf(comp, pi.type) ? { type: exportKindOf(comp, pi.type) } : {},
|
|
33715
|
+
...exportKindOf(comp, pi.type, pi.role) ? { type: exportKindOf(comp, pi.type, pi.role) } : {},
|
|
33121
33716
|
...pi.details !== void 0 ? { details: pi.details } : {},
|
|
33122
33717
|
...pi.role ? { role: pi.role } : {},
|
|
33123
33718
|
via: []
|
|
@@ -33470,7 +34065,8 @@ function exportUsageOf(consumer, producer) {
|
|
|
33470
34065
|
unexported.push(ref);
|
|
33471
34066
|
continue;
|
|
33472
34067
|
}
|
|
33473
|
-
const slot = used.get(entry.publicName) ?? { kind: entry.kind, members: /* @__PURE__ */ new Set() };
|
|
34068
|
+
const slot = used.get(entry.publicName) ?? { kind: entry.kind, members: /* @__PURE__ */ new Set(), specs: /* @__PURE__ */ new Set() };
|
|
34069
|
+
slot.specs.add(ref.specId);
|
|
33474
34070
|
if (entry.kind === "type") slot.members.add("type");
|
|
33475
34071
|
else if (ref.member !== void 0) slot.members.add(ref.member);
|
|
33476
34072
|
used.set(entry.publicName, slot);
|
|
@@ -33478,7 +34074,7 @@ function exportUsageOf(consumer, producer) {
|
|
|
33478
34074
|
return {
|
|
33479
34075
|
consumer,
|
|
33480
34076
|
producer,
|
|
33481
|
-
used: [...used.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([publicName, u]) => ({ publicName, kind: u.kind, members: [...u.members].sort() })),
|
|
34077
|
+
used: [...used.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([publicName, u]) => ({ publicName, kind: u.kind, members: [...u.members].sort(), specs: [...u.specs].sort() })),
|
|
33482
34078
|
unexported
|
|
33483
34079
|
};
|
|
33484
34080
|
}
|
|
@@ -33488,6 +34084,7 @@ function importedName(authored, use) {
|
|
|
33488
34084
|
}
|
|
33489
34085
|
function membersAt(ref) {
|
|
33490
34086
|
if (ref.position === "type") return ["type"];
|
|
34087
|
+
if (ref.position === "implements") return implementedMethods(ref.specId, ref.authored);
|
|
33491
34088
|
if (ref.position === "narrative" || ref.position === "calls") {
|
|
33492
34089
|
const impl = loadImplementationSpecs().find((i) => i.id === ref.specId);
|
|
33493
34090
|
const out = [];
|
|
@@ -33520,14 +34117,15 @@ function pinnedUsageOf(consumer, alias) {
|
|
|
33520
34117
|
const used = /* @__PURE__ */ new Map();
|
|
33521
34118
|
for (const ref of through) {
|
|
33522
34119
|
const publicName = ref.form === "import" ? importedName(ref.authored, use) : ref.authored.slice(alias.length + 2);
|
|
33523
|
-
const slot = used.get(publicName) ?? { kind: ref.position === "type" ? "type" : "component", members: /* @__PURE__ */ new Set() };
|
|
34120
|
+
const slot = used.get(publicName) ?? { kind: ref.position === "type" ? "type" : "component", members: /* @__PURE__ */ new Set(), specs: /* @__PURE__ */ new Set() };
|
|
34121
|
+
slot.specs.add(ref.specId);
|
|
33524
34122
|
for (const member of membersAt(ref)) slot.members.add(member);
|
|
33525
34123
|
used.set(publicName, slot);
|
|
33526
34124
|
}
|
|
33527
34125
|
return {
|
|
33528
34126
|
consumer,
|
|
33529
34127
|
producer: alias,
|
|
33530
|
-
used: [...used.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([publicName, u]) => ({ publicName, kind: u.kind, members: [...u.members].sort() })),
|
|
34128
|
+
used: [...used.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([publicName, u]) => ({ publicName, kind: u.kind, members: [...u.members].sort(), specs: [...u.specs].sort() })),
|
|
33531
34129
|
unexported: []
|
|
33532
34130
|
};
|
|
33533
34131
|
}
|
|
@@ -33697,7 +34295,8 @@ var UNKNOWN_KEY_HINTS = {
|
|
|
33697
34295
|
"system.exports": "The L0 export table is `publicInterfaces` (sdd_set_public_interfaces writes it).",
|
|
33698
34296
|
"system.export": "The L0 export table is `publicInterfaces` (sdd_set_public_interfaces writes it).",
|
|
33699
34297
|
"subsystem.exports": "A subsystem publishes through `publicInterfaces`.",
|
|
33700
|
-
"component.type": "A component's stereotype is `componentType`."
|
|
34298
|
+
"component.type": "A component's stereotype is `componentType`.",
|
|
34299
|
+
"implementation.symbol": "A symbol names the code realizing ONE contract method: set it on that method (`methods[].symbol`)."
|
|
33701
34300
|
};
|
|
33702
34301
|
function unknownKeysIn(raw, parsed, at, out) {
|
|
33703
34302
|
if (Array.isArray(raw)) {
|
|
@@ -34488,7 +35087,7 @@ function resolveFamilyTables(raws) {
|
|
|
34488
35087
|
const byKey3 = new Map(raws.map((r) => [r.record.namespace, r]));
|
|
34489
35088
|
const done = /* @__PURE__ */ new Set();
|
|
34490
35089
|
const resolving = /* @__PURE__ */ new Set();
|
|
34491
|
-
const
|
|
35090
|
+
const resolve42 = (raw) => {
|
|
34492
35091
|
const key2 = raw.record.namespace;
|
|
34493
35092
|
if (done.has(key2)) return raw.record.exports;
|
|
34494
35093
|
if (resolving.has(key2)) return null;
|
|
@@ -34503,7 +35102,7 @@ function resolveFamilyTables(raws) {
|
|
|
34503
35102
|
const producerKey = source !== void 0 ? raw.record.aliases.get(source) : void 0;
|
|
34504
35103
|
if (producerKey === void 0 || producerTables.has(producerKey)) continue;
|
|
34505
35104
|
const producer = byKey3.get(producerKey);
|
|
34506
|
-
producerTables.set(producerKey, producer ?
|
|
35105
|
+
producerTables.set(producerKey, producer ? resolve42(producer) : null);
|
|
34507
35106
|
audienceOf.set(producerKey, "project");
|
|
34508
35107
|
}
|
|
34509
35108
|
raw.record.subsystemExports = subsystemTables;
|
|
@@ -34524,7 +35123,7 @@ function resolveFamilyTables(raws) {
|
|
|
34524
35123
|
resolving.delete(key2);
|
|
34525
35124
|
}
|
|
34526
35125
|
};
|
|
34527
|
-
raws.forEach((raw) =>
|
|
35126
|
+
raws.forEach((raw) => resolve42(raw));
|
|
34528
35127
|
}
|
|
34529
35128
|
function bindAuthored(raw, raws, readables, authored) {
|
|
34530
35129
|
const key2 = raw.record.namespace;
|
|
@@ -35232,13 +35831,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
|
|
|
35232
35831
|
const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
35233
35832
|
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
35234
35833
|
const at = (prefix, key2) => prefix ? `${prefix}.${key2}` : key2;
|
|
35235
|
-
const say = (
|
|
35236
|
-
out.push(`${
|
|
35834
|
+
const say = (path50, why2) => {
|
|
35835
|
+
out.push(`${path50} \u2014 ${why2}`);
|
|
35237
35836
|
};
|
|
35238
|
-
const walkValue = (d, m, s,
|
|
35837
|
+
const walkValue = (d, m, s, path50, field) => {
|
|
35239
35838
|
if (Array.isArray(d)) {
|
|
35240
35839
|
if (!Array.isArray(m)) {
|
|
35241
|
-
say(
|
|
35840
|
+
say(path50, "the level does not have this field, so the write dropped it");
|
|
35242
35841
|
return;
|
|
35243
35842
|
}
|
|
35244
35843
|
if (field === "narrative" && d.some(carriesEditMarker)) return;
|
|
@@ -35246,32 +35845,32 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
|
|
|
35246
35845
|
if (carriesEditMarker(element)) continue;
|
|
35247
35846
|
const key2 = reportIdentityOf(field, element);
|
|
35248
35847
|
if (key2 === null) {
|
|
35249
|
-
if (!same(m, d)) say(
|
|
35250
|
-
else if (same(s, d)) say(
|
|
35848
|
+
if (!same(m, d)) say(path50, "the level does not keep this value, so the write dropped it");
|
|
35849
|
+
else if (same(s, d)) say(path50, "the stored spec already held this value");
|
|
35251
35850
|
return;
|
|
35252
35851
|
}
|
|
35253
35852
|
const mMatch = m.find((i) => reportIdentityOf(field, i) === key2);
|
|
35254
35853
|
const sMatch = Array.isArray(s) ? s.find((i) => reportIdentityOf(field, i) === key2) : void 0;
|
|
35255
35854
|
if (mMatch === void 0) {
|
|
35256
|
-
say(at(
|
|
35855
|
+
say(at(path50, key2), "the write did not store this element");
|
|
35257
35856
|
continue;
|
|
35258
35857
|
}
|
|
35259
|
-
walk2(element, mMatch, sMatch, at(
|
|
35858
|
+
walk2(element, mMatch, sMatch, at(path50, key2), field);
|
|
35260
35859
|
}
|
|
35261
35860
|
return;
|
|
35262
35861
|
}
|
|
35263
35862
|
if (isPlain(d)) {
|
|
35264
35863
|
if (!isPlain(m)) {
|
|
35265
|
-
say(
|
|
35864
|
+
say(path50, "the level does not have this field, so the write dropped it");
|
|
35266
35865
|
return;
|
|
35267
35866
|
}
|
|
35268
|
-
walk2(d, m, s,
|
|
35867
|
+
walk2(d, m, s, path50, field);
|
|
35269
35868
|
return;
|
|
35270
35869
|
}
|
|
35271
|
-
if (!same(m, d)) say(
|
|
35272
|
-
else if (same(s, d)) say(
|
|
35870
|
+
if (!same(m, d)) say(path50, "the write did not store it \u2014 the level's schema does not keep this value");
|
|
35871
|
+
else if (same(s, d)) say(path50, "the stored spec already held this value");
|
|
35273
35872
|
};
|
|
35274
|
-
function walk2(d, m, s,
|
|
35873
|
+
function walk2(d, m, s, path50, container) {
|
|
35275
35874
|
for (const [key2, value] of Object.entries(d)) {
|
|
35276
35875
|
if (DELTA_VERBS.has(key2) || JUMP_LABEL_TWINS.has(key2)) continue;
|
|
35277
35876
|
if (key2 === "label" && LABEL_TWIN_CONTAINERS.has(container)) continue;
|
|
@@ -35281,13 +35880,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
|
|
|
35281
35880
|
if (typeof name !== "string") continue;
|
|
35282
35881
|
const had = isPlain(s) && s[name] !== void 0;
|
|
35283
35882
|
const still = isPlain(m) && m[name] !== void 0;
|
|
35284
|
-
if (!had) say(at(
|
|
35285
|
-
else if (still) say(at(
|
|
35883
|
+
if (!had) say(at(path50, name), "unset named a field the stored spec did not have");
|
|
35884
|
+
else if (still) say(at(path50, name), "unset did not remove it");
|
|
35286
35885
|
}
|
|
35287
35886
|
continue;
|
|
35288
35887
|
}
|
|
35289
35888
|
if (value === void 0 || value === null) continue;
|
|
35290
|
-
const here = at(
|
|
35889
|
+
const here = at(path50, key2);
|
|
35291
35890
|
const mValue = isPlain(m) ? m[key2] : void 0;
|
|
35292
35891
|
const sValue = isPlain(s) ? s[key2] : void 0;
|
|
35293
35892
|
if (mValue === void 0) {
|
|
@@ -36545,12 +37144,12 @@ var SpecWorkspace = class {
|
|
|
36545
37144
|
return this.relativizeSpec("interface", stored);
|
|
36546
37145
|
}
|
|
36547
37146
|
prepareImplementationForWrite(spec, carry = true) {
|
|
36548
|
-
const
|
|
37147
|
+
const relative22 = this.relativizeSpec("implementation", spec, carry);
|
|
36549
37148
|
const member = this.getSubprojectPrefix(spec.id);
|
|
36550
37149
|
const memberRoot = member ? this.resolveSubprojectForNamespace(member) : null;
|
|
36551
|
-
if (memberRoot) return childRelativeFilePaths(
|
|
37150
|
+
if (memberRoot) return childRelativeFilePaths(relative22, this.rootDir, memberRoot);
|
|
36552
37151
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
36553
|
-
return partDir ? partRelativeFilePaths(
|
|
37152
|
+
return partDir ? partRelativeFilePaths(relative22, this.rootDir, partDir) : relative22;
|
|
36554
37153
|
}
|
|
36555
37154
|
/**
|
|
36556
37155
|
* A type written in its stored form — every type position canonical (one
|
|
@@ -37582,7 +38181,7 @@ var SpecWorkspace = class {
|
|
|
37582
38181
|
out[DELTA_JUMP_PINS] = pins;
|
|
37583
38182
|
return out;
|
|
37584
38183
|
};
|
|
37585
|
-
const
|
|
38184
|
+
const unpinned2 = (step) => {
|
|
37586
38185
|
const out = { ...step };
|
|
37587
38186
|
delete out[DELTA_JUMP_PINS];
|
|
37588
38187
|
for (const lf of JUMP_LIST_FIELDS) {
|
|
@@ -37792,7 +38391,7 @@ var SpecWorkspace = class {
|
|
|
37792
38391
|
}
|
|
37793
38392
|
}
|
|
37794
38393
|
}
|
|
37795
|
-
return steps.map(
|
|
38394
|
+
return steps.map(unpinned2).sort((a, b) => a.stepNumber - b.stepNumber);
|
|
37796
38395
|
};
|
|
37797
38396
|
const isPlainObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
37798
38397
|
const mergeExt = (existing, delta2) => {
|
|
@@ -39111,6 +39710,7 @@ function unregisterDomain(id) {
|
|
|
39111
39710
|
}
|
|
39112
39711
|
|
|
39113
39712
|
// src/core/external-producers.ts
|
|
39713
|
+
var fs16 = __toESM(require("fs"));
|
|
39114
39714
|
var path23 = __toESM(require("path"));
|
|
39115
39715
|
function hostedOnlyProducer(id) {
|
|
39116
39716
|
return `hosted-only producer \`${id}\`: available only through the hosted server`;
|
|
@@ -39224,9 +39824,15 @@ function resolveCandidate(alias, declaration2) {
|
|
|
39224
39824
|
reachable: whole
|
|
39225
39825
|
};
|
|
39226
39826
|
}
|
|
39227
|
-
function listConsumers() {
|
|
39827
|
+
function listConsumers(search) {
|
|
39228
39828
|
const bound2 = path23.resolve(getProjectRoot());
|
|
39229
39829
|
const { top } = climb(bound2);
|
|
39830
|
+
const family = familyConsumers(bound2, top);
|
|
39831
|
+
const seen = new Set(family.map((c) => dirKey(c.directory)));
|
|
39832
|
+
const found = searchedConsumers(bound2, search ?? []).filter((c) => !seen.has(dirKey(c.directory)));
|
|
39833
|
+
return [...family, ...found];
|
|
39834
|
+
}
|
|
39835
|
+
function familyConsumers(bound2, top) {
|
|
39230
39836
|
return runWithProjectRoot(top, () => {
|
|
39231
39837
|
const family = graph();
|
|
39232
39838
|
const producer = consumerNode(family, bound2);
|
|
@@ -39245,16 +39851,76 @@ function listConsumers() {
|
|
|
39245
39851
|
directory: node.directory,
|
|
39246
39852
|
alias,
|
|
39247
39853
|
section: external ? "externals" : "members",
|
|
39248
|
-
names: usage.used.map((u) => u.publicName).sort()
|
|
39854
|
+
names: usage.used.map((u) => u.publicName).sort(),
|
|
39855
|
+
...brokenNames(usage)
|
|
39249
39856
|
});
|
|
39250
39857
|
}
|
|
39251
39858
|
return consumers.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
39252
39859
|
});
|
|
39253
39860
|
}
|
|
39861
|
+
function brokenNames(usage) {
|
|
39862
|
+
const names = [...new Set(usage.unexported.map((r) => r.authored.split("::").pop() ?? r.authored))].sort();
|
|
39863
|
+
return names.length ? { broken: names } : {};
|
|
39864
|
+
}
|
|
39865
|
+
function projectRootsUnder(folder) {
|
|
39866
|
+
const isRoot = (dir) => fs16.existsSync(path23.join(dir, ".wai", "project.yaml"));
|
|
39867
|
+
const at = path23.resolve(folder);
|
|
39868
|
+
if (!fs16.existsSync(at)) return [];
|
|
39869
|
+
if (isRoot(at)) return [at];
|
|
39870
|
+
return fs16.readdirSync(at, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".") && d.name !== "node_modules").map((d) => path23.join(at, d.name)).filter(isRoot);
|
|
39871
|
+
}
|
|
39872
|
+
function searchedConsumers(bound2, search) {
|
|
39873
|
+
if (search.length === 0) return [];
|
|
39874
|
+
if (getHostedLookup() !== null) throw new Error("searching folders for consumers is a local read: a hosted request reads no folder outside its project");
|
|
39875
|
+
const producerId = runWithProjectRoot(bound2, () => {
|
|
39876
|
+
const config = consumerConfig(bound2);
|
|
39877
|
+
return config ? effectiveProjectId(config) : null;
|
|
39878
|
+
});
|
|
39879
|
+
const roots = [...new Set(search.flatMap(projectRootsUnder).map((r) => path23.resolve(r)))].filter((r) => dirKey(r) !== dirKey(bound2));
|
|
39880
|
+
const out = [];
|
|
39881
|
+
for (const root of roots) {
|
|
39882
|
+
const answer = runWithProjectRoot(root, () => {
|
|
39883
|
+
let config;
|
|
39884
|
+
try {
|
|
39885
|
+
config = consumerConfig(root);
|
|
39886
|
+
} catch {
|
|
39887
|
+
return null;
|
|
39888
|
+
}
|
|
39889
|
+
if (!config) return null;
|
|
39890
|
+
const declared = declaredExternals(config).find((e) => !e.problem && (e.sourcePath !== void 0 && dirKey(path23.resolve(root, e.sourcePath)) === dirKey(bound2) || e.sourceGit !== void 0 && producerId !== null && e.project === producerId));
|
|
39891
|
+
if (!declared) return null;
|
|
39892
|
+
const node = consumerNode(graph(), root);
|
|
39893
|
+
const usage = node ? pinnedUsage(node.namespace, declared.alias) : null;
|
|
39894
|
+
return {
|
|
39895
|
+
project: effectiveProjectId(config) ?? path23.basename(root),
|
|
39896
|
+
key: node?.namespace ?? "",
|
|
39897
|
+
directory: root,
|
|
39898
|
+
alias: declared.alias,
|
|
39899
|
+
section: "externals",
|
|
39900
|
+
names: (usage?.used ?? []).map((u) => u.publicName).sort(),
|
|
39901
|
+
found: "search"
|
|
39902
|
+
};
|
|
39903
|
+
});
|
|
39904
|
+
if (answer) out.push(answer);
|
|
39905
|
+
}
|
|
39906
|
+
return out.sort((a, b) => a.directory < b.directory ? -1 : a.directory > b.directory ? 1 : 0);
|
|
39907
|
+
}
|
|
39908
|
+
function approvedRevision(against) {
|
|
39909
|
+
const bound2 = path23.resolve(getProjectRoot());
|
|
39910
|
+
const repo = repositoryRoot(bound2);
|
|
39911
|
+
if (!repo) throw new Error(`${bound2} is in no git work tree, so there is no earlier revision to compare with \u2014 pass a saved surface snapshot (\`--against <file>\`) instead`);
|
|
39912
|
+
const commit2 = against !== void 0 ? commitOf(bound2, against) : lastCommitOf(bound2, path23.join(".wai", "lock.json"));
|
|
39913
|
+
if (!commit2) {
|
|
39914
|
+
throw new Error(against !== void 0 ? `"${against}" names no commit of the repository at ${repo}` : "no approval of this project was ever committed (.wai/lock.json is in no commit) \u2014 lock and commit first, or name a revision (`--against <ref>`)");
|
|
39915
|
+
}
|
|
39916
|
+
const relative22 = path23.relative(repo, bound2);
|
|
39917
|
+
const directory = fetch(repo, commit2, relative22 === "" ? void 0 : relative22);
|
|
39918
|
+
return { directory, commit: commit2, label: against !== void 0 ? `${against} (${commit2.slice(0, 12)})` : `the last approval, committed at ${commit2.slice(0, 12)}` };
|
|
39919
|
+
}
|
|
39254
39920
|
|
|
39255
39921
|
// src/core/part-context.ts
|
|
39256
39922
|
var crypto6 = __toESM(require("crypto"));
|
|
39257
|
-
var
|
|
39923
|
+
var fs17 = __toESM(require("fs"));
|
|
39258
39924
|
var path24 = __toESM(require("path"));
|
|
39259
39925
|
var PartContextUnavailable = class extends WaironError {
|
|
39260
39926
|
constructor(message) {
|
|
@@ -39265,7 +39931,7 @@ var PartContextUnavailable = class extends WaironError {
|
|
|
39265
39931
|
function sameDirectory(a, b) {
|
|
39266
39932
|
const canonical3 = (d) => {
|
|
39267
39933
|
try {
|
|
39268
|
-
return
|
|
39934
|
+
return fs17.realpathSync.native(d);
|
|
39269
39935
|
} catch {
|
|
39270
39936
|
return path24.resolve(d);
|
|
39271
39937
|
}
|
|
@@ -39393,7 +40059,7 @@ function excerpt() {
|
|
|
39393
40059
|
if (!isPart(config) || !partOf) throw new PartContextUnavailable(`${partRoot} is not a part: its configuration declares no partOf`);
|
|
39394
40060
|
if (partOf.path === void 0) throw new PartContextUnavailable(`the part at ${partRoot} records no path to its parent "${partOf.project}"`);
|
|
39395
40061
|
const parentRoot = path24.resolve(partRoot, partOf.path);
|
|
39396
|
-
if (!
|
|
40062
|
+
if (!fs17.existsSync(parentRoot)) throw new PartContextUnavailable(`the parent "${partOf.project}" is not on disk at ${parentRoot}`);
|
|
39397
40063
|
const read2 = runWithProjectRoot(parentRoot, () => readParent(parentRoot, partRoot, partOf.project));
|
|
39398
40064
|
const { commit: _provenance, ...content } = read2;
|
|
39399
40065
|
const digest3 = `sha256:${crypto6.createHash("sha256").update(canonicalize(content)).digest("hex")}`;
|
|
@@ -39401,7 +40067,7 @@ function excerpt() {
|
|
|
39401
40067
|
}
|
|
39402
40068
|
|
|
39403
40069
|
// src/core/provision.ts
|
|
39404
|
-
var
|
|
40070
|
+
var fs18 = __toESM(require("fs"));
|
|
39405
40071
|
var path25 = __toESM(require("path"));
|
|
39406
40072
|
function defaultProjectConfig(name, now, id) {
|
|
39407
40073
|
const declared = id ?? effectiveProjectId({ name });
|
|
@@ -39452,7 +40118,7 @@ function provisionProject(name) {
|
|
|
39452
40118
|
function ensureProjectInitialized(fallbackName, id, vision) {
|
|
39453
40119
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39454
40120
|
const paths = aiPathsAt(getProjectRoot());
|
|
39455
|
-
const hasSystem =
|
|
40121
|
+
const hasSystem = fs18.existsSync(paths.specsSystem());
|
|
39456
40122
|
let name = fallbackName;
|
|
39457
40123
|
if (hasSystem) {
|
|
39458
40124
|
const existing = loadSystemSpec();
|
|
@@ -39472,7 +40138,7 @@ function ensureProjectInitialized(fallbackName, id, vision) {
|
|
|
39472
40138
|
return { wroteConfig, wroteSystem };
|
|
39473
40139
|
}
|
|
39474
40140
|
function isPartDirectory(dir) {
|
|
39475
|
-
return
|
|
40141
|
+
return fs18.existsSync(dir) && !holdsProjectContent(dir);
|
|
39476
40142
|
}
|
|
39477
40143
|
function memberDeclarationsAt2(dir) {
|
|
39478
40144
|
let config = null;
|
|
@@ -39489,7 +40155,7 @@ function memberDeclarationsAt2(dir) {
|
|
|
39489
40155
|
out.push({ alias: member.alias, path: member.path, form: "members" });
|
|
39490
40156
|
}
|
|
39491
40157
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
39492
|
-
if (!
|
|
40158
|
+
if (!fs18.existsSync(specsDir)) return out;
|
|
39493
40159
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
39494
40160
|
let raw;
|
|
39495
40161
|
try {
|
|
@@ -39540,7 +40206,7 @@ function listDirectChainedSubprojects(projectRoot) {
|
|
|
39540
40206
|
return out;
|
|
39541
40207
|
}
|
|
39542
40208
|
function childHasSpecsButNoConfig(childDir) {
|
|
39543
|
-
return
|
|
40209
|
+
return fs18.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
|
|
39544
40210
|
}
|
|
39545
40211
|
function findChainingSubprojectsMissingConfig(projectRoot) {
|
|
39546
40212
|
const missing = [];
|
|
@@ -39612,7 +40278,7 @@ function createMember(alias, source, description, as) {
|
|
|
39612
40278
|
}
|
|
39613
40279
|
function holdsProjectContent(dir) {
|
|
39614
40280
|
const paths = aiPathsAt(dir);
|
|
39615
|
-
if (
|
|
40281
|
+
if (fs18.existsSync(paths.specsSystem()) || fs18.existsSync(path25.join(paths.root(), "lock.json"))) return true;
|
|
39616
40282
|
try {
|
|
39617
40283
|
return projectConfigRepositoryAt(dir).load()?.id !== void 0;
|
|
39618
40284
|
} catch {
|
|
@@ -39736,14 +40402,14 @@ function moveMember(alias, newPath) {
|
|
|
39736
40402
|
const oldDir = assertContainedProjectPath(root, currentPath);
|
|
39737
40403
|
const newDir = assertContainedProjectPath(root, nextPath);
|
|
39738
40404
|
if (oldDir !== newDir) {
|
|
39739
|
-
if (!
|
|
40405
|
+
if (!fs18.existsSync(oldDir)) {
|
|
39740
40406
|
throw new WaironError(`Member directory not found at its current path: ${oldDir}`);
|
|
39741
40407
|
}
|
|
39742
|
-
if (
|
|
40408
|
+
if (fs18.existsSync(newDir)) {
|
|
39743
40409
|
throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
39744
40410
|
}
|
|
39745
40411
|
ensureDir(path25.dirname(newDir));
|
|
39746
|
-
|
|
40412
|
+
fs18.renameSync(oldDir, newDir);
|
|
39747
40413
|
}
|
|
39748
40414
|
projectConfigRepository.setMemberPath(alias, nextPath);
|
|
39749
40415
|
invalidateSpecCache();
|
|
@@ -39756,13 +40422,13 @@ function moveSibling(alias, currentPath, newPath, isPart2) {
|
|
|
39756
40422
|
}
|
|
39757
40423
|
const oldDir = path25.resolve(root, currentPath);
|
|
39758
40424
|
const newDir = path25.resolve(root, nextPath);
|
|
39759
|
-
if (!
|
|
39760
|
-
if (
|
|
40425
|
+
if (!fs18.existsSync(oldDir)) throw new WaironError(`Member directory not found at its current path: ${oldDir}`);
|
|
40426
|
+
if (fs18.existsSync(newDir)) throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
39761
40427
|
ensureDir(path25.dirname(newDir));
|
|
39762
|
-
if (
|
|
40428
|
+
if (fs18.statSync(oldDir).dev !== fs18.statSync(path25.dirname(newDir)).dev) {
|
|
39763
40429
|
throw new WaironError(`Refusing to move the member "${alias}" to ${newDir}: it lies on another volume, and a rename across volumes is not atomic.`);
|
|
39764
40430
|
}
|
|
39765
|
-
|
|
40431
|
+
fs18.renameSync(oldDir, newDir);
|
|
39766
40432
|
if (isPart2) {
|
|
39767
40433
|
const parentId = effectiveProjectId(projectConfigRepository.load() ?? { name: loadSystemSpec()?.name ?? "" });
|
|
39768
40434
|
if (parentId !== null) runWithProjectRoot(newDir, () => projectConfigRepository.setPartOf({ project: parentId, path: toPosixPath(path25.relative(newDir, root)) || "." }));
|
|
@@ -39792,8 +40458,8 @@ function isWithinDir2(dir, file) {
|
|
|
39792
40458
|
}
|
|
39793
40459
|
var MOVED_REF_POSITIONS = /* @__PURE__ */ new Set(["component", "interface"]);
|
|
39794
40460
|
var INTO_MEMBER_POSITIONS = /* @__PURE__ */ new Set([...MOVED_REF_POSITIONS, "type"]);
|
|
39795
|
-
function externalizeSubsystem(subsystemId,
|
|
39796
|
-
return externalizeInto(subsystemId,
|
|
40461
|
+
function externalizeSubsystem(subsystemId, path50, as) {
|
|
40462
|
+
return externalizeInto(subsystemId, path50, as);
|
|
39797
40463
|
}
|
|
39798
40464
|
function externalizeInto(subsystemId, partPath, as) {
|
|
39799
40465
|
if (subsystemId.includes("::")) {
|
|
@@ -39820,7 +40486,7 @@ function externalizeInto(subsystemId, partPath, as) {
|
|
|
39820
40486
|
if (holdsProjectContent(partDir)) throw new WaironError(`cannot externalize "${subsystemId}": ${partDir} already holds a project.`);
|
|
39821
40487
|
const joined = bound2?.parts.find((p) => p.directory !== void 0 && path25.resolve(p.directory) === path25.resolve(partDir));
|
|
39822
40488
|
const alias = joined?.alias ?? subsystemId;
|
|
39823
|
-
if (!joined &&
|
|
40489
|
+
if (!joined && fs18.existsSync(aiPathsAt(partDir).projectConfig())) {
|
|
39824
40490
|
throw new WaironError(`cannot externalize "${subsystemId}": ${partDir} already holds a configuration (.wai/project.yaml) \u2014 a new part's directory holds none; creating one never overwrites it.`);
|
|
39825
40491
|
}
|
|
39826
40492
|
if (!joined && (declaredMembers(projectConfigRepository.load() ?? {}).some((m) => m.alias === alias) || projectConfigRepository.load()?.externals?.[alias] !== void 0)) {
|
|
@@ -39828,10 +40494,10 @@ function externalizeInto(subsystemId, partPath, as) {
|
|
|
39828
40494
|
}
|
|
39829
40495
|
const partSpecsDir = aiPathsAt(partDir).specsDir();
|
|
39830
40496
|
const targetDir = path25.join(partSpecsDir, subsystemId);
|
|
39831
|
-
if (owned.home !== void 0 &&
|
|
40497
|
+
if (owned.home !== void 0 && fs18.existsSync(targetDir)) throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${targetDir}`);
|
|
39832
40498
|
const destinationOf = (file) => path25.join(partSpecsDir, path25.relative(parentSpecsDir, file));
|
|
39833
40499
|
const strays = owned.files.filter((f) => !(owned.home !== void 0 && isWithinDir2(owned.home, f)));
|
|
39834
|
-
const taken = strays.filter((f) =>
|
|
40500
|
+
const taken = strays.filter((f) => fs18.existsSync(destinationOf(f)));
|
|
39835
40501
|
if (taken.length > 0) throw new WaironError(`cannot externalize "${subsystemId}": the part already holds ${taken.map((f) => toPosixPath(path25.relative(partDir, destinationOf(f)))).join(", ")}.`);
|
|
39836
40502
|
if (as === "project") {
|
|
39837
40503
|
if (joined) throw new WaironError(`cannot externalize "${subsystemId}" as a project into the existing part "${joined.alias}": externalize it into the part, then promote the part (\`wairon member promote ${joined.alias}\`).`);
|
|
@@ -39842,13 +40508,13 @@ function externalizeInto(subsystemId, partPath, as) {
|
|
|
39842
40508
|
const moved = [];
|
|
39843
40509
|
if (owned.home !== void 0) {
|
|
39844
40510
|
ensureDir(path25.dirname(targetDir));
|
|
39845
|
-
|
|
40511
|
+
fs18.renameSync(owned.home, targetDir);
|
|
39846
40512
|
moved.push(...specFilesUnder(targetDir));
|
|
39847
40513
|
}
|
|
39848
40514
|
for (const file of strays) {
|
|
39849
40515
|
const to = destinationOf(file);
|
|
39850
40516
|
ensureDir(path25.dirname(to));
|
|
39851
|
-
|
|
40517
|
+
fs18.renameSync(file, to);
|
|
39852
40518
|
moved.push(to);
|
|
39853
40519
|
removeEmptyDirsUpTo(path25.dirname(file), parentSpecsDir);
|
|
39854
40520
|
}
|
|
@@ -39859,10 +40525,10 @@ function externalizeInto(subsystemId, partPath, as) {
|
|
|
39859
40525
|
}
|
|
39860
40526
|
function subsystemFiles(subsystemId, specsDir) {
|
|
39861
40527
|
const subPath = getSubsystemPath(subsystemId);
|
|
39862
|
-
const nested = subPath.endsWith(".index.yaml") && path25.resolve(path25.dirname(subPath)) === path25.resolve(path25.join(specsDir, subsystemId)) &&
|
|
40528
|
+
const nested = subPath.endsWith(".index.yaml") && path25.resolve(path25.dirname(subPath)) === path25.resolve(path25.join(specsDir, subsystemId)) && fs18.existsSync(subPath);
|
|
39863
40529
|
const home = nested ? path25.dirname(subPath) : void 0;
|
|
39864
40530
|
const files = new Set(home !== void 0 ? specFilesUnder(home).map((f) => path25.resolve(f)) : []);
|
|
39865
|
-
if (!nested &&
|
|
40531
|
+
if (!nested && fs18.existsSync(subPath)) files.add(path25.resolve(subPath));
|
|
39866
40532
|
const l0 = path25.resolve(aiPathsAt(getProjectRoot()).specsSystem());
|
|
39867
40533
|
const others = () => specFilesUnder(specsDir).filter((f) => path25.resolve(f) !== l0 && !files.has(path25.resolve(f)));
|
|
39868
40534
|
const add2 = (file) => {
|
|
@@ -39890,13 +40556,13 @@ function subsystemFiles(subsystemId, specsDir) {
|
|
|
39890
40556
|
function removeEmptyDirsUpTo(dir, stop) {
|
|
39891
40557
|
let at = path25.resolve(dir);
|
|
39892
40558
|
const end = path25.resolve(stop);
|
|
39893
|
-
while (at !== end && isWithinDir2(end, at) &&
|
|
39894
|
-
|
|
40559
|
+
while (at !== end && isWithinDir2(end, at) && fs18.existsSync(at) && fs18.readdirSync(at).length === 0) {
|
|
40560
|
+
fs18.rmdirSync(at);
|
|
39895
40561
|
at = path25.dirname(at);
|
|
39896
40562
|
}
|
|
39897
40563
|
}
|
|
39898
40564
|
function specFilesUnder(dir) {
|
|
39899
|
-
return
|
|
40565
|
+
return fs18.existsSync(dir) ? listFilesRecursive(dir, ".yaml") : [];
|
|
39900
40566
|
}
|
|
39901
40567
|
function ownSpecFiles(family, except) {
|
|
39902
40568
|
const root = getProjectRoot();
|
|
@@ -40029,7 +40695,7 @@ function rewriteRefsIn(specs, remap, record) {
|
|
|
40029
40695
|
return rewritten;
|
|
40030
40696
|
}
|
|
40031
40697
|
function patchKeepingStamp(file, mutate) {
|
|
40032
|
-
if (!
|
|
40698
|
+
if (!fs18.existsSync(file)) return;
|
|
40033
40699
|
const raw = readYamlFile(file);
|
|
40034
40700
|
if (mutate(raw)) writeYamlFile(file, raw);
|
|
40035
40701
|
}
|
|
@@ -40074,7 +40740,7 @@ function promoteMember(alias, id) {
|
|
|
40074
40740
|
patchKeepingStamp(s.file, (raw) => raw.parentSystem === projectName ? false : (raw.parentSystem = projectName, true));
|
|
40075
40741
|
}
|
|
40076
40742
|
if (declaration2?.as === "part") projectConfigRepository.updateMember(alias, { as: "" });
|
|
40077
|
-
const result = { respelled: [], memberRespelled: [], exported: [], imported: [], consumersDropped: [], entriesDeclared: [], topicsCrossing: [] };
|
|
40743
|
+
const result = { respelled: [], memberRespelled: [], exported: [], imported: [], consumersDropped: [], entriesDeclared: [], topicsCrossing: [], pathsRebased: [], signaturesRederived: [] };
|
|
40078
40744
|
const parentSide = rewriteRefsIn(crossing.in.map((c) => c.spec).filter(unique), (ref, position) => INTO_MEMBER_POSITIONS.has(position) && crossing.insideTargets.has(ref) ? `${alias}::${ref}` : ref, { result, member: false });
|
|
40079
40745
|
const climbs = climbsOf(family, crossing.insideIds, parentId).map;
|
|
40080
40746
|
const climbing = inside.filter((s) => family.authoredReferences.some((r) => r.form === "super" && r.specId === s.id));
|
|
@@ -40107,8 +40773,71 @@ function promoteMember(alias, id) {
|
|
|
40107
40773
|
});
|
|
40108
40774
|
}
|
|
40109
40775
|
invalidateSpecCache();
|
|
40776
|
+
rebaseEscapingPaths(dir, getProjectRoot(), result);
|
|
40777
|
+
rederiveSignatures(new Set(result.respelled.map((r) => r.specId)), result.signaturesRederived);
|
|
40778
|
+
runWithProjectRoot(dir, () => {
|
|
40779
|
+
invalidateSpecCache();
|
|
40780
|
+
rederiveSignatures(new Set(result.memberRespelled.map((r) => localIn(r.specId, memberKey ?? alias))), result.signaturesRederived);
|
|
40781
|
+
});
|
|
40782
|
+
invalidateSpecCache();
|
|
40110
40783
|
return result;
|
|
40111
40784
|
}
|
|
40785
|
+
function rebaseEscapingPaths(memberDir, familyRoot, result) {
|
|
40786
|
+
const shownMember = toPosixPath(path25.relative(familyRoot, memberDir)) || ".";
|
|
40787
|
+
for (const file of specFilesUnder(aiPathsAt(memberDir).specsDir())) {
|
|
40788
|
+
let raw;
|
|
40789
|
+
try {
|
|
40790
|
+
raw = readYamlFile(file);
|
|
40791
|
+
} catch {
|
|
40792
|
+
continue;
|
|
40793
|
+
}
|
|
40794
|
+
if (!raw || typeof raw !== "object") continue;
|
|
40795
|
+
const isImplementation = "contract" in raw;
|
|
40796
|
+
if (!isImplementation && specKind(raw) !== "type") continue;
|
|
40797
|
+
let changed = false;
|
|
40798
|
+
const rebase = (holder, key2, where) => {
|
|
40799
|
+
const p = holder[key2];
|
|
40800
|
+
if (typeof p !== "string" || p === "" || path25.isAbsolute(p)) return;
|
|
40801
|
+
const absolute = path25.resolve(memberDir, p);
|
|
40802
|
+
if (isWithinDir2(memberDir, absolute)) return;
|
|
40803
|
+
const next = toPosixPath(path25.relative(familyRoot, absolute));
|
|
40804
|
+
if (next.startsWith("..") || path25.isAbsolute(next)) return;
|
|
40805
|
+
holder[key2] = next;
|
|
40806
|
+
changed = true;
|
|
40807
|
+
result.pathsRebased.push(`${raw.id} ${where}: ${p} \u2192 ${next} (the code is not moved: it is planned there until ${next} of the parent moves into ${shownMember})`);
|
|
40808
|
+
};
|
|
40809
|
+
for (const key2 of isImplementation ? ["sourcePath", "simPath"] : ["sourcePath"]) rebase(raw, key2, key2);
|
|
40810
|
+
if (Array.isArray(raw.methods)) {
|
|
40811
|
+
for (const method of raw.methods) {
|
|
40812
|
+
if (method && typeof method === "object") rebase(method, "sourcePath", `${method.name} sourcePath`);
|
|
40813
|
+
}
|
|
40814
|
+
}
|
|
40815
|
+
if (changed) writeYamlFile(file, raw);
|
|
40816
|
+
}
|
|
40817
|
+
invalidateSpecCache();
|
|
40818
|
+
}
|
|
40819
|
+
function rederiveSignatures(touched, lines) {
|
|
40820
|
+
if (touched.size === 0) return;
|
|
40821
|
+
invalidateSpecCache();
|
|
40822
|
+
const stale = signatureFacts().staleTexts.filter((t) => touched.has(t.specId));
|
|
40823
|
+
if (stale.length === 0) return;
|
|
40824
|
+
const saved = /* @__PURE__ */ new Set();
|
|
40825
|
+
for (const t of stale) {
|
|
40826
|
+
const key2 = `${t.kind}:${t.specId}`;
|
|
40827
|
+
if (!saved.has(key2)) {
|
|
40828
|
+
saved.add(key2);
|
|
40829
|
+
if (t.kind === "interface") {
|
|
40830
|
+
const intf = loadInterfaceSpec(t.specId);
|
|
40831
|
+
if (intf) saveInterfaceSpec(intf);
|
|
40832
|
+
} else {
|
|
40833
|
+
const type = loadTypeSpec(t.specId);
|
|
40834
|
+
if (type) saveSpec("type", type);
|
|
40835
|
+
}
|
|
40836
|
+
}
|
|
40837
|
+
lines.push(`${t.specId}.${t.method}: ${t.stored} \u2192 ${t.derived}`);
|
|
40838
|
+
}
|
|
40839
|
+
invalidateSpecCache();
|
|
40840
|
+
}
|
|
40112
40841
|
function callsInto2(callers, portals) {
|
|
40113
40842
|
const componentOf = new Map(callers.filter((s) => s.kind === "interface").map((s) => [s.id, String(s.raw.component ?? "")]));
|
|
40114
40843
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -40269,7 +40998,7 @@ function respellQualified(inside, outside, names, result) {
|
|
|
40269
40998
|
};
|
|
40270
40999
|
for (const [specs, side] of [[inside, "inside"], [outside, "outside"]]) {
|
|
40271
41000
|
for (const spec of specs) {
|
|
40272
|
-
if (!
|
|
41001
|
+
if (!fs18.existsSync(spec.file)) continue;
|
|
40273
41002
|
const raw = readYamlFile(spec.file);
|
|
40274
41003
|
if (!raw || typeof raw !== "object") continue;
|
|
40275
41004
|
const edits = [];
|
|
@@ -40333,24 +41062,99 @@ function dropCrossingConsumers(inside, outside, result) {
|
|
|
40333
41062
|
}
|
|
40334
41063
|
}
|
|
40335
41064
|
}
|
|
41065
|
+
function audienceRank4(audience) {
|
|
41066
|
+
const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
|
|
41067
|
+
return idx === -1 ? SURFACE_AUDIENCES.indexOf("instance") : idx;
|
|
41068
|
+
}
|
|
40336
41069
|
function exportReExported(inside, memberDir, alias, result) {
|
|
40337
41070
|
invalidateSpecCache();
|
|
40338
|
-
const
|
|
40339
|
-
|
|
40340
|
-
const
|
|
40341
|
-
|
|
41071
|
+
const parent = loadSystemSpec();
|
|
41072
|
+
const parentEntries = parent?.publicInterfaces ?? [];
|
|
41073
|
+
const entries = parentEntries.filter((e) => e.from === alias);
|
|
41074
|
+
if (!parent || entries.length === 0) return;
|
|
41075
|
+
const parentId = effectiveProjectId(projectConfigRepository.load() ?? { name: parent.name }) ?? parent.name;
|
|
41076
|
+
const repointed = /* @__PURE__ */ new Map();
|
|
40342
41077
|
runWithProjectRoot(memberDir, () => {
|
|
40343
|
-
for (const name of exportItems(targets)) result.exported.push(`${alias} L0: ${name} (re-exported by the parent)`);
|
|
40344
|
-
if (!whole) return;
|
|
40345
41078
|
invalidateSpecCache();
|
|
40346
41079
|
const system = loadSystemSpec();
|
|
40347
41080
|
if (!system) return;
|
|
40348
|
-
const own2 = system.publicInterfaces ?? [];
|
|
40349
|
-
const
|
|
40350
|
-
|
|
40351
|
-
|
|
40352
|
-
|
|
41081
|
+
const own2 = [...system.publicInterfaces ?? []];
|
|
41082
|
+
const types = loadTypeSpecs();
|
|
41083
|
+
let changed = false;
|
|
41084
|
+
for (const e of entries) {
|
|
41085
|
+
const audience = e.audience ?? "instance";
|
|
41086
|
+
if (e.component === void 0 && e.typeDef === void 0 && e.interface === void 0) {
|
|
41087
|
+
for (const s of inside.filter((x) => x.kind === "subsystem")) {
|
|
41088
|
+
const whole = own2.find((x) => x.from === s.id && x.component === void 0 && x.typeDef === void 0 && x.interface === void 0);
|
|
41089
|
+
if (whole) {
|
|
41090
|
+
if (audienceRank4(whole.audience) < audienceRank4(audience)) {
|
|
41091
|
+
whole.audience = audience;
|
|
41092
|
+
changed = true;
|
|
41093
|
+
result.exported.push(`${alias} L0: everything ${s.id} exports widened to ${audience} (re-exported whole by the parent)`);
|
|
41094
|
+
}
|
|
41095
|
+
continue;
|
|
41096
|
+
}
|
|
41097
|
+
own2.push({ from: s.id, audience });
|
|
41098
|
+
changed = true;
|
|
41099
|
+
result.exported.push(`${alias} L0: everything ${s.id} exports at ${audience} (re-exported whole by the parent)`);
|
|
41100
|
+
}
|
|
41101
|
+
continue;
|
|
41102
|
+
}
|
|
41103
|
+
const type = e.typeDef !== void 0 ? types.find((t) => t.id === localIn(e.typeDef, alias)) : void 0;
|
|
41104
|
+
const interfaceId = e.interface !== void 0 ? localIn(e.interface, alias) : void 0;
|
|
41105
|
+
const componentId = e.component !== void 0 ? localIn(e.component, alias) : interfaceId !== void 0 ? loadInterfaceSpec(interfaceId)?.component : void 0;
|
|
41106
|
+
const component = type === void 0 && componentId !== void 0 ? loadComponentSpec(componentId) : null;
|
|
41107
|
+
if (type === void 0 && !component) continue;
|
|
41108
|
+
const field = type ? "typeDef" : "component";
|
|
41109
|
+
const item = type ? type.id : component.id;
|
|
41110
|
+
const publicName = e.as ?? e.id ?? (interfaceId ?? item);
|
|
41111
|
+
if (type?.subsystem) exportTypeFromSubsystem(type.subsystem, type.id);
|
|
41112
|
+
const held = own2.find((x) => x[field] === item && (interfaceId === void 0 || x.interface === void 0 || x.interface === interfaceId));
|
|
41113
|
+
if (held) {
|
|
41114
|
+
if (audienceRank4(held.audience ?? "project") < audienceRank4(audience)) {
|
|
41115
|
+
held.audience = audience;
|
|
41116
|
+
changed = true;
|
|
41117
|
+
}
|
|
41118
|
+
const name = held.as ?? held.id ?? (held.interface ?? item);
|
|
41119
|
+
result.exported.push(`${alias} L0: ${item} as ${name} at ${held.audience} (re-exported by the parent)`);
|
|
41120
|
+
repointed.set(e, { name, field, item });
|
|
41121
|
+
continue;
|
|
41122
|
+
}
|
|
41123
|
+
const subsystem = type ? type.subsystem : component.subsystem;
|
|
41124
|
+
own2.push({
|
|
41125
|
+
...subsystem ? { from: subsystem } : {},
|
|
41126
|
+
[field]: item,
|
|
41127
|
+
...interfaceId !== void 0 && !type ? { interface: interfaceId } : {},
|
|
41128
|
+
...publicName !== (interfaceId ?? item) ? { as: publicName } : {},
|
|
41129
|
+
...e.details !== void 0 ? { details: e.details } : {},
|
|
41130
|
+
audience
|
|
41131
|
+
});
|
|
41132
|
+
changed = true;
|
|
41133
|
+
result.exported.push(`${alias} L0: ${item}${publicName !== item ? ` as ${publicName}` : ""} at ${audience} (re-exported by the parent)`);
|
|
41134
|
+
repointed.set(e, { name: publicName, field, item });
|
|
41135
|
+
}
|
|
41136
|
+
if (changed) saveSystemSpec({ ...system, publicInterfaces: own2 });
|
|
41137
|
+
invalidateSpecCache();
|
|
41138
|
+
const table = resolveProjectExports();
|
|
41139
|
+
for (const [e, to] of repointed) {
|
|
41140
|
+
const interfaceId = e.interface !== void 0 ? localIn(e.interface, alias) : void 0;
|
|
41141
|
+
const bound2 = table.entries.find((x) => to.field === "typeDef" ? x.typeDef === to.item : x.component === to.item && (interfaceId === void 0 || x.interface === void 0 || x.interface === interfaceId));
|
|
41142
|
+
if (bound2) to.name = bound2.publicName;
|
|
41143
|
+
}
|
|
41144
|
+
});
|
|
41145
|
+
invalidateSpecCache();
|
|
41146
|
+
if (repointed.size === 0) return;
|
|
41147
|
+
const next = parentEntries.map((e) => {
|
|
41148
|
+
const to = repointed.get(e);
|
|
41149
|
+
if (!to) return e;
|
|
41150
|
+
const { component: _c, interface: _i, typeDef: _t, ...rest } = e;
|
|
41151
|
+
const before = e.interface ?? e.component ?? e.typeDef;
|
|
41152
|
+
const out = { ...rest, [to.field]: to.name, ...e.as === void 0 && to.name !== before ? { as: before } : {} };
|
|
41153
|
+
const shownAs = out.as ?? to.name;
|
|
41154
|
+
result.exported.push(`${parentId} L0: ${shownAs} re-exported through ${alias} as ${to.name} at ${e.audience ?? "instance"} (was ${before} from subsystem ${alias})`);
|
|
41155
|
+
return out;
|
|
40353
41156
|
});
|
|
41157
|
+
saveSystemSpec({ ...parent, publicInterfaces: next });
|
|
40354
41158
|
invalidateSpecCache();
|
|
40355
41159
|
}
|
|
40356
41160
|
function promotedVision(id, parentName) {
|
|
@@ -40522,7 +41326,7 @@ function internalizeRefusals(scan, destination) {
|
|
|
40522
41326
|
const specsDir = aiPathsAt(getProjectRoot()).specsDir();
|
|
40523
41327
|
const memberSpecs = aiPathsAt(scan.memberDir).specsDir();
|
|
40524
41328
|
const skip = path25.resolve(aiPathsAt(scan.memberDir).specsSystem());
|
|
40525
|
-
const taken = (
|
|
41329
|
+
const taken = (fs18.existsSync(memberSpecs) ? listFilesRecursive(memberSpecs, "") : []).filter((f) => path25.resolve(f) !== skip && fs18.existsSync(path25.join(specsDir, path25.relative(memberSpecs, f)))).map((f) => toPosixPath(path25.relative(memberSpecs, f)));
|
|
40526
41330
|
if (taken.length > 0) out.push(`its specs would land on this project's ${taken.join(", ")}`);
|
|
40527
41331
|
out.push(...aliasConflicts(scan, bound2));
|
|
40528
41332
|
if (destination.packs === void 0) {
|
|
@@ -40540,7 +41344,7 @@ function internalizeRefusals(scan, destination) {
|
|
|
40540
41344
|
if (held !== void 0 && held !== scan.alias) out.push(`the export "${name}" is a public name this project already exports for ${held}`);
|
|
40541
41345
|
}
|
|
40542
41346
|
const wai = path25.join(scan.memberDir, ".wai");
|
|
40543
|
-
for (const entry of
|
|
41347
|
+
for (const entry of fs18.existsSync(wai) ? fs18.readdirSync(wai) : []) {
|
|
40544
41348
|
if (entry === "specs" || entry === "transactions" || MEMBER_WAI_GROUPS.has(entry)) continue;
|
|
40545
41349
|
out.push(`its .wai holds "${entry}", which this write does not recognise \u2014 move or delete it by hand`);
|
|
40546
41350
|
}
|
|
@@ -40751,15 +41555,15 @@ function carryPacks(scan, destination, result) {
|
|
|
40751
41555
|
}
|
|
40752
41556
|
function deleteMemberWai(memberDir) {
|
|
40753
41557
|
const wai = path25.join(memberDir, ".wai");
|
|
40754
|
-
if (!
|
|
41558
|
+
if (!fs18.existsSync(wai)) return [];
|
|
40755
41559
|
const deleted = listFilesRecursive(wai, "").filter((f) => !path25.relative(wai, f).split(path25.sep).includes("transactions")).map((f) => {
|
|
40756
41560
|
const rel2 = toPosixPath(path25.relative(wai, f));
|
|
40757
41561
|
return `${MEMBER_WAI_GROUPS.get(rel2.split("/")[0]) ?? "L0 (its pieces placed)"}: .wai/${rel2}`;
|
|
40758
41562
|
});
|
|
40759
|
-
for (const entry of
|
|
40760
|
-
if (entry !== "transactions")
|
|
41563
|
+
for (const entry of fs18.readdirSync(wai)) {
|
|
41564
|
+
if (entry !== "transactions") fs18.rmSync(path25.join(wai, entry), { recursive: true, force: true });
|
|
40761
41565
|
}
|
|
40762
|
-
if (
|
|
41566
|
+
if (fs18.readdirSync(wai).length === 0) fs18.rmdirSync(wai);
|
|
40763
41567
|
return deleted.sort();
|
|
40764
41568
|
}
|
|
40765
41569
|
function memberToDemote(family, alias) {
|
|
@@ -40772,7 +41576,7 @@ function memberToDemote(family, alias) {
|
|
|
40772
41576
|
throw new WaironError(`demote refused: the member "${alias}" is fetched from git \u2014 its files are the fetch cache's. Demote it from a checkout of its repository.`);
|
|
40773
41577
|
}
|
|
40774
41578
|
if (referenced?.sourceKind === "hosted") throw new WaironError(`demote refused: the member "${alias}" is a hosted record of its own \u2014 a part is part of its parent's record, so it must live in this project's tree.`);
|
|
40775
|
-
if (referenced?.directory !== void 0 &&
|
|
41579
|
+
if (referenced?.directory !== void 0 && fs18.existsSync(referenced.directory)) return { dir: referenced.directory, contained: false };
|
|
40776
41580
|
throw new WaironError(`demote refused: this project declares no project member "${alias}".`);
|
|
40777
41581
|
}
|
|
40778
41582
|
function localsOf(root) {
|
|
@@ -40804,7 +41608,7 @@ function demoteRefusals(scan, destination, memberIds, family) {
|
|
|
40804
41608
|
if (held !== void 0 && held !== scan.alias) out.push(`the export "${name}" is a public name this project already exports for ${held}`);
|
|
40805
41609
|
}
|
|
40806
41610
|
const wai = path25.join(scan.memberDir, ".wai");
|
|
40807
|
-
for (const entry of
|
|
41611
|
+
for (const entry of fs18.existsSync(wai) ? fs18.readdirSync(wai) : []) {
|
|
40808
41612
|
if (entry === "specs" || entry === "transactions" || MEMBER_WAI_GROUPS.has(entry)) continue;
|
|
40809
41613
|
out.push(`its .wai holds "${entry}", which this write does not recognise \u2014 move or delete it by hand`);
|
|
40810
41614
|
}
|
|
@@ -40832,7 +41636,7 @@ function demoteMember(alias, destination) {
|
|
|
40832
41636
|
refusals.push(`its metadata needs a home subsystem (--home) \u2014 ${home === "" ? "none was named" : `"${home}" is no subsystem of the member or of this project`}; its subsystems are ${subsystems.join(", ") || "none"}`);
|
|
40833
41637
|
}
|
|
40834
41638
|
if (refusals.length > 0) throw new WaironError(`demote refused: "${alias}" cannot become a part \u2014 ${refusals.join("; ")}.`);
|
|
40835
|
-
const result = { subsystems, types, placed: [], externals: [], deleted: [], notCarried: [], consumersRestored: [] };
|
|
41639
|
+
const result = { subsystems, types, placed: [], externals: [], deleted: [], notCarried: [], consumersRestored: [], signaturesRederived: [] };
|
|
40836
41640
|
const intoMember = localsOf(dir);
|
|
40837
41641
|
const backOut = localsOf(root);
|
|
40838
41642
|
invalidateSpecCache();
|
|
@@ -40875,6 +41679,7 @@ function demoteMember(alias, destination) {
|
|
|
40875
41679
|
invalidateSpecCache();
|
|
40876
41680
|
for (const spec of [...parentSide, ...memberSide]) if (spec.kind !== "system") normalizeReferences(spec.kind, spec.id);
|
|
40877
41681
|
invalidateSpecCache();
|
|
41682
|
+
rederiveSignatures(new Set([...parentSide, ...memberSide].map((s) => s.id)), result.signaturesRederived ??= []);
|
|
40878
41683
|
return result;
|
|
40879
41684
|
}
|
|
40880
41685
|
function respellQualifiedBack(parentSide, memberSide, aliases, tables, crossed) {
|
|
@@ -40899,7 +41704,7 @@ function respellQualifiedBack(parentSide, memberSide, aliases, tables, crossed)
|
|
|
40899
41704
|
return to;
|
|
40900
41705
|
};
|
|
40901
41706
|
for (const spec of specs) {
|
|
40902
|
-
if (!
|
|
41707
|
+
if (!fs18.existsSync(spec.file)) continue;
|
|
40903
41708
|
const raw = readYamlFile(spec.file);
|
|
40904
41709
|
if (!raw || typeof raw !== "object") continue;
|
|
40905
41710
|
let changed = false;
|
|
@@ -40954,16 +41759,16 @@ function deleteProjectContent(memberDir) {
|
|
|
40954
41759
|
const wai = path25.join(memberDir, ".wai");
|
|
40955
41760
|
const deleted = [];
|
|
40956
41761
|
const l0 = aiPathsAt(memberDir).specsSystem();
|
|
40957
|
-
if (
|
|
40958
|
-
|
|
41762
|
+
if (fs18.existsSync(l0)) {
|
|
41763
|
+
fs18.rmSync(l0);
|
|
40959
41764
|
deleted.push(`L0 (its pieces placed): .wai/${toPosixPath(path25.relative(wai, l0))}`);
|
|
40960
41765
|
}
|
|
40961
|
-
for (const entry of
|
|
41766
|
+
for (const entry of fs18.existsSync(wai) ? fs18.readdirSync(wai) : []) {
|
|
40962
41767
|
if (entry === "specs" || entry === "transactions") continue;
|
|
40963
41768
|
const at = path25.join(wai, entry);
|
|
40964
|
-
const files =
|
|
41769
|
+
const files = fs18.statSync(at).isDirectory() ? listFilesRecursive(at, "") : [at];
|
|
40965
41770
|
for (const f of files) deleted.push(`${MEMBER_WAI_GROUPS.get(entry) ?? "derived outputs"}: .wai/${toPosixPath(path25.relative(wai, f))}`);
|
|
40966
|
-
|
|
41771
|
+
fs18.rmSync(at, { recursive: true, force: true });
|
|
40967
41772
|
}
|
|
40968
41773
|
return deleted.sort();
|
|
40969
41774
|
}
|
|
@@ -40993,7 +41798,7 @@ function specContents(dir) {
|
|
|
40993
41798
|
const out = /* @__PURE__ */ new Map();
|
|
40994
41799
|
if (dir === null) return out;
|
|
40995
41800
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
40996
|
-
for (const f of specFilesUnder(specsDir)) out.set(toPosixPath(path25.relative(dir, f)),
|
|
41801
|
+
for (const f of specFilesUnder(specsDir)) out.set(toPosixPath(path25.relative(dir, f)), fs18.readFileSync(f, "utf8").replace(/\r\n/g, "\n"));
|
|
40997
41802
|
return out;
|
|
40998
41803
|
}
|
|
40999
41804
|
function advanceMember(alias, ref, commit2, dryRun) {
|
|
@@ -41032,16 +41837,16 @@ function crossingReferences(family, from, to) {
|
|
|
41032
41837
|
return out;
|
|
41033
41838
|
}
|
|
41034
41839
|
function moveTreeInto(src, dst, skip) {
|
|
41035
|
-
if (!
|
|
41840
|
+
if (!fs18.existsSync(src)) return [];
|
|
41036
41841
|
const files = listFilesRecursive(src, "").filter((f) => !skip.has(path25.resolve(f)));
|
|
41037
41842
|
const targets = files.map((f) => path25.join(dst, path25.relative(src, f)));
|
|
41038
|
-
const taken = targets.filter((t) =>
|
|
41843
|
+
const taken = targets.filter((t) => fs18.existsSync(t));
|
|
41039
41844
|
if (taken.length > 0) {
|
|
41040
41845
|
throw new WaironError(`cannot internalize: the member's specs would overwrite ${taken.map((t) => path25.relative(dst, t)).join(", ")} in this project.`);
|
|
41041
41846
|
}
|
|
41042
41847
|
files.forEach((file, i) => {
|
|
41043
41848
|
ensureDir(path25.dirname(targets[i]));
|
|
41044
|
-
|
|
41849
|
+
fs18.renameSync(file, targets[i]);
|
|
41045
41850
|
});
|
|
41046
41851
|
return targets;
|
|
41047
41852
|
}
|
|
@@ -41139,7 +41944,7 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot, only) {
|
|
|
41139
41944
|
}
|
|
41140
41945
|
}
|
|
41141
41946
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
41142
|
-
if (!
|
|
41947
|
+
if (!fs18.existsSync(indexPath)) return;
|
|
41143
41948
|
const raw = readYamlFile(indexPath);
|
|
41144
41949
|
mutate(raw);
|
|
41145
41950
|
raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -41226,7 +42031,7 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
|
|
|
41226
42031
|
if (movedImplementation) {
|
|
41227
42032
|
const oldFile = getImplementationPath(movedImplementation.id);
|
|
41228
42033
|
if (path25.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path25.resolve(oldFile)) {
|
|
41229
|
-
|
|
42034
|
+
fs18.unlinkSync(oldFile);
|
|
41230
42035
|
}
|
|
41231
42036
|
saveImplementationSpec({ ...movedImplementation, id: implementationId, previousIds: traced(movedImplementation.previousIds, movedImplementation.id) });
|
|
41232
42037
|
}
|
|
@@ -41283,17 +42088,17 @@ function keepPublicNames(specsDir, derives, dryRun = false) {
|
|
|
41283
42088
|
return { names: [...new Set(names)], specs };
|
|
41284
42089
|
}
|
|
41285
42090
|
function removeSpecFile2(file, id) {
|
|
41286
|
-
if (!
|
|
42091
|
+
if (!fs18.existsSync(file)) return;
|
|
41287
42092
|
try {
|
|
41288
42093
|
if (readYamlFile(file)?.id !== id) return;
|
|
41289
42094
|
} catch {
|
|
41290
42095
|
return;
|
|
41291
42096
|
}
|
|
41292
|
-
|
|
42097
|
+
fs18.unlinkSync(file);
|
|
41293
42098
|
const specsRoot = path25.resolve(aiPathsAt(getProjectRoot()).specsDir());
|
|
41294
42099
|
for (let dir = path25.dirname(path25.resolve(file)); dir !== specsRoot && isWithinDir2(specsRoot, dir); dir = path25.dirname(dir)) {
|
|
41295
|
-
if (
|
|
41296
|
-
|
|
42100
|
+
if (fs18.readdirSync(dir).length > 0) break;
|
|
42101
|
+
fs18.rmdirSync(dir);
|
|
41297
42102
|
}
|
|
41298
42103
|
}
|
|
41299
42104
|
var METHOD_CASINGS = {
|
|
@@ -41340,8 +42145,9 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
41340
42145
|
);
|
|
41341
42146
|
}
|
|
41342
42147
|
const casing = methodCasingOf(component);
|
|
41343
|
-
|
|
41344
|
-
|
|
42148
|
+
const dictated = loadInterfaceSpecs().some((i) => i.component === componentId && i.implements !== void 0);
|
|
42149
|
+
if (dictated ? !IDENTIFIER.test(newName) : !methodNameFits(newName, casing)) {
|
|
42150
|
+
throw new WaironError(dictated ? `invalid-name: "${newName}" is not an identifier (a letter or underscore, then letters, digits or underscores).` : `invalid-name: "${newName}" is not an identifier in this tree's method casing (${casing}).`);
|
|
41345
42151
|
}
|
|
41346
42152
|
const contracts = loadInterfaceSpecs().filter((i) => i.component === componentId);
|
|
41347
42153
|
const moving = contracts.filter((i) => i.methods.some((m) => m.name === methodName));
|
|
@@ -41707,6 +42513,64 @@ function renameField(typeId, field, newName) {
|
|
|
41707
42513
|
invalidateSpecCache();
|
|
41708
42514
|
return { type: qualifiedTypeId(type), from: field, to: newName, rewritten };
|
|
41709
42515
|
}
|
|
42516
|
+
function renameParam(componentId, methodName, param, newName) {
|
|
42517
|
+
const component = loadComponentSpec(componentId);
|
|
42518
|
+
if (!component) throw new WaironError(`component-missing: no component has the id "${componentId}".`);
|
|
42519
|
+
if (componentId.includes("::")) {
|
|
42520
|
+
throw new WaironError(
|
|
42521
|
+
`chained-component: "${componentId}" lives in another project; rename its parameter from that project's own root. ${memberRootWay(componentId, `sdd_rename_param ${componentId.slice(componentId.lastIndexOf("::") + 2)} ${methodName} ${param} <new name>`)}`
|
|
42522
|
+
);
|
|
42523
|
+
}
|
|
42524
|
+
const contracts = loadInterfaceSpecs().filter((i) => i.component === componentId);
|
|
42525
|
+
const moving = contracts.filter((i) => i.methods.some((m) => m.name === methodName));
|
|
42526
|
+
if (moving.length === 0) throw new WaironError(`method-missing: no contract of "${componentId}" declares the method "${methodName}".`);
|
|
42527
|
+
for (const contract of moving) {
|
|
42528
|
+
const method = contract.methods.find((m) => m.name === methodName);
|
|
42529
|
+
const params = method.params ?? [];
|
|
42530
|
+
if (method.signatureFrom !== void 0) {
|
|
42531
|
+
throw new WaironError(`param-missing: "${contract.id}.${methodName}" takes its parameters from ${method.signatureFrom}; rename the parameter there.`);
|
|
42532
|
+
}
|
|
42533
|
+
if (!params.some((p) => p.name === param)) {
|
|
42534
|
+
throw new WaironError(`param-missing: "${contract.id}.${methodName}" declares no parameter "${param}"${params.length ? ` (it declares ${params.map((p) => p.name).join(", ")})` : ""}.`);
|
|
42535
|
+
}
|
|
42536
|
+
if (params.some((p) => p.name === newName && p.name !== param)) {
|
|
42537
|
+
throw new WaironError(`name-taken: "${contract.id}.${methodName}" already has a parameter "${newName}".`);
|
|
42538
|
+
}
|
|
42539
|
+
const retired = params.find((p) => p.name !== param && (p.previousNames ?? []).includes(newName));
|
|
42540
|
+
if (retired) {
|
|
42541
|
+
throw new WaironError(`name-retired: parameter "${retired.name}" of "${contract.id}.${methodName}" lists "${newName}" in its previousNames \u2014 the name is retired on this method. Unsetting that previousNames releases it.`);
|
|
42542
|
+
}
|
|
42543
|
+
}
|
|
42544
|
+
if (!IDENTIFIER.test(newName)) {
|
|
42545
|
+
throw new WaironError(`invalid-name: "${newName}" is not an identifier (a letter or underscore, then letters, digits or underscores).`);
|
|
42546
|
+
}
|
|
42547
|
+
const variables = projectConfigRepository.load()?.rules?.naming?.variables;
|
|
42548
|
+
const dictated = contracts.some((i) => i.implements !== void 0);
|
|
42549
|
+
if (variables && !dictated && !methodNameFits(newName, variables)) {
|
|
42550
|
+
throw new WaironError(`invalid-name: "${newName}" does not match this tree's parameter naming (rules.naming.variables: ${variables}).`);
|
|
42551
|
+
}
|
|
42552
|
+
if (newName === param) return { component: componentId, method: methodName, from: param, to: newName, movedIn: [], rewritten: [] };
|
|
42553
|
+
assertSpecsInReach(moving.map((i) => ({ kind: "interface", id: i.id })), `renaming parameter "${param}" of "${componentId}.${methodName}"`, false);
|
|
42554
|
+
const rewritten = [];
|
|
42555
|
+
const placeholder = `{${param}}`;
|
|
42556
|
+
for (const contract of moving) {
|
|
42557
|
+
saveInterfaceSpec({
|
|
42558
|
+
...contract,
|
|
42559
|
+
methods: contract.methods.map((m) => {
|
|
42560
|
+
if (m.name !== methodName) return m;
|
|
42561
|
+
const params = (m.params ?? []).map((p) => p.name === param ? { ...p, name: newName, previousNames: [...p.previousNames ?? [], param] } : p);
|
|
42562
|
+
let endpoint = m.endpoint;
|
|
42563
|
+
if (endpoint && endpoint.transport === "HTTP" && endpoint.path.includes(placeholder)) {
|
|
42564
|
+
endpoint = { ...endpoint, path: endpoint.path.split(placeholder).join(`{${newName}}`) };
|
|
42565
|
+
rewritten.push(`${contract.id}.${methodName}: {${param}} -> {${newName}}`);
|
|
42566
|
+
}
|
|
42567
|
+
return { ...m, params, ...endpoint ? { endpoint } : {} };
|
|
42568
|
+
})
|
|
42569
|
+
});
|
|
42570
|
+
}
|
|
42571
|
+
invalidateSpecCache();
|
|
42572
|
+
return { component: componentId, method: methodName, from: param, to: newName, movedIn: moving.map((i) => i.id), rewritten };
|
|
42573
|
+
}
|
|
41710
42574
|
function typeFileOf(id, type) {
|
|
41711
42575
|
return getTypePath(id, type.subsystem, type.group);
|
|
41712
42576
|
}
|
|
@@ -41733,7 +42597,7 @@ function externalSourceOf(declaration2) {
|
|
|
41733
42597
|
}
|
|
41734
42598
|
|
|
41735
42599
|
// src/core/detection.ts
|
|
41736
|
-
var
|
|
42600
|
+
var fs19 = __toESM(require("fs"));
|
|
41737
42601
|
var path26 = __toESM(require("path"));
|
|
41738
42602
|
var PACKAGE_MARKERS = [
|
|
41739
42603
|
"package.json",
|
|
@@ -41785,7 +42649,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
|
41785
42649
|
});
|
|
41786
42650
|
}
|
|
41787
42651
|
function parseGitmodules(filePath) {
|
|
41788
|
-
const content =
|
|
42652
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
41789
42653
|
const entries = [];
|
|
41790
42654
|
let current2 = {};
|
|
41791
42655
|
for (const line of content.split("\n")) {
|
|
@@ -41808,7 +42672,7 @@ function parseGitmodules(filePath) {
|
|
|
41808
42672
|
}
|
|
41809
42673
|
function detectGitSubmodules(projectRoot) {
|
|
41810
42674
|
const gitmodulesPath = path26.join(projectRoot, ".gitmodules");
|
|
41811
|
-
if (!
|
|
42675
|
+
if (!fs19.existsSync(gitmodulesPath)) return [];
|
|
41812
42676
|
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
41813
42677
|
suggestedId: pathToId(entry.path),
|
|
41814
42678
|
suggestedName: pathToName(entry.path),
|
|
@@ -41826,7 +42690,7 @@ function walkForGit(projectRoot, currentDir, depth, results) {
|
|
|
41826
42690
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
41827
42691
|
let entries;
|
|
41828
42692
|
try {
|
|
41829
|
-
entries =
|
|
42693
|
+
entries = fs19.readdirSync(currentDir, { withFileTypes: true });
|
|
41830
42694
|
} catch {
|
|
41831
42695
|
return;
|
|
41832
42696
|
}
|
|
@@ -41837,7 +42701,7 @@ function walkForGit(projectRoot, currentDir, depth, results) {
|
|
|
41837
42701
|
const relPath = normalizePath(path26.relative(projectRoot, fullPath));
|
|
41838
42702
|
if (relPath === "" || relPath === ".") continue;
|
|
41839
42703
|
const gitPath = path26.join(fullPath, ".git");
|
|
41840
|
-
if (
|
|
42704
|
+
if (fs19.existsSync(gitPath)) {
|
|
41841
42705
|
results.push({
|
|
41842
42706
|
suggestedId: pathToId(relPath),
|
|
41843
42707
|
suggestedName: pathToName(relPath),
|
|
@@ -41859,7 +42723,7 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
41859
42723
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
41860
42724
|
let entries;
|
|
41861
42725
|
try {
|
|
41862
|
-
entries =
|
|
42726
|
+
entries = fs19.readdirSync(currentDir, { withFileTypes: true });
|
|
41863
42727
|
} catch {
|
|
41864
42728
|
return;
|
|
41865
42729
|
}
|
|
@@ -41869,7 +42733,7 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
41869
42733
|
const fullPath = path26.join(currentDir, entry.name);
|
|
41870
42734
|
const relPath = normalizePath(path26.relative(projectRoot, fullPath));
|
|
41871
42735
|
if (relPath === "" || relPath === ".") continue;
|
|
41872
|
-
const hasMarker = PACKAGE_MARKERS.some((m) =>
|
|
42736
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs19.existsSync(path26.join(fullPath, m)));
|
|
41873
42737
|
if (hasMarker) {
|
|
41874
42738
|
results.push({
|
|
41875
42739
|
suggestedId: pathToId(relPath),
|
|
@@ -41883,8 +42747,8 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
41883
42747
|
}
|
|
41884
42748
|
}
|
|
41885
42749
|
function pathToId(relPath) {
|
|
41886
|
-
const
|
|
41887
|
-
return
|
|
42750
|
+
const basename14 = path26.basename(relPath);
|
|
42751
|
+
return basename14.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
41888
42752
|
}
|
|
41889
42753
|
function pathToName(relPath) {
|
|
41890
42754
|
const id = pathToId(relPath);
|
|
@@ -41895,7 +42759,7 @@ function normalizePath(p) {
|
|
|
41895
42759
|
}
|
|
41896
42760
|
|
|
41897
42761
|
// src/core/variants.ts
|
|
41898
|
-
var
|
|
42762
|
+
var fs20 = __toESM(require("fs"));
|
|
41899
42763
|
var os4 = __toESM(require("os"));
|
|
41900
42764
|
var path27 = __toESM(require("path"));
|
|
41901
42765
|
var import_zod11 = require("zod");
|
|
@@ -41921,7 +42785,7 @@ function builtinVariantsDir() {
|
|
|
41921
42785
|
path27.resolve(__dirname, "templates", "variants")
|
|
41922
42786
|
// dist (library entry)
|
|
41923
42787
|
];
|
|
41924
|
-
return candidates.find((dir) =>
|
|
42788
|
+
return candidates.find((dir) => fs20.existsSync(dir)) ?? candidates[0];
|
|
41925
42789
|
}
|
|
41926
42790
|
function projectVariantsDir() {
|
|
41927
42791
|
return path27.join(getProjectRoot(), ".wai", "variants");
|
|
@@ -41929,7 +42793,7 @@ function projectVariantsDir() {
|
|
|
41929
42793
|
function variantFiles(dir) {
|
|
41930
42794
|
let entries;
|
|
41931
42795
|
try {
|
|
41932
|
-
entries =
|
|
42796
|
+
entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
41933
42797
|
} catch {
|
|
41934
42798
|
return [];
|
|
41935
42799
|
}
|
|
@@ -42045,7 +42909,7 @@ function rebasedText(content, fromBase, toBase, rebasedContent) {
|
|
|
42045
42909
|
|
|
42046
42910
|
// src/core/agent_resolver.ts
|
|
42047
42911
|
var path29 = __toESM(require("path"));
|
|
42048
|
-
var
|
|
42912
|
+
var fs21 = __toESM(require("fs"));
|
|
42049
42913
|
|
|
42050
42914
|
// src/core/templates.ts
|
|
42051
42915
|
var path28 = __toESM(require("path"));
|
|
@@ -42215,7 +43079,7 @@ function resolveBudget(profile, config, agentId) {
|
|
|
42215
43079
|
// src/core/agent_resolver.ts
|
|
42216
43080
|
var projectFilesCache = /* @__PURE__ */ new Map();
|
|
42217
43081
|
function listFilesRecursiveSafe(dirPath, ext) {
|
|
42218
|
-
if (!
|
|
43082
|
+
if (!fs21.existsSync(dirPath)) return [];
|
|
42219
43083
|
const nameLower = path29.basename(dirPath).toLowerCase();
|
|
42220
43084
|
const IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
42221
43085
|
"node_modules",
|
|
@@ -42231,7 +43095,7 @@ function listFilesRecursiveSafe(dirPath, ext) {
|
|
|
42231
43095
|
".vscode"
|
|
42232
43096
|
]);
|
|
42233
43097
|
if (IGNORED_DIRS.has(nameLower)) return [];
|
|
42234
|
-
const entries =
|
|
43098
|
+
const entries = fs21.readdirSync(dirPath, { withFileTypes: true });
|
|
42235
43099
|
const files = [];
|
|
42236
43100
|
for (const entry of entries) {
|
|
42237
43101
|
const fullPath = path29.join(dirPath, entry.name);
|
|
@@ -42628,7 +43492,7 @@ function composeAgentBrief(agentId) {
|
|
|
42628
43492
|
const shared = ownFence && isSubsystemOwner(record) ? sharedProjectFiles(ownFence) : [];
|
|
42629
43493
|
const codeFence = ownFence ? [...ownFence, ...shared] : ownFence;
|
|
42630
43494
|
if (codeFence) {
|
|
42631
|
-
const shown2 = (p) => /[*?]/.test(p) ||
|
|
43495
|
+
const shown2 = (p) => /[*?]/.test(p) || fs21.existsSync(path29.resolve(getProjectRoot(), p)) ? `- \`${p}\`` : `- \`${p}\` (planned \u2014 create it)`;
|
|
42632
43496
|
const own2 = ownFence ?? [];
|
|
42633
43497
|
const sharedSection = shared.length > 0 ? `
|
|
42634
43498
|
Shared with the other subsystem owners (the project's setup and shared code; coordinate edits):
|
|
@@ -42756,18 +43620,18 @@ function memberHop(agentId) {
|
|
|
42756
43620
|
const alias = agentId.slice(0, at);
|
|
42757
43621
|
const member = graph().nodes.find((n) => n.parent === "" && n.mountAlias === alias);
|
|
42758
43622
|
if (!member) return null;
|
|
42759
|
-
const
|
|
42760
|
-
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative:
|
|
43623
|
+
const relative22 = path29.relative(getProjectRoot(), member.directory).split(path29.sep).join("/");
|
|
43624
|
+
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative: relative22 };
|
|
42761
43625
|
}
|
|
42762
|
-
function throughMount(brief, alias,
|
|
42763
|
-
const under = (p) => path29.posix.join(
|
|
43626
|
+
function throughMount(brief, alias, relative22) {
|
|
43627
|
+
const under = (p) => path29.posix.join(relative22, p);
|
|
42764
43628
|
return {
|
|
42765
43629
|
...brief,
|
|
42766
43630
|
agentId: `${alias}::${brief.agentId}`,
|
|
42767
43631
|
ownedPaths: brief.ownedPaths.map(under),
|
|
42768
43632
|
...brief.codeFence ? { codeFence: brief.codeFence.map(under) } : {},
|
|
42769
43633
|
...brief.readPaths ? { readPaths: brief.readPaths.map(under) } : {},
|
|
42770
|
-
root: brief.root ? under(brief.root) :
|
|
43634
|
+
root: brief.root ? under(brief.root) : relative22
|
|
42771
43635
|
};
|
|
42772
43636
|
}
|
|
42773
43637
|
function loadRegistry() {
|
|
@@ -42804,7 +43668,7 @@ function sharedProjectFiles(fence) {
|
|
|
42804
43668
|
if (!claimed.has(file) && !out.includes(file)) out.push(file);
|
|
42805
43669
|
};
|
|
42806
43670
|
for (const file of [...SHARED_SETUP_FILES, ...SHARED_ROOT_FILES]) {
|
|
42807
|
-
if (
|
|
43671
|
+
if (fs21.existsSync(path29.join(root, file))) add2(file);
|
|
42808
43672
|
}
|
|
42809
43673
|
for (const type of loadTypeSpecs()) {
|
|
42810
43674
|
if (type.subsystem) continue;
|
|
@@ -43097,7 +43961,7 @@ function resolveTargetConfig(agentTarget, projectConfig) {
|
|
|
43097
43961
|
}
|
|
43098
43962
|
|
|
43099
43963
|
// src/core/treetransfer.ts
|
|
43100
|
-
var
|
|
43964
|
+
var fs22 = __toESM(require("fs"));
|
|
43101
43965
|
var path34 = __toESM(require("path"));
|
|
43102
43966
|
var sdkPortal = __toESM(require_dist());
|
|
43103
43967
|
var STAGING_PREFIX = ".wai-staging-";
|
|
@@ -43134,8 +43998,8 @@ function hasAuthoredSpecs(root) {
|
|
|
43134
43998
|
}
|
|
43135
43999
|
function createStagingDir(destDir) {
|
|
43136
44000
|
const root = path34.resolve(destDir);
|
|
43137
|
-
|
|
43138
|
-
const stagingDir =
|
|
44001
|
+
fs22.mkdirSync(root, { recursive: true });
|
|
44002
|
+
const stagingDir = fs22.mkdtempSync(path34.join(root, STAGING_PREFIX));
|
|
43139
44003
|
return stagingDir;
|
|
43140
44004
|
}
|
|
43141
44005
|
function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
@@ -43143,22 +44007,22 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
43143
44007
|
const backupDir = path34.join(root, BACKUP_DIR, timestampSlug());
|
|
43144
44008
|
let backedUp = false;
|
|
43145
44009
|
for (const rootRel of roots) {
|
|
43146
|
-
const
|
|
43147
|
-
const stagedSpecDir = path34.join(stagingDir,
|
|
43148
|
-
if (!
|
|
43149
|
-
const liveSpecDir = liveSpecDirFor(path34.join(root,
|
|
43150
|
-
if (backupExisting && liveSpecDir &&
|
|
43151
|
-
const backupTarget = path34.join(backupDir,
|
|
43152
|
-
|
|
43153
|
-
|
|
44010
|
+
const relative22 = rootRel === "." ? "" : rootRel;
|
|
44011
|
+
const stagedSpecDir = path34.join(stagingDir, relative22, ".wai");
|
|
44012
|
+
if (!fs22.existsSync(stagedSpecDir)) continue;
|
|
44013
|
+
const liveSpecDir = liveSpecDirFor(path34.join(root, relative22));
|
|
44014
|
+
if (backupExisting && liveSpecDir && fs22.existsSync(liveSpecDir)) {
|
|
44015
|
+
const backupTarget = path34.join(backupDir, relative22, path34.basename(liveSpecDir));
|
|
44016
|
+
fs22.mkdirSync(path34.dirname(backupTarget), { recursive: true });
|
|
44017
|
+
fs22.renameSync(liveSpecDir, backupTarget);
|
|
43154
44018
|
backedUp = true;
|
|
43155
|
-
} else if (liveSpecDir &&
|
|
43156
|
-
|
|
44019
|
+
} else if (liveSpecDir && fs22.existsSync(liveSpecDir)) {
|
|
44020
|
+
fs22.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
43157
44021
|
}
|
|
43158
|
-
const target = path34.join(root,
|
|
43159
|
-
|
|
43160
|
-
if (
|
|
43161
|
-
|
|
44022
|
+
const target = path34.join(root, relative22, ".wai");
|
|
44023
|
+
fs22.mkdirSync(path34.dirname(target), { recursive: true });
|
|
44024
|
+
if (fs22.existsSync(target)) fs22.rmSync(target, { recursive: true, force: true });
|
|
44025
|
+
fs22.renameSync(stagedSpecDir, target);
|
|
43162
44026
|
}
|
|
43163
44027
|
if (backedUp) {
|
|
43164
44028
|
return backupDir;
|
|
@@ -43167,7 +44031,7 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
43167
44031
|
}
|
|
43168
44032
|
function discardStagingDir(stagingDir) {
|
|
43169
44033
|
try {
|
|
43170
|
-
|
|
44034
|
+
fs22.rmSync(stagingDir, { recursive: true, force: true });
|
|
43171
44035
|
} catch {
|
|
43172
44036
|
}
|
|
43173
44037
|
}
|
|
@@ -43244,9 +44108,9 @@ function importSpecTree(archive, options) {
|
|
|
43244
44108
|
}
|
|
43245
44109
|
function liveSpecDirFor(rootDir) {
|
|
43246
44110
|
const modern = path34.join(rootDir, ".wai");
|
|
43247
|
-
if (
|
|
44111
|
+
if (fs22.existsSync(modern)) return modern;
|
|
43248
44112
|
const legacy = path34.join(rootDir, ".wairon");
|
|
43249
|
-
if (
|
|
44113
|
+
if (fs22.existsSync(legacy)) return legacy;
|
|
43250
44114
|
return null;
|
|
43251
44115
|
}
|
|
43252
44116
|
function timestampSlug() {
|
|
@@ -43484,8 +44348,8 @@ var RETIRED_CODES = /* @__PURE__ */ new Set([
|
|
|
43484
44348
|
var RENAMED_CODES = { MISSING_PORTAL_TYPE: "MISSING_PORTAL_TRANSPORT" };
|
|
43485
44349
|
var WRITE_ORDER = ["component", "interface", "implementation", "subsystem", "type", "system"];
|
|
43486
44350
|
var own = (id) => !id.includes("::");
|
|
43487
|
-
function liesUnder(
|
|
43488
|
-
return
|
|
44351
|
+
function liesUnder(path50, prefix) {
|
|
44352
|
+
return path50 === prefix || path50.startsWith(prefix + "/");
|
|
43489
44353
|
}
|
|
43490
44354
|
function prefixPhrase(prefixes) {
|
|
43491
44355
|
return prefixes.length > 0 ? prefixes.map((p) => `"${p}"`).join(", ") : "no prefix";
|
|
@@ -43776,12 +44640,15 @@ function designDigest(kind, content) {
|
|
|
43776
44640
|
if (memo2 !== void 0) return memo2;
|
|
43777
44641
|
const parsed = parsedSpec(content);
|
|
43778
44642
|
if (!parsed) return digest(content);
|
|
43779
|
-
const out = viewDigest(designViewOf(kind, parsed));
|
|
44643
|
+
const out = viewDigest(designViewOf(kind, schemaRead(kind, parsed)));
|
|
43780
44644
|
if (designDigestMemo.size > 2e4) designDigestMemo.clear();
|
|
43781
44645
|
designDigestMemo.set(key2, out);
|
|
43782
44646
|
return out;
|
|
43783
44647
|
}
|
|
43784
44648
|
var STATUS_CANDIDATES = [void 0, "draft", "design", "complete"];
|
|
44649
|
+
function schemaRead(kind, parsed) {
|
|
44650
|
+
return kind ? asSchemaReads(kind, parsed) : parsed;
|
|
44651
|
+
}
|
|
43785
44652
|
function designViewOf(kind, parsed) {
|
|
43786
44653
|
switch (kind) {
|
|
43787
44654
|
case "implementation":
|
|
@@ -43813,10 +44680,12 @@ function viewDigest(view3) {
|
|
|
43813
44680
|
function statusApprovedIn(kind, content, recorded) {
|
|
43814
44681
|
const parsed = parsedSpec(content);
|
|
43815
44682
|
if (!parsed) return void 0;
|
|
43816
|
-
const
|
|
43817
|
-
for (const
|
|
43818
|
-
const
|
|
43819
|
-
|
|
44683
|
+
const views = [designViewOf(kind, schemaRead(kind, parsed)), designViewOf(kind, parsed)];
|
|
44684
|
+
for (const view3 of views) {
|
|
44685
|
+
for (const status of STATUS_CANDIDATES) {
|
|
44686
|
+
const candidate = status === void 0 ? view3 : { ...view3, status };
|
|
44687
|
+
if (viewDigest(candidate) === recorded) return status ?? null;
|
|
44688
|
+
}
|
|
43820
44689
|
}
|
|
43821
44690
|
return void 0;
|
|
43822
44691
|
}
|
|
@@ -43908,19 +44777,31 @@ function diffAgainstApproval(root = getProjectRoot()) {
|
|
|
43908
44777
|
for (const rel2 of Object.keys(approved)) {
|
|
43909
44778
|
if (current2[rel2] === void 0) removed.push(rel2);
|
|
43910
44779
|
}
|
|
44780
|
+
const moved = [];
|
|
44781
|
+
const unpaired = [...removed].sort();
|
|
44782
|
+
for (const rel2 of [...added].sort()) {
|
|
44783
|
+
const at = unpaired.findIndex((old2) => approved[old2] === current2[rel2]);
|
|
44784
|
+
if (at < 0) continue;
|
|
44785
|
+
const [old] = unpaired.splice(at, 1);
|
|
44786
|
+
moved.push(`${old} -> ${rel2}`);
|
|
44787
|
+
added.splice(added.indexOf(rel2), 1);
|
|
44788
|
+
removed.splice(removed.indexOf(old), 1);
|
|
44789
|
+
unchangedPaths.push(rel2);
|
|
44790
|
+
}
|
|
43911
44791
|
return {
|
|
43912
44792
|
added: added.sort(),
|
|
43913
44793
|
changed: changed.sort(),
|
|
43914
44794
|
removed: removed.sort(),
|
|
43915
|
-
unchangedPaths: unchangedPaths.sort()
|
|
44795
|
+
unchangedPaths: unchangedPaths.sort(),
|
|
44796
|
+
moved
|
|
43916
44797
|
};
|
|
43917
44798
|
}
|
|
43918
44799
|
function approvalRecord(root = getProjectRoot()) {
|
|
43919
44800
|
return readLockRecordAt(root);
|
|
43920
44801
|
}
|
|
43921
44802
|
function settledSpecPaths(root = getProjectRoot()) {
|
|
43922
|
-
const
|
|
43923
|
-
return
|
|
44803
|
+
const diff3 = diffAgainstApproval(root);
|
|
44804
|
+
return diff3 ? new Set(diff3.unchangedPaths) : null;
|
|
43924
44805
|
}
|
|
43925
44806
|
var REEXPRESSED_BY = "wairon doctor --fix";
|
|
43926
44807
|
function reexpress(gate) {
|
|
@@ -43941,8 +44822,8 @@ function reexpress(gate) {
|
|
|
43941
44822
|
};
|
|
43942
44823
|
}
|
|
43943
44824
|
if (fromReading !== "content" || !record.specs) return null;
|
|
43944
|
-
const
|
|
43945
|
-
if (!
|
|
44825
|
+
const diff3 = diffAgainstApproval();
|
|
44826
|
+
if (!diff3 || diffSize(diff3) > 0) return null;
|
|
43946
44827
|
return { ...record, specs: captureApprovedSpecs(), specsReading: "design", format: 3, reexpressed };
|
|
43947
44828
|
}
|
|
43948
44829
|
function approvedStatuses(record, root = getProjectRoot()) {
|
|
@@ -43964,11 +44845,11 @@ function approvalVerdict(approvals) {
|
|
|
43964
44845
|
const approval = approvalRecord();
|
|
43965
44846
|
if (!approval) return quiet;
|
|
43966
44847
|
const by = describeApprover(approval.lockedBy);
|
|
43967
|
-
const
|
|
43968
|
-
const notes = memberNotes(approvals ?? [], approval,
|
|
44848
|
+
const diff3 = diffAgainstApproval();
|
|
44849
|
+
const notes = memberNotes(approvals ?? [], approval, diff3);
|
|
43969
44850
|
const pinDrift = notes.drift;
|
|
43970
44851
|
const childNote = notes.text;
|
|
43971
|
-
if (!
|
|
44852
|
+
if (!diff3) {
|
|
43972
44853
|
return {
|
|
43973
44854
|
text: `
|
|
43974
44855
|
Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec approval, so drift is only visible at whole-tree level. Re-lock to record it.${childNote}
|
|
@@ -43979,7 +44860,7 @@ Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec appro
|
|
|
43979
44860
|
const own2 = (approvals ?? []).find((a) => a.key === "");
|
|
43980
44861
|
const carry = isContentReadingRecord(approval) && own2?.state === "approved" ? `
|
|
43981
44862
|
${REEXPRESS_HINT}` : "";
|
|
43982
|
-
if (diffSize(
|
|
44863
|
+
if (diffSize(diff3) === 0) {
|
|
43983
44864
|
return {
|
|
43984
44865
|
text: `
|
|
43985
44866
|
Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}${carry}
|
|
@@ -43988,21 +44869,21 @@ Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${child
|
|
|
43988
44869
|
};
|
|
43989
44870
|
}
|
|
43990
44871
|
const parts = [];
|
|
43991
|
-
if (
|
|
43992
|
-
if (
|
|
43993
|
-
if (
|
|
43994
|
-
const named2 = [...
|
|
43995
|
-
const rest = diffSize(
|
|
43996
|
-
const n = diffSize(
|
|
44872
|
+
if (diff3.changed.length) parts.push(`${diff3.changed.length} changed`);
|
|
44873
|
+
if (diff3.added.length) parts.push(`${diff3.added.length} added`);
|
|
44874
|
+
if (diff3.removed.length) parts.push(`${diff3.removed.length} removed`);
|
|
44875
|
+
const named2 = [...diff3.changed, ...diff3.added, ...diff3.removed].slice(0, 5);
|
|
44876
|
+
const rest = diffSize(diff3) - named2.length;
|
|
44877
|
+
const n = diffSize(diff3);
|
|
43997
44878
|
const noun = `${n} spec${n === 1 ? "" : "s"}`;
|
|
43998
|
-
const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(
|
|
44879
|
+
const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(diff3)).length + 1)} since approval` : `${noun} changed since approval (${parts.join(", ")})`;
|
|
43999
44880
|
return {
|
|
44000
44881
|
text: `
|
|
44001
44882
|
${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
44002
44883
|
` + named2.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
|
|
44003
44884
|
\u2026 and ${rest} more` : "") + childNote + carry + "\n",
|
|
44004
44885
|
drifted: true,
|
|
44005
|
-
moved: [...
|
|
44886
|
+
moved: [...diff3.changed.map((p) => `~ ${p}`), ...diff3.added.map((p) => `+ ${p}`), ...diff3.removed.map((p) => `- ${p}`)]
|
|
44006
44887
|
};
|
|
44007
44888
|
} catch {
|
|
44008
44889
|
return quiet;
|
|
@@ -44015,7 +44896,7 @@ var REEXPRESS_HINT = "This approval predates lock format 3 and is read in its or
|
|
|
44015
44896
|
var FORMAT2_STALE_NOTE = "it was taken before code linkage (sourcePath, symbol, simPath and the like) left the approval, and something it covered moved since \u2014 the design, or only code linkage, which this record can no longer tell apart. One `wairon lock` clears it for good: from then on linkage never drifts the approval";
|
|
44016
44897
|
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in this release (members' composition subjects, `composition`; code conformance moved beside the claim)";
|
|
44017
44898
|
var GATE_MOVED_NOTE = "no own spec file changed since it was taken \u2014 not even its code linkage \u2014 so what moved is the gate it was judged under: the design rules this wairon release judges by, or the project's rule tuning, `composition`, network declaration, consumed contracts or a member's approval. One `wairon lock` re-approves the unchanged design under the current gate";
|
|
44018
|
-
function memberNotes(approvals, record,
|
|
44899
|
+
function memberNotes(approvals, record, diff3) {
|
|
44019
44900
|
const own2 = approvals.find((a) => a.key === "");
|
|
44020
44901
|
const direct = approvals.filter((a) => a.parent === "" && a.as !== "part");
|
|
44021
44902
|
const moved = direct.filter((a) => a.pinned === "moved");
|
|
@@ -44023,7 +44904,7 @@ function memberNotes(approvals, record, diff2) {
|
|
|
44023
44904
|
const lines = [];
|
|
44024
44905
|
if (own2?.upgraded) {
|
|
44025
44906
|
const by = record?.validatorVersion ? ` (taken by wairon ${record.validatorVersion})` : "";
|
|
44026
|
-
lines.push(
|
|
44907
|
+
lines.push(diff3 && diffSize(diff3) === 0 ? `This approval no longer matches${by}: ${GATE_MOVED_NOTE}.` : record && record.format === 2 ? `This approval no longer matches: ${FORMAT2_STALE_NOTE}.` : `This approval was taken under an earlier gate identity \u2014 ${GATE_UPGRADE_NOTE}. Re-lock once.`);
|
|
44027
44908
|
}
|
|
44028
44909
|
if (moved.length) {
|
|
44029
44910
|
lines.push(`${moved.length} member pin(s) moved since approval: ${moved.map((a) => a.alias ?? a.key).join(", ")} \u2014 re-lock to approve them.`);
|
|
@@ -44075,7 +44956,7 @@ function localApprover() {
|
|
|
44075
44956
|
}
|
|
44076
44957
|
|
|
44077
44958
|
// src/utils/ai-guide.ts
|
|
44078
|
-
var
|
|
44959
|
+
var fs23 = __toESM(require("fs"));
|
|
44079
44960
|
var os6 = __toESM(require("os"));
|
|
44080
44961
|
var path36 = __toESM(require("path"));
|
|
44081
44962
|
|
|
@@ -44165,10 +45046,10 @@ ${versionStamp()}
|
|
|
44165
45046
|
${body}
|
|
44166
45047
|
${GUIDE_MARKER_END}
|
|
44167
45048
|
`;
|
|
44168
|
-
const existing =
|
|
45049
|
+
const existing = fs23.existsSync(filePath) ? fs23.readFileSync(filePath, "utf-8") : "";
|
|
44169
45050
|
const stripped = stripGuideSection(existing);
|
|
44170
45051
|
const newContent = stripped.trimEnd() + section;
|
|
44171
|
-
|
|
45052
|
+
fs23.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
44172
45053
|
writeFile(filePath, newContent);
|
|
44173
45054
|
}
|
|
44174
45055
|
function stripGuideSection(content) {
|
|
@@ -44213,7 +45094,7 @@ Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
|
|
|
44213
45094
|
writeFile(filePath, content);
|
|
44214
45095
|
} else if (targetType === "copilot") {
|
|
44215
45096
|
const filePath = path36.join(projectRoot, ".github", "copilot-instructions.md");
|
|
44216
|
-
|
|
45097
|
+
fs23.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
44217
45098
|
const content = `# Wairon SDD Project
|
|
44218
45099
|
|
|
44219
45100
|
This project uses the Wairon Spec-Driven Development (SDD) framework.
|
|
@@ -44244,6 +45125,22 @@ function reinjectLocalGuides(projectRoot, targetTypes) {
|
|
|
44244
45125
|
}
|
|
44245
45126
|
return written;
|
|
44246
45127
|
}
|
|
45128
|
+
function registerProjectServer(projectRoot) {
|
|
45129
|
+
const file = path36.join(projectRoot, ".mcp.json");
|
|
45130
|
+
let settings = {};
|
|
45131
|
+
if (fs23.existsSync(file)) {
|
|
45132
|
+
try {
|
|
45133
|
+
settings = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
45134
|
+
} catch {
|
|
45135
|
+
return null;
|
|
45136
|
+
}
|
|
45137
|
+
}
|
|
45138
|
+
const servers = settings.mcpServers ?? {};
|
|
45139
|
+
if (servers.wairon !== void 0) return null;
|
|
45140
|
+
settings.mcpServers = { ...servers, wairon: { command: "wairon", args: ["mcp", "serve"] } };
|
|
45141
|
+
writeFile(file, JSON.stringify(settings, null, 2) + "\n");
|
|
45142
|
+
return file;
|
|
45143
|
+
}
|
|
44247
45144
|
|
|
44248
45145
|
// src/core/context.ts
|
|
44249
45146
|
function contextDir(...segments) {
|
|
@@ -44612,8 +45509,14 @@ function getStatusReport(options = {}, decor) {
|
|
|
44612
45509
|
`;
|
|
44613
45510
|
}
|
|
44614
45511
|
const own2 = options.approvals?.find((a) => a.key === "");
|
|
44615
|
-
|
|
45512
|
+
const nothingDesigned = own2?.state === "never" && loadSubsystemSpecs().length === 0 && !(options.approvals ?? []).some((a) => a.key !== "" && a.parent === "" && a.as !== "part");
|
|
45513
|
+
if (own2 && nothingDesigned) {
|
|
45514
|
+
output += `${mark.layer("system", "Approval:")} nothing to approve yet \u2014 the tree holds the L0 and nothing below it (add a subsystem, then \`wairon lock\`)
|
|
44616
45515
|
`;
|
|
45516
|
+
} else if (own2) {
|
|
45517
|
+
output += `${mark.layer("system", "Approval:")} this project is ${OWN_APPROVAL_WORDS[own2.state]}${own2.upgraded ? " (locked under an earlier gate identity \u2014 re-lock once)" : ""}
|
|
45518
|
+
`;
|
|
45519
|
+
}
|
|
44617
45520
|
return { text: output, failed: false };
|
|
44618
45521
|
}
|
|
44619
45522
|
|
|
@@ -44706,7 +45609,7 @@ var logger = {
|
|
|
44706
45609
|
|
|
44707
45610
|
// src/core/skills.ts
|
|
44708
45611
|
var path38 = __toESM(require("path"));
|
|
44709
|
-
var
|
|
45612
|
+
var fs24 = __toESM(require("fs"));
|
|
44710
45613
|
|
|
44711
45614
|
// src/core/instructions.ts
|
|
44712
45615
|
function governingProfile() {
|
|
@@ -44846,6 +45749,22 @@ function packSkillId(skill) {
|
|
|
44846
45749
|
function packNamespace(pack) {
|
|
44847
45750
|
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
44848
45751
|
}
|
|
45752
|
+
function extensionsFor(builtin, packSkills) {
|
|
45753
|
+
return packSkills.filter((s) => s.extends === builtin && fs24.existsSync(s.sourcePath));
|
|
45754
|
+
}
|
|
45755
|
+
function composeBuiltinSkill(name, packSkills) {
|
|
45756
|
+
const srcPath = skillTemplatePath(name);
|
|
45757
|
+
const base = fs24.existsSync(srcPath) ? fs24.readFileSync(srcPath, "utf-8") : "";
|
|
45758
|
+
const sections = extensionsFor(name, packSkills);
|
|
45759
|
+
if (sections.length === 0) return base;
|
|
45760
|
+
const parts = [base.trimEnd()];
|
|
45761
|
+
for (const section of sections) {
|
|
45762
|
+
const body = fs24.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
45763
|
+
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
45764
|
+
}
|
|
45765
|
+
return `${parts.join("\n\n")}
|
|
45766
|
+
`;
|
|
45767
|
+
}
|
|
44849
45768
|
function readFrontmatter(raw, fallbackName) {
|
|
44850
45769
|
const match2 = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
44851
45770
|
const block2 = match2 ? match2[1] : "";
|
|
@@ -44861,6 +45780,63 @@ function builtinSkillsDir() {
|
|
|
44861
45780
|
function skillTemplatePath(name) {
|
|
44862
45781
|
return path38.join(builtinSkillsDir(), `${name}.md`);
|
|
44863
45782
|
}
|
|
45783
|
+
function skillDestPath(type, destDir, name) {
|
|
45784
|
+
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
45785
|
+
return path38.join(destDir, name, "SKILL.md");
|
|
45786
|
+
}
|
|
45787
|
+
return path38.join(destDir, `${name}.md`);
|
|
45788
|
+
}
|
|
45789
|
+
function skillsDirForTarget(type) {
|
|
45790
|
+
switch (type) {
|
|
45791
|
+
case "claude":
|
|
45792
|
+
return fromProjectRoot(".claude", "skills");
|
|
45793
|
+
case "gemini":
|
|
45794
|
+
// Gemini-based Antigravity
|
|
45795
|
+
case "agy":
|
|
45796
|
+
case "codex":
|
|
45797
|
+
return fromProjectRoot(".agents", "skills");
|
|
45798
|
+
case "cursor":
|
|
45799
|
+
return fromProjectRoot(".cursor", "skills");
|
|
45800
|
+
default:
|
|
45801
|
+
return null;
|
|
45802
|
+
}
|
|
45803
|
+
}
|
|
45804
|
+
function exportSddSkills(targetTypes) {
|
|
45805
|
+
const types = targetTypes ?? activeTargetTypes2();
|
|
45806
|
+
const destinations = [];
|
|
45807
|
+
const skipped = [];
|
|
45808
|
+
let fileCount = 0;
|
|
45809
|
+
const packSkills = loadProjectExtensions().skills;
|
|
45810
|
+
for (const type of types) {
|
|
45811
|
+
const destDir = skillsDirForTarget(type);
|
|
45812
|
+
if (!destDir) {
|
|
45813
|
+
skipped.push(type);
|
|
45814
|
+
continue;
|
|
45815
|
+
}
|
|
45816
|
+
ensureDir(destDir);
|
|
45817
|
+
destinations.push(destDir);
|
|
45818
|
+
for (const name of SKILL_NAMES) {
|
|
45819
|
+
if (!fs24.existsSync(skillTemplatePath(name))) continue;
|
|
45820
|
+
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
45821
|
+
const destPath = skillDestPath(type, destDir, name);
|
|
45822
|
+
ensureDir(path38.dirname(destPath));
|
|
45823
|
+
writeFileIfChanged(destPath, content);
|
|
45824
|
+
fileCount++;
|
|
45825
|
+
}
|
|
45826
|
+
for (const skill of packSkills) {
|
|
45827
|
+
if (skill.extends !== void 0) continue;
|
|
45828
|
+
if (!skill.targets.includes(type)) continue;
|
|
45829
|
+
if (!fs24.existsSync(skill.sourcePath)) continue;
|
|
45830
|
+
const id = packSkillId(skill);
|
|
45831
|
+
const content = fs24.readFileSync(skill.sourcePath, "utf-8");
|
|
45832
|
+
const destPath = skillDestPath(type, destDir, id);
|
|
45833
|
+
ensureDir(path38.dirname(destPath));
|
|
45834
|
+
writeFileIfChanged(destPath, content);
|
|
45835
|
+
fileCount++;
|
|
45836
|
+
}
|
|
45837
|
+
}
|
|
45838
|
+
return { destinations, fileCount, skipped };
|
|
45839
|
+
}
|
|
44864
45840
|
function activeTargetTypes2() {
|
|
44865
45841
|
const config = loadProjectConfig();
|
|
44866
45842
|
if (!config) return [];
|
|
@@ -44869,7 +45845,7 @@ function activeTargetTypes2() {
|
|
|
44869
45845
|
var SKILL_RESOURCE_SCHEME = "wairon-skill";
|
|
44870
45846
|
var RESOURCE_SKILL_IDS = [...SKILL_NAMES].sort();
|
|
44871
45847
|
function readSkillFrontmatter(name) {
|
|
44872
|
-
return readFrontmatter(
|
|
45848
|
+
return readFrontmatter(fs24.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
44873
45849
|
}
|
|
44874
45850
|
function listSkillResources() {
|
|
44875
45851
|
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
@@ -44883,9 +45859,9 @@ function listSkillResources() {
|
|
|
44883
45859
|
defaultForHostedMcp: true
|
|
44884
45860
|
};
|
|
44885
45861
|
});
|
|
44886
|
-
const pack = loadProjectExtensions().skills.filter((s) => s.extends === void 0 &&
|
|
45862
|
+
const pack = loadProjectExtensions().skills.filter((s) => s.extends === void 0 && fs24.existsSync(s.sourcePath)).map((skill) => {
|
|
44887
45863
|
const id = packSkillId(skill);
|
|
44888
|
-
const fm = readFrontmatter(
|
|
45864
|
+
const fm = readFrontmatter(fs24.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
44889
45865
|
return {
|
|
44890
45866
|
id,
|
|
44891
45867
|
name: fm.name,
|
|
@@ -44906,7 +45882,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
44906
45882
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
44907
45883
|
var import_zod12 = require("zod");
|
|
44908
45884
|
var import_types16 = require("@modelcontextprotocol/sdk/types.js");
|
|
44909
|
-
var
|
|
45885
|
+
var path49 = __toESM(require("path"));
|
|
44910
45886
|
var crypto10 = __toESM(require("crypto"));
|
|
44911
45887
|
|
|
44912
45888
|
// src/core/adapters/authoring-core.ts
|
|
@@ -45169,6 +46145,16 @@ function carryInto(restatement, existing, candidate) {
|
|
|
45169
46145
|
method.previousNames = prev.previousNames;
|
|
45170
46146
|
carried.push(`previousNames (${String(method.name)})`);
|
|
45171
46147
|
}
|
|
46148
|
+
for (const method of candidate.methods) {
|
|
46149
|
+
const prev = stored.find((p) => p.name === method.name);
|
|
46150
|
+
const prevParams = Array.isArray(prev?.params) ? prev.params : [];
|
|
46151
|
+
for (const param of Array.isArray(method.params) ? method.params : []) {
|
|
46152
|
+
const before = prevParams.find((p) => p.name === param.name);
|
|
46153
|
+
if (before?.previousNames === void 0 || param.previousNames !== void 0) continue;
|
|
46154
|
+
param.previousNames = before.previousNames;
|
|
46155
|
+
carried.push(`previousNames (param ${String(method.name)}.${String(param.name)})`);
|
|
46156
|
+
}
|
|
46157
|
+
}
|
|
45172
46158
|
}
|
|
45173
46159
|
if (restatement.kind === "type" && Array.isArray(candidate.fields)) {
|
|
45174
46160
|
const stored = existing.fields ?? [];
|
|
@@ -45382,6 +46368,9 @@ function updateSpecGated(kind, id, delta, dryRun) {
|
|
|
45382
46368
|
const stored = loadSpec(kind, id);
|
|
45383
46369
|
const gate = componentCandidateGate(bound2, stored?.subsystem, stored);
|
|
45384
46370
|
const report3 = updateSpec(kind, id, delta, gate, dryRun);
|
|
46371
|
+
if (kind === "component" && typeof delta?.transport === "string" && report3.changes.some((c) => c.path === "transport")) {
|
|
46372
|
+
report3.notices.push(...adapterTransportImpact(id, delta.transport));
|
|
46373
|
+
}
|
|
45385
46374
|
const changed = changedMethods(report3);
|
|
45386
46375
|
if (changed.length > 0 && testRoots.length > 0) {
|
|
45387
46376
|
const written = Array.isArray(delta?.methods) ? delta.methods : [];
|
|
@@ -45389,6 +46378,19 @@ function updateSpecGated(kind, id, delta, dryRun) {
|
|
|
45389
46378
|
}
|
|
45390
46379
|
return report3;
|
|
45391
46380
|
}
|
|
46381
|
+
function adapterTransportImpact(portal, transport) {
|
|
46382
|
+
const components = loadComponentSpecs();
|
|
46383
|
+
if (components.find((c) => c.id === portal)?.componentType !== "Portal") return [];
|
|
46384
|
+
const names = (ref) => ref === portal || ref !== void 0 && ref.endsWith(`::${portal}`);
|
|
46385
|
+
const calledByStep = /* @__PURE__ */ new Set();
|
|
46386
|
+
for (const impl of loadImplementationSpecs()) {
|
|
46387
|
+
const owner = loadSpec("interface", impl.contract)?.component;
|
|
46388
|
+
if (owner === void 0) continue;
|
|
46389
|
+
const steps = impl.methods.flatMap((m) => m.narrative ?? []);
|
|
46390
|
+
if (steps.some((s) => (s.type === "call" || s.type === "register" || s.type === "dispatch") && names(s.targetComponent))) calledByStep.add(owner);
|
|
46391
|
+
}
|
|
46392
|
+
return components.filter((c) => c.componentType === "Adapter" && c.transport !== void 0 && c.transport !== transport).filter((c) => (c.dependsOn ?? []).some(names) || calledByStep.has(c.id)).map((c) => `ADAPTER_TRANSPORT_MISMATCH would follow: Adapter "${c.id}" states transport "${c.transport}" and calls "${portal}", whose transport becomes "${transport}" \u2014 state "${transport}" on "${c.id}", or unset its transport so it follows the Portal.`);
|
|
46393
|
+
}
|
|
45392
46394
|
function changedMethods(report3) {
|
|
45393
46395
|
const named2 = /* @__PURE__ */ new Set();
|
|
45394
46396
|
for (const change of report3.changes) {
|
|
@@ -45425,12 +46427,12 @@ function moveMethods2(from, to, methods, dryRun) {
|
|
|
45425
46427
|
}
|
|
45426
46428
|
|
|
45427
46429
|
// src/mcp/build.ts
|
|
45428
|
-
var
|
|
46430
|
+
var fs25 = __toESM(require("fs"));
|
|
45429
46431
|
var path39 = __toESM(require("path"));
|
|
45430
46432
|
var import_child_process3 = require("child_process");
|
|
45431
46433
|
function captureBuildStamp(entryPath) {
|
|
45432
46434
|
try {
|
|
45433
|
-
const s =
|
|
46435
|
+
const s = fs25.statSync(entryPath);
|
|
45434
46436
|
return { path: entryPath, mtimeMs: s.mtimeMs, size: s.size };
|
|
45435
46437
|
} catch {
|
|
45436
46438
|
return null;
|
|
@@ -45439,7 +46441,7 @@ function captureBuildStamp(entryPath) {
|
|
|
45439
46441
|
function isBuildStale(stamp) {
|
|
45440
46442
|
if (!stamp) return false;
|
|
45441
46443
|
try {
|
|
45442
|
-
const s =
|
|
46444
|
+
const s = fs25.statSync(stamp.path);
|
|
45443
46445
|
return s.mtimeMs !== stamp.mtimeMs || s.size !== stamp.size;
|
|
45444
46446
|
} catch {
|
|
45445
46447
|
return false;
|
|
@@ -45447,7 +46449,7 @@ function isBuildStale(stamp) {
|
|
|
45447
46449
|
}
|
|
45448
46450
|
var fingerprintCache = /* @__PURE__ */ new Map();
|
|
45449
46451
|
function readBuildFingerprint(stamp) {
|
|
45450
|
-
const current2 =
|
|
46452
|
+
const current2 = fs25.statSync(stamp.path);
|
|
45451
46453
|
const key2 = `${stamp.path}|${current2.mtimeMs}|${current2.size}`;
|
|
45452
46454
|
const cached = fingerprintCache.get(key2);
|
|
45453
46455
|
if (cached !== void 0) return cached;
|
|
@@ -45473,9 +46475,9 @@ function fingerprintModuleFor(entryPath) {
|
|
|
45473
46475
|
let dir = path39.dirname(entryPath);
|
|
45474
46476
|
for (; ; ) {
|
|
45475
46477
|
const manifest = path39.join(dir, "package.json");
|
|
45476
|
-
if (
|
|
46478
|
+
if (fs25.existsSync(manifest)) {
|
|
45477
46479
|
try {
|
|
45478
|
-
const pkg = JSON.parse(
|
|
46480
|
+
const pkg = JSON.parse(fs25.readFileSync(manifest, "utf8"));
|
|
45479
46481
|
const inDist = path39.relative(path39.join(dir, "dist"), entryPath);
|
|
45480
46482
|
if (pkg.name === "@wairon/cli" && pkg.main && !inDist.startsWith("..") && !path39.isAbsolute(inDist)) {
|
|
45481
46483
|
return path39.resolve(dir, pkg.main);
|
|
@@ -45491,6 +46493,9 @@ function fingerprintModuleFor(entryPath) {
|
|
|
45491
46493
|
}
|
|
45492
46494
|
|
|
45493
46495
|
// src/core/skills-portal.ts
|
|
46496
|
+
function exportSddSkills2(targetTypes) {
|
|
46497
|
+
return exportSddSkills(targetTypes);
|
|
46498
|
+
}
|
|
45494
46499
|
function buildServerInstructions3() {
|
|
45495
46500
|
return buildServerInstructions2();
|
|
45496
46501
|
}
|
|
@@ -45582,10 +46587,10 @@ function findingLine(f) {
|
|
|
45582
46587
|
}
|
|
45583
46588
|
function markFlows(flows3, f) {
|
|
45584
46589
|
if (f.specId === void 0) return;
|
|
45585
|
-
const onVerb = (flow) => flow.from.scope !== void 0 && flow.to.component === f.specId && (f.at
|
|
46590
|
+
const onVerb = (flow) => flow.from.scope !== void 0 && flow.to.component === f.specId && (f.at !== void 0 ? flow.to.verb === f.at : f.covers === void 0 || f.covers.includes(flow.to.verb ?? ""));
|
|
45586
46591
|
const onCall = (flow) => flow.from.scope === void 0 && flow.from.component === f.specId && f.at !== void 0 && flow.evidence.includes(`call ${f.at}`);
|
|
45587
46592
|
for (const flow of flows3.filter((x) => onVerb(x) || onCall(x))) {
|
|
45588
|
-
const field = f.severity === "error" ? "refusedBy" : "flaggedBy";
|
|
46593
|
+
const field = f.severity === "error" ? "refusedBy" : f.severity === "notice" ? "notedBy" : "flaggedBy";
|
|
45589
46594
|
const list = flow[field] ?? [];
|
|
45590
46595
|
if (!list.includes(findingLine(f))) list.push(findingLine(f));
|
|
45591
46596
|
flow[field] = list;
|
|
@@ -45608,7 +46613,10 @@ function project2(model) {
|
|
|
45608
46613
|
}
|
|
45609
46614
|
const marked = [...flows3.values()];
|
|
45610
46615
|
for (const f of model.findings ?? []) markFlows(marked, f);
|
|
45611
|
-
|
|
46616
|
+
const focus = model.focus;
|
|
46617
|
+
const inside = (party) => focus !== void 0 && party.project !== void 0 && (party.project === focus || party.project.startsWith(`${focus}::`));
|
|
46618
|
+
const kept = focus === void 0 ? marked : marked.filter((f) => inside(f.from) || inside(f.to));
|
|
46619
|
+
return kept.sort(byCalleeThenCaller);
|
|
45612
46620
|
}
|
|
45613
46621
|
function networkName2(id) {
|
|
45614
46622
|
return id === "" ? "(root network)" : id;
|
|
@@ -45618,7 +46626,7 @@ function chainOf(flow) {
|
|
|
45618
46626
|
for (const network of flow.crosses) lines.push(`enters network ${networkName2(network)}`);
|
|
45619
46627
|
if (flow.via !== void 0) lines.push(`through gateway ${flow.via}`);
|
|
45620
46628
|
lines.push(`reaches ${key(flow.to)} over ${flow.transport}${flow.binding ? ` (${flow.binding})` : ""}`);
|
|
45621
|
-
for (const flag of flow.flaggedBy ?? []) lines.push(`marked by the gate: ${flag}`);
|
|
46629
|
+
for (const flag of [...flow.flaggedBy ?? [], ...flow.notedBy ?? []]) lines.push(`marked by the gate: ${flag}`);
|
|
45622
46630
|
return lines;
|
|
45623
46631
|
}
|
|
45624
46632
|
function verbParty(v) {
|
|
@@ -45627,8 +46635,14 @@ function verbParty(v) {
|
|
|
45627
46635
|
function reachesOverNetwork(model, to) {
|
|
45628
46636
|
return model.verbs.some((v) => transportKind(v.transport) === "network" && isNamed(verbParty(v), to));
|
|
45629
46637
|
}
|
|
46638
|
+
function throughExternal(model, name) {
|
|
46639
|
+
const at = name.indexOf("::");
|
|
46640
|
+
const alias = at > 0 ? name.slice(0, at) : name;
|
|
46641
|
+
return (model.externals ?? []).includes(alias) ? alias : void 0;
|
|
46642
|
+
}
|
|
45630
46643
|
function isKnown(model, name) {
|
|
45631
46644
|
if (name === "outside" || name === "network") return true;
|
|
46645
|
+
if ((model.subsystems ?? []).some((s) => s === name || s.startsWith(`${name}::`)) || throughExternal(model, name) !== void 0) return true;
|
|
45632
46646
|
if (name.startsWith("network:")) return model.networks.some((n) => n.id === name.slice("network:".length));
|
|
45633
46647
|
const placed = (model.placements ?? []).some((p) => isNamed({ project: p.project, ...p.subsystem !== void 0 ? { subsystem: p.subsystem } : {}, component: p.component }, name));
|
|
45634
46648
|
return placed || model.verbs.some((v) => isNamed(verbParty(v), name)) || model.calls.some((c) => isNamed(callerOf(c), name));
|
|
@@ -45642,6 +46656,18 @@ function notAllowed(model, flows3, refused3, from, to) {
|
|
|
45642
46656
|
return { allowed: false, flows: [], chain: chain2, refusedBy };
|
|
45643
46657
|
}
|
|
45644
46658
|
if (!reachesOverNetwork(model, to)) {
|
|
46659
|
+
const alias = throughExternal(model, to);
|
|
46660
|
+
if (alias !== void 0) {
|
|
46661
|
+
return {
|
|
46662
|
+
allowed: false,
|
|
46663
|
+
flows: [],
|
|
46664
|
+
inProcess: true,
|
|
46665
|
+
chain: [
|
|
46666
|
+
`${to} is consumed through the external "${alias}": this design's network model holds no verb of it, so it neither allows nor forbids a network flow to it here`,
|
|
46667
|
+
`a library is reached in-process; a network producer's flows are derived and judged in its own project (\`wairon network flows\` there)`
|
|
46668
|
+
]
|
|
46669
|
+
};
|
|
46670
|
+
}
|
|
45645
46671
|
return {
|
|
45646
46672
|
allowed: false,
|
|
45647
46673
|
flows: [],
|
|
@@ -45689,17 +46715,31 @@ function codeOf(line) {
|
|
|
45689
46715
|
}
|
|
45690
46716
|
function gateCell(flow) {
|
|
45691
46717
|
if (refusedFlow(flow)) return `REFUSED: ${[...new Set(flow.refusedBy.map(codeOf))].join("; ")}`;
|
|
45692
|
-
return [...new Set(
|
|
46718
|
+
return [...new Set([...flow.flaggedBy ?? [], ...flow.notedBy ?? []].map(codeOf))].join("; ");
|
|
45693
46719
|
}
|
|
45694
46720
|
function gateFindingsOf(flows3) {
|
|
45695
46721
|
const errors = /* @__PURE__ */ new Set();
|
|
45696
46722
|
const flags = /* @__PURE__ */ new Set();
|
|
46723
|
+
const notes = /* @__PURE__ */ new Set();
|
|
45697
46724
|
for (const f of flows3) {
|
|
45698
46725
|
for (const line of f.refusedBy ?? []) errors.add(line);
|
|
45699
46726
|
for (const line of f.flaggedBy ?? []) flags.add(line);
|
|
46727
|
+
for (const line of f.notedBy ?? []) notes.add(line);
|
|
45700
46728
|
}
|
|
45701
46729
|
const withSeverity = (line, severity) => `${codeOf(line)} (${severity})${line.slice(codeOf(line).length)}`;
|
|
45702
|
-
return [
|
|
46730
|
+
return [
|
|
46731
|
+
...[...errors].map((l) => withSeverity(l, "error")),
|
|
46732
|
+
...[...flags].map((l) => withSeverity(l, "warning")),
|
|
46733
|
+
...[...notes].map((l) => withSeverity(l, "notice"))
|
|
46734
|
+
];
|
|
46735
|
+
}
|
|
46736
|
+
function namedRoot(flow, rootProject) {
|
|
46737
|
+
const party = (p) => ({
|
|
46738
|
+
...p,
|
|
46739
|
+
...p.project === "" ? { project: rootProject } : {},
|
|
46740
|
+
...p.network === "" ? { network: rootProject } : {}
|
|
46741
|
+
});
|
|
46742
|
+
return { ...flow, from: party(flow.from), to: party(flow.to), crosses: flow.crosses.map((n) => n === "" ? rootProject : n) };
|
|
45703
46743
|
}
|
|
45704
46744
|
function networkName3(id) {
|
|
45705
46745
|
return id === "" ? "(root network)" : id;
|
|
@@ -45734,11 +46774,11 @@ function markdownOf(flows3) {
|
|
|
45734
46774
|
const warning = refused3.length > 0 ? [`> **The design fails the gate:** ${refused3.length} row(s) marked REFUSED are refused by \`wairon validate\`, not allowed. Fix the design before trusting this matrix.`, ...gateFindingsOf(flows3).map((l) => `> - ${l.replace(/\n/g, " ")}`), ""] : [];
|
|
45735
46775
|
return [...warning, ...header, ...rows].join("\n") + "\n" + (flows3.length === 0 ? "\n_No network-transport flows: in-process and local verbs never produce one._\n" : "");
|
|
45736
46776
|
}
|
|
45737
|
-
function encodeFlows(flows3, format) {
|
|
46777
|
+
function encodeFlows(flows3, format, rootProject) {
|
|
45738
46778
|
const gate = { gateFindings: gateFindingsOf(flows3), refused: flows3.some(refusedFlow) };
|
|
45739
46779
|
switch (format) {
|
|
45740
46780
|
case "json":
|
|
45741
|
-
return { format, content: JSON.stringify(sortedKeys2(flows3), null, 2) + "\n", unbound: [], ...gate };
|
|
46781
|
+
return { format, content: JSON.stringify(sortedKeys2(rootProject ? flows3.map((f) => namedRoot(f, rootProject)) : flows3), null, 2) + "\n", unbound: [], ...gate };
|
|
45742
46782
|
case "csv":
|
|
45743
46783
|
return { format, content: csvOf(flows3), unbound: [], ...gate };
|
|
45744
46784
|
default:
|
|
@@ -45750,13 +46790,24 @@ function encodeFlows(flows3, format) {
|
|
|
45750
46790
|
function flows(options, format) {
|
|
45751
46791
|
const model = reachModel2(options);
|
|
45752
46792
|
const matrix = project2(model);
|
|
45753
|
-
|
|
46793
|
+
const doc = encodeFlows(matrix, format, model.rootProject);
|
|
46794
|
+
return withFamilyNote(doc, model);
|
|
46795
|
+
}
|
|
46796
|
+
function withFamilyNote(doc, model) {
|
|
46797
|
+
if (model.focus === void 0 || model.judgedAt === void 0) return doc;
|
|
46798
|
+
const note = `"${model.focus}" is a member: this is its enclosing family's network, declared and proven at the family root ${model.judgedAt}, narrowed to the flows that start or land inside it \u2014 the same rows \`wairon network\` derives there.`;
|
|
46799
|
+
return { ...doc, notes: [note, ...doc.notes ?? []] };
|
|
45754
46800
|
}
|
|
45755
46801
|
function why(options, from, to) {
|
|
45756
46802
|
const model = reachModel2(options);
|
|
45757
|
-
const matrix = project2(model);
|
|
46803
|
+
const matrix = project2(unfocused(model));
|
|
45758
46804
|
return explain(matrix, from, to, model);
|
|
45759
46805
|
}
|
|
46806
|
+
function unfocused(model) {
|
|
46807
|
+
if (model.focus === void 0) return model;
|
|
46808
|
+
const { focus: _focus, ...whole } = model;
|
|
46809
|
+
return whole;
|
|
46810
|
+
}
|
|
45760
46811
|
|
|
45761
46812
|
// src/mcp/network.ts
|
|
45762
46813
|
function lands(flow, to) {
|
|
@@ -45772,10 +46823,16 @@ function explain2(from, to) {
|
|
|
45772
46823
|
return why({}, from, to);
|
|
45773
46824
|
}
|
|
45774
46825
|
function declare2(declared, description) {
|
|
45775
|
-
const
|
|
46826
|
+
const held = declared ? loadProjectConfig()?.network : void 0;
|
|
46827
|
+
const kept = description ?? (typeof held === "object" && held !== null ? held.description ?? null : null);
|
|
46828
|
+
const network = declared ? kept ? { description: kept } : {} : null;
|
|
45776
46829
|
return setNetwork(network);
|
|
45777
46830
|
}
|
|
45778
46831
|
|
|
46832
|
+
// src/mcp/migrations.ts
|
|
46833
|
+
var fs30 = __toESM(require("fs"));
|
|
46834
|
+
var path48 = __toESM(require("path"));
|
|
46835
|
+
|
|
45779
46836
|
// src/migrations/orchestrator.ts
|
|
45780
46837
|
var path47 = __toESM(require("path"));
|
|
45781
46838
|
|
|
@@ -46828,12 +47885,12 @@ function pin(p, log) {
|
|
|
46828
47885
|
}
|
|
46829
47886
|
|
|
46830
47887
|
// src/migrations/membership.ts
|
|
46831
|
-
var
|
|
47888
|
+
var fs27 = __toESM(require("fs"));
|
|
46832
47889
|
var path43 = __toESM(require("path"));
|
|
46833
47890
|
|
|
46834
47891
|
// src/migrations/family-files.ts
|
|
46835
47892
|
var crypto8 = __toESM(require("crypto"));
|
|
46836
|
-
var
|
|
47893
|
+
var fs26 = __toESM(require("fs"));
|
|
46837
47894
|
var nodePath = __toESM(require("path"));
|
|
46838
47895
|
var WAI = ".wai";
|
|
46839
47896
|
var TRANSACTIONS = "transactions";
|
|
@@ -46846,7 +47903,7 @@ function pause(ms) {
|
|
|
46846
47903
|
}
|
|
46847
47904
|
function readOnly(target) {
|
|
46848
47905
|
try {
|
|
46849
|
-
return (
|
|
47906
|
+
return (fs26.statSync(target).mode & 128) === 0;
|
|
46850
47907
|
} catch {
|
|
46851
47908
|
return false;
|
|
46852
47909
|
}
|
|
@@ -46872,7 +47929,7 @@ function waiFiles(owner) {
|
|
|
46872
47929
|
const walk2 = (dir, rel2) => {
|
|
46873
47930
|
let entries;
|
|
46874
47931
|
try {
|
|
46875
|
-
entries =
|
|
47932
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
46876
47933
|
} catch {
|
|
46877
47934
|
return;
|
|
46878
47935
|
}
|
|
@@ -46892,7 +47949,7 @@ function waiOwners(root) {
|
|
|
46892
47949
|
const walk2 = (dir) => {
|
|
46893
47950
|
let entries;
|
|
46894
47951
|
try {
|
|
46895
|
-
entries =
|
|
47952
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
46896
47953
|
} catch {
|
|
46897
47954
|
return;
|
|
46898
47955
|
}
|
|
@@ -46916,7 +47973,7 @@ function areaFiles(owner, area) {
|
|
|
46916
47973
|
const abs = nodePath.join(owner, ...rel2.split("/"));
|
|
46917
47974
|
let stat;
|
|
46918
47975
|
try {
|
|
46919
|
-
stat =
|
|
47976
|
+
stat = fs26.lstatSync(abs);
|
|
46920
47977
|
} catch {
|
|
46921
47978
|
return;
|
|
46922
47979
|
}
|
|
@@ -46926,7 +47983,7 @@ function areaFiles(owner, area) {
|
|
|
46926
47983
|
return;
|
|
46927
47984
|
}
|
|
46928
47985
|
if (!stat.isDirectory()) return;
|
|
46929
|
-
for (const name of
|
|
47986
|
+
for (const name of fs26.readdirSync(abs)) walk2(`${rel2}/${name}`);
|
|
46930
47987
|
};
|
|
46931
47988
|
walk2(area.split(nodePath.sep).join("/").replace(/\/+$/, ""));
|
|
46932
47989
|
return out.sort();
|
|
@@ -46936,7 +47993,7 @@ function everyFile(owner) {
|
|
|
46936
47993
|
const walk2 = (dir, rel2) => {
|
|
46937
47994
|
let entries;
|
|
46938
47995
|
try {
|
|
46939
|
-
entries =
|
|
47996
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
46940
47997
|
} catch {
|
|
46941
47998
|
return;
|
|
46942
47999
|
}
|
|
@@ -46980,22 +48037,22 @@ function assertPlaceable(familyRoot, base, owner) {
|
|
|
46980
48037
|
}
|
|
46981
48038
|
function nearestExisting(p) {
|
|
46982
48039
|
let at = nodePath.resolve(p);
|
|
46983
|
-
while (!
|
|
48040
|
+
while (!fs26.existsSync(at) && at !== nodePath.dirname(at)) at = nodePath.dirname(at);
|
|
46984
48041
|
return at;
|
|
46985
48042
|
}
|
|
46986
48043
|
function copyFiles(owner, rels, at, digests) {
|
|
46987
|
-
|
|
48044
|
+
fs26.mkdirSync(at, { recursive: true });
|
|
46988
48045
|
for (const rel2 of rels) {
|
|
46989
48046
|
const live = nodePath.join(owner, ...rel2.split("/"));
|
|
46990
48047
|
let bytes;
|
|
46991
48048
|
try {
|
|
46992
|
-
bytes =
|
|
48049
|
+
bytes = fs26.readFileSync(live);
|
|
46993
48050
|
} catch (e) {
|
|
46994
48051
|
throw new Error(`could not copy ${live} into the rehearsal: ${e instanceof Error ? e.message : String(e)}`);
|
|
46995
48052
|
}
|
|
46996
48053
|
const copy = nodePath.join(at, ...rel2.split("/"));
|
|
46997
|
-
|
|
46998
|
-
|
|
48054
|
+
fs26.mkdirSync(nodePath.dirname(copy), { recursive: true });
|
|
48055
|
+
fs26.writeFileSync(copy, bytes);
|
|
46999
48056
|
digests.set(live, sha2562(bytes));
|
|
47000
48057
|
}
|
|
47001
48058
|
}
|
|
@@ -47004,7 +48061,7 @@ function compare4(rehearsal) {
|
|
|
47004
48061
|
const seen = /* @__PURE__ */ new Set();
|
|
47005
48062
|
for (const [live, copyRoot, rel2] of comparedFiles(rehearsal)) {
|
|
47006
48063
|
seen.add(nodePath.join(live, ...rel2.split("/")));
|
|
47007
|
-
const change = changeOf(rehearsal, live, rel2, sha2562(
|
|
48064
|
+
const change = changeOf(rehearsal, live, rel2, sha2562(fs26.readFileSync(nodePath.join(copyRoot, ...rel2.split("/")))));
|
|
47008
48065
|
if (change) changes.push(change);
|
|
47009
48066
|
}
|
|
47010
48067
|
for (const [liveFile, base] of rehearsal.baseDigests) {
|
|
@@ -47039,7 +48096,7 @@ function ownerOf3(rehearsal, liveFile) {
|
|
|
47039
48096
|
}
|
|
47040
48097
|
function move(from, to) {
|
|
47041
48098
|
const rels = everyFile(from);
|
|
47042
|
-
const taken = rels.find((rel2) =>
|
|
48099
|
+
const taken = rels.find((rel2) => fs26.existsSync(nodePath.join(to, ...rel2.split("/"))));
|
|
47043
48100
|
if (taken !== void 0) throw new Error(`cannot move ${from} to ${to}: ${nodePath.join(to, ...taken.split("/"))} already exists`);
|
|
47044
48101
|
for (const rel2 of rels) {
|
|
47045
48102
|
const target = nodePath.join(to, ...rel2.split("/"));
|
|
@@ -47049,45 +48106,45 @@ function move(from, to) {
|
|
|
47049
48106
|
for (const rel2 of rels) prune2(nodePath.dirname(nodePath.join(from, ...rel2.split("/"))), nodePath.dirname(from));
|
|
47050
48107
|
prune2(from, nodePath.dirname(from));
|
|
47051
48108
|
}
|
|
47052
|
-
function read(
|
|
48109
|
+
function read(path50) {
|
|
47053
48110
|
try {
|
|
47054
|
-
return
|
|
48111
|
+
return fs26.readFileSync(path50);
|
|
47055
48112
|
} catch (e) {
|
|
47056
48113
|
if (e.code === "ENOENT") return null;
|
|
47057
|
-
throw new Error(`could not read ${
|
|
48114
|
+
throw new Error(`could not read ${path50}: ${e instanceof Error ? e.message : String(e)}`);
|
|
47058
48115
|
}
|
|
47059
48116
|
}
|
|
47060
|
-
function digest2(
|
|
47061
|
-
const bytes = read(
|
|
48117
|
+
function digest2(path50) {
|
|
48118
|
+
const bytes = read(path50);
|
|
47062
48119
|
return bytes === null ? null : sha2562(bytes);
|
|
47063
48120
|
}
|
|
47064
48121
|
function replace(source, target) {
|
|
47065
|
-
retrying("replace", target, () =>
|
|
48122
|
+
retrying("replace", target, () => fs26.renameSync(source, target));
|
|
47066
48123
|
}
|
|
47067
|
-
function place(
|
|
47068
|
-
|
|
47069
|
-
const tmp = `${
|
|
47070
|
-
const fd =
|
|
48124
|
+
function place(path50, bytes) {
|
|
48125
|
+
fs26.mkdirSync(nodePath.dirname(path50), { recursive: true });
|
|
48126
|
+
const tmp = `${path50}.wairon-restore`;
|
|
48127
|
+
const fd = fs26.openSync(tmp, "w");
|
|
47071
48128
|
try {
|
|
47072
|
-
|
|
47073
|
-
|
|
48129
|
+
fs26.writeSync(fd, bytes);
|
|
48130
|
+
fs26.fsyncSync(fd);
|
|
47074
48131
|
} finally {
|
|
47075
|
-
|
|
48132
|
+
fs26.closeSync(fd);
|
|
47076
48133
|
}
|
|
47077
48134
|
try {
|
|
47078
|
-
retrying("place",
|
|
48135
|
+
retrying("place", path50, () => fs26.renameSync(tmp, path50));
|
|
47079
48136
|
} catch (e) {
|
|
47080
48137
|
try {
|
|
47081
|
-
|
|
48138
|
+
fs26.unlinkSync(tmp);
|
|
47082
48139
|
} catch {
|
|
47083
48140
|
}
|
|
47084
48141
|
throw e;
|
|
47085
48142
|
}
|
|
47086
48143
|
}
|
|
47087
|
-
function remove2(
|
|
47088
|
-
retrying("remove",
|
|
48144
|
+
function remove2(path50) {
|
|
48145
|
+
retrying("remove", path50, () => {
|
|
47089
48146
|
try {
|
|
47090
|
-
|
|
48147
|
+
fs26.unlinkSync(path50);
|
|
47091
48148
|
} catch (e) {
|
|
47092
48149
|
if (e.code !== "ENOENT") throw e;
|
|
47093
48150
|
}
|
|
@@ -47096,13 +48153,13 @@ function remove2(path49) {
|
|
|
47096
48153
|
function makeDirs(dir) {
|
|
47097
48154
|
const missing = [];
|
|
47098
48155
|
let at = nodePath.resolve(dir);
|
|
47099
|
-
while (!
|
|
48156
|
+
while (!fs26.existsSync(at)) {
|
|
47100
48157
|
missing.unshift(at);
|
|
47101
48158
|
const up = nodePath.dirname(at);
|
|
47102
48159
|
if (up === at) break;
|
|
47103
48160
|
at = up;
|
|
47104
48161
|
}
|
|
47105
|
-
for (const d of missing)
|
|
48162
|
+
for (const d of missing) fs26.mkdirSync(d);
|
|
47106
48163
|
return missing;
|
|
47107
48164
|
}
|
|
47108
48165
|
function prune2(dir, stopAt) {
|
|
@@ -47112,47 +48169,47 @@ function prune2(dir, stopAt) {
|
|
|
47112
48169
|
while (at !== stop && within3(stop, at)) {
|
|
47113
48170
|
let entries;
|
|
47114
48171
|
try {
|
|
47115
|
-
entries =
|
|
48172
|
+
entries = fs26.readdirSync(at);
|
|
47116
48173
|
} catch {
|
|
47117
48174
|
break;
|
|
47118
48175
|
}
|
|
47119
48176
|
if (entries.length > 0) break;
|
|
47120
|
-
|
|
48177
|
+
fs26.rmdirSync(at);
|
|
47121
48178
|
removed.push(at);
|
|
47122
48179
|
at = nodePath.dirname(at);
|
|
47123
48180
|
}
|
|
47124
48181
|
return removed;
|
|
47125
48182
|
}
|
|
47126
|
-
function resolve35(root,
|
|
47127
|
-
const dir = lexicalTarget(root,
|
|
48183
|
+
function resolve35(root, path50) {
|
|
48184
|
+
const dir = lexicalTarget(root, path50);
|
|
47128
48185
|
return dir !== null && landsWithin(root, dir) ? dir : null;
|
|
47129
48186
|
}
|
|
47130
48187
|
function strictlyWithin(root, dir) {
|
|
47131
48188
|
const rel2 = nodePath.relative(root, dir);
|
|
47132
48189
|
return rel2 !== "" && !rel2.startsWith("..") && !nodePath.isAbsolute(rel2);
|
|
47133
48190
|
}
|
|
47134
|
-
function lexicalTarget(root,
|
|
47135
|
-
if (!
|
|
47136
|
-
const dir = nodePath.resolve(root,
|
|
48191
|
+
function lexicalTarget(root, path50) {
|
|
48192
|
+
if (!path50 || nodePath.isAbsolute(path50) || nodePath.win32.isAbsolute(path50)) return null;
|
|
48193
|
+
const dir = nodePath.resolve(root, path50);
|
|
47137
48194
|
return strictlyWithin(root, dir) ? dir : null;
|
|
47138
48195
|
}
|
|
47139
48196
|
function landsWithin(root, dir) {
|
|
47140
48197
|
let at = dir;
|
|
47141
|
-
while (!
|
|
48198
|
+
while (!fs26.existsSync(at) && at !== nodePath.dirname(at)) at = nodePath.dirname(at);
|
|
47142
48199
|
try {
|
|
47143
|
-
const real =
|
|
47144
|
-
const realRoot =
|
|
48200
|
+
const real = fs26.realpathSync(at);
|
|
48201
|
+
const realRoot = fs26.realpathSync(root);
|
|
47145
48202
|
return real === realRoot || strictlyWithin(realRoot, real);
|
|
47146
48203
|
} catch {
|
|
47147
48204
|
return false;
|
|
47148
48205
|
}
|
|
47149
48206
|
}
|
|
47150
48207
|
function holdsProject(dir) {
|
|
47151
|
-
return
|
|
48208
|
+
return fs26.existsSync(nodePath.join(dir, ".wai", "project.yaml"));
|
|
47152
48209
|
}
|
|
47153
48210
|
function sameVolume(a, b) {
|
|
47154
48211
|
try {
|
|
47155
|
-
return
|
|
48212
|
+
return fs26.statSync(a).dev === fs26.statSync(b).dev;
|
|
47156
48213
|
} catch {
|
|
47157
48214
|
return false;
|
|
47158
48215
|
}
|
|
@@ -47167,7 +48224,7 @@ function emptyPlan(family, request) {
|
|
|
47167
48224
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
47168
48225
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
47169
48226
|
}
|
|
47170
|
-
var
|
|
48227
|
+
var posix6 = (p) => p.split(path43.sep).join("/");
|
|
47171
48228
|
var refuse = (plan6, code, project3, detail) => {
|
|
47172
48229
|
plan6.refusals.push({ code, project: project3, detail });
|
|
47173
48230
|
};
|
|
@@ -47195,7 +48252,7 @@ function hasSystemAt(dir) {
|
|
|
47195
48252
|
}
|
|
47196
48253
|
function occupied(dir) {
|
|
47197
48254
|
try {
|
|
47198
|
-
return !
|
|
48255
|
+
return !fs27.statSync(dir).isDirectory() || fs27.readdirSync(dir).length > 0;
|
|
47199
48256
|
} catch {
|
|
47200
48257
|
return false;
|
|
47201
48258
|
}
|
|
@@ -47245,7 +48302,7 @@ function attachEdits(plan6, family, bound2, dir, alias, bindingConfig) {
|
|
|
47245
48302
|
edit(plan6, key2, "id", `id: ${id} (declared${approved !== void 0 ? ", the id its lock approved" : ""})`, { root: dir, call: "setId", args: [id] });
|
|
47246
48303
|
}
|
|
47247
48304
|
if (!attached) plan6.notes.push(`${dir} has no project.yaml of its own \u2014 \`wairon doctor --fix\` writes one declaring "${alias}" as its id`);
|
|
47248
|
-
const relPath =
|
|
48305
|
+
const relPath = posix6(path43.relative(familyNode(family, bound2).directory, dir));
|
|
47249
48306
|
const declaration2 = { path: relPath, ...plan6.request.description !== void 0 ? { description: plan6.request.description } : {} };
|
|
47250
48307
|
edit(plan6, bound2, "member", `members: ${alias} \u2192 ${relPath} (an existing project, kept as it is)`, { root: familyNode(family, bound2).directory, call: "declareMember", args: [alias, declaration2] });
|
|
47251
48308
|
for (const requirement of requiredPolicies(bindingConfig)) {
|
|
@@ -47292,8 +48349,8 @@ function planDetach(family, bound2, request) {
|
|
|
47292
48349
|
if (plan6.request.widen) planWidening(plan6, member, narrow);
|
|
47293
48350
|
if (plan6.refusals.length > 0) return plan6;
|
|
47294
48351
|
const boundDir = familyNode(family, bound2).directory;
|
|
47295
|
-
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ??
|
|
47296
|
-
const declared = { ...held, path: memberLocationOf(held) ??
|
|
48352
|
+
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ?? posix6(path43.relative(boundDir, member.directory)));
|
|
48353
|
+
const declared = { ...held, path: memberLocationOf(held) ?? posix6(path43.relative(boundDir, member.directory)) };
|
|
47297
48354
|
detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward);
|
|
47298
48355
|
return plan6;
|
|
47299
48356
|
}
|
|
@@ -47333,7 +48390,7 @@ function planWidening(plan6, member, narrow) {
|
|
|
47333
48390
|
function detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward) {
|
|
47334
48391
|
const boundDir = familyNode(family, bound2).directory;
|
|
47335
48392
|
const relocation = plan6.request.relocation;
|
|
47336
|
-
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path:
|
|
48393
|
+
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path: posix6(path43.relative(from, producer.directory)) };
|
|
47337
48394
|
const where = (source) => source.hosted !== void 0 ? `hosted record ${source.hosted}` : source.path;
|
|
47338
48395
|
edit(plan6, bound2, "member", `members: ${alias} removed (${declared.path} ${relocation ? `moves to ${relocation.to}` : "stays as it is"})`, { root: boundDir, call: "removeMember", args: [alias] });
|
|
47339
48396
|
const boundSource = relocation ? sourceTo(boundDir, member) : { path: declared.path };
|
|
@@ -47407,7 +48464,7 @@ function adoptEdits(plan6, family, bound2, alias, external, dir, at = dir) {
|
|
|
47407
48464
|
edit(plan6, bound2, "pin", `pin ${alias} removed (the family supplies it now)`, { root: boundDir, call: "unpin", args: [alias] });
|
|
47408
48465
|
edit(plan6, bound2, "external", `externals: ${alias} removed`, { root: boundDir, call: "removeExternal", args: [alias] });
|
|
47409
48466
|
const declaration2 = {
|
|
47410
|
-
path:
|
|
48467
|
+
path: posix6(path43.relative(boundDir, at)),
|
|
47411
48468
|
...external.description !== void 0 ? { description: external.description } : {},
|
|
47412
48469
|
...external.use && external.use.length > 0 ? { use: external.use } : {}
|
|
47413
48470
|
};
|
|
@@ -47597,6 +48654,11 @@ function planRename(family, bound2, request) {
|
|
|
47597
48654
|
edit2(plan6, target.namespace, "id", `id: ${oldId} \u2192 ${newId} (${oldId} kept in previousIds)`, { root: target.directory, call: "renameId", args: [oldId, newId] });
|
|
47598
48655
|
for (const h of holders.filter((x) => x.external)) planPin(plan6, h, oldId, newId);
|
|
47599
48656
|
plan6.notes.push(`Consumers outside the family that name "${oldId}" by path are not found by a rename: each must rewrite its own external (its next gate reports the id change).`);
|
|
48657
|
+
if (target.namespace !== "" && target.mountAlias === oldId) {
|
|
48658
|
+
const oldKey = target.namespace;
|
|
48659
|
+
const newKey = target.parent ? `${target.parent}::${newId}` : newId;
|
|
48660
|
+
plan6.notes.push(`Deployment names: a network bindings file and an observed-flow export name this project's workloads by its key \u2014 rename the bindings keys "${oldKey}" and every "${oldKey}::\u2026" (its subsystems and Portals) to "${newKey}" and "${newKey}::\u2026", and the telemetry names that follow them. Until then \`wairon network policy\` and \`check\` resolve them through previousIds and print a NOTE naming each key.`);
|
|
48661
|
+
}
|
|
47600
48662
|
if (approved !== void 0 && target.mountAlias === newId) {
|
|
47601
48663
|
plan6.notes.push(`${label5(target.namespace)} kept "${oldId}" where its alias is "${newId}" because its lock approved "${approved}" (id-locked): the rename resolves it \u2014 PROJECT_ID_RENAMED until the re-lock approves "${newId}".`);
|
|
47602
48664
|
}
|
|
@@ -47746,7 +48808,7 @@ function emptyPlan3(family, request) {
|
|
|
47746
48808
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
47747
48809
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
47748
48810
|
}
|
|
47749
|
-
var
|
|
48811
|
+
var posix7 = (p) => p.split(path45.sep).join("/");
|
|
47750
48812
|
var label6 = (key2) => key2 === "" ? "the top project" : `"${key2}"`;
|
|
47751
48813
|
var refuse3 = (plan6, code, project3, detail) => {
|
|
47752
48814
|
plan6.refusals.push({ code, project: project3, detail });
|
|
@@ -47956,7 +49018,7 @@ function respellConsumer(plan6, family, c, own2) {
|
|
|
47956
49018
|
function repointConsumer(plan6, c, boundNode2) {
|
|
47957
49019
|
const declared = configAt5(c.node.directory)?.externals?.[c.alias];
|
|
47958
49020
|
const project3 = boundNode2.id !== void 0 && boundNode2.id !== c.alias ? boundNode2.id : null;
|
|
47959
|
-
const source = declared?.source ? { path:
|
|
49021
|
+
const source = declared?.source ? { path: posix7(path45.relative(c.node.directory, boundNode2.directory)) } : null;
|
|
47960
49022
|
edit3(plan6, c.node.namespace, "external", `externals: ${c.alias} names ${boundNode2.id ?? "the top project"}${source ? ` at ${source.path}` : ""}`, { root: c.node.directory, call: "repointExternal", args: [c.alias, project3, source] });
|
|
47961
49023
|
plan6.notes.push(`${label6(c.node.namespace)}'s pin of "${c.alias}" is taken again against ${boundNode2.id ?? "the top project"}: the producer changed, so its lock reads drifted until it is re-locked.`);
|
|
47962
49024
|
return c;
|
|
@@ -47992,8 +49054,8 @@ function planExternalize(family, bound2, request) {
|
|
|
47992
49054
|
plan6,
|
|
47993
49055
|
bound2,
|
|
47994
49056
|
"move",
|
|
47995
|
-
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${
|
|
47996
|
-
{ root: node.directory, call: "externalizeSubsystem", args: [id,
|
|
49057
|
+
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${posix7(path45.relative(node.directory, dir))}, declared as "${id}"`} \u2014 a storage move; no reference, export or pin changes`,
|
|
49058
|
+
{ root: node.directory, call: "externalizeSubsystem", args: [id, posix7(path45.relative(node.directory, dir))] }
|
|
47997
49059
|
);
|
|
47998
49060
|
plan6.notes.push("Source code is not moved: move the subsystem's code yourself if it should live beside its specs.");
|
|
47999
49061
|
return plan6;
|
|
@@ -48002,7 +49064,7 @@ function planExternalize(family, bound2, request) {
|
|
|
48002
49064
|
crossingsBack(plan6, family, bound2, subKey);
|
|
48003
49065
|
checkSiblings(plan6, family, bound2, id, subKey, table);
|
|
48004
49066
|
if (plan6.refusals.length > 0) return plan6;
|
|
48005
|
-
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${
|
|
49067
|
+
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${posix7(path45.relative(node.directory, dir))}, declared as "${id}", then promoted`, { root: node.directory, call: "externalizeSubsystem", args: [id, posix7(path45.relative(node.directory, dir)), "project"] });
|
|
48006
49068
|
edit3(plan6, id, "pin", `pin the new member's externals (its external for ${node.id ?? "this project"}, when its specs reference it)`, { root: dir, call: "pinExternals", args: [[]] });
|
|
48007
49069
|
repinIntactConsumers2(plan6, family, bound2, [dir]);
|
|
48008
49070
|
plan6.notes.push("Source code is not moved: move the subsystem's code into the member yourself.");
|
|
@@ -48212,6 +49274,7 @@ function write5(plan6, rehearsal) {
|
|
|
48212
49274
|
for (const e of planned.filter((x) => x.write.call === "demoteMember")) {
|
|
48213
49275
|
const demoted = writeOne3(rehearsal, e.write);
|
|
48214
49276
|
for (const line of demoted?.consumersRestored ?? []) plan6.edits.push({ project: e.project, kind: "reference", detail: `consumers across the old boundary restored: ${line}` });
|
|
49277
|
+
for (const line of demoted?.signaturesRederived ?? []) plan6.edits.push({ project: e.project, kind: "reference", detail: `signature text re-derived \u2014 ${line}` });
|
|
48215
49278
|
}
|
|
48216
49279
|
for (const e of planned.filter((x) => x.write.call === "unpin")) writeOne3(rehearsal, e.write);
|
|
48217
49280
|
}
|
|
@@ -48261,6 +49324,8 @@ function recordCrossing(plan6, bound2, member, result) {
|
|
|
48261
49324
|
for (const line of crossed.exported) plan6.edits.push({ project: bound2, kind: "export", detail: `export ${line}` });
|
|
48262
49325
|
for (const line of crossed.imported) plan6.edits.push({ project: bound2, kind: "external", detail: `import: ${line}` });
|
|
48263
49326
|
for (const line of crossed.consumersDropped) plan6.edits.push({ project: bound2, kind: "reference", detail: `consumers across the boundary to ${member} dropped: ${line}` });
|
|
49327
|
+
for (const line of crossed.pathsRebased ?? []) plan6.edits.push({ project: member, kind: "reference", detail: `path re-expressed under the new root \u2014 ${line}` });
|
|
49328
|
+
for (const line of crossed.signaturesRederived ?? []) plan6.edits.push({ project: bound2, kind: "reference", detail: `signature text re-derived \u2014 ${line}` });
|
|
48264
49329
|
for (const line of crossed.entriesDeclared ?? []) plan6.edits.push({ project: member, kind: "reference", detail: `entry declared for the callers left across the boundary \u2014 ${line}` });
|
|
48265
49330
|
const topics = crossed.topicsCrossing ?? [];
|
|
48266
49331
|
if (topics.length > 0) {
|
|
@@ -48298,11 +49363,11 @@ function writeOne3(rehearsal, w) {
|
|
|
48298
49363
|
|
|
48299
49364
|
// src/migrations/transaction.ts
|
|
48300
49365
|
var crypto9 = __toESM(require("crypto"));
|
|
48301
|
-
var
|
|
49366
|
+
var fs29 = __toESM(require("fs"));
|
|
48302
49367
|
var path46 = __toESM(require("path"));
|
|
48303
49368
|
|
|
48304
49369
|
// src/migrations/transaction-files.ts
|
|
48305
|
-
var
|
|
49370
|
+
var fs28 = __toESM(require("fs"));
|
|
48306
49371
|
var os7 = __toESM(require("os"));
|
|
48307
49372
|
var nodePath2 = __toESM(require("path"));
|
|
48308
49373
|
var yaml4 = __toESM(require("js-yaml"));
|
|
@@ -48316,7 +49381,7 @@ function pause2(ms) {
|
|
|
48316
49381
|
function removeTree(dir) {
|
|
48317
49382
|
for (let attempt = 1; ; attempt++) {
|
|
48318
49383
|
try {
|
|
48319
|
-
|
|
49384
|
+
fs28.rmSync(dir, { recursive: true, force: true });
|
|
48320
49385
|
return;
|
|
48321
49386
|
} catch (e) {
|
|
48322
49387
|
const code = e.code;
|
|
@@ -48326,20 +49391,20 @@ function removeTree(dir) {
|
|
|
48326
49391
|
}
|
|
48327
49392
|
}
|
|
48328
49393
|
function writeDurably(file, bytes) {
|
|
48329
|
-
|
|
49394
|
+
fs28.mkdirSync(nodePath2.dirname(file), { recursive: true });
|
|
48330
49395
|
const tmp = `${file}.tmp`;
|
|
48331
49396
|
try {
|
|
48332
|
-
const fd =
|
|
49397
|
+
const fd = fs28.openSync(tmp, "w");
|
|
48333
49398
|
try {
|
|
48334
|
-
|
|
48335
|
-
|
|
49399
|
+
fs28.writeSync(fd, typeof bytes === "string" ? Buffer.from(bytes, "utf8") : bytes);
|
|
49400
|
+
fs28.fsyncSync(fd);
|
|
48336
49401
|
} finally {
|
|
48337
|
-
|
|
49402
|
+
fs28.closeSync(fd);
|
|
48338
49403
|
}
|
|
48339
|
-
|
|
49404
|
+
fs28.renameSync(tmp, file);
|
|
48340
49405
|
} catch (e) {
|
|
48341
49406
|
try {
|
|
48342
|
-
|
|
49407
|
+
fs28.unlinkSync(tmp);
|
|
48343
49408
|
} catch {
|
|
48344
49409
|
}
|
|
48345
49410
|
throw e;
|
|
@@ -48349,18 +49414,18 @@ function writeDurably(file, bytes) {
|
|
|
48349
49414
|
function syncDirectory(dir) {
|
|
48350
49415
|
if (process.platform === "win32") return;
|
|
48351
49416
|
try {
|
|
48352
|
-
const fd =
|
|
49417
|
+
const fd = fs28.openSync(dir, "r");
|
|
48353
49418
|
try {
|
|
48354
|
-
|
|
49419
|
+
fs28.fsyncSync(fd);
|
|
48355
49420
|
} finally {
|
|
48356
|
-
|
|
49421
|
+
fs28.closeSync(fd);
|
|
48357
49422
|
}
|
|
48358
49423
|
} catch {
|
|
48359
49424
|
}
|
|
48360
49425
|
}
|
|
48361
49426
|
function openRehearsal(id) {
|
|
48362
49427
|
const dir = rehearsalDir(id);
|
|
48363
|
-
|
|
49428
|
+
fs28.mkdirSync(dir);
|
|
48364
49429
|
return dir;
|
|
48365
49430
|
}
|
|
48366
49431
|
function dropRehearsal(id) {
|
|
@@ -48371,14 +49436,14 @@ function dropRehearsal(id) {
|
|
|
48371
49436
|
}
|
|
48372
49437
|
function saveJournal(journal) {
|
|
48373
49438
|
const ignore = nodePath2.join(transactionsDir(journal.owner), ".gitignore");
|
|
48374
|
-
if (!
|
|
49439
|
+
if (!fs28.existsSync(ignore)) writeDurably(ignore, "*\n");
|
|
48375
49440
|
writeDurably(nodePath2.join(transactionDir(journal.owner, journal.id), "journal.yaml"), yaml4.dump(journal, { noRefs: true, lineWidth: 200 }));
|
|
48376
49441
|
}
|
|
48377
49442
|
function readJournal(owner, id) {
|
|
48378
49443
|
const file = nodePath2.join(transactionDir(owner, id), "journal.yaml");
|
|
48379
49444
|
let text3;
|
|
48380
49445
|
try {
|
|
48381
|
-
text3 =
|
|
49446
|
+
text3 = fs28.readFileSync(file, "utf8");
|
|
48382
49447
|
} catch (e) {
|
|
48383
49448
|
if (e.code === "ENOENT") return null;
|
|
48384
49449
|
throw e;
|
|
@@ -48393,7 +49458,7 @@ function listJournals(owner) {
|
|
|
48393
49458
|
const dir = transactionsDir(owner);
|
|
48394
49459
|
let names;
|
|
48395
49460
|
try {
|
|
48396
|
-
names =
|
|
49461
|
+
names = fs28.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
48397
49462
|
} catch {
|
|
48398
49463
|
return [];
|
|
48399
49464
|
}
|
|
@@ -48410,26 +49475,26 @@ function listJournals(owner) {
|
|
|
48410
49475
|
startedAt: ""
|
|
48411
49476
|
});
|
|
48412
49477
|
}
|
|
48413
|
-
function putFile(owner, id, area,
|
|
48414
|
-
writeDurably(locate(owner, id, area,
|
|
49478
|
+
function putFile(owner, id, area, path50, bytes) {
|
|
49479
|
+
writeDurably(locate(owner, id, area, path50), bytes);
|
|
48415
49480
|
}
|
|
48416
|
-
function readFile(owner, id, area,
|
|
49481
|
+
function readFile(owner, id, area, path50) {
|
|
48417
49482
|
try {
|
|
48418
|
-
return
|
|
49483
|
+
return fs28.readFileSync(locate(owner, id, area, path50));
|
|
48419
49484
|
} catch (e) {
|
|
48420
49485
|
if (e.code === "ENOENT") return null;
|
|
48421
49486
|
throw e;
|
|
48422
49487
|
}
|
|
48423
49488
|
}
|
|
48424
|
-
function locate(owner, id, area,
|
|
48425
|
-
return nodePath2.join(transactionDir(owner, id), area, ...
|
|
49489
|
+
function locate(owner, id, area, path50) {
|
|
49490
|
+
return nodePath2.join(transactionDir(owner, id), area, ...path50.split("/"));
|
|
48426
49491
|
}
|
|
48427
49492
|
function close(owner, id) {
|
|
48428
49493
|
removeTree(transactionDir(owner, id));
|
|
48429
49494
|
const dir = transactionsDir(owner);
|
|
48430
49495
|
let left;
|
|
48431
49496
|
try {
|
|
48432
|
-
left =
|
|
49497
|
+
left = fs28.readdirSync(dir);
|
|
48433
49498
|
} catch {
|
|
48434
49499
|
return;
|
|
48435
49500
|
}
|
|
@@ -48437,11 +49502,11 @@ function close(owner, id) {
|
|
|
48437
49502
|
removeTree(dir);
|
|
48438
49503
|
const wai = nodePath2.dirname(dir);
|
|
48439
49504
|
try {
|
|
48440
|
-
if (
|
|
49505
|
+
if (fs28.readdirSync(wai).length === 0) fs28.rmdirSync(wai);
|
|
48441
49506
|
} catch {
|
|
48442
49507
|
}
|
|
48443
49508
|
try {
|
|
48444
|
-
if (
|
|
49509
|
+
if (fs28.readdirSync(owner).length === 0) fs28.rmdirSync(owner);
|
|
48445
49510
|
} catch {
|
|
48446
49511
|
}
|
|
48447
49512
|
}
|
|
@@ -48463,14 +49528,14 @@ var transactionStore = {
|
|
|
48463
49528
|
listJournals(owner) {
|
|
48464
49529
|
return listJournals(owner);
|
|
48465
49530
|
},
|
|
48466
|
-
putFile(owner, id, area,
|
|
48467
|
-
putFile(owner, id, area,
|
|
49531
|
+
putFile(owner, id, area, path50, bytes) {
|
|
49532
|
+
putFile(owner, id, area, path50, bytes);
|
|
48468
49533
|
},
|
|
48469
|
-
readFile(owner, id, area,
|
|
48470
|
-
return readFile(owner, id, area,
|
|
49534
|
+
readFile(owner, id, area, path50) {
|
|
49535
|
+
return readFile(owner, id, area, path50);
|
|
48471
49536
|
},
|
|
48472
|
-
locate(owner, id, area,
|
|
48473
|
-
return locate(owner, id, area,
|
|
49537
|
+
locate(owner, id, area, path50) {
|
|
49538
|
+
return locate(owner, id, area, path50);
|
|
48474
49539
|
},
|
|
48475
49540
|
close(owner, id) {
|
|
48476
49541
|
close(owner, id);
|
|
@@ -48492,14 +49557,14 @@ var transactionRepository = {
|
|
|
48492
49557
|
listJournals(owner) {
|
|
48493
49558
|
return transactionStore.listJournals(owner);
|
|
48494
49559
|
},
|
|
48495
|
-
putFile(owner, id, area,
|
|
48496
|
-
transactionStore.putFile(owner, id, area,
|
|
49560
|
+
putFile(owner, id, area, path50, bytes) {
|
|
49561
|
+
transactionStore.putFile(owner, id, area, path50, bytes);
|
|
48497
49562
|
},
|
|
48498
|
-
readFile(owner, id, area,
|
|
48499
|
-
return transactionStore.readFile(owner, id, area,
|
|
49563
|
+
readFile(owner, id, area, path50) {
|
|
49564
|
+
return transactionStore.readFile(owner, id, area, path50);
|
|
48500
49565
|
},
|
|
48501
|
-
locate(owner, id, area,
|
|
48502
|
-
return transactionStore.locate(owner, id, area,
|
|
49566
|
+
locate(owner, id, area, path50) {
|
|
49567
|
+
return transactionStore.locate(owner, id, area, path50);
|
|
48503
49568
|
},
|
|
48504
49569
|
close(owner, id) {
|
|
48505
49570
|
transactionStore.close(owner, id);
|
|
@@ -48562,7 +49627,7 @@ function commonAncestor(paths) {
|
|
|
48562
49627
|
}
|
|
48563
49628
|
return at;
|
|
48564
49629
|
}
|
|
48565
|
-
function
|
|
49630
|
+
function diff2(rehearsal) {
|
|
48566
49631
|
return compare4(rehearsal);
|
|
48567
49632
|
}
|
|
48568
49633
|
function discard(rehearsal) {
|
|
@@ -48647,7 +49712,7 @@ function missingDirs(owner, entries) {
|
|
|
48647
49712
|
for (const entry of entries.filter((e) => e.action === "create")) {
|
|
48648
49713
|
const missing = [];
|
|
48649
49714
|
let at = path46.dirname(liveOf(owner, entry.path));
|
|
48650
|
-
while (!
|
|
49715
|
+
while (!fs29.existsSync(at) && at !== path46.dirname(at)) {
|
|
48651
49716
|
missing.unshift(at);
|
|
48652
49717
|
at = path46.dirname(at);
|
|
48653
49718
|
}
|
|
@@ -48681,7 +49746,7 @@ function stageBytes(rehearsal, journal, entry) {
|
|
|
48681
49746
|
function checkVolume(journal, entry, target) {
|
|
48682
49747
|
const staged = transactionRepository.locate(journal.owner, journal.id, "staged", entry.path);
|
|
48683
49748
|
let nearest = path46.dirname(target);
|
|
48684
|
-
while (!
|
|
49749
|
+
while (!fs29.existsSync(nearest) && nearest !== path46.dirname(nearest)) nearest = path46.dirname(nearest);
|
|
48685
49750
|
if (!sameVolume(staged, nearest)) {
|
|
48686
49751
|
throw new StagingError(`cross-volume: ${target} lies on another volume than its staged copy; a rename across volumes is not atomic`);
|
|
48687
49752
|
}
|
|
@@ -48765,7 +49830,7 @@ function recover(root, fix) {
|
|
|
48765
49830
|
var coordinatorRoot = (journal) => path46.resolve(journal.owner, journal.coordinator);
|
|
48766
49831
|
function journalAt(owner, id) {
|
|
48767
49832
|
try {
|
|
48768
|
-
const journal =
|
|
49833
|
+
const journal = fs29.existsSync(owner) ? transactionRepository.readJournal(owner, id) : null;
|
|
48769
49834
|
return journal ? { ...journal, owner } : null;
|
|
48770
49835
|
} catch {
|
|
48771
49836
|
return null;
|
|
@@ -48797,7 +49862,7 @@ function resolveOne(journal) {
|
|
|
48797
49862
|
for (const owner of [...owners.filter((o) => o !== coordinator.owner), coordinator.owner]) transactionRepository.close(owner, journal.id);
|
|
48798
49863
|
return { ...base, owners, phase, action: "cleaned", detail: "it had committed; its transaction directories were removed" };
|
|
48799
49864
|
}
|
|
48800
|
-
const absent = phase === "staging" ? [] : owners.filter((o) => !
|
|
49865
|
+
const absent = phase === "staging" ? [] : owners.filter((o) => !fs29.existsSync(o));
|
|
48801
49866
|
if (absent.length > 0) {
|
|
48802
49867
|
return { ...base, owners, phase, action: "refused", detail: `owner(s) out of reach: ${absent.join(", ")}` };
|
|
48803
49868
|
}
|
|
@@ -48923,7 +49988,7 @@ function rehearse2(plan6) {
|
|
|
48923
49988
|
const detail = e instanceof Error ? e.message : String(e);
|
|
48924
49989
|
return { ...plan6, refusals: [...plan6.refusals, { code: `${plan6.request.verb}-refused`, project: "", detail }] };
|
|
48925
49990
|
}
|
|
48926
|
-
const changes =
|
|
49991
|
+
const changes = diff2(rehearsal);
|
|
48927
49992
|
const refused3 = [
|
|
48928
49993
|
...unwritableOwners(changes).map((o) => ({ code: "family-partial", project: o, detail: `the plan must write ${o}, which this request may not write` })),
|
|
48929
49994
|
...deniedSubsystems(changes)
|
|
@@ -48932,7 +49997,7 @@ function rehearse2(plan6) {
|
|
|
48932
49997
|
discard(rehearsal);
|
|
48933
49998
|
return { ...plan6, refusals: [...plan6.refusals, ...refused3] };
|
|
48934
49999
|
}
|
|
48935
|
-
return { ...plan6, rehearsal, changes, relock: relockOf(plan6, changes) };
|
|
50000
|
+
return { ...plan6, rehearsal, changes, relock: relockOf(plan6, changes, rehearsal) };
|
|
48936
50001
|
}
|
|
48937
50002
|
function movedOf(plan6) {
|
|
48938
50003
|
const relocation = plan6.request.relocation;
|
|
@@ -49035,12 +50100,25 @@ function onCopy(rehearsal, run3) {
|
|
|
49035
50100
|
const top = reach2.topRoot !== void 0 ? rehearsalRoot(rehearsal, reach2.topRoot) : rehearsal.directory;
|
|
49036
50101
|
return runWithProjectBinding(rehearsalRoot(rehearsal, getProjectRoot()), { topRoot: top, parentReach: reach2.parentReach, ...reach2.narrowed ? { narrowed: true } : {} }, run3);
|
|
49037
50102
|
}
|
|
49038
|
-
function relockOf(plan6, changes) {
|
|
50103
|
+
function relockOf(plan6, changes, rehearsal) {
|
|
49039
50104
|
const ended = new Set(changes.filter((c) => c.action === "delete" && c.path === ".wai/project.yaml").map((c) => path47.resolve(c.project)));
|
|
49040
50105
|
const owners = [...new Set(changes.map((c) => path47.resolve(c.project)))].filter((o) => !ended.has(o));
|
|
49041
50106
|
const moved = movedOf(plan6);
|
|
49042
50107
|
const relocated = moved !== null && approvalRecord(moved.from) !== null ? [moved.to] : [];
|
|
49043
|
-
if (plan6.request.verb !== "rename")
|
|
50108
|
+
if (plan6.request.verb !== "rename") {
|
|
50109
|
+
const locked = owners.filter((owner) => approvalRecord(owner) !== null);
|
|
50110
|
+
const storageMove = plan6.request.verb === "externalize" && plan6.request.as !== "project";
|
|
50111
|
+
const stillApproved = (owner) => {
|
|
50112
|
+
if (!storageMove || !rehearsal) return false;
|
|
50113
|
+
try {
|
|
50114
|
+
const diff3 = diffAgainstApproval(rehearsalRoot(rehearsal, owner));
|
|
50115
|
+
return diff3 !== null && diff3.added.length + diff3.changed.length + diff3.removed.length === 0;
|
|
50116
|
+
} catch {
|
|
50117
|
+
return false;
|
|
50118
|
+
}
|
|
50119
|
+
};
|
|
50120
|
+
return [...locked.filter((owner) => !stillApproved(owner)), ...relocated];
|
|
50121
|
+
}
|
|
49044
50122
|
const renamed2 = plan6.edits.find((e) => e.write?.call === "renameId")?.write?.root;
|
|
49045
50123
|
const first = renamed2 !== void 0 ? path47.resolve(renamed2) : void 0;
|
|
49046
50124
|
return [...owners.filter((o) => o === first), ...owners.filter((o) => o !== first)];
|
|
@@ -49086,7 +50164,34 @@ function run2(request, dryRun) {
|
|
|
49086
50164
|
discard3(planned);
|
|
49087
50165
|
return { plan: planned, applied: false, relock: [] };
|
|
49088
50166
|
}
|
|
49089
|
-
|
|
50167
|
+
const report3 = apply2(planned);
|
|
50168
|
+
const made = report3.applied ? madeProjectRoot(report3) : null;
|
|
50169
|
+
if (made !== null) report3.sessionScaffold = scaffoldSession(made);
|
|
50170
|
+
return report3;
|
|
50171
|
+
}
|
|
50172
|
+
function madeProjectRoot(report3) {
|
|
50173
|
+
const { request, changes } = report3.plan;
|
|
50174
|
+
if (!(request.verb === "promote" || request.verb === "externalize" && request.as === "project")) return null;
|
|
50175
|
+
const created = changes.find((c) => c.action === "create" && c.path.replace(/\\/g, "/") === ".wai/specs/.index.yaml");
|
|
50176
|
+
const root = created?.project ?? null;
|
|
50177
|
+
return root !== null && fs30.existsSync(path48.join(root, ".wai", "project.yaml")) ? root : null;
|
|
50178
|
+
}
|
|
50179
|
+
function scaffoldSession(root) {
|
|
50180
|
+
try {
|
|
50181
|
+
return runWithProjectRoot(root, () => {
|
|
50182
|
+
const config = loadProjectConfig();
|
|
50183
|
+
const targets = config ? activeTargetTypes(config) : ["claude"];
|
|
50184
|
+
const written = [...reinjectLocalGuides(root, targets)];
|
|
50185
|
+
written.push(...exportSddSkills2(targets).destinations);
|
|
50186
|
+
if (targets.includes("claude")) {
|
|
50187
|
+
const server = registerProjectServer(root);
|
|
50188
|
+
if (server !== null) written.push(server);
|
|
50189
|
+
}
|
|
50190
|
+
return written.map((f) => path48.relative(root, f).replace(/\\/g, "/") || ".");
|
|
50191
|
+
});
|
|
50192
|
+
} catch (e) {
|
|
50193
|
+
return [`not set up (${e instanceof Error ? e.message : String(e)}): run \`wairon generate\` and \`wairon mcp install --backend claude\` there`];
|
|
50194
|
+
}
|
|
49090
50195
|
}
|
|
49091
50196
|
function pending2() {
|
|
49092
50197
|
const found = recover3(getProjectRoot(), false);
|
|
@@ -49113,7 +50218,7 @@ function statusFamilyContext() {
|
|
|
49113
50218
|
const members = listDirectChainedSubprojects(root);
|
|
49114
50219
|
if (members.length > 0) {
|
|
49115
50220
|
const described = members.map((m) => {
|
|
49116
|
-
const rel2 =
|
|
50221
|
+
const rel2 = path49.relative(root, m.dir).split(path49.sep).join("/") || ".";
|
|
49117
50222
|
return `${m.alias} (${rel2}${m.form === "mount" ? ", legacy L1 mount" : ""})`;
|
|
49118
50223
|
});
|
|
49119
50224
|
lines.push(`Family: member projects declared here \u2014 ${described.join(", ")}.`);
|
|
@@ -49213,7 +50318,8 @@ function migrationView(report3, dryRun) {
|
|
|
49213
50318
|
...madeAt ? {
|
|
49214
50319
|
newProject: {
|
|
49215
50320
|
root: madeAt,
|
|
49216
|
-
|
|
50321
|
+
...report3.sessionScaffold ? { sessionScaffold: report3.sessionScaffold } : {},
|
|
50322
|
+
next: report3.sessionScaffold && !report3.sessionScaffold.some((f) => f.startsWith("not set up")) ? `The new project at ${madeAt} is set up for its own sessions (its guide, skills and .mcp.json, listed under sessionScaffold), as the CLI's \`wairon member promote\` / \`subsystem externalize --as project\` set it up. A session opened in that folder designs it \u2014 its specs, L0 export table and project.yaml are written from its own root.` : `The new project at ${madeAt} has no guide, skills or MCP registration of its own yet. The human runs, in that folder: \`wairon generate\` and \`wairon mcp install --backend claude\` (the CLI's \`wairon member promote\` / \`subsystem externalize --as project\` do this themselves). Its own session then designs it \u2014 its specs, L0 export table and project.yaml are written from its own root.`
|
|
49217
50323
|
}
|
|
49218
50324
|
} : {},
|
|
49219
50325
|
...report3.outcome ? { outcome: { committed: report3.outcome.committed, restored: report3.outcome.restored, unrestored: report3.outcome.unrestored, ...report3.outcome.failure ? { failure: report3.outcome.failure } : {} } } : {},
|
|
@@ -49269,6 +50375,31 @@ var externalRemovalOutput = {
|
|
|
49269
50375
|
refusal: import_zod12.z.string().optional().describe("Why nothing was removed, in one sentence."),
|
|
49270
50376
|
...staleServerOutput
|
|
49271
50377
|
};
|
|
50378
|
+
var externalConsumersOutput = {
|
|
50379
|
+
consumers: import_zod12.z.array(import_zod12.z.object({
|
|
50380
|
+
project: import_zod12.z.string(),
|
|
50381
|
+
key: import_zod12.z.string(),
|
|
50382
|
+
directory: import_zod12.z.string(),
|
|
50383
|
+
alias: import_zod12.z.string(),
|
|
50384
|
+
section: import_zod12.z.enum(["externals", "members"]),
|
|
50385
|
+
names: import_zod12.z.array(import_zod12.z.string()),
|
|
50386
|
+
broken: import_zod12.z.array(import_zod12.z.string()).optional(),
|
|
50387
|
+
found: import_zod12.z.enum(["search"]).optional()
|
|
50388
|
+
})).describe("Each consumer with the alias and section it declares this project under, the public names its specs use, the names it still writes that this project no longer exports (broken), and found: search for one met in a searched folder outside the family."),
|
|
50389
|
+
...staleServerOutput
|
|
50390
|
+
};
|
|
50391
|
+
var surfaceDiffOutput = {
|
|
50392
|
+
project: import_zod12.z.string(),
|
|
50393
|
+
against: import_zod12.z.string().describe("What the surface was compared with: the last committed approval, a named revision, or a saved snapshot file."),
|
|
50394
|
+
changes: import_zod12.z.array(import_zod12.z.object({
|
|
50395
|
+
kind: import_zod12.z.enum(["added", "removed", "renamed", "changed"]),
|
|
50396
|
+
name: import_zod12.z.string(),
|
|
50397
|
+
member: import_zod12.z.string().optional(),
|
|
50398
|
+
from: import_zod12.z.string().optional(),
|
|
50399
|
+
detail: import_zod12.z.string()
|
|
50400
|
+
})).describe("Every public name or contract method added, removed, renamed (traced) or changed in signature, sorted by name."),
|
|
50401
|
+
...staleServerOutput
|
|
50402
|
+
};
|
|
49272
50403
|
var externalUseChangeOutput = {
|
|
49273
50404
|
alias: import_zod12.z.string(),
|
|
49274
50405
|
written: import_zod12.z.boolean().describe("Whether .wai/project.yaml now carries the changed `use` (false on a refusal, a dry run and when nothing changed)."),
|
|
@@ -49481,6 +50612,22 @@ var specChangeReportOutput = {
|
|
|
49481
50612
|
),
|
|
49482
50613
|
...staleServerOutput
|
|
49483
50614
|
};
|
|
50615
|
+
function statusNetworkLine() {
|
|
50616
|
+
try {
|
|
50617
|
+
const network = loadProjectConfig()?.network;
|
|
50618
|
+
if (network) {
|
|
50619
|
+
const description = typeof network === "object" && network.description ? ` ("${network.description}")` : "";
|
|
50620
|
+
return `Network: declared${description} \u2014 this project and every member below it form one isolated network boundary, and the network rules (GATEWAY_BYPASSED, ENTRY_UNPROVEN, ...) judge the design; sdd_get_network_flows lists who may reach what.
|
|
50621
|
+
`;
|
|
50622
|
+
}
|
|
50623
|
+
if (resolveChainingParent()) {
|
|
50624
|
+
return "Network: none declared here \u2014 a network the enclosing family declares is judged at its root, and sdd_get_network_flows reads it from there.\n";
|
|
50625
|
+
}
|
|
50626
|
+
return "Network: none declared \u2014 Portal entries are not narrowed by an isolated network (sdd_set_network declares one).\n";
|
|
50627
|
+
} catch {
|
|
50628
|
+
return "";
|
|
50629
|
+
}
|
|
50630
|
+
}
|
|
49484
50631
|
function externalsSection(advised) {
|
|
49485
50632
|
if (advised.length === 0) return "";
|
|
49486
50633
|
const lines = advised.map((i) => `- [${i.code}] ${i.message}`);
|
|
@@ -49826,6 +50973,7 @@ var SPEC_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
49826
50973
|
"sdd_rename_method",
|
|
49827
50974
|
"sdd_rename_type",
|
|
49828
50975
|
"sdd_rename_field",
|
|
50976
|
+
"sdd_rename_param",
|
|
49829
50977
|
"sdd_add_component",
|
|
49830
50978
|
"sdd_define_interface",
|
|
49831
50979
|
"sdd_set_endpoints",
|
|
@@ -50336,7 +51484,7 @@ ${renderChangeReport(report3)}`,
|
|
|
50336
51484
|
server,
|
|
50337
51485
|
"sdd_rename_method",
|
|
50338
51486
|
{
|
|
50339
|
-
description: "Rename a contract method and retarget every reference to it in the bound tree. The method moves on every interface of the component that declares it \u2014 its name, and the name inside its signature \u2014 and on the implementations of those contracts, carrying narrative, sourcePath, symbol, detail, intent and findings unchanged; an implementation that declared no symbol is pinned to the old name unless pinSymbol is false, so the function it already binds to keeps binding. Narrative call, register and dispatch steps naming this component and method, dispatch-table bindings and lifecycle entrypoints are retargeted, and so are the findings keyed on it: a lint allow at the method on a spec it moved in or covering `<component>.<method>`, and every debt-register entry (.wai/project.yaml rules.conformance.carried) keyed the same way \u2014 rewritten in place with every comment and all other formatting kept (a register that cannot be rewritten that precisely is refused before the first write). Prose is never rewritten and a gRPC endpoint binding keeps its wire method \u2014 renaming a contract method must not silently rename an RPC; both are reported as mentions. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename its method from that project's own root), a new name that is not an identifier in the method casing of the tree \u2014 the configured rules.naming.methods, else the convention of its targetLanguage: snake_case for Rust or Python, camelCase for TypeScript (invalid-name), a method the component does not declare (method-missing), and a name a moving contract already declares (name-taken). Returns the specs the method moved in, the specs retargeted (lint allows included), the specs whose prose still names it, the pinned symbol when one was set, and `carried`: each register edit.",
|
|
51487
|
+
description: "Rename a contract method and retarget every reference to it in the bound tree. The method moves on every interface of the component that declares it \u2014 its name, and the name inside its signature \u2014 and on the implementations of those contracts, carrying narrative, sourcePath, symbol, detail, intent and findings unchanged; an implementation that declared no symbol is pinned to the old name unless pinSymbol is false, so the function it already binds to keeps binding. Narrative call, register and dispatch steps naming this component and method, dispatch-table bindings and lifecycle entrypoints are retargeted, and so are the findings keyed on it: a lint allow at the method on a spec it moved in or covering `<component>.<method>`, and every debt-register entry (.wai/project.yaml rules.conformance.carried) keyed the same way \u2014 rewritten in place with every comment and all other formatting kept (a register that cannot be rewritten that precisely is refused before the first write). Prose is never rewritten and a gRPC endpoint binding keeps its wire method \u2014 renaming a contract method must not silently rename an RPC; both are reported as mentions. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename its method from that project's own root), a new name that is not an identifier in the method casing of the tree \u2014 the configured rules.naming.methods, else the convention of its targetLanguage: snake_case for Rust or Python, camelCase for TypeScript; for a component one of whose contracts implements another project's extension point, any identifier, since the producer names those methods (invalid-name), a method the component does not declare (method-missing), and a name a moving contract already declares (name-taken). Returns the specs the method moved in, the specs retargeted (lint allows included), the specs whose prose still names it, the pinned symbol when one was set, and `carried`: each register edit.",
|
|
50340
51488
|
inputSchema: {
|
|
50341
51489
|
id: import_zod12.z.string().describe("The component whose method is renamed (namespaced if needed)"),
|
|
50342
51490
|
method: import_zod12.z.string().describe("The method name as it stands"),
|
|
@@ -50393,6 +51541,26 @@ ${renderChangeReport(report3)}`,
|
|
|
50393
51541
|
}
|
|
50394
51542
|
}
|
|
50395
51543
|
);
|
|
51544
|
+
reg(
|
|
51545
|
+
server,
|
|
51546
|
+
"sdd_rename_param",
|
|
51547
|
+
{
|
|
51548
|
+
description: "Rename a parameter of a contract method. The parameter moves on every interface of the component that declares the method, keeping its place, type, description and optionality, and its old name joins the parameter's rename trace (previousNames), which the design export shows as `formerly` so a generator or consumer can tell the rename from a signature change; each signature is re-derived from the params, a method whose signatureFrom names this one follows by itself, and an HTTP endpoint path placeholder `{old}` on the method's own binding is respelled (the URL a caller sends is unchanged). Prose is never rewritten. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component), a method no contract of the component declares (method-missing), a parameter the method does not declare or takes from a signatureFrom (param-missing), a new name that is not an identifier, or not in rules.naming.variables when that is set and the component implements no extension point (invalid-name), and a name another parameter of the method holds (name-taken) or retired (name-retired). Returns the component, method, from and to, the interfaces it moved in and the placeholders respelled. The approval reads the contract as changed until the next `wairon lock`, as with any edit.",
|
|
51549
|
+
inputSchema: {
|
|
51550
|
+
id: import_zod12.z.string().describe("The component whose contract method's parameter is renamed (namespaced if needed)"),
|
|
51551
|
+
method: import_zod12.z.string().describe("The contract method"),
|
|
51552
|
+
param: import_zod12.z.string().describe("The parameter's name as it stands"),
|
|
51553
|
+
newName: import_zod12.z.string().describe("Its new name: an identifier")
|
|
51554
|
+
}
|
|
51555
|
+
},
|
|
51556
|
+
({ id, method, param, newName }) => {
|
|
51557
|
+
try {
|
|
51558
|
+
return json(renameParam(qualifiedComponentId(id), method, param, newName));
|
|
51559
|
+
} catch (e) {
|
|
51560
|
+
return errText(String(e));
|
|
51561
|
+
}
|
|
51562
|
+
}
|
|
51563
|
+
);
|
|
50396
51564
|
reg(
|
|
50397
51565
|
server,
|
|
50398
51566
|
"sdd_move_methods",
|
|
@@ -51086,7 +52254,7 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
51086
52254
|
memberDepth,
|
|
51087
52255
|
approvals
|
|
51088
52256
|
});
|
|
51089
|
-
const family = statusFamilyContext();
|
|
52257
|
+
const family = statusFamilyContext() + statusNetworkLine();
|
|
51090
52258
|
const verdict = approvalVerdict(approvals);
|
|
51091
52259
|
return text2(`${banner}${family}${report3.text}${verdict.text}${externalsSection(adviseExternals())}`);
|
|
51092
52260
|
} catch (e) {
|
|
@@ -51210,6 +52378,48 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
51210
52378
|
}
|
|
51211
52379
|
}
|
|
51212
52380
|
);
|
|
52381
|
+
reg(
|
|
52382
|
+
server,
|
|
52383
|
+
"sdd_list_consumers",
|
|
52384
|
+
{
|
|
52385
|
+
description: `Who consumes the bound project \u2014 the producer's answer to "who breaks if I change my surface": every project of the family in reach that declares it (as an external or a member), and, with search, every project root in (or directly under) the named folders that declares it as an external by a path source reaching it or a git source naming its id \u2014 a sibling checkout outside the family. Each with the alias, the public names its specs use, and the names it still writes that this project no longer exports. A read: writes nothing.`,
|
|
52386
|
+
inputSchema: {
|
|
52387
|
+
search: import_zod12.z.array(import_zod12.z.string()).optional().describe("Folders to scan for consumers outside the family (e.g. the folder holding the sibling checkouts), relative to the bound project root or absolute")
|
|
52388
|
+
},
|
|
52389
|
+
outputSchema: externalConsumersOutput
|
|
52390
|
+
},
|
|
52391
|
+
({ search }) => {
|
|
52392
|
+
try {
|
|
52393
|
+
const root = getProjectRoot();
|
|
52394
|
+
const consumers = listConsumers3(search?.map((d) => path49.resolve(root, d)));
|
|
52395
|
+
const lines = consumers.length ? consumers.map((c) => `${c.project} (${c.section}.${c.alias})${c.found ? ` [${c.directory}]` : ""}: ${c.names.length ? c.names.join(", ") : "declares it, uses no name yet"}${c.broken?.length ? ` \u2014 still writes ${c.broken.join(", ")} (no longer exported)` : ""}`) : [`No project ${search?.length ? "of the family in reach, nor in the searched folders," : "of the family in reach"} consumes this project.${search?.length ? "" : " Pass search with the folder holding sibling checkouts to look outside the family."}`];
|
|
52396
|
+
return structured(lines.join("\n"), { consumers });
|
|
52397
|
+
} catch (e) {
|
|
52398
|
+
return errMessage(e);
|
|
52399
|
+
}
|
|
52400
|
+
}
|
|
52401
|
+
);
|
|
52402
|
+
reg(
|
|
52403
|
+
server,
|
|
52404
|
+
"sdd_surface_diff",
|
|
52405
|
+
{
|
|
52406
|
+
description: "The bound project's public-surface changelog: its export table now against the same table at its last committed approval (the last commit that changed .wai/lock.json) \u2014 or at a named git revision, or in a saved native surface snapshot file \u2014 as names and contract methods added, removed, renamed (from the rename trace) and changed in signature. What a producer writes release notes from and weighs a breaking change by (pair it with sdd_list_consumers). A read: writes nothing.",
|
|
52407
|
+
inputSchema: {
|
|
52408
|
+
against: import_zod12.z.string().optional().describe("A git revision (branch, tag, commit) or a saved native surface snapshot file to compare with; the last committed approval when omitted")
|
|
52409
|
+
},
|
|
52410
|
+
outputSchema: surfaceDiffOutput
|
|
52411
|
+
},
|
|
52412
|
+
({ against }) => {
|
|
52413
|
+
try {
|
|
52414
|
+
const diff3 = diffSurface(against);
|
|
52415
|
+
const lines = diff3.changes.length ? diff3.changes.map((c) => `${c.kind} ${c.member ? `${c.name}.${c.member}` : c.name} \u2014 ${c.detail}`) : ["no change \u2014 every exported name, method and signature is as it was"];
|
|
52416
|
+
return structured(`Public surface of "${diff3.project}" against ${diff3.against}:
|
|
52417
|
+
${lines.join("\n")}`, { ...diff3 });
|
|
52418
|
+
} catch (e) {
|
|
52419
|
+
return errMessage(e);
|
|
52420
|
+
}
|
|
52421
|
+
}
|
|
52422
|
+
);
|
|
51213
52423
|
reg(
|
|
51214
52424
|
server,
|
|
51215
52425
|
"sdd_get_network_flows",
|
|
@@ -51478,6 +52688,7 @@ function loadProjectConfig3() {
|
|
|
51478
52688
|
SwitchCaseSchema,
|
|
51479
52689
|
SystemPublicInterfaceSchema,
|
|
51480
52690
|
SystemSpecSchema,
|
|
52691
|
+
TECHNOLOGY_PACKAGES,
|
|
51481
52692
|
TargetConfigSchema,
|
|
51482
52693
|
TechnologySchema,
|
|
51483
52694
|
TemplateNotFoundError,
|
|
@@ -51508,7 +52719,9 @@ function loadProjectConfig3() {
|
|
|
51508
52719
|
approvalKeyIn,
|
|
51509
52720
|
approvalRecord,
|
|
51510
52721
|
approvalVerdict,
|
|
52722
|
+
approvedRevision,
|
|
51511
52723
|
approvedStatuses,
|
|
52724
|
+
asSchemaReads,
|
|
51512
52725
|
assertContainedProjectPath,
|
|
51513
52726
|
assertProjectInitialized,
|
|
51514
52727
|
attachRetiredMounts,
|
|
@@ -51756,6 +52969,7 @@ function loadProjectConfig3() {
|
|
|
51756
52969
|
reexpress,
|
|
51757
52970
|
registerExporter,
|
|
51758
52971
|
registerPackRef,
|
|
52972
|
+
registerProjectServer,
|
|
51759
52973
|
reinjectLocalGuides,
|
|
51760
52974
|
relationHealth,
|
|
51761
52975
|
removeDomain,
|
|
@@ -51768,6 +52982,7 @@ function loadProjectConfig3() {
|
|
|
51768
52982
|
renameField,
|
|
51769
52983
|
renameId,
|
|
51770
52984
|
renameMethod,
|
|
52985
|
+
renameParam,
|
|
51771
52986
|
renameType,
|
|
51772
52987
|
renderDiagram,
|
|
51773
52988
|
repairForeignStepFields,
|
|
@@ -51824,8 +53039,10 @@ function loadProjectConfig3() {
|
|
|
51824
53039
|
storedMethodSignature,
|
|
51825
53040
|
storedTypeMethod,
|
|
51826
53041
|
subsystemDesignView,
|
|
53042
|
+
surfaceChanges,
|
|
51827
53043
|
syncContextFiles,
|
|
51828
53044
|
technologyName,
|
|
53045
|
+
technologyPackages,
|
|
51829
53046
|
technologyTokens,
|
|
51830
53047
|
toMarkdown,
|
|
51831
53048
|
transportExportKind,
|