@dtmd/temper 0.0.6 → 0.0.8

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.
Files changed (50) hide show
  1. package/README.md +84 -53
  2. package/bin/temper.js +52 -0
  3. package/dist/src/assembly.d.ts +15 -1
  4. package/dist/src/assembly.js +2 -1
  5. package/dist/src/builtins.d.ts +765 -48
  6. package/dist/src/builtins.js +767 -68
  7. package/dist/src/claude-code.d.ts +2 -2
  8. package/dist/src/claude-code.js +1 -1
  9. package/dist/src/contract.d.ts +180 -29
  10. package/dist/src/contract.js +128 -15
  11. package/dist/src/declarations.d.ts +92 -6
  12. package/dist/src/declarations.js +448 -102
  13. package/dist/src/dial.d.ts +75 -0
  14. package/dist/src/dial.js +82 -0
  15. package/dist/src/emit.d.ts +82 -1
  16. package/dist/src/emit.js +402 -56
  17. package/dist/src/generated/AssemblyFactRow.d.ts +2 -2
  18. package/dist/src/generated/BoundRow.d.ts +2 -2
  19. package/dist/src/generated/ClauseRow.d.ts +73 -3
  20. package/dist/src/generated/CollectionAddressRow.d.ts +21 -0
  21. package/dist/src/generated/CollectionAddressRow.js +2 -0
  22. package/dist/src/generated/Declarations.d.ts +17 -0
  23. package/dist/src/generated/EmbeddedMember.d.ts +3 -3
  24. package/dist/src/generated/FeatureValue.d.ts +2 -2
  25. package/dist/src/generated/Features.d.ts +52 -3
  26. package/dist/src/generated/KindFactRow.d.ts +38 -7
  27. package/dist/src/generated/MentionRow.d.ts +5 -3
  28. package/dist/src/generated/NestedMemberRow.d.ts +32 -0
  29. package/dist/src/generated/PayloadMember.d.ts +6 -0
  30. package/dist/src/generated/RegistrationRow.d.ts +35 -0
  31. package/dist/src/generated/RegistrationRow.js +2 -0
  32. package/dist/src/generated/RequirementRow.d.ts +4 -2
  33. package/dist/src/generated/SatisfiesRow.d.ts +2 -1
  34. package/dist/src/generated/SettingsRow.d.ts +25 -0
  35. package/dist/src/generated/SettingsRow.js +2 -0
  36. package/dist/src/generated/Shape.d.ts +15 -0
  37. package/dist/src/generated/Shape.js +2 -0
  38. package/dist/src/generated/TemplateRow.d.ts +24 -0
  39. package/dist/src/generated/TemplateRow.js +2 -0
  40. package/dist/src/generated/ValueType.d.ts +11 -2
  41. package/dist/src/generated/Verifier.d.ts +20 -0
  42. package/dist/src/generated/Verifier.js +2 -0
  43. package/dist/src/generated/index.d.ts +6 -0
  44. package/dist/src/index.d.ts +8 -8
  45. package/dist/src/index.js +3 -3
  46. package/dist/src/kind.d.ts +160 -29
  47. package/dist/src/kind.js +47 -16
  48. package/dist/src/prose.d.ts +81 -25
  49. package/dist/src/prose.js +91 -21
  50. package/package.json +10 -2
@@ -13,16 +13,16 @@
13
13
  * `memory` kinds — lives at the `./claude-code` subpath, never here.
14
14
  */
15
15
  export type { Blocks, File, Include, Mention, Mentionable, Prose, Reference, Text } from "./prose.js";
16
- export { blocks, file, include, renderText, text } from "./prose.js";
16
+ export { blocks, file, include, mentionOf, renderText, text } from "./prose.js";
17
17
  export type { Capability } from "./needs.js";
18
18
  export { bash, capability, permissionUnion } from "./needs.js";
19
- export type { Charset, Clause, Predicate, Requirement, Severity } from "./contract.js";
20
- export { allowedChars, clause, count, degree, deny, enumOf, forbiddenKeys, maxLen, maxLines, membership, minLen, mustDefine, nameMatchesDir, optional, range, required, requireSections, requirement, sectionContains, type, unique, uniqueName, } from "./contract.js";
21
- export type { EdgeField, EmbeddedMemberCollectionEntry, EmbeddedMemberValue, Format, KindDefinition, KindFacts, Layout, LayoutRegion, Locus, Member, MemberInit, Registration, UnitShape, } from "./kind.js";
19
+ export type { Charset, Clause, ExtentUnit, Predicate, Requirement, Severity, Verifier } from "./contract.js";
20
+ export { allowedChars, clause, closedKeys, count, degree, deny, enumOf, extent, forbiddenKeys, formatPlacesEdges, globValid, maxLen, membership, mentionReachable, minLen, mustDefine, nameMatchesDir, optional, range, required, requireSections, requirement, script, sectionContains, shape, telemetry, type, unique, uniqueName, when, } from "./contract.js";
21
+ export type { CollectionAddress, EdgeField, EdgeTargetFacts, EmbeddedMemberCollectionEntry, EmbeddedMemberValue, Format, KindDefinition, KindFacts, KindOptions, Layout, LayoutRegion, Locus, Member, MemberInit, Registration, ResolvedEmbeddedMemberCollectionEntry, ResolvedEmbeddedMemberValue, Shape, Template, UnitShape, } from "./kind.js";
22
22
  export { embeddedMemberValue, kind } from "./kind.js";
