@modernrelay/orbit-core 0.13.5 → 0.14.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.
- package/dist/{chunk-NIJX5NVJ.js → chunk-KRCG2JFN.js} +169 -3
- package/dist/chunk-KRCG2JFN.js.map +1 -0
- package/dist/engine.d.ts +1 -1
- package/dist/{index-CFZMson3.d.ts → index-CPRTWpGA.d.ts} +176 -176
- package/dist/index.d.ts +611 -611
- package/dist/index.js +191 -154
- package/dist/index.js.map +1 -1
- package/dist/{lane-CHHLuxgq.d.ts → lane-_KPQ1deI.d.ts} +43 -43
- package/dist/testing.d.ts +22 -22
- package/dist/testing.js +99 -16
- package/dist/testing.js.map +1 -1
- package/dist/worker/entry.js +259 -2
- package/dist/worker/entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GUXIITKK.js +0 -89
- package/dist/chunk-GUXIITKK.js.map +0 -1
- package/dist/chunk-NIJX5NVJ.js.map +0 -1
- package/dist/chunk-XAR262L3.js +0 -140
- package/dist/chunk-XAR262L3.js.map +0 -1
- package/dist/worker/entry.d.ts +0 -2
package/dist/chunk-XAR262L3.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { DIAGNOSTIC_SAMPLE_CAP } from './chunk-NIJX5NVJ.js';
|
|
2
|
-
|
|
3
|
-
// src/clusters.ts
|
|
4
|
-
var clusterProbe = {
|
|
5
|
-
derivations: 0,
|
|
6
|
-
memberVisits: 0
|
|
7
|
-
};
|
|
8
|
-
function resetClusterProbe() {
|
|
9
|
-
clusterProbe.derivations = 0;
|
|
10
|
-
clusterProbe.memberVisits = 0;
|
|
11
|
-
}
|
|
12
|
-
var DEFAULT_LAYOUT_SEED = 1592594996;
|
|
13
|
-
var DEFAULT_CLUSTER_CENTER_RADIUS = 1024;
|
|
14
|
-
function hash32(key, seed) {
|
|
15
|
-
let h = (2166136261 ^ (seed | 0)) >>> 0;
|
|
16
|
-
for (let i = 0; i < key.length; i++) {
|
|
17
|
-
h = (h ^ key.charCodeAt(i)) >>> 0;
|
|
18
|
-
h = Math.imul(h, 16777619) >>> 0;
|
|
19
|
-
}
|
|
20
|
-
h = (h ^ h >>> 16) >>> 0;
|
|
21
|
-
h = Math.imul(h, 2246822507) >>> 0;
|
|
22
|
-
h = (h ^ h >>> 13) >>> 0;
|
|
23
|
-
h = Math.imul(h, 3266489909) >>> 0;
|
|
24
|
-
return (h ^ h >>> 16) >>> 0;
|
|
25
|
-
}
|
|
26
|
-
var UNIT = 23283064365386963e-26;
|
|
27
|
-
function radicalInverse(index, base) {
|
|
28
|
-
let result = 0;
|
|
29
|
-
let denominator = 1;
|
|
30
|
-
let n = index;
|
|
31
|
-
while (n > 0) {
|
|
32
|
-
denominator *= base;
|
|
33
|
-
result += n % base / denominator;
|
|
34
|
-
n = Math.floor(n / base);
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
function frac(v) {
|
|
39
|
-
return v - Math.floor(v);
|
|
40
|
-
}
|
|
41
|
-
function generateClusterCenters(keys, seed = DEFAULT_LAYOUT_SEED, radius = DEFAULT_CLUSTER_CENTER_RADIUS) {
|
|
42
|
-
const out = new Float32Array(2 * keys.length);
|
|
43
|
-
for (let i = 0; i < keys.length; i++) {
|
|
44
|
-
const key = keys[i];
|
|
45
|
-
const u = frac(radicalInverse(i + 1, 2) + hash32(key, seed) * UNIT);
|
|
46
|
-
const v = frac(radicalInverse(i + 1, 3) + hash32(key, seed ^ 2654435769 | 0) * UNIT);
|
|
47
|
-
out[2 * i] = radius * (2 * u - 1);
|
|
48
|
-
out[2 * i + 1] = radius * (2 * v - 1);
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
function resolveClusterCenters(keys, explicit, seed = DEFAULT_LAYOUT_SEED, radius = DEFAULT_CLUSTER_CENTER_RADIUS) {
|
|
53
|
-
const out = generateClusterCenters(keys, seed, radius);
|
|
54
|
-
if (explicit === void 0 || explicit.size === 0) return out;
|
|
55
|
-
for (let i = 0; i < keys.length; i++) {
|
|
56
|
-
const pair = explicit.get(keys[i]);
|
|
57
|
-
if (pair === void 0) continue;
|
|
58
|
-
const x = pair[0];
|
|
59
|
-
const y = pair[1];
|
|
60
|
-
if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
|
|
61
|
-
out[2 * i] = x;
|
|
62
|
-
out[2 * i + 1] = y;
|
|
63
|
-
}
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
function deriveClusters(nodes, by, sceneCount = nodes.length) {
|
|
67
|
-
clusterProbe.derivations++;
|
|
68
|
-
const keys = [];
|
|
69
|
-
const ordinalByKey = /* @__PURE__ */ new Map();
|
|
70
|
-
const membersByKey = /* @__PURE__ */ new Map();
|
|
71
|
-
const slotOrdinals = new Float32Array(Math.max(sceneCount, nodes.length)).fill(NaN);
|
|
72
|
-
let errorCount = 0;
|
|
73
|
-
const errorSamples = [];
|
|
74
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
75
|
-
clusterProbe.memberVisits++;
|
|
76
|
-
const node = nodes[i];
|
|
77
|
-
let raw;
|
|
78
|
-
try {
|
|
79
|
-
raw = by(node);
|
|
80
|
-
} catch {
|
|
81
|
-
errorCount++;
|
|
82
|
-
if (errorSamples.length < DIAGNOSTIC_SAMPLE_CAP) errorSamples.push(node.id);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (typeof raw !== "string") continue;
|
|
86
|
-
let ordinal = ordinalByKey.get(raw);
|
|
87
|
-
if (ordinal === void 0) {
|
|
88
|
-
ordinal = keys.length;
|
|
89
|
-
keys.push(raw);
|
|
90
|
-
ordinalByKey.set(raw, ordinal);
|
|
91
|
-
membersByKey.set(raw, []);
|
|
92
|
-
}
|
|
93
|
-
membersByKey.get(raw).push(node.id);
|
|
94
|
-
slotOrdinals[i] = ordinal;
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
keys,
|
|
98
|
-
ordinalByKey,
|
|
99
|
-
membersByKey,
|
|
100
|
-
slotOrdinals,
|
|
101
|
-
diagnostic: errorCount === 0 ? null : {
|
|
102
|
-
code: "accessor-error",
|
|
103
|
-
severity: "warning",
|
|
104
|
-
count: errorCount,
|
|
105
|
-
sampleIds: errorSamples,
|
|
106
|
-
message: "clusters.by threw; the affected nodes derived as unclustered (\xA716.3/\xA78)"
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
function clusterCentroids(slotOrdinals, positions, clusterCount, fallback) {
|
|
111
|
-
const out = new Float32Array(2 * clusterCount);
|
|
112
|
-
const counts = new Float64Array(clusterCount);
|
|
113
|
-
const slots = Math.min(slotOrdinals.length, Math.floor(positions.length / 2));
|
|
114
|
-
for (let i = 0; i < slots; i++) {
|
|
115
|
-
const ordinal = slotOrdinals[i];
|
|
116
|
-
if (Number.isNaN(ordinal)) continue;
|
|
117
|
-
clusterProbe.memberVisits++;
|
|
118
|
-
const x = positions[2 * i];
|
|
119
|
-
const y = positions[2 * i + 1];
|
|
120
|
-
if (Number.isNaN(x) || Number.isNaN(y)) continue;
|
|
121
|
-
out[2 * ordinal] = out[2 * ordinal] + x;
|
|
122
|
-
out[2 * ordinal + 1] = out[2 * ordinal + 1] + y;
|
|
123
|
-
counts[ordinal] = counts[ordinal] + 1;
|
|
124
|
-
}
|
|
125
|
-
for (let c = 0; c < clusterCount; c++) {
|
|
126
|
-
const n = counts[c];
|
|
127
|
-
if (n === 0) {
|
|
128
|
-
out[2 * c] = fallback[2 * c] ?? 0;
|
|
129
|
-
out[2 * c + 1] = fallback[2 * c + 1] ?? 0;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
out[2 * c] = out[2 * c] / n;
|
|
133
|
-
out[2 * c + 1] = out[2 * c + 1] / n;
|
|
134
|
-
}
|
|
135
|
-
return out;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export { DEFAULT_CLUSTER_CENTER_RADIUS, DEFAULT_LAYOUT_SEED, clusterCentroids, clusterProbe, deriveClusters, generateClusterCenters, resetClusterProbe, resolveClusterCenters };
|
|
139
|
-
//# sourceMappingURL=chunk-XAR262L3.js.map
|
|
140
|
-
//# sourceMappingURL=chunk-XAR262L3.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clusters.ts"],"names":[],"mappings":"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","file":"chunk-XAR262L3.js","sourcesContent":["/**\n * §16.3 stage-4 non-collapsing clusters (S12-T06) — pure derivation, no\n * engine, no DOM, no instance.\n *\n * Contract summary (spec §16.3 / §7.6 stage 4, R-16.3-17..21):\n * - Clusters are a categorical PARTITION of the current PHYSICAL scene, never\n * a rewrite: `deriveClusters` returns membership only. It synthesizes no\n * super-nodes and no meta-edges, so node and edge counts are identical\n * before and after a cluster spec lands (R-16.3-17/19) — clusters coexist\n * with the stage-3 group rewrite by construction.\n * - `null` (and any non-string) from `by` means UNCLUSTERED: the slot carries\n * NaN in {@link ClusterDerivation.slotOrdinals} (the engine contract's\n * \"not in any cluster\" value, mirroring the NaN-position convention).\n * - Missing force centers generate deterministically from the ORDERED cluster\n * keys plus the layout seed (R-16.3-20). {@link generateClusterCenters} uses\n * only IEEE-754 correctly-rounded operations (+ - * / and integer ops) — no\n * transcendentals — so the same ordered keys and seed produce BIT-IDENTICAL\n * centers on any conforming engine, and a different seed produces different\n * ones.\n * - Overlay anchoring must never scan members per frame (R-16.3-21):\n * {@link clusterCentroids} is the ONLY member-scanning anchor path and runs\n * at settle (from the single permitted §7.1 readback) or at commit under a\n * fixed layout. {@link clusterProbe} counts every member visit so tests can\n * pin \"zero per-frame member iterations\".\n */\n\nimport type { GraphDiagnostic, GraphNode, NodeId } from './types';\nimport { DIAGNOSTIC_SAMPLE_CAP } from './types';\n\n// ---------------------------------------------------------------------------\n// Instrumentation (§18 test seam, re-exported from /testing).\n// ---------------------------------------------------------------------------\n\n/**\n * Cluster-work counters. `memberVisits` counts EVERY per-member iteration the\n * cluster lane performs (derivation scans and centroid scans — the only two\n * O(members) passes); `derivations` counts stage-4 recomputations. Both are\n * unconditional O(1) increments so the instrumented and production paths are\n * identical code. Tests snapshot, act, and compare: a per-frame overlay tick\n * must add ZERO member visits (R-16.3-21) and a stage-5 soft-mask change must\n * add ZERO derivations.\n */\nexport const clusterProbe = {\n derivations: 0,\n memberVisits: 0,\n};\n\n/** Reset both counters (per-test isolation). */\nexport function resetClusterProbe(): void {\n clusterProbe.derivations = 0;\n clusterProbe.memberVisits = 0;\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic force-center generation (R-16.3-20).\n// ---------------------------------------------------------------------------\n\n/**\n * The layout seed generated cluster centers key on.\n *\n * FINDING (S12-T06 investigation, recorded here because it is a contract\n * decision): the core owns NO numeric layout seed in v0.10. The only\n * layout-seeding knob on the §13 config contract is\n * `EngineConfigUpdate.seedRadius` (the ring radius unknown/NaN positions seed\n * onto) and `instance.ts` never sets it — every adapter defaults it itself\n * (`CosmosEngine` → `spaceSize / 4`). Spec §10's `LayoutSpec.seed` (the\n * `static` layout's reproducibility seed) is not implemented in v0.10 either.\n * So this constant is the single named place the instance keys generated\n * centers on until §10's normalized layout object lands and can supply\n * `LayoutSpec.seed` here.\n */\nexport const DEFAULT_LAYOUT_SEED = 0x5eed_1234;\n\n/**\n * Radius of the square region generated centers spread over, in SPACE\n * coordinates. Mirrors the role of `EngineConfigUpdate.seedRadius` (see\n * {@link DEFAULT_LAYOUT_SEED}) and matches `CosmosEngine`'s own default\n * (`spaceSize / 4` = 4096 / 4).\n */\nexport const DEFAULT_CLUSTER_CENTER_RADIUS = 1024;\n\n/** FNV-1a over the key mixed with the seed, finished with murmur3's fmix32. */\nfunction hash32(key: string, seed: number): number {\n let h = (0x811c9dc5 ^ (seed | 0)) >>> 0;\n for (let i = 0; i < key.length; i++) {\n h = (h ^ key.charCodeAt(i)) >>> 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n h = (h ^ (h >>> 16)) >>> 0;\n h = Math.imul(h, 0x85ebca6b) >>> 0;\n h = (h ^ (h >>> 13)) >>> 0;\n h = Math.imul(h, 0xc2b2ae35) >>> 0;\n return (h ^ (h >>> 16)) >>> 0;\n}\n\n/** 2^-32 — exact in binary64, so `hash * UNIT` is exact. */\nconst UNIT = 2.3283064365386963e-10;\n\n/** van der Corput radical inverse — integer ops plus one division per digit,\n * all correctly rounded, so the result is reproducible bit-for-bit. */\nfunction radicalInverse(index: number, base: number): number {\n let result = 0;\n let denominator = 1;\n let n = index;\n while (n > 0) {\n denominator *= base;\n result += (n % base) / denominator;\n n = Math.floor(n / base);\n }\n return result;\n}\n\nfunction frac(v: number): number {\n return v - Math.floor(v);\n}\n\n/**\n * Deterministic force centers for ORDERED cluster keys (R-16.3-20): a\n * low-discrepancy (Halton base 2/3) spread offset per key by a seeded hash,\n * mapped into `[-radius, radius]²`.\n *\n * Pure and total: same ordered keys + same seed ⇒ bit-identical output (only\n * IEEE-754 correctly-rounded arithmetic is used — no `Math.sin/cos`, whose\n * results are implementation-defined); a different seed ⇒ different output;\n * reordering or renaming keys ⇒ different output (both the ordinal and the\n * key text feed the placement).\n */\nexport function generateClusterCenters(\n keys: readonly string[],\n seed: number = DEFAULT_LAYOUT_SEED,\n radius: number = DEFAULT_CLUSTER_CENTER_RADIUS,\n): Float32Array {\n const out = new Float32Array(2 * keys.length);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i]!;\n const u = frac(radicalInverse(i + 1, 2) + hash32(key, seed) * UNIT);\n const v = frac(radicalInverse(i + 1, 3) + hash32(key, (seed ^ 0x9e3779b9) | 0) * UNIT);\n out[2 * i] = radius * (2 * u - 1);\n out[2 * i + 1] = radius * (2 * v - 1);\n }\n return out;\n}\n\n/**\n * Force centers for ordered keys with caller-supplied entries winning:\n * ONLY the missing keys generate (R-16.3-20). A non-finite explicit pair is\n * treated as missing (D4 boundary hygiene — a NaN center would poison the\n * engine's force field).\n */\nexport function resolveClusterCenters(\n keys: readonly string[],\n explicit: ReadonlyMap<string, readonly [number, number]> | undefined,\n seed: number = DEFAULT_LAYOUT_SEED,\n radius: number = DEFAULT_CLUSTER_CENTER_RADIUS,\n): Float32Array {\n const out = generateClusterCenters(keys, seed, radius);\n if (explicit === undefined || explicit.size === 0) return out;\n for (let i = 0; i < keys.length; i++) {\n const pair = explicit.get(keys[i]!);\n if (pair === undefined) continue;\n const x = pair[0];\n const y = pair[1];\n if (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n out[2 * i] = x;\n out[2 * i + 1] = y;\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Derivation.\n// ---------------------------------------------------------------------------\n\nexport interface ClusterDerivation {\n /** Distinct keys in FIRST-ENCOUNTER order over the physical scene (the §11\n * categorical-domain convention, shared with groupBy). Ordinal i ↔ keys[i]. */\n keys: readonly string[];\n /** key → ordinal (the reverse of `keys`). */\n ordinalByKey: ReadonlyMap<string, number>;\n /** key → member PHYSICAL node ids, scene order. Public ids only. */\n membersByKey: ReadonlyMap<string, readonly NodeId[]>;\n /**\n * PHYSICAL slot → cluster ordinal, NaN = unclustered. Length is the caller's\n * `sceneCount`, so synthetic suffix slots (§16.3 super-nodes/meta-edges) are\n * always NaN: aggregates are never members of a cluster. This IS the engine\n * `config.cluster.pointClusters` payload — no copy at the sink.\n */\n slotOrdinals: Float32Array;\n /** ONE aggregated 'accessor-error' warning when `by` threw (affected nodes\n * derive as unclustered — never silent loss, I3), else null. */\n diagnostic: GraphDiagnostic | null;\n}\n\n/**\n * §16.3 stage-4 derivation over the PHYSICAL prefix of the current scene.\n *\n * `nodes` are the physical rows (post-group-rewrite when a rewrite is live)\n * aligned to scene slots `0..nodes.length-1`; `sceneCount` is the FULL scene\n * point count so the returned `slotOrdinals` covers the synthetic suffix too.\n * Preserves everything and synthesizes nothing (R-16.3-17/19).\n */\nexport function deriveClusters<N>(\n nodes: readonly GraphNode<N>[],\n by: (node: GraphNode<N>) => string | null,\n sceneCount: number = nodes.length,\n): ClusterDerivation {\n clusterProbe.derivations++;\n const keys: string[] = [];\n const ordinalByKey = new Map<string, number>();\n const membersByKey = new Map<string, NodeId[]>();\n const slotOrdinals = new Float32Array(Math.max(sceneCount, nodes.length)).fill(NaN);\n let errorCount = 0;\n const errorSamples: string[] = [];\n\n for (let i = 0; i < nodes.length; i++) {\n clusterProbe.memberVisits++;\n const node = nodes[i]!;\n let raw: string | null;\n try {\n raw = by(node);\n } catch {\n errorCount++;\n if (errorSamples.length < DIAGNOSTIC_SAMPLE_CAP) errorSamples.push(node.id);\n continue;\n }\n if (typeof raw !== 'string') continue; // null / non-string ⇒ unclustered\n let ordinal = ordinalByKey.get(raw);\n if (ordinal === undefined) {\n ordinal = keys.length;\n keys.push(raw);\n ordinalByKey.set(raw, ordinal);\n membersByKey.set(raw, []);\n }\n membersByKey.get(raw)!.push(node.id);\n slotOrdinals[i] = ordinal;\n }\n\n return {\n keys,\n ordinalByKey,\n membersByKey,\n slotOrdinals,\n diagnostic:\n errorCount === 0\n ? null\n : {\n code: 'accessor-error',\n severity: 'warning',\n count: errorCount,\n sampleIds: errorSamples,\n message: 'clusters.by threw; the affected nodes derived as unclustered (§16.3/§8)',\n },\n };\n}\n\n/**\n * Centroids of every cluster from a slot-aligned position buffer — the ONLY\n * member-scanning anchor path (R-16.3-21). Called at settle (over the single\n * permitted §7.1 readback) and at commit under a FIXED layout; never per\n * frame. Slots with unknown (NaN) positions are skipped; a cluster with no\n * placeable member keeps its `fallback` entry (its force center), so a label\n * never jumps to the origin.\n */\nexport function clusterCentroids(\n slotOrdinals: Float32Array,\n positions: Float32Array,\n clusterCount: number,\n fallback: Float32Array,\n): Float32Array {\n const out = new Float32Array(2 * clusterCount);\n const counts = new Float64Array(clusterCount);\n const slots = Math.min(slotOrdinals.length, Math.floor(positions.length / 2));\n for (let i = 0; i < slots; i++) {\n const ordinal = slotOrdinals[i]!;\n if (Number.isNaN(ordinal)) continue;\n clusterProbe.memberVisits++;\n const x = positions[2 * i]!;\n const y = positions[2 * i + 1]!;\n if (Number.isNaN(x) || Number.isNaN(y)) continue;\n out[2 * ordinal] = out[2 * ordinal]! + x;\n out[2 * ordinal + 1] = out[2 * ordinal + 1]! + y;\n counts[ordinal] = counts[ordinal]! + 1;\n }\n for (let c = 0; c < clusterCount; c++) {\n const n = counts[c]!;\n if (n === 0) {\n out[2 * c] = fallback[2 * c] ?? 0;\n out[2 * c + 1] = fallback[2 * c + 1] ?? 0;\n continue;\n }\n out[2 * c] = out[2 * c]! / n;\n out[2 * c + 1] = out[2 * c + 1]! / n;\n }\n return out;\n}\n"]}
|
package/dist/worker/entry.d.ts
DELETED