@true-sight/model-schema 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.compileExplore = compileExplore;
4
4
  require("reflect-metadata");
5
+ const manifest_expr_types_js_1 = require("../../types/manifest-expr.types.js");
5
6
  const compile_errors_js_1 = require("../errors/compile-errors.js");
7
+ const field_js_1 = require("../expr/field.js");
6
8
  const explore_registry_js_1 = require("../registry/explore-registry.js");
7
9
  const view_registry_js_1 = require("../registry/view-registry.js");
8
10
  const compile_view_js_1 = require("./compile-view.js");
@@ -50,11 +52,54 @@ function compileExplore(ExploreClass) {
50
52
  scopedViewNames.push(joinViewMeta.name);
51
53
  }
52
54
  const views = [...viewClasses.values()].map((ViewClass) => (0, compile_view_js_1.compileView)(ViewClass));
55
+ const alwaysInclude = resolveAlwaysInclude(ExploreClass, exploreMeta.name, views, scopedViewNames);
53
56
  return {
54
57
  name: exploreMeta.name,
55
58
  label: exploreMeta.label,
56
59
  from: fromViewMeta.name,
57
60
  joins,
58
61
  views,
62
+ ...(alwaysInclude ? { alwaysInclude } : {}),
59
63
  };
60
64
  }
65
+ function resolveAlwaysInclude(ExploreClass, exploreName, views, scopedViewNames) {
66
+ const callback = (0, explore_registry_js_1.getAlwaysInclude)(ExploreClass);
67
+ if (!callback) {
68
+ return undefined;
69
+ }
70
+ const scope = (0, join_helpers_js_1.createJoinScope)(scopedViewNames);
71
+ const proxies = scopedViewNames.map((name) => scope[name]);
72
+ const raw = callback(...proxies);
73
+ if (!Array.isArray(raw)) {
74
+ throw new compile_errors_js_1.AlwaysIncludeError(exploreName, 'callback must return Field[]');
75
+ }
76
+ const known = new Set();
77
+ for (const view of views) {
78
+ for (const dimension of view.dimensions) {
79
+ known.add(`${view.name}.${dimension.name}`);
80
+ }
81
+ for (const measurement of view.measurements) {
82
+ known.add(`${view.name}.${measurement.name}`);
83
+ }
84
+ }
85
+ const seen = new Set();
86
+ const names = [];
87
+ for (const item of raw) {
88
+ if (!(item instanceof field_js_1.Field)) {
89
+ throw new compile_errors_js_1.AlwaysIncludeError(exploreName, 'callback must return Field[]');
90
+ }
91
+ const ref = item.ref;
92
+ if (!(0, manifest_expr_types_js_1.isRefString)(ref)) {
93
+ throw new compile_errors_js_1.AlwaysIncludeError(exploreName, `invalid name "${String(ref)}"`);
94
+ }
95
+ if (seen.has(ref)) {
96
+ continue;
97
+ }
98
+ seen.add(ref);
99
+ if (!known.has(ref)) {
100
+ throw new compile_errors_js_1.AlwaysIncludeError(exploreName, `unknown field '${ref}'`);
101
+ }
102
+ names.push(ref);
103
+ }
104
+ return names.length > 0 ? names : undefined;
105
+ }
@@ -0,0 +1,2 @@
1
+ import type { AlwaysIncludeCallback } from '../types/join-scope.types.js';
2
+ export declare function AlwaysInclude(callback: AlwaysIncludeCallback): (target: new (...args: unknown[]) => object) => void;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AlwaysInclude = AlwaysInclude;
4
+ const explore_registry_js_1 = require("../registry/explore-registry.js");
5
+ function AlwaysInclude(callback) {
6
+ return (target) => {
7
+ (0, explore_registry_js_1.registerAlwaysInclude)(target, callback);
8
+ };
9
+ }
@@ -4,4 +4,5 @@ export { Measure } from './measure.decorator.js';
4
4
  export { Explore } from './explore.decorator.js';
5
5
  export { Join } from './join.decorator.js';
6
6
  export { Model } from './model.decorator.js';
7
+ export { AlwaysInclude } from './always-include.decorator.js';
7
8
  export { joinScope, isJoinScopeTagged } from '../expr/join-scope.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isJoinScopeTagged = exports.joinScope = exports.Model = exports.Join = exports.Explore = exports.Measure = exports.Dimension = exports.View = void 0;