23
- export type { EnforcementMode, ExpectBinding, Harness } from "./assembly.js";
23
+ export type { Admission, EnforcementMode, ExpectBinding, Harness } from "./assembly.js";
24
24
  export { harness } from "./assembly.js";
25
- export type { AssemblyFactRow, ClauseRow, Declarations, KindFactRow, RequirementRow, SatisfiesRow, } from "./declarations.js";
26
- export { SEAM_VERSION, compileDeclarations } from "./declarations.js";
27
- export type { EmitResult, PayloadMember, ResolveOptions } from "./emit.js";
25
+ export type { EmitResult, RegistrationFact, ResolveOptions, SettingsResidue } from "./emit.js";
28
26
  export { emit } from "./emit.js";
27
+ export type { Dial, DialEntry } from "./dial.js";
28
+ export { dial, dialDefaultContract } from "./dial.js";
package/dist/src/index.js CHANGED
@@ -12,10 +12,10 @@
12
12
  * The first-party Claude Code provider face — the built-in `skill`/`rule`/
13
13
  * `memory` kinds — lives at the `./claude-code` subpath, never here.
14
14
  */
15
- export { blocks, file, include, renderText, text } from "./prose.js";
15
+ export { blocks, file, include, mentionOf, renderText, text } from "./prose.js";
16
16
  export { bash, capability, permissionUnion } from "./needs.js";
17
- export { allowedChars, clause, count, degree, deny, enumOf, forbiddenKeys, maxLen, maxLines, membership, minLen, mustDefine, nameMatchesDir, optional, range, required, requireSections, requirement, sectionContains, type, unique, uniqueName, } from "./contract.js";
17
+ export { allowedChars, clause, closedKeys, count, degree, deny, enumOf, extent, forbiddenKeys, formatPlacesEdges, globValid, maxLen, membership, mentionReachable, minLen, mustDefine, nameMatchesDir, optional, range, required, requireSections, requirement, script, sectionContains, shape, telemetry, type, unique, uniqueName, when, } from "./contract.js";
18
18
  export { embeddedMemberValue, kind } from "./kind.js";
19
19
  export { harness } from "./assembly.js";
20
- export { SEAM_VERSION, compileDeclarations } from "./declarations.js";
21
20
  export { emit } from "./emit.js";
21
+ export { dial, dialDefaultContract } from "./dial.js";
@@ -1,23 +1,35 @@
1
1
  /**
2
2
  * Kinds — the engine room. A kind is a plain typed surface — an interface
3
- * `T` and a constructor `kind<T>()` — plus six facts of runtime residue: label,
4
- * locus, layout, registration, edge fields, and content. `tsc` is the keystroke wall;
5
- * every type erases at the seam, and what a kind leaves behind is those six facts,
6
- * riding the lock as rows. Identity travels by import, never by string — a `kind`
7
- * reference is the imported value.
3
+ * `T` and a constructor `kind<T>()` — plus seven facts of runtime residue: label,
4
+ * locus, projection, registration, edge fields, content, and template. A registration kind (a hook,
5
+ * an MCP server) extends the content fact with a fields-only `shape` and a
6
+ * `collectionAddress` naming the host manifest it surfaces in. `tsc` is the keystroke
7
+ * wall; every type erases at the seam, and what a kind leaves behind rides the lock as
8
+ * rows. Identity travels by import, never by string — a `kind` reference is the imported
9
+ * value.
8
10
  */
9
11
  import type { Prose, Text } from "./prose.js";
10
12
  import type { Capability } from "./needs.js";
11
13
  import type { Requirement } from "./contract.js";
12
- /** The shape of the on-disk artifact a member projects to (fact 3, layout). */
13
- export type Format = "yaml-frontmatter";
14
+ /**
15
+ * The shape of the on-disk artifact a member projects to (fact 3, projection) — a closed
16
+ * vocabulary the engine implements once per entry, and the fact deciding which adapter
17
+ * reads a file kind's artifact. `"json-document"` is a whole artifact that is one JSON
18
+ * object: its top-level keys the member's fields, its identity a declared key among them.
19
+ * `"toml-document"` is that read over TOML, and a **read face only** — nothing projects a
20
+ * member declaring it, so it serves a document temper reads in place and never writes.
21
+ */
22
+ export type Format = "yaml-frontmatter" | "json-document" | "toml-document";
14
23
  /**
15
24
  * Whether a member is a lone file (identity from the stem), a directory with an
16
- * entry file (identity from the directory name), or a lone file whose identity is
17
- * read from a declared frontmatter field (`identityField`) instead of derived from
18
- * the path (an agent's `name`).
25
+ * entry file (identity from the directory name), a lone file whose identity is
26
+ * read from a declared field (`identityField`) instead of derived from the path (an
27
+ * agent's `name`) — on whichever surface the kind's `format` carries its fields — or a
28
+ * lone file keyed by the directory segment its single-`*` segment glob stars
29
+ * (`"starred-segment"`), coexisting inside another kind's directory rather than owning
30
+ * one. The identity source is this spelled fact, never inferred from the glob.
19
31
  */
