@johnhenry/sitefile 0.0.0-stage → 0.0.0

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 (174) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +475 -2
  4. package/lib/src/archive-writers.d.ts +31 -0
  5. package/lib/src/archive-writers.js +197 -0
  6. package/lib/src/attest.d.ts +126 -0
  7. package/lib/src/attest.js +220 -0
  8. package/lib/src/bake.d.ts +74 -0
  9. package/lib/src/bake.js +372 -0
  10. package/lib/src/base-image.d.ts +76 -0
  11. package/lib/src/base-image.js +138 -0
  12. package/lib/src/build-args.d.ts +26 -0
  13. package/lib/src/build-args.js +164 -0
  14. package/lib/src/cache-limit.d.ts +54 -0
  15. package/lib/src/cache-limit.js +239 -0
  16. package/lib/src/casefold-data.d.ts +2 -0
  17. package/lib/src/casefold-data.js +217 -0
  18. package/lib/src/cbor.d.ts +39 -0
  19. package/lib/src/cbor.js +174 -0
  20. package/lib/src/checklinks.d.ts +42 -0
  21. package/lib/src/checklinks.js +118 -0
  22. package/lib/src/cli/bake.d.ts +2 -0
  23. package/lib/src/cli/bake.js +179 -0
  24. package/lib/src/cli/build-image.d.ts +21 -0
  25. package/lib/src/cli/build-image.js +42 -0
  26. package/lib/src/cli/cache.d.ts +8 -0
  27. package/lib/src/cli/cache.js +109 -0
  28. package/lib/src/cli/common.d.ts +29 -0
  29. package/lib/src/cli/common.js +100 -0
  30. package/lib/src/cli/git-context.d.ts +18 -0
  31. package/lib/src/cli/git-context.js +48 -0
  32. package/lib/src/cli/images.d.ts +6 -0
  33. package/lib/src/cli/images.js +285 -0
  34. package/lib/src/cli/index.d.ts +10 -0
  35. package/lib/src/cli/index.js +95 -0
  36. package/lib/src/cli/outdated.d.ts +5 -0
  37. package/lib/src/cli/outdated.js +94 -0
  38. package/lib/src/cli/registry.d.ts +2 -0
  39. package/lib/src/cli/registry.js +122 -0
  40. package/lib/src/cli/serve-worker.d.ts +2 -0
  41. package/lib/src/cli/serve-worker.js +42 -0
  42. package/lib/src/cli/servers.d.ts +15 -0
  43. package/lib/src/cli/servers.js +451 -0
  44. package/lib/src/cli/vendor.d.ts +2 -0
  45. package/lib/src/cli/vendor.js +92 -0
  46. package/lib/src/cli/watch.d.ts +26 -0
  47. package/lib/src/cli/watch.js +81 -0
  48. package/lib/src/cli.d.ts +12 -0
  49. package/lib/src/cli.js +486 -0
  50. package/lib/src/csv.d.ts +27 -0
  51. package/lib/src/csv.js +117 -0
  52. package/lib/src/deflate.d.ts +76 -0
  53. package/lib/src/deflate.js +604 -0
  54. package/lib/src/digest.d.ts +3 -0
  55. package/lib/src/digest.js +7 -0
  56. package/lib/src/errors.d.ts +34 -0
  57. package/lib/src/errors.js +20 -0
  58. package/lib/src/executor.d.ts +386 -0
  59. package/lib/src/executor.js +2193 -0
  60. package/lib/src/explain.d.ts +64 -0
  61. package/lib/src/explain.js +445 -0
  62. package/lib/src/export-formats.d.ts +16 -0
  63. package/lib/src/export-formats.js +52 -0
  64. package/lib/src/exporter.d.ts +103 -0
  65. package/lib/src/exporter.js +676 -0
  66. package/lib/src/fingerprint.d.ts +40 -0
  67. package/lib/src/fingerprint.js +168 -0
  68. package/lib/src/frontmatter.d.ts +22 -0
  69. package/lib/src/frontmatter.js +45 -0
  70. package/lib/src/index.d.ts +12 -0
  71. package/lib/src/index.js +12 -0
  72. package/lib/src/language/ast.d.ts +353 -0
  73. package/lib/src/language/ast.js +57 -0
  74. package/lib/src/language/parser.d.ts +3 -0
  75. package/lib/src/language/parser.js +612 -0
  76. package/lib/src/language/scanner.d.ts +110 -0
  77. package/lib/src/language/scanner.js +449 -0
  78. package/lib/src/language/validate.d.ts +13 -0
  79. package/lib/src/language/validate.js +367 -0
  80. package/lib/src/links.d.ts +79 -0
  81. package/lib/src/links.js +583 -0
  82. package/lib/src/lint.d.ts +10 -0
  83. package/lib/src/lint.js +223 -0
  84. package/lib/src/markdown.d.ts +29 -0
  85. package/lib/src/markdown.js +612 -0
  86. package/lib/src/media.d.ts +82 -0
  87. package/lib/src/media.js +650 -0
  88. package/lib/src/memo.d.ts +57 -0
  89. package/lib/src/memo.js +93 -0
  90. package/lib/src/oci.d.ts +178 -0
  91. package/lib/src/oci.js +380 -0
  92. package/lib/src/outdated.d.ts +46 -0
  93. package/lib/src/outdated.js +195 -0
  94. package/lib/src/pages.d.ts +10 -0
  95. package/lib/src/pages.js +54 -0
  96. package/lib/src/paths.d.ts +39 -0
  97. package/lib/src/paths.js +101 -0
  98. package/lib/src/planner.d.ts +297 -0
  99. package/lib/src/planner.js +410 -0
  100. package/lib/src/plugins/cache.d.ts +70 -0
  101. package/lib/src/plugins/cache.js +226 -0
  102. package/lib/src/plugins/exec.d.ts +51 -0
  103. package/lib/src/plugins/exec.js +144 -0
  104. package/lib/src/plugins/host.d.ts +75 -0
  105. package/lib/src/plugins/host.js +277 -0
  106. package/lib/src/plugins/wasm.d.ts +23 -0
  107. package/lib/src/plugins/wasm.js +249 -0
  108. package/lib/src/profile.d.ts +42 -0
  109. package/lib/src/profile.js +68 -0
  110. package/lib/src/ref.d.ts +40 -0
  111. package/lib/src/ref.js +79 -0
  112. package/lib/src/registry-auth.d.ts +32 -0
  113. package/lib/src/registry-auth.js +151 -0
  114. package/lib/src/registry-cache.d.ts +71 -0
  115. package/lib/src/registry-cache.js +333 -0
  116. package/lib/src/registry-ops.d.ts +51 -0
  117. package/lib/src/registry-ops.js +248 -0
  118. package/lib/src/registry.d.ts +109 -0
  119. package/lib/src/registry.js +505 -0
  120. package/lib/src/render.d.ts +134 -0
  121. package/lib/src/render.js +576 -0
  122. package/lib/src/resources/archive.d.ts +11 -0
  123. package/lib/src/resources/archive.js +306 -0
  124. package/lib/src/resources/bundle-data.d.ts +16 -0
  125. package/lib/src/resources/bundle-data.js +50 -0
  126. package/lib/src/resources/convert.d.ts +12 -0
  127. package/lib/src/resources/convert.js +63 -0
  128. package/lib/src/resources/documents.d.ts +35 -0
  129. package/lib/src/resources/documents.js +122 -0
  130. package/lib/src/resources/git.d.ts +103 -0
  131. package/lib/src/resources/git.js +720 -0
  132. package/lib/src/resources/https.d.ts +40 -0
  133. package/lib/src/resources/https.js +256 -0
  134. package/lib/src/resources/ignore.d.ts +33 -0
  135. package/lib/src/resources/ignore.js +119 -0
  136. package/lib/src/resources/local.d.ts +64 -0
  137. package/lib/src/resources/local.js +248 -0
  138. package/lib/src/resources/lockfile.d.ts +52 -0
  139. package/lib/src/resources/lockfile.js +172 -0
  140. package/lib/src/resources/netpolicy.d.ts +6 -0
  141. package/lib/src/resources/netpolicy.js +82 -0
  142. package/lib/src/resources/types.d.ts +63 -0
  143. package/lib/src/resources/types.js +13 -0
  144. package/lib/src/resources/vendor.d.ts +94 -0
  145. package/lib/src/resources/vendor.js +296 -0
  146. package/lib/src/resources/wbn-reader.d.ts +44 -0
  147. package/lib/src/resources/wbn-reader.js +302 -0
  148. package/lib/src/schema.d.ts +25 -0
  149. package/lib/src/schema.js +169 -0
  150. package/lib/src/secrets.d.ts +21 -0
  151. package/lib/src/secrets.js +103 -0
  152. package/lib/src/serve-rules.d.ts +29 -0
  153. package/lib/src/serve-rules.js +122 -0
  154. package/lib/src/server.d.ts +45 -0
  155. package/lib/src/server.js +246 -0
  156. package/lib/src/servers.d.ts +71 -0
  157. package/lib/src/servers.js +139 -0
  158. package/lib/src/site.d.ts +60 -0
  159. package/lib/src/site.js +169 -0
  160. package/lib/src/store.d.ts +151 -0
  161. package/lib/src/store.js +596 -0
  162. package/lib/src/tree.d.ts +50 -0
  163. package/lib/src/tree.js +134 -0
  164. package/lib/src/values.d.ts +7 -0
  165. package/lib/src/values.js +37 -0
  166. package/lib/src/vendor.d.ts +19 -0
  167. package/lib/src/vendor.js +145 -0
  168. package/lib/src/watch.d.ts +44 -0
  169. package/lib/src/watch.js +137 -0
  170. package/lib/src/wbn.d.ts +5 -0
  171. package/lib/src/wbn.js +147 -0
  172. package/lib/src/yaml.d.ts +34 -0
  173. package/lib/src/yaml.js +562 -0
  174. package/package.json +58 -4
