@wairon/cli 5.1.1-dev.79 → 5.1.1-dev.80
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 +862 -209
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +789 -136
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +2 -2
- package/dist/templates/variants/logic-shapes.yaml +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3451,7 +3451,9 @@ __export(src_exports, {
|
|
|
3451
3451
|
NarrativeStepTypeSchema: () => NarrativeStepTypeSchema,
|
|
3452
3452
|
OutputTargetSchema: () => OutputTargetSchema,
|
|
3453
3453
|
PATTERN_TYPES: () => PATTERN_TYPES,
|
|
3454
|
+
PROJECT_ID_RE: () => PROJECT_ID_RE,
|
|
3454
3455
|
PROJECT_KINDS: () => PROJECT_KINDS,
|
|
3456
|
+
PUBLIC_NAME_RE: () => PUBLIC_NAME_RE,
|
|
3455
3457
|
PackSelectionSchema: () => PackSelectionSchema,
|
|
3456
3458
|
ParallelBranchSchema: () => ParallelBranchSchema,
|
|
3457
3459
|
PathsConfigSchema: () => PathsConfigSchema,
|
|
@@ -3486,6 +3488,7 @@ __export(src_exports, {
|
|
|
3486
3488
|
SurfaceOriginSchema: () => SurfaceOriginSchema,
|
|
3487
3489
|
SurfaceSnapshotSchema: () => SurfaceSnapshotSchema,
|
|
3488
3490
|
SurfaceTypeDefSchema: () => SurfaceTypeDefSchema,
|
|
3491
|
+
SurfaceTypeExportSchema: () => SurfaceTypeExportSchema,
|
|
3489
3492
|
SwitchCaseSchema: () => SwitchCaseSchema,
|
|
3490
3493
|
SystemPublicInterfaceSchema: () => SystemPublicInterfaceSchema,
|
|
3491
3494
|
SystemSpecSchema: () => SystemSpecSchema,
|
|
@@ -3556,10 +3559,12 @@ __export(src_exports, {
|
|
|
3556
3559
|
discoverPacks: () => discoverPacks,
|
|
3557
3560
|
dryRunSerializeSpecs: () => dryRunSerializeSpecs,
|
|
3558
3561
|
effectiveDetail: () => effectiveDetail,
|
|
3562
|
+
effectiveProjectId: () => effectiveProjectId,
|
|
3559
3563
|
emptyExtensions: () => emptyExtensions,
|
|
3560
3564
|
ensureDir: () => ensureDir,
|
|
3561
3565
|
ensureProjectInitialized: () => ensureProjectInitialized,
|
|
3562
3566
|
exportSpecTree: () => exportSpecTree,
|
|
3567
|
+
exportTargetKey: () => exportTargetKey,
|
|
3563
3568
|
externalizeSubsystem: () => externalizeSubsystem,
|
|
3564
3569
|
extractTypeIdentifiers: () => extractTypeIdentifiers,
|
|
3565
3570
|
factsFor: () => factsFor,
|
|
@@ -3600,6 +3605,7 @@ __export(src_exports, {
|
|
|
3600
3605
|
internalizeSubsystem: () => internalizeSubsystem,
|
|
3601
3606
|
isDraftSubsystem: () => isDraftSubsystem,
|
|
3602
3607
|
isLogic: () => isLogic,
|
|
3608
|
+
isOwnComponentEntry: () => isOwnComponentEntry,
|
|
3603
3609
|
isPattern: () => isPattern,
|
|
3604
3610
|
isProjectInitialized: () => isProjectInitialized,
|
|
3605
3611
|
isProvidedBy: () => isProvidedBy,
|
|
@@ -3653,6 +3659,7 @@ __export(src_exports, {
|
|
|
3653
3659
|
pathKey: () => pathKey,
|
|
3654
3660
|
pinInstalledPacksAsSelections: () => pinInstalledPacksAsSelections,
|
|
3655
3661
|
projectConfigExists: () => projectConfigExists,
|
|
3662
|
+
projectIdentity: () => projectIdentity,
|
|
3656
3663
|
provisionProject: () => provisionProject,
|
|
3657
3664
|
qualifiedTypeId: () => qualifiedTypeId,
|
|
3658
3665
|
readFileOrNull: () => readFileOrNull,
|
|
@@ -3678,7 +3685,9 @@ __export(src_exports, {
|
|
|
3678
3685
|
resolveExpectedOutputPaths: () => resolveExpectedOutputPaths,
|
|
3679
3686
|
resolveImport: () => resolveImport,
|
|
3680
3687
|
resolveInstalledPack: () => resolveInstalledPack,
|
|
3688
|
+
resolveProjectExports: () => resolveProjectExports,
|
|
3681
3689
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
3690
|
+
resolveSubsystemExports: () => resolveSubsystemExports,
|
|
3682
3691
|
resolveVariantGuidance: () => resolveVariantGuidance,
|
|
3683
3692
|
retireSpecialists: () => retireSpecialists,
|
|
3684
3693
|
runWithProjectBinding: () => runWithProjectBinding,
|
|
@@ -4462,7 +4471,16 @@ var ProjectConfigSchema = import_zod3.z.object({
|
|
|
4462
4471
|
* CLI versions.
|
|
4463
4472
|
*/
|
|
4464
4473
|
schemaVersion: import_zod3.z.string().default("1.0.0"),
|
|
4465
|
-
/**
|
|
4474
|
+
/**
|
|
4475
|
+
* The project's stable identity: a slug of [a-z0-9-_.] that starts and ends
|
|
4476
|
+
* alphanumeric (PROJECT_ID_RE). Optional in the file: a project that declares
|
|
4477
|
+
* none answers to its name slugified (effectiveProjectId) until a deliberate
|
|
4478
|
+
* writer sets one — an ordinary save never writes the default. Deliberately a plain
|
|
4479
|
+
* string here rather than the grammar: a malformed id must be REPORTED
|
|
4480
|
+
* (PROJECT_ID_AMBIGUOUS), not make the whole configuration unreadable.
|
|
4481
|
+
*/
|
|
4482
|
+
id: import_zod3.z.string().optional(),
|
|
4483
|
+
/** Human-readable display name. Not an identity — `id` is. */
|
|
4466
4484
|
name: import_zod3.z.string(),
|
|
4467
4485
|
/**
|
|
4468
4486
|
* The type/profile of the project, which configures targeted guidelines, rules,
|
|
@@ -4571,6 +4589,40 @@ function declaredPackNames(config) {
|
|
|
4571
4589
|
function declaredProfileIds(config) {
|
|
4572
4590
|
return [...new Set(config.profileSelection?.profileIds ?? [])];
|
|
4573
4591
|
}
|
|
4592
|
+
var PROJECT_ID_RE = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;
|
|
4593
|
+
function slugifyProjectName(name) {
|
|
4594
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").replace(/[^a-z0-9]+$/, "");
|
|
4595
|
+
return slug === "" ? null : slug;
|
|
4596
|
+
}
|
|
4597
|
+
function effectiveProjectId(config) {
|
|
4598
|
+
if (config.id !== void 0) return config.id;
|
|
4599
|
+
return slugifyProjectName(config.name);
|
|
4600
|
+
}
|
|
4601
|
+
function projectIdentity(config, lockedProjectId) {
|
|
4602
|
+
const id = effectiveProjectId(config);
|
|
4603
|
+
const source = config.id !== void 0 ? "declared" : id !== null ? "defaulted" : "none";
|
|
4604
|
+
const problems = [];
|
|
4605
|
+
if (source === "defaulted") {
|
|
4606
|
+
problems.push({ kind: "defaulted", detail: `no id is declared; the name "${config.name}" yields "${id}"` });
|
|
4607
|
+
} else if (source === "none") {
|
|
4608
|
+
problems.push({ kind: "ambiguous", detail: `no id is declared, and the name "${config.name}" yields no id` });
|
|
4609
|
+
} else if (!PROJECT_ID_RE.test(config.id)) {
|
|
4610
|
+
problems.push({ kind: "ambiguous", detail: `the declared id "${config.id}" breaks the project-id grammar` });
|
|
4611
|
+
}
|
|
4612
|
+
if (lockedProjectId !== void 0 && id !== lockedProjectId) {
|
|
4613
|
+
problems.push({
|
|
4614
|
+
kind: "changed",
|
|
4615
|
+
detail: id === null ? `the lock approved "${lockedProjectId}", and the project now has no id` : `the lock approved "${lockedProjectId}", and the project now resolves to "${id}"`
|
|
4616
|
+
});
|
|
4617
|
+
}
|
|
4618
|
+
return {
|
|
4619
|
+
...id !== null ? { id } : {},
|
|
4620
|
+
source,
|
|
4621
|
+
name: config.name,
|
|
4622
|
+
...lockedProjectId !== void 0 ? { lockedId: lockedProjectId } : {},
|
|
4623
|
+
problems
|
|
4624
|
+
};
|
|
4625
|
+
}
|
|
4574
4626
|
|
|
4575
4627
|
// src/config/project-config.ts
|
|
4576
4628
|
function waiDirAt(root) {
|
|
@@ -4858,18 +4910,19 @@ function registryOver(store, root) {
|
|
|
4858
4910
|
if (!config) throw new ProjectNotInitializedError();
|
|
4859
4911
|
return config;
|
|
4860
4912
|
};
|
|
4913
|
+
const save = (config) => store.write(config);
|
|
4861
4914
|
return {
|
|
4862
4915
|
create(config) {
|
|
4863
4916
|
if (store.exists()) {
|
|
4864
4917
|
throw new WaironError(`A project configuration already exists at ${root}; creating one never overwrites it.`);
|
|
4865
4918
|
}
|
|
4866
|
-
|
|
4919
|
+
save(config);
|
|
4867
4920
|
},
|
|
4868
4921
|
upsertPackSelection(selection) {
|
|
4869
4922
|
const config = current2();
|
|
4870
4923
|
const existing = packsOf(config);
|
|
4871
4924
|
const without = existing.filter((entry) => typeof entry === "string" || entry.name !== selection.name);
|
|
4872
|
-
|
|
4925
|
+
save(withPacks(config, [...without, selection]));
|
|
4873
4926
|
return without.length !== existing.length;
|
|
4874
4927
|
},
|
|
4875
4928
|
removePackSelection(packName) {
|
|
@@ -4877,24 +4930,24 @@ function registryOver(store, root) {
|
|
|
4877
4930
|
const existing = packsOf(config);
|
|
4878
4931
|
const remaining = existing.filter((entry) => typeof entry === "string" || entry.name !== packName);
|
|
4879
4932
|
if (remaining.length === existing.length) return false;
|
|
4880
|
-
|
|
4933
|
+
save(withPacks(config, remaining));
|
|
4881
4934
|
return true;
|
|
4882
4935
|
},
|
|
4883
4936
|
setProjectType(projectType) {
|
|
4884
|
-
|
|
4937
|
+
save({ ...current2(), projectType });
|
|
4885
4938
|
},
|
|
4886
4939
|
recordProfileSelection(selection) {
|
|
4887
|
-
|
|
4940
|
+
save({ ...current2(), profileSelection: selection });
|
|
4888
4941
|
},
|
|
4889
4942
|
setExecutionTier(tier) {
|
|
4890
4943
|
const config = current2();
|
|
4891
|
-
|
|
4944
|
+
save({ ...config, execution: { ...config.execution, tier } });
|
|
4892
4945
|
},
|
|
4893
4946
|
registerPackRef(ref) {
|
|
4894
4947
|
const config = current2();
|
|
4895
4948
|
const packs = packsOf(config);
|
|
4896
4949
|
if (packs.includes(ref)) return false;
|
|
4897
|
-
|
|
4950
|
+
save(withPacks(config, [...packs, ref]));
|
|
4898
4951
|
return true;
|
|
4899
4952
|
},
|
|
4900
4953
|
deregisterPackRef(ref) {
|
|
@@ -4902,16 +4955,16 @@ function registryOver(store, root) {
|
|
|
4902
4955
|
const packs = packsOf(config);
|
|
4903
4956
|
const remaining = packs.filter((entry) => entry !== ref);
|
|
4904
4957
|
if (remaining.length === packs.length) return false;
|
|
4905
|
-
|
|
4958
|
+
save(withPacks(config, remaining));
|
|
4906
4959
|
return true;
|
|
4907
4960
|
},
|
|
4908
4961
|
markSelectionsBundled(bundled) {
|
|
4909
4962
|
const config = current2();
|
|
4910
|
-
|
|
4963
|
+
save(withPacks(config, bundleInPlace(packsOf(config), bundled)));
|
|
4911
4964
|
},
|
|
4912
4965
|
pinGlobalPacksAsSelections(selections) {
|
|
4913
4966
|
const config = current2();
|
|
4914
|
-
|
|
4967
|
+
save({
|
|
4915
4968
|
...config,
|
|
4916
4969
|
extensions: { ...config.extensions, packs: [...packsOf(config), ...selections], useGlobalPacks: false }
|
|
4917
4970
|
});
|
|
@@ -5406,15 +5459,21 @@ var DiagramConfigSchema = import_zod8.z.object({
|
|
|
5406
5459
|
var SURFACE_AUDIENCES = ["project", "department", "instance", "partner", "external"];
|
|
5407
5460
|
var SurfaceAudienceSchema = import_zod8.z.enum(SURFACE_AUDIENCES);
|
|
5408
5461
|
var SystemPublicInterfaceSchema = import_zod8.z.object({
|
|
5409
|
-
/**
|
|
5462
|
+
/** Legacy public name, read as `as` when `as` is absent. */
|
|
5410
5463
|
id: import_zod8.z.string().optional(),
|
|
5411
5464
|
name: import_zod8.z.string().optional(),
|
|
5412
|
-
/**
|
|
5465
|
+
/** Legacy source subsystem, read as `from` when `from` is absent. */
|
|
5413
5466
|
subsystem: import_zod8.z.string().optional(),
|
|
5414
|
-
/**
|
|
5467
|
+
/** The component re-exported one to one; without it and without `typeDef`, a wildcard. */
|
|
5415
5468
|
component: import_zod8.z.string().optional(),
|
|
5416
|
-
/**
|
|
5469
|
+
/** The L3 interface the export narrows to. */
|
|
5417
5470
|
interface: import_zod8.z.string().optional(),
|
|
5471
|
+
/** The source subsystem the entry re-exports from. */
|
|
5472
|
+
from: import_zod8.z.string().optional(),
|
|
5473
|
+
/** A type exported as is, giving it a public name. */
|
|
5474
|
+
typeDef: import_zod8.z.string().optional(),
|
|
5475
|
+
/** The public name: defaults to `id`, then `interface`, then `component` or `typeDef`. */
|
|
5476
|
+
as: import_zod8.z.string().optional(),
|
|
5418
5477
|
/** Surface kind: REST, GraphQL, MessageBus, RPC, or Custom. */
|
|
5419
5478
|
type: import_zod8.z.string().optional(),
|
|
5420
5479
|
details: import_zod8.z.string().optional(),
|
|
@@ -5455,19 +5514,33 @@ var SystemSpecSchema = import_zod8.z.object({
|
|
|
5455
5514
|
});
|
|
5456
5515
|
var PublicInterfaceTypeSchema = import_zod8.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]);
|
|
5457
5516
|
var PublicInterfaceSchema = import_zod8.z.object({
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
/**
|
|
5517
|
+
/** Transport kind of an own item; a re-export inherits its target's. */
|
|
5518
|
+
type: PublicInterfaceTypeSchema.optional(),
|
|
5519
|
+
/** Consumer-facing description of an own item; a re-export inherits its target's. */
|
|
5520
|
+
details: import_zod8.z.string().optional(),
|
|
5521
|
+
/** For an own item, the L2 component that realizes it; for a re-export, the component re-exported from `from`. */
|
|
5461
5522
|
component: SpecIdSchema.optional(),
|
|
5462
|
-
/** Optional L3 interface
|
|
5523
|
+
/** Optional L3 interface the entry narrows to. */
|
|
5463
5524
|
interface: SpecIdSchema.optional(),
|
|
5525
|
+
/** Re-export source: another subsystem of the same project. */
|
|
5526
|
+
from: SpecIdSchema.optional(),
|
|
5527
|
+
/** A type exported as is: owned by this subsystem, or re-exported from `from`. */
|
|
5528
|
+
typeDef: SpecIdSchema.optional(),
|
|
5529
|
+
/** The public name; defaults to the narrowed interface, then the component or type id. */
|
|
5530
|
+
as: import_zod8.z.string().optional(),
|
|
5464
5531
|
/**
|
|
5465
5532
|
* The subsystems this surface is published to. Absent, anyone may depend on
|
|
5466
5533
|
* it; present, only these subsystems may — when every entry publishing the
|
|
5467
5534
|
* component names consumers, the union of those lists is the whole set.
|
|
5468
5535
|
*/
|
|
5469
5536
|
consumers: import_zod8.z.array(SpecIdSchema).optional()
|
|
5537
|
+
}).refine((pi) => pi.from !== void 0 || pi.typeDef !== void 0 || pi.type !== void 0 && pi.details !== void 0, {
|
|
5538
|
+
message: "An own component entry states its `type` and `details`; only a re-export (`from`) or a type export (`typeDef`) may leave them out.",
|
|
5539
|
+
path: ["type"]
|
|
5470
5540
|
});
|
|
5541
|
+
function isOwnComponentEntry(pi) {
|
|
5542
|
+
return pi.from === void 0 && pi.typeDef === void 0;
|
|
5543
|
+
}
|
|
5471
5544
|
var TrustedLinkSchema = import_zod8.z.object({
|
|
5472
5545
|
/** The peer subsystem id this link sanctions tight coupling with. */
|
|
5473
5546
|
subsystem: SpecIdSchema,
|
|
@@ -5784,8 +5857,8 @@ var MethodParamSchema = import_zod8.z.object({
|
|
|
5784
5857
|
var FindingDeclarationSchema = import_zod8.z.object({
|
|
5785
5858
|
/** UPPER_SNAKE and unique within the method; a pack's codes carry the pack prefix (<PACK>_<CODE>). */
|
|
5786
5859
|
code: import_zod8.z.string().regex(/^[A-Z][A-Z0-9_]*$/, "Finding code must be UPPER_SNAKE, e.g. UNREALIZED_FINDING"),
|
|
5787
|
-
/** The default before project severity overrides and draft-context downgrades. */
|
|
5788
|
-
severity: import_zod8.z.enum(["error", "warning"]),
|
|
5860
|
+
/** The default before project severity overrides and draft-context downgrades; a notice is reported but never fails the gate. */
|
|
5861
|
+
severity: import_zod8.z.enum(["error", "warning", "notice"]),
|
|
5789
5862
|
/** One line saying what the finding means. */
|
|
5790
5863
|
summary: import_zod8.z.string().min(1, "Finding summary must say what the finding means")
|
|
5791
5864
|
});
|
|
@@ -6329,6 +6402,14 @@ var SurfaceTypeDefSchema = import_zod8.z.object({
|
|
|
6329
6402
|
optional: import_zod8.z.boolean().optional()
|
|
6330
6403
|
})).default([])
|
|
6331
6404
|
});
|
|
6405
|
+
var SurfaceTypeExportSchema = import_zod8.z.object({
|
|
6406
|
+
/** The type's public name in the producer's export table. */
|
|
6407
|
+
id: import_zod8.z.string(),
|
|
6408
|
+
/** The id of the type's definition in the snapshot's type closure. */
|
|
6409
|
+
type: import_zod8.z.string(),
|
|
6410
|
+
/** Exposure level from the L0 entry. */
|
|
6411
|
+
audience: import_zod8.z.string().default("instance")
|
|
6412
|
+
});
|
|
6332
6413
|
var SurfaceContractEntrySchema = import_zod8.z.object({
|
|
6333
6414
|
id: import_zod8.z.string(),
|
|
6334
6415
|
name: import_zod8.z.string(),
|
|
@@ -6349,7 +6430,11 @@ var SurfaceContractEntrySchema = import_zod8.z.object({
|
|
|
6349
6430
|
* emits it as OpenAPI securitySchemes/security. */
|
|
6350
6431
|
auth: PortalAuthSchema.optional(),
|
|
6351
6432
|
/** The backing Portal's basePath — becomes the per-portal OpenAPI `servers` url. */
|
|
6352
|
-
basePath: import_zod8.z.string().optional()
|
|
6433
|
+
basePath: import_zod8.z.string().optional(),
|
|
6434
|
+
/** Stereotype of the backing component; absent on snapshots written before exports carried it. */
|
|
6435
|
+
componentType: import_zod8.z.string().optional(),
|
|
6436
|
+
/** The L3 interface the export narrows to, when it narrows. */
|
|
6437
|
+
interface: import_zod8.z.string().optional()
|
|
6353
6438
|
});
|
|
6354
6439
|
var SurfaceSnapshotSchema = import_zod8.z.object({
|
|
6355
6440
|
/** Producing project/system name — the snapshot's resolution identity. */
|
|
@@ -6361,8 +6446,12 @@ var SurfaceSnapshotSchema = import_zod8.z.object({
|
|
|
6361
6446
|
version: import_zod8.z.string().optional(),
|
|
6362
6447
|
generatedAt: import_zod8.z.string(),
|
|
6363
6448
|
interfaces: import_zod8.z.array(SurfaceContractEntrySchema).default([]),
|
|
6364
|
-
/** Transitive type closure of every exported signature — self-contained. */
|
|
6365
|
-
types: import_zod8.z.array(SurfaceTypeDefSchema).default([])
|
|
6449
|
+
/** Transitive type closure of every exported signature and exported type — self-contained. */
|
|
6450
|
+
types: import_zod8.z.array(SurfaceTypeDefSchema).default([]),
|
|
6451
|
+
/** The producing project's effective id, beside the display-oriented projectName. */
|
|
6452
|
+
projectId: import_zod8.z.string().optional(),
|
|
6453
|
+
/** The types the producer exports by name, listed apart from the contract entries. */
|
|
6454
|
+
exportedTypes: import_zod8.z.array(SurfaceTypeExportSchema).optional()
|
|
6366
6455
|
});
|
|
6367
6456
|
var NamedOpenApiSpecSchema = import_zod8.z.object({
|
|
6368
6457
|
portalId: import_zod8.z.string(),
|
|
@@ -6905,6 +6994,12 @@ function ambiguityMessage(resolution, subject, ref) {
|
|
|
6905
6994
|
return `${subject} cross-tree component "${ref}", which the surface snapshots of ${providers.map((p) => `"${p}"`).join(", ")} expose with different contracts \u2014 the reference matches more than one declared contract, so none of them can judge it. Name the provider it means (super::<provider>::${local}), or remove the snapshot that no longer applies.`;
|
|
6906
6995
|
}
|
|
6907
6996
|
|
|
6997
|
+
// src/models/exports.ts
|
|
6998
|
+
var PUBLIC_NAME_RE = /^[a-z0-9_-]+$/;
|
|
6999
|
+
function exportTargetKey(e) {
|
|
7000
|
+
return e.kind === "type" ? `type:${e.typeDef}` : `component:${e.component}${e.interface ? `#${e.interface}` : ""}`;
|
|
7001
|
+
}
|
|
7002
|
+
|
|
6908
7003
|
// src/core/narrative-labels.ts
|
|
6909
7004
|
var SCALAR_REFS = [
|
|
6910
7005
|
["onTrueLabel", "onTrueStep"],
|
|
@@ -6924,7 +7019,7 @@ function resolveNarrativeLabels(methodName, steps) {
|
|
|
6924
7019
|
else labels.set(s.label, s.stepNumber);
|
|
6925
7020
|
}
|
|
6926
7021
|
}
|
|
6927
|
-
const
|
|
7022
|
+
const lookup2 = (ref, where) => {
|
|
6928
7023
|
if (typeof ref !== "string" || !ref.length) {
|
|
6929
7024
|
errors.push(`${where} is not a label string`);
|
|
6930
7025
|
return void 0;
|
|
@@ -6938,7 +7033,7 @@ function resolveNarrativeLabels(methodName, steps) {
|
|
|
6938
7033
|
for (const s of steps) {
|
|
6939
7034
|
for (const [labelField, stepField] of SCALAR_REFS) {
|
|
6940
7035
|
if (s[labelField] === void 0) continue;
|
|
6941
|
-
const n =
|
|
7036
|
+
const n = lookup2(s[labelField], `step ${s.stepNumber} ${labelField}`);
|
|
6942
7037
|
if (n !== void 0) {
|
|
6943
7038
|
if (typeof s[stepField] === "number" && s[stepField] !== n) {
|
|
6944
7039
|
errors.push(`step ${s.stepNumber} sets both ${stepField}=${s[stepField]} and ${labelField}="${s[labelField]}" (= step ${n}) \u2014 they disagree`);
|
|
@@ -6954,7 +7049,7 @@ function resolveNarrativeLabels(methodName, steps) {
|
|
|
6954
7049
|
for (const entry of arr) {
|
|
6955
7050
|
if (!entry || typeof entry !== "object" || entry.label === void 0) continue;
|
|
6956
7051
|
const e = entry;
|
|
6957
|
-
const n =
|
|
7052
|
+
const n = lookup2(e.label, `step ${s.stepNumber} ${key} entry`);
|
|
6958
7053
|
if (n !== void 0) {
|
|
6959
7054
|
if (typeof e.step === "number" && e.step !== n) {
|
|
6960
7055
|
errors.push(`step ${s.stepNumber} ${key} entry sets both step=${e.step} and label="${e.label}" (= step ${n}) \u2014 they disagree`);
|
|
@@ -7394,13 +7489,17 @@ function buildCanvasModel(issues = []) {
|
|
|
7394
7489
|
const componentIds = new Set(components.map((c) => c.id));
|
|
7395
7490
|
const publicComponents = /* @__PURE__ */ new Set();
|
|
7396
7491
|
for (const sub of subsystems) {
|
|
7397
|
-
for (const pi of sub.publicInterfaces) {
|
|
7492
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
7398
7493
|
if (pi.component) publicComponents.add(pi.component);
|
|
7399
7494
|
}
|
|
7400
7495
|
}
|
|
7401
7496
|
const apiTagOf = /* @__PURE__ */ new Map();
|
|
7402
|
-
|
|
7403
|
-
|
|
7497
|
+
if (system) {
|
|
7498
|
+
for (const entry of resolveProjectExports().entries) {
|
|
7499
|
+
if (entry.kind === "component" && entry.component && !apiTagOf.has(entry.component)) {
|
|
7500
|
+
apiTagOf.set(entry.component, entry.publicName);
|
|
7501
|
+
}
|
|
7502
|
+
}
|
|
7404
7503
|
}
|
|
7405
7504
|
const patternTypes = /* @__PURE__ */ new Set(["Repository", "FeatureComponent", "RouterComponent"]);
|
|
7406
7505
|
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
@@ -7566,7 +7665,7 @@ function buildCanvasModel(issues = []) {
|
|
|
7566
7665
|
}
|
|
7567
7666
|
const portalOf = /* @__PURE__ */ new Map();
|
|
7568
7667
|
for (const sub of subsystems) {
|
|
7569
|
-
for (const pi of sub.publicInterfaces) {
|
|
7668
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
7570
7669
|
if (pi.component && !portalOf.has(sub.id)) portalOf.set(sub.id, pi.component);
|
|
7571
7670
|
}
|
|
7572
7671
|
}
|
|
@@ -11837,19 +11936,25 @@ function stateIdString() {
|
|
|
11837
11936
|
const s = computeStateId();
|
|
11838
11937
|
return `${s.algorithm}:${s.digest}`;
|
|
11839
11938
|
}
|
|
11840
|
-
function
|
|
11939
|
+
function qualifiedTypeId2(spec) {
|
|
11940
|
+
return spec.subsystem && !spec.id.startsWith(`${spec.subsystem}::`) ? `${spec.subsystem}::${spec.id}` : spec.id;
|
|
11941
|
+
}
|
|
11942
|
+
function computeTypeClosure(entries, types, exported = []) {
|
|
11841
11943
|
const included = /* @__PURE__ */ new Map();
|
|
11842
11944
|
const queue = [];
|
|
11945
|
+
const include = (spec) => {
|
|
11946
|
+
const key = qualifiedTypeId2(spec);
|
|
11947
|
+
if (included.has(key)) return;
|
|
11948
|
+
included.set(key, spec);
|
|
11949
|
+
queue.push(key);
|
|
11950
|
+
};
|
|
11843
11951
|
const enqueueRef = (ref) => {
|
|
11844
11952
|
if (BUILTIN_TYPES.has(ref.toLowerCase())) return;
|
|
11845
11953
|
for (const spec of types) {
|
|
11846
|
-
|
|
11847
|
-
if (matchTypeRef(ref, qualifiedId) && !included.has(spec.id)) {
|
|
11848
|
-
included.set(spec.id, spec);
|
|
11849
|
-
queue.push(spec.id);
|
|
11850
|
-
}
|
|
11954
|
+
if (matchTypeRef(ref, qualifiedTypeId2(spec))) include(spec);
|
|
11851
11955
|
}
|
|
11852
11956
|
};
|
|
11957
|
+
for (const spec of exported) include(spec);
|
|
11853
11958
|
for (const entry of entries) {
|
|
11854
11959
|
for (const m of entry.methods) {
|
|
11855
11960
|
for (const ref of methodTypeRefs(m)) enqueueRef(ref);
|
|
@@ -11861,63 +11966,87 @@ function computeTypeClosure(entries, types) {
|
|
|
11861
11966
|
for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
|
|
11862
11967
|
}
|
|
11863
11968
|
}
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11969
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
11970
|
+
return [...included.entries()].map(([qualified, t]) => {
|
|
11971
|
+
const id = usedIds.has(t.id) ? qualified : t.id;
|
|
11972
|
+
usedIds.add(id);
|
|
11973
|
+
return {
|
|
11974
|
+
id,
|
|
11975
|
+
name: t.name,
|
|
11976
|
+
kind: t.kind,
|
|
11977
|
+
fields: t.fields.map((f) => ({
|
|
11978
|
+
name: f.name,
|
|
11979
|
+
type: f.type,
|
|
11980
|
+
...f.description ? { description: f.description } : {},
|
|
11981
|
+
...f.optional ? { optional: true } : {}
|
|
11982
|
+
}))
|
|
11983
|
+
};
|
|
11984
|
+
});
|
|
11985
|
+
}
|
|
11986
|
+
function boundProjectId() {
|
|
11987
|
+
try {
|
|
11988
|
+
const config = loadProjectConfig();
|
|
11989
|
+
return config ? effectiveProjectId(config) ?? void 0 : void 0;
|
|
11990
|
+
} catch {
|
|
11991
|
+
return void 0;
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
function contractEntry(entry, comp, interfaces) {
|
|
11995
|
+
const methods = interfaces.filter((i) => i.component === comp.id && (!entry.interface || i.id === entry.interface)).flatMap((i) => i.methods);
|
|
11996
|
+
return {
|
|
11997
|
+
id: entry.publicName,
|
|
11998
|
+
name: entry.name ?? comp.name,
|
|
11999
|
+
audience: entry.audience ?? "instance",
|
|
12000
|
+
type: entry.type ?? "Custom",
|
|
12001
|
+
component: comp.id,
|
|
12002
|
+
methods,
|
|
12003
|
+
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
12004
|
+
// Project the backing Portal's auth + basePath so the codec can emit
|
|
12005
|
+
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
12006
|
+
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
12007
|
+
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
12008
|
+
details: entry.details ?? "",
|
|
12009
|
+
...entry.version ? { version: entry.version } : {},
|
|
12010
|
+
...entry.stability ? { stability: entry.stability } : {},
|
|
12011
|
+
componentType: comp.componentType,
|
|
12012
|
+
...entry.interface ? { interface: entry.interface } : {}
|
|
12013
|
+
};
|
|
11875
12014
|
}
|
|
11876
12015
|
function projectOwnSurface(maxAudience) {
|
|
11877
12016
|
const system = loadSystemSpec();
|
|
11878
12017
|
if (!system) {
|
|
11879
12018
|
throw new Error("Cannot project a surface: the L0 system spec is missing.");
|
|
11880
12019
|
}
|
|
11881
|
-
const
|
|
12020
|
+
const table = resolveProjectExports();
|
|
11882
12021
|
const components = loadComponentSpecs();
|
|
11883
12022
|
const interfaces = loadInterfaceSpecs();
|
|
11884
12023
|
const types = loadTypeSpecs();
|
|
11885
12024
|
const floor = audienceRank(maxAudience);
|
|
11886
|
-
const
|
|
12025
|
+
const visible = table.entries.filter((e) => audienceRank(e.audience ?? "instance") >= floor);
|
|
11887
12026
|
const entries = [];
|
|
11888
|
-
for (const
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
if (
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
component: comp.id,
|
|
11903
|
-
methods,
|
|
11904
|
-
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
11905
|
-
// Project the backing Portal's auth + basePath so the codec can emit
|
|
11906
|
-
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
11907
|
-
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
11908
|
-
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
11909
|
-
details: raw.details ?? "",
|
|
11910
|
-
...raw.version ? { version: raw.version } : {},
|
|
11911
|
-
...raw.stability ? { stability: raw.stability } : {}
|
|
11912
|
-
});
|
|
11913
|
-
}
|
|
12027
|
+
for (const entry of visible) {
|
|
12028
|
+
if (entry.kind !== "component") continue;
|
|
12029
|
+
const comp = components.find((c) => c.id === entry.component);
|
|
12030
|
+
if (comp) entries.push(contractEntry(entry, comp, interfaces));
|
|
12031
|
+
}
|
|
12032
|
+
const exportedTypes = visible.filter((e) => e.kind === "type");
|
|
12033
|
+
const exportedSpecs = exportedTypes.map((e) => types.find((t) => t.id === e.typeDef && (t.subsystem ?? e.source) === e.source)).filter((t) => !!t);
|
|
12034
|
+
const closure = computeTypeClosure(entries, types, exportedSpecs);
|
|
12035
|
+
const closureIdOf = (e) => {
|
|
12036
|
+
const spec = types.find((t) => t.id === e.typeDef && (t.subsystem ?? e.source) === e.source);
|
|
12037
|
+
if (!spec) return String(e.typeDef);
|
|
12038
|
+
return closure.some((d) => d.id === qualifiedTypeId2(spec)) ? qualifiedTypeId2(spec) : spec.id;
|
|
12039
|
+
};
|
|
12040
|
+
const projectId = boundProjectId();
|
|
11914
12041
|
return SurfaceSnapshotSchema.parse({
|
|
11915
12042
|
projectName: system.name,
|
|
12043
|
+
...projectId ? { projectId } : {},
|
|
11916
12044
|
origin: "generated",
|
|
11917
12045
|
stateId: stateIdString(),
|
|
11918
12046
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11919
12047
|
interfaces: entries,
|
|
11920
|
-
types:
|
|
12048
|
+
types: closure,
|
|
12049
|
+
...exportedTypes.length ? { exportedTypes: exportedTypes.map((e) => ({ id: e.publicName, type: closureIdOf(e), audience: e.audience ?? "instance" })) } : {}
|
|
11921
12050
|
});
|
|
11922
12051
|
}
|
|
11923
12052
|
function projectChildSurface() {
|
|
@@ -12037,7 +12166,8 @@ function listExternalInterfaces() {
|
|
|
12037
12166
|
...snapshot.stateId ? { stateId: snapshot.stateId } : {},
|
|
12038
12167
|
...snapshot.version ? { version: snapshot.version } : {},
|
|
12039
12168
|
freshness,
|
|
12040
|
-
interfaceIds: snapshot.interfaces.map((e) => e.id)
|
|
12169
|
+
interfaceIds: snapshot.interfaces.map((e) => e.id),
|
|
12170
|
+
...snapshot.projectId ? { projectId: snapshot.projectId } : {}
|
|
12041
12171
|
};
|
|
12042
12172
|
});
|
|
12043
12173
|
}
|
|
@@ -12098,7 +12228,7 @@ function defaultTargetConfig(type) {
|
|
|
12098
12228
|
enabled: true
|
|
12099
12229
|
};
|
|
12100
12230
|
}
|
|
12101
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
12231
|
+
var WAIRON_VERSION = "5.1.1-dev.80";
|
|
12102
12232
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
12103
12233
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
12104
12234
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -13020,18 +13150,18 @@ function buildImportGraph(index, universe) {
|
|
|
13020
13150
|
};
|
|
13021
13151
|
}
|
|
13022
13152
|
function buildOwnershipIndex(ctx) {
|
|
13023
|
-
const
|
|
13153
|
+
const ownedBy2 = /* @__PURE__ */ new Map();
|
|
13024
13154
|
for (const comp of ctx.components) {
|
|
13025
13155
|
if (isRetired(comp) || !isPattern(comp)) continue;
|
|
13026
13156
|
for (const memberId of comp.owns) {
|
|
13027
13157
|
const member = ctx.componentMap.get(memberId);
|
|
13028
13158
|
if (!member || isPattern(member)) continue;
|
|
13029
|
-
if (!
|
|
13159
|
+
if (!ownedBy2.has(memberId)) ownedBy2.set(memberId, comp.id);
|
|
13030
13160
|
}
|
|
13031
13161
|
}
|
|
13032
13162
|
return {
|
|
13033
|
-
ownedMembers: new Set(
|
|
13034
|
-
ownerOf: (memberId) =>
|
|
13163
|
+
ownedMembers: new Set(ownedBy2.keys()),
|
|
13164
|
+
ownerOf: (memberId) => ownedBy2.get(memberId)
|
|
13035
13165
|
};
|
|
13036
13166
|
}
|
|
13037
13167
|
function buildDependencyEdges(ctx) {
|
|
@@ -13224,6 +13354,45 @@ var namespaceShadowingRule = {
|
|
|
13224
13354
|
}
|
|
13225
13355
|
};
|
|
13226
13356
|
|
|
13357
|
+
// src/core/rules/integrity/project-identity.ts
|
|
13358
|
+
var projectIdentityRule = {
|
|
13359
|
+
name: "project-identity",
|
|
13360
|
+
description: "A project is keyed on its id, so the id must be declared, well-formed and stable: a project that declares none answers to one derived from its display name until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), and an id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED).",
|
|
13361
|
+
codes: [
|
|
13362
|
+
{ code: "PROJECT_ID_DEFAULTED", defaultSeverity: "notice", summary: "Project declares no id; it answers to one derived from its display name" },
|
|
13363
|
+
{ code: "PROJECT_ID_AMBIGUOUS", defaultSeverity: "warning", summary: "Project has no usable id: its name yields no slug, or its declared id breaks the grammar" },
|
|
13364
|
+
{ code: "PROJECT_ID_CHANGED", defaultSeverity: "error", summary: "Project id differs from the id the lock approved" }
|
|
13365
|
+
],
|
|
13366
|
+
check(ctx) {
|
|
13367
|
+
const identity = ctx.projectIdentity;
|
|
13368
|
+
if (!identity) return;
|
|
13369
|
+
const problem = (kind) => identity.problems.find((p) => p.kind === kind);
|
|
13370
|
+
if (problem("defaulted")) {
|
|
13371
|
+
ctx.addIssue(
|
|
13372
|
+
"notice",
|
|
13373
|
+
"PROJECT_ID_DEFAULTED",
|
|
13374
|
+
`Project "${identity.name}" declares no id in .wai/project.yaml, so it answers to "${identity.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity.id}\` (stage 3 requires an explicit id).`
|
|
13375
|
+
);
|
|
13376
|
+
}
|
|
13377
|
+
const ambiguous = problem("ambiguous");
|
|
13378
|
+
if (ambiguous) {
|
|
13379
|
+
ctx.addIssue(
|
|
13380
|
+
"warning",
|
|
13381
|
+
"PROJECT_ID_AMBIGUOUS",
|
|
13382
|
+
`Project "${identity.name}" has no usable id: ${ambiguous.detail}. A project id is lower-case letters, digits, "-", "_" and ".", starting and ending with a letter or a digit \u2014 declare one in .wai/project.yaml.`
|
|
13383
|
+
);
|
|
13384
|
+
}
|
|
13385
|
+
if (problem("changed")) {
|
|
13386
|
+
const now = identity.id === void 0 ? "no id at all" : `"${identity.id}"`;
|
|
13387
|
+
ctx.addIssue(
|
|
13388
|
+
"error",
|
|
13389
|
+
"PROJECT_ID_CHANGED",
|
|
13390
|
+
`Project "${identity.name}" now resolves to ${now}, but its lock approved "${identity.lockedId}" \u2014 everything keyed on the approved id no longer finds this project. Restore \`id: ${identity.lockedId}\` in .wai/project.yaml, or rename deliberately and re-lock.`
|
|
13391
|
+
);
|
|
13392
|
+
}
|
|
13393
|
+
}
|
|
13394
|
+
};
|
|
13395
|
+
|
|
13227
13396
|
// src/core/rules/integrity/roundtrip-serialization.ts
|
|
13228
13397
|
var roundtripRule = {
|
|
13229
13398
|
name: "roundtrip-serialization",
|
|
@@ -13349,7 +13518,7 @@ var publicSurfaceBindingRule = {
|
|
|
13349
13518
|
check(ctx) {
|
|
13350
13519
|
for (const sub of ctx.subsystems) {
|
|
13351
13520
|
const isDraftCtx = isDraftSubsystem(sub);
|
|
13352
|
-
for (const pi of sub.publicInterfaces) {
|
|
13521
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
13353
13522
|
if (!pi.component) {
|
|
13354
13523
|
ctx.addIssue("error", "PUBLIC_INTERFACE_UNBOUND", `Subsystem "${sub.id}" declares a ${pi.type} public interface with no backing component. Bind it to the component that realizes it (publicInterfaces[].component).`, sub.id, isDraftCtx);
|
|
13355
13524
|
continue;
|
|
@@ -13436,13 +13605,15 @@ var publicSurfaceDeclaredTypeRule = {
|
|
|
13436
13605
|
check(ctx) {
|
|
13437
13606
|
for (const sub of ctx.subsystems) {
|
|
13438
13607
|
const isDraftCtx = isDraftSubsystem(sub);
|
|
13439
|
-
for (const pi of sub.publicInterfaces) {
|
|
13608
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
13440
13609
|
const backing = pi.component ? ctx.componentMap.get(pi.component) : void 0;
|
|
13441
13610
|
if (!backing) continue;
|
|
13442
|
-
|
|
13443
|
-
|
|
13611
|
+
const piType = pi.type ?? "Custom";
|
|
13612
|
+
const piDetails = pi.details ?? "";
|
|
13613
|
+
if (!pubTypeMatches(piType, backing.componentType, backing.portalType)) {
|
|
13614
|
+
ctx.addIssue("error", "PUBLIC_INTERFACE_TYPE_MISMATCH", `Subsystem "${sub.id}" declares a ${pi.type} public interface backed by "${pi.component}" (${backing.componentType}${backing.portalType ? `/${backing.portalType}` : ""}), which cannot realize ${pi.type}. Expected ${expectedFor(piType)}.`, sub.id, isDraftCtx);
|
|
13444
13615
|
}
|
|
13445
|
-
if (
|
|
13616
|
+
if (piType === "Custom" && EVENT_VOCAB.test(piDetails)) {
|
|
13446
13617
|
const eventCapable = backing.componentType === "Observer" || backing.componentType === "Portal" && backing.portalType === "MessageBus";
|
|
13447
13618
|
if (!eventCapable) {
|
|
13448
13619
|
ctx.addIssue("warning", "PUBLIC_INTERFACE_EVENT_MISTYPED", `Subsystem "${sub.id}" declares a Custom public interface whose description implies an event/async boundary ("${pi.details}"), but it is backed by "${pi.component}" (${backing.componentType}${backing.portalType ? `/${backing.portalType}` : ""}), which cannot realize eventing. If this is genuinely event-driven, type it MessageBus and back it with an Observer or a Portal(MessageBus); otherwise reword the description to match the synchronous contract.`, sub.id, isDraftCtx);
|
|
@@ -13463,7 +13634,7 @@ var publicSurfaceBoundContractRule = {
|
|
|
13463
13634
|
check(ctx) {
|
|
13464
13635
|
for (const sub of ctx.subsystems) {
|
|
13465
13636
|
const isDraftCtx = isDraftSubsystem(sub);
|
|
13466
|
-
for (const pi of sub.publicInterfaces) {
|
|
13637
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
13467
13638
|
if (!pi.interface) continue;
|
|
13468
13639
|
if (!pi.component || !ctx.componentMap.has(pi.component)) continue;
|
|
13469
13640
|
const intf = ctx.interfaceMap.get(pi.interface);
|
|
@@ -13477,6 +13648,47 @@ var publicSurfaceBoundContractRule = {
|
|
|
13477
13648
|
}
|
|
13478
13649
|
};
|
|
13479
13650
|
|
|
13651
|
+
// src/core/rules/integrity/export-tables.ts
|
|
13652
|
+
var exportTablesRule = {
|
|
13653
|
+
name: "export-tables",
|
|
13654
|
+
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns, under a public name of [a-z0-9-_]+ (EXPORT_INVALID), and every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports). The rule judges the facts the export resolver gathered and resolves nothing itself.",
|
|
13655
|
+
codes: [
|
|
13656
|
+
{ code: "EXPORT_ID_DUPLICATE", defaultSeverity: "error", summary: "One public name bound to different targets by two wildcard re-exports or two explicit entries" },
|
|
13657
|
+
{ code: "EXPORT_CYCLE", defaultSeverity: "error", summary: "A re-export chain leads back to itself (a warning for a wildcard cycle, which resolves to its union)" },
|
|
13658
|
+
{ code: "EXPORT_INVALID", defaultSeverity: "notice", summary: "An export entry names a missing source or item, an item its source does not export or its level does not own, or a malformed public name" },
|
|
13659
|
+
{ code: "EXPORT_UNCONSUMABLE", defaultSeverity: "notice", summary: "An exported component no boundary caller may reach: neither a Portal nor an Observer" }
|
|
13660
|
+
],
|
|
13661
|
+
check(ctx) {
|
|
13662
|
+
const tables = ctx.exportTables;
|
|
13663
|
+
if (!tables) return;
|
|
13664
|
+
for (const table of tables) {
|
|
13665
|
+
const sub = table.level === "subsystem" ? ctx.subsystems.find((s) => s.id === table.owner) : void 0;
|
|
13666
|
+
const draft = sub ? isDraftSubsystem(sub) : false;
|
|
13667
|
+
const where = table.level === "subsystem" ? `Subsystem "${table.owner}"` : `The project's L0 export table`;
|
|
13668
|
+
for (const problem of table.problems) {
|
|
13669
|
+
const name = problem.publicName ? `"${problem.publicName}"` : "an entry";
|
|
13670
|
+
switch (problem.kind) {
|
|
13671
|
+
case "duplicate":
|
|
13672
|
+
ctx.addIssue("error", "EXPORT_ID_DUPLICATE", `${where} binds ${name} to more than one target (${(problem.targets ?? []).join(", ")}), so the name is left out of the table. Narrow one of the wildcards, or add an explicit entry that picks the target.`, table.owner, draft);
|
|
13673
|
+
break;
|
|
13674
|
+
case "named-cycle":
|
|
13675
|
+
ctx.addIssue("error", "EXPORT_CYCLE", `${where} ${problem.detail}. Export the item from the level that owns it.`, table.owner, draft);
|
|
13676
|
+
break;
|
|
13677
|
+
case "wildcard-cycle":
|
|
13678
|
+
ctx.addIssue("warning", "EXPORT_CYCLE", `${where}: ${problem.detail}. Re-export in one direction only.`, table.owner, draft);
|
|
13679
|
+
break;
|
|
13680
|
+
case "invalid":
|
|
13681
|
+
ctx.addIssue("notice", "EXPORT_INVALID", `${where} ${problem.detail}.`, table.owner, draft);
|
|
13682
|
+
break;
|
|
13683
|
+
case "unconsumable":
|
|
13684
|
+
ctx.addIssue("notice", "EXPORT_UNCONSUMABLE", `${where}: ${problem.detail}.`, table.owner, draft);
|
|
13685
|
+
break;
|
|
13686
|
+
}
|
|
13687
|
+
}
|
|
13688
|
+
}
|
|
13689
|
+
}
|
|
13690
|
+
};
|
|
13691
|
+
|
|
13480
13692
|
// src/core/rules/integrity/lint-allows.ts
|
|
13481
13693
|
var lintAllowsRule = {
|
|
13482
13694
|
name: "lint-allows",
|
|
@@ -14868,7 +15080,7 @@ var subsystemBoundaryDepsRule = {
|
|
|
14868
15080
|
}
|
|
14869
15081
|
};
|
|
14870
15082
|
function restrictedConsumers(ctx, subsystemId, componentId) {
|
|
14871
|
-
const entries = (ctx.subsystems.find((s) => s.id === subsystemId)?.publicInterfaces ?? []).filter((pi) => pi.component === componentId);
|
|
15083
|
+
const entries = (ctx.subsystems.find((s) => s.id === subsystemId)?.publicInterfaces ?? []).filter((pi) => isOwnComponentEntry(pi) && pi.component === componentId);
|
|
14872
15084
|
if (entries.length === 0 || entries.some((pi) => pi.consumers === void 0)) return null;
|
|
14873
15085
|
return [...new Set(entries.flatMap((pi) => pi.consumers ?? []))];
|
|
14874
15086
|
}
|
|
@@ -14922,14 +15134,35 @@ var logicDependencyClassRule = {
|
|
|
14922
15134
|
};
|
|
14923
15135
|
|
|
14924
15136
|
// src/core/rules/doctrine/data-block-dependencies.ts
|
|
15137
|
+
function indexReaches(ctx, from, target, seen = /* @__PURE__ */ new Set()) {
|
|
15138
|
+
for (const dep of from.dependsOn) {
|
|
15139
|
+
if (dep === target) return true;
|
|
15140
|
+
const next = ctx.componentMap.get(dep);
|
|
15141
|
+
if (!next || next.componentType !== "Index" || seen.has(dep)) continue;
|
|
15142
|
+
seen.add(dep);
|
|
15143
|
+
if (indexReaches(ctx, next, target, seen)) return true;
|
|
15144
|
+
}
|
|
15145
|
+
return false;
|
|
15146
|
+
}
|
|
15147
|
+
function derivedIndexBreach(ctx, from, to) {
|
|
15148
|
+
const ownership = ctx.ownershipIndex();
|
|
15149
|
+
const owner = ownership.ownerOf(from.id);
|
|
15150
|
+
if (!owner || owner !== ownership.ownerOf(to.id)) {
|
|
15151
|
+
return "An Index may project another Index only when one Repository owns both";
|
|
15152
|
+
}
|
|
15153
|
+
if (indexReaches(ctx, to, from.id)) {
|
|
15154
|
+
return `"${to.id}" depends back on "${from.id}" through Index edges \u2014 derived Indexes never form a cycle`;
|
|
15155
|
+
}
|
|
15156
|
+
return void 0;
|
|
15157
|
+
}
|
|
14925
15158
|
var dataBlockDepsRule = {
|
|
14926
15159
|
name: "data-block-dependencies",
|
|
14927
|
-
description: "Keeps the data layer facing down. A Store may depend only on another Store, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse. A Registry is the write path to its Store and may depend on that Store, a backend Adapter, or the pure logic that validates the write. An Index is a read projection and a Query a computed read, each over its Store through a backend Adapter where one serves it. An Adapter is a sink toward the system: it may use pure logic, but never a Store or any other Orchestrator.",
|
|
15160
|
+
description: "Keeps the data layer facing down. A Store may depend only on another Store, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse. A Registry is the write path to its Store and may depend on that Store, a backend Adapter, or the pure logic that validates the write. An Index is a read projection and a Query a computed read, each over its Store through a backend Adapter where one serves it; as an exceptional case an Index may project another Index of the same Repository, never in a cycle. An Adapter is a sink toward the system: it may use pure logic, but never a Store or any other Orchestrator.",
|
|
14928
15161
|
codes: [
|
|
14929
15162
|
{ code: "ARCHITECTURE_VIOLATION_STORE_DEP", defaultSeverity: "error", summary: "Store depending on anything but another Store, a backend Adapter or pure logic" },
|
|
14930
15163
|
{ code: "ARCHITECTURE_VIOLATION_REGISTRY_DEP", defaultSeverity: "warning", summary: "Registry depending on anything but its Store, a backend Adapter or pure logic (warning while new; aligned to the standard \xA77 validate\u2192write path)" },
|
|
14931
15164
|
{ code: "ARCHITECTURE_VIOLATION_ADAPTER_DEP", defaultSeverity: "error", summary: "Adapter depending on a Store or on an Orchestrator that is not pure" },
|
|
14932
|
-
{ code: "ARCHITECTURE_VIOLATION_INDEX_DEP", defaultSeverity: "error", summary: "Index depending on anything but its Store, an Adapter or
|
|
15165
|
+
{ code: "ARCHITECTURE_VIOLATION_INDEX_DEP", defaultSeverity: "error", summary: "Index depending on anything but its Store, an Adapter, pure logic or another Index of its own Repository (never in a cycle)" },
|
|
14933
15166
|
{ code: "ARCHITECTURE_VIOLATION_QUERY_DEP", defaultSeverity: "error", summary: "Query depending on anything but its Store, a backend Adapter or pure logic" }
|
|
14934
15167
|
],
|
|
14935
15168
|
check(ctx) {
|
|
@@ -14966,11 +15199,13 @@ var dataBlockDepsRule = {
|
|
|
14966
15199
|
edge.draftContext
|
|
14967
15200
|
);
|
|
14968
15201
|
}
|
|
14969
|
-
|
|
15202
|
+
const indexBreach = comp.componentType === "Index" && depComp.componentType === "Index" ? derivedIndexBreach(ctx, comp, depComp) : void 0;
|
|
15203
|
+
const derivedIndexOk = comp.componentType === "Index" && depComp.componentType === "Index" && !indexBreach;
|
|
15204
|
+
if (comp.componentType === "Index" && ownStoreOnly && !derivedIndexOk) {
|
|
14970
15205
|
ctx.addIssue(
|
|
14971
15206
|
"error",
|
|
14972
15207
|
"ARCHITECTURE_VIOLATION_INDEX_DEP",
|
|
14973
|
-
`Architectural violation: Index component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". An Index is a read projection and may depend only on its Store, a backend Adapter or pure logic
|
|
15208
|
+
`Architectural violation: Index component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". An Index is a read projection and may depend only on its Store, a backend Adapter or pure logic \u2014 or, as an exceptional case, on another Index of the same Repository, never in a cycle.${indexBreach ? ` ${indexBreach}.` : ""}`,
|
|
14974
15209
|
comp.id,
|
|
14975
15210
|
edge.draftContext
|
|
14976
15211
|
);
|
|
@@ -19355,6 +19590,9 @@ var SDD_RULES = [
|
|
|
19355
19590
|
// that would anchor a bare reference to the root instead.
|
|
19356
19591
|
reservedIdSegmentsRule,
|
|
19357
19592
|
namespaceShadowingRule,
|
|
19593
|
+
// The project's own id, beside the spec ids: declared, well-formed, and the
|
|
19594
|
+
// one its lock approved.
|
|
19595
|
+
projectIdentityRule,
|
|
19358
19596
|
roundtripRule,
|
|
19359
19597
|
// The type vocabulary in three questions: what a type declares about
|
|
19360
19598
|
// itself, then the identifiers its fields name, then the ones its
|
|
@@ -19446,6 +19684,8 @@ var SDD_RULES = [
|
|
|
19446
19684
|
publicSurfaceConsumersRule,
|
|
19447
19685
|
publicSurfaceDeclaredTypeRule,
|
|
19448
19686
|
publicSurfaceBoundContractRule,
|
|
19687
|
+
// The export tables the resolver settled: what the published names bind.
|
|
19688
|
+
exportTablesRule,
|
|
19449
19689
|
cyclesRule,
|
|
19450
19690
|
// Semantic-edge family: dispatch/lifecycle validity BEFORE reachability so a
|
|
19451
19691
|
// reader sees the broken edge finding next to the unused-detection fallout
|
|
@@ -20684,7 +20924,7 @@ function buildRuleContext(opts) {
|
|
|
20684
20924
|
for (const sub of subsystems) {
|
|
20685
20925
|
publicSet.set(
|
|
20686
20926
|
sub.id,
|
|
20687
|
-
new Set(sub.publicInterfaces.map((pi) => pi.component).filter((c) => !!c))
|
|
20927
|
+
new Set(sub.publicInterfaces.filter(isOwnComponentEntry).map((pi) => pi.component).filter((c) => !!c))
|
|
20688
20928
|
);
|
|
20689
20929
|
}
|
|
20690
20930
|
const surfaceSnapshots = opts.surfaceSnapshots ?? [];
|
|
@@ -20788,7 +21028,7 @@ function buildRuleContext(opts) {
|
|
|
20788
21028
|
const candidates = [];
|
|
20789
21029
|
for (const snapshot of pool) {
|
|
20790
21030
|
if (provider !== void 0 && !isProvidedBy(snapshot, provider)) continue;
|
|
20791
|
-
const entry = snapshot.interfaces.find((e) => e.component === local || e.id ===
|
|
21031
|
+
const entry = snapshot.interfaces.find((e) => e.id === local) ?? snapshot.interfaces.find((e) => e.component === local && (e.componentType === void 0 || e.id === e.component || e.id === e.interface));
|
|
20792
21032
|
if (entry) candidates.push({ snapshot, entry });
|
|
20793
21033
|
}
|
|
20794
21034
|
if (candidates.length === 0) continue;
|
|
@@ -21027,6 +21267,8 @@ function buildRuleContext(opts) {
|
|
|
21027
21267
|
ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [] },
|
|
21028
21268
|
variants: opts.variants ?? [],
|
|
21029
21269
|
surfaceSnapshots,
|
|
21270
|
+
...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
|
|
21271
|
+
...opts.exportTables ? { exportTables: opts.exportTables } : {},
|
|
21030
21272
|
mountSurfaceSnapshots,
|
|
21031
21273
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
21032
21274
|
roundTripIssues: opts.roundTripIssues,
|
|
@@ -21098,6 +21340,17 @@ function projectPackSelections() {
|
|
|
21098
21340
|
return [];
|
|
21099
21341
|
}
|
|
21100
21342
|
}
|
|
21343
|
+
function boundProjectIdentity() {
|
|
21344
|
+
let config;
|
|
21345
|
+
try {
|
|
21346
|
+
config = loadProjectConfig();
|
|
21347
|
+
} catch {
|
|
21348
|
+
return void 0;
|
|
21349
|
+
}
|
|
21350
|
+
if (!config) return void 0;
|
|
21351
|
+
const lockedProjectId = readLockRecord()?.projectId;
|
|
21352
|
+
return projectIdentity(config, lockedProjectId);
|
|
21353
|
+
}
|
|
21101
21354
|
function findTestsReferencing2(methods, projectRoot, testRoots = []) {
|
|
21102
21355
|
return findTestsReferencing(methods, projectRoot, testRoots);
|
|
21103
21356
|
}
|
|
@@ -21300,8 +21553,17 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
21300
21553
|
scopeSubsystem,
|
|
21301
21554
|
extensions,
|
|
21302
21555
|
variants: loadProjectVariants(),
|
|
21556
|
+
// Every subsystem's resolved export table, then the project's: the
|
|
21557
|
+
// export-tables rule judges the problems the resolver met.
|
|
21558
|
+
exportTables: [
|
|
21559
|
+
...subsystems.map((s) => resolveSubsystemExports(s.id)),
|
|
21560
|
+
resolveProjectExports()
|
|
21561
|
+
],
|
|
21303
21562
|
// By-name selections only: a legacy path ref pins nothing to check.
|
|
21304
21563
|
packSelections: projectPackSelections(),
|
|
21564
|
+
// The run that IS the parent's verdict on a chained child judges the
|
|
21565
|
+
// child's specs, not the parent's identity, so it carries none.
|
|
21566
|
+
projectIdentity: crossTree === "off" ? void 0 : boundProjectIdentity(),
|
|
21305
21567
|
surfaceSnapshots,
|
|
21306
21568
|
mountSurfaceSnapshots,
|
|
21307
21569
|
codeModel,
|
|
@@ -21555,7 +21817,7 @@ function loadSpecGraph() {
|
|
|
21555
21817
|
const implementations = loadImplementationSpecs();
|
|
21556
21818
|
const publicComponents = /* @__PURE__ */ new Set();
|
|
21557
21819
|
for (const sub of subsystems) {
|
|
21558
|
-
for (const pi of sub.publicInterfaces) {
|
|
21820
|
+
for (const pi of sub.publicInterfaces.filter(isOwnComponentEntry)) {
|
|
21559
21821
|
if (pi.component) publicComponents.add(pi.component);
|
|
21560
21822
|
}
|
|
21561
21823
|
}
|
|
@@ -21964,6 +22226,350 @@ function diagramSetIndex(files, systemName) {
|
|
|
21964
22226
|
return lines.join("\n");
|
|
21965
22227
|
}
|
|
21966
22228
|
|
|
22229
|
+
// src/core/exports.ts
|
|
22230
|
+
var CONSUMABLE = /* @__PURE__ */ new Set(["Portal", "Observer"]);
|
|
22231
|
+
function localName2(id) {
|
|
22232
|
+
return id.split("::").pop() ?? id;
|
|
22233
|
+
}
|
|
22234
|
+
function ownedBy(subsystemId, declaringSubsystem) {
|
|
22235
|
+
return declaringSubsystem !== void 0 && (declaringSubsystem === subsystemId || declaringSubsystem.startsWith(`${subsystemId}::`));
|
|
22236
|
+
}
|
|
22237
|
+
function worldOf(subsystems, components, interfaces, types) {
|
|
22238
|
+
return {
|
|
22239
|
+
subsystems: new Map(subsystems.map((s) => [s.id, s])),
|
|
22240
|
+
components: new Map(components.map((c) => [c.id, c])),
|
|
22241
|
+
interfaces: new Map(interfaces.map((i) => [i.id, i])),
|
|
22242
|
+
types: types.reduce((m, t) => m.set(t.id, [...m.get(t.id) ?? [], t]), /* @__PURE__ */ new Map())
|
|
22243
|
+
};
|
|
22244
|
+
}
|
|
22245
|
+
function lookup(map, ref, namespace) {
|
|
22246
|
+
if (!ref) return void 0;
|
|
22247
|
+
return map.get(ref) ?? (namespace ? map.get(`${namespace}::${ref}`) : void 0);
|
|
22248
|
+
}
|
|
22249
|
+
function lookupType(world, ref, subsystemId) {
|
|
22250
|
+
const candidates = lookup(world.types, ref, subsystemId);
|
|
22251
|
+
return candidates?.find((t) => ownedBy(subsystemId, t.subsystem)) ?? candidates?.[0];
|
|
22252
|
+
}
|
|
22253
|
+
function checkPublicName(name, owner, problems) {
|
|
22254
|
+
if (!PUBLIC_NAME_RE.test(name)) {
|
|
22255
|
+
problems.push({
|
|
22256
|
+
kind: "invalid",
|
|
22257
|
+
owner,
|
|
22258
|
+
publicName: name,
|
|
22259
|
+
detail: `public name "${name}" breaks [a-z0-9-_]+ \u2014 rename it with \`as\``
|
|
22260
|
+
});
|
|
22261
|
+
}
|
|
22262
|
+
}
|
|
22263
|
+
function checkConsumable(entry, owner, problems) {
|
|
22264
|
+
if (entry.kind === "component" && entry.componentType && !CONSUMABLE.has(entry.componentType)) {
|
|
22265
|
+
problems.push({
|
|
22266
|
+
kind: "unconsumable",
|
|
22267
|
+
owner,
|
|
22268
|
+
publicName: entry.publicName,
|
|
22269
|
+
targets: [entry.component],
|
|
22270
|
+
detail: `"${entry.publicName}" exports ${entry.componentType} "${entry.component}", which no caller across the boundary may reach \u2014 only a Portal (a gateway is a Portal variant) or an Observer can serve one`
|
|
22271
|
+
});
|
|
22272
|
+
}
|
|
22273
|
+
}
|
|
22274
|
+
function findInSource(world, source, table, request) {
|
|
22275
|
+
if (request.typeDef) {
|
|
22276
|
+
const type = lookupType(world, request.typeDef, source.id);
|
|
22277
|
+
if (!type) return {};
|
|
22278
|
+
const item2 = { publicName: localName2(type.id), kind: "type", source: type.subsystem ?? source.id, typeDef: type.id, via: [] };
|
|
22279
|
+
return { item: item2, found: table.entries.find((e) => e.kind === "type" && e.typeDef === type.id && e.source === item2.source) };
|
|
22280
|
+
}
|
|
22281
|
+
const intf = lookup(world.interfaces, request.interface, source.id);
|
|
22282
|
+
const comp = lookup(world.components, request.component ?? intf?.component, source.id);
|
|
22283
|
+
if (!comp) return {};
|
|
22284
|
+
const narrowed = intf?.id ?? request.interface;
|
|
22285
|
+
const item = {
|
|
22286
|
+
publicName: localName2(narrowed ?? comp.id),
|
|
22287
|
+
kind: "component",
|
|
22288
|
+
source: comp.subsystem,
|
|
22289
|
+
component: comp.id,
|
|
22290
|
+
...narrowed ? { interface: narrowed } : {},
|
|
22291
|
+
componentType: comp.componentType,
|
|
22292
|
+
via: []
|
|
22293
|
+
};
|
|
22294
|
+
const exported = table.entries.filter((e) => e.kind === "component" && e.component === comp.id);
|
|
22295
|
+
const found = narrowed ? exported.find((e) => e.interface === narrowed) ?? exported.find((e) => e.interface === void 0) : exported.find((e) => e.interface === void 0) ?? exported[0];
|
|
22296
|
+
return { item, found: found && narrowed ? { ...found, interface: narrowed } : found };
|
|
22297
|
+
}
|
|
22298
|
+
function bindNamed(world, owner, source, sourceTable, request, publicName, inCycle, problems) {
|
|
22299
|
+
const { found, item } = findInSource(world, source, sourceTable, request);
|
|
22300
|
+
const what = request.typeDef ? `type "${request.typeDef}"` : `component "${request.component}"${request.interface ? ` (interface "${request.interface}")` : ""}`;
|
|
22301
|
+
if (!item) {
|
|
22302
|
+
problems.push({ kind: "invalid", owner, detail: `re-exports ${what} from "${source.id}", which has no such item` });
|
|
22303
|
+
return void 0;
|
|
22304
|
+
}
|
|
22305
|
+
const name = publicName(item);
|
|
22306
|
+
if (found) return { ...found, publicName: name, via: [source.id, ...found.via] };
|
|
22307
|
+
if (inCycle) {
|
|
22308
|
+
problems.push({ kind: "named-cycle", owner, publicName: name, targets: [owner, source.id], detail: `re-exports ${what} from "${source.id}", but the chain leads back to "${owner}" without reaching the item` });
|
|
22309
|
+
return void 0;
|
|
22310
|
+
}
|
|
22311
|
+
const declaring = item.source;
|
|
22312
|
+
if (!ownedBy(source.id, declaring)) {
|
|
22313
|
+
problems.push({ kind: "invalid", owner, publicName: name, detail: `re-exports ${what} from "${source.id}", which neither exports nor owns it` });
|
|
22314
|
+
return void 0;
|
|
22315
|
+
}
|
|
22316
|
+
problems.push({ kind: "invalid", owner, publicName: name, detail: `re-exports ${what} from "${source.id}", which does not export it \u2014 publish it there first` });
|
|
22317
|
+
return { ...item, publicName: name, via: [source.id] };
|
|
22318
|
+
}
|
|
22319
|
+
function bindOwn(world, sub, pi, problems) {
|
|
22320
|
+
if (pi.typeDef) {
|
|
22321
|
+
const type = lookupType(world, pi.typeDef, sub.id);
|
|
22322
|
+
if (!type || !ownedBy(sub.id, type.subsystem)) {
|
|
22323
|
+
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"}` });
|
|
22324
|
+
return void 0;
|
|
22325
|
+
}
|
|
22326
|
+
const name2 = pi.as ?? localName2(type.id);
|
|
22327
|
+
checkPublicName(name2, sub.id, problems);
|
|
22328
|
+
return { publicName: name2, kind: "type", source: type.subsystem ?? sub.id, typeDef: type.id, via: [] };
|
|
22329
|
+
}
|
|
22330
|
+
const comp = lookup(world.components, pi.component, sub.id);
|
|
22331
|
+
if (!comp || !ownedBy(sub.id, comp.subsystem)) return void 0;
|
|
22332
|
+
const intf = lookup(world.interfaces, pi.interface, sub.id);
|
|
22333
|
+
const narrowed = intf?.id ?? pi.interface;
|
|
22334
|
+
const name = pi.as ?? localName2(narrowed ?? comp.id);
|
|
22335
|
+
checkPublicName(name, sub.id, problems);
|
|
22336
|
+
return {
|
|
22337
|
+
publicName: name,
|
|
22338
|
+
kind: "component",
|
|
22339
|
+
source: sub.id,
|
|
22340
|
+
component: comp.id,
|
|
22341
|
+
...narrowed ? { interface: narrowed } : {},
|
|
22342
|
+
componentType: comp.componentType,
|
|
22343
|
+
...pi.type ? { type: pi.type } : {},
|
|
22344
|
+
...pi.details !== void 0 ? { details: pi.details } : {},
|
|
22345
|
+
via: []
|
|
22346
|
+
};
|
|
22347
|
+
}
|
|
22348
|
+
function subsystemCandidates(world, sub, tables, group, problems) {
|
|
22349
|
+
const out = [];
|
|
22350
|
+
sub.publicInterfaces.forEach((pi, index) => {
|
|
22351
|
+
if (pi.from === void 0) {
|
|
22352
|
+
const own = bindOwn(world, sub, pi, problems);
|
|
22353
|
+
if (own) out.push({ entry: own, explicit: true, from: index });
|
|
22354
|
+
return;
|
|
22355
|
+
}
|
|
22356
|
+
const source = world.subsystems.get(pi.from);
|
|
22357
|
+
if (!source || source.id === sub.id) {
|
|
22358
|
+
problems.push({ kind: "invalid", owner: sub.id, detail: source ? `re-exports from itself` : `re-exports from "${pi.from}", which is not a subsystem of this project` });
|
|
22359
|
+
return;
|
|
22360
|
+
}
|
|
22361
|
+
const sourceTable = tables.get(source.id) ?? emptyTable(source.id, "subsystem");
|
|
22362
|
+
if (pi.component !== void 0 || pi.typeDef !== void 0 || pi.interface !== void 0) {
|
|
22363
|
+
const bound2 = bindNamed(
|
|
22364
|
+
world,
|
|
22365
|
+
sub.id,
|
|
22366
|
+
source,
|
|
22367
|
+
sourceTable,
|
|
22368
|
+
{ component: pi.component, interface: pi.interface, typeDef: pi.typeDef },
|
|
22369
|
+
(item) => pi.as ?? localName2(pi.interface ?? item.typeDef ?? item.component ?? ""),
|
|
22370
|
+
group.has(source.id),
|
|
22371
|
+
problems
|
|
22372
|
+
);
|
|
22373
|
+
if (!bound2) return;
|
|
22374
|
+
checkPublicName(bound2.publicName, sub.id, problems);
|
|
22375
|
+
const entry = { ...bound2, ...pi.type ? { type: pi.type } : {}, ...pi.details !== void 0 ? { details: pi.details } : {} };
|
|
22376
|
+
checkConsumable(entry, sub.id, problems);
|
|
22377
|
+
out.push({ entry, explicit: true, from: index });
|
|
22378
|
+
return;
|
|
22379
|
+
}
|
|
22380
|
+
for (const e of sourceTable.entries) {
|
|
22381
|
+
const entry = { ...e, via: [source.id, ...e.via] };
|
|
22382
|
+
checkConsumable(entry, sub.id, problems);
|
|
22383
|
+
out.push({ entry, explicit: false, from: index });
|
|
22384
|
+
}
|
|
22385
|
+
});
|
|
22386
|
+
return out;
|
|
22387
|
+
}
|
|
22388
|
+
function emptyTable(owner, level) {
|
|
22389
|
+
return { owner, level, entries: [], problems: [] };
|
|
22390
|
+
}
|
|
22391
|
+
function settle(owner, level, candidates, problems) {
|
|
22392
|
+
const byName = /* @__PURE__ */ new Map();
|
|
22393
|
+
for (const c of candidates) {
|
|
22394
|
+
const list = byName.get(c.entry.publicName) ?? [];
|
|
22395
|
+
list.push(c);
|
|
22396
|
+
byName.set(c.entry.publicName, list);
|
|
22397
|
+
}
|
|
22398
|
+
const entries = [];
|
|
22399
|
+
for (const [name, list] of byName) {
|
|
22400
|
+
const explicit = list.filter((c) => c.explicit);
|
|
22401
|
+
const pool = explicit.length ? explicit : list;
|
|
22402
|
+
const targets = [...new Set(pool.map((c) => exportTargetKey(c.entry)))];
|
|
22403
|
+
if (targets.length === 1) {
|
|
22404
|
+
entries.push(pool[0].entry);
|
|
22405
|
+
continue;
|
|
22406
|
+
}
|
|
22407
|
+
problems.push({
|
|
22408
|
+
kind: "duplicate",
|
|
22409
|
+
owner,
|
|
22410
|
+
publicName: name,
|
|
22411
|
+
targets,
|
|
22412
|
+
detail: `"${name}" is bound to ${targets.length} different targets (${targets.join(", ")}) by ${explicit.length ? "explicit entries" : "wildcard re-exports"}; it is left out of the table`
|
|
22413
|
+
});
|
|
22414
|
+
}
|
|
22415
|
+
return { owner, level, entries, problems };
|
|
22416
|
+
}
|
|
22417
|
+
function groupsInDependencyOrder(world) {
|
|
22418
|
+
const edges = /* @__PURE__ */ new Map();
|
|
22419
|
+
for (const sub of world.subsystems.values()) {
|
|
22420
|
+
edges.set(sub.id, sub.publicInterfaces.map((pi) => pi.from).filter((f) => f !== void 0 && f !== sub.id && world.subsystems.has(f)));
|
|
22421
|
+
}
|
|
22422
|
+
const index = /* @__PURE__ */ new Map();
|
|
22423
|
+
const low = /* @__PURE__ */ new Map();
|
|
22424
|
+
const stack = [];
|
|
22425
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
22426
|
+
const groups = [];
|
|
22427
|
+
let next = 0;
|
|
22428
|
+
const visit = (v) => {
|
|
22429
|
+
index.set(v, next);
|
|
22430
|
+
low.set(v, next);
|
|
22431
|
+
next++;
|
|
22432
|
+
stack.push(v);
|
|
22433
|
+
onStack.add(v);
|
|
22434
|
+
for (const w of edges.get(v) ?? []) {
|
|
22435
|
+
if (!index.has(w)) {
|
|
22436
|
+
visit(w);
|
|
22437
|
+
low.set(v, Math.min(low.get(v), low.get(w)));
|
|
22438
|
+
} else if (onStack.has(w)) low.set(v, Math.min(low.get(v), index.get(w)));
|
|
22439
|
+
}
|
|
22440
|
+
if (low.get(v) === index.get(v)) {
|
|
22441
|
+
const group = [];
|
|
22442
|
+
let w;
|
|
22443
|
+
do {
|
|
22444
|
+
w = stack.pop();
|
|
22445
|
+
onStack.delete(w);
|
|
22446
|
+
group.push(w);
|
|
22447
|
+
} while (w !== v);
|
|
22448
|
+
groups.push(group.sort());
|
|
22449
|
+
}
|
|
22450
|
+
};
|
|
22451
|
+
for (const id of [...world.subsystems.keys()].sort()) if (!index.has(id)) visit(id);
|
|
22452
|
+
return groups;
|
|
22453
|
+
}
|
|
22454
|
+
function fingerprint(table) {
|
|
22455
|
+
return table.entries.map((e) => `${e.publicName}=${exportTargetKey(e)}`).sort().join("|");
|
|
22456
|
+
}
|
|
22457
|
+
function resolveSubsystemTables(subsystems, components, interfaces, types) {
|
|
22458
|
+
const world = worldOf(subsystems, components, interfaces, types);
|
|
22459
|
+
const tables = /* @__PURE__ */ new Map();
|
|
22460
|
+
for (const group of groupsInDependencyOrder(world)) {
|
|
22461
|
+
const members = new Set(group);
|
|
22462
|
+
const cyclic = group.length > 1;
|
|
22463
|
+
for (let pass = 0, changed = true; changed && pass <= (cyclic ? group.length * 4 + 4 : 0); pass++) {
|
|
22464
|
+
changed = false;
|
|
22465
|
+
for (const id of group) {
|
|
22466
|
+
const problems = [];
|
|
22467
|
+
const table = settle(id, "subsystem", subsystemCandidates(world, world.subsystems.get(id), tables, cyclic ? members : /* @__PURE__ */ new Set(), problems), problems);
|
|
22468
|
+
const before = tables.get(id);
|
|
22469
|
+
if (!before || fingerprint(before) !== fingerprint(table)) changed = true;
|
|
22470
|
+
tables.set(id, table);
|
|
22471
|
+
}
|
|
22472
|
+
}
|
|
22473
|
+
const wildcardLoop = cyclic && group.some((id) => world.subsystems.get(id).publicInterfaces.some((pi) => pi.from !== void 0 && pi.component === void 0 && pi.typeDef === void 0 && pi.interface === void 0 && members.has(pi.from)));
|
|
22474
|
+
if (wildcardLoop) {
|
|
22475
|
+
tables.get(group[0]).problems.push({
|
|
22476
|
+
kind: "wildcard-cycle",
|
|
22477
|
+
owner: group[0],
|
|
22478
|
+
targets: group,
|
|
22479
|
+
detail: `subsystems ${group.map((g) => `"${g}"`).join(", ")} re-export each other through wildcards; their tables resolve to the union, but they form one circular module surface`
|
|
22480
|
+
});
|
|
22481
|
+
}
|
|
22482
|
+
}
|
|
22483
|
+
return tables;
|
|
22484
|
+
}
|
|
22485
|
+
function sourceOf(world, e) {
|
|
22486
|
+
if (e.from ?? e.subsystem) return e.from ?? e.subsystem;
|
|
22487
|
+
if (e.component) return world.components.get(e.component)?.subsystem;
|
|
22488
|
+
if (e.interface) {
|
|
22489
|
+
const intf = world.interfaces.get(e.interface);
|
|
22490
|
+
return intf ? world.components.get(intf.component)?.subsystem : void 0;
|
|
22491
|
+
}
|
|
22492
|
+
if (e.typeDef) return world.types.get(e.typeDef)?.[0]?.subsystem;
|
|
22493
|
+
return void 0;
|
|
22494
|
+
}
|
|
22495
|
+
function withEntryMetadata(entry, e) {
|
|
22496
|
+
return {
|
|
22497
|
+
...entry,
|
|
22498
|
+
audience: e.audience ?? "instance",
|
|
22499
|
+
...e.name ? { name: e.name } : {},
|
|
22500
|
+
...e.type ? { type: e.type } : {},
|
|
22501
|
+
...e.details !== void 0 ? { details: e.details } : {},
|
|
22502
|
+
...e.authPolicy ? { authPolicy: e.authPolicy } : {},
|
|
22503
|
+
...e.version ? { version: e.version } : {},
|
|
22504
|
+
...e.stability ? { stability: e.stability } : {}
|
|
22505
|
+
};
|
|
22506
|
+
}
|
|
22507
|
+
function resolveProjectTable(system, subsystems, components, interfaces, types, subsystemTables) {
|
|
22508
|
+
const world = worldOf(subsystems, components, interfaces, types);
|
|
22509
|
+
const owner = system.name;
|
|
22510
|
+
const problems = [];
|
|
22511
|
+
const candidates = [];
|
|
22512
|
+
(system.publicInterfaces ?? []).forEach((e, index) => {
|
|
22513
|
+
const sourceId = sourceOf(world, e);
|
|
22514
|
+
const source = sourceId ? world.subsystems.get(sourceId) : void 0;
|
|
22515
|
+
if (!source) {
|
|
22516
|
+
const label = e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef ?? `#${index + 1}`;
|
|
22517
|
+
problems.push({ kind: "invalid", owner, publicName: label, detail: sourceId ? `"${label}" re-exports from "${sourceId}", which is not a subsystem of this project` : `"${label}" names no source subsystem and no item whose owner could supply one` });
|
|
22518
|
+
return;
|
|
22519
|
+
}
|
|
22520
|
+
const sourceTable = subsystemTables.get(source.id) ?? emptyTable(source.id, "subsystem");
|
|
22521
|
+
if (e.component !== void 0 || e.typeDef !== void 0 || e.interface !== void 0) {
|
|
22522
|
+
const bound2 = bindNamed(
|
|
22523
|
+
world,
|
|
22524
|
+
owner,
|
|
22525
|
+
source,
|
|
22526
|
+
sourceTable,
|
|
22527
|
+
{ component: e.component, interface: e.interface, typeDef: e.typeDef },
|
|
22528
|
+
() => e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef,
|
|
22529
|
+
false,
|
|
22530
|
+
problems
|
|
22531
|
+
);
|
|
22532
|
+
if (!bound2) return;
|
|
22533
|
+
checkPublicName(bound2.publicName, owner, problems);
|
|
22534
|
+
const entry = withEntryMetadata(bound2, e);
|
|
22535
|
+
checkConsumable(entry, owner, problems);
|
|
22536
|
+
candidates.push({ entry, explicit: true, from: index });
|
|
22537
|
+
return;
|
|
22538
|
+
}
|
|
22539
|
+
for (const found of sourceTable.entries) {
|
|
22540
|
+
const entry = withEntryMetadata({ ...found, via: [source.id, ...found.via] }, e);
|
|
22541
|
+
checkConsumable(entry, owner, problems);
|
|
22542
|
+
candidates.push({ entry, explicit: false, from: index });
|
|
22543
|
+
}
|
|
22544
|
+
});
|
|
22545
|
+
return settle(owner, "project", candidates, problems);
|
|
22546
|
+
}
|
|
22547
|
+
var subsystemMemo = /* @__PURE__ */ new WeakMap();
|
|
22548
|
+
var projectMemo = /* @__PURE__ */ new WeakMap();
|
|
22549
|
+
function scanTables(subsystems) {
|
|
22550
|
+
const memo = subsystemMemo.get(subsystems);
|
|
22551
|
+
if (memo) return memo;
|
|
22552
|
+
const tables = resolveSubsystemTables(subsystems, loadComponentSpecs(), loadInterfaceSpecs(), loadTypeSpecs());
|
|
22553
|
+
subsystemMemo.set(subsystems, tables);
|
|
22554
|
+
return tables;
|
|
22555
|
+
}
|
|
22556
|
+
function resolveSubsystemExportTable(subsystemId) {
|
|
22557
|
+
const tables = scanTables(loadSubsystemSpecs());
|
|
22558
|
+
return tables.get(subsystemId) ?? emptyTable(subsystemId, "subsystem");
|
|
22559
|
+
}
|
|
22560
|
+
function resolveProjectExportTable() {
|
|
22561
|
+
const system = loadSystemSpec();
|
|
22562
|
+
if (!system) return emptyTable("", "project");
|
|
22563
|
+
const subsystems = loadSubsystemSpecs();
|
|
22564
|
+
const key = JSON.stringify([system.name, system.publicInterfaces ?? []]);
|
|
22565
|
+
const memo = projectMemo.get(subsystems);
|
|
22566
|
+
if (memo && memo.key === key) return memo.table;
|
|
22567
|
+
const subsystemTables = new Map(subsystems.map((sub) => [sub.id, resolveSubsystemExportTable(sub.id)]));
|
|
22568
|
+
const table = resolveProjectTable(system, subsystems, loadComponentSpecs(), loadInterfaceSpecs(), loadTypeSpecs(), subsystemTables);
|
|
22569
|
+
projectMemo.set(subsystems, { key, table });
|
|
22570
|
+
return table;
|
|
22571
|
+
}
|
|
22572
|
+
|
|
21967
22573
|
// src/core/specs.ts
|
|
21968
22574
|
function emptyIndex() {
|
|
21969
22575
|
return {
|
|
@@ -22212,13 +22818,18 @@ function mergeMountRealizations(subs) {
|
|
|
22212
22818
|
function stripNamespaceFromSubsystem(spec, prefix) {
|
|
22213
22819
|
const memberPrefix = spec.projectPath ? spec.id : prefix;
|
|
22214
22820
|
const relMember = (id) => id.includes("::") ? relativizeId(id, memberPrefix) : id;
|
|
22821
|
+
const relPeer = (id) => id.includes("::") ? relativizeId(id, prefix) : id;
|
|
22215
22822
|
return {
|
|
22216
22823
|
...spec,
|
|
22217
22824
|
id: relativizeId(spec.id, prefix),
|
|
22218
22825
|
publicInterfaces: spec.publicInterfaces.map((pi) => ({
|
|
22219
22826
|
...pi,
|
|
22220
|
-
|
|
22221
|
-
|
|
22827
|
+
// A re-export names a peer's item, written relative to the namespace the
|
|
22828
|
+
// subsystem is declared in (the loader's inverse), not its members'.
|
|
22829
|
+
component: pi.component ? pi.from ? relPeer(pi.component) : relMember(pi.component) : void 0,
|
|
22830
|
+
interface: pi.interface ? pi.from ? relPeer(pi.interface) : relMember(pi.interface) : void 0,
|
|
22831
|
+
...pi.typeDef ? { typeDef: pi.from ? relPeer(pi.typeDef) : relMember(pi.typeDef) } : {},
|
|
22832
|
+
...pi.from ? { from: relPeer(pi.from) } : {},
|
|
22222
22833
|
// consumers name SUBSYSTEMS — peers of this one, so they are relative to
|
|
22223
22834
|
// the namespace the subsystem itself is declared in, never its members'.
|
|
22224
22835
|
...pi.consumers ? { consumers: pi.consumers.map((c) => c.includes("::") ? relativizeId(c, prefix) : c) } : {}
|
|
@@ -22495,6 +23106,14 @@ function identityKeyOf(field, item) {
|
|
|
22495
23106
|
}
|
|
22496
23107
|
function publicInterfaceKey(o) {
|
|
22497
23108
|
const str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
23109
|
+
const from = str(o.from);
|
|
23110
|
+
const typeDef = str(o.typeDef);
|
|
23111
|
+
if (from || typeDef) {
|
|
23112
|
+
const item = typeDef ? `type ${typeDef}` : str(o.component) ?? "*";
|
|
23113
|
+
const narrowed = str(o.interface) ? `.${String(o.interface)}` : "";
|
|
23114
|
+
const renamed = str(o.as) ? ` as ${String(o.as)}` : "";
|
|
23115
|
+
return `${from ? `${from}:` : ""}${item}${narrowed}${renamed}`;
|
|
23116
|
+
}
|
|
22498
23117
|
const component = str(o.component);
|
|
22499
23118
|
if (component) return str(o.interface) ? `${component}.${String(o.interface)}` : component;
|
|
22500
23119
|
if (typeof o.type === "string" && typeof o.details === "string") return `${o.type} ${o.details}`;
|
|
@@ -22711,8 +23330,9 @@ function identityFieldsOf(field) {
|
|
|
22711
23330
|
case "dispatch":
|
|
22712
23331
|
return ["capability"];
|
|
22713
23332
|
// Bound: component + interface; not yet bound: type + details.
|
|
23333
|
+
// Re-exports and type exports: from + item + interface + as.
|
|
22714
23334
|
case "publicInterfaces":
|
|
22715
|
-
return ["component", "interface", "type", "details"];
|
|
23335
|
+
return ["component", "interface", "type", "details", "from", "typeDef", "as"];
|
|
22716
23336
|
case "lifecycle":
|
|
22717
23337
|
return ["phase", "component", "method"];
|
|
22718
23338
|
case "emits":
|
|
@@ -22984,14 +23604,19 @@ var SpecWorkspace = class {
|
|
|
22984
23604
|
return {
|
|
22985
23605
|
...sub,
|
|
22986
23606
|
id: qualifiedSubId,
|
|
22987
|
-
publicInterfaces: sub.publicInterfaces.map((p) =>
|
|
22988
|
-
|
|
22989
|
-
|
|
22990
|
-
|
|
22991
|
-
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
23607
|
+
publicInterfaces: sub.publicInterfaces.map((p) => {
|
|
23608
|
+
const itemPrefix = p.from ? namespacePrefix : componentPrefix;
|
|
23609
|
+
return {
|
|
23610
|
+
...p,
|
|
23611
|
+
component: p.component ? qualifyId(p.component, itemPrefix, this.rootSubsystems) : void 0,
|
|
23612
|
+
interface: p.interface ? qualifyId(p.interface, itemPrefix, this.rootSubsystems) : void 0,
|
|
23613
|
+
...p.typeDef ? { typeDef: qualifyId(p.typeDef, itemPrefix, this.rootSubsystems) } : {},
|
|
23614
|
+
...p.from ? { from: qualifySubsystemRef(p.from, namespacePrefix, this.rootSubsystems) } : {},
|
|
23615
|
+
// Each consumer is a subsystem reference: qualified into the namespace
|
|
23616
|
+
// this subsystem is declared in, like a component's `subsystem` field.
|
|
23617
|
+
...p.consumers ? { consumers: p.consumers.map((c) => qualifySubsystemRef(c, namespacePrefix, this.rootSubsystems)) } : {}
|
|
23618
|
+
};
|
|
23619
|
+
}),
|
|
22995
23620
|
lifecycle: sub.lifecycle?.map((le) => ({
|
|
22996
23621
|
...le,
|
|
22997
23622
|
component: qualifyId(le.component, componentPrefix, this.rootSubsystems)
|
|
@@ -24603,8 +25228,12 @@ var SpecWorkspace = class {
|
|
|
24603
25228
|
if (Array.isArray(out.publicInterfaces)) {
|
|
24604
25229
|
out.publicInterfaces = out.publicInterfaces.map((pi) => ({
|
|
24605
25230
|
...pi,
|
|
24606
|
-
|
|
24607
|
-
|
|
25231
|
+
// A re-export names a peer's item: qualified into the declaring
|
|
25232
|
+
// namespace (q), not this subsystem's members' (qm).
|
|
25233
|
+
...typeof pi?.component === "string" ? { component: (typeof pi?.from === "string" ? q : qm)(pi.component) } : {},
|
|
25234
|
+
...typeof pi?.interface === "string" ? { interface: (typeof pi?.from === "string" ? q : qm)(pi.interface) } : {},
|
|
25235
|
+
...typeof pi?.typeDef === "string" ? { typeDef: (typeof pi?.from === "string" ? q : qm)(pi.typeDef) } : {},
|
|
25236
|
+
...typeof pi?.from === "string" ? { from: qualifySubsystemRef(pi.from, prefix, this.rootSubsystems) } : {},
|
|
24608
25237
|
...Array.isArray(pi?.consumers) ? { consumers: pi.consumers.map((c) => typeof c === "string" ? qualifySubsystemRef(c, prefix, this.rootSubsystems) : c) } : {}
|
|
24609
25238
|
}));
|
|
24610
25239
|
}
|
|
@@ -25398,6 +26027,12 @@ function loadTypeSpecs() {
|
|
|
25398
26027
|
function loadTypeSpec(id) {
|
|
25399
26028
|
return current().loadTypeSpec(id);
|
|
25400
26029
|
}
|
|
26030
|
+
function resolveSubsystemExports(subsystemId) {
|
|
26031
|
+
return resolveSubsystemExportTable(subsystemId);
|
|
26032
|
+
}
|
|
26033
|
+
function resolveProjectExports() {
|
|
26034
|
+
return resolveProjectExportTable();
|
|
26035
|
+
}
|
|
25401
26036
|
function dryRunSerializeSpecs(include) {
|
|
25402
26037
|
return current().dryRunSerializeSpecs(include);
|
|
25403
26038
|
}
|
|
@@ -25590,9 +26225,11 @@ function unregisterDomain(id) {
|
|
|
25590
26225
|
// src/core/provision.ts
|
|
25591
26226
|
var fs12 = __toESM(require("fs"));
|
|
25592
26227
|
var path17 = __toESM(require("path"));
|
|
25593
|
-
function defaultProjectConfig(name, now) {
|
|
26228
|
+
function defaultProjectConfig(name, now, id) {
|
|
26229
|
+
const declared = id ?? effectiveProjectId({ name });
|
|
25594
26230
|
return {
|
|
25595
26231
|
schemaVersion: "1.0.0",
|
|
26232
|
+
...declared !== null ? { id: declared } : {},
|
|
25596
26233
|
name,
|
|
25597
26234
|
projectType: "backend",
|
|
25598
26235
|
targets: [{ type: "claude", outputDir: ".claude/agents", enabled: true }],
|
|
@@ -25634,7 +26271,7 @@ function provisionProject(name) {
|
|
|
25634
26271
|
projectConfigRepository.create(config);
|
|
25635
26272
|
saveSystemSpec(bootstrapSystemSpec(name, now));
|
|
25636
26273
|
}
|
|
25637
|
-
function ensureProjectInitialized(fallbackName) {
|
|
26274
|
+
function ensureProjectInitialized(fallbackName, id) {
|
|
25638
26275
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25639
26276
|
const paths = aiPathsAt(getProjectRoot());
|
|
25640
26277
|
const hasSystem = fs12.existsSync(paths.specsSystem());
|
|
@@ -25646,7 +26283,7 @@ function ensureProjectInitialized(fallbackName) {
|
|
|
25646
26283
|
let wroteConfig = false;
|
|
25647
26284
|
let wroteSystem = false;
|
|
25648
26285
|
if (!projectConfigRepository.exists()) {
|
|
25649
|
-
projectConfigRepository.create(defaultProjectConfig(name, now));
|
|
26286
|
+
projectConfigRepository.create(defaultProjectConfig(name, now, id));
|
|
25650
26287
|
wroteConfig = true;
|
|
25651
26288
|
}
|
|
25652
26289
|
if (!hasSystem) {
|
|
@@ -25727,7 +26364,7 @@ function backfillChainedSubprojectConfigs(projectRoot) {
|
|
|
25727
26364
|
walkChainedSubprojects(projectRoot, (childDir, subsystemId) => {
|
|
25728
26365
|
if (childHasSpecsButNoConfig(childDir)) {
|
|
25729
26366
|
runWithProjectRoot(childDir, () => {
|
|
25730
|
-
ensureProjectInitialized(subsystemId);
|
|
26367
|
+
ensureProjectInitialized(subsystemId, localSegment(subsystemId));
|
|
25731
26368
|
});
|
|
25732
26369
|
backfilled.push(childDir);
|
|
25733
26370
|
}
|
|
@@ -25743,7 +26380,7 @@ function createChainedSubsystem(subsystem, projectName) {
|
|
|
25743
26380
|
const childDir = assertContainedProjectPath(getProjectRoot(), projectPath);
|
|
25744
26381
|
runWithProjectRoot(childDir, () => {
|
|
25745
26382
|
ensureDir(aiPathsAt(childDir).specsDir());
|
|
25746
|
-
ensureProjectInitialized(projectName);
|
|
26383
|
+
ensureProjectInitialized(projectName, localSegment(subsystem.id));
|
|
25747
26384
|
});
|
|
25748
26385
|
invalidateSpecCache();
|
|
25749
26386
|
}
|
|
@@ -25774,6 +26411,9 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
|
|
|
25774
26411
|
saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
25775
26412
|
invalidateSpecCache();
|
|
25776
26413
|
}
|
|
26414
|
+
function localSegment(subsystemId) {
|
|
26415
|
+
return subsystemId.split("::").pop();
|
|
26416
|
+
}
|
|
25777
26417
|
function toPosixPath(p) {
|
|
25778
26418
|
return p.replace(/\\/g, "/");
|
|
25779
26419
|
}
|
|
@@ -25810,7 +26450,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
25810
26450
|
const childSystemName = foo.name || subsystemId;
|
|
25811
26451
|
runWithProjectRoot(childDir, () => {
|
|
25812
26452
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25813
|
-
projectConfigRepository.create(defaultProjectConfig(childSystemName, now));
|
|
26453
|
+
projectConfigRepository.create(defaultProjectConfig(childSystemName, now, subsystemId));
|
|
25814
26454
|
ensureDir(path17.join(childDir, ".wai", "specs"));
|
|
25815
26455
|
saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
|
|
25816
26456
|
});
|
|
@@ -29018,7 +29658,13 @@ function qualifiedComponentId(id) {
|
|
|
29018
29658
|
return chained.length === 1 ? chained[0] : id;
|
|
29019
29659
|
}
|
|
29020
29660
|
function publicInterfacesReplacement(subsystem, stored, list) {
|
|
29021
|
-
const identity = (pi) =>
|
|
29661
|
+
const identity = (pi) => {
|
|
29662
|
+
if (pi.from || pi.typeDef) {
|
|
29663
|
+
const item = pi.typeDef ? `type ${pi.typeDef}` : pi.component ?? "*";
|
|
29664
|
+
return `${pi.from ? `${pi.from}:` : ""}${item}${pi.interface ? `.${pi.interface}` : ""}${pi.as ? ` as ${pi.as}` : ""}`;
|
|
29665
|
+
}
|
|
29666
|
+
return pi.component ? `${pi.component}${pi.interface ? `.${pi.interface}` : ""}` : `${pi.type} ${pi.details}`;
|
|
29667
|
+
};
|
|
29022
29668
|
const restated = /* @__PURE__ */ new Map();
|
|
29023
29669
|
for (const pi of list) restated.set(identity(pi), (restated.get(identity(pi)) ?? 0) + 1);
|
|
29024
29670
|
const twice = [...restated].filter(([, n]) => n > 1).map(([key]) => `"${key}"`);
|
|
@@ -29031,7 +29677,7 @@ function publicInterfacesReplacement(subsystem, stored, list) {
|
|
|
29031
29677
|
const key = identity(pi);
|
|
29032
29678
|
seen.set(key, (seen.get(key) ?? 0) + 1);
|
|
29033
29679
|
if (seen.get(key) > (restated.get(key) ?? 0)) {
|
|
29034
|
-
deletes.push(pi.component ? { component: pi.component, interface: pi.interface, action: "delete" } : { type: pi.type, details: pi.details, action: "delete" });
|
|
29680
|
+
deletes.push(pi.from || pi.typeDef ? { from: pi.from, component: pi.component, interface: pi.interface, typeDef: pi.typeDef, as: pi.as, action: "delete" } : pi.component ? { component: pi.component, interface: pi.interface, action: "delete" } : { type: pi.type, details: pi.details, action: "delete" });
|
|
29035
29681
|
}
|
|
29036
29682
|
}
|
|
29037
29683
|
const restatedEntries = list.map((pi) => {
|
|
@@ -29656,17 +30302,21 @@ function createMcpServer(options = {}) {
|
|
|
29656
30302
|
}
|
|
29657
30303
|
}
|
|
29658
30304
|
);
|
|
30305
|
+
const publicInterfaceItemInput = import_zod11.z.object({
|
|
30306
|
+
type: import_zod11.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]).optional().describe("Transport kind of an own item (required on an own component item); a re-export inherits its target's"),
|
|
30307
|
+
details: import_zod11.z.string().optional().describe("Consumer-facing description of an own item (required on an own component item); a re-export inherits its target's"),
|
|
30308
|
+
component: import_zod11.z.string().optional().describe("For an own item, the L2 component that realizes it (this subsystem's published surface); for a re-export, the component re-exported from `from`"),
|
|
30309
|
+
interface: import_zod11.z.string().optional().describe("Optional L3 interface id the entry narrows to"),
|
|
30310
|
+
consumers: import_zod11.z.array(import_zod11.z.string()).optional().describe("The subsystem ids this surface is published to. Absent, any subsystem may depend on the component; present, only these may \u2014 when every entry publishing the component names consumers, the union of the lists is the whole set, and a dependency from any other subsystem is CROSS_SUBSYSTEM_UNLISTED_CONSUMER. Every id must name a subsystem of the tree (PUBLIC_INTERFACE_UNKNOWN_CONSUMER)."),
|
|
30311
|
+
from: import_zod11.z.string().optional().describe("Re-export source: another subsystem of this project. With neither component nor typeDef, the entry re-exports everything that subsystem exports (a wildcard)"),
|
|
30312
|
+
typeDef: import_zod11.z.string().optional().describe("A type exported as is: owned by this subsystem, or re-exported from `from`"),
|
|
30313
|
+
as: import_zod11.z.string().optional().describe("The public name; defaults to the narrowed interface, then the component or type id. Must fit [a-z0-9-_]+ (EXPORT_INVALID otherwise)")
|
|
30314
|
+
}).strict();
|
|
29659
30315
|
const subsystemInput = {
|
|
29660
30316
|
id: import_zod11.z.string().describe("Lowercase identifier for the subsystem"),
|
|
29661
30317
|
name: import_zod11.z.string().describe("Human-readable display name"),
|
|
29662
30318
|
description: import_zod11.z.string().describe("Purpose and details of the subsystem"),
|
|
29663
|
-
publicInterfaces: import_zod11.z.array(
|
|
29664
|
-
type: import_zod11.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]),
|
|
29665
|
-
details: import_zod11.z.string(),
|
|
29666
|
-
component: import_zod11.z.string().optional().describe("The L2 component id that realizes this interface (this subsystem's published surface)"),
|
|
29667
|
-
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component backing this entry"),
|
|
29668
|
-
consumers: import_zod11.z.array(import_zod11.z.string()).optional().describe("The subsystem ids this surface is published to. Absent, any subsystem may depend on the component; present, only these may \u2014 when every entry publishing the component names consumers, the union of the lists is the whole set, and a dependency from any other subsystem is CROSS_SUBSYSTEM_UNLISTED_CONSUMER. Every id must name a subsystem of the tree (PUBLIC_INTERFACE_UNKNOWN_CONSUMER).")
|
|
29669
|
-
}).strict()).optional().describe("Public entrypoints exposed by this subsystem, each bound to a realizing component"),
|
|
30319
|
+
publicInterfaces: import_zod11.z.array(publicInterfaceItemInput).optional().describe("The subsystem's export table: its own entrypoints, each bound to a realizing component, and re-exports of what other subsystems of this project export"),
|
|
29670
30320
|
projectPath: import_zod11.z.string().optional().describe("Relative path to external project root for subsystem chaining"),
|
|
29671
30321
|
targetLanguage: import_zod11.z.string().optional().describe("Override of the system-level targetLanguage for this subsystem"),
|
|
29672
30322
|
profile: import_zod11.z.string().optional().describe("Architectural profile override for this subsystem (built-ins: backend, frontend-reactive, frontend-controller, lowlevel-os, game-ecs, realtime-embedded, plc-cyclic; extension packs may add more \u2014 unknown names get UNKNOWN_PROFILE)"),
|
|
@@ -29728,16 +30378,10 @@ function createMcpServer(options = {}) {
|
|
|
29728
30378
|
server,
|
|
29729
30379
|
"sdd_set_public_interfaces",
|
|
29730
30380
|
{
|
|
29731
|
-
description: "Set (replace) an existing subsystem's publicInterfaces
|
|
30381
|
+
description: "Set (replace) an existing subsystem's export table (its publicInterfaces). An OWN item binds type + details to the component (and optional interface) that realizes it, or exports a type the subsystem owns (typeDef). A RE-EXPORT names another subsystem of the project in `from` and one component (optionally narrowed with `interface`) or one typeDef from what it exports, optionally renamed with `as` \u2014 `from` alone re-exports everything it exports; type and details are optional on a re-export, which inherits its target's. Use this to backfill bindings once the subsystem's components exist \u2014 cross-subsystem dependencies may only target a component its owning subsystem publishes; a re-export is a name, not a licence. A replacement, not a merge: an entry left out is removed. An own entry's identity is its component and interface, a re-export's its source, item and name, so a list naming one identity twice is refused. Written through the authoring seam as one gated delta, and answered with the change report \u2014 every entry added, changed or removed, or that nothing changed \u2014 as structured content beside the sentence.",
|
|
29732
30382
|
inputSchema: {
|
|
29733
30383
|
subsystem: import_zod11.z.string().describe("The L1 subsystem id to update"),
|
|
29734
|
-
publicInterfaces: import_zod11.z.array(
|
|
29735
|
-
type: import_zod11.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]),
|
|
29736
|
-
details: import_zod11.z.string(),
|
|
29737
|
-
component: import_zod11.z.string().optional().describe("The L2 component id that realizes this interface"),
|
|
29738
|
-
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component"),
|
|
29739
|
-
consumers: import_zod11.z.array(import_zod11.z.string()).optional().describe("The subsystem ids this surface is published to. Absent, any subsystem may depend on the component; present, only these may \u2014 when every entry publishing the component names consumers, the union of the lists is the whole set, and a dependency from any other subsystem is CROSS_SUBSYSTEM_UNLISTED_CONSUMER. Every id must name a subsystem of the tree (PUBLIC_INTERFACE_UNKNOWN_CONSUMER).")
|
|
29740
|
-
}).strict()).describe("The full replacement list of public interfaces for this subsystem")
|
|
30384
|
+
publicInterfaces: import_zod11.z.array(publicInterfaceItemInput).describe("The full replacement list of the subsystem's export table: own items and re-exports")
|
|
29741
30385
|
},
|
|
29742
30386
|
outputSchema: specChangeReportOutput
|
|
29743
30387
|
},
|
|
@@ -30003,7 +30647,7 @@ ${renderChangeReport(report)}`,
|
|
|
30003
30647
|
}).strict().optional().describe("Typed acknowledgment of a real caller OUTSIDE the modeled narrative graph. Unused-detection seeds the method as an entrypoint so reachability propagates through its narrative (unlike lint.allow); a method the internal walk already reaches is flagged stale (INVOKED_BY_REDUNDANT). Prefer a `register` narrative step when the wiring is internal."),
|
|
30004
30648
|
findings: import_zod11.z.array(import_zod11.z.object({
|
|
30005
30649
|
code: import_zod11.z.string().describe("UPPER_SNAKE finding code, unique within the method; a pack's codes carry the pack prefix (<PACK>_<CODE>)"),
|
|
30006
|
-
severity: import_zod11.z.enum(["error", "warning"]).describe("Default severity, before project severity overrides and draft-context downgrades"),
|
|
30650
|
+
severity: import_zod11.z.enum(["error", "warning", "notice"]).describe("Default severity, before project severity overrides and draft-context downgrades. A notice is reported but never fails the gate"),
|
|
30007
30651
|
summary: import_zod11.z.string().describe("One line saying what the finding means")
|
|
30008
30652
|
}).strict()).optional().describe("The finding codes this method can report, each with its default severity and summary. Each declared code must be anchored in the method's source file, as a string literal or a property-access name (UNREALIZED_FINDING). A code is declared once per method; sdd_update_spec upserts and deletes findings by code."),
|
|
30009
30653
|
ext: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Opaque pack/tool extension data for this method (namespaced keys) \u2014 preserved verbatim")
|
|
@@ -30447,9 +31091,9 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
30447
31091
|
{
|
|
30448
31092
|
description: "Update/patch an existing SDD specification (subsystem, component, interface, implementation, or type) using a granular delta. Updates fields, appends/merges array elements, or inserts/deletes narrative steps. Answers with exactly what changed, and with every path the delta named that the write did not act on. Pass dryRun to be told what it would do without writing it.",
|
|
30449
31093
|
inputSchema: {
|
|
30450
|
-
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The spec kind to update (system = the singleton L0 \u2014 vision, boundaries, globalRequirements, databases, and publicInterfaces: the project
|
|
31094
|
+
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The spec kind to update (system = the singleton L0 \u2014 vision, boundaries, globalRequirements, databases, and publicInterfaces: the project export table, each entry a re-export {from, component, interface, typeDef, as, name, type, details, audience: project|department|instance|partner|external} \u2014 from alone re-exports everything a subsystem exports, and a legacy {id, subsystem} reads as {as, from})"),
|
|
30451
31095
|
id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
|
|
30452
|
-
delta: import_zod11.z.record(import_zod11.z.any()).describe(`The partial fields to merge into the spec. ARRAYS UPSERT, they do not replace: an array whose elements carry an identity is merged element-by-element, so a delta naming ONE element leaves the others intact. Identity is "name" or "id" by default, and per field: dispatch by "capability", a listener's mounts by "portal", lifecycle by phase+component+method, emits/subscribesTo by topic+event, trustedLinks by "subsystem", a subsystem's publicInterfaces by component+interface (or type+details for an entry not yet bound to a component), invariants and patterns by "id", lint.allow by "code" AND "at" (an allow covers one occurrence, so several may share a code on one spec), an interface method's findings by "code", boundaries by "name", globalRequirements by "description", switch cases by "value", try catches by "error". Identity merging applies at EVERY depth, including an array INSIDE an element (a method's params, a step's catches). Add "action: 'delete'" (or "remove: true") alongside that identity to REMOVE an element \u2014 including a stale lint allow. Arrays of plain STRINGS (owns, dependsOn, guarantees) carry no per-element identity and are replaced wholesale; pass [] to clear any array outright. To REMOVE an optional field entirely, list it in "unset": e.g. {"unset": ["basePath", "variant"]} \u2014 passing null/undefined means "no change" (they are skipped), and writing "" would leave the field present but empty, which is a different and usually wrong spec. Unsetting a required field is refused by schema validation, which names it. For narrative steps, match by "stepNumber" and use "action: 'insert'" (shifts subsequent steps up) or "action: 'delete'" (shifts subsequent steps down and removes it). Step entries apply in ASCENDING stepNumber order, each against the numbering the earlier entries of the SAME delta left behind \u2014 delete step 3 and step 7 becomes step 6 \u2014 so prefer labels, and restate the step's "label" or "description" on a delete to have it checked against the step actually addressed. Renumbering RELOCATES every flow jump field (onTrueStep/onFalseStep/cases.step/defaultStep/endStep/catches.step/finallyStep/toStep) in the same narrative. A delete is REJECTED when the narrative has no such step, when a jump still targets it (retarget the referrers first), when a restated label/description does not match, or when it is a loop/try/parallel header whose body would be left standing (retype the header first to dissolve the region, then delete it). Changing a step's "type" REBUILDS it for the new type: its description and label are kept and every field the new type cannot carry is dropped (returned as a NOTICE); a delta that retypes AND sets such a field is refused. A step delta is also refused when it carries a marker the merge does not recognise: a non-boolean "remove", an "action" that is neither "insert" nor "delete", a "captureJumps" outside an insert, or no "stepNumber" to address. Inserting AT a jump target relocates those jumps past the inserted step by default (a NOTICE is returned) \u2014 add "captureJumps": true on the inserted step to retarget entry jumps onto it (loop/try endStep region tails always relocate with the body and are never captured). Every jump field has a "*Label" twin (toLabel, onFalseLabel, endLabel, \u2026, and "label" on a cases/catches entry) resolved against step labels AFTER the merge, so a delta may anchor on a label only pre-existing steps carry; a label the delta supplies REPLACES the stored number it twins, while setting the number and its label together in one delta is refused as a contradiction. Reference ids in deltas may use LOCAL names \u2014 they are qualified against the spec's namespace exactly as the loader would. Per-spec lint suppression: set "lint: { allow: [{ code, at, covers, reason }] }" to silence a WARNING or NOTICE code on this spec only (errors always surface; stale allows are flagged). An allow covers EXACTLY the occurrence it names: "at" is the site the finding names (a contract method, an import edge "from -> to", a declared edge "component -> target") and is REQUIRED for a code whose findings report one, while a finding that reports no site is covered only by an allow that names none; "covers" lists the units an aggregating finding reports, and the allow silences it only when every one is listed. This delta is deliberately OPEN below its top level \u2014 the shapes nest further than a schema here should restate \u2014 so a key that is not a field at its depth is not refused, it is NAMED BACK under NO EFFECT in the answer, together with any value the spec already held and any "unset" that removed nothing. Read that list: it is where a nested typo shows up.`),
|
|
31096
|
+
delta: import_zod11.z.record(import_zod11.z.any()).describe(`The partial fields to merge into the spec. ARRAYS UPSERT, they do not replace: an array whose elements carry an identity is merged element-by-element, so a delta naming ONE element leaves the others intact. Identity is "name" or "id" by default, and per field: dispatch by "capability", a listener's mounts by "portal", lifecycle by phase+component+method, emits/subscribesTo by topic+event, trustedLinks by "subsystem", a subsystem's publicInterfaces by component+interface (or type+details for an entry not yet bound to a component, and by from+item+interface+as for a re-export or a type export), invariants and patterns by "id", lint.allow by "code" AND "at" (an allow covers one occurrence, so several may share a code on one spec), an interface method's findings by "code", boundaries by "name", globalRequirements by "description", switch cases by "value", try catches by "error". Identity merging applies at EVERY depth, including an array INSIDE an element (a method's params, a step's catches). Add "action: 'delete'" (or "remove: true") alongside that identity to REMOVE an element \u2014 including a stale lint allow. Arrays of plain STRINGS (owns, dependsOn, guarantees) carry no per-element identity and are replaced wholesale; pass [] to clear any array outright. To REMOVE an optional field entirely, list it in "unset": e.g. {"unset": ["basePath", "variant"]} \u2014 passing null/undefined means "no change" (they are skipped), and writing "" would leave the field present but empty, which is a different and usually wrong spec. Unsetting a required field is refused by schema validation, which names it. For narrative steps, match by "stepNumber" and use "action: 'insert'" (shifts subsequent steps up) or "action: 'delete'" (shifts subsequent steps down and removes it). Step entries apply in ASCENDING stepNumber order, each against the numbering the earlier entries of the SAME delta left behind \u2014 delete step 3 and step 7 becomes step 6 \u2014 so prefer labels, and restate the step's "label" or "description" on a delete to have it checked against the step actually addressed. Renumbering RELOCATES every flow jump field (onTrueStep/onFalseStep/cases.step/defaultStep/endStep/catches.step/finallyStep/toStep) in the same narrative. A delete is REJECTED when the narrative has no such step, when a jump still targets it (retarget the referrers first), when a restated label/description does not match, or when it is a loop/try/parallel header whose body would be left standing (retype the header first to dissolve the region, then delete it). Changing a step's "type" REBUILDS it for the new type: its description and label are kept and every field the new type cannot carry is dropped (returned as a NOTICE); a delta that retypes AND sets such a field is refused. A step delta is also refused when it carries a marker the merge does not recognise: a non-boolean "remove", an "action" that is neither "insert" nor "delete", a "captureJumps" outside an insert, or no "stepNumber" to address. Inserting AT a jump target relocates those jumps past the inserted step by default (a NOTICE is returned) \u2014 add "captureJumps": true on the inserted step to retarget entry jumps onto it (loop/try endStep region tails always relocate with the body and are never captured). Every jump field has a "*Label" twin (toLabel, onFalseLabel, endLabel, \u2026, and "label" on a cases/catches entry) resolved against step labels AFTER the merge, so a delta may anchor on a label only pre-existing steps carry; a label the delta supplies REPLACES the stored number it twins, while setting the number and its label together in one delta is refused as a contradiction. Reference ids in deltas may use LOCAL names \u2014 they are qualified against the spec's namespace exactly as the loader would. Per-spec lint suppression: set "lint: { allow: [{ code, at, covers, reason }] }" to silence a WARNING or NOTICE code on this spec only (errors always surface; stale allows are flagged). An allow covers EXACTLY the occurrence it names: "at" is the site the finding names (a contract method, an import edge "from -> to", a declared edge "component -> target") and is REQUIRED for a code whose findings report one, while a finding that reports no site is covered only by an allow that names none; "covers" lists the units an aggregating finding reports, and the allow silences it only when every one is listed. This delta is deliberately OPEN below its top level \u2014 the shapes nest further than a schema here should restate \u2014 so a key that is not a field at its depth is not refused, it is NAMED BACK under NO EFFECT in the answer, together with any value the spec already held and any "unset" that removed nothing. Read that list: it is where a nested typo shows up.`),
|
|
30453
31097
|
dryRun: import_zod11.z.boolean().optional().describe("Ask what this delta WOULD do instead of doing it. The whole write runs, the candidate gate included, and the answer is the change report it would have produced \u2014 marked DRY RUN, with nothing stamped and not one byte of the stored file moved. Use it before a delta that renumbers a long narrative.")
|
|
30454
31098
|
},
|
|
30455
31099
|
outputSchema: specChangeReportOutput
|
|
@@ -30702,7 +31346,9 @@ function loadProjectConfig3() {
|
|
|
30702
31346
|
NarrativeStepTypeSchema,
|
|
30703
31347
|
OutputTargetSchema,
|
|
30704
31348
|
PATTERN_TYPES,
|
|
31349
|
+
PROJECT_ID_RE,
|
|
30705
31350
|
PROJECT_KINDS,
|
|
31351
|
+
PUBLIC_NAME_RE,
|
|
30706
31352
|
PackSelectionSchema,
|
|
30707
31353
|
ParallelBranchSchema,
|
|
30708
31354
|
PathsConfigSchema,
|
|
@@ -30737,6 +31383,7 @@ function loadProjectConfig3() {
|
|
|
30737
31383
|
SurfaceOriginSchema,
|
|
30738
31384
|
SurfaceSnapshotSchema,
|
|
30739
31385
|
SurfaceTypeDefSchema,
|
|
31386
|
+
SurfaceTypeExportSchema,
|
|
30740
31387
|
SwitchCaseSchema,
|
|
30741
31388
|
SystemPublicInterfaceSchema,
|
|
30742
31389
|
SystemSpecSchema,
|
|
@@ -30807,10 +31454,12 @@ function loadProjectConfig3() {
|
|
|
30807
31454
|
discoverPacks,
|
|
30808
31455
|
dryRunSerializeSpecs,
|
|
30809
31456
|
effectiveDetail,
|
|
31457
|
+
effectiveProjectId,
|
|
30810
31458
|
emptyExtensions,
|
|
30811
31459
|
ensureDir,
|
|
30812
31460
|
ensureProjectInitialized,
|
|
30813
31461
|
exportSpecTree,
|
|
31462
|
+
exportTargetKey,
|
|
30814
31463
|
externalizeSubsystem,
|
|
30815
31464
|
extractTypeIdentifiers,
|
|
30816
31465
|
factsFor,
|
|
@@ -30851,6 +31500,7 @@ function loadProjectConfig3() {
|
|
|
30851
31500
|
internalizeSubsystem,
|
|
30852
31501
|
isDraftSubsystem,
|
|
30853
31502
|
isLogic,
|
|
31503
|
+
isOwnComponentEntry,
|
|
30854
31504
|
isPattern,
|
|
30855
31505
|
isProjectInitialized,
|
|
30856
31506
|
isProvidedBy,
|
|
@@ -30904,6 +31554,7 @@ function loadProjectConfig3() {
|
|
|
30904
31554
|
pathKey,
|
|
30905
31555
|
pinInstalledPacksAsSelections,
|
|
30906
31556
|
projectConfigExists,
|
|
31557
|
+
projectIdentity,
|
|
30907
31558
|
provisionProject,
|
|
30908
31559
|
qualifiedTypeId,
|
|
30909
31560
|
readFileOrNull,
|
|
@@ -30929,7 +31580,9 @@ function loadProjectConfig3() {
|
|
|
30929
31580
|
resolveExpectedOutputPaths,
|
|
30930
31581
|
resolveImport,
|
|
30931
31582
|
resolveInstalledPack,
|
|
31583
|
+
resolveProjectExports,
|
|
30932
31584
|
resolveSubprojectForNamespace,
|
|
31585
|
+
resolveSubsystemExports,
|
|
30933
31586
|
resolveVariantGuidance,
|
|
30934
31587
|
retireSpecialists,
|
|
30935
31588
|
runWithProjectBinding,
|