@segmentstream-cloud/ontology 0.21.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.
- package/README.md +27 -2
- package/dist/aggregate-routes.d.ts +46 -9
- package/dist/aggregate-routes.js +186 -20
- package/dist/analyze.d.ts +12 -1
- package/dist/analyze.js +3 -1
- package/dist/diagnostics.d.ts +3 -3
- package/dist/diagnostics.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +108 -2
- package/dist/manifest.js +31 -2
- package/dist/measure.d.ts +57 -6
- package/dist/measure.js +7 -2
- package/dist/migrate/apply.js +24 -13
- package/dist/migrate/errors.d.ts +1 -1
- package/dist/migrate/model.d.ts +23 -3
- package/dist/migrate/model.js +1 -0
- package/dist/model-measures.d.ts +22 -0
- package/dist/model-measures.js +543 -0
- package/dist/ontology.d.ts +5 -4
- package/dist/render.d.ts +15 -3
- package/dist/render.js +56 -3
- package/dist/replication/baseline.d.ts +10 -2
- package/dist/replication/baseline.js +23 -14
- package/dist/replication/migrate.d.ts +6 -0
- package/dist/replication/migrate.js +64 -0
- package/dist/replication/model.d.ts +1 -1
- package/dist/replication/store.d.ts +2 -2
- package/dist/replication/store.js +8 -3
- package/dist/service/catalog.d.ts +2 -1
- package/dist/service/catalog.js +7 -3
- package/dist/service/mcp/server.js +5 -2
- package/dist/service/mcp/tools.js +25 -10
- package/dist/service/schemas.d.ts +21 -0
- package/dist/service/schemas.js +11 -2
- package/dist/warehouse-backed.js +4 -0
- package/package.json +1 -1
package/dist/manifest.d.ts
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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:
|
|
146
|
+
readonly ref: Reference;
|
|
97
147
|
readonly literal: (value: number | string, options?: MeasureOptions) => Measure;
|
|
98
|
-
readonly add:
|
|
99
|
-
readonly subtract:
|
|
100
|
-
readonly multiply:
|
|
101
|
-
readonly divide:
|
|
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:
|
|
39
|
+
ref: reference,
|
|
35
40
|
literal: (value, options) => ({
|
|
36
41
|
op: "literal",
|
|
37
42
|
value: String(value),
|
package/dist/migrate/apply.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { Duration, Effect, Schema } from "effect";
|
|
3
2
|
import { canonicalJson, decodeManifestDocument, encodeManifest, manifestHash, } from "../manifest.js";
|
|
4
3
|
import { CatalogInvalid, CatalogMismatch, MigrationFailed, MigrationRefused } from "./errors.js";
|
|
5
4
|
import { isNoopMigration } from "./model.js";
|
|
6
5
|
import { planMigration } from "./plan.js";
|
|
7
6
|
import { REPLICATION_DDL, REPLICATION_SCHEMA_VERSION, replicationEntityDdl, } from "../replication/schema.js";
|
|
8
|
-
import {
|
|
7
|
+
import { replicationMigrationRefusals } from "../replication/migrate.js";
|
|
9
8
|
import { ATTACHMENTS_TABLE } from "../storage/tables.js";
|
|
10
|
-
import { resolveReplicationSelection } from "../replication/model.js";
|
|
9
|
+
import { resolveReplicationSelection, } from "../replication/model.js";
|
|
11
10
|
import { AGENT_TABLES, API_KEYS_TABLE, CATALOG_TABLE, OAUTH_TABLES, USER_ROLES_TABLE, quoteIdentifier, quoteLiteral, } from "./storage.js";
|
|
12
11
|
const CATALOG = quoteIdentifier(CATALOG_TABLE);
|
|
13
12
|
/** Serializes migrations of one database; transaction-scoped, so it ends with the transaction. */
|
|
@@ -122,8 +121,25 @@ export const applyMigration = (plan, options) => Effect.gen(function* () {
|
|
|
122
121
|
if (presence?.present) {
|
|
123
122
|
const [state] = yield* sql.unsafe("SELECT * FROM _ontology_replication WHERE id FOR UPDATE");
|
|
124
123
|
if (state?.enabled) {
|
|
125
|
-
refresh =
|
|
126
|
-
|
|
124
|
+
refresh = yield* Effect.try({
|
|
125
|
+
try: () => resolveReplicationSelection(plan.target, state.exclusions ?? []),
|
|
126
|
+
catch: (cause) => new CatalogInvalid({ message: "The replication exclusions are invalid.", cause }),
|
|
127
|
+
});
|
|
128
|
+
const [target] = yield* sql.unsafe("SELECT * FROM _ontology_replication_targets WHERE generation = $1::uuid", [state.active_generation]);
|
|
129
|
+
if (!target) {
|
|
130
|
+
return yield* new CatalogInvalid({
|
|
131
|
+
message: "The active replication target is missing; pause and rebuild replication.",
|
|
132
|
+
cause: { generation: state.active_generation },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const refusals = replicationMigrationRefusals(target, plan.target, refresh);
|
|
136
|
+
if (refusals.length > 0) {
|
|
137
|
+
return yield* new MigrationRefused({
|
|
138
|
+
message: refusals.map((refusal) => `${refusal.path}: ${refusal.message}`).join(" "),
|
|
139
|
+
refusals,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Writers wait on this row. Capture and schema changes commit together on the same stream.
|
|
127
143
|
yield* sql.unsafe("UPDATE _ontology_replication SET enabled = false WHERE id");
|
|
128
144
|
}
|
|
129
145
|
}
|
|
@@ -148,14 +164,9 @@ export const applyMigration = (plan, options) => Effect.gen(function* () {
|
|
|
148
164
|
"applied_at" = now()
|
|
149
165
|
`, [plan.target.version, plan.target.hash, canonicalJson(encodeManifest(plan.target))]);
|
|
150
166
|
if (refresh !== undefined) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
manifest: plan.target,
|
|
155
|
-
exclusions: refresh,
|
|
156
|
-
selection,
|
|
157
|
-
newSource: true,
|
|
158
|
-
});
|
|
167
|
+
yield* sql.unsafe(`UPDATE _ontology_replication_targets SET manifest = $1::jsonb, selection = $2::jsonb
|
|
168
|
+
WHERE generation = (SELECT active_generation FROM _ontology_replication WHERE id)`, [JSON.stringify(plan.target), JSON.stringify(refresh)]);
|
|
169
|
+
yield* sql.unsafe("UPDATE _ontology_replication SET selection = $1::jsonb, enabled = true WHERE id", [JSON.stringify(Object.fromEntries(refresh.map((item) => [item.entityId, item])))]);
|
|
159
170
|
}
|
|
160
171
|
}));
|
|
161
172
|
return { hash: plan.target.hash, appliedSteps: plan.steps.length, changed: true };
|
package/dist/migrate/errors.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Schema } from "effect";
|
|
|
2
2
|
declare const MigrationRefused_base: Schema.Class<MigrationRefused, Schema.TaggedStruct<"MigrationRefused", {
|
|
3
3
|
readonly message: Schema.String;
|
|
4
4
|
readonly refusals: Schema.$Array<Schema.Struct<{
|
|
5
|
-
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict"]>;
|
|
5
|
+
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict", "replication-rebuild-required"]>;
|
|
6
6
|
readonly path: Schema.String;
|
|
7
7
|
readonly message: Schema.String;
|
|
8
8
|
}>>;
|
package/dist/migrate/model.d.ts
CHANGED
|
@@ -25,11 +25,11 @@ export declare const MigrationStep: Schema.Struct<{
|
|
|
25
25
|
readonly sql: Schema.$Array<Schema.String>;
|
|
26
26
|
}>;
|
|
27
27
|
export type MigrationStep = typeof MigrationStep.Type;
|
|
28
|
-
export declare const MIGRATION_REFUSAL_CODES: readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict"];
|
|
28
|
+
export declare const MIGRATION_REFUSAL_CODES: readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict", "replication-rebuild-required"];
|
|
29
29
|
export type MigrationRefusalCode = (typeof MIGRATION_REFUSAL_CODES)[number];
|
|
30
30
|
/** A change the planner will not make. A plan with refusals cannot be applied. */
|
|
31
31
|
export declare const MigrationRefusal: Schema.Struct<{
|
|
32
|
-
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict"]>;
|
|
32
|
+
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict", "replication-rebuild-required"]>;
|
|
33
33
|
/** Where, in the style of `check` diagnostics: `lead.properties.status`. */
|
|
34
34
|
readonly path: Schema.String;
|
|
35
35
|
readonly message: Schema.String;
|
|
@@ -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<{
|
|
@@ -284,7 +304,7 @@ export declare const MigrationPlan: Schema.Struct<{
|
|
|
284
304
|
readonly sql: Schema.$Array<Schema.String>;
|
|
285
305
|
}>>;
|
|
286
306
|
readonly refusals: Schema.$Array<Schema.Struct<{
|
|
287
|
-
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict"]>;
|
|
307
|
+
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict", "replication-rebuild-required"]>;
|
|
288
308
|
/** Where, in the style of `check` diagnostics: `lead.properties.status`. */
|
|
289
309
|
readonly path: Schema.String;
|
|
290
310
|
readonly message: Schema.String;
|
package/dist/migrate/model.js
CHANGED
|
@@ -55,6 +55,7 @@ export const MIGRATION_REFUSAL_CODES = [
|
|
|
55
55
|
"name-collision",
|
|
56
56
|
"identifier-too-long",
|
|
57
57
|
"rename-conflict",
|
|
58
|
+
"replication-rebuild-required",
|
|
58
59
|
];
|
|
59
60
|
/** A change the planner will not make. A plan with refusals cannot be applied. */
|
|
60
61
|
export const MigrationRefusal = 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 {};
|