@dtmd/temper 0.0.17 → 0.0.19
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/src/assembly.d.ts +40 -0
- package/dist/src/assembly.js +43 -0
- package/dist/src/builtins.d.ts +110 -4
- package/dist/src/builtins.js +64 -3
- package/dist/src/claude-code.d.ts +2 -2
- package/dist/src/claude-code.js +1 -1
- package/dist/src/contract.d.ts +96 -3
- package/dist/src/contract.js +89 -2
- package/dist/src/declarations.d.ts +3 -3
- package/dist/src/declarations.js +228 -24
- package/dist/src/emit.d.ts +28 -6
- package/dist/src/emit.js +102 -29
- package/dist/src/generated/ClauseRow.d.ts +29 -7
- package/dist/src/generated/Declarations.d.ts +8 -0
- package/dist/src/generated/InputRow.d.ts +21 -0
- package/dist/src/generated/InputRow.js +2 -0
- package/dist/src/generated/KindFactRow.d.ts +12 -0
- package/dist/src/generated/index.d.ts +1 -0
- package/dist/src/index.d.ts +8 -7
- package/dist/src/index.js +9 -5
- package/dist/src/kind.d.ts +314 -15
- package/dist/src/kind.js +184 -9
- package/dist/src/member-address.d.ts +102 -0
- package/dist/src/member-address.js +114 -0
- package/dist/src/prose.d.ts +29 -12
- package/dist/src/prose.js +50 -19
- package/package.json +3 -3
package/dist/src/declarations.js
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* in lockstep.
|
|
10
10
|
*/
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { isDeepStrictEqual } from "node:util";
|
|
12
13
|
import { isTextSpan, resolveLeaf } from "./prose.js";
|
|
13
14
|
import { SETTINGS_MANIFEST, TELEMETRY_EVENT_HOOKS, hook, tapHookRegistration } from "./builtins.js";
|
|
15
|
+
import { hostAddress, leafAddress, nestedAddress } from "./member-address.js";
|
|
14
16
|
/**
|
|
15
17
|
* Compile one `Clause` into its lock row: the shared `key`/`field`/`severity`/
|
|
16
18
|
* `guidance`/`cite` columns — the clause's four channels surviving erasure
|
|
@@ -60,20 +62,31 @@ function clauseRow(clause, kind) {
|
|
|
60
62
|
return {
|
|
61
63
|
kind,
|
|
62
64
|
predicate: predicate.key,
|
|
63
|
-
field: predicate
|
|
65
|
+
field: clauseField(predicate),
|
|
64
66
|
severity: clause.severity,
|
|
65
67
|
guidance: clause.guidance,
|
|
66
68
|
cite: clause.cite,
|
|
67
69
|
count: predicate.key === "count"
|
|
68
70
|
? { min: predicate.args?.min ?? 0, max: predicate.args?.max ?? Number.MAX_SAFE_INTEGER }
|
|
69
71
|
: undefined,
|
|
70
|
-
|
|
72
|
+
// The requirement-name column `membership`'s allowed-value target and
|
|
73
|
+
// `reached-from`'s closure roots share — one naming scheme for "the requirement
|
|
74
|
+
// whose satisfiers this clause reads" (`src/contract.rs` `predicate_from_row`).
|
|
75
|
+
target: predicate.key === "membership" || predicate.key === "reached-from"
|
|
76
|
+
? predicate.target
|
|
77
|
+
: undefined,
|
|
71
78
|
degree: predicate.key === "degree"
|
|
72
79
|
? {
|
|
73
80
|
incoming: edgeBoundArgs(predicate.args, "incoming"),
|
|
74
81
|
outgoing: edgeBoundArgs(predicate.args, "outgoing"),
|
|
75
82
|
}
|
|
76
83
|
: undefined,
|
|
84
|
+
// The by-incidence field set rides its own shared column, not the direction-only
|
|
85
|
+
// `degree` bound — the slot `reached-from`'s via set joins it. Copied into a
|
|
86
|
+
// fresh array: the predicate's set is read-only, the row's column is not.
|
|
87
|
+
fields: (predicate.key === "degree" || predicate.key === "reached-from") && predicate.fields
|
|
88
|
+
? [...predicate.fields]
|
|
89
|
+
: undefined,
|
|
77
90
|
gate: predicate.key === "mention-reachable" ? predicate.gate : undefined,
|
|
78
91
|
value_type: predicate.key === "type" && predicate.value_type ? [...predicate.value_type] : undefined,
|
|
79
92
|
shape: predicate.key === "shape" ? predicate.shape : undefined,
|
|
@@ -103,6 +116,57 @@ function clauseRow(clause, kind) {
|
|
|
103
116
|
: undefined,
|
|
104
117
|
};
|
|
105
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* The `field` column for one predicate: the field it names, or — for the four
|
|
121
|
+
* predicates that name a *section*, a field *set*, or a *requirement* rather than a
|
|
122
|
+
* field — an identity synthesized from the arguments the row already carries.
|
|
123
|
+
*
|
|
124
|
+
* `section_contains`, `require_sections`, `degree` and `reached-from` set no `field`,
|
|
125
|
+
* and the column is what emit stamps a clause's label from (`stamp_clause_label`,
|
|
126
|
+
* `src/drift.rs`), so reading `Predicate.field` folds every clause of one of those
|
|
127
|
+
* predicates on one kind into one label — two rows wearing one label, which
|
|
128
|
+
* admissibility refuses as a malformed lock. Synthesizing here keeps the whole fix at
|
|
129
|
+
* the lowering: the Rust reader reconstructs all four predicates from the
|
|
130
|
+
* `section`/`sections`/`fields`/`target` columns and never from this one, so nothing
|
|
131
|
+
* round-trips through the synthesized text.
|
|
132
|
+
*/
|
|
133
|
+
function clauseField(predicate) {
|
|
134
|
+
if (predicate.key === "section_contains") {
|
|
135
|
+
const { section } = predicate;
|
|
136
|
+
return section === undefined ? undefined : `${section.heading}.${section.marker}`;
|
|
137
|
+
}
|
|
138
|
+
if (predicate.key === "require_sections") {
|
|
139
|
+
// Joined with `+` rather than the label's own `.`, so the segment reads as the
|
|
140
|
+
// set it is and two different heading lists cannot fold to one label.
|
|
141
|
+
return predicate.sections?.join("+");
|
|
142
|
+
}
|
|
143
|
+
if (predicate.key === "degree") {
|
|
144
|
+
// A `degree` clause names no field of its own: its by-incidence filter is what
|
|
145
|
+
// distinguishes two bounds on one kind, so the filter is the segment — sorted and
|
|
146
|
+
// `+`-joined by the rule directly above, so two field lists spelling one set land
|
|
147
|
+
// one address and two different sets never fold to one. An unfiltered bound adds
|
|
148
|
+
// no segment: it ranges over every edge at the member, so two of them on one kind
|
|
149
|
+
// are a redundancy the author collapses, never a distinction an address must hold.
|
|
150
|
+
return predicate.fields === undefined ? undefined : [...predicate.fields].sort().join("+");
|
|
151
|
+
}
|
|
152
|
+
if (predicate.key === "reached-from") {
|
|
153
|
+
// A `reached-from` clause names no field either: its identity is the pair it walks.
|
|
154
|
+
// The roots requirement leads, and a declared via set follows after a `.` — the
|
|
155
|
+
// two-argument `section_contains` precedent — sorted and `+`-joined by `degree`'s
|
|
156
|
+
// rule directly above. So two closures rooted at one requirement over different
|
|
157
|
+
// arcs land two addresses instead of folding into one, and two spellings of one via
|
|
158
|
+
// set land one. An unfiltered closure adds no second segment: the roots are its
|
|
159
|
+
// whole identity, and two of them on one kind are a redundancy the author
|
|
160
|
+
// collapses.
|
|
161
|
+
const { target } = predicate;
|
|
162
|
+
if (target === undefined)
|
|
163
|
+
return undefined;
|
|
164
|
+
return predicate.fields === undefined
|
|
165
|
+
? target
|
|
166
|
+
: `${target}.${[...predicate.fields].sort().join("+")}`;
|
|
167
|
+
}
|
|
168
|
+
return predicate.field;
|
|
169
|
+
}
|
|
106
170
|
/** `min_len`/`max_len`/`extent`'s scalar bound off their shared `min`/`max`
|
|
107
171
|
* args keys — `undefined` for every other predicate, and for these three when
|
|
108
172
|
* neither endpoint is present. */
|
|
@@ -240,6 +304,16 @@ function collectionAddressRow(facts) {
|
|
|
240
304
|
entry_shape: facts.collectionAddress.entryShape,
|
|
241
305
|
};
|
|
242
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Lower a kind's exhaustive leaf-set witness into the row's `leaves` column: the
|
|
309
|
+
* witness's own keys, in the order its author declared them (`kind.ts`'s `LeafSet`,
|
|
310
|
+
* decision 0053 — the type is the declaration, and TypeScript erases it, so the record is
|
|
311
|
+
* how the corpus hands the key set over). `undefined` for a kind declaring no witness, so
|
|
312
|
+
* its row omits the column.
|
|
313
|
+
*/
|
|
314
|
+
function leafSetRow(leaves) {
|
|
315
|
+
return leaves === undefined ? undefined : Object.keys(leaves);
|
|
316
|
+
}
|
|
243
317
|
/**
|
|
244
318
|
* One kind's fact row — an `at` locus supplies `governs_root`/`governs_glob` and any
|
|
245
319
|
* other locus neither (a nested-file kind's path composes from its host's unit and the
|
|
@@ -247,7 +321,8 @@ function collectionAddressRow(facts) {
|
|
|
247
321
|
* A file locus's `commitment` class rides the same spelling, absent for the committed
|
|
248
322
|
* default. `templates` names the embedded kinds the corpus admits over it, and `content`
|
|
249
323
|
* lowers a declared layout (absent for a `file`-content kind). A registration kind
|
|
250
|
-
* extends the row with its `shape` marker and `collection_address`.
|
|
324
|
+
* extends the row with its `shape` marker and `collection_address`. A declared leaf-set
|
|
325
|
+
* witness lowers to `leaves` ([`leafSetRow`]), absent for a kind declaring none. Advisory
|
|
251
326
|
* `guidance`/`cite` pair rides alongside, locus-optional so an embedded kind's own
|
|
252
327
|
* counsel reaches the lock the same way a nested-file kind's already does (decision
|
|
253
328
|
* 0045) — callable for any locus; [`kindFactKindsInPlay`] decides which embedded kinds
|
|
@@ -267,11 +342,95 @@ function kindFactRow(facts, admissions) {
|
|
|
267
342
|
templates: templatesFor(facts, admissions),
|
|
268
343
|
content: contentRow(facts.content),
|
|
269
344
|
shape: facts.shape,
|
|
345
|
+
leaves: leafSetRow(facts.leaves),
|
|
270
346
|
collection_address: collectionAddressRow(facts),
|
|
271
347
|
guidance: facts.guidance,
|
|
272
348
|
cite: facts.cite,
|
|
273
349
|
};
|
|
274
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* The locus a colliding kind is declared at, for {@link resolveNameCollision}'s refusal:
|
|
353
|
+
* an `at` kind names the path its members are found at, and every other locus names
|
|
354
|
+
* itself — a kind that governs no glob has no path of its own to name.
|
|
355
|
+
*/
|
|
356
|
+
function locusLabel(facts) {
|
|
357
|
+
const { locus } = facts;
|
|
358
|
+
if (locus.kind !== "at")
|
|
359
|
+
return `the \`${locus.kind}\` locus`;
|
|
360
|
+
return `\`${locus.root === "." ? locus.glob : `${locus.root}/${locus.glob}`}\``;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* A kind's facts with the three faces a relocation may diverge from its base on erased:
|
|
364
|
+
* the `relocates` marker, the appended `edgeFields`, and an `at` locus's moved root and
|
|
365
|
+
* glob (`kind.ts`'s `KindRelocation`). Two values agreeing here are one kind up to a
|
|
366
|
+
* relocation delta; the delta's own faces are checked on their own terms.
|
|
367
|
+
*/
|
|
368
|
+
function relocationInvariants(facts) {
|
|
369
|
+
const bare = { ...facts };
|
|
370
|
+
delete bare.relocates;
|
|
371
|
+
delete bare.edgeFields;
|
|
372
|
+
if (facts.locus.kind === "at")
|
|
373
|
+
bare.locus = { ...facts.locus, root: "", glob: "" };
|
|
374
|
+
return bare;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Whether `relocation` was derived from `base` — the provenance the relocation-wins rule
|
|
378
|
+
* rests on. The marker names the base, `relocate` only ever *appends* edge fields (so the
|
|
379
|
+
* base's are a prefix of the relocation's), and every other fact rides through unchanged:
|
|
380
|
+
* the base's facts are a subset by construction, which is exactly what makes keeping the
|
|
381
|
+
* relocation lossless. Checked rather than assumed, because the marker carries a name and
|
|
382
|
+
* a name is what is in dispute — a value bearing it whose facts are not the other's
|
|
383
|
+
* superset is a third kind of the same name, and dropping *that* would be the very silent
|
|
384
|
+
* loss this decision exists to end.
|
|
385
|
+
*/
|
|
386
|
+
function isRelocationOf(relocation, base) {
|
|
387
|
+
if (relocation.relocates !== base.name || base.relocates !== undefined)
|
|
388
|
+
return false;
|
|
389
|
+
const inherited = base.edgeFields ?? [];
|
|
390
|
+
const declared = relocation.edgeFields ?? [];
|
|
391
|
+
return (declared.length >= inherited.length &&
|
|
392
|
+
isDeepStrictEqual(declared.slice(0, inherited.length), inherited) &&
|
|
393
|
+
isDeepStrictEqual(relocationInvariants(relocation), relocationInvariants(base)));
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Which of two `KindFacts` values sharing one name is the kind in play — a decision that
|
|
397
|
+
* must not depend on the order the two arrived in, since {@link kindsInPlay} draws its
|
|
398
|
+
* facts from `members`, then `expect`, then `admit`, then embedded templates, and a kind
|
|
399
|
+
* legitimately reaches it through any of them.
|
|
400
|
+
*
|
|
401
|
+
* A **relocation** wins over its base whichever way round they arrive
|
|
402
|
+
* ({@link isRelocationOf} proves the provenance): the base's facts are the relocation's
|
|
403
|
+
* minus the delta, so keeping the base would silently drop the relocation's added edge
|
|
404
|
+
* fields and moved locus. Two values that are structurally equal are one kind declared
|
|
405
|
+
* twice — keep the first. Anything else is a genuine collision and refuses: kind identity
|
|
406
|
+
* travels by import, never by string (`representation.md`, "kind"), while the lock is a
|
|
407
|
+
* string-keyed medium, so the second kind has no name to reach the engine
|
|
408
|
+
* under and dropping it is a silent loss of everything it declared. Two relocations of
|
|
409
|
+
* one base collide the same way — each is still a kind of the base's own name.
|
|
410
|
+
*
|
|
411
|
+
* # Throws
|
|
412
|
+
* On a collision, naming the kind and both loci — the two declarations an author has to
|
|
413
|
+
* go look at.
|
|
414
|
+
*/
|
|
415
|
+
function resolveNameCollision(held, arriving) {
|
|
416
|
+
// At most one holds: each direction demands the other value carry no marker at all.
|
|
417
|
+
if (isRelocationOf(held, arriving))
|
|
418
|
+
return held;
|
|
419
|
+
if (isRelocationOf(arriving, held))
|
|
420
|
+
return arriving;
|
|
421
|
+
if (isDeepStrictEqual(held, arriving))
|
|
422
|
+
return held;
|
|
423
|
+
throw new Error(held.relocates !== undefined && arriving.relocates !== undefined
|
|
424
|
+
? `two relocations of kind \`${held.name}\` are in play, at ${locusLabel(held)} and ` +
|
|
425
|
+
`${locusLabel(arriving)}, and they diverge. A relocation is still a kind of its base's ` +
|
|
426
|
+
`name, so a second one collides with the first: relocate the base once and import that ` +
|
|
427
|
+
`one value wherever it is used (specs/model/representation.md, "kind").`
|
|
428
|
+
: `two kinds named \`${held.name}\` are in play, at ${locusLabel(held)} and ` +
|
|
429
|
+
`${locusLabel(arriving)}. Kind identity travels by import, never by string, and the lock ` +
|
|
430
|
+
`is keyed by name — the second kind has no name of its own to reach the engine under. ` +
|
|
431
|
+
`Import the declared kind rather than redeclaring it; to move a built-in to another root ` +
|
|
432
|
+
`or add an edge field to it, use \`relocate()\` (specs/model/representation.md, "kind").`);
|
|
433
|
+
}
|
|
275
434
|
/**
|
|
276
435
|
* Every kind in play, at any locus — member kinds ∪ expect kinds ∪ their embedded
|
|
277
436
|
* children — name-sorted, so every family derived from it inherits one stable order.
|
|
@@ -285,15 +444,24 @@ function kindFactRow(facts, admissions) {
|
|
|
285
444
|
* Only *embedded* children are drawn in. A path-carrying template is the nested-file
|
|
286
445
|
* layer, whose child owns a unit and reaches the lock through `expect` like any other
|
|
287
446
|
* unit kind; pulling one in here would forge it a kind-fact row it never declared.
|
|
447
|
+
*
|
|
448
|
+
* Two facts values arriving under one name are decided by {@link resolveNameCollision},
|
|
449
|
+
* never by arrival order: the deduped facts are what `assemblyFactRows` derives every
|
|
450
|
+
* `edge` row from, so first-wins would drop a relocation's added edge on the floor
|
|
451
|
+
* whenever the base happened to be named first.
|
|
288
452
|
*/
|
|
289
453
|
function kindsInPlay(harness) {
|
|
290
454
|
const byName = new Map();
|
|
291
455
|
const pending = [];
|
|
292
456
|
const admit = (facts) => {
|
|
293
|
-
|
|
457
|
+
const held = byName.get(facts.name);
|
|
458
|
+
if (held === facts)
|
|
459
|
+
return;
|
|
460
|
+
const winner = held === undefined ? facts : resolveNameCollision(held, facts);
|
|
461
|
+
if (winner === held)
|
|
294
462
|
return;
|
|
295
|
-
byName.set(facts.name,
|
|
296
|
-
pending.push(
|
|
463
|
+
byName.set(facts.name, winner);
|
|
464
|
+
pending.push(winner);
|
|
297
465
|
};
|
|
298
466
|
for (const member of harness.members)
|
|
299
467
|
admit(member.facts);
|
|
@@ -317,13 +485,20 @@ function atLocusKindsInPlay(allKinds) {
|
|
|
317
485
|
/**
|
|
318
486
|
* The distinct kinds in play that take a kind-fact row: every non-embedded locus
|
|
319
487
|
* unconditionally (a nested-file kind owns a file the engine must place, and places it
|
|
320
|
-
* off its row, though it governs no glob to be discovered at), plus an embedded kind
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
488
|
+
* off its row, though it governs no glob to be discovered at), plus an embedded kind that
|
|
489
|
+
* has something of its own for a row to carry: `guidance` or `cite` (decision 0045), or a
|
|
490
|
+
* declared leaf set (decision 0053). The leaf set is on this list for the reason the
|
|
491
|
+
* column exists — it teaches what a child carries *before* the surface holds a member of
|
|
492
|
+
* it, and an embedded kind taking no row never reaches the lock to teach it. An embedded
|
|
493
|
+
* kind declaring none of the three has nothing for the row to carry, and its members
|
|
494
|
+
* already reach the corpus through their host's `templates` column alone (`kindsInPlay`),
|
|
495
|
+
* never the row.
|
|
324
496
|
*/
|
|
325
497
|
function kindFactKindsInPlay(allKinds) {
|
|
326
|
-
return allKinds.filter((facts) => facts.locus.kind !== "embedded" ||
|
|
498
|
+
return allKinds.filter((facts) => facts.locus.kind !== "embedded" ||
|
|
499
|
+
facts.guidance !== undefined ||
|
|
500
|
+
facts.cite !== undefined ||
|
|
501
|
+
facts.leaves !== undefined);
|
|
327
502
|
}
|
|
328
503
|
/** The requirement rows — assembly `require` and every member's `requires`, one namespace. */
|
|
329
504
|
function requirementRows(harness) {
|
|
@@ -388,7 +563,7 @@ function assemblyFactRows(harness, kinds) {
|
|
|
388
563
|
function satisfiesRows(harness) {
|
|
389
564
|
const rows = [];
|
|
390
565
|
for (const member of harness.members) {
|
|
391
|
-
const address =
|
|
566
|
+
const address = hostAddress(member.kind, member.name);
|
|
392
567
|
for (const requirement of member.satisfies) {
|
|
393
568
|
rows.push({ member: address, requirement });
|
|
394
569
|
}
|
|
@@ -409,7 +584,7 @@ function satisfiesRows(harness) {
|
|
|
409
584
|
function mentionRows(harness) {
|
|
410
585
|
const rows = [];
|
|
411
586
|
for (const member of harness.members) {
|
|
412
|
-
const address =
|
|
587
|
+
const address = hostAddress(member.kind, member.name);
|
|
413
588
|
if (member.prose?.kind === "text") {
|
|
414
589
|
for (const mention of member.prose.mentions) {
|
|
415
590
|
rows.push({ member: address, target: mention.target.address });
|
|
@@ -435,13 +610,11 @@ function mentionRows(harness) {
|
|
|
435
610
|
* contribute — top-level leaves addressed by their bare field name, a
|
|
436
611
|
* collection entry's leaves addressed `<collection>.<entry>.<field>` (one layer
|
|
437
612
|
* deep, matching the row's own shape) — each row keyed to the leaf's own
|
|
438
|
-
* structural address
|
|
439
|
-
* `src/read.rs`'s `parse_leaf_address` resolves. A bare-string leaf names no
|
|
440
|
-
* mention.
|
|
613
|
+
* structural address ({@link leafAddress}). A bare-string leaf names no mention.
|
|
441
614
|
*/
|
|
442
615
|
function embeddedLeafMentionRows(hostName, value) {
|
|
443
616
|
const rows = [];
|
|
444
|
-
const addressed = (childPath) =>
|
|
617
|
+
const addressed = (childPath) => leafAddress(hostName, value.kind, value.key, childPath);
|
|
445
618
|
for (const [field, leaf] of Object.entries(value.leaves)) {
|
|
446
619
|
if (typeof leaf === "string")
|
|
447
620
|
continue;
|
|
@@ -478,7 +651,7 @@ function includeRows(harness) {
|
|
|
478
651
|
rows.push({ member: address, source_path: fileURLToPath(new URL(include.path, include.moduleUrl)) });
|
|
479
652
|
};
|
|
480
653
|
for (const member of harness.members) {
|
|
481
|
-
const address =
|
|
654
|
+
const address = hostAddress(member.kind, member.name);
|
|
482
655
|
if (member.prose?.kind === "text") {
|
|
483
656
|
for (const include of member.prose.includes)
|
|
484
657
|
push(address, include);
|
|
@@ -494,6 +667,27 @@ function includeRows(harness) {
|
|
|
494
667
|
}
|
|
495
668
|
return rows;
|
|
496
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* The `input` rows — every member's declared inputs, in member-then-authored order.
|
|
672
|
+
* Each carries the declaring member's `kind:name` address and the input's path resolved
|
|
673
|
+
* against the stating module ({@link fileURLToPath} over the input's own `moduleUrl`),
|
|
674
|
+
* never the workspace — exactly as an include's is; the engine reads and fingerprints
|
|
675
|
+
* it, and splices nothing.
|
|
676
|
+
*
|
|
677
|
+
* Unlike an include, an input pairs with no body slot, so nothing downstream depends on
|
|
678
|
+
* this order — the authored one is kept anyway, so a re-emit is byte-stable and the
|
|
679
|
+
* author reads their own declarations back.
|
|
680
|
+
*/
|
|
681
|
+
function inputRows(harness) {
|
|
682
|
+
const rows = [];
|
|
683
|
+
for (const member of harness.members) {
|
|
684
|
+
const address = hostAddress(member.kind, member.name);
|
|
685
|
+
for (const declared of member.inputs) {
|
|
686
|
+
rows.push({ member: address, source_path: fileURLToPath(new URL(declared.path, declared.moduleUrl)) });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return rows;
|
|
690
|
+
}
|
|
497
691
|
/**
|
|
498
692
|
* One composed embedded value's key in an {@link EdgePlacements} table — its host's
|
|
499
693
|
* `kind:name` address plus the value's own kind and key, the same triple the
|
|
@@ -591,7 +785,7 @@ function nestedMemberRows(harness, admissions, scope, placements, extents) {
|
|
|
591
785
|
for (const member of harness.members) {
|
|
592
786
|
if (member.prose?.kind !== "blocks")
|
|
593
787
|
continue;
|
|
594
|
-
const host =
|
|
788
|
+
const host = hostAddress(member.kind, member.name);
|
|
595
789
|
for (const value of member.prose.values) {
|
|
596
790
|
if (isTextSpan(value))
|
|
597
791
|
continue;
|
|
@@ -729,22 +923,22 @@ export function declaredRequirements(harness) {
|
|
|
729
923
|
export function declaredAddresses(harness) {
|
|
730
924
|
const set = declaredRequirements(harness);
|
|
731
925
|
for (const member of harness.members) {
|
|
732
|
-
set.add(
|
|
926
|
+
set.add(hostAddress(member.kind, member.name));
|
|
733
927
|
if (member.prose?.kind !== "blocks")
|
|
734
928
|
continue;
|
|
735
929
|
for (const value of member.prose.values) {
|
|
736
930
|
if (isTextSpan(value))
|
|
737
931
|
continue;
|
|
738
|
-
set.add(
|
|
932
|
+
set.add(nestedAddress(hostAddress(member.kind, member.name), value.kind, value.key));
|
|
739
933
|
}
|
|
740
934
|
}
|
|
741
935
|
return set;
|
|
742
936
|
}
|
|
743
937
|
/**
|
|
744
938
|
* Every discoverable (`at`-locus) kind the program declares — the deferral signal a
|
|
745
|
-
* dangling mention is measured against (`prose.ts`'s `defersToGate`): a mention
|
|
746
|
-
* one of these
|
|
747
|
-
* naming no declared kind refuses at emit. Member kinds ∪ `expect` kinds; an embedded
|
|
939
|
+
* dangling mention is measured against (`prose.ts`'s `defersToGate`): a mention whose
|
|
940
|
+
* address is a host address of one of these and names no composed value defers to
|
|
941
|
+
* `check`, while a mention naming no declared kind refuses at emit. Member kinds ∪ `expect` kinds; an embedded
|
|
748
942
|
* kind is excluded — its members are composed within a host, never discovered, so a
|
|
749
943
|
* flat `kind:name` mention of one has no discovery locus to defer to.
|
|
750
944
|
*/
|
|
@@ -774,6 +968,15 @@ export function compileDeclarations(harness, placements, extents) {
|
|
|
774
968
|
clauses.push(clauseRow(clause, binding.kind.key));
|
|
775
969
|
}
|
|
776
970
|
}
|
|
971
|
+
// The root member's own clauses — the third source of a clause row, beside `expect`'s
|
|
972
|
+
// kind-keyed rows and a requirement's nested ones. They lower with **no** `kind`
|
|
973
|
+
// column: that absence at the top level is the discriminator
|
|
974
|
+
// `compose::root_contract_from_rows` reads, and `drift::stamp_clause_labels` addresses
|
|
975
|
+
// them under the `root` owner segment. Declaration order within the array, appended
|
|
976
|
+
// past the kind-sorted `expect` rows — a fixed position, so double emit is byte-stable.
|
|
977
|
+
for (const clause of harness.contract) {
|
|
978
|
+
clauses.push(clauseRow(clause, undefined));
|
|
979
|
+
}
|
|
777
980
|
return {
|
|
778
981
|
kinds: kindFactKindsInPlay(allKinds).map((facts) => kindFactRow(facts, admissions)),
|
|
779
982
|
clauses,
|
|
@@ -782,6 +985,7 @@ export function compileDeclarations(harness, placements, extents) {
|
|
|
782
985
|
satisfies: satisfiesRows(harness),
|
|
783
986
|
mentions: mentionRows(harness),
|
|
784
987
|
includes: includeRows(harness),
|
|
988
|
+
inputs: inputRows(harness),
|
|
785
989
|
nested_members: nestedMemberRows(harness, admissions, mentionScope(harness), placements, extents),
|
|
786
990
|
registrations: [...registrationRows(harness), ...tapHookRows(harness)],
|
|
787
991
|
settings: settingsRows(harness),
|
package/dist/src/emit.d.ts
CHANGED
|
@@ -9,10 +9,30 @@
|
|
|
9
9
|
* run.
|
|
10
10
|
*/
|
|
11
11
|
import type { Harness } from "./assembly.js";
|
|
12
|
-
import type { Member } from "./kind.js";
|
|
12
|
+
import type { EmbeddedMemberValue, Member } from "./kind.js";
|
|
13
13
|
import type { Declarations } from "./declarations.js";
|
|
14
14
|
import type { PayloadMember } from "./generated/index.js";
|
|
15
15
|
export type { PayloadMember } from "./generated/index.js";
|
|
16
|
+
/**
|
|
17
|
+
* One composed embedded value as an edge target, carried with the host member whose body
|
|
18
|
+
* it lives in: an embedded member owns no file, so every path fact about it is derived
|
|
19
|
+
* from its host's own projection.
|
|
20
|
+
*/
|
|
21
|
+
export interface EmbeddedTarget {
|
|
22
|
+
/** The member whose composed body carries the value — the projection it lives in. */
|
|
23
|
+
readonly host: Member;
|
|
24
|
+
/** The composed value itself. */
|
|
25
|
+
readonly value: EmbeddedMemberValue;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What one address in the member table names: a top-level composed member, or the
|
|
29
|
+
* embedded values one nested spelling reaches. The nested list carries one element for
|
|
30
|
+
* a full `<host-address>/<kind>/<key>` address, and one per host for a bare `kind:key` —
|
|
31
|
+
* a bare key names a nested member only when a single host carries it, and resolution
|
|
32
|
+
* refuses the rest as ambiguous rather than picking one: uniqueness is the resolver's
|
|
33
|
+
* bar, not the grammar's.
|
|
34
|
+
*/
|
|
35
|
+
export type EdgeTarget = Member | readonly EmbeddedTarget[];
|
|
16
36
|
/** What a mention may resolve against at emit. */
|
|
17
37
|
export interface ResolveOptions {
|
|
18
38
|
/** The addresses a mention may name — resolution-checked; a mention cannot dangle. */
|
|
@@ -23,12 +43,14 @@ export interface ResolveOptions {
|
|
|
23
43
|
*/
|
|
24
44
|
readonly deferrableKinds?: ReadonlySet<string>;
|
|
25
45
|
/**
|
|
26
|
-
* The program's composed members by
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
46
|
+
* The program's composed members by address — what an embedded value's edge field
|
|
47
|
+
* resolves against to derive its target facts. Top-level members index at their
|
|
48
|
+
* `kind:name` address, and each composed embedded value at both of its own spellings:
|
|
49
|
+
* its full `<host-address>/<kind>/<key>` address and its bare `kind:key`.
|
|
50
|
+
* An edge target never defers to the gate the way a bare mention may: the facts are
|
|
51
|
+
* rendered into the projection now, so an unresolved one has nothing true to place.
|
|
30
52
|
*/
|
|
31
|
-
readonly members?: ReadonlyMap<string,
|
|
53
|
+
readonly members?: ReadonlyMap<string, EdgeTarget>;
|
|
32
54
|
}
|
|
33
55
|
/**
|
|
34
56
|
* One fields-only registration member erased for the manifest write face: its key
|