@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.
- package/dist/features/search/search-index.cjs +23 -44
- package/dist/features/search/search-index.cjs.map +1 -1
- package/dist/features/search/search-index.js +24 -45
- package/dist/features/search/search-index.js.map +1 -1
- package/dist/features/sorting/ranks.cjs +117 -50
- package/dist/features/sorting/ranks.cjs.map +1 -1
- package/dist/features/sorting/ranks.js +117 -50
- package/dist/features/sorting/ranks.js.map +1 -1
- package/dist/features/sorting/sorting.cjs +32 -8
- package/dist/features/sorting/sorting.cjs.map +1 -1
- package/dist/features/sorting/sorting.js +32 -8
- package/dist/features/sorting/sorting.js.map +1 -1
- package/dist/pipeline/column-cache.cjs +6 -2
- package/dist/pipeline/column-cache.cjs.map +1 -1
- package/dist/pipeline/column-cache.js +6 -2
- package/dist/pipeline/column-cache.js.map +1 -1
- package/dist/pipeline/release.cjs +34 -0
- package/dist/pipeline/release.cjs.map +1 -0
- package/dist/pipeline/release.js +8 -0
- package/dist/pipeline/release.js.map +1 -0
- package/dist/pipeline/runner.cjs +15 -1
- package/dist/pipeline/runner.cjs.map +1 -1
- package/dist/pipeline/runner.js +15 -1
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/table/create-table.cjs +3 -0
- package/dist/table/create-table.cjs.map +1 -1
- package/dist/table/create-table.js +3 -0
- package/dist/table/create-table.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
|
@@ -15,6 +15,7 @@ import { boundaryDiagnostics } from "../../intercept/codes.js";
|
|
|
15
15
|
import { resolveColumns } from "../../pipeline/columns.js";
|
|
16
16
|
import { envDiagnostics } from "../../pipeline/env-codes.js";
|
|
17
17
|
import { allRows } from "../../pipeline/indices.js";
|
|
18
|
+
import { PROBE, RELEASE } from "../../pipeline/release.js";
|
|
18
19
|
import { sliced } from "../../pipeline/slice.js";
|
|
19
20
|
import { modeOption } from "../../source/mode.js";
|
|
20
21
|
import { detectCapability, hasCapability } from "../../versioning/capability.js";
|
|
@@ -150,19 +151,31 @@ var sorting = (opts = {}) => {
|
|
|
150
151
|
}
|
|
151
152
|
return collator;
|
|
152
153
|
};
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
let rankKeys = /* @__PURE__ */ new WeakMap();
|
|
155
|
+
let history = /* @__PURE__ */ new Map();
|
|
156
|
+
function keepHistory(by) {
|
|
157
|
+
for (const id of history.keys()) {
|
|
158
|
+
if (!by.some((e) => String(e.id) === id)) history.delete(id);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function ranksFor(ctx, id, values) {
|
|
155
162
|
let key = rankKeys.get(values);
|
|
156
163
|
if (!key) {
|
|
157
164
|
key = {};
|
|
158
165
|
rankKeys.set(values, key);
|
|
159
166
|
}
|
|
160
167
|
const c = getCollator(ctx.emit);
|
|
161
|
-
|
|
168
|
+
const into = history;
|
|
169
|
+
return sliced(ctx, key, function* () {
|
|
170
|
+
const r = yield* collationRanks(values, c, into.get(id));
|
|
171
|
+
if (!r) return null;
|
|
172
|
+
into.set(id, r.state);
|
|
173
|
+
return r.ranks;
|
|
174
|
+
});
|
|
162
175
|
}
|
|
163
176
|
let shown;
|
|
164
|
-
|
|
165
|
-
|
|
177
|
+
let sortByCache = /* @__PURE__ */ new WeakMap();
|
|
178
|
+
let pathCache = /* @__PURE__ */ new WeakMap();
|
|
166
179
|
let exsorted;
|
|
167
180
|
const sortByFailures = new CellFailures();
|
|
168
181
|
function sortByValues(ctx, col, sortBy) {
|
|
@@ -208,7 +221,7 @@ var sorting = (opts = {}) => {
|
|
|
208
221
|
byId.set(id, out);
|
|
209
222
|
return out;
|
|
210
223
|
}
|
|
211
|
-
|
|
224
|
+
const feature = defineFeature({
|
|
212
225
|
name: "sorting",
|
|
213
226
|
stage: "sort",
|
|
214
227
|
mode: opts.mode ?? "client",
|
|
@@ -246,6 +259,7 @@ var sorting = (opts = {}) => {
|
|
|
246
259
|
},
|
|
247
260
|
rowModel: (indices, state, ctx) => {
|
|
248
261
|
const by = entriesOf(state);
|
|
262
|
+
keepHistory(by);
|
|
249
263
|
if (by.length === 0 || indices.length < 2) {
|
|
250
264
|
shown = void 0;
|
|
251
265
|
return indices;
|
|
@@ -258,7 +272,7 @@ var sorting = (opts = {}) => {
|
|
|
258
272
|
const sortBy = col && fnField(col.def, "sortBy");
|
|
259
273
|
const values = sortBy ? sortByValues(ctx, col, sortBy) : ctx.getColumnValues(id) ?? pathValues(ctx, id);
|
|
260
274
|
const sortFn = col && !sortBy ? fnField(col.def, "sortFn") : void 0;
|
|
261
|
-
return { col, desc: e.desc === true, values, sortFn, ranked: false };
|
|
275
|
+
return { id, col, desc: e.desc === true, values, sortFn, ranked: false };
|
|
262
276
|
});
|
|
263
277
|
if (process.env.NODE_ENV !== "production") {
|
|
264
278
|
for (const e of entries) {
|
|
@@ -299,7 +313,7 @@ var sorting = (opts = {}) => {
|
|
|
299
313
|
let computing = false;
|
|
300
314
|
for (const e of entries) {
|
|
301
315
|
if (e.sortFn || e.values instanceof Float64Array) continue;
|
|
302
|
-
const ranks = ranksFor(ctx, e.values);
|
|
316
|
+
const ranks = ranksFor(ctx, e.id, e.values);
|
|
303
317
|
if (ranks === void 0) computing = true;
|
|
304
318
|
else if (ranks) {
|
|
305
319
|
e.values = ranks;
|
|
@@ -453,6 +467,16 @@ var sorting = (opts = {}) => {
|
|
|
453
467
|
}
|
|
454
468
|
}
|
|
455
469
|
});
|
|
470
|
+
const internal = feature;
|
|
471
|
+
internal[RELEASE] = () => {
|
|
472
|
+
rankKeys = /* @__PURE__ */ new WeakMap();
|
|
473
|
+
history = /* @__PURE__ */ new Map();
|
|
474
|
+
shown = void 0;
|
|
475
|
+
sortByCache = /* @__PURE__ */ new WeakMap();
|
|
476
|
+
pathCache = /* @__PURE__ */ new WeakMap();
|
|
477
|
+
};
|
|
478
|
+
if (process.env.NODE_ENV !== "production") internal[PROBE] = () => ({ rankKeys, history, shown, sortByCache, pathCache });
|
|
479
|
+
return feature;
|
|
456
480
|
};
|
|
457
481
|
export {
|
|
458
482
|
sorting
|
|
@@ -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":"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","names":["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":["reason","by"]}
|
|
@@ -58,7 +58,7 @@ function share(cells) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
function createColumnCache(host) {
|
|
61
|
-
|
|
61
|
+
let byData = /* @__PURE__ */ new WeakMap();
|
|
62
62
|
const pool = (0, import_context.createContextPool)();
|
|
63
63
|
const failures = new import_cell_batch.CellFailures();
|
|
64
64
|
function entry(data, column) {
|
|
@@ -244,7 +244,11 @@ function createColumnCache(host) {
|
|
|
244
244
|
values,
|
|
245
245
|
formatted,
|
|
246
246
|
formattedSliced: (data, column) => formatting(data, column, true),
|
|
247
|
-
batch
|
|
247
|
+
batch,
|
|
248
|
+
release: () => {
|
|
249
|
+
byData = /* @__PURE__ */ new WeakMap();
|
|
250
|
+
},
|
|
251
|
+
...process.env.NODE_ENV !== "production" ? { holds: (data) => byData.has(data) } : {}
|
|
248
252
|
};
|
|
249
253
|
}
|
|
250
254
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pipeline/column-cache.ts"],"sourcesContent":["import { describeReturn, expectedReturn, formatSlot, slotDiagnostics } from '../catalog/slots'\nimport { defaultFormat } from '../column/format'\nimport { getValue } from '../column/get-value'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { CellFailures, type CellSlot, runCells } from '../intercept/cell-batch'\nimport { type ContextBase, createContextPool } from '../intercept/context'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport { pipelineDiagnostics } from './codes'\nimport type { ColumnLike, ResolvedColumn } from './columns'\nimport { allRows } from './indices'\nimport { runJob } from './slice'\nimport type { ColumnValues } from './types'\n\nkeepDiagnostics(pipelineDiagnostics, slotDiagnostics)\n\nexport interface ColumnCacheHost {\n readonly runtime: InterceptorRuntime\n readonly emit: Emit\n base(): ContextBase\n /**\n * The §6.6 status rule's counter (pipeline/status-rule.ts): `emit` increments it for a diagnostic\n * the rule counts. An entry whose read incremented it is flagged, and every later read of that\n * entry increments it again (its cells still hold the fallback values).\n */\n readonly flags?: { count: number }\n}\n\n/**\n * Internal member of the pipeline's run context (not on the public `RowModelContext`): a column's\n * formatted values as a time-sliced job (search, #40); `undefined` for an unknown column.\n */\nexport interface SlicedFormatting {\n readonly formattedSliced?: (\n id: string,\n ) => Generator<undefined, readonly string[] | undefined, undefined>\n}\n\nexport interface ColumnCache {\n /**\n * The values of `column` for every row of `data` (index = row index). An all-number column is a\n * `Float64Array` with `NaN` for `null` / `undefined`; any other column a plain array.\n */\n values(data: readonly unknown[], column: ResolvedColumn): ColumnValues\n /** `format` (or the default formatter) applied to {@link values}: what the user sees. */\n formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[]\n /**\n * {@link formatted} as a time-sliced job (§9.1, #40): the cells are formatted in batches of a few\n * milliseconds with a `yield` between them (one `column:batch` per batch, never per cell).\n */\n formattedSliced(\n data: readonly unknown[],\n column: ResolvedColumn,\n ): Generator<undefined, readonly string[], undefined>\n /**\n * Runs `body` as one `column:batch` over `rows` (pooled ctx, `where` = `columns[i].<field>`):\n * interceptors see one dispatch per column, never one per cell. `body` loops over the cells\n * with an inline `try/catch` and reports its own failures.\n */\n batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ): void\n}\n\ninterface Entry {\n values?: ColumnValues\n /** Numeric columns: 1 = `null`, 2 = `undefined` per row. Absent when there are none. */\n nullish?: Uint8Array | undefined\n formatted?: readonly string[]\n /** Reading `values` / `formatted` reported a diagnostic the status rule counts. */\n flaggedValues?: boolean\n flaggedFormatted?: boolean\n}\n\nconst NULL = 1\nconst UNDEFINED = 2\n\n/** The nullish kinds of each cached numeric column that has any (see {@link nullishOf}). */\nconst nullishKinds = new WeakMap<Float64Array, Uint8Array>()\n\n/**\n * Which rows of a cached all-number column (`values` from the column cache) hold `null` /\n * `undefined` (non-zero) rather than a real `NaN`: both are stored as `NaN`. `undefined` when no\n * row does. Lets search format such a column on the fly (`''` / `'NaN'`) instead of caching a\n * string per row (#27).\n */\nexport function nullishOf(values: Float64Array): Uint8Array | undefined {\n return nullishKinds.get(values)\n}\n\nfunction allStrings(values: readonly unknown[]): boolean {\n for (let i = 0; i < values.length; i++) if (typeof values[i] !== 'string') return false\n return true\n}\n\nconst typeName = (v: unknown) => (Array.isArray(v) ? 'array' : typeof v)\n\n/** Rows per batch of a sliced format (#40): small, so the slicer's clock check comes often. */\nconst STEP = 512\n\n/** Distinct strings {@link share} remembers per column: enough for categories, tags, flags. */\nconst SHARED = 1024\n\n/**\n * Repeated formatted strings of a column share one copy (§9.3 memory, #27): each formatted cell is\n * a fresh string, so a column of tags or categories would keep one per row. Only the first\n * {@link SHARED} distinct strings are remembered; once per column, after formatting.\n */\nfunction share(cells: string[]) {\n const seen = new Map<string, string>()\n for (let r = 0; r < cells.length; r++) {\n const s = cells[r] as string\n const hit = seen.get(s)\n if (hit !== undefined) cells[r] = hit\n else if (seen.size < SHARED) seen.set(s, s)\n }\n}\n\n/**\n * The lazy column cache (§9.1): a column is read only when a step asks for it, once per data\n * reference, in one `column:batch` per column (never per cell, §3.4). Entries live in a `WeakMap`\n * keyed by the data reference, then by the column definition, so new data or a changed column\n * releases them.\n */\nexport function createColumnCache(host: ColumnCacheHost): ColumnCache {\n const byData = new WeakMap<readonly unknown[], WeakMap<ColumnLike, Entry>>()\n const pool = createContextPool()\n const failures = new CellFailures()\n\n function entry(data: readonly unknown[], column: ResolvedColumn): Entry {\n let cols = byData.get(data)\n if (!cols) {\n cols = new WeakMap()\n byData.set(data, cols)\n }\n let e = cols.get(column.def)\n if (!e) {\n e = {}\n cols.set(column.def, e)\n }\n return e\n }\n\n /** One `column:batch` (pooled ctx); `where` = `columns[i].<field>`, never row data. */\n function batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ) {\n const ctx = pool.acquire(\n 'column:batch',\n { column: column.id, slot, rows },\n `columns[${column.index}].${field}`,\n host.base(),\n )\n try {\n void host.runtime.run('column:batch', ctx, (): undefined => {\n try {\n body()\n } finally {\n failures.reset()\n }\n })\n } finally {\n pool.release(ctx)\n }\n }\n\n function readValues(data: readonly unknown[], column: ResolvedColumn, e: Entry): ColumnValues {\n const n = data.length\n const rows = allRows(data)\n const out: unknown[] = new Array(n).fill(undefined)\n const field = column.path ? 'key' : 'accessor'\n batch(column, 'accessor', field, rows, () => {\n const path = column.path\n const accessor = column.accessor\n if (path) {\n let cur = 0\n const onError = (error: unknown) => failures.add(cur, error)\n runCells(\n rows,\n (r) => {\n cur = r\n return getValue(data[r], path, onError)\n },\n out,\n undefined,\n failures,\n )\n } else if (accessor) {\n runCells(rows, (r) => accessor(data[r]), out, undefined, failures)\n }\n failures.report(host.emit, column.index, column.id, 'accessor', undefined, field)\n })\n return toNumeric(out, e) ?? out\n }\n\n /** A `Float64Array` when every non-nullish value is a number (and there is one); else nothing. */\n function toNumeric(values: unknown[], e: Entry): Float64Array | undefined {\n let numbers = 0\n let nullish = 0\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') numbers++\n else if (v === null || v === undefined) nullish++\n else return undefined\n }\n if (numbers === 0) return undefined\n const out = new Float64Array(values.length)\n const kinds = nullish > 0 ? new Uint8Array(values.length) : undefined\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') out[i] = v\n else {\n out[i] = Number.NaN\n if (kinds) kinds[i] = v === null ? NULL : UNDEFINED\n }\n }\n e.nullish = kinds\n if (kinds) nullishKinds.set(out, kinds)\n return out\n }\n\n const flags = host.flags\n\n function values(data: readonly unknown[], column: ResolvedColumn): ColumnValues {\n const e = entry(data, column)\n if (e.values) {\n if (flags && e.flaggedValues) flags.count++\n return e.values\n }\n const before = flags?.count ?? 0\n e.values = readValues(data, column, e)\n if (flags && flags.count > before) e.flaggedValues = true\n return e.values\n }\n\n /**\n * Formats `column` once per data reference, in one `column:batch`; `sliced` (#40): in batches of\n * {@link STEP} rows with a `yield` between them, so the slicer can end the task on time (a slow\n * formatter, e.g. dates, is never one long task). Failures and slot-contract breaks are counted\n * over the whole column and reported once.\n */\n function* formatting(\n data: readonly unknown[],\n column: ResolvedColumn,\n sliced: boolean,\n ): Generator<undefined, readonly string[], undefined> {\n let e = entry(data, column)\n if (e.formatted) {\n if (flags && e.flaggedFormatted) flags.count++\n return e.formatted\n }\n const before = flags?.count ?? 0\n const vals = values(data, column)\n if (sliced) yield // reading the values is a task of its own\n const kinds = e.nullish\n // The raw value: nullish numbers read back as null / undefined, not NaN.\n const valueAt = (r: number): unknown => {\n const k = kinds?.[r]\n return k ? (k === NULL ? null : undefined) : vals[r]\n }\n const n = data.length\n const rows = allRows(data)\n const format = column.format\n // Job-local counts: a sliced column spans several batches.\n const failed = new CellFailures()\n const bad = (process.env.NODE_ENV !== \"production\") ? new CellFailures() : undefined\n const unrendered = new CellFailures()\n let firstBad: unknown\n let firstType = ''\n let cur = 0\n // A column with `cell` renders itself: its formatted value only feeds search.\n const onUnrenderable = column.hasCell\n ? undefined\n : (v: unknown) => {\n if (unrendered.count === 0) firstType = typeName(v)\n unrendered.add(cur, undefined)\n }\n const cell = format\n ? (r: number) => {\n const s = format(valueAt(r))\n if (typeof s === 'string') return s\n // Slot contract (dev): counted only on this non-string path; coerced either way.\n if ((process.env.NODE_ENV !== \"production\") && bad) {\n if (bad.count === 0) firstBad = s\n bad.add(r, undefined)\n }\n return defaultFormat(s)\n }\n : (r: number) => {\n cur = r\n return defaultFormat(valueAt(r), onUnrenderable)\n }\n // Values that are all strings already format to themselves: no copy (§9.3 memory).\n const strings = !format && !(vals instanceof Float64Array) && allStrings(vals)\n let out: string[] | undefined = strings ? undefined : new Array(n).fill('')\n for (let from = 0; from < n; from += STEP) {\n const part = sliced && !strings ? rows.subarray(from, from + STEP) : rows\n // An interceptor may skip a batch: its cells keep the slot fallback ('').\n batch(column, 'format', 'format', part, () => {\n if (strings) out = vals as string[]\n else runCells(part, cell, out as string[], '', failed)\n })\n if (part === rows || from + STEP >= n) break\n yield\n }\n out ??= new Array<string>(n).fill('')\n failed.report(host.emit, column.index, column.id, 'format')\n if ((process.env.NODE_ENV !== \"production\") && bad && bad.count > 0) {\n host.emit('DK_CALLBACK_BAD_RETURN', {\n slot: `columns[${column.index}].format`,\n column: column.id,\n expected: expectedReturn(formatSlot),\n received: describeReturn(firstBad),\n count: bad.count,\n rows: Array.from(bad.sample()),\n fallback: formatSlot.fallback,\n })\n }\n if (unrendered.count > 0) {\n host.emit('DK_UNRENDERABLE_VALUE', {\n column: column.id,\n index: column.index,\n count: unrendered.count,\n rows: Array.from(unrendered.sample()),\n valueType: firstType,\n })\n }\n if (out !== vals) share(out)\n // A synchronous read may have cached the column while a sliced job ran.\n e = entry(data, column)\n if (!e.formatted) {\n e.formatted = out\n if (flags && flags.count > before) e.flaggedFormatted = true\n }\n return e.formatted\n }\n\n function formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[] {\n return runJob(() => formatting(data, column, false))\n }\n\n return {\n values,\n formatted,\n formattedSliced: (data, column) => formatting(data, column, true),\n batch,\n }\n}\n"],"mappings":"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","names":["values"]}
|
|
1
|
+
{"version":3,"sources":["../../src/pipeline/column-cache.ts"],"sourcesContent":["import { describeReturn, expectedReturn, formatSlot, slotDiagnostics } from '../catalog/slots'\nimport { defaultFormat } from '../column/format'\nimport { getValue } from '../column/get-value'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { CellFailures, type CellSlot, runCells } from '../intercept/cell-batch'\nimport { type ContextBase, createContextPool } from '../intercept/context'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport { pipelineDiagnostics } from './codes'\nimport type { ColumnLike, ResolvedColumn } from './columns'\nimport { allRows } from './indices'\nimport { runJob } from './slice'\nimport type { ColumnValues } from './types'\n\nkeepDiagnostics(pipelineDiagnostics, slotDiagnostics)\n\nexport interface ColumnCacheHost {\n readonly runtime: InterceptorRuntime\n readonly emit: Emit\n base(): ContextBase\n /**\n * The §6.6 status rule's counter (pipeline/status-rule.ts): `emit` increments it for a diagnostic\n * the rule counts. An entry whose read incremented it is flagged, and every later read of that\n * entry increments it again (its cells still hold the fallback values).\n */\n readonly flags?: { count: number }\n}\n\n/**\n * Internal member of the pipeline's run context (not on the public `RowModelContext`): a column's\n * formatted values as a time-sliced job (search, #40); `undefined` for an unknown column.\n */\nexport interface SlicedFormatting {\n readonly formattedSliced?: (\n id: string,\n ) => Generator<undefined, readonly string[] | undefined, undefined>\n}\n\nexport interface ColumnCache {\n /**\n * The values of `column` for every row of `data` (index = row index). An all-number column is a\n * `Float64Array` with `NaN` for `null` / `undefined`; any other column a plain array.\n */\n values(data: readonly unknown[], column: ResolvedColumn): ColumnValues\n /** `format` (or the default formatter) applied to {@link values}: what the user sees. */\n formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[]\n /**\n * {@link formatted} as a time-sliced job (§9.1, #40): the cells are formatted in batches of a few\n * milliseconds with a `yield` between them (one `column:batch` per batch, never per cell).\n */\n formattedSliced(\n data: readonly unknown[],\n column: ResolvedColumn,\n ): Generator<undefined, readonly string[], undefined>\n /**\n * Runs `body` as one `column:batch` over `rows` (pooled ctx, `where` = `columns[i].<field>`):\n * interceptors see one dispatch per column, never one per cell. `body` loops over the cells\n * with an inline `try/catch` and reports its own failures.\n */\n batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ): void\n /** Drops every entry (`destroy()`, #98). */\n release(): void\n /** Development only, for tests (#98): an entry of `data` is cached. */\n holds?(data: readonly unknown[]): boolean\n}\n\ninterface Entry {\n values?: ColumnValues\n /** Numeric columns: 1 = `null`, 2 = `undefined` per row. Absent when there are none. */\n nullish?: Uint8Array | undefined\n formatted?: readonly string[]\n /** Reading `values` / `formatted` reported a diagnostic the status rule counts. */\n flaggedValues?: boolean\n flaggedFormatted?: boolean\n}\n\nconst NULL = 1\nconst UNDEFINED = 2\n\n/** The nullish kinds of each cached numeric column that has any (see {@link nullishOf}). */\nconst nullishKinds = new WeakMap<Float64Array, Uint8Array>()\n\n/**\n * Which rows of a cached all-number column (`values` from the column cache) hold `null` /\n * `undefined` (non-zero) rather than a real `NaN`: both are stored as `NaN`. `undefined` when no\n * row does. Lets search format such a column on the fly (`''` / `'NaN'`) instead of caching a\n * string per row (#27).\n */\nexport function nullishOf(values: Float64Array): Uint8Array | undefined {\n return nullishKinds.get(values)\n}\n\nfunction allStrings(values: readonly unknown[]): boolean {\n for (let i = 0; i < values.length; i++) if (typeof values[i] !== 'string') return false\n return true\n}\n\nconst typeName = (v: unknown) => (Array.isArray(v) ? 'array' : typeof v)\n\n/** Rows per batch of a sliced format (#40): small, so the slicer's clock check comes often. */\nconst STEP = 512\n\n/** Distinct strings {@link share} remembers per column: enough for categories, tags, flags. */\nconst SHARED = 1024\n\n/**\n * Repeated formatted strings of a column share one copy (§9.3 memory, #27): each formatted cell is\n * a fresh string, so a column of tags or categories would keep one per row. Only the first\n * {@link SHARED} distinct strings are remembered; once per column, after formatting.\n */\nfunction share(cells: string[]) {\n const seen = new Map<string, string>()\n for (let r = 0; r < cells.length; r++) {\n const s = cells[r] as string\n const hit = seen.get(s)\n if (hit !== undefined) cells[r] = hit\n else if (seen.size < SHARED) seen.set(s, s)\n }\n}\n\n/**\n * The lazy column cache (§9.1): a column is read only when a step asks for it, once per data\n * reference, in one `column:batch` per column (never per cell, §3.4). Entries live in a `WeakMap`\n * keyed by the data reference, then by the column definition, so new data or a changed column\n * releases them.\n */\nexport function createColumnCache(host: ColumnCacheHost): ColumnCache {\n let byData = new WeakMap<readonly unknown[], WeakMap<ColumnLike, Entry>>()\n const pool = createContextPool()\n const failures = new CellFailures()\n\n function entry(data: readonly unknown[], column: ResolvedColumn): Entry {\n let cols = byData.get(data)\n if (!cols) {\n cols = new WeakMap()\n byData.set(data, cols)\n }\n let e = cols.get(column.def)\n if (!e) {\n e = {}\n cols.set(column.def, e)\n }\n return e\n }\n\n /** One `column:batch` (pooled ctx); `where` = `columns[i].<field>`, never row data. */\n function batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ) {\n const ctx = pool.acquire(\n 'column:batch',\n { column: column.id, slot, rows },\n `columns[${column.index}].${field}`,\n host.base(),\n )\n try {\n void host.runtime.run('column:batch', ctx, (): undefined => {\n try {\n body()\n } finally {\n failures.reset()\n }\n })\n } finally {\n pool.release(ctx)\n }\n }\n\n function readValues(data: readonly unknown[], column: ResolvedColumn, e: Entry): ColumnValues {\n const n = data.length\n const rows = allRows(data)\n const out: unknown[] = new Array(n).fill(undefined)\n const field = column.path ? 'key' : 'accessor'\n batch(column, 'accessor', field, rows, () => {\n const path = column.path\n const accessor = column.accessor\n if (path) {\n let cur = 0\n const onError = (error: unknown) => failures.add(cur, error)\n runCells(\n rows,\n (r) => {\n cur = r\n return getValue(data[r], path, onError)\n },\n out,\n undefined,\n failures,\n )\n } else if (accessor) {\n runCells(rows, (r) => accessor(data[r]), out, undefined, failures)\n }\n failures.report(host.emit, column.index, column.id, 'accessor', undefined, field)\n })\n return toNumeric(out, e) ?? out\n }\n\n /** A `Float64Array` when every non-nullish value is a number (and there is one); else nothing. */\n function toNumeric(values: unknown[], e: Entry): Float64Array | undefined {\n let numbers = 0\n let nullish = 0\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') numbers++\n else if (v === null || v === undefined) nullish++\n else return undefined\n }\n if (numbers === 0) return undefined\n const out = new Float64Array(values.length)\n const kinds = nullish > 0 ? new Uint8Array(values.length) : undefined\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') out[i] = v\n else {\n out[i] = Number.NaN\n if (kinds) kinds[i] = v === null ? NULL : UNDEFINED\n }\n }\n e.nullish = kinds\n if (kinds) nullishKinds.set(out, kinds)\n return out\n }\n\n const flags = host.flags\n\n function values(data: readonly unknown[], column: ResolvedColumn): ColumnValues {\n const e = entry(data, column)\n if (e.values) {\n if (flags && e.flaggedValues) flags.count++\n return e.values\n }\n const before = flags?.count ?? 0\n e.values = readValues(data, column, e)\n if (flags && flags.count > before) e.flaggedValues = true\n return e.values\n }\n\n /**\n * Formats `column` once per data reference, in one `column:batch`; `sliced` (#40): in batches of\n * {@link STEP} rows with a `yield` between them, so the slicer can end the task on time (a slow\n * formatter, e.g. dates, is never one long task). Failures and slot-contract breaks are counted\n * over the whole column and reported once.\n */\n function* formatting(\n data: readonly unknown[],\n column: ResolvedColumn,\n sliced: boolean,\n ): Generator<undefined, readonly string[], undefined> {\n let e = entry(data, column)\n if (e.formatted) {\n if (flags && e.flaggedFormatted) flags.count++\n return e.formatted\n }\n const before = flags?.count ?? 0\n const vals = values(data, column)\n if (sliced) yield // reading the values is a task of its own\n const kinds = e.nullish\n // The raw value: nullish numbers read back as null / undefined, not NaN.\n const valueAt = (r: number): unknown => {\n const k = kinds?.[r]\n return k ? (k === NULL ? null : undefined) : vals[r]\n }\n const n = data.length\n const rows = allRows(data)\n const format = column.format\n // Job-local counts: a sliced column spans several batches.\n const failed = new CellFailures()\n const bad = (process.env.NODE_ENV !== \"production\") ? new CellFailures() : undefined\n const unrendered = new CellFailures()\n let firstBad: unknown\n let firstType = ''\n let cur = 0\n // A column with `cell` renders itself: its formatted value only feeds search.\n const onUnrenderable = column.hasCell\n ? undefined\n : (v: unknown) => {\n if (unrendered.count === 0) firstType = typeName(v)\n unrendered.add(cur, undefined)\n }\n const cell = format\n ? (r: number) => {\n const s = format(valueAt(r))\n if (typeof s === 'string') return s\n // Slot contract (dev): counted only on this non-string path; coerced either way.\n if ((process.env.NODE_ENV !== \"production\") && bad) {\n if (bad.count === 0) firstBad = s\n bad.add(r, undefined)\n }\n return defaultFormat(s)\n }\n : (r: number) => {\n cur = r\n return defaultFormat(valueAt(r), onUnrenderable)\n }\n // Values that are all strings already format to themselves: no copy (§9.3 memory).\n const strings = !format && !(vals instanceof Float64Array) && allStrings(vals)\n let out: string[] | undefined = strings ? undefined : new Array(n).fill('')\n for (let from = 0; from < n; from += STEP) {\n const part = sliced && !strings ? rows.subarray(from, from + STEP) : rows\n // An interceptor may skip a batch: its cells keep the slot fallback ('').\n batch(column, 'format', 'format', part, () => {\n if (strings) out = vals as string[]\n else runCells(part, cell, out as string[], '', failed)\n })\n if (part === rows || from + STEP >= n) break\n yield\n }\n out ??= new Array<string>(n).fill('')\n failed.report(host.emit, column.index, column.id, 'format')\n if ((process.env.NODE_ENV !== \"production\") && bad && bad.count > 0) {\n host.emit('DK_CALLBACK_BAD_RETURN', {\n slot: `columns[${column.index}].format`,\n column: column.id,\n expected: expectedReturn(formatSlot),\n received: describeReturn(firstBad),\n count: bad.count,\n rows: Array.from(bad.sample()),\n fallback: formatSlot.fallback,\n })\n }\n if (unrendered.count > 0) {\n host.emit('DK_UNRENDERABLE_VALUE', {\n column: column.id,\n index: column.index,\n count: unrendered.count,\n rows: Array.from(unrendered.sample()),\n valueType: firstType,\n })\n }\n if (out !== vals) share(out)\n // A synchronous read may have cached the column while a sliced job ran.\n e = entry(data, column)\n if (!e.formatted) {\n e.formatted = out\n if (flags && flags.count > before) e.flaggedFormatted = true\n }\n return e.formatted\n }\n\n function formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[] {\n return runJob(() => formatting(data, column, false))\n }\n\n return {\n values,\n formatted,\n formattedSliced: (data, column) => formatting(data, column, true),\n batch,\n release: () => {\n byData = new WeakMap()\n },\n ...((process.env.NODE_ENV !== \"production\") ? { holds: (data: readonly unknown[]) => byData.has(data) } : {}),\n }\n}\n"],"mappings":"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","names":["values"]}
|
|
@@ -33,7 +33,7 @@ function share(cells) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
function createColumnCache(host) {
|
|
36
|
-
|
|
36
|
+
let byData = /* @__PURE__ */ new WeakMap();
|
|
37
37
|
const pool = createContextPool();
|
|
38
38
|
const failures = new CellFailures();
|
|
39
39
|
function entry(data, column) {
|
|
@@ -219,7 +219,11 @@ function createColumnCache(host) {
|
|
|
219
219
|
values,
|
|
220
220
|
formatted,
|
|
221
221
|
formattedSliced: (data, column) => formatting(data, column, true),
|
|
222
|
-
batch
|
|
222
|
+
batch,
|
|
223
|
+
release: () => {
|
|
224
|
+
byData = /* @__PURE__ */ new WeakMap();
|
|
225
|
+
},
|
|
226
|
+
...process.env.NODE_ENV !== "production" ? { holds: (data) => byData.has(data) } : {}
|
|
223
227
|
};
|
|
224
228
|
}
|
|
225
229
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pipeline/column-cache.ts"],"sourcesContent":["import { describeReturn, expectedReturn, formatSlot, slotDiagnostics } from '../catalog/slots'\nimport { defaultFormat } from '../column/format'\nimport { getValue } from '../column/get-value'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { CellFailures, type CellSlot, runCells } from '../intercept/cell-batch'\nimport { type ContextBase, createContextPool } from '../intercept/context'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport { pipelineDiagnostics } from './codes'\nimport type { ColumnLike, ResolvedColumn } from './columns'\nimport { allRows } from './indices'\nimport { runJob } from './slice'\nimport type { ColumnValues } from './types'\n\nkeepDiagnostics(pipelineDiagnostics, slotDiagnostics)\n\nexport interface ColumnCacheHost {\n readonly runtime: InterceptorRuntime\n readonly emit: Emit\n base(): ContextBase\n /**\n * The §6.6 status rule's counter (pipeline/status-rule.ts): `emit` increments it for a diagnostic\n * the rule counts. An entry whose read incremented it is flagged, and every later read of that\n * entry increments it again (its cells still hold the fallback values).\n */\n readonly flags?: { count: number }\n}\n\n/**\n * Internal member of the pipeline's run context (not on the public `RowModelContext`): a column's\n * formatted values as a time-sliced job (search, #40); `undefined` for an unknown column.\n */\nexport interface SlicedFormatting {\n readonly formattedSliced?: (\n id: string,\n ) => Generator<undefined, readonly string[] | undefined, undefined>\n}\n\nexport interface ColumnCache {\n /**\n * The values of `column` for every row of `data` (index = row index). An all-number column is a\n * `Float64Array` with `NaN` for `null` / `undefined`; any other column a plain array.\n */\n values(data: readonly unknown[], column: ResolvedColumn): ColumnValues\n /** `format` (or the default formatter) applied to {@link values}: what the user sees. */\n formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[]\n /**\n * {@link formatted} as a time-sliced job (§9.1, #40): the cells are formatted in batches of a few\n * milliseconds with a `yield` between them (one `column:batch` per batch, never per cell).\n */\n formattedSliced(\n data: readonly unknown[],\n column: ResolvedColumn,\n ): Generator<undefined, readonly string[], undefined>\n /**\n * Runs `body` as one `column:batch` over `rows` (pooled ctx, `where` = `columns[i].<field>`):\n * interceptors see one dispatch per column, never one per cell. `body` loops over the cells\n * with an inline `try/catch` and reports its own failures.\n */\n batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ): void\n}\n\ninterface Entry {\n values?: ColumnValues\n /** Numeric columns: 1 = `null`, 2 = `undefined` per row. Absent when there are none. */\n nullish?: Uint8Array | undefined\n formatted?: readonly string[]\n /** Reading `values` / `formatted` reported a diagnostic the status rule counts. */\n flaggedValues?: boolean\n flaggedFormatted?: boolean\n}\n\nconst NULL = 1\nconst UNDEFINED = 2\n\n/** The nullish kinds of each cached numeric column that has any (see {@link nullishOf}). */\nconst nullishKinds = new WeakMap<Float64Array, Uint8Array>()\n\n/**\n * Which rows of a cached all-number column (`values` from the column cache) hold `null` /\n * `undefined` (non-zero) rather than a real `NaN`: both are stored as `NaN`. `undefined` when no\n * row does. Lets search format such a column on the fly (`''` / `'NaN'`) instead of caching a\n * string per row (#27).\n */\nexport function nullishOf(values: Float64Array): Uint8Array | undefined {\n return nullishKinds.get(values)\n}\n\nfunction allStrings(values: readonly unknown[]): boolean {\n for (let i = 0; i < values.length; i++) if (typeof values[i] !== 'string') return false\n return true\n}\n\nconst typeName = (v: unknown) => (Array.isArray(v) ? 'array' : typeof v)\n\n/** Rows per batch of a sliced format (#40): small, so the slicer's clock check comes often. */\nconst STEP = 512\n\n/** Distinct strings {@link share} remembers per column: enough for categories, tags, flags. */\nconst SHARED = 1024\n\n/**\n * Repeated formatted strings of a column share one copy (§9.3 memory, #27): each formatted cell is\n * a fresh string, so a column of tags or categories would keep one per row. Only the first\n * {@link SHARED} distinct strings are remembered; once per column, after formatting.\n */\nfunction share(cells: string[]) {\n const seen = new Map<string, string>()\n for (let r = 0; r < cells.length; r++) {\n const s = cells[r] as string\n const hit = seen.get(s)\n if (hit !== undefined) cells[r] = hit\n else if (seen.size < SHARED) seen.set(s, s)\n }\n}\n\n/**\n * The lazy column cache (§9.1): a column is read only when a step asks for it, once per data\n * reference, in one `column:batch` per column (never per cell, §3.4). Entries live in a `WeakMap`\n * keyed by the data reference, then by the column definition, so new data or a changed column\n * releases them.\n */\nexport function createColumnCache(host: ColumnCacheHost): ColumnCache {\n const byData = new WeakMap<readonly unknown[], WeakMap<ColumnLike, Entry>>()\n const pool = createContextPool()\n const failures = new CellFailures()\n\n function entry(data: readonly unknown[], column: ResolvedColumn): Entry {\n let cols = byData.get(data)\n if (!cols) {\n cols = new WeakMap()\n byData.set(data, cols)\n }\n let e = cols.get(column.def)\n if (!e) {\n e = {}\n cols.set(column.def, e)\n }\n return e\n }\n\n /** One `column:batch` (pooled ctx); `where` = `columns[i].<field>`, never row data. */\n function batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ) {\n const ctx = pool.acquire(\n 'column:batch',\n { column: column.id, slot, rows },\n `columns[${column.index}].${field}`,\n host.base(),\n )\n try {\n void host.runtime.run('column:batch', ctx, (): undefined => {\n try {\n body()\n } finally {\n failures.reset()\n }\n })\n } finally {\n pool.release(ctx)\n }\n }\n\n function readValues(data: readonly unknown[], column: ResolvedColumn, e: Entry): ColumnValues {\n const n = data.length\n const rows = allRows(data)\n const out: unknown[] = new Array(n).fill(undefined)\n const field = column.path ? 'key' : 'accessor'\n batch(column, 'accessor', field, rows, () => {\n const path = column.path\n const accessor = column.accessor\n if (path) {\n let cur = 0\n const onError = (error: unknown) => failures.add(cur, error)\n runCells(\n rows,\n (r) => {\n cur = r\n return getValue(data[r], path, onError)\n },\n out,\n undefined,\n failures,\n )\n } else if (accessor) {\n runCells(rows, (r) => accessor(data[r]), out, undefined, failures)\n }\n failures.report(host.emit, column.index, column.id, 'accessor', undefined, field)\n })\n return toNumeric(out, e) ?? out\n }\n\n /** A `Float64Array` when every non-nullish value is a number (and there is one); else nothing. */\n function toNumeric(values: unknown[], e: Entry): Float64Array | undefined {\n let numbers = 0\n let nullish = 0\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') numbers++\n else if (v === null || v === undefined) nullish++\n else return undefined\n }\n if (numbers === 0) return undefined\n const out = new Float64Array(values.length)\n const kinds = nullish > 0 ? new Uint8Array(values.length) : undefined\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') out[i] = v\n else {\n out[i] = Number.NaN\n if (kinds) kinds[i] = v === null ? NULL : UNDEFINED\n }\n }\n e.nullish = kinds\n if (kinds) nullishKinds.set(out, kinds)\n return out\n }\n\n const flags = host.flags\n\n function values(data: readonly unknown[], column: ResolvedColumn): ColumnValues {\n const e = entry(data, column)\n if (e.values) {\n if (flags && e.flaggedValues) flags.count++\n return e.values\n }\n const before = flags?.count ?? 0\n e.values = readValues(data, column, e)\n if (flags && flags.count > before) e.flaggedValues = true\n return e.values\n }\n\n /**\n * Formats `column` once per data reference, in one `column:batch`; `sliced` (#40): in batches of\n * {@link STEP} rows with a `yield` between them, so the slicer can end the task on time (a slow\n * formatter, e.g. dates, is never one long task). Failures and slot-contract breaks are counted\n * over the whole column and reported once.\n */\n function* formatting(\n data: readonly unknown[],\n column: ResolvedColumn,\n sliced: boolean,\n ): Generator<undefined, readonly string[], undefined> {\n let e = entry(data, column)\n if (e.formatted) {\n if (flags && e.flaggedFormatted) flags.count++\n return e.formatted\n }\n const before = flags?.count ?? 0\n const vals = values(data, column)\n if (sliced) yield // reading the values is a task of its own\n const kinds = e.nullish\n // The raw value: nullish numbers read back as null / undefined, not NaN.\n const valueAt = (r: number): unknown => {\n const k = kinds?.[r]\n return k ? (k === NULL ? null : undefined) : vals[r]\n }\n const n = data.length\n const rows = allRows(data)\n const format = column.format\n // Job-local counts: a sliced column spans several batches.\n const failed = new CellFailures()\n const bad = (process.env.NODE_ENV !== \"production\") ? new CellFailures() : undefined\n const unrendered = new CellFailures()\n let firstBad: unknown\n let firstType = ''\n let cur = 0\n // A column with `cell` renders itself: its formatted value only feeds search.\n const onUnrenderable = column.hasCell\n ? undefined\n : (v: unknown) => {\n if (unrendered.count === 0) firstType = typeName(v)\n unrendered.add(cur, undefined)\n }\n const cell = format\n ? (r: number) => {\n const s = format(valueAt(r))\n if (typeof s === 'string') return s\n // Slot contract (dev): counted only on this non-string path; coerced either way.\n if ((process.env.NODE_ENV !== \"production\") && bad) {\n if (bad.count === 0) firstBad = s\n bad.add(r, undefined)\n }\n return defaultFormat(s)\n }\n : (r: number) => {\n cur = r\n return defaultFormat(valueAt(r), onUnrenderable)\n }\n // Values that are all strings already format to themselves: no copy (§9.3 memory).\n const strings = !format && !(vals instanceof Float64Array) && allStrings(vals)\n let out: string[] | undefined = strings ? undefined : new Array(n).fill('')\n for (let from = 0; from < n; from += STEP) {\n const part = sliced && !strings ? rows.subarray(from, from + STEP) : rows\n // An interceptor may skip a batch: its cells keep the slot fallback ('').\n batch(column, 'format', 'format', part, () => {\n if (strings) out = vals as string[]\n else runCells(part, cell, out as string[], '', failed)\n })\n if (part === rows || from + STEP >= n) break\n yield\n }\n out ??= new Array<string>(n).fill('')\n failed.report(host.emit, column.index, column.id, 'format')\n if ((process.env.NODE_ENV !== \"production\") && bad && bad.count > 0) {\n host.emit('DK_CALLBACK_BAD_RETURN', {\n slot: `columns[${column.index}].format`,\n column: column.id,\n expected: expectedReturn(formatSlot),\n received: describeReturn(firstBad),\n count: bad.count,\n rows: Array.from(bad.sample()),\n fallback: formatSlot.fallback,\n })\n }\n if (unrendered.count > 0) {\n host.emit('DK_UNRENDERABLE_VALUE', {\n column: column.id,\n index: column.index,\n count: unrendered.count,\n rows: Array.from(unrendered.sample()),\n valueType: firstType,\n })\n }\n if (out !== vals) share(out)\n // A synchronous read may have cached the column while a sliced job ran.\n e = entry(data, column)\n if (!e.formatted) {\n e.formatted = out\n if (flags && flags.count > before) e.flaggedFormatted = true\n }\n return e.formatted\n }\n\n function formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[] {\n return runJob(() => formatting(data, column, false))\n }\n\n return {\n values,\n formatted,\n formattedSliced: (data, column) => formatting(data, column, true),\n batch,\n }\n}\n"],"mappings":"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","names":["values"]}
|
|
1
|
+
{"version":3,"sources":["../../src/pipeline/column-cache.ts"],"sourcesContent":["import { describeReturn, expectedReturn, formatSlot, slotDiagnostics } from '../catalog/slots'\nimport { defaultFormat } from '../column/format'\nimport { getValue } from '../column/get-value'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { CellFailures, type CellSlot, runCells } from '../intercept/cell-batch'\nimport { type ContextBase, createContextPool } from '../intercept/context'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport { pipelineDiagnostics } from './codes'\nimport type { ColumnLike, ResolvedColumn } from './columns'\nimport { allRows } from './indices'\nimport { runJob } from './slice'\nimport type { ColumnValues } from './types'\n\nkeepDiagnostics(pipelineDiagnostics, slotDiagnostics)\n\nexport interface ColumnCacheHost {\n readonly runtime: InterceptorRuntime\n readonly emit: Emit\n base(): ContextBase\n /**\n * The §6.6 status rule's counter (pipeline/status-rule.ts): `emit` increments it for a diagnostic\n * the rule counts. An entry whose read incremented it is flagged, and every later read of that\n * entry increments it again (its cells still hold the fallback values).\n */\n readonly flags?: { count: number }\n}\n\n/**\n * Internal member of the pipeline's run context (not on the public `RowModelContext`): a column's\n * formatted values as a time-sliced job (search, #40); `undefined` for an unknown column.\n */\nexport interface SlicedFormatting {\n readonly formattedSliced?: (\n id: string,\n ) => Generator<undefined, readonly string[] | undefined, undefined>\n}\n\nexport interface ColumnCache {\n /**\n * The values of `column` for every row of `data` (index = row index). An all-number column is a\n * `Float64Array` with `NaN` for `null` / `undefined`; any other column a plain array.\n */\n values(data: readonly unknown[], column: ResolvedColumn): ColumnValues\n /** `format` (or the default formatter) applied to {@link values}: what the user sees. */\n formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[]\n /**\n * {@link formatted} as a time-sliced job (§9.1, #40): the cells are formatted in batches of a few\n * milliseconds with a `yield` between them (one `column:batch` per batch, never per cell).\n */\n formattedSliced(\n data: readonly unknown[],\n column: ResolvedColumn,\n ): Generator<undefined, readonly string[], undefined>\n /**\n * Runs `body` as one `column:batch` over `rows` (pooled ctx, `where` = `columns[i].<field>`):\n * interceptors see one dispatch per column, never one per cell. `body` loops over the cells\n * with an inline `try/catch` and reports its own failures.\n */\n batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ): void\n /** Drops every entry (`destroy()`, #98). */\n release(): void\n /** Development only, for tests (#98): an entry of `data` is cached. */\n holds?(data: readonly unknown[]): boolean\n}\n\ninterface Entry {\n values?: ColumnValues\n /** Numeric columns: 1 = `null`, 2 = `undefined` per row. Absent when there are none. */\n nullish?: Uint8Array | undefined\n formatted?: readonly string[]\n /** Reading `values` / `formatted` reported a diagnostic the status rule counts. */\n flaggedValues?: boolean\n flaggedFormatted?: boolean\n}\n\nconst NULL = 1\nconst UNDEFINED = 2\n\n/** The nullish kinds of each cached numeric column that has any (see {@link nullishOf}). */\nconst nullishKinds = new WeakMap<Float64Array, Uint8Array>()\n\n/**\n * Which rows of a cached all-number column (`values` from the column cache) hold `null` /\n * `undefined` (non-zero) rather than a real `NaN`: both are stored as `NaN`. `undefined` when no\n * row does. Lets search format such a column on the fly (`''` / `'NaN'`) instead of caching a\n * string per row (#27).\n */\nexport function nullishOf(values: Float64Array): Uint8Array | undefined {\n return nullishKinds.get(values)\n}\n\nfunction allStrings(values: readonly unknown[]): boolean {\n for (let i = 0; i < values.length; i++) if (typeof values[i] !== 'string') return false\n return true\n}\n\nconst typeName = (v: unknown) => (Array.isArray(v) ? 'array' : typeof v)\n\n/** Rows per batch of a sliced format (#40): small, so the slicer's clock check comes often. */\nconst STEP = 512\n\n/** Distinct strings {@link share} remembers per column: enough for categories, tags, flags. */\nconst SHARED = 1024\n\n/**\n * Repeated formatted strings of a column share one copy (§9.3 memory, #27): each formatted cell is\n * a fresh string, so a column of tags or categories would keep one per row. Only the first\n * {@link SHARED} distinct strings are remembered; once per column, after formatting.\n */\nfunction share(cells: string[]) {\n const seen = new Map<string, string>()\n for (let r = 0; r < cells.length; r++) {\n const s = cells[r] as string\n const hit = seen.get(s)\n if (hit !== undefined) cells[r] = hit\n else if (seen.size < SHARED) seen.set(s, s)\n }\n}\n\n/**\n * The lazy column cache (§9.1): a column is read only when a step asks for it, once per data\n * reference, in one `column:batch` per column (never per cell, §3.4). Entries live in a `WeakMap`\n * keyed by the data reference, then by the column definition, so new data or a changed column\n * releases them.\n */\nexport function createColumnCache(host: ColumnCacheHost): ColumnCache {\n let byData = new WeakMap<readonly unknown[], WeakMap<ColumnLike, Entry>>()\n const pool = createContextPool()\n const failures = new CellFailures()\n\n function entry(data: readonly unknown[], column: ResolvedColumn): Entry {\n let cols = byData.get(data)\n if (!cols) {\n cols = new WeakMap()\n byData.set(data, cols)\n }\n let e = cols.get(column.def)\n if (!e) {\n e = {}\n cols.set(column.def, e)\n }\n return e\n }\n\n /** One `column:batch` (pooled ctx); `where` = `columns[i].<field>`, never row data. */\n function batch(\n column: ResolvedColumn,\n slot: CellSlot,\n field: string,\n rows: Uint32Array,\n body: () => void,\n ) {\n const ctx = pool.acquire(\n 'column:batch',\n { column: column.id, slot, rows },\n `columns[${column.index}].${field}`,\n host.base(),\n )\n try {\n void host.runtime.run('column:batch', ctx, (): undefined => {\n try {\n body()\n } finally {\n failures.reset()\n }\n })\n } finally {\n pool.release(ctx)\n }\n }\n\n function readValues(data: readonly unknown[], column: ResolvedColumn, e: Entry): ColumnValues {\n const n = data.length\n const rows = allRows(data)\n const out: unknown[] = new Array(n).fill(undefined)\n const field = column.path ? 'key' : 'accessor'\n batch(column, 'accessor', field, rows, () => {\n const path = column.path\n const accessor = column.accessor\n if (path) {\n let cur = 0\n const onError = (error: unknown) => failures.add(cur, error)\n runCells(\n rows,\n (r) => {\n cur = r\n return getValue(data[r], path, onError)\n },\n out,\n undefined,\n failures,\n )\n } else if (accessor) {\n runCells(rows, (r) => accessor(data[r]), out, undefined, failures)\n }\n failures.report(host.emit, column.index, column.id, 'accessor', undefined, field)\n })\n return toNumeric(out, e) ?? out\n }\n\n /** A `Float64Array` when every non-nullish value is a number (and there is one); else nothing. */\n function toNumeric(values: unknown[], e: Entry): Float64Array | undefined {\n let numbers = 0\n let nullish = 0\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') numbers++\n else if (v === null || v === undefined) nullish++\n else return undefined\n }\n if (numbers === 0) return undefined\n const out = new Float64Array(values.length)\n const kinds = nullish > 0 ? new Uint8Array(values.length) : undefined\n for (let i = 0; i < values.length; i++) {\n const v = values[i]\n if (typeof v === 'number') out[i] = v\n else {\n out[i] = Number.NaN\n if (kinds) kinds[i] = v === null ? NULL : UNDEFINED\n }\n }\n e.nullish = kinds\n if (kinds) nullishKinds.set(out, kinds)\n return out\n }\n\n const flags = host.flags\n\n function values(data: readonly unknown[], column: ResolvedColumn): ColumnValues {\n const e = entry(data, column)\n if (e.values) {\n if (flags && e.flaggedValues) flags.count++\n return e.values\n }\n const before = flags?.count ?? 0\n e.values = readValues(data, column, e)\n if (flags && flags.count > before) e.flaggedValues = true\n return e.values\n }\n\n /**\n * Formats `column` once per data reference, in one `column:batch`; `sliced` (#40): in batches of\n * {@link STEP} rows with a `yield` between them, so the slicer can end the task on time (a slow\n * formatter, e.g. dates, is never one long task). Failures and slot-contract breaks are counted\n * over the whole column and reported once.\n */\n function* formatting(\n data: readonly unknown[],\n column: ResolvedColumn,\n sliced: boolean,\n ): Generator<undefined, readonly string[], undefined> {\n let e = entry(data, column)\n if (e.formatted) {\n if (flags && e.flaggedFormatted) flags.count++\n return e.formatted\n }\n const before = flags?.count ?? 0\n const vals = values(data, column)\n if (sliced) yield // reading the values is a task of its own\n const kinds = e.nullish\n // The raw value: nullish numbers read back as null / undefined, not NaN.\n const valueAt = (r: number): unknown => {\n const k = kinds?.[r]\n return k ? (k === NULL ? null : undefined) : vals[r]\n }\n const n = data.length\n const rows = allRows(data)\n const format = column.format\n // Job-local counts: a sliced column spans several batches.\n const failed = new CellFailures()\n const bad = (process.env.NODE_ENV !== \"production\") ? new CellFailures() : undefined\n const unrendered = new CellFailures()\n let firstBad: unknown\n let firstType = ''\n let cur = 0\n // A column with `cell` renders itself: its formatted value only feeds search.\n const onUnrenderable = column.hasCell\n ? undefined\n : (v: unknown) => {\n if (unrendered.count === 0) firstType = typeName(v)\n unrendered.add(cur, undefined)\n }\n const cell = format\n ? (r: number) => {\n const s = format(valueAt(r))\n if (typeof s === 'string') return s\n // Slot contract (dev): counted only on this non-string path; coerced either way.\n if ((process.env.NODE_ENV !== \"production\") && bad) {\n if (bad.count === 0) firstBad = s\n bad.add(r, undefined)\n }\n return defaultFormat(s)\n }\n : (r: number) => {\n cur = r\n return defaultFormat(valueAt(r), onUnrenderable)\n }\n // Values that are all strings already format to themselves: no copy (§9.3 memory).\n const strings = !format && !(vals instanceof Float64Array) && allStrings(vals)\n let out: string[] | undefined = strings ? undefined : new Array(n).fill('')\n for (let from = 0; from < n; from += STEP) {\n const part = sliced && !strings ? rows.subarray(from, from + STEP) : rows\n // An interceptor may skip a batch: its cells keep the slot fallback ('').\n batch(column, 'format', 'format', part, () => {\n if (strings) out = vals as string[]\n else runCells(part, cell, out as string[], '', failed)\n })\n if (part === rows || from + STEP >= n) break\n yield\n }\n out ??= new Array<string>(n).fill('')\n failed.report(host.emit, column.index, column.id, 'format')\n if ((process.env.NODE_ENV !== \"production\") && bad && bad.count > 0) {\n host.emit('DK_CALLBACK_BAD_RETURN', {\n slot: `columns[${column.index}].format`,\n column: column.id,\n expected: expectedReturn(formatSlot),\n received: describeReturn(firstBad),\n count: bad.count,\n rows: Array.from(bad.sample()),\n fallback: formatSlot.fallback,\n })\n }\n if (unrendered.count > 0) {\n host.emit('DK_UNRENDERABLE_VALUE', {\n column: column.id,\n index: column.index,\n count: unrendered.count,\n rows: Array.from(unrendered.sample()),\n valueType: firstType,\n })\n }\n if (out !== vals) share(out)\n // A synchronous read may have cached the column while a sliced job ran.\n e = entry(data, column)\n if (!e.formatted) {\n e.formatted = out\n if (flags && flags.count > before) e.flaggedFormatted = true\n }\n return e.formatted\n }\n\n function formatted(data: readonly unknown[], column: ResolvedColumn): readonly string[] {\n return runJob(() => formatting(data, column, false))\n }\n\n return {\n values,\n formatted,\n formattedSliced: (data, column) => formatting(data, column, true),\n batch,\n release: () => {\n byData = new WeakMap()\n },\n ...((process.env.NODE_ENV !== \"production\") ? { holds: (data: readonly unknown[]) => byData.has(data) } : {}),\n }\n}\n"],"mappings":"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","names":["values"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/pipeline/release.ts
|
|
21
|
+
var release_exports = {};
|
|
22
|
+
__export(release_exports, {
|
|
23
|
+
PROBE: () => PROBE,
|
|
24
|
+
RELEASE: () => RELEASE
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(release_exports);
|
|
27
|
+
var RELEASE = /* @__PURE__ */ Symbol();
|
|
28
|
+
var PROBE = /* @__PURE__ */ Symbol();
|
|
29
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
30
|
+
0 && (module.exports = {
|
|
31
|
+
PROBE,
|
|
32
|
+
RELEASE
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=release.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pipeline/release.ts"],"sourcesContent":["/**\n * Internal (#98), not exported: a feature's `[RELEASE]()` drops its per-instance caches. The table\n * calls it on `destroy()`, so a destroyed table still referenced (a ref, a store, StrictMode's\n * double mount) does not keep them.\n */\nexport const RELEASE = /* @__PURE__ */ Symbol()\n\n/** Development only, for tests (#98): `[PROBE]()` returns what a feature / the table still holds. */\nexport const PROBE = /* @__PURE__ */ Symbol()\n\nexport interface Releasable {\n [RELEASE]?: () => void\n [PROBE]?: () => Record<string, unknown>\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,IAAM,UAA0B,uBAAO;AAGvC,IAAM,QAAwB,uBAAO;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pipeline/release.ts"],"sourcesContent":["/**\n * Internal (#98), not exported: a feature's `[RELEASE]()` drops its per-instance caches. The table\n * calls it on `destroy()`, so a destroyed table still referenced (a ref, a store, StrictMode's\n * double mount) does not keep them.\n */\nexport const RELEASE = /* @__PURE__ */ Symbol()\n\n/** Development only, for tests (#98): `[PROBE]()` returns what a feature / the table still holds. */\nexport const PROBE = /* @__PURE__ */ Symbol()\n\nexport interface Releasable {\n [RELEASE]?: () => void\n [PROBE]?: () => Record<string, unknown>\n}\n"],"mappings":";AAKO,IAAM,UAA0B,uBAAO;AAGvC,IAAM,QAAwB,uBAAO;","names":[]}
|
package/dist/pipeline/runner.cjs
CHANGED
|
@@ -333,7 +333,21 @@ function createPipeline(host) {
|
|
|
333
333
|
current: () => model,
|
|
334
334
|
lastRun: () => lastRun,
|
|
335
335
|
replayContext,
|
|
336
|
-
dispose: () =>
|
|
336
|
+
dispose: () => {
|
|
337
|
+
slicer?.cancel();
|
|
338
|
+
cache?.release();
|
|
339
|
+
memo.clear();
|
|
340
|
+
keys = void 0;
|
|
341
|
+
last = void 0;
|
|
342
|
+
},
|
|
343
|
+
...process.env.NODE_ENV !== "production" ? {
|
|
344
|
+
probe: () => ({
|
|
345
|
+
memo: memo.size,
|
|
346
|
+
keys: keys !== void 0,
|
|
347
|
+
last: last !== void 0,
|
|
348
|
+
cached: cache?.holds?.(host.getData()) === true
|
|
349
|
+
})
|
|
350
|
+
} : {}
|
|
337
351
|
};
|
|
338
352
|
}
|
|
339
353
|
// Annotate the CommonJS export names for ESM import in node:
|