@telorun/sql 0.21.3 → 0.22.1
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/index.d.ts +18 -2
- package/dist/index.js +9 -2
- package/dist/schema/declaration-snapshot.d.ts +21 -0
- package/dist/schema/declaration-snapshot.js +46 -0
- package/dist/schema/declared-schema.d.ts +64 -0
- package/dist/schema/declared-schema.js +16 -0
- package/dist/schema/migration-runner.d.ts +30 -0
- package/dist/schema/migration-runner.js +38 -0
- package/dist/schema/normalize-table.d.ts +57 -0
- package/dist/schema/normalize-table.js +125 -0
- package/dist/schema/reclaim-policy.d.ts +34 -0
- package/dist/schema/reclaim-policy.js +40 -0
- package/dist/schema/schema-driver.d.ts +184 -0
- package/dist/schema/schema-driver.js +1 -0
- package/dist/schema/schema-ledger.d.ts +119 -0
- package/dist/schema/schema-ledger.js +231 -0
- package/dist/schema/schema-reconciler.d.ts +45 -0
- package/dist/schema/schema-reconciler.js +276 -0
- package/dist/schema/schema-run.d.ts +50 -0
- package/dist/schema/schema-run.js +318 -0
- package/dist/schema/table-reference.d.ts +25 -0
- package/dist/schema/table-reference.js +61 -0
- package/dist/sql-connection-base.d.ts +21 -0
- package/dist/sql-connection-base.js +30 -4
- package/dist/sql-connection.d.ts +19 -0
- package/package.json +5 -3
- package/src/index.ts +43 -2
- package/src/schema/declaration-snapshot.ts +71 -0
- package/src/schema/declared-schema.ts +73 -0
- package/src/schema/migration-runner.ts +69 -0
- package/src/schema/normalize-table.ts +224 -0
- package/src/schema/reclaim-policy.ts +73 -0
- package/src/schema/schema-driver.ts +207 -0
- package/src/schema/schema-ledger.ts +309 -0
- package/src/schema/schema-reconciler.ts +372 -0
- package/src/schema/schema-run.ts +441 -0
- package/src/schema/table-reference.ts +78 -0
- package/src/sql-connection-base.ts +35 -4
- package/src/sql-connection.ts +21 -0
- package/dist/sql-migration-controller.d.ts +0 -16
- package/dist/sql-migration-controller.js +0 -13
- package/dist/sql-migrations-controller.d.ts +0 -23
- package/dist/sql-migrations-controller.js +0 -98
- package/src/sql-migration-controller.ts +0 -20
- package/src/sql-migrations-controller.ts +0 -143
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { CompiledQuery, Migrator, } from "kysely";
|
|
2
|
-
import { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
3
|
-
function entryStatements(entry) {
|
|
4
|
-
return entry.statements ?? (entry.statement != null ? [entry.statement] : []);
|
|
5
|
-
}
|
|
6
|
-
class TeloMigrationProvider {
|
|
7
|
-
migrations;
|
|
8
|
-
constructor(migrations) {
|
|
9
|
-
this.migrations = migrations;
|
|
10
|
-
}
|
|
11
|
-
async getMigrations() {
|
|
12
|
-
return Object.fromEntries(Object.entries(this.migrations).map(([name, statements]) => [
|
|
13
|
-
name,
|
|
14
|
-
{
|
|
15
|
-
// Each statement runs as its own prepared statement on the migration
|
|
16
|
-
// transaction's connection, so a migration may hold multiple
|
|
17
|
-
// statements while the whole batch stays a single transaction.
|
|
18
|
-
async up(db) {
|
|
19
|
-
for (const statement of statements) {
|
|
20
|
-
await db.executeQuery(CompiledQuery.raw(statement));
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
]));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
class SqlMigrationsResource {
|
|
28
|
-
manifest;
|
|
29
|
-
ctx;
|
|
30
|
-
constructor(manifest, ctx) {
|
|
31
|
-
this.manifest = manifest;
|
|
32
|
-
this.ctx = ctx;
|
|
33
|
-
}
|
|
34
|
-
async run() {
|
|
35
|
-
const conn = resolveSqlConnection(this.manifest.connection, this.ctx, () => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`) ?? failMissingConnection();
|
|
36
|
-
const migrations = {};
|
|
37
|
-
// Legacy: standalone `Sql.Migration` resources in the same module scope.
|
|
38
|
-
for (const [, { resource }] of this.ctx.moduleContext.resourceInstances) {
|
|
39
|
-
if (resource.kind === "Sql.Migration") {
|
|
40
|
-
const version = (resource.version ?? resource.metadata.name);
|
|
41
|
-
migrations[version] = [resource.sql];
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
// Preferred: the keyed `migrations` map on this resource.
|
|
45
|
-
for (const [name, entry] of Object.entries(this.manifest.migrations ?? {})) {
|
|
46
|
-
const statements = entryStatements(entry);
|
|
47
|
-
if (statements.length === 0) {
|
|
48
|
-
throw new Error(`Sql.Migrations: migration '${name}' has no statement(s) — ` +
|
|
49
|
-
`set 'statement' or a non-empty 'statements'`);
|
|
50
|
-
}
|
|
51
|
-
migrations[name] = statements;
|
|
52
|
-
}
|
|
53
|
-
if (!conn.kysely) {
|
|
54
|
-
throw new Error(`Sql.Migrations '${this.manifest.metadata.name}': the referenced connection is not ` +
|
|
55
|
-
`built on kysely, which this kind's migration runner requires. Use a backend that ` +
|
|
56
|
-
`extends SqlConnectionBase, or run the statements through Sql.Command.`);
|
|
57
|
-
}
|
|
58
|
-
const migrator = new Migrator({
|
|
59
|
-
db: conn.kysely,
|
|
60
|
-
provider: new TeloMigrationProvider(migrations),
|
|
61
|
-
migrationTableName: "migrations",
|
|
62
|
-
migrationLockTableName: "migration_locks",
|
|
63
|
-
});
|
|
64
|
-
const { error, results } = await migrator.migrateToLatest();
|
|
65
|
-
// A schema change is the least reversible thing an app does at boot, and the
|
|
66
|
-
// per-migration outcome was being discarded — so a run that applied four
|
|
67
|
-
// migrations and a run that found none to apply looked identical afterwards.
|
|
68
|
-
// `info`, because which migrations a deployment applied is the fact you go
|
|
69
|
-
// looking for when a schema is not what you expected.
|
|
70
|
-
// `sql.migration.name`, not `db.migration.name`: OTel owns `db.*` and defines
|
|
71
|
-
// no migration attribute, and §6.2 forbids inventing keys inside a namespace
|
|
72
|
-
// someone else governs.
|
|
73
|
-
for (const applied of results ?? []) {
|
|
74
|
-
if (applied.status === "Success") {
|
|
75
|
-
this.ctx.log.info("Migration applied", { "sql.migration.name": applied.migrationName });
|
|
76
|
-
}
|
|
77
|
-
else if (applied.status === "Error") {
|
|
78
|
-
// The cause rides on the record: this is the error-severity line an
|
|
79
|
-
// operator finds first, and the migration name alone cannot say what
|
|
80
|
-
// went wrong. `error` keeps the type, stack and cause chain (§4.2).
|
|
81
|
-
this.ctx.log.error("Migration failed", { "sql.migration.name": applied.migrationName }, { error });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (error) {
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
if (!results?.length) {
|
|
88
|
-
this.ctx.log.debug("No pending migrations");
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function failMissingConnection() {
|
|
93
|
-
throw new Error("Sql.Migrations: missing connection");
|
|
94
|
-
}
|
|
95
|
-
export function register() { }
|
|
96
|
-
export async function create(resource, ctx) {
|
|
97
|
-
return new SqlMigrationsResource(resource, ctx);
|
|
98
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ResourceInstance } from "@telorun/sdk";
|
|
2
|
-
|
|
3
|
-
interface SqlMigrationManifest {
|
|
4
|
-
metadata: { name: string; module: string };
|
|
5
|
-
sql: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
class SqlMigrationResource implements ResourceInstance {
|
|
9
|
-
constructor(readonly manifest: SqlMigrationManifest) {}
|
|
10
|
-
|
|
11
|
-
snapshot(): Record<string, unknown> {
|
|
12
|
-
return {};
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function register(): void {}
|
|
17
|
-
|
|
18
|
-
export async function create(resource: SqlMigrationManifest): Promise<SqlMigrationResource> {
|
|
19
|
-
return new SqlMigrationResource(resource);
|
|
20
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
|
|
2
|
-
import {
|
|
3
|
-
CompiledQuery,
|
|
4
|
-
Migrator,
|
|
5
|
-
type Kysely,
|
|
6
|
-
type Migration,
|
|
7
|
-
type MigrationProvider,
|
|
8
|
-
} from "kysely";
|
|
9
|
-
import type { SqlConnection } from "./sql-connection.js";
|
|
10
|
-
import { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
11
|
-
|
|
12
|
-
// A migration entry is one statement or an ordered list of statements; both
|
|
13
|
-
// forms normalize to a non-empty array of single statements.
|
|
14
|
-
interface MigrationEntry {
|
|
15
|
-
statement?: string;
|
|
16
|
-
statements?: string[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface SqlMigrationsManifest {
|
|
20
|
-
metadata: { name: string; module: string };
|
|
21
|
-
connection: SqlConnection;
|
|
22
|
-
migrations?: Record<string, MigrationEntry>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function entryStatements(entry: MigrationEntry): string[] {
|
|
26
|
-
return entry.statements ?? (entry.statement != null ? [entry.statement] : []);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class TeloMigrationProvider implements MigrationProvider {
|
|
30
|
-
constructor(private readonly migrations: Record<string, string[]>) {}
|
|
31
|
-
|
|
32
|
-
async getMigrations(): Promise<Record<string, Migration>> {
|
|
33
|
-
return Object.fromEntries(
|
|
34
|
-
Object.entries(this.migrations).map(([name, statements]) => [
|
|
35
|
-
name,
|
|
36
|
-
{
|
|
37
|
-
// Each statement runs as its own prepared statement on the migration
|
|
38
|
-
// transaction's connection, so a migration may hold multiple
|
|
39
|
-
// statements while the whole batch stays a single transaction.
|
|
40
|
-
async up(db: Kysely<any>): Promise<void> {
|
|
41
|
-
for (const statement of statements) {
|
|
42
|
-
await db.executeQuery(CompiledQuery.raw(statement));
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
]),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
class SqlMigrationsResource implements ResourceInstance {
|
|
52
|
-
constructor(
|
|
53
|
-
private readonly manifest: SqlMigrationsManifest,
|
|
54
|
-
private readonly ctx: ResourceContext,
|
|
55
|
-
) {}
|
|
56
|
-
|
|
57
|
-
async run(): Promise<void> {
|
|
58
|
-
const conn =
|
|
59
|
-
resolveSqlConnection(
|
|
60
|
-
this.manifest.connection,
|
|
61
|
-
this.ctx,
|
|
62
|
-
() => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`,
|
|
63
|
-
) ?? failMissingConnection();
|
|
64
|
-
|
|
65
|
-
const migrations: Record<string, string[]> = {};
|
|
66
|
-
// Legacy: standalone `Sql.Migration` resources in the same module scope.
|
|
67
|
-
for (const [, { resource }] of this.ctx.moduleContext.resourceInstances) {
|
|
68
|
-
if (resource.kind === "Sql.Migration") {
|
|
69
|
-
const version = (resource.version ?? resource.metadata.name) as string;
|
|
70
|
-
migrations[version] = [resource.sql as string];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Preferred: the keyed `migrations` map on this resource.
|
|
74
|
-
for (const [name, entry] of Object.entries(this.manifest.migrations ?? {})) {
|
|
75
|
-
const statements = entryStatements(entry);
|
|
76
|
-
if (statements.length === 0) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`Sql.Migrations: migration '${name}' has no statement(s) — ` +
|
|
79
|
-
`set 'statement' or a non-empty 'statements'`,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
migrations[name] = statements;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!conn.kysely) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
`Sql.Migrations '${this.manifest.metadata.name}': the referenced connection is not ` +
|
|
88
|
-
`built on kysely, which this kind's migration runner requires. Use a backend that ` +
|
|
89
|
-
`extends SqlConnectionBase, or run the statements through Sql.Command.`,
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const migrator = new Migrator({
|
|
94
|
-
db: conn.kysely,
|
|
95
|
-
provider: new TeloMigrationProvider(migrations),
|
|
96
|
-
migrationTableName: "migrations",
|
|
97
|
-
migrationLockTableName: "migration_locks",
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const { error, results } = await migrator.migrateToLatest();
|
|
101
|
-
// A schema change is the least reversible thing an app does at boot, and the
|
|
102
|
-
// per-migration outcome was being discarded — so a run that applied four
|
|
103
|
-
// migrations and a run that found none to apply looked identical afterwards.
|
|
104
|
-
// `info`, because which migrations a deployment applied is the fact you go
|
|
105
|
-
// looking for when a schema is not what you expected.
|
|
106
|
-
// `sql.migration.name`, not `db.migration.name`: OTel owns `db.*` and defines
|
|
107
|
-
// no migration attribute, and §6.2 forbids inventing keys inside a namespace
|
|
108
|
-
// someone else governs.
|
|
109
|
-
for (const applied of results ?? []) {
|
|
110
|
-
if (applied.status === "Success") {
|
|
111
|
-
this.ctx.log.info("Migration applied", { "sql.migration.name": applied.migrationName });
|
|
112
|
-
} else if (applied.status === "Error") {
|
|
113
|
-
// The cause rides on the record: this is the error-severity line an
|
|
114
|
-
// operator finds first, and the migration name alone cannot say what
|
|
115
|
-
// went wrong. `error` keeps the type, stack and cause chain (§4.2).
|
|
116
|
-
this.ctx.log.error(
|
|
117
|
-
"Migration failed",
|
|
118
|
-
{ "sql.migration.name": applied.migrationName },
|
|
119
|
-
{ error },
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (error) {
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
if (!results?.length) {
|
|
127
|
-
this.ctx.log.debug("No pending migrations");
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function failMissingConnection(): never {
|
|
133
|
-
throw new Error("Sql.Migrations: missing connection");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function register(): void {}
|
|
137
|
-
|
|
138
|
-
export async function create(
|
|
139
|
-
resource: SqlMigrationsManifest,
|
|
140
|
-
ctx: ResourceContext,
|
|
141
|
-
): Promise<SqlMigrationsResource> {
|
|
142
|
-
return new SqlMigrationsResource(resource, ctx);
|
|
143
|
-
}
|