20
- export type UnitShape = "file" | "directory" | "named-field";
32
+ export type UnitShape = "file" | "directory" | "named-field" | "starred-segment";
21
33
  /**
22
34
  * One **channel** a kind's registration declares — a documented way a member
23
35
  * reaches the world (fact 4, `builtins.md`, "The shipped kinds": "user
@@ -40,25 +52,57 @@ export type Registration = {
40
52
  readonly field: string;
41
53
  } | {
42
54
  readonly via: "connection";
55
+ } | {
56
+ readonly via: "enablement";
57
+ readonly field: string;
58
+ } | {
59
+ readonly via: "registry";
43
60
  };
44
61
  /** One of a kind's fields that is a reference to another member — a graph edge (fact 5). */
45
62
  export interface EdgeField {
46
63
  readonly field: string;
47
- /** The target kind's name — the far end of the edge. */
48
- readonly to: string;
64
+ /**
65
+ * The far end of the edge: the non-empty **set** of kinds the field may target.
66
+ * A one-element set resolves a bare address within its one kind; a multi-element
67
+ * set demands the kind-qualified `kind:name` address always, since resolution
68
+ * reads the written text and never infers from the member population. The
69
+ * non-emptiness is the type's to hold — an edge declaring no target kind can
70
+ * never resolve.
71
+ */
72
+ readonly to: readonly [string, ...string[]];
49
73
  }
50
74
  /**
51
- * A kind's **locus** (fact 2): members live at path globs (`at`) or as typed
52
- * fenced blocks inside host documents (`embedded`). An `at` locus is split root +
53
- * glob so the kind fact row carries `governs_root`/`governs_glob` directly.
75
+ * A kind's **locus** (fact 2), in three spellings: members live at path globs (`at`),
76
+ * as typed fenced blocks inside host documents (`embedded`), or as `nested-file`
77
+ * children owning a file whose path composes from their host member's unit and the host
78
+ * kind's template pattern. An `at` locus is split root + glob so the kind fact row
79
+ * carries `governs_root`/`governs_glob` directly; a `nested-file` kind declares neither
80
+ * — the pattern is the host {@link Template}'s one home, so a child can never collide
81
+ * with its host's own governs glob. An `embedded` locus names no host: which types may
82
+ * compose a kind's body is the adopting corpus's `admit` declaration over the host kind
83
+ * (`assembly.ts`), so one embedded type means the same thing in every body that admits
84
+ * it. A `nested-file` child names its host per member ({@link MemberInit}'s `host`) —
85
+ * the unit its path composes under.
86
+ *
87
+ * An `at` locus may declare a `commitment` class of `local`: per-machine and
88
+ * uncommitted — the kind is declared and reviewed, its members' documents are not. The
89
+ * class is a *file* locus's own fact, which is why only `at` spells it. A local locus is
90
+ * read-side only (the document is the governed source, read at check in place under
91
+ * whatever format the kind declares, and never an `emit` input or target), and its
92
+ * members' rows never enter the lock —
93
+ * they derive at read time under the kind the lock declares. Absent is the other class
94
+ * and the default: committed, where the kind and its members' documents are reviewed
95
+ * together and `emit` owns the bytes.
54
96
  */
55
97
  export type Locus = {
56
98
  readonly kind: "at";
57
99
  readonly root: string;
58
100
  readonly glob: string;
101
+ readonly commitment?: "local";
59
102
  } | {
60
103
  readonly kind: "embedded";
61
- readonly withinHosts: readonly string[];
104
+ } | {
105
+ readonly kind: "nested-file";
62
106
  };
