@datakable/core 1.0.1 → 2.0.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 +5 -3
  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,5 +1,5 @@
1
- export { U as columnFiltersDiagnostics } from './codes-OsjAR3Wz.js';
2
- import { G as FilterFor, a1 as Path, P as LooseRow, a2 as PathValue, e as FeatureTypes, F as Feature } from './explain-Dn99IQt0.js';
1
+ export { U as columnFiltersDiagnostics } from './codes-B9bCK73-.js';
2
+ import { G as FilterFor, a1 as Path, P as LooseRow, a2 as PathValue, e as FeatureTypes, F as Feature } from './explain-i0OATi_W.js';
3
3
  import { a as DataMode } from './mode-DFV8ryXo.js';
4
4
 
5
5
  /** The value a filter compares against: the element type for array-valued cells (§6.1). */
@@ -34,6 +34,8 @@ module.exports = __toCommonJS(build_exports);
34
34
  var import_version = require("../version.cjs");
35
35
  var import_catalog = require("./catalog.cjs");
36
36
  var import_codes = require("./codes.cjs");
37
+ var import_process_guard = require("./process-guard.cjs");
38
+ var import_prose = require("./prose.cjs");
37
39
  var import_redact = require("./redact.cjs");
38
40
  var import_safe_json = require("./safe-json.cjs");
39
41
  var PACKAGE = `@datakable/core@${import_version.VERSION}`;
@@ -93,11 +95,12 @@ function buildDiagnostic(code, details, input = {}) {
93
95
  def = INTERNAL;
94
96
  d = {
95
97
  op: "diagnostic:emit",
96
- problem: `unknown diagnostic code "${code}": define it with defineDiagnostic() first`
98
+ problem: `unknown-code|${code}`
97
99
  };
98
100
  }
99
101
  const b = def;
100
102
  const dev = input.dev ?? process.env.NODE_ENV !== "production";
103
+ if (dev || (0, import_catalog.hasMessages)()) d = (0, import_prose.expandDetails)(d, dev);
101
104
  const r = input.redaction ?? (0, import_redact.resolveRedaction)(void 0, dev);
102
105
  const where = (0, import_redact.redactLocation)(b.where(d), r);
