satteri 0.8.0 → 0.9.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 (60) hide show
  1. package/README.md +35 -0
  2. package/dist/byte-writer.d.ts +31 -0
  3. package/dist/byte-writer.js +106 -0
  4. package/dist/child-stub.d.ts +17 -0
  5. package/dist/child-stub.js +57 -0
  6. package/dist/command-buffer.d.ts +23 -24
  7. package/dist/command-buffer.js +65 -128
  8. package/dist/compile.d.ts +5 -1
  9. package/dist/compile.js +40 -26
  10. package/dist/directive-types.d.ts +4 -2
  11. package/dist/generated/arena-layout.d.ts +37 -0
  12. package/dist/generated/arena-layout.js +41 -0
  13. package/dist/generated/wire-constants.d.ts +51 -0
  14. package/dist/generated/wire-constants.js +63 -0
  15. package/dist/handles.d.ts +21 -0
  16. package/dist/handles.js +1 -0
  17. package/dist/hast/child-stub.d.ts +18 -0
  18. package/dist/hast/child-stub.js +43 -0
  19. package/dist/hast/element-props.d.ts +2 -0
  20. package/dist/hast/element-props.js +23 -0
  21. package/dist/hast/generated/node-types.d.ts +14 -0
  22. package/dist/hast/generated/node-types.js +64 -0
  23. package/dist/hast/hast-materializer.d.ts +2 -1
  24. package/dist/hast/hast-materializer.js +9 -45
  25. package/dist/hast/hast-reader.d.ts +18 -23
  26. package/dist/hast/hast-reader.js +48 -115
  27. package/dist/hast/hast-visitor.d.ts +38 -12
  28. package/dist/hast/hast-visitor.js +417 -281
  29. package/dist/index.d.ts +10 -4
  30. package/dist/index.js +23 -1
  31. package/dist/lazy-child-resolver.d.ts +36 -0
  32. package/dist/lazy-child-resolver.js +95 -0
  33. package/dist/lazy-props.d.ts +1 -1
  34. package/dist/mdast/child-stub.d.ts +18 -0
  35. package/dist/mdast/child-stub.js +46 -0
  36. package/dist/mdast/column-align.d.ts +1 -0
  37. package/dist/mdast/column-align.js +6 -0
  38. package/dist/mdast/generated/layout.d.ts +19 -0
  39. package/dist/mdast/generated/layout.js +272 -0
  40. package/dist/mdast/generated/node-types.d.ts +58 -0
  41. package/dist/mdast/generated/node-types.js +266 -0
  42. package/dist/mdast/mdast-materializer.d.ts +3 -2
  43. package/dist/mdast/mdast-materializer.js +9 -93
  44. package/dist/mdast/mdast-reader.d.ts +4 -120
  45. package/dist/mdast/mdast-reader.js +45 -272
  46. package/dist/mdast/mdast-visitor.d.ts +55 -13
  47. package/dist/mdast/mdast-visitor.js +375 -410
  48. package/dist/mdx-attr.d.ts +2 -0
  49. package/dist/mdx-attr.js +20 -0
  50. package/dist/op-stream.d.ts +38 -0
  51. package/dist/op-stream.js +125 -0
  52. package/dist/phantom.d.ts +2 -0
  53. package/dist/phantom.js +13 -0
  54. package/dist/types.d.ts +34 -4
  55. package/dist/visitor-shared.d.ts +28 -0
  56. package/dist/visitor-shared.js +52 -0
  57. package/dist/wire-read.d.ts +27 -0
  58. package/dist/wire-read.js +59 -0
  59. package/index.js +52 -52
  60. package/package.json +6 -6
package/dist/compile.js CHANGED
@@ -3,6 +3,7 @@ import { visitMdastHandle, resolveMdastSubscriptions, } from "./mdast/mdast-visi
3
3
  import { createHastHandle, createMdxHastHandle, renderHandle, compileHandle, dropHandle, createMdastHandle, createMdxMdastHandle, applyCommandsToMdastHandle, convertMdastToHastHandle, getHandleSource, getMdastFrontmatter, serializeHandle, } from "#binding";
4
4
  import { MdastReader } from "./mdast/mdast-reader.js";
5
5
  import { materializeMdastTree } from "./mdast/mdast-materializer.js";
6
+ import { markHandleMutated } from "./lazy-child-resolver.js";
6
7
  import { HastReader } from "./hast/hast-reader.js";
7
8
  import { materializeHastTree } from "./hast/hast-materializer.js";
