satteri 0.10.4 → 0.11.1

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 (89) hide show
  1. package/README.md +11 -0
  2. package/dist/arena-reader.d.ts +13 -0
  3. package/dist/arena-reader.js +104 -0
  4. package/dist/binding.browser.d.ts +1 -1
  5. package/dist/binding.browser.js +1 -1
  6. package/dist/binding.d.ts +1 -1
  7. package/dist/binding.js +1 -1
  8. package/dist/byte-writer.d.ts +0 -18
  9. package/dist/byte-writer.js +6 -30
  10. package/dist/child-stub.d.ts +0 -17
  11. package/dist/child-stub.js +6 -22
  12. package/dist/command-buffer.d.ts +5 -26
  13. package/dist/command-buffer.js +28 -39
  14. package/dist/compile-options.d.ts +11 -0
  15. package/dist/compile-options.js +99 -0
  16. package/dist/compile-types.d.ts +330 -0
  17. package/dist/compile-types.js +1 -0
  18. package/dist/compile.d.ts +22 -328
  19. package/dist/compile.js +129 -379
  20. package/dist/directive-types.d.ts +1 -0
  21. package/dist/directive-types.js +1 -6
  22. package/dist/generated/arena-layout.d.ts +6 -4
  23. package/dist/generated/arena-layout.js +6 -7
  24. package/dist/generated/fused-wire.d.ts +3 -0
  25. package/dist/generated/fused-wire.js +229 -0
  26. package/dist/generated/parse-options.js +0 -5
  27. package/dist/generated/wire-constants.d.ts +3 -0
  28. package/dist/generated/wire-constants.js +33 -42
  29. package/dist/handles.d.ts +0 -7
  30. package/dist/hast/child-stub.d.ts +1 -8
  31. package/dist/hast/child-stub.js +14 -13
  32. package/dist/hast/element-props.d.ts +13 -0
  33. package/dist/hast/element-props.js +52 -6
  34. package/dist/hast/generated/node-types.d.ts +1 -9
  35. package/dist/hast/generated/node-types.js +1 -9
  36. package/dist/hast/generated/walk-decode.d.ts +10 -0
  37. package/dist/hast/generated/walk-decode.js +61 -0
  38. package/dist/hast/hast-materializer.d.ts +3 -10
  39. package/dist/hast/hast-materializer.js +73 -47
  40. package/dist/hast/hast-reader.d.ts +9 -38
  41. package/dist/hast/hast-reader.js +82 -271
  42. package/dist/hast/hast-visitor.d.ts +29 -16
  43. package/dist/hast/hast-visitor.js +197 -508
  44. package/dist/hast/hast-walk.d.ts +18 -0
  45. package/dist/hast/hast-walk.js +264 -0
  46. package/dist/index.d.ts +5 -5
  47. package/dist/index.js +4 -12
  48. package/dist/lazy-child-resolver.d.ts +1 -21
  49. package/dist/lazy-child-resolver.js +11 -32
  50. package/dist/materializer-cache.d.ts +5 -29
  51. package/dist/materializer-cache.js +29 -101
  52. package/dist/mdast/child-stub.d.ts +1 -12
  53. package/dist/mdast/child-stub.js +18 -28
  54. package/dist/mdast/column-align.d.ts +1 -1
  55. package/dist/mdast/column-align.js +0 -2
  56. package/dist/mdast/generated/layout.d.ts +0 -16
  57. package/dist/mdast/generated/layout.js +6 -47
  58. package/dist/mdast/generated/node-types.d.ts +1 -9
  59. package/dist/mdast/generated/node-types.js +3 -9
  60. package/dist/mdast/mdast-materializer.d.ts +3 -11
  61. package/dist/mdast/mdast-materializer.js +91 -63
  62. package/dist/mdast/mdast-reader.d.ts +15 -63
  63. package/dist/mdast/mdast-reader.js +132 -269
  64. package/dist/mdast/mdast-visitor.d.ts +23 -15
  65. package/dist/mdast/mdast-visitor.js +188 -294
  66. package/dist/mdast/mdast-walk.d.ts +13 -0
  67. package/dist/mdast/mdast-walk.js +144 -0
  68. package/dist/mdx-attr.js +0 -3
  69. package/dist/mdx-types.js +1 -6
  70. package/dist/op-stream.d.ts +1 -15
  71. package/dist/op-stream.js +3 -19
  72. package/dist/phantom.d.ts +0 -1
  73. package/dist/phantom.js +1 -9
  74. package/dist/plugin-pipeline.d.ts +32 -0
  75. package/dist/plugin-pipeline.js +177 -0
  76. package/dist/plugin.d.ts +8 -7
  77. package/dist/plugin.js +3 -9
  78. package/dist/types.d.ts +0 -1
  79. package/dist/visitor-node.d.ts +12 -0
  80. package/dist/visitor-node.js +23 -0
  81. package/dist/visitor-shared.d.ts +26 -30
  82. package/dist/visitor-shared.js +34 -36
  83. package/dist/wire-read.d.ts +0 -22
  84. package/dist/wire-read.js +3 -24
  85. package/index.d.ts +19 -10
  86. package/index.js +56 -53
  87. package/package.json +3 -12
  88. package/dist/string-pool.d.ts +0 -13
  89. package/dist/string-pool.js +0 -91
