@hypequery/datasets 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/agent-catalog.d.ts +2 -1
  2. package/dist/agent-catalog.d.ts.map +1 -1
  3. package/dist/api.type-test.js +22 -0
  4. package/dist/cache/definition-identity.d.ts +26 -0
  5. package/dist/cache/definition-identity.d.ts.map +1 -0
  6. package/dist/cache/definition-identity.js +164 -0
  7. package/dist/cache/protocol-cache-keys.d.ts +38 -0
  8. package/dist/cache/protocol-cache-keys.d.ts.map +1 -0
  9. package/dist/cache/protocol-cache-keys.js +191 -0
  10. package/dist/cache/query-signature.d.ts +6 -0
  11. package/dist/cache/query-signature.d.ts.map +1 -1
  12. package/dist/cache/query-signature.js +10 -13
  13. package/dist/cache/semantic-query-cache.d.ts +23 -0
  14. package/dist/cache/semantic-query-cache.d.ts.map +1 -1
  15. package/dist/catalog.d.ts +3 -2
  16. package/dist/catalog.d.ts.map +1 -1
  17. package/dist/catalog.js +25 -8
  18. package/dist/contract.d.ts +2 -1
  19. package/dist/contract.d.ts.map +1 -1
  20. package/dist/contract.js +2 -0
  21. package/dist/dataset-query.d.ts.map +1 -1
  22. package/dist/dataset-query.js +21 -4
  23. package/dist/dataset.d.ts +2 -2
  24. package/dist/dataset.d.ts.map +1 -1
  25. package/dist/dataset.js +9 -4
  26. package/dist/executor.d.ts +7 -1
  27. package/dist/executor.d.ts.map +1 -1
  28. package/dist/executor.js +60 -18
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/measure.d.ts +12 -2
  32. package/dist/measure.d.ts.map +1 -1
  33. package/dist/measure.js +11 -1
  34. package/dist/protocol-adapter.d.ts.map +1 -1
  35. package/dist/protocol-adapter.js +10 -1
  36. package/dist/protocol-deployment-adapter.d.ts.map +1 -1
  37. package/dist/protocol-deployment-adapter.js +2 -1
  38. package/dist/query-planner.d.ts +1 -1
  39. package/dist/query-planner.d.ts.map +1 -1
  40. package/dist/query-planner.js +5 -17
  41. package/dist/semantic-plan.d.ts +0 -1
  42. package/dist/semantic-plan.d.ts.map +1 -1
  43. package/dist/semantic-planner.d.ts.map +1 -1
  44. package/dist/semantic-planner.js +10 -3
  45. package/dist/semantic-query-schema.d.ts.map +1 -1
  46. package/dist/semantic-query-schema.js +2 -0
  47. package/dist/types.d.ts +62 -14
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/utils/agent-catalog-projection.d.ts +3 -2
  50. package/dist/utils/agent-catalog-projection.d.ts.map +1 -1
  51. package/dist/utils/agent-catalog-projection.js +2 -10
  52. package/dist/utils/approximate-measures.d.ts +2 -2
  53. package/dist/utils/approximate-measures.d.ts.map +1 -1
  54. package/dist/utils/approximate-measures.js +3 -6
  55. package/dist/utils/dataset-contract.d.ts.map +1 -1
  56. package/dist/utils/dataset-contract.js +2 -1
  57. package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
  58. package/dist/utils/dataset-definition-validation.js +6 -24
  59. package/dist/utils/dataset-derived-query.d.ts.map +1 -1
  60. package/dist/utils/dataset-derived-query.js +7 -50
  61. package/dist/utils/dataset-measures.d.ts +13 -6
  62. package/dist/utils/dataset-measures.d.ts.map +1 -1
  63. package/dist/utils/dataset-measures.js +25 -4
  64. package/dist/utils/dataset-metric-ref.d.ts +2 -2
  65. package/dist/utils/dataset-metric-ref.d.ts.map +1 -1
  66. package/dist/utils/dataset-query-result.d.ts.map +1 -1
  67. package/dist/utils/dataset-query-result.js +2 -1
  68. package/dist/utils/dataset-query-validation.d.ts.map +1 -1
  69. package/dist/utils/dataset-query-validation.js +7 -3
  70. package/dist/utils/derived-measure-sql.d.ts +3 -0
  71. package/dist/utils/derived-measure-sql.d.ts.map +1 -0
  72. package/dist/utils/derived-measure-sql.js +46 -0
  73. package/dist/utils/derived-measure-validation.d.ts.map +1 -1
  74. package/dist/utils/derived-measure-validation.js +6 -5
  75. package/dist/utils/measure-time-grains.d.ts +4 -0
  76. package/dist/utils/measure-time-grains.d.ts.map +1 -0
  77. package/dist/utils/measure-time-grains.js +16 -0
  78. package/dist/utils/query-timezone.d.ts +9 -0
  79. package/dist/utils/query-timezone.d.ts.map +1 -0
  80. package/dist/utils/query-timezone.js +35 -0
  81. package/dist/utils/semantic-metadata-validation.js +1 -1
  82. package/dist/utils/semantic-metadata.d.ts.map +1 -1
  83. package/dist/utils/semantic-metadata.js +3 -1
  84. package/dist/utils/shift-measure-definition.d.ts +4 -0
  85. package/dist/utils/shift-measure-definition.d.ts.map +1 -0
  86. package/dist/utils/shift-measure-definition.js +9 -0
  87. package/dist/utils/shift-measure-sql.d.ts +18 -0
  88. package/dist/utils/shift-measure-sql.d.ts.map +1 -0
  89. package/dist/utils/shift-measure-sql.js +46 -0
  90. package/dist/utils/shift-measure-validation.d.ts +3 -0
  91. package/dist/utils/shift-measure-validation.d.ts.map +1 -0
  92. package/dist/utils/shift-measure-validation.js +20 -0
  93. package/dist/utils/sql-under-joins.d.ts +10 -0
  94. package/dist/utils/sql-under-joins.d.ts.map +1 -0
  95. package/dist/utils/sql-under-joins.js +15 -0
  96. package/dist/utils/time-arithmetic-sql.d.ts +4 -0
  97. package/dist/utils/time-arithmetic-sql.d.ts.map +1 -0
  98. package/dist/utils/time-arithmetic-sql.js +9 -0
  99. package/dist/utils/time-axis-bounds.d.ts +24 -0
  100. package/dist/utils/time-axis-bounds.d.ts.map +1 -0
  101. package/dist/utils/time-axis-bounds.js +71 -0
  102. package/dist/utils/time-axis-calendar-guard.d.ts +9 -0
  103. package/dist/utils/time-axis-calendar-guard.d.ts.map +1 -0
  104. package/dist/utils/time-axis-calendar-guard.js +17 -0
  105. package/dist/utils/time-axis-intervals.d.ts +13 -0
  106. package/dist/utils/time-axis-intervals.d.ts.map +1 -0
  107. package/dist/utils/time-axis-intervals.js +81 -0
  108. package/dist/utils/time-measure-axis-sql.d.ts +12 -0
  109. package/dist/utils/time-measure-axis-sql.d.ts.map +1 -0
  110. package/dist/utils/time-measure-axis-sql.js +47 -0
  111. package/dist/utils/time-measure-axis.d.ts +19 -0
  112. package/dist/utils/time-measure-axis.d.ts.map +1 -0
  113. package/dist/utils/time-measure-axis.js +45 -0
  114. package/dist/utils/time-measure-dataset-sql.d.ts +9 -0
  115. package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -0
  116. package/dist/utils/time-measure-dataset-sql.js +63 -0
  117. package/dist/utils/time-measure-result-sql.d.ts +6 -0
  118. package/dist/utils/time-measure-result-sql.d.ts.map +1 -0
  119. package/dist/utils/time-measure-result-sql.js +30 -0
  120. package/dist/utils/time-measure-source-sql.d.ts +22 -0
  121. package/dist/utils/time-measure-source-sql.d.ts.map +1 -0
  122. package/dist/utils/time-measure-source-sql.js +75 -0
  123. package/dist/utils/time-measure-values-sql.d.ts +20 -0
  124. package/dist/utils/time-measure-values-sql.d.ts.map +1 -0
  125. package/dist/utils/time-measure-values-sql.js +87 -0
  126. package/dist/utils/time-query-measures.d.ts +6 -0
  127. package/dist/utils/time-query-measures.d.ts.map +1 -0
  128. package/dist/utils/time-query-measures.js +28 -0
  129. package/dist/utils/window-aggregation-sql.d.ts +5 -0
  130. package/dist/utils/window-aggregation-sql.d.ts.map +1 -0
  131. package/dist/utils/window-aggregation-sql.js +17 -0
  132. package/dist/utils/window-catalog-metadata.d.ts +14 -0
  133. package/dist/utils/window-catalog-metadata.d.ts.map +1 -0
  134. package/dist/utils/window-catalog-metadata.js +17 -0
  135. package/dist/utils/window-measure-definition.d.ts +4 -0
  136. package/dist/utils/window-measure-definition.d.ts.map +1 -0
  137. package/dist/utils/window-measure-definition.js +11 -0
  138. package/dist/utils/window-measure-dependencies.d.ts +4 -0
  139. package/dist/utils/window-measure-dependencies.d.ts.map +1 -0
  140. package/dist/utils/window-measure-dependencies.js +5 -0
  141. package/dist/utils/window-measure-validation.d.ts +3 -0
  142. package/dist/utils/window-measure-validation.d.ts.map +1 -0
  143. package/dist/utils/window-measure-validation.js +49 -0
  144. package/dist/utils/window-query-measures.d.ts +4 -0
  145. package/dist/utils/window-query-measures.d.ts.map +1 -0
  146. package/dist/utils/window-query-measures.js +17 -0
  147. package/dist/utils/window-query-signature.d.ts +4 -0
  148. package/dist/utils/window-query-signature.d.ts.map +1 -0
  149. package/dist/utils/window-query-signature.js +25 -0
  150. package/package.json +3 -3
