@datakable/core 1.0.1 → 1.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/README.md +4 -2
- package/dist/catalog/codes.cjs +12 -8
- package/dist/catalog/codes.cjs.map +1 -1
- package/dist/catalog/codes.js +12 -8
- package/dist/catalog/codes.js.map +1 -1
- package/dist/catalog/slots.cjs +10 -6
- package/dist/catalog/slots.cjs.map +1 -1
- package/dist/catalog/slots.js +10 -6
- package/dist/catalog/slots.js.map +1 -1
- package/dist/{codes-OsjAR3Wz.d.cts → codes-B9bCK73-.d.cts} +30 -4
- package/dist/{codes-OsjAR3Wz.d.ts → codes-B9bCK73-.d.ts} +30 -4
- package/dist/column/column-def.cjs.map +1 -1
- package/dist/column/get-value.cjs +6 -1
- package/dist/column/get-value.cjs.map +1 -1
- package/dist/column/get-value.js +6 -1
- package/dist/column/get-value.js.map +1 -1
- package/dist/column-filters.d.cts +2 -2
- package/dist/column-filters.d.ts +2 -2
- package/dist/diagnostics/build.cjs +4 -1
- package/dist/diagnostics/build.cjs.map +1 -1
- package/dist/diagnostics/build.js +4 -1
- package/dist/diagnostics/build.js.map +1 -1
- package/dist/diagnostics/catalog.cjs +11 -5
- package/dist/diagnostics/catalog.cjs.map +1 -1
- package/dist/diagnostics/catalog.js +10 -5
- package/dist/diagnostics/catalog.js.map +1 -1
- package/dist/diagnostics/codes.cjs +12 -8
- package/dist/diagnostics/codes.cjs.map +1 -1
- package/dist/diagnostics/codes.js +12 -8
- package/dist/diagnostics/codes.js.map +1 -1
- package/dist/diagnostics/console.cjs +13 -6
- package/dist/diagnostics/console.cjs.map +1 -1
- package/dist/diagnostics/console.js +13 -6
- package/dist/diagnostics/console.js.map +1 -1
- package/dist/diagnostics/env.cjs +1 -0
- package/dist/diagnostics/env.cjs.map +1 -1
- package/dist/diagnostics/env.js +1 -0
- package/dist/diagnostics/env.js.map +1 -1
- package/dist/diagnostics/explain-text.cjs +2 -1
- package/dist/diagnostics/explain-text.cjs.map +1 -1
- package/dist/diagnostics/explain-text.js +2 -1
- package/dist/diagnostics/explain-text.js.map +1 -1
- package/dist/diagnostics/explain.cjs +3 -2
- package/dist/diagnostics/explain.cjs.map +1 -1
- package/dist/diagnostics/explain.js +3 -2
- package/dist/diagnostics/explain.js.map +1 -1
- package/dist/diagnostics/hub.cjs +5 -4
- package/dist/diagnostics/hub.cjs.map +1 -1
- package/dist/diagnostics/hub.js +5 -4
- package/dist/diagnostics/hub.js.map +1 -1
- package/dist/diagnostics/process-guard.cjs +26 -0
- package/dist/diagnostics/process-guard.cjs.map +1 -0
- package/dist/diagnostics/process-guard.js +9 -0
- package/dist/diagnostics/process-guard.js.map +1 -0
- package/dist/diagnostics/prose.cjs +120 -0
- package/dist/diagnostics/prose.cjs.map +1 -0
- package/dist/diagnostics/prose.js +93 -0
- package/dist/diagnostics/prose.js.map +1 -0
- package/dist/diagnostics/redact.cjs +7 -4
- package/dist/diagnostics/redact.cjs.map +1 -1
- package/dist/diagnostics/redact.js +6 -4
- package/dist/diagnostics/redact.js.map +1 -1
- package/dist/diagnostics/trace-store.cjs +6 -0
- package/dist/diagnostics/trace-store.cjs.map +1 -1
- package/dist/diagnostics/trace-store.js +4 -0
- package/dist/diagnostics/trace-store.js.map +1 -1
- package/dist/diagnostics/tracer.cjs +74 -13
- package/dist/diagnostics/tracer.cjs.map +1 -1
- package/dist/diagnostics/tracer.js +75 -14
- package/dist/diagnostics/tracer.js.map +1 -1
- package/dist/diagnostics/types.cjs.map +1 -1
- package/dist/{explain-vkD2cewE.d.cts → explain-Q8Cg0cZy.d.cts} +13 -4
- package/dist/{explain-Dn99IQt0.d.ts → explain-i0OATi_W.d.ts} +13 -4
- package/dist/feature/codes.cjs +1 -0
- package/dist/feature/codes.cjs.map +1 -1
- package/dist/feature/codes.js +1 -0
- package/dist/feature/codes.js.map +1 -1
- package/dist/feature/install.cjs +4 -3
- package/dist/feature/install.cjs.map +1 -1
- package/dist/feature/install.js +4 -3
- package/dist/feature/install.js.map +1 -1
- package/dist/features/column-filters/codes.cjs +1 -0
- package/dist/features/column-filters/codes.cjs.map +1 -1
- package/dist/features/column-filters/codes.js +1 -0
- package/dist/features/column-filters/codes.js.map +1 -1
- package/dist/features/column-filters/column-filters.cjs +4 -2
- package/dist/features/column-filters/column-filters.cjs.map +1 -1
- package/dist/features/column-filters/column-filters.js +4 -2
- package/dist/features/column-filters/column-filters.js.map +1 -1
- package/dist/features/column-filters/match.cjs +5 -5
- package/dist/features/column-filters/match.cjs.map +1 -1
- package/dist/features/column-filters/match.js +5 -5
- package/dist/features/column-filters/match.js.map +1 -1
- package/dist/features/pagination/codes.cjs +1 -0
- package/dist/features/pagination/codes.cjs.map +1 -1
- package/dist/features/pagination/codes.js +1 -0
- package/dist/features/pagination/codes.js.map +1 -1
- package/dist/features/pagination/pagination.cjs +16 -13
- package/dist/features/pagination/pagination.cjs.map +1 -1
- package/dist/features/pagination/pagination.js +16 -13
- package/dist/features/pagination/pagination.js.map +1 -1
- package/dist/features/search/codes.cjs +1 -0
- package/dist/features/search/codes.cjs.map +1 -1
- package/dist/features/search/codes.js +1 -0
- package/dist/features/search/codes.js.map +1 -1
- package/dist/features/search/search-index.cjs +68 -15
- package/dist/features/search/search-index.cjs.map +1 -1
- package/dist/features/search/search-index.js +68 -15
- package/dist/features/search/search-index.js.map +1 -1
- package/dist/features/search/search.cjs +41 -9
- package/dist/features/search/search.cjs.map +1 -1
- package/dist/features/search/search.js +41 -9
- package/dist/features/search/search.js.map +1 -1
- package/dist/features/search/skipped-codes.cjs +45 -0
- package/dist/features/search/skipped-codes.cjs.map +1 -0
- package/dist/features/search/skipped-codes.js +20 -0
- package/dist/features/search/skipped-codes.js.map +1 -0
- package/dist/features/sorting/codes.cjs +1 -0
- package/dist/features/sorting/codes.cjs.map +1 -1
- package/dist/features/sorting/codes.js +1 -0
- package/dist/features/sorting/codes.js.map +1 -1
- package/dist/features/sorting/sorting.cjs +16 -20
- package/dist/features/sorting/sorting.cjs.map +1 -1
- package/dist/features/sorting/sorting.js +16 -20
- package/dist/features/sorting/sorting.js.map +1 -1
- package/dist/features/sorting/strategy.cjs +4 -4
- package/dist/features/sorting/strategy.cjs.map +1 -1
- package/dist/features/sorting/strategy.js +4 -4
- package/dist/features/sorting/strategy.js.map +1 -1
- package/dist/features/validity/codes.cjs +1 -0
- package/dist/features/validity/codes.cjs.map +1 -1
- package/dist/features/validity/codes.js +1 -0
- package/dist/features/validity/codes.js.map +1 -1
- package/dist/features/validity/validity.cjs +3 -3
- package/dist/features/validity/validity.cjs.map +1 -1
- package/dist/features/validity/validity.js +3 -3
- package/dist/features/validity/validity.js.map +1 -1
- package/dist/index.d.cts +13 -10
- package/dist/index.d.ts +13 -10
- package/dist/ingest/data-check-codes.cjs +4 -2
- package/dist/ingest/data-check-codes.cjs.map +1 -1
- package/dist/ingest/data-check-codes.js +4 -2
- package/dist/ingest/data-check-codes.js.map +1 -1
- package/dist/ingest/data-checks.cjs +1 -1
- package/dist/ingest/data-checks.cjs.map +1 -1
- package/dist/ingest/data-checks.js +1 -1
- package/dist/ingest/data-checks.js.map +1 -1
- package/dist/ingest/row-key-codes.cjs +1 -0
- package/dist/ingest/row-key-codes.cjs.map +1 -1
- package/dist/ingest/row-key-codes.js +1 -0
- package/dist/ingest/row-key-codes.js.map +1 -1
- package/dist/ingest/row-key.cjs +23 -20
- package/dist/ingest/row-key.cjs.map +1 -1
- package/dist/ingest/row-key.js +23 -20
- package/dist/ingest/row-key.js.map +1 -1
- package/dist/ingest/schema-codes.cjs +1 -0
- package/dist/ingest/schema-codes.cjs.map +1 -1
- package/dist/ingest/schema-codes.js +1 -0
- package/dist/ingest/schema-codes.js.map +1 -1
- package/dist/ingest/unique-fields.cjs +2 -2
- package/dist/ingest/unique-fields.cjs.map +1 -1
- package/dist/ingest/unique-fields.js +2 -2
- package/dist/ingest/unique-fields.js.map +1 -1
- package/dist/intercept/boundary.cjs +4 -2
- package/dist/intercept/boundary.cjs.map +1 -1
- package/dist/intercept/boundary.js +4 -2
- package/dist/intercept/boundary.js.map +1 -1
- package/dist/intercept/cell-batch.cjs +4 -4
- package/dist/intercept/cell-batch.cjs.map +1 -1
- package/dist/intercept/cell-batch.js +4 -4
- package/dist/intercept/cell-batch.js.map +1 -1
- package/dist/intercept/codes.cjs +1 -0
- package/dist/intercept/codes.cjs.map +1 -1
- package/dist/intercept/codes.js +1 -0
- package/dist/intercept/codes.js.map +1 -1
- package/dist/intercept/fallbacks.cjs +12 -20
- package/dist/intercept/fallbacks.cjs.map +1 -1
- package/dist/intercept/fallbacks.js +12 -20
- package/dist/intercept/fallbacks.js.map +1 -1
- package/dist/intercept/runtime.cjs +7 -9
- package/dist/intercept/runtime.cjs.map +1 -1
- package/dist/intercept/runtime.js +7 -9
- package/dist/intercept/runtime.js.map +1 -1
- package/dist/messages/index.cjs +4 -1
- package/dist/messages/index.cjs.map +1 -1
- package/dist/messages/index.js +4 -1
- package/dist/messages/index.js.map +1 -1
- package/dist/pagination.d.cts +2 -2
- package/dist/pagination.d.ts +2 -2
- package/dist/pipeline/codes.cjs +1 -0
- package/dist/pipeline/codes.cjs.map +1 -1
- package/dist/pipeline/codes.js +1 -0
- package/dist/pipeline/codes.js.map +1 -1
- package/dist/pipeline/column-cache.cjs +94 -76
- package/dist/pipeline/column-cache.cjs.map +1 -1
- package/dist/pipeline/column-cache.js +92 -75
- package/dist/pipeline/column-cache.js.map +1 -1
- package/dist/pipeline/dev-checks.cjs +1 -0
- package/dist/pipeline/dev-checks.cjs.map +1 -1
- package/dist/pipeline/dev-checks.js +1 -0
- package/dist/pipeline/dev-checks.js.map +1 -1
- package/dist/pipeline/env-codes.cjs +3 -2
- package/dist/pipeline/env-codes.cjs.map +1 -1
- package/dist/pipeline/env-codes.js +3 -2
- package/dist/pipeline/env-codes.js.map +1 -1
- package/dist/pipeline/perf-codes.cjs +1 -0
- package/dist/pipeline/perf-codes.cjs.map +1 -1
- package/dist/pipeline/perf-codes.js +1 -0
- package/dist/pipeline/perf-codes.js.map +1 -1
- package/dist/pipeline/runner.cjs +17 -9
- package/dist/pipeline/runner.cjs.map +1 -1
- package/dist/pipeline/runner.js +17 -9
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/pipeline/slice.cjs +3 -1
- package/dist/pipeline/slice.cjs.map +1 -1
- package/dist/pipeline/slice.js +3 -1
- package/dist/pipeline/slice.js.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/{search-async-entry-v5JdEk5Y.d.cts → search-async-entry-CuLYPfY_.d.cts} +2 -2
- package/dist/{search-async-entry-DGSw2pDL.d.ts → search-async-entry-Cx7xpuVZ.d.ts} +2 -2
- package/dist/search-async.d.cts +3 -3
- package/dist/search-async.d.ts +3 -3
- package/dist/search.d.cts +8 -6
- package/dist/search.d.ts +8 -6
- package/dist/server.d.cts +3 -3
- package/dist/server.d.ts +3 -3
- package/dist/sorting.d.cts +2 -2
- package/dist/sorting.d.ts +2 -2
- package/dist/source/codes.cjs +1 -0
- package/dist/source/codes.cjs.map +1 -1
- package/dist/source/codes.js +1 -0
- package/dist/source/codes.js.map +1 -1
- package/dist/source/fetch-codes.cjs +1 -0
- package/dist/source/fetch-codes.cjs.map +1 -1
- package/dist/source/fetch-codes.js +1 -0
- package/dist/source/fetch-codes.js.map +1 -1
- package/dist/table/codes.cjs +1 -0
- package/dist/table/codes.cjs.map +1 -1
- package/dist/table/codes.js +1 -0
- package/dist/table/codes.js.map +1 -1
- package/dist/table/create-table.cjs +39 -9
- package/dist/table/create-table.cjs.map +1 -1
- package/dist/table/create-table.js +39 -9
- package/dist/table/create-table.js.map +1 -1
- package/dist/table/data-mutation.cjs +1 -0
- package/dist/table/data-mutation.cjs.map +1 -1
- package/dist/table/data-mutation.js +1 -0
- package/dist/table/data-mutation.js.map +1 -1
- package/dist/table/internals.cjs.map +1 -1
- package/dist/table/internals.js.map +1 -1
- package/dist/table/ssr-codes.cjs +1 -0
- package/dist/table/ssr-codes.cjs.map +1 -1
- package/dist/table/ssr-codes.js +1 -0
- package/dist/table/ssr-codes.js.map +1 -1
- package/dist/table/ssr.cjs +4 -1
- package/dist/table/ssr.cjs.map +1 -1
- package/dist/table/ssr.js +4 -1
- package/dist/table/ssr.js.map +1 -1
- package/dist/table/state-feedback.cjs +19 -0
- package/dist/table/state-feedback.cjs.map +1 -1
- package/dist/table/state-feedback.js +19 -0
- package/dist/table/state-feedback.js.map +1 -1
- package/dist/table/types.cjs.map +1 -1
- package/dist/table/types.js.map +1 -1
- package/dist/validity.d.cts +2 -2
- package/dist/validity.d.ts +2 -2
- 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/dist/versioning/codes.cjs +18 -0
- package/dist/versioning/codes.cjs.map +1 -1
- package/dist/versioning/codes.js +18 -0
- package/dist/versioning/codes.js.map +1 -1
- package/dist/versioning/registry.cjs +4 -2
- package/dist/versioning/registry.cjs.map +1 -1
- package/dist/versioning/registry.js +4 -2
- package/dist/versioning/registry.js.map +1 -1
- package/package.json +39 -3
package/dist/feature/install.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/feature/install.ts
|
|
2
2
|
import { throwConfigError } from "../diagnostics/build.js";
|
|
3
3
|
import { keepDiagnostics } from "../diagnostics/catalog.js";
|
|
4
|
+
import "../diagnostics/process-guard.js";
|
|
4
5
|
import { errorName } from "../intercept/classify.js";
|
|
5
6
|
import { featureDiagnostics } from "./codes.js";
|
|
6
7
|
keepDiagnostics(featureDiagnostics);
|
|
@@ -20,7 +21,7 @@ function initialSlices(features, given, emit) {
|
|
|
20
21
|
out[f.name] = f.initialState();
|
|
21
22
|
} catch (e) {
|
|
22
23
|
out[f.name] = void 0;
|
|
23
|
-
reportFailure(emit, f.name, "initialState", "
|
|
24
|
+
reportFailure(emit, f.name, "initialState", "slice-undefined", e);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
return given ? Object.assign(out, given) : out;
|
|
@@ -43,7 +44,7 @@ function installApis(features, host) {
|
|
|
43
44
|
try {
|
|
44
45
|
api = f.createApi(ctx);
|
|
45
46
|
} catch (e) {
|
|
46
|
-
reportFailure(host.emit, name, "createApi", "
|
|
47
|
+
reportFailure(host.emit, name, "createApi", "api-not-installed", e);
|
|
47
48
|
failed.push(name);
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
@@ -95,7 +96,7 @@ function missingApi(failed, host) {
|
|
|
95
96
|
feature,
|
|
96
97
|
hook: "createApi",
|
|
97
98
|
method: key,
|
|
98
|
-
fallback:
|
|
99
|
+
fallback: `${failed.length === 1 ? "api" : "apis"}-missing|${key}`
|
|
99
100
|
})
|
|
100
101
|
);
|
|
101
102
|
return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/feature/install.ts"],"sourcesContent":["import { throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport type { Emit } from '../diagnostics/types'\nimport { errorName } from '../intercept/classify'\nimport type { Table, TableState } from '../table/types'\nimport { featureDiagnostics } from './codes'\nimport type { AnyFeature, FeatureContext, SliceUpdater } from './types'\n\nkeepDiagnostics(featureDiagnostics)\n\n/** What `createTable` gives the installer: the store operations a feature context needs. */\nexport interface FeatureHost {\n readonly table: Table<unknown, TableState>\n /** The table name, for config errors thrown before the first emit. */\n readonly tableName: string\n readonly emit: Emit\n getSlice(name: string): unknown\n /** `state:dispatch` of one slice (where = the feature name); notifies unless inside an API call. */\n setSlice(name: string, updater: SliceUpdater<unknown>): void\n /**\n * Runs an API method: no-op + `DK_TABLE_DESTROYED` after destroy (returning `destroyed?.()`),\n * trigger `api:<method>`, listeners notified once when the outermost call returns.\n */\n call(method: string, fn: () => unknown, destroyed?: () => unknown): unknown\n}\n\nfunction reportFailure(\n emit: Emit,\n feature: string,\n hook: 'initialState' | 'createApi',\n fallback: string,\n e: unknown,\n) {\n const name = errorName(e)\n emit(\n 'DK_FEATURE_FAILED',\n { feature, hook, fallback, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n}\n\n/**\n * The starting snapshot: each feature's `initialState()` under its name, then `given`\n * (`options.initialState`) per slice on top. `initialState()` is not called for a given slice.\n */\nexport function initialSlices(\n features: readonly AnyFeature[],\n given: object | undefined,\n emit: Emit,\n): TableState {\n const out: TableState = {}\n for (const f of features) {\n if (given && Object.hasOwn(given, f.name)) continue\n try {\n out[f.name] = f.initialState()\n } catch (e) {\n out[f.name] = undefined\n reportFailure(emit, f.name, 'initialState', '
|
|
1
|
+
{"version":3,"sources":["../../src/feature/install.ts"],"sourcesContent":["import { throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { errorName } from '../intercept/classify'\nimport type { Table, TableState } from '../table/types'\nimport { featureDiagnostics } from './codes'\nimport type { AnyFeature, FeatureContext, SliceUpdater } from './types'\n\nkeepDiagnostics(featureDiagnostics)\n\n/** What `createTable` gives the installer: the store operations a feature context needs. */\nexport interface FeatureHost {\n readonly table: Table<unknown, TableState>\n /** The table name, for config errors thrown before the first emit. */\n readonly tableName: string\n readonly emit: Emit\n getSlice(name: string): unknown\n /** `state:dispatch` of one slice (where = the feature name); notifies unless inside an API call. */\n setSlice(name: string, updater: SliceUpdater<unknown>): void\n /**\n * Runs an API method: no-op + `DK_TABLE_DESTROYED` after destroy (returning `destroyed?.()`),\n * trigger `api:<method>`, listeners notified once when the outermost call returns.\n */\n call(method: string, fn: () => unknown, destroyed?: () => unknown): unknown\n}\n\nfunction reportFailure(\n emit: Emit,\n feature: string,\n hook: 'initialState' | 'createApi',\n fallback: string,\n e: unknown,\n) {\n const name = errorName(e)\n emit(\n 'DK_FEATURE_FAILED',\n { feature, hook, fallback, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n}\n\n/**\n * The starting snapshot: each feature's `initialState()` under its name, then `given`\n * (`options.initialState`) per slice on top. `initialState()` is not called for a given slice.\n */\nexport function initialSlices(\n features: readonly AnyFeature[],\n given: object | undefined,\n emit: Emit,\n): TableState {\n const out: TableState = {}\n for (const f of features) {\n if (given && Object.hasOwn(given, f.name)) continue\n try {\n out[f.name] = f.initialState()\n } catch (e) {\n out[f.name] = undefined\n reportFailure(emit, f.name, 'initialState', 'slice-undefined', e)\n }\n }\n return given ? Object.assign(out, given) : out\n}\n\n/**\n * Creates every feature API (in the order the features were listed) and merges it onto the table.\n * Throws `DatakableConfigError` `DK_FEATURE_API_CONFLICT` when a member name is already taken by a\n * core table method or by an earlier feature. Methods are wrapped once here (cold path).\n */\nexport function installApis(features: readonly AnyFeature[], host: FeatureHost): void {\n const target = host.table as unknown as Record<string, unknown>\n const owners = new Map<string, string | null>()\n for (const k of Object.keys(target)) owners.set(k, null)\n /** Features whose `createApi()` threw: their methods are missing from the table. */\n const failed: string[] = []\n\n for (const f of features) {\n const name = f.name\n const ctx: FeatureContext<unknown> = {\n name,\n getState: () => host.getSlice(name),\n setState: (updater) => host.setSlice(name, updater),\n table: host.table,\n emit: host.emit,\n }\n let api: unknown\n try {\n api = f.createApi(ctx)\n } catch (e) {\n reportFailure(host.emit, name, 'createApi', 'api-not-installed', e)\n failed.push(name)\n continue\n }\n if (api === null || typeof api !== 'object') continue\n const members = api as Record<string, unknown>\n const keys = Object.keys(members)\n for (const method of keys) {\n if (owners.has(method)) {\n throwConfigError(\n 'DK_FEATURE_API_CONFLICT',\n { method, feature: name, owner: owners.get(method) ?? null },\n { table: host.tableName },\n )\n }\n }\n for (const method of keys) {\n owners.set(method, name)\n const member = members[method]\n const destroyed = f.destroyedResult?.[method]\n target[method] =\n typeof member === 'function'\n ? (...args: unknown[]) => host.call(method, () => member.apply(members, args), destroyed)\n : member\n }\n }\n if ((process.env.NODE_ENV !== \"production\") && failed.length > 0) Object.setPrototypeOf(target, missingApi(failed, host))\n}\n\n/**\n * Names generic code probes on any object (await, JSON, React, test runners, inspectors): never a\n * feature method, so never stubbed.\n */\nconst PROBES = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'asymmetricMatch',\n 'nodeType',\n 'tagName',\n])\nconst isProbe = (k: string) => PROBES.has(k) || /^[_$@]/.test(k)\n\n/**\n * Dev (R4): the prototype of a table where a feature's `createApi()` threw. The types still promise\n * that feature's methods, so a call to a name the table does not have is a no-op that reports\n * `DK_FEATURE_FAILED` (with the method) instead of a raw TypeError at the caller. Only reads of\n * missing names reach it: the table keeps its identity and own members, `in` / `hasOwn` stay\n * false, and `Object.prototype` members, symbols and generic probes (`then`, `toJSON`, …) resolve\n * as usual.\n */\nfunction missingApi(failed: readonly string[], host: FeatureHost): object {\n const feature = failed.join(', ')\n const stubs = new Map<string, () => undefined>()\n return new Proxy(Object.prototype, {\n get(proto, key, receiver) {\n if (typeof key !== 'string' || key in proto || isProbe(key)) {\n return Reflect.get(proto, key, receiver)\n }\n let stub = stubs.get(key)\n if (!stub) {\n stub = () => {\n host.call(key, () =>\n host.emit('DK_FEATURE_FAILED', {\n feature,\n hook: 'createApi',\n method: key,\n fallback: `${failed.length === 1 ? 'api' : 'apis'}-missing|${key}`,\n }),\n )\n return undefined\n }\n stubs.set(key, stub)\n }\n return stub\n },\n })\n}\n"],"mappings":"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","names":[]}
|
|
@@ -27,6 +27,7 @@ __export(codes_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(codes_exports);
|
|
28
28
|
var import_suggest = require("../../catalog/suggest.cjs");
|
|
29
29
|
var import_catalog = require("../../diagnostics/catalog.cjs");
|
|
30
|
+
var import_process_guard = require("../../diagnostics/process-guard.cjs");
|
|
30
31
|
var FILTER_KINDS = ["equals", "contains", "in", "range", "custom"];
|
|
31
32
|
var rowList = (rows, count) => {
|
|
32
33
|
if (rows.length === 0) return "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/codes.ts"],"sourcesContent":["import { suggest } from '../../catalog/suggest'\nimport { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FILTER_TYPE_MISMATCH: {\n /** Column id (`key` or `id`). */\n column: string\n /** Position in `options.columns`. */\n index: number\n /** The filter kind (`type`) as given, e.g. `contains`. */\n filter: string\n /** `cell`: cell values of the wrong type; `filter`: the filter value itself is malformed. */\n source: 'cell' | 'filter'\n /** What the filter accepts, e.g. `string`, `number | bigint`, `Date`, `array`. */\n expected: string\n /** The type of the first offending value (`string`, `number`, `Date`, `array`, …). */\n received: string\n /** Cells of the wrong type (0 for `source: 'filter'`). */\n count: number\n /** A sample of their row indices (first few, in order). */\n rows: readonly number[]\n }\n DK_STATE_UNKNOWN_KEY: {\n /** The state slice holding the key, e.g. `columnFilters`. */\n slice: string\n /** The key that names no column. */\n key: string\n /** The table's column ids (did-you-mean). */\n known: readonly string[]\n }\n }\n}\n\n/** The filter kinds of §6.1. */\nexport const FILTER_KINDS = ['equals', 'contains', 'in', 'range', 'custom'] as const\n\nconst rowList = (rows: readonly number[], count: number) => {\n if (rows.length === 0) return ''\n const list = rows.map((r) => `#${r}`).join(', ')\n return count > rows.length ? ` (first: ${list}, …)` : ` (rows ${list})`\n}\n\nconst plural = (n: number, one: string, many: string) => (n === 1 ? `1 ${one}` : `${n} ${many}`)\n\n/** A conversion from `received` to `expected`, as code, when one is obvious. */\nfunction convert(expected: string, received: string): string | undefined {\n if (expected.includes('number') && received === 'string') return 'Number(x)'\n if (expected === 'string' && (received === 'number' || received === 'bigint')) return 'String(x)'\n if (expected === 'Date' && (received === 'string' || received === 'number')) return 'new Date(x)'\n return undefined\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const columnFiltersMessages: DiagnosticMessages<\n 'DK_FILTER_TYPE_MISMATCH' | 'DK_STATE_UNKNOWN_KEY'\n> = {\n DK_STATE_UNKNOWN_KEY: {\n problem: (d) => `state.${d.slice} has an entry for \"${d.key}\", which is not a column`,\n impact: () =>\n 'the entry is ignored (the other entries still apply); it may come from an older URL or ' +\n 'saved state, or the column was renamed or removed',\n fix: (d) => {\n const near = suggest(d.key, d.known)\n return [\n near.length > 0\n ? `did you mean \"${near[0]}\"? (state.${d.slice}[\"${near[0]}\"])`\n : `use one of the column ids: ${d.known.map((k) => `\"${k}\"`).join(', ')}`,\n `drop \"${d.key}\" when parsing initialState / state (e.g. from the URL), or add the column`,\n ]\n },\n },\n DK_FILTER_TYPE_MISMATCH: {\n problem: (d) =>\n d.source === 'filter'\n ? `the \"${d.filter}\" filter on column \"${d.column}\" has a value of type ${d.received}; ` +\n `it expects ${d.expected}`\n : `the \"${d.filter}\" filter on column \"${d.column}\" compares ${d.expected} values, but ` +\n `${plural(d.count, 'cell holds', 'cells hold')} another type (first: ` +\n `${d.received})${rowList(d.rows, d.count)}`,\n impact: (d) =>\n d.source === 'filter'\n ? 'the filter matches no rows until its value is fixed'\n : 'those rows are treated as non-matching; the other rows are filtered normally',\n fix: (d) => {\n const at = `state.columnFilters[\"${d.column}\"]`\n if (d.source === 'filter') {\n if (!(FILTER_KINDS as readonly string[]).includes(d.filter)) {\n const near = suggest(d.filter, FILTER_KINDS)\n return [\n near.length > 0\n ? `did you mean type: '${near[0]}'? (${at})`\n : `use one of the filter kinds ${FILTER_KINDS.join(', ')} in ${at}`,\n ]\n }\n return [\n `give ${at} a value of type ${d.expected}: { type: '${d.filter}', value: … }`,\n 'remove the filter (setColumnFilter(id, undefined)) while there is no value',\n ]\n }\n const out: string[] = []\n const c = convert(d.expected, d.received)\n if (c) {\n out.push(\n `if the filter value is the wrong type, convert it before setting it: ${c.replace('x', 'input')}`,\n `if the data is the wrong type, normalise the column: ` +\n `{ id: '${d.column}', accessor: (row) => ${c.replace('x', 'row.<field>')} } ` +\n `(columns[${d.index}])`,\n )\n } else {\n out.push(\n `normalise the column so every cell is ${d.expected}: an accessor on columns[${d.index}]`,\n )\n }\n out.push(\n `or filter with a kind that fits ${d.received} cells, or a custom filter: ` +\n `{ type: 'custom', value, fn: (cell, value, row) => boolean }`,\n )\n return out\n },\n },\n}\n\n/** Codes owned by the `columnFilters` feature (§6.1, §8.3). */\nexport const columnFiltersDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FILTER_TYPE_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'column', index: d.index, id: d.column }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_FILTER_TYPE_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_STATE_UNKNOWN_KEY',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slice}.${d.key}` }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_STATE_UNKNOWN_KEY,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAwB;AACxB,qBAA0D;
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/codes.ts"],"sourcesContent":["import { suggest } from '../../catalog/suggest'\nimport { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FILTER_TYPE_MISMATCH: {\n /** Column id (`key` or `id`). */\n column: string\n /** Position in `options.columns`. */\n index: number\n /** The filter kind (`type`) as given, e.g. `contains`. */\n filter: string\n /** `cell`: cell values of the wrong type; `filter`: the filter value itself is malformed. */\n source: 'cell' | 'filter'\n /** What the filter accepts, e.g. `string`, `number | bigint`, `Date`, `array`. */\n expected: string\n /** The type of the first offending value (`string`, `number`, `Date`, `array`, …). */\n received: string\n /** Cells of the wrong type (0 for `source: 'filter'`). */\n count: number\n /** A sample of their row indices (first few, in order). */\n rows: readonly number[]\n }\n DK_STATE_UNKNOWN_KEY: {\n /** The state slice holding the key, e.g. `columnFilters`. */\n slice: string\n /** The key that names no column. */\n key: string\n /** The table's column ids (did-you-mean). */\n known: readonly string[]\n }\n }\n}\n\n/** The filter kinds of §6.1. */\nexport const FILTER_KINDS = ['equals', 'contains', 'in', 'range', 'custom'] as const\n\nconst rowList = (rows: readonly number[], count: number) => {\n if (rows.length === 0) return ''\n const list = rows.map((r) => `#${r}`).join(', ')\n return count > rows.length ? ` (first: ${list}, …)` : ` (rows ${list})`\n}\n\nconst plural = (n: number, one: string, many: string) => (n === 1 ? `1 ${one}` : `${n} ${many}`)\n\n/** A conversion from `received` to `expected`, as code, when one is obvious. */\nfunction convert(expected: string, received: string): string | undefined {\n if (expected.includes('number') && received === 'string') return 'Number(x)'\n if (expected === 'string' && (received === 'number' || received === 'bigint')) return 'String(x)'\n if (expected === 'Date' && (received === 'string' || received === 'number')) return 'new Date(x)'\n return undefined\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const columnFiltersMessages: DiagnosticMessages<\n 'DK_FILTER_TYPE_MISMATCH' | 'DK_STATE_UNKNOWN_KEY'\n> = {\n DK_STATE_UNKNOWN_KEY: {\n problem: (d) => `state.${d.slice} has an entry for \"${d.key}\", which is not a column`,\n impact: () =>\n 'the entry is ignored (the other entries still apply); it may come from an older URL or ' +\n 'saved state, or the column was renamed or removed',\n fix: (d) => {\n const near = suggest(d.key, d.known)\n return [\n near.length > 0\n ? `did you mean \"${near[0]}\"? (state.${d.slice}[\"${near[0]}\"])`\n : `use one of the column ids: ${d.known.map((k) => `\"${k}\"`).join(', ')}`,\n `drop \"${d.key}\" when parsing initialState / state (e.g. from the URL), or add the column`,\n ]\n },\n },\n DK_FILTER_TYPE_MISMATCH: {\n problem: (d) =>\n d.source === 'filter'\n ? `the \"${d.filter}\" filter on column \"${d.column}\" has a value of type ${d.received}; ` +\n `it expects ${d.expected}`\n : `the \"${d.filter}\" filter on column \"${d.column}\" compares ${d.expected} values, but ` +\n `${plural(d.count, 'cell holds', 'cells hold')} another type (first: ` +\n `${d.received})${rowList(d.rows, d.count)}`,\n impact: (d) =>\n d.source === 'filter'\n ? 'the filter matches no rows until its value is fixed'\n : 'those rows are treated as non-matching; the other rows are filtered normally',\n fix: (d) => {\n const at = `state.columnFilters[\"${d.column}\"]`\n if (d.source === 'filter') {\n if (!(FILTER_KINDS as readonly string[]).includes(d.filter)) {\n const near = suggest(d.filter, FILTER_KINDS)\n return [\n near.length > 0\n ? `did you mean type: '${near[0]}'? (${at})`\n : `use one of the filter kinds ${FILTER_KINDS.join(', ')} in ${at}`,\n ]\n }\n return [\n `give ${at} a value of type ${d.expected}: { type: '${d.filter}', value: … }`,\n 'remove the filter (setColumnFilter(id, undefined)) while there is no value',\n ]\n }\n const out: string[] = []\n const c = convert(d.expected, d.received)\n if (c) {\n out.push(\n `if the filter value is the wrong type, convert it before setting it: ${c.replace('x', 'input')}`,\n `if the data is the wrong type, normalise the column: ` +\n `{ id: '${d.column}', accessor: (row) => ${c.replace('x', 'row.<field>')} } ` +\n `(columns[${d.index}])`,\n )\n } else {\n out.push(\n `normalise the column so every cell is ${d.expected}: an accessor on columns[${d.index}]`,\n )\n }\n out.push(\n `or filter with a kind that fits ${d.received} cells, or a custom filter: ` +\n `{ type: 'custom', value, fn: (cell, value, row) => boolean }`,\n )\n return out\n },\n },\n}\n\n/** Codes owned by the `columnFilters` feature (§6.1, §8.3). */\nexport const columnFiltersDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FILTER_TYPE_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'column', index: d.index, id: d.column }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_FILTER_TYPE_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_STATE_UNKNOWN_KEY',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slice}.${d.key}` }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_STATE_UNKNOWN_KEY,\n ),\n]\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/features/column-filters/codes.ts
|
|
2
2
|
import { suggest } from "../../catalog/suggest.js";
|
|
3
3
|
import { defineDiagnostic } from "../../diagnostics/catalog.js";
|
|
4
|
+
import "../../diagnostics/process-guard.js";
|
|
4
5
|
var FILTER_KINDS = ["equals", "contains", "in", "range", "custom"];
|
|
5
6
|
var rowList = (rows, count) => {
|
|
6
7
|
if (rows.length === 0) return "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/codes.ts"],"sourcesContent":["import { suggest } from '../../catalog/suggest'\nimport { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FILTER_TYPE_MISMATCH: {\n /** Column id (`key` or `id`). */\n column: string\n /** Position in `options.columns`. */\n index: number\n /** The filter kind (`type`) as given, e.g. `contains`. */\n filter: string\n /** `cell`: cell values of the wrong type; `filter`: the filter value itself is malformed. */\n source: 'cell' | 'filter'\n /** What the filter accepts, e.g. `string`, `number | bigint`, `Date`, `array`. */\n expected: string\n /** The type of the first offending value (`string`, `number`, `Date`, `array`, …). */\n received: string\n /** Cells of the wrong type (0 for `source: 'filter'`). */\n count: number\n /** A sample of their row indices (first few, in order). */\n rows: readonly number[]\n }\n DK_STATE_UNKNOWN_KEY: {\n /** The state slice holding the key, e.g. `columnFilters`. */\n slice: string\n /** The key that names no column. */\n key: string\n /** The table's column ids (did-you-mean). */\n known: readonly string[]\n }\n }\n}\n\n/** The filter kinds of §6.1. */\nexport const FILTER_KINDS = ['equals', 'contains', 'in', 'range', 'custom'] as const\n\nconst rowList = (rows: readonly number[], count: number) => {\n if (rows.length === 0) return ''\n const list = rows.map((r) => `#${r}`).join(', ')\n return count > rows.length ? ` (first: ${list}, …)` : ` (rows ${list})`\n}\n\nconst plural = (n: number, one: string, many: string) => (n === 1 ? `1 ${one}` : `${n} ${many}`)\n\n/** A conversion from `received` to `expected`, as code, when one is obvious. */\nfunction convert(expected: string, received: string): string | undefined {\n if (expected.includes('number') && received === 'string') return 'Number(x)'\n if (expected === 'string' && (received === 'number' || received === 'bigint')) return 'String(x)'\n if (expected === 'Date' && (received === 'string' || received === 'number')) return 'new Date(x)'\n return undefined\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const columnFiltersMessages: DiagnosticMessages<\n 'DK_FILTER_TYPE_MISMATCH' | 'DK_STATE_UNKNOWN_KEY'\n> = {\n DK_STATE_UNKNOWN_KEY: {\n problem: (d) => `state.${d.slice} has an entry for \"${d.key}\", which is not a column`,\n impact: () =>\n 'the entry is ignored (the other entries still apply); it may come from an older URL or ' +\n 'saved state, or the column was renamed or removed',\n fix: (d) => {\n const near = suggest(d.key, d.known)\n return [\n near.length > 0\n ? `did you mean \"${near[0]}\"? (state.${d.slice}[\"${near[0]}\"])`\n : `use one of the column ids: ${d.known.map((k) => `\"${k}\"`).join(', ')}`,\n `drop \"${d.key}\" when parsing initialState / state (e.g. from the URL), or add the column`,\n ]\n },\n },\n DK_FILTER_TYPE_MISMATCH: {\n problem: (d) =>\n d.source === 'filter'\n ? `the \"${d.filter}\" filter on column \"${d.column}\" has a value of type ${d.received}; ` +\n `it expects ${d.expected}`\n : `the \"${d.filter}\" filter on column \"${d.column}\" compares ${d.expected} values, but ` +\n `${plural(d.count, 'cell holds', 'cells hold')} another type (first: ` +\n `${d.received})${rowList(d.rows, d.count)}`,\n impact: (d) =>\n d.source === 'filter'\n ? 'the filter matches no rows until its value is fixed'\n : 'those rows are treated as non-matching; the other rows are filtered normally',\n fix: (d) => {\n const at = `state.columnFilters[\"${d.column}\"]`\n if (d.source === 'filter') {\n if (!(FILTER_KINDS as readonly string[]).includes(d.filter)) {\n const near = suggest(d.filter, FILTER_KINDS)\n return [\n near.length > 0\n ? `did you mean type: '${near[0]}'? (${at})`\n : `use one of the filter kinds ${FILTER_KINDS.join(', ')} in ${at}`,\n ]\n }\n return [\n `give ${at} a value of type ${d.expected}: { type: '${d.filter}', value: … }`,\n 'remove the filter (setColumnFilter(id, undefined)) while there is no value',\n ]\n }\n const out: string[] = []\n const c = convert(d.expected, d.received)\n if (c) {\n out.push(\n `if the filter value is the wrong type, convert it before setting it: ${c.replace('x', 'input')}`,\n `if the data is the wrong type, normalise the column: ` +\n `{ id: '${d.column}', accessor: (row) => ${c.replace('x', 'row.<field>')} } ` +\n `(columns[${d.index}])`,\n )\n } else {\n out.push(\n `normalise the column so every cell is ${d.expected}: an accessor on columns[${d.index}]`,\n )\n }\n out.push(\n `or filter with a kind that fits ${d.received} cells, or a custom filter: ` +\n `{ type: 'custom', value, fn: (cell, value, row) => boolean }`,\n )\n return out\n },\n },\n}\n\n/** Codes owned by the `columnFilters` feature (§6.1, §8.3). */\nexport const columnFiltersDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FILTER_TYPE_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'column', index: d.index, id: d.column }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_FILTER_TYPE_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_STATE_UNKNOWN_KEY',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slice}.${d.key}` }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_STATE_UNKNOWN_KEY,\n ),\n]\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAkC,wBAAwB;
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/codes.ts"],"sourcesContent":["import { suggest } from '../../catalog/suggest'\nimport { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FILTER_TYPE_MISMATCH: {\n /** Column id (`key` or `id`). */\n column: string\n /** Position in `options.columns`. */\n index: number\n /** The filter kind (`type`) as given, e.g. `contains`. */\n filter: string\n /** `cell`: cell values of the wrong type; `filter`: the filter value itself is malformed. */\n source: 'cell' | 'filter'\n /** What the filter accepts, e.g. `string`, `number | bigint`, `Date`, `array`. */\n expected: string\n /** The type of the first offending value (`string`, `number`, `Date`, `array`, …). */\n received: string\n /** Cells of the wrong type (0 for `source: 'filter'`). */\n count: number\n /** A sample of their row indices (first few, in order). */\n rows: readonly number[]\n }\n DK_STATE_UNKNOWN_KEY: {\n /** The state slice holding the key, e.g. `columnFilters`. */\n slice: string\n /** The key that names no column. */\n key: string\n /** The table's column ids (did-you-mean). */\n known: readonly string[]\n }\n }\n}\n\n/** The filter kinds of §6.1. */\nexport const FILTER_KINDS = ['equals', 'contains', 'in', 'range', 'custom'] as const\n\nconst rowList = (rows: readonly number[], count: number) => {\n if (rows.length === 0) return ''\n const list = rows.map((r) => `#${r}`).join(', ')\n return count > rows.length ? ` (first: ${list}, …)` : ` (rows ${list})`\n}\n\nconst plural = (n: number, one: string, many: string) => (n === 1 ? `1 ${one}` : `${n} ${many}`)\n\n/** A conversion from `received` to `expected`, as code, when one is obvious. */\nfunction convert(expected: string, received: string): string | undefined {\n if (expected.includes('number') && received === 'string') return 'Number(x)'\n if (expected === 'string' && (received === 'number' || received === 'bigint')) return 'String(x)'\n if (expected === 'Date' && (received === 'string' || received === 'number')) return 'new Date(x)'\n return undefined\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const columnFiltersMessages: DiagnosticMessages<\n 'DK_FILTER_TYPE_MISMATCH' | 'DK_STATE_UNKNOWN_KEY'\n> = {\n DK_STATE_UNKNOWN_KEY: {\n problem: (d) => `state.${d.slice} has an entry for \"${d.key}\", which is not a column`,\n impact: () =>\n 'the entry is ignored (the other entries still apply); it may come from an older URL or ' +\n 'saved state, or the column was renamed or removed',\n fix: (d) => {\n const near = suggest(d.key, d.known)\n return [\n near.length > 0\n ? `did you mean \"${near[0]}\"? (state.${d.slice}[\"${near[0]}\"])`\n : `use one of the column ids: ${d.known.map((k) => `\"${k}\"`).join(', ')}`,\n `drop \"${d.key}\" when parsing initialState / state (e.g. from the URL), or add the column`,\n ]\n },\n },\n DK_FILTER_TYPE_MISMATCH: {\n problem: (d) =>\n d.source === 'filter'\n ? `the \"${d.filter}\" filter on column \"${d.column}\" has a value of type ${d.received}; ` +\n `it expects ${d.expected}`\n : `the \"${d.filter}\" filter on column \"${d.column}\" compares ${d.expected} values, but ` +\n `${plural(d.count, 'cell holds', 'cells hold')} another type (first: ` +\n `${d.received})${rowList(d.rows, d.count)}`,\n impact: (d) =>\n d.source === 'filter'\n ? 'the filter matches no rows until its value is fixed'\n : 'those rows are treated as non-matching; the other rows are filtered normally',\n fix: (d) => {\n const at = `state.columnFilters[\"${d.column}\"]`\n if (d.source === 'filter') {\n if (!(FILTER_KINDS as readonly string[]).includes(d.filter)) {\n const near = suggest(d.filter, FILTER_KINDS)\n return [\n near.length > 0\n ? `did you mean type: '${near[0]}'? (${at})`\n : `use one of the filter kinds ${FILTER_KINDS.join(', ')} in ${at}`,\n ]\n }\n return [\n `give ${at} a value of type ${d.expected}: { type: '${d.filter}', value: … }`,\n 'remove the filter (setColumnFilter(id, undefined)) while there is no value',\n ]\n }\n const out: string[] = []\n const c = convert(d.expected, d.received)\n if (c) {\n out.push(\n `if the filter value is the wrong type, convert it before setting it: ${c.replace('x', 'input')}`,\n `if the data is the wrong type, normalise the column: ` +\n `{ id: '${d.column}', accessor: (row) => ${c.replace('x', 'row.<field>')} } ` +\n `(columns[${d.index}])`,\n )\n } else {\n out.push(\n `normalise the column so every cell is ${d.expected}: an accessor on columns[${d.index}]`,\n )\n }\n out.push(\n `or filter with a kind that fits ${d.received} cells, or a custom filter: ` +\n `{ type: 'custom', value, fn: (cell, value, row) => boolean }`,\n )\n return out\n },\n },\n}\n\n/** Codes owned by the `columnFilters` feature (§6.1, §8.3). */\nexport const columnFiltersDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FILTER_TYPE_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'column', index: d.index, id: d.column }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_FILTER_TYPE_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_STATE_UNKNOWN_KEY',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slice}.${d.key}` }),\n },\n (process.env.NODE_ENV !== \"production\") && columnFiltersMessages.DK_STATE_UNKNOWN_KEY,\n ),\n]\n"],"mappings":"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","names":[]}
|
|
@@ -25,6 +25,7 @@ __export(column_filters_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(column_filters_exports);
|
|
26
26
|
var import_options = require("../../catalog/options.cjs");
|
|
27
27
|
var import_catalog = require("../../diagnostics/catalog.cjs");
|
|
28
|
+
var import_process_guard = require("../../diagnostics/process-guard.cjs");
|
|
28
29
|
var import_define = require("../../feature/define.cjs");
|
|
29
30
|
var import_cell_batch = require("../../intercept/cell-batch.cjs");
|
|
30
31
|
var import_classify = require("../../intercept/classify.cjs");
|
|
@@ -105,7 +106,7 @@ function report(ctx, c, id, kind, index) {
|
|
|
105
106
|
column: id,
|
|
106
107
|
rows: Array.from(failures.sample()),
|
|
107
108
|
count: failures.count,
|
|
108
|
-
fallback: "
|
|
109
|
+
fallback: "rows-unmatched",
|
|
109
110
|
...name === void 0 ? {} : { errorName: name }
|
|
110
111
|
},
|
|
111
112
|
{ cause: first.error }
|
|
@@ -191,12 +192,13 @@ var columnFilters = (opts = {}) => (0, import_define.defineFeature)({
|
|
|
191
192
|
initialState: () => ({}),
|
|
192
193
|
createApi: (ctx) => ({
|
|
193
194
|
setColumnFilter: (id, filter) => ctx.setState((prev) => {
|
|
195
|
+
if (prev[id] === filter) return prev;
|
|
194
196
|
const next = { ...prev };
|
|
195
197
|
if (filter === void 0) delete next[id];
|
|
196
198
|
else next[id] = filter;
|
|
197
199
|
return next;
|
|
198
200
|
}),
|
|
199
|
-
resetColumnFilters: () => ctx.setState({})
|
|
201
|
+
resetColumnFilters: () => ctx.setState((prev) => Object.keys(prev).length ? {} : prev)
|
|
200
202
|
}),
|
|
201
203
|
rowModel: filterRows
|
|
202
204
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/column-filters.ts"],"sourcesContent":["import { defineOptions } from '../../catalog/options'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport { defineFeature } from '../../feature/define'\nimport type { RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { columnFiltersDiagnostics } from './codes'\nimport {\n BAD,\n type Compiled,\n compile,\n MATCH,\n NO,\n type Outcome,\n type RawFilter,\n type Test,\n typeName,\n} from './match'\nimport type { ColumnFiltersState, ColumnFiltersTypes } from './types'\n\nkeepDiagnostics(columnFiltersDiagnostics, boundaryDiagnostics)\n\nexport interface ColumnFiltersOptions {\n /**\n * `'server'`: the server filters (the slice goes to `options.fetcher` as `filters`, or the app\n * fetches); core skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst columnFiltersOptions = { mode: modeOption } as const\n\n/** Position of column `id` in `options.columns` (diagnostic `where`); `-1` if absent. */\nfunction columnIndex(columns: readonly unknown[], id: string): number {\n for (let i = 0; i < columns.length; i++) {\n const c = columns[i] as { key?: unknown; id?: unknown } | null | undefined\n if (c && typeof c === 'object' && (typeof c.key === 'string' ? c.key : c.id) === id) return i\n }\n return -1\n}\n\nfunction columnIds(columns: readonly unknown[]): string[] {\n const out: string[] = []\n for (const c of columns as readonly ({ key?: unknown; id?: unknown } | null)[]) {\n const id = c && (typeof c.key === 'string' ? c.key : c.id)\n if (typeof id === 'string') out.push(id)\n }\n return out\n}\n\n/** Nullish and `NaN` (nullish in a numeric column cache) match no active filter (§6.1). */\nconst absent = (v: unknown) =>\n v === undefined || v === null || (typeof v === 'number' && Number.isNaN(v))\n\n/** One cell: an array cell matches if any element matches, else `BAD` if one had a wrong type. */\nfunction testCell(test: Test, v: unknown, r: number): Outcome {\n if (!Array.isArray(v)) return test(v, r)\n let out: Outcome = NO\n for (let i = 0; i < v.length; i++) {\n const e = v[i]\n if (absent(e)) continue\n const o = test(e, -1)\n if (o === MATCH) return MATCH\n if (o === BAD) out = BAD\n }\n return out\n}\n\n/**\n * Per-batch failures, recorded as numbers (§3.4) and reused across runs: wrong-type cells, or\n * throwing `custom` calls. Text and `where` are built only when something failed.\n */\nconst failures = new CellFailures()\nconst first = { type: '', error: undefined as unknown }\n\n/** Keeps the rows of `rows[0..n)` that pass `c`, compacted into `out` (may be `rows`). */\nfunction apply(\n c: Extract<Compiled, { kind: 'test' | 'custom' }>,\n values: ColumnValues,\n data: readonly unknown[],\n rows: Uint32Array,\n n: number,\n out: Uint32Array,\n): number {\n let k = 0\n if (c.kind === 'custom') {\n const { fn, value } = c\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n try {\n if (fn(v, value, data[r])) out[k++] = r\n } catch (e) {\n if (failures.count === 0) first.error = e\n failures.add(r, e)\n }\n }\n return k\n }\n const test = c.test\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n const o = testCell(test, v, r)\n if (o === MATCH) out[k++] = r\n else if (o === BAD) {\n if (failures.count === 0) first.type = typeName(v)\n failures.add(r, undefined)\n }\n }\n return k\n}\n\nfunction report(ctx: RowModelContext, c: Compiled, id: string, kind: string, index: number) {\n if (c.kind === 'custom') {\n // Like a column:batch filterFn failure (§3.4); `where` is the filter's state path.\n const name = errorName(first.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `state.columnFilters.${id}.fn`,\n column: id,\n rows: Array.from(failures.sample()),\n count: failures.count,\n fallback: 'the failed rows are treated as non-matching',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: first.error },\n )\n } else if (c.kind === 'test') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: kind,\n source: 'cell',\n expected: c.expected,\n received: first.type,\n count: failures.count,\n rows: Array.from(failures.sample()),\n })\n }\n}\n\n/**\n * Applies every active column filter (AND) to `indices`: one pass per filtered column over the\n * column cache (§9.1), no per-cell allocation. Returns `indices` itself when nothing is active;\n * otherwise a new array (`indices` is never mutated).\n */\nfunction filterRows(\n indices: Uint32Array,\n state: Readonly<Record<string, unknown>> | undefined,\n ctx: RowModelContext,\n): Uint32Array {\n if (!state || typeof state !== 'object') return indices\n let out: Uint32Array | undefined\n let n = indices.length\n for (const id of Object.keys(state)) {\n const filter = state[id]\n if (filter === undefined) continue\n const values = ctx.getColumnValues(id)\n if (!values) {\n // Not a column (§7.2): ignored. No columns at all (zero-config, no rows yet) is not reported.\n if (ctx.columns.length > 0) {\n ctx.emit('DK_STATE_UNKNOWN_KEY', {\n slice: 'columnFilters',\n key: id,\n known: columnIds(ctx.columns),\n })\n }\n continue\n }\n const c = compile(filter, values)\n if (c.kind === 'off') continue\n const index = columnIndex(ctx.columns, id)\n if (c.kind === 'bad') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: c.filter,\n source: 'filter',\n expected: c.expected,\n received: c.received,\n count: 0,\n rows: [],\n })\n return new Uint32Array(0)\n }\n const src = out ?? indices\n out ??= new Uint32Array(indices.length)\n failures.reset()\n first.error = undefined\n try {\n if (c.kind === 'custom') {\n // A user callback per cell: one `filterFn` column:batch (§3.4). A skipped batch keeps\n // the slot fallback: no row matches.\n const rows = src.subarray(0, n)\n const dst = out\n let k = 0\n ctx.batch(id, 'filterFn', rows, () => {\n k = apply(c, values, ctx.data, rows, rows.length, dst)\n })\n n = k\n } else n = apply(c, values, ctx.data, src, n, out)\n if (failures.count > 0) report(ctx, c, id, String((filter as RawFilter).type), index)\n } finally {\n failures.reset()\n first.error = undefined\n }\n }\n return out ? out.subarray(0, n) : indices\n}\n\n/**\n * Column filters (§6.1): `equals`, `contains` (case-insensitive, NFC), `in`, `range` (inclusive),\n * `custom`. Filters combine with AND; nullish cells match no active filter; array cells match if\n * any element matches; a cell of the wrong runtime type is a non-match + `DK_FILTER_TYPE_MISMATCH`.\n *\n * ```ts\n * const table = createTable({ data: users, columns, features: [columnFilters()] })\n * table.setColumnFilter('age', { type: 'range', value: { min: 18 } })\n * table.setColumnFilter('name', { type: 'contains', value: 'ada' })\n * ```\n */\nexport const columnFilters = (opts: ColumnFiltersOptions = {}) =>\n defineFeature<'columnFilters', ColumnFiltersTypes>({\n name: 'columnFilters',\n stage: 'filter',\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\") ? columnFiltersOptions : ({} as typeof columnFiltersOptions),\n opts,\n ),\n initialState: () => ({}),\n createApi: (ctx) => ({\n setColumnFilter: (id, filter) =>\n ctx.setState((prev) => {\n const next: Record<string, unknown> = { ...prev }\n if (filter === undefined) delete next[id]\n else next[id] = filter\n return next as ColumnFiltersState<unknown>\n }),\n resetColumnFilters: () => ctx.setState({}),\n }),\n rowModel: filterRows,\n })\n"],"mappings":"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","names":["import_codes"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/column-filters.ts"],"sourcesContent":["import { defineOptions } from '../../catalog/options'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\nimport { defineFeature } from '../../feature/define'\nimport type { RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { columnFiltersDiagnostics } from './codes'\nimport {\n BAD,\n type Compiled,\n compile,\n MATCH,\n NO,\n type Outcome,\n type RawFilter,\n type Test,\n typeName,\n} from './match'\nimport type { ColumnFiltersState, ColumnFiltersTypes } from './types'\n\nkeepDiagnostics(columnFiltersDiagnostics, boundaryDiagnostics)\n\nexport interface ColumnFiltersOptions {\n /**\n * `'server'`: the server filters (the slice goes to `options.fetcher` as `filters`, or the app\n * fetches); core skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst columnFiltersOptions = { mode: modeOption } as const\n\n/** Position of column `id` in `options.columns` (diagnostic `where`); `-1` if absent. */\nfunction columnIndex(columns: readonly unknown[], id: string): number {\n for (let i = 0; i < columns.length; i++) {\n const c = columns[i] as { key?: unknown; id?: unknown } | null | undefined\n if (c && typeof c === 'object' && (typeof c.key === 'string' ? c.key : c.id) === id) return i\n }\n return -1\n}\n\nfunction columnIds(columns: readonly unknown[]): string[] {\n const out: string[] = []\n for (const c of columns as readonly ({ key?: unknown; id?: unknown } | null)[]) {\n const id = c && (typeof c.key === 'string' ? c.key : c.id)\n if (typeof id === 'string') out.push(id)\n }\n return out\n}\n\n/** Nullish and `NaN` (nullish in a numeric column cache) match no active filter (§6.1). */\nconst absent = (v: unknown) =>\n v === undefined || v === null || (typeof v === 'number' && Number.isNaN(v))\n\n/** One cell: an array cell matches if any element matches, else `BAD` if one had a wrong type. */\nfunction testCell(test: Test, v: unknown, r: number): Outcome {\n if (!Array.isArray(v)) return test(v, r)\n let out: Outcome = NO\n for (let i = 0; i < v.length; i++) {\n const e = v[i]\n if (absent(e)) continue\n const o = test(e, -1)\n if (o === MATCH) return MATCH\n if (o === BAD) out = BAD\n }\n return out\n}\n\n/**\n * Per-batch failures, recorded as numbers (§3.4) and reused across runs: wrong-type cells, or\n * throwing `custom` calls. Text and `where` are built only when something failed.\n */\nconst failures = new CellFailures()\nconst first = { type: '', error: undefined as unknown }\n\n/** Keeps the rows of `rows[0..n)` that pass `c`, compacted into `out` (may be `rows`). */\nfunction apply(\n c: Extract<Compiled, { kind: 'test' | 'custom' }>,\n values: ColumnValues,\n data: readonly unknown[],\n rows: Uint32Array,\n n: number,\n out: Uint32Array,\n): number {\n let k = 0\n if (c.kind === 'custom') {\n const { fn, value } = c\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n try {\n if (fn(v, value, data[r])) out[k++] = r\n } catch (e) {\n if (failures.count === 0) first.error = e\n failures.add(r, e)\n }\n }\n return k\n }\n const test = c.test\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n const o = testCell(test, v, r)\n if (o === MATCH) out[k++] = r\n else if (o === BAD) {\n if (failures.count === 0) first.type = typeName(v)\n failures.add(r, undefined)\n }\n }\n return k\n}\n\nfunction report(ctx: RowModelContext, c: Compiled, id: string, kind: string, index: number) {\n if (c.kind === 'custom') {\n // Like a column:batch filterFn failure (§3.4); `where` is the filter's state path.\n const name = errorName(first.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `state.columnFilters.${id}.fn`,\n column: id,\n rows: Array.from(failures.sample()),\n count: failures.count,\n fallback: 'rows-unmatched',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: first.error },\n )\n } else if (c.kind === 'test') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: kind,\n source: 'cell',\n expected: c.expected,\n received: first.type,\n count: failures.count,\n rows: Array.from(failures.sample()),\n })\n }\n}\n\n/**\n * Applies every active column filter (AND) to `indices`: one pass per filtered column over the\n * column cache (§9.1), no per-cell allocation. Returns `indices` itself when nothing is active;\n * otherwise a new array (`indices` is never mutated).\n */\nfunction filterRows(\n indices: Uint32Array,\n state: Readonly<Record<string, unknown>> | undefined,\n ctx: RowModelContext,\n): Uint32Array {\n if (!state || typeof state !== 'object') return indices\n let out: Uint32Array | undefined\n let n = indices.length\n for (const id of Object.keys(state)) {\n const filter = state[id]\n if (filter === undefined) continue\n const values = ctx.getColumnValues(id)\n if (!values) {\n // Not a column (§7.2): ignored. No columns at all (zero-config, no rows yet) is not reported.\n if (ctx.columns.length > 0) {\n ctx.emit('DK_STATE_UNKNOWN_KEY', {\n slice: 'columnFilters',\n key: id,\n known: columnIds(ctx.columns),\n })\n }\n continue\n }\n const c = compile(filter, values)\n if (c.kind === 'off') continue\n const index = columnIndex(ctx.columns, id)\n if (c.kind === 'bad') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: c.filter,\n source: 'filter',\n expected: c.expected,\n received: c.received,\n count: 0,\n rows: [],\n })\n return new Uint32Array(0)\n }\n const src = out ?? indices\n out ??= new Uint32Array(indices.length)\n failures.reset()\n first.error = undefined\n try {\n if (c.kind === 'custom') {\n // A user callback per cell: one `filterFn` column:batch (§3.4). A skipped batch keeps\n // the slot fallback: no row matches.\n const rows = src.subarray(0, n)\n const dst = out\n let k = 0\n ctx.batch(id, 'filterFn', rows, () => {\n k = apply(c, values, ctx.data, rows, rows.length, dst)\n })\n n = k\n } else n = apply(c, values, ctx.data, src, n, out)\n if (failures.count > 0) report(ctx, c, id, String((filter as RawFilter).type), index)\n } finally {\n failures.reset()\n first.error = undefined\n }\n }\n return out ? out.subarray(0, n) : indices\n}\n\n/**\n * Column filters (§6.1): `equals`, `contains` (case-insensitive, NFC), `in`, `range` (inclusive),\n * `custom`. Filters combine with AND; nullish cells match no active filter; array cells match if\n * any element matches; a cell of the wrong runtime type is a non-match + `DK_FILTER_TYPE_MISMATCH`.\n *\n * ```ts\n * const table = createTable({ data: users, columns, features: [columnFilters()] })\n * table.setColumnFilter('age', { type: 'range', value: { min: 18 } })\n * table.setColumnFilter('name', { type: 'contains', value: 'ada' })\n * ```\n */\nexport const columnFilters = (opts: ColumnFiltersOptions = {}) =>\n defineFeature<'columnFilters', ColumnFiltersTypes>({\n name: 'columnFilters',\n stage: 'filter',\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\") ? columnFiltersOptions : ({} as typeof columnFiltersOptions),\n opts,\n ),\n initialState: () => ({}),\n createApi: (ctx) => ({\n setColumnFilter: (id, filter) =>\n ctx.setState((prev) => {\n // No change (the same filter, or clearing a column without one): keep the slice.\n if ((prev as Record<string, unknown>)[id] === filter) return prev\n const next: Record<string, unknown> = { ...prev }\n if (filter === undefined) delete next[id]\n else next[id] = filter\n return next as ColumnFiltersState<unknown>\n }),\n resetColumnFilters: () => ctx.setState((prev) => (Object.keys(prev).length ? {} : prev)),\n }),\n rowModel: filterRows,\n })\n"],"mappings":"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","names":["import_codes"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/features/column-filters/column-filters.ts
|
|
2
2
|
import { defineOptions } from "../../catalog/options.js";
|
|
3
3
|
import { keepDiagnostics } from "../../diagnostics/catalog.js";
|
|
4
|
+
import "../../diagnostics/process-guard.js";
|
|
4
5
|
import { defineFeature } from "../../feature/define.js";
|
|
5
6
|
import { CellFailures } from "../../intercept/cell-batch.js";
|
|
6
7
|
import { errorName } from "../../intercept/classify.js";
|
|
@@ -87,7 +88,7 @@ function report(ctx, c, id, kind, index) {
|
|
|
87
88
|
column: id,
|
|
88
89
|
rows: Array.from(failures.sample()),
|
|
89
90
|
count: failures.count,
|
|
90
|
-
fallback: "
|
|
91
|
+
fallback: "rows-unmatched",
|
|
91
92
|
...name === void 0 ? {} : { errorName: name }
|
|
92
93
|
},
|
|
93
94
|
{ cause: first.error }
|
|
@@ -173,12 +174,13 @@ var columnFilters = (opts = {}) => defineFeature({
|
|
|
173
174
|
initialState: () => ({}),
|
|
174
175
|
createApi: (ctx) => ({
|
|
175
176
|
setColumnFilter: (id, filter) => ctx.setState((prev) => {
|
|
177
|
+
if (prev[id] === filter) return prev;
|
|
176
178
|
const next = { ...prev };
|
|
177
179
|
if (filter === void 0) delete next[id];
|
|
178
180
|
else next[id] = filter;
|
|
179
181
|
return next;
|
|
180
182
|
}),
|
|
181
|
-
resetColumnFilters: () => ctx.setState({})
|
|
183
|
+
resetColumnFilters: () => ctx.setState((prev) => Object.keys(prev).length ? {} : prev)
|
|
182
184
|
}),
|
|
183
185
|
rowModel: filterRows
|
|
184
186
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/column-filters.ts"],"sourcesContent":["import { defineOptions } from '../../catalog/options'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport { defineFeature } from '../../feature/define'\nimport type { RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { columnFiltersDiagnostics } from './codes'\nimport {\n BAD,\n type Compiled,\n compile,\n MATCH,\n NO,\n type Outcome,\n type RawFilter,\n type Test,\n typeName,\n} from './match'\nimport type { ColumnFiltersState, ColumnFiltersTypes } from './types'\n\nkeepDiagnostics(columnFiltersDiagnostics, boundaryDiagnostics)\n\nexport interface ColumnFiltersOptions {\n /**\n * `'server'`: the server filters (the slice goes to `options.fetcher` as `filters`, or the app\n * fetches); core skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst columnFiltersOptions = { mode: modeOption } as const\n\n/** Position of column `id` in `options.columns` (diagnostic `where`); `-1` if absent. */\nfunction columnIndex(columns: readonly unknown[], id: string): number {\n for (let i = 0; i < columns.length; i++) {\n const c = columns[i] as { key?: unknown; id?: unknown } | null | undefined\n if (c && typeof c === 'object' && (typeof c.key === 'string' ? c.key : c.id) === id) return i\n }\n return -1\n}\n\nfunction columnIds(columns: readonly unknown[]): string[] {\n const out: string[] = []\n for (const c of columns as readonly ({ key?: unknown; id?: unknown } | null)[]) {\n const id = c && (typeof c.key === 'string' ? c.key : c.id)\n if (typeof id === 'string') out.push(id)\n }\n return out\n}\n\n/** Nullish and `NaN` (nullish in a numeric column cache) match no active filter (§6.1). */\nconst absent = (v: unknown) =>\n v === undefined || v === null || (typeof v === 'number' && Number.isNaN(v))\n\n/** One cell: an array cell matches if any element matches, else `BAD` if one had a wrong type. */\nfunction testCell(test: Test, v: unknown, r: number): Outcome {\n if (!Array.isArray(v)) return test(v, r)\n let out: Outcome = NO\n for (let i = 0; i < v.length; i++) {\n const e = v[i]\n if (absent(e)) continue\n const o = test(e, -1)\n if (o === MATCH) return MATCH\n if (o === BAD) out = BAD\n }\n return out\n}\n\n/**\n * Per-batch failures, recorded as numbers (§3.4) and reused across runs: wrong-type cells, or\n * throwing `custom` calls. Text and `where` are built only when something failed.\n */\nconst failures = new CellFailures()\nconst first = { type: '', error: undefined as unknown }\n\n/** Keeps the rows of `rows[0..n)` that pass `c`, compacted into `out` (may be `rows`). */\nfunction apply(\n c: Extract<Compiled, { kind: 'test' | 'custom' }>,\n values: ColumnValues,\n data: readonly unknown[],\n rows: Uint32Array,\n n: number,\n out: Uint32Array,\n): number {\n let k = 0\n if (c.kind === 'custom') {\n const { fn, value } = c\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n try {\n if (fn(v, value, data[r])) out[k++] = r\n } catch (e) {\n if (failures.count === 0) first.error = e\n failures.add(r, e)\n }\n }\n return k\n }\n const test = c.test\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n const o = testCell(test, v, r)\n if (o === MATCH) out[k++] = r\n else if (o === BAD) {\n if (failures.count === 0) first.type = typeName(v)\n failures.add(r, undefined)\n }\n }\n return k\n}\n\nfunction report(ctx: RowModelContext, c: Compiled, id: string, kind: string, index: number) {\n if (c.kind === 'custom') {\n // Like a column:batch filterFn failure (§3.4); `where` is the filter's state path.\n const name = errorName(first.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `state.columnFilters.${id}.fn`,\n column: id,\n rows: Array.from(failures.sample()),\n count: failures.count,\n fallback: 'the failed rows are treated as non-matching',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: first.error },\n )\n } else if (c.kind === 'test') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: kind,\n source: 'cell',\n expected: c.expected,\n received: first.type,\n count: failures.count,\n rows: Array.from(failures.sample()),\n })\n }\n}\n\n/**\n * Applies every active column filter (AND) to `indices`: one pass per filtered column over the\n * column cache (§9.1), no per-cell allocation. Returns `indices` itself when nothing is active;\n * otherwise a new array (`indices` is never mutated).\n */\nfunction filterRows(\n indices: Uint32Array,\n state: Readonly<Record<string, unknown>> | undefined,\n ctx: RowModelContext,\n): Uint32Array {\n if (!state || typeof state !== 'object') return indices\n let out: Uint32Array | undefined\n let n = indices.length\n for (const id of Object.keys(state)) {\n const filter = state[id]\n if (filter === undefined) continue\n const values = ctx.getColumnValues(id)\n if (!values) {\n // Not a column (§7.2): ignored. No columns at all (zero-config, no rows yet) is not reported.\n if (ctx.columns.length > 0) {\n ctx.emit('DK_STATE_UNKNOWN_KEY', {\n slice: 'columnFilters',\n key: id,\n known: columnIds(ctx.columns),\n })\n }\n continue\n }\n const c = compile(filter, values)\n if (c.kind === 'off') continue\n const index = columnIndex(ctx.columns, id)\n if (c.kind === 'bad') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: c.filter,\n source: 'filter',\n expected: c.expected,\n received: c.received,\n count: 0,\n rows: [],\n })\n return new Uint32Array(0)\n }\n const src = out ?? indices\n out ??= new Uint32Array(indices.length)\n failures.reset()\n first.error = undefined\n try {\n if (c.kind === 'custom') {\n // A user callback per cell: one `filterFn` column:batch (§3.4). A skipped batch keeps\n // the slot fallback: no row matches.\n const rows = src.subarray(0, n)\n const dst = out\n let k = 0\n ctx.batch(id, 'filterFn', rows, () => {\n k = apply(c, values, ctx.data, rows, rows.length, dst)\n })\n n = k\n } else n = apply(c, values, ctx.data, src, n, out)\n if (failures.count > 0) report(ctx, c, id, String((filter as RawFilter).type), index)\n } finally {\n failures.reset()\n first.error = undefined\n }\n }\n return out ? out.subarray(0, n) : indices\n}\n\n/**\n * Column filters (§6.1): `equals`, `contains` (case-insensitive, NFC), `in`, `range` (inclusive),\n * `custom`. Filters combine with AND; nullish cells match no active filter; array cells match if\n * any element matches; a cell of the wrong runtime type is a non-match + `DK_FILTER_TYPE_MISMATCH`.\n *\n * ```ts\n * const table = createTable({ data: users, columns, features: [columnFilters()] })\n * table.setColumnFilter('age', { type: 'range', value: { min: 18 } })\n * table.setColumnFilter('name', { type: 'contains', value: 'ada' })\n * ```\n */\nexport const columnFilters = (opts: ColumnFiltersOptions = {}) =>\n defineFeature<'columnFilters', ColumnFiltersTypes>({\n name: 'columnFilters',\n stage: 'filter',\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\") ? columnFiltersOptions : ({} as typeof columnFiltersOptions),\n opts,\n ),\n initialState: () => ({}),\n createApi: (ctx) => ({\n setColumnFilter: (id, filter) =>\n ctx.setState((prev) => {\n const next: Record<string, unknown> = { ...prev }\n if (filter === undefined) delete next[id]\n else next[id] = filter\n return next as ColumnFiltersState<unknown>\n }),\n resetColumnFilters: () => ctx.setState({}),\n }),\n rowModel: filterRows,\n })\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/column-filters.ts"],"sourcesContent":["import { defineOptions } from '../../catalog/options'\nimport { keepDiagnostics } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\nimport { defineFeature } from '../../feature/define'\nimport type { RowModelContext } from '../../feature/types'\nimport { CellFailures } from '../../intercept/cell-batch'\nimport { errorName } from '../../intercept/classify'\nimport { boundaryDiagnostics } from '../../intercept/codes'\nimport type { ColumnValues } from '../../pipeline/types'\nimport { type DataMode, modeOption } from '../../source/mode'\nimport { columnFiltersDiagnostics } from './codes'\nimport {\n BAD,\n type Compiled,\n compile,\n MATCH,\n NO,\n type Outcome,\n type RawFilter,\n type Test,\n typeName,\n} from './match'\nimport type { ColumnFiltersState, ColumnFiltersTypes } from './types'\n\nkeepDiagnostics(columnFiltersDiagnostics, boundaryDiagnostics)\n\nexport interface ColumnFiltersOptions {\n /**\n * `'server'`: the server filters (the slice goes to `options.fetcher` as `filters`, or the app\n * fetches); core skips this step (§7.1). Default `'client'`.\n */\n mode?: DataMode\n}\n\nconst columnFiltersOptions = { mode: modeOption } as const\n\n/** Position of column `id` in `options.columns` (diagnostic `where`); `-1` if absent. */\nfunction columnIndex(columns: readonly unknown[], id: string): number {\n for (let i = 0; i < columns.length; i++) {\n const c = columns[i] as { key?: unknown; id?: unknown } | null | undefined\n if (c && typeof c === 'object' && (typeof c.key === 'string' ? c.key : c.id) === id) return i\n }\n return -1\n}\n\nfunction columnIds(columns: readonly unknown[]): string[] {\n const out: string[] = []\n for (const c of columns as readonly ({ key?: unknown; id?: unknown } | null)[]) {\n const id = c && (typeof c.key === 'string' ? c.key : c.id)\n if (typeof id === 'string') out.push(id)\n }\n return out\n}\n\n/** Nullish and `NaN` (nullish in a numeric column cache) match no active filter (§6.1). */\nconst absent = (v: unknown) =>\n v === undefined || v === null || (typeof v === 'number' && Number.isNaN(v))\n\n/** One cell: an array cell matches if any element matches, else `BAD` if one had a wrong type. */\nfunction testCell(test: Test, v: unknown, r: number): Outcome {\n if (!Array.isArray(v)) return test(v, r)\n let out: Outcome = NO\n for (let i = 0; i < v.length; i++) {\n const e = v[i]\n if (absent(e)) continue\n const o = test(e, -1)\n if (o === MATCH) return MATCH\n if (o === BAD) out = BAD\n }\n return out\n}\n\n/**\n * Per-batch failures, recorded as numbers (§3.4) and reused across runs: wrong-type cells, or\n * throwing `custom` calls. Text and `where` are built only when something failed.\n */\nconst failures = new CellFailures()\nconst first = { type: '', error: undefined as unknown }\n\n/** Keeps the rows of `rows[0..n)` that pass `c`, compacted into `out` (may be `rows`). */\nfunction apply(\n c: Extract<Compiled, { kind: 'test' | 'custom' }>,\n values: ColumnValues,\n data: readonly unknown[],\n rows: Uint32Array,\n n: number,\n out: Uint32Array,\n): number {\n let k = 0\n if (c.kind === 'custom') {\n const { fn, value } = c\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n try {\n if (fn(v, value, data[r])) out[k++] = r\n } catch (e) {\n if (failures.count === 0) first.error = e\n failures.add(r, e)\n }\n }\n return k\n }\n const test = c.test\n for (let i = 0; i < n; i++) {\n const r = rows[i] as number\n const v = values[r]\n if (absent(v)) continue\n const o = testCell(test, v, r)\n if (o === MATCH) out[k++] = r\n else if (o === BAD) {\n if (failures.count === 0) first.type = typeName(v)\n failures.add(r, undefined)\n }\n }\n return k\n}\n\nfunction report(ctx: RowModelContext, c: Compiled, id: string, kind: string, index: number) {\n if (c.kind === 'custom') {\n // Like a column:batch filterFn failure (§3.4); `where` is the filter's state path.\n const name = errorName(first.error)\n ctx.emit(\n 'DK_CALLBACK_FAILED',\n {\n slot: `state.columnFilters.${id}.fn`,\n column: id,\n rows: Array.from(failures.sample()),\n count: failures.count,\n fallback: 'rows-unmatched',\n ...(name === undefined ? {} : { errorName: name }),\n },\n { cause: first.error },\n )\n } else if (c.kind === 'test') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: kind,\n source: 'cell',\n expected: c.expected,\n received: first.type,\n count: failures.count,\n rows: Array.from(failures.sample()),\n })\n }\n}\n\n/**\n * Applies every active column filter (AND) to `indices`: one pass per filtered column over the\n * column cache (§9.1), no per-cell allocation. Returns `indices` itself when nothing is active;\n * otherwise a new array (`indices` is never mutated).\n */\nfunction filterRows(\n indices: Uint32Array,\n state: Readonly<Record<string, unknown>> | undefined,\n ctx: RowModelContext,\n): Uint32Array {\n if (!state || typeof state !== 'object') return indices\n let out: Uint32Array | undefined\n let n = indices.length\n for (const id of Object.keys(state)) {\n const filter = state[id]\n if (filter === undefined) continue\n const values = ctx.getColumnValues(id)\n if (!values) {\n // Not a column (§7.2): ignored. No columns at all (zero-config, no rows yet) is not reported.\n if (ctx.columns.length > 0) {\n ctx.emit('DK_STATE_UNKNOWN_KEY', {\n slice: 'columnFilters',\n key: id,\n known: columnIds(ctx.columns),\n })\n }\n continue\n }\n const c = compile(filter, values)\n if (c.kind === 'off') continue\n const index = columnIndex(ctx.columns, id)\n if (c.kind === 'bad') {\n ctx.emit('DK_FILTER_TYPE_MISMATCH', {\n column: id,\n index,\n filter: c.filter,\n source: 'filter',\n expected: c.expected,\n received: c.received,\n count: 0,\n rows: [],\n })\n return new Uint32Array(0)\n }\n const src = out ?? indices\n out ??= new Uint32Array(indices.length)\n failures.reset()\n first.error = undefined\n try {\n if (c.kind === 'custom') {\n // A user callback per cell: one `filterFn` column:batch (§3.4). A skipped batch keeps\n // the slot fallback: no row matches.\n const rows = src.subarray(0, n)\n const dst = out\n let k = 0\n ctx.batch(id, 'filterFn', rows, () => {\n k = apply(c, values, ctx.data, rows, rows.length, dst)\n })\n n = k\n } else n = apply(c, values, ctx.data, src, n, out)\n if (failures.count > 0) report(ctx, c, id, String((filter as RawFilter).type), index)\n } finally {\n failures.reset()\n first.error = undefined\n }\n }\n return out ? out.subarray(0, n) : indices\n}\n\n/**\n * Column filters (§6.1): `equals`, `contains` (case-insensitive, NFC), `in`, `range` (inclusive),\n * `custom`. Filters combine with AND; nullish cells match no active filter; array cells match if\n * any element matches; a cell of the wrong runtime type is a non-match + `DK_FILTER_TYPE_MISMATCH`.\n *\n * ```ts\n * const table = createTable({ data: users, columns, features: [columnFilters()] })\n * table.setColumnFilter('age', { type: 'range', value: { min: 18 } })\n * table.setColumnFilter('name', { type: 'contains', value: 'ada' })\n * ```\n */\nexport const columnFilters = (opts: ColumnFiltersOptions = {}) =>\n defineFeature<'columnFilters', ColumnFiltersTypes>({\n name: 'columnFilters',\n stage: 'filter',\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\") ? columnFiltersOptions : ({} as typeof columnFiltersOptions),\n opts,\n ),\n initialState: () => ({}),\n createApi: (ctx) => ({\n setColumnFilter: (id, filter) =>\n ctx.setState((prev) => {\n // No change (the same filter, or clearing a column without one): keep the slice.\n if ((prev as Record<string, unknown>)[id] === filter) return prev\n const next: Record<string, unknown> = { ...prev }\n if (filter === undefined) delete next[id]\n else next[id] = filter\n return next as ColumnFiltersState<unknown>\n }),\n resetColumnFilters: () => ctx.setState((prev) => (Object.keys(prev).length ? {} : prev)),\n }),\n rowModel: filterRows,\n })\n"],"mappings":"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","names":[]}
|
|
@@ -62,7 +62,7 @@ function equals(fv) {
|
|
|
62
62
|
}
|
|
63
63
|
const type = typeof fv;
|
|
64
64
|
if (type === "object" || type === "function" || type === "symbol") {
|
|
65
|
-
return bad("equals", "
|
|
65
|
+
return bad("equals", "prim-or-date", fv);
|
|
66
66
|
}
|
|
67
67
|
return {
|
|
68
68
|
kind: "test",
|
|
@@ -103,7 +103,7 @@ function inList(fv) {
|
|
|
103
103
|
if (item === void 0 || item === null) continue;
|
|
104
104
|
if (item instanceof Date) times.add(item.getTime());
|
|
105
105
|
else if (typeof item === "object" || typeof item === "function") {
|
|
106
|
-
return bad("in", "
|
|
106
|
+
return bad("in", "prims-or-dates", item);
|
|
107
107
|
} else prims.add(item);
|
|
108
108
|
types.add(typeName(item));
|
|
109
109
|
}
|
|
@@ -133,12 +133,12 @@ function range(fv) {
|
|
|
133
133
|
const { min: rawMin, max: rawMax } = fv;
|
|
134
134
|
const min = bound(rawMin);
|
|
135
135
|
const max = bound(rawMax);
|
|
136
|
-
if (min === false) return bad("range",
|
|
137
|
-
if (max === false) return bad("range",
|
|
136
|
+
if (min === false) return bad("range", "range-bound", rawMin);
|
|
137
|
+
if (max === false) return bad("range", "range-bound", rawMax);
|
|
138
138
|
if (!min && !max) return OFF;
|
|
139
139
|
const family = min || max;
|
|
140
140
|
if (min && max && min.family !== max.family) {
|
|
141
|
-
return bad("range", `
|
|
141
|
+
return bad("range", `same-type|${min.family}`, rawMax);
|
|
142
142
|
}
|
|
143
143
|
const lo = min ? min.at : void 0;
|
|
144
144
|
const hi = max ? max.at : void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/match.ts"],"sourcesContent":["import type { ColumnValues } from '../../pipeline/types'\nimport { FILTER_KINDS } from './codes'\n\n/** Result of testing one value: a match, no match, or a value of the wrong type (non-match). */\nexport const NO = 0\nexport const MATCH = 1\nexport const BAD = 2\nexport type Outcome = typeof NO | typeof MATCH | typeof BAD\n\n/**\n * Tests one non-nullish value. `r` is the row index when the value is the whole cell, `-1` for an\n * element of an array cell (per-row caches apply to whole cells only).\n */\nexport type Test = (value: unknown, r: number) => Outcome\n\n/** A runtime filter, as read from state (runtime JSON ignores the types). */\nexport interface RawFilter {\n readonly type?: unknown\n readonly value?: unknown\n readonly fn?: unknown\n}\n\nexport type Compiled =\n /** Inactive: the step skips it. */\n | { readonly kind: 'off' }\n /** A value test with what it accepts (for `DK_FILTER_TYPE_MISMATCH`). */\n | { readonly kind: 'test'; readonly test: Test; readonly expected: string }\n | {\n readonly kind: 'custom'\n readonly fn: (v: unknown, fv: unknown, row: unknown) => unknown\n readonly value: unknown\n }\n /** Malformed filter value: matches nothing; reported once with `source: 'filter'`. */\n | {\n readonly kind: 'bad'\n readonly filter: string\n readonly expected: string\n readonly received: string\n }\n\nconst OFF: Compiled = { kind: 'off' }\n\n/** A short type name for diagnostics: `null`, `array`, `Date`, else `typeof`. Never a value. */\nexport const typeName = (v: unknown): string =>\n v === null ? 'null' : Array.isArray(v) ? 'array' : v instanceof Date ? 'Date' : typeof v\n\n/** `undefined`, `null` and `''` leave a filter (or a range bound) inactive (§6.1). */\nconst empty = (v: unknown) => v === undefined || v === null || v === ''\n\n/** Case-insensitive, NFC-normalised form for `contains` (Thai combining marks compare correctly). */\nconst fold = (s: string) => s.toLowerCase().normalize('NFC')\n\n/**\n * Folded cell strings per column-values array: the column cache returns the same array for the same\n * data and column, so typing into a `contains` filter folds each cell once.\n */\nconst folded = new WeakMap<ColumnValues, (string | undefined)[]>()\n\nfunction foldedFor(values: ColumnValues): (string | undefined)[] {\n let f = folded.get(values)\n if (!f) {\n f = new Array<string | undefined>(values.length)\n folded.set(values, f)\n }\n return f\n}\n\nconst bad = (filter: string, expected: string, received: unknown): Compiled => ({\n kind: 'bad',\n filter,\n expected,\n received: typeName(received),\n})\n\nfunction equals(fv: unknown): Compiled {\n if (empty(fv)) return OFF\n if (fv instanceof Date) {\n const t = fv.getTime()\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (e.getTime() === t ? MATCH : NO) : BAD),\n }\n }\n const type = typeof fv\n if (type === 'object' || type === 'function' || type === 'symbol') {\n return bad('equals', 'a primitive or a Date', fv)\n }\n return {\n kind: 'test',\n expected: type,\n test: (e) => (typeof e === type ? (e === fv ? MATCH : NO) : BAD),\n }\n}\n\nfunction contains(fv: unknown, values: ColumnValues): Compiled {\n if (empty(fv)) return OFF\n if (typeof fv !== 'string') return bad('contains', 'string', fv)\n const needle = fold(fv)\n const cache = foldedFor(values)\n return {\n kind: 'test',\n expected: 'string',\n test: (e, r) => {\n if (typeof e !== 'string') return BAD\n let s: string | undefined\n if (r < 0) s = fold(e)\n else {\n s = cache[r]\n if (s === undefined) {\n s = fold(e)\n cache[r] = s\n }\n }\n return s.includes(needle) ? MATCH : NO\n },\n }\n}\n\nfunction inList(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (!Array.isArray(fv)) return bad('in', 'array', fv)\n const prims = new Set<unknown>()\n const times = new Set<number>()\n const types = new Set<string>()\n for (const item of fv as unknown[]) {\n if (item === undefined || item === null) continue\n if (item instanceof Date) times.add(item.getTime())\n else if (typeof item === 'object' || typeof item === 'function') {\n return bad('in', 'an array of primitives or Dates', item)\n } else prims.add(item)\n types.add(typeName(item))\n }\n if (types.size === 0) return OFF\n const hasDate = times.size > 0\n return {\n kind: 'test',\n expected: [...types].join(' | '),\n test: (e) => {\n if (e instanceof Date) return hasDate ? (times.has(e.getTime()) ? MATCH : NO) : BAD\n return types.has(typeof e) ? (prims.has(e) ? MATCH : NO) : BAD\n },\n }\n}\n\ntype Family = 'number' | 'Date'\n\n/** A range bound: absent, its family + comparable value, or malformed. */\nfunction bound(v: unknown): { family: Family; at: number | bigint } | null | false {\n if (empty(v)) return null\n if (typeof v === 'number' || typeof v === 'bigint') return { family: 'number', at: v }\n if (v instanceof Date && !Number.isNaN(v.getTime())) return { family: 'Date', at: v.getTime() }\n return false\n}\n\nconst NUMERIC = 'number | bigint'\n\nfunction range(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (typeof fv !== 'object' || Array.isArray(fv) || fv instanceof Date) {\n return bad('range', '{ min?, max? }', fv)\n }\n const { min: rawMin, max: rawMax } = fv as { min?: unknown; max?: unknown }\n const min = bound(rawMin)\n const max = bound(rawMax)\n if (min === false) return bad('range', `${NUMERIC} or a valid Date bound`, rawMin)\n if (max === false) return bad('range', `${NUMERIC} or a valid Date bound`, rawMax)\n if (!min && !max) return OFF\n const family = (min || max) as { family: Family }\n if (min && max && min.family !== max.family) {\n return bad('range', `min and max of the same type (${min.family})`, rawMax)\n }\n // Mixed number / bigint comparisons are exact in JS (`3n >= 2.5`).\n const lo = min ? (min.at as number) : undefined\n const hi = max ? (max.at as number) : undefined\n const inside = (x: number) => (lo === undefined || x >= lo) && (hi === undefined || x <= hi)\n if (family.family === 'Date') {\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (inside(e.getTime()) ? MATCH : NO) : BAD),\n }\n }\n return {\n kind: 'test',\n expected: NUMERIC,\n test: (e) =>\n typeof e === 'number' || typeof e === 'bigint' ? (inside(e as number) ? MATCH : NO) : BAD,\n }\n}\n\n/** Compiles one column's filter for this run (cold: once per active filter per run). */\nexport function compile(filter: unknown, values: ColumnValues): Compiled {\n if (filter === undefined || filter === null) return OFF\n if (typeof filter !== 'object') return bad('(none)', '{ type, value }', filter)\n const f = filter as RawFilter\n switch (f.type) {\n case 'equals':\n return equals(f.value)\n case 'contains':\n return contains(f.value, values)\n case 'in':\n return inList(f.value)\n case 'range':\n return range(f.value)\n case 'custom':\n return typeof f.fn === 'function'\n ? {\n kind: 'custom',\n fn: f.fn as (v: unknown, fv: unknown, row: unknown) => unknown,\n value: f.value,\n }\n : bad('custom', 'fn: (value, filterValue, row) => boolean', f.fn)\n default:\n return {\n kind: 'bad',\n filter: typeof f.type === 'string' ? f.type : '(none)',\n expected: FILTER_KINDS.join(' | '),\n received: 'unknown filter kind',\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/match.ts"],"sourcesContent":["import type { ColumnValues } from '../../pipeline/types'\nimport { FILTER_KINDS } from './codes'\n\n/** Result of testing one value: a match, no match, or a value of the wrong type (non-match). */\nexport const NO = 0\nexport const MATCH = 1\nexport const BAD = 2\nexport type Outcome = typeof NO | typeof MATCH | typeof BAD\n\n/**\n * Tests one non-nullish value. `r` is the row index when the value is the whole cell, `-1` for an\n * element of an array cell (per-row caches apply to whole cells only).\n */\nexport type Test = (value: unknown, r: number) => Outcome\n\n/** A runtime filter, as read from state (runtime JSON ignores the types). */\nexport interface RawFilter {\n readonly type?: unknown\n readonly value?: unknown\n readonly fn?: unknown\n}\n\nexport type Compiled =\n /** Inactive: the step skips it. */\n | { readonly kind: 'off' }\n /** A value test with what it accepts (for `DK_FILTER_TYPE_MISMATCH`). */\n | { readonly kind: 'test'; readonly test: Test; readonly expected: string }\n | {\n readonly kind: 'custom'\n readonly fn: (v: unknown, fv: unknown, row: unknown) => unknown\n readonly value: unknown\n }\n /** Malformed filter value: matches nothing; reported once with `source: 'filter'`. */\n | {\n readonly kind: 'bad'\n readonly filter: string\n readonly expected: string\n readonly received: string\n }\n\nconst OFF: Compiled = { kind: 'off' }\n\n/** A short type name for diagnostics: `null`, `array`, `Date`, else `typeof`. Never a value. */\nexport const typeName = (v: unknown): string =>\n v === null ? 'null' : Array.isArray(v) ? 'array' : v instanceof Date ? 'Date' : typeof v\n\n/** `undefined`, `null` and `''` leave a filter (or a range bound) inactive (§6.1). */\nconst empty = (v: unknown) => v === undefined || v === null || v === ''\n\n/** Case-insensitive, NFC-normalised form for `contains` (Thai combining marks compare correctly). */\nconst fold = (s: string) => s.toLowerCase().normalize('NFC')\n\n/**\n * Folded cell strings per column-values array: the column cache returns the same array for the same\n * data and column, so typing into a `contains` filter folds each cell once.\n */\nconst folded = new WeakMap<ColumnValues, (string | undefined)[]>()\n\nfunction foldedFor(values: ColumnValues): (string | undefined)[] {\n let f = folded.get(values)\n if (!f) {\n f = new Array<string | undefined>(values.length)\n folded.set(values, f)\n }\n return f\n}\n\nconst bad = (filter: string, expected: string, received: unknown): Compiled => ({\n kind: 'bad',\n filter,\n expected,\n received: typeName(received),\n})\n\nfunction equals(fv: unknown): Compiled {\n if (empty(fv)) return OFF\n if (fv instanceof Date) {\n const t = fv.getTime()\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (e.getTime() === t ? MATCH : NO) : BAD),\n }\n }\n const type = typeof fv\n if (type === 'object' || type === 'function' || type === 'symbol') {\n return bad('equals', 'prim-or-date', fv)\n }\n return {\n kind: 'test',\n expected: type,\n test: (e) => (typeof e === type ? (e === fv ? MATCH : NO) : BAD),\n }\n}\n\nfunction contains(fv: unknown, values: ColumnValues): Compiled {\n if (empty(fv)) return OFF\n if (typeof fv !== 'string') return bad('contains', 'string', fv)\n const needle = fold(fv)\n const cache = foldedFor(values)\n return {\n kind: 'test',\n expected: 'string',\n test: (e, r) => {\n if (typeof e !== 'string') return BAD\n let s: string | undefined\n if (r < 0) s = fold(e)\n else {\n s = cache[r]\n if (s === undefined) {\n s = fold(e)\n cache[r] = s\n }\n }\n return s.includes(needle) ? MATCH : NO\n },\n }\n}\n\nfunction inList(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (!Array.isArray(fv)) return bad('in', 'array', fv)\n const prims = new Set<unknown>()\n const times = new Set<number>()\n const types = new Set<string>()\n for (const item of fv as unknown[]) {\n if (item === undefined || item === null) continue\n if (item instanceof Date) times.add(item.getTime())\n else if (typeof item === 'object' || typeof item === 'function') {\n return bad('in', 'prims-or-dates', item)\n } else prims.add(item)\n types.add(typeName(item))\n }\n if (types.size === 0) return OFF\n const hasDate = times.size > 0\n return {\n kind: 'test',\n expected: [...types].join(' | '),\n test: (e) => {\n if (e instanceof Date) return hasDate ? (times.has(e.getTime()) ? MATCH : NO) : BAD\n return types.has(typeof e) ? (prims.has(e) ? MATCH : NO) : BAD\n },\n }\n}\n\ntype Family = 'number' | 'Date'\n\n/** A range bound: absent, its family + comparable value, or malformed. */\nfunction bound(v: unknown): { family: Family; at: number | bigint } | null | false {\n if (empty(v)) return null\n if (typeof v === 'number' || typeof v === 'bigint') return { family: 'number', at: v }\n if (v instanceof Date && !Number.isNaN(v.getTime())) return { family: 'Date', at: v.getTime() }\n return false\n}\n\nconst NUMERIC = 'number | bigint'\n\nfunction range(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (typeof fv !== 'object' || Array.isArray(fv) || fv instanceof Date) {\n return bad('range', '{ min?, max? }', fv)\n }\n const { min: rawMin, max: rawMax } = fv as { min?: unknown; max?: unknown }\n const min = bound(rawMin)\n const max = bound(rawMax)\n // `expected` prose keys (diagnostics/prose.ts).\n if (min === false) return bad('range', 'range-bound', rawMin)\n if (max === false) return bad('range', 'range-bound', rawMax)\n if (!min && !max) return OFF\n const family = (min || max) as { family: Family }\n if (min && max && min.family !== max.family) {\n return bad('range', `same-type|${min.family}`, rawMax)\n }\n // Mixed number / bigint comparisons are exact in JS (`3n >= 2.5`).\n const lo = min ? (min.at as number) : undefined\n const hi = max ? (max.at as number) : undefined\n const inside = (x: number) => (lo === undefined || x >= lo) && (hi === undefined || x <= hi)\n if (family.family === 'Date') {\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (inside(e.getTime()) ? MATCH : NO) : BAD),\n }\n }\n return {\n kind: 'test',\n expected: NUMERIC,\n test: (e) =>\n typeof e === 'number' || typeof e === 'bigint' ? (inside(e as number) ? MATCH : NO) : BAD,\n }\n}\n\n/** Compiles one column's filter for this run (cold: once per active filter per run). */\nexport function compile(filter: unknown, values: ColumnValues): Compiled {\n if (filter === undefined || filter === null) return OFF\n if (typeof filter !== 'object') return bad('(none)', '{ type, value }', filter)\n const f = filter as RawFilter\n switch (f.type) {\n case 'equals':\n return equals(f.value)\n case 'contains':\n return contains(f.value, values)\n case 'in':\n return inList(f.value)\n case 'range':\n return range(f.value)\n case 'custom':\n return typeof f.fn === 'function'\n ? {\n kind: 'custom',\n fn: f.fn as (v: unknown, fv: unknown, row: unknown) => unknown,\n value: f.value,\n }\n : bad('custom', 'fn: (value, filterValue, row) => boolean', f.fn)\n default:\n return {\n kind: 'bad',\n filter: typeof f.type === 'string' ? f.type : '(none)',\n expected: FILTER_KINDS.join(' | '),\n received: 'unknown filter kind',\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -34,7 +34,7 @@ function equals(fv) {
|
|
|
34
34
|
}
|
|
35
35
|
const type = typeof fv;
|
|
36
36
|
if (type === "object" || type === "function" || type === "symbol") {
|
|
37
|
-
return bad("equals", "
|
|
37
|
+
return bad("equals", "prim-or-date", fv);
|
|
38
38
|
}
|
|
39
39
|
return {
|
|
40
40
|
kind: "test",
|
|
@@ -75,7 +75,7 @@ function inList(fv) {
|
|
|
75
75
|
if (item === void 0 || item === null) continue;
|
|
76
76
|
if (item instanceof Date) times.add(item.getTime());
|
|
77
77
|
else if (typeof item === "object" || typeof item === "function") {
|
|
78
|
-
return bad("in", "
|
|
78
|
+
return bad("in", "prims-or-dates", item);
|
|
79
79
|
} else prims.add(item);
|
|
80
80
|
types.add(typeName(item));
|
|
81
81
|
}
|
|
@@ -105,12 +105,12 @@ function range(fv) {
|
|
|
105
105
|
const { min: rawMin, max: rawMax } = fv;
|
|
106
106
|
const min = bound(rawMin);
|
|
107
107
|
const max = bound(rawMax);
|
|
108
|
-
if (min === false) return bad("range",
|
|
109
|
-
if (max === false) return bad("range",
|
|
108
|
+
if (min === false) return bad("range", "range-bound", rawMin);
|
|
109
|
+
if (max === false) return bad("range", "range-bound", rawMax);
|
|
110
110
|
if (!min && !max) return OFF;
|
|
111
111
|
const family = min || max;
|
|
112
112
|
if (min && max && min.family !== max.family) {
|
|
113
|
-
return bad("range", `
|
|
113
|
+
return bad("range", `same-type|${min.family}`, rawMax);
|
|
114
114
|
}
|
|
115
115
|
const lo = min ? min.at : void 0;
|
|
116
116
|
const hi = max ? max.at : void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/column-filters/match.ts"],"sourcesContent":["import type { ColumnValues } from '../../pipeline/types'\nimport { FILTER_KINDS } from './codes'\n\n/** Result of testing one value: a match, no match, or a value of the wrong type (non-match). */\nexport const NO = 0\nexport const MATCH = 1\nexport const BAD = 2\nexport type Outcome = typeof NO | typeof MATCH | typeof BAD\n\n/**\n * Tests one non-nullish value. `r` is the row index when the value is the whole cell, `-1` for an\n * element of an array cell (per-row caches apply to whole cells only).\n */\nexport type Test = (value: unknown, r: number) => Outcome\n\n/** A runtime filter, as read from state (runtime JSON ignores the types). */\nexport interface RawFilter {\n readonly type?: unknown\n readonly value?: unknown\n readonly fn?: unknown\n}\n\nexport type Compiled =\n /** Inactive: the step skips it. */\n | { readonly kind: 'off' }\n /** A value test with what it accepts (for `DK_FILTER_TYPE_MISMATCH`). */\n | { readonly kind: 'test'; readonly test: Test; readonly expected: string }\n | {\n readonly kind: 'custom'\n readonly fn: (v: unknown, fv: unknown, row: unknown) => unknown\n readonly value: unknown\n }\n /** Malformed filter value: matches nothing; reported once with `source: 'filter'`. */\n | {\n readonly kind: 'bad'\n readonly filter: string\n readonly expected: string\n readonly received: string\n }\n\nconst OFF: Compiled = { kind: 'off' }\n\n/** A short type name for diagnostics: `null`, `array`, `Date`, else `typeof`. Never a value. */\nexport const typeName = (v: unknown): string =>\n v === null ? 'null' : Array.isArray(v) ? 'array' : v instanceof Date ? 'Date' : typeof v\n\n/** `undefined`, `null` and `''` leave a filter (or a range bound) inactive (§6.1). */\nconst empty = (v: unknown) => v === undefined || v === null || v === ''\n\n/** Case-insensitive, NFC-normalised form for `contains` (Thai combining marks compare correctly). */\nconst fold = (s: string) => s.toLowerCase().normalize('NFC')\n\n/**\n * Folded cell strings per column-values array: the column cache returns the same array for the same\n * data and column, so typing into a `contains` filter folds each cell once.\n */\nconst folded = new WeakMap<ColumnValues, (string | undefined)[]>()\n\nfunction foldedFor(values: ColumnValues): (string | undefined)[] {\n let f = folded.get(values)\n if (!f) {\n f = new Array<string | undefined>(values.length)\n folded.set(values, f)\n }\n return f\n}\n\nconst bad = (filter: string, expected: string, received: unknown): Compiled => ({\n kind: 'bad',\n filter,\n expected,\n received: typeName(received),\n})\n\nfunction equals(fv: unknown): Compiled {\n if (empty(fv)) return OFF\n if (fv instanceof Date) {\n const t = fv.getTime()\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (e.getTime() === t ? MATCH : NO) : BAD),\n }\n }\n const type = typeof fv\n if (type === 'object' || type === 'function' || type === 'symbol') {\n return bad('equals', 'a primitive or a Date', fv)\n }\n return {\n kind: 'test',\n expected: type,\n test: (e) => (typeof e === type ? (e === fv ? MATCH : NO) : BAD),\n }\n}\n\nfunction contains(fv: unknown, values: ColumnValues): Compiled {\n if (empty(fv)) return OFF\n if (typeof fv !== 'string') return bad('contains', 'string', fv)\n const needle = fold(fv)\n const cache = foldedFor(values)\n return {\n kind: 'test',\n expected: 'string',\n test: (e, r) => {\n if (typeof e !== 'string') return BAD\n let s: string | undefined\n if (r < 0) s = fold(e)\n else {\n s = cache[r]\n if (s === undefined) {\n s = fold(e)\n cache[r] = s\n }\n }\n return s.includes(needle) ? MATCH : NO\n },\n }\n}\n\nfunction inList(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (!Array.isArray(fv)) return bad('in', 'array', fv)\n const prims = new Set<unknown>()\n const times = new Set<number>()\n const types = new Set<string>()\n for (const item of fv as unknown[]) {\n if (item === undefined || item === null) continue\n if (item instanceof Date) times.add(item.getTime())\n else if (typeof item === 'object' || typeof item === 'function') {\n return bad('in', 'an array of primitives or Dates', item)\n } else prims.add(item)\n types.add(typeName(item))\n }\n if (types.size === 0) return OFF\n const hasDate = times.size > 0\n return {\n kind: 'test',\n expected: [...types].join(' | '),\n test: (e) => {\n if (e instanceof Date) return hasDate ? (times.has(e.getTime()) ? MATCH : NO) : BAD\n return types.has(typeof e) ? (prims.has(e) ? MATCH : NO) : BAD\n },\n }\n}\n\ntype Family = 'number' | 'Date'\n\n/** A range bound: absent, its family + comparable value, or malformed. */\nfunction bound(v: unknown): { family: Family; at: number | bigint } | null | false {\n if (empty(v)) return null\n if (typeof v === 'number' || typeof v === 'bigint') return { family: 'number', at: v }\n if (v instanceof Date && !Number.isNaN(v.getTime())) return { family: 'Date', at: v.getTime() }\n return false\n}\n\nconst NUMERIC = 'number | bigint'\n\nfunction range(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (typeof fv !== 'object' || Array.isArray(fv) || fv instanceof Date) {\n return bad('range', '{ min?, max? }', fv)\n }\n const { min: rawMin, max: rawMax } = fv as { min?: unknown; max?: unknown }\n const min = bound(rawMin)\n const max = bound(rawMax)\n if (min === false) return bad('range', `${NUMERIC} or a valid Date bound`, rawMin)\n if (max === false) return bad('range', `${NUMERIC} or a valid Date bound`, rawMax)\n if (!min && !max) return OFF\n const family = (min || max) as { family: Family }\n if (min && max && min.family !== max.family) {\n return bad('range', `min and max of the same type (${min.family})`, rawMax)\n }\n // Mixed number / bigint comparisons are exact in JS (`3n >= 2.5`).\n const lo = min ? (min.at as number) : undefined\n const hi = max ? (max.at as number) : undefined\n const inside = (x: number) => (lo === undefined || x >= lo) && (hi === undefined || x <= hi)\n if (family.family === 'Date') {\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (inside(e.getTime()) ? MATCH : NO) : BAD),\n }\n }\n return {\n kind: 'test',\n expected: NUMERIC,\n test: (e) =>\n typeof e === 'number' || typeof e === 'bigint' ? (inside(e as number) ? MATCH : NO) : BAD,\n }\n}\n\n/** Compiles one column's filter for this run (cold: once per active filter per run). */\nexport function compile(filter: unknown, values: ColumnValues): Compiled {\n if (filter === undefined || filter === null) return OFF\n if (typeof filter !== 'object') return bad('(none)', '{ type, value }', filter)\n const f = filter as RawFilter\n switch (f.type) {\n case 'equals':\n return equals(f.value)\n case 'contains':\n return contains(f.value, values)\n case 'in':\n return inList(f.value)\n case 'range':\n return range(f.value)\n case 'custom':\n return typeof f.fn === 'function'\n ? {\n kind: 'custom',\n fn: f.fn as (v: unknown, fv: unknown, row: unknown) => unknown,\n value: f.value,\n }\n : bad('custom', 'fn: (value, filterValue, row) => boolean', f.fn)\n default:\n return {\n kind: 'bad',\n filter: typeof f.type === 'string' ? f.type : '(none)',\n expected: FILTER_KINDS.join(' | '),\n received: 'unknown filter kind',\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/features/column-filters/match.ts"],"sourcesContent":["import type { ColumnValues } from '../../pipeline/types'\nimport { FILTER_KINDS } from './codes'\n\n/** Result of testing one value: a match, no match, or a value of the wrong type (non-match). */\nexport const NO = 0\nexport const MATCH = 1\nexport const BAD = 2\nexport type Outcome = typeof NO | typeof MATCH | typeof BAD\n\n/**\n * Tests one non-nullish value. `r` is the row index when the value is the whole cell, `-1` for an\n * element of an array cell (per-row caches apply to whole cells only).\n */\nexport type Test = (value: unknown, r: number) => Outcome\n\n/** A runtime filter, as read from state (runtime JSON ignores the types). */\nexport interface RawFilter {\n readonly type?: unknown\n readonly value?: unknown\n readonly fn?: unknown\n}\n\nexport type Compiled =\n /** Inactive: the step skips it. */\n | { readonly kind: 'off' }\n /** A value test with what it accepts (for `DK_FILTER_TYPE_MISMATCH`). */\n | { readonly kind: 'test'; readonly test: Test; readonly expected: string }\n | {\n readonly kind: 'custom'\n readonly fn: (v: unknown, fv: unknown, row: unknown) => unknown\n readonly value: unknown\n }\n /** Malformed filter value: matches nothing; reported once with `source: 'filter'`. */\n | {\n readonly kind: 'bad'\n readonly filter: string\n readonly expected: string\n readonly received: string\n }\n\nconst OFF: Compiled = { kind: 'off' }\n\n/** A short type name for diagnostics: `null`, `array`, `Date`, else `typeof`. Never a value. */\nexport const typeName = (v: unknown): string =>\n v === null ? 'null' : Array.isArray(v) ? 'array' : v instanceof Date ? 'Date' : typeof v\n\n/** `undefined`, `null` and `''` leave a filter (or a range bound) inactive (§6.1). */\nconst empty = (v: unknown) => v === undefined || v === null || v === ''\n\n/** Case-insensitive, NFC-normalised form for `contains` (Thai combining marks compare correctly). */\nconst fold = (s: string) => s.toLowerCase().normalize('NFC')\n\n/**\n * Folded cell strings per column-values array: the column cache returns the same array for the same\n * data and column, so typing into a `contains` filter folds each cell once.\n */\nconst folded = new WeakMap<ColumnValues, (string | undefined)[]>()\n\nfunction foldedFor(values: ColumnValues): (string | undefined)[] {\n let f = folded.get(values)\n if (!f) {\n f = new Array<string | undefined>(values.length)\n folded.set(values, f)\n }\n return f\n}\n\nconst bad = (filter: string, expected: string, received: unknown): Compiled => ({\n kind: 'bad',\n filter,\n expected,\n received: typeName(received),\n})\n\nfunction equals(fv: unknown): Compiled {\n if (empty(fv)) return OFF\n if (fv instanceof Date) {\n const t = fv.getTime()\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (e.getTime() === t ? MATCH : NO) : BAD),\n }\n }\n const type = typeof fv\n if (type === 'object' || type === 'function' || type === 'symbol') {\n return bad('equals', 'prim-or-date', fv)\n }\n return {\n kind: 'test',\n expected: type,\n test: (e) => (typeof e === type ? (e === fv ? MATCH : NO) : BAD),\n }\n}\n\nfunction contains(fv: unknown, values: ColumnValues): Compiled {\n if (empty(fv)) return OFF\n if (typeof fv !== 'string') return bad('contains', 'string', fv)\n const needle = fold(fv)\n const cache = foldedFor(values)\n return {\n kind: 'test',\n expected: 'string',\n test: (e, r) => {\n if (typeof e !== 'string') return BAD\n let s: string | undefined\n if (r < 0) s = fold(e)\n else {\n s = cache[r]\n if (s === undefined) {\n s = fold(e)\n cache[r] = s\n }\n }\n return s.includes(needle) ? MATCH : NO\n },\n }\n}\n\nfunction inList(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (!Array.isArray(fv)) return bad('in', 'array', fv)\n const prims = new Set<unknown>()\n const times = new Set<number>()\n const types = new Set<string>()\n for (const item of fv as unknown[]) {\n if (item === undefined || item === null) continue\n if (item instanceof Date) times.add(item.getTime())\n else if (typeof item === 'object' || typeof item === 'function') {\n return bad('in', 'prims-or-dates', item)\n } else prims.add(item)\n types.add(typeName(item))\n }\n if (types.size === 0) return OFF\n const hasDate = times.size > 0\n return {\n kind: 'test',\n expected: [...types].join(' | '),\n test: (e) => {\n if (e instanceof Date) return hasDate ? (times.has(e.getTime()) ? MATCH : NO) : BAD\n return types.has(typeof e) ? (prims.has(e) ? MATCH : NO) : BAD\n },\n }\n}\n\ntype Family = 'number' | 'Date'\n\n/** A range bound: absent, its family + comparable value, or malformed. */\nfunction bound(v: unknown): { family: Family; at: number | bigint } | null | false {\n if (empty(v)) return null\n if (typeof v === 'number' || typeof v === 'bigint') return { family: 'number', at: v }\n if (v instanceof Date && !Number.isNaN(v.getTime())) return { family: 'Date', at: v.getTime() }\n return false\n}\n\nconst NUMERIC = 'number | bigint'\n\nfunction range(fv: unknown): Compiled {\n if (fv === undefined || fv === null) return OFF\n if (typeof fv !== 'object' || Array.isArray(fv) || fv instanceof Date) {\n return bad('range', '{ min?, max? }', fv)\n }\n const { min: rawMin, max: rawMax } = fv as { min?: unknown; max?: unknown }\n const min = bound(rawMin)\n const max = bound(rawMax)\n // `expected` prose keys (diagnostics/prose.ts).\n if (min === false) return bad('range', 'range-bound', rawMin)\n if (max === false) return bad('range', 'range-bound', rawMax)\n if (!min && !max) return OFF\n const family = (min || max) as { family: Family }\n if (min && max && min.family !== max.family) {\n return bad('range', `same-type|${min.family}`, rawMax)\n }\n // Mixed number / bigint comparisons are exact in JS (`3n >= 2.5`).\n const lo = min ? (min.at as number) : undefined\n const hi = max ? (max.at as number) : undefined\n const inside = (x: number) => (lo === undefined || x >= lo) && (hi === undefined || x <= hi)\n if (family.family === 'Date') {\n return {\n kind: 'test',\n expected: 'Date',\n test: (e) => (e instanceof Date ? (inside(e.getTime()) ? MATCH : NO) : BAD),\n }\n }\n return {\n kind: 'test',\n expected: NUMERIC,\n test: (e) =>\n typeof e === 'number' || typeof e === 'bigint' ? (inside(e as number) ? MATCH : NO) : BAD,\n }\n}\n\n/** Compiles one column's filter for this run (cold: once per active filter per run). */\nexport function compile(filter: unknown, values: ColumnValues): Compiled {\n if (filter === undefined || filter === null) return OFF\n if (typeof filter !== 'object') return bad('(none)', '{ type, value }', filter)\n const f = filter as RawFilter\n switch (f.type) {\n case 'equals':\n return equals(f.value)\n case 'contains':\n return contains(f.value, values)\n case 'in':\n return inList(f.value)\n case 'range':\n return range(f.value)\n case 'custom':\n return typeof f.fn === 'function'\n ? {\n kind: 'custom',\n fn: f.fn as (v: unknown, fv: unknown, row: unknown) => unknown,\n value: f.value,\n }\n : bad('custom', 'fn: (value, filterValue, row) => boolean', f.fn)\n default:\n return {\n kind: 'bad',\n filter: typeof f.type === 'string' ? f.type : '(none)',\n expected: FILTER_KINDS.join(' | '),\n received: 'unknown filter kind',\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -25,6 +25,7 @@ __export(codes_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(codes_exports);
|
|
27
27
|
var import_catalog = require("../../diagnostics/catalog.cjs");
|
|
28
|
+
var import_process_guard = require("../../diagnostics/process-guard.cjs");
|
|
28
29
|
var ACCEPTED = {
|
|
29
30
|
pageIndex: "an integer \u2265 0",
|
|
30
31
|
pageSize: "an integer > 0"
|