@@ -1,18 +1,19 @@
1
- /**
2
- * Shared materializer machinery for the HAST and MDAST flavors: per-reader
3
- * node memo, lazy `children` descriptors, and the frozen-mode (plugin walk
4
- * path) freeze rules.
5
- */
6
1
  import { deepFreeze } from "./freeze.js";
7
- /**
8
- * Build a memoizing materializer, memoized per `(reader, id)`.
9
- *
10
- * `node` materializes one node with lazy `children`; `frozen` (the plugin walk
11
- * path) deep-freezes every node at construction so plugins cannot corrupt the
12
- * shared cache. `tree` materializes a whole tree eagerly, which is what the
13
- * step-by-step API wants: it asked for the tree, so laziness would only add
14
- * per-node accessor overhead.
15
- */
2
+ export function installNodeData(node, rawData, label, nodeId) {
3
+ if (rawData === null)
4
+ return;
5
+ try {
6
+ const parsed = JSON.parse(rawData);
7
+ if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
8
+ node.data = parsed;
9
+ }
10
+ }
11
+ catch (err) {
12
+ if (process.env.NODE_ENV !== "production") {
13
+ console.warn(`${label}: malformed node_data for nodeId=${nodeId}`, err);
14
+ }
15
+ }
16
+ }
16
17
  export function createMaterializer(spec) {
17
18
  const readerCaches = new WeakMap();
18
19
  function materialize(reader, nodeId, frozen = false, refs) {
@@ -24,8 +25,7 @@ export function createMaterializer(spec) {
24
25
  }
25
26
  return node;
26
27
  }
27
- /** Frozen-mode `children`: memoized in `cache.childLists` because the node
28
- * is frozen, so the accessor cannot self-replace with a data property. */
28
+ // A frozen node cannot replace its children getter, so cache the array separately.
29
29
  function frozenChildrenDescriptor(reader, cache) {
30
30
  return {
31
31
  get() {
@@ -46,9 +46,7 @@ export function createMaterializer(spec) {
46
46
  enumerable: true,
47
47
  };
48
48
  }
49
- /** Mutable-mode `children`: self-replacing with a plain writable array on
50
- * first read. The id is captured rather than stored on the node, so a
51
- * materialized tree carries no marker for `toEqual` or a spread to find. */
49
+ // Capture the ID so mutable trees expose no arena identity marker.
52
50
  function mutableChildrenDescriptor(reader, nodeId) {
53
51
  return {
54
52
  get() {
@@ -88,16 +86,14 @@ export function createMaterializer(spec) {
88
86
  }
89
87
  return cache;
90
88
  }
91
- function buildNode(reader, cache, nodeId, nodeType, eager = false) {
89
+ function buildNode(reader, cache, nodeId, nodeType) {
92
90
  const typeName = spec.typeNames[nodeType] ?? `unknown(${nodeType})`;
93
- // Plain object, not a class: unified's `assertNode` rejects any other prototype.
91
+ // Unified's assertNode requires a plain-object prototype.
94
92
  const node = { type: typeName };
95
- const position = reader.getPosition(nodeId);
96
- if (position !== undefined) {
97
- node.position = position;
98
- }
93
+ // Populate before _nodeId so both materialization paths share V8 hidden classes.
94
+ spec.populate(node, reader, nodeId, nodeType);
99
95
  if (cache.frozen) {
100
- // Non-enumerable so `nid()` never trusts an id that a spread copied.
96
+ // Non-enumerable IDs prevent spread copies from impersonating arena nodes.
101
97
  cache.refs?.set(node, nodeId);
102
98
  Object.defineProperty(node, "_nodeId", {
103
99
  value: nodeId,
@@ -106,87 +102,19 @@ export function createMaterializer(spec) {
106
102
  enumerable: false,
107
103
  });
108
104
  }
109
- spec.populate(node, reader, nodeId, nodeType);
110
- // Plugins can set `data` on any node type, so rehydrate generically
111
- // (see website/content/docs/divergences.md for the code-block case).
112
- const rawData = reader.getNodeData(nodeId);
113
- if (rawData !== null) {
114
- try {
115
- const parsed = JSON.parse(rawData);
116
- if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
117
- Object.defineProperty(node, "data", {
118
- value: parsed,
119
- writable: true,
120
- configurable: true,
121
- enumerable: true,
122
- });
123
- }
124
- }
125
- catch (err) {
126
- if (process.env.NODE_ENV !== "production") {
127
- console.warn(`${spec.label}: malformed node_data for nodeId=${nodeId}`, err);
128
- }
129
- }
105
+ installNodeData(node, reader.getNodeData(nodeId), spec.label, nodeId);
106
+ if (cache.frozen) {
107
+ // All fields are eager until the children getter is installed below.
108
+ for (const value of Object.values(node))
109
+ deepFreeze(value);
130
110
  }
131
- if (!eager && spec.hasChildren(nodeType, node, reader, nodeId)) {
111
+ if (spec.hasChildren(nodeType, node, reader, nodeId)) {
132
112
  Object.defineProperty(node, "children", cache.children ?? mutableChildrenDescriptor(reader, nodeId));
133
113
  }
134
114
  if (cache.frozen) {
135
- // Deep-freeze the eager own values but not the lazy `children` accessor;
136
- // freeze eagerly even for containers so nothing is writable while cached.
137
- const descriptors = Object.getOwnPropertyDescriptors(node);
138
- for (const key of Object.keys(descriptors)) {
139
- const desc = descriptors[key];
140
- if (desc !== undefined && "value" in desc) {
141
- deepFreeze(desc.value);
142
- }
143
- }
144
115
  Object.freeze(node);
145
116
  }
146
117
  return node;
147
118
  }
148
- /**
149
- * Iterative on purpose: recursing to full document depth overflows on deeply
150
- * nested input, and nothing else here descends more than one level.
151
- *
152
- * Breadth-first over the parents still to fill, so every node is built and
153
- * every child list decoded exactly once, and a child is wired into its
154
- * parent's array as it is built rather than in a second pass.
155
- */
156
- function fillTree(reader, cache, rootId) {
157
- const rootType = reader.getNodeType(rootId);
158
- const root = buildNode(reader, cache, rootId, rootType, true);
159
- if (!spec.hasChildren(rootType, root, reader, rootId))
160
- return root;
161
- const parents = [root];
162
- const parentIds = [rootId];
163
- for (let p = 0; p < parentIds.length; p++) {
164
- const parent = parents[p];
165
- const parentId = parentIds[p];
166
- if (parent === undefined || parentId === undefined)
167
- continue;
168
- const ids = reader.getChildIds(parentId);
169
- const kids = new Array(ids.length);
170
- for (let i = 0; i < ids.length; i++) {
171
- const childId = ids[i];
172
- if (childId === undefined)
173
- continue;
174
- const childType = reader.getNodeType(childId);
175
- const child = buildNode(reader, cache, childId, childType, true);
176
- kids[i] = child;
177
- if (spec.hasChildren(childType, child, reader, childId)) {
178
- parents.push(child);
179
- parentIds.push(childId);
180
- }
181
- }
182
- // Assignment beats defineProperty here; `eager` left `children` uninstalled.
183
- parent.children = kids;
184
- }
185
- return root;
186
- }
187
- /** Whole tree at once: the caller will walk it, so lazy accessors cost more than they defer. */
188
- function materializeTree(reader, rootId) {
189
- return fillTree(reader, readerCache(reader, false, undefined), rootId);
190
- }
191
- return { node: materialize, tree: materializeTree };
119
+ return { node: materialize };
192
120
  }
@@ -2,19 +2,8 @@ import type { LazyChildResolver } from "../lazy-child-resolver.js";
2
2
  import type { MdastNode } from "../types.js";
3
3
  import type { MdastReader } from "./mdast-reader.js";
4
4
  type MdastResolver = LazyChildResolver<MdastReader, MdastNode>;
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). Spread/identity rules
9
- * are enforced by `nid()` (authoritative doc in hast-visitor.ts).
10
- *
11
- * A user-defined node's `type` is the one exception: it lives in the arena,
12
- * not the tag, so it joins the lazy fields rather than making every sibling
13
- * list snapshot the arena up front.
14
- */
15
5
  export declare class MdastChildStub {
16
- _resolver: MdastResolver;
17
- _id: number;
6
+ #private;
18
7
  type: string;
19
8
  constructor(resolver: MdastResolver, id: number, nodeType: number);
20
9
  }
@@ -3,8 +3,6 @@ import { MDAST_LAYOUT_KEYS } from "./generated/layout.js";
3
3
  import { NAME_TO_TYPE, TYPE_NAMES } from "./generated/node-types.js";
4
4
  import { LEAF_TYPES } from "./mdast-materializer.js";
5
5
  const N = NAME_TO_TYPE;
6
- /** Per-type stub fields for the types `addTypeProperties` hand-writes; the
7
- * fixed-layout types come from the generated `MDAST_LAYOUT_KEYS`. */
8
6
  const HAND_WRITTEN_FIELDS = {
9
7
  [N.list]: ["ordered", "start", "spread"],
10
8
  [N.listItem]: ["spread", "checked"],
@@ -17,9 +15,7 @@ const HAND_WRITTEN_FIELDS = {
17
15
  [N.mdxJsxTextElement]: ["name", "attributes"],
18
16
  };
19
17
  const TYPE_NAME_BY_TAG = flatByTag(TYPE_NAMES);
20
- /** Internal tag for user-defined nodes; its stored `name` field is folded into
21
- * the open `node.type`, so the stub exposes `value` (leaf content) but no
22
- * separate `name`. */
18
+ // Custom nodes expose their stored name as type, without a separate name field.
23
19
  const MDAST_CUSTOM = NAME_TO_TYPE.custom;
24
20
  const MDAST_STUB_DESCRIPTORS = [];
25
21
  for (const tag of Object.keys(TYPE_NAMES)) {
@@ -32,25 +28,15 @@ for (const tag of Object.keys(TYPE_NAMES)) {
32
28
  fields.push("children");
33
29
  MDAST_STUB_DESCRIPTORS[nodeType] = stubDescriptors(fields);
34
30
  }
35
- /** Unknown node types still expose the prelude-backed lazy fields. */
36
31
  const FALLBACK_DESCRIPTORS = stubDescriptors([]);
37
- /**
38
- * Walk-path child stub: arena id + `type` eagerly, every other field a lazy
39
- * forward to the materialized node (first read snapshots the arena via
40
- * `materializeOne`, which enforces the handle epoch). Spread/identity rules
41
- * are enforced by `nid()` (authoritative doc in hast-visitor.ts).
42
- *
43
- * A user-defined node's `type` is the one exception: it lives in the arena,
44
- * not the tag, so it joins the lazy fields rather than making every sibling
45
- * list snapshot the arena up front.
46
- */
32
+ // Custom type names live in the arena, so reading type must remain lazy too.
47
33
  export class MdastChildStub {
48
- _resolver;
49
- _id;
34
+ #resolver;
35
+ #id;
50
36
  type;
51
37
  constructor(resolver, id, nodeType) {
52
- this._resolver = resolver;
53
- this._id = id;
38
+ this.#resolver = resolver;
39
+ this.#id = id;
54
40
  if (nodeType === MDAST_CUSTOM) {
55
41
  installLazyCustomType(this);
56
42
  installLazyCustomChildren(this);
@@ -62,16 +48,22 @@ export class MdastChildStub {
62
48
  }
63
49
  /** @internal */
64
50
  get _refs() {
65
- return this._resolver.refs;
51
+ return this.#resolver.refs;
52
+ }
53
+ /** @internal */
54
+ get _id() {
55
+ return this.#id;
56
+ }
57
+ /** @internal */
58
+ _materialize() {
59
+ return this.#resolver.materializeOne(this.#id);
66
60
  }
67
61
  }
68
- /** `children` as a self-replacing accessor, and self-*removing* for a leaf: the
69
- * stub only knows the tag, so it cannot tell a leaf from a parent until the
70
- * arena snapshot says which shape this node has. */
62
+ // A custom node’s leafness is unknown until materialization; leaf stubs must lose their children field.
71
63
  function installLazyCustomChildren(stub) {
72
64
  Object.defineProperty(stub, "children", {
73
65
  get() {
74
- const real = this._resolver.materializeOne(this._id);
66
+ const real = this._materialize();
75
67
  const value = real.children;
76
68
  if (value === undefined) {
77
69
  delete this.children;
@@ -89,12 +81,10 @@ function installLazyCustomChildren(stub) {
89
81
  configurable: true,
90
82
  });
91
83
  }
92
- /** `type` as a self-replacing accessor: the materialized node folds the stored
93
- * name into `type`. Enumerable so a spread copy still carries it. */
94
84
  function installLazyCustomType(stub) {
95
85
  Object.defineProperty(stub, "type", {
96
86
  get() {
97
- const value = this._resolver.materializeOne(this._id).type;
87
+ const value = this._materialize().type;
98
88
  Object.defineProperty(this, "type", {
99
89
  value,
100
90
  writable: true,
@@ -1 +1 @@
1
- export declare function decodeColumnAlign(byte: number): string | null;
1
+ export declare function decodeColumnAlign(byte: number): "left" | "right" | "center" | null;
@@ -1,5 +1,3 @@
1
- /** Decode a table column-alignment byte — shared by the generated walk decoder
2
- * and the snapshot reader so the mapping lives in one place. */
3
1
  const ALIGN_NAMES = [null, "left", "right", "center"];
4
2
  export function decodeColumnAlign(byte) {
5
3
  return ALIGN_NAMES[byte] ?? null;
@@ -1,18 +1,2 @@
1
- import type { MdastReader } from "../mdast-reader.js";
2
- /** Materialized property names per tag (the non-skip `js` names above),
3
- * exported for the child-stub field tables. */
4
1
  export declare const MDAST_LAYOUT_KEYS: Readonly<Record<number, readonly string[]>>;
5
- /**
6
- * Decode a node's type-specific `type_data` from the walk buffer onto `node`,
7
- * driven by `MDAST_LAYOUTS` (fixed-field types) and `MDAST_TAILS` (counted
8
- * attribute lists). Returns `false` for tags in neither, so the caller falls
9
- * through to the remaining hand-written cases (list, listItem).
10
- */
11
2
  export declare function decodeMdastTypeData(view: DataView, buf: Uint8Array, start: number, nodeType: number, node: Record<string, unknown>): boolean;
12
- /**
13
- * Attach a node's fixed `type_data` fields, materialized from the arena
14
- * snapshot, driven by `MDAST_LAYOUTS`. Returns `false` for tags with no entry,
15
- * so the materializer falls through to its hand-written cases (list, table,
16
- * directives, MDX JSX). Fields are eager plain stores.
17
- */
18
- export declare function materializeMdastFields(reader: MdastReader, node: object, nodeId: number, nodeType: number): boolean;
@@ -3,7 +3,6 @@ import { ru16, ru32, rstr } from "../../wire-read.js";
3
3
  import { restorePhantomSpaces } from "../../phantom.js";
4
4
  import { decodeMdxJsxAttr } from "../../mdx-attr.js";
5
5
  import { decodeColumnAlign } from "../column-align.js";
6
- /** Walk-wire fields per node-type tag (see Rust `write_mdast_type_data_inline`). */
7
6
  const MDAST_LAYOUTS = {
8
7
  2: [{ js: "depth", offset: 0, kind: "u8", default: 1 }],
9
8
  7: [{ js: "value", offset: 0, kind: "str32" }],
@@ -64,8 +63,6 @@ const MDAST_LAYOUTS = {
64
63
  103: [{ js: "value", offset: 0, kind: "str32", phantom: true }],
65
64
  104: [{ js: "value", offset: 0, kind: "str32", phantom: true }],
66
65
  };
67
- /** Materialized property names per tag (the non-skip `js` names above),
68
- * exported for the child-stub field tables. */
69
66
  export const MDAST_LAYOUT_KEYS = {
70
67
  2: ["depth"],
71
68
  7: ["value"],
@@ -88,10 +85,6 @@ export const MDAST_LAYOUT_KEYS = {
88
85
  103: ["value"],
89
86
  104: ["value"],
90
87
  };
91
- /** Walk-wire tail descriptors (a name head, then a counted item list) for
92
- * the variable-length attribute types the generic decoder below handles
93
- * without a hand-written case: `map` is a string key/value object
94
- * (directives), `jsx` a typed MDX-JSX attribute array. */
95
88
  const MDAST_TAILS = {
96
89
  21: { head: [], item: [{ js: "align", kind: "u8" }], bytes: { attrsKey: "align" } },
97
90
  30: {
@@ -137,13 +130,12 @@ const MDAST_TAILS = {
137
130
  jsx: { attrsKey: "attributes" },
138
131
  },
139
132
  };
140
- /**
141
- * Decode a node's type-specific `type_data` from the walk buffer onto `node`,
142
- * driven by `MDAST_LAYOUTS` (fixed-field types) and `MDAST_TAILS` (counted
143
- * attribute lists). Returns `false` for tags in neither, so the caller falls
144
- * through to the remaining hand-written cases (list, listItem).
145
- */
146
133
  export function decodeMdastTypeData(view, buf, start, nodeType, node) {
134
+ if (nodeType === 7 || nodeType === 10 || nodeType === 13 || nodeType === 25 || nodeType === 26) {
135
+ const len = ru32(view, start);
136
+ node.value = rstr(buf, start + 4, len);
137
+ return true;
138
+ }
147
139
  const fields = MDAST_LAYOUTS[nodeType];
148
140
  if (fields !== undefined) {
149
141
  let pos = start;
@@ -170,9 +162,7 @@ export function decodeMdastTypeData(view, buf, start, nodeType, node) {
170
162
  const tail = MDAST_TAILS[nodeType];
171
163
  if (tail !== undefined) {
172
164
  let pos = start;
173
- // Reads are inlined and advance `pos` in place (no per-field tuple): this
174
- // runs per attribute item in the matched-node decode path, and the
175
- // unoptimized tiers CodSpeed measures won't elide the allocation.
165
+ // Inline reads avoid allocating a tuple per attribute field in unoptimized V8 tiers.
176
166
  for (const f of tail.head) {
177
167
  let value;
178
168
  if (f.kind === "u8") {
@@ -186,7 +176,6 @@ export function decodeMdastTypeData(view, buf, start, nodeType, node) {
186
176
  pos += len;
187
177
  value = f.phantom ? restorePhantomSpaces(raw) : raw;
188
178
  }
189
- // MDX JSX elements carry a nullable name (empty → null); map heads keep it.
190
179
  node[f.js] = tail.jsx !== undefined && value === "" ? null : value;
191
180
  }
192
181
  const count = ru16(view, pos);
@@ -236,33 +225,3 @@ export function decodeMdastTypeData(view, buf, start, nodeType, node) {
236
225
  }
237
226
  return false;
238
227
  }
239
- /**
240
- * Attach a node's fixed `type_data` fields, materialized from the arena
241
- * snapshot, driven by `MDAST_LAYOUTS`. Returns `false` for tags with no entry,
242
- * so the materializer falls through to its hand-written cases (list, table,
243
- * directives, MDX JSX). Fields are eager plain stores.
244
- */
245
- export function materializeMdastFields(reader, node, nodeId, nodeType) {
246
- const fields = MDAST_LAYOUTS[nodeType];
247
- if (fields === undefined)
248
- return false;
249
- const out = node;
250
- for (const f of fields) {
251
- if (f.skip)
252
- continue;
253
- if (f.kind === "u8") {
254
- // Short type_data falls back to the layout's declared default,
255
- // matching the Rust walk serializer.
256
- const b = reader.fieldU8(nodeId, f.offset, f.default ?? 0);
257
- out[f.js] = f.values ? (f.values[b] ?? f.values[0]) : b;
258
- }
259
- else {
260
- // Short type_data (e.g. a 20-byte ReferenceData-only imageReference)
261
- // reads as empty, mirroring the Rust decoders' bounds checks.
262
- const raw = reader.fieldString(nodeId, f.offset);
263
- const s = f.nullable && raw === "" ? null : raw;
264
- out[f.js] = f.phantom && typeof s === "string" ? restorePhantomSpaces(s) : s;
265
- }
266
- }
267
- return true;
268
- }
@@ -1,6 +1,4 @@
1
- /** Node-type tag -> canonical AST name. */
2
1
  export declare const TYPE_NAMES: Readonly<Record<number, string>>;
3
- /** Canonical AST name -> node-type tag. */
4
2
  export declare const NAME_TO_TYPE: Readonly<Record<string, number>>;
5
3
  /** Node-type tag by Rust enum variant name. */
6
4
  export declare const NodeType: Readonly<{
@@ -50,13 +48,7 @@ export declare const NodeType: Readonly<{
50
48
  }>;
51
49
  /** Node-type tag -> Rust variant name (`typeName` diagnostics). */
52
50
  export declare const NodeTypeName: Readonly<Record<number, string>>;
53
- /** Names a plugin can subscribe to (every node except `root`). */
54
51
  export declare const VISITOR_KEYS: ReadonlySet<string>;
55
- /** Name -> tag for the types the op-stream can encode; one lookup gates AND
56
- * resolves the emit-path tag. Excluded names (see `*_OPSTREAM_EXCLUDED` in
57
- * schema.rs) have no encoding — the visitor throws for them. */
58
52
  export declare const MDAST_OPSTREAM_TYPES: Readonly<Record<string, number>>;
59
- /** Names of the variable-length `custom` node types (hand-written or
60
- * `Tail`-generated codec). The op-stream round-trip oracle asserts it covers
61
- * every one, so a forgotten or drifted encode/decode arm fails loudly. */
62
53
  export declare const MDAST_CUSTOM_TYPES: readonly string[];
54
+ export declare const LEAF_TYPES: ReadonlySet<number>;
@@ -1,5 +1,4 @@
1
1
  // @generated by `cargo run -p satteri-layout-codegen`. Do not edit by hand.
2
- /** Node-type tag -> canonical AST name. */
3
2
  export const TYPE_NAMES = {
4
3
  0: "root",
5
4
  1: "paragraph",
@@ -45,7 +44,6 @@ export const TYPE_NAMES = {
45
44
  103: "mdxTextExpression",
46
45
  104: "mdxjsEsm",
47
46
  };
48
- /** Canonical AST name -> node-type tag. */
49
47
  export const NAME_TO_TYPE = {
50
48
  root: 0,
51
49
  paragraph: 1,
@@ -183,7 +181,6 @@ export const NodeTypeName = {
183
181
  103: "MdxTextExpression",
184
182
  104: "MdxjsEsm",
185
183
  };
186
- /** Names a plugin can subscribe to (every node except `root`). */
187
184
  export const VISITOR_KEYS = new Set([
188
185
  "paragraph",
189
186
  "heading",
@@ -228,9 +225,6 @@ export const VISITOR_KEYS = new Set([
228
225
  "mdxTextExpression",
229
226
  "mdxjsEsm",
230
227
  ]);
231
- /** Name -> tag for the types the op-stream can encode; one lookup gates AND
232
- * resolves the emit-path tag. Excluded names (see `*_OPSTREAM_EXCLUDED` in
233
- * schema.rs) have no encoding — the visitor throws for them. */
234
228
  export const MDAST_OPSTREAM_TYPES = {
235
229
  paragraph: 1,
236
230
  heading: 2,
@@ -275,9 +269,6 @@ export const MDAST_OPSTREAM_TYPES = {
275
269
  mdxTextExpression: 103,
276
270
  mdxjsEsm: 104,
277
271
  };
278
- /** Names of the variable-length `custom` node types (hand-written or
279
- * `Tail`-generated codec). The op-stream round-trip oracle asserts it covers
280
- * every one, so a forgotten or drifted encode/decode arm fails loudly. */
281
272
  export const MDAST_CUSTOM_TYPES = [
282
273
  "list",
283
274
  "listItem",
@@ -289,3 +280,6 @@ export const MDAST_CUSTOM_TYPES = [
289
280
  "mdxJsxFlowElement",
290
281
  "mdxJsxTextElement",
291
282
  ];
283
+ export const LEAF_TYPES = new Set([
284
+ 3, 7, 8, 9, 10, 13, 14, 16, 18, 20, 25, 26, 27, 28, 102, 103, 104,
285
+ ]);
@@ -1,19 +1,11 @@
1
1
  import type { Root } from "mdast";
2
2
  import type { MdastReader } from "./mdast-reader.js";
3
- export declare const LEAF_TYPES: ReadonlySet<number>;
4
- /** A `custom` node is a leaf when it has a non-empty `value` and no children or
5
- * `data.h*`. Leafness is per node there, not per type, so the read paths ask
6
- * this instead of {@link LEAF_TYPES}.
7
- * @see {@link Custom} */
3
+ import { LEAF_TYPES } from "./generated/node-types.js";
4
+ export { LEAF_TYPES };
8
5
  export declare function isCustomLeaf(node: {
9
6
  readonly value?: unknown;
10
7
  readonly data?: unknown;
11
8
  }, childCount: number): boolean;
12
- /**
13
- * Materialize a single MDAST node; scalars eager, `children` lazy, memoized
14
- * per `(reader, id)`; `frozen` (the plugin walk path) deep-freezes so plugins
15
- * cannot corrupt the shared cache.
16
- */
17
9
  export declare const materializeNode: (reader: MdastReader, nodeId: number, frozen?: boolean, refs?: import("../visitor-shared.js").NodeRefs) => import("mdast").Nodes;
18
- /** Materialize the full tree from root (nodeId=0). */
10
+ /** Materialize the full tree from root (nodeId=0) in one eager pass, breadth-first over the parents still to fill. */
19
11
  export declare function materializeMdastTree(reader: MdastReader): Root;