@malloydata/malloy 0.0.395 → 0.0.397

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 (61) hide show
  1. package/dist/annotation.d.ts +85 -1
  2. package/dist/annotation.js +133 -47
  3. package/dist/api/core.js +2 -7
  4. package/dist/api/foundation/compile.d.ts +7 -6
  5. package/dist/api/foundation/compile.js +22 -6
  6. package/dist/api/foundation/core.d.ts +10 -0
  7. package/dist/api/foundation/core.js +32 -9
  8. package/dist/api/foundation/runtime.d.ts +85 -5
  9. package/dist/api/foundation/runtime.js +184 -14
  10. package/dist/api/foundation/types.d.ts +2 -0
  11. package/dist/lang/ast/expressions/expr-func.js +30 -11
  12. package/dist/lang/ast/expressions/expr-given.js +1 -0
  13. package/dist/lang/ast/field-space/reference-field.js +1 -1
  14. package/dist/lang/ast/source-elements/sql-source.js +4 -0
  15. package/dist/lang/ast/source-elements/table-source.js +4 -0
  16. package/dist/lang/ast/statements/define-given.d.ts +1 -0
  17. package/dist/lang/ast/statements/define-given.js +7 -0
  18. package/dist/lang/ast/statements/import-statement.js +4 -0
  19. package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
  20. package/dist/lang/ast/types/annotation-elements.js +10 -3
  21. package/dist/lang/ast/types/malloy-element.d.ts +1 -0
  22. package/dist/lang/ast/types/malloy-element.js +4 -0
  23. package/dist/lang/composite-source-utils.js +1 -1
  24. package/dist/lang/malloy-to-ast.d.ts +9 -1
  25. package/dist/lang/malloy-to-ast.js +37 -11
  26. package/dist/lang/malloy-to-stable-query.js +3 -3
  27. package/dist/lang/parse-log.d.ts +7 -1
  28. package/dist/lang/parse-log.js +12 -0
  29. package/dist/lang/parse-malloy.d.ts +4 -1
  30. package/dist/lang/parse-malloy.js +26 -4
  31. package/dist/lang/parse-tree-walkers/model-annotation-walker.js +3 -11
  32. package/dist/lang/parse-utils.d.ts +10 -2
  33. package/dist/lang/parse-utils.js +89 -29
  34. package/dist/lang/test/test-translator.d.ts +19 -5
  35. package/dist/lang/test/test-translator.js +15 -12
  36. package/dist/lang/zone.d.ts +2 -0
  37. package/dist/lang/zone.js +10 -0
  38. package/dist/model/constant_expression_compiler.js +14 -5
  39. package/dist/model/expression_compiler.js +19 -17
  40. package/dist/model/field_instance.js +7 -3
  41. package/dist/model/given_binding.js +26 -21
  42. package/dist/model/index.d.ts +1 -0
  43. package/dist/model/index.js +3 -1
  44. package/dist/model/malloy_compile_error.d.ts +13 -0
  45. package/dist/model/malloy_compile_error.js +23 -0
  46. package/dist/model/malloy_types.d.ts +9 -0
  47. package/dist/model/persist_utils.js +1 -1
  48. package/dist/model/query_model_impl.js +2 -1
  49. package/dist/model/query_node.d.ts +5 -5
  50. package/dist/model/query_node.js +22 -17
  51. package/dist/model/query_query.js +23 -11
  52. package/dist/model/sql_compiled.js +6 -3
  53. package/dist/prefix.d.ts +51 -0
  54. package/dist/prefix.js +99 -0
  55. package/dist/taggable.d.ts +17 -1
  56. package/dist/test/resultMatchers.js +2 -1
  57. package/dist/to_stable.d.ts +7 -1
  58. package/dist/to_stable.js +13 -16
  59. package/dist/version.d.ts +1 -1
  60. package/dist/version.js +1 -1
  61. package/package.json +4 -4
@@ -29,6 +29,7 @@ const field_instance_1 = require("./field_instance");
29
29
  const filter_compilers_1 = require("./filter_compilers");
30
30
  const utils_1 = require("./utils");
31
31
  const query_node_1 = require("./query_node");
32
+ const malloy_compile_error_1 = require("./malloy_compile_error");
32
33
  const NUMERIC_DECIMAL_PRECISION = 9;
33
34
  function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
34
35
  const precision = 9;
@@ -186,7 +187,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
186
187
  return `${expr.kids.e.sql} ${expr.not ? 'NOT IN' : 'IN'} (${oneOf})`;
187
188
  }
188
189
  case 'inGiven': {
189
- const bound = resolveGivenBoundExpr(context, expr.givenRef.id, expr.givenRef.refName);
190
+ const bound = resolveGivenBoundExpr(context, expr.givenRef);
190
191
  // null binding collapses to empty-set semantics — not the SQL
191
192
  // `IN (NULL)` shape, which has confusing NULL-membership rules.
192
193
  if (bound.node === 'null') {
@@ -280,27 +281,27 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
280
281
  filterExpr = argument.value;
281
282
  }
282
283
  else {
283
- throw new Error(`Parameter ${name} was expected to be a filter expression`);
284
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${name}' has no value, but a filter expression is required here.`, 'compiler-filter-parameter-no-value', filterExpr.at);
284
285
  }
285
286
  }
