@wairon/cli 5.1.1-dev.94 → 5.1.1-dev.95
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 +1132 -129
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +975 -120
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3484,6 +3484,10 @@ __export(src_exports, {
|
|
|
3484
3484
|
RETIRED_STEREOTYPES: () => RETIRED_STEREOTYPES,
|
|
3485
3485
|
RegistrySchema: () => RegistrySchema,
|
|
3486
3486
|
RequirementItemSchema: () => RequirementItemSchema,
|
|
3487
|
+
ResolvedInterfaceSpecSchema: () => ResolvedInterfaceSpecSchema,
|
|
3488
|
+
ResolvedMethodSignatureSchema: () => ResolvedMethodSignatureSchema,
|
|
3489
|
+
ResolvedTypeMethodSchema: () => ResolvedTypeMethodSchema,
|
|
3490
|
+
ResolvedTypeSpecSchema: () => ResolvedTypeSpecSchema,
|
|
3487
3491
|
RulesConfigSchema: () => RulesConfigSchema,
|
|
3488
3492
|
SCAN_EXCLUDE_DIRS: () => SCAN_EXCLUDE_DIRS,
|
|
3489
3493
|
SDD_RULES: () => SDD_RULES,
|
|
@@ -3495,6 +3499,8 @@ __export(src_exports, {
|
|
|
3495
3499
|
SURFACE_AUDIENCES: () => SURFACE_AUDIENCES,
|
|
3496
3500
|
SpecIdSchema: () => SpecIdSchema,
|
|
3497
3501
|
SpecStatusSchema: () => SpecStatusSchema,
|
|
3502
|
+
StoredMethodSignatureSchema: () => StoredMethodSignatureSchema,
|
|
3503
|
+
StoredTypeMethodSchema: () => StoredTypeMethodSchema,
|
|
3498
3504
|
SubsystemSpecSchema: () => SubsystemSpecSchema,
|
|
3499
3505
|
SurfaceAudienceSchema: () => SurfaceAudienceSchema,
|
|
3500
3506
|
SurfaceContractEntrySchema: () => SurfaceContractEntrySchema,
|
|
@@ -3579,6 +3585,8 @@ __export(src_exports, {
|
|
|
3579
3585
|
dependencyCycles: () => dependencyCycles,
|
|
3580
3586
|
deregisterPackRef: () => deregisterPackRef,
|
|
3581
3587
|
deriveExecutionProfile: () => deriveExecutionProfile,
|
|
3588
|
+
deriveMethodSignature: () => deriveMethodSignature,
|
|
3589
|
+
deriveTypeSignature: () => deriveTypeSignature,
|
|
3582
3590
|
derivedDocPaths: () => derivedDocPaths,
|
|
3583
3591
|
describeProject: () => describeProject,
|
|
3584
3592
|
detectDomainCandidates: () => detectDomainCandidates,
|
|
@@ -3738,6 +3746,7 @@ __export(src_exports, {
|
|
|
3738
3746
|
renameMethod: () => renameMethod,
|
|
3739
3747
|
renderDiagram: () => renderDiagram,
|
|
3740
3748
|
repairForeignStepFields: () => repairForeignStepFields,
|
|
3749
|
+
repairSignatures: () => repairSignatures,
|
|
3741
3750
|
repointExternal: () => repointExternal,
|
|
3742
3751
|
requiredPolicies: () => requiredPolicies,
|
|
3743
3752
|
resolveAgentTopology: () => resolveAgentTopology,
|
|
@@ -3749,6 +3758,7 @@ __export(src_exports, {
|
|
|
3749
3758
|
resolveImport: () => resolveImport,
|
|
3750
3759
|
resolveInstalledPack: () => resolveInstalledPack,
|
|
3751
3760
|
resolveProjectExports: () => resolveProjectExports,
|
|
3761
|
+
resolveSignatures: () => resolveTree,
|
|
3752
3762
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
3753
3763
|
resolveSubsystemExports: () => resolveSubsystemExports,
|
|
3754
3764
|
resolveVariantGuidance: () => resolveVariantGuidance,
|
|
@@ -3770,11 +3780,15 @@ __export(src_exports, {
|
|
|
3770
3780
|
setProjectRoot: () => setProjectRoot,
|
|
3771
3781
|
setProjectType: () => setProjectType,
|
|
3772
3782
|
settledSpecPaths: () => settledSpecPaths,
|
|
3783
|
+
signatureFacts: () => signatureFacts,
|
|
3784
|
+
signatureTypeRefs: () => signatureTypeRefs,
|
|
3773
3785
|
specPathsInScope: () => specPathsInScope,
|
|
3774
3786
|
splitNamespace: () => splitNamespace,
|
|
3775
3787
|
stepConfigVerdict: () => stepConfigVerdict,
|
|
3776
3788
|
stepFieldsFor: () => stepFieldsFor,
|
|
3777
3789
|
stepGraph: () => stepGraph,
|
|
3790
|
+
storedMethodSignature: () => storedMethodSignature,
|
|
3791
|
+
storedTypeMethod: () => storedTypeMethod,
|
|
3778
3792
|
syncContextFiles: () => syncContextFiles,
|
|
3779
3793
|
technologyName: () => technologyName,
|
|
3780
3794
|
technologyTokens: () => technologyTokens,
|
|
@@ -7094,13 +7108,21 @@ var FindingDeclarationSchema = import_zod8.z.object({
|
|
|
7094
7108
|
var MethodSignatureSchema = import_zod8.z.object({
|
|
7095
7109
|
name: import_zod8.z.string().regex(/^[a-zA-Z0-9_]+$/, "Method name must be alphanumeric"),
|
|
7096
7110
|
description: import_zod8.z.string(),
|
|
7097
|
-
signature: import_zod8.z.string(),
|
|
7111
|
+
signature: import_zod8.z.string().optional(),
|
|
7098
7112
|
// e.g. "save(key: string, data: Buffer): Promise<void>"
|
|
7099
7113
|
// e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
|
|
7100
7114
|
// any real tree. See the grammar on src/models/type-references.ts.
|
|
7101
|
-
returns: import_zod8.z.string(),
|
|
7115
|
+
returns: import_zod8.z.string().optional(),
|
|
7102
7116
|
/** Structured parameters (authoritative for type checking when present). */
|
|
7103
7117
|
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
7118
|
+
/**
|
|
7119
|
+
* Where this method takes its params and returns from instead of stating
|
|
7120
|
+
* them: a signature type (`billing.change_listener`, `alias::name`), or a
|
|
7121
|
+
* contract method `component.method` its component reaches along a
|
|
7122
|
+
* dependsOn/owns edge. Resolved both ways by the loader (signature_resolver);
|
|
7123
|
+
* the writer stores a sourced method with only its source.
|
|
7124
|
+
*/
|
|
7125
|
+
signatureFrom: import_zod8.z.string().min(1).optional(),
|
|
7104
7126
|
/** Concrete wire binding for this method when its component is a Portal (set via sdd_set_endpoints). */
|
|
7105
7127
|
endpoint: EndpointSchema.optional(),
|
|
7106
7128
|
/**
|
|
@@ -7153,6 +7175,54 @@ var MethodSignatureSchema = import_zod8.z.object({
|
|
|
7153
7175
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
7154
7176
|
ext: ExtDataSchema.optional()
|
|
7155
7177
|
});
|
|
7178
|
+
function requireStoredSignature(method, ctx) {
|
|
7179
|
+
if (method.signatureFrom !== void 0) return;
|
|
7180
|
+
if (method.returns === void 0) {
|
|
7181
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["returns"], message: "Required: a method without a signatureFrom states its returns" });
|
|
7182
|
+
}
|
|
7183
|
+
if (method.params === void 0 && method.signature === void 0) {
|
|
7184
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["signature"], message: "Required: a method without params or a signatureFrom states a prose signature" });
|
|
7185
|
+
}
|
|
7186
|
+
}
|
|
7187
|
+
var StoredMethodSignatureSchema = MethodSignatureSchema.superRefine(requireStoredSignature);
|
|
7188
|
+
var ResolvedMethodSignatureSchema = MethodSignatureSchema.extend({
|
|
7189
|
+
signature: import_zod8.z.string(),
|
|
7190
|
+
returns: import_zod8.z.string()
|
|
7191
|
+
});
|
|
7192
|
+
function paramText(p) {
|
|
7193
|
+
return `${p.name}${p.optional ? "?" : ""}: ${p.type}`;
|
|
7194
|
+
}
|
|
7195
|
+
function leadingGenericList(name, stored) {
|
|
7196
|
+
if (!stored) return "";
|
|
7197
|
+
const text3 = stored.trimStart();
|
|
7198
|
+
if (!text3.startsWith(name)) return "";
|
|
7199
|
+
let i = name.length;
|
|
7200
|
+
while (text3[i] === " ") i++;
|
|
7201
|
+
if (text3[i] !== "<") return "";
|
|
7202
|
+
let depth = 0;
|
|
7203
|
+
for (let j = i; j < text3.length; j++) {
|
|
7204
|
+
if (text3[j] === "<") depth++;
|
|
7205
|
+
else if (text3[j] === ">") depth--;
|
|
7206
|
+
if (depth === 0) {
|
|
7207
|
+
const rest = text3.slice(j + 1).trimStart();
|
|
7208
|
+
return rest.startsWith("(") ? text3.slice(i, j + 1) : "";
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
return "";
|
|
7212
|
+
}
|
|
7213
|
+
function deriveMethodSignature(method) {
|
|
7214
|
+
if (!method.params) return void 0;
|
|
7215
|
+
const generics = leadingGenericList(method.name, method.signature);
|
|
7216
|
+
return `${method.name}${generics}(${method.params.map(paramText).join(", ")}): ${method.returns ?? "unknown"}`;
|
|
7217
|
+
}
|
|
7218
|
+
function storedMethodSignature(method) {
|
|
7219
|
+
if (method.signatureFrom !== void 0) {
|
|
7220
|
+
const { params: _params, returns: _returns, signature: _signature, ...rest } = method;
|
|
7221
|
+
return rest;
|
|
7222
|
+
}
|
|
7223
|
+
const derived = deriveMethodSignature(method);
|
|
7224
|
+
return derived === void 0 ? method : { ...method, signature: derived };
|
|
7225
|
+
}
|
|
7156
7226
|
var INTENT_FLOOR_MIN_CHARS = 40;
|
|
7157
7227
|
function passesIntentFloor(text3, methodName) {
|
|
7158
7228
|
if (!text3) return false;
|
|
@@ -7168,7 +7238,7 @@ var InterfaceSpecSchema = import_zod8.z.object({
|
|
|
7168
7238
|
description: import_zod8.z.string(),
|
|
7169
7239
|
component: import_zod8.z.string(),
|
|
7170
7240
|
// References L2 Component id
|
|
7171
|
-
methods: import_zod8.z.array(
|
|
7241
|
+
methods: import_zod8.z.array(StoredMethodSignatureSchema).default([]),
|
|
7172
7242
|
/** Per-spec lint suppressions (see LintConfigSchema). */
|
|
7173
7243
|
lint: LintConfigSchema.optional(),
|
|
7174
7244
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
@@ -7177,6 +7247,9 @@ var InterfaceSpecSchema = import_zod8.z.object({
|
|
|
7177
7247
|
createdAt: import_zod8.z.string().datetime(),
|
|
7178
7248
|
updatedAt: import_zod8.z.string().datetime()
|
|
7179
7249
|
});
|
|
7250
|
+
var ResolvedInterfaceSpecSchema = InterfaceSpecSchema.extend({
|
|
7251
|
+
methods: import_zod8.z.array(ResolvedMethodSignatureSchema).default([])
|
|
7252
|
+
});
|
|
7180
7253
|
var NarrativeStepTypeSchema = import_zod8.z.enum([
|
|
7181
7254
|
"local",
|
|
7182
7255
|
// in-component work
|
|
@@ -7514,7 +7587,7 @@ function implementationSourceFiles(impl) {
|
|
|
7514
7587
|
for (const method of impl.methods ?? []) add2(method.sourcePath);
|
|
7515
7588
|
return files;
|
|
7516
7589
|
}
|
|
7517
|
-
var TypeKindSchema = import_zod8.z.enum(["entity", "value-object"]);
|
|
7590
|
+
var TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature"]);
|
|
7518
7591
|
var TypeFieldSchema = import_zod8.z.object({
|
|
7519
7592
|
name: import_zod8.z.string(),
|
|
7520
7593
|
// A primitive, or another type id (qualified across subsystems, e.g.
|
|
@@ -7538,7 +7611,9 @@ var TypeFieldSchema = import_zod8.z.object({
|
|
|
7538
7611
|
});
|
|
7539
7612
|
var TypeMethodSchema = import_zod8.z.object({
|
|
7540
7613
|
name: import_zod8.z.string(),
|
|
7541
|
-
signature: import_zod8.z.string(),
|
|
7614
|
+
signature: import_zod8.z.string().optional(),
|
|
7615
|
+
/** Structured parameters in the contract method's param shape — authoritative for type checking when present. */
|
|
7616
|
+
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
7542
7617
|
returns: import_zod8.z.string(),
|
|
7543
7618
|
description: import_zod8.z.string().optional(),
|
|
7544
7619
|
/**
|
|
@@ -7557,6 +7632,14 @@ var TypeMethodSchema = import_zod8.z.object({
|
|
|
7557
7632
|
*/
|
|
7558
7633
|
symbol: import_zod8.z.string().optional()
|
|
7559
7634
|
});
|
|
7635
|
+
var ResolvedTypeMethodSchema = TypeMethodSchema.extend({
|
|
7636
|
+
signature: import_zod8.z.string()
|
|
7637
|
+
});
|
|
7638
|
+
var StoredTypeMethodSchema = TypeMethodSchema.superRefine((method, ctx) => {
|
|
7639
|
+
if (method.params === void 0 && method.signature === void 0) {
|
|
7640
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["signature"], message: "Required: a type method without params states a prose signature" });
|
|
7641
|
+
}
|
|
7642
|
+
});
|
|
7560
7643
|
var InvariantSchema = import_zod8.z.object({
|
|
7561
7644
|
/** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
|
|
7562
7645
|
id: SpecIdSchema,
|
|
@@ -7565,7 +7648,7 @@ var InvariantSchema = import_zod8.z.object({
|
|
|
7565
7648
|
});
|
|
7566
7649
|
var TypeSpecSchema = import_zod8.z.object({
|
|
7567
7650
|
kind: TypeKindSchema,
|
|
7568
|
-
// discriminator — entity | value-object
|
|
7651
|
+
// discriminator — entity | value-object | signature
|
|
7569
7652
|
id: SpecIdSchema,
|
|
7570
7653
|
name: import_zod8.z.string(),
|
|
7571
7654
|
description: import_zod8.z.string().optional(),
|
|
@@ -7575,7 +7658,7 @@ var TypeSpecSchema = import_zod8.z.object({
|
|
|
7575
7658
|
group: import_zod8.z.string().optional(),
|
|
7576
7659
|
fields: import_zod8.z.array(TypeFieldSchema).default([]),
|
|
7577
7660
|
/** Pure intrinsic behaviour only — anything needing a collaborator belongs on a component. */
|
|
7578
|
-
methods: import_zod8.z.array(
|
|
7661
|
+
methods: import_zod8.z.array(StoredTypeMethodSchema).default([]),
|
|
7579
7662
|
/**
|
|
7580
7663
|
* Linked Component ID if this system entity is implemented as a class Component
|
|
7581
7664
|
* (e.g., a Store or Registry that owns this entity's lifecycle and methods).
|
|
@@ -7621,9 +7704,27 @@ var TypeSpecSchema = import_zod8.z.object({
|
|
|
7621
7704
|
lint: LintConfigSchema.optional(),
|
|
7622
7705
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
7623
7706
|
ext: ExtDataSchema.optional(),
|
|
7707
|
+
/**
|
|
7708
|
+
* A signature's parameters, in the contract method's param shape; only on
|
|
7709
|
+
* kind signature (SIGNATURE_TYPE_MEMBERS otherwise).
|
|
7710
|
+
*/
|
|
7711
|
+
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
7712
|
+
/** A signature's one output type; required on kind signature and only there (SIGNATURE_TYPE_MEMBERS). */
|
|
7713
|
+
returns: import_zod8.z.string().optional(),
|
|
7624
7714
|
createdAt: import_zod8.z.string().datetime(),
|
|
7625
7715
|
updatedAt: import_zod8.z.string().datetime()
|
|
7626
7716
|
});
|
|
7717
|
+
var ResolvedTypeSpecSchema = TypeSpecSchema.extend({
|
|
7718
|
+
methods: import_zod8.z.array(ResolvedTypeMethodSchema).default([])
|
|
7719
|
+
});
|
|
7720
|
+
function deriveTypeSignature(type) {
|
|
7721
|
+
if (type.kind !== "signature") return void 0;
|
|
7722
|
+
return `(${(type.params ?? []).map(paramText).join(", ")}): ${type.returns ?? "unknown"}`;
|
|
7723
|
+
}
|
|
7724
|
+
function storedTypeMethod(method) {
|
|
7725
|
+
const derived = deriveMethodSignature(method);
|
|
7726
|
+
return derived === void 0 ? method : { ...method, signature: derived };
|
|
7727
|
+
}
|
|
7627
7728
|
function typeSourceFiles(type) {
|
|
7628
7729
|
const files = [];
|
|
7629
7730
|
const add2 = (file) => {
|
|
@@ -7643,7 +7744,16 @@ var SurfaceTypeDefSchema = import_zod8.z.object({
|
|
|
7643
7744
|
type: import_zod8.z.string(),
|
|
7644
7745
|
description: import_zod8.z.string().optional(),
|
|
7645
7746
|
optional: import_zod8.z.boolean().optional()
|
|
7646
|
-
})).default([])
|
|
7747
|
+
})).default([]),
|
|
7748
|
+
/** A signature's parameters (name, type, optional, description) in declared order; only on kind signature. */
|
|
7749
|
+
params: import_zod8.z.array(import_zod8.z.object({
|
|
7750
|
+
name: import_zod8.z.string(),
|
|
7751
|
+
type: import_zod8.z.string(),
|
|
7752
|
+
description: import_zod8.z.string().optional(),
|
|
7753
|
+
optional: import_zod8.z.boolean().optional()
|
|
7754
|
+
})).optional(),
|
|
7755
|
+
/** A signature's one output type; only on kind signature. */
|
|
7756
|
+
returns: import_zod8.z.string().optional()
|
|
7647
7757
|
});
|
|
7648
7758
|
var SurfaceTypeExportSchema = import_zod8.z.object({
|
|
7649
7759
|
/** The type's public name in the producer's export table. */
|
|
@@ -7662,8 +7772,8 @@ var SurfaceContractEntrySchema = import_zod8.z.object({
|
|
|
7662
7772
|
type: import_zod8.z.string().default("Custom"),
|
|
7663
7773
|
/** Local name of the backing Portal in the producing project. */
|
|
7664
7774
|
component: import_zod8.z.string(),
|
|
7665
|
-
/** Full contract methods (params, returns, guarantees, effect, endpoint). */
|
|
7666
|
-
methods: import_zod8.z.array(
|
|
7775
|
+
/** Full contract methods (params, returns, guarantees, effect, endpoint), resolved: a snapshot names no producer-internal source. */
|
|
7776
|
+
methods: import_zod8.z.array(ResolvedMethodSignatureSchema).default([]),
|
|
7667
7777
|
/** The backing portal's capability dispatch table, when generic-dispatch. */
|
|
7668
7778
|
dispatch: import_zod8.z.array(DispatchBindingSchema).optional(),
|
|
7669
7779
|
details: import_zod8.z.string().default(""),
|
|
@@ -7913,13 +8023,18 @@ function methodTypeRefs(m) {
|
|
|
7913
8023
|
for (const p of m.params) {
|
|
7914
8024
|
refs.push(...extractTypeIdentifiers(p.type));
|
|
7915
8025
|
}
|
|
7916
|
-
refs.push(...extractTypeIdentifiers(m.returns));
|
|
8026
|
+
refs.push(...extractTypeIdentifiers(m.returns ?? ""));
|
|
7917
8027
|
return Array.from(new Set(refs));
|
|
7918
8028
|
}
|
|
7919
|
-
return extractTypesFromSignature(m.signature, m.returns);
|
|
8029
|
+
return extractTypesFromSignature(m.signature ?? "", m.returns ?? "");
|
|
8030
|
+
}
|
|
8031
|
+
function signatureTypeRefs(type) {
|
|
8032
|
+
if (type.kind !== "signature") return [];
|
|
8033
|
+
const refs = [...(type.params ?? []).flatMap((p) => extractTypeIdentifiers(p.type)), ...extractTypeIdentifiers(type.returns ?? "")];
|
|
8034
|
+
return Array.from(new Set(refs));
|
|
7920
8035
|
}
|
|
7921
8036
|
function methodGenericParameters(method) {
|
|
7922
|
-
return extractGenericTypeVariables(method.signature);
|
|
8037
|
+
return extractGenericTypeVariables(method.signature ?? "");
|
|
7923
8038
|
}
|
|
7924
8039
|
function normalizePart(part) {
|
|
7925
8040
|
return part.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -8306,9 +8421,21 @@ function typeShape(snapshot, def) {
|
|
|
8306
8421
|
return {
|
|
8307
8422
|
id: def.id,
|
|
8308
8423
|
kind: def.kind,
|
|
8309
|
-
fields: sortedBy(def.fields, (f) => f.name).map((f) => ({ name: f.name, type: canonicalTypeRef(snapshot, f.type), optional: f.optional === true }))
|
|
8424
|
+
fields: sortedBy(def.fields, (f) => f.name).map((f) => ({ name: f.name, type: canonicalTypeRef(snapshot, f.type), optional: f.optional === true })),
|
|
8425
|
+
// A signature's shape: its params' types and optionality in order (never their names, as a method's), and its returns.
|
|
8426
|
+
...def.kind === "signature" ? {
|
|
8427
|
+
params: (def.params ?? []).map((p) => ({ type: canonicalTypeRef(snapshot, p.type), optional: p.optional === true })),
|
|
8428
|
+
returns: canonicalTypeRef(snapshot, def.returns ?? "unknown")
|
|
8429
|
+
} : {}
|
|
8310
8430
|
};
|
|
8311
8431
|
}
|
|
8432
|
+
function typeDefExprs(def) {
|
|
8433
|
+
return [
|
|
8434
|
+
...def.fields.map((f) => f.type),
|
|
8435
|
+
...(def.params ?? []).map((p) => p.type),
|
|
8436
|
+
...def.returns !== void 0 ? [def.returns] : []
|
|
8437
|
+
];
|
|
8438
|
+
}
|
|
8312
8439
|
function closureShapes(snapshot, exprs) {
|
|
8313
8440
|
const byId = new Map(snapshot.types.map((def) => [def.id, def]));
|
|
8314
8441
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -8317,7 +8444,7 @@ function closureShapes(snapshot, exprs) {
|
|
|
8317
8444
|
const def = byId.get(queue.shift());
|
|
8318
8445
|
if (!def || seen.has(def.id)) continue;
|
|
8319
8446
|
seen.set(def.id, def);
|
|
8320
|
-
for (const
|
|
8447
|
+
for (const expr of typeDefExprs(def)) queue.push(...extractTypeIdentifiers(canonicalTypeRef(snapshot, expr)));
|
|
8321
8448
|
}
|
|
8322
8449
|
return [...seen.keys()].sort().map((id) => typeShape(snapshot, seen.get(id)));
|
|
8323
8450
|
}
|
|
@@ -9229,6 +9356,7 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
9229
9356
|
signature: m.signature,
|
|
9230
9357
|
returns: m.returns,
|
|
9231
9358
|
...m.params && m.params.length ? { params: m.params } : {},
|
|
9359
|
+
...m.signatureFrom ? { signatureFrom: m.signatureFrom } : {},
|
|
9232
9360
|
...m.endpoint ? { endpoint: m.endpoint } : {},
|
|
9233
9361
|
...m.guarantees && m.guarantees.length ? { guarantees: m.guarantees } : {}
|
|
9234
9362
|
}))
|
|
@@ -9294,6 +9422,7 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
9294
9422
|
...f.references ? { references: f.references } : {}
|
|
9295
9423
|
})),
|
|
9296
9424
|
methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, returns: m.returns, ...m.description ? { description: m.description } : {} })),
|
|
9425
|
+
...t.kind === "signature" ? { signature: deriveTypeSignature(t) } : {},
|
|
9297
9426
|
usedBy: usedByFor(t),
|
|
9298
9427
|
...t.componentClass ? { componentClass: t.componentClass } : {},
|
|
9299
9428
|
...t.database ? { database: t.database } : {},
|
|
@@ -11028,15 +11157,17 @@ var MODEL = __MODEL_JSON__;
|
|
|
11028
11157
|
function tableShape(t) {
|
|
11029
11158
|
var fields = visibleFields(t);
|
|
11030
11159
|
var meths = det === 'full' ? t.methods : [];
|
|
11160
|
+
// A signature type is drawn with its derived text in place of a field list.
|
|
11161
|
+
var sig = t.signature && det !== 'names' ? t.signature : '';
|
|
11031
11162
|
var head = t.name + ' \\u00AB' + t.kind + '\\u00BB';
|
|
11032
|
-
var rows = fields.map(function (f) { return rowText(t, f); })
|
|
11163
|
+
var rows = (sig ? [sig] : []).concat(fields.map(function (f) { return rowText(t, f); }))
|
|
11033
11164
|
.concat(meths.map(function (m) { return '\\u0192 ' + m.name + '(): ' + m.returns; }));
|
|
11034
11165
|
var longest = head.length + 4;
|
|
11035
11166
|
rows.forEach(function (r) { if (r.length > longest) longest = r.length; });
|
|
11036
11167
|
var plain = rows.length === 0;
|
|
11037
11168
|
var pw = Math.max(170, head.length * 6.8 + 26);
|
|
11038
11169
|
var W = Math.max(210, Math.min(400, longest * 6.6 + 30));
|
|
11039
|
-
return { fields: fields, meths: meths, head: head, plain: plain, w: plain ? pw : W, h: plain ? 40 : TH_H + fields.length * ROW_H + meths.length * ROW_H };
|
|
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 };
|
|
11040
11171
|
}
|
|
11041
11172
|
|
|
11042
11173
|
// Emit one type table with its top-left at (ax, ay); returns its size.
|
|
@@ -11060,6 +11191,13 @@ var MODEL = __MODEL_JSON__;
|
|
|
11060
11191
|
position: { x: ax + sh.w / 2, y: ay + TH_H / 2 }, classes: 'typeHead ' + kindCls + (dim ? ' dimmed' : ''), grabbable: false,
|
|
11061
11192
|
});
|
|
11062
11193
|
var ry = ay + TH_H;
|
|
11194
|
+
if (sh.sig) {
|
|
11195
|
+
eles.push({
|
|
11196
|
+
data: { id: 'TS~' + t.id, parent: 'T~' + t.id, label: sh.sig, w: sh.w, h: ROW_H, tw: sh.w - 14 },
|
|
11197
|
+
position: { x: ax + sh.w / 2, y: ry + ROW_H / 2 }, classes: 'typeRow methRow' + (dim ? ' dimmed' : ''), grabbable: false,
|
|
11198
|
+
});
|
|
11199
|
+
ry += ROW_H;
|
|
11200
|
+
}
|
|
11063
11201
|
sh.fields.forEach(function (f) {
|
|
11064
11202
|
var rid = 'TF~' + t.id + '~' + f.name;
|
|
11065
11203
|
rowIds[rid] = 1;
|
|
@@ -13146,7 +13284,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
13146
13284
|
var mi = methodInfo(top.comp, top.method);
|
|
13147
13285
|
var parts = ['<div class="fstep" style="opacity:.7">No step-by-step narrative \\u2014 showing intent / contract:</div>'];
|
|
13148
13286
|
if (mi) {
|
|
13149
|
-
parts.push('<div class="fstep"><code>' + escText(mi.m.signature) + '</code
|
|
13287
|
+
parts.push('<div class="fstep"><code>' + escText(mi.m.signature) + '</code>' + (mi.m.signatureFrom ? ' from <code>' + escText(mi.m.signatureFrom) + '</code>' : '') + '</div>');
|
|
13150
13288
|
parts.push('<div class="fstep">' + escText(mi.m.description) + (mi.m.returns ? ' \\u2014 returns ' + escText(mi.m.returns) : '') + '</div>');
|
|
13151
13289
|
}
|
|
13152
13290
|
if (intent) parts.push('<div class="fstep" style="font-style:italic">' + escText(intent) + '</div>');
|
|
@@ -13487,6 +13625,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
13487
13625
|
: '<span class="chip" style="opacity:.6">no narrative</span>')
|
|
13488
13626
|
+ '</div>'
|
|
13489
13627
|
+ '<code>' + esc(m.signature) + '</code>'
|
|
13628
|
+
+ (m.signatureFrom ? '<div class="mdesc">signature from <code style="display:inline">' + esc(m.signatureFrom) + '</code></div>' : '')
|
|
13490
13629
|
+ '<div class="mdesc">' + esc(m.description) + ' \\u2014 returns ' + typeRefHtml(m.returns) + '</div>'
|
|
13491
13630
|
+ (mIntent && !hasNarr ? '<div class="mdesc" style="font-style:italic">' + esc(mIntent) + '</div>' : '')
|
|
13492
13631
|
+ (m.params ? '<div class="mdesc">params: ' + m.params.map(function (p) { return esc(p.name) + ': ' + typeRefHtml(p.type); }).join(', ') + '</div>' : '')
|
|
@@ -13590,7 +13729,11 @@ var MODEL = __MODEL_JSON__;
|
|
|
13590
13729
|
+ '</div>';
|
|
13591
13730
|
}).join('')
|
|
13592
13731
|
: '<span class="desc">no fields</span>';
|
|
13593
|
-
|
|
13732
|
+
if (ty.signature) {
|
|
13733
|
+
body += section('Signature', 1, '<div class="method"><code>' + esc(ty.signature) + '</code></div>', true);
|
|
13734
|
+
} else {
|
|
13735
|
+
body += section('Fields', ty.fields.length, fieldsInner, true);
|
|
13736
|
+
}
|
|
13594
13737
|
if (ty.usedBy && ty.usedBy.length) {
|
|
13595
13738
|
body += section('Used by methods', ty.usedBy.length, ty.usedBy.map(function (u) {
|
|
13596
13739
|
return chip(u.component + '.' + u.method + '()', 'component', u.component);
|
|
@@ -13929,21 +14072,30 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
13929
14072
|
for (const field of spec.fields) {
|
|
13930
14073
|
for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
|
|
13931
14074
|
}
|
|
14075
|
+
for (const param of spec.params ?? []) {
|
|
14076
|
+
for (const ref of extractTypeIdentifiers(param.type)) enqueueRef(ref);
|
|
14077
|
+
}
|
|
14078
|
+
if (spec.returns) {
|
|
14079
|
+
for (const ref of extractTypeIdentifiers(spec.returns)) enqueueRef(ref);
|
|
14080
|
+
}
|
|
13932
14081
|
}
|
|
13933
14082
|
const usedIds = /* @__PURE__ */ new Set();
|
|
13934
14083
|
return [...included.entries()].map(([qualified, t]) => {
|
|
13935
14084
|
const id = usedIds.has(t.id) ? qualified : t.id;
|
|
13936
14085
|
usedIds.add(id);
|
|
14086
|
+
const describe2 = (v) => ({
|
|
14087
|
+
name: v.name,
|
|
14088
|
+
type: v.type,
|
|
14089
|
+
...v.description ? { description: v.description } : {},
|
|
14090
|
+
...v.optional ? { optional: true } : {}
|
|
14091
|
+
});
|
|
13937
14092
|
return {
|
|
13938
14093
|
id,
|
|
13939
14094
|
name: t.name,
|
|
13940
14095
|
kind: t.kind,
|
|
13941
|
-
fields: t.fields.map(
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
...f.description ? { description: f.description } : {},
|
|
13945
|
-
...f.optional ? { optional: true } : {}
|
|
13946
|
-
}))
|
|
14096
|
+
fields: t.fields.map(describe2),
|
|
14097
|
+
// A signature type travels complete: its params and returns with it.
|
|
14098
|
+
...t.kind === "signature" ? { params: (t.params ?? []).map(describe2), returns: t.returns ?? "unknown" } : {}
|
|
13947
14099
|
};
|
|
13948
14100
|
});
|
|
13949
14101
|
}
|
|
@@ -13956,7 +14108,7 @@ function boundProjectId() {
|
|
|
13956
14108
|
}
|
|
13957
14109
|
}
|
|
13958
14110
|
function contractEntry(entry, comp, interfaces) {
|
|
13959
|
-
const methods = interfaces.filter((i) => i.component === comp.id && (!entry.interface || i.id === entry.interface)).flatMap((i) => i.methods);
|
|
14111
|
+
const methods = interfaces.filter((i) => i.component === comp.id && (!entry.interface || i.id === entry.interface)).flatMap((i) => i.methods).map(({ signatureFrom: _source, ...method }) => method);
|
|
13960
14112
|
return {
|
|
13961
14113
|
id: entry.publicName,
|
|
13962
14114
|
name: entry.name ?? comp.name,
|
|
@@ -14026,7 +14178,12 @@ function canonicalReferences(snapshot) {
|
|
|
14026
14178
|
...m.params ? { params: m.params.map((p) => ({ ...p, type: canon(p.type) })) } : {}
|
|
14027
14179
|
}))
|
|
14028
14180
|
})),
|
|
14029
|
-
types: snapshot.types.map((def) => ({
|
|
14181
|
+
types: snapshot.types.map((def) => ({
|
|
14182
|
+
...def,
|
|
14183
|
+
fields: def.fields.map((f) => ({ ...f, type: canon(f.type) })),
|
|
14184
|
+
...def.params ? { params: def.params.map((p) => ({ ...p, type: canon(p.type) })) } : {},
|
|
14185
|
+
...def.returns !== void 0 ? { returns: canon(def.returns) } : {}
|
|
14186
|
+
}))
|
|
14030
14187
|
};
|
|
14031
14188
|
}
|
|
14032
14189
|
function listSnapshots() {
|
|
@@ -15085,7 +15242,7 @@ function defaultTargetConfig(type) {
|
|
|
15085
15242
|
enabled: true
|
|
15086
15243
|
};
|
|
15087
15244
|
}
|
|
15088
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
15245
|
+
var WAIRON_VERSION = "5.1.1-dev.95";
|
|
15089
15246
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
15090
15247
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
15091
15248
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -16118,7 +16275,7 @@ function buildImplementationMethods(ctx) {
|
|
|
16118
16275
|
const component = ctx.componentMap.get(contract.component);
|
|
16119
16276
|
if (!component) continue;
|
|
16120
16277
|
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
16121
|
-
const
|
|
16278
|
+
const draftContext3 = ctx.isImplementationDraft(impl);
|
|
16122
16279
|
for (const method of impl.methods) {
|
|
16123
16280
|
const sourceFile = methodSourceFile(method, impl.sourcePath);
|
|
16124
16281
|
out.push({
|
|
@@ -16126,7 +16283,7 @@ function buildImplementationMethods(ctx) {
|
|
|
16126
16283
|
method,
|
|
16127
16284
|
component,
|
|
16128
16285
|
...sourceFile !== void 0 ? { sourceFile } : {},
|
|
16129
|
-
draftContext
|
|
16286
|
+
draftContext: draftContext3
|
|
16130
16287
|
});
|
|
16131
16288
|
}
|
|
16132
16289
|
}
|
|
@@ -16431,7 +16588,7 @@ var fieldTypeReferencesRule = {
|
|
|
16431
16588
|
var signatureTypeReferencesRule = {
|
|
16432
16589
|
name: "signature-type-references",
|
|
16433
16590
|
judges: "design",
|
|
16434
|
-
description: "Every type identifier
|
|
16591
|
+
description: "Every type identifier a method signature names must resolve to a builtin, a generic parameter in scope on the interface or the method, or a defined type: an interface method's params and returns, a type method's params when it has them, and a signature type's params and returns. A method that takes its signature from a source is skipped \u2014 its params are its source's, judged once where the source declares them.",
|
|
16435
16592
|
codes: [
|
|
16436
16593
|
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
16437
16594
|
],
|
|
@@ -16442,6 +16599,7 @@ var signatureTypeReferencesRule = {
|
|
|
16442
16599
|
Array.from(interfaceGenericParameters(intf)).map((g) => g.toLowerCase())
|
|
16443
16600
|
);
|
|
16444
16601
|
for (const m of intf.methods) {
|
|
16602
|
+
if (m.signatureFrom !== void 0) continue;
|
|
16445
16603
|
const methodGenerics = new Set(
|
|
16446
16604
|
Array.from(methodGenericParameters(m)).map((g) => g.toLowerCase())
|
|
16447
16605
|
);
|
|
@@ -16461,6 +16619,206 @@ var signatureTypeReferencesRule = {
|
|
|
16461
16619
|
}
|
|
16462
16620
|
}
|
|
16463
16621
|
}
|
|
16622
|
+
for (const t of ctx.types) {
|
|
16623
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
16624
|
+
const isDraftCtx = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
16625
|
+
const typeGenerics = new Set(Array.from(typeGenericParameters(t)).map((g) => g.toLowerCase()));
|
|
16626
|
+
const named2 = [
|
|
16627
|
+
...t.methods.filter((m) => m.params !== void 0).flatMap((m) => methodTypeRefs(m).map((ref) => ({ ref, by: `Method "${m.name}" on type "${t.id}"` }))),
|
|
16628
|
+
...signatureTypeRefs(t).map((ref) => ({ ref, by: `Signature type "${t.id}"` }))
|
|
16629
|
+
];
|
|
16630
|
+
for (const { ref, by } of named2) {
|
|
16631
|
+
if (ctx.isTypeResolved(ref, typeGenerics)) continue;
|
|
16632
|
+
const hint = ctx.importHint(ref);
|
|
16633
|
+
ctx.addIssue(
|
|
16634
|
+
"error",
|
|
16635
|
+
"UNDEFINED_TYPE_REFERENCE",
|
|
16636
|
+
`${by} references undefined type "${ref}" in signature.${hint ? ` A declared dependency exports it without this project importing it \u2014 add \`${hint}\`.` : ""}`,
|
|
16637
|
+
t.id,
|
|
16638
|
+
isDraftCtx
|
|
16639
|
+
);
|
|
16640
|
+
}
|
|
16641
|
+
}
|
|
16642
|
+
}
|
|
16643
|
+
};
|
|
16644
|
+
|
|
16645
|
+
// src/core/rules/integrity/signature-sources.ts
|
|
16646
|
+
function draftContext(ctx, interfaceId, component) {
|
|
16647
|
+
const intf = ctx.interfaceMap.get(interfaceId);
|
|
16648
|
+
return ctx.isComponentDraft(component) || intf?.status === "draft" || intf?.status === "design";
|
|
16649
|
+
}
|
|
16650
|
+
function targetComponent(target) {
|
|
16651
|
+
return target.slice(0, target.lastIndexOf("."));
|
|
16652
|
+
}
|
|
16653
|
+
var signatureSourcesRule = {
|
|
16654
|
+
name: "signature-sources",
|
|
16655
|
+
judges: "design",
|
|
16656
|
+
description: "Judges every signatureFrom the loader met, from ctx.signatureFacts (the loaded specs are already resolved). The value is read both ways, as a contract method and as a signature type: neither resolving \u2014 or only a type that is an entity or value-object \u2014 is unresolved; both resolving is ambiguous, and the finding names both candidates and asks the author to qualify the reference; a source that itself names a signatureFrom is a chain, which is never followed (when that source's own source is a signature type, the finding names it as the one to take directly); a method source whose component the method's component does not name in dependsOn or owns is off the design's edges; a stored method that names a source and also states params or returns that differ from the source's restates it, and the source's are in force. Every finding sits at the method.",
|
|
16657
|
+
codes: [
|
|
16658
|
+
{ code: "SIGNATURE_SOURCE_UNRESOLVED", defaultSeverity: "error", summary: "A method's signatureFrom names no contract method and no signature type" },
|
|
16659
|
+
{ code: "SIGNATURE_SOURCE_AMBIGUOUS", defaultSeverity: "error", summary: "A method's signatureFrom resolves both as a contract method and as a signature type; qualify it" },
|
|
16660
|
+
{ code: "SIGNATURE_SOURCE_CHAINED", defaultSeverity: "error", summary: "A method's signature source takes its own signature from a source; sources do not chain" },
|
|
16661
|
+
{ code: "SIGNATURE_SOURCE_OFF_EDGE", defaultSeverity: "error", summary: "A method takes its signature from a method of a component its own component neither dependsOn nor owns" },
|
|
16662
|
+
{ code: "SIGNATURE_SOURCE_RESTATED", defaultSeverity: "error", summary: "A method names a signature source and also states params or returns that differ from the source's" }
|
|
16663
|
+
],
|
|
16664
|
+
check(ctx) {
|
|
16665
|
+
for (const fact of ctx.signatureFacts?.sources ?? []) {
|
|
16666
|
+
if (!ctx.isSpecInScope(fact.interfaceId)) continue;
|
|
16667
|
+
const isDraft = draftContext(ctx, fact.interfaceId, fact.component);
|
|
16668
|
+
const where = `Method "${fact.method}" on interface "${fact.interfaceId}"`;
|
|
16669
|
+
const at = { at: fact.method };
|
|
16670
|
+
switch (fact.outcome) {
|
|
16671
|
+
case "unresolved":
|
|
16672
|
+
ctx.addIssue(
|
|
16673
|
+
"error",
|
|
16674
|
+
"SIGNATURE_SOURCE_UNRESOLVED",
|
|
16675
|
+
`${where} takes its signature from "${fact.source}", which names ${fact.detail ? `no contract method, and ${fact.detail}` : "no contract method and no signature type"}. A source is a \`component.method\` the component reaches, or a type of kind signature.`,
|
|
16676
|
+
fact.interfaceId,
|
|
16677
|
+
isDraft,
|
|
16678
|
+
void 0,
|
|
16679
|
+
at
|
|
16680
|
+
);
|
|
16681
|
+
continue;
|
|
16682
|
+
case "ambiguous":
|
|
16683
|
+
ctx.addIssue(
|
|
16684
|
+
"error",
|
|
16685
|
+
"SIGNATURE_SOURCE_AMBIGUOUS",
|
|
16686
|
+
`${where} takes its signature from "${fact.source}", which names both the contract method "${fact.candidates?.[0] ?? "?"}" and the signature type "${fact.candidates?.[1] ?? "?"}". Qualify the reference so only one reading matches.`,
|
|
16687
|
+
fact.interfaceId,
|
|
16688
|
+
isDraft,
|
|
16689
|
+
void 0,
|
|
16690
|
+
at
|
|
16691
|
+
);
|
|
16692
|
+
continue;
|
|
16693
|
+
case "chained": {
|
|
16694
|
+
const own = fact.detail ?? "";
|
|
16695
|
+
const direct = ctx.types.some((t) => t.kind === "signature" && (t.id === own || typeMatchesRef(t, own)));
|
|
16696
|
+
ctx.addIssue(
|
|
16697
|
+
"error",
|
|
16698
|
+
"SIGNATURE_SOURCE_CHAINED",
|
|
16699
|
+
`${where} takes its signature from "${fact.target ?? fact.source}", which takes its own from "${own}"; sources do not chain.${direct ? ` "${own}" is a signature type: name it directly as this method's signatureFrom.` : " State this method's params, or name a source that declares its own."}`,
|
|
16700
|
+
fact.interfaceId,
|
|
16701
|
+
isDraft,
|
|
16702
|
+
void 0,
|
|
16703
|
+
at
|
|
16704
|
+
);
|
|
16705
|
+
continue;
|
|
16706
|
+
}
|
|
16707
|
+
case "restated":
|
|
16708
|
+
if (fact.differs) {
|
|
16709
|
+
ctx.addIssue(
|
|
16710
|
+
"error",
|
|
16711
|
+
"SIGNATURE_SOURCE_RESTATED",
|
|
16712
|
+
`${where} takes its signature from "${fact.source}" and also states its own: ${fact.detail ?? "they differ from the source's"}. The source's are in force \u2014 drop the stated params and returns, or drop the signatureFrom.`,
|
|
16713
|
+
fact.interfaceId,
|
|
16714
|
+
isDraft,
|
|
16715
|
+
void 0,
|
|
16716
|
+
at
|
|
16717
|
+
);
|
|
16718
|
+
}
|
|
16719
|
+
continue;
|
|
16720
|
+
default:
|
|
16721
|
+
break;
|
|
16722
|
+
}
|
|
16723
|
+
if (fact.form !== "method" || !fact.target) continue;
|
|
16724
|
+
const source = targetComponent(fact.target);
|
|
16725
|
+
const owner = ctx.componentMap.get(fact.component);
|
|
16726
|
+
const reached = /* @__PURE__ */ new Set([...owner?.dependsOn ?? [], ...owner?.owns ?? []]);
|
|
16727
|
+
if (reached.has(source)) continue;
|
|
16728
|
+
ctx.addIssue(
|
|
16729
|
+
"error",
|
|
16730
|
+
"SIGNATURE_SOURCE_OFF_EDGE",
|
|
16731
|
+
`${where} takes its signature from "${fact.target}", but "${fact.component}" neither dependsOn nor owns "${source}". Add "${source}" to its dependsOn (or owns, for a member), or name a source it reaches.`,
|
|
16732
|
+
fact.interfaceId,
|
|
16733
|
+
isDraft,
|
|
16734
|
+
void 0,
|
|
16735
|
+
at
|
|
16736
|
+
);
|
|
16737
|
+
}
|
|
16738
|
+
}
|
|
16739
|
+
};
|
|
16740
|
+
|
|
16741
|
+
// src/core/rules/integrity/signature-types.ts
|
|
16742
|
+
var signatureTypesRule = {
|
|
16743
|
+
name: "signature-types",
|
|
16744
|
+
judges: "design",
|
|
16745
|
+
description: "A type of kind signature is a named function type and carries params and one returns, nothing else: fields, methods, invariants, componentClass, database, table or linkedEntity on it \u2014 or a returns missing from it \u2014 are reported; so are params or returns on an entity or a value-object, where they mean nothing.",
|
|
16746
|
+
codes: [
|
|
16747
|
+
{ code: "SIGNATURE_TYPE_MEMBERS", defaultSeverity: "error", summary: "A signature type carries a member other than params and returns, or lacks returns; or a data type carries params or returns" }
|
|
16748
|
+
],
|
|
16749
|
+
check(ctx) {
|
|
16750
|
+
for (const t of ctx.types) {
|
|
16751
|
+
if (!ctx.isSpecInScope(t.id)) continue;
|
|
16752
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
16753
|
+
const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
16754
|
+
if (t.kind === "signature") {
|
|
16755
|
+
const members = [
|
|
16756
|
+
...t.fields.length > 0 ? ["fields"] : [],
|
|
16757
|
+
...t.methods.length > 0 ? ["methods"] : [],
|
|
16758
|
+
...(t.invariants?.length ?? 0) > 0 ? ["invariants"] : [],
|
|
16759
|
+
...t.componentClass !== void 0 ? ["componentClass"] : [],
|
|
16760
|
+
...t.database !== void 0 ? ["database"] : [],
|
|
16761
|
+
...t.table !== void 0 ? ["table"] : [],
|
|
16762
|
+
...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
|
|
16763
|
+
];
|
|
16764
|
+
const missingReturns = t.returns === void 0;
|
|
16765
|
+
if (members.length === 0 && !missingReturns) continue;
|
|
16766
|
+
const problems = [
|
|
16767
|
+
...members.length > 0 ? [`carries ${members.join(", ")}`] : [],
|
|
16768
|
+
...missingReturns ? ["states no returns"] : []
|
|
16769
|
+
];
|
|
16770
|
+
ctx.addIssue(
|
|
16771
|
+
"error",
|
|
16772
|
+
"SIGNATURE_TYPE_MEMBERS",
|
|
16773
|
+
`Signature type "${t.id}" ${problems.join(" and ")}. A signature is a named function type: params and one returns, nothing else.`,
|
|
16774
|
+
t.id,
|
|
16775
|
+
isDraft
|
|
16776
|
+
);
|
|
16777
|
+
continue;
|
|
16778
|
+
}
|
|
16779
|
+
const stated = [...t.params !== void 0 ? ["params"] : [], ...t.returns !== void 0 ? ["returns"] : []];
|
|
16780
|
+
if (stated.length === 0) continue;
|
|
16781
|
+
ctx.addIssue(
|
|
16782
|
+
"error",
|
|
16783
|
+
"SIGNATURE_TYPE_MEMBERS",
|
|
16784
|
+
`Type "${t.id}" is ${t.kind === "entity" ? "an entity" : `a ${t.kind}`} and carries ${stated.join(" and ")}, which only a signature type has. Make it kind signature, or drop them.`,
|
|
16785
|
+
t.id,
|
|
16786
|
+
isDraft
|
|
16787
|
+
);
|
|
16788
|
+
}
|
|
16789
|
+
}
|
|
16790
|
+
};
|
|
16791
|
+
|
|
16792
|
+
// src/core/rules/integrity/signature-text.ts
|
|
16793
|
+
function draftContext2(ctx, specId, kind) {
|
|
16794
|
+
if (kind === "interface") {
|
|
16795
|
+
const intf = ctx.interfaceMap.get(specId);
|
|
16796
|
+
return !!intf && (ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design");
|
|
16797
|
+
}
|
|
16798
|
+
const type = ctx.types.find((t) => t.id === specId);
|
|
16799
|
+
const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
|
|
16800
|
+
return !!sub && (sub.status === "draft" || sub.status === "design");
|
|
16801
|
+
}
|
|
16802
|
+
var signatureTextRule = {
|
|
16803
|
+
name: "signature-text",
|
|
16804
|
+
judges: "design",
|
|
16805
|
+
description: "A stored signature text that differs from the text its method's params derive is stale: every reader is shown the derived text, but the file says something else, and a file read on its own (a diff, a review, a lock approval) is misled. Reported per method, from ctx.signatureFacts, for contract and type methods alike; `wairon doctor --fix` regenerates the stored text, and any save of the spec writes the derived text too.",
|
|
16806
|
+
codes: [
|
|
16807
|
+
{ code: "SIGNATURE_TEXT_STALE", defaultSeverity: "warning", summary: "A stored signature text differs from the text its params derive; doctor --fix regenerates it" }
|
|
16808
|
+
],
|
|
16809
|
+
check(ctx) {
|
|
16810
|
+
for (const stale of ctx.signatureFacts?.staleTexts ?? []) {
|
|
16811
|
+
if (!ctx.isSpecInScope(stale.specId)) continue;
|
|
16812
|
+
ctx.addIssue(
|
|
16813
|
+
"warning",
|
|
16814
|
+
"SIGNATURE_TEXT_STALE",
|
|
16815
|
+
`Method "${stale.method}" on ${stale.kind} "${stale.specId}" stores the signature "${stale.stored}", but its params derive "${stale.derived}" \u2014 which is what every reader is shown. Run \`wairon doctor --fix\` to regenerate the stored text (any save of the spec writes it too).`,
|
|
16816
|
+
stale.specId,
|
|
16817
|
+
draftContext2(ctx, stale.specId, stale.kind),
|
|
16818
|
+
void 0,
|
|
16819
|
+
{ at: stale.method }
|
|
16820
|
+
);
|
|
16821
|
+
}
|
|
16464
16822
|
}
|
|
16465
16823
|
};
|
|
16466
16824
|
|
|
@@ -17892,11 +18250,11 @@ var detailSufficiencyRule = {
|
|
|
17892
18250
|
const contract = ctx.interfaceMap.get(impl.contract);
|
|
17893
18251
|
if (!contract) continue;
|
|
17894
18252
|
const component = ctx.componentMap.get(contract.component);
|
|
17895
|
-
const
|
|
18253
|
+
const draftContext3 = ctx.isImplementationDraft(impl);
|
|
17896
18254
|
for (const method of impl.methods) {
|
|
17897
18255
|
const detail = effectiveDetail(method, impl, component);
|
|
17898
18256
|
if (contract.methods.some((m) => m.name === method.name) && method.narrative.length === 0 && detail.level !== "full") {
|
|
17899
|
-
dialedDown.push({ implementation: impl, method, component, detail, draftContext });
|
|
18257
|
+
dialedDown.push({ implementation: impl, method, component, detail, draftContext: draftContext3 });
|
|
17900
18258
|
}
|
|
17901
18259
|
}
|
|
17902
18260
|
}
|
|
@@ -19863,7 +20221,7 @@ var unusedTypesRule = {
|
|
|
19863
20221
|
// Stage 8: its verdict needs the whole system's specs, so a part judged alone skips it.
|
|
19864
20222
|
needsWholeTree: true,
|
|
19865
20223
|
judges: "design",
|
|
19866
|
-
description: "Flags types no field of any type, no interface method signature and no other type's method signature references. References are matched through the type-reference grammar (generic arguments, collections, qualified ids), and the declaring type's own generic parameters are left out \u2014 a type parameter is not a reference to a type. A type named only by its OWN methods stays unused, the way a function that only calls itself is.",
|
|
20224
|
+
description: "Flags types no field of any type, no interface method signature, no contract method's signatureFrom and no other type's method signature references. References are matched through the type-reference grammar (generic arguments, collections, qualified ids), and the declaring type's own generic parameters are left out \u2014 a type parameter is not a reference to a type. A signature type is used when a method names it as its signatureFrom or a param is typed by it. A type named only by its OWN methods stays unused, the way a function that only calls itself is.",
|
|
19867
20225
|
codes: [
|
|
19868
20226
|
{ code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields, signatures or type methods" }
|
|
19869
20227
|
],
|
|
@@ -19885,6 +20243,7 @@ var unusedTypesRule = {
|
|
|
19885
20243
|
markTypeReferenced(ref);
|
|
19886
20244
|
}
|
|
19887
20245
|
}
|
|
20246
|
+
for (const ref of signatureTypeRefs(t)) markTypeReferenced(ref, t.id);
|
|
19888
20247
|
}
|
|
19889
20248
|
for (const intf of ctx.interfaces) {
|
|
19890
20249
|
for (const m of intf.methods) {
|
|
@@ -19894,6 +20253,13 @@ var unusedTypesRule = {
|
|
|
19894
20253
|
}
|
|
19895
20254
|
}
|
|
19896
20255
|
}
|
|
20256
|
+
for (const fact of ctx.signatureFacts?.sources ?? []) {
|
|
20257
|
+
const named2 = fact.form === "signature" && fact.target ? fact.target : fact.outcome === "ambiguous" ? fact.candidates?.[1] : void 0;
|
|
20258
|
+
if (!named2) continue;
|
|
20259
|
+
for (const spec of ctx.types) {
|
|
20260
|
+
if (spec.id === named2 || typeMatchesRef(spec, named2)) referencedTypes.add(spec.id);
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
19897
20263
|
for (const t of ctx.types) {
|
|
19898
20264
|
for (const m of t.methods) {
|
|
19899
20265
|
const refs = methodTypeRefs(m);
|
|
@@ -21253,7 +21619,7 @@ function absence(optional) {
|
|
|
21253
21619
|
var typeShapeRule = {
|
|
21254
21620
|
name: "type-shape",
|
|
21255
21621
|
judges: "code",
|
|
21256
|
-
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. 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.",
|
|
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.",
|
|
21257
21623
|
codes: [
|
|
21258
21624
|
{
|
|
21259
21625
|
code: "UNREALIZED_TYPE_FIELD",
|
|
@@ -21430,7 +21796,7 @@ var paramConformanceRule = {
|
|
|
21430
21796
|
const stated = normalize3(declared.type);
|
|
21431
21797
|
return stated === realized.type || codeNameOf.get(stated) === realized.type;
|
|
21432
21798
|
};
|
|
21433
|
-
for (const { implementation, method, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
21799
|
+
for (const { implementation, method, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
|
|
21434
21800
|
const contract = ctx.interfaceMap.get(implementation.contract);
|
|
21435
21801
|
const declared = contract?.methods.find((m) => m.name === method.name)?.params ?? [];
|
|
21436
21802
|
if (declared.length === 0 || !sourceFile) continue;
|
|
@@ -21453,7 +21819,7 @@ var paramConformanceRule = {
|
|
|
21453
21819
|
"UNREALIZED_PARAM",
|
|
21454
21820
|
`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.`,
|
|
21455
21821
|
implementation.id,
|
|
21456
|
-
|
|
21822
|
+
draftContext3,
|
|
21457
21823
|
void 0,
|
|
21458
21824
|
{ at: method.name, covers: unrealized.map((found) => found.unit) }
|
|
21459
21825
|
);
|
|
@@ -21465,7 +21831,7 @@ var paramConformanceRule = {
|
|
|
21465
21831
|
"UNDECLARED_PARAM",
|
|
21466
21832
|
`${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.`,
|
|
21467
21833
|
implementation.id,
|
|
21468
|
-
|
|
21834
|
+
draftContext3,
|
|
21469
21835
|
void 0,
|
|
21470
21836
|
{ at: method.name, covers: undeclared.map((found) => found.unit) }
|
|
21471
21837
|
);
|
|
@@ -21477,7 +21843,7 @@ var paramConformanceRule = {
|
|
|
21477
21843
|
"PARAM_NAME_MISMATCH",
|
|
21478
21844
|
`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.`,
|
|
21479
21845
|
implementation.id,
|
|
21480
|
-
|
|
21846
|
+
draftContext3,
|
|
21481
21847
|
void 0,
|
|
21482
21848
|
{ at: method.name, covers: renamed.map((found) => found.unit) }
|
|
21483
21849
|
);
|
|
@@ -21489,7 +21855,7 @@ var paramConformanceRule = {
|
|
|
21489
21855
|
"PARAM_OPTIONALITY",
|
|
21490
21856
|
`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.`,
|
|
21491
21857
|
implementation.id,
|
|
21492
|
-
|
|
21858
|
+
draftContext3,
|
|
21493
21859
|
void 0,
|
|
21494
21860
|
{ at: method.name, covers: disagreed.map((found) => found.unit) }
|
|
21495
21861
|
);
|
|
@@ -21566,7 +21932,7 @@ var routeCoverageRule = {
|
|
|
21566
21932
|
const routes = code.factsAt(file).functionRoutes;
|
|
21567
21933
|
return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
|
|
21568
21934
|
});
|
|
21569
|
-
const
|
|
21935
|
+
const draftContext3 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
|
|
21570
21936
|
const where = holders.map((file) => `"${file}"`).join(", ");
|
|
21571
21937
|
if (read2.length === 0) {
|
|
21572
21938
|
ctx.addIssue(
|
|
@@ -21574,7 +21940,7 @@ var routeCoverageRule = {
|
|
|
21574
21940
|
"UNREADABLE_ROUTER",
|
|
21575
21941
|
`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.`,
|
|
21576
21942
|
anchor,
|
|
21577
|
-
|
|
21943
|
+
draftContext3,
|
|
21578
21944
|
void 0,
|
|
21579
21945
|
{ at: via }
|
|
21580
21946
|
);
|
|
@@ -21599,7 +21965,7 @@ var routeCoverageRule = {
|
|
|
21599
21965
|
"UNDECLARED_ROUTE",
|
|
21600
21966
|
`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.`,
|
|
21601
21967
|
anchor,
|
|
21602
|
-
|
|
21968
|
+
draftContext3,
|
|
21603
21969
|
void 0,
|
|
21604
21970
|
{ at: via, covers: undeclared }
|
|
21605
21971
|
);
|
|
@@ -21611,7 +21977,7 @@ var routeCoverageRule = {
|
|
|
21611
21977
|
"UNROUTED_ENDPOINT",
|
|
21612
21978
|
`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.`,
|
|
21613
21979
|
anchor,
|
|
21614
|
-
|
|
21980
|
+
draftContext3,
|
|
21615
21981
|
void 0,
|
|
21616
21982
|
{ at: via, covers: unrouted }
|
|
21617
21983
|
);
|
|
@@ -21714,7 +22080,7 @@ var exportConformanceRule = {
|
|
|
21714
22080
|
}
|
|
21715
22081
|
}
|
|
21716
22082
|
}
|
|
21717
|
-
for (const { implementation, method, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
22083
|
+
for (const { implementation, method, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
|
|
21718
22084
|
const handle = method.exportedVia;
|
|
21719
22085
|
if (!handle || !sourceFile) continue;
|
|
21720
22086
|
const file = pathKey(sourceFile);
|
|
@@ -21729,7 +22095,7 @@ var exportConformanceRule = {
|
|
|
21729
22095
|
"UNREALIZED_EXPORT_HANDLE",
|
|
21730
22096
|
`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.`,
|
|
21731
22097
|
implementation.id,
|
|
21732
|
-
|
|
22098
|
+
draftContext3
|
|
21733
22099
|
);
|
|
21734
22100
|
}
|
|
21735
22101
|
for (const listener of ctx.components) {
|
|
@@ -23019,6 +23385,68 @@ var namingDisciplineRule = {
|
|
|
23019
23385
|
}
|
|
23020
23386
|
};
|
|
23021
23387
|
|
|
23388
|
+
// src/core/rules/heuristic/signature-source-suggestions.ts
|
|
23389
|
+
function sameParams(a, b) {
|
|
23390
|
+
if (!b || a.length !== b.length) return false;
|
|
23391
|
+
return a.every((p, i) => p.name === b[i].name && p.type === b[i].type && !!p.optional === !!b[i].optional);
|
|
23392
|
+
}
|
|
23393
|
+
function localName(id) {
|
|
23394
|
+
return id.split("::").pop();
|
|
23395
|
+
}
|
|
23396
|
+
var signatureSourceSuggestionsRule = {
|
|
23397
|
+
name: "signature-source-suggestions",
|
|
23398
|
+
judges: "design",
|
|
23399
|
+
description: "A Repository facade method is pure 1:1 forwarding to an owned member by doctrine (facade-forwarding), so its signature IS the member's: where its params (name, type, optional marker, order) and returns equal the owned member method it forwards to, it could name that method as its signatureFrom, and the reference can never be wrong. Suggested only where the adoption would be legal: the member method names no source of its own (no chain). Nothing else is suggested: another forwarder \u2014 a Portal restating an Orchestrator, a client Adapter restating a remote Portal \u2014 is often a public surface that should stay deliberately decoupled from an internal signature, so wairon does not suggest coupling them. Adoption is opt-in, so this is a notice, and ONE per facade contract, the restating methods being the units it covers.",
|
|
23400
|
+
codes: [
|
|
23401
|
+
{ code: "SIGNATURE_SOURCE_AVAILABLE", defaultSeverity: "notice", summary: "A Repository facade's methods restate exactly the owned member methods they forward to and could name them as their signatureFrom" }
|
|
23402
|
+
],
|
|
23403
|
+
check(ctx) {
|
|
23404
|
+
for (const impl of ctx.implementations) {
|
|
23405
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
23406
|
+
if (!contract || !ctx.isSpecInScope(contract.id)) continue;
|
|
23407
|
+
const repository = ctx.componentMap.get(contract.component);
|
|
23408
|
+
if (!repository || repository.componentType !== "Repository") continue;
|
|
23409
|
+
const owned = new Set(repository.owns);
|
|
23410
|
+
const restating = [];
|
|
23411
|
+
for (const realized of impl.methods) {
|
|
23412
|
+
const steps = realized.narrative ?? [];
|
|
23413
|
+
let member;
|
|
23414
|
+
let memberMethod;
|
|
23415
|
+
if (steps.length === 1 && steps[0].type === "call") {
|
|
23416
|
+
member = steps[0].targetComponent;
|
|
23417
|
+
memberMethod = steps[0].targetMethod;
|
|
23418
|
+
} else if (steps.length === 0 && realized.calls?.length === 1) {
|
|
23419
|
+
const entry = realized.calls[0];
|
|
23420
|
+
const dot = entry.lastIndexOf(".");
|
|
23421
|
+
if (dot > 0) {
|
|
23422
|
+
member = entry.slice(0, dot);
|
|
23423
|
+
memberMethod = entry.slice(dot + 1);
|
|
23424
|
+
}
|
|
23425
|
+
}
|
|
23426
|
+
if (!member || !memberMethod) continue;
|
|
23427
|
+
const memberKey = [...owned].find((o) => o === member || localName(o) === localName(member));
|
|
23428
|
+
if (!memberKey) continue;
|
|
23429
|
+
const own = contract.methods.find((m) => m.name === realized.name);
|
|
23430
|
+
if (!own || own.signatureFrom !== void 0 || !own.params) continue;
|
|
23431
|
+
const target = (ctx.interfacesByComponent.get(memberKey) ?? []).flatMap((i) => i.methods).find((m) => m.name === memberMethod);
|
|
23432
|
+
if (!target || target.signatureFrom !== void 0) continue;
|
|
23433
|
+
if (!sameParams(own.params, target.params) || own.returns !== target.returns) continue;
|
|
23434
|
+
restating.push(`${own.name} \u2190 ${localName(memberKey)}.${memberMethod}`);
|
|
23435
|
+
}
|
|
23436
|
+
if (restating.length === 0) continue;
|
|
23437
|
+
ctx.addIssue(
|
|
23438
|
+
"notice",
|
|
23439
|
+
"SIGNATURE_SOURCE_AVAILABLE",
|
|
23440
|
+
`${restating.length === 1 ? "A method" : `${restating.length} methods`} of Repository facade "${contract.id}" restate${restating.length === 1 ? "s" : ""} exactly the owned member method${restating.length === 1 ? "" : "s"} ${restating.length === 1 ? "it forwards" : "they forward"} to: ${restating.map((u) => `"${u}"`).join("; ")}. ${restating.length === 1 ? "It" : "Each"} could name that member method as its signatureFrom (and drop its params and returns), so the signature is stated once. Optional \u2014 a facade's signature is its member's by doctrine.`,
|
|
23441
|
+
contract.id,
|
|
23442
|
+
ctx.isImplementationDraft(impl),
|
|
23443
|
+
void 0,
|
|
23444
|
+
{ at: contract.id, covers: restating }
|
|
23445
|
+
);
|
|
23446
|
+
}
|
|
23447
|
+
}
|
|
23448
|
+
};
|
|
23449
|
+
|
|
23022
23450
|
// src/core/rules/repository.ts
|
|
23023
23451
|
var SDD_RULES = [
|
|
23024
23452
|
hierarchyRule,
|
|
@@ -23038,6 +23466,9 @@ var SDD_RULES = [
|
|
|
23038
23466
|
typeDeclarationsRule,
|
|
23039
23467
|
fieldTypeReferencesRule,
|
|
23040
23468
|
signatureTypeReferencesRule,
|
|
23469
|
+
signatureSourcesRule,
|
|
23470
|
+
signatureTypesRule,
|
|
23471
|
+
signatureTextRule,
|
|
23041
23472
|
// Contracts and the targets narratives name, in four questions with one
|
|
23042
23473
|
// owner each: does the implementation mirror its contract, does a target
|
|
23043
23474
|
// inside this tree resolve, does a target that leaves it pin to exactly one
|
|
@@ -23223,6 +23654,7 @@ var SDD_RULES = [
|
|
|
23223
23654
|
narrativeComplexityRule,
|
|
23224
23655
|
namingDisciplineRule,
|
|
23225
23656
|
methodCohesionRule,
|
|
23657
|
+
signatureSourceSuggestionsRule,
|
|
23226
23658
|
// Pack resolution and reproducibility run late: they are about project
|
|
23227
23659
|
// CONFIGURATION (does the declared pack set resolve, and can it be reproduced
|
|
23228
23660
|
// elsewhere?) rather than spec content.
|
|
@@ -23590,24 +24022,6 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
23590
24022
|
if (!argument || !ts.isTypeQueryNode(argument) || !ts.isIdentifier(argument.exprName)) return void 0;
|
|
23591
24023
|
return argument.exprName.text;
|
|
23592
24024
|
};
|
|
23593
|
-
const baseObjectLiteral = (expression) => {
|
|
23594
|
-
let node = expression;
|
|
23595
|
-
const walked = /* @__PURE__ */ new Set();
|
|
23596
|
-
while (node && !walked.has(node)) {
|
|
23597
|
-
walked.add(node);
|
|
23598
|
-
if (ts.isCallExpression(node)) {
|
|
23599
|
-
const callee = node.expression;
|
|
23600
|
-
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
|
|
23601
|
-
const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
23602
|
-
if (literal) return literal;
|
|
23603
|
-
}
|
|
23604
|
-
node = callee;
|
|
23605
|
-
} else if (ts.isPropertyAccessExpression(node)) {
|
|
23606
|
-
node = node.expression;
|
|
23607
|
-
} else break;
|
|
23608
|
-
}
|
|
23609
|
-
return void 0;
|
|
23610
|
-
};
|
|
23611
24025
|
const chainedCombinators = (expression) => {
|
|
23612
24026
|
const applied = /* @__PURE__ */ new Set();
|
|
23613
24027
|
let node = expression;
|
|
@@ -23877,13 +24291,48 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
23877
24291
|
ts.forEachChild(node, visit);
|
|
23878
24292
|
};
|
|
23879
24293
|
visit(sf);
|
|
24294
|
+
const composedMembers = (expression, seen) => {
|
|
24295
|
+
const extensions = [];
|
|
24296
|
+
const compose2 = (members) => {
|
|
24297
|
+
const byName = new Map(members.map((member) => [member.name, member]));
|
|
24298
|
+
for (const extension of [...extensions].reverse()) {
|
|
24299
|
+
for (const member of schemaMembers(extension)) byName.set(member.name, member);
|
|
24300
|
+
}
|
|
24301
|
+
return [...byName.values()];
|
|
24302
|
+
};
|
|
24303
|
+
let node = expression;
|
|
24304
|
+
const walked = /* @__PURE__ */ new Set();
|
|
24305
|
+
while (node && !walked.has(node)) {
|
|
24306
|
+
walked.add(node);
|
|
24307
|
+
if (ts.isCallExpression(node)) {
|
|
24308
|
+
const callee = node.expression;
|
|
24309
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
|
|
24310
|
+
const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
24311
|
+
if (literal) return compose2(schemaMembers(literal));
|
|
24312
|
+
} else if (ts.isPropertyAccessExpression(callee) && callee.name.text === "extend") {
|
|
24313
|
+
const extension = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
24314
|
+
if (!extension) return void 0;
|
|
24315
|
+
extensions.push(extension);
|
|
24316
|
+
}
|
|
24317
|
+
node = callee;
|
|
24318
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
24319
|
+
node = node.expression;
|
|
24320
|
+
} else if (ts.isIdentifier(node)) {
|
|
24321
|
+
const base = schemaConstants.get(node.text);
|
|
24322
|
+
if (!base || seen.has(node.text)) return void 0;
|
|
24323
|
+
const members = composedMembers(base, /* @__PURE__ */ new Set([...seen, node.text]));
|
|
24324
|
+
return members ? compose2(members) : void 0;
|
|
24325
|
+
} else break;
|
|
24326
|
+
}
|
|
24327
|
+
return void 0;
|
|
24328
|
+
};
|
|
23880
24329
|
for (const alias of derivedAliases) {
|
|
23881
24330
|
if (typeShapes.has(alias.name)) continue;
|
|
23882
24331
|
const initializer = schemaConstants.get(alias.constant);
|
|
23883
24332
|
if (!initializer) continue;
|
|
23884
|
-
const
|
|
23885
|
-
if (!
|
|
23886
|
-
typeShapes.set(alias.name, { origin: "derived", fields
|
|
24333
|
+
const fields = composedMembers(initializer, /* @__PURE__ */ new Set([alias.constant]));
|
|
24334
|
+
if (!fields) continue;
|
|
24335
|
+
typeShapes.set(alias.name, { origin: "derived", fields, methods: [] });
|
|
23887
24336
|
}
|
|
23888
24337
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
23889
24338
|
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
|
|
@@ -24904,6 +25353,7 @@ function buildRuleContext(opts) {
|
|
|
24904
25353
|
...opts.exportTables ? { exportTables: opts.exportTables } : {},
|
|
24905
25354
|
...opts.projectFamily ? { projectFamily: opts.projectFamily } : {},
|
|
24906
25355
|
...opts.exportUsages ? { exportUsages: opts.exportUsages } : {},
|
|
25356
|
+
...opts.signatureFacts ? { signatureFacts: opts.signatureFacts } : {},
|
|
24907
25357
|
pinnedExternals,
|
|
24908
25358
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
24909
25359
|
roundTripIssues: opts.roundTripIssues,
|
|
@@ -25345,6 +25795,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25345
25795
|
const memberTables = family.nodes.filter((n) => n.namespace !== "").map((n) => resolveProjectExports(n.namespace));
|
|
25346
25796
|
const producers = new Set(family.references.filter((r) => r.consumer === "").map((r) => r.producer));
|
|
25347
25797
|
const exportUsages = [...producers].map((producer) => exportUsage("", producer));
|
|
25798
|
+
const signatures = signatureFacts();
|
|
25348
25799
|
const ctx = buildRuleContext({
|
|
25349
25800
|
system,
|
|
25350
25801
|
subsystems,
|
|
@@ -25366,6 +25817,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
25366
25817
|
],
|
|
25367
25818
|
projectFamily: family,
|
|
25368
25819
|
exportUsages,
|
|
25820
|
+
signatureFacts: signatures,
|
|
25369
25821
|
pinnedExternals,
|
|
25370
25822
|
// By-name selections only: a legacy path ref pins nothing to check. A dry
|
|
25371
25823
|
// run supplies its candidate's; otherwise the stored ones.
|
|
@@ -25485,12 +25937,26 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
25485
25937
|
if (ownIds.has(key) || context.ids.has(key)) continue;
|
|
25486
25938
|
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));
|
|
25487
25939
|
}
|
|
25940
|
+
const ownSignatures = signatureFacts();
|
|
25941
|
+
const resolved = resolveTree(
|
|
25942
|
+
[...own.interfaces, ...context.interfaces],
|
|
25943
|
+
[...own.components, ...context.components],
|
|
25944
|
+
[...own.types, ...context.types]
|
|
25945
|
+
);
|
|
25946
|
+
const ownInterfaceIds = new Set(own.interfaces.map((i) => i.id));
|
|
25947
|
+
const partSignatures = {
|
|
25948
|
+
sources: [
|
|
25949
|
+
...resolved.facts.sources.filter((f) => ownInterfaceIds.has(f.interfaceId) && f.outcome !== "restated"),
|
|
25950
|
+
...ownSignatures.sources.filter((f) => f.outcome === "restated")
|
|
25951
|
+
],
|
|
25952
|
+
staleTexts: ownSignatures.staleTexts
|
|
25953
|
+
};
|
|
25488
25954
|
const all = {
|
|
25489
25955
|
subsystems: [...own.subsystems, ...context.subsystems],
|
|
25490
25956
|
components: [...own.components, ...context.components],
|
|
25491
|
-
interfaces:
|
|
25957
|
+
interfaces: resolved.interfaces,
|
|
25492
25958
|
implementations: [...own.implementations, ...context.implementations],
|
|
25493
|
-
types:
|
|
25959
|
+
types: resolved.types
|
|
25494
25960
|
};
|
|
25495
25961
|
const system = context.system ?? SystemSpecSchema.parse({ createdAt: EXCERPT_EPOCH, updatedAt: EXCERPT_EPOCH, name: parentId, vision: `The part's parent, "${parentId}" (no L0 pinned).` });
|
|
25496
25962
|
const codeModel = buildCodeModel(own.implementations, own.types, root, [], []);
|
|
@@ -25510,6 +25976,7 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
25510
25976
|
exportTables: [],
|
|
25511
25977
|
projectFamily: graph(),
|
|
25512
25978
|
exportUsages: [],
|
|
25979
|
+
signatureFacts: partSignatures,
|
|
25513
25980
|
pinnedExternals: [],
|
|
25514
25981
|
packSelections: packs.filter((p) => typeof p !== "string"),
|
|
25515
25982
|
packRequirements: [],
|
|
@@ -26364,7 +26831,7 @@ function projectFamilyGraph() {
|
|
|
26364
26831
|
|
|
26365
26832
|
// src/core/exports.ts
|
|
26366
26833
|
var CONSUMABLE = /* @__PURE__ */ new Set(["Portal", "Observer"]);
|
|
26367
|
-
function
|
|
26834
|
+
function localName2(id) {
|
|
26368
26835
|
return id.split("::").pop() ?? id;
|
|
26369
26836
|
}
|
|
26370
26837
|
function ownedBy(subsystemId, declaringSubsystem) {
|
|
@@ -26411,7 +26878,7 @@ function findInSource(world, source, table, request) {
|
|
|
26411
26878
|
if (request.typeDef) {
|
|
26412
26879
|
const type = lookupType(world, request.typeDef, source.id);
|
|
26413
26880
|
if (!type) return {};
|
|
26414
|
-
const item2 = { publicName:
|
|
26881
|
+
const item2 = { publicName: localName2(type.id), kind: "type", source: type.subsystem ?? source.id, typeDef: type.id, via: [] };
|
|
26415
26882
|
return { item: item2, found: table.entries.find((e) => e.kind === "type" && e.typeDef === type.id && e.source === item2.source) };
|
|
26416
26883
|
}
|
|
26417
26884
|
const intf = lookup(world.interfaces, request.interface, source.id);
|
|
@@ -26419,7 +26886,7 @@ function findInSource(world, source, table, request) {
|
|
|
26419
26886
|
if (!comp) return {};
|
|
26420
26887
|
const narrowed2 = intf?.id ?? request.interface;
|
|
26421
26888
|
const item = {
|
|
26422
|
-
publicName:
|
|
26889
|
+
publicName: localName2(narrowed2 ?? comp.id),
|
|
26423
26890
|
kind: "component",
|
|
26424
26891
|
source: comp.subsystem,
|
|
26425
26892
|
component: comp.id,
|
|
@@ -26459,7 +26926,7 @@ function bindOwn(world, sub, pi, problems) {
|
|
|
26459
26926
|
problems.push({ kind: "invalid", owner: sub.id, detail: `exports type "${pi.typeDef}" as its own, but ${type ? `it belongs to "${type.subsystem ?? "the system"}"` : "no such type exists"}` });
|
|
26460
26927
|
return void 0;
|
|
26461
26928
|
}
|
|
26462
|
-
const name2 = pi.as ??
|
|
26929
|
+
const name2 = pi.as ?? localName2(type.id);
|
|
26463
26930
|
checkPublicName(name2, sub.id, problems);
|
|
26464
26931
|
return { publicName: name2, kind: "type", source: type.subsystem ?? sub.id, typeDef: type.id, via: [] };
|
|
26465
26932
|
}
|
|
@@ -26467,7 +26934,7 @@ function bindOwn(world, sub, pi, problems) {
|
|
|
26467
26934
|
if (!comp || !ownedBy(sub.id, comp.subsystem)) return void 0;
|
|
26468
26935
|
const intf = lookup(world.interfaces, pi.interface, sub.id);
|
|
26469
26936
|
const narrowed2 = intf?.id ?? pi.interface;
|
|
26470
|
-
const name = pi.as ??
|
|
26937
|
+
const name = pi.as ?? localName2(narrowed2 ?? comp.id);
|
|
26471
26938
|
checkPublicName(name, sub.id, problems);
|
|
26472
26939
|
return {
|
|
26473
26940
|
publicName: name,
|
|
@@ -26502,7 +26969,7 @@ function subsystemCandidates(world, sub, tables, group, problems) {
|
|
|
26502
26969
|
source,
|
|
26503
26970
|
sourceTable,
|
|
26504
26971
|
{ component: pi.component, interface: pi.interface, typeDef: pi.typeDef },
|
|
26505
|
-
(item) => pi.as ??
|
|
26972
|
+
(item) => pi.as ?? localName2(pi.interface ?? item.typeDef ?? item.component ?? ""),
|
|
26506
26973
|
group.has(source.id),
|
|
26507
26974
|
problems
|
|
26508
26975
|
);
|
|
@@ -26678,16 +27145,16 @@ function classifySource(world, sourceId, label7, sources) {
|
|
|
26678
27145
|
function bindOwnType(world, owner, e, problems) {
|
|
26679
27146
|
const type = (world.types.get(e.typeDef) ?? []).find((t) => !t.subsystem);
|
|
26680
27147
|
if (!type) {
|
|
26681
|
-
problems.push({ kind: "invalid", owner, publicName: e.as ?? e.id ??
|
|
27148
|
+
problems.push({ kind: "invalid", owner, publicName: e.as ?? e.id ?? localName2(e.typeDef), detail: `exports type "${e.typeDef}" as its own, but the project owns no project-level type of that id` });
|
|
26682
27149
|
return void 0;
|
|
26683
27150
|
}
|
|
26684
|
-
const name = e.as ?? e.id ??
|
|
27151
|
+
const name = e.as ?? e.id ?? localName2(type.id);
|
|
26685
27152
|
checkPublicName(name, owner, problems);
|
|
26686
27153
|
return { publicName: name, kind: "type", source: owner, typeDef: type.id, via: [] };
|
|
26687
27154
|
}
|
|
26688
27155
|
function bindFromProject(owner, e, source, table, index, problems) {
|
|
26689
27156
|
const named2 = e.component !== void 0 || e.typeDef !== void 0 || e.interface !== void 0;
|
|
26690
|
-
const requested = named2 ?
|
|
27157
|
+
const requested = named2 ? localName2(e.interface ?? e.component ?? e.typeDef) : void 0;
|
|
26691
27158
|
const picked = named2 ? table.entries.filter((x) => x.publicName === requested) : table.entries;
|
|
26692
27159
|
if (named2 && picked.length === 0) {
|
|
26693
27160
|
problems.push({ kind: "invalid", owner, publicName: e.as ?? e.id ?? requested, detail: `re-exports "${requested}" from ${source.label}, whose export table has no such public name \u2014 another project is reached only through its L0 exports` });
|
|
@@ -26724,7 +27191,7 @@ function bindFromSubsystem(world, owner, e, subsystem, subsystemTables, index, p
|
|
|
26724
27191
|
// The default public name is the item's own LOCAL id: a member's L0 is
|
|
26725
27192
|
// read with its ids keyed under its project, and its entry names the
|
|
26726
27193
|
// item as the member wrote it.
|
|
26727
|
-
() => e.as ?? e.id ??
|
|
27194
|
+
() => e.as ?? e.id ?? localName2(e.interface ?? e.component ?? e.typeDef),
|
|
26728
27195
|
false,
|
|
26729
27196
|
problems
|
|
26730
27197
|
);
|
|
@@ -26865,6 +27332,124 @@ function pinnedUsageOf(consumer, alias) {
|
|
|
26865
27332
|
};
|
|
26866
27333
|
}
|
|
26867
27334
|
|
|
27335
|
+
// src/core/signature-sources.ts
|
|
27336
|
+
function emptySignatureFacts() {
|
|
27337
|
+
return { sources: [], staleTexts: [] };
|
|
27338
|
+
}
|
|
27339
|
+
function namespaceOf(key) {
|
|
27340
|
+
const at = key.lastIndexOf("::");
|
|
27341
|
+
return at === -1 ? "" : key.slice(0, at);
|
|
27342
|
+
}
|
|
27343
|
+
function unresolvedMethod(method) {
|
|
27344
|
+
const { params: _params, ...rest } = method;
|
|
27345
|
+
return { ...rest, returns: "unknown", signature: `${method.name}(...): unknown` };
|
|
27346
|
+
}
|
|
27347
|
+
function sameParams2(a, b) {
|
|
27348
|
+
const left = a ?? [];
|
|
27349
|
+
const right = b ?? [];
|
|
27350
|
+
return left.length === right.length && left.every((p, i) => p.name === right[i].name && p.type === right[i].type && !!p.optional === !!right[i].optional);
|
|
27351
|
+
}
|
|
27352
|
+
function tablesOf(interfaces, components, types, references) {
|
|
27353
|
+
const methodsOf = /* @__PURE__ */ new Map();
|
|
27354
|
+
for (const intf of interfaces) methodsOf.set(intf.component, [...methodsOf.get(intf.component) ?? [], ...intf.methods]);
|
|
27355
|
+
const boundTypes = /* @__PURE__ */ new Map();
|
|
27356
|
+
for (const ref of references) {
|
|
27357
|
+
if (ref.position !== "type" || ref.binding === "outside" || ref.binding === "unresolved") continue;
|
|
27358
|
+
boundTypes.set(`${ref.specId}|${ref.authored}`, ref.resolved);
|
|
27359
|
+
}
|
|
27360
|
+
return { methodsOf, componentKeys: new Set(components.map((c) => c.id)), types, boundTypes };
|
|
27361
|
+
}
|
|
27362
|
+
function methodReading(tables, intf, value) {
|
|
27363
|
+
const dot = value.lastIndexOf(".");
|
|
27364
|
+
if (dot <= 0 || dot === value.length - 1) return null;
|
|
27365
|
+
const head2 = value.slice(0, dot);
|
|
27366
|
+
const tail = value.slice(dot + 1);
|
|
27367
|
+
const ns = namespaceOf(intf.id);
|
|
27368
|
+
const keys = [head2, ...ns && !head2.startsWith(`${ns}::`) ? [`${ns}::${head2}`] : []];
|
|
27369
|
+
for (const key of keys) {
|
|
27370
|
+
if (!tables.componentKeys.has(key)) continue;
|
|
27371
|
+
const method = (tables.methodsOf.get(key) ?? []).find((m) => m.name === tail);
|
|
27372
|
+
if (method) return { target: `${key}.${tail}`, method };
|
|
27373
|
+
}
|
|
27374
|
+
return null;
|
|
27375
|
+
}
|
|
27376
|
+
function typeReading(tables, intf, value) {
|
|
27377
|
+
const bound2 = value.includes("::") ? tables.boundTypes.get(`${intf.id}|${value}`) : void 0;
|
|
27378
|
+
const named2 = bound2 !== void 0 ? tables.types.filter((t) => t.id === bound2) : tables.types.filter((t) => t.id === value || typeMatchesRef(t, value));
|
|
27379
|
+
const ns = namespaceOf(intf.id);
|
|
27380
|
+
const local = (t) => namespaceOf(t.id) === ns ? 0 : 1;
|
|
27381
|
+
const ranked = [...named2].sort((a, b) => local(a) - local(b));
|
|
27382
|
+
return { signature: ranked.find((t) => t.kind === "signature"), other: ranked.find((t) => t.kind !== "signature") };
|
|
27383
|
+
}
|
|
27384
|
+
function typeTarget(type) {
|
|
27385
|
+
return type.subsystem && !type.id.includes("::") ? `${type.subsystem}::${type.id}` : type.id;
|
|
27386
|
+
}
|
|
27387
|
+
function restatedFact(base, stored, params, returns) {
|
|
27388
|
+
const statesParams = stored.params !== void 0;
|
|
27389
|
+
const statesReturns = stored.returns !== void 0;
|
|
27390
|
+
if (!statesParams && !statesReturns) return null;
|
|
27391
|
+
const paramsDiffer = statesParams && !sameParams2(stored.params, params);
|
|
27392
|
+
const returnsDiffer = statesReturns && stored.returns !== returns;
|
|
27393
|
+
const differs = paramsDiffer || returnsDiffer;
|
|
27394
|
+
const detail = !differs ? "restates its source exactly" : [paramsDiffer ? "its params differ from the source's" : "", returnsDiffer ? `its returns "${stored.returns}" differ from the source's "${returns}"` : ""].filter(Boolean).join("; ");
|
|
27395
|
+
return { ...base, outcome: "restated", differs, detail };
|
|
27396
|
+
}
|
|
27397
|
+
function resolveSourced(tables, intf, stored, facts) {
|
|
27398
|
+
const source = stored.signatureFrom;
|
|
27399
|
+
const base = { interfaceId: intf.id, component: intf.component, method: stored.name, source };
|
|
27400
|
+
const hit = methodReading(tables, intf, source);
|
|
27401
|
+
const { signature, other } = typeReading(tables, intf, source);
|
|
27402
|
+
if (hit && signature) {
|
|
27403
|
+
facts.sources.push({ ...base, outcome: "ambiguous", candidates: [hit.target, typeTarget(signature)] });
|
|
27404
|
+
return unresolvedMethod(stored);
|
|
27405
|
+
}
|
|
27406
|
+
if (!hit && !signature) {
|
|
27407
|
+
facts.sources.push({ ...base, outcome: "unresolved", ...other ? { detail: `"${typeTarget(other)}" is a type of kind ${other.kind}, not a signature` } : {} });
|
|
27408
|
+
return unresolvedMethod(stored);
|
|
27409
|
+
}
|
|
27410
|
+
if (hit && hit.method.signatureFrom !== void 0) {
|
|
27411
|
+
facts.sources.push({ ...base, form: "method", target: hit.target, outcome: "chained", detail: hit.method.signatureFrom });
|
|
27412
|
+
return unresolvedMethod(stored);
|
|
27413
|
+
}
|
|
27414
|
+
const form = hit ? "method" : "signature";
|
|
27415
|
+
const target = hit ? hit.target : typeTarget(signature);
|
|
27416
|
+
const params = hit ? hit.method.params : signature.params;
|
|
27417
|
+
const returns = hit ? hit.method.returns : signature.returns ?? "unknown";
|
|
27418
|
+
const restated = restatedFact({ ...base, form, target }, stored, params, returns);
|
|
27419
|
+
if (restated) facts.sources.push(restated);
|
|
27420
|
+
facts.sources.push({ ...base, form, target, outcome: "resolved" });
|
|
27421
|
+
const resolved = { ...stored, returns };
|
|
27422
|
+
if (params) resolved.params = params.map((p) => ({ ...p }));
|
|
27423
|
+
else delete resolved.params;
|
|
27424
|
+
if (!params && hit) resolved.signature = hit.method.signature.replace(new RegExp(`^\\s*${hit.method.name}\\b`), stored.name);
|
|
27425
|
+
return resolved;
|
|
27426
|
+
}
|
|
27427
|
+
function withDerivedText(method, stored, specId, kind, facts) {
|
|
27428
|
+
const derived = deriveMethodSignature({ ...method, signature: stored ?? method.signature });
|
|
27429
|
+
if (derived === void 0) return method;
|
|
27430
|
+
if (stored !== void 0 && stored !== derived) facts.staleTexts.push({ specId, kind, method: method.name, stored, derived });
|
|
27431
|
+
return { ...method, signature: derived };
|
|
27432
|
+
}
|
|
27433
|
+
function resolveTree(interfaces, components, types, references = []) {
|
|
27434
|
+
const intfs = interfaces.map((i) => ({ ...i, methods: i.methods.map((m) => ({ ...m })) }));
|
|
27435
|
+
const typeCopies = types.map((t) => ({ ...t, methods: t.methods.map((m) => ({ ...m })) }));
|
|
27436
|
+
const tables = tablesOf(intfs, [...components], typeCopies, references);
|
|
27437
|
+
const facts = emptySignatureFacts();
|
|
27438
|
+
const sourced = intfs.map((intf) => ({
|
|
27439
|
+
...intf,
|
|
27440
|
+
methods: intf.methods.map((m) => m.signatureFrom !== void 0 ? resolveSourced(tables, intf, m, facts) : m)
|
|
27441
|
+
}));
|
|
27442
|
+
const resolvedInterfaces = sourced.map((intf, i) => ({
|
|
27443
|
+
...intf,
|
|
27444
|
+
methods: intf.methods.map((m, j) => withDerivedText(m, intfs[i].methods[j].signature, intf.id, "interface", facts))
|
|
27445
|
+
}));
|
|
27446
|
+
const resolvedTypes = typeCopies.map((t) => ({
|
|
27447
|
+
...t,
|
|
27448
|
+
methods: t.methods.map((m) => withDerivedText(m, m.signature, t.id, "type", facts))
|
|
27449
|
+
}));
|
|
27450
|
+
return { interfaces: resolvedInterfaces, types: resolvedTypes, facts };
|
|
27451
|
+
}
|
|
27452
|
+
|
|
26868
27453
|
// src/core/specs.ts
|
|
26869
27454
|
var PartReadOnly = class extends WaironError {
|
|
26870
27455
|
constructor(message) {
|
|
@@ -26931,7 +27516,8 @@ function emptyIndex() {
|
|
|
26931
27516
|
implementation: {},
|
|
26932
27517
|
type: {},
|
|
26933
27518
|
group: {}
|
|
26934
|
-
}
|
|
27519
|
+
},
|
|
27520
|
+
signatures: { sources: [], staleTexts: [] }
|
|
26935
27521
|
};
|
|
26936
27522
|
}
|
|
26937
27523
|
function isWithin(dir, file) {
|
|
@@ -27164,6 +27750,17 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
|
27164
27750
|
walk2(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
|
|
27165
27751
|
return inspection;
|
|
27166
27752
|
}
|
|
27753
|
+
function signatureSourceParts(value) {
|
|
27754
|
+
const dot = value.lastIndexOf(".");
|
|
27755
|
+
if (dot <= 0 || dot === value.length - 1) return null;
|
|
27756
|
+
return { head: value.slice(0, dot), tail: value.slice(dot + 1) };
|
|
27757
|
+
}
|
|
27758
|
+
function mapSignatureSource(value, map) {
|
|
27759
|
+
const parts = signatureSourceParts(value);
|
|
27760
|
+
if (!parts) return value;
|
|
27761
|
+
const head2 = map("signatureFrom", parts.head);
|
|
27762
|
+
return head2 === parts.head ? value : `${head2}.${parts.tail}`;
|
|
27763
|
+
}
|
|
27167
27764
|
function mapSpecReferences(kind, spec, map) {
|
|
27168
27765
|
switch (kind) {
|
|
27169
27766
|
case "subsystem": {
|
|
@@ -27194,7 +27791,15 @@ function mapSpecReferences(kind, spec, map) {
|
|
|
27194
27791
|
}
|
|
27195
27792
|
case "interface": {
|
|
27196
27793
|
const i = spec;
|
|
27197
|
-
return {
|
|
27794
|
+
return {
|
|
27795
|
+
...i,
|
|
27796
|
+
component: map("contract", i.component),
|
|
27797
|
+
// A method source's head is a component reference; a value with no
|
|
27798
|
+
// head is a raw type position the mapper is never asked about.
|
|
27799
|
+
...i.methods?.some((m) => m.signatureFrom !== void 0) ? {
|
|
27800
|
+
methods: i.methods.map((m) => m.signatureFrom !== void 0 ? { ...m, signatureFrom: mapSignatureSource(m.signatureFrom, map) } : m)
|
|
27801
|
+
} : {}
|
|
27802
|
+
};
|
|
27198
27803
|
}
|
|
27199
27804
|
case "implementation": {
|
|
27200
27805
|
const impl = spec;
|
|
@@ -27239,6 +27844,7 @@ function rawReferences(kind, spec) {
|
|
|
27239
27844
|
for (const m of spec.methods) {
|
|
27240
27845
|
qualifiedTypeNames(m.returns).forEach((t) => add2("type", t));
|
|
27241
27846
|
m.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((t) => add2("type", t)));
|
|
27847
|
+
if (m.signatureFrom !== void 0 && !signatureSourceParts(m.signatureFrom)) add2("type", m.signatureFrom);
|
|
27242
27848
|
}
|
|
27243
27849
|
break;
|
|
27244
27850
|
case "implementation":
|
|
@@ -27250,9 +27856,14 @@ function rawReferences(kind, spec) {
|
|
|
27250
27856
|
}
|
|
27251
27857
|
}
|
|
27252
27858
|
break;
|
|
27253
|
-
case "type":
|
|
27254
|
-
|
|
27859
|
+
case "type": {
|
|
27860
|
+
const t = spec;
|
|
27861
|
+
for (const f of t.fields) qualifiedTypeNames(f.type).forEach((n) => add2("type", n));
|
|
27862
|
+
t.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((n) => add2("type", n)));
|
|
27863
|
+
qualifiedTypeNames(t.returns).forEach((n) => add2("type", n));
|
|
27864
|
+
for (const m of t.methods ?? []) m.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((n) => add2("type", n)));
|
|
27255
27865
|
break;
|
|
27866
|
+
}
|
|
27256
27867
|
default:
|
|
27257
27868
|
break;
|
|
27258
27869
|
}
|
|
@@ -27283,6 +27894,8 @@ function bareRawReferences(kind, spec) {
|
|
|
27283
27894
|
case "type": {
|
|
27284
27895
|
const t = spec;
|
|
27285
27896
|
for (const f of t.fields) for (const ref of fieldTypeRefs(t, f.type)) add2("type", ref);
|
|
27897
|
+
for (const ref of signatureTypeRefs(t)) add2("type", ref);
|
|
27898
|
+
for (const m of t.methods ?? []) for (const p of m.params ?? []) for (const ref of fieldTypeRefs(t, p.type)) add2("type", ref);
|
|
27286
27899
|
break;
|
|
27287
27900
|
}
|
|
27288
27901
|
case "implementation":
|
|
@@ -27349,6 +27962,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
27349
27962
|
typed(m, "signature");
|
|
27350
27963
|
typed(m, "returns");
|
|
27351
27964
|
for (const p of list(m.params)) typed(p, "type");
|
|
27965
|
+
if (typeof m.signatureFrom === "string") {
|
|
27966
|
+
m.signatureFrom = signatureSourceParts(m.signatureFrom) ? mapSignatureSource(m.signatureFrom, map) : mapTypeNames(m.signatureFrom, map);
|
|
27967
|
+
}
|
|
27352
27968
|
}
|
|
27353
27969
|
break;
|
|
27354
27970
|
case "implementation":
|
|
@@ -27374,6 +27990,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
27374
27990
|
at(doc, "subsystem", "subsystem");
|
|
27375
27991
|
at(doc, "group", "group");
|
|
27376
27992
|
for (const f of list(doc.fields)) typed(f, "type");
|
|
27993
|
+
for (const p of list(doc.params)) typed(p, "type");
|
|
27994
|
+
typed(doc, "returns");
|
|
27995
|
+
for (const m of list(doc.methods)) for (const p of list(m.params)) typed(p, "type");
|
|
27377
27996
|
break;
|
|
27378
27997
|
default:
|
|
27379
27998
|
break;
|
|
@@ -27393,7 +28012,9 @@ function mapRawReferences(kind, spec, map) {
|
|
|
27393
28012
|
...m,
|
|
27394
28013
|
...m.signature !== void 0 ? { signature: mapTypeNames(m.signature, map) } : {},
|
|
27395
28014
|
...m.returns !== void 0 ? { returns: mapTypeNames(m.returns, map) } : {},
|
|
27396
|
-
...m.params ? { params: m.params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {}
|
|
28015
|
+
...m.params ? { params: m.params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {},
|
|
28016
|
+
// A type source is a raw type position; a method source's head is bound, never raw.
|
|
28017
|
+
...m.signatureFrom !== void 0 && !signatureSourceParts(m.signatureFrom) ? { signatureFrom: mapTypeNames(m.signatureFrom, map) } : {}
|
|
27397
28018
|
}))
|
|
27398
28019
|
};
|
|
27399
28020
|
}
|
|
@@ -27421,7 +28042,14 @@ function mapRawReferences(kind, spec, map) {
|
|
|
27421
28042
|
}
|
|
27422
28043
|
case "type": {
|
|
27423
28044
|
const t = spec;
|
|
27424
|
-
|
|
28045
|
+
const typedParams = (params) => params ? { params: params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {};
|
|
28046
|
+
return {
|
|
28047
|
+
...t,
|
|
28048
|
+
fields: t.fields.map((f) => ({ ...f, type: mapTypeNames(f.type, map) })),
|
|
28049
|
+
...typedParams(t.params),
|
|
28050
|
+
...t.returns !== void 0 ? { returns: mapTypeNames(t.returns, map) } : {},
|
|
28051
|
+
methods: (t.methods ?? []).map((m) => ({ ...m, ...typedParams(m.params) }))
|
|
28052
|
+
};
|
|
27425
28053
|
}
|
|
27426
28054
|
default:
|
|
27427
28055
|
return spec;
|
|
@@ -27760,6 +28388,7 @@ function specKind(raw) {
|
|
|
27760
28388
|
if ("component" in raw && Array.isArray(raw.methods)) return "interface";
|
|
27761
28389
|
if ("contract" in raw && Array.isArray(raw.methods)) return "implementation";
|
|
27762
28390
|
if ("kind" in raw && Array.isArray(raw.fields)) return "type";
|
|
28391
|
+
if (raw.kind === "signature") return "type";
|
|
27763
28392
|
return void 0;
|
|
27764
28393
|
}
|
|
27765
28394
|
function rewriteSpecRefs(raw, remap) {
|
|
@@ -27814,6 +28443,16 @@ function rewriteSpecRefs(raw, remap) {
|
|
|
27814
28443
|
rewrite(raw, "component", "component");
|
|
27815
28444
|
for (const method of entries(raw.methods)) {
|
|
27816
28445
|
for (const param of entries(method?.params)) rewrite(param, "type", "type");
|
|
28446
|
+
const source = method?.signatureFrom;
|
|
28447
|
+
if (typeof source !== "string") continue;
|
|
28448
|
+
const parts = signatureSourceParts(source);
|
|
28449
|
+
const name = parts ? remap(parts.tail, "method", parts.head) : void 0;
|
|
28450
|
+
const component = parts ? remap(parts.head, "component", parts.tail) : void 0;
|
|
28451
|
+
const next = parts && (component !== parts.head || name !== parts.tail) ? `${component}.${name}` : remap(source, "type");
|
|
28452
|
+
if (next !== source) {
|
|
28453
|
+
method.signatureFrom = next;
|
|
28454
|
+
changed = true;
|
|
28455
|
+
}
|
|
27817
28456
|
}
|
|
27818
28457
|
} else if (kind === "implementation") {
|
|
27819
28458
|
rewrite(raw, "contract", "interface");
|
|
@@ -27844,6 +28483,11 @@ function rewriteSpecRefs(raw, remap) {
|
|
|
27844
28483
|
} else if (kind === "type") {
|
|
27845
28484
|
rewrite(raw, "componentClass", "entity-class");
|
|
27846
28485
|
for (const field of entries(raw.fields)) rewrite(field, "type", "type");
|
|
28486
|
+
for (const param of entries(raw.params)) rewrite(param, "type", "type");
|
|
28487
|
+
rewrite(raw, "returns", "type");
|
|
28488
|
+
for (const method of entries(raw.methods)) {
|
|
28489
|
+
for (const param of entries(method?.params)) rewrite(param, "type", "type");
|
|
28490
|
+
}
|
|
27847
28491
|
}
|
|
27848
28492
|
return changed;
|
|
27849
28493
|
}
|
|
@@ -28362,6 +29006,10 @@ var SpecWorkspace = class {
|
|
|
28362
29006
|
index.groups.push(...raw.index.groups);
|
|
28363
29007
|
for (const kind of Object.keys(index.paths)) Object.assign(index.paths[kind], raw.index.paths[kind]);
|
|
28364
29008
|
}
|
|
29009
|
+
const signatures = resolveTree(index.interfaces, index.components, index.types, raws.flatMap((r) => r.record.authoredReferences));
|
|
29010
|
+
index.interfaces = signatures.interfaces;
|
|
29011
|
+
index.types = signatures.types;
|
|
29012
|
+
index.signatures = signatures.facts;
|
|
28365
29013
|
return { index, raws, readables };
|
|
28366
29014
|
}
|
|
28367
29015
|
/**
|
|
@@ -28787,7 +29435,9 @@ var SpecWorkspace = class {
|
|
|
28787
29435
|
rewrite: local
|
|
28788
29436
|
});
|
|
28789
29437
|
};
|
|
29438
|
+
const localComponents = new Set(index.components.map((c) => nameKey(c.id.split("::").pop())));
|
|
28790
29439
|
const bindLocal = (kind, specKey) => (position, value) => {
|
|
29440
|
+
if (position === "signatureFrom" && !value.includes("::") && !localComponents.has(nameKey(value))) return value;
|
|
28791
29441
|
if (value.includes("::")) {
|
|
28792
29442
|
raw.pending.push({ kind, specKey, position, authored: value, raw: false });
|
|
28793
29443
|
return value;
|
|
@@ -29122,7 +29772,8 @@ var SpecWorkspace = class {
|
|
|
29122
29772
|
this.scanAll();
|
|
29123
29773
|
const from = this.getSubprojectPrefix(spec.id) ?? "";
|
|
29124
29774
|
const carrying = carry && !this.carryDisabled ? /* @__PURE__ */ new Map() : null;
|
|
29125
|
-
const
|
|
29775
|
+
const components = new Set(this.scanAll().components.map((c) => c.id));
|
|
29776
|
+
const mapped = mapSpecReferences(kind, spec, (position, value) => position === "signatureFrom" && !components.has(value) ? value : this.writeReference(from, spec.id, position, value, carrying));
|
|
29126
29777
|
return { ...mapped, id: localOf(from, spec.id).split("::").pop() };
|
|
29127
29778
|
}
|
|
29128
29779
|
// -------------------------------------------------------------------------
|
|
@@ -29218,8 +29869,8 @@ var SpecWorkspace = class {
|
|
|
29218
29869
|
if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
|
|
29219
29870
|
return path21.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
|
|
29220
29871
|
}
|
|
29221
|
-
const
|
|
29222
|
-
return path21.join(this.paths.specsDir(), "default",
|
|
29872
|
+
const targetComponent2 = componentId || "default";
|
|
29873
|
+
return path21.join(this.paths.specsDir(), "default", targetComponent2, ".interface.yaml");
|
|
29223
29874
|
}
|
|
29224
29875
|
getImplementationPath(id, contractId) {
|
|
29225
29876
|
const index = this.scanAll();
|
|
@@ -29415,8 +30066,15 @@ var SpecWorkspace = class {
|
|
|
29415
30066
|
prepareComponentForWrite(spec) {
|
|
29416
30067
|
return this.relativizeSpec("component", spec);
|
|
29417
30068
|
}
|
|
30069
|
+
/**
|
|
30070
|
+
* spec_registry.saveInterfaceSpec steps 1-2: every method in its stored form
|
|
30071
|
+
* (method_signature.storedForm — the text a method's params derive, nothing
|
|
30072
|
+
* a signatureFrom supplies), then every reference written back relative to
|
|
30073
|
+
* the owning project.
|
|
30074
|
+
*/
|
|
29418
30075
|
prepareInterfaceForWrite(spec) {
|
|
29419
|
-
|
|
30076
|
+
const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedMethodSignature(m)) };
|
|
30077
|
+
return this.relativizeSpec("interface", stored);
|
|
29420
30078
|
}
|
|
29421
30079
|
prepareImplementationForWrite(spec, carry = true) {
|
|
29422
30080
|
const relative19 = this.relativizeSpec("implementation", spec, carry);
|
|
@@ -29426,8 +30084,10 @@ var SpecWorkspace = class {
|
|
|
29426
30084
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
29427
30085
|
return partDir ? partRelativeFilePaths(relative19, this.rootDir, partDir) : relative19;
|
|
29428
30086
|
}
|
|
30087
|
+
/** A type written in its stored form — every params-bearing method's text derived — relative to the owning project. */
|
|
29429
30088
|
prepareTypeForWrite(spec) {
|
|
29430
|
-
|
|
30089
|
+
const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedTypeMethod(m)) };
|
|
30090
|
+
return this.relativizeSpec("type", stored);
|
|
29431
30091
|
}
|
|
29432
30092
|
prepareGroupForWrite(spec) {
|
|
29433
30093
|
return this.relativizeSpec("group", spec);
|
|
@@ -29637,7 +30297,7 @@ var SpecWorkspace = class {
|
|
|
29637
30297
|
if (!pathExists(p)) return null;
|
|
29638
30298
|
try {
|
|
29639
30299
|
const raw = readSpecFile(p);
|
|
29640
|
-
return InterfaceSpecSchema.parse(raw);
|
|
30300
|
+
return resolveTree([InterfaceSpecSchema.parse(raw)], index.components, index.types).interfaces[0];
|
|
29641
30301
|
} catch (e) {
|
|
29642
30302
|
this.loaderIssues.push({
|
|
29643
30303
|
severity: "error",
|
|
@@ -30202,6 +30862,17 @@ var SpecWorkspace = class {
|
|
|
30202
30862
|
* dry-run a promotion (write 'complete' → validate → restore) without leaving
|
|
30203
30863
|
* any change behind if validation fails or the user cancels.
|
|
30204
30864
|
*/
|
|
30865
|
+
/**
|
|
30866
|
+
* ispec_index.signatureFacts — what the current scan's signature resolution
|
|
30867
|
+
* recorded: every signatureFrom met and how it resolved, and every stored
|
|
30868
|
+
* text its params contradict. Read from the cached scan, rescanning first
|
|
30869
|
+
* when the tree changed.
|
|
30870
|
+
*/
|
|
30871
|
+
signatureFacts() {
|
|
30872
|
+
const index = this;
|
|
30873
|
+
index.listProjectRoots();
|
|
30874
|
+
return this.cachedIndex.signatures;
|
|
30875
|
+
}
|
|
30205
30876
|
snapshotSpecFiles() {
|
|
30206
30877
|
const index = this.scanAll();
|
|
30207
30878
|
const snapshot = /* @__PURE__ */ new Map();
|
|
@@ -30299,7 +30970,7 @@ var SpecWorkspace = class {
|
|
|
30299
30970
|
*/
|
|
30300
30971
|
updateSpec(kind, id, delta, hooks, dryRun = false) {
|
|
30301
30972
|
const notices = [];
|
|
30302
|
-
const result = this.load(kind, id);
|
|
30973
|
+
const result = storedFormOf(kind, this.load(kind, id));
|
|
30303
30974
|
if (!result) {
|
|
30304
30975
|
throw new Error(`Spec of kind "${kind}" with ID "${id}" does not exist. Define it first.`);
|
|
30305
30976
|
}
|
|
@@ -30812,6 +31483,7 @@ var SpecWorkspace = class {
|
|
|
30812
31483
|
const qualifiedDelta = qualifyDeltaRefs(mergeableDelta);
|
|
30813
31484
|
const mergedResult = mergeDelta(result, qualifiedDelta);
|
|
30814
31485
|
for (const field of unsetFields) delete mergedResult[field];
|
|
31486
|
+
deriveMergedTexts(kind, mergedResult);
|
|
30815
31487
|
const withDefaults = deltaSchema.safeParse(mergedResult);
|
|
30816
31488
|
if (withDefaults.success) {
|
|
30817
31489
|
for (const [key, value] of Object.entries(withDefaults.data)) {
|
|
@@ -31348,6 +32020,26 @@ var PROSE_FIELDS = /* @__PURE__ */ new Set([
|
|
|
31348
32020
|
"caller"
|
|
31349
32021
|
]);
|
|
31350
32022
|
var DELTA_JUMP_PINS = /* @__PURE__ */ Symbol("jumps written by this delta");
|
|
32023
|
+
function storedFormOf(kind, spec) {
|
|
32024
|
+
if (!spec) return spec;
|
|
32025
|
+
if (kind === "interface") {
|
|
32026
|
+
const intf = spec;
|
|
32027
|
+
return { ...intf, methods: intf.methods.map((m) => storedMethodSignature(m)) };
|
|
32028
|
+
}
|
|
32029
|
+
if (kind === "type") {
|
|
32030
|
+
const type = spec;
|
|
32031
|
+
return { ...type, methods: type.methods.map((m) => storedTypeMethod(m)) };
|
|
32032
|
+
}
|
|
32033
|
+
return spec;
|
|
32034
|
+
}
|
|
32035
|
+
function deriveMergedTexts(kind, merged) {
|
|
32036
|
+
if (kind !== "interface" && kind !== "type" || !Array.isArray(merged.methods)) return;
|
|
32037
|
+
merged.methods = merged.methods.map((m) => {
|
|
32038
|
+
if (!m || typeof m !== "object" || m.signatureFrom !== void 0 || !Array.isArray(m.params)) return m;
|
|
32039
|
+
const derived = deriveMethodSignature(m);
|
|
32040
|
+
return derived === void 0 ? m : { ...m, signature: derived };
|
|
32041
|
+
});
|
|
32042
|
+
}
|
|
31351
32043
|
function cloneSpec(spec) {
|
|
31352
32044
|
return JSON.parse(JSON.stringify(spec));
|
|
31353
32045
|
}
|
|
@@ -31714,6 +32406,9 @@ function specPathsInScope(scopeSubsystem) {
|
|
|
31714
32406
|
function snapshotSpecFiles() {
|
|
31715
32407
|
return current().snapshotSpecFiles();
|
|
31716
32408
|
}
|
|
32409
|
+
function signatureFacts() {
|
|
32410
|
+
return current().signatureFacts();
|
|
32411
|
+
}
|
|
31717
32412
|
function findLegacySpecFiles() {
|
|
31718
32413
|
return current().findLegacySpecFiles();
|
|
31719
32414
|
}
|
|
@@ -31982,7 +32677,7 @@ function referencedKeys(part, types) {
|
|
|
31982
32677
|
}
|
|
31983
32678
|
for (const i of loadInterfaceSpecs().filter((x) => own.has(x.id))) {
|
|
31984
32679
|
add2(i.component);
|
|
31985
|
-
for (const m of i.methods) addTypes(
|
|
32680
|
+
for (const m of i.methods) addTypes(sourcedMethodTypeRefs(m));
|
|
31986
32681
|
}
|
|
31987
32682
|
for (const impl of loadImplementationSpecs().filter((x) => own.has(x.id))) {
|
|
31988
32683
|
add2(impl.contract);
|
|
@@ -31995,10 +32690,20 @@ function referencedKeys(part, types) {
|
|
|
31995
32690
|
}
|
|
31996
32691
|
for (const t of types.filter((x) => own.has(x.id))) {
|
|
31997
32692
|
add2(t.subsystem);
|
|
31998
|
-
|
|
32693
|
+
addTypes(typeSpecTypeRefs(t));
|
|
31999
32694
|
}
|
|
32000
32695
|
return out;
|
|
32001
32696
|
}
|
|
32697
|
+
function sourcedMethodTypeRefs(m) {
|
|
32698
|
+
return [...methodTypeRefs(m), ...m.signatureFrom !== void 0 ? [m.signatureFrom] : []];
|
|
32699
|
+
}
|
|
32700
|
+
function typeSpecTypeRefs(t) {
|
|
32701
|
+
return [
|
|
32702
|
+
...(t.fields ?? []).flatMap((f) => fieldTypeRefs(t, f.type)),
|
|
32703
|
+
...(t.methods ?? []).filter((m) => m.params !== void 0).flatMap((m) => methodTypeRefs(m)),
|
|
32704
|
+
...signatureTypeRefs(t)
|
|
32705
|
+
];
|
|
32706
|
+
}
|
|
32002
32707
|
var KINDS = ["component", "interface", "subsystem", "type", "implementation"];
|
|
32003
32708
|
function closeOver(keys, types) {
|
|
32004
32709
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -32018,10 +32723,9 @@ function closeOver(keys, types) {
|
|
|
32018
32723
|
queue.push(comp.subsystem);
|
|
32019
32724
|
for (const contract of interfaces.filter((i) => i.component === comp.id)) queue.push(contract.id);
|
|
32020
32725
|
} else if (found.kind === "interface") {
|
|
32021
|
-
for (const m of found.spec.methods) addTypes(
|
|
32726
|
+
for (const m of found.spec.methods) addTypes(sourcedMethodTypeRefs(m));
|
|
32022
32727
|
} else if (found.kind === "type") {
|
|
32023
|
-
|
|
32024
|
-
for (const f of type.fields ?? []) addTypes(fieldTypeRefs(type, f.type));
|
|
32728
|
+
addTypes(typeSpecTypeRefs(found.spec));
|
|
32025
32729
|
} else if (found.kind === "implementation") {
|
|
32026
32730
|
queue.push(found.spec.contract);
|
|
32027
32731
|
}
|
|
@@ -33561,7 +34265,11 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
33561
34265
|
for (const contract of moving) {
|
|
33562
34266
|
saveInterfaceSpec({
|
|
33563
34267
|
...contract,
|
|
33564
|
-
methods: contract.methods.map((m) => m.name === methodName ? {
|
|
34268
|
+
methods: contract.methods.map((m) => m.name === methodName ? {
|
|
34269
|
+
...m,
|
|
34270
|
+
name: newName,
|
|
34271
|
+
...m.params === void 0 && m.signatureFrom === void 0 ? { signature: renameInSignature(m.signature, methodName, newName) } : {}
|
|
34272
|
+
} : m)
|
|
33565
34273
|
});
|
|
33566
34274
|
renamed.push(contract.id);
|
|
33567
34275
|
}
|
|
@@ -35076,6 +35784,46 @@ function repairForeignStepFields(apply3) {
|
|
|
35076
35784
|
return repairs;
|
|
35077
35785
|
}
|
|
35078
35786
|
|
|
35787
|
+
// src/core/signature-repair.ts
|
|
35788
|
+
function repairSignatures(apply3) {
|
|
35789
|
+
const facts = signatureFacts();
|
|
35790
|
+
const repairs = /* @__PURE__ */ new Map();
|
|
35791
|
+
const repairOf = (specId, kind) => {
|
|
35792
|
+
const key = `${kind}:${specId}`;
|
|
35793
|
+
let repair = repairs.get(key);
|
|
35794
|
+
if (!repair) {
|
|
35795
|
+
repair = { specId, kind, regenerated: [], dropped: [] };
|
|
35796
|
+
repairs.set(key, repair);
|
|
35797
|
+
}
|
|
35798
|
+
return repair;
|
|
35799
|
+
};
|
|
35800
|
+
const held = new Set(facts.sources.filter((f) => f.outcome === "restated" && f.differs).map((f) => f.interfaceId));
|
|
35801
|
+
const own = (specId, kind) => !specId.includes("::") && !(kind === "interface" && held.has(specId));
|
|
35802
|
+
for (const stale of facts.staleTexts) {
|
|
35803
|
+
if (own(stale.specId, stale.kind)) repairOf(stale.specId, stale.kind).regenerated.push(stale);
|
|
35804
|
+
}
|
|
35805
|
+
for (const fact of facts.sources) {
|
|
35806
|
+
if (fact.outcome !== "restated" || fact.differs || !own(fact.interfaceId, "interface")) continue;
|
|
35807
|
+
repairOf(fact.interfaceId, "interface").dropped.push(fact.method);
|
|
35808
|
+
}
|
|
35809
|
+
const planned = [...repairs.values()];
|
|
35810
|
+
if (apply3 && planned.length > 0) {
|
|
35811
|
+
const interfaces = new Map(loadInterfaceSpecs().map((i) => [i.id, i]));
|
|
35812
|
+
const types = new Map(loadTypeSpecs().map((t) => [t.id, t]));
|
|
35813
|
+
for (const repair of planned) {
|
|
35814
|
+
if (repair.kind === "interface") {
|
|
35815
|
+
const intf = interfaces.get(repair.specId);
|
|
35816
|
+
if (intf) saveInterfaceSpec(intf);
|
|
35817
|
+
continue;
|
|
35818
|
+
}
|
|
35819
|
+
const type = types.get(repair.specId);
|
|
35820
|
+
if (type) saveSpec("type", type);
|
|
35821
|
+
}
|
|
35822
|
+
invalidateSpecCache();
|
|
35823
|
+
}
|
|
35824
|
+
return planned;
|
|
35825
|
+
}
|
|
35826
|
+
|
|
35079
35827
|
// src/core/approval.ts
|
|
35080
35828
|
var crypto7 = __toESM(require("crypto"));
|
|
35081
35829
|
var path35 = __toESM(require("path"));
|
|
@@ -36103,6 +36851,8 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
|
36103
36851
|
return {
|
|
36104
36852
|
gate: (kind, merged) => {
|
|
36105
36853
|
refuseUnknownOwner(kind, merged, storedOwner);
|
|
36854
|
+
const restated = kind === "interface" ? restatedSources(merged.methods) : [];
|
|
36855
|
+
if (restated.length) throw new Error(`${restatedSourceRefusal(String(merged.id), restated)} Nothing was written.`);
|
|
36106
36856
|
if (kind !== "component") return;
|
|
36107
36857
|
const verdict = validateComponentCandidate(merged, options);
|
|
36108
36858
|
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
@@ -36110,6 +36860,43 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
|
36110
36860
|
}
|
|
36111
36861
|
};
|
|
36112
36862
|
}
|
|
36863
|
+
function restatedSources(methods) {
|
|
36864
|
+
if (!Array.isArray(methods)) return [];
|
|
36865
|
+
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));
|
|
36866
|
+
}
|
|
36867
|
+
function restatedSourceRefusal(id, methods) {
|
|
36868
|
+
return `SIGNATURE_SOURCE_RESTATED: interface "${id}" \u2014 ${methods.map((n) => `"${n}"`).join(", ")} ${methods.length === 1 ? "names" : "name"} a signatureFrom and also ${methods.length === 1 ? "states" : "state"} params or returns. The source supplies them: state the signatureFrom alone, or the params and returns alone. To adopt a source on a method that states its own, unset its params and returns in the same delta.`;
|
|
36869
|
+
}
|
|
36870
|
+
function unsignedMethods(methods) {
|
|
36871
|
+
if (!Array.isArray(methods)) return [];
|
|
36872
|
+
return methods.filter((m) => m && typeof m === "object" && m.signatureFrom === void 0 && m.params === void 0 && (typeof m.signature !== "string" || m.signature.trim() === "")).map((m) => String(m.name));
|
|
36873
|
+
}
|
|
36874
|
+
function deriveStatedTexts(kind, candidate) {
|
|
36875
|
+
if (kind !== "interface" && kind !== "type" || !Array.isArray(candidate.methods)) return [];
|
|
36876
|
+
const differed = [];
|
|
36877
|
+
candidate.methods = candidate.methods.map((m) => {
|
|
36878
|
+
if (!m || typeof m !== "object" || m.signatureFrom !== void 0 || !Array.isArray(m.params)) return m;
|
|
36879
|
+
const derived = deriveMethodSignature(m);
|
|
36880
|
+
if (derived === void 0) return m;
|
|
36881
|
+
if (typeof m.signature === "string" && m.signature !== derived) differed.push(`"${m.name}": stated "${m.signature}", written "${derived}"`);
|
|
36882
|
+
return { ...m, signature: derived };
|
|
36883
|
+
});
|
|
36884
|
+
return differed.length === 0 ? [] : [
|
|
36885
|
+
`Signature text derived from params, not taken as stated \u2014 ${differed.join("; ")}. A method with params shows the text its params derive.`
|
|
36886
|
+
];
|
|
36887
|
+
}
|
|
36888
|
+
function storedForm(kind, spec) {
|
|
36889
|
+
if (!spec) return spec;
|
|
36890
|
+
if (kind === "interface") {
|
|
36891
|
+
const intf = spec;
|
|
36892
|
+
return { ...intf, methods: intf.methods.map((m) => storedMethodSignature(m)) };
|
|
36893
|
+
}
|
|
36894
|
+
if (kind === "type") {
|
|
36895
|
+
const type = spec;
|
|
36896
|
+
return { ...type, methods: type.methods.map((m) => storedTypeMethod(m)) };
|
|
36897
|
+
}
|
|
36898
|
+
return spec;
|
|
36899
|
+
}
|
|
36113
36900
|
function refuseUnknownOwner(kind, merged, storedOwner) {
|
|
36114
36901
|
if (kind !== "component" && kind !== "type") return;
|
|
36115
36902
|
const owner = merged.subsystem;
|
|
@@ -36154,7 +36941,8 @@ function restatementParent(restatement) {
|
|
|
36154
36941
|
return null;
|
|
36155
36942
|
}
|
|
36156
36943
|
}
|
|
36157
|
-
function applyRestatement(restatement,
|
|
36944
|
+
function applyRestatement(restatement, loaded, parent) {
|
|
36945
|
+
const existing = storedForm(restatement.kind, loaded);
|
|
36158
36946
|
const replacedExisting = existing !== null;
|
|
36159
36947
|
const parentRef = restatementParent(restatement);
|
|
36160
36948
|
if (parentRef && !parent) return refused(restatement, replacedExisting, MISSING_PARENT[restatement.kind](parentRef.id));
|
|
@@ -36165,6 +36953,19 @@ function applyRestatement(restatement, existing, parent) {
|
|
|
36165
36953
|
const carried = carryInto(restatement, existing, candidate);
|
|
36166
36954
|
const cleared = clearedByOmission(existing, candidate, restatement.fields);
|
|
36167
36955
|
stampLifecycle(candidate, existing, status.status);
|
|
36956
|
+
if (restatement.kind === "interface") {
|
|
36957
|
+
const restated = restatedSources(candidate.methods);
|
|
36958
|
+
if (restated.length) return refused(restatement, replacedExisting, `${restatedSourceRefusal(String(candidate.id), restated)} Nothing was written.`);
|
|
36959
|
+
}
|
|
36960
|
+
const unsigned = restatement.kind === "interface" || restatement.kind === "type" ? unsignedMethods(candidate.methods) : [];
|
|
36961
|
+
if (unsigned.length) {
|
|
36962
|
+
return refused(
|
|
36963
|
+
restatement,
|
|
36964
|
+
replacedExisting,
|
|
36965
|
+
`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
|
+
);
|
|
36967
|
+
}
|
|
36968
|
+
const derivedNotices = deriveStatedTexts(restatement.kind, candidate);
|
|
36168
36969
|
const labelErrors = resolveLabelsOf(restatement.kind, candidate);
|
|
36169
36970
|
if (labelErrors.length) {
|
|
36170
36971
|
return refused(restatement, replacedExisting, `Unresolved narrative label references \u2014 nothing was saved:
|
|
@@ -36176,7 +36977,7 @@ function applyRestatement(restatement, existing, parent) {
|
|
|
36176
36977
|
spec: parsed.spec,
|
|
36177
36978
|
...status.status ? { status: status.status } : {},
|
|
36178
36979
|
replacedExisting,
|
|
36179
|
-
notices: existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [],
|
|
36980
|
+
notices: [...existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [], ...derivedNotices],
|
|
36180
36981
|
changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
|
|
36181
36982
|
};
|
|
36182
36983
|
}
|
|
@@ -40187,8 +40988,27 @@ var getSpecOutput = {
|
|
|
40187
40988
|
}).optional().describe(
|
|
40188
40989
|
"Derived, read-only guidance for a variant-tagged component \u2014 resolved from the variant registry, not part of the spec. Never write it back."
|
|
40189
40990
|
),
|
|
40991
|
+
resolvedSignatures: import_zod11.z.array(import_zod11.z.object({
|
|
40992
|
+
method: import_zod11.z.string().describe("The contract method that takes its signature from a source."),
|
|
40993
|
+
signatureFrom: import_zod11.z.string().describe("The source it names, as the spec holds it."),
|
|
40994
|
+
signature: import_zod11.z.string().describe("The text it shows, derived from the resolved params and returns."),
|
|
40995
|
+
params: import_zod11.z.array(import_zod11.z.record(import_zod11.z.unknown())).optional().describe("The params the source supplies; absent when it supplies none (unresolved, ambiguous or chained)."),
|
|
40996
|
+
returns: import_zod11.z.string().describe("The returns the source supplies (`unknown` when it supplies none).")
|
|
40997
|
+
})).optional().describe(
|
|
40998
|
+
"Derived, read-only: for a contract whose methods take their signature from a source, each such method's RESOLVED params, returns and text. The spec carries those methods in stored form (the signatureFrom alone) so it can be re-authored from as it is; never write these back beside the source."
|
|
40999
|
+
),
|
|
40190
41000
|
...staleServerOutput
|
|
40191
41001
|
};
|
|
41002
|
+
function storedContractAnswer(loaded) {
|
|
41003
|
+
const resolvedSignatures = loaded.methods.filter((m) => m.signatureFrom !== void 0).map((m) => ({
|
|
41004
|
+
method: m.name,
|
|
41005
|
+
signatureFrom: m.signatureFrom,
|
|
41006
|
+
signature: m.signature,
|
|
41007
|
+
...m.params ? { params: m.params } : {},
|
|
41008
|
+
returns: m.returns
|
|
41009
|
+
}));
|
|
41010
|
+
return { spec: { ...loaded, methods: loaded.methods.map((m) => storedMethodSignature(m)) }, resolvedSignatures };
|
|
41011
|
+
}
|
|
40192
41012
|
var FINGERPRINT_VERSION = 1;
|
|
40193
41013
|
function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
|
|
40194
41014
|
const def = schema?._def;
|
|
@@ -40966,19 +41786,25 @@ ${renderChangeReport(report2)}`,
|
|
|
40966
41786
|
}
|
|
40967
41787
|
}
|
|
40968
41788
|
);
|
|
41789
|
+
const methodParamItem = import_zod11.z.object({
|
|
41790
|
+
name: import_zod11.z.string(),
|
|
41791
|
+
type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
|
|
41792
|
+
description: import_zod11.z.string().optional(),
|
|
41793
|
+
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 is a union \u2014 "ProjectConfig | null". Say whichever is true; they are different contracts.'
|
|
41795
|
+
)
|
|
41796
|
+
}).strict();
|
|
40969
41797
|
const interfaceMethodShape = {
|
|
40970
41798
|
name: import_zod11.z.string(),
|
|
40971
41799
|
description: import_zod11.z.string(),
|
|
40972
|
-
signature: import_zod11.z.string()
|
|
40973
|
-
|
|
40974
|
-
|
|
40975
|
-
|
|
40976
|
-
|
|
40977
|
-
|
|
40978
|
-
|
|
40979
|
-
|
|
40980
|
-
)
|
|
40981
|
-
}).strict()).optional().describe("Structured parameters \u2014 authoritative for type checking (the prose signature becomes display-only). Strongly preferred."),
|
|
41800
|
+
signature: import_zod11.z.string().optional().describe(
|
|
41801
|
+
"Prose signature \u2014 only for a method WITHOUT params. With params the server derives and writes the text (`name(a: T, b?: U): R`) and ignores this one (a notice names one that differed); a method with a signatureFrom states none."
|
|
41802
|
+
),
|
|
41803
|
+
returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("The type the method answers with. Required unless signatureFrom is set, whose source supplies it")),
|
|
41804
|
+
signatureFrom: import_zod11.z.string().min(1).optional().describe(
|
|
41805
|
+
"Take this method's params and returns from ONE source instead of stating them: a signature type id (`billing.change_listener`, `alias::name`) or a contract method `component.method` whose component this one names in dependsOn or owns (no chains). State it ALONE \u2014 params or returns beside it are refused (SIGNATURE_SOURCE_RESTATED). A value that names both a method and a signature type is ambiguous: qualify it."
|
|
41806
|
+
),
|
|
41807
|
+
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."),
|
|
40982
41808
|
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)"),
|
|
40983
41809
|
effect: import_zod11.z.enum(["read", "write"]).optional().describe("State-effect direction on the component's held state \u2014 required on a durable Store's contract methods so the durability round-trip rule can pair writes with hydration read-backs"),
|
|
40984
41810
|
invokedBy: import_zod11.z.object({
|
|
@@ -41006,7 +41832,7 @@ ${renderChangeReport(report2)}`,
|
|
|
41006
41832
|
server,
|
|
41007
41833
|
"sdd_define_interface",
|
|
41008
41834
|
{
|
|
41009
|
-
description: "Define an L3 Contract / Interface with method signatures for a component.
|
|
41835
|
+
description: "Define an L3 Contract / Interface with method signatures for a component. Each method states its signature ONE of three ways: structured `params` with `returns` (strongly preferred \u2014 authoritative for type checking, and the signature text is then DERIVED and written by the server; a text you pass is ignored and a notice names one that differed), a `signatureFrom` naming its source \u2014 a signature type, or `component.method` along a dependsOn/owns edge \u2014 with neither params nor returns (both together are refused, SIGNATURE_SOURCE_RESTATED), or, for a method without params, a prose `signature` with `returns`. A method declares the finding codes it reports in `findings` ({code, severity, summary}); each code must be anchored in the method's source file, as a string literal or a property-access name (UNREALIZED_FINDING). Re-defining an existing id REPLACES the method list: a method left out of the input is REMOVED (and reported); spec-level lint/ext and each method's endpoint binding are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the notices a restatement raised, each as its own entry.",
|
|
41010
41836
|
inputSchema: interfaceInput,
|
|
41011
41837
|
outputSchema: specWriteReceiptOutput
|
|
41012
41838
|
},
|
|
@@ -41214,7 +42040,7 @@ ${renderChangeReport(report2)}`,
|
|
|
41214
42040
|
}
|
|
41215
42041
|
);
|
|
41216
42042
|
const typeInput = {
|
|
41217
|
-
kind: import_zod11.z.enum(["entity", "value-object"]).describe("entity (owned by a subsystem)
|
|
42043
|
+
kind: import_zod11.z.enum(["entity", "value-object", "signature"]).describe("entity (owned by a subsystem), value-object (often system-level shared), or signature \u2014 a named function type: params and returns, nothing else"),
|
|
41218
42044
|
id: import_zod11.z.string().describe("Lowercase identifier"),
|
|
41219
42045
|
name: import_zod11.z.string().describe("Human-readable name"),
|
|
41220
42046
|
description: import_zod11.z.string().optional(),
|
|
@@ -41230,7 +42056,8 @@ ${renderChangeReport(report2)}`,
|
|
|
41230
42056
|
}).strict()).optional().describe('Data fields (type is a primitive or a qualified type id, e.g. "billing.Invoice")'),
|
|
41231
42057
|
methods: import_zod11.z.array(import_zod11.z.object({
|
|
41232
42058
|
name: import_zod11.z.string(),
|
|
41233
|
-
signature: import_zod11.z.string(),
|
|
42059
|
+
signature: import_zod11.z.string().optional().describe("Prose signature \u2014 only for a method WITHOUT params; with params the server derives and writes the text"),
|
|
42060
|
+
params: import_zod11.z.array(methodParamItem).optional().describe("Structured parameters \u2014 authoritative for type checking; the signature text is derived from them"),
|
|
41234
42061
|
returns: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The type the method answers with")),
|
|
41235
42062
|
description: import_zod11.z.string().optional(),
|
|
41236
42063
|
sourcePath: import_zod11.z.string().optional().describe("Source file realizing this method when the type's own sourcePath does not hold it \u2014 a pure type method often lives apart from the declaration"),
|
|
@@ -41245,18 +42072,20 @@ ${renderChangeReport(report2)}`,
|
|
|
41245
42072
|
table: import_zod11.z.string().optional().describe("Optional database table name for table-schema types"),
|
|
41246
42073
|
linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type"),
|
|
41247
42074
|
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)"),
|
|
41248
|
-
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')
|
|
42075
|
+
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
|
+
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"))
|
|
41249
42078
|
};
|
|
41250
42079
|
const typeInputFields = Object.keys(typeInput);
|
|
41251
42080
|
reg(
|
|
41252
42081
|
server,
|
|
41253
42082
|
"sdd_add_type",
|
|
41254
42083
|
{
|
|
41255
|
-
description: "Define an entity or value-object
|
|
42084
|
+
description: "Define a type: an entity or value-object (the data components operate on), or a signature \u2014 a named function type. 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.",
|
|
41256
42085
|
inputSchema: typeInput,
|
|
41257
42086
|
outputSchema: specWriteReceiptOutput
|
|
41258
42087
|
},
|
|
41259
|
-
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol }) => {
|
|
42088
|
+
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns }) => {
|
|
41260
42089
|
try {
|
|
41261
42090
|
const spec = {
|
|
41262
42091
|
kind,
|
|
@@ -41280,7 +42109,9 @@ ${renderChangeReport(report2)}`,
|
|
|
41280
42109
|
...table ? { table } : {},
|
|
41281
42110
|
...linkedEntity ? { linkedEntity } : {},
|
|
41282
42111
|
...sourcePath ? { sourcePath } : {},
|
|
41283
|
-
...symbol ? { symbol } : {}
|
|
42112
|
+
...symbol ? { symbol } : {},
|
|
42113
|
+
...params ? { params } : {},
|
|
42114
|
+
...returns ? { returns } : {}
|
|
41284
42115
|
};
|
|
41285
42116
|
const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
|
|
41286
42117
|
return writeReceipt(
|
|
@@ -41338,7 +42169,7 @@ ${renderChangeReport(report2)}`,
|
|
|
41338
42169
|
server,
|
|
41339
42170
|
"sdd_get_spec",
|
|
41340
42171
|
{
|
|
41341
|
-
description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back. The structured content carries the same answer with the
|
|
42172
|
+
description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back. A contract's methods come back in their STORED form \u2014 a method that takes its signature from a source carries its signatureFrom, not the params the loader resolves into it \u2014 so the answer can be re-authored from as it is; the resolved params, returns and text of each such method come back as a derived, read-only "resolvedSignatures" marker. The structured content carries the same answer with the derived markers KEPT SEPARATE from the stored spec ({kind, id, spec, partialResult?, variantGuidance?, resolvedSignatures?}), so nothing derived can be mistaken for something stored; the text block folds them in as it always has.`,
|
|
41342
42173
|
inputSchema: {
|
|
41343
42174
|
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The kind of specification"),
|
|
41344
42175
|
id: import_zod11.z.string().describe('The identifier of the spec to fetch (the L0 system spec is a singleton \u2014 pass the system name or "system")'),
|
|
@@ -41371,6 +42202,12 @@ ${renderChangeReport(report2)}`,
|
|
|
41371
42202
|
break;
|
|
41372
42203
|
}
|
|
41373
42204
|
if (!result) return errText(`Spec of kind "${kind}" with ID "${id}" does not exist.`);
|
|
42205
|
+
let resolvedSignatures = [];
|
|
42206
|
+
if (kind === "interface") {
|
|
42207
|
+
const answer = storedContractAnswer(result);
|
|
42208
|
+
result = answer.spec;
|
|
42209
|
+
resolvedSignatures = answer.resolvedSignatures;
|
|
42210
|
+
}
|
|
41374
42211
|
if (methods !== void 0) {
|
|
41375
42212
|
const declared = result.methods;
|
|
41376
42213
|
if (!Array.isArray(declared)) {
|
|
@@ -41392,19 +42229,23 @@ ${renderChangeReport(report2)}`,
|
|
|
41392
42229
|
warning: `PARTIAL: ${names.length - kept.length} of this spec's ${names.length} methods are not in this answer. Never re-author from it \u2014 sdd_define_interface and sdd_write_narrative REPLACE the method list, so every method missing here would be removed from the spec.`
|
|
41393
42230
|
};
|
|
41394
42231
|
result = { ...result, methods: kept };
|
|
42232
|
+
resolvedSignatures = resolvedSignatures.filter((r) => methods.includes(r.method));
|
|
41395
42233
|
}
|
|
41396
42234
|
const guidance = kind === "component" ? resolveComponentVariantGuidance(result) : null;
|
|
42235
|
+
const resolved = resolvedSignatures.length > 0 ? { resolvedSignatures } : {};
|
|
41397
42236
|
const folded = {
|
|
41398
42237
|
...result,
|
|
41399
42238
|
...partial2 ? { partialResult: partial2 } : {},
|
|
41400
|
-
...guidance ? { variantGuidance: guidance } : {}
|
|
42239
|
+
...guidance ? { variantGuidance: guidance } : {},
|
|
42240
|
+
...resolved
|
|
41401
42241
|
};
|
|
41402
42242
|
return structured(JSON.stringify(folded, null, 2), {
|
|
41403
42243
|
kind,
|
|
41404
42244
|
id,
|
|
41405
42245
|
spec: result,
|
|
41406
42246
|
...partial2 ? { partialResult: partial2 } : {},
|
|
41407
|
-
...guidance ? { variantGuidance: guidance } : {}
|
|
42247
|
+
...guidance ? { variantGuidance: guidance } : {},
|
|
42248
|
+
...resolved
|
|
41408
42249
|
});
|
|
41409
42250
|
} catch (e) {
|
|
41410
42251
|
return errText(String(e));
|
|
@@ -41663,6 +42504,10 @@ function loadProjectConfig3() {
|
|
|
41663
42504
|
RETIRED_STEREOTYPES,
|
|
41664
42505
|
RegistrySchema,
|
|
41665
42506
|
RequirementItemSchema,
|
|
42507
|
+
ResolvedInterfaceSpecSchema,
|
|
42508
|
+
ResolvedMethodSignatureSchema,
|
|
42509
|
+
ResolvedTypeMethodSchema,
|
|
42510
|
+
ResolvedTypeSpecSchema,
|
|
41666
42511
|
RulesConfigSchema,
|
|
41667
42512
|
SCAN_EXCLUDE_DIRS,
|
|
41668
42513
|
SDD_RULES,
|
|
@@ -41674,6 +42519,8 @@ function loadProjectConfig3() {
|
|
|
41674
42519
|
SURFACE_AUDIENCES,
|
|
41675
42520
|
SpecIdSchema,
|
|
41676
42521
|
SpecStatusSchema,
|
|
42522
|
+
StoredMethodSignatureSchema,
|
|
42523
|
+
StoredTypeMethodSchema,
|
|
41677
42524
|
SubsystemSpecSchema,
|
|
41678
42525
|
SurfaceAudienceSchema,
|
|
41679
42526
|
SurfaceContractEntrySchema,
|
|
@@ -41758,6 +42605,8 @@ function loadProjectConfig3() {
|
|
|
41758
42605
|
dependencyCycles,
|
|
41759
42606
|
deregisterPackRef,
|
|
41760
42607
|
deriveExecutionProfile,
|
|
42608
|
+
deriveMethodSignature,
|
|
42609
|
+
deriveTypeSignature,
|
|
41761
42610
|
derivedDocPaths,
|
|
41762
42611
|
describeProject,
|
|
41763
42612
|
detectDomainCandidates,
|
|
@@ -41917,6 +42766,7 @@ function loadProjectConfig3() {
|
|
|
41917
42766
|
renameMethod,
|
|
41918
42767
|
renderDiagram,
|
|
41919
42768
|
repairForeignStepFields,
|
|
42769
|
+
repairSignatures,
|
|
41920
42770
|
repointExternal,
|
|
41921
42771
|
requiredPolicies,
|
|
41922
42772
|
resolveAgentTopology,
|
|
@@ -41928,6 +42778,7 @@ function loadProjectConfig3() {
|
|
|
41928
42778
|
resolveImport,
|
|
41929
42779
|
resolveInstalledPack,
|
|
41930
42780
|
resolveProjectExports,
|
|
42781
|
+
resolveSignatures,
|
|
41931
42782
|
resolveSubprojectForNamespace,
|
|
41932
42783
|
resolveSubsystemExports,
|
|
41933
42784
|
resolveVariantGuidance,
|
|
@@ -41949,11 +42800,15 @@ function loadProjectConfig3() {
|
|
|
41949
42800
|
setProjectRoot,
|
|
41950
42801
|
setProjectType,
|
|
41951
42802
|
settledSpecPaths,
|
|
42803
|
+
signatureFacts,
|
|
42804
|
+
signatureTypeRefs,
|
|
41952
42805
|
specPathsInScope,
|
|
41953
42806
|
splitNamespace,
|
|
41954
42807
|
stepConfigVerdict,
|
|
41955
42808
|
stepFieldsFor,
|
|
41956
42809
|
stepGraph,
|
|
42810
|
+
storedMethodSignature,
|
|
42811
|
+
storedTypeMethod,
|
|
41957
42812
|
syncContextFiles,
|
|
41958
42813
|
technologyName,
|
|
41959
42814
|
technologyTokens,
|