@hviana/sema 0.8.9 → 0.9.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.
- package/AGENTS.md +7 -7
- package/dist/src/alu/src/index.d.ts +1 -1
- package/dist/src/alu/src/index.js +1 -1
- package/dist/src/alu/src/parser.js +2 -6
- package/dist/src/alu/src/resonance.d.ts +13 -0
- package/dist/src/alu/src/resonance.js +41 -0
- package/dist/src/alu/test/alu.test.js +39 -0
- package/dist/src/bytes.d.ts +6 -2
- package/dist/src/bytes.js +10 -4
- package/dist/src/canon.js +44 -0
- package/dist/src/geometry.d.ts +19 -1
- package/dist/src/geometry.js +125 -141
- package/dist/src/meter.d.ts +27 -0
- package/dist/src/meter.js +28 -1
- package/dist/src/mind/articulation.js +14 -1
- package/dist/src/mind/attention.d.ts +12 -0
- package/dist/src/mind/attention.js +44 -16
- package/dist/src/mind/bridge.js +3 -3
- package/dist/src/mind/derivation.d.ts +40 -0
- package/dist/src/mind/derivation.js +34 -0
- package/dist/src/mind/graph-search.d.ts +89 -15
- package/dist/src/mind/graph-search.js +345 -174
- package/dist/src/mind/learning.js +1 -1
- package/dist/src/mind/mechanisms/cover.d.ts +19 -3
- package/dist/src/mind/mechanisms/cover.js +101 -58
- package/dist/src/mind/mechanisms/recall.js +0 -1
- package/dist/src/mind/mind.js +2 -2
- package/dist/src/mind/pipeline.d.ts +5 -1
- package/dist/src/mind/pipeline.js +175 -87
- package/dist/src/mind/primitives.d.ts +25 -5
- package/dist/src/mind/primitives.js +107 -44
- package/dist/src/mind/reasoning.d.ts +18 -4
- package/dist/src/mind/reasoning.js +445 -321
- package/dist/src/mind/recognition.js +55 -73
- package/dist/src/mind/resonance.js +1 -11
- package/dist/src/mind/traverse.d.ts +3 -3
- package/dist/src/mind/traverse.js +3 -3
- package/dist/src/mind/types.d.ts +7 -1
- package/dist/src/store-sqlite.d.ts +25 -0
- package/dist/src/store-sqlite.js +89 -1
- package/dist/src/store.d.ts +48 -4
- package/dist/src/store.js +86 -6
- package/docs/INDEX.md +18 -18
- package/docs/INVARIANTS.md +16 -16
- package/docs/architecture/bounded-reads.md +1 -1
- package/docs/architecture/caches.md +5 -4
- package/docs/architecture/closure.md +45 -5
- package/docs/architecture/cost-model.md +16 -0
- package/docs/architecture/factored-machinery.md +14 -13
- package/docs/architecture/fold-contract.md +51 -1
- package/docs/architecture/mechanism-market.md +21 -0
- package/docs/architecture/memoization.md +3 -3
- package/docs/architecture/meter.md +2 -1
- package/docs/architecture/saturation.md +12 -0
- package/docs/architecture/store.md +25 -2
- package/docs/failures/tempting-but-wrong.md +13 -2
- package/docs/harness/gates.md +12 -10
- package/docs/mechanisms/cover.md +23 -6
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +10 -2
- package/src/alu/src/index.ts +1 -0
- package/src/alu/src/parser.ts +6 -6
- package/src/alu/src/resonance.ts +42 -0
- package/src/alu/test/alu.test.ts +40 -0
- package/src/bytes.ts +13 -3
- package/src/canon.ts +40 -0
- package/src/geometry.ts +183 -154
- package/src/meter.ts +28 -1
- package/src/mind/articulation.ts +14 -2
- package/src/mind/attention.ts +47 -25
- package/src/mind/bridge.ts +3 -3
- package/src/mind/derivation.ts +77 -0
- package/src/mind/graph-search.ts +449 -221
- package/src/mind/learning.ts +1 -7
- package/src/mind/match.ts +1 -2
- package/src/mind/mechanisms/cast.ts +1 -2
- package/src/mind/mechanisms/cover.ts +149 -84
- package/src/mind/mechanisms/extraction.ts +1 -2
- package/src/mind/mechanisms/prefix-completion.ts +1 -1
- package/src/mind/mechanisms/recall.ts +1 -3
- package/src/mind/mechanisms/reference.ts +1 -1
- package/src/mind/mind.ts +5 -30
- package/src/mind/pipeline.ts +206 -102
- package/src/mind/primitives.ts +119 -43
- package/src/mind/reasoning.ts +558 -413
- package/src/mind/recognition.ts +49 -65
- package/src/mind/resonance.ts +2 -16
- package/src/mind/trace.ts +1 -1
- package/src/mind/traverse.ts +3 -3
- package/src/mind/types.ts +9 -11
- package/src/store-sqlite.ts +92 -1
- package/src/store.ts +113 -7
- package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
- package/test/106-the-join-fires.test.mjs +21 -0
- package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
- package/test/136-the-two-named-limits.test.mjs +3 -2
- package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
- package/test/148-exact-shortcuts-agree.test.mjs +188 -0
- package/test/149-the-closure-engine.test.mjs +138 -0
- package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
- package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
- package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
- package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
- package/test/24-generalization.test.mjs +32 -0
- package/test/36-bloom.test.mjs +53 -0
- package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
- package/test/48-recognise-turn-connective.test.mjs +3 -2
- package/test/55-cost-meter.test.mjs +4 -4
- package/test/90-connector-read-cap.test.mjs +7 -7
package/AGENTS.md
CHANGED
|
@@ -57,13 +57,13 @@ Five invariants. Violate one and the system degrades silently — tests pin them
|
|
|
57
57
|
|
|
58
58
|
Cross-cutting contracts (single-definition, imported everywhere):
|
|
59
59
|
`contentLevels` in `src/geometry.ts` is the one boundary rule;
|
|
60
|
-
`src/mind/derivation.ts` is the closure law
|
|
61
|
-
|
|
62
|
-
content-addressed ascent; `Precomputed` in
|
|
63
|
-
the per-response memo; `src/meter.ts` is the
|
|
64
|
-
See `docs/INDEX.md` and
|
|
65
|
-
ownership. Tie-breaks are
|
|
66
|
-
(`determinism.md`).
|
|
60
|
+
`src/mind/derivation.ts` is the closure law and its engine (`closeOver`);
|
|
61
|
+
`src/mind/canonical.ts` is the canonical segmentation contract;
|
|
62
|
+
`src/mind/junction.ts` is the shared content-addressed ascent; `Precomputed` in
|
|
63
|
+
`src/mind/pipeline-mechanism.ts` is the per-response memo; `src/meter.ts` is the
|
|
64
|
+
write-only work accounting surface. See `docs/INDEX.md` and
|
|
65
|
+
`factored-machinery.md` for the contract table and ownership. Tie-breaks are
|
|
66
|
+
corpus-determined, not interchangeable (`determinism.md`).
|
|
67
67
|
|
|
68
68
|
## 3. Where things live
|
|
69
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { asBit, asInt, asReal, bit, coerce, decimalCodec, type Domain, formatReal, int, isNd, isNumeric, joinDomain, nd, parseValue, real, symbol, symbolSpans, tagOf, type Value, type ValueCodec, } from "./value.js";
|
|
2
2
|
export { type Arity, type EvalExpr, type InfixSyntax, NO_RESONANCE, type OpContext, type Operation, OperationRegistry, type OpFn, type OpRuntime, type OpTraits, type ResonanceSync, } from "./operation.js";
|
|
3
|
-
export { type AluResonance, type ConceptAnchor, NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, } from "./resonance.js";
|
|
3
|
+
export { type AluResonance, type ConceptAnchor, NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
|
|
4
4
|
export { type ApplyScalar, ExprGrammar, freeVariables, type IsUnaryFn, type Token, tokenize, } from "./expr.js";
|
|
5
5
|
export { type AluHost, type ComputedSpan, QueryParser, type Span, STRUCTURAL_HOST, } from "./parser.js";
|
|
6
6
|
export { registerNd } from "./kernel-nd.js";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// See ./../README.md for the kernel and its derivation DAG.
|
|
9
9
|
export { asBit, asInt, asReal, bit, coerce, decimalCodec, formatReal, int, isNd, isNumeric, joinDomain, nd, parseValue, real, symbol, symbolSpans, tagOf, } from "./value.js";
|
|
10
10
|
export { NO_RESONANCE, OperationRegistry, } from "./operation.js";
|
|
11
|
-
export { NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, } from "./resonance.js";
|
|
11
|
+
export { NO_ALU_RESONANCE, prefetchOpposites, prefetchRecognisedOps, prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
|
|
12
12
|
export { ExprGrammar, freeVariables, tokenize, } from "./expr.js";
|
|
13
13
|
export { QueryParser, STRUCTURAL_HOST, } from "./parser.js";
|
|
14
14
|
export { registerNd } from "./kernel-nd.js";
|
|
@@ -40,8 +40,7 @@
|
|
|
40
40
|
// that names an operation literally or by resonance, applied to a
|
|
41
41
|
// following operand ("opposite large" → "small"), or, for the numerical
|
|
42
42
|
// layer, to a following EXPRESSION ("derivative of x^2 at 3").
|
|
43
|
-
import {
|
|
44
|
-
import { NO_RESONANCE } from "./operation.js";
|
|
43
|
+
import { prefetchResonance, withOppositesOnDemand, } from "./resonance.js";
|
|
45
44
|
import { int, real, symbol, symbolSpans } from "./value.js";
|
|
46
45
|
import { nonSpaceRuns } from "./text.js";
|
|
47
46
|
import { bytesEqual, latin1 } from "../../bytes.js";
|
|
@@ -494,10 +493,7 @@ export class QueryParser {
|
|
|
494
493
|
return null; // under-supplied → decline
|
|
495
494
|
const args = picked.map((t) => t.value ?? query.subarray(t.i, t.j));
|
|
496
495
|
const symbols = picked.flatMap((t, k) => t.kind === "term" ? [args[k]] : []);
|
|
497
|
-
const
|
|
498
|
-
? await prefetchOpposites(this.resonance, symbols)
|
|
499
|
-
: NO_RESONANCE;
|
|
500
|
-
const bytes = this.alu.applyBytes(name, args, resonance);
|
|
496
|
+
const bytes = await withOppositesOnDemand(this.resonance, symbols, (resonance) => this.alu.applyBytes(name, args, resonance));
|
|
501
497
|
if (bytes === null)
|
|
502
498
|
return null;
|
|
503
499
|
if (symbols.length === args.length && args.some((a) => bytesEqual(bytes, a))) {
|
|
@@ -35,6 +35,19 @@ export declare const NO_ALU_RESONANCE: AluResonance;
|
|
|
35
35
|
* chart uses). This is the async→sync bridge for the polymorphic inverse,
|
|
36
36
|
* mirroring how a concept hop's target is pre-resolved and read synchronously. */
|
|
37
37
|
export declare function prefetchOpposites(resonance: AluResonance, symbols: Iterable<Uint8Array>): Promise<ResonanceSync>;
|
|
38
|
+
/** {@link prefetchOpposites} resolved ON DEMAND: run `apply` against a
|
|
39
|
+
* synchronous snapshot that answers only the opposites already resolved, and
|
|
40
|
+
* when the computation ASKED for one of `symbols` that is not yet resolved,
|
|
41
|
+
* resolve it through the host and run `apply` again — until a run asks for
|
|
42
|
+
* nothing new. The result is `apply(prefetchOpposites(resonance, symbols))`
|
|
43
|
+
* exactly: an opposite outside `symbols` reads null in both, every one inside
|
|
44
|
+
* that the computation reads is the host's answer in both, and `apply` is
|
|
45
|
+
* pure, so a run that read the same answers returns the same bytes. What it
|
|
46
|
+
* saves is every host call no computation reads: only the polymorphic
|
|
47
|
+
* inverse reads opposites, yet the eager prefetch paid one per symbol operand
|
|
48
|
+
* for ANY operation — on SEMA a halo-index query each, measured at 30-200 ms
|
|
49
|
+
* of a plain dialogue turn's parse that computed nothing. */
|
|
50
|
+
export declare function withOppositesOnDemand<R>(resonance: AluResonance, symbols: Iterable<Uint8Array>, apply: (sync: ResonanceSync) => R): Promise<R>;
|
|
38
51
|
/** Pre-resolve BOTH capabilities a computation may need synchronously — the
|
|
39
52
|
* resonant opposite of a symbol (for the polymorphic inverse) AND the operation
|
|
40
53
|
* a symbol's MEANING names (for a higher-order nd op's function argument) — over
|
|
@@ -71,6 +71,47 @@ export async function prefetchOpposites(resonance, symbols) {
|
|
|
71
71
|
recogniseOp: () => null,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
/** {@link prefetchOpposites} resolved ON DEMAND: run `apply` against a
|
|
75
|
+
* synchronous snapshot that answers only the opposites already resolved, and
|
|
76
|
+
* when the computation ASKED for one of `symbols` that is not yet resolved,
|
|
77
|
+
* resolve it through the host and run `apply` again — until a run asks for
|
|
78
|
+
* nothing new. The result is `apply(prefetchOpposites(resonance, symbols))`
|
|
79
|
+
* exactly: an opposite outside `symbols` reads null in both, every one inside
|
|
80
|
+
* that the computation reads is the host's answer in both, and `apply` is
|
|
81
|
+
* pure, so a run that read the same answers returns the same bytes. What it
|
|
82
|
+
* saves is every host call no computation reads: only the polymorphic
|
|
83
|
+
* inverse reads opposites, yet the eager prefetch paid one per symbol operand
|
|
84
|
+
* for ANY operation — on SEMA a halo-index query each, measured at 30-200 ms
|
|
85
|
+
* of a plain dialogue turn's parse that computed nothing. */
|
|
86
|
+
export async function withOppositesOnDemand(resonance, symbols, apply) {
|
|
87
|
+
const allowed = new Set();
|
|
88
|
+
for (const bytes of symbols)
|
|
89
|
+
allowed.add(latin1(bytes));
|
|
90
|
+
const table = new Map();
|
|
91
|
+
const asked = new Map();
|
|
92
|
+
const sync = {
|
|
93
|
+
opposite: (bytes) => {
|
|
94
|
+
const key = latin1(bytes);
|
|
95
|
+
if (!allowed.has(key))
|
|
96
|
+
return null;
|
|
97
|
+
const known = table.get(key);
|
|
98
|
+
if (known !== undefined)
|
|
99
|
+
return known;
|
|
100
|
+
asked.set(key, bytes);
|
|
101
|
+
return null;
|
|
102
|
+
},
|
|
103
|
+
recogniseOp: () => null,
|
|
104
|
+
};
|
|
105
|
+
for (;;) {
|
|
106
|
+
const out = apply(sync);
|
|
107
|
+
if (asked.size === 0)
|
|
108
|
+
return out;
|
|
109
|
+
for (const [key, bytes] of asked) {
|
|
110
|
+
table.set(key, (await resonance.opposite(bytes)) ?? null);
|
|
111
|
+
}
|
|
112
|
+
asked.clear();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
74
115
|
/** Pre-resolve BOTH capabilities a computation may need synchronously — the
|
|
75
116
|
* resonant opposite of a symbol (for the polymorphic inverse) AND the operation
|
|
76
117
|
* a symbol's MEANING names (for a higher-order nd op's function argument) — over
|
|
@@ -432,6 +432,45 @@ test("conceptAnchors exposes the operation vocabulary for resonant recognition",
|
|
|
432
432
|
assert.ok(!forms.includes("0"));
|
|
433
433
|
}
|
|
434
434
|
});
|
|
435
|
+
test("a symbol's opposite is asked of the host only when the operation reads it", async () => {
|
|
436
|
+
// Only the polymorphic inverse reads an opposite. Resolving every symbol
|
|
437
|
+
// operand's opposite up front paid one host call per operand for ANY
|
|
438
|
+
// operation — on SEMA a halo-index query each, 30-200 ms of a plain
|
|
439
|
+
// dialogue turn's parse that computed nothing.
|
|
440
|
+
const asked = [];
|
|
441
|
+
const host = {
|
|
442
|
+
meaningOf: async () => null,
|
|
443
|
+
continuation: async (b) => {
|
|
444
|
+
asked.push(dec(b));
|
|
445
|
+
return dec(b) === "large" ? enc("small") : null;
|
|
446
|
+
},
|
|
447
|
+
segment: (bytes) => {
|
|
448
|
+
const runs = [];
|
|
449
|
+
for (let i = 0; i < bytes.length;) {
|
|
450
|
+
if (bytes[i] === 32) {
|
|
451
|
+
i++;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
let j = i;
|
|
455
|
+
while (j < bytes.length && bytes[j] !== 32)
|
|
456
|
+
j++;
|
|
457
|
+
runs.push({ i, j });
|
|
458
|
+
i = j;
|
|
459
|
+
}
|
|
460
|
+
return runs;
|
|
461
|
+
},
|
|
462
|
+
reach: Number.POSITIVE_INFINITY,
|
|
463
|
+
};
|
|
464
|
+
const u = new Alu({}, host);
|
|
465
|
+
// An operation that does not read opposites: nothing computed, nothing asked.
|
|
466
|
+
assert.deepEqual(await u.parse(enc("sqrt large")), []);
|
|
467
|
+
assert.deepEqual(asked, []);
|
|
468
|
+
// The inverse reads it: asked once, and grounded exactly as before.
|
|
469
|
+
const out = await u.parse(enc("opposite large"));
|
|
470
|
+
assert.equal(out.length, 1);
|
|
471
|
+
assert.equal(dec(out[0].bytes), "small");
|
|
472
|
+
assert.deepEqual(asked, ["large"]);
|
|
473
|
+
});
|
|
435
474
|
test("prefetchRecognisedOps bridges async recognition to a sync map", async () => {
|
|
436
475
|
// A stub host resonance: "the rate of change of" means a derivative.
|
|
437
476
|
const stub = {
|
package/dist/src/bytes.d.ts
CHANGED
|
@@ -6,8 +6,12 @@ export declare function concatBytes(parts: Uint8Array[]): Uint8Array;
|
|
|
6
6
|
/** Join two byte spans — the hot two-operand case of {@link concatBytes},
|
|
7
7
|
* fused without the array wrapper for the search's inner fuse loop. */
|
|
8
8
|
export declare function concat2(a: Uint8Array, b: Uint8Array): Uint8Array;
|
|
9
|
-
/** Latin-1 view of a byte span —
|
|
10
|
-
*
|
|
9
|
+
/** Latin-1 view of a byte span — ONE code unit per byte, so it is injective
|
|
10
|
+
* and safe as an exact cache key (every byte 0–255 maps to one code unit).
|
|
11
|
+
* Batched `String.fromCharCode`, chunked to stay within the engine's argument
|
|
12
|
+
* limit on long spans; the one definition every content-keyed memo uses.
|
|
13
|
+
* `apply` takes the typed array as its argument list directly — a spread
|
|
14
|
+
* walks it through the iterator protocol first, ~2.7× slower per short key. */
|
|
11
15
|
export declare function latin1(b: Uint8Array): string;
|
|
12
16
|
/** First index ≥ `from` at which `needle` occurs in `hay`, or -1. A short naive
|
|
13
17
|
* scan — used only to locate a result span inside a learnt framing form. */
|
package/dist/src/bytes.js
CHANGED
|
@@ -35,12 +35,18 @@ export function concat2(a, b) {
|
|
|
35
35
|
out.set(b, a.length);
|
|
36
36
|
return out;
|
|
37
37
|
}
|
|
38
|
-
/** Latin-1 view of a byte span —
|
|
39
|
-
*
|
|
38
|
+
/** Latin-1 view of a byte span — ONE code unit per byte, so it is injective
|
|
39
|
+
* and safe as an exact cache key (every byte 0–255 maps to one code unit).
|
|
40
|
+
* Batched `String.fromCharCode`, chunked to stay within the engine's argument
|
|
41
|
+
* limit on long spans; the one definition every content-keyed memo uses.
|
|
42
|
+
* `apply` takes the typed array as its argument list directly — a spread
|
|
43
|
+
* walks it through the iterator protocol first, ~2.7× slower per short key. */
|
|
40
44
|
export function latin1(b) {
|
|
45
|
+
const n = b.length;
|
|
41
46
|
let s = "";
|
|
42
|
-
for (let
|
|
43
|
-
s += String.fromCharCode(b
|
|
47
|
+
for (let i = 0; i < n; i += 4096) {
|
|
48
|
+
s += String.fromCharCode.apply(null, b.subarray(i, Math.min(i + 4096, n)));
|
|
49
|
+
}
|
|
44
50
|
return s;
|
|
45
51
|
}
|
|
46
52
|
/** First index ≥ `from` at which `needle` occurs in `hay`, or -1. A short naive
|
package/dist/src/canon.js
CHANGED
|
@@ -37,6 +37,12 @@ const enc = new TextEncoder();
|
|
|
37
37
|
* deliberately conservative: punctuation, digits and word order are content
|
|
38
38
|
* and pass through untouched. */
|
|
39
39
|
export function textCanon(bytes) {
|
|
40
|
+
if (isAscii(bytes))
|
|
41
|
+
return asciiCanon(bytes);
|
|
42
|
+
return unicodeCanon(bytes);
|
|
43
|
+
}
|
|
44
|
+
/** The general reading — the DEFINITION of {@link textCanon}. */
|
|
45
|
+
function unicodeCanon(bytes) {
|
|
40
46
|
const s = dec
|
|
41
47
|
.decode(bytes)
|
|
42
48
|
.normalize("NFKC")
|
|
@@ -44,6 +50,44 @@ export function textCanon(bytes) {
|
|
|
44
50
|
.replace(/(\S)\s+(?=\S)/g, "$1 ");
|
|
45
51
|
return enc.encode(s);
|
|
46
52
|
}
|
|
53
|
+
function isAscii(bytes) {
|
|
54
|
+
for (let i = 0; i < bytes.length; i++)
|
|
55
|
+
if (bytes[i] >= 0x80)
|
|
56
|
+
return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/** {@link unicodeCanon} on ASCII input, byte for byte, without the string
|
|
60
|
+
* round trip. Exact, not approximate: NFKC is the identity on ASCII, the
|
|
61
|
+
* lowercase of ASCII is A–Z → a–z, and the ASCII members of the regex's `\s`
|
|
62
|
+
* are TAB, LF, VT, FF, CR and SPACE — so an interior run of them becomes one
|
|
63
|
+
* space and an edge run stays verbatim, exactly as the regex rewrites it.
|
|
64
|
+
* The canonicalizer runs once per probed span on the recognition and join
|
|
65
|
+
* paths, so its constant is paid thousands of times per response; test/148
|
|
66
|
+
* pins the agreement over random ASCII and over the edge cases. */
|
|
67
|
+
function asciiCanon(bytes) {
|
|
68
|
+
const n = bytes.length;
|
|
69
|
+
const out = new Uint8Array(n);
|
|
70
|
+
let o = 0;
|
|
71
|
+
const ws = (b) => b === 0x20 || (b >= 0x09 && b <= 0x0d);
|
|
72
|
+
for (let i = 0; i < n;) {
|
|
73
|
+
const b = bytes[i];
|
|
74
|
+
if (!ws(b)) {
|
|
75
|
+
out[o++] = b >= 0x41 && b <= 0x5a ? b + 0x20 : b;
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
let j = i;
|
|
80
|
+
while (j < n && ws(bytes[j]))
|
|
81
|
+
j++;
|
|
82
|
+
if (i > 0 && j < n)
|
|
83
|
+
out[o++] = 0x20;
|
|
84
|
+
else
|
|
85
|
+
for (let k = i; k < j; k++)
|
|
86
|
+
out[o++] = bytes[k];
|
|
87
|
+
i = j;
|
|
88
|
+
}
|
|
89
|
+
return o === n ? out : out.slice(0, o);
|
|
90
|
+
}
|
|
47
91
|
/** 32-bit FNV-1a over a canonical key — the integer the store's canon index
|
|
48
92
|
* is keyed on. Same construction as the node table's content hash; a
|
|
49
93
|
* collision is resolved by verifying canon(stored) === key, never trusted. */
|
package/dist/src/geometry.d.ts
CHANGED
|
@@ -204,7 +204,7 @@ export interface ContentFold {
|
|
|
204
204
|
* streams. Verifying the bytes here would cost O(prefix) and defeat the
|
|
205
205
|
* whole point, so the obligation sits with the caller, and every caller
|
|
206
206
|
* discharges it structurally rather than by care: `perceiveDeposit` looks the
|
|
207
|
-
* entry up under `
|
|
207
|
+
* entry up under `latin1(bytes.subarray(0, L))` — the prefix's own bytes
|
|
208
208
|
* ARE the cache key — and a conversation's fold state advances only by
|
|
209
209
|
* append. A new caller that cannot make the same structural argument must
|
|
210
210
|
* pass no `prev` at all; the cold path is always correct.
|
|
@@ -214,6 +214,24 @@ export declare function contentFoldIncremental(space: Space, alphabet: Alphabet,
|
|
|
214
214
|
tree: Sema;
|
|
215
215
|
fold: ContentFold;
|
|
216
216
|
};
|
|
217
|
+
/** The node a byte stream's content fold NAMES — `foldTree` over
|
|
218
|
+
* {@link contentFoldSpan}'s tree, without building a single vector.
|
|
219
|
+
*
|
|
220
|
+
* A fold names a node only when every child is named, so identity needs the
|
|
221
|
+
* tree's SHAPE and the store's answer for each node — and the shape is a
|
|
222
|
+
* function of the bytes (cuts and levels) plus, inside an over-long row, each
|
|
223
|
+
* item's {@link itemKey}: eight coordinates of its raw gist. Those are read
|
|
224
|
+
* lazily through {@link boundCoord}, bit-identical to the coordinates the
|
|
225
|
+
* vector fold computes, so the grouping (the SAME {@link groupByLevel}) cannot
|
|
226
|
+
* differ. `segment(from, to)` names a level-0 segment (one flat node over
|
|
227
|
+
* single-byte atoms, or the atom itself); `branch(kids, from, to)` names the
|
|
228
|
+
* group covering [from, to) — `kids` holds null for an unnamed child, because
|
|
229
|
+
* the store names a branch by its BYTES when its children do not name it
|
|
230
|
+
* (the write side's step 1b, store.ts `intern`), so an unnamed child does not
|
|
231
|
+
* settle its ancestors. Every item's grouping key is read from its content,
|
|
232
|
+
* named or not, for the same reason. An empty stream is the caller's: its
|
|
233
|
+
* fold is the alphabet's zero-byte leaf, not a segment. */
|
|
234
|
+
export declare function contentIdentity(space: Space, alphabet: Alphabet, bytes: Uint8Array, segment: (from: number, to: number) => number | null, branch: (kids: ReadonlyArray<number | null>, from: number, to: number) => number | null): number | null;
|
|
217
235
|
/** A stable-prefix fold's reusable state: the segment edge offsets and each
|
|
218
236
|
* segment's independently-folded root ({@link riverFoldRaw} output). A
|
|
219
237
|
* grown stream whose boundary set EXTENDS a previous fold's reuses every
|