@wairon/cli 5.1.1-dev.98 → 5.1.1-dev.99

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/index.js CHANGED
@@ -3571,6 +3571,8 @@ __export(src_exports, {
3571
3571
  buildCanvasDataModel: () => buildCanvasDataModel,
3572
3572
  buildProjectGraph: () => buildProjectGraph,
3573
3573
  buildRuleContext: () => buildRuleContext,
3574
+ builtinProfileIds: () => builtinProfileIds,
3575
+ builtinProjectKinds: () => builtinProjectKinds,
3574
3576
  callSitesOf: () => callSitesOf,
3575
3577
  canonicalTypeRef: () => canonicalTypeRef,
3576
3578
  canonicalTypeText: () => canonicalTypeText,
@@ -3581,6 +3583,7 @@ __export(src_exports, {
3581
3583
  complexityLevel: () => complexityLevel,
3582
3584
  composeAgentBrief: () => composeAgentBrief,
3583
3585
  composeRuleSequence: () => composeRuleSequence,
3586
+ computeGateStateId: () => computeGateStateId,
3584
3587
  computeOwnStateId: () => computeOwnStateId,
3585
3588
  computeStateId: () => computeStateId,
3586
3589
  computeStateIdAt: () => computeStateIdAt,
@@ -3632,12 +3635,15 @@ __export(src_exports, {
3632
3635
  excerptParent: () => excerpt,
3633
3636
  exportDesign: () => exportDesign3,
3634
3637
  exportSpecTree: () => exportSpecTree,
3638
+ exportSurface: () => exportSurface2,
3635
3639
  exportTargetKey: () => exportTargetKey,
3636
3640
  exportUsage: () => exportUsage,
3637
3641
  externalizeSubsystem: () => externalizeSubsystem,
3638
3642
  extractTypeIdentifiers: () => extractTypeIdentifiers,
3639
3643
  factsFor: () => factsFor,
3644
+ familyApprovals: () => familyApprovals,
3640
3645
  familyNode: () => familyNode,
3646
+ familyRelations: () => familyRelations2,
3641
3647
  fieldTypeRefs: () => fieldTypeRefs,
3642
3648
  fieldTypesOf: () => fieldTypesOf,
3643
3649
  findChainingParent: () => findChainingParent,
@@ -3645,6 +3651,7 @@ __export(src_exports, {
3645
3651
  findLegacySpecFiles: () => findLegacySpecFiles,
3646
3652
  findProjectRoot: () => findProjectRoot,
3647
3653
  findSystemRoot: () => findSystemRoot,
3654
+ findTestsReferencing: () => findTestsReferencing2,
3648
3655
  fromProjectRoot: () => fromProjectRoot,
3649
3656
  generateAgent: () => generateAgent,
3650
3657
  generateAll: () => generateAll,
@@ -3652,12 +3659,14 @@ __export(src_exports, {
3652
3659
  generateDiagramSet: () => generateDiagramSet,
3653
3660
  generateSequenceDiagram: () => generateSequenceDiagram,
3654
3661
  getExporter: () => getExporter,
3662
+ getExternalsStatus: () => getExternalsStatus2,
3655
3663
  getHostedLookup: () => getHostedLookup,
3656
3664
  getLoaderIssues: () => getLoaderIssues,
3657
3665
  getProjectRoot: () => getProjectRoot,
3658
3666
  getProjectRootOverride: () => getProjectRootOverride,
3659
3667
  getRequestParentReach: () => getRequestParentReach,
3660
3668
  getRequestProjectRoot: () => getRequestProjectRoot,
3669
+ getSnapshot: () => getSnapshot2,
3661
3670
  getStatusReport: () => getStatusReport,
3662
3671
  getWriteReach: () => getWriteReach,
3663
3672
  globalGuideFilePath: () => globalGuideFilePath,
@@ -3672,6 +3681,7 @@ __export(src_exports, {
3672
3681
  importBindingOf: () => importBindingOf,
3673
3682
  importNames: () => importNames,
3674
3683
  importSpecTree: () => importSpecTree,
3684
+ importSurface: () => importSurface2,
3675
3685
  injectGuide: () => injectGuide,
3676
3686
  installPackFromDirectory: () => installPackFromDirectory,
3677
3687
  interfaceCanonicalTypes: () => interfaceCanonicalTypes,
@@ -3692,9 +3702,13 @@ __export(src_exports, {
3692
3702
  landReference: () => landReference,
3693
3703
  languageOfSourcePath: () => languageOfSourcePath,
3694
3704
  listDirectChainedSubprojects: () => listDirectChainedSubprojects,
3705
+ listExternals: () => listExternals2,
3695
3706
  listFiles: () => listFiles,
3696
3707
  listFilesRecursive: () => listFilesRecursive,
3697
3708
  listInstalledPacks: () => listInstalledPacks,
3709
+ listPinnedExternals: () => listPinnedExternals2,
3710
+ listRules: () => listRules2,
3711
+ listSnapshots: () => listSnapshots2,
3698
3712
  loadComponentSpec: () => loadComponentSpec,
3699
3713
  loadComponentSpecs: () => loadComponentSpecs,
3700
3714
  loadExtensionPacks: () => loadExtensionPacks,
@@ -3723,6 +3737,8 @@ __export(src_exports, {
3723
3737
  makeScopeFilter: () => makeScopeFilter,
3724
3738
  markSelectionsBundled: () => markSelectionsBundled,
3725
3739
  matchTypeRef: () => matchTypeRef,
3740
+ measurePackDoctrine: () => measurePackDoctrine,
3741
+ measurePackImpact: () => measurePackImpact,
3726
3742
  memberDeclarationOf: () => memberDeclarationOf,
3727
3743
  memberDigest: () => memberDigest,
3728
3744
  memberLocationOf: () => memberLocationOf,
@@ -3751,7 +3767,9 @@ __export(src_exports, {
3751
3767
  passesIntentFloor: () => passesIntentFloor,
3752
3768
  pathExists: () => pathExists,
3753
3769
  pathKey: () => pathKey,
3770
+ pinExternals: () => pinExternals2,
3754
3771
  pinInstalledPacksAsSelections: () => pinInstalledPacksAsSelections,
3772
+ pinnedParent: () => pinnedParent2,
3755
3773
  producerOf: () => producerOf,
3756
3774
  projectConfigExists: () => projectConfigExists,
3757
3775
  projectFamily: () => graph,
@@ -3776,6 +3794,7 @@ __export(src_exports, {
3776
3794
  removeExternal: () => removeExternal,
3777
3795
  removeMember: () => removeMember,
3778
3796
  removePackSelection: () => removePackSelection,
3797
+ removeSnapshot: () => removeSnapshot2,
3779
3798
  renameAlias: () => renameAlias,
3780
3799
  renameComponent: () => renameComponent,
3781
3800
  renameId: () => renameProjectId,
@@ -3846,8 +3865,12 @@ __export(src_exports, {
3846
3865
  updateMember: () => updateMember,
3847
3866
  updateSpec: () => updateSpec,
3848
3867
  upsertPackSelection: () => upsertPackSelection,
3868
+ validateAsComplete: () => validateAsComplete,
3869
+ validateComponentCandidate: () => validateComponentCandidate,
3849
3870
  validateFamily: () => validateFamily,
3850
3871
  validateProject: () => validateProject,
3872
+ validateProjectConfig: () => validateProjectConfig,
3873
+ validateRegistry: () => validateRegistry,
3851
3874
  withPack: () => withPack,
3852
3875
  writeFile: () => writeFile,
3853
3876
  writeFileIfChanged: () => writeFileIfChanged,
@@ -5701,17 +5724,17 @@ function overlayKnownFields(schema, typed, onDisk) {
5701
5724
  }
5702
5725
  return typed;
5703
5726
  }
5704
- function overlayLevel(typed, onDisk, schemaFor) {
5727
+ function overlayLevel(typed, onDisk, schemaFor2) {
5705
5728
  const document = isPlainObject(onDisk) ? onDisk : {};
5706
5729
  const out = {};
5707
5730
  for (const [key, stored] of Object.entries(document)) {
5708
- const schema = schemaFor(key);
5731
+ const schema = schemaFor2(key);
5709
5732
  if (schema === null) out[key] = stored;
5710
5733
  else if (typed[key] !== void 0) out[key] = overlayKnownFields(schema, typed[key], stored);
5711
5734
  }
5712
5735
  for (const [key, value] of Object.entries(typed)) {
5713
5736
  if (hasOwn(document, key) || value === void 0) continue;
5714
- const schema = schemaFor(key);
5737
+ const schema = schemaFor2(key);
5715
5738
  if (schema !== null) out[key] = overlayKnownFields(schema, value, void 0);
5716
5739
  }
5717
5740
  return out;
@@ -15173,6 +15196,514 @@ function unpin(alias) {
15173
15196
 
15174
15197
  // src/core/openapi.ts
15175
15198
  var yaml2 = __toESM(require("js-yaml"));
15199
+ var PRIMITIVE_SCHEMAS = {
15200
+ string: { type: "string" },
15201
+ int: { type: "integer" },
15202
+ float: { type: "number" },
15203
+ bool: { type: "boolean" },
15204
+ bytes: { type: "string", contentEncoding: "base64" },
15205
+ date: { type: "string", format: "date" },
15206
+ datetime: { type: "string", format: "date-time" },
15207
+ duration: { type: "string", format: "duration" },
15208
+ any: {},
15209
+ void: {}
15210
+ };
15211
+ function expressionOf(typeRef2) {
15212
+ return parseTypeExpression(typeRef2.trim(), "returns").expression;
15213
+ }
15214
+ function closureHit(name, closureIds) {
15215
+ const local = name.split(/::|\./).pop().toLowerCase().replace(/[^a-z0-9_-]/g, "");
15216
+ return [...closureIds].find((id) => id.toLowerCase() === local);
15217
+ }
15218
+ function schemaOfExpression(expr, closureIds) {
15219
+ switch (expr.form) {
15220
+ case "primitive":
15221
+ return { ...PRIMITIVE_SCHEMAS[expr.name ?? ""] ?? {} };
15222
+ case "named":
15223
+ case "applied": {
15224
+ const hit = closureHit(expr.name ?? "", closureIds);
15225
+ return hit ? { $ref: `#/components/schemas/${hit}` } : { description: `Unresolved type: ${expr.name}` };
15226
+ }
15227
+ case "list":
15228
+ return { type: "array", items: schemaOfExpression(expr.args[0], closureIds) };
15229
+ case "set":
15230
+ return { type: "array", uniqueItems: true, items: schemaOfExpression(expr.args[0], closureIds) };
15231
+ case "map": {
15232
+ const key = expr.args[0];
15233
+ const keyHit = key.form === "named" ? closureHit(key.name ?? "", closureIds) : void 0;
15234
+ const propertyNames = keyHit ? { propertyNames: { $ref: `#/components/schemas/${keyHit}` } } : key.form === "primitive" && key.name === "int" ? { propertyNames: { pattern: "^-?[0-9]+$" } } : {};
15235
+ return { type: "object", ...propertyNames, additionalProperties: schemaOfExpression(expr.args[1], closureIds) };
15236
+ }
15237
+ case "optional": {
15238
+ const inner2 = schemaOfExpression(expr.args[0], closureIds);
15239
+ if (Object.keys(inner2).length === 0) return inner2;
15240
+ if (typeof inner2.type === "string") return { ...inner2, type: [inner2.type, "null"] };
15241
+ return { anyOf: [inner2, { type: "null" }] };
15242
+ }
15243
+ case "union":
15244
+ return { oneOf: expr.args.map((member) => schemaOfExpression(member, closureIds)) };
15245
+ case "async":
15246
+ return schemaOfExpression(expr.args[0], closureIds);
15247
+ }
15248
+ }
15249
+ function schemaFor(typeRef2, closureIds) {
15250
+ const expr = expressionOf(typeRef2);
15251
+ if (!expr) return { description: `Not a canonical type expression: ${typeRef2.trim()}` };
15252
+ return schemaOfExpression(expr, closureIds);
15253
+ }
15254
+ function returnsNothing(returns) {
15255
+ const expr = expressionOf(returns);
15256
+ const inner2 = expr?.form === "async" ? expr.args[0] : expr;
15257
+ return inner2?.form === "primitive" && inner2.name === "void";
15258
+ }
15259
+ function enumDescription(values) {
15260
+ return ["Values:", ...values.map((v) => `- \`${v.name}\`${v.description ? `: ${v.description}` : ""}`)].join("\n");
15261
+ }
15262
+ function enumValuesFrom(schema) {
15263
+ const described = /* @__PURE__ */ new Map();
15264
+ if (typeof schema.description === "string") {
15265
+ for (const line of schema.description.split("\n")) {
15266
+ const m = /^- `([^`]+)`: (.+)$/.exec(line.trim());
15267
+ if (m) described.set(m[1], m[2]);
15268
+ }
15269
+ }
15270
+ return schema.enum.filter((v) => typeof v === "string" && v.length > 0).map((name) => ({ name, ...described.has(name) ? { description: described.get(name) } : {} }));
15271
+ }
15272
+ function operationFor(method, closureIds) {
15273
+ const endpoint = method.endpoint;
15274
+ const httpVerb = endpoint && endpoint.transport === "HTTP" ? endpoint.method.toLowerCase() : "post";
15275
+ const bodyVerbs = /* @__PURE__ */ new Set(["post", "put", "patch"]);
15276
+ const params = method.params ?? [];
15277
+ const op = {
15278
+ operationId: method.name,
15279
+ summary: method.description,
15280
+ responses: {
15281
+ "200": {
15282
+ description: method.returns || "Success",
15283
+ ...method.returns && !returnsNothing(method.returns) ? { content: { "application/json": { schema: schemaFor(method.returns, closureIds) } } } : {}
15284
+ }
15285
+ }
15286
+ };
15287
+ if (method.guarantees?.length) op["x-wairon-guarantees"] = method.guarantees;
15288
+ if (method.effect) op["x-wairon-effect"] = method.effect;
15289
+ if (method.ext && Object.keys(method.ext).length) op["x-wairon-ext"] = method.ext;
15290
+ if (params.length) {
15291
+ if (bodyVerbs.has(httpVerb)) {
15292
+ op.requestBody = {
15293
+ required: true,
15294
+ content: {
15295
+ "application/json": {
15296
+ schema: {
15297
+ type: "object",
15298
+ properties: Object.fromEntries(params.map((p) => [p.name, schemaFor(p.type, closureIds)])),
15299
+ required: params.filter((p) => !p.optional).map((p) => p.name)
15300
+ }
15301
+ }
15302
+ }
15303
+ };
15304
+ } else {
15305
+ op.parameters = params.map((p) => ({
15306
+ name: p.name,
15307
+ in: "query",
15308
+ required: !p.optional,
15309
+ ...p.description ? { description: p.description } : {},
15310
+ schema: schemaFor(p.type, closureIds)
15311
+ }));
15312
+ }
15313
+ }
15314
+ return op;
15315
+ }
15316
+ var SIGNATURE_EXTENSION = "x-wairon-signature";
15317
+ function signatureComponent(t, closureIds) {
15318
+ const params = t.params ?? [];
15319
+ const returns = t.returns ?? "any";
15320
+ const text3 = `(${params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${returns}`;
15321
+ return {
15322
+ title: t.name,
15323
+ 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}.`,
15324
+ [SIGNATURE_EXTENSION]: {
15325
+ // Each type twice: as wairon wrote it (what a wairon reader decodes, exactly),
15326
+ // and as a $ref or schema (what an OpenAPI reader can follow).
15327
+ params: params.map((p) => ({
15328
+ name: p.name,
15329
+ type: p.type,
15330
+ schema: schemaFor(p.type, closureIds),
15331
+ ...p.optional ? { optional: true } : {},
15332
+ ...p.description ? { description: p.description } : {}
15333
+ })),
15334
+ returns: { type: returns, schema: schemaFor(returns, closureIds) }
15335
+ }
15336
+ };
15337
+ }
15338
+ function signatureFromComponent(id, schema) {
15339
+ const raw = schema[SIGNATURE_EXTENSION];
15340
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
15341
+ const ext = raw;
15342
+ const typeOf = (entry) => {
15343
+ if (typeof entry.type === "string" && entry.type.trim()) return entry.type;
15344
+ return typeRefFromSchema(entry.schema);
15345
+ };
15346
+ const params = (Array.isArray(ext.params) ? ext.params : []).filter((p) => !!p && typeof p === "object" && typeof p.name === "string").map((p) => ({
15347
+ name: p.name,
15348
+ type: typeOf(p),
15349
+ ...p.optional === true ? { optional: true } : {},
15350
+ ...typeof p.description === "string" ? { description: p.description } : {}
15351
+ }));
15352
+ const returns = ext.returns && typeof ext.returns === "object" && !Array.isArray(ext.returns) ? typeOf(ext.returns) : "any";
15353
+ return { id, name: typeof schema.title === "string" ? schema.title : id, kind: "signature", fields: [], params, returns };
15354
+ }
15355
+ function securitySchemeObject(auth) {
15356
+ if (auth.scheme === "none") return null;
15357
+ const desc = auth.description ? { description: auth.description } : {};
15358
+ switch (auth.scheme) {
15359
+ case "apiKey":
15360
+ return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "X-API-Key", ...desc };
15361
+ case "bearer":
15362
+ return { type: "http", scheme: "bearer", ...auth.bearerFormat ? { bearerFormat: auth.bearerFormat } : {}, ...desc };
15363
+ case "basic":
15364
+ return { type: "http", scheme: "basic", ...desc };
15365
+ case "oauth2": {
15366
+ const flow = { scopes: Object.fromEntries((auth.scopes ?? []).map((s) => [s.name, s.description])) };
15367
+ if (auth.authorizationUrl) flow.authorizationUrl = auth.authorizationUrl;
15368
+ if (auth.tokenUrl) flow.tokenUrl = auth.tokenUrl;
15369
+ if (auth.refreshUrl) flow.refreshUrl = auth.refreshUrl;
15370
+ return { type: "oauth2", flows: { [auth.flow ?? "authorizationCode"]: flow }, ...desc };
15371
+ }
15372
+ case "openIdConnect":
15373
+ return { type: "openIdConnect", openIdConnectUrl: auth.openIdConnectUrl ?? "", ...desc };
15374
+ case "custom":
15375
+ return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "Authorization", description: auth.description ?? auth.example ?? "Custom authentication scheme." };
15376
+ default:
15377
+ return null;
15378
+ }
15379
+ }
15380
+ function schemeBaseName(scheme) {
15381
+ return { apiKey: "ApiKeyAuth", bearer: "BearerAuth", basic: "BasicAuth", oauth2: "OAuth2", openIdConnect: "OpenIdConnect", custom: "CustomAuth" }[scheme] ?? "Auth";
15382
+ }
15383
+ function buildSecurity(entries) {
15384
+ const schemes = {};
15385
+ const nameByContent = /* @__PURE__ */ new Map();
15386
+ const securityByEntry = /* @__PURE__ */ new Map();
15387
+ for (const entry of entries) {
15388
+ if (!entry.auth) continue;
15389
+ const obj = securitySchemeObject(entry.auth);
15390
+ if (!obj) continue;
15391
+ const content = JSON.stringify(obj);
15392
+ let name = nameByContent.get(content);
15393
+ if (!name) {
15394
+ name = schemeBaseName(entry.auth.scheme);
15395
+ for (let n = 2; schemes[name]; n++) name = schemeBaseName(entry.auth.scheme) + n;
15396
+ schemes[name] = obj;
15397
+ nameByContent.set(content, name);
15398
+ }
15399
+ const scopeNames = entry.auth.scheme === "oauth2" ? (entry.auth.scopes ?? []).map((s) => s.name) : [];
15400
+ securityByEntry.set(entry.id, { [name]: scopeNames });
15401
+ }
15402
+ return { schemes, securityByEntry };
15403
+ }
15404
+ function httpEntriesOf(snapshot) {
15405
+ return snapshot.interfaces.filter((e) => e.type === "REST" || e.methods.some((m) => m.endpoint?.transport === "HTTP"));
15406
+ }
15407
+ function renderDoc(snapshot, entries, closureIds, opts = {}) {
15408
+ const { schemes, securityByEntry } = buildSecurity(entries);
15409
+ const paths = {};
15410
+ for (const entry of entries) {
15411
+ const security = securityByEntry.get(entry.id);
15412
+ for (const method of entry.methods) {
15413
+ const endpoint = method.endpoint;
15414
+ if (!endpoint || endpoint.transport !== "HTTP") continue;
15415
+ const p = endpoint.path.startsWith("/") ? endpoint.path : `/${endpoint.path}`;
15416
+ paths[p] = paths[p] ?? {};
15417
+ paths[p][endpoint.method.toLowerCase()] = {
15418
+ tags: [entry.id],
15419
+ ...operationFor(method, closureIds),
15420
+ ...security ? { security: [security] } : {}
15421
+ };
15422
+ }
15423
+ }
15424
+ const schemas = {};
15425
+ for (const t of snapshot.types) {
15426
+ if (t.kind === "enum") {
15427
+ const values = t.values ?? [];
15428
+ schemas[t.id] = { type: "string", title: t.name, enum: values.map((v) => v.name), description: enumDescription(values) };
15429
+ continue;
15430
+ }
15431
+ if (t.holds !== void 0) {
15432
+ schemas[t.id] = { ...schemaFor(t.holds, closureIds), title: t.name };
15433
+ continue;
15434
+ }
15435
+ schemas[t.id] = t.kind === "signature" ? signatureComponent(t, closureIds) : {
15436
+ type: "object",
15437
+ title: t.name,
15438
+ properties: Object.fromEntries(t.fields.map((f) => [f.name, schemaFor(f.type, closureIds)])),
15439
+ required: t.fields.filter((f) => !f.optional).map((f) => f.name)
15440
+ };
15441
+ }
15442
+ const components = {};
15443
+ if (Object.keys(schemas).length) components.schemas = schemas;
15444
+ if (Object.keys(schemes).length) components.securitySchemes = schemes;
15445
+ const basePaths = [...new Set(entries.map((e) => e.basePath).filter((b) => !!b))];
15446
+ const servers = opts.servers && basePaths.length === 1 ? [{ url: basePaths[0] }] : void 0;
15447
+ return {
15448
+ openapi: "3.1.0",
15449
+ info: {
15450
+ title: opts.title ?? snapshot.projectName,
15451
+ version: snapshot.version ?? "0.0.0",
15452
+ ...snapshot.stateId ? { "x-wairon-state-id": snapshot.stateId } : {},
15453
+ "x-wairon-origin": snapshot.origin,
15454
+ "x-wairon-generated-at": snapshot.generatedAt
15455
+ },
15456
+ ...servers ? { servers } : {},
15457
+ paths,
15458
+ ...Object.keys(components).length ? { components } : {}
15459
+ };
15460
+ }
15461
+ function toOpenApiSet(snapshot) {
15462
+ const closureIds = new Set(snapshot.types.map((t) => t.id));
15463
+ const byPortal = /* @__PURE__ */ new Map();
15464
+ const order2 = [];
15465
+ for (const entry of httpEntriesOf(snapshot)) {
15466
+ if (!byPortal.has(entry.component)) {
15467
+ byPortal.set(entry.component, []);
15468
+ order2.push(entry.component);
15469
+ }
15470
+ byPortal.get(entry.component).push(entry);
15471
+ }
15472
+ return order2.map((portalId) => {
15473
+ const entries = byPortal.get(portalId);
15474
+ const name = entries[0]?.name ?? portalId;
15475
+ return { portalId, name, document: JSON.stringify(renderDoc(snapshot, entries, closureIds, { title: name, servers: true }), null, 2) };
15476
+ });
15477
+ }
15478
+ function isOpenApiDocument(body) {
15479
+ try {
15480
+ const parsed = yaml2.load(body);
15481
+ return !!parsed && typeof parsed === "object" && typeof parsed.openapi === "string";
15482
+ } catch {
15483
+ return false;
15484
+ }
15485
+ }
15486
+ function isNullSchema(schema) {
15487
+ return !!schema && typeof schema === "object" && schema.type === "null";
15488
+ }
15489
+ function isTopLevelUnion(text3) {
15490
+ let depth = 0;
15491
+ for (const ch of text3) {
15492
+ if (ch === "<" || ch === "(") depth++;
15493
+ else if (ch === ">" || ch === ")") depth--;
15494
+ else if (ch === "|" && depth === 0) return true;
15495
+ }
15496
+ return false;
15497
+ }
15498
+ function typeRefFromSchema(schema) {
15499
+ if (!schema || typeof schema !== "object") return "any";
15500
+ const ref = schema.$ref;
15501
+ if (typeof ref === "string") return ref.split("/").pop() ?? "any";
15502
+ for (const key of ["anyOf", "oneOf"]) {
15503
+ const members = schema[key];
15504
+ if (!Array.isArray(members) || members.length === 0) continue;
15505
+ const nullable = members.some(isNullSchema);
15506
+ const texts = members.filter((m) => !isNullSchema(m)).map((m) => typeRefFromSchema(m));
15507
+ if (texts.length === 0 || texts.includes("any")) return "any";
15508
+ const text3 = texts.join(" | ");
15509
+ if (!nullable) return text3;
15510
+ return isTopLevelUnion(text3) ? `(${text3})?` : `${text3}?`;
15511
+ }
15512
+ const t = schema.type;
15513
+ if (Array.isArray(t)) {
15514
+ const types = t.filter((x) => typeof x === "string");
15515
+ const nonNull = types.filter((x) => x !== "null");
15516
+ if (nonNull.length !== 1) return "any";
15517
+ const inner2 = typeRefFromSchema({ ...schema, type: nonNull[0] });
15518
+ return types.includes("null") && inner2 !== "any" ? `${inner2}?` : inner2;
15519
+ }
15520
+ if (t === "array") {
15521
+ const elem = typeRefFromSchema(schema.items);
15522
+ return schema.uniqueItems === true ? `set<${elem}>` : `list<${elem}>`;
15523
+ }
15524
+ if (t === "integer") return "int";
15525
+ if (t === "number") return "float";
15526
+ if (t === "boolean") return "bool";
15527
+ if (t === "string") {
15528
+ if (schema.format === "date") return "date";
15529
+ if (schema.format === "date-time") return "datetime";
15530
+ if (schema.format === "duration") return "duration";
15531
+ if (schema.contentEncoding === "base64" || schema.format === "byte" || schema.format === "binary") return "bytes";
15532
+ return "string";
15533
+ }
15534
+ if (t === "object" || t === void 0) {
15535
+ const additional = schema.additionalProperties;
15536
+ if (additional && typeof additional === "object") {
15537
+ const names = schema.propertyNames;
15538
+ const key = names && typeof names.$ref === "string" ? typeRefFromSchema(names) : names && typeof names.pattern === "string" && names.pattern.includes("[0-9]") ? "int" : "string";
15539
+ return `map<${key}, ${typeRefFromSchema(additional)}>`;
15540
+ }
15541
+ if (additional === true) return "map<string, any>";
15542
+ }
15543
+ return "any";
15544
+ }
15545
+ var SCALAR_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean"]);
15546
+ function inlineEnumNote(schema) {
15547
+ if (!schema || schema.$ref !== void 0 || !Array.isArray(schema.enum)) return void 0;
15548
+ const values = schema.enum.filter((v) => typeof v === "string" || typeof v === "number");
15549
+ return values.length ? `One of: ${values.join(", ")}.` : void 0;
15550
+ }
15551
+ function describedWith(description, note) {
15552
+ const text3 = [description, note].filter((x) => !!x).join(" ");
15553
+ return text3 ? { description: text3 } : {};
15554
+ }
15555
+ function authFromSecurityScheme(scheme) {
15556
+ const desc = typeof scheme.description === "string" ? { description: scheme.description } : {};
15557
+ if (scheme.type === "apiKey") {
15558
+ return {
15559
+ scheme: "apiKey",
15560
+ ...scheme.in === "header" || scheme.in === "query" || scheme.in === "cookie" ? { in: scheme.in } : {},
15561
+ ...typeof scheme.name === "string" ? { name: scheme.name } : {},
15562
+ ...desc
15563
+ };
15564
+ }
15565
+ if (scheme.type === "http") {
15566
+ if (scheme.scheme === "bearer") return { scheme: "bearer", ...typeof scheme.bearerFormat === "string" ? { bearerFormat: scheme.bearerFormat } : {}, ...desc };
15567
+ if (scheme.scheme === "basic") return { scheme: "basic", ...desc };
15568
+ }
15569
+ if (scheme.type === "oauth2") {
15570
+ const flows = scheme.flows ?? {};
15571
+ const flowKey = Object.keys(flows)[0];
15572
+ const f = flows[flowKey] ?? {};
15573
+ return {
15574
+ scheme: "oauth2",
15575
+ ...flowKey === "authorizationCode" || flowKey === "clientCredentials" || flowKey === "implicit" || flowKey === "password" ? { flow: flowKey } : {},
15576
+ ...typeof f.authorizationUrl === "string" ? { authorizationUrl: f.authorizationUrl } : {},
15577
+ ...typeof f.tokenUrl === "string" ? { tokenUrl: f.tokenUrl } : {},
15578
+ ...typeof f.refreshUrl === "string" ? { refreshUrl: f.refreshUrl } : {},
15579
+ scopes: Object.entries(f.scopes ?? {}).map(([name, description]) => ({ name, description })),
15580
+ ...desc
15581
+ };
15582
+ }
15583
+ if (scheme.type === "openIdConnect") {
15584
+ return { scheme: "openIdConnect", openIdConnectUrl: typeof scheme.openIdConnectUrl === "string" ? scheme.openIdConnectUrl : "", ...desc };
15585
+ }
15586
+ return void 0;
15587
+ }
15588
+ function fromOpenApi(document, projectName) {
15589
+ let parsed;
15590
+ try {
15591
+ parsed = yaml2.load(document);
15592
+ } catch (e) {
15593
+ throw new Error(`Invalid surface document: not parseable as JSON/YAML (${e instanceof Error ? e.message : String(e)})`);
15594
+ }
15595
+ if (!parsed || typeof parsed !== "object" || typeof parsed.openapi !== "string" || typeof parsed.paths !== "object") {
15596
+ throw new Error('Invalid surface document: missing OpenAPI "openapi"/"paths" structure.');
15597
+ }
15598
+ const info = parsed.info ?? {};
15599
+ const methods = [];
15600
+ for (const [rawPath, ops] of Object.entries(parsed.paths)) {
15601
+ for (const [verb, opRaw] of Object.entries(ops ?? {})) {
15602
+ if (!["get", "post", "put", "delete", "patch", "options", "head"].includes(verb)) continue;
15603
+ const op = opRaw ?? {};
15604
+ const name = typeof op.operationId === "string" && /^[a-zA-Z0-9_]+$/.test(op.operationId) ? op.operationId : `${verb}_${rawPath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
15605
+ const params = [];
15606
+ for (const p of op.parameters ?? []) {
15607
+ if (typeof p.name !== "string") continue;
15608
+ const schema = p.schema;
15609
+ params.push({
15610
+ name: p.name,
15611
+ type: typeRefFromSchema(schema),
15612
+ ...p.required === true ? {} : { optional: true },
15613
+ ...describedWith(typeof p.description === "string" ? p.description : void 0, inlineEnumNote(schema))
15614
+ });
15615
+ }
15616
+ const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
15617
+ if (bodySchema) {
15618
+ const props = bodySchema.properties ?? {};
15619
+ const required = new Set(bodySchema.required ?? []);
15620
+ if (Object.keys(props).length) {
15621
+ for (const [pname, pschema] of Object.entries(props)) {
15622
+ params.push({
15623
+ name: pname,
15624
+ type: typeRefFromSchema(pschema),
15625
+ ...required.has(pname) ? {} : { optional: true },
15626
+ ...describedWith(typeof pschema.description === "string" ? pschema.description : void 0, inlineEnumNote(pschema))
15627
+ });
15628
+ }
15629
+ } else {
15630
+ params.push({ name: "body", type: typeRefFromSchema(bodySchema) });
15631
+ }
15632
+ }
15633
+ const okResponse = op.responses?.["200"] ?? op.responses?.["201"];
15634
+ const responseSchema = okResponse?.content?.["application/json"]?.schema;
15635
+ const returns = responseSchema ? typeRefFromSchema(responseSchema) : "void";
15636
+ const rawGuarantees = op["x-wairon-guarantees"];
15637
+ const guarantees = Array.isArray(rawGuarantees) ? rawGuarantees.filter((g) => typeof g === "string" && g.length > 0) : [];
15638
+ const rawEffect = op["x-wairon-effect"];
15639
+ const effect = rawEffect === "read" || rawEffect === "write" || rawEffect === "lifecycle" ? rawEffect : void 0;
15640
+ const rawExt = op["x-wairon-ext"];
15641
+ const ext = rawExt && typeof rawExt === "object" && !Array.isArray(rawExt) ? rawExt : void 0;
15642
+ methods.push({
15643
+ name,
15644
+ description: typeof op.summary === "string" ? op.summary : typeof op.description === "string" ? op.description : name,
15645
+ signature: `${name}(${params.map((p) => `${p.name}: ${p.type}`).join(", ")}): ${returns}`,
15646
+ returns,
15647
+ params,
15648
+ endpoint: { transport: "HTTP", method: verb.toUpperCase(), path: rawPath },
15649
+ ...guarantees.length ? { guarantees } : {},
15650
+ ...effect ? { effect } : {},
15651
+ ...ext ? { ext } : {}
15652
+ });
15653
+ }
15654
+ }
15655
+ const types = [];
15656
+ const schemas = parsed.components?.schemas ?? {};
15657
+ for (const [id, schema] of Object.entries(schemas)) {
15658
+ const signature = signatureFromComponent(id, schema);
15659
+ if (signature) {
15660
+ types.push(signature);
15661
+ continue;
15662
+ }
15663
+ if (schema.type === "string" && Array.isArray(schema.enum)) {
15664
+ types.push({ id, name: typeof schema.title === "string" ? schema.title : id, kind: "enum", fields: [], values: enumValuesFrom(schema) });
15665
+ continue;
15666
+ }
15667
+ if (typeof schema.type === "string" && SCALAR_SCHEMA_TYPES.has(schema.type) && schema.properties === void 0) {
15668
+ types.push({ id, name: typeof schema.title === "string" ? schema.title : id, kind: "value-object", fields: [], holds: typeRefFromSchema(schema) });
15669
+ continue;
15670
+ }
15671
+ const props = schema.properties ?? {};
15672
+ const required = new Set(schema.required ?? []);
15673
+ types.push({
15674
+ id,
15675
+ name: typeof schema.title === "string" ? schema.title : id,
15676
+ kind: "value-object",
15677
+ fields: Object.entries(props).map(([fname, fschema]) => ({
15678
+ name: fname,
15679
+ type: typeRefFromSchema(fschema),
15680
+ ...required.has(fname) ? {} : { optional: true }
15681
+ }))
15682
+ });
15683
+ }
15684
+ const securitySchemes = parsed.components?.securitySchemes ?? {};
15685
+ const firstScheme = Object.values(securitySchemes)[0];
15686
+ const importedAuth = firstScheme ? authFromSecurityScheme(firstScheme) : void 0;
15687
+ const entry = {
15688
+ id: `${projectName}-api`,
15689
+ name: typeof info.title === "string" ? info.title : projectName,
15690
+ audience: "external",
15691
+ type: "REST",
15692
+ component: `${projectName}-api`,
15693
+ methods,
15694
+ details: typeof info.description === "string" ? info.description : `Imported OpenAPI surface of ${projectName}.`,
15695
+ ...typeof info.version === "string" ? { version: info.version } : {},
15696
+ ...importedAuth ? { auth: importedAuth } : {}
15697
+ };
15698
+ return SurfaceSnapshotSchema.parse({
15699
+ projectName,
15700
+ origin: "authored",
15701
+ ...typeof info.version === "string" ? { version: info.version } : {},
15702
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
15703
+ interfaces: [entry],
15704
+ types
15705
+ });
15706
+ }
15176
15707
 
15177
15708
  // src/config/defaults.ts
15178
15709
  var os2 = __toESM(require("os"));
@@ -15192,7 +15723,7 @@ function defaultTargetConfig(type) {
15192
15723
  enabled: true
15193
15724
  };
15194
15725
  }
15195
- var WAIRON_VERSION = "5.1.1-dev.98";
15726
+ var WAIRON_VERSION = "5.1.1-dev.99";
15196
15727
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
15197
15728
  var ARCHITECT_AGENT_ID = "agent-architect";
15198
15729
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -15814,9 +16345,32 @@ function listSnapshots() {
15814
16345
  }
15815
16346
  return out;
15816
16347
  }
16348
+ function getSnapshot(projectName) {
16349
+ return listSnapshots().find((s) => s.projectName === projectName) ?? null;
16350
+ }
15817
16351
  function snapshotFilename(projectName) {
15818
16352
  return `${safeFilenamePart(projectName)}.yaml`;
15819
16353
  }
16354
+ function writeSnapshotIfChanged(snapshot, rootDir) {
16355
+ const dir = surfacesDir(rootDir);
16356
+ fs9.mkdirSync(dir, { recursive: true });
16357
+ const p = path14.join(dir, snapshotFilename(snapshot.projectName));
16358
+ const next = SurfaceSnapshotSchema.parse(snapshot);
16359
+ if (fs9.existsSync(p)) {
16360
+ try {
16361
+ const existing = SurfaceSnapshotSchema.parse(readYamlFile(p));
16362
+ if (surfaceContentKey(existing) === surfaceContentKey(next)) {
16363
+ return { path: p, changed: false };
16364
+ }
16365
+ } catch {
16366
+ }
16367
+ }
16368
+ writeYamlFile(p, next);
16369
+ return { path: p, changed: true };
16370
+ }
16371
+ function saveSnapshot(snapshot) {
16372
+ return writeSnapshotIfChanged(snapshot, getProjectRoot()).path;
16373
+ }
15820
16374
  function removeSnapshot(projectName) {
15821
16375
  const dir = surfacesDir(getProjectRoot());
15822
16376
  const direct = path14.join(dir, snapshotFilename(projectName));
@@ -15866,6 +16420,53 @@ function listPinnedExternals() {
15866
16420
  return { alias, project: entry.project, entry, snapshot };
15867
16421
  });
15868
16422
  }
16423
+ function selectPortalSpec(renderedSet, portalId) {
16424
+ const hit = renderedSet.find((spec) => spec.portalId === portalId);
16425
+ if (!hit) {
16426
+ const known = renderedSet.map((s) => s.portalId).join(", ");
16427
+ throw new Error(`Unknown portal "${portalId}" \u2014 this surface renders: ${known || "(no portals)"}.`);
16428
+ }
16429
+ return [hit];
16430
+ }
16431
+ function perPortalPath(resolvedOut, portalId) {
16432
+ const ext = path14.extname(resolvedOut);
16433
+ const stem = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
16434
+ return `${stem}.${safeFilenamePart(portalId)}${ext}`;
16435
+ }
16436
+ function writeSurfaceFile(targetPath, snapshot, renderedSet) {
16437
+ const resolved = path14.resolve(targetPath);
16438
+ fs9.mkdirSync(path14.dirname(resolved), { recursive: true });
16439
+ if (!renderedSet || renderedSet.length === 0) {
16440
+ writeYamlFile(resolved, snapshot);
16441
+ return [resolved];
16442
+ }
16443
+ if (renderedSet.length === 1) {
16444
+ fs9.writeFileSync(resolved, renderedSet[0].document);
16445
+ return [resolved];
16446
+ }
16447
+ return renderedSet.map((spec) => {
16448
+ const target = perPortalPath(resolved, spec.portalId);
16449
+ fs9.writeFileSync(target, spec.document);
16450
+ return target;
16451
+ });
16452
+ }
16453
+ function exportResult(snapshot, renderedSet, writtenPaths) {
16454
+ const rendered = renderedSet?.length === 1 ? renderedSet[0].document : void 0;
16455
+ return {
16456
+ snapshot,
16457
+ ...rendered !== void 0 ? { rendered } : {},
16458
+ ...renderedSet ? { renderedSet } : {},
16459
+ ...writtenPaths.length === 1 ? { writtenTo: writtenPaths[0] } : {},
16460
+ ...writtenPaths.length ? { writtenPaths } : {}
16461
+ };
16462
+ }
16463
+ function exportSurface(maxAudience, format, outPath, portalId) {
16464
+ const snapshot = projectOwnSurface(maxAudience);
16465
+ let renderedSet = format === "openapi" ? toOpenApiSet(snapshot) : void 0;
16466
+ if (renderedSet && portalId) renderedSet = selectPortalSpec(renderedSet, portalId);
16467
+ const writtenPaths = outPath ? writeSurfaceFile(outPath, snapshot, renderedSet) : [];
16468
+ return exportResult(snapshot, renderedSet, writtenPaths);
16469
+ }
15869
16470
  function withSortedKeys(value) {
15870
16471
  if (Array.isArray(value)) return value.map(withSortedKeys);
15871
16472
  if (value && typeof value === "object") {
@@ -15892,6 +16493,28 @@ function exportDesign2(outPath, approval) {
15892
16493
  if (outPath) writeDesignTo(outPath, design);
15893
16494
  return design;
15894
16495
  }
16496
+ function readSurfaceDocument(sourcePath) {
16497
+ const resolved = path14.resolve(sourcePath);
16498
+ if (!fs9.existsSync(resolved)) {
16499
+ throw new Error(`Surface document not found: ${resolved}`);
16500
+ }
16501
+ return fs9.readFileSync(resolved, "utf8");
16502
+ }
16503
+ function importSurface(sourcePath, origin) {
16504
+ const resolved = path14.resolve(sourcePath);
16505
+ const body = readSurfaceDocument(resolved);
16506
+ let snapshot;
16507
+ if (isOpenApiDocument(body)) {
16508
+ const projectName = path14.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
16509
+ snapshot = fromOpenApi(body, projectName);
16510
+ snapshot = { ...snapshot, origin };
16511
+ } else {
16512
+ snapshot = SurfaceSnapshotSchema.parse(parseYaml(body, resolved));
16513
+ snapshot = { ...snapshot, origin };
16514
+ }
16515
+ saveSnapshot(snapshot);
16516
+ return snapshot;
16517
+ }
15895
16518
  function listFamilyPins() {
15896
16519
  const snapshots = listSnapshots();
15897
16520
  const pins = snapshots.filter((snapshot) => snapshot.origin === "generated").map((snapshot) => ({
@@ -16162,20 +16785,30 @@ function listExternals() {
16162
16785
  };
16163
16786
  });
16164
16787
  }
16788
+ function surfaceContentKey(snapshot) {
16789
+ const { stateId, generatedAt, origin, ...content } = snapshot;
16790
+ return JSON.stringify(content);
16791
+ }
16165
16792
 
16166
16793
  // src/core/surface-portal.ts
16794
+ function exportSurface2(maxAudience, format, outPath, portalId) {
16795
+ return exportSurface(maxAudience, format, outPath, portalId);
16796
+ }
16167
16797
  function exportDesign3(outPath, approval) {
16168
16798
  return exportDesign2(outPath, approval);
16169
16799
  }
16800
+ function importSurface2(sourcePath, origin) {
16801
+ return importSurface(sourcePath, origin);
16802
+ }
16170
16803
  function listSnapshots2() {
16171
16804
  return listSnapshots();
16172
16805
  }
16806
+ function getSnapshot2(projectName) {
16807
+ return getSnapshot(projectName);
16808
+ }
16173
16809
  function removeSnapshot2(projectName) {
16174
16810
  return removeSnapshot(projectName);
16175
16811
  }
16176
- function listFamilyPins2() {
16177
- return listFamilyPins();
16178
- }
16179
16812
  function pinExternals2(aliases) {
16180
16813
  return pinExternals(aliases);
16181
16814
  }
@@ -16188,12 +16821,6 @@ function listExternals2() {
16188
16821
  function listPinnedExternals2() {
16189
16822
  return listPinnedExternals();
16190
16823
  }
16191
- function renamePin2(alias, newAlias, project) {
16192
- return renamePin(alias, newAlias, project);
16193
- }
16194
- function unpin2(alias) {
16195
- return unpin(alias);
16196
- }
16197
16824
  function pinnedParent2() {
16198
16825
  return pinnedParent();
16199
16826
  }
@@ -16711,6 +17338,86 @@ function projectApprovals(depth) {
16711
17338
  }
16712
17339
  return entries;
16713
17340
  }
17341
+ function relationReach(root) {
17342
+ const granted = getRequestParentReach();
17343
+ if (!granted) return null;
17344
+ const lookup2 = getHostedLookup();
17345
+ const base = granted.parentReach ? { topRoot: granted.topRoot ?? root, parentReach: true, ...granted.narrowed ? { narrowed: true } : {} } : { topRoot: root, parentReach: true, narrowed: true };
17346
+ return { ...base, ...lookup2 ? { hostedLookup: lookup2 } : {} };
17347
+ }
17348
+ function outsideReach(node) {
17349
+ const lookup2 = getHostedLookup();
17350
+ if (!lookup2 || node.namespace === "") return null;
17351
+ return lookup2(node.id ?? node.namespace) === null ? `the project "${node.id ?? node.namespace}" lies outside this request's reach` : null;
17352
+ }
17353
+ function referenceUse(ref) {
17354
+ return ref.publicName ?? ref.resolved.split("::").pop() ?? ref.authored;
17355
+ }
17356
+ function failureAt(ref, failures) {
17357
+ const site = `${ref.specId} (${ref.position})`;
17358
+ const last = ref.resolved.split("::").pop() ?? "";
17359
+ return failures.find((i) => i.resolution.callSite === site && (i.resolution.canonicalTarget === ref.authored || (i.resolution.canonicalTarget ?? "").endsWith(`::${last}`)));
17360
+ }
17361
+ function containedStatus(child, refs, failures) {
17362
+ const base = { alias: child.mountAlias, project: child.id ?? child.namespace, sourceKind: "family", pinned: false };
17363
+ const unreachable = outsideReach(child);
17364
+ if (unreachable) {
17365
+ const uses2 = [...new Set(refs.map(referenceUse))].map((name) => ({ publicName: name, state: "unavailable", code: "EXTERNAL_CHECK_UNAVAILABLE", detail: unreachable }));
17366
+ return { ...base, reachable: false, stale: false, outOfReach: true, uses: uses2, detail: unreachable };
17367
+ }
17368
+ const uses = [];
17369
+ for (const ref of refs) {
17370
+ const failed = failureAt(ref, failures);
17371
+ const use = failed ? { publicName: referenceUse(ref), state: "removed", code: failed.code, detail: failed.resolution.reason } : { publicName: referenceUse(ref), state: "unchanged" };
17372
+ if (!uses.some((u) => u.publicName === use.publicName && u.state === use.state)) uses.push(use);
17373
+ }
17374
+ return { ...base, reachable: true, stale: uses.some((u) => u.state === "removed"), uses, detail: "a contained member, read live \u2014 nothing is pinned" };
17375
+ }
17376
+ function containedRelations(answered) {
17377
+ const own = graph();
17378
+ const consumed = own.nodes.filter((n) => n.parent === "" && n.mountAlias !== void 0 && !answered.some((s) => s.alias === n.mountAlias)).map((child) => ({
17379
+ child,
17380
+ refs: own.authoredReferences.filter((r) => (own.owners.get(r.specId) ?? "") === "" && r.producer === child.namespace && r.binding !== "local")
17381
+ })).filter((c) => c.refs.length > 0);
17382
+ if (consumed.length === 0) return [];
17383
+ const config = configOrNull();
17384
+ const failures = validateProject({ rules: config?.rules, projectType: config?.projectType }).issues.filter((i) => i.resolution !== void 0 && i.resolution.outcome !== "resolved");
17385
+ return consumed.map(({ child, refs }) => containedStatus(child, refs, failures));
17386
+ }
17387
+ function relationsAt(key, dir, reach2) {
17388
+ const bound2 = (fn) => reach2 === null ? runWithProjectRoot(dir, fn) : runWithProjectBinding(dir, reach2, fn);
17389
+ return bound2(() => {
17390
+ const statuses = getExternalsStatus2();
17391
+ const contained = containedRelations(statuses);
17392
+ return { project: key, externals: [...statuses, ...contained], comparedAt: (/* @__PURE__ */ new Date()).toISOString() };
17393
+ });
17394
+ }
17395
+ function unopened(key, detail) {
17396
+ return { project: key, externals: [], comparedAt: (/* @__PURE__ */ new Date()).toISOString(), detail };
17397
+ }
17398
+ function familyRelations() {
17399
+ const family = graph();
17400
+ const root = path15.resolve(getProjectRoot());
17401
+ const reach2 = relationReach(root);
17402
+ const out = [];
17403
+ for (const node of family.nodes) {
17404
+ const unreachable = outsideReach(node);
17405
+ if (unreachable || !fs10.existsSync(node.directory)) {
17406
+ out.push(unopened(node.namespace, unreachable ?? `its root ${node.directory} does not exist`));
17407
+ continue;
17408
+ }
17409
+ out.push(relationsAt(node.namespace, node.directory, reach2));
17410
+ }
17411
+ for (const ref of referencedOf(family.nodes)) {
17412
+ if (ref.directory === void 0) {
17413
+ out.push(unopened(ref.key, ref.problem ?? "its root could not be located"));
17414
+ continue;
17415
+ }
17416
+ const lookup2 = getHostedLookup();
17417
+ out.push(relationsAt(ref.key, ref.directory, { topRoot: ref.directory, parentReach: true, narrowed: true, ...lookup2 ? { hostedLookup: lookup2 } : {} }));
17418
+ }
17419
+ return out;
17420
+ }
16714
17421
  function requiredBy(key) {
16715
17422
  return key === "" ? "the family root" : `"${key}"`;
16716
17423
  }
@@ -27650,6 +28357,11 @@ function measure(candidate) {
27650
28357
  after: totals(next, after)
27651
28358
  };
27652
28359
  }
28360
+ function doctrine(manifest) {
28361
+ const baseline = defaultsBaseline();
28362
+ const changes = doctrineChanges(manifest, baseline);
28363
+ return { pack: manifest.name, ...manifest.version ? { version: manifest.version } : {}, changes };
28364
+ }
27653
28365
 
27654
28366
  // src/core/rules/gate-identity.ts
27655
28367
  var crypto4 = __toESM(require("crypto"));
@@ -27737,6 +28449,16 @@ function computeGateIdentity(content, doctrine2, builtinRules, inputs, gate, mem
27737
28449
  const digest3 = crypto4.createHash("sha256").update(canonicalize(payload)).digest("hex");
27738
28450
  return { algorithm: GATE_ALGORITHM, digest: digest3 };
27739
28451
  }
28452
+ function analyzerDigest(builtinRules, gate) {
28453
+ const codeCodes = codeJudgedCodes(builtinRules);
28454
+ const overrides = Object.entries(gate.rules?.sddRuleSeverity ?? {}).filter(([code]) => codeCodes.has(code));
28455
+ const payload = {
28456
+ rules: ruleIdentities(builtinRules.filter(judgesCode)),
28457
+ conformance: gate.rules?.conformance ?? null,
28458
+ severities: byKey2(overrides, ([code]) => code).map(([code, severity]) => ({ code, severity }))
28459
+ };
28460
+ return crypto4.createHash("sha256").update(canonicalize(payload)).digest("hex");
28461
+ }
27740
28462
 
27741
28463
  // src/core/validation.ts
27742
28464
  function projectPackSelections() {
@@ -27832,6 +28554,29 @@ function checkOverlappingOwnership(agents, issues) {
27832
28554
  }
27833
28555
  }
27834
28556
  }
28557
+ function validateProjectConfig(config) {
28558
+ const issues = [];
28559
+ if (config.targets.length === 0) {
28560
+ issues.push(issue("error", "NO_TARGETS", "No output targets configured in project.yaml"));
28561
+ }
28562
+ const enabled = config.targets.filter((t) => {
28563
+ if (typeof t === "string") return true;
28564
+ return t.enabled !== false;
28565
+ });
28566
+ if (enabled.length === 0) {
28567
+ issues.push(issue("error", "NO_ENABLED_TARGETS", "All configured targets are disabled"));
28568
+ }
28569
+ return {
28570
+ valid: issues.every((i) => i.severity !== "error"),
28571
+ issues
28572
+ };
28573
+ }
28574
+ function listRules2() {
28575
+ const extensions = loadProjectExtensions();
28576
+ registerBuiltinRules();
28577
+ registerPackRules(extensions.rules);
28578
+ return ruleSequence();
28579
+ }
27835
28580
  function memberOwning(owners, memberKeys, key) {
27836
28581
  const owner = owners.get(key);
27837
28582
  if (owner !== void 0) return owner === "" ? void 0 : owner;
@@ -28171,12 +28916,45 @@ function settledStatusBearing(loaded) {
28171
28916
  take(loaded.implementations, index.paths.implementation);
28172
28917
  return out;
28173
28918
  }
28919
+ function validateAsComplete(options) {
28920
+ const { result, codeModel } = runOwnersGate({ ...options ?? {}, treatAllAsComplete: true });
28921
+ const sequence = ruleSequence();
28922
+ const doctrineDigest = analyzerDigest(sequence, { rules: options?.rules ?? projectRules() });
28923
+ return { ...result, analysis: codeAnalysisOf(result, sequence, codeModel, doctrineDigest) };
28924
+ }
28925
+ function projectRules() {
28926
+ try {
28927
+ return loadProjectConfig()?.rules;
28928
+ } catch {
28929
+ return void 0;
28930
+ }
28931
+ }
28932
+ var GRADE_ORDER = ["generic", "pattern", "exact"];
28933
+ function weakestGrade(codeModel) {
28934
+ const grades = codeModel.files.filter((f) => f.status === "analyzed" && f.analysisGrade).map((f) => GRADE_ORDER.indexOf(f.analysisGrade));
28935
+ return grades.length ? GRADE_ORDER[Math.min(...grades)] : "none";
28936
+ }
28937
+ function codeAnalysisOf(result, sequence, codeModel, doctrineDigest) {
28938
+ const codes = [...new Set(sequence.filter(judgesCode).flatMap((r) => r.codes.map((c) => c.code)))].sort();
28939
+ const codeSet = new Set(codes);
28940
+ const count = (severity) => result.issues.filter((i) => codeSet.has(i.code) && i.severity === severity).length;
28941
+ return {
28942
+ analyzer: { validatorVersion: WAIRON_VERSION, doctrineDigest, grade: weakestGrade(codeModel) },
28943
+ codes,
28944
+ errors: count("error"),
28945
+ warnings: count("warning"),
28946
+ notices: count("notice")
28947
+ };
28948
+ }
28174
28949
  function validateFamily(options) {
28175
28950
  return run(options);
28176
28951
  }
28177
28952
  function measurePackImpact(candidate) {
28178
28953
  return measure(candidate);
28179
28954
  }
28955
+ function measurePackDoctrine(manifest) {
28956
+ return doctrine(manifest);
28957
+ }
28180
28958
  function builtinProfileIds() {
28181
28959
  return [...BUILTIN_PROFILES];
28182
28960
  }
@@ -28220,6 +28998,9 @@ function directMemberSubjects() {
28220
28998
  function familyApprovals(depth) {
28221
28999
  return projectApprovals(depth);
28222
29000
  }
29001
+ function familyRelations2() {
29002
+ return familyRelations();
29003
+ }
28223
29004
 
28224
29005
  // src/core/diagram.ts
28225
29006
  function renderDiagram(format, relations) {
@@ -39957,6 +40738,17 @@ var path47 = __toESM(require("path"));
39957
40738
  // src/migrations/chaining-migration.ts
39958
40739
  var path42 = __toESM(require("path"));
39959
40740
 
40741
+ // src/core/pin-maintenance-portal.ts
40742
+ function renamePin2(alias, newAlias, project) {
40743
+ return renamePin(alias, newAlias, project);
40744
+ }
40745
+ function unpin2(alias) {
40746
+ return unpin(alias);
40747
+ }
40748
+ function listFamilyPins2() {
40749
+ return listFamilyPins();
40750
+ }
40751
+
39960
40752
  // src/migrations/position-migration.ts
39961
40753
  var path41 = __toESM(require("path"));
39962
40754
 
@@ -45266,6 +46058,8 @@ function loadProjectConfig3() {
45266
46058
  buildCanvasDataModel,
45267
46059
  buildProjectGraph,
45268
46060
  buildRuleContext,
46061
+ builtinProfileIds,
46062
+ builtinProjectKinds,
45269
46063
  callSitesOf,
45270
46064
  canonicalTypeRef,
45271
46065
  canonicalTypeText,
@@ -45276,6 +46070,7 @@ function loadProjectConfig3() {
45276
46070
  complexityLevel,
45277
46071
  composeAgentBrief,
45278
46072
  composeRuleSequence,
46073
+ computeGateStateId,
45279
46074
  computeOwnStateId,
45280
46075
  computeStateId,
45281
46076
  computeStateIdAt,
@@ -45327,12 +46122,15 @@ function loadProjectConfig3() {
45327
46122
  excerptParent,
45328
46123
  exportDesign,
45329
46124
  exportSpecTree,
46125
+ exportSurface,
45330
46126
  exportTargetKey,
45331
46127
  exportUsage,
45332
46128
  externalizeSubsystem,
45333
46129
  extractTypeIdentifiers,
45334
46130
  factsFor,
46131
+ familyApprovals,
45335
46132
  familyNode,
46133
+ familyRelations,
45336
46134
  fieldTypeRefs,
45337
46135
  fieldTypesOf,
45338
46136
  findChainingParent,
@@ -45340,6 +46138,7 @@ function loadProjectConfig3() {
45340
46138
  findLegacySpecFiles,
45341
46139
  findProjectRoot,
45342
46140
  findSystemRoot,
46141
+ findTestsReferencing,
45343
46142
  fromProjectRoot,
45344
46143
  generateAgent,
45345
46144
  generateAll,
@@ -45347,12 +46146,14 @@ function loadProjectConfig3() {
45347
46146
  generateDiagramSet,
45348
46147
  generateSequenceDiagram,
45349
46148
  getExporter,
46149
+ getExternalsStatus,
45350
46150
  getHostedLookup,
45351
46151
  getLoaderIssues,
45352
46152
  getProjectRoot,
45353
46153
  getProjectRootOverride,
45354
46154
  getRequestParentReach,
45355
46155
  getRequestProjectRoot,
46156
+ getSnapshot,
45356
46157
  getStatusReport,
45357
46158
  getWriteReach,
45358
46159
  globalGuideFilePath,
@@ -45367,6 +46168,7 @@ function loadProjectConfig3() {
45367
46168
  importBindingOf,
45368
46169
  importNames,
45369
46170
  importSpecTree,
46171
+ importSurface,
45370
46172
  injectGuide,
45371
46173
  installPackFromDirectory,
45372
46174
  interfaceCanonicalTypes,
@@ -45387,9 +46189,13 @@ function loadProjectConfig3() {
45387
46189
  landReference,
45388
46190
  languageOfSourcePath,
45389
46191
  listDirectChainedSubprojects,
46192
+ listExternals,
45390
46193
  listFiles,
45391
46194
  listFilesRecursive,
45392
46195
  listInstalledPacks,
46196
+ listPinnedExternals,
46197
+ listRules,
46198
+ listSnapshots,
45393
46199
  loadComponentSpec,
45394
46200
  loadComponentSpecs,
45395
46201
  loadExtensionPacks,
@@ -45418,6 +46224,8 @@ function loadProjectConfig3() {
45418
46224
  makeScopeFilter,
45419
46225
  markSelectionsBundled,
45420
46226
  matchTypeRef,
46227
+ measurePackDoctrine,
46228
+ measurePackImpact,
45421
46229
  memberDeclarationOf,
45422
46230
  memberDigest,
45423
46231
  memberLocationOf,
@@ -45446,7 +46254,9 @@ function loadProjectConfig3() {
45446
46254
  passesIntentFloor,
45447
46255
  pathExists,
45448
46256
  pathKey,
46257
+ pinExternals,
45449
46258
  pinInstalledPacksAsSelections,
46259
+ pinnedParent,
45450
46260
  producerOf,
45451
46261
  projectConfigExists,
45452
46262
  projectFamily,
@@ -45471,6 +46281,7 @@ function loadProjectConfig3() {
45471
46281
  removeExternal,
45472
46282
  removeMember,
45473
46283
  removePackSelection,
46284
+ removeSnapshot,
45474
46285
  renameAlias,
45475
46286
  renameComponent,
45476
46287
  renameId,
@@ -45541,8 +46352,12 @@ function loadProjectConfig3() {
45541
46352
  updateMember,
45542
46353
  updateSpec,
45543
46354
  upsertPackSelection,
46355
+ validateAsComplete,
46356
+ validateComponentCandidate,
45544
46357
  validateFamily,
45545
46358
  validateProject,
46359
+ validateProjectConfig,
46360
+ validateRegistry,
45546
46361
  withPack,
45547
46362
  writeFile,
45548
46363
  writeFileIfChanged,