@datakable/core 2.0.3 → 2.1.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/sorting/ranks.ts"],"sourcesContent":["import { RANK, typeRank } from './compare'\n\n/** Comparisons / rows between two `yield`s of the jobs below (§9.1). */\nconst CHUNK = 4096\n/** Run length sorted natively before the merge passes. */\nconst RUN = 512\n\n/**\n * Sorts `0..n-1` by `cmp` as a time-sliced job (§9.1): native sorts of short runs, then bottom-up\n * merge passes that `yield` every {@link CHUNK} comparisons. Stable.\n */\nfunction* sortPositions(\n n: number,\n cmp: (a: number, b: number) => number,\n): Generator<undefined, Uint32Array, undefined> {\n let a = new Uint32Array(n)\n for (let i = 0; i < n; i++) a[i] = i\n for (let lo = 0; lo < n; lo += RUN) {\n a.subarray(lo, Math.min(lo + RUN, n)).sort(cmp)\n yield\n }\n let b = new Uint32Array(n)\n let ops = 0\n for (let width = RUN; width < n; width *= 2) {\n for (let lo = 0; lo < n; lo += 2 * width) {\n const mid = Math.min(lo + width, n)\n const hi = Math.min(lo + 2 * width, n)\n let i = lo\n let j = mid\n let k = lo\n while (i < mid && j < hi) {\n const x = a[i] as number\n const y = a[j] as number\n if (cmp(y, x) < 0) {\n b[k++] = y\n j++\n } else {\n b[k++] = x\n i++\n }\n if (++ops === CHUNK) {\n ops = 0\n yield\n }\n }\n while (i < mid) b[k++] = a[i++] as number\n while (j < hi) b[k++] = a[j++] as number\n }\n const t = a\n a = b\n b = t\n }\n return a\n}\n\n/**\n * Collation ranks of a string column (§6.4, §9.1): per row, the dense rank of its string among\n * the column's distinct strings in `collator` order (strings the collator finds equal share a\n * rank), `NaN` for a missing value. Sorting by these numbers gives exactly the order of\n * `collator.compare`, with integer keys (counting / radix sort) instead of a collator call per\n * comparison. A time-sliced job; `null` when a present value is not a string.\n */\nexport function* collationRanks(\n values: readonly unknown[],\n collator: Intl.Collator,\n): Generator<undefined, Float64Array | null, undefined> {\n const n = values.length\n const ids = new Map<string, number>()\n const distinct: string[] = []\n /** Per row: its string's position in `distinct`, -1 when missing. */\n const slot = new Int32Array(n)\n for (let r = 0; r < n; r++) {\n const v = values[r]\n if (typeof v === 'string') {\n let id = ids.get(v)\n if (id === undefined) {\n id = distinct.length\n ids.set(v, id)\n distinct.push(v)\n }\n slot[r] = id\n } else if (typeRank(v) === RANK.missing) slot[r] = -1\n else return null\n if (r % CHUNK === CHUNK - 1) yield\n }\n const order = yield* sortPositions(distinct.length, (a, b) =>\n collator.compare(distinct[a] as string, distinct[b] as string),\n )\n const rankOf = new Float64Array(distinct.length)\n let rank = 0\n for (let k = 0; k < order.length; k++) {\n const id = order[k] as number\n const s = distinct[id] as string\n if (k > 0 && collator.compare(distinct[order[k - 1] as number] as string, s) !== 0) rank++\n rankOf[id] = rank\n if (k % CHUNK === CHUNK - 1) yield\n }\n const out = new Float64Array(n)\n for (let r = 0; r < n; r++) {\n const id = slot[r] as number\n out[r] = id < 0 ? Number.NaN : (rankOf[id] as number)\n }\n return out\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/features/sorting/ranks.ts"],"sourcesContent":["import { RANK, typeRank } from './compare'\n\n/** Comparisons / rows between two `yield`s of the jobs below (§9.1). */\nconst CHUNK = 4096\n/**\n * Run length sorted natively before the merge passes (#97, was 512): each doubling saves a merge\n * pass, and one native run of collator calls stays within a slice even under a 4x CPU throttle.\n */\nconst RUN = 1024\n\n/**\n * Sorts `0..n-1` by `cmp` as a time-sliced job (§9.1): native sorts of `run`s, then bottom-up\n * merge passes that `yield` every {@link CHUNK} comparisons. Stable. Also returns `eq`:\n * `eq[k] = 1` when `order[k]` compares equal to `order[k - 1]`, from the comparisons the merges\n * make anyway (#97). Inside a native run it costs a second pass only when that sort saw a 0.\n */\nfunction* sortPositions(\n n: number,\n cmp: (a: number, b: number) => number,\n run: number,\n): Generator<undefined, { order: Uint32Array; eq: Uint8Array }, undefined> {\n let a = new Uint32Array(n)\n let ea = new Uint8Array(n)\n for (let i = 0; i < n; i++) a[i] = i\n for (let lo = 0; lo < n; lo += run) {\n const hi = Math.min(lo + run, n)\n let zero = false\n a.subarray(lo, hi).sort((x, y) => {\n const c = cmp(x, y)\n if (c === 0) zero = true\n return c\n })\n // Neighbours can only be equal if the sort saw a 0 (it must, to order them).\n if (zero)\n for (let k = lo + 1; k < hi; k++) ea[k] = cmp(a[k - 1] as number, a[k] as number) ? 0 : 1\n yield\n }\n let b = new Uint32Array(n)\n let eb = new Uint8Array(n)\n let ops = 0\n for (let width = run; width < n; width *= 2) {\n for (let lo = 0; lo < n; lo += 2 * width) {\n const mid = Math.min(lo + width, n)\n const hi = Math.min(lo + 2 * width, n)\n let i = lo\n let j = mid\n // The run of the last output (-1: none yet) and whether it equals the other run's head.\n let prev = -1\n let same = 0\n for (let k = lo; k < hi; k++) {\n let c = 1\n let src: number\n if (i < mid && j < hi) {\n c = cmp(a[j] as number, a[i] as number)\n src = c < 0 ? 1 : 0\n if (++ops === CHUNK) {\n ops = 0\n yield\n }\n } else src = i < mid ? 0 : 1\n const p = src ? j++ : i++\n b[k] = a[p] as number\n eb[k] = prev === src ? (ea[p] as number) : prev < 0 ? 0 : same\n same = src === 0 && c === 0 ? 1 : 0\n prev = src\n }\n }\n const t = a\n a = b\n b = t\n const e = ea\n ea = eb\n eb = e\n }\n return { order: a, eq: ea }\n}\n\n/**\n * What the next {@link collationRanks} of the same column starts from (#95): the values it ranked,\n * their ranks (the `ranks` it returned, shared) and the distinct strings in rank order (one per\n * rank, shared with the data). No string → id map: a row whose string is unchanged at its position\n * (or at a shifted one) keeps its rank, any other string is found by binary search over `reps`.\n * Never mutated (a job builds a new one), so a cancelled job leaves it usable.\n */\nexport interface RankState {\n readonly values: readonly unknown[]\n readonly ranks: ArrayLike<number>\n readonly reps: readonly string[]\n}\n\nexport interface Ranked {\n readonly ranks: Float64Array\n readonly state: RankState\n}\n\n/**\n * Collation ranks of a string column (§6.4, §9.1): per row, the dense rank of its string among\n * the column's distinct strings in `collator` order (strings the collator finds equal share a\n * rank), `NaN` for a missing value. Sorting by these numbers gives exactly the order of\n * `collator.compare`, with integer keys (counting / radix sort) instead of a collator call per\n * comparison. A time-sliced job; `null` when a present value is not a string.\n *\n * Incremental across data versions (#95): a string ranked in `prev` (the state of an earlier array\n * of the same column) keeps that rank as its sort key. It is read from the same row of `prev` when\n * the string is the same there (or at the row offset of the last match, for inserted / removed\n * rows), else found by binary search over the ranked strings; a new string is placed in the gap\n * below the first greater one. Only new strings are sorted (two in the same gap call the\n * collator): a one-row edit costs O(log d) collator calls (and a sort of numbers), not a cold\n * sort. The new state holds only the strings present now.\n */\nexport function* collationRanks(\n values: readonly unknown[],\n collator: Intl.Collator,\n prev: RankState = { values: [], ranks: [], reps: [] },\n): Generator<undefined, Ranked | null, undefined> {\n const n = values.length\n const { values: was, ranks: wasRank, reps } = prev\n const m = reps.length\n /** Per row: its rank in `prev`, `m + id` for the new string `fresh[id]`, NaN when missing. */\n const out = new Float64Array(n)\n /** Ranks of `prev` still present (the rest are dropped from the new state). */\n const used = new Uint8Array(m)\n // New strings only (a cold job: every distinct string), dropped with the job.\n const freshIds = new Map<string, number>()\n const fresh: string[] = []\n /** Per new string: `g - 0.5`, between ranks g-1 and g of `prev`. */\n const gap: number[] = []\n /** Per rank of `prev`: one row holding it, built on the first lookup that finds one. */\n let rowOf: Int32Array | undefined\n /** Offset to `prev`'s row of the last match: rows inserted / removed above shift it. */\n let shift = 0\n let ops = 0\n for (let r = 0; r < n; r++) {\n const v = values[r]\n if (typeof v === 'string') {\n let k: number | undefined\n const q = r + shift\n // Out of range: `undefined`, never a string.\n if (was[q] === v) k = wasRank[q] as number\n else {\n const id = freshIds.get(v)\n if (id !== undefined) k = m + id\n else {\n let lo = 0\n let hi = m\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n const c = collator.compare(v, reps[mid] as string)\n ops++\n if (c === 0) {\n k = mid\n break\n }\n if (c < 0) hi = mid\n else lo = mid + 1\n }\n if (k === undefined) {\n k = m + fresh.length\n freshIds.set(v, fresh.length)\n fresh.push(v)\n gap.push(lo - 0.5)\n } else {\n if (!rowOf) {\n rowOf = new Int32Array(m)\n for (let p = 0; p < was.length; p++) {\n const x = wasRank[p] as number\n if (!Number.isNaN(x)) rowOf[x] = p\n }\n }\n shift = (rowOf[k] as number) - r\n }\n }\n }\n if (k < m) used[k] = 1\n out[r] = k\n } else if (typeRank(v) === RANK.missing) out[r] = Number.NaN\n else return null\n if (++ops >= CHUNK) {\n ops = 0\n yield\n }\n }\n // The same strings as `prev` (a new array of equal content, rows moved): the same ranks.\n if (!fresh.length && !used.includes(0)) return { ranks: out, state: { values, ranks: out, reps } }\n\n const f = fresh.length\n const { order, eq } = yield* sortPositions(\n f,\n // By gap; two new strings in the same gap (cold: all of them) go to the collator.\n (a, b) =>\n (gap[a] as number) - (gap[b] as number) ||\n collator.compare(fresh[a] as string, fresh[b] as string),\n // Comparing numbers is far cheaper: longer native runs, fewer merge passes.\n m ? 8 * RUN : RUN,\n )\n // Merge the kept ranks of `prev` and the new strings (each before the rank above its gap). No\n // collator call: one pass, like the rank lookup below.\n const remap = new Float64Array(m + f)\n const kept: string[] = []\n let rank = -1\n let j = 0\n for (let g = 0; g <= m; g++) {\n while (j < f && (gap[order[j] as number] as number) < g) {\n const id = order[j] as number\n if (!eq[j]) kept[++rank] = fresh[id] as string\n remap[m + id] = rank\n j++\n }\n if (g < m && used[g]) {\n kept[++rank] = reps[g] as string\n remap[g] = rank\n }\n }\n for (let r = 0; r < n; r++) {\n const k = out[r] as number\n if (!Number.isNaN(k)) out[r] = remap[k] as number\n }\n return { ranks: out, state: { values, ranks: out, reps: kept } }\n}\n"],"mappings":"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","names":[]}
@@ -1,85 +1,152 @@
1
1
  // src/features/sorting/ranks.ts
2
2
  import { RANK, typeRank } from "./compare.js";
3
3
  var CHUNK = 4096;
4
- var RUN = 512;
5
- function* sortPositions(n, cmp) {
4
+ var RUN = 1024;
5
+ function* sortPositions(n, cmp, run) {
6
6
  let a = new Uint32Array(n);
7
+ let ea = new Uint8Array(n);
7
8
  for (let i = 0; i < n; i++) a[i] = i;
8
- for (let lo = 0; lo < n; lo += RUN) {
9
- a.subarray(lo, Math.min(lo + RUN, n)).sort(cmp);
9
+ for (let lo = 0; lo < n; lo += run) {
10
+ const hi = Math.min(lo + run, n);
11
+ let zero = false;
12
+ a.subarray(lo, hi).sort((x, y) => {
13
+ const c = cmp(x, y);
14
+ if (c === 0) zero = true;
15
+ return c;
16
+ });
17
+ if (zero)
18
+ for (let k = lo + 1; k < hi; k++) ea[k] = cmp(a[k - 1], a[k]) ? 0 : 1;
10
19
  yield;
11
20
  }
12
21
  let b = new Uint32Array(n);
22
+ let eb = new Uint8Array(n);
13
23
  let ops = 0;
14
- for (let width = RUN; width < n; width *= 2) {
24
+ for (let width = run; width < n; width *= 2) {
15
25
  for (let lo = 0; lo < n; lo += 2 * width) {
16
26
  const mid = Math.min(lo + width, n);
17
27
  const hi = Math.min(lo + 2 * width, n);
18
28
  let i = lo;
19
29
  let j = mid;
20
- let k = lo;
21
- while (i < mid && j < hi) {
22
- const x = a[i];
23
- const y = a[j];
24
- if (cmp(y, x) < 0) {
25
- b[k++] = y;
26
- j++;
27
- } else {
28
- b[k++] = x;
29
- i++;
30
- }
31
- if (++ops === CHUNK) {
32
- ops = 0;
33
- yield;
34
- }
30
+ let prev = -1;
31
+ let same = 0;
32
+ for (let k = lo; k < hi; k++) {
33
+ let c = 1;
34
+ let src;
35
+ if (i < mid && j < hi) {
36
+ c = cmp(a[j], a[i]);
37
+ src = c < 0 ? 1 : 0;
38
+ if (++ops === CHUNK) {
39
+ ops = 0;
40
+ yield;
41
+ }
42
+ } else src = i < mid ? 0 : 1;
43
+ const p = src ? j++ : i++;
44
+ b[k] = a[p];
45
+ eb[k] = prev === src ? ea[p] : prev < 0 ? 0 : same;
46
+ same = src === 0 && c === 0 ? 1 : 0;
47
+ prev = src;
35
48
  }
36
- while (i < mid) b[k++] = a[i++];
37
- while (j < hi) b[k++] = a[j++];
38
49
  }
39
50
  const t = a;
40
51
  a = b;
41
52
  b = t;
53
+ const e = ea;
54
+ ea = eb;
55
+ eb = e;
42
56
  }
43
- return a;
57
+ return { order: a, eq: ea };
44
58
  }
45
- function* collationRanks(values, collator) {
59
+ function* collationRanks(values, collator, prev = { values: [], ranks: [], reps: [] }) {
46
60
  const n = values.length;
47
- const ids = /* @__PURE__ */ new Map();
48
- const distinct = [];
49
- const slot = new Int32Array(n);
61
+ const { values: was, ranks: wasRank, reps } = prev;
62
+ const m = reps.length;
63
+ const out = new Float64Array(n);
64
+ const used = new Uint8Array(m);
65
+ const freshIds = /* @__PURE__ */ new Map();
66
+ const fresh = [];
67
+ const gap = [];
68
+ let rowOf;
69
+ let shift = 0;
70
+ let ops = 0;
50
71
  for (let r = 0; r < n; r++) {
51
72
  const v = values[r];
52
73
  if (typeof v === "string") {
53
- let id = ids.get(v);
54
- if (id === void 0) {
55
- id = distinct.length;
56
- ids.set(v, id);
57
- distinct.push(v);
74
+ let k;
75
+ const q = r + shift;
76
+ if (was[q] === v) k = wasRank[q];
77
+ else {
78
+ const id = freshIds.get(v);
79
+ if (id !== void 0) k = m + id;
80
+ else {
81
+ let lo = 0;
82
+ let hi = m;
83
+ while (lo < hi) {
84
+ const mid = lo + hi >>> 1;
85
+ const c = collator.compare(v, reps[mid]);
86
+ ops++;
87
+ if (c === 0) {
88
+ k = mid;
89
+ break;
90
+ }
91
+ if (c < 0) hi = mid;
92
+ else lo = mid + 1;
93
+ }
94
+ if (k === void 0) {
95
+ k = m + fresh.length;
96
+ freshIds.set(v, fresh.length);
97
+ fresh.push(v);
98
+ gap.push(lo - 0.5);
99
+ } else {
100
+ if (!rowOf) {
101
+ rowOf = new Int32Array(m);
102
+ for (let p = 0; p < was.length; p++) {
103
+ const x = wasRank[p];
104
+ if (!Number.isNaN(x)) rowOf[x] = p;
105
+ }
106
+ }
107
+ shift = rowOf[k] - r;
108
+ }
109
+ }
58
110
  }
59
- slot[r] = id;
60
- } else if (typeRank(v) === RANK.missing) slot[r] = -1;
111
+ if (k < m) used[k] = 1;
112
+ out[r] = k;
113
+ } else if (typeRank(v) === RANK.missing) out[r] = Number.NaN;
61
114
  else return null;
62
- if (r % CHUNK === CHUNK - 1) yield;
115
+ if (++ops >= CHUNK) {
116
+ ops = 0;
117
+ yield;
118
+ }
63
119
  }
64
- const order = yield* sortPositions(
65
- distinct.length,
66
- (a, b) => collator.compare(distinct[a], distinct[b])
120
+ if (!fresh.length && !used.includes(0)) return { ranks: out, state: { values, ranks: out, reps } };
121
+ const f = fresh.length;
122
+ const { order, eq } = yield* sortPositions(
123
+ f,
124
+ // By gap; two new strings in the same gap (cold: all of them) go to the collator.
125
+ (a, b) => gap[a] - gap[b] || collator.compare(fresh[a], fresh[b]),
126
+ // Comparing numbers is far cheaper: longer native runs, fewer merge passes.
127
+ m ? 8 * RUN : RUN
67
128
  );
68
- const rankOf = new Float64Array(distinct.length);
69
- let rank = 0;
70
- for (let k = 0; k < order.length; k++) {
71
- const id = order[k];
72
- const s = distinct[id];
73
- if (k > 0 && collator.compare(distinct[order[k - 1]], s) !== 0) rank++;
74
- rankOf[id] = rank;
75
- if (k % CHUNK === CHUNK - 1) yield;
129
+ const remap = new Float64Array(m + f);
130
+ const kept = [];
131
+ let rank = -1;
132
+ let j = 0;
133
+ for (let g = 0; g <= m; g++) {
134
+ while (j < f && gap[order[j]] < g) {
135
+ const id = order[j];
136
+ if (!eq[j]) kept[++rank] = fresh[id];
137
+ remap[m + id] = rank;
138
+ j++;
139
+ }
140
+ if (g < m && used[g]) {
141
+ kept[++rank] = reps[g];
142
+ remap[g] = rank;
143
+ }
76
144
  }
77
- const out = new Float64Array(n);
78
145
  for (let r = 0; r < n; r++) {
79
- const id = slot[r];
80
- out[r] = id < 0 ? Number.NaN : rankOf[id];
146
+ const k = out[r];
147
+ if (!Number.isNaN(k)) out[r] = remap[k];
81
148
  }
82
- return out;
149
+ return { ranks: out, state: { values, ranks: out, reps: kept } };
83
150
  }
84
151
  export {
85
152
  collationRanks
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/sorting/ranks.ts"],"sourcesContent":["import { RANK, typeRank } from './compare'\n\n/** Comparisons / rows between two `yield`s of the jobs below (§9.1). */\nconst CHUNK = 4096\n/** Run length sorted natively before the merge passes. */\nconst RUN = 512\n\n/**\n * Sorts `0..n-1` by `cmp` as a time-sliced job (§9.1): native sorts of short runs, then bottom-up\n * merge passes that `yield` every {@link CHUNK} comparisons. Stable.\n */\nfunction* sortPositions(\n n: number,\n cmp: (a: number, b: number) => number,\n): Generator<undefined, Uint32Array, undefined> {\n let a = new Uint32Array(n)\n for (let i = 0; i < n; i++) a[i] = i\n for (let lo = 0; lo < n; lo += RUN) {\n a.subarray(lo, Math.min(lo + RUN, n)).sort(cmp)\n yield\n }\n let b = new Uint32Array(n)\n let ops = 0\n for (let width = RUN; width < n; width *= 2) {\n for (let lo = 0; lo < n; lo += 2 * width) {\n const mid = Math.min(lo + width, n)\n const hi = Math.min(lo + 2 * width, n)\n let i = lo\n let j = mid\n let k = lo\n while (i < mid && j < hi) {\n const x = a[i] as number\n const y = a[j] as number\n if (cmp(y, x) < 0) {\n b[k++] = y\n j++\n } else {\n b[k++] = x\n i++\n }\n if (++ops === CHUNK) {\n ops = 0\n yield\n }\n }\n while (i < mid) b[k++] = a[i++] as number\n while (j < hi) b[k++] = a[j++] as number\n }\n const t = a\n a = b\n b = t\n }\n return a\n}\n\n/**\n * Collation ranks of a string column (§6.4, §9.1): per row, the dense rank of its string among\n * the column's distinct strings in `collator` order (strings the collator finds equal share a\n * rank), `NaN` for a missing value. Sorting by these numbers gives exactly the order of\n * `collator.compare`, with integer keys (counting / radix sort) instead of a collator call per\n * comparison. A time-sliced job; `null` when a present value is not a string.\n */\nexport function* collationRanks(\n values: readonly unknown[],\n collator: Intl.Collator,\n): Generator<undefined, Float64Array | null, undefined> {\n const n = values.length\n const ids = new Map<string, number>()\n const distinct: string[] = []\n /** Per row: its string's position in `distinct`, -1 when missing. */\n const slot = new Int32Array(n)\n for (let r = 0; r < n; r++) {\n const v = values[r]\n if (typeof v === 'string') {\n let id = ids.get(v)\n if (id === undefined) {\n id = distinct.length\n ids.set(v, id)\n distinct.push(v)\n }\n slot[r] = id\n } else if (typeRank(v) === RANK.missing) slot[r] = -1\n else return null\n if (r % CHUNK === CHUNK - 1) yield\n }\n const order = yield* sortPositions(distinct.length, (a, b) =>\n collator.compare(distinct[a] as string, distinct[b] as string),\n )\n const rankOf = new Float64Array(distinct.length)\n let rank = 0\n for (let k = 0; k < order.length; k++) {\n const id = order[k] as number\n const s = distinct[id] as string\n if (k > 0 && collator.compare(distinct[order[k - 1] as number] as string, s) !== 0) rank++\n rankOf[id] = rank\n if (k % CHUNK === CHUNK - 1) yield\n }\n const out = new Float64Array(n)\n for (let r = 0; r < n; r++) {\n const id = slot[r] as number\n out[r] = id < 0 ? Number.NaN : (rankOf[id] as number)\n }\n return out\n}\n"],"mappings":";AAAA,SAAS,MAAM,gBAAgB;AAG/B,IAAM,QAAQ;AAEd,IAAM,MAAM;AAMZ,UAAU,cACR,GACA,KAC8C;AAC9C,MAAI,IAAI,IAAI,YAAY,CAAC;AACzB,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,GAAE,CAAC,IAAI;AACnC,WAAS,KAAK,GAAG,KAAK,GAAG,MAAM,KAAK;AAClC,MAAE,SAAS,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG;AAC9C;AAAA,EACF;AACA,MAAI,IAAI,IAAI,YAAY,CAAC;AACzB,MAAI,MAAM;AACV,WAAS,QAAQ,KAAK,QAAQ,GAAG,SAAS,GAAG;AAC3C,aAAS,KAAK,GAAG,KAAK,GAAG,MAAM,IAAI,OAAO;AACxC,YAAM,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC;AAClC,YAAM,KAAK,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC;AACrC,UAAI,IAAI;AACR,UAAI,IAAI;AACR,UAAI,IAAI;AACR,aAAO,IAAI,OAAO,IAAI,IAAI;AACxB,cAAM,IAAI,EAAE,CAAC;AACb,cAAM,IAAI,EAAE,CAAC;AACb,YAAI,IAAI,GAAG,CAAC,IAAI,GAAG;AACjB,YAAE,GAAG,IAAI;AACT;AAAA,QACF,OAAO;AACL,YAAE,GAAG,IAAI;AACT;AAAA,QACF;AACA,YAAI,EAAE,QAAQ,OAAO;AACnB,gBAAM;AACN;AAAA,QACF;AAAA,MACF;AACA,aAAO,IAAI,IAAK,GAAE,GAAG,IAAI,EAAE,GAAG;AAC9B,aAAO,IAAI,GAAI,GAAE,GAAG,IAAI,EAAE,GAAG;AAAA,IAC/B;AACA,UAAM,IAAI;AACV,QAAI;AACJ,QAAI;AAAA,EACN;AACA,SAAO;AACT;AASO,UAAU,eACf,QACA,UACsD;AACtD,QAAM,IAAI,OAAO;AACjB,QAAM,MAAM,oBAAI,IAAoB;AACpC,QAAM,WAAqB,CAAC;AAE5B,QAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,OAAO,MAAM,UAAU;AACzB,UAAI,KAAK,IAAI,IAAI,CAAC;AAClB,UAAI,OAAO,QAAW;AACpB,aAAK,SAAS;AACd,YAAI,IAAI,GAAG,EAAE;AACb,iBAAS,KAAK,CAAC;AAAA,MACjB;AACA,WAAK,CAAC,IAAI;AAAA,IACZ,WAAW,SAAS,CAAC,MAAM,KAAK,QAAS,MAAK,CAAC,IAAI;AAAA,QAC9C,QAAO;AACZ,QAAI,IAAI,UAAU,QAAQ,EAAG;AAAA,EAC/B;AACA,QAAM,QAAQ,OAAO;AAAA,IAAc,SAAS;AAAA,IAAQ,CAAC,GAAG,MACtD,SAAS,QAAQ,SAAS,CAAC,GAAa,SAAS,CAAC,CAAW;AAAA,EAC/D;AACA,QAAM,SAAS,IAAI,aAAa,SAAS,MAAM;AAC/C,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,KAAK,MAAM,CAAC;AAClB,UAAM,IAAI,SAAS,EAAE;AACrB,QAAI,IAAI,KAAK,SAAS,QAAQ,SAAS,MAAM,IAAI,CAAC,CAAW,GAAa,CAAC,MAAM,EAAG;AACpF,WAAO,EAAE,IAAI;AACb,QAAI,IAAI,UAAU,QAAQ,EAAG;AAAA,EAC/B;AACA,QAAM,MAAM,IAAI,aAAa,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,CAAC,IAAI,KAAK,IAAI,OAAO,MAAO,OAAO,EAAE;AAAA,EAC3C;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/features/sorting/ranks.ts"],"sourcesContent":["import { RANK, typeRank } from './compare'\n\n/** Comparisons / rows between two `yield`s of the jobs below (§9.1). */\nconst CHUNK = 4096\n/**\n * Run length sorted natively before the merge passes (#97, was 512): each doubling saves a merge\n * pass, and one native run of collator calls stays within a slice even under a 4x CPU throttle.\n */\nconst RUN = 1024\n\n/**\n * Sorts `0..n-1` by `cmp` as a time-sliced job (§9.1): native sorts of `run`s, then bottom-up\n * merge passes that `yield` every {@link CHUNK} comparisons. Stable. Also returns `eq`:\n * `eq[k] = 1` when `order[k]` compares equal to `order[k - 1]`, from the comparisons the merges\n * make anyway (#97). Inside a native run it costs a second pass only when that sort saw a 0.\n */\nfunction* sortPositions(\n n: number,\n cmp: (a: number, b: number) => number,\n run: number,\n): Generator<undefined, { order: Uint32Array; eq: Uint8Array }, undefined> {\n let a = new Uint32Array(n)\n let ea = new Uint8Array(n)\n for (let i = 0; i < n; i++) a[i] = i\n for (let lo = 0; lo < n; lo += run) {\n const hi = Math.min(lo + run, n)\n let zero = false\n a.subarray(lo, hi).sort((x, y) => {\n const c = cmp(x, y)\n if (c === 0) zero = true\n return c\n })\n // Neighbours can only be equal if the sort saw a 0 (it must, to order them).\n if (zero)\n for (let k = lo + 1; k < hi; k++) ea[k] = cmp(a[k - 1] as number, a[k] as number) ? 0 : 1\n yield\n }\n let b = new Uint32Array(n)\n let eb = new Uint8Array(n)\n let ops = 0\n for (let width = run; width < n; width *= 2) {\n for (let lo = 0; lo < n; lo += 2 * width) {\n const mid = Math.min(lo + width, n)\n const hi = Math.min(lo + 2 * width, n)\n let i = lo\n let j = mid\n // The run of the last output (-1: none yet) and whether it equals the other run's head.\n let prev = -1\n let same = 0\n for (let k = lo; k < hi; k++) {\n let c = 1\n let src: number\n if (i < mid && j < hi) {\n c = cmp(a[j] as number, a[i] as number)\n src = c < 0 ? 1 : 0\n if (++ops === CHUNK) {\n ops = 0\n yield\n }\n } else src = i < mid ? 0 : 1\n const p = src ? j++ : i++\n b[k] = a[p] as number\n eb[k] = prev === src ? (ea[p] as number) : prev < 0 ? 0 : same\n same = src === 0 && c === 0 ? 1 : 0\n prev = src\n }\n }\n const t = a\n a = b\n b = t\n const e = ea\n ea = eb\n eb = e\n }\n return { order: a, eq: ea }\n}\n\n/**\n * What the next {@link collationRanks} of the same column starts from (#95): the values it ranked,\n * their ranks (the `ranks` it returned, shared) and the distinct strings in rank order (one per\n * rank, shared with the data). No string → id map: a row whose string is unchanged at its position\n * (or at a shifted one) keeps its rank, any other string is found by binary search over `reps`.\n * Never mutated (a job builds a new one), so a cancelled job leaves it usable.\n */\nexport interface RankState {\n readonly values: readonly unknown[]\n readonly ranks: ArrayLike<number>\n readonly reps: readonly string[]\n}\n\nexport interface Ranked {\n readonly ranks: Float64Array\n readonly state: RankState\n}\n\n/**\n * Collation ranks of a string column (§6.4, §9.1): per row, the dense rank of its string among\n * the column's distinct strings in `collator` order (strings the collator finds equal share a\n * rank), `NaN` for a missing value. Sorting by these numbers gives exactly the order of\n * `collator.compare`, with integer keys (counting / radix sort) instead of a collator call per\n * comparison. A time-sliced job; `null` when a present value is not a string.\n *\n * Incremental across data versions (#95): a string ranked in `prev` (the state of an earlier array\n * of the same column) keeps that rank as its sort key. It is read from the same row of `prev` when\n * the string is the same there (or at the row offset of the last match, for inserted / removed\n * rows), else found by binary search over the ranked strings; a new string is placed in the gap\n * below the first greater one. Only new strings are sorted (two in the same gap call the\n * collator): a one-row edit costs O(log d) collator calls (and a sort of numbers), not a cold\n * sort. The new state holds only the strings present now.\n */\nexport function* collationRanks(\n values: readonly unknown[],\n collator: Intl.Collator,\n prev: RankState = { values: [], ranks: [], reps: [] },\n): Generator<undefined, Ranked | null, undefined> {\n const n = values.length\n const { values: was, ranks: wasRank, reps } = prev\n const m = reps.length\n /** Per row: its rank in `prev`, `m + id` for the new string `fresh[id]`, NaN when missing. */\n const out = new Float64Array(n)\n /** Ranks of `prev` still present (the rest are dropped from the new state). */\n const used = new Uint8Array(m)\n // New strings only (a cold job: every distinct string), dropped with the job.\n const freshIds = new Map<string, number>()\n const fresh: string[] = []\n /** Per new string: `g - 0.5`, between ranks g-1 and g of `prev`. */\n const gap: number[] = []\n /** Per rank of `prev`: one row holding it, built on the first lookup that finds one. */\n let rowOf: Int32Array | undefined\n /** Offset to `prev`'s row of the last match: rows inserted / removed above shift it. */\n let shift = 0\n let ops = 0\n for (let r = 0; r < n; r++) {\n const v = values[r]\n if (typeof v === 'string') {\n let k: number | undefined\n const q = r + shift\n // Out of range: `undefined`, never a string.\n if (was[q] === v) k = wasRank[q] as number\n else {\n const id = freshIds.get(v)\n if (id !== undefined) k = m + id\n else {\n let lo = 0\n let hi = m\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n const c = collator.compare(v, reps[mid] as string)\n ops++\n if (c === 0) {\n k = mid\n break\n }\n if (c < 0) hi = mid\n else lo = mid + 1\n }\n if (k === undefined) {\n k = m + fresh.length\n freshIds.set(v, fresh.length)\n fresh.push(v)\n gap.push(lo - 0.5)\n } else {\n if (!rowOf) {\n rowOf = new Int32Array(m)\n for (let p = 0; p < was.length; p++) {\n const x = wasRank[p] as number\n if (!Number.isNaN(x)) rowOf[x] = p\n }\n }\n shift = (rowOf[k] as number) - r\n }\n }\n }\n if (k < m) used[k] = 1\n out[r] = k\n } else if (typeRank(v) === RANK.missing) out[r] = Number.NaN\n else return null\n if (++ops >= CHUNK) {\n ops = 0\n yield\n }\n }\n // The same strings as `prev` (a new array of equal content, rows moved): the same ranks.\n if (!fresh.length && !used.includes(0)) return { ranks: out, state: { values, ranks: out, reps } }\n\n const f = fresh.length\n const { order, eq } = yield* sortPositions(\n f,\n // By gap; two new strings in the same gap (cold: all of them) go to the collator.\n (a, b) =>\n (gap[a] as number) - (gap[b] as number) ||\n collator.compare(fresh[a] as string, fresh[b] as string),\n // Comparing numbers is far cheaper: longer native runs, fewer merge passes.\n m ? 8 * RUN : RUN,\n )\n // Merge the kept ranks of `prev` and the new strings (each before the rank above its gap). No\n // collator call: one pass, like the rank lookup below.\n const remap = new Float64Array(m + f)\n const kept: string[] = []\n let rank = -1\n let j = 0\n for (let g = 0; g <= m; g++) {\n while (j < f && (gap[order[j] as number] as number) < g) {\n const id = order[j] as number\n if (!eq[j]) kept[++rank] = fresh[id] as string\n remap[m + id] = rank\n j++\n }\n if (g < m && used[g]) {\n kept[++rank] = reps[g] as string\n remap[g] = rank\n }\n }\n for (let r = 0; r < n; r++) {\n const k = out[r] as number\n if (!Number.isNaN(k)) out[r] = remap[k] as number\n }\n return { ranks: out, state: { values, ranks: out, reps: kept } }\n}\n"],"mappings":"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","names":[]}
@@ -39,6 +39,7 @@ var import_codes2 = require("../../intercept/codes.cjs");
39
39
  var import_columns = require("../../pipeline/columns.cjs");
40
40
  var import_env_codes = require("../../pipeline/env-codes.cjs");
41
41
  var import_indices = require("../../pipeline/indices.cjs");
42
+ var import_release = require("../../pipeline/release.cjs");
42
43
  var import_slice = require("../../pipeline/slice.cjs");
43
44
  var import_mode = require("../../source/mode.cjs");
44
45
  var import_capability = require("../../versioning/capability.cjs");
@@ -162,19 +163,31 @@ var sorting = (opts = {}) => {
162
163
  }
163
164
  return collator;
164
165
  };
165
- const rankKeys = /* @__PURE__ */ new WeakMap();
166
- function ranksFor(ctx, values) {
166
+ let rankKeys = /* @__PURE__ */ new WeakMap();
167
+ let history = /* @__PURE__ */ new Map();
168
+ function keepHistory(by) {
169
+ for (const id of history.keys()) {
170
+ if (!by.some((e) => String(e.id) === id)) history.delete(id);
171
+ }
172
+ }
173
+ function ranksFor(ctx, id, values) {
167
174
  let key = rankKeys.get(values);
168
175
  if (!key) {
169
176
  key = {};
170
177
  rankKeys.set(values, key);
171
178
  }
172
179
  const c = getCollator(ctx.emit);
173
- return (0, import_slice.sliced)(ctx, key, () => (0, import_ranks.collationRanks)(values, c));
180
+ const into = history;
181
+ return (0, import_slice.sliced)(ctx, key, function* () {
182
+ const r = yield* (0, import_ranks.collationRanks)(values, c, into.get(id));
183
+ if (!r) return null;
184
+ into.set(id, r.state);
185
+ return r.ranks;
186
+ });
174
187
  }
175
188
  let shown;
176
- const sortByCache = /* @__PURE__ */ new WeakMap();
177
- const pathCache = /* @__PURE__ */ new WeakMap();
189
+ let sortByCache = /* @__PURE__ */ new WeakMap();
190
+ let pathCache = /* @__PURE__ */ new WeakMap();
178
191
  let exsorted;
179
192
  const sortByFailures = new import_cell_batch.CellFailures();
180
193
  function sortByValues(ctx, col, sortBy) {
@@ -220,7 +233,7 @@ var sorting = (opts = {}) => {
220
233
  byId.set(id, out);
221
234
  return out;
222
235
  }
223
- return (0, import_define.defineFeature)({
236
+ const feature = (0, import_define.defineFeature)({
224
237
  name: "sorting",
225
238
  stage: "sort",
226
239
  mode: opts.mode ?? "client",
@@ -258,6 +271,7 @@ var sorting = (opts = {}) => {
258
271
  },
259
272
  rowModel: (indices, state, ctx) => {
260
273
  const by = entriesOf(state);
274
+ keepHistory(by);
261
275
  if (by.length === 0 || indices.length < 2) {
262
276
  shown = void 0;
263
277
  return indices;
@@ -270,7 +284,7 @@ var sorting = (opts = {}) => {
270
284
  const sortBy = col && fnField(col.def, "sortBy");
271
285
  const values = sortBy ? sortByValues(ctx, col, sortBy) : ctx.getColumnValues(id) ?? pathValues(ctx, id);
272
286
  const sortFn = col && !sortBy ? fnField(col.def, "sortFn") : void 0;
273
- return { col, desc: e.desc === true, values, sortFn, ranked: false };
287
+ return { id, col, desc: e.desc === true, values, sortFn, ranked: false };
274
288
  });
275
289
  if (process.env.NODE_ENV !== "production") {
276
290
  for (const e of entries) {
@@ -311,7 +325,7 @@ var sorting = (opts = {}) => {
311
325
  let computing = false;
312
326
  for (const e of entries) {
313
327
  if (e.sortFn || e.values instanceof Float64Array) continue;
314
- const ranks = ranksFor(ctx, e.values);
328
+ const ranks = ranksFor(ctx, e.id, e.values);
315
329
  if (ranks === void 0) computing = true;
316
330
  else if (ranks) {
317
331
  e.values = ranks;
@@ -465,6 +479,16 @@ var sorting = (opts = {}) => {
465
479
  }
466
480
  }
467
481
  });
482
+ const internal = feature;
483
+ internal[import_release.RELEASE] = () => {
484
+ rankKeys = /* @__PURE__ */ new WeakMap();
485
+ history = /* @__PURE__ */ new Map();
486
+ shown = void 0;
487
+ sortByCache = /* @__PURE__ */ new WeakMap();
488
+ pathCache = /* @__PURE__ */ new WeakMap();
489
+ };
490
+ if (process.env.NODE_ENV !== "production") internal[import_release.PROBE] = () => ({ rankKeys, history, shown, sortByCache, pathCache });
491
+ return feature;
468
492
  };
469
493
  // Annotate the CommonJS export names for ESM import in node:
470
494
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/sorting/sorting.ts"],"sourcesContent":["import { optionDiagnostics } from '../../catalog/codes'\nimport { defineOptions } from '../../catalog/options'\nimport { describeReturn } from '../../catalog/slots'\nimport type { SortAlgorithm } from '../../column/column-def'\nimport { compilePath, getValue } from '../../column/get-value'\nimport type { Path } from '../../column/path'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\nimport { proseArg } from '../../diagnostics/prose'\nimport { describeValue } from '../../diagnostics/redact'\nimport type { Emit } from '../../diagnostics/types'\nimport { defineFeature } from '../../feature/define'\nimport { type SliceFix, sliceGuard } from '../../feature/slice-guard'\nimport type { FeatureTypes, RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport { type ResolvedColumn, resolveColumns } from '../../pipeline/columns'\nimport { envDiagnostics } from '../../pipeline/env-codes'\nimport { allRows } from '../../pipeline/indices'\nimport { sliced } from '../../pipeline/slice'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { detectCapability, hasCapability } from '../../versioning/capability'\nimport { comparatorDiagnostics, sortingDiagnostics } from './codes'\nimport {\n defaultIndexComparator,\n type IndexComparator,\n RANK,\n sortFnIndexComparator,\n typeRank,\n} from './compare'\nimport { collationRanks } from './ranks'\nimport {\n chooseAuto,\n hasExsorted,\n intKeys,\n needsIntKeys,\n sortIntKeys,\n sortWithComparator,\n} from './strategy'\n\nkeepDiagnostics(\n sortingDiagnostics,\n comparatorDiagnostics,\n boundaryDiagnostics,\n optionDiagnostics,\n envDiagnostics,\n)\n\n/** One sort key: a column id (a path of the row type) and its direction. */\nexport type SortEntry<T> = { id: Path<T>; desc: boolean }\n\n/** The `sorting` slice (§6.4): multi-column, earlier entries first. Empty = unsorted. */\nexport type SortingState<T> = { by: readonly SortEntry<T>[] }\n\nexport type SortDirection = 'asc' | 'desc'\n\n/** An entry with an `id` (a `null` entry or one without an id is malformed, #89). */\nconst isEntry = (e: unknown): e is SortEntry<unknown> => (e as { id?: unknown } | null)?.id != null\n\nconst byOf = (s: unknown): unknown => (s as { by?: unknown } | null | undefined)?.by\n\n/**\n * The entries of a slice; `[]` for a malformed one (a controlled slice not fed back yet, #89).\n * The slice itself is replaced by the guard (`DK_INVALID_STATE`).\n */\nconst entriesOf = (s: unknown): readonly SortEntry<unknown>[] => {\n const by = byOf(s)\n return Array.isArray(by) ? by.filter(isEntry) : []\n}\n\n/** A malformed restored slice: the valid entries are kept (#89). */\nfunction fixSlice(s: unknown): SliceFix<SortingState<unknown>> | undefined {\n const raw = byOf(s)\n const by = entriesOf(s)\n if (Array.isArray(raw) && raw.length === by.length) return undefined\n const bad = Array.isArray(raw) ? raw.findIndex((e) => !isEntry(e)) : -1\n return [\n { by },\n bad < 0 ? describeValue(raw ?? s) : `by[${bad}]: ${describeValue((raw as unknown[])[bad])}`,\n `{ by: [${by.length} valid] }`,\n ]\n}\n\n/** Replaces the sort; an equal one keeps the slice (same reference, no notification, #21). */\nconst setSort = (\n ctx: { getState(): unknown; setState(s: SortingState<unknown>): void },\n by: readonly SortEntry<unknown>[],\n) => {\n // JSON equality (key order included, #21); a restored entry nested deep enough to overflow\n // JSON.stringify counts as a change instead of throwing (#103).\n try {\n if (JSON.stringify(byOf(ctx.getState())) === JSON.stringify(by)) return\n } catch {}\n // A value that is not an array reaches the guard (dev: replaced, DK_INVALID_STATE).\n ctx.setState({ by: Array.isArray(by) ? by.slice() : by })\n}\n\nexport type SortingApi<T> = {\n /**\n * Cycles `id` asc → desc → none. Without `multi` the result is a single-column sort; with `multi`\n * (Shift+click) `id` is appended, cycled in place or removed and the other entries are kept.\n */\n toggleSort(id: Path<T>, options?: { multi?: boolean }): void\n /** Replaces the whole sort. */\n setSorting(by: readonly SortEntry<T>[]): void\n /** Removes every sort entry (input order). */\n clearSorting(): void\n /** `id`'s direction in the current sort, or `false` (for `aria-sort`). */\n getSortDirection(id: Path<T>): SortDirection | false\n}\n\nexport interface SortingTypes extends FeatureTypes {\n state: SortingState<this['T']>\n api: SortingApi<this['T']>\n}\n\n/**\n * How rows are sorted (§6.4): `'auto'` (default) picks per sort from one O(n) scan — exsorted\n * tim / counting / radix for a single column of safe-integer keys (numbers, `Date`s), native\n * `Array.prototype.sort` otherwise. A built-in name forces that algorithm (radix / counting only\n * apply to safe-integer keys); a function sorts the row indices itself.\n */\nexport type SortAlgorithmOption = SortAlgorithm\n\nexport interface SortingOptions {\n /**\n * Locale(s) of the string collator (`Intl.Collator(locale, { numeric: true })`). Default `'en'`,\n * not the runtime's: the server and the browser must sort alike (SSR hydration, #12).\n */\n locale?: string | readonly string[]\n /** The sort algorithm (default `'auto'`); a column's `sortAlgorithm` takes precedence. */\n algorithm?: SortAlgorithmOption\n /** Reports the algorithm chosen for each sort, why, n and the time taken (`DK_SORT_STRATEGY`). */\n debug?: boolean\n /**\n * `'server'`: the server sorts (its state goes to `options.fetcher`, or the app fetches); core\n * skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst validLocale = (v: unknown) => {\n try {\n Intl.getCanonicalLocales(v as string | string[])\n return true\n } catch {\n return false\n }\n}\n\nconst ALGORITHMS = ['auto', 'tim', 'radix', 'counting', 'native'] as const\n\nconst sortingOptions = {\n locale: {\n type: ['string', 'array'],\n check: validLocale,\n expected: 'a BCP 47 locale tag or a list of them, e.g. \"th\" or [\"th\", \"en\"]',\n },\n algorithm: {\n type: ['string', 'function'],\n oneOf: ALGORITHMS,\n // A literal (not built from ALGORITHMS): a module-level call would keep the schema in production.\n expected:\n \"one of 'auto', 'tim', 'radix', 'counting', 'native', or (indices, compare) => indices\",\n },\n debug: { type: 'boolean' },\n mode: modeOption,\n} as const\n\ntype CustomAlgorithm = Extract<SortAlgorithm, (...args: never[]) => unknown>\n\nconst now = () => (typeof performance === 'undefined' ? Date.now() : performance.now())\n\n/**\n * From this many rows, `auto` sorts a string column by precomputed collation ranks (§9.1): one\n * time-sliced job per column values, then integer-key sorts (a few ms at 100k rows) instead of a\n * collator call per comparison (~100–200 ms at 100k, one long task).\n */\nconst RANKS_MIN = 10_000\n\nconst missing = (v: unknown) => typeRank(v) === RANK.missing\n\n/**\n * Dev check of a column's `sortFn` (§8.3 `DK_SORT_COMPARATOR_INVALID`), sampled: up to 8 present\n * values spread over the input, every pair both ways. A throw gives no verdict (the sort reports\n * it as `DK_CALLBACK_FAILED`).\n */\nfunction checkSortFn(\n fn: SortFn,\n values: ColumnValues,\n indices: Uint32Array,\n): { reason: 'nan' | 'not-number' | 'inconsistent'; received?: string } | undefined {\n const sample: unknown[] = []\n const step = Math.max(1, Math.floor(indices.length / 8))\n for (let i = 0; i < indices.length && sample.length < 8; i += step) {\n const v = values[indices[i] as number]\n if (!missing(v)) sample.push(v)\n }\n try {\n for (let i = 0; i < sample.length; i++) {\n for (let j = i; j < sample.length; j++) {\n const x = fn(sample[i], sample[j])\n const y = fn(sample[j], sample[i])\n for (const r of [x, y]) {\n if (typeof r !== 'number') return { reason: 'not-number', received: describeReturn(r) }\n if (Number.isNaN(r)) return { reason: 'nan' }\n }\n if (Math.sign(x) !== -Math.sign(y)) return { reason: 'inconsistent' }\n }\n }\n } catch {\n return undefined\n }\n return undefined\n}\n\ntype SortFn = (a: unknown, b: unknown) => number\ntype SortBy = (row: unknown) => unknown\n\nconst fnField = (def: object, field: 'sortFn' | 'sortBy') => {\n const f = (def as Record<string, unknown>)[field]\n return typeof f === 'function' ? f : undefined\n}\n\n/** Aborts a native sort when a column's `sortFn` throws (the entry then falls back). */\nclass SortFnFailed {\n constructor(\n readonly entry: number,\n readonly error: unknown,\n ) {}\n}\n\n/**\n * Sorting (§6.4): `toggleSort` (asc → desc → none, `multi`), stable sort of the row indices with\n * the default comparator — missing values (`null`, `undefined`, `NaN`, invalid `Date`) last in both\n * directions, mixed types by type rank, strings with one `Intl.Collator(locale, { numeric: true })`\n * — or a column's `sortFn` / `sortBy`. Never touches the data: a copy of the index array is sorted.\n *\n * `algorithm: 'auto'` (default) picks per sort from one O(n) scan (§6.4): exsorted tim / counting /\n * radix for a single column of safe-integer keys (numbers, `Date`s), native `Array.prototype.sort`\n * (stable) for strings, floats, multi-column and `sortFn`. Every choice gives the same stable order.\n * `sorting({ algorithm })` or a column's `sortAlgorithm` overrides it; `debug: true` reports each\n * choice (`DK_SORT_STRATEGY`).\n */\nexport const sorting = (opts: SortingOptions = {}) => {\n // Dev only (§9.3): production reads a malformed slice as no sort and stays cheap.\n const guard = (process.env.NODE_ENV !== \"production\") ? sliceGuard('sorting', fixSlice) : undefined\n let collator: Intl.Collator | undefined\n /** The collator; `DK_ENV_FALLBACK` when the runtime supports none of the requested locales. */\n const getCollator = (emit: Emit) => {\n if (collator) return collator\n const locale = (opts.locale ?? 'en') as string | string[]\n let supported = true\n try {\n collator = new Intl.Collator(locale, { numeric: true })\n supported = Intl.Collator.supportedLocalesOf(locale).length > 0\n } catch {\n collator = new Intl.Collator(undefined, { numeric: true })\n supported = false\n }\n if (!supported) {\n emit('DK_ENV_FALLBACK', {\n api: 'Intl.Collator',\n feature: 'sorting',\n requested: ([] as string[]).concat(locale).join(', '),\n fallback: `collation|${collator.resolvedOptions().locale}`,\n })\n }\n return collator\n }\n\n /** Values array → the key of its collation-ranks job (`ctx.slice`); released with the values. */\n const rankKeys = new WeakMap<object, object>()\n function ranksFor(ctx: RowModelContext, values: readonly unknown[]) {\n let key = rankKeys.get(values)\n if (!key) {\n key = {}\n rankKeys.set(values, key)\n }\n const c = getCollator(ctx.emit)\n return sliced(ctx, key, () => collationRanks(values, c))\n }\n /** The last sorted output and its input: the stand-in while ranks are computed (§9.1). */\n let shown: { input: Uint32Array; output: Uint32Array } | undefined\n\n // Values that the column cache does not hold, per data reference (released with it).\n const sortByCache = new WeakMap<readonly unknown[], WeakMap<object, unknown[]>>()\n const pathCache = new WeakMap<readonly unknown[], Map<string, unknown[]>>()\n /** exsorted feature detection (§8.5), once per feature instance, on the first sort needing it. */\n let exsorted: boolean | undefined\n\n /** `sortBy` failures (§3.4): numbers only; text is built only when a cell failed. */\n const sortByFailures = new CellFailures()\n\n /**\n * `sortBy` is a cell slot: one `column:batch` per column per data reference, an inline\n * `try/catch` per row. A failed row (or a batch an interceptor skipped) keys as `undefined`,\n * which sorts last.\n */\n function sortByValues(ctx: RowModelContext, col: ResolvedColumn, sortBy: SortBy): unknown[] {\n const data = ctx.data\n let byDef = sortByCache.get(data)\n if (!byDef) {\n byDef = new WeakMap()\n sortByCache.set(data, byDef)\n }\n let out = byDef.get(col.def)\n if (out) return out\n const values: unknown[] = new Array(data.length).fill(undefined)\n const rows = allRows(data)\n sortByFailures.reset()\n ctx.batch(col.id, 'sortBy', rows, () => {\n for (let i = 0; i < rows.length; i++) {\n const r = rows[i] as number\n try {\n values[r] = sortBy(data[r])\n } catch (e) {\n sortByFailures.add(r, e)\n }\n }\n sortByFailures.report(ctx.emit, col.index, col.id, 'sortBy')\n })\n sortByFailures.reset()\n out = values\n byDef.set(col.def, out)\n return out\n }\n\n function pathValues(ctx: RowModelContext, id: string): unknown[] {\n const data = ctx.data\n let byId = pathCache.get(data)\n if (!byId) {\n byId = new Map()\n pathCache.set(data, byId)\n }\n let out = byId.get(id)\n if (out) return out\n const path = compilePath(id)\n out = new Array(data.length)\n for (let r = 0; r < data.length; r++) out[r] = getValue(data[r], path)\n byId.set(id, out)\n return out\n }\n\n return defineFeature<'sorting', SortingTypes>({\n name: 'sorting',\n stage: 'sort',\n mode: opts.mode ?? 'client',\n options: defineOptions(\n // The option validator runs in development only (§9.3): production drops the schema.\n (process.env.NODE_ENV !== \"production\") ? sortingOptions : ({} as typeof sortingOptions),\n opts,\n ),\n ...(guard ? { interceptors: [guard.interceptor] } : undefined),\n initialState: () => ({ by: [] }),\n // After destroy(): still a declared value (`aria-sort` reads it during teardown renders).\n destroyedResult: { getSortDirection: () => false },\n createApi: (ctx) => {\n guard?.install(ctx)\n // Values, not updaters: a library step is never reported as a failing user `updater` (#89).\n return {\n toggleSort: (id, options) => {\n const by = entriesOf(ctx.getState())\n const i = by.findIndex((e) => e.id === id)\n const cur = by[i]\n const next = !cur ? { id, desc: false } : cur.desc ? undefined : { id, desc: true }\n if (!options?.multi) return ctx.setState({ by: next ? [next] : [] })\n if (!cur) return ctx.setState({ by: [...by, next as SortEntry<unknown>] })\n const out = by.slice()\n if (next) out[i] = next as SortEntry<unknown>\n else out.splice(i, 1)\n ctx.setState({ by: out })\n },\n setSorting: (by) => setSort(ctx, by),\n clearSorting: () => setSort(ctx, []),\n getSortDirection: (id) => {\n const e = entriesOf(ctx.getState()).find((x) => x.id === id)\n return e ? (e.desc ? 'desc' : 'asc') : false\n },\n }\n },\n rowModel: (indices, state, ctx) => {\n const by = entriesOf(state)\n if (by.length === 0 || indices.length < 2) {\n shown = undefined\n return indices\n }\n const t0 = opts.debug ? now() : 0\n const cols = resolveColumns(ctx.columns)\n const entries = by.map((e) => {\n const id = String(e.id)\n const col = cols.byId.get(id)\n const sortBy = col && (fnField(col.def, 'sortBy') as SortBy | undefined)\n const values: ColumnValues = sortBy\n ? sortByValues(ctx, col, sortBy)\n : (ctx.getColumnValues(id) ?? pathValues(ctx, id))\n const sortFn =\n col && !sortBy ? (fnField(col.def, 'sortFn') as SortFn | undefined) : undefined\n return { col, desc: e.desc === true, values, sortFn, ranked: false }\n })\n\n // Dev (§8.3): a sampled sortFn check; an invalid one falls back to the default comparator.\n if ((process.env.NODE_ENV !== \"production\")) {\n for (const e of entries) {\n if (!e.sortFn) continue\n const bad = checkSortFn(e.sortFn, e.values, indices)\n if (!bad) continue\n e.sortFn = undefined\n const col = e.col as ResolvedColumn\n ctx.emit('DK_SORT_COMPARATOR_INVALID', { column: col.id, index: col.index, ...bad })\n }\n }\n\n // The requested algorithm: the primary column's `sortAlgorithm`, else the table's.\n const primary = entries[0] as (typeof entries)[number]\n const colAlgorithm =\n primary.col && (primary.col.def as { sortAlgorithm?: unknown }).sortAlgorithm\n const fromColumn = colAlgorithm !== undefined\n let requested = (fromColumn ? colAlgorithm : (opts.algorithm ?? 'auto')) as SortAlgorithm\n const noEffect = (reason: string) =>\n ctx.emit(\n 'DK_OPTION_NO_EFFECT',\n fromColumn\n ? { path: `columns[${primary.col?.index}].sortAlgorithm`, reason, once: false }\n : { path: 'algorithm', feature: 'sorting', reason, once: false },\n )\n if (\n typeof requested !== 'function' &&\n !(ALGORITHMS as readonly unknown[]).includes(requested)\n ) {\n noEffect(`not-algorithm|${proseArg(JSON.stringify(requested))}`)\n requested = 'auto'\n }\n if (requested !== 'native' && exsorted === undefined) {\n // DK_DEP_INCOMPATIBLE is a development check (§8.5); the fallback is not.\n exsorted = (process.env.NODE_ENV !== \"production\")\n ? detectCapability(\n hasExsorted,\n {\n feature: 'sorting',\n dependency: 'exsorted',\n capability: 'timSort / radixSort / countingSort',\n supported: '^1.2.3',\n fallback: 'Array.prototype.sort (native, stable)',\n },\n ctx.emit,\n )\n : hasCapability(hasExsorted)\n }\n\n // auto at large n (§9.1): string columns sort by collation ranks, computed by a sliced job.\n if (requested === 'auto' && indices.length >= RANKS_MIN) {\n let computing = false\n for (const e of entries) {\n if (e.sortFn || e.values instanceof Float64Array) continue\n const ranks = ranksFor(ctx, e.values)\n if (ranks === undefined) computing = true\n else if (ranks) {\n e.values = ranks\n e.ranked = true\n }\n }\n // Meanwhile: the previous order of these rows (or the input order); 'pending'.\n if (computing) return shown && shown.input === indices ? shown.output : indices\n }\n const ranked = entries.some((e) => e.ranked)\n\n const build = (): IndexComparator => {\n const cmps = entries.map((e, k) => {\n const fn = e.sortFn\n if (!fn) return defaultIndexComparator(e.values, e.desc, getCollator(ctx.emit))\n const guarded: SortFn = (a, b) => {\n try {\n return fn(a, b)\n } catch (error) {\n throw new SortFnFailed(k, error)\n }\n }\n return sortFnIndexComparator(e.values, e.desc, guarded)\n })\n if (cmps.length === 1) return cmps[0] as IndexComparator\n return (a, b) => {\n for (let k = 0; k < cmps.length; k++) {\n const c = (cmps[k] as IndexComparator)(a, b)\n if (c !== 0) return c\n }\n return 0\n }\n }\n\n let algorithm: string = 'native'\n let reason = ''\n /** One attempt; a throwing sortFn propagates as SortFnFailed (the loop below retries). */\n const sortOnce = (): Uint32Array => {\n const want = requested\n if (typeof want === 'function') {\n const custom = want as CustomAlgorithm\n let res: unknown\n try {\n res = custom(indices.slice(), build())\n } catch (error) {\n if (error instanceof SortFnFailed) throw error\n reportCustom(true, error)\n return native('custom-threw')\n }\n if (res instanceof Uint32Array && res.length === indices.length) {\n algorithm = 'custom'\n reason = fromColumn ? 'column sortAlgorithm' : 'sorting({ algorithm })'\n return res\n }\n reportCustom(false, res)\n return native('custom-bad')\n }\n if (exsorted === false) return native('exsorted unavailable')\n const by = fromColumn ? 'column sortAlgorithm' : 'sorting({ algorithm })'\n // Integer keys: a single column with the default comparator (§6.4).\n const single = entries.length === 1 && !primary.sortFn\n const keys =\n single && want !== 'native' ? intKeys(primary.values, indices, primary.desc) : undefined\n if (want !== 'auto' && want !== 'native') {\n if (keys) {\n try {\n const out = sortIntKeys(keys, want)\n algorithm = want\n reason = `int-keys|${by}`\n return out\n } catch {\n noEffect(`exsorted-${want === 'counting' ? 'range' : 'failed'}|${want}`)\n }\n } else if (needsIntKeys(want)) {\n noEffect(`needs-int-keys|${want}`)\n } else {\n algorithm = want\n reason = by\n return sortWithComparator(indices, build(), want as 'tim')\n }\n } else if (want === 'native') return native(by)\n if (!keys) {\n return native(\n single ? 'not-safe-int' : entries.length > 1 ? 'multi-column' : 'custom sortFn',\n )\n }\n const choice = chooseAuto(keys)\n algorithm = choice.algorithm\n reason = `auto-${ranked ? 'ranked-' : ''}${choice.reason}`\n return sortIntKeys(keys, choice.algorithm)\n }\n const native = (why: string) => {\n algorithm = 'native'\n reason = ranked ? `ranks|${why}` : why\n return sortWithComparator(indices, build(), 'native')\n }\n /** A custom algorithm threw (`threw`, `value` = the error) or returned a bad `value`. */\n const reportCustom = (threw: boolean, value: unknown) => {\n const slot = fromColumn\n ? `columns[${primary.col?.index}].sortAlgorithm`\n : 'sorting.algorithm'\n const fallback = 'sorted-native'\n if (!threw) {\n // The slot contract check is dev only (§3.4 slotContract); the fallback applies always.\n if ((process.env.NODE_ENV !== \"production\")) {\n ctx.emit('DK_CALLBACK_BAD_RETURN', {\n slot,\n expected: `a Uint32Array of ${indices.length} row indices`,\n received: describeReturn(value),\n count: 1,\n fallback,\n })\n }\n return\n }\n const name = errorName(value)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot,\n count: 1,\n fallback,\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: value },\n )\n }\n\n // At most one retry per entry: each failing sortFn falls back to the default comparator.\n for (;;) {\n try {\n const out = sortOnce()\n if (opts.debug) {\n ctx.emit('DK_SORT_STRATEGY', {\n algorithm,\n reason,\n n: indices.length,\n ms: now() - t0,\n by: by.map((e) => `${String(e.id)}:${e.desc ? 'desc' : 'asc'}`),\n ...(primary.col ? { column: { index: primary.col.index, id: primary.col.id } } : {}),\n })\n }\n shown = { input: indices, output: out }\n return out\n } catch (e) {\n if (!(e instanceof SortFnFailed)) throw e\n const entry = entries[e.entry] as (typeof entries)[number]\n entry.sortFn = undefined\n const col = entry.col as ResolvedColumn\n const name = errorName(e.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `columns[${col.index}].sortFn`,\n column: col.id,\n count: 1,\n fallback: 'sorted-default',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: e.error },\n )\n }\n }\n },\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkC;AAClC,qBAA8B;AAC9B,mBAA+B;AAE/B,uBAAsC;AAEtC,qBAAgC;AAChC,2BAAO;AACP,mBAAyB;AACzB,oBAA8B;AAE9B,oBAA8B;AAC9B,yBAA0C;AAE1C,wBAA6B;AAC7B,sBAA0B;AAC1B,IAAAA,gBAAoC;AACpC,qBAAoD;AACpD,uBAA+B;AAC/B,qBAAwB;AACxB,mBAAuB;AAEvB,kBAA0C;AAC1C,wBAAgD;AAChD,IAAAA,gBAA0D;AAC1D,qBAMO;AACP,mBAA+B;AAC/B,sBAOO;AAAA,IAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWA,IAAM,UAAU,CAAC,MAAyC,GAA+B,MAAM;AAE/F,IAAM,OAAO,CAAC,MAAyB,GAA2C;AAMlF,IAAM,YAAY,CAAC,MAA8C;AAC/D,QAAM,KAAK,KAAK,CAAC;AACjB,SAAO,MAAM,QAAQ,EAAE,IAAI,GAAG,OAAO,OAAO,IAAI,CAAC;AACnD;AAGA,SAAS,SAAS,GAAyD;AACzE,QAAM,MAAM,KAAK,CAAC;AAClB,QAAM,KAAK,UAAU,CAAC;AACtB,MAAI,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,GAAG,OAAQ,QAAO;AAC3D,QAAM,MAAM,MAAM,QAAQ,GAAG,IAAI,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI;AACrE,SAAO;AAAA,IACL,EAAE,GAAG;AAAA,IACL,MAAM,QAAI,6BAAc,OAAO,CAAC,IAAI,MAAM,GAAG,UAAM,6BAAe,IAAkB,GAAG,CAAC,CAAC;AAAA,IACzF,UAAU,GAAG,MAAM;AAAA,EACrB;AACF;AAGA,IAAM,UAAU,CACd,KACA,OACG;AAGH,MAAI;AACF,QAAI,KAAK,UAAU,KAAK,IAAI,SAAS,CAAC,CAAC,MAAM,KAAK,UAAU,EAAE,EAAG;AAAA,EACnE,QAAQ;AAAA,EAAC;AAET,MAAI,SAAS,EAAE,IAAI,MAAM,QAAQ,EAAE,IAAI,GAAG,MAAM,IAAI,GAAG,CAAC;AAC1D;AA8CA,IAAM,cAAc,CAAC,MAAe;AAClC,MAAI;AACF,SAAK,oBAAoB,CAAsB;AAC/C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,aAAa,CAAC,QAAQ,OAAO,SAAS,YAAY,QAAQ;AAEhE,IAAM,iBAAiB;AAAA,EACrB,QAAQ;AAAA,IACN,MAAM,CAAC,UAAU,OAAO;AAAA,IACxB,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,IACT,MAAM,CAAC,UAAU,UAAU;AAAA,IAC3B,OAAO;AAAA;AAAA,IAEP,UACE;AAAA,EACJ;AAAA,EACA,OAAO,EAAE,MAAM,UAAU;AAAA,EACzB,MAAM;AACR;AAIA,IAAM,MAAM,MAAO,OAAO,gBAAgB,cAAc,KAAK,IAAI,IAAI,YAAY,IAAI;AAOrF,IAAM,YAAY;AAElB,IAAM,UAAU,CAAC,UAAe,yBAAS,CAAC,MAAM,oBAAK;AAOrD,SAAS,YACP,IACA,QACA,SACkF;AAClF,QAAM,SAAoB,CAAC;AAC3B,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAS,CAAC,CAAC;AACvD,WAAS,IAAI,GAAG,IAAI,QAAQ,UAAU,OAAO,SAAS,GAAG,KAAK,MAAM;AAClE,UAAM,IAAI,OAAO,QAAQ,CAAC,CAAW;AACrC,QAAI,CAAC,QAAQ,CAAC,EAAG,QAAO,KAAK,CAAC;AAAA,EAChC;AACA,MAAI;AACF,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,cAAM,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACjC,cAAM,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACjC,mBAAW,KAAK,CAAC,GAAG,CAAC,GAAG;AACtB,cAAI,OAAO,MAAM,SAAU,QAAO,EAAE,QAAQ,cAAc,cAAU,6BAAe,CAAC,EAAE;AACtF,cAAI,OAAO,MAAM,CAAC,EAAG,QAAO,EAAE,QAAQ,MAAM;AAAA,QAC9C;AACA,YAAI,KAAK,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,EAAG,QAAO,EAAE,QAAQ,eAAe;AAAA,MACtE;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,IAAM,UAAU,CAAC,KAAa,UAA+B;AAC3D,QAAM,IAAK,IAAgC,KAAK;AAChD,SAAO,OAAO,MAAM,aAAa,IAAI;AACvC;AAGA,IAAM,eAAN,MAAmB;AAAA,EACjB,YACW,OACA,OACT;AAFS;AACA;AAAA,EACR;AACL;AAcO,IAAM,UAAU,CAAC,OAAuB,CAAC,MAAM;AAEpD,QAAM,QAAS,QAAQ,IAAI,aAAa,mBAAgB,+BAAW,WAAW,QAAQ,IAAI;AAC1F,MAAI;AAEJ,QAAM,cAAc,CAAC,SAAe;AAClC,QAAI,SAAU,QAAO;AACrB,UAAM,SAAU,KAAK,UAAU;AAC/B,QAAI,YAAY;AAChB,QAAI;AACF,iBAAW,IAAI,KAAK,SAAS,QAAQ,EAAE,SAAS,KAAK,CAAC;AACtD,kBAAY,KAAK,SAAS,mBAAmB,MAAM,EAAE,SAAS;AAAA,IAChE,QAAQ;AACN,iBAAW,IAAI,KAAK,SAAS,QAAW,EAAE,SAAS,KAAK,CAAC;AACzD,kBAAY;AAAA,IACd;AACA,QAAI,CAAC,WAAW;AACd,WAAK,mBAAmB;AAAA,QACtB,KAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAY,CAAC,EAAe,OAAO,MAAM,EAAE,KAAK,IAAI;AAAA,QACpD,UAAU,aAAa,SAAS,gBAAgB,EAAE,MAAM;AAAA,MAC1D,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,oBAAI,QAAwB;AAC7C,WAAS,SAAS,KAAsB,QAA4B;AAClE,QAAI,MAAM,SAAS,IAAI,MAAM;AAC7B,QAAI,CAAC,KAAK;AACR,YAAM,CAAC;AACP,eAAS,IAAI,QAAQ,GAAG;AAAA,IAC1B;AACA,UAAM,IAAI,YAAY,IAAI,IAAI;AAC9B,eAAO,qBAAO,KAAK,KAAK,UAAM,6BAAe,QAAQ,CAAC,CAAC;AAAA,EACzD;AAEA,MAAI;AAGJ,QAAM,cAAc,oBAAI,QAAwD;AAChF,QAAM,YAAY,oBAAI,QAAoD;AAE1E,MAAI;AAGJ,QAAM,iBAAiB,IAAI,+BAAa;AAOxC,WAAS,aAAa,KAAsB,KAAqB,QAA2B;AAC1F,UAAM,OAAO,IAAI;AACjB,QAAI,QAAQ,YAAY,IAAI,IAAI;AAChC,QAAI,CAAC,OAAO;AACV,cAAQ,oBAAI,QAAQ;AACpB,kBAAY,IAAI,MAAM,KAAK;AAAA,IAC7B;AACA,QAAI,MAAM,MAAM,IAAI,IAAI,GAAG;AAC3B,QAAI,IAAK,QAAO;AAChB,UAAM,SAAoB,IAAI,MAAM,KAAK,MAAM,EAAE,KAAK,MAAS;AAC/D,UAAM,WAAO,wBAAQ,IAAI;AACzB,mBAAe,MAAM;AACrB,QAAI,MAAM,IAAI,IAAI,UAAU,MAAM,MAAM;AACtC,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,cAAM,IAAI,KAAK,CAAC;AAChB,YAAI;AACF,iBAAO,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC;AAAA,QAC5B,SAAS,GAAG;AACV,yBAAe,IAAI,GAAG,CAAC;AAAA,QACzB;AAAA,MACF;AACA,qBAAe,OAAO,IAAI,MAAM,IAAI,OAAO,IAAI,IAAI,QAAQ;AAAA,IAC7D,CAAC;AACD,mBAAe,MAAM;AACrB,UAAM;AACN,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,KAAsB,IAAuB;AAC/D,UAAM,OAAO,IAAI;AACjB,QAAI,OAAO,UAAU,IAAI,IAAI;AAC7B,QAAI,CAAC,MAAM;AACT,aAAO,oBAAI,IAAI;AACf,gBAAU,IAAI,MAAM,IAAI;AAAA,IAC1B;AACA,QAAI,MAAM,KAAK,IAAI,EAAE;AACrB,QAAI,IAAK,QAAO;AAChB,UAAM,WAAO,8BAAY,EAAE;AAC3B,UAAM,IAAI,MAAM,KAAK,MAAM;AAC3B,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,CAAC,QAAI,2BAAS,KAAK,CAAC,GAAG,IAAI;AACrE,SAAK,IAAI,IAAI,GAAG;AAChB,WAAO;AAAA,EACT;AAEA,aAAO,6BAAuC;AAAA,IAC5C,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM,KAAK,QAAQ;AAAA,IACnB,aAAS;AAAA;AAAA,MAEN,QAAQ,IAAI,aAAa,eAAgB,iBAAkB,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,IACA,GAAI,QAAQ,EAAE,cAAc,CAAC,MAAM,WAAW,EAAE,IAAI;AAAA,IACpD,cAAc,OAAO,EAAE,IAAI,CAAC,EAAE;AAAA;AAAA,IAE9B,iBAAiB,EAAE,kBAAkB,MAAM,MAAM;AAAA,IACjD,WAAW,CAAC,QAAQ;AAClB,aAAO,QAAQ,GAAG;AAElB,aAAO;AAAA,QACL,YAAY,CAAC,IAAI,YAAY;AAC3B,gBAAM,KAAK,UAAU,IAAI,SAAS,CAAC;AACnC,gBAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACzC,gBAAM,MAAM,GAAG,CAAC;AAChB,gBAAM,OAAO,CAAC,MAAM,EAAE,IAAI,MAAM,MAAM,IAAI,IAAI,OAAO,SAAY,EAAE,IAAI,MAAM,KAAK;AAClF,cAAI,CAAC,SAAS,MAAO,QAAO,IAAI,SAAS,EAAE,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;AACnE,cAAI,CAAC,IAAK,QAAO,IAAI,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,IAA0B,EAAE,CAAC;AACzE,gBAAM,MAAM,GAAG,MAAM;AACrB,cAAI,KAAM,KAAI,CAAC,IAAI;AAAA,cACd,KAAI,OAAO,GAAG,CAAC;AACpB,cAAI,SAAS,EAAE,IAAI,IAAI,CAAC;AAAA,QAC1B;AAAA,QACA,YAAY,CAAC,OAAO,QAAQ,KAAK,EAAE;AAAA,QACnC,cAAc,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,QACnC,kBAAkB,CAAC,OAAO;AACxB,gBAAM,IAAI,UAAU,IAAI,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC3D,iBAAO,IAAK,EAAE,OAAO,SAAS,QAAS;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS,OAAO,QAAQ;AACjC,YAAM,KAAK,UAAU,KAAK;AAC1B,UAAI,GAAG,WAAW,KAAK,QAAQ,SAAS,GAAG;AACzC,gBAAQ;AACR,eAAO;AAAA,MACT;AACA,YAAM,KAAK,KAAK,QAAQ,IAAI,IAAI;AAChC,YAAM,WAAO,+BAAe,IAAI,OAAO;AACvC,YAAM,UAAU,GAAG,IAAI,CAAC,MAAM;AAC5B,cAAM,KAAK,OAAO,EAAE,EAAE;AACtB,cAAM,MAAM,KAAK,KAAK,IAAI,EAAE;AAC5B,cAAM,SAAS,OAAQ,QAAQ,IAAI,KAAK,QAAQ;AAChD,cAAM,SAAuB,SACzB,aAAa,KAAK,KAAK,MAAM,IAC5B,IAAI,gBAAgB,EAAE,KAAK,WAAW,KAAK,EAAE;AAClD,cAAM,SACJ,OAAO,CAAC,SAAU,QAAQ,IAAI,KAAK,QAAQ,IAA2B;AACxE,eAAO,EAAE,KAAK,MAAM,EAAE,SAAS,MAAM,QAAQ,QAAQ,QAAQ,MAAM;AAAA,MACrE,CAAC;AAGD,UAAK,QAAQ,IAAI,aAAa,cAAe;AAC3C,mBAAW,KAAK,SAAS;AACvB,cAAI,CAAC,EAAE,OAAQ;AACf,gBAAM,MAAM,YAAY,EAAE,QAAQ,EAAE,QAAQ,OAAO;AACnD,cAAI,CAAC,IAAK;AACV,YAAE,SAAS;AACX,gBAAM,MAAM,EAAE;AACd,cAAI,KAAK,8BAA8B,EAAE,QAAQ,IAAI,IAAI,OAAO,IAAI,OAAO,GAAG,IAAI,CAAC;AAAA,QACrF;AAAA,MACF;AAGA,YAAM,UAAU,QAAQ,CAAC;AACzB,YAAM,eACJ,QAAQ,OAAQ,QAAQ,IAAI,IAAoC;AAClE,YAAM,aAAa,iBAAiB;AACpC,UAAI,YAAa,aAAa,eAAgB,KAAK,aAAa;AAChE,YAAM,WAAW,CAACC,YAChB,IAAI;AAAA,QACF;AAAA,QACA,aACI,EAAE,MAAM,WAAW,QAAQ,KAAK,KAAK,mBAAmB,QAAAA,SAAQ,MAAM,MAAM,IAC5E,EAAE,MAAM,aAAa,SAAS,WAAW,QAAAA,SAAQ,MAAM,MAAM;AAAA,MACnE;AACF,UACE,OAAO,cAAc,cACrB,CAAE,WAAkC,SAAS,SAAS,GACtD;AACA,iBAAS,qBAAiB,uBAAS,KAAK,UAAU,SAAS,CAAC,CAAC,EAAE;AAC/D,oBAAY;AAAA,MACd;AACA,UAAI,cAAc,YAAY,aAAa,QAAW;AAEpD,mBAAY,QAAQ,IAAI,aAAa,mBACjC;AAAA,UACE;AAAA,UACA;AAAA,YACE,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,WAAW;AAAA,YACX,UAAU;AAAA,UACZ;AAAA,UACA,IAAI;AAAA,QACN,QACA,iCAAc,2BAAW;AAAA,MAC/B;AAGA,UAAI,cAAc,UAAU,QAAQ,UAAU,WAAW;AACvD,YAAI,YAAY;AAChB,mBAAW,KAAK,SAAS;AACvB,cAAI,EAAE,UAAU,EAAE,kBAAkB,aAAc;AAClD,gBAAM,QAAQ,SAAS,KAAK,EAAE,MAAM;AACpC,cAAI,UAAU,OAAW,aAAY;AAAA,mBAC5B,OAAO;AACd,cAAE,SAAS;AACX,cAAE,SAAS;AAAA,UACb;AAAA,QACF;AAEA,YAAI,UAAW,QAAO,SAAS,MAAM,UAAU,UAAU,MAAM,SAAS;AAAA,MAC1E;AACA,YAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,MAAM;AAE3C,YAAM,QAAQ,MAAuB;AACnC,cAAM,OAAO,QAAQ,IAAI,CAAC,GAAG,MAAM;AACjC,gBAAM,KAAK,EAAE;AACb,cAAI,CAAC,GAAI,YAAO,uCAAuB,EAAE,QAAQ,EAAE,MAAM,YAAY,IAAI,IAAI,CAAC;AAC9E,gBAAM,UAAkB,CAAC,GAAG,MAAM;AAChC,gBAAI;AACF,qBAAO,GAAG,GAAG,CAAC;AAAA,YAChB,SAAS,OAAO;AACd,oBAAM,IAAI,aAAa,GAAG,KAAK;AAAA,YACjC;AAAA,UACF;AACA,qBAAO,sCAAsB,EAAE,QAAQ,EAAE,MAAM,OAAO;AAAA,QACxD,CAAC;AACD,YAAI,KAAK,WAAW,EAAG,QAAO,KAAK,CAAC;AACpC,eAAO,CAAC,GAAG,MAAM;AACf,mBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,kBAAM,IAAK,KAAK,CAAC,EAAsB,GAAG,CAAC;AAC3C,gBAAI,MAAM,EAAG,QAAO;AAAA,UACtB;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI,YAAoB;AACxB,UAAI,SAAS;AAEb,YAAM,WAAW,MAAmB;AAClC,cAAM,OAAO;AACb,YAAI,OAAO,SAAS,YAAY;AAC9B,gBAAM,SAAS;AACf,cAAI;AACJ,cAAI;AACF,kBAAM,OAAO,QAAQ,MAAM,GAAG,MAAM,CAAC;AAAA,UACvC,SAAS,OAAO;AACd,gBAAI,iBAAiB,aAAc,OAAM;AACzC,yBAAa,MAAM,KAAK;AACxB,mBAAO,OAAO,cAAc;AAAA,UAC9B;AACA,cAAI,eAAe,eAAe,IAAI,WAAW,QAAQ,QAAQ;AAC/D,wBAAY;AACZ,qBAAS,aAAa,yBAAyB;AAC/C,mBAAO;AAAA,UACT;AACA,uBAAa,OAAO,GAAG;AACvB,iBAAO,OAAO,YAAY;AAAA,QAC5B;AACA,YAAI,aAAa,MAAO,QAAO,OAAO,sBAAsB;AAC5D,cAAMC,MAAK,aAAa,yBAAyB;AAEjD,cAAM,SAAS,QAAQ,WAAW,KAAK,CAAC,QAAQ;AAChD,cAAM,OACJ,UAAU,SAAS,eAAW,yBAAQ,QAAQ,QAAQ,SAAS,QAAQ,IAAI,IAAI;AACjF,YAAI,SAAS,UAAU,SAAS,UAAU;AACxC,cAAI,MAAM;AACR,gBAAI;AACF,oBAAM,UAAM,6BAAY,MAAM,IAAI;AAClC,0BAAY;AACZ,uBAAS,YAAYA,GAAE;AACvB,qBAAO;AAAA,YACT,QAAQ;AACN,uBAAS,YAAY,SAAS,aAAa,UAAU,QAAQ,IAAI,IAAI,EAAE;AAAA,YACzE;AAAA,UACF,eAAW,8BAAa,IAAI,GAAG;AAC7B,qBAAS,kBAAkB,IAAI,EAAE;AAAA,UACnC,OAAO;AACL,wBAAY;AACZ,qBAASA;AACT,uBAAO,oCAAmB,SAAS,MAAM,GAAG,IAAa;AAAA,UAC3D;AAAA,QACF,WAAW,SAAS,SAAU,QAAO,OAAOA,GAAE;AAC9C,YAAI,CAAC,MAAM;AACT,iBAAO;AAAA,YACL,SAAS,iBAAiB,QAAQ,SAAS,IAAI,iBAAiB;AAAA,UAClE;AAAA,QACF;AACA,cAAM,aAAS,4BAAW,IAAI;AAC9B,oBAAY,OAAO;AACnB,iBAAS,QAAQ,SAAS,YAAY,EAAE,GAAG,OAAO,MAAM;AACxD,mBAAO,6BAAY,MAAM,OAAO,SAAS;AAAA,MAC3C;AACA,YAAM,SAAS,CAAC,QAAgB;AAC9B,oBAAY;AACZ,iBAAS,SAAS,SAAS,GAAG,KAAK;AACnC,mBAAO,oCAAmB,SAAS,MAAM,GAAG,QAAQ;AAAA,MACtD;AAEA,YAAM,eAAe,CAAC,OAAgB,UAAmB;AACvD,cAAM,OAAO,aACT,WAAW,QAAQ,KAAK,KAAK,oBAC7B;AACJ,cAAM,WAAW;AACjB,YAAI,CAAC,OAAO;AAEV,cAAK,QAAQ,IAAI,aAAa,cAAe;AAC3C,gBAAI,KAAK,0BAA0B;AAAA,cACjC;AAAA,cACA,UAAU,oBAAoB,QAAQ,MAAM;AAAA,cAC5C,cAAU,6BAAe,KAAK;AAAA,cAC9B,OAAO;AAAA,cACP;AAAA,YACF,CAAC;AAAA,UACH;AACA;AAAA,QACF;AACA,cAAM,WAAO,2BAAU,KAAK;AAC5B,YAAI;AAAA,UACF;AAAA,UACA;AAAA,YACE;AAAA,YACA,OAAO;AAAA,YACP;AAAA,YACA,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,WAAW,KAAK;AAAA,UAClD;AAAA,UACA,EAAE,OAAO,MAAM;AAAA,QACjB;AAAA,MACF;AAGA,iBAAS;AACP,YAAI;AACF,gBAAM,MAAM,SAAS;AACrB,cAAI,KAAK,OAAO;AACd,gBAAI,KAAK,oBAAoB;AAAA,cAC3B;AAAA,cACA;AAAA,cACA,GAAG,QAAQ;AAAA,cACX,IAAI,IAAI,IAAI;AAAA,cACZ,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,SAAS,KAAK,EAAE;AAAA,cAC9D,GAAI,QAAQ,MAAM,EAAE,QAAQ,EAAE,OAAO,QAAQ,IAAI,OAAO,IAAI,QAAQ,IAAI,GAAG,EAAE,IAAI,CAAC;AAAA,YACpF,CAAC;AAAA,UACH;AACA,kBAAQ,EAAE,OAAO,SAAS,QAAQ,IAAI;AACtC,iBAAO;AAAA,QACT,SAAS,GAAG;AACV,cAAI,EAAE,aAAa,cAAe,OAAM;AACxC,gBAAM,QAAQ,QAAQ,EAAE,KAAK;AAC7B,gBAAM,SAAS;AACf,gBAAM,MAAM,MAAM;AAClB,gBAAM,WAAO,2BAAU,EAAE,KAAK;AAC9B,cAAI;AAAA,YACF;AAAA,YACA;AAAA,cACE,MAAM,WAAW,IAAI,KAAK;AAAA,cAC1B,QAAQ,IAAI;AAAA,cACZ,OAAO;AAAA,cACP,UAAU;AAAA,cACV,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,WAAW,KAAK;AAAA,YAClD;AAAA,YACA,EAAE,OAAO,EAAE,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":["import_codes","reason","by"]}
1
+ {"version":3,"sources":["../../../src/features/sorting/sorting.ts"],"sourcesContent":["import { optionDiagnostics } from '../../catalog/codes'\nimport { defineOptions } from '../../catalog/options'\nimport { describeReturn } from '../../catalog/slots'\nimport type { SortAlgorithm } from '../../column/column-def'\nimport { compilePath, getValue } from '../../column/get-value'\nimport type { Path } from '../../column/path'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\nimport { proseArg } from '../../diagnostics/prose'\nimport { describeValue } from '../../diagnostics/redact'\nimport type { Emit } from '../../diagnostics/types'\nimport { defineFeature } from '../../feature/define'\nimport { type SliceFix, sliceGuard } from '../../feature/slice-guard'\nimport type { FeatureTypes, RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport { type ResolvedColumn, resolveColumns } from '../../pipeline/columns'\nimport { envDiagnostics } from '../../pipeline/env-codes'\nimport { allRows } from '../../pipeline/indices'\nimport { PROBE, RELEASE, type Releasable } from '../../pipeline/release'\nimport { sliced } from '../../pipeline/slice'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { detectCapability, hasCapability } from '../../versioning/capability'\nimport { comparatorDiagnostics, sortingDiagnostics } from './codes'\nimport {\n defaultIndexComparator,\n type IndexComparator,\n RANK,\n sortFnIndexComparator,\n typeRank,\n} from './compare'\nimport { collationRanks, type RankState } from './ranks'\nimport {\n chooseAuto,\n hasExsorted,\n intKeys,\n needsIntKeys,\n sortIntKeys,\n sortWithComparator,\n} from './strategy'\n\nkeepDiagnostics(\n sortingDiagnostics,\n comparatorDiagnostics,\n boundaryDiagnostics,\n optionDiagnostics,\n envDiagnostics,\n)\n\n/** One sort key: a column id (a path of the row type) and its direction. */\nexport type SortEntry<T> = { id: Path<T>; desc: boolean }\n\n/** The `sorting` slice (§6.4): multi-column, earlier entries first. Empty = unsorted. */\nexport type SortingState<T> = { by: readonly SortEntry<T>[] }\n\nexport type SortDirection = 'asc' | 'desc'\n\n/** An entry with an `id` (a `null` entry or one without an id is malformed, #89). */\nconst isEntry = (e: unknown): e is SortEntry<unknown> => (e as { id?: unknown } | null)?.id != null\n\nconst byOf = (s: unknown): unknown => (s as { by?: unknown } | null | undefined)?.by\n\n/**\n * The entries of a slice; `[]` for a malformed one (a controlled slice not fed back yet, #89).\n * The slice itself is replaced by the guard (`DK_INVALID_STATE`).\n */\nconst entriesOf = (s: unknown): readonly SortEntry<unknown>[] => {\n const by = byOf(s)\n return Array.isArray(by) ? by.filter(isEntry) : []\n}\n\n/** A malformed restored slice: the valid entries are kept (#89). */\nfunction fixSlice(s: unknown): SliceFix<SortingState<unknown>> | undefined {\n const raw = byOf(s)\n const by = entriesOf(s)\n if (Array.isArray(raw) && raw.length === by.length) return undefined\n const bad = Array.isArray(raw) ? raw.findIndex((e) => !isEntry(e)) : -1\n return [\n { by },\n bad < 0 ? describeValue(raw ?? s) : `by[${bad}]: ${describeValue((raw as unknown[])[bad])}`,\n `{ by: [${by.length} valid] }`,\n ]\n}\n\n/** Replaces the sort; an equal one keeps the slice (same reference, no notification, #21). */\nconst setSort = (\n ctx: { getState(): unknown; setState(s: SortingState<unknown>): void },\n by: readonly SortEntry<unknown>[],\n) => {\n // JSON equality (key order included, #21); a restored entry nested deep enough to overflow\n // JSON.stringify counts as a change instead of throwing (#103).\n try {\n if (JSON.stringify(byOf(ctx.getState())) === JSON.stringify(by)) return\n } catch {}\n // A value that is not an array reaches the guard (dev: replaced, DK_INVALID_STATE).\n ctx.setState({ by: Array.isArray(by) ? by.slice() : by })\n}\n\nexport type SortingApi<T> = {\n /**\n * Cycles `id` asc → desc → none. Without `multi` the result is a single-column sort; with `multi`\n * (Shift+click) `id` is appended, cycled in place or removed and the other entries are kept.\n */\n toggleSort(id: Path<T>, options?: { multi?: boolean }): void\n /** Replaces the whole sort. */\n setSorting(by: readonly SortEntry<T>[]): void\n /** Removes every sort entry (input order). */\n clearSorting(): void\n /** `id`'s direction in the current sort, or `false` (for `aria-sort`). */\n getSortDirection(id: Path<T>): SortDirection | false\n}\n\nexport interface SortingTypes extends FeatureTypes {\n state: SortingState<this['T']>\n api: SortingApi<this['T']>\n}\n\n/**\n * How rows are sorted (§6.4): `'auto'` (default) picks per sort from one O(n) scan — exsorted\n * tim / counting / radix for a single column of safe-integer keys (numbers, `Date`s), native\n * `Array.prototype.sort` otherwise. A built-in name forces that algorithm (radix / counting only\n * apply to safe-integer keys); a function sorts the row indices itself.\n */\nexport type SortAlgorithmOption = SortAlgorithm\n\nexport interface SortingOptions {\n /**\n * Locale(s) of the string collator (`Intl.Collator(locale, { numeric: true })`). Default `'en'`,\n * not the runtime's: the server and the browser must sort alike (SSR hydration, #12).\n */\n locale?: string | readonly string[]\n /** The sort algorithm (default `'auto'`); a column's `sortAlgorithm` takes precedence. */\n algorithm?: SortAlgorithmOption\n /** Reports the algorithm chosen for each sort, why, n and the time taken (`DK_SORT_STRATEGY`). */\n debug?: boolean\n /**\n * `'server'`: the server sorts (its state goes to `options.fetcher`, or the app fetches); core\n * skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst validLocale = (v: unknown) => {\n try {\n Intl.getCanonicalLocales(v as string | string[])\n return true\n } catch {\n return false\n }\n}\n\nconst ALGORITHMS = ['auto', 'tim', 'radix', 'counting', 'native'] as const\n\nconst sortingOptions = {\n locale: {\n type: ['string', 'array'],\n check: validLocale,\n expected: 'a BCP 47 locale tag or a list of them, e.g. \"th\" or [\"th\", \"en\"]',\n },\n algorithm: {\n type: ['string', 'function'],\n oneOf: ALGORITHMS,\n // A literal (not built from ALGORITHMS): a module-level call would keep the schema in production.\n expected:\n \"one of 'auto', 'tim', 'radix', 'counting', 'native', or (indices, compare) => indices\",\n },\n debug: { type: 'boolean' },\n mode: modeOption,\n} as const\n\ntype CustomAlgorithm = Extract<SortAlgorithm, (...args: never[]) => unknown>\n\nconst now = () => (typeof performance === 'undefined' ? Date.now() : performance.now())\n\n/**\n * From this many rows, `auto` sorts a string column by precomputed collation ranks (§9.1): one\n * time-sliced job per column values, then integer-key sorts (a few ms at 100k rows) instead of a\n * collator call per comparison (~100–200 ms at 100k, one long task).\n */\nconst RANKS_MIN = 10_000\n\nconst missing = (v: unknown) => typeRank(v) === RANK.missing\n\n/**\n * Dev check of a column's `sortFn` (§8.3 `DK_SORT_COMPARATOR_INVALID`), sampled: up to 8 present\n * values spread over the input, every pair both ways. A throw gives no verdict (the sort reports\n * it as `DK_CALLBACK_FAILED`).\n */\nfunction checkSortFn(\n fn: SortFn,\n values: ColumnValues,\n indices: Uint32Array,\n): { reason: 'nan' | 'not-number' | 'inconsistent'; received?: string } | undefined {\n const sample: unknown[] = []\n const step = Math.max(1, Math.floor(indices.length / 8))\n for (let i = 0; i < indices.length && sample.length < 8; i += step) {\n const v = values[indices[i] as number]\n if (!missing(v)) sample.push(v)\n }\n try {\n for (let i = 0; i < sample.length; i++) {\n for (let j = i; j < sample.length; j++) {\n const x = fn(sample[i], sample[j])\n const y = fn(sample[j], sample[i])\n for (const r of [x, y]) {\n if (typeof r !== 'number') return { reason: 'not-number', received: describeReturn(r) }\n if (Number.isNaN(r)) return { reason: 'nan' }\n }\n if (Math.sign(x) !== -Math.sign(y)) return { reason: 'inconsistent' }\n }\n }\n } catch {\n return undefined\n }\n return undefined\n}\n\ntype SortFn = (a: unknown, b: unknown) => number\ntype SortBy = (row: unknown) => unknown\n\nconst fnField = (def: object, field: 'sortFn' | 'sortBy') => {\n const f = (def as Record<string, unknown>)[field]\n return typeof f === 'function' ? f : undefined\n}\n\n/** Aborts a native sort when a column's `sortFn` throws (the entry then falls back). */\nclass SortFnFailed {\n constructor(\n readonly entry: number,\n readonly error: unknown,\n ) {}\n}\n\n/**\n * Sorting (§6.4): `toggleSort` (asc → desc → none, `multi`), stable sort of the row indices with\n * the default comparator — missing values (`null`, `undefined`, `NaN`, invalid `Date`) last in both\n * directions, mixed types by type rank, strings with one `Intl.Collator(locale, { numeric: true })`\n * — or a column's `sortFn` / `sortBy`. Never touches the data: a copy of the index array is sorted.\n *\n * `algorithm: 'auto'` (default) picks per sort from one O(n) scan (§6.4): exsorted tim / counting /\n * radix for a single column of safe-integer keys (numbers, `Date`s), native `Array.prototype.sort`\n * (stable) for strings, floats, multi-column and `sortFn`. Every choice gives the same stable order.\n * `sorting({ algorithm })` or a column's `sortAlgorithm` overrides it; `debug: true` reports each\n * choice (`DK_SORT_STRATEGY`).\n */\nexport const sorting = (opts: SortingOptions = {}) => {\n // Dev only (§9.3): production reads a malformed slice as no sort and stays cheap.\n const guard = (process.env.NODE_ENV !== \"production\") ? sliceGuard('sorting', fixSlice) : undefined\n let collator: Intl.Collator | undefined\n /** The collator; `DK_ENV_FALLBACK` when the runtime supports none of the requested locales. */\n const getCollator = (emit: Emit) => {\n if (collator) return collator\n const locale = (opts.locale ?? 'en') as string | string[]\n let supported = true\n try {\n collator = new Intl.Collator(locale, { numeric: true })\n supported = Intl.Collator.supportedLocalesOf(locale).length > 0\n } catch {\n collator = new Intl.Collator(undefined, { numeric: true })\n supported = false\n }\n if (!supported) {\n emit('DK_ENV_FALLBACK', {\n api: 'Intl.Collator',\n feature: 'sorting',\n requested: ([] as string[]).concat(locale).join(', '),\n fallback: `collation|${collator.resolvedOptions().locale}`,\n })\n }\n return collator\n }\n\n /** Values array → the key of its collation-ranks job (`ctx.slice`); released with the values. */\n let rankKeys = new WeakMap<object, object>()\n /**\n * Per column id: the state of its last collation ranks, across data versions (#95). A new data\n * array re-ranks only what changed; it holds the column's current distinct strings, so only the\n * columns of the current sort keep one (`keepHistory`).\n */\n let history = new Map<string, RankState>()\n /** Drops the rank history of the columns that are not in `by` (the §9.3 memory budget). */\n function keepHistory(by: readonly { id: unknown }[]) {\n for (const id of history.keys()) {\n if (!by.some((e) => String(e.id) === id)) history.delete(id)\n }\n }\n function ranksFor(ctx: RowModelContext, id: string, values: readonly unknown[]) {\n let key = rankKeys.get(values)\n if (!key) {\n key = {}\n rankKeys.set(values, key)\n }\n const c = getCollator(ctx.emit)\n const into = history\n return sliced(ctx, key, function* () {\n const r = yield* collationRanks(values, c, into.get(id))\n if (!r) return null\n into.set(id, r.state)\n return r.ranks\n })\n }\n /** The last sorted output and its input: the stand-in while ranks are computed (§9.1). */\n let shown: { input: Uint32Array; output: Uint32Array } | undefined\n\n // Values that the column cache does not hold, per data reference (released with it).\n let sortByCache = new WeakMap<readonly unknown[], WeakMap<object, unknown[]>>()\n let pathCache = new WeakMap<readonly unknown[], Map<string, unknown[]>>()\n /** exsorted feature detection (§8.5), once per feature instance, on the first sort needing it. */\n let exsorted: boolean | undefined\n\n /** `sortBy` failures (§3.4): numbers only; text is built only when a cell failed. */\n const sortByFailures = new CellFailures()\n\n /**\n * `sortBy` is a cell slot: one `column:batch` per column per data reference, an inline\n * `try/catch` per row. A failed row (or a batch an interceptor skipped) keys as `undefined`,\n * which sorts last.\n */\n function sortByValues(ctx: RowModelContext, col: ResolvedColumn, sortBy: SortBy): unknown[] {\n const data = ctx.data\n let byDef = sortByCache.get(data)\n if (!byDef) {\n byDef = new WeakMap()\n sortByCache.set(data, byDef)\n }\n let out = byDef.get(col.def)\n if (out) return out\n const values: unknown[] = new Array(data.length).fill(undefined)\n const rows = allRows(data)\n sortByFailures.reset()\n ctx.batch(col.id, 'sortBy', rows, () => {\n for (let i = 0; i < rows.length; i++) {\n const r = rows[i] as number\n try {\n values[r] = sortBy(data[r])\n } catch (e) {\n sortByFailures.add(r, e)\n }\n }\n sortByFailures.report(ctx.emit, col.index, col.id, 'sortBy')\n })\n sortByFailures.reset()\n out = values\n byDef.set(col.def, out)\n return out\n }\n\n function pathValues(ctx: RowModelContext, id: string): unknown[] {\n const data = ctx.data\n let byId = pathCache.get(data)\n if (!byId) {\n byId = new Map()\n pathCache.set(data, byId)\n }\n let out = byId.get(id)\n if (out) return out\n const path = compilePath(id)\n out = new Array(data.length)\n for (let r = 0; r < data.length; r++) out[r] = getValue(data[r], path)\n byId.set(id, out)\n return out\n }\n\n const feature = defineFeature<'sorting', SortingTypes>({\n name: 'sorting',\n stage: 'sort',\n mode: opts.mode ?? 'client',\n options: defineOptions(\n // The option validator runs in development only (§9.3): production drops the schema.\n (process.env.NODE_ENV !== \"production\") ? sortingOptions : ({} as typeof sortingOptions),\n opts,\n ),\n ...(guard ? { interceptors: [guard.interceptor] } : undefined),\n initialState: () => ({ by: [] }),\n // After destroy(): still a declared value (`aria-sort` reads it during teardown renders).\n destroyedResult: { getSortDirection: () => false },\n createApi: (ctx) => {\n guard?.install(ctx)\n // Values, not updaters: a library step is never reported as a failing user `updater` (#89).\n return {\n toggleSort: (id, options) => {\n const by = entriesOf(ctx.getState())\n const i = by.findIndex((e) => e.id === id)\n const cur = by[i]\n const next = !cur ? { id, desc: false } : cur.desc ? undefined : { id, desc: true }\n if (!options?.multi) return ctx.setState({ by: next ? [next] : [] })\n if (!cur) return ctx.setState({ by: [...by, next as SortEntry<unknown>] })\n const out = by.slice()\n if (next) out[i] = next as SortEntry<unknown>\n else out.splice(i, 1)\n ctx.setState({ by: out })\n },\n setSorting: (by) => setSort(ctx, by),\n clearSorting: () => setSort(ctx, []),\n getSortDirection: (id) => {\n const e = entriesOf(ctx.getState()).find((x) => x.id === id)\n return e ? (e.desc ? 'desc' : 'asc') : false\n },\n }\n },\n rowModel: (indices, state, ctx) => {\n const by = entriesOf(state)\n keepHistory(by)\n if (by.length === 0 || indices.length < 2) {\n shown = undefined\n return indices\n }\n const t0 = opts.debug ? now() : 0\n const cols = resolveColumns(ctx.columns)\n const entries = by.map((e) => {\n const id = String(e.id)\n const col = cols.byId.get(id)\n const sortBy = col && (fnField(col.def, 'sortBy') as SortBy | undefined)\n const values: ColumnValues = sortBy\n ? sortByValues(ctx, col, sortBy)\n : (ctx.getColumnValues(id) ?? pathValues(ctx, id))\n const sortFn =\n col && !sortBy ? (fnField(col.def, 'sortFn') as SortFn | undefined) : undefined\n return { id, col, desc: e.desc === true, values, sortFn, ranked: false }\n })\n\n // Dev (§8.3): a sampled sortFn check; an invalid one falls back to the default comparator.\n if ((process.env.NODE_ENV !== \"production\")) {\n for (const e of entries) {\n if (!e.sortFn) continue\n const bad = checkSortFn(e.sortFn, e.values, indices)\n if (!bad) continue\n e.sortFn = undefined\n const col = e.col as ResolvedColumn\n ctx.emit('DK_SORT_COMPARATOR_INVALID', { column: col.id, index: col.index, ...bad })\n }\n }\n\n // The requested algorithm: the primary column's `sortAlgorithm`, else the table's.\n const primary = entries[0] as (typeof entries)[number]\n const colAlgorithm =\n primary.col && (primary.col.def as { sortAlgorithm?: unknown }).sortAlgorithm\n const fromColumn = colAlgorithm !== undefined\n let requested = (fromColumn ? colAlgorithm : (opts.algorithm ?? 'auto')) as SortAlgorithm\n const noEffect = (reason: string) =>\n ctx.emit(\n 'DK_OPTION_NO_EFFECT',\n fromColumn\n ? { path: `columns[${primary.col?.index}].sortAlgorithm`, reason, once: false }\n : { path: 'algorithm', feature: 'sorting', reason, once: false },\n )\n if (\n typeof requested !== 'function' &&\n !(ALGORITHMS as readonly unknown[]).includes(requested)\n ) {\n noEffect(`not-algorithm|${proseArg(JSON.stringify(requested))}`)\n requested = 'auto'\n }\n if (requested !== 'native' && exsorted === undefined) {\n // DK_DEP_INCOMPATIBLE is a development check (§8.5); the fallback is not.\n exsorted = (process.env.NODE_ENV !== \"production\")\n ? detectCapability(\n hasExsorted,\n {\n feature: 'sorting',\n dependency: 'exsorted',\n capability: 'timSort / radixSort / countingSort',\n supported: '^1.2.3',\n fallback: 'Array.prototype.sort (native, stable)',\n },\n ctx.emit,\n )\n : hasCapability(hasExsorted)\n }\n\n // auto at large n (§9.1): string columns sort by collation ranks, computed by a sliced job.\n if (requested === 'auto' && indices.length >= RANKS_MIN) {\n let computing = false\n for (const e of entries) {\n if (e.sortFn || e.values instanceof Float64Array) continue\n const ranks = ranksFor(ctx, e.id, e.values)\n if (ranks === undefined) computing = true\n else if (ranks) {\n e.values = ranks\n e.ranked = true\n }\n }\n // Meanwhile: the previous order of these rows (or the input order); 'pending'.\n if (computing) return shown && shown.input === indices ? shown.output : indices\n }\n const ranked = entries.some((e) => e.ranked)\n\n const build = (): IndexComparator => {\n const cmps = entries.map((e, k) => {\n const fn = e.sortFn\n if (!fn) return defaultIndexComparator(e.values, e.desc, getCollator(ctx.emit))\n const guarded: SortFn = (a, b) => {\n try {\n return fn(a, b)\n } catch (error) {\n throw new SortFnFailed(k, error)\n }\n }\n return sortFnIndexComparator(e.values, e.desc, guarded)\n })\n if (cmps.length === 1) return cmps[0] as IndexComparator\n return (a, b) => {\n for (let k = 0; k < cmps.length; k++) {\n const c = (cmps[k] as IndexComparator)(a, b)\n if (c !== 0) return c\n }\n return 0\n }\n }\n\n let algorithm: string = 'native'\n let reason = ''\n /** One attempt; a throwing sortFn propagates as SortFnFailed (the loop below retries). */\n const sortOnce = (): Uint32Array => {\n const want = requested\n if (typeof want === 'function') {\n const custom = want as CustomAlgorithm\n let res: unknown\n try {\n res = custom(indices.slice(), build())\n } catch (error) {\n if (error instanceof SortFnFailed) throw error\n reportCustom(true, error)\n return native('custom-threw')\n }\n if (res instanceof Uint32Array && res.length === indices.length) {\n algorithm = 'custom'\n reason = fromColumn ? 'column sortAlgorithm' : 'sorting({ algorithm })'\n return res\n }\n reportCustom(false, res)\n return native('custom-bad')\n }\n if (exsorted === false) return native('exsorted unavailable')\n const by = fromColumn ? 'column sortAlgorithm' : 'sorting({ algorithm })'\n // Integer keys: a single column with the default comparator (§6.4).\n const single = entries.length === 1 && !primary.sortFn\n const keys =\n single && want !== 'native' ? intKeys(primary.values, indices, primary.desc) : undefined\n if (want !== 'auto' && want !== 'native') {\n if (keys) {\n try {\n const out = sortIntKeys(keys, want)\n algorithm = want\n reason = `int-keys|${by}`\n return out\n } catch {\n noEffect(`exsorted-${want === 'counting' ? 'range' : 'failed'}|${want}`)\n }\n } else if (needsIntKeys(want)) {\n noEffect(`needs-int-keys|${want}`)\n } else {\n algorithm = want\n reason = by\n return sortWithComparator(indices, build(), want as 'tim')\n }\n } else if (want === 'native') return native(by)\n if (!keys) {\n return native(\n single ? 'not-safe-int' : entries.length > 1 ? 'multi-column' : 'custom sortFn',\n )\n }\n const choice = chooseAuto(keys)\n algorithm = choice.algorithm\n reason = `auto-${ranked ? 'ranked-' : ''}${choice.reason}`\n return sortIntKeys(keys, choice.algorithm)\n }\n const native = (why: string) => {\n algorithm = 'native'\n reason = ranked ? `ranks|${why}` : why\n return sortWithComparator(indices, build(), 'native')\n }\n /** A custom algorithm threw (`threw`, `value` = the error) or returned a bad `value`. */\n const reportCustom = (threw: boolean, value: unknown) => {\n const slot = fromColumn\n ? `columns[${primary.col?.index}].sortAlgorithm`\n : 'sorting.algorithm'\n const fallback = 'sorted-native'\n if (!threw) {\n // The slot contract check is dev only (§3.4 slotContract); the fallback applies always.\n if ((process.env.NODE_ENV !== \"production\")) {\n ctx.emit('DK_CALLBACK_BAD_RETURN', {\n slot,\n expected: `a Uint32Array of ${indices.length} row indices`,\n received: describeReturn(value),\n count: 1,\n fallback,\n })\n }\n return\n }\n const name = errorName(value)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot,\n count: 1,\n fallback,\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: value },\n )\n }\n\n // At most one retry per entry: each failing sortFn falls back to the default comparator.\n for (;;) {\n try {\n const out = sortOnce()\n if (opts.debug) {\n ctx.emit('DK_SORT_STRATEGY', {\n algorithm,\n reason,\n n: indices.length,\n ms: now() - t0,\n by: by.map((e) => `${String(e.id)}:${e.desc ? 'desc' : 'asc'}`),\n ...(primary.col ? { column: { index: primary.col.index, id: primary.col.id } } : {}),\n })\n }\n shown = { input: indices, output: out }\n return out\n } catch (e) {\n if (!(e instanceof SortFnFailed)) throw e\n const entry = entries[e.entry] as (typeof entries)[number]\n entry.sortFn = undefined\n const col = entry.col as ResolvedColumn\n const name = errorName(e.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `columns[${col.index}].sortFn`,\n column: col.id,\n count: 1,\n fallback: 'sorted-default',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: e.error },\n )\n }\n }\n },\n })\n // #98: a destroyed table drops these caches (the instance may stay referenced).\n const internal = feature as Releasable\n internal[RELEASE] = () => {\n rankKeys = new WeakMap()\n history = new Map()\n shown = undefined\n sortByCache = new WeakMap()\n pathCache = new WeakMap()\n }\n if ((process.env.NODE_ENV !== \"production\")) internal[PROBE] = () => ({ rankKeys, history, shown, sortByCache, pathCache })\n return feature\n}\n"],"mappings":"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","names":["import_codes","reason","by"]}