@zintrust/d1-migrator 1.8.1 → 1.8.3
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/cli/DataMigrator.d.ts +1 -0
- package/dist/cli/DataMigrator.d.ts.map +1 -1
- package/dist/cli/DataMigrator.js +125 -43
- package/dist/cli/MigrateToD1Command.d.ts.map +1 -1
- package/dist/cli/MigrateToD1Command.js +162 -8
- package/dist/cli/SchemaAnalyzer.d.ts +3 -1
- package/dist/cli/SchemaAnalyzer.d.ts.map +1 -1
- package/dist/cli/SchemaAnalyzer.js +87 -4
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ import type { MigrationConfig, MigrationProgress } from '../types';
|
|
|
9
9
|
export interface SourceConnection {
|
|
10
10
|
driver: MigrationConfig['sourceDriver'];
|
|
11
11
|
connectionString: string;
|
|
12
|
+
sourceConnectionOrigin?: MigrationConfig['sourceConnectionOrigin'];
|
|
12
13
|
connected: boolean;
|
|
13
14
|
adapter?: DatabaseAdapter;
|
|
14
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataMigrator.d.ts","sourceRoot":"","sources":["../../src/cli/DataMigrator.ts"],"names":[],"mappings":"AACA;;;GAGG;AAWH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC,cAAc,CAAC,CAAC;IACxC,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,IAAI,GAAG,WAAW,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxE,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;
|
|
1
|
+
{"version":3,"file":"DataMigrator.d.ts","sourceRoot":"","sources":["../../src/cli/DataMigrator.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/DataMigrator.js
CHANGED
|
@@ -10,6 +10,69 @@ import { SQLiteAdapter } from '@zintrust/db-sqlite';
|
|
|
10
10
|
import { SQLServerAdapter } from '@zintrust/db-sqlserver';
|
|
11
11
|
import { SchemaBuilder } from '../schema/SchemaBuilder.js';
|
|
12
12
|
import { SchemaAnalyzer } from './SchemaAnalyzer.js';
|
|
13
|
+
const preserveEncodedMySqlPassword = (origin) => {
|
|
14
|
+
return origin === 'option' || origin === 'env';
|
|
15
|
+
};
|
|
16
|
+
const redactConnectionString = (connectionString) => {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = new URL(connectionString);
|
|
19
|
+
if (parsed.password.trim() !== '') {
|
|
20
|
+
parsed.password = '***';
|
|
21
|
+
}
|
|
22
|
+
return parsed.toString();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return connectionString;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const parseMySqlConnectionDetails = (connectionString, origin) => {
|
|
29
|
+
if (!preserveEncodedMySqlPassword(origin)) {
|
|
30
|
+
return parseConnectionDetails(connectionString, 3306, 'mysql', 'root');
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const parsed = new URL(connectionString);
|
|
34
|
+
const databaseName = decodeURIComponent(parsed.pathname.replace(/^\/+/, ''));
|
|
35
|
+
return {
|
|
36
|
+
host: parsed.hostname || 'localhost',
|
|
37
|
+
port: parsed.port ? Number.parseInt(parsed.port, 10) : 3306,
|
|
38
|
+
database: databaseName || 'mysql',
|
|
39
|
+
username: parsed.username || 'root',
|
|
40
|
+
password: parsed.password || '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw ErrorFactory.createValidationError('Invalid source connection string format', error);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const getErrorCause = (error) => {
|
|
48
|
+
if (error === null || typeof error !== 'object') {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return error.cause;
|
|
52
|
+
};
|
|
53
|
+
const getErrorMessage = (error) => {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
return error.message;
|
|
56
|
+
}
|
|
57
|
+
return String(error);
|
|
58
|
+
};
|
|
59
|
+
const getErrorChainMessages = (error) => {
|
|
60
|
+
const messages = [];
|
|
61
|
+
let current = error;
|
|
62
|
+
while (current !== undefined) {
|
|
63
|
+
const message = getErrorMessage(current);
|
|
64
|
+
if (message.trim() !== '') {
|
|
65
|
+
messages.push(message);
|
|
66
|
+
}
|
|
67
|
+
current = getErrorCause(current);
|
|
68
|
+
}
|
|
69
|
+
return [...new Set(messages)];
|
|
70
|
+
};
|
|
71
|
+
const describePasswordForLog = (password) => {
|
|
72
|
+
const hasSpecialCharacters = /[^a-zA-Z0-9]/.test(password);
|
|
73
|
+
const containsBang = password.includes('!');
|
|
74
|
+
return `len=${password.length}, special_chars=${hasSpecialCharacters}, contains_bang=${containsBang}`;
|
|
75
|
+
};
|
|
13
76
|
const normalizeNullLikeValue = (value) => {
|
|
14
77
|
if (typeof value !== 'string')
|
|
15
78
|
return value;
|
|
@@ -51,6 +114,51 @@ const parseSqliteDatabasePath = (connectionString) => {
|
|
|
51
114
|
return trimmed;
|
|
52
115
|
}
|
|
53
116
|
};
|
|
117
|
+
const createSourceAdapter = (config) => {
|
|
118
|
+
switch (config.sourceDriver) {
|
|
119
|
+
case 'mysql': {
|
|
120
|
+
const connectionDetails = parseMySqlConnectionDetails(config.sourceConnection, config.sourceConnectionOrigin);
|
|
121
|
+
Logger.info(`[DataMigrator] Source password diagnostics: ${describePasswordForLog(connectionDetails.password)}`);
|
|
122
|
+
return MySQLAdapter.create({
|
|
123
|
+
driver: 'mysql',
|
|
124
|
+
host: connectionDetails.host,
|
|
125
|
+
port: connectionDetails.port,
|
|
126
|
+
database: connectionDetails.database,
|
|
127
|
+
username: connectionDetails.username,
|
|
128
|
+
password: connectionDetails.password,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
case 'postgresql': {
|
|
132
|
+
const connectionDetails = parseConnectionDetails(config.sourceConnection, 5432, 'postgres', 'postgres');
|
|
133
|
+
return PostgreSQLAdapter.create({
|
|
134
|
+
driver: 'postgresql',
|
|
135
|
+
host: connectionDetails.host,
|
|
136
|
+
port: connectionDetails.port,
|
|
137
|
+
database: connectionDetails.database,
|
|
138
|
+
username: connectionDetails.username,
|
|
139
|
+
password: connectionDetails.password,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
case 'sqlite':
|
|
143
|
+
return SQLiteAdapter.create({
|
|
144
|
+
driver: 'sqlite',
|
|
145
|
+
database: parseSqliteDatabasePath(config.sourceConnection),
|
|
146
|
+
});
|
|
147
|
+
case 'sqlserver': {
|
|
148
|
+
const connectionDetails = parseConnectionDetails(config.sourceConnection, 1433, 'master', 'sa');
|
|
149
|
+
return SQLServerAdapter.create({
|
|
150
|
+
driver: 'sqlserver',
|
|
151
|
+
host: connectionDetails.host,
|
|
152
|
+
port: connectionDetails.port,
|
|
153
|
+
database: connectionDetails.database,
|
|
154
|
+
username: connectionDetails.username,
|
|
155
|
+
password: connectionDetails.password,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
default:
|
|
159
|
+
throw ErrorFactory.createValidationError(`Unsupported driver: ${config.sourceDriver}`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
54
162
|
const safelyDisconnect = async (label, connection) => {
|
|
55
163
|
try {
|
|
56
164
|
await connection?.adapter?.disconnect?.();
|
|
@@ -121,7 +229,9 @@ export const DataMigrator = Object.freeze({
|
|
|
121
229
|
return progress;
|
|
122
230
|
}
|
|
123
231
|
catch (error) {
|
|
124
|
-
|
|
232
|
+
const errorChain = getErrorChainMessages(error);
|
|
233
|
+
Logger.error(`Data migration failed: ${errorChain.join(' -> ')}`);
|
|
234
|
+
Logger.error('Data migration failure details:', error);
|
|
125
235
|
throw error;
|
|
126
236
|
}
|
|
127
237
|
finally {
|
|
@@ -133,52 +243,22 @@ export const DataMigrator = Object.freeze({
|
|
|
133
243
|
* Connect to source database
|
|
134
244
|
*/
|
|
135
245
|
async connectToSource(config) {
|
|
136
|
-
Logger.info(`Connecting to ${config.sourceDriver} database
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
adapter = PostgreSQLAdapter.create({
|
|
148
|
-
driver: 'postgresql',
|
|
149
|
-
host: connectionDetails.host,
|
|
150
|
-
port: connectionDetails.port,
|
|
151
|
-
database: connectionDetails.database,
|
|
152
|
-
username: connectionDetails.username,
|
|
153
|
-
password: connectionDetails.password,
|
|
154
|
-
});
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case 'sqlite':
|
|
158
|
-
adapter = SQLiteAdapter.create({
|
|
159
|
-
driver: 'sqlite',
|
|
160
|
-
database: parseSqliteDatabasePath(config.sourceConnection),
|
|
161
|
-
});
|
|
162
|
-
break;
|
|
163
|
-
case 'sqlserver': {
|
|
164
|
-
const connectionDetails = parseConnectionDetails(config.sourceConnection, 1433, 'master', 'sa');
|
|
165
|
-
adapter = SQLServerAdapter.create({
|
|
166
|
-
driver: 'sqlserver',
|
|
167
|
-
host: connectionDetails.host,
|
|
168
|
-
port: connectionDetails.port,
|
|
169
|
-
database: connectionDetails.database,
|
|
170
|
-
username: connectionDetails.username,
|
|
171
|
-
password: connectionDetails.password,
|
|
172
|
-
});
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
default:
|
|
176
|
-
throw ErrorFactory.createValidationError(`Unsupported driver: ${config.sourceDriver}`);
|
|
246
|
+
Logger.info(`Connecting to ${config.sourceDriver} database...`);
|
|
247
|
+
Logger.info(`[DataMigrator] Source connection (redacted): ${redactConnectionString(config.sourceConnection)}`);
|
|
248
|
+
const adapter = createSourceAdapter(config);
|
|
249
|
+
try {
|
|
250
|
+
await adapter.connect();
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
const errorChain = getErrorChainMessages(error);
|
|
254
|
+
Logger.error(`Source database connection failed: ${errorChain.join(' -> ')}`);
|
|
255
|
+
Logger.error('Source database connection failure details:', error);
|
|
256
|
+
throw error;
|
|
177
257
|
}
|
|
178
|
-
await adapter.connect();
|
|
179
258
|
const connection = {
|
|
180
259
|
driver: config.sourceDriver,
|
|
181
260
|
connectionString: config.sourceConnection || '',
|
|
261
|
+
sourceConnectionOrigin: config.sourceConnectionOrigin,
|
|
182
262
|
connected: true,
|
|
183
263
|
adapter,
|
|
184
264
|
};
|
|
@@ -227,6 +307,7 @@ export const DataMigrator = Object.freeze({
|
|
|
227
307
|
const sourceSchema = await SchemaAnalyzer.analyzeSchema({
|
|
228
308
|
driver: sourceConnection.driver,
|
|
229
309
|
connectionString: sourceConnection.connectionString,
|
|
310
|
+
sourceConnectionOrigin: sourceConnection.sourceConnectionOrigin,
|
|
230
311
|
});
|
|
231
312
|
const d1Schema = SchemaBuilder.buildD1Schema(sourceSchema.tables, config.sourceDriver);
|
|
232
313
|
SchemaBuilder.assertValidSchema(d1Schema);
|
|
@@ -250,6 +331,7 @@ export const DataMigrator = Object.freeze({
|
|
|
250
331
|
const sourceSchema = await SchemaAnalyzer.analyzeSchema({
|
|
251
332
|
driver: _connection.driver,
|
|
252
333
|
connectionString: _connection.connectionString,
|
|
334
|
+
sourceConnectionOrigin: _connection.sourceConnectionOrigin,
|
|
253
335
|
});
|
|
254
336
|
const tables = sourceSchema.tables.map((table) => ({
|
|
255
337
|
name: table.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrateToD1Command.d.ts","sourceRoot":"","sources":["../../src/cli/MigrateToD1Command.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"MigrateToD1Command.d.ts","sourceRoot":"","sources":["../../src/cli/MigrateToD1Command.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,UAAU,CAAC;AAOxE,KAAK,iBAAiB,GAAG;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,IAAI,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC;AAktBF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,iBA6G/B,CAAC;AAEH;;GAEG;AACH,iBAAe,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CActE;AAED;;GAEG;AACH,iBAAe,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmGxE;AAED;;GAEG;AACH,iBAAe,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGtE;AAED;;GAEG;AACH,iBAAS,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA2BrF;AAGD,eAAO,MAAM,iBAAiB;;;;;EAK5B,CAAC"}
|
|
@@ -48,6 +48,18 @@ const TARGET_DATABASE_ENV_KEYS = Object.freeze([
|
|
|
48
48
|
'D1_DATABASE',
|
|
49
49
|
'D1_DATABASE_ID',
|
|
50
50
|
]);
|
|
51
|
+
const uniq = (items) => {
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
if (seen.has(item)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
seen.add(item);
|
|
59
|
+
out.push(item);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
};
|
|
51
63
|
const describeConfiguredD1Target = (config) => {
|
|
52
64
|
const parts = [];
|
|
53
65
|
if (typeof config.database_name === 'string' && config.database_name.trim() !== '') {
|
|
@@ -157,6 +169,127 @@ const encodeConnectionSegment = (value) => {
|
|
|
157
169
|
return `%${match.charCodeAt(0).toString(16).toUpperCase()}`;
|
|
158
170
|
});
|
|
159
171
|
};
|
|
172
|
+
const decodeConnectionSegment = (value) => {
|
|
173
|
+
return value.trim() === '' ? '' : decodeURIComponent(value);
|
|
174
|
+
};
|
|
175
|
+
const getNetworkScheme = (sourceDriver) => {
|
|
176
|
+
if (sourceDriver === 'mysql')
|
|
177
|
+
return 'mysql';
|
|
178
|
+
if (sourceDriver === 'postgresql')
|
|
179
|
+
return 'postgresql';
|
|
180
|
+
if (sourceDriver === 'sqlserver')
|
|
181
|
+
return 'mssql';
|
|
182
|
+
return undefined;
|
|
183
|
+
};
|
|
184
|
+
const getDefaultNetworkPort = (sourceDriver) => {
|
|
185
|
+
if (sourceDriver === 'mysql')
|
|
186
|
+
return 3306;
|
|
187
|
+
if (sourceDriver === 'postgresql')
|
|
188
|
+
return 5432;
|
|
189
|
+
return 1433;
|
|
190
|
+
};
|
|
191
|
+
const parseNetworkConnectionDetails = (connectionString, sourceDriver) => {
|
|
192
|
+
const expectedScheme = getNetworkScheme(sourceDriver);
|
|
193
|
+
if (expectedScheme === undefined) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const parsed = new URL(connectionString);
|
|
198
|
+
const protocol = parsed.protocol.replace(/:$/, '').toLowerCase();
|
|
199
|
+
const allowedProtocols = sourceDriver === 'sqlserver' ? ['mssql', 'sqlserver'] : [expectedScheme];
|
|
200
|
+
if (!allowedProtocols.includes(protocol)) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
scheme: expectedScheme,
|
|
205
|
+
host: parsed.hostname || 'localhost',
|
|
206
|
+
port: parsed.port.trim() === ''
|
|
207
|
+
? getDefaultNetworkPort(sourceDriver)
|
|
208
|
+
: Number.parseInt(parsed.port, 10),
|
|
209
|
+
database: decodeConnectionSegment(parsed.pathname.replace(/^\/+/, '')),
|
|
210
|
+
username: decodeConnectionSegment(parsed.username),
|
|
211
|
+
password: decodeConnectionSegment(parsed.password),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const normalizeSourceConnectionString = (sourceConnection, sourceDriver, origin) => {
|
|
219
|
+
if (origin !== 'db-env') {
|
|
220
|
+
return sourceConnection;
|
|
221
|
+
}
|
|
222
|
+
const details = parseNetworkConnectionDetails(sourceConnection, sourceDriver);
|
|
223
|
+
if (details === undefined) {
|
|
224
|
+
return sourceConnection;
|
|
225
|
+
}
|
|
226
|
+
return buildNetworkConnectionString(details);
|
|
227
|
+
};
|
|
228
|
+
const redactConnectionString = (sourceConnection) => {
|
|
229
|
+
try {
|
|
230
|
+
const parsed = new URL(sourceConnection);
|
|
231
|
+
if (parsed.password.trim() !== '') {
|
|
232
|
+
parsed.password = '***';
|
|
233
|
+
}
|
|
234
|
+
return parsed.toString();
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return sourceConnection;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const getPasswordForDiagnostics = (sourceConnection, sourceDriver, origin) => {
|
|
241
|
+
if (origin === 'db-env') {
|
|
242
|
+
return parseNetworkConnectionDetails(sourceConnection, sourceDriver)?.password;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const parsed = new URL(sourceConnection);
|
|
246
|
+
return parsed.password;
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const describePasswordForLog = (password) => {
|
|
253
|
+
const hasSpecialCharacters = /[^a-zA-Z0-9]/.test(password);
|
|
254
|
+
const containsBang = password.includes('!');
|
|
255
|
+
return `len=${password.length}, special_chars=${hasSpecialCharacters}, contains_bang=${containsBang}`;
|
|
256
|
+
};
|
|
257
|
+
const getErrorCause = (error) => {
|
|
258
|
+
if (error === null || typeof error !== 'object') {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
return error.cause;
|
|
262
|
+
};
|
|
263
|
+
const getErrorMessage = (error) => {
|
|
264
|
+
if (error instanceof Error) {
|
|
265
|
+
return error.message;
|
|
266
|
+
}
|
|
267
|
+
return String(error);
|
|
268
|
+
};
|
|
269
|
+
const getErrorChainMessages = (error) => {
|
|
270
|
+
const messages = [];
|
|
271
|
+
let current = error;
|
|
272
|
+
while (current !== undefined) {
|
|
273
|
+
const message = getErrorMessage(current);
|
|
274
|
+
if (message.trim() !== '') {
|
|
275
|
+
messages.push(message);
|
|
276
|
+
}
|
|
277
|
+
current = getErrorCause(current);
|
|
278
|
+
}
|
|
279
|
+
return uniq(messages);
|
|
280
|
+
};
|
|
281
|
+
const logSourceConnectionDiagnostics = (sourceDriver, sourceConnection, origin, originalValue) => {
|
|
282
|
+
Logger.info(`[d1-migrator] Source connection origin: ${origin}`);
|
|
283
|
+
Logger.info(`[d1-migrator] Source connection driver: ${sourceDriver}`);
|
|
284
|
+
Logger.info(`[d1-migrator] Source connection (redacted): ${redactConnectionString(sourceConnection)}`);
|
|
285
|
+
const originalPassword = getPasswordForDiagnostics(originalValue, sourceDriver, origin);
|
|
286
|
+
const finalPassword = getPasswordForDiagnostics(sourceConnection, sourceDriver, origin);
|
|
287
|
+
if (originalPassword === undefined || finalPassword === undefined) {
|
|
288
|
+
Logger.info('[d1-migrator] Source connection diagnostics: non-network source or unable to parse URL');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
Logger.info(`[d1-migrator] Source password diagnostics: provided(${describePasswordForLog(originalPassword)}), final(${describePasswordForLog(finalPassword)}), matches=${originalPassword === finalPassword}`);
|
|
292
|
+
};
|
|
160
293
|
const normalizeSourceDriver = (value) => {
|
|
161
294
|
if (value === undefined) {
|
|
162
295
|
return undefined;
|
|
@@ -272,15 +405,27 @@ const buildSourceConnectionFromDbEnv = (sourceDriver) => {
|
|
|
272
405
|
const resolveSourceConnection = (options, sourceDriver) => {
|
|
273
406
|
const fromOption = readOptionString(options, ['source-connection', 'sourceConnection']);
|
|
274
407
|
if (fromOption !== undefined) {
|
|
275
|
-
return
|
|
408
|
+
return {
|
|
409
|
+
value: normalizeSourceConnectionString(fromOption, sourceDriver, 'option'),
|
|
410
|
+
origin: 'option',
|
|
411
|
+
originalValue: fromOption,
|
|
412
|
+
};
|
|
276
413
|
}
|
|
277
414
|
const fromEnv = readEnvString(SOURCE_CONNECTION_ENV_KEYS);
|
|
278
415
|
if (fromEnv !== undefined) {
|
|
279
|
-
return
|
|
416
|
+
return {
|
|
417
|
+
value: normalizeSourceConnectionString(fromEnv, sourceDriver, 'env'),
|
|
418
|
+
origin: 'env',
|
|
419
|
+
originalValue: fromEnv,
|
|
420
|
+
};
|
|
280
421
|
}
|
|
281
422
|
const fromDbEnv = buildSourceConnectionFromDbEnv(sourceDriver);
|
|
282
423
|
if (fromDbEnv !== undefined && fromDbEnv.trim().length > 0) {
|
|
283
|
-
return
|
|
424
|
+
return {
|
|
425
|
+
value: normalizeSourceConnectionString(fromDbEnv, sourceDriver, 'db-env'),
|
|
426
|
+
origin: 'db-env',
|
|
427
|
+
originalValue: fromDbEnv,
|
|
428
|
+
};
|
|
284
429
|
}
|
|
285
430
|
throw ErrorFactory.createValidationError('Source connection is required. Use --source-connection or set MIGRATE_TO_D1_SOURCE_CONNECTION (or DB_* variables)');
|
|
286
431
|
};
|
|
@@ -314,7 +459,7 @@ const resolveTargetDatabase = (options) => {
|
|
|
314
459
|
};
|
|
315
460
|
const resolveMigrationConfig = (options) => {
|
|
316
461
|
const sourceDriver = resolveSourceDriver(options);
|
|
317
|
-
const
|
|
462
|
+
const sourceConnectionResolution = resolveSourceConnection(options, sourceDriver);
|
|
318
463
|
const targetDatabase = resolveTargetDatabase(options);
|
|
319
464
|
const targetType = resolveTargetType(options);
|
|
320
465
|
const dryRun = resolveFlag(options, ['dry-run', 'dryRun'], ['MIGRATE_TO_D1_DRY_RUN', 'D1_MIGRATOR_DRY_RUN']);
|
|
@@ -328,7 +473,8 @@ const resolveMigrationConfig = (options) => {
|
|
|
328
473
|
}
|
|
329
474
|
return {
|
|
330
475
|
config: {
|
|
331
|
-
sourceConnection,
|
|
476
|
+
sourceConnection: sourceConnectionResolution.value,
|
|
477
|
+
sourceConnectionOrigin: sourceConnectionResolution.origin,
|
|
332
478
|
sourceDriver,
|
|
333
479
|
targetDatabase,
|
|
334
480
|
targetType,
|
|
@@ -339,6 +485,8 @@ const resolveMigrationConfig = (options) => {
|
|
|
339
485
|
migrationId,
|
|
340
486
|
},
|
|
341
487
|
schemaOnly,
|
|
488
|
+
sourceConnectionOrigin: sourceConnectionResolution.origin,
|
|
489
|
+
originalSourceConnection: sourceConnectionResolution.originalValue,
|
|
342
490
|
};
|
|
343
491
|
};
|
|
344
492
|
/**
|
|
@@ -366,7 +514,8 @@ export const MigrateToD1Command = BaseCommand.create({
|
|
|
366
514
|
execute: async (options) => {
|
|
367
515
|
try {
|
|
368
516
|
Logger.info('Starting D1 migration process...');
|
|
369
|
-
const { config, schemaOnly } = resolveMigrationConfig(options);
|
|
517
|
+
const { config, schemaOnly, sourceConnectionOrigin, originalSourceConnection } = resolveMigrationConfig(options);
|
|
518
|
+
logSourceConnectionDiagnostics(config.sourceDriver, config.sourceConnection, sourceConnectionOrigin, originalSourceConnection);
|
|
370
519
|
const configValidation = validateConfig(config);
|
|
371
520
|
if (!configValidation.valid) {
|
|
372
521
|
throw ErrorFactory.createValidationError(`Invalid migration configuration: ${configValidation.errors.join(', ')}`);
|
|
@@ -381,6 +530,7 @@ export const MigrateToD1Command = BaseCommand.create({
|
|
|
381
530
|
const connection = {
|
|
382
531
|
driver: config.sourceDriver,
|
|
383
532
|
connectionString: config.sourceConnection,
|
|
533
|
+
sourceConnectionOrigin: config.sourceConnectionOrigin,
|
|
384
534
|
};
|
|
385
535
|
// Analyze source schema
|
|
386
536
|
Logger.info('Analyzing source database schema...');
|
|
@@ -419,8 +569,10 @@ export const MigrateToD1Command = BaseCommand.create({
|
|
|
419
569
|
Logger.info('D1 migration completed successfully');
|
|
420
570
|
}
|
|
421
571
|
catch (error) {
|
|
422
|
-
|
|
423
|
-
|
|
572
|
+
const errorChain = getErrorChainMessages(error);
|
|
573
|
+
Logger.error(`Migration failed: ${errorChain.join(' -> ')}`);
|
|
574
|
+
Logger.error('Migration failure details:', error);
|
|
575
|
+
throw ErrorFactory.createValidationError(`Migration failed: ${errorChain[0] ?? getErrorMessage(error)}`, error);
|
|
424
576
|
}
|
|
425
577
|
},
|
|
426
578
|
});
|
|
@@ -464,6 +616,7 @@ async function runInteractiveMode(config) {
|
|
|
464
616
|
const connection = {
|
|
465
617
|
driver: config.sourceDriver,
|
|
466
618
|
connectionString: config.sourceConnection,
|
|
619
|
+
sourceConnectionOrigin: config.sourceConnectionOrigin,
|
|
467
620
|
};
|
|
468
621
|
const sourceSchema = await SchemaAnalyzer.analyzeSchema(connection);
|
|
469
622
|
Logger.info(`Found ${sourceSchema.tables.length} tables to migrate`);
|
|
@@ -540,6 +693,7 @@ async function runAutomatedMode(config) {
|
|
|
540
693
|
const connection = {
|
|
541
694
|
driver: config.sourceDriver,
|
|
542
695
|
connectionString: config.sourceConnection,
|
|
696
|
+
sourceConnectionOrigin: config.sourceConnectionOrigin,
|
|
543
697
|
};
|
|
544
698
|
const sourceSchema = await SchemaAnalyzer.analyzeSchema(connection);
|
|
545
699
|
Logger.info(`✓ Found ${sourceSchema.tables.length} tables to migrate`);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Schema Analyzer
|
|
3
3
|
* Analyzes database schemas for migration compatibility
|
|
4
4
|
*/
|
|
5
|
-
import type { ColumnSchema, DatabaseSchema, ForeignKeySchema, IndexSchema, TableConstraint, TableRelationship, TableSchema } from '../types';
|
|
5
|
+
import type { ColumnSchema, DatabaseSchema, ForeignKeySchema, IndexSchema, SourceConnectionOrigin, TableConstraint, TableRelationship, TableSchema } from '../types';
|
|
6
6
|
export interface IDatabaseAdapter {
|
|
7
7
|
connect(): Promise<void>;
|
|
8
8
|
disconnect(): Promise<void>;
|
|
@@ -30,6 +30,7 @@ export declare const SchemaAnalyzer: Readonly<{
|
|
|
30
30
|
analyzeSchema(connection: {
|
|
31
31
|
driver: string;
|
|
32
32
|
connectionString: string;
|
|
33
|
+
sourceConnectionOrigin?: SourceConnectionOrigin;
|
|
33
34
|
}): Promise<DatabaseSchema>;
|
|
34
35
|
/**
|
|
35
36
|
* Extract tables from source database
|
|
@@ -37,6 +38,7 @@ export declare const SchemaAnalyzer: Readonly<{
|
|
|
37
38
|
extractTables(connection: {
|
|
38
39
|
driver: string;
|
|
39
40
|
connectionString: string;
|
|
41
|
+
sourceConnectionOrigin?: SourceConnectionOrigin;
|
|
40
42
|
}): Promise<TableSchema[]>;
|
|
41
43
|
/**
|
|
42
44
|
* Extract relationships from source database
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SchemaAnalyzer.d.ts","sourceRoot":"","sources":["../../src/cli/SchemaAnalyzer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,WAAW,EACZ,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"SchemaAnalyzer.d.ts","sourceRoot":"","sources":["../../src/cli/SchemaAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,80 @@ import { MySQLAdapter } from '@zintrust/db-mysql';
|
|
|
7
7
|
import { PostgreSQLAdapter } from '@zintrust/db-postgres';
|
|
8
8
|
import { SQLiteAdapter } from '@zintrust/db-sqlite';
|
|
9
9
|
import { SQLServerAdapter } from '@zintrust/db-sqlserver';
|
|
10
|
+
const preserveEncodedMySqlPassword = (origin) => {
|
|
11
|
+
return origin === 'option' || origin === 'env';
|
|
12
|
+
};
|
|
13
|
+
const parseConnectionDetails = (connectionString, defaultPort, defaultDatabase, defaultUsername) => {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = new URL(connectionString);
|
|
16
|
+
const databaseName = decodeURIComponent(parsed.pathname.replace(/^\/+/, ''));
|
|
17
|
+
return {
|
|
18
|
+
host: parsed.hostname || 'localhost',
|
|
19
|
+
port: parsed.port ? Number.parseInt(parsed.port, 10) : defaultPort,
|
|
20
|
+
database: databaseName || defaultDatabase,
|
|
21
|
+
username: parsed.username ? decodeURIComponent(parsed.username) : defaultUsername,
|
|
22
|
+
password: parsed.password ? decodeURIComponent(parsed.password) : '',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw ErrorFactory.createValidationError('Invalid source connection string format', error);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const parseMySqlConnectionDetails = (connectionString, origin) => {
|
|
30
|
+
if (!preserveEncodedMySqlPassword(origin)) {
|
|
31
|
+
return parseConnectionDetails(connectionString, 3306, 'mysql', 'root');
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(connectionString);
|
|
35
|
+
const databaseName = decodeURIComponent(parsed.pathname.replace(/^\/+/, ''));
|
|
36
|
+
return {
|
|
37
|
+
host: parsed.hostname || 'localhost',
|
|
38
|
+
port: parsed.port ? Number.parseInt(parsed.port, 10) : 3306,
|
|
39
|
+
database: databaseName || 'mysql',
|
|
40
|
+
username: parsed.username || 'root',
|
|
41
|
+
password: parsed.password || '',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw ErrorFactory.createValidationError('Invalid source connection string format', error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const redactConnectionString = (connectionString) => {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = new URL(connectionString);
|
|
51
|
+
if (parsed.password.trim() !== '') {
|
|
52
|
+
parsed.password = '***';
|
|
53
|
+
}
|
|
54
|
+
return parsed.toString();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return connectionString;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const getErrorCause = (error) => {
|
|
61
|
+
if (error === null || typeof error !== 'object') {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return error.cause;
|
|
65
|
+
};
|
|
66
|
+
const getErrorMessage = (error) => {
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
return error.message;
|
|
69
|
+
}
|
|
70
|
+
return String(error);
|
|
71
|
+
};
|
|
72
|
+
const getErrorChainMessages = (error) => {
|
|
73
|
+
const messages = [];
|
|
74
|
+
let current = error;
|
|
75
|
+
while (current !== undefined) {
|
|
76
|
+
const message = getErrorMessage(current);
|
|
77
|
+
if (message.trim() !== '') {
|
|
78
|
+
messages.push(message);
|
|
79
|
+
}
|
|
80
|
+
current = getErrorCause(current);
|
|
81
|
+
}
|
|
82
|
+
return [...new Set(messages)];
|
|
83
|
+
};
|
|
10
84
|
/**
|
|
11
85
|
* SchemaAnalyzer - Sealed namespace for schema analysis
|
|
12
86
|
* Provides database schema analysis and compatibility checking
|
|
@@ -40,16 +114,23 @@ export const SchemaAnalyzer = Object.freeze({
|
|
|
40
114
|
*/
|
|
41
115
|
async extractTables(connection) {
|
|
42
116
|
Logger.info(`Extracting tables from ${connection.driver} database...`);
|
|
117
|
+
Logger.info(`[SchemaAnalyzer] Source connection (redacted): ${redactConnectionString(connection.connectionString)}`);
|
|
43
118
|
try {
|
|
44
119
|
// Create appropriate adapter based on driver
|
|
45
120
|
let adapter;
|
|
46
121
|
switch (connection.driver) {
|
|
47
|
-
case 'mysql':
|
|
122
|
+
case 'mysql': {
|
|
123
|
+
const connectionDetails = parseMySqlConnectionDetails(connection.connectionString, connection.sourceConnectionOrigin);
|
|
48
124
|
adapter = MySQLAdapter.create({
|
|
49
125
|
driver: connection.driver,
|
|
50
|
-
|
|
126
|
+
host: connectionDetails.host,
|
|
127
|
+
port: connectionDetails.port,
|
|
128
|
+
database: connectionDetails.database,
|
|
129
|
+
username: connectionDetails.username,
|
|
130
|
+
password: connectionDetails.password,
|
|
51
131
|
});
|
|
52
132
|
break;
|
|
133
|
+
}
|
|
53
134
|
case 'postgresql':
|
|
54
135
|
adapter = PostgreSQLAdapter.create({
|
|
55
136
|
driver: connection.driver,
|
|
@@ -79,8 +160,10 @@ export const SchemaAnalyzer = Object.freeze({
|
|
|
79
160
|
return tableSchemas;
|
|
80
161
|
}
|
|
81
162
|
catch (error) {
|
|
82
|
-
|
|
83
|
-
|
|
163
|
+
const errorChain = getErrorChainMessages(error);
|
|
164
|
+
Logger.error(`Failed to extract database tables: ${errorChain.join(' -> ')}`);
|
|
165
|
+
Logger.error('Schema extraction failure details:', error);
|
|
166
|
+
throw ErrorFactory.createTryCatchError(`Schema extraction failed: ${errorChain[0] ?? getErrorMessage(error)}`, error);
|
|
84
167
|
}
|
|
85
168
|
},
|
|
86
169
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Command } from 'commander';
|
|
6
6
|
export type SourceDatabaseDriver = 'mysql' | 'postgresql' | 'sqlite' | 'sqlserver';
|
|
7
|
+
export type SourceConnectionOrigin = 'option' | 'env' | 'db-env';
|
|
7
8
|
export interface MigrationConfig {
|
|
8
9
|
sourceConnection: string;
|
|
10
|
+
sourceConnectionOrigin?: SourceConnectionOrigin;
|
|
9
11
|
sourceDriver: SourceDatabaseDriver;
|
|
10
12
|
targetDatabase: string;
|
|
11
13
|
targetType: 'd1' | 'd1-remote';
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnF,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,oBAAoB,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IAClE,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAC/C,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAChD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,yBAAyB,CAAC;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,eAAe,CAAC;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnF,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,YAAY,EAAE,oBAAoB,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IAClE,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAC/C,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAChD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,yBAAyB,CAAC;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,eAAe,CAAC;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC"}
|