@datakable/core 1.0.0 → 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.
Files changed (290) hide show
  1. package/README.md +4 -2
  2. package/dist/catalog/codes.cjs +12 -8
  3. package/dist/catalog/codes.cjs.map +1 -1
  4. package/dist/catalog/codes.js +12 -8
  5. package/dist/catalog/codes.js.map +1 -1
  6. package/dist/catalog/slots.cjs +10 -6
  7. package/dist/catalog/slots.cjs.map +1 -1
  8. package/dist/catalog/slots.js +10 -6
  9. package/dist/catalog/slots.js.map +1 -1
  10. package/dist/{codes-BKdS9EiO.d.cts → codes-B9bCK73-.d.cts} +46 -8
  11. package/dist/{codes-BKdS9EiO.d.ts → codes-B9bCK73-.d.ts} +46 -8
  12. package/dist/column/column-def.cjs.map +1 -1
  13. package/dist/column/get-value.cjs +6 -1
  14. package/dist/column/get-value.cjs.map +1 -1
  15. package/dist/column/get-value.js +6 -1
  16. package/dist/column/get-value.js.map +1 -1
  17. package/dist/column-filters.d.cts +2 -2
  18. package/dist/column-filters.d.ts +2 -2
  19. package/dist/diagnostics/build.cjs +10 -4
  20. package/dist/diagnostics/build.cjs.map +1 -1
  21. package/dist/diagnostics/build.js +8 -3
  22. package/dist/diagnostics/build.js.map +1 -1
  23. package/dist/diagnostics/catalog.cjs +11 -5
  24. package/dist/diagnostics/catalog.cjs.map +1 -1
  25. package/dist/diagnostics/catalog.js +10 -5
  26. package/dist/diagnostics/catalog.js.map +1 -1
  27. package/dist/diagnostics/codes.cjs +12 -8
  28. package/dist/diagnostics/codes.cjs.map +1 -1
  29. package/dist/diagnostics/codes.js +12 -8
  30. package/dist/diagnostics/codes.js.map +1 -1
  31. package/dist/diagnostics/console.cjs +13 -6
  32. package/dist/diagnostics/console.cjs.map +1 -1
  33. package/dist/diagnostics/console.js +13 -6
  34. package/dist/diagnostics/console.js.map +1 -1
  35. package/dist/diagnostics/env.cjs +1 -0
  36. package/dist/diagnostics/env.cjs.map +1 -1
  37. package/dist/diagnostics/env.js +1 -0
  38. package/dist/diagnostics/env.js.map +1 -1
  39. package/dist/diagnostics/explain-text.cjs +2 -1
  40. package/dist/diagnostics/explain-text.cjs.map +1 -1
  41. package/dist/diagnostics/explain-text.js +2 -1
  42. package/dist/diagnostics/explain-text.js.map +1 -1
  43. package/dist/diagnostics/explain.cjs +3 -2
  44. package/dist/diagnostics/explain.cjs.map +1 -1
  45. package/dist/diagnostics/explain.js +3 -2
  46. package/dist/diagnostics/explain.js.map +1 -1
  47. package/dist/diagnostics/hub.cjs +6 -5
  48. package/dist/diagnostics/hub.cjs.map +1 -1
  49. package/dist/diagnostics/hub.js +7 -6
  50. package/dist/diagnostics/hub.js.map +1 -1
  51. package/dist/diagnostics/process-guard.cjs +26 -0
  52. package/dist/diagnostics/process-guard.cjs.map +1 -0
  53. package/dist/diagnostics/process-guard.js +9 -0
  54. package/dist/diagnostics/process-guard.js.map +1 -0
  55. package/dist/diagnostics/prose.cjs +120 -0
  56. package/dist/diagnostics/prose.cjs.map +1 -0
  57. package/dist/diagnostics/prose.js +93 -0
  58. package/dist/diagnostics/prose.js.map +1 -0
  59. package/dist/diagnostics/redact.cjs +7 -4
  60. package/dist/diagnostics/redact.cjs.map +1 -1
  61. package/dist/diagnostics/redact.js +6 -4
  62. package/dist/diagnostics/redact.js.map +1 -1
  63. package/dist/diagnostics/trace-store.cjs +6 -0
  64. package/dist/diagnostics/trace-store.cjs.map +1 -1
  65. package/dist/diagnostics/trace-store.js +4 -0
  66. package/dist/diagnostics/trace-store.js.map +1 -1
  67. package/dist/diagnostics/tracer.cjs +74 -13
  68. package/dist/diagnostics/tracer.cjs.map +1 -1
  69. package/dist/diagnostics/tracer.js +75 -14
  70. package/dist/diagnostics/tracer.js.map +1 -1
  71. package/dist/diagnostics/types.cjs.map +1 -1
  72. package/dist/{explain-aagaYHcy.d.cts → explain-Q8Cg0cZy.d.cts} +13 -4
  73. package/dist/{explain-DcUZfFh5.d.ts → explain-i0OATi_W.d.ts} +13 -4
  74. package/dist/feature/codes.cjs +1 -0
  75. package/dist/feature/codes.cjs.map +1 -1
  76. package/dist/feature/codes.js +1 -0
  77. package/dist/feature/codes.js.map +1 -1
  78. package/dist/feature/define.cjs +3 -1
  79. package/dist/feature/define.cjs.map +1 -1
  80. package/dist/feature/define.js +3 -1
  81. package/dist/feature/define.js.map +1 -1
  82. package/dist/feature/install.cjs +4 -3
  83. package/dist/feature/install.cjs.map +1 -1
  84. package/dist/feature/install.js +4 -3
  85. package/dist/feature/install.js.map +1 -1
  86. package/dist/feature/runtime.cjs.map +1 -1
  87. package/dist/feature/runtime.js.map +1 -1
  88. package/dist/features/column-filters/codes.cjs +1 -0
  89. package/dist/features/column-filters/codes.cjs.map +1 -1
  90. package/dist/features/column-filters/codes.js +1 -0
  91. package/dist/features/column-filters/codes.js.map +1 -1
  92. package/dist/features/column-filters/column-filters.cjs +4 -2
  93. package/dist/features/column-filters/column-filters.cjs.map +1 -1
  94. package/dist/features/column-filters/column-filters.js +4 -2
  95. package/dist/features/column-filters/column-filters.js.map +1 -1
  96. package/dist/features/column-filters/match.cjs +5 -5
  97. package/dist/features/column-filters/match.cjs.map +1 -1
  98. package/dist/features/column-filters/match.js +5 -5
  99. package/dist/features/column-filters/match.js.map +1 -1
  100. package/dist/features/pagination/codes.cjs +6 -3
  101. package/dist/features/pagination/codes.cjs.map +1 -1
  102. package/dist/features/pagination/codes.js +6 -3
  103. package/dist/features/pagination/codes.js.map +1 -1
  104. package/dist/features/pagination/pagination.cjs +19 -14
  105. package/dist/features/pagination/pagination.cjs.map +1 -1
  106. package/dist/features/pagination/pagination.js +19 -14
  107. package/dist/features/pagination/pagination.js.map +1 -1
  108. package/dist/features/search/codes.cjs +1 -0
  109. package/dist/features/search/codes.cjs.map +1 -1
  110. package/dist/features/search/codes.js +1 -0
  111. package/dist/features/search/codes.js.map +1 -1
  112. package/dist/features/search/search-index.cjs +105 -48
  113. package/dist/features/search/search-index.cjs.map +1 -1
  114. package/dist/features/search/search-index.js +106 -49
  115. package/dist/features/search/search-index.js.map +1 -1
  116. package/dist/features/search/search.cjs +82 -24
  117. package/dist/features/search/search.cjs.map +1 -1
  118. package/dist/features/search/search.js +82 -24
  119. package/dist/features/search/search.js.map +1 -1
  120. package/dist/features/search/skipped-codes.cjs +45 -0
  121. package/dist/features/search/skipped-codes.cjs.map +1 -0
  122. package/dist/features/search/skipped-codes.js +20 -0
  123. package/dist/features/search/skipped-codes.js.map +1 -0
  124. package/dist/features/sorting/codes.cjs +1 -0
  125. package/dist/features/sorting/codes.cjs.map +1 -1
  126. package/dist/features/sorting/codes.js +1 -0
  127. package/dist/features/sorting/codes.js.map +1 -1
  128. package/dist/features/sorting/sorting.cjs +19 -23
  129. package/dist/features/sorting/sorting.cjs.map +1 -1
  130. package/dist/features/sorting/sorting.js +19 -23
  131. package/dist/features/sorting/sorting.js.map +1 -1
  132. package/dist/features/sorting/strategy.cjs +4 -4
  133. package/dist/features/sorting/strategy.cjs.map +1 -1
  134. package/dist/features/sorting/strategy.js +4 -4
  135. package/dist/features/sorting/strategy.js.map +1 -1
  136. package/dist/features/validity/codes.cjs +1 -0
  137. package/dist/features/validity/codes.cjs.map +1 -1
  138. package/dist/features/validity/codes.js +1 -0
  139. package/dist/features/validity/codes.js.map +1 -1
  140. package/dist/features/validity/validity.cjs +3 -3
  141. package/dist/features/validity/validity.cjs.map +1 -1
  142. package/dist/features/validity/validity.js +3 -3
  143. package/dist/features/validity/validity.js.map +1 -1
  144. package/dist/index.d.cts +16 -11
  145. package/dist/index.d.ts +16 -11
  146. package/dist/ingest/data-check-codes.cjs +4 -2
  147. package/dist/ingest/data-check-codes.cjs.map +1 -1
  148. package/dist/ingest/data-check-codes.js +4 -2
  149. package/dist/ingest/data-check-codes.js.map +1 -1
  150. package/dist/ingest/data-checks.cjs +1 -1
  151. package/dist/ingest/data-checks.cjs.map +1 -1
  152. package/dist/ingest/data-checks.js +1 -1
  153. package/dist/ingest/data-checks.js.map +1 -1
  154. package/dist/ingest/row-key-codes.cjs +1 -0
  155. package/dist/ingest/row-key-codes.cjs.map +1 -1
  156. package/dist/ingest/row-key-codes.js +1 -0
  157. package/dist/ingest/row-key-codes.js.map +1 -1
  158. package/dist/ingest/row-key.cjs +23 -20
  159. package/dist/ingest/row-key.cjs.map +1 -1
  160. package/dist/ingest/row-key.js +23 -20
  161. package/dist/ingest/row-key.js.map +1 -1
  162. package/dist/ingest/schema-codes.cjs +1 -0
  163. package/dist/ingest/schema-codes.cjs.map +1 -1
  164. package/dist/ingest/schema-codes.js +1 -0
  165. package/dist/ingest/schema-codes.js.map +1 -1
  166. package/dist/ingest/unique-fields.cjs +2 -2
  167. package/dist/ingest/unique-fields.cjs.map +1 -1
  168. package/dist/ingest/unique-fields.js +2 -2
  169. package/dist/ingest/unique-fields.js.map +1 -1
  170. package/dist/intercept/boundary.cjs +4 -2
  171. package/dist/intercept/boundary.cjs.map +1 -1
  172. package/dist/intercept/boundary.js +4 -2
  173. package/dist/intercept/boundary.js.map +1 -1
  174. package/dist/intercept/cell-batch.cjs +7 -4
  175. package/dist/intercept/cell-batch.cjs.map +1 -1
  176. package/dist/intercept/cell-batch.js +7 -4
  177. package/dist/intercept/cell-batch.js.map +1 -1
  178. package/dist/intercept/codes.cjs +1 -0
  179. package/dist/intercept/codes.cjs.map +1 -1
  180. package/dist/intercept/codes.js +1 -0
  181. package/dist/intercept/codes.js.map +1 -1
  182. package/dist/intercept/fallbacks.cjs +12 -20
  183. package/dist/intercept/fallbacks.cjs.map +1 -1
  184. package/dist/intercept/fallbacks.js +12 -20
  185. package/dist/intercept/fallbacks.js.map +1 -1
  186. package/dist/intercept/runtime.cjs +7 -9
  187. package/dist/intercept/runtime.cjs.map +1 -1
  188. package/dist/intercept/runtime.js +7 -9
  189. package/dist/intercept/runtime.js.map +1 -1
  190. package/dist/messages/index.cjs +4 -1
  191. package/dist/messages/index.cjs.map +1 -1
  192. package/dist/messages/index.js +4 -1
  193. package/dist/messages/index.js.map +1 -1
  194. package/dist/pagination.d.cts +2 -2
  195. package/dist/pagination.d.ts +2 -2
  196. package/dist/pipeline/codes.cjs +1 -0
  197. package/dist/pipeline/codes.cjs.map +1 -1
  198. package/dist/pipeline/codes.js +1 -0
  199. package/dist/pipeline/codes.js.map +1 -1
  200. package/dist/pipeline/column-cache.cjs +94 -76
  201. package/dist/pipeline/column-cache.cjs.map +1 -1
  202. package/dist/pipeline/column-cache.js +92 -75
  203. package/dist/pipeline/column-cache.js.map +1 -1
  204. package/dist/pipeline/dev-checks.cjs +1 -0
  205. package/dist/pipeline/dev-checks.cjs.map +1 -1
  206. package/dist/pipeline/dev-checks.js +1 -0
  207. package/dist/pipeline/dev-checks.js.map +1 -1
  208. package/dist/pipeline/env-codes.cjs +3 -2
  209. package/dist/pipeline/env-codes.cjs.map +1 -1
  210. package/dist/pipeline/env-codes.js +3 -2
  211. package/dist/pipeline/env-codes.js.map +1 -1
  212. package/dist/pipeline/perf-codes.cjs +1 -0
  213. package/dist/pipeline/perf-codes.cjs.map +1 -1
  214. package/dist/pipeline/perf-codes.js +1 -0
  215. package/dist/pipeline/perf-codes.js.map +1 -1
  216. package/dist/pipeline/row.cjs +8 -7
  217. package/dist/pipeline/row.cjs.map +1 -1
  218. package/dist/pipeline/row.js +8 -7
  219. package/dist/pipeline/row.js.map +1 -1
  220. package/dist/pipeline/runner.cjs +17 -9
  221. package/dist/pipeline/runner.cjs.map +1 -1
  222. package/dist/pipeline/runner.js +17 -9
  223. package/dist/pipeline/runner.js.map +1 -1
  224. package/dist/pipeline/slice.cjs +3 -1
  225. package/dist/pipeline/slice.cjs.map +1 -1
  226. package/dist/pipeline/slice.js +3 -1
  227. package/dist/pipeline/slice.js.map +1 -1
  228. package/dist/schema.d.cts +2 -2
  229. package/dist/schema.d.ts +2 -2
  230. package/dist/{search-async-entry-C3mXxLP8.d.cts → search-async-entry-CuLYPfY_.d.cts} +2 -2
  231. package/dist/{search-async-entry-BmQYT120.d.ts → search-async-entry-Cx7xpuVZ.d.ts} +2 -2
  232. package/dist/search-async.d.cts +3 -3
  233. package/dist/search-async.d.ts +3 -3
  234. package/dist/search.d.cts +8 -6
  235. package/dist/search.d.ts +8 -6
  236. package/dist/server.d.cts +3 -3
  237. package/dist/server.d.ts +3 -3
  238. package/dist/sorting.d.cts +6 -3
  239. package/dist/sorting.d.ts +6 -3
  240. package/dist/source/codes.cjs +1 -0
  241. package/dist/source/codes.cjs.map +1 -1
  242. package/dist/source/codes.js +1 -0
  243. package/dist/source/codes.js.map +1 -1
  244. package/dist/source/fetch-codes.cjs +1 -0
  245. package/dist/source/fetch-codes.cjs.map +1 -1
  246. package/dist/source/fetch-codes.js +1 -0
  247. package/dist/source/fetch-codes.js.map +1 -1
  248. package/dist/table/codes.cjs +1 -0
  249. package/dist/table/codes.cjs.map +1 -1
  250. package/dist/table/codes.js +1 -0
  251. package/dist/table/codes.js.map +1 -1
  252. package/dist/table/create-table.cjs +39 -9
  253. package/dist/table/create-table.cjs.map +1 -1
  254. package/dist/table/create-table.js +39 -9
  255. package/dist/table/create-table.js.map +1 -1
  256. package/dist/table/data-mutation.cjs +1 -0
  257. package/dist/table/data-mutation.cjs.map +1 -1
  258. package/dist/table/data-mutation.js +1 -0
  259. package/dist/table/data-mutation.js.map +1 -1
  260. package/dist/table/internals.cjs.map +1 -1
  261. package/dist/table/internals.js.map +1 -1
  262. package/dist/table/ssr-codes.cjs +1 -0
  263. package/dist/table/ssr-codes.cjs.map +1 -1
  264. package/dist/table/ssr-codes.js +1 -0
  265. package/dist/table/ssr-codes.js.map +1 -1
  266. package/dist/table/ssr.cjs +4 -1
  267. package/dist/table/ssr.cjs.map +1 -1
  268. package/dist/table/ssr.js +4 -1
  269. package/dist/table/ssr.js.map +1 -1
  270. package/dist/table/state-feedback.cjs +19 -0
  271. package/dist/table/state-feedback.cjs.map +1 -1
  272. package/dist/table/state-feedback.js +19 -0
  273. package/dist/table/state-feedback.js.map +1 -1
  274. package/dist/table/types.cjs.map +1 -1
  275. package/dist/table/types.js.map +1 -1
  276. package/dist/validity.d.cts +2 -2
  277. package/dist/validity.d.ts +2 -2
  278. package/dist/version.cjs +1 -1
  279. package/dist/version.cjs.map +1 -1
  280. package/dist/version.js +1 -1
  281. package/dist/version.js.map +1 -1
  282. package/dist/versioning/codes.cjs +18 -0
  283. package/dist/versioning/codes.cjs.map +1 -1
  284. package/dist/versioning/codes.js +18 -0
  285. package/dist/versioning/codes.js.map +1 -1
  286. package/dist/versioning/registry.cjs +4 -2
  287. package/dist/versioning/registry.cjs.map +1 -1
  288. package/dist/versioning/registry.js +4 -2
  289. package/dist/versioning/registry.js.map +1 -1
  290. package/package.json +39 -3
