@hviana/sema 0.1.3 → 0.1.5

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 (123) hide show
  1. package/dist/example/demo.js +25 -25
  2. package/dist/example/train_base.d.ts +21 -19
  3. package/dist/example/train_base.js +1952 -1684
  4. package/dist/src/alphabet.d.ts +3 -3
  5. package/dist/src/alphabet.js +27 -24
  6. package/dist/src/alu/src/alu.d.ts +194 -169
  7. package/dist/src/alu/src/alu.js +414 -374
  8. package/dist/src/alu/src/expr.d.ts +43 -36
  9. package/dist/src/alu/src/expr.js +278 -252
  10. package/dist/src/alu/src/index.d.ts +98 -9
  11. package/dist/src/alu/src/index.js +63 -9
  12. package/dist/src/alu/src/kernel-arith.d.ts +5 -1
  13. package/dist/src/alu/src/kernel-arith.js +285 -195
  14. package/dist/src/alu/src/kernel-bits.d.ts +5 -1
  15. package/dist/src/alu/src/kernel-bits.js +115 -81
  16. package/dist/src/alu/src/kernel-logic.js +63 -33
  17. package/dist/src/alu/src/kernel-nd.js +195 -155
  18. package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
  19. package/dist/src/alu/src/kernel-numeric.js +331 -259
  20. package/dist/src/alu/src/operation.d.ts +132 -112
  21. package/dist/src/alu/src/operation.js +163 -149
  22. package/dist/src/alu/src/parser.d.ts +175 -168
  23. package/dist/src/alu/src/parser.js +421 -382
  24. package/dist/src/alu/src/resonance.d.ts +25 -16
  25. package/dist/src/alu/src/resonance.js +56 -49
  26. package/dist/src/alu/src/text.d.ts +7 -3
  27. package/dist/src/alu/src/text.js +37 -31
  28. package/dist/src/alu/src/value.d.ts +14 -14
  29. package/dist/src/alu/src/value.js +160 -150
  30. package/dist/src/alu/test/alu.test.js +656 -543
  31. package/dist/src/bytes.d.ts +5 -1
  32. package/dist/src/bytes.js +40 -33
  33. package/dist/src/config.d.ts +102 -102
  34. package/dist/src/config.js +83 -83
  35. package/dist/src/derive/src/deduction.d.ts +62 -60
  36. package/dist/src/derive/src/deduction.js +109 -103
  37. package/dist/src/derive/src/index.d.ts +7 -1
  38. package/dist/src/derive/src/priority-queue.d.ts +8 -8
  39. package/dist/src/derive/src/priority-queue.js +61 -57
  40. package/dist/src/derive/src/rewrite.d.ts +18 -15
  41. package/dist/src/derive/src/rewrite.js +67 -59
  42. package/dist/src/derive/src/trie.d.ts +56 -56
  43. package/dist/src/derive/src/trie.js +179 -175
  44. package/dist/src/derive/test/derive.test.js +100 -96
  45. package/dist/src/extension.d.ts +16 -13
  46. package/dist/src/geometry.d.ts +42 -17
  47. package/dist/src/geometry.js +266 -235
  48. package/dist/src/index.d.ts +17 -2
  49. package/dist/src/index.js +7 -1
  50. package/dist/src/ingest-cache.d.ts +30 -25
  51. package/dist/src/ingest-cache.js +127 -108
  52. package/dist/src/mind/articulation.d.ts +5 -1
  53. package/dist/src/mind/articulation.js +112 -77
  54. package/dist/src/mind/attention.d.ts +108 -40
  55. package/dist/src/mind/attention.js +871 -749
  56. package/dist/src/mind/canonical.d.ts +19 -4
  57. package/dist/src/mind/canonical.js +31 -27
  58. package/dist/src/mind/graph-search.d.ts +225 -201
  59. package/dist/src/mind/graph-search.js +821 -742
  60. package/dist/src/mind/index.d.ts +10 -2
  61. package/dist/src/mind/junction.d.ts +58 -31
  62. package/dist/src/mind/junction.js +237 -172
  63. package/dist/src/mind/learning.d.ts +52 -16
  64. package/dist/src/mind/learning.js +165 -143
  65. package/dist/src/mind/match.d.ts +69 -20
  66. package/dist/src/mind/match.js +318 -259
  67. package/dist/src/mind/mechanisms/alu.js +16 -16
  68. package/dist/src/mind/mechanisms/cast.d.ts +13 -9
  69. package/dist/src/mind/mechanisms/cast.js +456 -363
  70. package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
  71. package/dist/src/mind/mechanisms/confluence.js +183 -152
  72. package/dist/src/mind/mechanisms/cover.d.ts +8 -2
  73. package/dist/src/mind/mechanisms/cover.js +210 -148
  74. package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
  75. package/dist/src/mind/mechanisms/extraction.js +288 -234
  76. package/dist/src/mind/mechanisms/recall.d.ts +10 -6
  77. package/dist/src/mind/mechanisms/recall.js +185 -126
  78. package/dist/src/mind/mind.d.ts +154 -129
  79. package/dist/src/mind/mind.js +288 -258
  80. package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
  81. package/dist/src/mind/pipeline-mechanism.js +172 -161
  82. package/dist/src/mind/pipeline.d.ts +14 -4
  83. package/dist/src/mind/pipeline.js +189 -125
  84. package/dist/src/mind/primitives.d.ts +32 -8
  85. package/dist/src/mind/primitives.js +117 -99
  86. package/dist/src/mind/rationale.d.ts +98 -86
  87. package/dist/src/mind/rationale.js +121 -113
  88. package/dist/src/mind/reasoning.d.ts +13 -2
  89. package/dist/src/mind/reasoning.js +166 -129
  90. package/dist/src/mind/recognition.d.ts +4 -1
  91. package/dist/src/mind/recognition.js +208 -183
  92. package/dist/src/mind/resonance.d.ts +22 -5
  93. package/dist/src/mind/resonance.js +0 -0
  94. package/dist/src/mind/trace.d.ts +25 -6
  95. package/dist/src/mind/trace.js +58 -50
  96. package/dist/src/mind/traverse.d.ts +58 -16
  97. package/dist/src/mind/traverse.js +357 -304
  98. package/dist/src/mind/types.d.ts +127 -120
  99. package/dist/src/mind/types.js +69 -60
  100. package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
  101. package/dist/src/rabitq-hnsw/src/database.js +359 -342
  102. package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
  103. package/dist/src/rabitq-hnsw/src/heap.js +80 -75
  104. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
  105. package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
  106. package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
  107. package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
  108. package/dist/src/rabitq-hnsw/src/prng.js +27 -25
  109. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
  110. package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
  111. package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
  112. package/dist/src/rabitq-hnsw/src/store.js +843 -752
  113. package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
  114. package/dist/src/sema.d.ts +35 -0
  115. package/dist/src/sema.js +77 -0
  116. package/dist/src/store-sqlite.d.ts +166 -144
  117. package/dist/src/store-sqlite.js +660 -578
  118. package/dist/src/store.d.ts +630 -593
  119. package/dist/src/store.js +1581 -1440
  120. package/dist/src/vec.d.ts +9 -5
  121. package/dist/src/vec.js +73 -61
  122. package/package.json +2 -2
  123. package/src/sema.ts +102 -0
