@bitcobblers/wod-wiki-wql 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,896 @@
1
+ import { LRParser } from '@lezer/lr';
2
+ import { LanguageSupport, HighlightStyle, LRLanguage } from '@codemirror/language';
3
+ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
4
+ import { Extension } from '@codemirror/state';
5
+ import { ResultOrigin, AnalyticsDataPoint, BlockIndexRow, Note, WorkoutResult, StoredOutputStatement } from '@bitcobblers/wod-wiki-core';
6
+
7
+ /**
8
+ * WQL vocabulary — the single owner of the WQL dictionary: aggregators,
9
+ * comparison operators, metric families/aggregates, calc.* targets, tag
10
+ * keys, dimensions, rollup periods, grains, sources, scopes, display units,
11
+ * and structural keywords. Every surface that needs these words imports from
12
+ * here (or from language.ts, which re-exports this module for editor
13
+ * consumers) rather than hand-writing its own copy — issue #871.
14
+ *
15
+ * The calc registry's built-in calcs (@bitcobblers/wod-wiki-lang's calc seeds)
16
+ * publish exactly the `calc.*` targets listed in WQL_CALC_TARGETS; keep the
17
+ * two in sync. User-authored calcs extend this set at runtime.
18
+ */
19
+ /** Canonical Metric Key families (CONTEXT.md §Analytics). */
20
+ declare const WQL_METRIC_FAMILIES: readonly ["reps", "distance", "resistance", "elapsed", "power", "pace"];
21
+ type WqlMetricFamily = (typeof WQL_METRIC_FAMILIES)[number];
22
+ /** Tier-2 aggregate keys written to the Analytics Store. */
23
+ declare const WQL_METRIC_AGGREGATES: readonly ["totalVolume", "totalDistance", "tis", "sessionLoad"];
24
+ type WqlMetricAggregate = (typeof WQL_METRIC_AGGREGATES)[number];
25
+ /** Aggregate head operators for the analytics grammar (agg:metric{filters}). */
26
+ declare const WQL_AGGREGATORS: readonly ["sum", "avg", "min", "max", "count", "last", "delta"];
27
+ type WqlAggregator = (typeof WQL_AGGREGATORS)[number];
28
+ /** Comparison operators for cross-store `where` joins (#800). */
29
+ declare const WQL_COMPARISON_OPS: readonly [">", ">=", "<", "<=", "==", "!="];
30
+ type WqlComparisonOp = (typeof WQL_COMPARISON_OPS)[number];
31
+ /** 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"];
33
+ type WqlTagKey = (typeof WQL_TAG_KEYS)[number];
34
+ /** Virtual dimensions — time buckets and stream positions, not fact fields. */
35
+ declare const WQL_VIRTUAL_DIMS: readonly ["day", "week", "session", "round"];
36
+ type WqlVirtualDim = (typeof WQL_VIRTUAL_DIMS)[number];
37
+ /**
38
+ * Canonical `calc.*` targets. The single source of truth for the set of
39
+ * calculation metric keys: source for the composer/CM6 typeahead (#871) and
40
+ * the dashboard's known-vs-proposed gate (model.ts). Must
41
+ * match the keys the calc engine registers in
42
+ * @bitcobblers/wod-wiki-lang seeds (`calc.e1rm`, `calc.metMinutes`,
43
+ * `calc.acwr`, `calc.monotony`, `calc.strain`, `calc.ctl`, `calc.atl`,
44
+ * `calc.tsb`, etc.). NOTE: `calc.pmc` (composite {ctl, atl, tsb} series) is
45
+ * deliberately absent — the store calc model publishes one scalar key per
46
+ * definition, so PMC ships as the three loads (#905); a composite stays
47
+ * 'proposed' until a dedicated series widget lands.
48
+ */
49
+ declare const WQL_CALC_TARGETS: readonly ["calc.metMinutes", "calc.acwr", "calc.monotony", "calc.strain", "calc.e1rm", "calc.ctl", "calc.atl", "calc.tsb", "calc.soreness", "calc.sleep", "calc.hrv", "calc.readiness", "calc.mvcBw", "calc.ef", "calc.adherence", "calc.pct1rm", "calc.sends"];
50
+ type WqlCalcTarget = (typeof WQL_CALC_TARGETS)[number];
51
+ declare const WQL_INTENSITY_TIERS: readonly ["low", "moderate", "high"];
52
+ type WqlIntensityTier = (typeof WQL_INTENSITY_TIERS)[number];
53
+ declare const WQL_GRAINS: readonly ["segment", "summary", "rollup"];
54
+ type WqlGrain = (typeof WQL_GRAINS)[number];
55
+ /** Content-discovery query targets (find:<target>). */
56
+ declare const WQL_FIND_TARGETS: readonly ["note", "block", "effort"];
57
+ type WqlFindTarget = (typeof WQL_FIND_TARGETS)[number];
58
+ /** Content query scopes (in <scope>). */
59
+ declare const WQL_SCOPES: readonly ["journal", "collections", "feeds", "all"];
60
+ type WqlScope = (typeof WQL_SCOPES)[number];
61
+ /** Content-specific filter keys (beyond the analytics tag keys). */
62
+ declare const WQL_CONTENT_FILTER_KEYS: readonly ["type", "text", "has", "source", "catalog", "effort", "discipline", "intensity", "note", "page", "origin", "grain", "metric", "block", "result", "tags"];
63
+ type WqlContentFilterKey = (typeof WQL_CONTENT_FILTER_KEYS)[number];
64
+ /**
65
+ * Composer source planes (issue #838, decision #836): the single `source`
66
+ * head slot that pivots the query kind. Content sources compile the `find:`
67
+ * skeleton; `metrics` compiles the aggregate skeleton. `efforts` compiles
68
+ * `find:effort` against the effort registry (origin/intensity/discipline
69
+ * vocab from types.ts). The `results` plane is
70
+ * deliberately absent — it needs new grammar/engine semantics and is
71
+ * deferred to its own ticket.
72
+ */
73
+ declare const WQL_SOURCES: readonly ["journal", "collections", "feeds", "notes", "blocks", "efforts", "metrics"];
74
+ type WqlSource = (typeof WQL_SOURCES)[number];
75
+ /** Rollup periods the aggregate grammar accepts (wql.ts: unit d|w only). */
76
+ declare const WQL_ROLLUP_PERIODS: readonly ["1d", "1w"];
77
+ type WqlRollupPeriod = (typeof WQL_ROLLUP_PERIODS)[number];
78
+ /** Display units the app can render (analytics unit preference, kg/lb). The
79
+ * `in <unit>` directive accepts any word; these are the canonical options. */
80
+ declare const WQL_DISPLAY_UNITS: readonly ["kg", "lb"];
81
+ type WqlDisplayUnit = (typeof WQL_DISPLAY_UNITS)[number];
82
+ /** All WQL keywords */
83
+ declare const WQL_KEYWORDS: readonly ["find", "rows", "where", "group", "by", "order", "limit", "sum", "avg", "min", "max", "count", "last", "delta"];
84
+ type WqlKeyword = (typeof WQL_KEYWORDS)[number];
85
+
86
+ declare const parser: LRParser;
87
+
88
+ declare const Query = 1;
89
+ declare const Head = 2;
90
+ declare const Aggregator$1 = 3;
91
+ declare const Word = 4;
92
+ declare const Metric = 5;
93
+ declare const Filters = 6;
94
+ declare const Filter = 7;
95
+ declare const Negate = 8;
96
+ declare const TagKey = 9;
97
+ declare const TagValue$1 = 10;
98
+ declare const Value = 11;
99
+ declare const Star = 12;
100
+ declare const Quoted = 13;
101
+ declare const GroupBy = 14;
102
+ declare const By = 15;
103
+ declare const Dimension = 16;
104
+ declare const Rollup = 17;
105
+ declare const RollupDot = 18;
106
+ declare const Int = 19;
107
+
108
+ declare const wql_parser_terms_By: typeof By;
109
+ declare const wql_parser_terms_Dimension: typeof Dimension;
110
+ declare const wql_parser_terms_Filter: typeof Filter;
111
+ declare const wql_parser_terms_Filters: typeof Filters;
112
+ declare const wql_parser_terms_GroupBy: typeof GroupBy;
113
+ declare const wql_parser_terms_Head: typeof Head;
114
+ declare const wql_parser_terms_Int: typeof Int;
115
+ declare const wql_parser_terms_Metric: typeof Metric;
116
+ declare const wql_parser_terms_Negate: typeof Negate;
117
+ declare const wql_parser_terms_Query: typeof Query;
118
+ declare const wql_parser_terms_Quoted: typeof Quoted;
119
+ declare const wql_parser_terms_Rollup: typeof Rollup;
120
+ declare const wql_parser_terms_RollupDot: typeof RollupDot;
121
+ declare const wql_parser_terms_Star: typeof Star;
122
+ declare const wql_parser_terms_TagKey: typeof TagKey;
123
+ declare const wql_parser_terms_Value: typeof Value;
124
+ declare const wql_parser_terms_Word: typeof Word;
125
+ declare namespace wql_parser_terms {
126
+ 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 };
127
+ }
128
+
129
+ /**
130
+ * WqlSuffix — Unified WQL suffix parser and adapter (issue #870).
131
+ *
132
+ * Encapsulates suffix parsing order, regexes, depth-0 brace-aware splitting,
133
+ * and AST extraction for all WQL suffix clauses:
134
+ * - Outer join: `where <joinClause>`
135
+ * - Display unit: `in <unit>`
136
+ * - Rollup period: `.rollup(<size><unit>)`
137
+ * - Group-by: `by {<dims>}`
138
+ * - Time window: `last <n><unit>`
139
+ * - Scope: `in <scope>`
140
+ */
141
+ interface ParsedWqlRollupSuffix {
142
+ size: number;
143
+ unit: string;
144
+ raw: string;
145
+ }
146
+ interface ParsedWqlLastSuffix {
147
+ size: number;
148
+ unit: 'd' | 'w';
149
+ raw: string;
150
+ }
151
+ interface ParsedWqlSuffixes {
152
+ /** Outer cross-store join clause text (`where ...`). */
153
+ where?: string;
154
+ /** Display unit directive (`in kg`). */
155
+ displayUnit?: string;
156
+ /** Rollup period (`.rollup(1w)`). */
157
+ rollup?: ParsedWqlRollupSuffix;
158
+ /** Group-by dimensions (`by {week, effort}`). */
159
+ groupBy?: string[];
160
+ /** Content time window (`last 8w`). */
161
+ last?: ParsedWqlLastSuffix;
162
+ /** Content scope (`in journal` / `in all`). */
163
+ scope?: string;
164
+ /** Primary query text with suffixes removed. */
165
+ primaryText: string;
166
+ }
167
+ /**
168
+ * Split a query at the first top-level `where` keyword (depth-0, word-bounded).
169
+ * Returns the primary half and, when present, the join clause text.
170
+ */
171
+ declare function splitAtWhere(text: string): {
172
+ primary: string;
173
+ where?: string;
174
+ };
175
+ /**
176
+ * Extract suffixes from a raw WQL query string.
177
+ * Strips outer join (`where`), display unit (`in kg`), rollup (`.rollup(1w)`),
178
+ * group-by (`by {week}`), time window (`last 8w`), and scope (`in journal`).
179
+ */
180
+ declare function parseWqlSuffixes(raw: string): ParsedWqlSuffixes;
181
+
182
+ /**
183
+ * WQL — Wod Query Language (Datadog-flavored). See CONTEXT.md glossary.
184
+ *
185
+ * <aggregator>:<metric.namespace>{<tag filters>} by {<dimensions>} .rollup(<period>)
186
+ *
187
+ * sum:totalVolume{discipline:strength} by {week}.rollup(1w)
188
+ * avg:tis{effort:thruster,!discipline:recovery} by {session}
189
+ *
190
+ * This module holds the AST contract the Query Service executes and the
191
+ * Lezer-backed `parseQuery` front-end over grammar/wql.grammar (house
192
+ * pattern). The grammar accepts the full WQL surface with error recovery;
193
+ * this mapper validates the recovered tree and produces the AST below.
194
+ */
195
+
196
+ type Aggregator = WqlAggregator;
197
+ interface TagValue {
198
+ value: string;
199
+ wildcard: boolean;
200
+ }
201
+ interface TagFilter {
202
+ key: string;
203
+ negate: boolean;
204
+ values: TagValue[];
205
+ }
206
+ /** Comparison operator in a cross-store metric predicate (`> 5000`). */
207
+ type ComparisonOp = WqlComparisonOp;
208
+ /**
209
+ * Analytics half of a cross-store join — the metric predicate attached to a
210
+ * find query via `where`. Example: `sum:totalVolume{discipline:strength} > 5000`.
211
+ * Aggregates are evaluated against RAW WorkoutResult logs (not derived facts),
212
+ * joined at the blockContentId level ("logs win", issue #800).
213
+ */
214
+ interface MetricPredicate {
215
+ agg: Aggregator;
216
+ metric: string;
217
+ filters: TagFilter[];
218
+ operator: ComparisonOp;
219
+ threshold: number;
220
+ }
221
+ /**
222
+ * Content half of a cross-store join — the find predicate attached to an
223
+ * analytics query via `where`. Example: `find:note{tags:competition} in journal`.
224
+ * Restricts the metric computation to the blockContentIds owned by matching
225
+ * content; the metric is recomputed from raw logs for those blocks only.
226
+ */
227
+ interface FindPredicate {
228
+ target: string;
229
+ filters: TagFilter[];
230
+ scope?: string;
231
+ last?: {
232
+ size: number;
233
+ unit: 'd' | 'w';
234
+ };
235
+ }
236
+ interface ParsedQuery {
237
+ raw: string;
238
+ agg: Aggregator;
239
+ /** Canonical Metric Key (fact row `metricKey`). */
240
+ metric: string;
241
+ filters: TagFilter[];
242
+ /** Tag keys, or virtual dims: day | week | session | round. */
243
+ groupBy: string[];
244
+ rollup?: {
245
+ size: number;
246
+ unit: 'd' | 'w';
247
+ };
248
+ /** Optional display unit directive — `in kg` / `in lb`. */
249
+ displayUnit?: string;
250
+ /** Cross-store content join (`where find:note{...}`); restricts to raw logs. */
251
+ join?: FindPredicate;
252
+ error?: string;
253
+ }
254
+ /** Result of parsing a content-discovery query (`find:target{filters} in scope`). */
255
+ interface ParsedFindQuery {
256
+ raw: string;
257
+ /** Content target: 'note' or 'block'. */
258
+ target: string;
259
+ filters: TagFilter[];
260
+ /** Where to look: journal | collections | feeds | all. */
261
+ scope?: string;
262
+ /** Time window: last 8w, last 4d. */
263
+ last?: {
264
+ size: number;
265
+ unit: 'd' | 'w';
266
+ };
267
+ /** Cross-store metric join (`where sum:totalVolume{} > 5000`). */
268
+ join?: MetricPredicate;
269
+ error?: string;
270
+ }
271
+ type AnyParsedQuery = ParsedQuery | ParsedFindQuery | ParsedRowsQuery;
272
+ /** Type guard: true for content-discovery queries. */
273
+ declare function isFindQuery(parsed: AnyParsedQuery): parsed is ParsedFindQuery;
274
+ /** Type guard: true for rows queries. */
275
+ declare function isRowsQuery(parsed: AnyParsedQuery): parsed is ParsedRowsQuery;
276
+ interface SeriesPoint {
277
+ ts: number;
278
+ value: number;
279
+ }
280
+ /**
281
+ * Result of parsing a rows query (`rows:{filters}` / `rows:segment{filters}`) —
282
+ * the third WQL family (ADR docs/adr/rows-query-plane.md, #949): raw
283
+ * output-statement rows re-derived from WorkoutResult logs, scoped by
284
+ * `result:` / `block:` / `note:`. Never aggregates — no by/rollup/where.
285
+ */
286
+ interface ParsedRowsQuery {
287
+ raw: string;
288
+ /** Family discriminator (the other families are told apart by 'target'/'agg'). */
289
+ family: 'rows';
290
+ /** Output-statement type narrowing from the optional target (`rows:segment{…}`); undefined = all types. */
291
+ outputType?: string;
292
+ filters: TagFilter[];
293
+ /** Time window over the workout end time: last 8w, last 4d. */
294
+ last?: {
295
+ size: number;
296
+ unit: 'd' | 'w';
297
+ };
298
+ error?: string;
299
+ }
300
+ interface Series {
301
+ key: string;
302
+ label: string;
303
+ points: SeriesPoint[];
304
+ unit?: string;
305
+ }
306
+ /**
307
+ * Parse a WQL query string into either an analytics ParsedQuery or a content
308
+ * ParsedFindQuery. Dispatch is textual: a leading `find:` routes to the
309
+ * content path, everything else to analytics.
310
+ */
311
+ declare function parseQuery(raw: string): AnyParsedQuery;
312
+
313
+ /**
314
+ * Canonical discipline vocabulary — the single source of truth for effort
315
+ * disciplines across the resolver, the TIS factor table, fact-row tags, the
316
+ * WQL tag dictionary, and UI filters.
317
+ */
318
+ declare const EFFORT_DISCIPLINES: readonly ["bodyweight", "cycling", "gymnastics", "kettlebell", "recovery", "rowing", "running", "strength", "swimming", "walking"];
319
+ type EffortDiscipline = (typeof EFFORT_DISCIPLINES)[number];
320
+ declare function isEffortDiscipline(value: unknown): value is EffortDiscipline;
321
+ /**
322
+ * TIS discipline multiplier per canonical discipline. Loaded modalities
323
+ * (strength + the loaded-skill pair kettlebell/gymnastics) score 1.2;
324
+ * recovery absorbs the retired 'yoga' case at 0.9; the monostructural five
325
+ * and bodyweight take the 1.0 default. Unknown/absent disciplines fall back
326
+ * to 1.0.
327
+ */
328
+ declare const DISCIPLINE_FACTORS: Record<EffortDiscipline, number>;
329
+ /** Factor for an arbitrary discipline string; 1.0 when absent or unknown. */
330
+ declare function disciplineFactorFor(discipline: string | undefined): number;
331
+
332
+ /**
333
+ * WQL language support — CodeMirror highlighting + autocomplete over the
334
+ * Lezer grammar (grammar/wql.grammar).
335
+ *
336
+ * The completion vocabulary is the analytics dictionary:
337
+ * - aggregators from the AST contract (WQL_AGGREGATORS)
338
+ * - Canonical Metric Keys (CONTEXT.md §Analytics): base families, Tier-2
339
+ * aggregates, and `<effortSlug>.<family>` for every known effort
340
+ * - Tag keys read off fact rows by the Query Service, with value
341
+ * vocabularies for discipline (canonical EFFORT_DISCIPLINES), intensity,
342
+ * and grain; effort values come from the EffortResolver
343
+ * - virtual dimensions day | week | session | round
344
+ */
345
+
346
+ declare const wqlLanguage: LRLanguage;
347
+ interface WqlCompletionOptions {
348
+ /**
349
+ * Effort slugs for `{effort:…}` values and `<effortSlug>.<family>` metric
350
+ * keys — feed from the EffortResolver:
351
+ * `() => resolver.list().map(e => e.slug)`.
352
+ */
353
+ effortNames?: () => readonly string[];
354
+ }
355
+ declare function wqlCompletionSource(options_?: WqlCompletionOptions): (context: CompletionContext) => CompletionResult | null;
356
+ declare function wqlCompletion(options?: WqlCompletionOptions): Extension;
357
+ /**
358
+ * Query-field colors, mapped onto the app's theme tokens so
359
+ * the field follows light/dark mode: aggregator + structural keywords take
360
+ * the primary hue, the metric takes accent, tag keys secondary, values
361
+ * success-green, dimensions warning-ochre.
362
+ */
363
+ declare const wqlHighlightStyle: HighlightStyle;
364
+ declare function wql(completionOptions?: WqlCompletionOptions): LanguageSupport;
365
+
366
+ /**
367
+ * Analytics unit conversion seam — narrow, fake-free, pure.
368
+ *
369
+ * Supports exactly the unit families analytics currently needs:
370
+ * - mass: kg ↔ lb (exact 0.45359237 kg per lb)
371
+ * - distance: m ↔ km
372
+ *
373
+ * Any unknown or incompatible units pass through unchanged.
374
+ */
375
+ declare const KG_PER_LB = 0.45359237;
376
+ type UnitFamily = 'mass' | 'distance';
377
+ interface UnitFamilyDef {
378
+ /** Base unit for the family (used for documentation, not conversion). */
379
+ base: string;
380
+ /** Factor to convert FROM this unit TO the base unit. */
381
+ units: Record<string, number>;
382
+ }
383
+ declare const UNIT_FAMILIES: Record<UnitFamily, UnitFamilyDef>;
384
+ declare function getUnitFamily(unit: string | undefined): UnitFamily | undefined;
385
+ /**
386
+ * Convert a value between known units.
387
+ *
388
+ * Returns the input unchanged when:
389
+ * - from === to
390
+ * - either unit is unknown
391
+ * - the units belong to different families
392
+ */
393
+ declare function convert(value: number, from: string | undefined, to: string | undefined): number;
394
+ /**
395
+ * Determine the target display unit for a set of matched facts and query
396
+ * options. Returns the unit to render and whether values should be converted.
397
+ */
398
+ declare function resolveDisplayUnit(facts: ReadonlyArray<{
399
+ unit?: string;
400
+ metricUnit?: string;
401
+ }>, { directive, preferred, }: {
402
+ directive?: string;
403
+ preferred?: string;
404
+ }): {
405
+ unit?: string;
406
+ convert: boolean;
407
+ };
408
+
409
+ /**
410
+ * Workout derivation helpers for WQL QueryService and cross-store joins.
411
+ */
412
+
413
+ /**
414
+ * Map a summary projection name to its Canonical Metric Key — the one key two
415
+ * workouts must share for an aggregate to be compared across them. Same key
416
+ * in fact rows and display. 'Total Volume' → 'totalVolume', 'TIS' → 'tis'.
417
+ */
418
+ declare function resolveCanonicalMetricKey(projectionName: string): string;
419
+ /**
420
+ * Identity of the block a result belongs to, stamped on every fact row.
421
+ */
422
+ interface SummaryFactIdentity {
423
+ noteId: string;
424
+ resultId: string;
425
+ /** FK to NoteSegment.id (positional section id of the block run). */
426
+ segmentId?: string;
427
+ /** NoteSegment.version at record time. */
428
+ segmentVersion?: number;
429
+ /** Content-stable cross-note join key. */
430
+ blockContentId?: string;
431
+ /** Which surface produced the result; trend queries exclude 'playground' by default. */
432
+ origin?: ResultOrigin;
433
+ /** FK to the `page` store (copied from the parent note). */
434
+ pageId?: string;
435
+ /**
436
+ * Canonical workout time — WorkoutResult.createdAt (true workout end).
437
+ * Every fact row carries it as `timestamp` so time-range queries mean
438
+ * "when the workout happened", never "when the metric was derived".
439
+ */
440
+ workoutTimestamp?: number;
441
+ }
442
+ /** Logs shape read by normalizeSummaryFacts (StoredOutputStatement-compatible). */
443
+ interface SummaryFactSourceOutput {
444
+ outputType?: string;
445
+ metrics: readonly {
446
+ type?: string;
447
+ value?: unknown;
448
+ image?: string;
449
+ unit?: string;
450
+ /** Summary-processor payload (effortSlug / effortDiscipline / effortIntensityTier / …). */
451
+ metadata?: Record<string, unknown>;
452
+ }[];
453
+ timeSpan?: {
454
+ started?: number;
455
+ ended?: number;
456
+ };
457
+ }
458
+ /**
459
+ * Convert Tier-2 summary outputs (outputType 'analytics') in a result's logs
460
+ * into persisted fact rows — one row per result × Canonical Metric Key.
461
+ */
462
+ declare function normalizeSummaryFacts(logs: readonly SummaryFactSourceOutput[], identity: SummaryFactIdentity): AnalyticsDataPoint[];
463
+
464
+ /**
465
+ * Pure projection of a block_index into static Notes — one Note per distinct
466
+ * noteId, with a `catalog` field set to `noteId.split('/')[0]`. The catalog is
467
+ * the directory the file lives under (e.g. `crossfit-girls` for collections,
468
+ * `crossfit-programming` for feeds) and is what the Library's panel uses to
469
+ * target the `+ Filter → Catalog` menu.
470
+ */
471
+ declare function staticNotesFromBlocks(blocks: BlockIndexRow[]): Note[];
472
+ /**
473
+ * Tag → noteIds index over a block index's frontmatter rows. Static-corpus
474
+ * notes declare tags only in frontmatter `tags:`; this recovers the mapping
475
+ * the note store needs to answer `tags:` clauses (issue #853 — the store
476
+ * previously returned an empty set for every tag).
477
+ */
478
+ declare function staticTagIndexFromBlocks(blocks: BlockIndexRow[]): Map<string, Set<string>>;
479
+
480
+ /**
481
+ * Injected store interfaces for WQL QueryService.
482
+ * Zero database/storage dependencies — 100% pure abstractions.
483
+ */
484
+
485
+ /** Store surface the Query Service needs for fact rows. */
486
+ interface FactQueryStore {
487
+ getFactsByMetric(metricKey: string): Promise<AnalyticsDataPoint[]>;
488
+ getFactsByTimeRange(start: number, end: number): Promise<AnalyticsDataPoint[]>;
489
+ getNoteTagLabels(noteId: string): Promise<string[]>;
490
+ }
491
+ /** Store surface for content queries (`find:note`). */
492
+ interface NoteQueryStore {
493
+ getAllNotes(): Promise<Note[]>;
494
+ getNoteIdsForTag(label: string): Promise<Set<string>>;
495
+ }
496
+ /** Store surface for block-index queries (`find:block`). */
497
+ interface BlockQueryStore {
498
+ getAllBlocks(): Promise<BlockIndexRow[]>;
499
+ }
500
+ /** Pure effort model interface for `find:effort` queries. */
501
+ interface IEffort {
502
+ id: string;
503
+ slug: string;
504
+ label: string;
505
+ aliases: string[];
506
+ baseAttributes: {
507
+ met?: number;
508
+ discipline?: string;
509
+ intensityTier?: string;
510
+ [key: string]: unknown;
511
+ };
512
+ registrySource?: 'bundled' | 'user' | string;
513
+ [key: string]: unknown;
514
+ }
515
+ /** Store surface for effort queries (`find:effort`). */
516
+ interface EffortQueryStore {
517
+ getAllEfforts(): Promise<IEffort[]>;
518
+ }
519
+ /** Store surface for raw WorkoutResult logs — the cross-store join & rows source. */
520
+ interface ResultLogStore {
521
+ getResultsByContentId(blockContentId: string): Promise<WorkoutResult[]>;
522
+ /** Rows plane (#949): single-result scope (`rows:{result:…}`). */
523
+ getResultById(resultId: string): Promise<WorkoutResult | undefined>;
524
+ /** Rows plane (#949): whole-note scope (`rows:{note:…}`). */
525
+ getResultsForNote(noteId: string): Promise<WorkoutResult[]>;
526
+ }
527
+ /** Optional bundle of stores for QueryService initialization. */
528
+ interface QueryServiceStores {
529
+ factStore?: FactQueryStore;
530
+ noteStore?: NoteQueryStore;
531
+ blockStore?: BlockQueryStore;
532
+ resultStore?: ResultLogStore;
533
+ effortStore?: EffortQueryStore;
534
+ staticNoteStore?: NoteQueryStore;
535
+ staticBlockStore?: BlockQueryStore;
536
+ }
537
+
538
+ /**
539
+ * Query Service — the executor of WQL against the Analytics Store
540
+ * (CONTEXT.md glossary). Four-stage physical plan:
541
+ *
542
+ * SELECT (index-first: by-metric + by-timestamp IDBKeyRange fetches,
543
+ * intersected in memory)
544
+ * BUCKET (time dim or rollup period)
545
+ * AGGREGATE (per bucket)
546
+ * GROUP (tag-dimension fan-out)
547
+ *
548
+ * Inputs are uncapped — personal-journal scale; widgets and tables are dumb
549
+ * consumers of QueryResult. Fact rows are summary-grain AnalyticsDataPoint
550
+ * rows; Tags are query-time dimensions read off the row (effort, discipline,
551
+ * note, intensity, …) — 'tags' resolves through the note_tags store.
552
+ *
553
+ * Fully inverted dependencies: zero IndexedDB/storage module-level imports.
554
+ */
555
+
556
+ interface FindQueryResult {
557
+ parsed: ParsedFindQuery;
558
+ notes: Note[];
559
+ blocks: BlockIndexRow[];
560
+ /** Registry rows for find:effort queries. */
561
+ efforts?: IEffort[];
562
+ stages: {
563
+ selected: number;
564
+ matched: number;
565
+ };
566
+ }
567
+ interface QueryOptions {
568
+ rangeStart?: number;
569
+ rangeEnd?: number;
570
+ /** App-level unit preference ('kg' | 'lb'). Used when query has no `in <unit>` directive. */
571
+ preferredUnit?: string;
572
+ }
573
+ /** Options for `runFind` / `runFindBlock` — overrides for the parsed WQL. */
574
+ interface FindOptions {
575
+ /** Explicit timestamp range; overrides parsed WQL's `last` clause when set. */
576
+ range?: {
577
+ start: number;
578
+ end: number;
579
+ };
580
+ /** Time-window anchoring mode. 'wall-clock' anchors to Date.now();
581
+ * 'latest-activity' anchors to the newest row in the scope selection
582
+ * (#857). Defaults to 'wall-clock'. */
583
+ anchor?: 'wall-clock' | 'latest-activity';
584
+ /** Explicit reference time for the window (useful for tests/replay).
585
+ * Overrides `anchor`. */
586
+ anchorNow?: number;
587
+ }
588
+ interface QueryResult {
589
+ parsed: ParsedQuery;
590
+ series: Series[];
591
+ stages: {
592
+ selected: number;
593
+ buckets: number;
594
+ aggregated: number;
595
+ groups: number;
596
+ };
597
+ matched: AnalyticsDataPoint[];
598
+ /** Single scalar value when the result has exactly one point. */
599
+ scalar?: number;
600
+ /** Result display unit, if determined by directive, preference, or fact metadata. */
601
+ unit?: string;
602
+ }
603
+ /** One run in a rows result: the stored result plus the output statements
604
+ * that survived the optional output-type narrowing (`rows:segment{…}`). */
605
+ interface RowsRun {
606
+ result: WorkoutResult;
607
+ logs: StoredOutputStatement[];
608
+ }
609
+ interface RowsQueryResult {
610
+ parsed: ParsedRowsQuery;
611
+ runs: RowsRun[];
612
+ error?: string;
613
+ }
614
+ declare class QueryService {
615
+ private readonly store;
616
+ private readonly noteStore;
617
+ private readonly blockStore;
618
+ private readonly resultStore;
619
+ private readonly effortStore;
620
+ private readonly staticNoteStore?;
621
+ private readonly staticBlockStore?;
622
+ constructor(storesOrFactStore?: QueryServiceStores | FactQueryStore, noteStore?: NoteQueryStore, blockStore?: BlockQueryStore, resultStore?: ResultLogStore, effortStore?: EffortQueryStore, staticNoteStore?: NoteQueryStore, staticBlockStore?: BlockQueryStore);
623
+ getFactsByTimeRange(start: number, end: number): Promise<AnalyticsDataPoint[]>;
624
+ runQuery(raw: string, options?: QueryOptions): Promise<QueryResult>;
625
+ /**
626
+ * Execute a rows query (rows:{…}, #949) — the session results table plane.
627
+ * Re-derives output statements from raw WorkoutResult logs through the
628
+ * ResultLogStore seam ("logs win" — facts are never read for this path).
629
+ */
630
+ runRows(parsed: ParsedRowsQuery, options?: {
631
+ anchorNow?: number;
632
+ }): Promise<RowsQueryResult>;
633
+ /**
634
+ * Execute a content-discovery query (find:note). Naive in-memory filtering
635
+ * per the tracer-bullet scope (#797): load all notes, then apply tag/text/
636
+ * time filters.
637
+ */
638
+ runFind(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
639
+ /**
640
+ * Execute a find:block query against the derived block_index store.
641
+ */
642
+ runFindBlock(parsed: ParsedFindQuery, options?: FindOptions): Promise<FindQueryResult>;
643
+ /**
644
+ * Execute a find:effort query against the effort registry.
645
+ */
646
+ runFindEffort(parsed: ParsedFindQuery): Promise<FindQueryResult>;
647
+ run(parsed: ParsedQuery, options?: QueryOptions): Promise<QueryResult>;
648
+ /**
649
+ * Stages 2–4 — BUCKET → GROUP → AGGREGATE over an already-selected +
650
+ * filtered `matched` set.
651
+ */
652
+ private buildResult;
653
+ /** Direction 2 — re-derive the metric from raw logs, restricted to the
654
+ * blockContentIds owned by the find predicate's content matches. */
655
+ private runJoined;
656
+ /** Direction 1 — keep only content owning a wod block whose raw-log metric
657
+ * aggregate satisfies the predicate. `notes` for find:note, `blocks` for
658
+ * find:block. */
659
+ private applyMetricJoin;
660
+ /** Re-derive the metric from raw logs for each content id, aggregate per id,
661
+ * and return the ids whose aggregate satisfies the join predicate. */
662
+ private contentIdsSatisfying;
663
+ /** Filter per-effort vs un-attributed overall summary rows to avoid
664
+ * double-counting. */
665
+ private applyEffortScope;
666
+ /** Re-derive summary facts for one Canonical Metric Key from RAW WorkoutResult
667
+ * logs across the given content ids — the cross-store join source. One fact
668
+ * row per result (normalizeSummaryFacts dedupes within a result). */
669
+ private deriveMetricFacts;
670
+ /** All content blocks across the journal + static corpus. */
671
+ private allContentBlocks;
672
+ /** Map each note id to the wod blockContentIds it owns. */
673
+ private noteContentMap;
674
+ /** Collect wod blockContentIds owned by a find query's content matches. */
675
+ private contentIdsFromFindResult;
676
+ /** Load note tag labels only when the query touches 'tags'. */
677
+ private loadNoteTags;
678
+ }
679
+
680
+ /**
681
+ * Dashboard Note model (#898 map, format locked in #899).
682
+ *
683
+ * A dashboard is a markdown note: frontmatter marks it (`dashboard: true`) and
684
+ * declares top-level controls as `dashboard.*` dot-keys; the body composes
685
+ * ```query[:<type>][-<N>|-full] blocks whose title/question associate from the
686
+ * markdown heading/paragraph directly above each block. Queries reference
687
+ * tokens as `$name`, substituted as raw text at execution time. A block body
688
+ * is one line: WQL query + optional `/`-separated positional params.
689
+ *
690
+ * This module is pure — no React, no editor, no query service — so the editor
691
+ * extensions, the inline block renderer, and the dashboard route all share it.
692
+ */
693
+ declare const DASHBOARD_WIDGET_TYPES: readonly ["table", "value", "timeseries", "bar", "toplist", "stacked-bar", "goal-rings", "zone-distribution"];
694
+ type DashboardWidgetType = (typeof DASHBOARD_WIDGET_TYPES)[number];
695
+ declare function isDashboardWidgetType(type: string): type is DashboardWidgetType;
696
+ /** The widget type a block renders as: a bare ```query fence defaults to `table`. */
697
+ declare function resolveWidgetType(type: string | undefined): string;
698
+ /** Shared badge messages — the editor, inline renderer, and route all word these identically. */
699
+ declare function unknownWidgetTypeMessage(type: string): string;
700
+ declare function unknownTokensMessage(missing: readonly string[]): string;
701
+ /** Types whose renderer is planned for future maps (none remaining in #901). */
702
+ declare const PLANNED_WIDGET_TYPES: readonly string[];
703
+ /**
704
+ * True when a metric is a proposed calculation not yet landed in the engine
705
+ * (e.g. `calc.readiness`, `calc.mvcBw`, `calc.hrv`). Renderers show a labeled
706
+ * placeholder badge (#901).
707
+ */
708
+ declare function isProposedMetric(metricKey: string | undefined): boolean;
709
+ declare const DASHBOARD_GRID_MAX_COLS = 4;
710
+ interface QueryWidgetSuffix {
711
+ /** Raw widget type from the tag, lowercased; '' for a bare ```query fence. */
712
+ type: string;
713
+ /** Grid column span 1..4 (`-N`). */
714
+ spanCols?: number;
715
+ /** Full-row flag (`-full`). Mutually exclusive with spanCols. */
716
+ spanFull?: boolean;
717
+ /** Set when the suffix is malformed (the type is still reported raw). */
718
+ error?: string;
719
+ }
720
+ /**
721
+ * Parse the suffix after `query:` in a fence tag — `<type>`, `<type>-<N>` or
722
+ * `<type>-full`. Unknown types parse fine (renderers badge them); structural
723
+ * problems (empty type, bad span, `-N-full` combo) set `error`.
724
+ */
725
+ declare function parseQueryWidgetSuffix(suffix: string): QueryWidgetSuffix;
726
+ /** Frontmatter meta shape. */
727
+ type DashboardMeta = Record<string, string | number | string[]>;
728
+ interface DashboardToken {
729
+ /** Token name without the `dashboard.` prefix (referenced as `$name`). */
730
+ name: string;
731
+ /** Scalar → single entry; block list → entries in declared order. */
732
+ values: string[];
733
+ /** List tokens render as segmented controls; scalars as inputs. */
734
+ isList: boolean;
735
+ }
736
+ /** True when the note's frontmatter marks it as a dashboard (`dashboard: true`). */
737
+ declare function isDashboardMeta(meta: DashboardMeta): boolean;
738
+ /** Extract `dashboard.*` dot-keys as tokens, in frontmatter declaration order. */
739
+ declare function extractDashboardTokens(meta: DashboardMeta): DashboardToken[];
740
+ /** Default value per token: the scalar, or the first list entry (its control default). */
741
+ declare function defaultTokenValues(tokens: readonly DashboardToken[]): Record<string, string>;
742
+ /**
743
+ * Write a control change back into frontmatter meta (decision #899-6): a list
744
+ * token reorders so the chosen value becomes the first entry (the default);
745
+ * a scalar token's value is replaced. Returns a NEW meta object — serialize
746
+ * with `serializeFrontmatter` and patch the note's frontmatter section.
747
+ */
748
+ declare function setDashboardTokenValue(meta: DashboardMeta, name: string, value: string): DashboardMeta;
749
+ /**
750
+ * Substitute `$name` references with token values — raw text replacement at
751
+ * execution time (decision #899-6). Unknown references stay literal and are
752
+ * reported in `missing` (renderers badge them; never silent).
753
+ */
754
+ declare function substituteTokens(query: string, values: Record<string, string>): {
755
+ query: string;
756
+ missing: string[];
757
+ };
758
+ /** Token names a query references, in order of first appearance. */
759
+ declare function referencedTokens(query: string): string[];
760
+ /**
761
+ * Split a block body into the WQL query and its trailing `/`-separated
762
+ * positional parameters (decision #899-7). Splits at the FIRST ` / ` so
763
+ * params stay trailing; a literal ` / ` inside a tag value is pathological
764
+ * and unsupported. Params are whitespace-split literals or `$token` refs.
765
+ */
766
+ declare function splitWidgetBody(body: string): {
767
+ query: string;
768
+ params: string[];
769
+ };
770
+ /** Structural input for the builder — satisfied by EditorSection + doc text. */
771
+ interface DashboardSectionInput {
772
+ /** Section type ('query', 'markdown', 'frontmatter', …). */
773
+ type: string;
774
+ /** Markdown subtype ('heading', 'paragraph', …) for markdown sections. */
775
+ subtype?: string;
776
+ /** Raw inner content of the section. */
777
+ content: string;
778
+ /** Widget type suffix parsed from the fence tag (query sections). */
779
+ widgetType?: string;
780
+ /** Grid column span parsed from the fence tag (query sections). */
781
+ spanCols?: number;
782
+ /** Full-row flag parsed from the fence tag (query sections). */
783
+ spanFull?: boolean;
784
+ /** Malformed fence-suffix reason — carried so route mode badges it too. */
785
+ widgetError?: string;
786
+ }
787
+ interface DashboardWidget {
788
+ /** Stable key within the document (section index). */
789
+ key: string;
790
+ /** Raw widget type ('' → default `table` in dashboard composition). */
791
+ type: string;
792
+ spanCols?: number;
793
+ spanFull?: boolean;
794
+ /** Raw block body (query + params). */
795
+ body: string;
796
+ /** Malformed fence-suffix reason — renderers badge instead of executing. */
797
+ widgetError?: string;
798
+ /** WQL query after parameter split. */
799
+ query: string;
800
+ /** Positional parameters (literals or `$token` refs). */
801
+ params: string[];
802
+ /** Title from the markdown heading directly above (or above the question). */
803
+ title?: string;
804
+ /** Coaching question from the paragraph directly above the block. */
805
+ question?: string;
806
+ }
807
+ interface DashboardDocument {
808
+ isDashboard: boolean;
809
+ /** Note title from frontmatter. */
810
+ title?: string;
811
+ tokens: DashboardToken[];
812
+ widgets: DashboardWidget[];
813
+ }
814
+ /**
815
+ * Build the dashboard document from ordered sections + frontmatter meta.
816
+ *
817
+ * Title/question association (decision #899-4) is strictly adjacency-based:
818
+ * a paragraph directly above the block is its question; a heading directly
819
+ * above that paragraph — or directly above the block — is its title. Anything
820
+ * else leaves the widget untitled (renderers fall back to the query text).
821
+ */
822
+ declare function buildDashboardDocument(sections: readonly DashboardSectionInput[], meta: DashboardMeta): DashboardDocument;
823
+
824
+ interface DashboardParsedSection extends DashboardSectionInput {
825
+ /** 0-indexed start line of the section in the raw content. */
826
+ startLine: number;
827
+ /** 0-indexed inclusive end line of the section in the raw content. */
828
+ endLine: number;
829
+ }
830
+ /**
831
+ * Parses a raw markdown note into sections suitable for `buildDashboardDocument`,
832
+ * keeping track of line numbers so edits can write back to the source text.
833
+ */
834
+ declare function parseDashboardNote(rawContent: string): {
835
+ meta: DashboardMeta;
836
+ sections: DashboardParsedSection[];
837
+ };
838
+
839
+ /**
840
+ * buildDashboardScaffold — raw markdown for a freshly created dashboard note
841
+ * (#907 creation flow, format locked in #899).
842
+ */
843
+ declare const DEFAULT_DASHBOARD_TITLE = "New Dashboard";
844
+ /** Slugify a dashboard title for route addressing (/dashboard/:slug). */
845
+ declare function dashboardSlug(title: string): string;
846
+ declare function buildDashboardScaffold(title?: string): string;
847
+
848
+ /**
849
+ * Shared frontmatter parser utilities for WQL dashboard models and notes.
850
+ */
851
+ interface ParsedFrontmatter {
852
+ meta: Record<string, string | number | string[]>;
853
+ body: string;
854
+ }
855
+ interface LinkWidget {
856
+ kind: 'youtube' | 'amazon' | 'strava' | 'source' | 'website' | 'book';
857
+ url?: string;
858
+ label: string;
859
+ videoId?: string;
860
+ }
861
+ /**
862
+ * Parse the leading YAML frontmatter block of a markdown string.
863
+ */
864
+ declare function parseFrontmatter(raw: string): ParsedFrontmatter;
865
+ /**
866
+ * Parse frontmatter body content (the lines between the `---` delimiters).
867
+ */
868
+ declare function parseFrontmatterBody(innerContent: string): ParsedFrontmatter['meta'];
869
+ /**
870
+ * Serialize frontmatter metadata back to YAML body lines.
871
+ */
872
+ declare function serializeFrontmatter(meta: ParsedFrontmatter['meta']): string;
873
+ /** Body of `raw` with the leading frontmatter block removed. */
874
+ declare function stripFrontmatter(raw: string): string;
875
+ /** Scalar value at `key`, or `undefined` when absent or a list. */
876
+ declare function getScalar(meta: ParsedFrontmatter['meta'], key: string): string | number | undefined;
877
+ /** List value at `key`, or `[]` when absent or a scalar. */
878
+ declare function getList(meta: ParsedFrontmatter['meta'], key: string): string[];
879
+ /**
880
+ * Parse the `category` YAML array from a full markdown string's frontmatter.
881
+ */
882
+ declare function parseFrontmatterCategories(raw: string): string[];
883
+ /**
884
+ * Extract the `tags` list from a frontmatter block's raw content.
885
+ */
886
+ declare function extractFrontmatterTags(raw: string): string[];
887
+ /**
888
+ * Parse flat scalar key-value pairs from raw inner content (no delimiters).
889
+ */
890
+ declare function parseFlatProperties(innerContent: string): Record<string, string>;
891
+ /**
892
+ * Parse scalar key-value pairs from an array of lines.
893
+ */
894
+ declare function parseFrontmatterProps(lines: string[]): Record<string, string>;
895
+
896
+ export { type Aggregator, type AnyParsedQuery, 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 FactQueryStore, type FindOptions, type FindPredicate, type FindQueryResult, type IEffort, KG_PER_LB, type LinkWidget, type MetricPredicate, type NoteQueryStore, PLANNED_WIDGET_TYPES, type ParsedFindQuery, type ParsedFrontmatter, type ParsedQuery, type ParsedRowsQuery, type ParsedWqlLastSuffix, type ParsedWqlRollupSuffix, type ParsedWqlSuffixes, type QueryOptions, type QueryResult, QueryService, type QueryServiceStores, type QueryWidgetSuffix, type ResultLogStore, type RowsQueryResult, type RowsRun, type Series, type SeriesPoint, type SummaryFactIdentity, type SummaryFactSourceOutput, type TagFilter, type TagValue, UNIT_FAMILIES, type UnitFamily, type UnitFamilyDef, WQL_AGGREGATORS, WQL_CALC_TARGETS, WQL_COMPARISON_OPS, WQL_CONTENT_FILTER_KEYS, WQL_DISPLAY_UNITS, WQL_FIND_TARGETS, WQL_GRAINS, WQL_INTENSITY_TIERS, WQL_KEYWORDS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_ROLLUP_PERIODS, WQL_SCOPES, WQL_SOURCES, WQL_TAG_KEYS, WQL_VIRTUAL_DIMS, type WqlAggregator, type WqlCalcTarget, type WqlComparisonOp, type WqlCompletionOptions, type WqlContentFilterKey, type WqlDisplayUnit, type WqlFindTarget, type WqlGrain, type WqlIntensityTier, type WqlKeyword, type WqlMetricAggregate, type WqlMetricFamily, type WqlRollupPeriod, type WqlScope, type WqlSource, type WqlTagKey, type WqlVirtualDim, buildDashboardDocument, buildDashboardScaffold, convert, dashboardSlug, defaultTokenValues, disciplineFactorFor, extractDashboardTokens, extractFrontmatterTags, getList, getScalar, getUnitFamily, isDashboardMeta, isDashboardWidgetType, isEffortDiscipline, isFindQuery, isProposedMetric, isRowsQuery, normalizeSummaryFacts, parseDashboardNote, parseFlatProperties, parseFrontmatter, parseFrontmatterBody, parseFrontmatterCategories, parseFrontmatterProps, parseQuery, parseQueryWidgetSuffix, parseWqlSuffixes, referencedTokens, resolveCanonicalMetricKey, resolveDisplayUnit, resolveWidgetType, serializeFrontmatter, setDashboardTokenValue, splitAtWhere, splitWidgetBody, staticNotesFromBlocks, staticTagIndexFromBlocks, stripFrontmatter, substituteTokens, unknownTokensMessage, unknownWidgetTypeMessage, wql, wqlCompletion, wqlCompletionSource, wqlHighlightStyle, wqlLanguage, parser as wqlParser, wql_parser_terms as wqlTerms };