pocketbase-zod-schema 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/cli/index.cjs +406 -102
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +404 -100
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/migrate.cjs +409 -105
- package/dist/cli/migrate.cjs.map +1 -1
- package/dist/cli/migrate.js +404 -100
- package/dist/cli/migrate.js.map +1 -1
- package/dist/index.cjs +515 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +511 -158
- package/dist/index.js.map +1 -1
- package/dist/migration/diff.cjs +21 -3
- package/dist/migration/diff.cjs.map +1 -1
- package/dist/migration/diff.js +21 -3
- package/dist/migration/diff.js.map +1 -1
- package/dist/migration/index.cjs +457 -123
- package/dist/migration/index.cjs.map +1 -1
- package/dist/migration/index.d.cts +1 -1
- package/dist/migration/index.d.ts +1 -1
- package/dist/migration/index.js +456 -123
- package/dist/migration/index.js.map +1 -1
- package/dist/migration/snapshot.cjs +432 -118
- package/dist/migration/snapshot.cjs.map +1 -1
- package/dist/migration/snapshot.d.cts +34 -12
- package/dist/migration/snapshot.d.ts +34 -12
- package/dist/migration/snapshot.js +430 -117
- package/dist/migration/snapshot.js.map +1 -1
- package/dist/mutator.d.cts +3 -3
- package/dist/mutator.d.ts +3 -3
- package/dist/schema.cjs +34 -0
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +33 -1
- package/dist/schema.js.map +1 -1
- package/dist/types.d.cts +5 -2
- package/dist/types.d.ts +5 -2
- package/dist/user-_AM523hb.d.cts +123 -0
- package/dist/user-_AM523hb.d.ts +123 -0
- package/package.json +2 -3
- package/dist/user-C39DQ40N.d.cts +0 -53
- package/dist/user-C39DQ40N.d.ts +0 -53
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
1
|
+
import * as path5 from 'path';
|
|
2
|
+
import * as fs5 from 'fs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import ora from 'ora';
|
|
@@ -132,10 +132,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
|
|
|
132
132
|
operation;
|
|
133
133
|
code;
|
|
134
134
|
originalError;
|
|
135
|
-
constructor(message,
|
|
135
|
+
constructor(message, path7, operation, code, originalError) {
|
|
136
136
|
super(message);
|
|
137
137
|
this.name = "FileSystemError";
|
|
138
|
-
this.path =
|
|
138
|
+
this.path = path7;
|
|
139
139
|
this.operation = operation;
|
|
140
140
|
this.code = code;
|
|
141
141
|
this.originalError = originalError;
|
|
@@ -1076,20 +1076,20 @@ function mergeConfig(config) {
|
|
|
1076
1076
|
}
|
|
1077
1077
|
function resolveSchemaDir(config) {
|
|
1078
1078
|
const workspaceRoot = config.workspaceRoot || process.cwd();
|
|
1079
|
-
if (
|
|
1079
|
+
if (path5.isAbsolute(config.schemaDir)) {
|
|
1080
1080
|
return config.schemaDir;
|
|
1081
1081
|
}
|
|
1082
|
-
return
|
|
1082
|
+
return path5.join(workspaceRoot, config.schemaDir);
|
|
1083
1083
|
}
|
|
1084
1084
|
function discoverSchemaFiles(config) {
|
|
1085
1085
|
const normalizedConfig = typeof config === "string" ? { schemaDir: config } : config;
|
|
1086
1086
|
const mergedConfig = mergeConfig(normalizedConfig);
|
|
1087
1087
|
const schemaDir = resolveSchemaDir(normalizedConfig);
|
|
1088
1088
|
try {
|
|
1089
|
-
if (!
|
|
1089
|
+
if (!fs5.existsSync(schemaDir)) {
|
|
1090
1090
|
throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
|
|
1091
1091
|
}
|
|
1092
|
-
const files =
|
|
1092
|
+
const files = fs5.readdirSync(schemaDir);
|
|
1093
1093
|
const schemaFiles = files.filter((file) => {
|
|
1094
1094
|
const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
|
|
1095
1095
|
if (!hasValidExtension) return false;
|
|
@@ -1105,7 +1105,7 @@ function discoverSchemaFiles(config) {
|
|
|
1105
1105
|
});
|
|
1106
1106
|
return schemaFiles.map((file) => {
|
|
1107
1107
|
const ext = mergedConfig.includeExtensions.find((ext2) => file.endsWith(ext2)) || ".ts";
|
|
1108
|
-
return
|
|
1108
|
+
return path5.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
|
|
1109
1109
|
});
|
|
1110
1110
|
} catch (error) {
|
|
1111
1111
|
if (error instanceof FileSystemError) {
|
|
@@ -1139,19 +1139,19 @@ async function importSchemaModule(filePath, config) {
|
|
|
1139
1139
|
let resolvedPath = null;
|
|
1140
1140
|
const jsPath = `${importPath}.js`;
|
|
1141
1141
|
const tsPath = `${importPath}.ts`;
|
|
1142
|
-
if (
|
|
1142
|
+
if (fs5.existsSync(jsPath)) {
|
|
1143
1143
|
resolvedPath = jsPath;
|
|
1144
|
-
} else if (
|
|
1144
|
+
} else if (fs5.existsSync(tsPath)) {
|
|
1145
1145
|
resolvedPath = tsPath;
|
|
1146
1146
|
} else {
|
|
1147
1147
|
resolvedPath = jsPath;
|
|
1148
1148
|
}
|
|
1149
|
-
const fileUrl = new URL(`file://${
|
|
1149
|
+
const fileUrl = new URL(`file://${path5.resolve(resolvedPath)}`);
|
|
1150
1150
|
const module = await import(fileUrl.href);
|
|
1151
1151
|
return module;
|
|
1152
1152
|
} catch (error) {
|
|
1153
1153
|
const tsPath = `${filePath}.ts`;
|
|
1154
|
-
const isTypeScriptFile =
|
|
1154
|
+
const isTypeScriptFile = fs5.existsSync(tsPath);
|
|
1155
1155
|
if (isTypeScriptFile) {
|
|
1156
1156
|
throw new SchemaParsingError(
|
|
1157
1157
|
`Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
|
|
@@ -1170,7 +1170,7 @@ Please either:
|
|
|
1170
1170
|
}
|
|
1171
1171
|
}
|
|
1172
1172
|
function getCollectionNameFromFile(filePath) {
|
|
1173
|
-
const filename =
|
|
1173
|
+
const filename = path5.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1174
1174
|
return toCollectionName(filename);
|
|
1175
1175
|
}
|
|
1176
1176
|
function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
|
|
@@ -1335,7 +1335,7 @@ async function buildSchemaDefinition(config) {
|
|
|
1335
1335
|
importPath = normalizedConfig.pathTransformer(filePath);
|
|
1336
1336
|
} else if (mergedConfig.useCompiledFiles) {
|
|
1337
1337
|
const distPath = filePath.replace(/\/src\//, "/dist/");
|
|
1338
|
-
if (
|
|
1338
|
+
if (fs5.existsSync(`${distPath}.js`) || fs5.existsSync(`${distPath}.mjs`)) {
|
|
1339
1339
|
importPath = distPath;
|
|
1340
1340
|
} else {
|
|
1341
1341
|
importPath = filePath;
|
|
@@ -1512,6 +1512,9 @@ function compareFieldOptions(currentField, previousField) {
|
|
|
1512
1512
|
for (const key of allKeys) {
|
|
1513
1513
|
const currentValue = currentOptions[key];
|
|
1514
1514
|
const previousValue = previousOptions[key];
|
|
1515
|
+
if (currentValue === void 0 && previousValue === void 0) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1515
1518
|
if (!areValuesEqual(currentValue, previousValue)) {
|
|
1516
1519
|
changes.push({
|
|
1517
1520
|
property: `options.${key}`,
|
|
@@ -1532,11 +1535,26 @@ function compareRelationConfigurations(currentField, previousField) {
|
|
|
1532
1535
|
if (!currentRelation || !previousRelation) {
|
|
1533
1536
|
return changes;
|
|
1534
1537
|
}
|
|
1535
|
-
|
|
1538
|
+
const normalizeCollection = (collection) => {
|
|
1539
|
+
if (!collection) return collection;
|
|
1540
|
+
if (collection === "_pb_users_auth_") {
|
|
1541
|
+
return "Users";
|
|
1542
|
+
}
|
|
1543
|
+
const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
|
|
1544
|
+
if (nameMatch) {
|
|
1545
|
+
return nameMatch[1];
|
|
1546
|
+
}
|
|
1547
|
+
return collection;
|
|
1548
|
+
};
|
|
1549
|
+
const normalizedCurrent = normalizeCollection(currentRelation.collection);
|
|
1550
|
+
const normalizedPrevious = normalizeCollection(previousRelation.collection);
|
|
1551
|
+
if (normalizedCurrent !== normalizedPrevious) {
|
|
1536
1552
|
changes.push({
|
|
1537
1553
|
property: "relation.collection",
|
|
1538
|
-
oldValue:
|
|
1539
|
-
|
|
1554
|
+
oldValue: normalizedPrevious,
|
|
1555
|
+
// Use normalized value for clarity
|
|
1556
|
+
newValue: normalizedCurrent
|
|
1557
|
+
// Use normalized value for clarity
|
|
1540
1558
|
});
|
|
1541
1559
|
}
|
|
1542
1560
|
if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
|
|
@@ -1756,10 +1774,10 @@ function mergeConfig3(config) {
|
|
|
1756
1774
|
}
|
|
1757
1775
|
function resolveMigrationDir(config) {
|
|
1758
1776
|
const workspaceRoot = config.workspaceRoot || process.cwd();
|
|
1759
|
-
if (
|
|
1777
|
+
if (path5.isAbsolute(config.migrationDir)) {
|
|
1760
1778
|
return config.migrationDir;
|
|
1761
1779
|
}
|
|
1762
|
-
return
|
|
1780
|
+
return path5.join(workspaceRoot, config.migrationDir);
|
|
1763
1781
|
}
|
|
1764
1782
|
function generateTimestamp(config) {
|
|
1765
1783
|
if (config?.timestampGenerator) {
|
|
@@ -1817,9 +1835,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
|
|
|
1817
1835
|
}
|
|
1818
1836
|
function writeMigrationFile(migrationDir, filename, content) {
|
|
1819
1837
|
try {
|
|
1820
|
-
if (!
|
|
1838
|
+
if (!fs5.existsSync(migrationDir)) {
|
|
1821
1839
|
try {
|
|
1822
|
-
|
|
1840
|
+
fs5.mkdirSync(migrationDir, { recursive: true });
|
|
1823
1841
|
} catch (error) {
|
|
1824
1842
|
const fsError = error;
|
|
1825
1843
|
if (fsError.code === "EACCES" || fsError.code === "EPERM") {
|
|
@@ -1840,15 +1858,15 @@ function writeMigrationFile(migrationDir, filename, content) {
|
|
|
1840
1858
|
);
|
|
1841
1859
|
}
|
|
1842
1860
|
}
|
|
1843
|
-
const filePath =
|
|
1844
|
-
|
|
1861
|
+
const filePath = path5.join(migrationDir, filename);
|
|
1862
|
+
fs5.writeFileSync(filePath, content, "utf-8");
|
|
1845
1863
|
return filePath;
|
|
1846
1864
|
} catch (error) {
|
|
1847
1865
|
if (error instanceof FileSystemError) {
|
|
1848
1866
|
throw error;
|
|
1849
1867
|
}
|
|
1850
1868
|
const fsError = error;
|
|
1851
|
-
const filePath =
|
|
1869
|
+
const filePath = path5.join(migrationDir, filename);
|
|
1852
1870
|
if (fsError.code === "EACCES" || fsError.code === "EPERM") {
|
|
1853
1871
|
throw new FileSystemError(
|
|
1854
1872
|
`Permission denied writing migration file. Check file and directory permissions.`,
|
|
@@ -2393,57 +2411,18 @@ function generate(diff, config) {
|
|
|
2393
2411
|
);
|
|
2394
2412
|
}
|
|
2395
2413
|
}
|
|
2414
|
+
|
|
2415
|
+
// src/migration/pocketbase-converter.ts
|
|
2396
2416
|
var SNAPSHOT_VERSION = "1.0.0";
|
|
2397
|
-
({
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
try {
|
|
2401
|
-
if (!fs4.existsSync(migrationsPath)) {
|
|
2402
|
-
return null;
|
|
2403
|
-
}
|
|
2404
|
-
const files = fs4.readdirSync(migrationsPath);
|
|
2405
|
-
const snapshotFiles = files.filter(
|
|
2406
|
-
(file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
|
|
2407
|
-
);
|
|
2408
|
-
if (snapshotFiles.length === 0) {
|
|
2409
|
-
return null;
|
|
2410
|
-
}
|
|
2411
|
-
snapshotFiles.sort().reverse();
|
|
2412
|
-
const latestSnapshot = snapshotFiles[0];
|
|
2413
|
-
if (!latestSnapshot) {
|
|
2414
|
-
return null;
|
|
2415
|
-
}
|
|
2416
|
-
return path4.join(migrationsPath, latestSnapshot);
|
|
2417
|
-
} catch (error) {
|
|
2418
|
-
console.warn(`Error finding latest snapshot: ${error}`);
|
|
2419
|
-
return null;
|
|
2417
|
+
function resolveCollectionIdToName(collectionId) {
|
|
2418
|
+
if (collectionId === "_pb_users_auth_") {
|
|
2419
|
+
return "Users";
|
|
2420
2420
|
}
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
if (!migrationsPath) {
|
|
2425
|
-
return null;
|
|
2421
|
+
const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
|
|
2422
|
+
if (nameMatch) {
|
|
2423
|
+
return nameMatch[1];
|
|
2426
2424
|
}
|
|
2427
|
-
|
|
2428
|
-
try {
|
|
2429
|
-
const migrationContent = fs4.readFileSync(migrationsPath, "utf-8");
|
|
2430
|
-
return convertPocketBaseMigration(migrationContent);
|
|
2431
|
-
} catch (error) {
|
|
2432
|
-
console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
|
|
2433
|
-
return null;
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
const latestSnapshotPath = findLatestSnapshot(migrationsPath);
|
|
2437
|
-
if (latestSnapshotPath) {
|
|
2438
|
-
try {
|
|
2439
|
-
const migrationContent = fs4.readFileSync(latestSnapshotPath, "utf-8");
|
|
2440
|
-
return convertPocketBaseMigration(migrationContent);
|
|
2441
|
-
} catch (error) {
|
|
2442
|
-
console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
|
|
2443
|
-
return null;
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
return null;
|
|
2425
|
+
return collectionId;
|
|
2447
2426
|
}
|
|
2448
2427
|
function convertPocketBaseCollection(pbCollection) {
|
|
2449
2428
|
const fields = [];
|
|
@@ -2462,17 +2441,28 @@ function convertPocketBaseCollection(pbCollection) {
|
|
|
2462
2441
|
type: pbField.type,
|
|
2463
2442
|
required: pbField.required || false
|
|
2464
2443
|
};
|
|
2465
|
-
|
|
2466
|
-
|
|
2444
|
+
field.options = pbField.options ? { ...pbField.options } : {};
|
|
2445
|
+
if (pbField.type === "select") {
|
|
2446
|
+
if (pbField.values && Array.isArray(pbField.values)) {
|
|
2447
|
+
field.options.values = pbField.values;
|
|
2448
|
+
} else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
|
|
2449
|
+
field.options.values = pbField.options.values;
|
|
2450
|
+
}
|
|
2467
2451
|
}
|
|
2468
2452
|
if (pbField.type === "relation") {
|
|
2453
|
+
const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
|
|
2454
|
+
const collectionName = resolveCollectionIdToName(collectionId);
|
|
2469
2455
|
field.relation = {
|
|
2470
|
-
collection:
|
|
2471
|
-
cascadeDelete: pbField.options?.cascadeDelete
|
|
2472
|
-
maxSelect: pbField.options?.maxSelect,
|
|
2473
|
-
minSelect: pbField.options?.minSelect
|
|
2456
|
+
collection: collectionName,
|
|
2457
|
+
cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
|
|
2458
|
+
maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
|
|
2459
|
+
minSelect: pbField.minSelect ?? pbField.options?.minSelect
|
|
2474
2460
|
};
|
|
2475
2461
|
}
|
|
2462
|
+
const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
|
|
2463
|
+
if (Object.keys(field.options).length === 0) {
|
|
2464
|
+
delete field.options;
|
|
2465
|
+
} else if (pbField.type === "select" && hasOnlyValues) ;
|
|
2476
2466
|
fields.push(field);
|
|
2477
2467
|
}
|
|
2478
2468
|
}
|
|
@@ -2537,6 +2527,320 @@ function convertPocketBaseMigration(migrationContent) {
|
|
|
2537
2527
|
}
|
|
2538
2528
|
}
|
|
2539
2529
|
|
|
2530
|
+
// src/migration/migration-parser.ts
|
|
2531
|
+
function extractTimestampFromFilename(filename) {
|
|
2532
|
+
const match = filename.match(/^(\d+)_/);
|
|
2533
|
+
if (match) {
|
|
2534
|
+
return parseInt(match[1], 10);
|
|
2535
|
+
}
|
|
2536
|
+
return null;
|
|
2537
|
+
}
|
|
2538
|
+
function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
|
|
2539
|
+
try {
|
|
2540
|
+
if (!fs5.existsSync(migrationsPath)) {
|
|
2541
|
+
return [];
|
|
2542
|
+
}
|
|
2543
|
+
const files = fs5.readdirSync(migrationsPath);
|
|
2544
|
+
const migrationFiles = [];
|
|
2545
|
+
for (const file of files) {
|
|
2546
|
+
if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
if (!file.endsWith(".js")) {
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
2552
|
+
const timestamp = extractTimestampFromFilename(file);
|
|
2553
|
+
if (timestamp && timestamp > snapshotTimestamp) {
|
|
2554
|
+
migrationFiles.push({
|
|
2555
|
+
path: path5.join(migrationsPath, file),
|
|
2556
|
+
timestamp
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
|
|
2561
|
+
return migrationFiles.map((f) => f.path);
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
console.warn(`Error finding migrations after snapshot: ${error}`);
|
|
2564
|
+
return [];
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
function parseMigrationOperationsFromContent(content) {
|
|
2568
|
+
const collectionsToCreate = [];
|
|
2569
|
+
const collectionsToDelete = [];
|
|
2570
|
+
try {
|
|
2571
|
+
let searchIndex = 0;
|
|
2572
|
+
while (true) {
|
|
2573
|
+
const collectionStart = content.indexOf("new Collection(", searchIndex);
|
|
2574
|
+
if (collectionStart === -1) {
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
const openParen = collectionStart + "new Collection(".length;
|
|
2578
|
+
let braceCount = 0;
|
|
2579
|
+
let parenCount = 1;
|
|
2580
|
+
let inString = false;
|
|
2581
|
+
let stringChar = null;
|
|
2582
|
+
let i = openParen;
|
|
2583
|
+
while (i < content.length && /\s/.test(content[i])) {
|
|
2584
|
+
i++;
|
|
2585
|
+
}
|
|
2586
|
+
if (content[i] !== "{") {
|
|
2587
|
+
searchIndex = i + 1;
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
const objectStart = i;
|
|
2591
|
+
braceCount = 1;
|
|
2592
|
+
i++;
|
|
2593
|
+
while (i < content.length && (braceCount > 0 || parenCount > 0)) {
|
|
2594
|
+
const char = content[i];
|
|
2595
|
+
const prevChar = i > 0 ? content[i - 1] : "";
|
|
2596
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
2597
|
+
inString = true;
|
|
2598
|
+
stringChar = char;
|
|
2599
|
+
} else if (inString && char === stringChar && prevChar !== "\\") {
|
|
2600
|
+
inString = false;
|
|
2601
|
+
stringChar = null;
|
|
2602
|
+
}
|
|
2603
|
+
if (!inString) {
|
|
2604
|
+
if (char === "{") braceCount++;
|
|
2605
|
+
if (char === "}") braceCount--;
|
|
2606
|
+
if (char === "(") parenCount++;
|
|
2607
|
+
if (char === ")") parenCount--;
|
|
2608
|
+
}
|
|
2609
|
+
i++;
|
|
2610
|
+
}
|
|
2611
|
+
if (braceCount === 0 && parenCount === 0) {
|
|
2612
|
+
const objectContent = content.substring(objectStart, i - 1);
|
|
2613
|
+
try {
|
|
2614
|
+
const collectionObj = new Function(`return ${objectContent}`)();
|
|
2615
|
+
if (collectionObj && collectionObj.name) {
|
|
2616
|
+
const schema = convertPocketBaseCollection(collectionObj);
|
|
2617
|
+
collectionsToCreate.push(schema);
|
|
2618
|
+
}
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
console.warn(`Failed to parse collection definition: ${error}`);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
searchIndex = i;
|
|
2624
|
+
}
|
|
2625
|
+
const deleteMatches = content.matchAll(
|
|
2626
|
+
/app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
|
|
2627
|
+
);
|
|
2628
|
+
for (const match of deleteMatches) {
|
|
2629
|
+
if (match[1]) {
|
|
2630
|
+
collectionsToDelete.push(match[1]);
|
|
2631
|
+
} else {
|
|
2632
|
+
const varNameMatch = match[0].match(/collection_(\w+)/);
|
|
2633
|
+
if (varNameMatch) {
|
|
2634
|
+
const varName = `collection_${varNameMatch[1]}`;
|
|
2635
|
+
const deleteIndex = content.indexOf(match[0]);
|
|
2636
|
+
const beforeDelete = content.substring(0, deleteIndex);
|
|
2637
|
+
const varDefMatch = beforeDelete.match(
|
|
2638
|
+
new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
|
|
2639
|
+
);
|
|
2640
|
+
if (varDefMatch && varDefMatch.length > 0) {
|
|
2641
|
+
const collectionDefMatch = beforeDelete.match(
|
|
2642
|
+
new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
|
|
2643
|
+
);
|
|
2644
|
+
if (collectionDefMatch) {
|
|
2645
|
+
try {
|
|
2646
|
+
const collectionDefStr = collectionDefMatch[1];
|
|
2647
|
+
const collectionObj = new Function(`return ${collectionDefStr}`)();
|
|
2648
|
+
if (collectionObj && collectionObj.name) {
|
|
2649
|
+
collectionsToDelete.push(collectionObj.name);
|
|
2650
|
+
}
|
|
2651
|
+
} catch {
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
const findAndDeleteMatches = content.matchAll(
|
|
2659
|
+
/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
|
|
2660
|
+
);
|
|
2661
|
+
for (const match of findAndDeleteMatches) {
|
|
2662
|
+
collectionsToDelete.push(match[1]);
|
|
2663
|
+
}
|
|
2664
|
+
} catch (error) {
|
|
2665
|
+
console.warn(`Failed to parse migration operations from content: ${error}`);
|
|
2666
|
+
}
|
|
2667
|
+
return { collectionsToCreate, collectionsToDelete };
|
|
2668
|
+
}
|
|
2669
|
+
function parseMigrationOperations(migrationContent) {
|
|
2670
|
+
try {
|
|
2671
|
+
const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
|
|
2672
|
+
if (!migrateMatch) {
|
|
2673
|
+
return parseMigrationOperationsFromContent(migrationContent);
|
|
2674
|
+
}
|
|
2675
|
+
const startIndex = migrateMatch.index + migrateMatch[0].length;
|
|
2676
|
+
let i = startIndex;
|
|
2677
|
+
let parenCount = 0;
|
|
2678
|
+
let foundFirstParen = false;
|
|
2679
|
+
while (i < migrationContent.length) {
|
|
2680
|
+
const char = migrationContent[i];
|
|
2681
|
+
if (char === "(") {
|
|
2682
|
+
parenCount++;
|
|
2683
|
+
foundFirstParen = true;
|
|
2684
|
+
i++;
|
|
2685
|
+
break;
|
|
2686
|
+
}
|
|
2687
|
+
i++;
|
|
2688
|
+
}
|
|
2689
|
+
if (!foundFirstParen) {
|
|
2690
|
+
return parseMigrationOperationsFromContent(migrationContent);
|
|
2691
|
+
}
|
|
2692
|
+
let inString = false;
|
|
2693
|
+
let stringChar = null;
|
|
2694
|
+
let foundBrace = false;
|
|
2695
|
+
let braceStart = -1;
|
|
2696
|
+
while (i < migrationContent.length && !foundBrace) {
|
|
2697
|
+
const char = migrationContent[i];
|
|
2698
|
+
const prevChar = i > 0 ? migrationContent[i - 1] : "";
|
|
2699
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
2700
|
+
inString = true;
|
|
2701
|
+
stringChar = char;
|
|
2702
|
+
} else if (inString && char === stringChar && prevChar !== "\\") {
|
|
2703
|
+
inString = false;
|
|
2704
|
+
stringChar = null;
|
|
2705
|
+
}
|
|
2706
|
+
if (!inString) {
|
|
2707
|
+
if (char === "(") parenCount++;
|
|
2708
|
+
if (char === ")") {
|
|
2709
|
+
parenCount--;
|
|
2710
|
+
if (parenCount === 0) {
|
|
2711
|
+
i++;
|
|
2712
|
+
while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
|
|
2713
|
+
i++;
|
|
2714
|
+
}
|
|
2715
|
+
if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
|
|
2716
|
+
i += 2;
|
|
2717
|
+
while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
|
|
2718
|
+
i++;
|
|
2719
|
+
}
|
|
2720
|
+
if (i < migrationContent.length && migrationContent[i] === "{") {
|
|
2721
|
+
foundBrace = true;
|
|
2722
|
+
braceStart = i + 1;
|
|
2723
|
+
break;
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
i++;
|
|
2730
|
+
}
|
|
2731
|
+
if (!foundBrace || braceStart === -1) {
|
|
2732
|
+
return parseMigrationOperationsFromContent(migrationContent);
|
|
2733
|
+
}
|
|
2734
|
+
let braceCount = 1;
|
|
2735
|
+
i = braceStart;
|
|
2736
|
+
inString = false;
|
|
2737
|
+
stringChar = null;
|
|
2738
|
+
while (i < migrationContent.length && braceCount > 0) {
|
|
2739
|
+
const char = migrationContent[i];
|
|
2740
|
+
const prevChar = i > 0 ? migrationContent[i - 1] : "";
|
|
2741
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
2742
|
+
inString = true;
|
|
2743
|
+
stringChar = char;
|
|
2744
|
+
} else if (inString && char === stringChar && prevChar !== "\\") {
|
|
2745
|
+
inString = false;
|
|
2746
|
+
stringChar = null;
|
|
2747
|
+
}
|
|
2748
|
+
if (!inString) {
|
|
2749
|
+
if (char === "{") braceCount++;
|
|
2750
|
+
if (char === "}") braceCount--;
|
|
2751
|
+
}
|
|
2752
|
+
i++;
|
|
2753
|
+
}
|
|
2754
|
+
if (braceCount === 0) {
|
|
2755
|
+
const upMigrationContent = migrationContent.substring(braceStart, i - 1);
|
|
2756
|
+
return parseMigrationOperationsFromContent(upMigrationContent);
|
|
2757
|
+
}
|
|
2758
|
+
return parseMigrationOperationsFromContent(migrationContent);
|
|
2759
|
+
} catch (error) {
|
|
2760
|
+
console.warn(`Failed to parse migration operations: ${error}`);
|
|
2761
|
+
return { collectionsToCreate: [], collectionsToDelete: [] };
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
({
|
|
2765
|
+
workspaceRoot: process.cwd()});
|
|
2766
|
+
function findLatestSnapshot(migrationsPath) {
|
|
2767
|
+
try {
|
|
2768
|
+
if (!fs5.existsSync(migrationsPath)) {
|
|
2769
|
+
return null;
|
|
2770
|
+
}
|
|
2771
|
+
const files = fs5.readdirSync(migrationsPath);
|
|
2772
|
+
const snapshotFiles = files.filter(
|
|
2773
|
+
(file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
|
|
2774
|
+
);
|
|
2775
|
+
if (snapshotFiles.length === 0) {
|
|
2776
|
+
return null;
|
|
2777
|
+
}
|
|
2778
|
+
snapshotFiles.sort().reverse();
|
|
2779
|
+
const latestSnapshot = snapshotFiles[0];
|
|
2780
|
+
if (!latestSnapshot) {
|
|
2781
|
+
return null;
|
|
2782
|
+
}
|
|
2783
|
+
return path5.join(migrationsPath, latestSnapshot);
|
|
2784
|
+
} catch (error) {
|
|
2785
|
+
console.warn(`Error finding latest snapshot: ${error}`);
|
|
2786
|
+
return null;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
function applyMigrationOperations(snapshot, operations) {
|
|
2790
|
+
const updatedCollections = new Map(snapshot.collections);
|
|
2791
|
+
for (const collectionName of operations.collectionsToDelete) {
|
|
2792
|
+
updatedCollections.delete(collectionName);
|
|
2793
|
+
}
|
|
2794
|
+
for (const collection of operations.collectionsToCreate) {
|
|
2795
|
+
updatedCollections.set(collection.name, collection);
|
|
2796
|
+
}
|
|
2797
|
+
return {
|
|
2798
|
+
...snapshot,
|
|
2799
|
+
collections: updatedCollections
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
function loadSnapshotWithMigrations(config = {}) {
|
|
2803
|
+
const migrationsPath = config.migrationsPath;
|
|
2804
|
+
if (!migrationsPath) {
|
|
2805
|
+
return null;
|
|
2806
|
+
}
|
|
2807
|
+
if (fs5.existsSync(migrationsPath) && fs5.statSync(migrationsPath).isFile()) {
|
|
2808
|
+
try {
|
|
2809
|
+
const migrationContent = fs5.readFileSync(migrationsPath, "utf-8");
|
|
2810
|
+
return convertPocketBaseMigration(migrationContent);
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
|
|
2813
|
+
return null;
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
const latestSnapshotPath = findLatestSnapshot(migrationsPath);
|
|
2817
|
+
if (!latestSnapshotPath) {
|
|
2818
|
+
return null;
|
|
2819
|
+
}
|
|
2820
|
+
try {
|
|
2821
|
+
const migrationContent = fs5.readFileSync(latestSnapshotPath, "utf-8");
|
|
2822
|
+
let snapshot = convertPocketBaseMigration(migrationContent);
|
|
2823
|
+
const snapshotFilename = path5.basename(latestSnapshotPath);
|
|
2824
|
+
const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
|
|
2825
|
+
if (snapshotTimestamp) {
|
|
2826
|
+
const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
|
|
2827
|
+
for (const migrationFile of migrationFiles) {
|
|
2828
|
+
try {
|
|
2829
|
+
const migrationContent2 = fs5.readFileSync(migrationFile, "utf-8");
|
|
2830
|
+
const operations = parseMigrationOperations(migrationContent2);
|
|
2831
|
+
snapshot = applyMigrationOperations(snapshot, operations);
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
return snapshot;
|
|
2838
|
+
} catch (error) {
|
|
2839
|
+
console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
|
|
2840
|
+
return null;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2540
2844
|
// src/migration/validation.ts
|
|
2541
2845
|
function detectCollectionDeletions(diff) {
|
|
2542
2846
|
const changes = [];
|
|
@@ -2705,8 +3009,8 @@ var DEFAULT_CONFIG5 = {
|
|
|
2705
3009
|
};
|
|
2706
3010
|
function findConfigFile(directory) {
|
|
2707
3011
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
2708
|
-
const filePath =
|
|
2709
|
-
if (
|
|
3012
|
+
const filePath = path5.join(directory, fileName);
|
|
3013
|
+
if (fs5.existsSync(filePath)) {
|
|
2710
3014
|
return filePath;
|
|
2711
3015
|
}
|
|
2712
3016
|
}
|
|
@@ -2714,7 +3018,7 @@ function findConfigFile(directory) {
|
|
|
2714
3018
|
}
|
|
2715
3019
|
function loadJsonConfig(configPath) {
|
|
2716
3020
|
try {
|
|
2717
|
-
const content =
|
|
3021
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
2718
3022
|
return JSON.parse(content);
|
|
2719
3023
|
} catch (error) {
|
|
2720
3024
|
if (error instanceof SyntaxError) {
|
|
@@ -2743,10 +3047,10 @@ async function loadJsConfig(configPath) {
|
|
|
2743
3047
|
}
|
|
2744
3048
|
}
|
|
2745
3049
|
async function loadConfigFile(configPath) {
|
|
2746
|
-
if (!
|
|
3050
|
+
if (!fs5.existsSync(configPath)) {
|
|
2747
3051
|
return null;
|
|
2748
3052
|
}
|
|
2749
|
-
const ext =
|
|
3053
|
+
const ext = path5.extname(configPath).toLowerCase();
|
|
2750
3054
|
if (ext === ".json") {
|
|
2751
3055
|
return loadJsonConfig(configPath);
|
|
2752
3056
|
} else if (ext === ".js" || ext === ".mjs") {
|
|
@@ -2813,10 +3117,10 @@ function validateConfig(config, configPath) {
|
|
|
2813
3117
|
}
|
|
2814
3118
|
const cwd = process.cwd();
|
|
2815
3119
|
const possiblePaths = [
|
|
2816
|
-
|
|
2817
|
-
|
|
3120
|
+
path5.resolve(cwd, config.schema.directory),
|
|
3121
|
+
path5.resolve(cwd, "shared", config.schema.directory)
|
|
2818
3122
|
];
|
|
2819
|
-
const schemaDir = possiblePaths.find((p) =>
|
|
3123
|
+
const schemaDir = possiblePaths.find((p) => fs5.existsSync(p));
|
|
2820
3124
|
if (!schemaDir) {
|
|
2821
3125
|
throw new ConfigurationError(`Schema directory not found. Tried: ${possiblePaths.join(", ")}`, configPath, [
|
|
2822
3126
|
"schema.directory"
|
|
@@ -2828,15 +3132,15 @@ async function loadConfig(options = {}) {
|
|
|
2828
3132
|
let configFilePath;
|
|
2829
3133
|
const cwd = process.cwd();
|
|
2830
3134
|
if (options.config) {
|
|
2831
|
-
const explicitPath =
|
|
2832
|
-
if (!
|
|
3135
|
+
const explicitPath = path5.resolve(cwd, options.config);
|
|
3136
|
+
if (!fs5.existsSync(explicitPath)) {
|
|
2833
3137
|
throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
|
|
2834
3138
|
}
|
|
2835
3139
|
configFilePath = explicitPath;
|
|
2836
3140
|
} else {
|
|
2837
|
-
const searchDirs = [cwd,
|
|
3141
|
+
const searchDirs = [cwd, path5.join(cwd, "shared")];
|
|
2838
3142
|
for (const dir of searchDirs) {
|
|
2839
|
-
if (
|
|
3143
|
+
if (fs5.existsSync(dir)) {
|
|
2840
3144
|
const found = findConfigFile(dir);
|
|
2841
3145
|
if (found) {
|
|
2842
3146
|
configFilePath = found;
|
|
@@ -2865,18 +3169,18 @@ async function loadConfig(options = {}) {
|
|
|
2865
3169
|
function getSchemaDirectory(config) {
|
|
2866
3170
|
const cwd = process.cwd();
|
|
2867
3171
|
const possiblePaths = [
|
|
2868
|
-
|
|
2869
|
-
|
|
3172
|
+
path5.resolve(cwd, config.schema.directory),
|
|
3173
|
+
path5.resolve(cwd, "shared", config.schema.directory)
|
|
2870
3174
|
];
|
|
2871
|
-
return possiblePaths.find((p) =>
|
|
3175
|
+
return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
|
|
2872
3176
|
}
|
|
2873
3177
|
function getMigrationsDirectory(config) {
|
|
2874
3178
|
const cwd = process.cwd();
|
|
2875
3179
|
const possiblePaths = [
|
|
2876
|
-
|
|
2877
|
-
|
|
3180
|
+
path5.resolve(cwd, config.migrations.directory),
|
|
3181
|
+
path5.resolve(cwd, "shared", config.migrations.directory)
|
|
2878
3182
|
];
|
|
2879
|
-
return possiblePaths.find((p) =>
|
|
3183
|
+
return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
|
|
2880
3184
|
}
|
|
2881
3185
|
var currentVerbosity = "normal";
|
|
2882
3186
|
function setVerbosity(level) {
|
|
@@ -3153,7 +3457,7 @@ async function executeGenerate(options) {
|
|
|
3153
3457
|
const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
|
|
3154
3458
|
logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
|
|
3155
3459
|
logInfo("Loading previous snapshot...");
|
|
3156
|
-
const previousSnapshot =
|
|
3460
|
+
const previousSnapshot = loadSnapshotWithMigrations({
|
|
3157
3461
|
migrationsPath: migrationsDir,
|
|
3158
3462
|
workspaceRoot: process.cwd()
|
|
3159
3463
|
});
|
|
@@ -3180,7 +3484,7 @@ async function executeGenerate(options) {
|
|
|
3180
3484
|
"Creating migration file...",
|
|
3181
3485
|
() => Promise.resolve(generate(diff, migrationsDir))
|
|
3182
3486
|
);
|
|
3183
|
-
logSuccess(`Migration file created: ${
|
|
3487
|
+
logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
|
|
3184
3488
|
logSection("\u2705 Next Steps");
|
|
3185
3489
|
console.log();
|
|
3186
3490
|
console.log(" 1. Review the generated migration file:");
|
|
@@ -3346,7 +3650,7 @@ async function executeStatus(options) {
|
|
|
3346
3650
|
const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
|
|
3347
3651
|
logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
|
|
3348
3652
|
logInfo("Loading previous snapshot...");
|
|
3349
|
-
const previousSnapshot =
|
|
3653
|
+
const previousSnapshot = loadSnapshotWithMigrations({
|
|
3350
3654
|
migrationsPath: migrationsDir,
|
|
3351
3655
|
workspaceRoot: process.cwd()
|
|
3352
3656
|
});
|