@bitcobblers/wod-wiki-wql 0.11.43 → 0.35.2116
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/dist/index.cjs +3623 -658
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1332 -229
- package/dist/index.d.ts +1332 -229
- package/dist/index.mjs +3549 -655
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { LRParser } from '@lezer/lr';
|
|
2
2
|
import { LanguageSupport, HighlightStyle, LRLanguage } from '@codemirror/language';
|
|
3
|
-
import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
|
3
|
+
import { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
|
4
4
|
import { Extension } from '@codemirror/state';
|
|
5
|
-
import {
|
|
5
|
+
import { SummaryCoverage, BlockIndexRow, Note, EventRecord, PageNote, Page, AnalyticsDataPoint, ResultOrigin, StoredOutputStatement } from '@bitcobblers/wod-wiki-core';
|
|
6
|
+
export { SYSTEM_DEFAULT_OUTPUT_UNIT, UnitConversionError, convertViaCatalog, dimensionKeyOf, lookupUnit, systemDefaultUnit } from '@bitcobblers/wod-wiki-core';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* WQL vocabulary — the single owner of the WQL dictionary: aggregators,
|
|
@@ -28,9 +29,20 @@ type WqlAggregator = (typeof WQL_AGGREGATORS)[number];
|
|
|
28
29
|
/** Comparison operators for cross-store `where` joins (#800). */
|
|
29
30
|
declare const WQL_COMPARISON_OPS: readonly [">", ">=", "<", "<=", "==", "!="];
|
|
30
31
|
type WqlComparisonOp = (typeof WQL_COMPARISON_OPS)[number];
|
|
32
|
+
/** Frontmatter typed-tag keys (CONTEXT.md § Tags) — values are dynamic,
|
|
33
|
+
* backed by the host's tags store (`by-type` index), not a static enum. */
|
|
34
|
+
declare const WQL_TYPED_TAG_KEYS: readonly ["domain", "format", "equipment", "quality", "intent"];
|
|
35
|
+
type WqlTypedTagKey = (typeof WQL_TYPED_TAG_KEYS)[number];
|
|
31
36
|
/** Tag keys the Query Service reads off a fact row (QueryService.factTagValue). */
|
|
32
|
-
declare const WQL_TAG_KEYS: readonly ["effort", "discipline", "intensity", "note", "page", "origin", "grain", "metric", "block", "result", "tags"];
|
|
37
|
+
declare const WQL_TAG_KEYS: readonly ["domain", "format", "equipment", "quality", "intent", "effort", "discipline", "intensity", "grade", "note", "page", "origin", "grain", "metric", "block", "result", "tags"];
|
|
33
38
|
type WqlTagKey = (typeof WQL_TAG_KEYS)[number];
|
|
39
|
+
/** Content-plane keys — meaningful only on find:/rows:. On an aggregate
|
|
40
|
+
* they are a category error (a fact row has no note text or source);
|
|
41
|
+
* every other non-structural key is a candidate custom dimension. */
|
|
42
|
+
declare const WQL_CONTENT_ONLY_KEYS: readonly ["type", "text", "has", "source", "catalog"];
|
|
43
|
+
/** Filter keys the effort registry can resolve (runFindEffort) — anything
|
|
44
|
+
* else on find:effort is ignored and reported as an advisory. */
|
|
45
|
+
declare const WQL_EFFORT_FILTER_KEYS: readonly ["effort", "discipline", "intensity", "origin", "text"];
|
|
34
46
|
/** Virtual dimensions — time buckets and stream positions, not fact fields. */
|
|
35
47
|
declare const WQL_VIRTUAL_DIMS: readonly ["day", "week", "session", "round"];
|
|
36
48
|
type WqlVirtualDim = (typeof WQL_VIRTUAL_DIMS)[number];
|
|
@@ -56,11 +68,42 @@ type WqlIntensityTier = (typeof WQL_INTENSITY_TIERS)[number];
|
|
|
56
68
|
declare const WQL_GRAINS: readonly ["summary", "event"];
|
|
57
69
|
type WqlGrain = (typeof WQL_GRAINS)[number];
|
|
58
70
|
/** Content-discovery query targets (find:<target>). */
|
|
59
|
-
declare const WQL_FIND_TARGETS: readonly ["note", "block", "effort"];
|
|
71
|
+
declare const WQL_FIND_TARGETS: readonly ["note", "block", "effort", "session", "segment", "event"];
|
|
60
72
|
type WqlFindTarget = (typeof WQL_FIND_TARGETS)[number];
|
|
61
|
-
/** Canonical source filter values
|
|
62
|
-
|
|
73
|
+
/** Canonical source filter values: the allowed storage locations (the
|
|
74
|
+
* former `collections` spelling is retired — renamed to feeds; storage
|
|
75
|
+
* identities keep their `collection:` prefixes). Seed corpus locations
|
|
76
|
+
* joined the note plane: prebuilt dashboards and the effort library. */
|
|
77
|
+
declare const WQL_SOURCE_VALUES: readonly ["journal", "feeds", "guides", "playground", "dashboards", "efforts"];
|
|
63
78
|
type WqlSourceValue = (typeof WQL_SOURCE_VALUES)[number];
|
|
79
|
+
/** Colon source heads — the unprefixed `:<head>` names. Find targets map
|
|
80
|
+
* 1:1 to the content planes; journal/feed(s)/catalog(s)/playground are
|
|
81
|
+
* source-scoped note heads (they parse to target 'note' plus an injected
|
|
82
|
+
* `source:` filter). */
|
|
83
|
+
declare const WQL_SOURCE_HEADS: readonly ["note", "block", "effort", "session", "segment", "event", "journal", "feed", "feeds", "catalog", "catalogs", "playground", "dashboard"];
|
|
84
|
+
type WqlSourceHead = (typeof WQL_SOURCE_HEADS)[number];
|
|
85
|
+
/** Storage scope a source-scoped note head binds (undefined = plain target). */
|
|
86
|
+
declare const WQL_SOURCE_HEAD_SCOPES: Readonly<Record<string, WqlSourceValue>>;
|
|
87
|
+
/** Type-alias note heads — `:<head>` parses to target 'note' plus an injected
|
|
88
|
+
* `type:` filter (no storage scope, so the head never narrows by source).
|
|
89
|
+
* `:dashboard` is the canonical member: dashboards are notes typed
|
|
90
|
+
* 'dashboard', findable with ordinary note tags/text/frontmatter filters. */
|
|
91
|
+
declare const WQL_TYPE_HEADS: Readonly<Record<string, string>>;
|
|
92
|
+
/** Former generic `:note` default scope — now EMPTY: the generic note head
|
|
93
|
+
* is the inclusive note plane (every row). Kept exported (empty) so the
|
|
94
|
+
* serializer and host surfaces keep a single name for "the default scope",
|
|
95
|
+
* which no longer narrows anything. */
|
|
96
|
+
declare const WQL_NOTE_DEFAULT_SOURCES: readonly WqlSourceValue[];
|
|
97
|
+
/** Colon function heads — the unprefixed `:<fn>{metric:…}` aggregate stage. */
|
|
98
|
+
declare const WQL_FUNCTION_HEADS: readonly ["sum", "avg", "min", "max", "count", "last", "delta"];
|
|
99
|
+
type WqlFunctionHead = WqlAggregator;
|
|
100
|
+
/** Colon chart heads — terminal pipeline sinks (`:timeseries`, `:bar`, …). */
|
|
101
|
+
declare const WQL_CHART_HEADS: readonly ["timeseries", "bar", "table", "donut", "toplist", "value"];
|
|
102
|
+
type WqlChartHead = (typeof WQL_CHART_HEADS)[number];
|
|
103
|
+
/** Standard datasets the host registers per page/run before children
|
|
104
|
+
* evaluate (`@session` active session, `@today` civil-day telemetry+notes). */
|
|
105
|
+
declare const WQL_STANDARD_DATASETS: readonly ["@session", "@today"];
|
|
106
|
+
type WqlStandardDataset = (typeof WQL_STANDARD_DATASETS)[number];
|
|
64
107
|
/** Result planes — `rows:<plane>` narrows raw output-statement rows by the
|
|
65
108
|
* unified store's promoted `outputType` column. The STORE vocabulary is
|
|
66
109
|
* open (core KNOWN_OUTPUT_TYPES, ticket 002: unknowns are stored and
|
|
@@ -69,17 +112,8 @@ type WqlSourceValue = (typeof WQL_SOURCE_VALUES)[number];
|
|
|
69
112
|
* stay addressable programmatically via hand-built ASTs. */
|
|
70
113
|
declare const WQL_RESULT_PLANES: readonly ["segment", "system", "load", "event", "compiler", "completion", "analytics", "wellness"];
|
|
71
114
|
type WqlResultPlane = (typeof WQL_RESULT_PLANES)[number];
|
|
72
|
-
/** All `rows:` targets — content planes plus result planes (rows model
|
|
73
|
-
* §1.0: every query selects rows from a target) plus `all`, the explicit
|
|
74
|
-
* no-narrowing pseudo-target (spec v2 decision 1: the bare `rows:{…}`
|
|
75
|
-
* alias retires). Content-plane targets scope by content, not outputType
|
|
76
|
-
* (C4). */
|
|
77
|
-
declare const WQL_ROWS_TARGETS: readonly ["note", "block", "effort", "segment", "system", "load", "event", "compiler", "completion", "analytics", "wellness", "all"];
|
|
78
|
-
type WqlRowsTarget = (typeof WQL_ROWS_TARGETS)[number];
|
|
79
|
-
/** Rows scope filter keys (C4): the exact-match keys a rows query filters by. */
|
|
80
|
-
declare const WQL_ROWS_SCOPE_KEYS: readonly ["result", "block", "note"];
|
|
81
115
|
/** Content-specific filter keys (beyond the analytics tag keys). */
|
|
82
|
-
declare const WQL_CONTENT_FILTER_KEYS: readonly ["type", "text", "has", "source", "catalog", "effort", "discipline", "intensity", "note", "page", "origin", "grain", "metric", "block", "result", "tags"];
|
|
116
|
+
declare const WQL_CONTENT_FILTER_KEYS: readonly ["type", "text", "has", "source", "catalog", "domain", "format", "equipment", "quality", "intent", "effort", "discipline", "intensity", "grade", "note", "page", "origin", "grain", "metric", "block", "result", "tags"];
|
|
83
117
|
type WqlContentFilterKey = (typeof WQL_CONTENT_FILTER_KEYS)[number];
|
|
84
118
|
/**
|
|
85
119
|
* Composer source planes (issue #838, decision #836): the single `source`
|
|
@@ -90,10 +124,10 @@ type WqlContentFilterKey = (typeof WQL_CONTENT_FILTER_KEYS)[number];
|
|
|
90
124
|
* deliberately absent — it needs new grammar/engine semantics and is
|
|
91
125
|
* deferred to its own ticket.
|
|
92
126
|
*/
|
|
93
|
-
declare const WQL_SOURCES: readonly ["
|
|
127
|
+
declare const WQL_SOURCES: readonly ["note", "block", "effort", "session", "segment", "event"];
|
|
94
128
|
type WqlSource = (typeof WQL_SOURCES)[number];
|
|
95
|
-
/** Rollup periods the aggregate grammar accepts (wql.ts: unit d|w
|
|
96
|
-
declare const WQL_ROLLUP_PERIODS: readonly ["
|
|
129
|
+
/** Rollup periods the aggregate grammar accepts (wql.ts: N > 1, unit d|w). */
|
|
130
|
+
declare const WQL_ROLLUP_PERIODS: readonly ["2w", "4w"];
|
|
97
131
|
type WqlRollupPeriod = (typeof WQL_ROLLUP_PERIODS)[number];
|
|
98
132
|
/** Display units the app can render (analytics unit preference, kg/lb). The
|
|
99
133
|
* `in <unit>` directive accepts any word; these are the canonical options. */
|
|
@@ -103,29 +137,54 @@ type WqlDisplayUnit = (typeof WQL_DISPLAY_UNITS)[number];
|
|
|
103
137
|
declare const WQL_KEYWORDS: readonly ["find", "rows", "where", "group", "by", "order", "limit", "sum", "avg", "min", "max", "count", "last", "delta"];
|
|
104
138
|
type WqlKeyword = (typeof WQL_KEYWORDS)[number];
|
|
105
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Grouping dimensions the content consumer (stream entryGrouping) applies on
|
|
142
|
+
* top of find results: the note/block/effort/session executors return flat
|
|
143
|
+
* results and the view groups them, ordered and multi-dim. The find tables
|
|
144
|
+
* (segment/event) group inside the executor over selected columns instead.
|
|
145
|
+
*/
|
|
146
|
+
declare const CONTENT_GROUPING_DIMENSIONS: readonly ["date", "day", "week", "month", "year", "discipline", "origin", "source", "kind", "type", "tag"];
|
|
147
|
+
/** Guided clause types, including structural clauses honored by their consumers. */
|
|
148
|
+
declare function allowedFilterTypesForTarget(target: string, family: 'find' | 'aggregate'): ReadonlySet<string>;
|
|
149
|
+
/** Suggested filter keys. Fact-backed queries also accept custom dimension keys. */
|
|
150
|
+
declare function wqlFilterKeys(target: string, family: 'find' | 'aggregate'): readonly string[];
|
|
151
|
+
/** Content grouping is applied by the stream consumer; table grouping reads
|
|
152
|
+
* selected columns. Effort/session have no executor grouping but their views
|
|
153
|
+
* group content (the effort view profile groups by discipline). */
|
|
154
|
+
declare function wqlGroupingDimensions(target: string, family: 'find' | 'aggregate'): readonly string[];
|
|
155
|
+
/** Fact dimensions are open, but content-only fields and plane have no fact meaning. */
|
|
156
|
+
declare function supportsWqlFilterKey(target: string, family: 'find' | 'aggregate', rawKey: string): boolean;
|
|
157
|
+
|
|
106
158
|
declare const parser: LRParser;
|
|
107
159
|
|
|
108
160
|
declare const Query = 1;
|
|
109
|
-
declare const
|
|
110
|
-
declare const
|
|
111
|
-
declare const
|
|
112
|
-
declare const
|
|
113
|
-
declare const
|
|
114
|
-
declare const
|
|
115
|
-
declare const
|
|
116
|
-
declare const
|
|
117
|
-
declare const
|
|
118
|
-
declare const
|
|
119
|
-
declare const
|
|
120
|
-
declare const
|
|
121
|
-
declare const
|
|
122
|
-
declare const
|
|
123
|
-
declare const
|
|
124
|
-
declare const
|
|
125
|
-
declare const
|
|
126
|
-
declare const
|
|
161
|
+
declare const Pipeline = 2;
|
|
162
|
+
declare const Stage = 3;
|
|
163
|
+
declare const Head = 4;
|
|
164
|
+
declare const Word = 5;
|
|
165
|
+
declare const Aggregator$1 = 6;
|
|
166
|
+
declare const Metric = 7;
|
|
167
|
+
declare const Filters = 8;
|
|
168
|
+
declare const Filter = 9;
|
|
169
|
+
declare const Negate = 10;
|
|
170
|
+
declare const TagKey = 11;
|
|
171
|
+
declare const TagValue$1 = 12;
|
|
172
|
+
declare const Value = 13;
|
|
173
|
+
declare const Star = 14;
|
|
174
|
+
declare const Quoted = 15;
|
|
175
|
+
declare const DatasetReference = 16;
|
|
176
|
+
declare const At = 17;
|
|
177
|
+
declare const Single = 18;
|
|
178
|
+
declare const GroupBy = 19;
|
|
179
|
+
declare const By = 20;
|
|
180
|
+
declare const Dimension = 21;
|
|
181
|
+
declare const Rollup = 22;
|
|
182
|
+
declare const RollupDot = 23;
|
|
183
|
+
declare const Int = 24;
|
|
127
184
|
|
|
185
|
+
declare const wql_parser_terms_At: typeof At;
|
|
128
186
|
declare const wql_parser_terms_By: typeof By;
|
|
187
|
+
declare const wql_parser_terms_DatasetReference: typeof DatasetReference;
|
|
129
188
|
declare const wql_parser_terms_Dimension: typeof Dimension;
|
|
130
189
|
declare const wql_parser_terms_Filter: typeof Filter;
|
|
131
190
|
declare const wql_parser_terms_Filters: typeof Filters;
|
|
@@ -134,16 +193,19 @@ declare const wql_parser_terms_Head: typeof Head;
|
|
|
134
193
|
declare const wql_parser_terms_Int: typeof Int;
|
|
135
194
|
declare const wql_parser_terms_Metric: typeof Metric;
|
|
136
195
|
declare const wql_parser_terms_Negate: typeof Negate;
|
|
196
|
+
declare const wql_parser_terms_Pipeline: typeof Pipeline;
|
|
137
197
|
declare const wql_parser_terms_Query: typeof Query;
|
|
138
198
|
declare const wql_parser_terms_Quoted: typeof Quoted;
|
|
139
199
|
declare const wql_parser_terms_Rollup: typeof Rollup;
|
|
140
200
|
declare const wql_parser_terms_RollupDot: typeof RollupDot;
|
|
201
|
+
declare const wql_parser_terms_Single: typeof Single;
|
|
202
|
+
declare const wql_parser_terms_Stage: typeof Stage;
|
|
141
203
|
declare const wql_parser_terms_Star: typeof Star;
|
|
142
204
|
declare const wql_parser_terms_TagKey: typeof TagKey;
|
|
143
205
|
declare const wql_parser_terms_Value: typeof Value;
|
|
144
206
|
declare const wql_parser_terms_Word: typeof Word;
|
|
145
207
|
declare namespace wql_parser_terms {
|
|
146
|
-
export { Aggregator$1 as Aggregator, wql_parser_terms_By as By, wql_parser_terms_Dimension as Dimension, wql_parser_terms_Filter as Filter, wql_parser_terms_Filters as Filters, wql_parser_terms_GroupBy as GroupBy, wql_parser_terms_Head as Head, wql_parser_terms_Int as Int, wql_parser_terms_Metric as Metric, wql_parser_terms_Negate as Negate, wql_parser_terms_Query as Query, wql_parser_terms_Quoted as Quoted, wql_parser_terms_Rollup as Rollup, wql_parser_terms_RollupDot as RollupDot, wql_parser_terms_Star as Star, wql_parser_terms_TagKey as TagKey, TagValue$1 as TagValue, wql_parser_terms_Value as Value, wql_parser_terms_Word as Word };
|
|
208
|
+
export { Aggregator$1 as Aggregator, wql_parser_terms_At as At, wql_parser_terms_By as By, wql_parser_terms_DatasetReference as DatasetReference, wql_parser_terms_Dimension as Dimension, wql_parser_terms_Filter as Filter, wql_parser_terms_Filters as Filters, wql_parser_terms_GroupBy as GroupBy, wql_parser_terms_Head as Head, wql_parser_terms_Int as Int, wql_parser_terms_Metric as Metric, wql_parser_terms_Negate as Negate, wql_parser_terms_Pipeline as Pipeline, wql_parser_terms_Query as Query, wql_parser_terms_Quoted as Quoted, wql_parser_terms_Rollup as Rollup, wql_parser_terms_RollupDot as RollupDot, wql_parser_terms_Single as Single, wql_parser_terms_Stage as Stage, wql_parser_terms_Star as Star, wql_parser_terms_TagKey as TagKey, TagValue$1 as TagValue, wql_parser_terms_Value as Value, wql_parser_terms_Word as Word };
|
|
147
209
|
}
|
|
148
210
|
|
|
149
211
|
/**
|
|
@@ -180,9 +242,13 @@ type ParsedWqlWindowSuffix = {
|
|
|
180
242
|
interface ParsedWqlSuffixes {
|
|
181
243
|
/** Outer cross-store join clause text (`where ...`). */
|
|
182
244
|
where?: string;
|
|
245
|
+
/** Stored source binding target (`=> @sourceName`). */
|
|
246
|
+
storedAs?: string;
|
|
247
|
+
/** Source dataset reference for downstream query (`@source | ...` or `in @source`). */
|
|
248
|
+
sourceRef?: string;
|
|
183
249
|
/** Display unit directive (`in kg`). */
|
|
184
250
|
displayUnit?: string;
|
|
185
|
-
/** Rollup period (`.rollup(
|
|
251
|
+
/** Rollup period (`.rollup(2w)` — 1-unit forms are rejected in parseQuery). */
|
|
186
252
|
rollup?: ParsedWqlRollupSuffix;
|
|
187
253
|
/** Group-by dimensions (`by {week, effort}`). */
|
|
188
254
|
groupBy?: string[];
|
|
@@ -205,7 +271,7 @@ declare function splitAtWhere(text: string): {
|
|
|
205
271
|
};
|
|
206
272
|
/**
|
|
207
273
|
* Extract suffixes from a raw WQL query string.
|
|
208
|
-
* Strips outer join (`where`), display unit (`in kg`), rollup (`.rollup(
|
|
274
|
+
* Strips outer join (`where`), display unit (`in kg`), rollup (`.rollup(Nw)`),
|
|
209
275
|
* group-by (`by {week}`), time window (`last 8w` / `from … [to …]`), and
|
|
210
276
|
* legacy scope (`in journal`).
|
|
211
277
|
*/
|
|
@@ -216,7 +282,7 @@ declare function parseWqlSuffixes(raw: string): ParsedWqlSuffixes;
|
|
|
216
282
|
*
|
|
217
283
|
* <aggregator>:<metric.namespace>{<tag filters>} by {<dimensions>} .rollup(<period>)
|
|
218
284
|
*
|
|
219
|
-
* sum:totalVolume{discipline:strength} by {week}
|
|
285
|
+
* sum:totalVolume{discipline:strength} by {week}
|
|
220
286
|
* avg:tis{effort:thruster,!discipline:recovery} by {session}
|
|
221
287
|
*
|
|
222
288
|
* This module holds the AST contract the Query Service executes and the
|
|
@@ -252,7 +318,7 @@ interface MetricPredicate {
|
|
|
252
318
|
}
|
|
253
319
|
/**
|
|
254
320
|
* Content half of a cross-store join — the find predicate attached to an
|
|
255
|
-
* analytics query via `where`. Example: `find:note{tags:competition}
|
|
321
|
+
* analytics query via `where`. Example: `find:note{tags:competition,source:journal}`.
|
|
256
322
|
* Restricts the metric computation to the blockContentIds owned by matching
|
|
257
323
|
* content; the metric is recomputed from raw logs for those blocks only.
|
|
258
324
|
*/
|
|
@@ -291,17 +357,50 @@ interface ParsedAggregateQuery {
|
|
|
291
357
|
advisories?: string[];
|
|
292
358
|
error?: string;
|
|
293
359
|
}
|
|
294
|
-
/**
|
|
360
|
+
/**
|
|
361
|
+
* Ticket 18 — drill-down: construct a cross-workout `rows:segment` query
|
|
362
|
+
* inheriting an aggregate point's tag/metadata filters, the clicked bucket's
|
|
363
|
+
* EXACT half-open civil boundaries (structural, not display timestamps), and
|
|
364
|
+
* the clicked group tuple as exact filters.
|
|
365
|
+
*/
|
|
366
|
+
declare function buildDrillDownQuery(options: {
|
|
367
|
+
filters?: Array<{
|
|
368
|
+
key: string;
|
|
369
|
+
values: readonly string[];
|
|
370
|
+
}>;
|
|
371
|
+
/** Half-open bucket bounds (ms epoch) from the clicked point's bucket. */
|
|
372
|
+
start?: number;
|
|
373
|
+
end?: number;
|
|
374
|
+
/** Civil dates (YYYY-MM-DD) — used when the point is a calendar bucket. */
|
|
375
|
+
startIso?: string;
|
|
376
|
+
endIso?: string;
|
|
377
|
+
timeZone?: string;
|
|
378
|
+
limit?: number;
|
|
379
|
+
}): string;
|
|
380
|
+
/** Result of parsing a content-discovery query — the colon source heads
|
|
381
|
+
* (`:note{…}`, `:journal{…}`); the legacy `find:` spelling is retired and
|
|
382
|
+
* survives only inside `where` join halves. */
|
|
295
383
|
interface ParsedFindQuery {
|
|
296
384
|
family: 'find';
|
|
297
385
|
raw: string;
|
|
298
386
|
/** Content target — a WQL_FIND_TARGETS value (C7 closed enum). */
|
|
299
387
|
target: string;
|
|
300
388
|
filters: TagFilter[];
|
|
389
|
+
/** Implicit default source scope for the generic `:note` head —
|
|
390
|
+
* journal | feeds | playground. NOT a filter: authored filters stay
|
|
391
|
+
* untouched (composer pills never see it); the executor ANDs it in only
|
|
392
|
+
* when no explicit `source:` filter was written. */
|
|
393
|
+
sourceScope?: string[];
|
|
301
394
|
/** Time-selection window (C1): `last 8w` or `from … [to …]`. */
|
|
302
395
|
window?: QueryWindow;
|
|
303
396
|
/** Cross-store metric join (`where sum:totalVolume{} > 5000`). */
|
|
304
397
|
join?: MetricPredicate;
|
|
398
|
+
/** Presentation pipes (`| select … | order by … | limit …`). */
|
|
399
|
+
pipes?: RowsPipes;
|
|
400
|
+
/** Group-by dimensions (`by {effort}`). */
|
|
401
|
+
groupBy?: string[];
|
|
402
|
+
/** Optional display unit directive (`in kg` / `in lb`). */
|
|
403
|
+
displayUnit?: string;
|
|
305
404
|
/** Deprecation advisories (C2 normalizer). */
|
|
306
405
|
advisories?: string[];
|
|
307
406
|
error?: string;
|
|
@@ -321,16 +420,53 @@ type QueryWindow = {
|
|
|
321
420
|
start: string;
|
|
322
421
|
end?: string;
|
|
323
422
|
};
|
|
324
|
-
type AnyParsedQuery = ParsedAggregateQuery | ParsedFindQuery |
|
|
423
|
+
type AnyParsedQuery = ParsedAggregateQuery | ParsedFindQuery | ParsedPipelineQuery;
|
|
325
424
|
/** Type guard: true for content-discovery queries. */
|
|
326
425
|
declare function isFindQuery(parsed: AnyParsedQuery): parsed is ParsedFindQuery;
|
|
327
|
-
/** Type guard: true for rows queries. */
|
|
328
|
-
declare function isRowsQuery(parsed: AnyParsedQuery): parsed is ParsedRowsQuery;
|
|
329
426
|
/** Type guard: true for analytics (aggregate) queries. */
|
|
330
427
|
declare function isAggregateQuery(parsed: AnyParsedQuery): parsed is ParsedAggregateQuery;
|
|
428
|
+
/** Type guard: true for left-to-right value pipelines (`:src | :fn | :chart`). */
|
|
429
|
+
declare function isPipelineQuery(parsed: AnyParsedQuery): parsed is ParsedPipelineQuery;
|
|
430
|
+
/** A pipeline's input: a full single query, or a named dataset
|
|
431
|
+
* (`@session` / `@today` / a host-registered page source — name keeps `@`). */
|
|
432
|
+
type PipelineSource = {
|
|
433
|
+
kind: 'query';
|
|
434
|
+
query: ParsedFindQuery | ParsedAggregateQuery;
|
|
435
|
+
} | {
|
|
436
|
+
kind: 'dataset';
|
|
437
|
+
name: string;
|
|
438
|
+
};
|
|
439
|
+
/** Terminal chart stage of a pipeline: the chart head plus its authored
|
|
440
|
+
* parameter filters (`:bar{type:session}` → `{head:'bar', filters:[…]}`). */
|
|
441
|
+
interface PipelineSink {
|
|
442
|
+
head: WqlChartHead;
|
|
443
|
+
filters: TagFilter[];
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Left-to-right value pipeline (`:journal{…} last 8w | :sum{metric:tis} | :timeseries{}`).
|
|
447
|
+
* Transforms apply in order to the previous stage's output — the executor
|
|
448
|
+
* reduces in memory, it never rescans stores. A dataset alone (`@today`) is
|
|
449
|
+
* a pipeline with no transforms; a lone chart head starts from an implicit
|
|
450
|
+
* `:segment` source.
|
|
451
|
+
*/
|
|
452
|
+
interface ParsedPipelineQuery {
|
|
453
|
+
family: 'pipeline';
|
|
454
|
+
raw: string;
|
|
455
|
+
source: PipelineSource;
|
|
456
|
+
/** Ordered aggregate stages, each applied to the previous stage's rows. */
|
|
457
|
+
transforms: ParsedAggregateQuery[];
|
|
458
|
+
sink?: PipelineSink;
|
|
459
|
+
/** Parse-time disclosures (ignored chart suffixes, …). */
|
|
460
|
+
advisories?: string[];
|
|
461
|
+
error?: string;
|
|
462
|
+
}
|
|
331
463
|
interface SeriesPoint {
|
|
332
464
|
ts: number;
|
|
333
465
|
value: number;
|
|
466
|
+
/** True for a structurally present bucket with no recorded observations
|
|
467
|
+
* (ticket 12 calendar domain generation) — display zero-fill, never an
|
|
468
|
+
* observation. */
|
|
469
|
+
missing?: boolean;
|
|
334
470
|
}
|
|
335
471
|
/**
|
|
336
472
|
* Result of parsing a rows query (`rows:{filters}` / `rows:segment{filters}`) —
|
|
@@ -338,12 +474,31 @@ interface SeriesPoint {
|
|
|
338
474
|
* output-statement rows re-derived from WorkoutResult logs, scoped by
|
|
339
475
|
* `result:` / `block:` / `note:`. Never aggregates — no by/rollup/where.
|
|
340
476
|
*/
|
|
477
|
+
/** Ticket 18 — pipe clauses: presentation only, never alter the match. */
|
|
478
|
+
interface RowsPipes {
|
|
479
|
+
error?: string;
|
|
480
|
+
select?: Array<{
|
|
481
|
+
col: string;
|
|
482
|
+
unit?: string;
|
|
483
|
+
}>;
|
|
484
|
+
order?: Array<{
|
|
485
|
+
col: string;
|
|
486
|
+
dir: 'asc' | 'desc';
|
|
487
|
+
}>;
|
|
488
|
+
limit?: number;
|
|
489
|
+
offset?: number;
|
|
490
|
+
}
|
|
341
491
|
interface ParsedRowsQuery {
|
|
492
|
+
/** Ticket 18 — pipe clauses (| select / | order by / | limit). */
|
|
493
|
+
pipes?: RowsPipes;
|
|
342
494
|
raw: string;
|
|
343
495
|
/** Family discriminator shared by all three query ASTs (C5). */
|
|
344
496
|
family: 'rows';
|
|
345
497
|
/** Output-statement type narrowing from the optional target (`rows:segment{…}`); undefined = all types. */
|
|
346
498
|
outputType?: string;
|
|
499
|
+
/** The rows target itself — 'all' | 'segment' | content plane (ticket 18:
|
|
500
|
+
* segment without scope = cross-workout form). */
|
|
501
|
+
target?: string;
|
|
347
502
|
filters: TagFilter[];
|
|
348
503
|
/** Time-selection window (C1): `last 4w` or `from … [to …]` over the
|
|
349
504
|
* workout end time. */
|
|
@@ -352,11 +507,14 @@ interface ParsedRowsQuery {
|
|
|
352
507
|
advisories?: string[];
|
|
353
508
|
error?: string;
|
|
354
509
|
}
|
|
510
|
+
/** One aggregated series. `error` carries the diagnostic when this series'
|
|
511
|
+
* calculation failed (ticket 13) — an errored series has no valid values. */
|
|
355
512
|
interface Series {
|
|
356
513
|
key: string;
|
|
357
514
|
label: string;
|
|
358
515
|
points: SeriesPoint[];
|
|
359
516
|
unit?: string;
|
|
517
|
+
error?: string;
|
|
360
518
|
}
|
|
361
519
|
/**
|
|
362
520
|
* Parse a WQL query string into one of the three query families —
|
|
@@ -364,10 +522,17 @@ interface Series {
|
|
|
364
522
|
* (C5). Dispatch is textual: a leading `find:` routes to the content path,
|
|
365
523
|
* `rows` to the rows path, everything else to analytics.
|
|
366
524
|
*/
|
|
525
|
+
declare function rowsParseError(raw: string): string;
|
|
526
|
+
/**
|
|
527
|
+
* Parse a WQL query string into one of the query families — analytics
|
|
528
|
+
* aggregate, content find, or pipeline — discriminated by `family` (C5).
|
|
529
|
+
* Dispatch: `rows` is a retired-error; `find:` is retired; a pipe-separated
|
|
530
|
+
* stage list (or a lone `@dataset`/chart head) is a pipeline; a colon
|
|
531
|
+
* source/function head or legacy `agg:metric` head parses as a single query.
|
|
532
|
+
*/
|
|
367
533
|
declare function parseQuery(raw: string): AnyParsedQuery;
|
|
368
534
|
/**
|
|
369
535
|
* C2 Compatibility normalizer: rewrites legacy query syntax into modern WQL.
|
|
370
|
-
* - Bare `rows:{…}` heads rewrite to `rows:all{…}`
|
|
371
536
|
* - Legacy trailing `in <scope>` rewrites into `{source:<scope>}`
|
|
372
537
|
* Returns the normalized query string and any deprecation advisories.
|
|
373
538
|
*/
|
|
@@ -375,6 +540,14 @@ declare function normalizeWql(raw: string): {
|
|
|
375
540
|
query: string;
|
|
376
541
|
advisories: string[];
|
|
377
542
|
};
|
|
543
|
+
/**
|
|
544
|
+
* Target-capability advisories for a find AST: every clause the executor will
|
|
545
|
+
* silently drop, apply differently than written, or group outside the
|
|
546
|
+
* executor. A pure function of the parsed shape — `parseFindQuery` attaches
|
|
547
|
+
* these at parse time and `QueryService.runFind` re-derives them, so
|
|
548
|
+
* hand-built ASTs get the same loud disclosure without re-serializing text.
|
|
549
|
+
*/
|
|
550
|
+
declare function findTargetAdvisories(query: ParsedFindQuery): string[];
|
|
378
551
|
|
|
379
552
|
/**
|
|
380
553
|
* WQL serializer (C6 — one structured interface for code).
|
|
@@ -393,7 +566,10 @@ declare function normalizeWql(raw: string): {
|
|
|
393
566
|
* to parse has no well-defined structure to re-emit, and echoing the input
|
|
394
567
|
* keeps the function total.
|
|
395
568
|
*
|
|
396
|
-
* Round-trip equality holds for text-surface-representable ASTs.
|
|
569
|
+
* Round-trip equality holds for text-surface-representable ASTs. Single
|
|
570
|
+
* aggregate queries serialize in the retained legacy `sum:metric{…}` form;
|
|
571
|
+
* find queries serialize with colon source heads (find: is retired) and
|
|
572
|
+
* pipelines as `source | :fn{…} | :chart{…}`. Three value
|
|
397
573
|
* shapes the grammar cannot express (or parses away) sit outside that
|
|
398
574
|
* contract — documented here rather than silently corrupted:
|
|
399
575
|
* - a `"` inside a filter value: the quoted token has no escape form;
|
|
@@ -425,6 +601,53 @@ declare const DISCIPLINE_FACTORS: Record<EffortDiscipline, number>;
|
|
|
425
601
|
/** Factor for an arbitrary discipline string; 1.0 when absent or unknown. */
|
|
426
602
|
declare function disciplineFactorFor(discipline: string | undefined): number;
|
|
427
603
|
|
|
604
|
+
/**
|
|
605
|
+
* IFieldCatalog — the consumer interface for field discovery (wayfinder
|
|
606
|
+
* datadog-analytics ticket 15, per the field discovery contract: typeahead
|
|
607
|
+
* reads catalog indexes with bounded result counts and NEVER scans event or
|
|
608
|
+
* result history; the query package performs no browser-storage access — the
|
|
609
|
+
* app injects an implementation over the V17 stores).
|
|
610
|
+
*/
|
|
611
|
+
/** One typed field variant as surfaced to authoring surfaces. */
|
|
612
|
+
interface CatalogFieldSuggestion {
|
|
613
|
+
/** Typed field identity (collision-free key). */
|
|
614
|
+
id: string;
|
|
615
|
+
/** Normalized full path. */
|
|
616
|
+
path: string;
|
|
617
|
+
kind: string;
|
|
618
|
+
/** Physical/named dimension of a numeric variant, when known. */
|
|
619
|
+
dimension?: string;
|
|
620
|
+
/** Observed effective units (original spellings as context). */
|
|
621
|
+
units: readonly string[];
|
|
622
|
+
/** Observed original spellings (provenance context). */
|
|
623
|
+
spellings: readonly string[];
|
|
624
|
+
}
|
|
625
|
+
/** One observed categorical value of a field (original spelling kept). */
|
|
626
|
+
interface CatalogValueSuggestion {
|
|
627
|
+
fieldId: string;
|
|
628
|
+
value: string;
|
|
629
|
+
}
|
|
630
|
+
interface IFieldCatalog {
|
|
631
|
+
/**
|
|
632
|
+
* Bounded normalized-path prefix listing for field typeahead. Implementations
|
|
633
|
+
* MUST serve it from the catalog index — never from event/result scans — and
|
|
634
|
+
* MUST cap results at `limit` (narrower prefixes reveal further suggestions
|
|
635
|
+
* rather than silently claiming the page is the whole vocabulary).
|
|
636
|
+
*/
|
|
637
|
+
listByPrefix(prefix: string, limit?: number): Promise<CatalogFieldSuggestion[]>;
|
|
638
|
+
/** Typed-variant lookup by exact normalized path — all variants of the path
|
|
639
|
+
* (e.g. numeric and text `score`), for `field(...)` alternatives. */
|
|
640
|
+
lookup(path: string): Promise<CatalogFieldSuggestion[]>;
|
|
641
|
+
/** Bounded categorical-value prefix lookup scoped to one field path. */
|
|
642
|
+
listValues(path: string, valuePrefix: string, limit?: number): Promise<CatalogValueSuggestion[]>;
|
|
643
|
+
/**
|
|
644
|
+
* Change notification — catalog commits signal interested consumers so new
|
|
645
|
+
* fields appear without reload (invalidation wiring lands with ticket 20).
|
|
646
|
+
* Returns an unsubscribe function.
|
|
647
|
+
*/
|
|
648
|
+
onChange?(listener: () => void): () => void;
|
|
649
|
+
}
|
|
650
|
+
|
|
428
651
|
/**
|
|
429
652
|
* WQL language support — CodeMirror highlighting + autocomplete over the
|
|
430
653
|
* Lezer grammar (grammar/wql.grammar).
|
|
@@ -447,8 +670,28 @@ interface WqlCompletionOptions {
|
|
|
447
670
|
* `() => resolver.list().map(e => e.slug)`.
|
|
448
671
|
*/
|
|
449
672
|
effortNames?: () => readonly string[];
|
|
673
|
+
/**
|
|
674
|
+
* Dynamic values for the frontmatter typed-tag filters (`{domain:…}`,
|
|
675
|
+
* `{format:…}`, `{equipment:…}`, `{quality:…}`, `{intent:…}`) — fed from
|
|
676
|
+
* the tags store's `by-type` index:
|
|
677
|
+
* `async key => (await storage.getAllFromIndex('tags', 'by-type', key)).map(t => t.label)`.
|
|
678
|
+
* Absent or empty results fall through to catalog discovery.
|
|
679
|
+
*/
|
|
680
|
+
tagTypeValues?: (key: string) => Promise<readonly string[]> | readonly string[];
|
|
681
|
+
/**
|
|
682
|
+
* Host-supplied values for a filter key (e.g. the composer's suggestion
|
|
683
|
+
* feeds). Non-empty results win over the static vocabularies below.
|
|
684
|
+
*/
|
|
685
|
+
values?: (key: string) => Promise<readonly Completion[]>;
|
|
686
|
+
/**
|
|
687
|
+
* Injected field catalog (ticket 15): discovered typed variants join the
|
|
688
|
+
* static metric vocabulary, and categorical values back filter-value
|
|
689
|
+
* suggestions for discovered fields. Bounded prefix lookups only — the
|
|
690
|
+
* catalog never scans event/result history.
|
|
691
|
+
*/
|
|
692
|
+
catalog?: IFieldCatalog;
|
|
450
693
|
}
|
|
451
|
-
declare function wqlCompletionSource(options_?: WqlCompletionOptions): (context: CompletionContext) => CompletionResult | null;
|
|
694
|
+
declare function wqlCompletionSource(options_?: WqlCompletionOptions): (context: CompletionContext) => CompletionResult | Promise<CompletionResult | null> | null;
|
|
452
695
|
declare function wqlCompletion(options?: WqlCompletionOptions): Extension;
|
|
453
696
|
/**
|
|
454
697
|
* Query-field colors, mapped onto the app's theme tokens so
|
|
@@ -460,141 +703,244 @@ declare const wqlHighlightStyle: HighlightStyle;
|
|
|
460
703
|
declare function wql(completionOptions?: WqlCompletionOptions): LanguageSupport;
|
|
461
704
|
|
|
462
705
|
/**
|
|
463
|
-
*
|
|
706
|
+
* WQL unit policy (wayfinder ticket 13, home per decision ticket 21).
|
|
464
707
|
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
708
|
+
* Recognition and conversion evidence live in the core catalog — this module
|
|
709
|
+
* adds the wql application policy: strict conversion before arithmetic, the
|
|
710
|
+
* system-default output table applied when the query has no `in <unit>`
|
|
711
|
+
* directive, and errors (never pass-through) for unknown units and
|
|
712
|
+
* dimension-incompatible conversions.
|
|
468
713
|
*
|
|
469
|
-
*
|
|
714
|
+
* Cutover notes (ticket 13): the old first-record-unit fallback and the
|
|
715
|
+
* unknown/incompatible pass-through in `resolveDisplayUnit` are gone. The
|
|
716
|
+
* host `kg | lb` widget preference is no longer a tier in the default chain —
|
|
717
|
+
* `useAnalyticsUnitPreference` callers migrate to the system defaults under
|
|
718
|
+
* ticket 19; no new callers allowed.
|
|
470
719
|
*/
|
|
471
|
-
|
|
720
|
+
|
|
721
|
+
/** Legacy 2-family vocabulary kept only as a type alias for existing
|
|
722
|
+
* callers of the display seam; arithmetic dimensions come from the catalog. */
|
|
472
723
|
type UnitFamily = 'mass' | 'distance';
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
base: string;
|
|
476
|
-
/** Factor to convert FROM this unit TO the base unit. */
|
|
477
|
-
units: Record<string, number>;
|
|
478
|
-
}
|
|
479
|
-
declare const UNIT_FAMILIES: Record<UnitFamily, UnitFamilyDef>;
|
|
480
|
-
declare function getUnitFamily(unit: string | undefined): UnitFamily | undefined;
|
|
481
|
-
/**
|
|
482
|
-
* Convert a value between known units.
|
|
483
|
-
*
|
|
484
|
-
* Returns the input unchanged when:
|
|
485
|
-
* - from === to
|
|
486
|
-
* - either unit is unknown
|
|
487
|
-
* - the units belong to different families
|
|
488
|
-
*/
|
|
489
|
-
declare function convert(value: number, from: string | undefined, to: string | undefined): number;
|
|
490
|
-
/**
|
|
491
|
-
* Determine the target display unit for a set of matched facts and query
|
|
492
|
-
* options. Returns the unit to render and whether values should be converted.
|
|
493
|
-
*/
|
|
494
|
-
declare function resolveDisplayUnit(facts: ReadonlyArray<{
|
|
724
|
+
/** The fact-unit shape resolveOutputUnit reads. */
|
|
725
|
+
interface UnitBearingFact {
|
|
495
726
|
unit?: string;
|
|
496
727
|
metricUnit?: string;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
728
|
+
}
|
|
729
|
+
/** Display conversion for UI surfaces (RawPointsTable etc.): converts
|
|
730
|
+
* within a shared dimension through the catalog, passes unknown or
|
|
731
|
+
* cross-dimension values through unchanged instead of throwing. Arithmetic
|
|
732
|
+
* must use {@link convertViaCatalog}. Ticket 19 migrates the remaining
|
|
733
|
+
* display callers onto renderer formatting. */
|
|
734
|
+
declare function convertDisplay(value: number, from: string | undefined, to: string | undefined): number;
|
|
735
|
+
/** Dimension compatibility probe for display surfaces. */
|
|
736
|
+
declare function getUnitFamily(unit: string | undefined): string | undefined;
|
|
737
|
+
interface OutputUnitResolution {
|
|
738
|
+
/** The resolved output unit — undefined means unitless / no conversion. */
|
|
501
739
|
unit?: string;
|
|
740
|
+
/** Whether fact values must be converted into `unit` before aggregation. */
|
|
502
741
|
convert: boolean;
|
|
503
|
-
|
|
504
|
-
|
|
742
|
+
/** Diagnostic when the directive is unusable for this result (unknown
|
|
743
|
+
* unit, or dimension-incompatible with the selected observations). */
|
|
744
|
+
error?: string;
|
|
745
|
+
}
|
|
505
746
|
/**
|
|
506
|
-
*
|
|
747
|
+
* Resolve the output unit for an aggregate result (arithmetic contract §5):
|
|
748
|
+
*
|
|
749
|
+
* 1. an explicit `in <unit>` directive wins when it is a known unit and
|
|
750
|
+
* dimensionally compatible with the observations; incompatible → error
|
|
751
|
+
* for the affected calculation;
|
|
752
|
+
* 2. otherwise the system default for the observations' dimension
|
|
753
|
+
* (mass `kg`, distance `m`, duration `s`, …; ratio unitless);
|
|
754
|
+
* 3. observations with no unit at all stay unitless — no first-record
|
|
755
|
+
* fallback, no reinterpretation of stored measurements.
|
|
507
756
|
*/
|
|
757
|
+
declare function resolveOutputUnit(facts: ReadonlyArray<UnitBearingFact>, { directive, preferred, }: {
|
|
758
|
+
directive?: string;
|
|
759
|
+
preferred?: string;
|
|
760
|
+
}): OutputUnitResolution;
|
|
508
761
|
|
|
509
762
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
*
|
|
763
|
+
* Calendar — system-local civil calendar arithmetic over captured execution
|
|
764
|
+
* contexts (wayfinder datadog-analytics ticket 12, per the time alignment
|
|
765
|
+
* contract §§ Evaluation context, Calendar and duration windows).
|
|
766
|
+
*
|
|
767
|
+
* Calendar days run from local midnight to the next local midnight in the
|
|
768
|
+
* context's timezone; weeks start on Monday. All arithmetic is component
|
|
769
|
+
* math via the timezone's civil date — never multiplication by 86 400 000
|
|
770
|
+
* ms, which mislabels DST-shifted days. Range endpoints are half-open:
|
|
771
|
+
* start included, exclusive end; an inclusive `to` civil date is implemented
|
|
772
|
+
* as an exclusive bound at the following local midnight.
|
|
517
773
|
*/
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
|
|
525
|
-
/** Content-stable cross-note join key. */
|
|
526
|
-
blockContentId?: string;
|
|
527
|
-
/** Which surface produced the result; trend queries exclude 'playground' by default. */
|
|
528
|
-
origin?: ResultOrigin;
|
|
529
|
-
/** FK to the `page` store (copied from the parent note). */
|
|
530
|
-
pageId?: string;
|
|
531
|
-
/**
|
|
532
|
-
* Canonical workout time — WorkoutResult.createdAt (true workout end).
|
|
533
|
-
* Every fact row carries it as `timestamp` so time-range queries mean
|
|
534
|
-
* "when the workout happened", never "when the metric was derived".
|
|
535
|
-
*/
|
|
536
|
-
workoutTimestamp?: number;
|
|
774
|
+
/** One captured execution context: every relative window, bucket, and
|
|
775
|
+
* boundary decision in a document run resolves against this one capture. */
|
|
776
|
+
interface ExecutionContext {
|
|
777
|
+
/** Captured current instant (ms epoch). */
|
|
778
|
+
readonly instant: number;
|
|
779
|
+
/** IANA timezone name of the effective system timezone. */
|
|
780
|
+
readonly timeZone: string;
|
|
537
781
|
}
|
|
538
|
-
/**
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
782
|
+
/** Capture the execution context — once per document run (ticket 19's
|
|
783
|
+
* runner captures; QueryService consumes). */
|
|
784
|
+
declare function captureContext(instant?: number, timeZone?: string): ExecutionContext;
|
|
785
|
+
/** The system's IANA timezone. */
|
|
786
|
+
declare function systemTimeZone(): string;
|
|
787
|
+
/** Civil YYYY-MM-DD of `instant` interpreted in `timeZone` — the calendar
|
|
788
|
+
* the athlete lives in, not UTC. */
|
|
789
|
+
declare function civilDateOf(instant: number, timeZone: string): string;
|
|
790
|
+
/** Add `days` (may be negative) to a civil date — pure date math across
|
|
791
|
+
* month/year boundaries and leap days; no timezone involved. */
|
|
792
|
+
declare function civilDateAdd(iso: string, days: number): string;
|
|
793
|
+
/** Days between two civil dates (second minus first). */
|
|
794
|
+
declare function civilDateDiff(a: string, b: string): number;
|
|
795
|
+
/** The Monday-starting civil week of a civil date. */
|
|
796
|
+
declare function civilMonday(iso: string): string;
|
|
797
|
+
/** The instant of local midnight (00:00) starting civil date `iso` in
|
|
798
|
+
* `timeZone` — component math that survives DST boundaries (a 23h or 25h
|
|
799
|
+
* day still starts at its own local 00:00). */
|
|
800
|
+
declare function zonedStartOfDay(iso: string, timeZone: string): number;
|
|
801
|
+
/** Local noon of civil date `iso` in `timeZone` — the presentation-only
|
|
802
|
+
* representative instant for calendar buckets (never a join key). */
|
|
803
|
+
declare function zonedNoon(iso: string, timeZone: string): number;
|
|
804
|
+
/** A resolved range: [start, end). `endExclusive` marks the half-open
|
|
805
|
+
* convention; `end === Number.MAX_SAFE_INTEGER` with `endExclusive ===
|
|
806
|
+
* false` is the unbounded side. */
|
|
807
|
+
interface ResolvedRange {
|
|
808
|
+
readonly start: number;
|
|
809
|
+
readonly end: number;
|
|
810
|
+
/** True when `end` is an exclusive half-open bound (calendar windows and
|
|
811
|
+
* captured-instant relative cutoffs). */
|
|
812
|
+
readonly endExclusive: boolean;
|
|
553
813
|
}
|
|
554
|
-
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*/
|
|
566
|
-
declare function toEventRows(logs: readonly StoredOutputStatement[], identity: SummaryFactIdentity): UnifiedEventRecord[];
|
|
567
|
-
/**
|
|
568
|
-
* Tier-2 analytics outputs → deterministic summary event rows (tickets
|
|
569
|
-
* 002/004): id `${resultId}:summary:${rowKey}` — no timestamp salt, so
|
|
570
|
-
* re-finalize overwrites cleanly. Fold identity lives in metrics[0].metadata
|
|
571
|
-
* (canonicalKey, effort metadata, groupTags) — shape-uniform with events.
|
|
814
|
+
/** Resolve a parsed query window against the captured context — the single
|
|
815
|
+
* window→range mapping every execution path shares (time contract):
|
|
816
|
+
*
|
|
817
|
+
* - `last Nd`: N local dates including today — midnight N−1 dates back
|
|
818
|
+
* through the captured current instant (inclusive cutoff, exclusive
|
|
819
|
+
* encoding `instant + 1`). Never N×86 400 000 ms.
|
|
820
|
+
* - `last Nw`: current Monday-start week plus N−1 preceding complete
|
|
821
|
+
* weeks, through the captured instant. Never a rolling 7N-date window.
|
|
822
|
+
* - explicit `from`/`to`: both named dates included in full — implemented
|
|
823
|
+
* as an exclusive bound at the following local midnight. An open `to`
|
|
824
|
+
* stays unbounded. Future ranges are preserved, never replaced.
|
|
572
825
|
*/
|
|
573
|
-
declare function
|
|
826
|
+
declare function resolveWindowRange(w: {
|
|
827
|
+
kind: 'relative';
|
|
828
|
+
size: number;
|
|
829
|
+
unit: 'd' | 'w';
|
|
830
|
+
} | {
|
|
831
|
+
kind: 'range';
|
|
832
|
+
start: string;
|
|
833
|
+
end?: string;
|
|
834
|
+
} | undefined, ctx: ExecutionContext): ResolvedRange | undefined;
|
|
835
|
+
/** Half-open membership predicate: start included, exclusive end excluded
|
|
836
|
+
* (a metric exactly at Monday midnight belongs only to the new week).
|
|
837
|
+
* Unbounded ends admit everything. */
|
|
838
|
+
declare function inRange(instant: number, range: ResolvedRange | undefined): boolean;
|
|
839
|
+
|
|
574
840
|
/**
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
841
|
+
* Query documents (wayfinder datadog-analytics ticket 17, per the query
|
|
842
|
+
* documents contract).
|
|
843
|
+
*
|
|
844
|
+
* A query block is a self-contained document:
|
|
845
|
+
*
|
|
846
|
+
* defaults [by {dim, …}] [window]
|
|
847
|
+
* <ident> = <query | expression> [(normalize <bucket>)] [-> <unit>]
|
|
848
|
+
* show <ident>, …
|
|
849
|
+
*
|
|
850
|
+
* A body that carries no document constructs parses as a DEGENERATE document
|
|
851
|
+
* wrapping the single query — zero behavior change for legacy bodies
|
|
852
|
+
* (decision ticket 22: every widget body is a document; single-line bodies
|
|
853
|
+
* are degenerate documents).
|
|
854
|
+
*
|
|
855
|
+
* Evaluation order follows the reference graph; forward references are
|
|
856
|
+
* allowed; cycles are a document-level diagnostic.
|
|
580
857
|
*/
|
|
581
|
-
declare function projectEventToFacts(record: UnifiedEventRecord): AnalyticsDataPoint[];
|
|
582
858
|
|
|
859
|
+
interface DocumentDefaults {
|
|
860
|
+
groupBy?: string[];
|
|
861
|
+
window?: QueryWindow;
|
|
862
|
+
}
|
|
863
|
+
interface DocumentFormula {
|
|
864
|
+
expression: string;
|
|
865
|
+
/** Referenced assignment names, in appearance order. */
|
|
866
|
+
references: string[];
|
|
867
|
+
/** Persisted normalization intent (user-selected target bucket). */
|
|
868
|
+
normalize?: string;
|
|
869
|
+
/** Explicit output unit directive. */
|
|
870
|
+
unit?: string;
|
|
871
|
+
}
|
|
872
|
+
interface DocumentAssignment {
|
|
873
|
+
name: string;
|
|
874
|
+
kind: 'query' | 'formula';
|
|
875
|
+
/** For kind 'query' — the parsed query of any family (defaults merged at run). */
|
|
876
|
+
queryText?: string;
|
|
877
|
+
parsed?: AnyParsedQuery;
|
|
878
|
+
formula?: DocumentFormula;
|
|
879
|
+
raw: string;
|
|
880
|
+
}
|
|
881
|
+
interface QueryDocument {
|
|
882
|
+
defaults?: DocumentDefaults;
|
|
883
|
+
assignments: DocumentAssignment[];
|
|
884
|
+
show: string[];
|
|
885
|
+
/** True when the body carried no document constructs (legacy single query). */
|
|
886
|
+
degenerate: boolean;
|
|
887
|
+
}
|
|
888
|
+
interface ParsedDocument {
|
|
889
|
+
doc: QueryDocument;
|
|
890
|
+
diagnostics: string[];
|
|
891
|
+
}
|
|
583
892
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
* the directory the file lives under (e.g. `crossfit-girls` for collections,
|
|
587
|
-
* `crossfit-programming` for feeds) and is what the Library's panel uses to
|
|
588
|
-
* target the `+ Filter → Catalog` menu.
|
|
893
|
+
* Parse a query-document body. Never throws: syntax problems surface as
|
|
894
|
+
* diagnostics so one broken block never takes down a surface.
|
|
589
895
|
*/
|
|
590
|
-
declare function
|
|
896
|
+
declare function parseDocument(text: string): ParsedDocument;
|
|
897
|
+
/** Lossless serialization: round-trips parse(serialize(parse(text))) to the
|
|
898
|
+
* same document (used by dashboards and note rendering to persist intent). */
|
|
899
|
+
declare function serializeDocument(doc: QueryDocument): string;
|
|
900
|
+
|
|
591
901
|
/**
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
902
|
+
* Contribution selection — grain integrity and coverage (wayfinder
|
|
903
|
+
* datadog-analytics ticket 16, per the automatic grain contract).
|
|
904
|
+
*
|
|
905
|
+
* Coverage is chosen within each workout/result and typed metric — never by
|
|
906
|
+
* an `any attributed record exists` check over the whole query:
|
|
907
|
+
*
|
|
908
|
+
* - Detail observations and substitute summaries of the same population are
|
|
909
|
+
* duplicate representations: only one is aggregated (detail wins).
|
|
910
|
+
* - A summary with a coverage partition (effort / group tags) disjoint from
|
|
911
|
+
* the present detail substitutes exactly for its uncovered partition —
|
|
912
|
+
* provided its retained statistics answer the requested operation.
|
|
913
|
+
* - A retained population represented only by an insufficient summary
|
|
914
|
+
* reports insufficient evidence — never an invented observation.
|
|
915
|
+
* - Explicit grain filters are hard source constraints and report
|
|
916
|
+
* limitations rather than silently switching representation.
|
|
596
917
|
*/
|
|
597
|
-
|
|
918
|
+
|
|
919
|
+
/** Compact coverage references carried on aggregate results (ticket 16
|
|
920
|
+
* explainability; ticket 18's drill-down consumes the on-demand detail). */
|
|
921
|
+
interface CoverageReport {
|
|
922
|
+
/** (resultId, metric) populations selected as detail observations. */
|
|
923
|
+
detailPopulations: number;
|
|
924
|
+
/** Substitute summaries that answered the requested operation. */
|
|
925
|
+
selectedSummaries: Array<{
|
|
926
|
+
resultId: string;
|
|
927
|
+
metricKey: string;
|
|
928
|
+
coverage?: SummaryCoverage;
|
|
929
|
+
}>;
|
|
930
|
+
/** Duplicate representations dropped because detail covered them. */
|
|
931
|
+
ignoredDuplicates: Array<{
|
|
932
|
+
resultId: string;
|
|
933
|
+
metricKey: string;
|
|
934
|
+
id: string;
|
|
935
|
+
}>;
|
|
936
|
+
/** Populations that could not answer the operation — reported, never
|
|
937
|
+
* silently omitted and never fabricated. */
|
|
938
|
+
insufficientScopes: Array<{
|
|
939
|
+
resultId: string;
|
|
940
|
+
metricKey: string;
|
|
941
|
+
reason: string;
|
|
942
|
+
}>;
|
|
943
|
+
}
|
|
598
944
|
|
|
599
945
|
/**
|
|
600
946
|
* Injected store interfaces for WQL QueryService.
|
|
@@ -610,37 +956,125 @@ declare function staticTagIndexFromBlocks(blocks: BlockIndexRow[]): Map<string,
|
|
|
610
956
|
* Query Service needs, plus the write contract owned by the write-path
|
|
611
957
|
* lifecycle (ticket 005).
|
|
612
958
|
*/
|
|
613
|
-
interface
|
|
959
|
+
interface EventStore {
|
|
960
|
+
/** Ticket 12/14 complete-fetch seam: candidates whose METRIC date
|
|
961
|
+
* (civil, multiEntry index) falls in the requested dates — rows whose
|
|
962
|
+
* own date differs from their fetch-hint timestamp are reachable. */
|
|
963
|
+
getEventsByMetricDates?(dates: readonly string[]): Promise<EventRecord[]>;
|
|
614
964
|
/** Windowed fetch — the one proven culling index (ticket 001). */
|
|
615
|
-
getEventsByTimeRange(start: number, end: number): Promise<
|
|
965
|
+
getEventsByTimeRange(start: number, end: number): Promise<EventRecord[]>;
|
|
616
966
|
/** Per-result fetch (rows:{result:…}, re-finalize, orphan inspection). */
|
|
617
|
-
getEventsByResult(resultId: string): Promise<
|
|
967
|
+
getEventsByResult(resultId: string): Promise<EventRecord[]>;
|
|
618
968
|
/** Note-scoped fetch (rows:{note:…}). */
|
|
619
|
-
getEventsForNote(noteId: string): Promise<
|
|
969
|
+
getEventsForNote(noteId: string): Promise<EventRecord[]>;
|
|
620
970
|
/** Content-scoped fetch — the cross-store join hot path (indexed). */
|
|
621
|
-
getEventsByContent(blockContentId: string): Promise<
|
|
971
|
+
getEventsByContent(blockContentId: string): Promise<EventRecord[]>;
|
|
622
972
|
/** Full scan — all-time SELECT leg (ticket 001: scan beats non-selective indexes). */
|
|
623
|
-
scanAll(): Promise<
|
|
973
|
+
scanAll(): Promise<EventRecord[]>;
|
|
624
974
|
/** Append event rows (per-statement flush; same-tick coalescing allowed). */
|
|
625
|
-
appendEvents(rows:
|
|
975
|
+
appendEvents(rows: EventRecord[]): Promise<void>;
|
|
626
976
|
/** Atomic finalize: clear the result's engine-authored summaries, write finals. */
|
|
627
|
-
finalizeSummaries(resultId: string, rows:
|
|
977
|
+
finalizeSummaries(resultId: string, rows: EventRecord[]): Promise<void>;
|
|
628
978
|
/** Reconcile deletes (wellness note-save) + GC sweeps. */
|
|
629
979
|
deleteEvents(ids: string[]): Promise<void>;
|
|
630
980
|
}
|
|
631
|
-
/** Store surface for content queries (
|
|
981
|
+
/** Store surface for content queries (`:note`). */
|
|
632
982
|
interface NoteQueryStore {
|
|
633
983
|
getAllNotes(): Promise<Note[]>;
|
|
634
984
|
getNoteIdsForTag(label: string): Promise<Set<string>>;
|
|
635
985
|
/** Note-tags label set of one note (moved here from FactQueryStore — it
|
|
636
986
|
* reads note tags, not facts). */
|
|
637
987
|
getNoteTagLabels(noteId: string): Promise<string[]>;
|
|
988
|
+
/** Indexed/domain candidate read for :note — `selection` carries only
|
|
989
|
+
* exact-semantics predicates, so rows are a SUPERSET of the matches and
|
|
990
|
+
* QueryService re-applies every residual filter in JS. `undefined` =
|
|
991
|
+
* capability unavailable (no domain projection / scoped transaction) →
|
|
992
|
+
* the caller falls back to getAllNotes(). Server order/offset/limit ride
|
|
993
|
+
* only when no residual filter, window, or join precedes the slice. */
|
|
994
|
+
queryDomain?(request: {
|
|
995
|
+
plan: 'notes';
|
|
996
|
+
selection?: WqlDomainPredicate[];
|
|
997
|
+
filters?: WqlDomainPredicate[];
|
|
998
|
+
order?: WqlDomainOrder[];
|
|
999
|
+
offset?: number;
|
|
1000
|
+
limit?: number;
|
|
1001
|
+
}): Promise<WqlDomainReadResult | undefined>;
|
|
638
1002
|
}
|
|
639
|
-
/**
|
|
1003
|
+
/**
|
|
1004
|
+
* Candidate/domain read wire — a structural mirror of packages/storage/src/
|
|
1005
|
+
* domain.ts (root-owned). WQL stays DB-independent: no storage-package
|
|
1006
|
+
* dependency; the app adapter bridges these shapes 1:1 to
|
|
1007
|
+
* IStorage.queryDomain. Keep field-by-field compatible with that file.
|
|
1008
|
+
*
|
|
1009
|
+
* Clause algebra (identical to the wire): clauses AND; a clause's value list
|
|
1010
|
+
* ORs; `negate` excludes any row matching any value.
|
|
1011
|
+
*/
|
|
1012
|
+
type WqlDomainPredicate = {
|
|
1013
|
+
field: 'id' | 'noteId' | 'type' | 'catalog' | 'source' | 'tags' | 'effort';
|
|
1014
|
+
values: string[];
|
|
1015
|
+
negate?: boolean;
|
|
1016
|
+
} | {
|
|
1017
|
+
field: 'text';
|
|
1018
|
+
value: string;
|
|
1019
|
+
} | {
|
|
1020
|
+
field: 'date';
|
|
1021
|
+
start: number;
|
|
1022
|
+
end: number;
|
|
1023
|
+
endExclusive?: boolean;
|
|
1024
|
+
} | {
|
|
1025
|
+
field: 'page';
|
|
1026
|
+
value: boolean;
|
|
1027
|
+
}
|
|
1028
|
+
/** The :note default page-exclusion stage (notes plan only). `feeds`
|
|
1029
|
+
* = an authored feed source clause rides along: true drops literal
|
|
1030
|
+
* type 'page' rows; false drops isPage rows (page-like kinds or page:/
|
|
1031
|
+
* guides: source prefixes on non-note kinds). */
|
|
1032
|
+
| {
|
|
1033
|
+
field: 'defaultNotes';
|
|
1034
|
+
feeds: boolean;
|
|
1035
|
+
} | {
|
|
1036
|
+
field: 'sourceFence';
|
|
1037
|
+
};
|
|
1038
|
+
/** Server order clause — structural mirror of the storage wire's
|
|
1039
|
+
* DomainOrder. Only exact-parity orderings are ever sent (natural id order
|
|
1040
|
+
* matches the historical whole-store iteration order). */
|
|
1041
|
+
type WqlDomainOrder = {
|
|
1042
|
+
field: 'id' | 'createdAt' | 'date' | 'title' | 'noteTitle' | 'dataType' | 'position';
|
|
1043
|
+
direction: 'asc' | 'desc';
|
|
1044
|
+
};
|
|
1045
|
+
/** Candidate read result for the plans WQL consumes. `rows` are original
|
|
1046
|
+
* entity payloads; the counts follow the wire contract (selected before
|
|
1047
|
+
* filters, matched before limit) — selectedCount baselines the historical
|
|
1048
|
+
* pre-filter `stages.selected`, matchedCount is informational (WQL
|
|
1049
|
+
* recounts after its own residual pipeline and slice). */
|
|
1050
|
+
type WqlDomainReadResult = {
|
|
1051
|
+
plan: 'notes';
|
|
1052
|
+
projectionVersion: number;
|
|
1053
|
+
selectedCount: number;
|
|
1054
|
+
matchedCount: number;
|
|
1055
|
+
rows: Note[];
|
|
1056
|
+
} | {
|
|
1057
|
+
plan: 'blocks';
|
|
1058
|
+
projectionVersion: number;
|
|
1059
|
+
selectedCount: number;
|
|
1060
|
+
matchedCount: number;
|
|
1061
|
+
rows: BlockIndexRow[];
|
|
1062
|
+
};
|
|
1063
|
+
/** Store surface for block-index queries (`:block`). */
|
|
640
1064
|
interface BlockQueryStore {
|
|
641
1065
|
getAllBlocks(): Promise<BlockIndexRow[]>;
|
|
1066
|
+
/** Indexed/domain candidate read for :block — same superset contract as
|
|
1067
|
+
* the note seam; `undefined` falls back to getAllBlocks(). */
|
|
1068
|
+
queryDomain?(request: {
|
|
1069
|
+
plan: 'blocks';
|
|
1070
|
+
selection?: WqlDomainPredicate[];
|
|
1071
|
+
filters?: WqlDomainPredicate[];
|
|
1072
|
+
order?: WqlDomainOrder[];
|
|
1073
|
+
offset?: number;
|
|
1074
|
+
limit?: number;
|
|
1075
|
+
}): Promise<WqlDomainReadResult | undefined>;
|
|
642
1076
|
}
|
|
643
|
-
/** Pure effort model interface for
|
|
1077
|
+
/** Pure effort model interface for `:effort` queries. */
|
|
644
1078
|
interface IEffort {
|
|
645
1079
|
id: string;
|
|
646
1080
|
slug: string;
|
|
@@ -655,18 +1089,69 @@ interface IEffort {
|
|
|
655
1089
|
registrySource?: 'bundled' | 'user' | string;
|
|
656
1090
|
[key: string]: unknown;
|
|
657
1091
|
}
|
|
658
|
-
/** Store surface for effort queries (`find:effort`). */
|
|
659
1092
|
interface EffortQueryStore {
|
|
660
1093
|
getAllEfforts(): Promise<IEffort[]>;
|
|
661
1094
|
}
|
|
662
1095
|
/** Optional bundle of stores for QueryService initialization. */
|
|
1096
|
+
/** One note's hydration relations — structurally the storage entries
|
|
1097
|
+
* bundle's relation slice, so a host can bridge its entry batch reads
|
|
1098
|
+
* (local: one transaction; remote: entries plan with ids chunked at the
|
|
1099
|
+
* domain parser's 64-value limit) straight through without mapping. WQL
|
|
1100
|
+
* reads only `label` off each tag record and never imports storage. */
|
|
1101
|
+
interface NoteRelationBundle {
|
|
1102
|
+
/** The note's junction tag records (label plane). */
|
|
1103
|
+
tags: ReadonlyArray<{
|
|
1104
|
+
label: string;
|
|
1105
|
+
}>;
|
|
1106
|
+
/** page_notes junction rows for the note; first by position is the
|
|
1107
|
+
* parent page. */
|
|
1108
|
+
links: PageNote[];
|
|
1109
|
+
/** Resolved junction pages by page id. */
|
|
1110
|
+
pages: Page[];
|
|
1111
|
+
}
|
|
663
1112
|
interface QueryServiceStores {
|
|
664
|
-
eventStore?:
|
|
1113
|
+
eventStore?: EventStore;
|
|
665
1114
|
noteStore?: NoteQueryStore;
|
|
1115
|
+
/** Serves every block row — user journal and seeded corpus alike (the
|
|
1116
|
+
* seed importer materializes corpus rows into the same store). */
|
|
666
1117
|
blockStore?: BlockQueryStore;
|
|
667
1118
|
effortStore?: EffortQueryStore;
|
|
1119
|
+
/** Derived corpus-note projections (catalog/tag planes). */
|
|
668
1120
|
staticNoteStore?: NoteQueryStore;
|
|
669
|
-
|
|
1121
|
+
/** Serves exercise containment rows (`block_efforts` store). */
|
|
1122
|
+
blockEffortsStore?: {
|
|
1123
|
+
getAllFromIndex(index: string, key: string): Promise<any[]>;
|
|
1124
|
+
};
|
|
1125
|
+
/** Serves tag records (`tags` store). */
|
|
1126
|
+
tagsStore?: {
|
|
1127
|
+
getAllFromIndex(index: string, key: string): Promise<any[]>;
|
|
1128
|
+
};
|
|
1129
|
+
/** Serves note-tag junction rows (`note_tags` store). */
|
|
1130
|
+
noteTagsStore?: {
|
|
1131
|
+
getAllFromIndex(index: string, key: string): Promise<any[]>;
|
|
1132
|
+
};
|
|
1133
|
+
/** Canonical page_notes junction reads for container resolution — a
|
|
1134
|
+
* note's parent Page wins over a standalone Note container. */
|
|
1135
|
+
pageStore?: {
|
|
1136
|
+
getNotePages(noteId: string): Promise<PageNote[]>;
|
|
1137
|
+
getPage(pageId: string): Promise<Page | undefined>;
|
|
1138
|
+
};
|
|
1139
|
+
/** Batched relation reads for the hydration paths (containers, run
|
|
1140
|
+
* tags) — one bundle per distinct note id instead of a page/tag read
|
|
1141
|
+
* per matched result. Optional: absent → the per-note pageStore /
|
|
1142
|
+
* noteStore seams above serve each request. */
|
|
1143
|
+
relationStore?: {
|
|
1144
|
+
getBatches(noteIds: readonly string[]): Promise<Map<string, NoteRelationBundle>>;
|
|
1145
|
+
};
|
|
1146
|
+
/** Synchronous named datasets (`@session`, `@today`, user-stored sources)
|
|
1147
|
+
* populated by the host before queries run — pipeline dataset stages
|
|
1148
|
+
* never touch the database. */
|
|
1149
|
+
datasetStore?: {
|
|
1150
|
+
getDataset(name: string): {
|
|
1151
|
+
events: EventRecord[];
|
|
1152
|
+
notes: Note[];
|
|
1153
|
+
} | undefined;
|
|
1154
|
+
};
|
|
670
1155
|
}
|
|
671
1156
|
|
|
672
1157
|
/**
|
|
@@ -690,37 +1175,124 @@ interface QueryServiceStores {
|
|
|
690
1175
|
* Fully inverted dependencies: zero IndexedDB/storage module-level imports.
|
|
691
1176
|
*/
|
|
692
1177
|
|
|
1178
|
+
/** Extract the catalog directory id from a Note or BlockIndexRow.
|
|
1179
|
+
* Uses explicit `catalog` when present; falls back to parsing `sourceId`
|
|
1180
|
+
* (stripping `collection:`/`feed:` prefixes and `feeds/` path components) or `noteId`.
|
|
1181
|
+
* Exported so host mappers (Entry derivation) carry the exact field the
|
|
1182
|
+
* `catalog:` filter matches — presentation catalogs must not diverge. */
|
|
1183
|
+
declare function catalogOfItem(item: {
|
|
1184
|
+
id?: string;
|
|
1185
|
+
noteId?: string;
|
|
1186
|
+
sourceId?: string;
|
|
1187
|
+
catalog?: string;
|
|
1188
|
+
}): string | undefined;
|
|
1189
|
+
/** Match a single row against one source filter value. The `journal` kind matches
|
|
1190
|
+
* rows with no sourceId, the literal 'journal', or UUID / journal/date lineage
|
|
1191
|
+
* pointing to the original note. Lineage also passes corpus exclusions below.
|
|
1192
|
+
* The feed / guide kinds match
|
|
1193
|
+
* rows whose sourceId starts with the kind (storage identities keep their
|
|
1194
|
+
* `collection:`/`page:collection:` prefixes — only the query vocabulary
|
|
1195
|
+
* was renamed). A `kind:id` literal matches the
|
|
1196
|
+
* exact id. `playground` matches the playground intake's sourceId convention
|
|
1197
|
+
* and, on the note plane, legacy rows typed 'playground' (playground pages
|
|
1198
|
+
* saved before the sourceId convention existed — their sourceId is absent).
|
|
1199
|
+
* `all` spans exactly the WQL source domain plus sourceless rows — identical
|
|
1200
|
+
* to the source fence. `journal` rejects the rest of the seed corpus: rows
|
|
1201
|
+
* with seed provenance (importer stamps, or surviving seedChunkId /
|
|
1202
|
+
* markdown/ sourcePath / catalog on legacy user-edited rows whose
|
|
1203
|
+
* seedOrigin flipped to 'user') and typed dashboards never journal — a user
|
|
1204
|
+
* journal note carries none of those markers. Exported so host mappers
|
|
1205
|
+
* classify result rows with the exact predicate the `source:` filter
|
|
1206
|
+
* applies. */
|
|
1207
|
+
declare function sourceMatches(item: {
|
|
1208
|
+
id?: string;
|
|
1209
|
+
noteId?: string;
|
|
1210
|
+
sourceId?: string;
|
|
1211
|
+
type?: string;
|
|
1212
|
+
seedOrigin?: string;
|
|
1213
|
+
seedChunkId?: string;
|
|
1214
|
+
sourcePath?: string;
|
|
1215
|
+
catalog?: string;
|
|
1216
|
+
}, kind: string): boolean;
|
|
693
1217
|
interface FindQueryResult {
|
|
694
1218
|
parsed: ParsedFindQuery;
|
|
695
1219
|
notes: Note[];
|
|
696
1220
|
blocks: BlockIndexRow[];
|
|
697
|
-
/** Registry rows for
|
|
1221
|
+
/** Registry rows for :effort queries. */
|
|
698
1222
|
efforts?: IEffort[];
|
|
1223
|
+
/** Session run cards for :session queries (#1041). */
|
|
1224
|
+
runs?: RowsRun[];
|
|
1225
|
+
/** Tabular output for :segment and :event queries (#1042). */
|
|
1226
|
+
table?: TabularResult;
|
|
1227
|
+
/** Raw event rows behind the result — session run statements and table
|
|
1228
|
+
* observations. Pipeline stages aggregate these in memory (no re-read). */
|
|
1229
|
+
events?: EventRecord[];
|
|
1230
|
+
/** Highest container per note id: the parent Page when the canonical
|
|
1231
|
+
* page_notes junction links one, else the standalone Note. Present only
|
|
1232
|
+
* when a pageStore is injected and notes were matched. */
|
|
1233
|
+
containers?: Record<string, NoteContainer>;
|
|
699
1234
|
stages: {
|
|
700
1235
|
selected: number;
|
|
701
1236
|
matched: number;
|
|
702
1237
|
};
|
|
703
1238
|
}
|
|
1239
|
+
/** Highest-container link for a matched note (parent Page wins). `slug` and
|
|
1240
|
+
* `date` route app URLs (/p/:slug, /journal/:date). Core Page has no type —
|
|
1241
|
+
* the parent is never classified by the note's kind. */
|
|
1242
|
+
type NoteContainer = {
|
|
1243
|
+
kind: 'page';
|
|
1244
|
+
id: string;
|
|
1245
|
+
slug?: string;
|
|
1246
|
+
date?: string;
|
|
1247
|
+
} | {
|
|
1248
|
+
kind: 'note';
|
|
1249
|
+
id: string;
|
|
1250
|
+
};
|
|
1251
|
+
/** Options for runPipeline: query options plus host-populated datasets.
|
|
1252
|
+
* `pageSources` wins over an injected `datasetStore`; both are synchronous
|
|
1253
|
+
* in-memory lookups — dataset stages never roundtrip the database. */
|
|
1254
|
+
interface PipelineOptions extends QueryOptions {
|
|
1255
|
+
/** Host-populated datasets keyed by name (names include `@`). */
|
|
1256
|
+
pageSources?: ReadonlyMap<string, {
|
|
1257
|
+
events: EventRecord[];
|
|
1258
|
+
notes: Note[];
|
|
1259
|
+
}>;
|
|
1260
|
+
}
|
|
1261
|
+
/** Result of one pipeline run: the final stage's data under its existing
|
|
1262
|
+
* result type, plus the chart sink marker for the renderer. */
|
|
1263
|
+
interface PipelineResult {
|
|
1264
|
+
parsed: ParsedPipelineQuery;
|
|
1265
|
+
series?: Series[];
|
|
1266
|
+
table?: TabularResult;
|
|
1267
|
+
notes?: Note[];
|
|
1268
|
+
blocks?: BlockIndexRow[];
|
|
1269
|
+
efforts?: IEffort[];
|
|
1270
|
+
runs?: RowsRun[];
|
|
1271
|
+
/** Highest container per note id (see FindQueryResult.containers). */
|
|
1272
|
+
containers?: Record<string, NoteContainer>;
|
|
1273
|
+
unit?: string;
|
|
1274
|
+
chart?: PipelineSink;
|
|
1275
|
+
error?: string;
|
|
1276
|
+
}
|
|
704
1277
|
interface QueryOptions {
|
|
705
1278
|
rangeStart?: number;
|
|
706
1279
|
rangeEnd?: number;
|
|
707
1280
|
/** App-level unit preference ('kg' | 'lb'). Used when query has no `in <unit>` directive. */
|
|
708
1281
|
preferredUnit?: string;
|
|
1282
|
+
/** Captured execution context (ticket 12) — one `{instant, timeZone}`
|
|
1283
|
+
* capture per document run; defaults to a fresh system capture. */
|
|
1284
|
+
context?: ExecutionContext;
|
|
709
1285
|
}
|
|
710
1286
|
/** Options for `runFind` / `runFindBlock` — overrides for the parsed WQL. */
|
|
711
1287
|
interface FindOptions {
|
|
712
|
-
/**
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
/** Time-window anchoring mode. 'wall-clock' anchors to Date.now();
|
|
718
|
-
* 'latest-activity' anchors to the newest row in the scope selection
|
|
719
|
-
* (#857). Defaults to 'wall-clock'. */
|
|
720
|
-
anchor?: 'wall-clock' | 'latest-activity';
|
|
721
|
-
/** Explicit reference time for the window (useful for tests/replay).
|
|
722
|
-
* Overrides `anchor`. */
|
|
1288
|
+
/** Host-supplied timestamp range (half-open [start, end)) — the DEFAULT
|
|
1289
|
+
* when the parsed WQL has no window; an explicit query window wins. */
|
|
1290
|
+
range?: ResolvedRange;
|
|
1291
|
+
/** Explicit reference time for the window (tests/replay). Superseded by
|
|
1292
|
+
* `context` when both are given. */
|
|
723
1293
|
anchorNow?: number;
|
|
1294
|
+
/** Captured execution context (ticket 12). */
|
|
1295
|
+
context?: ExecutionContext;
|
|
724
1296
|
}
|
|
725
1297
|
interface QueryResult {
|
|
726
1298
|
parsed: ParsedAggregateQuery;
|
|
@@ -736,6 +1308,10 @@ interface QueryResult {
|
|
|
736
1308
|
scalar?: number;
|
|
737
1309
|
/** Result display unit, if determined by directive, preference, or fact metadata. */
|
|
738
1310
|
unit?: string;
|
|
1311
|
+
/** First diagnostic across series (ticket 13) — per-widget badges in 19. */
|
|
1312
|
+
error?: string;
|
|
1313
|
+
/** Compact coverage references (ticket 16 explainability). */
|
|
1314
|
+
coverage?: CoverageReport;
|
|
739
1315
|
}
|
|
740
1316
|
/** One run in a rows result: the canonical result identity plus the event
|
|
741
1317
|
* rows that survived the optional output-type narrowing (`rows:segment{…}`). */
|
|
@@ -744,48 +1320,125 @@ interface RowsRun {
|
|
|
744
1320
|
noteId: string;
|
|
745
1321
|
/** Canonical workout time (result.createdAt under the unified model). */
|
|
746
1322
|
timestamp: number;
|
|
747
|
-
events:
|
|
1323
|
+
events: EventRecord[];
|
|
748
1324
|
}
|
|
749
1325
|
interface RowsQueryResult {
|
|
750
1326
|
parsed: ParsedRowsQuery;
|
|
751
1327
|
runs: RowsRun[];
|
|
752
1328
|
error?: string;
|
|
1329
|
+
/** Ticket 18 — cross-workout tabular result (cross-workout rows only). */
|
|
1330
|
+
table?: TabularResult;
|
|
1331
|
+
}
|
|
1332
|
+
interface TabularColumn {
|
|
1333
|
+
name: string;
|
|
1334
|
+
type: 'date' | 'string' | 'number';
|
|
1335
|
+
unit?: string;
|
|
1336
|
+
}
|
|
1337
|
+
/** Civil dates (YYYY-MM-DD, `timeZone`-local) whose [local midnight,
|
|
1338
|
+
* next local midnight) windows intersect `[start, end)` — the by-metric-date
|
|
1339
|
+
* candidate set for the complete fetch (ticket 12/14). Callers pass bounded
|
|
1340
|
+
* ranges only: an unbounded side uses the all-store scan as the complete
|
|
1341
|
+
* fetch instead of enumerating an open horizon. */
|
|
1342
|
+
declare function civilDatesCoveredByRange(start: number, end: number, timeZone: string): string[];
|
|
1343
|
+
/** The stable tabular shape consumed by table widgets (ticket 19 wires
|
|
1344
|
+
* widgets): bounded page + full match count. Missing column values are
|
|
1345
|
+
* ABSENT (undefined) — strictly distinct from a recorded 0. */
|
|
1346
|
+
interface TabularGroup {
|
|
1347
|
+
key: string;
|
|
1348
|
+
label: string;
|
|
1349
|
+
rows: Array<Record<string, unknown>>;
|
|
1350
|
+
}
|
|
1351
|
+
interface TabularResult {
|
|
1352
|
+
columns: TabularColumn[];
|
|
1353
|
+
rows: Array<Record<string, unknown>>;
|
|
1354
|
+
groups?: TabularGroup[];
|
|
1355
|
+
totalCount: number;
|
|
1356
|
+
limit?: number;
|
|
1357
|
+
offset?: number;
|
|
753
1358
|
}
|
|
1359
|
+
/** Structural missing-group sentinel — distinct from any literal text
|
|
1360
|
+
* (group identity is the JSON tuple, so it cannot collide). */
|
|
1361
|
+
declare const UNASSIGNED = "\0unassigned";
|
|
754
1362
|
declare class QueryService {
|
|
755
1363
|
private readonly store;
|
|
756
1364
|
private readonly noteStore;
|
|
757
1365
|
private readonly blockStore;
|
|
758
1366
|
private readonly effortStore;
|
|
759
1367
|
private readonly staticNoteStore?;
|
|
760
|
-
private readonly
|
|
761
|
-
|
|
1368
|
+
private readonly pageStore?;
|
|
1369
|
+
private readonly relationStore?;
|
|
1370
|
+
private readonly datasetStore?;
|
|
1371
|
+
private blockEffortsStore;
|
|
1372
|
+
private tagsStore;
|
|
1373
|
+
private noteTagsStore;
|
|
1374
|
+
constructor(storesOrEventStore?: QueryServiceStores | EventStore, noteStore?: NoteQueryStore, blockStore?: BlockQueryStore, effortStore?: EffortQueryStore, staticNoteStore?: NoteQueryStore);
|
|
762
1375
|
/** Windowed flat-fact fetch — the projected view over the event store. */
|
|
763
1376
|
getFactsByTimeRange(start: number, end: number): Promise<AnalyticsDataPoint[]>;
|
|
764
1377
|
runQuery(raw: string, options?: QueryOptions): Promise<QueryResult>;
|
|
765
1378
|
/**
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
* hits the promoted column; content-plane targets scope by content.
|
|
1379
|
+
* Legacy rows-family executor kept for hand-built ASTs (RowsTable, CLI).
|
|
1380
|
+
* Text `rows:…` no longer parses (#1044); this delegates to the find paths
|
|
1381
|
+
* so a programmatic ParsedRowsQuery still resolves.
|
|
770
1382
|
*/
|
|
771
1383
|
runRows(parsed: ParsedRowsQuery, options?: {
|
|
772
1384
|
anchorNow?: number;
|
|
1385
|
+
context?: ExecutionContext;
|
|
773
1386
|
}): Promise<RowsQueryResult>;
|
|
774
1387
|
/**
|
|
775
|
-
* Execute a
|
|
1388
|
+
* Execute a :session query (#1041/#1042) — grouped run cards per completed session.
|
|
1389
|
+
*/
|
|
1390
|
+
runFindSession(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
|
|
1391
|
+
/**
|
|
1392
|
+
* Execute a :segment or :event query (#1042/#1048) — flat cross-workout table.
|
|
1393
|
+
*/
|
|
1394
|
+
runFindTable(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
|
|
1395
|
+
/**
|
|
1396
|
+
* Execute a content-discovery query (:note). Naive in-memory filtering
|
|
776
1397
|
* per the tracer-bullet scope (#797): load all notes, then apply tag/text/
|
|
777
1398
|
* time filters.
|
|
778
1399
|
*/
|
|
779
1400
|
runFind(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
|
|
1401
|
+
/** Highest container per note id: the parent Page from the canonical
|
|
1402
|
+
* page_notes junction (first link by position) wins; a note with no page
|
|
1403
|
+
* link stays a standalone Note container. Batched when a relationStore
|
|
1404
|
+
* is injected — one bundle read per run instead of a link/page read per
|
|
1405
|
+
* matched note. */
|
|
1406
|
+
private containersForNotes;
|
|
780
1407
|
/**
|
|
781
|
-
* Execute a
|
|
1408
|
+
* Execute a :block query against the derived block_index store.
|
|
782
1409
|
*/
|
|
783
1410
|
runFindBlock(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
|
|
784
1411
|
/**
|
|
785
|
-
* Execute a
|
|
1412
|
+
* Execute a :effort query against the effort registry.
|
|
786
1413
|
*/
|
|
787
1414
|
runFindEffort(parsed: ParsedFindQuery): Promise<FindQueryResult>;
|
|
788
|
-
run(parsed:
|
|
1415
|
+
run(parsed: AnyParsedQuery, options?: QueryOptions): Promise<QueryResult>;
|
|
1416
|
+
/**
|
|
1417
|
+
* In-memory aggregation over ALREADY-FETCHED event rows — the pipeline
|
|
1418
|
+
* dataset/transform path. Identical metric projection, selection,
|
|
1419
|
+
* bucketing, unit, and aggregation stages as `run`; no store reads (a
|
|
1420
|
+
* content join on carried data is REJECTED by runAggregateEvents — it
|
|
1421
|
+
* would abandon the carried population).
|
|
1422
|
+
*/
|
|
1423
|
+
runAggregateEvents(query: ParsedAggregateQuery, events: readonly EventRecord[], options?: QueryOptions): Promise<QueryResult>;
|
|
1424
|
+
/** Public pipeline executor — parse the text, then run. */
|
|
1425
|
+
runPipeline(queryText: string, options?: PipelineOptions): Promise<PipelineResult>;
|
|
1426
|
+
/** Execute an already-parsed pipeline: resolve the source once, then flow
|
|
1427
|
+
* the carried value left to right through the transforms — each function
|
|
1428
|
+
* reduces the PRECEDING stage, never an independent re-query. Dataset
|
|
1429
|
+
* stages look up `pageSources` (then an injected datasetStore)
|
|
1430
|
+
* synchronously; no database roundtrip. */
|
|
1431
|
+
runPipelineParsed(parsed: AnyParsedQuery, options?: PipelineOptions): Promise<PipelineResult>;
|
|
1432
|
+
/** Events behind a content source — resolved ONLY when a transform needs
|
|
1433
|
+
* them: session/table sources already carry their rows; note sources read
|
|
1434
|
+
* the per-note index; block sources read the content index. */
|
|
1435
|
+
private sourceEventsFromFind;
|
|
1436
|
+
/** QueryExecutor adapter: a pipeline run's final stage rendered as the
|
|
1437
|
+
* aggregate QueryResult shape chart consumers already read. */
|
|
1438
|
+
private pipelineResultToQueryResult;
|
|
1439
|
+
/** Stages 2–4 over raw event rows: project → metric filter → range filter
|
|
1440
|
+
* → tag filters → coverage selection → BUCKET/GROUP/AGGREGATE. */
|
|
1441
|
+
private aggregateEventRows;
|
|
789
1442
|
/**
|
|
790
1443
|
* Stages 2–4 — BUCKET → GROUP → AGGREGATE over an already-selected +
|
|
791
1444
|
* filtered `matched` set.
|
|
@@ -795,30 +1448,388 @@ declare class QueryService {
|
|
|
795
1448
|
* blockContentIds owned by the find predicate's content matches. */
|
|
796
1449
|
private runJoined;
|
|
797
1450
|
/** Direction 1 — keep only content owning a wod block whose raw-log metric
|
|
798
|
-
* aggregate satisfies the predicate. `notes` for
|
|
799
|
-
*
|
|
1451
|
+
* aggregate satisfies the predicate. `notes` for :note, `blocks` for
|
|
1452
|
+
* :block. */
|
|
800
1453
|
private applyMetricJoin;
|
|
801
1454
|
/** Re-derive the metric from raw logs for each content id, aggregate per id,
|
|
802
1455
|
* and return the ids whose aggregate satisfies the join predicate. */
|
|
803
1456
|
private contentIdsSatisfying;
|
|
804
|
-
/** Filter per-effort vs un-attributed overall summary rows to avoid
|
|
805
|
-
* double-counting. */
|
|
806
|
-
private applyEffortScope;
|
|
807
1457
|
/** Summary facts for one Canonical Metric Key across the given content ids —
|
|
808
1458
|
* the cross-store join source. Reads finalize-written summary rows straight
|
|
809
1459
|
* off the content index (ticket 003: the store is authoritative; the old
|
|
810
1460
|
* freshness re-derivation is moot). One fact row per result × rowKey. */
|
|
811
1461
|
private deriveMetricFacts;
|
|
812
|
-
/** All content blocks
|
|
1462
|
+
/** All content blocks — journal and seeded corpus in one store. */
|
|
813
1463
|
private allContentBlocks;
|
|
814
1464
|
/** Map each note id to the wod blockContentIds it owns. */
|
|
815
1465
|
private noteContentMap;
|
|
816
1466
|
/** Collect wod blockContentIds owned by a find query's content matches. */
|
|
817
1467
|
private contentIdsFromFindResult;
|
|
818
|
-
/** Load note tag labels only when the query touches 'tags'.
|
|
1468
|
+
/** Load note tag labels only when the query touches 'tags'. Batched when
|
|
1469
|
+
* a relationStore is injected — one bundle read per run instead of a
|
|
1470
|
+
* junction read per row's note. */
|
|
819
1471
|
private loadNoteTags;
|
|
1472
|
+
/** Resolve matching note IDs for an exercise slug via the block_efforts containment index. */
|
|
1473
|
+
private getNoteIdsForEffort;
|
|
1474
|
+
/** Resolve matching note IDs for a canonical typed tag (e.g. equipment:kettlebell). */
|
|
1475
|
+
private getNoteIdsForTypedTag;
|
|
1476
|
+
/**
|
|
1477
|
+
* Find catalogs: identifies catalogs whose member notes (or catalog landing
|
|
1478
|
+
* notes) match the authored criteria (effort, tags, text, typed tags, etc.).
|
|
1479
|
+
*/
|
|
1480
|
+
private runFindCatalog;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
/**
|
|
1484
|
+
* Document runner + formula evaluation (wayfinder datadog-analytics
|
|
1485
|
+
* ticket 17).
|
|
1486
|
+
*
|
|
1487
|
+
* - `FormulaEvaluator` is an INJECTED capability: the engine/app composition
|
|
1488
|
+
* adapts lang's calc Pratt evaluator behind it. `wql` ships a small
|
|
1489
|
+
* built-in pointwise arithmetic evaluator so documents evaluate without
|
|
1490
|
+
* the injection (add/sub/mul/div, corr) — never a dependency on lang.
|
|
1491
|
+
* - Post-aggregate formulas evaluate pointwise over the ALIGNMENT DOMAIN:
|
|
1492
|
+
* the intersection of input requested ranges and the union of observed
|
|
1493
|
+
* group tuples. Missing operands substitute zero, flagged as
|
|
1494
|
+
* missing-derived; missing-derived zeros are excluded from downstream
|
|
1495
|
+
* ordinary averages; division by zero propagates an error.
|
|
1496
|
+
* - `corr(a, b)` is paired-only over in-range positions; the pair count is
|
|
1497
|
+
* reported.
|
|
1498
|
+
*/
|
|
1499
|
+
|
|
1500
|
+
/** Presence provenance of one operand value at one aligned position. */
|
|
1501
|
+
interface OperandValue {
|
|
1502
|
+
value: number;
|
|
1503
|
+
/** True when the value was substituted for a missing input. */
|
|
1504
|
+
missingDerived: boolean;
|
|
1505
|
+
}
|
|
1506
|
+
type PointwiseInputs = Record<string, (position: number) => OperandValue | undefined>;
|
|
1507
|
+
interface FormulaEvaluation {
|
|
1508
|
+
value: number;
|
|
1509
|
+
missingDerived: boolean;
|
|
1510
|
+
error?: string;
|
|
1511
|
+
}
|
|
1512
|
+
interface FormulaEvaluator {
|
|
1513
|
+
/**
|
|
1514
|
+
* Evaluate `expression` at one aligned position. `inputs` maps referenced
|
|
1515
|
+
* assignment names to position accessors; a missing operand is `undefined`
|
|
1516
|
+
* (the evaluator substitutes zero and flags the result).
|
|
1517
|
+
*/
|
|
1518
|
+
evaluate(expression: string, inputs: PointwiseInputs, position: number): FormulaEvaluation;
|
|
1519
|
+
}
|
|
1520
|
+
/** Built-in pointwise arithmetic evaluator (+ - * /, unary minus, corr-free). */
|
|
1521
|
+
declare function createBuiltinFormulaEvaluator(): FormulaEvaluator;
|
|
1522
|
+
/** Append document-default window/groupBy as textual suffixes before family
|
|
1523
|
+
* dispatch — defaults apply to find and pipeline documents too (a bare
|
|
1524
|
+
* `last 4w` block default must bound a `:journal{…}` content run). */
|
|
1525
|
+
declare function appendSuffixes(queryText: string, window: QueryWindow, groupBy?: string[]): string;
|
|
1526
|
+
/** One ordered `show` output — widget-independent (ticket 19 consumes). */
|
|
1527
|
+
interface DocumentOutput {
|
|
1528
|
+
name: string;
|
|
1529
|
+
kind: 'aggregate' | 'formula' | 'find' | 'pipeline';
|
|
1530
|
+
/** Ticket 19 — TabularResult for cross-workout rows outputs. */
|
|
1531
|
+
table?: TabularResult;
|
|
1532
|
+
unit?: string;
|
|
1533
|
+
groupBy?: string[];
|
|
1534
|
+
window?: QueryWindow;
|
|
1535
|
+
/** Series for aggregate and formula outputs. */
|
|
1536
|
+
series?: Array<{
|
|
1537
|
+
key: string;
|
|
1538
|
+
label: string;
|
|
1539
|
+
points: Array<{
|
|
1540
|
+
ts: number;
|
|
1541
|
+
value: number;
|
|
1542
|
+
missing?: boolean;
|
|
1543
|
+
missingDerived?: boolean;
|
|
1544
|
+
}>;
|
|
1545
|
+
unit?: string;
|
|
1546
|
+
}>;
|
|
1547
|
+
/** Formula provenance: pair counts for corr, coverage notes. */
|
|
1548
|
+
pairs?: number;
|
|
1549
|
+
/** Content-plane payloads (find / find-source pipeline outputs). */
|
|
1550
|
+
notes?: Note[];
|
|
1551
|
+
blocks?: BlockIndexRow[];
|
|
1552
|
+
efforts?: unknown[];
|
|
1553
|
+
/** Highest-container page/note links keyed by note id. */
|
|
1554
|
+
containers?: Record<string, NoteContainer>;
|
|
1555
|
+
/** Session run cards (find:session / session-source pipelines). */
|
|
1556
|
+
runs?: RowsRun[];
|
|
1557
|
+
/** Terminal chart sink of a pipeline output (renderer dispatch). */
|
|
1558
|
+
chart?: PipelineSink;
|
|
1559
|
+
error?: string;
|
|
1560
|
+
}
|
|
1561
|
+
interface DocumentResult {
|
|
1562
|
+
outputs: DocumentOutput[];
|
|
1563
|
+
diagnostics: string[];
|
|
1564
|
+
}
|
|
1565
|
+
/** Payload surfaces the ticket-19 family dispatch may hand back. */
|
|
1566
|
+
interface FamilyRunPayload {
|
|
1567
|
+
series?: DocumentOutput['series'];
|
|
1568
|
+
table?: TabularResult;
|
|
1569
|
+
notes?: Note[];
|
|
1570
|
+
blocks?: BlockIndexRow[];
|
|
1571
|
+
efforts?: unknown[];
|
|
1572
|
+
containers?: Record<string, NoteContainer>;
|
|
1573
|
+
runs?: RowsRun[];
|
|
1574
|
+
unit?: string;
|
|
1575
|
+
chart?: PipelineSink;
|
|
1576
|
+
error?: string;
|
|
1577
|
+
}
|
|
1578
|
+
interface QueryDocumentRunnerOptions {
|
|
1579
|
+
/** Injected formula evaluator (lang's calc evaluator via the engine). */
|
|
1580
|
+
formulaEvaluator?: FormulaEvaluator;
|
|
1581
|
+
/** Host default window — used only when the document has none. */
|
|
1582
|
+
rangeStart?: number;
|
|
1583
|
+
rangeEnd?: number;
|
|
1584
|
+
/** Ticket 19 family dispatch: find queries surface FindQueryResult. */
|
|
1585
|
+
runFind?: (queryText: string) => Promise<FamilyRunPayload>;
|
|
1586
|
+
/** Pipeline queries (`:source | :fn | :chart`, `@dataset | …`) surface
|
|
1587
|
+
* the pipeline result — the host's QueryService.runPipeline. */
|
|
1588
|
+
runPipeline?: (queryText: string) => Promise<FamilyRunPayload>;
|
|
1589
|
+
}
|
|
1590
|
+
declare class QueryDocumentRunner$1 {
|
|
1591
|
+
private readonly runAggregate;
|
|
1592
|
+
private readonly evaluator;
|
|
1593
|
+
constructor(runAggregate: (queryText: string, overrides: {
|
|
1594
|
+
groupBy?: string[];
|
|
1595
|
+
window?: QueryWindow;
|
|
1596
|
+
}) => Promise<{
|
|
1597
|
+
series?: DocumentOutput['series'];
|
|
1598
|
+
unit?: string;
|
|
1599
|
+
error?: string;
|
|
1600
|
+
window?: QueryWindow;
|
|
1601
|
+
}>, options?: QueryDocumentRunnerOptions);
|
|
1602
|
+
private readonly options;
|
|
1603
|
+
/** Parse → validate → evaluate in reference order → ordered outputs. */
|
|
1604
|
+
run(text: string): Promise<DocumentResult>;
|
|
1605
|
+
/** Defaults precedence (ticket 17): explicit per-query range/grouping >
|
|
1606
|
+
* block defaults > host. Explicit per-query by{} fully replaces block
|
|
1607
|
+
* grouping — never merges. */
|
|
1608
|
+
private mergeDefaults;
|
|
820
1609
|
}
|
|
821
1610
|
|
|
1611
|
+
/**
|
|
1612
|
+
* QueryDocumentRunner — the ONE execution path for query documents
|
|
1613
|
+
* (wayfinder datadog-analytics ticket 19, per the shared query execution
|
|
1614
|
+
* contract). Dashboard surfaces and embedded note query documents evaluate
|
|
1615
|
+
* through this runner via a host adapter; no surface runs queries itself.
|
|
1616
|
+
*
|
|
1617
|
+
* Per document run:
|
|
1618
|
+
* 1. token substitution (`$name` → active parameter values, raw text);
|
|
1619
|
+
* 2. parse as a QueryDocument (degenerate single query = legacy body);
|
|
1620
|
+
* 3. AST rollup inspection — `calc.*`-class targets (including pipeline
|
|
1621
|
+
* transform stages) trigger the host's rollup-ensure ONCE per run
|
|
1622
|
+
* (never string matching over query text);
|
|
1623
|
+
* 4. one captured execution context threads every window resolution;
|
|
1624
|
+
* 5. family dispatch: aggregate → aggregate run, find → runFind,
|
|
1625
|
+
* rows → runRows, pipeline → runPipeline.
|
|
1626
|
+
*
|
|
1627
|
+
* Zero browser dependencies — stores, catalog, and evaluator are injected.
|
|
1628
|
+
* Hosts that lack a family capability surface a diagnostic on the output —
|
|
1629
|
+
* never a silent empty result.
|
|
1630
|
+
*/
|
|
1631
|
+
|
|
1632
|
+
/** Host-populated page source dataset: named standard sources (`@session`,
|
|
1633
|
+
* `@today`, user names include `@`) resolved in memory before queries run. */
|
|
1634
|
+
interface PageSourceEntry {
|
|
1635
|
+
events: EventRecord[];
|
|
1636
|
+
notes: Note[];
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* PageSourceRegistry — the host's named page-source store (ticket 5).
|
|
1640
|
+
* Constructed/populated ONCE per page load / run context, BEFORE child
|
|
1641
|
+
* queries evaluate. Dataset pipeline stages (`@today | :sum{…}`) read it
|
|
1642
|
+
* synchronously through `QueryServiceStores.datasetStore` — no database
|
|
1643
|
+
* roundtrip downstream.
|
|
1644
|
+
*/
|
|
1645
|
+
declare class PageSourceRegistry extends Map<string, PageSourceEntry> {
|
|
1646
|
+
/** QueryService datasetStore seam alias — names include the `@`. */
|
|
1647
|
+
getDataset(name: string): PageSourceEntry | undefined;
|
|
1648
|
+
}
|
|
1649
|
+
interface QueryDocumentRunnerHost {
|
|
1650
|
+
runAggregate(queryText: string, overrides: {
|
|
1651
|
+
groupBy?: string[];
|
|
1652
|
+
window?: QueryWindow;
|
|
1653
|
+
context?: ExecutionContext;
|
|
1654
|
+
}): Promise<{
|
|
1655
|
+
series?: DocumentOutput['series'];
|
|
1656
|
+
unit?: string;
|
|
1657
|
+
error?: string;
|
|
1658
|
+
}>;
|
|
1659
|
+
runFind?(queryText: string, context?: ExecutionContext): Promise<{
|
|
1660
|
+
kind: 'find';
|
|
1661
|
+
notes?: Note[];
|
|
1662
|
+
blocks?: BlockIndexRow[];
|
|
1663
|
+
efforts?: unknown[];
|
|
1664
|
+
containers?: Record<string, NoteContainer>;
|
|
1665
|
+
runs?: RowsRun[];
|
|
1666
|
+
table?: TabularResult;
|
|
1667
|
+
unit?: string;
|
|
1668
|
+
error?: string;
|
|
1669
|
+
}>;
|
|
1670
|
+
/** Pipeline execution (`:source | :fn | :chart`, `@dataset | …`) — the
|
|
1671
|
+
* host's QueryService.runPipeline. */
|
|
1672
|
+
runPipeline?(queryText: string, context?: ExecutionContext): Promise<FamilyRunPayload>;
|
|
1673
|
+
/** Injected formula evaluator override (engine/app composition adapts
|
|
1674
|
+
* lang's calc evaluator); the built-in pointwise evaluator otherwise. */
|
|
1675
|
+
formulaEvaluator?: FormulaEvaluator;
|
|
1676
|
+
/** Host rollup driver: recompute-on-open for `calc.*` facts. Must skip
|
|
1677
|
+
* already-up-to-date prerequisites (no ensure→refresh loop). */
|
|
1678
|
+
rollupEnsure?: () => Promise<void>;
|
|
1679
|
+
}
|
|
1680
|
+
interface SharedRunnerOptions {
|
|
1681
|
+
formulaEvaluator?: FormulaEvaluator;
|
|
1682
|
+
/** One captured execution context per document run (ticket 12). */
|
|
1683
|
+
context?: ExecutionContext;
|
|
1684
|
+
/** Host default range — applies only when the document has no window. */
|
|
1685
|
+
rangeStart?: number;
|
|
1686
|
+
rangeEnd?: number;
|
|
1687
|
+
/** Active token values (frontmatter controls, dashboard controls). */
|
|
1688
|
+
tokens?: Record<string, string>;
|
|
1689
|
+
}
|
|
1690
|
+
declare class QueryDocumentRunner {
|
|
1691
|
+
private readonly host;
|
|
1692
|
+
private readonly initialOptions;
|
|
1693
|
+
constructor(host: QueryDocumentRunnerHost, options?: SharedRunnerOptions);
|
|
1694
|
+
/**
|
|
1695
|
+
* Run one document. `runOptions` override construction-time captures
|
|
1696
|
+
* (tokens, context) — one capture per run. Each run gets its OWN
|
|
1697
|
+
* evaluation snapshot: overlapping runs (one awaiting rollupEnsure while
|
|
1698
|
+
* another executes) can never borrow each other's context.
|
|
1699
|
+
*/
|
|
1700
|
+
run(text: string, runOptions?: SharedRunnerOptions): Promise<DocumentResult>;
|
|
1701
|
+
}
|
|
1702
|
+
/** Substitute `$name` tokens with active parameter values (raw text). */
|
|
1703
|
+
declare function substituteTokens$1(text: string, tokens: Record<string, string>): string;
|
|
1704
|
+
/**
|
|
1705
|
+
* AST rollup inspection (replaces `.includes('calc.')` string sniffing):
|
|
1706
|
+
* true when the parsed head's metric — a pipeline's source aggregate metric
|
|
1707
|
+
* or any transform stage's — is a calc target whose derivation depends on
|
|
1708
|
+
* workload rollup facts.
|
|
1709
|
+
*/
|
|
1710
|
+
declare function consumesRollupFacts(parsed: AnyParsedQuery | undefined): boolean;
|
|
1711
|
+
|
|
1712
|
+
/**
|
|
1713
|
+
* Workout derivation helpers for WQL QueryService and cross-store joins.
|
|
1714
|
+
*/
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Map a summary projection name to its Canonical Metric Key — the one key two
|
|
1718
|
+
* workouts must share for an aggregate to be compared across them. Same key
|
|
1719
|
+
* in fact rows and display. 'Total Volume' → 'totalVolume', 'TIS' → 'tis'.
|
|
1720
|
+
*/
|
|
1721
|
+
declare function resolveCanonicalMetricKey(projectionName: string): string;
|
|
1722
|
+
/**
|
|
1723
|
+
* Identity of the block a result belongs to, stamped on every fact row.
|
|
1724
|
+
*/
|
|
1725
|
+
interface SummaryFactIdentity {
|
|
1726
|
+
noteId: string;
|
|
1727
|
+
resultId: string;
|
|
1728
|
+
/** FK to NoteSegment.id (positional section id of the block run). */
|
|
1729
|
+
segmentId?: string;
|
|
1730
|
+
/** NoteSegment.version at record time. */
|
|
1731
|
+
segmentVersion?: number;
|
|
1732
|
+
/** Content-stable cross-note join key. */
|
|
1733
|
+
blockContentId?: string;
|
|
1734
|
+
/** Which surface produced the result; trend queries exclude 'playground' by default. */
|
|
1735
|
+
origin?: ResultOrigin;
|
|
1736
|
+
/** FK to the `page` store (copied from the parent note). */
|
|
1737
|
+
pageId?: string;
|
|
1738
|
+
/**
|
|
1739
|
+
* Canonical workout time — WorkoutResult.createdAt (true workout end).
|
|
1740
|
+
* Every fact row carries it as `timestamp` so time-range queries mean
|
|
1741
|
+
* "when the workout happened", never "when the metric was derived".
|
|
1742
|
+
*/
|
|
1743
|
+
workoutTimestamp?: number;
|
|
1744
|
+
}
|
|
1745
|
+
/** Logs shape read by normalizeSummaryFacts (StoredOutputStatement-compatible). */
|
|
1746
|
+
interface SummaryFactSourceOutput {
|
|
1747
|
+
outputType?: string;
|
|
1748
|
+
metrics: readonly {
|
|
1749
|
+
type?: string;
|
|
1750
|
+
value?: unknown;
|
|
1751
|
+
image?: string;
|
|
1752
|
+
unit?: string;
|
|
1753
|
+
/** Summary-processor payload (effortSlug / effortDiscipline / effortIntensityTier / …). */
|
|
1754
|
+
metadata?: Record<string, unknown>;
|
|
1755
|
+
}[];
|
|
1756
|
+
timeSpan?: {
|
|
1757
|
+
started?: number;
|
|
1758
|
+
ended?: number;
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Convert Tier-2 summary outputs (outputType 'analytics') in a result's logs
|
|
1763
|
+
* into persisted fact rows — one row per result × Canonical Metric Key.
|
|
1764
|
+
*/
|
|
1765
|
+
declare function normalizeSummaryFacts(logs: readonly SummaryFactSourceOutput[], identity: SummaryFactIdentity): AnalyticsDataPoint[];
|
|
1766
|
+
/** Identity for unified event-row emission — same fields as the fact identity. */
|
|
1767
|
+
type EventRowIdentity = SummaryFactIdentity;
|
|
1768
|
+
/**
|
|
1769
|
+
* Logs → event rows, 1:1 per statement (ticket 002). Deterministic ids
|
|
1770
|
+
* `${resultId}:${seq}`; query-critical scalars promoted top-level.
|
|
1771
|
+
*
|
|
1772
|
+
* Ticket 12 temporal anchoring: the row timestamp is the statement's OWN
|
|
1773
|
+
* timeSpan start when it has one — the workout-start timestamp never
|
|
1774
|
+
* relocates a metric observation whose own instant differs. Statements with
|
|
1775
|
+
* a timeSpan carry per-metric temporal anchors ('instant') so projection
|
|
1776
|
+
* groups each observation under its own date.
|
|
1777
|
+
*/
|
|
1778
|
+
declare function toEventRows(logs: readonly StoredOutputStatement[], identity: SummaryFactIdentity): EventRecord[];
|
|
1779
|
+
/**
|
|
1780
|
+
* Tier-2 analytics outputs → deterministic summary event rows (tickets
|
|
1781
|
+
* 002/004): id `${resultId}:summary:${rowKey}` — no timestamp salt, so
|
|
1782
|
+
* re-finalize overwrites cleanly. Fold identity lives in metrics[0].metadata
|
|
1783
|
+
* (canonicalKey, effort metadata, groupTags) — shape-uniform with events.
|
|
1784
|
+
*/
|
|
1785
|
+
declare function toSummaryEventRows(logs: readonly StoredOutputStatement[], identity: SummaryFactIdentity): EventRecord[];
|
|
1786
|
+
/**
|
|
1787
|
+
* Event row → flat fact currency for the four-stage plan (ticket 003):
|
|
1788
|
+
* one AnalyticsDataPoint per numeric metric. Canonical key resolution
|
|
1789
|
+
* mirrors the summary fold — metadata.canonicalKey first, name-derived
|
|
1790
|
+
* from the row's label metric as fallback. Deterministic fact ids:
|
|
1791
|
+
* `${record.id}:${factOrdinal}`.
|
|
1792
|
+
*/
|
|
1793
|
+
declare function projectEventToFacts(record: EventRecord): AnalyticsDataPoint[];
|
|
1794
|
+
/**
|
|
1795
|
+
* Inverse of {@link toEventRows}/{@link toSummaryEventRows}: rebuild the
|
|
1796
|
+
* statement stream a session's event rows represent. Event rows keep their
|
|
1797
|
+
* recorded order (id `${resultId}:${seq}`); summary rows follow in fold
|
|
1798
|
+
* order. Consumers that need the statement shape (replay, review display)
|
|
1799
|
+
* read events and call this — the store keeps only event rows.
|
|
1800
|
+
*/
|
|
1801
|
+
declare function eventsToStoredLogs(events: readonly EventRecord[]): StoredOutputStatement[];
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* Landing/catalog projection for a block row, derived from its sourceId route
|
|
1805
|
+
* (`collection:<catalog>/<item>`, `page:collection:<catalog>`,
|
|
1806
|
+
* `feed:feeds/<catalog>/…`) — never from `noteId`, which is a UUID for
|
|
1807
|
+
* imported notes. Legacy path-keyed rows keep deriving from the noteId stem
|
|
1808
|
+
* through `catalogOfItem` (undefined for UUID or no path); a bare
|
|
1809
|
+
* `collection:<catalog>` route is the legacy landing and gets its canonical
|
|
1810
|
+
* `page:collection:` sourceId.
|
|
1811
|
+
*/
|
|
1812
|
+
declare function staticCatalogRoute(block: Pick<BlockIndexRow, 'noteId' | 'sourceId'>): {
|
|
1813
|
+
landing: boolean;
|
|
1814
|
+
catalog?: string;
|
|
1815
|
+
sourceId?: string;
|
|
1816
|
+
};
|
|
1817
|
+
/**
|
|
1818
|
+
* Pure projection of a block_index into static Notes — one Note per distinct
|
|
1819
|
+
* noteId. `id` stays the canonical storage identity (UUID for imported rows);
|
|
1820
|
+
* `catalog` and feed-landing status come from the sourceId route, not
|
|
1821
|
+
* the noteId path. The catalog (e.g. `crossfit-girls`) is what the Library's
|
|
1822
|
+
* panel uses to target the `+ Filter → Catalog` menu.
|
|
1823
|
+
*/
|
|
1824
|
+
declare function staticNotesFromBlocks(blocks: BlockIndexRow[]): Note[];
|
|
1825
|
+
/**
|
|
1826
|
+
* Tag → noteIds index over a block index's frontmatter rows. Static-corpus
|
|
1827
|
+
* notes declare tags only in frontmatter `tags:`; this recovers the mapping
|
|
1828
|
+
* the note store needs to answer `tags:` clauses (issue #853 — the store
|
|
1829
|
+
* previously returned an empty set for every tag).
|
|
1830
|
+
*/
|
|
1831
|
+
declare function staticTagIndexFromBlocks(blocks: BlockIndexRow[]): Map<string, Set<string>>;
|
|
1832
|
+
|
|
822
1833
|
/**
|
|
823
1834
|
* Dashboard Note model (#898 map, format locked in #899).
|
|
824
1835
|
*
|
|
@@ -827,12 +1838,13 @@ declare class QueryService {
|
|
|
827
1838
|
* ```query[:<type>][-<N>|-full] blocks whose title/question associate from the
|
|
828
1839
|
* markdown heading/paragraph directly above each block. Queries reference
|
|
829
1840
|
* tokens as `$name`, substituted as raw text at execution time. A block body
|
|
830
|
-
* is
|
|
1841
|
+
* body is a Query Document (decision 22); presentation params ride the
|
|
1842
|
+
* fence tag as `key=value` attributes.
|
|
831
1843
|
*
|
|
832
1844
|
* This module is pure — no React, no editor, no query service — so the editor
|
|
833
1845
|
* extensions, the inline block renderer, and the dashboard route all share it.
|
|
834
1846
|
*/
|
|
835
|
-
declare const DASHBOARD_WIDGET_TYPES: readonly ["table", "value", "timeseries", "bar", "toplist", "stacked-bar", "goal-rings", "zone-distribution"];
|
|
1847
|
+
declare const DASHBOARD_WIDGET_TYPES: readonly ["table", "list", "value", "timeseries", "bar", "donut", "toplist", "stacked-bar", "goal-rings", "zone-distribution"];
|
|
836
1848
|
type DashboardWidgetType = (typeof DASHBOARD_WIDGET_TYPES)[number];
|
|
837
1849
|
declare function isDashboardWidgetType(type: string): type is DashboardWidgetType;
|
|
838
1850
|
/** The widget type a block renders as: a bare ```query fence defaults to `table`. */
|
|
@@ -856,6 +1868,11 @@ interface QueryWidgetSuffix {
|
|
|
856
1868
|
spanCols?: number;
|
|
857
1869
|
/** Full-row flag (`-full`). Mutually exclusive with spanCols. */
|
|
858
1870
|
spanFull?: boolean;
|
|
1871
|
+
/** Presentation attributes: `key=value` tokens after the type/span
|
|
1872
|
+
* (decision 22 — positional params ride the fence tag, not the body). */
|
|
1873
|
+
attributes: Record<string, string>;
|
|
1874
|
+
/** Attribute order for stable re-serialization. */
|
|
1875
|
+
attributeOrder: string[];
|
|
859
1876
|
/** Set when the suffix is malformed (the type is still reported raw). */
|
|
860
1877
|
error?: string;
|
|
861
1878
|
}
|
|
@@ -899,16 +1916,6 @@ declare function substituteTokens(query: string, values: Record<string, string>)
|
|
|
899
1916
|
};
|
|
900
1917
|
/** Token names a query references, in order of first appearance. */
|
|
901
1918
|
declare function referencedTokens(query: string): string[];
|
|
902
|
-
/**
|
|
903
|
-
* Split a block body into the WQL query and its trailing `/`-separated
|
|
904
|
-
* positional parameters (decision #899-7). Splits at the FIRST ` / ` so
|
|
905
|
-
* params stay trailing; a literal ` / ` inside a tag value is pathological
|
|
906
|
-
* and unsupported. Params are whitespace-split literals or `$token` refs.
|
|
907
|
-
*/
|
|
908
|
-
declare function splitWidgetBody(body: string): {
|
|
909
|
-
query: string;
|
|
910
|
-
params: string[];
|
|
911
|
-
};
|
|
912
1919
|
/** Structural input for the builder — satisfied by EditorSection + doc text. */
|
|
913
1920
|
interface DashboardSectionInput {
|
|
914
1921
|
/** Section type ('query', 'markdown', 'frontmatter', …). */
|
|
@@ -925,6 +1932,9 @@ interface DashboardSectionInput {
|
|
|
925
1932
|
spanFull?: boolean;
|
|
926
1933
|
/** Malformed fence-suffix reason — carried so route mode badges it too. */
|
|
927
1934
|
widgetError?: string;
|
|
1935
|
+
/** Fence-tag presentation attributes (decision 22). */
|
|
1936
|
+
attributes?: Record<string, string>;
|
|
1937
|
+
attributeOrder?: string[];
|
|
928
1938
|
}
|
|
929
1939
|
interface DashboardWidget {
|
|
930
1940
|
/** Stable key within the document (section index). */
|
|
@@ -933,14 +1943,15 @@ interface DashboardWidget {
|
|
|
933
1943
|
type: string;
|
|
934
1944
|
spanCols?: number;
|
|
935
1945
|
spanFull?: boolean;
|
|
936
|
-
/**
|
|
1946
|
+
/** Full fenced body — a Query Document (decision 22), comment lines
|
|
1947
|
+
* stripped, surrounding whitespace trimmed, line structure preserved. */
|
|
937
1948
|
body: string;
|
|
938
1949
|
/** Malformed fence-suffix reason — renderers badge instead of executing. */
|
|
939
1950
|
widgetError?: string;
|
|
940
|
-
/**
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1951
|
+
/** Fence-tag presentation attributes (decision 22) — positional params
|
|
1952
|
+
* ride the tag, never the body. */
|
|
1953
|
+
attributes: Record<string, string>;
|
|
1954
|
+
attributeOrder: string[];
|
|
944
1955
|
/** Title from the markdown heading directly above (or above the question). */
|
|
945
1956
|
title?: string;
|
|
946
1957
|
/** Coaching question from the paragraph directly above the block. */
|
|
@@ -1035,17 +2046,109 @@ declare function parseFlatProperties(innerContent: string): Record<string, strin
|
|
|
1035
2046
|
*/
|
|
1036
2047
|
declare function parseFrontmatterProps(lines: string[]): Record<string, string>;
|
|
1037
2048
|
|
|
2049
|
+
/**
|
|
2050
|
+
* Dashboard note operations — pure markdown↔markdown transforms over the
|
|
2051
|
+
* locked dashboard-note format (#899). Every operation:
|
|
2052
|
+
*
|
|
2053
|
+
* - takes the note's raw content and returns the NEW raw content (the caller
|
|
2054
|
+
* persists through journalNotes, the only write path);
|
|
2055
|
+
* - is identity-guarded: the target widget is located by its positional key
|
|
2056
|
+
* (`w${index}`, matching buildDashboardDocument) AND its expected body
|
|
2057
|
+
* content. A guard mismatch returns null and writes NOTHING — a concurrent
|
|
2058
|
+
* edit or reorder between read and write can never retarget the splice to
|
|
2059
|
+
* the wrong widget.
|
|
2060
|
+
*
|
|
2061
|
+
* A widget's editable unit is its "group": the title heading / coaching
|
|
2062
|
+
* question paragraph the document builder associates with the block (strict
|
|
2063
|
+
* adjacency, same rule as buildDashboardDocument) plus the ```query fence
|
|
2064
|
+
* itself. Move/duplicate/remove operate on the whole group so the rendered
|
|
2065
|
+
* card — title, question, chart — travels together.
|
|
2066
|
+
*/
|
|
2067
|
+
interface WidgetSpec {
|
|
2068
|
+
/** Card title — renders as a `## ` heading above the block. */
|
|
2069
|
+
title?: string;
|
|
2070
|
+
/** Coaching question — the paragraph between the heading and the block. */
|
|
2071
|
+
question?: string;
|
|
2072
|
+
/** Raw widget type from the fence suffix ('' → the `table` default). */
|
|
2073
|
+
type: string;
|
|
2074
|
+
spanCols?: number;
|
|
2075
|
+
spanFull?: boolean;
|
|
2076
|
+
/** The composed Query Document text — stored verbatim as the block body
|
|
2077
|
+
* (single-line or multi-line; decision 22). */
|
|
2078
|
+
wql: string;
|
|
2079
|
+
/** Fence-tag presentation attributes (goal targets, …). */
|
|
2080
|
+
attributes?: Record<string, string>;
|
|
2081
|
+
/** Attribute emission order (defaults to Object key order). */
|
|
2082
|
+
attributeOrder?: string[];
|
|
2083
|
+
}
|
|
2084
|
+
type WidgetSpan = Pick<WidgetSpec, 'spanCols' | 'spanFull'>;
|
|
2085
|
+
/** Opening fence line for a widget spec — type/span plus `key=value`
|
|
2086
|
+
* attributes (decision 22: presentation params ride the fence tag). */
|
|
2087
|
+
declare function widgetFenceTag(spec: Pick<WidgetSpec, 'type' | 'spanCols' | 'spanFull' | 'attributes' | 'attributeOrder'>): string;
|
|
2088
|
+
/** Block body: the Query Document text verbatim (trimmed). */
|
|
2089
|
+
declare function widgetBodyLine(spec: Pick<WidgetSpec, 'wql'>): string;
|
|
2090
|
+
/** The markdown lines of a whole widget group (heading? / question? / fence block). */
|
|
2091
|
+
declare function renderWidgetGroup(spec: WidgetSpec): string[];
|
|
2092
|
+
/**
|
|
2093
|
+
* Append a new widget group at the end of the note body. Frontmatter and all
|
|
2094
|
+
* existing content are preserved verbatim.
|
|
2095
|
+
*/
|
|
2096
|
+
declare function appendWidget(raw: string, spec: WidgetSpec): string;
|
|
2097
|
+
/** Structured operation result (decision 22): a missing widget is distinct
|
|
2098
|
+
* from a concurrent edit, and neither silently no-ops. */
|
|
2099
|
+
type WidgetOpResult = {
|
|
2100
|
+
ok: true;
|
|
2101
|
+
note: string;
|
|
2102
|
+
} | {
|
|
2103
|
+
ok: false;
|
|
2104
|
+
reason: 'not-found' | 'stale-body';
|
|
2105
|
+
};
|
|
2106
|
+
/**
|
|
2107
|
+
* Replace a widget's whole group (title/question/type/body) in place.
|
|
2108
|
+
* Identity-guarded against the widget's exact full body text.
|
|
2109
|
+
*/
|
|
2110
|
+
declare function updateWidget(raw: string, key: string, expectedBody: string, spec: WidgetSpec): WidgetOpResult;
|
|
2111
|
+
/** Duplicate a widget's group directly below itself. Identity-guarded. */
|
|
2112
|
+
declare function duplicateWidget(raw: string, key: string, expectedBody: string): WidgetOpResult;
|
|
2113
|
+
/**
|
|
2114
|
+
* Remove a widget's group (title/question/fence). Identity-guarded. Collapses
|
|
2115
|
+
* the blank-line gap the removal leaves behind.
|
|
2116
|
+
*/
|
|
2117
|
+
declare function removeWidget(raw: string, key: string, expectedBody: string): WidgetOpResult;
|
|
2118
|
+
/**
|
|
2119
|
+
* Reorder a widget among the query blocks — the whole group (title, question,
|
|
2120
|
+
* block) moves past its neighbor. `delta` is -1 (up) or +1 (down); out-of-range
|
|
2121
|
+
* moves are no-ops that return the content unchanged. Identity-guarded.
|
|
2122
|
+
*/
|
|
2123
|
+
declare function moveWidget(raw: string, key: string, expectedBody: string, delta: -1 | 1): WidgetOpResult;
|
|
2124
|
+
/**
|
|
2125
|
+
* Resize (or re-span) a widget: rewrites only the opening fence line, leaving
|
|
2126
|
+
* the body, params, and surrounding markdown untouched. `spanFull` wins over
|
|
2127
|
+
* `spanCols`. Identity-guarded.
|
|
2128
|
+
*/
|
|
2129
|
+
declare function resizeWidget(raw: string, key: string, expectedBody: string, span: WidgetSpan): WidgetOpResult;
|
|
2130
|
+
/**
|
|
2131
|
+
* Rewrite one dashboard note's legacy widget bodies to Query Documents
|
|
2132
|
+
* (decision 22, answer 1): the first-` / ` positional split is retired.
|
|
2133
|
+
* For each query fence whose body carries ` / ` positional params, the
|
|
2134
|
+
* query text stays the body and the params move onto the fence tag as
|
|
2135
|
+
* numbered attributes (`param1=…`) — values preserved verbatim, losslessly
|
|
2136
|
+
* editable. A body already document-shaped is untouched; the note that
|
|
2137
|
+
* cannot be rewritten safely returns unchanged (the caller badges it).
|
|
2138
|
+
*/
|
|
2139
|
+
declare function migrateLegacyDashboardBodies(raw: string): string;
|
|
2140
|
+
|
|
1038
2141
|
/**
|
|
1039
2142
|
* Auto-generated build version metadata.
|
|
1040
2143
|
* Do not edit directly; updated by scripts/stamp-version.ts
|
|
1041
2144
|
*/
|
|
1042
|
-
declare const VERSION = "0.
|
|
1043
|
-
declare const GIT_SHA = "
|
|
1044
|
-
declare const BUILD_TIME = "2026-
|
|
2145
|
+
declare const VERSION = "0.35.2116";
|
|
2146
|
+
declare const GIT_SHA = "a4d411f";
|
|
2147
|
+
declare const BUILD_TIME = "2026-10-12T00:14:24.795Z";
|
|
1045
2148
|
declare const SEMVER: {
|
|
1046
|
-
readonly version: "0.
|
|
1047
|
-
readonly gitSha: "
|
|
1048
|
-
readonly buildTime: "2026-
|
|
2149
|
+
readonly version: "0.35.2116";
|
|
2150
|
+
readonly gitSha: "a4d411f";
|
|
2151
|
+
readonly buildTime: "2026-10-12T00:14:24.795Z";
|
|
1049
2152
|
};
|
|
1050
2153
|
|
|
1051
|
-
export { type Aggregator, type AnyParsedQuery, BUILD_TIME, type BlockQueryStore, type ComparisonOp, DASHBOARD_GRID_MAX_COLS, DASHBOARD_WIDGET_TYPES, DEFAULT_DASHBOARD_TITLE, DISCIPLINE_FACTORS, type DashboardDocument, type DashboardMeta, type DashboardParsedSection, type DashboardSectionInput, type DashboardToken, type DashboardWidget, type DashboardWidgetType, EFFORT_DISCIPLINES, type EffortDiscipline, type EffortQueryStore, type EventRowIdentity, type FindOptions, type FindPredicate, type FindQueryResult, GIT_SHA, type IEffort,
|
|
2154
|
+
export { type Aggregator, type AnyParsedQuery, BUILD_TIME, type BlockQueryStore, CONTENT_GROUPING_DIMENSIONS, type CatalogFieldSuggestion, type CatalogValueSuggestion, type ComparisonOp, DASHBOARD_GRID_MAX_COLS, DASHBOARD_WIDGET_TYPES, DEFAULT_DASHBOARD_TITLE, DISCIPLINE_FACTORS, type DashboardDocument, type DashboardMeta, type DashboardParsedSection, type DashboardSectionInput, type DashboardToken, type DashboardWidget, type DashboardWidgetType, type DocumentAssignment, type DocumentDefaults, type DocumentFormula, type DocumentOutput, type DocumentResult, EFFORT_DISCIPLINES, type EffortDiscipline, type EffortQueryStore, type EventRowIdentity, type EventStore, type ExecutionContext, type FamilyRunPayload, type FindOptions, type FindPredicate, type FindQueryResult, type FormulaEvaluation, type FormulaEvaluator, GIT_SHA, type IEffort, type IFieldCatalog, type LinkWidget, type MetricPredicate, type NoteContainer, type NoteQueryStore, type NoteRelationBundle, type OperandValue, type OutputUnitResolution, PLANNED_WIDGET_TYPES, type PageSourceEntry, PageSourceRegistry, type ParsedAggregateQuery, type ParsedDocument, type ParsedFindQuery, type ParsedFrontmatter, type ParsedPipelineQuery, type ParsedRowsQuery, type ParsedWqlRollupSuffix, type ParsedWqlSuffixes, type ParsedWqlWindowSuffix, type PipelineOptions, type PipelineResult, type PipelineSink, type PipelineSource, type PointwiseInputs, type QueryDocument, QueryDocumentRunner$1 as QueryDocumentRunner, type QueryDocumentRunnerHost, type QueryDocumentRunnerOptions, type QueryOptions, type QueryResult, QueryService, type QueryServiceStores, type QueryWidgetSuffix, type QueryWindow, type ResolvedRange, type RowsPipes, type RowsQueryResult, type RowsRun, SEMVER, type Series, type SeriesPoint, QueryDocumentRunner as SharedQueryDocumentRunner, type SharedRunnerOptions, type SummaryFactIdentity, type SummaryFactSourceOutput, type TabularColumn, type TabularGroup, type TabularResult, type TagFilter, type TagValue, UNASSIGNED, type UnitBearingFact, type UnitFamily, VERSION, WQL_AGGREGATORS, WQL_CALC_TARGETS, WQL_CHART_HEADS, WQL_COMPARISON_OPS, WQL_CONTENT_FILTER_KEYS, WQL_CONTENT_ONLY_KEYS, WQL_DISPLAY_UNITS, WQL_EFFORT_FILTER_KEYS, WQL_FIND_TARGETS, WQL_FUNCTION_HEADS, WQL_GRAINS, WQL_INTENSITY_TIERS, WQL_KEYWORDS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_NOTE_DEFAULT_SOURCES, WQL_RESULT_PLANES, WQL_ROLLUP_PERIODS, WQL_SOURCES, WQL_SOURCE_HEADS, WQL_SOURCE_HEAD_SCOPES, WQL_SOURCE_VALUES, WQL_STANDARD_DATASETS, WQL_TAG_KEYS, WQL_TYPED_TAG_KEYS, WQL_TYPE_HEADS, WQL_VIRTUAL_DIMS, type WidgetOpResult, type WidgetSpan, type WidgetSpec, type WqlAggregator, type WqlCalcTarget, type WqlChartHead, type WqlComparisonOp, type WqlCompletionOptions, type WqlContentFilterKey, type WqlDisplayUnit, type WqlDomainOrder, type WqlDomainPredicate, type WqlDomainReadResult, type WqlFindTarget, type WqlFunctionHead, type WqlGrain, type WqlIntensityTier, type WqlKeyword, type WqlMetricAggregate, type WqlMetricFamily, type WqlResultPlane, type WqlRollupPeriod, type WqlSource, type WqlSourceHead, type WqlSourceValue, type WqlStandardDataset, type WqlTagKey, type WqlTypedTagKey, type WqlVirtualDim, allowedFilterTypesForTarget, appendSuffixes, appendWidget, buildDashboardDocument, buildDashboardScaffold, buildDrillDownQuery, captureContext, catalogOfItem, civilDateAdd, civilDateDiff, civilDateOf, civilDatesCoveredByRange, civilMonday, consumesRollupFacts, convertDisplay, createBuiltinFormulaEvaluator, dashboardSlug, defaultTokenValues, disciplineFactorFor, duplicateWidget, eventsToStoredLogs, extractDashboardTokens, extractFrontmatterTags, findTargetAdvisories, getList, getScalar, getUnitFamily, inRange, isAggregateQuery, isDashboardMeta, isDashboardWidgetType, isEffortDiscipline, isFindQuery, isPipelineQuery, isProposedMetric, migrateLegacyDashboardBodies, moveWidget, normalizeSummaryFacts, normalizeWql, parseDashboardNote, parseDocument, parseFlatProperties, parseFrontmatter, parseFrontmatterBody, parseFrontmatterCategories, parseFrontmatterProps, parseQuery, parseQueryWidgetSuffix, parseWqlSuffixes, projectEventToFacts, referencedTokens, removeWidget, renderWidgetGroup, resizeWidget, resolveCanonicalMetricKey, resolveOutputUnit, resolveWidgetType, resolveWindowRange, rowsParseError, serialize, serializeDocument, serializeFrontmatter, setDashboardTokenValue, sourceMatches, splitAtWhere, staticCatalogRoute, staticNotesFromBlocks, staticTagIndexFromBlocks, stripFrontmatter, substituteTokens, substituteTokens$1 as substituteTokensRaw, supportsWqlFilterKey, systemTimeZone, toEventRows, toSummaryEventRows, unknownTokensMessage, unknownWidgetTypeMessage, updateWidget, widgetBodyLine, widgetFenceTag, wql, wqlCompletion, wqlCompletionSource, wqlFilterKeys, wqlGroupingDimensions, wqlHighlightStyle, wqlLanguage, parser as wqlParser, wql_parser_terms as wqlTerms, zonedNoon, zonedStartOfDay };
|