@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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The member-address grammar — one home for every spelling a member's identity takes and
|
|
3
|
+
* for the reader those writers round-trip against. Three spellings, each the one before
|
|
4
|
+
* it plus a segment:
|
|
5
|
+
*
|
|
6
|
+
* - `<kind>:<name>` — a **host address**, a top-level member's own identity.
|
|
7
|
+
* - `<host-address>/<kind>/<key>` — a **nested-member address**, the identity a member
|
|
8
|
+
* embedded in a host carries.
|
|
9
|
+
* - `<member>/<kind>/<key>/<leaf>` — a **leaf address**, one authored string beneath a
|
|
10
|
+
* nested member. A grain of its own, and no member address at all.
|
|
11
|
+
*
|
|
12
|
+
* Because the third is the second plus a `/<leaf>` tail, both are read off one
|
|
13
|
+
* segmentation ({@link segment}) rather than two independently-shaped parses that could
|
|
14
|
+
* come to disagree about where the member ends. The engine's `src/member_address.rs` is
|
|
15
|
+
* the other end of the same grammar: a spelling moves on both sides at once or the seam
|
|
16
|
+
* breaks.
|
|
17
|
+
*/
|
|
18
|
+
/** The two halves a host address names — the reader's answer, and never re-split by hand. */
|
|
19
|
+
export interface HostAddress {
|
|
20
|
+
/** The member's kind. */
|
|
21
|
+
readonly kind: string;
|
|
22
|
+
/** The member's name among its kind. */
|
|
23
|
+
readonly name: string;
|
|
24
|
+
}
|
|
25
|
+
/** One parsed nested-member address. */
|
|
26
|
+
export interface NestedAddress {
|
|
27
|
+
/** The host member's own `<kind>:<name>` address — the segment before the first `/`. */
|
|
28
|
+
readonly host: string;
|
|
29
|
+
/** The nested member's kind. */
|
|
30
|
+
readonly kind: string;
|
|
31
|
+
/** The nested member's key among its host's members of that kind. */
|
|
32
|
+
readonly key: string;
|
|
33
|
+
}
|
|
34
|
+
/** One parsed leaf address — a nested-member address plus its `/<leaf>` tail. */
|
|
35
|
+
export interface LeafAddress {
|
|
36
|
+
/**
|
|
37
|
+
* The member the leaf lives under, verbatim as its author spelled it: the canonical
|
|
38
|
+
* `<kind>:<name>` host address, or the bare member id this SDK's own leaf writer and
|
|
39
|
+
* the committed lock mention targets spell. Which of the two a head is, is resolution's
|
|
40
|
+
* question, not the grammar's.
|
|
41
|
+
*/
|
|
42
|
+
readonly member: string;
|
|
43
|
+
/** The nested member's kind. */
|
|
44
|
+
readonly kind: string;
|
|
45
|
+
/** The nested member's key among its host's members of that kind. */
|
|
46
|
+
readonly key: string;
|
|
47
|
+
/** The leaf's path within the nested member — the whole remainder after the third slash. */
|
|
48
|
+
readonly childPath: string;
|
|
49
|
+
}
|
|
50
|
+
/** Spell a top-level member's own `<kind>:<name>` address. */
|
|
51
|
+
export declare function hostAddress(kind: string, name: string): string;
|
|
52
|
+
/** Spell a nested member's address from its host's address, its own kind and its key. */
|
|
53
|
+
export declare function nestedAddress(host: string, kind: string, key: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Spell one leaf's address beneath a nested member. `member` is carried verbatim, so a
|
|
56
|
+
* writer holding the bare member id spells the short form the lock already commits
|
|
57
|
+
* ({@link LeafAddress.member}).
|
|
58
|
+
*/
|
|
59
|
+
export declare function leafAddress(member: string, kind: string, key: string, childPath: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* The member-table **lookup key** a bare `<kind>:<key>` reference spells — shaped like a
|
|
62
|
+
* host address and never one: a nested member's address composes through its host, and a
|
|
63
|
+
* corpus-unique key was rejected as the grammar because uniqueness is the resolver's bar,
|
|
64
|
+
* not the grammar's. The engine strips this prefix back off before matching, so the two
|
|
65
|
+
* ends agree and the apparent mismatch is not a defect to fix.
|
|
66
|
+
*/
|
|
67
|
+
export declare function bareLookupKey(kind: string, key: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* The key an authored edge address is looked up under: a one-element `to` set resolves a
|
|
70
|
+
* bare address within its one admissible kind, so an unqualified address is lifted into a
|
|
71
|
+
* {@link bareLookupKey}; an address already carrying a colon is the kind-qualified form
|
|
72
|
+
* the author wrote and stands as it is.
|
|
73
|
+
*/
|
|
74
|
+
export declare function edgeLookupKey(address: string, to: readonly string[]): string;
|
|
75
|
+
/**
|
|
76
|
+
* The `(kind, name)` a host address spells, or `undefined` when it spells none — the
|
|
77
|
+
* reader half of {@link hostAddress}. Both halves are non-empty: an address names exactly
|
|
78
|
+
* one thing or it names nothing. Splits at the **first** colon, so a name carrying one
|
|
79
|
+
* stays whole.
|
|
80
|
+
*
|
|
81
|
+
* A `/` anywhere is this grammar's own segment separator ({@link segment} splits on it),
|
|
82
|
+
* so an address carrying one is a segmented spelling with its own reader
|
|
83
|
+
* ({@link parseNestedAddress}, {@link parseLeafAddress}) and no host address at all. The
|
|
84
|
+
* refusal lives here rather than at each caller, so a reader that asks "is this a host
|
|
85
|
+
* address?" never has to re-spell the separator to get the answer right.
|
|
86
|
+
*/
|
|
87
|
+
export declare function parseHostAddress(address: string): HostAddress | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Parse a nested-member address, or `undefined` when `address` is not one: exactly three
|
|
90
|
+
* non-empty segments, the first of them a host address.
|
|
91
|
+
*
|
|
92
|
+
* A `/<leaf>` tail is ruled out here explicitly — a leaf is its own grain, and truncating
|
|
93
|
+
* one to the member that happens to contain it would answer a leaf reference with a member
|
|
94
|
+
* the author never named.
|
|
95
|
+
*/
|
|
96
|
+
export declare function parseNestedAddress(address: string): NestedAddress | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Parse a leaf address, or `undefined` when `target` carries no tail or a segment-shaped
|
|
99
|
+
* hole. The head is carried on verbatim rather than split, because the bare member id is
|
|
100
|
+
* a live short form ({@link LeafAddress.member}).
|
|
101
|
+
*/
|
|
102
|
+
export declare function parseLeafAddress(target: string): LeafAddress | undefined;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The member-address grammar — one home for every spelling a member's identity takes and
|
|
3
|
+
* for the reader those writers round-trip against. Three spellings, each the one before
|
|
4
|
+
* it plus a segment:
|
|
5
|
+
*
|
|
6
|
+
* - `<kind>:<name>` — a **host address**, a top-level member's own identity.
|
|
7
|
+
* - `<host-address>/<kind>/<key>` — a **nested-member address**, the identity a member
|
|
8
|
+
* embedded in a host carries.
|
|
9
|
+
* - `<member>/<kind>/<key>/<leaf>` — a **leaf address**, one authored string beneath a
|
|
10
|
+
* nested member. A grain of its own, and no member address at all.
|
|
11
|
+
*
|
|
12
|
+
* Because the third is the second plus a `/<leaf>` tail, both are read off one
|
|
13
|
+
* segmentation ({@link segment}) rather than two independently-shaped parses that could
|
|
14
|
+
* come to disagree about where the member ends. The engine's `src/member_address.rs` is
|
|
15
|
+
* the other end of the same grammar: a spelling moves on both sides at once or the seam
|
|
16
|
+
* breaks.
|
|
17
|
+
*/
|
|
18
|
+
/** Spell a top-level member's own `<kind>:<name>` address. */
|
|
19
|
+
export function hostAddress(kind, name) {
|
|
20
|
+
return `${kind}:${name}`;
|
|
21
|
+
}
|
|
22
|
+
/** Spell a nested member's address from its host's address, its own kind and its key. */
|
|
23
|
+
export function nestedAddress(host, kind, key) {
|
|
24
|
+
return `${host}/${kind}/${key}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Spell one leaf's address beneath a nested member. `member` is carried verbatim, so a
|
|
28
|
+
* writer holding the bare member id spells the short form the lock already commits
|
|
29
|
+
* ({@link LeafAddress.member}).
|
|
30
|
+
*/
|
|
31
|
+
export function leafAddress(member, kind, key, childPath) {
|
|
32
|
+
return `${nestedAddress(member, kind, key)}/${childPath}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The member-table **lookup key** a bare `<kind>:<key>` reference spells — shaped like a
|
|
36
|
+
* host address and never one: a nested member's address composes through its host, and a
|
|
37
|
+
* corpus-unique key was rejected as the grammar because uniqueness is the resolver's bar,
|
|
38
|
+
* not the grammar's. The engine strips this prefix back off before matching, so the two
|
|
39
|
+
* ends agree and the apparent mismatch is not a defect to fix.
|
|
40
|
+
*/
|
|
41
|
+
export function bareLookupKey(kind, key) {
|
|
42
|
+
return `${kind}:${key}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The key an authored edge address is looked up under: a one-element `to` set resolves a
|
|
46
|
+
* bare address within its one admissible kind, so an unqualified address is lifted into a
|
|
47
|
+
* {@link bareLookupKey}; an address already carrying a colon is the kind-qualified form
|
|
48
|
+
* the author wrote and stands as it is.
|
|
49
|
+
*/
|
|
50
|
+
export function edgeLookupKey(address, to) {
|
|
51
|
+
return to.length === 1 && !address.includes(":") ? bareLookupKey(to[0], address) : address;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The `(kind, name)` a host address spells, or `undefined` when it spells none — the
|
|
55
|
+
* reader half of {@link hostAddress}. Both halves are non-empty: an address names exactly
|
|
56
|
+
* one thing or it names nothing. Splits at the **first** colon, so a name carrying one
|
|
57
|
+
* stays whole.
|
|
58
|
+
*
|
|
59
|
+
* A `/` anywhere is this grammar's own segment separator ({@link segment} splits on it),
|
|
60
|
+
* so an address carrying one is a segmented spelling with its own reader
|
|
61
|
+
* ({@link parseNestedAddress}, {@link parseLeafAddress}) and no host address at all. The
|
|
62
|
+
* refusal lives here rather than at each caller, so a reader that asks "is this a host
|
|
63
|
+
* address?" never has to re-spell the separator to get the answer right.
|
|
64
|
+
*/
|
|
65
|
+
export function parseHostAddress(address) {
|
|
66
|
+
if (address.includes("/"))
|
|
67
|
+
return undefined;
|
|
68
|
+
const colon = address.indexOf(":");
|
|
69
|
+
if (colon <= 0 || colon === address.length - 1)
|
|
70
|
+
return undefined;
|
|
71
|
+
return { kind: address.slice(0, colon), name: address.slice(colon + 1) };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Cut an address into its segments, or `undefined` when it carries fewer than three or a
|
|
75
|
+
* segment-shaped hole. The tail keeps its own dots and slashes, so it is the whole
|
|
76
|
+
* remainder after the third slash rather than a fourth segment among more.
|
|
77
|
+
*/
|
|
78
|
+
function segment(address) {
|
|
79
|
+
const parts = address.split("/");
|
|
80
|
+
if (parts.length < 3)
|
|
81
|
+
return undefined;
|
|
82
|
+
const [host, kind, key] = parts;
|
|
83
|
+
const tail = parts.length > 3 ? parts.slice(3).join("/") : undefined;
|
|
84
|
+
if (host === "" || kind === "" || key === "" || tail === "")
|
|
85
|
+
return undefined;
|
|
86
|
+
return { host, kind, key, tail };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse a nested-member address, or `undefined` when `address` is not one: exactly three
|
|
90
|
+
* non-empty segments, the first of them a host address.
|
|
91
|
+
*
|
|
92
|
+
* A `/<leaf>` tail is ruled out here explicitly — a leaf is its own grain, and truncating
|
|
93
|
+
* one to the member that happens to contain it would answer a leaf reference with a member
|
|
94
|
+
* the author never named.
|
|
95
|
+
*/
|
|
96
|
+
export function parseNestedAddress(address) {
|
|
97
|
+
const segments = segment(address);
|
|
98
|
+
if (segments === undefined || segments.tail !== undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
if (parseHostAddress(segments.host) === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
return { host: segments.host, kind: segments.kind, key: segments.key };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse a leaf address, or `undefined` when `target` carries no tail or a segment-shaped
|
|
106
|
+
* hole. The head is carried on verbatim rather than split, because the bare member id is
|
|
107
|
+
* a live short form ({@link LeafAddress.member}).
|
|
108
|
+
*/
|
|
109
|
+
export function parseLeafAddress(target) {
|
|
110
|
+
const segments = segment(target);
|
|
111
|
+
if (segments?.tail === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
return { member: segments.host, kind: segments.kind, key: segments.key, childPath: segments.tail };
|
|
114
|
+
}
|
package/dist/src/prose.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prose —
|
|
2
|
+
* Prose — four 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, `
|
|
5
|
-
* prose spans with embedded-member values in
|
|
6
|
-
* the constructor, the words land byte-identical to their
|
|
4
|
+
* for short inline prose, `span()` for prose the program computes, `blocks()` for a
|
|
5
|
+
* composed body that interleaves verbatim prose spans with embedded-member values in
|
|
6
|
+
* authored order. Whatever the constructor, the words land byte-identical to their
|
|
7
|
+
* authored text. The two inline constructors build the same {@link Text} and differ
|
|
8
|
+
* only in where the words come from: reach for `` text`…` `` when they are literal, for
|
|
9
|
+
* `span()` when they are a string the program derived — the tag's interpolations are
|
|
10
|
+
* references, never words, so a computed string has no slot to ride in.
|
|
7
11
|
* Interpolations in `` text`…` `` are references, two intents apart: a **mention**
|
|
8
12
|
* (a {@link Mentionable}) is a declared one-way edge that moves no content, and an
|
|
9
13
|
* **include** (an {@link Include}) pulls the target file's bytes into the host's emitted
|
|
@@ -19,8 +23,8 @@ export interface Mentionable {
|
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Spell a top-level member as the {@link Mentionable} a mention carries: its
|
|
22
|
-
*
|
|
23
|
-
*
|
|
26
|
+
* {@link hostAddress}, its bare name the display text — the convention every corpus
|
|
27
|
+
* repeats to cite a member from prose.
|
|
24
28
|
*/
|
|
25
29
|
export declare function mentionOf(member: Member): Mentionable;
|
|
26
30
|
/** One authored interpolation: position in the template plus its target. */
|
|
@@ -57,11 +61,11 @@ export interface MentionScope {
|
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* Whether a mention's unresolved address **defers to the gate** rather than refusing at
|
|
60
|
-
* emit: a
|
|
61
|
-
* discovery locus (an `at`-locus kind) may name a member discovered on disk, so
|
|
62
|
-
* owns the verdict.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
64
|
+
* emit: a host address ({@link parseHostAddress}) whose kind is one the program declares
|
|
65
|
+
* at a discovery locus (an `at`-locus kind) may name a member discovered on disk, so
|
|
66
|
+
* `check` owns the verdict. Anything the grammar's reader answers `undefined` for — a
|
|
67
|
+
* segmented embedded address, a bare requirement name, a name-less `kind:` — names no
|
|
68
|
+
* discoverable member, and stays a dangling refusal however deferrable its head reads.
|
|
65
69
|
*/
|
|
66
70
|
export declare function defersToGate(address: string, deferrableKinds: ReadonlySet<string>): boolean;
|
|
67
71
|
/**
|
|
@@ -116,7 +120,7 @@ export interface Blocks {
|
|
|
116
120
|
readonly kind: "blocks";
|
|
117
121
|
readonly values: readonly (Text | EmbeddedMemberValue)[];
|
|
118
122
|
}
|
|
119
|
-
/** A member's prose — one of the
|
|
123
|
+
/** A member's prose — one of the four constructors, one field type. */
|
|
120
124
|
export type Prose = File | Text | Blocks;
|
|
121
125
|
/**
|
|
122
126
|
* Declare a document whose medium is preserved — read in whole at emit.
|
|
@@ -136,6 +140,19 @@ export declare function file(moduleUrl: string, path: string): File;
|
|
|
136
140
|
* are the tool's alone, so a stray one is a loud authoring error, not a silent mis-split.
|
|
137
141
|
*/
|
|
138
142
|
export declare function text(strings: TemplateStringsArray, ...targets: Reference[]): Text;
|
|
143
|
+
/**
|
|
144
|
+
* The computed-prose constructor: the same inline prose as `` text`…` ``, from a string
|
|
145
|
+
* the program built rather than a template the author typed — a rolled-up participants
|
|
146
|
+
* line, a table derived from the members. An author reaches for the tag whenever the
|
|
147
|
+
* words are literal, since only the tag can carry a {@link Reference}; a computed string
|
|
148
|
+
* cannot, because the tag's interpolations *are* the references. So a span is prose with
|
|
149
|
+
* zero references by construction, dedented by the same rule, and composes wherever a
|
|
150
|
+
* `` text`…` `` span does — a member's whole body or a {@link blocks} child.
|
|
151
|
+
*
|
|
152
|
+
* # Throws
|
|
153
|
+
* If `words` carries {@link MENTION_SLOT} or {@link INCLUDE_SLOT} ({@link refuseReservedMarkers}).
|
|
154
|
+
*/
|
|
155
|
+
export declare function span(words: string): Text;
|
|
139
156
|
/**
|
|
140
157
|
* Declare an include target — its bytes are pulled into the host's projection at emit,
|
|
141
158
|
* a dependency the lock fingerprints. `moduleUrl` is the declaring module's own
|
package/dist/src/prose.js
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prose —
|
|
2
|
+
* Prose — four 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, `
|
|
5
|
-
* prose spans with embedded-member values in
|
|
6
|
-
* the constructor, the words land byte-identical to their
|
|
4
|
+
* for short inline prose, `span()` for prose the program computes, `blocks()` for a
|
|
5
|
+
* composed body that interleaves verbatim prose spans with embedded-member values in
|
|
6
|
+
* authored order. Whatever the constructor, the words land byte-identical to their
|
|
7
|
+
* authored text. The two inline constructors build the same {@link Text} and differ
|
|
8
|
+
* only in where the words come from: reach for `` text`…` `` when they are literal, for
|
|
9
|
+
* `span()` when they are a string the program derived — the tag's interpolations are
|
|
10
|
+
* references, never words, so a computed string has no slot to ride in.
|
|
7
11
|
* Interpolations in `` text`…` `` are references, two intents apart: a **mention**
|
|
8
12
|
* (a {@link Mentionable}) is a declared one-way edge that moves no content, and an
|
|
9
13
|
* **include** (an {@link Include}) pulls the target file's bytes into the host's emitted
|
|
10
14
|
* projection. Both are authored per word and resolution-checked at emit, never mined.
|
|
11
15
|
*/
|
|
16
|
+
import { hostAddress, parseHostAddress } from "./member-address.js";
|
|
12
17
|
/**
|
|
13
18
|
* Spell a top-level member as the {@link Mentionable} a mention carries: its
|
|
14
|
-
*
|
|
15
|
-
*
|
|
19
|
+
* {@link hostAddress}, its bare name the display text — the convention every corpus
|
|
20
|
+
* repeats to cite a member from prose.
|
|
16
21
|
*/
|
|
17
22
|
export function mentionOf(member) {
|
|
18
|
-
return { address:
|
|
23
|
+
return { address: hostAddress(member.kind, member.name), display: member.name };
|
|
19
24
|
}
|
|
20
25
|
/**
|
|
21
26
|
* Whether a mention's unresolved address **defers to the gate** rather than refusing at
|
|
22
|
-
* emit: a
|
|
23
|
-
* discovery locus (an `at`-locus kind) may name a member discovered on disk, so
|
|
24
|
-
* owns the verdict.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
+
* emit: a host address ({@link parseHostAddress}) whose kind is one the program declares
|
|
28
|
+
* at a discovery locus (an `at`-locus kind) may name a member discovered on disk, so
|
|
29
|
+
* `check` owns the verdict. Anything the grammar's reader answers `undefined` for — a
|
|
30
|
+
* segmented embedded address, a bare requirement name, a name-less `kind:` — names no
|
|
31
|
+
* discoverable member, and stays a dangling refusal however deferrable its head reads.
|
|
27
32
|
*/
|
|
28
33
|
export function defersToGate(address, deferrableKinds) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const colon = address.indexOf(":");
|
|
32
|
-
return colon > 0 && deferrableKinds.has(address.slice(0, colon));
|
|
34
|
+
const host = parseHostAddress(address);
|
|
35
|
+
return host !== undefined && deferrableKinds.has(host.kind);
|
|
33
36
|
}
|
|
34
37
|
/**
|
|
35
38
|
* Refuse a mention whose address neither resolves against the scope's `mentionable`
|
|
@@ -66,6 +69,20 @@ const MENTION_SLOT = "\u0000";
|
|
|
66
69
|
* carries one, so the split is unambiguous.
|
|
67
70
|
*/
|
|
68
71
|
const INCLUDE_SLOT = "\u0001";
|
|
72
|
+
/**
|
|
73
|
+
* Refuse authored words carrying {@link MENTION_SLOT} or {@link INCLUDE_SLOT} — the
|
|
74
|
+
* markers are the tool's alone. A stray one would split into a chunk no reference
|
|
75
|
+
* claims, and {@link renderText} drops every chunk past the last mention, so the loud
|
|
76
|
+
* error is the only alternative to silently swallowing the words after it.
|
|
77
|
+
*
|
|
78
|
+
* # Throws
|
|
79
|
+
* If `words` carries either reserved marker.
|
|
80
|
+
*/
|
|
81
|
+
function refuseReservedMarkers(words) {
|
|
82
|
+
if (words.includes(MENTION_SLOT) || words.includes(INCLUDE_SLOT)) {
|
|
83
|
+
throw new Error("authored prose contains a reserved reference marker (U+0000/U+0001); remove it.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
69
86
|
/** Strip the common leading indentation a template literal picks up from its module. */
|
|
70
87
|
function dedent(text) {
|
|
71
88
|
const lines = text.split("\n");
|
|
@@ -103,9 +120,7 @@ export function text(strings, ...targets) {
|
|
|
103
120
|
const mentions = [];
|
|
104
121
|
const includes = [];
|
|
105
122
|
strings.forEach((chunk, i) => {
|
|
106
|
-
|
|
107
|
-
throw new Error("authored prose contains a reserved reference marker (U+0000/U+0001); remove it.");
|
|
108
|
-
}
|
|
123
|
+
refuseReservedMarkers(chunk);
|
|
109
124
|
if (i === 0)
|
|
110
125
|
return;
|
|
111
126
|
const target = targets[i - 1];
|
|
@@ -120,6 +135,22 @@ export function text(strings, ...targets) {
|
|
|
120
135
|
});
|
|
121
136
|
return { kind: "text", template: dedent(template), mentions, includes };
|
|
122
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* The computed-prose constructor: the same inline prose as `` text`…` ``, from a string
|
|
140
|
+
* the program built rather than a template the author typed — a rolled-up participants
|
|
141
|
+
* line, a table derived from the members. An author reaches for the tag whenever the
|
|
142
|
+
* words are literal, since only the tag can carry a {@link Reference}; a computed string
|
|
143
|
+
* cannot, because the tag's interpolations *are* the references. So a span is prose with
|
|
144
|
+
* zero references by construction, dedented by the same rule, and composes wherever a
|
|
145
|
+
* `` text`…` `` span does — a member's whole body or a {@link blocks} child.
|
|
146
|
+
*
|
|
147
|
+
* # Throws
|
|
148
|
+
* If `words` carries {@link MENTION_SLOT} or {@link INCLUDE_SLOT} ({@link refuseReservedMarkers}).
|
|
149
|
+
*/
|
|
150
|
+
export function span(words) {
|
|
151
|
+
refuseReservedMarkers(words);
|
|
152
|
+
return { kind: "text", template: dedent(words), mentions: [], includes: [] };
|
|
153
|
+
}
|
|
123
154
|
/**
|
|
124
155
|
* Declare an include target — its bytes are pulled into the host's projection at emit,
|
|
125
156
|
* a dependency the lock fingerprints. `moduleUrl` is the declaring module's own
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dtmd/temper",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "The temper authoring face — the six-noun model as typed modules: harness(), kind<T>(), clause values, needs, and file()/text/blocks(). Emit compiles to the declaration rows the engine reads, a byte-faithful projection, and the lock.",
|
|
5
5
|
"license": "(MIT OR Apache-2.0)",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"temper": "bin/temper.js"
|
|
53
53
|
},
|
|
54
54
|
"optionalDependencies": {
|
|
55
|
-
"@dtmd/temper-linux-x64": "0.0.
|
|
56
|
-
"@dtmd/temper-win32-x64": "0.0.
|
|
55
|
+
"@dtmd/temper-linux-x64": "0.0.19",
|
|
56
|
+
"@dtmd/temper-win32-x64": "0.0.19"
|
|
57
57
|
}
|
|
58
58
|
}
|