@segmentstream-cloud/ontology 0.22.0 → 0.23.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.
@@ -13,9 +13,11 @@ import { type SignInOptions, type SignInPolicy } from "./sign-in.js";
13
13
  * a relationship with a warehouse-backed end carries `on` and omits `optional` and `onDelete`.
14
14
  * Version 6 adds the `countDistinct` measure operator and lets stored entities declare `measures`,
15
15
  * whose columns name the stored properties' replication view columns. Version 7 adds the avg, min,
16
- * max and filtered measure operators and balanceOver on measures.
16
+ * max and filtered measure operators and balanceOver on measures. Version 8 adds the top-level
17
+ * `measures`, the measures of the model, written only when the ontology declares some: each combines
18
+ * measures of entities, named `entity.measure`, with its inputs and its alignment.
17
19
  */
18
- export declare const MANIFEST_VERSION = 7;
20
+ export declare const MANIFEST_VERSION = 8;
19
21
  /** The phrase used for a direction the author did not phrase. */
20
22
  export declare const DEFAULT_PHRASE = "has";
21
23
  export declare const SelectOptionManifest: Schema.Struct<{
@@ -146,6 +148,27 @@ export declare const MeasureManifest: Schema.Struct<{
146
148
  readonly balanceOver: Schema.optionalKey<Schema.String>;
147
149
  }>;
148
150
  export type MeasureManifest = typeof MeasureManifest.Type;
151
+ /**
152
+ * A measure of the model, `defineOntology({ measures })`. Its expression is an entity measure's
153
+ * arithmetic whose `measure` nodes name `entity.measure`, with references to other model measures
154
+ * expanded. Names are wire names throughout.
155
+ */
156
+ export declare const ModelMeasureManifest: Schema.Struct<{
157
+ readonly name: Schema.String;
158
+ readonly label: Schema.String;
159
+ readonly description: Schema.String;
160
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
161
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
162
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
163
+ /** The entity measures the expression reads, `entity.measure`, each once, in reading order. */
164
+ readonly inputs: Schema.$Array<Schema.String>;
165
+ /**
166
+ * Groups of properties that stand for one another: each names every entity the measure reads,
167
+ * with a property or `null` for an entity without a counterpart.
168
+ */
169
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
170
+ }>;
171
+ export type ModelMeasureManifest = typeof ModelMeasureManifest.Type;
149
172
  export declare const EntityManifest: Schema.Struct<{
150
173
  readonly source: Schema.optionalKey<Schema.Struct<{
151
174
  readonly asset: Schema.String;
@@ -438,6 +461,25 @@ export declare const OntologyManifestWire: Schema.encodeKeys<Schema.Struct<{
438
461
  readonly key: Schema.String;
439
462
  readonly description: Schema.String;
440
463
  }>>>;
464
+ /**
465
+ * The measures of the model, since version 8. Written only when there are some; absent reads as
466
+ * none.
467
+ */
468
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
469
+ readonly name: Schema.String;
470
+ readonly label: Schema.String;
471
+ readonly description: Schema.String;
472
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
473
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
474
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
475
+ /** The entity measures the expression reads, `entity.measure`, each once, in reading order. */
476
+ readonly inputs: Schema.$Array<Schema.String>;
477
+ /**
478
+ * Groups of properties that stand for one another: each names every entity the measure reads,
479
+ * with a property or `null` for an entity without a counterpart.
480
+ */
481
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
482
+ }>>>;
441
483
  }>, {
442
484
  readonly signIn: "sign_in";
443
485
  }>;
@@ -589,6 +631,25 @@ export declare const OntologyManifest: Schema.Union<readonly [Schema.encodeKeys<
589
631
  readonly key: Schema.String;
590
632
  readonly description: Schema.String;
591
633
  }>>>;
634
+ /**
635
+ * The measures of the model, since version 8. Written only when there are some; absent reads as
636
+ * none.
637
+ */
638
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
639
+ readonly name: Schema.String;
640
+ readonly label: Schema.String;
641
+ readonly description: Schema.String;
642
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
643
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
644
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
645
+ /** The entity measures the expression reads, `entity.measure`, each once, in reading order. */
646
+ readonly inputs: Schema.$Array<Schema.String>;
647
+ /**
648
+ * Groups of properties that stand for one another: each names every entity the measure reads,
649
+ * with a property or `null` for an entity without a counterpart.
650
+ */
651
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
652
+ }>>>;
592
653
  }>, {
593
654
  readonly signIn: "sign_in";
594
655
  }>, Schema.Struct<{
@@ -731,6 +792,25 @@ export declare const OntologyManifest: Schema.Union<readonly [Schema.encodeKeys<
731
792
  readonly key: Schema.String;
732
793
  readonly description: Schema.String;
733
794
  }>>>;
795
+ /**
796
+ * The measures of the model, since version 8. Written only when there are some; absent reads as
797
+ * none.
798
+ */
799
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
800
+ readonly name: Schema.String;
801
+ readonly label: Schema.String;
802
+ readonly description: Schema.String;
803
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
804
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
805
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
806
+ /** The entity measures the expression reads, `entity.measure`, each once, in reading order. */
807
+ readonly inputs: Schema.$Array<Schema.String>;
808
+ /**
809
+ * Groups of properties that stand for one another: each names every entity the measure reads,
810
+ * with a property or `null` for an entity without a counterpart.
811
+ */
812
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
813
+ }>>>;
734
814
  }>]>;