@@ -0,0 +1,562 @@
1
+ import { SitefileError } from './errors.js';
2
+ /**
3
+ * A small, safe YAML subset parser (no dependencies, no code execution).
4
+ *
5
+ * Supported:
6
+ * - Block style by indentation (spaces only; a tab in indentation is an error).
7
+ * - Mappings `key: value`, nested mappings, sequences `- item`, sequences of
8
+ * mappings (`- name: a` / ` url: /a`), mappings of sequences (including
9
+ * the indentless form), arbitrary nesting up to 64 levels.
10
+ * - Scalars: "double" quoted (escapes \" \\ \n \t), 'single' quoted ('' escape),
11
+ * plain strings (trimmed; ` #` begins a comment), `true`/`false`, integers
12
+ * and decimals (numbers), `null` / `~` / empty (null). Dates such as
13
+ * 2026-01-02 stay strings.
14
+ * - Flow collections on a single line: `[a, b, {x: 1}]`, `{a: 1, b: [2, 3]}`.
15
+ * - Block scalars `|` (literal) and `>` (folded) with optional `-` / `+`
16
+ * chomping; the indentation comes from the first content line.
17
+ * - Comments, blank lines, an optional leading `---` and a trailing `...`.
18
+ * - Keys are plain or quoted scalars and are always strings. Duplicate keys
19
+ * are an error. A `__proto__` key is stored as ordinary own data.
20
+ * - An empty document is `null`.
21
+ *
22
+ * Not supported (E_DATA at the offending line and column): anchors and aliases
23
+ * (`&` `*`), tags (`!`), multiple documents, complex keys (`? `), merge keys
24
+ * (`<<`), multi-line flow collections, multi-line plain or quoted scalars,
25
+ * block scalar indentation indicators, tabs for indentation.
26
+ *
27
+ * `firstLine` is the 1-based file line of the first line of `text`, so errors
28
+ * point at the right place when the YAML is embedded in another file.
29
+ */
30
+ export function parseYaml(text, file, firstLine = 1) {
31
+ return parseYamlDocument(text, file, firstLine, false);
32
+ }
33
+ const MAX_DEPTH = 64;
34
+ const INT_RE = /^-?\d+$/;
35
+ const DECIMAL_RE = /^-?\d+\.\d+$/;
36
+ /**
37
+ * Like parseYaml; with `rootMapping` the document must be a mapping (or empty)
38
+ * and indented content at the top is rejected. Used by frontmatter.
39
+ */
40
+ export function parseYamlDocument(text, file, firstLine, rootMapping) {
41
+ const src = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
42
+ const base = firstLine - 1;
43
+ const lines = src.split('\n').map((r, i) => {
44
+ const raw = r.replace(/\r$/, '');
45
+ let indent = 0;
46
+ while (raw[indent] === ' ')
47
+ indent++;
48
+ return { no: i + 1, raw, indent, text: raw.slice(indent), parent: -1 };
49
+ });
50
+ const failAt = (line, detail, column) => {
51
+ throw new SitefileError('E_DATA', detail, { file, line: line.no + base, column });
52
+ };
53
+ // Document markers.
54
+ let started = false;
55
+ let sawStart = false;
56
+ let end = lines.length;
57
+ for (let i = 0; i < lines.length; i++) {
58
+ const l = lines[i];
59
+ if (l.indent === 0) {
60
+ const t = l.raw.trimEnd();
61
+ if (t === '---' || l.raw.startsWith('--- ')) {
62
+ if (t !== '---')
63
+ failAt(l, 'content on the document marker line is not supported', 5);
64
+ if (started || sawStart)
65
+ failAt(l, 'multiple documents are not supported', 1);
66
+ sawStart = true;
67
+ l.text = '';
68
+ continue;
69
+ }
70
+ if (t === '...') {
71
+ for (let j = i + 1; j < lines.length; j++) {
72
+ const rest = lines[j].text.trim();
73
+ if (rest !== '' && !rest.startsWith('#'))
74
+ failAt(lines[j], 'multiple documents are not supported', 1);
75
+ }
76
+ end = i;
77
+ break;
78
+ }
79
+ }
80
+ const tt = l.text.trim();
81
+ if (!started && tt !== '' && !tt.startsWith('#'))
82
+ started = true;
83
+ }
84
+ lines.length = end;
85
+ const p = new Parser(lines, base, failAt);
86
+ const first = p.peek();
87
+ if (!first)
88
+ return null;
89
+ if (first.indent > 0 && rootMapping)
90
+ failAt(first, 'unexpected indentation', first.indent + 1);
91
+ if (rootMapping) {
92
+ if (isItem(first.text))
93
+ failAt(first, 'list item without a preceding key', first.indent + 1);
94
+ if (!p.tryKey(first))
95
+ failAt(first, "expected 'key: value'", first.indent + 1);
96
+ }
97
+ const value = p.nodeAt(first);
98
+ const extra = p.peek();
99
+ if (extra)
100
+ failAt(extra, extra.indent > first.indent ? 'unexpected indentation' : 'unexpected content', extra.indent + 1);
101
+ return value;
102
+ }
103
+ function isItem(text) {
104
+ return /^-(?:[ \t]|$)/.test(text);
105
+ }
106
+ class Parser {
107
+ lines;
108
+ base;
109
+ failAt;
110
+ pos = 0;
111
+ depth = 0;
112
+ constructor(lines, base, failAt) {
113
+ this.lines = lines;
114
+ this.base = base;
115
+ this.failAt = failAt;
116
+ }
117
+ /** Next significant line (skipping blanks and comments); leaves pos on it. */
118
+ peek() {
119
+ while (this.pos < this.lines.length) {
120
+ const l = this.lines[this.pos];
121
+ const t = l.text.trim();
122
+ if (t === '' || t.startsWith('#')) {
123
+ this.pos++;
124
+ continue;
125
+ }
126
+ if (l.text.startsWith('\t'))
127
+ this.failAt(l, 'tabs are not allowed for indentation', l.indent + 1);
128
+ return l;
129
+ }
130
+ return undefined;
131
+ }
132
+ fail(line) {
133
+ return (detail, column) => this.failAt(line, detail, column);
134
+ }
135
+ enter(line) {
136
+ if (++this.depth > MAX_DEPTH)
137
+ this.failAt(line, `nesting is deeper than ${MAX_DEPTH} levels`, line.indent + 1);
138
+ }
139
+ /** Parse the next node if it is indented at least `minIndent`, else null. */
140
+ parseNode(minIndent) {
141
+ const line = this.peek();
142
+ if (!line || line.indent < minIndent)
143
+ return null;
144
+ return this.nodeAt(line);
145
+ }
146
+ /** Parse the node starting on `line` (which must be the current line). */
147
+ nodeAt(line) {
148
+ if (isItem(line.text))
149
+ return this.parseSeq(line.indent);
150
+ if (this.tryKey(line))
151
+ return this.parseMap(line.indent);
152
+ return this.inlineValue(line, 0, line.parent);
153
+ }
154
+ parseSeq(n) {
155
+ const first = this.lines[this.pos];
156
+ this.enter(first);
157
+ const out = [];
158
+ for (;;) {
159
+ const line = this.peek();
160
+ if (!line || line.indent < n)
161
+ break;
162
+ if (line.indent > n)
163
+ this.failAt(line, 'unexpected indentation', line.indent + 1);
164
+ if (!isItem(line.text))
165
+ break;
166
+ const m = /^-( *)(.*)$/.exec(line.text);
167
+ const spaces = m[1].length;
168
+ const rest = m[2];
169
+ if (rest === '' || (spaces > 0 && rest.startsWith('#'))) {
170
+ this.pos++;
171
+ out.push(this.parseNode(n + 1));
172
+ }
173
+ else {
174
+ line.indent = n + 1 + spaces;
175
+ line.text = rest;
176
+ line.parent = n;
177
+ out.push(this.nodeAt(line));
178
+ }
179
+ }
180
+ this.depth--;
181
+ return out;
182
+ }
183
+ parseMap(n) {
184
+ const first = this.lines[this.pos];
185
+ this.enter(first);
186
+ const map = new Map();
187
+ for (;;) {
188
+ const line = this.peek();
189
+ if (!line || line.indent < n)
190
+ break;
191
+ if (line.indent > n)
192
+ this.failAt(line, 'unexpected indentation', line.indent + 1);
193
+ if (isItem(line.text))
194
+ this.failAt(line, 'list item without a preceding key', line.indent + 1);
195
+ const k = this.tryKey(line);
196
+ if (!k)
197
+ this.failAt(line, "expected 'key: value'", line.indent + 1);
198
+ const { key, valueIdx } = k;
199
+ if (map.has(key))
200
+ this.failAt(line, `duplicate key '${key}'`, line.indent + 1);
201
+ const rest = line.text.slice(valueIdx);
202
+ const trimmed = rest.trim();
203
+ let value;
204
+ if (trimmed === '' || trimmed.startsWith('#')) {
205
+ this.pos++;
206
+ const next = this.peek();
207
+ if (next && next.indent > n)
208
+ value = this.nodeAt(next);
209
+ else if (next && next.indent === n && isItem(next.text))
210
+ value = this.parseSeq(n);
211
+ else
212
+ value = null;
213
+ }
214
+ else {
215
+ value = this.inlineValue(line, valueIdx, n);
216
+ }
217
+ map.set(key, value);
218
+ }
219
+ this.depth--;
220
+ // fromEntries defines own data properties, so a `__proto__` key is safe.
221
+ return Object.fromEntries(map);
222
+ }
223
+ /** If `line` is `key: ...`, return the key; throws for unsupported forms. */
224
+ tryKey(line) {
225
+ const t = line.text;
226
+ const col = line.indent + 1;
227
+ const fail = this.fail(line);
228
+ if (t[0] === '?' && (t.length === 1 || /\s/.test(t[1])))
229
+ fail('complex keys are not supported', col);
230
+ const c0 = t[0];
231
+ if (c0 === '"' || c0 === "'") {
232
+ const [key, end] = parseQuoted(t, col, fail);
233
+ let i = end;
234
+ while (t[i] === ' ' || t[i] === '\t')
235
+ i++;
236
+ if (t[i] === ':' && (i + 1 >= t.length || t[i + 1] === ' ' || t[i + 1] === '\t'))
237
+ return { key, valueIdx: i + 1 };
238
+ return null;
239
+ }
240
+ if (c0 === '[' || c0 === '{')
241
+ return null;
242
+ const ci = commentIndex(t);
243
+ const head = ci === -1 ? t : t.slice(0, ci);
244
+ const m = /:(?:[ \t]|$)/.exec(head);
245
+ if (!m)
246
+ return null;
247
+ const key = head.slice(0, m.index).trim();
248
+ if (key === '')
249
+ fail('empty key', col);
250
+ if (/^[&*!]/.test(key))
251
+ fail('anchors, aliases and tags are not supported', col);
252
+ if (key === '<<')
253
+ fail('merge keys are not supported', col);
254
+ return { key, valueIdx: m.index + 1 };
255
+ }
256
+ /**
257
+ * Parse the scalar/flow/block-scalar value that starts at `line.text[idx]`.
258
+ * Advances pos past everything consumed.
259
+ */
260
+ inlineValue(line, idx, parentIndent) {
261
+ const t = line.text.slice(idx);
262
+ const lead = t.length - t.trimStart().length;
263
+ const s = t.slice(lead);
264
+ const col = line.indent + idx + lead + 1;
265
+ const fail = this.fail(line);
266
+ const first = s[0];
267
+ if (first === '|' || first === '>')
268
+ return this.blockScalar(line, s, col, parentIndent);
269
+ if (first === '[' || first === '{') {
270
+ const [v, end] = parseFlow(s, 0, col, fail, this.depth + 1);
271
+ assertTrailing(s.slice(end), col + end, fail);
272
+ this.pos++;
273
+ return v;
274
+ }
275
+ if (first === '"' || first === "'") {
276
+ const [v, end] = parseQuoted(s, col, fail);
277
+ assertTrailing(s.slice(end), col + end, fail);
278
+ this.pos++;
279
+ return v;
280
+ }
281
+ if (first === '&' || first === '*' || first === '!')
282
+ fail('anchors, aliases and tags are not supported', col);
283
+ if (/^-(?:[ \t]|$)/.test(s))
284
+ fail('a list item is not allowed here', col);
285
+ if (/^\?(?:[ \t]|$)/.test(s))
286
+ fail('complex keys are not supported', col);
287
+ const plain = stripComment(s).trim();
288
+ if (/:(?:[ \t]|$)/.test(plain))
289
+ fail('nested maps on one line are not supported', col);
290
+ this.pos++;
291
+ return plainScalar(plain);
292
+ }
293
+ blockScalar(line, s, col, parentIndent) {
294
+ const fail = this.fail(line);
295
+ const m = /^([|>])([+-]?)([0-9]?)([ \t]+#.*|[ \t]*)$/.exec(s);
296
+ if (!m)
297
+ return fail('invalid block scalar header', col);
298
+ if (m[3])
299
+ fail('block scalar indentation indicators are not supported', col);
300
+ const style = m[1];
301
+ const chomp = m[2];
302
+ this.pos++;
303
+ const content = [];
304
+ let blockIndent = -1;
305
+ while (this.pos < this.lines.length) {
306
+ const l = this.lines[this.pos];
307
+ const raw = l.raw;
308
+ if (raw.trim() === '') {
309
+ content.push('');
310
+ this.pos++;
311
+ continue;
312
+ }
313
+ let ind = 0;
314
+ while (raw[ind] === ' ')
315
+ ind++;
316
+ if (blockIndent < 0) {
317
+ if (ind <= parentIndent)
318
+ break;
319
+ blockIndent = ind;
320
+ }
321
+ else if (ind < blockIndent) {
322
+ if (ind <= parentIndent)
323
+ break;
324
+ this.failAt(l, 'bad indentation in block scalar', ind + 1);
325
+ }
326
+ content.push(raw.slice(blockIndent));
327
+ this.pos++;
328
+ }
329
+ let end = content.length;
330
+ while (end > 0 && content[end - 1] === '')
331
+ end--;
332
+ const trailing = content.length - end;
333
+ const body = content.slice(0, end);
334
+ if (body.length === 0)
335
+ return chomp === '+' ? '\n'.repeat(trailing) : '';
336
+ const text = style === '|' ? body.join('\n') : fold(body);
337
+ const suffix = chomp === '-' ? '' : chomp === '+' ? '\n'.repeat(trailing + 1) : '\n';
338
+ return text + suffix;
339
+ }
340
+ }
341
+ function fold(lines) {
342
+ let out = '';
343
+ let blanks = 0;
344
+ let prev = null;
345
+ for (const line of lines) {
346
+ if (line === '') {
347
+ blanks++;
348
+ continue;
349
+ }
350
+ if (prev === null) {
351
+ out = '\n'.repeat(blanks) + line;
352
+ }
353
+ else {
354
+ const more = /^[ \t]/.test(prev) || /^[ \t]/.test(line);
355
+ if (more)
356
+ out += '\n'.repeat(blanks + 1);
357
+ else if (blanks === 0)
358
+ out += ' ';
359
+ else
360
+ out += '\n'.repeat(blanks);
361
+ out += line;
362
+ }
363
+ blanks = 0;
364
+ prev = line;
365
+ }
366
+ return out;
367
+ }
368
+ function commentIndex(t) {
369
+ const m = /(^|[ \t])#/.exec(t);
370
+ return m ? m.index : -1;
371
+ }
372
+ function stripComment(t) {
373
+ const i = commentIndex(t);
374
+ return i === -1 ? t : t.slice(0, i);
375
+ }
376
+ function plainScalar(p) {
377
+ if (p === '' || p === '~' || p === 'null')
378
+ return null;
379
+ if (p === 'true')
380
+ return true;
381
+ if (p === 'false')
382
+ return false;
383
+ if (INT_RE.test(p) || DECIMAL_RE.test(p))
384
+ return Number(p);
385
+ return p;
386
+ }
387
+ /** Only whitespace or a ` #` comment may follow a closed value. */
388
+ function assertTrailing(rest, col, fail) {
389
+ const r = rest.trim();
390
+ if (r === '' || (r.startsWith('#') && /^[ \t]/.test(rest)))
391
+ return;
392
+ fail(`unexpected text after value: '${r}'`, col + rest.length - rest.trimStart().length);
393
+ }
394
+ /** Parse a quoted string at t[0]; returns the value and the index after it. */
395
+ function parseQuoted(t, col, fail) {
396
+ const q = t[0];
397
+ let out = '';
398
+ let i = 1;
399
+ while (i < t.length) {
400
+ const c = t[i];
401
+ if (q === '"' && c === '\\') {
402
+ const n = t[i + 1];
403
+ const map = { '"': '"', '\\': '\\', n: '\n', t: '\t' };
404
+ if (n === undefined || map[n] === undefined)
405
+ return fail(`unsupported escape '\\${n ?? ''}'`, col + i);
406
+ out += map[n];
407
+ i += 2;
408
+ }
409
+ else if (q === "'" && c === "'" && t[i + 1] === "'") {
410
+ out += "'";
411
+ i += 2;
412
+ }
413
+ else if (c === q) {
414
+ return [out, i + 1];
415
+ }
416
+ else {
417
+ out += c;
418
+ i++;
419
+ }
420
+ }
421
+ return fail('unterminated quoted string', col);
422
+ }
423
+ const WS = (c) => c === ' ' || c === '\t';
424
+ /**
425
+ * Parse a flow collection or value starting at t[start]. `col` is the column
426
+ * of t[0]. Returns the value and the index just after it.
427
+ */
428
+ function parseFlow(t, start, col, fail, depth) {
429
+ if (depth > MAX_DEPTH)
430
+ return fail(`nesting is deeper than ${MAX_DEPTH} levels`, col + start);
431
+ const c = t[start];
432
+ if (c === '[')
433
+ return parseFlowSeq(t, start, col, fail, depth);
434
+ if (c === '{')
435
+ return parseFlowMap(t, start, col, fail, depth);
436
+ return fail('expected a flow collection', col + start);
437
+ }
438
+ function parseFlowSeq(t, start, col, fail, depth) {
439
+ const open = col + start;
440
+ const items = [];
441
+ let i = start + 1;
442
+ for (;;) {
443
+ while (WS(t[i]))
444
+ i++;
445
+ if (i >= t.length)
446
+ return fail("unterminated list: missing ']'", open);
447
+ if (t[i] === ']') {
448
+ i++;
449
+ break;
450
+ }
451
+ if (t[i] === ',')
452
+ return fail('empty list item', col + i);
453
+ const [v, ni] = parseFlowValue(t, i, col, fail, depth, open, "unterminated list: missing ']'", false);
454
+ items.push(v);
455
+ i = ni;
456
+ while (WS(t[i]))
457
+ i++;
458
+ if (t[i] === ',')
459
+ i++;
460
+ else if (i >= t.length)
461
+ return fail("unterminated list: missing ']'", open);
462
+ else if (t[i] !== ']')
463
+ return fail("expected ',' or ']'", col + i);
464
+ }
465
+ return [items, i];
466
+ }
467
+ function parseFlowMap(t, start, col, fail, depth) {
468
+ const open = col + start;
469
+ const unterminated = "unterminated map: missing '}'";
470
+ const map = new Map();
471
+ let i = start + 1;
472
+ for (;;) {
473
+ while (WS(t[i]))
474
+ i++;
475
+ if (i >= t.length)
476
+ return fail(unterminated, open);
477
+ if (t[i] === '}') {
478
+ i++;
479
+ break;
480
+ }
481
+ if (t[i] === ',')
482
+ return fail('empty map entry', col + i);
483
+ // key
484
+ const keyCol = col + i;
485
+ let key;
486
+ if (t[i] === '"' || t[i] === "'") {
487
+ const [k, len] = parseQuoted(t.slice(i), col + i, fail);
488
+ key = k;
489
+ i += len;
490
+ while (WS(t[i]))
491
+ i++;
492
+ if (t[i] !== ':')
493
+ return fail("expected ':' after key", col + i);
494
+ i++;
495
+ }
496
+ else {
497
+ let j = i;
498
+ while (j < t.length && t[j] !== ',' && t[j] !== '}' && t[j] !== ']') {
499
+ if (t[j] === ':' && (j + 1 >= t.length || ' \t,}]'.includes(t[j + 1])))
500
+ break;
501
+ j++;
502
+ }
503
+ if (j >= t.length)
504
+ return fail(unterminated, open);
505
+ if (t[j] !== ':')
506
+ return fail("expected ':' after key", keyCol);
507
+ key = t.slice(i, j).trim();
508
+ if (key === '')
509
+ return fail('empty key', keyCol);
510
+ if (/^[&*!\[{]/.test(key))
511
+ return fail('anchors, aliases, tags and complex keys are not supported', keyCol);
512
+ if (key === '<<')
513
+ return fail('merge keys are not supported', keyCol);
514
+ i = j + 1;
515
+ }
516
+ if (map.has(key))
517
+ return fail(`duplicate key '${key}'`, keyCol);
518
+ // value
519
+ while (WS(t[i]))
520
+ i++;
521
+ let value = null;
522
+ if (i >= t.length)
523
+ return fail(unterminated, open);
524
+ if (t[i] !== ',' && t[i] !== '}') {
525
+ const [v, ni] = parseFlowValue(t, i, col, fail, depth, open, unterminated, true);
526
+ value = v;
527
+ i = ni;
528
+ }
529
+ map.set(key, value);
530
+ while (WS(t[i]))
531
+ i++;
532
+ if (t[i] === ',')
533
+ i++;
534
+ else if (i >= t.length)
535
+ return fail(unterminated, open);
536
+ else if (t[i] !== '}')
537
+ return fail("expected ',' or '}'", col + i);
538
+ }
539
+ return [Object.fromEntries(map), i];
540
+ }
541
+ function parseFlowValue(t, i, col, fail, depth, open, unterminated, inMap) {
542
+ const c = t[i];
543
+ if (c === '[' || c === '{')
544
+ return parseFlow(t, i, col, fail, depth + 1);
545
+ if (c === '"' || c === "'") {
546
+ const [v, len] = parseQuoted(t.slice(i), col + i, fail);
547
+ return [v, i + len];
548
+ }
549
+ let j = i;
550
+ while (j < t.length && t[j] !== ',' && t[j] !== ']' && t[j] !== '}')
551
+ j++;
552
+ if (j >= t.length)
553
+ return fail(unterminated, open);
554
+ const raw = t.slice(i, j).trim();
555
+ if (/^[&*!]/.test(raw))
556
+ return fail('anchors, aliases and tags are not supported', col + i);
557
+ if (/:(?:\s|$)/.test(raw) || /^[|>]/.test(raw)) {
558
+ return fail(inMap ? 'nested maps on one line are not supported' : 'unsupported list item', col + i);
559
+ }
560
+ return [plainScalar(raw), j];
561
+ }
562
+ //# sourceMappingURL=yaml.js.map
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@johnhenry/sitefile",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.0",
4
+ "description": "Docker-inspired static artifact builder: compose immutable file trees from local and remote sources, render templates, and export a deployable site. No container runtime, no shell, no runtime dependencies.",
5
+ "keywords": [
6
+ "static-site",
7
+ "static-site-generator",
8
+ "build",
9
+ "dockerfile",
10
+ "reproducible-builds",
11
+ "web-bundle",
12
+ "oci",
13
+ "sitefile"
14
+ ],
15
+ "homepage": "https://opensource.johnhenry.me/sitefile/",
16
+ "bugs": {
17
+ "url": "https://github.com/johnhenry/sitefile/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/johnhenry/sitefile.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "John Henry",
25
+ "type": "module",
26
+ "bin": {
27
+ "sitefile": "./lib/src/cli.js"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./lib/src/index.d.ts",
32
+ "default": "./lib/src/index.js"
33
+ }
34
+ },
35
+ "types": "./lib/src/index.d.ts",
36
+ "files": [
37
+ "lib/src/",
38
+ "!lib/src/**/*.map",
39
+ "README.md",
40
+ "LICENSE",
41
+ "CHANGELOG.md"
42
+ ],
43
+ "engines": {
44
+ "node": ">=26.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json",
48
+ "typecheck": "tsc -p tsconfig.json --noEmit",
49
+ "test": "npm run build && node --import ./lib/test/helpers/isolate-home.js --test \"lib/test/**/*.test.js\"",
50
+ "examples": "npm run build && node examples/run.mjs",
51
+ "conformance": "node conformance/run.mjs",
52
+ "bench": "node bench/generate.mjs && node bench/run.mjs",
53
+ "prepack": "npm run build"
54
+ },
55
+ "devDependencies": {
56
+ "@johnhenry/packfile": "^0.2.0",
57
+ "@types/node": "^26.6.6",
58
+ "typescript": "^7.0.2"
59
+ }
60
+ }