@segmentstream-cloud/ontology 0.21.0 → 0.22.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/dist/migrate/apply.js +24 -13
- package/dist/migrate/errors.d.ts +1 -1
- package/dist/migrate/model.d.ts +3 -3
- package/dist/migrate/model.js +1 -0
- 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/package.json +1 -1
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;
|
|
@@ -284,7 +284,7 @@ export declare const MigrationPlan: Schema.Struct<{
|
|
|
284
284
|
readonly sql: Schema.$Array<Schema.String>;
|
|
285
285
|
}>>;
|
|
286
286
|
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"]>;
|
|
287
|
+
readonly code: Schema.Literals<readonly ["invalid-manifest", "type-change", "relationship-change", "drop-refused", "name-collision", "identifier-too-long", "rename-conflict", "replication-rebuild-required"]>;
|
|
288
288
|
/** Where, in the style of `check` diagnostics: `lead.properties.status`. */
|
|
289
289
|
readonly path: Schema.String;
|
|
290
290
|
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({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
2
|
import type { SqlClient } from "effect/unstable/sql";
|
|
3
3
|
import type { OntologyManifest } from "../manifest.js";
|
|
4
4
|
import { type ReplicationSelection, type ReplicationExclusion } from "./model.js";
|
|
@@ -9,6 +9,13 @@ export interface ReplicationTarget {
|
|
|
9
9
|
readonly manifest: OntologyManifest;
|
|
10
10
|
readonly selection: ReadonlyArray<ReplicationSelection>;
|
|
11
11
|
}
|
|
12
|
+
declare const ReplicationBaselineFailed_base: Schema.Class<ReplicationBaselineFailed, Schema.TaggedStruct<"ReplicationBaselineFailed", {
|
|
13
|
+
readonly message: Schema.String;
|
|
14
|
+
readonly cause: Schema.optionalKey<Schema.Unknown>;
|
|
15
|
+
}>, import("effect/Cause").YieldableError>;
|
|
16
|
+
/** An initial snapshot cannot be created with the requested state or record projection. */
|
|
17
|
+
export declare class ReplicationBaselineFailed extends ReplicationBaselineFailed_base {
|
|
18
|
+
}
|
|
12
19
|
/** All baseline rows are committed with capture enablement; interrupted setup can safely resume. */
|
|
13
20
|
export declare const createReplicationBaseline: (sql: SqlClient.SqlClient, input: {
|
|
14
21
|
readonly generation: string;
|
|
@@ -16,4 +23,5 @@ export declare const createReplicationBaseline: (sql: SqlClient.SqlClient, input
|
|
|
16
23
|
readonly exclusions?: ReadonlyArray<ReplicationExclusion>;
|
|
17
24
|
readonly selection: ReadonlyArray<ReplicationSelection>;
|
|
18
25
|
readonly newSource?: boolean;
|
|
19
|
-
}) => Effect.Effect<ReplicationTarget, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
26
|
+
}) => Effect.Effect<ReplicationTarget, ReplicationBaselineFailed | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
27
|
+
export {};
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { Effect } from "effect";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
3
|
import { joinColumnNames, joinTableName, quoteIdentifier as qi, quoteLiteral as ql, tableName, } from "../migrate/storage.js";
|
|
4
4
|
import { REPLICATION_RECORD_BYTES, validateReplicationSelection, } from "./model.js";
|
|
5
5
|
import { snapshotExpression } from "./schema.js";
|
|
6
|
-
|
|
6
|
+
/** An initial snapshot cannot be created with the requested state or record projection. */
|
|
7
|
+
export class ReplicationBaselineFailed extends Schema.TaggedError()("ReplicationBaselineFailed", { message: Schema.String, cause: Schema.optionalKey(Schema.Unknown) }) {
|
|
8
|
+
}
|
|
7
9
|
/** All baseline rows are committed with capture enablement; interrupted setup can safely resume. */
|
|
8
10
|
export const createReplicationBaseline = (sql, input) => sql.withTransaction(Effect.gen(function* () {
|
|
9
11
|
yield* sql.unsafe("SET LOCAL statement_timeout = '30s'");
|
|
10
12
|
yield* sql.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
11
13
|
const [state] = yield* sql.unsafe("SELECT source_id::text, revision::text, enabled FROM _ontology_replication WHERE id FOR UPDATE");
|
|
12
14
|
if (!state) {
|
|
13
|
-
return yield*
|
|
15
|
+
return yield* new ReplicationBaselineFailed({
|
|
16
|
+
message: "Run the ontology SDK migration before enabling replication.",
|
|
17
|
+
});
|
|
14
18
|
}
|
|
15
19
|
const existing = yield* sql.unsafe("SELECT * FROM _ontology_replication_targets WHERE generation = $1::uuid", [input.generation]);
|
|
16
20
|
if (existing[0]) {
|
|
17
21
|
return existing[0];
|
|
18
22
|
}
|
|
19
23
|
if (state.enabled) {
|
|
20
|
-
return yield*
|
|
24
|
+
return yield* new ReplicationBaselineFailed({
|
|
25
|
+
message: "Pause replication before starting a new generation.",
|
|
26
|
+
});
|
|
21
27
|
}
|
|
22
28
|
const manifest = input.manifest;
|
|
23
|
-
yield* Effect.
|
|
29
|
+
yield* Effect.try({
|
|
30
|
+
try: () => validateReplicationSelection(manifest, input.selection),
|
|
31
|
+
catch: (cause) => new ReplicationBaselineFailed({ message: "Invalid replication projection.", cause }),
|
|
32
|
+
});
|
|
24
33
|
// Drain older SDK transactions that started before the compatibility fence was enabled.
|
|
25
34
|
const tables = manifest.entities
|
|
26
35
|
.filter((e) => e.source === undefined && !e.builtIn)
|
|
@@ -57,17 +66,17 @@ export const createReplicationBaseline = (sql, input) => sql.withTransaction(Eff
|
|
|
57
66
|
JSON.stringify(manifest),
|
|
58
67
|
JSON.stringify(input.selection),
|
|
59
68
|
]);
|
|
60
|
-
let total = 0;
|
|
61
69
|
const baseline = (entityId, kind, records) => Effect.gen(function* () {
|
|
62
70
|
// Keep the snapshot in PostgreSQL; large baselines must not fill a serverless process's heap.
|
|
63
|
-
const [size] = yield* sql.unsafe(`SELECT
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
const [size] = yield* sql.unsafe(`SELECT EXISTS (
|
|
72
|
+
SELECT 1 FROM (${records}) snapshot
|
|
73
|
+
WHERE octet_length(payload::text) > ${REPLICATION_RECORD_BYTES}
|
|
74
|
+
) AS oversized`);
|
|
75
|
+
if (size.oversized) {
|
|
76
|
+
return yield* new ReplicationBaselineFailed({
|
|
77
|
+
message: `A projected baseline record exceeds ${REPLICATION_RECORD_BYTES} bytes; ` +
|
|
78
|
+
"exclude large fields before enabling replication.",
|
|
79
|
+
});
|
|
71
80
|
}
|
|
72
81
|
yield* sql.unsafe(`INSERT INTO _ontology_replication_baselines (generation, event)
|
|
73
82
|
SELECT $1::uuid, jsonb_build_object('source_id', $2::text, 'entity_id', $3::text,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OntologyManifest } from "../manifest.js";
|
|
2
|
+
import type { MigrationRefusal } from "../migrate/model.js";
|
|
3
|
+
import type { ReplicationTarget } from "./baseline.js";
|
|
4
|
+
import type { ReplicationSelection } from "./model.js";
|
|
5
|
+
/** A live stream can change its projection only when retained JSON still describes current rows. */
|
|
6
|
+
export declare function replicationMigrationRefusals(current: ReplicationTarget, manifest: OntologyManifest, selection: ReadonlyArray<ReplicationSelection>): ReadonlyArray<MigrationRefusal>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { toSnakeCase } from "../names.js";
|
|
2
|
+
import { queryFields } from "../query.js";
|
|
3
|
+
/** A live stream can change its projection only when retained JSON still describes current rows. */
|
|
4
|
+
export function replicationMigrationRefusals(current, manifest, selection) {
|
|
5
|
+
const refusals = [];
|
|
6
|
+
const refuse = (path, reason) => {
|
|
7
|
+
refusals.push({
|
|
8
|
+
code: "replication-rebuild-required",
|
|
9
|
+
path,
|
|
10
|
+
message: `${reason} Pause replication, apply the migration, then explicitly rebuild replication.`,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
for (const previous of current.selection) {
|
|
14
|
+
const entity = current.manifest.entities.find((item) => item.id === previous.entityId);
|
|
15
|
+
const next = selection.find((item) => item.entityId === entity.id);
|
|
16
|
+
if (!next) {
|
|
17
|
+
refuse(entity.name, "Removing a replicated entity would discard its pending history.");
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
for (const field of previous.fields) {
|
|
21
|
+
if (!next.fields.includes(field)) {
|
|
22
|
+
refuse(`${entity.name}.${field}`, "Removing or renaming a replicated field would leave retained JSON under its old name.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const relationship of previous.relationships ?? []) {
|
|
26
|
+
if (!next.relationships?.includes(relationship)) {
|
|
27
|
+
refuse(`${entity.name}.${relationship}`, "Removing a replicated relationship would leave its old edges in retained JSON.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const next of selection) {
|
|
32
|
+
const entity = manifest.entities.find((item) => item.id === next.entityId);
|
|
33
|
+
const previousEntity = current.manifest.entities.find((item) => item.id === entity.id);
|
|
34
|
+
// A newly created table starts empty and all subsequent writes are captured normally.
|
|
35
|
+
if (!previousEntity) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const previous = current.selection.find((item) => item.entityId === entity.id);
|
|
39
|
+
if (!previous) {
|
|
40
|
+
refuse(entity.name, "Previously excluded records are absent from retained JSON.");
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const previousFields = new Set(queryFields(previousEntity).map((field) => field.name));
|
|
44
|
+
for (const field of next.fields.filter((name) => !previous.fields.includes(name))) {
|
|
45
|
+
const property = entity.properties.find((item) => item.name === field);
|
|
46
|
+
const renamed = property?.renamedFrom;
|
|
47
|
+
if (previousFields.has(field) ||
|
|
48
|
+
(renamed !== undefined &&
|
|
49
|
+
(previousFields.has(renamed) || previousFields.has(toSnakeCase(renamed))))) {
|
|
50
|
+
refuse(`${entity.name}.${field}`, "Existing values are absent from this JSON field.");
|
|
51
|
+
}
|
|
52
|
+
else if (property?.default !== undefined && property.default !== null) {
|
|
53
|
+
refuse(`${entity.name}.${field}`, "Adding a field with a non-null default changes existing rows without captured writes.");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const relationship of next.relationships ?? []) {
|
|
57
|
+
if (!previous.relationships?.includes(relationship) &&
|
|
58
|
+
previousEntity.relationships.some((item) => item.name === relationship)) {
|
|
59
|
+
refuse(`${entity.name}.${relationship}`, "Previously excluded relationship edges are absent from retained JSON.");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return refusals;
|
|
64
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type OntologyManifest } from "../manifest.js";
|
|
2
|
-
/** The
|
|
2
|
+
/** The current projection of a continuous replication stream. `id` is always included. */
|
|
3
3
|
export interface ReplicationSelection {
|
|
4
4
|
readonly entityId: string;
|
|
5
5
|
readonly fields: ReadonlyArray<string>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import type { SqlClient } from "effect/unstable/sql";
|
|
3
3
|
import { type ReplicationEvent, type ReplicationSelection, type ReplicationExclusion } from "./model.js";
|
|
4
|
-
import { type ReplicationTarget } from "./baseline.js";
|
|
4
|
+
import { ReplicationBaselineFailed, type ReplicationTarget } from "./baseline.js";
|
|
5
5
|
export type { ReplicationTarget } from "./baseline.js";
|
|
6
6
|
export declare const startReplication: (sql: SqlClient.SqlClient, input: {
|
|
7
7
|
readonly generation: string;
|
|
8
8
|
readonly exclusions?: ReadonlyArray<ReplicationExclusion>;
|
|
9
9
|
readonly selection?: ReadonlyArray<ReplicationSelection>;
|
|
10
10
|
readonly newSource?: boolean;
|
|
11
|
-
}) => Effect.Effect<ReplicationTarget, import("../index.js").CatalogInvalid | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
11
|
+
}) => Effect.Effect<ReplicationTarget, import("../index.js").CatalogInvalid | ReplicationBaselineFailed | import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
12
12
|
export declare const activeReplicationTarget: (sql: SqlClient.SqlClient) => Effect.Effect<ReplicationTarget, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
13
13
|
export declare const pauseReplication: (sql: SqlClient.SqlClient) => import("effect/unstable/sql/Statement").Statement<object>;
|
|
14
14
|
export declare const replicationTarget: (sql: SqlClient.SqlClient, generation: string) => Effect.Effect<ReplicationTarget, import("effect/unstable/sql/SqlError").SqlError, never>;
|
|
@@ -2,17 +2,22 @@ import { Effect } from "effect";
|
|
|
2
2
|
import { readCatalog } from "../migrate/apply.js";
|
|
3
3
|
import { historyTableName, quoteIdentifier as qi } from "../migrate/storage.js";
|
|
4
4
|
import { REPLICATION_BATCH_BYTES, REPLICATION_BATCH_SIZE, publishMessage, replicationEventId, resolveReplicationSelection, } from "./model.js";
|
|
5
|
-
import { createReplicationBaseline } from "./baseline.js";
|
|
5
|
+
import { createReplicationBaseline, ReplicationBaselineFailed, } from "./baseline.js";
|
|
6
6
|
export const startReplication = (sql, input) => sql.withTransaction(Effect.gen(function* () {
|
|
7
7
|
yield* sql.unsafe("SET LOCAL statement_timeout = '30s'");
|
|
8
8
|
yield* sql.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
9
9
|
yield* sql.unsafe("SELECT id FROM _ontology_replication WHERE id FOR UPDATE");
|
|
10
10
|
const manifest = yield* readCatalog({ sql });
|
|
11
11
|
if (!manifest) {
|
|
12
|
-
return yield*
|
|
12
|
+
return yield* new ReplicationBaselineFailed({
|
|
13
|
+
message: "The ontology has not been migrated.",
|
|
14
|
+
});
|
|
13
15
|
}
|
|
14
16
|
const selection = input.selection ??
|
|
15
|
-
(yield* Effect.
|
|
17
|
+
(yield* Effect.try({
|
|
18
|
+
try: () => resolveReplicationSelection(manifest, input.exclusions),
|
|
19
|
+
catch: (cause) => new ReplicationBaselineFailed({ message: "Invalid replication exclusions.", cause }),
|
|
20
|
+
}));
|
|
16
21
|
return yield* createReplicationBaseline(sql, { ...input, manifest, selection });
|
|
17
22
|
}));
|
|
18
23
|
export const activeReplicationTarget = (sql) => sql
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/ontology",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Define a SegmentStream workspace app's ontology: entities, relationships, roles and actions, written with Effect.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|