286
287
  if (filterExpr.node === 'given') {
287
- const id = filterExpr.id;
288
- const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(id);
288
+ const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(filterExpr.id);
289
289
  if (supplied !== undefined) {
290
290
  filterExpr = supplied;
291
291
  }
292
292
  else {
293
- const decl = context.getModel().givens[id];
293
+ const decl = context.getModel().givens[filterExpr.id];
294
294
  if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined) {
295
295
  filterExpr = decl.default;
296
296
  }
297
297
  else {
298
- throw new Error(unsatisfiedGivenMessage(filterExpr.refName));
298
+ throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(filterExpr.refName), 'compiler-given-no-value', filterExpr.at);
299
299
  }
300
300
  }
301
301
  }
302
302
  if (filterExpr.node !== 'filterLiteral') {
303
- throw new Error('Can only use filter expression literals or parameters as filter expressions');
303
+ throw new malloy_compile_error_1.MalloyCompileError("Filter context requires a filter-expression literal (e.g., `f'...'`) " +
304
+ `or a parameter that resolves to one; got node '${filterExpr.node}'.`, 'compiler-filter-not-literal', undefined);
304
305
  }
305
306
  const filterSrc = filterExpr.filterSrc;
306
307
  let fParse;
@@ -320,10 +321,11 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
320
321
  fParse = malloy_filter_1.TemporalFilterExpression.parse(filterSrc);
321
322
  break;
322
323
  default:
323
- throw new Error(`unsupported filter type ${filterMatchExpr.dataType}`);
324
+ throw new malloy_compile_error_1.MalloyCompileError(`Filter type '${filterMatchExpr.dataType}' is not supported. ` +
325
+ 'Supported filter types: string, number, boolean, date, timestamp, timestamptz.', 'compiler-filter-type-unsupported', undefined);
324
326
  }
325
327
  if (fParse.log.length > 0) {
326
- throw new Error(`Filter expression parse error: ${fParse.log[0]}`);
328
+ throw new malloy_compile_error_1.MalloyCompileError(`Filter expression parse error: ${fParse.log[0]}.`, 'compiler-filter-parse-error', undefined);
327
329
  }
328
330
  return filter_compilers_1.FilterCompilers.compile(filterMatchExpr.dataType, fParse.parsed, filterMatchExpr.kids.expr.sql || '', context.dialect, qi);
329
331
  }
@@ -581,7 +583,7 @@ function generateFunctionCallExpression(resultSet, context, frag, state) {
581
583
  }
