@objectstack/metadata 17.2.0 → 17.4.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/CHANGELOG.md +1967 -0
- package/README.md +6 -4
- package/dist/errors.cjs +1 -85
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +1 -22
- package/dist/errors.d.ts +1 -22
- package/dist/errors.js +2 -84
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +1014 -302
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +496 -29
- package/dist/index.d.ts +496 -29
- package/dist/index.js +1013 -299
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.cjs +175 -67
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +97 -15
- package/dist/migrations/index.d.ts +97 -15
- package/dist/migrations/index.js +178 -67
- package/dist/migrations/index.js.map +1 -1
- package/dist/node.cjs +1014 -302
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +156 -3
- package/dist/node.d.ts +156 -3
- package/dist/node.js +1013 -299
- package/dist/node.js.map +1 -1
- package/dist/view-container.cjs +37 -0
- package/dist/view-container.cjs.map +1 -0
- package/dist/view-container.d.cts +76 -0
- package/dist/view-container.d.ts +76 -0
- package/dist/view-container.js +12 -0
- package/dist/view-container.js.map +1 -0
- package/package.json +52 -21
|
@@ -27,8 +27,10 @@ interface MigrationResult {
|
|
|
27
27
|
/**
|
|
28
28
|
* Rename `env_id` → `project_id` on all metadata tables.
|
|
29
29
|
*
|
|
30
|
-
* @param driver An IDataDriver with access to the target database.
|
|
31
|
-
*
|
|
30
|
+
* @param driver An IDataDriver with access to the target database. Raw SQL is
|
|
31
|
+
* issued through the surface `IDataDriver` declares —
|
|
32
|
+
* `execute(sql, bindings?)` — falling back to
|
|
33
|
+
* `raw(sql, bindings?)`; see `./driver-exec.ts`.
|
|
32
34
|
* @returns Per-table migration results.
|
|
33
35
|
*/
|
|
34
36
|
declare function migrateEnvIdToProjectId(driver: IDataDriver): Promise<MigrationResult[]>;
|
|
@@ -36,10 +38,9 @@ declare function migrateEnvIdToProjectId(driver: IDataDriver): Promise<Migration
|
|
|
36
38
|
/**
|
|
37
39
|
* Migration: project_id → environment_id
|
|
38
40
|
*
|
|
39
|
-
* Renames the `project_id` column to `environment_id` on the metadata
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* - sys_metadata_history
|
|
41
|
+
* Renames the `project_id` column to `environment_id` on the metadata storage
|
|
42
|
+
* tables — but only on the tables whose CURRENT declaration actually knows
|
|
43
|
+
* `environment_id`.
|
|
43
44
|
*
|
|
44
45
|
* Forward counterpart of {@link migrateEnvIdToProjectId} (which performed the
|
|
45
46
|
* earlier `env_id → project_id` rename). Together they let an operator walk an
|
|
@@ -48,6 +49,35 @@ declare function migrateEnvIdToProjectId(driver: IDataDriver): Promise<Migration
|
|
|
48
49
|
* migrateEnvIdToProjectId(driver); // env_id → project_id (legacy)
|
|
49
50
|
* migrateProjectIdToEnvironmentId(driver); // project_id → environment_id (v5)
|
|
50
51
|
*
|
|
52
|
+
* ─────────────────────────────────────────────────────────────────────
|
|
53
|
+
* Why the table list is DERIVED and not written out (#13205)
|
|
54
|
+
*
|
|
55
|
+
* This migration is the terminal step of that chain: its target column is
|
|
56
|
+
* the CURRENT declared shape, so "should this table be renamed?" is not an
|
|
57
|
+
* independent fact — it is `does this object still declare environment_id?`.
|
|
58
|
+
* Written out by hand, the two drifted apart: `sys_metadata_history` stayed
|
|
59
|
+
* on the list after the branch/project-removal amendment (M1) removed
|
|
60
|
+
* `environment_id` from its declaration, so against a database whose
|
|
61
|
+
* physical `sys_metadata_history` still carried `project_id` this migration
|
|
62
|
+
* renamed it to a column NO declaration knows about — minting exactly the
|
|
63
|
+
* orphan column class the metadata drift audit exists to remove.
|
|
64
|
+
*
|
|
65
|
+
* The old guard could not catch it: the loop gates on `project_id` existing
|
|
66
|
+
* PHYSICALLY (`_columnExists`), which says nothing about the target column
|
|
67
|
+
* being DECLARED. So the list is now computed from the declarations in
|
|
68
|
+
* `@objectstack/metadata-core` (already a dependency of this package — no
|
|
69
|
+
* new edge), and a candidate that does not declare the target column is
|
|
70
|
+
* reported as `skipped_not_declared` rather than dropped silently: an
|
|
71
|
+
* operator reading the result sees the table was considered and why nothing
|
|
72
|
+
* happened, instead of having to guess whether it was forgotten again.
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ The sibling `migrate-env-id-to-project-id.ts` is deliberately NOT
|
|
75
|
+
* changed this way. Its target (`project_id`) is an INTERMEDIATE column that
|
|
76
|
+
* no current declaration carries by design — gating it on today's
|
|
77
|
+
* declarations would disable the chain's first step entirely. The rule
|
|
78
|
+
* "target must be declared" is sound only for the terminal migration.
|
|
79
|
+
* ─────────────────────────────────────────────────────────────────────
|
|
80
|
+
*
|
|
51
81
|
* (The per-type projection tables `sys_object` / `sys_view` / `sys_flow` /
|
|
52
82
|
* `sys_agent` / `sys_tool` were removed in 2026-05 along with the projection
|
|
53
83
|
* pipeline — see ADR 0005 addendum. They are intentionally not included.)
|
|
@@ -63,15 +93,24 @@ declare function migrateEnvIdToProjectId(driver: IDataDriver): Promise<Migration
|
|
|
63
93
|
|
|
64
94
|
interface ProjectIdToEnvironmentIdResult {
|
|
65
95
|
table: string;
|
|
66
|
-
|
|
96
|
+
/**
|
|
97
|
+
* `skipped_not_declared` — the table is a known metadata storage table, but
|
|
98
|
+
* its current declaration has no `environment_id`, so renaming into it
|
|
99
|
+
* would create a column nothing declares. Nothing was executed.
|
|
100
|
+
*/
|
|
101
|
+
status: 'renamed' | 'already_done' | 'table_missing' | 'skipped_not_declared' | 'error';
|
|
67
102
|
error?: string;
|
|
68
103
|
}
|
|
69
104
|
/**
|
|
70
|
-
* Rename `project_id` → `environment_id` on all metadata tables
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
105
|
+
* Rename `project_id` → `environment_id` on all metadata tables that still
|
|
106
|
+
* declare `environment_id`.
|
|
107
|
+
*
|
|
108
|
+
* @param driver An IDataDriver with access to the target database. Raw SQL is
|
|
109
|
+
* issued through the surface `IDataDriver` declares —
|
|
110
|
+
* `execute(sql, bindings?)` — falling back to
|
|
111
|
+
* `raw(sql, bindings?)`; see `./driver-exec.ts`.
|
|
112
|
+
* @returns Per-table migration results — one entry per candidate table,
|
|
113
|
+
* including the ones skipped for lacking the declared target.
|
|
75
114
|
*/
|
|
76
115
|
declare function migrateProjectIdToEnvironmentId(driver: IDataDriver): Promise<ProjectIdToEnvironmentIdResult[]>;
|
|
77
116
|
|
|
@@ -100,7 +139,9 @@ interface DropProjectionResult {
|
|
|
100
139
|
/**
|
|
101
140
|
* Drop the deprecated per-type metadata projection tables.
|
|
102
141
|
*
|
|
103
|
-
* @param driver An `IDataDriver
|
|
142
|
+
* @param driver An `IDataDriver`. Raw SQL is issued through the surface
|
|
143
|
+
* `IDataDriver` declares — `execute(sql, bindings?)` — falling
|
|
144
|
+
* back to `raw(sql, bindings?)`; see `./driver-exec.ts`.
|
|
104
145
|
* @returns Per-table results.
|
|
105
146
|
*/
|
|
106
147
|
declare function dropProjectionTables(driver: IDataDriver): Promise<DropProjectionResult[]>;
|
|
@@ -132,16 +173,57 @@ declare function dropProjectionTables(driver: IDataDriver): Promise<DropProjecti
|
|
|
132
173
|
* await migrateSysNotificationToEvent({ driver, data });
|
|
133
174
|
*
|
|
134
175
|
* `driver` provides raw access to read legacy columns the re-modeled schema no
|
|
135
|
-
* longer projects and to clear them
|
|
176
|
+
* longer projects and to clear them — through the surface `IDataDriver`
|
|
177
|
+
* declares, `execute(sql, bindings?)`, falling back to `raw(sql, bindings?)`
|
|
178
|
+
* (see `./driver-exec.ts`); `data` (IDataEngine) performs the
|
|
136
179
|
* structured inbox/receipt writes and the event rewrite so ids, JSON fields and
|
|
137
180
|
* tenant stamping are handled uniformly across drivers.
|
|
181
|
+
*
|
|
182
|
+
* A completed run also records itself in the `sys_migration` deployment ledger
|
|
183
|
+
* under `NOTIFICATION_EVENT_MIGRATION_ID`, per the ruled claim matrix carried
|
|
184
|
+
* on that constant (#16100) — see "The run receipt" below. That row is a
|
|
185
|
+
* RECEIPT an operator reads, never a gate.
|
|
138
186
|
*/
|
|
139
187
|
|
|
188
|
+
/**
|
|
189
|
+
* What one run recorded in the `sys_migration` deployment ledger (#16100).
|
|
190
|
+
*
|
|
191
|
+
* This directory reports to its CALLER and to nobody else — no module under
|
|
192
|
+
* `packages/metadata/src/migrations` takes a logger — so the ledger claim's
|
|
193
|
+
* own fate is reported the same way the migration's is. That is also the third
|
|
194
|
+
* legal answer to AGENTS.md's degradation rule: a failure handed to the caller
|
|
195
|
+
* does not "look normal from the outside", because the caller was told.
|
|
196
|
+
*
|
|
197
|
+
* - `inserted` / `updated` — the claim landed, as a new row or over the row
|
|
198
|
+
* that was already there.
|
|
199
|
+
* - `not-claimed` — nothing was owed. An `error` run writes no ledger claim
|
|
200
|
+
* at all (the ruled matrix), so this is the correct, complete outcome for
|
|
201
|
+
* it and never a failure.
|
|
202
|
+
* - `no-ledger` — a claim was owed and there is nowhere to put it: the host
|
|
203
|
+
* is not an engine that carries the ledger, or `sys_migration` is not
|
|
204
|
+
* registered on this kernel. `reason` says which.
|
|
205
|
+
* - `failed` — a claim was owed, the write was attempted, and it threw. The
|
|
206
|
+
* data migration itself still did what `status` says it did; what is
|
|
207
|
+
* missing is the durable record that it ran. `reason` carries the error.
|
|
208
|
+
*/
|
|
209
|
+
interface SysNotificationMigrationReceipt {
|
|
210
|
+
outcome: 'inserted' | 'updated' | 'not-claimed' | 'no-ledger' | 'failed';
|
|
211
|
+
/** Why no claim landed — present on `no-ledger` and `failed` only. */
|
|
212
|
+
reason?: string;
|
|
213
|
+
}
|
|
140
214
|
interface SysNotificationMigrationResult {
|
|
141
215
|
status: 'migrated' | 'already_done' | 'not_applicable' | 'error';
|
|
142
216
|
/** Number of legacy rows split into inbox + receipt + event. */
|
|
143
217
|
migrated: number;
|
|
144
218
|
error?: string;
|
|
219
|
+
/**
|
|
220
|
+
* What this run claimed in the `sys_migration` ledger under
|
|
221
|
+
* {@link NOTIFICATION_EVENT_MIGRATION_ID}. Always present: writing the
|
|
222
|
+
* receipt is part of what a run DOES, and a caller that cannot tell "the
|
|
223
|
+
* claim landed" from "the claim was never attempted" is the unanswerable
|
|
224
|
+
* state the ledger row exists to remove.
|
|
225
|
+
*/
|
|
226
|
+
receipt: SysNotificationMigrationReceipt;
|
|
145
227
|
}
|
|
146
228
|
interface SysNotificationMigrationOptions {
|
|
147
229
|
driver: IDataDriver;
|
|
@@ -151,4 +233,4 @@ interface SysNotificationMigrationOptions {
|
|
|
151
233
|
}
|
|
152
234
|
declare function migrateSysNotificationToEvent(opts: SysNotificationMigrationOptions): Promise<SysNotificationMigrationResult>;
|
|
153
235
|
|
|
154
|
-
export { type DropProjectionResult, type MigrationResult, type ProjectIdToEnvironmentIdResult, type SysNotificationMigrationOptions, type SysNotificationMigrationResult, dropProjectionTables, migrateEnvIdToProjectId, migrateProjectIdToEnvironmentId, migrateSysNotificationToEvent };
|
|
236
|
+
export { type DropProjectionResult, type MigrationResult, type ProjectIdToEnvironmentIdResult, type SysNotificationMigrationOptions, type SysNotificationMigrationReceipt, type SysNotificationMigrationResult, dropProjectionTables, migrateEnvIdToProjectId, migrateProjectIdToEnvironmentId, migrateSysNotificationToEvent };
|
package/dist/migrations/index.js
CHANGED
|
@@ -1,20 +1,34 @@
|
|
|
1
|
+
// src/migrations/driver-exec.ts
|
|
2
|
+
function resolveDriverExec(driver) {
|
|
3
|
+
const candidate = driver;
|
|
4
|
+
if (!candidate) return void 0;
|
|
5
|
+
if (typeof candidate.execute === "function") {
|
|
6
|
+
return (sql, bindings) => candidate.execute(sql, bindings ? [...bindings] : []);
|
|
7
|
+
}
|
|
8
|
+
if (typeof candidate.raw === "function") {
|
|
9
|
+
return (sql, bindings) => candidate.raw(sql, bindings ? [...bindings] : []);
|
|
10
|
+
}
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
function driverExecRefusal(helper) {
|
|
14
|
+
return `${helper}: driver must expose an .execute(sql, bindings?) or .raw(sql, bindings?) method. SqlDriver (better-sqlite3/knex) exposes .execute(), as does its SqliteWasmDriver subclass; cloud-side TursoDriver also conforms.`;
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
// src/migrations/migrate-env-id-to-project-id.ts
|
|
2
18
|
var AFFECTED_TABLES = [
|
|
3
19
|
"sys_metadata",
|
|
4
20
|
"sys_metadata_history"
|
|
5
21
|
];
|
|
6
22
|
async function migrateEnvIdToProjectId(driver) {
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
9
|
-
throw new Error(
|
|
10
|
-
"migrateEnvIdToProjectId: driver must expose a .raw(sql, bindings?) method. SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms."
|
|
11
|
-
);
|
|
23
|
+
const exec = resolveDriverExec(driver);
|
|
24
|
+
if (!exec) {
|
|
25
|
+
throw new Error(driverExecRefusal("migrateEnvIdToProjectId"));
|
|
12
26
|
}
|
|
13
27
|
const results = [];
|
|
14
28
|
for (const table of AFFECTED_TABLES) {
|
|
15
29
|
try {
|
|
16
|
-
const hasColumn = await _columnExists(
|
|
17
|
-
const alreadyMigrated = await _columnExists(
|
|
30
|
+
const hasColumn = await _columnExists(exec, table, "env_id");
|
|
31
|
+
const alreadyMigrated = await _columnExists(exec, table, "project_id");
|
|
18
32
|
if (alreadyMigrated && !hasColumn) {
|
|
19
33
|
results.push({ table, status: "already_done" });
|
|
20
34
|
continue;
|
|
@@ -23,7 +37,7 @@ async function migrateEnvIdToProjectId(driver) {
|
|
|
23
37
|
results.push({ table, status: "table_missing" });
|
|
24
38
|
continue;
|
|
25
39
|
}
|
|
26
|
-
await
|
|
40
|
+
await exec(`ALTER TABLE "${table}" RENAME COLUMN env_id TO project_id`);
|
|
27
41
|
results.push({ table, status: "renamed" });
|
|
28
42
|
} catch (err) {
|
|
29
43
|
results.push({ table, status: "error", error: err?.message ?? String(err) });
|
|
@@ -31,14 +45,14 @@ async function migrateEnvIdToProjectId(driver) {
|
|
|
31
45
|
}
|
|
32
46
|
return results;
|
|
33
47
|
}
|
|
34
|
-
async function _columnExists(
|
|
48
|
+
async function _columnExists(exec, table, column) {
|
|
35
49
|
try {
|
|
36
|
-
const rows = await
|
|
50
|
+
const rows = await exec(`PRAGMA table_info("${table}")`);
|
|
37
51
|
if (Array.isArray(rows) && rows.length > 0) {
|
|
38
52
|
const list2 = Array.isArray(rows[0]) ? rows[0] : rows;
|
|
39
53
|
return list2.some((r) => r?.name === column);
|
|
40
54
|
}
|
|
41
|
-
const result = await
|
|
55
|
+
const result = await exec(
|
|
42
56
|
`SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,
|
|
43
57
|
[table, column]
|
|
44
58
|
);
|
|
@@ -50,22 +64,29 @@ async function _columnExists(driver, table, column) {
|
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
// src/migrations/migrate-project-id-to-environment-id.ts
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
];
|
|
67
|
+
import { SysMetadataObject, SysMetadataHistoryObject } from "@objectstack/metadata-core";
|
|
68
|
+
var SOURCE_COLUMN = "project_id";
|
|
69
|
+
var TARGET_COLUMN = "environment_id";
|
|
70
|
+
var CANDIDATE_OBJECTS = [SysMetadataObject, SysMetadataHistoryObject];
|
|
71
|
+
function declaresColumn(object, column) {
|
|
72
|
+
return Object.prototype.hasOwnProperty.call(object.fields ?? {}, column);
|
|
73
|
+
}
|
|
74
|
+
var CANDIDATE_TABLES = CANDIDATE_OBJECTS.map((o) => o.name);
|
|
75
|
+
var AFFECTED_TABLES2 = CANDIDATE_OBJECTS.filter((o) => declaresColumn(o, TARGET_COLUMN)).map((o) => o.name);
|
|
57
76
|
async function migrateProjectIdToEnvironmentId(driver) {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
throw new Error(
|
|
61
|
-
"migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms."
|
|
62
|
-
);
|
|
77
|
+
const exec = resolveDriverExec(driver);
|
|
78
|
+
if (!exec) {
|
|
79
|
+
throw new Error(driverExecRefusal("migrateProjectIdToEnvironmentId"));
|
|
63
80
|
}
|
|
64
81
|
const results = [];
|
|
65
|
-
for (const table of
|
|
82
|
+
for (const table of CANDIDATE_TABLES) {
|
|
83
|
+
if (!AFFECTED_TABLES2.includes(table)) {
|
|
84
|
+
results.push({ table, status: "skipped_not_declared" });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
66
87
|
try {
|
|
67
|
-
const hasColumn = await _columnExists2(
|
|
68
|
-
const alreadyMigrated = await _columnExists2(
|
|
88
|
+
const hasColumn = await _columnExists2(exec, table, SOURCE_COLUMN);
|
|
89
|
+
const alreadyMigrated = await _columnExists2(exec, table, TARGET_COLUMN);
|
|
69
90
|
if (alreadyMigrated && !hasColumn) {
|
|
70
91
|
results.push({ table, status: "already_done" });
|
|
71
92
|
continue;
|
|
@@ -74,8 +95,8 @@ async function migrateProjectIdToEnvironmentId(driver) {
|
|
|
74
95
|
results.push({ table, status: "table_missing" });
|
|
75
96
|
continue;
|
|
76
97
|
}
|
|
77
|
-
await
|
|
78
|
-
`ALTER TABLE "${table}" RENAME COLUMN
|
|
98
|
+
await exec(
|
|
99
|
+
`ALTER TABLE "${table}" RENAME COLUMN ${SOURCE_COLUMN} TO ${TARGET_COLUMN}`
|
|
79
100
|
);
|
|
80
101
|
results.push({ table, status: "renamed" });
|
|
81
102
|
} catch (err) {
|
|
@@ -84,14 +105,14 @@ async function migrateProjectIdToEnvironmentId(driver) {
|
|
|
84
105
|
}
|
|
85
106
|
return results;
|
|
86
107
|
}
|
|
87
|
-
async function _columnExists2(
|
|
108
|
+
async function _columnExists2(exec, table, column) {
|
|
88
109
|
try {
|
|
89
|
-
const rows = await
|
|
110
|
+
const rows = await exec(`PRAGMA table_info("${table}")`);
|
|
90
111
|
if (Array.isArray(rows) && rows.length > 0) {
|
|
91
112
|
const list2 = Array.isArray(rows[0]) ? rows[0] : rows;
|
|
92
113
|
return list2.some((r) => r?.name === column);
|
|
93
114
|
}
|
|
94
|
-
const result = await
|
|
115
|
+
const result = await exec(
|
|
95
116
|
`SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,
|
|
96
117
|
[table, column]
|
|
97
118
|
);
|
|
@@ -111,14 +132,14 @@ var DEPRECATED_TABLES = [
|
|
|
111
132
|
"sys_tool"
|
|
112
133
|
];
|
|
113
134
|
async function dropProjectionTables(driver) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
throw new Error("dropProjectionTables
|
|
135
|
+
const exec = resolveDriverExec(driver);
|
|
136
|
+
if (!exec) {
|
|
137
|
+
throw new Error(driverExecRefusal("dropProjectionTables"));
|
|
117
138
|
}
|
|
118
139
|
const results = [];
|
|
119
140
|
for (const table of DEPRECATED_TABLES) {
|
|
120
141
|
try {
|
|
121
|
-
await
|
|
142
|
+
await exec(`DROP TABLE IF EXISTS ${table}`);
|
|
122
143
|
results.push({ table, status: "dropped" });
|
|
123
144
|
} catch (error) {
|
|
124
145
|
results.push({
|
|
@@ -132,9 +153,14 @@ async function dropProjectionTables(driver) {
|
|
|
132
153
|
}
|
|
133
154
|
|
|
134
155
|
// src/migrations/migrate-sys-notification-to-event.ts
|
|
156
|
+
import {
|
|
157
|
+
DATA_MIGRATION_FLAG_OBJECT,
|
|
158
|
+
NOTIFICATION_EVENT_MIGRATION_ID
|
|
159
|
+
} from "@objectstack/spec/system";
|
|
135
160
|
var EVENT_OBJECT = "sys_notification";
|
|
136
161
|
var INBOX_OBJECT = "sys_inbox_message";
|
|
137
162
|
var RECEIPT_OBJECT = "sys_notification_receipt";
|
|
163
|
+
var HISTORICAL_IMPORT = { context: { preserveAudit: true } };
|
|
138
164
|
var LEGACY_COLUMNS = [
|
|
139
165
|
"recipient_id",
|
|
140
166
|
"type",
|
|
@@ -146,57 +172,70 @@ var LEGACY_COLUMNS = [
|
|
|
146
172
|
"read_at"
|
|
147
173
|
];
|
|
148
174
|
async function migrateSysNotificationToEvent(opts) {
|
|
149
|
-
const driver = opts.driver;
|
|
150
|
-
const { data } = opts;
|
|
151
175
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
152
|
-
|
|
176
|
+
const outcome = await runNotificationEventMigration(opts, now);
|
|
177
|
+
const receipt = await recordNotificationEventReceipt(opts.data, outcome.status, now());
|
|
178
|
+
return { ...outcome, receipt };
|
|
179
|
+
}
|
|
180
|
+
async function runNotificationEventMigration(opts, now) {
|
|
181
|
+
const { data } = opts;
|
|
182
|
+
const exec = resolveDriverExec(opts.driver);
|
|
183
|
+
if (!exec) {
|
|
153
184
|
return {
|
|
154
185
|
status: "error",
|
|
155
186
|
migrated: 0,
|
|
156
|
-
error: "migrateSysNotificationToEvent
|
|
187
|
+
error: driverExecRefusal("migrateSysNotificationToEvent")
|
|
157
188
|
};
|
|
158
189
|
}
|
|
159
|
-
if (!await columnExists(
|
|
190
|
+
if (!await columnExists(exec, EVENT_OBJECT, "recipient_id")) {
|
|
160
191
|
return { status: "not_applicable", migrated: 0 };
|
|
161
192
|
}
|
|
162
193
|
const presentLegacy = [];
|
|
163
194
|
for (const col of LEGACY_COLUMNS) {
|
|
164
|
-
if (await columnExists(
|
|
195
|
+
if (await columnExists(exec, EVENT_OBJECT, col)) presentLegacy.push(col);
|
|
165
196
|
}
|
|
166
197
|
let migrated = 0;
|
|
167
198
|
try {
|
|
168
|
-
const rows = await selectLegacyRows(
|
|
199
|
+
const rows = await selectLegacyRows(exec);
|
|
169
200
|
if (rows.length === 0) return { status: "already_done", migrated: 0 };
|
|
170
201
|
for (const row of rows) {
|
|
171
202
|
const id = String(row.id);
|
|
172
203
|
const recipientId = row.recipient_id != null ? String(row.recipient_id) : null;
|
|
173
204
|
if (!recipientId) continue;
|
|
174
205
|
const orgId = row.organization_id != null ? String(row.organization_id) : null;
|
|
175
|
-
const createdAt = row.created_at != null ?
|
|
206
|
+
const createdAt = row.created_at != null ? canonicalTimestampText(row.created_at) : now();
|
|
176
207
|
const title = row.title != null ? String(row.title) : row.type != null ? String(row.type) : "Notification";
|
|
177
208
|
const isRead = row.is_read === true || row.is_read === 1 || row.is_read === "1";
|
|
178
209
|
const eventTopic = row.type != null && String(row.type).length > 0 ? String(row.type) : "legacy";
|
|
179
|
-
await data.insert(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
await data.insert(
|
|
211
|
+
INBOX_OBJECT,
|
|
212
|
+
{
|
|
213
|
+
user_id: recipientId,
|
|
214
|
+
notification_id: id,
|
|
215
|
+
topic: eventTopic,
|
|
216
|
+
title,
|
|
217
|
+
body_md: row.body ?? null,
|
|
218
|
+
severity: "info",
|
|
219
|
+
action_url: row.url ?? null,
|
|
220
|
+
organization_id: orgId,
|
|
221
|
+
created_at: createdAt
|
|
222
|
+
},
|
|
223
|
+
HISTORICAL_IMPORT
|
|
224
|
+
);
|
|
225
|
+
await data.insert(
|
|
226
|
+
RECEIPT_OBJECT,
|
|
227
|
+
{
|
|
228
|
+
notification_id: id,
|
|
229
|
+
delivery_id: null,
|
|
230
|
+
user_id: recipientId,
|
|
231
|
+
channel: "inbox",
|
|
232
|
+
state: isRead ? "read" : "delivered",
|
|
233
|
+
at: isRead && row.read_at != null ? canonicalTimestampText(row.read_at) : createdAt,
|
|
234
|
+
organization_id: orgId,
|
|
235
|
+
created_at: createdAt
|
|
236
|
+
},
|
|
237
|
+
HISTORICAL_IMPORT
|
|
238
|
+
);
|
|
200
239
|
await data.update(
|
|
201
240
|
EVENT_OBJECT,
|
|
202
241
|
{
|
|
@@ -214,7 +253,7 @@ async function migrateSysNotificationToEvent(opts) {
|
|
|
214
253
|
);
|
|
215
254
|
if (presentLegacy.length > 0) {
|
|
216
255
|
const setClause = presentLegacy.map((c) => `"${c}" = NULL`).join(", ");
|
|
217
|
-
await
|
|
256
|
+
await exec(`UPDATE "${EVENT_OBJECT}" SET ${setClause} WHERE id = ?`, [id]);
|
|
218
257
|
}
|
|
219
258
|
migrated += 1;
|
|
220
259
|
}
|
|
@@ -223,8 +262,80 @@ async function migrateSysNotificationToEvent(opts) {
|
|
|
223
262
|
return { status: "error", migrated, error: err?.message ?? String(err) };
|
|
224
263
|
}
|
|
225
264
|
}
|
|
226
|
-
|
|
227
|
-
|
|
265
|
+
var LEDGER_CLAIM = {
|
|
266
|
+
migrated: { claims: true, appliesBackfill: true },
|
|
267
|
+
already_done: { claims: true, appliesBackfill: false },
|
|
268
|
+
not_applicable: { claims: true, appliesBackfill: false },
|
|
269
|
+
// An `error` run writes NO ledger claim at all — it does not know what it
|
|
270
|
+
// did, so it may not say.
|
|
271
|
+
error: { claims: false, appliesBackfill: false }
|
|
272
|
+
};
|
|
273
|
+
var LEDGER_METHODS = ["getObject", "find", "insert", "update"];
|
|
274
|
+
function resolveMigrationLedger(data) {
|
|
275
|
+
const candidate = data;
|
|
276
|
+
for (const method of LEDGER_METHODS) {
|
|
277
|
+
if (typeof candidate[method] !== "function") return void 0;
|
|
278
|
+
}
|
|
279
|
+
return candidate;
|
|
280
|
+
}
|
|
281
|
+
function buildNotificationEventClaim(status, now, exists) {
|
|
282
|
+
const claim = LEDGER_CLAIM[status];
|
|
283
|
+
const row = {
|
|
284
|
+
id: NOTIFICATION_EVENT_MIGRATION_ID,
|
|
285
|
+
last_run_at: now,
|
|
286
|
+
blocking: 0,
|
|
287
|
+
details: JSON.stringify({ outcome: status }),
|
|
288
|
+
updated_at: now
|
|
289
|
+
};
|
|
290
|
+
if (claim.appliesBackfill) row.applied_at = now;
|
|
291
|
+
if (!exists) {
|
|
292
|
+
row.applied_at = claim.appliesBackfill ? now : null;
|
|
293
|
+
row.verified_at = null;
|
|
294
|
+
row.created_at = now;
|
|
295
|
+
}
|
|
296
|
+
return row;
|
|
297
|
+
}
|
|
298
|
+
async function recordNotificationEventReceipt(data, status, now) {
|
|
299
|
+
if (!LEDGER_CLAIM[status].claims) return { outcome: "not-claimed" };
|
|
300
|
+
const ledger = resolveMigrationLedger(data);
|
|
301
|
+
if (!ledger) {
|
|
302
|
+
return {
|
|
303
|
+
outcome: "no-ledger",
|
|
304
|
+
reason: `the \`data\` engine carries no object registry (${LEDGER_METHODS.join("/")}), so ${DATA_MIGRATION_FLAG_OBJECT} cannot be reached from here`
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
if (!ledger.getObject(DATA_MIGRATION_FLAG_OBJECT)) {
|
|
309
|
+
return {
|
|
310
|
+
outcome: "no-ledger",
|
|
311
|
+
reason: `${DATA_MIGRATION_FLAG_OBJECT} is not registered on this kernel \u2014 compose PlatformObjectsPlugin, which carries the deployment ledger`
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const context = { isSystem: true };
|
|
315
|
+
const rows = await ledger.find(
|
|
316
|
+
DATA_MIGRATION_FLAG_OBJECT,
|
|
317
|
+
{ where: { id: NOTIFICATION_EVENT_MIGRATION_ID }, limit: 1 },
|
|
318
|
+
{ context }
|
|
319
|
+
);
|
|
320
|
+
const exists = rows?.[0]?.id === NOTIFICATION_EVENT_MIGRATION_ID;
|
|
321
|
+
const row = buildNotificationEventClaim(status, now, exists);
|
|
322
|
+
if (exists) {
|
|
323
|
+
await ledger.update(DATA_MIGRATION_FLAG_OBJECT, row, { context });
|
|
324
|
+
return { outcome: "updated" };
|
|
325
|
+
}
|
|
326
|
+
await ledger.insert(DATA_MIGRATION_FLAG_OBJECT, row, { context });
|
|
327
|
+
return { outcome: "inserted" };
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return { outcome: "failed", reason: err?.message ?? String(err) };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function canonicalTimestampText(value) {
|
|
333
|
+
if (typeof value === "string") return value;
|
|
334
|
+
if (value instanceof Date) return value.toISOString();
|
|
335
|
+
return String(value);
|
|
336
|
+
}
|
|
337
|
+
async function selectLegacyRows(exec) {
|
|
338
|
+
const result = await exec(
|
|
228
339
|
`SELECT id, recipient_id, type, title, body, url, actor_name, is_read, read_at, created_at, organization_id FROM "${EVENT_OBJECT}" WHERE recipient_id IS NOT NULL`
|
|
229
340
|
);
|
|
230
341
|
if (Array.isArray(result) && result.length > 0 && Array.isArray(result[0])) {
|
|
@@ -232,9 +343,9 @@ async function selectLegacyRows(driver) {
|
|
|
232
343
|
}
|
|
233
344
|
return Array.isArray(result) ? result : [];
|
|
234
345
|
}
|
|
235
|
-
async function columnExists(
|
|
346
|
+
async function columnExists(exec, table, column) {
|
|
236
347
|
try {
|
|
237
|
-
const rows = await
|
|
348
|
+
const rows = await exec(`PRAGMA table_info("${table}")`);
|
|
238
349
|
const list = Array.isArray(rows) ? Array.isArray(rows[0]) ? rows[0] : rows : [];
|
|
239
350
|
if (list.length > 0 && list.some((r) => r?.name != null)) {
|
|
240
351
|
return list.some((r) => r?.name === column);
|
|
@@ -242,7 +353,7 @@ async function columnExists(driver, table, column) {
|
|
|
242
353
|
} catch {
|
|
243
354
|
}
|
|
244
355
|
try {
|
|
245
|
-
const result = await
|
|
356
|
+
const result = await exec(
|
|
246
357
|
`SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,
|
|
247
358
|
[table, column]
|
|
248
359
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/migrations/migrate-env-id-to-project-id.ts","../../src/migrations/migrate-project-id-to-environment-id.ts","../../src/migrations/drop-projection-tables.ts","../../src/migrations/migrate-sys-notification-to-event.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: env_id → project_id\n *\n * Renames the `env_id` column to `project_id` on the metadata storage tables:\n * - sys_metadata\n * - sys_metadata_history\n *\n * (The per-type projection tables `sys_object` / `sys_view` / `sys_flow` /\n * `sys_agent` / `sys_tool` were removed in 2026-05 along with the projection\n * pipeline — see ADR 0005 addendum. They are intentionally not included.)\n *\n * Safe to run multiple times (idempotent): checks for column existence before\n * attempting to rename. If `project_id` already exists, the step is skipped.\n *\n * Usage:\n * import { migrateEnvIdToProjectId } from '@objectstack/metadata/migrations';\n * await migrateEnvIdToProjectId(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nconst AFFECTED_TABLES = [\n 'sys_metadata',\n 'sys_metadata_history',\n] as const;\n\nexport interface MigrationResult {\n table: string;\n status: 'renamed' | 'already_done' | 'table_missing' | 'error';\n error?: string;\n}\n\n/**\n * Rename `env_id` → `project_id` on all metadata tables.\n *\n * @param driver An IDataDriver with access to the target database.\n * Must expose a raw query method: `driver.raw(sql, bindings?)`.\n * @returns Per-table migration results.\n */\nexport async function migrateEnvIdToProjectId(driver: IDataDriver): Promise<MigrationResult[]> {\n const driverAny = driver as any;\n\n if (typeof driverAny.raw !== 'function') {\n throw new Error(\n 'migrateEnvIdToProjectId: driver must expose a .raw(sql, bindings?) method. ' +\n 'SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms.'\n );\n }\n\n const results: MigrationResult[] = [];\n\n for (const table of AFFECTED_TABLES) {\n try {\n // Detect dialect: SQLite uses PRAGMA, others use information_schema.\n const hasColumn = await _columnExists(driverAny, table, 'env_id');\n const alreadyMigrated = await _columnExists(driverAny, table, 'project_id');\n\n if (alreadyMigrated && !hasColumn) {\n results.push({ table, status: 'already_done' });\n continue;\n }\n\n if (!hasColumn) {\n // Neither column exists — table might not exist yet.\n results.push({ table, status: 'table_missing' });\n continue;\n }\n\n // Perform the rename. SQLite ≥ 3.25.0 supports ALTER TABLE RENAME COLUMN.\n await driverAny.raw(`ALTER TABLE \"${table}\" RENAME COLUMN env_id TO project_id`);\n\n results.push({ table, status: 'renamed' });\n } catch (err: any) {\n results.push({ table, status: 'error', error: err?.message ?? String(err) });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function _columnExists(driver: any, table: string, column: string): Promise<boolean> {\n try {\n // SQLite: PRAGMA table_info returns rows with `name` column.\n const rows: any[] = await driver.raw(`PRAGMA table_info(\"${table}\")`);\n if (Array.isArray(rows) && rows.length > 0) {\n // knex wraps PRAGMA result; handle both `rows` and `rows[0]` shapes.\n const list: any[] = Array.isArray(rows[0]) ? rows[0] : rows;\n return list.some((r: any) => r?.name === column);\n }\n\n // Fallback for non-SQLite: query information_schema.\n const result: any[] = await driver.raw(\n `SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,\n [table, column]\n );\n const list: any[] = Array.isArray(result[0]) ? result[0] : result;\n return list.length > 0;\n } catch {\n return false;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: project_id → environment_id\n *\n * Renames the `project_id` column to `environment_id` on the metadata\n * storage tables:\n * - sys_metadata\n * - sys_metadata_history\n *\n * Forward counterpart of {@link migrateEnvIdToProjectId} (which performed the\n * earlier `env_id → project_id` rename). Together they let an operator walk an\n * old schema all the way forward in two steps:\n *\n * migrateEnvIdToProjectId(driver); // env_id → project_id (legacy)\n * migrateProjectIdToEnvironmentId(driver); // project_id → environment_id (v5)\n *\n * (The per-type projection tables `sys_object` / `sys_view` / `sys_flow` /\n * `sys_agent` / `sys_tool` were removed in 2026-05 along with the projection\n * pipeline — see ADR 0005 addendum. They are intentionally not included.)\n *\n * Safe to run multiple times (idempotent): checks for column existence before\n * attempting to rename. If `environment_id` already exists, the step is\n * skipped.\n *\n * Usage:\n * import { migrateProjectIdToEnvironmentId } from '@objectstack/metadata/migrations';\n * await migrateProjectIdToEnvironmentId(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nconst AFFECTED_TABLES = [\n 'sys_metadata',\n 'sys_metadata_history',\n] as const;\n\nexport interface ProjectIdToEnvironmentIdResult {\n table: string;\n status: 'renamed' | 'already_done' | 'table_missing' | 'error';\n error?: string;\n}\n\n/**\n * Rename `project_id` → `environment_id` on all metadata tables.\n *\n * @param driver An IDataDriver with access to the target database.\n * Must expose a raw query method: `driver.raw(sql, bindings?)`.\n * @returns Per-table migration results.\n */\nexport async function migrateProjectIdToEnvironmentId(\n driver: IDataDriver,\n): Promise<ProjectIdToEnvironmentIdResult[]> {\n const driverAny = driver as any;\n\n if (typeof driverAny.raw !== 'function') {\n throw new Error(\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' +\n 'SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms.'\n );\n }\n\n const results: ProjectIdToEnvironmentIdResult[] = [];\n\n for (const table of AFFECTED_TABLES) {\n try {\n const hasColumn = await _columnExists(driverAny, table, 'project_id');\n const alreadyMigrated = await _columnExists(driverAny, table, 'environment_id');\n\n if (alreadyMigrated && !hasColumn) {\n results.push({ table, status: 'already_done' });\n continue;\n }\n\n if (!hasColumn) {\n results.push({ table, status: 'table_missing' });\n continue;\n }\n\n await driverAny.raw(\n `ALTER TABLE \"${table}\" RENAME COLUMN project_id TO environment_id`,\n );\n\n results.push({ table, status: 'renamed' });\n } catch (err: any) {\n results.push({ table, status: 'error', error: err?.message ?? String(err) });\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function _columnExists(driver: any, table: string, column: string): Promise<boolean> {\n try {\n const rows: any[] = await driver.raw(`PRAGMA table_info(\"${table}\")`);\n if (Array.isArray(rows) && rows.length > 0) {\n const list: any[] = Array.isArray(rows[0]) ? rows[0] : rows;\n return list.some((r: any) => r?.name === column);\n }\n\n const result: any[] = await driver.raw(\n `SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,\n [table, column],\n );\n const list: any[] = Array.isArray(result[0]) ? result[0] : result;\n return list.length > 0;\n } catch {\n return false;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: drop deprecated metadata projection tables.\n *\n * In 2026-05 the per-type projection tables (`sys_object` / `sys_view` /\n * `sys_flow` / `sys_agent` / `sys_tool`) and the corresponding\n * `MetadataProjector` were removed (see ADR 0005 addendum). All metadata\n * now lives as JSON inside `sys_metadata` — these projection tables are\n * dead weight on any existing database.\n *\n * This migration drops them if present. It is idempotent and safe to run\n * on databases that never had them (the `DROP TABLE IF EXISTS` is a no-op).\n *\n * Usage:\n * import { dropProjectionTables } from '@objectstack/metadata/migrations';\n * await dropProjectionTables(driver);\n */\n\nimport type { IDataDriver } from '@objectstack/spec/contracts';\n\nconst DEPRECATED_TABLES = [\n 'sys_object',\n 'sys_view',\n 'sys_flow',\n 'sys_agent',\n 'sys_tool',\n] as const;\n\nexport interface DropProjectionResult {\n table: string;\n status: 'dropped' | 'not_present' | 'error';\n error?: string;\n}\n\n/**\n * Drop the deprecated per-type metadata projection tables.\n *\n * @param driver An `IDataDriver` with `driver.raw(sql, bindings?)` access.\n * @returns Per-table results.\n */\nexport async function dropProjectionTables(driver: IDataDriver): Promise<DropProjectionResult[]> {\n const driverAny = driver as any;\n if (typeof driverAny.raw !== 'function') {\n throw new Error('dropProjectionTables: driver must expose a raw(sql) method');\n }\n\n const results: DropProjectionResult[] = [];\n for (const table of DEPRECATED_TABLES) {\n try {\n await driverAny.raw(`DROP TABLE IF EXISTS ${table}`);\n results.push({ table, status: 'dropped' });\n } catch (error) {\n results.push({\n table,\n status: 'error',\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return results;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Migration: sys_notification (per-user inbox) → notification event (ADR-0030)\n *\n * ADR-0030 re-models `sys_notification` from a per-user *inbox* into the L2\n * *event* (one row per `emit`). This migration preserves users' existing bell\n * notifications across the cut-over by splitting each legacy row into the new\n * layered model:\n *\n * legacy sys_notification row (recipient_id, type, title, body, url,\n * actor_name, is_read, read_at, …)\n * │\n * ├─► sys_inbox_message (L5 in-app materialization, keyed by user)\n * ├─► sys_notification_receipt (L5 read-state: 'read' if is_read else 'delivered')\n * └─► the sys_notification row itself is rewritten to the event shape\n * (topic ← type, payload ← {title,body,url,actor_name}) and its legacy\n * inbox columns are cleared.\n *\n * Idempotent: it acts only on rows that still carry the legacy shape\n * (`recipient_id IS NOT NULL`); a second run is a no-op. Safe when the legacy\n * columns were never present (a fresh install created directly in the new\n * shape) — it reports `not_applicable`.\n *\n * Usage:\n * import { migrateSysNotificationToEvent } from '@objectstack/metadata/migrations';\n * await migrateSysNotificationToEvent({ driver, data });\n *\n * `driver` provides raw access to read legacy columns the re-modeled schema no\n * longer projects and to clear them; `data` (IDataEngine) performs the\n * structured inbox/receipt writes and the event rewrite so ids, JSON fields and\n * tenant stamping are handled uniformly across drivers.\n */\n\nimport type { IDataDriver, IDataEngine } from '@objectstack/spec/contracts';\n\nconst EVENT_OBJECT = 'sys_notification';\nconst INBOX_OBJECT = 'sys_inbox_message';\nconst RECEIPT_OBJECT = 'sys_notification_receipt';\n\n/** Legacy inbox columns cleared once a row is rewritten to the event shape. */\nconst LEGACY_COLUMNS = [\n 'recipient_id',\n 'type',\n 'title',\n 'body',\n 'url',\n 'actor_name',\n 'is_read',\n 'read_at',\n] as const;\n\nexport interface SysNotificationMigrationResult {\n status: 'migrated' | 'already_done' | 'not_applicable' | 'error';\n /** Number of legacy rows split into inbox + receipt + event. */\n migrated: number;\n error?: string;\n}\n\nexport interface SysNotificationMigrationOptions {\n driver: IDataDriver;\n data: IDataEngine;\n /** Defaults to `() => new Date().toISOString()`. */\n now?(): string;\n}\n\nexport async function migrateSysNotificationToEvent(\n opts: SysNotificationMigrationOptions,\n): Promise<SysNotificationMigrationResult> {\n const driver = opts.driver as any;\n const { data } = opts;\n const now = opts.now ?? (() => new Date().toISOString());\n\n if (typeof driver?.raw !== 'function') {\n return {\n status: 'error',\n migrated: 0,\n error: 'migrateSysNotificationToEvent: driver must expose a .raw(sql, bindings?) method.',\n };\n }\n\n // No legacy `recipient_id` column → the table never held the inbox shape.\n if (!(await columnExists(driver, EVENT_OBJECT, 'recipient_id'))) {\n return { status: 'not_applicable', migrated: 0 };\n }\n\n // Only null-out columns that actually exist on this deployment.\n const presentLegacy: string[] = [];\n for (const col of LEGACY_COLUMNS) {\n if (await columnExists(driver, EVENT_OBJECT, col)) presentLegacy.push(col);\n }\n\n let migrated = 0;\n try {\n const rows = await selectLegacyRows(driver);\n if (rows.length === 0) return { status: 'already_done', migrated: 0 };\n\n for (const row of rows) {\n const id = String(row.id);\n const recipientId = row.recipient_id != null ? String(row.recipient_id) : null;\n if (!recipientId) continue; // defensive — guarded by the SELECT filter\n const orgId = row.organization_id != null ? String(row.organization_id) : null;\n const createdAt = row.created_at != null ? String(row.created_at) : now();\n const title = row.title != null ? String(row.title) : (row.type != null ? String(row.type) : 'Notification');\n const isRead = row.is_read === true || row.is_read === 1 || row.is_read === '1';\n // One topic for both the inbox row and the rewritten event, so the\n // materialization and its L2 event never disagree (empty/null legacy\n // `type` → 'legacy').\n const eventTopic = row.type != null && String(row.type).length > 0 ? String(row.type) : 'legacy';\n\n // L5 in-app materialization.\n await data.insert(INBOX_OBJECT, {\n user_id: recipientId,\n notification_id: id,\n topic: eventTopic,\n title,\n body_md: row.body ?? null,\n severity: 'info',\n action_url: row.url ?? null,\n organization_id: orgId,\n created_at: createdAt,\n });\n\n // L5 receipt (read-state spine).\n await data.insert(RECEIPT_OBJECT, {\n notification_id: id,\n delivery_id: null,\n user_id: recipientId,\n channel: 'inbox',\n state: isRead ? 'read' : 'delivered',\n at: isRead && row.read_at != null ? String(row.read_at) : createdAt,\n organization_id: orgId,\n created_at: createdAt,\n });\n\n // Rewrite the row itself to the L2 event shape (engine handles JSON).\n await data.update(\n EVENT_OBJECT,\n {\n id,\n topic: eventTopic,\n severity: 'info',\n payload: {\n title: row.title ?? null,\n body: row.body ?? null,\n url: row.url ?? null,\n actorName: row.actor_name ?? null,\n },\n },\n { where: { id } },\n );\n\n // Clear the legacy inbox columns so the row no longer matches the\n // migration filter (idempotency) and carries no stale recipient.\n if (presentLegacy.length > 0) {\n const setClause = presentLegacy.map((c) => `\"${c}\" = NULL`).join(', ');\n await driver.raw(`UPDATE \"${EVENT_OBJECT}\" SET ${setClause} WHERE id = ?`, [id]);\n }\n\n migrated += 1;\n }\n\n return { status: 'migrated', migrated };\n } catch (err: any) {\n return { status: 'error', migrated, error: err?.message ?? String(err) };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function selectLegacyRows(driver: any): Promise<any[]> {\n const result: any[] = await driver.raw(\n `SELECT id, recipient_id, type, title, body, url, actor_name, is_read, read_at, created_at, organization_id ` +\n `FROM \"${EVENT_OBJECT}\" WHERE recipient_id IS NOT NULL`,\n );\n // knex wraps some results as `[rows]`; normalize both shapes.\n if (Array.isArray(result) && result.length > 0 && Array.isArray(result[0])) {\n return result[0];\n }\n return Array.isArray(result) ? result : [];\n}\n\nasync function columnExists(driver: any, table: string, column: string): Promise<boolean> {\n // SQLite path: PRAGMA table_info. On Postgres/others this raises a syntax\n // error — swallow it *locally* and fall through to information_schema (the\n // outer-catch version of this would never reach the fallback, making the\n // migration silently no-op on every non-SQLite DB).\n try {\n const rows: any = await driver.raw(`PRAGMA table_info(\"${table}\")`);\n const list: any[] = Array.isArray(rows)\n ? (Array.isArray(rows[0]) ? rows[0] : rows)\n : [];\n if (list.length > 0 && list.some((r: any) => r?.name != null)) {\n return list.some((r: any) => r?.name === column);\n }\n } catch {\n /* not SQLite — fall through to information_schema */\n }\n // Postgres / others.\n try {\n const result: any = await driver.raw(\n `SELECT column_name FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,\n [table, column],\n );\n const list: any[] = Array.isArray(result)\n ? (Array.isArray(result[0]) ? result[0] : result)\n : [];\n return list.length > 0;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["list","AFFECTED_TABLES","_columnExists","list"]}
|
|
1
|
+
{"version":3,"sources":["../../src/migrations/driver-exec.ts","../../src/migrations/migrate-env-id-to-project-id.ts","../../src/migrations/migrate-project-id-to-environment-id.ts","../../src/migrations/drop-projection-tables.ts","../../src/migrations/migrate-sys-notification-to-event.ts"],"mappings":"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","names":["list","AFFECTED_TABLES","_columnExists","list"]}
|