@datakable/core 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) 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-OsjAR3Wz.d.cts → codes-B9bCK73-.d.cts} +30 -4
  11. package/dist/{codes-OsjAR3Wz.d.ts → codes-B9bCK73-.d.ts} +30 -4
  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 +4 -1
  20. package/dist/diagnostics/build.cjs.map +1 -1
  21. package/dist/diagnostics/build.js +4 -1
  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 +5 -4
  48. package/dist/diagnostics/hub.cjs.map +1 -1
  49. package/dist/diagnostics/hub.js +5 -4
  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-vkD2cewE.d.cts → explain-Q8Cg0cZy.d.cts} +13 -4
  73. package/dist/{explain-Dn99IQt0.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/install.cjs +4 -3
  79. package/dist/feature/install.cjs.map +1 -1
  80. package/dist/feature/install.js +4 -3
  81. package/dist/feature/install.js.map +1 -1
  82. package/dist/features/column-filters/codes.cjs +1 -0
  83. package/dist/features/column-filters/codes.cjs.map +1 -1
  84. package/dist/features/column-filters/codes.js +1 -0
  85. package/dist/features/column-filters/codes.js.map +1 -1
  86. package/dist/features/column-filters/column-filters.cjs +4 -2
  87. package/dist/features/column-filters/column-filters.cjs.map +1 -1
  88. package/dist/features/column-filters/column-filters.js +4 -2
  89. package/dist/features/column-filters/column-filters.js.map +1 -1
  90. package/dist/features/column-filters/match.cjs +5 -5
  91. package/dist/features/column-filters/match.cjs.map +1 -1
  92. package/dist/features/column-filters/match.js +5 -5
  93. package/dist/features/column-filters/match.js.map +1 -1
  94. package/dist/features/pagination/codes.cjs +1 -0
  95. package/dist/features/pagination/codes.cjs.map +1 -1
  96. package/dist/features/pagination/codes.js +1 -0
  97. package/dist/features/pagination/codes.js.map +1 -1
  98. package/dist/features/pagination/pagination.cjs +16 -13
  99. package/dist/features/pagination/pagination.cjs.map +1 -1
  100. package/dist/features/pagination/pagination.js +16 -13
  101. package/dist/features/pagination/pagination.js.map +1 -1
  102. package/dist/features/search/codes.cjs +1 -0
  103. package/dist/features/search/codes.cjs.map +1 -1
  104. package/dist/features/search/codes.js +1 -0
  105. package/dist/features/search/codes.js.map +1 -1
  106. package/dist/features/search/search-index.cjs +68 -15
  107. package/dist/features/search/search-index.cjs.map +1 -1
  108. package/dist/features/search/search-index.js +68 -15
  109. package/dist/features/search/search-index.js.map +1 -1
  110. package/dist/features/search/search.cjs +41 -9
  111. package/dist/features/search/search.cjs.map +1 -1
  112. package/dist/features/search/search.js +41 -9
  113. package/dist/features/search/search.js.map +1 -1
  114. package/dist/features/search/skipped-codes.cjs +45 -0
  115. package/dist/features/search/skipped-codes.cjs.map +1 -0
  116. package/dist/features/search/skipped-codes.js +20 -0
  117. package/dist/features/search/skipped-codes.js.map +1 -0
  118. package/dist/features/sorting/codes.cjs +1 -0
  119. package/dist/features/sorting/codes.cjs.map +1 -1
  120. package/dist/features/sorting/codes.js +1 -0
  121. package/dist/features/sorting/codes.js.map +1 -1
  122. package/dist/features/sorting/sorting.cjs +16 -20
  123. package/dist/features/sorting/sorting.cjs.map +1 -1
  124. package/dist/features/sorting/sorting.js +16 -20
  125. package/dist/features/sorting/sorting.js.map +1 -1
  126. package/dist/features/sorting/strategy.cjs +4 -4
  127. package/dist/features/sorting/strategy.cjs.map +1 -1
  128. package/dist/features/sorting/strategy.js +4 -4
  129. package/dist/features/sorting/strategy.js.map +1 -1
  130. package/dist/features/validity/codes.cjs +1 -0
  131. package/dist/features/validity/codes.cjs.map +1 -1
  132. package/dist/features/validity/codes.js +1 -0
  133. package/dist/features/validity/codes.js.map +1 -1
  134. package/dist/features/validity/validity.cjs +3 -3
  135. package/dist/features/validity/validity.cjs.map +1 -1
  136. package/dist/features/validity/validity.js +3 -3
  137. package/dist/features/validity/validity.js.map +1 -1
  138. package/dist/index.d.cts +13 -10
  139. package/dist/index.d.ts +13 -10
  140. package/dist/ingest/data-check-codes.cjs +4 -2
  141. package/dist/ingest/data-check-codes.cjs.map +1 -1
  142. package/dist/ingest/data-check-codes.js +4 -2
  143. package/dist/ingest/data-check-codes.js.map +1 -1
  144. package/dist/ingest/data-checks.cjs +1 -1
  145. package/dist/ingest/data-checks.cjs.map +1 -1
  146. package/dist/ingest/data-checks.js +1 -1
  147. package/dist/ingest/data-checks.js.map +1 -1
  148. package/dist/ingest/row-key-codes.cjs +1 -0
  149. package/dist/ingest/row-key-codes.cjs.map +1 -1
  150. package/dist/ingest/row-key-codes.js +1 -0
  151. package/dist/ingest/row-key-codes.js.map +1 -1
  152. package/dist/ingest/row-key.cjs +23 -20
  153. package/dist/ingest/row-key.cjs.map +1 -1
  154. package/dist/ingest/row-key.js +23 -20
  155. package/dist/ingest/row-key.js.map +1 -1
  156. package/dist/ingest/schema-codes.cjs +1 -0
  157. package/dist/ingest/schema-codes.cjs.map +1 -1
  158. package/dist/ingest/schema-codes.js +1 -0
  159. package/dist/ingest/schema-codes.js.map +1 -1
  160. package/dist/ingest/unique-fields.cjs +2 -2
  161. package/dist/ingest/unique-fields.cjs.map +1 -1
  162. package/dist/ingest/unique-fields.js +2 -2
  163. package/dist/ingest/unique-fields.js.map +1 -1
  164. package/dist/intercept/boundary.cjs +4 -2
  165. package/dist/intercept/boundary.cjs.map +1 -1
  166. package/dist/intercept/boundary.js +4 -2
  167. package/dist/intercept/boundary.js.map +1 -1
  168. package/dist/intercept/cell-batch.cjs +4 -4
  169. package/dist/intercept/cell-batch.cjs.map +1 -1
  170. package/dist/intercept/cell-batch.js +4 -4
  171. package/dist/intercept/cell-batch.js.map +1 -1
  172. package/dist/intercept/codes.cjs +1 -0
  173. package/dist/intercept/codes.cjs.map +1 -1
  174. package/dist/intercept/codes.js +1 -0
  175. package/dist/intercept/codes.js.map +1 -1
  176. package/dist/intercept/fallbacks.cjs +12 -20
  177. package/dist/intercept/fallbacks.cjs.map +1 -1
  178. package/dist/intercept/fallbacks.js +12 -20
  179. package/dist/intercept/fallbacks.js.map +1 -1
  180. package/dist/intercept/runtime.cjs +7 -9
  181. package/dist/intercept/runtime.cjs.map +1 -1
  182. package/dist/intercept/runtime.js +7 -9
  183. package/dist/intercept/runtime.js.map +1 -1
  184. package/dist/messages/index.cjs +4 -1
  185. package/dist/messages/index.cjs.map +1 -1
  186. package/dist/messages/index.js +4 -1
  187. package/dist/messages/index.js.map +1 -1
  188. package/dist/pagination.d.cts +2 -2
  189. package/dist/pagination.d.ts +2 -2
  190. package/dist/pipeline/codes.cjs +1 -0
  191. package/dist/pipeline/codes.cjs.map +1 -1
  192. package/dist/pipeline/codes.js +1 -0
  193. package/dist/pipeline/codes.js.map +1 -1
  194. package/dist/pipeline/column-cache.cjs +94 -76
  195. package/dist/pipeline/column-cache.cjs.map +1 -1
  196. package/dist/pipeline/column-cache.js +92 -75
  197. package/dist/pipeline/column-cache.js.map +1 -1
  198. package/dist/pipeline/dev-checks.cjs +1 -0
  199. package/dist/pipeline/dev-checks.cjs.map +1 -1
  200. package/dist/pipeline/dev-checks.js +1 -0
  201. package/dist/pipeline/dev-checks.js.map +1 -1
  202. package/dist/pipeline/env-codes.cjs +3 -2
  203. package/dist/pipeline/env-codes.cjs.map +1 -1
  204. package/dist/pipeline/env-codes.js +3 -2
  205. package/dist/pipeline/env-codes.js.map +1 -1
  206. package/dist/pipeline/perf-codes.cjs +1 -0
  207. package/dist/pipeline/perf-codes.cjs.map +1 -1
  208. package/dist/pipeline/perf-codes.js +1 -0
  209. package/dist/pipeline/perf-codes.js.map +1 -1
  210. package/dist/pipeline/runner.cjs +17 -9
  211. package/dist/pipeline/runner.cjs.map +1 -1
  212. package/dist/pipeline/runner.js +17 -9
  213. package/dist/pipeline/runner.js.map +1 -1
  214. package/dist/pipeline/slice.cjs +3 -1
  215. package/dist/pipeline/slice.cjs.map +1 -1
  216. package/dist/pipeline/slice.js +3 -1
  217. package/dist/pipeline/slice.js.map +1 -1
  218. package/dist/schema.d.cts +2 -2
  219. package/dist/schema.d.ts +2 -2
  220. package/dist/{search-async-entry-v5JdEk5Y.d.cts → search-async-entry-CuLYPfY_.d.cts} +2 -2
  221. package/dist/{search-async-entry-DGSw2pDL.d.ts → search-async-entry-Cx7xpuVZ.d.ts} +2 -2
  222. package/dist/search-async.d.cts +3 -3
  223. package/dist/search-async.d.ts +3 -3
  224. package/dist/search.d.cts +8 -6
  225. package/dist/search.d.ts +8 -6
  226. package/dist/server.d.cts +3 -3
  227. package/dist/server.d.ts +3 -3
  228. package/dist/sorting.d.cts +2 -2
  229. package/dist/sorting.d.ts +2 -2
  230. package/dist/source/codes.cjs +1 -0
  231. package/dist/source/codes.cjs.map +1 -1
  232. package/dist/source/codes.js +1 -0
  233. package/dist/source/codes.js.map +1 -1
  234. package/dist/source/fetch-codes.cjs +1 -0
  235. package/dist/source/fetch-codes.cjs.map +1 -1
  236. package/dist/source/fetch-codes.js +1 -0
  237. package/dist/source/fetch-codes.js.map +1 -1
  238. package/dist/table/codes.cjs +1 -0
  239. package/dist/table/codes.cjs.map +1 -1
  240. package/dist/table/codes.js +1 -0
  241. package/dist/table/codes.js.map +1 -1
  242. package/dist/table/create-table.cjs +39 -9
  243. package/dist/table/create-table.cjs.map +1 -1
  244. package/dist/table/create-table.js +39 -9
  245. package/dist/table/create-table.js.map +1 -1
  246. package/dist/table/data-mutation.cjs +1 -0
  247. package/dist/table/data-mutation.cjs.map +1 -1
  248. package/dist/table/data-mutation.js +1 -0
  249. package/dist/table/data-mutation.js.map +1 -1
  250. package/dist/table/internals.cjs.map +1 -1
  251. package/dist/table/internals.js.map +1 -1
  252. package/dist/table/ssr-codes.cjs +1 -0
  253. package/dist/table/ssr-codes.cjs.map +1 -1
  254. package/dist/table/ssr-codes.js +1 -0
  255. package/dist/table/ssr-codes.js.map +1 -1
  256. package/dist/table/ssr.cjs +4 -1
  257. package/dist/table/ssr.cjs.map +1 -1
  258. package/dist/table/ssr.js +4 -1
  259. package/dist/table/ssr.js.map +1 -1
  260. package/dist/table/state-feedback.cjs +19 -0
  261. package/dist/table/state-feedback.cjs.map +1 -1
  262. package/dist/table/state-feedback.js +19 -0
  263. package/dist/table/state-feedback.js.map +1 -1
  264. package/dist/table/types.cjs.map +1 -1
  265. package/dist/table/types.js.map +1 -1
  266. package/dist/validity.d.cts +2 -2
  267. package/dist/validity.d.ts +2 -2
  268. package/dist/version.cjs +1 -1
  269. package/dist/version.cjs.map +1 -1
  270. package/dist/version.js +1 -1
  271. package/dist/version.js.map +1 -1
  272. package/dist/versioning/codes.cjs +18 -0
  273. package/dist/versioning/codes.cjs.map +1 -1
  274. package/dist/versioning/codes.js +18 -0
  275. package/dist/versioning/codes.js.map +1 -1
  276. package/dist/versioning/registry.cjs +4 -2
  277. package/dist/versioning/registry.cjs.map +1 -1
  278. package/dist/versioning/registry.js +4 -2
  279. package/dist/versioning/registry.js.map +1 -1
  280. package/package.json +39 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/ssr.ts"],"sourcesContent":["import { keepDiagnostics } from '../diagnostics/catalog'\nimport { deferral, getTableInternals } from './internals'\nimport { ssrDiagnostics } from './ssr-codes'\n\nkeepDiagnostics(ssrDiagnostics)\n\n/**\n * SSR seam for bindings (§7.2): runs `create` (a `createTable` call) so that the table it creates\n * starts no async work. The fetcher's first request and a `searchFn` for an initial query wait for\n * `startTable(table)`; meanwhile the row model has `status: 'pending'`. Nothing is started, so a\n * server render leaves no request, timer or microtask chain behind. On the server (no\n * `document`), each deferred source reports `DK_SSR_ASYNC_DEFERRED` (info).\n *\n * `@datakable/react`'s `useDataTable` creates its table this way and starts it after the commit;\n * effects never run on the server.\n */\nexport function createDeferred<R extends object>(create: () => R): R {\n deferral.depth++\n let table: R\n try {\n table = create()\n } finally {\n deferral.depth--\n }\n if (typeof document === 'undefined') {\n const internals = getTableInternals(table)\n // Through the extensions: SSR does not import the request machinery (§9.3).\n for (const e of internals.extensions()) {\n for (const source of e.deferred?.() ?? []) {\n internals.emit('DK_SSR_ASYNC_DEFERRED', { source }, { phase: 'ssr' })\n }\n }\n }\n return table\n}\n\n/**\n * Starts the async work a `createDeferred` table deferred (§7.2): the fetcher's first request\n * (unless `initialData` answered it) and the `searchFn` for the applied query. Idempotent; a no-op\n * for a table that was not deferred or is destroyed.\n */\nexport function startTable(table: object): void {\n getTableInternals(table).start()\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,SAAS,UAAU,yBAAyB;AAC5C,SAAS,sBAAsB;AAE/B,gBAAgB,cAAc;AAYvB,SAAS,eAAiC,QAAoB;AACnE,WAAS;AACT,MAAI;AACJ,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,UAAE;AACA,aAAS;AAAA,EACX;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,YAAY,kBAAkB,KAAK;AAEzC,eAAW,KAAK,UAAU,WAAW,GAAG;AACtC,iBAAW,UAAU,EAAE,WAAW,KAAK,CAAC,GAAG;AACzC,kBAAU,KAAK,yBAAyB,EAAE,OAAO,GAAG,EAAE,OAAO,MAAM,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,WAAW,OAAqB;AAC9C,oBAAkB,KAAK,EAAE,MAAM;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/table/ssr.ts"],"sourcesContent":["import { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport { firstInSession } from '../versioning/registry'\nimport { deferral, getTableInternals } from './internals'\nimport { ssrDiagnostics } from './ssr-codes'\n\nkeepDiagnostics(ssrDiagnostics)\n\n/**\n * SSR seam for bindings (§7.2): runs `create` (a `createTable` call) so that the table it creates\n * starts no async work. The fetcher's first request and a `searchFn` for an initial query wait for\n * `startTable(table)`; meanwhile the row model has `status: 'pending'`. Nothing is started, so a\n * server render leaves no request, timer or microtask chain behind. On the server (no\n * `document`), each deferred source reports `DK_SSR_ASYNC_DEFERRED` (info), printed once per process\n * in development for a table name and source (#38: a dev server renders a page several times per\n * request).\n *\n * `@datakable/react`'s `useDataTable` creates its table this way and starts it after the commit;\n * effects never run on the server.\n */\nexport function createDeferred<R extends object>(create: () => R): R {\n deferral.depth++\n let table: R\n try {\n table = create()\n } finally {\n deferral.depth--\n }\n if (typeof document === 'undefined') {\n const internals = getTableInternals(table)\n // Through the extensions: SSR does not import the request machinery (§9.3).\n for (const e of internals.extensions()) {\n for (const source of e.deferred?.() ?? []) {\n // Development (dev servers render a page several times); production keeps the registry out.\n const quiet = (process.env.NODE_ENV !== \"production\") && !firstInSession(`DK_SSR_ASYNC_DEFERRED:${internals.name}:${source}`)\n internals.emit('DK_SSR_ASYNC_DEFERRED', { source }, { phase: 'ssr', quiet })\n }\n }\n }\n return table\n}\n\n/**\n * Starts the async work a `createDeferred` table deferred (§7.2): the fetcher's first request\n * (unless `initialData` answered it) and the `searchFn` for the applied query. Idempotent; a no-op\n * for a table that was not deferred or is destroyed.\n */\nexport function startTable(table: object): void {\n getTableInternals(table).start()\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,OAAO;AACP,SAAS,sBAAsB;AAC/B,SAAS,UAAU,yBAAyB;AAC5C,SAAS,sBAAsB;AAE/B,gBAAgB,cAAc;AAcvB,SAAS,eAAiC,QAAoB;AACnE,WAAS;AACT,MAAI;AACJ,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,UAAE;AACA,aAAS;AAAA,EACX;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,YAAY,kBAAkB,KAAK;AAEzC,eAAW,KAAK,UAAU,WAAW,GAAG;AACtC,iBAAW,UAAU,EAAE,WAAW,KAAK,CAAC,GAAG;AAEzC,cAAM,QAAS,QAAQ,IAAI,aAAa,gBAAiB,CAAC,eAAe,yBAAyB,UAAU,IAAI,IAAI,MAAM,EAAE;AAC5H,kBAAU,KAAK,yBAAyB,EAAE,OAAO,GAAG,EAAE,OAAO,OAAO,MAAM,CAAC;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,WAAW,OAAqB;AAC9C,oBAAkB,KAAK,EAAE,MAAM;AACjC;","names":[]}
@@ -26,6 +26,7 @@ __export(state_feedback_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(state_feedback_exports);
28
28
  var import_catalog = require("../diagnostics/catalog.cjs");
29
+ var import_process_guard = require("../diagnostics/process-guard.cjs");
29
30
  var list = (s) => s.map((k) => `"${k}"`).join(", ");
30
31
  var stateFeedbackDiagnostics = process.env.NODE_ENV !== "production" ? [
31
32
  (0, import_catalog.defineDiagnostic)({
@@ -39,6 +40,17 @@ var stateFeedbackDiagnostics = process.env.NODE_ENV !== "production" ? [
39
40
  `or move ${list(d.slices)} to initialState so the table owns it`,
40
41
  "if you reject the change on purpose, disable the control that asks for it instead"
41
42
  ]
43
+ }),
44
+ (0, import_catalog.defineDiagnostic)({
45
+ code: "DK_CONTROLLED_SLICE_DROPPED",
46
+ level: "warn",
47
+ where: (d) => ({ kind: "option", path: `state.${d.slices[0] ?? ""}` }),
48
+ problem: (d) => `setOptions({ state }) fed back ${list(d.kept)} without ${list(d.slices)}: setOptions replaces the whole state option, so ${list(d.slices)} is no longer controlled`,
49
+ impact: (d) => `the table now owns ${list(d.slices)}: its changes no longer reach onStateChange and your copy goes stale`,
50
+ fix: (d) => [
51
+ `pass every controlled slice each time, e.g. table.setOptions({ state: { ...current, ${d.kept[0]} } })`,
52
+ `to release a slice on purpose, pass it as undefined: { ${d.slices[0]}: undefined }`
53
+ ]
42
54
  })
43
55
  ] : [];
44
56
  var FEEDBACK_MS = 1e3;
@@ -51,6 +63,13 @@ function createStateFeedbackCheck(emit) {
51
63
  let state;
52
64
  let controlled = /* @__PURE__ */ new Set();
53
65
  function settle(nextState, nextControlled) {
66
+ if (nextState && nextState !== state && (asking.size > 0 || waiting.size > 0)) {
67
+ const kept = Object.keys(nextState);
68
+ const slices = [...controlled].filter((k) => !(k in nextState));
69
+ if (kept.length > 0 && slices.length > 0) {
70
+ emit("DK_CONTROLLED_SLICE_DROPPED", { slices, kept });
71
+ }
72
+ }
54
73
  state = nextState;
55
74
  controlled = nextControlled;
56
75
  for (const [k, before] of waiting) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/state-feedback.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport type { Emit, Trigger } from '../diagnostics/types'\nimport type { TableState } from './types'\n\n/**\n * `DK_CONTROLLED_NOT_FED_BACK` (§3.2, dev): `onStateChange` was told about a change to a controlled\n * slice, but the owner never passed a new value back in `options.state`. The table shows only what\n * it is given, so the slice is frozen. Without this check that freeze is silent (§8.1). Dev only:\n * the definition, its text and the check sit behind `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_CONTROLLED_NOT_FED_BACK: {\n /** Controlled slices whose change was not fed back. */\n slices: string[]\n /** How long the owner had to feed it back. */\n waitedMs: number\n }\n }\n}\n\nconst list = (s: string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\nexport const stateFeedbackDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_CONTROLLED_NOT_FED_BACK',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `onStateChange was called with a change to ${list(d.slices)}, but options.state ` +\n `still had the old value ${d.waitedMs} ms later`,\n impact: () =>\n 'the change was not applied: a controlled slice shows only the value you pass back, so ' +\n 'interactions that change it look frozen',\n fix: (d) => [\n 'apply the updater and feed the result back: onStateChange: (updater) => ' +\n 'setMyState(updater), then pass the new state as options.state (React: the state prop; ' +\n 'createTable: table.setOptions({ state }))',\n `or move ${list(d.slices)} to initialState so the table owns it`,\n 'if you reject the change on purpose, disable the control that asks for it instead',\n ],\n }),\n ]\n : []\n\n/** Time the owner has to feed a change back (a render, a store update, a router navigation). */\nexport const FEEDBACK_MS = 1000\n\n/**\n * Watches the changes the table asks the owner of controlled slices to make: `ask` before\n * `onStateChange` is called, `asked` once it returned (not when it threw), `settle` after every\n * change to `options.state`. A slice whose value is still the old one `FEEDBACK_MS` later is\n * reported once per ask.\n */\nexport function createStateFeedbackCheck(emit: Emit) {\n /** Slice → its value in `options.state` when the change was asked. */\n const waiting = new Map<string, unknown>()\n /** The ask in progress (`ask` → `asked`): slice → its value before `onStateChange`. */\n const asking = new Map<string, unknown>()\n let asker: Trigger = 'init'\n let timer: ReturnType<typeof setTimeout> | undefined\n /** The interaction that asked first (the report's `context.trigger`). */\n let askedBy: Trigger = 'init'\n let state: TableState | undefined\n let controlled: ReadonlySet<string> = new Set()\n\n function settle(nextState: TableState | undefined, nextControlled: ReadonlySet<string>) {\n state = nextState\n controlled = nextControlled\n for (const [k, before] of waiting) {\n if (!controlled.has(k) || !Object.is(state?.[k], before)) waiting.delete(k)\n }\n if (waiting.size === 0) dispose()\n }\n\n function check() {\n timer = undefined\n settle(state, controlled)\n if (waiting.size === 0) return\n const slices = [...waiting.keys()]\n waiting.clear()\n emit('DK_CONTROLLED_NOT_FED_BACK', { slices, waitedMs: FEEDBACK_MS }, { trigger: askedBy })\n }\n\n function dispose() {\n waiting.clear()\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return {\n ask(slices: string[], trigger: Trigger) {\n asking.clear()\n for (const k of slices) asking.set(k, state?.[k])\n asker = trigger\n },\n asked() {\n for (const [k, before] of asking) if (!waiting.has(k)) waiting.set(k, before)\n asking.clear()\n // A synchronous feed-back (setOptions inside onStateChange) already settled.\n settle(state, controlled)\n if (waiting.size === 0 || timer !== undefined) return\n askedBy = asker\n timer = setTimeout(check, FEEDBACK_MS)\n // Never keeps a server process (SSR, tests) alive.\n ;(timer as { unref?: () => void }).unref?.()\n },\n settle,\n dispose,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAiC;AAsBjC,IAAM,OAAO,CAAC,MAAgB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAEvD,IAAM,2BAA4B,QAAQ,IAAI,aAAa,eAC9D;AAAA,MACE,iCAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG;AAAA,IACpE,SAAS,CAAC,MACR,6CAA6C,KAAK,EAAE,MAAM,CAAC,+CAChC,EAAE,QAAQ;AAAA,IACvC,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV;AAAA,MAGA,WAAW,KAAK,EAAE,MAAM,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,EACF,CAAC;AACH,IACA,CAAC;AAGE,IAAM,cAAc;AAQpB,SAAS,yBAAyB,MAAY;AAEnD,QAAM,UAAU,oBAAI,IAAqB;AAEzC,QAAM,SAAS,oBAAI,IAAqB;AACxC,MAAI,QAAiB;AACrB,MAAI;AAEJ,MAAI,UAAmB;AACvB,MAAI;AACJ,MAAI,aAAkC,oBAAI,IAAI;AAE9C,WAAS,OAAO,WAAmC,gBAAqC;AACtF,YAAQ;AACR,iBAAa;AACb,eAAW,CAAC,GAAG,MAAM,KAAK,SAAS;AACjC,UAAI,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,MAAM,EAAG,SAAQ,OAAO,CAAC;AAAA,IAC5E;AACA,QAAI,QAAQ,SAAS,EAAG,SAAQ;AAAA,EAClC;AAEA,WAAS,QAAQ;AACf,YAAQ;AACR,WAAO,OAAO,UAAU;AACxB,QAAI,QAAQ,SAAS,EAAG;AACxB,UAAM,SAAS,CAAC,GAAG,QAAQ,KAAK,CAAC;AACjC,YAAQ,MAAM;AACd,SAAK,8BAA8B,EAAE,QAAQ,UAAU,YAAY,GAAG,EAAE,SAAS,QAAQ,CAAC;AAAA,EAC5F;AAEA,WAAS,UAAU;AACjB,YAAQ,MAAM;AACd,QAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,YAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL,IAAI,QAAkB,SAAkB;AACtC,aAAO,MAAM;AACb,iBAAW,KAAK,OAAQ,QAAO,IAAI,GAAG,QAAQ,CAAC,CAAC;AAChD,cAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AACN,iBAAW,CAAC,GAAG,MAAM,KAAK,OAAQ,KAAI,CAAC,QAAQ,IAAI,CAAC,EAAG,SAAQ,IAAI,GAAG,MAAM;AAC5E,aAAO,MAAM;AAEb,aAAO,OAAO,UAAU;AACxB,UAAI,QAAQ,SAAS,KAAK,UAAU,OAAW;AAC/C,gBAAU;AACV,cAAQ,WAAW,OAAO,WAAW;AAEpC,MAAC,MAAiC,QAAQ;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/table/state-feedback.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit, Trigger } from '../diagnostics/types'\nimport type { TableState } from './types'\n\n/**\n * `DK_CONTROLLED_NOT_FED_BACK` (§3.2, dev): `onStateChange` was told about a change to a controlled\n * slice, but the owner never passed a new value back in `options.state`. The table shows only what\n * it is given, so the slice is frozen. Without this check that freeze is silent (§8.1). Dev only:\n * the definition, its text and the check sit behind `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_CONTROLLED_NOT_FED_BACK: {\n /** Controlled slices whose change was not fed back. */\n slices: string[]\n /** How long the owner had to feed it back. */\n waitedMs: number\n }\n DK_CONTROLLED_SLICE_DROPPED: {\n /** Controlled slices missing from the new `options.state`. */\n slices: string[]\n /** The slices that new `options.state` did pass. */\n kept: string[]\n }\n }\n}\n\nconst list = (s: string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\nexport const stateFeedbackDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_CONTROLLED_NOT_FED_BACK',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `onStateChange was called with a change to ${list(d.slices)}, but options.state ` +\n `still had the old value ${d.waitedMs} ms later`,\n impact: () =>\n 'the change was not applied: a controlled slice shows only the value you pass back, so ' +\n 'interactions that change it look frozen',\n fix: (d) => [\n 'apply the updater and feed the result back: onStateChange: (updater) => ' +\n 'setMyState(updater), then pass the new state as options.state (React: the state prop; ' +\n 'createTable: table.setOptions({ state }))',\n `or move ${list(d.slices)} to initialState so the table owns it`,\n 'if you reject the change on purpose, disable the control that asks for it instead',\n ],\n }),\n defineDiagnostic({\n code: 'DK_CONTROLLED_SLICE_DROPPED',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `setOptions({ state }) fed back ${list(d.kept)} without ${list(d.slices)}: setOptions ` +\n `replaces the whole state option, so ${list(d.slices)} is no longer controlled`,\n impact: (d) =>\n `the table now owns ${list(d.slices)}: its changes no longer reach onStateChange and your ` +\n 'copy goes stale',\n fix: (d) => [\n 'pass every controlled slice each time, e.g. table.setOptions({ state: { ...current, ' +\n `${d.kept[0]} } })`,\n `to release a slice on purpose, pass it as undefined: { ${d.slices[0]}: undefined }`,\n ],\n }),\n ]\n : []\n\n/** Time the owner has to feed a change back (a render, a store update, a router navigation). */\nexport const FEEDBACK_MS = 1000\n\n/**\n * Watches the changes the table asks the owner of controlled slices to make: `ask` before\n * `onStateChange` is called, `asked` once it returned (not when it threw), `settle` after every\n * change to `options.state`. A slice whose value is still the old one `FEEDBACK_MS` later is\n * reported once per ask.\n */\nexport function createStateFeedbackCheck(emit: Emit) {\n /** Slice → its value in `options.state` when the change was asked. */\n const waiting = new Map<string, unknown>()\n /** The ask in progress (`ask` → `asked`): slice → its value before `onStateChange`. */\n const asking = new Map<string, unknown>()\n let asker: Trigger = 'init'\n let timer: ReturnType<typeof setTimeout> | undefined\n /** The interaction that asked first (the report's `context.trigger`). */\n let askedBy: Trigger = 'init'\n let state: TableState | undefined\n let controlled: ReadonlySet<string> = new Set()\n\n function settle(nextState: TableState | undefined, nextControlled: ReadonlySet<string>) {\n // A feed-back (an ask in flight or waiting) that passes some slices and omits others (#49).\n if (nextState && nextState !== state && (asking.size > 0 || waiting.size > 0)) {\n const kept = Object.keys(nextState)\n const slices = [...controlled].filter((k) => !(k in nextState))\n if (kept.length > 0 && slices.length > 0) {\n emit('DK_CONTROLLED_SLICE_DROPPED', { slices, kept })\n }\n }\n state = nextState\n controlled = nextControlled\n for (const [k, before] of waiting) {\n if (!controlled.has(k) || !Object.is(state?.[k], before)) waiting.delete(k)\n }\n if (waiting.size === 0) dispose()\n }\n\n function check() {\n timer = undefined\n settle(state, controlled)\n if (waiting.size === 0) return\n const slices = [...waiting.keys()]\n waiting.clear()\n emit('DK_CONTROLLED_NOT_FED_BACK', { slices, waitedMs: FEEDBACK_MS }, { trigger: askedBy })\n }\n\n function dispose() {\n waiting.clear()\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return {\n ask(slices: string[], trigger: Trigger) {\n asking.clear()\n for (const k of slices) asking.set(k, state?.[k])\n asker = trigger\n },\n asked() {\n for (const [k, before] of asking) if (!waiting.has(k)) waiting.set(k, before)\n asking.clear()\n // A synchronous feed-back (setOptions inside onStateChange) already settled.\n settle(state, controlled)\n if (waiting.size === 0 || timer !== undefined) return\n askedBy = asker\n timer = setTimeout(check, FEEDBACK_MS)\n // Never keeps a server process (SSR, tests) alive.\n ;(timer as { unref?: () => void }).unref?.()\n },\n settle,\n dispose,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/table/state-feedback.ts
2
2
  import { defineDiagnostic } from "../diagnostics/catalog.js";
3
+ import "../diagnostics/process-guard.js";
3
4
  var list = (s) => s.map((k) => `"${k}"`).join(", ");
4
5
  var stateFeedbackDiagnostics = process.env.NODE_ENV !== "production" ? [
5
6
  defineDiagnostic({
@@ -13,6 +14,17 @@ var stateFeedbackDiagnostics = process.env.NODE_ENV !== "production" ? [
13
14
  `or move ${list(d.slices)} to initialState so the table owns it`,
14
15
  "if you reject the change on purpose, disable the control that asks for it instead"
15
16
  ]
17
+ }),
18
+ defineDiagnostic({
19
+ code: "DK_CONTROLLED_SLICE_DROPPED",
20
+ level: "warn",
21
+ where: (d) => ({ kind: "option", path: `state.${d.slices[0] ?? ""}` }),
22
+ problem: (d) => `setOptions({ state }) fed back ${list(d.kept)} without ${list(d.slices)}: setOptions replaces the whole state option, so ${list(d.slices)} is no longer controlled`,
23
+ impact: (d) => `the table now owns ${list(d.slices)}: its changes no longer reach onStateChange and your copy goes stale`,
24
+ fix: (d) => [
25
+ `pass every controlled slice each time, e.g. table.setOptions({ state: { ...current, ${d.kept[0]} } })`,
26
+ `to release a slice on purpose, pass it as undefined: { ${d.slices[0]}: undefined }`
27
+ ]
16
28
  })
17
29
  ] : [];
18
30
  var FEEDBACK_MS = 1e3;
@@ -25,6 +37,13 @@ function createStateFeedbackCheck(emit) {
25
37
  let state;
26
38
  let controlled = /* @__PURE__ */ new Set();
27
39
  function settle(nextState, nextControlled) {
40
+ if (nextState && nextState !== state && (asking.size > 0 || waiting.size > 0)) {
41
+ const kept = Object.keys(nextState);
42
+ const slices = [...controlled].filter((k) => !(k in nextState));
43
+ if (kept.length > 0 && slices.length > 0) {
44
+ emit("DK_CONTROLLED_SLICE_DROPPED", { slices, kept });
45
+ }
46
+ }
28
47
  state = nextState;
29
48
  controlled = nextControlled;
30
49
  for (const [k, before] of waiting) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/state-feedback.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport type { Emit, Trigger } from '../diagnostics/types'\nimport type { TableState } from './types'\n\n/**\n * `DK_CONTROLLED_NOT_FED_BACK` (§3.2, dev): `onStateChange` was told about a change to a controlled\n * slice, but the owner never passed a new value back in `options.state`. The table shows only what\n * it is given, so the slice is frozen. Without this check that freeze is silent (§8.1). Dev only:\n * the definition, its text and the check sit behind `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_CONTROLLED_NOT_FED_BACK: {\n /** Controlled slices whose change was not fed back. */\n slices: string[]\n /** How long the owner had to feed it back. */\n waitedMs: number\n }\n }\n}\n\nconst list = (s: string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\nexport const stateFeedbackDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_CONTROLLED_NOT_FED_BACK',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `onStateChange was called with a change to ${list(d.slices)}, but options.state ` +\n `still had the old value ${d.waitedMs} ms later`,\n impact: () =>\n 'the change was not applied: a controlled slice shows only the value you pass back, so ' +\n 'interactions that change it look frozen',\n fix: (d) => [\n 'apply the updater and feed the result back: onStateChange: (updater) => ' +\n 'setMyState(updater), then pass the new state as options.state (React: the state prop; ' +\n 'createTable: table.setOptions({ state }))',\n `or move ${list(d.slices)} to initialState so the table owns it`,\n 'if you reject the change on purpose, disable the control that asks for it instead',\n ],\n }),\n ]\n : []\n\n/** Time the owner has to feed a change back (a render, a store update, a router navigation). */\nexport const FEEDBACK_MS = 1000\n\n/**\n * Watches the changes the table asks the owner of controlled slices to make: `ask` before\n * `onStateChange` is called, `asked` once it returned (not when it threw), `settle` after every\n * change to `options.state`. A slice whose value is still the old one `FEEDBACK_MS` later is\n * reported once per ask.\n */\nexport function createStateFeedbackCheck(emit: Emit) {\n /** Slice → its value in `options.state` when the change was asked. */\n const waiting = new Map<string, unknown>()\n /** The ask in progress (`ask` → `asked`): slice → its value before `onStateChange`. */\n const asking = new Map<string, unknown>()\n let asker: Trigger = 'init'\n let timer: ReturnType<typeof setTimeout> | undefined\n /** The interaction that asked first (the report's `context.trigger`). */\n let askedBy: Trigger = 'init'\n let state: TableState | undefined\n let controlled: ReadonlySet<string> = new Set()\n\n function settle(nextState: TableState | undefined, nextControlled: ReadonlySet<string>) {\n state = nextState\n controlled = nextControlled\n for (const [k, before] of waiting) {\n if (!controlled.has(k) || !Object.is(state?.[k], before)) waiting.delete(k)\n }\n if (waiting.size === 0) dispose()\n }\n\n function check() {\n timer = undefined\n settle(state, controlled)\n if (waiting.size === 0) return\n const slices = [...waiting.keys()]\n waiting.clear()\n emit('DK_CONTROLLED_NOT_FED_BACK', { slices, waitedMs: FEEDBACK_MS }, { trigger: askedBy })\n }\n\n function dispose() {\n waiting.clear()\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return {\n ask(slices: string[], trigger: Trigger) {\n asking.clear()\n for (const k of slices) asking.set(k, state?.[k])\n asker = trigger\n },\n asked() {\n for (const [k, before] of asking) if (!waiting.has(k)) waiting.set(k, before)\n asking.clear()\n // A synchronous feed-back (setOptions inside onStateChange) already settled.\n settle(state, controlled)\n if (waiting.size === 0 || timer !== undefined) return\n askedBy = asker\n timer = setTimeout(check, FEEDBACK_MS)\n // Never keeps a server process (SSR, tests) alive.\n ;(timer as { unref?: () => void }).unref?.()\n },\n settle,\n dispose,\n }\n}\n"],"mappings":";AAAA,SAAS,wBAAwB;AAsBjC,IAAM,OAAO,CAAC,MAAgB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAEvD,IAAM,2BAA4B,QAAQ,IAAI,aAAa,eAC9D;AAAA,EACE,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG;AAAA,IACpE,SAAS,CAAC,MACR,6CAA6C,KAAK,EAAE,MAAM,CAAC,+CAChC,EAAE,QAAQ;AAAA,IACvC,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV;AAAA,MAGA,WAAW,KAAK,EAAE,MAAM,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,EACF,CAAC;AACH,IACA,CAAC;AAGE,IAAM,cAAc;AAQpB,SAAS,yBAAyB,MAAY;AAEnD,QAAM,UAAU,oBAAI,IAAqB;AAEzC,QAAM,SAAS,oBAAI,IAAqB;AACxC,MAAI,QAAiB;AACrB,MAAI;AAEJ,MAAI,UAAmB;AACvB,MAAI;AACJ,MAAI,aAAkC,oBAAI,IAAI;AAE9C,WAAS,OAAO,WAAmC,gBAAqC;AACtF,YAAQ;AACR,iBAAa;AACb,eAAW,CAAC,GAAG,MAAM,KAAK,SAAS;AACjC,UAAI,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,MAAM,EAAG,SAAQ,OAAO,CAAC;AAAA,IAC5E;AACA,QAAI,QAAQ,SAAS,EAAG,SAAQ;AAAA,EAClC;AAEA,WAAS,QAAQ;AACf,YAAQ;AACR,WAAO,OAAO,UAAU;AACxB,QAAI,QAAQ,SAAS,EAAG;AACxB,UAAM,SAAS,CAAC,GAAG,QAAQ,KAAK,CAAC;AACjC,YAAQ,MAAM;AACd,SAAK,8BAA8B,EAAE,QAAQ,UAAU,YAAY,GAAG,EAAE,SAAS,QAAQ,CAAC;AAAA,EAC5F;AAEA,WAAS,UAAU;AACjB,YAAQ,MAAM;AACd,QAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,YAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL,IAAI,QAAkB,SAAkB;AACtC,aAAO,MAAM;AACb,iBAAW,KAAK,OAAQ,QAAO,IAAI,GAAG,QAAQ,CAAC,CAAC;AAChD,cAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AACN,iBAAW,CAAC,GAAG,MAAM,KAAK,OAAQ,KAAI,CAAC,QAAQ,IAAI,CAAC,EAAG,SAAQ,IAAI,GAAG,MAAM;AAC5E,aAAO,MAAM;AAEb,aAAO,OAAO,UAAU;AACxB,UAAI,QAAQ,SAAS,KAAK,UAAU,OAAW;AAC/C,gBAAU;AACV,cAAQ,WAAW,OAAO,WAAW;AAEpC,MAAC,MAAiC,QAAQ;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/table/state-feedback.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit, Trigger } from '../diagnostics/types'\nimport type { TableState } from './types'\n\n/**\n * `DK_CONTROLLED_NOT_FED_BACK` (§3.2, dev): `onStateChange` was told about a change to a controlled\n * slice, but the owner never passed a new value back in `options.state`. The table shows only what\n * it is given, so the slice is frozen. Without this check that freeze is silent (§8.1). Dev only:\n * the definition, its text and the check sit behind `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_CONTROLLED_NOT_FED_BACK: {\n /** Controlled slices whose change was not fed back. */\n slices: string[]\n /** How long the owner had to feed it back. */\n waitedMs: number\n }\n DK_CONTROLLED_SLICE_DROPPED: {\n /** Controlled slices missing from the new `options.state`. */\n slices: string[]\n /** The slices that new `options.state` did pass. */\n kept: string[]\n }\n }\n}\n\nconst list = (s: string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\nexport const stateFeedbackDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_CONTROLLED_NOT_FED_BACK',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `onStateChange was called with a change to ${list(d.slices)}, but options.state ` +\n `still had the old value ${d.waitedMs} ms later`,\n impact: () =>\n 'the change was not applied: a controlled slice shows only the value you pass back, so ' +\n 'interactions that change it look frozen',\n fix: (d) => [\n 'apply the updater and feed the result back: onStateChange: (updater) => ' +\n 'setMyState(updater), then pass the new state as options.state (React: the state prop; ' +\n 'createTable: table.setOptions({ state }))',\n `or move ${list(d.slices)} to initialState so the table owns it`,\n 'if you reject the change on purpose, disable the control that asks for it instead',\n ],\n }),\n defineDiagnostic({\n code: 'DK_CONTROLLED_SLICE_DROPPED',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: `state.${d.slices[0] ?? ''}` }),\n problem: (d) =>\n `setOptions({ state }) fed back ${list(d.kept)} without ${list(d.slices)}: setOptions ` +\n `replaces the whole state option, so ${list(d.slices)} is no longer controlled`,\n impact: (d) =>\n `the table now owns ${list(d.slices)}: its changes no longer reach onStateChange and your ` +\n 'copy goes stale',\n fix: (d) => [\n 'pass every controlled slice each time, e.g. table.setOptions({ state: { ...current, ' +\n `${d.kept[0]} } })`,\n `to release a slice on purpose, pass it as undefined: { ${d.slices[0]}: undefined }`,\n ],\n }),\n ]\n : []\n\n/** Time the owner has to feed a change back (a render, a store update, a router navigation). */\nexport const FEEDBACK_MS = 1000\n\n/**\n * Watches the changes the table asks the owner of controlled slices to make: `ask` before\n * `onStateChange` is called, `asked` once it returned (not when it threw), `settle` after every\n * change to `options.state`. A slice whose value is still the old one `FEEDBACK_MS` later is\n * reported once per ask.\n */\nexport function createStateFeedbackCheck(emit: Emit) {\n /** Slice → its value in `options.state` when the change was asked. */\n const waiting = new Map<string, unknown>()\n /** The ask in progress (`ask` → `asked`): slice → its value before `onStateChange`. */\n const asking = new Map<string, unknown>()\n let asker: Trigger = 'init'\n let timer: ReturnType<typeof setTimeout> | undefined\n /** The interaction that asked first (the report's `context.trigger`). */\n let askedBy: Trigger = 'init'\n let state: TableState | undefined\n let controlled: ReadonlySet<string> = new Set()\n\n function settle(nextState: TableState | undefined, nextControlled: ReadonlySet<string>) {\n // A feed-back (an ask in flight or waiting) that passes some slices and omits others (#49).\n if (nextState && nextState !== state && (asking.size > 0 || waiting.size > 0)) {\n const kept = Object.keys(nextState)\n const slices = [...controlled].filter((k) => !(k in nextState))\n if (kept.length > 0 && slices.length > 0) {\n emit('DK_CONTROLLED_SLICE_DROPPED', { slices, kept })\n }\n }\n state = nextState\n controlled = nextControlled\n for (const [k, before] of waiting) {\n if (!controlled.has(k) || !Object.is(state?.[k], before)) waiting.delete(k)\n }\n if (waiting.size === 0) dispose()\n }\n\n function check() {\n timer = undefined\n settle(state, controlled)\n if (waiting.size === 0) return\n const slices = [...waiting.keys()]\n waiting.clear()\n emit('DK_CONTROLLED_NOT_FED_BACK', { slices, waitedMs: FEEDBACK_MS }, { trigger: askedBy })\n }\n\n function dispose() {\n waiting.clear()\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return {\n ask(slices: string[], trigger: Trigger) {\n asking.clear()\n for (const k of slices) asking.set(k, state?.[k])\n asker = trigger\n },\n asked() {\n for (const [k, before] of asking) if (!waiting.has(k)) waiting.set(k, before)\n asking.clear()\n // A synchronous feed-back (setOptions inside onStateChange) already settled.\n settle(state, controlled)\n if (waiting.size === 0 || timer !== undefined) return\n askedBy = asker\n timer = setTimeout(check, FEEDBACK_MS)\n // Never keeps a server process (SSR, tests) alive.\n ;(timer as { unref?: () => void }).unref?.()\n },\n settle,\n dispose,\n }\n}\n"],"mappings":";AAAA,SAAS,wBAAwB;AACjC,OAAO;AA4BP,IAAM,OAAO,CAAC,MAAgB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAEvD,IAAM,2BAA4B,QAAQ,IAAI,aAAa,eAC9D;AAAA,EACE,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG;AAAA,IACpE,SAAS,CAAC,MACR,6CAA6C,KAAK,EAAE,MAAM,CAAC,+CAChC,EAAE,QAAQ;AAAA,IACvC,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV;AAAA,MAGA,WAAW,KAAK,EAAE,MAAM,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,EACF,CAAC;AAAA,EACD,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG;AAAA,IACpE,SAAS,CAAC,MACR,kCAAkC,KAAK,EAAE,IAAI,CAAC,YAAY,KAAK,EAAE,MAAM,CAAC,oDACjC,KAAK,EAAE,MAAM,CAAC;AAAA,IACvD,QAAQ,CAAC,MACP,sBAAsB,KAAK,EAAE,MAAM,CAAC;AAAA,IAEtC,KAAK,CAAC,MAAM;AAAA,MACV,uFACK,EAAE,KAAK,CAAC,CAAC;AAAA,MACd,0DAA0D,EAAE,OAAO,CAAC,CAAC;AAAA,IACvE;AAAA,EACF,CAAC;AACH,IACA,CAAC;AAGE,IAAM,cAAc;AAQpB,SAAS,yBAAyB,MAAY;AAEnD,QAAM,UAAU,oBAAI,IAAqB;AAEzC,QAAM,SAAS,oBAAI,IAAqB;AACxC,MAAI,QAAiB;AACrB,MAAI;AAEJ,MAAI,UAAmB;AACvB,MAAI;AACJ,MAAI,aAAkC,oBAAI,IAAI;AAE9C,WAAS,OAAO,WAAmC,gBAAqC;AAEtF,QAAI,aAAa,cAAc,UAAU,OAAO,OAAO,KAAK,QAAQ,OAAO,IAAI;AAC7E,YAAM,OAAO,OAAO,KAAK,SAAS;AAClC,YAAM,SAAS,CAAC,GAAG,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,UAAU;AAC9D,UAAI,KAAK,SAAS,KAAK,OAAO,SAAS,GAAG;AACxC,aAAK,+BAA+B,EAAE,QAAQ,KAAK,CAAC;AAAA,MACtD;AAAA,IACF;AACA,YAAQ;AACR,iBAAa;AACb,eAAW,CAAC,GAAG,MAAM,KAAK,SAAS;AACjC,UAAI,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,MAAM,EAAG,SAAQ,OAAO,CAAC;AAAA,IAC5E;AACA,QAAI,QAAQ,SAAS,EAAG,SAAQ;AAAA,EAClC;AAEA,WAAS,QAAQ;AACf,YAAQ;AACR,WAAO,OAAO,UAAU;AACxB,QAAI,QAAQ,SAAS,EAAG;AACxB,UAAM,SAAS,CAAC,GAAG,QAAQ,KAAK,CAAC;AACjC,YAAQ,MAAM;AACd,SAAK,8BAA8B,EAAE,QAAQ,UAAU,YAAY,GAAG,EAAE,SAAS,QAAQ,CAAC;AAAA,EAC5F;AAEA,WAAS,UAAU;AACjB,YAAQ,MAAM;AACd,QAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,YAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL,IAAI,QAAkB,SAAkB;AACtC,aAAO,MAAM;AACb,iBAAW,KAAK,OAAQ,QAAO,IAAI,GAAG,QAAQ,CAAC,CAAC;AAChD,cAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AACN,iBAAW,CAAC,GAAG,MAAM,KAAK,OAAQ,KAAI,CAAC,QAAQ,IAAI,CAAC,EAAG,SAAQ,IAAI,GAAG,MAAM;AAC5E,aAAO,MAAM;AAEb,aAAO,OAAO,UAAU;AACxB,UAAI,QAAQ,SAAS,KAAK,UAAU,OAAW;AAC/C,gBAAU;AACV,cAAQ,WAAW,OAAO,WAAW;AAEpC,MAAC,MAAiC,QAAQ;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/types.ts"],"sourcesContent":["import type { ColumnDef, LooseRow, Path } from '../column'\nimport type { RowExplanation, TableExplanation } from '../diagnostics/explain'\nimport type { RedactFn, RedactLevel } from '../diagnostics/redact'\nimport type { Diagnostic } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { InvalidRow } from '../ingest/schema'\nimport type { TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { RowKey } from '../pipeline/row'\nimport type { RowModel } from '../pipeline/types'\nimport type { FetchOutcome } from '../source/fetcher'\nimport type { ServerFetcher } from '../source/server-brand'\n\n/** Table state: one entry per slice (feature state is typed by `StateOf`, T08). */\nexport type TableState = Record<string, unknown>\n\n/** A partial state merged per slice, or a function of the current state returning one. */\nexport type Updater<S> = Partial<S> | ((prev: S) => Partial<S>)\n\n/** `options.state`: a slice given as `undefined` counts as not passed (owned by the store). */\nexport type ControlledState<S> = { [K in keyof S]?: S[K] | undefined }\n\n/** Called after each committed change with the new and the previously notified snapshot. */\nexport type Listener<S> = (state: S, prev: S) => void\n\n/**\n * Max consecutive re-entrant notification rounds (a listener updating state, whose listeners\n * update state, …). The update that would start the next round is dropped (`DK_UPDATE_LOOP`).\n */\nexport const UPDATE_DEPTH_LIMIT = 50\n\nexport interface TableDiagnosticsOptions {\n /**\n * Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level\n * (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never\n * logged; a sensitive `rowKey` column is hashed at every level.\n */\n redact?: RedactLevel | RedactFn\n}\n\nexport interface TableOptions<T, S extends TableState = TableState> {\n /** Names the table in diagnostics; default `table#n`. */\n name?: string\n data?: readonly T[]\n columns?: readonly ColumnDef<T>[]\n /**\n * A Standard Schema v1 (zod, valibot, arktype, …) every row is validated with (§5.1), opted in\n * with `withSchema(UserSchema)` from `@datakable/core/schema`; the row type is its output. An\n * async schema sets `status: 'validating'` while it runs. Read once, at creation.\n */\n schema?: TableSchema<unknown, T>\n /**\n * What happens to rows that fail `schema` (§5.1): `'drop'` (default) removes them, `'flag'` keeps\n * them with `row.valid === false`, `'throw'` throws `DatakableSchemaError`. All three list them in\n * `getInvalidRows()`. Read once, at creation; no effect without `schema`.\n */\n invalidRows?: InvalidRowsMode\n /** Row identity (§5.2). Read by redaction here; key handling is the ingest's. */\n rowKey?: Path<LooseRow<T>> | ((row: T) => string | number)\n /**\n * Installed features (§3.3), in any order: the pipeline runs them by `stage`. Each adds its state\n * slice and merges its API onto the table. Read once, at creation (`setOptions` ignores it).\n */\n features?: readonly AnyFeature[]\n /** Starting values of owned slices; a feature slice given here replaces its `initialState()`. */\n initialState?: Partial<S>\n /**\n * Controlled slices (§3.2): every key with a defined value is owned by the caller, who feeds\n * changes back through `setOptions({ state })`. Slices not passed are owned by the store.\n */\n state?: ControlledState<S>\n /**\n * Receives changes to controlled slices as an updater that merges only the changed slices into\n * the caller's state: `onStateChange: (u) => setControlled(u)`. Not called for owned slices.\n */\n onStateChange?: (updater: (prev: Partial<S>) => Partial<S>) => void\n /** User / feature interceptors (§3.4). The errorBoundary is always installed outermost. */\n interceptors?: readonly Interceptor<OpName>[]\n /** User sink for every diagnostic (e.g. Sentry). */\n onDiagnostic?: (d: Diagnostic) => void\n /**\n * ms before an async request (`fetcher`, `searchFn`) is aborted with `DK_ASYNC_TIMEOUT`.\n * Default 30 000; `Infinity` disables it. Read once, at creation.\n */\n timeout?: number\n /**\n * Server source (§7.1): `fetcher({ query, filters, sort, pageIndex, pageSize, signal })` →\n * `{ rows, rowCount }`. It receives only the state of the server-mode features and runs at\n * creation and whenever that state changes (`status: 'fetching'`), through `async:request`\n * (latest wins, abort, `timeout`). Needs at least one feature with `mode: 'server'`\n * (`DK_FETCHER_WITHOUT_SERVER_MODE`). The returned rows go through `schema` and the data checks.\n * Opt in with `withFetcher(fetchUsers)` from `@datakable/core/server`: a table without a fetcher\n * does not bundle the server source (changed 2026-10-13 for §9.3).\n */\n fetcher?: ServerFetcher<T>\n /**\n * Server mode without a fetcher (§7.1): the total number of rows on the server, while `data` is\n * only the current page. Server-mode pagination counts pages from it (`DK_ROW_COUNT_MISSING`\n * when missing).\n */\n rowCount?: number\n /**\n * SSR (§7.2): the rows the server already produced for the initial state (e.g. its fetcher\n * result). Ingested at creation like `data` when `data` is not given, and counts as the result of\n * the initial async work: the fetcher's first request is skipped (a later server-state change\n * fetches), a `searchFn` is not called for the initial query, and hydration does not re-fetch.\n * With server pagination, `rowCount` gives its total. Read once, at creation.\n */\n initialData?: readonly T[]\n /** Rethrow error-level diagnostics as `DatakableError`. */\n strict?: boolean\n diagnostics?: TableDiagnosticsOptions\n}\n\nexport interface Table<\n T,\n S extends TableState = TableState,\n M extends InvalidRowsMode = 'drop',\n /** The element type `setOptions({ data })` accepts: `unknown` (raw input) with a schema. */\n In = T,\n> {\n /** The current immutable snapshot; the same reference until a slice changes. */\n getState(): S\n setState(updater: Updater<S>): void\n /** Returns the unsubscribe function. One throwing listener never stops the others. */\n subscribe(listener: Listener<S>): () => void\n /** Merges `options` into the current ones (`table:setOptions`). */\n setOptions(\n options: Partial<Omit<TableOptions<T, S>, 'features' | 'data'>> & { data?: readonly In[] },\n ): void\n getOptions(): Readonly<TableOptions<T, S>>\n /** The ingested data (the previous data while an async ingest is pending or failed). */\n getData(): readonly T[]\n /**\n * The row model (§6.6): the visible rows (`Row` objects for the current page only), the counts\n * and the status. The same object until data, state or columns change the result.\n */\n getRowModel(): RowModel<T, M>\n /**\n * The rows that failed `schema` in the current data (§5.1), in every `invalidRows` mode:\n * `{ index, raw, issues }` with `index` into `options.data`. Empty without a schema.\n */\n getInvalidRows(): readonly InvalidRow[]\n /** Oldest first, at most 100 entries. */\n getDiagnostics(): Diagnostic[]\n /**\n * The last pipeline run (§8.4): rows per step, schema drops, the page, open diagnostics, and\n * timings when traced (the tracer runs in development). `String(table.explain())` is a one-line\n * summary. Read-only.\n */\n explain(): TableExplanation\n /**\n * Replays the row with key `key` through the schema and every step (§8.4): which step kept it\n * where, and the step that removed it, with a fix. Values are redacted like diagnostics (§8.2).\n * `String(table.explainRow(key))` is the console report. Read-only.\n */\n explainRow(key: RowKey): RowExplanation\n /**\n * Runs `options.fetcher` again with the current params (§7.1), e.g. a Retry button after\n * `DK_FETCH_FAILED`. Never rejects. Without a fetcher: `{ status: 'applied' }` at once.\n */\n refetch(): Promise<FetchOutcome>\n /** Releases listeners. Later mutating calls are no-ops that report `DK_TABLE_DESTROYED`. */\n destroy(): void\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BO,IAAM,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../src/table/types.ts"],"sourcesContent":["import type { ColumnDef, LooseRow, Path } from '../column'\nimport type { RowExplanation, TableExplanation } from '../diagnostics/explain'\nimport type { RedactFn, RedactLevel } from '../diagnostics/redact'\nimport type { Diagnostic } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { InvalidRow } from '../ingest/schema'\nimport type { TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { RowKey } from '../pipeline/row'\nimport type { RowModel } from '../pipeline/types'\nimport type { FetchOutcome } from '../source/fetcher'\nimport type { ServerFetcher } from '../source/server-brand'\n\n/** Table state: one entry per slice (feature state is typed by `StateOf`, T08). */\nexport type TableState = Record<string, unknown>\n\n/** A partial state merged per slice, or a function of the current state returning one. */\nexport type Updater<S> = Partial<S> | ((prev: S) => Partial<S>)\n\n/** `options.state`: a slice given as `undefined` counts as not passed (owned by the store). */\nexport type ControlledState<S> = { [K in keyof S]?: S[K] | undefined }\n\n/** Called after each committed change with the new and the previously notified snapshot. */\nexport type Listener<S> = (state: S, prev: S) => void\n\n/**\n * Max consecutive re-entrant notification rounds (a listener updating state, whose listeners\n * update state, …). The update that would start the next round is dropped (`DK_UPDATE_LOOP`).\n */\nexport const UPDATE_DEPTH_LIMIT = 50\n\nexport interface TableDiagnosticsOptions {\n /**\n * Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level\n * (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never\n * logged; a `rowKey` path into a sensitive column is hashed at every level.\n */\n redact?: RedactLevel | RedactFn\n}\n\nexport interface TableOptions<T, S extends TableState = TableState> {\n /** Names the table in diagnostics; default `table#n`. */\n name?: string\n data?: readonly T[]\n columns?: readonly ColumnDef<T>[]\n /**\n * A Standard Schema v1 (zod, valibot, arktype, …) every row is validated with (§5.1), opted in\n * with `withSchema(UserSchema)` from `@datakable/core/schema`; the row type is its output. An\n * async schema sets `status: 'validating'` while it runs. Read once, at creation.\n */\n schema?: TableSchema<unknown, T>\n /**\n * What happens to rows that fail `schema` (§5.1): `'drop'` (default) removes them, `'flag'` keeps\n * them with `row.valid === false`, `'throw'` throws `DatakableSchemaError`. All three list them in\n * `getInvalidRows()`. Read once, at creation; no effect without `schema`.\n */\n invalidRows?: InvalidRowsMode\n /** Row identity (§5.2). Read by redaction here; key handling is the ingest's. */\n rowKey?: Path<LooseRow<T>> | ((row: T) => string | number)\n /**\n * Installed features (§3.3), in any order: the pipeline runs them by `stage`. Each adds its state\n * slice and merges its API onto the table. Read once, at creation (`setOptions` ignores it).\n */\n features?: readonly AnyFeature[]\n /** Starting values of owned slices; a feature slice given here replaces its `initialState()`. */\n initialState?: Partial<S>\n /**\n * Controlled slices (§3.2): every key with a defined value is owned by the caller, who feeds\n * changes back through `setOptions({ state })`. Slices not passed are owned by the store.\n */\n state?: ControlledState<S>\n /**\n * Receives changes to controlled slices as an updater that merges only the changed slices into\n * the caller's state: `onStateChange: (u) => setControlled(u)`. Not called for owned slices.\n */\n onStateChange?: (updater: (prev: Partial<S>) => Partial<S>) => void\n /** User / feature interceptors (§3.4). The errorBoundary is always installed outermost. */\n interceptors?: readonly Interceptor<OpName>[]\n /** User sink for every diagnostic (e.g. Sentry). */\n onDiagnostic?: (d: Diagnostic) => void\n /**\n * ms before an async request (`fetcher`, `searchFn`) is aborted with `DK_ASYNC_TIMEOUT`.\n * Default 30 000; `Infinity` disables it. Read once, at creation.\n */\n timeout?: number\n /**\n * Server source (§7.1): `fetcher({ query, filters, sort, pageIndex, pageSize, signal })` →\n * `{ rows, rowCount }`. It receives only the state of the server-mode features and runs at\n * creation and whenever that state changes (`status: 'fetching'`), through `async:request`\n * (latest wins, abort, `timeout`). Needs at least one feature with `mode: 'server'`\n * (`DK_FETCHER_WITHOUT_SERVER_MODE`). The returned rows go through `schema` and the data checks.\n * Opt in with `withFetcher(fetchUsers)` from `@datakable/core/server`: a table without a fetcher\n * does not bundle the server source (changed 2026-10-13 for §9.3).\n */\n fetcher?: ServerFetcher<T>\n /**\n * Server mode without a fetcher (§7.1): the total number of rows on the server, while `data` is\n * only the current page. Server-mode pagination counts pages from it (`DK_ROW_COUNT_MISSING`\n * when missing).\n */\n rowCount?: number\n /**\n * SSR (§7.2): the rows the server already produced for the initial state (e.g. its fetcher\n * result). Ingested at creation like `data` when `data` is not given, and counts as the result of\n * the initial async work: the fetcher's first request is skipped (a later server-state change\n * fetches), a `searchFn` is not called for the initial query, and hydration does not re-fetch.\n * With server pagination, `rowCount` gives its total. Read once, at creation.\n */\n initialData?: readonly T[]\n /** Rethrow error-level diagnostics as `DatakableError`. */\n strict?: boolean\n diagnostics?: TableDiagnosticsOptions\n}\n\nexport interface Table<\n T,\n S extends TableState = TableState,\n M extends InvalidRowsMode = 'drop',\n /** The element type `setOptions({ data })` accepts: `unknown` (raw input) with a schema. */\n In = T,\n> {\n /** The current immutable snapshot; the same reference until a slice changes. */\n getState(): S\n setState(updater: Updater<S>): void\n /** Returns the unsubscribe function. One throwing listener never stops the others. */\n subscribe(listener: Listener<S>): () => void\n /** Merges `options` into the current ones (`table:setOptions`). */\n setOptions(\n options: Partial<Omit<TableOptions<T, S>, 'features' | 'data'>> & { data?: readonly In[] },\n ): void\n getOptions(): Readonly<TableOptions<T, S>>\n /** The ingested data (the previous data while an async ingest is pending or failed). */\n getData(): readonly T[]\n /**\n * The row model (§6.6): the visible rows (`Row` objects for the current page only), the counts\n * and the status. The same object until data, state or columns change the result.\n */\n getRowModel(): RowModel<T, M>\n /**\n * The rows that failed `schema` in the current data (§5.1), in every `invalidRows` mode:\n * `{ index, raw, issues }` with `index` into `options.data`. Empty without a schema.\n */\n getInvalidRows(): readonly InvalidRow[]\n /** Oldest first, at most 100 entries. */\n getDiagnostics(): Diagnostic[]\n /**\n * The last pipeline run (§8.4): rows per step, schema drops, the page, open diagnostics, and\n * timings when traced (the tracer runs in development). `String(table.explain())` is a one-line\n * summary. Read-only.\n */\n explain(): TableExplanation\n /**\n * Replays the row with key `key` through the schema and every step (§8.4): which step kept it\n * where, and the step that removed it, with a fix. Values are redacted like diagnostics (§8.2).\n * `String(table.explainRow(key))` is the console report. Read-only.\n */\n explainRow(key: RowKey): RowExplanation\n /**\n * Runs `options.fetcher` again with the current params (§7.1), e.g. a Retry button after\n * `DK_FETCH_FAILED`. Never rejects. Without a fetcher: `{ status: 'applied' }` at once.\n */\n refetch(): Promise<FetchOutcome>\n /** Releases listeners. Later mutating calls are no-ops that report `DK_TABLE_DESTROYED`. */\n destroy(): void\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BO,IAAM,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/types.ts"],"sourcesContent":["import type { ColumnDef, LooseRow, Path } from '../column'\nimport type { RowExplanation, TableExplanation } from '../diagnostics/explain'\nimport type { RedactFn, RedactLevel } from '../diagnostics/redact'\nimport type { Diagnostic } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { InvalidRow } from '../ingest/schema'\nimport type { TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { RowKey } from '../pipeline/row'\nimport type { RowModel } from '../pipeline/types'\nimport type { FetchOutcome } from '../source/fetcher'\nimport type { ServerFetcher } from '../source/server-brand'\n\n/** Table state: one entry per slice (feature state is typed by `StateOf`, T08). */\nexport type TableState = Record<string, unknown>\n\n/** A partial state merged per slice, or a function of the current state returning one. */\nexport type Updater<S> = Partial<S> | ((prev: S) => Partial<S>)\n\n/** `options.state`: a slice given as `undefined` counts as not passed (owned by the store). */\nexport type ControlledState<S> = { [K in keyof S]?: S[K] | undefined }\n\n/** Called after each committed change with the new and the previously notified snapshot. */\nexport type Listener<S> = (state: S, prev: S) => void\n\n/**\n * Max consecutive re-entrant notification rounds (a listener updating state, whose listeners\n * update state, …). The update that would start the next round is dropped (`DK_UPDATE_LOOP`).\n */\nexport const UPDATE_DEPTH_LIMIT = 50\n\nexport interface TableDiagnosticsOptions {\n /**\n * Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level\n * (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never\n * logged; a sensitive `rowKey` column is hashed at every level.\n */\n redact?: RedactLevel | RedactFn\n}\n\nexport interface TableOptions<T, S extends TableState = TableState> {\n /** Names the table in diagnostics; default `table#n`. */\n name?: string\n data?: readonly T[]\n columns?: readonly ColumnDef<T>[]\n /**\n * A Standard Schema v1 (zod, valibot, arktype, …) every row is validated with (§5.1), opted in\n * with `withSchema(UserSchema)` from `@datakable/core/schema`; the row type is its output. An\n * async schema sets `status: 'validating'` while it runs. Read once, at creation.\n */\n schema?: TableSchema<unknown, T>\n /**\n * What happens to rows that fail `schema` (§5.1): `'drop'` (default) removes them, `'flag'` keeps\n * them with `row.valid === false`, `'throw'` throws `DatakableSchemaError`. All three list them in\n * `getInvalidRows()`. Read once, at creation; no effect without `schema`.\n */\n invalidRows?: InvalidRowsMode\n /** Row identity (§5.2). Read by redaction here; key handling is the ingest's. */\n rowKey?: Path<LooseRow<T>> | ((row: T) => string | number)\n /**\n * Installed features (§3.3), in any order: the pipeline runs them by `stage`. Each adds its state\n * slice and merges its API onto the table. Read once, at creation (`setOptions` ignores it).\n */\n features?: readonly AnyFeature[]\n /** Starting values of owned slices; a feature slice given here replaces its `initialState()`. */\n initialState?: Partial<S>\n /**\n * Controlled slices (§3.2): every key with a defined value is owned by the caller, who feeds\n * changes back through `setOptions({ state })`. Slices not passed are owned by the store.\n */\n state?: ControlledState<S>\n /**\n * Receives changes to controlled slices as an updater that merges only the changed slices into\n * the caller's state: `onStateChange: (u) => setControlled(u)`. Not called for owned slices.\n */\n onStateChange?: (updater: (prev: Partial<S>) => Partial<S>) => void\n /** User / feature interceptors (§3.4). The errorBoundary is always installed outermost. */\n interceptors?: readonly Interceptor<OpName>[]\n /** User sink for every diagnostic (e.g. Sentry). */\n onDiagnostic?: (d: Diagnostic) => void\n /**\n * ms before an async request (`fetcher`, `searchFn`) is aborted with `DK_ASYNC_TIMEOUT`.\n * Default 30 000; `Infinity` disables it. Read once, at creation.\n */\n timeout?: number\n /**\n * Server source (§7.1): `fetcher({ query, filters, sort, pageIndex, pageSize, signal })` →\n * `{ rows, rowCount }`. It receives only the state of the server-mode features and runs at\n * creation and whenever that state changes (`status: 'fetching'`), through `async:request`\n * (latest wins, abort, `timeout`). Needs at least one feature with `mode: 'server'`\n * (`DK_FETCHER_WITHOUT_SERVER_MODE`). The returned rows go through `schema` and the data checks.\n * Opt in with `withFetcher(fetchUsers)` from `@datakable/core/server`: a table without a fetcher\n * does not bundle the server source (changed 2026-10-13 for §9.3).\n */\n fetcher?: ServerFetcher<T>\n /**\n * Server mode without a fetcher (§7.1): the total number of rows on the server, while `data` is\n * only the current page. Server-mode pagination counts pages from it (`DK_ROW_COUNT_MISSING`\n * when missing).\n */\n rowCount?: number\n /**\n * SSR (§7.2): the rows the server already produced for the initial state (e.g. its fetcher\n * result). Ingested at creation like `data` when `data` is not given, and counts as the result of\n * the initial async work: the fetcher's first request is skipped (a later server-state change\n * fetches), a `searchFn` is not called for the initial query, and hydration does not re-fetch.\n * With server pagination, `rowCount` gives its total. Read once, at creation.\n */\n initialData?: readonly T[]\n /** Rethrow error-level diagnostics as `DatakableError`. */\n strict?: boolean\n diagnostics?: TableDiagnosticsOptions\n}\n\nexport interface Table<\n T,\n S extends TableState = TableState,\n M extends InvalidRowsMode = 'drop',\n /** The element type `setOptions({ data })` accepts: `unknown` (raw input) with a schema. */\n In = T,\n> {\n /** The current immutable snapshot; the same reference until a slice changes. */\n getState(): S\n setState(updater: Updater<S>): void\n /** Returns the unsubscribe function. One throwing listener never stops the others. */\n subscribe(listener: Listener<S>): () => void\n /** Merges `options` into the current ones (`table:setOptions`). */\n setOptions(\n options: Partial<Omit<TableOptions<T, S>, 'features' | 'data'>> & { data?: readonly In[] },\n ): void\n getOptions(): Readonly<TableOptions<T, S>>\n /** The ingested data (the previous data while an async ingest is pending or failed). */\n getData(): readonly T[]\n /**\n * The row model (§6.6): the visible rows (`Row` objects for the current page only), the counts\n * and the status. The same object until data, state or columns change the result.\n */\n getRowModel(): RowModel<T, M>\n /**\n * The rows that failed `schema` in the current data (§5.1), in every `invalidRows` mode:\n * `{ index, raw, issues }` with `index` into `options.data`. Empty without a schema.\n */\n getInvalidRows(): readonly InvalidRow[]\n /** Oldest first, at most 100 entries. */\n getDiagnostics(): Diagnostic[]\n /**\n * The last pipeline run (§8.4): rows per step, schema drops, the page, open diagnostics, and\n * timings when traced (the tracer runs in development). `String(table.explain())` is a one-line\n * summary. Read-only.\n */\n explain(): TableExplanation\n /**\n * Replays the row with key `key` through the schema and every step (§8.4): which step kept it\n * where, and the step that removed it, with a fix. Values are redacted like diagnostics (§8.2).\n * `String(table.explainRow(key))` is the console report. Read-only.\n */\n explainRow(key: RowKey): RowExplanation\n /**\n * Runs `options.fetcher` again with the current params (§7.1), e.g. a Retry button after\n * `DK_FETCH_FAILED`. Never rejects. Without a fetcher: `{ status: 'applied' }` at once.\n */\n refetch(): Promise<FetchOutcome>\n /** Releases listeners. Later mutating calls are no-ops that report `DK_TABLE_DESTROYED`. */\n destroy(): void\n}\n"],"mappings":";AA+BO,IAAM,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../src/table/types.ts"],"sourcesContent":["import type { ColumnDef, LooseRow, Path } from '../column'\nimport type { RowExplanation, TableExplanation } from '../diagnostics/explain'\nimport type { RedactFn, RedactLevel } from '../diagnostics/redact'\nimport type { Diagnostic } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { InvalidRow } from '../ingest/schema'\nimport type { TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { RowKey } from '../pipeline/row'\nimport type { RowModel } from '../pipeline/types'\nimport type { FetchOutcome } from '../source/fetcher'\nimport type { ServerFetcher } from '../source/server-brand'\n\n/** Table state: one entry per slice (feature state is typed by `StateOf`, T08). */\nexport type TableState = Record<string, unknown>\n\n/** A partial state merged per slice, or a function of the current state returning one. */\nexport type Updater<S> = Partial<S> | ((prev: S) => Partial<S>)\n\n/** `options.state`: a slice given as `undefined` counts as not passed (owned by the store). */\nexport type ControlledState<S> = { [K in keyof S]?: S[K] | undefined }\n\n/** Called after each committed change with the new and the previously notified snapshot. */\nexport type Listener<S> = (state: S, prev: S) => void\n\n/**\n * Max consecutive re-entrant notification rounds (a listener updating state, whose listeners\n * update state, …). The update that would start the next round is dropped (`DK_UPDATE_LOOP`).\n */\nexport const UPDATE_DEPTH_LIMIT = 50\n\nexport interface TableDiagnosticsOptions {\n /**\n * Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level\n * (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never\n * logged; a `rowKey` path into a sensitive column is hashed at every level.\n */\n redact?: RedactLevel | RedactFn\n}\n\nexport interface TableOptions<T, S extends TableState = TableState> {\n /** Names the table in diagnostics; default `table#n`. */\n name?: string\n data?: readonly T[]\n columns?: readonly ColumnDef<T>[]\n /**\n * A Standard Schema v1 (zod, valibot, arktype, …) every row is validated with (§5.1), opted in\n * with `withSchema(UserSchema)` from `@datakable/core/schema`; the row type is its output. An\n * async schema sets `status: 'validating'` while it runs. Read once, at creation.\n */\n schema?: TableSchema<unknown, T>\n /**\n * What happens to rows that fail `schema` (§5.1): `'drop'` (default) removes them, `'flag'` keeps\n * them with `row.valid === false`, `'throw'` throws `DatakableSchemaError`. All three list them in\n * `getInvalidRows()`. Read once, at creation; no effect without `schema`.\n */\n invalidRows?: InvalidRowsMode\n /** Row identity (§5.2). Read by redaction here; key handling is the ingest's. */\n rowKey?: Path<LooseRow<T>> | ((row: T) => string | number)\n /**\n * Installed features (§3.3), in any order: the pipeline runs them by `stage`. Each adds its state\n * slice and merges its API onto the table. Read once, at creation (`setOptions` ignores it).\n */\n features?: readonly AnyFeature[]\n /** Starting values of owned slices; a feature slice given here replaces its `initialState()`. */\n initialState?: Partial<S>\n /**\n * Controlled slices (§3.2): every key with a defined value is owned by the caller, who feeds\n * changes back through `setOptions({ state })`. Slices not passed are owned by the store.\n */\n state?: ControlledState<S>\n /**\n * Receives changes to controlled slices as an updater that merges only the changed slices into\n * the caller's state: `onStateChange: (u) => setControlled(u)`. Not called for owned slices.\n */\n onStateChange?: (updater: (prev: Partial<S>) => Partial<S>) => void\n /** User / feature interceptors (§3.4). The errorBoundary is always installed outermost. */\n interceptors?: readonly Interceptor<OpName>[]\n /** User sink for every diagnostic (e.g. Sentry). */\n onDiagnostic?: (d: Diagnostic) => void\n /**\n * ms before an async request (`fetcher`, `searchFn`) is aborted with `DK_ASYNC_TIMEOUT`.\n * Default 30 000; `Infinity` disables it. Read once, at creation.\n */\n timeout?: number\n /**\n * Server source (§7.1): `fetcher({ query, filters, sort, pageIndex, pageSize, signal })` →\n * `{ rows, rowCount }`. It receives only the state of the server-mode features and runs at\n * creation and whenever that state changes (`status: 'fetching'`), through `async:request`\n * (latest wins, abort, `timeout`). Needs at least one feature with `mode: 'server'`\n * (`DK_FETCHER_WITHOUT_SERVER_MODE`). The returned rows go through `schema` and the data checks.\n * Opt in with `withFetcher(fetchUsers)` from `@datakable/core/server`: a table without a fetcher\n * does not bundle the server source (changed 2026-10-13 for §9.3).\n */\n fetcher?: ServerFetcher<T>\n /**\n * Server mode without a fetcher (§7.1): the total number of rows on the server, while `data` is\n * only the current page. Server-mode pagination counts pages from it (`DK_ROW_COUNT_MISSING`\n * when missing).\n */\n rowCount?: number\n /**\n * SSR (§7.2): the rows the server already produced for the initial state (e.g. its fetcher\n * result). Ingested at creation like `data` when `data` is not given, and counts as the result of\n * the initial async work: the fetcher's first request is skipped (a later server-state change\n * fetches), a `searchFn` is not called for the initial query, and hydration does not re-fetch.\n * With server pagination, `rowCount` gives its total. Read once, at creation.\n */\n initialData?: readonly T[]\n /** Rethrow error-level diagnostics as `DatakableError`. */\n strict?: boolean\n diagnostics?: TableDiagnosticsOptions\n}\n\nexport interface Table<\n T,\n S extends TableState = TableState,\n M extends InvalidRowsMode = 'drop',\n /** The element type `setOptions({ data })` accepts: `unknown` (raw input) with a schema. */\n In = T,\n> {\n /** The current immutable snapshot; the same reference until a slice changes. */\n getState(): S\n setState(updater: Updater<S>): void\n /** Returns the unsubscribe function. One throwing listener never stops the others. */\n subscribe(listener: Listener<S>): () => void\n /** Merges `options` into the current ones (`table:setOptions`). */\n setOptions(\n options: Partial<Omit<TableOptions<T, S>, 'features' | 'data'>> & { data?: readonly In[] },\n ): void\n getOptions(): Readonly<TableOptions<T, S>>\n /** The ingested data (the previous data while an async ingest is pending or failed). */\n getData(): readonly T[]\n /**\n * The row model (§6.6): the visible rows (`Row` objects for the current page only), the counts\n * and the status. The same object until data, state or columns change the result.\n */\n getRowModel(): RowModel<T, M>\n /**\n * The rows that failed `schema` in the current data (§5.1), in every `invalidRows` mode:\n * `{ index, raw, issues }` with `index` into `options.data`. Empty without a schema.\n */\n getInvalidRows(): readonly InvalidRow[]\n /** Oldest first, at most 100 entries. */\n getDiagnostics(): Diagnostic[]\n /**\n * The last pipeline run (§8.4): rows per step, schema drops, the page, open diagnostics, and\n * timings when traced (the tracer runs in development). `String(table.explain())` is a one-line\n * summary. Read-only.\n */\n explain(): TableExplanation\n /**\n * Replays the row with key `key` through the schema and every step (§8.4): which step kept it\n * where, and the step that removed it, with a fix. Values are redacted like diagnostics (§8.2).\n * `String(table.explainRow(key))` is the console report. Read-only.\n */\n explainRow(key: RowKey): RowExplanation\n /**\n * Runs `options.fetcher` again with the current params (§7.1), e.g. a Retry button after\n * `DK_FETCH_FAILED`. Never rejects. Without a fetcher: `{ status: 'applied' }` at once.\n */\n refetch(): Promise<FetchOutcome>\n /** Releases listeners. Later mutating calls are no-ops that report `DK_TABLE_DESTROYED`. */\n destroy(): void\n}\n"],"mappings":";AA+BO,IAAM,qBAAqB;","names":[]}
@@ -1,5 +1,5 @@
1
- export { aw as validityDiagnostics } from './codes-OsjAR3Wz.cjs';
2
- import { F as Feature, b as FixedTypes } from './explain-vkD2cewE.cjs';
1
+ export { aw as validityDiagnostics } from './codes-B9bCK73-.cjs';
2
+ import { F as Feature, b as FixedTypes } from './explain-Q8Cg0cZy.cjs';
3
3
 
4
4
  /** Which rows the `validity` step keeps (§6.2). */
5
5
  type ValidityState = 'all' | 'valid' | 'invalid';
@@ -1,5 +1,5 @@
1
- export { aw as validityDiagnostics } from './codes-OsjAR3Wz.js';
2
- import { F as Feature, b as FixedTypes } from './explain-Dn99IQt0.js';
1
+ export { aw as validityDiagnostics } from './codes-B9bCK73-.js';
2
+ import { F as Feature, b as FixedTypes } from './explain-i0OATi_W.js';
3
3
 
4
4
  /** Which rows the `validity` step keeps (§6.2). */
5
5
  type ValidityState = 'all' | 'valid' | 'invalid';
package/dist/version.cjs CHANGED
@@ -25,7 +25,7 @@ __export(version_exports, {
25
25
  module.exports = __toCommonJS(version_exports);
26
26
 
27
27
  // package.json
28
- var version = "1.0.1";
28
+ var version = "1.1.0";
29
29
 
30
30
  // src/version.ts
31
31
  var VERSION = version;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../package.json"],"sourcesContent":["// Read from package.json at build time (the bundler inlines the one field), so a release never\n// ships a stale hard-coded version.\nimport { version } from '../package.json'\n\n/** The `@datakable/core` version (`context.package`, docs URLs, the global version registry). */\nexport const VERSION: string = version\n","{\n \"name\": \"@datakable/core\",\n \"version\": \"1.0.1\",\n \"description\": \"Headless, framework-agnostic core of DataKable \\u2014 table state, sorting, filtering and types.\",\n \"license\": \"MIT\",\n \"author\": \"Supakit Kitjanabumrungsak\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Ink01101011/datakable.git\",\n \"directory\": \"packages/core\"\n },\n \"homepage\": \"https://github.com/Ink01101011/datakable#readme\",\n \"bugs\": \"https://github.com/Ink01101011/datakable/issues\",\n \"keywords\": [\n \"react\",\n \"datatable\",\n \"table\",\n \"data-grid\",\n \"typescript\",\n \"type-safe\",\n \"headless\"\n ],\n \"type\": \"module\",\n \"sideEffects\": [\n \"./dist/messages/index.js\",\n \"./dist/messages/index.cjs\"\n ],\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n },\n \"./column-filters\": {\n \"import\": {\n \"types\": \"./dist/column-filters.d.ts\",\n \"default\": \"./dist/features/column-filters/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/column-filters.d.cts\",\n \"default\": \"./dist/features/column-filters/index.cjs\"\n }\n },\n \"./validity\": {\n \"import\": {\n \"types\": \"./dist/validity.d.ts\",\n \"default\": \"./dist/features/validity/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/validity.d.cts\",\n \"default\": \"./dist/features/validity/index.cjs\"\n }\n },\n \"./search\": {\n \"import\": {\n \"types\": \"./dist/search.d.ts\",\n \"default\": \"./dist/features/search/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/search.d.cts\",\n \"default\": \"./dist/features/search/index.cjs\"\n }\n },\n \"./sorting\": {\n \"import\": {\n \"types\": \"./dist/sorting.d.ts\",\n \"default\": \"./dist/features/sorting/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/sorting.d.cts\",\n \"default\": \"./dist/features/sorting/index.cjs\"\n }\n },\n \"./pagination\": {\n \"import\": {\n \"types\": \"./dist/pagination.d.ts\",\n \"default\": \"./dist/features/pagination/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/pagination.d.cts\",\n \"default\": \"./dist/features/pagination/index.cjs\"\n }\n },\n \"./messages\": {\n \"import\": {\n \"types\": \"./dist/messages.d.ts\",\n \"default\": \"./dist/messages/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/messages.d.cts\",\n \"default\": \"./dist/messages/index.cjs\"\n }\n },\n \"./schema\": {\n \"import\": {\n \"types\": \"./dist/schema.d.ts\",\n \"default\": \"./dist/ingest/schema-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/schema.d.cts\",\n \"default\": \"./dist/ingest/schema-entry.cjs\"\n }\n },\n \"./search-async\": {\n \"import\": {\n \"types\": \"./dist/search-async.d.ts\",\n \"default\": \"./dist/features/search/search-async-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/search-async.d.cts\",\n \"default\": \"./dist/features/search/search-async-entry.cjs\"\n }\n },\n \"./server\": {\n \"import\": {\n \"types\": \"./dist/server.d.ts\",\n \"default\": \"./dist/source/server-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/server.d.cts\",\n \"default\": \"./dist/source/server-entry.cjs\"\n }\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/postbuild.mjs\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"check:pkg\": \"publint --strict && attw --pack . --profile node16 && node scripts/check-catalog.mjs\",\n \"check:catalog\": \"node scripts/check-catalog.mjs\"\n },\n \"dependencies\": {\n \"ex-search\": \"1.1.1\",\n \"exsorted\": \"1.2.3\",\n \"kk-debounce\": \"1.1.0\"\n },\n \"devDependencies\": {\n \"esbuild\": \"0.27.7\"\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEE,cAAW;;;ADGN,IAAM,UAAkB;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts","../package.json"],"sourcesContent":["// Read from package.json at build time (the bundler inlines the one field), so a release never\n// ships a stale hard-coded version.\nimport { version } from '../package.json'\n\n/** The `@datakable/core` version (`context.package`, docs URLs, the global version registry). */\nexport const VERSION: string = version\n","{\n \"name\": \"@datakable/core\",\n \"version\": \"1.1.0\",\n \"description\": \"Headless, framework-agnostic core of DataKable \\u2014 table state, sorting, filtering and types.\",\n \"license\": \"MIT\",\n \"author\": \"Supakit Kitjanabumrungsak\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Ink01101011/datakable.git\",\n \"directory\": \"packages/core\"\n },\n \"homepage\": \"https://github.com/Ink01101011/datakable#readme\",\n \"bugs\": \"https://github.com/Ink01101011/datakable/issues\",\n \"keywords\": [\n \"react\",\n \"datatable\",\n \"table\",\n \"data-grid\",\n \"typescript\",\n \"type-safe\",\n \"headless\"\n ],\n \"type\": \"module\",\n \"sideEffects\": [\n \"./dist/messages/index.js\",\n \"./dist/messages/index.cjs\",\n \"./dist/diagnostics/process-guard.js\",\n \"./dist/diagnostics/process-guard.cjs\"\n ],\n \"engines\": {\n \"node\": \">=18.18.0\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \"column-filters\": [\n \"./dist/column-filters.d.ts\"\n ],\n \"validity\": [\n \"./dist/validity.d.ts\"\n ],\n \"search\": [\n \"./dist/search.d.ts\"\n ],\n \"sorting\": [\n \"./dist/sorting.d.ts\"\n ],\n \"pagination\": [\n \"./dist/pagination.d.ts\"\n ],\n \"messages\": [\n \"./dist/messages.d.ts\"\n ],\n \"schema\": [\n \"./dist/schema.d.ts\"\n ],\n \"search-async\": [\n \"./dist/search-async.d.ts\"\n ],\n \"server\": [\n \"./dist/server.d.ts\"\n ]\n }\n },\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n },\n \"./column-filters\": {\n \"import\": {\n \"types\": \"./dist/column-filters.d.ts\",\n \"default\": \"./dist/features/column-filters/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/column-filters.d.cts\",\n \"default\": \"./dist/features/column-filters/index.cjs\"\n }\n },\n \"./validity\": {\n \"import\": {\n \"types\": \"./dist/validity.d.ts\",\n \"default\": \"./dist/features/validity/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/validity.d.cts\",\n \"default\": \"./dist/features/validity/index.cjs\"\n }\n },\n \"./search\": {\n \"import\": {\n \"types\": \"./dist/search.d.ts\",\n \"default\": \"./dist/features/search/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/search.d.cts\",\n \"default\": \"./dist/features/search/index.cjs\"\n }\n },\n \"./sorting\": {\n \"import\": {\n \"types\": \"./dist/sorting.d.ts\",\n \"default\": \"./dist/features/sorting/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/sorting.d.cts\",\n \"default\": \"./dist/features/sorting/index.cjs\"\n }\n },\n \"./pagination\": {\n \"import\": {\n \"types\": \"./dist/pagination.d.ts\",\n \"default\": \"./dist/features/pagination/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/pagination.d.cts\",\n \"default\": \"./dist/features/pagination/index.cjs\"\n }\n },\n \"./messages\": {\n \"import\": {\n \"types\": \"./dist/messages.d.ts\",\n \"default\": \"./dist/messages/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/messages.d.cts\",\n \"default\": \"./dist/messages/index.cjs\"\n }\n },\n \"./schema\": {\n \"import\": {\n \"types\": \"./dist/schema.d.ts\",\n \"default\": \"./dist/ingest/schema-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/schema.d.cts\",\n \"default\": \"./dist/ingest/schema-entry.cjs\"\n }\n },\n \"./search-async\": {\n \"import\": {\n \"types\": \"./dist/search-async.d.ts\",\n \"default\": \"./dist/features/search/search-async-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/search-async.d.cts\",\n \"default\": \"./dist/features/search/search-async-entry.cjs\"\n }\n },\n \"./server\": {\n \"import\": {\n \"types\": \"./dist/server.d.ts\",\n \"default\": \"./dist/source/server-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/server.d.cts\",\n \"default\": \"./dist/source/server-entry.cjs\"\n }\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/postbuild.mjs\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"check:pkg\": \"publint --strict && attw --pack . && node scripts/check-catalog.mjs\",\n \"check:catalog\": \"node scripts/check-catalog.mjs\"\n },\n \"dependencies\": {\n \"ex-search\": \"1.1.1\",\n \"exsorted\": \"1.2.3\",\n \"kk-debounce\": \"1.1.0\"\n },\n \"devDependencies\": {\n \"esbuild\": \"0.27.7\"\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEE,cAAW;;;ADGN,IAAM,UAAkB;","names":[]}
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "1.0.1";
2
+ var version = "1.1.0";
3
3
 
4
4
  // src/version.ts
5
5
  var VERSION = version;
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/version.ts"],"sourcesContent":["{\n \"name\": \"@datakable/core\",\n \"version\": \"1.0.1\",\n \"description\": \"Headless, framework-agnostic core of DataKable \\u2014 table state, sorting, filtering and types.\",\n \"license\": \"MIT\",\n \"author\": \"Supakit Kitjanabumrungsak\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Ink01101011/datakable.git\",\n \"directory\": \"packages/core\"\n },\n \"homepage\": \"https://github.com/Ink01101011/datakable#readme\",\n \"bugs\": \"https://github.com/Ink01101011/datakable/issues\",\n \"keywords\": [\n \"react\",\n \"datatable\",\n \"table\",\n \"data-grid\",\n \"typescript\",\n \"type-safe\",\n \"headless\"\n ],\n \"type\": \"module\",\n \"sideEffects\": [\n \"./dist/messages/index.js\",\n \"./dist/messages/index.cjs\"\n ],\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n },\n \"./column-filters\": {\n \"import\": {\n \"types\": \"./dist/column-filters.d.ts\",\n \"default\": \"./dist/features/column-filters/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/column-filters.d.cts\",\n \"default\": \"./dist/features/column-filters/index.cjs\"\n }\n },\n \"./validity\": {\n \"import\": {\n \"types\": \"./dist/validity.d.ts\",\n \"default\": \"./dist/features/validity/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/validity.d.cts\",\n \"default\": \"./dist/features/validity/index.cjs\"\n }\n },\n \"./search\": {\n \"import\": {\n \"types\": \"./dist/search.d.ts\",\n \"default\": \"./dist/features/search/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/search.d.cts\",\n \"default\": \"./dist/features/search/index.cjs\"\n }\n },\n \"./sorting\": {\n \"import\": {\n \"types\": \"./dist/sorting.d.ts\",\n \"default\": \"./dist/features/sorting/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/sorting.d.cts\",\n \"default\": \"./dist/features/sorting/index.cjs\"\n }\n },\n \"./pagination\": {\n \"import\": {\n \"types\": \"./dist/pagination.d.ts\",\n \"default\": \"./dist/features/pagination/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/pagination.d.cts\",\n \"default\": \"./dist/features/pagination/index.cjs\"\n }\n },\n \"./messages\": {\n \"import\": {\n \"types\": \"./dist/messages.d.ts\",\n \"default\": \"./dist/messages/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/messages.d.cts\",\n \"default\": \"./dist/messages/index.cjs\"\n }\n },\n \"./schema\": {\n \"import\": {\n \"types\": \"./dist/schema.d.ts\",\n \"default\": \"./dist/ingest/schema-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/schema.d.cts\",\n \"default\": \"./dist/ingest/schema-entry.cjs\"\n }\n },\n \"./search-async\": {\n \"import\": {\n \"types\": \"./dist/search-async.d.ts\",\n \"default\": \"./dist/features/search/search-async-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/search-async.d.cts\",\n \"default\": \"./dist/features/search/search-async-entry.cjs\"\n }\n },\n \"./server\": {\n \"import\": {\n \"types\": \"./dist/server.d.ts\",\n \"default\": \"./dist/source/server-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/server.d.cts\",\n \"default\": \"./dist/source/server-entry.cjs\"\n }\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/postbuild.mjs\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"check:pkg\": \"publint --strict && attw --pack . --profile node16 && node scripts/check-catalog.mjs\",\n \"check:catalog\": \"node scripts/check-catalog.mjs\"\n },\n \"dependencies\": {\n \"ex-search\": \"1.1.1\",\n \"exsorted\": \"1.2.3\",\n \"kk-debounce\": \"1.1.0\"\n },\n \"devDependencies\": {\n \"esbuild\": \"0.27.7\"\n }\n}\n","// Read from package.json at build time (the bundler inlines the one field), so a release never\n// ships a stale hard-coded version.\nimport { version } from '../package.json'\n\n/** The `@datakable/core` version (`context.package`, docs URLs, the global version registry). */\nexport const VERSION: string = version\n"],"mappings":";AAEE,cAAW;;;ACGN,IAAM,UAAkB;","names":[]}
1
+ {"version":3,"sources":["../package.json","../src/version.ts"],"sourcesContent":["{\n \"name\": \"@datakable/core\",\n \"version\": \"1.1.0\",\n \"description\": \"Headless, framework-agnostic core of DataKable \\u2014 table state, sorting, filtering and types.\",\n \"license\": \"MIT\",\n \"author\": \"Supakit Kitjanabumrungsak\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Ink01101011/datakable.git\",\n \"directory\": \"packages/core\"\n },\n \"homepage\": \"https://github.com/Ink01101011/datakable#readme\",\n \"bugs\": \"https://github.com/Ink01101011/datakable/issues\",\n \"keywords\": [\n \"react\",\n \"datatable\",\n \"table\",\n \"data-grid\",\n \"typescript\",\n \"type-safe\",\n \"headless\"\n ],\n \"type\": \"module\",\n \"sideEffects\": [\n \"./dist/messages/index.js\",\n \"./dist/messages/index.cjs\",\n \"./dist/diagnostics/process-guard.js\",\n \"./dist/diagnostics/process-guard.cjs\"\n ],\n \"engines\": {\n \"node\": \">=18.18.0\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"typesVersions\": {\n \"*\": {\n \"column-filters\": [\n \"./dist/column-filters.d.ts\"\n ],\n \"validity\": [\n \"./dist/validity.d.ts\"\n ],\n \"search\": [\n \"./dist/search.d.ts\"\n ],\n \"sorting\": [\n \"./dist/sorting.d.ts\"\n ],\n \"pagination\": [\n \"./dist/pagination.d.ts\"\n ],\n \"messages\": [\n \"./dist/messages.d.ts\"\n ],\n \"schema\": [\n \"./dist/schema.d.ts\"\n ],\n \"search-async\": [\n \"./dist/search-async.d.ts\"\n ],\n \"server\": [\n \"./dist/server.d.ts\"\n ]\n }\n },\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n },\n \"./column-filters\": {\n \"import\": {\n \"types\": \"./dist/column-filters.d.ts\",\n \"default\": \"./dist/features/column-filters/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/column-filters.d.cts\",\n \"default\": \"./dist/features/column-filters/index.cjs\"\n }\n },\n \"./validity\": {\n \"import\": {\n \"types\": \"./dist/validity.d.ts\",\n \"default\": \"./dist/features/validity/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/validity.d.cts\",\n \"default\": \"./dist/features/validity/index.cjs\"\n }\n },\n \"./search\": {\n \"import\": {\n \"types\": \"./dist/search.d.ts\",\n \"default\": \"./dist/features/search/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/search.d.cts\",\n \"default\": \"./dist/features/search/index.cjs\"\n }\n },\n \"./sorting\": {\n \"import\": {\n \"types\": \"./dist/sorting.d.ts\",\n \"default\": \"./dist/features/sorting/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/sorting.d.cts\",\n \"default\": \"./dist/features/sorting/index.cjs\"\n }\n },\n \"./pagination\": {\n \"import\": {\n \"types\": \"./dist/pagination.d.ts\",\n \"default\": \"./dist/features/pagination/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/pagination.d.cts\",\n \"default\": \"./dist/features/pagination/index.cjs\"\n }\n },\n \"./messages\": {\n \"import\": {\n \"types\": \"./dist/messages.d.ts\",\n \"default\": \"./dist/messages/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/messages.d.cts\",\n \"default\": \"./dist/messages/index.cjs\"\n }\n },\n \"./schema\": {\n \"import\": {\n \"types\": \"./dist/schema.d.ts\",\n \"default\": \"./dist/ingest/schema-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/schema.d.cts\",\n \"default\": \"./dist/ingest/schema-entry.cjs\"\n }\n },\n \"./search-async\": {\n \"import\": {\n \"types\": \"./dist/search-async.d.ts\",\n \"default\": \"./dist/features/search/search-async-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/search-async.d.cts\",\n \"default\": \"./dist/features/search/search-async-entry.cjs\"\n }\n },\n \"./server\": {\n \"import\": {\n \"types\": \"./dist/server.d.ts\",\n \"default\": \"./dist/source/server-entry.js\"\n },\n \"require\": {\n \"types\": \"./dist/server.d.cts\",\n \"default\": \"./dist/source/server-entry.cjs\"\n }\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/postbuild.mjs\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc -p tsconfig.json\",\n \"check:pkg\": \"publint --strict && attw --pack . && node scripts/check-catalog.mjs\",\n \"check:catalog\": \"node scripts/check-catalog.mjs\"\n },\n \"dependencies\": {\n \"ex-search\": \"1.1.1\",\n \"exsorted\": \"1.2.3\",\n \"kk-debounce\": \"1.1.0\"\n },\n \"devDependencies\": {\n \"esbuild\": \"0.27.7\"\n }\n}\n","// Read from package.json at build time (the bundler inlines the one field), so a release never\n// ships a stale hard-coded version.\nimport { version } from '../package.json'\n\n/** The `@datakable/core` version (`context.package`, docs URLs, the global version registry). */\nexport const VERSION: string = version\n"],"mappings":";AAEE,cAAW;;;ACGN,IAAM,UAAkB;","names":[]}
@@ -26,6 +26,7 @@ __export(codes_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(codes_exports);
28
28
  var import_catalog = require("../diagnostics/catalog.cjs");
29
+ var import_process_guard = require("../diagnostics/process-guard.cjs");
29
30
  var import_semver = require("./semver.cjs");
30
31
  var FIXED_GROUP = ["@datakable/core", "@datakable/react", "datakable"];
31
32
  var OPTIONS = "options.";
@@ -55,6 +56,15 @@ var versioningMessages = {
55
56
  'or force one version: "pnpm": { "overrides": { "@datakable/core": "<version>" } } in package.json (npm "overrides", yarn "resolutions")'
56
57
  ]
57
58
  },
59
+ DK_DUPLICATE_CORE_SAME_VERSION: {
60
+ problem: (d) => `@datakable/core ${d.version} is loaded ${d.copies} times, all this version: its ESM and CJS builds, a bundler's layers (Next.js dev: RSC, SSR, route handlers), a hot reload, or two installs of it`,
61
+ impact: () => "tables work, each with its own copy; a DatakableError thrown by one copy is not an instanceof the classes of the other, and a browser bundle with both ships core twice",
62
+ fix: () => [
63
+ 'compare errors by their .code (e.g. err.code === "DK_INVALID_OPTION"), not instanceof',
64
+ "mixed import and require of @datakable/core in one Node process: use one of them",
65
+ "in a browser bundle, find the second install: pnpm why @datakable/core, then pnpm dedupe"
66
+ ]
67
+ },
58
68
  DK_VERSION_MISMATCH: {
59
69
  problem: (d) => `${d.package}@${d.version} is used with @datakable/core@${d.core}; these packages are released together and must have the same version`,
60
70
  impact: () => "it may work, but APIs and types between the two can disagree",
@@ -103,6 +113,14 @@ var versioningDiagnostics = [
103
113
  },
104
114
  process.env.NODE_ENV !== "production" && versioningMessages.DK_DUPLICATE_CORE
105
115
  ),
116
+ (0, import_catalog.defineDiagnostic)(
117
+ {
118
+ code: "DK_DUPLICATE_CORE_SAME_VERSION",
119
+ level: "info",
120
+ where: () => CREATE
121
+ },
122
+ process.env.NODE_ENV !== "production" && versioningMessages.DK_DUPLICATE_CORE_SAME_VERSION
123
+ ),
106
124
  (0, import_catalog.defineDiagnostic)(
107
125
  {
108
126
  code: "DK_VERSION_MISMATCH",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/versioning/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport type { Location } from '../diagnostics/types'\nimport { compareVersions, parseVersion } from './semver'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DEPRECATED: {\n /** `options.<path>` for an option, else the method, e.g. `table.setGlobalFilter`. */\n api: string\n since: string\n removeIn: string\n replacement: string\n codemod?: string\n }\n DK_DUPLICATE_CORE: {\n /** Versions of every registered core copy, in registration order (this copy last). */\n versions: string[]\n }\n DK_VERSION_MISMATCH: {\n /** The fixed-group package whose version differs, e.g. `@datakable/react`. */\n package: string\n version: string\n /** This core's version. */\n core: string\n }\n DK_DEP_INCOMPATIBLE: {\n /** The feature that uses the capability. */\n feature: string\n dependency: string\n capability: string\n /** The dependency range DataKable supports. */\n supported: string\n /** What is used instead. */\n fallback: string\n }\n DK_FEATURE_INCOMPATIBLE: {\n feature: string\n /** `requires.core` of the feature. */\n required: string\n core: string\n /** `true` when `required` is not a semver range. */\n invalid: boolean\n }\n }\n}\n\n/** The fixed release group (§12): always published with one version. */\nexport const FIXED_GROUP = ['@datakable/core', '@datakable/react', 'datakable'] as const\n\nconst OPTIONS = 'options.'\nconst CREATE: Location = { kind: 'operation', op: 'createTable' }\n\n/** The newer of two versions (unparseable ones lose). */\nconst newest = (a: string, b: string): string => {\n const pa = parseVersion(a)\n const pb = parseVersion(b)\n if (!pa) return b\n if (!pb) return a\n return compareVersions(pa, pb) < 0 ? b : a\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const versioningMessages: DiagnosticMessages<\n | 'DK_DEPRECATED'\n | 'DK_DUPLICATE_CORE'\n | 'DK_VERSION_MISMATCH'\n | 'DK_DEP_INCOMPATIBLE'\n | 'DK_FEATURE_INCOMPATIBLE'\n> = {\n DK_DEPRECATED: {\n problem: (d) => `${d.api} is deprecated since ${d.since}`,\n impact: (d) =>\n `it still works in this version and is removed in ${d.removeIn}; this warning is shown ` +\n 'once per session',\n fix: (d) => [\n `use ${d.replacement} instead`,\n ...(d.codemod ? [`run the codemod: ${d.codemod}`] : []),\n ],\n },\n DK_DUPLICATE_CORE: {\n problem: (d) =>\n `${d.versions.length} copies of @datakable/core are loaded (${d.versions.join(', ')}); ` +\n 'features, interceptors and types from one copy do not match the other',\n impact: () =>\n 'each table works with its own copy, but plugins built against the other copy may be ' +\n 'ignored or fail instanceof / registry checks, and the bundle ships core twice',\n fix: () => [\n 'find who pulls the second copy: pnpm why @datakable/core (npm ls @datakable/core / yarn why)',\n 'dedupe it: pnpm dedupe (npm dedupe), and align @datakable/react and datakable to one version',\n 'or force one version: \"pnpm\": { \"overrides\": { \"@datakable/core\": \"<version>\" } } in ' +\n 'package.json (npm \"overrides\", yarn \"resolutions\")',\n ],\n },\n DK_VERSION_MISMATCH: {\n problem: (d) =>\n `${d.package}@${d.version} is used with @datakable/core@${d.core}; ` +\n 'these packages are released together and must have the same version',\n impact: () => 'it may work, but APIs and types between the two can disagree',\n fix: (d) => {\n const v = newest(d.version, d.core)\n return [\n `pnpm add ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')} (install only the ones you use)`,\n `or with npm: npm install ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')}`,\n ]\n },\n },\n DK_DEP_INCOMPATIBLE: {\n problem: (d) =>\n `${d.dependency} has no usable ${d.capability}; it was probably overridden to a version ` +\n `outside ${d.supported}`,\n impact: (d) => `feature \"${d.feature}\" uses ${d.fallback} instead; results stay correct`,\n fix: (d) => [\n `install a supported version: pnpm add ${d.dependency}@\"${d.supported}\" (check pnpm why ${d.dependency})`,\n `remove the override / resolution of ${d.dependency} from package.json`,\n ],\n },\n DK_FEATURE_INCOMPATIBLE: {\n problem: (d) =>\n d.invalid\n ? `feature \"${d.feature}\" declares requires.core \"${d.required}\", which is not a semver range`\n : `feature \"${d.feature}\" requires @datakable/core ${d.required}, but this is ${d.core}`,\n impact: () => 'the table was not created',\n fix: (d) =>\n d.invalid\n ? [\n `the feature author must set requires: { core: '^1.0' } (a semver range such as ` +\n `'^1.0', '>=1.2 <2' or '1.x')`,\n `meanwhile, remove feature \"${d.feature}\" from options.features`,\n ]\n : [\n `upgrade core: pnpm add @datakable/core@\"${d.required}\" (and @datakable/react / ` +\n 'datakable to the same version)',\n `or use a version of feature \"${d.feature}\" that supports @datakable/core ${d.core}`,\n ],\n },\n}\n\n/** Codes owned by versioning (§8.5). */\nexport const versioningDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_DEPRECATED',\n level: 'warn',\n where: (d) =>\n d.api.startsWith(OPTIONS)\n ? { kind: 'option', path: d.api.slice(OPTIONS.length) }\n : { kind: 'operation', op: d.api },\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEPRECATED,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE',\n level: 'error',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE,\n ),\n defineDiagnostic(\n {\n code: 'DK_VERSION_MISMATCH',\n level: 'warn',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_VERSION_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_DEP_INCOMPATIBLE',\n level: 'warn',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEP_INCOMPATIBLE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_INCOMPATIBLE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_FEATURE_INCOMPATIBLE,\n ),\n]\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/versioning/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Location } from '../diagnostics/types'\nimport { compareVersions, parseVersion } from './semver'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DEPRECATED: {\n /** `options.<path>` for an option, else the method, e.g. `table.setGlobalFilter`. */\n api: string\n since: string\n removeIn: string\n replacement: string\n codemod?: string\n }\n DK_DUPLICATE_CORE: {\n /** Versions of every registered core copy, in registration order (this copy last). */\n versions: string[]\n }\n DK_DUPLICATE_CORE_SAME_VERSION: {\n version: string\n /** Copies of core registered so far (this copy included), all of `version`. */\n copies: number\n }\n DK_VERSION_MISMATCH: {\n /** The fixed-group package whose version differs, e.g. `@datakable/react`. */\n package: string\n version: string\n /** This core's version. */\n core: string\n }\n DK_DEP_INCOMPATIBLE: {\n /** The feature that uses the capability. */\n feature: string\n dependency: string\n capability: string\n /** The dependency range DataKable supports. */\n supported: string\n /** What is used instead. */\n fallback: string\n }\n DK_FEATURE_INCOMPATIBLE: {\n feature: string\n /** `requires.core` of the feature. */\n required: string\n core: string\n /** `true` when `required` is not a semver range. */\n invalid: boolean\n }\n }\n}\n\n/** The fixed release group (§12): always published with one version. */\nexport const FIXED_GROUP = ['@datakable/core', '@datakable/react', 'datakable'] as const\n\nconst OPTIONS = 'options.'\nconst CREATE: Location = { kind: 'operation', op: 'createTable' }\n\n/** The newer of two versions (unparseable ones lose). */\nconst newest = (a: string, b: string): string => {\n const pa = parseVersion(a)\n const pb = parseVersion(b)\n if (!pa) return b\n if (!pb) return a\n return compareVersions(pa, pb) < 0 ? b : a\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const versioningMessages: DiagnosticMessages<\n | 'DK_DEPRECATED'\n | 'DK_DUPLICATE_CORE'\n | 'DK_DUPLICATE_CORE_SAME_VERSION'\n | 'DK_VERSION_MISMATCH'\n | 'DK_DEP_INCOMPATIBLE'\n | 'DK_FEATURE_INCOMPATIBLE'\n> = {\n DK_DEPRECATED: {\n problem: (d) => `${d.api} is deprecated since ${d.since}`,\n impact: (d) =>\n `it still works in this version and is removed in ${d.removeIn}; this warning is shown ` +\n 'once per session',\n fix: (d) => [\n `use ${d.replacement} instead`,\n ...(d.codemod ? [`run the codemod: ${d.codemod}`] : []),\n ],\n },\n DK_DUPLICATE_CORE: {\n problem: (d) =>\n `${d.versions.length} copies of @datakable/core are loaded (${d.versions.join(', ')}); ` +\n 'features, interceptors and types from one copy do not match the other',\n impact: () =>\n 'each table works with its own copy, but plugins built against the other copy may be ' +\n 'ignored or fail instanceof / registry checks, and the bundle ships core twice',\n fix: () => [\n 'find who pulls the second copy: pnpm why @datakable/core (npm ls @datakable/core / yarn why)',\n 'dedupe it: pnpm dedupe (npm dedupe), and align @datakable/react and datakable to one version',\n 'or force one version: \"pnpm\": { \"overrides\": { \"@datakable/core\": \"<version>\" } } in ' +\n 'package.json (npm \"overrides\", yarn \"resolutions\")',\n ],\n },\n DK_DUPLICATE_CORE_SAME_VERSION: {\n problem: (d) =>\n `@datakable/core ${d.version} is loaded ${d.copies} times, all this version: its ESM and ` +\n \"CJS builds, a bundler's layers (Next.js dev: RSC, SSR, route handlers), a hot reload, or \" +\n 'two installs of it',\n impact: () =>\n 'tables work, each with its own copy; a DatakableError thrown by one copy is not an ' +\n 'instanceof the classes of the other, and a browser bundle with both ships core twice',\n fix: () => [\n 'compare errors by their .code (e.g. err.code === \"DK_INVALID_OPTION\"), not instanceof',\n 'mixed import and require of @datakable/core in one Node process: use one of them',\n 'in a browser bundle, find the second install: pnpm why @datakable/core, then pnpm dedupe',\n ],\n },\n DK_VERSION_MISMATCH: {\n problem: (d) =>\n `${d.package}@${d.version} is used with @datakable/core@${d.core}; ` +\n 'these packages are released together and must have the same version',\n impact: () => 'it may work, but APIs and types between the two can disagree',\n fix: (d) => {\n const v = newest(d.version, d.core)\n return [\n `pnpm add ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')} (install only the ones you use)`,\n `or with npm: npm install ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')}`,\n ]\n },\n },\n DK_DEP_INCOMPATIBLE: {\n problem: (d) =>\n `${d.dependency} has no usable ${d.capability}; it was probably overridden to a version ` +\n `outside ${d.supported}`,\n impact: (d) => `feature \"${d.feature}\" uses ${d.fallback} instead; results stay correct`,\n fix: (d) => [\n `install a supported version: pnpm add ${d.dependency}@\"${d.supported}\" (check pnpm why ${d.dependency})`,\n `remove the override / resolution of ${d.dependency} from package.json`,\n ],\n },\n DK_FEATURE_INCOMPATIBLE: {\n problem: (d) =>\n d.invalid\n ? `feature \"${d.feature}\" declares requires.core \"${d.required}\", which is not a semver range`\n : `feature \"${d.feature}\" requires @datakable/core ${d.required}, but this is ${d.core}`,\n impact: () => 'the table was not created',\n fix: (d) =>\n d.invalid\n ? [\n `the feature author must set requires: { core: '^1.0' } (a semver range such as ` +\n `'^1.0', '>=1.2 <2' or '1.x')`,\n `meanwhile, remove feature \"${d.feature}\" from options.features`,\n ]\n : [\n `upgrade core: pnpm add @datakable/core@\"${d.required}\" (and @datakable/react / ` +\n 'datakable to the same version)',\n `or use a version of feature \"${d.feature}\" that supports @datakable/core ${d.core}`,\n ],\n },\n}\n\n/** Codes owned by versioning (§8.5). */\nexport const versioningDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_DEPRECATED',\n level: 'warn',\n where: (d) =>\n d.api.startsWith(OPTIONS)\n ? { kind: 'option', path: d.api.slice(OPTIONS.length) }\n : { kind: 'operation', op: d.api },\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEPRECATED,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE',\n level: 'error',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE_SAME_VERSION',\n level: 'info',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE_SAME_VERSION,\n ),\n defineDiagnostic(\n {\n code: 'DK_VERSION_MISMATCH',\n level: 'warn',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_VERSION_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_DEP_INCOMPATIBLE',\n level: 'warn',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEP_INCOMPATIBLE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_INCOMPATIBLE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_FEATURE_INCOMPATIBLE,\n ),\n]\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/versioning/codes.ts
2
2
  import { defineDiagnostic } from "../diagnostics/catalog.js";
3
+ import "../diagnostics/process-guard.js";
3
4
  import { compareVersions, parseVersion } from "./semver.js";
4
5
  var FIXED_GROUP = ["@datakable/core", "@datakable/react", "datakable"];
5
6
  var OPTIONS = "options.";
@@ -29,6 +30,15 @@ var versioningMessages = {
29
30
  'or force one version: "pnpm": { "overrides": { "@datakable/core": "<version>" } } in package.json (npm "overrides", yarn "resolutions")'
30
31
  ]
31
32
  },
33
+ DK_DUPLICATE_CORE_SAME_VERSION: {
34
+ problem: (d) => `@datakable/core ${d.version} is loaded ${d.copies} times, all this version: its ESM and CJS builds, a bundler's layers (Next.js dev: RSC, SSR, route handlers), a hot reload, or two installs of it`,
35
+ impact: () => "tables work, each with its own copy; a DatakableError thrown by one copy is not an instanceof the classes of the other, and a browser bundle with both ships core twice",
36
+ fix: () => [
37
+ 'compare errors by their .code (e.g. err.code === "DK_INVALID_OPTION"), not instanceof',
38
+ "mixed import and require of @datakable/core in one Node process: use one of them",
39
+ "in a browser bundle, find the second install: pnpm why @datakable/core, then pnpm dedupe"
40
+ ]
41
+ },
32
42
  DK_VERSION_MISMATCH: {
33
43
  problem: (d) => `${d.package}@${d.version} is used with @datakable/core@${d.core}; these packages are released together and must have the same version`,
34
44
  impact: () => "it may work, but APIs and types between the two can disagree",
@@ -77,6 +87,14 @@ var versioningDiagnostics = [
77
87
  },
78
88
  process.env.NODE_ENV !== "production" && versioningMessages.DK_DUPLICATE_CORE
79
89
  ),
90
+ defineDiagnostic(
91
+ {
92
+ code: "DK_DUPLICATE_CORE_SAME_VERSION",
93
+ level: "info",
94
+ where: () => CREATE
95
+ },
96
+ process.env.NODE_ENV !== "production" && versioningMessages.DK_DUPLICATE_CORE_SAME_VERSION
97
+ ),
80
98
  defineDiagnostic(
81
99
  {
82
100
  code: "DK_VERSION_MISMATCH",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/versioning/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport type { Location } from '../diagnostics/types'\nimport { compareVersions, parseVersion } from './semver'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DEPRECATED: {\n /** `options.<path>` for an option, else the method, e.g. `table.setGlobalFilter`. */\n api: string\n since: string\n removeIn: string\n replacement: string\n codemod?: string\n }\n DK_DUPLICATE_CORE: {\n /** Versions of every registered core copy, in registration order (this copy last). */\n versions: string[]\n }\n DK_VERSION_MISMATCH: {\n /** The fixed-group package whose version differs, e.g. `@datakable/react`. */\n package: string\n version: string\n /** This core's version. */\n core: string\n }\n DK_DEP_INCOMPATIBLE: {\n /** The feature that uses the capability. */\n feature: string\n dependency: string\n capability: string\n /** The dependency range DataKable supports. */\n supported: string\n /** What is used instead. */\n fallback: string\n }\n DK_FEATURE_INCOMPATIBLE: {\n feature: string\n /** `requires.core` of the feature. */\n required: string\n core: string\n /** `true` when `required` is not a semver range. */\n invalid: boolean\n }\n }\n}\n\n/** The fixed release group (§12): always published with one version. */\nexport const FIXED_GROUP = ['@datakable/core', '@datakable/react', 'datakable'] as const\n\nconst OPTIONS = 'options.'\nconst CREATE: Location = { kind: 'operation', op: 'createTable' }\n\n/** The newer of two versions (unparseable ones lose). */\nconst newest = (a: string, b: string): string => {\n const pa = parseVersion(a)\n const pb = parseVersion(b)\n if (!pa) return b\n if (!pb) return a\n return compareVersions(pa, pb) < 0 ? b : a\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const versioningMessages: DiagnosticMessages<\n | 'DK_DEPRECATED'\n | 'DK_DUPLICATE_CORE'\n | 'DK_VERSION_MISMATCH'\n | 'DK_DEP_INCOMPATIBLE'\n | 'DK_FEATURE_INCOMPATIBLE'\n> = {\n DK_DEPRECATED: {\n problem: (d) => `${d.api} is deprecated since ${d.since}`,\n impact: (d) =>\n `it still works in this version and is removed in ${d.removeIn}; this warning is shown ` +\n 'once per session',\n fix: (d) => [\n `use ${d.replacement} instead`,\n ...(d.codemod ? [`run the codemod: ${d.codemod}`] : []),\n ],\n },\n DK_DUPLICATE_CORE: {\n problem: (d) =>\n `${d.versions.length} copies of @datakable/core are loaded (${d.versions.join(', ')}); ` +\n 'features, interceptors and types from one copy do not match the other',\n impact: () =>\n 'each table works with its own copy, but plugins built against the other copy may be ' +\n 'ignored or fail instanceof / registry checks, and the bundle ships core twice',\n fix: () => [\n 'find who pulls the second copy: pnpm why @datakable/core (npm ls @datakable/core / yarn why)',\n 'dedupe it: pnpm dedupe (npm dedupe), and align @datakable/react and datakable to one version',\n 'or force one version: \"pnpm\": { \"overrides\": { \"@datakable/core\": \"<version>\" } } in ' +\n 'package.json (npm \"overrides\", yarn \"resolutions\")',\n ],\n },\n DK_VERSION_MISMATCH: {\n problem: (d) =>\n `${d.package}@${d.version} is used with @datakable/core@${d.core}; ` +\n 'these packages are released together and must have the same version',\n impact: () => 'it may work, but APIs and types between the two can disagree',\n fix: (d) => {\n const v = newest(d.version, d.core)\n return [\n `pnpm add ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')} (install only the ones you use)`,\n `or with npm: npm install ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')}`,\n ]\n },\n },\n DK_DEP_INCOMPATIBLE: {\n problem: (d) =>\n `${d.dependency} has no usable ${d.capability}; it was probably overridden to a version ` +\n `outside ${d.supported}`,\n impact: (d) => `feature \"${d.feature}\" uses ${d.fallback} instead; results stay correct`,\n fix: (d) => [\n `install a supported version: pnpm add ${d.dependency}@\"${d.supported}\" (check pnpm why ${d.dependency})`,\n `remove the override / resolution of ${d.dependency} from package.json`,\n ],\n },\n DK_FEATURE_INCOMPATIBLE: {\n problem: (d) =>\n d.invalid\n ? `feature \"${d.feature}\" declares requires.core \"${d.required}\", which is not a semver range`\n : `feature \"${d.feature}\" requires @datakable/core ${d.required}, but this is ${d.core}`,\n impact: () => 'the table was not created',\n fix: (d) =>\n d.invalid\n ? [\n `the feature author must set requires: { core: '^1.0' } (a semver range such as ` +\n `'^1.0', '>=1.2 <2' or '1.x')`,\n `meanwhile, remove feature \"${d.feature}\" from options.features`,\n ]\n : [\n `upgrade core: pnpm add @datakable/core@\"${d.required}\" (and @datakable/react / ` +\n 'datakable to the same version)',\n `or use a version of feature \"${d.feature}\" that supports @datakable/core ${d.core}`,\n ],\n },\n}\n\n/** Codes owned by versioning (§8.5). */\nexport const versioningDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_DEPRECATED',\n level: 'warn',\n where: (d) =>\n d.api.startsWith(OPTIONS)\n ? { kind: 'option', path: d.api.slice(OPTIONS.length) }\n : { kind: 'operation', op: d.api },\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEPRECATED,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE',\n level: 'error',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE,\n ),\n defineDiagnostic(\n {\n code: 'DK_VERSION_MISMATCH',\n level: 'warn',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_VERSION_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_DEP_INCOMPATIBLE',\n level: 'warn',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEP_INCOMPATIBLE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_INCOMPATIBLE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_FEATURE_INCOMPATIBLE,\n ),\n]\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/versioning/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Location } from '../diagnostics/types'\nimport { compareVersions, parseVersion } from './semver'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DEPRECATED: {\n /** `options.<path>` for an option, else the method, e.g. `table.setGlobalFilter`. */\n api: string\n since: string\n removeIn: string\n replacement: string\n codemod?: string\n }\n DK_DUPLICATE_CORE: {\n /** Versions of every registered core copy, in registration order (this copy last). */\n versions: string[]\n }\n DK_DUPLICATE_CORE_SAME_VERSION: {\n version: string\n /** Copies of core registered so far (this copy included), all of `version`. */\n copies: number\n }\n DK_VERSION_MISMATCH: {\n /** The fixed-group package whose version differs, e.g. `@datakable/react`. */\n package: string\n version: string\n /** This core's version. */\n core: string\n }\n DK_DEP_INCOMPATIBLE: {\n /** The feature that uses the capability. */\n feature: string\n dependency: string\n capability: string\n /** The dependency range DataKable supports. */\n supported: string\n /** What is used instead. */\n fallback: string\n }\n DK_FEATURE_INCOMPATIBLE: {\n feature: string\n /** `requires.core` of the feature. */\n required: string\n core: string\n /** `true` when `required` is not a semver range. */\n invalid: boolean\n }\n }\n}\n\n/** The fixed release group (§12): always published with one version. */\nexport const FIXED_GROUP = ['@datakable/core', '@datakable/react', 'datakable'] as const\n\nconst OPTIONS = 'options.'\nconst CREATE: Location = { kind: 'operation', op: 'createTable' }\n\n/** The newer of two versions (unparseable ones lose). */\nconst newest = (a: string, b: string): string => {\n const pa = parseVersion(a)\n const pb = parseVersion(b)\n if (!pa) return b\n if (!pb) return a\n return compareVersions(pa, pb) < 0 ? b : a\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const versioningMessages: DiagnosticMessages<\n | 'DK_DEPRECATED'\n | 'DK_DUPLICATE_CORE'\n | 'DK_DUPLICATE_CORE_SAME_VERSION'\n | 'DK_VERSION_MISMATCH'\n | 'DK_DEP_INCOMPATIBLE'\n | 'DK_FEATURE_INCOMPATIBLE'\n> = {\n DK_DEPRECATED: {\n problem: (d) => `${d.api} is deprecated since ${d.since}`,\n impact: (d) =>\n `it still works in this version and is removed in ${d.removeIn}; this warning is shown ` +\n 'once per session',\n fix: (d) => [\n `use ${d.replacement} instead`,\n ...(d.codemod ? [`run the codemod: ${d.codemod}`] : []),\n ],\n },\n DK_DUPLICATE_CORE: {\n problem: (d) =>\n `${d.versions.length} copies of @datakable/core are loaded (${d.versions.join(', ')}); ` +\n 'features, interceptors and types from one copy do not match the other',\n impact: () =>\n 'each table works with its own copy, but plugins built against the other copy may be ' +\n 'ignored or fail instanceof / registry checks, and the bundle ships core twice',\n fix: () => [\n 'find who pulls the second copy: pnpm why @datakable/core (npm ls @datakable/core / yarn why)',\n 'dedupe it: pnpm dedupe (npm dedupe), and align @datakable/react and datakable to one version',\n 'or force one version: \"pnpm\": { \"overrides\": { \"@datakable/core\": \"<version>\" } } in ' +\n 'package.json (npm \"overrides\", yarn \"resolutions\")',\n ],\n },\n DK_DUPLICATE_CORE_SAME_VERSION: {\n problem: (d) =>\n `@datakable/core ${d.version} is loaded ${d.copies} times, all this version: its ESM and ` +\n \"CJS builds, a bundler's layers (Next.js dev: RSC, SSR, route handlers), a hot reload, or \" +\n 'two installs of it',\n impact: () =>\n 'tables work, each with its own copy; a DatakableError thrown by one copy is not an ' +\n 'instanceof the classes of the other, and a browser bundle with both ships core twice',\n fix: () => [\n 'compare errors by their .code (e.g. err.code === \"DK_INVALID_OPTION\"), not instanceof',\n 'mixed import and require of @datakable/core in one Node process: use one of them',\n 'in a browser bundle, find the second install: pnpm why @datakable/core, then pnpm dedupe',\n ],\n },\n DK_VERSION_MISMATCH: {\n problem: (d) =>\n `${d.package}@${d.version} is used with @datakable/core@${d.core}; ` +\n 'these packages are released together and must have the same version',\n impact: () => 'it may work, but APIs and types between the two can disagree',\n fix: (d) => {\n const v = newest(d.version, d.core)\n return [\n `pnpm add ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')} (install only the ones you use)`,\n `or with npm: npm install ${FIXED_GROUP.map((p) => `${p}@${v}`).join(' ')}`,\n ]\n },\n },\n DK_DEP_INCOMPATIBLE: {\n problem: (d) =>\n `${d.dependency} has no usable ${d.capability}; it was probably overridden to a version ` +\n `outside ${d.supported}`,\n impact: (d) => `feature \"${d.feature}\" uses ${d.fallback} instead; results stay correct`,\n fix: (d) => [\n `install a supported version: pnpm add ${d.dependency}@\"${d.supported}\" (check pnpm why ${d.dependency})`,\n `remove the override / resolution of ${d.dependency} from package.json`,\n ],\n },\n DK_FEATURE_INCOMPATIBLE: {\n problem: (d) =>\n d.invalid\n ? `feature \"${d.feature}\" declares requires.core \"${d.required}\", which is not a semver range`\n : `feature \"${d.feature}\" requires @datakable/core ${d.required}, but this is ${d.core}`,\n impact: () => 'the table was not created',\n fix: (d) =>\n d.invalid\n ? [\n `the feature author must set requires: { core: '^1.0' } (a semver range such as ` +\n `'^1.0', '>=1.2 <2' or '1.x')`,\n `meanwhile, remove feature \"${d.feature}\" from options.features`,\n ]\n : [\n `upgrade core: pnpm add @datakable/core@\"${d.required}\" (and @datakable/react / ` +\n 'datakable to the same version)',\n `or use a version of feature \"${d.feature}\" that supports @datakable/core ${d.core}`,\n ],\n },\n}\n\n/** Codes owned by versioning (§8.5). */\nexport const versioningDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_DEPRECATED',\n level: 'warn',\n where: (d) =>\n d.api.startsWith(OPTIONS)\n ? { kind: 'option', path: d.api.slice(OPTIONS.length) }\n : { kind: 'operation', op: d.api },\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEPRECATED,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE',\n level: 'error',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE,\n ),\n defineDiagnostic(\n {\n code: 'DK_DUPLICATE_CORE_SAME_VERSION',\n level: 'info',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DUPLICATE_CORE_SAME_VERSION,\n ),\n defineDiagnostic(\n {\n code: 'DK_VERSION_MISMATCH',\n level: 'warn',\n where: () => CREATE,\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_VERSION_MISMATCH,\n ),\n defineDiagnostic(\n {\n code: 'DK_DEP_INCOMPATIBLE',\n level: 'warn',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_DEP_INCOMPATIBLE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_INCOMPATIBLE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && versioningMessages.DK_FEATURE_INCOMPATIBLE,\n ),\n]\n"],"mappings":"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","names":[]}
@@ -81,8 +81,10 @@ function checkVersions(features, emit, table) {
81
81
  const reg = getVersionRegistry();
82
82
  if (!reg.cores.includes(self)) {
83
83
  reg.cores.push(self);
84
- if (reg.cores.length > 1) {
85
- emit("DK_DUPLICATE_CORE", { versions: reg.cores.map((c) => String(c?.version)) });
84
+ const versions = reg.cores.map((c) => String(c?.version));
85
+ if (versions.some((v) => v !== import_version.VERSION)) emit("DK_DUPLICATE_CORE", { versions });
86
+ else if (versions.length > 1 && firstInSession("DK_DUPLICATE_CORE_SAME_VERSION")) {
87
+ emit("DK_DUPLICATE_CORE_SAME_VERSION", { version: import_version.VERSION, copies: versions.length });
86
88
  }
87
89
  }
88
90
  for (const name of import_codes.FIXED_GROUP) {