@wairon/cli 5.1.1-dev.95 → 5.1.1-dev.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +2378 -503
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +2055 -392
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +6 -5
- package/dist/templates/skills/sdd-delegate.md +2 -2
- package/dist/templates/skills/sdd-implement.md +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2959,7 +2959,6 @@ var require_dist = __commonJS({
|
|
|
2959
2959
|
"# languages:",
|
|
2960
2960
|
"# rust:",
|
|
2961
2961
|
"# unsupportedFlow: {}",
|
|
2962
|
-
"# foreignBuiltins: []",
|
|
2963
2962
|
"languages: {}",
|
|
2964
2963
|
"",
|
|
2965
2964
|
"# Reusable, versioned architecture patterns:",
|
|
@@ -3186,12 +3185,12 @@ var require_dist = __commonJS({
|
|
|
3186
3185
|
const suggestedFileName = `${sealed.name}-${sealed.version}.wpack`;
|
|
3187
3186
|
return { archive: archiveBytes, info, suggestedFileName };
|
|
3188
3187
|
}
|
|
3189
|
-
function inspectArchive(
|
|
3190
|
-
const entries = listEntries(
|
|
3188
|
+
function inspectArchive(archive) {
|
|
3189
|
+
const entries = listEntries(archive);
|
|
3191
3190
|
if (!entries.some((entry) => entry.path === ENVELOPE_FILENAME)) {
|
|
3192
3191
|
throw new Error("not a .wpack archive (missing wairon-pack.yaml envelope)");
|
|
3193
3192
|
}
|
|
3194
|
-
const envelopeBytes = inflateEntry(
|
|
3193
|
+
const envelopeBytes = inflateEntry(archive, ENVELOPE_FILENAME);
|
|
3195
3194
|
const manifest = parseManifest(decode(envelopeBytes));
|
|
3196
3195
|
const compatible = checkCompatibility(manifest, SDK_VERSION);
|
|
3197
3196
|
const fileEntries = entries.filter((entry) => entry.kind === "file");
|
|
@@ -3209,13 +3208,13 @@ var require_dist = __commonJS({
|
|
|
3209
3208
|
if (manifest.minWaironVersion !== void 0) info.minWaironVersion = manifest.minWaironVersion;
|
|
3210
3209
|
return info;
|
|
3211
3210
|
}
|
|
3212
|
-
function extractPack(
|
|
3211
|
+
function extractPack(archive, destDir, limits) {
|
|
3213
3212
|
const effectiveLimits = limits === void 0 ? defaultLimits() : limits;
|
|
3214
|
-
const entries = listEntries(
|
|
3213
|
+
const entries = listEntries(archive);
|
|
3215
3214
|
const plan6 = planExtraction(entries, effectiveLimits);
|
|
3216
3215
|
const files = [];
|
|
3217
3216
|
for (const approvedPath of plan6.paths) {
|
|
3218
|
-
const contents = inflateEntry(
|
|
3217
|
+
const contents = inflateEntry(archive, approvedPath);
|
|
3219
3218
|
files.push({ path: approvedPath, contents });
|
|
3220
3219
|
}
|
|
3221
3220
|
const envelope = files.find((file) => file.path === ENVELOPE_FILENAME);
|
|
@@ -3272,28 +3271,28 @@ var require_dist = __commonJS({
|
|
|
3272
3271
|
const suggestedFileName = `${slugify(projectName)}.waitree`;
|
|
3273
3272
|
return { archive: archiveBytes, manifest: sealed, suggestedFileName, fileCount: files.length };
|
|
3274
3273
|
}
|
|
3275
|
-
function inspectTreeArchive3(
|
|
3276
|
-
const entries = listEntries(
|
|
3274
|
+
function inspectTreeArchive3(archive) {
|
|
3275
|
+
const entries = listEntries(archive);
|
|
3277
3276
|
if (!entries.some((entry) => entry.path === TREE_ENVELOPE_FILENAME)) {
|
|
3278
3277
|
throw new Error("not a .waitree archive (missing wairon-tree.yaml envelope)");
|
|
3279
3278
|
}
|
|
3280
|
-
const envelopeBytes = inflateEntry(
|
|
3279
|
+
const envelopeBytes = inflateEntry(archive, TREE_ENVELOPE_FILENAME);
|
|
3281
3280
|
const manifest = parseTreeManifest(decode(envelopeBytes));
|
|
3282
3281
|
const compatible = checkTreeCompatibility(manifest, SDK_VERSION);
|
|
3283
3282
|
const fileEntries = entries.filter((entry) => entry.kind === "file");
|
|
3284
3283
|
const totalUncompressedBytes = entries.reduce((sum, entry) => sum + entry.uncompressedSize, 0);
|
|
3285
3284
|
return { manifest, entryCount: fileEntries.length, totalUncompressedBytes, compatible };
|
|
3286
3285
|
}
|
|
3287
|
-
function extractTreeArchive3(
|
|
3286
|
+
function extractTreeArchive3(archive, destDir, limits, refuseExecutableEntries) {
|
|
3288
3287
|
const effectiveLimits = limits === void 0 ? defaultTreeLimits() : limits;
|
|
3289
|
-
const entries = listEntries(
|
|
3288
|
+
const entries = listEntries(archive);
|
|
3290
3289
|
const plan6 = planTreeExtraction(entries, effectiveLimits);
|
|
3291
3290
|
if (refuseExecutableEntries === true) {
|
|
3292
3291
|
assertPortableTreePaths(plan6.paths);
|
|
3293
3292
|
}
|
|
3294
3293
|
const files = [];
|
|
3295
3294
|
for (const approvedPath of plan6.paths) {
|
|
3296
|
-
const contents = inflateEntry(
|
|
3295
|
+
const contents = inflateEntry(archive, approvedPath);
|
|
3297
3296
|
files.push({ path: approvedPath, contents });
|
|
3298
3297
|
}
|
|
3299
3298
|
const envelope = files.find((file) => file.path === TREE_ENVELOPE_FILENAME);
|
|
@@ -3326,9 +3325,9 @@ var require_dist = __commonJS({
|
|
|
3326
3325
|
}
|
|
3327
3326
|
function readInnerIdentity(files, manifest) {
|
|
3328
3327
|
const innerPath = manifest.kind === "code" ? "package.json" : manifest.entry;
|
|
3329
|
-
const
|
|
3330
|
-
if (!
|
|
3331
|
-
const parsed = manifest.kind === "code" ? JSON.parse(decode(
|
|
3328
|
+
const inner2 = files.find((file) => file.path === innerPath);
|
|
3329
|
+
if (!inner2) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
|
|
3330
|
+
const parsed = manifest.kind === "code" ? JSON.parse(decode(inner2.contents)) : (0, import_js_yaml3.load)(decode(inner2.contents));
|
|
3332
3331
|
const name = parsed?.name;
|
|
3333
3332
|
const version = parsed?.version;
|
|
3334
3333
|
if (typeof name !== "string" || typeof version !== "string") {
|
|
@@ -3394,7 +3393,6 @@ __export(src_exports, {
|
|
|
3394
3393
|
AgentTemplateSchema: () => AgentTemplateSchema,
|
|
3395
3394
|
BACKEND_COMMANDS: () => BACKEND_COMMANDS,
|
|
3396
3395
|
BUILTIN_PROFILES: () => BUILTIN_PROFILES,
|
|
3397
|
-
BUILTIN_TYPES: () => BUILTIN_TYPES,
|
|
3398
3396
|
BoundaryItemSchema: () => BoundaryItemSchema,
|
|
3399
3397
|
BuiltinTargetConfigSchema: () => BuiltinTargetConfigSchema,
|
|
3400
3398
|
BuiltinTargetSchema: () => BuiltinTargetSchema,
|
|
@@ -3428,6 +3426,7 @@ __export(src_exports, {
|
|
|
3428
3426
|
DurabilitySchema: () => DurabilitySchema,
|
|
3429
3427
|
EXTERNAL_ALIAS_RE: () => EXTERNAL_ALIAS_RE,
|
|
3430
3428
|
EndpointSchema: () => EndpointSchema,
|
|
3429
|
+
EnumValueSchema: () => EnumValueSchema,
|
|
3431
3430
|
EventBindingSchema: () => EventBindingSchema,
|
|
3432
3431
|
ExtDataSchema: () => ExtDataSchema,
|
|
3433
3432
|
ExternalDeclarationSchema: () => ExternalDeclarationSchema,
|
|
@@ -3442,6 +3441,7 @@ __export(src_exports, {
|
|
|
3442
3441
|
GeminiExporter: () => GeminiExporter,
|
|
3443
3442
|
GroupSpecSchema: () => GroupSpecSchema,
|
|
3444
3443
|
GuaranteeSchema: () => GuaranteeSchema,
|
|
3444
|
+
HOLDABLE_PRIMITIVES: () => HOLDABLE_PRIMITIVES,
|
|
3445
3445
|
HttpMethodSchema: () => HttpMethodSchema,
|
|
3446
3446
|
ImplementationSpecSchema: () => ImplementationSpecSchema,
|
|
3447
3447
|
InterfaceSpecSchema: () => InterfaceSpecSchema,
|
|
@@ -3461,6 +3461,7 @@ __export(src_exports, {
|
|
|
3461
3461
|
NarrativeStepTypeSchema: () => NarrativeStepTypeSchema,
|
|
3462
3462
|
OutputTargetSchema: () => OutputTargetSchema,
|
|
3463
3463
|
PATTERN_TYPES: () => PATTERN_TYPES,
|
|
3464
|
+
PRIMITIVE_TYPES: () => PRIMITIVE_TYPES,
|
|
3464
3465
|
PROJECT_ID_RE: () => PROJECT_ID_RE,
|
|
3465
3466
|
PROJECT_KINDS: () => PROJECT_KINDS,
|
|
3466
3467
|
PUBLIC_NAME_RE: () => PUBLIC_NAME_RE,
|
|
@@ -3548,6 +3549,7 @@ __export(src_exports, {
|
|
|
3548
3549
|
buildRuleContext: () => buildRuleContext,
|
|
3549
3550
|
callSitesOf: () => callSitesOf,
|
|
3550
3551
|
canonicalTypeRef: () => canonicalTypeRef,
|
|
3552
|
+
canonicalTypeText: () => canonicalTypeText,
|
|
3551
3553
|
captureApprovedSpecs: () => captureApprovedSpecs,
|
|
3552
3554
|
checkDeclarativePack: () => checkDeclarativePack,
|
|
3553
3555
|
clearLoaderIssues: () => clearLoaderIssues,
|
|
@@ -3592,6 +3594,7 @@ __export(src_exports, {
|
|
|
3592
3594
|
detectDomainCandidates: () => detectDomainCandidates,
|
|
3593
3595
|
diagnoseProjectPacks: () => diagnoseProjectPacks,
|
|
3594
3596
|
diagramSetIndex: () => diagramSetIndex,
|
|
3597
|
+
dialectOf: () => dialectOf,
|
|
3595
3598
|
diffAgainstApproval: () => diffAgainstApproval,
|
|
3596
3599
|
diffSize: () => diffSize,
|
|
3597
3600
|
discoverPacks: () => discoverPacks,
|
|
@@ -3599,6 +3602,7 @@ __export(src_exports, {
|
|
|
3599
3602
|
effectiveDetail: () => effectiveDetail,
|
|
3600
3603
|
effectiveProjectId: () => effectiveProjectId,
|
|
3601
3604
|
emptyExtensions: () => emptyExtensions,
|
|
3605
|
+
emptyTypeSpellingFacts: () => emptyTypeSpellingFacts,
|
|
3602
3606
|
ensureDir: () => ensureDir,
|
|
3603
3607
|
ensureProjectInitialized: () => ensureProjectInitialized,
|
|
3604
3608
|
excerptParent: () => excerpt,
|
|
@@ -3645,6 +3649,7 @@ __export(src_exports, {
|
|
|
3645
3649
|
importSpecTree: () => importSpecTree,
|
|
3646
3650
|
injectGuide: () => injectGuide,
|
|
3647
3651
|
installPackFromDirectory: () => installPackFromDirectory,
|
|
3652
|
+
interfaceCanonicalTypes: () => interfaceCanonicalTypes,
|
|
3648
3653
|
interfaceGenericParameters: () => interfaceGenericParameters,
|
|
3649
3654
|
internalizeMember: () => internalizeMember,
|
|
3650
3655
|
isDraftSubsystem: () => isDraftSubsystem,
|
|
@@ -3656,9 +3661,11 @@ __export(src_exports, {
|
|
|
3656
3661
|
isProjectInitialized: () => isProjectInitialized,
|
|
3657
3662
|
isProvidedBy: () => isProvidedBy,
|
|
3658
3663
|
isRetired: () => isRetired,
|
|
3664
|
+
isTypeVocabulary: () => isTypeVocabulary,
|
|
3659
3665
|
judgesCode: () => judgesCode,
|
|
3660
3666
|
keyIn: () => keyIn,
|
|
3661
3667
|
landReference: () => landReference,
|
|
3668
|
+
languageOfSourcePath: () => languageOfSourcePath,
|
|
3662
3669
|
listDirectChainedSubprojects: () => listDirectChainedSubprojects,
|
|
3663
3670
|
listFiles: () => listFiles,
|
|
3664
3671
|
listFilesRecursive: () => listFilesRecursive,
|
|
@@ -3701,6 +3708,7 @@ __export(src_exports, {
|
|
|
3701
3708
|
moveMethods: () => moveMethods,
|
|
3702
3709
|
moveMountToMembers: () => moveMountToMembers,
|
|
3703
3710
|
nameKey: () => nameKey,
|
|
3711
|
+
namesAnInteger: () => namesAnInteger,
|
|
3704
3712
|
narrativeStepForeignFields: () => narrativeStepForeignFields,
|
|
3705
3713
|
normalizeReferences: () => normalizeReferences2,
|
|
3706
3714
|
ownerOf: () => ownerOf,
|
|
@@ -3712,6 +3720,8 @@ __export(src_exports, {
|
|
|
3712
3720
|
parseDeclarativePack: () => parseDeclarativePack,
|
|
3713
3721
|
parseDeclaredCall: () => parseDeclaredCall,
|
|
3714
3722
|
parseMemberSource: () => parseMemberSource,
|
|
3723
|
+
parseTypeExpression: () => parseTypeExpression,
|
|
3724
|
+
parseTypePosition: () => parseTypePosition,
|
|
3715
3725
|
parseYaml: () => parseYaml,
|
|
3716
3726
|
passesIntentFloor: () => passesIntentFloor,
|
|
3717
3727
|
pathExists: () => pathExists,
|
|
@@ -3725,6 +3735,7 @@ __export(src_exports, {
|
|
|
3725
3735
|
provisionProject: () => provisionProject,
|
|
3726
3736
|
qualifiedTypeId: () => qualifiedTypeId,
|
|
3727
3737
|
rangeProblem: () => rangeProblem,
|
|
3738
|
+
readCodeAnnotation: () => readCodeAnnotation,
|
|
3728
3739
|
readFileOrNull: () => readFileOrNull,
|
|
3729
3740
|
readJsonFile: () => readJsonFile,
|
|
3730
3741
|
readLockRecord: () => readLockRecord,
|
|
@@ -3747,6 +3758,7 @@ __export(src_exports, {
|
|
|
3747
3758
|
renderDiagram: () => renderDiagram,
|
|
3748
3759
|
repairForeignStepFields: () => repairForeignStepFields,
|
|
3749
3760
|
repairSignatures: () => repairSignatures,
|
|
3761
|
+
repairTypeSpellings: () => repairTypeSpellings,
|
|
3750
3762
|
repointExternal: () => repointExternal,
|
|
3751
3763
|
requiredPolicies: () => requiredPolicies,
|
|
3752
3764
|
resolveAgentTopology: () => resolveAgentTopology,
|
|
@@ -3758,7 +3770,7 @@ __export(src_exports, {
|
|
|
3758
3770
|
resolveImport: () => resolveImport,
|
|
3759
3771
|
resolveInstalledPack: () => resolveInstalledPack,
|
|
3760
3772
|
resolveProjectExports: () => resolveProjectExports,
|
|
3761
|
-
resolveSignatures: () =>
|
|
3773
|
+
resolveSignatures: () => resolveSignatures,
|
|
3762
3774
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
3763
3775
|
resolveSubsystemExports: () => resolveSubsystemExports,
|
|
3764
3776
|
resolveVariantGuidance: () => resolveVariantGuidance,
|
|
@@ -3794,9 +3806,15 @@ __export(src_exports, {
|
|
|
3794
3806
|
technologyTokens: () => technologyTokens,
|
|
3795
3807
|
toMarkdown: () => toMarkdown,
|
|
3796
3808
|
typeBindingOf: () => typeBindingOf,
|
|
3809
|
+
typeCanonicalTypes: () => typeCanonicalTypes,
|
|
3810
|
+
typeDialectFor: () => typeDialectFor,
|
|
3797
3811
|
typeGenericParameters: () => typeGenericParameters,
|
|
3812
|
+
typeIsMany: () => typeIsMany,
|
|
3798
3813
|
typeMatchesRef: () => typeMatchesRef,
|
|
3814
|
+
typeNamedRefs: () => typeNamedRefs,
|
|
3815
|
+
typeProblemIntProposal: () => typeProblemIntProposal,
|
|
3799
3816
|
typeSourceFiles: () => typeSourceFiles,
|
|
3817
|
+
typeSpellingFacts: () => typeSpellingFacts,
|
|
3800
3818
|
uninstallPack: () => uninstallPack,
|
|
3801
3819
|
updateMember: () => updateMember,
|
|
3802
3820
|
updateSpec: () => updateSpec,
|
|
@@ -3811,7 +3829,8 @@ __export(src_exports, {
|
|
|
3811
3829
|
writeReachDenials: () => writeReachDenials,
|
|
3812
3830
|
writeReachPermits: () => writeReachPermits,
|
|
3813
3831
|
writeRootGuideDelegator: () => writeRootGuideDelegator,
|
|
3814
|
-
writeYamlFile: () => writeYamlFile
|
|
3832
|
+
writeYamlFile: () => writeYamlFile,
|
|
3833
|
+
writtenTypeRefs: () => writtenTypeRefs
|
|
3815
3834
|
});
|
|
3816
3835
|
module.exports = __toCommonJS(src_exports);
|
|
3817
3836
|
|
|
@@ -4260,6 +4279,14 @@ var AgentBriefSchema = import_zod2.z.object({
|
|
|
4260
4279
|
instructions: import_zod2.z.string(),
|
|
4261
4280
|
/** Rendered variant guidance, also folded into instructions */
|
|
4262
4281
|
variantGuidance: import_zod2.z.string().optional(),
|
|
4282
|
+
/**
|
|
4283
|
+
* How the neutral types of the contracts this agent implements are spelled
|
|
4284
|
+
* in its language: the write table (type_dialect.mappingLines) of the dialect
|
|
4285
|
+
* for the language its implementations are written in, also folded into
|
|
4286
|
+
* instructions. Absent when no dialect is shipped for that language or the
|
|
4287
|
+
* agent implements nothing.
|
|
4288
|
+
*/
|
|
4289
|
+
typeMapping: import_zod2.z.array(import_zod2.z.string()).optional(),
|
|
4263
4290
|
/**
|
|
4264
4291
|
* Set only on a brief for a member's agent (an id qualified `<alias>::`): the
|
|
4265
4292
|
* member's directory relative to the root the brief was asked from. The
|
|
@@ -5272,14 +5299,16 @@ function locateScalar(tokens, path49) {
|
|
|
5272
5299
|
const base = Math.min(...region.map((t) => t.col));
|
|
5273
5300
|
const last = k === path49.length - 1;
|
|
5274
5301
|
let at;
|
|
5275
|
-
|
|
5302
|
+
const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
|
|
5303
|
+
if (items.length === 0) {
|
|
5276
5304
|
at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
|
|
5277
5305
|
if (at < 0) return `no block key "${segment}" at ${path49.slice(0, k).join(".") || "the document root"}`;
|
|
5278
5306
|
if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
|
|
5279
5307
|
} else {
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5308
|
+
if (!/^\d+$/.test(segment)) return `"${segment}" is not a sequence index at ${path49.slice(0, k).join(".") || "the document root"}`;
|
|
5309
|
+
const index = Number(segment);
|
|
5310
|
+
if (index >= items.length) return `no block sequence item ${segment} at ${path49.slice(0, k).join(".")}`;
|
|
5311
|
+
at = items[index].i;
|
|
5283
5312
|
if (last) {
|
|
5284
5313
|
const value = region[at + 1];
|
|
5285
5314
|
return value && value.kind === "scalar" && value.line === region[at].line ? value : `item ${segment} holds no inline scalar`;
|
|
@@ -5751,7 +5780,7 @@ function registryOver(store, root) {
|
|
|
5751
5780
|
const at = typeof entry.at === "string" ? entry.at : void 0;
|
|
5752
5781
|
const covers = Array.isArray(entry.covers) ? entry.covers.filter((u) => typeof u === "string") : void 0;
|
|
5753
5782
|
const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
|
|
5754
|
-
const base = ["rules", "conformance", "carried", g, "findings", f];
|
|
5783
|
+
const base = ["rules", "conformance", "carried", String(g), "findings", String(f)];
|
|
5755
5784
|
const named2 = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
|
|
5756
5785
|
const change = (path49, field, from, to) => {
|
|
5757
5786
|
if (from === to) return;
|
|
@@ -5760,7 +5789,7 @@ function registryOver(store, root) {
|
|
|
5760
5789
|
};
|
|
5761
5790
|
change(["spec"], "spec", entry.spec, next.spec);
|
|
5762
5791
|
if (at !== void 0) change(["at"], "at", at, next.at);
|
|
5763
|
-
(covers ?? []).forEach((unit, u) => change(["covers", u], "covers", unit, next.covers[u]));
|
|
5792
|
+
(covers ?? []).forEach((unit, u) => change(["covers", String(u)], "covers", unit, next.covers[u]));
|
|
5764
5793
|
});
|
|
5765
5794
|
});
|
|
5766
5795
|
store.rewriteScalars(edits, dryRun);
|
|
@@ -7087,11 +7116,10 @@ var GuaranteeSchema = import_zod8.z.string().min(1);
|
|
|
7087
7116
|
var MethodParamSchema = import_zod8.z.object({
|
|
7088
7117
|
name: import_zod8.z.string(),
|
|
7089
7118
|
/**
|
|
7090
|
-
* A
|
|
7091
|
-
*
|
|
7092
|
-
* `
|
|
7093
|
-
*
|
|
7094
|
-
* src/models/type-references.ts.
|
|
7119
|
+
* A type expression in the neutral type grammar (src/models/type-grammar.ts):
|
|
7120
|
+
* a primitive, a named type (`billing.Invoice`, `alias::name`), `list<T>`,
|
|
7121
|
+
* `set<T>`, `map<K, V>`, `T?`, a union of named types. Stored canonical; an
|
|
7122
|
+
* alias (`Invoice[]`, `Invoice | null`, `boolean`) is normalised on write.
|
|
7095
7123
|
*/
|
|
7096
7124
|
type: import_zod8.z.string(),
|
|
7097
7125
|
description: import_zod8.z.string().optional(),
|
|
@@ -7110,8 +7138,9 @@ var MethodSignatureSchema = import_zod8.z.object({
|
|
|
7110
7138
|
description: import_zod8.z.string(),
|
|
7111
7139
|
signature: import_zod8.z.string().optional(),
|
|
7112
7140
|
// e.g. "save(key: string, data: Buffer): Promise<void>"
|
|
7113
|
-
//
|
|
7114
|
-
//
|
|
7141
|
+
// A type expression in the neutral grammar, at the returns position — the only
|
|
7142
|
+
// one `async T` and `void` may stand in: "async void", "Invoice?". Stored
|
|
7143
|
+
// canonical; see src/models/type-grammar.ts.
|
|
7115
7144
|
returns: import_zod8.z.string().optional(),
|
|
7116
7145
|
/** Structured parameters (authoritative for type checking when present). */
|
|
7117
7146
|
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
@@ -7133,11 +7162,16 @@ var MethodSignatureSchema = import_zod8.z.object({
|
|
|
7133
7162
|
*/
|
|
7134
7163
|
guarantees: import_zod8.z.array(GuaranteeSchema).optional(),
|
|
7135
7164
|
/**
|
|
7136
|
-
*
|
|
7137
|
-
*
|
|
7138
|
-
*
|
|
7165
|
+
* What this method does to its component's held state: `read` observes it, `write`
|
|
7166
|
+
* modifies an entity's domain fields, and `lifecycle` creates, destroys, or
|
|
7167
|
+
* (un)registers an entity's existence or membership without modifying its fields —
|
|
7168
|
+
* closed under composition (a lifecycle method calls only read and lifecycle methods,
|
|
7169
|
+
* LIFECYCLE_CALLS_WRITE). Required on a durable Store's contract methods so the
|
|
7170
|
+
* durability round-trip rule can pair mutations with hydration read-backs
|
|
7171
|
+
* (MISSING_HYDRATION); a Supervisor may call a data component it does not own only
|
|
7172
|
+
* through read and lifecycle methods (SUPERVISOR_WRITE_SHORTCUT). Optional elsewhere.
|
|
7139
7173
|
*/
|
|
7140
|
-
effect: import_zod8.z.enum(["read", "write"]).optional(),
|
|
7174
|
+
effect: import_zod8.z.enum(["read", "write", "lifecycle"]).optional(),
|
|
7141
7175
|
/**
|
|
7142
7176
|
* Typed acknowledgment of a real caller OUTSIDE the modeled narrative graph
|
|
7143
7177
|
* (runtime timer/hook, external system, sibling subsystem). Unused-detection
|
|
@@ -7587,12 +7621,12 @@ function implementationSourceFiles(impl) {
|
|
|
7587
7621
|
for (const method of impl.methods ?? []) add2(method.sourcePath);
|
|
7588
7622
|
return files;
|
|
7589
7623
|
}
|
|
7590
|
-
var TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature"]);
|
|
7624
|
+
var TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature", "enum"]);
|
|
7591
7625
|
var TypeFieldSchema = import_zod8.z.object({
|
|
7592
7626
|
name: import_zod8.z.string(),
|
|
7593
|
-
// A
|
|
7594
|
-
// "billing.Invoice")
|
|
7595
|
-
//
|
|
7627
|
+
// A type expression in the neutral grammar (src/models/type-grammar.ts): a
|
|
7628
|
+
// primitive, a named type ("billing.Invoice"), list/set/map, `T?`, a union
|
|
7629
|
+
// of named types. Stored canonical.
|
|
7596
7630
|
type: import_zod8.z.string(),
|
|
7597
7631
|
description: import_zod8.z.string().optional(),
|
|
7598
7632
|
optional: import_zod8.z.boolean().default(false),
|
|
@@ -7646,9 +7680,13 @@ var InvariantSchema = import_zod8.z.object({
|
|
|
7646
7680
|
/** The property that must hold, stated precisely enough to test against. */
|
|
7647
7681
|
description: import_zod8.z.string().min(1)
|
|
7648
7682
|
});
|
|
7683
|
+
var EnumValueSchema = import_zod8.z.object({
|
|
7684
|
+
name: import_zod8.z.string().min(1),
|
|
7685
|
+
description: import_zod8.z.string().optional()
|
|
7686
|
+
});
|
|
7649
7687
|
var TypeSpecSchema = import_zod8.z.object({
|
|
7650
7688
|
kind: TypeKindSchema,
|
|
7651
|
-
// discriminator — entity | value-object | signature
|
|
7689
|
+
// discriminator — entity | value-object | signature | enum
|
|
7652
7690
|
id: SpecIdSchema,
|
|
7653
7691
|
name: import_zod8.z.string(),
|
|
7654
7692
|
description: import_zod8.z.string().optional(),
|
|
@@ -7711,6 +7749,20 @@ var TypeSpecSchema = import_zod8.z.object({
|
|
|
7711
7749
|
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
7712
7750
|
/** A signature's one output type; required on kind signature and only there (SIGNATURE_TYPE_MEMBERS). */
|
|
7713
7751
|
returns: import_zod8.z.string().optional(),
|
|
7752
|
+
/**
|
|
7753
|
+
* An enum's values, in declared order (the order is part of the design),
|
|
7754
|
+
* unique by nameKey; required and non-empty on kind enum and only there
|
|
7755
|
+
* (ENUM_MEMBERS).
|
|
7756
|
+
*/
|
|
7757
|
+
values: import_zod8.z.array(EnumValueSchema).optional(),
|
|
7758
|
+
/**
|
|
7759
|
+
* A named scalar's one primitive (string, int, float, bool, bytes, date,
|
|
7760
|
+
* datetime or duration), in place of fields: a value-object that is a
|
|
7761
|
+
* newtype or type alias in every language. Read as a type position (the
|
|
7762
|
+
* grammar's `holds` position); only on a value-object and never beside
|
|
7763
|
+
* fields (NAMED_SCALAR_MEMBERS).
|
|
7764
|
+
*/
|
|
7765
|
+
holds: import_zod8.z.string().optional(),
|
|
7714
7766
|
createdAt: import_zod8.z.string().datetime(),
|
|
7715
7767
|
updatedAt: import_zod8.z.string().datetime()
|
|
7716
7768
|
});
|
|
@@ -7753,7 +7805,11 @@ var SurfaceTypeDefSchema = import_zod8.z.object({
|
|
|
7753
7805
|
optional: import_zod8.z.boolean().optional()
|
|
7754
7806
|
})).optional(),
|
|
7755
7807
|
/** A signature's one output type; only on kind signature. */
|
|
7756
|
-
returns: import_zod8.z.string().optional()
|
|
7808
|
+
returns: import_zod8.z.string().optional(),
|
|
7809
|
+
/** An enum's values in declared order; only on kind enum. */
|
|
7810
|
+
values: import_zod8.z.array(EnumValueSchema).optional(),
|
|
7811
|
+
/** A named scalar's one primitive, in place of fields; only on a value-object that declares it. */
|
|
7812
|
+
holds: import_zod8.z.string().optional()
|
|
7757
7813
|
});
|
|
7758
7814
|
var SurfaceTypeExportSchema = import_zod8.z.object({
|
|
7759
7815
|
/** The type's public name in the producer's export table. */
|
|
@@ -7862,80 +7918,767 @@ var GroupSpecSchema = import_zod8.z.object({
|
|
|
7862
7918
|
updatedAt: import_zod8.z.string().datetime()
|
|
7863
7919
|
});
|
|
7864
7920
|
|
|
7865
|
-
// src/models/type-
|
|
7866
|
-
var
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
"boolean",
|
|
7871
|
-
"
|
|
7872
|
-
"
|
|
7873
|
-
"
|
|
7874
|
-
"int",
|
|
7875
|
-
"
|
|
7876
|
-
"
|
|
7877
|
-
"
|
|
7878
|
-
"
|
|
7879
|
-
"
|
|
7880
|
-
"
|
|
7881
|
-
"
|
|
7882
|
-
"
|
|
7883
|
-
"
|
|
7884
|
-
"
|
|
7885
|
-
"
|
|
7886
|
-
"
|
|
7887
|
-
"
|
|
7888
|
-
"
|
|
7889
|
-
"
|
|
7890
|
-
"
|
|
7891
|
-
"
|
|
7921
|
+
// src/models/type-grammar.ts
|
|
7922
|
+
var PRIMITIVE_TYPES = ["string", "int", "float", "bool", "bytes", "date", "datetime", "duration", "void", "any"];
|
|
7923
|
+
var PRIMITIVES = new Set(PRIMITIVE_TYPES);
|
|
7924
|
+
var HOLDABLE_PRIMITIVES = PRIMITIVE_TYPES.filter((p) => p !== "void" && p !== "any");
|
|
7925
|
+
var PRIMITIVE_ALIASES = /* @__PURE__ */ new Map([
|
|
7926
|
+
["boolean", "bool"],
|
|
7927
|
+
["integer", "int"],
|
|
7928
|
+
["long", "int"],
|
|
7929
|
+
["i8", "int"],
|
|
7930
|
+
["i16", "int"],
|
|
7931
|
+
["i32", "int"],
|
|
7932
|
+
["i64", "int"],
|
|
7933
|
+
["i128", "int"],
|
|
7934
|
+
["isize", "int"],
|
|
7935
|
+
["u8", "int"],
|
|
7936
|
+
["u16", "int"],
|
|
7937
|
+
["u32", "int"],
|
|
7938
|
+
["u64", "int"],
|
|
7939
|
+
["u128", "int"],
|
|
7940
|
+
["usize", "int"],
|
|
7941
|
+
["double", "float"],
|
|
7942
|
+
["f32", "float"],
|
|
7943
|
+
["f64", "float"],
|
|
7944
|
+
["Buffer", "bytes"],
|
|
7945
|
+
["Uint8Array", "bytes"],
|
|
7946
|
+
["Date", "datetime"],
|
|
7947
|
+
["timestamp", "datetime"],
|
|
7948
|
+
["object", "any"],
|
|
7949
|
+
["unknown", "any"],
|
|
7950
|
+
["json", "any"],
|
|
7951
|
+
["Json", "any"],
|
|
7952
|
+
["str", "string"]
|
|
7953
|
+
]);
|
|
7954
|
+
var GENERIC_ALIASES = /* @__PURE__ */ new Map([
|
|
7955
|
+
["list", { form: "list", arity: 1 }],
|
|
7956
|
+
["Array", { form: "list", arity: 1 }],
|
|
7957
|
+
["ReadonlyArray", { form: "list", arity: 1 }],
|
|
7958
|
+
["List", { form: "list", arity: 1 }],
|
|
7959
|
+
["vec", { form: "list", arity: 1 }],
|
|
7960
|
+
["vector", { form: "list", arity: 1 }],
|
|
7961
|
+
["set", { form: "set", arity: 1 }],
|
|
7962
|
+
["Set", { form: "set", arity: 1 }],
|
|
7963
|
+
["ReadonlySet", { form: "set", arity: 1 }],
|
|
7964
|
+
["map", { form: "map", arity: 2 }],
|
|
7965
|
+
["Map", { form: "map", arity: 2 }],
|
|
7966
|
+
["Record", { form: "map", arity: 2 }],
|
|
7967
|
+
["ReadonlyMap", { form: "map", arity: 2 }],
|
|
7968
|
+
["dict", { form: "map", arity: 2 }],
|
|
7969
|
+
["dictionary", { form: "map", arity: 2 }],
|
|
7970
|
+
["HashMap", { form: "map", arity: 2 }],
|
|
7971
|
+
["Option", { form: "optional", arity: 1 }],
|
|
7972
|
+
["Optional", { form: "optional", arity: 1 }],
|
|
7973
|
+
["Promise", { form: "async", arity: 1 }]
|
|
7974
|
+
]);
|
|
7975
|
+
var NOT_NEUTRAL = /* @__PURE__ */ new Map([
|
|
7976
|
+
["uuid", "a named value-object holding a string (e.g. order_id)"],
|
|
7977
|
+
["decimal", "a named value-object (e.g. money), or float"],
|
|
7978
|
+
["char", "string"],
|
|
7979
|
+
["byte", "int, or bytes for binary data"],
|
|
7980
|
+
["time", "datetime for an instant, or duration for an elapsed time"],
|
|
7981
|
+
["tuple", "a named value-object"],
|
|
7982
|
+
["result", "the success type as the returns (failures as findings or throws), or a named value-object"],
|
|
7983
|
+
["error", "a named value-object"],
|
|
7984
|
+
["never", "void"],
|
|
7985
|
+
["box", "the inner type (ownership is an implementation detail)"],
|
|
7986
|
+
["arc", "the inner type (ownership is an implementation detail)"],
|
|
7987
|
+
["rc", "the inner type (ownership is an implementation detail)"],
|
|
7988
|
+
["ref", "the inner type (ownership is an implementation detail)"],
|
|
7989
|
+
["cell", "the inner type (ownership is an implementation detail)"],
|
|
7990
|
+
["refcell", "the inner type (ownership is an implementation detail)"],
|
|
7991
|
+
["mutex", "the inner type (synchronisation is an implementation detail)"],
|
|
7992
|
+
["rwlock", "the inner type (synchronisation is an implementation detail)"],
|
|
7993
|
+
["std", "a named type"],
|
|
7994
|
+
["mcpserver", "a named type"]
|
|
7995
|
+
]);
|
|
7996
|
+
var UTILITY_TYPES = /* @__PURE__ */ new Set([
|
|
7997
|
+
"Partial",
|
|
7998
|
+
"Required",
|
|
7999
|
+
"Readonly",
|
|
8000
|
+
"Pick",
|
|
8001
|
+
"Omit",
|
|
8002
|
+
"Exclude",
|
|
8003
|
+
"Extract",
|
|
8004
|
+
"NonNullable",
|
|
8005
|
+
"ReturnType",
|
|
8006
|
+
"Parameters",
|
|
8007
|
+
"InstanceType",
|
|
8008
|
+
"Awaited",
|
|
8009
|
+
"ConstructorParameters",
|
|
8010
|
+
"ThisType",
|
|
8011
|
+
"Uppercase",
|
|
8012
|
+
"Lowercase"
|
|
8013
|
+
]);
|
|
8014
|
+
var NONES = /* @__PURE__ */ new Set(["null", "undefined"]);
|
|
8015
|
+
var LOWER_PRIMITIVE = new Map([
|
|
8016
|
+
...PRIMITIVE_TYPES.map((p) => [p, p]),
|
|
8017
|
+
...[...PRIMITIVE_ALIASES].map(([k, v]) => [k.toLowerCase(), v])
|
|
8018
|
+
]);
|
|
8019
|
+
var LOWER_GENERIC = new Map([...GENERIC_ALIASES].map(([k, v]) => [k.toLowerCase(), v]));
|
|
8020
|
+
function isTypeVocabulary(name) {
|
|
8021
|
+
const lower = name.toLowerCase();
|
|
8022
|
+
return LOWER_PRIMITIVE.has(lower) || LOWER_GENERIC.has(lower) || NOT_NEUTRAL.has(lower) || NONES.has(lower) || lower === "true" || lower === "false" || lower === "number";
|
|
8023
|
+
}
|
|
8024
|
+
function primitiveOf(name) {
|
|
8025
|
+
if (PRIMITIVES.has(name)) return name;
|
|
8026
|
+
const exact = PRIMITIVE_ALIASES.get(name);
|
|
8027
|
+
if (exact) return exact;
|
|
8028
|
+
return LOWER_PRIMITIVE.get(name.toLowerCase());
|
|
8029
|
+
}
|
|
8030
|
+
function genericOf(name) {
|
|
8031
|
+
return GENERIC_ALIASES.get(name) ?? LOWER_GENERIC.get(name.toLowerCase());
|
|
8032
|
+
}
|
|
8033
|
+
var TypeSyntaxError = class extends Error {
|
|
8034
|
+
};
|
|
8035
|
+
var PUNCTUATION = ["...", "::", "=>", "<", ">", ",", "|", "?", "(", ")", "[", "]", "{", "}", ":", "&", ";", "."];
|
|
8036
|
+
function tokenize2(text3) {
|
|
8037
|
+
const out = [];
|
|
8038
|
+
let i = 0;
|
|
8039
|
+
while (i < text3.length) {
|
|
8040
|
+
const c = text3[i];
|
|
8041
|
+
if (/\s/.test(c)) {
|
|
8042
|
+
i++;
|
|
8043
|
+
continue;
|
|
8044
|
+
}
|
|
8045
|
+
if (/[A-Za-z_$]/.test(c)) {
|
|
8046
|
+
let j = i + 1;
|
|
8047
|
+
while (j < text3.length && /[A-Za-z0-9_$-]/.test(text3[j])) j++;
|
|
8048
|
+
while (text3[j - 1] === "-") j--;
|
|
8049
|
+
out.push({ t: "id", v: text3.slice(i, j) });
|
|
8050
|
+
i = j;
|
|
8051
|
+
continue;
|
|
8052
|
+
}
|
|
8053
|
+
if (/[0-9]/.test(c)) {
|
|
8054
|
+
let j = i + 1;
|
|
8055
|
+
while (j < text3.length && /[0-9.]/.test(text3[j])) j++;
|
|
8056
|
+
out.push({ t: "num", v: text3.slice(i, j) });
|
|
8057
|
+
i = j;
|
|
8058
|
+
continue;
|
|
8059
|
+
}
|
|
8060
|
+
if (c === "'" || c === '"' || c === "`") {
|
|
8061
|
+
let j = i + 1;
|
|
8062
|
+
while (j < text3.length && text3[j] !== c) j += text3[j] === "\\" ? 2 : 1;
|
|
8063
|
+
if (j >= text3.length) throw new TypeSyntaxError(`an unterminated string literal at "${text3.slice(i)}"`);
|
|
8064
|
+
out.push({ t: "str", v: text3.slice(i, j + 1) });
|
|
8065
|
+
i = j + 1;
|
|
8066
|
+
continue;
|
|
8067
|
+
}
|
|
8068
|
+
const punct = PUNCTUATION.find((p) => text3.startsWith(p, i));
|
|
8069
|
+
if (!punct) throw new TypeSyntaxError(`"${c}" is not part of any type spelling`);
|
|
8070
|
+
out.push({ t: "p", v: punct });
|
|
8071
|
+
i += punct.length;
|
|
8072
|
+
}
|
|
8073
|
+
return out;
|
|
8074
|
+
}
|
|
8075
|
+
var RawParser = class {
|
|
8076
|
+
constructor(tokens) {
|
|
8077
|
+
this.tokens = tokens;
|
|
8078
|
+
this.at = 0;
|
|
8079
|
+
}
|
|
8080
|
+
/** type-position: an optional `async` prefix, one type, and nothing after it. */
|
|
8081
|
+
position() {
|
|
8082
|
+
if (this.tokens.length === 0) throw new TypeSyntaxError("the position is empty");
|
|
8083
|
+
const node = this.isAsyncPrefix() ? (this.at++, { k: "async", inner: this.type() }) : this.type();
|
|
8084
|
+
if (this.at < this.tokens.length) throw new TypeSyntaxError(`"${this.rest()}" is left over after the type`);
|
|
8085
|
+
return node;
|
|
8086
|
+
}
|
|
8087
|
+
isAsyncPrefix() {
|
|
8088
|
+
const first = this.tokens[0];
|
|
8089
|
+
const next = this.tokens[1];
|
|
8090
|
+
if (!first || first.t !== "id" || first.v !== "async" || !next) return false;
|
|
8091
|
+
return next.t !== "p" || ["(", "{", "[", "::"].includes(next.v);
|
|
8092
|
+
}
|
|
8093
|
+
rest() {
|
|
8094
|
+
return this.tokens.slice(this.at).map((t) => t.v).join(" ");
|
|
8095
|
+
}
|
|
8096
|
+
peek(offset = 0) {
|
|
8097
|
+
return this.tokens[this.at + offset];
|
|
8098
|
+
}
|
|
8099
|
+
isP(v, offset = 0) {
|
|
8100
|
+
const t = this.peek(offset);
|
|
8101
|
+
return !!t && t.t === "p" && t.v === v;
|
|
8102
|
+
}
|
|
8103
|
+
expect(v) {
|
|
8104
|
+
if (!this.isP(v)) {
|
|
8105
|
+
const got = this.peek();
|
|
8106
|
+
throw new TypeSyntaxError(got ? `expected "${v}" but found "${got.v}"` : `expected "${v}" but the text ended`);
|
|
8107
|
+
}
|
|
8108
|
+
this.at++;
|
|
8109
|
+
}
|
|
8110
|
+
type() {
|
|
8111
|
+
if (this.isP("|")) this.at++;
|
|
8112
|
+
const members = [this.intersection()];
|
|
8113
|
+
while (this.isP("|")) {
|
|
8114
|
+
this.at++;
|
|
8115
|
+
members.push(this.intersection());
|
|
8116
|
+
}
|
|
8117
|
+
return members.length === 1 ? members[0] : { k: "union", members };
|
|
8118
|
+
}
|
|
8119
|
+
intersection() {
|
|
8120
|
+
const members = [this.postfix()];
|
|
8121
|
+
while (this.isP("&")) {
|
|
8122
|
+
this.at++;
|
|
8123
|
+
members.push(this.postfix());
|
|
8124
|
+
}
|
|
8125
|
+
return members.length === 1 ? members[0] : { k: "intersection", members };
|
|
8126
|
+
}
|
|
8127
|
+
postfix() {
|
|
8128
|
+
let node = this.primary();
|
|
8129
|
+
for (; ; ) {
|
|
8130
|
+
if (this.isP("[") && this.isP("]", 1)) {
|
|
8131
|
+
this.at += 2;
|
|
8132
|
+
node = { k: "array", elem: node };
|
|
8133
|
+
} else if (this.isP("?")) {
|
|
8134
|
+
this.at++;
|
|
8135
|
+
node = { k: "opt", inner: node };
|
|
8136
|
+
} else {
|
|
8137
|
+
return node;
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
primary() {
|
|
8142
|
+
const tok = this.peek();
|
|
8143
|
+
if (!tok) throw new TypeSyntaxError("a type was expected but the text ended");
|
|
8144
|
+
if (tok.t === "str" || tok.t === "num") {
|
|
8145
|
+
this.at++;
|
|
8146
|
+
return { k: "literal", text: tok.v };
|
|
8147
|
+
}
|
|
8148
|
+
if (tok.t === "id" && (tok.v === "keyof" || tok.v === "typeof" || tok.v === "unique") && this.peek(1)?.t === "id") {
|
|
8149
|
+
this.at++;
|
|
8150
|
+
return { k: "operator", op: tok.v, inner: this.postfix() };
|
|
8151
|
+
}
|
|
8152
|
+
const next = this.peek(1);
|
|
8153
|
+
if (tok.t === "id" && tok.v === "readonly" && next && (next.t === "id" || next.t === "p" && (next.v === "(" || next.v === "["))) {
|
|
8154
|
+
this.at++;
|
|
8155
|
+
return this.postfix();
|
|
8156
|
+
}
|
|
8157
|
+
if (tok.t === "id" || tok.t === "p" && tok.v === "::") return this.named();
|
|
8158
|
+
if (tok.t === "p" && tok.v === "(") return this.parenOrFunction();
|
|
8159
|
+
if (tok.t === "p" && tok.v === "{") return this.objectShape();
|
|
8160
|
+
if (tok.t === "p" && tok.v === "[") return this.tuple();
|
|
8161
|
+
throw new TypeSyntaxError(`"${tok.v}" cannot start a type`);
|
|
8162
|
+
}
|
|
8163
|
+
ident() {
|
|
8164
|
+
const tok = this.peek();
|
|
8165
|
+
if (!tok || tok.t !== "id") throw new TypeSyntaxError(tok ? `expected a name but found "${tok.v}"` : "expected a name but the text ended");
|
|
8166
|
+
this.at++;
|
|
8167
|
+
return tok.v;
|
|
8168
|
+
}
|
|
8169
|
+
named() {
|
|
8170
|
+
let name = "";
|
|
8171
|
+
if (this.isP("::")) {
|
|
8172
|
+
this.at++;
|
|
8173
|
+
name = "::";
|
|
8174
|
+
}
|
|
8175
|
+
name += this.ident();
|
|
8176
|
+
while (this.isP("::") || this.isP(".")) {
|
|
8177
|
+
const sep14 = this.peek().v;
|
|
8178
|
+
this.at++;
|
|
8179
|
+
name += sep14 + this.ident();
|
|
8180
|
+
}
|
|
8181
|
+
if (!this.isP("<")) return { k: "name", name };
|
|
8182
|
+
this.at++;
|
|
8183
|
+
const args = [this.type()];
|
|
8184
|
+
while (this.isP(",")) {
|
|
8185
|
+
this.at++;
|
|
8186
|
+
args.push(this.type());
|
|
8187
|
+
}
|
|
8188
|
+
this.expect(">");
|
|
8189
|
+
return { k: "name", name, args };
|
|
8190
|
+
}
|
|
8191
|
+
/** The index of the `)` closing the `(` at the cursor, or -1. */
|
|
8192
|
+
closingParen() {
|
|
8193
|
+
let depth = 0;
|
|
8194
|
+
for (let i = this.at; i < this.tokens.length; i++) {
|
|
8195
|
+
const t = this.tokens[i];
|
|
8196
|
+
if (t.t !== "p") continue;
|
|
8197
|
+
if (t.v === "(") depth++;
|
|
8198
|
+
else if (t.v === ")" && --depth === 0) return i;
|
|
8199
|
+
}
|
|
8200
|
+
return -1;
|
|
8201
|
+
}
|
|
8202
|
+
parenOrFunction() {
|
|
8203
|
+
const close2 = this.closingParen();
|
|
8204
|
+
const after = close2 === -1 ? void 0 : this.tokens[close2 + 1];
|
|
8205
|
+
if (!after || after.t !== "p" || after.v !== "=>") {
|
|
8206
|
+
this.expect("(");
|
|
8207
|
+
const inner2 = this.type();
|
|
8208
|
+
this.expect(")");
|
|
8209
|
+
return { k: "paren", inner: inner2 };
|
|
8210
|
+
}
|
|
8211
|
+
this.expect("(");
|
|
8212
|
+
const params = [];
|
|
8213
|
+
while (!this.isP(")")) {
|
|
8214
|
+
if (this.isP("...")) this.at++;
|
|
8215
|
+
this.ident();
|
|
8216
|
+
if (this.isP("?")) this.at++;
|
|
8217
|
+
if (this.isP(":")) {
|
|
8218
|
+
this.at++;
|
|
8219
|
+
params.push(this.type());
|
|
8220
|
+
}
|
|
8221
|
+
if (!this.isP(",")) break;
|
|
8222
|
+
this.at++;
|
|
8223
|
+
}
|
|
8224
|
+
this.expect(")");
|
|
8225
|
+
this.expect("=>");
|
|
8226
|
+
return { k: "function", params, ret: this.type() };
|
|
8227
|
+
}
|
|
8228
|
+
objectShape() {
|
|
8229
|
+
this.expect("{");
|
|
8230
|
+
const members = [];
|
|
8231
|
+
while (!this.isP("}")) {
|
|
8232
|
+
if (this.isP("[")) {
|
|
8233
|
+
this.at++;
|
|
8234
|
+
this.ident();
|
|
8235
|
+
this.expect(":");
|
|
8236
|
+
members.push(this.type());
|
|
8237
|
+
this.expect("]");
|
|
8238
|
+
} else {
|
|
8239
|
+
if (this.peek()?.t === "id" && this.peek().v === "readonly" && this.peek(1)?.t === "id") this.at++;
|
|
8240
|
+
const key = this.peek();
|
|
8241
|
+
if (!key || key.t !== "id" && key.t !== "str" && key.t !== "num") throw new TypeSyntaxError("an inline object member has no name");
|
|
8242
|
+
this.at++;
|
|
8243
|
+
if (this.isP("?")) this.at++;
|
|
8244
|
+
if (this.isP("(")) {
|
|
8245
|
+
members.push(this.parenOrFunctionMember());
|
|
8246
|
+
this.skipSeparator();
|
|
8247
|
+
continue;
|
|
8248
|
+
}
|
|
8249
|
+
}
|
|
8250
|
+
this.expect(":");
|
|
8251
|
+
members.push(this.type());
|
|
8252
|
+
this.skipSeparator();
|
|
8253
|
+
}
|
|
8254
|
+
this.expect("}");
|
|
8255
|
+
return { k: "object", members };
|
|
8256
|
+
}
|
|
8257
|
+
/** A method member of an inline object: (params): returns. */
|
|
8258
|
+
parenOrFunctionMember() {
|
|
8259
|
+
this.expect("(");
|
|
8260
|
+
const params = [];
|
|
8261
|
+
while (!this.isP(")")) {
|
|
8262
|
+
if (this.isP("...")) this.at++;
|
|
8263
|
+
this.ident();
|
|
8264
|
+
if (this.isP("?")) this.at++;
|
|
8265
|
+
if (this.isP(":")) {
|
|
8266
|
+
this.at++;
|
|
8267
|
+
params.push(this.type());
|
|
8268
|
+
}
|
|
8269
|
+
if (!this.isP(",")) break;
|
|
8270
|
+
this.at++;
|
|
8271
|
+
}
|
|
8272
|
+
this.expect(")");
|
|
8273
|
+
this.expect(":");
|
|
8274
|
+
return { k: "function", params, ret: this.type() };
|
|
8275
|
+
}
|
|
8276
|
+
skipSeparator() {
|
|
8277
|
+
if (this.isP(";") || this.isP(",")) this.at++;
|
|
8278
|
+
}
|
|
8279
|
+
tuple() {
|
|
8280
|
+
this.expect("[");
|
|
8281
|
+
const elems = [];
|
|
8282
|
+
while (!this.isP("]")) {
|
|
8283
|
+
elems.push(this.type());
|
|
8284
|
+
if (!this.isP(",")) break;
|
|
8285
|
+
this.at++;
|
|
8286
|
+
}
|
|
8287
|
+
this.expect("]");
|
|
8288
|
+
return { k: "tuple", elems };
|
|
8289
|
+
}
|
|
8290
|
+
};
|
|
8291
|
+
function readRaw(text3) {
|
|
8292
|
+
return new RawParser(tokenize2(text3)).position();
|
|
8293
|
+
}
|
|
8294
|
+
var TypeProblemError = class extends Error {
|
|
8295
|
+
constructor(code, detail, replacement) {
|
|
8296
|
+
super(detail);
|
|
8297
|
+
this.code = code;
|
|
8298
|
+
this.detail = detail;
|
|
8299
|
+
this.replacement = replacement;
|
|
8300
|
+
}
|
|
8301
|
+
};
|
|
8302
|
+
var RETURNS_POSITIONS = /* @__PURE__ */ new Set(["returns", "type-method-returns", "signature-returns"]);
|
|
8303
|
+
var LOOSE_NUMBER = "number";
|
|
8304
|
+
function primitive(name) {
|
|
8305
|
+
return { form: "primitive", name, args: [] };
|
|
8306
|
+
}
|
|
8307
|
+
function optionalOf(inner2) {
|
|
8308
|
+
if (inner2.form === "primitive" && inner2.name === "any") return inner2;
|
|
8309
|
+
if (inner2.form === "optional") throw new TypeProblemError("TYPE_POSITION_INVALID", "a type is made optional twice (T??)", "T?");
|
|
8310
|
+
if (inner2.form === "primitive" && inner2.name === "void") throw new TypeProblemError("TYPE_POSITION_INVALID", "void cannot be optional (void?)", "void");
|
|
8311
|
+
return { form: "optional", args: [inner2] };
|
|
8312
|
+
}
|
|
8313
|
+
function inner(ctx) {
|
|
8314
|
+
return { ...ctx, top: false, voidAllowed: false, omittable: false };
|
|
8315
|
+
}
|
|
8316
|
+
function normalise(node, ctx, notes) {
|
|
8317
|
+
switch (node.k) {
|
|
8318
|
+
case "paren":
|
|
8319
|
+
return normalise(node.inner, ctx, notes);
|
|
8320
|
+
case "async":
|
|
8321
|
+
return asyncOf(node.inner, ctx, notes, "async");
|
|
8322
|
+
case "array":
|
|
8323
|
+
return { form: "list", args: [normalise(node.elem, inner(ctx), notes)] };
|
|
8324
|
+
case "opt":
|
|
8325
|
+
return optionalOf(normalise(node.inner, inner(ctx), notes));
|
|
8326
|
+
case "union":
|
|
8327
|
+
return unionOf(node.members, ctx, notes);
|
|
8328
|
+
case "name":
|
|
8329
|
+
return node.args === void 0 ? bareName(node.name, ctx, notes) : appliedName(node.name, node.args, ctx, notes);
|
|
8330
|
+
case "literal":
|
|
8331
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `${node.text} is a literal value, not a type \u2014 a literal union is not in the grammar`, "an enum type naming the values");
|
|
8332
|
+
case "object":
|
|
8333
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline object shape is not in the grammar", "a named value-object");
|
|
8334
|
+
case "function":
|
|
8335
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline function type is not in the grammar", "a signature type");
|
|
8336
|
+
case "intersection":
|
|
8337
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an intersection (A & B) is not in the grammar", "a named type");
|
|
8338
|
+
case "tuple":
|
|
8339
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a tuple is not in the grammar", "a named value-object");
|
|
8340
|
+
case "operator":
|
|
8341
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${node.op}" is TypeScript's type algebra, not in the grammar`, "a named type");
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
function asyncOf(innerNode, ctx, notes, spelled) {
|
|
8345
|
+
if (!ctx.top || !RETURNS_POSITIONS.has(ctx.position)) {
|
|
8346
|
+
throw new TypeProblemError("TYPE_POSITION_INVALID", `${spelled} may stand only at the top of a returns, not in a ${ctx.position}`);
|
|
8347
|
+
}
|
|
8348
|
+
const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true }, notes);
|
|
8349
|
+
return { form: "async", args: [read2] };
|
|
8350
|
+
}
|
|
8351
|
+
function bareName(name, ctx, notes) {
|
|
8352
|
+
const lower = name.toLowerCase();
|
|
8353
|
+
if (NONES.has(name)) {
|
|
8354
|
+
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" on its own names no type \u2014 "no value" joins a type as T?`, "T?, or void for a returns that answers nothing");
|
|
8355
|
+
}
|
|
8356
|
+
if (lower === "true" || lower === "false") {
|
|
8357
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `${name} is a literal value, not a type`, "bool, or an enum type naming the values");
|
|
8358
|
+
}
|
|
8359
|
+
if (name === "number" || lower === "number") {
|
|
8360
|
+
if (ctx.looseNumber) return primitive(LOOSE_NUMBER);
|
|
8361
|
+
notes.notNeutral ??= { detail: '"number" does not say whether it holds an integer \u2014 int or float?', replacement: "int or float" };
|
|
8362
|
+
return primitive("float");
|
|
8363
|
+
}
|
|
8364
|
+
const prim = primitiveOf(name);
|
|
8365
|
+
if (prim !== void 0) {
|
|
8366
|
+
if (prim === "void" && !ctx.voidAllowed) {
|
|
8367
|
+
throw new TypeProblemError("TYPE_POSITION_INVALID", `void may stand only as a whole returns (or async void), not in a ${ctx.top ? ctx.position : "collection, union or argument"}`);
|
|
8368
|
+
}
|
|
8369
|
+
return primitive(prim);
|
|
8370
|
+
}
|
|
8371
|
+
if (genericOf(name) !== void 0) {
|
|
8372
|
+
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" needs its type arguments`, `${genericOf(name)?.form ?? "list"}<...>`);
|
|
8373
|
+
}
|
|
8374
|
+
const legacy = NOT_NEUTRAL.get(lower);
|
|
8375
|
+
if (legacy !== void 0) {
|
|
8376
|
+
notes.notNeutral ??= { detail: `"${name}" has no neutral meaning`, replacement: legacy };
|
|
8377
|
+
return primitive("any");
|
|
8378
|
+
}
|
|
8379
|
+
return { form: "named", name, args: [] };
|
|
8380
|
+
}
|
|
8381
|
+
function appliedName(name, args, ctx, notes) {
|
|
8382
|
+
const alias = genericOf(name);
|
|
8383
|
+
if (alias !== void 0) {
|
|
8384
|
+
if (args.length !== alias.arity) {
|
|
8385
|
+
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes ${alias.arity} type argument${alias.arity === 1 ? "" : "s"}, ${args.length} given`);
|
|
8386
|
+
}
|
|
8387
|
+
if (alias.form === "async") return asyncOf(args[0], ctx, notes, `${name}<...>`);
|
|
8388
|
+
if (alias.form === "optional") return optionalOf(normalise(args[0], inner(ctx), notes));
|
|
8389
|
+
const read2 = args.map((a) => normalise(a, inner(ctx), notes));
|
|
8390
|
+
if (alias.form === "map") assertMapKey(read2[0], args[0]);
|
|
8391
|
+
return { form: alias.form, args: read2 };
|
|
8392
|
+
}
|
|
8393
|
+
if (UTILITY_TYPES.has(name)) {
|
|
8394
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${name}<...>" is a TypeScript utility type, not in the grammar`, "a named type");
|
|
8395
|
+
}
|
|
8396
|
+
if (primitiveOf(name) !== void 0 || NONES.has(name)) {
|
|
8397
|
+
throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes no type arguments`);
|
|
8398
|
+
}
|
|
8399
|
+
const legacy = NOT_NEUTRAL.get(name.toLowerCase());
|
|
8400
|
+
if (legacy !== void 0) {
|
|
8401
|
+
args.forEach((a) => normalise(a, { ...inner(ctx), voidAllowed: true }, notes));
|
|
8402
|
+
notes.notNeutral ??= { detail: `"${name}<...>" has no neutral meaning`, replacement: legacy };
|
|
8403
|
+
return primitive("any");
|
|
8404
|
+
}
|
|
8405
|
+
return { form: "applied", name, args: args.map((a) => normalise(a, inner(ctx), notes)) };
|
|
8406
|
+
}
|
|
8407
|
+
function assertHoldable(expression) {
|
|
8408
|
+
if (expression.form === "primitive" && HOLDABLE_PRIMITIVES.includes(expression.name ?? "")) return;
|
|
8409
|
+
throw new TypeProblemError(
|
|
8410
|
+
"TYPE_POSITION_INVALID",
|
|
8411
|
+
`a named scalar holds one primitive (${HOLDABLE_PRIMITIVES.join(", ")}), not ${canonicalTypeText(expression)}`,
|
|
8412
|
+
"one of those primitives, or fields for a value-object that holds more than one value"
|
|
8413
|
+
);
|
|
8414
|
+
}
|
|
8415
|
+
function assertMapKey(key, written) {
|
|
8416
|
+
if (key.form === "named") return;
|
|
8417
|
+
if (key.form === "primitive" && (key.name === "string" || key.name === "int" || key.name === LOOSE_NUMBER)) return;
|
|
8418
|
+
if (key.form === "primitive" && key.name === "float" && written.k === "name" && written.name.toLowerCase() === "number") return;
|
|
8419
|
+
throw new TypeProblemError("TYPE_POSITION_INVALID", "a map key must be string, int or an enum", "map<string, V>");
|
|
8420
|
+
}
|
|
8421
|
+
function unionOf(members, ctx, notes) {
|
|
8422
|
+
const nones = members.filter((m) => m.k === "name" && m.args === void 0 && NONES.has(m.name));
|
|
8423
|
+
const rest = members.filter((m) => !(m.k === "name" && m.args === void 0 && NONES.has(m.name)));
|
|
8424
|
+
if (rest.length === 0) throw new TypeProblemError("TYPE_EXPRESSION_INVALID", "a union of nones names no type");
|
|
8425
|
+
if (rest.some((m) => m.k === "literal" || m.k === "name" && m.args === void 0 && /^(true|false)$/i.test(m.name))) {
|
|
8426
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a union of literal values is not in the grammar", "an enum type naming the values");
|
|
8427
|
+
}
|
|
8428
|
+
const memberCtx = inner(ctx);
|
|
8429
|
+
let hasNone = nones.length > 0;
|
|
8430
|
+
const read2 = rest.map((m) => {
|
|
8431
|
+
const e = normalise(m, memberCtx, notes);
|
|
8432
|
+
if (e.form !== "optional") return e;
|
|
8433
|
+
hasNone = true;
|
|
8434
|
+
return e.args[0];
|
|
8435
|
+
});
|
|
8436
|
+
let base;
|
|
8437
|
+
if (read2.length === 1) {
|
|
8438
|
+
base = read2[0];
|
|
8439
|
+
} else {
|
|
8440
|
+
const loose = read2.find((e) => e.form !== "named" && e.form !== "applied");
|
|
8441
|
+
if (loose) {
|
|
8442
|
+
throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a union may hold only named types \u2014 this one mixes in a primitive or a collection", "a named type (an entity, value-object or enum), or two params");
|
|
8443
|
+
}
|
|
8444
|
+
base = { form: "union", args: read2 };
|
|
8445
|
+
}
|
|
8446
|
+
if (!hasNone) return base;
|
|
8447
|
+
const onlyUndefined = nones.length > 0 && nones.every((n) => n.name === "undefined") && read2.length === rest.length && !rest.some((m) => m.k === "opt");
|
|
8448
|
+
if (ctx.omittable && ctx.top && onlyUndefined) return base;
|
|
8449
|
+
return optionalOf(base);
|
|
8450
|
+
}
|
|
8451
|
+
function canonicalTypeText(expr) {
|
|
8452
|
+
switch (expr.form) {
|
|
8453
|
+
case "primitive":
|
|
8454
|
+
case "named":
|
|
8455
|
+
return expr.name ?? "";
|
|
8456
|
+
case "list":
|
|
8457
|
+
case "set":
|
|
8458
|
+
return `${expr.form}<${canonicalTypeText(expr.args[0])}>`;
|
|
8459
|
+
case "map":
|
|
8460
|
+
return `map<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
|
|
8461
|
+
case "optional": {
|
|
8462
|
+
const innerText = canonicalTypeText(expr.args[0]);
|
|
8463
|
+
return expr.args[0].form === "union" ? `(${innerText})?` : `${innerText}?`;
|
|
8464
|
+
}
|
|
8465
|
+
case "union":
|
|
8466
|
+
return expr.args.map(canonicalTypeText).join(" | ");
|
|
8467
|
+
case "async":
|
|
8468
|
+
return `async ${canonicalTypeText(expr.args[0])}`;
|
|
8469
|
+
case "applied":
|
|
8470
|
+
return `${expr.name}<${expr.args.map(canonicalTypeText).join(", ")}>`;
|
|
8471
|
+
}
|
|
8472
|
+
}
|
|
8473
|
+
function typeNamedRefs(expr) {
|
|
8474
|
+
if (expr.form === "named") return [expr.name];
|
|
8475
|
+
const own = expr.form === "applied" ? [expr.name] : [];
|
|
8476
|
+
return [...own, ...expr.args.flatMap(typeNamedRefs)];
|
|
8477
|
+
}
|
|
8478
|
+
function typeIsMany(expr) {
|
|
8479
|
+
if (expr.form === "list" || expr.form === "set" || expr.form === "map") return true;
|
|
8480
|
+
if (expr.form === "optional" || expr.form === "async") return typeIsMany(expr.args[0]);
|
|
8481
|
+
return false;
|
|
8482
|
+
}
|
|
8483
|
+
function contextFor(position, omittable2, looseNumber = false) {
|
|
8484
|
+
return { position, top: true, voidAllowed: RETURNS_POSITIONS.has(position), omittable: omittable2, looseNumber };
|
|
8485
|
+
}
|
|
8486
|
+
function parseTypePosition(text3, position, omittable2 = false) {
|
|
8487
|
+
let raw;
|
|
8488
|
+
try {
|
|
8489
|
+
raw = readRaw(text3);
|
|
8490
|
+
} catch (e) {
|
|
8491
|
+
if (!(e instanceof TypeSyntaxError)) throw e;
|
|
8492
|
+
return { expression: null, canonical: text3, problem: { code: "TYPE_EXPRESSION_INVALID", written: text3, detail: `"${text3}" does not parse as a type: ${e.message}` } };
|
|
8493
|
+
}
|
|
8494
|
+
const notes = {};
|
|
8495
|
+
try {
|
|
8496
|
+
const expression = normalise(raw, contextFor(position, omittable2), notes);
|
|
8497
|
+
if (position === "holds") assertHoldable(expression);
|
|
8498
|
+
const problem = notes.notNeutral ? { code: "TYPE_NOT_NEUTRAL", written: text3, detail: notes.notNeutral.detail, replacement: notes.notNeutral.replacement } : null;
|
|
8499
|
+
return { expression, canonical: canonicalTypeText(expression), problem };
|
|
8500
|
+
} catch (e) {
|
|
8501
|
+
if (!(e instanceof TypeProblemError)) throw e;
|
|
8502
|
+
return {
|
|
8503
|
+
expression: null,
|
|
8504
|
+
canonical: text3,
|
|
8505
|
+
problem: { code: e.code, written: text3, detail: `"${text3}": ${e.detail}`, ...e.replacement ? { replacement: e.replacement } : {} }
|
|
8506
|
+
};
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
function parseTypeExpression(text3, position) {
|
|
8510
|
+
return parseTypePosition(text3, position);
|
|
8511
|
+
}
|
|
8512
|
+
function readCodeAnnotation(annotation, looseNumber) {
|
|
8513
|
+
let raw;
|
|
8514
|
+
try {
|
|
8515
|
+
raw = readRaw(annotation);
|
|
8516
|
+
} catch (e) {
|
|
8517
|
+
if (e instanceof TypeSyntaxError) return null;
|
|
8518
|
+
throw e;
|
|
8519
|
+
}
|
|
8520
|
+
try {
|
|
8521
|
+
return normalise(raw, contextFor("returns", false, looseNumber), {});
|
|
8522
|
+
} catch (e) {
|
|
8523
|
+
if (e instanceof TypeProblemError) return null;
|
|
8524
|
+
throw e;
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
function writtenTypeRefs(text3, position) {
|
|
8528
|
+
const parse = parseTypePosition(text3, position);
|
|
8529
|
+
if (parse.expression && !parse.problem) return typeNamedRefs(parse.expression);
|
|
8530
|
+
try {
|
|
8531
|
+
return rawRefs(readRaw(text3));
|
|
8532
|
+
} catch (e) {
|
|
8533
|
+
if (e instanceof TypeSyntaxError) return [];
|
|
8534
|
+
throw e;
|
|
8535
|
+
}
|
|
8536
|
+
}
|
|
8537
|
+
function rawRefs(node) {
|
|
8538
|
+
switch (node.k) {
|
|
8539
|
+
case "name": {
|
|
8540
|
+
const own = isTypeVocabulary(node.name) || UTILITY_TYPES.has(node.name) ? [] : [node.name];
|
|
8541
|
+
return [...own, ...(node.args ?? []).flatMap(rawRefs)];
|
|
8542
|
+
}
|
|
8543
|
+
case "array":
|
|
8544
|
+
return rawRefs(node.elem);
|
|
8545
|
+
case "opt":
|
|
8546
|
+
case "paren":
|
|
8547
|
+
case "async":
|
|
8548
|
+
case "operator":
|
|
8549
|
+
return rawRefs(node.inner);
|
|
8550
|
+
case "union":
|
|
8551
|
+
case "intersection":
|
|
8552
|
+
case "object":
|
|
8553
|
+
return node.members.flatMap(rawRefs);
|
|
8554
|
+
case "tuple":
|
|
8555
|
+
return node.elems.flatMap(rawRefs);
|
|
8556
|
+
case "function":
|
|
8557
|
+
return [...node.params.flatMap(rawRefs), ...rawRefs(node.ret)];
|
|
8558
|
+
case "literal":
|
|
8559
|
+
return [];
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
var Canonicalizer = class {
|
|
8563
|
+
constructor(specId, kind) {
|
|
8564
|
+
this.specId = specId;
|
|
8565
|
+
this.kind = kind;
|
|
8566
|
+
this.respellings = [];
|
|
8567
|
+
this.problems = [];
|
|
8568
|
+
}
|
|
8569
|
+
/** One position: its canonical text when it has one, else the text as written, recorded either way. */
|
|
8570
|
+
read(text3, position, path49, omittable2 = false) {
|
|
8571
|
+
const parse = parseTypePosition(text3, position, omittable2);
|
|
8572
|
+
if (parse.problem) {
|
|
8573
|
+
this.problems.push({ ...parse.problem, specId: this.specId, kind: this.kind, path: path49 });
|
|
8574
|
+
return text3;
|
|
8575
|
+
}
|
|
8576
|
+
if (parse.canonical !== text3) this.respellings.push({ specId: this.specId, kind: this.kind, path: path49, written: text3, stored: parse.canonical });
|
|
8577
|
+
return parse.canonical;
|
|
8578
|
+
}
|
|
8579
|
+
params(params, position, prefix) {
|
|
8580
|
+
return params.map((p) => ({ ...p, type: this.read(p.type, position, `${prefix}.${p.name}`, !!p.optional) }));
|
|
8581
|
+
}
|
|
8582
|
+
};
|
|
8583
|
+
function interfaceCanonicalTypes(intf) {
|
|
8584
|
+
const reader = new Canonicalizer(intf.id, "interface");
|
|
8585
|
+
const methods = (intf.methods ?? []).map((m) => {
|
|
8586
|
+
if (m.signatureFrom !== void 0) return m;
|
|
8587
|
+
const out = { ...m };
|
|
8588
|
+
if (m.params !== void 0) out.params = reader.params(m.params, "param", `methods.${m.name}.params`);
|
|
8589
|
+
if (typeof m.returns === "string") out.returns = reader.read(m.returns, "returns", `methods.${m.name}.returns`);
|
|
8590
|
+
return out;
|
|
8591
|
+
});
|
|
8592
|
+
return { spec: { ...intf, methods }, respellings: reader.respellings, problems: reader.problems };
|
|
8593
|
+
}
|
|
8594
|
+
function typeCanonicalTypes(type) {
|
|
8595
|
+
const reader = new Canonicalizer(type.id, "type");
|
|
8596
|
+
const out = { ...type };
|
|
8597
|
+
if (Array.isArray(type.fields)) {
|
|
8598
|
+
out.fields = type.fields.map((f) => ({ ...f, type: reader.read(f.type, "field", `fields.${f.name}`, !!f.optional) }));
|
|
8599
|
+
}
|
|
8600
|
+
if (Array.isArray(type.methods)) {
|
|
8601
|
+
out.methods = type.methods.map((m) => {
|
|
8602
|
+
const method = { ...m };
|
|
8603
|
+
if (m.params !== void 0) method.params = reader.params(m.params, "type-method-param", `methods.${m.name}.params`);
|
|
8604
|
+
if (typeof m.returns === "string") method.returns = reader.read(m.returns, "type-method-returns", `methods.${m.name}.returns`);
|
|
8605
|
+
return method;
|
|
8606
|
+
});
|
|
8607
|
+
}
|
|
8608
|
+
if (type.params !== void 0) out.params = reader.params(type.params, "signature-param", "params");
|
|
8609
|
+
if (typeof type.holds === "string") out.holds = reader.read(type.holds, "holds", "holds");
|
|
8610
|
+
if (typeof type.returns === "string") out.returns = reader.read(type.returns, "signature-returns", "returns");
|
|
8611
|
+
return { spec: out, respellings: reader.respellings, problems: reader.problems };
|
|
8612
|
+
}
|
|
8613
|
+
var INTEGER_WORDS = /* @__PURE__ */ new Set([
|
|
8614
|
+
"count",
|
|
8615
|
+
"counts",
|
|
8616
|
+
"size",
|
|
8617
|
+
"sizes",
|
|
8618
|
+
"length",
|
|
8619
|
+
"len",
|
|
8620
|
+
"limit",
|
|
8621
|
+
"limits",
|
|
8622
|
+
"port",
|
|
8623
|
+
"step",
|
|
8624
|
+
"steps",
|
|
8625
|
+
"index",
|
|
8626
|
+
"idx",
|
|
8627
|
+
"depth",
|
|
8628
|
+
"level",
|
|
8629
|
+
"levels",
|
|
8630
|
+
"days",
|
|
8631
|
+
"day",
|
|
8632
|
+
"hours",
|
|
8633
|
+
"minutes",
|
|
8634
|
+
"seconds",
|
|
8635
|
+
"ms",
|
|
8636
|
+
"millis",
|
|
8637
|
+
"milliseconds",
|
|
7892
8638
|
"bytes",
|
|
7893
|
-
"
|
|
7894
|
-
"
|
|
7895
|
-
"
|
|
7896
|
-
"
|
|
7897
|
-
"
|
|
7898
|
-
"
|
|
7899
|
-
"
|
|
7900
|
-
"
|
|
7901
|
-
"
|
|
7902
|
-
"
|
|
7903
|
-
"
|
|
7904
|
-
"
|
|
7905
|
-
"
|
|
7906
|
-
"
|
|
7907
|
-
"
|
|
7908
|
-
"
|
|
7909
|
-
"
|
|
7910
|
-
"vec",
|
|
7911
|
-
"array",
|
|
7912
|
-
"map",
|
|
7913
|
-
"set",
|
|
7914
|
-
"dict",
|
|
7915
|
-
"dictionary",
|
|
7916
|
-
"hashmap",
|
|
7917
|
-
"tuple",
|
|
7918
|
-
"result",
|
|
7919
|
-
"option",
|
|
7920
|
-
"box",
|
|
7921
|
-
"arc",
|
|
7922
|
-
"rc",
|
|
7923
|
-
"ref",
|
|
7924
|
-
"cell",
|
|
7925
|
-
"refcell",
|
|
7926
|
-
"mutex",
|
|
7927
|
-
"rwlock",
|
|
7928
|
-
"std",
|
|
7929
|
-
"promise",
|
|
7930
|
-
"record",
|
|
7931
|
-
"json",
|
|
7932
|
-
"unknown",
|
|
7933
|
-
"never",
|
|
7934
|
-
"error",
|
|
7935
|
-
"mcpserver"
|
|
8639
|
+
"version",
|
|
8640
|
+
"total",
|
|
8641
|
+
"offset",
|
|
8642
|
+
"page",
|
|
8643
|
+
"pages",
|
|
8644
|
+
"retries",
|
|
8645
|
+
"attempts",
|
|
8646
|
+
"number",
|
|
8647
|
+
"num",
|
|
8648
|
+
"line",
|
|
8649
|
+
"lines",
|
|
8650
|
+
"column",
|
|
8651
|
+
"columns",
|
|
8652
|
+
"max",
|
|
8653
|
+
"min",
|
|
8654
|
+
"width",
|
|
8655
|
+
"height"
|
|
7936
8656
|
]);
|
|
8657
|
+
function namesAnInteger(name) {
|
|
8658
|
+
const words2 = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[\s_\-.]+/).filter(Boolean);
|
|
8659
|
+
return words2.some((word) => INTEGER_WORDS.has(word));
|
|
8660
|
+
}
|
|
8661
|
+
function positionNameOf(path49) {
|
|
8662
|
+
const segments = path49.split(".");
|
|
8663
|
+
const last = segments[segments.length - 1];
|
|
8664
|
+
return last === "returns" && segments.length >= 3 ? segments[segments.length - 2] : last;
|
|
8665
|
+
}
|
|
8666
|
+
var NUMBER_NAME = /(?<![\w:.-])number(?![\w:.-])/g;
|
|
8667
|
+
function typeProblemIntProposal(problem) {
|
|
8668
|
+
if (problem.code !== "TYPE_NOT_NEUTRAL" || problem.replacement !== "int or float") return null;
|
|
8669
|
+
if (problem.specId === void 0 || problem.kind === void 0 || problem.path === void 0) return null;
|
|
8670
|
+
if (!namesAnInteger(positionNameOf(problem.path))) return null;
|
|
8671
|
+
const parse = parseTypePosition(problem.written.replace(NUMBER_NAME, "int"), "returns");
|
|
8672
|
+
if (!parse.expression || parse.problem) return null;
|
|
8673
|
+
return { specId: problem.specId, kind: problem.kind, path: problem.path, written: problem.written, stored: parse.canonical };
|
|
8674
|
+
}
|
|
8675
|
+
function emptyTypeSpellingFacts() {
|
|
8676
|
+
return { respellings: [], problems: [] };
|
|
8677
|
+
}
|
|
8678
|
+
|
|
8679
|
+
// src/models/type-references.ts
|
|
7937
8680
|
function extractTypeIdentifiers(typeStr) {
|
|
7938
|
-
let cleaned = typeStr.replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
8681
|
+
let cleaned = typeStr.replace(/^\s*async\s+(?=\S)/, "").replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
7939
8682
|
cleaned = cleaned.replace(/(?<=\S)\s*\([^)]*\)\s*$/, "");
|
|
7940
8683
|
cleaned = cleaned.replace(/(?<=\S)\s+[-—:]\s+[a-z\s_-]+$/, "");
|
|
7941
8684
|
cleaned = cleaned.replace(/(["'`])(?:\\.|[^\\])*?\1/g, " ");
|
|
@@ -8021,16 +8764,19 @@ function methodTypeRefs(m) {
|
|
|
8021
8764
|
if (m.params && m.params.length > 0) {
|
|
8022
8765
|
const refs = [];
|
|
8023
8766
|
for (const p of m.params) {
|
|
8024
|
-
refs.push(...
|
|
8767
|
+
refs.push(...writtenTypeRefs(p.type, "param"));
|
|
8025
8768
|
}
|
|
8026
|
-
refs.push(...
|
|
8769
|
+
if (m.returns !== void 0) refs.push(...writtenTypeRefs(m.returns, "returns"));
|
|
8027
8770
|
return Array.from(new Set(refs));
|
|
8028
8771
|
}
|
|
8029
8772
|
return extractTypesFromSignature(m.signature ?? "", m.returns ?? "");
|
|
8030
8773
|
}
|
|
8031
8774
|
function signatureTypeRefs(type) {
|
|
8032
8775
|
if (type.kind !== "signature") return [];
|
|
8033
|
-
const refs = [
|
|
8776
|
+
const refs = [
|
|
8777
|
+
...(type.params ?? []).flatMap((p) => writtenTypeRefs(p.type, "signature-param")),
|
|
8778
|
+
...type.returns !== void 0 ? writtenTypeRefs(type.returns, "signature-returns") : []
|
|
8779
|
+
];
|
|
8034
8780
|
return Array.from(new Set(refs));
|
|
8035
8781
|
}
|
|
8036
8782
|
function methodGenericParameters(method) {
|
|
@@ -8065,12 +8811,108 @@ function typeGenericParameters(type) {
|
|
|
8065
8811
|
}
|
|
8066
8812
|
function fieldTypeRefs(type, fieldType) {
|
|
8067
8813
|
const generics = new Set(Array.from(typeGenericParameters(type)).map((g) => g.toLowerCase()));
|
|
8068
|
-
return
|
|
8814
|
+
return writtenTypeRefs(fieldType, "field").filter((ref) => !generics.has(ref.toLowerCase()));
|
|
8069
8815
|
}
|
|
8070
8816
|
function interfaceGenericParameters(intf) {
|
|
8071
8817
|
return extractTypeGenerics(intf.name);
|
|
8072
8818
|
}
|
|
8073
8819
|
|
|
8820
|
+
// src/models/type-dialects.ts
|
|
8821
|
+
function lastSegment(name) {
|
|
8822
|
+
return name.split(/::|\./).filter(Boolean).pop() ?? name;
|
|
8823
|
+
}
|
|
8824
|
+
function sameType(code, spec, codeNames) {
|
|
8825
|
+
if (code.form === "primitive" && code.name === "number") return spec.form === "primitive" && (spec.name === "int" || spec.name === "float");
|
|
8826
|
+
if (code.form === "named" && (spec.form === "named" || spec.form === "applied")) {
|
|
8827
|
+
if (spec.form === "applied") return false;
|
|
8828
|
+
return namesAgree(code.name, spec.name, codeNames);
|
|
8829
|
+
}
|
|
8830
|
+
if (code.form !== spec.form) return false;
|
|
8831
|
+
if (code.form === "primitive") return code.name === spec.name;
|
|
8832
|
+
if (code.form === "applied" && !namesAgree(code.name, spec.name, codeNames)) return false;
|
|
8833
|
+
if (code.args.length !== spec.args.length) return false;
|
|
8834
|
+
return code.args.every((arg, i) => sameType(arg, spec.args[i], codeNames));
|
|
8835
|
+
}
|
|
8836
|
+
function namesAgree(codeName, specName, codeNames) {
|
|
8837
|
+
if (codeName === specName) return true;
|
|
8838
|
+
const mapped = codeNames.get(specName) ?? codeNames.get(lastSegment(specName));
|
|
8839
|
+
return mapped === codeName || mapped === lastSegment(codeName);
|
|
8840
|
+
}
|
|
8841
|
+
function writeTypeScript(expr) {
|
|
8842
|
+
switch (expr.form) {
|
|
8843
|
+
case "primitive":
|
|
8844
|
+
return TS_PRIMITIVES[expr.name ?? ""] ?? expr.name ?? "unknown";
|
|
8845
|
+
case "named":
|
|
8846
|
+
return expr.name ?? "unknown";
|
|
8847
|
+
case "list": {
|
|
8848
|
+
const elem = writeTypeScript(expr.args[0]);
|
|
8849
|
+
return /[|\s]/.test(elem) ? `(${elem})[]` : `${elem}[]`;
|
|
8850
|
+
}
|
|
8851
|
+
case "set":
|
|
8852
|
+
return `Set<${writeTypeScript(expr.args[0])}>`;
|
|
8853
|
+
case "map":
|
|
8854
|
+
return `Record<${writeTypeScript(expr.args[0])}, ${writeTypeScript(expr.args[1])}>`;
|
|
8855
|
+
case "optional":
|
|
8856
|
+
return `${writeTypeScript(expr.args[0])} | null`;
|
|
8857
|
+
case "union":
|
|
8858
|
+
return expr.args.map(writeTypeScript).join(" | ");
|
|
8859
|
+
case "async":
|
|
8860
|
+
return `Promise<${writeTypeScript(expr.args[0])}>`;
|
|
8861
|
+
case "applied":
|
|
8862
|
+
return `${expr.name}<${expr.args.map(writeTypeScript).join(", ")}>`;
|
|
8863
|
+
}
|
|
8864
|
+
}
|
|
8865
|
+
var TS_PRIMITIVES = {
|
|
8866
|
+
string: "string",
|
|
8867
|
+
int: "number",
|
|
8868
|
+
float: "number",
|
|
8869
|
+
bool: "boolean",
|
|
8870
|
+
bytes: "Uint8Array",
|
|
8871
|
+
date: "string",
|
|
8872
|
+
datetime: "string",
|
|
8873
|
+
duration: "string",
|
|
8874
|
+
void: "void",
|
|
8875
|
+
any: "unknown"
|
|
8876
|
+
};
|
|
8877
|
+
var typescriptDialect = {
|
|
8878
|
+
language: "typescript",
|
|
8879
|
+
read(annotation) {
|
|
8880
|
+
return readCodeAnnotation(annotation, false);
|
|
8881
|
+
},
|
|
8882
|
+
agrees(annotation, expression, codeNames) {
|
|
8883
|
+
const code = readCodeAnnotation(annotation, true);
|
|
8884
|
+
return code !== null && sameType(code, expression, codeNames);
|
|
8885
|
+
},
|
|
8886
|
+
write(expression) {
|
|
8887
|
+
return writeTypeScript(expression);
|
|
8888
|
+
},
|
|
8889
|
+
mappingLines() {
|
|
8890
|
+
return [
|
|
8891
|
+
"string \u2192 string",
|
|
8892
|
+
"int, float \u2192 number",
|
|
8893
|
+
"bool \u2192 boolean",
|
|
8894
|
+
"bytes \u2192 Uint8Array",
|
|
8895
|
+
"date, datetime, duration \u2192 string (ISO 8601)",
|
|
8896
|
+
"any \u2192 unknown",
|
|
8897
|
+
"list<T> \u2192 T[]",
|
|
8898
|
+
"set<T> \u2192 Set<T>",
|
|
8899
|
+
"map<K, V> \u2192 Record<K, V>",
|
|
8900
|
+
"T? \u2192 T | null",
|
|
8901
|
+
"A | B \u2192 A | B",
|
|
8902
|
+
"async T \u2192 Promise<T>",
|
|
8903
|
+
"an enum E \u2192 type E = 'a' | 'b' (a string-literal union alias)",
|
|
8904
|
+
"a named scalar E holding P \u2192 type E = P (an alias of the primitive)"
|
|
8905
|
+
];
|
|
8906
|
+
}
|
|
8907
|
+
};
|
|
8908
|
+
var DIALECTS = /* @__PURE__ */ new Map([
|
|
8909
|
+
["typescript", typescriptDialect],
|
|
8910
|
+
["javascript", typescriptDialect]
|
|
8911
|
+
]);
|
|
8912
|
+
function typeDialectFor(language) {
|
|
8913
|
+
return DIALECTS.get(language.toLowerCase()) ?? null;
|
|
8914
|
+
}
|
|
8915
|
+
|
|
8074
8916
|
// src/models/step-graph.ts
|
|
8075
8917
|
function stepGraph(method) {
|
|
8076
8918
|
const steps = method.narrative;
|
|
@@ -8365,6 +9207,36 @@ function callSitesOf(facts, fn) {
|
|
|
8365
9207
|
function hasFunctionBody(facts, symbol) {
|
|
8366
9208
|
return callSitesOf(facts, symbol) !== void 0;
|
|
8367
9209
|
}
|
|
9210
|
+
var EXTENSION_LANGUAGE = {
|
|
9211
|
+
".ts": "typescript",
|
|
9212
|
+
".tsx": "typescript",
|
|
9213
|
+
".mts": "typescript",
|
|
9214
|
+
".cts": "typescript",
|
|
9215
|
+
".js": "javascript",
|
|
9216
|
+
".jsx": "javascript",
|
|
9217
|
+
".mjs": "javascript",
|
|
9218
|
+
".cjs": "javascript",
|
|
9219
|
+
".py": "python",
|
|
9220
|
+
".rs": "rust",
|
|
9221
|
+
".go": "go",
|
|
9222
|
+
".cs": "csharp",
|
|
9223
|
+
".java": "java",
|
|
9224
|
+
".c": "c",
|
|
9225
|
+
".h": "c",
|
|
9226
|
+
".cpp": "cpp",
|
|
9227
|
+
".cc": "cpp",
|
|
9228
|
+
".hpp": "cpp",
|
|
9229
|
+
".rb": "ruby",
|
|
9230
|
+
".php": "php",
|
|
9231
|
+
".kt": "kotlin",
|
|
9232
|
+
".swift": "swift"
|
|
9233
|
+
};
|
|
9234
|
+
function languageOfSourcePath(sourcePath) {
|
|
9235
|
+
return EXTENSION_LANGUAGE[path9.extname(sourcePath).toLowerCase()];
|
|
9236
|
+
}
|
|
9237
|
+
function dialectOf(facts) {
|
|
9238
|
+
return facts.language ? typeDialectFor(facts.language) : null;
|
|
9239
|
+
}
|
|
8368
9240
|
function resolveImport(fromFile, specifier, knownPaths) {
|
|
8369
9241
|
if (!specifier.startsWith(".")) return void 0;
|
|
8370
9242
|
const joined = pathKey(path9.posix.normalize(path9.posix.join(path9.posix.dirname(fromFile), specifier)));
|
|
@@ -8410,11 +9282,18 @@ function closureTypeOf(snapshot, identifier) {
|
|
|
8410
9282
|
if (matches.length === 1) return matches[0];
|
|
8411
9283
|
return matches.find((def) => nameKey(def.id) === nameKey(ref));
|
|
8412
9284
|
}
|
|
9285
|
+
function renamedRefs(expr, rename) {
|
|
9286
|
+
const args = expr.args.map((a) => renamedRefs(a, rename));
|
|
9287
|
+
return expr.form === "named" || expr.form === "applied" ? { ...expr, name: rename(expr.name), args } : { ...expr, args };
|
|
9288
|
+
}
|
|
8413
9289
|
function canonicalTypeRef(snapshot, typeExpr) {
|
|
9290
|
+
const rename = (identifier) => closureTypeOf(snapshot, identifier)?.id ?? identifier;
|
|
9291
|
+
const parse = parseTypeExpression(typeExpr, "returns");
|
|
9292
|
+
if (parse.expression) return canonicalTypeText(renamedRefs(parse.expression, rename));
|
|
8414
9293
|
return typeExpr.replace(TYPE_IDENTIFIER, (identifier) => {
|
|
8415
9294
|
if (!/[A-Za-z]/.test(identifier)) return identifier;
|
|
8416
|
-
if (
|
|
8417
|
-
return
|
|
9295
|
+
if (isTypeVocabulary(identifier)) return identifier.toLowerCase();
|
|
9296
|
+
return rename(identifier);
|
|
8418
9297
|
});
|
|
8419
9298
|
}
|
|
8420
9299
|
function typeShape(snapshot, def) {
|
|
@@ -8426,7 +9305,9 @@ function typeShape(snapshot, def) {
|
|
|
8426
9305
|
...def.kind === "signature" ? {
|
|
8427
9306
|
params: (def.params ?? []).map((p) => ({ type: canonicalTypeRef(snapshot, p.type), optional: p.optional === true })),
|
|
8428
9307
|
returns: canonicalTypeRef(snapshot, def.returns ?? "unknown")
|
|
8429
|
-
} : {}
|
|
9308
|
+
} : {},
|
|
9309
|
+
...def.kind === "enum" ? { values: (def.values ?? []).map((v) => v.name) } : {},
|
|
9310
|
+
...def.holds !== void 0 ? { holds: canonicalTypeRef(snapshot, def.holds) } : {}
|
|
8430
9311
|
};
|
|
8431
9312
|
}
|
|
8432
9313
|
function typeDefExprs(def) {
|
|
@@ -8694,7 +9575,7 @@ function shortestPath(from, to, members, edges, rank) {
|
|
|
8694
9575
|
// src/models/validation-options.ts
|
|
8695
9576
|
function selectsFamily(options, declaresMembers) {
|
|
8696
9577
|
if (options?.family) return true;
|
|
8697
|
-
return declaresMembers && options?.
|
|
9578
|
+
return declaresMembers && options?.memberDepth !== 0;
|
|
8698
9579
|
}
|
|
8699
9580
|
|
|
8700
9581
|
// src/core/narrative-labels.ts
|
|
@@ -9423,17 +10304,23 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
9423
10304
|
})),
|
|
9424
10305
|
methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, returns: m.returns, ...m.description ? { description: m.description } : {} })),
|
|
9425
10306
|
...t.kind === "signature" ? { signature: deriveTypeSignature(t) } : {},
|
|
10307
|
+
...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
|
|
10308
|
+
...t.holds !== void 0 ? { holds: t.holds } : {},
|
|
9426
10309
|
usedBy: usedByFor(t),
|
|
9427
10310
|
...t.componentClass ? { componentClass: t.componentClass } : {},
|
|
9428
10311
|
...t.database ? { database: t.database } : {},
|
|
9429
10312
|
...t.table ? { table: t.table } : {},
|
|
9430
10313
|
...t.linkedEntity ? { linkedEntity: t.linkedEntity } : {}
|
|
9431
10314
|
}));
|
|
9432
|
-
const
|
|
10315
|
+
const multiplicity = (field) => {
|
|
10316
|
+
const expression = parseTypeExpression(field.type, "field").expression;
|
|
10317
|
+
if (expression && typeIsMany(expression)) return "*";
|
|
10318
|
+
return field.optional || expression?.form === "optional" ? "0..1" : "1";
|
|
10319
|
+
};
|
|
9433
10320
|
const typeEdges = [];
|
|
9434
10321
|
for (const t of typeSpecs) {
|
|
9435
10322
|
for (const field of t.fields) {
|
|
9436
|
-
const refs = new Set(
|
|
10323
|
+
const refs = new Set(writtenTypeRefs(field.type, "field"));
|
|
9437
10324
|
if (field.references) {
|
|
9438
10325
|
const refStr = field.references;
|
|
9439
10326
|
refs.add(refStr);
|
|
@@ -9448,8 +10335,8 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
9448
10335
|
const qualified = other.subsystem && !other.id.startsWith(`${other.subsystem}::`) ? `${other.subsystem}::${other.id}` : other.id;
|
|
9449
10336
|
return matchTypeRef(ref, qualified);
|
|
9450
10337
|
});
|
|
9451
|
-
if (target && target.id !== t.id) {
|
|
9452
|
-
const card =
|
|
10338
|
+
if (target && target.id !== t.id && target.kind !== "enum" && target.holds === void 0) {
|
|
10339
|
+
const card = multiplicity(field);
|
|
9453
10340
|
if (!typeEdges.some((e) => e.from === t.id && e.to === target.id && e.field === field.name)) {
|
|
9454
10341
|
typeEdges.push({ from: t.id, to: target.id, field: field.name, card });
|
|
9455
10342
|
}
|
|
@@ -11114,7 +12001,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
11114
12001
|
return '';
|
|
11115
12002
|
}
|
|
11116
12003
|
function visibleFields(t) {
|
|
11117
|
-
if (det === 'names') return [];
|
|
12004
|
+
if (det === 'names' || t.values || t.holds) return [];
|
|
11118
12005
|
if (det === 'keys') return t.fields.filter(function (f) { return markerOf(t, f) !== ''; });
|
|
11119
12006
|
return t.fields;
|
|
11120
12007
|
}
|
|
@@ -11159,15 +12046,19 @@ var MODEL = __MODEL_JSON__;
|
|
|
11159
12046
|
var meths = det === 'full' ? t.methods : [];
|
|
11160
12047
|
// A signature type is drawn with its derived text in place of a field list.
|
|
11161
12048
|
var sig = t.signature && det !== 'names' ? t.signature : '';
|
|
11162
|
-
|
|
11163
|
-
var
|
|
12049
|
+
// An enum is drawn with its values in place of a field list.
|
|
12050
|
+
var vals = t.values && det !== 'names' ? t.values : [];
|
|
12051
|
+
// A named scalar is drawn compactly: its name and the primitive it holds.
|
|
12052
|
+
var head = t.name + (t.holds ? ' = ' + t.holds : '') + ' \\u00AB' + t.kind + '\\u00BB';
|
|
12053
|
+
var rows = (sig ? [sig] : []).concat(vals.map(function (v) { return '\\u2022 ' + v.name; }))
|
|
12054
|
+
.concat(fields.map(function (f) { return rowText(t, f); }))
|
|
11164
12055
|
.concat(meths.map(function (m) { return '\\u0192 ' + m.name + '(): ' + m.returns; }));
|
|
11165
12056
|
var longest = head.length + 4;
|
|
11166
12057
|
rows.forEach(function (r) { if (r.length > longest) longest = r.length; });
|
|
11167
12058
|
var plain = rows.length === 0;
|
|
11168
12059
|
var pw = Math.max(170, head.length * 6.8 + 26);
|
|
11169
12060
|
var W = Math.max(210, Math.min(400, longest * 6.6 + 30));
|
|
11170
|
-
return { fields: fields, meths: meths, sig: sig, head: head, plain: plain, w: plain ? pw : W, h: plain ? 40 : TH_H + (sig ? ROW_H : 0) + fields.length * ROW_H + meths.length * ROW_H };
|
|
12061
|
+
return { fields: fields, meths: meths, sig: sig, vals: vals, head: head, plain: plain, w: plain ? pw : W, h: plain ? 40 : TH_H + (sig ? ROW_H : 0) + vals.length * ROW_H + fields.length * ROW_H + meths.length * ROW_H };
|
|
11171
12062
|
}
|
|
11172
12063
|
|
|
11173
12064
|
// Emit one type table with its top-left at (ax, ay); returns its size.
|
|
@@ -11198,6 +12089,13 @@ var MODEL = __MODEL_JSON__;
|
|
|
11198
12089
|
});
|
|
11199
12090
|
ry += ROW_H;
|
|
11200
12091
|
}
|
|
12092
|
+
sh.vals.forEach(function (v, vi) {
|
|
12093
|
+
eles.push({
|
|
12094
|
+
data: { id: 'TV~' + t.id + '~' + vi, parent: 'T~' + t.id, label: '\\u2022 ' + v.name, w: sh.w, h: ROW_H, tw: sh.w - 14 },
|
|
12095
|
+
position: { x: ax + sh.w / 2, y: ry + ROW_H / 2 }, classes: 'typeRow' + (dim ? ' dimmed' : ''), grabbable: false,
|
|
12096
|
+
});
|
|
12097
|
+
ry += ROW_H;
|
|
12098
|
+
});
|
|
11201
12099
|
sh.fields.forEach(function (f) {
|
|
11202
12100
|
var rid = 'TF~' + t.id + '~' + f.name;
|
|
11203
12101
|
rowIds[rid] = 1;
|
|
@@ -13731,6 +14629,17 @@ var MODEL = __MODEL_JSON__;
|
|
|
13731
14629
|
: '<span class="desc">no fields</span>';
|
|
13732
14630
|
if (ty.signature) {
|
|
13733
14631
|
body += section('Signature', 1, '<div class="method"><code>' + esc(ty.signature) + '</code></div>', true);
|
|
14632
|
+
} else if (ty.holds) {
|
|
14633
|
+
body += section('Holds', 1, '<div class="method"><code>' + esc(ty.holds) + '</code>'
|
|
14634
|
+
+ '<div class="mdesc">A named scalar: one ' + esc(ty.holds) + ' under this name.</div></div>', true);
|
|
14635
|
+
} else if (ty.values) {
|
|
14636
|
+
body += section('Values', ty.values.length, ty.values.length
|
|
14637
|
+
? ty.values.map(function (v) {
|
|
14638
|
+
return '<div class="method"><div class="mname">' + esc(v.name) + '</div>'
|
|
14639
|
+
+ (v.description ? '<div class="mdesc">' + esc(v.description) + '</div>' : '')
|
|
14640
|
+
+ '</div>';
|
|
14641
|
+
}).join('')
|
|
14642
|
+
: '<span class="desc">no values</span>', true);
|
|
13734
14643
|
} else {
|
|
13735
14644
|
body += section('Fields', ty.fields.length, fieldsInner, true);
|
|
13736
14645
|
}
|
|
@@ -14056,7 +14965,7 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
14056
14965
|
queue.push(key);
|
|
14057
14966
|
};
|
|
14058
14967
|
const enqueueRef = (ref) => {
|
|
14059
|
-
if (
|
|
14968
|
+
if (isTypeVocabulary(ref)) return;
|
|
14060
14969
|
for (const spec of types) {
|
|
14061
14970
|
if (matchTypeRef(ref, qualifiedTypeId2(spec))) include(spec);
|
|
14062
14971
|
}
|
|
@@ -14070,13 +14979,13 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
14070
14979
|
while (queue.length) {
|
|
14071
14980
|
const spec = included.get(queue.shift());
|
|
14072
14981
|
for (const field of spec.fields) {
|
|
14073
|
-
for (const ref of
|
|
14982
|
+
for (const ref of writtenTypeRefs(field.type, "field")) enqueueRef(ref);
|
|
14074
14983
|
}
|
|
14075
14984
|
for (const param of spec.params ?? []) {
|
|
14076
|
-
for (const ref of
|
|
14985
|
+
for (const ref of writtenTypeRefs(param.type, "signature-param")) enqueueRef(ref);
|
|
14077
14986
|
}
|
|
14078
14987
|
if (spec.returns) {
|
|
14079
|
-
for (const ref of
|
|
14988
|
+
for (const ref of writtenTypeRefs(spec.returns, "signature-returns")) enqueueRef(ref);
|
|
14080
14989
|
}
|
|
14081
14990
|
}
|
|
14082
14991
|
const usedIds = /* @__PURE__ */ new Set();
|
|
@@ -14095,7 +15004,11 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
14095
15004
|
kind: t.kind,
|
|
14096
15005
|
fields: t.fields.map(describe2),
|
|
14097
15006
|
// A signature type travels complete: its params and returns with it.
|
|
14098
|
-
...t.kind === "signature" ? { params: (t.params ?? []).map(describe2), returns: t.returns ?? "
|
|
15007
|
+
...t.kind === "signature" ? { params: (t.params ?? []).map(describe2), returns: t.returns ?? "any" } : {},
|
|
15008
|
+
// An enum travels with its values, in declared order.
|
|
15009
|
+
...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
|
|
15010
|
+
// A named scalar travels with the primitive it holds.
|
|
15011
|
+
...t.holds !== void 0 ? { holds: t.holds } : {}
|
|
14099
15012
|
};
|
|
14100
15013
|
});
|
|
14101
15014
|
}
|
|
@@ -14714,7 +15627,7 @@ function depthOf(family, node) {
|
|
|
14714
15627
|
return depth;
|
|
14715
15628
|
}
|
|
14716
15629
|
function selectProjects(family, options) {
|
|
14717
|
-
const limit =
|
|
15630
|
+
const limit = options.memberDepth ?? Infinity;
|
|
14718
15631
|
const nodes = family.nodes.filter((n) => depthOf(family, n) <= limit);
|
|
14719
15632
|
const scope = options.scopeSubsystem;
|
|
14720
15633
|
if (!scope) return nodes.map((node) => ({ node }));
|
|
@@ -15242,7 +16155,7 @@ function defaultTargetConfig(type) {
|
|
|
15242
16155
|
enabled: true
|
|
15243
16156
|
};
|
|
15244
16157
|
}
|
|
15245
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
16158
|
+
var WAIRON_VERSION = "5.1.1-dev.97";
|
|
15246
16159
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
15247
16160
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
15248
16161
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -15469,7 +16382,7 @@ var ProfileDefSchema = import_zod9.z.object({
|
|
|
15469
16382
|
});
|
|
15470
16383
|
var LanguagePackDefSchema = import_zod9.z.object({
|
|
15471
16384
|
unsupportedFlow: import_zod9.z.record(import_zod9.z.string()).default({}),
|
|
15472
|
-
foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).
|
|
16385
|
+
foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).optional()
|
|
15473
16386
|
});
|
|
15474
16387
|
var PackSkillSchema = import_zod9.z.object({
|
|
15475
16388
|
/** A NEW skill, installed namespaced as `<pack-id>-<id>`. Mutually exclusive with `extends`. */
|
|
@@ -15603,8 +16516,9 @@ function globalPacksEnabled(config) {
|
|
|
15603
16516
|
return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
|
|
15604
16517
|
}
|
|
15605
16518
|
function emptyExtensions() {
|
|
15606
|
-
return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
|
|
16519
|
+
return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [], deprecations: [] };
|
|
15607
16520
|
}
|
|
16521
|
+
var FOREIGN_BUILTINS_RETIRED = "nothing replaces it: contracts are written in the neutral type grammar and a language is chosen at L4, so the foreign-builtin check it fed is retired";
|
|
15608
16522
|
function globalPacksDir() {
|
|
15609
16523
|
return process.env.WAIRON_PACKS_DIR ?? path17.join(os3.homedir(), ".wairon", "packs");
|
|
15610
16524
|
}
|
|
@@ -15665,11 +16579,11 @@ function mergePack(out, pack, ref, scope, packDir) {
|
|
|
15665
16579
|
Object.assign(out.profiles, pack.profiles);
|
|
15666
16580
|
for (const [lang, def] of Object.entries(pack.languages)) {
|
|
15667
16581
|
const key = lang.toLowerCase();
|
|
16582
|
+
if (def.foreignBuiltins !== void 0) {
|
|
16583
|
+
out.deprecations.push(`${pack.name}: languages.${lang}.foreignBuiltins \u2014 ${FOREIGN_BUILTINS_RETIRED}`);
|
|
16584
|
+
}
|
|
15668
16585
|
const existing = out.languages[key];
|
|
15669
|
-
out.languages[key] = existing
|
|
15670
|
-
unsupportedFlow: { ...existing.unsupportedFlow, ...def.unsupportedFlow },
|
|
15671
|
-
foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
|
|
15672
|
-
} : def;
|
|
16586
|
+
out.languages[key] = { unsupportedFlow: { ...existing?.unsupportedFlow, ...def.unsupportedFlow } };
|
|
15673
16587
|
}
|
|
15674
16588
|
for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path17.resolve(packDir, s.source) });
|
|
15675
16589
|
for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
|
|
@@ -16217,7 +17131,7 @@ function buildImportGraph(index, universe) {
|
|
|
16217
17131
|
function buildOwnershipIndex(ctx) {
|
|
16218
17132
|
const ownedBy2 = /* @__PURE__ */ new Map();
|
|
16219
17133
|
for (const comp of ctx.components) {
|
|
16220
|
-
if (isRetired(comp) || !isPattern(comp)) continue;
|
|
17134
|
+
if (isRetired(comp) || !(isPattern(comp) || comp.componentType === "Supervisor")) continue;
|
|
16221
17135
|
for (const memberId of comp.owns) {
|
|
16222
17136
|
const member = ctx.componentMap.get(memberId);
|
|
16223
17137
|
if (!member || isPattern(member)) continue;
|
|
@@ -16275,7 +17189,7 @@ function buildImplementationMethods(ctx) {
|
|
|
16275
17189
|
const component = ctx.componentMap.get(contract.component);
|
|
16276
17190
|
if (!component) continue;
|
|
16277
17191
|
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
16278
|
-
const
|
|
17192
|
+
const draftContext4 = ctx.isImplementationDraft(impl);
|
|
16279
17193
|
for (const method of impl.methods) {
|
|
16280
17194
|
const sourceFile = methodSourceFile(method, impl.sourcePath);
|
|
16281
17195
|
out.push({
|
|
@@ -16283,7 +17197,7 @@ function buildImplementationMethods(ctx) {
|
|
|
16283
17197
|
method,
|
|
16284
17198
|
component,
|
|
16285
17199
|
...sourceFile !== void 0 ? { sourceFile } : {},
|
|
16286
|
-
draftContext:
|
|
17200
|
+
draftContext: draftContext4
|
|
16287
17201
|
});
|
|
16288
17202
|
}
|
|
16289
17203
|
}
|
|
@@ -16521,7 +17435,7 @@ var roundtripRule = {
|
|
|
16521
17435
|
var typeDeclarationsRule = {
|
|
16522
17436
|
name: "type-declarations",
|
|
16523
17437
|
judges: "design",
|
|
16524
|
-
description: "A type owned by a subsystem must reference an existing one, and a type declaring neither fields nor methods is a placeholder that can inform neither implementers nor the ERD.",
|
|
17438
|
+
description: "A type owned by a subsystem must reference an existing one, and a type declaring neither fields nor methods (nor, for an enum, values, nor, for a signature type, returns, nor, for a named scalar, the primitive it holds) is a placeholder that can inform neither implementers nor the ERD.",
|
|
16525
17439
|
codes: [
|
|
16526
17440
|
{ code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
|
|
16527
17441
|
{ code: "HOLLOW_TYPE", defaultSeverity: "warning", summary: "Type declares no fields and no methods \u2014 a placeholder that informs neither implementers nor the ERD" }
|
|
@@ -16539,7 +17453,8 @@ var typeDeclarationsRule = {
|
|
|
16539
17453
|
isDraftCtx
|
|
16540
17454
|
);
|
|
16541
17455
|
}
|
|
16542
|
-
|
|
17456
|
+
const models = (t.fields?.length ?? 0) > 0 || (t.methods?.length ?? 0) > 0 || (t.values?.length ?? 0) > 0 || t.returns !== void 0 || t.holds !== void 0;
|
|
17457
|
+
if (!models) {
|
|
16543
17458
|
ctx.addIssue(
|
|
16544
17459
|
"warning",
|
|
16545
17460
|
"HOLLOW_TYPE",
|
|
@@ -16776,6 +17691,7 @@ var signatureTypesRule = {
|
|
|
16776
17691
|
);
|
|
16777
17692
|
continue;
|
|
16778
17693
|
}
|
|
17694
|
+
if (t.kind === "enum") continue;
|
|
16779
17695
|
const stated = [...t.params !== void 0 ? ["params"] : [], ...t.returns !== void 0 ? ["returns"] : []];
|
|
16780
17696
|
if (stated.length === 0) continue;
|
|
16781
17697
|
ctx.addIssue(
|
|
@@ -16822,6 +17738,184 @@ var signatureTextRule = {
|
|
|
16822
17738
|
}
|
|
16823
17739
|
};
|
|
16824
17740
|
|
|
17741
|
+
// src/core/rules/integrity/type-expressions.ts
|
|
17742
|
+
function draftContext3(ctx, specId, kind) {
|
|
17743
|
+
if (kind === "interface") {
|
|
17744
|
+
const intf = ctx.interfaceMap.get(specId);
|
|
17745
|
+
return !!intf && (ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design");
|
|
17746
|
+
}
|
|
17747
|
+
const type = ctx.types.find((t) => t.id === specId);
|
|
17748
|
+
const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
|
|
17749
|
+
return !!sub && (sub.status === "draft" || sub.status === "design");
|
|
17750
|
+
}
|
|
17751
|
+
function siteOf(path49) {
|
|
17752
|
+
const [head2, name] = path49.split(".");
|
|
17753
|
+
return (head2 === "methods" || head2 === "fields") && name ? name : path49;
|
|
17754
|
+
}
|
|
17755
|
+
function located(kind, specId, path49) {
|
|
17756
|
+
return `${path49 ? `"${path49}" of ` : ""}${kind ?? "spec"} "${specId}"`;
|
|
17757
|
+
}
|
|
17758
|
+
function problemMessage(problem, specId) {
|
|
17759
|
+
const where = located(problem.kind, specId, problem.path);
|
|
17760
|
+
const replacement = problem.replacement ? ` Write ${problem.replacement} instead.` : "";
|
|
17761
|
+
switch (problem.code) {
|
|
17762
|
+
case "TYPE_EXPRESSION_INVALID":
|
|
17763
|
+
return `The type at ${where} does not parse under the type grammar: ${problem.detail}.${replacement} Every reader treats the position as opaque any until it does.`;
|
|
17764
|
+
case "TYPE_POSITION_INVALID":
|
|
17765
|
+
return `The type at ${where} breaks a position rule: ${problem.detail}.${replacement}`;
|
|
17766
|
+
case "TYPE_FORM_UNSUPPORTED":
|
|
17767
|
+
return `The type at ${where} uses a form the type grammar leaves out: ${problem.detail}.${replacement} Every reader treats the position as opaque any until it is remodelled; the writer refuses it in a new write.`;
|
|
17768
|
+
case "TYPE_NOT_NEUTRAL": {
|
|
17769
|
+
if (problem.replacement === "int or float") {
|
|
17770
|
+
const proposal = typeProblemIntProposal({ ...problem, specId }) ? ` Its name says a whole number, so \`wairon doctor\` proposes int here \u2014 confirm it by writing int, or write float.` : " Write int or float.";
|
|
17771
|
+
return `The type at ${where} is "${problem.written}": number does not say whether it holds an integer \u2014 int or float?${proposal} Until then it is read as float.`;
|
|
17772
|
+
}
|
|
17773
|
+
return `The type at ${where} is "${problem.written}": ${problem.detail}, so it is read as any.${replacement}`;
|
|
17774
|
+
}
|
|
17775
|
+
}
|
|
17776
|
+
}
|
|
17777
|
+
var typeExpressionsRule = {
|
|
17778
|
+
name: "type-expressions",
|
|
17779
|
+
judges: "design",
|
|
17780
|
+
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 async 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, an inline object or a mixed union a named value-object. The writer refuses all but the stale spelling at write time; these findings are how a tree that held them before the grammar existed is told, while it still loads and its consumers read the position as opaque any \u2014 which is why only what no real tree holds (a text that does not parse, a broken position rule) is an error. No finding when the run carries no facts (a candidate run).",
|
|
17781
|
+
codes: [
|
|
17782
|
+
{ code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "error", summary: "A structured type position does not parse under the type grammar" },
|
|
17783
|
+
{ code: "TYPE_POSITION_INVALID", defaultSeverity: "error", summary: "A type position breaks a position rule: void or async out of place, a non-scalar map key, or T??" },
|
|
17784
|
+
{ 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" },
|
|
17785
|
+
{ 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" },
|
|
17786
|
+
{ 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" }
|
|
17787
|
+
],
|
|
17788
|
+
check(ctx) {
|
|
17789
|
+
const facts = ctx.typeSpellingFacts;
|
|
17790
|
+
if (!facts) return;
|
|
17791
|
+
for (const problem of facts.problems) {
|
|
17792
|
+
if (problem.specId === void 0 || !ctx.isSpecInScope(problem.specId)) continue;
|
|
17793
|
+
const severity = problem.code === "TYPE_EXPRESSION_INVALID" || problem.code === "TYPE_POSITION_INVALID" ? "error" : "warning";
|
|
17794
|
+
ctx.addIssue(
|
|
17795
|
+
severity,
|
|
17796
|
+
problem.code,
|
|
17797
|
+
problemMessage(problem, problem.specId),
|
|
17798
|
+
problem.specId,
|
|
17799
|
+
draftContext3(ctx, problem.specId, problem.kind),
|
|
17800
|
+
void 0,
|
|
17801
|
+
problem.path ? { at: siteOf(problem.path) } : void 0
|
|
17802
|
+
);
|
|
17803
|
+
}
|
|
17804
|
+
for (const respelling of facts.respellings) {
|
|
17805
|
+
if (!ctx.isSpecInScope(respelling.specId)) continue;
|
|
17806
|
+
ctx.addIssue(
|
|
17807
|
+
"warning",
|
|
17808
|
+
"TYPE_SPELLING_STALE",
|
|
17809
|
+
`The type at ${located(respelling.kind, respelling.specId, respelling.path)} is stored as "${respelling.written}", an alias of its canonical spelling "${respelling.stored}" \u2014 which is what every reader is shown. Any save of the spec writes the canonical text, and \`wairon doctor --fix\` rewrites every stale spelling at once.`,
|
|
17810
|
+
respelling.specId,
|
|
17811
|
+
draftContext3(ctx, respelling.specId, respelling.kind),
|
|
17812
|
+
void 0,
|
|
17813
|
+
{ at: siteOf(respelling.path) }
|
|
17814
|
+
);
|
|
17815
|
+
}
|
|
17816
|
+
}
|
|
17817
|
+
};
|
|
17818
|
+
|
|
17819
|
+
// src/core/rules/integrity/enum-types.ts
|
|
17820
|
+
var enumTypesRule = {
|
|
17821
|
+
name: "enum-types",
|
|
17822
|
+
judges: "design",
|
|
17823
|
+
description: "An enum is a closed, ordered set of named values with optional pure methods, nothing else: an enum without values, with two values equal by reference_resolution.nameKey (every language derives one identifier from both), or carrying fields, params, returns, invariants, componentClass, database, table or linkedEntity is reported; so are values on a type of any other kind, where they mean nothing.",
|
|
17824
|
+
codes: [
|
|
17825
|
+
{ code: "ENUM_MEMBERS", defaultSeverity: "error", summary: "An enum has no values, two values equal by nameKey, or a member it cannot carry; or a type of another kind carries values" }
|
|
17826
|
+
],
|
|
17827
|
+
check(ctx) {
|
|
17828
|
+
for (const t of ctx.types) {
|
|
17829
|
+
if (!ctx.isSpecInScope(t.id)) continue;
|
|
17830
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
17831
|
+
const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
17832
|
+
if (t.kind === "enum") {
|
|
17833
|
+
const values = t.values ?? [];
|
|
17834
|
+
const problems = [];
|
|
17835
|
+
if (values.length === 0) problems.push("lists no values");
|
|
17836
|
+
const seen = /* @__PURE__ */ new Map();
|
|
17837
|
+
for (const value of values) {
|
|
17838
|
+
const key = nameKey(value.name);
|
|
17839
|
+
const earlier = seen.get(key);
|
|
17840
|
+
if (earlier !== void 0) problems.push(`lists "${earlier}" and "${value.name}", which every language derives one identifier from`);
|
|
17841
|
+
else seen.set(key, value.name);
|
|
17842
|
+
}
|
|
17843
|
+
const members = [
|
|
17844
|
+
...t.fields.length > 0 ? ["fields"] : [],
|
|
17845
|
+
...t.params !== void 0 ? ["params"] : [],
|
|
17846
|
+
...t.returns !== void 0 ? ["returns"] : [],
|
|
17847
|
+
...(t.invariants?.length ?? 0) > 0 ? ["invariants"] : [],
|
|
17848
|
+
...t.componentClass !== void 0 ? ["componentClass"] : [],
|
|
17849
|
+
...t.database !== void 0 ? ["database"] : [],
|
|
17850
|
+
...t.table !== void 0 ? ["table"] : [],
|
|
17851
|
+
...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
|
|
17852
|
+
];
|
|
17853
|
+
if (members.length > 0) problems.push(`carries ${members.join(", ")}`);
|
|
17854
|
+
if (problems.length === 0) continue;
|
|
17855
|
+
ctx.addIssue(
|
|
17856
|
+
"error",
|
|
17857
|
+
"ENUM_MEMBERS",
|
|
17858
|
+
`Enum type "${t.id}" ${problems.join("; ")}. An enum is a closed, ordered set of named values, unique by name ignoring case and separators, with optional pure methods and nothing else.`,
|
|
17859
|
+
t.id,
|
|
17860
|
+
isDraft
|
|
17861
|
+
);
|
|
17862
|
+
continue;
|
|
17863
|
+
}
|
|
17864
|
+
if (t.values === void 0) continue;
|
|
17865
|
+
ctx.addIssue(
|
|
17866
|
+
"error",
|
|
17867
|
+
"ENUM_MEMBERS",
|
|
17868
|
+
`Type "${t.id}" is ${t.kind === "entity" ? "an entity" : `a ${t.kind}`} and carries values, which only an enum has. Make it kind enum, or drop them.`,
|
|
17869
|
+
t.id,
|
|
17870
|
+
isDraft
|
|
17871
|
+
);
|
|
17872
|
+
}
|
|
17873
|
+
}
|
|
17874
|
+
};
|
|
17875
|
+
|
|
17876
|
+
// src/core/rules/integrity/named-scalar-types.ts
|
|
17877
|
+
var namedScalarTypesRule = {
|
|
17878
|
+
name: "named-scalar-types",
|
|
17879
|
+
judges: "design",
|
|
17880
|
+
description: "A named scalar is a value-object that declares `holds: <primitive>` in place of fields \u2014 a newtype or type alias in every language. It models its value and nothing else beside its pure methods and invariants: holds beside fields, componentClass, database, table or linkedEntity is reported, and so is holds on an entity, an enum or a signature type, where it means nothing. Whether holds names one of the primitives is the type-expressions rule's question (TYPE_POSITION_INVALID on the holds position), never a second voice here. Mirrors enum-types and signature-types: judged at validate time, on the type's own fields.",
|
|
17881
|
+
codes: [
|
|
17882
|
+
{ code: "NAMED_SCALAR_MEMBERS", defaultSeverity: "error", summary: "A named scalar (a value-object holding a primitive) carries fields or a member it cannot carry; or a type of another kind declares holds" }
|
|
17883
|
+
],
|
|
17884
|
+
check(ctx) {
|
|
17885
|
+
for (const t of ctx.types) {
|
|
17886
|
+
if (!ctx.isSpecInScope(t.id)) continue;
|
|
17887
|
+
if (t.holds === void 0) continue;
|
|
17888
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
17889
|
+
const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
17890
|
+
if (t.kind !== "value-object") {
|
|
17891
|
+
ctx.addIssue(
|
|
17892
|
+
"error",
|
|
17893
|
+
"NAMED_SCALAR_MEMBERS",
|
|
17894
|
+
`Type "${t.id}" is ${t.kind === "entity" ? "an entity" : `a ${t.kind}`} and declares holds, which only a value-object has: a named scalar is a value-object holding one primitive. Make it a value-object, or drop holds.`,
|
|
17895
|
+
t.id,
|
|
17896
|
+
isDraft
|
|
17897
|
+
);
|
|
17898
|
+
continue;
|
|
17899
|
+
}
|
|
17900
|
+
const members = [
|
|
17901
|
+
...t.fields.length > 0 ? ["fields"] : [],
|
|
17902
|
+
...t.componentClass !== void 0 ? ["componentClass"] : [],
|
|
17903
|
+
...t.database !== void 0 ? ["database"] : [],
|
|
17904
|
+
...t.table !== void 0 ? ["table"] : [],
|
|
17905
|
+
...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
|
|
17906
|
+
];
|
|
17907
|
+
if (members.length === 0) continue;
|
|
17908
|
+
ctx.addIssue(
|
|
17909
|
+
"error",
|
|
17910
|
+
"NAMED_SCALAR_MEMBERS",
|
|
17911
|
+
`Named scalar "${t.id}" holds ${t.holds} and carries ${members.join(", ")}. A named scalar is one primitive under a name, with optional pure methods and invariants and nothing else: drop ${members.length === 1 ? "it" : "them"}, or drop holds and model the value-object's fields.`,
|
|
17912
|
+
t.id,
|
|
17913
|
+
isDraft
|
|
17914
|
+
);
|
|
17915
|
+
}
|
|
17916
|
+
}
|
|
17917
|
+
};
|
|
17918
|
+
|
|
16825
17919
|
// src/core/rules/integrity/public-surface-binding.ts
|
|
16826
17920
|
var publicSurfaceBindingRule = {
|
|
16827
17921
|
name: "public-surface-binding",
|
|
@@ -17334,10 +18428,10 @@ var projectBoundariesRule = {
|
|
|
17334
18428
|
var lintAllowsRule = {
|
|
17335
18429
|
name: "lint-allows",
|
|
17336
18430
|
judges: "design",
|
|
17337
|
-
description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings and notices; errors always surface.",
|
|
18431
|
+
description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings and notices; errors always surface \u2014 so an allow naming a code that is an error on its spec (it fired there as one, or its resolved severity is error) covers nothing, and the finding says plainly that an error cannot be allowed rather than that the code never fired.",
|
|
17338
18432
|
codes: [
|
|
17339
18433
|
{ code: "UNKNOWN_LINT_ALLOW_CODE", defaultSeverity: "warning", summary: "lint.allow names an issue code no registered rule emits" },
|
|
17340
|
-
{ code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry
|
|
18434
|
+
{ code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry covers nothing this run \u2014 the code never fired, it fired at sites this allow does not name, or it is an error, which no allow can cover" }
|
|
17341
18435
|
],
|
|
17342
18436
|
check(ctx) {
|
|
17343
18437
|
for (const a of ctx.lintAllows) {
|
|
@@ -17350,9 +18444,20 @@ var lintAllowsRule = {
|
|
|
17350
18444
|
);
|
|
17351
18445
|
continue;
|
|
17352
18446
|
}
|
|
17353
|
-
if (a.used) continue;
|
|
17354
18447
|
const reported = ctx.sitesReported(a.specId, a.code);
|
|
17355
18448
|
const at = a.at ? ` at "${a.at}"` : "";
|
|
18449
|
+
const fired = reported.unsited || reported.sites.length > 0;
|
|
18450
|
+
const isError = reported.errored || !fired && ctx.severityOf(a.code, a.specId) === "error";
|
|
18451
|
+
if (isError) {
|
|
18452
|
+
ctx.addIssue(
|
|
18453
|
+
"warning",
|
|
18454
|
+
"UNUSED_LINT_ALLOW",
|
|
18455
|
+
`Spec "${a.specId}" allows "${a.code}"${at} (reason: ${a.reason}), but "${a.code}" is an error${fired ? " and fired here as one" : ""}, and an error cannot be allowed \u2014 lint.allow silences warnings and notices only. Fix what the finding names, or remove the allow (a project may re-tune the code's severity in rules.sddRuleSeverity).`,
|
|
18456
|
+
a.specId
|
|
18457
|
+
);
|
|
18458
|
+
continue;
|
|
18459
|
+
}
|
|
18460
|
+
if (a.used) continue;
|
|
17356
18461
|
let why;
|
|
17357
18462
|
if (a.at && reported.unsited && reported.sites.length === 0) {
|
|
17358
18463
|
why = `findings of "${a.code}" on this spec name no site at all, so this allow must not name one \u2014 drop the \`at\``;
|
|
@@ -18250,11 +19355,11 @@ var detailSufficiencyRule = {
|
|
|
18250
19355
|
const contract = ctx.interfaceMap.get(impl.contract);
|
|
18251
19356
|
if (!contract) continue;
|
|
18252
19357
|
const component = ctx.componentMap.get(contract.component);
|
|
18253
|
-
const
|
|
19358
|
+
const draftContext4 = ctx.isImplementationDraft(impl);
|
|
18254
19359
|
for (const method of impl.methods) {
|
|
18255
19360
|
const detail = effectiveDetail(method, impl, component);
|
|
18256
19361
|
if (contract.methods.some((m) => m.name === method.name) && method.narrative.length === 0 && detail.level !== "full") {
|
|
18257
|
-
dialedDown.push({ implementation: impl, method, component, detail, draftContext:
|
|
19362
|
+
dialedDown.push({ implementation: impl, method, component, detail, draftContext: draftContext4 });
|
|
18258
19363
|
}
|
|
18259
19364
|
}
|
|
18260
19365
|
}
|
|
@@ -18875,18 +19980,47 @@ var dataBlockDepsRule = {
|
|
|
18875
19980
|
};
|
|
18876
19981
|
|
|
18877
19982
|
// src/core/rules/doctrine/entrypoint-dependencies.ts
|
|
19983
|
+
function maintainedRegistries(ctx) {
|
|
19984
|
+
const maintained = /* @__PURE__ */ new Map();
|
|
19985
|
+
const add2 = (supervisorId, registryId) => {
|
|
19986
|
+
let set = maintained.get(supervisorId);
|
|
19987
|
+
if (!set) maintained.set(supervisorId, set = /* @__PURE__ */ new Set());
|
|
19988
|
+
set.add(registryId);
|
|
19989
|
+
};
|
|
19990
|
+
for (const comp of ctx.components) {
|
|
19991
|
+
if (comp.componentType !== "Supervisor" || isRetired(comp)) continue;
|
|
19992
|
+
for (const memberId of comp.owns) {
|
|
19993
|
+
if (ctx.componentMap.get(memberId)?.componentType === "Registry") add2(comp.id, memberId);
|
|
19994
|
+
}
|
|
19995
|
+
}
|
|
19996
|
+
for (const impl of ctx.implementations) {
|
|
19997
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
19998
|
+
const supervisor = contract ? ctx.componentMap.get(contract.component) : void 0;
|
|
19999
|
+
if (!supervisor || supervisor.componentType !== "Supervisor" || isRetired(supervisor)) continue;
|
|
20000
|
+
for (const implMethod of impl.methods) {
|
|
20001
|
+
for (const step of implMethod.narrative) {
|
|
20002
|
+
if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
|
|
20003
|
+
if (ctx.componentMap.get(step.targetComponent)?.componentType !== "Registry") continue;
|
|
20004
|
+
const called = ctx.interfaceMethodsOf(step.targetComponent).find((m) => m.name === step.targetMethod);
|
|
20005
|
+
if (called?.effect === "lifecycle") add2(supervisor.id, step.targetComponent);
|
|
20006
|
+
}
|
|
20007
|
+
}
|
|
20008
|
+
}
|
|
20009
|
+
return new Map([...maintained].map(([id, set]) => [id, [...set]]));
|
|
20010
|
+
}
|
|
18878
20011
|
var entrypointDepsRule = {
|
|
18879
20012
|
name: "entrypoint-dependencies",
|
|
18880
20013
|
judges: "design",
|
|
18881
|
-
description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor
|
|
20014
|
+
description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor stays out of presentation; what it may do to the data it reaches is judged per call (supervisor-shared-data), and what it owns as its supervision state by the ownership rules. A component depending on a live Actor reaches it through the Actor's supervision: by depending on a Supervisor that supervises it, or on a Registry such a Supervisor maintains (owns, or calls with lifecycle-effect methods) \u2014 the lookup hop callers really take.",
|
|
18882
20015
|
codes: [
|
|
18883
20016
|
{ code: "ARCHITECTURE_VIOLATION_PORTAL_DEP", defaultSeverity: "error", summary: "Component depending on a Portal/Observer" },
|
|
18884
20017
|
{ code: "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP", defaultSeverity: "error", summary: "Portal/Observer reaching the data layer directly" },
|
|
18885
20018
|
{ code: "ARCHITECTURE_VIOLATION_VIEW_DEP", defaultSeverity: "error", summary: "View depending on persistence layers or on logic that is not pure" },
|
|
18886
|
-
{ code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on
|
|
18887
|
-
{ code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise,
|
|
20019
|
+
{ code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on a presentation block (View, FeatureComponent, RouterComponent) \u2014 a Supervisor manages live processes; the data it reaches is judged per call by supervisor-shared-data" },
|
|
20020
|
+
{ code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise, reaching it neither through a Supervisor that supervises it nor through a Registry such a Supervisor maintains \u2014 model the real lookup hop" }
|
|
18888
20021
|
],
|
|
18889
20022
|
check(ctx) {
|
|
20023
|
+
const lookups = maintainedRegistries(ctx);
|
|
18890
20024
|
for (const edge of ctx.dependencyEdges().matrix) {
|
|
18891
20025
|
const comp = edge.from;
|
|
18892
20026
|
const depComp = edge.to;
|
|
@@ -18922,27 +20056,32 @@ var entrypointDepsRule = {
|
|
|
18922
20056
|
edge.draftContext
|
|
18923
20057
|
);
|
|
18924
20058
|
}
|
|
18925
|
-
if (comp.componentType === "Supervisor" && ["
|
|
20059
|
+
if (comp.componentType === "Supervisor" && ["View", "FeatureComponent", "RouterComponent"].includes(depComp.componentType)) {
|
|
18926
20060
|
ctx.addIssue(
|
|
18927
20061
|
"error",
|
|
18928
20062
|
"ARCHITECTURE_VIOLATION_SUPERVISOR_DEP",
|
|
18929
|
-
`Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor
|
|
20063
|
+
`Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor manages live processes and stays out of presentation \u2014 the UI reaches the system through its Portals and workflows, never through a Supervisor's dependencies.`,
|
|
18930
20064
|
comp.id,
|
|
18931
20065
|
edge.draftContext
|
|
18932
20066
|
);
|
|
18933
20067
|
}
|
|
18934
20068
|
if (depComp.componentType === "Actor" && comp.componentType !== "Supervisor") {
|
|
18935
|
-
const
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
20069
|
+
const supervisors = ctx.components.filter((c) => c.componentType === "Supervisor" && c.dependsOn.includes(depComp.id));
|
|
20070
|
+
const registries = supervisors.flatMap((s) => (lookups.get(s.id) ?? []).map((registry) => ({ registry, supervisor: s.id })));
|
|
20071
|
+
const reached = supervisors.some((s) => comp.dependsOn.includes(s.id)) || registries.some((r) => comp.dependsOn.includes(r.registry));
|
|
20072
|
+
if (!reached) {
|
|
20073
|
+
let remedy;
|
|
20074
|
+
if (registries.length > 0) {
|
|
20075
|
+
remedy = `depend on ${registries.length === 1 ? "the Registry" : "one of the Registries"} its Supervisor maintains (${registries.map((r) => `"${r.registry}", kept by "${r.supervisor}"`).join("; ")}) and look the Actor up by id there`;
|
|
20076
|
+
} else if (supervisors.length > 0) {
|
|
20077
|
+
remedy = `${supervisors.length === 1 ? "its Supervisor" : "its Supervisors"} (${supervisors.map((s) => `"${s.id}"`).join(", ")}) maintain no Registry of its live handles yet: model that lookup as a Registry the Supervisor keeps through lifecycle-effect methods (registering and unregistering a live handle), and depend on that Registry`;
|
|
20078
|
+
} else {
|
|
20079
|
+
remedy = `no Supervisor depends on "${depComp.id}" yet: give it a Supervisor that depends on it and maintains a Registry of its live handles through lifecycle-effect methods, and depend on that Registry`;
|
|
20080
|
+
}
|
|
18942
20081
|
ctx.addIssue(
|
|
18943
20082
|
"error",
|
|
18944
20083
|
"ACTOR_REACHED_WITHOUT_SUPERVISOR",
|
|
18945
|
-
`Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without
|
|
20084
|
+
`Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without reaching it through its supervision. A live Actor is found by id through a Registry its Supervisor maintains, or messaged through that Supervisor \u2014 ${remedy}.`,
|
|
18946
20085
|
comp.id,
|
|
18947
20086
|
edge.draftContext
|
|
18948
20087
|
);
|
|
@@ -18956,9 +20095,9 @@ var entrypointDepsRule = {
|
|
|
18956
20095
|
var portalWriteShortcutRule = {
|
|
18957
20096
|
name: "portal-write-shortcut",
|
|
18958
20097
|
judges: "design",
|
|
18959
|
-
description: "A Portal narrative call step or dispatch-table binding that reaches a write-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a write routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
|
|
20098
|
+
description: "A Portal narrative call step or dispatch-table binding that reaches a write- or lifecycle-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a mutation \u2014 a write, or a lifecycle change to what exists \u2014 routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
|
|
18960
20099
|
codes: [
|
|
18961
|
-
{ code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write-effect method on a Repository/Index directly \u2014 reads may shortcut,
|
|
20100
|
+
{ code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write- or lifecycle-effect method on a Repository/Index directly \u2014 reads may shortcut, mutations route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" }
|
|
18962
20101
|
],
|
|
18963
20102
|
check(ctx) {
|
|
18964
20103
|
for (const impl of ctx.implementations) {
|
|
@@ -18972,11 +20111,11 @@ var portalWriteShortcutRule = {
|
|
|
18972
20111
|
const target = ctx.componentMap.get(step.targetComponent);
|
|
18973
20112
|
if (!target || target.componentType !== "Repository" && target.componentType !== "Index") continue;
|
|
18974
20113
|
const targetMethod = ctx.interfaceMethodsOf(target.id).find((m) => m.name === step.targetMethod);
|
|
18975
|
-
if (targetMethod?.effect !== "write") continue;
|
|
20114
|
+
if (targetMethod?.effect !== "write" && targetMethod?.effect !== "lifecycle") continue;
|
|
18976
20115
|
ctx.addIssue(
|
|
18977
20116
|
"error",
|
|
18978
20117
|
"PORTAL_WRITE_SHORTCUT",
|
|
18979
|
-
`Portal "${component.id}": step ${step.stepNumber} of "${implMethod.name}" calls
|
|
20118
|
+
`Portal "${component.id}": step ${step.stepNumber} of "${implMethod.name}" calls ${targetMethod.effect}-effect method ${target.id}.${step.targetMethod} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the ${targetMethod.effect === "write" ? "write" : "lifecycle change"} through an Orchestrator that owns the workflow.`,
|
|
18980
20119
|
impl.id,
|
|
18981
20120
|
isDraftCtx || ctx.isComponentDraft(target.id)
|
|
18982
20121
|
);
|
|
@@ -18990,11 +20129,11 @@ var portalWriteShortcutRule = {
|
|
|
18990
20129
|
const target = ctx.componentMap.get(binding.component);
|
|
18991
20130
|
if (!target || target.componentType !== "Repository" && target.componentType !== "Index") continue;
|
|
18992
20131
|
const targetMethod = ctx.interfaceMethodsOf(target.id).find((m) => m.name === binding.method);
|
|
18993
|
-
if (targetMethod?.effect !== "write") continue;
|
|
20132
|
+
if (targetMethod?.effect !== "write" && targetMethod?.effect !== "lifecycle") continue;
|
|
18994
20133
|
ctx.addIssue(
|
|
18995
20134
|
"error",
|
|
18996
20135
|
"PORTAL_WRITE_SHORTCUT",
|
|
18997
|
-
`Portal "${comp.id}": dispatch binding "${binding.capability}" routes to
|
|
20136
|
+
`Portal "${comp.id}": dispatch binding "${binding.capability}" routes to ${targetMethod.effect}-effect method ${target.id}.${binding.method} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the ${targetMethod.effect === "write" ? "write" : "lifecycle change"} through an Orchestrator that owns the workflow.`,
|
|
18998
20137
|
comp.id,
|
|
18999
20138
|
isDraftCtx || ctx.isComponentDraft(target.id)
|
|
19000
20139
|
);
|
|
@@ -19003,28 +20142,101 @@ var portalWriteShortcutRule = {
|
|
|
19003
20142
|
}
|
|
19004
20143
|
};
|
|
19005
20144
|
|
|
20145
|
+
// src/core/rules/doctrine/supervisor-shared-data.ts
|
|
20146
|
+
var DATA_COMPONENTS = /* @__PURE__ */ new Set(["Store", "Registry", "Repository", "Index", "Query"]);
|
|
20147
|
+
var supervisorSharedDataRule = {
|
|
20148
|
+
name: "supervisor-shared-data",
|
|
20149
|
+
judges: "design",
|
|
20150
|
+
description: "A Supervisor keeps its own supervision state (the Stores and Registries it owns) with full read and write, but data it does not own is shared: a Supervisor narrative call step that reaches a method of a Store, Registry, Repository, Index or Query the Supervisor does not own is legal only when that method declares effect read or lifecycle. A write \u2014 or a method that declares no effect \u2014 goes through a workflow: the Supervisor depends on the Orchestrator that does it. A read on behalf of a request still passes; in practice the paired write is what forces the workflow out, and the read moves with it.",
|
|
20151
|
+
codes: [
|
|
20152
|
+
{ code: "SUPERVISOR_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Supervisor narrative call reaches a method of a data component it does not own whose declared effect is neither read nor lifecycle \u2014 writes to shared data route through an Orchestrator" }
|
|
20153
|
+
],
|
|
20154
|
+
check(ctx) {
|
|
20155
|
+
const ownership = ctx.ownershipIndex();
|
|
20156
|
+
for (const impl of ctx.implementations) {
|
|
20157
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
20158
|
+
const supervisor = contract ? ctx.componentMap.get(contract.component) : void 0;
|
|
20159
|
+
if (!supervisor || supervisor.componentType !== "Supervisor" || isRetired(supervisor)) continue;
|
|
20160
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
20161
|
+
for (const implMethod of impl.methods) {
|
|
20162
|
+
for (const step of implMethod.narrative) {
|
|
20163
|
+
if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
|
|
20164
|
+
const target = ctx.componentMap.get(step.targetComponent);
|
|
20165
|
+
if (!target || !DATA_COMPONENTS.has(target.componentType)) continue;
|
|
20166
|
+
if (ownership.ownerOf(target.id) === supervisor.id) continue;
|
|
20167
|
+
const called = ctx.interfaceMethodsOf(target.id).find((m) => m.name === step.targetMethod);
|
|
20168
|
+
if (!called || called.effect === "read" || called.effect === "lifecycle") continue;
|
|
20169
|
+
const what = called.effect === "write" ? "a write-effect method" : "a method that declares no effect";
|
|
20170
|
+
const untaggedHint = called.effect === "write" ? "" : ` If ${target.id}.${called.name} only reads, or only changes what exists, declare its effect (read or lifecycle).`;
|
|
20171
|
+
ctx.addIssue(
|
|
20172
|
+
"error",
|
|
20173
|
+
"SUPERVISOR_WRITE_SHORTCUT",
|
|
20174
|
+
`Supervisor "${supervisor.id}": step ${step.stepNumber} of "${implMethod.name}" calls ${target.id}.${called.name}, ${what} on ${target.componentType} "${target.id}", which the Supervisor does not own. A Supervisor reads shared data and changes what exists in it (read and lifecycle effects); a write to its fields goes through a workflow \u2014 depend on the Orchestrator that does it and call that instead.${untaggedHint}`,
|
|
20175
|
+
impl.id,
|
|
20176
|
+
isDraftCtx || ctx.isComponentDraft(target.id)
|
|
20177
|
+
);
|
|
20178
|
+
}
|
|
20179
|
+
}
|
|
20180
|
+
}
|
|
20181
|
+
}
|
|
20182
|
+
};
|
|
20183
|
+
|
|
20184
|
+
// src/core/rules/doctrine/lifecycle-effect-closure.ts
|
|
20185
|
+
var lifecycleEffectClosureRule = {
|
|
20186
|
+
name: "lifecycle-effect-closure",
|
|
20187
|
+
judges: "design",
|
|
20188
|
+
description: "A lifecycle-effect method may create, destroy, or (un)register an entity's existence or membership, never modify its domain fields \u2014 and the effect is closed under composition: its narrative may call only read- and lifecycle-effect methods besides its construction and local steps. A call step to a write-effect method from a lifecycle-effect method is a declaration error: the method is a write, or the write belongs to a workflow beside it. Called methods that declare no effect are not judged.",
|
|
20189
|
+
codes: [
|
|
20190
|
+
{ code: "LIFECYCLE_CALLS_WRITE", defaultSeverity: "error", summary: "Lifecycle-effect method whose narrative calls a write-effect method \u2014 the lifecycle effect is closed under composition" }
|
|
20191
|
+
],
|
|
20192
|
+
check(ctx) {
|
|
20193
|
+
for (const impl of ctx.implementations) {
|
|
20194
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
20195
|
+
if (!contract) continue;
|
|
20196
|
+
const declared = ctx.interfaceMethodsOf(contract.component);
|
|
20197
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
20198
|
+
for (const implMethod of impl.methods) {
|
|
20199
|
+
if (declared.find((m) => m.name === implMethod.name)?.effect !== "lifecycle") continue;
|
|
20200
|
+
for (const step of implMethod.narrative) {
|
|
20201
|
+
if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
|
|
20202
|
+
const called = ctx.interfaceMethodsOf(step.targetComponent).find((m) => m.name === step.targetMethod);
|
|
20203
|
+
if (called?.effect !== "write") continue;
|
|
20204
|
+
ctx.addIssue(
|
|
20205
|
+
"error",
|
|
20206
|
+
"LIFECYCLE_CALLS_WRITE",
|
|
20207
|
+
`"${contract.component}.${implMethod.name}" declares effect lifecycle, but step ${step.stepNumber} calls write-effect method ${step.targetComponent}.${step.targetMethod}. A lifecycle method creates, destroys or (un)registers what exists and never modifies domain fields, and the effect is closed under composition \u2014 it may call only read and lifecycle methods. Declare the method a write, or move the write to a workflow beside it.`,
|
|
20208
|
+
impl.id,
|
|
20209
|
+
isDraftCtx
|
|
20210
|
+
);
|
|
20211
|
+
}
|
|
20212
|
+
}
|
|
20213
|
+
}
|
|
20214
|
+
}
|
|
20215
|
+
};
|
|
20216
|
+
|
|
19006
20217
|
// src/core/rules/doctrine/pattern-membership.ts
|
|
19007
20218
|
var patternMembershipRule = {
|
|
19008
20219
|
name: "pattern-membership",
|
|
19009
20220
|
judges: "design",
|
|
19010
|
-
description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern to claim it.",
|
|
20221
|
+
description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. The one building block that may own is a Supervisor, which owns its supervision state and may own nothing at all; what it may own is pattern-containment's question. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern or Supervisor to claim it.",
|
|
19011
20222
|
codes: [
|
|
19012
20223
|
{ code: "EMPTY_PATTERN", defaultSeverity: "error", summary: "Pattern with no owned member blocks" },
|
|
19013
|
-
{ code: "BLOCK_OWNS_MEMBERS", defaultSeverity: "error", summary: "Building block using owns" },
|
|
20224
|
+
{ code: "BLOCK_OWNS_MEMBERS", defaultSeverity: "error", summary: "Building block other than a Supervisor using owns" },
|
|
19014
20225
|
{ code: "INVALID_OWNED_MEMBER", defaultSeverity: "error", summary: "owns names a non-existent component" },
|
|
19015
20226
|
{ code: "PATTERN_OWNS_PATTERN", defaultSeverity: "error", summary: "Pattern owning another pattern" },
|
|
19016
|
-
{ code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two patterns" }
|
|
20227
|
+
{ code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two owners (patterns or Supervisors)" }
|
|
19017
20228
|
],
|
|
19018
20229
|
check(ctx) {
|
|
19019
20230
|
for (const comp of ctx.components) {
|
|
19020
20231
|
if (isRetired(comp)) continue;
|
|
19021
20232
|
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
19022
20233
|
const pattern = isPattern(comp);
|
|
20234
|
+
const supervisor = comp.componentType === "Supervisor";
|
|
19023
20235
|
if (pattern && comp.owns.length === 0) {
|
|
19024
20236
|
ctx.addIssue("error", "EMPTY_PATTERN", `Pattern "${comp.id}" (${comp.componentType}) must own member blocks via "owns".`, comp.id, isDraftCtx);
|
|
19025
20237
|
}
|
|
19026
|
-
if (!pattern && comp.owns.length > 0) {
|
|
19027
|
-
ctx.addIssue("error", "BLOCK_OWNS_MEMBERS", `Building block "${comp.id}" (${comp.componentType}) cannot own members; only patterns (${Array.from(PATTERN_TYPES).join("/")}) use "owns".`, comp.id, isDraftCtx);
|
|
20238
|
+
if (!pattern && !supervisor && comp.owns.length > 0) {
|
|
20239
|
+
ctx.addIssue("error", "BLOCK_OWNS_MEMBERS", `Building block "${comp.id}" (${comp.componentType}) cannot own members; only patterns (${Array.from(PATTERN_TYPES).join("/")}) use "owns", and a Supervisor for its supervision state.`, comp.id, isDraftCtx);
|
|
19028
20240
|
}
|
|
19029
20241
|
}
|
|
19030
20242
|
const ownership = ctx.ownershipIndex();
|
|
@@ -19032,15 +20244,18 @@ var patternMembershipRule = {
|
|
|
19032
20244
|
if (isRetired(comp)) continue;
|
|
19033
20245
|
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
19034
20246
|
const pattern = isPattern(comp);
|
|
20247
|
+
const supervisor = comp.componentType === "Supervisor";
|
|
19035
20248
|
for (const memberId of comp.owns) {
|
|
19036
20249
|
const member = ctx.componentMap.get(memberId);
|
|
19037
20250
|
if (!member) {
|
|
19038
20251
|
ctx.addIssue("error", "INVALID_OWNED_MEMBER", `Component "${comp.id}" owns "${memberId}" which does not exist.`, comp.id, isDraftCtx);
|
|
19039
20252
|
continue;
|
|
19040
20253
|
}
|
|
19041
|
-
if (!pattern) continue;
|
|
20254
|
+
if (!pattern && !supervisor) continue;
|
|
19042
20255
|
if (isPattern(member)) {
|
|
19043
|
-
|
|
20256
|
+
if (pattern) {
|
|
20257
|
+
ctx.addIssue("error", "PATTERN_OWNS_PATTERN", `Pattern "${comp.id}" owns "${memberId}", which is itself a pattern. Patterns own only building blocks \u2014 compose patterns at the subsystem (L1) level.`, comp.id, isDraftCtx);
|
|
20258
|
+
}
|
|
19044
20259
|
continue;
|
|
19045
20260
|
}
|
|
19046
20261
|
const firstOwner = ownership.ownerOf(memberId);
|
|
@@ -19054,13 +20269,15 @@ var patternMembershipRule = {
|
|
|
19054
20269
|
|
|
19055
20270
|
// src/core/rules/doctrine/pattern-containment.ts
|
|
19056
20271
|
var REPOSITORY_MEMBERS = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Query", "Adapter"]);
|
|
20272
|
+
var SUPERVISION_STATE = /* @__PURE__ */ new Set(["Store", "Registry"]);
|
|
19057
20273
|
var patternContainmentRule = {
|
|
19058
20274
|
name: "pattern-containment",
|
|
19059
20275
|
judges: "design",
|
|
19060
|
-
description: "Holds each
|
|
20276
|
+
description: "Holds each owner to the containment its definition prescribes. A Repository may own only Store, Registry, Index, Query and (optionally) Adapter, judged member by member. A Supervisor may own only its supervision state \u2014 Stores and Registries that are its own, one hop \u2014 judged member by member too. A FeatureComponent owns exactly one Orchestrator (the logic side) and one or more Views (its faces \u2014 list, detail, form \u2014 sharing the one logic component), and nothing else. A RouterComponent owns exactly one Portal as its facade and at least one other child to route to. A counting pattern that still owns a retired member is not judged until that member is migrated: its counts change with the migration, and STEREOTYPE_RETIRED is the one finding.",
|
|
19061
20277
|
codes: [
|
|
19062
20278
|
{ code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Query/Adapter member" },
|
|
19063
20279
|
{ code: "FEATURE_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "FeatureComponent not owning exactly one Orchestrator + one or more Views" },
|
|
20280
|
+
{ code: "SUPERVISOR_CONTAINMENT", defaultSeverity: "error", summary: "Supervisor owning anything but a Store or Registry \u2014 a Supervisor owns only its supervision state" },
|
|
19064
20281
|
{ code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent not owning exactly one Portal facade, or owning no children to route to" }
|
|
19065
20282
|
],
|
|
19066
20283
|
check(ctx) {
|
|
@@ -19111,6 +20328,15 @@ var patternContainmentRule = {
|
|
|
19111
20328
|
}
|
|
19112
20329
|
}
|
|
19113
20330
|
}
|
|
20331
|
+
for (const comp of ctx.components) {
|
|
20332
|
+
if (comp.componentType !== "Supervisor" || isRetired(comp)) continue;
|
|
20333
|
+
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
20334
|
+
for (const memberId of comp.owns) {
|
|
20335
|
+
const member = ctx.componentMap.get(memberId);
|
|
20336
|
+
if (!member || isRetired(member) || SUPERVISION_STATE.has(member.componentType)) continue;
|
|
20337
|
+
ctx.addIssue("error", "SUPERVISOR_CONTAINMENT", `Supervisor "${comp.id}" owns "${memberId}" of type ${member.componentType}; a Supervisor owns only its supervision state \u2014 Stores and Registries that are its own. Depend on "${memberId}" as a collaborator instead.`, comp.id, isDraftCtx);
|
|
20338
|
+
}
|
|
20339
|
+
}
|
|
19114
20340
|
}
|
|
19115
20341
|
};
|
|
19116
20342
|
|
|
@@ -19118,7 +20344,7 @@ var patternContainmentRule = {
|
|
|
19118
20344
|
var unownedBlocksRule = {
|
|
19119
20345
|
name: "unowned-blocks",
|
|
19120
20346
|
judges: "design",
|
|
19121
|
-
description: `Judges the data blocks no pattern owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
|
|
20347
|
+
description: `Judges the data blocks no owner (a pattern, or a Supervisor keeping its supervision state) owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
|
|
19122
20348
|
codes: [
|
|
19123
20349
|
{ code: "UNOWNED_STORE", defaultSeverity: "warning", summary: "Store not owned by any pattern \u2014 recommended shape is a Repository; a deliberate standalone Store needs a lint.allow" },
|
|
19124
20350
|
{ code: "REGISTRY_WITHOUT_STORE", defaultSeverity: "warning", summary: "Standalone Registry with no Store to write to \u2014 either mistyped (a fused file-backed store belongs typed Store) or orphaned" },
|
|
@@ -19164,9 +20390,10 @@ var unownedBlocksRule = {
|
|
|
19164
20390
|
var memberVisibilityRule = {
|
|
19165
20391
|
name: "member-visibility",
|
|
19166
20392
|
judges: "design",
|
|
19167
|
-
description: "A component may depend on a block within its own group (it is the
|
|
20393
|
+
description: "A component may depend on a block within its own group (it is the owner, or a sibling member of the same owner), on any pattern facade, or on a standalone block \u2014 never on a block privately owned by ANOTHER pattern, which must be reached through that pattern's facade. A Store or Registry a Supervisor owns is that Supervisor's supervision state and nobody else depends on it: a component that does is the intruder, and the finding is reported on it, naming the Supervisor and the state. A retired depending component is skipped, as everywhere in this family.",
|
|
19168
20394
|
codes: [
|
|
19169
|
-
{ code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" }
|
|
20395
|
+
{ code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" },
|
|
20396
|
+
{ code: "SUPERVISION_STATE_INTRUSION", defaultSeverity: "error", summary: "Dependency on a Store or Registry a Supervisor owns as its supervision state \u2014 reported on the intruder, naming the Supervisor and the state" }
|
|
19170
20397
|
],
|
|
19171
20398
|
check(ctx) {
|
|
19172
20399
|
const ownership = ctx.ownershipIndex();
|
|
@@ -19177,7 +20404,12 @@ var memberVisibilityRule = {
|
|
|
19177
20404
|
if (!owner) continue;
|
|
19178
20405
|
if (owner === comp.id) continue;
|
|
19179
20406
|
if (ownership.ownerOf(comp.id) === owner) continue;
|
|
19180
|
-
|
|
20407
|
+
const isDraftCtx = ctx.isComponentDraft(comp.id) || ctx.isComponentDraft(depId);
|
|
20408
|
+
if (ctx.componentMap.get(owner)?.componentType === "Supervisor") {
|
|
20409
|
+
ctx.addIssue("error", "SUPERVISION_STATE_INTRUSION", `"${comp.id}" depends on "${depId}", which Supervisor "${owner}" owns as its supervision state. Supervision state is private to its Supervisor \u2014 nobody else depends on it. If "${comp.id}" needs this data it is shared data: move "${depId}" out of "${owner}"'s owns into a data component both depend on (the Supervisor keeping it through read and lifecycle calls), or reach what "${comp.id}" needs through "${owner}".`, comp.id, isDraftCtx);
|
|
20410
|
+
continue;
|
|
20411
|
+
}
|
|
20412
|
+
ctx.addIssue("error", "VISIBILITY_VIOLATION", `Component "${comp.id}" depends on "${depId}", which is privately owned by pattern "${owner}". Depend on the facade "${owner}" instead.`, comp.id, isDraftCtx);
|
|
19181
20413
|
}
|
|
19182
20414
|
}
|
|
19183
20415
|
}
|
|
@@ -19679,6 +20911,25 @@ var packRequirementsRule = {
|
|
|
19679
20911
|
}
|
|
19680
20912
|
};
|
|
19681
20913
|
|
|
20914
|
+
// src/core/rules/extension/pack-deprecations.ts
|
|
20915
|
+
var packDeprecationsRule = {
|
|
20916
|
+
name: "pack-deprecations",
|
|
20917
|
+
judges: "design",
|
|
20918
|
+
description: "A loaded pack still declaring a field wairon has deprecated (ctx.ext.deprecations \u2014 today a language's foreignBuiltins, whose foreign-builtin check is retired because contracts are written in the neutral type grammar) is told so: the field is accepted and ignored for one release and removed in the release after. A notice: it never fails the gate.",
|
|
20919
|
+
codes: [
|
|
20920
|
+
{ code: "PACK_FIELD_DEPRECATED", defaultSeverity: "notice", summary: "A loaded pack declares a deprecated field, accepted and ignored for one release" }
|
|
20921
|
+
],
|
|
20922
|
+
check(ctx) {
|
|
20923
|
+
for (const deprecation of ctx.ext.deprecations) {
|
|
20924
|
+
ctx.addIssue(
|
|
20925
|
+
"notice",
|
|
20926
|
+
"PACK_FIELD_DEPRECATED",
|
|
20927
|
+
`Deprecated pack field \u2014 ${deprecation}. It is accepted and ignored in this release and removed in the next; take it out of the pack.`
|
|
20928
|
+
);
|
|
20929
|
+
}
|
|
20930
|
+
}
|
|
20931
|
+
};
|
|
20932
|
+
|
|
19682
20933
|
// src/core/rules/wiring/dependency-cycles.ts
|
|
19683
20934
|
var cyclesRule = {
|
|
19684
20935
|
name: "dependency-cycles",
|
|
@@ -20291,9 +21542,9 @@ var durabilityRule = {
|
|
|
20291
21542
|
// Stage 8: its verdict needs the whole system's specs, so a part judged alone skips it.
|
|
20292
21543
|
needsWholeTree: true,
|
|
20293
21544
|
judges: "design",
|
|
20294
|
-
description: "A durable Store (persisted RAM projection) must carry effect-tagged contract methods, and its
|
|
21545
|
+
description: "A durable Store (persisted RAM projection) must carry effect-tagged contract methods, and its mutations \u2014 write-effect methods, and lifecycle-effect methods, which change what exists \u2014 require a hydration read-back reachable from a lifecycle init entrypoint. read-through is exempt (every read IS the read-back), as are ram-projection (rebuilt not restored) and cache (evictable, loss-safe). The flagship semantic check is opt-out by declaration, never silently absent \u2014 the declaration itself is enforced by durability-declaration.",
|
|
20295
21546
|
codes: [
|
|
20296
|
-
{ code: "MISSING_EFFECT_TAG", defaultSeverity: "warning", summary: "Durable Store contract method lacks an effect: read | write tag" },
|
|
21547
|
+
{ code: "MISSING_EFFECT_TAG", defaultSeverity: "warning", summary: "Durable Store contract method lacks an effect: read | write | lifecycle tag" },
|
|
20297
21548
|
{ code: "MISSING_HYDRATION", defaultSeverity: "error", summary: "Durable Store is written but no read-back is reachable from any lifecycle init entrypoint" }
|
|
20298
21549
|
],
|
|
20299
21550
|
check(ctx) {
|
|
@@ -20321,7 +21572,7 @@ var durabilityRule = {
|
|
|
20321
21572
|
isDraftCtx
|
|
20322
21573
|
);
|
|
20323
21574
|
}
|
|
20324
|
-
const writes = methods.filter((method) => method.effect === "write");
|
|
21575
|
+
const writes = methods.filter((method) => method.effect === "write" || method.effect === "lifecycle");
|
|
20325
21576
|
const reads = methods.filter((method) => method.effect === "read");
|
|
20326
21577
|
if (writes.length === 0) continue;
|
|
20327
21578
|
const hydrated = initReach !== null && reads.some((method) => initReach.reachesMethod(comp.id, method.name));
|
|
@@ -20340,14 +21591,22 @@ var durabilityRule = {
|
|
|
20340
21591
|
};
|
|
20341
21592
|
|
|
20342
21593
|
// src/core/rules/wiring/untyped-seams.ts
|
|
20343
|
-
var
|
|
20344
|
-
function
|
|
20345
|
-
return
|
|
21594
|
+
var UNTYPED_SPELLINGS = /* @__PURE__ */ new Set(["any", "json", "unknown", "object"]);
|
|
21595
|
+
function holdsAny(expr) {
|
|
21596
|
+
return expr.form === "primitive" && expr.name === "any" || expr.args.some(holdsAny);
|
|
21597
|
+
}
|
|
21598
|
+
function tokensUntyped(text3) {
|
|
21599
|
+
return extractTypeIdentifiers(text3).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()));
|
|
21600
|
+
}
|
|
21601
|
+
function positionUntyped(text3, position) {
|
|
21602
|
+
const parse = parseTypePosition(text3, position);
|
|
21603
|
+
if (parse.expression && !parse.problem) return holdsAny(parse.expression);
|
|
21604
|
+
return tokensUntyped(text3);
|
|
20346
21605
|
}
|
|
20347
21606
|
var untypedSeamRule = {
|
|
20348
21607
|
name: "untyped-seams",
|
|
20349
21608
|
judges: "design",
|
|
20350
|
-
description: "Methods on a subsystem's published components (its public surface) should not take or return
|
|
21609
|
+
description: "Methods on a subsystem's published components (its public surface) should not take or return `any` \u2014 the one untyped type of the grammar, which object, unknown, json and Json are canonicalised to \u2014 judged through each method's named types and parsed expressions so a prose signature is judged like structured params (its tokens still read leniently): cross-subsystem contracts are the swap seam and must be typed. Generic-dispatch portals carry per-capability types via their dispatch table instead.",
|
|
20351
21610
|
codes: [
|
|
20352
21611
|
{ code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown/object parameter or return crossing a subsystem public surface" }
|
|
20353
21612
|
],
|
|
@@ -20365,14 +21624,14 @@ var untypedSeamRule = {
|
|
|
20365
21624
|
const offenders = [];
|
|
20366
21625
|
if (m.params && m.params.length > 0) {
|
|
20367
21626
|
for (const p of m.params) {
|
|
20368
|
-
if (
|
|
21627
|
+
if (positionUntyped(p.type, "param")) {
|
|
20369
21628
|
offenders.push(`param "${p.name}: ${p.type}"`);
|
|
20370
21629
|
}
|
|
20371
21630
|
}
|
|
20372
|
-
if (
|
|
21631
|
+
if (m.returns && positionUntyped(m.returns, "returns")) {
|
|
20373
21632
|
offenders.push(`return "${m.returns}"`);
|
|
20374
21633
|
}
|
|
20375
|
-
} else if (
|
|
21634
|
+
} else if (methodTypeRefs(m).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()))) {
|
|
20376
21635
|
offenders.push(`signature "${m.signature}"`);
|
|
20377
21636
|
}
|
|
20378
21637
|
if (offenders.length) {
|
|
@@ -21616,10 +22875,37 @@ function originOf(shape) {
|
|
|
21616
22875
|
function absence(optional) {
|
|
21617
22876
|
return optional ? "may be absent" : "is always there";
|
|
21618
22877
|
}
|
|
22878
|
+
function judgeNamedScalar(ctx, type, file, facts) {
|
|
22879
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") return;
|
|
22880
|
+
const dialect = dialectOf(facts);
|
|
22881
|
+
const holds = parseTypePosition(type.holds, "holds").expression;
|
|
22882
|
+
if (!dialect || !holds) return;
|
|
22883
|
+
const symbol = type.symbol ?? type.name;
|
|
22884
|
+
const own = (record) => !!record && Object.prototype.hasOwnProperty.call(record, symbol);
|
|
22885
|
+
let declared;
|
|
22886
|
+
if (own(facts.aliasTypes)) {
|
|
22887
|
+
const annotation = facts.aliasTypes[symbol];
|
|
22888
|
+
if (dialect.agrees(annotation, holds, /* @__PURE__ */ new Map())) return;
|
|
22889
|
+
declared = `an alias of \`${annotation}\``;
|
|
22890
|
+
} else if (own(facts.typeShapes)) {
|
|
22891
|
+
declared = "a record (an object shape)";
|
|
22892
|
+
} else {
|
|
22893
|
+
return;
|
|
22894
|
+
}
|
|
22895
|
+
ctx.addIssue(
|
|
22896
|
+
"warning",
|
|
22897
|
+
"TYPE_HOLDS_MISMATCH",
|
|
22898
|
+
`Named scalar "${type.id}" holds ${type.holds}, but "${file}" declares "${symbol}" as ${declared}. Every reader of a named scalar is told what one value of it is, and the code says otherwise. Make the declaration an alias of the primitive (in TypeScript, \`type ${symbol} = ${dialect.write(holds)}\`), or change what the type holds.`,
|
|
22899
|
+
type.id,
|
|
22900
|
+
void 0,
|
|
22901
|
+
void 0,
|
|
22902
|
+
{ at: symbol }
|
|
22903
|
+
);
|
|
22904
|
+
}
|
|
21619
22905
|
var typeShapeRule = {
|
|
21620
22906
|
name: "type-shape",
|
|
21621
22907
|
judges: "code",
|
|
21622
|
-
description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members \u2014 and on through a composition the same file writes out (`Base.extend({\u2026})` on a schema constant it declares), the extension's keys laid over the base's. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count.",
|
|
22908
|
+
description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members \u2014 and on through a composition the same file writes out (`Base.extend({\u2026})` on a schema constant it declares), the extension's keys laid over the base's. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count. A NAMED SCALAR (a value-object declaring `holds`) has no field list: its shape is the primitive it holds, compared with the right side of the type alias the file declares under its name, read through the file's dialect (`type PackPath = string` agrees with holds string); an object shape under that name, or an alias the dialect does not read as that primitive, is TYPE_HOLDS_MISMATCH.",
|
|
21623
22909
|
codes: [
|
|
21624
22910
|
{
|
|
21625
22911
|
code: "UNREALIZED_TYPE_FIELD",
|
|
@@ -21635,6 +22921,12 @@ var typeShapeRule = {
|
|
|
21635
22921
|
summary: "The shape at a type's sourcePath carries a field the type spec does not declare \u2014 data the design never described, so no diagram draws it and no brief hands it to an implementer",
|
|
21636
22922
|
carryable: true
|
|
21637
22923
|
},
|
|
22924
|
+
{
|
|
22925
|
+
code: "TYPE_HOLDS_MISMATCH",
|
|
22926
|
+
defaultSeverity: "warning",
|
|
22927
|
+
summary: "A named scalar's holds disagrees with what its sourcePath declares under its name \u2014 an alias of another type, or a record \u2014 so every reader is told one value of it is something the code says it is not",
|
|
22928
|
+
carryable: true
|
|
22929
|
+
},
|
|
21638
22930
|
{
|
|
21639
22931
|
code: "TYPE_FIELD_OPTIONALITY",
|
|
21640
22932
|
defaultSeverity: "warning",
|
|
@@ -21649,6 +22941,10 @@ var typeShapeRule = {
|
|
|
21649
22941
|
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
21650
22942
|
const file = pathKey(type.sourcePath);
|
|
21651
22943
|
const facts = code.factsAt(file);
|
|
22944
|
+
if (type.holds !== void 0) {
|
|
22945
|
+
judgeNamedScalar(ctx, type, file, facts);
|
|
22946
|
+
continue;
|
|
22947
|
+
}
|
|
21652
22948
|
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
21653
22949
|
const symbol = type.symbol ?? type.name;
|
|
21654
22950
|
const shapes = facts.typeShapes;
|
|
@@ -21703,6 +22999,67 @@ var typeShapeRule = {
|
|
|
21703
22999
|
}
|
|
21704
23000
|
};
|
|
21705
23001
|
|
|
23002
|
+
// src/core/rules/conformance/enum-values.ts
|
|
23003
|
+
var enumValuesRule = {
|
|
23004
|
+
name: "enum-values",
|
|
23005
|
+
judges: "code",
|
|
23006
|
+
description: "Code-to-contract for an ENUM: an enum type's values are compared, name by name, with the values the declaration at its sourcePath holds \u2014 a string-literal union alias, the array a z.enum constant holds, or a string enum's member values (source_file_facts.enumValues). `typeRealization` already asks whether the declaration exists; nothing asked whether it holds the values the design lists, and an enum is read by every diagram, OpenAPI document and brief as the closed set it claims. Exact grade only; a declaration the analyzer reads no values from is typeRealization's question, never this rule's. Order is not judged: languages differ on whether declaration order is observable.",
|
|
23007
|
+
codes: [
|
|
23008
|
+
{
|
|
23009
|
+
code: "UNREALIZED_ENUM_VALUE",
|
|
23010
|
+
defaultSeverity: "warning",
|
|
23011
|
+
summary: "An enum type lists a value the declaration at its sourcePath does not hold \u2014 the design promises a value the code would reject",
|
|
23012
|
+
carryable: true
|
|
23013
|
+
},
|
|
23014
|
+
{
|
|
23015
|
+
code: "UNDECLARED_ENUM_VALUE",
|
|
23016
|
+
defaultSeverity: "warning",
|
|
23017
|
+
summary: "The declaration at an enum type's sourcePath holds a value the enum does not list \u2014 a value the design never described",
|
|
23018
|
+
carryable: true
|
|
23019
|
+
}
|
|
23020
|
+
],
|
|
23021
|
+
check(ctx) {
|
|
23022
|
+
const code = ctx.codeIndex();
|
|
23023
|
+
for (const type of ctx.types) {
|
|
23024
|
+
if (type.kind !== "enum" || !type.sourcePath) continue;
|
|
23025
|
+
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
23026
|
+
const file = pathKey(type.sourcePath);
|
|
23027
|
+
const facts = code.factsAt(file);
|
|
23028
|
+
const symbol = type.symbol ?? type.name;
|
|
23029
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
23030
|
+
const declared = facts.enumValues;
|
|
23031
|
+
if (!declared || !Object.prototype.hasOwnProperty.call(declared, symbol)) continue;
|
|
23032
|
+
const held = new Set(declared[symbol]);
|
|
23033
|
+
const listed = (type.values ?? []).map((value) => value.name);
|
|
23034
|
+
const listedSet = new Set(listed);
|
|
23035
|
+
const unrealized = listed.filter((value) => !held.has(value));
|
|
23036
|
+
if (unrealized.length > 0) {
|
|
23037
|
+
ctx.addIssue(
|
|
23038
|
+
"warning",
|
|
23039
|
+
"UNREALIZED_ENUM_VALUE",
|
|
23040
|
+
`Enum type "${type.id}" lists ${unrealized.length} value(s) the declaration "${symbol}" in "${file}" does not hold \u2014 ${unrealized.map((value) => `"${value}"`).join(", ")}. The design promises a value the code would reject, and every diagram, OpenAPI document and brief offers it. Add it to the declaration, or drop it from the enum (values compare exactly: a value is data).`,
|
|
23041
|
+
type.id,
|
|
23042
|
+
void 0,
|
|
23043
|
+
void 0,
|
|
23044
|
+
{ at: symbol, covers: unrealized }
|
|
23045
|
+
);
|
|
23046
|
+
}
|
|
23047
|
+
const undeclared = declared[symbol].filter((value) => !listedSet.has(value));
|
|
23048
|
+
if (undeclared.length > 0) {
|
|
23049
|
+
ctx.addIssue(
|
|
23050
|
+
"warning",
|
|
23051
|
+
"UNDECLARED_ENUM_VALUE",
|
|
23052
|
+
`The declaration "${symbol}" in "${file}" holds ${undeclared.length} value(s) enum type "${type.id}" does not list \u2014 ${undeclared.map((value) => `"${value}"`).join(", ")}. A value the design never described is one no diagram shows and no brief hands an implementer. List it on the enum, or take it out of the code.`,
|
|
23053
|
+
type.id,
|
|
23054
|
+
void 0,
|
|
23055
|
+
void 0,
|
|
23056
|
+
{ at: symbol, covers: undeclared }
|
|
23057
|
+
);
|
|
23058
|
+
}
|
|
23059
|
+
}
|
|
23060
|
+
}
|
|
23061
|
+
};
|
|
23062
|
+
|
|
21706
23063
|
// src/core/rules/conformance/param-conformance.ts
|
|
21707
23064
|
function omittable(optional) {
|
|
21708
23065
|
return optional ? "may be left out" : "is required";
|
|
@@ -21753,7 +23110,7 @@ function agreed(judgements, reading) {
|
|
|
21753
23110
|
var paramConformanceRule = {
|
|
21754
23111
|
name: "param-conformance",
|
|
21755
23112
|
judges: "code",
|
|
21756
|
-
description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
|
|
23113
|
+
description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. Types agree when the code's annotation, read through the dialect of the language the file was analyzed as (type_dialect.agrees), is the contract's canonical type \u2014 so `string[]` in the code agrees with `list<string>` in the contract, TypeScript's `number` with int and float alike, and an annotation the dialect cannot read agrees with nothing. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
|
|
21757
23114
|
codes: [
|
|
21758
23115
|
{
|
|
21759
23116
|
code: "UNREALIZED_PARAM",
|
|
@@ -21790,13 +23147,13 @@ var paramConformanceRule = {
|
|
|
21790
23147
|
codeNameOf.set(type.id, named2);
|
|
21791
23148
|
if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named2);
|
|
21792
23149
|
}
|
|
21793
|
-
const
|
|
21794
|
-
|
|
21795
|
-
|
|
21796
|
-
|
|
21797
|
-
return
|
|
23150
|
+
const typeAgreesIn = (dialect) => (declared, realized) => {
|
|
23151
|
+
if (!realized.type || !dialect) return false;
|
|
23152
|
+
const stated = parseTypePosition(declared.type, "param", !!declared.optional);
|
|
23153
|
+
if (!stated.expression) return false;
|
|
23154
|
+
return dialect.agrees(realized.type, stated.expression, codeNameOf);
|
|
21798
23155
|
};
|
|
21799
|
-
for (const { implementation, method, sourceFile, draftContext:
|
|
23156
|
+
for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
|
|
21800
23157
|
const contract = ctx.interfaceMap.get(implementation.contract);
|
|
21801
23158
|
const declared = contract?.methods.find((m) => m.name === method.name)?.params ?? [];
|
|
21802
23159
|
if (declared.length === 0 || !sourceFile) continue;
|
|
@@ -21809,6 +23166,7 @@ var paramConformanceRule = {
|
|
|
21809
23166
|
const candidates = signatures[symbol];
|
|
21810
23167
|
if (candidates.length === 0) continue;
|
|
21811
23168
|
const injected = new Set(implementation.injectedParams ?? []);
|
|
23169
|
+
const typeAgrees = typeAgreesIn(dialectOf(facts));
|
|
21812
23170
|
const judgements = candidates.map((realized) => judge(declared, realized, injected, typeAgrees));
|
|
21813
23171
|
const subject = candidates.length > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
|
|
21814
23172
|
const opening = subject.charAt(0).toUpperCase() + subject.slice(1);
|
|
@@ -21819,7 +23177,7 @@ var paramConformanceRule = {
|
|
|
21819
23177
|
"UNREALIZED_PARAM",
|
|
21820
23178
|
`Method "${method.name}" of contract "${implementation.contract}" declares ${unrealized.length} parameter(s) ${subject} does not take \u2014 ${unrealized.map((found) => found.told).join(", ")}. The contract is promising an argument that would go nowhere, and nothing breaks at a call site to correct it: every brief and every caller built from the contract passes one. Take the parameter in the code, or drop it from the contract.`,
|
|
21821
23179
|
implementation.id,
|
|
21822
|
-
|
|
23180
|
+
draftContext4,
|
|
21823
23181
|
void 0,
|
|
21824
23182
|
{ at: method.name, covers: unrealized.map((found) => found.unit) }
|
|
21825
23183
|
);
|
|
@@ -21831,7 +23189,7 @@ var paramConformanceRule = {
|
|
|
21831
23189
|
"UNDECLARED_PARAM",
|
|
21832
23190
|
`${opening} realizing method "${method.name}" of contract "${implementation.contract}" takes ${undeclared.length} parameter(s) the contract does not declare and no declared injection accounts for \u2014 ${undeclared.map((found) => found.told).join(", ")}. An argument a caller must supply that the design never mentions is how a credential ends up in a signature nobody has read against its contract. Declare it on the contract, name it in the implementation's \`injectedParams\` when whatever wires this component up supplies it (a LEADING run only), or take it out of the signature.`,
|
|
21833
23191
|
implementation.id,
|
|
21834
|
-
|
|
23192
|
+
draftContext4,
|
|
21835
23193
|
void 0,
|
|
21836
23194
|
{ at: method.name, covers: undeclared.map((found) => found.unit) }
|
|
21837
23195
|
);
|
|
@@ -21843,7 +23201,7 @@ var paramConformanceRule = {
|
|
|
21843
23201
|
"PARAM_NAME_MISMATCH",
|
|
21844
23202
|
`Method "${method.name}" of contract "${implementation.contract}" and ${subject} agree on position and type but not on name for ${renamed.length} parameter(s) \u2014 ${renamed.map((f) => f.told).join("; ")}. The contract, the ERD and every brief carry one word and the code answers to another, which is a rename nobody recorded rather than a different argument \u2014 the declared type agreeing is what says so. Rename one side to the other.`,
|
|
21845
23203
|
implementation.id,
|
|
21846
|
-
|
|
23204
|
+
draftContext4,
|
|
21847
23205
|
void 0,
|
|
21848
23206
|
{ at: method.name, covers: renamed.map((found) => found.unit) }
|
|
21849
23207
|
);
|
|
@@ -21855,7 +23213,7 @@ var paramConformanceRule = {
|
|
|
21855
23213
|
"PARAM_OPTIONALITY",
|
|
21856
23214
|
`Method "${method.name}" of contract "${implementation.contract}" and ${subject} disagree about whether ${disagreed.length} parameter(s) may be left out \u2014 ${disagreed.map((f) => f.told).join("; ")}. One of them is telling a caller an argument is required when it is not, or the reverse. Omittable is the code's word for it: a default value and a rest parameter make an argument omittable exactly as a question mark does.`,
|
|
21857
23215
|
implementation.id,
|
|
21858
|
-
|
|
23216
|
+
draftContext4,
|
|
21859
23217
|
void 0,
|
|
21860
23218
|
{ at: method.name, covers: disagreed.map((found) => found.unit) }
|
|
21861
23219
|
);
|
|
@@ -21864,6 +23222,62 @@ var paramConformanceRule = {
|
|
|
21864
23222
|
}
|
|
21865
23223
|
};
|
|
21866
23224
|
|
|
23225
|
+
// src/core/rules/conformance/async-conformance.ts
|
|
23226
|
+
var asyncConformanceRule = {
|
|
23227
|
+
name: "async-conformance",
|
|
23228
|
+
judges: "code",
|
|
23229
|
+
description: "Code-to-contract for ASYNC: a contract method whose returns is `async T` is realized by a function that completes later, and one whose returns is not, by one that completes now \u2014 every caller in every language with async/await is shaped by the difference, so a contract that hides it misleads every implementer and translation built from it. Compared with source_file_facts.asyncFunctions for the method's realizing function (under its `symbol`), at exact grade only; where a name has several bodies only what they all agree on is reported, as param-conformance does. A method whose file only calls the function is methodRealization's finding.",
|
|
23230
|
+
codes: [
|
|
23231
|
+
{
|
|
23232
|
+
code: "ASYNC_MISMATCH",
|
|
23233
|
+
defaultSeverity: "warning",
|
|
23234
|
+
summary: "A contract method and the function realizing it disagree about whether the call completes later: the returns says `async T` and the code completes now, or the code is async and the returns does not say so",
|
|
23235
|
+
carryable: true
|
|
23236
|
+
}
|
|
23237
|
+
],
|
|
23238
|
+
check(ctx) {
|
|
23239
|
+
const code = ctx.codeIndex();
|
|
23240
|
+
for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
|
|
23241
|
+
const contractMethod = ctx.interfaceMap.get(implementation.contract)?.methods.find((m) => m.name === method.name);
|
|
23242
|
+
if (!contractMethod || typeof contractMethod.returns !== "string" || !sourceFile) continue;
|
|
23243
|
+
const file = pathKey(sourceFile);
|
|
23244
|
+
const facts = code.factsAt(file);
|
|
23245
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
23246
|
+
const symbol = method.symbol ?? method.name;
|
|
23247
|
+
const signatures = facts.functionParams;
|
|
23248
|
+
if (!signatures || !Object.prototype.hasOwnProperty.call(signatures, symbol)) continue;
|
|
23249
|
+
const bodies = signatures[symbol].length;
|
|
23250
|
+
if (bodies === 0) continue;
|
|
23251
|
+
const returns = parseTypeExpression(contractMethod.returns, "returns");
|
|
23252
|
+
if (!returns.expression) continue;
|
|
23253
|
+
const contractAsync = returns.expression.form === "async";
|
|
23254
|
+
const asyncBodies = (facts.asyncFunctions ?? []).filter((name) => name === symbol).length;
|
|
23255
|
+
const subject = bodies > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
|
|
23256
|
+
if (contractAsync && asyncBodies === 0) {
|
|
23257
|
+
ctx.addIssue(
|
|
23258
|
+
"warning",
|
|
23259
|
+
"ASYNC_MISMATCH",
|
|
23260
|
+
`Method "${method.name}" of contract "${implementation.contract}" returns "${contractMethod.returns}", which completes later, but ${subject} completes now \u2014 it is neither declared async nor annotated to return a Promise. Every caller is told to await a call that answers at once. Make the code async, or drop \`async\` from the returns.`,
|
|
23261
|
+
implementation.id,
|
|
23262
|
+
draftContext4,
|
|
23263
|
+
void 0,
|
|
23264
|
+
{ at: method.name }
|
|
23265
|
+
);
|
|
23266
|
+
} else if (!contractAsync && asyncBodies === bodies) {
|
|
23267
|
+
ctx.addIssue(
|
|
23268
|
+
"warning",
|
|
23269
|
+
"ASYNC_MISMATCH",
|
|
23270
|
+
`Method "${method.name}" of contract "${implementation.contract}" returns "${contractMethod.returns}", which completes now, but ${subject} completes later (declared async, or annotated to return a Promise). Every caller built from the contract forgets to await it. Write the returns as "async ${contractMethod.returns}" \u2014 or, if the code is async by accident, make it complete now.`,
|
|
23271
|
+
implementation.id,
|
|
23272
|
+
draftContext4,
|
|
23273
|
+
void 0,
|
|
23274
|
+
{ at: method.name }
|
|
23275
|
+
);
|
|
23276
|
+
}
|
|
23277
|
+
}
|
|
23278
|
+
}
|
|
23279
|
+
};
|
|
23280
|
+
|
|
21867
23281
|
// src/core/rules/conformance/route-coverage.ts
|
|
21868
23282
|
function liesUnder2(path49, prefix) {
|
|
21869
23283
|
return path49 === prefix || path49.startsWith(prefix + "/");
|
|
@@ -21932,7 +23346,7 @@ var routeCoverageRule = {
|
|
|
21932
23346
|
const routes = code.factsAt(file).functionRoutes;
|
|
21933
23347
|
return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
|
|
21934
23348
|
});
|
|
21935
|
-
const
|
|
23349
|
+
const draftContext4 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
|
|
21936
23350
|
const where = holders.map((file) => `"${file}"`).join(", ");
|
|
21937
23351
|
if (read2.length === 0) {
|
|
21938
23352
|
ctx.addIssue(
|
|
@@ -21940,7 +23354,7 @@ var routeCoverageRule = {
|
|
|
21940
23354
|
"UNREADABLE_ROUTER",
|
|
21941
23355
|
`Listener "${listener.id}" mounts portal "${portal.id}" through "${via}" in ${where}, but no branch of "${via}" reads as a route \u2014 so none of its routes were checked against the contract at all. Only one idiom is read: an \`if\` whose conditions, together with those of every enclosing \`if\`, compare \`<request>.method\` with a string and \`parts[i]\` / \`parts.length\` with literals. Silence here would read as a clean router; it is only one this analysis cannot see. Write the router in that idiom, or carry this finding with the reason it cannot be.`,
|
|
21942
23356
|
anchor,
|
|
21943
|
-
|
|
23357
|
+
draftContext4,
|
|
21944
23358
|
void 0,
|
|
21945
23359
|
{ at: via }
|
|
21946
23360
|
);
|
|
@@ -21965,7 +23379,7 @@ var routeCoverageRule = {
|
|
|
21965
23379
|
"UNDECLARED_ROUTE",
|
|
21966
23380
|
`Portal "${portal.id}"'s router "${via}" (${where}), mounted by listener "${listener.id}", answers ${undeclared.length} route(s) no contract endpoint of "${portal.id}" declares \u2014 ${undeclared.map((key) => `"${key}"`).join(", ")}. A surface the code serves and the design never promised is how a write runs with no contract: no brief, no auth review, no rule reading it. Declare each as a contract method with its endpoint, or take the route out of the router.`,
|
|
21967
23381
|
anchor,
|
|
21968
|
-
|
|
23382
|
+
draftContext4,
|
|
21969
23383
|
void 0,
|
|
21970
23384
|
{ at: via, covers: undeclared }
|
|
21971
23385
|
);
|
|
@@ -21977,7 +23391,7 @@ var routeCoverageRule = {
|
|
|
21977
23391
|
"UNROUTED_ENDPOINT",
|
|
21978
23392
|
`Portal "${portal.id}" binds ${unrouted.length} HTTP endpoint(s) under listener "${listener.id}"'s mount that no route of its router "${via}" (${where}) answers \u2014 ${unrouted.map((key) => `"${key}"`).join(", ")}. The contract promises a route no request can reach. Route it in the router, or drop the endpoint from the contract.`,
|
|
21979
23393
|
anchor,
|
|
21980
|
-
|
|
23394
|
+
draftContext4,
|
|
21981
23395
|
void 0,
|
|
21982
23396
|
{ at: via, covers: unrouted }
|
|
21983
23397
|
);
|
|
@@ -22080,7 +23494,7 @@ var exportConformanceRule = {
|
|
|
22080
23494
|
}
|
|
22081
23495
|
}
|
|
22082
23496
|
}
|
|
22083
|
-
for (const { implementation, method, sourceFile, draftContext:
|
|
23497
|
+
for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
|
|
22084
23498
|
const handle = method.exportedVia;
|
|
22085
23499
|
if (!handle || !sourceFile) continue;
|
|
22086
23500
|
const file = pathKey(sourceFile);
|
|
@@ -22095,7 +23509,7 @@ var exportConformanceRule = {
|
|
|
22095
23509
|
"UNREALIZED_EXPORT_HANDLE",
|
|
22096
23510
|
`Method "${method.name}" in implementation "${implementation.id}" declares exportedVia "${handle}", but "${file}" exports no such name. A handle names the export a consumer imports to REACH the method \u2014 the value that composes it, which \`symbol\` cannot name because \`symbol\` names the function inside \u2014 so one the file does not publish promises a route nobody can import. It allows nothing either: every export of this file is still read against the contracts. Name the binding the file actually exports, export it, or drop the handle.`,
|
|
22097
23511
|
implementation.id,
|
|
22098
|
-
|
|
23512
|
+
draftContext4
|
|
22099
23513
|
);
|
|
22100
23514
|
}
|
|
22101
23515
|
for (const listener of ctx.components) {
|
|
@@ -22468,82 +23882,6 @@ var couplingRule = {
|
|
|
22468
23882
|
}
|
|
22469
23883
|
};
|
|
22470
23884
|
|
|
22471
|
-
// src/core/rules/heuristic/signature-language-builtins.ts
|
|
22472
|
-
var LANGUAGE_MARKERS = {
|
|
22473
|
-
rust: /* @__PURE__ */ new Set([
|
|
22474
|
-
"u8",
|
|
22475
|
-
"u16",
|
|
22476
|
-
"u32",
|
|
22477
|
-
"u64",
|
|
22478
|
-
"u128",
|
|
22479
|
-
"usize",
|
|
22480
|
-
"i8",
|
|
22481
|
-
"i16",
|
|
22482
|
-
"i32",
|
|
22483
|
-
"i64",
|
|
22484
|
-
"i128",
|
|
22485
|
-
"isize",
|
|
22486
|
-
"f32",
|
|
22487
|
-
"f64",
|
|
22488
|
-
"vec",
|
|
22489
|
-
"box",
|
|
22490
|
-
"arc",
|
|
22491
|
-
"rc",
|
|
22492
|
-
"refcell",
|
|
22493
|
-
"cell",
|
|
22494
|
-
"mutex",
|
|
22495
|
-
"rwlock",
|
|
22496
|
-
"str"
|
|
22497
|
-
]),
|
|
22498
|
-
typescript: /* @__PURE__ */ new Set(["any", "unknown", "never", "undefined", "promise", "record"]),
|
|
22499
|
-
javascript: /* @__PURE__ */ new Set(["promise", "undefined"]),
|
|
22500
|
-
python: /* @__PURE__ */ new Set(["dict", "tuple"]),
|
|
22501
|
-
csharp: /* @__PURE__ */ new Set(["task"]),
|
|
22502
|
-
go: /* @__PURE__ */ new Set(["chan", "rune"])
|
|
22503
|
-
};
|
|
22504
|
-
var signatureLanguageBuiltinsRule = {
|
|
22505
|
-
name: "signature-language-builtins",
|
|
22506
|
-
judges: "design",
|
|
22507
|
-
description: "Contracts must speak the declared target language: builtin types that unambiguously belong to another language family are flagged in method signatures (e.g. `usize` in a TypeScript system, `Promise` in a Rust one). Set targetLanguage on the system (L0) or override per subsystem (L1).",
|
|
22508
|
-
codes: [
|
|
22509
|
-
{ code: "LANGUAGE_FOREIGN_BUILTIN", defaultSeverity: "warning", summary: "Signature uses a builtin from a different language family" }
|
|
22510
|
-
],
|
|
22511
|
-
check(ctx) {
|
|
22512
|
-
const markersFor = (family) => {
|
|
22513
|
-
const base = LANGUAGE_MARKERS[family];
|
|
22514
|
-
const extra = ctx.ext.languages[family]?.foreignBuiltins;
|
|
22515
|
-
if (!extra?.length) return base;
|
|
22516
|
-
return /* @__PURE__ */ new Set([...base ?? [], ...extra.map((s) => s.toLowerCase())]);
|
|
22517
|
-
};
|
|
22518
|
-
const families = [.../* @__PURE__ */ new Set([...Object.keys(LANGUAGE_MARKERS), ...Object.keys(ctx.ext.languages)])];
|
|
22519
|
-
const scans = [];
|
|
22520
|
-
for (const intf of ctx.interfaces) {
|
|
22521
|
-
const comp = ctx.componentMap.get(intf.component);
|
|
22522
|
-
const lang = ctx.targetLanguageFor(comp?.subsystem);
|
|
22523
|
-
if (!lang) continue;
|
|
22524
|
-
const ownMarkers = markersFor(lang);
|
|
22525
|
-
if (!ownMarkers || ownMarkers.size === 0) continue;
|
|
22526
|
-
const draft = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
|
|
22527
|
-
for (const method of intf.methods) {
|
|
22528
|
-
for (const ref of methodTypeRefs(method)) scans.push({ intf, method, ref, lang, ownMarkers, draft });
|
|
22529
|
-
}
|
|
22530
|
-
}
|
|
22531
|
-
for (const scan of scans) {
|
|
22532
|
-
const refLower = scan.ref.toLowerCase();
|
|
22533
|
-
if (scan.ownMarkers.has(refLower)) continue;
|
|
22534
|
-
const foreign = families.find((family) => family !== scan.lang && markersFor(family)?.has(refLower));
|
|
22535
|
-
if (!foreign) continue;
|
|
22536
|
-
ctx.addIssue(
|
|
22537
|
-
"warning",
|
|
22538
|
-
"LANGUAGE_FOREIGN_BUILTIN",
|
|
22539
|
-
`Method "${scan.method.name}" on interface "${scan.intf.id}" uses "${scan.ref}", a ${foreign} builtin, but the target language here is ${scan.lang}. Use the ${scan.lang} equivalent so implementers generate idiomatic code.`,
|
|
22540
|
-
scan.intf.id,
|
|
22541
|
-
scan.draft
|
|
22542
|
-
);
|
|
22543
|
-
}
|
|
22544
|
-
}
|
|
22545
|
-
};
|
|
22546
|
-
|
|
22547
23885
|
// src/core/rules/heuristic/narrative-language-constructs.ts
|
|
22548
23886
|
var UNSUPPORTED_FLOW = {
|
|
22549
23887
|
rust: {
|
|
@@ -23469,6 +24807,11 @@ var SDD_RULES = [
|
|
|
23469
24807
|
signatureSourcesRule,
|
|
23470
24808
|
signatureTypesRule,
|
|
23471
24809
|
signatureTextRule,
|
|
24810
|
+
// The type grammar beside the signatures: how each stored type position is
|
|
24811
|
+
// spelled, then what an enum and a named scalar may hold.
|
|
24812
|
+
typeExpressionsRule,
|
|
24813
|
+
enumTypesRule,
|
|
24814
|
+
namedScalarTypesRule,
|
|
23472
24815
|
// Contracts and the targets narratives name, in four questions with one
|
|
23473
24816
|
// owner each: does the implementation mirror its contract, does a target
|
|
23474
24817
|
// inside this tree resolve, does a target that leaves it pin to exactly one
|
|
@@ -23522,6 +24865,10 @@ var SDD_RULES = [
|
|
|
23522
24865
|
dataBlockDepsRule,
|
|
23523
24866
|
entrypointDepsRule,
|
|
23524
24867
|
portalWriteShortcutRule,
|
|
24868
|
+
// The process layer's data reach and the lifecycle effect's closure, both
|
|
24869
|
+
// read from narratives against the callee's declared effect.
|
|
24870
|
+
supervisorSharedDataRule,
|
|
24871
|
+
lifecycleEffectClosureRule,
|
|
23525
24872
|
// Patterns in four questions: who may own and what a claim must name,
|
|
23526
24873
|
// what each pattern must contain, which data blocks are left standing
|
|
23527
24874
|
// alone, and who may see a private member.
|
|
@@ -23633,14 +24980,17 @@ var SDD_RULES = [
|
|
|
23633
24980
|
// others named.
|
|
23634
24981
|
typeRealizationRule,
|
|
23635
24982
|
typeShapeRule,
|
|
24983
|
+
// An enum's values beside the data shape, and whether the call completes
|
|
24984
|
+
// later beside the signature: the same readings, of their own facts.
|
|
24985
|
+
enumValuesRule,
|
|
23636
24986
|
paramConformanceRule,
|
|
24987
|
+
asyncConformanceRule,
|
|
23637
24988
|
routeCoverageRule,
|
|
23638
24989
|
unclaimedSourceRule,
|
|
23639
24990
|
exportConformanceRule,
|
|
23640
24991
|
couplingRule,
|
|
23641
|
-
// Target-language fit
|
|
23642
|
-
//
|
|
23643
|
-
signatureLanguageBuiltinsRule,
|
|
24992
|
+
// Target-language fit: what a NARRATIVE may describe. What a contract may
|
|
24993
|
+
// name is the neutral type grammar's (type-expressions), not a language's.
|
|
23644
24994
|
narrativeLanguageConstructsRule,
|
|
23645
24995
|
// The declaration before the consequences: which stereotype may bind a
|
|
23646
24996
|
// technology at all, then where its name may appear.
|
|
@@ -23664,6 +25014,9 @@ var SDD_RULES = [
|
|
|
23664
25014
|
// stage): configuration too, and the family run cannot judge a member against
|
|
23665
25015
|
// a requirement that does not parse.
|
|
23666
25016
|
packRequirementsRule,
|
|
25017
|
+
// The fields a loaded pack still declares that wairon has deprecated: about
|
|
25018
|
+
// the packs' configuration too, and only ever a notice.
|
|
25019
|
+
packDeprecationsRule,
|
|
23667
25020
|
// MUST run last, in this order: each audits what the earlier rules did
|
|
23668
25021
|
// with a declared exception. The debt register first (which carried
|
|
23669
25022
|
// findings the conformance family actually matched), then the allows
|
|
@@ -23706,30 +25059,6 @@ var import_module2 = require("module");
|
|
|
23706
25059
|
function emptyCodeModel() {
|
|
23707
25060
|
return { files: [], projectRoot: "", rootFiles: [] };
|
|
23708
25061
|
}
|
|
23709
|
-
var EXTENSION_LANGUAGE = {
|
|
23710
|
-
".ts": "typescript",
|
|
23711
|
-
".tsx": "typescript",
|
|
23712
|
-
".mts": "typescript",
|
|
23713
|
-
".cts": "typescript",
|
|
23714
|
-
".js": "javascript",
|
|
23715
|
-
".jsx": "javascript",
|
|
23716
|
-
".mjs": "javascript",
|
|
23717
|
-
".cjs": "javascript",
|
|
23718
|
-
".py": "python",
|
|
23719
|
-
".rs": "rust",
|
|
23720
|
-
".go": "go",
|
|
23721
|
-
".cs": "csharp",
|
|
23722
|
-
".java": "java",
|
|
23723
|
-
".c": "c",
|
|
23724
|
-
".h": "c",
|
|
23725
|
-
".cpp": "cpp",
|
|
23726
|
-
".cc": "cpp",
|
|
23727
|
-
".hpp": "cpp",
|
|
23728
|
-
".rb": "ruby",
|
|
23729
|
-
".php": "php",
|
|
23730
|
-
".kt": "kotlin",
|
|
23731
|
-
".swift": "swift"
|
|
23732
|
-
};
|
|
23733
25062
|
var C_FAMILY_COMMENTS = { lineComments: ["//"], blockComments: [["/*", "*/"]] };
|
|
23734
25063
|
var NAMED_IMPORT_BINDINGS_RE = /\bimport\s*\{([^}]*)\}/g;
|
|
23735
25064
|
var JS_PATTERNS = {
|
|
@@ -23962,6 +25291,9 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
23962
25291
|
const typeShapes = /* @__PURE__ */ new Map();
|
|
23963
25292
|
const functionParams = /* @__PURE__ */ new Map();
|
|
23964
25293
|
const functionRoutes = /* @__PURE__ */ new Map();
|
|
25294
|
+
const asyncFunctions = [];
|
|
25295
|
+
const enumValues = /* @__PURE__ */ new Map();
|
|
25296
|
+
const aliasTypes = /* @__PURE__ */ new Map();
|
|
23965
25297
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
23966
25298
|
const schemaConstants = /* @__PURE__ */ new Map();
|
|
23967
25299
|
const derivedAliases = [];
|
|
@@ -24176,6 +25508,48 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
24176
25508
|
if (fn.body) walk2(fn.body, []);
|
|
24177
25509
|
return routes;
|
|
24178
25510
|
};
|
|
25511
|
+
const completesLater = (fn) => {
|
|
25512
|
+
const mods = fn.modifiers;
|
|
25513
|
+
if (mods?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) return true;
|
|
25514
|
+
const returned = fn.type;
|
|
25515
|
+
return !!returned && ts.isTypeReferenceNode(returned) && ts.isIdentifier(returned.typeName) && returned.typeName.text === "Promise";
|
|
25516
|
+
};
|
|
25517
|
+
const stringLiteralUnion = (type) => {
|
|
25518
|
+
const members = ts.isUnionTypeNode(type) ? [...type.types] : [type];
|
|
25519
|
+
const values = [];
|
|
25520
|
+
for (const member of members) {
|
|
25521
|
+
if (!ts.isLiteralTypeNode(member) || !ts.isStringLiteral(member.literal)) return void 0;
|
|
25522
|
+
values.push(member.literal.text);
|
|
25523
|
+
}
|
|
25524
|
+
return values;
|
|
25525
|
+
};
|
|
25526
|
+
const zodEnumValues = (expression) => {
|
|
25527
|
+
const strings = (array, hops) => {
|
|
25528
|
+
let node2 = array;
|
|
25529
|
+
while (node2 && (ts.isAsExpression(node2) || ts.isParenthesizedExpression(node2) || ts.isSatisfiesExpression(node2))) node2 = node2.expression;
|
|
25530
|
+
if (node2 && ts.isIdentifier(node2) && hops > 0) return strings(schemaConstants.get(node2.text), hops - 1);
|
|
25531
|
+
if (!node2 || !ts.isArrayLiteralExpression(node2) || node2.elements.length === 0) return void 0;
|
|
25532
|
+
const values = [];
|
|
25533
|
+
for (const element of node2.elements) {
|
|
25534
|
+
if (!ts.isStringLiteral(element) && !ts.isNoSubstitutionTemplateLiteral(element)) return void 0;
|
|
25535
|
+
values.push(element.text);
|
|
25536
|
+
}
|
|
25537
|
+
return values;
|
|
25538
|
+
};
|
|
25539
|
+
let node = expression;
|
|
25540
|
+
const walked = /* @__PURE__ */ new Set();
|
|
25541
|
+
while (node && !walked.has(node)) {
|
|
25542
|
+
walked.add(node);
|
|
25543
|
+
if (ts.isCallExpression(node)) {
|
|
25544
|
+
const callee = node.expression;
|
|
25545
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "enum") return strings(node.arguments[0], 1);
|
|
25546
|
+
node = callee;
|
|
25547
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
25548
|
+
node = node.expression;
|
|
25549
|
+
} else break;
|
|
25550
|
+
}
|
|
25551
|
+
return void 0;
|
|
25552
|
+
};
|
|
24179
25553
|
const visit = (node) => {
|
|
24180
25554
|
const fnName = namedFunctionName(node);
|
|
24181
25555
|
if (fnName && node.body) {
|
|
@@ -24187,6 +25561,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
24187
25561
|
const signatures = functionParams.get(fnName) ?? [];
|
|
24188
25562
|
signatures.push(declaredParameters(node));
|
|
24189
25563
|
functionParams.set(fnName, signatures);
|
|
25564
|
+
if (completesLater(node)) asyncFunctions.push(fnName);
|
|
24190
25565
|
const routes = collectRoutes(node);
|
|
24191
25566
|
if (routes.size > 0) {
|
|
24192
25567
|
const known = functionRoutes.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
@@ -24209,10 +25584,17 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
24209
25584
|
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
24210
25585
|
if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
|
|
24211
25586
|
else {
|
|
25587
|
+
if (!aliasTypes.has(node.name.text)) aliasTypes.set(node.name.text, node.type.getText(sf));
|
|
24212
25588
|
const constant = inferredSchemaConstant(node.type);
|
|
24213
25589
|
if (constant) derivedAliases.push({ name: node.name.text, constant });
|
|
25590
|
+
const literals = stringLiteralUnion(node.type);
|
|
25591
|
+
if (literals) enumValues.set(node.name.text, literals);
|
|
24214
25592
|
}
|
|
24215
25593
|
}
|
|
25594
|
+
if (ts.isEnumDeclaration(node)) {
|
|
25595
|
+
const values = node.members.map((member) => member.initializer && (ts.isStringLiteral(member.initializer) || ts.isNoSubstitutionTemplateLiteral(member.initializer)) ? member.initializer.text : void 0);
|
|
25596
|
+
if (values.length > 0 && values.every((v) => v !== void 0)) enumValues.set(node.name.text, values);
|
|
25597
|
+
}
|
|
24216
25598
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
|
|
24217
25599
|
schemaConstants.set(node.name.text, node.initializer);
|
|
24218
25600
|
}
|
|
@@ -24334,8 +25716,17 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
24334
25716
|
if (!fields) continue;
|
|
24335
25717
|
typeShapes.set(alias.name, { origin: "derived", fields, methods: [] });
|
|
24336
25718
|
}
|
|
25719
|
+
for (const [name, initializer] of schemaConstants) {
|
|
25720
|
+
const values = zodEnumValues(initializer);
|
|
25721
|
+
if (values) enumValues.set(name, values);
|
|
25722
|
+
}
|
|
25723
|
+
for (const alias of derivedAliases) {
|
|
25724
|
+
if (enumValues.has(alias.name)) continue;
|
|
25725
|
+
const values = enumValues.get(alias.constant);
|
|
25726
|
+
if (values && schemaConstants.has(alias.constant)) enumValues.set(alias.name, values);
|
|
25727
|
+
}
|
|
24337
25728
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
24338
|
-
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
|
|
25729
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
|
|
24339
25730
|
}
|
|
24340
25731
|
function resolveRelativeModule(fromFile, specifier) {
|
|
24341
25732
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -24414,7 +25805,7 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
|
|
|
24414
25805
|
const record = (absolute) => {
|
|
24415
25806
|
const key = pathKey(path18.relative(projectRoot, absolute));
|
|
24416
25807
|
if (seen.has(key) || isExcluded(key)) return;
|
|
24417
|
-
if (!
|
|
25808
|
+
if (!languageOfSourcePath(key)) return;
|
|
24418
25809
|
seen.add(key);
|
|
24419
25810
|
found.push(key);
|
|
24420
25811
|
};
|
|
@@ -24496,7 +25887,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
24496
25887
|
continue;
|
|
24497
25888
|
}
|
|
24498
25889
|
const text3 = buffer.toString("utf8");
|
|
24499
|
-
const language =
|
|
25890
|
+
const language = languageOfSourcePath(sourcePath);
|
|
24500
25891
|
let analyzed;
|
|
24501
25892
|
if (language === "typescript" || language === "javascript") {
|
|
24502
25893
|
const ts = resolveTypeScript(projectRoot);
|
|
@@ -24524,6 +25915,9 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
24524
25915
|
typeShapes: Object.fromEntries(facts.typeShapes),
|
|
24525
25916
|
functionParams: Object.fromEntries(facts.functionParams),
|
|
24526
25917
|
functionRoutes: Object.fromEntries([...facts.functionRoutes].map(([k, v]) => [k, [...v.values()]])),
|
|
25918
|
+
asyncFunctions: [...facts.asyncFunctions],
|
|
25919
|
+
enumValues: Object.fromEntries(facts.enumValues),
|
|
25920
|
+
aliasTypes: Object.fromEntries(facts.aliasTypes),
|
|
24527
25921
|
topLevelMutableBindings: [...facts.mutableBindings],
|
|
24528
25922
|
reexportOnly: facts.reexportOnly
|
|
24529
25923
|
};
|
|
@@ -24953,7 +26347,7 @@ function buildRuleContext(opts) {
|
|
|
24953
26347
|
const authoredTypeRefs = new Set((family?.authoredReferences ?? []).filter((r) => r.position === "type" && r.binding !== "local" && memberOf(r.specId) === void 0).map((r) => r.authored));
|
|
24954
26348
|
const isTypeResolved = (ref, generics) => {
|
|
24955
26349
|
const refLower = ref.toLowerCase();
|
|
24956
|
-
if (
|
|
26350
|
+
if (isTypeVocabulary(refLower)) return true;
|
|
24957
26351
|
if (generics.has(refLower)) return true;
|
|
24958
26352
|
if (ownTypes.some((spec) => typeMatchesRef(spec, ref))) return true;
|
|
24959
26353
|
const [first, ...rest] = ref.split(/::|\./);
|
|
@@ -25167,7 +26561,7 @@ function buildRuleContext(opts) {
|
|
|
25167
26561
|
}
|
|
25168
26562
|
return projectNaming;
|
|
25169
26563
|
};
|
|
25170
|
-
const isBuiltinType = (ref) =>
|
|
26564
|
+
const isBuiltinType = (ref) => isTypeVocabulary(ref);
|
|
25171
26565
|
const getRuleSeverity = (ruleCode, defaultSeverity, isDraftContext, subsystemId) => {
|
|
25172
26566
|
if (rules?.sddRuleSeverity?.[ruleCode]) {
|
|
25173
26567
|
return rules.sddRuleSeverity[ruleCode];
|
|
@@ -25198,7 +26592,16 @@ function buildRuleContext(opts) {
|
|
|
25198
26592
|
const sitesSeen = /* @__PURE__ */ new Map();
|
|
25199
26593
|
const sitesReported = (specId, code) => {
|
|
25200
26594
|
const seen = sitesSeen.get(`${specId}\0${code}`);
|
|
25201
|
-
return { sites: [...seen?.sites ?? []], unsited: seen?.unsited ?? false };
|
|
26595
|
+
return { sites: [...seen?.sites ?? []], unsited: seen?.unsited ?? false, errored: seen?.errored ?? false };
|
|
26596
|
+
};
|
|
26597
|
+
const codeDefaults = new Map(opts.issueCodeSeverities ?? []);
|
|
26598
|
+
for (const a of opts.extensions?.assertions ?? []) {
|
|
26599
|
+
if (!codeDefaults.has(a.fullCode)) codeDefaults.set(a.fullCode, a.severity);
|
|
26600
|
+
}
|
|
26601
|
+
const severityOf = (code, specId) => {
|
|
26602
|
+
const defaultSeverity = codeDefaults.get(code);
|
|
26603
|
+
if (!defaultSeverity) return void 0;
|
|
26604
|
+
return getRuleSeverity(code, defaultSeverity, false, subsystemOfSpec.get(specId));
|
|
25202
26605
|
};
|
|
25203
26606
|
for (const s of subsystems) collectAllows(s.id, s.lint);
|
|
25204
26607
|
for (const c of components) collectAllows(c.id, c.lint);
|
|
@@ -25241,9 +26644,10 @@ function buildRuleContext(opts) {
|
|
|
25241
26644
|
if (specId) {
|
|
25242
26645
|
const key = `${specId}\0${code}`;
|
|
25243
26646
|
let seen = sitesSeen.get(key);
|
|
25244
|
-
if (!seen) sitesSeen.set(key, seen = { sites: /* @__PURE__ */ new Set(), unsited: false });
|
|
26647
|
+
if (!seen) sitesSeen.set(key, seen = { sites: /* @__PURE__ */ new Set(), unsited: false, errored: false });
|
|
25245
26648
|
if (parts) seen.sites.add(parts.at);
|
|
25246
26649
|
else seen.unsited = true;
|
|
26650
|
+
if (severity === "error") seen.errored = true;
|
|
25247
26651
|
}
|
|
25248
26652
|
let allowClaimed = false;
|
|
25249
26653
|
if (specId) {
|
|
@@ -25345,7 +26749,7 @@ function buildRuleContext(opts) {
|
|
|
25345
26749
|
documentationConfigFor,
|
|
25346
26750
|
namingConfigFor,
|
|
25347
26751
|
isBuiltinType,
|
|
25348
|
-
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [] },
|
|
26752
|
+
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [], deprecations: extensions.deprecations ?? [] },
|
|
25349
26753
|
variants: opts.variants ?? [],
|
|
25350
26754
|
surfaceSnapshots,
|
|
25351
26755
|
...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
|
|
@@ -25354,11 +26758,13 @@ function buildRuleContext(opts) {
|
|
|
25354
26758
|
...opts.projectFamily ? { projectFamily: opts.projectFamily } : {},
|
|
25355
26759
|
...opts.exportUsages ? { exportUsages: opts.exportUsages } : {},
|
|
25356
26760
|
...opts.signatureFacts ? { signatureFacts: opts.signatureFacts } : {},
|
|
26761
|
+
...opts.typeSpellingFacts ? { typeSpellingFacts: opts.typeSpellingFacts } : {},
|
|
25357
26762
|
pinnedExternals,
|
|
25358
26763
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
25359
26764
|
roundTripIssues: opts.roundTripIssues,
|
|
25360
26765
|
lintAllows: lintAllows2,
|
|
25361
26766
|
sitesReported,
|
|
26767
|
+
severityOf,
|
|
25362
26768
|
knownIssueCodes: opts.knownIssueCodes,
|
|
25363
26769
|
carriedFindings,
|
|
25364
26770
|
carryableIssueCodes: opts.carryableIssueCodes ?? /* @__PURE__ */ new Set(),
|
|
@@ -25708,7 +27114,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25708
27114
|
let extensions;
|
|
25709
27115
|
let treatAllAsComplete = false;
|
|
25710
27116
|
let packSelections;
|
|
25711
|
-
if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "
|
|
27117
|
+
if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "memberDepth" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "family" in rulesOrOptions || "packSelections" in rulesOrOptions)) {
|
|
25712
27118
|
const opts = rulesOrOptions;
|
|
25713
27119
|
rules = opts.rules;
|
|
25714
27120
|
projectType = opts.projectType ?? "backend";
|
|
@@ -25718,7 +27124,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25718
27124
|
packSelections = opts.packSelections;
|
|
25719
27125
|
}
|
|
25720
27126
|
extensions ??= loadProjectExtensions();
|
|
25721
|
-
scanAllSpecs(
|
|
27127
|
+
scanAllSpecs();
|
|
25722
27128
|
const issues = [];
|
|
25723
27129
|
clearLoaderIssues();
|
|
25724
27130
|
const boundConfig = boundConfiguration();
|
|
@@ -25791,11 +27197,13 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25791
27197
|
const carryableCodes = new Set(
|
|
25792
27198
|
knownIssueCodes().filter((rc) => rc.carryable).map((rc) => rc.code)
|
|
25793
27199
|
);
|
|
27200
|
+
const codeSeverities = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
|
|
25794
27201
|
const family = graph();
|
|
25795
27202
|
const memberTables = family.nodes.filter((n) => n.namespace !== "").map((n) => resolveProjectExports(n.namespace));
|
|
25796
27203
|
const producers = new Set(family.references.filter((r) => r.consumer === "").map((r) => r.producer));
|
|
25797
27204
|
const exportUsages = [...producers].map((producer) => exportUsage("", producer));
|
|
25798
27205
|
const signatures = signatureFacts();
|
|
27206
|
+
const typeSpellings = typeSpellingFacts();
|
|
25799
27207
|
const ctx = buildRuleContext({
|
|
25800
27208
|
system,
|
|
25801
27209
|
subsystems,
|
|
@@ -25818,6 +27226,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25818
27226
|
projectFamily: family,
|
|
25819
27227
|
exportUsages,
|
|
25820
27228
|
signatureFacts: signatures,
|
|
27229
|
+
typeSpellingFacts: typeSpellings,
|
|
25821
27230
|
pinnedExternals,
|
|
25822
27231
|
// By-name selections only: a legacy path ref pins nothing to check. A dry
|
|
25823
27232
|
// run supplies its candidate's; otherwise the stored ones.
|
|
@@ -25831,6 +27240,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25831
27240
|
codeModel,
|
|
25832
27241
|
roundTripIssues,
|
|
25833
27242
|
knownIssueCodes: knownCodes,
|
|
27243
|
+
issueCodeSeverities: codeSeverities,
|
|
25834
27244
|
carryableIssueCodes: carryableCodes,
|
|
25835
27245
|
issues
|
|
25836
27246
|
});
|
|
@@ -25938,7 +27348,12 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
25938
27348
|
issues.push(issue("warning", "EXTERNAL_CHECK_UNAVAILABLE", `"${by}" names "${key}", which neither this part nor its pinned excerpt of "${parentId}" holds \u2014 used since the pin: re-pin with \`wairon externals pin\` while the parent is on disk.`, by));
|
|
25939
27349
|
}
|
|
25940
27350
|
const ownSignatures = signatureFacts();
|
|
25941
|
-
const
|
|
27351
|
+
const ownTypeSpellings = typeSpellingFacts();
|
|
27352
|
+
const partTypeSpellings = {
|
|
27353
|
+
respellings: ownTypeSpellings.respellings.filter((r) => isOwn(r.specId)),
|
|
27354
|
+
problems: ownTypeSpellings.problems.filter((p) => p.specId === void 0 || isOwn(p.specId))
|
|
27355
|
+
};
|
|
27356
|
+
const resolved = resolveSignatures(
|
|
25942
27357
|
[...own.interfaces, ...context.interfaces],
|
|
25943
27358
|
[...own.components, ...context.components],
|
|
25944
27359
|
[...own.types, ...context.types]
|
|
@@ -25977,6 +27392,7 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
25977
27392
|
projectFamily: graph(),
|
|
25978
27393
|
exportUsages: [],
|
|
25979
27394
|
signatureFacts: partSignatures,
|
|
27395
|
+
typeSpellingFacts: partTypeSpellings,
|
|
25980
27396
|
pinnedExternals: [],
|
|
25981
27397
|
packSelections: packs.filter((p) => typeof p !== "string"),
|
|
25982
27398
|
packRequirements: [],
|
|
@@ -26032,7 +27448,7 @@ function builtinProjectKinds() {
|
|
|
26032
27448
|
return [...PROJECT_KINDS];
|
|
26033
27449
|
}
|
|
26034
27450
|
function computeGateStateId() {
|
|
26035
|
-
scanAllSpecs(
|
|
27451
|
+
scanAllSpecs();
|
|
26036
27452
|
const content = computeOwnStateId();
|
|
26037
27453
|
const extensions = loadProjectExtensions();
|
|
26038
27454
|
let gate = {};
|
|
@@ -26684,16 +28100,16 @@ function bindReferencedMembers(node, root) {
|
|
|
26684
28100
|
out.push({ ...base, sourceKind: "hosted", hosted: member.source.hosted });
|
|
26685
28101
|
continue;
|
|
26686
28102
|
}
|
|
26687
|
-
const
|
|
28103
|
+
const located2 = root.referenced.find((r) => r.alias === member.alias);
|
|
26688
28104
|
const sourceKind = member.storage === "git" ? "git" : "path";
|
|
26689
|
-
const directory =
|
|
26690
|
-
const commit2 =
|
|
28105
|
+
const directory = located2?.directory ?? (member.storage === "path" ? path20.resolve(node.directory, member.source.path ?? "") : void 0);
|
|
28106
|
+
const commit2 = located2?.commit ?? member.source.commit;
|
|
26691
28107
|
out.push({
|
|
26692
28108
|
...base,
|
|
26693
28109
|
sourceKind,
|
|
26694
28110
|
...directory !== void 0 ? { directory } : {},
|
|
26695
28111
|
...commit2 !== void 0 ? { commit: commit2 } : {},
|
|
26696
|
-
...
|
|
28112
|
+
...located2?.availability === "unavailable" ? { problem: located2.reason } : {}
|
|
26697
28113
|
});
|
|
26698
28114
|
}
|
|
26699
28115
|
return out;
|
|
@@ -27517,7 +28933,8 @@ function emptyIndex() {
|
|
|
27517
28933
|
type: {},
|
|
27518
28934
|
group: {}
|
|
27519
28935
|
},
|
|
27520
|
-
signatures: { sources: [], staleTexts: [] }
|
|
28936
|
+
signatures: { sources: [], staleTexts: [] },
|
|
28937
|
+
typeSpellings: emptyTypeSpellingFacts()
|
|
27521
28938
|
};
|
|
27522
28939
|
}
|
|
27523
28940
|
function isWithin(dir, file) {
|
|
@@ -27874,7 +29291,7 @@ function bareRawReferences(kind, spec) {
|
|
|
27874
29291
|
const out = [];
|
|
27875
29292
|
const seen = /* @__PURE__ */ new Set();
|
|
27876
29293
|
const add2 = (position, value, generics = /* @__PURE__ */ new Set()) => {
|
|
27877
|
-
if (!value || value.includes("::") ||
|
|
29294
|
+
if (!value || value.includes("::") || isTypeVocabulary(value) || generics.has(value.toLowerCase())) return;
|
|
27878
29295
|
const k = `${position}|${value}`;
|
|
27879
29296
|
if (seen.has(k)) return;
|
|
27880
29297
|
seen.add(k);
|
|
@@ -28294,6 +29711,49 @@ function bindImport(raw, suppliers, p) {
|
|
|
28294
29711
|
}
|
|
28295
29712
|
};
|
|
28296
29713
|
}
|
|
29714
|
+
function typePositionText(spec, at) {
|
|
29715
|
+
const [head2, name, member, param] = at.split(".");
|
|
29716
|
+
const named2 = (list, key) => Array.isArray(list) ? list.find((x) => x?.name === key) : void 0;
|
|
29717
|
+
if (head2 === "returns") return spec.returns;
|
|
29718
|
+
if (head2 === "params") return named2(spec.params, name)?.type;
|
|
29719
|
+
if (head2 === "fields") return named2(spec.fields, name)?.type;
|
|
29720
|
+
if (head2 !== "methods") return void 0;
|
|
29721
|
+
const method = named2(spec.methods, name);
|
|
29722
|
+
return member === "returns" ? method?.returns : named2(method?.params, param)?.type;
|
|
29723
|
+
}
|
|
29724
|
+
function writtenSignatureTexts(raws) {
|
|
29725
|
+
const texts = /* @__PURE__ */ new Map();
|
|
29726
|
+
const record = (kind, specId, methods) => {
|
|
29727
|
+
for (const m of methods) {
|
|
29728
|
+
if (m.signatureFrom !== void 0) continue;
|
|
29729
|
+
const derived = deriveMethodSignature(m);
|
|
29730
|
+
if (derived !== void 0) texts.set(`${kind}|${specId}|${m.name}`, derived);
|
|
29731
|
+
}
|
|
29732
|
+
};
|
|
29733
|
+
for (const raw of raws) {
|
|
29734
|
+
for (const intf of raw.index.interfaces) record("interface", intf.id, intf.methods ?? []);
|
|
29735
|
+
for (const type of raw.index.types) record("type", type.id, type.methods ?? []);
|
|
29736
|
+
}
|
|
29737
|
+
return texts;
|
|
29738
|
+
}
|
|
29739
|
+
function canonicalizeRootTypes(raws) {
|
|
29740
|
+
const facts = emptyTypeSpellingFacts();
|
|
29741
|
+
for (const raw of raws) {
|
|
29742
|
+
raw.index.interfaces = raw.index.interfaces.map((intf) => {
|
|
29743
|
+
const read2 = interfaceCanonicalTypes(intf);
|
|
29744
|
+
facts.respellings.push(...read2.respellings);
|
|
29745
|
+
facts.problems.push(...read2.problems);
|
|
29746
|
+
return read2.spec;
|
|
29747
|
+
});
|
|
29748
|
+
raw.index.types = raw.index.types.map((type) => {
|
|
29749
|
+
const read2 = typeCanonicalTypes(type);
|
|
29750
|
+
facts.respellings.push(...read2.respellings);
|
|
29751
|
+
facts.problems.push(...read2.problems);
|
|
29752
|
+
return read2.spec;
|
|
29753
|
+
});
|
|
29754
|
+
}
|
|
29755
|
+
return facts;
|
|
29756
|
+
}
|
|
28297
29757
|
function bindRootReferences(raw, raws, readables) {
|
|
28298
29758
|
const bound2 = /* @__PURE__ */ new Map();
|
|
28299
29759
|
for (const p of raw.pending) {
|
|
@@ -28388,7 +29848,8 @@ function specKind(raw) {
|
|
|
28388
29848
|
if ("component" in raw && Array.isArray(raw.methods)) return "interface";
|
|
28389
29849
|
if ("contract" in raw && Array.isArray(raw.methods)) return "implementation";
|
|
28390
29850
|
if ("kind" in raw && Array.isArray(raw.fields)) return "type";
|
|
28391
|
-
if (raw.kind === "signature") return "type";
|
|
29851
|
+
if (raw.kind === "signature" || raw.kind === "enum") return "type";
|
|
29852
|
+
if ("kind" in raw && typeof raw.holds === "string") return "type";
|
|
28392
29853
|
return void 0;
|
|
28393
29854
|
}
|
|
28394
29855
|
function rewriteSpecRefs(raw, remap) {
|
|
@@ -28686,12 +30147,12 @@ function pairSteps(before, after) {
|
|
|
28686
30147
|
const gapBefore = before.slice(oi + 1, oj).map((step, k) => ({ step, at: oi + 1 + k }));
|
|
28687
30148
|
const gapAfter = after.slice(ni + 1, nj).map((step, k) => ({ step, at: ni + 1 + k }));
|
|
28688
30149
|
const similar = commonRun(gapBefore, gapAfter, (x, y) => recognisablySame(x.step, y.step)).map(([x, y]) => [gapBefore[x].at, gapAfter[y].at]);
|
|
28689
|
-
const
|
|
28690
|
-
for (let k = 0; k <
|
|
28691
|
-
const [a0, b0] =
|
|
28692
|
-
const [a1, b1] =
|
|
30150
|
+
const inner2 = [[oi, ni], ...similar, [oj, nj]];
|
|
30151
|
+
for (let k = 0; k < inner2.length - 1; k++) {
|
|
30152
|
+
const [a0, b0] = inner2[k];
|
|
30153
|
+
const [a1, b1] = inner2[k + 1];
|
|
28693
30154
|
if (a1 - a0 === b1 - b0) for (let d = 1; d < a1 - a0; d++) pairs.push([a0 + d, b0 + d]);
|
|
28694
|
-
if (k <
|
|
30155
|
+
if (k < inner2.length - 2) pairs.push(inner2[k + 1]);
|
|
28695
30156
|
}
|
|
28696
30157
|
if (g < bounds.length - 2) pairs.push(bounds[g + 1]);
|
|
28697
30158
|
}
|
|
@@ -28898,7 +30359,7 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
|
|
|
28898
30359
|
var SpecWorkspace = class {
|
|
28899
30360
|
constructor(rootDir) {
|
|
28900
30361
|
this.cachedIndex = null;
|
|
28901
|
-
this.
|
|
30362
|
+
this.cachedDepth = null;
|
|
28902
30363
|
this.cachedSpecDirs = [];
|
|
28903
30364
|
this.cachedSignature = null;
|
|
28904
30365
|
this.lastSignatureCheckMs = 0;
|
|
@@ -28927,7 +30388,7 @@ var SpecWorkspace = class {
|
|
|
28927
30388
|
}
|
|
28928
30389
|
invalidate() {
|
|
28929
30390
|
this.cachedIndex = null;
|
|
28930
|
-
this.
|
|
30391
|
+
this.cachedDepth = null;
|
|
28931
30392
|
this.cachedSpecDirs = [];
|
|
28932
30393
|
this.cachedConfigFiles = [];
|
|
28933
30394
|
this.cachedRoots = [];
|
|
@@ -28942,8 +30403,8 @@ var SpecWorkspace = class {
|
|
|
28942
30403
|
// Scanning
|
|
28943
30404
|
// -------------------------------------------------------------------------
|
|
28944
30405
|
scanAll(options) {
|
|
28945
|
-
const
|
|
28946
|
-
if (this.cachedIndex && this.
|
|
30406
|
+
const maxDepth = options?.memberDepth ?? Infinity;
|
|
30407
|
+
if (this.cachedIndex && this.cachedDepth === maxDepth) {
|
|
28947
30408
|
const now = Date.now();
|
|
28948
30409
|
if (now - this.lastSignatureCheckMs <= SIGNATURE_TTL_MS) return this.cachedIndex;
|
|
28949
30410
|
this.lastSignatureCheckMs = now;
|
|
@@ -28951,10 +30412,9 @@ var SpecWorkspace = class {
|
|
|
28951
30412
|
this.invalidate();
|
|
28952
30413
|
}
|
|
28953
30414
|
this.loaderIssues = [];
|
|
28954
|
-
this.
|
|
30415
|
+
this.cachedDepth = maxDepth;
|
|
28955
30416
|
this.scanVisitedSpecDirs = [];
|
|
28956
30417
|
this.scanVisitedConfigFiles = [];
|
|
28957
|
-
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
28958
30418
|
const scan = this.scanFamily(maxDepth);
|
|
28959
30419
|
this.cachedIndex = scan.index;
|
|
28960
30420
|
this.cachedRoots = scan.raws.map((raw) => raw.record);
|
|
@@ -28995,6 +30455,8 @@ var SpecWorkspace = class {
|
|
|
28995
30455
|
raws.forEach((raw) => this.bindAliases(raw, raws));
|
|
28996
30456
|
const readables = raws.map((raw) => readableOf(raw));
|
|
28997
30457
|
resolveFamilyTables(raws);
|
|
30458
|
+
const writtenTexts = writtenSignatureTexts(raws);
|
|
30459
|
+
const typeSpellings = canonicalizeRootTypes(raws);
|
|
28998
30460
|
raws.forEach((raw) => bindRootReferences(raw, raws, readables));
|
|
28999
30461
|
const index = emptyIndex();
|
|
29000
30462
|
for (const raw of raws) {
|
|
@@ -29009,7 +30471,11 @@ var SpecWorkspace = class {
|
|
|
29009
30471
|
const signatures = resolveTree(index.interfaces, index.components, index.types, raws.flatMap((r) => r.record.authoredReferences));
|
|
29010
30472
|
index.interfaces = signatures.interfaces;
|
|
29011
30473
|
index.types = signatures.types;
|
|
29012
|
-
index.signatures =
|
|
30474
|
+
index.signatures = {
|
|
30475
|
+
...signatures.facts,
|
|
30476
|
+
staleTexts: signatures.facts.staleTexts.filter((stale) => writtenTexts.get(`${stale.kind}|${stale.specId}|${stale.method}`) !== stale.stored)
|
|
30477
|
+
};
|
|
30478
|
+
index.typeSpellings = typeSpellings;
|
|
29013
30479
|
return { index, raws, readables };
|
|
29014
30480
|
}
|
|
29015
30481
|
/**
|
|
@@ -29155,26 +30621,26 @@ var SpecWorkspace = class {
|
|
|
29155
30621
|
decl.kind = "project";
|
|
29156
30622
|
return;
|
|
29157
30623
|
}
|
|
29158
|
-
const
|
|
29159
|
-
if (
|
|
29160
|
-
if ("unavailable" in
|
|
30624
|
+
const located2 = this.memberRoot(raw, decl);
|
|
30625
|
+
if (located2 === null) return;
|
|
30626
|
+
if ("unavailable" in located2) {
|
|
29161
30627
|
if (asserted === "project" || pinnedAsMember(raw.dir, decl.alias)) {
|
|
29162
30628
|
decl.kind = "project";
|
|
29163
|
-
raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason:
|
|
30629
|
+
raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
|
|
29164
30630
|
return;
|
|
29165
30631
|
}
|
|
29166
30632
|
decl.kind = "part";
|
|
29167
|
-
this.memberProblem(raw, "part-unavailable", decl.alias,
|
|
29168
|
-
raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason:
|
|
30633
|
+
this.memberProblem(raw, "part-unavailable", decl.alias, located2.unavailable);
|
|
30634
|
+
raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
|
|
29169
30635
|
return;
|
|
29170
30636
|
}
|
|
29171
|
-
const kind = projectContentAt(
|
|
30637
|
+
const kind = projectContentAt(located2.dir) ? "project" : "part";
|
|
29172
30638
|
decl.kind = kind;
|
|
29173
30639
|
if (asserted !== void 0 && asserted !== kind) {
|
|
29174
30640
|
this.memberProblem(raw, "kind-mismatch", decl.alias, `the member "${decl.alias}" is asserted \`as: ${asserted}\`, but its content makes it a ${kind}: ${kind === "project" ? "its tree declares an id, holds an L0 or carries a lock \u2014 drop the `as`, or remove that content with `wairon member demote`" : "its tree declares no id, holds no L0 and carries no lock \u2014 drop the `as`, or create the project content with `wairon member promote`"}`);
|
|
29175
30641
|
}
|
|
29176
|
-
if (kind === "part") this.readPart(raw, decl,
|
|
29177
|
-
else if (decl.storage !== "contained") this.recordReferenced(raw, decl,
|
|
30642
|
+
if (kind === "part") this.readPart(raw, decl, located2.dir, located2.commit);
|
|
30643
|
+
else if (decl.storage !== "contained") this.recordReferenced(raw, decl, located2.dir, located2.commit);
|
|
29178
30644
|
}
|
|
29179
30645
|
/**
|
|
29180
30646
|
* A referenced project member (stage 8): where its root is — the sibling
|
|
@@ -30073,7 +31539,8 @@ var SpecWorkspace = class {
|
|
|
30073
31539
|
* the owning project.
|
|
30074
31540
|
*/
|
|
30075
31541
|
prepareInterfaceForWrite(spec) {
|
|
30076
|
-
const
|
|
31542
|
+
const canonical3 = interfaceCanonicalTypes(spec).spec;
|
|
31543
|
+
const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedMethodSignature(m)) };
|
|
30077
31544
|
return this.relativizeSpec("interface", stored);
|
|
30078
31545
|
}
|
|
30079
31546
|
prepareImplementationForWrite(spec, carry = true) {
|
|
@@ -30084,9 +31551,14 @@ var SpecWorkspace = class {
|
|
|
30084
31551
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
30085
31552
|
return partDir ? partRelativeFilePaths(relative19, this.rootDir, partDir) : relative19;
|
|
30086
31553
|
}
|
|
30087
|
-
/**
|
|
31554
|
+
/**
|
|
31555
|
+
* A type written in its stored form — every type position canonical (one
|
|
31556
|
+
* that is not canonical written as it stands), every params-bearing method's
|
|
31557
|
+
* text derived from them — relative to the owning project.
|
|
31558
|
+
*/
|
|
30088
31559
|
prepareTypeForWrite(spec) {
|
|
30089
|
-
const
|
|
31560
|
+
const canonical3 = typeCanonicalTypes(spec).spec;
|
|
31561
|
+
const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedTypeMethod(m)) };
|
|
30090
31562
|
return this.relativizeSpec("type", stored);
|
|
30091
31563
|
}
|
|
30092
31564
|
prepareGroupForWrite(spec) {
|
|
@@ -30297,7 +31769,7 @@ var SpecWorkspace = class {
|
|
|
30297
31769
|
if (!pathExists(p)) return null;
|
|
30298
31770
|
try {
|
|
30299
31771
|
const raw = readSpecFile(p);
|
|
30300
|
-
return resolveTree([InterfaceSpecSchema.parse(raw)], index.components, index.types).interfaces[0];
|
|
31772
|
+
return resolveTree([interfaceCanonicalTypes(InterfaceSpecSchema.parse(raw)).spec], index.components, index.types).interfaces[0];
|
|
30301
31773
|
} catch (e) {
|
|
30302
31774
|
this.loaderIssues.push({
|
|
30303
31775
|
severity: "error",
|
|
@@ -30873,6 +32345,33 @@ var SpecWorkspace = class {
|
|
|
30873
32345
|
index.listProjectRoots();
|
|
30874
32346
|
return this.cachedIndex.signatures;
|
|
30875
32347
|
}
|
|
32348
|
+
/**
|
|
32349
|
+
* A delta's merged spec with every type position canonical, IN PLACE (an
|
|
32350
|
+
* interface or a type; nothing else holds type positions). Answers the
|
|
32351
|
+
* respellings the write applies: each alias the merge holds — written by the
|
|
32352
|
+
* delta — and each alias the stored file still holds at a position the merge
|
|
32353
|
+
* kept, which the scan recorded and the save rewrites with it. A position
|
|
32354
|
+
* that is not canonical stays as written.
|
|
32355
|
+
*/
|
|
32356
|
+
canonicalizeMerged(kind, merged) {
|
|
32357
|
+
if (kind !== "interface" && kind !== "type") return [];
|
|
32358
|
+
const read2 = kind === "interface" ? interfaceCanonicalTypes(merged) : typeCanonicalTypes(merged);
|
|
32359
|
+
Object.assign(merged, read2.spec);
|
|
32360
|
+
const respelled = new Set(read2.respellings.map((r) => r.path));
|
|
32361
|
+
const fromFile = this.scanAll().typeSpellings.respellings.filter((r) => r.specId === merged.id && r.kind === kind && !respelled.has(r.path) && typePositionText(merged, r.path) === r.stored);
|
|
32362
|
+
return [...read2.respellings, ...fromFile];
|
|
32363
|
+
}
|
|
32364
|
+
/**
|
|
32365
|
+
* ispec_index.typeSpellingFacts — what the current scan's type
|
|
32366
|
+
* canonicalisation recorded: every stored type position that is an alias of
|
|
32367
|
+
* its canonical spelling, and every one that is not canonical at all. Read
|
|
32368
|
+
* from the cached scan, rescanning first when the tree changed.
|
|
32369
|
+
*/
|
|
32370
|
+
typeSpellingFacts() {
|
|
32371
|
+
const index = this;
|
|
32372
|
+
index.listProjectRoots();
|
|
32373
|
+
return this.cachedIndex.typeSpellings;
|
|
32374
|
+
}
|
|
30876
32375
|
snapshotSpecFiles() {
|
|
30877
32376
|
const index = this.scanAll();
|
|
30878
32377
|
const snapshot = /* @__PURE__ */ new Map();
|
|
@@ -31483,6 +32982,7 @@ var SpecWorkspace = class {
|
|
|
31483
32982
|
const qualifiedDelta = qualifyDeltaRefs(mergeableDelta);
|
|
31484
32983
|
const mergedResult = mergeDelta(result, qualifiedDelta);
|
|
31485
32984
|
for (const field of unsetFields) delete mergedResult[field];
|
|
32985
|
+
const respellings = this.canonicalizeMerged(kind, mergedResult);
|
|
31486
32986
|
deriveMergedTexts(kind, mergedResult);
|
|
31487
32987
|
const withDefaults = deltaSchema.safeParse(mergedResult);
|
|
31488
32988
|
if (withDefaults.success) {
|
|
@@ -31523,6 +33023,7 @@ var SpecWorkspace = class {
|
|
|
31523
33023
|
changes,
|
|
31524
33024
|
ineffective,
|
|
31525
33025
|
notices,
|
|
33026
|
+
respellings: [],
|
|
31526
33027
|
testsToRevisit: [],
|
|
31527
33028
|
summary: `No change to ${kind} "${id}" \u2014 the delta matches what is stored, so nothing ${dryRun ? "would be" : "was"} written.`
|
|
31528
33029
|
};
|
|
@@ -31539,6 +33040,7 @@ var SpecWorkspace = class {
|
|
|
31539
33040
|
changes,
|
|
31540
33041
|
ineffective,
|
|
31541
33042
|
notices,
|
|
33043
|
+
respellings,
|
|
31542
33044
|
testsToRevisit: [],
|
|
31543
33045
|
...stripped.length ? { strippedKeys: stripped } : {},
|
|
31544
33046
|
summary: `Dry run on ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"} would be made. Nothing was written.`
|
|
@@ -31557,6 +33059,7 @@ var SpecWorkspace = class {
|
|
|
31557
33059
|
changes,
|
|
31558
33060
|
ineffective,
|
|
31559
33061
|
notices,
|
|
33062
|
+
respellings,
|
|
31560
33063
|
// The store never searches for tests: it holds the tree and knows
|
|
31561
33064
|
// nothing about the rule engine that reads code. The GATED write above
|
|
31562
33065
|
// it fills this in, which is also the only write path a human drives.
|
|
@@ -32111,6 +33614,7 @@ function moveChangeReports(edits, dryRun) {
|
|
|
32111
33614
|
changes,
|
|
32112
33615
|
ineffective: [],
|
|
32113
33616
|
notices: [],
|
|
33617
|
+
respellings: [],
|
|
32114
33618
|
testsToRevisit: [],
|
|
32115
33619
|
summary: edit4.before === null ? `${dryRun ? "Would create" : "Created"} ${edit4.kind} "${edit4.id}" to receive the moved methods.` : `${dryRun ? "Would move" : "Moved"} into ${edit4.kind} "${edit4.id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
|
|
32116
33620
|
}));
|
|
@@ -32409,6 +33913,9 @@ function snapshotSpecFiles() {
|
|
|
32409
33913
|
function signatureFacts() {
|
|
32410
33914
|
return current().signatureFacts();
|
|
32411
33915
|
}
|
|
33916
|
+
function typeSpellingFacts() {
|
|
33917
|
+
return current().typeSpellingFacts();
|
|
33918
|
+
}
|
|
32412
33919
|
function findLegacySpecFiles() {
|
|
32413
33920
|
return current().findLegacySpecFiles();
|
|
32414
33921
|
}
|
|
@@ -35230,6 +36737,19 @@ function composeAgentBrief(agentId) {
|
|
|
35230
36737
|
## Project guidance
|
|
35231
36738
|
|
|
35232
36739
|
${guidance.trim()}
|
|
36740
|
+
`;
|
|
36741
|
+
}
|
|
36742
|
+
const language = implementationLanguage(record);
|
|
36743
|
+
const dialect = language ? typeDialectFor(language) : null;
|
|
36744
|
+
const typeMapping = dialect ? dialect.mappingLines() : void 0;
|
|
36745
|
+
if (typeMapping) {
|
|
36746
|
+
instructions = `${instructions.trimEnd()}
|
|
36747
|
+
|
|
36748
|
+
## Types in ${language}
|
|
36749
|
+
|
|
36750
|
+
Contracts speak wairon's neutral type grammar; write each type in ${language} as:
|
|
36751
|
+
|
|
36752
|
+
${typeMapping.map((line) => `- ${line}`).join("\n")}
|
|
35233
36753
|
`;
|
|
35234
36754
|
}
|
|
35235
36755
|
const profile = deriveExecutionProfile(record);
|
|
@@ -35243,10 +36763,28 @@ ${guidance.trim()}
|
|
|
35243
36763
|
readPaths: record.readPaths,
|
|
35244
36764
|
instructions,
|
|
35245
36765
|
variantGuidance: record.variantGuidance || void 0,
|
|
36766
|
+
...typeMapping ? { typeMapping } : {},
|
|
35246
36767
|
profile: budget ? profile : void 0,
|
|
35247
36768
|
budget
|
|
35248
36769
|
};
|
|
35249
36770
|
}
|
|
36771
|
+
function implementationLanguage(record) {
|
|
36772
|
+
const owned = new Set(record.ownedPaths);
|
|
36773
|
+
const implementations = loadImplementationSpecs().filter((impl) => implementationSourceFiles(impl).some((file) => owned.has(file)));
|
|
36774
|
+
for (const impl of implementations) {
|
|
36775
|
+
for (const technology of impl.technologies ?? []) {
|
|
36776
|
+
const name = typeof technology === "string" ? technology : technology.name;
|
|
36777
|
+
if (typeDialectFor(name)) return name.toLowerCase();
|
|
36778
|
+
}
|
|
36779
|
+
}
|
|
36780
|
+
for (const impl of implementations) {
|
|
36781
|
+
for (const file of implementationSourceFiles(impl)) {
|
|
36782
|
+
const language = languageOfSourcePath(file);
|
|
36783
|
+
if (language) return language;
|
|
36784
|
+
}
|
|
36785
|
+
}
|
|
36786
|
+
return void 0;
|
|
36787
|
+
}
|
|
35250
36788
|
function memberHop(agentId) {
|
|
35251
36789
|
const at = agentId.indexOf("::");
|
|
35252
36790
|
if (at < 0) return null;
|
|
@@ -35824,6 +37362,52 @@ function repairSignatures(apply3) {
|
|
|
35824
37362
|
return planned;
|
|
35825
37363
|
}
|
|
35826
37364
|
|
|
37365
|
+
// src/core/type-spelling-repair.ts
|
|
37366
|
+
function repairTypeSpellings(apply3) {
|
|
37367
|
+
const facts = typeSpellingFacts();
|
|
37368
|
+
const held = new Set(signatureFacts().sources.filter((f) => f.outcome === "restated" && f.differs).map((f) => f.interfaceId));
|
|
37369
|
+
const repairs = /* @__PURE__ */ new Map();
|
|
37370
|
+
const repairOf = (specId, kind) => {
|
|
37371
|
+
const key = `${kind}:${specId}`;
|
|
37372
|
+
let repair = repairs.get(key);
|
|
37373
|
+
if (!repair) {
|
|
37374
|
+
repair = { specId, kind, rewritten: [], proposals: [], authorNeeded: [] };
|
|
37375
|
+
repairs.set(key, repair);
|
|
37376
|
+
}
|
|
37377
|
+
return repair;
|
|
37378
|
+
};
|
|
37379
|
+
const own = (specId) => !specId.includes("::");
|
|
37380
|
+
for (const respelling of facts.respellings) {
|
|
37381
|
+
if (!own(respelling.specId)) continue;
|
|
37382
|
+
if (respelling.kind === "interface" && held.has(respelling.specId)) continue;
|
|
37383
|
+
repairOf(respelling.specId, respelling.kind).rewritten.push(respelling);
|
|
37384
|
+
}
|
|
37385
|
+
for (const problem of facts.problems) {
|
|
37386
|
+
if (problem.specId === void 0 || problem.kind === void 0 || !own(problem.specId)) continue;
|
|
37387
|
+
const repair = repairOf(problem.specId, problem.kind);
|
|
37388
|
+
const proposal = typeProblemIntProposal(problem);
|
|
37389
|
+
if (proposal) repair.proposals.push(proposal);
|
|
37390
|
+
else repair.authorNeeded.push(problem);
|
|
37391
|
+
}
|
|
37392
|
+
const planned = [...repairs.values()];
|
|
37393
|
+
const rewriting = planned.filter((repair) => repair.rewritten.length > 0);
|
|
37394
|
+
if (apply3 && rewriting.length > 0) {
|
|
37395
|
+
const interfaces = new Map(loadInterfaceSpecs().map((i) => [i.id, i]));
|
|
37396
|
+
const types = new Map(loadTypeSpecs().map((t) => [t.id, t]));
|
|
37397
|
+
for (const repair of rewriting) {
|
|
37398
|
+
if (repair.kind === "interface") {
|
|
37399
|
+
const intf = interfaces.get(repair.specId);
|
|
37400
|
+
if (intf) saveInterfaceSpec(intf);
|
|
37401
|
+
continue;
|
|
37402
|
+
}
|
|
37403
|
+
const type = types.get(repair.specId);
|
|
37404
|
+
if (type) saveSpec("type", type);
|
|
37405
|
+
}
|
|
37406
|
+
invalidateSpecCache();
|
|
37407
|
+
}
|
|
37408
|
+
return planned;
|
|
37409
|
+
}
|
|
37410
|
+
|
|
35827
37411
|
// src/core/approval.ts
|
|
35828
37412
|
var crypto7 = __toESM(require("crypto"));
|
|
35829
37413
|
var path35 = __toESM(require("path"));
|
|
@@ -36342,7 +37926,7 @@ function parentPinState(parent) {
|
|
|
36342
37926
|
}
|
|
36343
37927
|
function getStatusReport(options = {}, decor) {
|
|
36344
37928
|
const mark = fillDecor(decor);
|
|
36345
|
-
scanAllSpecs({
|
|
37929
|
+
scanAllSpecs({ memberDepth: options.memberDepth });
|
|
36346
37930
|
const system = loadSystemSpec();
|
|
36347
37931
|
const loaderErrors = getLoaderIssues();
|
|
36348
37932
|
if (loaderErrors.length > 0) {
|
|
@@ -36532,6 +38116,11 @@ function getStatusReport(options = {}, decor) {
|
|
|
36532
38116
|
}
|
|
36533
38117
|
|
|
36534
38118
|
// src/core/index.ts
|
|
38119
|
+
function resolveSignatures(interfaces, components, types) {
|
|
38120
|
+
const canonicalInterfaces = interfaces.map((intf) => interfaceCanonicalTypes(intf).spec);
|
|
38121
|
+
const canonicalTypes = types.map((type) => typeCanonicalTypes(type).spec);
|
|
38122
|
+
return resolveTree(canonicalInterfaces, components, canonicalTypes);
|
|
38123
|
+
}
|
|
36535
38124
|
function resolveDomains2() {
|
|
36536
38125
|
return resolveDomains();
|
|
36537
38126
|
}
|
|
@@ -36847,12 +38436,14 @@ function noticesFrom(verdict) {
|
|
|
36847
38436
|
...verdict.notices.map((n) => `${n.code} (notice): ${n.message}`)
|
|
36848
38437
|
];
|
|
36849
38438
|
}
|
|
36850
|
-
function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
38439
|
+
function componentCandidateGate(options = candidateOptions(), storedOwner, stored) {
|
|
36851
38440
|
return {
|
|
36852
38441
|
gate: (kind, merged) => {
|
|
36853
38442
|
refuseUnknownOwner(kind, merged, storedOwner);
|
|
36854
38443
|
const restated = kind === "interface" ? restatedSources(merged.methods) : [];
|
|
36855
38444
|
if (restated.length) throw new Error(`${restatedSourceRefusal(String(merged.id), restated)} Nothing was written.`);
|
|
38445
|
+
const written = deltaWrittenTypeProblems(kind, merged, stored ?? null);
|
|
38446
|
+
if (written.length) throw new Error(`${typeProblemRefusal(kind, String(merged.id), written)} Nothing was written.`);
|
|
36856
38447
|
if (kind !== "component") return;
|
|
36857
38448
|
const verdict = validateComponentCandidate(merged, options);
|
|
36858
38449
|
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
@@ -36860,6 +38451,22 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
|
36860
38451
|
}
|
|
36861
38452
|
};
|
|
36862
38453
|
}
|
|
38454
|
+
function canonicalTypesOf(kind, spec) {
|
|
38455
|
+
if (!spec || typeof spec !== "object") return null;
|
|
38456
|
+
if (kind === "interface") return interfaceCanonicalTypes(spec);
|
|
38457
|
+
if (kind === "type") return typeCanonicalTypes(spec);
|
|
38458
|
+
return null;
|
|
38459
|
+
}
|
|
38460
|
+
function deltaWrittenTypeProblems(kind, merged, stored) {
|
|
38461
|
+
const now = canonicalTypesOf(kind, merged)?.problems ?? [];
|
|
38462
|
+
if (now.length === 0) return [];
|
|
38463
|
+
const before = new Set((canonicalTypesOf(kind, stored)?.problems ?? []).map((p) => `${p.path}|${p.written}`));
|
|
38464
|
+
return now.filter((p) => !before.has(`${p.path}|${p.written}`));
|
|
38465
|
+
}
|
|
38466
|
+
function typeProblemRefusal(kind, id, problems) {
|
|
38467
|
+
const lines = problems.map((p) => `${p.code} at ${p.path}: ${p.detail}${p.replacement ? ` \u2014 write ${p.replacement} instead` : ""}.`);
|
|
38468
|
+
return `Refusing to write ${kind} "${id}": ${problems.length === 1 ? "a type position is" : `${problems.length} type positions are`} not in the neutral type grammar. ${lines.join(" ")} Aliases (string[], T | null, boolean, Promise<T>, ...) are accepted and respelled; these forms have no canonical spelling.`;
|
|
38469
|
+
}
|
|
36863
38470
|
function restatedSources(methods) {
|
|
36864
38471
|
if (!Array.isArray(methods)) return [];
|
|
36865
38472
|
return methods.filter((m) => m && typeof m === "object" && m.signatureFrom !== void 0 && (m.params !== void 0 || m.returns !== void 0)).map((m) => String(m.name));
|
|
@@ -36965,6 +38572,11 @@ function applyRestatement(restatement, loaded, parent) {
|
|
|
36965
38572
|
`Refusing to write ${restatement.kind} "${String(candidate.id)}": ${unsigned.map((n) => `"${n}"`).join(", ")} ${unsigned.length === 1 ? "states" : "state"} neither params${restatement.kind === "interface" ? ", a signatureFrom" : ""} nor a prose signature, so nothing says what the method takes. Nothing was written.`
|
|
36966
38573
|
);
|
|
36967
38574
|
}
|
|
38575
|
+
const types = canonicalTypesOf(restatement.kind, candidate);
|
|
38576
|
+
if (types && types.problems.length) {
|
|
38577
|
+
return refused(restatement, replacedExisting, `${typeProblemRefusal(restatement.kind, String(candidate.id), types.problems)} Nothing was written.`);
|
|
38578
|
+
}
|
|
38579
|
+
if (types) Object.assign(candidate, types.spec);
|
|
36968
38580
|
const derivedNotices = deriveStatedTexts(restatement.kind, candidate);
|
|
36969
38581
|
const labelErrors = resolveLabelsOf(restatement.kind, candidate);
|
|
36970
38582
|
if (labelErrors.length) {
|
|
@@ -36978,7 +38590,8 @@ function applyRestatement(restatement, loaded, parent) {
|
|
|
36978
38590
|
...status.status ? { status: status.status } : {},
|
|
36979
38591
|
replacedExisting,
|
|
36980
38592
|
notices: [...existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [], ...derivedNotices],
|
|
36981
|
-
changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
|
|
38593
|
+
changedMethods: changedMethodsOf(restatement.kind, existing, candidate),
|
|
38594
|
+
respellings: types?.respellings ?? []
|
|
36982
38595
|
};
|
|
36983
38596
|
}
|
|
36984
38597
|
var SPEC_SCHEMA = {
|
|
@@ -37017,7 +38630,7 @@ function parseCandidate(kind, candidate) {
|
|
|
37017
38630
|
};
|
|
37018
38631
|
}
|
|
37019
38632
|
function refused(restatement, replacedExisting, refusal2) {
|
|
37020
|
-
return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [] };
|
|
38633
|
+
return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [], respellings: [] };
|
|
37021
38634
|
}
|
|
37022
38635
|
function statusForCreate(restatement, existing) {
|
|
37023
38636
|
if (!STATUSED_KINDS.has(restatement.kind)) return {};
|
|
@@ -37162,6 +38775,7 @@ function writeSpec(restatement) {
|
|
|
37162
38775
|
replacedExisting: application.replacedExisting,
|
|
37163
38776
|
...application.status ? { status: application.status } : {},
|
|
37164
38777
|
notices: [...gateNotices, ...persisted.notices, ...application.notices],
|
|
38778
|
+
respellings: application.respellings,
|
|
37165
38779
|
testsToRevisit
|
|
37166
38780
|
};
|
|
37167
38781
|
}
|
|
@@ -37217,7 +38831,7 @@ function updateSpecGated(kind, id, delta, dryRun) {
|
|
|
37217
38831
|
const bound2 = candidateOptions();
|
|
37218
38832
|
const testRoots = bound2.rules?.conformance?.testRoots ?? [];
|
|
37219
38833
|
const stored = loadSpec(kind, id);
|
|
37220
|
-
const gate = componentCandidateGate(bound2, stored?.subsystem);
|
|
38834
|
+
const gate = componentCandidateGate(bound2, stored?.subsystem, stored);
|
|
37221
38835
|
const report2 = updateSpec(kind, id, delta, gate, dryRun);
|
|
37222
38836
|
const changed = changedMethods(report2);
|
|
37223
38837
|
if (changed.length > 0 && testRoots.length > 0) {
|
|
@@ -40641,7 +42255,7 @@ function errText(message) {
|
|
|
40641
42255
|
function errMessage(e) {
|
|
40642
42256
|
return errText(e instanceof Error ? e.message : String(e));
|
|
40643
42257
|
}
|
|
40644
|
-
var TYPE_REF_GRAMMAR = (what) => `${what}: a primitive
|
|
42258
|
+
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.`;
|
|
40645
42259
|
function structured(content, data) {
|
|
40646
42260
|
return { content: [{ type: "text", text: content }], structuredContent: data };
|
|
40647
42261
|
}
|
|
@@ -40736,6 +42350,7 @@ function renderChangeReport(report2) {
|
|
|
40736
42350
|
if (report2.strippedKeys?.length) {
|
|
40737
42351
|
lines.push("", `DROPPED FROM THE STORED FILE (${report2.dryRun ? "would be " : ""}removed \u2014 the schema does not know them):`, ...report2.strippedKeys.map((k) => `- ${k}`));
|
|
40738
42352
|
}
|
|
42353
|
+
lines.push(...renderRespellings(report2.respellings));
|
|
40739
42354
|
lines.push(...renderTestsToRevisit(report2.testsToRevisit));
|
|
40740
42355
|
return lines.join("\n");
|
|
40741
42356
|
}
|
|
@@ -40763,6 +42378,17 @@ function renderTestsToRevisit(entries) {
|
|
|
40763
42378
|
}
|
|
40764
42379
|
return lines;
|
|
40765
42380
|
}
|
|
42381
|
+
var typeRespellingOutput = import_zod11.z.object({
|
|
42382
|
+
specId: import_zod11.z.string().describe("The interface or type holding the position."),
|
|
42383
|
+
kind: import_zod11.z.enum(["interface", "type"]).describe("interface | type."),
|
|
42384
|
+
path: import_zod11.z.string().describe("Where in the spec: methods.save.params.key, methods.save.returns, fields.createdAt, params.listener."),
|
|
42385
|
+
written: import_zod11.z.string().describe("The text the input (or the stored file) held."),
|
|
42386
|
+
stored: import_zod11.z.string().describe("The canonical spelling, which is what is stored and shown.")
|
|
42387
|
+
});
|
|
42388
|
+
function renderRespellings(entries) {
|
|
42389
|
+
if (!entries.length) return [];
|
|
42390
|
+
return ["", "RESPELLED (stored canonical):", ...entries.map((r) => `- ${r.path}: "${r.written}" \u2192 "${r.stored}"`)];
|
|
42391
|
+
}
|
|
40766
42392
|
var specWriteReceiptOutput = {
|
|
40767
42393
|
kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
|
|
40768
42394
|
id: import_zod11.z.string().describe('The id the spec is stored under; "system" for the L0 singleton.'),
|
|
@@ -40776,6 +42402,9 @@ var specWriteReceiptOutput = {
|
|
|
40776
42402
|
notices: import_zod11.z.array(import_zod11.z.string()).describe(
|
|
40777
42403
|
"The notice lines the text answer lists, one per entry: what was carried forward, what a restatement removed, what an omission cleared, and every gate warning the write raised. Empty for a clean create."
|
|
40778
42404
|
),
|
|
42405
|
+
respellings: import_zod11.z.array(typeRespellingOutput).describe(
|
|
42406
|
+
"Each type position the write normalised: what the input wrote against the canonical spelling stored. Aliases are accepted, never refused, and this list is how an author learns the canonical form. Empty when every position was already canonical."
|
|
42407
|
+
),
|
|
40779
42408
|
testsToRevisit: testsToRevisitOutput,
|
|
40780
42409
|
...staleServerOutput
|
|
40781
42410
|
};
|
|
@@ -40784,8 +42413,10 @@ function writeReceipt(sentence, receipt) {
|
|
|
40784
42413
|
|
|
40785
42414
|
NOTICE:
|
|
40786
42415
|
- ${receipt.notices.join("\n- ")}` : "";
|
|
42416
|
+
const respelledBlock = renderRespellings(receipt.respellings).join("\n");
|
|
40787
42417
|
const testsBlock = renderTestsToRevisit(receipt.testsToRevisit).join("\n");
|
|
40788
|
-
return structured(`${sentence}${noticeBlock}${
|
|
42418
|
+
return structured(`${sentence}${noticeBlock}${respelledBlock ? `
|
|
42419
|
+
${respelledBlock}` : ""}${testsBlock ? `
|
|
40789
42420
|
${testsBlock}` : ""}`, receipt);
|
|
40790
42421
|
}
|
|
40791
42422
|
var specDeletionOutput = {
|
|
@@ -40817,6 +42448,9 @@ var specChangeReportOutput = {
|
|
|
40817
42448
|
"Every path the delta named that the write did not act on, each with why. Read it: a nested typo the permissive delta cannot refuse shows up here and nowhere else."
|
|
40818
42449
|
),
|
|
40819
42450
|
notices: import_zod11.z.array(import_zod11.z.string()).describe("Store placement notices, gate warnings and delta notices."),
|
|
42451
|
+
respellings: import_zod11.z.array(typeRespellingOutput).describe(
|
|
42452
|
+
"Each type position the write normalised (or, on a dry run, would): what the delta or the stored file wrote against the canonical spelling stored. Aliases are accepted and respelled, never refused \u2014 this is how the canonical form is taught. Empty when every position was already canonical."
|
|
42453
|
+
),
|
|
40820
42454
|
summary: import_zod11.z.string().describe("One line for people."),
|
|
40821
42455
|
testsToRevisit: testsToRevisitOutput,
|
|
40822
42456
|
strippedKeys: import_zod11.z.array(import_zod11.z.string()).optional().describe(
|
|
@@ -41016,46 +42650,46 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
|
|
|
41016
42650
|
const known = seen.get(schema);
|
|
41017
42651
|
if (known !== void 0) return { ref: known };
|
|
41018
42652
|
seen.set(schema, seen.size);
|
|
41019
|
-
const
|
|
42653
|
+
const inner2 = (s) => schemaShape(s, seen);
|
|
41020
42654
|
const kind = String(def.typeName);
|
|
41021
42655
|
switch (kind) {
|
|
41022
42656
|
case "ZodObject": {
|
|
41023
42657
|
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
41024
42658
|
return {
|
|
41025
42659
|
kind,
|
|
41026
|
-
keys: Object.keys(shape).sort().map((key) => [key,
|
|
42660
|
+
keys: Object.keys(shape).sort().map((key) => [key, inner2(shape[key])]),
|
|
41027
42661
|
unknownKeys: def.unknownKeys,
|
|
41028
|
-
catchall:
|
|
42662
|
+
catchall: inner2(def.catchall)
|
|
41029
42663
|
};
|
|
41030
42664
|
}
|
|
41031
42665
|
case "ZodArray":
|
|
41032
|
-
return { kind, of:
|
|
42666
|
+
return { kind, of: inner2(def.type), min: def.minLength?.value, max: def.maxLength?.value };
|
|
41033
42667
|
case "ZodOptional":
|
|
41034
42668
|
case "ZodNullable":
|
|
41035
42669
|
case "ZodReadonly":
|
|
41036
42670
|
case "ZodBranded":
|
|
41037
42671
|
case "ZodCatch":
|
|
41038
|
-
return { kind, of:
|
|
42672
|
+
return { kind, of: inner2(def.innerType ?? def.type) };
|
|
41039
42673
|
case "ZodDefault":
|
|
41040
|
-
return { kind, of:
|
|
42674
|
+
return { kind, of: inner2(def.innerType), default: JSON.stringify(def.defaultValue?.()) };
|
|
41041
42675
|
case "ZodEffects":
|
|
41042
|
-
return { kind, effect: def.effect?.type, of:
|
|
42676
|
+
return { kind, effect: def.effect?.type, of: inner2(def.schema) };
|
|
41043
42677
|
case "ZodLazy":
|
|
41044
|
-
return { kind, of:
|
|
42678
|
+
return { kind, of: inner2(def.getter()) };
|
|
41045
42679
|
case "ZodUnion":
|
|
41046
42680
|
case "ZodDiscriminatedUnion": {
|
|
41047
42681
|
const options = def.options instanceof Map ? [...def.options.values()] : def.options;
|
|
41048
|
-
return { kind, discriminator: def.discriminator, of: options.map(
|
|
42682
|
+
return { kind, discriminator: def.discriminator, of: options.map(inner2) };
|
|
41049
42683
|
}
|
|
41050
42684
|
case "ZodIntersection":
|
|
41051
|
-
return { kind, left:
|
|
42685
|
+
return { kind, left: inner2(def.left), right: inner2(def.right) };
|
|
41052
42686
|
case "ZodTuple":
|
|
41053
|
-
return { kind, of: def.items.map(
|
|
42687
|
+
return { kind, of: def.items.map(inner2), rest: inner2(def.rest) };
|
|
41054
42688
|
case "ZodRecord":
|
|
41055
42689
|
case "ZodMap":
|
|
41056
|
-
return { kind, key:
|
|
42690
|
+
return { kind, key: inner2(def.keyType), value: inner2(def.valueType) };
|
|
41057
42691
|
case "ZodSet":
|
|
41058
|
-
return { kind, of:
|
|
42692
|
+
return { kind, of: inner2(def.valueType) };
|
|
41059
42693
|
case "ZodEnum":
|
|
41060
42694
|
return { kind, values: def.values };
|
|
41061
42695
|
case "ZodNativeEnum":
|
|
@@ -41063,7 +42697,7 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
|
|
|
41063
42697
|
case "ZodLiteral":
|
|
41064
42698
|
return { kind, value: def.value };
|
|
41065
42699
|
case "ZodPipeline":
|
|
41066
|
-
return { kind, in:
|
|
42700
|
+
return { kind, in: inner2(def.in), out: inner2(def.out) };
|
|
41067
42701
|
default:
|
|
41068
42702
|
return { kind, checks: def.checks?.map((c) => [c.kind, c.value ?? null]) ?? [] };
|
|
41069
42703
|
}
|
|
@@ -41791,7 +43425,7 @@ ${renderChangeReport(report2)}`,
|
|
|
41791
43425
|
type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
|
|
41792
43426
|
description: import_zod11.z.string().optional(),
|
|
41793
43427
|
optional: import_zod11.z.boolean().optional().describe(
|
|
41794
|
-
'Whether the parameter may be OMITTED by a caller. It is not nullability: a parameter that must be passed but may be passed as nothing is a required parameter whose type
|
|
43428
|
+
'Whether the parameter may be OMITTED by a caller. It is not nullability: a parameter that must be passed but may be passed as nothing is a required parameter whose type says so \u2014 "ProjectConfig?". Say whichever is true; they are different contracts (both together: may be left out, and may be none).'
|
|
41795
43429
|
)
|
|
41796
43430
|
}).strict();
|
|
41797
43431
|
const interfaceMethodShape = {
|
|
@@ -41806,7 +43440,7 @@ ${renderChangeReport(report2)}`,
|
|
|
41806
43440
|
),
|
|
41807
43441
|
params: import_zod11.z.array(methodParamItem).optional().describe("Structured parameters \u2014 authoritative for type checking, and the signature text is derived from them. Strongly preferred."),
|
|
41808
43442
|
guarantees: import_zod11.z.array(import_zod11.z.string().min(1)).optional().describe("Semantic guarantees the method promises (combinable); any guarantee a narrative step asserts must be declared here. Builtin tokens: idempotent | atomic | transactional | exactly-once; extension packs may declare more (any other token is UNKNOWN_GUARANTEE)"),
|
|
41809
|
-
effect: import_zod11.z.enum(["read", "write"]).optional().describe("
|
|
43443
|
+
effect: import_zod11.z.enum(["read", "write", "lifecycle"]).optional().describe("What the method does to the component's held state: read observes it; write modifies an entity's domain fields; lifecycle creates, destroys, or (un)registers an entity's existence or membership without modifying its fields, and calls only read and lifecycle methods (LIFECYCLE_CALLS_WRITE). Required on a durable Store's contract methods so the durability round-trip rule can pair mutations with hydration read-backs; a Supervisor may call a data component it does not own only through read and lifecycle methods (SUPERVISOR_WRITE_SHORTCUT)"),
|
|
41810
43444
|
invokedBy: import_zod11.z.object({
|
|
41811
43445
|
kind: import_zod11.z.enum(["runtime", "external", "sibling-subsystem"]).describe("Who owns the out-of-graph invocation: runtime (timer/signal/shutdown hook), external (a system outside this project), sibling-subsystem (a modeled sibling whose edge is not narrated here)"),
|
|
41812
43446
|
caller: import_zod11.z.string().optional().describe("WHO invokes it and when, as reviewable prose \u2014 missing or placeholder-thin prose is INVOKED_BY_UNDESCRIBED")
|
|
@@ -42040,7 +43674,7 @@ ${renderChangeReport(report2)}`,
|
|
|
42040
43674
|
}
|
|
42041
43675
|
);
|
|
42042
43676
|
const typeInput = {
|
|
42043
|
-
kind: import_zod11.z.enum(["entity", "value-object", "signature"]).describe("entity (owned by a subsystem), value-object (often system-level shared),
|
|
43677
|
+
kind: import_zod11.z.enum(["entity", "value-object", "signature", "enum"]).describe("entity (owned by a subsystem), value-object (often system-level shared), signature \u2014 a named function type: params and returns, nothing else \u2014 or enum: a closed, ordered set of named values"),
|
|
42044
43678
|
id: import_zod11.z.string().describe("Lowercase identifier"),
|
|
42045
43679
|
name: import_zod11.z.string().describe("Human-readable name"),
|
|
42046
43680
|
description: import_zod11.z.string().optional(),
|
|
@@ -42074,18 +43708,23 @@ ${renderChangeReport(report2)}`,
|
|
|
42074
43708
|
sourcePath: import_zod11.z.string().optional().describe("Source file holding the declaration of this type (project-relative). Naming one turns the type into a claim on code: the file must resolve and the declaration must be anchored in it (UNREALIZED_TYPE)"),
|
|
42075
43709
|
symbol: import_zod11.z.string().optional().describe('Code-level name realizing the declaration when it differs from name, e.g. a type named "Invoice Line" declared as InvoiceLine'),
|
|
42076
43710
|
params: import_zod11.z.array(methodParamItem).optional().describe("kind signature only: the function type's parameters, in declared order (SIGNATURE_TYPE_MEMBERS on any other kind)"),
|
|
42077
|
-
returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output"))
|
|
43711
|
+
returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output")),
|
|
43712
|
+
values: import_zod11.z.array(import_zod11.z.object({
|
|
43713
|
+
name: import_zod11.z.string().min(1).describe(`The value's name, exactly as data carries it ("stable", "local_only")`),
|
|
43714
|
+
description: import_zod11.z.string().optional().describe("What the value means")
|
|
43715
|
+
}).strict()).optional().describe("kind enum only, and required there: the values in declared order \u2014 the order is part of the design (a narrowest-first track list means something). Unique by name ignoring case and separators; no ordinals and no separate wire value"),
|
|
43716
|
+
holds: import_zod11.z.string().optional().describe('kind value-object only: makes it a NAMED SCALAR \u2014 one primitive under a name (string, int, float, bool, bytes, date, datetime or duration), a newtype or type alias in every language (`type PackPath = string`), declared in place of fields. Not a union, not a named type, not any; fields beside it, or holds on another kind, is NAMED_SCALAR_MEMBERS. Aliases are respelled (boolean \u2192 bool) and number is refused ("int or float?")')
|
|
42078
43717
|
};
|
|
42079
43718
|
const typeInputFields = Object.keys(typeInput);
|
|
42080
43719
|
reg(
|
|
42081
43720
|
server,
|
|
42082
43721
|
"sdd_add_type",
|
|
42083
43722
|
{
|
|
42084
|
-
description: "Define a type: an entity or value-object (the data components operate on),
|
|
43723
|
+
description: "Define a type: an entity or value-object (the data components operate on), a signature \u2014 a named function type \u2014 or an enum: a closed, ordered set of named `values` ({name, description?}, unique by name ignoring case and separators; the name is also the value as data carries it), with optional pure methods and nothing else (no fields, params or returns). A value-object may instead be a NAMED SCALAR: it `holds` one primitive in place of fields (a newtype or type alias in every language). Every type position (a field's type, a method's params and returns, a signature's params and returns, a named scalar's holds) is written in the neutral type grammar; aliases are respelled and listed in the receipt. Entities are owned by a subsystem; shared value objects and signatures omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument \u2014 and a type method may carry structured params, which then derive its signature text as a contract method's do. A signature carries top-level params and returns and nothing else (no fields, methods or invariants \u2014 SIGNATURE_TYPE_MEMBERS); a contract method takes it by naming it as its signatureFrom, and a param may be typed by one (a callback). A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. An owning subsystem the tree does not have is refused, writing nothing, exactly as a component under an unknown subsystem is. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
|
|
42085
43724
|
inputSchema: typeInput,
|
|
42086
43725
|
outputSchema: specWriteReceiptOutput
|
|
42087
43726
|
},
|
|
42088
|
-
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns }) => {
|
|
43727
|
+
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns, values, holds }) => {
|
|
42089
43728
|
try {
|
|
42090
43729
|
const spec = {
|
|
42091
43730
|
kind,
|
|
@@ -42111,7 +43750,9 @@ ${renderChangeReport(report2)}`,
|
|
|
42111
43750
|
...sourcePath ? { sourcePath } : {},
|
|
42112
43751
|
...symbol ? { symbol } : {},
|
|
42113
43752
|
...params ? { params } : {},
|
|
42114
|
-
...returns ? { returns } : {}
|
|
43753
|
+
...returns ? { returns } : {},
|
|
43754
|
+
...values ? { values } : {},
|
|
43755
|
+
...holds ? { holds } : {}
|
|
42115
43756
|
};
|
|
42116
43757
|
const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
|
|
42117
43758
|
return writeReceipt(
|
|
@@ -42144,7 +43785,8 @@ ${renderChangeReport(report2)}`,
|
|
|
42144
43785
|
rules: config.rules,
|
|
42145
43786
|
projectType: config.projectType,
|
|
42146
43787
|
scopeSubsystem: subsystem,
|
|
42147
|
-
|
|
43788
|
+
// The tool's flag at the edge: recursive false is a member depth of 0.
|
|
43789
|
+
...recursive === false ? { memberDepth: 0 } : {},
|
|
42148
43790
|
family
|
|
42149
43791
|
};
|
|
42150
43792
|
const result = selectsFamily(options2, declaredMembers(config).length > 0) ? validateFamily(options2) : validateProject(options2);
|
|
@@ -42279,7 +43921,7 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
42279
43921
|
server,
|
|
42280
43922
|
"sdd_update_spec",
|
|
42281
43923
|
{
|
|
42282
|
-
description: "Update/patch an existing SDD specification (subsystem, component, interface, implementation, or type) using a granular delta. Updates fields, appends/merges array elements, or inserts/deletes narrative steps. Answers with exactly what changed, and with every path the delta named that the write did not act on. Pass dryRun to be told what it would do without writing it.",
|
|
43924
|
+
description: "Update/patch an existing SDD specification (subsystem, component, interface, implementation, or type) using a granular delta. Updates fields, appends/merges array elements, or inserts/deletes narrative steps. Answers with exactly what changed, and with every path the delta named that the write did not act on. Type positions (the type of a param, a returns, the type of a field, the params and returns of a signature) are written in the neutral type grammar: an alias the delta writes (string[], T | null, boolean, Promise<T>) is stored canonical and listed under `respellings`, and a stored alias at a position the write keeps is respelled with it; a position the delta writes that has no canonical spelling (number \u2014 int or float?, an inline object or function type, a literal union, a union mixing in a primitive) is refused, naming the replacement, while one the stored spec already held is left alone. Pass dryRun to be told what it would do without writing it.",
|
|
42283
43925
|
inputSchema: {
|
|
42284
43926
|
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The spec kind to update (system = the singleton L0 \u2014 vision, boundaries, globalRequirements, databases, and publicInterfaces: the project export table, each entry a re-export {from, component, interface, typeDef, as, name, type, details, audience: project|department|instance|partner|external} \u2014 from alone re-exports everything a subsystem exports, and a legacy {id, subsystem} reads as {as, from})"),
|
|
42285
43927
|
id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
|
|
@@ -42308,18 +43950,19 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
42308
43950
|
}
|
|
42309
43951
|
},
|
|
42310
43952
|
({ subsystem, recursive }) => {
|
|
43953
|
+
const memberDepth = recursive === false ? 0 : void 0;
|
|
42311
43954
|
const banner = pendingBanner().map((line) => `\u26A0 TRANSACTION PENDING: ${line}
|
|
42312
43955
|
`).join("");
|
|
42313
43956
|
try {
|
|
42314
43957
|
let approvals;
|
|
42315
43958
|
try {
|
|
42316
|
-
approvals = familyApprovals(
|
|
43959
|
+
approvals = familyApprovals(memberDepth);
|
|
42317
43960
|
} catch {
|
|
42318
43961
|
approvals = void 0;
|
|
42319
43962
|
}
|
|
42320
43963
|
const report2 = getStatusReport({
|
|
42321
43964
|
subsystem,
|
|
42322
|
-
|
|
43965
|
+
memberDepth,
|
|
42323
43966
|
approvals
|
|
42324
43967
|
});
|
|
42325
43968
|
const family = statusFamilyContext();
|
|
@@ -42414,7 +44057,6 @@ function loadProjectConfig3() {
|
|
|
42414
44057
|
AgentTemplateSchema,
|
|
42415
44058
|
BACKEND_COMMANDS,
|
|
42416
44059
|
BUILTIN_PROFILES,
|
|
42417
|
-
BUILTIN_TYPES,
|
|
42418
44060
|
BoundaryItemSchema,
|
|
42419
44061
|
BuiltinTargetConfigSchema,
|
|
42420
44062
|
BuiltinTargetSchema,
|
|
@@ -42448,6 +44090,7 @@ function loadProjectConfig3() {
|
|
|
42448
44090
|
DurabilitySchema,
|
|
42449
44091
|
EXTERNAL_ALIAS_RE,
|
|
42450
44092
|
EndpointSchema,
|
|
44093
|
+
EnumValueSchema,
|
|
42451
44094
|
EventBindingSchema,
|
|
42452
44095
|
ExtDataSchema,
|
|
42453
44096
|
ExternalDeclarationSchema,
|
|
@@ -42462,6 +44105,7 @@ function loadProjectConfig3() {
|
|
|
42462
44105
|
GeminiExporter,
|
|
42463
44106
|
GroupSpecSchema,
|
|
42464
44107
|
GuaranteeSchema,
|
|
44108
|
+
HOLDABLE_PRIMITIVES,
|
|
42465
44109
|
HttpMethodSchema,
|
|
42466
44110
|
ImplementationSpecSchema,
|
|
42467
44111
|
InterfaceSpecSchema,
|
|
@@ -42481,6 +44125,7 @@ function loadProjectConfig3() {
|
|
|
42481
44125
|
NarrativeStepTypeSchema,
|
|
42482
44126
|
OutputTargetSchema,
|
|
42483
44127
|
PATTERN_TYPES,
|
|
44128
|
+
PRIMITIVE_TYPES,
|
|
42484
44129
|
PROJECT_ID_RE,
|
|
42485
44130
|
PROJECT_KINDS,
|
|
42486
44131
|
PUBLIC_NAME_RE,
|
|
@@ -42568,6 +44213,7 @@ function loadProjectConfig3() {
|
|
|
42568
44213
|
buildRuleContext,
|
|
42569
44214
|
callSitesOf,
|
|
42570
44215
|
canonicalTypeRef,
|
|
44216
|
+
canonicalTypeText,
|
|
42571
44217
|
captureApprovedSpecs,
|
|
42572
44218
|
checkDeclarativePack,
|
|
42573
44219
|
clearLoaderIssues,
|
|
@@ -42612,6 +44258,7 @@ function loadProjectConfig3() {
|
|
|
42612
44258
|
detectDomainCandidates,
|
|
42613
44259
|
diagnoseProjectPacks,
|
|
42614
44260
|
diagramSetIndex,
|
|
44261
|
+
dialectOf,
|
|
42615
44262
|
diffAgainstApproval,
|
|
42616
44263
|
diffSize,
|
|
42617
44264
|
discoverPacks,
|
|
@@ -42619,6 +44266,7 @@ function loadProjectConfig3() {
|
|
|
42619
44266
|
effectiveDetail,
|
|
42620
44267
|
effectiveProjectId,
|
|
42621
44268
|
emptyExtensions,
|
|
44269
|
+
emptyTypeSpellingFacts,
|
|
42622
44270
|
ensureDir,
|
|
42623
44271
|
ensureProjectInitialized,
|
|
42624
44272
|
excerptParent,
|
|
@@ -42665,6 +44313,7 @@ function loadProjectConfig3() {
|
|
|
42665
44313
|
importSpecTree,
|
|
42666
44314
|
injectGuide,
|
|
42667
44315
|
installPackFromDirectory,
|
|
44316
|
+
interfaceCanonicalTypes,
|
|
42668
44317
|
interfaceGenericParameters,
|
|
42669
44318
|
internalizeMember,
|
|
42670
44319
|
isDraftSubsystem,
|
|
@@ -42676,9 +44325,11 @@ function loadProjectConfig3() {
|
|
|
42676
44325
|
isProjectInitialized,
|
|
42677
44326
|
isProvidedBy,
|
|
42678
44327
|
isRetired,
|
|
44328
|
+
isTypeVocabulary,
|
|
42679
44329
|
judgesCode,
|
|
42680
44330
|
keyIn,
|
|
42681
44331
|
landReference,
|
|
44332
|
+
languageOfSourcePath,
|
|
42682
44333
|
listDirectChainedSubprojects,
|
|
42683
44334
|
listFiles,
|
|
42684
44335
|
listFilesRecursive,
|
|
@@ -42721,6 +44372,7 @@ function loadProjectConfig3() {
|
|
|
42721
44372
|
moveMethods,
|
|
42722
44373
|
moveMountToMembers,
|
|
42723
44374
|
nameKey,
|
|
44375
|
+
namesAnInteger,
|
|
42724
44376
|
narrativeStepForeignFields,
|
|
42725
44377
|
normalizeReferences,
|
|
42726
44378
|
ownerOf,
|
|
@@ -42732,6 +44384,8 @@ function loadProjectConfig3() {
|
|
|
42732
44384
|
parseDeclarativePack,
|
|
42733
44385
|
parseDeclaredCall,
|
|
42734
44386
|
parseMemberSource,
|
|
44387
|
+
parseTypeExpression,
|
|
44388
|
+
parseTypePosition,
|
|
42735
44389
|
parseYaml,
|
|
42736
44390
|
passesIntentFloor,
|
|
42737
44391
|
pathExists,
|
|
@@ -42745,6 +44399,7 @@ function loadProjectConfig3() {
|
|
|
42745
44399
|
provisionProject,
|
|
42746
44400
|
qualifiedTypeId,
|
|
42747
44401
|
rangeProblem,
|
|
44402
|
+
readCodeAnnotation,
|
|
42748
44403
|
readFileOrNull,
|
|
42749
44404
|
readJsonFile,
|
|
42750
44405
|
readLockRecord,
|
|
@@ -42767,6 +44422,7 @@ function loadProjectConfig3() {
|
|
|
42767
44422
|
renderDiagram,
|
|
42768
44423
|
repairForeignStepFields,
|
|
42769
44424
|
repairSignatures,
|
|
44425
|
+
repairTypeSpellings,
|
|
42770
44426
|
repointExternal,
|
|
42771
44427
|
requiredPolicies,
|
|
42772
44428
|
resolveAgentTopology,
|
|
@@ -42814,9 +44470,15 @@ function loadProjectConfig3() {
|
|
|
42814
44470
|
technologyTokens,
|
|
42815
44471
|
toMarkdown,
|
|
42816
44472
|
typeBindingOf,
|
|
44473
|
+
typeCanonicalTypes,
|
|
44474
|
+
typeDialectFor,
|
|
42817
44475
|
typeGenericParameters,
|
|
44476
|
+
typeIsMany,
|
|
42818
44477
|
typeMatchesRef,
|
|
44478
|
+
typeNamedRefs,
|
|
44479
|
+
typeProblemIntProposal,
|
|
42819
44480
|
typeSourceFiles,
|
|
44481
|
+
typeSpellingFacts,
|
|
42820
44482
|
uninstallPack,
|
|
42821
44483
|
updateMember,
|
|
42822
44484
|
updateSpec,
|
|
@@ -42831,6 +44493,7 @@ function loadProjectConfig3() {
|
|
|
42831
44493
|
writeReachDenials,
|
|
42832
44494
|
writeReachPermits,
|
|
42833
44495
|
writeRootGuideDelegator,
|
|
42834
|
-
writeYamlFile
|
|
44496
|
+
writeYamlFile,
|
|
44497
|
+
writtenTypeRefs
|
|
42835
44498
|
});
|
|
42836
44499
|
//# sourceMappingURL=index.js.map
|