3
+ exports.isJoinScopeTagged = exports.joinScope = exports.AlwaysInclude = exports.Model = exports.Join = exports.Explore = exports.Measure = exports.Dimension = exports.View = void 0;
4
4
  var view_decorator_js_1 = require("./view.decorator.js");
5
5
  Object.defineProperty(exports, "View", { enumerable: true, get: function () { return view_decorator_js_1.View; } });
6
6
  var dimension_decorator_js_1 = require("./dimension.decorator.js");
@@ -13,6 +13,8 @@ var join_decorator_js_1 = require("./join.decorator.js");
13
13
  Object.defineProperty(exports, "Join", { enumerable: true, get: function () { return join_decorator_js_1.Join; } });
14
14
  var model_decorator_js_1 = require("./model.decorator.js");
15
15
  Object.defineProperty(exports, "Model", { enumerable: true, get: function () { return model_decorator_js_1.Model; } });
16
+ var always_include_decorator_js_1 = require("./always-include.decorator.js");
17
+ Object.defineProperty(exports, "AlwaysInclude", { enumerable: true, get: function () { return always_include_decorator_js_1.AlwaysInclude; } });
16
18
  var join_scope_js_1 = require("../expr/join-scope.js");
17
19
  Object.defineProperty(exports, "joinScope", { enumerable: true, get: function () { return join_scope_js_1.joinScope; } });
18
20
  Object.defineProperty(exports, "isJoinScopeTagged", { enumerable: true, get: function () { return join_scope_js_1.isJoinScopeTagged; } });
