@datakable/core 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/catalog/codes.cjs +12 -8
- package/dist/catalog/codes.cjs.map +1 -1
- package/dist/catalog/codes.js +12 -8
- package/dist/catalog/codes.js.map +1 -1
- package/dist/catalog/slots.cjs +10 -6
- package/dist/catalog/slots.cjs.map +1 -1
- package/dist/catalog/slots.js +10 -6
- package/dist/catalog/slots.js.map +1 -1
- package/dist/{codes-OsjAR3Wz.d.cts → codes-B9bCK73-.d.cts} +30 -4
- package/dist/{codes-OsjAR3Wz.d.ts → codes-B9bCK73-.d.ts} +30 -4
- package/dist/column/column-def.cjs.map +1 -1
- package/dist/column/get-value.cjs +6 -1
- package/dist/column/get-value.cjs.map +1 -1
- package/dist/column/get-value.js +6 -1
- package/dist/column/get-value.js.map +1 -1
- package/dist/column-filters.d.cts +2 -2
- package/dist/column-filters.d.ts +2 -2
- package/dist/diagnostics/build.cjs +4 -1
- package/dist/diagnostics/build.cjs.map +1 -1
- package/dist/diagnostics/build.js +4 -1
- package/dist/diagnostics/build.js.map +1 -1
- package/dist/diagnostics/catalog.cjs +11 -5
- package/dist/diagnostics/catalog.cjs.map +1 -1
- package/dist/diagnostics/catalog.js +10 -5
- package/dist/diagnostics/catalog.js.map +1 -1
- package/dist/diagnostics/codes.cjs +12 -8
- package/dist/diagnostics/codes.cjs.map +1 -1
- package/dist/diagnostics/codes.js +12 -8
- package/dist/diagnostics/codes.js.map +1 -1
- package/dist/diagnostics/console.cjs +13 -6
- package/dist/diagnostics/console.cjs.map +1 -1
- package/dist/diagnostics/console.js +13 -6
- package/dist/diagnostics/console.js.map +1 -1
- package/dist/diagnostics/env.cjs +1 -0
- package/dist/diagnostics/env.cjs.map +1 -1
- package/dist/diagnostics/env.js +1 -0
- package/dist/diagnostics/env.js.map +1 -1
- package/dist/diagnostics/explain-text.cjs +2 -1
- package/dist/diagnostics/explain-text.cjs.map +1 -1
- package/dist/diagnostics/explain-text.js +2 -1
- package/dist/diagnostics/explain-text.js.map +1 -1
- package/dist/diagnostics/explain.cjs +3 -2
- package/dist/diagnostics/explain.cjs.map +1 -1
- package/dist/diagnostics/explain.js +3 -2
- package/dist/diagnostics/explain.js.map +1 -1
- package/dist/diagnostics/hub.cjs +5 -4
- package/dist/diagnostics/hub.cjs.map +1 -1
- package/dist/diagnostics/hub.js +5 -4
- package/dist/diagnostics/hub.js.map +1 -1
- package/dist/diagnostics/process-guard.cjs +26 -0
- package/dist/diagnostics/process-guard.cjs.map +1 -0
- package/dist/diagnostics/process-guard.js +9 -0
- package/dist/diagnostics/process-guard.js.map +1 -0
- package/dist/diagnostics/prose.cjs +120 -0
- package/dist/diagnostics/prose.cjs.map +1 -0
- package/dist/diagnostics/prose.js +93 -0
- package/dist/diagnostics/prose.js.map +1 -0
- package/dist/diagnostics/redact.cjs +7 -4
- package/dist/diagnostics/redact.cjs.map +1 -1
- package/dist/diagnostics/redact.js +6 -4
- package/dist/diagnostics/redact.js.map +1 -1
- package/dist/diagnostics/trace-store.cjs +6 -0
- package/dist/diagnostics/trace-store.cjs.map +1 -1
- package/dist/diagnostics/trace-store.js +4 -0
- package/dist/diagnostics/trace-store.js.map +1 -1
- package/dist/diagnostics/tracer.cjs +74 -13
- package/dist/diagnostics/tracer.cjs.map +1 -1
- package/dist/diagnostics/tracer.js +75 -14
- package/dist/diagnostics/tracer.js.map +1 -1
- package/dist/diagnostics/types.cjs.map +1 -1
- package/dist/{explain-vkD2cewE.d.cts → explain-Q8Cg0cZy.d.cts} +13 -4
- package/dist/{explain-Dn99IQt0.d.ts → explain-i0OATi_W.d.ts} +13 -4
- package/dist/feature/codes.cjs +1 -0
- package/dist/feature/codes.cjs.map +1 -1
- package/dist/feature/codes.js +1 -0
- package/dist/feature/codes.js.map +1 -1
- package/dist/feature/install.cjs +4 -3
- package/dist/feature/install.cjs.map +1 -1
- package/dist/feature/install.js +4 -3
- package/dist/feature/install.js.map +1 -1
- package/dist/features/column-filters/codes.cjs +1 -0
- package/dist/features/column-filters/codes.cjs.map +1 -1
- package/dist/features/column-filters/codes.js +1 -0
- package/dist/features/column-filters/codes.js.map +1 -1
- package/dist/features/column-filters/column-filters.cjs +4 -2
- package/dist/features/column-filters/column-filters.cjs.map +1 -1
- package/dist/features/column-filters/column-filters.js +4 -2
- package/dist/features/column-filters/column-filters.js.map +1 -1
- package/dist/features/column-filters/match.cjs +5 -5
- package/dist/features/column-filters/match.cjs.map +1 -1
- package/dist/features/column-filters/match.js +5 -5
- package/dist/features/column-filters/match.js.map +1 -1
- package/dist/features/pagination/codes.cjs +1 -0
- package/dist/features/pagination/codes.cjs.map +1 -1
- package/dist/features/pagination/codes.js +1 -0
- package/dist/features/pagination/codes.js.map +1 -1
- package/dist/features/pagination/pagination.cjs +16 -13
- package/dist/features/pagination/pagination.cjs.map +1 -1
- package/dist/features/pagination/pagination.js +16 -13
- package/dist/features/pagination/pagination.js.map +1 -1
- package/dist/features/search/codes.cjs +1 -0
- package/dist/features/search/codes.cjs.map +1 -1
- package/dist/features/search/codes.js +1 -0
- package/dist/features/search/codes.js.map +1 -1
- package/dist/features/search/search-index.cjs +68 -15
- package/dist/features/search/search-index.cjs.map +1 -1
- package/dist/features/search/search-index.js +68 -15
- package/dist/features/search/search-index.js.map +1 -1
- package/dist/features/search/search.cjs +41 -9
- package/dist/features/search/search.cjs.map +1 -1
- package/dist/features/search/search.js +41 -9
- package/dist/features/search/search.js.map +1 -1
- package/dist/features/search/skipped-codes.cjs +45 -0
- package/dist/features/search/skipped-codes.cjs.map +1 -0
- package/dist/features/search/skipped-codes.js +20 -0
- package/dist/features/search/skipped-codes.js.map +1 -0
- package/dist/features/sorting/codes.cjs +1 -0
- package/dist/features/sorting/codes.cjs.map +1 -1
- package/dist/features/sorting/codes.js +1 -0
- package/dist/features/sorting/codes.js.map +1 -1
- package/dist/features/sorting/sorting.cjs +16 -20
- package/dist/features/sorting/sorting.cjs.map +1 -1
- package/dist/features/sorting/sorting.js +16 -20
- package/dist/features/sorting/sorting.js.map +1 -1
- package/dist/features/sorting/strategy.cjs +4 -4
- package/dist/features/sorting/strategy.cjs.map +1 -1
- package/dist/features/sorting/strategy.js +4 -4
- package/dist/features/sorting/strategy.js.map +1 -1
- package/dist/features/validity/codes.cjs +1 -0
- package/dist/features/validity/codes.cjs.map +1 -1
- package/dist/features/validity/codes.js +1 -0
- package/dist/features/validity/codes.js.map +1 -1
- package/dist/features/validity/validity.cjs +3 -3
- package/dist/features/validity/validity.cjs.map +1 -1
- package/dist/features/validity/validity.js +3 -3
- package/dist/features/validity/validity.js.map +1 -1
- package/dist/index.d.cts +13 -10
- package/dist/index.d.ts +13 -10
- package/dist/ingest/data-check-codes.cjs +4 -2
- package/dist/ingest/data-check-codes.cjs.map +1 -1
- package/dist/ingest/data-check-codes.js +4 -2
- package/dist/ingest/data-check-codes.js.map +1 -1
- package/dist/ingest/data-checks.cjs +1 -1
- package/dist/ingest/data-checks.cjs.map +1 -1
- package/dist/ingest/data-checks.js +1 -1
- package/dist/ingest/data-checks.js.map +1 -1
- package/dist/ingest/row-key-codes.cjs +1 -0
- package/dist/ingest/row-key-codes.cjs.map +1 -1
- package/dist/ingest/row-key-codes.js +1 -0
- package/dist/ingest/row-key-codes.js.map +1 -1
- package/dist/ingest/row-key.cjs +23 -20
- package/dist/ingest/row-key.cjs.map +1 -1
- package/dist/ingest/row-key.js +23 -20
- package/dist/ingest/row-key.js.map +1 -1
- package/dist/ingest/schema-codes.cjs +1 -0
- package/dist/ingest/schema-codes.cjs.map +1 -1
- package/dist/ingest/schema-codes.js +1 -0
- package/dist/ingest/schema-codes.js.map +1 -1
- package/dist/ingest/unique-fields.cjs +2 -2
- package/dist/ingest/unique-fields.cjs.map +1 -1
- package/dist/ingest/unique-fields.js +2 -2
- package/dist/ingest/unique-fields.js.map +1 -1
- package/dist/intercept/boundary.cjs +4 -2
- package/dist/intercept/boundary.cjs.map +1 -1
- package/dist/intercept/boundary.js +4 -2
- package/dist/intercept/boundary.js.map +1 -1
- package/dist/intercept/cell-batch.cjs +4 -4
- package/dist/intercept/cell-batch.cjs.map +1 -1
- package/dist/intercept/cell-batch.js +4 -4
- package/dist/intercept/cell-batch.js.map +1 -1
- package/dist/intercept/codes.cjs +1 -0
- package/dist/intercept/codes.cjs.map +1 -1
- package/dist/intercept/codes.js +1 -0
- package/dist/intercept/codes.js.map +1 -1
- package/dist/intercept/fallbacks.cjs +12 -20
- package/dist/intercept/fallbacks.cjs.map +1 -1
- package/dist/intercept/fallbacks.js +12 -20
- package/dist/intercept/fallbacks.js.map +1 -1
- package/dist/intercept/runtime.cjs +7 -9
- package/dist/intercept/runtime.cjs.map +1 -1
- package/dist/intercept/runtime.js +7 -9
- package/dist/intercept/runtime.js.map +1 -1
- package/dist/messages/index.cjs +4 -1
- package/dist/messages/index.cjs.map +1 -1
- package/dist/messages/index.js +4 -1
- package/dist/messages/index.js.map +1 -1
- package/dist/pagination.d.cts +2 -2
- package/dist/pagination.d.ts +2 -2
- package/dist/pipeline/codes.cjs +1 -0
- package/dist/pipeline/codes.cjs.map +1 -1
- package/dist/pipeline/codes.js +1 -0
- package/dist/pipeline/codes.js.map +1 -1
- package/dist/pipeline/column-cache.cjs +94 -76
- package/dist/pipeline/column-cache.cjs.map +1 -1
- package/dist/pipeline/column-cache.js +92 -75
- package/dist/pipeline/column-cache.js.map +1 -1
- package/dist/pipeline/dev-checks.cjs +1 -0
- package/dist/pipeline/dev-checks.cjs.map +1 -1
- package/dist/pipeline/dev-checks.js +1 -0
- package/dist/pipeline/dev-checks.js.map +1 -1
- package/dist/pipeline/env-codes.cjs +3 -2
- package/dist/pipeline/env-codes.cjs.map +1 -1
- package/dist/pipeline/env-codes.js +3 -2
- package/dist/pipeline/env-codes.js.map +1 -1
- package/dist/pipeline/perf-codes.cjs +1 -0
- package/dist/pipeline/perf-codes.cjs.map +1 -1
- package/dist/pipeline/perf-codes.js +1 -0
- package/dist/pipeline/perf-codes.js.map +1 -1
- package/dist/pipeline/runner.cjs +17 -9
- package/dist/pipeline/runner.cjs.map +1 -1
- package/dist/pipeline/runner.js +17 -9
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/pipeline/slice.cjs +3 -1
- package/dist/pipeline/slice.cjs.map +1 -1
- package/dist/pipeline/slice.js +3 -1
- package/dist/pipeline/slice.js.map +1 -1
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/{search-async-entry-v5JdEk5Y.d.cts → search-async-entry-CuLYPfY_.d.cts} +2 -2
- package/dist/{search-async-entry-DGSw2pDL.d.ts → search-async-entry-Cx7xpuVZ.d.ts} +2 -2
- package/dist/search-async.d.cts +3 -3
- package/dist/search-async.d.ts +3 -3
- package/dist/search.d.cts +8 -6
- package/dist/search.d.ts +8 -6
- package/dist/server.d.cts +3 -3
- package/dist/server.d.ts +3 -3
- package/dist/sorting.d.cts +2 -2
- package/dist/sorting.d.ts +2 -2
- package/dist/source/codes.cjs +1 -0
- package/dist/source/codes.cjs.map +1 -1
- package/dist/source/codes.js +1 -0
- package/dist/source/codes.js.map +1 -1
- package/dist/source/fetch-codes.cjs +1 -0
- package/dist/source/fetch-codes.cjs.map +1 -1
- package/dist/source/fetch-codes.js +1 -0
- package/dist/source/fetch-codes.js.map +1 -1
- package/dist/table/codes.cjs +1 -0
- package/dist/table/codes.cjs.map +1 -1
- package/dist/table/codes.js +1 -0
- package/dist/table/codes.js.map +1 -1
- package/dist/table/create-table.cjs +39 -9
- package/dist/table/create-table.cjs.map +1 -1
- package/dist/table/create-table.js +39 -9
- package/dist/table/create-table.js.map +1 -1
- package/dist/table/data-mutation.cjs +1 -0
- package/dist/table/data-mutation.cjs.map +1 -1
- package/dist/table/data-mutation.js +1 -0
- package/dist/table/data-mutation.js.map +1 -1
- package/dist/table/internals.cjs.map +1 -1
- package/dist/table/internals.js.map +1 -1
- package/dist/table/ssr-codes.cjs +1 -0
- package/dist/table/ssr-codes.cjs.map +1 -1
- package/dist/table/ssr-codes.js +1 -0
- package/dist/table/ssr-codes.js.map +1 -1
- package/dist/table/ssr.cjs +4 -1
- package/dist/table/ssr.cjs.map +1 -1
- package/dist/table/ssr.js +4 -1
- package/dist/table/ssr.js.map +1 -1
- package/dist/table/state-feedback.cjs +19 -0
- package/dist/table/state-feedback.cjs.map +1 -1
- package/dist/table/state-feedback.js +19 -0
- package/dist/table/state-feedback.js.map +1 -1
- package/dist/table/types.cjs.map +1 -1
- package/dist/table/types.js.map +1 -1
- package/dist/validity.d.cts +2 -2
- package/dist/validity.d.ts +2 -2
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/versioning/codes.cjs +18 -0
- package/dist/versioning/codes.cjs.map +1 -1
- package/dist/versioning/codes.js +18 -0
- package/dist/versioning/codes.js.map +1 -1
- package/dist/versioning/registry.cjs +4 -2
- package/dist/versioning/registry.cjs.map +1 -1
- package/dist/versioning/registry.js +4 -2
- package/dist/versioning/registry.js.map +1 -1
- package/package.json +39 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/diagnostics/trace-store.ts"],"sourcesContent":["import type { Trigger } from './types'\n\n/** One step of the traced run: counts (never rows) and its duration (§9.1: bounded). */\nexport interface TraceStep {\n readonly input: number\n readonly output: number\n readonly ms: number\n}\n\n/** What the tracer kept of the last `pipeline:run` of a table (the last run only). */\nexport interface Trace {\n readonly run: number\n readonly trigger: Trigger\n /** Duration of the whole run; `undefined` while it is in progress. */\n ms: number | undefined\n /** Steps that ran (a step served from its memo is absent), by feature name. */\n readonly steps: Map<string, TraceStep>\n /** The last `state:dispatch` (its `where`: a method / feature name) and its duration. */\n dispatch: { where: string; ms: number } | undefined\n}\n\n/** Kept apart from the tracer so `explain()` reads traces without bundling the tracer itself. */\nconst traces = new WeakMap<object, Trace>()\n\n/** The last traced run of `table`, if a tracer is installed. */\nexport const readTrace = (table: object): Trace | undefined => traces.get(table)\n\nexport const writeTrace = (table: object, trace: Trace): void => {\n traces.set(table, trace)\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/diagnostics/trace-store.ts"],"sourcesContent":["import type { ContextBase } from '../intercept/context'\nimport type { Trigger } from './types'\n\n/** One step of the traced run: counts (never rows) and its duration (§9.1: bounded). */\nexport interface TraceStep {\n readonly input: number\n readonly output: number\n /** With time-sliced work (#61): the runs that waited on it, this one, and the slices between. */\n readonly ms: number\n /** Time-sliced work (#61): the runs {@link ms} spans (absent when the step ran in one). */\n readonly runs?: number\n}\n\n/** What the tracer kept of the last `pipeline:run` of a table (the last run only). */\nexport interface Trace {\n readonly run: number\n readonly trigger: Trigger\n /** Duration of the whole run; `undefined` while it is in progress. */\n ms: number | undefined\n /** Steps that ran (a step served from its memo is absent), by feature name. */\n readonly steps: Map<string, TraceStep>\n /** The last `state:dispatch` (its `where`: a method / feature name) and its duration. */\n dispatch: { where: string; ms: number } | undefined\n}\n\n/**\n * Set on the `pipeline:step` ctx's `meta` by the runner when the step waits on time-sliced work\n * (its result comes in a later run).\n */\nexport const SLICED = 'sliced'\n\n/**\n * The tracer's (#61): the runner reports each slice of time-sliced work run between pipeline runs\n * (no step is timing it). Unset until a tracer is created.\n */\nexport const sliceTimer: {\n current?: ((base: ContextBase, feature: string, ms: number) => void) | undefined\n} = {}\n\n/** Kept apart from the tracer so `explain()` reads traces without bundling the tracer itself. */\nconst traces = new WeakMap<object, Trace>()\n\n/** The last traced run of `table`, if a tracer is installed. */\nexport const readTrace = (table: object): Trace | undefined => traces.get(table)\n\nexport const writeTrace = (table: object, trace: Trace): void => {\n traces.set(table, trace)\n}\n"],"mappings":";AA6BO,IAAM,SAAS;AAMf,IAAM,aAET,CAAC;AAGL,IAAM,SAAS,oBAAI,QAAuB;AAGnC,IAAM,YAAY,CAAC,UAAqC,OAAO,IAAI,KAAK;AAExE,IAAM,aAAa,CAAC,OAAe,UAAuB;AAC/D,SAAO,IAAI,OAAO,KAAK;AACzB;","names":[]}
|
|
@@ -30,16 +30,70 @@ var import_trace_store = require("./trace-store.cjs");
|
|
|
30
30
|
var STEP_BUDGET_MS = 50;
|
|
31
31
|
var now = () => typeof performance === "object" && typeof performance?.now === "function" ? performance.now() : Date.now();
|
|
32
32
|
var SEEN = "tracer";
|
|
33
|
+
var work = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function workOf(table) {
|
|
35
|
+
let w = work.get(table);
|
|
36
|
+
if (!w) {
|
|
37
|
+
w = { jobs: /* @__PURE__ */ new Map(), slots: /* @__PURE__ */ new Map() };
|
|
38
|
+
work.set(table, w);
|
|
39
|
+
}
|
|
40
|
+
return w;
|
|
41
|
+
}
|
|
42
|
+
function hint(emit, w, feature, stage, ms, rows, sliced) {
|
|
43
|
+
let slot;
|
|
44
|
+
let top = ["", 0];
|
|
45
|
+
for (const [where, t] of w.slots) {
|
|
46
|
+
if (t[1] > top[1]) {
|
|
47
|
+
slot = where;
|
|
48
|
+
top = t;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
w.slots.clear();
|
|
52
|
+
if (ms <= STEP_BUDGET_MS) return;
|
|
53
|
+
emit("DK_PERF_HINT", {
|
|
54
|
+
feature,
|
|
55
|
+
stage,
|
|
56
|
+
ms,
|
|
57
|
+
rows,
|
|
58
|
+
budget: STEP_BUDGET_MS,
|
|
59
|
+
...sliced ? { sliced } : {},
|
|
60
|
+
// Named when it took at least half of the time.
|
|
61
|
+
...slot && top[1] * 2 >= ms ? { slot, column: top[0], share: Math.min(1, top[1] / ms) } : {}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function onSlice(base, feature, ms) {
|
|
65
|
+
const table = base.table;
|
|
66
|
+
const trace = (0, import_trace_store.readTrace)(table);
|
|
67
|
+
if (!trace) return;
|
|
68
|
+
const w = workOf(table);
|
|
69
|
+
const job = w.jobs.get(feature);
|
|
70
|
+
if (job) job.ms += ms;
|
|
71
|
+
hint(base.emit, w, feature, job?.stage ?? "", ms, trace.steps.get(feature)?.input ?? 0, true);
|
|
72
|
+
}
|
|
33
73
|
function tracer() {
|
|
34
74
|
(0, import_catalog.keepDiagnostics)(import_perf_codes.perfDiagnostics);
|
|
75
|
+
import_trace_store.sliceTimer.current = onSlice;
|
|
35
76
|
return {
|
|
36
77
|
name: "tracer",
|
|
37
|
-
on: ["pipeline:run", "pipeline:step", "state:dispatch"],
|
|
78
|
+
on: ["pipeline:run", "pipeline:step", "state:dispatch", "column:batch"],
|
|
79
|
+
// column:batch: one timing per batch of a column, never per cell (§3.4).
|
|
80
|
+
hot: true,
|
|
38
81
|
intercept(ctx, next) {
|
|
39
82
|
const table = ctx.table;
|
|
40
83
|
if (ctx.meta.has(SEEN) || typeof table !== "object" || table === null) return next();
|
|
41
84
|
ctx.meta.set(SEEN, true);
|
|
42
85
|
const start = now();
|
|
86
|
+
if (ctx.op === "column:batch") {
|
|
87
|
+
try {
|
|
88
|
+
return next();
|
|
89
|
+
} finally {
|
|
90
|
+
const slots = workOf(table).slots;
|
|
91
|
+
const t = slots.get(ctx.where);
|
|
92
|
+
const ms = now() - start;
|
|
93
|
+
if (t) t[1] += ms;
|
|
94
|
+
else slots.set(ctx.where, [ctx.input.column, ms]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
43
97
|
if (ctx.op === "pipeline:run") {
|
|
44
98
|
const trace = {
|
|
45
99
|
run: ctx.run,
|
|
@@ -56,25 +110,32 @@ function tracer() {
|
|
|
56
110
|
}
|
|
57
111
|
}
|
|
58
112
|
if (ctx.op === "pipeline:step") {
|
|
113
|
+
const w = workOf(table);
|
|
114
|
+
w.slots.clear();
|
|
59
115
|
const out = next();
|
|
60
|
-
const
|
|
116
|
+
const own = now() - start;
|
|
117
|
+
const { feature, stage, indices } = ctx.input;
|
|
118
|
+
const pending = w.jobs;
|
|
119
|
+
const job = pending.get(feature);
|
|
120
|
+
let ms = own;
|
|
121
|
+
let runs;
|
|
122
|
+
if (ctx.meta.has(import_trace_store.SLICED)) {
|
|
123
|
+
pending.set(feature, { stage, ms: (job?.ms ?? 0) + own, runs: (job?.runs ?? 0) + 1 });
|
|
124
|
+
} else if (job) {
|
|
125
|
+
pending.delete(feature);
|
|
126
|
+
ms += job.ms;
|
|
127
|
+
runs = job.runs + 1;
|
|
128
|
+
}
|
|
61
129
|
const trace = (0, import_trace_store.readTrace)(table);
|
|
62
130
|
if (trace?.run === ctx.run) {
|
|
63
|
-
trace.steps.set(
|
|
64
|
-
input:
|
|
131
|
+
trace.steps.set(feature, {
|
|
132
|
+
input: indices.length,
|
|
65
133
|
output: out.length,
|
|
66
|
-
ms
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
if (ms > STEP_BUDGET_MS) {
|
|
70
|
-
ctx.emit("DK_PERF_HINT", {
|
|
71
|
-
feature: ctx.input.feature,
|
|
72
|
-
stage: ctx.input.stage,
|
|
73
134
|
ms,
|
|
74
|
-
|
|
75
|
-
budget: STEP_BUDGET_MS
|
|
135
|
+
...runs ? { runs } : {}
|
|
76
136
|
});
|
|
77
137
|
}
|
|
138
|
+
hint(ctx.emit, w, feature, stage, own, indices.length, false);
|
|
78
139
|
return out;
|
|
79
140
|
}
|
|
80
141
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/diagnostics/tracer.ts"],"sourcesContent":["import type { Interceptor } from '../intercept/define'\nimport { perfDiagnostics } from '../pipeline/perf-codes'\nimport { keepDiagnostics } from './catalog'\nimport { readTrace, type Trace, writeTrace } from './trace-store'\n\n/**\n * The longest a single step may take (ms): §9.3's longest-task budget. A traced step over it\n * reports `DK_PERF_HINT` (§8.3) with its numbers.\n */\nexport const STEP_BUDGET_MS = 50\n\ntype Traced = 'pipeline:run' | 'pipeline:step' | 'state:dispatch'\n\nconst now = (): number =>\n typeof performance === 'object' && typeof performance?.now === 'function'\n ? performance.now()\n : Date.now()\n\n/** Set on the ctx by the first tracer of a chain: a second one (user + built-in) only passes on. */\nconst SEEN = 'tracer'\n\n/**\n * The `tracer` interceptor (§3.4): timings and per-step counts of the last pipeline run, read by\n * `table.explain()`. `createTable` installs it in development; in production it is off (§8.2) and\n * opt-in with `createTable({ interceptors: [tracer()] })`. Keeps the last run only: counts, never\n * rows.\n */\nexport function tracer(): Interceptor<Traced> {\n // Inside the factory: a bundle that never creates a tracer drops the definition (§9.1).\n keepDiagnostics(perfDiagnostics)\n return {\n name: 'tracer',\n on: ['pipeline:run', 'pipeline:step', 'state:dispatch'],\n intercept(ctx, next) {\n const table = ctx.table\n if (ctx.meta.has(SEEN) || typeof table !== 'object' || table === null) return next()\n ctx.meta.set(SEEN, true)\n const start = now()\n if (ctx.op === 'pipeline:run') {\n const trace: Trace = {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch: readTrace(table)?.dispatch,\n }\n writeTrace(table, trace)\n try {\n return next()\n } finally {\n trace.ms = now() - start\n }\n }\n if (ctx.op === 'pipeline:step') {\n const out = next() as Uint32Array\n const
|
|
1
|
+
{"version":3,"sources":["../../src/diagnostics/tracer.ts"],"sourcesContent":["import type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport { perfDiagnostics } from '../pipeline/perf-codes'\nimport { keepDiagnostics } from './catalog'\nimport { readTrace, SLICED, sliceTimer, type Trace, writeTrace } from './trace-store'\nimport type { Emit } from './types'\n\n/**\n * The longest a single step may take (ms): §9.3's longest-task budget. A traced step over it\n * reports `DK_PERF_HINT` (§8.3) with its numbers.\n */\nexport const STEP_BUDGET_MS = 50\n\ntype Traced = 'pipeline:run' | 'pipeline:step' | 'state:dispatch' | 'column:batch'\n\nconst now = (): number =>\n typeof performance === 'object' && typeof performance?.now === 'function'\n ? performance.now()\n : Date.now()\n\n/** Set on the ctx by the first tracer of a chain: a second one (user + built-in) only passes on. */\nconst SEEN = 'tracer'\n\n/**\n * Per table, across runs (#61): a step waiting on time-sliced work, by feature: its stage, the\n * time of its runs so far plus the slices since, and how many runs. The run that gets the result\n * adds its own time and shows the total.\n */\ntype Job = { stage: string; ms: number; runs: number }\n\n/**\n * Per table: its steps waiting on sliced work, and the time of each `column:batch` slot\n * (`columns[0].format` → column id, ms) since the current step or slice began (#62).\n */\ntype Work = { jobs: Map<string, Job>; slots: Map<string, [string, number]> }\nconst work = new WeakMap<object, Work>()\n\nfunction workOf(table: object): Work {\n let w = work.get(table)\n if (!w) {\n w = { jobs: new Map(), slots: new Map() }\n work.set(table, w)\n }\n return w\n}\n\n/** DK_PERF_HINT for a task over the budget, with the slot most of its time went to (#62). */\nfunction hint(\n emit: Emit,\n w: Work,\n feature: string,\n stage: string,\n ms: number,\n rows: number,\n sliced: boolean,\n) {\n let slot: string | undefined\n let top: [string, number] = ['', 0]\n for (const [where, t] of w.slots) {\n if (t[1] > top[1]) {\n slot = where\n top = t\n }\n }\n w.slots.clear()\n if (ms <= STEP_BUDGET_MS) return\n emit('DK_PERF_HINT', {\n feature,\n stage,\n ms,\n rows,\n budget: STEP_BUDGET_MS,\n ...(sliced ? { sliced } : {}),\n // Named when it took at least half of the time.\n ...(slot && top[1] * 2 >= ms ? { slot, column: top[0], share: Math.min(1, top[1] / ms) } : {}),\n })\n}\n\n/** A slice of time-sliced work, run between pipeline runs (#61): its own task. */\nfunction onSlice(base: ContextBase, feature: string, ms: number) {\n const table = base.table as object\n const trace = readTrace(table)\n // Only for tables this tracer (or another) traces.\n if (!trace) return\n const w = workOf(table)\n const job = w.jobs.get(feature)\n if (job) job.ms += ms\n hint(base.emit, w, feature, job?.stage ?? '', ms, trace.steps.get(feature)?.input ?? 0, true)\n}\n\n/**\n * The `tracer` interceptor (§3.4): timings and per-step counts of the last pipeline run, read by\n * `table.explain()`. `createTable` installs it in development; in production it is off (§8.2) and\n * opt-in with `createTable({ interceptors: [tracer()] })`. Keeps the last run only: counts, never\n * rows. Time-sliced work (§9.1) is added to the step that waited on it (#61).\n */\nexport function tracer(): Interceptor<Traced> {\n // Inside the factory: a bundle that never creates a tracer drops the definition (§9.1).\n keepDiagnostics(perfDiagnostics)\n sliceTimer.current = onSlice\n return {\n name: 'tracer',\n on: ['pipeline:run', 'pipeline:step', 'state:dispatch', 'column:batch'],\n // column:batch: one timing per batch of a column, never per cell (§3.4).\n hot: true,\n intercept(ctx, next) {\n const table = ctx.table\n if (ctx.meta.has(SEEN) || typeof table !== 'object' || table === null) return next()\n ctx.meta.set(SEEN, true)\n const start = now()\n if (ctx.op === 'column:batch') {\n try {\n return next()\n } finally {\n const slots = workOf(table).slots\n const t = slots.get(ctx.where)\n const ms = now() - start\n if (t) t[1] += ms\n else slots.set(ctx.where, [ctx.input.column, ms])\n }\n }\n if (ctx.op === 'pipeline:run') {\n const trace: Trace = {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch: readTrace(table)?.dispatch,\n }\n writeTrace(table, trace)\n try {\n return next()\n } finally {\n trace.ms = now() - start\n }\n }\n if (ctx.op === 'pipeline:step') {\n const w = workOf(table)\n w.slots.clear()\n const out = next() as Uint32Array\n const own = now() - start\n const { feature, stage, indices } = ctx.input\n const pending = w.jobs\n const job = pending.get(feature)\n let ms = own\n let runs: number | undefined\n if (ctx.meta.has(SLICED)) {\n // Its result comes in a later run: that run shows the total.\n pending.set(feature, { stage, ms: (job?.ms ?? 0) + own, runs: (job?.runs ?? 0) + 1 })\n } else if (job) {\n pending.delete(feature)\n ms += job.ms\n runs = job.runs + 1\n }\n const trace = readTrace(table)\n if (trace?.run === ctx.run) {\n trace.steps.set(feature, {\n input: indices.length,\n output: out.length,\n ms,\n ...(runs ? { runs } : {}),\n })\n }\n // One task: the step's own time.\n hint(ctx.emit, w, feature, stage, own, indices.length, false)\n return out\n }\n try {\n return next()\n } finally {\n const dispatch = { where: ctx.where, ms: now() - start }\n const trace = readTrace(table)\n // Before the first (lazy) run: kept on an empty trace until a run carries it over.\n if (trace) trace.dispatch = dispatch\n else {\n writeTrace(table, {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch,\n })\n }\n }\n },\n } as Interceptor<Traced>\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,wBAAgC;AAChC,qBAAgC;AAChC,yBAAsE;AAO/D,IAAM,iBAAiB;AAI9B,IAAM,MAAM,MACV,OAAO,gBAAgB,YAAY,OAAO,aAAa,QAAQ,aAC3D,YAAY,IAAI,IAChB,KAAK,IAAI;AAGf,IAAM,OAAO;AAcb,IAAM,OAAO,oBAAI,QAAsB;AAEvC,SAAS,OAAO,OAAqB;AACnC,MAAI,IAAI,KAAK,IAAI,KAAK;AACtB,MAAI,CAAC,GAAG;AACN,QAAI,EAAE,MAAM,oBAAI,IAAI,GAAG,OAAO,oBAAI,IAAI,EAAE;AACxC,SAAK,IAAI,OAAO,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAGA,SAAS,KACP,MACA,GACA,SACA,OACA,IACA,MACA,QACA;AACA,MAAI;AACJ,MAAI,MAAwB,CAAC,IAAI,CAAC;AAClC,aAAW,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO;AAChC,QAAI,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG;AACjB,aAAO;AACP,YAAM;AAAA,IACR;AAAA,EACF;AACA,IAAE,MAAM,MAAM;AACd,MAAI,MAAM,eAAgB;AAC1B,OAAK,gBAAgB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA;AAAA,IAE3B,GAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC;AAAA,EAC9F,CAAC;AACH;AAGA,SAAS,QAAQ,MAAmB,SAAiB,IAAY;AAC/D,QAAM,QAAQ,KAAK;AACnB,QAAM,YAAQ,8BAAU,KAAK;AAE7B,MAAI,CAAC,MAAO;AACZ,QAAM,IAAI,OAAO,KAAK;AACtB,QAAM,MAAM,EAAE,KAAK,IAAI,OAAO;AAC9B,MAAI,IAAK,KAAI,MAAM;AACnB,OAAK,KAAK,MAAM,GAAG,SAAS,KAAK,SAAS,IAAI,IAAI,MAAM,MAAM,IAAI,OAAO,GAAG,SAAS,GAAG,IAAI;AAC9F;AAQO,SAAS,SAA8B;AAE5C,sCAAgB,iCAAe;AAC/B,gCAAW,UAAU;AACrB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,CAAC,gBAAgB,iBAAiB,kBAAkB,cAAc;AAAA;AAAA,IAEtE,KAAK;AAAA,IACL,UAAU,KAAK,MAAM;AACnB,YAAM,QAAQ,IAAI;AAClB,UAAI,IAAI,KAAK,IAAI,IAAI,KAAK,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO,KAAK;AACnF,UAAI,KAAK,IAAI,MAAM,IAAI;AACvB,YAAM,QAAQ,IAAI;AAClB,UAAI,IAAI,OAAO,gBAAgB;AAC7B,YAAI;AACF,iBAAO,KAAK;AAAA,QACd,UAAE;AACA,gBAAM,QAAQ,OAAO,KAAK,EAAE;AAC5B,gBAAM,IAAI,MAAM,IAAI,IAAI,KAAK;AAC7B,gBAAM,KAAK,IAAI,IAAI;AACnB,cAAI,EAAG,GAAE,CAAC,KAAK;AAAA,cACV,OAAM,IAAI,IAAI,OAAO,CAAC,IAAI,MAAM,QAAQ,EAAE,CAAC;AAAA,QAClD;AAAA,MACF;AACA,UAAI,IAAI,OAAO,gBAAgB;AAC7B,cAAM,QAAe;AAAA,UACnB,KAAK,IAAI;AAAA,UACT,SAAS,IAAI;AAAA,UACb,IAAI;AAAA,UACJ,OAAO,oBAAI,IAAI;AAAA,UACf,cAAU,8BAAU,KAAK,GAAG;AAAA,QAC9B;AACA,2CAAW,OAAO,KAAK;AACvB,YAAI;AACF,iBAAO,KAAK;AAAA,QACd,UAAE;AACA,gBAAM,KAAK,IAAI,IAAI;AAAA,QACrB;AAAA,MACF;AACA,UAAI,IAAI,OAAO,iBAAiB;AAC9B,cAAM,IAAI,OAAO,KAAK;AACtB,UAAE,MAAM,MAAM;AACd,cAAM,MAAM,KAAK;AACjB,cAAM,MAAM,IAAI,IAAI;AACpB,cAAM,EAAE,SAAS,OAAO,QAAQ,IAAI,IAAI;AACxC,cAAM,UAAU,EAAE;AAClB,cAAM,MAAM,QAAQ,IAAI,OAAO;AAC/B,YAAI,KAAK;AACT,YAAI;AACJ,YAAI,IAAI,KAAK,IAAI,yBAAM,GAAG;AAExB,kBAAQ,IAAI,SAAS,EAAE,OAAO,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,QAAQ,KAAK,EAAE,CAAC;AAAA,QACtF,WAAW,KAAK;AACd,kBAAQ,OAAO,OAAO;AACtB,gBAAM,IAAI;AACV,iBAAO,IAAI,OAAO;AAAA,QACpB;AACA,cAAM,YAAQ,8BAAU,KAAK;AAC7B,YAAI,OAAO,QAAQ,IAAI,KAAK;AAC1B,gBAAM,MAAM,IAAI,SAAS;AAAA,YACvB,OAAO,QAAQ;AAAA,YACf,QAAQ,IAAI;AAAA,YACZ;AAAA,YACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,UACzB,CAAC;AAAA,QACH;AAEA,aAAK,IAAI,MAAM,GAAG,SAAS,OAAO,KAAK,QAAQ,QAAQ,KAAK;AAC5D,eAAO;AAAA,MACT;AACA,UAAI;AACF,eAAO,KAAK;AAAA,MACd,UAAE;AACA,cAAM,WAAW,EAAE,OAAO,IAAI,OAAO,IAAI,IAAI,IAAI,MAAM;AACvD,cAAM,YAAQ,8BAAU,KAAK;AAE7B,YAAI,MAAO,OAAM,WAAW;AAAA,aACvB;AACH,6CAAW,OAAO;AAAA,YAChB,KAAK,IAAI;AAAA,YACT,SAAS,IAAI;AAAA,YACb,IAAI;AAAA,YACJ,OAAO,oBAAI,IAAI;AAAA,YACf;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,20 +1,74 @@
|
|
|
1
1
|
// src/diagnostics/tracer.ts
|
|
2
2
|
import { perfDiagnostics } from "../pipeline/perf-codes.js";
|
|
3
3
|
import { keepDiagnostics } from "./catalog.js";
|
|
4
|
-
import { readTrace, writeTrace } from "./trace-store.js";
|
|
4
|
+
import { readTrace, SLICED, sliceTimer, writeTrace } from "./trace-store.js";
|
|
5
5
|
var STEP_BUDGET_MS = 50;
|
|
6
6
|
var now = () => typeof performance === "object" && typeof performance?.now === "function" ? performance.now() : Date.now();
|
|
7
7
|
var SEEN = "tracer";
|
|
8
|
+
var work = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function workOf(table) {
|
|
10
|
+
let w = work.get(table);
|
|
11
|
+
if (!w) {
|
|
12
|
+
w = { jobs: /* @__PURE__ */ new Map(), slots: /* @__PURE__ */ new Map() };
|
|
13
|
+
work.set(table, w);
|
|
14
|
+
}
|
|
15
|
+
return w;
|
|
16
|
+
}
|
|
17
|
+
function hint(emit, w, feature, stage, ms, rows, sliced) {
|
|
18
|
+
let slot;
|
|
19
|
+
let top = ["", 0];
|
|
20
|
+
for (const [where, t] of w.slots) {
|
|
21
|
+
if (t[1] > top[1]) {
|
|
22
|
+
slot = where;
|
|
23
|
+
top = t;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
w.slots.clear();
|
|
27
|
+
if (ms <= STEP_BUDGET_MS) return;
|
|
28
|
+
emit("DK_PERF_HINT", {
|
|
29
|
+
feature,
|
|
30
|
+
stage,
|
|
31
|
+
ms,
|
|
32
|
+
rows,
|
|
33
|
+
budget: STEP_BUDGET_MS,
|
|
34
|
+
...sliced ? { sliced } : {},
|
|
35
|
+
// Named when it took at least half of the time.
|
|
36
|
+
...slot && top[1] * 2 >= ms ? { slot, column: top[0], share: Math.min(1, top[1] / ms) } : {}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function onSlice(base, feature, ms) {
|
|
40
|
+
const table = base.table;
|
|
41
|
+
const trace = readTrace(table);
|
|
42
|
+
if (!trace) return;
|
|
43
|
+
const w = workOf(table);
|
|
44
|
+
const job = w.jobs.get(feature);
|
|
45
|
+
if (job) job.ms += ms;
|
|
46
|
+
hint(base.emit, w, feature, job?.stage ?? "", ms, trace.steps.get(feature)?.input ?? 0, true);
|
|
47
|
+
}
|
|
8
48
|
function tracer() {
|
|
9
49
|
keepDiagnostics(perfDiagnostics);
|
|
50
|
+
sliceTimer.current = onSlice;
|
|
10
51
|
return {
|
|
11
52
|
name: "tracer",
|
|
12
|
-
on: ["pipeline:run", "pipeline:step", "state:dispatch"],
|
|
53
|
+
on: ["pipeline:run", "pipeline:step", "state:dispatch", "column:batch"],
|
|
54
|
+
// column:batch: one timing per batch of a column, never per cell (§3.4).
|
|
55
|
+
hot: true,
|
|
13
56
|
intercept(ctx, next) {
|
|
14
57
|
const table = ctx.table;
|
|
15
58
|
if (ctx.meta.has(SEEN) || typeof table !== "object" || table === null) return next();
|
|
16
59
|
ctx.meta.set(SEEN, true);
|
|
17
60
|
const start = now();
|
|
61
|
+
if (ctx.op === "column:batch") {
|
|
62
|
+
try {
|
|
63
|
+
return next();
|
|
64
|
+
} finally {
|
|
65
|
+
const slots = workOf(table).slots;
|
|
66
|
+
const t = slots.get(ctx.where);
|
|
67
|
+
const ms = now() - start;
|
|
68
|
+
if (t) t[1] += ms;
|
|
69
|
+
else slots.set(ctx.where, [ctx.input.column, ms]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
18
72
|
if (ctx.op === "pipeline:run") {
|
|
19
73
|
const trace = {
|
|
20
74
|
run: ctx.run,
|
|
@@ -31,25 +85,32 @@ function tracer() {
|
|
|
31
85
|
}
|
|
32
86
|
}
|
|
33
87
|
if (ctx.op === "pipeline:step") {
|
|
88
|
+
const w = workOf(table);
|
|
89
|
+
w.slots.clear();
|
|
34
90
|
const out = next();
|
|
35
|
-
const
|
|
91
|
+
const own = now() - start;
|
|
92
|
+
const { feature, stage, indices } = ctx.input;
|
|
93
|
+
const pending = w.jobs;
|
|
94
|
+
const job = pending.get(feature);
|
|
95
|
+
let ms = own;
|
|
96
|
+
let runs;
|
|
97
|
+
if (ctx.meta.has(SLICED)) {
|
|
98
|
+
pending.set(feature, { stage, ms: (job?.ms ?? 0) + own, runs: (job?.runs ?? 0) + 1 });
|
|
99
|
+
} else if (job) {
|
|
100
|
+
pending.delete(feature);
|
|
101
|
+
ms += job.ms;
|
|
102
|
+
runs = job.runs + 1;
|
|
103
|
+
}
|
|
36
104
|
const trace = readTrace(table);
|
|
37
105
|
if (trace?.run === ctx.run) {
|
|
38
|
-
trace.steps.set(
|
|
39
|
-
input:
|
|
106
|
+
trace.steps.set(feature, {
|
|
107
|
+
input: indices.length,
|
|
40
108
|
output: out.length,
|
|
41
|
-
ms
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
if (ms > STEP_BUDGET_MS) {
|
|
45
|
-
ctx.emit("DK_PERF_HINT", {
|
|
46
|
-
feature: ctx.input.feature,
|
|
47
|
-
stage: ctx.input.stage,
|
|
48
109
|
ms,
|
|
49
|
-
|
|
50
|
-
budget: STEP_BUDGET_MS
|
|
110
|
+
...runs ? { runs } : {}
|
|
51
111
|
});
|
|
52
112
|
}
|
|
113
|
+
hint(ctx.emit, w, feature, stage, own, indices.length, false);
|
|
53
114
|
return out;
|
|
54
115
|
}
|
|
55
116
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/diagnostics/tracer.ts"],"sourcesContent":["import type { Interceptor } from '../intercept/define'\nimport { perfDiagnostics } from '../pipeline/perf-codes'\nimport { keepDiagnostics } from './catalog'\nimport { readTrace, type Trace, writeTrace } from './trace-store'\n\n/**\n * The longest a single step may take (ms): §9.3's longest-task budget. A traced step over it\n * reports `DK_PERF_HINT` (§8.3) with its numbers.\n */\nexport const STEP_BUDGET_MS = 50\n\ntype Traced = 'pipeline:run' | 'pipeline:step' | 'state:dispatch'\n\nconst now = (): number =>\n typeof performance === 'object' && typeof performance?.now === 'function'\n ? performance.now()\n : Date.now()\n\n/** Set on the ctx by the first tracer of a chain: a second one (user + built-in) only passes on. */\nconst SEEN = 'tracer'\n\n/**\n * The `tracer` interceptor (§3.4): timings and per-step counts of the last pipeline run, read by\n * `table.explain()`. `createTable` installs it in development; in production it is off (§8.2) and\n * opt-in with `createTable({ interceptors: [tracer()] })`. Keeps the last run only: counts, never\n * rows.\n */\nexport function tracer(): Interceptor<Traced> {\n // Inside the factory: a bundle that never creates a tracer drops the definition (§9.1).\n keepDiagnostics(perfDiagnostics)\n return {\n name: 'tracer',\n on: ['pipeline:run', 'pipeline:step', 'state:dispatch'],\n intercept(ctx, next) {\n const table = ctx.table\n if (ctx.meta.has(SEEN) || typeof table !== 'object' || table === null) return next()\n ctx.meta.set(SEEN, true)\n const start = now()\n if (ctx.op === 'pipeline:run') {\n const trace: Trace = {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch: readTrace(table)?.dispatch,\n }\n writeTrace(table, trace)\n try {\n return next()\n } finally {\n trace.ms = now() - start\n }\n }\n if (ctx.op === 'pipeline:step') {\n const out = next() as Uint32Array\n const
|
|
1
|
+
{"version":3,"sources":["../../src/diagnostics/tracer.ts"],"sourcesContent":["import type { ContextBase } from '../intercept/context'\nimport type { Interceptor } from '../intercept/define'\nimport { perfDiagnostics } from '../pipeline/perf-codes'\nimport { keepDiagnostics } from './catalog'\nimport { readTrace, SLICED, sliceTimer, type Trace, writeTrace } from './trace-store'\nimport type { Emit } from './types'\n\n/**\n * The longest a single step may take (ms): §9.3's longest-task budget. A traced step over it\n * reports `DK_PERF_HINT` (§8.3) with its numbers.\n */\nexport const STEP_BUDGET_MS = 50\n\ntype Traced = 'pipeline:run' | 'pipeline:step' | 'state:dispatch' | 'column:batch'\n\nconst now = (): number =>\n typeof performance === 'object' && typeof performance?.now === 'function'\n ? performance.now()\n : Date.now()\n\n/** Set on the ctx by the first tracer of a chain: a second one (user + built-in) only passes on. */\nconst SEEN = 'tracer'\n\n/**\n * Per table, across runs (#61): a step waiting on time-sliced work, by feature: its stage, the\n * time of its runs so far plus the slices since, and how many runs. The run that gets the result\n * adds its own time and shows the total.\n */\ntype Job = { stage: string; ms: number; runs: number }\n\n/**\n * Per table: its steps waiting on sliced work, and the time of each `column:batch` slot\n * (`columns[0].format` → column id, ms) since the current step or slice began (#62).\n */\ntype Work = { jobs: Map<string, Job>; slots: Map<string, [string, number]> }\nconst work = new WeakMap<object, Work>()\n\nfunction workOf(table: object): Work {\n let w = work.get(table)\n if (!w) {\n w = { jobs: new Map(), slots: new Map() }\n work.set(table, w)\n }\n return w\n}\n\n/** DK_PERF_HINT for a task over the budget, with the slot most of its time went to (#62). */\nfunction hint(\n emit: Emit,\n w: Work,\n feature: string,\n stage: string,\n ms: number,\n rows: number,\n sliced: boolean,\n) {\n let slot: string | undefined\n let top: [string, number] = ['', 0]\n for (const [where, t] of w.slots) {\n if (t[1] > top[1]) {\n slot = where\n top = t\n }\n }\n w.slots.clear()\n if (ms <= STEP_BUDGET_MS) return\n emit('DK_PERF_HINT', {\n feature,\n stage,\n ms,\n rows,\n budget: STEP_BUDGET_MS,\n ...(sliced ? { sliced } : {}),\n // Named when it took at least half of the time.\n ...(slot && top[1] * 2 >= ms ? { slot, column: top[0], share: Math.min(1, top[1] / ms) } : {}),\n })\n}\n\n/** A slice of time-sliced work, run between pipeline runs (#61): its own task. */\nfunction onSlice(base: ContextBase, feature: string, ms: number) {\n const table = base.table as object\n const trace = readTrace(table)\n // Only for tables this tracer (or another) traces.\n if (!trace) return\n const w = workOf(table)\n const job = w.jobs.get(feature)\n if (job) job.ms += ms\n hint(base.emit, w, feature, job?.stage ?? '', ms, trace.steps.get(feature)?.input ?? 0, true)\n}\n\n/**\n * The `tracer` interceptor (§3.4): timings and per-step counts of the last pipeline run, read by\n * `table.explain()`. `createTable` installs it in development; in production it is off (§8.2) and\n * opt-in with `createTable({ interceptors: [tracer()] })`. Keeps the last run only: counts, never\n * rows. Time-sliced work (§9.1) is added to the step that waited on it (#61).\n */\nexport function tracer(): Interceptor<Traced> {\n // Inside the factory: a bundle that never creates a tracer drops the definition (§9.1).\n keepDiagnostics(perfDiagnostics)\n sliceTimer.current = onSlice\n return {\n name: 'tracer',\n on: ['pipeline:run', 'pipeline:step', 'state:dispatch', 'column:batch'],\n // column:batch: one timing per batch of a column, never per cell (§3.4).\n hot: true,\n intercept(ctx, next) {\n const table = ctx.table\n if (ctx.meta.has(SEEN) || typeof table !== 'object' || table === null) return next()\n ctx.meta.set(SEEN, true)\n const start = now()\n if (ctx.op === 'column:batch') {\n try {\n return next()\n } finally {\n const slots = workOf(table).slots\n const t = slots.get(ctx.where)\n const ms = now() - start\n if (t) t[1] += ms\n else slots.set(ctx.where, [ctx.input.column, ms])\n }\n }\n if (ctx.op === 'pipeline:run') {\n const trace: Trace = {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch: readTrace(table)?.dispatch,\n }\n writeTrace(table, trace)\n try {\n return next()\n } finally {\n trace.ms = now() - start\n }\n }\n if (ctx.op === 'pipeline:step') {\n const w = workOf(table)\n w.slots.clear()\n const out = next() as Uint32Array\n const own = now() - start\n const { feature, stage, indices } = ctx.input\n const pending = w.jobs\n const job = pending.get(feature)\n let ms = own\n let runs: number | undefined\n if (ctx.meta.has(SLICED)) {\n // Its result comes in a later run: that run shows the total.\n pending.set(feature, { stage, ms: (job?.ms ?? 0) + own, runs: (job?.runs ?? 0) + 1 })\n } else if (job) {\n pending.delete(feature)\n ms += job.ms\n runs = job.runs + 1\n }\n const trace = readTrace(table)\n if (trace?.run === ctx.run) {\n trace.steps.set(feature, {\n input: indices.length,\n output: out.length,\n ms,\n ...(runs ? { runs } : {}),\n })\n }\n // One task: the step's own time.\n hint(ctx.emit, w, feature, stage, own, indices.length, false)\n return out\n }\n try {\n return next()\n } finally {\n const dispatch = { where: ctx.where, ms: now() - start }\n const trace = readTrace(table)\n // Before the first (lazy) run: kept on an empty trace until a run carries it over.\n if (trace) trace.dispatch = dispatch\n else {\n writeTrace(table, {\n run: ctx.run,\n trigger: ctx.trigger,\n ms: undefined,\n steps: new Map(),\n dispatch,\n })\n }\n }\n },\n } as Interceptor<Traced>\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/diagnostics/types.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from './catalog'\n\nexport type { DiagnosticCode, DiagnosticDetails } from './catalog'\n\nexport type DiagnosticLevel = 'error' | 'warn' | 'info'\n\nexport type Trigger = 'init' | 'data-change' | 'setOptions' | 'hydrate' | 'fetch' | `api:${string}`\n\nexport type Phase = 'ssr' | 'hydrate' | 'client'\n\n/**\n * Where in the developer's code or data (§8.1). `interceptor` and `operation` are additions for\n * plugin-level codes the spec table does not locate (interceptors, op registry).\n */\nexport type Location =\n | { kind: 'column'; index: number; id: string }\n | { kind: 'feature'; name: string }\n | { kind: 'option'; path: string }\n | { kind: 'row'; index: number; key?: string }\n | { kind: 'interceptor'; name: string; op: string }\n | { kind: 'operation'; op: string; slot?: string }\n\n/** A summary of table state — never raw input (§8.1). */\nexport interface DiagnosticStateSummary {\n searchLength: number\n activeFilters: string[]\n sort: string[]\n page: number\n}\n\nexport interface DiagnosticContext {\n /** `options.name`, else `table#n`. */\n table: string\n /** Dev: creation site, e.g. `UsersPage.tsx:42`. */\n createdAt?: string\n /** `@datakable/core@x.y.z` */\n package: string\n trigger: Trigger\n /** Pipeline run id — joins explain() and other diagnostics. */\n run: number\n phase: Phase\n mode: Record<string, 'client' | 'server'>\n state: DiagnosticStateSummary\n occurrences: { count: number; first: number; last: number }\n /** e.g. `DK_SCHEMA_INVALID_ROW#run57` */\n causedBy?: string\n /** Original error (ES2022 cause). Reaches `onDiagnostic` only; `toJSON` and the console leave it out. */\n cause?: unknown\n}\n\nexport interface Diagnostic {\n /** Stable public API; never reused or redefined. */\n code: DiagnosticCode\n level: DiagnosticLevel\n /** What happened, instance-specific. Empty in production unless messages are opted in. */\n problem: string\n where: Location\n /** Consequence + the fallback the system applied. */\n impact: string\n /** Concrete fixes, best first. */\n fix: string[]\n /** `docs/errors.md#<code>` (+ query params in production). */\n docs: string\n /** The catalog details. Redaction (T26) rewrites them before any sink sees them. */\n details: Record<string, unknown>\n context: DiagnosticContext\n /** `code@where` — grouping key (dedupe, Sentry). */\n fingerprint: string\n /** Safe serializer: bigint, cycles and Errors never throw; `context.cause` is left out. */\n toJSON(): object\n}\n\n/** Per-call additions to the live context. */\nexport interface EmitOptions {\n cause?: unknown\n causedBy?: string\n run?: number\n trigger?: Trigger\n phase?: Phase\n}\n\n/** Emits a catalog diagnostic; `details` is typed by `code` (see `DiagnosticCatalog`). */\nexport type Emit = <C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n) => void\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/diagnostics/types.ts"],"sourcesContent":["import type { DiagnosticCode, DiagnosticDetails } from './catalog'\n\nexport type { DiagnosticCode, DiagnosticDetails } from './catalog'\n\nexport type DiagnosticLevel = 'error' | 'warn' | 'info'\n\nexport type Trigger = 'init' | 'data-change' | 'setOptions' | 'hydrate' | 'fetch' | `api:${string}`\n\nexport type Phase = 'ssr' | 'hydrate' | 'client'\n\n/**\n * Where in the developer's code or data (§8.1). `interceptor` and `operation` are additions for\n * plugin-level codes the spec table does not locate (interceptors, op registry).\n */\nexport type Location =\n | { kind: 'column'; index: number; id: string }\n | { kind: 'feature'; name: string }\n | { kind: 'option'; path: string }\n | { kind: 'row'; index: number; key?: string }\n | { kind: 'interceptor'; name: string; op: string }\n | { kind: 'operation'; op: string; slot?: string }\n\n/** A summary of table state — never raw input (§8.1). */\nexport interface DiagnosticStateSummary {\n searchLength: number\n activeFilters: string[]\n sort: string[]\n page: number\n}\n\nexport interface DiagnosticContext {\n /** `options.name`, else `table#n`. */\n table: string\n /** Dev: creation site, e.g. `UsersPage.tsx:42`. */\n createdAt?: string\n /** `@datakable/core@x.y.z` */\n package: string\n trigger: Trigger\n /** Pipeline run id — joins explain() and other diagnostics. */\n run: number\n phase: Phase\n mode: Record<string, 'client' | 'server'>\n state: DiagnosticStateSummary\n occurrences: { count: number; first: number; last: number }\n /** e.g. `DK_SCHEMA_INVALID_ROW#run57` */\n causedBy?: string\n /** Original error (ES2022 cause). Reaches `onDiagnostic` only; `toJSON` and the console leave it out. */\n cause?: unknown\n}\n\nexport interface Diagnostic {\n /** Stable public API; never reused or redefined. */\n code: DiagnosticCode\n level: DiagnosticLevel\n /** What happened, instance-specific. Empty in production unless messages are opted in. */\n problem: string\n where: Location\n /** Consequence + the fallback the system applied. */\n impact: string\n /** Concrete fixes, best first. */\n fix: string[]\n /** `docs/errors.md#<code>` (+ query params in production). */\n docs: string\n /** The catalog details. Redaction (T26) rewrites them before any sink sees them. */\n details: Record<string, unknown>\n context: DiagnosticContext\n /** `code@where` — grouping key (dedupe, Sentry). */\n fingerprint: string\n /** Safe serializer: bigint, cycles and Errors never throw; `context.cause` is left out. */\n toJSON(): object\n}\n\n/** Per-call additions to the live context. */\nexport interface EmitOptions {\n cause?: unknown\n causedBy?: string\n run?: number\n trigger?: Trigger\n phase?: Phase\n /**\n * Not printed by the default console sink (another table printed it in this process, #38). Still\n * stored and delivered to `onDiagnostic`.\n */\n quiet?: boolean\n}\n\n/** Emits a catalog diagnostic; `details` is typed by `code` (see `DiagnosticCatalog`). */\nexport type Emit = <C extends DiagnosticCode>(\n code: C,\n details: DiagnosticDetails<C>,\n options?: EmitOptions,\n) => void\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as RowIssue, E as Emit, a as EmitOptions, D as Diagnostic, T as Trigger, P as Phase, I as InvalidRowsMode, F as FlaggedRow, b as Row, c as DiagnosticCode, d as DiagnosticDetails, e as TableSchema, f as RedactLevel, g as RedactFn, h as InvalidRow, i as RowKey } from './codes-
|
|
1
|
+
import { R as RowIssue, E as Emit, a as EmitOptions, D as Diagnostic, T as Trigger, P as Phase, I as InvalidRowsMode, F as FlaggedRow, b as Row, c as DiagnosticCode, d as DiagnosticDetails, e as TableSchema, f as RedactLevel, g as RedactFn, h as InvalidRow, i as RowKey } from './codes-B9bCK73-.cjs';
|
|
2
2
|
|
|
3
3
|
/** Runtime types an option can accept (`'array'` is checked before `'object'`). */
|
|
4
4
|
type OptionType = 'string' | 'number' | 'boolean' | 'function' | 'object' | 'array';
|
|
@@ -130,7 +130,11 @@ type SortAlgorithm = 'auto' | 'tim' | 'radix' | 'counting' | 'native' | ((indice
|
|
|
130
130
|
/** Fields shared by every column whose value type is `V`. */
|
|
131
131
|
type BaseColumn<T, V, R> = {
|
|
132
132
|
header?: string;
|
|
133
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Search weight. Once any column declares one, only weighted columns are searched; when none
|
|
135
|
+
* does, the columns of strings, numbers and booleans are (a date, array or object column needs a
|
|
136
|
+
* weight to be searched).
|
|
137
|
+
*/
|
|
134
138
|
search?: number;
|
|
135
139
|
sortable?: boolean;
|
|
136
140
|
sortFn?: (a: V, b: V) => number;
|
|
@@ -635,7 +639,7 @@ interface TableDiagnosticsOptions {
|
|
|
635
639
|
/**
|
|
636
640
|
* Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level
|
|
637
641
|
* (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never
|
|
638
|
-
* logged; a
|
|
642
|
+
* logged; a `rowKey` path into a sensitive column is hashed at every level.
|
|
639
643
|
*/
|
|
640
644
|
redact?: RedactLevel | RedactFn;
|
|
641
645
|
}
|
|
@@ -950,8 +954,13 @@ interface ExplainStep {
|
|
|
950
954
|
readonly cached: boolean;
|
|
951
955
|
/** The step threw or returned a bad output and passed its input through. */
|
|
952
956
|
readonly failed: boolean;
|
|
953
|
-
/**
|
|
957
|
+
/**
|
|
958
|
+
* Duration (tracer only, development or `interceptors: [tracer()]`). A step that waited on
|
|
959
|
+
* time-sliced work: the total of its runs and of the slices between them (#61).
|
|
960
|
+
*/
|
|
954
961
|
readonly ms?: number;
|
|
962
|
+
/** Time-sliced work (#61): the runs {@link ms} spans; absent when the step ran in one. */
|
|
963
|
+
readonly runs?: number;
|
|
955
964
|
}
|
|
956
965
|
/** What `table.explain()` returns (§8.4); `String(e)` is the console summary. */
|
|
957
966
|
interface TableExplanation {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as RowIssue, E as Emit, a as EmitOptions, D as Diagnostic, T as Trigger, P as Phase, I as InvalidRowsMode, F as FlaggedRow, b as Row, c as DiagnosticCode, d as DiagnosticDetails, e as TableSchema, f as RedactLevel, g as RedactFn, h as InvalidRow, i as RowKey } from './codes-
|
|
1
|
+
import { R as RowIssue, E as Emit, a as EmitOptions, D as Diagnostic, T as Trigger, P as Phase, I as InvalidRowsMode, F as FlaggedRow, b as Row, c as DiagnosticCode, d as DiagnosticDetails, e as TableSchema, f as RedactLevel, g as RedactFn, h as InvalidRow, i as RowKey } from './codes-B9bCK73-.js';
|
|
2
2
|
|
|
3
3
|
/** Runtime types an option can accept (`'array'` is checked before `'object'`). */
|
|
4
4
|
type OptionType = 'string' | 'number' | 'boolean' | 'function' | 'object' | 'array';
|
|
@@ -130,7 +130,11 @@ type SortAlgorithm = 'auto' | 'tim' | 'radix' | 'counting' | 'native' | ((indice
|
|
|
130
130
|
/** Fields shared by every column whose value type is `V`. */
|
|
131
131
|
type BaseColumn<T, V, R> = {
|
|
132
132
|
header?: string;
|
|
133
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Search weight. Once any column declares one, only weighted columns are searched; when none
|
|
135
|
+
* does, the columns of strings, numbers and booleans are (a date, array or object column needs a
|
|
136
|
+
* weight to be searched).
|
|
137
|
+
*/
|
|
134
138
|
search?: number;
|
|
135
139
|
sortable?: boolean;
|
|
136
140
|
sortFn?: (a: V, b: V) => number;
|
|
@@ -635,7 +639,7 @@ interface TableDiagnosticsOptions {
|
|
|
635
639
|
/**
|
|
636
640
|
* Redaction (§8.2): a level, or a custom `redact(path, value)` on top of the default level
|
|
637
641
|
* (`'keys'` in development, `'safe'` in production). Columns with `sensitive: true` are never
|
|
638
|
-
* logged; a
|
|
642
|
+
* logged; a `rowKey` path into a sensitive column is hashed at every level.
|
|
639
643
|
*/
|
|
640
644
|
redact?: RedactLevel | RedactFn;
|
|
641
645
|
}
|
|
@@ -950,8 +954,13 @@ interface ExplainStep {
|
|
|
950
954
|
readonly cached: boolean;
|
|
951
955
|
/** The step threw or returned a bad output and passed its input through. */
|
|
952
956
|
readonly failed: boolean;
|
|
953
|
-
/**
|
|
957
|
+
/**
|
|
958
|
+
* Duration (tracer only, development or `interceptors: [tracer()]`). A step that waited on
|
|
959
|
+
* time-sliced work: the total of its runs and of the slices between them (#61).
|
|
960
|
+
*/
|
|
954
961
|
readonly ms?: number;
|
|
962
|
+
/** Time-sliced work (#61): the runs {@link ms} spans; absent when the step ran in one. */
|
|
963
|
+
readonly runs?: number;
|
|
955
964
|
}
|
|
956
965
|
/** What `table.explain()` returns (§8.4); `String(e)` is the console summary. */
|
|
957
966
|
interface TableExplanation {
|
package/dist/feature/codes.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(codes_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(codes_exports);
|
|
27
27
|
var import_catalog = require("../diagnostics/catalog.cjs");
|
|
28
|
+
var import_process_guard = require("../diagnostics/process-guard.cjs");
|
|
28
29
|
var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
29
30
|
var featureMessages = {
|
|
30
31
|
DK_FEATURE_DUPLICATE: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/feature/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FEATURE_DUPLICATE: {\n feature: string\n /** Positions in `options.features` of the first and the repeated entry. */\n first: number\n second: number\n }\n DK_FEATURE_API_CONFLICT: {\n method: string\n /** The feature whose API was installed last (the one rejected). */\n feature: string\n /** Who already owns `method`: another feature's name, or `null` for the table itself. */\n owner: string | null\n }\n DK_FEATURE_FAILED: {\n feature: string\n /** The hook that threw. `rowModel` is reported by the pipeline runner (T09). */\n hook: 'initialState' | 'createApi' | 'rowModel'\n /** The fallback that was applied. */\n fallback: string\n /** `error.name` only; the full error is `context.cause`. */\n errorName?: string\n /**\n * Dev: a method of a feature whose `createApi()` threw was called later (`hook: 'createApi'`);\n * `feature` names every feature whose API is missing.\n */\n method?: string\n }\n }\n}\n\nconst capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const featureMessages: DiagnosticMessages<\n 'DK_FEATURE_DUPLICATE' | 'DK_FEATURE_API_CONFLICT' | 'DK_FEATURE_FAILED'\n> = {\n DK_FEATURE_DUPLICATE: {\n problem: (d) =>\n `feature \"${d.feature}\" is installed twice (features[${d.first}] and features[${d.second}]); ` +\n 'a feature name is its state slice key and must be unique per table',\n impact: () => 'the table was not created',\n fix: (d) => [\n `remove features[${d.second}] (or features[${d.first}]): pass \"${d.feature}\" once, with all ` +\n 'its options in one call',\n 'for two different features that share a name, rename one: defineFeature({ name: ... })',\n ],\n },\n DK_FEATURE_API_CONFLICT: {\n problem: (d) =>\n `feature \"${d.feature}\" exposes \"${d.method}\", which ` +\n (d.owner === null\n ? 'the table already has (a core table method)'\n : `feature \"${d.owner}\" already exposes`),\n impact: () => 'the table was not created: one of the two methods would be unreachable',\n fix: (d) => [\n `namespace the method in feature \"${d.feature}\": ` +\n `${d.feature}${capitalize(d.method)}() or ${d.feature}.${d.method}()`,\n d.owner === null\n ? `core table methods cannot be replaced; use an interceptor (e.g. on state:dispatch) to ` +\n `change what table.${d.method}() does`\n : `or rename it in feature \"${d.owner}\", or install only one of the two features`,\n ],\n },\n DK_FEATURE_FAILED: {\n problem: (d) =>\n d.method === undefined\n ? `feature \"${d.feature}\" ${d.hook}() threw${d.errorName ? ` ${d.errorName}` : ''}`\n : `table.${d.method}() was called, but it does not exist: createApi() threw at creation ` +\n `for feature \"${d.feature}\", so its API is missing`,\n impact: (d) => `${d.fallback}; the rest of the table is unaffected`,\n fix: (d) => [\n ...(d.method === undefined\n ? []\n : [\n `see the first DK_FEATURE_FAILED of feature \"${d.feature}\" (trigger init): its ` +\n 'context.cause is the createApi() error',\n ]),\n `fix ${d.hook}() of feature \"${d.feature}\": it must not throw` +\n (d.hook === 'rowModel' ? ' (return its input indices when it cannot filter)' : ''),\n 'the original error is context.cause (passed to onDiagnostic)',\n ],\n },\n}\n\n/** Codes owned by the feature contract (§3.3, §8.3). */\nexport const featureDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FEATURE_DUPLICATE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_DUPLICATE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_API_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_API_CONFLICT,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_FAILED,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;
|
|
1
|
+
{"version":3,"sources":["../../src/feature/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FEATURE_DUPLICATE: {\n feature: string\n /** Positions in `options.features` of the first and the repeated entry. */\n first: number\n second: number\n }\n DK_FEATURE_API_CONFLICT: {\n method: string\n /** The feature whose API was installed last (the one rejected). */\n feature: string\n /** Who already owns `method`: another feature's name, or `null` for the table itself. */\n owner: string | null\n }\n DK_FEATURE_FAILED: {\n feature: string\n /** The hook that threw. `rowModel` is reported by the pipeline runner (T09). */\n hook: 'initialState' | 'createApi' | 'rowModel'\n /** The fallback that was applied. */\n fallback: string\n /** `error.name` only; the full error is `context.cause`. */\n errorName?: string\n /**\n * Dev: a method of a feature whose `createApi()` threw was called later (`hook: 'createApi'`);\n * `feature` names every feature whose API is missing.\n */\n method?: string\n }\n }\n}\n\nconst capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const featureMessages: DiagnosticMessages<\n 'DK_FEATURE_DUPLICATE' | 'DK_FEATURE_API_CONFLICT' | 'DK_FEATURE_FAILED'\n> = {\n DK_FEATURE_DUPLICATE: {\n problem: (d) =>\n `feature \"${d.feature}\" is installed twice (features[${d.first}] and features[${d.second}]); ` +\n 'a feature name is its state slice key and must be unique per table',\n impact: () => 'the table was not created',\n fix: (d) => [\n `remove features[${d.second}] (or features[${d.first}]): pass \"${d.feature}\" once, with all ` +\n 'its options in one call',\n 'for two different features that share a name, rename one: defineFeature({ name: ... })',\n ],\n },\n DK_FEATURE_API_CONFLICT: {\n problem: (d) =>\n `feature \"${d.feature}\" exposes \"${d.method}\", which ` +\n (d.owner === null\n ? 'the table already has (a core table method)'\n : `feature \"${d.owner}\" already exposes`),\n impact: () => 'the table was not created: one of the two methods would be unreachable',\n fix: (d) => [\n `namespace the method in feature \"${d.feature}\": ` +\n `${d.feature}${capitalize(d.method)}() or ${d.feature}.${d.method}()`,\n d.owner === null\n ? `core table methods cannot be replaced; use an interceptor (e.g. on state:dispatch) to ` +\n `change what table.${d.method}() does`\n : `or rename it in feature \"${d.owner}\", or install only one of the two features`,\n ],\n },\n DK_FEATURE_FAILED: {\n problem: (d) =>\n d.method === undefined\n ? `feature \"${d.feature}\" ${d.hook}() threw${d.errorName ? ` ${d.errorName}` : ''}`\n : `table.${d.method}() was called, but it does not exist: createApi() threw at creation ` +\n `for feature \"${d.feature}\", so its API is missing`,\n impact: (d) => `${d.fallback}; the rest of the table is unaffected`,\n fix: (d) => [\n ...(d.method === undefined\n ? []\n : [\n `see the first DK_FEATURE_FAILED of feature \"${d.feature}\" (trigger init): its ` +\n 'context.cause is the createApi() error',\n ]),\n `fix ${d.hook}() of feature \"${d.feature}\": it must not throw` +\n (d.hook === 'rowModel' ? ' (return its input indices when it cannot filter)' : ''),\n 'the original error is context.cause (passed to onDiagnostic)',\n ],\n },\n}\n\n/** Codes owned by the feature contract (§3.3, §8.3). */\nexport const featureDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FEATURE_DUPLICATE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_DUPLICATE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_API_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_API_CONFLICT,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_FAILED,\n ),\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA0D;AAC1D,2BAAO;AAkCP,IAAM,aAAa,CAAC,MAAc,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAGhE,IAAM,kBAET;AAAA,EACF,sBAAsB;AAAA,IACpB,SAAS,CAAC,MACR,YAAY,EAAE,OAAO,kCAAkC,EAAE,KAAK,kBAAkB,EAAE,MAAM;AAAA,IAE1F,QAAQ,MAAM;AAAA,IACd,KAAK,CAAC,MAAM;AAAA,MACV,mBAAmB,EAAE,MAAM,kBAAkB,EAAE,KAAK,aAAa,EAAE,OAAO;AAAA,MAE1E;AAAA,IACF;AAAA,EACF;AAAA,EACA,yBAAyB;AAAA,IACvB,SAAS,CAAC,MACR,YAAY,EAAE,OAAO,cAAc,EAAE,MAAM,eAC1C,EAAE,UAAU,OACT,gDACA,YAAY,EAAE,KAAK;AAAA,IACzB,QAAQ,MAAM;AAAA,IACd,KAAK,CAAC,MAAM;AAAA,MACV,oCAAoC,EAAE,OAAO,MACxC,EAAE,OAAO,GAAG,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,IAAI,EAAE,MAAM;AAAA,MACnE,EAAE,UAAU,OACR,2GACqB,EAAE,MAAM,YAC7B,4BAA4B,EAAE,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB,SAAS,CAAC,MACR,EAAE,WAAW,SACT,YAAY,EAAE,OAAO,KAAK,EAAE,IAAI,WAAW,EAAE,YAAY,IAAI,EAAE,SAAS,KAAK,EAAE,KAC/E,SAAS,EAAE,MAAM,oFACD,EAAE,OAAO;AAAA,IAC/B,QAAQ,CAAC,MAAM,GAAG,EAAE,QAAQ;AAAA,IAC5B,KAAK,CAAC,MAAM;AAAA,MACV,GAAI,EAAE,WAAW,SACb,CAAC,IACD;AAAA,QACE,+CAA+C,EAAE,OAAO;AAAA,MAE1D;AAAA,MACJ,OAAO,EAAE,IAAI,kBAAkB,EAAE,OAAO,0BACrC,EAAE,SAAS,aAAa,sDAAsD;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,qBAAqB;AAAA,MAChC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AAAA,MACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AACF;","names":[]}
|
package/dist/feature/codes.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/feature/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FEATURE_DUPLICATE: {\n feature: string\n /** Positions in `options.features` of the first and the repeated entry. */\n first: number\n second: number\n }\n DK_FEATURE_API_CONFLICT: {\n method: string\n /** The feature whose API was installed last (the one rejected). */\n feature: string\n /** Who already owns `method`: another feature's name, or `null` for the table itself. */\n owner: string | null\n }\n DK_FEATURE_FAILED: {\n feature: string\n /** The hook that threw. `rowModel` is reported by the pipeline runner (T09). */\n hook: 'initialState' | 'createApi' | 'rowModel'\n /** The fallback that was applied. */\n fallback: string\n /** `error.name` only; the full error is `context.cause`. */\n errorName?: string\n /**\n * Dev: a method of a feature whose `createApi()` threw was called later (`hook: 'createApi'`);\n * `feature` names every feature whose API is missing.\n */\n method?: string\n }\n }\n}\n\nconst capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const featureMessages: DiagnosticMessages<\n 'DK_FEATURE_DUPLICATE' | 'DK_FEATURE_API_CONFLICT' | 'DK_FEATURE_FAILED'\n> = {\n DK_FEATURE_DUPLICATE: {\n problem: (d) =>\n `feature \"${d.feature}\" is installed twice (features[${d.first}] and features[${d.second}]); ` +\n 'a feature name is its state slice key and must be unique per table',\n impact: () => 'the table was not created',\n fix: (d) => [\n `remove features[${d.second}] (or features[${d.first}]): pass \"${d.feature}\" once, with all ` +\n 'its options in one call',\n 'for two different features that share a name, rename one: defineFeature({ name: ... })',\n ],\n },\n DK_FEATURE_API_CONFLICT: {\n problem: (d) =>\n `feature \"${d.feature}\" exposes \"${d.method}\", which ` +\n (d.owner === null\n ? 'the table already has (a core table method)'\n : `feature \"${d.owner}\" already exposes`),\n impact: () => 'the table was not created: one of the two methods would be unreachable',\n fix: (d) => [\n `namespace the method in feature \"${d.feature}\": ` +\n `${d.feature}${capitalize(d.method)}() or ${d.feature}.${d.method}()`,\n d.owner === null\n ? `core table methods cannot be replaced; use an interceptor (e.g. on state:dispatch) to ` +\n `change what table.${d.method}() does`\n : `or rename it in feature \"${d.owner}\", or install only one of the two features`,\n ],\n },\n DK_FEATURE_FAILED: {\n problem: (d) =>\n d.method === undefined\n ? `feature \"${d.feature}\" ${d.hook}() threw${d.errorName ? ` ${d.errorName}` : ''}`\n : `table.${d.method}() was called, but it does not exist: createApi() threw at creation ` +\n `for feature \"${d.feature}\", so its API is missing`,\n impact: (d) => `${d.fallback}; the rest of the table is unaffected`,\n fix: (d) => [\n ...(d.method === undefined\n ? []\n : [\n `see the first DK_FEATURE_FAILED of feature \"${d.feature}\" (trigger init): its ` +\n 'context.cause is the createApi() error',\n ]),\n `fix ${d.hook}() of feature \"${d.feature}\": it must not throw` +\n (d.hook === 'rowModel' ? ' (return its input indices when it cannot filter)' : ''),\n 'the original error is context.cause (passed to onDiagnostic)',\n ],\n },\n}\n\n/** Codes owned by the feature contract (§3.3, §8.3). */\nexport const featureDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FEATURE_DUPLICATE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_DUPLICATE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_API_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_API_CONFLICT,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_FAILED,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;
|
|
1
|
+
{"version":3,"sources":["../../src/feature/codes.ts"],"sourcesContent":["import { type DiagnosticMessages, defineDiagnostic } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\n\ndeclare module '../diagnostics/catalog' {\n interface DiagnosticCatalog {\n DK_FEATURE_DUPLICATE: {\n feature: string\n /** Positions in `options.features` of the first and the repeated entry. */\n first: number\n second: number\n }\n DK_FEATURE_API_CONFLICT: {\n method: string\n /** The feature whose API was installed last (the one rejected). */\n feature: string\n /** Who already owns `method`: another feature's name, or `null` for the table itself. */\n owner: string | null\n }\n DK_FEATURE_FAILED: {\n feature: string\n /** The hook that threw. `rowModel` is reported by the pipeline runner (T09). */\n hook: 'initialState' | 'createApi' | 'rowModel'\n /** The fallback that was applied. */\n fallback: string\n /** `error.name` only; the full error is `context.cause`. */\n errorName?: string\n /**\n * Dev: a method of a feature whose `createApi()` threw was called later (`hook: 'createApi'`);\n * `feature` names every feature whose API is missing.\n */\n method?: string\n }\n }\n}\n\nconst capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)\n\n/** Message text (§8.2): development builds, or production via `@datakable/core/messages`. */\nexport const featureMessages: DiagnosticMessages<\n 'DK_FEATURE_DUPLICATE' | 'DK_FEATURE_API_CONFLICT' | 'DK_FEATURE_FAILED'\n> = {\n DK_FEATURE_DUPLICATE: {\n problem: (d) =>\n `feature \"${d.feature}\" is installed twice (features[${d.first}] and features[${d.second}]); ` +\n 'a feature name is its state slice key and must be unique per table',\n impact: () => 'the table was not created',\n fix: (d) => [\n `remove features[${d.second}] (or features[${d.first}]): pass \"${d.feature}\" once, with all ` +\n 'its options in one call',\n 'for two different features that share a name, rename one: defineFeature({ name: ... })',\n ],\n },\n DK_FEATURE_API_CONFLICT: {\n problem: (d) =>\n `feature \"${d.feature}\" exposes \"${d.method}\", which ` +\n (d.owner === null\n ? 'the table already has (a core table method)'\n : `feature \"${d.owner}\" already exposes`),\n impact: () => 'the table was not created: one of the two methods would be unreachable',\n fix: (d) => [\n `namespace the method in feature \"${d.feature}\": ` +\n `${d.feature}${capitalize(d.method)}() or ${d.feature}.${d.method}()`,\n d.owner === null\n ? `core table methods cannot be replaced; use an interceptor (e.g. on state:dispatch) to ` +\n `change what table.${d.method}() does`\n : `or rename it in feature \"${d.owner}\", or install only one of the two features`,\n ],\n },\n DK_FEATURE_FAILED: {\n problem: (d) =>\n d.method === undefined\n ? `feature \"${d.feature}\" ${d.hook}() threw${d.errorName ? ` ${d.errorName}` : ''}`\n : `table.${d.method}() was called, but it does not exist: createApi() threw at creation ` +\n `for feature \"${d.feature}\", so its API is missing`,\n impact: (d) => `${d.fallback}; the rest of the table is unaffected`,\n fix: (d) => [\n ...(d.method === undefined\n ? []\n : [\n `see the first DK_FEATURE_FAILED of feature \"${d.feature}\" (trigger init): its ` +\n 'context.cause is the createApi() error',\n ]),\n `fix ${d.hook}() of feature \"${d.feature}\": it must not throw` +\n (d.hook === 'rowModel' ? ' (return its input indices when it cannot filter)' : ''),\n 'the original error is context.cause (passed to onDiagnostic)',\n ],\n },\n}\n\n/** Codes owned by the feature contract (§3.3, §8.3). */\nexport const featureDiagnostics = [\n defineDiagnostic(\n {\n code: 'DK_FEATURE_DUPLICATE',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_DUPLICATE,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_API_CONFLICT',\n level: 'error',\n config: true,\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_API_CONFLICT,\n ),\n defineDiagnostic(\n {\n code: 'DK_FEATURE_FAILED',\n level: 'error',\n where: (d) => ({ kind: 'feature', name: d.feature }),\n },\n (process.env.NODE_ENV !== \"production\") && featureMessages.DK_FEATURE_FAILED,\n ),\n]\n"],"mappings":";AAAA,SAAkC,wBAAwB;AAC1D,OAAO;AAkCP,IAAM,aAAa,CAAC,MAAc,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAGhE,IAAM,kBAET;AAAA,EACF,sBAAsB;AAAA,IACpB,SAAS,CAAC,MACR,YAAY,EAAE,OAAO,kCAAkC,EAAE,KAAK,kBAAkB,EAAE,MAAM;AAAA,IAE1F,QAAQ,MAAM;AAAA,IACd,KAAK,CAAC,MAAM;AAAA,MACV,mBAAmB,EAAE,MAAM,kBAAkB,EAAE,KAAK,aAAa,EAAE,OAAO;AAAA,MAE1E;AAAA,IACF;AAAA,EACF;AAAA,EACA,yBAAyB;AAAA,IACvB,SAAS,CAAC,MACR,YAAY,EAAE,OAAO,cAAc,EAAE,MAAM,eAC1C,EAAE,UAAU,OACT,gDACA,YAAY,EAAE,KAAK;AAAA,IACzB,QAAQ,MAAM;AAAA,IACd,KAAK,CAAC,MAAM;AAAA,MACV,oCAAoC,EAAE,OAAO,MACxC,EAAE,OAAO,GAAG,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,IAAI,EAAE,MAAM;AAAA,MACnE,EAAE,UAAU,OACR,2GACqB,EAAE,MAAM,YAC7B,4BAA4B,EAAE,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,mBAAmB;AAAA,IACjB,SAAS,CAAC,MACR,EAAE,WAAW,SACT,YAAY,EAAE,OAAO,KAAK,EAAE,IAAI,WAAW,EAAE,YAAY,IAAI,EAAE,SAAS,KAAK,EAAE,KAC/E,SAAS,EAAE,MAAM,oFACD,EAAE,OAAO;AAAA,IAC/B,QAAQ,CAAC,MAAM,GAAG,EAAE,QAAQ;AAAA,IAC5B,KAAK,CAAC,MAAM;AAAA,MACV,GAAI,EAAE,WAAW,SACb,CAAC,IACD;AAAA,QACE,+CAA+C,EAAE,OAAO;AAAA,MAE1D;AAAA,MACJ,OAAO,EAAE,IAAI,kBAAkB,EAAE,OAAO,0BACrC,EAAE,SAAS,aAAa,sDAAsD;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,qBAAqB;AAAA,EAChC;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AAAA,EACA;AAAA,IACE;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,CAAC,OAAO,EAAE,MAAM,WAAW,MAAM,EAAE,QAAQ;AAAA,IACpD;AAAA,IACC,QAAQ,IAAI,aAAa,gBAAiB,gBAAgB;AAAA,EAC7D;AACF;","names":[]}
|
package/dist/feature/install.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(install_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(install_exports);
|
|
27
27
|
var import_build = require("../diagnostics/build.cjs");
|
|
28
28
|
var import_catalog = require("../diagnostics/catalog.cjs");
|
|
29
|
+
var import_process_guard = require("../diagnostics/process-guard.cjs");
|
|
29
30
|
var import_classify = require("../intercept/classify.cjs");
|
|
30
31
|
var import_codes = require("./codes.cjs");
|
|
31
32
|
(0, import_catalog.keepDiagnostics)(import_codes.featureDiagnostics);
|
|
@@ -45,7 +46,7 @@ function initialSlices(features, given, emit) {
|
|
|
45
46
|
out[f.name] = f.initialState();
|
|
46
47
|
} catch (e) {
|
|
47
48
|
out[f.name] = void 0;
|
|
48
|
-
reportFailure(emit, f.name, "initialState", "
|
|
49
|
+
reportFailure(emit, f.name, "initialState", "slice-undefined", e);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
return given ? Object.assign(out, given) : out;
|
|
@@ -68,7 +69,7 @@ function installApis(features, host) {
|
|
|
68
69
|
try {
|
|
69
70
|
api = f.createApi(ctx);
|
|
70
71
|
} catch (e) {
|
|
71
|
-
reportFailure(host.emit, name, "createApi", "
|
|
72
|
+
reportFailure(host.emit, name, "createApi", "api-not-installed", e);
|
|
72
73
|
failed.push(name);
|
|
73
74
|
continue;
|
|
74
75
|
}
|
|
@@ -120,7 +121,7 @@ function missingApi(failed, host) {
|
|
|
120
121
|
feature,
|
|
121
122
|
hook: "createApi",
|
|
122
123
|
method: key,
|
|
123
|
-
fallback:
|
|
124
|
+
fallback: `${failed.length === 1 ? "api" : "apis"}-missing|${key}`
|
|
124
125
|
})
|
|
125
126
|
);
|
|
126
127
|
return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/feature/install.ts"],"sourcesContent":["import { throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport type { Emit } from '../diagnostics/types'\nimport { errorName } from '../intercept/classify'\nimport type { Table, TableState } from '../table/types'\nimport { featureDiagnostics } from './codes'\nimport type { AnyFeature, FeatureContext, SliceUpdater } from './types'\n\nkeepDiagnostics(featureDiagnostics)\n\n/** What `createTable` gives the installer: the store operations a feature context needs. */\nexport interface FeatureHost {\n readonly table: Table<unknown, TableState>\n /** The table name, for config errors thrown before the first emit. */\n readonly tableName: string\n readonly emit: Emit\n getSlice(name: string): unknown\n /** `state:dispatch` of one slice (where = the feature name); notifies unless inside an API call. */\n setSlice(name: string, updater: SliceUpdater<unknown>): void\n /**\n * Runs an API method: no-op + `DK_TABLE_DESTROYED` after destroy (returning `destroyed?.()`),\n * trigger `api:<method>`, listeners notified once when the outermost call returns.\n */\n call(method: string, fn: () => unknown, destroyed?: () => unknown): unknown\n}\n\nfunction reportFailure(\n emit: Emit,\n feature: string,\n hook: 'initialState' | 'createApi',\n fallback: string,\n e: unknown,\n) {\n const name = errorName(e)\n emit(\n 'DK_FEATURE_FAILED',\n { feature, hook, fallback, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n}\n\n/**\n * The starting snapshot: each feature's `initialState()` under its name, then `given`\n * (`options.initialState`) per slice on top. `initialState()` is not called for a given slice.\n */\nexport function initialSlices(\n features: readonly AnyFeature[],\n given: object | undefined,\n emit: Emit,\n): TableState {\n const out: TableState = {}\n for (const f of features) {\n if (given && Object.hasOwn(given, f.name)) continue\n try {\n out[f.name] = f.initialState()\n } catch (e) {\n out[f.name] = undefined\n reportFailure(emit, f.name, 'initialState', '
|
|
1
|
+
{"version":3,"sources":["../../src/feature/install.ts"],"sourcesContent":["import { throwConfigError } from '../diagnostics/build'\nimport { keepDiagnostics } from '../diagnostics/catalog'\nimport '../diagnostics/process-guard'\nimport type { Emit } from '../diagnostics/types'\nimport { errorName } from '../intercept/classify'\nimport type { Table, TableState } from '../table/types'\nimport { featureDiagnostics } from './codes'\nimport type { AnyFeature, FeatureContext, SliceUpdater } from './types'\n\nkeepDiagnostics(featureDiagnostics)\n\n/** What `createTable` gives the installer: the store operations a feature context needs. */\nexport interface FeatureHost {\n readonly table: Table<unknown, TableState>\n /** The table name, for config errors thrown before the first emit. */\n readonly tableName: string\n readonly emit: Emit\n getSlice(name: string): unknown\n /** `state:dispatch` of one slice (where = the feature name); notifies unless inside an API call. */\n setSlice(name: string, updater: SliceUpdater<unknown>): void\n /**\n * Runs an API method: no-op + `DK_TABLE_DESTROYED` after destroy (returning `destroyed?.()`),\n * trigger `api:<method>`, listeners notified once when the outermost call returns.\n */\n call(method: string, fn: () => unknown, destroyed?: () => unknown): unknown\n}\n\nfunction reportFailure(\n emit: Emit,\n feature: string,\n hook: 'initialState' | 'createApi',\n fallback: string,\n e: unknown,\n) {\n const name = errorName(e)\n emit(\n 'DK_FEATURE_FAILED',\n { feature, hook, fallback, ...(name === undefined ? {} : { errorName: name }) },\n { cause: e },\n )\n}\n\n/**\n * The starting snapshot: each feature's `initialState()` under its name, then `given`\n * (`options.initialState`) per slice on top. `initialState()` is not called for a given slice.\n */\nexport function initialSlices(\n features: readonly AnyFeature[],\n given: object | undefined,\n emit: Emit,\n): TableState {\n const out: TableState = {}\n for (const f of features) {\n if (given && Object.hasOwn(given, f.name)) continue\n try {\n out[f.name] = f.initialState()\n } catch (e) {\n out[f.name] = undefined\n reportFailure(emit, f.name, 'initialState', 'slice-undefined', e)\n }\n }\n return given ? Object.assign(out, given) : out\n}\n\n/**\n * Creates every feature API (in the order the features were listed) and merges it onto the table.\n * Throws `DatakableConfigError` `DK_FEATURE_API_CONFLICT` when a member name is already taken by a\n * core table method or by an earlier feature. Methods are wrapped once here (cold path).\n */\nexport function installApis(features: readonly AnyFeature[], host: FeatureHost): void {\n const target = host.table as unknown as Record<string, unknown>\n const owners = new Map<string, string | null>()\n for (const k of Object.keys(target)) owners.set(k, null)\n /** Features whose `createApi()` threw: their methods are missing from the table. */\n const failed: string[] = []\n\n for (const f of features) {\n const name = f.name\n const ctx: FeatureContext<unknown> = {\n name,\n getState: () => host.getSlice(name),\n setState: (updater) => host.setSlice(name, updater),\n table: host.table,\n emit: host.emit,\n }\n let api: unknown\n try {\n api = f.createApi(ctx)\n } catch (e) {\n reportFailure(host.emit, name, 'createApi', 'api-not-installed', e)\n failed.push(name)\n continue\n }\n if (api === null || typeof api !== 'object') continue\n const members = api as Record<string, unknown>\n const keys = Object.keys(members)\n for (const method of keys) {\n if (owners.has(method)) {\n throwConfigError(\n 'DK_FEATURE_API_CONFLICT',\n { method, feature: name, owner: owners.get(method) ?? null },\n { table: host.tableName },\n )\n }\n }\n for (const method of keys) {\n owners.set(method, name)\n const member = members[method]\n const destroyed = f.destroyedResult?.[method]\n target[method] =\n typeof member === 'function'\n ? (...args: unknown[]) => host.call(method, () => member.apply(members, args), destroyed)\n : member\n }\n }\n if ((process.env.NODE_ENV !== \"production\") && failed.length > 0) Object.setPrototypeOf(target, missingApi(failed, host))\n}\n\n/**\n * Names generic code probes on any object (await, JSON, React, test runners, inspectors): never a\n * feature method, so never stubbed.\n */\nconst PROBES = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'asymmetricMatch',\n 'nodeType',\n 'tagName',\n])\nconst isProbe = (k: string) => PROBES.has(k) || /^[_$@]/.test(k)\n\n/**\n * Dev (R4): the prototype of a table where a feature's `createApi()` threw. The types still promise\n * that feature's methods, so a call to a name the table does not have is a no-op that reports\n * `DK_FEATURE_FAILED` (with the method) instead of a raw TypeError at the caller. Only reads of\n * missing names reach it: the table keeps its identity and own members, `in` / `hasOwn` stay\n * false, and `Object.prototype` members, symbols and generic probes (`then`, `toJSON`, …) resolve\n * as usual.\n */\nfunction missingApi(failed: readonly string[], host: FeatureHost): object {\n const feature = failed.join(', ')\n const stubs = new Map<string, () => undefined>()\n return new Proxy(Object.prototype, {\n get(proto, key, receiver) {\n if (typeof key !== 'string' || key in proto || isProbe(key)) {\n return Reflect.get(proto, key, receiver)\n }\n let stub = stubs.get(key)\n if (!stub) {\n stub = () => {\n host.call(key, () =>\n host.emit('DK_FEATURE_FAILED', {\n feature,\n hook: 'createApi',\n method: key,\n fallback: `${failed.length === 1 ? 'api' : 'apis'}-missing|${key}`,\n }),\n )\n return undefined\n }\n stubs.set(key, stub)\n }\n return stub\n },\n })\n}\n"],"mappings":"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","names":[]}
|