@@ -11,156 +11,156 @@ export type Input = string | Uint8Array | Grid | Grid[];
11
11
  /** The host capabilities GraphSearch consults during a cover. MindContext
12
12
  * extends this so the Mind can pass itself as the host. */
13
13
  export interface GraphSearchHost {
14
- resolve(bytes: Uint8Array): number | null;
15
- recogniseSpan?(bytes: Uint8Array): {
16
- sites: ReadonlyArray<Site>;
17
- leaves: ReadonlyArray<Leaf>;
18
- splits: ReadonlySet<number>;
19
- };
20
- chooseNext?(node: number): number | undefined;
14
+ resolve(bytes: Uint8Array): number | null;
15
+ recogniseSpan?(bytes: Uint8Array): {
16
+ sites: ReadonlyArray<Site>;
17
+ leaves: ReadonlyArray<Leaf>;
18
+ splits: ReadonlySet<number>;
19
+ };
20
+ chooseNext?(node: number): number | undefined;
21
21
  }
22
22
  export interface Recognition {
23
- /** Forms that can lead somewhere — they have an edge or a halo. */
24
- sites: Site[];
25
- /** The query's perceived leaves (the search's covering axioms). */
26
- leaves: Leaf[];
27
- /** Sub-leaf positions where a form boundary falls between leaf edges. */
28
- splits: Set<number>;
23
+ /** Forms that can lead somewhere — they have an edge or a halo. */
24
+ sites: Site[];
25
+ /** The query's perceived leaves (the search's covering axioms). */
26
+ leaves: Leaf[];
27
+ /** Sub-leaf positions where a form boundary falls between leaf edges. */
28
+ splits: Set<number>;
29
29
  }