8
9
  /**
@@ -74,13 +75,20 @@ function featuresToNative(features) {
74
75
  }
75
76
  return { features: result, convertOptions };
76
77
  }
78
+ /** Drop a handle after bumping its epoch: the arena dies with the handle, so a
79
+ * child stub retained past the pipeline must hit the designed retention error,
80
+ * not a cryptic RangeError from snapshotting the freed arena. */
81
+ function disposeHandle(handle) {
82
+ markHandleMutated(handle);
83
+ dropHandle(handle);
84
+ }
77
85
  function warnDroppedTransforms(plugin, dropped, kind) {
78
86
  const name = plugin.name ?? "<anonymous>";
79
87
  const noun = dropped === 1 ? "transform" : "transforms";
80
88
  console.warn(`satteri: plugin "${name}" queued ${dropped} ${kind} ${noun} on node(s) that were removed or ` +
81
89
  `replaced earlier in the same pass; ${dropped === 1 ? "it was" : "they were"} dropped.`);
82
90
  }
83
- function runMdastPluginsOnHandle(handle, plugins, fileURL) {
91
+ function runMdastPluginsOnHandle(handle, plugins, fileURL, data) {
84
92
  // Each plugin runs once over the tree. A transform that passes a child
85
93
  // through (returning it inside the replacement) keeps that child's identity,
86
94
  // so a patch the same pass queued on it still applies — nesting composes in
@@ -88,10 +96,11 @@ function runMdastPluginsOnHandle(handle, plugins, fileURL) {
88
96
  // them up front, or hand off to a later plugin that sees the materialized tree.
89
97
  const runPlugin = (plugin) => {
90
98
  const subs = resolveMdastSubscriptions(plugin);
91
- const result = visitMdastHandle(handle, plugin, subs, () => getHandleSource(handle), fileURL);
99
+ const result = visitMdastHandle(handle, plugin, subs, () => getHandleSource(handle), fileURL, data);
92
100
  const apply = (r) => {
93
101
  if (!r.hasMutations)
94
102
  return;
103
+ markHandleMutated(handle);
95
104
  const dropped = applyCommandsToMdastHandle(handle, r.commandBuffer);
96
105
  if (dropped)
97
106
  warnDroppedTransforms(plugin, dropped, "mdast");
@@ -111,7 +120,7 @@ function runMdastPluginsOnHandle(handle, plugins, fileURL) {
111
120
  };
112
121
  return runNext();
113
122
  }
114
- function runHastPluginsOnHandle(handle, plugins, source, fileURL) {
123
+ function runHastPluginsOnHandle(handle, plugins, source, fileURL, data) {
115
124
  if (plugins.length === 0)
116
125
  return;
117
126
  let i = 0;
@@ -121,7 +130,7 @@ function runHastPluginsOnHandle(handle, plugins, source, fileURL) {
121
130
  i++;
122
131
  const plugin = typeof raw === "function" ? raw() : raw;
123
132
  const subs = resolveSubscriptions(plugin);
124
- const result = visitHastHandle(handle, plugin, subs, source, fileURL);
133
+ const result = visitHastHandle(handle, plugin, subs, source, fileURL, data);
125
134
  const warnIfDropped = (dropped) => {
126
135
  if (dropped)
127
136
  warnDroppedTransforms(plugin, dropped, "hast");
@@ -183,28 +192,29 @@ function mdxOptionsToNative(opts) {
183
192
  export function markdownToHtml(source, options = {}) {
184
193
  const { mdastPlugins = [], hastPlugins = [], features, fileURL } = options;
185
194
  const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features);
186
- const result = createHastHandleFromMdast(source, mdastPlugins, false, fileURL, nativeFeatures, nativeConvertOptions);
195
+ const data = {};
196
+ const result = createHastHandleFromMdast(source, mdastPlugins, false, fileURL, nativeFeatures, nativeConvertOptions, data);
187
197
  const renderAndDrop = (h, frontmatter) => {
188
198
  try {
189
199
  const html = renderHandle(h);
190
- return { html, frontmatter };
200
+ return { html, frontmatter, data };
191
201
  }
192
202
  finally {
193
- dropHandle(h);
203
+ disposeHandle(h);
194
204
  }
195
205
  };
196
206
  const runHastThenRender = (r) => {
197
207
  let hastResult;
198
208
  try {
199
- hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL);
209
+ hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL, data);
200
210
  }
201
211
  catch (err) {
202
- dropHandle(r.hastHandle);
212
+ disposeHandle(r.hastHandle);
203
213
  throw err;
204
214
  }
205
215
  if (hastResult instanceof Promise) {
206
216
  return hastResult.then(() => renderAndDrop(r.hastHandle, r.frontmatter), (err) => {
207
- dropHandle(r.hastHandle);
217
+ disposeHandle(r.hastHandle);
208
218
  throw err;
209
219
  });
210
220
  }
@@ -218,28 +228,29 @@ export function mdxToJs(source, options = {}) {
218
228
  const { mdastPlugins = [], hastPlugins = [], features, fileURL, ...mdxFields } = options;
219
229
  const mdxOptions = mdxOptionsToNative(mdxFields);
220
230
  const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features);
221
- const result = createHastHandleFromMdast(source, mdastPlugins, true, fileURL, nativeFeatures, nativeConvertOptions);
231
+ const data = {};
232
+ const result = createHastHandleFromMdast(source, mdastPlugins, true, fileURL, nativeFeatures, nativeConvertOptions, data);
222
233
  const compileAndDrop = (h, frontmatter) => {
223
234
  try {
224
235
  const code = compileHandle(h, mdxOptions);
225
- return { code, frontmatter };
236
+ return { code, frontmatter, data };
226
237
  }
227
238
  finally {
228
- dropHandle(h);
239
+ disposeHandle(h);
229
240
  }
230
241
  };
231
242
  const runHastThenCompile = (r) => {
232
243
  let hastResult;
233
244
  try {
234
- hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL);
245
+ hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL, data);
235
246
  }
236
247
  catch (err) {
237
- dropHandle(r.hastHandle);
248
+ disposeHandle(r.hastHandle);
238
249
  throw err;
239
250
  }
240
251
  if (hastResult instanceof Promise) {
241
252
  return hastResult.then(() => compileAndDrop(r.hastHandle, r.frontmatter), (err) => {
242
- dropHandle(r.hastHandle);
253
+ disposeHandle(r.hastHandle);
243
254
  throw err;
244
255
  });
245
256
  }
@@ -280,37 +291,40 @@ function createHastHandleFromMdast(source, mdastPlugins, mdx, fileURL,
280
291
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
281
292
  nativeFeatures,
282
293
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
283
- nativeConvertOptions) {
294
+ nativeConvertOptions, data) {
284
295
  const mdastHandle = mdx
285
296
  ? createMdxMdastHandle(source, nativeFeatures)
286
297
  : createMdastHandle(source, nativeFeatures);
287
- // finally{drop} is intentional: convertMdastToHastHandle empties the arena
298
+ // finally{dispose} is intentional: convertMdastToHastHandle empties the arena
288
299
  // on success, but if any step here throws the handle would otherwise leak.
289
300
  const finalize = (r) => {
290
301
  try {
291
302
  const frontmatter = readFrontmatter(r.handle);
303
+ // The conversion empties the mdast arena; bump the epoch so a stub
304
+ // retained past the mdast pass fails with the retention error.
305
+ markHandleMutated(r.handle);
292
306
  const hastHandle = convertMdastToHastHandle(r.handle, nativeConvertOptions);
293
307
  return { hastHandle, frontmatter };
294
308
  }
295
309
  finally {
296
- dropHandle(r.handle);
310
+ disposeHandle(r.handle);
297
311
  }
298
312
  };
299
313
  try {
300
314
  if (mdastPlugins.length === 0) {
301
315
  return finalize({ handle: mdastHandle });
302
316
  }
303
- const mdastResult = runMdastPluginsOnHandle(mdastHandle, mdastPlugins, fileURL);
317
+ const mdastResult = runMdastPluginsOnHandle(mdastHandle, mdastPlugins, fileURL, data);
304
318
  if (mdastResult instanceof Promise) {
305
319
  return mdastResult.then(finalize, (err) => {
306
- dropHandle(mdastHandle);
320
+ disposeHandle(mdastHandle);
307
321
  throw err;
308
322
  });
309
323
  }
310
324
  return finalize(mdastResult);
311
325
  }
312
326
  catch (err) {
313
- dropHandle(mdastHandle);
327
+ disposeHandle(mdastHandle);
314
328
  throw err;
315
329
  }
316
330
  }
@@ -322,7 +336,7 @@ export function markdownToMdast(source, options = {}) {
322
336
  return materializeMdastTree(new MdastReader(serializeHandle(handle)));
323
337
  }
324
338
  finally {
325
- dropHandle(handle);
339
+ disposeHandle(handle);
326
340
  }
327
341
  }
328
342
  /** Parse MDX source into a materialized mdast tree. */
@@ -332,7 +346,7 @@ export function mdxToMdast(source, options = {}) {
332
346
  return materializeMdastTree(new MdastReader(serializeHandle(handle)));
333
347
  }
334
348
  finally {
335
- dropHandle(handle);
349
+ disposeHandle(handle);
336
350
  }
337
351
  }
338
352
  /** Convert Markdown source to a materialized hast tree. */
@@ -343,7 +357,7 @@ export function markdownToHast(source, options = {}) {
343
357
  return materializeHastTree(new HastReader(serializeHandle(handle)));
344
358
  }
345
359
  finally {
346
- dropHandle(handle);
360
+ disposeHandle(handle);
347
361
  }
348
362
  }
349
363
  /** Convert MDX source to a materialized hast tree. */
@@ -354,6 +368,6 @@ export function mdxToHast(source, options = {}) {
354
368
  return materializeHastTree(new HastReader(serializeHandle(handle)));
355
369
  }
356
370
  finally {
357
- dropHandle(handle);
371
+ disposeHandle(handle);
358
372
  }
359
373
  }
@@ -1,5 +1,5 @@
1
1
  import type { BlockContent, Data as MdastData, DefinitionContent, Parent as MdastParent, PhrasingContent } from "mdast";
2
- export type DirectiveAttributes = Record<string, string>;
2
+ export type DirectiveAttributes = Record<string, string | null | undefined>;
3
3
  export interface ContainerDirective extends MdastParent {
4
4
  type: "containerDirective";
5
5
  name: string;
@@ -8,7 +8,6 @@ export interface ContainerDirective extends MdastParent {
8
8
  data?: ContainerDirectiveData | undefined;
9
9
  }
10
10
  export interface ContainerDirectiveData extends MdastData {
11
- directiveLabel?: boolean | undefined;
12
11
  }
13
12
  export interface LeafDirective extends MdastParent {
14
13
  type: "leafDirective";
@@ -33,6 +32,9 @@ declare module "mdast" {
33
32
  containerDirective: ContainerDirective;
34
33
  leafDirective: LeafDirective;
35
34
  }
35
+ interface ParagraphData {
36
+ directiveLabel?: boolean | null | undefined;
37
+ }
36
38
  interface PhrasingContentMap {
37
39
  textDirective: TextDirective;
38
40
  }
@@ -0,0 +1,37 @@
1
+ /** `b"MDAR"` magic, read as a little-endian u32. */
2
+ export declare const ARENA_MAGIC = 1380009037;
3
+ /** `Arena<K>` kind tags carried in the header's `kind` field. */
4
+ export declare const KIND_MDAST = 1;
5
+ export declare const KIND_HAST = 2;
6
+ /** `ArenaNode` `#[repr(C)]` field byte offsets (u32 fields; `node_type` is a u8). */
7
+ export declare const FIELD: {
8
+ readonly id: 0;
9
+ readonly node_type: 4;
10
+ readonly parent: 8;
11
+ readonly start_offset: 12;
12
+ readonly end_offset: 16;
13
+ readonly start_line: 20;
14
+ readonly start_column: 24;
15
+ readonly end_line: 28;
16
+ readonly end_column: 32;
17
+ readonly children_start: 36;
18
+ readonly children_count: 40;
19
+ readonly data_offset: 44;
20
+ readonly data_len: 48;
21
+ };
22
+ /** Raw-buffer header byte offsets (4-byte fields, u32 LE). */
23
+ export declare const HEADER: {
24
+ readonly magic: 0;
25
+ readonly kind: 4;
26
+ readonly node_struct_size: 8;
27
+ readonly node_count: 12;
28
+ readonly nodes_offset: 16;
29
+ readonly children_count: 20;
30
+ readonly children_offset: 24;
31
+ readonly type_data_len: 28;
32
+ readonly type_data_offset: 32;
33
+ readonly source_len: 36;
34
+ readonly source_offset: 40;
35
+ readonly node_data_count: 44;
36
+ readonly node_data_offset: 48;
37
+ };
@@ -0,0 +1,41 @@
1
+ // @generated by `cargo run -p satteri-layout-codegen`. Do not edit by hand.
2
+ // Arena raw-buffer layout, declared once in `satteri-layout-codegen/src/schema.rs`.
3
+ // The Rust side is pinned to these offsets by the compile-time asserts in
4
+ // `satteri-arena/src/generated/layout.rs`.
5
+ /** `b"MDAR"` magic, read as a little-endian u32. */
6
+ export const ARENA_MAGIC = 0x5241444d;
7
+ /** `Arena<K>` kind tags carried in the header's `kind` field. */
8
+ export const KIND_MDAST = 1;
9
+ export const KIND_HAST = 2;
10
+ /** `ArenaNode` `#[repr(C)]` field byte offsets (u32 fields; `node_type` is a u8). */
11
+ export const FIELD = {
12
+ id: 0,
13
+ node_type: 4,
14
+ parent: 8,
15
+ start_offset: 12,
16
+ end_offset: 16,
17
+ start_line: 20,
18
+ start_column: 24,
19
+ end_line: 28,
20
+ end_column: 32,
21
+ children_start: 36,
22
+ children_count: 40,
23
+ data_offset: 44,
24
+ data_len: 48,
25
+ };
26
+ /** Raw-buffer header byte offsets (4-byte fields, u32 LE). */
27
+ export const HEADER = {
28
+ magic: 0,
29
+ kind: 4,
30
+ node_struct_size: 8,
31
+ node_count: 12,
32
+ nodes_offset: 16,
33
+ children_count: 20,
34
+ children_offset: 24,
35
+ type_data_len: 28,
36
+ type_data_offset: 32,
37
+ source_len: 36,
38
+ source_offset: 40,
39
+ node_data_count: 44,
40
+ node_data_offset: 48,
41
+ };
@@ -0,0 +1,51 @@
1
+ export declare const OP_OPEN = 1;
2
+ export declare const OP_CLOSE = 2;
3
+ export declare const OP_REF = 3;
4
+ export declare const OP_KEEP_CHILDREN = 4;
5
+ export declare const OP_STR = 5;
6
+ export declare const OP_U8 = 6;
7
+ export declare const OP_U32 = 7;
8
+ export declare const OP_BOOL = 8;
9
+ export declare const OP_DATA = 9;
10
+ export declare const OP_PROP = 10;
11
+ export declare const OP_ALIGN = 11;
12
+ export declare const OF_VALUE = 0;
13
+ export declare const OF_URL = 1;
14
+ export declare const OF_TITLE = 2;
15
+ export declare const OF_ALT = 3;
16
+ export declare const OF_LANG = 4;
17
+ export declare const OF_META = 5;
18
+ export declare const OF_IDENTIFIER = 6;
19
+ export declare const OF_LABEL = 7;
20
+ export declare const OF_NAME = 8;
21
+ export declare const OF_REFERENCE_TYPE = 9;
22
+ export declare const OF_DEPTH = 10;
23
+ export declare const OF_CHECKED = 11;
24
+ export declare const OF_START = 12;
25
+ export declare const OF_ORDERED = 13;
26
+ export declare const OF_SPREAD = 14;
27
+ export declare const OF_TAGNAME = 15;
28
+ export declare const OF_EXPLICIT = 16;
29
+ export declare const CMD_REMOVE = 1;
30
+ export declare const CMD_INSERT_BEFORE = 5;
31
+ export declare const CMD_INSERT_AFTER = 6;
32
+ export declare const CMD_PREPEND_CHILD = 7;
33
+ export declare const CMD_APPEND_CHILD = 8;
34
+ export declare const CMD_WRAP = 9;
35
+ export declare const CMD_REPLACE = 11;
36
+ export declare const CMD_SET_PROPERTY = 12;
37
+ export declare const CMD_SET_CHILDREN = 13;
38
+ export declare const PAYLOAD_RAW_MARKDOWN = 16;
39
+ export declare const PAYLOAD_RAW_HTML = 17;
40
+ export declare const PAYLOAD_OPSTREAM = 20;
41
+ export declare const PROP_STRING = 0;
42
+ export declare const PROP_BOOL_TRUE = 1;
43
+ export declare const PROP_BOOL_FALSE = 2;
44
+ export declare const PROP_SPACE_SEP = 3;
45
+ export declare const PROP_COMMA_SEP = 4;
46
+ export declare const PROP_INT = 5;
47
+ export declare const PROP_NULL = 6;
48
+ export declare const MDX_ATTR_BOOLEAN_PROP = 0;
49
+ export declare const MDX_ATTR_LITERAL_PROP = 1;
50
+ export declare const MDX_ATTR_EXPRESSION_PROP = 2;
51
+ export declare const MDX_ATTR_SPREAD = 3;
@@ -0,0 +1,63 @@
1
+ // @generated by `cargo run -p satteri-layout-codegen`. Do not edit by hand.
2
+ // JS<->Rust wire-protocol byte values, declared once in
3
+ // `satteri-layout-codegen/src/schema.rs`. Rust twins live in
4
+ // `satteri-plugin-api/src/generated/wire_constants.rs` and
5
+ // `satteri-ast/src/generated/wire_constants.rs`.
6
+ // Op-stream op codes (JS `OpWriter` -> Rust `replay_opstream`).
7
+ export const OP_OPEN = 0x01; // [type: u8]
8
+ export const OP_CLOSE = 0x02;
9
+ export const OP_REF = 0x03; // [id: u32 LE] — splice an existing node
10
+ export const OP_KEEP_CHILDREN = 0x04; // splice the anchor node's original children
11
+ export const OP_STR = 0x05; // [field: u8][len: u32 LE][utf8]
12
+ export const OP_U8 = 0x06; // [field: u8][value: u8]
13
+ export const OP_U32 = 0x07; // [field: u8][value: u32 LE]
14
+ export const OP_BOOL = 0x08; // [field: u8][0|1]
15
+ export const OP_DATA = 0x09; // [len: u32 LE][json utf8]
16
+ export const OP_PROP = 0x0a; // [name str][kind: u8][value str] — HAST element property
17
+ export const OP_ALIGN = 0x0b; // [len: u32 LE][ColumnAlign bytes] — table column alignment
18
+ // Op-stream field ids (single namespace across OP_STR/OP_U8/OP_U32/OP_BOOL).
19
+ export const OF_VALUE = 0;
20
+ export const OF_URL = 1;
21
+ export const OF_TITLE = 2;
22
+ export const OF_ALT = 3;
23
+ export const OF_LANG = 4;
24
+ export const OF_META = 5;
25
+ export const OF_IDENTIFIER = 6;
26
+ export const OF_LABEL = 7;
27
+ export const OF_NAME = 8; // directive / MDX JSX element name
28
+ export const OF_REFERENCE_TYPE = 9;
29
+ export const OF_DEPTH = 10;
30
+ export const OF_CHECKED = 11;
31
+ export const OF_START = 12;
32
+ export const OF_ORDERED = 13;
33
+ export const OF_SPREAD = 14;
34
+ export const OF_TAGNAME = 15; // HAST element tag name
35
+ export const OF_EXPLICIT = 16; // MDX JSX `_mdxExplicitJsx` flag
36
+ // Command bytes (0x01–0x0F range). Each is followed by [nodeId: u32 LE];
37
+ // structural commands then carry [payloadType: u8][payload…].
38
+ export const CMD_REMOVE = 0x01;
39
+ export const CMD_INSERT_BEFORE = 0x05;
40
+ export const CMD_INSERT_AFTER = 0x06;
41
+ export const CMD_PREPEND_CHILD = 0x07;
42
+ export const CMD_APPEND_CHILD = 0x08;
43
+ export const CMD_WRAP = 0x09;
44
+ export const CMD_REPLACE = 0x0b;
45
+ export const CMD_SET_PROPERTY = 0x0c; // [valueType: u8][name str][value str], PROP_* value kinds
46
+ export const CMD_SET_CHILDREN = 0x0d; // payload is a Root-wrapped child list
47
+ // Structural-command payload types (0x10+, a range distinct from commands).
48
+ export const PAYLOAD_RAW_MARKDOWN = 0x10; // [len: u32 LE][utf8] — re-parsed as markdown
49
+ export const PAYLOAD_RAW_HTML = 0x11; // [len: u32 LE][utf8] — re-parsed as HTML/MDX
50
+ export const PAYLOAD_OPSTREAM = 0x14; // [len: u32 LE][op bytes] — replayed straight into the arena
51
+ // Property value kinds (HAST element properties and SET_PROPERTY commands).
52
+ export const PROP_STRING = 0; // UTF-8 value
53
+ export const PROP_BOOL_TRUE = 1; // no value bytes
54
+ export const PROP_BOOL_FALSE = 2; // no value bytes
55
+ export const PROP_SPACE_SEP = 3; // space-separated list (UTF-8)
56
+ export const PROP_COMMA_SEP = 4; // comma-separated list (UTF-8)
57
+ export const PROP_INT = 5; // decimal string, parsed to i64
58
+ export const PROP_NULL = 6; // no value bytes
59
+ // MDX JSX attribute kinds (MDAST and HAST MDX JSX element type_data).
60
+ export const MDX_ATTR_BOOLEAN_PROP = 0; // name only, no value
61
+ export const MDX_ATTR_LITERAL_PROP = 1; // name="literal"
62
+ export const MDX_ATTR_EXPRESSION_PROP = 2; // name={expr}
63
+ export const MDX_ATTR_SPREAD = 3; // {...expr}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Opaque handles to Rust-owned arenas. The napi-generated `index.d.ts` refers
3
+ * to `MdastHandle`/`HastHandle`/`AnyHandle` as bare, undeclared names; these
4
+ * global declarations give those signatures real types. The kind brand makes
5
+ * passing a hast handle to an mdast entry point (a runtime error in Rust) a
6
+ * compile error in TS.
7
+ */
8
+ declare global {
9
+ interface MdastHandle {
10
+ readonly __satteriHandleKind: "mdast";
11
+ }
12
+ interface HastHandle {
13
+ readonly __satteriHandleKind: "hast";
14
+ }
15
+ /** Handle accepted by kind-agnostic entry points (drop, serialize, …). */
16
+ type AnyHandle = MdastHandle | HastHandle;
17
+ }
18
+ type MdastHandleAlias = MdastHandle;
19
+ type HastHandleAlias = HastHandle;
20
+ type AnyHandleAlias = AnyHandle;
21
+ export type { MdastHandleAlias as MdastHandle, HastHandleAlias as HastHandle, AnyHandleAlias as AnyHandle, };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { LazyChildResolver } from "../lazy-child-resolver.js";
2
+ import type { HastNode } from "../types.js";
3
+ import type { HastReader } from "./hast-reader.js";
4
+ type HastResolver = LazyChildResolver<HastReader, HastNode>;
5
+ /**
6
+ * Walk-path child stub: arena id + `type` eagerly, every other field a lazy
7
+ * forward to the materialized node (first read snapshots the arena via
8
+ * `materializeOne`, which enforces the handle epoch — the pass seal is checked
9
+ * where the stubs are built). Spread/identity rules are enforced by `nid()`
10
+ * in hast-visitor.ts.
11
+ */
12
+ export declare class HastChildStub {
13
+ _resolver: HastResolver;
14
+ _id: number;
15
+ type: string;
16
+ constructor(resolver: HastResolver, id: number, nodeType: number);
17
+ }
18
+ export {};
@@ -0,0 +1,43 @@
1
+ import { flatByTag, stubDescriptors } from "../child-stub.js";
2
+ import { NAME_TO_TYPE, TYPE_NAMES } from "./generated/node-types.js";
3
+ const N = NAME_TO_TYPE;
4
+ /** Per-type stub fields, mirroring `materializeHastNode`'s switch. */
5
+ const HAST_STUB_FIELDS = {
6
+ [N.root]: ["children"],
7
+ [N.element]: ["tagName", "properties", "children"],
8
+ [N.text]: ["value"],
9
+ [N.comment]: ["value"],
10
+ [N.doctype]: [],
11
+ [N.raw]: ["value"],
12
+ [N.mdxJsxFlowElement]: ["name", "attributes", "children"],
13
+ [N.mdxJsxTextElement]: ["name", "attributes", "children"],
14
+ [N.mdxFlowExpression]: ["value"],
15
+ [N.mdxTextExpression]: ["value"],
16
+ [N.mdxjsEsm]: ["value"],
17
+ };
18
+ const TYPE_NAME_BY_TAG = flatByTag(TYPE_NAMES);
19
+ const HAST_STUB_DESCRIPTORS = [];
20
+ for (const tag of Object.keys(HAST_STUB_FIELDS)) {
21
+ const nodeType = Number(tag);
22
+ HAST_STUB_DESCRIPTORS[nodeType] = stubDescriptors(HAST_STUB_FIELDS[nodeType]);
23
+ }
24
+ /** Unknown node types still expose the prelude-backed lazy fields. */
25
+ const FALLBACK_DESCRIPTORS = stubDescriptors([]);
26
+ /**
27
+ * Walk-path child stub: arena id + `type` eagerly, every other field a lazy
28
+ * forward to the materialized node (first read snapshots the arena via
29
+ * `materializeOne`, which enforces the handle epoch — the pass seal is checked
30
+ * where the stubs are built). Spread/identity rules are enforced by `nid()`
31
+ * in hast-visitor.ts.
32
+ */
33
+ export class HastChildStub {
34
+ _resolver;
35
+ _id;
36
+ type;
37
+ constructor(resolver, id, nodeType) {
38
+ this._resolver = resolver;
39
+ this._id = id;
40
+ this.type = TYPE_NAME_BY_TAG[nodeType] ?? `unknown(${nodeType})`;
41
+ Object.defineProperties(this, HAST_STUB_DESCRIPTORS[nodeType] ?? FALLBACK_DESCRIPTORS);
42
+ }
43
+ }
@@ -0,0 +1,2 @@
1
+ export type HastPropertyValue = string | number | boolean | string[];
2
+ export declare function decodeElementProp(kind: number, value: string): HastPropertyValue;
@@ -0,0 +1,23 @@
1
+ /** Decode a HAST element property value from its wire `(kind, value)` — shared
2
+ * by the walk decoder and the snapshot reader so the kind dispatch lives once.
3
+ * The bool kinds carry no value string (callers pass `""`). */
4
+ import { PROP_BOOL_TRUE, PROP_BOOL_FALSE, PROP_SPACE_SEP, PROP_COMMA_SEP, PROP_INT, } from "../generated/wire-constants.js";
5
+ export function decodeElementProp(kind, value) {
6
+ switch (kind) {
7
+ case PROP_BOOL_TRUE:
8
+ return true;
9
+ case PROP_BOOL_FALSE:
10
+ return false;
11
+ case PROP_SPACE_SEP:
12
+ return value.split(" ").filter((s) => s.length > 0);
13
+ case PROP_COMMA_SEP:
14
+ return value
15
+ .split(",")
16
+ .map((s) => s.trim())
17
+ .filter((s) => s.length > 0);
18
+ case PROP_INT:
19
+ return Number(value);
20
+ default:
21
+ return value; // PROP_STRING
22
+ }
23
+ }
@@ -0,0 +1,14 @@
1
+ /** Node-type tag -> canonical AST name. */
2
+ export declare const TYPE_NAMES: Readonly<Record<number, string>>;
3
+ /** Canonical AST name -> node-type tag. */
4
+ export declare const NAME_TO_TYPE: Readonly<Record<string, number>>;
5
+ /** Names a plugin can subscribe to (every node except `root`). */
6
+ export declare const VISITOR_KEYS: ReadonlySet<string>;
7
+ /** Name -> tag for the types the op-stream can encode; one lookup gates AND
8
+ * resolves the emit-path tag. Excluded names (see `*_OPSTREAM_EXCLUDED` in
9
+ * schema.rs) have no encoding — the visitor throws for them. */
10
+ export declare const HAST_OPSTREAM_TYPES: Readonly<Record<string, number>>;
11
+ /** Names of the variable-length `custom` node types (hand-written or
12
+ * `Tail`-generated codec). The op-stream round-trip oracle asserts it covers
13
+ * every one, so a forgotten or drifted encode/decode arm fails loudly. */
14
+ export declare const HAST_CUSTOM_TYPES: readonly string[];
@@ -0,0 +1,64 @@
1
+ // @generated by `cargo run -p satteri-layout-codegen`. Do not edit by hand.
2
+ /** Node-type tag -> canonical AST name. */
3
+ export const TYPE_NAMES = {
4
+ 0: "root",
5
+ 1: "element",
6
+ 2: "text",
7
+ 3: "comment",
8
+ 4: "doctype",
9
+ 5: "raw",
10
+ 10: "mdxJsxFlowElement",
11
+ 11: "mdxJsxTextElement",
12
+ 12: "mdxFlowExpression",
13
+ 13: "mdxjsEsm",
14
+ 14: "mdxTextExpression",
15
+ };
16
+ /** Canonical AST name -> node-type tag. */
17
+ export const NAME_TO_TYPE = {
18
+ root: 0,
19
+ element: 1,
20
+ text: 2,
21
+ comment: 3,
22
+ doctype: 4,
23
+ raw: 5,
24
+ mdxJsxFlowElement: 10,
25
+ mdxJsxTextElement: 11,
26
+ mdxFlowExpression: 12,
27
+ mdxjsEsm: 13,
28
+ mdxTextExpression: 14,
29
+ };
30
+ /** Names a plugin can subscribe to (every node except `root`). */
31
+ export const VISITOR_KEYS = new Set([
32
+ "element",
33
+ "text",
34
+ "comment",
35
+ "doctype",
36
+ "raw",
37
+ "mdxJsxFlowElement",
38
+ "mdxJsxTextElement",
39
+ "mdxFlowExpression",
40
+ "mdxjsEsm",
41
+ "mdxTextExpression",
42
+ ]);
43
+ /** Name -> tag for the types the op-stream can encode; one lookup gates AND
44
+ * resolves the emit-path tag. Excluded names (see `*_OPSTREAM_EXCLUDED` in
45
+ * schema.rs) have no encoding — the visitor throws for them. */
46
+ export const HAST_OPSTREAM_TYPES = {
47
+ element: 1,
48
+ text: 2,
49
+ comment: 3,
50
+ raw: 5,
51
+ mdxJsxFlowElement: 10,
52
+ mdxJsxTextElement: 11,
53
+ mdxFlowExpression: 12,
54
+ mdxjsEsm: 13,
55
+ mdxTextExpression: 14,
56
+ };
57
+ /** Names of the variable-length `custom` node types (hand-written or
58
+ * `Tail`-generated codec). The op-stream round-trip oracle asserts it covers
59
+ * every one, so a forgotten or drifted encode/decode arm fails loudly. */
60
+ export const HAST_CUSTOM_TYPES = [
61
+ "element",
62
+ "mdxJsxFlowElement",
63
+ "mdxJsxTextElement",
64
+ ];
@@ -1,4 +1,5 @@
1
1
  import { HastReader } from "./hast-reader.js";
2
+ import type { Root } from "hast";
2
3
  import type { HastNode } from "../types.js";
3
4
  export type { HastNode };
4
5
  /**
@@ -8,4 +9,4 @@ export declare function materializeHastNode(reader: HastReader, nodeId: number):
8
9
  /**
9
10
  * Materialize the full HAST tree from root (nodeId=0).
10
11
  */
11
- export declare function materializeHastTree(reader: HastReader): HastNode;
12
+ export declare function materializeHastTree(reader: HastReader): Root;