63
107
  /**
64
108
  * One region of a kind's **layout** — one of the three corpus primitives over the
@@ -86,17 +130,57 @@ export type LayoutRegion = {
86
130
  export interface Layout {
87
131
  readonly regions: readonly LayoutRegion[];
88
132
  }
89
- /** The six facts of a kind's runtime residue. */
133
+ /**
134
+ * A kind's **body shape** marker — `"fields"` for a fields-only kind: no body slot at
135
+ * all, the member its typed fields and edges and nothing more (a hook, an MCP server).
136
+ * Absent leaves the kind body-bearing, its body `file` (the default) or a declared
137
+ * {@link Layout}.
138
+ */
139
+ export type Shape = "fields";
140
+ /**
141
+ * A registration member's **collection address** — where inside a host manifest its
142
+ * registration surfaces: which `manifest` (`settings.json`, `.mcp.json`) and which
143
+ * `keyPath` it keys at — one of the four addresses the shipped kinds surface at.
144
+ * Carried by a fields-only registration kind; absent for a kind that owns its own file
145
+ * locus.
146
+ */
147
+ export interface CollectionAddress {
148
+ readonly manifest: string;
149
+ readonly keyPath: "hooks.<Event>" | "mcpServers.*" | "enabledPlugins.*" | "extraKnownMarketplaces.*";
150
+ readonly entryShape: string;
151
+ }
152
+ /**
153
+ * A kind's **template** for one inner layer of nested members it hosts (fact 7): the
154
+ * child `kind`, plus the `path` pattern its children sit at — relative to the parent's
155
+ * own unit — when they are files (a skill's bundled reference documents at `*.md`).
156
+ * Omit `path` for an embedded layer, whose children live in the host's body and own no
157
+ * unit of their own.
158
+ *
159
+ * The declaration is the kind's own nesting fact, and a declared fact only: nothing
160
+ * discovers a file child off the pattern, exactly as a host's embedded members resolve
161
+ * off `nested_members` by address rather than off its templates. An adopting corpus may
162
+ * override the child kind by admitting its own over the host (`declarations.ts`'s
163
+ * `templatesFor`).
164
+ */
165
+ export interface Template {
166
+ /** The child kind this layer templates — a kind value, since identity travels by import. */
167
+ readonly kind: KindDefinition<any>;
168
+ /** Where a file child's unit sits, relative to the parent's unit; absent for an embedded layer. */
169
+ readonly path?: string;
170
+ }
171
+ /** The seven facts of a kind's runtime residue. */
90
172
  export interface KindFacts {
91
173
  /** Fact 1, label — the compiled debug label findings speak; the kind's name. */
92
174
  readonly name: string;
93
175
  /** The declared provider authority, when the kind qualifies by one. */
94
176
  readonly provider?: string;
95
- /** Fact 2, locus — where members live. */
177
+ /** Fact 2, locus — where members live, and for a file locus whether their documents
178
+ * are committed: a `local` commitment class declares the kind reviewed and its
179
+ * members' documents not. */
96
180
  readonly locus: Locus;
97
- /** Fact 3a, layout — the projection format; omitted for a frontmatterless kind. */
181
+ /** Fact 3a, projection — the artifact format; omitted for a kind that declares none. */
98
182
  readonly format?: Format;
99
- /** Fact 3b, layout — the on-disk unit shape. */
183
+ /** Fact 3b, projection — the on-disk unit shape. */
100
184
  readonly unitShape: UnitShape;
101
185
  /** Fact 4, registration — the declared channel set naming every documented way
102
186
  * the world reaches a member (never rivals — a member is live if any one is). */
@@ -107,7 +191,9 @@ export interface KindFacts {
107
191
  * identity is read from (an agent's `name`), never the filename or directory.
108
192
  * For `"directory"` it is a projection-order detail only (a skill's `name`
109
193
  * still writes into frontmatter, but identity is the directory name); absent
110
- * when identity is the file stem and no field carries it (a rule).
194
+ * when identity is the file stem and no field carries it (a rule), or the
195
+ * starred directory segment (`"starred-segment"`) — both path-derived, never a
196
+ * field.
111
197
  */
112
198
  readonly identityField?: string;
113
199
  /** Fact 5, edge fields — the kind's fields that are references to other members. */
@@ -115,6 +201,15 @@ export interface KindFacts {
115
201
  /** Fact 6, content — a declared {@link Layout} over the body's heading tree; absent
116
202
  * leaves the kind `file`-content (one verbatim prose body, the default). */
117
203
  readonly content?: Layout;
204
+ /** Fact 6b, content — the fields-only body shape (`"fields"`, no body slot); absent
205
+ * leaves the kind body-bearing (`file` or a {@link Layout}). */
206
+ readonly shape?: Shape;
207
+ /** The registration member's {@link CollectionAddress} — which manifest and key path
208
+ * its registration surfaces at; absent for a kind that owns its own file locus. */
209
+ readonly collectionAddress?: CollectionAddress;
210
+ /** Fact 7, template — one {@link Template} per inner layer of nested members the kind
211
+ * hosts; absent for a kind that nests nothing. */
212
+ readonly templates?: readonly Template[];
118
213
  }
119
214
  /**
120
215
  * One authored member — a typed value in the library. Kind identity travels by
@@ -125,10 +220,12 @@ export interface KindFacts {
125
220
  export interface Member {
126
221
  /** The kind's name — its declaration-row and lock identity. */
127
222
  readonly kind: string;
128
- /** The kind's six facts — carried for projection and the declaration rows. */
223
+ /** The kind's seven facts — carried for projection and the declaration rows. */
129
224
  readonly facts: KindFacts;
130
225
  /** Identity within the kind. */
131
226
  readonly name: string;
227
+ /** The host member a nested-file child's path composes under; absent at every other locus. */
228
+ readonly host?: Member;
132
229
  /** The member's words. */
133
230
  readonly prose?: Prose;
134
231
  /** The kind's typed fields, flat and ordered — the projected frontmatter. */
@@ -143,13 +240,15 @@ export interface Member {
143
240
  /** The init a kind constructor takes — the framework keys plus the kind's typed fields `T`. */
144
241
  export type MemberInit<T> = {
145
242
  readonly name: string;
243
+ /** The host member this member's unit composes under — a nested-file child's, and only its. */
244
+ readonly host?: Member;
146
245
  readonly prose?: Prose;
147
246
  readonly satisfies?: readonly string[];
148
247
  readonly requires?: Readonly<Record<string, Requirement>>;
149
248
  readonly needs?: readonly Capability[];
150
249
  } & T;
151
250
  /**
152
- * A kind — a callable constructor carrying its six facts. Calling it builds a
251
+ * A kind — a callable constructor carrying its seven facts. Calling it builds a
153
252
  * member; `key` (its name) keys `expect` and a `kind` reference in a requirement.
154
253
  * The value *is* the identity — it travels by import, never by string.
155
254
  */
@@ -168,14 +267,14 @@ export interface KindDefinition<T> {
168
267
  */
169
268
  readonly render?: (value: ResolvedEmbeddedMemberValue) => string;
170
269
  }
171
- /** The options `kind()` takes beyond its six facts — today, only the embedded `render` hook. */
270
+ /** The options `kind()` takes beyond its seven facts — today, only the embedded `render` hook. */
172
271
  export interface KindOptions {
173
272
  readonly render?: (value: ResolvedEmbeddedMemberValue) => string;
174
273
  }
175
274
  /**
176
275
  * Define a kind. Returns a constructor over the kind's typed
177
276
  * fields `T`; every type erases at the seam, so what the returned member carries
178
- * into emit is the six facts plus flat field data. `options.render`, when given,
277
+ * into emit is the seven facts plus flat field data. `options.render`, when given,
179
278
  * rides alongside `facts`/`key` on the returned constructor — never on the member
180
279
  * it builds, since it is erased before a member reaches emit.
181
280
  */
@@ -195,7 +294,7 @@ export interface EmbeddedMemberCollectionEntry {
195
294
  readonly leaves: Readonly<Record<string, string | Text>>;
196
295
  }
197
296
  /**
198
- * An **embedded member's** composed value (posture 3, passed to `blocks()`):
297
+ * An **embedded member's** composed value — one child of a composed body, passed to `blocks()`:
199
298
  * leaves are authored strings keyed by field name; sibling collections are keyed
200
299
  * by collection name, each an authored-order list of entries — leaf addresses
201
300
  * are structural and keyed. Its
@@ -219,6 +318,12 @@ export interface EmbeddedMemberValue {
219
318
  readonly collections: Readonly<Record<string, readonly EmbeddedMemberCollectionEntry[]>>;
220
319
  /** The originating kind's `render` hook, when declared — resolved once at construction. */
221
320
  readonly render?: (value: ResolvedEmbeddedMemberValue) => string;
321
+ /**
322
+ * The originating kind's declared edge fields — which of the value's leaves are
323
+ * addresses, and the target facts emit derives from them. Resolved once at
324
+ * construction, the way `render` is.
325
+ */
326
+ readonly edgeFields?: readonly EdgeField[];
222
327
  }
223
328
  /**
224
329
  * One resolved sibling-collection entry: its own key plus its leaf fields,
@@ -230,6 +335,23 @@ export interface ResolvedEmbeddedMemberCollectionEntry {
230
335
  /** The entry's own leaf fields, already resolved to their final strings. */
231
336
  readonly leaves: Readonly<Record<string, string>>;
232
337
  }
338
+ /**
339
+ * The closed set of facts an embedded format may place about one edge field's
340
+ * target — derived at emit off the resolved target member, never authored at the
341
+ * instance and never fabricated, so a rendered reference is true by construction.
342
+ * The set is exactly these four; a fifth fact is a spec question, not a
343
+ * convenience.
344
+ */
345
+ export interface EdgeTargetFacts {
346
+ /** The target member's identity within its kind. */
347
+ readonly name: string;
348
+ /** The target's `kind:name` address — what the edge field's leaf authored. */
349
+ readonly address: string;
350
+ /** The target member's kind. */
351
+ readonly kind: string;
352
+ /** The target's projection, relative to the host member's own projection. */
353
+ readonly path: string;
354
+ }
233
355
  /**
234
356
  * An {@link EmbeddedMemberValue} after every leaf (top-level and each
235
357
  * collection entry's) resolves to its final stored string
@@ -245,12 +367,21 @@ export interface ResolvedEmbeddedMemberValue {
245
367
  readonly leaves: Readonly<Record<string, string>>;
246
368
  /** Sibling collections, each entry's leaves already resolved. */
247
369
  readonly collections: Readonly<Record<string, readonly ResolvedEmbeddedMemberCollectionEntry[]>>;
370
+ /**
371
+ * The target facts of each edge field this value *fills*, keyed by the edge field's
372
+ * own name — the data a `render` hook selects to spell a reference. An unfilled
373
+ * field is no edge and carries no entry; a kind declaring no edge fields (or a value
374
+ * composed off a bare kind name, which carries none) has an empty map.
375
+ */
376
+ readonly targets: Readonly<Record<string, EdgeTargetFacts>>;
248
377
  }
249
378
  /**
250
379
  * Compose an embedded member's value for `blocks()` — the shape any project's own
251
380
  * child kind uses. `kind` names the child kind: a bare string, or the child kind's
252
- * own `KindDefinition` — passing the definition carries its `render` hook (when
253
- * declared) through to emit, with no other change to the composed value's shape.
381
+ * own `KindDefinition` — passing the definition carries its `render` hook and its
382
+ * declared edge fields (when declared) through to emit, with no other change to the
383
+ * composed value's shape. A bare string names a kind whose facts are out of reach, so
384
+ * such a value renders with no target facts.
254
385
  */
255
386
  export declare function embeddedMemberValue(init: {
256
387
  kind: string | KindDefinition<any>;
package/dist/src/kind.js CHANGED
@@ -1,21 +1,25 @@
1
1
  /**
2
2
  * Kinds — the engine room. A kind is a plain typed surface — an interface
3
- * `T` and a constructor `kind<T>()` — plus six facts of runtime residue: label,
4
- * locus, layout, registration, edge fields, and content. `tsc` is the keystroke wall;
5
- * every type erases at the seam, and what a kind leaves behind is those six facts,
6
- * riding the lock as rows. Identity travels by import, never by string — a `kind`
7
- * reference is the imported value.
3
+ * `T` and a constructor `kind<T>()` — plus seven facts of runtime residue: label,
4
+ * locus, projection, registration, edge fields, content, and template. A registration kind (a hook,
5
+ * an MCP server) extends the content fact with a fields-only `shape` and a
6
+ * `collectionAddress` naming the host manifest it surfaces in. `tsc` is the keystroke
7
+ * wall; every type erases at the seam, and what a kind leaves behind rides the lock as
8
+ * rows. Identity travels by import, never by string — a `kind` reference is the imported
9
+ * value.
8
10
  */
9
11
  /** The framework keys of a member init — everything else is a typed field (flat). */
10
- const FRAMEWORK_KEYS = new Set(["name", "prose", "satisfies", "requires", "needs"]);
12
+ const FRAMEWORK_KEYS = new Set(["name", "host", "prose", "satisfies", "requires", "needs"]);
11
13
  /**
12
- * Build the ordered projected-frontmatter fields for a member: nothing for a
13
- * frontmatterless kind (memory declares no `format`), else the identity field
14
- * (when the kind writes its name into frontmatter) followed by the typed fields
15
- * in the author's declared order.
14
+ * Build the ordered projected fields for a member: nothing for a frontmatterless
15
+ * body-bearing kind (memory declares no `format` and is not fields-only), else the
16
+ * identity field (when the kind writes its name into frontmatter) followed by the
17
+ * typed fields in the author's declared order. A fields-only registration kind (a
18
+ * hook, an MCP server) carries its typed fields though it declares no `format` —
19
+ * the fields are the whole member, folded into a manifest entry, never a header.
16
20
  */
17
21
  function orderedFields(facts, init) {
18
- if (facts.format === undefined)
22
+ if (facts.format === undefined && facts.shape !== "fields")
19
23
  return [];
20
24
  const typed = [];
21
25
  for (const [key, value] of Object.entries(init)) {
@@ -25,10 +29,31 @@ function orderedFields(facts, init) {
25
29
  const head = facts.identityField !== undefined ? [[facts.identityField, init.name]] : [];
26
30
  return [...head, ...typed];
27
31
  }
32
+ /**
33
+ * The host a member init names, checked against its kind's locus: a nested-file child's
34
+ * path composes from its host's unit, so it names one and every other locus names none.
35
+ *
36
+ * # Throws
37
+ * If a nested-file member declares no host, or a member at any other locus declares one.
38
+ */
39
+ function hostOf(facts, init) {
40
+ const nested = facts.locus.kind === "nested-file";
41
+ if (nested && init.host === undefined) {
42
+ throw new Error(`member \`${init.name}\` of kind \`${facts.name}\`: a nested file child owns a file whose ` +
43
+ `path composes from its host's unit, so it names the \`host\` member it sits under ` +
44
+ `(specs/model/representation.md, "locus").`);
45
+ }
46
+ if (!nested && init.host !== undefined) {
47
+ throw new Error(`member \`${init.name}\` of kind \`${facts.name}\`: \`host\` names the member a nested ` +
48
+ `file child composes its path under, and this kind's locus is \`${facts.locus.kind}\` — ` +
49
+ `its path composes from nobody.`);
50
+ }
51
+ return init.host;
52
+ }
28
53
  /**
29
54
  * Define a kind. Returns a constructor over the kind's typed
30
55
  * fields `T`; every type erases at the seam, so what the returned member carries
31
- * into emit is the six facts plus flat field data. `options.render`, when given,
56
+ * into emit is the seven facts plus flat field data. `options.render`, when given,
32
57
  * rides alongside `facts`/`key` on the returned constructor — never on the member
33
58
  * it builds, since it is erased before a member reaches emit.
34
59
  */
@@ -37,6 +62,7 @@ export function kind(facts, options = {}) {
37
62
  kind: facts.name,
38
63
  facts,
39
64
  name: init.name,
65
+ host: hostOf(facts, init),
40
66
  prose: init.prose,
41
67
  fields: orderedFields(facts, init),
42
68
  satisfies: init.satisfies ?? [],
@@ -48,16 +74,21 @@ export function kind(facts, options = {}) {
48
74
  /**
49
75
  * Compose an embedded member's value for `blocks()` — the shape any project's own
50
76
  * child kind uses. `kind` names the child kind: a bare string, or the child kind's
51
- * own `KindDefinition` — passing the definition carries its `render` hook (when
52
- * declared) through to emit, with no other change to the composed value's shape.
77
+ * own `KindDefinition` — passing the definition carries its `render` hook and its
78
+ * declared edge fields (when declared) through to emit, with no other change to the
79
+ * composed value's shape. A bare string names a kind whose facts are out of reach, so
80
+ * such a value renders with no target facts.
53
81
  */
54
82
  export function embeddedMemberValue(init) {
55
- const [kindName, render] = typeof init.kind === "string" ? [init.kind, undefined] : [init.kind.key, init.kind.render];
83
+ const definition = typeof init.kind === "string" ? undefined : init.kind;
84
+ const render = definition?.render;
85
+ const edgeFields = definition?.facts.edgeFields;
56
86
  return {
57
- kind: kindName,
87
+ kind: definition?.key ?? init.kind,
58
88
  key: init.key,
59
89
  leaves: init.leaves,
60
90
  collections: init.collections ?? {},
61
91
  ...(render !== undefined ? { render } : {}),
92
+ ...(edgeFields !== undefined ? { edgeFields } : {}),
62
93
  };
63
94
  }
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Prose — three constructors, one field type. A member's words are data the
3
3
  * member declares: `file()` for a document that keeps its medium, `` text`…` ``
4
- * for short inline prose, `blocks()` for fully composed embedded-member values. Whatever
5
- * the constructor, the words land byte-identical to their authored text (law 5).
4
+ * for short inline prose, `blocks()` for a composed body that interleaves verbatim
5
+ * prose spans with embedded-member values in authored order. Whatever
6
+ * the constructor, the words land byte-identical to their authored text.
6
7
  * Interpolations in `` text`…` `` are references, two intents apart: a **mention**
7
8
  * (a {@link Mentionable}) is a declared one-way edge that moves no content, and an
8
9
  * **include** (an {@link Include}) pulls the target file's bytes into the host's emitted
9
- * projection. Both are authored per word and resolution-checked at emit, never mined
10
- * (law 8).
10
+ * projection. Both are authored per word and resolution-checked at emit, never mined.
11
11
  */
12
- import type { EmbeddedMemberValue } from "./kind.js";
12
+ import type { EmbeddedMemberValue, Member } from "./kind.js";
13
13
  /** A declared value a mention may name — the target of the one-way citation edge. */
14
14
  export interface Mentionable {
15
15
  /** The mention's rendered form and graph edge target (`kind:name` or a leaf address). */
@@ -17,6 +17,12 @@ export interface Mentionable {
17
17
  /** The display text the one corpus-wide rule renders in place. */
18
18
  readonly display: string;
19
19
  }
20
+ /**
21
+ * Spell a top-level member as the {@link Mentionable} a mention carries: its
22
+ * `kind:name` address, its bare name the display text — the convention every
23
+ * corpus repeats to cite a member from prose, captured once here.
24
+ */
25
+ export declare function mentionOf(member: Member): Mentionable;
20
26
  /** One authored interpolation: position in the template plus its target. */
21
27
  export interface Mention {
22
28
  readonly index: number;
@@ -37,11 +43,42 @@ export interface Include {
37
43
  }
38
44
  /** A `` text`…` `` interpolation — a mention (moves no content) or an include (moves content). */
39
45
  export type Reference = Mentionable | Include;
46
+ /**
47
+ * The resolution universe a mention is checked against at emit: the addresses that
48
+ * resolve here and now ({@link mentionable}), and the kinds whose members the program
49
+ * declares but does not compose ({@link deferrableKinds}) — a mention naming one of
50
+ * those defers to `check` rather than refusing at emit.
51
+ */
52
+ export interface MentionScope {
53
+ /** Every address a mention may resolve against in the program's own universe. */
54
+ readonly mentionable: ReadonlySet<string>;
55
+ /** The discoverable (`at`-locus) kinds the program declares — the deferral signal. */
56
+ readonly deferrableKinds: ReadonlySet<string>;
57
+ }
58
+ /**
59
+ * Whether a mention's unresolved address **defers to the gate** rather than refusing at
60
+ * emit: a top-level `kind:name` address whose kind is one the program declares at a
61
+ * discovery locus (an `at`-locus kind) may name a member discovered on disk, so `check`
62
+ * owns the verdict. An embedded leaf address (a `<host>/<kind>/<key>` form, carrying a
63
+ * `/`), a bare requirement name (no `:`), or a kind the program does not declare has no
64
+ * discovery locus and stays a dangling refusal.
65
+ */
66
+ export declare function defersToGate(address: string, deferrableKinds: ReadonlySet<string>): boolean;
67
+ /**
68
+ * Refuse a mention whose address neither resolves against the scope's `mentionable`
69
+ * set nor defers to the gate ({@link defersToGate}) — the one dangling-mention refusal,
70
+ * shared by a member-level `Text` body, a composed body's prose span, and an embedded
71
+ * `Text` leaf. `context` prefixes the error so it names the host.
72
+ *
73
+ * # Throws
74
+ * If a mention names no declared value and its address has no discovery locus.
75
+ */
76
+ export declare function checkMentions(mentions: readonly Mention[], scope: MentionScope, context: string): void;
40
77
  /**
41
78
  * `file(moduleUrl, path)` — the document keeps its medium: markdown in a
42
- * markdown file, full tooling, forever legal (posture 1, `15-kinds.md`).
43
- * Resolved and read in byte-for-byte at emit, relative to the declaring
44
- * module, never the process cwd.
79
+ * markdown file, full tooling, forever legal. Resolved and read in
80
+ * byte-for-byte at emit, relative to the declaring module, never the
81
+ * process cwd.
45
82
  */
46
83
  export interface File {
47
84
  readonly kind: "file";
@@ -53,7 +90,7 @@ export interface File {
53
90
  /**
54
91
  * `` text`…` `` — short prose inline, dedented, byte-deterministic; the
55
92
  * three-line rule that would be silly as a sidecar file. Mentions ride beside
56
- * the text, never inside it (law 5).
93
+ * the text, never inside it.
57
94
  */
58
95
  export interface Text {
59
96
  readonly kind: "text";
@@ -67,20 +104,23 @@ export interface Text {
67
104
  readonly includes: readonly Include[];
68
105
  }
69
106
  /**
70
- * `blocks(…)` — fully composed embedded-member values (posture 3): typed
71
- * collections rendered to `member.<kind> <key>` TOML fences, byte-identical to
72
- * the same values authored as fences directly (posture 2) and read back by the
107
+ * `blocks(…)` — a composed body of ordered children: verbatim prose spans
108
+ * ({@link Text}) and embedded-member values interleaved in authored order,
109
+ * the write-side mirror of a layout's ordered regions. A {@link Text} span stays
110
+ * prose — it carries its own mentions and includes and mints no wrapper member;
111
+ * an embedded value renders to its `member.<kind> <key>` TOML fence, byte-identical
112
+ * to the same value authored as a fence directly and read back by the
73
113
  * engine's fold (`src/extract.rs` `parse_embedded_info`/`parse_embedded_member`).
74
114
  */
75
115
  export interface Blocks {
76
116
  readonly kind: "blocks";
77
- readonly values: readonly EmbeddedMemberValue[];
117
+ readonly values: readonly (Text | EmbeddedMemberValue)[];
78
118
  }
79
119
  /** A member's prose — one of the three constructors, one field type. */
80
120
  export type Prose = File | Text | Blocks;
81
121
  /**
82
- * Declare a document whose medium is preserved — read in whole at emit
83
- * (posture 1). `moduleUrl` is the declaring module's own `import.meta.url`,
122
+ * Declare a document whose medium is preserved — read in whole at emit.
123
+ * `moduleUrl` is the declaring module's own `import.meta.url`,
84
124
  * so `path` resolves relative to that module, never the process cwd:
85
125
  * `file(import.meta.url, "./long.md")`.
86
126
  */
@@ -89,7 +129,7 @@ export declare function file(moduleUrl: string, path: string): File;
89
129
  * The inline dedenting prose constructor. Interpolate {@link Reference} values — a
90
130
  * {@link Mentionable} is a mention (moves no content), an {@link Include} pulls the
91
131
  * target's bytes in; either is opt-in per word, and plain prose with zero references is
92
- * fully legal forever (the opt-in Decision, `20-surface.md`).
132
+ * fully legal forever.
93
133
  *
94
134
  * # Throws
95
135
  * If an authored chunk carries {@link MENTION_SLOT} or {@link INCLUDE_SLOT} — the markers
@@ -103,23 +143,39 @@ export declare function text(strings: TemplateStringsArray, ...targets: Referenc
103
143
  * `include(import.meta.url, "./fragment.md")`.
104
144
  */
105
145
  export declare function include(moduleUrl: string, path: string): Include;
106
- /** Compose fully-typed embedded-member values into a member's body (posture 3). */
107
- export declare function blocks(...values: EmbeddedMemberValue[]): Blocks;
146
+ /**
147
+ * Compose a member's body from ordered children: verbatim prose spans
148
+ * ({@link text}) and embedded-member values interleaved in authored order. A prose
149
+ * span rides as prose — no wrapper member is minted to carry a narrative.
150
+ *
151
+ * # Throws
152
+ * If a child is a {@link File} — the parameter type excludes one, so only a JS or
153
+ * cast caller arrives here, and the alternative is a raw crash downstream on the
154
+ * `leaves` a `File` has no reason to carry.
155
+ */
156
+ export declare function blocks(...values: (Text | EmbeddedMemberValue)[]): Blocks;
157
+ /**
158
+ * Whether a composed-body child is a verbatim prose span rather than an embedded
159
+ * member value — the discriminant emit and the row builders branch on to render a
160
+ * span as prose and count its refs at host level, never as a nested member.
161
+ */
162
+ export declare function isTextSpan(value: Text | EmbeddedMemberValue): value is Text;
108
163
  /**
109
164
  * Render an inline body to its final text — the display rule applied: each
110
165
  * mention slot becomes its target's display form, the surrounding words
111
- * untouched (law 5). The chunk count is `mentions.length + 1` by {@link text}'s
166
+ * untouched. The chunk count is `mentions.length + 1` by {@link text}'s
112
167
  * construction, so the walk consumes every slot.
113
168
  */
114
169
  export declare function renderText(prose: Text): string;
115
170
  /**
116
171
  * Resolve a leaf's authored value to its stored/rendered string: a bare string
117
- * is unchanged; a `Text` leaf is mention-resolution-checked against
118
- * `mentionable` (loud on a dangling address) and rendered by {@link renderText}
119
- * — the same rule a member-level `Text` body resolves by. `context` prefixes
120
- * the dangling-mention error so it names the leaf, not just the mention.
172
+ * is unchanged; a `Text` leaf is mention-resolution-checked against `scope`
173
+ * ({@link checkMentions}: loud on a dangling address, a discovery-locus one
174
+ * deferred) and rendered by {@link renderText} — the same rule a member-level
175
+ * `Text` body resolves by. `context` prefixes the dangling-mention error so it
176
+ * names the leaf, not just the mention.
121
177
  *
122
178
  * # Throws
123
- * If a mention names no declared value.
179
+ * If a mention names no declared value and has no discovery locus.
124
180
  */
125
- export declare function resolveLeaf(value: string | Text, mentionable: ReadonlySet<string>, context: string): string;
181
+ export declare function resolveLeaf(value: string | Text, scope: MentionScope, context: string): string;