@@ -1,8 +1,9 @@
1
+ import { baseMeasureNames } from './dataset-measures.js';
1
2
  import { datasetTimeGrains } from './dataset-time-grains.js';
2
3
  import { snapshotSemanticMetadata } from './semantic-metadata.js';
3
4
  export function buildMetricContract(metricName, ds, spec, label, description, metadata = {}, grain) {
4
5
  const dimensionNames = Object.keys(ds.dimensions);
5
- const measureNames = Object.keys(ds.measures);
6
+ const measureNames = baseMeasureNames(ds.measures);
6
7
  const filterNames = Object.keys(ds.filters).length > 0
7
8
  ? Object.keys(ds.filters)
8
9
  : dimensionNames.filter(name => ds.dimensions[name]?.filterable !== false);
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-definition-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-definition-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAMrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAC5D,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AA+M/D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,CAAC,GAClE,IAAI,CAwDN"}
1
+ {"version":3,"file":"dataset-definition-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-definition-validation.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAOrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAC5D,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AA+M/D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,CAAC,GAClE,IAAI,CA0DN"}
@@ -1,28 +1,8 @@
1
- /**
2
- * Definition-time structural validation for `dataset()`.
3
- *
4
- * Every check here runs when the dataset is *constructed*, not when it is
5
- * queried. A dataset is normally defined at module scope, so a malformed model
6
- * fails at import — in the build, in CI, and in the first test that touches the
7
- * module — instead of on the first request that happens to exercise the broken
8
- * part. The alternative is a typo in `tenantKey` that constructs cleanly, passes
9
- * review, and is discovered by a query that quietly returns another tenant's
10
- * rows.
11
- *
12
- * Two categories of check live here:
13
- *
14
- * - **Identifier safety.** `source`, `tenantKey`, `timeKey`, dimension columns
15
- * and measure fields are all interpolated into SQL as identifiers. They are
16
- * validated once here rather than trusted at every call site that builds a
17
- * predicate from them.
18
- * - **Raw SQL shape.** The `sql` escape hatch on dimensions and measures is an
19
- * expression, never a statement. A value carrying a statement terminator or a
20
- * comment is rejected structurally, so it cannot become a second statement or
21
- * comment out the rest of a clause.
22
- */
1
+ import { validateShiftMeasures } from './shift-measure-validation.js';
23
2
  import { escapeRegExp, isSafeSQLIdentifier, stripSqlLiterals } from '../sql-utils.js';
24
3
  import { validateDerivedMeasures } from './derived-measure-validation.js';
25
- import { isDerivedMeasure } from './dataset-measures.js';
4
+ import { isDerivedMeasure, isWindowMeasure, isShiftMeasure } from './dataset-measures.js';
5
+ import { validateWindowMeasures } from './window-measure-validation.js';
26
6
  import { validateDatasetAgentMetadata } from './semantic-metadata-validation.js';
27
7
  /**
28
8
  * Statement terminators and comment openers.
@@ -139,7 +119,7 @@ function validateMeasures(datasetName, measures, dimensions) {
139
119
  if (typeof definition !== 'object' || definition === null) {
140
120
  fail(datasetName, `measure "${name}" must be created with measure.*().`);
141
121
  }
142
- if (isDerivedMeasure(definition))
122
+ if (isDerivedMeasure(definition) || isWindowMeasure(definition) || isShiftMeasure(definition))
143
123
  continue;
144
124
  if (definition.__type !== 'measure_definition') {
145
125
  fail(datasetName, `measure "${name}" must be created with measure.*().`);
@@ -220,6 +200,8 @@ export function validateDatasetDefinition(name, config) {
220
200
  const dimensions = config.dimensions ?? {};
221
201
  validateDimensions(name, dimensions);
222
202
  const measures = config.measures ?? {};
203
+ validateWindowMeasures(name, config.timeKey, measures);
204
+ validateShiftMeasures(name, config.timeKey, measures);
223
205
  validateDerivedMeasures(name, measures);
224
206
  validateMeasures(name, measures, dimensions);
225
207
  validateLimits(name, config.limits);
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-derived-query.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-derived-query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAA4B,MAAM,aAAa,CAAC;AAClH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,KAAK,cAAc,GAAG,CACpB,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,KAClC,gBAAgB,CAAC;AAEtB,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAE9F;AA6CD,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,CA2CxC;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAgB7B"}
1
+ {"version":3,"file":"dataset-derived-query.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-derived-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,KAAK,cAAc,GAAG,CACpB,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,KAClC,gBAAgB,CAAC;AAEtB,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAE9F;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,CA2CxC;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAgB7B"}
@@ -1,64 +1,21 @@
1
+ import { derivedProjection } from './derived-measure-sql.js';
2
+ import { baseMeasureNames, getBaseMeasure, getDerivedMeasure } from './dataset-measures.js';
1
3
  import { quoteSQLIdentifier } from '../sql-utils.js';
2
4
  import { validateDatasetQueryInput } from './dataset-query-validation.js';
3
5
  import { overfetchLimit } from './pagination.js';
4
6
  import { toDatasetQueryResult } from './dataset-query-result.js';
5
7
  export function hasSelectedDerivedMeasure(ds, query) {
6
- return (query.measures ?? []).some(name => Object.hasOwn(ds.derivedMeasures, name));
7
- }
8
- function expressionSql(expression, uses) {
9
- switch (expression.kind) {
10
- case 'ref': {
11
- if (!Object.hasOwn(uses, expression.name)) {
12
- throw new Error(`Formula references undeclared input "${expression.name}".`);
13
- }
14
- const measureName = uses[expression.name];
15
- return quoteSQLIdentifier(measureName);
16
- }
17
- case 'literal': {
18
- if (expression.value === null)
19
- return 'NULL';
20
- if (typeof expression.value === 'number') {
21
- if (!Number.isFinite(expression.value))
22
- throw new Error('Formula contains a non-finite number.');
23
- return String(expression.value);
24
- }
25
- if (typeof expression.value === 'boolean')
26
- return expression.value ? 'true' : 'false';
27
- return `'${expression.value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
28
- }
29
- case 'binary': {
30
- const operators = { add: '+', subtract: '-', multiply: '*', divide: '/' };
31
- const operator = operators[expression.operator];
32
- if (!operator)
33
- throw new Error('Formula contains an unsupported binary operator.');
34
- return `(${expressionSql(expression.left, uses)} ${operator} ${expressionSql(expression.right, uses)})`;
35
- }
36
- case 'function': {
37
- const args = expression.args.map(arg => expressionSql(arg, uses));
38
- switch (expression.name) {
39
- case 'nullIfZero': return `NULLIF(${args[0]}, 0)`;
40
- case 'coalesce': return `COALESCE(${args.join(', ')})`;
41
- case 'round': return `ROUND(${args.join(', ')})`;
42
- case 'floor': return `FLOOR(${args[0]})`;
43
- case 'ceil': return `CEIL(${args[0]})`;
44
- default: throw new Error('Formula contains an unsupported function.');
45
- }
46
- }
47
- }
48
- }
49
- function derivedProjection(name, definition) {
50
- const aliases = Object.fromEntries(Object.keys(definition.uses).map(alias => [alias, alias]));
51
- return `${expressionSql(definition.formula(aliases).expression, definition.uses)} AS ${quoteSQLIdentifier(name)}`;
8
+ return (query.measures ?? []).some(name => getDerivedMeasure(ds.measures, name) !== undefined);
52
9
  }
53
10
  export function buildDerivedDatasetSql(ds, query, options, buildBaseQuery) {
54
11
  const validation = validateDatasetQueryInput(ds, query, options.context);
55
12
  if (!validation.valid) {
56
13
  throw new Error(`Invalid dataset query: ${validation.errors.join('; ')}`);
57
14
  }
58
- const selected = query.measures ?? Object.keys(ds.measures);
59
- const baseMeasures = new Set(selected.filter(name => Object.hasOwn(ds.measures, name)));
15
+ const selected = query.measures ?? baseMeasureNames(ds.measures);
16
+ const baseMeasures = new Set(selected.filter(name => getBaseMeasure(ds.measures, name) !== undefined));
60
17
  for (const name of selected) {
61
- const derived = Object.hasOwn(ds.derivedMeasures, name) ? ds.derivedMeasures[name] : undefined;
18
+ const derived = getDerivedMeasure(ds.measures, name);
62
19
  if (derived)
63
20
  Object.values(derived.uses).forEach(base => baseMeasures.add(base));
64
21
  }
@@ -76,7 +33,7 @@ export function buildDerivedDatasetSql(ds, query, options, buildBaseQuery) {
76
33
  for (const dimension of query.dimensions ?? [])
77
34
  projections.push(quoteSQLIdentifier(dimension));
78
35
  for (const name of selected) {
79
- const derived = Object.hasOwn(ds.derivedMeasures, name) ? ds.derivedMeasures[name] : undefined;
36
+ const derived = getDerivedMeasure(ds.measures, name);
80
37
  projections.push(derived
81
38
  ? derivedProjection(name, derived)
82
39
  : quoteSQLIdentifier(name));
@@ -1,10 +1,17 @@
1
- import type { BaseMeasures, DatasetMeasureDefinition, DerivedMeasureDefinition, DerivedMeasures, MeasureDefinition } from '../types.js';
2
- export declare function isDerivedMeasure(definition: DatasetMeasureDefinition): definition is DerivedMeasureDefinition;
3
- export declare function isBaseMeasure(definition: DatasetMeasureDefinition): definition is MeasureDefinition;
1
+ import type { DatasetMeasureDefinition, DerivedMeasureDefinition, DerivedMeasures, MeasureDefinition, WindowMeasureDefinition, ShiftMeasureDefinition } from '../types.js';
2
+ export declare function isDerivedMeasure(definition: DatasetMeasureDefinition | null | undefined): definition is DerivedMeasureDefinition;
3
+ export declare function isBaseMeasure(definition: DatasetMeasureDefinition | null | undefined): definition is MeasureDefinition;
4
+ export declare function isWindowMeasure(definition: DatasetMeasureDefinition | null | undefined): definition is WindowMeasureDefinition;
5
+ export declare function isShiftMeasure(definition: DatasetMeasureDefinition | null | undefined): definition is ShiftMeasureDefinition;
4
6
  export declare function baseMeasureNames(measures: Record<string, DatasetMeasureDefinition>): string[];
5
- export declare function splitDatasetMeasures<TMeasures extends Record<string, DatasetMeasureDefinition>>(measures: TMeasures | undefined): {
6
- base: BaseMeasures<TMeasures>;
7
- derived: DerivedMeasures<TMeasures>;
7
+ export declare function getBaseMeasure(measures: Record<string, DatasetMeasureDefinition>, name: string): MeasureDefinition | undefined;
8
+ export declare function splitDatasetMeasures(measures: Record<string, DatasetMeasureDefinition> | undefined): {
9
+ base: Record<string, MeasureDefinition>;
10
+ derived: Record<string, DerivedMeasureDefinition>;
11
+ windows: Record<string, WindowMeasureDefinition>;
12
+ shifts: Record<string, ShiftMeasureDefinition>;
8
13
  };
14
+ /** Preserve the literal keys and formula aliases of the deprecated typed view. */
15
+ export declare function derivedMeasuresView<TMeasures extends Record<string, DatasetMeasureDefinition>>(measures: TMeasures): DerivedMeasures<TMeasures>;
9
16
  export declare function getDerivedMeasure(measures: Record<string, DatasetMeasureDefinition>, name: string): DerivedMeasureDefinition | undefined;
10
17
  //# sourceMappingURL=dataset-measures.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-measures.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-measures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAErB,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,wBAAwB,GACnC,UAAU,IAAI,wBAAwB,CAExC;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,wBAAwB,GACnC,UAAU,IAAI,iBAAiB,CAEjC;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GAAG,MAAM,EAAE,CAI7F;AAED,wBAAgB,oBAAoB,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC7F,QAAQ,EAAE,SAAS,GAAG,SAAS,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;CAAE,CAMxE;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAClD,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,SAAS,CAItC"}
1
+ {"version":3,"file":"dataset-measures.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-measures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GACtD,UAAU,IAAI,wBAAwB,CAExC;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GACtD,UAAU,IAAI,iBAAiB,CAEjC;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GACtD,UAAU,IAAI,uBAAuB,CAEvC;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,IAAI,sBAAsB,CAE5H;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GAAG,MAAM,EAAE,CAI7F;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAClD,IAAI,EAAE,MAAM,GACX,iBAAiB,GAAG,SAAS,CAI/B;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GAAG,SAAS,GAC7D;IACD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAChD,CAQA;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAC5F,QAAQ,EAAE,SAAS,GAClB,eAAe,CAAC,SAAS,CAAC,CAK5B;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,EAClD,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,SAAS,CAItC"}
@@ -1,21 +1,42 @@
1
1
  export function isDerivedMeasure(definition) {
2
- return definition.__type === 'derived_measure_definition';
2
+ return definition?.__type === 'derived_measure_definition';
3
3
  }
4
4
  export function isBaseMeasure(definition) {
5
- return definition.__type === 'measure_definition';
5
+ return definition?.__type === 'measure_definition';
6
+ }
7
+ export function isWindowMeasure(definition) {
8
+ return definition?.__type === 'window_measure_definition';
9
+ }
10
+ export function isShiftMeasure(definition) {
11
+ return definition?.__type === 'shift_measure_definition';
6
12
  }
7
13
  export function baseMeasureNames(measures) {
8
14
  return Object.entries(measures)
9
15
  .filter(([, definition]) => isBaseMeasure(definition))
10
16
  .map(([name]) => name);
11
17
  }
18
+ export function getBaseMeasure(measures, name) {
19
+ if (!Object.hasOwn(measures, name))
20
+ return undefined;
21
+ const definition = measures[name];
22
+ return isBaseMeasure(definition) ? definition : undefined;
23
+ }
12
24
  export function splitDatasetMeasures(measures) {
13
25
  const entries = Object.entries(measures ?? {});
14
26
  return {
15
- base: Object.fromEntries(entries.filter(([, definition]) => isBaseMeasure(definition))),
16
- derived: Object.fromEntries(entries.filter(([, definition]) => isDerivedMeasure(definition))),
27
+ base: Object.fromEntries(entries.filter((entry) => isBaseMeasure(entry[1]))),
28
+ derived: Object.fromEntries(entries.filter((entry) => isDerivedMeasure(entry[1]))),
29
+ shifts: Object.fromEntries(entries.filter((entry) => isShiftMeasure(entry[1]))),
30
+ windows: Object.fromEntries(entries.filter((entry) => isWindowMeasure(entry[1]))),
17
31
  };
18
32
  }
33
+ /** Preserve the literal keys and formula aliases of the deprecated typed view. */
34
+ export function derivedMeasuresView(measures) {
35
+ // TypeScript narrows each entry's value, but cannot reconstruct the mapped
36
+ // subset of generic keys after Object.fromEntries. The filtering above
37
+ // establishes that correspondence; preserve it at this single boundary.
38
+ return splitDatasetMeasures(measures).derived;
39
+ }
19
40
  export function getDerivedMeasure(measures, name) {
20
41
  if (!Object.hasOwn(measures, name))
21
42
  return undefined;
@@ -1,6 +1,6 @@
1
- import type { AggregationSpec, BaseMetricConfig, BaseMetricRef, DatasetInstance, DerivedMetricConfig, DerivedMetricRef, DerivedMetricSpec, DimensionDefinition, MeasureDefinition, MetricRef, RelationshipDefinition, SemanticMetadata } from '../types.js';
1
+ import type { AggregationSpec, BaseMetricConfig, BaseMetricRef, DatasetInstance, DerivedMetricConfig, DerivedMetricRef, DerivedMetricSpec, DimensionDefinition, DatasetMeasureDefinition, MeasureDefinition, MetricRef, RelationshipDefinition, SemanticMetadata } from '../types.js';
2
2
  type AnyDimensions = Record<string, DimensionDefinition>;
3
- type AnyMeasures = Record<string, MeasureDefinition>;
3
+ type AnyMeasures = Record<string, DatasetMeasureDefinition>;
4
4
  type AnyRelationships = Record<string, RelationshipDefinition>;
5
5
  export declare function isDerivedMetricConfig<TMeasures extends Record<string, MeasureDefinition>, TDatasetName extends string>(config: BaseMetricConfig<TMeasures> | DerivedMetricConfig<TDatasetName>): config is DerivedMetricConfig<TDatasetName>;
6
6
  export declare function createMetricRef<TDatasetName extends string, TMetricName extends string, TSpec extends AggregationSpec | DerivedMetricSpec<TDatasetName>, TDataset extends DatasetInstance<AnyDimensions, AnyMeasures, AnyRelationships, TDatasetName>>(ds: TDataset, name: TMetricName, spec: TSpec, label?: string, description?: string, metadata?: SemanticMetadata): MetricRef<TDatasetName, TMetricName, TSpec, TDataset>;
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-metric-ref.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-metric-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,iBAAiB,EACjB,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAKrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CACnC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnD,YAAY,SAAS,MAAM,EAE3B,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,GACtE,MAAM,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAE7C;AAED,wBAAgB,eAAe,CAC7B,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,EAC1B,KAAK,SAAS,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC,EAC/D,QAAQ,SAAS,eAAe,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,CAAC,EAE5F,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,GAAE,gBAAqB,GAC9B,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAsCvD;AAED,wBAAgB,uBAAuB,CAAC,YAAY,SAAS,MAAM,EACjE,MAAM,EAAE,mBAAmB,CAAC,YAAY,CAAC,GACxC,iBAAiB,CAAC,YAAY,CAAC,CAMjC;AAED,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"dataset-metric-ref.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-metric-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,wBAAwB,EACxB,iBAAiB,EACjB,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAKrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAC5D,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CACnC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnD,YAAY,SAAS,MAAM,EAE3B,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,GACtE,MAAM,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAE7C;AAED,wBAAgB,eAAe,CAC7B,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,EAC1B,KAAK,SAAS,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC,EAC/D,QAAQ,SAAS,eAAe,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,CAAC,EAE5F,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,GAAE,gBAAqB,GAC9B,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAsCvD;AAED,wBAAgB,uBAAuB,CAAC,YAAY,SAAS,MAAM,EACjE,MAAM,EAAE,mBAAmB,CAAC,YAAY,CAAC,GACxC,iBAAiB,CAAC,YAAY,CAAC,CAMjC;AAED,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-query-result.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAKrB,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,OAAO,EAAE;IACP,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,GACA,kBAAkB,CAgBpB"}
1
+ {"version":3,"file":"dataset-query-result.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-result.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAKrB,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,OAAO,EAAE;IACP,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,GACA,kBAAkB,CAgBpB"}
@@ -1,10 +1,11 @@
1
+ import { baseMeasureNames } from './dataset-measures.js';
1
2
  import { applyPagination } from './pagination.js';
2
3
  import { serializeSemanticMeasureValues } from './semantic-result-serialization.js';
3
4
  import { getRuntimeTenantId } from './tenant-runtime.js';
4
5
  /** Apply the same result contract to builder-backed and derived dataset queries. */
5
6
  export function toDatasetQueryResult(rows, options) {
6
7
  const { dataset, query, sql, timingMs, context } = options;
7
- const selectedMeasures = query.measures ?? Object.keys(dataset.measures);
8
+ const selectedMeasures = query.measures ?? baseMeasureNames(dataset.measures);
8
9
  const { data, pagination } = applyPagination(rows, query.limit, query.offset);
9
10
  const serializedData = serializeSemanticMeasureValues(data, selectedMeasures);
10
11
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAgB9E,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAiKlB"}
1
+ {"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAiB9E,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAmKlB"}
@@ -1,15 +1,18 @@
1
+ import { queryTimezoneErrors } from './query-timezone.js';
2
+ import { baseMeasureNames } from './dataset-measures.js';
1
3
  import { validateFilterValue } from '../validation.js';
2
4
  import { unsupportedTimeGrainError } from './dataset-time-grains.js';
3
5
  import { getRuntimeTenantPredicate, validateTenantRuntime, } from './tenant-runtime.js';
4
6
  import { isQualifiedField, resolveQualifiedField, } from './relationship-fields.js';
5
7
  import { validateQualifiedFilter, validateRelationshipTenantRuntime, } from './relationship-validation.js';
6
8
  import { segmentSelectionErrors } from './segments.js';
9
+ import { analyzeTimeMeasureAxis } from './time-measure-axis.js';
7
10
  export function validateDatasetQueryInput(ds, query, context) {
8
- const errors = [];
11
+ const errors = queryTimezoneErrors(query.timezone);
9
12
  const dimensionNames = Object.keys(ds.dimensions);
10
- const measureNames = [...Object.keys(ds.measures), ...Object.keys(ds.derivedMeasures ?? {})];
13
+ const measureNames = Object.keys(ds.measures);
11
14
  const selectedDimensions = query.dimensions ?? [];
12
- const selectedMeasures = query.measures ?? Object.keys(ds.measures);
15
+ const selectedMeasures = query.measures ?? baseMeasureNames(ds.measures);
13
16
  const filterNames = Object.keys(ds.filters);
14
17
  const orderableFields = new Set([
15
18
  ...selectedDimensions,
@@ -114,6 +117,7 @@ export function validateDatasetQueryInput(ds, query, context) {
114
117
  if (query.by && !ds.timeKey) {
115
118
  errors.push(`Cannot use "by" grain — dataset "${ds.name}" has no timeKey.`);
116
119
  }
120
+ errors.push(...analyzeTimeMeasureAxis(ds, query).errors);
117
121
  errors.push(...segmentSelectionErrors(ds, query.segments));
118
122
  const grainError = query.by && ds.timeKey ? unsupportedTimeGrainError(ds, query.by) : undefined;
119
123
  if (grainError) {
@@ -0,0 +1,3 @@
1
+ import type { DerivedMeasureDefinition } from '../types.js';
2
+ export declare function derivedProjection(name: string, definition: DerivedMeasureDefinition): string;
3
+ //# sourceMappingURL=derived-measure-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derived-measure-sql.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAyC5D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,wBAAwB,GAAG,MAAM,CAG5F"}
@@ -0,0 +1,46 @@
1
+ import { quoteSQLIdentifier } from '../sql-utils.js';
2
+ function expressionSql(expression, uses) {
3
+ switch (expression.kind) {
4
+ case 'ref': {
5
+ if (!Object.hasOwn(uses, expression.name)) {
6
+ throw new Error(`Formula references undeclared input "${expression.name}".`);
7
+ }
8
+ const measureName = uses[expression.name];
9
+ return quoteSQLIdentifier(measureName);
10
+ }
11
+ case 'literal': {
12
+ if (expression.value === null)
13
+ return 'NULL';
14
+ if (typeof expression.value === 'number') {
15
+ if (!Number.isFinite(expression.value))
16
+ throw new Error('Formula contains a non-finite number.');
17
+ return String(expression.value);
18
+ }
19
+ if (typeof expression.value === 'boolean')
20
+ return expression.value ? 'true' : 'false';
21
+ return `'${expression.value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
22
+ }
23
+ case 'binary': {
24
+ const operators = { add: '+', subtract: '-', multiply: '*', divide: '/' };
25
+ const operator = operators[expression.operator];
26
+ if (!operator)
27
+ throw new Error('Formula contains an unsupported binary operator.');
28
+ return `(${expressionSql(expression.left, uses)} ${operator} ${expressionSql(expression.right, uses)})`;
29
+ }
30
+ case 'function': {
31
+ const args = expression.args.map(arg => expressionSql(arg, uses));
32
+ switch (expression.name) {
33
+ case 'nullIfZero': return `NULLIF(${args[0]}, 0)`;
34
+ case 'coalesce': return `COALESCE(${args.join(', ')})`;
35
+ case 'round': return `ROUND(${args.join(', ')})`;
36
+ case 'floor': return `FLOOR(${args[0]})`;
37
+ case 'ceil': return `CEIL(${args[0]})`;
38
+ default: throw new Error('Formula contains an unsupported function.');
39
+ }
40
+ }
41
+ }
42
+ }
43
+ export function derivedProjection(name, definition) {
44
+ const aliases = Object.fromEntries(Object.keys(definition.uses).map(alias => [alias, alias]));
45
+ return `${expressionSql(definition.formula(aliases).expression, definition.uses)} AS ${quoteSQLIdentifier(name)}`;
46
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"derived-measure-validation.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EAIzB,MAAM,aAAa,CAAC;AAmHrB,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GACjD,IAAI,CAqDN"}
1
+ {"version":3,"file":"derived-measure-validation.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,aAAa,CAAC;AAmHrB,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GACjD,IAAI,CAmDN"}
@@ -1,5 +1,5 @@
1
1
  import { isSafeSQLIdentifier } from '../sql-utils.js';
2
- import { isBaseMeasure, isDerivedMeasure } from './dataset-measures.js';
2
+ import { splitDatasetMeasures } from './dataset-measures.js';
3
3
  function fail(datasetName, measureName, detail) {
4
4
  throw new Error(`Invalid dataset "${datasetName}": derived measure "${measureName}" ${detail}`);
5
5
  }
@@ -90,8 +90,8 @@ function assertAcyclic(datasetName, derivedMeasures) {
90
90
  Object.keys(derivedMeasures).forEach(visit);
91
91
  }
92
92
  export function validateDerivedMeasures(datasetName, measures) {
93
- const baseMeasures = Object.fromEntries(Object.entries(measures).filter(([, definition]) => definition && isBaseMeasure(definition)));
94
- const derivedMeasures = Object.fromEntries(Object.entries(measures).filter(([, definition]) => definition && isDerivedMeasure(definition)));
93
+ const { base: baseMeasures, derived: derivedMeasures, windows, shifts } = splitDatasetMeasures(measures);
94
+ const timeMeasureNames = new Set([...Object.keys(windows), ...Object.keys(shifts)]);
95
95
  for (const [measureName, definition] of Object.entries(derivedMeasures)) {
96
96
  if (!isSafeSQLIdentifier(measureName)) {
97
97
  fail(datasetName, measureName, 'name is not a safe identifier.');
@@ -107,7 +107,7 @@ export function validateDerivedMeasures(datasetName, measures) {
107
107
  for (const [measureName, definition] of Object.entries(derivedMeasures)) {
108
108
  const uses = Object.entries(definition.uses);
109
109
  if (uses.length === 0)
110
- fail(datasetName, measureName, 'must reference at least one base measure.');
110
+ fail(datasetName, measureName, 'must reference at least one base, window, or shift measure.');
111
111
  if (typeof definition.formula !== 'function')
112
112
  fail(datasetName, measureName, 'must define a formula function.');
113
113
  for (const [alias, dependencyName] of uses) {
@@ -119,8 +119,9 @@ export function validateDerivedMeasures(datasetName, measures) {
119
119
  if (Object.hasOwn(derivedMeasures, dependencyName)) {
120
120
  fail(datasetName, measureName, `references derived measure "${dependencyName}"; v1 inputs must be base measures.`);
121
121
  }
122
- if (!Object.hasOwn(baseMeasures, dependencyName))
122
+ if (!Object.hasOwn(baseMeasures, dependencyName) && !timeMeasureNames.has(dependencyName)) {
123
123
  fail(datasetName, measureName, `references missing measure "${dependencyName}".`);
124
+ }
124
125
  }
125
126
  const references = new Set();
126
127
  collectReferences(datasetName, measureName, evaluateFormula(datasetName, measureName, definition).expression, references, { nodes: 0 });
@@ -0,0 +1,4 @@
1
+ import type { DatasetMeasureDefinition, TimeGrain } from '../types.js';
2
+ /** Intersect time-measure requirements with the dataset's declared grains. */
3
+ export declare function measureSupportedGrains(measures: Readonly<Record<string, DatasetMeasureDefinition>>, name: string, grains: readonly TimeGrain[]): TimeGrain[];
4
+ //# sourceMappingURL=measure-time-grains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure-time-grains.d.ts","sourceRoot":"","sources":["../../src/utils/measure-time-grains.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIvE,8EAA8E;AAC9E,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,SAAS,EAAE,GACvG,SAAS,EAAE,CAUb"}
@@ -0,0 +1,16 @@
1
+ import { getDerivedMeasure, isShiftMeasure, isWindowMeasure } from './dataset-measures.js';
2
+ import { intervalBuckets, windowGrainErrors } from './time-axis-intervals.js';
3
+ /** Intersect time-measure requirements with the dataset's declared grains. */
4
+ export function measureSupportedGrains(measures, name, grains) {
5
+ const definition = measures[name];
6
+ if (isShiftMeasure(definition)) {
7
+ return grains.filter(grain => intervalBuckets(definition.interval.amount, definition.interval.unit, grain) !== undefined);
8
+ }
9
+ if (isWindowMeasure(definition))
10
+ return grains.filter(grain => !windowGrainErrors(name, definition, grain).length);
11
+ const derived = getDerivedMeasure(measures, name);
12
+ if (!derived)
13
+ return [...grains];
14
+ const inputs = Object.values(derived.uses).map(input => measureSupportedGrains(measures, input, grains));
15
+ return grains.filter(grain => inputs.every(supported => supported.includes(grain)));
16
+ }
@@ -0,0 +1,9 @@
1
+ import type { AnyDatasetInstance } from '../types.js';
2
+ /** Named zones only: reject fixed offsets, invalid zones, and SQL fragments. */
3
+ export declare function isQueryTimezone(value: unknown): value is string;
4
+ export declare function queryTimezoneErrors(timezone: unknown): string[];
5
+ /** Conversion preserves timestamp instants and interprets Date values as local dates. */
6
+ export declare function queryTimeSql(expression: string, timezone: string | undefined): string;
7
+ /** Apply the query zone to time-key predicates, including semantic filter aliases. */
8
+ export declare function queryTimeFilterSql(ds: AnyDatasetInstance, field: string, expression: string, timezone: string | undefined): string;
9
+ //# sourceMappingURL=query-timezone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-timezone.d.ts","sourceRoot":"","sources":["../../src/utils/query-timezone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAS/D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAG/D;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMrF;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GACtF,MAAM,CAMR"}
@@ -0,0 +1,35 @@
1
+ /** Named zones only: reject fixed offsets, invalid zones, and SQL fragments. */
2
+ export function isQueryTimezone(value) {
3
+ if (typeof value !== 'string' || value.length > 100
4
+ || !/^[A-Za-z][A-Za-z0-9._+-]*(?:\/[A-Za-z0-9._+-]+)*$/.test(value))
5
+ return false;
6
+ try {
7
+ new Intl.DateTimeFormat('en', { timeZone: value });
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ export function queryTimezoneErrors(timezone) {
15
+ return timezone === undefined || isQueryTimezone(timezone)
16
+ ? [] : ['Invalid timezone: use an IANA name such as UTC or America/New_York.'];
17
+ }
18
+ /** Conversion preserves timestamp instants and interprets Date values as local dates. */
19
+ export function queryTimeSql(expression, timezone) {
20
+ timezone ??= 'UTC';
21
+ const errors = queryTimezoneErrors(timezone);
22
+ if (errors.length)
23
+ throw new Error(errors[0]);
24
+ const name = new Intl.DateTimeFormat('en', { timeZone: timezone }).resolvedOptions().timeZone;
25
+ return `toDateTime64(${expression}, 9, '${name}')`;
26
+ }
27
+ /** Apply the query zone to time-key predicates, including semantic filter aliases. */
28
+ export function queryTimeFilterSql(ds, field, expression, timezone) {
29
+ if (!ds.timeKey || field.includes('.'))
30
+ return expression;
31
+ const resolved = ds.filters[field]?.field ?? field;
32
+ const column = ds.dimensions[resolved]?.column ?? resolved;
33
+ const timeColumn = ds.dimensions[ds.timeKey]?.column ?? ds.timeKey;
34
+ return column === timeColumn ? queryTimeSql(expression, timezone) : expression;
35
+ }
@@ -34,7 +34,7 @@ export function validateSemanticMetadata(datasetName, location, metadata) {
34
34
  validateText(datasetName, `${location}.${key}[${index}]`, value);
35
35
  }
36
36
  }
37
- for (const key of ['format', 'unit', 'timezone']) {
37
+ for (const key of ['format', 'unit']) {
38
38
  const value = metadata[key];
39
39
  if (value !== undefined)
40
40
  validateText(datasetName, `${location}.${key}`, value);
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAUrF"}
1
+ {"version":3,"file":"semantic-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAYrF"}
@@ -1,13 +1,15 @@
1
1
  import { uniqueSorted } from './canonical-json.js';
2
2
  /** Copy agent metadata without carrying unrelated config fields into public contracts. */
3
3
  export function snapshotSemanticMetadata(metadata) {
4
+ if ('timezone' in metadata) {
5
+ throw new Error('Set timezone on createDatasetClient or the query, not semantic metadata.');
6
+ }
4
7
  return {
5
8
  ...(metadata.examples !== undefined ? { examples: uniqueSorted(metadata.examples) } : {}),
6
9
  ...(metadata.synonyms !== undefined ? { synonyms: uniqueSorted(metadata.synonyms) } : {}),
7
10
  ...(metadata.format !== undefined ? { format: metadata.format } : {}),
8
11
  ...(metadata.unit !== undefined ? { unit: metadata.unit } : {}),
9
12
  ...(metadata.currency !== undefined ? { currency: metadata.currency } : {}),
10
- ...(metadata.timezone !== undefined ? { timezone: metadata.timezone } : {}),
11
13
  ...(metadata.sensitivity !== undefined ? { sensitivity: metadata.sensitivity } : {}),
12
14
  };
13
15
  }
@@ -0,0 +1,4 @@
1
+ import type { MeasureTimeInterval, ShiftMeasureDefinition } from '../types.js';
2
+ export type ShiftMeasureOptions = Pick<ShiftMeasureDefinition, 'label' | 'description' | 'examples' | 'synonyms' | 'format' | 'unit' | 'currency' | 'sensitivity'>;
3
+ export declare function createShiftMeasure<const TMeasureName extends string>(baseMeasure: TMeasureName, interval: MeasureTimeInterval, options?: ShiftMeasureOptions): ShiftMeasureDefinition<TMeasureName>;
4
+ //# sourceMappingURL=shift-measure-definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift-measure-definition.d.ts","sourceRoot":"","sources":["../../src/utils/shift-measure-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG/E,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,sBAAsB,EAC3D,OAAO,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC,CAAC;AAEtG,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,YAAY,SAAS,MAAM,EAClE,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,sBAAsB,CAAC,YAAY,CAAC,CAOtC"}
@@ -0,0 +1,9 @@
1
+ import { snapshotSemanticMetadata } from './semantic-metadata.js';
2
+ export function createShiftMeasure(baseMeasure, interval, options) {
3
+ return {
4
+ __type: 'shift_measure_definition', measure: baseMeasure, interval: { ...interval },
5
+ ...snapshotSemanticMetadata(options ?? {}),
6
+ ...(options?.label === undefined ? {} : { label: options.label }),
7
+ ...(options?.description === undefined ? {} : { description: options.description }),
8
+ };
9
+ }
@@ -0,0 +1,18 @@
1
+ import type { ShiftMeasureDefinition, TimeGrain } from '../types.js';
2
+ import type { TimeMeasureAxis } from './time-measure-axis.js';
3
+ /** Preserve the selected portion of a bucket and both authored endpoint operators. */
4
+ export declare function shiftRangeSql(shift: ShiftMeasureDefinition, axis: TimeMeasureAxis, period?: string): {
5
+ lower: string;
6
+ upper: string;
7
+ lowerInclusive: string;
8
+ upperInclusive: string;
9
+ };
10
+ export declare function shiftRangePredicateSql(time: string, range: ReturnType<typeof shiftRangeSql>): string;
11
+ /** Check the shifted scan independently of whether it contains source rows. */
12
+ export declare function shiftCalendarGuardSql(shift: ShiftMeasureDefinition, grain: TimeGrain, axisGuard: string): string;
13
+ /** Map output buckets to their earlier half-open ranges, retaining calendar arithmetic. */
14
+ export declare function shiftBucketCtes(shift: ShiftMeasureDefinition, axis: TimeMeasureAxis, index: number): {
15
+ ctes: string[];
16
+ rowsSql: string;
17
+ };
18
+ //# sourceMappingURL=shift-measure-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift-measure-sql.d.ts","sourceRoot":"","sources":["../../src/utils/shift-measure-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;EAQlG;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,GAAG,MAAM,CAGpG;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOhH;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAkBxI"}