103
106
  d = (0, import_redact.redactDetails)(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/build.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport {\n type DiagnosticCode,\n type DiagnosticDefinition,\n type DiagnosticDetails,\n type DiagnosticText,\n getDiagnosticDefinition,\n getDiagnosticText,\n hasMessages,\n} from './catalog'\nimport { coreDiagnostics } from './codes'\nimport {\n type RedactField,\n type Redactor,\n redactDetails,\n redactLocation,\n resolveRedaction,\n} from './redact'\nimport { toSafeJson } from './safe-json'\nimport type {\n Diagnostic,\n DiagnosticContext,\n DiagnosticLevel,\n DiagnosticStateSummary,\n Location,\n Phase,\n Trigger,\n} from './types'\n\nexport const PACKAGE = `@datakable/core@${VERSION}`\nconst DOCS = 'https://github.com/Ink01101011/datakable/blob/main/docs/errors.md'\n\n// Referencing the core entries keeps them in the bundle (`sideEffects: false`).\nconst INTERNAL = coreDiagnostics.find((d) => d.code === 'DK_INTERNAL') as DiagnosticDefinition\n\n/** Everything the enrich step adds; all optional so a diagnostic can be built outside a table. */\nexport interface BuildInput {\n table?: string\n createdAt?: string\n trigger?: Trigger\n run?: number\n phase?: Phase\n mode?: Record<string, 'client' | 'server'>\n state?: DiagnosticStateSummary\n /** Timestamp for `occurrences` (epoch ms). Default `wallClock()`. */\n now?: number\n cause?: unknown\n causedBy?: string\n /** Build the problem / impact / fix text. Default: development builds only (§8.2); production\n * builds also build it once `@datakable/core/messages` is installed. */\n dev?: boolean\n /** Redaction policy (§8.2). Default: `'keys'` when `dev`, else `'safe'`. */\n redaction?: Redactor\n}\n\n/**\n * Epoch ms read through `performance`, never `Date` (#7): Next 16 `cacheComponents` aborts a\n * prerender that reads `Date.now()` and reserves `performance` for introspection like this.\n */\nexport const wallClock = (): number =>\n typeof performance === 'object' && performance?.timeOrigin\n ? Math.floor(performance.timeOrigin + performance.now())\n : Date.now()\n\nexport const defaultPhase = (): Phase => (typeof document === 'undefined' ? 'ssr' : 'client')\n\nexport function formatWhere(where: Location): string {\n switch (where.kind) {\n case 'column':\n return `columns[${where.index}] (id \"${where.id}\")`\n case 'feature':\n return `feature \"${where.name}\"`\n case 'option':\n return `options.${where.path}`\n case 'row':\n return where.key === undefined\n ? `row #${where.index}`\n : `row #${where.index} (key ${where.key})`\n case 'interceptor':\n return `interceptor \"${where.name}\" on ${where.op}`\n case 'operation':\n return where.slot ? `operation ${where.op} at ${where.slot}` : `operation ${where.op}`\n }\n}\n\nclass DiagnosticRecord implements Diagnostic {\n constructor(\n public code: DiagnosticCode,\n public level: DiagnosticLevel,\n public problem: string,\n public where: Location,\n public impact: string,\n public fix: string[],\n public docs: string,\n public details: Record<string, unknown>,\n public context: DiagnosticContext,\n public fingerprint: string,\n ) {}\n\n toJSON(): object {\n const { cause: _cause, ...context } = this.context\n return {\n code: this.code,\n level: this.level,\n problem: this.problem,\n where: toSafeJson(this.where),\n impact: this.impact,\n fix: this.fix,\n docs: this.docs,\n details: toSafeJson(this.details),\n context: toSafeJson(context),\n fingerprint: this.fingerprint,\n }\n }\n}\n\n/**\n * Builds a diagnostic from a catalog code + details (the `enrich` step). Never throws for an unknown\n * code: that becomes `DK_INTERNAL`, so a typo in a plugin cannot take the logger down.\n */\nexport function buildDiagnostic<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input: BuildInput = {},\n): Diagnostic {\n let def = getDiagnosticDefinition(code)\n let d = details as Record<string, unknown>\n if (!def) {\n def = INTERNAL\n d = {\n op: 'diagnostic:emit',\n problem: `unknown diagnostic code \"${code}\": define it with defineDiagnostic() first`,\n }\n }\n const b = def as unknown as DiagnosticDefinition<'DK_INTERNAL'>\n const dev = input.dev ?? (process.env.NODE_ENV !== \"production\")\n // Redact first: `where`, the text builders, the fingerprint and the docs URL only ever see\n // redacted data, so no consumer downstream (interceptors, sinks, strict errors) can leak it.\n const r = input.redaction ?? resolveRedaction(undefined, dev)\n const where = redactLocation(b.where(d as DiagnosticDetails<'DK_INTERNAL'>), r)\n d = redactDetails(\n d,\n def.redact as Readonly<Record<string, RedactField | undefined>> | undefined,\n where,\n r,\n )\n const dd = d as DiagnosticDetails<'DK_INTERNAL'>\n // Text: development builds, or production with `@datakable/core/messages` installed (§8.2).\n const t =\n dev || hasMessages()\n ? (getDiagnosticText(def.code) as DiagnosticText<'DK_INTERNAL'> | undefined)\n : undefined\n const whereText = formatWhere(where)\n const now = input.now ?? wallClock()\n const context: DiagnosticContext = {\n table: input.table ?? '(global)',\n package: PACKAGE,\n trigger: input.trigger ?? 'init',\n run: input.run ?? 0,\n phase: input.phase ?? defaultPhase(),\n mode: input.mode ?? {},\n state: input.state ?? { searchLength: 0, activeFilters: [], sort: [], page: 0 },\n occurrences: { count: 1, first: now, last: now },\n }\n if (input.createdAt !== undefined) context.createdAt = input.createdAt\n if (input.causedBy !== undefined) context.causedBy = input.causedBy\n if (input.cause !== undefined) context.cause = input.cause\n let docs = def.docs ?? `${DOCS}#${def.code.toLowerCase()}`\n if (!dev) {\n const hash = docs.indexOf('#')\n const params = `?v=${VERSION}&where=${encodeURIComponent(whereText)}`\n docs = hash < 0 ? docs + params : docs.slice(0, hash) + params + docs.slice(hash)\n }\n return new DiagnosticRecord(\n def.code,\n def.level,\n t ? t.problem(dd) : '',\n where,\n t ? t.impact(dd) : '',\n t ? t.fix(dd) : [],\n docs,\n d,\n context,\n `${def.code}@${whereText}`,\n )\n}\n\n/** Thrown under `strict: true` for error-level diagnostics, and for core invariants (§8.2). */\nexport class DatakableError extends Error {\n override name = 'DatakableError'\n readonly code: DiagnosticCode\n readonly level: DiagnosticLevel\n readonly problem: string\n readonly where: Location\n readonly impact: string\n readonly fix: string[]\n readonly docs: string\n readonly diagnostic: Diagnostic\n\n constructor(d: Diagnostic) {\n const lines = [`${d.code}: ${d.problem || '(see docs)'}`, ` where ${formatWhere(d.where)}`]\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n super(lines.join('\\n'), d.context.cause === undefined ? undefined : { cause: d.context.cause })\n this.code = d.code\n this.level = d.level\n this.problem = d.problem\n this.where = d.where\n this.impact = d.impact\n this.fix = d.fix\n this.docs = d.docs\n this.diagnostic = d\n }\n}\n\n/** Deterministic programmer errors thrown at creation (`error (config)` codes, §8.6). */\nexport class DatakableConfigError extends DatakableError {\n override name = 'DatakableConfigError'\n}\n\nexport function throwConfigError<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input?: BuildInput,\n): never {\n throw new DatakableConfigError(buildDiagnostic(code, details, input))\n}\n\n/** A core invariant broke (unregistered op, boundary installed twice): `DK_INTERNAL`. */\nexport function internalError(op: string, problem: string): DatakableError {\n return new DatakableError(buildDiagnostic('DK_INTERNAL', { op, problem }))\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/build.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport {\n type DiagnosticCode,\n type DiagnosticDefinition,\n type DiagnosticDetails,\n type DiagnosticText,\n getDiagnosticDefinition,\n getDiagnosticText,\n hasMessages,\n} from './catalog'\nimport { coreDiagnostics } from './codes'\nimport './process-guard'\nimport { expandDetails } from './prose'\nimport {\n type RedactField,\n type Redactor,\n redactDetails,\n redactLocation,\n resolveRedaction,\n} from './redact'\nimport { toSafeJson } from './safe-json'\nimport type {\n Diagnostic,\n DiagnosticContext,\n DiagnosticLevel,\n DiagnosticStateSummary,\n Location,\n Phase,\n Trigger,\n} from './types'\n\nexport const PACKAGE = `@datakable/core@${VERSION}`\nconst DOCS = 'https://github.com/Ink01101011/datakable/blob/main/docs/errors.md'\n\n// Referencing the core entries keeps them in the bundle (`sideEffects: false`).\nconst INTERNAL = coreDiagnostics.find((d) => d.code === 'DK_INTERNAL') as DiagnosticDefinition\n\n/** Everything the enrich step adds; all optional so a diagnostic can be built outside a table. */\nexport interface BuildInput {\n table?: string\n createdAt?: string\n trigger?: Trigger\n run?: number\n phase?: Phase\n mode?: Record<string, 'client' | 'server'>\n state?: DiagnosticStateSummary\n /** Timestamp for `occurrences` (epoch ms). Default `wallClock()`. */\n now?: number\n cause?: unknown\n causedBy?: string\n /** Build the problem / impact / fix text. Default: development builds only (§8.2); production\n * builds also build it once `@datakable/core/messages` is installed. */\n dev?: boolean\n /** Redaction policy (§8.2). Default: `'keys'` when `dev`, else `'safe'`. */\n redaction?: Redactor\n}\n\n/**\n * Epoch ms read through `performance`, never `Date` (#7): Next 16 `cacheComponents` aborts a\n * prerender that reads `Date.now()` and reserves `performance` for introspection like this.\n */\nexport const wallClock = (): number =>\n typeof performance === 'object' && performance?.timeOrigin\n ? Math.floor(performance.timeOrigin + performance.now())\n : Date.now()\n\nexport const defaultPhase = (): Phase => (typeof document === 'undefined' ? 'ssr' : 'client')\n\nexport function formatWhere(where: Location): string {\n switch (where.kind) {\n case 'column':\n return `columns[${where.index}] (id \"${where.id}\")`\n case 'feature':\n return `feature \"${where.name}\"`\n case 'option':\n return `options.${where.path}`\n case 'row':\n return where.key === undefined\n ? `row #${where.index}`\n : `row #${where.index} (key ${where.key})`\n case 'interceptor':\n return `interceptor \"${where.name}\" on ${where.op}`\n case 'operation':\n return where.slot ? `operation ${where.op} at ${where.slot}` : `operation ${where.op}`\n }\n}\n\nclass DiagnosticRecord implements Diagnostic {\n constructor(\n public code: DiagnosticCode,\n public level: DiagnosticLevel,\n public problem: string,\n public where: Location,\n public impact: string,\n public fix: string[],\n public docs: string,\n public details: Record<string, unknown>,\n public context: DiagnosticContext,\n public fingerprint: string,\n ) {}\n\n toJSON(): object {\n const { cause: _cause, ...context } = this.context\n return {\n code: this.code,\n level: this.level,\n problem: this.problem,\n where: toSafeJson(this.where),\n impact: this.impact,\n fix: this.fix,\n docs: this.docs,\n details: toSafeJson(this.details),\n context: toSafeJson(context),\n fingerprint: this.fingerprint,\n }\n }\n}\n\n/**\n * Builds a diagnostic from a catalog code + details (the `enrich` step). Never throws for an unknown\n * code: that becomes `DK_INTERNAL`, so a typo in a plugin cannot take the logger down.\n */\nexport function buildDiagnostic<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input: BuildInput = {},\n): Diagnostic {\n let def = getDiagnosticDefinition(code)\n let d = details as Record<string, unknown>\n if (!def) {\n def = INTERNAL\n d = {\n op: 'diagnostic:emit',\n problem: `unknown-code|${code}`,\n }\n }\n const b = def as unknown as DiagnosticDefinition<'DK_INTERNAL'>\n const dev = input.dev ?? (process.env.NODE_ENV !== \"production\")\n // Detail prose keys (`fallback: 'cells-undefined'`) read as sentences wherever text is built.\n if (dev || hasMessages()) d = expandDetails(d, dev)\n // Redact first: `where`, the text builders, the fingerprint and the docs URL only ever see\n // redacted data, so no consumer downstream (interceptors, sinks, strict errors) can leak it.\n const r = input.redaction ?? resolveRedaction(undefined, dev)\n const where = redactLocation(b.where(d as DiagnosticDetails<'DK_INTERNAL'>), r)\n d = redactDetails(\n d,\n def.redact as Readonly<Record<string, RedactField | undefined>> | undefined,\n where,\n r,\n )\n const dd = d as DiagnosticDetails<'DK_INTERNAL'>\n // Text: development builds, or production with `@datakable/core/messages` installed (§8.2).\n const t =\n dev || hasMessages()\n ? (getDiagnosticText(def.code) as DiagnosticText<'DK_INTERNAL'> | undefined)\n : undefined\n const whereText = formatWhere(where)\n const now = input.now ?? wallClock()\n const context: DiagnosticContext = {\n table: input.table ?? '(global)',\n package: PACKAGE,\n trigger: input.trigger ?? 'init',\n run: input.run ?? 0,\n phase: input.phase ?? defaultPhase(),\n mode: input.mode ?? {},\n state: input.state ?? { searchLength: 0, activeFilters: [], sort: [], page: 0 },\n occurrences: { count: 1, first: now, last: now },\n }\n if (input.createdAt !== undefined) context.createdAt = input.createdAt\n if (input.causedBy !== undefined) context.causedBy = input.causedBy\n if (input.cause !== undefined) context.cause = input.cause\n let docs = def.docs ?? `${DOCS}#${def.code.toLowerCase()}`\n if (!dev) {\n const hash = docs.indexOf('#')\n const params = `?v=${VERSION}&where=${encodeURIComponent(whereText)}`\n docs = hash < 0 ? docs + params : docs.slice(0, hash) + params + docs.slice(hash)\n }\n return new DiagnosticRecord(\n def.code,\n def.level,\n t ? t.problem(dd) : '',\n where,\n t ? t.impact(dd) : '',\n t ? t.fix(dd) : [],\n docs,\n d,\n context,\n `${def.code}@${whereText}`,\n )\n}\n\n/** Thrown under `strict: true` for error-level diagnostics, and for core invariants (§8.2). */\nexport class DatakableError extends Error {\n override name = 'DatakableError'\n readonly code: DiagnosticCode\n readonly level: DiagnosticLevel\n readonly problem: string\n readonly where: Location\n readonly impact: string\n readonly fix: string[]\n readonly docs: string\n readonly diagnostic: Diagnostic\n\n constructor(d: Diagnostic) {\n const lines = [`${d.code}: ${d.problem || '(see docs)'}`, ` where ${formatWhere(d.where)}`]\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n super(lines.join('\\n'), d.context.cause === undefined ? undefined : { cause: d.context.cause })\n this.code = d.code\n this.level = d.level\n this.problem = d.problem\n this.where = d.where\n this.impact = d.impact\n this.fix = d.fix\n this.docs = d.docs\n this.diagnostic = d\n }\n}\n\n/** Deterministic programmer errors thrown at creation (`error (config)` codes, §8.6). */\nexport class DatakableConfigError extends DatakableError {\n override name = 'DatakableConfigError'\n}\n\nexport function throwConfigError<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input?: BuildInput,\n): never {\n throw new DatakableConfigError(buildDiagnostic(code, details, input))\n}\n\n/** A core invariant broke (unregistered op, boundary installed twice): `DK_INTERNAL`. */\nexport function internalError(op: string, problem: string): DatakableError {\n return new DatakableError(buildDiagnostic('DK_INTERNAL', { op, problem }))\n}\n"],"mappings":"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","names":[]}
@@ -6,6 +6,8 @@ import {
6
6
  hasMessages
7
7
  } from "./catalog.js";
8
8
  import { coreDiagnostics } from "./codes.js";
9
+ import "./process-guard.js";
10
+ import { expandDetails } from "./prose.js";
9
11
  import {
10
12
  redactDetails,
11
13
  redactLocation,
@@ -69,11 +71,12 @@ function buildDiagnostic(code, details, input = {}) {
69
71
  def = INTERNAL;
70
72
  d = {
71
73
  op: "diagnostic:emit",
72
- problem: `unknown diagnostic code "${code}": define it with defineDiagnostic() first`
74
+ problem: `unknown-code|${code}`
73
75
  };
74
76
  }
75
77
  const b = def;
76
78
  const dev = input.dev ?? process.env.NODE_ENV !== "production";
79
+ if (dev || hasMessages()) d = expandDetails(d, dev);
77
80
  const r = input.redaction ?? resolveRedaction(void 0, dev);
78
81
  const where = redactLocation(b.where(d), r);
79
82
  d = redactDetails(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/build.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport {\n type DiagnosticCode,\n type DiagnosticDefinition,\n type DiagnosticDetails,\n type DiagnosticText,\n getDiagnosticDefinition,\n getDiagnosticText,\n hasMessages,\n} from './catalog'\nimport { coreDiagnostics } from './codes'\nimport {\n type RedactField,\n type Redactor,\n redactDetails,\n redactLocation,\n resolveRedaction,\n} from './redact'\nimport { toSafeJson } from './safe-json'\nimport type {\n Diagnostic,\n DiagnosticContext,\n DiagnosticLevel,\n DiagnosticStateSummary,\n Location,\n Phase,\n Trigger,\n} from './types'\n\nexport const PACKAGE = `@datakable/core@${VERSION}`\nconst DOCS = 'https://github.com/Ink01101011/datakable/blob/main/docs/errors.md'\n\n// Referencing the core entries keeps them in the bundle (`sideEffects: false`).\nconst INTERNAL = coreDiagnostics.find((d) => d.code === 'DK_INTERNAL') as DiagnosticDefinition\n\n/** Everything the enrich step adds; all optional so a diagnostic can be built outside a table. */\nexport interface BuildInput {\n table?: string\n createdAt?: string\n trigger?: Trigger\n run?: number\n phase?: Phase\n mode?: Record<string, 'client' | 'server'>\n state?: DiagnosticStateSummary\n /** Timestamp for `occurrences` (epoch ms). Default `wallClock()`. */\n now?: number\n cause?: unknown\n causedBy?: string\n /** Build the problem / impact / fix text. Default: development builds only (§8.2); production\n * builds also build it once `@datakable/core/messages` is installed. */\n dev?: boolean\n /** Redaction policy (§8.2). Default: `'keys'` when `dev`, else `'safe'`. */\n redaction?: Redactor\n}\n\n/**\n * Epoch ms read through `performance`, never `Date` (#7): Next 16 `cacheComponents` aborts a\n * prerender that reads `Date.now()` and reserves `performance` for introspection like this.\n */\nexport const wallClock = (): number =>\n typeof performance === 'object' && performance?.timeOrigin\n ? Math.floor(performance.timeOrigin + performance.now())\n : Date.now()\n\nexport const defaultPhase = (): Phase => (typeof document === 'undefined' ? 'ssr' : 'client')\n\nexport function formatWhere(where: Location): string {\n switch (where.kind) {\n case 'column':\n return `columns[${where.index}] (id \"${where.id}\")`\n case 'feature':\n return `feature \"${where.name}\"`\n case 'option':\n return `options.${where.path}`\n case 'row':\n return where.key === undefined\n ? `row #${where.index}`\n : `row #${where.index} (key ${where.key})`\n case 'interceptor':\n return `interceptor \"${where.name}\" on ${where.op}`\n case 'operation':\n return where.slot ? `operation ${where.op} at ${where.slot}` : `operation ${where.op}`\n }\n}\n\nclass DiagnosticRecord implements Diagnostic {\n constructor(\n public code: DiagnosticCode,\n public level: DiagnosticLevel,\n public problem: string,\n public where: Location,\n public impact: string,\n public fix: string[],\n public docs: string,\n public details: Record<string, unknown>,\n public context: DiagnosticContext,\n public fingerprint: string,\n ) {}\n\n toJSON(): object {\n const { cause: _cause, ...context } = this.context\n return {\n code: this.code,\n level: this.level,\n problem: this.problem,\n where: toSafeJson(this.where),\n impact: this.impact,\n fix: this.fix,\n docs: this.docs,\n details: toSafeJson(this.details),\n context: toSafeJson(context),\n fingerprint: this.fingerprint,\n }\n }\n}\n\n/**\n * Builds a diagnostic from a catalog code + details (the `enrich` step). Never throws for an unknown\n * code: that becomes `DK_INTERNAL`, so a typo in a plugin cannot take the logger down.\n */\nexport function buildDiagnostic<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input: BuildInput = {},\n): Diagnostic {\n let def = getDiagnosticDefinition(code)\n let d = details as Record<string, unknown>\n if (!def) {\n def = INTERNAL\n d = {\n op: 'diagnostic:emit',\n problem: `unknown diagnostic code \"${code}\": define it with defineDiagnostic() first`,\n }\n }\n const b = def as unknown as DiagnosticDefinition<'DK_INTERNAL'>\n const dev = input.dev ?? (process.env.NODE_ENV !== \"production\")\n // Redact first: `where`, the text builders, the fingerprint and the docs URL only ever see\n // redacted data, so no consumer downstream (interceptors, sinks, strict errors) can leak it.\n const r = input.redaction ?? resolveRedaction(undefined, dev)\n const where = redactLocation(b.where(d as DiagnosticDetails<'DK_INTERNAL'>), r)\n d = redactDetails(\n d,\n def.redact as Readonly<Record<string, RedactField | undefined>> | undefined,\n where,\n r,\n )\n const dd = d as DiagnosticDetails<'DK_INTERNAL'>\n // Text: development builds, or production with `@datakable/core/messages` installed (§8.2).\n const t =\n dev || hasMessages()\n ? (getDiagnosticText(def.code) as DiagnosticText<'DK_INTERNAL'> | undefined)\n : undefined\n const whereText = formatWhere(where)\n const now = input.now ?? wallClock()\n const context: DiagnosticContext = {\n table: input.table ?? '(global)',\n package: PACKAGE,\n trigger: input.trigger ?? 'init',\n run: input.run ?? 0,\n phase: input.phase ?? defaultPhase(),\n mode: input.mode ?? {},\n state: input.state ?? { searchLength: 0, activeFilters: [], sort: [], page: 0 },\n occurrences: { count: 1, first: now, last: now },\n }\n if (input.createdAt !== undefined) context.createdAt = input.createdAt\n if (input.causedBy !== undefined) context.causedBy = input.causedBy\n if (input.cause !== undefined) context.cause = input.cause\n let docs = def.docs ?? `${DOCS}#${def.code.toLowerCase()}`\n if (!dev) {\n const hash = docs.indexOf('#')\n const params = `?v=${VERSION}&where=${encodeURIComponent(whereText)}`\n docs = hash < 0 ? docs + params : docs.slice(0, hash) + params + docs.slice(hash)\n }\n return new DiagnosticRecord(\n def.code,\n def.level,\n t ? t.problem(dd) : '',\n where,\n t ? t.impact(dd) : '',\n t ? t.fix(dd) : [],\n docs,\n d,\n context,\n `${def.code}@${whereText}`,\n )\n}\n\n/** Thrown under `strict: true` for error-level diagnostics, and for core invariants (§8.2). */\nexport class DatakableError extends Error {\n override name = 'DatakableError'\n readonly code: DiagnosticCode\n readonly level: DiagnosticLevel\n readonly problem: string\n readonly where: Location\n readonly impact: string\n readonly fix: string[]\n readonly docs: string\n readonly diagnostic: Diagnostic\n\n constructor(d: Diagnostic) {\n const lines = [`${d.code}: ${d.problem || '(see docs)'}`, ` where ${formatWhere(d.where)}`]\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n super(lines.join('\\n'), d.context.cause === undefined ? undefined : { cause: d.context.cause })\n this.code = d.code\n this.level = d.level\n this.problem = d.problem\n this.where = d.where\n this.impact = d.impact\n this.fix = d.fix\n this.docs = d.docs\n this.diagnostic = d\n }\n}\n\n/** Deterministic programmer errors thrown at creation (`error (config)` codes, §8.6). */\nexport class DatakableConfigError extends DatakableError {\n override name = 'DatakableConfigError'\n}\n\nexport function throwConfigError<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input?: BuildInput,\n): never {\n throw new DatakableConfigError(buildDiagnostic(code, details, input))\n}\n\n/** A core invariant broke (unregistered op, boundary installed twice): `DK_INTERNAL`. */\nexport function internalError(op: string, problem: string): DatakableError {\n return new DatakableError(buildDiagnostic('DK_INTERNAL', { op, problem }))\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB;AAAA,EAKE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAWpB,IAAM,UAAU,mBAAmB,OAAO;AACjD,IAAM,OAAO;AAGb,IAAM,WAAW,gBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,aAAa;AA0B9D,IAAM,YAAY,MACvB,OAAO,gBAAgB,YAAY,aAAa,aAC5C,KAAK,MAAM,YAAY,aAAa,YAAY,IAAI,CAAC,IACrD,KAAK,IAAI;AAER,IAAM,eAAe,MAAc,OAAO,aAAa,cAAc,QAAQ;AAE7E,SAAS,YAAY,OAAyB;AACnD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,WAAW,MAAM,KAAK,UAAU,MAAM,EAAE;AAAA,IACjD,KAAK;AACH,aAAO,YAAY,MAAM,IAAI;AAAA,IAC/B,KAAK;AACH,aAAO,WAAW,MAAM,IAAI;AAAA,IAC9B,KAAK;AACH,aAAO,MAAM,QAAQ,SACjB,QAAQ,MAAM,KAAK,KACnB,QAAQ,MAAM,KAAK,SAAS,MAAM,GAAG;AAAA,IAC3C,KAAK;AACH,aAAO,gBAAgB,MAAM,IAAI,QAAQ,MAAM,EAAE;AAAA,IACnD,KAAK;AACH,aAAO,MAAM,OAAO,aAAa,MAAM,EAAE,OAAO,MAAM,IAAI,KAAK,aAAa,MAAM,EAAE;AAAA,EACxF;AACF;AAEA,IAAM,mBAAN,MAA6C;AAAA,EAC3C,YACS,MACA,OACA,SACA,OACA,QACA,KACA,MACA,SACA,SACA,aACP;AAVO;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA,EACN;AAAA,EAEH,SAAiB;AACf,UAAM,EAAE,OAAO,QAAQ,GAAG,QAAQ,IAAI,KAAK;AAC3C,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,OAAO,WAAW,KAAK,KAAK;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,SAAS,WAAW,KAAK,OAAO;AAAA,MAChC,SAAS,WAAW,OAAO;AAAA,MAC3B,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;AAMO,SAAS,gBACd,MACA,SACA,QAAoB,CAAC,GACT;AACZ,MAAI,MAAM,wBAAwB,IAAI;AACtC,MAAI,IAAI;AACR,MAAI,CAAC,KAAK;AACR,UAAM;AACN,QAAI;AAAA,MACF,IAAI;AAAA,MACJ,SAAS,4BAA4B,IAAI;AAAA,IAC3C;AAAA,EACF;AACA,QAAM,IAAI;AACV,QAAM,MAAM,MAAM,OAAQ,QAAQ,IAAI,aAAa;AAGnD,QAAM,IAAI,MAAM,aAAa,iBAAiB,QAAW,GAAG;AAC5D,QAAM,QAAQ,eAAe,EAAE,MAAM,CAAqC,GAAG,CAAC;AAC9E,MAAI;AAAA,IACF;AAAA,IACA,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,EACF;AACA,QAAM,KAAK;AAEX,QAAM,IACJ,OAAO,YAAY,IACd,kBAAkB,IAAI,IAAI,IAC3B;AACN,QAAM,YAAY,YAAY,KAAK;AACnC,QAAM,MAAM,MAAM,OAAO,UAAU;AACnC,QAAM,UAA6B;AAAA,IACjC,OAAO,MAAM,SAAS;AAAA,IACtB,SAAS;AAAA,IACT,SAAS,MAAM,WAAW;AAAA,IAC1B,KAAK,MAAM,OAAO;AAAA,IAClB,OAAO,MAAM,SAAS,aAAa;AAAA,IACnC,MAAM,MAAM,QAAQ,CAAC;AAAA,IACrB,OAAO,MAAM,SAAS,EAAE,cAAc,GAAG,eAAe,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,EAAE;AAAA,IAC9E,aAAa,EAAE,OAAO,GAAG,OAAO,KAAK,MAAM,IAAI;AAAA,EACjD;AACA,MAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAC7D,MAAI,MAAM,aAAa,OAAW,SAAQ,WAAW,MAAM;AAC3D,MAAI,MAAM,UAAU,OAAW,SAAQ,QAAQ,MAAM;AACrD,MAAI,OAAO,IAAI,QAAQ,GAAG,IAAI,IAAI,IAAI,KAAK,YAAY,CAAC;AACxD,MAAI,CAAC,KAAK;AACR,UAAM,OAAO,KAAK,QAAQ,GAAG;AAC7B,UAAM,SAAS,MAAM,OAAO,UAAU,mBAAmB,SAAS,CAAC;AACnE,WAAO,OAAO,IAAI,OAAO,SAAS,KAAK,MAAM,GAAG,IAAI,IAAI,SAAS,KAAK,MAAM,IAAI;AAAA,EAClF;AACA,SAAO,IAAI;AAAA,IACT,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI,EAAE,QAAQ,EAAE,IAAI;AAAA,IACpB;AAAA,IACA,IAAI,EAAE,OAAO,EAAE,IAAI;AAAA,IACnB,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,IAAI,IAAI,IAAI,SAAS;AAAA,EAC1B;AACF;AAGO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EAWxC,YAAY,GAAe;AACzB,UAAM,QAAQ,CAAC,GAAG,EAAE,IAAI,KAAK,EAAE,WAAW,YAAY,IAAI,aAAa,YAAY,EAAE,KAAK,CAAC,EAAE;AAC7F,MAAE,IAAI,QAAQ,CAAC,GAAG,MAAM;AACtB,YAAM,KAAK,GAAG,MAAM,IAAI,eAAe,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;AAAA,IACrE,CAAC;AACD,UAAM,KAAK,aAAa,EAAE,IAAI,EAAE;AAChC,UAAM,MAAM,KAAK,IAAI,GAAG,EAAE,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,EAAE,QAAQ,MAAM,CAAC;AAhBhG,SAAS,OAAO;AAiBd,SAAK,OAAO,EAAE;AACd,SAAK,QAAQ,EAAE;AACf,SAAK,UAAU,EAAE;AACjB,SAAK,QAAQ,EAAE;AACf,SAAK,SAAS,EAAE;AAChB,SAAK,MAAM,EAAE;AACb,SAAK,OAAO,EAAE;AACd,SAAK,aAAa;AAAA,EACpB;AACF;AAGO,IAAM,uBAAN,cAAmC,eAAe;AAAA,EAAlD;AAAA;AACL,SAAS,OAAO;AAAA;AAClB;AAEO,SAAS,iBACd,MACA,SACA,OACO;AACP,QAAM,IAAI,qBAAqB,gBAAgB,MAAM,SAAS,KAAK,CAAC;AACtE;AAGO,SAAS,cAAc,IAAY,SAAiC;AACzE,SAAO,IAAI,eAAe,gBAAgB,eAAe,EAAE,IAAI,QAAQ,CAAC,CAAC;AAC3E;","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/build.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport {\n type DiagnosticCode,\n type DiagnosticDefinition,\n type DiagnosticDetails,\n type DiagnosticText,\n getDiagnosticDefinition,\n getDiagnosticText,\n hasMessages,\n} from './catalog'\nimport { coreDiagnostics } from './codes'\nimport './process-guard'\nimport { expandDetails } from './prose'\nimport {\n type RedactField,\n type Redactor,\n redactDetails,\n redactLocation,\n resolveRedaction,\n} from './redact'\nimport { toSafeJson } from './safe-json'\nimport type {\n Diagnostic,\n DiagnosticContext,\n DiagnosticLevel,\n DiagnosticStateSummary,\n Location,\n Phase,\n Trigger,\n} from './types'\n\nexport const PACKAGE = `@datakable/core@${VERSION}`\nconst DOCS = 'https://github.com/Ink01101011/datakable/blob/main/docs/errors.md'\n\n// Referencing the core entries keeps them in the bundle (`sideEffects: false`).\nconst INTERNAL = coreDiagnostics.find((d) => d.code === 'DK_INTERNAL') as DiagnosticDefinition\n\n/** Everything the enrich step adds; all optional so a diagnostic can be built outside a table. */\nexport interface BuildInput {\n table?: string\n createdAt?: string\n trigger?: Trigger\n run?: number\n phase?: Phase\n mode?: Record<string, 'client' | 'server'>\n state?: DiagnosticStateSummary\n /** Timestamp for `occurrences` (epoch ms). Default `wallClock()`. */\n now?: number\n cause?: unknown\n causedBy?: string\n /** Build the problem / impact / fix text. Default: development builds only (§8.2); production\n * builds also build it once `@datakable/core/messages` is installed. */\n dev?: boolean\n /** Redaction policy (§8.2). Default: `'keys'` when `dev`, else `'safe'`. */\n redaction?: Redactor\n}\n\n/**\n * Epoch ms read through `performance`, never `Date` (#7): Next 16 `cacheComponents` aborts a\n * prerender that reads `Date.now()` and reserves `performance` for introspection like this.\n */\nexport const wallClock = (): number =>\n typeof performance === 'object' && performance?.timeOrigin\n ? Math.floor(performance.timeOrigin + performance.now())\n : Date.now()\n\nexport const defaultPhase = (): Phase => (typeof document === 'undefined' ? 'ssr' : 'client')\n\nexport function formatWhere(where: Location): string {\n switch (where.kind) {\n case 'column':\n return `columns[${where.index}] (id \"${where.id}\")`\n case 'feature':\n return `feature \"${where.name}\"`\n case 'option':\n return `options.${where.path}`\n case 'row':\n return where.key === undefined\n ? `row #${where.index}`\n : `row #${where.index} (key ${where.key})`\n case 'interceptor':\n return `interceptor \"${where.name}\" on ${where.op}`\n case 'operation':\n return where.slot ? `operation ${where.op} at ${where.slot}` : `operation ${where.op}`\n }\n}\n\nclass DiagnosticRecord implements Diagnostic {\n constructor(\n public code: DiagnosticCode,\n public level: DiagnosticLevel,\n public problem: string,\n public where: Location,\n public impact: string,\n public fix: string[],\n public docs: string,\n public details: Record<string, unknown>,\n public context: DiagnosticContext,\n public fingerprint: string,\n ) {}\n\n toJSON(): object {\n const { cause: _cause, ...context } = this.context\n return {\n code: this.code,\n level: this.level,\n problem: this.problem,\n where: toSafeJson(this.where),\n impact: this.impact,\n fix: this.fix,\n docs: this.docs,\n details: toSafeJson(this.details),\n context: toSafeJson(context),\n fingerprint: this.fingerprint,\n }\n }\n}\n\n/**\n * Builds a diagnostic from a catalog code + details (the `enrich` step). Never throws for an unknown\n * code: that becomes `DK_INTERNAL`, so a typo in a plugin cannot take the logger down.\n */\nexport function buildDiagnostic<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input: BuildInput = {},\n): Diagnostic {\n let def = getDiagnosticDefinition(code)\n let d = details as Record<string, unknown>\n if (!def) {\n def = INTERNAL\n d = {\n op: 'diagnostic:emit',\n problem: `unknown-code|${code}`,\n }\n }\n const b = def as unknown as DiagnosticDefinition<'DK_INTERNAL'>\n const dev = input.dev ?? (process.env.NODE_ENV !== \"production\")\n // Detail prose keys (`fallback: 'cells-undefined'`) read as sentences wherever text is built.\n if (dev || hasMessages()) d = expandDetails(d, dev)\n // Redact first: `where`, the text builders, the fingerprint and the docs URL only ever see\n // redacted data, so no consumer downstream (interceptors, sinks, strict errors) can leak it.\n const r = input.redaction ?? resolveRedaction(undefined, dev)\n const where = redactLocation(b.where(d as DiagnosticDetails<'DK_INTERNAL'>), r)\n d = redactDetails(\n d,\n def.redact as Readonly<Record<string, RedactField | undefined>> | undefined,\n where,\n r,\n )\n const dd = d as DiagnosticDetails<'DK_INTERNAL'>\n // Text: development builds, or production with `@datakable/core/messages` installed (§8.2).\n const t =\n dev || hasMessages()\n ? (getDiagnosticText(def.code) as DiagnosticText<'DK_INTERNAL'> | undefined)\n : undefined\n const whereText = formatWhere(where)\n const now = input.now ?? wallClock()\n const context: DiagnosticContext = {\n table: input.table ?? '(global)',\n package: PACKAGE,\n trigger: input.trigger ?? 'init',\n run: input.run ?? 0,\n phase: input.phase ?? defaultPhase(),\n mode: input.mode ?? {},\n state: input.state ?? { searchLength: 0, activeFilters: [], sort: [], page: 0 },\n occurrences: { count: 1, first: now, last: now },\n }\n if (input.createdAt !== undefined) context.createdAt = input.createdAt\n if (input.causedBy !== undefined) context.causedBy = input.causedBy\n if (input.cause !== undefined) context.cause = input.cause\n let docs = def.docs ?? `${DOCS}#${def.code.toLowerCase()}`\n if (!dev) {\n const hash = docs.indexOf('#')\n const params = `?v=${VERSION}&where=${encodeURIComponent(whereText)}`\n docs = hash < 0 ? docs + params : docs.slice(0, hash) + params + docs.slice(hash)\n }\n return new DiagnosticRecord(\n def.code,\n def.level,\n t ? t.problem(dd) : '',\n where,\n t ? t.impact(dd) : '',\n t ? t.fix(dd) : [],\n docs,\n d,\n context,\n `${def.code}@${whereText}`,\n )\n}\n\n/** Thrown under `strict: true` for error-level diagnostics, and for core invariants (§8.2). */\nexport class DatakableError extends Error {\n override name = 'DatakableError'\n readonly code: DiagnosticCode\n readonly level: DiagnosticLevel\n readonly problem: string\n readonly where: Location\n readonly impact: string\n readonly fix: string[]\n readonly docs: string\n readonly diagnostic: Diagnostic\n\n constructor(d: Diagnostic) {\n const lines = [`${d.code}: ${d.problem || '(see docs)'}`, ` where ${formatWhere(d.where)}`]\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n super(lines.join('\\n'), d.context.cause === undefined ? undefined : { cause: d.context.cause })\n this.code = d.code\n this.level = d.level\n this.problem = d.problem\n this.where = d.where\n this.impact = d.impact\n this.fix = d.fix\n this.docs = d.docs\n this.diagnostic = d\n }\n}\n\n/** Deterministic programmer errors thrown at creation (`error (config)` codes, §8.6). */\nexport class DatakableConfigError extends DatakableError {\n override name = 'DatakableConfigError'\n}\n\nexport function throwConfigError<C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n input?: BuildInput,\n): never {\n throw new DatakableConfigError(buildDiagnostic(code, details, input))\n}\n\n/** A core invariant broke (unregistered op, boundary installed twice): `DK_INTERNAL`. */\nexport function internalError(op: string, problem: string): DatakableError {\n return new DatakableError(buildDiagnostic('DK_INTERNAL', { op, problem }))\n}\n"],"mappings":"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","names":[]}
@@ -25,18 +25,22 @@ __export(catalog_exports, {
25
25
  getDiagnosticText: () => getDiagnosticText,
26
26
  hasMessages: () => hasMessages,
27
27
  installMessages: () => installMessages,
28
+ installedText: () => installedText,
28
29
  keepDiagnostics: () => keepDiagnostics,
29
30
  listDiagnosticCodes: () => listDiagnosticCodes
30
31
  });
31
32
  module.exports = __toCommonJS(catalog_exports);
33
+ var import_process_guard = require("./process-guard.cjs");
32
34
  var registry = /* @__PURE__ */ new Map();
33
35
  function defineDiagnostic(meta, text) {
34
36
  const definition = text ? { ...meta, ...text } : meta;
35
- const prev = registry.get(definition.code);
36
- if (process.env.NODE_ENV !== "production" && prev && prev.level !== definition.level) {
37
- throw new Error(
38
- `[DataKable] diagnostic code ${definition.code} is already defined as level "${prev.level}"; codes are never redefined \u2014 pick a new code`
39
- );
37
+ if (process.env.NODE_ENV !== "production") {
38
+ const prev = registry.get(definition.code);
39
+ if (prev && prev.level !== definition.level) {
40
+ throw new Error(
41
+ `[DataKable] diagnostic code ${definition.code} is already defined as level "${prev.level}"; codes are never redefined \u2014 pick a new code`
42
+ );
43
+ }
40
44
  }
41
45
  registry.set(definition.code, definition);
42
46
  return definition;
@@ -47,6 +51,7 @@ function installMessages(...all) {
47
51
  for (const code of Object.keys(m)) messages.set(code, m[code]);
48
52
  }
49
53
  }
54
+ var installedText = (key) => messages.get(key);
50
55
  var hasMessages = () => messages.size > 0;
51
56
  function getDiagnosticText(code) {
52
57
  const def = registry.get(code);
@@ -70,6 +75,7 @@ function listDiagnosticCodes() {
70
75
  getDiagnosticText,
71
76
  hasMessages,
72
77
  installMessages,
78
+ installedText,
73
79
  keepDiagnostics,
74
80
  listDiagnosticCodes
75
81
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/catalog.ts"],"sourcesContent":["import type { RedactField } from './redact'\nimport type { DiagnosticLevel, Location } from './types'\n\n/**\n * Code → details type, one entry per diagnostic code. Open for declaration merging, like\n * `Operations`: the module that owns a code declares its details next to its `defineDiagnostic`\n * (`declare module '@datakable/core' { interface DiagnosticCatalog { DK_X: {...} } }`).\n */\n// biome-ignore lint/suspicious/noEmptyInterface: filled by declaration merging\nexport interface DiagnosticCatalog {}\n\nexport type DiagnosticCode = keyof DiagnosticCatalog & string\nexport type DiagnosticDetails<C extends DiagnosticCode> = DiagnosticCatalog[C]\n\n/**\n * The human-readable half of a catalog entry (§8.2). The builders get the emitted details and must\n * compute their text from that real data. They run only in development, or in production after\n * `import '@datakable/core/messages'` (`datakable/messages`), so keep them pure and cheap.\n */\nexport interface DiagnosticText<C extends DiagnosticCode = DiagnosticCode> {\n problem(details: DiagnosticDetails<C>): string\n /** Consequence + the fallback applied. */\n impact(details: DiagnosticDetails<C>): string\n /** Concrete fixes, best first; never empty. */\n fix(details: DiagnosticDetails<C>): string[]\n}\n\n/** Text for several codes, keyed by code (one object per owning module). */\nexport type DiagnosticMessages<C extends DiagnosticCode> = { readonly [K in C]: DiagnosticText<K> }\n\n/**\n * One catalog entry (§3.5, §8.3). `where` runs always; the text builders (`DiagnosticText`) only in\n * development or with messages opted in.\n */\nexport interface DiagnosticDefinition<C extends DiagnosticCode = DiagnosticCode>\n extends DiagnosticText<C> {\n readonly code: C\n readonly level: DiagnosticLevel\n /** `error (config)` codes: thrown as `DatakableConfigError` at creation (§8.6). */\n readonly config?: boolean\n where(details: DiagnosticDetails<C>): Location\n /** Overrides the default `docs/errors.md#<code>` URL. */\n readonly docs?: string\n /**\n * Detail fields that carry user data (row keys, cell values, schema issues, thrown errors), so\n * redaction (§8.2) rewrites them before `where`, the text builders or any sink see them. See\n * `RedactField`. Fields not listed are logged as-is.\n */\n readonly redact?: { readonly [K in keyof DiagnosticDetails<C>]?: RedactField }\n}\n\n/** A definition without its text: the part a production bundle keeps. */\nexport type DiagnosticMeta<C extends DiagnosticCode = DiagnosticCode> = Omit<\n DiagnosticDefinition<C>,\n keyof DiagnosticText\n>\n\nconst registry = new Map<string, DiagnosticDefinition>()\n\n/**\n * Adds a code to the catalog and returns the definition. A code may be defined again only with the\n * same level (module re-evaluation); a code is never redefined with another meaning (§8.5).\n *\n * Core passes the text separately as `(process.env.NODE_ENV !== \"production\") && messages.DK_X` (§8.2): a production bundle folds that\n * to `false`, drops the module's message object, and keeps only `code`, `level`, `where` and\n * `redact`. The same message object is what `@datakable/core/messages` installs.\n */\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n definition: DiagnosticDefinition<C>,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C>,\n text: DiagnosticText<C> | false,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C> & Partial<DiagnosticText<C>>,\n text?: DiagnosticText<C> | false,\n): DiagnosticDefinition<C> {\n const definition = (text ? { ...meta, ...text } : meta) as DiagnosticDefinition<C>\n const prev = registry.get(definition.code)\n // Development check: codes are fixed at build time, a production bundle skips it.\n if ((process.env.NODE_ENV !== \"production\") && prev && prev.level !== definition.level) {\n throw new Error(\n `[DataKable] diagnostic code ${definition.code} is already defined as level ` +\n `\"${prev.level}\"; codes are never redefined — pick a new code`,\n )\n }\n registry.set(definition.code, definition as unknown as DiagnosticDefinition)\n return definition\n}\n\nconst messages = new Map<string, DiagnosticText>()\n\n/**\n * Installs full message text for production builds (§8.2); called by `@datakable/core/messages`.\n * Development builds already carry the text on each definition.\n */\nexport function installMessages(...all: ReadonlyArray<Readonly<Record<string, unknown>>>): void {\n for (const m of all) {\n for (const code of Object.keys(m)) messages.set(code, m[code] as DiagnosticText)\n }\n}\n\n/** `true` once `installMessages` ran: production diagnostics then build their text too. */\nexport const hasMessages = (): boolean => messages.size > 0\n\n/** The text builders for a code: from its definition (development) or installed messages. */\nexport function getDiagnosticText(code: string): DiagnosticText | undefined {\n const def = registry.get(code)\n return def && typeof def.problem === 'function' ? def : messages.get(code)\n}\n\n/**\n * Uses the definitions of the codes a module emits, so a bundler keeps them. `@datakable/core` is\n * built one file per module with `sideEffects: false`: a codes module that is only referenced\n * (`void defs`) is dropped from a consumer's bundle together with its `defineDiagnostic` calls, and\n * the codes would then emit as unknown. Call this at the top level of the emitting module. It\n * (re-)registers the definitions, which is idempotent. `pnpm check:catalog` verifies it per bundle.\n */\nexport function keepDiagnostics(\n ...lists: ReadonlyArray<readonly { readonly code: string }[]>\n): void {\n for (const list of lists) {\n for (const def of list) registry.set(def.code, def as unknown as DiagnosticDefinition)\n }\n}\n\nexport function getDiagnosticDefinition(code: string): DiagnosticDefinition | undefined {\n return registry.get(code)\n}\n\nexport function listDiagnosticCodes(): string[] {\n return [...registry.keys()]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDA,IAAM,WAAW,oBAAI,IAAkC;AAiBhD,SAAS,iBACd,MACA,MACyB;AACzB,QAAM,aAAc,OAAO,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI;AAClD,QAAM,OAAO,SAAS,IAAI,WAAW,IAAI;AAEzC,MAAK,QAAQ,IAAI,aAAa,gBAAiB,QAAQ,KAAK,UAAU,WAAW,OAAO;AACtF,UAAM,IAAI;AAAA,MACR,+BAA+B,WAAW,IAAI,iCACxC,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AACA,WAAS,IAAI,WAAW,MAAM,UAA6C;AAC3E,SAAO;AACT;AAEA,IAAM,WAAW,oBAAI,IAA4B;AAM1C,SAAS,mBAAmB,KAA6D;AAC9F,aAAW,KAAK,KAAK;AACnB,eAAW,QAAQ,OAAO,KAAK,CAAC,EAAG,UAAS,IAAI,MAAM,EAAE,IAAI,CAAmB;AAAA,EACjF;AACF;AAGO,IAAM,cAAc,MAAe,SAAS,OAAO;AAGnD,SAAS,kBAAkB,MAA0C;AAC1E,QAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,SAAO,OAAO,OAAO,IAAI,YAAY,aAAa,MAAM,SAAS,IAAI,IAAI;AAC3E;AASO,SAAS,mBACX,OACG;AACN,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,KAAM,UAAS,IAAI,IAAI,MAAM,GAAsC;AAAA,EACvF;AACF;AAEO,SAAS,wBAAwB,MAAgD;AACtF,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,sBAAgC;AAC9C,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/catalog.ts"],"sourcesContent":["import './process-guard'\nimport type { RedactField } from './redact'\nimport type { DiagnosticLevel, Location } from './types'\n\n/**\n * Code → details type, one entry per diagnostic code. Open for declaration merging, like\n * `Operations`: the module that owns a code declares its details next to its `defineDiagnostic`\n * (`declare module '@datakable/core' { interface DiagnosticCatalog { DK_X: {...} } }`).\n */\n// biome-ignore lint/suspicious/noEmptyInterface: filled by declaration merging\nexport interface DiagnosticCatalog {}\n\nexport type DiagnosticCode = keyof DiagnosticCatalog & string\nexport type DiagnosticDetails<C extends DiagnosticCode> = DiagnosticCatalog[C]\n\n/**\n * The human-readable half of a catalog entry (§8.2). The builders get the emitted details and must\n * compute their text from that real data. They run only in development, or in production after\n * `import '@datakable/core/messages'` (`datakable/messages`), so keep them pure and cheap.\n */\nexport interface DiagnosticText<C extends DiagnosticCode = DiagnosticCode> {\n problem(details: DiagnosticDetails<C>): string\n /** Consequence + the fallback applied. */\n impact(details: DiagnosticDetails<C>): string\n /** Concrete fixes, best first; never empty. */\n fix(details: DiagnosticDetails<C>): string[]\n}\n\n/** Text for several codes, keyed by code (one object per owning module). */\nexport type DiagnosticMessages<C extends DiagnosticCode> = { readonly [K in C]: DiagnosticText<K> }\n\n/**\n * One catalog entry (§3.5, §8.3). `where` runs always; the text builders (`DiagnosticText`) only in\n * development or with messages opted in.\n */\nexport interface DiagnosticDefinition<C extends DiagnosticCode = DiagnosticCode>\n extends DiagnosticText<C> {\n readonly code: C\n readonly level: DiagnosticLevel\n /** `error (config)` codes: thrown as `DatakableConfigError` at creation (§8.6). */\n readonly config?: boolean\n where(details: DiagnosticDetails<C>): Location\n /** Overrides the default `docs/errors.md#<code>` URL. */\n readonly docs?: string\n /**\n * Detail fields that carry user data (row keys, cell values, schema issues, thrown errors), so\n * redaction (§8.2) rewrites them before `where`, the text builders or any sink see them. See\n * `RedactField`. Fields not listed are logged as-is.\n */\n readonly redact?: { readonly [K in keyof DiagnosticDetails<C>]?: RedactField }\n}\n\n/** A definition without its text: the part a production bundle keeps. */\nexport type DiagnosticMeta<C extends DiagnosticCode = DiagnosticCode> = Omit<\n DiagnosticDefinition<C>,\n keyof DiagnosticText\n>\n\nconst registry = new Map<string, DiagnosticDefinition>()\n\n/**\n * Adds a code to the catalog and returns the definition. A code may be defined again only with the\n * same level (module re-evaluation); a code is never redefined with another meaning (§8.5).\n *\n * Core passes the text separately as `(process.env.NODE_ENV !== \"production\") && messages.DK_X` (§8.2): a production bundle folds that\n * to `false`, drops the module's message object, and keeps only `code`, `level`, `where` and\n * `redact`. The same message object is what `@datakable/core/messages` installs.\n */\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n definition: DiagnosticDefinition<C>,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C>,\n text: DiagnosticText<C> | false,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C> & Partial<DiagnosticText<C>>,\n text?: DiagnosticText<C> | false,\n): DiagnosticDefinition<C> {\n const definition = (text ? { ...meta, ...text } : meta) as DiagnosticDefinition<C>\n // Development check: codes are fixed at build time, a production bundle skips it.\n if ((process.env.NODE_ENV !== \"production\")) {\n const prev = registry.get(definition.code)\n if (prev && prev.level !== definition.level) {\n throw new Error(\n `[DataKable] diagnostic code ${definition.code} is already defined as level ` +\n `\"${prev.level}\"; codes are never redefined — pick a new code`,\n )\n }\n }\n registry.set(definition.code, definition as unknown as DiagnosticDefinition)\n return definition\n}\n\nconst messages = new Map<string, DiagnosticText>()\n\n/**\n * Installs full message text for production builds (§8.2); called by `@datakable/core/messages`.\n * Development builds already carry the text on each definition.\n */\nexport function installMessages(...all: ReadonlyArray<Readonly<Record<string, unknown>>>): void {\n for (const m of all) {\n for (const code of Object.keys(m)) messages.set(code, m[code] as DiagnosticText)\n }\n}\n\n/** An installed message by key: a code's text builders, or a detail prose sentence (`prose.ts`). */\nexport const installedText = (key: string): unknown => messages.get(key)\n\n/** `true` once `installMessages` ran: production diagnostics then build their text too. */\nexport const hasMessages = (): boolean => messages.size > 0\n\n/** The text builders for a code: from its definition (development) or installed messages. */\nexport function getDiagnosticText(code: string): DiagnosticText | undefined {\n const def = registry.get(code)\n return def && typeof def.problem === 'function' ? def : messages.get(code)\n}\n\n/**\n * Uses the definitions of the codes a module emits, so a bundler keeps them. `@datakable/core` is\n * built one file per module with `sideEffects: false`: a codes module that is only referenced\n * (`void defs`) is dropped from a consumer's bundle together with its `defineDiagnostic` calls, and\n * the codes would then emit as unknown. Call this at the top level of the emitting module. It\n * (re-)registers the definitions, which is idempotent. `pnpm check:catalog` verifies it per bundle.\n */\nexport function keepDiagnostics(\n ...lists: ReadonlyArray<readonly { readonly code: string }[]>\n): void {\n for (const list of lists) {\n for (const def of list) registry.set(def.code, def as unknown as DiagnosticDefinition)\n }\n}\n\nexport function getDiagnosticDefinition(code: string): DiagnosticDefinition | undefined {\n return registry.get(code)\n}\n\nexport function listDiagnosticCodes(): string[] {\n return [...registry.keys()]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAAO;AA0DP,IAAM,WAAW,oBAAI,IAAkC;AAiBhD,SAAS,iBACd,MACA,MACyB;AACzB,QAAM,aAAc,OAAO,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI;AAElD,MAAK,QAAQ,IAAI,aAAa,cAAe;AAC3C,UAAM,OAAO,SAAS,IAAI,WAAW,IAAI;AACzC,QAAI,QAAQ,KAAK,UAAU,WAAW,OAAO;AAC3C,YAAM,IAAI;AAAA,QACR,+BAA+B,WAAW,IAAI,iCACxC,KAAK,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACA,WAAS,IAAI,WAAW,MAAM,UAA6C;AAC3E,SAAO;AACT;AAEA,IAAM,WAAW,oBAAI,IAA4B;AAM1C,SAAS,mBAAmB,KAA6D;AAC9F,aAAW,KAAK,KAAK;AACnB,eAAW,QAAQ,OAAO,KAAK,CAAC,EAAG,UAAS,IAAI,MAAM,EAAE,IAAI,CAAmB;AAAA,EACjF;AACF;AAGO,IAAM,gBAAgB,CAAC,QAAyB,SAAS,IAAI,GAAG;AAGhE,IAAM,cAAc,MAAe,SAAS,OAAO;AAGnD,SAAS,kBAAkB,MAA0C;AAC1E,QAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,SAAO,OAAO,OAAO,IAAI,YAAY,aAAa,MAAM,SAAS,IAAI,IAAI;AAC3E;AASO,SAAS,mBACX,OACG;AACN,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,KAAM,UAAS,IAAI,IAAI,MAAM,GAAsC;AAAA,EACvF;AACF;AAEO,SAAS,wBAAwB,MAAgD;AACtF,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,sBAAgC;AAC9C,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;","names":[]}
@@ -1,12 +1,15 @@
1
1
  // src/diagnostics/catalog.ts
2
+ import "./process-guard.js";
2
3
  var registry = /* @__PURE__ */ new Map();
3
4
  function defineDiagnostic(meta, text) {
4
5
  const definition = text ? { ...meta, ...text } : meta;
5
- const prev = registry.get(definition.code);
6
- if (process.env.NODE_ENV !== "production" && prev && prev.level !== definition.level) {
7
- throw new Error(
8
- `[DataKable] diagnostic code ${definition.code} is already defined as level "${prev.level}"; codes are never redefined \u2014 pick a new code`
9
- );
6
+ if (process.env.NODE_ENV !== "production") {
7
+ const prev = registry.get(definition.code);
8
+ if (prev && prev.level !== definition.level) {
9
+ throw new Error(
10
+ `[DataKable] diagnostic code ${definition.code} is already defined as level "${prev.level}"; codes are never redefined \u2014 pick a new code`
11
+ );
12
+ }
10
13
  }
11
14
  registry.set(definition.code, definition);
12
15
  return definition;
@@ -17,6 +20,7 @@ function installMessages(...all) {
17
20
  for (const code of Object.keys(m)) messages.set(code, m[code]);
18
21
  }
19
22
  }
23
+ var installedText = (key) => messages.get(key);
20
24
  var hasMessages = () => messages.size > 0;
21
25
  function getDiagnosticText(code) {
22
26
  const def = registry.get(code);
@@ -39,6 +43,7 @@ export {
39
43
  getDiagnosticText,
40
44
  hasMessages,
41
45
  installMessages,
46
+ installedText,
42
47
  keepDiagnostics,
43
48
  listDiagnosticCodes
44
49
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/catalog.ts"],"sourcesContent":["import type { RedactField } from './redact'\nimport type { DiagnosticLevel, Location } from './types'\n\n/**\n * Code → details type, one entry per diagnostic code. Open for declaration merging, like\n * `Operations`: the module that owns a code declares its details next to its `defineDiagnostic`\n * (`declare module '@datakable/core' { interface DiagnosticCatalog { DK_X: {...} } }`).\n */\n// biome-ignore lint/suspicious/noEmptyInterface: filled by declaration merging\nexport interface DiagnosticCatalog {}\n\nexport type DiagnosticCode = keyof DiagnosticCatalog & string\nexport type DiagnosticDetails<C extends DiagnosticCode> = DiagnosticCatalog[C]\n\n/**\n * The human-readable half of a catalog entry (§8.2). The builders get the emitted details and must\n * compute their text from that real data. They run only in development, or in production after\n * `import '@datakable/core/messages'` (`datakable/messages`), so keep them pure and cheap.\n */\nexport interface DiagnosticText<C extends DiagnosticCode = DiagnosticCode> {\n problem(details: DiagnosticDetails<C>): string\n /** Consequence + the fallback applied. */\n impact(details: DiagnosticDetails<C>): string\n /** Concrete fixes, best first; never empty. */\n fix(details: DiagnosticDetails<C>): string[]\n}\n\n/** Text for several codes, keyed by code (one object per owning module). */\nexport type DiagnosticMessages<C extends DiagnosticCode> = { readonly [K in C]: DiagnosticText<K> }\n\n/**\n * One catalog entry (§3.5, §8.3). `where` runs always; the text builders (`DiagnosticText`) only in\n * development or with messages opted in.\n */\nexport interface DiagnosticDefinition<C extends DiagnosticCode = DiagnosticCode>\n extends DiagnosticText<C> {\n readonly code: C\n readonly level: DiagnosticLevel\n /** `error (config)` codes: thrown as `DatakableConfigError` at creation (§8.6). */\n readonly config?: boolean\n where(details: DiagnosticDetails<C>): Location\n /** Overrides the default `docs/errors.md#<code>` URL. */\n readonly docs?: string\n /**\n * Detail fields that carry user data (row keys, cell values, schema issues, thrown errors), so\n * redaction (§8.2) rewrites them before `where`, the text builders or any sink see them. See\n * `RedactField`. Fields not listed are logged as-is.\n */\n readonly redact?: { readonly [K in keyof DiagnosticDetails<C>]?: RedactField }\n}\n\n/** A definition without its text: the part a production bundle keeps. */\nexport type DiagnosticMeta<C extends DiagnosticCode = DiagnosticCode> = Omit<\n DiagnosticDefinition<C>,\n keyof DiagnosticText\n>\n\nconst registry = new Map<string, DiagnosticDefinition>()\n\n/**\n * Adds a code to the catalog and returns the definition. A code may be defined again only with the\n * same level (module re-evaluation); a code is never redefined with another meaning (§8.5).\n *\n * Core passes the text separately as `(process.env.NODE_ENV !== \"production\") && messages.DK_X` (§8.2): a production bundle folds that\n * to `false`, drops the module's message object, and keeps only `code`, `level`, `where` and\n * `redact`. The same message object is what `@datakable/core/messages` installs.\n */\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n definition: DiagnosticDefinition<C>,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C>,\n text: DiagnosticText<C> | false,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C> & Partial<DiagnosticText<C>>,\n text?: DiagnosticText<C> | false,\n): DiagnosticDefinition<C> {\n const definition = (text ? { ...meta, ...text } : meta) as DiagnosticDefinition<C>\n const prev = registry.get(definition.code)\n // Development check: codes are fixed at build time, a production bundle skips it.\n if ((process.env.NODE_ENV !== \"production\") && prev && prev.level !== definition.level) {\n throw new Error(\n `[DataKable] diagnostic code ${definition.code} is already defined as level ` +\n `\"${prev.level}\"; codes are never redefined — pick a new code`,\n )\n }\n registry.set(definition.code, definition as unknown as DiagnosticDefinition)\n return definition\n}\n\nconst messages = new Map<string, DiagnosticText>()\n\n/**\n * Installs full message text for production builds (§8.2); called by `@datakable/core/messages`.\n * Development builds already carry the text on each definition.\n */\nexport function installMessages(...all: ReadonlyArray<Readonly<Record<string, unknown>>>): void {\n for (const m of all) {\n for (const code of Object.keys(m)) messages.set(code, m[code] as DiagnosticText)\n }\n}\n\n/** `true` once `installMessages` ran: production diagnostics then build their text too. */\nexport const hasMessages = (): boolean => messages.size > 0\n\n/** The text builders for a code: from its definition (development) or installed messages. */\nexport function getDiagnosticText(code: string): DiagnosticText | undefined {\n const def = registry.get(code)\n return def && typeof def.problem === 'function' ? def : messages.get(code)\n}\n\n/**\n * Uses the definitions of the codes a module emits, so a bundler keeps them. `@datakable/core` is\n * built one file per module with `sideEffects: false`: a codes module that is only referenced\n * (`void defs`) is dropped from a consumer's bundle together with its `defineDiagnostic` calls, and\n * the codes would then emit as unknown. Call this at the top level of the emitting module. It\n * (re-)registers the definitions, which is idempotent. `pnpm check:catalog` verifies it per bundle.\n */\nexport function keepDiagnostics(\n ...lists: ReadonlyArray<readonly { readonly code: string }[]>\n): void {\n for (const list of lists) {\n for (const def of list) registry.set(def.code, def as unknown as DiagnosticDefinition)\n }\n}\n\nexport function getDiagnosticDefinition(code: string): DiagnosticDefinition | undefined {\n return registry.get(code)\n}\n\nexport function listDiagnosticCodes(): string[] {\n return [...registry.keys()]\n}\n"],"mappings":";AAyDA,IAAM,WAAW,oBAAI,IAAkC;AAiBhD,SAAS,iBACd,MACA,MACyB;AACzB,QAAM,aAAc,OAAO,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI;AAClD,QAAM,OAAO,SAAS,IAAI,WAAW,IAAI;AAEzC,MAAK,QAAQ,IAAI,aAAa,gBAAiB,QAAQ,KAAK,UAAU,WAAW,OAAO;AACtF,UAAM,IAAI;AAAA,MACR,+BAA+B,WAAW,IAAI,iCACxC,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AACA,WAAS,IAAI,WAAW,MAAM,UAA6C;AAC3E,SAAO;AACT;AAEA,IAAM,WAAW,oBAAI,IAA4B;AAM1C,SAAS,mBAAmB,KAA6D;AAC9F,aAAW,KAAK,KAAK;AACnB,eAAW,QAAQ,OAAO,KAAK,CAAC,EAAG,UAAS,IAAI,MAAM,EAAE,IAAI,CAAmB;AAAA,EACjF;AACF;AAGO,IAAM,cAAc,MAAe,SAAS,OAAO;AAGnD,SAAS,kBAAkB,MAA0C;AAC1E,QAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,SAAO,OAAO,OAAO,IAAI,YAAY,aAAa,MAAM,SAAS,IAAI,IAAI;AAC3E;AASO,SAAS,mBACX,OACG;AACN,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,KAAM,UAAS,IAAI,IAAI,MAAM,GAAsC;AAAA,EACvF;AACF;AAEO,SAAS,wBAAwB,MAAgD;AACtF,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,sBAAgC;AAC9C,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/catalog.ts"],"sourcesContent":["import './process-guard'\nimport type { RedactField } from './redact'\nimport type { DiagnosticLevel, Location } from './types'\n\n/**\n * Code → details type, one entry per diagnostic code. Open for declaration merging, like\n * `Operations`: the module that owns a code declares its details next to its `defineDiagnostic`\n * (`declare module '@datakable/core' { interface DiagnosticCatalog { DK_X: {...} } }`).\n */\n// biome-ignore lint/suspicious/noEmptyInterface: filled by declaration merging\nexport interface DiagnosticCatalog {}\n\nexport type DiagnosticCode = keyof DiagnosticCatalog & string\nexport type DiagnosticDetails<C extends DiagnosticCode> = DiagnosticCatalog[C]\n\n/**\n * The human-readable half of a catalog entry (§8.2). The builders get the emitted details and must\n * compute their text from that real data. They run only in development, or in production after\n * `import '@datakable/core/messages'` (`datakable/messages`), so keep them pure and cheap.\n */\nexport interface DiagnosticText<C extends DiagnosticCode = DiagnosticCode> {\n problem(details: DiagnosticDetails<C>): string\n /** Consequence + the fallback applied. */\n impact(details: DiagnosticDetails<C>): string\n /** Concrete fixes, best first; never empty. */\n fix(details: DiagnosticDetails<C>): string[]\n}\n\n/** Text for several codes, keyed by code (one object per owning module). */\nexport type DiagnosticMessages<C extends DiagnosticCode> = { readonly [K in C]: DiagnosticText<K> }\n\n/**\n * One catalog entry (§3.5, §8.3). `where` runs always; the text builders (`DiagnosticText`) only in\n * development or with messages opted in.\n */\nexport interface DiagnosticDefinition<C extends DiagnosticCode = DiagnosticCode>\n extends DiagnosticText<C> {\n readonly code: C\n readonly level: DiagnosticLevel\n /** `error (config)` codes: thrown as `DatakableConfigError` at creation (§8.6). */\n readonly config?: boolean\n where(details: DiagnosticDetails<C>): Location\n /** Overrides the default `docs/errors.md#<code>` URL. */\n readonly docs?: string\n /**\n * Detail fields that carry user data (row keys, cell values, schema issues, thrown errors), so\n * redaction (§8.2) rewrites them before `where`, the text builders or any sink see them. See\n * `RedactField`. Fields not listed are logged as-is.\n */\n readonly redact?: { readonly [K in keyof DiagnosticDetails<C>]?: RedactField }\n}\n\n/** A definition without its text: the part a production bundle keeps. */\nexport type DiagnosticMeta<C extends DiagnosticCode = DiagnosticCode> = Omit<\n DiagnosticDefinition<C>,\n keyof DiagnosticText\n>\n\nconst registry = new Map<string, DiagnosticDefinition>()\n\n/**\n * Adds a code to the catalog and returns the definition. A code may be defined again only with the\n * same level (module re-evaluation); a code is never redefined with another meaning (§8.5).\n *\n * Core passes the text separately as `(process.env.NODE_ENV !== \"production\") && messages.DK_X` (§8.2): a production bundle folds that\n * to `false`, drops the module's message object, and keeps only `code`, `level`, `where` and\n * `redact`. The same message object is what `@datakable/core/messages` installs.\n */\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n definition: DiagnosticDefinition<C>,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C>,\n text: DiagnosticText<C> | false,\n): DiagnosticDefinition<C>\nexport function defineDiagnostic<const C extends DiagnosticCode>(\n meta: DiagnosticMeta<C> & Partial<DiagnosticText<C>>,\n text?: DiagnosticText<C> | false,\n): DiagnosticDefinition<C> {\n const definition = (text ? { ...meta, ...text } : meta) as DiagnosticDefinition<C>\n // Development check: codes are fixed at build time, a production bundle skips it.\n if ((process.env.NODE_ENV !== \"production\")) {\n const prev = registry.get(definition.code)\n if (prev && prev.level !== definition.level) {\n throw new Error(\n `[DataKable] diagnostic code ${definition.code} is already defined as level ` +\n `\"${prev.level}\"; codes are never redefined — pick a new code`,\n )\n }\n }\n registry.set(definition.code, definition as unknown as DiagnosticDefinition)\n return definition\n}\n\nconst messages = new Map<string, DiagnosticText>()\n\n/**\n * Installs full message text for production builds (§8.2); called by `@datakable/core/messages`.\n * Development builds already carry the text on each definition.\n */\nexport function installMessages(...all: ReadonlyArray<Readonly<Record<string, unknown>>>): void {\n for (const m of all) {\n for (const code of Object.keys(m)) messages.set(code, m[code] as DiagnosticText)\n }\n}\n\n/** An installed message by key: a code's text builders, or a detail prose sentence (`prose.ts`). */\nexport const installedText = (key: string): unknown => messages.get(key)\n\n/** `true` once `installMessages` ran: production diagnostics then build their text too. */\nexport const hasMessages = (): boolean => messages.size > 0\n\n/** The text builders for a code: from its definition (development) or installed messages. */\nexport function getDiagnosticText(code: string): DiagnosticText | undefined {\n const def = registry.get(code)\n return def && typeof def.problem === 'function' ? def : messages.get(code)\n}\n\n/**\n * Uses the definitions of the codes a module emits, so a bundler keeps them. `@datakable/core` is\n * built one file per module with `sideEffects: false`: a codes module that is only referenced\n * (`void defs`) is dropped from a consumer's bundle together with its `defineDiagnostic` calls, and\n * the codes would then emit as unknown. Call this at the top level of the emitting module. It\n * (re-)registers the definitions, which is idempotent. `pnpm check:catalog` verifies it per bundle.\n */\nexport function keepDiagnostics(\n ...lists: ReadonlyArray<readonly { readonly code: string }[]>\n): void {\n for (const list of lists) {\n for (const def of list) registry.set(def.code, def as unknown as DiagnosticDefinition)\n }\n}\n\nexport function getDiagnosticDefinition(code: string): DiagnosticDefinition | undefined {\n return registry.get(code)\n}\n\nexport function listDiagnosticCodes(): string[] {\n return [...registry.keys()]\n}\n"],"mappings":";AAAA,OAAO;AA0DP,IAAM,WAAW,oBAAI,IAAkC;AAiBhD,SAAS,iBACd,MACA,MACyB;AACzB,QAAM,aAAc,OAAO,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI;AAElD,MAAK,QAAQ,IAAI,aAAa,cAAe;AAC3C,UAAM,OAAO,SAAS,IAAI,WAAW,IAAI;AACzC,QAAI,QAAQ,KAAK,UAAU,WAAW,OAAO;AAC3C,YAAM,IAAI;AAAA,QACR,+BAA+B,WAAW,IAAI,iCACxC,KAAK,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACA,WAAS,IAAI,WAAW,MAAM,UAA6C;AAC3E,SAAO;AACT;AAEA,IAAM,WAAW,oBAAI,IAA4B;AAM1C,SAAS,mBAAmB,KAA6D;AAC9F,aAAW,KAAK,KAAK;AACnB,eAAW,QAAQ,OAAO,KAAK,CAAC,EAAG,UAAS,IAAI,MAAM,EAAE,IAAI,CAAmB;AAAA,EACjF;AACF;AAGO,IAAM,gBAAgB,CAAC,QAAyB,SAAS,IAAI,GAAG;AAGhE,IAAM,cAAc,MAAe,SAAS,OAAO;AAGnD,SAAS,kBAAkB,MAA0C;AAC1E,QAAM,MAAM,SAAS,IAAI,IAAI;AAC7B,SAAO,OAAO,OAAO,IAAI,YAAY,aAAa,MAAM,SAAS,IAAI,IAAI;AAC3E;AASO,SAAS,mBACX,OACG;AACN,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,KAAM,UAAS,IAAI,IAAI,MAAM,GAAsC;AAAA,EACvF;AACF;AAEO,SAAS,wBAAwB,MAAgD;AACtF,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,sBAAgC;AAC9C,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;","names":[]}
@@ -26,6 +26,7 @@ __export(codes_exports, {
26
26
  module.exports = __toCommonJS(codes_exports);
27
27
  var import_version = require("../version.cjs");
28
28
  var import_catalog = require("./catalog.cjs");
29
+ var import_process_guard = require("./process-guard.cjs");
29
30
  var ISSUES = "https://github.com/Ink01101011/datakable/issues/new";
30
31
  function issueLink(d) {
31
32
  const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ""}`;
@@ -89,14 +90,17 @@ var coreDiagnostics = [
89
90
  },
90
91
  process.env.NODE_ENV !== "production" && coreMessages.DK_INTERCEPTOR_FAILED
91
92
  ),
92
- (0, import_catalog.defineDiagnostic)(
93
- {
94
- code: "DK_HOT_INTERCEPTOR",
95
- level: "warn",
96
- where: (d) => ({ kind: "interceptor", name: d.interceptor, op: d.op })
97
- },
98
- process.env.NODE_ENV !== "production" && coreMessages.DK_HOT_INTERCEPTOR
99
- ),
93
+ // Dev-only warning (never emitted in production): no definition there either.
94
+ ...process.env.NODE_ENV !== "production" ? [
95
+ (0, import_catalog.defineDiagnostic)(
96
+ {
97
+ code: "DK_HOT_INTERCEPTOR",
98
+ level: "warn",
99
+ where: (d) => ({ kind: "interceptor", name: d.interceptor, op: d.op })
100
+ },
101
+ process.env.NODE_ENV !== "production" && coreMessages.DK_HOT_INTERCEPTOR
102
+ )
103
+ ] : [],
100
104
  (0, import_catalog.defineDiagnostic)(
101
105
  {
102
106
  code: "DK_OPERATION_CONFLICT",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/codes.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport { type DiagnosticMessages, defineDiagnostic } from './catalog'\n\ndeclare module './catalog' {\n interface DiagnosticCatalog {\n DK_INTERCEPTOR_FAILED: { interceptor: string; op: string; slot: string }\n DK_HOT_INTERCEPTOR: { interceptor: string; op: string }\n DK_OPERATION_CONFLICT: {\n op: string\n kind: string\n hot: boolean\n previousKind: string\n previousHot: boolean\n }\n DK_INTERNAL: {\n op: string\n problem: string\n /** The ctx `where` of the failed call (errorBoundary). */\n slot?: string\n /** `error.name` of the classified throw (errorBoundary). */\n errorName?: string\n /** The registry fallback applied (errorBoundary); invariant callers abort instead. */\n fallback?: string\n /**\n * `error.message` of the classified throw (errorBoundary). Engine messages embed row values\n * (`Cannot create property 'x' on string '<value>'`), so it is redacted as a cell value.\n */\n message?: unknown\n }\n }\n}\n\nconst ISSUES = 'https://github.com/Ink01101011/datakable/issues/new'\n\n/** Prefilled GitHub issue (§8.3): version, op, slot and error name — never values. */\nfunction issueLink(d: { op: string; slot?: string; errorName?: string }): string {\n const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ''}`\n const body = [\n `package: @datakable/core@${VERSION}`,\n `operation: ${d.op}`,\n `slot: ${d.slot ?? '-'}`,\n `error: ${d.errorName ?? '-'}`,\n '',\n 'diagnostic.toJSON():',\n '',\n 'table.explain():',\n '',\n ].join('\\n')\n return `${ISSUES}?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const coreMessages: DiagnosticMessages<\n 'DK_INTERCEPTOR_FAILED' | 'DK_HOT_INTERCEPTOR' | 'DK_OPERATION_CONFLICT' | 'DK_INTERNAL'\n> = {\n DK_INTERCEPTOR_FAILED: {\n problem: (d) => `interceptor \"${d.interceptor}\" threw on ${d.op} (at ${d.slot})`,\n impact: () => 'the interceptor was skipped for this call; the operation ran without it',\n fix: (d) => [\n `catch errors inside \"${d.interceptor}\" and always return next() (or a valid result)`,\n `see context.cause for the original error`,\n ],\n },\n DK_HOT_INTERCEPTOR: {\n problem: (d) =>\n `interceptor \"${d.interceptor}\" is attached to hot op \"${d.op}\" without hot: true`,\n // Measured by intercept/chain.bench.ts (Apple M4 Pro, Node 25.2.1): one pass-through\n // interceptor adds ~10 ns per call; spec §9.2 measured +15% when wrapping per cell. Kept inside\n // the builder (no module constant), so production bundles carry none of it.\n impact: () =>\n 'it runs once per column per pipeline run; measured cost: ~10 ns per call per ' +\n 'interceptor (+15% on a per-cell format call, spec §9.2) on top of the interceptor body, ' +\n 'which runs columns × pipeline runs times. It still runs (dev-only warning).',\n fix: (d) => [\n `set hot: true on \"${d.interceptor}\" to accept the cost, and never loop per cell inside it`,\n `attach \"${d.interceptor}\" to cold ops only (list them in \\`on\\` instead of '*' / '${d.op}')`,\n ],\n },\n DK_OPERATION_CONFLICT: {\n problem: (d) =>\n `operation \"${d.op}\" is already registered as ${d.previousKind}` +\n `${d.previousHot ? ' (hot)' : ''}; got ${d.kind}${d.hot ? ' (hot)' : ''}`,\n impact: () => 'the registration was rejected; the first one stays in effect',\n fix: (d) => [\n `register \"${d.op}\" with the same kind and hot flag (${d.previousKind}, hot: ${d.previousHot})`,\n `or give the new operation its own name, e.g. \"myplugin:${d.op.split(':').pop()}\"`,\n ],\n },\n DK_INTERNAL: {\n problem: (d) =>\n d.errorName\n ? `${d.errorName} thrown inside DataKable code during ${d.op}` +\n `${d.message === undefined ? '' : ` (message: ${String(d.message)})`} — likely a DataKable bug`\n : d.problem,\n impact: (d) => (d.fallback ? `${d.fallback}; the table keeps working` : 'the call was aborted'),\n fix: (d) => [\n `this is likely a DataKable bug or a plugin wiring error — report it: ${issueLink(d)}`,\n 'attach diagnostic.toJSON() and table.explain() (both redacted) to the issue',\n 'meanwhile, check plugins / interceptors registered on this operation',\n ],\n },\n}\n\n/** Codes owned by core plumbing (interceptor runtime, op registry). Features define their own. */\nexport const coreDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INTERCEPTOR_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERCEPTOR_FAILED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HOT_INTERCEPTOR',\n level: 'warn',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_HOT_INTERCEPTOR,\n ),\n defineDiagnostic(\n {\n code: 'DK_OPERATION_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'operation', op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_OPERATION_CONFLICT,\n ),\n // Core invariants (unknown op, boundary installed twice) and throws that errorBoundary traced to\n // DataKable's own code (top stack frame in a datakable path).\n defineDiagnostic(\n {\n code: 'DK_INTERNAL',\n level: 'error',\n where: (d) =>\n d.slot ? { kind: 'operation', op: d.op, slot: d.slot } : { kind: 'operation', op: d.op },\n redact: { message: 'value' },\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERNAL,\n ),\n]\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/codes.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport { type DiagnosticMessages, defineDiagnostic } from './catalog'\nimport './process-guard'\n\ndeclare module './catalog' {\n interface DiagnosticCatalog {\n DK_INTERCEPTOR_FAILED: { interceptor: string; op: string; slot: string }\n DK_HOT_INTERCEPTOR: { interceptor: string; op: string }\n DK_OPERATION_CONFLICT: {\n op: string\n kind: string\n hot: boolean\n previousKind: string\n previousHot: boolean\n }\n DK_INTERNAL: {\n op: string\n problem: string\n /** The ctx `where` of the failed call (errorBoundary). */\n slot?: string\n /** `error.name` of the classified throw (errorBoundary). */\n errorName?: string\n /** The registry fallback applied (errorBoundary); invariant callers abort instead. */\n fallback?: string\n /**\n * `error.message` of the classified throw (errorBoundary). Engine messages embed row values\n * (`Cannot create property 'x' on string '<value>'`), so it is redacted as a cell value.\n */\n message?: unknown\n }\n }\n}\n\nconst ISSUES = 'https://github.com/Ink01101011/datakable/issues/new'\n\n/** Prefilled GitHub issue (§8.3): version, op, slot and error name — never values. */\nfunction issueLink(d: { op: string; slot?: string; errorName?: string }): string {\n const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ''}`\n const body = [\n `package: @datakable/core@${VERSION}`,\n `operation: ${d.op}`,\n `slot: ${d.slot ?? '-'}`,\n `error: ${d.errorName ?? '-'}`,\n '',\n 'diagnostic.toJSON():',\n '',\n 'table.explain():',\n '',\n ].join('\\n')\n return `${ISSUES}?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const coreMessages: DiagnosticMessages<\n 'DK_INTERCEPTOR_FAILED' | 'DK_HOT_INTERCEPTOR' | 'DK_OPERATION_CONFLICT' | 'DK_INTERNAL'\n> = {\n DK_INTERCEPTOR_FAILED: {\n problem: (d) => `interceptor \"${d.interceptor}\" threw on ${d.op} (at ${d.slot})`,\n impact: () => 'the interceptor was skipped for this call; the operation ran without it',\n fix: (d) => [\n `catch errors inside \"${d.interceptor}\" and always return next() (or a valid result)`,\n `see context.cause for the original error`,\n ],\n },\n DK_HOT_INTERCEPTOR: {\n problem: (d) =>\n `interceptor \"${d.interceptor}\" is attached to hot op \"${d.op}\" without hot: true`,\n // Measured by intercept/chain.bench.ts (Apple M4 Pro, Node 25.2.1): one pass-through\n // interceptor adds ~10 ns per call; spec §9.2 measured +15% when wrapping per cell. Kept inside\n // the builder (no module constant), so production bundles carry none of it.\n impact: () =>\n 'it runs once per column per pipeline run; measured cost: ~10 ns per call per ' +\n 'interceptor (+15% on a per-cell format call, spec §9.2) on top of the interceptor body, ' +\n 'which runs columns × pipeline runs times. It still runs (dev-only warning).',\n fix: (d) => [\n `set hot: true on \"${d.interceptor}\" to accept the cost, and never loop per cell inside it`,\n `attach \"${d.interceptor}\" to cold ops only (list them in \\`on\\` instead of '*' / '${d.op}')`,\n ],\n },\n DK_OPERATION_CONFLICT: {\n problem: (d) =>\n `operation \"${d.op}\" is already registered as ${d.previousKind}` +\n `${d.previousHot ? ' (hot)' : ''}; got ${d.kind}${d.hot ? ' (hot)' : ''}`,\n impact: () => 'the registration was rejected; the first one stays in effect',\n fix: (d) => [\n `register \"${d.op}\" with the same kind and hot flag (${d.previousKind}, hot: ${d.previousHot})`,\n `or give the new operation its own name, e.g. \"myplugin:${d.op.split(':').pop()}\"`,\n ],\n },\n DK_INTERNAL: {\n problem: (d) =>\n d.errorName\n ? `${d.errorName} thrown inside DataKable code during ${d.op}` +\n `${d.message === undefined ? '' : ` (message: ${String(d.message)})`} — likely a DataKable bug`\n : d.problem,\n impact: (d) => (d.fallback ? `${d.fallback}; the table keeps working` : 'the call was aborted'),\n fix: (d) => [\n `this is likely a DataKable bug or a plugin wiring error — report it: ${issueLink(d)}`,\n 'attach diagnostic.toJSON() and table.explain() (both redacted) to the issue',\n 'meanwhile, check plugins / interceptors registered on this operation',\n ],\n },\n}\n\n/** Codes owned by core plumbing (interceptor runtime, op registry). Features define their own. */\nexport const coreDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INTERCEPTOR_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERCEPTOR_FAILED,\n ),\n // Dev-only warning (never emitted in production): no definition there either.\n ...((process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic(\n {\n code: 'DK_HOT_INTERCEPTOR',\n level: 'warn',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_HOT_INTERCEPTOR,\n ),\n ]\n : []),\n defineDiagnostic(\n {\n code: 'DK_OPERATION_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'operation', op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_OPERATION_CONFLICT,\n ),\n // Core invariants (unknown op, boundary installed twice) and throws that errorBoundary traced to\n // DataKable's own code (top stack frame in a datakable path).\n defineDiagnostic(\n {\n code: 'DK_INTERNAL',\n level: 'error',\n where: (d) =>\n d.slot ? { kind: 'operation', op: d.op, slot: d.slot } : { kind: 'operation', op: d.op },\n redact: { message: 'value' },\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERNAL,\n ),\n]\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
1
  // src/diagnostics/codes.ts
2
2
  import { VERSION } from "../version.js";
3
3
  import { defineDiagnostic } from "./catalog.js";
4
+ import "./process-guard.js";
4
5
  var ISSUES = "https://github.com/Ink01101011/datakable/issues/new";
5
6
  function issueLink(d) {
6
7
  const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ""}`;
@@ -64,14 +65,17 @@ var coreDiagnostics = [
64
65
  },
65
66
  process.env.NODE_ENV !== "production" && coreMessages.DK_INTERCEPTOR_FAILED
66
67
  ),
67
- defineDiagnostic(
68
- {
69
- code: "DK_HOT_INTERCEPTOR",
70
- level: "warn",
71
- where: (d) => ({ kind: "interceptor", name: d.interceptor, op: d.op })
72
- },
73
- process.env.NODE_ENV !== "production" && coreMessages.DK_HOT_INTERCEPTOR
74
- ),
68
+ // Dev-only warning (never emitted in production): no definition there either.
69
+ ...process.env.NODE_ENV !== "production" ? [
70
+ defineDiagnostic(
71
+ {
72
+ code: "DK_HOT_INTERCEPTOR",
73
+ level: "warn",
74
+ where: (d) => ({ kind: "interceptor", name: d.interceptor, op: d.op })
75
+ },
76
+ process.env.NODE_ENV !== "production" && coreMessages.DK_HOT_INTERCEPTOR
77
+ )
78
+ ] : [],
75
79
  defineDiagnostic(
76
80
  {
77
81
  code: "DK_OPERATION_CONFLICT",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/codes.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport { type DiagnosticMessages, defineDiagnostic } from './catalog'\n\ndeclare module './catalog' {\n interface DiagnosticCatalog {\n DK_INTERCEPTOR_FAILED: { interceptor: string; op: string; slot: string }\n DK_HOT_INTERCEPTOR: { interceptor: string; op: string }\n DK_OPERATION_CONFLICT: {\n op: string\n kind: string\n hot: boolean\n previousKind: string\n previousHot: boolean\n }\n DK_INTERNAL: {\n op: string\n problem: string\n /** The ctx `where` of the failed call (errorBoundary). */\n slot?: string\n /** `error.name` of the classified throw (errorBoundary). */\n errorName?: string\n /** The registry fallback applied (errorBoundary); invariant callers abort instead. */\n fallback?: string\n /**\n * `error.message` of the classified throw (errorBoundary). Engine messages embed row values\n * (`Cannot create property 'x' on string '<value>'`), so it is redacted as a cell value.\n */\n message?: unknown\n }\n }\n}\n\nconst ISSUES = 'https://github.com/Ink01101011/datakable/issues/new'\n\n/** Prefilled GitHub issue (§8.3): version, op, slot and error name — never values. */\nfunction issueLink(d: { op: string; slot?: string; errorName?: string }): string {\n const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ''}`\n const body = [\n `package: @datakable/core@${VERSION}`,\n `operation: ${d.op}`,\n `slot: ${d.slot ?? '-'}`,\n `error: ${d.errorName ?? '-'}`,\n '',\n 'diagnostic.toJSON():',\n '',\n 'table.explain():',\n '',\n ].join('\\n')\n return `${ISSUES}?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const coreMessages: DiagnosticMessages<\n 'DK_INTERCEPTOR_FAILED' | 'DK_HOT_INTERCEPTOR' | 'DK_OPERATION_CONFLICT' | 'DK_INTERNAL'\n> = {\n DK_INTERCEPTOR_FAILED: {\n problem: (d) => `interceptor \"${d.interceptor}\" threw on ${d.op} (at ${d.slot})`,\n impact: () => 'the interceptor was skipped for this call; the operation ran without it',\n fix: (d) => [\n `catch errors inside \"${d.interceptor}\" and always return next() (or a valid result)`,\n `see context.cause for the original error`,\n ],\n },\n DK_HOT_INTERCEPTOR: {\n problem: (d) =>\n `interceptor \"${d.interceptor}\" is attached to hot op \"${d.op}\" without hot: true`,\n // Measured by intercept/chain.bench.ts (Apple M4 Pro, Node 25.2.1): one pass-through\n // interceptor adds ~10 ns per call; spec §9.2 measured +15% when wrapping per cell. Kept inside\n // the builder (no module constant), so production bundles carry none of it.\n impact: () =>\n 'it runs once per column per pipeline run; measured cost: ~10 ns per call per ' +\n 'interceptor (+15% on a per-cell format call, spec §9.2) on top of the interceptor body, ' +\n 'which runs columns × pipeline runs times. It still runs (dev-only warning).',\n fix: (d) => [\n `set hot: true on \"${d.interceptor}\" to accept the cost, and never loop per cell inside it`,\n `attach \"${d.interceptor}\" to cold ops only (list them in \\`on\\` instead of '*' / '${d.op}')`,\n ],\n },\n DK_OPERATION_CONFLICT: {\n problem: (d) =>\n `operation \"${d.op}\" is already registered as ${d.previousKind}` +\n `${d.previousHot ? ' (hot)' : ''}; got ${d.kind}${d.hot ? ' (hot)' : ''}`,\n impact: () => 'the registration was rejected; the first one stays in effect',\n fix: (d) => [\n `register \"${d.op}\" with the same kind and hot flag (${d.previousKind}, hot: ${d.previousHot})`,\n `or give the new operation its own name, e.g. \"myplugin:${d.op.split(':').pop()}\"`,\n ],\n },\n DK_INTERNAL: {\n problem: (d) =>\n d.errorName\n ? `${d.errorName} thrown inside DataKable code during ${d.op}` +\n `${d.message === undefined ? '' : ` (message: ${String(d.message)})`} — likely a DataKable bug`\n : d.problem,\n impact: (d) => (d.fallback ? `${d.fallback}; the table keeps working` : 'the call was aborted'),\n fix: (d) => [\n `this is likely a DataKable bug or a plugin wiring error — report it: ${issueLink(d)}`,\n 'attach diagnostic.toJSON() and table.explain() (both redacted) to the issue',\n 'meanwhile, check plugins / interceptors registered on this operation',\n ],\n },\n}\n\n/** Codes owned by core plumbing (interceptor runtime, op registry). Features define their own. */\nexport const coreDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INTERCEPTOR_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERCEPTOR_FAILED,\n ),\n defineDiagnostic(\n {\n code: 'DK_HOT_INTERCEPTOR',\n level: 'warn',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_HOT_INTERCEPTOR,\n ),\n defineDiagnostic(\n {\n code: 'DK_OPERATION_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'operation', op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_OPERATION_CONFLICT,\n ),\n // Core invariants (unknown op, boundary installed twice) and throws that errorBoundary traced to\n // DataKable's own code (top stack frame in a datakable path).\n defineDiagnostic(\n {\n code: 'DK_INTERNAL',\n level: 'error',\n where: (d) =>\n d.slot ? { kind: 'operation', op: d.op, slot: d.slot } : { kind: 'operation', op: d.op },\n redact: { message: 'value' },\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERNAL,\n ),\n]\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/codes.ts"],"sourcesContent":["import { VERSION } from '../version'\nimport { type DiagnosticMessages, defineDiagnostic } from './catalog'\nimport './process-guard'\n\ndeclare module './catalog' {\n interface DiagnosticCatalog {\n DK_INTERCEPTOR_FAILED: { interceptor: string; op: string; slot: string }\n DK_HOT_INTERCEPTOR: { interceptor: string; op: string }\n DK_OPERATION_CONFLICT: {\n op: string\n kind: string\n hot: boolean\n previousKind: string\n previousHot: boolean\n }\n DK_INTERNAL: {\n op: string\n problem: string\n /** The ctx `where` of the failed call (errorBoundary). */\n slot?: string\n /** `error.name` of the classified throw (errorBoundary). */\n errorName?: string\n /** The registry fallback applied (errorBoundary); invariant callers abort instead. */\n fallback?: string\n /**\n * `error.message` of the classified throw (errorBoundary). Engine messages embed row values\n * (`Cannot create property 'x' on string '<value>'`), so it is redacted as a cell value.\n */\n message?: unknown\n }\n }\n}\n\nconst ISSUES = 'https://github.com/Ink01101011/datakable/issues/new'\n\n/** Prefilled GitHub issue (§8.3): version, op, slot and error name — never values. */\nfunction issueLink(d: { op: string; slot?: string; errorName?: string }): string {\n const title = `DK_INTERNAL in ${d.op}${d.errorName ? ` (${d.errorName})` : ''}`\n const body = [\n `package: @datakable/core@${VERSION}`,\n `operation: ${d.op}`,\n `slot: ${d.slot ?? '-'}`,\n `error: ${d.errorName ?? '-'}`,\n '',\n 'diagnostic.toJSON():',\n '',\n 'table.explain():',\n '',\n ].join('\\n')\n return `${ISSUES}?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`\n}\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const coreMessages: DiagnosticMessages<\n 'DK_INTERCEPTOR_FAILED' | 'DK_HOT_INTERCEPTOR' | 'DK_OPERATION_CONFLICT' | 'DK_INTERNAL'\n> = {\n DK_INTERCEPTOR_FAILED: {\n problem: (d) => `interceptor \"${d.interceptor}\" threw on ${d.op} (at ${d.slot})`,\n impact: () => 'the interceptor was skipped for this call; the operation ran without it',\n fix: (d) => [\n `catch errors inside \"${d.interceptor}\" and always return next() (or a valid result)`,\n `see context.cause for the original error`,\n ],\n },\n DK_HOT_INTERCEPTOR: {\n problem: (d) =>\n `interceptor \"${d.interceptor}\" is attached to hot op \"${d.op}\" without hot: true`,\n // Measured by intercept/chain.bench.ts (Apple M4 Pro, Node 25.2.1): one pass-through\n // interceptor adds ~10 ns per call; spec §9.2 measured +15% when wrapping per cell. Kept inside\n // the builder (no module constant), so production bundles carry none of it.\n impact: () =>\n 'it runs once per column per pipeline run; measured cost: ~10 ns per call per ' +\n 'interceptor (+15% on a per-cell format call, spec §9.2) on top of the interceptor body, ' +\n 'which runs columns × pipeline runs times. It still runs (dev-only warning).',\n fix: (d) => [\n `set hot: true on \"${d.interceptor}\" to accept the cost, and never loop per cell inside it`,\n `attach \"${d.interceptor}\" to cold ops only (list them in \\`on\\` instead of '*' / '${d.op}')`,\n ],\n },\n DK_OPERATION_CONFLICT: {\n problem: (d) =>\n `operation \"${d.op}\" is already registered as ${d.previousKind}` +\n `${d.previousHot ? ' (hot)' : ''}; got ${d.kind}${d.hot ? ' (hot)' : ''}`,\n impact: () => 'the registration was rejected; the first one stays in effect',\n fix: (d) => [\n `register \"${d.op}\" with the same kind and hot flag (${d.previousKind}, hot: ${d.previousHot})`,\n `or give the new operation its own name, e.g. \"myplugin:${d.op.split(':').pop()}\"`,\n ],\n },\n DK_INTERNAL: {\n problem: (d) =>\n d.errorName\n ? `${d.errorName} thrown inside DataKable code during ${d.op}` +\n `${d.message === undefined ? '' : ` (message: ${String(d.message)})`} — likely a DataKable bug`\n : d.problem,\n impact: (d) => (d.fallback ? `${d.fallback}; the table keeps working` : 'the call was aborted'),\n fix: (d) => [\n `this is likely a DataKable bug or a plugin wiring error — report it: ${issueLink(d)}`,\n 'attach diagnostic.toJSON() and table.explain() (both redacted) to the issue',\n 'meanwhile, check plugins / interceptors registered on this operation',\n ],\n },\n}\n\n/** Codes owned by core plumbing (interceptor runtime, op registry). Features define their own. */\nexport const coreDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_INTERCEPTOR_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERCEPTOR_FAILED,\n ),\n // Dev-only warning (never emitted in production): no definition there either.\n ...((process.env.NODE_ENV !== \"production\")\n ? [\n defineDiagnostic(\n {\n code: 'DK_HOT_INTERCEPTOR',\n level: 'warn',\n where: (d) => ({ kind: 'interceptor', name: d.interceptor, op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_HOT_INTERCEPTOR,\n ),\n ]\n : []),\n defineDiagnostic(\n {\n code: 'DK_OPERATION_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'operation', op: d.op }),\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_OPERATION_CONFLICT,\n ),\n // Core invariants (unknown op, boundary installed twice) and throws that errorBoundary traced to\n // DataKable's own code (top stack frame in a datakable path).\n defineDiagnostic(\n {\n code: 'DK_INTERNAL',\n level: 'error',\n where: (d) =>\n d.slot ? { kind: 'operation', op: d.op, slot: d.slot } : { kind: 'operation', op: d.op },\n redact: { message: 'value' },\n },\n (process.env.NODE_ENV !== \"production\") && coreMessages.DK_INTERNAL,\n ),\n]\n"],"mappings":"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","names":[]}
@@ -25,6 +25,7 @@ __export(console_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(console_exports);
27
27
  var import_build = require("./build.cjs");
28
+ var import_process_guard = require("./process-guard.cjs");
28
29
  var ICON = { error: "\u2716", warn: "\u26A0", info: "\u2139" };
29
30
  function formatDiagnostic(d) {
30
31
  const header = `${ICON[d.level]} [DataKable ${d.context.table}] ${d.code}: ${d.problem}`;
@@ -60,14 +61,20 @@ function createConsoleSink(o) {
60
61
  return;
61
62
  }
62
63
  printed++;
63
- if (!(process.env.NODE_ENV !== "production") || !o.dev) {
64
- console.error(`[DataKable ${o.table}] ${d.code} at ${(0, import_build.formatWhere)(d.where)} \u2014 ${d.docs}`);
64
+ if (process.env.NODE_ENV !== "production" && o.dev) {
65
+ const { header, body } = formatDiagnostic(d);
66
+ const log = d.level === "error" ? "error" : d.level === "warn" ? "warn" : "info";
67
+ if (typeof console.groupCollapsed !== "function" || typeof console.groupEnd !== "function") {
68
+ console[log](`${header}
69
+ ${body}`);
70
+ return;
71
+ }
72
+ console.groupCollapsed(header);
73
+ console[log](body);
74
+ console.groupEnd();
65
75
  return;
66
76
  }
67
- const { header, body } = formatDiagnostic(d);
68
- console.groupCollapsed(header);
69
- (d.level === "error" ? console.error : d.level === "warn" ? console.warn : console.info)(body);
70
- console.groupEnd();
77
+ console.error(`[DataKable ${o.table}] ${d.code} at ${(0, import_build.formatWhere)(d.where)} \u2014 ${d.docs}`);
71
78
  };
72
79
  }
73
80
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/diagnostics/console.ts"],"sourcesContent":["import { formatWhere } from './build'\nimport type { Diagnostic } from './types'\n\nconst ICON = { error: '✖', warn: '⚠', info: 'ℹ' } as const\n\n/** The §8.1 console text: header + aligned body. Also usable by explain() and tests. */\nexport function formatDiagnostic(d: Diagnostic): { header: string; body: string } {\n const header = `${ICON[d.level]} [DataKable ${d.context.table}] ${d.code}: ${d.problem}`\n const lines = [` where ${formatWhere(d.where)}`]\n if (d.impact) lines.push(` impact ${d.impact}`)\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n const c = d.context\n lines.push(` context trigger=${c.trigger} run=${c.run} phase=${c.phase}`)\n return { header, body: lines.join('\\n') }\n}\n\nexport interface ConsoleSinkOptions {\n table: string\n dev: boolean\n rateLimit: { max: number; windowMs: number }\n now: () => number\n}\n\n/**\n * Default console sink (§8.2): dev prints one collapsed group per diagnostic; prod prints\n * error level only, on one line. A fixed-window rate limit protects the console; what it holds back\n * is still in getDiagnostics() and onDiagnostic.\n */\nexport function createConsoleSink(o: ConsoleSinkOptions): (d: Diagnostic) => void {\n let windowStart = Number.NEGATIVE_INFINITY\n let printed = 0\n let suppressed = 0\n // The grouped format is development-only: a production build prints one line (§8.2). `(process.env.NODE_ENV !== \"production\")` is\n // read inline so the bundler drops the grouped branch.\n return (d) => {\n if ((!(process.env.NODE_ENV !== \"production\") || !o.dev) && d.level !== 'error') return\n const t = o.now()\n if (t - windowStart >= o.rateLimit.windowMs) {\n if (suppressed > 0) {\n console.warn(\n `[DataKable ${o.table}] ${suppressed} diagnostics were not printed (rate limit); ` +\n 'read them with getDiagnostics() or onDiagnostic',\n )\n }\n windowStart = t\n printed = 0\n suppressed = 0\n }\n if (printed >= o.rateLimit.max) {\n suppressed++\n return\n }\n printed++\n if (!(process.env.NODE_ENV !== \"production\") || !o.dev) {\n console.error(`[DataKable ${o.table}] ${d.code} at ${formatWhere(d.where)} — ${d.docs}`)\n return\n }\n const { header, body } = formatDiagnostic(d)\n console.groupCollapsed(header)\n ;(d.level === 'error' ? console.error : d.level === 'warn' ? console.warn : console.info)(body)\n console.groupEnd()\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4B;AAG5B,IAAM,OAAO,EAAE,OAAO,UAAK,MAAM,UAAK,MAAM,SAAI;AAGzC,SAAS,iBAAiB,GAAiD;AAChF,QAAM,SAAS,GAAG,KAAK,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,KAAK,KAAK,EAAE,IAAI,KAAK,EAAE,OAAO;AACtF,QAAM,QAAQ,CAAC,iBAAa,0BAAY,EAAE,KAAK,CAAC,EAAE;AAClD,MAAI,EAAE,OAAQ,OAAM,KAAK,aAAa,EAAE,MAAM,EAAE;AAChD,IAAE,IAAI,QAAQ,CAAC,GAAG,MAAM;AACtB,UAAM,KAAK,GAAG,MAAM,IAAI,eAAe,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;AAAA,EACrE,CAAC;AACD,QAAM,KAAK,aAAa,EAAE,IAAI,EAAE;AAChC,QAAM,IAAI,EAAE;AACZ,QAAM,KAAK,qBAAqB,EAAE,OAAO,QAAQ,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE;AACzE,SAAO,EAAE,QAAQ,MAAM,MAAM,KAAK,IAAI,EAAE;AAC1C;AAcO,SAAS,kBAAkB,GAAgD;AAChF,MAAI,cAAc,OAAO;AACzB,MAAI,UAAU;AACd,MAAI,aAAa;AAGjB,SAAO,CAAC,MAAM;AACZ,SAAK,EAAE,QAAQ,IAAI,aAAa,iBAAiB,CAAC,EAAE,QAAQ,EAAE,UAAU,QAAS;AACjF,UAAM,IAAI,EAAE,IAAI;AAChB,QAAI,IAAI,eAAe,EAAE,UAAU,UAAU;AAC3C,UAAI,aAAa,GAAG;AAClB,gBAAQ;AAAA,UACN,cAAc,EAAE,KAAK,KAAK,UAAU;AAAA,QAEtC;AAAA,MACF;AACA,oBAAc;AACd,gBAAU;AACV,mBAAa;AAAA,IACf;AACA,QAAI,WAAW,EAAE,UAAU,KAAK;AAC9B;AACA;AAAA,IACF;AACA;AACA,QAAI,EAAE,QAAQ,IAAI,aAAa,iBAAiB,CAAC,EAAE,KAAK;AACtD,cAAQ,MAAM,cAAc,EAAE,KAAK,KAAK,EAAE,IAAI,WAAO,0BAAY,EAAE,KAAK,CAAC,WAAM,EAAE,IAAI,EAAE;AACvF;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,KAAK,IAAI,iBAAiB,CAAC;AAC3C,YAAQ,eAAe,MAAM;AAC5B,KAAC,EAAE,UAAU,UAAU,QAAQ,QAAQ,EAAE,UAAU,SAAS,QAAQ,OAAO,QAAQ,MAAM,IAAI;AAC9F,YAAQ,SAAS;AAAA,EACnB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/diagnostics/console.ts"],"sourcesContent":["import { formatWhere } from './build'\nimport './process-guard'\nimport type { Diagnostic } from './types'\n\nconst ICON = { error: '✖', warn: '⚠', info: 'ℹ' } as const\n\n/** The §8.1 console text: header + aligned body. Also usable by explain() and tests. */\nexport function formatDiagnostic(d: Diagnostic): { header: string; body: string } {\n const header = `${ICON[d.level]} [DataKable ${d.context.table}] ${d.code}: ${d.problem}`\n const lines = [` where ${formatWhere(d.where)}`]\n if (d.impact) lines.push(` impact ${d.impact}`)\n d.fix.forEach((f, i) => {\n lines.push(`${i === 0 ? ' fix ' : ' '}${i + 1}. ${f}`)\n })\n lines.push(` docs ${d.docs}`)\n const c = d.context\n lines.push(` context trigger=${c.trigger} run=${c.run} phase=${c.phase}`)\n return { header, body: lines.join('\\n') }\n}\n\nexport interface ConsoleSinkOptions {\n table: string\n dev: boolean\n rateLimit: { max: number; windowMs: number }\n now: () => number\n}\n\n/**\n * Default console sink (§8.2): dev prints one collapsed group per diagnostic; prod prints\n * error level only, on one line. A fixed-window rate limit protects the console; what it holds back\n * is still in getDiagnostics() and onDiagnostic.\n */\nexport function createConsoleSink(o: ConsoleSinkOptions): (d: Diagnostic) => void {\n let windowStart = Number.NEGATIVE_INFINITY\n let printed = 0\n let suppressed = 0\n // The grouped format is development-only: a production build prints one line (§8.2). `(process.env.NODE_ENV !== \"production\")` is\n // read inline so the bundler drops the grouped branch.\n return (d) => {\n if ((!(process.env.NODE_ENV !== \"production\") || !o.dev) && d.level !== 'error') return\n const t = o.now()\n if (t - windowStart >= o.rateLimit.windowMs) {\n if (suppressed > 0) {\n console.warn(\n `[DataKable ${o.table}] ${suppressed} diagnostics were not printed (rate limit); ` +\n 'read them with getDiagnostics() or onDiagnostic',\n )\n }\n windowStart = t\n printed = 0\n suppressed = 0\n }\n if (printed >= o.rateLimit.max) {\n suppressed++\n return\n }\n printed++\n // `(process.env.NODE_ENV !== \"production\") &&` first, not an early `if (!(process.env.NODE_ENV !== \"production\")) return`: a branch that folds to `false` is removed\n // while parsing, so a production bundle keeps no `formatDiagnostic` (code after a folded early\n // return is dropped only when printing, after tree shaking kept what it calls).\n if ((process.env.NODE_ENV !== \"production\") && o.dev) {\n const { header, body } = formatDiagnostic(d)\n const log = d.level === 'error' ? 'error' : d.level === 'warn' ? 'warn' : 'info'\n // Runtimes without console groups (the Edge Runtime: log/info/warn/error/debug only) get the\n // same text as one call.\n if (typeof console.groupCollapsed !== 'function' || typeof console.groupEnd !== 'function') {\n console[log](`${header}\\n${body}`)\n return\n }\n console.groupCollapsed(header)\n console[log](body)\n console.groupEnd()\n return\n }\n console.error(`[DataKable ${o.table}] ${d.code} at ${formatWhere(d.where)} — ${d.docs}`)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4B;AAC5B,2BAAO;AAGP,IAAM,OAAO,EAAE,OAAO,UAAK,MAAM,UAAK,MAAM,SAAI;AAGzC,SAAS,iBAAiB,GAAiD;AAChF,QAAM,SAAS,GAAG,KAAK,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,KAAK,KAAK,EAAE,IAAI,KAAK,EAAE,OAAO;AACtF,QAAM,QAAQ,CAAC,iBAAa,0BAAY,EAAE,KAAK,CAAC,EAAE;AAClD,MAAI,EAAE,OAAQ,OAAM,KAAK,aAAa,EAAE,MAAM,EAAE;AAChD,IAAE,IAAI,QAAQ,CAAC,GAAG,MAAM;AACtB,UAAM,KAAK,GAAG,MAAM,IAAI,eAAe,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;AAAA,EACrE,CAAC;AACD,QAAM,KAAK,aAAa,EAAE,IAAI,EAAE;AAChC,QAAM,IAAI,EAAE;AACZ,QAAM,KAAK,qBAAqB,EAAE,OAAO,QAAQ,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE;AACzE,SAAO,EAAE,QAAQ,MAAM,MAAM,KAAK,IAAI,EAAE;AAC1C;AAcO,SAAS,kBAAkB,GAAgD;AAChF,MAAI,cAAc,OAAO;AACzB,MAAI,UAAU;AACd,MAAI,aAAa;AAGjB,SAAO,CAAC,MAAM;AACZ,SAAK,EAAE,QAAQ,IAAI,aAAa,iBAAiB,CAAC,EAAE,QAAQ,EAAE,UAAU,QAAS;AACjF,UAAM,IAAI,EAAE,IAAI;AAChB,QAAI,IAAI,eAAe,EAAE,UAAU,UAAU;AAC3C,UAAI,aAAa,GAAG;AAClB,gBAAQ;AAAA,UACN,cAAc,EAAE,KAAK,KAAK,UAAU;AAAA,QAEtC;AAAA,MACF;AACA,oBAAc;AACd,gBAAU;AACV,mBAAa;AAAA,IACf;AACA,QAAI,WAAW,EAAE,UAAU,KAAK;AAC9B;AACA;AAAA,IACF;AACA;AAIA,QAAK,QAAQ,IAAI,aAAa,gBAAiB,EAAE,KAAK;AACpD,YAAM,EAAE,QAAQ,KAAK,IAAI,iBAAiB,CAAC;AAC3C,YAAM,MAAM,EAAE,UAAU,UAAU,UAAU,EAAE,UAAU,SAAS,SAAS;AAG1E,UAAI,OAAO,QAAQ,mBAAmB,cAAc,OAAO,QAAQ,aAAa,YAAY;AAC1F,gBAAQ,GAAG,EAAE,GAAG,MAAM;AAAA,EAAK,IAAI,EAAE;AACjC;AAAA,MACF;AACA,cAAQ,eAAe,MAAM;AAC7B,cAAQ,GAAG,EAAE,IAAI;AACjB,cAAQ,SAAS;AACjB;AAAA,IACF;AACA,YAAQ,MAAM,cAAc,EAAE,KAAK,KAAK,EAAE,IAAI,WAAO,0BAAY,EAAE,KAAK,CAAC,WAAM,EAAE,IAAI,EAAE;AAAA,EACzF;AACF;","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/diagnostics/console.ts
2
2
  import { formatWhere } from "./build.js";
3
+ import "./process-guard.js";
3
4
  var ICON = { error: "\u2716", warn: "\u26A0", info: "\u2139" };
4
5
  function formatDiagnostic(d) {
5
6
  const header = `${ICON[d.level]} [DataKable ${d.context.table}] ${d.code}: ${d.problem}`;
@@ -35,14 +36,20 @@ function createConsoleSink(o) {
35
36
  return;
36
37
  }
37
38
  printed++;
38
- if (!(process.env.NODE_ENV !== "production") || !o.dev) {
39
- console.error(`[DataKable ${o.table}] ${d.code} at ${formatWhere(d.where)} \u2014 ${d.docs}`);
39
+ if (process.env.NODE_ENV !== "production" && o.dev) {
40
+ const { header, body } = formatDiagnostic(d);
41
+ const log = d.level === "error" ? "error" : d.level === "warn" ? "warn" : "info";
42
+ if (typeof console.groupCollapsed !== "function" || typeof console.groupEnd !== "function") {
43
+ console[log](`${header}
44
+ ${body}`);
45
+ return;
46
+ }
47
+ console.groupCollapsed(header);
48
+ console[log](body);
49
+ console.groupEnd();
40
50
  return;
41
51
  }
42
- const { header, body } = formatDiagnostic(d);
43
- console.groupCollapsed(header);
44
- (d.level === "error" ? console.error : d.level === "warn" ? console.warn : console.info)(body);
45
- console.groupEnd();
52
+ console.error(`[DataKable ${o.table}] ${d.code} at ${formatWhere(d.where)} \u2014 ${d.docs}`);
46
53
  };
47
54
  }
48
55
  export {