@orkestrel/scaffold 0.0.67 → 0.0.68
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/bin/main.js +67 -44
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/templates/brief.md +9 -0
- package/dist/host/claude/agents/orkestrel.md +4 -4
- package/dist/host/claude/rules/names.md +15 -0
- package/dist/host/claude/rules/tests.md +33 -4
- package/dist/host/claude/rules/workspace.md +14 -2
- package/dist/host/dotfiles/prettierignore +3 -0
- package/dist/host/guides/README.md +65 -0
- package/dist/host/guides/abort.md +169 -0
- package/dist/host/guides/agent.md +1509 -0
- package/dist/host/guides/brief.md +1266 -0
- package/dist/host/guides/browser.md +2200 -0
- package/dist/host/guides/budget.md +196 -0
- package/dist/host/guides/codec.md +519 -0
- package/dist/host/guides/console.md +785 -0
- package/dist/host/guides/contract.md +1193 -0
- package/dist/host/guides/csv.md +541 -0
- package/dist/host/guides/database.md +2518 -0
- package/dist/host/guides/emitter.md +233 -0
- package/dist/host/guides/form.md +1791 -0
- package/dist/host/guides/html.md +717 -0
- package/dist/host/guides/indexeddb.md +505 -0
- package/dist/host/guides/interpret.md +1029 -0
- package/dist/host/guides/lsp.md +515 -0
- package/dist/host/guides/markdown.md +964 -0
- package/dist/host/guides/mcp.md +5554 -0
- package/dist/host/guides/middleware.md +927 -0
- package/dist/host/guides/msg.md +440 -0
- package/dist/host/guides/ndjson.md +120 -0
- package/dist/host/guides/ollama.md +380 -0
- package/dist/host/guides/pool.md +280 -0
- package/dist/host/guides/probe.md +1210 -0
- package/dist/host/guides/process.md +1620 -0
- package/dist/host/guides/program.md +1110 -0
- package/dist/host/guides/qualifier.md +854 -0
- package/dist/host/guides/queue.md +370 -0
- package/dist/host/guides/rater.md +330 -0
- package/dist/host/guides/reason.md +1122 -0
- package/dist/host/guides/relation.md +373 -0
- package/dist/host/guides/router.md +753 -0
- package/dist/host/guides/scaffold.md +192 -31
- package/dist/host/guides/sea.md +383 -0
- package/dist/host/guides/server.md +752 -0
- package/dist/host/guides/sqlite.md +330 -0
- package/dist/host/guides/sse.md +187 -0
- package/dist/host/guides/supervisor.md +4890 -0
- package/dist/host/guides/table.md +1556 -0
- package/dist/host/guides/template.md +280 -0
- package/dist/host/guides/terminal.md +1145 -0
- package/dist/host/guides/test.md +2969 -0
- package/dist/host/guides/timeout.md +252 -0
- package/dist/host/guides/tool.md +311 -0
- package/dist/host/guides/toolbox.md +1038 -0
- package/dist/host/guides/websocket.md +282 -0
- package/dist/host/guides/worker.md +615 -0
- package/dist/host/guides/workflow.md +1507 -0
- package/dist/host/guides/workspace.md +595 -0
- package/dist/host/manifest.json +1218 -10
- package/dist/host/tests/policy.test.ts +279 -2
- package/dist/host/tests/setupPolicy.ts +437 -6
- package/dist/src/core/index.cjs +38 -16
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +33 -9
- package/dist/src/core/index.d.ts +33 -9
- package/dist/src/core/index.js +37 -17
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1750 -1567
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +106 -24
- package/dist/src/server/index.d.ts +106 -24
- package/dist/src/server/index.js +1751 -1570
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# Codec
|
|
2
|
+
|
|
3
|
+
> The fleet's byte-to-text codings: sound `encode` / `decode` / guard triples over `string` and
|
|
4
|
+
> `Uint8Array` for RFC 4648 Base64, base64url, and hex and for the UTF-8, ISO-8859-1,
|
|
5
|
+
> Windows-1252, and UTF-16LE charsets, beside a `measure*` that answers a coding's byte-side size
|
|
6
|
+
> question without producing those bytes.
|
|
7
|
+
|
|
8
|
+
A coding is a spec-named, stateless mapping with one canonical spelling per input, written as an
|
|
9
|
+
`encode*` that produces only the canonical form, a `decode*` that accepts exactly that form and
|
|
10
|
+
answers `undefined` for everything else, and an `is*` guard that names the exact set the partial
|
|
11
|
+
direction accepts. Every function is pure ES: no `atob` / `btoa`, no `Buffer`, no `TextEncoder` /
|
|
12
|
+
`TextDecoder`, no `node:*`, and no dependency on another `@orkestrel` package. Totality is
|
|
13
|
+
implemented rather than caught: codec ships no error type, no options bag, no class, and no type of
|
|
14
|
+
its own. It is not a formats package — it does not compress, frame a stream, escape a document, map
|
|
15
|
+
values into a store, or read JSON. Source: [`src/core`](../src/core), published through
|
|
16
|
+
`@orkestrel/codec` with no runtime dependency.
|
|
17
|
+
|
|
18
|
+
## The families
|
|
19
|
+
|
|
20
|
+
The families are fixed, and their direction belongs to the coding rather than to the package.
|
|
21
|
+
`encode*` moves a value toward the coding's wire form and `decode*` moves it back toward the native
|
|
22
|
+
one. For the RFC 4648 faces the wire form is text, so `encodeBase64` takes bytes and returns a
|
|
23
|
+
string. For a charset the wire form is bytes, so `encodeUTF8` takes a string and returns bytes.
|
|
24
|
+
Nothing else inverts with it: both faces keep the same laws, each written in the direction its
|
|
25
|
+
own `encode*` points.
|
|
26
|
+
|
|
27
|
+
Which side can fail belongs to the coding too. An RFC 4648 `encode*` cannot fail, so its `decode*`
|
|
28
|
+
carries every refusal. A charset can refuse on either side, and each one refuses where its
|
|
29
|
+
specification leaves a gap: `encodeUTF8` refuses ill-formed text, `decodeUTF8` refuses a
|
|
30
|
+
non-shortest spelling, `encodeLatin1` refuses a code unit past 0xFF, and `decodeLatin1` refuses
|
|
31
|
+
nothing at all.
|
|
32
|
+
|
|
33
|
+
`is*` takes an `unknown` and never throws. It attaches to its coding's **partial** direction,
|
|
34
|
+
because a guard names the set some function refuses and a total function has no set to name. The
|
|
35
|
+
RFC 4648 guards therefore sit on the text side and narrow to `string`. `isUTF8`, `isWindows1252`,
|
|
36
|
+
and `isUTF16LE` sit on the bytes side and narrow to `Uint8Array`, because those decoders can refuse.
|
|
37
|
+
`isLatin1` sits on the text side and narrows to `string`, because that coding's decoder is total and
|
|
38
|
+
its encoder is the only side with a set to name. UTF-8's text side ships no guard at all:
|
|
39
|
+
`text.isWellFormed()` is ECMA-262's own name for exactly the strings `encodeUTF8` accepts, so a
|
|
40
|
+
guard there would be a wrapper adding nothing.
|
|
41
|
+
|
|
42
|
+
`measure*` answers the coding's byte-side size question without doing the work that produces those
|
|
43
|
+
bytes, and `undefined` for a text the coding refuses — the name `@orkestrel/websocket` already
|
|
44
|
+
carries for `measureWebSocketFrame`, which reads a frame's declared payload length off the buffer
|
|
45
|
+
without buffering the payload. Which text a measure reads belongs to the coding, the same way the
|
|
46
|
+
encode direction does: an RFC 4648 face's wire form is text, so its measure takes wire text and
|
|
47
|
+
answers the byte length its decoder would allocate; UTF-8's wire form is bytes, so `measureUTF8`
|
|
48
|
+
takes native text and answers the wire byte length its encoder would write. Each row in the
|
|
49
|
+
following Measures table spells its own law.
|
|
50
|
+
|
|
51
|
+
A face's guard and its partial function are one grammar: the guard answers by asking that function,
|
|
52
|
+
so the set the guard names and the set the function accepts cannot drift apart. A measure is the one
|
|
53
|
+
family that cannot ask. Its reason to exist is that it never allocates the bytes, so it walks the
|
|
54
|
+
grammar itself and the suite holds its walk against the producing function's — a measure that
|
|
55
|
+
produces the bytes has measured nothing.
|
|
56
|
+
|
|
57
|
+
## Surface
|
|
58
|
+
|
|
59
|
+
### Codings
|
|
60
|
+
|
|
61
|
+
The RFC 4648 faces: the codings from [`helpers.ts`](../src/core/helpers.ts) and the guards from
|
|
62
|
+
[`validators.ts`](../src/core/validators.ts). `Base64` names the §4 coding, `Base64URL` the §5 one,
|
|
63
|
+
and `Hex` the §8 one; the alphabets and the reverse lookups behind them are module data, not public
|
|
64
|
+
API, because publishing an alphabet invites hand-rolling the coding it belongs to.
|
|
65
|
+
|
|
66
|
+
| Name | Kind | Signature | Summary |
|
|
67
|
+
| ----------------- | -------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `encodeBase64` | function | `(bytes: Uint8Array) => string` | Encodes a byte sequence as standard padded Base64, in the RFC 4648 §4 alphabet (`+`, `/`) with `=` padding. That spelling is the canonical form and the only form `decodeBase64` accepts. Total: encoding cannot fail. |
|
|
69
|
+
| `decodeBase64` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Decodes canonical standard Base64 text into its bytes, reading back exactly what `encodeBase64` writes. Every other text is `undefined`: a wrong alphabet, embedded whitespace, wrong padding, and a non-zero unused trailing bit alike. |
|
|
70
|
+
| `isBase64` | function | `(value: unknown) => value is string` | Checks whether a value is canonical standard Base64 text — true for exactly the strings `decodeBase64` answers bytes for. Total on any value: a number, `null`, or a byte sequence is false rather than a throw. |
|
|
71
|
+
| `encodeBase64URL` | function | `(bytes: Uint8Array) => string` | Encodes a byte sequence as unpadded base64url, in the RFC 4648 §5 url alphabet (`-`, `_`) with the padding removed. That spelling is the canonical form and the only form `decodeBase64URL` accepts. Total: encoding cannot fail. |
|
|
72
|
+
| `decodeBase64URL` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Decodes canonical base64url text into its bytes, reading back exactly what `encodeBase64URL` writes. A padded text, a `+`, and a `/` belong to the §4 face and are `undefined` here. |
|
|
73
|
+
| `isBase64URL` | function | `(value: unknown) => value is string` | Checks whether a value is canonical base64url text — true for exactly the strings `decodeBase64URL` answers bytes for. A padded text, a `+`, and a `/` belong to the §4 face and are false. Total on any value: a number, `null`, or a byte sequence is false rather than a throw. |
|
|
74
|
+
| `encodeHex` | function | `(bytes: Uint8Array) => string` | Encodes a byte sequence as lowercase hex, in the RFC 4648 §8 base16 alphabet, two digits per byte. That spelling is the canonical form and the only form `decodeHex` accepts. Total: encoding cannot fail. |
|
|
75
|
+
| `decodeHex` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Decodes canonical lowercase hex text into its bytes, reading back exactly what `encodeHex` writes. An uppercase digit, an odd length, a `0x` prefix, whitespace, and any character outside the alphabet are `undefined`. |
|
|
76
|
+
| `isHex` | function | `(value: unknown) => value is string` | Checks whether a value is canonical lowercase hex text — true for exactly the strings `decodeHex` answers bytes for. An uppercase digit, an odd length, and a `0x` prefix are false. Total on any value: a number, `null`, or a byte sequence is false rather than a throw. |
|
|
77
|
+
|
|
78
|
+
### Measures
|
|
79
|
+
|
|
80
|
+
The byte-side size a text carries, read off the text itself — from
|
|
81
|
+
[`helpers.ts`](../src/core/helpers.ts), each beside the coding it measures. Every RFC 4648 face here
|
|
82
|
+
has one: `measureBase64` reads the §4 face, `measureBase64URL` the §5 face, and `measureHex` the §8
|
|
83
|
+
face, each answering the byte length its own decoder would allocate. `measureUTF8` reads the charset
|
|
84
|
+
face, and it reads it the other way round: native text in, wire bytes counted. The remaining
|
|
85
|
+
charsets have none, for the reason the membership bar gives.
|
|
86
|
+
|
|
87
|
+
`computeBytes` in `@orkestrel/scaffold` counts UTF-8 bytes too, and it answers a different question
|
|
88
|
+
for a lone surrogate: the three bytes `TextEncoder` writes for the replacement character, where
|
|
89
|
+
`measureUTF8` answers `undefined`. That divergence is deliberate — it is the strict door this
|
|
90
|
+
package keeps on every face — and a consumer wanting the replacement count calls the counter that
|
|
91
|
+
produces it.
|
|
92
|
+
|
|
93
|
+
| Name | Kind | Signature | Summary |
|
|
94
|
+
| ------------------ | -------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `measureBase64` | function | `(text: string) => number \| undefined` | Measures the byte length canonical standard Base64 text decodes to, without allocating those bytes, and answers `undefined` for exactly the texts `decodeBase64` refuses. |
|
|
96
|
+
| `measureBase64URL` | function | `(text: string) => number \| undefined` | Measures the byte length canonical base64url text decodes to, without allocating those bytes, and answers `undefined` for exactly the texts `decodeBase64URL` refuses. |
|
|
97
|
+
| `measureHex` | function | `(text: string) => number \| undefined` | Measures the byte length canonical lowercase hex text decodes to, without allocating those bytes, and answers `undefined` for exactly the texts `decodeHex` refuses. |
|
|
98
|
+
| `measureUTF8` | function | `(text: string) => number \| undefined` | Measures the UTF-8 byte length text encodes to, without allocating those bytes, and answers `undefined` for exactly the ill-formed strings `encodeUTF8` refuses. |
|
|
99
|
+
|
|
100
|
+
### Charsets
|
|
101
|
+
|
|
102
|
+
The charset faces, whose wire form is bytes rather than text: the codings from
|
|
103
|
+
[`helpers.ts`](../src/core/helpers.ts) and the guards from
|
|
104
|
+
[`validators.ts`](../src/core/validators.ts). `UTF8` names the RFC 3629 coding, `Latin1` the
|
|
105
|
+
ISO/IEC 8859-1 one, `Windows1252` the code page, and `UTF16LE` the little-endian UTF-16 form.
|
|
106
|
+
`WINDOWS_1252_HIGH` — the written-out 0x80-0x9F table `encodeWindows1252` and `decodeWindows1252`
|
|
107
|
+
read — is module data rather than public API, for the reason the Base64 alphabets are.
|
|
108
|
+
|
|
109
|
+
| Name | Kind | Signature | Summary |
|
|
110
|
+
| ------------------- | -------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| `encodeUTF8` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Encodes text as UTF-8 bytes, in the RFC 3629 shortest form for every code point — the canonical spelling, and the only form `decodeUTF8` accepts. Ill-formed text is the one refusal: a lone surrogate has no UTF-8 spelling, so the answer is `undefined`. |
|
|
112
|
+
| `decodeUTF8` | function | `(bytes: Uint8Array) => string \| undefined` | Decodes UTF-8 bytes into their text, reading back exactly what `encodeUTF8` writes. An overlong spelling, an encoded surrogate, a code point past U+10FFFF, a truncated sequence, a stray continuation byte, and a lead byte outside the grammar are `undefined`. A leading BOM is kept as U+FEFF rather than stripped. |
|
|
113
|
+
| `isUTF8` | function | `(value: unknown) => value is Uint8Array` | Checks whether a value is bytes that decode as strict UTF-8 — true for exactly the byte sequences `decodeUTF8` answers text for. An overlong spelling, an encoded surrogate, and a truncated sequence are false. Total on any value: a string, `null`, a sibling view kind, or a proxy is false rather than a throw. |
|
|
114
|
+
| `encodeLatin1` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Encodes text as ISO/IEC 8859-1 bytes, writing each code unit as the byte of the same value, which is the whole of that coding. A code unit past 0xFF has no byte here, so a text carrying one is `undefined`. |
|
|
115
|
+
| `decodeLatin1` | function | `(bytes: Uint8Array) => string` | Decodes ISO/IEC 8859-1 bytes into their text, reading each byte as the code point of the same value. Total: every byte names a character, so there is no failure mode and no `undefined` return. |
|
|
116
|
+
| `isLatin1` | function | `(value: unknown) => value is string` | Checks whether a value is text ISO/IEC 8859-1 can encode — true for exactly the strings `encodeLatin1` answers bytes for. This coding's guard names the encode side because its decoder is total and refuses nothing there. Total on any value: a number, `null`, or a byte sequence is false rather than a throw. |
|
|
117
|
+
| `encodeWindows1252` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Encodes text as Windows-1252 bytes, inverting the mapping `decodeWindows1252` reads: the identity under U+0080 and across U+00A0-U+00FF, and the reverse of the high table between them. A character outside that image is `undefined`, every C1 control included. |
|
|
118
|
+
| `decodeWindows1252` | function | `(bytes: Uint8Array) => string \| undefined` | Decodes Windows-1252 bytes into their text, reading 0x00-0x7F and 0xA0-0xFF as the identity and 0x80-0x9F through the written-out high table. Bytes 0x81, 0x8D, 0x8F, 0x90, and 0x9D name no character in the code page and are `undefined`. |
|
|
119
|
+
| `isWindows1252` | function | `(value: unknown) => value is Uint8Array` | Checks whether a value is bytes that decode as Windows-1252 — true for exactly the byte sequences `decodeWindows1252` answers text for. Bytes 0x81, 0x8D, 0x8F, 0x90, and 0x9D are undefined slots of the code page and are false. Total on any value: a string, `null`, a sibling view kind, or a proxy is false rather than a throw. |
|
|
120
|
+
| `encodeUTF16LE` | function | `(text: string) => Uint8Array<ArrayBuffer> \| undefined` | Encodes text as little-endian UTF-16 bytes, writing each code unit as its low byte then its high byte, which is the whole coding. Ill-formed text is the one refusal: an unpaired surrogate is no UTF-16 sequence, so the answer is `undefined`. |
|
|
121
|
+
| `decodeUTF16LE` | function | `(bytes: Uint8Array) => string \| undefined` | Decodes little-endian UTF-16 bytes into their text, reading two bytes per code unit, low byte first. An odd length and an unpaired surrogate are `undefined`. A leading FF FE is kept as U+FEFF rather than stripped. |
|
|
122
|
+
| `isUTF16LE` | function | `(value: unknown) => value is Uint8Array` | Checks whether a value is bytes that decode as UTF-16LE — true for exactly the byte sequences `decodeUTF16LE` answers text for. An odd length and an unpaired surrogate are false. Total on any value: a string, `null`, a sibling view kind, or a proxy is false rather than a throw. |
|
|
123
|
+
|
|
124
|
+
## The laws
|
|
125
|
+
|
|
126
|
+
Each face keeps the round-trip and canonical-form laws, and the suite drives both as sweeps rather
|
|
127
|
+
than as spot vectors. Each law is written in the direction the face's own `encode*` points, so an
|
|
128
|
+
RFC 4648 face reads it over bytes and text where a charset reads it over text and bytes.
|
|
129
|
+
|
|
130
|
+
**The round-trip law.** `decode*(encode*(value))` deep-equals `value`, for every input the face
|
|
131
|
+
admits, the empty one included. On the §4 face that reads `decodeBase64(encodeBase64(bytes))`; on
|
|
132
|
+
the UTF-8 face it reads `decodeUTF8(encodeUTF8(text))`.
|
|
133
|
+
|
|
134
|
+
**The canonical-form law.** `encode*(decode*(wire))` returns `wire`, for every wire form the face's
|
|
135
|
+
guard admits. On the §4 face the wire form is text and the law reads
|
|
136
|
+
`encodeBase64(decodeBase64(text)) === text`; on the UTF-8 face it is bytes and the law reads
|
|
137
|
+
`encodeUTF8(decodeUTF8(bytes))` deep-equals `bytes`.
|
|
138
|
+
|
|
139
|
+
A measure keeps the sound-triple law, over every string rather than over the admitted ones alone,
|
|
140
|
+
and it is written in the direction that measure reads.
|
|
141
|
+
|
|
142
|
+
**The sound-triple law.** `measure*(text)` equals the length of the bytes the function producing
|
|
143
|
+
them would return, or `undefined` where that function refuses `text`. On the RFC 4648 faces the
|
|
144
|
+
producer is the decoder, so the law reads `measureBase64(text) === decodeBase64(text)?.length`; on
|
|
145
|
+
the UTF-8 face the producer is the encoder, so it reads
|
|
146
|
+
`measureUTF8(text) === encodeUTF8(text)?.length`. An admitted text pins the length; a refused text
|
|
147
|
+
pins `undefined` on both sides. The suite drives `measureBase64`, `measureBase64URL`, and
|
|
148
|
+
`measureHex` against their decoders across each face's sweep population, membership rows, measure
|
|
149
|
+
rows, octet-prefix encodings, and the mutant population, and drives `measureUTF8` against
|
|
150
|
+
`encodeUTF8` across the well-formed text population, the ill-formed rows, and every boundary code
|
|
151
|
+
point — so a divergence between a measure's walk and its producer's reddens on the texts those
|
|
152
|
+
populations reach.
|
|
153
|
+
|
|
154
|
+
The canonical-form law is the one that does the work. It says a decoder may accept only the
|
|
155
|
+
spelling its own encoder produces, which rules out every lenient door at once: the wrong alphabet
|
|
156
|
+
and embedded whitespace close for both faces. Missing or excess padding and a length off the
|
|
157
|
+
four-character group boundary are the §4 doors; §5 spells the same closure its own way — the
|
|
158
|
+
unpadded url alphabet, refusing `=`, `+`, and `/` outright, and refusing any `length % 4 === 1`
|
|
159
|
+
residue, which no amount of padding can complete. And a non-zero unused trailing bit closes last,
|
|
160
|
+
for both faces alike. That last refusal is the one consumers meet: `'aa=='` carries a set bit in
|
|
161
|
+
the sextet the padding discards, so `decodeBase64('aa==')` is `undefined` and `isBase64('aa==')` is
|
|
162
|
+
false. `'aQ=='` is the canonical spelling of the byte `'aa=='` was reaching for, and it decodes.
|
|
163
|
+
The url face refuses `'aa'` for the same reason, and admits `'aQ'`.
|
|
164
|
+
|
|
165
|
+
The §8 face has fewer doors to close and closes them the same way. Hex carries no padding and no
|
|
166
|
+
unused trailing bit, so an odd length and a character outside the alphabet are the whole refusal
|
|
167
|
+
set — and uppercase is one of those characters. RFC 4648 §8 prints its table uppercase; this
|
|
168
|
+
package's canonical spelling is lowercase. That is a deliberate departure, and the canonical-form
|
|
169
|
+
law is what forces a choice at all: one spelling per input, so the package picks the one the fleet
|
|
170
|
+
already produces — `bytesToHex` in `@orkestrel/scaffold`, the digest hex in `@orkestrel/mcp`, and
|
|
171
|
+
Node's own `digest('hex')`. So `decodeHex('AB')` is `undefined` and `isHex('AB')` is false, by the
|
|
172
|
+
argument that refuses `'aa=='`: `'AB'` re-encodes as `'ab'`, so admitting it would break the law.
|
|
173
|
+
`'ab'` is the spelling that decodes. `'0xab'` is `undefined` because the prefix is a notation
|
|
174
|
+
around the coding rather than part of it, and `'abc'` is `undefined` because a byte takes two
|
|
175
|
+
digits.
|
|
176
|
+
|
|
177
|
+
### The charset doors
|
|
178
|
+
|
|
179
|
+
A charset closes its doors in the direction its own specification leaves open, so the refusals do
|
|
180
|
+
not read alike across the charset faces.
|
|
181
|
+
|
|
182
|
+
UTF-8 closes on both sides. `encodeUTF8` refuses ill-formed text, which is exactly what
|
|
183
|
+
`String.prototype.isWellFormed` reports false for — a lone surrogate is a UTF-16 artifact with no
|
|
184
|
+
UTF-8 spelling, and inventing one is what the replacement character does. `decodeUTF8` refuses every
|
|
185
|
+
non-canonical byte spelling: an overlong such as `C0 80` or `E0 80 80`, an encoded surrogate such as
|
|
186
|
+
`ED A0 80`, a code point past U+10FFFF such as `F4 90 80 80`, a truncated sequence such as `E2 82`,
|
|
187
|
+
a continuation byte with no lead, and a lead byte the grammar has no width for.
|
|
188
|
+
|
|
189
|
+
ISO-8859-1 closes on one side only. Every byte names a character, so `decodeLatin1` is total and
|
|
190
|
+
returns a bare `string`; the only door is `encodeLatin1`, which refuses a code unit past 0xFF.
|
|
191
|
+
`isLatin1` guards that door because it is the only one there is.
|
|
192
|
+
|
|
193
|
+
Windows-1252 closes where the code page itself stops. Bytes 0x81, 0x8D, 0x8F, 0x90, and 0x9D are
|
|
194
|
+
undefined slots, so `decodeWindows1252` refuses them. The defined mapping is a bijection — no
|
|
195
|
+
character is reachable from two bytes — so `encodeWindows1252` is its exact inverse and refuses
|
|
196
|
+
every character outside the image, which includes all of U+0080-U+009F because no defined slot
|
|
197
|
+
reaches a C1 control.
|
|
198
|
+
|
|
199
|
+
UTF-16LE closes on what the wire form can get wrong: an odd length, which completes no code unit,
|
|
200
|
+
and a surrogate the byte stream leaves unpaired — a lead with nothing after it, a lead followed by
|
|
201
|
+
a BMP code unit, and a trail with no lead alike.
|
|
202
|
+
|
|
203
|
+
### The BOM stance
|
|
204
|
+
|
|
205
|
+
A byte order mark is data here, not a signal. `EF BB BF` decodes to U+FEFF on the UTF-8 face and
|
|
206
|
+
`FF FE` decodes to U+FEFF on the UTF-16LE face, in leading position exactly as anywhere else, and
|
|
207
|
+
each encodes back to those bytes. The round-trip law forces it: a decoder that dropped a leading BOM
|
|
208
|
+
would answer a text whose re-encoding is shorter than the bytes it was given, and no amount of
|
|
209
|
+
documentation makes that a round trip. A consumer that wants the mark removed removes it, the same
|
|
210
|
+
way a consumer wanting lenient Base64 normalizes first.
|
|
211
|
+
|
|
212
|
+
### Where this package parts from WHATWG
|
|
213
|
+
|
|
214
|
+
The platform's own codings are the obvious oracle for these faces, and they disagree with this
|
|
215
|
+
package in ways worth naming rather than discovering.
|
|
216
|
+
|
|
217
|
+
- **`TextDecoder('latin1')` is not ISO-8859-1.** The WHATWG `latin1` label is an alias for
|
|
218
|
+
windows-1252: the decoder reports `encoding === 'windows-1252'` and answers U+20AC for the byte
|
|
219
|
+
0x80. `decodeLatin1` answers U+0080, because ISO-8859-1 is the identity. The codings agree
|
|
220
|
+
outside 0x80-0x9F and part company across it.
|
|
221
|
+
- **The WHATWG windows-1252 index defines all 256 entries.** It maps each undefined slot to its own
|
|
222
|
+
C1 control, so `TextDecoder('windows-1252')` answers U+0081 for the byte 0x81 where
|
|
223
|
+
`decodeWindows1252` answers `undefined`. Setting `fatal: true` does not change that. The suite
|
|
224
|
+
therefore holds the oracle to the bytes it agrees on and pins those slots directly.
|
|
225
|
+
- **A fatal `TextDecoder` strips a leading BOM by default.** `ignoreBOM: true` is what keeps it, and
|
|
226
|
+
this package keeps it always. That is the same divergence stated as the BOM stance, seen from the
|
|
227
|
+
platform's side.
|
|
228
|
+
|
|
229
|
+
## Membership
|
|
230
|
+
|
|
231
|
+
A coding belongs here when it is:
|
|
232
|
+
|
|
233
|
+
- **stateless and spec-named** — a mapping between bytes and text fixed by a published
|
|
234
|
+
specification, carrying no configuration and no instance;
|
|
235
|
+
- **single-spelled** — exactly one canonical wire form per input;
|
|
236
|
+
- **guard-decidable** — membership in the accepted set is decidable from the value alone, so an
|
|
237
|
+
`is*` can name it;
|
|
238
|
+
- **both-lawed** — the round-trip law and the canonical-form law hold as written;
|
|
239
|
+
- **wanted** — a real consumer in the fleet needs it.
|
|
240
|
+
|
|
241
|
+
The charsets meet that bar in the inverted direction and nothing else changes. Each is fixed by a
|
|
242
|
+
published specification, carries no configuration, and spells one canonical byte sequence per text.
|
|
243
|
+
Latin-1 is the case worth reading twice: its decoder refuses nothing, so its guard names its encode
|
|
244
|
+
side, and the bar is met by the direction that has a set to name rather than by the one that does
|
|
245
|
+
not.
|
|
246
|
+
|
|
247
|
+
A measure belongs here when the coding it measures is already here, the sound-triple law holds as
|
|
248
|
+
written in that coding's own direction, and a consumer needs the size before the bytes. It names no
|
|
249
|
+
grammar of its own, so it ships beside its coding rather than as a face. `measureBase64`,
|
|
250
|
+
`measureBase64URL`, `measureHex`, and `measureUTF8` each meet that bar. UTF-8 is the charset that
|
|
251
|
+
meets it, because its width varies per code point and the walk deciding that width is work a
|
|
252
|
+
consumer sizing a buffer would otherwise repeat. The other charsets fail on the consumer needing the
|
|
253
|
+
size before the bytes rather than on the coding being here: `encodeLatin1` and `encodeWindows1252`
|
|
254
|
+
write one byte per code unit and `encodeUTF16LE` writes two, so `text.length` already answers the
|
|
255
|
+
question and a measure there would name no walk its encoder skips.
|
|
256
|
+
|
|
257
|
+
A transform that carries state between calls, that takes a parameter changing what it produces,
|
|
258
|
+
that reads a document grammar rather than a byte-to-text mapping, or that encodes a caller's policy
|
|
259
|
+
rather than a specification, is outside the bar. Leniency is a caller's policy in particular: a
|
|
260
|
+
consumer that must accept whitespace or unpadded §4 input normalizes its input and then calls the
|
|
261
|
+
strict decoder, so the leniency lives with the consumer that owns it rather than in every consumer
|
|
262
|
+
of this package.
|
|
263
|
+
|
|
264
|
+
## Declared non-goals
|
|
265
|
+
|
|
266
|
+
- **No lenient doors.** No whitespace stripping, no optional padding, no permissive alphabet, no BOM
|
|
267
|
+
discarding, no replacement character, and no option to relax any of it.
|
|
268
|
+
- **No error type.** A decoder reports failure as `undefined` and a guard reports it as `false`.
|
|
269
|
+
Nothing here throws, so there is nothing to catch and no code to branch on.
|
|
270
|
+
- **Never:** compression, stream framing, document escaping, value-to-store mapping, and JSON.
|
|
271
|
+
Those are other packages' work.
|
|
272
|
+
|
|
273
|
+
## Patterns
|
|
274
|
+
|
|
275
|
+
### Encode and decode a byte sequence
|
|
276
|
+
|
|
277
|
+
This fence builds the round trip through standard Base64, from bytes to text and back, including
|
|
278
|
+
the empty sequence.
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import { decodeBase64, encodeBase64 } from '@orkestrel/codec'
|
|
282
|
+
|
|
283
|
+
encodeBase64(new Uint8Array([104, 105])) // 'aGk='
|
|
284
|
+
decodeBase64('aGk=') // Uint8Array [104, 105]
|
|
285
|
+
encodeBase64(new Uint8Array([])) // ''
|
|
286
|
+
decodeBase64('') // Uint8Array []
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Reach the url face
|
|
290
|
+
|
|
291
|
+
This fence builds the base64url encoding beside the padded spellings it refuses.
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import { decodeBase64URL, encodeBase64URL } from '@orkestrel/codec'
|
|
295
|
+
|
|
296
|
+
encodeBase64URL(new Uint8Array([104, 105])) // 'aGk' — §5 carries no padding
|
|
297
|
+
encodeBase64URL(new Uint8Array([0xfb, 0xff, 0xbf])) // '-_-_'
|
|
298
|
+
decodeBase64URL('-_-_') // Uint8Array [251, 255, 191]
|
|
299
|
+
decodeBase64URL('aGk=') // undefined — padding belongs to §4
|
|
300
|
+
decodeBase64URL('+/+/') // undefined — those characters belong to §4
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Meet the canonical refusals
|
|
304
|
+
|
|
305
|
+
This fence builds the exact texts standard Base64 refuses, beside the canonical spelling each one
|
|
306
|
+
was reaching for.
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import { decodeBase64, encodeBase64, isBase64 } from '@orkestrel/codec'
|
|
310
|
+
|
|
311
|
+
decodeBase64('aa==') // undefined — the unused trailing bits are not zero
|
|
312
|
+
decodeBase64('aQ==') // Uint8Array [105] — the canonical spelling of that byte
|
|
313
|
+
encodeBase64(new Uint8Array([105])) // 'aQ=='
|
|
314
|
+
decodeBase64('AQ D') // undefined — whitespace
|
|
315
|
+
decodeBase64('A') // undefined — a length off the group boundary
|
|
316
|
+
decodeBase64('AQID=') // undefined — padding off the group boundary
|
|
317
|
+
decodeBase64('-_-_') // undefined — the url alphabet
|
|
318
|
+
isBase64('aa==') // false
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Ask a value whether a decoder would take it
|
|
322
|
+
|
|
323
|
+
This fence builds the guard calls that answer whether a value belongs to each Base64 face, without
|
|
324
|
+
decoding it.
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import { isBase64, isBase64URL } from '@orkestrel/codec'
|
|
328
|
+
|
|
329
|
+
isBase64('aGk=') // true
|
|
330
|
+
isBase64('aGk') // false — §4 requires the padding
|
|
331
|
+
isBase64URL('aGk') // true
|
|
332
|
+
isBase64URL('aGk=') // false
|
|
333
|
+
isBase64URL(42) // false — total on any value, never a throw
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Drive the round-trip and canonical-form laws
|
|
337
|
+
|
|
338
|
+
This fence builds both laws on one buffer: decoding an encoding back to the original bytes, then
|
|
339
|
+
re-encoding the decoded text to itself.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import { decodeBase64, encodeBase64, isBase64 } from '@orkestrel/codec'
|
|
343
|
+
|
|
344
|
+
const bytes = new Uint8Array([0, 1, 2, 253, 254, 255])
|
|
345
|
+
const text = encodeBase64(bytes) // 'AAEC/f7/'
|
|
346
|
+
|
|
347
|
+
// The round-trip law: decoding an encoding returns the bytes.
|
|
348
|
+
decodeBase64(text) // deep-equals bytes
|
|
349
|
+
|
|
350
|
+
// The canonical-form law: re-encoding an admitted text returns the text.
|
|
351
|
+
isBase64(text) // true
|
|
352
|
+
const decoded = decodeBase64(text)
|
|
353
|
+
if (decoded !== undefined) encodeBase64(decoded) // === text
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Read the hex face
|
|
357
|
+
|
|
358
|
+
This fence builds the lowercase hex round trip beside the uppercase and prefixed spellings it
|
|
359
|
+
refuses.
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { decodeHex, encodeHex, isHex } from '@orkestrel/codec'
|
|
363
|
+
|
|
364
|
+
encodeHex(new Uint8Array([0xab])) // 'ab'
|
|
365
|
+
decodeHex('ab') // Uint8Array [171]
|
|
366
|
+
decodeHex('AB') // undefined — this package spells the §8 alphabet lowercase
|
|
367
|
+
decodeHex('0xab') // undefined — the prefix is notation around the coding
|
|
368
|
+
decodeHex('abc') // undefined — a byte takes two digits
|
|
369
|
+
isHex('ab') // true
|
|
370
|
+
isHex('AB') // false
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Encode and decode through a charset
|
|
374
|
+
|
|
375
|
+
This fence builds each charset's own round trip and its refusals, from UTF-8 through Windows-1252
|
|
376
|
+
to UTF-16LE.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
import {
|
|
380
|
+
decodeLatin1,
|
|
381
|
+
decodeUTF8,
|
|
382
|
+
decodeUTF16LE,
|
|
383
|
+
decodeWindows1252,
|
|
384
|
+
encodeLatin1,
|
|
385
|
+
encodeUTF8,
|
|
386
|
+
encodeUTF16LE,
|
|
387
|
+
encodeWindows1252,
|
|
388
|
+
} from '@orkestrel/codec'
|
|
389
|
+
|
|
390
|
+
// UTF-8 closes on both sides: ill-formed text going out, a non-shortest spelling coming back.
|
|
391
|
+
encodeUTF8('hi') // Uint8Array [104, 105]
|
|
392
|
+
decodeUTF8(new Uint8Array([104, 105])) // 'hi'
|
|
393
|
+
encodeUTF8('\ud800') // undefined — a lone surrogate has no UTF-8 spelling
|
|
394
|
+
decodeUTF8(new Uint8Array([0xc0, 0x80])) // undefined — the overlong spelling of U+0000
|
|
395
|
+
decodeUTF8(new Uint8Array([0xef, 0xbb, 0xbf])) // '\ufeff' — the BOM is data, not a signal
|
|
396
|
+
|
|
397
|
+
// ISO-8859-1 is the identity on a byte, so only its encode side can refuse.
|
|
398
|
+
encodeLatin1('é') // Uint8Array [233]
|
|
399
|
+
decodeLatin1(new Uint8Array([0x80])) // '\u0080' — not the euro sign the latin1 label answers
|
|
400
|
+
encodeLatin1('Ā') // undefined — a code unit past 0xFF
|
|
401
|
+
|
|
402
|
+
// Windows-1252 closes where the code page itself stops.
|
|
403
|
+
encodeWindows1252('€') // Uint8Array [128]
|
|
404
|
+
decodeWindows1252(new Uint8Array([0x80])) // '€'
|
|
405
|
+
decodeWindows1252(new Uint8Array([0x81])) // undefined — an undefined code-page slot
|
|
406
|
+
|
|
407
|
+
// UTF-16LE closes on an odd length and on a surrogate the byte stream leaves unpaired.
|
|
408
|
+
encodeUTF16LE('hi') // Uint8Array [104, 0, 105, 0]
|
|
409
|
+
decodeUTF16LE(new Uint8Array([0x68])) // undefined — an odd length completes no code unit
|
|
410
|
+
decodeUTF16LE(new Uint8Array([0x00, 0xd8])) // undefined — a lead surrogate with nothing after it
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Measure without producing the bytes
|
|
414
|
+
|
|
415
|
+
This fence builds each measure's byte-length answer beside the same texts its decoder or encoder
|
|
416
|
+
would refuse.
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
import { measureBase64, measureBase64URL, measureHex, measureUTF8 } from '@orkestrel/codec'
|
|
420
|
+
|
|
421
|
+
// An RFC 4648 measure reads wire text and answers the byte length its decoder would allocate.
|
|
422
|
+
measureBase64('aGk=') // 2
|
|
423
|
+
measureBase64('aa==') // undefined — the same texts decodeBase64 refuses
|
|
424
|
+
measureBase64URL('aGk') // 2
|
|
425
|
+
measureBase64URL('aGk=') // undefined — padding belongs to §4
|
|
426
|
+
measureHex('abcd') // 2
|
|
427
|
+
measureHex('AB') // undefined — uppercase re-encodes as 'ab'
|
|
428
|
+
|
|
429
|
+
// UTF-8 inverts the direction: native text in, wire bytes counted.
|
|
430
|
+
measureUTF8('hi') // 2
|
|
431
|
+
measureUTF8('é') // 2
|
|
432
|
+
measureUTF8('€') // 3
|
|
433
|
+
measureUTF8('\u{10000}') // 4
|
|
434
|
+
measureUTF8('\ud800') // undefined — ill-formed text has no UTF-8 spelling
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Tests
|
|
438
|
+
|
|
439
|
+
- [`tests/src/core/helpers.test.ts`](../tests/src/core/helpers.test.ts) — every law as a sweep. The
|
|
440
|
+
round-trip law runs the whole octet space in one buffer, every padding residue, every single
|
|
441
|
+
byte, and every byte pair on the §4 and §5 faces; the §8 face runs the same one-buffer sweep and
|
|
442
|
+
residue prefixes beside its empty, single-byte, and byte-pair walks. The canonical-form law runs an
|
|
443
|
+
exhaustive walk over short texts spanning the Base64 alphabets and another walk over short hex
|
|
444
|
+
texts carrying uppercase and foreign characters, re-encoding every admitted text to itself. The
|
|
445
|
+
sound-triple law runs `measureBase64` against `decodeBase64` and `measureBase64URL` against
|
|
446
|
+
`decodeBase64URL` over the Base64 sweep population and the written-out Base64 membership and
|
|
447
|
+
measure rows, runs `measureHex` against `decodeHex` over the hex sweep population and the
|
|
448
|
+
written-out hex rows, holds every measure against every canonical encoding of an octet prefix, and
|
|
449
|
+
drives each RFC 4648 measure over a deterministic mutant population — canonical encodings of octet
|
|
450
|
+
prefixes up to 24 bytes, each carried under one substitution, insertion, or truncation from a
|
|
451
|
+
written-out xorshift over a constant seed, so a refusal lands deep inside a text whose prefix is
|
|
452
|
+
admissible where the four-character sweeps cannot reach. `measureUTF8` runs against `encodeUTF8`
|
|
453
|
+
instead, over the well-formed text population, the ill-formed rows, every boundary code point, and
|
|
454
|
+
its own written-out rows. A case reads what the mutants actually reach on each face, so a
|
|
455
|
+
population that admitted everything would fail rather than pass quietly. Beside the sweeps sit the
|
|
456
|
+
named vectors, the named measures on each face, the canonical refusals, the Base64 alphabets read
|
|
457
|
+
against the specification in both directions, and the hex alphabet read against the language's own
|
|
458
|
+
radix conversion in both directions.
|
|
459
|
+
|
|
460
|
+
The charset faces run the round-trip and canonical-form laws in their own direction. The
|
|
461
|
+
round-trip law walks a well-formed text population built from characters spanning every UTF-8
|
|
462
|
+
width threshold, the BOM, the Latin-1 ceiling, the Windows-1252 bands, and the code points on
|
|
463
|
+
either side of the surrogate range, and pins each width threshold at the byte length the
|
|
464
|
+
specification fixes. The canonical-form law walks the exhaustive two-byte space on every charset
|
|
465
|
+
face, re-encoding every admitted pair to itself, and reads the Windows-1252 defined mapping as a
|
|
466
|
+
bijection and the Latin-1 mapping as the identity bijection. Beside the sweeps sit the written-out
|
|
467
|
+
refusal rows — the overlongs, the encoded surrogates, the out-of-range and truncated sequences,
|
|
468
|
+
the undefined code-page slots, the odd length, and the unpaired surrogates — each pinned against
|
|
469
|
+
both its decoder and its guard.
|
|
470
|
+
|
|
471
|
+
The platform's own codings run as oracles over the populations they agree on, each one probed
|
|
472
|
+
before a sweep was written to it. `decodeUTF8` and `decodeUTF16LE` are held against a fatal
|
|
473
|
+
`TextDecoder` carrying `ignoreBOM` over the whole two-byte space, `encodeUTF8` against
|
|
474
|
+
`TextEncoder` over the well-formed texts, and `decodeWindows1252` against the WHATWG index over
|
|
475
|
+
the bytes that index and this coding both define. `decodeLatin1` is held against
|
|
476
|
+
`String.fromCharCode` rather than against the `latin1` label, and an assertion pins the label's
|
|
477
|
+
disagreement to the 0x80-0x9F band so a later reader cannot quietly adopt it as the oracle.
|
|
478
|
+
|
|
479
|
+
Further sweeps reach the multi-byte defects a two-byte space cannot present. The embedded
|
|
480
|
+
sweep wraps every byte pair as `41 b1 b2 41`, which moves each pair into the middle of a buffer
|
|
481
|
+
the decoder has already started walking. The mutation sweep takes the canonical encoding of each
|
|
482
|
+
boundary code point and substitutes every byte position through all 256 values, which reaches the
|
|
483
|
+
overlong spellings of a four-byte code point and the encoded surrogates that sit one lead byte
|
|
484
|
+
from a canonical U+D7FF. Each runs against the same fatal `TextDecoder`.
|
|
485
|
+
|
|
486
|
+
The Windows-1252 high table carries another reading that is not a platform decoder at all. The
|
|
487
|
+
WHATWG index defines all 256 slots, so it is silent on exactly the omissions that make this code
|
|
488
|
+
page what it is; a hand transcription of the published table in `tests/setup.ts`, carrying the
|
|
489
|
+
characters rather than their code points, is compared against the source table entry by entry and
|
|
490
|
+
in both key directions. That is what `RFC_STANDARD` does for the Base64 alphabets, and it closes
|
|
491
|
+
the same shared-error class here.
|
|
492
|
+
|
|
493
|
+
- [`tests/src/core/validators.test.ts`](../tests/src/core/validators.test.ts) — the guard-family
|
|
494
|
+
proofs: the iff law binding each RFC 4648 guard to its decoder over the written-out membership
|
|
495
|
+
rows, the iff law binding `isHex` to `decodeHex` over the hex membership rows, the iff law binding
|
|
496
|
+
`isUTF8`, `isWindows1252`, and `isUTF16LE` to their decoders over the exhaustive two-byte space and
|
|
497
|
+
`isLatin1` to `encodeLatin1` over the well-formed text population, the totality of the total
|
|
498
|
+
Latin-1 decoder and of the guardless UTF-8 text side, and guard totality against foreign values,
|
|
499
|
+
hostile values, and a byte sequence sharing a buffer.
|
|
500
|
+
- [`tests/policy.test.ts`](../tests/policy.test.ts) — repository coding law: source placement,
|
|
501
|
+
exports, and syntax.
|
|
502
|
+
- [`tests/config.test.ts`](../tests/config.test.ts) — the root configuration's aliases, projects,
|
|
503
|
+
outputs, and the gate each proof runs from.
|
|
504
|
+
- [`tests/guides.test.ts`](../tests/guides.test.ts) — this guide against the real surface, in both
|
|
505
|
+
directions, plus the transcribed fences and the equality gate: every `Summary` cell against its
|
|
506
|
+
declaration's description paragraph, the titled `Encode and decode a byte sequence` fence against
|
|
507
|
+
the `@example` block of that title (pinned so the titled pair cannot be retired silently), and the
|
|
508
|
+
README pitch against this guide's tagline.
|
|
509
|
+
- [`tests/distribution.test.ts`](../tests/distribution.test.ts) — the packed package installs and
|
|
510
|
+
resolves through its public exports.
|
|
511
|
+
|
|
512
|
+
## See also
|
|
513
|
+
|
|
514
|
+
- [`AGENTS.md`](../AGENTS.md) — the repository rules this package is written to.
|
|
515
|
+
- [`guide.md`](guide.md) — the mirrored guide for `@orkestrel/guide`, the devDependency powering the
|
|
516
|
+
guides-parity suite.
|
|
517
|
+
- [`scaffold.md`](scaffold.md) — the mirrored guide for `@orkestrel/scaffold`, the devDependency
|
|
518
|
+
that generated this workspace.
|
|
519
|
+
- [`README.md`](README.md) — the guides index.
|