@wairon/cli 5.1.1-dev.100 → 5.1.1-dev.102
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 +83 -37
- package/dist/cli/index.js +1313 -430
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +37562 -0
- package/dist/index.js +1033 -401
- package/dist/index.js.map +1 -1
- package/dist/templates/architect.yaml +14 -12
- package/dist/templates/domain-owner.yaml +3 -2
- package/dist/templates/skills/sdd-architect.md +11 -5
- package/dist/templates/skills/sdd-auditor.md +5 -4
- package/dist/templates/skills/sdd-delegate.md +3 -2
- package/dist/templates/skills/sdd-implement.md +4 -4
- package/dist/templates/skills/sdd-narrative.md +1 -1
- package/dist/webapp.html +770 -0
- package/package.json +2 -1
- package/schemas/design-export-1.json +4 -4
package/dist/index.js
CHANGED
|
@@ -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 posix7 = relativePath.split(path210.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
3320
|
+
return posix7 === "" || posix7 === "." ? "." : posix7;
|
|
3321
3321
|
}
|
|
3322
3322
|
function slugify(projectName) {
|
|
3323
3323
|
const slug2 = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -3709,6 +3709,7 @@ __export(src_exports, {
|
|
|
3709
3709
|
keyIn: () => keyIn,
|
|
3710
3710
|
landReference: () => landReference,
|
|
3711
3711
|
languageOfSourcePath: () => languageOfSourcePath,
|
|
3712
|
+
lineEndingFor: () => lineEndingFor,
|
|
3712
3713
|
listDirectChainedSubprojects: () => listDirectChainedSubprojects,
|
|
3713
3714
|
listExternals: () => listExternals2,
|
|
3714
3715
|
listFiles: () => listFiles,
|
|
@@ -3828,6 +3829,7 @@ __export(src_exports, {
|
|
|
3828
3829
|
resolveSubsystemExports: () => resolveSubsystemExports,
|
|
3829
3830
|
resolveVariantGuidance: () => resolveVariantGuidance,
|
|
3830
3831
|
retireSpecialists: () => retireSpecialists,
|
|
3832
|
+
returnTypeOf: () => returnTypeOf,
|
|
3831
3833
|
rewriteReferences: () => rewriteReferences2,
|
|
3832
3834
|
runWithHostedLookup: () => runWithHostedLookup,
|
|
3833
3835
|
runWithProjectBinding: () => runWithProjectBinding,
|
|
@@ -3866,6 +3868,7 @@ __export(src_exports, {
|
|
|
3866
3868
|
typeIsMany: () => typeIsMany,
|
|
3867
3869
|
typeMatchesRef: () => typeMatchesRef,
|
|
3868
3870
|
typeNamedRefs: () => typeNamedRefs,
|
|
3871
|
+
typeProblemEnumProposal: () => typeProblemEnumProposal,
|
|
3869
3872
|
typeProblemIntProposal: () => typeProblemIntProposal,
|
|
3870
3873
|
typeSourceFiles: () => typeSourceFiles,
|
|
3871
3874
|
typeSpellingFacts: () => typeSpellingFacts,
|
|
@@ -3879,6 +3882,7 @@ __export(src_exports, {
|
|
|
3879
3882
|
validateProject: () => validateProject,
|
|
3880
3883
|
validateProjectConfig: () => validateProjectConfig,
|
|
3881
3884
|
validateRegistry: () => validateRegistry,
|
|
3885
|
+
withLineEndings: () => withLineEndings,
|
|
3882
3886
|
withPack: () => withPack,
|
|
3883
3887
|
writeFile: () => writeFile,
|
|
3884
3888
|
writeFileIfChanged: () => writeFileIfChanged,
|
|
@@ -3893,8 +3897,8 @@ __export(src_exports, {
|
|
|
3893
3897
|
module.exports = __toCommonJS(src_exports);
|
|
3894
3898
|
|
|
3895
3899
|
// src/config/project-config.ts
|
|
3896
|
-
var
|
|
3897
|
-
var
|
|
3900
|
+
var fs3 = __toESM(require("fs"));
|
|
3901
|
+
var path5 = __toESM(require("path"));
|
|
3898
3902
|
var YAML = __toESM(require("yaml"));
|
|
3899
3903
|
var import_zod4 = require("zod");
|
|
3900
3904
|
|
|
@@ -3907,17 +3911,119 @@ function ensureDir(dirPath) {
|
|
|
3907
3911
|
}
|
|
3908
3912
|
function writeFile(filePath, content) {
|
|
3909
3913
|
ensureDir(path.dirname(filePath));
|
|
3910
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
3914
|
+
fs.writeFileSync(filePath, withLineEndings(filePath, content), "utf-8");
|
|
3911
3915
|
}
|
|
3912
3916
|
function writeFileIfChanged(filePath, content) {
|
|
3917
|
+
const text3 = withLineEndings(filePath, content);
|
|
3913
3918
|
if (fs.existsSync(filePath)) {
|
|
3914
3919
|
const existing = fs.readFileSync(filePath, "utf-8");
|
|
3915
|
-
if (existing ===
|
|
3920
|
+
if (existing === text3) return false;
|
|
3916
3921
|
}
|
|
3917
3922
|
ensureDir(path.dirname(filePath));
|
|
3918
|
-
fs.writeFileSync(filePath,
|
|
3923
|
+
fs.writeFileSync(filePath, text3, "utf-8");
|
|
3919
3924
|
return true;
|
|
3920
3925
|
}
|
|
3926
|
+
var EOL_SAMPLE_BYTES = 64 * 1024;
|
|
3927
|
+
function endingsOf(text3) {
|
|
3928
|
+
const crlf = text3.split("\r\n").length - 1;
|
|
3929
|
+
const lf = text3.split("\n").length - 1 - crlf;
|
|
3930
|
+
if (crlf === 0 && lf === 0) return null;
|
|
3931
|
+
return crlf > lf ? "\r\n" : "\n";
|
|
3932
|
+
}
|
|
3933
|
+
function sampleOf(file) {
|
|
3934
|
+
let fd;
|
|
3935
|
+
try {
|
|
3936
|
+
fd = fs.openSync(file, "r");
|
|
3937
|
+
const buffer = Buffer.alloc(EOL_SAMPLE_BYTES);
|
|
3938
|
+
const read2 = fs.readSync(fd, buffer, 0, EOL_SAMPLE_BYTES, 0);
|
|
3939
|
+
return buffer.subarray(0, read2).toString("utf-8");
|
|
3940
|
+
} catch {
|
|
3941
|
+
return null;
|
|
3942
|
+
} finally {
|
|
3943
|
+
if (fd !== void 0) fs.closeSync(fd);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
function attributeMatches(pattern, relative20) {
|
|
3947
|
+
const glob = pattern.replace(/^\//, "");
|
|
3948
|
+
const target = glob.includes("/") ? relative20 : path.posix.basename(relative20);
|
|
3949
|
+
const source = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\u0000/g, "(?:.*/)?");
|
|
3950
|
+
return new RegExp(`^${source}$`).test(target);
|
|
3951
|
+
}
|
|
3952
|
+
function attributeEol(attributesFile, file) {
|
|
3953
|
+
const text3 = readFileOrNull(attributesFile);
|
|
3954
|
+
if (text3 === null) return null;
|
|
3955
|
+
const relative20 = path.relative(path.dirname(attributesFile), file).split(path.sep).join("/");
|
|
3956
|
+
let eol = null;
|
|
3957
|
+
for (const raw of text3.split(/\r?\n/)) {
|
|
3958
|
+
const line = raw.trim();
|
|
3959
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
3960
|
+
const [pattern, ...attrs] = line.split(/\s+/);
|
|
3961
|
+
if (!attributeMatches(pattern, relative20)) continue;
|
|
3962
|
+
for (const attr of attrs) {
|
|
3963
|
+
if (attr === "eol=crlf") eol = "\r\n";
|
|
3964
|
+
else if (attr === "eol=lf") eol = "\n";
|
|
3965
|
+
else if (attr === "-text" || attr === "binary") eol = null;
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
return eol;
|
|
3969
|
+
}
|
|
3970
|
+
function conventionFolders(dir) {
|
|
3971
|
+
const folders = [];
|
|
3972
|
+
let at = path.resolve(dir);
|
|
3973
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
3974
|
+
folders.push(at);
|
|
3975
|
+
if (fs.existsSync(path.join(at, ".git")) || fs.existsSync(path.join(at, ".wai"))) return folders;
|
|
3976
|
+
const parent = path.dirname(at);
|
|
3977
|
+
if (parent === at) break;
|
|
3978
|
+
at = parent;
|
|
3979
|
+
}
|
|
3980
|
+
return folders.slice(0, 1);
|
|
3981
|
+
}
|
|
3982
|
+
function folderEndings(folder, ext, exclude) {
|
|
3983
|
+
let names;
|
|
3984
|
+
try {
|
|
3985
|
+
names = fs.readdirSync(folder);
|
|
3986
|
+
} catch {
|
|
3987
|
+
return null;
|
|
3988
|
+
}
|
|
3989
|
+
const candidates = names.filter((n) => path.join(folder, n) !== exclude && /\.(ya?ml|json|md|ts|js|txt)$/i.test(n)).sort((a, b) => Number(path.extname(b) === ext) - Number(path.extname(a) === ext)).slice(0, 24);
|
|
3990
|
+
let crlf = 0;
|
|
3991
|
+
let lf = 0;
|
|
3992
|
+
for (const name of candidates) {
|
|
3993
|
+
const full = path.join(folder, name);
|
|
3994
|
+
try {
|
|
3995
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
3996
|
+
} catch {
|
|
3997
|
+
continue;
|
|
3998
|
+
}
|
|
3999
|
+
const sample = sampleOf(full);
|
|
4000
|
+
const endings = sample === null ? null : endingsOf(sample);
|
|
4001
|
+
if (endings === "\r\n") crlf++;
|
|
4002
|
+
else if (endings === "\n") lf++;
|
|
4003
|
+
}
|
|
4004
|
+
if (crlf === lf) return null;
|
|
4005
|
+
return crlf > lf ? "\r\n" : "\n";
|
|
4006
|
+
}
|
|
4007
|
+
function lineEndingFor(filePath) {
|
|
4008
|
+
const own = sampleOf(filePath);
|
|
4009
|
+
const existing = own === null ? null : endingsOf(own);
|
|
4010
|
+
if (existing) return existing;
|
|
4011
|
+
const folders = conventionFolders(path.dirname(filePath));
|
|
4012
|
+
for (const folder of folders) {
|
|
4013
|
+
const eol = attributeEol(path.join(folder, ".gitattributes"), filePath);
|
|
4014
|
+
if (eol) return eol;
|
|
4015
|
+
}
|
|
4016
|
+
const ext = path.extname(filePath);
|
|
4017
|
+
for (const folder of folders) {
|
|
4018
|
+
const endings = folderEndings(folder, ext, path.resolve(filePath));
|
|
4019
|
+
if (endings) return endings;
|
|
4020
|
+
}
|
|
4021
|
+
return "\n";
|
|
4022
|
+
}
|
|
4023
|
+
function withLineEndings(filePath, content) {
|
|
4024
|
+
const normalised = content.replace(/\r\n/g, "\n");
|
|
4025
|
+
return lineEndingFor(filePath) === "\r\n" ? normalised.replace(/\n/g, "\r\n") : normalised;
|
|
4026
|
+
}
|
|
3921
4027
|
function readFileOrNull(filePath) {
|
|
3922
4028
|
try {
|
|
3923
4029
|
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -4063,44 +4169,67 @@ function aiDirAt(root, ...segments) {
|
|
|
4063
4169
|
return path.join(base, ...segments);
|
|
4064
4170
|
}
|
|
4065
4171
|
|
|
4066
|
-
// src/
|
|
4067
|
-
var
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
function readYamlFile(filePath) {
|
|
4085
|
-
const content = readFileOrNull(filePath);
|
|
4086
|
-
if (content === null) return null;
|
|
4087
|
-
return parseYaml(content, filePath);
|
|
4088
|
-
}
|
|
4089
|
-
function writeYamlFile(filePath, value) {
|
|
4090
|
-
writeFile(filePath, serializeYaml(value));
|
|
4172
|
+
// src/config/defaults.ts
|
|
4173
|
+
var os = __toESM(require("os"));
|
|
4174
|
+
var path2 = __toESM(require("path"));
|
|
4175
|
+
var DEFAULT_TARGET_DIRS = {
|
|
4176
|
+
claude: ".claude/agents",
|
|
4177
|
+
gemini: ".gemini/agents",
|
|
4178
|
+
agy: ".gemini/agents",
|
|
4179
|
+
cursor: ".cursor/rules",
|
|
4180
|
+
copilot: ".github/prompts",
|
|
4181
|
+
codex: ".codex/agents"
|
|
4182
|
+
};
|
|
4183
|
+
function defaultTargetConfig(type) {
|
|
4184
|
+
return {
|
|
4185
|
+
type,
|
|
4186
|
+
outputDir: DEFAULT_TARGET_DIRS[type],
|
|
4187
|
+
enabled: true
|
|
4188
|
+
};
|
|
4091
4189
|
}
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4190
|
+
var WAIRON_VERSION = "5.1.1-dev.102";
|
|
4191
|
+
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
4192
|
+
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
4193
|
+
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
4194
|
+
function globalTemplatesDir(projectOverride) {
|
|
4195
|
+
if (process.env.WAIRON_TEMPLATES_DIR) {
|
|
4196
|
+
return process.env.WAIRON_TEMPLATES_DIR;
|
|
4197
|
+
}
|
|
4198
|
+
if (projectOverride) {
|
|
4199
|
+
return projectOverride;
|
|
4099
4200
|
}
|
|
4201
|
+
return path2.join(os.homedir(), ".wairon", "templates");
|
|
4100
4202
|
}
|
|
4101
|
-
|
|
4102
|
-
|
|
4203
|
+
var BACKEND_COMMANDS = {
|
|
4204
|
+
claude: "claude",
|
|
4205
|
+
gemini: "gemini"
|
|
4206
|
+
};
|
|
4207
|
+
function backendCommand(backend) {
|
|
4208
|
+
return BACKEND_COMMANDS[backend] ?? backend;
|
|
4103
4209
|
}
|
|
4210
|
+
var SUPPORTED_ALIASES = ["wai"];
|
|
4211
|
+
var SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
4212
|
+
"node_modules",
|
|
4213
|
+
"dist",
|
|
4214
|
+
"build",
|
|
4215
|
+
"out",
|
|
4216
|
+
".git",
|
|
4217
|
+
".cache",
|
|
4218
|
+
"coverage",
|
|
4219
|
+
"__pycache__",
|
|
4220
|
+
".venv",
|
|
4221
|
+
"venv",
|
|
4222
|
+
"target",
|
|
4223
|
+
// Rust
|
|
4224
|
+
"vendor"
|
|
4225
|
+
]);
|
|
4226
|
+
|
|
4227
|
+
// src/core/adapters/git-source.ts
|
|
4228
|
+
var import_child_process = require("child_process");
|
|
4229
|
+
var crypto = __toESM(require("crypto"));
|
|
4230
|
+
var fs2 = __toESM(require("fs"));
|
|
4231
|
+
var os2 = __toESM(require("os"));
|
|
4232
|
+
var path3 = __toESM(require("path"));
|
|
4104
4233
|
|
|
4105
4234
|
// src/utils/errors.ts
|
|
4106
4235
|
var WaironError = class extends Error {
|
|
@@ -4150,6 +4279,167 @@ ${reasons.map((r) => ` - ${r}`).join("\n")}`);
|
|
|
4150
4279
|
}
|
|
4151
4280
|
};
|
|
4152
4281
|
|
|
4282
|
+
// src/core/adapters/git-source.ts
|
|
4283
|
+
var SourceUnavailable = class extends WaironError {
|
|
4284
|
+
constructor(message) {
|
|
4285
|
+
super(message);
|
|
4286
|
+
this.name = "SourceUnavailable";
|
|
4287
|
+
}
|
|
4288
|
+
};
|
|
4289
|
+
function osUserCache() {
|
|
4290
|
+
if (process.platform === "win32") return process.env.LOCALAPPDATA ?? path3.join(os2.homedir(), "AppData", "Local");
|
|
4291
|
+
if (process.platform === "darwin") return path3.join(os2.homedir(), "Library", "Caches");
|
|
4292
|
+
return process.env.XDG_CACHE_HOME ?? path3.join(os2.homedir(), ".cache");
|
|
4293
|
+
}
|
|
4294
|
+
var cacheRoots = /* @__PURE__ */ new Map();
|
|
4295
|
+
function cacheRoot() {
|
|
4296
|
+
const setting = process.env.WAIRON_CACHE_DIR ?? "";
|
|
4297
|
+
let root = cacheRoots.get(setting);
|
|
4298
|
+
if (root === void 0) {
|
|
4299
|
+
root = setting !== "" ? path3.resolve(setting) : path3.join(osUserCache(), "wairon");
|
|
4300
|
+
cacheRoots.set(setting, root);
|
|
4301
|
+
}
|
|
4302
|
+
return root;
|
|
4303
|
+
}
|
|
4304
|
+
function git(args, cwd) {
|
|
4305
|
+
return (0, import_child_process.execFileSync)("git", args, {
|
|
4306
|
+
cwd,
|
|
4307
|
+
encoding: "utf8",
|
|
4308
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
4309
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo" }
|
|
4310
|
+
});
|
|
4311
|
+
}
|
|
4312
|
+
function reasonOf(e) {
|
|
4313
|
+
const stderr = e?.stderr;
|
|
4314
|
+
const text3 = typeof stderr === "string" && stderr.trim() !== "" ? stderr : e instanceof Error ? e.message : String(e);
|
|
4315
|
+
return text3.trim().split(/\r?\n/).filter((l) => l.trim() !== "").slice(-1)[0] ?? "git failed";
|
|
4316
|
+
}
|
|
4317
|
+
function cacheEntryOf(url, commit2) {
|
|
4318
|
+
const repo = crypto.createHash("sha256").update(url).digest("hex");
|
|
4319
|
+
return path3.join(cacheRoot(), "git", repo, commit2);
|
|
4320
|
+
}
|
|
4321
|
+
function within(entry, url, commit2, dir) {
|
|
4322
|
+
const at = dir ? path3.join(entry, dir) : entry;
|
|
4323
|
+
if (!fs2.existsSync(at)) {
|
|
4324
|
+
throw new SourceUnavailable(`the commit ${commit2} of ${url} holds no "${dir}"`);
|
|
4325
|
+
}
|
|
4326
|
+
return at;
|
|
4327
|
+
}
|
|
4328
|
+
function fetchInto(tmp, url, commit2) {
|
|
4329
|
+
git(["init", "-q"], tmp);
|
|
4330
|
+
try {
|
|
4331
|
+
git(["fetch", "-q", "--depth", "1", url, commit2], tmp);
|
|
4332
|
+
} catch {
|
|
4333
|
+
git(["fetch", "-q", url, "+refs/heads/*:refs/remotes/source/*", "+refs/tags/*:refs/tags/*"], tmp);
|
|
4334
|
+
}
|
|
4335
|
+
git(["-c", "advice.detachedHead=false", "checkout", "-q", commit2], tmp);
|
|
4336
|
+
fs2.rmSync(path3.join(tmp, ".git"), { recursive: true, force: true });
|
|
4337
|
+
}
|
|
4338
|
+
function fetch(url, commit2, dir) {
|
|
4339
|
+
const entry = cacheEntryOf(url, commit2);
|
|
4340
|
+
if (fs2.existsSync(entry)) return within(entry, url, commit2, dir);
|
|
4341
|
+
fs2.mkdirSync(path3.dirname(entry), { recursive: true });
|
|
4342
|
+
const tmp = `${entry}.tmp-${process.pid}-${crypto.randomBytes(4).toString("hex")}`;
|
|
4343
|
+
fs2.mkdirSync(tmp, { recursive: true });
|
|
4344
|
+
try {
|
|
4345
|
+
fetchInto(tmp, url, commit2);
|
|
4346
|
+
try {
|
|
4347
|
+
fs2.renameSync(tmp, entry);
|
|
4348
|
+
} catch (e) {
|
|
4349
|
+
if (!fs2.existsSync(entry)) throw e;
|
|
4350
|
+
fs2.rmSync(tmp, { recursive: true, force: true });
|
|
4351
|
+
}
|
|
4352
|
+
} catch (e) {
|
|
4353
|
+
fs2.rmSync(tmp, { recursive: true, force: true });
|
|
4354
|
+
if (fs2.readdirSync(path3.dirname(entry)).length === 0) fs2.rmdirSync(path3.dirname(entry));
|
|
4355
|
+
throw e instanceof SourceUnavailable ? e : new SourceUnavailable(`cannot fetch ${commit2} of ${url}: ${reasonOf(e)}`);
|
|
4356
|
+
}
|
|
4357
|
+
return within(entry, url, commit2, dir);
|
|
4358
|
+
}
|
|
4359
|
+
function resolve3(url, ref) {
|
|
4360
|
+
let listed;
|
|
4361
|
+
try {
|
|
4362
|
+
listed = git(["ls-remote", url, ref ?? "HEAD"]);
|
|
4363
|
+
} catch (e) {
|
|
4364
|
+
throw new SourceUnavailable(`cannot reach ${url}: ${reasonOf(e)}`);
|
|
4365
|
+
}
|
|
4366
|
+
const wanted = commitListed(listed, ref ?? "HEAD");
|
|
4367
|
+
if (!wanted) throw new SourceUnavailable(`${url} lists no ref "${ref ?? "HEAD"}"`);
|
|
4368
|
+
return wanted;
|
|
4369
|
+
}
|
|
4370
|
+
function commitListed(listed, ref) {
|
|
4371
|
+
const lines = listed.split(/\r?\n/).filter((l) => l.trim() !== "").map((l) => l.split(/\s+/));
|
|
4372
|
+
const names = [ref, `refs/heads/${ref}`, `refs/tags/${ref}^{}`, `refs/tags/${ref}`];
|
|
4373
|
+
for (const name of names) {
|
|
4374
|
+
const line = lines.find(([, listedName]) => listedName === name);
|
|
4375
|
+
if (line) return line[0];
|
|
4376
|
+
}
|
|
4377
|
+
return void 0;
|
|
4378
|
+
}
|
|
4379
|
+
function trackedFiles(directory, pathspecs) {
|
|
4380
|
+
if (pathspecs.length === 0) return [];
|
|
4381
|
+
try {
|
|
4382
|
+
return git(["ls-files", "-z", "--", ...pathspecs], directory).split("\0").filter((p) => p !== "");
|
|
4383
|
+
} catch {
|
|
4384
|
+
return null;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
function head(directory) {
|
|
4388
|
+
try {
|
|
4389
|
+
return git(["rev-parse", "HEAD"], directory).trim() || null;
|
|
4390
|
+
} catch {
|
|
4391
|
+
return null;
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
function repositoryRoot(directory) {
|
|
4395
|
+
let at = path3.resolve(directory);
|
|
4396
|
+
for (; ; ) {
|
|
4397
|
+
if (fs2.existsSync(path3.join(at, ".git"))) return at;
|
|
4398
|
+
const up = path3.dirname(at);
|
|
4399
|
+
if (up === at) return null;
|
|
4400
|
+
at = up;
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
// src/utils/yaml.ts
|
|
4405
|
+
var yaml = __toESM(require("js-yaml"));
|
|
4406
|
+
function parseYaml(content, sourcePath) {
|
|
4407
|
+
try {
|
|
4408
|
+
return yaml.load(content);
|
|
4409
|
+
} catch (err) {
|
|
4410
|
+
const loc = sourcePath ? ` (${sourcePath})` : "";
|
|
4411
|
+
throw new Error(`Failed to parse YAML${loc}: ${String(err)}`);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
function serializeYaml(value) {
|
|
4415
|
+
return yaml.dump(value, {
|
|
4416
|
+
indent: 2,
|
|
4417
|
+
lineWidth: 100,
|
|
4418
|
+
noRefs: true,
|
|
4419
|
+
sortKeys: false
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
function readYamlFile(filePath) {
|
|
4423
|
+
const content = readFileOrNull(filePath);
|
|
4424
|
+
if (content === null) return null;
|
|
4425
|
+
return parseYaml(content, filePath);
|
|
4426
|
+
}
|
|
4427
|
+
function writeYamlFile(filePath, value) {
|
|
4428
|
+
writeFile(filePath, serializeYaml(value));
|
|
4429
|
+
}
|
|
4430
|
+
function readJsonFile(filePath) {
|
|
4431
|
+
const content = readFileOrNull(filePath);
|
|
4432
|
+
if (content === null) return null;
|
|
4433
|
+
try {
|
|
4434
|
+
return JSON.parse(content);
|
|
4435
|
+
} catch (err) {
|
|
4436
|
+
throw new Error(`Failed to parse JSON (${filePath}): ${String(err)}`);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
function writeJsonFile(filePath, value) {
|
|
4440
|
+
writeFile(filePath, JSON.stringify(value, null, 2) + "\n");
|
|
4441
|
+
}
|
|
4442
|
+
|
|
4153
4443
|
// src/models/identity-rename.ts
|
|
4154
4444
|
var EDGE = " -> ";
|
|
4155
4445
|
var QUALIFIED_UNIT = /^(\d+:)?([^.\s]+)\.([^.\s]+)$/;
|
|
@@ -4185,7 +4475,7 @@ function rekeyAnchor(rename, anchor) {
|
|
|
4185
4475
|
}
|
|
4186
4476
|
|
|
4187
4477
|
// src/models/project.ts
|
|
4188
|
-
var
|
|
4478
|
+
var path4 = __toESM(require("path"));
|
|
4189
4479
|
var import_zod3 = require("zod");
|
|
4190
4480
|
|
|
4191
4481
|
// src/models/agent.ts
|
|
@@ -4735,7 +5025,7 @@ var ExternalDeclarationSchema = import_zod3.z.object({
|
|
|
4735
5025
|
/**
|
|
4736
5026
|
* The producer's public names this project imports, so its specs may name
|
|
4737
5027
|
* them bare: `['*']` imports every public name the producer exports to this
|
|
4738
|
-
* project, `[
|
|
5028
|
+
* project, `[shared-error]` only those. Plain strings: a malformed entry is
|
|
4739
5029
|
* REPORTED (the declaration's problem), never unreadable.
|
|
4740
5030
|
*/
|
|
4741
5031
|
use: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
@@ -4938,7 +5228,7 @@ var PACK_EXT_RE = /\.(ya?ml|cjs|js)$/i;
|
|
|
4938
5228
|
function declaredPackNames(config) {
|
|
4939
5229
|
const selection = config.profileSelection;
|
|
4940
5230
|
return [.../* @__PURE__ */ new Set([
|
|
4941
|
-
...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ?
|
|
5231
|
+
...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ? path4.basename(entry).replace(PACK_EXT_RE, "") : entry.name),
|
|
4942
5232
|
...selection?.requiredPackNames ?? [],
|
|
4943
5233
|
...selection?.defaultPackNames ?? []
|
|
4944
5234
|
])];
|
|
@@ -5236,13 +5526,13 @@ function isPart(config) {
|
|
|
5236
5526
|
|
|
5237
5527
|
// src/config/project-config.ts
|
|
5238
5528
|
function waiDirAt(root) {
|
|
5239
|
-
const wai =
|
|
5240
|
-
const legacy =
|
|
5241
|
-
return !
|
|
5529
|
+
const wai = path5.join(root, ".wai");
|
|
5530
|
+
const legacy = path5.join(root, ".wairon");
|
|
5531
|
+
return !fs3.existsSync(wai) && fs3.existsSync(legacy) ? legacy : wai;
|
|
5242
5532
|
}
|
|
5243
5533
|
function projectConfigFsAdapterAt(rootDir) {
|
|
5244
|
-
const root =
|
|
5245
|
-
const file = () =>
|
|
5534
|
+
const root = path5.resolve(rootDir);
|
|
5535
|
+
const file = () => path5.join(waiDirAt(root), "project.yaml");
|
|
5246
5536
|
return {
|
|
5247
5537
|
readDocument() {
|
|
5248
5538
|
return readYamlFile(file());
|
|
@@ -5256,7 +5546,7 @@ function projectConfigFsAdapterAt(rootDir) {
|
|
|
5256
5546
|
writeFile(file(), editDocumentText(text3, document, root));
|
|
5257
5547
|
},
|
|
5258
5548
|
documentExists() {
|
|
5259
|
-
return
|
|
5549
|
+
return fs3.existsSync(file());
|
|
5260
5550
|
},
|
|
5261
5551
|
readText() {
|
|
5262
5552
|
return readFileOrNull(file());
|
|
@@ -5278,8 +5568,9 @@ function storeOver(adapter, root) {
|
|
|
5278
5568
|
if (!checked.success) {
|
|
5279
5569
|
throw new WaironError(`Refusing to write an invalid .wai/project.yaml at ${root}: ${checked.error.message}`);
|
|
5280
5570
|
}
|
|
5281
|
-
const
|
|
5282
|
-
|
|
5571
|
+
const onDisk = currentDocument();
|
|
5572
|
+
const document = config.partOf !== void 0 ? { schemaVersion: config.schemaVersion, partOf: config.partOf } : withoutDefaultedMaterialization(config, onDisk);
|
|
5573
|
+
adapter.writeDocument(overlayKnownFields(ProjectConfigSchema, document, onDisk));
|
|
5283
5574
|
},
|
|
5284
5575
|
exists() {
|
|
5285
5576
|
return adapter.documentExists();
|
|
@@ -5679,9 +5970,69 @@ function asReadable(document) {
|
|
|
5679
5970
|
if (!isPlainObject(document) || document.partOf === void 0) return document;
|
|
5680
5971
|
return { name: "", createdAt: PART_EPOCH, updatedAt: PART_EPOCH, ...document };
|
|
5681
5972
|
}
|
|
5973
|
+
var MANAGED_AGENT_MARKER = "wairon:managed";
|
|
5974
|
+
var MANAGED_AGENT_NAME = /-(owner|implementer|architect)\.(md|ya?ml)$/;
|
|
5975
|
+
function holdsMaterializedAgentFiles(document, root) {
|
|
5976
|
+
const dirs = new Set(Object.values(DEFAULT_TARGET_DIRS));
|
|
5977
|
+
for (const target of Array.isArray(document.targets) ? document.targets : []) {
|
|
5978
|
+
const outputDir = isPlainObject(target) ? target.outputDir : void 0;
|
|
5979
|
+
if (typeof outputDir === "string") dirs.add(outputDir);
|
|
5980
|
+
}
|
|
5981
|
+
const base = path5.resolve(root);
|
|
5982
|
+
const inside = [];
|
|
5983
|
+
for (const dir of dirs) {
|
|
5984
|
+
const rel2 = path5.relative(base, path5.resolve(base, dir));
|
|
5985
|
+
if (rel2.startsWith("..") || path5.isAbsolute(rel2)) continue;
|
|
5986
|
+
let present = false;
|
|
5987
|
+
try {
|
|
5988
|
+
present = fs3.statSync(path5.join(base, rel2)).isDirectory();
|
|
5989
|
+
} catch {
|
|
5990
|
+
present = false;
|
|
5991
|
+
}
|
|
5992
|
+
if (present) inside.push(rel2 === "" ? "." : rel2.split(path5.sep).join("/"));
|
|
5993
|
+
}
|
|
5994
|
+
if (inside.length === 0) return false;
|
|
5995
|
+
const tracked = trackedFiles(base, inside);
|
|
5996
|
+
const candidates = [];
|
|
5997
|
+
if (tracked === null) {
|
|
5998
|
+
for (const dir of inside) {
|
|
5999
|
+
let names;
|
|
6000
|
+
try {
|
|
6001
|
+
names = fs3.readdirSync(path5.join(base, dir));
|
|
6002
|
+
} catch {
|
|
6003
|
+
continue;
|
|
6004
|
+
}
|
|
6005
|
+
for (const name of names) candidates.push(path5.posix.join(dir, name));
|
|
6006
|
+
}
|
|
6007
|
+
} else {
|
|
6008
|
+
const folders = new Set(inside.map((dir) => path5.posix.normalize(dir)));
|
|
6009
|
+
for (const file of tracked) if (folders.has(path5.posix.dirname(file))) candidates.push(file);
|
|
6010
|
+
}
|
|
6011
|
+
for (const file of candidates) {
|
|
6012
|
+
const head2 = readFileOrNull(path5.join(base, file));
|
|
6013
|
+
if (head2 === null) continue;
|
|
6014
|
+
if (MANAGED_AGENT_NAME.test(path5.posix.basename(file))) return true;
|
|
6015
|
+
if (head2.slice(0, 4096).includes(MANAGED_AGENT_MARKER)) return true;
|
|
6016
|
+
}
|
|
6017
|
+
return false;
|
|
6018
|
+
}
|
|
6019
|
+
function withInferredMaterialization(document, root) {
|
|
6020
|
+
if (!isPlainObject(document) || document.partOf !== void 0) return document;
|
|
6021
|
+
const rules = isPlainObject(document.rules) ? document.rules : void 0;
|
|
6022
|
+
if (rules !== void 0 && hasOwn(rules, "materializeAgentFiles")) return document;
|
|
6023
|
+
if (!holdsMaterializedAgentFiles(document, root)) return document;
|
|
6024
|
+
return { ...document, rules: { ...rules ?? {}, materializeAgentFiles: true } };
|
|
6025
|
+
}
|
|
6026
|
+
function withoutDefaultedMaterialization(config, onDisk) {
|
|
6027
|
+
if (!isPlainObject(onDisk) || config.rules?.materializeAgentFiles !== false) return config;
|
|
6028
|
+
const stated = isPlainObject(onDisk.rules) && hasOwn(onDisk.rules, "materializeAgentFiles");
|
|
6029
|
+
if (stated) return config;
|
|
6030
|
+
const { materializeAgentFiles: _defaulted, ...rules } = config.rules;
|
|
6031
|
+
return { ...config, rules };
|
|
6032
|
+
}
|
|
5682
6033
|
function parseConfig(document, root) {
|
|
5683
6034
|
try {
|
|
5684
|
-
return ProjectConfigSchema.parse(asReadable(document));
|
|
6035
|
+
return ProjectConfigSchema.parse(asReadable(withInferredMaterialization(document, root)));
|
|
5685
6036
|
} catch (e) {
|
|
5686
6037
|
throw new WaironError(
|
|
5687
6038
|
`Invalid .wai/project.yaml: ${e instanceof Error ? e.message : String(e)}
|
|
@@ -6122,15 +6473,15 @@ function indexOver(store, root) {
|
|
|
6122
6473
|
specsDir() {
|
|
6123
6474
|
try {
|
|
6124
6475
|
const declared = store.readField("paths.specsDir");
|
|
6125
|
-
if (declared) return
|
|
6476
|
+
if (declared) return path5.resolve(root, declared);
|
|
6126
6477
|
} catch {
|
|
6127
6478
|
}
|
|
6128
|
-
return
|
|
6479
|
+
return path5.join(waiDirAt(root), "specs");
|
|
6129
6480
|
}
|
|
6130
6481
|
};
|
|
6131
6482
|
}
|
|
6132
6483
|
function projectConfigRepositoryOver(adapter, rootDir) {
|
|
6133
|
-
const root =
|
|
6484
|
+
const root = path5.resolve(rootDir);
|
|
6134
6485
|
const store = storeOver(adapter, root);
|
|
6135
6486
|
const index = indexOver(store, root);
|
|
6136
6487
|
const registry = registryOver(store, root);
|
|
@@ -6323,15 +6674,15 @@ var fs14 = __toESM(require("fs"));
|
|
|
6323
6674
|
var path21 = __toESM(require("path"));
|
|
6324
6675
|
|
|
6325
6676
|
// src/config/paths.ts
|
|
6326
|
-
var
|
|
6327
|
-
var
|
|
6677
|
+
var fs4 = __toESM(require("fs"));
|
|
6678
|
+
var path6 = __toESM(require("path"));
|
|
6328
6679
|
function aiPathsAt(rootDir) {
|
|
6329
|
-
const resolvedRoot =
|
|
6680
|
+
const resolvedRoot = path6.resolve(rootDir);
|
|
6330
6681
|
const aiDirAt2 = (...segments) => {
|
|
6331
|
-
const waiPath =
|
|
6332
|
-
const waironPath =
|
|
6333
|
-
const base = !
|
|
6334
|
-
return
|
|
6682
|
+
const waiPath = path6.join(resolvedRoot, ".wai");
|
|
6683
|
+
const waironPath = path6.join(resolvedRoot, ".wairon");
|
|
6684
|
+
const base = !fs4.existsSync(waiPath) && fs4.existsSync(waironPath) ? waironPath : waiPath;
|
|
6685
|
+
return path6.join(base, ...segments);
|
|
6335
6686
|
};
|
|
6336
6687
|
const specsDir = () => projectConfigRepositoryAt(resolvedRoot).specsDir();
|
|
6337
6688
|
return {
|
|
@@ -6348,12 +6699,12 @@ function aiPathsAt(rootDir) {
|
|
|
6348
6699
|
contextDomainsMd: () => aiDirAt2("context", "domains.md"),
|
|
6349
6700
|
contextWaironGuideMd: () => aiDirAt2("context", "wairon-guide.md"),
|
|
6350
6701
|
specsDir,
|
|
6351
|
-
specsSystem: () =>
|
|
6352
|
-
specsSubsystemsDir: () =>
|
|
6353
|
-
specsComponentsDir: () =>
|
|
6354
|
-
specsInterfacesDir: () =>
|
|
6355
|
-
specsImplementationsDir: () =>
|
|
6356
|
-
specsTypesDir: () =>
|
|
6702
|
+
specsSystem: () => path6.join(specsDir(), ".index.yaml"),
|
|
6703
|
+
specsSubsystemsDir: () => path6.join(specsDir(), "subsystems"),
|
|
6704
|
+
specsComponentsDir: () => path6.join(specsDir(), "components"),
|
|
6705
|
+
specsInterfacesDir: () => path6.join(specsDir(), "interfaces"),
|
|
6706
|
+
specsImplementationsDir: () => path6.join(specsDir(), "implementations"),
|
|
6707
|
+
specsTypesDir: () => path6.join(specsDir(), "types")
|
|
6357
6708
|
};
|
|
6358
6709
|
}
|
|
6359
6710
|
var AI_PATHS = {
|
|
@@ -6387,8 +6738,8 @@ function assertProjectInitialized() {
|
|
|
6387
6738
|
}
|
|
6388
6739
|
|
|
6389
6740
|
// src/core/statehash.ts
|
|
6390
|
-
var
|
|
6391
|
-
var
|
|
6741
|
+
var crypto2 = __toESM(require("crypto"));
|
|
6742
|
+
var path7 = __toESM(require("path"));
|
|
6392
6743
|
|
|
6393
6744
|
// src/utils/canonical-json.ts
|
|
6394
6745
|
function canonicalize(value) {
|
|
@@ -6427,7 +6778,7 @@ function digestTree(keep, asStored = (impl) => impl) {
|
|
|
6427
6778
|
implementations: kind(loadImplementationSpecs().map(asStored)),
|
|
6428
6779
|
types: kind(loadTypeSpecs())
|
|
6429
6780
|
};
|
|
6430
|
-
const digest3 =
|
|
6781
|
+
const digest3 = crypto2.createHash("sha256").update(canonicalize(tree)).digest("hex");
|
|
6431
6782
|
return { algorithm: CONTENT_ALGORITHM, digest: digest3 };
|
|
6432
6783
|
}
|
|
6433
6784
|
function computeStateId() {
|
|
@@ -6447,11 +6798,11 @@ function computeOwnStateId() {
|
|
|
6447
6798
|
return digestTree((id) => (owners.get(id) ?? "") === "", asStored);
|
|
6448
6799
|
}
|
|
6449
6800
|
function isInside(dir, file) {
|
|
6450
|
-
const rel2 =
|
|
6451
|
-
return rel2 !== "" && !rel2.startsWith("..") && !
|
|
6801
|
+
const rel2 = path7.relative(dir, file);
|
|
6802
|
+
return rel2 !== "" && !rel2.startsWith("..") && !path7.isAbsolute(rel2);
|
|
6452
6803
|
}
|
|
6453
6804
|
function fromPartRoot(impl, root, partDir) {
|
|
6454
|
-
const from = (p) =>
|
|
6805
|
+
const from = (p) => path7.isAbsolute(p) ? p : path7.relative(partDir, path7.resolve(root, p)).split(path7.sep).join("/");
|
|
6455
6806
|
return {
|
|
6456
6807
|
...impl,
|
|
6457
6808
|
...impl.sourcePath ? { sourcePath: from(impl.sourcePath) } : {},
|
|
@@ -6464,8 +6815,8 @@ function stateIdEquals(a, b) {
|
|
|
6464
6815
|
}
|
|
6465
6816
|
|
|
6466
6817
|
// src/core/lockfile.ts
|
|
6467
|
-
var
|
|
6468
|
-
var
|
|
6818
|
+
var fs5 = __toESM(require("fs"));
|
|
6819
|
+
var path8 = __toESM(require("path"));
|
|
6469
6820
|
function lockPath() {
|
|
6470
6821
|
return aiDir("lock.json");
|
|
6471
6822
|
}
|
|
@@ -6479,14 +6830,14 @@ function normalizeApprover(value) {
|
|
|
6479
6830
|
}
|
|
6480
6831
|
function readLockRecord() {
|
|
6481
6832
|
try {
|
|
6482
|
-
return normalizeRecord(JSON.parse(
|
|
6833
|
+
return normalizeRecord(JSON.parse(fs5.readFileSync(lockPath(), "utf8")));
|
|
6483
6834
|
} catch {
|
|
6484
6835
|
return null;
|
|
6485
6836
|
}
|
|
6486
6837
|
}
|
|
6487
6838
|
function readLockRecordAt(root) {
|
|
6488
6839
|
try {
|
|
6489
|
-
return normalizeRecord(JSON.parse(
|
|
6840
|
+
return normalizeRecord(JSON.parse(fs5.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
|
|
6490
6841
|
} catch {
|
|
6491
6842
|
return null;
|
|
6492
6843
|
}
|
|
@@ -6497,10 +6848,10 @@ function normalizeRecord(raw) {
|
|
|
6497
6848
|
}
|
|
6498
6849
|
function writeLockRecord(record) {
|
|
6499
6850
|
const p = lockPath();
|
|
6500
|
-
|
|
6851
|
+
fs5.mkdirSync(path8.dirname(p), { recursive: true });
|
|
6501
6852
|
const tmp = `${p}.tmp`;
|
|
6502
|
-
|
|
6503
|
-
|
|
6853
|
+
fs5.writeFileSync(tmp, withLineEndings(p, JSON.stringify(withSortedMaps(record), null, 2) + "\n"));
|
|
6854
|
+
fs5.renameSync(tmp, p);
|
|
6504
6855
|
}
|
|
6505
6856
|
function withSortedMaps(record) {
|
|
6506
6857
|
const sorted = (m) => {
|
|
@@ -6520,8 +6871,8 @@ function withSortedMaps(record) {
|
|
|
6520
6871
|
}
|
|
6521
6872
|
|
|
6522
6873
|
// src/core/spec-files.ts
|
|
6523
|
-
var
|
|
6524
|
-
var
|
|
6874
|
+
var fs6 = __toESM(require("fs"));
|
|
6875
|
+
var path9 = __toESM(require("path"));
|
|
6525
6876
|
var SPEC_FILE_EXTENSION = ".yaml";
|
|
6526
6877
|
function readSpecFile(specPath) {
|
|
6527
6878
|
return readYamlFile(specPath);
|
|
@@ -6533,136 +6884,17 @@ function listSpecFiles(specsDir) {
|
|
|
6533
6884
|
return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
|
|
6534
6885
|
}
|
|
6535
6886
|
function removeSpecFile(specPath, specsRoot) {
|
|
6536
|
-
if (!
|
|
6537
|
-
|
|
6538
|
-
pruneEmptyDirs(
|
|
6887
|
+
if (!fs6.existsSync(specPath)) return false;
|
|
6888
|
+
fs6.unlinkSync(specPath);
|
|
6889
|
+
pruneEmptyDirs(path9.dirname(specPath), path9.resolve(specsRoot));
|
|
6539
6890
|
return true;
|
|
6540
6891
|
}
|
|
6541
6892
|
function pruneEmptyDirs(dir, specsRoot) {
|
|
6542
6893
|
let at = dir;
|
|
6543
6894
|
while (at !== specsRoot && at.startsWith(specsRoot)) {
|
|
6544
|
-
if (!
|
|
6545
|
-
|
|
6546
|
-
at =
|
|
6547
|
-
}
|
|
6548
|
-
}
|
|
6549
|
-
|
|
6550
|
-
// src/core/adapters/git-source.ts
|
|
6551
|
-
var import_child_process = require("child_process");
|
|
6552
|
-
var crypto2 = __toESM(require("crypto"));
|
|
6553
|
-
var fs6 = __toESM(require("fs"));
|
|
6554
|
-
var os = __toESM(require("os"));
|
|
6555
|
-
var path8 = __toESM(require("path"));
|
|
6556
|
-
var SourceUnavailable = class extends WaironError {
|
|
6557
|
-
constructor(message) {
|
|
6558
|
-
super(message);
|
|
6559
|
-
this.name = "SourceUnavailable";
|
|
6560
|
-
}
|
|
6561
|
-
};
|
|
6562
|
-
function osUserCache() {
|
|
6563
|
-
if (process.platform === "win32") return process.env.LOCALAPPDATA ?? path8.join(os.homedir(), "AppData", "Local");
|
|
6564
|
-
if (process.platform === "darwin") return path8.join(os.homedir(), "Library", "Caches");
|
|
6565
|
-
return process.env.XDG_CACHE_HOME ?? path8.join(os.homedir(), ".cache");
|
|
6566
|
-
}
|
|
6567
|
-
var cacheRoots = /* @__PURE__ */ new Map();
|
|
6568
|
-
function cacheRoot() {
|
|
6569
|
-
const setting = process.env.WAIRON_CACHE_DIR ?? "";
|
|
6570
|
-
let root = cacheRoots.get(setting);
|
|
6571
|
-
if (root === void 0) {
|
|
6572
|
-
root = setting !== "" ? path8.resolve(setting) : path8.join(osUserCache(), "wairon");
|
|
6573
|
-
cacheRoots.set(setting, root);
|
|
6574
|
-
}
|
|
6575
|
-
return root;
|
|
6576
|
-
}
|
|
6577
|
-
function git(args, cwd) {
|
|
6578
|
-
return (0, import_child_process.execFileSync)("git", args, {
|
|
6579
|
-
cwd,
|
|
6580
|
-
encoding: "utf8",
|
|
6581
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
6582
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo" }
|
|
6583
|
-
});
|
|
6584
|
-
}
|
|
6585
|
-
function reasonOf(e) {
|
|
6586
|
-
const stderr = e?.stderr;
|
|
6587
|
-
const text3 = typeof stderr === "string" && stderr.trim() !== "" ? stderr : e instanceof Error ? e.message : String(e);
|
|
6588
|
-
return text3.trim().split(/\r?\n/).filter((l) => l.trim() !== "").slice(-1)[0] ?? "git failed";
|
|
6589
|
-
}
|
|
6590
|
-
function cacheEntryOf(url, commit2) {
|
|
6591
|
-
const repo = crypto2.createHash("sha256").update(url).digest("hex");
|
|
6592
|
-
return path8.join(cacheRoot(), "git", repo, commit2);
|
|
6593
|
-
}
|
|
6594
|
-
function within(entry, url, commit2, dir) {
|
|
6595
|
-
const at = dir ? path8.join(entry, dir) : entry;
|
|
6596
|
-
if (!fs6.existsSync(at)) {
|
|
6597
|
-
throw new SourceUnavailable(`the commit ${commit2} of ${url} holds no "${dir}"`);
|
|
6598
|
-
}
|
|
6599
|
-
return at;
|
|
6600
|
-
}
|
|
6601
|
-
function fetchInto(tmp, url, commit2) {
|
|
6602
|
-
git(["init", "-q"], tmp);
|
|
6603
|
-
try {
|
|
6604
|
-
git(["fetch", "-q", "--depth", "1", url, commit2], tmp);
|
|
6605
|
-
} catch {
|
|
6606
|
-
git(["fetch", "-q", url, "+refs/heads/*:refs/remotes/source/*", "+refs/tags/*:refs/tags/*"], tmp);
|
|
6607
|
-
}
|
|
6608
|
-
git(["-c", "advice.detachedHead=false", "checkout", "-q", commit2], tmp);
|
|
6609
|
-
fs6.rmSync(path8.join(tmp, ".git"), { recursive: true, force: true });
|
|
6610
|
-
}
|
|
6611
|
-
function fetch(url, commit2, dir) {
|
|
6612
|
-
const entry = cacheEntryOf(url, commit2);
|
|
6613
|
-
if (fs6.existsSync(entry)) return within(entry, url, commit2, dir);
|
|
6614
|
-
fs6.mkdirSync(path8.dirname(entry), { recursive: true });
|
|
6615
|
-
const tmp = `${entry}.tmp-${process.pid}-${crypto2.randomBytes(4).toString("hex")}`;
|
|
6616
|
-
fs6.mkdirSync(tmp, { recursive: true });
|
|
6617
|
-
try {
|
|
6618
|
-
fetchInto(tmp, url, commit2);
|
|
6619
|
-
try {
|
|
6620
|
-
fs6.renameSync(tmp, entry);
|
|
6621
|
-
} catch (e) {
|
|
6622
|
-
if (!fs6.existsSync(entry)) throw e;
|
|
6623
|
-
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
6624
|
-
}
|
|
6625
|
-
} catch (e) {
|
|
6626
|
-
fs6.rmSync(tmp, { recursive: true, force: true });
|
|
6627
|
-
if (fs6.readdirSync(path8.dirname(entry)).length === 0) fs6.rmdirSync(path8.dirname(entry));
|
|
6628
|
-
throw e instanceof SourceUnavailable ? e : new SourceUnavailable(`cannot fetch ${commit2} of ${url}: ${reasonOf(e)}`);
|
|
6629
|
-
}
|
|
6630
|
-
return within(entry, url, commit2, dir);
|
|
6631
|
-
}
|
|
6632
|
-
function resolve7(url, ref) {
|
|
6633
|
-
let listed;
|
|
6634
|
-
try {
|
|
6635
|
-
listed = git(["ls-remote", url, ref ?? "HEAD"]);
|
|
6636
|
-
} catch (e) {
|
|
6637
|
-
throw new SourceUnavailable(`cannot reach ${url}: ${reasonOf(e)}`);
|
|
6638
|
-
}
|
|
6639
|
-
const wanted = commitListed(listed, ref ?? "HEAD");
|
|
6640
|
-
if (!wanted) throw new SourceUnavailable(`${url} lists no ref "${ref ?? "HEAD"}"`);
|
|
6641
|
-
return wanted;
|
|
6642
|
-
}
|
|
6643
|
-
function commitListed(listed, ref) {
|
|
6644
|
-
const lines = listed.split(/\r?\n/).filter((l) => l.trim() !== "").map((l) => l.split(/\s+/));
|
|
6645
|
-
const names = [ref, `refs/heads/${ref}`, `refs/tags/${ref}^{}`, `refs/tags/${ref}`];
|
|
6646
|
-
for (const name of names) {
|
|
6647
|
-
const line = lines.find(([, listedName]) => listedName === name);
|
|
6648
|
-
if (line) return line[0];
|
|
6649
|
-
}
|
|
6650
|
-
return void 0;
|
|
6651
|
-
}
|
|
6652
|
-
function head(directory) {
|
|
6653
|
-
try {
|
|
6654
|
-
return git(["rev-parse", "HEAD"], directory).trim() || null;
|
|
6655
|
-
} catch {
|
|
6656
|
-
return null;
|
|
6657
|
-
}
|
|
6658
|
-
}
|
|
6659
|
-
function repositoryRoot(directory) {
|
|
6660
|
-
let at = path8.resolve(directory);
|
|
6661
|
-
for (; ; ) {
|
|
6662
|
-
if (fs6.existsSync(path8.join(at, ".git"))) return at;
|
|
6663
|
-
const up = path8.dirname(at);
|
|
6664
|
-
if (up === at) return null;
|
|
6665
|
-
at = up;
|
|
6895
|
+
if (!fs6.existsSync(at) || fs6.readdirSync(at).length > 0) return;
|
|
6896
|
+
fs6.rmdirSync(at);
|
|
6897
|
+
at = path9.dirname(at);
|
|
6666
6898
|
}
|
|
6667
6899
|
}
|
|
6668
6900
|
|
|
@@ -8061,7 +8293,11 @@ var GENERIC_ALIASES = /* @__PURE__ */ new Map([
|
|
|
8061
8293
|
["HashMap", { form: "map", arity: 2 }],
|
|
8062
8294
|
["Option", { form: "optional", arity: 1 }],
|
|
8063
8295
|
["Optional", { form: "optional", arity: 1 }],
|
|
8064
|
-
["Promise", { form: "async", arity: 1 }]
|
|
8296
|
+
["Promise", { form: "async", arity: 1 }],
|
|
8297
|
+
// The success-or-failure type the mainstream languages with typed failures
|
|
8298
|
+
// share (Rust, Swift, Kotlin, F#); Rust's spelling is its alias.
|
|
8299
|
+
["result", { form: "result", arity: 2 }],
|
|
8300
|
+
["Result", { form: "result", arity: 2 }]
|
|
8065
8301
|
]);
|
|
8066
8302
|
var NOT_NEUTRAL = /* @__PURE__ */ new Map([
|
|
8067
8303
|
["uuid", "a named value-object holding a string (e.g. order_id)"],
|
|
@@ -8070,7 +8306,6 @@ var NOT_NEUTRAL = /* @__PURE__ */ new Map([
|
|
|
8070
8306
|
["byte", "int, or bytes for binary data"],
|
|
8071
8307
|
["time", "datetime for an instant, or duration for an elapsed time"],
|
|
8072
8308
|
["tuple", "a named value-object"],
|
|
8073
|
-
["result", "the success type as the returns (failures as findings or throws), or a named value-object"],
|
|
8074
8309
|
["error", "a named value-object"],
|
|
8075
8310
|
["never", "void"],
|
|
8076
8311
|
["box", "the inner type (ownership is an implementation detail)"],
|
|
@@ -8265,9 +8500,9 @@ var RawParser = class {
|
|
|
8265
8500
|
}
|
|
8266
8501
|
name += this.ident();
|
|
8267
8502
|
while (this.isP("::") || this.isP(".")) {
|
|
8268
|
-
const
|
|
8503
|
+
const sep15 = this.peek().v;
|
|
8269
8504
|
this.at++;
|
|
8270
|
-
name +=
|
|
8505
|
+
name += sep15 + this.ident();
|
|
8271
8506
|
}
|
|
8272
8507
|
if (!this.isP("<")) return { k: "name", name };
|
|
8273
8508
|
this.at++;
|
|
@@ -8294,6 +8529,10 @@ var RawParser = class {
|
|
|
8294
8529
|
const close2 = this.closingParen();
|
|
8295
8530
|
const after = close2 === -1 ? void 0 : this.tokens[close2 + 1];
|
|
8296
8531
|
if (!after || after.t !== "p" || after.v !== "=>") {
|
|
8532
|
+
if (this.isP(")", 1)) {
|
|
8533
|
+
this.at += 2;
|
|
8534
|
+
return { k: "name", name: "void" };
|
|
8535
|
+
}
|
|
8297
8536
|
this.expect("(");
|
|
8298
8537
|
const inner2 = this.type();
|
|
8299
8538
|
this.expect(")");
|
|
@@ -8402,7 +8641,7 @@ function optionalOf(inner2) {
|
|
|
8402
8641
|
return { form: "optional", args: [inner2] };
|
|
8403
8642
|
}
|
|
8404
8643
|
function inner(ctx) {
|
|
8405
|
-
return { ...ctx, top: false, voidAllowed: false, omittable: false };
|
|
8644
|
+
return { ...ctx, top: false, voidAllowed: false, resultAllowed: false, omittable: false };
|
|
8406
8645
|
}
|
|
8407
8646
|
function normalise(node, ctx, notes) {
|
|
8408
8647
|
switch (node.k) {
|
|
@@ -8436,9 +8675,21 @@ function asyncOf(innerNode, ctx, notes, spelled) {
|
|
|
8436
8675
|
if (!ctx.top || !RETURNS_POSITIONS.has(ctx.position)) {
|
|
8437
8676
|
throw new TypeProblemError("TYPE_POSITION_INVALID", `${spelled} may stand only at the top of a returns, not in a ${ctx.position}`);
|
|
8438
8677
|
}
|
|
8439
|
-
const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true }, notes);
|
|
8678
|
+
const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true, resultAllowed: true }, notes);
|
|
8440
8679
|
return { form: "async", args: [read2] };
|
|
8441
8680
|
}
|
|
8681
|
+
function resultOf(args, ctx, notes, spelled) {
|
|
8682
|
+
if (!ctx.resultAllowed) {
|
|
8683
|
+
throw new TypeProblemError(
|
|
8684
|
+
"TYPE_POSITION_INVALID",
|
|
8685
|
+
`${spelled} may stand only as a whole returns (or under its async), not in a ${ctx.top ? ctx.position : "collection, union, optional or argument"}`,
|
|
8686
|
+
"the success type here (a failure belongs to a method's returns)"
|
|
8687
|
+
);
|
|
8688
|
+
}
|
|
8689
|
+
const ok = normalise(args[0], { ...inner(ctx), voidAllowed: true }, notes);
|
|
8690
|
+
const err = normalise(args[1], inner(ctx), notes);
|
|
8691
|
+
return { form: "result", args: [ok, err] };
|
|
8692
|
+
}
|
|
8442
8693
|
function bareName(name, ctx, notes) {
|
|
8443
8694
|
const lower = name.toLowerCase();
|
|
8444
8695
|
if (NONES.has(name)) {
|
|
@@ -8476,6 +8727,7 @@ function appliedName(name, args, ctx, notes) {
|
|
|
8476
8727
|
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes ${alias.arity} type argument${alias.arity === 1 ? "" : "s"}, ${args.length} given`);
|
|
8477
8728
|
}
|
|
8478
8729
|
if (alias.form === "async") return asyncOf(args[0], ctx, notes, `${name}<...>`);
|
|
8730
|
+
if (alias.form === "result") return resultOf(args, ctx, notes, `${name}<...>`);
|
|
8479
8731
|
if (alias.form === "optional") return optionalOf(normalise(args[0], inner(ctx), notes));
|
|
8480
8732
|
const read2 = args.map((a) => normalise(a, inner(ctx), notes));
|
|
8481
8733
|
if (alias.form === "map") assertMapKey(read2[0], args[0]);
|
|
@@ -8549,6 +8801,8 @@ function canonicalTypeText(expr) {
|
|
|
8549
8801
|
return `${expr.form}<${canonicalTypeText(expr.args[0])}>`;
|
|
8550
8802
|
case "map":
|
|
8551
8803
|
return `map<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
|
|
8804
|
+
case "result":
|
|
8805
|
+
return `result<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
|
|
8552
8806
|
case "optional": {
|
|
8553
8807
|
const innerText = canonicalTypeText(expr.args[0]);
|
|
8554
8808
|
return expr.args[0].form === "union" ? `(${innerText})?` : `${innerText}?`;
|
|
@@ -8568,11 +8822,12 @@ function typeNamedRefs(expr) {
|
|
|
8568
8822
|
}
|
|
8569
8823
|
function typeIsMany(expr) {
|
|
8570
8824
|
if (expr.form === "list" || expr.form === "set" || expr.form === "map") return true;
|
|
8571
|
-
if (expr.form === "optional" || expr.form === "async") return typeIsMany(expr.args[0]);
|
|
8825
|
+
if (expr.form === "optional" || expr.form === "async" || expr.form === "result") return typeIsMany(expr.args[0]);
|
|
8572
8826
|
return false;
|
|
8573
8827
|
}
|
|
8574
8828
|
function contextFor(position, omittable2, looseNumber = false) {
|
|
8575
|
-
|
|
8829
|
+
const returns = RETURNS_POSITIONS.has(position);
|
|
8830
|
+
return { position, top: true, voidAllowed: returns, resultAllowed: returns, omittable: omittable2, looseNumber };
|
|
8576
8831
|
}
|
|
8577
8832
|
function parseTypePosition(text3, position, omittable2 = false) {
|
|
8578
8833
|
let raw;
|
|
@@ -8763,6 +9018,47 @@ function typeProblemIntProposal(problem) {
|
|
|
8763
9018
|
if (!parse.expression || parse.problem) return null;
|
|
8764
9019
|
return { specId: problem.specId, kind: problem.kind, path: problem.path, written: problem.written, stored: parse.canonical };
|
|
8765
9020
|
}
|
|
9021
|
+
function stringLiteralValue(text3) {
|
|
9022
|
+
const quote = text3[0];
|
|
9023
|
+
if (quote !== "'" && quote !== '"' && quote !== "`" || text3[text3.length - 1] !== quote) return null;
|
|
9024
|
+
return text3.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
9025
|
+
}
|
|
9026
|
+
function kebabOf(name) {
|
|
9027
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_.]+/g, "-").toLowerCase();
|
|
9028
|
+
}
|
|
9029
|
+
function typeProblemEnumProposal(problem) {
|
|
9030
|
+
if (problem.code !== "TYPE_FORM_UNSUPPORTED") return null;
|
|
9031
|
+
if (problem.specId === void 0 || problem.kind === void 0 || problem.path === void 0) return null;
|
|
9032
|
+
let raw;
|
|
9033
|
+
try {
|
|
9034
|
+
raw = readRaw(problem.written);
|
|
9035
|
+
} catch (e) {
|
|
9036
|
+
if (e instanceof TypeSyntaxError) return null;
|
|
9037
|
+
throw e;
|
|
9038
|
+
}
|
|
9039
|
+
const members = raw.k === "union" ? raw.members : [raw];
|
|
9040
|
+
const values = [];
|
|
9041
|
+
let optional = false;
|
|
9042
|
+
for (const member of members) {
|
|
9043
|
+
if (member.k === "name" && member.args === void 0 && NONES.has(member.name)) {
|
|
9044
|
+
optional = true;
|
|
9045
|
+
continue;
|
|
9046
|
+
}
|
|
9047
|
+
const value = member.k === "literal" ? stringLiteralValue(member.text) : null;
|
|
9048
|
+
if (value === null) return null;
|
|
9049
|
+
if (!values.includes(value)) values.push(value);
|
|
9050
|
+
}
|
|
9051
|
+
if (values.length === 0) return null;
|
|
9052
|
+
return {
|
|
9053
|
+
specId: problem.specId,
|
|
9054
|
+
kind: problem.kind,
|
|
9055
|
+
path: problem.path,
|
|
9056
|
+
written: problem.written,
|
|
9057
|
+
enumId: kebabOf(positionNameOf(problem.path)),
|
|
9058
|
+
values,
|
|
9059
|
+
optional
|
|
9060
|
+
};
|
|
9061
|
+
}
|
|
8766
9062
|
function emptyTypeSpellingFacts() {
|
|
8767
9063
|
return { respellings: [], problems: [] };
|
|
8768
9064
|
}
|
|
@@ -8913,6 +9209,7 @@ function lastSegment(name) {
|
|
|
8913
9209
|
return name.split(/::|\./).filter(Boolean).pop() ?? name;
|
|
8914
9210
|
}
|
|
8915
9211
|
function sameType(code, spec, codeNames) {
|
|
9212
|
+
if (spec.form === "result" && code.form !== "result") return sameType(code, spec.args[0], codeNames);
|
|
8916
9213
|
if (code.form === "primitive" && code.name === "number") return spec.form === "primitive" && (spec.name === "int" || spec.name === "float");
|
|
8917
9214
|
if (code.form === "named" && (spec.form === "named" || spec.form === "applied")) {
|
|
8918
9215
|
if (spec.form === "applied") return false;
|
|
@@ -8949,6 +9246,8 @@ function writeTypeScript(expr) {
|
|
|
8949
9246
|
return expr.args.map(writeTypeScript).join(" | ");
|
|
8950
9247
|
case "async":
|
|
8951
9248
|
return `Promise<${writeTypeScript(expr.args[0])}>`;
|
|
9249
|
+
case "result":
|
|
9250
|
+
return writeTypeScript(expr.args[0]);
|
|
8952
9251
|
case "applied":
|
|
8953
9252
|
return `${expr.name}<${expr.args.map(writeTypeScript).join(", ")}>`;
|
|
8954
9253
|
}
|
|
@@ -8991,6 +9290,7 @@ var typescriptDialect = {
|
|
|
8991
9290
|
"T? \u2192 T | null",
|
|
8992
9291
|
"A | B \u2192 A | B",
|
|
8993
9292
|
"async T \u2192 Promise<T>",
|
|
9293
|
+
"result<T, E> \u2192 T, throwing E on failure (result<void, E> \u2192 void)",
|
|
8994
9294
|
"an enum E \u2192 type E = 'a' | 'b' (a string-literal union alias)",
|
|
8995
9295
|
"a named scalar E holding P \u2192 type E = P (an alias of the primitive)"
|
|
8996
9296
|
];
|
|
@@ -9265,7 +9565,7 @@ function jumpFieldsOf(s) {
|
|
|
9265
9565
|
}
|
|
9266
9566
|
|
|
9267
9567
|
// src/models/code-model.ts
|
|
9268
|
-
var
|
|
9568
|
+
var path10 = __toESM(require("path"));
|
|
9269
9569
|
function pathKey(sourcePath) {
|
|
9270
9570
|
return sourcePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
9271
9571
|
}
|
|
@@ -9339,14 +9639,20 @@ var EXTENSION_LANGUAGE = {
|
|
|
9339
9639
|
".swift": "swift"
|
|
9340
9640
|
};
|
|
9341
9641
|
function languageOfSourcePath(sourcePath) {
|
|
9342
|
-
return EXTENSION_LANGUAGE[
|
|
9642
|
+
return EXTENSION_LANGUAGE[path10.extname(sourcePath).toLowerCase()];
|
|
9343
9643
|
}
|
|
9344
9644
|
function dialectOf(facts) {
|
|
9345
9645
|
return facts.language ? typeDialectFor(facts.language) : null;
|
|
9346
9646
|
}
|
|
9347
|
-
function
|
|
9348
|
-
|
|
9349
|
-
|
|
9647
|
+
function returnTypeOf(facts, fn) {
|
|
9648
|
+
return ownEntry(facts.returnTypes, fn);
|
|
9649
|
+
}
|
|
9650
|
+
function resolveImport(fromFile, specifier, knownPaths, packages) {
|
|
9651
|
+
if (!specifier.startsWith(".")) {
|
|
9652
|
+
const mapped = ownEntry(packages, specifier);
|
|
9653
|
+
return mapped !== void 0 && knownPaths.has(mapped) ? mapped : void 0;
|
|
9654
|
+
}
|
|
9655
|
+
const joined = pathKey(path10.posix.normalize(path10.posix.join(path10.posix.dirname(fromFile), specifier)));
|
|
9350
9656
|
const candidates = [
|
|
9351
9657
|
joined,
|
|
9352
9658
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -9493,7 +9799,7 @@ function exportTargetKey(e) {
|
|
|
9493
9799
|
}
|
|
9494
9800
|
|
|
9495
9801
|
// src/models/project-family.ts
|
|
9496
|
-
var
|
|
9802
|
+
var path11 = __toESM(require("path"));
|
|
9497
9803
|
function walkMemberPath(byKey3, start, segments) {
|
|
9498
9804
|
let node = start;
|
|
9499
9805
|
for (const segment of segments.slice(0, -1)) {
|
|
@@ -9555,15 +9861,15 @@ function keyIn(project, localId) {
|
|
|
9555
9861
|
return project ? `${project}::${localId}` : localId;
|
|
9556
9862
|
}
|
|
9557
9863
|
function approvalKeyIn(file, root, parts) {
|
|
9558
|
-
const resolved =
|
|
9864
|
+
const resolved = path11.resolve(file);
|
|
9559
9865
|
for (const part of parts) {
|
|
9560
9866
|
if (part.directory === void 0) continue;
|
|
9561
|
-
const rel2 =
|
|
9562
|
-
if (rel2 !== "" && !rel2.startsWith("..") && !
|
|
9563
|
-
return `members/${part.alias}/${rel2.split(
|
|
9867
|
+
const rel2 = path11.relative(part.directory, resolved);
|
|
9868
|
+
if (rel2 !== "" && !rel2.startsWith("..") && !path11.isAbsolute(rel2)) {
|
|
9869
|
+
return `members/${part.alias}/${rel2.split(path11.sep).join("/")}`;
|
|
9564
9870
|
}
|
|
9565
9871
|
}
|
|
9566
|
-
return
|
|
9872
|
+
return path11.relative(root, resolved).split(path11.sep).join("/");
|
|
9567
9873
|
}
|
|
9568
9874
|
function familyNode(family, namespace) {
|
|
9569
9875
|
return family.nodes.find((n) => n.namespace === namespace) ?? null;
|
|
@@ -9696,7 +10002,7 @@ var DesignDependencyRoleSchema = import_zod9.z.enum(DESIGN_DEPENDENCY_ROLES);
|
|
|
9696
10002
|
var ExtSchema = import_zod9.z.record(import_zod9.z.unknown());
|
|
9697
10003
|
var TypeExpressionSchema = import_zod9.z.lazy(
|
|
9698
10004
|
() => import_zod9.z.object({
|
|
9699
|
-
form: import_zod9.z.enum(["primitive", "named", "list", "set", "map", "optional", "union", "async", "applied"]),
|
|
10005
|
+
form: import_zod9.z.enum(["primitive", "named", "list", "set", "map", "optional", "union", "async", "applied", "result"]),
|
|
9700
10006
|
name: import_zod9.z.string().optional(),
|
|
9701
10007
|
args: import_zod9.z.array(TypeExpressionSchema)
|
|
9702
10008
|
}).passthrough()
|
|
@@ -9933,7 +10239,7 @@ function resolveNarrativeLabels(methodName, steps) {
|
|
|
9933
10239
|
|
|
9934
10240
|
// src/core/canvas.ts
|
|
9935
10241
|
var fs7 = __toESM(require("fs"));
|
|
9936
|
-
var
|
|
10242
|
+
var path12 = __toESM(require("path"));
|
|
9937
10243
|
|
|
9938
10244
|
// src/core/canvas-layout.ts
|
|
9939
10245
|
function computeLayout(model, collapsed) {
|
|
@@ -10704,9 +11010,9 @@ function embedJson(value) {
|
|
|
10704
11010
|
}
|
|
10705
11011
|
function loadCytoscapeLib() {
|
|
10706
11012
|
const candidates = [
|
|
10707
|
-
|
|
11013
|
+
path12.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
|
|
10708
11014
|
// src/core & dist/cli
|
|
10709
|
-
|
|
11015
|
+
path12.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
|
|
10710
11016
|
// dist (library entry)
|
|
10711
11017
|
];
|
|
10712
11018
|
for (const p of candidates) {
|
|
@@ -15089,17 +15395,17 @@ function safeFilenamePart(value) {
|
|
|
15089
15395
|
|
|
15090
15396
|
// src/core/externals.ts
|
|
15091
15397
|
var fs8 = __toESM(require("fs"));
|
|
15092
|
-
var
|
|
15398
|
+
var path13 = __toESM(require("path"));
|
|
15093
15399
|
function lockPath2(root) {
|
|
15094
|
-
return
|
|
15400
|
+
return path13.join(root, ".wai", "externals.lock.yaml");
|
|
15095
15401
|
}
|
|
15096
15402
|
function snapshotPath(root, alias) {
|
|
15097
|
-
return
|
|
15403
|
+
return path13.join(root, ".wai", "externals", `${alias}.yaml`);
|
|
15098
15404
|
}
|
|
15099
15405
|
function writeAtomically(file, text3) {
|
|
15100
|
-
fs8.mkdirSync(
|
|
15406
|
+
fs8.mkdirSync(path13.dirname(file), { recursive: true });
|
|
15101
15407
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
15102
|
-
fs8.writeFileSync(tmp, text3);
|
|
15408
|
+
fs8.writeFileSync(tmp, withLineEndings(file, text3));
|
|
15103
15409
|
fs8.renameSync(tmp, file);
|
|
15104
15410
|
}
|
|
15105
15411
|
function sortedLock(lock) {
|
|
@@ -15142,7 +15448,7 @@ var externalsFileAdapter = {
|
|
|
15142
15448
|
const file = snapshotPath(getProjectRoot(), alias);
|
|
15143
15449
|
if (!fs8.existsSync(file)) return false;
|
|
15144
15450
|
fs8.unlinkSync(file);
|
|
15145
|
-
const dir =
|
|
15451
|
+
const dir = path13.dirname(file);
|
|
15146
15452
|
if (fs8.existsSync(dir) && fs8.readdirSync(dir).length === 0) fs8.rmdirSync(dir);
|
|
15147
15453
|
return true;
|
|
15148
15454
|
},
|
|
@@ -15275,6 +15581,8 @@ function schemaOfExpression(expr, closureIds) {
|
|
|
15275
15581
|
return { oneOf: expr.args.map((member) => schemaOfExpression(member, closureIds)) };
|
|
15276
15582
|
case "async":
|
|
15277
15583
|
return schemaOfExpression(expr.args[0], closureIds);
|
|
15584
|
+
case "result":
|
|
15585
|
+
return schemaOfExpression(expr.args[0], closureIds);
|
|
15278
15586
|
}
|
|
15279
15587
|
}
|
|
15280
15588
|
function schemaFor(typeRef2, closureIds) {
|
|
@@ -15284,7 +15592,8 @@ function schemaFor(typeRef2, closureIds) {
|
|
|
15284
15592
|
}
|
|
15285
15593
|
function returnsNothing(returns) {
|
|
15286
15594
|
const expr = expressionOf(returns);
|
|
15287
|
-
const
|
|
15595
|
+
const awaited = expr?.form === "async" ? expr.args[0] : expr;
|
|
15596
|
+
const inner2 = awaited?.form === "result" ? awaited.args[0] : awaited;
|
|
15288
15597
|
return inner2?.form === "primitive" && inner2.name === "void";
|
|
15289
15598
|
}
|
|
15290
15599
|
function enumDescription(values) {
|
|
@@ -15736,61 +16045,6 @@ function fromOpenApi(document, projectName) {
|
|
|
15736
16045
|
});
|
|
15737
16046
|
}
|
|
15738
16047
|
|
|
15739
|
-
// src/config/defaults.ts
|
|
15740
|
-
var os2 = __toESM(require("os"));
|
|
15741
|
-
var path13 = __toESM(require("path"));
|
|
15742
|
-
var DEFAULT_TARGET_DIRS = {
|
|
15743
|
-
claude: ".claude/agents",
|
|
15744
|
-
gemini: ".gemini/agents",
|
|
15745
|
-
agy: ".gemini/agents",
|
|
15746
|
-
cursor: ".cursor/rules",
|
|
15747
|
-
copilot: ".github/prompts",
|
|
15748
|
-
codex: ".codex/agents"
|
|
15749
|
-
};
|
|
15750
|
-
function defaultTargetConfig(type) {
|
|
15751
|
-
return {
|
|
15752
|
-
type,
|
|
15753
|
-
outputDir: DEFAULT_TARGET_DIRS[type],
|
|
15754
|
-
enabled: true
|
|
15755
|
-
};
|
|
15756
|
-
}
|
|
15757
|
-
var WAIRON_VERSION = "5.1.1-dev.100";
|
|
15758
|
-
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
15759
|
-
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
15760
|
-
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
15761
|
-
function globalTemplatesDir(projectOverride) {
|
|
15762
|
-
if (process.env.WAIRON_TEMPLATES_DIR) {
|
|
15763
|
-
return process.env.WAIRON_TEMPLATES_DIR;
|
|
15764
|
-
}
|
|
15765
|
-
if (projectOverride) {
|
|
15766
|
-
return projectOverride;
|
|
15767
|
-
}
|
|
15768
|
-
return path13.join(os2.homedir(), ".wairon", "templates");
|
|
15769
|
-
}
|
|
15770
|
-
var BACKEND_COMMANDS = {
|
|
15771
|
-
claude: "claude",
|
|
15772
|
-
gemini: "gemini"
|
|
15773
|
-
};
|
|
15774
|
-
function backendCommand(backend) {
|
|
15775
|
-
return BACKEND_COMMANDS[backend] ?? backend;
|
|
15776
|
-
}
|
|
15777
|
-
var SUPPORTED_ALIASES = ["wai"];
|
|
15778
|
-
var SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
15779
|
-
"node_modules",
|
|
15780
|
-
"dist",
|
|
15781
|
-
"build",
|
|
15782
|
-
"out",
|
|
15783
|
-
".git",
|
|
15784
|
-
".cache",
|
|
15785
|
-
"coverage",
|
|
15786
|
-
"__pycache__",
|
|
15787
|
-
".venv",
|
|
15788
|
-
"venv",
|
|
15789
|
-
"target",
|
|
15790
|
-
// Rust
|
|
15791
|
-
"vendor"
|
|
15792
|
-
]);
|
|
15793
|
-
|
|
15794
16048
|
// src/core/design-export.ts
|
|
15795
16049
|
var NoSystemSpecError = class extends Error {
|
|
15796
16050
|
constructor() {
|
|
@@ -17285,7 +17539,7 @@ function approvalFinding(entry, config) {
|
|
|
17285
17539
|
return finding(
|
|
17286
17540
|
config,
|
|
17287
17541
|
"MEMBER_DRIFTED",
|
|
17288
|
-
`The member ${named(entry.key)} was approved under an earlier gate identity \u2014 the gate identity gained inputs in
|
|
17542
|
+
`The member ${named(entry.key)} was approved under an earlier gate identity \u2014 the gate identity gained inputs in this release (members' composition subjects, \`composition\`; code conformance moved beside the claim), which says nothing about its design. Re-lock it once at its own root (\`wairon lock\`).`,
|
|
17289
17543
|
entry.key
|
|
17290
17544
|
);
|
|
17291
17545
|
}
|
|
@@ -17666,12 +17920,12 @@ function packFiles(dir, prefix = "") {
|
|
|
17666
17920
|
function computePackDigest(packDir) {
|
|
17667
17921
|
const hash = crypto3.createHash("sha256");
|
|
17668
17922
|
const abs = path16.resolve(packDir);
|
|
17669
|
-
let
|
|
17923
|
+
let isFile2 = false;
|
|
17670
17924
|
try {
|
|
17671
|
-
|
|
17925
|
+
isFile2 = fs11.statSync(abs).isFile();
|
|
17672
17926
|
} catch {
|
|
17673
17927
|
}
|
|
17674
|
-
const entries =
|
|
17928
|
+
const entries = isFile2 ? [[path16.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path16.join(abs, rel2)]);
|
|
17675
17929
|
for (const [rel2, full] of entries) {
|
|
17676
17930
|
hash.update(rel2);
|
|
17677
17931
|
hash.update("\0");
|
|
@@ -18291,6 +18545,16 @@ function verifyIntegrity(selection, packDir, where) {
|
|
|
18291
18545
|
message: `Pack "${wanted}" resolved to content that does not match the pinned integrity: expected ${selection.integrity}, found ${actual} (${where}). The pack was modified after it was pinned \u2014 reinstall or re-bundle the pinned version, or update the pin with \`wairon pack use ${selection.name} --pin\` if the change is intended.`
|
|
18292
18546
|
};
|
|
18293
18547
|
}
|
|
18548
|
+
function bundlesSingleFile(bundleDir, storePath) {
|
|
18549
|
+
try {
|
|
18550
|
+
if (!fs12.statSync(storePath).isFile()) return false;
|
|
18551
|
+
const entry = packDirEntry(bundleDir);
|
|
18552
|
+
if (!entry || fs12.readdirSync(bundleDir).length !== 1) return false;
|
|
18553
|
+
return fs12.readFileSync(entry).equals(fs12.readFileSync(storePath));
|
|
18554
|
+
} catch {
|
|
18555
|
+
return false;
|
|
18556
|
+
}
|
|
18557
|
+
}
|
|
18294
18558
|
function detectBundleStoreDrift(selection, bundleDir) {
|
|
18295
18559
|
const bundledVersion = path17.basename(bundleDir);
|
|
18296
18560
|
const installed = resolveInstalledPack(selection.name, bundledVersion);
|
|
@@ -18298,6 +18562,7 @@ function detectBundleStoreDrift(selection, bundleDir) {
|
|
|
18298
18562
|
const bundleDigest = computePackDigest(bundleDir);
|
|
18299
18563
|
const storeDigest = computePackDigest(installed.path);
|
|
18300
18564
|
if (bundleDigest === storeDigest) return void 0;
|
|
18565
|
+
if (bundlesSingleFile(bundleDir, installed.path)) return void 0;
|
|
18301
18566
|
return {
|
|
18302
18567
|
code: "PACK_STORE_DRIFT",
|
|
18303
18568
|
severity: "warning",
|
|
@@ -18355,6 +18620,7 @@ function appendUnique(map, key, value, same) {
|
|
|
18355
18620
|
list.push(value);
|
|
18356
18621
|
}
|
|
18357
18622
|
function buildCodeIndex(model) {
|
|
18623
|
+
const packages = model.packages ?? {};
|
|
18358
18624
|
const facts = /* @__PURE__ */ new Map();
|
|
18359
18625
|
const paths = /* @__PURE__ */ new Set();
|
|
18360
18626
|
const exactPaths = /* @__PURE__ */ new Set();
|
|
@@ -18387,7 +18653,7 @@ function buildCodeIndex(model) {
|
|
|
18387
18653
|
const f = facts.get(from);
|
|
18388
18654
|
if (f?.status !== "analyzed") continue;
|
|
18389
18655
|
for (const specifier of f.reexports) {
|
|
18390
|
-
const to = resolveImport(from, specifier, paths);
|
|
18656
|
+
const to = resolveImport(from, specifier, paths, packages);
|
|
18391
18657
|
if (to && !out.has(to)) {
|
|
18392
18658
|
out.add(to);
|
|
18393
18659
|
queue.push(to);
|
|
@@ -18399,7 +18665,7 @@ function buildCodeIndex(model) {
|
|
|
18399
18665
|
};
|
|
18400
18666
|
const NO_ORIGIN = /* @__PURE__ */ new Set();
|
|
18401
18667
|
const landingFrom = (scope, specifier) => {
|
|
18402
|
-
const to = resolveImport(scope, specifier, paths);
|
|
18668
|
+
const to = resolveImport(scope, specifier, paths, packages);
|
|
18403
18669
|
return to ? republishedFrom(to) : NO_ORIGIN;
|
|
18404
18670
|
};
|
|
18405
18671
|
const scopeOf2 = (site, from) => {
|
|
@@ -18429,7 +18695,7 @@ function buildCodeIndex(model) {
|
|
|
18429
18695
|
};
|
|
18430
18696
|
const possibleOriginsOf = (site, from) => {
|
|
18431
18697
|
const proven = originOf2(site, from);
|
|
18432
|
-
if (!site.field && !site.constructed && !site.via) return proven;
|
|
18698
|
+
if (!site.field && !site.constructed && !site.via && !site.returnedBy && !site.enclosingClass) return proven;
|
|
18433
18699
|
const resolved = scopeOf2(site, from);
|
|
18434
18700
|
if (!resolved) return proven;
|
|
18435
18701
|
const { path: scope, facts: f } = resolved;
|
|
@@ -18442,24 +18708,59 @@ function buildCodeIndex(model) {
|
|
|
18442
18708
|
if (site.field) for (const typeName of fieldTypesOf(f, site.field)) widenThroughType(typeName);
|
|
18443
18709
|
if (site.constructed) widenThrough(site.constructed, importBindingOf(f, site.constructed)?.from);
|
|
18444
18710
|
if (site.via) for (const typeName of localTypesOf(f, site.via)) widenThroughType(typeName);
|
|
18711
|
+
if (site.enclosingClass !== void 0 && holdsMemberBody(f, site.name, site.enclosingClass)) {
|
|
18712
|
+
for (const candidate of republishedFrom(scope)) out.add(candidate);
|
|
18713
|
+
}
|
|
18714
|
+
if (site.returnedBy !== void 0) {
|
|
18715
|
+
for (const fnFile of originOf2({ name: site.returnedBy, member: false }, scope)) {
|
|
18716
|
+
const at = facts.get(fnFile);
|
|
18717
|
+
if (at?.status !== "analyzed" || at.analysisGrade !== "exact") continue;
|
|
18718
|
+
const typeName = returnTypeOf(at, site.returnedBy);
|
|
18719
|
+
if (typeName === void 0) continue;
|
|
18720
|
+
const specifier = typeBindingOf(at, typeName);
|
|
18721
|
+
const landings = specifier !== void 0 ? landingFrom(fnFile, specifier) : declarationsAt(fnFile).has(typeName) ? republishedFrom(fnFile) : NO_ORIGIN;
|
|
18722
|
+
for (const candidate of landings) {
|
|
18723
|
+
const there = facts.get(candidate);
|
|
18724
|
+
if (there && holdsMemberBody(there, site.name, typeName)) out.add(candidate);
|
|
18725
|
+
}
|
|
18726
|
+
}
|
|
18727
|
+
}
|
|
18445
18728
|
return out;
|
|
18446
18729
|
};
|
|
18730
|
+
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);
|
|
18447
18731
|
const declarationsAt = (path49) => namesAt(declarations, pathKey(path49), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
|
|
18448
|
-
const forwardsOf = (start, name) => {
|
|
18732
|
+
const forwardsOf = (start, name, container) => {
|
|
18449
18733
|
const out = [];
|
|
18734
|
+
const listed = /* @__PURE__ */ new Set();
|
|
18450
18735
|
const seen = /* @__PURE__ */ new Set();
|
|
18451
|
-
const queue = [{ file: pathKey(start), name }];
|
|
18736
|
+
const queue = [{ file: pathKey(start), name, container }];
|
|
18452
18737
|
while (queue.length) {
|
|
18453
18738
|
const at = queue.shift();
|
|
18454
|
-
const key = `${at.file}#${at.name}`;
|
|
18739
|
+
const key = `${at.scope ? "s" : "p"}|${at.container ?? ""}|${at.file}#${at.name}`;
|
|
18455
18740
|
if (seen.has(key)) continue;
|
|
18456
18741
|
seen.add(key);
|
|
18457
|
-
|
|
18458
|
-
|
|
18742
|
+
if (!listed.has(`${at.file}#${at.name}`)) {
|
|
18743
|
+
listed.add(`${at.file}#${at.name}`);
|
|
18744
|
+
out.push({ file: at.file, name: at.name });
|
|
18745
|
+
}
|
|
18746
|
+
const f = facts.get(at.file);
|
|
18747
|
+
if (at.scope) {
|
|
18748
|
+
const binding = f ? importBindingOf(f, at.name) : void 0;
|
|
18749
|
+
if (!binding || binding.namespace) continue;
|
|
18750
|
+
const to = resolveImport(at.file, binding.from, paths, packages);
|
|
18751
|
+
if (to) queue.push({ file: to, name: binding.imported ?? at.name });
|
|
18752
|
+
continue;
|
|
18753
|
+
}
|
|
18754
|
+
for (const alias of f?.exportAliases ?? []) {
|
|
18755
|
+
if (alias.exported !== at.name || alias.container !== at.container) continue;
|
|
18756
|
+
queue.push({ file: at.file, name: alias.local, scope: true });
|
|
18757
|
+
}
|
|
18758
|
+
if (at.container !== void 0) continue;
|
|
18759
|
+
for (const binding of f?.reexportBindings ?? []) {
|
|
18459
18760
|
const star = binding.exported === "*";
|
|
18460
18761
|
if (!star && binding.exported !== at.name) continue;
|
|
18461
18762
|
if (star && at.name === "default") continue;
|
|
18462
|
-
const to = resolveImport(at.file, binding.from, paths);
|
|
18763
|
+
const to = resolveImport(at.file, binding.from, paths, packages);
|
|
18463
18764
|
if (!to) continue;
|
|
18464
18765
|
if (star && !facts.get(to)?.exportedNames.includes(at.name)) continue;
|
|
18465
18766
|
queue.push({ file: to, name: star ? at.name : binding.local });
|
|
@@ -18470,6 +18771,7 @@ function buildCodeIndex(model) {
|
|
|
18470
18771
|
return {
|
|
18471
18772
|
paths,
|
|
18472
18773
|
exactPaths,
|
|
18774
|
+
packages,
|
|
18473
18775
|
factsAt: (path49) => facts.get(pathKey(path49)),
|
|
18474
18776
|
declarationsAt,
|
|
18475
18777
|
anchorsAt: (path49) => namesAt(anchors, pathKey(path49), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
|
|
@@ -18523,7 +18825,7 @@ function buildImportGraph(index, universe) {
|
|
|
18523
18825
|
const traces = /* @__PURE__ */ new Map();
|
|
18524
18826
|
const resolveAll = (from, specifiers, into) => {
|
|
18525
18827
|
for (const specifier of specifiers) {
|
|
18526
|
-
const to = resolveImport(from, specifier, universe);
|
|
18828
|
+
const to = resolveImport(from, specifier, universe, index.packages);
|
|
18527
18829
|
if (to && to !== from) into.add(to);
|
|
18528
18830
|
}
|
|
18529
18831
|
};
|
|
@@ -18819,7 +19121,7 @@ var localIdsRule = {
|
|
|
18819
19121
|
var projectIdentityRule = {
|
|
18820
19122
|
name: "project-identity",
|
|
18821
19123
|
judges: "design",
|
|
18822
|
-
description: "The bound project is keyed on its id, so the id must be declared, well-formed, stable and its own: a project that declares none answers to its display name slugified until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), an id the rename migration moved from the one the lock approved is owed a re-lock and nothing more (PROJECT_ID_RENAMED: the approved id is one of the configuration's previousIds), and any other id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED). It judges the bound project's own identity only. The family half \u2014 a member with a defaulted id (named with its alias as the id to declare), a member with no id, and two members resolving to one id \u2014 is a family check
|
|
19124
|
+
description: "The bound project is keyed on its id, so the id must be declared, well-formed, stable and its own: a project that declares none answers to its display name slugified until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), an id the rename migration moved from the one the lock approved is owed a re-lock and nothing more (PROJECT_ID_RENAMED: the approved id is one of the configuration's previousIds), and any other id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED). It judges the bound project's own identity only. The family half \u2014 a member with a defaulted id (named with its alias as the id to declare), a member with no id, and two members resolving to one id \u2014 is a family check (family_validator.checkMembers), which reads the family root's graph.",
|
|
18823
19125
|
codes: [
|
|
18824
19126
|
{ code: "PROJECT_ID_AMBIGUOUS", defaultSeverity: "warning", summary: "Project has no usable id: its name yields no slug, or its declared id breaks the grammar" },
|
|
18825
19127
|
{ code: "PROJECT_ID_CHANGED", defaultSeverity: "error", summary: "Project id differs from the id the lock approved" },
|
|
@@ -18837,7 +19139,7 @@ function judgeOwnIdentity(ctx, identity2) {
|
|
|
18837
19139
|
ctx.addIssue(
|
|
18838
19140
|
"notice",
|
|
18839
19141
|
"PROJECT_ID_DEFAULTED",
|
|
18840
|
-
`Project "${identity2.name}" declares no id in .wai/project.yaml, so it answers to "${identity2.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity2.id}\` (
|
|
19142
|
+
`Project "${identity2.name}" declares no id in .wai/project.yaml, so it answers to "${identity2.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity2.id}\` (a project is keyed on an explicit id).`
|
|
18841
19143
|
);
|
|
18842
19144
|
}
|
|
18843
19145
|
const ambiguous = problem("ambiguous");
|
|
@@ -19226,10 +19528,10 @@ function problemMessage(problem, specId) {
|
|
|
19226
19528
|
var typeExpressionsRule = {
|
|
19227
19529
|
name: "type-expressions",
|
|
19228
19530
|
judges: "design",
|
|
19229
|
-
description: "Reports what the scan's type canonicalisation recorded (ctx.typeSpellingFacts), one finding per position, located by spec and path: a stored text that does not parse (TYPE_EXPRESSION_INVALID); one that breaks a position rule \u2014 void or
|
|
19531
|
+
description: "Reports what the scan's type canonicalisation recorded (ctx.typeSpellingFacts), one finding per position, located by spec and path: a stored text that does not parse (TYPE_EXPRESSION_INVALID); one that breaks a position rule \u2014 void, async or result out of place, a map key that is not string, int or an enum, a named scalar's holds that is not one primitive other than void and any, `T??` (TYPE_POSITION_INVALID); one using a form the grammar leaves out \u2014 an inline object shape, an inline function type, a string-literal union, a union mixing in a primitive or a collection, an intersection, a utility type or a tuple (TYPE_FORM_UNSUPPORTED); one naming `number` or a legacy builtin with no neutral meaning (TYPE_NOT_NEUTRAL); and one that is an alias of its canonical spelling (TYPE_SPELLING_STALE, which any save or doctor --fix repairs). Every message names the replacement: `number` asks \"int or float?\" (and says when the doctor repair proposes int), a function type names a signature type, a literal union an enum (which the doctor repair proposes), an inline object or a mixed union a named value-object. The writer refuses all but the stale spelling at write time; on load every one of them is a warning \u2014 these findings are how a tree that held them before the grammar existed is told, while it still loads, stays lockable with its debt in view, and its consumers read the position as opaque any, which never invents a shape. No finding when the run carries no facts (a candidate run).",
|
|
19230
19532
|
codes: [
|
|
19231
|
-
{ code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "
|
|
19232
|
-
{ code: "TYPE_POSITION_INVALID", defaultSeverity: "
|
|
19533
|
+
{ code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "warning", summary: "A structured type position does not parse under the type grammar" },
|
|
19534
|
+
{ code: "TYPE_POSITION_INVALID", defaultSeverity: "warning", summary: "A type position breaks a position rule: void, async or result out of place, a non-scalar map key, or T??" },
|
|
19233
19535
|
{ code: "TYPE_FORM_UNSUPPORTED", defaultSeverity: "warning", summary: "A type position uses a form the grammar leaves out \u2014 inline object, inline function type, string-literal union, union mixing in a primitive or collection, intersection, utility type or tuple \u2014 and the message names its named replacement" },
|
|
19234
19536
|
{ code: "TYPE_NOT_NEUTRAL", defaultSeverity: "warning", summary: "A type position names number (int or float?) or a legacy builtin with no neutral meaning, and the message names the replacement" },
|
|
19235
19537
|
{ code: "TYPE_SPELLING_STALE", defaultSeverity: "warning", summary: "A stored type position is an alias of its canonical spelling; any save or doctor --fix rewrites it" }
|
|
@@ -19239,9 +19541,8 @@ var typeExpressionsRule = {
|
|
|
19239
19541
|
if (!facts) return;
|
|
19240
19542
|
for (const problem of facts.problems) {
|
|
19241
19543
|
if (problem.specId === void 0 || !ctx.isSpecInScope(problem.specId)) continue;
|
|
19242
|
-
const severity = problem.code === "TYPE_EXPRESSION_INVALID" || problem.code === "TYPE_POSITION_INVALID" ? "error" : "warning";
|
|
19243
19544
|
ctx.addIssue(
|
|
19244
|
-
|
|
19545
|
+
"warning",
|
|
19245
19546
|
problem.code,
|
|
19246
19547
|
problemMessage(problem, problem.specId),
|
|
19247
19548
|
problem.specId,
|
|
@@ -19575,7 +19876,7 @@ var publicSurfaceBoundContractRule = {
|
|
|
19575
19876
|
var exportTablesRule = {
|
|
19576
19877
|
name: "export-tables",
|
|
19577
19878
|
judges: "design",
|
|
19578
|
-
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets, or two public names share one nameKey, since a bare import could not tell them apart; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns \u2014 a subsystem-owned type at L1, a project-level type (no subsystem) for an L0 `{ typeDef }` without `from` \u2014 under a public name of [a-z0-9-_]+ (EXPORT_INVALID), every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports
|
|
19879
|
+
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets, or two public names share one nameKey, since a bare import could not tell them apart; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns \u2014 a subsystem-owned type at L1, a project-level type (no subsystem) for an L0 `{ typeDef }` without `from` \u2014 under a public name of [a-z0-9-_]+ (EXPORT_INVALID), every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports), and an L0 entry that re-exports a member's or an external's export never declares a wider audience than that export has (EXPORT_WIDENS_AUDIENCE): a re-export can narrow reach, never widen it. The rule judges the facts the export resolver recorded in the scan \u2014 for the bound root's table and every contained member's \u2014 and resolves nothing itself.",
|
|
19579
19880
|
codes: [
|
|
19580
19881
|
{ code: "EXPORT_ID_DUPLICATE", defaultSeverity: "error", summary: "One public name bound to different targets by two wildcard re-exports or two explicit entries" },
|
|
19581
19882
|
{ code: "EXPORT_CYCLE", defaultSeverity: "error", summary: "A re-export chain leads back to itself (a warning for a wildcard cycle, which resolves to its union)" },
|
|
@@ -19737,7 +20038,7 @@ function whyNoRewrite(ref) {
|
|
|
19737
20038
|
var referenceFormsRule = {
|
|
19738
20039
|
name: "reference-forms",
|
|
19739
20040
|
judges: "design",
|
|
19740
|
-
description: "A cross-project reference is `alias::name` (or a bare name a `use` imports): the alias is one of the referring project's members or externals, the name a public name of that project's L0 table. The older forms still bind for one release and are reported (DEPRECATED_REFERENCE_FORM), each naming the spec, where in it, the form as written and the text to write instead (the scan's rewrite: a bare local id, or `alias::publicName`): a leading `::`, which escapes to the root of whatever checkout loads it; `super::`, which climbs the containing projects \u2014 a place in one family, not a name; a member path (`desktop::shell` written inside desktop, `
|
|
20041
|
+
description: "A cross-project reference is `alias::name` (or a bare name a `use` imports): the alias is one of the referring project's members or externals, the name a public name of that project's L0 table. The older forms still bind for one release and are reported (DEPRECATED_REFERENCE_FORM), each naming the spec, where in it, the form as written and the text to write instead (the scan's rewrite: a bare local id, or `alias::publicName`): a leading `::`, which escapes to the root of whatever checkout loads it; `super::`, which climbs the containing projects \u2014 a place in one family, not a name; a member path (`desktop::shell` written inside desktop, `app_core::engine::x` written inside engine), a first segment the referring project does not declare, read from the bound root; and a self-prefix \u2014 the referring project's own id as the first segment, with `::` or `.` (`registry.advisory-channel` inside registry), which `wairon doctor --fix` rewrites to the bare local id. Wairon's own writer emits none of them. A form with no rewrite (its target is out of reach) is reported with the reason. It reads the project graph's authored references, which the scan records before binding erases the form.",
|
|
19741
20042
|
codes: [
|
|
19742
20043
|
{ code: "DEPRECATED_REFERENCE_FORM", defaultSeverity: "notice", summary: "A reference written with a leading ::, super:: or a member path where a local id or alias::name belongs" }
|
|
19743
20044
|
],
|
|
@@ -19768,9 +20069,9 @@ function carriedFields(mount) {
|
|
|
19768
20069
|
var memberDeclarationsRule = {
|
|
19769
20070
|
name: "member-declarations",
|
|
19770
20071
|
judges: "design",
|
|
19771
|
-
description: "A project declares its members in project.yaml `members`, each by one source in one grammar. A deprecated declaration form loads for one release and is reported (DEPRECATED_MOUNT_FORM, notice): the L1 form \u2014 a subsystem carrying projectPath \u2014 naming the mount, its path, the `members` entry that replaces it, and every field it carries that `doctor --fix` will move into the member (or stop on); a mount whose alias `members` also declares is reported as ignored; and
|
|
20072
|
+
description: "A project declares its members in project.yaml `members`, each by one source in one grammar. A deprecated declaration form loads for one release and is reported (DEPRECATED_MOUNT_FORM, notice): the L1 form \u2014 a subsystem carrying projectPath \u2014 naming the mount, its path, the `members` entry that replaces it, and every field it carries that `doctor --fix` will move into the member (or stop on); a mount whose alias `members` also declares is reported as ignored; and the deprecated long-form `path` key, naming the `source` (or shorthand) `doctor --fix` rewrites it to. It also judges what each member of the bound project IS against what is declared or written about it, from the scan's problems at the bound root (a member's own members are that member's gate's): what a member is follows from its content \u2014 an id, an L0 or a lock make it a project, anything else a part \u2014 and every contradiction is ONE finding, MEMBER_KIND_MISMATCH (error), naming the alias and the contradiction: an asserted `as` the content contradicts; a part whose configuration declares project fields (members, externals, rules, extensions, composition, targets) without the id that would make it a project (add the id, or move them to the parent); a non-contained part with no PartOf, or one naming another project (opened alone it could not say what it belongs to); a contained member declaring PartOf; a `use` on a member whose content is a part (its names are already this project's own). A part whose files could not be read \u2014 its directory absent, or its git commit not cached and not fetchable (offline, refused, unknown) \u2014 is PART_UNAVAILABLE (error): the project's own subsystems are missing, so its verdict cannot be a pass. The member findings name no spec: members live in configuration. It reads the project graph's nodes, their legacy mounts, the bound root's declared members and its problems.",
|
|
19772
20073
|
codes: [
|
|
19773
|
-
{ code: "DEPRECATED_MOUNT_FORM", defaultSeverity: "notice", summary: "A member declared in a deprecated form \u2014 an L1 subsystem carrying projectPath, or
|
|
20074
|
+
{ code: "DEPRECATED_MOUNT_FORM", defaultSeverity: "notice", summary: "A member declared in a deprecated form \u2014 an L1 subsystem carrying projectPath, or the long-form `path` key \u2014 which doctor --fix rewrites to a `members` entry with one `source`." },
|
|
19774
20075
|
{ code: "MEMBER_KIND_MISMATCH", defaultSeverity: "error", summary: "What a member's content makes it (an id, L0 or lock: a project; else a part) contradicts what is declared or written about it: an asserted `as`, project fields in a part, a missing or wrong PartOf, a `use` on a part." },
|
|
19775
20076
|
{ code: "PART_UNAVAILABLE", defaultSeverity: "error", summary: "A part's files could not be read (absent directory, unpinned or unfetchable git commit): the project's own subsystems are missing." }
|
|
19776
20077
|
],
|
|
@@ -19808,7 +20109,7 @@ var memberDeclarationsRule = {
|
|
|
19808
20109
|
ctx.addIssue(
|
|
19809
20110
|
"notice",
|
|
19810
20111
|
"DEPRECATED_MOUNT_FORM",
|
|
19811
|
-
`This project declares the member "${member.alias}" with the long-form \`path\` key, which
|
|
20112
|
+
`This project declares the member "${member.alias}" with the long-form \`path\` key, which the one location key replaced: \`source\`. \`doctor --fix\` rewrites it to ${rest ? `\`source: ${written}\` in its long form` : `the shorthand \`${member.alias}: ${written}\``} \u2014 its meaning unchanged.`
|
|
19812
20113
|
);
|
|
19813
20114
|
}
|
|
19814
20115
|
for (const problem of family.problems) {
|
|
@@ -19845,7 +20146,7 @@ function declaration(producer) {
|
|
|
19845
20146
|
var projectBoundariesRule = {
|
|
19846
20147
|
name: "project-boundaries",
|
|
19847
20148
|
judges: "design",
|
|
19848
|
-
description: "The one judge of every cross-project reference the bound project writes (
|
|
20149
|
+
description: "The one judge of every cross-project reference the bound project writes (every project is a crate of its own, reached only through its L0 exports and only as a dependency it declared). It reads each of the project's own references from the project graph \u2014 `::` forms and bare names bound through `use` imports alike \u2014 takes its resolution from resolveCrossProject, decided before any severity from the project's own files alone, and reports every outcome but resolved once, carrying the resolution: forbidden is EXTERNAL_UNDECLARED (the reference reaches a project the referrer declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers); missing is EXTERNAL_NOT_EXPORTED (the producer's table \u2014 a contained member's live one, or the external's pin \u2014 has no public name covering the referrer, or not the used method, or exports the type only inside a signature's closure); an ambiguous import is IMPORT_AMBIGUOUS (two imports supply one bare name: two explicit `use` names, or two `*` with no explicit one \u2014 name it `alias::name` or narrow a `use`); unavailable is EXTERNAL_CHECK_UNAVAILABLE (no pin, an unreadable pin, a member absent on disk, or a deprecated form naming no alias \u2014 never a pass). A bare name no import supplies is not this rule's: it is the unresolved-reference finding it always was, with rule_context.importHint appended. A trustedLinks entry naming a subsystem of another project (`alias::sub`) is TRUSTED_LINK_CROSSES_PROJECT: a sanctioned fast lane stays inside one project. An undeclared, unexported or ambiguous reference is an error, since the owner's gate judges it exactly; a reference is never left to a parent's verdict.",
|
|
19849
20150
|
codes: [
|
|
19850
20151
|
{ code: "EXTERNAL_UNDECLARED", defaultSeverity: "error", summary: "A reference reaches another project the referring project declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers" },
|
|
19851
20152
|
{ code: "EXTERNAL_NOT_EXPORTED", defaultSeverity: "error", summary: "A reference reaches another project at anything but a public name, or a member of one, whose audience covers the referrer" },
|
|
@@ -22391,7 +22692,7 @@ var reproducibilityRule = {
|
|
|
22391
22692
|
ctx.addIssue(
|
|
22392
22693
|
"warning",
|
|
22393
22694
|
"PACK_SOURCE_UNFETCHABLE",
|
|
22394
|
-
`Pack selection "${selection.name}" records no source and is not bundled, so a fresh machine or CI runner cannot obtain it \u2014 \`wairon pack sync\` has nothing to fetch.
|
|
22695
|
+
`Pack selection "${selection.name}" records no source and is not bundled, so a fresh machine or CI runner cannot obtain it \u2014 \`wairon pack sync\` has nothing to fetch. Commit a copy the repository carries (\`wairon pack bundle ${selection.name}\`, or \`wairon pack bundle --all\` for every selection), or record a URL \`wairon pack sync\` fetches it from (\`wairon pack use ${selection.name} --source <url>\`). A selection \`wairon doctor --fix\` recorded from a pack installed from a local path has no URL to record: bundle it.`
|
|
22395
22696
|
);
|
|
22396
22697
|
}
|
|
22397
22698
|
}
|
|
@@ -23378,10 +23679,22 @@ var invariantBackingRule = {
|
|
|
23378
23679
|
};
|
|
23379
23680
|
|
|
23380
23681
|
// src/core/rules/wiring/invariant-references.ts
|
|
23682
|
+
function unknownInvariantMessage(where, ref, types, aliases) {
|
|
23683
|
+
const parts = splitInvariantRef(ref);
|
|
23684
|
+
const qualifier = parts ? parts.typeRef.lastIndexOf("::") : -1;
|
|
23685
|
+
if (parts && qualifier > 0) {
|
|
23686
|
+
const bare = `${parts.typeRef.slice(qualifier + 2)}.${parts.invariantId}`;
|
|
23687
|
+
if (resolveInvariantRef(bare, types, aliases)) {
|
|
23688
|
+
const prefix = parts.typeRef.slice(0, qualifier);
|
|
23689
|
+
return `${where} asserts invariant "${ref}", whose qualifier "${prefix}::" names no project this one reaches the type through \u2014 the invariant is declared here, so write "${bare}" (a project's own alias, written from inside it, is not a qualifier it reads as its own; \`wairon doctor --fix\` rewrites it in a migrated family).`;
|
|
23690
|
+
}
|
|
23691
|
+
}
|
|
23692
|
+
return `${where} asserts invariant "${ref}", but no entity declares it (expected "<type-id>.<invariant-id>" naming a declared entry in that entity's invariants).`;
|
|
23693
|
+
}
|
|
23381
23694
|
var invariantReferencesRule = {
|
|
23382
23695
|
name: "invariant-references",
|
|
23383
23696
|
judges: "design",
|
|
23384
|
-
description: "Every invariant a narrative step asserts (step.assertsInvariants) must resolve to a declared entity invariant: the reference splits at its last dot into a type reference and an invariant id, and some entity matching that type reference must declare that id. A reference nothing declares is an error \u2014 the step claims a property that exists nowhere.",
|
|
23697
|
+
description: "Every invariant a narrative step asserts (step.assertsInvariants) must resolve to a declared entity invariant: the reference splits at its last dot into a type reference and an invariant id, and some entity matching that type reference must declare that id. A reference nothing declares is an error \u2014 the step claims a property that exists nowhere. The message says which: when the type reference carries a qualifier (`alias::type`) and the bare reference does resolve, the cause is the qualifier \u2014 typically the project's own alias written from inside it \u2014 and the message names it and the bare reference to write instead, never claiming that no entity declares the invariant.",
|
|
23385
23698
|
codes: [
|
|
23386
23699
|
{ code: "UNKNOWN_INVARIANT_REF", defaultSeverity: "error", summary: "A narrative step asserts an invariant that no entity declares" }
|
|
23387
23700
|
],
|
|
@@ -23396,7 +23709,7 @@ var invariantReferencesRule = {
|
|
|
23396
23709
|
ctx.addIssue(
|
|
23397
23710
|
"error",
|
|
23398
23711
|
"UNKNOWN_INVARIANT_REF",
|
|
23399
|
-
`Step ${step.stepNumber} of "${method.name}" in implementation "${impl.id}"
|
|
23712
|
+
unknownInvariantMessage(`Step ${step.stepNumber} of "${method.name}" in implementation "${impl.id}"`, ref, ctx.types, aliases),
|
|
23400
23713
|
impl.id,
|
|
23401
23714
|
isDraftCtx
|
|
23402
23715
|
);
|
|
@@ -23741,13 +24054,13 @@ function invokedAs(code, site, file) {
|
|
|
23741
24054
|
if (!site.member) {
|
|
23742
24055
|
const binding = importBindingOf(facts, site.name);
|
|
23743
24056
|
if (!binding || binding.namespace) return [];
|
|
23744
|
-
const module3 = resolveImport(scope, binding.from, code.paths);
|
|
24057
|
+
const module3 = resolveImport(scope, binding.from, code.paths, code.packages);
|
|
23745
24058
|
return module3 ? code.forwardsOf(module3, binding.imported ?? site.name) : [];
|
|
23746
24059
|
}
|
|
23747
24060
|
if (!site.via) return [];
|
|
23748
24061
|
const receiver = importBindingOf(facts, site.via);
|
|
23749
24062
|
if (!receiver?.namespace) return [];
|
|
23750
|
-
const module2 = resolveImport(scope, receiver.from, code.paths);
|
|
24063
|
+
const module2 = resolveImport(scope, receiver.from, code.paths, code.packages);
|
|
23751
24064
|
return module2 ? code.forwardsOf(module2, site.name) : [];
|
|
23752
24065
|
}
|
|
23753
24066
|
function claimsOf(implMethod) {
|
|
@@ -23769,6 +24082,8 @@ function describeSite(site) {
|
|
|
23769
24082
|
if (site.via) return `${site.via}.${site.name}(\u2026)`;
|
|
23770
24083
|
if (site.field) return `this.${site.field}.${site.name}(\u2026)`;
|
|
23771
24084
|
if (site.constructed) return `new ${site.constructed}(\u2026).${site.name}(\u2026)`;
|
|
24085
|
+
if (site.returnedBy) return `${site.returnedBy}(\u2026).${site.name}(\u2026)`;
|
|
24086
|
+
if (site.enclosingClass) return `this.${site.name}(\u2026)`;
|
|
23772
24087
|
return `<receiver>.${site.name}(\u2026)`;
|
|
23773
24088
|
}
|
|
23774
24089
|
function describeMiss(m) {
|
|
@@ -23781,7 +24096,7 @@ function describeMiss(m) {
|
|
|
23781
24096
|
var callConformanceRule = {
|
|
23782
24097
|
name: "call-conformance",
|
|
23783
24098
|
judges: "code",
|
|
23784
|
-
description: "Code\u2194spec Level 3: the CLAIMED call \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. A method claims a call two ways and they face one check: a narrative `call` step, and an entry of the declared `calls` a narrative-less method reaches its collaborators through. They assert the same thing, and the step number is no part of the claim \u2014 this check never reads order, so it is only how a finding points at one. Forward: every claim must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from,
|
|
24099
|
+
description: "Code\u2194spec Level 3: the CLAIMED call \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. A method claims a call two ways and they face one check: a narrative `call` step, and an entry of the declared `calls` a narrative-less method reaches its collaborators through. They assert the same thing, and the step number is no part of the claim \u2014 this check never reads order, so it is only how a finding points at one. Forward: every claim must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from, a plain `<name>.<method>()` receiver through the type the file ANNOTATES that name with \u2014 a parameter's, or an annotated variable's, which is how a module that wires its collaborators as closures writes every one of its calls \u2014 a `fn(...).<method>()` receiver through the type fn RETURNS where the code settles it, and a `this.<method>()` receiver through the class whose own method it sits in; a package specifier naming a package of this repository resolves like a relative one, and an export alias (`export { a as b }`, an adapter object property `{ b: a }`) forwards by identity like a re-export. A matching call whose origin a pure model cannot resolve is reported apart as CALL_ORIGIN_UNRESOLVED, which NAMES the shape it could not follow and asks for nothing \u2014 a coverage hole in the reader, reported for the reason CONFORMANCE_DEGRADED is: a silently degraded gate is worse than a degraded one. A finding names a landing only from the PROVEN tier so that widening what a call reached can accept a claim but never accuse one, and a target that names no file of its own falls back to name membership. Converse: a call that resolves to a modelled method of ANOTHER component in the SAME file crosses a component boundary while looking local, so the method must claim it \u2014 in a step or in `calls` (UNDECLARED_COLOCATED_CALL) \u2014 judged on the proven tier alone, and a same-file private helper is no modelled method and is never reported; a function forwarding by identity to a modelled method written in another file IS that method, so what it calls is that method's subject. A method that claims nothing at all is judged in neither direction: what it leaves unsaid is UNUSED_*'s subject. Order, arguments and conditions stay unverified, dispatch steps (runtime-table routed) are no claim about a call site, a malformed declared reference is MALFORMED_DECLARED_CALL's finding and names no target here, the conformance dial (off) skips, and weaker analysis grades never guess.",
|
|
23785
24100
|
codes: [
|
|
23786
24101
|
{ code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step or declared call realized by no call that resolves to the target method's own source file \u2014 the call is absent, or it lands in another module", carryable: true },
|
|
23787
24102
|
{ code: "CALL_ORIGIN_UNRESOLVED", defaultSeverity: "warning", summary: "Narrative call step or declared call whose target name IS called, but only from call sites written in a shape this analysis cannot resolve to a file \u2014 the claim was not checked, and is neither proven realized nor accused", carryable: true },
|
|
@@ -23824,7 +24139,7 @@ var callConformanceRule = {
|
|
|
23824
24139
|
if (!ctx.componentMap.has(claim.component)) continue;
|
|
23825
24140
|
const target = resolveCallTarget(ctx, claim.component, claim.method);
|
|
23826
24141
|
if (target.accepted.has(fnSymbol)) continue;
|
|
23827
|
-
if (isTarget(code.forwardsOf(file, fnSymbol), target)) continue;
|
|
24142
|
+
if (isTarget(code.forwardsOf(file, fnSymbol, implMethod.exportedVia), target)) continue;
|
|
23828
24143
|
const matching = sites.filter((s) => target.accepted.has(s.name));
|
|
23829
24144
|
const ref = `${claim.component}.${claim.method}`;
|
|
23830
24145
|
if (target.files.size === 0) {
|
|
@@ -23867,7 +24182,7 @@ var callConformanceRule = {
|
|
|
23867
24182
|
ctx.addIssue(
|
|
23868
24183
|
"warning",
|
|
23869
24184
|
"CALL_ORIGIN_UNRESOLVED",
|
|
23870
|
-
`Method "${implMethod.name}" in implementation "${impl.id}": ${unresolved.length} ${claimNoun(implMethod)} ARE called by name inside the function "${fnSymbol}" in "${file}", but every site calling them is written in a shape this analysis cannot resolve to a file \u2014 ${unresolved.map(describeMiss).join("; ")}. What resolves is a name bound to a module of THIS project: a bare or namespaced call through such an import binding, a \`this.<field>\` receiver whose declared type names one, a \`new Class(\u2026)\` receiver whose class does,
|
|
24185
|
+
`Method "${implMethod.name}" in implementation "${impl.id}": ${unresolved.length} ${claimNoun(implMethod)} ARE called by name inside the function "${fnSymbol}" in "${file}", but every site calling them is written in a shape this analysis cannot resolve to a file \u2014 ${unresolved.map(describeMiss).join("; ")}. What resolves is a name bound to a module of THIS project: a bare or namespaced call through such an import binding (a package of this repository included), a \`this.<field>\` receiver whose declared type names one, a \`new Class(\u2026)\` receiver whose class does, a receiver NAME the file annotates with a type that does, a \`fn(\u2026)\` receiver whose function's return the code settles on a type with that method's body, and \`this\` inside a class's own method that has it. What does not: an import from a third-party PACKAGE, a receiver holding a value the module assembled, a receiver this model records no name for, and one the file annotates with nothing. This reports what was not checked, not what is wrong \u2014 the claim is neither proven realized nor accused of being missing, and no working call is asked to be rewritten to suit the reader.`,
|
|
23871
24186
|
impl.id,
|
|
23872
24187
|
entry.draftContext,
|
|
23873
24188
|
void 0,
|
|
@@ -23889,7 +24204,9 @@ var callConformanceRule = {
|
|
|
23889
24204
|
reached.set(site, owner);
|
|
23890
24205
|
return owner !== void 0;
|
|
23891
24206
|
};
|
|
23892
|
-
|
|
24207
|
+
const forwardsToModelled = code.forwardsOf(file, fnSymbol, implMethod.exportedVia).some((p) => p.file !== here && modelledAt.get(p.file)?.byName.has(p.name));
|
|
24208
|
+
const conversed = forwardsToModelled ? [] : closedCallSites(code, file, fnSymbol, stopAtModelled, implMethod.exportedVia) ?? [];
|
|
24209
|
+
for (const site of conversed) {
|
|
23893
24210
|
const owner = reached.get(site);
|
|
23894
24211
|
if (!owner || owner.component === component.id) continue;
|
|
23895
24212
|
const ref = `${owner.component}.${owner.method}`;
|
|
@@ -25077,7 +25394,7 @@ var exportConformanceRule = {
|
|
|
25077
25394
|
const facts = code.factsAt(file);
|
|
25078
25395
|
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
25079
25396
|
for (const binding of facts.reexportBindings ?? []) {
|
|
25080
|
-
const target = resolveImport(file, binding.from, code.paths);
|
|
25397
|
+
const target = resolveImport(file, binding.from, code.paths, code.packages);
|
|
25081
25398
|
const methods = target ? typeMethodsAt.get(target) : void 0;
|
|
25082
25399
|
if (!methods) continue;
|
|
25083
25400
|
if (binding.local === "*") {
|
|
@@ -25089,7 +25406,7 @@ var exportConformanceRule = {
|
|
|
25089
25406
|
}
|
|
25090
25407
|
const takenFrom = /* @__PURE__ */ new Map();
|
|
25091
25408
|
const take = (consumer, writtenIn, specifier, names) => {
|
|
25092
|
-
const target = resolveImport(writtenIn, specifier, code.paths);
|
|
25409
|
+
const target = resolveImport(writtenIn, specifier, code.paths, code.packages);
|
|
25093
25410
|
if (!target) return;
|
|
25094
25411
|
let byName = takenFrom.get(target);
|
|
25095
25412
|
if (!byName) takenFrom.set(target, byName = /* @__PURE__ */ new Map());
|
|
@@ -26823,6 +27140,9 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
26823
27140
|
const reexports = /* @__PURE__ */ new Set();
|
|
26824
27141
|
const starExports = [];
|
|
26825
27142
|
const namedReexports = [];
|
|
27143
|
+
const exportAliases = [];
|
|
27144
|
+
const propertyAliases = [];
|
|
27145
|
+
const returnTypes = /* @__PURE__ */ new Map();
|
|
26826
27146
|
const complexity = /* @__PURE__ */ new Map();
|
|
26827
27147
|
const calls = /* @__PURE__ */ new Map();
|
|
26828
27148
|
const bodies = /* @__PURE__ */ new Map();
|
|
@@ -26839,7 +27159,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
26839
27159
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
26840
27160
|
const schemaConstants = /* @__PURE__ */ new Map();
|
|
26841
27161
|
const derivedAliases = [];
|
|
26842
|
-
const receiverKey = (site) => site.via ?? (site.field ? `this.${site.field}` : site.constructed ? `new ${site.constructed}` : "");
|
|
27162
|
+
const receiverKey = (site) => site.via ?? (site.field ? `this.${site.field}` : site.constructed ? `new ${site.constructed}` : site.returnedBy ? `${site.returnedBy}()` : site.enclosingClass ? `this:${site.enclosingClass}` : "");
|
|
26843
27163
|
const siteKey = (site) => `${site.member ? "m" : "b"}:${receiverKey(site)}:${site.name}`;
|
|
26844
27164
|
const addBindingNames = (name) => {
|
|
26845
27165
|
if (ts.isIdentifier(name)) declared.add(name.text);
|
|
@@ -26947,6 +27267,44 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
26947
27267
|
}
|
|
26948
27268
|
return void 0;
|
|
26949
27269
|
};
|
|
27270
|
+
const enclosingClassOf = (node) => {
|
|
27271
|
+
let at = node.parent;
|
|
27272
|
+
while (at) {
|
|
27273
|
+
if (ts.isFunctionExpression(at) || ts.isFunctionDeclaration(at) || ts.isClassExpression(at)) return void 0;
|
|
27274
|
+
if ((ts.isMethodDeclaration(at) || ts.isGetAccessorDeclaration(at) || ts.isSetAccessorDeclaration(at)) && at.parent && ts.isObjectLiteralExpression(at.parent)) return void 0;
|
|
27275
|
+
if (ts.isClassDeclaration(at)) {
|
|
27276
|
+
return at.name && at.parent && ts.isSourceFile(at.parent) ? at.name.text : void 0;
|
|
27277
|
+
}
|
|
27278
|
+
at = at.parent;
|
|
27279
|
+
}
|
|
27280
|
+
return void 0;
|
|
27281
|
+
};
|
|
27282
|
+
const returnedTypeOf = (fn) => {
|
|
27283
|
+
const annotated = fn.type;
|
|
27284
|
+
if (annotated) {
|
|
27285
|
+
if (!ts.isTypeReferenceNode(annotated)) return void 0;
|
|
27286
|
+
if (ts.isIdentifier(annotated.typeName) && annotated.typeName.text === "Promise" && annotated.typeArguments?.length === 1) {
|
|
27287
|
+
const inner2 = annotated.typeArguments[0];
|
|
27288
|
+
return ts.isTypeReferenceNode(inner2) && !inner2.typeArguments ? typeReferenceName(inner2) : void 0;
|
|
27289
|
+
}
|
|
27290
|
+
return annotated.typeArguments ? void 0 : typeReferenceName(annotated);
|
|
27291
|
+
}
|
|
27292
|
+
const constructedClass = (expression) => {
|
|
27293
|
+
let node = expression;
|
|
27294
|
+
while (node && (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node))) node = node.expression;
|
|
27295
|
+
return node && ts.isNewExpression(node) && ts.isIdentifier(node.expression) ? node.expression.text : void 0;
|
|
27296
|
+
};
|
|
27297
|
+
if (!fn.body) return void 0;
|
|
27298
|
+
if (!ts.isBlock(fn.body)) return constructedClass(fn.body);
|
|
27299
|
+
const returns = [];
|
|
27300
|
+
const collect = (node) => {
|
|
27301
|
+
if (ts.isFunctionLike(node)) return;
|
|
27302
|
+
if (ts.isReturnStatement(node)) returns.push(node);
|
|
27303
|
+
ts.forEachChild(node, collect);
|
|
27304
|
+
};
|
|
27305
|
+
ts.forEachChild(fn.body, collect);
|
|
27306
|
+
return returns.length === 1 ? constructedClass(returns[0].expression) : void 0;
|
|
27307
|
+
};
|
|
26950
27308
|
const isTopLevelVariable = (decl) => ts.isVariableDeclaration(decl) && ts.isIdentifier(decl.name) && !!decl.parent && ts.isVariableDeclarationList(decl.parent) && !!decl.parent.parent && ts.isVariableStatement(decl.parent.parent) && !!decl.parent.parent.parent && ts.isSourceFile(decl.parent.parent.parent);
|
|
26951
27309
|
const containerOf = (owner) => {
|
|
26952
27310
|
if (!owner) return void 0;
|
|
@@ -27008,6 +27366,11 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
27008
27366
|
addSite({ name, member: true, field: receiver.name.text });
|
|
27009
27367
|
} else if (ts.isNewExpression(receiver) && ts.isIdentifier(receiver.expression)) {
|
|
27010
27368
|
addSite({ name, member: true, constructed: receiver.expression.text });
|
|
27369
|
+
} else if (ts.isCallExpression(receiver) && ts.isIdentifier(receiver.expression)) {
|
|
27370
|
+
addSite({ name, member: true, returnedBy: receiver.expression.text });
|
|
27371
|
+
} else if (receiver.kind === ts.SyntaxKind.ThisKeyword) {
|
|
27372
|
+
const owner = enclosingClassOf(node);
|
|
27373
|
+
addSite(owner !== void 0 ? { name, member: true, enclosingClass: owner } : { name, member: true });
|
|
27011
27374
|
} else addSite({ name, member: true });
|
|
27012
27375
|
}
|
|
27013
27376
|
}
|
|
@@ -27134,6 +27497,8 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
27134
27497
|
signatures.push(declaredParameters(node));
|
|
27135
27498
|
functionParams.set(fnName, signatures);
|
|
27136
27499
|
if (completesLater(node)) asyncFunctions.push(fnName);
|
|
27500
|
+
const returned = returnedTypeOf(node) ?? null;
|
|
27501
|
+
returnTypes.set(fnName, returnTypes.has(fnName) && returnTypes.get(fnName) !== returned ? null : returned);
|
|
27137
27502
|
const routes = collectRoutes(node);
|
|
27138
27503
|
if (routes.size > 0) {
|
|
27139
27504
|
const known = functionRoutes.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
@@ -27170,6 +27535,23 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
27170
27535
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
|
|
27171
27536
|
schemaConstants.set(node.name.text, node.initializer);
|
|
27172
27537
|
}
|
|
27538
|
+
if (isTopLevelVariable(node) && node.initializer) {
|
|
27539
|
+
let literal = node.initializer;
|
|
27540
|
+
while (ts.isAsExpression(literal) || ts.isSatisfiesExpression(literal) || ts.isParenthesizedExpression(literal)) {
|
|
27541
|
+
literal = literal.expression;
|
|
27542
|
+
}
|
|
27543
|
+
if (ts.isObjectLiteralExpression(literal)) {
|
|
27544
|
+
const container = node.name.text;
|
|
27545
|
+
for (const property of literal.properties) {
|
|
27546
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
27547
|
+
propertyAliases.push({ exported: property.name.text, local: property.name.text, container });
|
|
27548
|
+
} else if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.initializer)) {
|
|
27549
|
+
const key = propertyNameText(property.name);
|
|
27550
|
+
if (key) propertyAliases.push({ exported: key, local: property.initializer.text, container });
|
|
27551
|
+
}
|
|
27552
|
+
}
|
|
27553
|
+
}
|
|
27554
|
+
}
|
|
27173
27555
|
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
|
|
27174
27556
|
const name = node.name && ts.isIdentifier(node.name) ? node.name.text : void 0;
|
|
27175
27557
|
if (name) {
|
|
@@ -27221,9 +27603,10 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
27221
27603
|
for (const el of node.exportClause.elements) {
|
|
27222
27604
|
declared.add(el.name.text);
|
|
27223
27605
|
exported.add(el.name.text);
|
|
27224
|
-
if (
|
|
27225
|
-
|
|
27226
|
-
}
|
|
27606
|
+
if (node.isTypeOnly || el.isTypeOnly) continue;
|
|
27607
|
+
const local = (el.propertyName ?? el.name).text;
|
|
27608
|
+
if (spec) namedReexports.push({ exported: el.name.text, local, from: spec });
|
|
27609
|
+
else exportAliases.push({ exported: el.name.text, local });
|
|
27227
27610
|
}
|
|
27228
27611
|
} else if (!node.exportClause && spec) {
|
|
27229
27612
|
starExports.push(spec);
|
|
@@ -27298,7 +27681,13 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
27298
27681
|
if (values && schemaConstants.has(alias.constant)) enumValues.set(alias.name, values);
|
|
27299
27682
|
}
|
|
27300
27683
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
27301
|
-
|
|
27684
|
+
for (const alias of propertyAliases) if (exported.has(alias.container)) exportAliases.push(alias);
|
|
27685
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, exportAliases, returnTypes, complexity, calls, bodies, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
|
|
27686
|
+
}
|
|
27687
|
+
var NO_PACKAGES = /* @__PURE__ */ new Map();
|
|
27688
|
+
function resolveModule(fromFile, specifier, packages) {
|
|
27689
|
+
if (!specifier.startsWith(".")) return packages.get(specifier) ?? null;
|
|
27690
|
+
return resolveRelativeModule(fromFile, specifier);
|
|
27302
27691
|
}
|
|
27303
27692
|
function resolveRelativeModule(fromFile, specifier) {
|
|
27304
27693
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -27321,8 +27710,8 @@ function resolveRelativeModule(fromFile, specifier) {
|
|
|
27321
27710
|
}
|
|
27322
27711
|
return null;
|
|
27323
27712
|
}
|
|
27324
|
-
function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCache) {
|
|
27325
|
-
const target =
|
|
27713
|
+
function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCache, packages) {
|
|
27714
|
+
const target = resolveModule(filePath, specifier, packages);
|
|
27326
27715
|
if (!target || path18.relative(projectRoot, target).startsWith("..")) return null;
|
|
27327
27716
|
let targetFacts = exactCache.get(target);
|
|
27328
27717
|
if (targetFacts === void 0) {
|
|
@@ -27336,7 +27725,7 @@ function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCach
|
|
|
27336
27725
|
if (!targetFacts) return null;
|
|
27337
27726
|
if (!visited.has(target)) {
|
|
27338
27727
|
visited.add(target);
|
|
27339
|
-
chaseReexports(ts, targetFacts, target, projectRoot, visited, exactCache);
|
|
27728
|
+
chaseReexports(ts, targetFacts, target, projectRoot, visited, exactCache, packages);
|
|
27340
27729
|
}
|
|
27341
27730
|
return { path: target, facts: targetFacts };
|
|
27342
27731
|
}
|
|
@@ -27359,13 +27748,33 @@ function carryBody(facts, target, targetPath, local, exported, projectRoot) {
|
|
|
27359
27748
|
}
|
|
27360
27749
|
facts.bodies.set(exported, carried);
|
|
27361
27750
|
}
|
|
27362
|
-
function
|
|
27751
|
+
function carryAliasBody(facts, source, from, local, alias) {
|
|
27752
|
+
const sourceBodies = source.bodies.get(local) ?? [];
|
|
27753
|
+
if (!sourceBodies.length || sourceBodies.some((b) => b.nested)) return;
|
|
27754
|
+
const moduleScope = sourceBodies.filter((b) => b.container === void 0);
|
|
27755
|
+
if (!moduleScope.length) return;
|
|
27756
|
+
const score = source.complexity.get(local);
|
|
27757
|
+
if (score !== void 0) {
|
|
27758
|
+
facts.complexity.set(alias.exported, Math.max(facts.complexity.get(alias.exported) ?? 0, score));
|
|
27759
|
+
}
|
|
27760
|
+
const relocate2 = (site) => from !== void 0 ? { ...site, from: site.from ?? from } : site;
|
|
27761
|
+
const sites = facts.calls.get(alias.exported) ?? /* @__PURE__ */ new Map();
|
|
27762
|
+
const carried = facts.bodies.get(alias.exported) ?? [];
|
|
27763
|
+
for (const body of moduleScope) {
|
|
27764
|
+
const moved = body.sites.map(relocate2);
|
|
27765
|
+
for (const site of moved) sites.set(`${from ?? ""}|${alias.container ?? ""}|${JSON.stringify(site)}`, site);
|
|
27766
|
+
carried.push(alias.container !== void 0 ? { container: alias.container, sites: moved } : { sites: moved });
|
|
27767
|
+
}
|
|
27768
|
+
facts.calls.set(alias.exported, sites);
|
|
27769
|
+
facts.bodies.set(alias.exported, carried);
|
|
27770
|
+
}
|
|
27771
|
+
function chaseReexports(ts, facts, filePath, projectRoot, visited, exactCache, packages = NO_PACKAGES) {
|
|
27363
27772
|
for (const re of facts.namedReexports) {
|
|
27364
|
-
const target = reexportTarget(ts, filePath, re.from, projectRoot, visited, exactCache);
|
|
27773
|
+
const target = reexportTarget(ts, filePath, re.from, projectRoot, visited, exactCache, packages);
|
|
27365
27774
|
if (target) carryBody(facts, target.facts, target.path, re.local, re.exported, projectRoot);
|
|
27366
27775
|
}
|
|
27367
27776
|
for (const spec of facts.starExports) {
|
|
27368
|
-
const target = reexportTarget(ts, filePath, spec, projectRoot, visited, exactCache);
|
|
27777
|
+
const target = reexportTarget(ts, filePath, spec, projectRoot, visited, exactCache, packages);
|
|
27369
27778
|
if (!target) continue;
|
|
27370
27779
|
for (const name of target.facts.exported) {
|
|
27371
27780
|
facts.exported.add(name);
|
|
@@ -27373,6 +27782,150 @@ function chaseReexports(ts, facts, filePath, projectRoot, visited, exactCache) {
|
|
|
27373
27782
|
carryBody(facts, target.facts, target.path, name, name, projectRoot);
|
|
27374
27783
|
}
|
|
27375
27784
|
}
|
|
27785
|
+
for (const alias of facts.exportAliases) {
|
|
27786
|
+
const binding = facts.importBindings.get(alias.local);
|
|
27787
|
+
if (binding) {
|
|
27788
|
+
if (binding.namespace) continue;
|
|
27789
|
+
const target = reexportTarget(ts, filePath, binding.from, projectRoot, visited, exactCache, packages);
|
|
27790
|
+
if (!target) continue;
|
|
27791
|
+
const from = pathKey(path18.relative(projectRoot, target.path));
|
|
27792
|
+
carryAliasBody(facts, target.facts, from, binding.imported ?? alias.local, alias);
|
|
27793
|
+
} else if (alias.container !== void 0 || alias.local !== alias.exported) {
|
|
27794
|
+
carryAliasBody(facts, facts, void 0, alias.local, alias);
|
|
27795
|
+
}
|
|
27796
|
+
}
|
|
27797
|
+
}
|
|
27798
|
+
var BUILD_EXTENSIONS = [".d.mts", ".d.cts", ".d.ts", ".mjs", ".cjs", ".jsx", ".js"];
|
|
27799
|
+
var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
27800
|
+
var ENTRY_CONDITIONS = ["types", "import", "require", "default"];
|
|
27801
|
+
function isOwnFile(projectRoot, absolute) {
|
|
27802
|
+
const rel2 = path18.relative(projectRoot, absolute);
|
|
27803
|
+
if (rel2.startsWith("..") || path18.isAbsolute(rel2)) return false;
|
|
27804
|
+
return !pathKey(rel2).split("/").includes("node_modules");
|
|
27805
|
+
}
|
|
27806
|
+
function isFile(absolute) {
|
|
27807
|
+
try {
|
|
27808
|
+
return fs13.statSync(absolute).isFile();
|
|
27809
|
+
} catch {
|
|
27810
|
+
return false;
|
|
27811
|
+
}
|
|
27812
|
+
}
|
|
27813
|
+
function readJsonFile2(file, ts) {
|
|
27814
|
+
let text3;
|
|
27815
|
+
try {
|
|
27816
|
+
text3 = fs13.readFileSync(file, "utf8");
|
|
27817
|
+
} catch {
|
|
27818
|
+
return void 0;
|
|
27819
|
+
}
|
|
27820
|
+
try {
|
|
27821
|
+
return JSON.parse(text3);
|
|
27822
|
+
} catch {
|
|
27823
|
+
if (!ts) return void 0;
|
|
27824
|
+
const parsed = ts.parseConfigFileTextToJson(file, text3);
|
|
27825
|
+
return parsed.error ? void 0 : parsed.config;
|
|
27826
|
+
}
|
|
27827
|
+
}
|
|
27828
|
+
function conditionTargets(value) {
|
|
27829
|
+
if (typeof value === "string") return [value];
|
|
27830
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return [];
|
|
27831
|
+
const record = value;
|
|
27832
|
+
const keys = [...ENTRY_CONDITIONS.filter((k) => k in record), ...Object.keys(record).filter((k) => !ENTRY_CONDITIONS.includes(k))];
|
|
27833
|
+
return keys.flatMap((k) => conditionTargets(record[k]));
|
|
27834
|
+
}
|
|
27835
|
+
function packageEntries(pkg) {
|
|
27836
|
+
const entries = /* @__PURE__ */ new Map();
|
|
27837
|
+
const exportsField = pkg.exports;
|
|
27838
|
+
if (exportsField !== void 0 && exportsField !== null) {
|
|
27839
|
+
const isSubpathMap = typeof exportsField === "object" && !Array.isArray(exportsField) && Object.keys(exportsField).some((k) => k.startsWith("."));
|
|
27840
|
+
if (isSubpathMap) {
|
|
27841
|
+
for (const [subpath, value] of Object.entries(exportsField)) {
|
|
27842
|
+
if (!subpath.startsWith(".") || subpath.includes("*")) continue;
|
|
27843
|
+
const targets = conditionTargets(value);
|
|
27844
|
+
if (targets.length) entries.set(subpath, targets);
|
|
27845
|
+
}
|
|
27846
|
+
} else {
|
|
27847
|
+
const targets = conditionTargets(exportsField);
|
|
27848
|
+
if (targets.length) entries.set(".", targets);
|
|
27849
|
+
}
|
|
27850
|
+
return entries;
|
|
27851
|
+
}
|
|
27852
|
+
const fallbacks = ["module", "main", "types"].map((k) => pkg[k]).filter((v) => typeof v === "string");
|
|
27853
|
+
if (fallbacks.length) entries.set(".", fallbacks);
|
|
27854
|
+
return entries;
|
|
27855
|
+
}
|
|
27856
|
+
function entrySource(packageDir, target, compiler, projectRoot) {
|
|
27857
|
+
const absolute = path18.resolve(packageDir, target);
|
|
27858
|
+
if (!isOwnFile(projectRoot, absolute)) return null;
|
|
27859
|
+
const isDeclaration = /\.d\.[cm]?ts$/.test(absolute);
|
|
27860
|
+
if (!isDeclaration && languageOfSourcePath(absolute) && isFile(absolute) && !(compiler.outDir && !path18.relative(compiler.outDir, absolute).startsWith(".."))) {
|
|
27861
|
+
return absolute;
|
|
27862
|
+
}
|
|
27863
|
+
if (!compiler.outDir || !compiler.rootDir) return null;
|
|
27864
|
+
const inOut = path18.relative(compiler.outDir, absolute);
|
|
27865
|
+
if (inOut.startsWith("..") || path18.isAbsolute(inOut)) return null;
|
|
27866
|
+
const extension = BUILD_EXTENSIONS.find((ext) => inOut.endsWith(ext));
|
|
27867
|
+
const stem = extension ? inOut.slice(0, -extension.length) : inOut;
|
|
27868
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
27869
|
+
const candidate = path18.join(compiler.rootDir, stem + ext);
|
|
27870
|
+
if (isOwnFile(projectRoot, candidate) && isFile(candidate)) return candidate;
|
|
27871
|
+
}
|
|
27872
|
+
return null;
|
|
27873
|
+
}
|
|
27874
|
+
function readLocalPackages(projectRoot) {
|
|
27875
|
+
const out = /* @__PURE__ */ new Map();
|
|
27876
|
+
const ts = resolveTypeScript(projectRoot);
|
|
27877
|
+
const root = readJsonFile2(path18.join(projectRoot, "package.json"), null);
|
|
27878
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) return out;
|
|
27879
|
+
const rootPkg = root;
|
|
27880
|
+
const declared = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces && typeof rootPkg.workspaces === "object" && Array.isArray(rootPkg.workspaces.packages) ? rootPkg.workspaces.packages : [];
|
|
27881
|
+
const dirs = [projectRoot];
|
|
27882
|
+
for (const entry of declared) {
|
|
27883
|
+
if (typeof entry !== "string") continue;
|
|
27884
|
+
const key = pathKey(entry).replace(/\/+$/, "");
|
|
27885
|
+
if (!key || path18.isAbsolute(key)) continue;
|
|
27886
|
+
if (key.endsWith("/*")) {
|
|
27887
|
+
const base = path18.resolve(projectRoot, key.slice(0, -2));
|
|
27888
|
+
if (base.includes("*") || !isOwnFile(projectRoot, base)) continue;
|
|
27889
|
+
let children;
|
|
27890
|
+
try {
|
|
27891
|
+
children = fs13.readdirSync(base, { withFileTypes: true });
|
|
27892
|
+
} catch {
|
|
27893
|
+
continue;
|
|
27894
|
+
}
|
|
27895
|
+
for (const child of children.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
27896
|
+
if (child.isDirectory() && child.name !== "node_modules" && !child.name.startsWith(".")) {
|
|
27897
|
+
dirs.push(path18.join(base, child.name));
|
|
27898
|
+
}
|
|
27899
|
+
}
|
|
27900
|
+
} else if (!key.includes("*")) {
|
|
27901
|
+
const dir = path18.resolve(projectRoot, key);
|
|
27902
|
+
if (isOwnFile(projectRoot, dir)) dirs.push(dir);
|
|
27903
|
+
}
|
|
27904
|
+
}
|
|
27905
|
+
for (const dir of dirs) {
|
|
27906
|
+
const pkg = dir === projectRoot ? rootPkg : readJsonFile2(path18.join(dir, "package.json"), null);
|
|
27907
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) continue;
|
|
27908
|
+
const name = pkg.name;
|
|
27909
|
+
if (typeof name !== "string" || !name) continue;
|
|
27910
|
+
const tsconfig = readJsonFile2(path18.join(dir, "tsconfig.json"), ts);
|
|
27911
|
+
const options = tsconfig?.compilerOptions ?? {};
|
|
27912
|
+
const compiler = {
|
|
27913
|
+
outDir: typeof options.outDir === "string" ? path18.resolve(dir, options.outDir) : void 0,
|
|
27914
|
+
rootDir: typeof options.rootDir === "string" ? path18.resolve(dir, options.rootDir) : void 0
|
|
27915
|
+
};
|
|
27916
|
+
for (const [subpath, targets] of packageEntries(pkg)) {
|
|
27917
|
+
const specifier = subpath === "." ? name : `${name}/${subpath.replace(/^\.\//, "")}`;
|
|
27918
|
+
if (out.has(specifier)) continue;
|
|
27919
|
+
for (const target of targets) {
|
|
27920
|
+
const source = entrySource(dir, target, compiler, projectRoot);
|
|
27921
|
+
if (source) {
|
|
27922
|
+
out.set(specifier, source);
|
|
27923
|
+
break;
|
|
27924
|
+
}
|
|
27925
|
+
}
|
|
27926
|
+
}
|
|
27927
|
+
}
|
|
27928
|
+
return out;
|
|
27376
27929
|
}
|
|
27377
27930
|
function looksBinary(buffer) {
|
|
27378
27931
|
const probe = buffer.subarray(0, Math.min(buffer.length, 4096));
|
|
@@ -27430,6 +27983,9 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
27430
27983
|
const seen = /* @__PURE__ */ new Set();
|
|
27431
27984
|
const exactCache = /* @__PURE__ */ new Map();
|
|
27432
27985
|
const rootFiles = walkDeclaredRoots(sourceRoots, exclude, projectRoot);
|
|
27986
|
+
const localPackages = readLocalPackages(projectRoot);
|
|
27987
|
+
const packages = {};
|
|
27988
|
+
for (const [specifier, absolute] of localPackages) packages[specifier] = pathKey(path18.relative(projectRoot, absolute));
|
|
27433
27989
|
const declaredPaths = [];
|
|
27434
27990
|
for (const impl of implementations) {
|
|
27435
27991
|
declaredPaths.push(...implementationSourceFiles(impl));
|
|
@@ -27475,7 +28031,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
27475
28031
|
if (ts) {
|
|
27476
28032
|
try {
|
|
27477
28033
|
const facts = walkExact(ts, text3, absolute);
|
|
27478
|
-
chaseReexports(ts, facts, absolute, projectRoot, /* @__PURE__ */ new Set([absolute]), exactCache);
|
|
28034
|
+
chaseReexports(ts, facts, absolute, projectRoot, /* @__PURE__ */ new Set([absolute]), exactCache, localPackages);
|
|
27479
28035
|
analyzed = {
|
|
27480
28036
|
analysisGrade: "exact",
|
|
27481
28037
|
declaredNames: [...facts.declared],
|
|
@@ -27487,6 +28043,10 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
27487
28043
|
...facts.namedReexports.map(({ exported, local, from }) => ({ exported, local, from })),
|
|
27488
28044
|
...facts.starExports.map((from) => ({ exported: "*", local: "*", from }))
|
|
27489
28045
|
],
|
|
28046
|
+
exportAliases: facts.exportAliases.map((alias) => ({ ...alias })),
|
|
28047
|
+
returnTypes: Object.fromEntries(
|
|
28048
|
+
[...facts.returnTypes].filter((entry) => entry[1] !== null)
|
|
28049
|
+
),
|
|
27490
28050
|
functionComplexity: Object.fromEntries(facts.complexity),
|
|
27491
28051
|
functionCallSites: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v.values()]])),
|
|
27492
28052
|
functionBodies: Object.fromEntries(facts.bodies),
|
|
@@ -27516,7 +28076,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
27516
28076
|
}
|
|
27517
28077
|
files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
|
|
27518
28078
|
}
|
|
27519
|
-
return { files, projectRoot, rootFiles };
|
|
28079
|
+
return { files, projectRoot, rootFiles, packages };
|
|
27520
28080
|
}
|
|
27521
28081
|
var IMPORT_FROM_RE = /(?:^|;)[ \t]*import\s+(?!\()([^'";]*?)\s*from\s*['"]([^'"]+)['"]/gm;
|
|
27522
28082
|
var DYNAMIC_IMPORT_RE = /\{([^{}]*)\}\s*=\s*await\s+import\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
@@ -29483,8 +30043,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
29483
30043
|
const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
29484
30044
|
for (const step of steps) {
|
|
29485
30045
|
for (const par of parallelArms) {
|
|
29486
|
-
const
|
|
29487
|
-
if (
|
|
30046
|
+
const sep15 = par.sepByStep.get(step.stepNumber);
|
|
30047
|
+
if (sep15 !== void 0) lines.push(` and ${escapeLabel(sep15)}`);
|
|
29488
30048
|
}
|
|
29489
30049
|
switch (step.type) {
|
|
29490
30050
|
case "local":
|
|
@@ -30214,6 +30774,15 @@ function classifySource(world, sourceId, label7, sources) {
|
|
|
30214
30774
|
detail: `"${label7}" re-exports from "${sourceId}", which is neither a subsystem of this project, a member nor a declared external the scan read \u2014 a project re-exports only from its subsystems, members and externals of its family`
|
|
30215
30775
|
};
|
|
30216
30776
|
}
|
|
30777
|
+
function duplicateDetail(entries, index) {
|
|
30778
|
+
const e = entries[index];
|
|
30779
|
+
if (e.from !== void 0 || e.component !== void 0 || e.interface !== void 0 || e.typeDef !== void 0) return null;
|
|
30780
|
+
const name = e.as ?? e.id;
|
|
30781
|
+
if (name === void 0) return null;
|
|
30782
|
+
const other = entries.findIndex((x, i) => i !== index && (x.as ?? x.id) === name);
|
|
30783
|
+
if (other === -1) return null;
|
|
30784
|
+
return `has a duplicate entry "${name}": entry #${index + 1} names no source and no item, while entry #${other + 1} declares the same id \u2014 delete the copy (entry #${index + 1}), or give it a source and an item under an id of its own`;
|
|
30785
|
+
}
|
|
30217
30786
|
function bindOwnType(world, owner, e, problems) {
|
|
30218
30787
|
const type = (world.types.get(e.typeDef) ?? []).find((t) => !t.subsystem);
|
|
30219
30788
|
if (!type) {
|
|
@@ -30285,7 +30854,8 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
|
|
|
30285
30854
|
const owner = sources.namespace ? sources.namespace : system.name;
|
|
30286
30855
|
const problems = [];
|
|
30287
30856
|
const candidates = [];
|
|
30288
|
-
|
|
30857
|
+
const entries = system.publicInterfaces ?? [];
|
|
30858
|
+
entries.forEach((e, index) => {
|
|
30289
30859
|
const label7 = e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef ?? `#${index + 1}`;
|
|
30290
30860
|
const source = classifySource(world, sourceOf(world, e), label7, sources);
|
|
30291
30861
|
switch (source.kind) {
|
|
@@ -30311,7 +30881,7 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
|
|
|
30311
30881
|
return;
|
|
30312
30882
|
}
|
|
30313
30883
|
default:
|
|
30314
|
-
problems.push({ kind: "invalid", owner, publicName: label7, detail: source.detail });
|
|
30884
|
+
problems.push({ kind: "invalid", owner, publicName: label7, detail: duplicateDetail(entries, index) ?? source.detail });
|
|
30315
30885
|
}
|
|
30316
30886
|
});
|
|
30317
30887
|
return settle(owner, "project", candidates, problems);
|
|
@@ -30904,6 +31474,16 @@ function qualifiedTypeNames(typeStr) {
|
|
|
30904
31474
|
}
|
|
30905
31475
|
return out;
|
|
30906
31476
|
}
|
|
31477
|
+
function invariantTypeRef(ref) {
|
|
31478
|
+
const at = ref.lastIndexOf(".");
|
|
31479
|
+
return at > 0 && at < ref.length - 1 ? ref.slice(0, at) : void 0;
|
|
31480
|
+
}
|
|
31481
|
+
function mapInvariantRef(ref, map) {
|
|
31482
|
+
const typeRef2 = invariantTypeRef(ref);
|
|
31483
|
+
if (typeRef2 === void 0 || !typeRef2.includes("::")) return ref;
|
|
31484
|
+
const next = map("type", typeRef2);
|
|
31485
|
+
return next === typeRef2 ? ref : `${next}${ref.slice(typeRef2.length)}`;
|
|
31486
|
+
}
|
|
30907
31487
|
function rawReferences(kind, spec) {
|
|
30908
31488
|
const out = [];
|
|
30909
31489
|
const add2 = (position, value) => {
|
|
@@ -30926,6 +31506,7 @@ function rawReferences(kind, spec) {
|
|
|
30926
31506
|
for (const step of m.narrative) {
|
|
30927
31507
|
const source = step.auth?.from;
|
|
30928
31508
|
if (typeof source === "string" && source.startsWith(COMPONENT_AUTH_SOURCE)) add2("auth", source.slice(COMPONENT_AUTH_SOURCE.length));
|
|
31509
|
+
for (const ref of step.assertsInvariants ?? []) add2("type", invariantTypeRef(ref));
|
|
30929
31510
|
}
|
|
30930
31511
|
}
|
|
30931
31512
|
break;
|
|
@@ -31045,6 +31626,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
31045
31626
|
for (const m of list(doc.methods)) {
|
|
31046
31627
|
for (const step of list(m.narrative)) {
|
|
31047
31628
|
at(step, "targetComponent", "narrative");
|
|
31629
|
+
if (Array.isArray(step.assertsInvariants)) {
|
|
31630
|
+
step.assertsInvariants = step.assertsInvariants.map((ref) => typeof ref === "string" ? mapInvariantRef(ref, map) : ref);
|
|
31631
|
+
}
|
|
31048
31632
|
const auth = step.auth;
|
|
31049
31633
|
if (auth && typeof auth.from === "string" && auth.from.startsWith(COMPONENT_AUTH_SOURCE)) {
|
|
31050
31634
|
auth.from = `${COMPONENT_AUTH_SOURCE}${map("auth", auth.from.slice(COMPONENT_AUTH_SOURCE.length))}`;
|
|
@@ -31104,7 +31688,8 @@ function mapRawReferences(kind, spec, map) {
|
|
|
31104
31688
|
return call && next !== call.compId ? `${next}.${call.methodName}` : entry;
|
|
31105
31689
|
})
|
|
31106
31690
|
} : {},
|
|
31107
|
-
narrative: m.narrative.map((
|
|
31691
|
+
narrative: m.narrative.map((authored) => {
|
|
31692
|
+
const step = authored.assertsInvariants ? { ...authored, assertsInvariants: authored.assertsInvariants.map((ref) => mapInvariantRef(ref, map)) } : authored;
|
|
31108
31693
|
const source = step.auth?.from;
|
|
31109
31694
|
if (typeof source !== "string" || !source.startsWith(COMPONENT_AUTH_SOURCE)) return step;
|
|
31110
31695
|
const auth = step.auth;
|
|
@@ -33200,12 +33785,12 @@ var SpecWorkspace = class {
|
|
|
33200
33785
|
return this.relativizeSpec("interface", stored);
|
|
33201
33786
|
}
|
|
33202
33787
|
prepareImplementationForWrite(spec, carry = true) {
|
|
33203
|
-
const
|
|
33788
|
+
const relative20 = this.relativizeSpec("implementation", spec, carry);
|
|
33204
33789
|
const member = this.getSubprojectPrefix(spec.id);
|
|
33205
33790
|
const memberRoot = member ? this.resolveSubprojectForNamespace(member) : null;
|
|
33206
|
-
if (memberRoot) return childRelativeFilePaths(
|
|
33791
|
+
if (memberRoot) return childRelativeFilePaths(relative20, this.rootDir, memberRoot);
|
|
33207
33792
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
33208
|
-
return partDir ? partRelativeFilePaths(
|
|
33793
|
+
return partDir ? partRelativeFilePaths(relative20, this.rootDir, partDir) : relative20;
|
|
33209
33794
|
}
|
|
33210
33795
|
/**
|
|
33211
33796
|
* A type written in its stored form — every type position canonical (one
|
|
@@ -35756,7 +36341,7 @@ function resolveGit(external, config) {
|
|
|
35756
36341
|
const declared = gitDeclarationOf(config, external);
|
|
35757
36342
|
if (!declared) return { ...external, problem: "git producer unavailable: its declaration names no repository" };
|
|
35758
36343
|
try {
|
|
35759
|
-
const commit2 = external.role === "member" && declared.commit ? declared.commit :
|
|
36344
|
+
const commit2 = external.role === "member" && declared.commit ? declared.commit : resolve3(declared.url, declared.ref);
|
|
35760
36345
|
const directory = fetch(declared.url, commit2, declared.dir);
|
|
35761
36346
|
return { ...external, directory: path23.resolve(directory), commit: commit2, problem: void 0 };
|
|
35762
36347
|
} catch (e) {
|
|
@@ -36185,7 +36770,7 @@ function scaffoldPart(alias, memberDir, contained) {
|
|
|
36185
36770
|
}
|
|
36186
36771
|
function createGitMember(alias, source, description, as) {
|
|
36187
36772
|
const url = source.git;
|
|
36188
|
-
const commit2 = source.commit ??
|
|
36773
|
+
const commit2 = source.commit ?? resolve3(url, source.ref);
|
|
36189
36774
|
const dir = fetch(url, commit2, source.dir);
|
|
36190
36775
|
const kind = holdsProjectContent(dir) ? "project" : "part";
|
|
36191
36776
|
if (as !== void 0 && as !== kind) {
|
|
@@ -37145,7 +37730,7 @@ function advanceMember(alias, ref, commit2, dryRun) {
|
|
|
37145
37730
|
const url = member.source.git;
|
|
37146
37731
|
const from = member.source.commit ?? "";
|
|
37147
37732
|
const followed = commit2 === void 0 ? ref ?? member.source.ref : void 0;
|
|
37148
|
-
const to = commit2 ??
|
|
37733
|
+
const to = commit2 ?? resolve3(url, followed);
|
|
37149
37734
|
const after = specContents(fetch(url, to, member.source.dir));
|
|
37150
37735
|
const before = specContents(from !== "" ? fetch(url, from, member.source.dir) : null);
|
|
37151
37736
|
const added = [...after.keys()].filter((f) => !before.has(f)).sort();
|
|
@@ -38686,17 +39271,17 @@ function memberHop(agentId) {
|
|
|
38686
39271
|
const alias = agentId.slice(0, at);
|
|
38687
39272
|
const member = graph().nodes.find((n) => n.parent === "" && n.mountAlias === alias);
|
|
38688
39273
|
if (!member) return null;
|
|
38689
|
-
const
|
|
38690
|
-
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative:
|
|
39274
|
+
const relative20 = path29.relative(getProjectRoot(), member.directory).split(path29.sep).join("/");
|
|
39275
|
+
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative: relative20 };
|
|
38691
39276
|
}
|
|
38692
|
-
function throughMount(brief, alias,
|
|
38693
|
-
const under = (p) => path29.posix.join(
|
|
39277
|
+
function throughMount(brief, alias, relative20) {
|
|
39278
|
+
const under = (p) => path29.posix.join(relative20, p);
|
|
38694
39279
|
return {
|
|
38695
39280
|
...brief,
|
|
38696
39281
|
agentId: `${alias}::${brief.agentId}`,
|
|
38697
39282
|
ownedPaths: brief.ownedPaths.map(under),
|
|
38698
39283
|
...brief.readPaths ? { readPaths: brief.readPaths.map(under) } : {},
|
|
38699
|
-
root: brief.root ? under(brief.root) :
|
|
39284
|
+
root: brief.root ? under(brief.root) : relative20
|
|
38700
39285
|
};
|
|
38701
39286
|
}
|
|
38702
39287
|
function loadRegistry() {
|
|
@@ -38975,19 +39560,19 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
|
|
|
38975
39560
|
const backupDir = path34.join(root, BACKUP_DIR, timestampSlug());
|
|
38976
39561
|
let backedUp = false;
|
|
38977
39562
|
for (const rootRel of roots) {
|
|
38978
|
-
const
|
|
38979
|
-
const stagedSpecDir = path34.join(stagingDir,
|
|
39563
|
+
const relative20 = rootRel === "." ? "" : rootRel;
|
|
39564
|
+
const stagedSpecDir = path34.join(stagingDir, relative20, ".wai");
|
|
38980
39565
|
if (!fs20.existsSync(stagedSpecDir)) continue;
|
|
38981
|
-
const liveSpecDir = liveSpecDirFor(path34.join(root,
|
|
39566
|
+
const liveSpecDir = liveSpecDirFor(path34.join(root, relative20));
|
|
38982
39567
|
if (backupExisting && liveSpecDir && fs20.existsSync(liveSpecDir)) {
|
|
38983
|
-
const backupTarget = path34.join(backupDir,
|
|
39568
|
+
const backupTarget = path34.join(backupDir, relative20, path34.basename(liveSpecDir));
|
|
38984
39569
|
fs20.mkdirSync(path34.dirname(backupTarget), { recursive: true });
|
|
38985
39570
|
fs20.renameSync(liveSpecDir, backupTarget);
|
|
38986
39571
|
backedUp = true;
|
|
38987
39572
|
} else if (liveSpecDir && fs20.existsSync(liveSpecDir)) {
|
|
38988
39573
|
fs20.rmSync(liveSpecDir, { recursive: true, force: true });
|
|
38989
39574
|
}
|
|
38990
|
-
const target = path34.join(root,
|
|
39575
|
+
const target = path34.join(root, relative20, ".wai");
|
|
38991
39576
|
fs20.mkdirSync(path34.dirname(target), { recursive: true });
|
|
38992
39577
|
if (fs20.existsSync(target)) fs20.rmSync(target, { recursive: true, force: true });
|
|
38993
39578
|
fs20.renameSync(stagedSpecDir, target);
|
|
@@ -39266,7 +39851,7 @@ function repairTypeSpellings(apply3) {
|
|
|
39266
39851
|
const key = `${kind}:${specId}`;
|
|
39267
39852
|
let repair = repairs.get(key);
|
|
39268
39853
|
if (!repair) {
|
|
39269
|
-
repair = { specId, kind, rewritten: [], proposals: [], authorNeeded: [] };
|
|
39854
|
+
repair = { specId, kind, rewritten: [], proposals: [], enumProposals: [], authorNeeded: [] };
|
|
39270
39855
|
repairs.set(key, repair);
|
|
39271
39856
|
}
|
|
39272
39857
|
return repair;
|
|
@@ -39281,7 +39866,9 @@ function repairTypeSpellings(apply3) {
|
|
|
39281
39866
|
if (problem.specId === void 0 || problem.kind === void 0 || !own(problem.specId)) continue;
|
|
39282
39867
|
const repair = repairOf(problem.specId, problem.kind);
|
|
39283
39868
|
const proposal = typeProblemIntProposal(problem);
|
|
39869
|
+
const enumProposal = proposal ? null : typeProblemEnumProposal(problem);
|
|
39284
39870
|
if (proposal) repair.proposals.push(proposal);
|
|
39871
|
+
else if (enumProposal) repair.enumProposals.push(enumProposal);
|
|
39285
39872
|
else repair.authorNeeded.push(problem);
|
|
39286
39873
|
}
|
|
39287
39874
|
const planned = [...repairs.values()];
|
|
@@ -39428,7 +40015,7 @@ ${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
|
39428
40015
|
return quiet;
|
|
39429
40016
|
}
|
|
39430
40017
|
}
|
|
39431
|
-
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in
|
|
40018
|
+
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in this release (members' composition subjects, `composition`; code conformance moved beside the claim)";
|
|
39432
40019
|
function memberNotes(approvals) {
|
|
39433
40020
|
const own = approvals.find((a) => a.key === "");
|
|
39434
40021
|
const direct = approvals.filter((a) => a.parent === "" && a.as !== "part");
|
|
@@ -39510,16 +40097,16 @@ var GLOBAL_GUIDE_BODY = `## wairon \u2014 Spec-Driven Development (optional)
|
|
|
39510
40097
|
If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore it. wairon does not orchestrate sessions \u2014 it equips yours.
|
|
39511
40098
|
|
|
39512
40099
|
### In SDD Projects:
|
|
39513
|
-
- **Source of Truth**: All architecture lives in the spec tree under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 L5 Narrative).
|
|
40100
|
+
- **Source of Truth**: All architecture lives in the spec tree under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 L5 Narrative). Agent files under \`.claude/agents/\` are an opt-in generated view (\`rules.materializeAgentFiles\`) \u2014 never edit them; agents are served as live briefs.
|
|
39514
40101
|
- **Validation**: Conformance checks (stereotype rules, cycle checks, reference integrity) are run via the \`sdd_validate_tree\` MCP tool.
|
|
39515
40102
|
- **Operating Rules**:
|
|
39516
40103
|
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
39517
40104
|
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
39518
40105
|
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
39519
40106
|
4. **Delegation**: Delegate implementation via the \`sdd-delegate\` skill \u2014 live agent briefs (\`sdd_get_agent_brief\` MCP tool / \`wairon-agent://\` resource) are composed per call and always current; no session restart. Generated agent files are an optional materialized view of the same topology. User-owned per-agent guidance may live in \`.wai/agents/<agent-id>.md\` (folded into every brief; scaffold via \`wairon agent customize <id>\`).
|
|
39520
|
-
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
40107
|
+
5. **Design First, then approval**: Complete the spec and pass \`sdd_validate_tree\`, then ask the human to approve it with \`wairon lock\` before writing code. Approval is the lock record (\`.wai/lock.json\`), not a spec's \`status\` \u2014 the lock does not rewrite statuses; \`sdd_get_status\` reports the approval state.
|
|
39521
40108
|
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
39522
|
-
7. **Members & References**: A project may
|
|
40109
|
+
7. **Members & References**: A project may declare **members** in its \`.wai/project.yaml\` \`members\` (create one with \`sdd_add_member\`). A **part** (the default) stores some of this project's subsystems in another folder or repository: local ids, this project's lock. A **project** member is an independent boundary with its own spec tree and lock, designed from its own root. Reference what another project exports as \`alias::name\` (the alias is a member or a declared external, the name a public name of its L0 export table); an id without \`::\` is local. A leading \`::\`, \`super::\`, member paths and an L1 subsystem carrying \`projectPath\` are deprecated: they still resolve for one release, are reported, and \`wairon doctor --fix\` rewrites them.`;
|
|
39523
40110
|
var LOCAL_GUIDE_BODY = `## Wairon \u2014 Spec-Driven Development (you are operating inside it)
|
|
39524
40111
|
|
|
39525
40112
|
This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System \u2192 L1 Subsystem \u2192 L2 Component \u2192 L3 Interface \u2192 L4 Implementation \u2192 Narrative); agent topology and code are derived from it.
|
|
@@ -39536,7 +40123,8 @@ This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System
|
|
|
39536
40123
|
- **\`alias::name\`**: An id without \`::\` is local to the project that writes it. Anything another project provides is referenced as \`alias::name\` \u2014 the alias is one of your members or declared \`externals\`, the name a public name in that project's L0 export table. A reference to something it does not export is reported (\`EXTERNAL_NOT_EXPORTED\`).
|
|
39537
40124
|
- **Deprecated forms** (they still resolve for one release, are reported, and \`wairon doctor --fix\` rewrites them): a leading \`::\` (\`::shared::error-type\`), \`super::\` (\`super::sibling_comp\`), member paths (\`billing::invoice::invoice_portal\`), and an L1 subsystem carrying \`projectPath\` (\`DEPRECATED_MOUNT_FORM\`).
|
|
39538
40125
|
- **Do not run the \`wairon\` CLI**: Use \`sdd_validate_tree\` and \`sdd_get_status\` instead of CLI commands.
|
|
39539
|
-
- **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to
|
|
40126
|
+
- **Handoff to implementation**: Once design is complete and validates cleanly, tell the human: *"The specs are complete and validate. Please run \`wairon lock\` to approve them, and commit \`.wai/lock.json\`."* The lock records the approval; it does not rewrite spec files or their \`status\`. No session restart is needed after the lock \u2014 delegate implementation right away via the \`sdd-delegate\` skill.
|
|
40127
|
+
- **Approval, not status**: a design is ready to implement when it is APPROVED \u2014 \`sdd_get_status\` reports the approval state (approved, or which specs changed since), and \`wairon lock-check\` gives the same verdict in CI. A spec's \`status\` (draft/design/complete) is authoring readiness only; never wait for it to become \`complete\`.
|
|
39540
40128
|
- **To implement code**: Delegate via the \`sdd-delegate\` skill: fetch the component's live brief with the \`sdd_get_agent_brief\` MCP tool (or the \`wairon-agent://\` resource) and spawn a subagent from it. Briefs are composed per call from the current spec tree, so they are always current \u2014 never wait for a restart. Implementations must match L3 interfaces and L5 narratives exactly. Generated agent files under \`.claude/agents/\` are an optional materialized view of the same topology \u2014 the live briefs are canonical.
|
|
39541
40129
|
|
|
39542
40130
|
### Rules (enforced by \`sdd_validate_tree\`)
|
|
@@ -40005,7 +40593,7 @@ function getStatusReport(options = {}, decor) {
|
|
|
40005
40593
|
}
|
|
40006
40594
|
const own = options.approvals?.find((a) => a.key === "");
|
|
40007
40595
|
const hasMembers = (options.approvals ?? []).some((a) => a.key !== "" && a.as !== "part");
|
|
40008
|
-
if (own && (own.state !== "never" || hasMembers)) output += `${mark.layer("system", "Approval:")} this project is ${own.state}${own.upgraded ? " (locked under
|
|
40596
|
+
if (own && (own.state !== "never" || hasMembers)) output += `${mark.layer("system", "Approval:")} this project is ${own.state}${own.upgraded ? " (locked under an earlier gate identity \u2014 re-lock once)" : ""}
|
|
40009
40597
|
`;
|
|
40010
40598
|
return { text: output, failed: false };
|
|
40011
40599
|
}
|
|
@@ -41187,7 +41775,7 @@ var KINDS_AT = {
|
|
|
41187
41775
|
auth: ["implementation"],
|
|
41188
41776
|
subsystem: ["component", "type"],
|
|
41189
41777
|
group: ["type"],
|
|
41190
|
-
type: ["interface", "type"]
|
|
41778
|
+
type: ["interface", "type", "implementation"]
|
|
41191
41779
|
};
|
|
41192
41780
|
function kindOf(ref) {
|
|
41193
41781
|
const kinds = KINDS_AT[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -42267,7 +42855,7 @@ function emptyPlan(family, request) {
|
|
|
42267
42855
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
42268
42856
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
42269
42857
|
}
|
|
42270
|
-
var
|
|
42858
|
+
var posix5 = (p) => p.split(path43.sep).join("/");
|
|
42271
42859
|
var refuse = (plan6, code, project, detail) => {
|
|
42272
42860
|
plan6.refusals.push({ code, project, detail });
|
|
42273
42861
|
};
|
|
@@ -42345,7 +42933,7 @@ function attachEdits(plan6, family, bound2, dir, alias, bindingConfig) {
|
|
|
42345
42933
|
edit(plan6, key, "id", `id: ${id} (declared${approved !== void 0 ? ", the id its lock approved" : ""})`, { root: dir, call: "setId", args: [id] });
|
|
42346
42934
|
}
|
|
42347
42935
|
if (!attached) plan6.notes.push(`${dir} has no project.yaml of its own \u2014 \`wairon doctor --fix\` writes one declaring "${alias}" as its id`);
|
|
42348
|
-
const relPath =
|
|
42936
|
+
const relPath = posix5(path43.relative(familyNode(family, bound2).directory, dir));
|
|
42349
42937
|
const declaration2 = { path: relPath, ...plan6.request.description !== void 0 ? { description: plan6.request.description } : {} };
|
|
42350
42938
|
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] });
|
|
42351
42939
|
for (const requirement of requiredPolicies(bindingConfig)) {
|
|
@@ -42392,8 +42980,8 @@ function planDetach(family, bound2, request) {
|
|
|
42392
42980
|
if (plan6.request.widen) planWidening(plan6, member, narrow);
|
|
42393
42981
|
if (plan6.refusals.length > 0) return plan6;
|
|
42394
42982
|
const boundDir = familyNode(family, bound2).directory;
|
|
42395
|
-
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ??
|
|
42396
|
-
const declared = { ...held, path: memberLocationOf(held) ??
|
|
42983
|
+
const held = memberDeclarationOf(configAt3(boundDir)?.members?.[alias] ?? posix5(path43.relative(boundDir, member.directory)));
|
|
42984
|
+
const declared = { ...held, path: memberLocationOf(held) ?? posix5(path43.relative(boundDir, member.directory)) };
|
|
42397
42985
|
detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward);
|
|
42398
42986
|
return plan6;
|
|
42399
42987
|
}
|
|
@@ -42433,7 +43021,7 @@ function planWidening(plan6, member, narrow) {
|
|
|
42433
43021
|
function detachEdits(plan6, family, bound2, member, alias, declared, consumers, outward) {
|
|
42434
43022
|
const boundDir = familyNode(family, bound2).directory;
|
|
42435
43023
|
const relocation = plan6.request.relocation;
|
|
42436
|
-
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path:
|
|
43024
|
+
const sourceTo = (from, producer) => relocation ? { hosted: producer.id ?? producer.mountAlias ?? producer.namespace } : { path: posix5(path43.relative(from, producer.directory)) };
|
|
42437
43025
|
const where = (source) => source.hosted !== void 0 ? `hosted record ${source.hosted}` : source.path;
|
|
42438
43026
|
edit(plan6, bound2, "member", `members: ${alias} removed (${declared.path} ${relocation ? `moves to ${relocation.to}` : "stays as it is"})`, { root: boundDir, call: "removeMember", args: [alias] });
|
|
42439
43027
|
const boundSource = relocation ? sourceTo(boundDir, member) : { path: declared.path };
|
|
@@ -42507,7 +43095,7 @@ function adoptEdits(plan6, family, bound2, alias, external, dir, at = dir) {
|
|
|
42507
43095
|
edit(plan6, bound2, "pin", `pin ${alias} removed (the family supplies it now)`, { root: boundDir, call: "unpin", args: [alias] });
|
|
42508
43096
|
edit(plan6, bound2, "external", `externals: ${alias} removed`, { root: boundDir, call: "removeExternal", args: [alias] });
|
|
42509
43097
|
const declaration2 = {
|
|
42510
|
-
path:
|
|
43098
|
+
path: posix5(path43.relative(boundDir, at)),
|
|
42511
43099
|
...external.description !== void 0 ? { description: external.description } : {},
|
|
42512
43100
|
...external.use && external.use.length > 0 ? { use: external.use } : {}
|
|
42513
43101
|
};
|
|
@@ -42618,7 +43206,7 @@ var KINDS_AT2 = {
|
|
|
42618
43206
|
auth: ["implementation"],
|
|
42619
43207
|
subsystem: ["component", "type"],
|
|
42620
43208
|
group: ["type"],
|
|
42621
|
-
type: ["interface", "type"]
|
|
43209
|
+
type: ["interface", "type", "implementation"]
|
|
42622
43210
|
};
|
|
42623
43211
|
function kindOf2(ref) {
|
|
42624
43212
|
const kinds = KINDS_AT2[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -42819,7 +43407,7 @@ function emptyPlan3(family, request) {
|
|
|
42819
43407
|
const whole = !(reach2 !== null && !reach2.parentReach);
|
|
42820
43408
|
return { request, familyRoot: familyNode(family, "").directory, whole, edits: [], refusals: [], changes: [], relock: [], notes: [] };
|
|
42821
43409
|
}
|
|
42822
|
-
var
|
|
43410
|
+
var posix6 = (p) => p.split(path45.sep).join("/");
|
|
42823
43411
|
var label6 = (key) => key === "" ? "the top project" : `"${key}"`;
|
|
42824
43412
|
var refuse3 = (plan6, code, project, detail) => {
|
|
42825
43413
|
plan6.refusals.push({ code, project, detail });
|
|
@@ -42850,7 +43438,7 @@ var KINDS_AT3 = {
|
|
|
42850
43438
|
auth: ["implementation"],
|
|
42851
43439
|
subsystem: ["component", "type"],
|
|
42852
43440
|
group: ["type"],
|
|
42853
|
-
type: ["interface", "type"]
|
|
43441
|
+
type: ["interface", "type", "implementation"]
|
|
42854
43442
|
};
|
|
42855
43443
|
function kindOf3(ref) {
|
|
42856
43444
|
const kinds = KINDS_AT3[ref.position] ?? ["component", "interface", "implementation", "type", "subsystem"];
|
|
@@ -43029,7 +43617,7 @@ function respellConsumer(plan6, family, c, own) {
|
|
|
43029
43617
|
function repointConsumer(plan6, c, boundNode2) {
|
|
43030
43618
|
const declared = configAt5(c.node.directory)?.externals?.[c.alias];
|
|
43031
43619
|
const project = boundNode2.id !== void 0 && boundNode2.id !== c.alias ? boundNode2.id : null;
|
|
43032
|
-
const source = declared?.source ? { path:
|
|
43620
|
+
const source = declared?.source ? { path: posix6(path45.relative(c.node.directory, boundNode2.directory)) } : null;
|
|
43033
43621
|
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, project, source] });
|
|
43034
43622
|
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.`);
|
|
43035
43623
|
return c;
|
|
@@ -43065,8 +43653,8 @@ function planExternalize(family, bound2, request) {
|
|
|
43065
43653
|
plan6,
|
|
43066
43654
|
bound2,
|
|
43067
43655
|
"move",
|
|
43068
|
-
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${
|
|
43069
|
-
{ root: node.directory, call: "externalizeSubsystem", args: [id,
|
|
43656
|
+
`externalize ${id}: its specs into ${joined ? `the part "${joined.alias}"` : `a part at ${posix6(path45.relative(node.directory, dir))}, declared as "${id}"`} \u2014 a storage move; no reference, export or pin changes`,
|
|
43657
|
+
{ root: node.directory, call: "externalizeSubsystem", args: [id, posix6(path45.relative(node.directory, dir))] }
|
|
43070
43658
|
);
|
|
43071
43659
|
plan6.notes.push("Source code is not moved: move the subsystem's code yourself if it should live beside its specs.");
|
|
43072
43660
|
return plan6;
|
|
@@ -43075,7 +43663,7 @@ function planExternalize(family, bound2, request) {
|
|
|
43075
43663
|
crossingsBack(plan6, family, bound2, subKey);
|
|
43076
43664
|
checkSiblings(plan6, family, bound2, id, subKey, table);
|
|
43077
43665
|
if (plan6.refusals.length > 0) return plan6;
|
|
43078
|
-
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${
|
|
43666
|
+
edit3(plan6, bound2, "move", `externalize ${id} as a project: its specs into a member at ${posix6(path45.relative(node.directory, dir))}, declared as "${id}", then promoted`, { root: node.directory, call: "externalizeSubsystem", args: [id, posix6(path45.relative(node.directory, dir)), "project"] });
|
|
43079
43667
|
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: [[]] });
|
|
43080
43668
|
plan6.notes.push("Source code is not moved: move the subsystem's code into the member yourself.");
|
|
43081
43669
|
return plan6;
|
|
@@ -44204,7 +44792,7 @@ function errText(message) {
|
|
|
44204
44792
|
function errMessage(e) {
|
|
44205
44793
|
return errText(e instanceof Error ? e.message : String(e));
|
|
44206
44794
|
}
|
|
44207
|
-
var TYPE_REF_GRAMMAR = (what) => `${what}, in the neutral type grammar: a primitive (string, int, float, bool, bytes, date, datetime, duration, any; void only as a whole returns), a defined type id ("billing.Invoice", "alias::name"), list<T>, set<T>, map<K, V> (K is string, int or an enum), T? (T or no value), a union of NAMED types ("Invoice | Receipt"), and on a returns only, async T ("async void"). Every named type must resolve. TypeScript spellings are accepted and respelled \u2014 "Invoice[]" \u2192 list<Invoice>, "Invoice | null" \u2192 Invoice?, boolean \u2192 bool, Record<string, V> \u2192 map<string, V>, Promise<T> \u2192 async T \u2014 and the answer lists each respelling. Refused, naming the replacement: number ("int or float?"), inline object or function types (name a value-object or a signature type), string-literal unions (name an enum), and unions mixing in a primitive or a collection.`;
|
|
44795
|
+
var TYPE_REF_GRAMMAR = (what) => `${what}, in the neutral type grammar: a primitive (string, int, float, bool, bytes, date, datetime, duration, any; void only as a whole returns), a defined type id ("billing.Invoice", "alias::name"), list<T>, set<T>, map<K, V> (K is string, int or an enum), T? (T or no value), a union of NAMED types ("Invoice | Receipt"), and on a returns only, async T ("async void") and result<T, E> (completes with T or fails with E; "async result<void, E>"). Every named type must resolve. TypeScript and Rust spellings are accepted and respelled \u2014 "Invoice[]" \u2192 list<Invoice>, "Invoice | null" \u2192 Invoice?, boolean \u2192 bool, Record<string, V> \u2192 map<string, V>, Promise<T> \u2192 async T, Result<(), E> \u2192 result<void, E> \u2014 and the answer lists each respelling. Refused, naming the replacement: number ("int or float?"), inline object or function types (name a value-object or a signature type), string-literal unions (name an enum), and unions mixing in a primitive or a collection.`;
|
|
44208
44796
|
function structured(content, data) {
|
|
44209
44797
|
return { content: [{ type: "text", text: content }], structuredContent: data };
|
|
44210
44798
|
}
|
|
@@ -44304,6 +44892,28 @@ function renderChangeReport(report3) {
|
|
|
44304
44892
|
return lines.join("\n");
|
|
44305
44893
|
}
|
|
44306
44894
|
var SPEC_KINDS = ["system", "subsystem", "component", "interface", "implementation", "type"];
|
|
44895
|
+
function specKindsHolding(id) {
|
|
44896
|
+
const system = loadSystemSpec();
|
|
44897
|
+
const held = {
|
|
44898
|
+
system: !!system && (id === "system" || id === system.name),
|
|
44899
|
+
subsystem: !!loadSubsystemSpec(id),
|
|
44900
|
+
component: !!loadComponentSpec(id),
|
|
44901
|
+
interface: !!loadInterfaceSpec(id),
|
|
44902
|
+
implementation: !!loadImplementationSpec(id),
|
|
44903
|
+
type: !!loadTypeSpec(id)
|
|
44904
|
+
};
|
|
44905
|
+
return SPEC_KINDS.filter((k) => held[k]);
|
|
44906
|
+
}
|
|
44907
|
+
var PORTAL_TYPE_TRANSPORT = {
|
|
44908
|
+
HTTP_API: "HTTP",
|
|
44909
|
+
gRPC: "gRPC",
|
|
44910
|
+
GraphQL: "GraphQL",
|
|
44911
|
+
MessageBus: "MessageBus",
|
|
44912
|
+
NamedPipe: "NamedPipe",
|
|
44913
|
+
IPC: "IPC",
|
|
44914
|
+
CLI: "CLI",
|
|
44915
|
+
Custom: void 0
|
|
44916
|
+
};
|
|
44307
44917
|
var STATUS_VALUES = ["draft", "design", "complete"];
|
|
44308
44918
|
var testsToRevisitOutput = import_zod12.z.array(import_zod12.z.object({
|
|
44309
44919
|
method: import_zod12.z.string().describe("The contract method this write changed or deleted."),
|
|
@@ -44446,7 +45056,7 @@ var memberCreationOutput = {
|
|
|
44446
45056
|
profile: import_zod12.z.string().optional()
|
|
44447
45057
|
})).describe("Requirements no installed version satisfies: nothing was written for them, and the next family run reports each as POLICY_NOT_ADOPTED."),
|
|
44448
45058
|
projectType: import_zod12.z.string().optional().describe("The profile written as the new member's projectType, when exactly one adopted requirement names one."),
|
|
44449
|
-
as: import_zod12.z.string().describe("part | project: what was created (
|
|
45059
|
+
as: import_zod12.z.string().describe("part | project: what was created (a part by default)."),
|
|
44450
45060
|
storage: import_zod12.z.string().describe("contained | path | git: where its files live."),
|
|
44451
45061
|
commit: import_zod12.z.string().optional().describe("git: the commit the member was pinned at."),
|
|
44452
45062
|
...staleServerOutput
|
|
@@ -45028,7 +45638,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45028
45638
|
server,
|
|
45029
45639
|
"sdd_add_member",
|
|
45030
45640
|
{
|
|
45031
|
-
description: "Create a member of the bound project at `source`, in the one members grammar: `services/scheduler` (contained), `../admin` (a sibling checkout), `git@host:acme/payments.git` (a git repository, pinned at its default branch head unless `#<commit>` is given). By default a PART
|
|
45641
|
+
description: "Create a member of the bound project at `source`, in the one members grammar: `services/scheduler` (contained), `../admin` (a sibling checkout), `git@host:acme/payments.git` (a git repository, pinned at its default branch head unless `#<commit>` is given). By default a PART: a piece of this project stored in another folder or repository, whose subsystems are this project's own (local ids, the ordinary subsystem rules, this project's lock); it gets only a specs folder, plus a configuration naming this project when it is not contained. With `as: project` an independent PROJECT instead: its project content is created \u2014 a project.yaml declaring the alias as its id, its L0, and the bound project's required packs (composition.requirePolicies) once \u2014 and it is reached only as `alias::name` through its L0 exports. What a member is follows from that content. A git member is not scaffolded; the repository's content decides what it is. Boundaries are earned: create a part unless the piece needs its own team, release, approval or public surface; sdd_promote_member makes one later. Idempotent \u2014 the same call again writes nothing; a different member under the alias is refused.",
|
|
45032
45642
|
inputSchema: {
|
|
45033
45643
|
alias: import_zod12.z.string().describe("The member's alias ([a-z0-9-_]+); a project's new id"),
|
|
45034
45644
|
source: import_zod12.z.string().describe("A contained path, a `../` sibling path, or a git URL (optionally `#<commit>`)"),
|
|
@@ -45098,7 +45708,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45098
45708
|
server,
|
|
45099
45709
|
"sdd_externalize_subsystem",
|
|
45100
45710
|
{
|
|
45101
|
-
description: "Move an internal subsystem out to the path. By default
|
|
45711
|
+
description: "Move an internal subsystem out to the path. By default into a PART \u2014 a storage move: the subsystem's specs move to that directory (a new part, or the existing part there), and nothing else changes: no reference, export, external or lock entry, the same verdict. With `as: project`, into a member PROJECT instead, family-wide and all-or-nothing \u2014 the move followed by a promote: what crosses the new boundary exported and imported, references respelled `alias::name`, the new member's external for this project pinned, and every other family project's references checked to still resolve (a reference that would need a component made public refuses the plan). Source code is not moved. dryRun answers the plan and writes nothing; run it first and show the plan. Never locks: the answer names the projects to re-lock.",
|
|
45102
45712
|
inputSchema: {
|
|
45103
45713
|
subsystem: import_zod12.z.string().describe("The subsystem to move out"),
|
|
45104
45714
|
path: import_zod12.z.string().describe("The part's root, relative to the bound project (a new directory, or an existing part's)"),
|
|
@@ -45112,7 +45722,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45112
45722
|
server,
|
|
45113
45723
|
"sdd_promote_member",
|
|
45114
45724
|
{
|
|
45115
|
-
description: "Promote a part of the bound project to an independent project in place
|
|
45725
|
+
description: "Promote a part of the bound project to an independent project in place \u2014 its files stay where they are; it gets an id, an L0 exporting exactly what the rest of the project uses of it, its references across the new boundary respelled `alias::name`, the parent declared as its external, and pins on both sides. Refused (each reason listed) for a part fetched from git, a trusted link that would cross the new boundary, a used component its subsystem does not publish, or an id already taken. All-or-nothing; dryRun answers the plan and writes nothing \u2014 run it first and show the plan. Never locks: the answer names the project to re-lock and the new one to lock.",
|
|
45116
45726
|
inputSchema: {
|
|
45117
45727
|
alias: import_zod12.z.string().describe("The part's alias"),
|
|
45118
45728
|
id: import_zod12.z.string().optional().describe("The new project's id (default: the alias)"),
|
|
@@ -45125,7 +45735,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45125
45735
|
server,
|
|
45126
45736
|
"sdd_demote_member",
|
|
45127
45737
|
{
|
|
45128
|
-
description: "Demote a project member of the bound project to a part in place
|
|
45738
|
+
description: "Demote a project member of the bound project to a part in place \u2014 sdd_promote_member's inverse: its files stay where they are; its own metadata goes to the homes the destination names (as sdd_internalize_member sends it), its L0, lock and pins end, its references across the old boundary become local ids, and its configuration is reduced to naming its parent. Refused while another family project consumes it (a part has no exports \u2014 each consumer listed), for a member fetched from git, or for internalize's reasons. All-or-nothing; dryRun answers the plan and writes nothing \u2014 run it first and show the plan. Never locks.",
|
|
45129
45739
|
inputSchema: {
|
|
45130
45740
|
alias: import_zod12.z.string().describe("The project member's alias"),
|
|
45131
45741
|
destination: import_zod12.z.object({
|
|
@@ -45468,7 +46078,7 @@ ${renderChangeReport(report3)}`,
|
|
|
45468
46078
|
interface: import_zod12.z.string().describe('The L3 interface ID (e.g. "ibilling-gateway")'),
|
|
45469
46079
|
endpoints: import_zod12.z.array(import_zod12.z.object({
|
|
45470
46080
|
method: import_zod12.z.string().describe("The NAME of the interface method to bind (not the HTTP verb)"),
|
|
45471
|
-
transport: import_zod12.z.enum(["HTTP", "gRPC", "GraphQL", "MessageBus", "NamedPipe", "IPC", "CLI", "Custom"]).describe("Wire protocol; must
|
|
46081
|
+
transport: import_zod12.z.enum(["HTTP", "HTTP_API", "gRPC", "GraphQL", "MessageBus", "NamedPipe", "IPC", "CLI", "Custom"]).describe("Wire protocol; must be the one the Portal's portalType implies. Either vocabulary is accepted: the endpoint's (HTTP) or the portalType's (HTTP_API), stored as HTTP; any other transport is refused, naming the one the portalType implies"),
|
|
45472
46082
|
httpMethod: import_zod12.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]).optional().describe("HTTP: verb"),
|
|
45473
46083
|
path: import_zod12.z.string().optional().describe('HTTP: route path, e.g. "/v1/checkout"'),
|
|
45474
46084
|
service: import_zod12.z.string().optional().describe("gRPC: service name"),
|
|
@@ -45491,8 +46101,10 @@ ${renderChangeReport(report3)}`,
|
|
|
45491
46101
|
try {
|
|
45492
46102
|
const intf = loadInterfaceSpec(interfaceId);
|
|
45493
46103
|
if (!intf) return errText(`Interface "${interfaceId}" does not exist.`);
|
|
45494
|
-
const
|
|
45495
|
-
|
|
46104
|
+
const owner = intf.component ? loadComponentSpec(intf.component) : null;
|
|
46105
|
+
const implied = owner?.componentType === "Portal" && owner.portalType ? PORTAL_TYPE_TRANSPORT[owner.portalType] : void 0;
|
|
46106
|
+
const buildRaw = (e, transport) => {
|
|
46107
|
+
switch (transport) {
|
|
45496
46108
|
case "HTTP":
|
|
45497
46109
|
return { transport: "HTTP", method: e.httpMethod, path: e.path };
|
|
45498
46110
|
case "gRPC":
|
|
@@ -45516,13 +46128,19 @@ ${renderChangeReport(report3)}`,
|
|
|
45516
46128
|
for (const e of endpoints) {
|
|
45517
46129
|
const m = intf.methods.find((x) => x.name === e.method);
|
|
45518
46130
|
if (!m) return errText(`Method "${e.method}" not found on interface "${interfaceId}". Define it via sdd_define_interface first.`);
|
|
45519
|
-
const
|
|
46131
|
+
const transport = e.transport === "HTTP_API" ? "HTTP" : e.transport;
|
|
46132
|
+
if (implied && transport !== implied) {
|
|
46133
|
+
return errText(
|
|
46134
|
+
`Method "${e.method}": transport "${e.transport}" does not match ${owner.id}'s portalType ${owner.portalType}, which implies transport "${implied}". Bind it as "${implied}", or change the Portal's portalType.`
|
|
46135
|
+
);
|
|
46136
|
+
}
|
|
46137
|
+
const parsed = EndpointSchema.safeParse(buildRaw(e, transport));
|
|
45520
46138
|
if (!parsed.success) {
|
|
45521
46139
|
const detail = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
45522
|
-
return errText(`Invalid ${
|
|
46140
|
+
return errText(`Invalid ${transport} endpoint for method "${e.method}": ${detail}`);
|
|
45523
46141
|
}
|
|
45524
46142
|
methods.push({ name: e.method, endpoint: parsed.data });
|
|
45525
|
-
bound2.push(`${e.method}\u2192${
|
|
46143
|
+
bound2.push(`${e.method}\u2192${transport}`);
|
|
45526
46144
|
}
|
|
45527
46145
|
const report3 = updateSpecGated("interface", interfaceId, { methods });
|
|
45528
46146
|
return structured(
|
|
@@ -45783,14 +46401,24 @@ ${renderChangeReport(report3)}`,
|
|
|
45783
46401
|
{
|
|
45784
46402
|
description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back. A contract's methods come back in their STORED form \u2014 a method that takes its signature from a source carries its signatureFrom, not the params the loader resolves into it \u2014 so the answer can be re-authored from as it is; the resolved params, returns and text of each such method come back as a derived, read-only "resolvedSignatures" marker. The structured content carries the same answer with the derived markers KEPT SEPARATE from the stored spec ({kind, id, spec, partialResult?, variantGuidance?, resolvedSignatures?}), so nothing derived can be mistaken for something stored; the text block folds them in as it always has.`,
|
|
45785
46403
|
inputSchema: {
|
|
45786
|
-
kind: import_zod12.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The kind of specification"),
|
|
46404
|
+
kind: import_zod12.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).optional().describe("The kind of specification. Omit it to infer the kind from the id: the read is refused, naming the candidate kinds, when the id names specs at more than one level (an interface and an implementation sharing an id, say), or at none"),
|
|
45787
46405
|
id: import_zod12.z.string().describe('The identifier of the spec to fetch (the L0 system spec is a singleton \u2014 pass the system name or "system")'),
|
|
45788
46406
|
methods: import_zod12.z.array(import_zod12.z.string().min(1)).optional().describe("Return only these methods by name; every other field of the spec comes back unchanged. Only an interface, an implementation or a type declares methods \u2014 asking for one elsewhere is refused, as is a name the spec does not declare (the answer names the ones it does). Omit it for the whole spec.")
|
|
45789
46407
|
},
|
|
45790
46408
|
outputSchema: getSpecOutput
|
|
45791
46409
|
},
|
|
45792
|
-
({ kind, id, methods }) => {
|
|
46410
|
+
({ kind: askedKind, id, methods }) => {
|
|
45793
46411
|
try {
|
|
46412
|
+
let kind;
|
|
46413
|
+
if (askedKind) {
|
|
46414
|
+
kind = askedKind;
|
|
46415
|
+
} else {
|
|
46416
|
+
const holders = specKindsHolding(id);
|
|
46417
|
+
if (holders.length !== 1) {
|
|
46418
|
+
return errText(holders.length === 0 ? `No spec of any kind has the ID "${id}".` : `The ID "${id}" names specs of more than one kind (${holders.join(", ")}). Pass "kind" to choose one.`);
|
|
46419
|
+
}
|
|
46420
|
+
kind = holders[0];
|
|
46421
|
+
}
|
|
45794
46422
|
let result = null;
|
|
45795
46423
|
let partial2 = null;
|
|
45796
46424
|
switch (kind) {
|
|
@@ -46343,6 +46971,7 @@ function loadProjectConfig3() {
|
|
|
46343
46971
|
keyIn,
|
|
46344
46972
|
landReference,
|
|
46345
46973
|
languageOfSourcePath,
|
|
46974
|
+
lineEndingFor,
|
|
46346
46975
|
listDirectChainedSubprojects,
|
|
46347
46976
|
listExternals,
|
|
46348
46977
|
listFiles,
|
|
@@ -46462,6 +47091,7 @@ function loadProjectConfig3() {
|
|
|
46462
47091
|
resolveSubsystemExports,
|
|
46463
47092
|
resolveVariantGuidance,
|
|
46464
47093
|
retireSpecialists,
|
|
47094
|
+
returnTypeOf,
|
|
46465
47095
|
rewriteReferences,
|
|
46466
47096
|
runWithHostedLookup,
|
|
46467
47097
|
runWithProjectBinding,
|
|
@@ -46500,6 +47130,7 @@ function loadProjectConfig3() {
|
|
|
46500
47130
|
typeIsMany,
|
|
46501
47131
|
typeMatchesRef,
|
|
46502
47132
|
typeNamedRefs,
|
|
47133
|
+
typeProblemEnumProposal,
|
|
46503
47134
|
typeProblemIntProposal,
|
|
46504
47135
|
typeSourceFiles,
|
|
46505
47136
|
typeSpellingFacts,
|
|
@@ -46513,6 +47144,7 @@ function loadProjectConfig3() {
|
|
|
46513
47144
|
validateProject,
|
|
46514
47145
|
validateProjectConfig,
|
|
46515
47146
|
validateRegistry,
|
|
47147
|
+
withLineEndings,
|
|
46516
47148
|
withPack,
|
|
46517
47149
|
writeFile,
|
|
46518
47150
|
writeFileIfChanged,
|