emilsoftware-utilities 2.0.0-dev.34 → 2.0.0-dev.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +14 -0
- package/dist/accessi-module/AccessiModule.d.ts +29 -0
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/console.js +37 -2
- package/dist/accessi-module/Console/wiki.js +32 -1
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/database-updates/AccessiCatalogMigrator.d.ts +83 -0
- package/dist/accessi-module/database-updates/AccessiCatalogMigrator.js +360 -0
- package/dist/accessi-module/database-updates/AccessiCatalogMigrator.js.map +1 -0
- package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.d.ts +6 -0
- package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js +20 -1
- package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js.map +1 -1
- package/dist/accessi-module/database-updates/accessiOptionsFromEnv.d.ts +5 -0
- package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js +79 -0
- package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js.map +1 -0
- package/dist/accessi-module/database-updates/accessiSchema.d.ts +1 -1
- package/dist/accessi-module/database-updates/accessiSchema.js +10 -1
- package/dist/accessi-module/database-updates/accessiSchema.js.map +1 -1
- package/dist/accessi-module/database-updates/runAccessiCatalogMigrations.d.ts +1 -0
- package/dist/accessi-module/database-updates/runAccessiCatalogMigrations.js +69 -0
- package/dist/accessi-module/database-updates/runAccessiCatalogMigrations.js.map +1 -0
- package/dist/accessi-module/database-updates/runAccessiDbUpdate.js +2 -58
- package/dist/accessi-module/database-updates/runAccessiDbUpdate.js.map +1 -1
- package/dist/accessi-module/index.d.ts +3 -0
- package/dist/accessi-module/index.js +10 -1
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.36.tgz +0 -0
- package/package.json +5 -3
- package/dist/emilsoftware-utilities-2.0.0-dev.34.tgz +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { AccessiOptions, CatalogScriptsOptions } from '../AccessiModule';
|
|
2
|
+
export interface CatalogScriptMigration {
|
|
3
|
+
name: string;
|
|
4
|
+
applied: boolean;
|
|
5
|
+
skipped: boolean;
|
|
6
|
+
reapplied: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CatalogMigrationReport {
|
|
10
|
+
folder: string;
|
|
11
|
+
discovered: number;
|
|
12
|
+
applied: string[];
|
|
13
|
+
skipped: string[];
|
|
14
|
+
invalid: {
|
|
15
|
+
script: string;
|
|
16
|
+
violations: string[];
|
|
17
|
+
}[];
|
|
18
|
+
failed: {
|
|
19
|
+
script: string;
|
|
20
|
+
error: string;
|
|
21
|
+
}[];
|
|
22
|
+
}
|
|
23
|
+
/** Estrae la prima parola chiave di uno statement, saltando spazi e commenti iniziali. */
|
|
24
|
+
export declare function leadingStatementKeyword(statement: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Valida gli statement di una catalog migration: ritorna la lista delle violazioni (vuota se conforme).
|
|
27
|
+
* Regola tassativa: solo DML. Nessuna modifica a colonne/schema/trigger/permessi.
|
|
28
|
+
*/
|
|
29
|
+
export declare function analyzeCatalogScript(statements: string[]): string[];
|
|
30
|
+
/** Hash SHA-256 del contenuto dello script, usato per rilevare modifiche non versionate. */
|
|
31
|
+
export declare function computeCatalogScriptChecksum(content: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Divide un file SQL in statement eseguibili separatamente.
|
|
34
|
+
*
|
|
35
|
+
* Supporta:
|
|
36
|
+
* - terminator personalizzati con `SET TERM <char>` / `SET TERM <char> ;`
|
|
37
|
+
* - stringhe `'...'` con escape `''`
|
|
38
|
+
* - commenti di riga `--` e di blocco `/* ... * /`
|
|
39
|
+
*
|
|
40
|
+
* Non interpreta la semantica SQL: e volutamente un tokenizer prudente che non spezza
|
|
41
|
+
* mai all'interno di stringhe o commenti.
|
|
42
|
+
*/
|
|
43
|
+
export declare function splitSqlStatements(sql: string, defaultTerminator?: string): string[];
|
|
44
|
+
/** Elenca gli script `.sql` della cartella, ordinati per percorso (prefissi numerici consigliati). */
|
|
45
|
+
export declare function discoverCatalogScripts(options: CatalogScriptsOptions): {
|
|
46
|
+
name: string;
|
|
47
|
+
path: string;
|
|
48
|
+
}[];
|
|
49
|
+
/** Elenca gli script non ancora applicati (o con checksum diverso) senza eseguirli. */
|
|
50
|
+
export declare function listPendingCatalogScripts(options: AccessiOptions): Promise<{
|
|
51
|
+
applied: string[];
|
|
52
|
+
pending: string[];
|
|
53
|
+
changed: string[];
|
|
54
|
+
invalid: {
|
|
55
|
+
script: string;
|
|
56
|
+
violations: string[];
|
|
57
|
+
}[];
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Applica le catalog migrations presenti nella cartella configurata.
|
|
61
|
+
*
|
|
62
|
+
* Idempotenza:
|
|
63
|
+
* - uno script gia presente nel ledger con lo stesso checksum viene saltato;
|
|
64
|
+
* - uno script gia applicato ma modificato segue `onChecksumMismatch` (`error` default, `warn`, `reapply`);
|
|
65
|
+
* - un errore interrompe il run (default) senza avanzare, lasciando il ledger all'ultimo script completato.
|
|
66
|
+
*
|
|
67
|
+
* Il chiamante deve avere gia riconciliato lo schema (`AccessiDatabaseUpdater.run`): la tabella
|
|
68
|
+
* ledger `ACCESSI_CATALOG_SCRIPT` e creata dall'updater.
|
|
69
|
+
*/
|
|
70
|
+
export declare function applyCatalogScripts(options: AccessiOptions): Promise<CatalogMigrationReport>;
|
|
71
|
+
/** Esegue la riconciliazione dello schema non e responsabilita di questa classe: usare l'updater prima. */
|
|
72
|
+
export declare class AccessiCatalogMigrator {
|
|
73
|
+
static apply(options: AccessiOptions): Promise<CatalogMigrationReport>;
|
|
74
|
+
static listPending(options: AccessiOptions): Promise<{
|
|
75
|
+
applied: string[];
|
|
76
|
+
pending: string[];
|
|
77
|
+
changed: string[];
|
|
78
|
+
invalid: {
|
|
79
|
+
script: string;
|
|
80
|
+
violations: string[];
|
|
81
|
+
}[];
|
|
82
|
+
}>;
|
|
83
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AccessiCatalogMigrator = void 0;
|
|
13
|
+
exports.leadingStatementKeyword = leadingStatementKeyword;
|
|
14
|
+
exports.analyzeCatalogScript = analyzeCatalogScript;
|
|
15
|
+
exports.computeCatalogScriptChecksum = computeCatalogScriptChecksum;
|
|
16
|
+
exports.splitSqlStatements = splitSqlStatements;
|
|
17
|
+
exports.discoverCatalogScripts = discoverCatalogScripts;
|
|
18
|
+
exports.listPendingCatalogScripts = listPendingCatalogScripts;
|
|
19
|
+
exports.applyCatalogScripts = applyCatalogScripts;
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
21
|
+
const fs_1 = require("fs");
|
|
22
|
+
const path_1 = require("path");
|
|
23
|
+
const Orm_1 = require("../../Orm");
|
|
24
|
+
const Logger_1 = require("../../Logger");
|
|
25
|
+
/**
|
|
26
|
+
* Le catalog migrations possono modificare SOLO righe (DML). Questa whitelist rende tassativo il
|
|
27
|
+
* vincolo: qualunque altro statement (DDL, trigger, transazioni, GRANT, ...) viene rifiutato.
|
|
28
|
+
*/
|
|
29
|
+
const ALLOWED_STATEMENT_KEYWORDS = new Set([
|
|
30
|
+
'INSERT', 'UPDATE', 'DELETE', 'MERGE', 'SELECT', 'EXECUTE',
|
|
31
|
+
]);
|
|
32
|
+
/** Estrae la prima parola chiave di uno statement, saltando spazi e commenti iniziali. */
|
|
33
|
+
function leadingStatementKeyword(statement) {
|
|
34
|
+
let index = 0;
|
|
35
|
+
while (index < statement.length) {
|
|
36
|
+
const char = statement[index];
|
|
37
|
+
if (/\s/.test(char)) {
|
|
38
|
+
index += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (char === '-' && statement[index + 1] === '-') {
|
|
42
|
+
const newline = statement.indexOf('\n', index);
|
|
43
|
+
if (newline === -1)
|
|
44
|
+
return '';
|
|
45
|
+
index = newline + 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === '/' && statement[index + 1] === '*') {
|
|
49
|
+
const end = statement.indexOf('*/', index + 2);
|
|
50
|
+
if (end === -1)
|
|
51
|
+
return '';
|
|
52
|
+
index = end + 2;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const match = /^([A-Za-z]+)/.exec(statement.slice(index));
|
|
58
|
+
return match ? match[1].toUpperCase() : '';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Valida gli statement di una catalog migration: ritorna la lista delle violazioni (vuota se conforme).
|
|
62
|
+
* Regola tassativa: solo DML. Nessuna modifica a colonne/schema/trigger/permessi.
|
|
63
|
+
*/
|
|
64
|
+
function analyzeCatalogScript(statements) {
|
|
65
|
+
const violations = [];
|
|
66
|
+
for (const statement of statements) {
|
|
67
|
+
const keyword = leadingStatementKeyword(statement);
|
|
68
|
+
if (!keyword)
|
|
69
|
+
continue;
|
|
70
|
+
if (!ALLOWED_STATEMENT_KEYWORDS.has(keyword)) {
|
|
71
|
+
const preview = statement.replace(/\s+/g, ' ').trim().slice(0, 120);
|
|
72
|
+
violations.push(`Statement non consentito (${keyword}): le catalog migrations possono modificare solo righe (INSERT/UPDATE/DELETE/MERGE). Estratto: "${preview}"`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return violations;
|
|
76
|
+
}
|
|
77
|
+
/** Ritorna la policy di checksum effettiva (default `error`). */
|
|
78
|
+
function checksumPolicy(options) {
|
|
79
|
+
var _a;
|
|
80
|
+
return (_a = options.onChecksumMismatch) !== null && _a !== void 0 ? _a : 'error';
|
|
81
|
+
}
|
|
82
|
+
/** Hash SHA-256 del contenuto dello script, usato per rilevare modifiche non versionate. */
|
|
83
|
+
function computeCatalogScriptChecksum(content) {
|
|
84
|
+
return (0, crypto_1.createHash)('sha256').update(content, 'utf8').digest('hex');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Divide un file SQL in statement eseguibili separatamente.
|
|
88
|
+
*
|
|
89
|
+
* Supporta:
|
|
90
|
+
* - terminator personalizzati con `SET TERM <char>` / `SET TERM <char> ;`
|
|
91
|
+
* - stringhe `'...'` con escape `''`
|
|
92
|
+
* - commenti di riga `--` e di blocco `/* ... * /`
|
|
93
|
+
*
|
|
94
|
+
* Non interpreta la semantica SQL: e volutamente un tokenizer prudente che non spezza
|
|
95
|
+
* mai all'interno di stringhe o commenti.
|
|
96
|
+
*/
|
|
97
|
+
function splitSqlStatements(sql, defaultTerminator = ';') {
|
|
98
|
+
var _a;
|
|
99
|
+
const statements = [];
|
|
100
|
+
const lines = sql.replace(/\r\n?/g, '\n').split('\n');
|
|
101
|
+
let buffer = '';
|
|
102
|
+
let terminator = defaultTerminator;
|
|
103
|
+
let inString = false;
|
|
104
|
+
let inLineComment = false;
|
|
105
|
+
let inBlockComment = false;
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
const setTerm = /^\s*SET\s+TERM\s+(\S+)/i.exec(line);
|
|
108
|
+
if (setTerm && !inString && !inLineComment && !inBlockComment) {
|
|
109
|
+
const raw = (_a = setTerm[1]) !== null && _a !== void 0 ? _a : '';
|
|
110
|
+
terminator = raw.length > 1 && raw.endsWith(';') ? raw.slice(0, -1) : raw || defaultTerminator;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
buffer += `${line}\n`;
|
|
114
|
+
let start = 0;
|
|
115
|
+
let index = 0;
|
|
116
|
+
while (index < buffer.length) {
|
|
117
|
+
const char = buffer[index];
|
|
118
|
+
const next = buffer[index + 1];
|
|
119
|
+
if (inLineComment) {
|
|
120
|
+
if (char === '\n')
|
|
121
|
+
inLineComment = false;
|
|
122
|
+
index += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (inBlockComment) {
|
|
126
|
+
if (char === '*' && next === '/') {
|
|
127
|
+
inBlockComment = false;
|
|
128
|
+
index += 2;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
index += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (inString) {
|
|
135
|
+
if (char === "'") {
|
|
136
|
+
if (next === "'") {
|
|
137
|
+
index += 2;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
inString = false;
|
|
141
|
+
}
|
|
142
|
+
index += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (char === "'") {
|
|
146
|
+
inString = true;
|
|
147
|
+
index += 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (char === '-' && next === '-') {
|
|
151
|
+
inLineComment = true;
|
|
152
|
+
index += 2;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (char === '/' && next === '*') {
|
|
156
|
+
inBlockComment = true;
|
|
157
|
+
index += 2;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (buffer.startsWith(terminator, index)) {
|
|
161
|
+
const statement = buffer.slice(start, index).trim();
|
|
162
|
+
if (statement)
|
|
163
|
+
statements.push(statement);
|
|
164
|
+
index += terminator.length;
|
|
165
|
+
start = index;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
index += 1;
|
|
169
|
+
}
|
|
170
|
+
buffer = buffer.slice(start);
|
|
171
|
+
}
|
|
172
|
+
const trailing = buffer.trim();
|
|
173
|
+
if (trailing)
|
|
174
|
+
statements.push(trailing);
|
|
175
|
+
return statements;
|
|
176
|
+
}
|
|
177
|
+
/** Elenca gli script `.sql` della cartella, ordinati per percorso (prefissi numerici consigliati). */
|
|
178
|
+
function discoverCatalogScripts(options) {
|
|
179
|
+
const folder = (0, path_1.resolve)(options.folder);
|
|
180
|
+
if (!(0, fs_1.existsSync)(folder)) {
|
|
181
|
+
throw new Error(`AccessiCatalogMigrator: cartella non trovata: ${folder}`);
|
|
182
|
+
}
|
|
183
|
+
const include = options.include && options.include.length > 0 ? options.include : ['.sql'];
|
|
184
|
+
const found = [];
|
|
185
|
+
const walk = (directory) => {
|
|
186
|
+
for (const entry of (0, fs_1.readdirSync)(directory, { withFileTypes: true })) {
|
|
187
|
+
const absolute = (0, path_1.join)(directory, entry.name);
|
|
188
|
+
if (entry.isDirectory()) {
|
|
189
|
+
walk(absolute);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!entry.isFile())
|
|
193
|
+
continue;
|
|
194
|
+
const matches = include.some((pattern) => pattern.startsWith('.') ? (0, path_1.extname)(entry.name).toLowerCase() === pattern.toLowerCase() : entry.name.includes(pattern));
|
|
195
|
+
if (!matches)
|
|
196
|
+
continue;
|
|
197
|
+
const name = (0, path_1.relative)(folder, absolute).split(path_1.sep).join('/');
|
|
198
|
+
found.push({ name, path: absolute });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
walk(folder);
|
|
202
|
+
return found.sort((a, b) => a.name.localeCompare(b.name));
|
|
203
|
+
}
|
|
204
|
+
function isLedgerPresent(options) {
|
|
205
|
+
return Orm_1.Orm.query(options.databaseOptions, "SELECT 1 FROM RDB$RELATIONS WHERE TRIM(RDB$RELATION_NAME) = 'ACCESSI_CATALOG_SCRIPT'", [], false).then((rows) => rows.length > 0);
|
|
206
|
+
}
|
|
207
|
+
/** Legge il ledger come mappa `script -> checksum`. */
|
|
208
|
+
function readLedger(options) {
|
|
209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
210
|
+
var _a, _b, _c, _d;
|
|
211
|
+
const rows = (yield Orm_1.Orm.query(options.databaseOptions, 'SELECT SCRIPT_NAME AS NAME, CHECKSUM FROM ACCESSI_CATALOG_SCRIPT', [], false));
|
|
212
|
+
const ledger = new Map();
|
|
213
|
+
for (const row of rows) {
|
|
214
|
+
const name = String((_b = (_a = row.NAME) !== null && _a !== void 0 ? _a : row.name) !== null && _b !== void 0 ? _b : '').trim();
|
|
215
|
+
const checksum = String((_d = (_c = row.CHECKSUM) !== null && _c !== void 0 ? _c : row.checksum) !== null && _d !== void 0 ? _d : '').trim();
|
|
216
|
+
if (name)
|
|
217
|
+
ledger.set(name, checksum);
|
|
218
|
+
}
|
|
219
|
+
return ledger;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function upsertLedger(options, name, checksum, reapplied) {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
if (reapplied) {
|
|
225
|
+
yield Orm_1.Orm.execute(options.databaseOptions, 'UPDATE ACCESSI_CATALOG_SCRIPT SET CHECKSUM = ?, APPLIED_AT = CURRENT_TIMESTAMP, APPLIED_COUNT = APPLIED_COUNT + 1 WHERE SCRIPT_NAME = ?', [checksum, name], false);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
yield Orm_1.Orm.execute(options.databaseOptions, 'UPDATE OR INSERT INTO ACCESSI_CATALOG_SCRIPT (SCRIPT_NAME, CHECKSUM, APPLIED_AT, APPLIED_COUNT) VALUES (?, ?, CURRENT_TIMESTAMP, 1) MATCHING (SCRIPT_NAME)', [name, checksum], false);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/** Elenca gli script non ancora applicati (o con checksum diverso) senza eseguirli. */
|
|
232
|
+
function listPendingCatalogScripts(options) {
|
|
233
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
+
const config = options.catalogScripts;
|
|
235
|
+
if (!(config === null || config === void 0 ? void 0 : config.folder))
|
|
236
|
+
throw new Error('AccessiCatalogMigrator: catalogScripts.folder non configurato.');
|
|
237
|
+
if (!(yield isLedgerPresent(options))) {
|
|
238
|
+
throw new Error('AccessiCatalogMigrator: tabella ACCESSI_CATALOG_SCRIPT assente. Eseguire prima AccessiDatabaseUpdater.run (db:update:accessi).');
|
|
239
|
+
}
|
|
240
|
+
const allowDdl = config.allowDdl === true;
|
|
241
|
+
const ledger = yield readLedger(options);
|
|
242
|
+
const discovered = discoverCatalogScripts(config);
|
|
243
|
+
const pending = [];
|
|
244
|
+
const changed = [];
|
|
245
|
+
const invalid = [];
|
|
246
|
+
for (const script of discovered) {
|
|
247
|
+
const content = (0, fs_1.readFileSync)(script.path, 'utf8');
|
|
248
|
+
const checksum = computeCatalogScriptChecksum(content);
|
|
249
|
+
const violations = allowDdl ? [] : analyzeCatalogScript(splitSqlStatements(content));
|
|
250
|
+
if (violations.length > 0) {
|
|
251
|
+
invalid.push({ script: script.name, violations });
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const appliedChecksum = ledger.get(script.name);
|
|
255
|
+
if (appliedChecksum === undefined)
|
|
256
|
+
pending.push(script.name);
|
|
257
|
+
else if (appliedChecksum !== checksum)
|
|
258
|
+
changed.push(script.name);
|
|
259
|
+
}
|
|
260
|
+
return { applied: [...ledger.keys()].sort(), pending, changed, invalid };
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Applica le catalog migrations presenti nella cartella configurata.
|
|
265
|
+
*
|
|
266
|
+
* Idempotenza:
|
|
267
|
+
* - uno script gia presente nel ledger con lo stesso checksum viene saltato;
|
|
268
|
+
* - uno script gia applicato ma modificato segue `onChecksumMismatch` (`error` default, `warn`, `reapply`);
|
|
269
|
+
* - un errore interrompe il run (default) senza avanzare, lasciando il ledger all'ultimo script completato.
|
|
270
|
+
*
|
|
271
|
+
* Il chiamante deve avere gia riconciliato lo schema (`AccessiDatabaseUpdater.run`): la tabella
|
|
272
|
+
* ledger `ACCESSI_CATALOG_SCRIPT` e creata dall'updater.
|
|
273
|
+
*/
|
|
274
|
+
function applyCatalogScripts(options) {
|
|
275
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
276
|
+
var _a;
|
|
277
|
+
const logger = new Logger_1.Logger(AccessiCatalogMigrator.name);
|
|
278
|
+
const config = options.catalogScripts;
|
|
279
|
+
if (!(config === null || config === void 0 ? void 0 : config.folder))
|
|
280
|
+
throw new Error('AccessiCatalogMigrator: catalogScripts.folder non configurato.');
|
|
281
|
+
if (!(yield isLedgerPresent(options))) {
|
|
282
|
+
throw new Error('AccessiCatalogMigrator: tabella ACCESSI_CATALOG_SCRIPT assente. Eseguire prima AccessiDatabaseUpdater.run (db:update:accessi).');
|
|
283
|
+
}
|
|
284
|
+
const folder = (0, path_1.resolve)(config.folder);
|
|
285
|
+
const discovered = discoverCatalogScripts(config);
|
|
286
|
+
const ledger = yield readLedger(options);
|
|
287
|
+
const stopOnError = config.stopOnError !== false;
|
|
288
|
+
const allowDdl = config.allowDdl === true;
|
|
289
|
+
const maxScripts = Number.isInteger(config.maxScriptsPerRun) && ((_a = config.maxScriptsPerRun) !== null && _a !== void 0 ? _a : 0) > 0 ? config.maxScriptsPerRun : undefined;
|
|
290
|
+
const report = { folder, discovered: discovered.length, applied: [], skipped: [], invalid: [], failed: [] };
|
|
291
|
+
logger.info(`Catalog migrations: ${discovered.length} script trovati in ${folder}.`);
|
|
292
|
+
for (const script of discovered) {
|
|
293
|
+
if (maxScripts !== undefined && report.applied.length >= maxScripts) {
|
|
294
|
+
logger.warning(`Catalog migrations: limite maxScriptsPerRun=${maxScripts} raggiunto, resto rimandato.`);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
const content = (0, fs_1.readFileSync)(script.path, 'utf8');
|
|
298
|
+
const checksum = computeCatalogScriptChecksum(content);
|
|
299
|
+
const appliedChecksum = ledger.get(script.name);
|
|
300
|
+
if (appliedChecksum === checksum) {
|
|
301
|
+
report.skipped.push(script.name);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
let reapplied = false;
|
|
305
|
+
if (appliedChecksum !== undefined && appliedChecksum !== checksum) {
|
|
306
|
+
const policy = checksumPolicy(config);
|
|
307
|
+
if (policy === 'error') {
|
|
308
|
+
throw new Error(`AccessiCatalogMigrator: lo script "${script.name}" e gia applicato con un checksum diverso (migrazioni immutabili). ` +
|
|
309
|
+
`Ripristina il file o configura onChecksumMismatch='reapply'/'warn' consapevolmente.`);
|
|
310
|
+
}
|
|
311
|
+
if (policy === 'warn') {
|
|
312
|
+
logger.warning(`Catalog migrations: "${script.name}" applicato con checksum diverso, ignorato (policy warn).`);
|
|
313
|
+
report.skipped.push(script.name);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
reapplied = true;
|
|
317
|
+
logger.warning(`Catalog migrations: "${script.name}" riapplicato (policy reapply).`);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const statements = splitSqlStatements(content);
|
|
321
|
+
const violations = allowDdl ? [] : analyzeCatalogScript(statements);
|
|
322
|
+
if (violations.length > 0) {
|
|
323
|
+
report.invalid.push({ script: script.name, violations });
|
|
324
|
+
for (const violation of violations) {
|
|
325
|
+
logger.error(`Catalog migration non conforme: ${script.name}. ${violation}`);
|
|
326
|
+
}
|
|
327
|
+
// Il file non viene applicato ne registrato nel ledger; il run prosegue con gli altri.
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
for (const statement of statements) {
|
|
331
|
+
yield Orm_1.Orm.execute(options.databaseOptions, statement, [], false);
|
|
332
|
+
}
|
|
333
|
+
yield upsertLedger(options, script.name, checksum, reapplied);
|
|
334
|
+
ledger.set(script.name, checksum);
|
|
335
|
+
report.applied.push(script.name);
|
|
336
|
+
logger.info(`Catalog migration applicata: ${script.name} (${statements.length} statement).`);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
340
|
+
report.failed.push({ script: script.name, error: message });
|
|
341
|
+
logger.error(`Catalog migration fallita: ${script.name}. ${message}`);
|
|
342
|
+
if (stopOnError) {
|
|
343
|
+
throw new Error(`AccessiCatalogMigrator: script "${script.name}" fallito. ${message}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return report;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/** Esegue la riconciliazione dello schema non e responsabilita di questa classe: usare l'updater prima. */
|
|
351
|
+
class AccessiCatalogMigrator {
|
|
352
|
+
static apply(options) {
|
|
353
|
+
return applyCatalogScripts(options);
|
|
354
|
+
}
|
|
355
|
+
static listPending(options) {
|
|
356
|
+
return listPendingCatalogScripts(options);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
exports.AccessiCatalogMigrator = AccessiCatalogMigrator;
|
|
360
|
+
//# sourceMappingURL=AccessiCatalogMigrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessiCatalogMigrator.js","sourceRoot":"","sources":["../../../src/accessi-module/database-updates/AccessiCatalogMigrator.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'crypto';\nimport { existsSync, readFileSync, readdirSync } from 'fs';\nimport { extname, join, relative, resolve, sep } from 'path';\nimport { Orm } from '../../Orm';\nimport { Logger } from '../../Logger';\nimport type { AccessiOptions, CatalogScriptChecksumPolicy, CatalogScriptsOptions } from '../AccessiModule';\n\nexport interface CatalogScriptMigration {\n name: string;\n applied: boolean;\n skipped: boolean;\n reapplied: boolean;\n error?: string;\n}\n\nexport interface CatalogMigrationReport {\n folder: string;\n discovered: number;\n applied: string[];\n skipped: string[];\n invalid: { script: string; violations: string[] }[];\n failed: { script: string; error: string }[];\n}\n\n/**\n * Le catalog migrations possono modificare SOLO righe (DML). Questa whitelist rende tassativo il\n * vincolo: qualunque altro statement (DDL, trigger, transazioni, GRANT, ...) viene rifiutato.\n */\nconst ALLOWED_STATEMENT_KEYWORDS: ReadonlySet<string> = new Set([\n 'INSERT', 'UPDATE', 'DELETE', 'MERGE', 'SELECT', 'EXECUTE',\n]);\n\n/** Estrae la prima parola chiave di uno statement, saltando spazi e commenti iniziali. */\nexport function leadingStatementKeyword(statement: string): string {\n let index = 0;\n while (index < statement.length) {\n const char = statement[index] as string;\n if (/\\s/.test(char)) { index += 1; continue; }\n if (char === '-' && statement[index + 1] === '-') {\n const newline = statement.indexOf('\\n', index);\n if (newline === -1) return '';\n index = newline + 1;\n continue;\n }\n if (char === '/' && statement[index + 1] === '*') {\n const end = statement.indexOf('*/', index + 2);\n if (end === -1) return '';\n index = end + 2;\n continue;\n }\n break;\n }\n const match = /^([A-Za-z]+)/.exec(statement.slice(index));\n return match ? (match[1] as string).toUpperCase() : '';\n}\n\n/**\n * Valida gli statement di una catalog migration: ritorna la lista delle violazioni (vuota se conforme).\n * Regola tassativa: solo DML. Nessuna modifica a colonne/schema/trigger/permessi.\n */\nexport function analyzeCatalogScript(statements: string[]): string[] {\n const violations: string[] = [];\n for (const statement of statements) {\n const keyword = leadingStatementKeyword(statement);\n if (!keyword) continue;\n if (!ALLOWED_STATEMENT_KEYWORDS.has(keyword)) {\n const preview = statement.replace(/\\s+/g, ' ').trim().slice(0, 120);\n violations.push(\n `Statement non consentito (${keyword}): le catalog migrations possono modificare solo righe (INSERT/UPDATE/DELETE/MERGE). Estratto: \"${preview}\"`,\n );\n }\n }\n return violations;\n}\n\n/** Ritorna la policy di checksum effettiva (default `error`). */\nfunction checksumPolicy(options: CatalogScriptsOptions): CatalogScriptChecksumPolicy {\n return options.onChecksumMismatch ?? 'error';\n}\n\n/** Hash SHA-256 del contenuto dello script, usato per rilevare modifiche non versionate. */\nexport function computeCatalogScriptChecksum(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/**\n * Divide un file SQL in statement eseguibili separatamente.\n *\n * Supporta:\n * - terminator personalizzati con `SET TERM <char>` / `SET TERM <char> ;`\n * - stringhe `'...'` con escape `''`\n * - commenti di riga `--` e di blocco `/* ... * /`\n *\n * Non interpreta la semantica SQL: e volutamente un tokenizer prudente che non spezza\n * mai all'interno di stringhe o commenti.\n */\nexport function splitSqlStatements(sql: string, defaultTerminator = ';'): string[] {\n const statements: string[] = [];\n const lines = sql.replace(/\\r\\n?/g, '\\n').split('\\n');\n let buffer = '';\n let terminator = defaultTerminator;\n let inString = false;\n let inLineComment = false;\n let inBlockComment = false;\n\n for (const line of lines) {\n const setTerm = /^\\s*SET\\s+TERM\\s+(\\S+)/i.exec(line);\n if (setTerm && !inString && !inLineComment && !inBlockComment) {\n const raw = setTerm[1] ?? '';\n terminator = raw.length > 1 && raw.endsWith(';') ? raw.slice(0, -1) : raw || defaultTerminator;\n continue;\n }\n\n buffer += `${line}\\n`;\n\n let start = 0;\n let index = 0;\n while (index < buffer.length) {\n const char = buffer[index] as string;\n const next = buffer[index + 1];\n\n if (inLineComment) {\n if (char === '\\n') inLineComment = false;\n index += 1;\n continue;\n }\n if (inBlockComment) {\n if (char === '*' && next === '/') {\n inBlockComment = false;\n index += 2;\n continue;\n }\n index += 1;\n continue;\n }\n if (inString) {\n if (char === \"'\") {\n if (next === \"'\") {\n index += 2;\n continue;\n }\n inString = false;\n }\n index += 1;\n continue;\n }\n\n if (char === \"'\") {\n inString = true;\n index += 1;\n continue;\n }\n if (char === '-' && next === '-') {\n inLineComment = true;\n index += 2;\n continue;\n }\n if (char === '/' && next === '*') {\n inBlockComment = true;\n index += 2;\n continue;\n }\n if (buffer.startsWith(terminator, index)) {\n const statement = buffer.slice(start, index).trim();\n if (statement) statements.push(statement);\n index += terminator.length;\n start = index;\n continue;\n }\n index += 1;\n }\n\n buffer = buffer.slice(start);\n }\n\n const trailing = buffer.trim();\n if (trailing) statements.push(trailing);\n return statements;\n}\n\n/** Elenca gli script `.sql` della cartella, ordinati per percorso (prefissi numerici consigliati). */\nexport function discoverCatalogScripts(options: CatalogScriptsOptions): { name: string; path: string }[] {\n const folder = resolve(options.folder);\n if (!existsSync(folder)) {\n throw new Error(`AccessiCatalogMigrator: cartella non trovata: ${folder}`);\n }\n\n const include = options.include && options.include.length > 0 ? options.include : ['.sql'];\n const found: { name: string; path: string }[] = [];\n\n const walk = (directory: string): void => {\n for (const entry of readdirSync(directory, { withFileTypes: true })) {\n const absolute = join(directory, entry.name);\n if (entry.isDirectory()) {\n walk(absolute);\n continue;\n }\n if (!entry.isFile()) continue;\n const matches = include.some((pattern) =>\n pattern.startsWith('.') ? extname(entry.name).toLowerCase() === pattern.toLowerCase() : entry.name.includes(pattern),\n );\n if (!matches) continue;\n const name = relative(folder, absolute).split(sep).join('/');\n found.push({ name, path: absolute });\n }\n };\n\n walk(folder);\n return found.sort((a, b) => a.name.localeCompare(b.name));\n}\n\nfunction isLedgerPresent(options: AccessiOptions): Promise<boolean> {\n return Orm.query(\n options.databaseOptions,\n \"SELECT 1 FROM RDB$RELATIONS WHERE TRIM(RDB$RELATION_NAME) = 'ACCESSI_CATALOG_SCRIPT'\",\n [],\n false,\n ).then((rows) => rows.length > 0);\n}\n\n/** Legge il ledger come mappa `script -> checksum`. */\nasync function readLedger(options: AccessiOptions): Promise<Map<string, string>> {\n const rows = (await Orm.query(\n options.databaseOptions,\n 'SELECT SCRIPT_NAME AS NAME, CHECKSUM FROM ACCESSI_CATALOG_SCRIPT',\n [],\n false,\n )) as Array<Record<string, unknown>>;\n const ledger = new Map<string, string>();\n for (const row of rows) {\n const name = String(row.NAME ?? row.name ?? '').trim();\n const checksum = String(row.CHECKSUM ?? row.checksum ?? '').trim();\n if (name) ledger.set(name, checksum);\n }\n return ledger;\n}\n\nasync function upsertLedger(options: AccessiOptions, name: string, checksum: string, reapplied: boolean): Promise<void> {\n if (reapplied) {\n await Orm.execute(\n options.databaseOptions,\n 'UPDATE ACCESSI_CATALOG_SCRIPT SET CHECKSUM = ?, APPLIED_AT = CURRENT_TIMESTAMP, APPLIED_COUNT = APPLIED_COUNT + 1 WHERE SCRIPT_NAME = ?',\n [checksum, name],\n false,\n );\n return;\n }\n await Orm.execute(\n options.databaseOptions,\n 'UPDATE OR INSERT INTO ACCESSI_CATALOG_SCRIPT (SCRIPT_NAME, CHECKSUM, APPLIED_AT, APPLIED_COUNT) VALUES (?, ?, CURRENT_TIMESTAMP, 1) MATCHING (SCRIPT_NAME)',\n [name, checksum],\n false,\n );\n}\n\n/** Elenca gli script non ancora applicati (o con checksum diverso) senza eseguirli. */\nexport async function listPendingCatalogScripts(options: AccessiOptions): Promise<{ applied: string[]; pending: string[]; changed: string[]; invalid: { script: string; violations: string[] }[] }> {\n const config = options.catalogScripts;\n if (!config?.folder) throw new Error('AccessiCatalogMigrator: catalogScripts.folder non configurato.');\n if (!(await isLedgerPresent(options))) {\n throw new Error('AccessiCatalogMigrator: tabella ACCESSI_CATALOG_SCRIPT assente. Eseguire prima AccessiDatabaseUpdater.run (db:update:accessi).');\n }\n\n const allowDdl = config.allowDdl === true;\n const ledger = await readLedger(options);\n const discovered = discoverCatalogScripts(config);\n const pending: string[] = [];\n const changed: string[] = [];\n const invalid: { script: string; violations: string[] }[] = [];\n\n for (const script of discovered) {\n const content = readFileSync(script.path, 'utf8');\n const checksum = computeCatalogScriptChecksum(content);\n const violations = allowDdl ? [] : analyzeCatalogScript(splitSqlStatements(content));\n if (violations.length > 0) {\n invalid.push({ script: script.name, violations });\n continue;\n }\n const appliedChecksum = ledger.get(script.name);\n if (appliedChecksum === undefined) pending.push(script.name);\n else if (appliedChecksum !== checksum) changed.push(script.name);\n }\n\n return { applied: [...ledger.keys()].sort(), pending, changed, invalid };\n}\n\n/**\n * Applica le catalog migrations presenti nella cartella configurata.\n *\n * Idempotenza:\n * - uno script gia presente nel ledger con lo stesso checksum viene saltato;\n * - uno script gia applicato ma modificato segue `onChecksumMismatch` (`error` default, `warn`, `reapply`);\n * - un errore interrompe il run (default) senza avanzare, lasciando il ledger all'ultimo script completato.\n *\n * Il chiamante deve avere gia riconciliato lo schema (`AccessiDatabaseUpdater.run`): la tabella\n * ledger `ACCESSI_CATALOG_SCRIPT` e creata dall'updater.\n */\nexport async function applyCatalogScripts(options: AccessiOptions): Promise<CatalogMigrationReport> {\n const logger = new Logger(AccessiCatalogMigrator.name);\n const config = options.catalogScripts;\n if (!config?.folder) throw new Error('AccessiCatalogMigrator: catalogScripts.folder non configurato.');\n if (!(await isLedgerPresent(options))) {\n throw new Error('AccessiCatalogMigrator: tabella ACCESSI_CATALOG_SCRIPT assente. Eseguire prima AccessiDatabaseUpdater.run (db:update:accessi).');\n }\n\n const folder = resolve(config.folder);\n const discovered = discoverCatalogScripts(config);\n const ledger = await readLedger(options);\n const stopOnError = config.stopOnError !== false;\n const allowDdl = config.allowDdl === true;\n const maxScripts = Number.isInteger(config.maxScriptsPerRun) && (config.maxScriptsPerRun ?? 0) > 0 ? config.maxScriptsPerRun : undefined;\n const report: CatalogMigrationReport = { folder, discovered: discovered.length, applied: [], skipped: [], invalid: [], failed: [] };\n\n logger.info(`Catalog migrations: ${discovered.length} script trovati in ${folder}.`);\n\n for (const script of discovered) {\n if (maxScripts !== undefined && report.applied.length >= maxScripts) {\n logger.warning(`Catalog migrations: limite maxScriptsPerRun=${maxScripts} raggiunto, resto rimandato.`);\n break;\n }\n\n const content = readFileSync(script.path, 'utf8');\n const checksum = computeCatalogScriptChecksum(content);\n const appliedChecksum = ledger.get(script.name);\n\n if (appliedChecksum === checksum) {\n report.skipped.push(script.name);\n continue;\n }\n\n let reapplied = false;\n if (appliedChecksum !== undefined && appliedChecksum !== checksum) {\n const policy = checksumPolicy(config);\n if (policy === 'error') {\n throw new Error(\n `AccessiCatalogMigrator: lo script \"${script.name}\" e gia applicato con un checksum diverso (migrazioni immutabili). ` +\n `Ripristina il file o configura onChecksumMismatch='reapply'/'warn' consapevolmente.`,\n );\n }\n if (policy === 'warn') {\n logger.warning(`Catalog migrations: \"${script.name}\" applicato con checksum diverso, ignorato (policy warn).`);\n report.skipped.push(script.name);\n continue;\n }\n reapplied = true;\n logger.warning(`Catalog migrations: \"${script.name}\" riapplicato (policy reapply).`);\n }\n\n try {\n const statements = splitSqlStatements(content);\n const violations = allowDdl ? [] : analyzeCatalogScript(statements);\n if (violations.length > 0) {\n report.invalid.push({ script: script.name, violations });\n for (const violation of violations) {\n logger.error(`Catalog migration non conforme: ${script.name}. ${violation}`);\n }\n // Il file non viene applicato ne registrato nel ledger; il run prosegue con gli altri.\n continue;\n }\n for (const statement of statements) {\n await Orm.execute(options.databaseOptions, statement, [], false);\n }\n await upsertLedger(options, script.name, checksum, reapplied);\n ledger.set(script.name, checksum);\n report.applied.push(script.name);\n logger.info(`Catalog migration applicata: ${script.name} (${statements.length} statement).`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n report.failed.push({ script: script.name, error: message });\n logger.error(`Catalog migration fallita: ${script.name}. ${message}`);\n if (stopOnError) {\n throw new Error(`AccessiCatalogMigrator: script \"${script.name}\" fallito. ${message}`);\n }\n }\n }\n\n return report;\n}\n\n/** Esegue la riconciliazione dello schema non e responsabilita di questa classe: usare l'updater prima. */\nexport class AccessiCatalogMigrator {\n static apply(options: AccessiOptions): Promise<CatalogMigrationReport> {\n return applyCatalogScripts(options);\n }\n\n static listPending(options: AccessiOptions): Promise<{ applied: string[]; pending: string[]; changed: string[]; invalid: { script: string; violations: string[] }[] }> {\n return listPendingCatalogScripts(options);\n }\n}\n"]}
|
|
@@ -16,6 +16,12 @@ export declare class AccessiDatabaseUpdater extends DatabaseUpdater implements O
|
|
|
16
16
|
onModuleInit(): Promise<void>;
|
|
17
17
|
/** Shared bootstrap barrier for services in the same Nest module. Failed attempts may be retried. */
|
|
18
18
|
static initialize(options: AccessiOptions): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Prima riconcilia lo schema, poi applica le catalog migrations applicative (menu, ruoli, ...).
|
|
21
|
+
* Con `autoUpdateDatabase: false` la verifica resta in sola lettura e le catalog migrations
|
|
22
|
+
* non vengono eseguite automaticamente.
|
|
23
|
+
*/
|
|
24
|
+
private static bootstrap;
|
|
19
25
|
static getLatestVersion(): string;
|
|
20
26
|
static getCurrentVersion(options: AccessiOptions): Promise<string | null>;
|
|
21
27
|
/** Serialize callers targeting the same database in this process. Cross-process DDL is a deployment concern. */
|
|
@@ -28,6 +28,7 @@ const DatabaseUpdater_1 = require("../../DatabaseUpdater");
|
|
|
28
28
|
const Orm_1 = require("../../Orm");
|
|
29
29
|
const Logger_1 = require("../../Logger");
|
|
30
30
|
const accessiSchema_1 = require("./accessiSchema");
|
|
31
|
+
const AccessiCatalogMigrator_1 = require("./AccessiCatalogMigrator");
|
|
31
32
|
/** Reconciles the actual schema; version labels never bypass verification. */
|
|
32
33
|
let AccessiDatabaseUpdater = AccessiDatabaseUpdater_1 = class AccessiDatabaseUpdater extends DatabaseUpdater_1.DatabaseUpdater {
|
|
33
34
|
constructor(accessiOptions) {
|
|
@@ -41,13 +42,31 @@ let AccessiDatabaseUpdater = AccessiDatabaseUpdater_1 = class AccessiDatabaseUpd
|
|
|
41
42
|
static initialize(options) {
|
|
42
43
|
let pending = this.initialization.get(options);
|
|
43
44
|
if (!pending) {
|
|
44
|
-
pending =
|
|
45
|
+
pending = this.bootstrap(options);
|
|
45
46
|
this.initialization.set(options, pending);
|
|
46
47
|
const cleanup = () => this.initialization.delete(options);
|
|
47
48
|
pending.then(cleanup, cleanup);
|
|
48
49
|
}
|
|
49
50
|
return pending;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Prima riconcilia lo schema, poi applica le catalog migrations applicative (menu, ruoli, ...).
|
|
54
|
+
* Con `autoUpdateDatabase: false` la verifica resta in sola lettura e le catalog migrations
|
|
55
|
+
* non vengono eseguite automaticamente.
|
|
56
|
+
*/
|
|
57
|
+
static bootstrap(options) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
var _a;
|
|
60
|
+
if (options.autoUpdateDatabase === false) {
|
|
61
|
+
yield this.assertCompatible(options);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
yield this.run(options);
|
|
65
|
+
if ((_a = options.catalogScripts) === null || _a === void 0 ? void 0 : _a.enabled) {
|
|
66
|
+
yield (0, AccessiCatalogMigrator_1.applyCatalogScripts)(options);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
51
70
|
static getLatestVersion() { return accessiSchema_1.ACCESSI_SCHEMA_VERSION; }
|
|
52
71
|
static getCurrentVersion(options) {
|
|
53
72
|
return __awaiter(this, void 0, void 0, function* () {
|