@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/create-table.ts"],"sourcesContent":["import { optionDiagnostics } from '../catalog/codes'\nimport { validateOptionChange, validateOptions } from '../catalog/validator'\nimport { DatakableError, defaultPhase, throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport { type ExplainHost, explainRow, explainTable } from '../diagnostics/explain'\nimport { createDiagnostics } from '../diagnostics/hub'\nimport type { RedactionOptions } from '../diagnostics/redact'\nimport { tracer } from '../diagnostics/tracer'\nimport type { Trigger } from '../diagnostics/types'\nimport { runtimeFor } from '../feature/runtime'\nimport type { AnyFeature, ApiOf, SliceUpdater, TableStateFor } from '../feature/types'\nimport { createDataChecks } from '../ingest/data-checks'\nimport { createDerivedColumns } from '../ingest/derive-columns'\nimport type { SchemaOutcome } from '../ingest/schema'\nimport { SCHEMA_INGEST, type TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { StandardSchemaV1 } from '../ingest/standard-schema'\nimport { errorBoundary } from '../intercept/boundary'\nimport { callUser, errorName } from '../intercept/classify'\nimport { type ContextBase, createContext } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport { FAILED, FALLBACK } from '../intercept/fallbacks'\nimport type { OpInput, OpName, OpResult } from '../intercept/operations'\nimport { type Core, createInterceptorRuntime } from '../intercept/runtime'\nimport { createPipeline } from '../pipeline/runner'\nimport type { RowModel, RowModelStatus } from '../pipeline/types'\nimport { checkModeOrder, type FetchOutcome } from '../source/fetcher'\nimport type { ServerSource } from '../source/server'\nimport { SERVER_SOURCE, type ServerFetcher } from '../source/server-brand'\nimport { checkVersions } from '../versioning/registry'\nimport { tableDiagnostics } from './codes'\nimport { createDataMutationCheck, dataMutationDiagnostics } from './data-mutation'\nimport {\n deferral,\n type ExtensionFactory,\n setTableInternals,\n type TableExtension,\n type TableHost,\n} from './internals'\nimport { keepReadOnce } from './read-once'\nimport { createStateFeedbackCheck, stateFeedbackDiagnostics } from './state-feedback'\nimport {\n type ControlledState,\n type Listener,\n type Table,\n type TableOptions,\n type TableState,\n UPDATE_DEPTH_LIMIT,\n type Updater,\n} from './types'\n\nkeepDiagnostics(\n tableDiagnostics,\n dataMutationDiagnostics,\n stateFeedbackDiagnostics,\n optionDiagnostics,\n)\n\nconst EMPTY: readonly never[] = []\n\n/** §6.6 status precedence, lowest first: the table shows the highest of its sources. */\nconst RANK: readonly RowModelStatus[] = [\n 'idle',\n 'pending',\n 'validating',\n 'searching',\n 'fetching',\n 'error',\n]\n\nconst isThenable = (v: unknown): v is PromiseLike<unknown> =>\n v != null && typeof (v as { then?: unknown }).then === 'function'\n\nconst freeze = <S extends object>(s: S): S => ((process.env.NODE_ENV !== \"production\") ? Object.freeze(s) : s)\n\n/** The row type: the schema output with a `schema` (§5.1), else `T`. */\nexport type SchemaRow<T, Sch> = [Sch] extends [TableSchema] ? StandardSchemaV1.InferOutput<Sch> : T\n\n/**\n * `createTable` options: with a `schema`, the row type is its output and `data` is the raw input\n * (`unknown[]`, e.g. parsed JSON), validated per row.\n */\nexport type CreateTableOptions<T, S extends TableState, Sch, M extends InvalidRowsMode> = Omit<\n TableOptions<SchemaRow<T, Sch>, S>,\n 'data' | 'schema' | 'invalidRows' | 'fetcher' | 'initialData'\n> & {\n data?: readonly ([Sch] extends [TableSchema] ? unknown : T)[]\n /** With a schema, the raw rows (validated like `data`). */\n initialData?: readonly ([Sch] extends [TableSchema] ? unknown : T)[]\n /** With a schema, the fetcher returns raw rows (validated like `data`). */\n fetcher?: ServerFetcher<[Sch] extends [TableSchema] ? unknown : T>\n // Without an inferred schema (explicit type arguments, a `TableOptions<T>` object) these keep\n // their `TableOptions` types.\n schema?: [Sch] extends [TableSchema] ? Sch : TableSchema<unknown, T>\n invalidRows?: [Sch] extends [TableSchema] ? M : InvalidRowsMode\n}\n\n/** Column id: `key` for key columns, `id` for accessor columns. */\nconst columnId = (c: { key?: unknown; id?: unknown }): unknown => c.key ?? c.id\n\n/**\n * Creates the table store (§3.2): immutable state snapshots, controlled slices, isolated listeners,\n * a re-entrancy guard and `destroy()`. Every unit of work runs as an operation (§3.4) through the\n * table's interceptor runtime, with the errorBoundary outermost and diagnostics on a per-table hub.\n *\n * `ctx.where` is always a constant option path or method name (`options`, `options.data`,\n * `setState`, `destroy`): it reaches fingerprints, docs URLs and issue links unredacted, so it must\n * never carry row keys or cell values.\n */\nexport function createTable<\n T,\n S extends TableState = never,\n const Fs extends readonly AnyFeature[] = [],\n const Sch extends TableSchema | undefined = undefined,\n const M extends InvalidRowsMode = 'drop',\n>(\n options: CreateTableOptions<T, TableStateFor<S, Fs, SchemaRow<T, Sch>>, Sch, M> & {\n features?: Fs\n },\n): Table<\n SchemaRow<T, Sch>,\n TableStateFor<S, Fs, SchemaRow<T, Sch>>,\n M,\n [Sch] extends [TableSchema] ? unknown : T\n> &\n ApiOf<Fs, SchemaRow<T, Sch>>\nexport function createTable<T, S extends TableState>(\n initialOptions: TableOptions<T, S>,\n): Table<T, S> {\n let options: TableOptions<T, S> = { ...initialOptions }\n let destroyed = false\n let trigger: Trigger = 'init'\n /** Pipeline run id; each `pipeline:run` increments it. */\n let run = 0\n let data: readonly T[] = EMPTY\n let ingestSeq = 0\n /** Data given by the user has been applied once (`RowModel.isInitialLoad`, §6.6). */\n let loaded = false\n /** The last `data:ingest` failed (op fallback): `status: 'error'` until one succeeds (§6.6). */\n let ingestFailed = false\n /** The ingest whose async schema validation is in flight (`status: 'validating'`); 0: none. */\n let validatingSeq = 0\n /** Rows of the current data that failed the schema (`getInvalidRows`, §5.1). */\n let schemaOutcome: SchemaOutcome | undefined\n /**\n * SSR (§7.2): `options.initialData` until the creation ingest read it (`undefined` with `data`),\n * then its ingested rows while they are the table's data (`initialRows`).\n */\n let initialInput = options.data === undefined ? options.initialData : undefined\n let initialRows: readonly unknown[] | undefined\n /** Created inside `createDeferred` (§7.2): async work waits for `startTable`. */\n let deferring = deferral.depth > 0\n /**\n * Opt-in parts of the store (`TableInternals.use`): the async request machinery and the server\n * source. A table without an async source creates none (§9.3).\n */\n const extensions = new Map<ExtensionFactory<TableExtension>, TableExtension>()\n /** The server source (`withFetcher`, §7.1), once a fetcher was given. */\n let server: ServerSource | undefined\n\n let controlled = new Set<string>()\n let snapshot: S\n /** The snapshot listeners last saw (`prev` of the next notification). */\n let notified: S\n /** A committed change (state or data) not yet notified. */\n let pending = false\n let notifying = false\n let rounds = 0\n let loopReported = false\n let current: number | undefined\n let listenerSeq = 0\n const listeners = new Map<number, Listener<S>>()\n /** Depth of feature API calls in progress: listeners are notified when the outermost returns. */\n let apiDepth = 0\n /** Data, state or columns changed since the row model was computed. */\n let rowModelDirty = true\n /** Diagnostic context (§8.1): the data mode of each pipeline feature (set once features load). */\n const modes: Record<string, 'client' | 'server'> = {}\n\n const table: Table<T, S> = {\n getState: () => snapshot,\n setState,\n subscribe,\n setOptions,\n getOptions: () => options,\n getData: () => data,\n // Core method: on the literal before installApis, so no feature can take the name.\n getRowModel,\n getInvalidRows: () => schemaOutcome?.invalid ?? EMPTY,\n getDiagnostics: () => hub.getDiagnostics(),\n // Core methods (§8.4), read-only: also on the literal before installApis.\n explain: () => explainTable(explainHost),\n explainRow: (key) => explainRow(explainHost, key),\n refetch,\n destroy,\n }\n\n // Redaction (§8.2): `diagnostics.redact` gives the level or a custom rule; column defs give the\n // sensitive columns and whether the rowKey column is sensitive. Both are read per emit, so\n // `setOptions({ columns })` is followed.\n const redactOption = options.diagnostics?.redact\n const redaction: RedactionOptions = {\n ...(typeof redactOption === 'string' ? { level: redactOption } : {}),\n ...(typeof redactOption === 'function' ? { redact: redactOption } : {}),\n sensitiveColumns,\n rowKeySensitive: () =>\n typeof options.rowKey === 'string' && sensitiveColumns().includes(options.rowKey),\n }\n const hub = createDiagnostics({\n ...(options.name === undefined ? {} : { name: options.name }),\n table,\n onDiagnostic: (d) => options.onDiagnostic?.(d),\n ...(options.strict === undefined ? {} : { strict: options.strict }),\n getContext: () => ({ trigger, run, mode: modes }),\n getDataRef: () => data,\n redaction,\n })\n // Option validator (§3.4, `table:init`), development only: before anything reads the options.\n // Invalid values throw DK_INVALID_OPTION; unknown / no-effect options are reported on the hub.\n if ((process.env.NODE_ENV !== \"production\")) validateOptions(options, { table: hub.table, emit: hub.emit })\n // Features (§3.3) are read once, at creation. `features` is the listed order (API install, conflict\n // reports); `pipeline` is stage order for the runner. Duplicates throw here (config error).\n const features: readonly AnyFeature[] = options.features ?? EMPTY\n // The feature runtime comes with the features (`defineFeature`): none for a table without.\n const rt = runtimeFor(features)\n const pipeline = rt ? rt.orderFeatures(features, hub.table) : EMPTY\n // Data modes (§7.1): DK_MODE_ORDER (a config error, development). A fetcher without a server-mode\n // feature (DK_FETCHER_WITHOUT_SERVER_MODE) is checked by the server source.\n if ((process.env.NODE_ENV !== \"production\")) checkModeOrder(pipeline, hub.table)\n for (const f of pipeline) if (f.stage !== 'none') modes[f.name] = f.mode ?? 'client'\n // Extension interceptors come first (asyncControl, one per table, installed with the first async\n // source: user / feature interceptors and the core see its signal); the boundary stays\n // outermost. Feature interceptors (pipeline order) come next, then the user's.\n const front: Interceptor<OpName>[] = []\n const featureInterceptors = pipeline.flatMap((f) => f.interceptors ?? EMPTY)\n // The tracer (§3.4, explain timings) is development-only: `(process.env.NODE_ENV !== \"production\")` folds to `false` in production\n // builds, which drops it from the bundle; there it is opt-in with `interceptors: [tracer()]`.\n const traced = (process.env.NODE_ENV !== \"production\") ? [tracer() as Interceptor<OpName>] : EMPTY\n const withFront = (list: readonly Interceptor<OpName>[] | undefined) => [\n ...front,\n ...traced,\n ...featureInterceptors,\n ...(list ?? EMPTY),\n ]\n const runtime = createInterceptorRuntime({\n interceptors: withFront(options.interceptors),\n emit: hub.emit,\n })\n runtime.installBoundary(errorBoundary)\n hub.attach(runtime)\n // Dev check (§5.3): the same `data` array passed again with changed contents (DK_DATA_MUTATED).\n const dataCheck = (process.env.NODE_ENV !== \"production\") ? createDataMutationCheck(hub.emit) : undefined\n // Feature `requires.core` (throws DK_FEATURE_INCOMPATIBLE), duplicate cores, version mismatch\n // (§8.5): development checks (changed 2026-10-13 for §9.3).\n if ((process.env.NODE_ENV !== \"production\")) checkVersions(features, hub.emit, hub.table)\n // Ingest data checks (§5.3) and zero-config columns (§4.1): both read only the first rows.\n // The data checks are developer hints: development only (changed 2026-10-13 for §9.3).\n const dataChecks = (process.env.NODE_ENV !== \"production\") ? createDataChecks(hub.emit) : undefined\n /** Controlled changes not fed back (DK_CONTROLLED_NOT_FED_BACK, development). */\n const feedback = (process.env.NODE_ENV !== \"production\") ? createStateFeedbackCheck(hub.emit) : undefined\n const derivedColumns = createDerivedColumns()\n // Schema ingest (§5.1): `schema` and `invalidRows` are read once (validated above). The step\n // comes with the schema (`withSchema`, `@datakable/core/schema`): a table without one never\n // bundles it.\n const schemaIngest = (options.schema as Partial<TableSchema> | undefined)?.[SCHEMA_INGEST]?.(\n options.invalidRows ?? 'drop',\n () => options.columns,\n )\n // Never silent (§8.6): in production (no option validator) an unwrapped schema would be ignored.\n if (options.schema && !schemaIngest) unwrapped('schema', 'withSchema')\n\n function sensitiveColumns(): string[] {\n const out: string[] = []\n for (const c of options.columns ?? EMPTY) {\n const id = columnId(c as { key?: unknown; id?: unknown })\n if (c.sensitive && typeof id === 'string') out.push(id)\n }\n return out\n }\n\n function base(): ContextBase {\n return { table, run, trigger, phase: defaultPhase(), emit: hub.emit }\n }\n\n function op<O extends OpName>(\n name: O,\n input: OpInput<O>,\n where: string,\n core: Core<O>,\n fallback?: unknown,\n ): OpResult<O> {\n const ctx = createContext(name, input, where, base())\n if (fallback !== undefined) ctx.meta.set(FALLBACK, fallback)\n return runtime.run(name, ctx, core)\n }\n\n function withTrigger<R>(t: Trigger, fn: () => R): R {\n const prev = trigger\n trigger = t\n try {\n return fn()\n } finally {\n trigger = prev\n }\n }\n\n // --- state -------------------------------------------------------------------------------------\n\n function controlledKeys(state: ControlledState<S> | undefined): Set<string> {\n const keys = new Set<string>()\n if (state) for (const k of Object.keys(state)) if (state[k] !== undefined) keys.add(k)\n return keys\n }\n\n /** `s` with the controlled slices from `options.state`; `s` itself when none differ. */\n function overlay(s: S): S {\n let next: TableState | undefined\n const state = options.state as TableState | undefined\n if (state) {\n for (const k of controlled) {\n if (!Object.is(s[k], state[k])) {\n next ??= { ...s }\n next[k] = state[k]\n }\n }\n }\n return next ? freeze(next as S) : s\n }\n\n function commit(next: S) {\n snapshot = next\n pending = true\n rowModelDirty = true\n }\n\n function warnControlled(dropped: string[] | undefined) {\n if (options.onStateChange || controlled.size === 0) return\n const slices = dropped ?? [...controlled]\n hub.emit('DK_CONTROLLED_WITHOUT_HANDLER', { slices, dropped: dropped !== undefined })\n }\n\n function syncControlled() {\n controlled = controlledKeys(options.state)\n const next = overlay(snapshot)\n if (next !== snapshot) commit(next)\n if ((process.env.NODE_ENV !== \"production\")) feedback?.settle(options.state as TableState | undefined, controlled)\n warnControlled(undefined)\n }\n\n function dispatch(updater: Updater<S>, slot: string) {\n // A method argument, not an option: `where` = `operation table.setState at updater`.\n const partial: Partial<S> =\n typeof updater === 'function'\n ? callUser(\n 'updater',\n updater,\n snapshot,\n undefined,\n slot === 'setState' ? 'table.setState' : `${slot}.setState`,\n )\n : updater\n if (partial === null || typeof partial !== 'object') return\n let next: TableState | undefined\n let changes: TableState | undefined\n let dropped: string[] | undefined\n for (const k of Object.keys(partial)) {\n const v = (partial as TableState)[k]\n if (Object.is(snapshot[k], v)) continue\n if (controlled.has(k)) {\n if (options.onStateChange) {\n changes ??= {}\n changes[k] = v\n } else {\n dropped ??= []\n dropped.push(k)\n }\n continue\n }\n next ??= { ...snapshot }\n next[k] = v\n }\n // The owner is told first: if onStateChange throws, nothing is committed (state unchanged).\n if (changes && options.onStateChange) {\n const c = changes as Partial<S>\n if ((process.env.NODE_ENV !== \"production\")) feedback?.ask(Object.keys(changes), trigger)\n callUser('onStateChange', options.onStateChange, (prev: Partial<S>) => ({ ...prev, ...c }))\n if ((process.env.NODE_ENV !== \"production\")) feedback?.asked()\n }\n if (next) commit(freeze(next as S))\n if (dropped) warnControlled(dropped)\n }\n\n /**\n * Notifies listeners until no change is pending. Updates made by listeners are committed at once\n * and notified in the next round (not nested), so every listener sees rounds in order.\n */\n function flush() {\n if (notifying) return\n sync()\n if (!pending) return\n notifying = true\n let rethrow: unknown\n try {\n while (pending && !destroyed) {\n // Server state changed (this round's state): the fetch starts in the same notification.\n sync()\n pending = false\n rounds++\n const prev = notified\n const state = snapshot\n notified = state\n // Listeners read the row model of the state they are notified with.\n ensureRowModel()\n for (const [id, listener] of [...listeners]) {\n // A listener may destroy the table: the rest of the round is not notified.\n if (destroyed) break\n current = id\n try {\n listener(state, prev)\n } catch (e) {\n // strict: a DatakableError (e.g. DK_UPDATE_LOOP) thrown inside the listener.\n if (e instanceof DatakableError) {\n rethrow ??= e\n continue\n }\n try {\n const name = errorName(e)\n hub.emit(\n 'DK_LISTENER_FAILED',\n { listener: id, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n } catch (strictError) {\n rethrow ??= strictError\n }\n }\n }\n }\n } finally {\n notifying = false\n rounds = 0\n loopReported = false\n current = undefined\n }\n if (rethrow !== undefined) throw rethrow\n }\n\n /** `false` (and one `DK_UPDATE_LOOP`) when a re-entrant update would exceed the depth limit. */\n function admit(slices: string[]): boolean {\n if (!notifying || rounds < UPDATE_DEPTH_LIMIT) return true\n if (!loopReported) {\n loopReported = true\n hub.emit('DK_UPDATE_LOOP', {\n limit: UPDATE_DEPTH_LIMIT,\n slices,\n ...(current === undefined ? {} : { listener: current }),\n })\n }\n return false\n }\n\n function destroyedCall(method: string) {\n withTrigger(`api:${method}`, () => hub.emit('DK_TABLE_DESTROYED', { method }))\n }\n\n // --- data --------------------------------------------------------------------------------------\n\n /** Applies ingested data; `given`: it came from a user `options.data` (marks the first load). */\n function setData(next: readonly T[], given: boolean) {\n if (given && !loaded) {\n // isInitialLoad flips: a new row model and a notification even for the same reference.\n loaded = true\n pending = true\n rowModelDirty = true\n }\n if (next === data) return\n data = next\n pending = true\n // After `data` is set: diagnostics dedupe per data reference.\n dataChecks?.check(data, options.columns)\n // The pipeline runs before the next notification (or on getRowModel), as `pipeline:run` with\n // ctx.meta[FALLBACK] = the last good row model (pipeline/runner.ts).\n rowModelDirty = true\n }\n\n // --- row model ---------------------------------------------------------------------------------\n\n const runner = createPipeline<T>({\n runtime,\n featureRuntime: rt,\n emit: hub.emit,\n features: pipeline,\n listed: features,\n base,\n nextRun: () => ++run,\n getData: () => data,\n getState: () => snapshot,\n getColumns: () => options.columns ?? derivedColumns(data),\n getRowKey: () => options.rowKey,\n isInitialLoad: () => !loaded,\n status,\n issues: (index) => schemaOutcome?.flagged?.get(index),\n flagMode: schemaIngest !== undefined && options.invalidRows === 'flag',\n // Time-sliced work ended (§9.1): 'pending' → the finished row model, never silent (§6.6).\n refresh: (t) => {\n if (destroyed) return\n withTrigger(t, () => {\n touch()\n if (apiDepth === 0) flush()\n })\n },\n })\n\n const explainHost: ExplainHost = {\n table,\n features: pipeline,\n rowModel: getRowModel as () => RowModel<unknown>,\n lastRun: runner.lastRun,\n replay: runner.replayContext,\n data: () => data,\n state: () => snapshot,\n rowKey: () => options.rowKey,\n schemaMode: () => (schemaIngest ? (options.invalidRows ?? 'drop') : undefined),\n schema: () => schemaOutcome,\n diagnostics: () => hub.getDiagnostics(),\n redaction,\n ...(rt ? { detail: rt.explainDetail } : {}),\n }\n\n /**\n * Status from outside the pipeline (§6.6): a failed ingest, a running async schema, the search\n * slice (`pending` / `searching` / `error`), and the extensions' (a failed fetch, an in-flight\n * fetcher / `searchFn`, deferred async work). Precedence: error > fetching > searching >\n * validating > pending > idle.\n */\n function status(): RowModelStatus {\n const search = searchStatus()\n let s: RowModelStatus =\n ingestFailed || search === 'error'\n ? 'error'\n : search === 'searching'\n ? 'searching'\n : validatingSeq !== 0\n ? 'validating'\n : search === 'pending'\n ? 'pending'\n : 'idle'\n for (const e of extensions.values()) {\n const x = e.status?.()\n if (x !== undefined && RANK.indexOf(x) > RANK.indexOf(s)) s = x\n }\n return s\n }\n\n /** Extensions start work that follows state (the fetcher on server state, §7.1). */\n function sync() {\n for (const e of extensions.values()) e.sync?.()\n }\n\n /** The `status` of the first search-stage slice that reports one (§6.3). */\n function searchStatus(): unknown {\n for (const f of pipeline) {\n if (f.stage !== 'search') continue\n const s = (snapshot as Record<string, unknown> | undefined)?.[f.name]\n if (s !== null && typeof s === 'object' && 'status' in s) {\n const v = (s as { status: unknown }).status\n if (v !== 'idle') return v\n }\n }\n return 'idle'\n }\n\n /** A row model change not caused by state: recompute and notify on the next flush. */\n function touch() {\n pending = true\n rowModelDirty = true\n }\n\n /** Recomputes the row model if data, state or columns changed (never after destroy). */\n function ensureRowModel() {\n if (!rowModelDirty || destroyed) return\n rowModelDirty = false\n runner.run()\n }\n\n function getRowModel(): RowModel<T> {\n ensureRowModel()\n // Only empty when destroyed before the first run.\n return (\n runner.current() ?? {\n rows: EMPTY,\n filteredCount: 0,\n totalCount: 0,\n status: status(),\n isInitialLoad: !loaded,\n }\n )\n }\n\n /**\n * Applies the result of the latest `data:ingest`. A fallback (the boundary set `FAILED`) keeps\n * the previous data, sets `status: 'error'` and never marks the table loaded (§6.6, §8.6).\n */\n function applyIngest(out: readonly unknown[], failed: boolean, given: boolean, initial = false) {\n if (validatingSeq !== 0) {\n validatingSeq = 0\n touch()\n }\n if (failed) {\n ingestFailed = true\n touch()\n return\n }\n if (ingestFailed) {\n ingestFailed = false\n touch()\n }\n schemaOutcome = schemaIngest?.outcome(out)\n setData(out as readonly T[], given)\n // `initialData` is the initial async result only while it is the data (§7.2).\n initialRows = initial ? out : undefined\n }\n\n /**\n * `data:ingest` with the previous data as fallback: the schema step (§5.1) runs in its core. An\n * async result is applied if still latest. `fetched`: rows of an applied fetcher result (§7.1),\n * ingested like `options.data` (schema, data checks) and marking the table loaded.\n */\n function ingest(fetched?: readonly unknown[]) {\n const seq = ++ingestSeq\n // At creation, `initialData` stands in for a missing `data` (§7.2); read once.\n const initial = fetched === undefined && initialInput !== undefined\n const input = fetched ?? (initial ? initialInput : options.data)\n initialInput = undefined\n const given = input !== undefined\n const source: Trigger = fetched ? 'fetch' : 'data-change'\n // A validation still in flight is superseded (its result is discarded): not 'validating' now,\n // unless this ingest's own schema step goes async.\n if (validatingSeq !== 0) {\n validatingSeq = 0\n touch()\n }\n try {\n withTrigger(source, () => {\n const ctx = createContext(\n 'data:ingest',\n { data: input ?? EMPTY },\n fetched ? 'fetcher' : initial ? 'options.initialData' : 'options.data',\n base(),\n )\n ctx.meta.set(FALLBACK, data)\n let sync = true\n const out = runtime.run('data:ingest', ctx, (c) => {\n const input = c.input.data as readonly unknown[]\n if (!schemaIngest) return input\n const r = schemaIngest.validate(input, c.emit)\n if (isThenable(r) && seq === ingestSeq && !destroyed) {\n // 'validating' is never silent: a new row model and a notification now (§6.6).\n validatingSeq = seq\n touch()\n if (!sync) flush()\n }\n return r\n })\n sync = false\n if (!isThenable(out)) {\n applyIngest(out, ctx.meta.has(FAILED), given, initial)\n return\n }\n const settle = (d: readonly unknown[], failed: boolean) => {\n if (seq !== ingestSeq || destroyed) return\n withTrigger(source, () => {\n applyIngest(d, failed, given, initial)\n flush()\n })\n }\n Promise.resolve(out).then(\n (d) => settle(d, ctx.meta.has(FAILED)),\n // A DatakableError the boundary rethrew (strict, or `invalidRows: 'throw'` with an async\n // schema, already reported): the previous data is kept with 'error'. No unhandled\n // rejection either way (§8.6).\n () => settle(data, true),\n )\n })\n } finally {\n // Dev (§5.3): fingerprint `data` after the core read it, so the check never takes reads\n // first; also when the ingest threw, so a later same-reference check compares this data.\n if (!fetched) dataCheck?.remember(options.data)\n }\n }\n\n // --- API ---------------------------------------------------------------------------------------\n\n /** `state:dispatch` (where = `slot`, a constant method / feature name); notifies unless batched. */\n function update(updater: Updater<S>, slot: string) {\n void op('state:dispatch', { updater }, slot, (): undefined => {\n dispatch(updater, slot)\n })\n if (apiDepth === 0) flush()\n }\n\n function setState(updater: Updater<S>) {\n if (destroyed) return destroyedCall('setState')\n if (!admit(typeof updater === 'function' ? [] : Object.keys(updater ?? {}))) return\n withTrigger('api:setState', () => update(updater, 'setState'))\n }\n\n // --- features ----------------------------------------------------------------------------------\n\n /** A feature's `ctx.setState`: replaces its own slice (trigger: the API call's, if any). */\n function setSlice(name: string, updater: SliceUpdater<unknown>) {\n if (destroyed) return destroyedCall(`${name}.setState`)\n if (!admit([name])) return\n const slice: Updater<S> =\n typeof updater === 'function'\n ? (prev) => ({ [name]: (updater as (p: unknown) => unknown)(prev[name]) }) as Partial<S>\n : ({ [name]: updater } as Partial<S>)\n update(slice, name)\n }\n\n /** Runs a feature API method (wrapped once by `installApis`). */\n function callApi(method: string, fn: () => unknown, ifDestroyed?: () => unknown): unknown {\n if (destroyed) {\n destroyedCall(method)\n return ifDestroyed?.()\n }\n return withTrigger(`api:${method}`, () => {\n apiDepth++\n try {\n return fn()\n } finally {\n apiDepth--\n if (apiDepth === 0) flush()\n }\n })\n }\n\n function subscribe(listener: Listener<S>): () => void {\n if (destroyed) {\n destroyedCall('subscribe')\n return () => {}\n }\n const id = ++listenerSeq\n listeners.set(id, listener)\n return () => {\n listeners.delete(id)\n }\n }\n\n function setOptions(partial: Partial<TableOptions<T, S>>) {\n if (destroyed) return destroyedCall('setOptions')\n if (!admit(Object.keys(partial.state ?? {}))) return\n withTrigger('setOptions', () => {\n const previous = options\n const next = { ...options, ...partial }\n // Option validator (§3.4, `table:setOptions`, development): an invalid value throws before\n // anything changes. Read-once options (`features`, `timeout`, `diagnostics`, …) keep their\n // value in every build.\n if ((process.env.NODE_ENV !== \"production\")) validateOptionChange(partial, next, previous, { table: hub.table, emit: hub.emit })\n else keepReadOnce(partial, next, previous)\n if ('fetcher' in partial) useServer(next.fetcher, 'setOptions')?.check(next.fetcher)\n void op('table:setOptions', { options: next, previous }, 'options', (): undefined => {\n options = next\n if (next.interceptors !== previous.interceptors) {\n runtime.setInterceptors(withFront(next.interceptors))\n }\n if ('state' in partial || 'onStateChange' in partial) syncControlled()\n if (next.columns !== previous.columns || next.rowKey !== previous.rowKey) {\n rowModelDirty = true\n pending = true\n }\n // The page count changed (§6.5): listeners re-read it (and pagination clamps).\n if (next.rowCount !== previous.rowCount) pending = true\n // A new fetcher reference runs at once, even with the same params (flush → sync).\n if (next.fetcher !== previous.fetcher) server?.reset()\n // New data is checked when applied (setData); same data with new columns is checked here.\n if (next.data !== previous.data) ingest()\n else {\n if ('data' in partial) dataCheck?.check(next.data)\n if (next.columns !== previous.columns) dataChecks?.check(data, next.columns)\n }\n })\n flush()\n })\n }\n\n function destroy() {\n if (destroyed) return\n withTrigger('api:destroy', () => {\n try {\n void op('table:destroy', undefined, 'destroy', (): undefined => {\n destroyed = true\n })\n } finally {\n // Even if an interceptor skipped the core: the table is destroyed.\n destroyed = true\n ingestSeq++\n listeners.clear()\n runner.dispose()\n if ((process.env.NODE_ENV !== \"production\")) feedback?.dispose()\n }\n })\n }\n\n // --- extensions (§9.3) ---------------------------------------------------------------------\n\n function batch<R>(t: Trigger, fn: () => R): R {\n return withTrigger(t, () => {\n apiDepth++\n try {\n return fn()\n } finally {\n apiDepth--\n if (apiDepth === 0) flush()\n }\n })\n }\n\n const host: TableHost = {\n table: table as unknown as Table<unknown, TableState>,\n name: hub.table,\n emit: hub.emit,\n runtime,\n features: pipeline,\n options: () => options as TableOptions<unknown>,\n state: () => snapshot,\n base,\n destroyed: () => destroyed,\n loaded: () => loaded,\n status,\n touch,\n notify: () => {\n if (apiDepth === 0) flush()\n },\n batch,\n ingest: (rows) => ingest(rows),\n intercept(interceptor) {\n front.push(interceptor)\n runtime.setInterceptors(withFront(options.interceptors))\n },\n deferring: () => deferring,\n use,\n }\n\n function use<E extends TableExtension>(factory: ExtensionFactory<E>): E {\n let e = extensions.get(factory as ExtensionFactory<TableExtension>) as E | undefined\n if (!e) {\n e = factory(host)\n extensions.set(factory as ExtensionFactory<TableExtension>, e)\n }\n return e\n }\n\n /** The server source a `withFetcher` fetcher brings (created once); else the current one. */\n function useServer(fetcher: unknown, phase?: 'setOptions'): ServerSource | undefined {\n const factory = (fetcher as Partial<ServerFetcher<unknown>> | undefined)?.[SERVER_SOURCE]\n if (factory) server = use(factory)\n // Never silent (§8.6): in production an unwrapped fetcher would never run.\n else if (fetcher) unwrapped('fetcher', 'withFetcher', phase)\n return server\n }\n\n /**\n * `DK_INVALID_OPTION` for a schema / fetcher not wrapped with its opt-in import (§5.1, §7.1). A\n * production check: in development the option validator reports it first, with the full text.\n */\n function unwrapped(\n path: 'schema' | 'fetcher',\n wrap: string,\n phase: 'init' | 'setOptions' = 'init',\n ): never {\n return throwConfigError(\n 'DK_INVALID_OPTION',\n { path, expected: `${wrap}(${path})`, received: path, suggestions: [], phase },\n { table: hub.table, trigger: phase },\n )\n }\n\n /**\n * Runs the fetcher again with the current params (§7.1); never rejects. Without a fetcher:\n * `{ status: 'applied' }` at once.\n */\n function refetch(): Promise<FetchOutcome> {\n if (destroyed) {\n destroyedCall('refetch')\n return Promise.resolve({ status: 'superseded' })\n }\n return (\n withTrigger('api:refetch', () => server?.sync(true)) ?? Promise.resolve({ status: 'applied' })\n )\n }\n\n /** `startTable` (§7.2): the deferred work starts; listeners see the new status at once. */\n function start() {\n if (!deferring || destroyed) return\n deferring = false\n let had = false\n for (const e of extensions.values()) if (e.start?.()) had = true\n if (!had) return\n // The pipeline re-runs: 'pending' ends and the search feature's check starts its searchFn.\n touch()\n withTrigger('hydrate', () => flush())\n }\n\n setTableInternals(table, {\n features: pipeline,\n rowCount: () => (typeof options.fetcher === 'function' ? server?.rowCount() : options.rowCount),\n hasFetcher: () => typeof options.fetcher === 'function',\n emit: hub.emit,\n use,\n peek: (factory) => extensions.get(factory as ExtensionFactory<TableExtension>) as never,\n extensions: () => extensions.values(),\n start,\n initialRows: () =>\n initialRows !== undefined && initialRows === data ? initialRows : undefined,\n })\n\n // --- init --------------------------------------------------------------------------------------\n\n controlled = controlledKeys(options.state)\n if ((process.env.NODE_ENV !== \"production\")) feedback?.settle(options.state as TableState | undefined, controlled)\n // Feature `initialState()` slices, with `options.initialState` on top per slice.\n snapshot = overlay(\n freeze(\n (rt\n ? rt.initialSlices(features, options.initialState, hub.emit)\n : { ...options.initialState }) as S,\n ),\n )\n notified = snapshot\n // Merges the feature APIs onto the table; a name clash throws DK_FEATURE_API_CONFLICT.\n rt?.installApis(features, {\n table: table as unknown as Table<unknown, TableState>,\n tableName: hub.table,\n emit: hub.emit,\n getSlice: (name) => snapshot[name],\n setSlice,\n call: callApi,\n })\n // Server source (§7.1): with a `withFetcher` fetcher (throws DK_FETCHER_WITHOUT_SERVER_MODE).\n useServer(options.fetcher)\n withTrigger('init', () => {\n // `void`: sync ops never return a promise (op kinds, §3.4).\n void op('table:init', { options }, 'options', (): undefined => {\n // Ingest first: dedupe is per data reference, so warnings follow the initial data.\n ingest()\n warnControlled(undefined)\n })\n // Server source (§7.1): the first request starts now ('fetching', isInitialLoad: true), unless\n // initialData answered the initial server state (§7.2).\n sync()\n })\n pending = false\n return table\n}\n"],"mappings":"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","names":["sync","input"]}
1
+ {"version":3,"sources":["../../src/table/create-table.ts"],"sourcesContent":["import { optionDiagnostics } from '../catalog/codes'\nimport { validateOptionChange, validateOptions } from '../catalog/validator'\nimport { DatakableError, defaultPhase, throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport { type ExplainHost, explainRow, explainTable } from '../diagnostics/explain'\nimport { createDiagnostics } from '../diagnostics/hub'\nimport { pathsOverlap, type RedactionOptions } from '../diagnostics/redact'\nimport { tracer } from '../diagnostics/tracer'\nimport type { Trigger } from '../diagnostics/types'\nimport { runtimeFor } from '../feature/runtime'\nimport type { AnyFeature, ApiOf, SliceUpdater, TableStateFor } from '../feature/types'\nimport { createDataChecks } from '../ingest/data-checks'\nimport { createDerivedColumns } from '../ingest/derive-columns'\nimport type { SchemaOutcome } from '../ingest/schema'\nimport { SCHEMA_INGEST, type TableSchema } from '../ingest/schema-brand'\nimport type { InvalidRowsMode } from '../ingest/schema-codes'\nimport type { StandardSchemaV1 } from '../ingest/standard-schema'\nimport { errorBoundary } from '../intercept/boundary'\nimport { callUser, errorName } from '../intercept/classify'\nimport { type ContextBase, createContext } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport { FAILED, FALLBACK } from '../intercept/fallbacks'\nimport type { OpInput, OpName, OpResult } from '../intercept/operations'\nimport { type Core, createInterceptorRuntime } from '../intercept/runtime'\nimport { createPipeline } from '../pipeline/runner'\nimport type { RowModel, RowModelStatus } from '../pipeline/types'\nimport { checkModeOrder, type FetchOutcome } from '../source/fetcher'\nimport type { ServerSource } from '../source/server'\nimport { SERVER_SOURCE, type ServerFetcher } from '../source/server-brand'\nimport { checkVersions } from '../versioning/registry'\nimport { tableDiagnostics } from './codes'\nimport { createDataMutationCheck, dataMutationDiagnostics } from './data-mutation'\nimport {\n deferral,\n type ExtensionFactory,\n setTableInternals,\n type TableExtension,\n type TableHost,\n} from './internals'\nimport { keepReadOnce } from './read-once'\nimport { createStateFeedbackCheck, stateFeedbackDiagnostics } from './state-feedback'\nimport {\n type ControlledState,\n type Listener,\n type Table,\n type TableOptions,\n type TableState,\n UPDATE_DEPTH_LIMIT,\n type Updater,\n} from './types'\n\nkeepDiagnostics(\n tableDiagnostics,\n dataMutationDiagnostics,\n stateFeedbackDiagnostics,\n optionDiagnostics,\n)\n\nconst EMPTY: readonly never[] = []\n\n/** §6.6 status precedence, lowest first: the table shows the highest of its sources. */\nconst RANK: readonly RowModelStatus[] = [\n 'idle',\n 'pending',\n 'validating',\n 'searching',\n 'fetching',\n 'error',\n]\n\nconst isThenable = (v: unknown): v is PromiseLike<unknown> =>\n v != null && typeof (v as { then?: unknown }).then === 'function'\n\n/** Objects already deep-frozen (dev): an unchanged slice is not walked again on the next commit. */\nconst deepFrozen = (process.env.NODE_ENV !== \"production\") ? new WeakSet<object>() : undefined\n\n/**\n * Freezes plain objects and arrays reachable from `v` (dev). Class instances (Date, Map, typed\n * arrays, user classes) are left alone: freezing them would break their own methods.\n */\nfunction deepFreeze(v: unknown): void {\n if (v === null || typeof v !== 'object' || deepFrozen?.has(v)) return\n const isArray = Array.isArray(v)\n if (!isArray) {\n const proto = Object.getPrototypeOf(v)\n if (proto !== Object.prototype && proto !== null) return\n }\n deepFrozen?.add(v)\n Object.freeze(v)\n if (isArray) for (let i = 0; i < v.length; i++) deepFreeze(v[i])\n else for (const k in v) deepFreeze((v as Record<string, unknown>)[k])\n}\n\n/** Snapshots and their slices are immutable in development (#20); production skips the walk. */\nconst freeze = <S extends object>(s: S): S => {\n if ((process.env.NODE_ENV !== \"production\")) deepFreeze(s)\n return s\n}\n\n/** The row type: the schema output with a `schema` (§5.1), else `T`. */\nexport type SchemaRow<T, Sch> = [Sch] extends [TableSchema] ? StandardSchemaV1.InferOutput<Sch> : T\n\n/**\n * `createTable` options: with a `schema`, the row type is its output and `data` is the raw input\n * (`unknown[]`, e.g. parsed JSON), validated per row.\n */\nexport type CreateTableOptions<T, S extends TableState, Sch, M extends InvalidRowsMode> = Omit<\n TableOptions<SchemaRow<T, Sch>, S>,\n 'data' | 'schema' | 'invalidRows' | 'fetcher' | 'initialData'\n> & {\n data?: readonly ([Sch] extends [TableSchema] ? unknown : T)[]\n /** With a schema, the raw rows (validated like `data`). */\n initialData?: readonly ([Sch] extends [TableSchema] ? unknown : T)[]\n /** With a schema, the fetcher returns raw rows (validated like `data`). */\n fetcher?: ServerFetcher<[Sch] extends [TableSchema] ? unknown : T>\n // Without an inferred schema (explicit type arguments, a `TableOptions<T>` object) these keep\n // their `TableOptions` types.\n schema?: [Sch] extends [TableSchema] ? Sch : TableSchema<unknown, T>\n invalidRows?: [Sch] extends [TableSchema] ? M : InvalidRowsMode\n}\n\n/** Column id: `key` for key columns, `id` for accessor columns. */\nconst columnId = (c: { key?: unknown; id?: unknown }): unknown => c.key ?? c.id\n\n/**\n * Creates the table store (§3.2): immutable state snapshots, controlled slices, isolated listeners,\n * a re-entrancy guard and `destroy()`. Every unit of work runs as an operation (§3.4) through the\n * table's interceptor runtime, with the errorBoundary outermost and diagnostics on a per-table hub.\n *\n * `ctx.where` is always a constant option path or method name (`options`, `options.data`,\n * `setState`, `destroy`): it reaches fingerprints, docs URLs and issue links unredacted, so it must\n * never carry row keys or cell values.\n */\nexport function createTable<\n T,\n S extends TableState = never,\n const Fs extends readonly AnyFeature[] = [],\n const Sch extends TableSchema | undefined = undefined,\n const M extends InvalidRowsMode = 'drop',\n>(\n options: CreateTableOptions<T, TableStateFor<S, Fs, SchemaRow<T, Sch>>, Sch, M> & {\n features?: Fs\n },\n): Table<\n SchemaRow<T, Sch>,\n TableStateFor<S, Fs, SchemaRow<T, Sch>>,\n M,\n [Sch] extends [TableSchema] ? unknown : T\n> &\n ApiOf<Fs, SchemaRow<T, Sch>>\nexport function createTable<T, S extends TableState>(\n initialOptions: TableOptions<T, S>,\n): Table<T, S> {\n let options: TableOptions<T, S> = { ...initialOptions }\n let destroyed = false\n let trigger: Trigger = 'init'\n /** Pipeline run id; each `pipeline:run` increments it. */\n let run = 0\n let data: readonly T[] = EMPTY\n let ingestSeq = 0\n /** Data given by the user has been applied once (`RowModel.isInitialLoad`, §6.6). */\n let loaded = false\n /** The last `data:ingest` failed (op fallback): `status: 'error'` until one succeeds (§6.6). */\n let ingestFailed = false\n /** The ingest whose async schema validation is in flight (`status: 'validating'`); 0: none. */\n let validatingSeq = 0\n /** Rows of the current data that failed the schema (`getInvalidRows`, §5.1). */\n let schemaOutcome: SchemaOutcome | undefined\n /**\n * SSR (§7.2): `options.initialData` until the creation ingest read it (`undefined` with `data`),\n * then its ingested rows while they are the table's data (`initialRows`).\n */\n let initialInput = options.data === undefined ? options.initialData : undefined\n let initialRows: readonly unknown[] | undefined\n /** Created inside `createDeferred` (§7.2): async work waits for `startTable`. */\n let deferring = deferral.depth > 0\n /**\n * Opt-in parts of the store (`TableInternals.use`): the async request machinery and the server\n * source. A table without an async source creates none (§9.3).\n */\n const extensions = new Map<ExtensionFactory<TableExtension>, TableExtension>()\n /** The server source (`withFetcher`, §7.1), once a fetcher was given. */\n let server: ServerSource | undefined\n\n let controlled = new Set<string>()\n let snapshot: S\n /** The snapshot listeners last saw (`prev` of the next notification). */\n let notified: S\n /** A committed change (state or data) not yet notified. */\n let pending = false\n let notifying = false\n let rounds = 0\n let loopReported = false\n let current: number | undefined\n let listenerSeq = 0\n const listeners = new Map<number, Listener<S>>()\n /** Depth of feature API calls in progress: listeners are notified when the outermost returns. */\n let apiDepth = 0\n /** Data, state or columns changed since the row model was computed. */\n let rowModelDirty = true\n /** Diagnostic context (§8.1): the data mode of each pipeline feature (set once features load). */\n const modes: Record<string, 'client' | 'server'> = {}\n\n const table: Table<T, S> = {\n getState: () => snapshot,\n setState,\n subscribe,\n setOptions,\n getOptions: () => options,\n getData: () => data,\n // Core method: on the literal before installApis, so no feature can take the name.\n getRowModel,\n getInvalidRows: () => schemaOutcome?.invalid ?? EMPTY,\n getDiagnostics: () => hub.getDiagnostics(),\n // Core methods (§8.4), read-only: also on the literal before installApis.\n explain: () => explainTable(explainHost),\n explainRow: (key) => explainRow(explainHost, key),\n refetch,\n destroy,\n }\n\n // Redaction (§8.2): `diagnostics.redact` gives the level or a custom rule; column defs give the\n // sensitive columns and whether the rowKey column is sensitive. Both are read per emit, so\n // `setOptions({ columns })` is followed.\n const redactOption = options.diagnostics?.redact\n const redaction: RedactionOptions = {\n ...(typeof redactOption === 'string' ? { level: redactOption } : {}),\n ...(typeof redactOption === 'function' ? { redact: redactOption } : {}),\n sensitiveColumns,\n // A rowKey path into (or above) a sensitive column reads its data too (#45).\n rowKeySensitive: () => {\n const k = options.rowKey\n return typeof k === 'string' && sensitiveColumns().some((c) => pathsOverlap(k, c))\n },\n }\n const hub = createDiagnostics({\n ...(options.name === undefined ? {} : { name: options.name }),\n table,\n onDiagnostic: (d) => options.onDiagnostic?.(d),\n ...(options.strict === undefined ? {} : { strict: options.strict }),\n getContext: () => ({ trigger, run, mode: modes }),\n getDataRef: () => data,\n redaction,\n })\n // Option validator (§3.4, `table:init`), development only: before anything reads the options.\n // Invalid values throw DK_INVALID_OPTION; unknown / no-effect options are reported on the hub.\n if ((process.env.NODE_ENV !== \"production\")) validateOptions(options, { table: hub.table, emit: hub.emit })\n // Features (§3.3) are read once, at creation. `features` is the listed order (API install, conflict\n // reports); `pipeline` is stage order for the runner. Duplicates throw here (config error).\n const features: readonly AnyFeature[] = options.features ?? EMPTY\n // The feature runtime comes with the features (`defineFeature`): none for a table without.\n const rt = runtimeFor(features)\n const pipeline = rt ? rt.orderFeatures(features, hub.table) : EMPTY\n // Data modes (§7.1): DK_MODE_ORDER (a config error, development). A fetcher without a server-mode\n // feature (DK_FETCHER_WITHOUT_SERVER_MODE) is checked by the server source.\n if ((process.env.NODE_ENV !== \"production\")) checkModeOrder(pipeline, hub.table)\n for (const f of pipeline) if (f.stage !== 'none') modes[f.name] = f.mode ?? 'client'\n // Extension interceptors come first (asyncControl, one per table, installed with the first async\n // source: user / feature interceptors and the core see its signal); the boundary stays\n // outermost. Feature interceptors (pipeline order) come next, then the user's.\n const front: Interceptor<OpName>[] = []\n const featureInterceptors = pipeline.flatMap((f) => f.interceptors ?? EMPTY)\n // The tracer (§3.4, explain timings) is development-only: `(process.env.NODE_ENV !== \"production\")` folds to `false` in production\n // builds, which drops it from the bundle; there it is opt-in with `interceptors: [tracer()]`.\n const traced = (process.env.NODE_ENV !== \"production\") ? [tracer() as Interceptor<OpName>] : EMPTY\n const withFront = (list: readonly Interceptor<OpName>[] | undefined) => [\n ...front,\n ...traced,\n ...featureInterceptors,\n ...(list ?? EMPTY),\n ]\n const runtime = createInterceptorRuntime({\n interceptors: withFront(options.interceptors),\n emit: hub.emit,\n })\n runtime.installBoundary(errorBoundary)\n hub.attach(runtime)\n // Dev check (§5.3): the same `data` array passed again with changed contents (DK_DATA_MUTATED).\n const dataCheck = (process.env.NODE_ENV !== \"production\") ? createDataMutationCheck(hub.emit) : undefined\n // Feature `requires.core` (throws DK_FEATURE_INCOMPATIBLE), duplicate cores, version mismatch\n // (§8.5): development checks (changed 2026-10-13 for §9.3).\n if ((process.env.NODE_ENV !== \"production\")) checkVersions(features, hub.emit, hub.table)\n // Ingest data checks (§5.3) and zero-config columns (§4.1): both read only the first rows.\n // The data checks are developer hints: development only (changed 2026-10-13 for §9.3).\n const dataChecks = (process.env.NODE_ENV !== \"production\") ? createDataChecks(hub.emit) : undefined\n /** Controlled changes not fed back (DK_CONTROLLED_NOT_FED_BACK, development). */\n const feedback = (process.env.NODE_ENV !== \"production\") ? createStateFeedbackCheck(hub.emit) : undefined\n const derivedColumns = createDerivedColumns()\n // Schema ingest (§5.1): `schema` and `invalidRows` are read once (validated above). The step\n // comes with the schema (`withSchema`, `@datakable/core/schema`): a table without one never\n // bundles it.\n const schemaIngest = (options.schema as Partial<TableSchema> | undefined)?.[SCHEMA_INGEST]?.(\n options.invalidRows ?? 'drop',\n () => options.columns,\n )\n // Never silent (§8.6): in production (no option validator) an unwrapped schema would be ignored.\n if (options.schema && !schemaIngest) unwrapped('schema', options.schema, 'withSchema')\n\n function sensitiveColumns(): string[] {\n const out: string[] = []\n for (const c of options.columns ?? EMPTY) {\n const id = columnId(c as { key?: unknown; id?: unknown })\n if (c.sensitive && typeof id === 'string') out.push(id)\n }\n return out\n }\n\n function base(): ContextBase {\n return { table, run, trigger, phase: defaultPhase(), emit: hub.emit }\n }\n\n function op<O extends OpName>(\n name: O,\n input: OpInput<O>,\n where: string,\n core: Core<O>,\n fallback?: unknown,\n ): OpResult<O> {\n const ctx = createContext(name, input, where, base())\n if (fallback !== undefined) ctx.meta.set(FALLBACK, fallback)\n return runtime.run(name, ctx, core)\n }\n\n function withTrigger<R>(t: Trigger, fn: () => R): R {\n const prev = trigger\n trigger = t\n try {\n return fn()\n } finally {\n trigger = prev\n }\n }\n\n // --- state -------------------------------------------------------------------------------------\n\n function controlledKeys(state: ControlledState<S> | undefined): Set<string> {\n const keys = new Set<string>()\n if (state) for (const k of Object.keys(state)) if (state[k] !== undefined) keys.add(k)\n return keys\n }\n\n /** `s` with the controlled slices from `options.state`; `s` itself when none differ. */\n function overlay(s: S): S {\n let next: TableState | undefined\n const state = options.state as TableState | undefined\n if (state) {\n for (const k of controlled) {\n if (!Object.is(s[k], state[k])) {\n next ??= { ...s }\n next[k] = state[k]\n }\n }\n }\n return next ? freeze(next as S) : s\n }\n\n function commit(next: S) {\n snapshot = next\n pending = true\n rowModelDirty = true\n }\n\n function warnControlled(dropped: string[] | undefined) {\n if (options.onStateChange || controlled.size === 0) return\n const slices = dropped ?? [...controlled]\n hub.emit('DK_CONTROLLED_WITHOUT_HANDLER', { slices, dropped: dropped !== undefined })\n }\n\n function syncControlled() {\n controlled = controlledKeys(options.state)\n const next = overlay(snapshot)\n if (next !== snapshot) commit(next)\n if ((process.env.NODE_ENV !== \"production\")) feedback?.settle(options.state as TableState | undefined, controlled)\n warnControlled(undefined)\n }\n\n function dispatch(updater: Updater<S>, slot: string) {\n // A method argument, not an option: `where` = `operation table.setState at updater`.\n const partial: Partial<S> =\n typeof updater === 'function'\n ? callUser(\n 'updater',\n updater,\n snapshot,\n undefined,\n slot === 'setState' ? 'table.setState' : `${slot}.setState`,\n )\n : updater\n if (partial === null || typeof partial !== 'object') return\n let next: TableState | undefined\n let changes: TableState | undefined\n let dropped: string[] | undefined\n for (const k of Object.keys(partial)) {\n const v = (partial as TableState)[k]\n if (Object.is(snapshot[k], v)) continue\n if (controlled.has(k)) {\n if (options.onStateChange) {\n changes ??= {}\n changes[k] = v\n } else {\n dropped ??= []\n dropped.push(k)\n }\n continue\n }\n next ??= { ...snapshot }\n next[k] = v\n }\n // The owner is told first: if onStateChange throws, nothing is committed (state unchanged).\n if (changes && options.onStateChange) {\n const c = changes as Partial<S>\n if ((process.env.NODE_ENV !== \"production\")) feedback?.ask(Object.keys(changes), trigger)\n callUser('onStateChange', options.onStateChange, (prev: Partial<S>) => ({ ...prev, ...c }))\n if ((process.env.NODE_ENV !== \"production\")) feedback?.asked()\n }\n if (next) commit(freeze(next as S))\n if (dropped) warnControlled(dropped)\n }\n\n /**\n * Notifies listeners until no change is pending. Updates made by listeners are committed at once\n * and notified in the next round (not nested), so every listener sees rounds in order.\n */\n function flush() {\n if (notifying) return\n sync()\n if (!pending) return\n notifying = true\n let rethrow: unknown\n try {\n while (pending && !destroyed) {\n // Server state changed (this round's state): the fetch starts in the same notification.\n sync()\n pending = false\n rounds++\n const prev = notified\n const state = snapshot\n notified = state\n // Listeners read the row model of the state they are notified with.\n ensureRowModel()\n for (const [id, listener] of [...listeners]) {\n // A listener may destroy the table: the rest of the round is not notified.\n if (destroyed) break\n current = id\n try {\n listener(state, prev)\n } catch (e) {\n // strict: a DatakableError (e.g. DK_UPDATE_LOOP) thrown inside the listener.\n if (e instanceof DatakableError) {\n rethrow ??= e\n continue\n }\n try {\n const name = errorName(e)\n hub.emit(\n 'DK_LISTENER_FAILED',\n { listener: id, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n } catch (strictError) {\n rethrow ??= strictError\n }\n }\n }\n }\n } finally {\n notifying = false\n rounds = 0\n loopReported = false\n current = undefined\n }\n if (rethrow !== undefined) throw rethrow\n }\n\n /** `false` (and one `DK_UPDATE_LOOP`) when a re-entrant update would exceed the depth limit. */\n function admit(slices: string[]): boolean {\n if (!notifying || rounds < UPDATE_DEPTH_LIMIT) return true\n if (!loopReported) {\n loopReported = true\n hub.emit('DK_UPDATE_LOOP', {\n limit: UPDATE_DEPTH_LIMIT,\n slices,\n ...(current === undefined ? {} : { listener: current }),\n })\n }\n return false\n }\n\n function destroyedCall(method: string) {\n withTrigger(`api:${method}`, () => hub.emit('DK_TABLE_DESTROYED', { method }))\n }\n\n // --- data --------------------------------------------------------------------------------------\n\n /** Applies ingested data; `given`: it came from a user `options.data` (marks the first load). */\n function setData(next: readonly T[], given: boolean) {\n if (given && !loaded) {\n // isInitialLoad flips: a new row model and a notification even for the same reference.\n loaded = true\n pending = true\n rowModelDirty = true\n }\n if (next === data) return\n data = next\n pending = true\n // After `data` is set: diagnostics dedupe per data reference.\n if ((process.env.NODE_ENV !== \"production\")) dataChecks?.check(data, options.columns)\n // The pipeline runs before the next notification (or on getRowModel), as `pipeline:run` with\n // ctx.meta[FALLBACK] = the last good row model (pipeline/runner.ts).\n rowModelDirty = true\n }\n\n // --- row model ---------------------------------------------------------------------------------\n\n const runner = createPipeline<T>({\n runtime,\n featureRuntime: rt,\n emit: hub.emit,\n features: pipeline,\n listed: features,\n base,\n nextRun: () => ++run,\n getData: () => data,\n getState: () => snapshot,\n getColumns: () => options.columns ?? derivedColumns(data),\n getRowKey: () => options.rowKey,\n isInitialLoad: () => !loaded,\n status,\n issues: (index) => schemaOutcome?.flagged?.get(index),\n flagMode: schemaIngest !== undefined && options.invalidRows === 'flag',\n // Time-sliced work ended (§9.1): 'pending' → the finished row model, never silent (§6.6).\n refresh: (t) => {\n if (destroyed) return\n withTrigger(t, () => {\n touch()\n if (apiDepth === 0) flush()\n })\n },\n })\n\n const explainHost: ExplainHost = {\n table,\n features: pipeline,\n rowModel: getRowModel as () => RowModel<unknown>,\n lastRun: runner.lastRun,\n replay: runner.replayContext,\n data: () => data,\n state: () => snapshot,\n rowKey: () => options.rowKey,\n schemaMode: () => (schemaIngest ? (options.invalidRows ?? 'drop') : undefined),\n schema: () => schemaOutcome,\n diagnostics: () => hub.getDiagnostics(),\n redaction,\n ...(rt ? { detail: rt.explainDetail } : {}),\n }\n\n /**\n * Status from outside the pipeline (§6.6): a failed ingest, a running async schema, the search\n * slice (`pending` / `searching` / `error`), and the extensions' (a failed fetch, an in-flight\n * fetcher / `searchFn`, deferred async work). Precedence: error > fetching > searching >\n * validating > pending > idle.\n */\n function status(): RowModelStatus {\n const search = searchStatus()\n let s: RowModelStatus =\n ingestFailed || search === 'error'\n ? 'error'\n : search === 'searching'\n ? 'searching'\n : validatingSeq !== 0\n ? 'validating'\n : search === 'pending'\n ? 'pending'\n : 'idle'\n for (const e of extensions.values()) {\n const x = e.status?.()\n if (x !== undefined && RANK.indexOf(x) > RANK.indexOf(s)) s = x\n }\n return s\n }\n\n /** Extensions start work that follows state (the fetcher on server state, §7.1). */\n function sync() {\n for (const e of extensions.values()) e.sync?.()\n }\n\n /** The `status` of the first search-stage slice that reports one (§6.3). */\n function searchStatus(): unknown {\n for (const f of pipeline) {\n if (f.stage !== 'search') continue\n const s = (snapshot as Record<string, unknown> | undefined)?.[f.name]\n if (s !== null && typeof s === 'object' && 'status' in s) {\n const v = (s as { status: unknown }).status\n if (v !== 'idle') return v\n }\n }\n return 'idle'\n }\n\n /** A row model change not caused by state: recompute and notify on the next flush. */\n function touch() {\n pending = true\n rowModelDirty = true\n }\n\n /** Recomputes the row model if data, state or columns changed (never after destroy). */\n function ensureRowModel() {\n if (!rowModelDirty || destroyed) return\n rowModelDirty = false\n runner.run()\n }\n\n function getRowModel(): RowModel<T> {\n ensureRowModel()\n // Only empty when destroyed before the first run.\n return (\n runner.current() ?? {\n rows: EMPTY,\n filteredCount: 0,\n totalCount: 0,\n status: status(),\n isInitialLoad: !loaded,\n }\n )\n }\n\n /**\n * Applies the result of the latest `data:ingest`. A fallback (the boundary set `FAILED`) keeps\n * the previous data, sets `status: 'error'` and never marks the table loaded (§6.6, §8.6).\n */\n function applyIngest(out: readonly unknown[], failed: boolean, given: boolean, initial = false) {\n if (validatingSeq !== 0) {\n validatingSeq = 0\n touch()\n }\n if (failed) {\n ingestFailed = true\n touch()\n return\n }\n if (ingestFailed) {\n ingestFailed = false\n touch()\n }\n schemaOutcome = schemaIngest?.outcome(out)\n setData(out as readonly T[], given)\n // `initialData` is the initial async result only while it is the data (§7.2).\n initialRows = initial ? out : undefined\n }\n\n /**\n * `data:ingest` with the previous data as fallback: the schema step (§5.1) runs in its core. An\n * async result is applied if still latest. `fetched`: rows of an applied fetcher result (§7.1),\n * ingested like `options.data` (schema, data checks) and marking the table loaded.\n */\n function ingest(fetched?: readonly unknown[]) {\n const seq = ++ingestSeq\n // At creation, `initialData` stands in for a missing `data` (§7.2); read once.\n const initial = fetched === undefined && initialInput !== undefined\n const input = fetched ?? (initial ? initialInput : options.data)\n initialInput = undefined\n const given = input !== undefined\n const source: Trigger = fetched ? 'fetch' : 'data-change'\n // A validation still in flight is superseded (its result is discarded): not 'validating' now,\n // unless this ingest's own schema step goes async.\n if (validatingSeq !== 0) {\n validatingSeq = 0\n touch()\n }\n try {\n withTrigger(source, () => {\n const ctx = createContext(\n 'data:ingest',\n { data: input ?? EMPTY },\n fetched ? 'fetcher' : initial ? 'options.initialData' : 'options.data',\n base(),\n )\n ctx.meta.set(FALLBACK, data)\n let sync = true\n const out = runtime.run('data:ingest', ctx, (c) => {\n const input = c.input.data as readonly unknown[]\n if (!schemaIngest) return input\n const r = schemaIngest.validate(input, c.emit)\n if (isThenable(r) && seq === ingestSeq && !destroyed) {\n // 'validating' is never silent: a new row model and a notification now (§6.6).\n validatingSeq = seq\n touch()\n if (!sync) flush()\n }\n return r\n })\n sync = false\n if (!isThenable(out)) {\n applyIngest(out, ctx.meta.has(FAILED), given, initial)\n return\n }\n const settle = (d: readonly unknown[], failed: boolean) => {\n if (seq !== ingestSeq || destroyed) return\n withTrigger(source, () => {\n applyIngest(d, failed, given, initial)\n flush()\n })\n }\n Promise.resolve(out).then(\n (d) => settle(d, ctx.meta.has(FAILED)),\n // A DatakableError the boundary rethrew (strict, or `invalidRows: 'throw'` with an async\n // schema, already reported): the previous data is kept with 'error'. No unhandled\n // rejection either way (§8.6).\n () => settle(data, true),\n )\n })\n } finally {\n // Dev (§5.3): fingerprint `data` after the core read it, so the check never takes reads\n // first; also when the ingest threw, so a later same-reference check compares this data.\n if ((process.env.NODE_ENV !== \"production\") && !fetched) dataCheck?.remember(options.data)\n }\n }\n\n // --- API ---------------------------------------------------------------------------------------\n\n /** `state:dispatch` (where = `slot`, a constant method / feature name); notifies unless batched. */\n function update(updater: Updater<S>, slot: string) {\n void op('state:dispatch', { updater }, slot, (): undefined => {\n dispatch(updater, slot)\n })\n if (apiDepth === 0) flush()\n }\n\n function setState(updater: Updater<S>) {\n if (destroyed) return destroyedCall('setState')\n if (!admit(typeof updater === 'function' ? [] : Object.keys(updater ?? {}))) return\n withTrigger('api:setState', () => update(updater, 'setState'))\n }\n\n // --- features ----------------------------------------------------------------------------------\n\n /** A feature's `ctx.setState`: replaces its own slice (trigger: the API call's, if any). */\n function setSlice(name: string, updater: SliceUpdater<unknown>) {\n if (destroyed) return destroyedCall(`${name}.setState`)\n if (!admit([name])) return\n const slice: Updater<S> =\n typeof updater === 'function'\n ? (prev) => ({ [name]: (updater as (p: unknown) => unknown)(prev[name]) }) as Partial<S>\n : ({ [name]: updater } as Partial<S>)\n update(slice, name)\n }\n\n /** Runs a feature API method (wrapped once by `installApis`). */\n function callApi(method: string, fn: () => unknown, ifDestroyed?: () => unknown): unknown {\n if (destroyed) {\n destroyedCall(method)\n return ifDestroyed?.()\n }\n return withTrigger(`api:${method}`, () => {\n apiDepth++\n try {\n return fn()\n } finally {\n apiDepth--\n if (apiDepth === 0) flush()\n }\n })\n }\n\n function subscribe(listener: Listener<S>): () => void {\n if (destroyed) {\n destroyedCall('subscribe')\n return () => {}\n }\n const id = ++listenerSeq\n listeners.set(id, listener)\n return () => {\n listeners.delete(id)\n }\n }\n\n function setOptions(partial: Partial<TableOptions<T, S>>) {\n if (destroyed) return destroyedCall('setOptions')\n if (!admit(Object.keys(partial.state ?? {}))) return\n withTrigger('setOptions', () => {\n const previous = options\n const next = { ...options, ...partial }\n // Option validator (§3.4, `table:setOptions`, development): an invalid value throws before\n // anything changes. Read-once options (`features`, `timeout`, `diagnostics`, …) keep their\n // value in every build.\n if ((process.env.NODE_ENV !== \"production\")) validateOptionChange(partial, next, previous, { table: hub.table, emit: hub.emit })\n else keepReadOnce(partial, next, previous)\n if ('fetcher' in partial) useServer(next.fetcher, 'setOptions')?.check(next.fetcher)\n void op('table:setOptions', { options: next, previous }, 'options', (): undefined => {\n options = next\n if (next.interceptors !== previous.interceptors) {\n runtime.setInterceptors(withFront(next.interceptors))\n }\n if ('state' in partial || 'onStateChange' in partial) syncControlled()\n if (next.columns !== previous.columns || next.rowKey !== previous.rowKey) {\n rowModelDirty = true\n pending = true\n }\n // The page count changed (§6.5): listeners re-read it (and pagination clamps).\n if (next.rowCount !== previous.rowCount) pending = true\n // A new fetcher reference runs at once, even with the same params (flush → sync).\n if (next.fetcher !== previous.fetcher) server?.reset()\n // New data is checked when applied (setData); same data with new columns is checked here.\n if (next.data !== previous.data) ingest()\n else if ((process.env.NODE_ENV !== \"production\")) {\n if ('data' in partial) dataCheck?.check(next.data)\n if (next.columns !== previous.columns) dataChecks?.check(data, next.columns)\n }\n })\n flush()\n })\n }\n\n function destroy() {\n if (destroyed) return\n withTrigger('api:destroy', () => {\n try {\n void op('table:destroy', undefined, 'destroy', (): undefined => {\n destroyed = true\n })\n } finally {\n // Even if an interceptor skipped the core: the table is destroyed.\n destroyed = true\n ingestSeq++\n listeners.clear()\n runner.dispose()\n if ((process.env.NODE_ENV !== \"production\")) feedback?.dispose()\n }\n })\n }\n\n // --- extensions (§9.3) ---------------------------------------------------------------------\n\n function batch<R>(t: Trigger, fn: () => R): R {\n return withTrigger(t, () => {\n apiDepth++\n try {\n return fn()\n } finally {\n apiDepth--\n if (apiDepth === 0) flush()\n }\n })\n }\n\n const host: TableHost = {\n table: table as unknown as Table<unknown, TableState>,\n name: hub.table,\n emit: hub.emit,\n runtime,\n features: pipeline,\n options: () => options as TableOptions<unknown>,\n state: () => snapshot,\n base,\n destroyed: () => destroyed,\n loaded: () => loaded,\n status,\n touch,\n notify: () => {\n if (apiDepth === 0) flush()\n },\n batch,\n ingest: (rows) => ingest(rows),\n intercept(interceptor) {\n front.push(interceptor)\n runtime.setInterceptors(withFront(options.interceptors))\n },\n deferring: () => deferring,\n use,\n }\n\n function use<E extends TableExtension>(factory: ExtensionFactory<E>): E {\n let e = extensions.get(factory as ExtensionFactory<TableExtension>) as E | undefined\n if (!e) {\n e = factory(host)\n extensions.set(factory as ExtensionFactory<TableExtension>, e)\n }\n return e\n }\n\n /** The server source a `withFetcher` fetcher brings (created once); else the current one. */\n function useServer(fetcher: unknown, phase?: 'setOptions'): ServerSource | undefined {\n const factory = (fetcher as Partial<ServerFetcher<unknown>> | undefined)?.[SERVER_SOURCE]\n if (factory) server = use(factory)\n // Never silent (§8.6): in production an unwrapped fetcher would never run.\n else if (fetcher) unwrapped('fetcher', fetcher, 'withFetcher', 'server', phase)\n return server\n }\n\n /**\n * `DK_INVALID_OPTION` for a schema / fetcher not wrapped with its opt-in import (§5.1, §7.1). A\n * production check: in development the option validator reports it first, with the full text.\n */\n function unwrapped(\n path: 'schema' | 'fetcher',\n value: unknown,\n wrap: string,\n entry: string = path,\n phase: 'init' | 'setOptions' = 'init',\n ): never {\n return throwConfigError(\n 'DK_INVALID_OPTION',\n {\n path,\n expected: `wrapped|${wrap}|${path}|${entry}`, // prose key (diagnostics/prose.ts)\n received: typeof value,\n suggestions: [],\n phase,\n },\n { table: hub.table, trigger: phase },\n )\n }\n\n /**\n * Runs the fetcher again with the current params (§7.1); never rejects. Without a fetcher:\n * `{ status: 'applied' }` at once.\n */\n function refetch(): Promise<FetchOutcome> {\n if (destroyed) {\n destroyedCall('refetch')\n return Promise.resolve({ status: 'superseded' })\n }\n return (\n withTrigger('api:refetch', () => server?.sync(true)) ?? Promise.resolve({ status: 'applied' })\n )\n }\n\n /** `startTable` (§7.2): the deferred work starts; listeners see the new status at once. */\n function start() {\n if (!deferring || destroyed) return\n deferring = false\n let had = false\n for (const e of extensions.values()) if (e.start?.()) had = true\n if (!had) return\n // The pipeline re-runs: 'pending' ends and the search feature's check starts its searchFn.\n touch()\n withTrigger('hydrate', () => flush())\n }\n\n setTableInternals(table, {\n features: pipeline,\n name: options.name,\n rowCount: () => (typeof options.fetcher === 'function' ? server?.rowCount() : options.rowCount),\n hasFetcher: () => typeof options.fetcher === 'function',\n emit: hub.emit,\n use,\n peek: (factory) => extensions.get(factory as ExtensionFactory<TableExtension>) as never,\n extensions: () => extensions.values(),\n start,\n initialRows: () =>\n initialRows !== undefined && initialRows === data ? initialRows : undefined,\n })\n\n // --- init --------------------------------------------------------------------------------------\n\n controlled = controlledKeys(options.state)\n if ((process.env.NODE_ENV !== \"production\")) feedback?.settle(options.state as TableState | undefined, controlled)\n // Feature `initialState()` slices, with `options.initialState` on top per slice.\n snapshot = overlay(\n freeze(\n (rt\n ? rt.initialSlices(features, options.initialState, hub.emit)\n : { ...options.initialState }) as S,\n ),\n )\n notified = snapshot\n // Merges the feature APIs onto the table; a name clash throws DK_FEATURE_API_CONFLICT.\n rt?.installApis(features, {\n table: table as unknown as Table<unknown, TableState>,\n tableName: hub.table,\n emit: hub.emit,\n getSlice: (name) => snapshot[name],\n setSlice,\n call: callApi,\n })\n // Server source (§7.1): with a `withFetcher` fetcher (throws DK_FETCHER_WITHOUT_SERVER_MODE).\n useServer(options.fetcher)\n withTrigger('init', () => {\n // `void`: sync ops never return a promise (op kinds, §3.4).\n void op('table:init', { options }, 'options', (): undefined => {\n // Ingest first: dedupe is per data reference, so warnings follow the initial data.\n ingest()\n warnControlled(undefined)\n })\n // Server source (§7.1): the first request starts now ('fetching', isInitialLoad: true), unless\n // initialData answered the initial server state (§7.2).\n sync()\n })\n pending = false\n return table\n}\n"],"mappings":"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","names":["sync","input"]}
@@ -27,6 +27,7 @@ __export(data_mutation_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(data_mutation_exports);
29
29
  var import_catalog = require("../diagnostics/catalog.cjs");
30
+ var import_process_guard = require("../diagnostics/process-guard.cjs");
30
31
  var dataMutationDiagnostics = process.env.NODE_ENV !== "production" ? [
31
32
  (0, import_catalog.defineDiagnostic)({
32
33
  code: "DK_DATA_MUTATED",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/data-mutation.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport type { Emit } from '../diagnostics/types'\n\n/**\n * `DK_DATA_MUTATED` (§5.3, dev): `options.data` passed again as the same array, but its contents\n * changed since it was ingested. DataKable re-reads data only on a new reference, so the table would\n * silently keep showing the old rows. Dev only: the definition, its text and the check sit behind\n * `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DATA_MUTATED: {\n /** `data.length` when it was ingested, and now. */\n previousLength: number\n length: number\n /** Sampled row indices whose row was replaced or changed in place (empty: length changed). */\n rows: readonly number[]\n }\n }\n}\n\nexport const dataMutationDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_DATA_MUTATED',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'data' }),\n problem: (d) =>\n 'options.data is the same array as before, but its contents changed' +\n (d.previousLength === d.length\n ? ` (rows ${d.rows.map((r) => `#${r}`).join(', ')} changed in place)`\n : ` (length ${d.previousLength} → ${d.length})`),\n impact: () =>\n 'the table still shows the previous contents: data is re-read only when the array ' +\n 'reference changes',\n fix: () => [\n 'pass a new array instead of mutating it: setOptions({ data: [...data, row] }); in React, ' +\n 'update state immutably: setRows((rows) => [...rows, row])',\n 'to change one row, replace it: rows.map((r) => (r.id === id ? { ...r, ...patch } : r))',\n ],\n }),\n ]\n : []\n\n/** At most this many rows are sampled, and this many own keys per row. */\nconst ROWS = 16\nconst KEYS = 64\n\nexport interface DataPrint {\n readonly length: number\n /** Sampled row positions. */\n readonly rows: readonly number[]\n /** Per sampled row: the row itself (identity) and a shallow hash of its own values. */\n readonly refs: readonly unknown[]\n readonly hashes: readonly number[]\n}\n\nconst fnv = (h: number, s: string) => {\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 0x01000193)\n return h\n}\n\n/**\n * Shallow hash of a row: its first {@link KEYS} own keys and their primitive data values (or\n * types); accessor properties count by key only.\n */\nfunction rowHash(row: unknown): number {\n let h = 0x811c9dc5\n if (row === null || typeof row !== 'object') return fnv(h, `${typeof row}:${String(row)}`) >>> 0\n try {\n let k = 0\n for (const key in row) {\n if (!Object.hasOwn(row, key)) continue\n if (k++ === KEYS) break\n // Never call a getter: a dev check must not run user code (side effects, read budgets).\n const d = Object.getOwnPropertyDescriptor(row, key)\n h = fnv(h, key)\n if (!d || !('value' in d)) {\n h = fnv(h, 'accessor')\n continue\n }\n const v = d.value\n h = fnv(h, v !== null && typeof v === 'object' ? 'object' : `${typeof v}:${String(v)}`)\n }\n } catch {\n // A throwing getter / Proxy trap: the row is compared by identity only.\n }\n return h >>> 0\n}\n\n/** A bounded fingerprint of `data`: its length and {@link ROWS} evenly spaced rows (row 0 always). */\nexport function dataPrint(data: readonly unknown[]): DataPrint {\n const n = data.length\n const stride = n <= ROWS ? 1 : Math.floor(n / ROWS)\n const rows: number[] = []\n const refs: unknown[] = []\n const hashes: number[] = []\n for (let i = 0; i < n && rows.length < ROWS; i += stride) {\n rows.push(i)\n refs.push(data[i])\n hashes.push(rowHash(data[i]))\n }\n return { length: n, rows, refs, hashes }\n}\n\n/** `'length'` when the length changed, else the sampled row indices that changed (empty: same). */\nexport function diffPrint(print: DataPrint, data: readonly unknown[]): 'length' | number[] {\n if (data.length !== print.length) return 'length'\n const changed: number[] = []\n for (let i = 0; i < print.rows.length; i++) {\n const r = print.rows[i] as number\n const row = data[r]\n if (row !== print.refs[i] || rowHash(row) !== print.hashes[i]) changed.push(r)\n }\n return changed\n}\n\n/**\n * A dev check must never change what the table does: rows that cannot be read (a throwing Proxy or\n * getter) leave the check off, and the pipeline reports them as usual.\n */\nfunction safePrint(data: readonly unknown[]): DataPrint | undefined {\n try {\n return dataPrint(data)\n } catch {\n return undefined\n }\n}\n\n/** Remembers the ingested `data` and reports `DK_DATA_MUTATED` when the same array comes back changed. */\nexport function createDataMutationCheck(emit: Emit) {\n let print: DataPrint | undefined\n return {\n /** `options.data` was ingested. */\n remember(data: readonly unknown[] | undefined) {\n print = data === undefined ? undefined : safePrint(data)\n },\n /** `options.data` was passed again with the same reference. */\n check(data: readonly unknown[] | undefined) {\n if (!print || data === undefined) return\n let diff: 'length' | number[]\n try {\n diff = diffPrint(print, data)\n } catch {\n return\n }\n if (diff !== 'length' && diff.length === 0) return\n emit('DK_DATA_MUTATED', {\n previousLength: print.length,\n length: data.length,\n rows: diff === 'length' ? [] : diff,\n })\n // Compare later calls with what was last seen: each further change reports again.\n print = safePrint(data)\n },\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/table/data-mutation.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\n\n/**\n * `DK_DATA_MUTATED` (§5.3, dev): `options.data` passed again as the same array, but its contents\n * changed since it was ingested. DataKable re-reads data only on a new reference, so the table would\n * silently keep showing the old rows. Dev only: the definition, its text and the check sit behind\n * `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DATA_MUTATED: {\n /** `data.length` when it was ingested, and now. */\n previousLength: number\n length: number\n /** Sampled row indices whose row was replaced or changed in place (empty: length changed). */\n rows: readonly number[]\n }\n }\n}\n\nexport const dataMutationDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_DATA_MUTATED',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'data' }),\n problem: (d) =>\n 'options.data is the same array as before, but its contents changed' +\n (d.previousLength === d.length\n ? ` (rows ${d.rows.map((r) => `#${r}`).join(', ')} changed in place)`\n : ` (length ${d.previousLength} → ${d.length})`),\n impact: () =>\n 'the table still shows the previous contents: data is re-read only when the array ' +\n 'reference changes',\n fix: () => [\n 'pass a new array instead of mutating it: setOptions({ data: [...data, row] }); in React, ' +\n 'update state immutably: setRows((rows) => [...rows, row])',\n 'to change one row, replace it: rows.map((r) => (r.id === id ? { ...r, ...patch } : r))',\n ],\n }),\n ]\n : []\n\n/** At most this many rows are sampled, and this many own keys per row. */\nconst ROWS = 16\nconst KEYS = 64\n\nexport interface DataPrint {\n readonly length: number\n /** Sampled row positions. */\n readonly rows: readonly number[]\n /** Per sampled row: the row itself (identity) and a shallow hash of its own values. */\n readonly refs: readonly unknown[]\n readonly hashes: readonly number[]\n}\n\nconst fnv = (h: number, s: string) => {\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 0x01000193)\n return h\n}\n\n/**\n * Shallow hash of a row: its first {@link KEYS} own keys and their primitive data values (or\n * types); accessor properties count by key only.\n */\nfunction rowHash(row: unknown): number {\n let h = 0x811c9dc5\n if (row === null || typeof row !== 'object') return fnv(h, `${typeof row}:${String(row)}`) >>> 0\n try {\n let k = 0\n for (const key in row) {\n if (!Object.hasOwn(row, key)) continue\n if (k++ === KEYS) break\n // Never call a getter: a dev check must not run user code (side effects, read budgets).\n const d = Object.getOwnPropertyDescriptor(row, key)\n h = fnv(h, key)\n if (!d || !('value' in d)) {\n h = fnv(h, 'accessor')\n continue\n }\n const v = d.value\n h = fnv(h, v !== null && typeof v === 'object' ? 'object' : `${typeof v}:${String(v)}`)\n }\n } catch {\n // A throwing getter / Proxy trap: the row is compared by identity only.\n }\n return h >>> 0\n}\n\n/** A bounded fingerprint of `data`: its length and {@link ROWS} evenly spaced rows (row 0 always). */\nexport function dataPrint(data: readonly unknown[]): DataPrint {\n const n = data.length\n const stride = n <= ROWS ? 1 : Math.floor(n / ROWS)\n const rows: number[] = []\n const refs: unknown[] = []\n const hashes: number[] = []\n for (let i = 0; i < n && rows.length < ROWS; i += stride) {\n rows.push(i)\n refs.push(data[i])\n hashes.push(rowHash(data[i]))\n }\n return { length: n, rows, refs, hashes }\n}\n\n/** `'length'` when the length changed, else the sampled row indices that changed (empty: same). */\nexport function diffPrint(print: DataPrint, data: readonly unknown[]): 'length' | number[] {\n if (data.length !== print.length) return 'length'\n const changed: number[] = []\n for (let i = 0; i < print.rows.length; i++) {\n const r = print.rows[i] as number\n const row = data[r]\n if (row !== print.refs[i] || rowHash(row) !== print.hashes[i]) changed.push(r)\n }\n return changed\n}\n\n/**\n * A dev check must never change what the table does: rows that cannot be read (a throwing Proxy or\n * getter) leave the check off, and the pipeline reports them as usual.\n */\nfunction safePrint(data: readonly unknown[]): DataPrint | undefined {\n try {\n return dataPrint(data)\n } catch {\n return undefined\n }\n}\n\n/** Remembers the ingested `data` and reports `DK_DATA_MUTATED` when the same array comes back changed. */\nexport function createDataMutationCheck(emit: Emit) {\n let print: DataPrint | undefined\n return {\n /** `options.data` was ingested. */\n remember(data: readonly unknown[] | undefined) {\n print = data === undefined ? undefined : safePrint(data)\n },\n /** `options.data` was passed again with the same reference. */\n check(data: readonly unknown[] | undefined) {\n if (!print || data === undefined) return\n let diff: 'length' | number[]\n try {\n diff = diffPrint(print, data)\n } catch {\n return\n }\n if (diff !== 'length' && diff.length === 0) return\n emit('DK_DATA_MUTATED', {\n previousLength: print.length,\n length: data.length,\n rows: diff === 'length' ? [] : diff,\n })\n // Compare later calls with what was last seen: each further change reports again.\n print = safePrint(data)\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/table/data-mutation.ts
2
2
  import { defineDiagnostic } from "../diagnostics/catalog.js";
3
+ import "../diagnostics/process-guard.js";
3
4
  var dataMutationDiagnostics = process.env.NODE_ENV !== "production" ? [
4
5
  defineDiagnostic({
5
6
  code: "DK_DATA_MUTATED",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/data-mutation.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport type { Emit } from '../diagnostics/types'\n\n/**\n * `DK_DATA_MUTATED` (§5.3, dev): `options.data` passed again as the same array, but its contents\n * changed since it was ingested. DataKable re-reads data only on a new reference, so the table would\n * silently keep showing the old rows. Dev only: the definition, its text and the check sit behind\n * `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DATA_MUTATED: {\n /** `data.length` when it was ingested, and now. */\n previousLength: number\n length: number\n /** Sampled row indices whose row was replaced or changed in place (empty: length changed). */\n rows: readonly number[]\n }\n }\n}\n\nexport const dataMutationDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_DATA_MUTATED',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'data' }),\n problem: (d) =>\n 'options.data is the same array as before, but its contents changed' +\n (d.previousLength === d.length\n ? ` (rows ${d.rows.map((r) => `#${r}`).join(', ')} changed in place)`\n : ` (length ${d.previousLength} → ${d.length})`),\n impact: () =>\n 'the table still shows the previous contents: data is re-read only when the array ' +\n 'reference changes',\n fix: () => [\n 'pass a new array instead of mutating it: setOptions({ data: [...data, row] }); in React, ' +\n 'update state immutably: setRows((rows) => [...rows, row])',\n 'to change one row, replace it: rows.map((r) => (r.id === id ? { ...r, ...patch } : r))',\n ],\n }),\n ]\n : []\n\n/** At most this many rows are sampled, and this many own keys per row. */\nconst ROWS = 16\nconst KEYS = 64\n\nexport interface DataPrint {\n readonly length: number\n /** Sampled row positions. */\n readonly rows: readonly number[]\n /** Per sampled row: the row itself (identity) and a shallow hash of its own values. */\n readonly refs: readonly unknown[]\n readonly hashes: readonly number[]\n}\n\nconst fnv = (h: number, s: string) => {\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 0x01000193)\n return h\n}\n\n/**\n * Shallow hash of a row: its first {@link KEYS} own keys and their primitive data values (or\n * types); accessor properties count by key only.\n */\nfunction rowHash(row: unknown): number {\n let h = 0x811c9dc5\n if (row === null || typeof row !== 'object') return fnv(h, `${typeof row}:${String(row)}`) >>> 0\n try {\n let k = 0\n for (const key in row) {\n if (!Object.hasOwn(row, key)) continue\n if (k++ === KEYS) break\n // Never call a getter: a dev check must not run user code (side effects, read budgets).\n const d = Object.getOwnPropertyDescriptor(row, key)\n h = fnv(h, key)\n if (!d || !('value' in d)) {\n h = fnv(h, 'accessor')\n continue\n }\n const v = d.value\n h = fnv(h, v !== null && typeof v === 'object' ? 'object' : `${typeof v}:${String(v)}`)\n }\n } catch {\n // A throwing getter / Proxy trap: the row is compared by identity only.\n }\n return h >>> 0\n}\n\n/** A bounded fingerprint of `data`: its length and {@link ROWS} evenly spaced rows (row 0 always). */\nexport function dataPrint(data: readonly unknown[]): DataPrint {\n const n = data.length\n const stride = n <= ROWS ? 1 : Math.floor(n / ROWS)\n const rows: number[] = []\n const refs: unknown[] = []\n const hashes: number[] = []\n for (let i = 0; i < n && rows.length < ROWS; i += stride) {\n rows.push(i)\n refs.push(data[i])\n hashes.push(rowHash(data[i]))\n }\n return { length: n, rows, refs, hashes }\n}\n\n/** `'length'` when the length changed, else the sampled row indices that changed (empty: same). */\nexport function diffPrint(print: DataPrint, data: readonly unknown[]): 'length' | number[] {\n if (data.length !== print.length) return 'length'\n const changed: number[] = []\n for (let i = 0; i < print.rows.length; i++) {\n const r = print.rows[i] as number\n const row = data[r]\n if (row !== print.refs[i] || rowHash(row) !== print.hashes[i]) changed.push(r)\n }\n return changed\n}\n\n/**\n * A dev check must never change what the table does: rows that cannot be read (a throwing Proxy or\n * getter) leave the check off, and the pipeline reports them as usual.\n */\nfunction safePrint(data: readonly unknown[]): DataPrint | undefined {\n try {\n return dataPrint(data)\n } catch {\n return undefined\n }\n}\n\n/** Remembers the ingested `data` and reports `DK_DATA_MUTATED` when the same array comes back changed. */\nexport function createDataMutationCheck(emit: Emit) {\n let print: DataPrint | undefined\n return {\n /** `options.data` was ingested. */\n remember(data: readonly unknown[] | undefined) {\n print = data === undefined ? undefined : safePrint(data)\n },\n /** `options.data` was passed again with the same reference. */\n check(data: readonly unknown[] | undefined) {\n if (!print || data === undefined) return\n let diff: 'length' | number[]\n try {\n diff = diffPrint(print, data)\n } catch {\n return\n }\n if (diff !== 'length' && diff.length === 0) return\n emit('DK_DATA_MUTATED', {\n previousLength: print.length,\n length: data.length,\n rows: diff === 'length' ? [] : diff,\n })\n // Compare later calls with what was last seen: each further change reports again.\n print = safePrint(data)\n },\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/table/data-mutation.ts"],"sourcesContent":["import { defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\n\n/**\n * `DK_DATA_MUTATED` (§5.3, dev): `options.data` passed again as the same array, but its contents\n * changed since it was ingested. DataKable re-reads data only on a new reference, so the table would\n * silently keep showing the old rows. Dev only: the definition, its text and the check sit behind\n * `(process.env.NODE_ENV !== \"production\")`, so a production bundle has none of it.\n */\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_DATA_MUTATED: {\n /** `data.length` when it was ingested, and now. */\n previousLength: number\n length: number\n /** Sampled row indices whose row was replaced or changed in place (empty: length changed). */\n rows: readonly number[]\n }\n }\n}\n\nexport const dataMutationDiagnostics = (process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic({\n code: 'DK_DATA_MUTATED',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'data' }),\n problem: (d) =>\n 'options.data is the same array as before, but its contents changed' +\n (d.previousLength === d.length\n ? ` (rows ${d.rows.map((r) => `#${r}`).join(', ')} changed in place)`\n : ` (length ${d.previousLength} → ${d.length})`),\n impact: () =>\n 'the table still shows the previous contents: data is re-read only when the array ' +\n 'reference changes',\n fix: () => [\n 'pass a new array instead of mutating it: setOptions({ data: [...data, row] }); in React, ' +\n 'update state immutably: setRows((rows) => [...rows, row])',\n 'to change one row, replace it: rows.map((r) => (r.id === id ? { ...r, ...patch } : r))',\n ],\n }),\n ]\n : []\n\n/** At most this many rows are sampled, and this many own keys per row. */\nconst ROWS = 16\nconst KEYS = 64\n\nexport interface DataPrint {\n readonly length: number\n /** Sampled row positions. */\n readonly rows: readonly number[]\n /** Per sampled row: the row itself (identity) and a shallow hash of its own values. */\n readonly refs: readonly unknown[]\n readonly hashes: readonly number[]\n}\n\nconst fnv = (h: number, s: string) => {\n for (let i = 0; i < s.length; i++) h = Math.imul(h ^ s.charCodeAt(i), 0x01000193)\n return h\n}\n\n/**\n * Shallow hash of a row: its first {@link KEYS} own keys and their primitive data values (or\n * types); accessor properties count by key only.\n */\nfunction rowHash(row: unknown): number {\n let h = 0x811c9dc5\n if (row === null || typeof row !== 'object') return fnv(h, `${typeof row}:${String(row)}`) >>> 0\n try {\n let k = 0\n for (const key in row) {\n if (!Object.hasOwn(row, key)) continue\n if (k++ === KEYS) break\n // Never call a getter: a dev check must not run user code (side effects, read budgets).\n const d = Object.getOwnPropertyDescriptor(row, key)\n h = fnv(h, key)\n if (!d || !('value' in d)) {\n h = fnv(h, 'accessor')\n continue\n }\n const v = d.value\n h = fnv(h, v !== null && typeof v === 'object' ? 'object' : `${typeof v}:${String(v)}`)\n }\n } catch {\n // A throwing getter / Proxy trap: the row is compared by identity only.\n }\n return h >>> 0\n}\n\n/** A bounded fingerprint of `data`: its length and {@link ROWS} evenly spaced rows (row 0 always). */\nexport function dataPrint(data: readonly unknown[]): DataPrint {\n const n = data.length\n const stride = n <= ROWS ? 1 : Math.floor(n / ROWS)\n const rows: number[] = []\n const refs: unknown[] = []\n const hashes: number[] = []\n for (let i = 0; i < n && rows.length < ROWS; i += stride) {\n rows.push(i)\n refs.push(data[i])\n hashes.push(rowHash(data[i]))\n }\n return { length: n, rows, refs, hashes }\n}\n\n/** `'length'` when the length changed, else the sampled row indices that changed (empty: same). */\nexport function diffPrint(print: DataPrint, data: readonly unknown[]): 'length' | number[] {\n if (data.length !== print.length) return 'length'\n const changed: number[] = []\n for (let i = 0; i < print.rows.length; i++) {\n const r = print.rows[i] as number\n const row = data[r]\n if (row !== print.refs[i] || rowHash(row) !== print.hashes[i]) changed.push(r)\n }\n return changed\n}\n\n/**\n * A dev check must never change what the table does: rows that cannot be read (a throwing Proxy or\n * getter) leave the check off, and the pipeline reports them as usual.\n */\nfunction safePrint(data: readonly unknown[]): DataPrint | undefined {\n try {\n return dataPrint(data)\n } catch {\n return undefined\n }\n}\n\n/** Remembers the ingested `data` and reports `DK_DATA_MUTATED` when the same array comes back changed. */\nexport function createDataMutationCheck(emit: Emit) {\n let print: DataPrint | undefined\n return {\n /** `options.data` was ingested. */\n remember(data: readonly unknown[] | undefined) {\n print = data === undefined ? undefined : safePrint(data)\n },\n /** `options.data` was passed again with the same reference. */\n check(data: readonly unknown[] | undefined) {\n if (!print || data === undefined) return\n let diff: 'length' | number[]\n try {\n diff = diffPrint(print, data)\n } catch {\n return\n }\n if (diff !== 'length' && diff.length === 0) return\n emit('DK_DATA_MUTATED', {\n previousLength: print.length,\n length: data.length,\n rows: diff === 'length' ? [] : diff,\n })\n // Compare later calls with what was last seen: each further change reports again.\n print = safePrint(data)\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/internals.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from '../diagnostics/catalog'\nimport type { Emit, EmitOptions, Trigger } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport type { RowModelStatus } from '../pipeline/types'\nimport type { Table, TableOptions, TableState } from './types'\n\n/**\n * What a table extension sees of its table (`TableInternals.use`). Extensions are the opt-in parts\n * of the store that a bare table does not bundle (changed 2026-10-13 for §9.3): the async request\n * machinery (`source/requests.ts`, for `searchFn` and the fetcher) and the server source\n * (`source/server.ts`, `withFetcher`). Not public API.\n */\nexport interface TableHost {\n readonly table: Table<unknown, TableState>\n /** `options.name`, else `table#n` (diagnostic context, config errors). */\n readonly name: string\n readonly emit: Emit\n /** The interceptor runtime of the table (ops run through it with the errorBoundary outermost). */\n readonly runtime: InterceptorRuntime\n /** The installed features in pipeline order. */\n readonly features: readonly AnyFeature[]\n options(): TableOptions<unknown>\n state(): TableState\n /** Context values of a new op ctx (table, run, trigger, phase, emit). */\n base(): ContextBase\n destroyed(): boolean\n /** Data given by the user or a fetcher has been applied once (`isInitialLoad` is `false`). */\n loaded(): boolean\n /** The table's status (§6.6), every source included. */\n status(): RowModelStatus\n /** A row model change not caused by state: recomputed and notified on the next flush. */\n touch(): void\n /** Notifies listeners now, unless an API call or batch is in progress (it notifies at its end). */\n notify(): void\n /** Runs `fn` under trigger `t` as one batch: listeners are notified once, when it returns. */\n batch<R>(t: Trigger, fn: () => R): R\n /** Ingests rows a fetcher returned through `data:ingest` (schema, data checks; marks loaded). */\n ingest(rows: readonly unknown[]): void\n /** Installs `interceptor` first in the chain (inside the errorBoundary), e.g. asyncControl. */\n intercept(interceptor: Interceptor<OpName>): void\n /** The table was created inside `createDeferred` and `startTable` has not run yet (§7.2). */\n deferring(): boolean\n /** Another extension of the same table (`TableInternals.use`). */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n}\n\n/** Hooks a table extension may implement. */\nexport interface TableExtension {\n /** Its contribution to `RowModel.status` (§6.6 precedence), or `undefined`. */\n status?(): RowModelStatus | undefined\n /** Before each notification round: start work that follows state (the fetcher, §7.1). */\n sync?(): void\n /** `startTable` (§7.2): starts deferred work; `true` when something was waiting. */\n start?(): boolean\n /** Every async source deferred since creation (`createDeferred`, `DK_SSR_ASYNC_DEFERRED`). */\n deferred?(): readonly ('fetcher' | 'searchFn')[]\n}\n\n/** Creates one table's extension; `use` calls it once per table. */\nexport type ExtensionFactory<E extends TableExtension> = (host: TableHost) => E\n\n/**\n * Internal seam for core modules that work through a table (search T19, fetcher T21, pagination,\n * SSR). Not exported from the package: `createTable` registers each table here.\n */\nexport interface TableInternals {\n /**\n * The installed features in pipeline order (`orderFeatures`: by stage, stable). Fixed at\n * creation; the pipeline runner (T09) runs their `rowModel`s in this order.\n */\n readonly features: readonly AnyFeature[]\n /**\n * The server row count (§6.5, §7.1): the last applied fetcher result's `rowCount` with a fetcher\n * (`undefined` until one applied), else `options.rowCount`.\n */\n rowCount(): number | undefined\n /** `options.fetcher` is set. */\n hasFetcher(): boolean\n /** The table's diagnostics hub emit (`emitTableDiagnostic`). */\n readonly emit: Emit\n /**\n * The ingested `options.initialData` while it is still the table's data (§7.2): the result of\n * the initial async work. `undefined` without it, or once other data was applied.\n */\n initialRows(): readonly unknown[] | undefined\n /** The table's extension made by `factory`, created on first use. */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n /** The table's extension made by `factory`, if it was created. */\n peek<E extends TableExtension>(factory: ExtensionFactory<E>): E | undefined\n /** The extensions created so far (SSR reads their deferred sources without importing them). */\n extensions(): Iterable<TableExtension>\n /** Starts the deferred work (`startTable`); a no-op when not deferred. */\n start(): void\n}\n\nconst registry = new WeakMap<object, TableInternals>()\n\nexport function setTableInternals(table: object, internals: TableInternals): void {\n registry.set(table, internals)\n}\n\nexport function getTableInternals(table: object): TableInternals {\n const i = registry.get(table)\n if (!i) throw new TypeError('getTableInternals: not a table made by createTable()')\n return i\n}\n\n/**\n * Reports a catalog diagnostic on `table`'s hub (its sinks, `onDiagnostic`, `getDiagnostics()`), for\n * bindings that detect problems outside the table, e.g. `@datakable/react`'s unstable-columns warning.\n * The code must be defined with `defineDiagnostic`.\n */\nexport function emitTableDiagnostic<C extends DiagnosticCode>(\n table: object,\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n): void {\n getTableInternals(table).emit(code, details, options)\n}\n\n/**\n * `createDeferred` (ssr.ts) nesting depth: a table created while it is > 0 defers its async work\n * until `startTable` (§7.2). Kept here so `createTable` does not import the SSR module.\n */\nexport const deferral = { depth: 0 }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmGA,IAAM,WAAW,oBAAI,QAAgC;AAE9C,SAAS,kBAAkB,OAAe,WAAiC;AAChF,WAAS,IAAI,OAAO,SAAS;AAC/B;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,IAAI,SAAS,IAAI,KAAK;AAC5B,MAAI,CAAC,EAAG,OAAM,IAAI,UAAU,sDAAsD;AAClF,SAAO;AACT;AAOO,SAAS,oBACd,OACA,MACA,SACA,SACM;AACN,oBAAkB,KAAK,EAAE,KAAK,MAAM,SAAS,OAAO;AACtD;AAMO,IAAM,WAAW,EAAE,OAAO,EAAE;","names":[]}
1
+ {"version":3,"sources":["../../src/table/internals.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from '../diagnostics/catalog'\nimport type { Emit, EmitOptions, Trigger } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport type { RowModelStatus } from '../pipeline/types'\nimport type { Table, TableOptions, TableState } from './types'\n\n/**\n * What a table extension sees of its table (`TableInternals.use`). Extensions are the opt-in parts\n * of the store that a bare table does not bundle (changed 2026-10-13 for §9.3): the async request\n * machinery (`source/requests.ts`, for `searchFn` and the fetcher) and the server source\n * (`source/server.ts`, `withFetcher`). Not public API.\n */\nexport interface TableHost {\n readonly table: Table<unknown, TableState>\n /** `options.name`, else `table#n` (diagnostic context, config errors). */\n readonly name: string\n readonly emit: Emit\n /** The interceptor runtime of the table (ops run through it with the errorBoundary outermost). */\n readonly runtime: InterceptorRuntime\n /** The installed features in pipeline order. */\n readonly features: readonly AnyFeature[]\n options(): TableOptions<unknown>\n state(): TableState\n /** Context values of a new op ctx (table, run, trigger, phase, emit). */\n base(): ContextBase\n destroyed(): boolean\n /** Data given by the user or a fetcher has been applied once (`isInitialLoad` is `false`). */\n loaded(): boolean\n /** The table's status (§6.6), every source included. */\n status(): RowModelStatus\n /** A row model change not caused by state: recomputed and notified on the next flush. */\n touch(): void\n /** Notifies listeners now, unless an API call or batch is in progress (it notifies at its end). */\n notify(): void\n /** Runs `fn` under trigger `t` as one batch: listeners are notified once, when it returns. */\n batch<R>(t: Trigger, fn: () => R): R\n /** Ingests rows a fetcher returned through `data:ingest` (schema, data checks; marks loaded). */\n ingest(rows: readonly unknown[]): void\n /** Installs `interceptor` first in the chain (inside the errorBoundary), e.g. asyncControl. */\n intercept(interceptor: Interceptor<OpName>): void\n /** The table was created inside `createDeferred` and `startTable` has not run yet (§7.2). */\n deferring(): boolean\n /** Another extension of the same table (`TableInternals.use`). */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n}\n\n/** Hooks a table extension may implement. */\nexport interface TableExtension {\n /** Its contribution to `RowModel.status` (§6.6 precedence), or `undefined`. */\n status?(): RowModelStatus | undefined\n /** Before each notification round: start work that follows state (the fetcher, §7.1). */\n sync?(): void\n /** `startTable` (§7.2): starts deferred work; `true` when something was waiting. */\n start?(): boolean\n /** Every async source deferred since creation (`createDeferred`, `DK_SSR_ASYNC_DEFERRED`). */\n deferred?(): readonly ('fetcher' | 'searchFn')[]\n}\n\n/** Creates one table's extension; `use` calls it once per table. */\nexport type ExtensionFactory<E extends TableExtension> = (host: TableHost) => E\n\n/**\n * Internal seam for core modules that work through a table (search T19, fetcher T21, pagination,\n * SSR). Not exported from the package: `createTable` registers each table here.\n */\nexport interface TableInternals {\n /**\n * The installed features in pipeline order (`orderFeatures`: by stage, stable). Fixed at\n * creation; the pipeline runner (T09) runs their `rowModel`s in this order.\n */\n readonly features: readonly AnyFeature[]\n /** `options.name` as given (`undefined` without one). */\n readonly name: string | undefined\n /**\n * The server row count (§6.5, §7.1): the last applied fetcher result's `rowCount` with a fetcher\n * (`undefined` until one applied), else `options.rowCount`.\n */\n rowCount(): number | undefined\n /** `options.fetcher` is set. */\n hasFetcher(): boolean\n /** The table's diagnostics hub emit (`emitTableDiagnostic`). */\n readonly emit: Emit\n /**\n * The ingested `options.initialData` while it is still the table's data (§7.2): the result of\n * the initial async work. `undefined` without it, or once other data was applied.\n */\n initialRows(): readonly unknown[] | undefined\n /** The table's extension made by `factory`, created on first use. */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n /** The table's extension made by `factory`, if it was created. */\n peek<E extends TableExtension>(factory: ExtensionFactory<E>): E | undefined\n /** The extensions created so far (SSR reads their deferred sources without importing them). */\n extensions(): Iterable<TableExtension>\n /** Starts the deferred work (`startTable`); a no-op when not deferred. */\n start(): void\n}\n\nconst registry = new WeakMap<object, TableInternals>()\n\nexport function setTableInternals(table: object, internals: TableInternals): void {\n registry.set(table, internals)\n}\n\nexport function getTableInternals(table: object): TableInternals {\n const i = registry.get(table)\n if (!i) throw new TypeError('getTableInternals: not a table made by createTable()')\n return i\n}\n\n/**\n * Reports a catalog diagnostic on `table`'s hub (its sinks, `onDiagnostic`, `getDiagnostics()`), for\n * bindings that detect problems outside the table, e.g. `@datakable/react`'s unstable-columns warning.\n * The code must be defined with `defineDiagnostic`.\n */\nexport function emitTableDiagnostic<C extends DiagnosticCode>(\n table: object,\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n): void {\n getTableInternals(table).emit(code, details, options)\n}\n\n/**\n * `createDeferred` (ssr.ts) nesting depth: a table created while it is > 0 defers its async work\n * until `startTable` (§7.2). Kept here so `createTable` does not import the SSR module.\n */\nexport const deferral = { depth: 0 }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqGA,IAAM,WAAW,oBAAI,QAAgC;AAE9C,SAAS,kBAAkB,OAAe,WAAiC;AAChF,WAAS,IAAI,OAAO,SAAS;AAC/B;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,IAAI,SAAS,IAAI,KAAK;AAC5B,MAAI,CAAC,EAAG,OAAM,IAAI,UAAU,sDAAsD;AAClF,SAAO;AACT;AAOO,SAAS,oBACd,OACA,MACA,SACA,SACM;AACN,oBAAkB,KAAK,EAAE,KAAK,MAAM,SAAS,OAAO;AACtD;AAMO,IAAM,WAAW,EAAE,OAAO,EAAE;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/internals.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from '../diagnostics/catalog'\nimport type { Emit, EmitOptions, Trigger } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport type { RowModelStatus } from '../pipeline/types'\nimport type { Table, TableOptions, TableState } from './types'\n\n/**\n * What a table extension sees of its table (`TableInternals.use`). Extensions are the opt-in parts\n * of the store that a bare table does not bundle (changed 2026-10-13 for §9.3): the async request\n * machinery (`source/requests.ts`, for `searchFn` and the fetcher) and the server source\n * (`source/server.ts`, `withFetcher`). Not public API.\n */\nexport interface TableHost {\n readonly table: Table<unknown, TableState>\n /** `options.name`, else `table#n` (diagnostic context, config errors). */\n readonly name: string\n readonly emit: Emit\n /** The interceptor runtime of the table (ops run through it with the errorBoundary outermost). */\n readonly runtime: InterceptorRuntime\n /** The installed features in pipeline order. */\n readonly features: readonly AnyFeature[]\n options(): TableOptions<unknown>\n state(): TableState\n /** Context values of a new op ctx (table, run, trigger, phase, emit). */\n base(): ContextBase\n destroyed(): boolean\n /** Data given by the user or a fetcher has been applied once (`isInitialLoad` is `false`). */\n loaded(): boolean\n /** The table's status (§6.6), every source included. */\n status(): RowModelStatus\n /** A row model change not caused by state: recomputed and notified on the next flush. */\n touch(): void\n /** Notifies listeners now, unless an API call or batch is in progress (it notifies at its end). */\n notify(): void\n /** Runs `fn` under trigger `t` as one batch: listeners are notified once, when it returns. */\n batch<R>(t: Trigger, fn: () => R): R\n /** Ingests rows a fetcher returned through `data:ingest` (schema, data checks; marks loaded). */\n ingest(rows: readonly unknown[]): void\n /** Installs `interceptor` first in the chain (inside the errorBoundary), e.g. asyncControl. */\n intercept(interceptor: Interceptor<OpName>): void\n /** The table was created inside `createDeferred` and `startTable` has not run yet (§7.2). */\n deferring(): boolean\n /** Another extension of the same table (`TableInternals.use`). */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n}\n\n/** Hooks a table extension may implement. */\nexport interface TableExtension {\n /** Its contribution to `RowModel.status` (§6.6 precedence), or `undefined`. */\n status?(): RowModelStatus | undefined\n /** Before each notification round: start work that follows state (the fetcher, §7.1). */\n sync?(): void\n /** `startTable` (§7.2): starts deferred work; `true` when something was waiting. */\n start?(): boolean\n /** Every async source deferred since creation (`createDeferred`, `DK_SSR_ASYNC_DEFERRED`). */\n deferred?(): readonly ('fetcher' | 'searchFn')[]\n}\n\n/** Creates one table's extension; `use` calls it once per table. */\nexport type ExtensionFactory<E extends TableExtension> = (host: TableHost) => E\n\n/**\n * Internal seam for core modules that work through a table (search T19, fetcher T21, pagination,\n * SSR). Not exported from the package: `createTable` registers each table here.\n */\nexport interface TableInternals {\n /**\n * The installed features in pipeline order (`orderFeatures`: by stage, stable). Fixed at\n * creation; the pipeline runner (T09) runs their `rowModel`s in this order.\n */\n readonly features: readonly AnyFeature[]\n /**\n * The server row count (§6.5, §7.1): the last applied fetcher result's `rowCount` with a fetcher\n * (`undefined` until one applied), else `options.rowCount`.\n */\n rowCount(): number | undefined\n /** `options.fetcher` is set. */\n hasFetcher(): boolean\n /** The table's diagnostics hub emit (`emitTableDiagnostic`). */\n readonly emit: Emit\n /**\n * The ingested `options.initialData` while it is still the table's data (§7.2): the result of\n * the initial async work. `undefined` without it, or once other data was applied.\n */\n initialRows(): readonly unknown[] | undefined\n /** The table's extension made by `factory`, created on first use. */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n /** The table's extension made by `factory`, if it was created. */\n peek<E extends TableExtension>(factory: ExtensionFactory<E>): E | undefined\n /** The extensions created so far (SSR reads their deferred sources without importing them). */\n extensions(): Iterable<TableExtension>\n /** Starts the deferred work (`startTable`); a no-op when not deferred. */\n start(): void\n}\n\nconst registry = new WeakMap<object, TableInternals>()\n\nexport function setTableInternals(table: object, internals: TableInternals): void {\n registry.set(table, internals)\n}\n\nexport function getTableInternals(table: object): TableInternals {\n const i = registry.get(table)\n if (!i) throw new TypeError('getTableInternals: not a table made by createTable()')\n return i\n}\n\n/**\n * Reports a catalog diagnostic on `table`'s hub (its sinks, `onDiagnostic`, `getDiagnostics()`), for\n * bindings that detect problems outside the table, e.g. `@datakable/react`'s unstable-columns warning.\n * The code must be defined with `defineDiagnostic`.\n */\nexport function emitTableDiagnostic<C extends DiagnosticCode>(\n table: object,\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n): void {\n getTableInternals(table).emit(code, details, options)\n}\n\n/**\n * `createDeferred` (ssr.ts) nesting depth: a table created while it is > 0 defers its async work\n * until `startTable` (§7.2). Kept here so `createTable` does not import the SSR module.\n */\nexport const deferral = { depth: 0 }\n"],"mappings":";AAmGA,IAAM,WAAW,oBAAI,QAAgC;AAE9C,SAAS,kBAAkB,OAAe,WAAiC;AAChF,WAAS,IAAI,OAAO,SAAS;AAC/B;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,IAAI,SAAS,IAAI,KAAK;AAC5B,MAAI,CAAC,EAAG,OAAM,IAAI,UAAU,sDAAsD;AAClF,SAAO;AACT;AAOO,SAAS,oBACd,OACA,MACA,SACA,SACM;AACN,oBAAkB,KAAK,EAAE,KAAK,MAAM,SAAS,OAAO;AACtD;AAMO,IAAM,WAAW,EAAE,OAAO,EAAE;","names":[]}
1
+ {"version":3,"sources":["../../src/table/internals.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from '../diagnostics/catalog'\nimport type { Emit, EmitOptions, Trigger } from '../diagnostics/types'\nimport type { AnyFeature } from '../feature/types'\nimport type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport type { OpName } from '../intercept/operations'\nimport type { InterceptorRuntime } from '../intercept/runtime'\nimport type { RowModelStatus } from '../pipeline/types'\nimport type { Table, TableOptions, TableState } from './types'\n\n/**\n * What a table extension sees of its table (`TableInternals.use`). Extensions are the opt-in parts\n * of the store that a bare table does not bundle (changed 2026-10-13 for §9.3): the async request\n * machinery (`source/requests.ts`, for `searchFn` and the fetcher) and the server source\n * (`source/server.ts`, `withFetcher`). Not public API.\n */\nexport interface TableHost {\n readonly table: Table<unknown, TableState>\n /** `options.name`, else `table#n` (diagnostic context, config errors). */\n readonly name: string\n readonly emit: Emit\n /** The interceptor runtime of the table (ops run through it with the errorBoundary outermost). */\n readonly runtime: InterceptorRuntime\n /** The installed features in pipeline order. */\n readonly features: readonly AnyFeature[]\n options(): TableOptions<unknown>\n state(): TableState\n /** Context values of a new op ctx (table, run, trigger, phase, emit). */\n base(): ContextBase\n destroyed(): boolean\n /** Data given by the user or a fetcher has been applied once (`isInitialLoad` is `false`). */\n loaded(): boolean\n /** The table's status (§6.6), every source included. */\n status(): RowModelStatus\n /** A row model change not caused by state: recomputed and notified on the next flush. */\n touch(): void\n /** Notifies listeners now, unless an API call or batch is in progress (it notifies at its end). */\n notify(): void\n /** Runs `fn` under trigger `t` as one batch: listeners are notified once, when it returns. */\n batch<R>(t: Trigger, fn: () => R): R\n /** Ingests rows a fetcher returned through `data:ingest` (schema, data checks; marks loaded). */\n ingest(rows: readonly unknown[]): void\n /** Installs `interceptor` first in the chain (inside the errorBoundary), e.g. asyncControl. */\n intercept(interceptor: Interceptor<OpName>): void\n /** The table was created inside `createDeferred` and `startTable` has not run yet (§7.2). */\n deferring(): boolean\n /** Another extension of the same table (`TableInternals.use`). */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n}\n\n/** Hooks a table extension may implement. */\nexport interface TableExtension {\n /** Its contribution to `RowModel.status` (§6.6 precedence), or `undefined`. */\n status?(): RowModelStatus | undefined\n /** Before each notification round: start work that follows state (the fetcher, §7.1). */\n sync?(): void\n /** `startTable` (§7.2): starts deferred work; `true` when something was waiting. */\n start?(): boolean\n /** Every async source deferred since creation (`createDeferred`, `DK_SSR_ASYNC_DEFERRED`). */\n deferred?(): readonly ('fetcher' | 'searchFn')[]\n}\n\n/** Creates one table's extension; `use` calls it once per table. */\nexport type ExtensionFactory<E extends TableExtension> = (host: TableHost) => E\n\n/**\n * Internal seam for core modules that work through a table (search T19, fetcher T21, pagination,\n * SSR). Not exported from the package: `createTable` registers each table here.\n */\nexport interface TableInternals {\n /**\n * The installed features in pipeline order (`orderFeatures`: by stage, stable). Fixed at\n * creation; the pipeline runner (T09) runs their `rowModel`s in this order.\n */\n readonly features: readonly AnyFeature[]\n /** `options.name` as given (`undefined` without one). */\n readonly name: string | undefined\n /**\n * The server row count (§6.5, §7.1): the last applied fetcher result's `rowCount` with a fetcher\n * (`undefined` until one applied), else `options.rowCount`.\n */\n rowCount(): number | undefined\n /** `options.fetcher` is set. */\n hasFetcher(): boolean\n /** The table's diagnostics hub emit (`emitTableDiagnostic`). */\n readonly emit: Emit\n /**\n * The ingested `options.initialData` while it is still the table's data (§7.2): the result of\n * the initial async work. `undefined` without it, or once other data was applied.\n */\n initialRows(): readonly unknown[] | undefined\n /** The table's extension made by `factory`, created on first use. */\n use<E extends TableExtension>(factory: ExtensionFactory<E>): E\n /** The table's extension made by `factory`, if it was created. */\n peek<E extends TableExtension>(factory: ExtensionFactory<E>): E | undefined\n /** The extensions created so far (SSR reads their deferred sources without importing them). */\n extensions(): Iterable<TableExtension>\n /** Starts the deferred work (`startTable`); a no-op when not deferred. */\n start(): void\n}\n\nconst registry = new WeakMap<object, TableInternals>()\n\nexport function setTableInternals(table: object, internals: TableInternals): void {\n registry.set(table, internals)\n}\n\nexport function getTableInternals(table: object): TableInternals {\n const i = registry.get(table)\n if (!i) throw new TypeError('getTableInternals: not a table made by createTable()')\n return i\n}\n\n/**\n * Reports a catalog diagnostic on `table`'s hub (its sinks, `onDiagnostic`, `getDiagnostics()`), for\n * bindings that detect problems outside the table, e.g. `@datakable/react`'s unstable-columns warning.\n * The code must be defined with `defineDiagnostic`.\n */\nexport function emitTableDiagnostic<C extends DiagnosticCode>(\n table: object,\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n): void {\n getTableInternals(table).emit(code, details, options)\n}\n\n/**\n * `createDeferred` (ssr.ts) nesting depth: a table created while it is > 0 defers its async work\n * until `startTable` (§7.2). Kept here so `createTable` does not import the SSR module.\n */\nexport const deferral = { depth: 0 }\n"],"mappings":";AAqGA,IAAM,WAAW,oBAAI,QAAgC;AAE9C,SAAS,kBAAkB,OAAe,WAAiC;AAChF,WAAS,IAAI,OAAO,SAAS;AAC/B;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,IAAI,SAAS,IAAI,KAAK;AAC5B,MAAI,CAAC,EAAG,OAAM,IAAI,UAAU,sDAAsD;AAClF,SAAO;AACT;AAOO,SAAS,oBACd,OACA,MACA,SACA,SACM;AACN,oBAAkB,KAAK,EAAE,KAAK,MAAM,SAAS,OAAO;AACtD;AAMO,IAAM,WAAW,EAAE,OAAO,EAAE;","names":[]}
@@ -25,6 +25,7 @@ __export(ssr_codes_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(ssr_codes_exports);
27
27
  var import_catalog = require("../diagnostics/catalog.cjs");
28
+ var import_process_guard = require("../diagnostics/process-guard.cjs");
28
29
  var list = (s) => s.map((k) => `"${k}"`).join(", ");
29
30
  var ssrMessages = {
30
31
  DK_SSR_ASYNC_DEFERRED: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/ssr-codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_SSR_ASYNC_DEFERRED: {\n /** The async work that waits for the client: the fetcher, or the search feature's searchFn. */\n source: 'fetcher' | 'searchFn'\n }\n DK_HYDRATION_MISMATCH: {\n /**\n * What differs between the snapshot React hydrated with and the table's live one: state\n * slice names, then `rows` when the state is equal but the row model is not (data, columns).\n */\n slices: string[]\n }\n }\n}\n\nconst list = (s: readonly string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const ssrMessages: DiagnosticMessages<'DK_SSR_ASYNC_DEFERRED' | 'DK_HYDRATION_MISMATCH'> = {\n DK_SSR_ASYNC_DEFERRED: {\n problem: (d) =>\n d.source === 'fetcher'\n ? 'the fetcher did not run during the server render (async work is deferred to the client)'\n : 'searchFn did not run for the initial query during the server render (async work is ' +\n 'deferred to the client)',\n impact: (d) =>\n d.source === 'fetcher'\n ? \"the server HTML has status 'pending' and no fetched rows; the fetch starts after hydration\"\n : \"the server HTML has status 'pending' and the rows unsearched; the search runs after \" +\n 'hydration',\n fix: (d) =>\n d.source === 'fetcher'\n ? [\n 'fetch on the server and pass the result as initialData (with rowCount for server ' +\n 'pagination): the HTML then has the rows, and hydration does not re-fetch',\n 'or keep it: the client shows its loading state until the first fetch applies',\n ]\n : [\n 'search on the server and pass the result as initialData: it is used as the result of ' +\n 'the initial query, and hydration does not search again',\n 'or use client matching (no searchFn), which runs during the server render',\n ],\n },\n DK_HYDRATION_MISMATCH: {\n problem: (d) =>\n `the table changed while React hydrated it: ${list(d.slices)} differ from the server snapshot`,\n impact: () =>\n 'React hydrated with the server snapshot and re-renders with the live one at once: the server ' +\n 'HTML is replaced (a visible flash)',\n fix: (d) => [\n `give the server and the client the same initial ${list(d.slices)} (initialState / state / ` +\n 'data / initialData), e.g. parse them from the same URL',\n 'apply client-only changes (localStorage, window size, …) after mount, in useEffect, not ' +\n 'while rendering',\n ],\n },\n}\n\n/** SSR codes (§7.2, §8.3): kept by `createDeferred` (ssr.ts) and `@datakable/react`. */\nexport const ssrDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_SSR_ASYNC_DEFERRED',\n level: 'info',\n where: (d) =>\n d.source === 'fetcher'\n ? { kind: 'option', path: 'fetcher' }\n : { kind: 'feature', name: 'search' },\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_SSR_ASYNC_DEFERRED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HYDRATION_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'operation', op: 'hydrate', slot: d.slices[0] ?? 'rows' }),\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_HYDRATION_MISMATCH,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;AAkB1D,IAAM,OAAO,CAAC,MAAyB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAGhE,IAAM,cAAqF;AAAA,EAChG,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,YACT,4FACA;AAAA,IAEN,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,+FACA;AAAA,IAEN,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA;AAAA,MACE;AAAA,MAEA;AAAA,IACF;AAAA,EACR;AAAA,EACA,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,8CAA8C,KAAK,EAAE,MAAM,CAAC;AAAA,IAC9D,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV,mDAAmD,KAAK,EAAE,MAAM,CAAC;AAAA,MAEjE;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,iBAAiB;AAAA,MAC5B;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,YACT,EAAE,MAAM,UAAU,MAAM,UAAU,IAClC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,IAC1C;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,aAAa,IAAI,WAAW,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO;AAAA,IACjF;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/table/ssr-codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_SSR_ASYNC_DEFERRED: {\n /** The async work that waits for the client: the fetcher, or the search feature's searchFn. */\n source: 'fetcher' | 'searchFn'\n }\n DK_HYDRATION_MISMATCH: {\n /**\n * What differs between the snapshot React hydrated with and the table's live one: state\n * slice names, then `rows` when the state is equal but the row model is not (data, columns).\n */\n slices: string[]\n }\n }\n}\n\nconst list = (s: readonly string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const ssrMessages: DiagnosticMessages<'DK_SSR_ASYNC_DEFERRED' | 'DK_HYDRATION_MISMATCH'> = {\n DK_SSR_ASYNC_DEFERRED: {\n problem: (d) =>\n d.source === 'fetcher'\n ? 'the fetcher did not run during the server render (async work is deferred to the client)'\n : 'searchFn did not run for the initial query during the server render (async work is ' +\n 'deferred to the client)',\n impact: (d) =>\n d.source === 'fetcher'\n ? \"the server HTML has status 'pending' and no fetched rows; the fetch starts after hydration\"\n : \"the server HTML has status 'pending' and the rows unsearched; the search runs after \" +\n 'hydration',\n fix: (d) =>\n d.source === 'fetcher'\n ? [\n 'fetch on the server and pass the result as initialData (with rowCount for server ' +\n 'pagination): the HTML then has the rows, and hydration does not re-fetch',\n 'or keep it: the client shows its loading state until the first fetch applies',\n ]\n : [\n 'search on the server and pass the result as initialData: it is used as the result of ' +\n 'the initial query, and hydration does not search again',\n 'or use client matching (no searchFn), which runs during the server render',\n ],\n },\n DK_HYDRATION_MISMATCH: {\n problem: (d) =>\n `the table changed while React hydrated it: ${list(d.slices)} differ from the server snapshot`,\n impact: () =>\n 'React hydrated with the server snapshot and re-renders with the live one at once: the server ' +\n 'HTML is replaced (a visible flash)',\n fix: (d) => [\n `give the server and the client the same initial ${list(d.slices)} (initialState / state / ` +\n 'data / initialData), e.g. parse them from the same URL',\n 'apply client-only changes (localStorage, window size, …) after mount, in useEffect, not ' +\n 'while rendering',\n ],\n },\n}\n\n/** SSR codes (§7.2, §8.3): kept by `createDeferred` (ssr.ts) and `@datakable/react`. */\nexport const ssrDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_SSR_ASYNC_DEFERRED',\n level: 'info',\n where: (d) =>\n d.source === 'fetcher'\n ? { kind: 'option', path: 'fetcher' }\n : { kind: 'feature', name: 'search' },\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_SSR_ASYNC_DEFERRED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HYDRATION_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'operation', op: 'hydrate', slot: d.slices[0] ?? 'rows' }),\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_HYDRATION_MISMATCH,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;AAC1D,2BAAO;AAkBP,IAAM,OAAO,CAAC,MAAyB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAGhE,IAAM,cAAqF;AAAA,EAChG,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,YACT,4FACA;AAAA,IAEN,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,+FACA;AAAA,IAEN,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA;AAAA,MACE;AAAA,MAEA;AAAA,IACF;AAAA,EACR;AAAA,EACA,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,8CAA8C,KAAK,EAAE,MAAM,CAAC;AAAA,IAC9D,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV,mDAAmD,KAAK,EAAE,MAAM,CAAC;AAAA,MAEjE;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,iBAAiB;AAAA,MAC5B;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,YACT,EAAE,MAAM,UAAU,MAAM,UAAU,IAClC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,IAC1C;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,aAAa,IAAI,WAAW,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO;AAAA,IACjF;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AACF;","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/table/ssr-codes.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 ssrMessages = {
5
6
  DK_SSR_ASYNC_DEFERRED: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/ssr-codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_SSR_ASYNC_DEFERRED: {\n /** The async work that waits for the client: the fetcher, or the search feature's searchFn. */\n source: 'fetcher' | 'searchFn'\n }\n DK_HYDRATION_MISMATCH: {\n /**\n * What differs between the snapshot React hydrated with and the table's live one: state\n * slice names, then `rows` when the state is equal but the row model is not (data, columns).\n */\n slices: string[]\n }\n }\n}\n\nconst list = (s: readonly string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const ssrMessages: DiagnosticMessages<'DK_SSR_ASYNC_DEFERRED' | 'DK_HYDRATION_MISMATCH'> = {\n DK_SSR_ASYNC_DEFERRED: {\n problem: (d) =>\n d.source === 'fetcher'\n ? 'the fetcher did not run during the server render (async work is deferred to the client)'\n : 'searchFn did not run for the initial query during the server render (async work is ' +\n 'deferred to the client)',\n impact: (d) =>\n d.source === 'fetcher'\n ? \"the server HTML has status 'pending' and no fetched rows; the fetch starts after hydration\"\n : \"the server HTML has status 'pending' and the rows unsearched; the search runs after \" +\n 'hydration',\n fix: (d) =>\n d.source === 'fetcher'\n ? [\n 'fetch on the server and pass the result as initialData (with rowCount for server ' +\n 'pagination): the HTML then has the rows, and hydration does not re-fetch',\n 'or keep it: the client shows its loading state until the first fetch applies',\n ]\n : [\n 'search on the server and pass the result as initialData: it is used as the result of ' +\n 'the initial query, and hydration does not search again',\n 'or use client matching (no searchFn), which runs during the server render',\n ],\n },\n DK_HYDRATION_MISMATCH: {\n problem: (d) =>\n `the table changed while React hydrated it: ${list(d.slices)} differ from the server snapshot`,\n impact: () =>\n 'React hydrated with the server snapshot and re-renders with the live one at once: the server ' +\n 'HTML is replaced (a visible flash)',\n fix: (d) => [\n `give the server and the client the same initial ${list(d.slices)} (initialState / state / ` +\n 'data / initialData), e.g. parse them from the same URL',\n 'apply client-only changes (localStorage, window size, …) after mount, in useEffect, not ' +\n 'while rendering',\n ],\n },\n}\n\n/** SSR codes (§7.2, §8.3): kept by `createDeferred` (ssr.ts) and `@datakable/react`. */\nexport const ssrDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_SSR_ASYNC_DEFERRED',\n level: 'info',\n where: (d) =>\n d.source === 'fetcher'\n ? { kind: 'option', path: 'fetcher' }\n : { kind: 'feature', name: 'search' },\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_SSR_ASYNC_DEFERRED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HYDRATION_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'operation', op: 'hydrate', slot: d.slices[0] ?? 'rows' }),\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_HYDRATION_MISMATCH,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;AAkB1D,IAAM,OAAO,CAAC,MAAyB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAGhE,IAAM,cAAqF;AAAA,EAChG,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,YACT,4FACA;AAAA,IAEN,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,+FACA;AAAA,IAEN,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA;AAAA,MACE;AAAA,MAEA;AAAA,IACF;AAAA,EACR;AAAA,EACA,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,8CAA8C,KAAK,EAAE,MAAM,CAAC;AAAA,IAC9D,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV,mDAAmD,KAAK,EAAE,MAAM,CAAC;AAAA,MAEjE;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,YACT,EAAE,MAAM,UAAU,MAAM,UAAU,IAClC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,IAC1C;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,aAAa,IAAI,WAAW,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO;AAAA,IACjF;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/table/ssr-codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_SSR_ASYNC_DEFERRED: {\n /** The async work that waits for the client: the fetcher, or the search feature's searchFn. */\n source: 'fetcher' | 'searchFn'\n }\n DK_HYDRATION_MISMATCH: {\n /**\n * What differs between the snapshot React hydrated with and the table's live one: state\n * slice names, then `rows` when the state is equal but the row model is not (data, columns).\n */\n slices: string[]\n }\n }\n}\n\nconst list = (s: readonly string[]) => s.map((k) => `\"${k}\"`).join(', ')\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const ssrMessages: DiagnosticMessages<'DK_SSR_ASYNC_DEFERRED' | 'DK_HYDRATION_MISMATCH'> = {\n DK_SSR_ASYNC_DEFERRED: {\n problem: (d) =>\n d.source === 'fetcher'\n ? 'the fetcher did not run during the server render (async work is deferred to the client)'\n : 'searchFn did not run for the initial query during the server render (async work is ' +\n 'deferred to the client)',\n impact: (d) =>\n d.source === 'fetcher'\n ? \"the server HTML has status 'pending' and no fetched rows; the fetch starts after hydration\"\n : \"the server HTML has status 'pending' and the rows unsearched; the search runs after \" +\n 'hydration',\n fix: (d) =>\n d.source === 'fetcher'\n ? [\n 'fetch on the server and pass the result as initialData (with rowCount for server ' +\n 'pagination): the HTML then has the rows, and hydration does not re-fetch',\n 'or keep it: the client shows its loading state until the first fetch applies',\n ]\n : [\n 'search on the server and pass the result as initialData: it is used as the result of ' +\n 'the initial query, and hydration does not search again',\n 'or use client matching (no searchFn), which runs during the server render',\n ],\n },\n DK_HYDRATION_MISMATCH: {\n problem: (d) =>\n `the table changed while React hydrated it: ${list(d.slices)} differ from the server snapshot`,\n impact: () =>\n 'React hydrated with the server snapshot and re-renders with the live one at once: the server ' +\n 'HTML is replaced (a visible flash)',\n fix: (d) => [\n `give the server and the client the same initial ${list(d.slices)} (initialState / state / ` +\n 'data / initialData), e.g. parse them from the same URL',\n 'apply client-only changes (localStorage, window size, …) after mount, in useEffect, not ' +\n 'while rendering',\n ],\n },\n}\n\n/** SSR codes (§7.2, §8.3): kept by `createDeferred` (ssr.ts) and `@datakable/react`. */\nexport const ssrDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_SSR_ASYNC_DEFERRED',\n level: 'info',\n where: (d) =>\n d.source === 'fetcher'\n ? { kind: 'option', path: 'fetcher' }\n : { kind: 'feature', name: 'search' },\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_SSR_ASYNC_DEFERRED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HYDRATION_MISMATCH',\n level: 'warn',\n where: (d) => ({ kind: 'operation', op: 'hydrate', slot: d.slices[0] ?? 'rows' }),\n },\n (process.env.NODE_ENV !== \"production\") && ssrMessages.DK_HYDRATION_MISMATCH,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;AAC1D,OAAO;AAkBP,IAAM,OAAO,CAAC,MAAyB,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAGhE,IAAM,cAAqF;AAAA,EAChG,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,EAAE,WAAW,YACT,4FACA;AAAA,IAEN,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,+FACA;AAAA,IAEN,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA;AAAA,MACE;AAAA,MAEA;AAAA,IACF;AAAA,EACR;AAAA,EACA,uBAAuB;AAAA,IACrB,SAAS,CAAC,MACR,8CAA8C,KAAK,EAAE,MAAM,CAAC;AAAA,IAC9D,QAAQ,MACN;AAAA,IAEF,KAAK,CAAC,MAAM;AAAA,MACV,mDAAmD,KAAK,EAAE,MAAM,CAAC;AAAA,MAEjE;AAAA,IAEF;AAAA,EACF;AACF;AAGO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,MACN,EAAE,WAAW,YACT,EAAE,MAAM,UAAU,MAAM,UAAU,IAClC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,IAC1C;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,aAAa,IAAI,WAAW,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO;AAAA,IACjF;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,YAAY;AAAA,EACzD;AACF;","names":[]}
@@ -25,6 +25,8 @@ __export(ssr_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(ssr_exports);
27
27
  var import_catalog = require("../diagnostics/catalog.cjs");
28
+ var import_process_guard = require("../diagnostics/process-guard.cjs");
29
+ var import_registry = require("../versioning/registry.cjs");
28
30
  var import_internals = require("./internals.cjs");
29
31
  var import_ssr_codes = require("./ssr-codes.cjs");
30
32
  (0, import_catalog.keepDiagnostics)(import_ssr_codes.ssrDiagnostics);
@@ -40,7 +42,8 @@ function createDeferred(create) {
40
42
  const internals = (0, import_internals.getTableInternals)(table);
41
43
  for (const e of internals.extensions()) {
42
44
  for (const source of e.deferred?.() ?? []) {
43
- internals.emit("DK_SSR_ASYNC_DEFERRED", { source }, { phase: "ssr" });
45
+ const quiet = process.env.NODE_ENV !== "production" && !(0, import_registry.firstInSession)(`DK_SSR_ASYNC_DEFERRED:${internals.name}:${source}`);
46
+ internals.emit("DK_SSR_ASYNC_DEFERRED", { source }, { phase: "ssr", quiet });
44
47
  }
45
48
  }
46
49
  }
@@ -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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAgC;AAChC,uBAA4C;AAC5C,uBAA+B;AAAA,IAE/B,gCAAgB,+BAAc;AAYvB,SAAS,eAAiC,QAAoB;AACnE,4BAAS;AACT,MAAI;AACJ,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,UAAE;AACA,8BAAS;AAAA,EACX;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,gBAAY,oCAAkB,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,0CAAkB,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAgC;AAChC,2BAAO;AACP,sBAA+B;AAC/B,uBAA4C;AAC5C,uBAA+B;AAAA,IAE/B,gCAAgB,+BAAc;AAcvB,SAAS,eAAiC,QAAoB;AACnE,4BAAS;AACT,MAAI;AACJ,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,UAAE;AACA,8BAAS;AAAA,EACX;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,gBAAY,oCAAkB,KAAK;AAEzC,eAAW,KAAK,UAAU,WAAW,GAAG;AACtC,iBAAW,UAAU,EAAE,WAAW,KAAK,CAAC,GAAG;AAEzC,cAAM,QAAS,QAAQ,IAAI,aAAa,gBAAiB,KAAC,gCAAe,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,0CAAkB,KAAK,EAAE,MAAM;AACjC;","names":[]}
package/dist/table/ssr.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/table/ssr.ts
2
2
  import { keepDiagnostics } from "../diagnostics/catalog.js";
3
+ import "../diagnostics/process-guard.js";
4
+ import { firstInSession } from "../versioning/registry.js";
3
5
  import { deferral, getTableInternals } from "./internals.js";
4
6
  import { ssrDiagnostics } from "./ssr-codes.js";
5
7
  keepDiagnostics(ssrDiagnostics);
@@ -15,7 +17,8 @@ function createDeferred(create) {
15
17
  const internals = getTableInternals(table);
16
18
  for (const e of internals.extensions()) {
17
19
  for (const source of e.deferred?.() ?? []) {
18
- internals.emit("DK_SSR_ASYNC_DEFERRED", { source }, { phase: "ssr" });
20
+ const quiet = process.env.NODE_ENV !== "production" && !firstInSession(`DK_SSR_ASYNC_DEFERRED:${internals.name}:${source}`);
21
+ internals.emit("DK_SSR_ASYNC_DEFERRED", { source }, { phase: "ssr", quiet });
19
22
  }
20
23
  }
21
24
  }