30
30
  /** How the consensus climb weights a region's Document-Frequency reach. */
31
31
  export type DFMode = "inverse" | "direct" | "combined";
32
32
  /** One POINT OF ATTENTION the consensus climb resolved. */
33
33
  export interface Attention {
34
- /** The learnt context this point resolves to. */
35
- anchor: number;
36
- /** IDF-weighted consensus vote — the strength that orders points. */
37
- vote: number;
38
- /** The union of the query byte-spans whose evidence supports this point. */
39
- start: number;
40
- end: number;
34
+ /** The learnt context this point resolves to. */
35
+ anchor: number;
36
+ /** IDF-weighted consensus vote — the strength that orders points. */
37
+ vote: number;
38
+ /** The union of the query byte-spans whose evidence supports this point. */
39
+ start: number;
40
+ end: number;
41
41
  }
42
42
  /** Both read-outs of one consensus climb. */
43
43
  export interface AttentionRead {
44
- roots: Attention[];
45
- ranked: Attention[];
44
+ roots: Attention[];
45
+ ranked: Attention[];
46
46
  }
47
47
  /** A positioned region of a byte stream paired with its gist. */
48
48
  export interface Segment {
49
- start: number;
50
- end: number;
51
- v: Vec;
49
+ start: number;
50
+ end: number;
51
+ v: Vec;
52
52
  }
53
53
  /** A region of the query's perceived tree for the consensus climb. */
54
54
  export interface Region {
55
- v: Vec;
56
- start: number;
57
- end: number;
58
- chunk: boolean;
59
- /** Whether the region's bytes resolve to a KNOWN node (content-addressed,
60
- * exact). Exact regions vote with full weight; approximate ones pay the
61
- * contrastive margin (see voteRegions) — under the linear fold a raw
62
- * resonance score is byte-overlap, evidence only in excess of its best
63
- * rival conclusion. */
64
- known: boolean;
55
+ v: Vec;
56
+ start: number;
57
+ end: number;
58
+ chunk: boolean;
59
+ /** Whether the region's bytes resolve to a KNOWN node (content-addressed,
60
+ * exact). Exact regions vote with full weight; approximate ones pay the
61
+ * contrastive margin (see voteRegions) — under the linear fold a raw
62
+ * resonance score is byte-overlap, evidence only in excess of its best
63
+ * rival conclusion. */
64
+ known: boolean;
65
65
  }
66
66
  /** Per-region vote data from the consensus climb's resonance pass. */
67
67
  export interface RegionVote {
68
- start: number;
69
- end: number;
70
- canonicalFailed: boolean;
71
- roots: readonly number[];
72
- w: number;
73
- wFocus: number;
68
+ start: number;
69
+ end: number;
70
+ canonicalFailed: boolean;
71
+ roots: readonly number[];
72
+ w: number;
73
+ wFocus: number;
74
74
  }
75
75
  /** The edge-bearing contexts reached by climbing from a node, plus saturation info. */