@@ -10,3 +10,6 @@ export declare class JoinScopeError extends Error {
10
10
  export declare class DuplicateNameError extends Error {
11
11
  constructor(kind: string, name: string, context?: string);
12
12
  }
13
+ export declare class AlwaysIncludeError extends Error {
14
+ constructor(exploreName: string, reason: string);
15
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DuplicateNameError = exports.JoinScopeError = exports.InvalidMeasureError = exports.MissingMetadataError = void 0;
3
+ exports.AlwaysIncludeError = exports.DuplicateNameError = exports.JoinScopeError = exports.InvalidMeasureError = exports.MissingMetadataError = void 0;
4
4
  class MissingMetadataError extends Error {
5
5
  constructor(kind, className) {
6
6
  super(`${kind} metadata missing on ${className}`);
@@ -31,3 +31,10 @@ class DuplicateNameError extends Error {
31
31
  }
32
32
  }
33
33
  exports.DuplicateNameError = DuplicateNameError;
34
+ class AlwaysIncludeError extends Error {
35
+ constructor(exploreName, reason) {
36
+ super(`Always include on explore '${exploreName}': ${reason}`);
37
+ this.name = 'AlwaysIncludeError';
38
+ }
39
+ }
40
+ exports.AlwaysIncludeError = AlwaysIncludeError;
@@ -1,5 +1,6 @@
1
1
  import type { ManifestExpr } from '../../types/manifest-expr.types.js';
2
2
  import type { JoinRelationship, JoinType } from '../../types/manifest.types.js';
3
+ import type { AlwaysIncludeCallback } from '../types/join-scope.types.js';
3
4
  export type ViewConstructor = new (...args: unknown[]) => object;
4
5
  export type ExploreOptions = {
5
6
  name: string;
@@ -20,3 +21,5 @@ export declare function registerExplore(ctor: object, options: ExploreOptions):
20
21
  export declare function getExploreMeta(ctor: object): ExploreOptions | undefined;
21
22
  export declare function addJoin(prototype: object, entry: JoinEntry): void;
22
23
  export declare function getJoins(prototype: object): JoinEntry[];
24
+ export declare function registerAlwaysInclude(ctor: object, callback: AlwaysIncludeCallback): void;
25
+ export declare function getAlwaysInclude(ctor: object): AlwaysIncludeCallback | undefined;
@@ -4,8 +4,11 @@ exports.registerExplore = registerExplore;
4
4
  exports.getExploreMeta = getExploreMeta;
5
5
  exports.addJoin = addJoin;
6
6
  exports.getJoins = getJoins;
7
+ exports.registerAlwaysInclude = registerAlwaysInclude;
8
+ exports.getAlwaysInclude = getAlwaysInclude;
7
9
  const exploreMeta = new WeakMap();
8
10
  const joinMeta = new WeakMap();
11
+ const alwaysIncludeMeta = new WeakMap();
9
12
  function registerExplore(ctor, options) {
10
13
  exploreMeta.set(ctor, options);
11
14
  }
@@ -20,3 +23,9 @@ function addJoin(prototype, entry) {
20
23
  function getJoins(prototype) {
21
24
  return joinMeta.get(prototype) ?? [];
22
25
  }
26
+ function registerAlwaysInclude(ctor, callback) {
27
+ alwaysIncludeMeta.set(ctor, callback);
28
+ }
29
+ function getAlwaysInclude(ctor) {
30
+ return alwaysIncludeMeta.get(ctor);
31
+ }
@@ -1,6 +1,8 @@
1
1
  import type { ManifestExpr } from '../../types/manifest-expr.types.js';
2
+ import type { Field } from '../expr/field.js';
2
3
  import type { JoinScopeProxy } from './view-name-map.types.js';
3
4
  export type JoinScopeCallback = (...proxies: JoinScopeProxy[]) => ManifestExpr;
5
+ export type AlwaysIncludeCallback = (...proxies: JoinScopeProxy[]) => Field<unknown>[];
4
6
  export type JoinScopeSqlOn = (scope: Record<string, unknown>) => ManifestExpr;
5
7
  export interface JoinScopeTagged {
6
8
  readonly __joinScopeBrand: symbol;
package/dist/index.d.ts CHANGED
@@ -5,9 +5,9 @@ export type { AggregateType, DimensionDefinition, DimensionType, Explore as Mani
5
5
  export type { ManifestValidationError, ValidationResult, } from './validate/validation-result.js';
6
6
  export { validateManifest } from './validate/validate-manifest.js';
7
7
  export { Field, field, count, sum, min, max, raw, joinScope, isJoinScopeTagged } from './dsl/expr/index.js';
8
- export type { JoinScopeCallback, JoinScopeSqlOn, JoinScopeTagged } from './dsl/types/join-scope.types.js';
9
- export { View, Dimension, Measure, Explore, Join, Model } from './dsl/decorators/index.js';
8
+ export type { JoinScopeCallback, JoinScopeSqlOn, JoinScopeTagged, AlwaysIncludeCallback } from './dsl/types/join-scope.types.js';
9
+ export { View, Dimension, Measure, Explore, Join, Model, AlwaysInclude } from './dsl/decorators/index.js';
10
10
  export type { ViewProxy, ViewConstructor } from './dsl/types/view-proxy.types.js';
11
11
  export type { ViewNameMap, ViewProxyByName, } from './dsl/types/view-name-map.types.js';
12
12
  export { compileView, compileExplore, compileModel, compileModels } from './dsl/compile/index.js';
13
- export { MissingMetadataError, InvalidMeasureError, JoinScopeError, DuplicateNameError, } from './dsl/errors/compile-errors.js';
13
+ export { MissingMetadataError, InvalidMeasureError, JoinScopeError, DuplicateNameError, AlwaysIncludeError, } from './dsl/errors/compile-errors.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DuplicateNameError = exports.JoinScopeError = exports.InvalidMeasureError = exports.MissingMetadataError = exports.compileModels = exports.compileModel = exports.compileExplore = exports.compileView = exports.Model = exports.Join = exports.Explore = exports.Measure = exports.Dimension = exports.View = exports.isJoinScopeTagged = exports.joinScope = exports.raw = exports.max = exports.min = exports.sum = exports.count = exports.field = exports.Field = exports.validateManifest = exports.REF_STRING_PATTERN = exports.isRefString = exports.isManifestExprObject = exports.isJoinPair = exports.SCHEMA_VERSION = void 0;
3
+ exports.AlwaysIncludeError = exports.DuplicateNameError = exports.JoinScopeError = exports.InvalidMeasureError = exports.MissingMetadataError = exports.compileModels = exports.compileModel = exports.compileExplore = exports.compileView = exports.AlwaysInclude = exports.Model = exports.Join = exports.Explore = exports.Measure = exports.Dimension = exports.View = exports.isJoinScopeTagged = exports.joinScope = exports.raw = exports.max = exports.min = exports.sum = exports.count = exports.field = exports.Field = exports.validateManifest = exports.REF_STRING_PATTERN = exports.isRefString = exports.isManifestExprObject = exports.isJoinPair = exports.SCHEMA_VERSION = void 0;
4
4
  var constants_js_1 = require("./constants.js");
5
5
  Object.defineProperty(exports, "SCHEMA_VERSION", { enumerable: true, get: function () { return constants_js_1.SCHEMA_VERSION; } });
6
6
  var manifest_expr_types_js_1 = require("./types/manifest-expr.types.js");
@@ -27,6 +27,7 @@ Object.defineProperty(exports, "Measure", { enumerable: true, get: function () {
27
27
  Object.defineProperty(exports, "Explore", { enumerable: true, get: function () { return index_js_2.Explore; } });
28
28
  Object.defineProperty(exports, "Join", { enumerable: true, get: function () { return index_js_2.Join; } });
29
29
  Object.defineProperty(exports, "Model", { enumerable: true, get: function () { return index_js_2.Model; } });
30
+ Object.defineProperty(exports, "AlwaysInclude", { enumerable: true, get: function () { return index_js_2.AlwaysInclude; } });
30
31
  var index_js_3 = require("./dsl/compile/index.js");
31
32
  Object.defineProperty(exports, "compileView", { enumerable: true, get: function () { return index_js_3.compileView; } });
32
33
  Object.defineProperty(exports, "compileExplore", { enumerable: true, get: function () { return index_js_3.compileExplore; } });
@@ -37,3 +38,4 @@ Object.defineProperty(exports, "MissingMetadataError", { enumerable: true, get:
37
38
  Object.defineProperty(exports, "InvalidMeasureError", { enumerable: true, get: function () { return compile_errors_js_1.InvalidMeasureError; } });
38
39
  Object.defineProperty(exports, "JoinScopeError", { enumerable: true, get: function () { return compile_errors_js_1.JoinScopeError; } });
39
40
  Object.defineProperty(exports, "DuplicateNameError", { enumerable: true, get: function () { return compile_errors_js_1.DuplicateNameError; } });
41
+ Object.defineProperty(exports, "AlwaysIncludeError", { enumerable: true, get: function () { return compile_errors_js_1.AlwaysIncludeError; } });
@@ -39,6 +39,7 @@ export interface Explore {
39
39
  from: string;
40
40
  joins: ExploreJoin[];
41
41
  views: ViewDefinition[];
42
+ alwaysInclude?: string[];
42
43
  }
43
44
  export interface Model {
44
45
  name: string;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.collectSemanticErrors = collectSemanticErrors;
4
+ const manifest_expr_types_js_1 = require("../types/manifest-expr.types.js");
4
5
  const expr_semantic_js_1 = require("./expr-semantic.js");
5
6
  function duplicateNameErrors(names, pathPrefix, entityLabel) {
6
7
  const seen = new Map();
@@ -43,6 +44,31 @@ function collectSemanticErrors(manifest) {
43
44
  message: `from view "${explore.from}" is not in explore views`,
44
45
  });
45
46
  }
47
+ const qualifiedFields = new Set();
48
+ for (const view of explore.views) {
49
+ for (const dimension of view.dimensions) {
50
+ qualifiedFields.add(`${view.name}.${dimension.name}`);
51
+ }
52
+ for (const measurement of view.measurements) {
53
+ qualifiedFields.add(`${view.name}.${measurement.name}`);
54
+ }
55
+ }
56
+ (explore.alwaysInclude ?? []).forEach((name, index) => {
57
+ const path = `${explorePath}.alwaysInclude[${index}]`;
58
+ if (!(0, manifest_expr_types_js_1.isRefString)(name)) {
59
+ errors.push({
60
+ path,
61
+ message: `alwaysInclude name "${String(name)}" is not a view.field ref`,
62
+ });
63
+ return;
64
+ }
65
+ if (!qualifiedFields.has(name)) {
66
+ errors.push({
67
+ path,
68
+ message: `alwaysInclude name "${name}" is not a dimension or measurement on this explore`,
69
+ });
70
+ }
71
+ });
46
72
  explore.joins.forEach((join, joinIndex) => {
47
73
  if (!viewNames.has(join.fromView)) {
48
74
  errors.push({
@@ -186,6 +186,10 @@
186
186
  "views": {
187
187
  "type": "array",
188
188
  "items": { "$ref": "#/definitions/ViewDefinition" }
189
+ },
190
+ "alwaysInclude": {
191
+ "type": "array",
192
+ "items": { "$ref": "#/definitions/RefString" }
189
193
  }
190
194
  }
191
195
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-sight/model-schema",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [