@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/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.95";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -3169,6 +3169,49 @@ function conceptNoun(component) {
|
|
|
3169
3169
|
}
|
|
3170
3170
|
return "";
|
|
3171
3171
|
}
|
|
3172
|
+
function requireStoredSignature(method2, ctx) {
|
|
3173
|
+
if (method2.signatureFrom !== void 0) return;
|
|
3174
|
+
if (method2.returns === void 0) {
|
|
3175
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["returns"], message: "Required: a method without a signatureFrom states its returns" });
|
|
3176
|
+
}
|
|
3177
|
+
if (method2.params === void 0 && method2.signature === void 0) {
|
|
3178
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["signature"], message: "Required: a method without params or a signatureFrom states a prose signature" });
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
function paramText(p) {
|
|
3182
|
+
return `${p.name}${p.optional ? "?" : ""}: ${p.type}`;
|
|
3183
|
+
}
|
|
3184
|
+
function leadingGenericList(name, stored) {
|
|
3185
|
+
if (!stored) return "";
|
|
3186
|
+
const text3 = stored.trimStart();
|
|
3187
|
+
if (!text3.startsWith(name)) return "";
|
|
3188
|
+
let i = name.length;
|
|
3189
|
+
while (text3[i] === " ") i++;
|
|
3190
|
+
if (text3[i] !== "<") return "";
|
|
3191
|
+
let depth = 0;
|
|
3192
|
+
for (let j = i; j < text3.length; j++) {
|
|
3193
|
+
if (text3[j] === "<") depth++;
|
|
3194
|
+
else if (text3[j] === ">") depth--;
|
|
3195
|
+
if (depth === 0) {
|
|
3196
|
+
const rest = text3.slice(j + 1).trimStart();
|
|
3197
|
+
return rest.startsWith("(") ? text3.slice(i, j + 1) : "";
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
return "";
|
|
3201
|
+
}
|
|
3202
|
+
function deriveMethodSignature(method2) {
|
|
3203
|
+
if (!method2.params) return void 0;
|
|
3204
|
+
const generics = leadingGenericList(method2.name, method2.signature);
|
|
3205
|
+
return `${method2.name}${generics}(${method2.params.map(paramText).join(", ")}): ${method2.returns ?? "unknown"}`;
|
|
3206
|
+
}
|
|
3207
|
+
function storedMethodSignature(method2) {
|
|
3208
|
+
if (method2.signatureFrom !== void 0) {
|
|
3209
|
+
const { params: _params, returns: _returns, signature: _signature, ...rest } = method2;
|
|
3210
|
+
return rest;
|
|
3211
|
+
}
|
|
3212
|
+
const derived = deriveMethodSignature(method2);
|
|
3213
|
+
return derived === void 0 ? method2 : { ...method2, signature: derived };
|
|
3214
|
+
}
|
|
3172
3215
|
function passesIntentFloor(text3, methodName) {
|
|
3173
3216
|
if (!text3) return false;
|
|
3174
3217
|
const t = text3.trim();
|
|
@@ -3238,6 +3281,14 @@ function implementationSourceFiles(impl) {
|
|
|
3238
3281
|
for (const method2 of impl.methods ?? []) add2(method2.sourcePath);
|
|
3239
3282
|
return files;
|
|
3240
3283
|
}
|
|
3284
|
+
function deriveTypeSignature(type) {
|
|
3285
|
+
if (type.kind !== "signature") return void 0;
|
|
3286
|
+
return `(${(type.params ?? []).map(paramText).join(", ")}): ${type.returns ?? "unknown"}`;
|
|
3287
|
+
}
|
|
3288
|
+
function storedTypeMethod(method2) {
|
|
3289
|
+
const derived = deriveMethodSignature(method2);
|
|
3290
|
+
return derived === void 0 ? method2 : { ...method2, signature: derived };
|
|
3291
|
+
}
|
|
3241
3292
|
function typeSourceFiles(type) {
|
|
3242
3293
|
const files = [];
|
|
3243
3294
|
const add2 = (file) => {
|
|
@@ -3253,7 +3304,7 @@ function relationHealth(status2) {
|
|
|
3253
3304
|
if (status2.drifted) return "drifted";
|
|
3254
3305
|
return "ok";
|
|
3255
3306
|
}
|
|
3256
|
-
var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, PortalMountSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, TechnologySchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceTypeExportSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, ExternalLockEntrySchema, ExternalsLockSchema, ParentExcerptSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
|
|
3307
|
+
var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, PortalMountSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, StoredMethodSignatureSchema, ResolvedMethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, ResolvedInterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, TechnologySchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, ResolvedTypeMethodSchema, StoredTypeMethodSchema, InvariantSchema, TypeSpecSchema, ResolvedTypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceTypeExportSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, ExternalLockEntrySchema, ExternalsLockSchema, ParentExcerptSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
|
|
3257
3308
|
var init_specs = __esm({
|
|
3258
3309
|
"src/models/specs.ts"() {
|
|
3259
3310
|
"use strict";
|
|
@@ -3651,13 +3702,21 @@ var init_specs = __esm({
|
|
|
3651
3702
|
MethodSignatureSchema = import_zod8.z.object({
|
|
3652
3703
|
name: import_zod8.z.string().regex(/^[a-zA-Z0-9_]+$/, "Method name must be alphanumeric"),
|
|
3653
3704
|
description: import_zod8.z.string(),
|
|
3654
|
-
signature: import_zod8.z.string(),
|
|
3705
|
+
signature: import_zod8.z.string().optional(),
|
|
3655
3706
|
// e.g. "save(key: string, data: Buffer): Promise<void>"
|
|
3656
3707
|
// e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
|
|
3657
3708
|
// any real tree. See the grammar on src/models/type-references.ts.
|
|
3658
|
-
returns: import_zod8.z.string(),
|
|
3709
|
+
returns: import_zod8.z.string().optional(),
|
|
3659
3710
|
/** Structured parameters (authoritative for type checking when present). */
|
|
3660
3711
|
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
3712
|
+
/**
|
|
3713
|
+
* Where this method takes its params and returns from instead of stating
|
|
3714
|
+
* them: a signature type (`billing.change_listener`, `alias::name`), or a
|
|
3715
|
+
* contract method `component.method` its component reaches along a
|
|
3716
|
+
* dependsOn/owns edge. Resolved both ways by the loader (signature_resolver);
|
|
3717
|
+
* the writer stores a sourced method with only its source.
|
|
3718
|
+
*/
|
|
3719
|
+
signatureFrom: import_zod8.z.string().min(1).optional(),
|
|
3661
3720
|
/** Concrete wire binding for this method when its component is a Portal (set via sdd_set_endpoints). */
|
|
3662
3721
|
endpoint: EndpointSchema.optional(),
|
|
3663
3722
|
/**
|
|
@@ -3710,6 +3769,11 @@ var init_specs = __esm({
|
|
|
3710
3769
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
3711
3770
|
ext: ExtDataSchema.optional()
|
|
3712
3771
|
});
|
|
3772
|
+
StoredMethodSignatureSchema = MethodSignatureSchema.superRefine(requireStoredSignature);
|
|
3773
|
+
ResolvedMethodSignatureSchema = MethodSignatureSchema.extend({
|
|
3774
|
+
signature: import_zod8.z.string(),
|
|
3775
|
+
returns: import_zod8.z.string()
|
|
3776
|
+
});
|
|
3713
3777
|
INTENT_FLOOR_MIN_CHARS = 40;
|
|
3714
3778
|
InterfaceSpecSchema = import_zod8.z.object({
|
|
3715
3779
|
id: SpecIdSchema.regex(/^i[a-z0-9-_]+$/, 'Interface id must be prefixed with a lowercase "i"'),
|
|
@@ -3717,7 +3781,7 @@ var init_specs = __esm({
|
|
|
3717
3781
|
description: import_zod8.z.string(),
|
|
3718
3782
|
component: import_zod8.z.string(),
|
|
3719
3783
|
// References L2 Component id
|
|
3720
|
-
methods: import_zod8.z.array(
|
|
3784
|
+
methods: import_zod8.z.array(StoredMethodSignatureSchema).default([]),
|
|
3721
3785
|
/** Per-spec lint suppressions (see LintConfigSchema). */
|
|
3722
3786
|
lint: LintConfigSchema.optional(),
|
|
3723
3787
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
@@ -3726,6 +3790,9 @@ var init_specs = __esm({
|
|
|
3726
3790
|
createdAt: import_zod8.z.string().datetime(),
|
|
3727
3791
|
updatedAt: import_zod8.z.string().datetime()
|
|
3728
3792
|
});
|
|
3793
|
+
ResolvedInterfaceSpecSchema = InterfaceSpecSchema.extend({
|
|
3794
|
+
methods: import_zod8.z.array(ResolvedMethodSignatureSchema).default([])
|
|
3795
|
+
});
|
|
3729
3796
|
NarrativeStepTypeSchema = import_zod8.z.enum([
|
|
3730
3797
|
"local",
|
|
3731
3798
|
// in-component work
|
|
@@ -4002,7 +4069,7 @@ var init_specs = __esm({
|
|
|
4002
4069
|
createdAt: import_zod8.z.string().datetime(),
|
|
4003
4070
|
updatedAt: import_zod8.z.string().datetime()
|
|
4004
4071
|
});
|
|
4005
|
-
TypeKindSchema = import_zod8.z.enum(["entity", "value-object"]);
|
|
4072
|
+
TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature"]);
|
|
4006
4073
|
TypeFieldSchema = import_zod8.z.object({
|
|
4007
4074
|
name: import_zod8.z.string(),
|
|
4008
4075
|
// A primitive, or another type id (qualified across subsystems, e.g.
|
|
@@ -4026,7 +4093,9 @@ var init_specs = __esm({
|
|
|
4026
4093
|
});
|
|
4027
4094
|
TypeMethodSchema = import_zod8.z.object({
|
|
4028
4095
|
name: import_zod8.z.string(),
|
|
4029
|
-
signature: import_zod8.z.string(),
|
|
4096
|
+
signature: import_zod8.z.string().optional(),
|
|
4097
|
+
/** Structured parameters in the contract method's param shape — authoritative for type checking when present. */
|
|
4098
|
+
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
4030
4099
|
returns: import_zod8.z.string(),
|
|
4031
4100
|
description: import_zod8.z.string().optional(),
|
|
4032
4101
|
/**
|
|
@@ -4045,6 +4114,14 @@ var init_specs = __esm({
|
|
|
4045
4114
|
*/
|
|
4046
4115
|
symbol: import_zod8.z.string().optional()
|
|
4047
4116
|
});
|
|
4117
|
+
ResolvedTypeMethodSchema = TypeMethodSchema.extend({
|
|
4118
|
+
signature: import_zod8.z.string()
|
|
4119
|
+
});
|
|
4120
|
+
StoredTypeMethodSchema = TypeMethodSchema.superRefine((method2, ctx) => {
|
|
4121
|
+
if (method2.params === void 0 && method2.signature === void 0) {
|
|
4122
|
+
ctx.addIssue({ code: import_zod8.z.ZodIssueCode.custom, path: ["signature"], message: "Required: a type method without params states a prose signature" });
|
|
4123
|
+
}
|
|
4124
|
+
});
|
|
4048
4125
|
InvariantSchema = import_zod8.z.object({
|
|
4049
4126
|
/** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
|
|
4050
4127
|
id: SpecIdSchema,
|
|
@@ -4053,7 +4130,7 @@ var init_specs = __esm({
|
|
|
4053
4130
|
});
|
|
4054
4131
|
TypeSpecSchema = import_zod8.z.object({
|
|
4055
4132
|
kind: TypeKindSchema,
|
|
4056
|
-
// discriminator — entity | value-object
|
|
4133
|
+
// discriminator — entity | value-object | signature
|
|
4057
4134
|
id: SpecIdSchema,
|
|
4058
4135
|
name: import_zod8.z.string(),
|
|
4059
4136
|
description: import_zod8.z.string().optional(),
|
|
@@ -4063,7 +4140,7 @@ var init_specs = __esm({
|
|
|
4063
4140
|
group: import_zod8.z.string().optional(),
|
|
4064
4141
|
fields: import_zod8.z.array(TypeFieldSchema).default([]),
|
|
4065
4142
|
/** Pure intrinsic behaviour only — anything needing a collaborator belongs on a component. */
|
|
4066
|
-
methods: import_zod8.z.array(
|
|
4143
|
+
methods: import_zod8.z.array(StoredTypeMethodSchema).default([]),
|
|
4067
4144
|
/**
|
|
4068
4145
|
* Linked Component ID if this system entity is implemented as a class Component
|
|
4069
4146
|
* (e.g., a Store or Registry that owns this entity's lifecycle and methods).
|
|
@@ -4109,9 +4186,19 @@ var init_specs = __esm({
|
|
|
4109
4186
|
lint: LintConfigSchema.optional(),
|
|
4110
4187
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
4111
4188
|
ext: ExtDataSchema.optional(),
|
|
4189
|
+
/**
|
|
4190
|
+
* A signature's parameters, in the contract method's param shape; only on
|
|
4191
|
+
* kind signature (SIGNATURE_TYPE_MEMBERS otherwise).
|
|
4192
|
+
*/
|
|
4193
|
+
params: import_zod8.z.array(MethodParamSchema).optional(),
|
|
4194
|
+
/** A signature's one output type; required on kind signature and only there (SIGNATURE_TYPE_MEMBERS). */
|
|
4195
|
+
returns: import_zod8.z.string().optional(),
|
|
4112
4196
|
createdAt: import_zod8.z.string().datetime(),
|
|
4113
4197
|
updatedAt: import_zod8.z.string().datetime()
|
|
4114
4198
|
});
|
|
4199
|
+
ResolvedTypeSpecSchema = TypeSpecSchema.extend({
|
|
4200
|
+
methods: import_zod8.z.array(ResolvedTypeMethodSchema).default([])
|
|
4201
|
+
});
|
|
4115
4202
|
SurfaceOriginSchema = import_zod8.z.enum(["generated", "exchanged", "authored"]);
|
|
4116
4203
|
SurfaceTypeDefSchema = import_zod8.z.object({
|
|
4117
4204
|
id: import_zod8.z.string(),
|
|
@@ -4122,7 +4209,16 @@ var init_specs = __esm({
|
|
|
4122
4209
|
type: import_zod8.z.string(),
|
|
4123
4210
|
description: import_zod8.z.string().optional(),
|
|
4124
4211
|
optional: import_zod8.z.boolean().optional()
|
|
4125
|
-
})).default([])
|
|
4212
|
+
})).default([]),
|
|
4213
|
+
/** A signature's parameters (name, type, optional, description) in declared order; only on kind signature. */
|
|
4214
|
+
params: import_zod8.z.array(import_zod8.z.object({
|
|
4215
|
+
name: import_zod8.z.string(),
|
|
4216
|
+
type: import_zod8.z.string(),
|
|
4217
|
+
description: import_zod8.z.string().optional(),
|
|
4218
|
+
optional: import_zod8.z.boolean().optional()
|
|
4219
|
+
})).optional(),
|
|
4220
|
+
/** A signature's one output type; only on kind signature. */
|
|
4221
|
+
returns: import_zod8.z.string().optional()
|
|
4126
4222
|
});
|
|
4127
4223
|
SurfaceTypeExportSchema = import_zod8.z.object({
|
|
4128
4224
|
/** The type's public name in the producer's export table. */
|
|
@@ -4141,8 +4237,8 @@ var init_specs = __esm({
|
|
|
4141
4237
|
type: import_zod8.z.string().default("Custom"),
|
|
4142
4238
|
/** Local name of the backing Portal in the producing project. */
|
|
4143
4239
|
component: import_zod8.z.string(),
|
|
4144
|
-
/** Full contract methods (params, returns, guarantees, effect, endpoint). */
|
|
4145
|
-
methods: import_zod8.z.array(
|
|
4240
|
+
/** Full contract methods (params, returns, guarantees, effect, endpoint), resolved: a snapshot names no producer-internal source. */
|
|
4241
|
+
methods: import_zod8.z.array(ResolvedMethodSignatureSchema).default([]),
|
|
4146
4242
|
/** The backing portal's capability dispatch table, when generic-dispatch. */
|
|
4147
4243
|
dispatch: import_zod8.z.array(DispatchBindingSchema).optional(),
|
|
4148
4244
|
details: import_zod8.z.string().default(""),
|
|
@@ -4317,13 +4413,18 @@ function methodTypeRefs(m) {
|
|
|
4317
4413
|
for (const p of m.params) {
|
|
4318
4414
|
refs.push(...extractTypeIdentifiers(p.type));
|
|
4319
4415
|
}
|
|
4320
|
-
refs.push(...extractTypeIdentifiers(m.returns));
|
|
4416
|
+
refs.push(...extractTypeIdentifiers(m.returns ?? ""));
|
|
4321
4417
|
return Array.from(new Set(refs));
|
|
4322
4418
|
}
|
|
4323
|
-
return extractTypesFromSignature(m.signature, m.returns);
|
|
4419
|
+
return extractTypesFromSignature(m.signature ?? "", m.returns ?? "");
|
|
4420
|
+
}
|
|
4421
|
+
function signatureTypeRefs(type) {
|
|
4422
|
+
if (type.kind !== "signature") return [];
|
|
4423
|
+
const refs = [...(type.params ?? []).flatMap((p) => extractTypeIdentifiers(p.type)), ...extractTypeIdentifiers(type.returns ?? "")];
|
|
4424
|
+
return Array.from(new Set(refs));
|
|
4324
4425
|
}
|
|
4325
4426
|
function methodGenericParameters(method2) {
|
|
4326
|
-
return extractGenericTypeVariables(method2.signature);
|
|
4427
|
+
return extractGenericTypeVariables(method2.signature ?? "");
|
|
4327
4428
|
}
|
|
4328
4429
|
function normalizePart(part) {
|
|
4329
4430
|
return part.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -4795,9 +4896,21 @@ function typeShape(snapshot, def) {
|
|
|
4795
4896
|
return {
|
|
4796
4897
|
id: def.id,
|
|
4797
4898
|
kind: def.kind,
|
|
4798
|
-
fields: sortedBy(def.fields, (f) => f.name).map((f) => ({ name: f.name, type: canonicalTypeRef(snapshot, f.type), optional: f.optional === true }))
|
|
4899
|
+
fields: sortedBy(def.fields, (f) => f.name).map((f) => ({ name: f.name, type: canonicalTypeRef(snapshot, f.type), optional: f.optional === true })),
|
|
4900
|
+
// A signature's shape: its params' types and optionality in order (never their names, as a method's), and its returns.
|
|
4901
|
+
...def.kind === "signature" ? {
|
|
4902
|
+
params: (def.params ?? []).map((p) => ({ type: canonicalTypeRef(snapshot, p.type), optional: p.optional === true })),
|
|
4903
|
+
returns: canonicalTypeRef(snapshot, def.returns ?? "unknown")
|
|
4904
|
+
} : {}
|
|
4799
4905
|
};
|
|
4800
4906
|
}
|
|
4907
|
+
function typeDefExprs(def) {
|
|
4908
|
+
return [
|
|
4909
|
+
...def.fields.map((f) => f.type),
|
|
4910
|
+
...(def.params ?? []).map((p) => p.type),
|
|
4911
|
+
...def.returns !== void 0 ? [def.returns] : []
|
|
4912
|
+
];
|
|
4913
|
+
}
|
|
4801
4914
|
function closureShapes(snapshot, exprs) {
|
|
4802
4915
|
const byId = new Map(snapshot.types.map((def) => [def.id, def]));
|
|
4803
4916
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -4806,7 +4919,7 @@ function closureShapes(snapshot, exprs) {
|
|
|
4806
4919
|
const def = byId.get(queue.shift());
|
|
4807
4920
|
if (!def || seen.has(def.id)) continue;
|
|
4808
4921
|
seen.set(def.id, def);
|
|
4809
|
-
for (const
|
|
4922
|
+
for (const expr of typeDefExprs(def)) queue.push(...extractTypeIdentifiers(canonicalTypeRef(snapshot, expr)));
|
|
4810
4923
|
}
|
|
4811
4924
|
return [...seen.keys()].sort().map((id) => typeShape(snapshot, seen.get(id)));
|
|
4812
4925
|
}
|
|
@@ -5779,6 +5892,7 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
5779
5892
|
signature: m.signature,
|
|
5780
5893
|
returns: m.returns,
|
|
5781
5894
|
...m.params && m.params.length ? { params: m.params } : {},
|
|
5895
|
+
...m.signatureFrom ? { signatureFrom: m.signatureFrom } : {},
|
|
5782
5896
|
...m.endpoint ? { endpoint: m.endpoint } : {},
|
|
5783
5897
|
...m.guarantees && m.guarantees.length ? { guarantees: m.guarantees } : {}
|
|
5784
5898
|
}))
|
|
@@ -5844,6 +5958,7 @@ function buildCanvasModel(issues = [], relations) {
|
|
|
5844
5958
|
...f.references ? { references: f.references } : {}
|
|
5845
5959
|
})),
|
|
5846
5960
|
methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, returns: m.returns, ...m.description ? { description: m.description } : {} })),
|
|
5961
|
+
...t.kind === "signature" ? { signature: deriveTypeSignature(t) } : {},
|
|
5847
5962
|
usedBy: usedByFor(t),
|
|
5848
5963
|
...t.componentClass ? { componentClass: t.componentClass } : {},
|
|
5849
5964
|
...t.database ? { database: t.database } : {},
|
|
@@ -7587,15 +7702,17 @@ var MODEL = __MODEL_JSON__;
|
|
|
7587
7702
|
function tableShape(t) {
|
|
7588
7703
|
var fields = visibleFields(t);
|
|
7589
7704
|
var meths = det === 'full' ? t.methods : [];
|
|
7705
|
+
// A signature type is drawn with its derived text in place of a field list.
|
|
7706
|
+
var sig = t.signature && det !== 'names' ? t.signature : '';
|
|
7590
7707
|
var head = t.name + ' \\u00AB' + t.kind + '\\u00BB';
|
|
7591
|
-
var rows = fields.map(function (f) { return rowText(t, f); })
|
|
7708
|
+
var rows = (sig ? [sig] : []).concat(fields.map(function (f) { return rowText(t, f); }))
|
|
7592
7709
|
.concat(meths.map(function (m) { return '\\u0192 ' + m.name + '(): ' + m.returns; }));
|
|
7593
7710
|
var longest = head.length + 4;
|
|
7594
7711
|
rows.forEach(function (r) { if (r.length > longest) longest = r.length; });
|
|
7595
7712
|
var plain = rows.length === 0;
|
|
7596
7713
|
var pw = Math.max(170, head.length * 6.8 + 26);
|
|
7597
7714
|
var W = Math.max(210, Math.min(400, longest * 6.6 + 30));
|
|
7598
|
-
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 };
|
|
7715
|
+
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 };
|
|
7599
7716
|
}
|
|
7600
7717
|
|
|
7601
7718
|
// Emit one type table with its top-left at (ax, ay); returns its size.
|
|
@@ -7619,6 +7736,13 @@ var MODEL = __MODEL_JSON__;
|
|
|
7619
7736
|
position: { x: ax + sh.w / 2, y: ay + TH_H / 2 }, classes: 'typeHead ' + kindCls + (dim ? ' dimmed' : ''), grabbable: false,
|
|
7620
7737
|
});
|
|
7621
7738
|
var ry = ay + TH_H;
|
|
7739
|
+
if (sh.sig) {
|
|
7740
|
+
eles.push({
|
|
7741
|
+
data: { id: 'TS~' + t.id, parent: 'T~' + t.id, label: sh.sig, w: sh.w, h: ROW_H, tw: sh.w - 14 },
|
|
7742
|
+
position: { x: ax + sh.w / 2, y: ry + ROW_H / 2 }, classes: 'typeRow methRow' + (dim ? ' dimmed' : ''), grabbable: false,
|
|
7743
|
+
});
|
|
7744
|
+
ry += ROW_H;
|
|
7745
|
+
}
|
|
7622
7746
|
sh.fields.forEach(function (f) {
|
|
7623
7747
|
var rid = 'TF~' + t.id + '~' + f.name;
|
|
7624
7748
|
rowIds[rid] = 1;
|
|
@@ -9705,7 +9829,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
9705
9829
|
var mi = methodInfo(top.comp, top.method);
|
|
9706
9830
|
var parts = ['<div class="fstep" style="opacity:.7">No step-by-step narrative \\u2014 showing intent / contract:</div>'];
|
|
9707
9831
|
if (mi) {
|
|
9708
|
-
parts.push('<div class="fstep"><code>' + escText(mi.m.signature) + '</code
|
|
9832
|
+
parts.push('<div class="fstep"><code>' + escText(mi.m.signature) + '</code>' + (mi.m.signatureFrom ? ' from <code>' + escText(mi.m.signatureFrom) + '</code>' : '') + '</div>');
|
|
9709
9833
|
parts.push('<div class="fstep">' + escText(mi.m.description) + (mi.m.returns ? ' \\u2014 returns ' + escText(mi.m.returns) : '') + '</div>');
|
|
9710
9834
|
}
|
|
9711
9835
|
if (intent) parts.push('<div class="fstep" style="font-style:italic">' + escText(intent) + '</div>');
|
|
@@ -10046,6 +10170,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
10046
10170
|
: '<span class="chip" style="opacity:.6">no narrative</span>')
|
|
10047
10171
|
+ '</div>'
|
|
10048
10172
|
+ '<code>' + esc(m.signature) + '</code>'
|
|
10173
|
+
+ (m.signatureFrom ? '<div class="mdesc">signature from <code style="display:inline">' + esc(m.signatureFrom) + '</code></div>' : '')
|
|
10049
10174
|
+ '<div class="mdesc">' + esc(m.description) + ' \\u2014 returns ' + typeRefHtml(m.returns) + '</div>'
|
|
10050
10175
|
+ (mIntent && !hasNarr ? '<div class="mdesc" style="font-style:italic">' + esc(mIntent) + '</div>' : '')
|
|
10051
10176
|
+ (m.params ? '<div class="mdesc">params: ' + m.params.map(function (p) { return esc(p.name) + ': ' + typeRefHtml(p.type); }).join(', ') + '</div>' : '')
|
|
@@ -10149,7 +10274,11 @@ var MODEL = __MODEL_JSON__;
|
|
|
10149
10274
|
+ '</div>';
|
|
10150
10275
|
}).join('')
|
|
10151
10276
|
: '<span class="desc">no fields</span>';
|
|
10152
|
-
|
|
10277
|
+
if (ty.signature) {
|
|
10278
|
+
body += section('Signature', 1, '<div class="method"><code>' + esc(ty.signature) + '</code></div>', true);
|
|
10279
|
+
} else {
|
|
10280
|
+
body += section('Fields', ty.fields.length, fieldsInner, true);
|
|
10281
|
+
}
|
|
10153
10282
|
if (ty.usedBy && ty.usedBy.length) {
|
|
10154
10283
|
body += section('Used by methods', ty.usedBy.length, ty.usedBy.map(function (u) {
|
|
10155
10284
|
return chip(u.component + '.' + u.method + '()', 'component', u.component);
|
|
@@ -10533,6 +10662,46 @@ function operationFor(method2, closureIds) {
|
|
|
10533
10662
|
}
|
|
10534
10663
|
return op;
|
|
10535
10664
|
}
|
|
10665
|
+
function signatureComponent(t, closureIds) {
|
|
10666
|
+
const params = t.params ?? [];
|
|
10667
|
+
const returns = t.returns ?? "unknown";
|
|
10668
|
+
const text3 = `(${params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${returns}`;
|
|
10669
|
+
return {
|
|
10670
|
+
title: t.name,
|
|
10671
|
+
description: `A function type ${text3}. A function has no JSON form, so no JSON value of this type can be sent; its params and returns are under ${SIGNATURE_EXTENSION}.`,
|
|
10672
|
+
[SIGNATURE_EXTENSION]: {
|
|
10673
|
+
// Each type twice: as wairon wrote it (what a wairon reader decodes, exactly),
|
|
10674
|
+
// and as a $ref or schema (what an OpenAPI reader can follow).
|
|
10675
|
+
params: params.map((p) => ({
|
|
10676
|
+
name: p.name,
|
|
10677
|
+
type: p.type,
|
|
10678
|
+
schema: schemaFor(p.type, closureIds),
|
|
10679
|
+
...p.optional ? { optional: true } : {},
|
|
10680
|
+
...p.description ? { description: p.description } : {}
|
|
10681
|
+
})),
|
|
10682
|
+
returns: { type: returns, schema: schemaFor(returns, closureIds) }
|
|
10683
|
+
}
|
|
10684
|
+
};
|
|
10685
|
+
}
|
|
10686
|
+
function signatureFromComponent(id, schema) {
|
|
10687
|
+
const raw = schema[SIGNATURE_EXTENSION];
|
|
10688
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
10689
|
+
const ext = raw;
|
|
10690
|
+
const typeOf = (entry) => {
|
|
10691
|
+
if (typeof entry.type === "string" && entry.type.trim()) return entry.type;
|
|
10692
|
+
const schema2 = entry.schema;
|
|
10693
|
+
if (schema2 && typeof schema2 === "object" && Object.keys(schema2).length === 0) return "unknown";
|
|
10694
|
+
return typeRefFromSchema(schema2);
|
|
10695
|
+
};
|
|
10696
|
+
const params = (Array.isArray(ext.params) ? ext.params : []).filter((p) => !!p && typeof p === "object" && typeof p.name === "string").map((p) => ({
|
|
10697
|
+
name: p.name,
|
|
10698
|
+
type: typeOf(p),
|
|
10699
|
+
...p.optional === true ? { optional: true } : {},
|
|
10700
|
+
...typeof p.description === "string" ? { description: p.description } : {}
|
|
10701
|
+
}));
|
|
10702
|
+
const returns = ext.returns && typeof ext.returns === "object" && !Array.isArray(ext.returns) ? typeOf(ext.returns) : "unknown";
|
|
10703
|
+
return { id, name: typeof schema.title === "string" ? schema.title : id, kind: "signature", fields: [], params, returns };
|
|
10704
|
+
}
|
|
10536
10705
|
function securitySchemeObject(auth) {
|
|
10537
10706
|
if (auth.scheme === "none") return null;
|
|
10538
10707
|
const desc = auth.description ? { description: auth.description } : {};
|
|
@@ -10604,7 +10773,7 @@ function renderDoc(snapshot, entries, closureIds, opts = {}) {
|
|
|
10604
10773
|
}
|
|
10605
10774
|
const schemas = {};
|
|
10606
10775
|
for (const t of snapshot.types) {
|
|
10607
|
-
schemas[t.id] = {
|
|
10776
|
+
schemas[t.id] = t.kind === "signature" ? signatureComponent(t, closureIds) : {
|
|
10608
10777
|
type: "object",
|
|
10609
10778
|
title: t.name,
|
|
10610
10779
|
properties: Object.fromEntries(t.fields.map((f) => [f.name, schemaFor(f.type, closureIds)])),
|
|
@@ -10764,6 +10933,11 @@ function fromOpenApi(document, projectName) {
|
|
|
10764
10933
|
const types = [];
|
|
10765
10934
|
const schemas = parsed.components?.schemas ?? {};
|
|
10766
10935
|
for (const [id, schema] of Object.entries(schemas)) {
|
|
10936
|
+
const signature = signatureFromComponent(id, schema);
|
|
10937
|
+
if (signature) {
|
|
10938
|
+
types.push(signature);
|
|
10939
|
+
continue;
|
|
10940
|
+
}
|
|
10767
10941
|
const props = schema.properties ?? {};
|
|
10768
10942
|
const required = new Set(schema.required ?? []);
|
|
10769
10943
|
types.push({
|
|
@@ -10800,7 +10974,7 @@ function fromOpenApi(document, projectName) {
|
|
|
10800
10974
|
types
|
|
10801
10975
|
});
|
|
10802
10976
|
}
|
|
10803
|
-
var yaml2, PRIMITIVES;
|
|
10977
|
+
var yaml2, PRIMITIVES, SIGNATURE_EXTENSION;
|
|
10804
10978
|
var init_openapi = __esm({
|
|
10805
10979
|
"src/core/openapi.ts"() {
|
|
10806
10980
|
"use strict";
|
|
@@ -10824,6 +10998,7 @@ var init_openapi = __esm({
|
|
|
10824
10998
|
unknown: {},
|
|
10825
10999
|
void: {}
|
|
10826
11000
|
};
|
|
11001
|
+
SIGNATURE_EXTENSION = "x-wairon-signature";
|
|
10827
11002
|
}
|
|
10828
11003
|
});
|
|
10829
11004
|
|
|
@@ -10868,21 +11043,30 @@ function computeTypeClosure(entries, types, exported = []) {
|
|
|
10868
11043
|
for (const field of spec.fields) {
|
|
10869
11044
|
for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
|
|
10870
11045
|
}
|
|
11046
|
+
for (const param of spec.params ?? []) {
|
|
11047
|
+
for (const ref of extractTypeIdentifiers(param.type)) enqueueRef(ref);
|
|
11048
|
+
}
|
|
11049
|
+
if (spec.returns) {
|
|
11050
|
+
for (const ref of extractTypeIdentifiers(spec.returns)) enqueueRef(ref);
|
|
11051
|
+
}
|
|
10871
11052
|
}
|
|
10872
11053
|
const usedIds = /* @__PURE__ */ new Set();
|
|
10873
11054
|
return [...included.entries()].map(([qualified, t]) => {
|
|
10874
11055
|
const id = usedIds.has(t.id) ? qualified : t.id;
|
|
10875
11056
|
usedIds.add(id);
|
|
11057
|
+
const describe3 = (v) => ({
|
|
11058
|
+
name: v.name,
|
|
11059
|
+
type: v.type,
|
|
11060
|
+
...v.description ? { description: v.description } : {},
|
|
11061
|
+
...v.optional ? { optional: true } : {}
|
|
11062
|
+
});
|
|
10876
11063
|
return {
|
|
10877
11064
|
id,
|
|
10878
11065
|
name: t.name,
|
|
10879
11066
|
kind: t.kind,
|
|
10880
|
-
fields: t.fields.map(
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
...f.description ? { description: f.description } : {},
|
|
10884
|
-
...f.optional ? { optional: true } : {}
|
|
10885
|
-
}))
|
|
11067
|
+
fields: t.fields.map(describe3),
|
|
11068
|
+
// A signature type travels complete: its params and returns with it.
|
|
11069
|
+
...t.kind === "signature" ? { params: (t.params ?? []).map(describe3), returns: t.returns ?? "unknown" } : {}
|
|
10886
11070
|
};
|
|
10887
11071
|
});
|
|
10888
11072
|
}
|
|
@@ -10895,7 +11079,7 @@ function boundProjectId() {
|
|
|
10895
11079
|
}
|
|
10896
11080
|
}
|
|
10897
11081
|
function contractEntry(entry, comp, interfaces) {
|
|
10898
|
-
const methods = interfaces.filter((i) => i.component === comp.id && (!entry.interface || i.id === entry.interface)).flatMap((i) => i.methods);
|
|
11082
|
+
const methods = interfaces.filter((i) => i.component === comp.id && (!entry.interface || i.id === entry.interface)).flatMap((i) => i.methods).map(({ signatureFrom: _source, ...method2 }) => method2);
|
|
10899
11083
|
return {
|
|
10900
11084
|
id: entry.publicName,
|
|
10901
11085
|
name: entry.name ?? comp.name,
|
|
@@ -10965,7 +11149,12 @@ function canonicalReferences(snapshot) {
|
|
|
10965
11149
|
...m.params ? { params: m.params.map((p) => ({ ...p, type: canon(p.type) })) } : {}
|
|
10966
11150
|
}))
|
|
10967
11151
|
})),
|
|
10968
|
-
types: snapshot.types.map((def) => ({
|
|
11152
|
+
types: snapshot.types.map((def) => ({
|
|
11153
|
+
...def,
|
|
11154
|
+
fields: def.fields.map((f) => ({ ...f, type: canon(f.type) })),
|
|
11155
|
+
...def.params ? { params: def.params.map((p) => ({ ...p, type: canon(p.type) })) } : {},
|
|
11156
|
+
...def.returns !== void 0 ? { returns: canon(def.returns) } : {}
|
|
11157
|
+
}))
|
|
10969
11158
|
};
|
|
10970
11159
|
}
|
|
10971
11160
|
function listSnapshots() {
|
|
@@ -13257,7 +13446,7 @@ function buildImplementationMethods(ctx) {
|
|
|
13257
13446
|
const component = ctx.componentMap.get(contract.component);
|
|
13258
13447
|
if (!component) continue;
|
|
13259
13448
|
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
13260
|
-
const
|
|
13449
|
+
const draftContext3 = ctx.isImplementationDraft(impl);
|
|
13261
13450
|
for (const method2 of impl.methods) {
|
|
13262
13451
|
const sourceFile = methodSourceFile(method2, impl.sourcePath);
|
|
13263
13452
|
out.push({
|
|
@@ -13265,7 +13454,7 @@ function buildImplementationMethods(ctx) {
|
|
|
13265
13454
|
method: method2,
|
|
13266
13455
|
component,
|
|
13267
13456
|
...sourceFile !== void 0 ? { sourceFile } : {},
|
|
13268
|
-
draftContext
|
|
13457
|
+
draftContext: draftContext3
|
|
13269
13458
|
});
|
|
13270
13459
|
}
|
|
13271
13460
|
}
|
|
@@ -13628,7 +13817,7 @@ var init_signature_type_references = __esm({
|
|
|
13628
13817
|
signatureTypeReferencesRule = {
|
|
13629
13818
|
name: "signature-type-references",
|
|
13630
13819
|
judges: "design",
|
|
13631
|
-
description: "Every type identifier
|
|
13820
|
+
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.",
|
|
13632
13821
|
codes: [
|
|
13633
13822
|
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
13634
13823
|
],
|
|
@@ -13639,6 +13828,7 @@ var init_signature_type_references = __esm({
|
|
|
13639
13828
|
Array.from(interfaceGenericParameters(intf)).map((g) => g.toLowerCase())
|
|
13640
13829
|
);
|
|
13641
13830
|
for (const m of intf.methods) {
|
|
13831
|
+
if (m.signatureFrom !== void 0) continue;
|
|
13642
13832
|
const methodGenerics = new Set(
|
|
13643
13833
|
Array.from(methodGenericParameters(m)).map((g) => g.toLowerCase())
|
|
13644
13834
|
);
|
|
@@ -13658,6 +13848,225 @@ var init_signature_type_references = __esm({
|
|
|
13658
13848
|
}
|
|
13659
13849
|
}
|
|
13660
13850
|
}
|
|
13851
|
+
for (const t of ctx.types) {
|
|
13852
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
13853
|
+
const isDraftCtx = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
13854
|
+
const typeGenerics = new Set(Array.from(typeGenericParameters(t)).map((g) => g.toLowerCase()));
|
|
13855
|
+
const named2 = [
|
|
13856
|
+
...t.methods.filter((m) => m.params !== void 0).flatMap((m) => methodTypeRefs(m).map((ref) => ({ ref, by: `Method "${m.name}" on type "${t.id}"` }))),
|
|
13857
|
+
...signatureTypeRefs(t).map((ref) => ({ ref, by: `Signature type "${t.id}"` }))
|
|
13858
|
+
];
|
|
13859
|
+
for (const { ref, by } of named2) {
|
|
13860
|
+
if (ctx.isTypeResolved(ref, typeGenerics)) continue;
|
|
13861
|
+
const hint = ctx.importHint(ref);
|
|
13862
|
+
ctx.addIssue(
|
|
13863
|
+
"error",
|
|
13864
|
+
"UNDEFINED_TYPE_REFERENCE",
|
|
13865
|
+
`${by} references undefined type "${ref}" in signature.${hint ? ` A declared dependency exports it without this project importing it \u2014 add \`${hint}\`.` : ""}`,
|
|
13866
|
+
t.id,
|
|
13867
|
+
isDraftCtx
|
|
13868
|
+
);
|
|
13869
|
+
}
|
|
13870
|
+
}
|
|
13871
|
+
}
|
|
13872
|
+
};
|
|
13873
|
+
}
|
|
13874
|
+
});
|
|
13875
|
+
|
|
13876
|
+
// src/core/rules/integrity/signature-sources.ts
|
|
13877
|
+
function draftContext(ctx, interfaceId, component) {
|
|
13878
|
+
const intf = ctx.interfaceMap.get(interfaceId);
|
|
13879
|
+
return ctx.isComponentDraft(component) || intf?.status === "draft" || intf?.status === "design";
|
|
13880
|
+
}
|
|
13881
|
+
function targetComponent(target) {
|
|
13882
|
+
return target.slice(0, target.lastIndexOf("."));
|
|
13883
|
+
}
|
|
13884
|
+
var signatureSourcesRule;
|
|
13885
|
+
var init_signature_sources = __esm({
|
|
13886
|
+
"src/core/rules/integrity/signature-sources.ts"() {
|
|
13887
|
+
"use strict";
|
|
13888
|
+
init_models();
|
|
13889
|
+
signatureSourcesRule = {
|
|
13890
|
+
name: "signature-sources",
|
|
13891
|
+
judges: "design",
|
|
13892
|
+
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.",
|
|
13893
|
+
codes: [
|
|
13894
|
+
{ code: "SIGNATURE_SOURCE_UNRESOLVED", defaultSeverity: "error", summary: "A method's signatureFrom names no contract method and no signature type" },
|
|
13895
|
+
{ code: "SIGNATURE_SOURCE_AMBIGUOUS", defaultSeverity: "error", summary: "A method's signatureFrom resolves both as a contract method and as a signature type; qualify it" },
|
|
13896
|
+
{ code: "SIGNATURE_SOURCE_CHAINED", defaultSeverity: "error", summary: "A method's signature source takes its own signature from a source; sources do not chain" },
|
|
13897
|
+
{ 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" },
|
|
13898
|
+
{ 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" }
|
|
13899
|
+
],
|
|
13900
|
+
check(ctx) {
|
|
13901
|
+
for (const fact of ctx.signatureFacts?.sources ?? []) {
|
|
13902
|
+
if (!ctx.isSpecInScope(fact.interfaceId)) continue;
|
|
13903
|
+
const isDraft = draftContext(ctx, fact.interfaceId, fact.component);
|
|
13904
|
+
const where = `Method "${fact.method}" on interface "${fact.interfaceId}"`;
|
|
13905
|
+
const at = { at: fact.method };
|
|
13906
|
+
switch (fact.outcome) {
|
|
13907
|
+
case "unresolved":
|
|
13908
|
+
ctx.addIssue(
|
|
13909
|
+
"error",
|
|
13910
|
+
"SIGNATURE_SOURCE_UNRESOLVED",
|
|
13911
|
+
`${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.`,
|
|
13912
|
+
fact.interfaceId,
|
|
13913
|
+
isDraft,
|
|
13914
|
+
void 0,
|
|
13915
|
+
at
|
|
13916
|
+
);
|
|
13917
|
+
continue;
|
|
13918
|
+
case "ambiguous":
|
|
13919
|
+
ctx.addIssue(
|
|
13920
|
+
"error",
|
|
13921
|
+
"SIGNATURE_SOURCE_AMBIGUOUS",
|
|
13922
|
+
`${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.`,
|
|
13923
|
+
fact.interfaceId,
|
|
13924
|
+
isDraft,
|
|
13925
|
+
void 0,
|
|
13926
|
+
at
|
|
13927
|
+
);
|
|
13928
|
+
continue;
|
|
13929
|
+
case "chained": {
|
|
13930
|
+
const own = fact.detail ?? "";
|
|
13931
|
+
const direct = ctx.types.some((t) => t.kind === "signature" && (t.id === own || typeMatchesRef(t, own)));
|
|
13932
|
+
ctx.addIssue(
|
|
13933
|
+
"error",
|
|
13934
|
+
"SIGNATURE_SOURCE_CHAINED",
|
|
13935
|
+
`${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."}`,
|
|
13936
|
+
fact.interfaceId,
|
|
13937
|
+
isDraft,
|
|
13938
|
+
void 0,
|
|
13939
|
+
at
|
|
13940
|
+
);
|
|
13941
|
+
continue;
|
|
13942
|
+
}
|
|
13943
|
+
case "restated":
|
|
13944
|
+
if (fact.differs) {
|
|
13945
|
+
ctx.addIssue(
|
|
13946
|
+
"error",
|
|
13947
|
+
"SIGNATURE_SOURCE_RESTATED",
|
|
13948
|
+
`${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.`,
|
|
13949
|
+
fact.interfaceId,
|
|
13950
|
+
isDraft,
|
|
13951
|
+
void 0,
|
|
13952
|
+
at
|
|
13953
|
+
);
|
|
13954
|
+
}
|
|
13955
|
+
continue;
|
|
13956
|
+
default:
|
|
13957
|
+
break;
|
|
13958
|
+
}
|
|
13959
|
+
if (fact.form !== "method" || !fact.target) continue;
|
|
13960
|
+
const source = targetComponent(fact.target);
|
|
13961
|
+
const owner = ctx.componentMap.get(fact.component);
|
|
13962
|
+
const reached = /* @__PURE__ */ new Set([...owner?.dependsOn ?? [], ...owner?.owns ?? []]);
|
|
13963
|
+
if (reached.has(source)) continue;
|
|
13964
|
+
ctx.addIssue(
|
|
13965
|
+
"error",
|
|
13966
|
+
"SIGNATURE_SOURCE_OFF_EDGE",
|
|
13967
|
+
`${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.`,
|
|
13968
|
+
fact.interfaceId,
|
|
13969
|
+
isDraft,
|
|
13970
|
+
void 0,
|
|
13971
|
+
at
|
|
13972
|
+
);
|
|
13973
|
+
}
|
|
13974
|
+
}
|
|
13975
|
+
};
|
|
13976
|
+
}
|
|
13977
|
+
});
|
|
13978
|
+
|
|
13979
|
+
// src/core/rules/integrity/signature-types.ts
|
|
13980
|
+
var signatureTypesRule;
|
|
13981
|
+
var init_signature_types = __esm({
|
|
13982
|
+
"src/core/rules/integrity/signature-types.ts"() {
|
|
13983
|
+
"use strict";
|
|
13984
|
+
signatureTypesRule = {
|
|
13985
|
+
name: "signature-types",
|
|
13986
|
+
judges: "design",
|
|
13987
|
+
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.",
|
|
13988
|
+
codes: [
|
|
13989
|
+
{ 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" }
|
|
13990
|
+
],
|
|
13991
|
+
check(ctx) {
|
|
13992
|
+
for (const t of ctx.types) {
|
|
13993
|
+
if (!ctx.isSpecInScope(t.id)) continue;
|
|
13994
|
+
const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
|
|
13995
|
+
const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
|
|
13996
|
+
if (t.kind === "signature") {
|
|
13997
|
+
const members = [
|
|
13998
|
+
...t.fields.length > 0 ? ["fields"] : [],
|
|
13999
|
+
...t.methods.length > 0 ? ["methods"] : [],
|
|
14000
|
+
...(t.invariants?.length ?? 0) > 0 ? ["invariants"] : [],
|
|
14001
|
+
...t.componentClass !== void 0 ? ["componentClass"] : [],
|
|
14002
|
+
...t.database !== void 0 ? ["database"] : [],
|
|
14003
|
+
...t.table !== void 0 ? ["table"] : [],
|
|
14004
|
+
...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
|
|
14005
|
+
];
|
|
14006
|
+
const missingReturns = t.returns === void 0;
|
|
14007
|
+
if (members.length === 0 && !missingReturns) continue;
|
|
14008
|
+
const problems = [
|
|
14009
|
+
...members.length > 0 ? [`carries ${members.join(", ")}`] : [],
|
|
14010
|
+
...missingReturns ? ["states no returns"] : []
|
|
14011
|
+
];
|
|
14012
|
+
ctx.addIssue(
|
|
14013
|
+
"error",
|
|
14014
|
+
"SIGNATURE_TYPE_MEMBERS",
|
|
14015
|
+
`Signature type "${t.id}" ${problems.join(" and ")}. A signature is a named function type: params and one returns, nothing else.`,
|
|
14016
|
+
t.id,
|
|
14017
|
+
isDraft
|
|
14018
|
+
);
|
|
14019
|
+
continue;
|
|
14020
|
+
}
|
|
14021
|
+
const stated = [...t.params !== void 0 ? ["params"] : [], ...t.returns !== void 0 ? ["returns"] : []];
|
|
14022
|
+
if (stated.length === 0) continue;
|
|
14023
|
+
ctx.addIssue(
|
|
14024
|
+
"error",
|
|
14025
|
+
"SIGNATURE_TYPE_MEMBERS",
|
|
14026
|
+
`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.`,
|
|
14027
|
+
t.id,
|
|
14028
|
+
isDraft
|
|
14029
|
+
);
|
|
14030
|
+
}
|
|
14031
|
+
}
|
|
14032
|
+
};
|
|
14033
|
+
}
|
|
14034
|
+
});
|
|
14035
|
+
|
|
14036
|
+
// src/core/rules/integrity/signature-text.ts
|
|
14037
|
+
function draftContext2(ctx, specId, kind) {
|
|
14038
|
+
if (kind === "interface") {
|
|
14039
|
+
const intf = ctx.interfaceMap.get(specId);
|
|
14040
|
+
return !!intf && (ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design");
|
|
14041
|
+
}
|
|
14042
|
+
const type = ctx.types.find((t) => t.id === specId);
|
|
14043
|
+
const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
|
|
14044
|
+
return !!sub && (sub.status === "draft" || sub.status === "design");
|
|
14045
|
+
}
|
|
14046
|
+
var signatureTextRule;
|
|
14047
|
+
var init_signature_text = __esm({
|
|
14048
|
+
"src/core/rules/integrity/signature-text.ts"() {
|
|
14049
|
+
"use strict";
|
|
14050
|
+
signatureTextRule = {
|
|
14051
|
+
name: "signature-text",
|
|
14052
|
+
judges: "design",
|
|
14053
|
+
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.",
|
|
14054
|
+
codes: [
|
|
14055
|
+
{ code: "SIGNATURE_TEXT_STALE", defaultSeverity: "warning", summary: "A stored signature text differs from the text its params derive; doctor --fix regenerates it" }
|
|
14056
|
+
],
|
|
14057
|
+
check(ctx) {
|
|
14058
|
+
for (const stale of ctx.signatureFacts?.staleTexts ?? []) {
|
|
14059
|
+
if (!ctx.isSpecInScope(stale.specId)) continue;
|
|
14060
|
+
ctx.addIssue(
|
|
14061
|
+
"warning",
|
|
14062
|
+
"SIGNATURE_TEXT_STALE",
|
|
14063
|
+
`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).`,
|
|
14064
|
+
stale.specId,
|
|
14065
|
+
draftContext2(ctx, stale.specId, stale.kind),
|
|
14066
|
+
void 0,
|
|
14067
|
+
{ at: stale.method }
|
|
14068
|
+
);
|
|
14069
|
+
}
|
|
13661
14070
|
}
|
|
13662
14071
|
};
|
|
13663
14072
|
}
|
|
@@ -15252,11 +15661,11 @@ var init_detail_sufficiency = __esm({
|
|
|
15252
15661
|
const contract = ctx.interfaceMap.get(impl.contract);
|
|
15253
15662
|
if (!contract) continue;
|
|
15254
15663
|
const component = ctx.componentMap.get(contract.component);
|
|
15255
|
-
const
|
|
15664
|
+
const draftContext3 = ctx.isImplementationDraft(impl);
|
|
15256
15665
|
for (const method2 of impl.methods) {
|
|
15257
15666
|
const detail = effectiveDetail(method2, impl, component);
|
|
15258
15667
|
if (contract.methods.some((m) => m.name === method2.name) && method2.narrative.length === 0 && detail.level !== "full") {
|
|
15259
|
-
dialedDown.push({ implementation: impl, method: method2, component, detail, draftContext });
|
|
15668
|
+
dialedDown.push({ implementation: impl, method: method2, component, detail, draftContext: draftContext3 });
|
|
15260
15669
|
}
|
|
15261
15670
|
}
|
|
15262
15671
|
}
|
|
@@ -17481,7 +17890,7 @@ var init_unused_types = __esm({
|
|
|
17481
17890
|
// Stage 8: its verdict needs the whole system's specs, so a part judged alone skips it.
|
|
17482
17891
|
needsWholeTree: true,
|
|
17483
17892
|
judges: "design",
|
|
17484
|
-
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.",
|
|
17893
|
+
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.",
|
|
17485
17894
|
codes: [
|
|
17486
17895
|
{ code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields, signatures or type methods" }
|
|
17487
17896
|
],
|
|
@@ -17503,6 +17912,7 @@ var init_unused_types = __esm({
|
|
|
17503
17912
|
markTypeReferenced(ref);
|
|
17504
17913
|
}
|
|
17505
17914
|
}
|
|
17915
|
+
for (const ref of signatureTypeRefs(t)) markTypeReferenced(ref, t.id);
|
|
17506
17916
|
}
|
|
17507
17917
|
for (const intf of ctx.interfaces) {
|
|
17508
17918
|
for (const m of intf.methods) {
|
|
@@ -17512,6 +17922,13 @@ var init_unused_types = __esm({
|
|
|
17512
17922
|
}
|
|
17513
17923
|
}
|
|
17514
17924
|
}
|
|
17925
|
+
for (const fact of ctx.signatureFacts?.sources ?? []) {
|
|
17926
|
+
const named2 = fact.form === "signature" && fact.target ? fact.target : fact.outcome === "ambiguous" ? fact.candidates?.[1] : void 0;
|
|
17927
|
+
if (!named2) continue;
|
|
17928
|
+
for (const spec of ctx.types) {
|
|
17929
|
+
if (spec.id === named2 || typeMatchesRef(spec, named2)) referencedTypes.add(spec.id);
|
|
17930
|
+
}
|
|
17931
|
+
}
|
|
17515
17932
|
for (const t of ctx.types) {
|
|
17516
17933
|
for (const m of t.methods) {
|
|
17517
17934
|
const refs = methodTypeRefs(m);
|
|
@@ -19014,7 +19431,7 @@ var init_type_shape = __esm({
|
|
|
19014
19431
|
typeShapeRule = {
|
|
19015
19432
|
name: "type-shape",
|
|
19016
19433
|
judges: "code",
|
|
19017
|
-
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.",
|
|
19434
|
+
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.",
|
|
19018
19435
|
codes: [
|
|
19019
19436
|
{
|
|
19020
19437
|
code: "UNREALIZED_TYPE_FIELD",
|
|
@@ -19198,7 +19615,7 @@ var init_param_conformance = __esm({
|
|
|
19198
19615
|
const stated = normalize3(declared.type);
|
|
19199
19616
|
return stated === realized.type || codeNameOf.get(stated) === realized.type;
|
|
19200
19617
|
};
|
|
19201
|
-
for (const { implementation, method: method2, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
19618
|
+
for (const { implementation, method: method2, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
|
|
19202
19619
|
const contract = ctx.interfaceMap.get(implementation.contract);
|
|
19203
19620
|
const declared = contract?.methods.find((m) => m.name === method2.name)?.params ?? [];
|
|
19204
19621
|
if (declared.length === 0 || !sourceFile) continue;
|
|
@@ -19221,7 +19638,7 @@ var init_param_conformance = __esm({
|
|
|
19221
19638
|
"UNREALIZED_PARAM",
|
|
19222
19639
|
`Method "${method2.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.`,
|
|
19223
19640
|
implementation.id,
|
|
19224
|
-
|
|
19641
|
+
draftContext3,
|
|
19225
19642
|
void 0,
|
|
19226
19643
|
{ at: method2.name, covers: unrealized.map((found) => found.unit) }
|
|
19227
19644
|
);
|
|
@@ -19233,7 +19650,7 @@ var init_param_conformance = __esm({
|
|
|
19233
19650
|
"UNDECLARED_PARAM",
|
|
19234
19651
|
`${opening} realizing method "${method2.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.`,
|
|
19235
19652
|
implementation.id,
|
|
19236
|
-
|
|
19653
|
+
draftContext3,
|
|
19237
19654
|
void 0,
|
|
19238
19655
|
{ at: method2.name, covers: undeclared.map((found) => found.unit) }
|
|
19239
19656
|
);
|
|
@@ -19245,7 +19662,7 @@ var init_param_conformance = __esm({
|
|
|
19245
19662
|
"PARAM_NAME_MISMATCH",
|
|
19246
19663
|
`Method "${method2.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.`,
|
|
19247
19664
|
implementation.id,
|
|
19248
|
-
|
|
19665
|
+
draftContext3,
|
|
19249
19666
|
void 0,
|
|
19250
19667
|
{ at: method2.name, covers: renamed.map((found) => found.unit) }
|
|
19251
19668
|
);
|
|
@@ -19257,7 +19674,7 @@ var init_param_conformance = __esm({
|
|
|
19257
19674
|
"PARAM_OPTIONALITY",
|
|
19258
19675
|
`Method "${method2.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.`,
|
|
19259
19676
|
implementation.id,
|
|
19260
|
-
|
|
19677
|
+
draftContext3,
|
|
19261
19678
|
void 0,
|
|
19262
19679
|
{ at: method2.name, covers: disagreed.map((found) => found.unit) }
|
|
19263
19680
|
);
|
|
@@ -19341,7 +19758,7 @@ var init_route_coverage = __esm({
|
|
|
19341
19758
|
const routes = code.factsAt(file).functionRoutes;
|
|
19342
19759
|
return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
|
|
19343
19760
|
});
|
|
19344
|
-
const
|
|
19761
|
+
const draftContext3 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
|
|
19345
19762
|
const where = holders.map((file) => `"${file}"`).join(", ");
|
|
19346
19763
|
if (read2.length === 0) {
|
|
19347
19764
|
ctx.addIssue(
|
|
@@ -19349,7 +19766,7 @@ var init_route_coverage = __esm({
|
|
|
19349
19766
|
"UNREADABLE_ROUTER",
|
|
19350
19767
|
`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.`,
|
|
19351
19768
|
anchor,
|
|
19352
|
-
|
|
19769
|
+
draftContext3,
|
|
19353
19770
|
void 0,
|
|
19354
19771
|
{ at: via }
|
|
19355
19772
|
);
|
|
@@ -19374,7 +19791,7 @@ var init_route_coverage = __esm({
|
|
|
19374
19791
|
"UNDECLARED_ROUTE",
|
|
19375
19792
|
`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.`,
|
|
19376
19793
|
anchor,
|
|
19377
|
-
|
|
19794
|
+
draftContext3,
|
|
19378
19795
|
void 0,
|
|
19379
19796
|
{ at: via, covers: undeclared }
|
|
19380
19797
|
);
|
|
@@ -19386,7 +19803,7 @@ var init_route_coverage = __esm({
|
|
|
19386
19803
|
"UNROUTED_ENDPOINT",
|
|
19387
19804
|
`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.`,
|
|
19388
19805
|
anchor,
|
|
19389
|
-
|
|
19806
|
+
draftContext3,
|
|
19390
19807
|
void 0,
|
|
19391
19808
|
{ at: via, covers: unrouted }
|
|
19392
19809
|
);
|
|
@@ -19503,7 +19920,7 @@ var init_export_conformance = __esm({
|
|
|
19503
19920
|
}
|
|
19504
19921
|
}
|
|
19505
19922
|
}
|
|
19506
|
-
for (const { implementation, method: method2, sourceFile, draftContext } of ctx.implementationMethods()) {
|
|
19923
|
+
for (const { implementation, method: method2, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
|
|
19507
19924
|
const handle = method2.exportedVia;
|
|
19508
19925
|
if (!handle || !sourceFile) continue;
|
|
19509
19926
|
const file = pathKey(sourceFile);
|
|
@@ -19518,7 +19935,7 @@ var init_export_conformance = __esm({
|
|
|
19518
19935
|
"UNREALIZED_EXPORT_HANDLE",
|
|
19519
19936
|
`Method "${method2.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.`,
|
|
19520
19937
|
implementation.id,
|
|
19521
|
-
|
|
19938
|
+
draftContext3
|
|
19522
19939
|
);
|
|
19523
19940
|
}
|
|
19524
19941
|
for (const listener of ctx.components) {
|
|
@@ -20886,6 +21303,74 @@ var init_naming_discipline = __esm({
|
|
|
20886
21303
|
}
|
|
20887
21304
|
});
|
|
20888
21305
|
|
|
21306
|
+
// src/core/rules/heuristic/signature-source-suggestions.ts
|
|
21307
|
+
function sameParams(a, b) {
|
|
21308
|
+
if (!b || a.length !== b.length) return false;
|
|
21309
|
+
return a.every((p, i) => p.name === b[i].name && p.type === b[i].type && !!p.optional === !!b[i].optional);
|
|
21310
|
+
}
|
|
21311
|
+
function localName(id) {
|
|
21312
|
+
return id.split("::").pop();
|
|
21313
|
+
}
|
|
21314
|
+
var signatureSourceSuggestionsRule;
|
|
21315
|
+
var init_signature_source_suggestions = __esm({
|
|
21316
|
+
"src/core/rules/heuristic/signature-source-suggestions.ts"() {
|
|
21317
|
+
"use strict";
|
|
21318
|
+
signatureSourceSuggestionsRule = {
|
|
21319
|
+
name: "signature-source-suggestions",
|
|
21320
|
+
judges: "design",
|
|
21321
|
+
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.",
|
|
21322
|
+
codes: [
|
|
21323
|
+
{ 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" }
|
|
21324
|
+
],
|
|
21325
|
+
check(ctx) {
|
|
21326
|
+
for (const impl of ctx.implementations) {
|
|
21327
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
21328
|
+
if (!contract || !ctx.isSpecInScope(contract.id)) continue;
|
|
21329
|
+
const repository = ctx.componentMap.get(contract.component);
|
|
21330
|
+
if (!repository || repository.componentType !== "Repository") continue;
|
|
21331
|
+
const owned = new Set(repository.owns);
|
|
21332
|
+
const restating = [];
|
|
21333
|
+
for (const realized of impl.methods) {
|
|
21334
|
+
const steps = realized.narrative ?? [];
|
|
21335
|
+
let member;
|
|
21336
|
+
let memberMethod;
|
|
21337
|
+
if (steps.length === 1 && steps[0].type === "call") {
|
|
21338
|
+
member = steps[0].targetComponent;
|
|
21339
|
+
memberMethod = steps[0].targetMethod;
|
|
21340
|
+
} else if (steps.length === 0 && realized.calls?.length === 1) {
|
|
21341
|
+
const entry = realized.calls[0];
|
|
21342
|
+
const dot = entry.lastIndexOf(".");
|
|
21343
|
+
if (dot > 0) {
|
|
21344
|
+
member = entry.slice(0, dot);
|
|
21345
|
+
memberMethod = entry.slice(dot + 1);
|
|
21346
|
+
}
|
|
21347
|
+
}
|
|
21348
|
+
if (!member || !memberMethod) continue;
|
|
21349
|
+
const memberKey = [...owned].find((o) => o === member || localName(o) === localName(member));
|
|
21350
|
+
if (!memberKey) continue;
|
|
21351
|
+
const own = contract.methods.find((m) => m.name === realized.name);
|
|
21352
|
+
if (!own || own.signatureFrom !== void 0 || !own.params) continue;
|
|
21353
|
+
const target = (ctx.interfacesByComponent.get(memberKey) ?? []).flatMap((i) => i.methods).find((m) => m.name === memberMethod);
|
|
21354
|
+
if (!target || target.signatureFrom !== void 0) continue;
|
|
21355
|
+
if (!sameParams(own.params, target.params) || own.returns !== target.returns) continue;
|
|
21356
|
+
restating.push(`${own.name} \u2190 ${localName(memberKey)}.${memberMethod}`);
|
|
21357
|
+
}
|
|
21358
|
+
if (restating.length === 0) continue;
|
|
21359
|
+
ctx.addIssue(
|
|
21360
|
+
"notice",
|
|
21361
|
+
"SIGNATURE_SOURCE_AVAILABLE",
|
|
21362
|
+
`${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.`,
|
|
21363
|
+
contract.id,
|
|
21364
|
+
ctx.isImplementationDraft(impl),
|
|
21365
|
+
void 0,
|
|
21366
|
+
{ at: contract.id, covers: restating }
|
|
21367
|
+
);
|
|
21368
|
+
}
|
|
21369
|
+
}
|
|
21370
|
+
};
|
|
21371
|
+
}
|
|
21372
|
+
});
|
|
21373
|
+
|
|
20889
21374
|
// src/core/rules/repository.ts
|
|
20890
21375
|
function addRule(rule) {
|
|
20891
21376
|
ruleSet.push(rule);
|
|
@@ -20925,6 +21410,9 @@ var init_repository = __esm({
|
|
|
20925
21410
|
init_type_declarations();
|
|
20926
21411
|
init_field_type_references();
|
|
20927
21412
|
init_signature_type_references();
|
|
21413
|
+
init_signature_sources();
|
|
21414
|
+
init_signature_types();
|
|
21415
|
+
init_signature_text();
|
|
20928
21416
|
init_public_surface_binding();
|
|
20929
21417
|
init_public_surface_consumers();
|
|
20930
21418
|
init_public_surface_declared_type();
|
|
@@ -21020,6 +21508,7 @@ var init_repository = __esm({
|
|
|
21020
21508
|
init_narrative_complexity();
|
|
21021
21509
|
init_naming_discipline();
|
|
21022
21510
|
init_method_cohesion();
|
|
21511
|
+
init_signature_source_suggestions();
|
|
21023
21512
|
SDD_RULES = [
|
|
21024
21513
|
hierarchyRule,
|
|
21025
21514
|
// Namespace integrity right after hierarchy: unresolvable/unwritable ids
|
|
@@ -21038,6 +21527,9 @@ var init_repository = __esm({
|
|
|
21038
21527
|
typeDeclarationsRule,
|
|
21039
21528
|
fieldTypeReferencesRule,
|
|
21040
21529
|
signatureTypeReferencesRule,
|
|
21530
|
+
signatureSourcesRule,
|
|
21531
|
+
signatureTypesRule,
|
|
21532
|
+
signatureTextRule,
|
|
21041
21533
|
// Contracts and the targets narratives name, in four questions with one
|
|
21042
21534
|
// owner each: does the implementation mirror its contract, does a target
|
|
21043
21535
|
// inside this tree resolve, does a target that leaves it pin to exactly one
|
|
@@ -21223,6 +21715,7 @@ var init_repository = __esm({
|
|
|
21223
21715
|
narrativeComplexityRule,
|
|
21224
21716
|
namingDisciplineRule,
|
|
21225
21717
|
methodCohesionRule,
|
|
21718
|
+
signatureSourceSuggestionsRule,
|
|
21226
21719
|
// Pack resolution and reproducibility run late: they are about project
|
|
21227
21720
|
// CONFIGURATION (does the declared pack set resolve, and can it be reproduced
|
|
21228
21721
|
// elsewhere?) rather than spec content.
|
|
@@ -21438,24 +21931,6 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
21438
21931
|
if (!argument || !ts.isTypeQueryNode(argument) || !ts.isIdentifier(argument.exprName)) return void 0;
|
|
21439
21932
|
return argument.exprName.text;
|
|
21440
21933
|
};
|
|
21441
|
-
const baseObjectLiteral = (expression) => {
|
|
21442
|
-
let node = expression;
|
|
21443
|
-
const walked = /* @__PURE__ */ new Set();
|
|
21444
|
-
while (node && !walked.has(node)) {
|
|
21445
|
-
walked.add(node);
|
|
21446
|
-
if (ts.isCallExpression(node)) {
|
|
21447
|
-
const callee = node.expression;
|
|
21448
|
-
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
|
|
21449
|
-
const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
21450
|
-
if (literal) return literal;
|
|
21451
|
-
}
|
|
21452
|
-
node = callee;
|
|
21453
|
-
} else if (ts.isPropertyAccessExpression(node)) {
|
|
21454
|
-
node = node.expression;
|
|
21455
|
-
} else break;
|
|
21456
|
-
}
|
|
21457
|
-
return void 0;
|
|
21458
|
-
};
|
|
21459
21934
|
const chainedCombinators = (expression) => {
|
|
21460
21935
|
const applied = /* @__PURE__ */ new Set();
|
|
21461
21936
|
let node = expression;
|
|
@@ -21725,13 +22200,48 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
21725
22200
|
ts.forEachChild(node, visit);
|
|
21726
22201
|
};
|
|
21727
22202
|
visit(sf);
|
|
22203
|
+
const composedMembers = (expression, seen) => {
|
|
22204
|
+
const extensions = [];
|
|
22205
|
+
const compose2 = (members) => {
|
|
22206
|
+
const byName = new Map(members.map((member) => [member.name, member]));
|
|
22207
|
+
for (const extension of [...extensions].reverse()) {
|
|
22208
|
+
for (const member of schemaMembers(extension)) byName.set(member.name, member);
|
|
22209
|
+
}
|
|
22210
|
+
return [...byName.values()];
|
|
22211
|
+
};
|
|
22212
|
+
let node = expression;
|
|
22213
|
+
const walked = /* @__PURE__ */ new Set();
|
|
22214
|
+
while (node && !walked.has(node)) {
|
|
22215
|
+
walked.add(node);
|
|
22216
|
+
if (ts.isCallExpression(node)) {
|
|
22217
|
+
const callee = node.expression;
|
|
22218
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
|
|
22219
|
+
const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
22220
|
+
if (literal) return compose2(schemaMembers(literal));
|
|
22221
|
+
} else if (ts.isPropertyAccessExpression(callee) && callee.name.text === "extend") {
|
|
22222
|
+
const extension = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
|
|
22223
|
+
if (!extension) return void 0;
|
|
22224
|
+
extensions.push(extension);
|
|
22225
|
+
}
|
|
22226
|
+
node = callee;
|
|
22227
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
22228
|
+
node = node.expression;
|
|
22229
|
+
} else if (ts.isIdentifier(node)) {
|
|
22230
|
+
const base = schemaConstants.get(node.text);
|
|
22231
|
+
if (!base || seen.has(node.text)) return void 0;
|
|
22232
|
+
const members = composedMembers(base, /* @__PURE__ */ new Set([...seen, node.text]));
|
|
22233
|
+
return members ? compose2(members) : void 0;
|
|
22234
|
+
} else break;
|
|
22235
|
+
}
|
|
22236
|
+
return void 0;
|
|
22237
|
+
};
|
|
21728
22238
|
for (const alias of derivedAliases) {
|
|
21729
22239
|
if (typeShapes.has(alias.name)) continue;
|
|
21730
22240
|
const initializer = schemaConstants.get(alias.constant);
|
|
21731
22241
|
if (!initializer) continue;
|
|
21732
|
-
const
|
|
21733
|
-
if (!
|
|
21734
|
-
typeShapes.set(alias.name, { origin: "derived", fields
|
|
22242
|
+
const fields = composedMembers(initializer, /* @__PURE__ */ new Set([alias.constant]));
|
|
22243
|
+
if (!fields) continue;
|
|
22244
|
+
typeShapes.set(alias.name, { origin: "derived", fields, methods: [] });
|
|
21735
22245
|
}
|
|
21736
22246
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
21737
22247
|
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
|
|
@@ -22810,6 +23320,7 @@ function buildRuleContext(opts) {
|
|
|
22810
23320
|
...opts.exportTables ? { exportTables: opts.exportTables } : {},
|
|
22811
23321
|
...opts.projectFamily ? { projectFamily: opts.projectFamily } : {},
|
|
22812
23322
|
...opts.exportUsages ? { exportUsages: opts.exportUsages } : {},
|
|
23323
|
+
...opts.signatureFacts ? { signatureFacts: opts.signatureFacts } : {},
|
|
22813
23324
|
pinnedExternals,
|
|
22814
23325
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
22815
23326
|
roundTripIssues: opts.roundTripIssues,
|
|
@@ -23405,6 +23916,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
23405
23916
|
const memberTables = family.nodes.filter((n) => n.namespace !== "").map((n) => resolveProjectExports(n.namespace));
|
|
23406
23917
|
const producers = new Set(family.references.filter((r) => r.consumer === "").map((r) => r.producer));
|
|
23407
23918
|
const exportUsages = [...producers].map((producer) => exportUsage("", producer));
|
|
23919
|
+
const signatures = signatureFacts();
|
|
23408
23920
|
const ctx = buildRuleContext({
|
|
23409
23921
|
system,
|
|
23410
23922
|
subsystems,
|
|
@@ -23426,6 +23938,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
|
23426
23938
|
],
|
|
23427
23939
|
projectFamily: family,
|
|
23428
23940
|
exportUsages,
|
|
23941
|
+
signatureFacts: signatures,
|
|
23429
23942
|
pinnedExternals,
|
|
23430
23943
|
// By-name selections only: a legacy path ref pins nothing to check. A dry
|
|
23431
23944
|
// run supplies its candidate's; otherwise the stored ones.
|
|
@@ -23544,12 +24057,26 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
23544
24057
|
if (ownIds.has(key) || context.ids.has(key)) continue;
|
|
23545
24058
|
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));
|
|
23546
24059
|
}
|
|
24060
|
+
const ownSignatures = signatureFacts();
|
|
24061
|
+
const resolved = resolveTree(
|
|
24062
|
+
[...own.interfaces, ...context.interfaces],
|
|
24063
|
+
[...own.components, ...context.components],
|
|
24064
|
+
[...own.types, ...context.types]
|
|
24065
|
+
);
|
|
24066
|
+
const ownInterfaceIds = new Set(own.interfaces.map((i) => i.id));
|
|
24067
|
+
const partSignatures = {
|
|
24068
|
+
sources: [
|
|
24069
|
+
...resolved.facts.sources.filter((f) => ownInterfaceIds.has(f.interfaceId) && f.outcome !== "restated"),
|
|
24070
|
+
...ownSignatures.sources.filter((f) => f.outcome === "restated")
|
|
24071
|
+
],
|
|
24072
|
+
staleTexts: ownSignatures.staleTexts
|
|
24073
|
+
};
|
|
23547
24074
|
const all = {
|
|
23548
24075
|
subsystems: [...own.subsystems, ...context.subsystems],
|
|
23549
24076
|
components: [...own.components, ...context.components],
|
|
23550
|
-
interfaces:
|
|
24077
|
+
interfaces: resolved.interfaces,
|
|
23551
24078
|
implementations: [...own.implementations, ...context.implementations],
|
|
23552
|
-
types:
|
|
24079
|
+
types: resolved.types
|
|
23553
24080
|
};
|
|
23554
24081
|
const system = context.system ?? SystemSpecSchema.parse({ createdAt: EXCERPT_EPOCH, updatedAt: EXCERPT_EPOCH, name: parentId, vision: `The part's parent, "${parentId}" (no L0 pinned).` });
|
|
23555
24082
|
const codeModel = buildCodeModel(own.implementations, own.types, root, [], []);
|
|
@@ -23569,6 +24096,7 @@ function judgePartAlone(config, scopeSubsystem) {
|
|
|
23569
24096
|
exportTables: [],
|
|
23570
24097
|
projectFamily: graph(),
|
|
23571
24098
|
exportUsages: [],
|
|
24099
|
+
signatureFacts: partSignatures,
|
|
23572
24100
|
pinnedExternals: [],
|
|
23573
24101
|
packSelections: packs.filter((p) => typeof p !== "string"),
|
|
23574
24102
|
packRequirements: [],
|
|
@@ -24501,7 +25029,7 @@ var init_project_family2 = __esm({
|
|
|
24501
25029
|
});
|
|
24502
25030
|
|
|
24503
25031
|
// src/core/exports.ts
|
|
24504
|
-
function
|
|
25032
|
+
function localName2(id) {
|
|
24505
25033
|
return id.split("::").pop() ?? id;
|
|
24506
25034
|
}
|
|
24507
25035
|
function ownedBy(subsystemId, declaringSubsystem) {
|
|
@@ -24548,7 +25076,7 @@ function findInSource(world, source, table, request) {
|
|
|
24548
25076
|
if (request.typeDef) {
|
|
24549
25077
|
const type = lookupType(world, request.typeDef, source.id);
|
|
24550
25078
|
if (!type) return {};
|
|
24551
|
-
const item2 = { publicName:
|
|
25079
|
+
const item2 = { publicName: localName2(type.id), kind: "type", source: type.subsystem ?? source.id, typeDef: type.id, via: [] };
|
|
24552
25080
|
return { item: item2, found: table.entries.find((e) => e.kind === "type" && e.typeDef === type.id && e.source === item2.source) };
|
|
24553
25081
|
}
|
|
24554
25082
|
const intf = lookup(world.interfaces, request.interface, source.id);
|
|
@@ -24556,7 +25084,7 @@ function findInSource(world, source, table, request) {
|
|
|
24556
25084
|
if (!comp) return {};
|
|
24557
25085
|
const narrowed2 = intf?.id ?? request.interface;
|
|
24558
25086
|
const item = {
|
|
24559
|
-
publicName:
|
|
25087
|
+
publicName: localName2(narrowed2 ?? comp.id),
|
|
24560
25088
|
kind: "component",
|
|
24561
25089
|
source: comp.subsystem,
|
|
24562
25090
|
component: comp.id,
|
|
@@ -24596,7 +25124,7 @@ function bindOwn(world, sub, pi, problems) {
|
|
|
24596
25124
|
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"}` });
|
|
24597
25125
|
return void 0;
|
|
24598
25126
|
}
|
|
24599
|
-
const name2 = pi.as ??
|
|
25127
|
+
const name2 = pi.as ?? localName2(type.id);
|
|
24600
25128
|
checkPublicName(name2, sub.id, problems);
|
|
24601
25129
|
return { publicName: name2, kind: "type", source: type.subsystem ?? sub.id, typeDef: type.id, via: [] };
|
|
24602
25130
|
}
|
|
@@ -24604,7 +25132,7 @@ function bindOwn(world, sub, pi, problems) {
|
|
|
24604
25132
|
if (!comp || !ownedBy(sub.id, comp.subsystem)) return void 0;
|
|
24605
25133
|
const intf = lookup(world.interfaces, pi.interface, sub.id);
|
|
24606
25134
|
const narrowed2 = intf?.id ?? pi.interface;
|
|
24607
|
-
const name = pi.as ??
|
|
25135
|
+
const name = pi.as ?? localName2(narrowed2 ?? comp.id);
|
|
24608
25136
|
checkPublicName(name, sub.id, problems);
|
|
24609
25137
|
return {
|
|
24610
25138
|
publicName: name,
|
|
@@ -24639,7 +25167,7 @@ function subsystemCandidates(world, sub, tables, group, problems) {
|
|
|
24639
25167
|
source,
|
|
24640
25168
|
sourceTable,
|
|
24641
25169
|
{ component: pi.component, interface: pi.interface, typeDef: pi.typeDef },
|
|
24642
|
-
(item) => pi.as ??
|
|
25170
|
+
(item) => pi.as ?? localName2(pi.interface ?? item.typeDef ?? item.component ?? ""),
|
|
24643
25171
|
group.has(source.id),
|
|
24644
25172
|
problems
|
|
24645
25173
|
);
|
|
@@ -24815,16 +25343,16 @@ function classifySource(world, sourceId, label7, sources) {
|
|
|
24815
25343
|
function bindOwnType(world, owner, e, problems) {
|
|
24816
25344
|
const type = (world.types.get(e.typeDef) ?? []).find((t) => !t.subsystem);
|
|
24817
25345
|
if (!type) {
|
|
24818
|
-
problems.push({ kind: "invalid", owner, publicName: e.as ?? e.id ??
|
|
25346
|
+
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` });
|
|
24819
25347
|
return void 0;
|
|
24820
25348
|
}
|
|
24821
|
-
const name = e.as ?? e.id ??
|
|
25349
|
+
const name = e.as ?? e.id ?? localName2(type.id);
|
|
24822
25350
|
checkPublicName(name, owner, problems);
|
|
24823
25351
|
return { publicName: name, kind: "type", source: owner, typeDef: type.id, via: [] };
|
|
24824
25352
|
}
|
|
24825
25353
|
function bindFromProject(owner, e, source, table, index, problems) {
|
|
24826
25354
|
const named2 = e.component !== void 0 || e.typeDef !== void 0 || e.interface !== void 0;
|
|
24827
|
-
const requested = named2 ?
|
|
25355
|
+
const requested = named2 ? localName2(e.interface ?? e.component ?? e.typeDef) : void 0;
|
|
24828
25356
|
const picked = named2 ? table.entries.filter((x) => x.publicName === requested) : table.entries;
|
|
24829
25357
|
if (named2 && picked.length === 0) {
|
|
24830
25358
|
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` });
|
|
@@ -24861,7 +25389,7 @@ function bindFromSubsystem(world, owner, e, subsystem, subsystemTables, index, p
|
|
|
24861
25389
|
// The default public name is the item's own LOCAL id: a member's L0 is
|
|
24862
25390
|
// read with its ids keyed under its project, and its entry names the
|
|
24863
25391
|
// item as the member wrote it.
|
|
24864
|
-
() => e.as ?? e.id ??
|
|
25392
|
+
() => e.as ?? e.id ?? localName2(e.interface ?? e.component ?? e.typeDef),
|
|
24865
25393
|
false,
|
|
24866
25394
|
problems
|
|
24867
25395
|
);
|
|
@@ -25013,6 +25541,130 @@ var init_exports2 = __esm({
|
|
|
25013
25541
|
}
|
|
25014
25542
|
});
|
|
25015
25543
|
|
|
25544
|
+
// src/core/signature-sources.ts
|
|
25545
|
+
function emptySignatureFacts() {
|
|
25546
|
+
return { sources: [], staleTexts: [] };
|
|
25547
|
+
}
|
|
25548
|
+
function namespaceOf(key) {
|
|
25549
|
+
const at = key.lastIndexOf("::");
|
|
25550
|
+
return at === -1 ? "" : key.slice(0, at);
|
|
25551
|
+
}
|
|
25552
|
+
function unresolvedMethod(method2) {
|
|
25553
|
+
const { params: _params, ...rest } = method2;
|
|
25554
|
+
return { ...rest, returns: "unknown", signature: `${method2.name}(...): unknown` };
|
|
25555
|
+
}
|
|
25556
|
+
function sameParams2(a, b) {
|
|
25557
|
+
const left = a ?? [];
|
|
25558
|
+
const right = b ?? [];
|
|
25559
|
+
return left.length === right.length && left.every((p, i) => p.name === right[i].name && p.type === right[i].type && !!p.optional === !!right[i].optional);
|
|
25560
|
+
}
|
|
25561
|
+
function tablesOf(interfaces, components, types, references) {
|
|
25562
|
+
const methodsOf = /* @__PURE__ */ new Map();
|
|
25563
|
+
for (const intf of interfaces) methodsOf.set(intf.component, [...methodsOf.get(intf.component) ?? [], ...intf.methods]);
|
|
25564
|
+
const boundTypes = /* @__PURE__ */ new Map();
|
|
25565
|
+
for (const ref of references) {
|
|
25566
|
+
if (ref.position !== "type" || ref.binding === "outside" || ref.binding === "unresolved") continue;
|
|
25567
|
+
boundTypes.set(`${ref.specId}|${ref.authored}`, ref.resolved);
|
|
25568
|
+
}
|
|
25569
|
+
return { methodsOf, componentKeys: new Set(components.map((c) => c.id)), types, boundTypes };
|
|
25570
|
+
}
|
|
25571
|
+
function methodReading(tables, intf, value) {
|
|
25572
|
+
const dot = value.lastIndexOf(".");
|
|
25573
|
+
if (dot <= 0 || dot === value.length - 1) return null;
|
|
25574
|
+
const head2 = value.slice(0, dot);
|
|
25575
|
+
const tail = value.slice(dot + 1);
|
|
25576
|
+
const ns = namespaceOf(intf.id);
|
|
25577
|
+
const keys = [head2, ...ns && !head2.startsWith(`${ns}::`) ? [`${ns}::${head2}`] : []];
|
|
25578
|
+
for (const key of keys) {
|
|
25579
|
+
if (!tables.componentKeys.has(key)) continue;
|
|
25580
|
+
const method2 = (tables.methodsOf.get(key) ?? []).find((m) => m.name === tail);
|
|
25581
|
+
if (method2) return { target: `${key}.${tail}`, method: method2 };
|
|
25582
|
+
}
|
|
25583
|
+
return null;
|
|
25584
|
+
}
|
|
25585
|
+
function typeReading(tables, intf, value) {
|
|
25586
|
+
const bound2 = value.includes("::") ? tables.boundTypes.get(`${intf.id}|${value}`) : void 0;
|
|
25587
|
+
const named2 = bound2 !== void 0 ? tables.types.filter((t) => t.id === bound2) : tables.types.filter((t) => t.id === value || typeMatchesRef(t, value));
|
|
25588
|
+
const ns = namespaceOf(intf.id);
|
|
25589
|
+
const local = (t) => namespaceOf(t.id) === ns ? 0 : 1;
|
|
25590
|
+
const ranked = [...named2].sort((a, b) => local(a) - local(b));
|
|
25591
|
+
return { signature: ranked.find((t) => t.kind === "signature"), other: ranked.find((t) => t.kind !== "signature") };
|
|
25592
|
+
}
|
|
25593
|
+
function typeTarget(type) {
|
|
25594
|
+
return type.subsystem && !type.id.includes("::") ? `${type.subsystem}::${type.id}` : type.id;
|
|
25595
|
+
}
|
|
25596
|
+
function restatedFact(base, stored, params, returns) {
|
|
25597
|
+
const statesParams = stored.params !== void 0;
|
|
25598
|
+
const statesReturns = stored.returns !== void 0;
|
|
25599
|
+
if (!statesParams && !statesReturns) return null;
|
|
25600
|
+
const paramsDiffer = statesParams && !sameParams2(stored.params, params);
|
|
25601
|
+
const returnsDiffer = statesReturns && stored.returns !== returns;
|
|
25602
|
+
const differs = paramsDiffer || returnsDiffer;
|
|
25603
|
+
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("; ");
|
|
25604
|
+
return { ...base, outcome: "restated", differs, detail };
|
|
25605
|
+
}
|
|
25606
|
+
function resolveSourced(tables, intf, stored, facts) {
|
|
25607
|
+
const source = stored.signatureFrom;
|
|
25608
|
+
const base = { interfaceId: intf.id, component: intf.component, method: stored.name, source };
|
|
25609
|
+
const hit = methodReading(tables, intf, source);
|
|
25610
|
+
const { signature, other } = typeReading(tables, intf, source);
|
|
25611
|
+
if (hit && signature) {
|
|
25612
|
+
facts.sources.push({ ...base, outcome: "ambiguous", candidates: [hit.target, typeTarget(signature)] });
|
|
25613
|
+
return unresolvedMethod(stored);
|
|
25614
|
+
}
|
|
25615
|
+
if (!hit && !signature) {
|
|
25616
|
+
facts.sources.push({ ...base, outcome: "unresolved", ...other ? { detail: `"${typeTarget(other)}" is a type of kind ${other.kind}, not a signature` } : {} });
|
|
25617
|
+
return unresolvedMethod(stored);
|
|
25618
|
+
}
|
|
25619
|
+
if (hit && hit.method.signatureFrom !== void 0) {
|
|
25620
|
+
facts.sources.push({ ...base, form: "method", target: hit.target, outcome: "chained", detail: hit.method.signatureFrom });
|
|
25621
|
+
return unresolvedMethod(stored);
|
|
25622
|
+
}
|
|
25623
|
+
const form = hit ? "method" : "signature";
|
|
25624
|
+
const target = hit ? hit.target : typeTarget(signature);
|
|
25625
|
+
const params = hit ? hit.method.params : signature.params;
|
|
25626
|
+
const returns = hit ? hit.method.returns : signature.returns ?? "unknown";
|
|
25627
|
+
const restated = restatedFact({ ...base, form, target }, stored, params, returns);
|
|
25628
|
+
if (restated) facts.sources.push(restated);
|
|
25629
|
+
facts.sources.push({ ...base, form, target, outcome: "resolved" });
|
|
25630
|
+
const resolved = { ...stored, returns };
|
|
25631
|
+
if (params) resolved.params = params.map((p) => ({ ...p }));
|
|
25632
|
+
else delete resolved.params;
|
|
25633
|
+
if (!params && hit) resolved.signature = hit.method.signature.replace(new RegExp(`^\\s*${hit.method.name}\\b`), stored.name);
|
|
25634
|
+
return resolved;
|
|
25635
|
+
}
|
|
25636
|
+
function withDerivedText(method2, stored, specId, kind, facts) {
|
|
25637
|
+
const derived = deriveMethodSignature({ ...method2, signature: stored ?? method2.signature });
|
|
25638
|
+
if (derived === void 0) return method2;
|
|
25639
|
+
if (stored !== void 0 && stored !== derived) facts.staleTexts.push({ specId, kind, method: method2.name, stored, derived });
|
|
25640
|
+
return { ...method2, signature: derived };
|
|
25641
|
+
}
|
|
25642
|
+
function resolveTree(interfaces, components, types, references = []) {
|
|
25643
|
+
const intfs = interfaces.map((i) => ({ ...i, methods: i.methods.map((m) => ({ ...m })) }));
|
|
25644
|
+
const typeCopies = types.map((t) => ({ ...t, methods: t.methods.map((m) => ({ ...m })) }));
|
|
25645
|
+
const tables = tablesOf(intfs, [...components], typeCopies, references);
|
|
25646
|
+
const facts = emptySignatureFacts();
|
|
25647
|
+
const sourced = intfs.map((intf) => ({
|
|
25648
|
+
...intf,
|
|
25649
|
+
methods: intf.methods.map((m) => m.signatureFrom !== void 0 ? resolveSourced(tables, intf, m, facts) : m)
|
|
25650
|
+
}));
|
|
25651
|
+
const resolvedInterfaces = sourced.map((intf, i) => ({
|
|
25652
|
+
...intf,
|
|
25653
|
+
methods: intf.methods.map((m, j) => withDerivedText(m, intfs[i].methods[j].signature, intf.id, "interface", facts))
|
|
25654
|
+
}));
|
|
25655
|
+
const resolvedTypes = typeCopies.map((t) => ({
|
|
25656
|
+
...t,
|
|
25657
|
+
methods: t.methods.map((m) => withDerivedText(m, m.signature, t.id, "type", facts))
|
|
25658
|
+
}));
|
|
25659
|
+
return { interfaces: resolvedInterfaces, types: resolvedTypes, facts };
|
|
25660
|
+
}
|
|
25661
|
+
var init_signature_sources2 = __esm({
|
|
25662
|
+
"src/core/signature-sources.ts"() {
|
|
25663
|
+
"use strict";
|
|
25664
|
+
init_models();
|
|
25665
|
+
}
|
|
25666
|
+
});
|
|
25667
|
+
|
|
25016
25668
|
// src/core/specs.ts
|
|
25017
25669
|
function deniedOwners(reach2, owners) {
|
|
25018
25670
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -25067,7 +25719,8 @@ function emptyIndex() {
|
|
|
25067
25719
|
implementation: {},
|
|
25068
25720
|
type: {},
|
|
25069
25721
|
group: {}
|
|
25070
|
-
}
|
|
25722
|
+
},
|
|
25723
|
+
signatures: { sources: [], staleTexts: [] }
|
|
25071
25724
|
};
|
|
25072
25725
|
}
|
|
25073
25726
|
function isWithin(dir, file) {
|
|
@@ -25299,6 +25952,17 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
|
25299
25952
|
walk2(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
|
|
25300
25953
|
return inspection;
|
|
25301
25954
|
}
|
|
25955
|
+
function signatureSourceParts(value) {
|
|
25956
|
+
const dot = value.lastIndexOf(".");
|
|
25957
|
+
if (dot <= 0 || dot === value.length - 1) return null;
|
|
25958
|
+
return { head: value.slice(0, dot), tail: value.slice(dot + 1) };
|
|
25959
|
+
}
|
|
25960
|
+
function mapSignatureSource(value, map) {
|
|
25961
|
+
const parts = signatureSourceParts(value);
|
|
25962
|
+
if (!parts) return value;
|
|
25963
|
+
const head2 = map("signatureFrom", parts.head);
|
|
25964
|
+
return head2 === parts.head ? value : `${head2}.${parts.tail}`;
|
|
25965
|
+
}
|
|
25302
25966
|
function mapSpecReferences(kind, spec, map) {
|
|
25303
25967
|
switch (kind) {
|
|
25304
25968
|
case "subsystem": {
|
|
@@ -25329,7 +25993,15 @@ function mapSpecReferences(kind, spec, map) {
|
|
|
25329
25993
|
}
|
|
25330
25994
|
case "interface": {
|
|
25331
25995
|
const i = spec;
|
|
25332
|
-
return {
|
|
25996
|
+
return {
|
|
25997
|
+
...i,
|
|
25998
|
+
component: map("contract", i.component),
|
|
25999
|
+
// A method source's head is a component reference; a value with no
|
|
26000
|
+
// head is a raw type position the mapper is never asked about.
|
|
26001
|
+
...i.methods?.some((m) => m.signatureFrom !== void 0) ? {
|
|
26002
|
+
methods: i.methods.map((m) => m.signatureFrom !== void 0 ? { ...m, signatureFrom: mapSignatureSource(m.signatureFrom, map) } : m)
|
|
26003
|
+
} : {}
|
|
26004
|
+
};
|
|
25333
26005
|
}
|
|
25334
26006
|
case "implementation": {
|
|
25335
26007
|
const impl = spec;
|
|
@@ -25374,6 +26046,7 @@ function rawReferences(kind, spec) {
|
|
|
25374
26046
|
for (const m of spec.methods) {
|
|
25375
26047
|
qualifiedTypeNames(m.returns).forEach((t) => add2("type", t));
|
|
25376
26048
|
m.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((t) => add2("type", t)));
|
|
26049
|
+
if (m.signatureFrom !== void 0 && !signatureSourceParts(m.signatureFrom)) add2("type", m.signatureFrom);
|
|
25377
26050
|
}
|
|
25378
26051
|
break;
|
|
25379
26052
|
case "implementation":
|
|
@@ -25385,9 +26058,14 @@ function rawReferences(kind, spec) {
|
|
|
25385
26058
|
}
|
|
25386
26059
|
}
|
|
25387
26060
|
break;
|
|
25388
|
-
case "type":
|
|
25389
|
-
|
|
26061
|
+
case "type": {
|
|
26062
|
+
const t = spec;
|
|
26063
|
+
for (const f of t.fields) qualifiedTypeNames(f.type).forEach((n) => add2("type", n));
|
|
26064
|
+
t.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((n) => add2("type", n)));
|
|
26065
|
+
qualifiedTypeNames(t.returns).forEach((n) => add2("type", n));
|
|
26066
|
+
for (const m of t.methods ?? []) m.params?.forEach((p) => qualifiedTypeNames(p.type).forEach((n) => add2("type", n)));
|
|
25390
26067
|
break;
|
|
26068
|
+
}
|
|
25391
26069
|
default:
|
|
25392
26070
|
break;
|
|
25393
26071
|
}
|
|
@@ -25417,6 +26095,8 @@ function bareRawReferences(kind, spec) {
|
|
|
25417
26095
|
case "type": {
|
|
25418
26096
|
const t = spec;
|
|
25419
26097
|
for (const f of t.fields) for (const ref of fieldTypeRefs(t, f.type)) add2("type", ref);
|
|
26098
|
+
for (const ref of signatureTypeRefs(t)) add2("type", ref);
|
|
26099
|
+
for (const m of t.methods ?? []) for (const p of m.params ?? []) for (const ref of fieldTypeRefs(t, p.type)) add2("type", ref);
|
|
25420
26100
|
break;
|
|
25421
26101
|
}
|
|
25422
26102
|
case "implementation":
|
|
@@ -25483,6 +26163,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
25483
26163
|
typed(m, "signature");
|
|
25484
26164
|
typed(m, "returns");
|
|
25485
26165
|
for (const p of list2(m.params)) typed(p, "type");
|
|
26166
|
+
if (typeof m.signatureFrom === "string") {
|
|
26167
|
+
m.signatureFrom = signatureSourceParts(m.signatureFrom) ? mapSignatureSource(m.signatureFrom, map) : mapTypeNames(m.signatureFrom, map);
|
|
26168
|
+
}
|
|
25486
26169
|
}
|
|
25487
26170
|
break;
|
|
25488
26171
|
case "implementation":
|
|
@@ -25508,6 +26191,9 @@ function respellStoredReferences(kind, doc, map) {
|
|
|
25508
26191
|
at(doc, "subsystem", "subsystem");
|
|
25509
26192
|
at(doc, "group", "group");
|
|
25510
26193
|
for (const f of list2(doc.fields)) typed(f, "type");
|
|
26194
|
+
for (const p of list2(doc.params)) typed(p, "type");
|
|
26195
|
+
typed(doc, "returns");
|
|
26196
|
+
for (const m of list2(doc.methods)) for (const p of list2(m.params)) typed(p, "type");
|
|
25511
26197
|
break;
|
|
25512
26198
|
default:
|
|
25513
26199
|
break;
|
|
@@ -25527,7 +26213,9 @@ function mapRawReferences(kind, spec, map) {
|
|
|
25527
26213
|
...m,
|
|
25528
26214
|
...m.signature !== void 0 ? { signature: mapTypeNames(m.signature, map) } : {},
|
|
25529
26215
|
...m.returns !== void 0 ? { returns: mapTypeNames(m.returns, map) } : {},
|
|
25530
|
-
...m.params ? { params: m.params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {}
|
|
26216
|
+
...m.params ? { params: m.params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {},
|
|
26217
|
+
// A type source is a raw type position; a method source's head is bound, never raw.
|
|
26218
|
+
...m.signatureFrom !== void 0 && !signatureSourceParts(m.signatureFrom) ? { signatureFrom: mapTypeNames(m.signatureFrom, map) } : {}
|
|
25531
26219
|
}))
|
|
25532
26220
|
};
|
|
25533
26221
|
}
|
|
@@ -25555,7 +26243,14 @@ function mapRawReferences(kind, spec, map) {
|
|
|
25555
26243
|
}
|
|
25556
26244
|
case "type": {
|
|
25557
26245
|
const t = spec;
|
|
25558
|
-
|
|
26246
|
+
const typedParams = (params) => params ? { params: params.map((p) => ({ ...p, type: mapTypeNames(p.type, map) })) } : {};
|
|
26247
|
+
return {
|
|
26248
|
+
...t,
|
|
26249
|
+
fields: t.fields.map((f) => ({ ...f, type: mapTypeNames(f.type, map) })),
|
|
26250
|
+
...typedParams(t.params),
|
|
26251
|
+
...t.returns !== void 0 ? { returns: mapTypeNames(t.returns, map) } : {},
|
|
26252
|
+
methods: (t.methods ?? []).map((m) => ({ ...m, ...typedParams(m.params) }))
|
|
26253
|
+
};
|
|
25559
26254
|
}
|
|
25560
26255
|
default:
|
|
25561
26256
|
return spec;
|
|
@@ -25892,6 +26587,7 @@ function specKind(raw) {
|
|
|
25892
26587
|
if ("component" in raw && Array.isArray(raw.methods)) return "interface";
|
|
25893
26588
|
if ("contract" in raw && Array.isArray(raw.methods)) return "implementation";
|
|
25894
26589
|
if ("kind" in raw && Array.isArray(raw.fields)) return "type";
|
|
26590
|
+
if (raw.kind === "signature") return "type";
|
|
25895
26591
|
return void 0;
|
|
25896
26592
|
}
|
|
25897
26593
|
function rewriteSpecRefs(raw, remap) {
|
|
@@ -25946,6 +26642,16 @@ function rewriteSpecRefs(raw, remap) {
|
|
|
25946
26642
|
rewrite(raw, "component", "component");
|
|
25947
26643
|
for (const method2 of entries(raw.methods)) {
|
|
25948
26644
|
for (const param of entries(method2?.params)) rewrite(param, "type", "type");
|
|
26645
|
+
const source = method2?.signatureFrom;
|
|
26646
|
+
if (typeof source !== "string") continue;
|
|
26647
|
+
const parts = signatureSourceParts(source);
|
|
26648
|
+
const name = parts ? remap(parts.tail, "method", parts.head) : void 0;
|
|
26649
|
+
const component = parts ? remap(parts.head, "component", parts.tail) : void 0;
|
|
26650
|
+
const next = parts && (component !== parts.head || name !== parts.tail) ? `${component}.${name}` : remap(source, "type");
|
|
26651
|
+
if (next !== source) {
|
|
26652
|
+
method2.signatureFrom = next;
|
|
26653
|
+
changed = true;
|
|
26654
|
+
}
|
|
25949
26655
|
}
|
|
25950
26656
|
} else if (kind === "implementation") {
|
|
25951
26657
|
rewrite(raw, "contract", "interface");
|
|
@@ -25976,6 +26682,11 @@ function rewriteSpecRefs(raw, remap) {
|
|
|
25976
26682
|
} else if (kind === "type") {
|
|
25977
26683
|
rewrite(raw, "componentClass", "entity-class");
|
|
25978
26684
|
for (const field of entries(raw.fields)) rewrite(field, "type", "type");
|
|
26685
|
+
for (const param of entries(raw.params)) rewrite(param, "type", "type");
|
|
26686
|
+
rewrite(raw, "returns", "type");
|
|
26687
|
+
for (const method2 of entries(raw.methods)) {
|
|
26688
|
+
for (const param of entries(method2?.params)) rewrite(param, "type", "type");
|
|
26689
|
+
}
|
|
25979
26690
|
}
|
|
25980
26691
|
return changed;
|
|
25981
26692
|
}
|
|
@@ -26371,6 +27082,26 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
|
|
|
26371
27082
|
walk2(delta, merged, stored, "", "");
|
|
26372
27083
|
return out;
|
|
26373
27084
|
}
|
|
27085
|
+
function storedFormOf(kind, spec) {
|
|
27086
|
+
if (!spec) return spec;
|
|
27087
|
+
if (kind === "interface") {
|
|
27088
|
+
const intf = spec;
|
|
27089
|
+
return { ...intf, methods: intf.methods.map((m) => storedMethodSignature(m)) };
|
|
27090
|
+
}
|
|
27091
|
+
if (kind === "type") {
|
|
27092
|
+
const type = spec;
|
|
27093
|
+
return { ...type, methods: type.methods.map((m) => storedTypeMethod(m)) };
|
|
27094
|
+
}
|
|
27095
|
+
return spec;
|
|
27096
|
+
}
|
|
27097
|
+
function deriveMergedTexts(kind, merged) {
|
|
27098
|
+
if (kind !== "interface" && kind !== "type" || !Array.isArray(merged.methods)) return;
|
|
27099
|
+
merged.methods = merged.methods.map((m) => {
|
|
27100
|
+
if (!m || typeof m !== "object" || m.signatureFrom !== void 0 || !Array.isArray(m.params)) return m;
|
|
27101
|
+
const derived = deriveMethodSignature(m);
|
|
27102
|
+
return derived === void 0 ? m : { ...m, signature: derived };
|
|
27103
|
+
});
|
|
27104
|
+
}
|
|
26374
27105
|
function cloneSpec(spec) {
|
|
26375
27106
|
return JSON.parse(JSON.stringify(spec));
|
|
26376
27107
|
}
|
|
@@ -26728,6 +27459,9 @@ function specPathsInScope(scopeSubsystem) {
|
|
|
26728
27459
|
function snapshotSpecFiles() {
|
|
26729
27460
|
return current().snapshotSpecFiles();
|
|
26730
27461
|
}
|
|
27462
|
+
function signatureFacts() {
|
|
27463
|
+
return current().signatureFacts();
|
|
27464
|
+
}
|
|
26731
27465
|
function findLegacySpecFiles() {
|
|
26732
27466
|
return current().findLegacySpecFiles();
|
|
26733
27467
|
}
|
|
@@ -26793,6 +27527,7 @@ var init_specs2 = __esm({
|
|
|
26793
27527
|
init_project_family();
|
|
26794
27528
|
init_project_config();
|
|
26795
27529
|
init_project_family2();
|
|
27530
|
+
init_signature_sources2();
|
|
26796
27531
|
PartReadOnly = class extends WaironError {
|
|
26797
27532
|
constructor(message) {
|
|
26798
27533
|
super(message);
|
|
@@ -26932,6 +27667,10 @@ var init_specs2 = __esm({
|
|
|
26932
27667
|
index.groups.push(...raw.index.groups);
|
|
26933
27668
|
for (const kind of Object.keys(index.paths)) Object.assign(index.paths[kind], raw.index.paths[kind]);
|
|
26934
27669
|
}
|
|
27670
|
+
const signatures = resolveTree(index.interfaces, index.components, index.types, raws.flatMap((r) => r.record.authoredReferences));
|
|
27671
|
+
index.interfaces = signatures.interfaces;
|
|
27672
|
+
index.types = signatures.types;
|
|
27673
|
+
index.signatures = signatures.facts;
|
|
26935
27674
|
return { index, raws, readables };
|
|
26936
27675
|
}
|
|
26937
27676
|
/**
|
|
@@ -27357,7 +28096,9 @@ var init_specs2 = __esm({
|
|
|
27357
28096
|
rewrite: local
|
|
27358
28097
|
});
|
|
27359
28098
|
};
|
|
28099
|
+
const localComponents = new Set(index.components.map((c) => nameKey(c.id.split("::").pop())));
|
|
27360
28100
|
const bindLocal = (kind, specKey) => (position, value) => {
|
|
28101
|
+
if (position === "signatureFrom" && !value.includes("::") && !localComponents.has(nameKey(value))) return value;
|
|
27361
28102
|
if (value.includes("::")) {
|
|
27362
28103
|
raw.pending.push({ kind, specKey, position, authored: value, raw: false });
|
|
27363
28104
|
return value;
|
|
@@ -27692,7 +28433,8 @@ var init_specs2 = __esm({
|
|
|
27692
28433
|
this.scanAll();
|
|
27693
28434
|
const from = this.getSubprojectPrefix(spec.id) ?? "";
|
|
27694
28435
|
const carrying = carry && !this.carryDisabled ? /* @__PURE__ */ new Map() : null;
|
|
27695
|
-
const
|
|
28436
|
+
const components = new Set(this.scanAll().components.map((c) => c.id));
|
|
28437
|
+
const mapped = mapSpecReferences(kind, spec, (position, value) => position === "signatureFrom" && !components.has(value) ? value : this.writeReference(from, spec.id, position, value, carrying));
|
|
27696
28438
|
return { ...mapped, id: localOf(from, spec.id).split("::").pop() };
|
|
27697
28439
|
}
|
|
27698
28440
|
// -------------------------------------------------------------------------
|
|
@@ -27788,8 +28530,8 @@ var init_specs2 = __esm({
|
|
|
27788
28530
|
if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
|
|
27789
28531
|
return path23.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
|
|
27790
28532
|
}
|
|
27791
|
-
const
|
|
27792
|
-
return path23.join(this.paths.specsDir(), "default",
|
|
28533
|
+
const targetComponent2 = componentId || "default";
|
|
28534
|
+
return path23.join(this.paths.specsDir(), "default", targetComponent2, ".interface.yaml");
|
|
27793
28535
|
}
|
|
27794
28536
|
getImplementationPath(id, contractId) {
|
|
27795
28537
|
const index = this.scanAll();
|
|
@@ -27985,8 +28727,15 @@ var init_specs2 = __esm({
|
|
|
27985
28727
|
prepareComponentForWrite(spec) {
|
|
27986
28728
|
return this.relativizeSpec("component", spec);
|
|
27987
28729
|
}
|
|
28730
|
+
/**
|
|
28731
|
+
* spec_registry.saveInterfaceSpec steps 1-2: every method in its stored form
|
|
28732
|
+
* (method_signature.storedForm — the text a method's params derive, nothing
|
|
28733
|
+
* a signatureFrom supplies), then every reference written back relative to
|
|
28734
|
+
* the owning project.
|
|
28735
|
+
*/
|
|
27988
28736
|
prepareInterfaceForWrite(spec) {
|
|
27989
|
-
|
|
28737
|
+
const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedMethodSignature(m)) };
|
|
28738
|
+
return this.relativizeSpec("interface", stored);
|
|
27990
28739
|
}
|
|
27991
28740
|
prepareImplementationForWrite(spec, carry = true) {
|
|
27992
28741
|
const relative26 = this.relativizeSpec("implementation", spec, carry);
|
|
@@ -27996,8 +28745,10 @@ var init_specs2 = __esm({
|
|
|
27996
28745
|
const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
|
|
27997
28746
|
return partDir ? partRelativeFilePaths(relative26, this.rootDir, partDir) : relative26;
|
|
27998
28747
|
}
|
|
28748
|
+
/** A type written in its stored form — every params-bearing method's text derived — relative to the owning project. */
|
|
27999
28749
|
prepareTypeForWrite(spec) {
|
|
28000
|
-
|
|
28750
|
+
const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedTypeMethod(m)) };
|
|
28751
|
+
return this.relativizeSpec("type", stored);
|
|
28001
28752
|
}
|
|
28002
28753
|
prepareGroupForWrite(spec) {
|
|
28003
28754
|
return this.relativizeSpec("group", spec);
|
|
@@ -28207,7 +28958,7 @@ var init_specs2 = __esm({
|
|
|
28207
28958
|
if (!pathExists(p)) return null;
|
|
28208
28959
|
try {
|
|
28209
28960
|
const raw = readSpecFile(p);
|
|
28210
|
-
return InterfaceSpecSchema.parse(raw);
|
|
28961
|
+
return resolveTree([InterfaceSpecSchema.parse(raw)], index.components, index.types).interfaces[0];
|
|
28211
28962
|
} catch (e) {
|
|
28212
28963
|
this.loaderIssues.push({
|
|
28213
28964
|
severity: "error",
|
|
@@ -28772,6 +29523,17 @@ var init_specs2 = __esm({
|
|
|
28772
29523
|
* dry-run a promotion (write 'complete' → validate → restore) without leaving
|
|
28773
29524
|
* any change behind if validation fails or the user cancels.
|
|
28774
29525
|
*/
|
|
29526
|
+
/**
|
|
29527
|
+
* ispec_index.signatureFacts — what the current scan's signature resolution
|
|
29528
|
+
* recorded: every signatureFrom met and how it resolved, and every stored
|
|
29529
|
+
* text its params contradict. Read from the cached scan, rescanning first
|
|
29530
|
+
* when the tree changed.
|
|
29531
|
+
*/
|
|
29532
|
+
signatureFacts() {
|
|
29533
|
+
const index = this;
|
|
29534
|
+
index.listProjectRoots();
|
|
29535
|
+
return this.cachedIndex.signatures;
|
|
29536
|
+
}
|
|
28775
29537
|
snapshotSpecFiles() {
|
|
28776
29538
|
const index = this.scanAll();
|
|
28777
29539
|
const snapshot = /* @__PURE__ */ new Map();
|
|
@@ -28869,7 +29631,7 @@ var init_specs2 = __esm({
|
|
|
28869
29631
|
*/
|
|
28870
29632
|
updateSpec(kind, id, delta, hooks, dryRun = false) {
|
|
28871
29633
|
const notices = [];
|
|
28872
|
-
const result = this.load(kind, id);
|
|
29634
|
+
const result = storedFormOf(kind, this.load(kind, id));
|
|
28873
29635
|
if (!result) {
|
|
28874
29636
|
throw new Error(`Spec of kind "${kind}" with ID "${id}" does not exist. Define it first.`);
|
|
28875
29637
|
}
|
|
@@ -29382,6 +30144,7 @@ var init_specs2 = __esm({
|
|
|
29382
30144
|
const qualifiedDelta = qualifyDeltaRefs(mergeableDelta);
|
|
29383
30145
|
const mergedResult = mergeDelta(result, qualifiedDelta);
|
|
29384
30146
|
for (const field of unsetFields) delete mergedResult[field];
|
|
30147
|
+
deriveMergedTexts(kind, mergedResult);
|
|
29385
30148
|
const withDefaults = deltaSchema.safeParse(mergedResult);
|
|
29386
30149
|
if (withDefaults.success) {
|
|
29387
30150
|
for (const [key, value] of Object.entries(withDefaults.data)) {
|
|
@@ -30193,7 +30956,7 @@ function referencedKeys(part, types) {
|
|
|
30193
30956
|
}
|
|
30194
30957
|
for (const i of loadInterfaceSpecs().filter((x) => own.has(x.id))) {
|
|
30195
30958
|
add2(i.component);
|
|
30196
|
-
for (const m of i.methods) addTypes(
|
|
30959
|
+
for (const m of i.methods) addTypes(sourcedMethodTypeRefs(m));
|
|
30197
30960
|
}
|
|
30198
30961
|
for (const impl of loadImplementationSpecs().filter((x) => own.has(x.id))) {
|
|
30199
30962
|
add2(impl.contract);
|
|
@@ -30206,10 +30969,20 @@ function referencedKeys(part, types) {
|
|
|
30206
30969
|
}
|
|
30207
30970
|
for (const t of types.filter((x) => own.has(x.id))) {
|
|
30208
30971
|
add2(t.subsystem);
|
|
30209
|
-
|
|
30972
|
+
addTypes(typeSpecTypeRefs(t));
|
|
30210
30973
|
}
|
|
30211
30974
|
return out;
|
|
30212
30975
|
}
|
|
30976
|
+
function sourcedMethodTypeRefs(m) {
|
|
30977
|
+
return [...methodTypeRefs(m), ...m.signatureFrom !== void 0 ? [m.signatureFrom] : []];
|
|
30978
|
+
}
|
|
30979
|
+
function typeSpecTypeRefs(t) {
|
|
30980
|
+
return [
|
|
30981
|
+
...(t.fields ?? []).flatMap((f) => fieldTypeRefs(t, f.type)),
|
|
30982
|
+
...(t.methods ?? []).filter((m) => m.params !== void 0).flatMap((m) => methodTypeRefs(m)),
|
|
30983
|
+
...signatureTypeRefs(t)
|
|
30984
|
+
];
|
|
30985
|
+
}
|
|
30213
30986
|
function closeOver(keys, types) {
|
|
30214
30987
|
const out = /* @__PURE__ */ new Map();
|
|
30215
30988
|
const queue = [...keys];
|
|
@@ -30228,10 +31001,9 @@ function closeOver(keys, types) {
|
|
|
30228
31001
|
queue.push(comp.subsystem);
|
|
30229
31002
|
for (const contract of interfaces.filter((i) => i.component === comp.id)) queue.push(contract.id);
|
|
30230
31003
|
} else if (found.kind === "interface") {
|
|
30231
|
-
for (const m of found.spec.methods) addTypes(
|
|
31004
|
+
for (const m of found.spec.methods) addTypes(sourcedMethodTypeRefs(m));
|
|
30232
31005
|
} else if (found.kind === "type") {
|
|
30233
|
-
|
|
30234
|
-
for (const f of type.fields ?? []) addTypes(fieldTypeRefs(type, f.type));
|
|
31006
|
+
addTypes(typeSpecTypeRefs(found.spec));
|
|
30235
31007
|
} else if (found.kind === "implementation") {
|
|
30236
31008
|
queue.push(found.spec.contract);
|
|
30237
31009
|
}
|
|
@@ -31762,7 +32534,11 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
31762
32534
|
for (const contract of moving) {
|
|
31763
32535
|
saveInterfaceSpec({
|
|
31764
32536
|
...contract,
|
|
31765
|
-
methods: contract.methods.map((m) => m.name === methodName ? {
|
|
32537
|
+
methods: contract.methods.map((m) => m.name === methodName ? {
|
|
32538
|
+
...m,
|
|
32539
|
+
name: newName,
|
|
32540
|
+
...m.params === void 0 && m.signatureFrom === void 0 ? { signature: renameInSignature(m.signature, methodName, newName) } : {}
|
|
32541
|
+
} : m)
|
|
31766
32542
|
});
|
|
31767
32543
|
renamed.push(contract.id);
|
|
31768
32544
|
}
|
|
@@ -36806,6 +37582,52 @@ var init_narrative_repair = __esm({
|
|
|
36806
37582
|
}
|
|
36807
37583
|
});
|
|
36808
37584
|
|
|
37585
|
+
// src/core/signature-repair.ts
|
|
37586
|
+
function repairSignatures(apply4) {
|
|
37587
|
+
const facts = signatureFacts();
|
|
37588
|
+
const repairs = /* @__PURE__ */ new Map();
|
|
37589
|
+
const repairOf = (specId, kind) => {
|
|
37590
|
+
const key = `${kind}:${specId}`;
|
|
37591
|
+
let repair = repairs.get(key);
|
|
37592
|
+
if (!repair) {
|
|
37593
|
+
repair = { specId, kind, regenerated: [], dropped: [] };
|
|
37594
|
+
repairs.set(key, repair);
|
|
37595
|
+
}
|
|
37596
|
+
return repair;
|
|
37597
|
+
};
|
|
37598
|
+
const held = new Set(facts.sources.filter((f) => f.outcome === "restated" && f.differs).map((f) => f.interfaceId));
|
|
37599
|
+
const own = (specId, kind) => !specId.includes("::") && !(kind === "interface" && held.has(specId));
|
|
37600
|
+
for (const stale of facts.staleTexts) {
|
|
37601
|
+
if (own(stale.specId, stale.kind)) repairOf(stale.specId, stale.kind).regenerated.push(stale);
|
|
37602
|
+
}
|
|
37603
|
+
for (const fact of facts.sources) {
|
|
37604
|
+
if (fact.outcome !== "restated" || fact.differs || !own(fact.interfaceId, "interface")) continue;
|
|
37605
|
+
repairOf(fact.interfaceId, "interface").dropped.push(fact.method);
|
|
37606
|
+
}
|
|
37607
|
+
const planned = [...repairs.values()];
|
|
37608
|
+
if (apply4 && planned.length > 0) {
|
|
37609
|
+
const interfaces = new Map(loadInterfaceSpecs().map((i) => [i.id, i]));
|
|
37610
|
+
const types = new Map(loadTypeSpecs().map((t) => [t.id, t]));
|
|
37611
|
+
for (const repair of planned) {
|
|
37612
|
+
if (repair.kind === "interface") {
|
|
37613
|
+
const intf = interfaces.get(repair.specId);
|
|
37614
|
+
if (intf) saveInterfaceSpec(intf);
|
|
37615
|
+
continue;
|
|
37616
|
+
}
|
|
37617
|
+
const type = types.get(repair.specId);
|
|
37618
|
+
if (type) saveSpec("type", type);
|
|
37619
|
+
}
|
|
37620
|
+
invalidateSpecCache();
|
|
37621
|
+
}
|
|
37622
|
+
return planned;
|
|
37623
|
+
}
|
|
37624
|
+
var init_signature_repair = __esm({
|
|
37625
|
+
"src/core/signature-repair.ts"() {
|
|
37626
|
+
"use strict";
|
|
37627
|
+
init_specs2();
|
|
37628
|
+
}
|
|
37629
|
+
});
|
|
37630
|
+
|
|
36809
37631
|
// src/models/lock.ts
|
|
36810
37632
|
function describeApprover(who) {
|
|
36811
37633
|
const label7 = who.name ? `${who.name} (${who.id})` : who.id;
|
|
@@ -37613,6 +38435,7 @@ var init_core = __esm({
|
|
|
37613
38435
|
init_domain_curator();
|
|
37614
38436
|
init_rules();
|
|
37615
38437
|
init_specs2();
|
|
38438
|
+
init_signature_sources2();
|
|
37616
38439
|
init_external_producers();
|
|
37617
38440
|
init_part_context();
|
|
37618
38441
|
init_provision();
|
|
@@ -37633,6 +38456,7 @@ var init_core = __esm({
|
|
|
37633
38456
|
init_provision();
|
|
37634
38457
|
init_stereotype_migration();
|
|
37635
38458
|
init_narrative_repair();
|
|
38459
|
+
init_signature_repair();
|
|
37636
38460
|
init_approval();
|
|
37637
38461
|
init_approver();
|
|
37638
38462
|
init_specs2();
|
|
@@ -38043,6 +38867,8 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
|
38043
38867
|
return {
|
|
38044
38868
|
gate: (kind, merged) => {
|
|
38045
38869
|
refuseUnknownOwner(kind, merged, storedOwner);
|
|
38870
|
+
const restated = kind === "interface" ? restatedSources(merged.methods) : [];
|
|
38871
|
+
if (restated.length) throw new Error(`${restatedSourceRefusal(String(merged.id), restated)} Nothing was written.`);
|
|
38046
38872
|
if (kind !== "component") return;
|
|
38047
38873
|
const verdict = validateComponentCandidate(merged, options);
|
|
38048
38874
|
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
@@ -38050,6 +38876,43 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
|
|
|
38050
38876
|
}
|
|
38051
38877
|
};
|
|
38052
38878
|
}
|
|
38879
|
+
function restatedSources(methods) {
|
|
38880
|
+
if (!Array.isArray(methods)) return [];
|
|
38881
|
+
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));
|
|
38882
|
+
}
|
|
38883
|
+
function restatedSourceRefusal(id, methods) {
|
|
38884
|
+
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.`;
|
|
38885
|
+
}
|
|
38886
|
+
function unsignedMethods(methods) {
|
|
38887
|
+
if (!Array.isArray(methods)) return [];
|
|
38888
|
+
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));
|
|
38889
|
+
}
|
|
38890
|
+
function deriveStatedTexts(kind, candidate) {
|
|
38891
|
+
if (kind !== "interface" && kind !== "type" || !Array.isArray(candidate.methods)) return [];
|
|
38892
|
+
const differed = [];
|
|
38893
|
+
candidate.methods = candidate.methods.map((m) => {
|
|
38894
|
+
if (!m || typeof m !== "object" || m.signatureFrom !== void 0 || !Array.isArray(m.params)) return m;
|
|
38895
|
+
const derived = deriveMethodSignature(m);
|
|
38896
|
+
if (derived === void 0) return m;
|
|
38897
|
+
if (typeof m.signature === "string" && m.signature !== derived) differed.push(`"${m.name}": stated "${m.signature}", written "${derived}"`);
|
|
38898
|
+
return { ...m, signature: derived };
|
|
38899
|
+
});
|
|
38900
|
+
return differed.length === 0 ? [] : [
|
|
38901
|
+
`Signature text derived from params, not taken as stated \u2014 ${differed.join("; ")}. A method with params shows the text its params derive.`
|
|
38902
|
+
];
|
|
38903
|
+
}
|
|
38904
|
+
function storedForm(kind, spec) {
|
|
38905
|
+
if (!spec) return spec;
|
|
38906
|
+
if (kind === "interface") {
|
|
38907
|
+
const intf = spec;
|
|
38908
|
+
return { ...intf, methods: intf.methods.map((m) => storedMethodSignature(m)) };
|
|
38909
|
+
}
|
|
38910
|
+
if (kind === "type") {
|
|
38911
|
+
const type = spec;
|
|
38912
|
+
return { ...type, methods: type.methods.map((m) => storedTypeMethod(m)) };
|
|
38913
|
+
}
|
|
38914
|
+
return spec;
|
|
38915
|
+
}
|
|
38053
38916
|
function refuseUnknownOwner(kind, merged, storedOwner) {
|
|
38054
38917
|
if (kind !== "component" && kind !== "type") return;
|
|
38055
38918
|
const owner = merged.subsystem;
|
|
@@ -38075,7 +38938,8 @@ function restatementParent(restatement) {
|
|
|
38075
38938
|
return null;
|
|
38076
38939
|
}
|
|
38077
38940
|
}
|
|
38078
|
-
function applyRestatement(restatement,
|
|
38941
|
+
function applyRestatement(restatement, loaded, parent) {
|
|
38942
|
+
const existing = storedForm(restatement.kind, loaded);
|
|
38079
38943
|
const replacedExisting = existing !== null;
|
|
38080
38944
|
const parentRef = restatementParent(restatement);
|
|
38081
38945
|
if (parentRef && !parent) return refused(restatement, replacedExisting, MISSING_PARENT[restatement.kind](parentRef.id));
|
|
@@ -38086,6 +38950,19 @@ function applyRestatement(restatement, existing, parent) {
|
|
|
38086
38950
|
const carried = carryInto(restatement, existing, candidate);
|
|
38087
38951
|
const cleared = clearedByOmission(existing, candidate, restatement.fields);
|
|
38088
38952
|
stampLifecycle(candidate, existing, status2.status);
|
|
38953
|
+
if (restatement.kind === "interface") {
|
|
38954
|
+
const restated = restatedSources(candidate.methods);
|
|
38955
|
+
if (restated.length) return refused(restatement, replacedExisting, `${restatedSourceRefusal(String(candidate.id), restated)} Nothing was written.`);
|
|
38956
|
+
}
|
|
38957
|
+
const unsigned = restatement.kind === "interface" || restatement.kind === "type" ? unsignedMethods(candidate.methods) : [];
|
|
38958
|
+
if (unsigned.length) {
|
|
38959
|
+
return refused(
|
|
38960
|
+
restatement,
|
|
38961
|
+
replacedExisting,
|
|
38962
|
+
`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.`
|
|
38963
|
+
);
|
|
38964
|
+
}
|
|
38965
|
+
const derivedNotices = deriveStatedTexts(restatement.kind, candidate);
|
|
38089
38966
|
const labelErrors = resolveLabelsOf(restatement.kind, candidate);
|
|
38090
38967
|
if (labelErrors.length) {
|
|
38091
38968
|
return refused(restatement, replacedExisting, `Unresolved narrative label references \u2014 nothing was saved:
|
|
@@ -38097,7 +38974,7 @@ function applyRestatement(restatement, existing, parent) {
|
|
|
38097
38974
|
spec: parsed.spec,
|
|
38098
38975
|
...status2.status ? { status: status2.status } : {},
|
|
38099
38976
|
replacedExisting,
|
|
38100
|
-
notices: existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [],
|
|
38977
|
+
notices: [...existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [], ...derivedNotices],
|
|
38101
38978
|
changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
|
|
38102
38979
|
};
|
|
38103
38980
|
}
|
|
@@ -42137,6 +43014,16 @@ function pendingBanner() {
|
|
|
42137
43014
|
return [];
|
|
42138
43015
|
}
|
|
42139
43016
|
}
|
|
43017
|
+
function storedContractAnswer(loaded) {
|
|
43018
|
+
const resolvedSignatures = loaded.methods.filter((m) => m.signatureFrom !== void 0).map((m) => ({
|
|
43019
|
+
method: m.name,
|
|
43020
|
+
signatureFrom: m.signatureFrom,
|
|
43021
|
+
signature: m.signature,
|
|
43022
|
+
...m.params ? { params: m.params } : {},
|
|
43023
|
+
returns: m.returns
|
|
43024
|
+
}));
|
|
43025
|
+
return { spec: { ...loaded, methods: loaded.methods.map((m) => storedMethodSignature(m)) }, resolvedSignatures };
|
|
43026
|
+
}
|
|
42140
43027
|
function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
|
|
42141
43028
|
const def = schema?._def;
|
|
42142
43029
|
if (!def) return null;
|
|
@@ -42986,19 +43873,25 @@ ${renderChangeReport(report3)}`,
|
|
|
42986
43873
|
}
|
|
42987
43874
|
}
|
|
42988
43875
|
);
|
|
43876
|
+
const methodParamItem = import_zod11.z.object({
|
|
43877
|
+
name: import_zod11.z.string(),
|
|
43878
|
+
type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
|
|
43879
|
+
description: import_zod11.z.string().optional(),
|
|
43880
|
+
optional: import_zod11.z.boolean().optional().describe(
|
|
43881
|
+
'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.'
|
|
43882
|
+
)
|
|
43883
|
+
}).strict();
|
|
42989
43884
|
const interfaceMethodShape = {
|
|
42990
43885
|
name: import_zod11.z.string(),
|
|
42991
43886
|
description: import_zod11.z.string(),
|
|
42992
|
-
signature: import_zod11.z.string()
|
|
42993
|
-
|
|
42994
|
-
|
|
42995
|
-
|
|
42996
|
-
|
|
42997
|
-
|
|
42998
|
-
|
|
42999
|
-
|
|
43000
|
-
)
|
|
43001
|
-
}).strict()).optional().describe("Structured parameters \u2014 authoritative for type checking (the prose signature becomes display-only). Strongly preferred."),
|
|
43887
|
+
signature: import_zod11.z.string().optional().describe(
|
|
43888
|
+
"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."
|
|
43889
|
+
),
|
|
43890
|
+
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")),
|
|
43891
|
+
signatureFrom: import_zod11.z.string().min(1).optional().describe(
|
|
43892
|
+
"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."
|
|
43893
|
+
),
|
|
43894
|
+
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."),
|
|
43002
43895
|
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)"),
|
|
43003
43896
|
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"),
|
|
43004
43897
|
invokedBy: import_zod11.z.object({
|
|
@@ -43026,7 +43919,7 @@ ${renderChangeReport(report3)}`,
|
|
|
43026
43919
|
server,
|
|
43027
43920
|
"sdd_define_interface",
|
|
43028
43921
|
{
|
|
43029
|
-
description: "Define an L3 Contract / Interface with method signatures for a component.
|
|
43922
|
+
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.",
|
|
43030
43923
|
inputSchema: interfaceInput,
|
|
43031
43924
|
outputSchema: specWriteReceiptOutput
|
|
43032
43925
|
},
|
|
@@ -43234,7 +44127,7 @@ ${renderChangeReport(report3)}`,
|
|
|
43234
44127
|
}
|
|
43235
44128
|
);
|
|
43236
44129
|
const typeInput = {
|
|
43237
|
-
kind: import_zod11.z.enum(["entity", "value-object"]).describe("entity (owned by a subsystem)
|
|
44130
|
+
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"),
|
|
43238
44131
|
id: import_zod11.z.string().describe("Lowercase identifier"),
|
|
43239
44132
|
name: import_zod11.z.string().describe("Human-readable name"),
|
|
43240
44133
|
description: import_zod11.z.string().optional(),
|
|
@@ -43250,7 +44143,8 @@ ${renderChangeReport(report3)}`,
|
|
|
43250
44143
|
}).strict()).optional().describe('Data fields (type is a primitive or a qualified type id, e.g. "billing.Invoice")'),
|
|
43251
44144
|
methods: import_zod11.z.array(import_zod11.z.object({
|
|
43252
44145
|
name: import_zod11.z.string(),
|
|
43253
|
-
signature: import_zod11.z.string(),
|
|
44146
|
+
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"),
|
|
44147
|
+
params: import_zod11.z.array(methodParamItem).optional().describe("Structured parameters \u2014 authoritative for type checking; the signature text is derived from them"),
|
|
43254
44148
|
returns: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The type the method answers with")),
|
|
43255
44149
|
description: import_zod11.z.string().optional(),
|
|
43256
44150
|
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"),
|
|
@@ -43265,18 +44159,20 @@ ${renderChangeReport(report3)}`,
|
|
|
43265
44159
|
table: import_zod11.z.string().optional().describe("Optional database table name for table-schema types"),
|
|
43266
44160
|
linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type"),
|
|
43267
44161
|
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)"),
|
|
43268
|
-
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')
|
|
44162
|
+
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'),
|
|
44163
|
+
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)"),
|
|
44164
|
+
returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output"))
|
|
43269
44165
|
};
|
|
43270
44166
|
const typeInputFields = Object.keys(typeInput);
|
|
43271
44167
|
reg(
|
|
43272
44168
|
server,
|
|
43273
44169
|
"sdd_add_type",
|
|
43274
44170
|
{
|
|
43275
|
-
description: "Define an entity or value-object
|
|
44171
|
+
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.",
|
|
43276
44172
|
inputSchema: typeInput,
|
|
43277
44173
|
outputSchema: specWriteReceiptOutput
|
|
43278
44174
|
},
|
|
43279
|
-
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol }) => {
|
|
44175
|
+
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns }) => {
|
|
43280
44176
|
try {
|
|
43281
44177
|
const spec = {
|
|
43282
44178
|
kind,
|
|
@@ -43300,7 +44196,9 @@ ${renderChangeReport(report3)}`,
|
|
|
43300
44196
|
...table ? { table } : {},
|
|
43301
44197
|
...linkedEntity ? { linkedEntity } : {},
|
|
43302
44198
|
...sourcePath ? { sourcePath } : {},
|
|
43303
|
-
...symbol ? { symbol } : {}
|
|
44199
|
+
...symbol ? { symbol } : {},
|
|
44200
|
+
...params ? { params } : {},
|
|
44201
|
+
...returns ? { returns } : {}
|
|
43304
44202
|
};
|
|
43305
44203
|
const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
|
|
43306
44204
|
return writeReceipt(
|
|
@@ -43358,7 +44256,7 @@ ${renderChangeReport(report3)}`,
|
|
|
43358
44256
|
server,
|
|
43359
44257
|
"sdd_get_spec",
|
|
43360
44258
|
{
|
|
43361
|
-
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
|
|
44259
|
+
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.`,
|
|
43362
44260
|
inputSchema: {
|
|
43363
44261
|
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The kind of specification"),
|
|
43364
44262
|
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")'),
|
|
@@ -43391,6 +44289,12 @@ ${renderChangeReport(report3)}`,
|
|
|
43391
44289
|
break;
|
|
43392
44290
|
}
|
|
43393
44291
|
if (!result) return errText(`Spec of kind "${kind}" with ID "${id}" does not exist.`);
|
|
44292
|
+
let resolvedSignatures = [];
|
|
44293
|
+
if (kind === "interface") {
|
|
44294
|
+
const answer = storedContractAnswer(result);
|
|
44295
|
+
result = answer.spec;
|
|
44296
|
+
resolvedSignatures = answer.resolvedSignatures;
|
|
44297
|
+
}
|
|
43394
44298
|
if (methods !== void 0) {
|
|
43395
44299
|
const declared = result.methods;
|
|
43396
44300
|
if (!Array.isArray(declared)) {
|
|
@@ -43412,19 +44316,23 @@ ${renderChangeReport(report3)}`,
|
|
|
43412
44316
|
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.`
|
|
43413
44317
|
};
|
|
43414
44318
|
result = { ...result, methods: kept };
|
|
44319
|
+
resolvedSignatures = resolvedSignatures.filter((r) => methods.includes(r.method));
|
|
43415
44320
|
}
|
|
43416
44321
|
const guidance = kind === "component" ? resolveComponentVariantGuidance(result) : null;
|
|
44322
|
+
const resolved = resolvedSignatures.length > 0 ? { resolvedSignatures } : {};
|
|
43417
44323
|
const folded = {
|
|
43418
44324
|
...result,
|
|
43419
44325
|
...partial2 ? { partialResult: partial2 } : {},
|
|
43420
|
-
...guidance ? { variantGuidance: guidance } : {}
|
|
44326
|
+
...guidance ? { variantGuidance: guidance } : {},
|
|
44327
|
+
...resolved
|
|
43421
44328
|
};
|
|
43422
44329
|
return structured(JSON.stringify(folded, null, 2), {
|
|
43423
44330
|
kind,
|
|
43424
44331
|
id,
|
|
43425
44332
|
spec: result,
|
|
43426
44333
|
...partial2 ? { partialResult: partial2 } : {},
|
|
43427
|
-
...guidance ? { variantGuidance: guidance } : {}
|
|
44334
|
+
...guidance ? { variantGuidance: guidance } : {},
|
|
44335
|
+
...resolved
|
|
43428
44336
|
});
|
|
43429
44337
|
} catch (e) {
|
|
43430
44338
|
return errText(String(e));
|
|
@@ -43971,6 +44879,15 @@ var init_server = __esm({
|
|
|
43971
44879
|
}).optional().describe(
|
|
43972
44880
|
"Derived, read-only guidance for a variant-tagged component \u2014 resolved from the variant registry, not part of the spec. Never write it back."
|
|
43973
44881
|
),
|
|
44882
|
+
resolvedSignatures: import_zod11.z.array(import_zod11.z.object({
|
|
44883
|
+
method: import_zod11.z.string().describe("The contract method that takes its signature from a source."),
|
|
44884
|
+
signatureFrom: import_zod11.z.string().describe("The source it names, as the spec holds it."),
|
|
44885
|
+
signature: import_zod11.z.string().describe("The text it shows, derived from the resolved params and returns."),
|
|
44886
|
+
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)."),
|
|
44887
|
+
returns: import_zod11.z.string().describe("The returns the source supplies (`unknown` when it supplies none).")
|
|
44888
|
+
})).optional().describe(
|
|
44889
|
+
"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."
|
|
44890
|
+
),
|
|
43974
44891
|
...staleServerOutput
|
|
43975
44892
|
};
|
|
43976
44893
|
FINGERPRINT_VERSION = 1;
|
|
@@ -47068,6 +47985,19 @@ async function runDoctor(options = {}) {
|
|
|
47068
47985
|
} catch (e) {
|
|
47069
47986
|
line(tally, "warn", `Could not plan the narrative-step repair: ${e instanceof Error ? e.message : String(e)}`);
|
|
47070
47987
|
}
|
|
47988
|
+
try {
|
|
47989
|
+
const signatures = repairSignatures(false);
|
|
47990
|
+
if (signatures.length > 0) {
|
|
47991
|
+
console.log(import_chalk12.default.bold("Signatures"));
|
|
47992
|
+
for (const repair of signatures) {
|
|
47993
|
+
line(tally, "warn", `${repair.kind} ${repair.specId}: ${describeSignatureRepair(repair)}`);
|
|
47994
|
+
}
|
|
47995
|
+
line(tally, "warn", "Run `wairon doctor --fix` to write them in their stored form (any save of the spec does too).");
|
|
47996
|
+
logger.blank();
|
|
47997
|
+
}
|
|
47998
|
+
} catch (e) {
|
|
47999
|
+
line(tally, "warn", `Could not plan the signature repair: ${e instanceof Error ? e.message : String(e)}`);
|
|
48000
|
+
}
|
|
47071
48001
|
try {
|
|
47072
48002
|
const planned = plan5({ verb: "chaining", rehearse: false });
|
|
47073
48003
|
const pending4 = planned.chaining;
|
|
@@ -47288,6 +48218,17 @@ async function applyFixes(options, tally) {
|
|
|
47288
48218
|
} catch (e) {
|
|
47289
48219
|
console.log(` ${icon("error")} Narrative-step repair failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
47290
48220
|
}
|
|
48221
|
+
try {
|
|
48222
|
+
const signatures = repairSignatures(true);
|
|
48223
|
+
if (signatures.length > 0) {
|
|
48224
|
+
console.log(` ${icon("ok")} Rewrote ${signatures.length} spec(s) into their stored signature form:`);
|
|
48225
|
+
for (const repair of signatures) {
|
|
48226
|
+
console.log(` ${repair.kind} ${repair.specId}: ${describeSignatureRepair(repair)}`);
|
|
48227
|
+
}
|
|
48228
|
+
}
|
|
48229
|
+
} catch (e) {
|
|
48230
|
+
console.log(` ${icon("error")} Signature repair failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
48231
|
+
}
|
|
47291
48232
|
await migrateChaining(options, tally);
|
|
47292
48233
|
await repairMcpRegistration(options, targets);
|
|
47293
48234
|
logger.blank();
|
|
@@ -47504,6 +48445,12 @@ function printApplied(report3) {
|
|
|
47504
48445
|
console.log(` ${icon("warn")} The writes staled the approval of ${report3.relock.length} project(s); nothing was locked here. Re-lock each with \`wairon lock\`:`);
|
|
47505
48446
|
for (const dir of report3.relock) console.log(` ${dir}`);
|
|
47506
48447
|
}
|
|
48448
|
+
function describeSignatureRepair(repair) {
|
|
48449
|
+
const parts = [];
|
|
48450
|
+
if (repair.regenerated.length > 0) parts.push(`regenerates the text of ${repair.regenerated.map((s) => s.method).join(", ")}`);
|
|
48451
|
+
if (repair.dropped.length > 0) parts.push(`drops the restated signature of ${repair.dropped.join(", ")}`);
|
|
48452
|
+
return parts.join("; ");
|
|
48453
|
+
}
|
|
47507
48454
|
function pendingCount(migration) {
|
|
47508
48455
|
return migration.rewrites.length + migration.projects.reduce((n, p) => n + (p.idToWrite ? 1 : 0) + (p.createsSystem ? 1 : 0) + p.exports.length + p.externals.length + p.imports.length + p.pins.length + p.members.length + p.supersededPins.length + p.locations.length, 0);
|
|
47509
48456
|
}
|
|
@@ -51909,11 +52856,13 @@ function listProducerConfigs() {
|
|
|
51909
52856
|
init_core();
|
|
51910
52857
|
|
|
51911
52858
|
// src/producers/projection.ts
|
|
52859
|
+
init_models();
|
|
51912
52860
|
function project(diagramUrl) {
|
|
51913
52861
|
const system = loadSystemSpec();
|
|
51914
52862
|
const subsystems = loadSubsystemSpecs();
|
|
51915
52863
|
const components = loadComponentSpecs();
|
|
51916
52864
|
const interfaces = loadInterfaceSpecs();
|
|
52865
|
+
const types = loadTypeSpecs();
|
|
51917
52866
|
let mermaid = "";
|
|
51918
52867
|
try {
|
|
51919
52868
|
mermaid = renderDiagram("mermaid");
|
|
@@ -51921,7 +52870,7 @@ function project(diagramUrl) {
|
|
|
51921
52870
|
}
|
|
51922
52871
|
return {
|
|
51923
52872
|
title: "wairon specs",
|
|
51924
|
-
body: systemBody(system, mermaid, diagramUrl),
|
|
52873
|
+
body: systemBody(system, mermaid, diagramUrl, types),
|
|
51925
52874
|
children: subsystems.map((sub) => ({
|
|
51926
52875
|
title: sub.name || sub.id,
|
|
51927
52876
|
body: subsystemBody(sub),
|
|
@@ -51950,7 +52899,7 @@ function projectGraph() {
|
|
|
51950
52899
|
}
|
|
51951
52900
|
return { nodes, edges };
|
|
51952
52901
|
}
|
|
51953
|
-
function systemBody(system, mermaid, diagramUrl) {
|
|
52902
|
+
function systemBody(system, mermaid, diagramUrl, types) {
|
|
51954
52903
|
const parts = ["_Generated by wairon from the spec tree \u2014 edit the specs, not this page._", ""];
|
|
51955
52904
|
if (system) {
|
|
51956
52905
|
parts.push(`# ${system.name}`, "");
|
|
@@ -51961,6 +52910,14 @@ function systemBody(system, mermaid, diagramUrl) {
|
|
|
51961
52910
|
}
|
|
51962
52911
|
if (diagramUrl) parts.push("## Diagram", `[Open the interactive canvas](${diagramUrl})`, "");
|
|
51963
52912
|
if (mermaid) parts.push("```mermaid", mermaid.trim(), "```", "");
|
|
52913
|
+
const signatures = types.filter((t) => t.kind === "signature");
|
|
52914
|
+
if (signatures.length) {
|
|
52915
|
+
parts.push("## Signature types");
|
|
52916
|
+
for (const t of signatures) {
|
|
52917
|
+
parts.push(`- **${t.name || t.id}** \`${deriveTypeSignature(t)}\`${t.description ? ` \u2014 ${t.description}` : ""}`);
|
|
52918
|
+
}
|
|
52919
|
+
parts.push("");
|
|
52920
|
+
}
|
|
51964
52921
|
return parts.join("\n");
|
|
51965
52922
|
}
|
|
51966
52923
|
function publicInterfaceLine(p) {
|
|
@@ -51988,7 +52945,10 @@ function componentBody(comp, interfaces) {
|
|
|
51988
52945
|
const intf = interfaces.find((i) => i.component === comp.id);
|
|
51989
52946
|
if (intf?.methods?.length) {
|
|
51990
52947
|
parts.push("## Methods");
|
|
51991
|
-
for (const m of intf.methods)
|
|
52948
|
+
for (const m of intf.methods) {
|
|
52949
|
+
const from = m.signatureFrom ? ` (from \`${m.signatureFrom}\`)` : "";
|
|
52950
|
+
parts.push(`- **${m.name}** \`${m.signature}\`${from} \u2192 ${m.returns}`);
|
|
52951
|
+
}
|
|
51992
52952
|
parts.push("");
|
|
51993
52953
|
}
|
|
51994
52954
|
return parts.join("\n");
|
|
@@ -56838,8 +57798,8 @@ function narrowToScope(projects, scope) {
|
|
|
56838
57798
|
if (scope.all) return projects;
|
|
56839
57799
|
return projects.filter((p) => scope.projectIds.includes(p.id));
|
|
56840
57800
|
}
|
|
56841
|
-
function
|
|
56842
|
-
return cfg.quotaPolicy
|
|
57801
|
+
function effectiveQuotaPolicy(cfg) {
|
|
57802
|
+
return cfg?.quotaPolicy ? { ...DISABLED_QUOTA_POLICY, ...cfg.quotaPolicy } : DISABLED_QUOTA_POLICY;
|
|
56843
57803
|
}
|
|
56844
57804
|
function scopeRecords(projects, scope) {
|
|
56845
57805
|
if (scope && scope.length > 0 && scope !== "instance") {
|
|
@@ -57028,7 +57988,7 @@ function evaluateQuota(cfg, credential, scope) {
|
|
|
57028
57988
|
const readScope = requireOperationsReadScope(cfg, principal);
|
|
57029
57989
|
const projects = narrowToScope(listProjectRecords(cfg.dataDir), readScope);
|
|
57030
57990
|
const usage = collectUsage(projects, scope);
|
|
57031
|
-
const policy =
|
|
57991
|
+
const policy = effectiveQuotaPolicy(cfg);
|
|
57032
57992
|
return evaluateUsage(usage, policy);
|
|
57033
57993
|
}
|
|
57034
57994
|
function principalSubject6(principal) {
|
|
@@ -63377,6 +64337,8 @@ function resolveHostConfig(options) {
|
|
|
63377
64337
|
if (builtinPassword) cfg.builtinAdminPassword = builtinPassword;
|
|
63378
64338
|
const auditPolicy = process.env["WAIRON_AUDIT_POLICY"];
|
|
63379
64339
|
if (auditPolicy !== void 0 && auditPolicy.trim() !== "") cfg.auditPolicy = parseAuditPolicyEnv(auditPolicy);
|
|
64340
|
+
const quotaPolicy = process.env["WAIRON_QUOTA_POLICY"];
|
|
64341
|
+
if (quotaPolicy !== void 0 && quotaPolicy.trim() !== "") cfg.quotaPolicy = parseQuotaPolicyEnv(quotaPolicy);
|
|
63380
64342
|
return cfg;
|
|
63381
64343
|
}
|
|
63382
64344
|
var AUDIT_POLICY_FIELDS = {
|
|
@@ -63421,6 +64383,47 @@ function parseAuditPolicyEnv(raw) {
|
|
|
63421
64383
|
}
|
|
63422
64384
|
return policy;
|
|
63423
64385
|
}
|
|
64386
|
+
var NON_NEGATIVE_INTEGER = {
|
|
64387
|
+
expects: "a non-negative integer",
|
|
64388
|
+
accepts: (v) => typeof v === "number" && Number.isInteger(v) && v >= 0
|
|
64389
|
+
};
|
|
64390
|
+
var QUOTA_POLICY_FIELDS = {
|
|
64391
|
+
enabled: { expects: "a boolean", accepts: (v) => typeof v === "boolean" },
|
|
64392
|
+
// 'block' is accepted, and evaluation still downgrades it to an observation.
|
|
64393
|
+
mode: {
|
|
64394
|
+
expects: 'one of "observe", "warn", "block"',
|
|
64395
|
+
accepts: (v) => typeof v === "string" && ["observe", "warn", "block"].includes(v)
|
|
64396
|
+
},
|
|
64397
|
+
maxProjectsPerUser: NON_NEGATIVE_INTEGER,
|
|
64398
|
+
maxMcpRequestsPerMinute: NON_NEGATIVE_INTEGER,
|
|
64399
|
+
maxProjectBytes: NON_NEGATIVE_INTEGER,
|
|
64400
|
+
maxAuditEventsPerDay: NON_NEGATIVE_INTEGER
|
|
64401
|
+
};
|
|
64402
|
+
function parseQuotaPolicyEnv(raw) {
|
|
64403
|
+
let parsed;
|
|
64404
|
+
try {
|
|
64405
|
+
parsed = JSON.parse(raw);
|
|
64406
|
+
} catch (e) {
|
|
64407
|
+
throw new WaironError(`WAIRON_QUOTA_POLICY is not valid JSON: ${e.message}`);
|
|
64408
|
+
}
|
|
64409
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
64410
|
+
throw new WaironError("WAIRON_QUOTA_POLICY must be a JSON object of quota policy fields.");
|
|
64411
|
+
}
|
|
64412
|
+
const policy = {};
|
|
64413
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
64414
|
+
const field = QUOTA_POLICY_FIELDS[key];
|
|
64415
|
+
if (!Object.prototype.hasOwnProperty.call(QUOTA_POLICY_FIELDS, key) || !field) {
|
|
64416
|
+
throw new WaironError(
|
|
64417
|
+
`WAIRON_QUOTA_POLICY sets an unknown field "${key}"; the known fields are ${Object.keys(QUOTA_POLICY_FIELDS).join(", ")}.`
|
|
64418
|
+
);
|
|
64419
|
+
}
|
|
64420
|
+
if (!field.accepts(value)) {
|
|
64421
|
+
throw new WaironError(`WAIRON_QUOTA_POLICY field "${key}" must be ${field.expects}.`);
|
|
64422
|
+
}
|
|
64423
|
+
policy[key] = value;
|
|
64424
|
+
}
|
|
64425
|
+
return policy;
|
|
64426
|
+
}
|
|
63424
64427
|
function masterCredential() {
|
|
63425
64428
|
return process.env["WAIRON_ADMIN_TOKEN"] ?? null;
|
|
63426
64429
|
}
|