76
76
  export interface AncestorReach {
77
- roots: number[];
78
- contextsReached: number;
79
- saturated: boolean;
77
+ roots: number[];
78
+ contextsReached: number;
79
+ saturated: boolean;
80
80
  }
81
81
  /** Saturated-interval information for the noise-drop gate. */
82
82
  export interface SaturationInfo {
83
- leadingEnd: number;
84
- hasLeading: boolean;
85
- intervals: Array<{
86
- start: number;
87
- end: number;
88
- }>;
83
+ leadingEnd: number;
84
+ hasLeading: boolean;
85
+ intervals: Array<{
86
+ start: number;
87
+ end: number;
88
+ }>;
89
89
  }
90
90
  /** The items of poolVotes' deduction system. */
91
91
  export type AItem = {
92
- kind: "region";
93
- ri: number;
92
+ kind: "region";
93
+ ri: number;
94
94
  } | {
95
- kind: "anchor";
96
- id: number;
95
+ kind: "anchor";
96
+ id: number;
97
97
  } | {
98
- kind: "anchorFocus";
99
- id: number;
98
+ kind: "anchorFocus";
99
+ id: number;
100
100
  };
101
101
  export interface MindContext extends GraphSearchHost {
102
- store: Store;
103
- space: Space;
104
- alphabet: Alphabet;
105
- cfg: MindConfig;
106
- search: GraphSearch;
107
- trace: Rationale | null;
108
- climbMemo: WeakMap<Uint8Array, Map<string, AttentionRead>> | null;
109
- /** Per-response memo of {@link recognise} keyed by the byte-array OBJECT
110
- * (think, articulate, and the post-grounding pre-consume all recognise the
111
- * same query/answer objects). Valid because the store is read-only while
112
- * a response is in flight; bypassed when a trace is attached so every
113
- * recognise still emits its rationale step. Null outside respond(). */
114
- recogniseMemo: WeakMap<Uint8Array, Recognition> | null;
115
- /** Per-response memo of {@link perceive} keyed by the byte-array OBJECT —
116
- * the GENERAL memo the result-level ones (recogniseMemo, climbMemo,
117
- * _gistCache) each partially compensate for: resolve(), gistOf(), and
118
- * every mechanism's re-perception of the same query/answer object hit it
119
- * (a reason hop used to fold the same answer three times). Valid because
120
- * the store is read-only while a response is in flight and perception is
121
- * a pure function of bytes; only inference-shaped calls (plain Uint8Array,
122
- * no leafAt/lookup capabilities) are memoised, so the deposit path never
123
- * sees it. Keyed by CONTENT (latin1 of the bytes), not object identity —
124
- * mechanisms materialise the same span in fresh subarrays constantly
125
- * (measured on a trained store: 46% of one response's perceptions were
126
- * byte-identical repeats an identity key missed). NOT bypassed under
127
- * trace — perception emits no rationale steps, so there is nothing a memo
128
- * hit could swallow. Null outside respond(). */
129
- perceiveMemo: Map<string, Sema> | null;
130
- _edgeGuide: Vec | null;
131
- _edgeChoice: Map<number, number>;
132
- _prevSeen: Set<number> | null;
133
- /** Session cache of node-id → perceived gist, for candidate scoring
134
- * ({@link chooseAmong} in the reverse projection's recall path re-gists up to
135
- * √N contexts per pick — the measured bottleneck there). `chooseNext` does
136
- * NOT use this cache; forward-edge disambiguation uses prevOf counts
137
- * (distributional evidence) instead of gist comparison, because for short
138
- * answer candidates the gist is dominated by accidental byte-pattern
139
- * correlations. A node's bytes are immutable and perception is a pure
140
- * function of bytes, so an entry stays valid for the store's lifetime —
141
- * never invalidated. Bounded LRU (byte-sized); a miss only re-perceives,
142
- * never a correctness risk. */
143
- _gistCache: BoundedMap<number, Vec>;
144
- /** DEPOSIT-path stable-prefix tree cache: content key (latin1) of a
145
- * deposited input → its plain-fold level PYRAMID, so the NEXT deposit
146
- * whose prefix it is (a conversation's accumulated context) reuses every
147
- * full aligned block and refolds only the right edge of each level —
148
- * O(turn) per deposit instead of O(context), with the tree BIT-IDENTICAL
149
- * to a from-scratch fold (see FoldPyramid). Purely a performance cache.
150
- * Entry-count bounded (a pyramid is ~KB/byte of content; only the few
151
- * live conversation chains matter). */
152
- _depositTrees: BoundedMap<string, FoldPyramid>;
153
- /** The byte lengths present in {@link _depositTrees} — the candidate
154
- * prefix lengths probed (longest first). Drifts on eviction (a stale
155
- * length only costs a miss); cleared with the map when it outgrows the
156
- * probe budget. */
157
- _depositLens: Set<number>;
158
- /** Mind-lifetime intern memo by NODE IDENTITY: perceived-tree node → its
159
- * content-addressed id. Valid forever (ids are permanent, Sema nodes
160
- * immutable); WeakMap, so entries live exactly as long as the pyramid
161
- * cache keeps the shared subtrees alive. Lets internTreeIds skip whole
162
- * shared subtrees and indexSubSpans keep its seenBefore window skip. */
163
- _internIds: WeakMap<Sema, number>;
102
+ store: Store;
103
+ space: Space;
104
+ alphabet: Alphabet;
105
+ cfg: MindConfig;
106
+ search: GraphSearch;
107
+ trace: Rationale | null;
108
+ climbMemo: WeakMap<Uint8Array, Map<string, AttentionRead>> | null;
109
+ /** Per-response memo of {@link recognise} keyed by the byte-array OBJECT
110
+ * (think, articulate, and the post-grounding pre-consume all recognise the
111
+ * same query/answer objects). Valid because the store is read-only while
112
+ * a response is in flight; bypassed when a trace is attached so every
113
+ * recognise still emits its rationale step. Null outside respond(). */
114
+ recogniseMemo: WeakMap<Uint8Array, Recognition> | null;
115
+ /** Per-response memo of {@link perceive} keyed by the byte-array OBJECT —
116
+ * the GENERAL memo the result-level ones (recogniseMemo, climbMemo,
117
+ * _gistCache) each partially compensate for: resolve(), gistOf(), and
118
+ * every mechanism's re-perception of the same query/answer object hit it
119
+ * (a reason hop used to fold the same answer three times). Valid because
120
+ * the store is read-only while a response is in flight and perception is
121
+ * a pure function of bytes; only inference-shaped calls (plain Uint8Array,
122
+ * no leafAt/lookup capabilities) are memoised, so the deposit path never
123
+ * sees it. Keyed by CONTENT (latin1 of the bytes), not object identity —
124
+ * mechanisms materialise the same span in fresh subarrays constantly
125
+ * (measured on a trained store: 46% of one response's perceptions were
126
+ * byte-identical repeats an identity key missed). NOT bypassed under
127
+ * trace — perception emits no rationale steps, so there is nothing a memo
128
+ * hit could swallow. Null outside respond(). */
129
+ perceiveMemo: Map<string, Sema> | null;
130
+ _edgeGuide: Vec | null;
131
+ _edgeChoice: Map<number, number>;
132
+ _prevSeen: Set<number> | null;
133
+ /** Session cache of node-id → perceived gist, for candidate scoring
134
+ * ({@link chooseAmong} in the reverse projection's recall path re-gists up to
135
+ * √N contexts per pick — the measured bottleneck there). `chooseNext` does
136
+ * NOT use this cache; forward-edge disambiguation uses prevOf counts
137
+ * (distributional evidence) instead of gist comparison, because for short
138
+ * answer candidates the gist is dominated by accidental byte-pattern
139
+ * correlations. A node's bytes are immutable and perception is a pure
140
+ * function of bytes, so an entry stays valid for the store's lifetime —
141
+ * never invalidated. Bounded LRU (byte-sized); a miss only re-perceives,
142
+ * never a correctness risk. */
143
+ _gistCache: BoundedMap<number, Vec>;
144
+ /** DEPOSIT-path stable-prefix tree cache: content key (latin1) of a
145
+ * deposited input → its plain-fold level PYRAMID, so the NEXT deposit
146
+ * whose prefix it is (a conversation's accumulated context) reuses every
147
+ * full aligned block and refolds only the right edge of each level —
148
+ * O(turn) per deposit instead of O(context), with the tree BIT-IDENTICAL
149
+ * to a from-scratch fold (see FoldPyramid). Purely a performance cache.
150
+ * Entry-count bounded (a pyramid is ~KB/byte of content; only the few
151
+ * live conversation chains matter). */
152
+ _depositTrees: BoundedMap<string, FoldPyramid>;
153
+ /** The byte lengths present in {@link _depositTrees} — the candidate
154
+ * prefix lengths probed (longest first). Drifts on eviction (a stale
155
+ * length only costs a miss); cleared with the map when it outgrows the
156
+ * probe budget. */
157
+ _depositLens: Set<number>;
158
+ /** Mind-lifetime intern memo by NODE IDENTITY: perceived-tree node → its
159
+ * content-addressed id. Valid forever (ids are permanent, Sema nodes
160
+ * immutable); WeakMap, so entries live exactly as long as the pyramid
161
+ * cache keeps the shared subtrees alive. Lets internTreeIds skip whole
162
+ * shared subtrees and indexSubSpans keep its seenBefore window skip. */
163
+ _internIds: WeakMap<Sema, number>;
164
164
  }