@@ -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: "the failed rows are treated as non-matching",
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", "a primitive or a Date", fv);
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", "an array of primitives or Dates", item);
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", `${NUMERIC} or a valid Date bound`, rawMin);
137
- if (max === false) return bad("range", `${NUMERIC} or a valid Date bound`, rawMax);
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", `min and max of the same type (${min.family})`, rawMax);
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", "a primitive or a Date", fv);
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", "an array of primitives or Dates", item);
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", `${NUMERIC} or a valid Date bound`, rawMin);
109
- if (max === false) return bad("range", `${NUMERIC} or a valid Date bound`, rawMax);
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", `min and max of the same type (${min.family})`, rawMax);
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,15 +25,18 @@ __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"
31
32
  };
32
33
  var paginationMessages = {
33
34
  DK_INVALID_PAGINATION: {
34
- problem: (d) => d.source === "state" ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}` : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,
35
+ problem: (d) => d.source === "option" ? `pagination({ pageSize: ${d.received} }); pageSize must be ${ACCEPTED.pageSize}` : d.source === "state" ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}` : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,
35
36
  impact: (d) => d.fallback,
36
- fix: (d) => d.source === "state" ? [
37
+ fix: (d) => d.source === "option" ? [
38
+ `pass ${ACCEPTED.pageSize} to pagination({ pageSize }), or leave it out for the default of 10`
39
+ ] : d.source === "state" ? [
37
40
  `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and the controlled options.state)`,
38
41
  d.field === "pageIndex" ? "use table.setPageIndex(i): it clamps to the existing pages" : "use table.setPageSize(n): it also keeps the first visible row on screen"
39
42
  ] : [
@@ -54,7 +57,7 @@ var paginationDiagnostics = [
54
57
  {
55
58
  code: "DK_INVALID_PAGINATION",
56
59
  level: "warn",
57
- where: (d) => d.source === "state" ? { kind: "option", path: `state.pagination.${d.field}` } : { kind: "operation", op: `table.${d.source}` }
60
+ where: (d) => d.source === "option" ? { kind: "feature", name: "pagination" } : d.source === "state" ? { kind: "option", path: `state.pagination.${d.field}` } : { kind: "operation", op: `table.${d.source}` }
58
61
  },
59
62
  process.env.NODE_ENV !== "production" && paginationMessages.DK_INVALID_PAGINATION
60
63
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/pagination/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_INVALID_PAGINATION: {\n /** The API method that received the value, or `'state'` for a slice written to the store. */\n source: 'setPageIndex' | 'setPageSize' | 'state'\n field: 'pageIndex' | 'pageSize'\n /** The value's type only (`describeValue`), plus the number itself when it is one. */\n received: string\n /** What was done instead. */\n fallback: string\n }\n DK_ROW_COUNT_MISSING: {\n /** `data.length`, used as the total instead. */\n rows: number\n }\n }\n}\n\nconst ACCEPTED = {\n pageIndex: 'an integer ≥ 0',\n pageSize: 'an integer > 0',\n} as const\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const paginationMessages: DiagnosticMessages<\n 'DK_INVALID_PAGINATION' | 'DK_ROW_COUNT_MISSING'\n> = {\n DK_INVALID_PAGINATION: {\n problem: (d) =>\n d.source === 'state'\n ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}`\n : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,\n impact: (d) => d.fallback,\n fix: (d) =>\n d.source === 'state'\n ? [\n `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and ` +\n 'the controlled options.state)',\n d.field === 'pageIndex'\n ? 'use table.setPageIndex(i): it clamps to the existing pages'\n : 'use table.setPageSize(n): it also keeps the first visible row on screen',\n ]\n : [\n `pass ${ACCEPTED[d.field]} to ${d.source}()` +\n (d.received.startsWith('string') ? ' (convert input values with Number(...))' : ''),\n ],\n },\n DK_ROW_COUNT_MISSING: {\n problem: () =>\n 'pagination is in server mode, but there is no fetcher and no rowCount option: the ' +\n 'total number of rows is unknown',\n impact: (d) =>\n `the page count falls back to data.length (${d.rows}) as the total: one page, ` +\n 'canNextPage() is false',\n fix: () => [\n 'pass the total from your API: setOptions({ data: page.rows, rowCount: page.total })',\n \"or pass a fetcher that resolves { rows, rowCount }, or use pagination({ mode: 'client' }) \" +\n 'with all rows as data',\n ],\n },\n}\n\n/** Codes owned by the `pagination` feature (§6.5). */\nexport const paginationDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INVALID_PAGINATION',\n level: 'warn',\n where: (d) =>\n d.source === 'state'\n ? { kind: 'option', path: `state.pagination.${d.field}` }\n : { kind: 'operation', op: `table.${d.source}` },\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_INVALID_PAGINATION,\n ),\n defineDiagnostic(\n {\n code: 'DK_ROW_COUNT_MISSING',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'rowCount' }),\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_ROW_COUNT_MISSING,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;AAoB1D,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,IAAM,qBAET;AAAA,EACF,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,UACT,4BAA4B,EAAE,KAAK,MAAM,EAAE,QAAQ,gBAAgB,SAAS,EAAE,KAAK,CAAC,KACpF,GAAG,EAAE,MAAM,eAAe,EAAE,QAAQ,KAAK,EAAE,KAAK,YAAY,SAAS,EAAE,KAAK,CAAC;AAAA,IACnF,QAAQ,CAAC,MAAM,EAAE;AAAA,IACjB,KAAK,CAAC,MACJ,EAAE,WAAW,UACT;AAAA,MACE,oBAAoB,EAAE,KAAK,OAAO,SAAS,EAAE,KAAK,CAAC;AAAA,MAEnD,EAAE,UAAU,cACR,+DACA;AAAA,IACN,IACA;AAAA,MACE,QAAQ,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,QACrC,EAAE,SAAS,WAAW,QAAQ,IAAI,6CAA6C;AAAA,IACpF;AAAA,EACR;AAAA,EACA,sBAAsB;AAAA,IACpB,SAAS,MACP;AAAA,IAEF,QAAQ,CAAC,MACP,6CAA6C,EAAE,IAAI;AAAA,IAErD,KAAK,MAAM;AAAA,MACT;AAAA,MACA;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,wBAAwB;AAAA,MACnC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,UACT,EAAE,MAAM,UAAU,MAAM,oBAAoB,EAAE,KAAK,GAAG,IACtD,EAAE,MAAM,aAAa,IAAI,SAAS,EAAE,MAAM,GAAG;AAAA,IACrD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,WAAW;AAAA,IACnD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/features/pagination/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_INVALID_PAGINATION: {\n /**\n * The API method that received the value, `'state'` for a slice written to the store, or\n * `'option'` for `pagination({ pageSize })` (checked here in production, where the option\n * validator is off).\n */\n source: 'setPageIndex' | 'setPageSize' | 'state' | 'option'\n field: 'pageIndex' | 'pageSize'\n /** The value's type only (`describeValue`), plus the number itself when it is one. */\n received: string\n /** What was done instead. */\n fallback: string\n }\n DK_ROW_COUNT_MISSING: {\n /** `data.length`, used as the total instead. */\n rows: number\n }\n }\n}\n\nconst ACCEPTED = {\n pageIndex: 'an integer ≥ 0',\n pageSize: 'an integer > 0',\n} as const\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const paginationMessages: DiagnosticMessages<\n 'DK_INVALID_PAGINATION' | 'DK_ROW_COUNT_MISSING'\n> = {\n DK_INVALID_PAGINATION: {\n problem: (d) =>\n d.source === 'option'\n ? `pagination({ pageSize: ${d.received} }); pageSize must be ${ACCEPTED.pageSize}`\n : d.source === 'state'\n ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}`\n : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,\n impact: (d) => d.fallback,\n fix: (d) =>\n d.source === 'option'\n ? [\n `pass ${ACCEPTED.pageSize} to pagination({ pageSize }), or leave it out for the ` +\n 'default of 10',\n ]\n : d.source === 'state'\n ? [\n `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and ` +\n 'the controlled options.state)',\n d.field === 'pageIndex'\n ? 'use table.setPageIndex(i): it clamps to the existing pages'\n : 'use table.setPageSize(n): it also keeps the first visible row on screen',\n ]\n : [\n `pass ${ACCEPTED[d.field]} to ${d.source}()` +\n (d.received.startsWith('string') ? ' (convert input values with Number(...))' : ''),\n ],\n },\n DK_ROW_COUNT_MISSING: {\n problem: () =>\n 'pagination is in server mode, but there is no fetcher and no rowCount option: the ' +\n 'total number of rows is unknown',\n impact: (d) =>\n `the page count falls back to data.length (${d.rows}) as the total: one page, ` +\n 'canNextPage() is false',\n fix: () => [\n 'pass the total from your API: setOptions({ data: page.rows, rowCount: page.total })',\n \"or pass a fetcher that resolves { rows, rowCount }, or use pagination({ mode: 'client' }) \" +\n 'with all rows as data',\n ],\n },\n}\n\n/** Codes owned by the `pagination` feature (§6.5). */\nexport const paginationDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INVALID_PAGINATION',\n level: 'warn',\n where: (d) =>\n d.source === 'option'\n ? { kind: 'feature', name: 'pagination' }\n : d.source === 'state'\n ? { kind: 'option', path: `state.pagination.${d.field}` }\n : { kind: 'operation', op: `table.${d.source}` },\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_INVALID_PAGINATION,\n ),\n defineDiagnostic(\n {\n code: 'DK_ROW_COUNT_MISSING',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'rowCount' }),\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_ROW_COUNT_MISSING,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;AAC1D,2BAAO;AAwBP,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,IAAM,qBAET;AAAA,EACF,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,WACT,0BAA0B,EAAE,QAAQ,yBAAyB,SAAS,QAAQ,KAC9E,EAAE,WAAW,UACX,4BAA4B,EAAE,KAAK,MAAM,EAAE,QAAQ,gBAAgB,SAAS,EAAE,KAAK,CAAC,KACpF,GAAG,EAAE,MAAM,eAAe,EAAE,QAAQ,KAAK,EAAE,KAAK,YAAY,SAAS,EAAE,KAAK,CAAC;AAAA,IACrF,QAAQ,CAAC,MAAM,EAAE;AAAA,IACjB,KAAK,CAAC,MACJ,EAAE,WAAW,WACT;AAAA,MACE,QAAQ,SAAS,QAAQ;AAAA,IAE3B,IACA,EAAE,WAAW,UACX;AAAA,MACE,oBAAoB,EAAE,KAAK,OAAO,SAAS,EAAE,KAAK,CAAC;AAAA,MAEnD,EAAE,UAAU,cACR,+DACA;AAAA,IACN,IACA;AAAA,MACE,QAAQ,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,QACrC,EAAE,SAAS,WAAW,QAAQ,IAAI,6CAA6C;AAAA,IACpF;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,SAAS,MACP;AAAA,IAEF,QAAQ,CAAC,MACP,6CAA6C,EAAE,IAAI;AAAA,IAErD,KAAK,MAAM;AAAA,MACT;AAAA,MACA;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,wBAAwB;AAAA,MACnC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,WACT,EAAE,MAAM,WAAW,MAAM,aAAa,IACtC,EAAE,WAAW,UACX,EAAE,MAAM,UAAU,MAAM,oBAAoB,EAAE,KAAK,GAAG,IACtD,EAAE,MAAM,aAAa,IAAI,SAAS,EAAE,MAAM,GAAG;AAAA,IACvD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,WAAW;AAAA,IACnD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AACF;","names":[]}
@@ -1,14 +1,17 @@
1
1
  // src/features/pagination/codes.ts
2
2
  import { defineDiagnostic } from "../../diagnostics/catalog.js";
3
+ import "../../diagnostics/process-guard.js";
3
4
  var ACCEPTED = {
4
5
  pageIndex: "an integer \u2265 0",
5
6
  pageSize: "an integer > 0"
6
7
  };
7
8
  var paginationMessages = {
8
9
  DK_INVALID_PAGINATION: {
9
- problem: (d) => d.source === "state" ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}` : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,
10
+ problem: (d) => d.source === "option" ? `pagination({ pageSize: ${d.received} }); pageSize must be ${ACCEPTED.pageSize}` : d.source === "state" ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}` : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,
10
11
  impact: (d) => d.fallback,
11
- fix: (d) => d.source === "state" ? [
12
+ fix: (d) => d.source === "option" ? [
13
+ `pass ${ACCEPTED.pageSize} to pagination({ pageSize }), or leave it out for the default of 10`
14
+ ] : d.source === "state" ? [
12
15
  `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and the controlled options.state)`,
13
16
  d.field === "pageIndex" ? "use table.setPageIndex(i): it clamps to the existing pages" : "use table.setPageSize(n): it also keeps the first visible row on screen"
14
17
  ] : [
@@ -29,7 +32,7 @@ var paginationDiagnostics = [
29
32
  {
30
33
  code: "DK_INVALID_PAGINATION",
31
34
  level: "warn",
32
- where: (d) => d.source === "state" ? { kind: "option", path: `state.pagination.${d.field}` } : { kind: "operation", op: `table.${d.source}` }
35
+ where: (d) => d.source === "option" ? { kind: "feature", name: "pagination" } : d.source === "state" ? { kind: "option", path: `state.pagination.${d.field}` } : { kind: "operation", op: `table.${d.source}` }
33
36
  },
34
37
  process.env.NODE_ENV !== "production" && paginationMessages.DK_INVALID_PAGINATION
35
38
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/features/pagination/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_INVALID_PAGINATION: {\n /** The API method that received the value, or `'state'` for a slice written to the store. */\n source: 'setPageIndex' | 'setPageSize' | 'state'\n field: 'pageIndex' | 'pageSize'\n /** The value's type only (`describeValue`), plus the number itself when it is one. */\n received: string\n /** What was done instead. */\n fallback: string\n }\n DK_ROW_COUNT_MISSING: {\n /** `data.length`, used as the total instead. */\n rows: number\n }\n }\n}\n\nconst ACCEPTED = {\n pageIndex: 'an integer ≥ 0',\n pageSize: 'an integer > 0',\n} as const\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const paginationMessages: DiagnosticMessages<\n 'DK_INVALID_PAGINATION' | 'DK_ROW_COUNT_MISSING'\n> = {\n DK_INVALID_PAGINATION: {\n problem: (d) =>\n d.source === 'state'\n ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}`\n : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,\n impact: (d) => d.fallback,\n fix: (d) =>\n d.source === 'state'\n ? [\n `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and ` +\n 'the controlled options.state)',\n d.field === 'pageIndex'\n ? 'use table.setPageIndex(i): it clamps to the existing pages'\n : 'use table.setPageSize(n): it also keeps the first visible row on screen',\n ]\n : [\n `pass ${ACCEPTED[d.field]} to ${d.source}()` +\n (d.received.startsWith('string') ? ' (convert input values with Number(...))' : ''),\n ],\n },\n DK_ROW_COUNT_MISSING: {\n problem: () =>\n 'pagination is in server mode, but there is no fetcher and no rowCount option: the ' +\n 'total number of rows is unknown',\n impact: (d) =>\n `the page count falls back to data.length (${d.rows}) as the total: one page, ` +\n 'canNextPage() is false',\n fix: () => [\n 'pass the total from your API: setOptions({ data: page.rows, rowCount: page.total })',\n \"or pass a fetcher that resolves { rows, rowCount }, or use pagination({ mode: 'client' }) \" +\n 'with all rows as data',\n ],\n },\n}\n\n/** Codes owned by the `pagination` feature (§6.5). */\nexport const paginationDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INVALID_PAGINATION',\n level: 'warn',\n where: (d) =>\n d.source === 'state'\n ? { kind: 'option', path: `state.pagination.${d.field}` }\n : { kind: 'operation', op: `table.${d.source}` },\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_INVALID_PAGINATION,\n ),\n defineDiagnostic(\n {\n code: 'DK_ROW_COUNT_MISSING',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'rowCount' }),\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_ROW_COUNT_MISSING,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;AAoB1D,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,IAAM,qBAET;AAAA,EACF,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,UACT,4BAA4B,EAAE,KAAK,MAAM,EAAE,QAAQ,gBAAgB,SAAS,EAAE,KAAK,CAAC,KACpF,GAAG,EAAE,MAAM,eAAe,EAAE,QAAQ,KAAK,EAAE,KAAK,YAAY,SAAS,EAAE,KAAK,CAAC;AAAA,IACnF,QAAQ,CAAC,MAAM,EAAE;AAAA,IACjB,KAAK,CAAC,MACJ,EAAE,WAAW,UACT;AAAA,MACE,oBAAoB,EAAE,KAAK,OAAO,SAAS,EAAE,KAAK,CAAC;AAAA,MAEnD,EAAE,UAAU,cACR,+DACA;AAAA,IACN,IACA;AAAA,MACE,QAAQ,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,QACrC,EAAE,SAAS,WAAW,QAAQ,IAAI,6CAA6C;AAAA,IACpF;AAAA,EACR;AAAA,EACA,sBAAsB;AAAA,IACpB,SAAS,MACP;AAAA,IAEF,QAAQ,CAAC,MACP,6CAA6C,EAAE,IAAI;AAAA,IAErD,KAAK,MAAM;AAAA,MACT;AAAA,MACA;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,wBAAwB;AAAA,EACnC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,UACT,EAAE,MAAM,UAAU,MAAM,oBAAoB,EAAE,KAAK,GAAG,IACtD,EAAE,MAAM,aAAa,IAAI,SAAS,EAAE,MAAM,GAAG;AAAA,IACrD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,WAAW;AAAA,IACnD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/features/pagination/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../../diagnostics/catalog'\nimport '../../diagnostics/process-guard'\n\ndeclare module '../../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_INVALID_PAGINATION: {\n /**\n * The API method that received the value, `'state'` for a slice written to the store, or\n * `'option'` for `pagination({ pageSize })` (checked here in production, where the option\n * validator is off).\n */\n source: 'setPageIndex' | 'setPageSize' | 'state' | 'option'\n field: 'pageIndex' | 'pageSize'\n /** The value's type only (`describeValue`), plus the number itself when it is one. */\n received: string\n /** What was done instead. */\n fallback: string\n }\n DK_ROW_COUNT_MISSING: {\n /** `data.length`, used as the total instead. */\n rows: number\n }\n }\n}\n\nconst ACCEPTED = {\n pageIndex: 'an integer ≥ 0',\n pageSize: 'an integer > 0',\n} as const\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const paginationMessages: DiagnosticMessages<\n 'DK_INVALID_PAGINATION' | 'DK_ROW_COUNT_MISSING'\n> = {\n DK_INVALID_PAGINATION: {\n problem: (d) =>\n d.source === 'option'\n ? `pagination({ pageSize: ${d.received} }); pageSize must be ${ACCEPTED.pageSize}`\n : d.source === 'state'\n ? `the pagination state has ${d.field} = ${d.received}; it must be ${ACCEPTED[d.field]}`\n : `${d.source}() received ${d.received}; ${d.field} must be ${ACCEPTED[d.field]}`,\n impact: (d) => d.fallback,\n fix: (d) =>\n d.source === 'option'\n ? [\n `pass ${ACCEPTED.pageSize} to pagination({ pageSize }), or leave it out for the ` +\n 'default of 10',\n ]\n : d.source === 'state'\n ? [\n `write pagination.${d.field} as ${ACCEPTED[d.field]} (check initialState, setState and ` +\n 'the controlled options.state)',\n d.field === 'pageIndex'\n ? 'use table.setPageIndex(i): it clamps to the existing pages'\n : 'use table.setPageSize(n): it also keeps the first visible row on screen',\n ]\n : [\n `pass ${ACCEPTED[d.field]} to ${d.source}()` +\n (d.received.startsWith('string') ? ' (convert input values with Number(...))' : ''),\n ],\n },\n DK_ROW_COUNT_MISSING: {\n problem: () =>\n 'pagination is in server mode, but there is no fetcher and no rowCount option: the ' +\n 'total number of rows is unknown',\n impact: (d) =>\n `the page count falls back to data.length (${d.rows}) as the total: one page, ` +\n 'canNextPage() is false',\n fix: () => [\n 'pass the total from your API: setOptions({ data: page.rows, rowCount: page.total })',\n \"or pass a fetcher that resolves { rows, rowCount }, or use pagination({ mode: 'client' }) \" +\n 'with all rows as data',\n ],\n },\n}\n\n/** Codes owned by the `pagination` feature (§6.5). */\nexport const paginationDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INVALID_PAGINATION',\n level: 'warn',\n where: (d) =>\n d.source === 'option'\n ? { kind: 'feature', name: 'pagination' }\n : d.source === 'state'\n ? { kind: 'option', path: `state.pagination.${d.field}` }\n : { kind: 'operation', op: `table.${d.source}` },\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_INVALID_PAGINATION,\n ),\n defineDiagnostic(\n {\n code: 'DK_ROW_COUNT_MISSING',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'rowCount' }),\n },\n (process.env.NODE_ENV !== \"production\") && paginationMessages.DK_ROW_COUNT_MISSING,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;AAC1D,OAAO;AAwBP,IAAM,WAAW;AAAA,EACf,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,IAAM,qBAET;AAAA,EACF,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,WACT,0BAA0B,EAAE,QAAQ,yBAAyB,SAAS,QAAQ,KAC9E,EAAE,WAAW,UACX,4BAA4B,EAAE,KAAK,MAAM,EAAE,QAAQ,gBAAgB,SAAS,EAAE,KAAK,CAAC,KACpF,GAAG,EAAE,MAAM,eAAe,EAAE,QAAQ,KAAK,EAAE,KAAK,YAAY,SAAS,EAAE,KAAK,CAAC;AAAA,IACrF,QAAQ,CAAC,MAAM,EAAE;AAAA,IACjB,KAAK,CAAC,MACJ,EAAE,WAAW,WACT;AAAA,MACE,QAAQ,SAAS,QAAQ;AAAA,IAE3B,IACA,EAAE,WAAW,UACX;AAAA,MACE,oBAAoB,EAAE,KAAK,OAAO,SAAS,EAAE,KAAK,CAAC;AAAA,MAEnD,EAAE,UAAU,cACR,+DACA;AAAA,IACN,IACA;AAAA,MACE,QAAQ,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,QACrC,EAAE,SAAS,WAAW,QAAQ,IAAI,6CAA6C;AAAA,IACpF;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,SAAS,MACP;AAAA,IAEF,QAAQ,CAAC,MACP,6CAA6C,EAAE,IAAI;AAAA,IAErD,KAAK,MAAM;AAAA,MACT;AAAA,MACA;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,wBAAwB;AAAA,EACnC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,WACT,EAAE,MAAM,WAAW,MAAM,aAAa,IACtC,EAAE,WAAW,UACX,EAAE,MAAM,UAAU,MAAM,oBAAoB,EAAE,KAAK,GAAG,IACtD,EAAE,MAAM,aAAa,IAAI,SAAS,EAAE,MAAM,GAAG;AAAA,IACvD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,WAAW;AAAA,IACnD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,mBAAmB;AAAA,EAChE;AACF;","names":[]}
@@ -25,6 +25,7 @@ __export(pagination_exports, {
25
25
  module.exports = __toCommonJS(pagination_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_redact = require("../../diagnostics/redact.cjs");
29
30
  var import_define = require("../../feature/define.cjs");
30
31
  var import_define2 = require("../../intercept/define.cjs");
@@ -54,7 +55,8 @@ function effectiveIndex(pageIndex, pageSize, rowCount) {
54
55
  return pageIndex > last ? last : pageIndex;
55
56
  }
56
57
  var pagination = (opts = {}) => {
57
- const initialSize = opts.pageSize ?? DEFAULT_PAGE_SIZE;
58
+ const badSize = opts.pageSize !== void 0 && !isSize(opts.pageSize);
59
+ const initialSize = badSize ? DEFAULT_PAGE_SIZE : opts.pageSize ?? DEFAULT_PAGE_SIZE;
58
60
  const autoReset = opts.autoResetPageIndex !== false;
59
61
  const mode = opts.mode ?? "client";
60
62
  const hosts = /* @__PURE__ */ new WeakMap();
@@ -89,8 +91,8 @@ var pagination = (opts = {}) => {
89
91
  let missingReported = false;
90
92
  const rowCount = () => {
91
93
  if (mode !== "server") return table.getRowModel().filteredCount;
92
- const n = internals.rowCount();
93
- if (n !== void 0 || internals.hasFetcher()) return n;
94
+ const n2 = internals.rowCount();
95
+ if (n2 !== void 0 || internals.hasFetcher()) return n2;
94
96
  const rows = table.getRowModel().totalCount;
95
97
  if (!missingReported) {
96
98
  missingReported = true;
@@ -101,17 +103,17 @@ var pagination = (opts = {}) => {
101
103
  function sanitize(s, prev) {
102
104
  const fallbackSize = isSize(prev?.pageSize) ? prev.pageSize : initialSize;
103
105
  if (s === null || typeof s !== "object") {
104
- report("state", "pageIndex", s, "the pagination state was reset to its initial value");
106
+ report("state", "pageIndex", s, "page-reset");
105
107
  return { pageIndex: 0, pageSize: initialSize };
106
108
  }
107
109
  const { pageIndex, pageSize } = s;
108
110
  let fixed2;
109
111
  if (!isIndex(pageIndex)) {
110
- report("state", "pageIndex", pageIndex, "pageIndex was replaced by 0");
112
+ report("state", "pageIndex", pageIndex, "page-index-0");
111
113
  fixed2 = { ...s, pageIndex: 0 };
112
114
  }
113
115
  if (!isSize(pageSize)) {
114
- report("state", "pageSize", pageSize, `pageSize was replaced by ${fallbackSize}`);
116
+ report("state", "pageSize", pageSize, `page-size|${fallbackSize}`);
115
117
  fixed2 = { ...fixed2 ?? s, pageSize: fallbackSize };
116
118
  }
117
119
  return fixed2;
@@ -119,9 +121,8 @@ var pagination = (opts = {}) => {
119
121
  function report(source, field, value, fallback) {
120
122
  ctx.emit("DK_INVALID_PAGINATION", { source, field, received: received(value), fallback });
121
123
  }
122
- function current() {
124
+ function current(count = rowCount()) {
123
125
  const s = ctx.getState();
124
- const count = rowCount();
125
126
  const index = count === void 0 ? s.pageIndex : effectiveIndex(s.pageIndex, s.pageSize, count);
126
127
  return { s, count, index };
127
128
  }
@@ -148,23 +149,27 @@ var pagination = (opts = {}) => {
148
149
  }
149
150
  }
150
151
  });
152
+ if (badSize) report("option", "pageSize", opts.pageSize, `pageSize ${initialSize} was used`);
151
153
  const fixed = sanitize(ctx.getState(), void 0);
152
154
  if (fixed) ctx.setState(fixed);
153
- table.subscribe(() => {
154
- const { s, index } = current();
155
- if (index !== s.pageIndex) ctx.setState({ ...s, pageIndex: index });
156
- });
155
+ const clamp = (count) => {
156
+ const { s, index } = current(count);
157
+ if (index < s.pageIndex) ctx.setState({ ...s, pageIndex: index });
158
+ };
159
+ const n = mode === "server" ? internals.rowCount() : table.getOptions().data?.length;
160
+ if (n) clamp(n);
161
+ table.subscribe(() => clamp());
157
162
  return {
158
163
  setPageIndex(index) {
159
164
  if (!Number.isInteger(index)) {
160
- report("setPageIndex", "pageIndex", index, "the call was ignored");
165
+ report("setPageIndex", "pageIndex", index, "call-ignored");
161
166
  return;
162
167
  }
163
168
  goTo(index);
164
169
  },
165
170
  setPageSize(size) {
166
171
  if (!isSize(size)) {
167
- report("setPageSize", "pageSize", size, "the call was ignored");
172
+ report("setPageSize", "pageSize", size, "call-ignored");
168
173
  return;
169
174
  }
170
175
  const { s, index } = current();