735
815
  export type OntologyManifest = typeof OntologyManifest.Type;
736
816
  /** Retain the complete document when decoding a persisted or published manifest. */
@@ -833,6 +913,18 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
833
913
  }[];
834
914
  readonly version: number;
835
915
  readonly hash: string;
916
+ readonly measures?: readonly {
917
+ readonly name: string;
918
+ readonly label: string;
919
+ readonly description: string;
920
+ readonly unit: "number" | "currency" | "ratio" | "percent";
921
+ readonly format: "decimal" | "currency" | "percent";
922
+ readonly expression: Schema.Json;
923
+ readonly inputs: readonly string[];
924
+ readonly align: readonly {
925
+ readonly [x: string]: string | null;
926
+ }[];
927
+ }[] | undefined;
836
928
  readonly connections?: readonly {
837
929
  readonly key: string;
838
930
  readonly description: string;
@@ -939,6 +1031,18 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
939
1031
  }[];
940
1032
  readonly version: number;
941
1033
  readonly hash: string;
1034
+ readonly measures?: readonly {
1035
+ readonly name: string;
1036
+ readonly label: string;
1037
+ readonly description: string;
1038
+ readonly unit: "number" | "currency" | "ratio" | "percent";
1039
+ readonly format: "decimal" | "currency" | "percent";
1040
+ readonly expression: Schema.Json;
1041
+ readonly inputs: readonly string[];
1042
+ readonly align: readonly {
1043
+ readonly [x: string]: string | null;
1044
+ }[];
1045
+ }[] | undefined;
942
1046
  readonly connections?: readonly {
943
1047
  readonly key: string;
944
1048
  readonly description: string;
@@ -973,6 +1077,8 @@ export declare function buildManifest(input: {
973
1077
  readonly roles: ReadonlyArray<Role>;
974
1078
  readonly signIn: SignInOptions;
975
1079
  readonly connections?: ReadonlyArray<ResolvedConnection>;
1080
+ /** The measures of the model, resolved to their manifest form. */
1081
+ readonly measures?: ReadonlyArray<ModelMeasureManifest>;
976
1082
  }): OntologyManifest;
977
1083
  /** The keys of every connection the manifest's actions call, without duplicates. */
978
1084
  export declare function actionConnectionKeys(manifest: Pick<OntologyManifest, "entities">): Array<string>;
package/dist/manifest.js CHANGED
@@ -16,9 +16,11 @@ import { DEFAULT_SIGN_IN_POLICY, isSignInPolicy, } from "./sign-in.js";
16
16
  * a relationship with a warehouse-backed end carries `on` and omits `optional` and `onDelete`.
17
17
  * Version 6 adds the `countDistinct` measure operator and lets stored entities declare `measures`,
18
18
  * whose columns name the stored properties' replication view columns. Version 7 adds the avg, min,
19
- * max and filtered measure operators and balanceOver on measures.
19
+ * max and filtered measure operators and balanceOver on measures. Version 8 adds the top-level
20
+ * `measures`, the measures of the model, written only when the ontology declares some: each combines
21
+ * measures of entities, named `entity.measure`, with its inputs and its alignment.
20
22
  */
21
- export const MANIFEST_VERSION = 7;
23
+ export const MANIFEST_VERSION = 8;
22
24
  /** The first version whose documents use snake_case names on the wire. */
23
25
  const WIRE_MANIFEST_VERSION = 4;
24
26
  /** The phrase used for a direction the author did not phrase. */
@@ -139,6 +141,26 @@ export const MeasureManifest = Schema.Struct({
139
141
  */
140
142
  balanceOver: Schema.optionalKey(Schema.String),
141
143
  });
144
+ /**
145
+ * A measure of the model, `defineOntology({ measures })`. Its expression is an entity measure's
146
+ * arithmetic whose `measure` nodes name `entity.measure`, with references to other model measures
147
+ * expanded. Names are wire names throughout.
148
+ */
149
+ export const ModelMeasureManifest = Schema.Struct({
150
+ name: Schema.String,
151
+ label: Schema.String,
152
+ description: Schema.String,
153
+ unit: Schema.Literals(["number", "currency", "ratio", "percent"]),
154
+ format: Schema.Literals(["decimal", "currency", "percent"]),
155
+ expression: Schema.Json,
156
+ /** The entity measures the expression reads, `entity.measure`, each once, in reading order. */
157
+ inputs: Schema.Array(Schema.String),
158
+ /**
159
+ * Groups of properties that stand for one another: each names every entity the measure reads,
160
+ * with a property or `null` for an entity without a counterpart.
161
+ */
162
+ align: Schema.Array(Schema.Record(Schema.String, Schema.NullOr(Schema.String))),
163
+ });
142
164
  export const EntityManifest = Schema.Struct({
143
165
  source: Schema.optionalKey(Schema.Struct({
144
166
  asset: Schema.String,
@@ -200,6 +222,11 @@ const OntologyManifestModel = Schema.Struct({
200
222
  signIn: Schema.optionalKey(SignInManifest),
201
223
  /** The connections the actions call. Written only when there are some; absent reads as none. */
202
224
  connections: Schema.optionalKey(Schema.Array(ConnectionManifest)),
225
+ /**
226
+ * The measures of the model, since version 8. Written only when there are some; absent reads as
227
+ * none.
228
+ */
229
+ measures: Schema.optionalKey(Schema.Array(ModelMeasureManifest)),
203
230
  });
204
231
  const PropertyManifestWire = PropertyManifest.pipe(Schema.encodeKeys({
205
232
  renamedFrom: "renamed_from",
@@ -429,6 +456,7 @@ function entityManifest(input) {
429
456
  /** Builds the manifest of a resolved ontology that passed `check`. */
430
457
  export function buildManifest(input) {
431
458
  const connections = input.connections ?? [];
459
+ const measures = input.measures ?? [];
432
460
  const warehouseBacked = new Set(input.entities
433
461
  .filter((candidate) => candidate.entity.definition.source !== undefined)
434
462
  .map((candidate) => candidate.name));
@@ -450,6 +478,7 @@ export function buildManifest(input) {
450
478
  })),
451
479
  }
452
480
  : {}),
481
+ ...(measures.length > 0 ? { measures } : {}),
453
482
  };
454
483
  return { ...body, hash: manifestHash(encodeManifest({ ...body, hash: "" })) };
455
484
  }
package/dist/measure.d.ts CHANGED
@@ -71,12 +71,62 @@ export type Measure = MeasureExpression & Partial<MeasureOptions> & {
71
71
  readonly where?: ReadonlyArray<MeasureCondition | MeasureColumnCondition>;
72
72
  /** An aggregate's balance property, as its builder's `balanceOver` option declares it. */
73
73
  readonly balanceOver?: string;
74
+ /** A measure of an entity reads that entity's rows only: alignment is for model measures. */
75
+ readonly align?: never;
74
76
  };
75
77
  export type Measures = Readonly<Record<string, Measure>>;
78
+ /**
79
+ * One group of a model measure's alignment: every entity the measure reads, by its TypeScript name,
80
+ * with its property that stands for the others', or `null` when the entity has no counterpart, as a
81
+ * current snapshot has no date.
82
+ */
83
+ export type AlignmentGroup = Readonly<Record<string, string | null>>;
84
+ /** The options of a model measure: a measure's options and how its entities' properties align. */
85
+ export interface ModelMeasureOptions extends MeasureOptions {
86
+ /**
87
+ * At most 5 groups of properties that stand for one another across the entities the measure
88
+ * reads, such as a sales date and a plan month. A request that groups or filters by one member of
89
+ * a group reads each entity through its own member. Properties of one group share a type, and a
90
+ * property appears in one group at most.
91
+ */
92
+ readonly align?: ReadonlyArray<AlignmentGroup>;
93
+ }
94
+ /**
95
+ * A measure of the model, declared in `defineOntology({ measures })`: arithmetic over measures of
96
+ * entities, `m.ref("entity.measure")`, and over other model measures, `m.ref("name")`. It reads
97
+ * entity measures and aggregates no rows of its own.
98
+ */
99
+ export type ModelMeasure = MeasureExpression & Partial<MeasureOptions> & {
100
+ readonly align?: ReadonlyArray<AlignmentGroup>;
101
+ };
102
+ export type ModelMeasures = Readonly<Record<string, ModelMeasure>>;
103
+ /** A model measure's options that declare an alignment, which only model measures take. */
104
+ type AlignedOptions = ModelMeasureOptions & {
105
+ readonly align: ReadonlyArray<AlignmentGroup>;
106
+ };
107
+ /**
108
+ * An argument of arithmetic: any expression but one that declares an alignment, which belongs on
109
+ * the model measure itself.
110
+ */
111
+ type Argument = MeasureExpression & {
112
+ readonly align?: never;
113
+ };
114
+ /** Arithmetic of two expressions; with `align` among its options, a model measure. */
115
+ interface Arithmetic {
116
+ (left: Argument, right: Argument, options: AlignedOptions): ModelMeasure;
117
+ (left: Argument, right: Argument, options?: MeasureOptions): Measure;
118
+ }
119
+ /** A reference to another measure; with `align` among its options, a model measure. */
120
+ interface Reference {
121
+ (name: string, options: AlignedOptions): ModelMeasure;
122
+ (name: string, options?: MeasureOptions): Measure;
123
+ }
76
124
  /**
77
125
  * Arithmetic combines aggregates; references name another measure of the same entity. Every
78
126
  * aggregate takes `where`, conditions on properties of the entity that the rows it reads must meet,
79
- * and `balanceOver`, the date its rows are snapshots over.
127
+ * and `balanceOver`, the date its rows are snapshots over. In `defineOntology({ measures })`,
128
+ * references name measures of entities, `m.ref("sales.revenue")`, or other model measures, and
129
+ * arithmetic and references take `align`.
80
130
  */
81
131
  export declare const m: {
82
132
  readonly count: (options?: AggregateOptions) => Measure;
@@ -93,12 +143,12 @@ export declare const m: {
93
143
  readonly min: (property: string, options?: AggregateOptions) => Measure;
94
144
  /** The greatest value of an integer or number property. Maximums do not add up. */
95
145
  readonly max: (property: string, options?: AggregateOptions) => Measure;
96
- readonly ref: (name: string, options?: MeasureOptions) => Measure;
146
+ readonly ref: Reference;
97
147
  readonly literal: (value: number | string, options?: MeasureOptions) => Measure;
98
- readonly add: (left: MeasureExpression, right: MeasureExpression, options?: MeasureOptions) => Measure;
99
- readonly subtract: (left: MeasureExpression, right: MeasureExpression, options?: MeasureOptions) => Measure;
100
- readonly multiply: (left: MeasureExpression, right: MeasureExpression, options?: MeasureOptions) => Measure;
101
- readonly divide: (left: MeasureExpression, right: MeasureExpression, options?: MeasureOptions) => Measure;
148
+ readonly add: Arithmetic;
149
+ readonly subtract: Arithmetic;
150
+ readonly multiply: Arithmetic;
151
+ readonly divide: Arithmetic;
102
152
  };
103
153
  /** The conditions an expression declares: an aggregate's `where` option, or a `filtered` node's. */
104
154
  export declare function conditionsOf(value: Measure | MeasureExpression): ReadonlyArray<MeasureCondition | MeasureColumnCondition> | undefined;
@@ -119,3 +169,4 @@ export declare function conditionProperty(condition: object): unknown;
119
169
  * naming columns too.
120
170
  */
121
171
  export declare function measureExpression(value: MeasureExpression, columns: ReadonlyMap<string, string>): MeasureExpression;
172
+ export {};
package/dist/measure.js CHANGED
@@ -3,6 +3,9 @@ export const MEASURE_AGGREGATES = ["count", "sum", "countDistinct", "avg", "min"
3
3
  function binary(op) {
4
4
  return (left, right, options) => ({ op, args: [left, right], ...options });
5
5
  }
6
+ function reference(name, options) {
7
+ return { op: "measure", name, ...options };
8
+ }
6
9
  /** An aggregate of one property of the entity. */
7
10
  function aggregate(op) {
8
11
  return (property, options) => ({
@@ -14,7 +17,9 @@ function aggregate(op) {
14
17
  /**
15
18
  * Arithmetic combines aggregates; references name another measure of the same entity. Every
16
19
  * aggregate takes `where`, conditions on properties of the entity that the rows it reads must meet,
17
- * and `balanceOver`, the date its rows are snapshots over.
20
+ * and `balanceOver`, the date its rows are snapshots over. In `defineOntology({ measures })`,
21
+ * references name measures of entities, `m.ref("sales.revenue")`, or other model measures, and
22
+ * arithmetic and references take `align`.
18
23
  */
19
24
  export const m = {
20
25
  count: (options) => ({ op: "count", ...options }),
@@ -31,7 +36,7 @@ export const m = {
31
36
  min: aggregate("min"),
32
37
  /** The greatest value of an integer or number property. Maximums do not add up. */
33
38
  max: aggregate("max"),
34
- ref: (name, options) => ({ op: "measure", name, ...options }),
39
+ ref: reference,
35
40
  literal: (value, options) => ({
36
41
  op: "literal",
37
42
  value: String(value),
@@ -161,6 +161,16 @@ export declare const MigrationPlan: Schema.Struct<{
161
161
  readonly key: Schema.String;
162
162
  readonly description: Schema.String;
163
163
  }>>>;
164
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
165
+ readonly name: Schema.String;
166
+ readonly label: Schema.String;
167
+ readonly description: Schema.String;
168
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
169
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
170
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
171
+ readonly inputs: Schema.$Array<Schema.String>;
172
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
173
+ }>>>;
164
174
  }>, {
165
175
  readonly signIn: "sign_in";
166
176
  }>, Schema.Struct<{
@@ -267,6 +277,16 @@ export declare const MigrationPlan: Schema.Struct<{
267
277
  readonly key: Schema.String;
268
278
  readonly description: Schema.String;
269
279
  }>>>;
280
+ readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
281
+ readonly name: Schema.String;
282
+ readonly label: Schema.String;
283
+ readonly description: Schema.String;
284
+ readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
285
+ readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
286
+ readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
287
+ readonly inputs: Schema.$Array<Schema.String>;
288
+ readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
289
+ }>>>;
270
290
  }>]>;
271
291
  /** Non-transactional steps first, then the steps of the one transaction, in execution order. */
272
292
  readonly steps: Schema.$Array<Schema.Struct<{
@@ -0,0 +1,22 @@
1
+ import type { ResolvedEntity } from "./analyze.js";
2
+ import type { Diagnostic, DiagnosticCode } from "./diagnostics.js";
3
+ import type { ModelMeasureManifest } from "./manifest.js";
4
+ type Report = (code: DiagnosticCode, path: string, message: string, severity?: Diagnostic["severity"]) => void;
5
+ /** The most measures a model declares. */
6
+ export declare const MAXIMUM_MODEL_MEASURES = 50;
7
+ /** The most distinct entity measures one model measure reads, references expanded. */
8
+ export declare const MAXIMUM_MODEL_MEASURE_INPUTS = 10;
9
+ /** The most alignment groups one model measure declares. */
10
+ export declare const MAXIMUM_ALIGNMENT_GROUPS = 5;
11
+ /**
12
+ * Resolves and checks the measures of the model, `defineOntology({ measures })`, against the
13
+ * resolved entities, whose measures were checked first (docs/ontology-aggregates-release-3-spec.md,
14
+ * "Declaring model measures"). Returns the manifest form of each measure that passed, in declaration
15
+ * order: references expanded, names on the wire, inputs in reading order.
16
+ */
17
+ export declare function resolveModelMeasures(input: {
18
+ readonly measures: unknown;
19
+ readonly entities: ReadonlyArray<ResolvedEntity>;
20
+ readonly report: Report;
21
+ }): ReadonlyArray<ModelMeasureManifest>;
22
+ export {};