165
165
  /** Read a whole node's bytes. */
166
166
  export declare const ALL = 2147483647;
@@ -168,7 +168,14 @@ export declare const ALL = 2147483647;
168
168
  export declare function spliceAll(segs: Seg[]): Uint8Array | null;
169
169
  /** Lift the answer out of the cover for think: the recognised region, free of
170
170
  * the asker's surrounding (unrecognised) framing. */
171
- export declare function liftAnswer(segs: Seg[], queryLen: number): Uint8Array | null;
171
+ export declare function liftAnswer(
172
+ segs: Seg[],
173
+ queryLen: number,
174
+ ): Uint8Array | null;
172
175
  /** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
173
176
  * tracked deposit interned (`prevSeen`). */
174
- export declare function changedNodes(tree: Sema, ids: Map<Sema, number>, prevSeen: Set<number>): Sema[];
177
+ export declare function changedNodes(
178
+ tree: Sema,
179
+ ids: Map<Sema, number>,
180
+ prevSeen: Set<number>,
181
+ ): Sema[];
@@ -11,74 +11,83 @@ import { dominates } from "../geometry.js";
11
11
  export const ALL = 0x7fffffff;
12
12
  /** Splice every chosen span in order — the whole cover as one byte string. */
13
13
  export function spliceAll(segs) {
14
- if (!segs.some((s) => s.rec))
15
- return null;
16
- return concatBytes(segs.map((s) => s.bytes));
14
+ if (!segs.some((s) => s.rec)) {
15
+ return null;
16
+ }
17
+ return concatBytes(segs.map((s) => s.bytes));
17
18
  }
18
19
  /** Lift the answer out of the cover for think: the recognised region, free of
19
20
  * the asker's surrounding (unrecognised) framing. */
20
21
  export function liftAnswer(segs, queryLen) {
21
- const recognised = [];
22
- for (let k = 0; k < segs.length; k++)
23
- if (segs[k].rec)
24
- recognised.push(k);
25
- if (recognised.length === 0)
26
- return null;
27
- if (recognised.length === 1) {
28
- const s = segs[recognised[0]];
29
- if (dominates(s.j - s.i, queryLen)) {
30
- return concatBytes(segs.map((x) => x.bytes));
31
- }
32
- return s.bytes;
22
+ const recognised = [];
23
+ for (let k = 0; k < segs.length; k++) {
24
+ if (segs[k].rec) {
25
+ recognised.push(k);
33
26
  }
34
- const lo = recognised[0];
35
- const hi = recognised[recognised.length - 1];
36
- return concatBytes(segs.slice(lo, hi + 1).map((x) => x.bytes));
27
+ }
28
+ if (recognised.length === 0) {
29
+ return null;
30
+ }
31
+ if (recognised.length === 1) {
32
+ const s = segs[recognised[0]];
33
+ if (dominates(s.j - s.i, queryLen)) {
34
+ return concatBytes(segs.map((x) => x.bytes));
35
+ }
36
+ return s.bytes;
37
+ }
38
+ const lo = recognised[0];
39
+ const hi = recognised[recognised.length - 1];
40
+ return concatBytes(segs.slice(lo, hi + 1).map((x) => x.bytes));
37
41
  }
38
42
  /** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
39
43
  * tracked deposit interned (`prevSeen`). */
40
44
  export function changedNodes(tree, ids, prevSeen) {
41
- const newCount = new Map();
42
- const count = (n) => {
43
- const memo = newCount.get(n);
44
- if (memo !== undefined)
45
- return memo;
46
- const id = ids.get(n);
47
- // PRUNE: a node whose id the previous deposit already interned is old,
48
- // and content addressing makes that transitive the same id names the
49
- // same content, so every descendant was interned then too. The whole
50
- // subtree counts 0 without walking it; with the pyramid fold sharing a
51
- // conversation's prefix subtree, this is what keeps the changed-nodes
52
- // read O(new nodes) instead of O(context). (A node internTreeIds
53
- // memo-skipped has an id here exactly when it is such a shared root.)
54
- if (id !== undefined && prevSeen.has(id)) {
55
- newCount.set(n, 0);
56
- return 0;
57
- }
58
- let c = 1; // reachable only when NOT pruned above ⇒ this node is new
59
- if (n.kids) {
60
- for (const k of n.kids)
61
- c += count(k);
62
- }
63
- newCount.set(n, c);
64
- return c;
65
- };
66
- const total = count(tree);
67
- if (total === 0)
68
- return [tree];
69
- let n = tree;
70
- for (;;) {
71
- if (n.kids === null)
72
- return [n];
73
- let holder = null;
74
- for (const k of n.kids) {
75
- if (newCount.get(k) === total) {
76
- holder = k;
77
- break;
78
- }
79
- }
80
- if (holder === null)
81
- return [n];
82
- n = holder;
45
+ const newCount = new Map();
46
+ const count = (n) => {
47
+ const memo = newCount.get(n);
48
+ if (memo !== undefined) {
49
+ return memo;
50
+ }
51
+ const id = ids.get(n);
52
+ // PRUNE: a node whose id the previous deposit already interned is old,
53
+ // and content addressing makes that transitive the same id names the
54
+ // same content, so every descendant was interned then too. The whole
55
+ // subtree counts 0 without walking it; with the pyramid fold sharing a
56
+ // conversation's prefix subtree, this is what keeps the changed-nodes
57
+ // read O(new nodes) instead of O(context). (A node internTreeIds
58
+ // memo-skipped has an id here exactly when it is such a shared root.)
59
+ if (id !== undefined && prevSeen.has(id)) {
60
+ newCount.set(n, 0);
61
+ return 0;
62
+ }
63
+ let c = 1; // reachable only when NOT pruned above ⇒ this node is new
64
+ if (n.kids) {
65
+ for (const k of n.kids) {
66
+ c += count(k);
67
+ }
68
+ }
69
+ newCount.set(n, c);
70
+ return c;
71
+ };
72
+ const total = count(tree);
73
+ if (total === 0) {
74
+ return [tree];
75
+ }
76
+ let n = tree;
77
+ for (;;) {
78
+ if (n.kids === null) {
79
+ return [n];
80
+ }
81
+ let holder = null;
82
+ for (const k of n.kids) {
83
+ if (newCount.get(k) === total) {
84
+ holder = k;
85
+ break;
86
+ }
87
+ }
88
+ if (holder === null) {
89
+ return [n];
83
90
  }
91
+ n = holder;
92
+ }
84
93
  }