@xtrable-ltd/nanoesis 0.1.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 (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/adapter-azure-blob.d.ts +97 -0
  4. package/dist/adapter-azure-blob.js +127 -0
  5. package/dist/adapter-cloudflare.d.ts +28 -0
  6. package/dist/adapter-cloudflare.js +32 -0
  7. package/dist/adapter-fs.d.ts +38 -0
  8. package/dist/adapter-fs.js +54 -0
  9. package/dist/adapter-local-jwt.d.ts +205 -0
  10. package/dist/adapter-local-jwt.js +550 -0
  11. package/dist/adapter-sharp.d.ts +11 -0
  12. package/dist/adapter-sharp.js +39 -0
  13. package/dist/adapter-shell.d.ts +48 -0
  14. package/dist/adapter-shell.js +56 -0
  15. package/dist/adapter-trusted-header.d.ts +43 -0
  16. package/dist/adapter-trusted-header.js +21 -0
  17. package/dist/chunk-G2UEZTYC.js +2541 -0
  18. package/dist/editor-api.d.ts +198 -0
  19. package/dist/editor-api.js +592 -0
  20. package/dist/editor.d.ts +13 -0
  21. package/dist/editor.js +6 -0
  22. package/dist/index.d.ts +1238 -0
  23. package/dist/index.js +124 -0
  24. package/editor/assets/TemplatesPane-5qsDAK_B.js +792 -0
  25. package/editor/assets/TemplatesPane-B4_sg2u5.css +1 -0
  26. package/editor/assets/abap-BrgZPUOV.js +6 -0
  27. package/editor/assets/apex-DyP6w7ZV.js +6 -0
  28. package/editor/assets/azcli-BaLxmfj-.js +6 -0
  29. package/editor/assets/bat-CFOPXBzS.js +6 -0
  30. package/editor/assets/bicep-BfEKNvv3.js +7 -0
  31. package/editor/assets/cameligo-BFG1Mk7z.js +6 -0
  32. package/editor/assets/clojure-DTECt2xU.js +6 -0
  33. package/editor/assets/codicon-DCmgc-ay.ttf +0 -0
  34. package/editor/assets/coffee-CDGzqUPQ.js +6 -0
  35. package/editor/assets/cpp-CLLBncYj.js +6 -0
  36. package/editor/assets/csharp-dUCx_-0o.js +6 -0
  37. package/editor/assets/csp-5Rap-vPy.js +6 -0
  38. package/editor/assets/css-D3h14YRZ.js +8 -0
  39. package/editor/assets/css.worker-DaIe3gwK.js +84 -0
  40. package/editor/assets/cssMode-CGp4MIjR.js +9 -0
  41. package/editor/assets/cypher-DrQuvNYM.js +6 -0
  42. package/editor/assets/dart-CFKIUWau.js +6 -0
  43. package/editor/assets/dockerfile-Zznr-cwX.js +6 -0
  44. package/editor/assets/ecl-Ce3n6wWz.js +6 -0
  45. package/editor/assets/editor.worker-BCzxt1at.js +12 -0
  46. package/editor/assets/elixir-deUWdS0T.js +6 -0
  47. package/editor/assets/flow9-i9-g7ZhI.js +6 -0
  48. package/editor/assets/freemarker2-CJkwxmPv.js +8 -0
  49. package/editor/assets/fsharp-CzKuDChf.js +6 -0
  50. package/editor/assets/go-Cphgjts3.js +6 -0
  51. package/editor/assets/graphql-Cg7bfA9N.js +6 -0
  52. package/editor/assets/handlebars-CKb5i2nM.js +6 -0
  53. package/editor/assets/hcl-0cvrggvQ.js +6 -0
  54. package/editor/assets/html-DyMbQx0w.js +6 -0
  55. package/editor/assets/html.worker-CKrFyw_2.js +461 -0
  56. package/editor/assets/htmlMode-DVPeqtn-.js +9 -0
  57. package/editor/assets/index-CbuWEnUB.css +7 -0
  58. package/editor/assets/index-DJmSgobK.js +129 -0
  59. package/editor/assets/ini-Drc7WvVn.js +6 -0
  60. package/editor/assets/java-B_fMsGYe.js +6 -0
  61. package/editor/assets/javascript-Bp1Qh9wR.js +6 -0
  62. package/editor/assets/json.worker-B7c_PmGb.js +49 -0
  63. package/editor/assets/jsonMode-FLEeVtx7.js +15 -0
  64. package/editor/assets/julia-Bqgm2twL.js +6 -0
  65. package/editor/assets/kotlin-BSkB5QuD.js +6 -0
  66. package/editor/assets/less-BsTHnhdd.js +7 -0
  67. package/editor/assets/lexon-YWi4-JPR.js +6 -0
  68. package/editor/assets/liquid-Bh8c534t.js +6 -0
  69. package/editor/assets/lua-nf6ki56Z.js +6 -0
  70. package/editor/assets/m3-Cpb6xl2v.js +6 -0
  71. package/editor/assets/markdown-DSZPf7rp.js +6 -0
  72. package/editor/assets/mdx-BUbo8M9l.js +6 -0
  73. package/editor/assets/mips-B_c3zf-v.js +6 -0
  74. package/editor/assets/msdax-rUNN04Wq.js +6 -0
  75. package/editor/assets/mysql-DDwshQtU.js +6 -0
  76. package/editor/assets/nanoesis-logo-CgieIWPg.png +0 -0
  77. package/editor/assets/objective-c-B5zXfXm9.js +6 -0
  78. package/editor/assets/pascal-CXOwvkN_.js +6 -0
  79. package/editor/assets/pascaligo-Bc-ZgV77.js +6 -0
  80. package/editor/assets/perl-CwNk8-XU.js +6 -0
  81. package/editor/assets/pgsql-tGk8EFnU.js +6 -0
  82. package/editor/assets/php-CpIb_Oan.js +6 -0
  83. package/editor/assets/pla-B03wrqEc.js +6 -0
  84. package/editor/assets/postiats-BKlk5iyT.js +6 -0
  85. package/editor/assets/powerquery-Bhzvs7bI.js +6 -0
  86. package/editor/assets/powershell-Dd3NCNK9.js +6 -0
  87. package/editor/assets/protobuf-COyEY5Pt.js +7 -0
  88. package/editor/assets/pug-BaJupSGV.js +6 -0
  89. package/editor/assets/python-CuJlk8g3.js +6 -0
  90. package/editor/assets/qsharp-DXyYeYxl.js +6 -0
  91. package/editor/assets/r-CdQndTaG.js +6 -0
  92. package/editor/assets/razor-CuQT_1Ku.js +6 -0
  93. package/editor/assets/redis-CVwtpugi.js +6 -0
  94. package/editor/assets/redshift-25W9uPmb.js +6 -0
  95. package/editor/assets/restructuredtext-DfzH4Xui.js +6 -0
  96. package/editor/assets/ruby-Cp1zYvxS.js +6 -0
  97. package/editor/assets/rust-D5C2fndG.js +6 -0
  98. package/editor/assets/sb-CDntyWJ8.js +6 -0
  99. package/editor/assets/scala-BoFRg7Ot.js +6 -0
  100. package/editor/assets/scheme-Bio4gycK.js +6 -0
  101. package/editor/assets/scss-4Ik7cdeQ.js +8 -0
  102. package/editor/assets/shell-CX-rkNHf.js +6 -0
  103. package/editor/assets/solidity-Tw7wswEv.js +6 -0
  104. package/editor/assets/sophia-C5WLch3f.js +6 -0
  105. package/editor/assets/sparql-DHaeiCBh.js +6 -0
  106. package/editor/assets/sql-CCSDG5nI.js +6 -0
  107. package/editor/assets/st-pnP8ivHi.js +6 -0
  108. package/editor/assets/swift-DwJ7jVG9.js +8 -0
  109. package/editor/assets/systemverilog-B9Xyijhd.js +6 -0
  110. package/editor/assets/tcl-DnHyzjbg.js +6 -0
  111. package/editor/assets/ts.worker-BhkL8olL.js +51334 -0
  112. package/editor/assets/tsMode-CT2HUNtN.js +16 -0
  113. package/editor/assets/twig-CPajHgWi.js +6 -0
  114. package/editor/assets/typescript-CtMx97cn.js +6 -0
  115. package/editor/assets/typespec-D-MeaMDU.js +6 -0
  116. package/editor/assets/vb-DgyLZaXg.js +6 -0
  117. package/editor/assets/wgsl-BIv9DU6q.js +303 -0
  118. package/editor/assets/xml-CyfpINj_.js +6 -0
  119. package/editor/assets/yaml-BBWmgfMA.js +6 -0
  120. package/editor/config.json +3 -0
  121. package/editor/index.html +28 -0
  122. package/package.json +85 -0
@@ -0,0 +1,1238 @@
1
+ /**
2
+ * Convert an arbitrary string into a URL-safe slug: lowercase ASCII, with words
3
+ * separated by single dashes and no leading/trailing dashes.
4
+ *
5
+ * Slugs are filenames in nanoesis (DESIGN §5.3), this function is the single
6
+ * definition of what a valid URL segment looks like, shared by the rename UI and
7
+ * the compiler so the editor and the engine can never disagree.
8
+ */
9
+ declare function slugify(input: string): string;
10
+
11
+ /**
12
+ * Turn a token name into a human label for the editor (DESIGN §6.1):
13
+ * `hero_image` → "Hero image". Separators become spaces, camelCase is split, and
14
+ * the result is sentence-cased.
15
+ */
16
+ declare function humanize(name: string): string;
17
+
18
+ /** Best-effort content type from a path's extension; defaults to octet-stream. */
19
+ declare function contentTypeFor(path: string): string;
20
+
21
+ /**
22
+ * The content model (DESIGN §5). Content JSON is *pure data*: a value's type
23
+ * comes from the template, never from the file, so these types model only the
24
+ * JSON shape, not field semantics.
25
+ */
26
+ /** A JSON-serialisable field value: a primitive, an array of primitives, or an
27
+ * array of flat records (a structured field such as authors). */
28
+ type FieldPrimitive = string | number | boolean | null;
29
+ /**
30
+ * One structured entry, a flat record whose values are primitives, e.g. a single
31
+ * author byline `{ name, user? }`. Kept flat so the value stays pure JSON data
32
+ * (DESIGN §5): semantics still come from the template, not the file.
33
+ */
34
+ type FieldRecord = Readonly<Record<string, FieldPrimitive>>;
35
+ type FieldValue = FieldPrimitive | readonly FieldPrimitive[] | readonly FieldRecord[];
36
+ /**
37
+ * One content item: one file per item, filename = slug (DESIGN §5.4). Reserved
38
+ * system keys sit at the top level; author values live in `fields`, isolated so
39
+ * arbitrary token names can never collide with system keys. `template` is
40
+ * optional here because a folder can supply the default (DESIGN §5.5); the
41
+ * effective template is resolved during loading.
42
+ */
43
+ interface ContentItem {
44
+ readonly template?: string;
45
+ readonly title: string;
46
+ /** Whether the item goes to the live site (DESIGN §5.4). Absent in JSON means false. */
47
+ readonly isPublished: boolean;
48
+ readonly created?: string;
49
+ /** Publish date, author-set; also the default loop sort key. ISO 8601. */
50
+ readonly published?: string;
51
+ readonly updated?: string;
52
+ readonly fields: Readonly<Record<string, FieldValue>>;
53
+ }
54
+ /** A directory marked as a collection (DESIGN §5.6). */
55
+ interface CollectionConfig {
56
+ /** A recursive collection includes every item beneath it. */
57
+ readonly recursive: boolean;
58
+ }
59
+ /**
60
+ * The reserved per-directory sort file (DESIGN §5.2): display name, ordered
61
+ * contents, collection setting, and default template, all in one place.
62
+ */
63
+ interface SortFile {
64
+ readonly name?: string;
65
+ readonly collection?: CollectionConfig;
66
+ readonly defaultTemplate?: string;
67
+ /** Ordered child filenames/dirnames; missing entries are tolerated. */
68
+ readonly order: readonly string[];
69
+ }
70
+ /** A content page in the tree (DESIGN §5.1). */
71
+ interface ItemNode {
72
+ readonly kind: 'item';
73
+ /** Filename without extension = URL segment (DESIGN §5.3). */
74
+ readonly slug: string;
75
+ /** Content path from the root, e.g. "blog/going-static". */
76
+ readonly path: string;
77
+ readonly item: ContentItem;
78
+ }
79
+ /** A directory in the tree; the file/folder layout *is* the site structure. */
80
+ interface DirNode {
81
+ readonly kind: 'dir';
82
+ /** Directory name = URL segment ('' for the tree root). */
83
+ readonly slug: string;
84
+ /** Display name (sort file `name`, or the humanised slug). */
85
+ readonly name: string;
86
+ /** Content path from the root, e.g. "blog" (''for the root). */
87
+ readonly path: string;
88
+ readonly collection?: CollectionConfig;
89
+ readonly defaultTemplate?: string;
90
+ /** Children ordered per the sort file, unknown trailing entries appended. */
91
+ readonly children: readonly TreeNode[];
92
+ }
93
+ type TreeNode = DirNode | ItemNode;
94
+
95
+ /** Thrown when raw content JSON cannot be coerced into a {@link ContentItem}. */
96
+ declare class ContentParseError extends Error {
97
+ constructor(message: string);
98
+ }
99
+ /**
100
+ * Validate untrusted content JSON into a typed {@link ContentItem}, the single
101
+ * boundary where content data is checked (CLAUDE.md §4). Tolerant of drift
102
+ * (unknown keys ignored, missing isPublished/fields defaulted; DESIGN §5.4) but
103
+ * strict on the system keys that carry meaning.
104
+ */
105
+ declare function parseContentItem(raw: unknown): ContentItem;
106
+
107
+ /**
108
+ * Parse a directory's sort file (DESIGN §5.2). Deliberately tolerant: a stale or
109
+ * malformed sort file never blocks a publish, it just falls back to defaults.
110
+ */
111
+ declare function parseSortFile(raw: unknown): SortFile;
112
+
113
+ /**
114
+ * The storage port an adopter implements (DESIGN §11c): a flat key-to-bytes store, an
115
+ * S3 bucket, an Azure container, a local folder, or a `Map`. The engine and the editor
116
+ * reach every byte through this. Enumeration is deliberately *not* here, the content
117
+ * index (§11d) owns it, so a store never needs a `list` or a scan, which is the
118
+ * operation blob/S3 cannot do cheaply. Keys are opaque, POSIX-style site-relative paths
119
+ * (e.g. "content/blog/post.json"); the store treats them as literal map keys.
120
+ */
121
+ interface BlobStore {
122
+ /** The bytes stored at `key`, or `undefined` if nothing is stored there. */
123
+ get(key: string): Promise<Uint8Array | undefined>;
124
+ /** Create or overwrite `key` with `bytes`. */
125
+ put(key: string, bytes: Uint8Array): Promise<void>;
126
+ /** Remove `key`. Deleting an absent key is a no-op (idempotent). */
127
+ delete(key: string): Promise<void>;
128
+ }
129
+ /**
130
+ * In-memory {@link BlobStore} backed by a plain map, the test double the engine's
131
+ * store and index tests run against (the LSP guarantee that real adapters are
132
+ * interchangeable). It copies bytes in and out, so a caller can never mutate stored
133
+ * data by holding on to a buffer.
134
+ */
135
+ declare class InMemoryBlobStore implements BlobStore {
136
+ private readonly blobs;
137
+ constructor(initial?: Readonly<Record<string, Uint8Array | string>>);
138
+ get(key: string): Promise<Uint8Array | undefined>;
139
+ put(key: string, bytes: Uint8Array): Promise<void>;
140
+ delete(key: string): Promise<void>;
141
+ }
142
+
143
+ /**
144
+ * The content-source port (DESIGN §3). The engine reads everything, content
145
+ * tree, templates, components, through this narrow interface and never touches a
146
+ * filesystem or blob store directly (CLAUDE.md §2/§3). Paths are POSIX-style and
147
+ * relative to the site root (e.g. "content/blog/post.json").
148
+ */
149
+ type EntryKind = 'file' | 'dir';
150
+ interface DirEntry {
151
+ readonly name: string;
152
+ readonly kind: EntryKind;
153
+ }
154
+ interface ContentSource {
155
+ /** Immediate children of a directory. */
156
+ list(dir: string): Promise<readonly DirEntry[]>;
157
+ /** Read a UTF-8 text file; rejects if it does not exist. */
158
+ readText(path: string): Promise<string>;
159
+ /** Read raw bytes (for images and other binary assets); rejects if missing. */
160
+ readBytes(path: string): Promise<Uint8Array>;
161
+ /** Whether a file or directory exists at the path. */
162
+ exists(path: string): Promise<boolean>;
163
+ }
164
+ /**
165
+ * An in-memory {@link ContentSource} backed by a flat path→text map. Directories
166
+ * are inferred from path prefixes. This is the test double the engine's loader
167
+ * tests run against, the LSP guarantee that adapters are interchangeable.
168
+ */
169
+ declare class InMemoryContentSource implements ContentSource {
170
+ private readonly files;
171
+ constructor(files: Readonly<Record<string, string | Uint8Array>>);
172
+ readText(path: string): Promise<string>;
173
+ readBytes(path: string): Promise<Uint8Array>;
174
+ exists(path: string): Promise<boolean>;
175
+ list(dir: string): Promise<readonly DirEntry[]>;
176
+ }
177
+
178
+ /**
179
+ * The outcome of a {@link WorkingStore.rename}. Clobbering an existing destination, or
180
+ * renaming a path that does not exist, are *expected* outcomes (a user renames to a name
181
+ * already in use), so they are returned as data, not thrown (CLAUDE §2): the host maps
182
+ * them to 409/404.
183
+ */
184
+ type RenameResult = {
185
+ readonly ok: true;
186
+ } | {
187
+ readonly ok: false;
188
+ readonly reason: 'exists' | 'missing';
189
+ };
190
+ /**
191
+ * The editor's working store (DESIGN §11c): a read {@link ContentSource} for
192
+ * compile/validate, plus the write/delete/rename the editor content API needs. The
193
+ * editor API depends on this port, never a concrete store, so the index-backed
194
+ * {@link IndexedStore} (over an adopter's get/put/delete) and the blob adapter are
195
+ * interchangeable behind it.
196
+ */
197
+ interface WorkingStore extends ContentSource {
198
+ /** Create or overwrite `path` with raw bytes (binary-correct). */
199
+ write(path: string, contents: Uint8Array): Promise<void>;
200
+ /** Delete a file, or a whole directory subtree; a missing path is a no-op. */
201
+ delete(path: string): Promise<void>;
202
+ /** Move a file or subtree; refuses to clobber, or to rename a missing path. */
203
+ rename(from: string, to: string): Promise<RenameResult>;
204
+ }
205
+
206
+ /**
207
+ * A {@link ContentSource} backed by the content index (DESIGN §11d) over a
208
+ * {@link BlobStore}: enumeration (`list`/`exists`) is synthesised from the index's key
209
+ * set, reads go straight to `get`. This is the working store the editor and compile run
210
+ * against once an adopter provides only get/put/delete, every existing `list` caller is
211
+ * unchanged because it still sees a `ContentSource`.
212
+ *
213
+ * Mutations (`write`/`delete`/`rename`) write the file(s) first, then the index
214
+ * (content-first, §11d), keeping the in-memory copy in step so reads on this instance
215
+ * reflect the change at once. The index is only rewritten when the key *set* changes, an
216
+ * overwrite of an existing key is a single `put`.
217
+ *
218
+ * Construction does no I/O: the index loads (and recovers through the backup ring, §11d)
219
+ * lazily on the first enumeration or mutation, so a host can wire the store synchronously.
220
+ *
221
+ * The prefix-synthesis below mirrors {@link InMemoryContentSource} exactly (a cross-check
222
+ * test pins them together); the two are interchangeable implementations of the port.
223
+ */
224
+ declare class IndexedStore implements WorkingStore {
225
+ private readonly store;
226
+ private index;
227
+ private loading;
228
+ constructor(store: BlobStore);
229
+ /** The index, loaded once on first need and cached (mutations replace the cached copy). */
230
+ private loaded;
231
+ list(dir: string): Promise<readonly DirEntry[]>;
232
+ readText(path: string): Promise<string>;
233
+ readBytes(path: string): Promise<Uint8Array>;
234
+ exists(path: string): Promise<boolean>;
235
+ /**
236
+ * Create or overwrite `key`. The index is rewritten only when `key` is new (an
237
+ * overwrite leaves the key set unchanged, so editing an item is a single `put`).
238
+ */
239
+ write(key: string, bytes: Uint8Array): Promise<void>;
240
+ /**
241
+ * Delete a file, or a whole directory subtree (every key under `key/`). Idempotent: a
242
+ * path the index does not know is still deleted from the store (clearing an orphan),
243
+ * and deleting nothing is a no-op.
244
+ */
245
+ delete(key: string): Promise<void>;
246
+ /**
247
+ * Move/rename a file, or a whole directory subtree (every key under `from/` is remapped
248
+ * under `to/`). Clobbering an existing destination, or renaming a missing path, are
249
+ * returned as data (`ok: false`), not thrown (CLAUDE §2), the same contract the host's
250
+ * `/api/rename` enforces. (Mutating the reserved namespace is a programmer error and
251
+ * still throws.)
252
+ */
253
+ rename(from: string, to: string): Promise<RenameResult>;
254
+ }
255
+
256
+ /**
257
+ * The content index (DESIGN §11d): the sorted set of every key in the working store,
258
+ * the directory listing a flat blob store cannot give cheaply, lifted into one object
259
+ * we own. Enumeration (the editor tree, compile) reads this, so the adopter's store
260
+ * stays get/put/delete with no `list`. Persisted under a reserved key with a fixed-size
261
+ * backup ring for integrity.
262
+ */
263
+ interface ContentIndex {
264
+ /** Monotonic, bumped on every save; orders the backup ring during recovery. */
265
+ readonly version: number;
266
+ /** Every key that exists, sorted and de-duplicated (deterministic, CLAUDE §2). */
267
+ readonly keys: readonly string[];
268
+ /** Checksum over version + keys, so a corrupt stored index is caught on read. */
269
+ readonly checksum: string;
270
+ }
271
+ /** The empty index of a fresh store: version 0, no keys. */
272
+ declare function emptyIndex(): ContentIndex;
273
+ /**
274
+ * Load the index, recovering through the backup ring if the live copy is missing or
275
+ * corrupt (DESIGN §11d): the highest valid version among the ring slots wins. Returns
276
+ * the empty index when nothing valid is found, a fresh store, or the accepted residual
277
+ * case where the live index and every backup are lost (the files still exist by key,
278
+ * but cannot be enumerated until something rewrites the index).
279
+ */
280
+ declare function loadIndex(store: BlobStore): Promise<ContentIndex>;
281
+ /**
282
+ * Write a new index whose keys are `nextKeys`, backing up the one it replaces first
283
+ * (DESIGN §11d). The previous index goes into its ring slot (`version % ring`, oldest
284
+ * overwritten), then the new index is written to the live key, a constant two writes
285
+ * per save regardless of ring size. Returns the new index (the caller's next `prev`).
286
+ */
287
+ declare function saveIndex(store: BlobStore, prev: ContentIndex, nextKeys: readonly string[]): Promise<ContentIndex>;
288
+
289
+ /**
290
+ * The authors byline (DESIGN §6.11). An `authors` field stores an ordered list of
291
+ * author refs; the compiler renders them as one grammatical line. Everything here
292
+ * is pure: resolving a stored userName to its *current* display name needs the user
293
+ * store, which the engine must not touch, so the host injects that lookup as an
294
+ * {@link AuthorDirectory}. Without one, rendering falls back to the stored name
295
+ * snapshot, so the engine always produces a byline on its own.
296
+ */
297
+ /**
298
+ * One stored author. A guest is just `{ name }`. A picked user also carries the
299
+ * (immutable) `user` handle, so a later display-name change propagates to every
300
+ * past byline and a bio link can be derived without a content migration. `name` is
301
+ * the display-name snapshot, kept as the offline fallback (deleted user / no
302
+ * directory). `href` is reserved for a future explicit guest link, additive.
303
+ */
304
+ interface AuthorRef {
305
+ readonly name: string;
306
+ readonly user?: string;
307
+ readonly href?: string;
308
+ }
309
+ /** Current display info for a stored `user` handle (host-injected; keeps the engine pure). */
310
+ interface AuthorEntry {
311
+ readonly displayName: string;
312
+ /** Reserved for a future bio-page link; unused by the current renderer. */
313
+ readonly href?: string;
314
+ }
315
+ /** Resolve a stored `user` handle to its live display info, or undefined if unknown. */
316
+ type AuthorDirectory = (user: string) => AuthorEntry | undefined;
317
+ /**
318
+ * A user offered to the byline picker (DESIGN §6.11): their current display name plus
319
+ * the stable `user` handle the editor stores. The directory endpoint returns these; the
320
+ * editor stores a picked one as `{ name, user }` (an {@link AuthorRef}).
321
+ */
322
+ interface AuthorOption {
323
+ readonly name: string;
324
+ readonly user: string;
325
+ }
326
+ /**
327
+ * Coerce a stored field value into author refs, dropping anything without a usable
328
+ * name (a blank name, a non-object entry, a non-array value). The content boundary
329
+ * already guarantees a flat record shape; this is the semantic narrowing the byline
330
+ * needs.
331
+ */
332
+ declare function toAuthorRefs(value: FieldValue | undefined): AuthorRef[];
333
+ /**
334
+ * Join names into a grammatical byline (DESIGN §6.11): `0 → ""`, `1 → "A"`,
335
+ * `2 → "A and B"`, `3+ → "A, B and C"` (no Oxford comma). Operates on the strings
336
+ * as given; escaping is {@link renderAuthors}' job, so this stays a pure string join
337
+ * the table tests can pin exhaustively.
338
+ */
339
+ declare function joinAuthors(names: readonly string[]): string;
340
+ /**
341
+ * Render an authors field value to an HTML byline. Each author's display name is
342
+ * resolved live through the `directory` (falling back to the stored `name`
343
+ * snapshot), HTML-escaped, then joined grammatically. The result is HTML, not plain
344
+ * text, so future `<a>` bio links slot in without changing the compiler's path.
345
+ */
346
+ declare function renderAuthors(value: FieldValue | undefined, directory?: AuthorDirectory): string;
347
+
348
+ interface CompileSiteOptions {
349
+ /** Absolute base URL; enables sitemap.xml (and RSS when `rss` is set). */
350
+ readonly baseUrl?: string;
351
+ /** Emit an RSS feed for a collection (requires `baseUrl`). */
352
+ readonly rss?: {
353
+ readonly collection: string;
354
+ readonly title: string;
355
+ readonly description?: string;
356
+ };
357
+ /** Emit content.json, the lean content manifest (default true). */
358
+ readonly contentIndex?: boolean;
359
+ /** Image encoder for the media pipeline; without it, images keep their raw path. */
360
+ readonly imageEncoder?: ImageEncoder;
361
+ /** Max images encoded in parallel per page (default {@link DEFAULT_IMAGE_CONCURRENCY}). */
362
+ readonly imageConcurrency?: number;
363
+ /**
364
+ * Resolve an author's stored `user` handle to its current display name (DESIGN
365
+ * §6.11). Host-injected (the engine can't read the user store); without it,
366
+ * `authors` bylines use the stored name snapshot.
367
+ */
368
+ readonly authorDirectory?: AuthorDirectory;
369
+ }
370
+ /** One compiled output file, text (HTML/XML/JSON) or binary (image variants, files). */
371
+ interface Artifact {
372
+ /** Output path relative to the published root, e.g. "blog/first/index.html". */
373
+ readonly path: string;
374
+ readonly contents: string | Uint8Array;
375
+ }
376
+ /**
377
+ * Compile a whole site to artifacts (DESIGN §15 step 9, compile phase). Walks the
378
+ * content tree and renders every **published** item through its template, the
379
+ * per-item template, else the nearest folder default (DESIGN §5.5). A site graph
380
+ * resolves loops and references. Drafts are skipped; output is deterministic.
381
+ */
382
+ declare function compileSite(source: ContentSource, options?: CompileSiteOptions): Promise<Artifact[]>;
383
+ /** Options for {@link compilePage}. */
384
+ interface CompilePageOptions {
385
+ /**
386
+ * Render this item instead of the one on disk, so the editor can preview live,
387
+ * unsaved edits. Its `template` (or the folder default) selects the template.
388
+ */
389
+ readonly draft?: ContentItem;
390
+ /**
391
+ * A caller-supplied media lookup. The editor passes one that maps asset refs to
392
+ * readable preview URLs (no variant generation), so a preview is instant; when
393
+ * omitted, the real media phase runs (using `imageEncoder` if given).
394
+ */
395
+ readonly media?: MediaResolver;
396
+ /** Image encoder for the real media phase; ignored when `media` is supplied. */
397
+ readonly imageEncoder?: ImageEncoder;
398
+ /** Author display-name resolver for `authors` bylines (DESIGN §6.11); see
399
+ * {@link CompileSiteOptions.authorDirectory}. */
400
+ readonly authorDirectory?: AuthorDirectory;
401
+ }
402
+ /** The result of {@link compilePage}, the page HTML and any media it produced. */
403
+ interface CompiledPage {
404
+ readonly html: string;
405
+ /** Media artifacts from the real pipeline; empty when a `media` resolver was supplied. */
406
+ readonly mediaArtifacts: readonly Artifact[];
407
+ }
408
+ /**
409
+ * Compile a *single* item to HTML by its tree path, the editor's live preview
410
+ * (DESIGN §6.7). It shares the whole compile path with {@link compileSite}: the
411
+ * same tree, components, site graph (so loops/references resolve), template
412
+ * resolution, and scope. Two differences serve the preview: an item is rendered
413
+ * regardless of its `state` (you preview drafts), and the caller may inject a
414
+ * `draft` override and a `media` resolver. Returns `undefined` when the path names
415
+ * no item or the item has no resolvable template.
416
+ */
417
+ declare function compilePage(source: ContentSource, itemPath: string, options?: CompilePageOptions): Promise<CompiledPage | undefined>;
418
+ /**
419
+ * Build the resolution context the compiler consults for loops and references
420
+ * (DESIGN §6.6) from a loaded content tree. Pure (no I/O): the host loads the tree
421
+ * through a port, then hands it here. Exported so the editor's unit preview can
422
+ * resolve loops/references against the real site, sharing this exact logic with the
423
+ * publish path rather than re-implementing it.
424
+ */
425
+ declare function buildResolveContext(tree: DirNode): ResolveContext;
426
+
427
+ /**
428
+ * The media pipeline (DESIGN §8). Images are transformed into responsive,
429
+ * modern-format variants and an `<img>` is expanded into an optimised `<picture>`.
430
+ * The *policy* (which widths/formats, content-hashed names, picture markup) lives
431
+ * here in the pure engine; the actual pixel encoding is a port (CLAUDE.md §3) so
432
+ * the engine needs no native dependency and stays testable.
433
+ */
434
+ type ImageFormat = 'avif' | 'webp' | 'jpeg' | 'png';
435
+ interface EncodeRequest {
436
+ /** Candidate widths; the encoder must not upscale and should include the native width. */
437
+ readonly widths: readonly number[];
438
+ readonly formats: readonly ImageFormat[];
439
+ }
440
+ interface EncodedVariant {
441
+ readonly format: ImageFormat;
442
+ readonly width: number;
443
+ readonly bytes: Uint8Array;
444
+ }
445
+ interface EncodedImage {
446
+ readonly sourceWidth: number;
447
+ readonly sourceHeight: number;
448
+ readonly variants: readonly EncodedVariant[];
449
+ /** Tiny inline placeholder (data URI) shown while the image loads. */
450
+ readonly blurDataUri?: string;
451
+ }
452
+ /** The pixel-encoding port, implemented with sharp in `@nanoesis/adapter-sharp`. */
453
+ interface ImageEncoder {
454
+ encode(input: Uint8Array, request: EncodeRequest): Promise<EncodedImage>;
455
+ }
456
+ /** One `<source>`'s worth of responsive variants. */
457
+ interface SourceSet {
458
+ readonly format: ImageFormat;
459
+ readonly mime: string;
460
+ readonly srcset: string;
461
+ }
462
+ /** The compiled facts about one image, used to build its `<picture>`. */
463
+ interface ImageInfo {
464
+ readonly width: number;
465
+ readonly height: number;
466
+ readonly sources: readonly SourceSet[];
467
+ readonly fallbackSrc: string;
468
+ readonly blurDataUri?: string;
469
+ }
470
+ /**
471
+ * Per-item media lookup the compiler consults: a processed image by its asset
472
+ * reference, or the published URL of a copied file. Built by the orchestrator.
473
+ */
474
+ interface MediaResolver {
475
+ image(assetRef: string): ImageInfo | undefined;
476
+ file(assetRef: string): string | undefined;
477
+ }
478
+ /** A small, fast, non-cryptographic content hash (FNV-1a) for cache-busting names. */
479
+ declare function contentHash(bytes: Uint8Array): string;
480
+ /**
481
+ * Encode one image into variants and compute its {@link ImageInfo} plus the
482
+ * binary artifacts to publish. Variant filenames are content-hashed so they
483
+ * cache-bust for free (DESIGN §8). `assetPath` is the published-relative path of
484
+ * the original, e.g. "blog/assets/hero.jpg".
485
+ */
486
+ declare function processImage(input: Uint8Array, assetPath: string, encoder: ImageEncoder): Promise<{
487
+ readonly artifacts: readonly Artifact[];
488
+ readonly info: ImageInfo;
489
+ }>;
490
+ /**
491
+ * Build the `<picture>` markup for an image (DESIGN §8). `extraAttrs` carries the
492
+ * resolved attributes from the original `<img>` (alt, class, …) onto the fallback.
493
+ */
494
+ declare function buildPictureMarkup(info: ImageInfo, extraAttrs: ReadonlyArray<readonly [string, string]>): string;
495
+
496
+ /**
497
+ * An item bound into loop scope (DESIGN §6.6). `{item}` resolves to its URL;
498
+ * `{item.field}` to one of its fields.
499
+ */
500
+ interface BoundItem {
501
+ readonly url: string;
502
+ readonly fields: Scope;
503
+ }
504
+ /** Loop ordering options parsed from a query loop's attributes. */
505
+ interface CollectionQuery {
506
+ readonly sort?: string;
507
+ readonly limit?: number;
508
+ }
509
+ /**
510
+ * The resolution context (the site graph) the compiler consults for loops and
511
+ * references (DESIGN §6.6, §10). Supplied by the host/orchestrator; kept behind
512
+ * this interface so the compiler stays decoupled from the content tree.
513
+ */
514
+ interface ResolveContext {
515
+ /** Published items in a collection, for an automatic/query loop. */
516
+ collection(path: string, query: CollectionQuery): readonly BoundItem[];
517
+ /** Resolve a curated reference (content path) to a bound item if it exists & is published. */
518
+ reference(path: string): BoundItem | undefined;
519
+ }
520
+ type ScopeValue = FieldValue | BoundItem;
521
+ /** A flat map of token name → value, for one scope (page fields, props, or a loop item). */
522
+ type Scope = Readonly<Record<string, ScopeValue>>;
523
+ /** Components keyed by their lowercased custom-element tag (DESIGN §6.8). */
524
+ type ComponentMap = ReadonlyMap<string, string>;
525
+ interface CompileInput {
526
+ /** The entry template's HTML source. */
527
+ readonly template: string;
528
+ /**
529
+ * The document shell (`templates/document.html`), if the site has one (DESIGN §6.10).
530
+ * It wraps every page: compiled in the *same* page scope (so its `<head>` tokens are
531
+ * page fields) with the page body filling its `<slot>`. Optional and additive, without
532
+ * it, the page template is the whole document, exactly as before.
533
+ */
534
+ readonly document?: string;
535
+ /** Resolved page-field values. */
536
+ readonly scope: Scope;
537
+ /** Available components, tag → source. */
538
+ readonly components: ComponentMap;
539
+ /** Site graph for loops/references; loops resolve to nothing without it. */
540
+ readonly context?: ResolveContext;
541
+ /** Per-item media lookup; without it, image/file tokens keep their raw value. */
542
+ readonly media?: MediaResolver;
543
+ /**
544
+ * Resolves an author's stored `user` handle to its current display name (DESIGN
545
+ * §6.11). Host-injected so the engine stays pure; without it, `authors` fields
546
+ * fall back to the stored name snapshot.
547
+ */
548
+ readonly authorDirectory?: AuthorDirectory;
549
+ /** The page template's co-located CSS, emitted once into `<head>` (DESIGN §6.9). */
550
+ readonly templateStyle?: string;
551
+ /** The page template's co-located JS, emitted once at the end of `<body>`. */
552
+ readonly templateScript?: string;
553
+ /** Component CSS by tag, injected as a scoped `<style>` at each instance's root. */
554
+ readonly componentStyles?: ReadonlyMap<string, string>;
555
+ /** Component JS by tag, emitted once per page (deduped across instances). */
556
+ readonly componentScripts?: ReadonlyMap<string, string>;
557
+ }
558
+ /**
559
+ * Compile a template + a content scope into HTML (DESIGN §6). Components, loops,
560
+ * and media resolve recursively at compile time and substitution is per-context,
561
+ * text, attribute, URL, rich-text, JSON-LD, which is what keeps output
562
+ * injection-safe. Nothing of the framework survives into the output.
563
+ */
564
+ declare function compileTemplate(input: CompileInput): string;
565
+
566
+ /**
567
+ * Redirect generation (DESIGN §9). A move or slug rename changes a page's URL;
568
+ * internal `ref:` links auto-update, but external/bookmarked/indexed URLs would
569
+ * 404. The editor records the old→new mapping *at the moment of the move* (the only
570
+ * actor that knows a move happened, to this stateless compiler a move is
571
+ * indistinguishable from a delete+create) into a single reserved
572
+ * `content/_redirects.json`. Publish reads that as just another input and emits a
573
+ * Cloudflare/Netlify-style `_redirects` artifact at the published root.
574
+ *
575
+ * This module is pure: parsing untrusted JSON into typed rules, and turning rules +
576
+ * the set of currently-live URLs into the artifact. The file read lives in the loader.
577
+ */
578
+
579
+ /** One redirect rule. `from`/`to` are URL paths; a `*` in `from` is a wildcard
580
+ * (folder move), with `:splat` in `to` standing for the matched tail. */
581
+ interface RedirectRule {
582
+ readonly from: string;
583
+ readonly to: string;
584
+ /** HTTP status; defaults to 301 (permanent). */
585
+ readonly status?: number;
586
+ }
587
+ /**
588
+ * Parse the reserved `content/_redirects.json` (untrusted input) into typed rules.
589
+ * Malformed entries are dropped and a non-array yields no rules, a garbage redirect
590
+ * file never blocks a publish (mirrors the sort-file contract, CLAUDE.md §2).
591
+ */
592
+ declare function parseRedirects(raw: unknown): RedirectRule[];
593
+ /**
594
+ * Build the `_redirects` artifact from the recorded rules and the set of
595
+ * currently-live page URLs. Exact rules collapse chains (A→B→C ⇒ A→C), drop a rule
596
+ * whose source is now a live page or that resolves to itself (covers cycles), and
597
+ * last-wins on a duplicate source. Wildcard rules (folder moves) pass through,
598
+ * deduped. All rules are sorted before emit so output is deterministic (CLAUDE.md §2).
599
+ * Returns `undefined` when nothing remains to emit (no file written).
600
+ */
601
+ declare function buildRedirects(rules: readonly RedirectRule[], liveUrls: ReadonlySet<string>): Artifact | undefined;
602
+
603
+ declare const DEFAULT_DIRS: {
604
+ readonly content: "content";
605
+ readonly templates: "templates";
606
+ readonly components: "components";
607
+ /** Static passthrough, copied verbatim to the published root (DESIGN §8). The
608
+ * engine never reads it; named here so hosts/editor share one source of truth. */
609
+ readonly public: "public";
610
+ };
611
+ declare function loadContentTree(source: ContentSource, contentDir?: string): Promise<DirNode>;
612
+ /**
613
+ * Read the reserved redirect map (`content/_redirects.json`, DESIGN §9) into typed
614
+ * rules, or `[]` when absent. A missing or garbage file never blocks a publish,
615
+ * the rules are emitted as the `_redirects` artifact by {@link buildRedirects}.
616
+ */
617
+ declare function loadRedirects(source: ContentSource, contentDir?: string): Promise<RedirectRule[]>;
618
+ /** Editor-set site configuration (the reserved `content/_site.json`). */
619
+ interface SiteConfig {
620
+ /** Absolute site base URL; when set, publish emits sitemap.xml and absolute RSS links. */
621
+ readonly baseUrl?: string;
622
+ }
623
+ /**
624
+ * Read the reserved site-config file (`content/_site.json`), or `{}` when absent or
625
+ * garbage, config never blocks a publish. The editor writes it from the admin Settings;
626
+ * `compileSite` reads it so a base URL set in the editor switches sitemap.xml on without
627
+ * any host/env change (an explicit host `baseUrl` option still overrides it).
628
+ */
629
+ declare function loadSiteConfig(source: ContentSource, contentDir?: string): Promise<SiteConfig>;
630
+ /**
631
+ * Build the component map (tag → source) from the components directory, recursing
632
+ * into subfolders. Subfolders are purely organizational (DESIGN §7): a component's
633
+ * tag is its **basename** (`components/marketing/hero.html` → `<hero>`), because a
634
+ * component is referenced by an HTML tag, which is a flat namespace (no slashes).
635
+ * Two files sharing a basename collide on one tag, last wins here; the validation
636
+ * gate reports it (`component-tag-collision`) so a publish can't ship the wrong one.
637
+ */
638
+ declare function loadComponents(source: ContentSource, componentsDir?: string): Promise<ComponentMap>;
639
+ /** Component CSS by tag (co-located `<name>.css` files); DESIGN §6.9. */
640
+ declare function loadComponentStyles(source: ContentSource, componentsDir?: string): Promise<ReadonlyMap<string, string>>;
641
+ /** Component JS by tag (co-located `<name>.js` files); DESIGN §6.9. */
642
+ declare function loadComponentScripts(source: ContentSource, componentsDir?: string): Promise<ReadonlyMap<string, string>>;
643
+ /** Read a named template's source. */
644
+ declare function loadTemplate(source: ContentSource, name: string, templatesDir?: string): Promise<string>;
645
+ /** The reserved document-shell template name, `templates/document.html` (DESIGN §6.10). */
646
+ declare const DOCUMENT_SHELL = "document";
647
+ /**
648
+ * The document shell source if the site defines one, else undefined. The shell wraps every
649
+ * page (its `<head>`/chrome), so it's never selected as an item's template; it's optional.
650
+ */
651
+ declare function loadDocumentShell(source: ContentSource, templatesDir?: string): Promise<string | undefined>;
652
+
653
+ /**
654
+ * The field-type registry (DESIGN §6.7). Field types and their rules are defined
655
+ * *once* here and shared by the compiler, the editor's form generator, and (later)
656
+ * Monaco's IntelliSense. This is the OCP extension point (CLAUDE.md §3): adding a
657
+ * field type means adding an entry here, never editing a switch in the compiler or
658
+ * the inference cascade.
659
+ */
660
+ type FieldType = 'image' | 'file' | 'url' | 'email' | 'phone' | 'date' | 'time' | 'code' | 'richtext' | 'authors' | 'text' | 'shorttext';
661
+ /** How a resolved value is placed into output, the compiler reads this, never the type name. */
662
+ type ValueKind = 'text' | 'html' | 'url' | 'asset' | 'authors';
663
+ interface FieldTypeDef {
664
+ readonly type: FieldType;
665
+ readonly valueKind: ValueKind;
666
+ /** Default editor control hint, consumed by the form generator (DESIGN §7). */
667
+ readonly control: string;
668
+ readonly multiline: boolean;
669
+ }
670
+ declare const FIELD_TYPES: Readonly<Record<FieldType, FieldTypeDef>>;
671
+ declare function isFieldType(value: string): value is FieldType;
672
+ declare function valueKindOf(type: FieldType): ValueKind;
673
+
674
+ /**
675
+ * Contextual token inference (DESIGN §6.2). A token's control type is inferred from
676
+ * *where it sits*, via a precedence cascade:
677
+ *
678
+ * annotation → element+attribute rule → attribute-name rule → element rule → default
679
+ *
680
+ * Inference only covers the *obvious* cases, a heading is short text, an `<img src>` is
681
+ * an image, an `<a href>` is a link, everything else is plain text. The non-obvious
682
+ * controls, **rich text, date, and time**, are deliberately *not* inferred (no "why is
683
+ * this div rich text?" surprise); they're opt-in via `data-type` (§6.5), e.g.
684
+ * `<div data-type="richtext">{body}</div>`, `<div data-type="date">{published}</div>`.
685
+ */
686
+ interface TokenContext {
687
+ /** Lowercased tag name of the element the token sits in (text) or on (attribute). */
688
+ readonly tag: string;
689
+ /** Attribute name if the token is in an attribute; undefined if it's text content. */
690
+ readonly attribute?: string;
691
+ /** The token is the entire attribute value / text content (not a fragment of it). */
692
+ readonly wholeValue: boolean;
693
+ /** A `data-type` annotation on the element, if present (the escape hatch, §6.5). */
694
+ readonly annotation?: string;
695
+ /** Literal attribute text before the token, lowercased, to spot `mailto:`/`tel:`. */
696
+ readonly valuePrefix?: string;
697
+ /** Whether the element carries a `download` attribute (`<a … download>`). */
698
+ readonly download?: boolean;
699
+ }
700
+ /** Infer the control type for a single token occurrence. */
701
+ declare function inferControl(ctx: TokenContext): FieldType;
702
+
703
+ /**
704
+ * Token parsing (DESIGN §6). A token is `{name}` or a dotted `{item.title}`.
705
+ * Dotted-ness alone distinguishes an author field (`{title}`) from a value bound
706
+ * to the current loop item (`{item.title}`), DESIGN §6.3, so no extra syntax
707
+ * is needed.
708
+ */
709
+ interface TokenRef {
710
+ /** The raw match including braces, e.g. "{item.title}". */
711
+ readonly raw: string;
712
+ /** The first path segment, e.g. "item" (the page field name when not dotted). */
713
+ readonly name: string;
714
+ /** All dotted segments, e.g. ["item", "title"]. */
715
+ readonly path: readonly string[];
716
+ /** True when dotted, a loop-bound value, not a page field. */
717
+ readonly dotted: boolean;
718
+ }
719
+ /** Find every token occurrence in a string, in source order. */
720
+ declare function findTokens(input: string): TokenRef[];
721
+ /** If the trimmed input is exactly one token, return it; otherwise null. */
722
+ declare function wholeValueToken(input: string): TokenRef | null;
723
+
724
+ /**
725
+ * The authoring reference (DESIGN §14). A single, generated description of the whole
726
+ * template surface, every field type, annotation, loop, and token, that powers three
727
+ * faces at once: the editor's in-app Syntax panel, a downloadable `.md` an author can
728
+ * hand to an LLM, and (later) the MCP server's "how to author" resource. Generated, not
729
+ * hand-maintained, so it can never drift from the engine that's actually running: the
730
+ * field-type section is built from {@link FIELD_TYPES}, and a test asserts it covers
731
+ * every registered type. This is the CLAUDE.md §1 tie-breaker in action, one source of
732
+ * truth removes the concept of "docs you keep in sync."
733
+ */
734
+ /** One documented item, a field type, an annotation, a loop construct. */
735
+ interface ReferenceEntry {
736
+ readonly name: string;
737
+ readonly summary: string;
738
+ /** A minimal, idiomatic template snippet (rendered as an HTML code block). */
739
+ readonly example?: string;
740
+ /** A short supplementary line (e.g. the site's own collection list). */
741
+ readonly detail?: string;
742
+ }
743
+ interface ReferenceSection {
744
+ readonly title: string;
745
+ readonly intro: string;
746
+ readonly entries: readonly ReferenceEntry[];
747
+ }
748
+ interface AuthoringReference {
749
+ readonly title: string;
750
+ readonly intro: string;
751
+ readonly sections: readonly ReferenceSection[];
752
+ }
753
+ /** Optional, site-specific facts that make the reference concrete (DESIGN §14). */
754
+ interface ReferenceContext {
755
+ /** Folder paths that can be looped over or picked from (e.g. `/blog`). */
756
+ readonly collections?: readonly string[];
757
+ /** Component tag names available in the site (e.g. `hero`, `card`). */
758
+ readonly components?: readonly string[];
759
+ }
760
+ /**
761
+ * Build the structured reference. Pure: with no context it describes the grammar; given a
762
+ * site's {@link ReferenceContext} it also lists that site's real collections and components.
763
+ */
764
+ declare function buildAuthoringReference(context?: ReferenceContext): AuthoringReference;
765
+ /**
766
+ * Project the structured reference to GitHub-flavoured Markdown, the download an author
767
+ * hands to an LLM, and the same content the engine and editor agree on.
768
+ */
769
+ declare function renderReferenceMarkdown(reference: AuthoringReference): string;
770
+
771
+ /**
772
+ * Aggregate artifacts (DESIGN §15 step 7): sitemap, RSS feed, and a lean content
773
+ * manifest. All are pure functions of the published pages, so output stays
774
+ * deterministic, dates come from the content, never the wall clock (CLAUDE.md §2).
775
+ */
776
+ interface PageEntry {
777
+ /** Content path, e.g. "blog/going-static". */
778
+ readonly path: string;
779
+ /** Filename slug (last segment). */
780
+ readonly slug: string;
781
+ /** Site-relative URL, e.g. "/blog/going-static/". */
782
+ readonly url: string;
783
+ readonly title: string;
784
+ readonly published?: string;
785
+ readonly updated?: string;
786
+ readonly summary?: string;
787
+ }
788
+ interface RssOptions {
789
+ readonly baseUrl: string;
790
+ readonly title: string;
791
+ readonly description: string;
792
+ /** Collection content path the feed covers, e.g. "blog". */
793
+ readonly collection: string;
794
+ }
795
+ /** Plain-text content of an HTML fragment (collapses whitespace), for search/RSS. */
796
+ declare function textContent(html: string): string;
797
+ /** sitemap.xml over every published page (DESIGN §9 canonical URLs). */
798
+ declare function buildSitemap(entries: readonly PageEntry[], baseUrl: string): Artifact;
799
+ /** RSS 2.0 feed for a collection, newest first. */
800
+ declare function buildRss(entries: readonly PageEntry[], options: RssOptions): Artifact;
801
+ /**
802
+ * A lean, public content manifest the dev owns (DESIGN §14): a JSON array of every
803
+ * published page as `{ url, title, published?, summary? }`, in tree order (deterministic).
804
+ * Emitted at the site root as `content.json`; the dev builds search, Fuse or anything,
805
+ * from it however they like, rather than the engine shipping a pre-baked full-text index.
806
+ */
807
+ declare function buildContentIndex(entries: readonly PageEntry[]): Artifact;
808
+
809
+ /**
810
+ * Derive the editor's form from a template, "the template is the schema"
811
+ * (DESIGN §5.5/§6). Walking the same parse the compiler uses, each page-scope
812
+ * token becomes one field whose control is inferred from where it sits, deduped by
813
+ * name and labelled. This is the single source of truth the editor's form
814
+ * generator and the compiler share (DESIGN §6.7).
815
+ *
816
+ * Inference is **component-aware**: when a component is used (`<doc file="{x}">`),
817
+ * the bound field's type comes from how that prop is used *inside* the component,
818
+ * not from the attribute's position on the usage. A component's prop types are
819
+ * simply the field types of its own body, so the same derivation resolves them,
820
+ * recursively, through nested components, with a cycle guard.
821
+ */
822
+ interface DerivedField {
823
+ readonly name: string;
824
+ readonly type: FieldType;
825
+ readonly label: string;
826
+ readonly required: boolean;
827
+ readonly multiline: boolean;
828
+ readonly help?: string;
829
+ /** Minimum character length (`data-minlength`); the gate warns when shorter. */
830
+ readonly minLength?: number;
831
+ /** Maximum character length (`data-maxlength`); the gate warns when longer. */
832
+ readonly maxLength?: number;
833
+ /** A curated multi-reference (a `data-each` loop field). */
834
+ readonly multiple?: boolean;
835
+ /** Collection the reference picker is scoped to (`data-pick-from`). */
836
+ readonly pickFrom?: string;
837
+ }
838
+ /** Author-declared length bounds on a field value (`data-minlength`/`data-maxlength`). */
839
+ interface LengthConstraints {
840
+ readonly minLength?: number;
841
+ readonly maxLength?: number;
842
+ }
843
+ declare function deriveFields(template: string, components?: ComponentMap): DerivedField[];
844
+
845
+ /**
846
+ * Static analysis of a template's structure, used by the validation gate, the
847
+ * media phase, and (later) the editor. Extracts the signals downstream needs:
848
+ * required fields, looped collections, curated references, and image/file fields.
849
+ *
850
+ * Component-aware (like `deriveFields`): a field bound to a component prop
851
+ * (`<doc image="{x}">`) is categorised by how that prop is used inside the
852
+ * component, so the media phase processes it correctly.
853
+ */
854
+ interface TemplateAnalysis {
855
+ readonly requiredFields: readonly string[];
856
+ readonly queryLoopPaths: readonly string[];
857
+ readonly curatedLoopFields: readonly string[];
858
+ readonly imageFields: readonly string[];
859
+ readonly fileFields: readonly string[];
860
+ /** Fields injected as HTML (DESIGN §6.4), where authors embed inline media. */
861
+ readonly richTextFields: readonly string[];
862
+ /** Author-declared length bounds per field (`data-minlength`/`data-maxlength`). */
863
+ readonly constraints: ReadonlyMap<string, LengthConstraints>;
864
+ /** Internal `ref:` targets written into this template's own markup (DESIGN §5.7). */
865
+ readonly references: readonly string[];
866
+ }
867
+ declare function analyzeTemplate(source: string, components?: ComponentMap): TemplateAnalysis;
868
+
869
+ /**
870
+ * The validation gate (DESIGN §10). Its purpose is **referential integrity**, the
871
+ * site never ships a broken link. Errors block a publish; warnings inform. Results
872
+ * are returned as data (not thrown), so a host can present the clear list a user
873
+ * needs (CLAUDE.md §2: "3 things would break: …").
874
+ */
875
+ type Severity = 'error' | 'warning';
876
+ interface Diagnostic {
877
+ readonly severity: Severity;
878
+ readonly code: string;
879
+ readonly message: string;
880
+ /** Content path of the item/dir the diagnostic concerns, when applicable. */
881
+ readonly path?: string;
882
+ }
883
+ interface ValidationResult {
884
+ readonly diagnostics: readonly Diagnostic[];
885
+ readonly errors: readonly Diagnostic[];
886
+ readonly warnings: readonly Diagnostic[];
887
+ /** True when there are no errors, i.e. a publish may proceed. */
888
+ readonly ok: boolean;
889
+ }
890
+ declare function validateSite(source: ContentSource): Promise<ValidationResult>;
891
+
892
+ /**
893
+ * The pre-build hook port (DESIGN §11/CLAUDE.md §2). Runs once inside the publish
894
+ * pipeline, after the validation gate has passed and before compile, so a host can
895
+ * invoke the site's own asset build (Tailwind, esbuild, etc.) and have its output
896
+ * picked up by the engine's `public/` passthrough.
897
+ *
898
+ * The engine never reaches a shell, that lives in a host-side adapter (e.g.
899
+ * `ShellPreBuildHook` in `@nanoesis/adapter-shell`). A failed hook throws; the
900
+ * orchestrator propagates that exactly like a compile error (sink untouched, no
901
+ * purge, the live site is left as it was).
902
+ */
903
+ interface PreBuildHook {
904
+ /** Run the hook once. Throw to abort the publish, the sink stays untouched. */
905
+ run(): Promise<void>;
906
+ }
907
+
908
+ /**
909
+ * The CDN-purge port (DESIGN §3, §11). After a publish writes the new site, the host
910
+ * invalidates the CDN so readers see it. Phase 1 purges *everything* in one call
911
+ * (DESIGN §11), no per-path batching, no manifest. Per-path purge is a Phase-2
912
+ * concern (DESIGN §12).
913
+ */
914
+ interface PurgeService {
915
+ /** Invalidate the entire cache for the published site. */
916
+ purgeAll(): Promise<void>;
917
+ }
918
+ /**
919
+ * A {@link PurgeService} that does nothing, the right choice for a local build, a
920
+ * CDN-less deploy, and the M4 local example (no Cloudflare account). Publishing still
921
+ * "purges"; there is simply nothing to invalidate.
922
+ */
923
+ declare const noopPurgeService: PurgeService;
924
+
925
+ /**
926
+ * The artifact-sink port (DESIGN §3, §11, §11c). The publish pipeline emits every
927
+ * compiled output, HTML pages, image variants, copied public files, through this single
928
+ * `write`, so the engine never touches a filesystem or blob store directly (CLAUDE.md
929
+ * §2/§3). Publish does not wipe: it emits the full set and returns the written paths
930
+ * (DESIGN §11c), so clearing or pruning the target is the adopter's job, done by the host
931
+ * around the publish, not a step the engine owns.
932
+ *
933
+ * Paths are POSIX-style and relative to the published root (e.g. "blog/first/index.html").
934
+ */
935
+ interface ArtifactSink {
936
+ /** Write one artifact, creating any intermediate structure the backing store needs. */
937
+ write(path: string, contents: string | Uint8Array): Promise<void>;
938
+ }
939
+ /**
940
+ * An in-memory {@link ArtifactSink} backed by a flat path→contents map. This is the
941
+ * test double the publish-orchestration tests write into, the LSP guarantee that
942
+ * sinks are interchangeable (the filesystem and blob sinks must behave identically).
943
+ */
944
+ declare class InMemoryArtifactSink implements ArtifactSink {
945
+ private readonly store;
946
+ write(path: string, contents: string | Uint8Array): Promise<void>;
947
+ /** The artifacts written so far, path→contents, for assertions in tests. */
948
+ get written(): ReadonlyMap<string, string | Uint8Array>;
949
+ }
950
+
951
+ interface PublishOptions extends CompileSiteOptions {
952
+ /** CDN purge run once after upload; defaults to a no-op (DESIGN §11). */
953
+ readonly purge?: PurgeService;
954
+ /** Passthrough static-file directory copied verbatim to the root (default "public"). */
955
+ readonly publicDir?: string;
956
+ /**
957
+ * Optional host-supplied build hook (e.g. Tailwind). Runs once after the gate passes
958
+ * and before compile, so its output lands in `public/` in time for the passthrough.
959
+ * A throwing hook aborts the publish; the sink is left untouched.
960
+ */
961
+ readonly prebuild?: PreBuildHook;
962
+ /** Max sink writes in flight at once (default {@link DEFAULT_WRITE_CONCURRENCY}). */
963
+ readonly writeConcurrency?: number;
964
+ }
965
+ interface PublishResult {
966
+ /** True when the validation gate passed and the site was published. */
967
+ readonly ok: boolean;
968
+ /** The validation gate's findings (DESIGN §10), present whether or not it passed. */
969
+ readonly validation: ValidationResult;
970
+ /** Output paths written to the sink, sorted; empty when validation blocked the publish. */
971
+ readonly written: readonly string[];
972
+ }
973
+ /**
974
+ * Run the Phase-1 publish pipeline (DESIGN §11) through ports: **validate → compile →
975
+ * write → purge**. The pipeline is host-agnostic, the same orchestration drives a local
976
+ * filesystem host (e.g. `host-mcp`: fs sink, no-op purge) and the Azure Function host (blob
977
+ * sink, Cloudflare purge). Publish only emits artifacts and returns the full written-path list, clearing
978
+ * or pruning the target is the adopter's job (DESIGN §11c), not a step here.
979
+ *
980
+ * A failed validation gate is returned as data, not thrown (CLAUDE.md §2): nothing is
981
+ * written, nothing is purged, and `ok` is false with the diagnostics attached. This is
982
+ * the "3 things would break: …" contract.
983
+ *
984
+ * `public/` files are read through the same {@link ContentSource} and written verbatim
985
+ * to the published root, after the compiled pages, so a `public/` file deliberately
986
+ * shadows a compiled output of the same path (matching the local build).
987
+ */
988
+ declare function publishSite(source: ContentSource, sink: ArtifactSink, options?: PublishOptions): Promise<PublishResult>;
989
+
990
+ /**
991
+ * The identity port (DESIGN §11). The engine has *no auth logic*, auth is purely an
992
+ * HTTP-layer concern that lives in the host, but the port type lives here alongside
993
+ * the other port interfaces (`ContentSource`, `ArtifactSink`, `PurgeService`,
994
+ * `ImageEncoder`, `PreBuildHook`). Adapters implement this port without depending on
995
+ * any host package, exactly mirroring how the storage and CDN adapters depend on the
996
+ * engine's port types rather than a specific host.
997
+ *
998
+ * Hosts re-export these symbols for ergonomics (so a BYO user imports the identity
999
+ * surface from the host package they're already using). The engine itself never reads
1000
+ * a request, never checks a role, it compiles data to data.
1001
+ */
1002
+ /**
1003
+ * The authorization roles (DESIGN §11). Roles are a **set**, a principal may hold
1004
+ * several. They map onto the editor's surfaces:
1005
+ * - `author`, the Content workspace (content authoring).
1006
+ * - `developer`, the Templates workspace (templates, components, public files).
1007
+ * - `admin`, everything: implies `author` + `developer`, and additionally owns
1008
+ * user management and editor settings/branding.
1009
+ * A user can be author-only, developer-only, both, or admin (which subsumes both).
1010
+ */
1011
+ type Role = 'author' | 'developer' | 'admin';
1012
+ interface Principal {
1013
+ /** Stable identifier (opaque, e.g. a UUID for local-jwt, the gateway's id otherwise). */
1014
+ readonly userId: string;
1015
+ /** Human-readable handle for display (the account menu). Not a stable key, userId is. */
1016
+ readonly username: string;
1017
+ readonly roles: readonly Role[];
1018
+ }
1019
+ interface IdentityProvider {
1020
+ /** Resolve a request (via its header getter) to a principal, or null if anonymous. */
1021
+ authenticate(getHeader: (name: string) => string | undefined): Promise<Principal | null>;
1022
+ }
1023
+ /** Whether a principal holds a role; `admin` implies every role. */
1024
+ declare function hasRole(principal: Principal | null, role: Role): boolean;
1025
+ /** Whether a principal may use any editing surface (author, developer, or admin). */
1026
+ declare function canEdit(principal: Principal | null): boolean;
1027
+ /**
1028
+ * The HTTP-side credential-management surface (DESIGN §11). Providers that own
1029
+ * passwords/tokens (e.g. the local-JWT adapter) expose this; the host mounts each
1030
+ * method as a route. Trusted-header / external-IdP providers omit it, the gateway
1031
+ * does login. Like {@link IdentityProvider}, the engine declares the contract but has
1032
+ * no auth logic; the host wires it and the adapter implements it.
1033
+ */
1034
+ interface AuthEndpoints {
1035
+ /** Verify credentials, issue a fresh token. On first-run, bootstraps an admin. */
1036
+ login(req: LoginRequest): Promise<AuthResult<LoginSuccess>>;
1037
+ /** Verify the current token, issue a fresh one with the same revocation counter. */
1038
+ refresh(token: string): Promise<AuthResult<RefreshSuccess>>;
1039
+ /** Invalidate every token issued so far for the caller; idempotent. */
1040
+ logout(token: string): Promise<AuthResult<{
1041
+ readonly ok: true;
1042
+ }>>;
1043
+ /** Status info for a login UI (e.g. is this the first-run-becomes-admin path?). */
1044
+ state(): Promise<{
1045
+ readonly firstRun: boolean;
1046
+ }>;
1047
+ /**
1048
+ * Verify the caller's current password and replace it. The host calls this after
1049
+ * authenticating the request and passes the principal's userId, the adapter trusts
1050
+ * that the caller is who they say they are. Bumps loginCount (so other sessions die)
1051
+ * and returns a fresh token for the caller to swap in.
1052
+ */
1053
+ changePassword(userId: string, req: ChangePasswordRequest): Promise<AuthResult<ChangePasswordSuccess>>;
1054
+ /**
1055
+ * Mint a long-lived **personal access token** for `userId`, e.g. for the MCP HTTP
1056
+ * transport, where a token lives in a client's config. Optional: providers that don't
1057
+ * own tokens omit it (the route 404s). Unlike a session token it is *independent of the
1058
+ * login session*, it survives logins/logouts so a paste-once token keeps working, and is
1059
+ * revoked only by {@link revokeTokens} (or deleting the user). It still grants the user's
1060
+ * current roles, so the editor's role gate applies unchanged.
1061
+ */
1062
+ createToken?(userId: string): Promise<AuthResult<CreateTokenSuccess>>;
1063
+ /** Revoke every personal access token for `userId`; idempotent. Optional, paired with
1064
+ * {@link createToken}. Does not touch the user's login sessions. */
1065
+ revokeTokens?(userId: string): Promise<AuthResult<{
1066
+ readonly ok: true;
1067
+ }>>;
1068
+ }
1069
+ interface ChangePasswordRequest {
1070
+ readonly currentPassword: string;
1071
+ readonly newPassword: string;
1072
+ }
1073
+ interface ChangePasswordSuccess {
1074
+ /**
1075
+ * A fresh JWT. Changing the password bumped loginCount, so the caller's prior JWT
1076
+ * is now invalid, the client must replace it to continue making requests.
1077
+ */
1078
+ readonly token: string;
1079
+ }
1080
+ interface CreateTokenSuccess {
1081
+ /** The minted personal access token. Shown to the user once; treated like a password. */
1082
+ readonly token: string;
1083
+ /** Expiry as epoch milliseconds, so a UI can show "expires on …". */
1084
+ readonly expiresAt: number;
1085
+ }
1086
+ interface LoginRequest {
1087
+ readonly username: string;
1088
+ readonly password: string;
1089
+ }
1090
+ interface LoginSuccess {
1091
+ readonly token: string;
1092
+ readonly principal: Principal;
1093
+ /** True when this call created the first admin (first-run mode). */
1094
+ readonly firstRunBootstrap: boolean;
1095
+ }
1096
+ interface RefreshSuccess {
1097
+ readonly token: string;
1098
+ /** The same principal the token represents, saves the client a separate /me call. */
1099
+ readonly principal: Principal;
1100
+ }
1101
+ /**
1102
+ * Structured response shape, success carries `value`, failure carries an HTTP
1103
+ * `status` plus a short machine-readable `error`. Matches the rest of the host
1104
+ * (boundary failures are data, not exceptions).
1105
+ */
1106
+ type AuthResult<TOk> = {
1107
+ readonly ok: true;
1108
+ readonly value: TOk;
1109
+ } | {
1110
+ readonly ok: false;
1111
+ readonly status: number;
1112
+ readonly error: string;
1113
+ };
1114
+ /**
1115
+ * The admin-only user-management surface (DESIGN §11, Phase B). Separate from
1116
+ * {@link AuthEndpoints} per ISP: a provider that owns credentials (the local-JWT
1117
+ * adapter) may also offer user administration, but a host that only needs login
1118
+ * shouldn't depend on the management methods. Like the other auth ports the engine
1119
+ * only declares it, the host mounts each method as an **admin-gated** route and
1120
+ * passes the *caller's* userId so the adapter can enforce the self-edit guard;
1121
+ * trusted-header / external-IdP deploys omit it (users are managed upstream).
1122
+ *
1123
+ * Two invariants the adapter must hold (both tested independently): **never zero
1124
+ * admins** (a delete or demotion removing the last `admin` is rejected) and **no
1125
+ * self-edit of role/existence** (an admin can't change their own role or delete
1126
+ * their own account). Role changes, password resets, and deletes all revoke the
1127
+ * target's existing sessions (DESIGN §11 `loginCount`).
1128
+ */
1129
+ interface UserAdminEndpoints {
1130
+ /** Every user, password hashes omitted, in a deterministic order. */
1131
+ listUsers(): Promise<AuthResult<readonly UserSummary[]>>;
1132
+ /** Create a user with an admin-set initial password. Rejects a duplicate username. */
1133
+ createUser(req: CreateUserRequest): Promise<AuthResult<UserSummary>>;
1134
+ /**
1135
+ * Change a user's roles. `callerId` is the requesting admin, for the self-edit guard.
1136
+ * A role change bumps the target's `loginCount` (their stale token, which carries the
1137
+ * old roles, stops working, they re-auth with the new roles).
1138
+ */
1139
+ updateUser(callerId: string, targetId: string, req: UpdateUserRequest): Promise<AuthResult<UserSummary>>;
1140
+ /** Set a new password for a user (admin recovery). Bumps `loginCount` (kills their sessions). */
1141
+ resetPassword(targetId: string, req: ResetPasswordRequest): Promise<AuthResult<{
1142
+ readonly ok: true;
1143
+ }>>;
1144
+ /** Remove a user. `callerId` is for the self-edit guard; also honours never-zero-admins. */
1145
+ deleteUser(callerId: string, targetId: string): Promise<AuthResult<{
1146
+ readonly ok: true;
1147
+ }>>;
1148
+ }
1149
+ /** A user as exposed to admin tooling, never includes the password hash. */
1150
+ interface UserSummary {
1151
+ readonly id: string;
1152
+ readonly username: string;
1153
+ /**
1154
+ * Optional human-friendly name for bylines and the account menu. Absent means
1155
+ * "fall back to {@link username}", consumers resolve `displayName ?? username`.
1156
+ * The raw (possibly absent) value is carried here so an edit form shows what's
1157
+ * actually set rather than the defaulted-to username.
1158
+ */
1159
+ readonly displayName?: string;
1160
+ readonly roles: readonly Role[];
1161
+ /** Epoch ms; record creation time. */
1162
+ readonly createdAt: number;
1163
+ /** Epoch ms; last write time. */
1164
+ readonly updatedAt: number;
1165
+ /** Derived: a lockout is currently in effect (brute-force protection). */
1166
+ readonly locked: boolean;
1167
+ }
1168
+ interface CreateUserRequest {
1169
+ readonly username: string;
1170
+ readonly password: string;
1171
+ readonly roles: readonly Role[];
1172
+ /** Optional display name; trimmed, and blank is treated as unset. */
1173
+ readonly displayName?: string;
1174
+ }
1175
+ interface UpdateUserRequest {
1176
+ readonly roles?: readonly Role[];
1177
+ /**
1178
+ * Optional display name. `undefined` leaves it unchanged; an empty (or blank)
1179
+ * string clears it back to the username fallback. A displayName-only change is
1180
+ * cosmetic and does not revoke the user's sessions.
1181
+ */
1182
+ readonly displayName?: string;
1183
+ }
1184
+ interface ResetPasswordRequest {
1185
+ readonly newPassword: string;
1186
+ }
1187
+
1188
+ /**
1189
+ * URL and output-path generation (DESIGN §9). Directory-style URLs are derived
1190
+ * from the tree and served via the portable index.html-in-a-folder pattern, so no
1191
+ * host rewrite rules are needed. A folder's `index` item is its section landing
1192
+ * page. Canonical URLs carry a trailing slash; the other form is redirected.
1193
+ */
1194
+ /**
1195
+ * The output file path for an item's content path.
1196
+ * "about" → "about/index.html"
1197
+ * "blog/first" → "blog/first/index.html"
1198
+ * "blog/index" → "blog/index.html" (section landing page)
1199
+ * "index" → "index.html" (site root)
1200
+ */
1201
+ declare function outputPathForItem(contentPath: string): string;
1202
+ /**
1203
+ * The canonical URL (trailing slash) for an item's content path.
1204
+ * "about" → "/about/"
1205
+ * "blog/first" → "/blog/first/"
1206
+ * "blog/index" → "/blog/"
1207
+ * "index" → "/"
1208
+ */
1209
+ declare function urlForItem(contentPath: string): string;
1210
+
1211
+ /**
1212
+ * Context-aware escaping (DESIGN §6.4). Token substitution is never naive
1213
+ * find/replace, each context escapes differently, which is what keeps generated
1214
+ * artifacts injection-safe.
1215
+ *
1216
+ * In the compiler, HTML-text and attribute escaping mostly come *for free* from
1217
+ * re-serialising the parse5 AST. These helpers cover the cases the serializer
1218
+ * can't: building markup by hand, JSON-LD raw-text (`<script>` content is not
1219
+ * HTML-escaped), and neutralising dangerous URL schemes.
1220
+ */
1221
+ /** Escape a value for an HTML text node. */
1222
+ declare function escapeHtmlText(value: string): string;
1223
+ /** Escape a value for a double-quoted HTML attribute. */
1224
+ declare function escapeHtmlAttribute(value: string): string;
1225
+ /**
1226
+ * Escape a value for *inside* a JSON string literal (the quotes are already in
1227
+ * the template: `"name": "{title}"`). `She said "hi"` becomes `She said \"hi\"`.
1228
+ */
1229
+ declare function escapeJsonStringContent(value: string): string;
1230
+ /**
1231
+ * Neutralise dangerous URL schemes in a whole-value `href`/`src`. Control
1232
+ * characters and whitespace are stripped before sniffing the scheme, smuggling
1233
+ * them into `java\nscript:` is a classic bypass. Returns `#` for a blocked URL,
1234
+ * otherwise the original value unchanged.
1235
+ */
1236
+ declare function sanitizeUrl(url: string): string;
1237
+
1238
+ export { type Artifact, type ArtifactSink, type AuthEndpoints, type AuthResult, type AuthorDirectory, type AuthorEntry, type AuthorOption, type AuthorRef, type AuthoringReference, type BlobStore, type BoundItem, type ChangePasswordRequest, type ChangePasswordSuccess, type CollectionConfig, type CollectionQuery, type CompileInput, type CompilePageOptions, type CompileSiteOptions, type CompiledPage, type ComponentMap, type ContentIndex, type ContentItem, ContentParseError, type ContentSource, type CreateTokenSuccess, type CreateUserRequest, DEFAULT_DIRS, DOCUMENT_SHELL, type DerivedField, type Diagnostic, type DirEntry, type DirNode, type EncodeRequest, type EncodedImage, type EncodedVariant, type EntryKind, FIELD_TYPES, type FieldPrimitive, type FieldRecord, type FieldType, type FieldTypeDef, type FieldValue, type IdentityProvider, type ImageEncoder, type ImageFormat, type ImageInfo, InMemoryArtifactSink, InMemoryBlobStore, InMemoryContentSource, IndexedStore, type ItemNode, type LengthConstraints, type LoginRequest, type LoginSuccess, type MediaResolver, type PageEntry, type PreBuildHook, type Principal, type PublishOptions, type PublishResult, type PurgeService, type RedirectRule, type ReferenceContext, type ReferenceEntry, type ReferenceSection, type RefreshSuccess, type RenameResult, type ResetPasswordRequest, type ResolveContext, type Role, type RssOptions, type Scope, type Severity, type SiteConfig, type SortFile, type TemplateAnalysis, type TokenContext, type TokenRef, type TreeNode, type UpdateUserRequest, type UserAdminEndpoints, type UserSummary, type ValidationResult, type ValueKind, type WorkingStore, analyzeTemplate, buildAuthoringReference, buildContentIndex, buildPictureMarkup, buildRedirects, buildResolveContext, buildRss, buildSitemap, canEdit, compilePage, compileSite, compileTemplate, contentHash, contentTypeFor, deriveFields, emptyIndex, escapeHtmlAttribute, escapeHtmlText, escapeJsonStringContent, findTokens, hasRole, humanize, inferControl, isFieldType, joinAuthors, loadComponentScripts, loadComponentStyles, loadComponents, loadContentTree, loadDocumentShell, loadIndex, loadRedirects, loadSiteConfig, loadTemplate, noopPurgeService, outputPathForItem, parseContentItem, parseRedirects, parseSortFile, processImage, publishSite, renderAuthors, renderReferenceMarkdown, sanitizeUrl, saveIndex, slugify, textContent, toAuthorRefs, urlForItem, validateSite, valueKindOf, wholeValueToken };