@noormdev/sdk 1.0.0-alpha.18 → 1.0.0-alpha.19
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/{chunk-4HW4CS2C.js → chunk-BP4PMFJF.js} +2 -2
- package/dist/chunk-BP4PMFJF.js.map +1 -0
- package/dist/{chunk-LJDHQEWW.js → chunk-HB3JXUJV.js} +3 -3
- package/dist/{chunk-LJDHQEWW.js.map → chunk-HB3JXUJV.js.map} +1 -1
- package/dist/{chunk-KF7GE2VM.js → chunk-UZARPCOH.js} +4 -4
- package/dist/{chunk-KF7GE2VM.js.map → chunk-UZARPCOH.js.map} +1 -1
- package/dist/engine-3J25GVCU.js +4 -0
- package/dist/{engine-IFYFFWFR.js.map → engine-3J25GVCU.js.map} +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +353 -134
- package/dist/index.js.map +1 -1
- package/dist/{mssql-7VB7EHRX.js → mssql-65M7MY4Q.js} +43 -5
- package/dist/mssql-65M7MY4Q.js.map +1 -0
- package/dist/storage-WTYJE355.js +4 -0
- package/dist/{storage-NDKBEHIZ.js.map → storage-WTYJE355.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-4HW4CS2C.js.map +0 -1
- package/dist/engine-IFYFFWFR.js +0 -4
- package/dist/mssql-7VB7EHRX.js.map +0 -1
- package/dist/storage-NDKBEHIZ.js +0 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { processFile, DtReader, isTemplate, FORMAT_VERSION, DT_EXTENSIONS, encryptWithPassphrase, ENCODED_TYPES } from './chunk-
|
|
2
|
-
import { initializeVault, getVaultStatus, setVaultSecret, getVaultSecret, getAllVaultSecrets, listVaultSecretKeys, deleteVaultSecret, vaultSecretExists, getVaultKey, noormDb, getNoormTables, encryptVaultKey } from './chunk-
|
|
3
|
-
import { observer } from './chunk-
|
|
1
|
+
import { processFile, DtReader, isTemplate, FORMAT_VERSION, DT_EXTENSIONS, encryptWithPassphrase, ENCODED_TYPES } from './chunk-UZARPCOH.js';
|
|
2
|
+
import { initializeVault, getVaultStatus, setVaultSecret, getVaultSecret, getAllVaultSecrets, listVaultSecretKeys, deleteVaultSecret, vaultSecretExists, getVaultKey, noormDb, getNoormTables, encryptVaultKey } from './chunk-HB3JXUJV.js';
|
|
3
|
+
import { observer } from './chunk-BP4PMFJF.js';
|
|
4
4
|
import { constants, existsSync, readFileSync, mkdirSync, writeFileSync, createWriteStream } from 'fs';
|
|
5
5
|
import path, { join, dirname, resolve } from 'path';
|
|
6
6
|
import { makeNestedConfig, gigabytes, attempt, retry, clone, merge, attemptSync } from '@logosdx/utils';
|
|
@@ -1757,11 +1757,17 @@ var ConnectionManager = class {
|
|
|
1757
1757
|
}
|
|
1758
1758
|
const trackedEntries = Array.from(this.#tracked.entries());
|
|
1759
1759
|
for (const [id, entry] of trackedEntries) {
|
|
1760
|
+
let timer;
|
|
1760
1761
|
const destroyWithTimeout = Promise.race([
|
|
1761
|
-
entry.conn.destroy()
|
|
1762
|
-
|
|
1762
|
+
entry.conn.destroy().then(() => {
|
|
1763
|
+
clearTimeout(timer);
|
|
1764
|
+
}),
|
|
1765
|
+
new Promise((resolve2) => {
|
|
1766
|
+
timer = setTimeout(resolve2, CLOSE_TIMEOUT);
|
|
1767
|
+
})
|
|
1763
1768
|
]);
|
|
1764
1769
|
const [, err] = await attempt(() => destroyWithTimeout);
|
|
1770
|
+
clearTimeout(timer);
|
|
1765
1771
|
this.#tracked.delete(id);
|
|
1766
1772
|
if (err) {
|
|
1767
1773
|
observer.emit("error", { source: "connection", error: err });
|
|
@@ -1841,7 +1847,7 @@ async function getDialectFactory(dialect) {
|
|
|
1841
1847
|
case "mysql":
|
|
1842
1848
|
return (await import('./mysql-C2DR4TF7.js')).createMysqlConnection;
|
|
1843
1849
|
case "mssql":
|
|
1844
|
-
return (await import('./mssql-
|
|
1850
|
+
return (await import('./mssql-65M7MY4Q.js')).createMssqlConnection;
|
|
1845
1851
|
default:
|
|
1846
1852
|
throw new Error(`Unsupported dialect: ${dialect}`);
|
|
1847
1853
|
}
|
|
@@ -1915,7 +1921,6 @@ async function createConnection(config, configName = "__default__") {
|
|
|
1915
1921
|
destroy: wrappedDestroy
|
|
1916
1922
|
};
|
|
1917
1923
|
observer.emit("connection:open", { configName, dialect: config.dialect });
|
|
1918
|
-
await waitForIdentityToLoad(trackedConn.db, config.dialect);
|
|
1919
1924
|
return trackedConn;
|
|
1920
1925
|
}
|
|
1921
1926
|
var SYSTEM_DATABASES = {
|
|
@@ -2061,79 +2066,170 @@ var INDEXES = [
|
|
|
2061
2066
|
{ name: "idx_change_name_config", table: "change", columns: ["name", "config_name"] },
|
|
2062
2067
|
{ name: "idx_vault_secret_key", table: "vault", columns: ["secret_key"] }
|
|
2063
2068
|
];
|
|
2069
|
+
var PREFIXED_TABLES = {
|
|
2070
|
+
executions: "__noorm_executions__",
|
|
2071
|
+
change: "__noorm_change__",
|
|
2072
|
+
vault: "__noorm_vault__"
|
|
2073
|
+
};
|
|
2064
2074
|
var v2 = {
|
|
2065
2075
|
version: 2,
|
|
2066
2076
|
description: "Move tracking tables to noorm schema",
|
|
2067
2077
|
async up(db, dialect) {
|
|
2068
2078
|
if (dialect === "sqlite" || dialect === "mysql") return;
|
|
2069
2079
|
await db.transaction().execute(async (trx) => {
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2080
|
+
const src = dialect === "postgres" ? "public" : "dbo";
|
|
2081
|
+
async function tableExistsIn(schema, table) {
|
|
2082
|
+
if (dialect === "postgres") {
|
|
2083
|
+
const { rows: rows2 } = await sql`
|
|
2084
|
+
SELECT 1 AS n FROM information_schema.tables
|
|
2085
|
+
WHERE table_schema = ${schema} AND table_name = ${table}
|
|
2086
|
+
`.execute(trx);
|
|
2087
|
+
return rows2.length > 0;
|
|
2088
|
+
}
|
|
2076
2089
|
const { rows } = await sql`
|
|
2077
|
-
SELECT
|
|
2078
|
-
|
|
2079
|
-
WHERE
|
|
2080
|
-
AND constraint_type = 'FOREIGN KEY'
|
|
2090
|
+
SELECT 1 AS n FROM sys.tables t
|
|
2091
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2092
|
+
WHERE t.name = ${table} AND s.name = ${schema}
|
|
2081
2093
|
`.execute(trx);
|
|
2082
|
-
|
|
2083
|
-
|
|
2094
|
+
return rows.length > 0;
|
|
2095
|
+
}
|
|
2096
|
+
async function getForeignKeys(schema, table) {
|
|
2097
|
+
if (!await tableExistsIn(schema, table)) return [];
|
|
2098
|
+
if (dialect === "postgres") {
|
|
2099
|
+
const { rows: rows2 } = await sql`
|
|
2100
|
+
SELECT tc.constraint_name AS name
|
|
2101
|
+
FROM information_schema.table_constraints tc
|
|
2102
|
+
WHERE tc.table_schema = ${schema}
|
|
2103
|
+
AND tc.table_name = ${table}
|
|
2104
|
+
AND tc.constraint_type = 'FOREIGN KEY'
|
|
2105
|
+
`.execute(trx);
|
|
2106
|
+
return rows2.map((r) => r.name);
|
|
2084
2107
|
}
|
|
2085
|
-
} else {
|
|
2086
2108
|
const { rows } = await sql`
|
|
2087
|
-
SELECT fk.name
|
|
2109
|
+
SELECT fk.name
|
|
2088
2110
|
FROM sys.foreign_keys fk
|
|
2089
2111
|
JOIN sys.tables t ON fk.parent_object_id = t.object_id
|
|
2090
|
-
|
|
2112
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2113
|
+
WHERE t.name = ${table} AND s.name = ${schema}
|
|
2091
2114
|
`.execute(trx);
|
|
2092
|
-
|
|
2093
|
-
|
|
2115
|
+
return rows.map((r) => r.name);
|
|
2116
|
+
}
|
|
2117
|
+
async function indexExistsIn(schema, idxName) {
|
|
2118
|
+
if (dialect === "postgres") {
|
|
2119
|
+
const { rows: rows2 } = await sql`
|
|
2120
|
+
SELECT 1 AS n FROM pg_indexes
|
|
2121
|
+
WHERE schemaname = ${schema} AND indexname = ${idxName}
|
|
2122
|
+
`.execute(trx);
|
|
2123
|
+
return rows2.length > 0;
|
|
2094
2124
|
}
|
|
2125
|
+
const { rows } = await sql`
|
|
2126
|
+
SELECT 1 AS n FROM sys.indexes i
|
|
2127
|
+
JOIN sys.tables t ON i.object_id = t.object_id
|
|
2128
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2129
|
+
WHERE i.name = ${idxName} AND s.name = ${schema}
|
|
2130
|
+
`.execute(trx);
|
|
2131
|
+
return rows.length > 0;
|
|
2095
2132
|
}
|
|
2096
2133
|
if (dialect === "postgres") {
|
|
2097
|
-
|
|
2098
|
-
await sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);
|
|
2099
|
-
}
|
|
2134
|
+
await sql`CREATE SCHEMA IF NOT EXISTS noorm`.execute(trx);
|
|
2100
2135
|
} else {
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2136
|
+
await sql`IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'noorm') EXEC('CREATE SCHEMA noorm')`.execute(trx);
|
|
2137
|
+
}
|
|
2138
|
+
const fkLocations = [
|
|
2139
|
+
{ schema: src, table: "__noorm_executions__" },
|
|
2140
|
+
{ schema: "noorm", table: "__noorm_executions__" },
|
|
2141
|
+
{ schema: "noorm", table: "executions" }
|
|
2142
|
+
];
|
|
2143
|
+
for (const loc of fkLocations) {
|
|
2144
|
+
const fks = await getForeignKeys(loc.schema, loc.table);
|
|
2145
|
+
for (const fkName of fks) {
|
|
2146
|
+
await sql`ALTER TABLE ${sql.table(`${loc.schema}.${loc.table}`)} DROP CONSTRAINT ${sql.ref(fkName)}`.execute(trx);
|
|
2112
2147
|
}
|
|
2113
2148
|
}
|
|
2114
|
-
for (const
|
|
2115
|
-
if (
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2149
|
+
for (const idx of INDEXES) {
|
|
2150
|
+
if (await indexExistsIn(src, idx.name)) {
|
|
2151
|
+
if (dialect === "postgres") {
|
|
2152
|
+
await sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);
|
|
2153
|
+
} else {
|
|
2154
|
+
const tableName = PREFIXED_TABLES[idx.table];
|
|
2155
|
+
await sql`
|
|
2156
|
+
IF EXISTS (SELECT 1 FROM sys.indexes WHERE name = ${idx.name})
|
|
2157
|
+
DROP INDEX ${sql.ref(idx.name)} ON ${sql.table(tableName)}
|
|
2158
|
+
`.execute(trx);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
if (await indexExistsIn("noorm", idx.name)) {
|
|
2162
|
+
if (dialect === "postgres") {
|
|
2163
|
+
await sql`DROP INDEX IF EXISTS ${sql.ref(`noorm.${idx.name}`)}`.execute(trx);
|
|
2164
|
+
} else {
|
|
2165
|
+
const oldName = PREFIXED_TABLES[idx.table];
|
|
2166
|
+
const onOldName = await tableExistsIn("noorm", oldName);
|
|
2167
|
+
const tbl = onOldName ? `noorm.${oldName}` : `noorm.${idx.table}`;
|
|
2168
|
+
await sql`
|
|
2169
|
+
IF EXISTS (SELECT 1 FROM sys.indexes WHERE name = ${idx.name})
|
|
2170
|
+
DROP INDEX ${sql.ref(idx.name)} ON ${sql.table(tbl)}
|
|
2171
|
+
`.execute(trx);
|
|
2172
|
+
}
|
|
2119
2173
|
}
|
|
2120
2174
|
}
|
|
2121
2175
|
for (const t of TABLE_MAP) {
|
|
2176
|
+
const inNoormNew = await tableExistsIn("noorm", t.new);
|
|
2177
|
+
const inNoormOld = await tableExistsIn("noorm", t.old);
|
|
2178
|
+
const inSrc = await tableExistsIn(src, t.old);
|
|
2179
|
+
if (inNoormNew) {
|
|
2180
|
+
if (inSrc) {
|
|
2181
|
+
if (dialect === "postgres") {
|
|
2182
|
+
await sql`DROP TABLE ${sql.table(`${src}.${t.old}`)} CASCADE`.execute(trx);
|
|
2183
|
+
} else {
|
|
2184
|
+
await sql`DROP TABLE ${sql.table(`${src}.${t.old}`)}`.execute(trx);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
if (inNoormOld) {
|
|
2188
|
+
if (dialect === "postgres") {
|
|
2189
|
+
await sql`DROP TABLE ${sql.table(`noorm.${t.old}`)} CASCADE`.execute(trx);
|
|
2190
|
+
} else {
|
|
2191
|
+
await sql`DROP TABLE ${sql.table(`noorm.${t.old}`)}`.execute(trx);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
continue;
|
|
2195
|
+
}
|
|
2196
|
+
if (!inNoormOld && !inSrc) continue;
|
|
2197
|
+
if (!inNoormOld && inSrc) {
|
|
2198
|
+
if (dialect === "postgres") {
|
|
2199
|
+
await sql`ALTER TABLE ${sql.table(t.old)} SET SCHEMA noorm`.execute(trx);
|
|
2200
|
+
} else {
|
|
2201
|
+
await sql`ALTER SCHEMA noorm TRANSFER ${sql.ref(`dbo.${t.old}`)}`.execute(trx);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2122
2204
|
if (dialect === "postgres") {
|
|
2123
2205
|
await sql`ALTER TABLE ${sql.table(`noorm.${t.old}`)} RENAME TO ${sql.ref(t.new)}`.execute(trx);
|
|
2124
2206
|
} else {
|
|
2125
2207
|
await sql`EXEC sp_rename ${sql.raw(`'noorm.${t.old}'`)}, ${sql.raw(`'${t.new}'`)}`.execute(trx);
|
|
2126
2208
|
}
|
|
2209
|
+
if (inSrc && inNoormOld) {
|
|
2210
|
+
if (await tableExistsIn(src, t.old)) {
|
|
2211
|
+
if (dialect === "postgres") {
|
|
2212
|
+
await sql`DROP TABLE ${sql.table(`${src}.${t.old}`)} CASCADE`.execute(trx);
|
|
2213
|
+
} else {
|
|
2214
|
+
await sql`DROP TABLE ${sql.table(`${src}.${t.old}`)}`.execute(trx);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
const fksOnExec = await getForeignKeys("noorm", "executions");
|
|
2220
|
+
if (fksOnExec.length === 0) {
|
|
2221
|
+
await sql`
|
|
2222
|
+
ALTER TABLE ${sql.table("noorm.executions")}
|
|
2223
|
+
ADD CONSTRAINT fk_executions_change_id
|
|
2224
|
+
FOREIGN KEY (change_id) REFERENCES ${sql.table("noorm.change")}(id)
|
|
2225
|
+
ON DELETE CASCADE
|
|
2226
|
+
`.execute(trx);
|
|
2127
2227
|
}
|
|
2128
|
-
await sql`
|
|
2129
|
-
ALTER TABLE ${sql.table("noorm.executions")}
|
|
2130
|
-
ADD CONSTRAINT fk_executions_change_id
|
|
2131
|
-
FOREIGN KEY (change_id) REFERENCES ${sql.table("noorm.change")}(id)
|
|
2132
|
-
ON DELETE CASCADE
|
|
2133
|
-
`.execute(trx);
|
|
2134
2228
|
for (const idx of INDEXES) {
|
|
2135
|
-
|
|
2136
|
-
|
|
2229
|
+
if (!await indexExistsIn("noorm", idx.name)) {
|
|
2230
|
+
const cols = idx.columns.join(", ");
|
|
2231
|
+
await sql`CREATE INDEX ${sql.ref(idx.name)} ON ${sql.table(`noorm.${idx.table}`)} (${sql.raw(cols)})`.execute(trx);
|
|
2232
|
+
}
|
|
2137
2233
|
}
|
|
2138
2234
|
});
|
|
2139
2235
|
},
|
|
@@ -2196,12 +2292,7 @@ var v2 = {
|
|
|
2196
2292
|
ON DELETE CASCADE
|
|
2197
2293
|
`.execute(trx);
|
|
2198
2294
|
for (const idx of INDEXES) {
|
|
2199
|
-
const
|
|
2200
|
-
executions: "__noorm_executions__",
|
|
2201
|
-
change: "__noorm_change__",
|
|
2202
|
-
vault: "__noorm_vault__"
|
|
2203
|
-
};
|
|
2204
|
-
const tableName = prefixedTables[idx.table];
|
|
2295
|
+
const tableName = PREFIXED_TABLES[idx.table];
|
|
2205
2296
|
const cols = idx.columns.join(", ");
|
|
2206
2297
|
await sql`CREATE INDEX ${sql.ref(idx.name)} ON ${sql.table(tableName)} (${sql.raw(cols)})`.execute(trx);
|
|
2207
2298
|
}
|
|
@@ -2332,17 +2423,22 @@ async function migrateSchema(db, dialect, options) {
|
|
|
2332
2423
|
throw new VersionMismatchError("schema", status.current, status.expected);
|
|
2333
2424
|
}
|
|
2334
2425
|
if (!status.needsMigration) return;
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2426
|
+
let currentVersion = status.current;
|
|
2427
|
+
if (currentVersion === 0) {
|
|
2428
|
+
const tableFound = await tablesExist(db, dialect);
|
|
2429
|
+
if (!tableFound) {
|
|
2430
|
+
await bootstrapSchema(db, dialect);
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
currentVersion = 1;
|
|
2338
2434
|
}
|
|
2339
2435
|
const start = performance.now();
|
|
2340
2436
|
observer.emit("version:schema:migrating", {
|
|
2341
|
-
from:
|
|
2437
|
+
from: currentVersion,
|
|
2342
2438
|
to: CURRENT_VERSIONS.schema
|
|
2343
2439
|
});
|
|
2344
2440
|
const existing = await getLatestVersionRecord(db, dialect);
|
|
2345
|
-
const pendingMigrations = MIGRATIONS.filter((m) => m.version >
|
|
2441
|
+
const pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
|
|
2346
2442
|
for (const migration of pendingMigrations) {
|
|
2347
2443
|
const [, err] = await attempt(() => migration.up(db, dialect));
|
|
2348
2444
|
if (err) {
|
|
@@ -2359,7 +2455,7 @@ async function migrateSchema(db, dialect, options) {
|
|
|
2359
2455
|
}).execute();
|
|
2360
2456
|
const durationMs = performance.now() - start;
|
|
2361
2457
|
observer.emit("version:schema:migrated", {
|
|
2362
|
-
from:
|
|
2458
|
+
from: currentVersion,
|
|
2363
2459
|
to: CURRENT_VERSIONS.schema,
|
|
2364
2460
|
durationMs
|
|
2365
2461
|
});
|
|
@@ -3178,11 +3274,13 @@ var ChangeHistory = class {
|
|
|
3178
3274
|
#ndb;
|
|
3179
3275
|
#tables;
|
|
3180
3276
|
#configName;
|
|
3277
|
+
#dialect;
|
|
3181
3278
|
constructor(db, configName, dialect = "sqlite") {
|
|
3182
3279
|
this.#db = db;
|
|
3183
3280
|
this.#ndb = noormDb(db, dialect);
|
|
3184
3281
|
this.#tables = getNoormTables(dialect);
|
|
3185
3282
|
this.#configName = configName;
|
|
3283
|
+
this.#dialect = dialect;
|
|
3186
3284
|
}
|
|
3187
3285
|
// ─────────────────────────────────────────────────────────
|
|
3188
3286
|
// Status Queries
|
|
@@ -3358,34 +3456,63 @@ var ChangeHistory = class {
|
|
|
3358
3456
|
* @returns The created operation's ID
|
|
3359
3457
|
*/
|
|
3360
3458
|
async createOperation(data2) {
|
|
3361
|
-
const
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
)
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3459
|
+
const insertQuery = this.#ndb.insertInto(this.#tables.change).values({
|
|
3460
|
+
name: data2.name,
|
|
3461
|
+
change_type: "change",
|
|
3462
|
+
direction: data2.direction,
|
|
3463
|
+
status: "pending",
|
|
3464
|
+
config_name: this.#configName,
|
|
3465
|
+
executed_by: data2.executedBy
|
|
3466
|
+
});
|
|
3467
|
+
let id;
|
|
3468
|
+
if (this.#dialect === "mssql") {
|
|
3469
|
+
const [result, insertErr] = await attempt(
|
|
3470
|
+
() => insertQuery.output("inserted.id as id").executeTakeFirstOrThrow()
|
|
3471
|
+
);
|
|
3472
|
+
if (insertErr) {
|
|
3473
|
+
throw new Error("Failed to create change operation record", { cause: insertErr });
|
|
3474
|
+
}
|
|
3475
|
+
id = result?.id;
|
|
3476
|
+
} else {
|
|
3477
|
+
const [result, err] = await attempt(
|
|
3478
|
+
() => insertQuery.returning("id").executeTakeFirstOrThrow()
|
|
3378
3479
|
);
|
|
3379
|
-
if (
|
|
3380
|
-
throw new Error("Failed to
|
|
3480
|
+
if (err) {
|
|
3481
|
+
throw new Error("Failed to create change operation record", { cause: err });
|
|
3482
|
+
}
|
|
3483
|
+
id = result?.id ?? void 0;
|
|
3484
|
+
if (id === null || id === void 0) {
|
|
3485
|
+
const lastIdQuery = this.#lastInsertIdQuery();
|
|
3486
|
+
if (lastIdQuery) {
|
|
3487
|
+
const [lastIdResult] = await attempt(() => lastIdQuery.execute(this.#db));
|
|
3488
|
+
id = lastIdResult?.rows?.[0]?.id;
|
|
3489
|
+
}
|
|
3381
3490
|
}
|
|
3382
|
-
id = lastIdResult.rows[0].id;
|
|
3383
3491
|
}
|
|
3384
3492
|
if (typeof id !== "number" || !Number.isFinite(id) || id <= 0) {
|
|
3385
3493
|
throw new Error(`Invalid operation ID returned: ${id}`);
|
|
3386
3494
|
}
|
|
3387
3495
|
return id;
|
|
3388
3496
|
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Get dialect-specific last-insert-id query.
|
|
3499
|
+
*
|
|
3500
|
+
* Returns null if the dialect should always use RETURNING/OUTPUT.
|
|
3501
|
+
*/
|
|
3502
|
+
#lastInsertIdQuery() {
|
|
3503
|
+
switch (this.#dialect) {
|
|
3504
|
+
case "sqlite":
|
|
3505
|
+
return sql`SELECT last_insert_rowid() as id`;
|
|
3506
|
+
case "mysql":
|
|
3507
|
+
return sql`SELECT LAST_INSERT_ID() as id`;
|
|
3508
|
+
case "mssql":
|
|
3509
|
+
return sql`SELECT SCOPE_IDENTITY() as id`;
|
|
3510
|
+
case "postgres":
|
|
3511
|
+
return sql`SELECT lastval() as id`;
|
|
3512
|
+
default:
|
|
3513
|
+
return null;
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3389
3516
|
/**
|
|
3390
3517
|
* Create pending file records for all files.
|
|
3391
3518
|
*
|
|
@@ -3463,7 +3590,7 @@ var ChangeHistory = class {
|
|
|
3463
3590
|
const [, err] = await attempt(
|
|
3464
3591
|
() => this.#ndb.updateTable(this.#tables.executions).set({
|
|
3465
3592
|
status: "skipped",
|
|
3466
|
-
skip_reason: reason
|
|
3593
|
+
skip_reason: reason.slice(0, 100)
|
|
3467
3594
|
}).where("change_id", "=", operationId).where("status", "=", "pending").execute()
|
|
3468
3595
|
);
|
|
3469
3596
|
if (err) {
|
|
@@ -3524,18 +3651,33 @@ var ChangeHistory = class {
|
|
|
3524
3651
|
* @returns The created record's ID
|
|
3525
3652
|
*/
|
|
3526
3653
|
async recordReset(executedBy, reason) {
|
|
3654
|
+
const insertQuery = this.#ndb.insertInto(this.#tables.change).values({
|
|
3655
|
+
name: "__reset__",
|
|
3656
|
+
change_type: "change",
|
|
3657
|
+
direction: "change",
|
|
3658
|
+
status: "success",
|
|
3659
|
+
config_name: this.#configName,
|
|
3660
|
+
executed_by: executedBy,
|
|
3661
|
+
error_message: reason ?? "",
|
|
3662
|
+
duration_ms: 0,
|
|
3663
|
+
checksum: ""
|
|
3664
|
+
});
|
|
3665
|
+
if (this.#dialect === "mssql") {
|
|
3666
|
+
const [result2, insertErr] = await attempt(
|
|
3667
|
+
() => insertQuery.output("inserted.id as id").executeTakeFirstOrThrow()
|
|
3668
|
+
);
|
|
3669
|
+
if (insertErr) {
|
|
3670
|
+
observer.emit("error", {
|
|
3671
|
+
source: "change",
|
|
3672
|
+
error: insertErr,
|
|
3673
|
+
context: { operation: "record-reset" }
|
|
3674
|
+
});
|
|
3675
|
+
return 0;
|
|
3676
|
+
}
|
|
3677
|
+
return result2?.id ?? 0;
|
|
3678
|
+
}
|
|
3527
3679
|
const [result, err] = await attempt(
|
|
3528
|
-
() =>
|
|
3529
|
-
name: "__reset__",
|
|
3530
|
-
change_type: "change",
|
|
3531
|
-
direction: "change",
|
|
3532
|
-
status: "success",
|
|
3533
|
-
config_name: this.#configName,
|
|
3534
|
-
executed_by: executedBy,
|
|
3535
|
-
error_message: reason ?? "",
|
|
3536
|
-
duration_ms: 0,
|
|
3537
|
-
checksum: ""
|
|
3538
|
-
}).returning("id").executeTakeFirstOrThrow()
|
|
3680
|
+
() => insertQuery.returning("id").executeTakeFirstOrThrow()
|
|
3539
3681
|
);
|
|
3540
3682
|
if (err) {
|
|
3541
3683
|
observer.emit("error", {
|
|
@@ -3749,11 +3891,13 @@ var Tracker = class {
|
|
|
3749
3891
|
#ndb;
|
|
3750
3892
|
#tables;
|
|
3751
3893
|
#configName;
|
|
3894
|
+
#dialect;
|
|
3752
3895
|
constructor(db, configName, dialect = "sqlite") {
|
|
3753
3896
|
this.#db = db;
|
|
3754
3897
|
this.#ndb = noormDb(db, dialect);
|
|
3755
3898
|
this.#tables = getNoormTables(dialect);
|
|
3756
3899
|
this.#configName = configName;
|
|
3900
|
+
this.#dialect = dialect;
|
|
3757
3901
|
}
|
|
3758
3902
|
/**
|
|
3759
3903
|
* Check if a file needs to run.
|
|
@@ -3839,34 +3983,63 @@ var Tracker = class {
|
|
|
3839
3983
|
async createOperation(data2) {
|
|
3840
3984
|
const direction = data2.direction ?? "commit";
|
|
3841
3985
|
const dbDirection = direction === "commit" ? "change" : "revert";
|
|
3842
|
-
const
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
)
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3986
|
+
const insertQuery = this.#ndb.insertInto(this.#tables.change).values({
|
|
3987
|
+
name: data2.name,
|
|
3988
|
+
change_type: data2.changeType,
|
|
3989
|
+
direction: dbDirection,
|
|
3990
|
+
status: "pending",
|
|
3991
|
+
config_name: data2.configName,
|
|
3992
|
+
executed_by: data2.executedBy
|
|
3993
|
+
});
|
|
3994
|
+
let id;
|
|
3995
|
+
if (this.#dialect === "mssql") {
|
|
3996
|
+
const [result, insertErr] = await attempt(
|
|
3997
|
+
() => insertQuery.output("inserted.id as id").executeTakeFirstOrThrow()
|
|
3998
|
+
);
|
|
3999
|
+
if (insertErr) {
|
|
4000
|
+
throw new Error("Failed to create operation record", { cause: insertErr });
|
|
4001
|
+
}
|
|
4002
|
+
id = result?.id;
|
|
4003
|
+
} else {
|
|
4004
|
+
const [result, err] = await attempt(
|
|
4005
|
+
() => insertQuery.returning("id").executeTakeFirstOrThrow()
|
|
3859
4006
|
);
|
|
3860
|
-
if (
|
|
3861
|
-
throw new Error("Failed to
|
|
4007
|
+
if (err) {
|
|
4008
|
+
throw new Error("Failed to create operation record", { cause: err });
|
|
4009
|
+
}
|
|
4010
|
+
id = result?.id ?? void 0;
|
|
4011
|
+
if (id === null || id === void 0) {
|
|
4012
|
+
const lastIdQuery = this.#lastInsertIdQuery();
|
|
4013
|
+
if (lastIdQuery) {
|
|
4014
|
+
const [lastIdResult] = await attempt(() => lastIdQuery.execute(this.#db));
|
|
4015
|
+
id = lastIdResult?.rows?.[0]?.id;
|
|
4016
|
+
}
|
|
3862
4017
|
}
|
|
3863
|
-
id = lastIdResult.rows[0].id;
|
|
3864
4018
|
}
|
|
3865
4019
|
if (typeof id !== "number" || !Number.isFinite(id) || id <= 0) {
|
|
3866
4020
|
throw new Error(`Invalid operation ID returned: ${id}`);
|
|
3867
4021
|
}
|
|
3868
4022
|
return id;
|
|
3869
4023
|
}
|
|
4024
|
+
/**
|
|
4025
|
+
* Get dialect-specific last-insert-id query.
|
|
4026
|
+
*
|
|
4027
|
+
* Returns null if the dialect should always use RETURNING/OUTPUT.
|
|
4028
|
+
*/
|
|
4029
|
+
#lastInsertIdQuery() {
|
|
4030
|
+
switch (this.#dialect) {
|
|
4031
|
+
case "sqlite":
|
|
4032
|
+
return sql`SELECT last_insert_rowid() as id`;
|
|
4033
|
+
case "mysql":
|
|
4034
|
+
return sql`SELECT LAST_INSERT_ID() as id`;
|
|
4035
|
+
case "mssql":
|
|
4036
|
+
return sql`SELECT SCOPE_IDENTITY() as id`;
|
|
4037
|
+
case "postgres":
|
|
4038
|
+
return sql`SELECT lastval() as id`;
|
|
4039
|
+
default:
|
|
4040
|
+
return null;
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
3870
4043
|
/**
|
|
3871
4044
|
* Record a file execution.
|
|
3872
4045
|
*
|
|
@@ -4029,10 +4202,11 @@ var Tracker = class {
|
|
|
4029
4202
|
* @returns Error message if skip failed, null on success
|
|
4030
4203
|
*/
|
|
4031
4204
|
async skipRemainingFiles(operationId, reason) {
|
|
4205
|
+
const truncatedReason = reason.slice(0, 100);
|
|
4032
4206
|
const [, err] = await attempt(
|
|
4033
4207
|
() => this.#ndb.updateTable(this.#tables.executions).set({
|
|
4034
4208
|
status: "skipped",
|
|
4035
|
-
skip_reason:
|
|
4209
|
+
skip_reason: truncatedReason
|
|
4036
4210
|
}).where("change_id", "=", operationId).where("status", "=", "pending").execute()
|
|
4037
4211
|
);
|
|
4038
4212
|
if (err) {
|
|
@@ -5746,7 +5920,10 @@ async function runBuild(context, sqlPath, options = {}, preFilteredFiles) {
|
|
|
5746
5920
|
error: discoverErr,
|
|
5747
5921
|
context: { sqlPath, operation: "discover-files" }
|
|
5748
5922
|
});
|
|
5749
|
-
return createFailedBatchResult(
|
|
5923
|
+
return createFailedBatchResult(
|
|
5924
|
+
`${discoverErr.message} (${sqlPath})`,
|
|
5925
|
+
performance.now() - start
|
|
5926
|
+
);
|
|
5750
5927
|
}
|
|
5751
5928
|
files = discovered;
|
|
5752
5929
|
}
|
|
@@ -5754,13 +5931,14 @@ async function runBuild(context, sqlPath, options = {}, preFilteredFiles) {
|
|
|
5754
5931
|
sqlPath,
|
|
5755
5932
|
fileCount: files.length
|
|
5756
5933
|
});
|
|
5757
|
-
const result = await executeFilesInternal(context, files, opts, "build");
|
|
5934
|
+
const result = await executeFilesInternal(context, files, opts, "build", sqlPath);
|
|
5758
5935
|
observer.emit("build:complete", {
|
|
5759
5936
|
status: result.status,
|
|
5760
5937
|
filesRun: result.filesRun,
|
|
5761
5938
|
filesSkipped: result.filesSkipped,
|
|
5762
5939
|
filesFailed: result.filesFailed,
|
|
5763
|
-
durationMs: result.durationMs
|
|
5940
|
+
durationMs: result.durationMs,
|
|
5941
|
+
error: result.error
|
|
5764
5942
|
});
|
|
5765
5943
|
return result;
|
|
5766
5944
|
}
|
|
@@ -5813,14 +5991,17 @@ async function runDir(context, dirpath, options = {}) {
|
|
|
5813
5991
|
error: discoverErr,
|
|
5814
5992
|
context: { dirpath, operation: "discover-files" }
|
|
5815
5993
|
});
|
|
5816
|
-
return createFailedBatchResult(
|
|
5994
|
+
return createFailedBatchResult(
|
|
5995
|
+
`${discoverErr.message} (${dirpath})`,
|
|
5996
|
+
performance.now() - start
|
|
5997
|
+
);
|
|
5817
5998
|
}
|
|
5818
5999
|
observer.emit("run:dir", {
|
|
5819
6000
|
dirpath,
|
|
5820
6001
|
fileCount: files.length,
|
|
5821
6002
|
configName: context.configName
|
|
5822
6003
|
});
|
|
5823
|
-
return executeFilesInternal(context, files, opts, "run");
|
|
6004
|
+
return executeFilesInternal(context, files, opts, "run", dirpath);
|
|
5824
6005
|
}
|
|
5825
6006
|
async function runFiles(context, files, options = {}) {
|
|
5826
6007
|
const opts = { ...DEFAULT_RUN_OPTIONS_INTERNAL, ...options };
|
|
@@ -5916,9 +6097,12 @@ async function executeFiles2(context, files, runOptions, execOptions) {
|
|
|
5916
6097
|
observer.emit("error", {
|
|
5917
6098
|
source: "runner:create-operation",
|
|
5918
6099
|
error: createErr,
|
|
5919
|
-
context: { operationName: execOptions.operationName }
|
|
6100
|
+
context: { operationName: execOptions.operationName, sqlPath: execOptions.sqlPath }
|
|
5920
6101
|
});
|
|
5921
|
-
return createFailedBatchResult(
|
|
6102
|
+
return createFailedBatchResult(
|
|
6103
|
+
formatErrorChain(createErr),
|
|
6104
|
+
performance.now() - start
|
|
6105
|
+
);
|
|
5922
6106
|
}
|
|
5923
6107
|
const fileRecords = [];
|
|
5924
6108
|
for (const file of files) {
|
|
@@ -5941,7 +6125,10 @@ async function executeFiles2(context, files, runOptions, execOptions) {
|
|
|
5941
6125
|
context: { operationId }
|
|
5942
6126
|
});
|
|
5943
6127
|
await tracker.finalizeOperation(operationId, "failed", 0, "", createRecordsErr);
|
|
5944
|
-
return createFailedBatchResult(
|
|
6128
|
+
return createFailedBatchResult(
|
|
6129
|
+
createRecordsErr,
|
|
6130
|
+
performance.now() - start
|
|
6131
|
+
);
|
|
5945
6132
|
}
|
|
5946
6133
|
const results = [];
|
|
5947
6134
|
let failed = false;
|
|
@@ -5962,7 +6149,7 @@ async function executeFiles2(context, files, runOptions, execOptions) {
|
|
|
5962
6149
|
failed = true;
|
|
5963
6150
|
const skipErr = await tracker.skipRemainingFiles(
|
|
5964
6151
|
operationId,
|
|
5965
|
-
`Skipped
|
|
6152
|
+
`Skipped: failure in ${path.basename(file.path)}`.slice(0, 100)
|
|
5966
6153
|
);
|
|
5967
6154
|
if (skipErr) {
|
|
5968
6155
|
observer.emit("error", {
|
|
@@ -6008,7 +6195,7 @@ async function executeFiles2(context, files, runOptions, execOptions) {
|
|
|
6008
6195
|
changeId: operationId
|
|
6009
6196
|
};
|
|
6010
6197
|
}
|
|
6011
|
-
async function executeFilesInternal(context, files, options, changeType) {
|
|
6198
|
+
async function executeFilesInternal(context, files, options, changeType, sqlPath) {
|
|
6012
6199
|
const fileInputs = files.map((f) => ({
|
|
6013
6200
|
path: f,
|
|
6014
6201
|
type: "sql"
|
|
@@ -6016,7 +6203,8 @@ async function executeFilesInternal(context, files, options, changeType) {
|
|
|
6016
6203
|
const execOptions = {
|
|
6017
6204
|
changeType,
|
|
6018
6205
|
direction: "commit",
|
|
6019
|
-
operationName: `${changeType}:${(/* @__PURE__ */ new Date()).toISOString()}
|
|
6206
|
+
operationName: `${changeType}:${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
6207
|
+
sqlPath
|
|
6020
6208
|
};
|
|
6021
6209
|
return executeFiles2(context, fileInputs, options, execOptions);
|
|
6022
6210
|
}
|
|
@@ -6086,7 +6274,7 @@ async function executeSingleFileWithUpdate(context, filepath, checksum, options,
|
|
|
6086
6274
|
filepath,
|
|
6087
6275
|
checksum: finalChecksum,
|
|
6088
6276
|
status: "failed",
|
|
6089
|
-
error: execErr
|
|
6277
|
+
error: getErrorMessage(execErr),
|
|
6090
6278
|
durationMs
|
|
6091
6279
|
};
|
|
6092
6280
|
await tracker.updateFileExecution(
|
|
@@ -6094,13 +6282,13 @@ async function executeSingleFileWithUpdate(context, filepath, checksum, options,
|
|
|
6094
6282
|
relFilepath,
|
|
6095
6283
|
"failed",
|
|
6096
6284
|
Math.round(durationMs),
|
|
6097
|
-
execErr
|
|
6285
|
+
getErrorMessage(execErr)
|
|
6098
6286
|
);
|
|
6099
6287
|
observer.emit("file:after", {
|
|
6100
6288
|
filepath,
|
|
6101
6289
|
status: "failed",
|
|
6102
6290
|
durationMs,
|
|
6103
|
-
error: execErr
|
|
6291
|
+
error: getErrorMessage(execErr)
|
|
6104
6292
|
});
|
|
6105
6293
|
return result2;
|
|
6106
6294
|
}
|
|
@@ -6210,7 +6398,7 @@ async function executeSingleFile(context, filepath, options, tracker, operationI
|
|
|
6210
6398
|
filepath,
|
|
6211
6399
|
checksum,
|
|
6212
6400
|
status: "failed",
|
|
6213
|
-
error: execErr
|
|
6401
|
+
error: getErrorMessage(execErr),
|
|
6214
6402
|
durationMs
|
|
6215
6403
|
};
|
|
6216
6404
|
await tracker.recordExecution({
|
|
@@ -6218,14 +6406,14 @@ async function executeSingleFile(context, filepath, options, tracker, operationI
|
|
|
6218
6406
|
filepath: relFilepath,
|
|
6219
6407
|
checksum,
|
|
6220
6408
|
status: "failed",
|
|
6221
|
-
errorMessage: execErr
|
|
6409
|
+
errorMessage: getErrorMessage(execErr),
|
|
6222
6410
|
durationMs: Math.round(durationMs)
|
|
6223
6411
|
});
|
|
6224
6412
|
observer.emit("file:after", {
|
|
6225
6413
|
filepath,
|
|
6226
6414
|
status: "failed",
|
|
6227
6415
|
durationMs,
|
|
6228
|
-
error: execErr
|
|
6416
|
+
error: getErrorMessage(execErr)
|
|
6229
6417
|
});
|
|
6230
6418
|
return result2;
|
|
6231
6419
|
}
|
|
@@ -6368,9 +6556,40 @@ function createFailedBatchResult(error, durationMs) {
|
|
|
6368
6556
|
filesRun: 0,
|
|
6369
6557
|
filesSkipped: 0,
|
|
6370
6558
|
filesFailed: 0,
|
|
6371
|
-
durationMs
|
|
6559
|
+
durationMs,
|
|
6560
|
+
error
|
|
6372
6561
|
};
|
|
6373
6562
|
}
|
|
6563
|
+
function formatErrorChain(err) {
|
|
6564
|
+
const parts = [err.message];
|
|
6565
|
+
let current = err.cause;
|
|
6566
|
+
while (current instanceof Error) {
|
|
6567
|
+
parts.push(current.message);
|
|
6568
|
+
current = current.cause;
|
|
6569
|
+
}
|
|
6570
|
+
return parts.join(": ");
|
|
6571
|
+
}
|
|
6572
|
+
function getErrorMessage(err) {
|
|
6573
|
+
if (err instanceof AggregateError && err.errors?.length > 0) {
|
|
6574
|
+
return err.errors.map((e) => getErrorMessage(e)).join("; ");
|
|
6575
|
+
}
|
|
6576
|
+
if (err instanceof Error && err.message) return err.message;
|
|
6577
|
+
if (typeof err === "string") return err;
|
|
6578
|
+
if (typeof err === "object" && err !== null) {
|
|
6579
|
+
const e = err;
|
|
6580
|
+
if (typeof e["message"] === "string" && e["message"]) return e["message"];
|
|
6581
|
+
if (Array.isArray(e["errors"]) && e["errors"].length > 0) {
|
|
6582
|
+
return e["errors"].map((inner) => getErrorMessage(inner)).join("; ");
|
|
6583
|
+
}
|
|
6584
|
+
if (typeof e["originalError"] === "object" && e["originalError"] !== null) {
|
|
6585
|
+
const orig = e["originalError"];
|
|
6586
|
+
if (typeof orig["message"] === "string" && orig["message"]) return orig["message"];
|
|
6587
|
+
}
|
|
6588
|
+
const str = String(err);
|
|
6589
|
+
if (str !== "[object Object]") return str;
|
|
6590
|
+
}
|
|
6591
|
+
return "Unknown error";
|
|
6592
|
+
}
|
|
6374
6593
|
|
|
6375
6594
|
// src/sdk/namespaces/run.ts
|
|
6376
6595
|
var RunNamespace = class {
|