582
584
  function generateFieldFragment(resultSet, context, expr, state) {
583
585
  // find the structDef and return the path to the field...
584
- const fieldRef = context.getFieldByName(expr.path);
586
+ const fieldRef = context.getFieldByName(expr.path, expr.at);
585
587
  if ((0, malloy_types_1.hasExpression)(fieldRef.fieldDef)) {
586
588
  const ret = exprToSQL(resultSet, fieldRef.parent, fieldRef.fieldDef.e, state);
587
589
  return `(${ret})`;
@@ -614,7 +616,7 @@ function generateParameterFragment(resultSet, context, expr, state) {
614
616
  if (argument === null || argument === void 0 ? void 0 : argument.value) {
615
617
  return exprToSQL(resultSet, context, argument.value, state);
616
618
  }
617
- throw new Error(`Can't generate SQL, no value for ${expr.path}`);
619
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${expr.path.join('.')}' has no value supplied.`, 'compiler-parameter-no-value', expr.at);
618
620
  }
619
621
  /**
620
622
  * Resolve a given to the Expr that should stand in for it at SQL emit:
@@ -625,20 +627,20 @@ function generateParameterFragment(resultSet, context, expr, state) {
625
627
  * Shared by `generateGivenFragment` ($NAME directly in an expression)
626
628
  * and the `'inGiven'` SQL-emit case ($ARR in `expr in $ARR`).
627
629
  */
628
- function resolveGivenBoundExpr(context, id, refName) {
630
+ function resolveGivenBoundExpr(context, ref) {
629
631
  var _a, _b;
630
- const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(id);
632
+ const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(ref.id);
631
633
  if (supplied !== undefined)
632
634
  return supplied;
633
- const decl = context.getModel().givens[id];
635
+ const decl = context.getModel().givens[ref.id];
634
636
  if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
635
637
  return decl.default;
636
- throw new Error(unsatisfiedGivenMessage(refName));
638
+ throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(ref.refName), 'compiler-given-no-value', ref.at);
637
639
  }
638
640
  function generateGivenFragment(resultSet, context, expr, state) {
639
641
  // The bound expr may itself be a `$OTHER`-bearing expression; recursive
640
642
  // compile handles default chains.
641
- const bound = resolveGivenBoundExpr(context, expr.id, expr.refName);
643
+ const bound = resolveGivenBoundExpr(context, expr);
642
644
  return exprToSQL(resultSet, context, bound, state);
643
645
  }
644
646
  function unsatisfiedGivenMessage(refName) {
@@ -663,7 +665,7 @@ function generateDimFragment(resultSet, context, expr, state) {
663
665
  }
664
666
  function generateUngroupedFragment(resultSet, context, expr, state) {
665
667
  if (state.totalGroupSet !== -1) {
666
- throw new Error('Already in ALL. Cannot nest within an all calcuation.');
668
+ throw new malloy_compile_error_1.MalloyCompileError("Cannot nest 'all()' or 'exclude()' inside another 'all()' calculation.", 'compiler-nested-ungroup', undefined);
667
669
  }
668
670
  let totalGroupSet;
669
671
  let ungroupSet;
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.FieldInstanceResultRoot = exports.FieldInstanceResult = exports.FieldInstanceField = void 0;
8
8
  exports.sqlFullChildReference = sqlFullChildReference;
9
+ const malloy_compile_error_1 = require("./malloy_compile_error");
9
10
  const malloy_types_1 = require("./malloy_types");
10
11
  const utils_1 = require("./utils");
11
12
  const join_instance_1 = require("./join_instance");
@@ -139,7 +140,8 @@ class FieldInstanceResult {
139
140
  const fi = this.allFields.get(as);
140
141
  if (fi) {
141
142
  if (fi.type === 'query') {
142
- throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
143
+ throw new malloy_compile_error_1.MalloyCompileError(`Field '${field.fieldDef.name}' is already defined as a nested view; ` +
144
+ 'cannot also use it as a scalar field in this output.', 'compiler-field-redefined-as-struct', field.fieldDef.location);
143
145
  }
144
146
  const fif = fi;
145
147
  if (fif.fieldUsage.type === 'result') {
@@ -148,7 +150,8 @@ class FieldInstanceResult {
148
150
  return;
149
151
  }
150
152
  else {
151
- throw new Error(`Ambiguous output field name '${field.fieldDef.name}'.`);
153
+ throw new malloy_compile_error_1.MalloyCompileError(`Output field name '${field.fieldDef.name}' is ambiguous — ` +
154
+ 'defined more than once at the query output level.', 'compiler-ambiguous-output-name', field.fieldDef.location);
152
155
  }
153
156
  }
154
157
  }
@@ -373,7 +376,8 @@ class FieldInstanceResult {
373
376
  // verify that all names specified are available in the current scope.
374
377
  for (const fieldName of (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || []) {
375
378
  if (inScopeFieldNames.indexOf(fieldName) === -1) {
376
- throw new Error(`${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}(): unknown field name "${fieldName}" or name not in scope.`);
379
+ throw new malloy_compile_error_1.MalloyCompileError(`'${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}()' references field '${fieldName}', ` +
380
+ 'which is not defined or not in scope at this nesting level.', 'compiler-ungroup-field-unknown', undefined);
377
381
  }
378
382
  }
379
383
  return ret;
@@ -10,6 +10,7 @@ const luxon_1 = require("luxon");
10
10
  const closest_match_1 = require("../util/closest_match");
11
11
  const inline_expr_1 = require("./inline_expr");
12
12
  const malloy_types_1 = require("./malloy_types");
13
+ const malloy_compile_error_1 = require("./malloy_compile_error");
13
14
  function resolveSuppliedGivens(supplied, modelDef) {
14
15
  var _a;
15
16
  const out = new Map();
@@ -20,9 +21,9 @@ function resolveSuppliedGivens(supplied, modelDef) {
20
21
  // prototype pollution from e.g. an Object.create(...)-derived input.
21
22
  for (const [name, value] of Object.entries(supplied)) {
22
23
  if (value === undefined) {
23
- throw new Error(`givens.${name}: explicit undefined is not a valid value. ` +
24
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${name}: explicit undefined is not a valid value. ` +
24
25
  'Omit the key to defer to declaration default or a lower supply ' +
25
- 'layer; use null for an explicit null value.');
26
+ 'layer; use null for an explicit null value.', 'runtime-given-undefined', undefined);
26
27
  }
27
28
  const entry = modelDef.contents[name];
28
29
  if (!entry || entry.type !== 'given') {
@@ -33,7 +34,8 @@ function resolveSuppliedGivens(supplied, modelDef) {
33
34
  }
34
35
  const suggestion = (0, closest_match_1.closestMatch)(name, surfaceNames);
35
36
  const hint = suggestion ? ` (did you mean '${suggestion}'?)` : '';
36
- throw new Error(`givens: unknown given '${name}'${hint}. Model surfaces [${surfaceNames.join(', ')}]`);
37
+ throw new malloy_compile_error_1.MalloyCompileError(`givens: unknown given '${name}'${hint}. ` +
38
+ `Model surfaces [${surfaceNames.join(', ')}]`, 'runtime-given-unknown', undefined);
37
39
  }
38
40
  const decl = givens[entry.id];
39
41
  if (!decl) {
@@ -83,10 +85,13 @@ function valueToExpr(path, type, value) {
83
85
  if (value === null) {
84
86
  return { node: 'null' };
85
87
  }
88
+ function bad(msg, code = 'runtime-given-bad-value') {
89
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}: ${msg}`, code, undefined);
90
+ }
86
91
  switch (type.type) {
87
92
  case 'string': {
88
93
  if (typeof value !== 'string') {
89
- throw new TypeError(`givens.${path}: expected string, got ${describeJs(value)}`);
94
+ bad(`expected string, got ${describeJs(value)}`);
90
95
  }
91
96
  return { node: 'stringLiteral', literal: value };
92
97
  }
@@ -94,7 +99,7 @@ function valueToExpr(path, type, value) {
94
99
  let lit;
95
100
  if (typeof value === 'number') {
96
101
  if (!Number.isFinite(value)) {
97
- throw new TypeError(`givens.${path}: number must be finite, got ${value}`);
102
+ bad(`number must be finite, got ${value}`);
98
103
  }
99
104
  lit = String(value);
100
105
  }
@@ -103,27 +108,27 @@ function valueToExpr(path, type, value) {
103
108
  }
104
109
  else if (typeof value === 'string') {
105
110
  if (!/^-?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?$/.test(value)) {
106
- throw new TypeError(`givens.${path}: number-as-string must be numeric, got '${value}'`);
111
+ bad(`number-as-string must be numeric, got '${value}'`);
107
112
  }
108
113
  lit = value;
109
114
  }
110
115
  else {
111
- throw new TypeError(`givens.${path}: expected number | bigint | string, got ${describeJs(value)}`);
116
+ bad(`expected number | bigint | string, got ${describeJs(value)}`);
112
117
  }
113
118
  return { node: 'numberLiteral', literal: lit };
114
119
  }
115
120
  case 'boolean': {
116
121
  if (typeof value !== 'boolean') {
117
- throw new TypeError(`givens.${path}: expected boolean, got ${describeJs(value)}`);
122
+ bad(`expected boolean, got ${describeJs(value)}`);
118
123
  }
119
124
  return { node: value ? 'true' : 'false' };
120
125
  }
121
126
  case 'date': {
122
127
  if (typeof value !== 'string') {
123
- throw new TypeError(`givens.${path}: expected ISO date string 'YYYY-MM-DD', got ${describeJs(value)}`);
128
+ bad(`expected ISO date string 'YYYY-MM-DD', got ${describeJs(value)}`);
124
129
  }
125
130
  if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
126
- throw new TypeError(`givens.${path}: date must match 'YYYY-MM-DD', got '${value}'`);
131
+ bad(`date must match 'YYYY-MM-DD', got '${value}'`);
127
132
  }
128
133
  return { node: 'dateLiteral', literal: value, typeDef: { type: 'date' } };
129
134
  }
@@ -133,17 +138,17 @@ function valueToExpr(path, type, value) {
133
138
  // strings (those want 'timestamptz'). Parse the rest with Luxon and
134
139
  // emit canonical "YYYY-MM-DD HH:MM:SS.sss" for the dialect.
135
140
  if (typeof value !== 'string') {
136
- throw new TypeError(`givens.${path}: expected ISO timestamp string (no offset), got ${describeJs(value)}`);
141
+ bad(`expected ISO timestamp string (no offset), got ${describeJs(value)}`);
137
142
  }
138
143
  if (/Z$|[+-]\d{2}:?\d{2}$/.test(value)) {
139
- throw new TypeError(`givens.${path}: 'timestamp' is naive — use 'timestamptz' for offset/zoned values, got '${value}'`);
144
+ bad(`'timestamp' is naive — use 'timestamptz' for offset/zoned values, got '${value}'`);
140
145
  }
141
146
  // ISO uses T-separator; SQL form uses space. Accept both.
142
147
  let dt = luxon_1.DateTime.fromISO(value, { zone: 'utc' });
143
148
  if (!dt.isValid)
144
149
  dt = luxon_1.DateTime.fromSQL(value, { zone: 'utc' });
145
150
  if (!dt.isValid) {
146
- throw new TypeError(`givens.${path}: invalid timestamp value '${value}': ${(_a = dt.invalidReason) !== null && _a !== void 0 ? _a : 'unknown'}`);
151
+ bad(`invalid timestamp value '${value}': ${(_a = dt.invalidReason) !== null && _a !== void 0 ? _a : 'unknown'}`);
147
152
  }
148
153
  return {
149
154
  node: 'timestampLiteral',
@@ -162,10 +167,10 @@ function valueToExpr(path, type, value) {
162
167
  dt = dt.toUTC();
163
168
  }
164
169
  else {
165
- throw new TypeError(`givens.${path}: expected JS Date or ISO timestamptz string, got ${describeJs(value)}`);
170
+ bad(`expected JS Date or ISO timestamptz string, got ${describeJs(value)}`);
166
171
  }
167
172
  if (!dt.isValid) {
168
- throw new TypeError(`givens.${path}: invalid timestamptz value '${value}': ${(_b = dt.invalidReason) !== null && _b !== void 0 ? _b : 'unknown'}`);
173
+ bad(`invalid timestamptz value '${value}': ${(_b = dt.invalidReason) !== null && _b !== void 0 ? _b : 'unknown'}`);
169
174
  }
170
175
  return {
171
176
  node: 'timestamptzLiteral',
@@ -176,13 +181,13 @@ function valueToExpr(path, type, value) {
176
181
  }
177
182
  case 'filter expression': {
178
183
  if (typeof value !== 'string') {
179
- throw new TypeError(`givens.${path}: filter<T> givens require a JS string of Malloy filter source, got ${describeJs(value)}`);
184
+ bad(`filter<T> givens require a JS string of Malloy filter source, got ${describeJs(value)}`);
180
185
  }
181
186
  return { node: 'filterLiteral', filterSrc: value };
182
187
  }
183
188
  case 'array': {
184
189
  if (!Array.isArray(value)) {
185
- throw new TypeError(`givens.${path}: expected array, got ${describeJs(value)}`);
190
+ bad(`expected array, got ${describeJs(value)}`);
186
191
  }
187
192
  // RepeatedRecord (array of records) carries `record_element` as its
188
193
  // element type and the record's schema in `fields`. Each element is
@@ -198,19 +203,19 @@ function valueToExpr(path, type, value) {
198
203
  if (typeof value !== 'object' ||
199
204
  Array.isArray(value) ||
200
205
  value instanceof Date) {
201
- throw new TypeError(`givens.${path}: expected object, got ${describeJs(value)}`);
206
+ bad(`expected object, got ${describeJs(value)}`);
202
207
  }
203
208
  const obj = value;
204
209
  const declared = new Set(type.fields.map(f => f.name));
205
210
  for (const k of Object.keys(obj)) {
206
211
  if (!declared.has(k)) {
207
- throw new TypeError(`givens.${path}.${k}: unexpected key (not in record type [${[...declared].join(', ')}])`);
212
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}.${k}: unexpected key (not in record type [${[...declared].join(', ')}])`, 'runtime-given-record-extra-key', undefined);
208
213
  }
209
214
  }
210
215
  const kids = (0, malloy_types_1.mkSafeRecord)();
211
216
  for (const field of type.fields) {
212
217
  if (!(field.name in obj)) {
213
- throw new TypeError(`givens.${path}.${field.name}: missing required key`);
218
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}.${field.name}: missing required key`, 'runtime-given-record-missing-key', undefined);
214
219
  }
215
220
  kids[field.name] = valueToExpr(`${path}.${field.name}`, malloy_types_1.TD.atomicDef(field), obj[field.name]);
216
221
  }
@@ -219,7 +224,7 @@ function valueToExpr(path, type, value) {
219
224
  case 'json':
220
225
  case 'sql native':
221
226
  case 'error':
222
- throw new Error(`givens.${path}: type '${type.type}' is not bindable`);
227
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}: type '${type.type}' is not bindable as a given value.`, 'runtime-given-type-not-bindable', undefined);
223
228
  default: {
224
229
  // Exhaustiveness: future GivenTypeDef additions will trip this.
225
230
  const _x = type;
@@ -7,4 +7,5 @@ export { getResultStructDefForQuery, getResultStructDefForView, } from './query_
7
7
  export { indent, composeSQLExpr, makeDigest, mkModelDef, pathToKey, typeDefToString, } from './utils';
8
8
  export { constantExprToSQL } from './constant_expression_compiler';
9
9
  export { getCompiledSQL } from './sql_compiled';
10
+ export { MalloyCompileError } from './malloy_compile_error';
10
11
  export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.getCompiledSQL = exports.constantExprToSQL = exports.typeDefToString = exports.pathToKey = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
39
+ exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.getCompiledSQL = exports.constantExprToSQL = exports.typeDefToString = exports.pathToKey = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
40
40
  __exportStar(require("./malloy_types"), exports);
41
41
  const query_node_1 = require("./query_node");
42
42
  Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
@@ -66,6 +66,8 @@ var constant_expression_compiler_1 = require("./constant_expression_compiler");
66
66
  Object.defineProperty(exports, "constantExprToSQL", { enumerable: true, get: function () { return constant_expression_compiler_1.constantExprToSQL; } });
67
67
  var sql_compiled_1 = require("./sql_compiled");
68
68
  Object.defineProperty(exports, "getCompiledSQL", { enumerable: true, get: function () { return sql_compiled_1.getCompiledSQL; } });
69
+ var malloy_compile_error_1 = require("./malloy_compile_error");
70
+ Object.defineProperty(exports, "MalloyCompileError", { enumerable: true, get: function () { return malloy_compile_error_1.MalloyCompileError; } });
69
71
  var source_def_utils_1 = require("./source_def_utils");
70
72
  Object.defineProperty(exports, "mkSourceID", { enumerable: true, get: function () { return source_def_utils_1.mkSourceID; } });
71
73
  Object.defineProperty(exports, "mkBuildID", { enumerable: true, get: function () { return source_def_utils_1.mkBuildID; } });
@@ -0,0 +1,13 @@
1
+ import type { DocumentLocation } from './malloy_types';
2
+ /**
3
+ * Thrown by the SQL compiler (`src/model/`) for user-actionable errors the
4
+ * translator didn't catch. Caught at the materializer boundary and turned
5
+ * into a `LogMessage`. Singular `at` reflects the fail-fast contract — the
6
+ * first such error stops compilation. Invariant violations stay as bare
7
+ * `Error` and surface as `code: 'compiler-bug'` instead.
8
+ */
9
+ export declare class MalloyCompileError extends Error {
10
+ readonly code: string;
11
+ readonly at: DocumentLocation | undefined;
12
+ constructor(message: string, code: string, at: DocumentLocation | undefined);
13
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MalloyCompileError = void 0;
8
+ /**
9
+ * Thrown by the SQL compiler (`src/model/`) for user-actionable errors the
10
+ * translator didn't catch. Caught at the materializer boundary and turned
11
+ * into a `LogMessage`. Singular `at` reflects the fail-fast contract — the
12
+ * first such error stops compilation. Invariant violations stay as bare
13
+ * `Error` and surface as `code: 'compiler-bug'` instead.
14
+ */
15
+ class MalloyCompileError extends Error {
16
+ constructor(message, code, at) {
17
+ super(message);
18
+ this.code = code;
19
+ this.at = at;
20
+ }
21
+ }
22
+ exports.MalloyCompileError = MalloyCompileError;
23
+ //# sourceMappingURL=malloy_compile_error.js.map
@@ -127,11 +127,13 @@ export interface SourceReferenceNode extends ExprLeaf {
127
127
  export interface ParameterNode extends ExprLeaf {
128
128
  node: 'parameter';
129
129
  path: string[];
130
+ at?: DocumentLocation;
130
131
  }
131
132
  export interface GivenRefNode extends ExprLeaf {
132
133
  node: 'given';
133
134
  id: GivenID;
134
135
  refName: string;
136
+ at?: DocumentLocation;
135
137
  }
136
138
  export interface NowNode extends ExprLeaf {
137
139
  node: 'now';
@@ -1136,6 +1138,13 @@ export interface Annotation {
1136
1138
  export interface Note {
1137
1139
  text: string;
1138
1140
  at: DocumentLocation;
1141
+ /**
1142
+ * For block annotations: characters of leading whitespace removed from each
1143
+ * body line by the dedent pass. Used to map payload-parser error columns
1144
+ * back to source (`source_col = indentStripped + parser_col` for body lines).
1145
+ * Omitted for single-line notes and blocks with no common indent.
1146
+ */
1147
+ indentStripped?: number;
1139
1148
  }
1140
1149
  /** Annotations with a uuid to make it easier to stream */
1141
1150
  export interface ModelAnnotation extends Annotation {
@@ -24,7 +24,7 @@ function resolveSource(modelDef, name) {
24
24
  function checkPersistAnnotation(source) {
25
25
  if (!source.annotation)
26
26
  return { persist: false, log: [] };
27
- const { tag, log } = (0, annotation_1.annotationToTag)(source.annotation, { prefix: /^#@ / });
27
+ const { tag, log } = new annotation_1.Annotations(source.annotation).parseAsTag('@');
28
28
  return { persist: tag.has('persist'), log };
29
29
  }
30
30
  /**
@@ -14,6 +14,7 @@ const stage_writer_1 = require("./stage_writer");
14
14
  const dialect_1 = require("../dialect");
15
15
  const query_node_1 = require("./query_node");
16
16
  const row_data_utils_1 = require("../api/row_data_utils");
17
+ const malloy_compile_error_1 = require("./malloy_compile_error");
17
18
  function makeQueryModel(modelDef) {
18
19
  return new QueryModelImpl(modelDef);
19
20
  }
@@ -66,7 +67,7 @@ class QueryModelImpl {
66
67
  if (s) {
67
68
  return s;
68
69
  }
69
- throw new Error(`Struct ${name} not found in model.`);
70
+ throw new malloy_compile_error_1.MalloyCompileError(`Source '${name}' is not defined in this model.`, 'compiler-undefined-source', undefined);
70
71
  }
71
72
  getStructFromRef(structRef, sourceArguments, prepareResultOptions) {
72
73
  prepareResultOptions !== null && prepareResultOptions !== void 0 ? prepareResultOptions : (prepareResultOptions = {});
@@ -1,4 +1,4 @@
1
- import type { FieldDef, BooleanFieldDef, DateFieldDef, StringFieldDef, JSONFieldDef, NumberFieldDef, ATimestampFieldDef, NativeUnsupportedFieldDef, JoinFieldDef, Argument, Given, GivenID, PrepareResultOptions, AtomicFieldDef, BasicAtomicDef, FilterCondition, RefToField, StructDef, TurtleDef, TurtleDefPlusFilters, SourceDef, Query } from './malloy_types';
1
+ import type { FieldDef, BooleanFieldDef, DateFieldDef, DocumentLocation, StringFieldDef, JSONFieldDef, NumberFieldDef, ATimestampFieldDef, NativeUnsupportedFieldDef, JoinFieldDef, Argument, Given, GivenID, PrepareResultOptions, AtomicFieldDef, BasicAtomicDef, FilterCondition, RefToField, StructDef, TurtleDef, TurtleDefPlusFilters, SourceDef, Query } from './malloy_types';
2
2
  import type { EventStream } from '../runtime_types';
3
3
  import type { Tag } from '@malloydata/malloy-tag';
4
4
  import type { Dialect } from '../dialect';
@@ -106,7 +106,7 @@ export declare class QueryStruct {
106
106
  getFullOutputName(): string;
107
107
  unnestWithNumbers(): boolean;
108
108
  getJoinableParent(): QueryStruct;
109
- addFieldToNameMap(as: string, n: QueryField): void;
109
+ addFieldToNameMap(as: string, n: QueryField, at?: DocumentLocation): void;
110
110
  /** the the primary key or throw an error. */
111
111
  getPrimaryKeyField(fieldDef: FieldDef): QueryBasicField;
112
112
  /**
@@ -126,14 +126,14 @@ export declare class QueryStruct {
126
126
  primaryKey(): QueryBasicField | undefined;
127
127
  getChildByName(name: string): QueryField | undefined;
128
128
  /** convert a path into a field reference */
129
- getFieldByName(path: string[]): QueryField;
130
- getQueryFieldByName(name: string[]): QueryField;
129
+ getFieldByName(path: string[], at?: DocumentLocation): QueryField;
130
+ getQueryFieldByName(name: string[], at?: DocumentLocation): QueryField;
131
131
  getQueryFieldReference(f: RefToField): QueryField;
132
132
  getDimensionOrMeasureByName(name: string[]): QueryField;
133
133
  /** returns a query object for the given name */
134
134
  getDimensionByName(name: string[]): QueryBasicField;
135
135
  /** returns a query object for the given name */
136
- getStructByName(name: string[]): QueryStruct;
136
+ getStructByName(name: string[], at?: DocumentLocation): QueryStruct;
137
137
  getDistinctKey(): QueryBasicField;
138
138
  applyStructFiltersToTurtleDef(turtleDef: TurtleDef | TurtleDefPlusFilters): TurtleDef;
139
139
  }
@@ -12,6 +12,7 @@ exports.isBasicAggregate = isBasicAggregate;
12
12
  exports.isBasicCalculation = isBasicCalculation;
13
13
  exports.isBasicScalar = isBasicScalar;
14
14
  const uuid_1 = require("uuid");
15
+ const malloy_compile_error_1 = require("./malloy_compile_error");
15
16
  const malloy_types_1 = require("./malloy_types");
16
17
  const annotation_1 = require("../annotation");
17
18
  const dialect_1 = require("../dialect");
@@ -205,7 +206,7 @@ class QueryStruct {
205
206
  modelCompilerFlags() {
206
207
  if (this._modelTag === undefined) {
207
208
  const annotation = this.structDef.modelAnnotation;
208
- const { tag } = (0, annotation_1.annotationToTag)(annotation, { prefix: /^##!\s*/ });
209
+ const { tag } = new annotation_1.Annotations(annotation).parseAsTag('!');
209
210
  this._modelTag = tag;
210
211
  }
211
212
  return this._modelTag;
@@ -245,7 +246,8 @@ class QueryStruct {
245
246
  ? this.parent.resolveParentParameterReferences(resolved1)
246
247
  : resolved1;
247
248
  if (resolved2.value === null) {
248
- throw new Error('Invalid parameter value');
249
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${frag.path[0]}' resolves to a null value chain; ` +
250
+ 'this parameter was not supplied.', 'compiler-parameter-no-value', undefined);
249
251
  }
250
252
  else {
251
253
  return resolved2.value;
@@ -284,10 +286,10 @@ class QueryStruct {
284
286
  if (!QueryStruct.turtleFieldMaker) {
285
287
  throw new Error('INTERNAL ERROR: QueryQuery must initialize QueryStruct nested factory method');
286
288
  }
287
- this.addFieldToNameMap(as, QueryStruct.turtleFieldMaker(field, this));
289
+ this.addFieldToNameMap(as, QueryStruct.turtleFieldMaker(field, this), field.location);
288
290
  }
289
291
  else if ((0, malloy_types_1.isAtomic)(field) || (0, malloy_types_1.isJoinedSource)(field)) {
290
- this.addFieldToNameMap(as, this.makeQueryField(field));
292
+ this.addFieldToNameMap(as, this.makeQueryField(field), field.location);
291
293
  }
292
294
  else {
293
295
  // According to the type system this should be impossible, but we have seen this happen
@@ -413,9 +415,9 @@ class QueryStruct {
413
415
  }
414
416
  return this;
415
417
  }
416
- addFieldToNameMap(as, n) {
418
+ addFieldToNameMap(as, n, at) {
417
419
  if (this.nameMap.has(as)) {
418
- throw new Error(`Redefinition of ${as}`);
420
+ throw new malloy_compile_error_1.MalloyCompileError(`Field name '${as}' is defined more than once in this scope.`, 'compiler-name-redefined', at);
419
421
  }
420
422
  this.nameMap.set(as, n);
421
423
  }
@@ -426,7 +428,9 @@ class QueryStruct {
426
428
  return pk;
427
429
  }
428
430
  else {
429
- throw new Error(`Missing primary key for ${fieldDef}`);
431
+ throw new malloy_compile_error_1.MalloyCompileError(`Source '${(0, malloy_types_1.getIdentifier)(this.structDef)}' has no primary key; ` +
432
+ `cannot compute a unique key for field '${(0, malloy_types_1.getIdentifier)(fieldDef)}'. ` +
433
+ 'Add `primary_key: <field>` to the source definition.', 'compiler-missing-primary-key', fieldDef.location);
430
434
  }
431
435
  }
432
436
  /**
@@ -528,7 +532,7 @@ class QueryStruct {
528
532
  return this.nameMap.get(name);
529
533
  }
530
534
  /** convert a path into a field reference */
531
- getFieldByName(path) {
535
+ getFieldByName(path, at) {
532
536
  let found = undefined;
533
537
  let lookIn = this;
534
538
  let notFound = path[0];
@@ -542,22 +546,23 @@ class QueryStruct {
542
546
  found instanceof QueryFieldStruct ? found.queryStruct : undefined;
543
547
  }
544
548
  if (found === undefined) {
545
- const pathErr = path.length > 1 ? ` in ${path.join('.')}` : '';
546
- throw new Error(`${notFound} not found${pathErr}`);
549
+ const pathErr = path.length > 1 ? ` in path '${path.join('.')}'` : '';
550
+ throw new malloy_compile_error_1.MalloyCompileError(`Field '${notFound}' not found${pathErr}.`, 'compiler-field-not-found', at);
547
551
  }
548
552
  return found;
549
553
  }
550
554
  // structs referenced in queries are converted to fields.
551
- getQueryFieldByName(name) {
552
- const field = this.getFieldByName(name);
555
+ getQueryFieldByName(name, at) {
556
+ const field = this.getFieldByName(name, at);
553
557
  if (field instanceof QueryFieldStruct) {
554
- throw new Error(`Cannot reference ${name.join('.')} as a scalar'`);
558
+ throw new malloy_compile_error_1.MalloyCompileError(`'${name.join('.')}' refers to a source or join, not a scalar field. ` +
559
+ 'Use `source.field` to reference fields inside it.', 'compiler-cannot-reference-as-scalar', at);
555
560
  }
556
561
  return field;
557
562
  }
558
563
  getQueryFieldReference(f) {
559
564
  const { path, annotation, drillExpression } = f;
560
- const field = this.getFieldByName(path);
565
+ const field = this.getFieldByName(path, f.at);
561
566
  if (annotation || drillExpression) {
562
567
  if (field.parent === undefined) {
563
568
  throw new Error('Inconcievable, field reference to orphaned query field');
@@ -591,15 +596,15 @@ class QueryStruct {
591
596
  throw new Error(`${name} is not an atomic scalar field? Inconceivable!`);
592
597
  }
593
598
  /** returns a query object for the given name */
594
- getStructByName(name) {
599
+ getStructByName(name, at) {
595
600
  if (name.length === 0) {
596
601
  return this;
597
602
  }
598
- const struct = this.getFieldByName(name);
603
+ const struct = this.getFieldByName(name, at);
599
604
  if (struct instanceof QueryFieldStruct) {
600
605
  return struct.queryStruct;
601
606
  }
602
- throw new Error(`Error: Path to structure not found '${name.join('.')}'`);
607
+ throw new malloy_compile_error_1.MalloyCompileError(`'${name.join('.')}' is not a source or join.`, 'compiler-struct-not-found', at);
603
608
  }
604
609
  getDistinctKey() {
605
610
  if (this.structDef.type !== 'record') {