pocketbase-zod-schema 1.0.1 → 1.0.2
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 +8 -0
- package/README.md +23 -67
- package/dist/cli/migrate.cjs +359 -625
- package/dist/cli/migrate.cjs.map +1 -1
- package/dist/cli/migrate.js +356 -622
- package/dist/cli/migrate.js.map +1 -1
- package/dist/index.cjs +9 -549
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +903 -6
- package/dist/index.d.ts +903 -6
- package/dist/index.js +10 -534
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +539 -2642
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1344 -18
- package/dist/server.d.ts +1344 -18
- package/dist/server.js +538 -2508
- package/dist/server.js.map +1 -1
- package/package.json +2 -64
- package/dist/cli/index.cjs +0 -8013
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.d.cts +0 -32
- package/dist/cli/index.d.ts +0 -32
- package/dist/cli/index.js +0 -7959
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/utils/index.cjs +0 -596
- package/dist/cli/utils/index.cjs.map +0 -1
- package/dist/cli/utils/index.d.cts +0 -282
- package/dist/cli/utils/index.d.ts +0 -282
- package/dist/cli/utils/index.js +0 -542
- package/dist/cli/utils/index.js.map +0 -1
- package/dist/enums.cjs +0 -19
- package/dist/enums.cjs.map +0 -1
- package/dist/enums.d.cts +0 -11
- package/dist/enums.d.ts +0 -11
- package/dist/enums.js +0 -17
- package/dist/enums.js.map +0 -1
- package/dist/fields-D2ZkO9Pe.d.cts +0 -511
- package/dist/fields-D2ZkO9Pe.d.ts +0 -511
- package/dist/migration/analyzer.cjs +0 -1769
- package/dist/migration/analyzer.cjs.map +0 -1
- package/dist/migration/analyzer.d.cts +0 -245
- package/dist/migration/analyzer.d.ts +0 -245
- package/dist/migration/analyzer.js +0 -1728
- package/dist/migration/analyzer.js.map +0 -1
- package/dist/migration/diff.cjs +0 -959
- package/dist/migration/diff.cjs.map +0 -1
- package/dist/migration/diff.d.cts +0 -370
- package/dist/migration/diff.d.ts +0 -370
- package/dist/migration/diff.js +0 -927
- package/dist/migration/diff.js.map +0 -1
- package/dist/migration/engine.cjs +0 -3340
- package/dist/migration/engine.cjs.map +0 -1
- package/dist/migration/engine.d.cts +0 -105
- package/dist/migration/engine.d.ts +0 -105
- package/dist/migration/engine.js +0 -3250
- package/dist/migration/engine.js.map +0 -1
- package/dist/migration/generator.cjs +0 -1632
- package/dist/migration/generator.cjs.map +0 -1
- package/dist/migration/generator.d.cts +0 -493
- package/dist/migration/generator.d.ts +0 -493
- package/dist/migration/generator.js +0 -1565
- package/dist/migration/generator.js.map +0 -1
- package/dist/migration/index.cjs +0 -8331
- package/dist/migration/index.cjs.map +0 -1
- package/dist/migration/index.d.cts +0 -193
- package/dist/migration/index.d.ts +0 -193
- package/dist/migration/index.js +0 -8155
- package/dist/migration/index.js.map +0 -1
- package/dist/migration/snapshot.cjs +0 -2891
- package/dist/migration/snapshot.cjs.map +0 -1
- package/dist/migration/snapshot.d.cts +0 -186
- package/dist/migration/snapshot.d.ts +0 -186
- package/dist/migration/snapshot.js +0 -2856
- package/dist/migration/snapshot.js.map +0 -1
- package/dist/migration/utils/index.cjs +0 -946
- package/dist/migration/utils/index.cjs.map +0 -1
- package/dist/migration/utils/index.d.cts +0 -66
- package/dist/migration/utils/index.d.ts +0 -66
- package/dist/migration/utils/index.js +0 -908
- package/dist/migration/utils/index.js.map +0 -1
- package/dist/mutator.cjs +0 -355
- package/dist/mutator.cjs.map +0 -1
- package/dist/mutator.d.cts +0 -164
- package/dist/mutator.d.ts +0 -164
- package/dist/mutator.js +0 -353
- package/dist/mutator.js.map +0 -1
- package/dist/schema.cjs +0 -737
- package/dist/schema.cjs.map +0 -1
- package/dist/schema.d.cts +0 -563
- package/dist/schema.d.ts +0 -563
- package/dist/schema.js +0 -696
- package/dist/schema.js.map +0 -1
- package/dist/type-mapper-DElLGPUA.d.ts +0 -235
- package/dist/type-mapper-DRxu96CN.d.cts +0 -235
- package/dist/types-Bs7N3Gwt.d.cts +0 -148
- package/dist/types-BxJs7mmh.d.ts +0 -504
- package/dist/types-ByOyly4V.d.ts +0 -148
- package/dist/types-CY5e-ORN.d.cts +0 -504
- package/dist/verify-CkMwxocW.d.cts +0 -402
- package/dist/verify-DqY4TpV6.d.ts +0 -402
package/dist/cli/migrate.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var chalk = require('chalk');
|
|
5
5
|
var commander = require('commander');
|
|
6
6
|
var fs10 = require('fs');
|
|
7
|
-
var
|
|
7
|
+
var path7 = require('path');
|
|
8
8
|
var url = require('url');
|
|
9
9
|
var zod = require('zod');
|
|
10
10
|
var crypto = require('crypto');
|
|
@@ -34,7 +34,7 @@ function _interopNamespace(e) {
|
|
|
34
34
|
|
|
35
35
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
36
36
|
var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
|
|
37
|
-
var
|
|
37
|
+
var path7__namespace = /*#__PURE__*/_interopNamespace(path7);
|
|
38
38
|
var vm__namespace = /*#__PURE__*/_interopNamespace(vm);
|
|
39
39
|
var acorn__namespace = /*#__PURE__*/_interopNamespace(acorn);
|
|
40
40
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
@@ -172,10 +172,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
|
|
|
172
172
|
operation;
|
|
173
173
|
code;
|
|
174
174
|
originalError;
|
|
175
|
-
constructor(message,
|
|
175
|
+
constructor(message, path11, operation, code, originalError) {
|
|
176
176
|
super(message);
|
|
177
177
|
this.name = "FileSystemError";
|
|
178
|
-
this.path =
|
|
178
|
+
this.path = path11;
|
|
179
179
|
this.operation = operation;
|
|
180
180
|
this.code = code;
|
|
181
181
|
this.originalError = originalError;
|
|
@@ -250,7 +250,6 @@ var DEFAULT_CONFIG = {
|
|
|
250
250
|
"permission-templates.js"
|
|
251
251
|
],
|
|
252
252
|
includeExtensions: [".ts", ".js"],
|
|
253
|
-
schemaPatterns: ["Schema", "InputSchema", "Collection"],
|
|
254
253
|
useCompiledFiles: true
|
|
255
254
|
};
|
|
256
255
|
function mergeConfig(config) {
|
|
@@ -258,16 +257,15 @@ function mergeConfig(config) {
|
|
|
258
257
|
...DEFAULT_CONFIG,
|
|
259
258
|
...config,
|
|
260
259
|
excludePatterns: config.excludePatterns || DEFAULT_CONFIG.excludePatterns,
|
|
261
|
-
includeExtensions: config.includeExtensions || DEFAULT_CONFIG.includeExtensions
|
|
262
|
-
schemaPatterns: config.schemaPatterns || DEFAULT_CONFIG.schemaPatterns
|
|
260
|
+
includeExtensions: config.includeExtensions || DEFAULT_CONFIG.includeExtensions
|
|
263
261
|
};
|
|
264
262
|
}
|
|
265
263
|
function resolveSchemaDir(config) {
|
|
266
264
|
const workspaceRoot = config.workspaceRoot || process.cwd();
|
|
267
|
-
if (
|
|
265
|
+
if (path7__namespace.isAbsolute(config.schemaDir)) {
|
|
268
266
|
return config.schemaDir;
|
|
269
267
|
}
|
|
270
|
-
return
|
|
268
|
+
return path7__namespace.join(workspaceRoot, config.schemaDir);
|
|
271
269
|
}
|
|
272
270
|
({
|
|
273
271
|
id: zod.z.string().describe("unique id"),
|
|
@@ -277,17 +275,6 @@ function resolveSchemaDir(config) {
|
|
|
277
275
|
created: zod.z.string().describe("creation timestamp"),
|
|
278
276
|
updated: zod.z.string().describe("last update timestamp")
|
|
279
277
|
});
|
|
280
|
-
({
|
|
281
|
-
created: zod.z.string().describe("creation timestamp"),
|
|
282
|
-
updated: zod.z.string().describe("last update timestamp")
|
|
283
|
-
});
|
|
284
|
-
({
|
|
285
|
-
thumbnailURL: zod.z.string().optional(),
|
|
286
|
-
imageFiles: zod.z.array(zod.z.string())
|
|
287
|
-
});
|
|
288
|
-
({
|
|
289
|
-
imageFiles: zod.z.array(zod.z.instanceof(File))
|
|
290
|
-
});
|
|
291
278
|
var RELATION_METADATA_KEY = "__pocketbase_relation__";
|
|
292
279
|
function extractRelationMetadata(description) {
|
|
293
280
|
if (!description) return null;
|
|
@@ -506,10 +493,10 @@ var RuleValidator = class {
|
|
|
506
493
|
fields;
|
|
507
494
|
collectionName;
|
|
508
495
|
isAuthCollection;
|
|
509
|
-
constructor(collectionName, fields,
|
|
496
|
+
constructor(collectionName, fields, isAuthCollection = false) {
|
|
510
497
|
this.collectionName = collectionName;
|
|
511
498
|
this.fields = new Map(fields.map((f) => [f.name, f]));
|
|
512
|
-
this.isAuthCollection =
|
|
499
|
+
this.isAuthCollection = isAuthCollection;
|
|
513
500
|
this.addSystemFields();
|
|
514
501
|
}
|
|
515
502
|
/**
|
|
@@ -805,11 +792,11 @@ var PermissionAnalyzer = class {
|
|
|
805
792
|
* }
|
|
806
793
|
* ```
|
|
807
794
|
*/
|
|
808
|
-
validatePermissions(collectionName, permissions, fields,
|
|
809
|
-
const validator = new RuleValidator(collectionName, fields,
|
|
795
|
+
validatePermissions(collectionName, permissions, fields, isAuthCollection = false) {
|
|
796
|
+
const validator = new RuleValidator(collectionName, fields, isAuthCollection);
|
|
810
797
|
const results = /* @__PURE__ */ new Map();
|
|
811
798
|
const ruleTypes = ["listRule", "viewRule", "createRule", "updateRule", "deleteRule"];
|
|
812
|
-
if (
|
|
799
|
+
if (isAuthCollection) {
|
|
813
800
|
ruleTypes.push("manageRule");
|
|
814
801
|
}
|
|
815
802
|
for (const ruleType of ruleTypes) {
|
|
@@ -862,146 +849,22 @@ var PermissionAnalyzer = class {
|
|
|
862
849
|
};
|
|
863
850
|
}
|
|
864
851
|
};
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
mouse: "mice",
|
|
882
|
-
Mouse: "Mice",
|
|
883
|
-
goose: "geese",
|
|
884
|
-
Goose: "Geese",
|
|
885
|
-
// Words ending in -y
|
|
886
|
-
category: "categories",
|
|
887
|
-
Category: "Categories",
|
|
888
|
-
company: "companies",
|
|
889
|
-
Company: "Companies",
|
|
890
|
-
city: "cities",
|
|
891
|
-
City: "Cities",
|
|
892
|
-
country: "countries",
|
|
893
|
-
Country: "Countries",
|
|
894
|
-
story: "stories",
|
|
895
|
-
Story: "Stories",
|
|
896
|
-
party: "parties",
|
|
897
|
-
Party: "Parties",
|
|
898
|
-
family: "families",
|
|
899
|
-
Family: "Families",
|
|
900
|
-
activity: "activities",
|
|
901
|
-
Activity: "Activities",
|
|
902
|
-
priority: "priorities",
|
|
903
|
-
Priority: "Priorities",
|
|
904
|
-
// Words ending in -f or -fe
|
|
905
|
-
life: "lives",
|
|
906
|
-
Life: "Lives",
|
|
907
|
-
wife: "wives",
|
|
908
|
-
Wife: "Wives",
|
|
909
|
-
knife: "knives",
|
|
910
|
-
Knife: "Knives",
|
|
911
|
-
leaf: "leaves",
|
|
912
|
-
Leaf: "Leaves",
|
|
913
|
-
shelf: "shelves",
|
|
914
|
-
Shelf: "Shelves",
|
|
915
|
-
half: "halves",
|
|
916
|
-
Half: "Halves",
|
|
917
|
-
// Words ending in -is
|
|
918
|
-
analysis: "analyses",
|
|
919
|
-
Analysis: "Analyses",
|
|
920
|
-
basis: "bases",
|
|
921
|
-
Basis: "Bases",
|
|
922
|
-
crisis: "crises",
|
|
923
|
-
Crisis: "Crises",
|
|
924
|
-
thesis: "theses",
|
|
925
|
-
Thesis: "Theses",
|
|
926
|
-
// Words ending in -us
|
|
927
|
-
cactus: "cacti",
|
|
928
|
-
Cactus: "Cacti",
|
|
929
|
-
focus: "foci",
|
|
930
|
-
Focus: "Foci",
|
|
931
|
-
fungus: "fungi",
|
|
932
|
-
Fungus: "Fungi",
|
|
933
|
-
nucleus: "nuclei",
|
|
934
|
-
Nucleus: "Nuclei",
|
|
935
|
-
radius: "radii",
|
|
936
|
-
Radius: "Radii",
|
|
937
|
-
// Words ending in -on
|
|
938
|
-
phenomenon: "phenomena",
|
|
939
|
-
Phenomenon: "Phenomena",
|
|
940
|
-
criterion: "criteria",
|
|
941
|
-
Criterion: "Criteria",
|
|
942
|
-
// Words ending in -um
|
|
943
|
-
datum: "data",
|
|
944
|
-
Datum: "Data",
|
|
945
|
-
medium: "media",
|
|
946
|
-
Medium: "Media",
|
|
947
|
-
curriculum: "curricula",
|
|
948
|
-
Curriculum: "Curricula",
|
|
949
|
-
// Unchanged plurals
|
|
950
|
-
sheep: "sheep",
|
|
951
|
-
Sheep: "Sheep",
|
|
952
|
-
deer: "deer",
|
|
953
|
-
Deer: "Deer",
|
|
954
|
-
fish: "fish",
|
|
955
|
-
Fish: "Fish",
|
|
956
|
-
species: "species",
|
|
957
|
-
Species: "Species",
|
|
958
|
-
series: "series",
|
|
959
|
-
Series: "Series"
|
|
960
|
-
};
|
|
961
|
-
function pluralize(singular) {
|
|
962
|
-
if (SPECIAL_CASES[singular]) {
|
|
963
|
-
return SPECIAL_CASES[singular];
|
|
964
|
-
}
|
|
965
|
-
if (singular.length > 3 && singular.endsWith("s") && !singular.endsWith("ss")) {
|
|
966
|
-
return singular;
|
|
967
|
-
}
|
|
968
|
-
const lowerSingular = singular.toLowerCase();
|
|
969
|
-
let plural;
|
|
970
|
-
if (/(?:s|ss|sh|ch|x|z)$/.test(lowerSingular)) {
|
|
971
|
-
plural = singular + "es";
|
|
972
|
-
} else if (/[^aeiou]y$/.test(lowerSingular)) {
|
|
973
|
-
plural = singular.slice(0, -1) + "ies";
|
|
974
|
-
} else if (/[^aeiou]o$/.test(lowerSingular)) {
|
|
975
|
-
plural = singular + "es";
|
|
976
|
-
} else if (/fe?$/.test(lowerSingular)) {
|
|
977
|
-
if (lowerSingular.endsWith("fe")) {
|
|
978
|
-
plural = singular.slice(0, -2) + "ves";
|
|
979
|
-
} else {
|
|
980
|
-
plural = singular.slice(0, -1) + "ves";
|
|
981
|
-
}
|
|
982
|
-
} else {
|
|
983
|
-
plural = singular + "s";
|
|
984
|
-
}
|
|
985
|
-
return plural;
|
|
986
|
-
}
|
|
987
|
-
function toCollectionName(entityName) {
|
|
988
|
-
return pluralize(entityName);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// src/migration/utils/relation-detector.ts
|
|
992
|
-
function unwrapType(zodType) {
|
|
993
|
-
let unwrappedType = zodType;
|
|
994
|
-
if (unwrappedType instanceof zod.z.ZodOptional) {
|
|
995
|
-
unwrappedType = unwrappedType.unwrap();
|
|
996
|
-
}
|
|
997
|
-
if (unwrappedType instanceof zod.z.ZodNullable) {
|
|
998
|
-
unwrappedType = unwrappedType.unwrap();
|
|
999
|
-
}
|
|
1000
|
-
if (unwrappedType instanceof zod.z.ZodDefault) {
|
|
1001
|
-
unwrappedType = unwrappedType.unwrap();
|
|
1002
|
-
}
|
|
1003
|
-
return unwrappedType;
|
|
1004
|
-
}
|
|
852
|
+
var POCKETBASE_FIELD_TYPES = [
|
|
853
|
+
"text",
|
|
854
|
+
"email",
|
|
855
|
+
"url",
|
|
856
|
+
"number",
|
|
857
|
+
"bool",
|
|
858
|
+
"date",
|
|
859
|
+
"select",
|
|
860
|
+
"relation",
|
|
861
|
+
"file",
|
|
862
|
+
"json",
|
|
863
|
+
"editor",
|
|
864
|
+
"geoPoint",
|
|
865
|
+
"autodate",
|
|
866
|
+
"password"
|
|
867
|
+
];
|
|
1005
868
|
function getChecks(zodType) {
|
|
1006
869
|
const def = zodType.def ?? zodType._def;
|
|
1007
870
|
return def?.checks ?? [];
|
|
@@ -1014,115 +877,8 @@ function getJsonSchema(zodType) {
|
|
|
1014
877
|
return null;
|
|
1015
878
|
}
|
|
1016
879
|
}
|
|
1017
|
-
function isSingleRelationField(fieldName, zodType) {
|
|
1018
|
-
const unwrappedType = unwrapType(zodType);
|
|
1019
|
-
if (!(unwrappedType instanceof zod.z.ZodString)) {
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
|
-
const startsWithUppercase = /^[A-Z]/.test(fieldName);
|
|
1023
|
-
const commonStringFields = ["Title", "Name", "Description", "Content", "Summary", "Status", "Type"];
|
|
1024
|
-
const isCommonField = commonStringFields.includes(fieldName);
|
|
1025
|
-
return startsWithUppercase && !isCommonField;
|
|
1026
|
-
}
|
|
1027
|
-
function isMultipleRelationField(fieldName, zodType) {
|
|
1028
|
-
const unwrappedType = unwrapType(zodType);
|
|
1029
|
-
if (!(unwrappedType instanceof zod.z.ZodArray)) {
|
|
1030
|
-
return false;
|
|
1031
|
-
}
|
|
1032
|
-
const elementType = unwrappedType.element;
|
|
1033
|
-
if (!(elementType instanceof zod.z.ZodString)) {
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
const hasUppercase = /[A-Z]/.test(fieldName);
|
|
1037
|
-
return hasUppercase;
|
|
1038
|
-
}
|
|
1039
|
-
var REFERENCE_NAME_SUFFIXES = /* @__PURE__ */ new Set([
|
|
1040
|
-
"ref",
|
|
1041
|
-
"refs",
|
|
1042
|
-
"id",
|
|
1043
|
-
"ids",
|
|
1044
|
-
"uid",
|
|
1045
|
-
"uids",
|
|
1046
|
-
"uuid",
|
|
1047
|
-
"uuids",
|
|
1048
|
-
"fk",
|
|
1049
|
-
"fks",
|
|
1050
|
-
"pk",
|
|
1051
|
-
"pks"
|
|
1052
|
-
]);
|
|
1053
|
-
function resolveTargetCollection(fieldName) {
|
|
1054
|
-
const matches = fieldName.match(/[A-Z][a-z]+/g);
|
|
1055
|
-
if (!matches || matches.length === 0) {
|
|
1056
|
-
return pluralize(fieldName);
|
|
1057
|
-
}
|
|
1058
|
-
const entityName = matches[matches.length - 1];
|
|
1059
|
-
if (REFERENCE_NAME_SUFFIXES.has(entityName.toLowerCase())) {
|
|
1060
|
-
throw new Error(buildUnresolvableTargetMessage(fieldName, entityName, matches.slice(0, -1)));
|
|
1061
|
-
}
|
|
1062
|
-
return pluralize(entityName);
|
|
1063
|
-
}
|
|
1064
|
-
function buildUnresolvableTargetMessage(fieldName, suffix, leadingSegments) {
|
|
1065
|
-
const likelyTarget = leadingSegments.length > 0 ? pluralize(leadingSegments.join("")) : "TargetCollection";
|
|
1066
|
-
return `Cannot infer the relation target for field "${fieldName}": the name ends in "${suffix}", which marks it as a reference without naming the collection it points at. Declare the target explicitly, e.g. ${fieldName}: RelationField({ collection: "${likelyTarget}" }).`;
|
|
1067
|
-
}
|
|
1068
|
-
function isRelationField(fieldName, zodType) {
|
|
1069
|
-
return isSingleRelationField(fieldName, zodType) || isMultipleRelationField(fieldName, zodType);
|
|
1070
|
-
}
|
|
1071
|
-
function getMaxSelect(fieldName, zodType) {
|
|
1072
|
-
if (isSingleRelationField(fieldName, zodType)) {
|
|
1073
|
-
return 1;
|
|
1074
|
-
}
|
|
1075
|
-
if (isMultipleRelationField(fieldName, zodType)) {
|
|
1076
|
-
const unwrappedType = unwrapType(zodType);
|
|
1077
|
-
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1078
|
-
const checks = getChecks(unwrappedType);
|
|
1079
|
-
const maxCheck = checks.find((check) => check.kind === "max");
|
|
1080
|
-
if (maxCheck) {
|
|
1081
|
-
return maxCheck.value;
|
|
1082
|
-
}
|
|
1083
|
-
const schema = getJsonSchema(unwrappedType);
|
|
1084
|
-
if (schema && typeof schema.maxItems === "number") {
|
|
1085
|
-
return schema.maxItems;
|
|
1086
|
-
}
|
|
1087
|
-
return 999;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return 1;
|
|
1091
|
-
}
|
|
1092
|
-
function getMinSelect(fieldName, zodType) {
|
|
1093
|
-
if (isSingleRelationField(fieldName, zodType)) {
|
|
1094
|
-
return 0;
|
|
1095
|
-
}
|
|
1096
|
-
if (isMultipleRelationField(fieldName, zodType)) {
|
|
1097
|
-
const unwrappedType = unwrapType(zodType);
|
|
1098
|
-
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1099
|
-
const checks = getChecks(unwrappedType);
|
|
1100
|
-
const minCheck = checks.find((check) => check.kind === "min");
|
|
1101
|
-
if (minCheck) {
|
|
1102
|
-
return minCheck.value;
|
|
1103
|
-
}
|
|
1104
|
-
const schema = getJsonSchema(unwrappedType);
|
|
1105
|
-
if (schema && typeof schema.minItems === "number") {
|
|
1106
|
-
return schema.minItems;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
return 0;
|
|
1111
|
-
}
|
|
1112
|
-
function getChecks2(zodType) {
|
|
1113
|
-
const def = zodType.def ?? zodType._def;
|
|
1114
|
-
return def?.checks ?? [];
|
|
1115
|
-
}
|
|
1116
|
-
function getJsonSchema2(zodType) {
|
|
1117
|
-
try {
|
|
1118
|
-
const toJSONSchema = zodType.toJSONSchema;
|
|
1119
|
-
return typeof toJSONSchema === "function" ? toJSONSchema.call(zodType) : null;
|
|
1120
|
-
} catch {
|
|
1121
|
-
return null;
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
880
|
function mapZodStringType(zodType) {
|
|
1125
|
-
const checks =
|
|
881
|
+
const checks = getChecks(zodType);
|
|
1126
882
|
const hasEmail = checks.some((check) => check.kind === "email" || check.def?.format === "email");
|
|
1127
883
|
if (hasEmail) {
|
|
1128
884
|
return "email";
|
|
@@ -1148,14 +904,11 @@ function mapZodBooleanType(_zodType) {
|
|
|
1148
904
|
function mapZodEnumType(_zodType) {
|
|
1149
905
|
return "select";
|
|
1150
906
|
}
|
|
1151
|
-
function mapZodArrayType(zodType
|
|
907
|
+
function mapZodArrayType(zodType) {
|
|
1152
908
|
const elementType = zodType.element;
|
|
1153
909
|
if (elementType instanceof zod.z.ZodFile) {
|
|
1154
910
|
return "file";
|
|
1155
911
|
}
|
|
1156
|
-
if (elementType instanceof zod.z.ZodString) {
|
|
1157
|
-
return "relation";
|
|
1158
|
-
}
|
|
1159
912
|
return "json";
|
|
1160
913
|
}
|
|
1161
914
|
function mapZodDateType(_zodType) {
|
|
@@ -1164,7 +917,7 @@ function mapZodDateType(_zodType) {
|
|
|
1164
917
|
function mapZodRecordType(_zodType) {
|
|
1165
918
|
return "json";
|
|
1166
919
|
}
|
|
1167
|
-
function mapZodTypeToPocketBase(zodType
|
|
920
|
+
function mapZodTypeToPocketBase(zodType) {
|
|
1168
921
|
const unwrappedType = unwrapZodType(zodType);
|
|
1169
922
|
const metadata = extractFieldMetadata(unwrappedType.description);
|
|
1170
923
|
if (metadata && metadata.type) {
|
|
@@ -1194,7 +947,7 @@ function mapZodTypeToPocketBase(zodType, fieldName) {
|
|
|
1194
947
|
function extractFieldOptions(zodType) {
|
|
1195
948
|
const options = {};
|
|
1196
949
|
const unwrappedType = unwrapZodType(zodType);
|
|
1197
|
-
const checks =
|
|
950
|
+
const checks = getChecks(unwrappedType);
|
|
1198
951
|
if (unwrappedType instanceof zod.z.ZodString) {
|
|
1199
952
|
for (const check of checks) {
|
|
1200
953
|
if (check.kind === "min") {
|
|
@@ -1208,7 +961,7 @@ function extractFieldOptions(zodType) {
|
|
|
1208
961
|
}
|
|
1209
962
|
}
|
|
1210
963
|
if (options.min === void 0 || options.max === void 0 || options.pattern === void 0) {
|
|
1211
|
-
const schema =
|
|
964
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1212
965
|
if (schema) {
|
|
1213
966
|
if (options.min === void 0 && typeof schema.minLength === "number") {
|
|
1214
967
|
options.min = schema.minLength;
|
|
@@ -1231,7 +984,7 @@ function extractFieldOptions(zodType) {
|
|
|
1231
984
|
options.max = check.value;
|
|
1232
985
|
}
|
|
1233
986
|
}
|
|
1234
|
-
const schema =
|
|
987
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1235
988
|
if (schema) {
|
|
1236
989
|
if (options.min === void 0 && typeof schema.minimum === "number") {
|
|
1237
990
|
options.min = schema.minimum;
|
|
@@ -1249,7 +1002,7 @@ function extractFieldOptions(zodType) {
|
|
|
1249
1002
|
options.values = unwrappedType.options.map(String);
|
|
1250
1003
|
}
|
|
1251
1004
|
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1252
|
-
const arrayChecks =
|
|
1005
|
+
const arrayChecks = getChecks(unwrappedType);
|
|
1253
1006
|
for (const check of arrayChecks) {
|
|
1254
1007
|
if (check.kind === "min") {
|
|
1255
1008
|
options.minSelect = check.value;
|
|
@@ -1259,7 +1012,7 @@ function extractFieldOptions(zodType) {
|
|
|
1259
1012
|
}
|
|
1260
1013
|
}
|
|
1261
1014
|
if (options.minSelect === void 0 || options.maxSelect === void 0) {
|
|
1262
|
-
const schema =
|
|
1015
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1263
1016
|
if (schema) {
|
|
1264
1017
|
if (options.minSelect === void 0 && typeof schema.minItems === "number") {
|
|
1265
1018
|
options.minSelect = schema.minItems;
|
|
@@ -1424,10 +1177,6 @@ function validateViewQuery(collectionName, viewQuery) {
|
|
|
1424
1177
|
);
|
|
1425
1178
|
}
|
|
1426
1179
|
}
|
|
1427
|
-
function getCollectionNameFromFile(filePath) {
|
|
1428
|
-
const filename = path8__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1429
|
-
return toCollectionName(filename);
|
|
1430
|
-
}
|
|
1431
1180
|
function extractCollectionNameFromSchema(zodSchema) {
|
|
1432
1181
|
if (!zodSchema.description) {
|
|
1433
1182
|
return null;
|
|
@@ -1467,42 +1216,42 @@ function extractViewQueryFromSchema(zodSchema) {
|
|
|
1467
1216
|
}
|
|
1468
1217
|
return null;
|
|
1469
1218
|
}
|
|
1470
|
-
function
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1219
|
+
function selectCollectionSchema(module) {
|
|
1220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1221
|
+
const candidates = [];
|
|
1222
|
+
const consider = (exportName, value) => {
|
|
1223
|
+
if (!(value instanceof zod.z.ZodObject) || seen.has(value)) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const collectionName = extractCollectionNameFromSchema(value);
|
|
1227
|
+
if (!collectionName) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
seen.add(value);
|
|
1231
|
+
candidates.push({ schema: value, exportName, collectionName });
|
|
1232
|
+
};
|
|
1233
|
+
consider("default", module.default);
|
|
1475
1234
|
for (const [key, value] of Object.entries(module)) {
|
|
1476
|
-
if (key
|
|
1477
|
-
|
|
1478
|
-
if (patterns.includes("InputSchema") && key.endsWith("InputSchema")) {
|
|
1479
|
-
result.inputSchema = value;
|
|
1480
|
-
} else if (!result.schema) {
|
|
1481
|
-
if (patterns.includes("Collection") && key.endsWith("Collection")) {
|
|
1482
|
-
result.schema = value;
|
|
1483
|
-
} else if (patterns.includes("Schema") && key.endsWith("Schema") && !key.endsWith("InputSchema")) {
|
|
1484
|
-
result.schema = value;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1235
|
+
if (key !== "default") {
|
|
1236
|
+
consider(key, value);
|
|
1487
1237
|
}
|
|
1488
1238
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
function selectSchemaForCollection(schemas) {
|
|
1492
|
-
if (schemas.schema) {
|
|
1493
|
-
return schemas.schema;
|
|
1239
|
+
if (candidates.length === 0) {
|
|
1240
|
+
return null;
|
|
1494
1241
|
}
|
|
1495
|
-
if (
|
|
1496
|
-
|
|
1242
|
+
if (candidates.length > 1) {
|
|
1243
|
+
const names = candidates.map((c) => `${c.exportName} (${c.collectionName})`).join(", ");
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
`Multiple collection schemas exported from one file: ${names}. Define one collection per file.`
|
|
1246
|
+
);
|
|
1497
1247
|
}
|
|
1498
|
-
return
|
|
1248
|
+
return candidates[0];
|
|
1499
1249
|
}
|
|
1500
1250
|
function extractFieldDefinitions(zodSchema, excludeFields) {
|
|
1501
1251
|
const shape = zodSchema.shape;
|
|
1502
1252
|
const fields = [];
|
|
1503
1253
|
const baseFields = ["id", "collectionId", "collectionName", "created", "updated", "expand"];
|
|
1504
|
-
const
|
|
1505
|
-
const allExclusions = /* @__PURE__ */ new Set([...baseFields, ...defaultExcludeFields, ...[]]);
|
|
1254
|
+
const allExclusions = /* @__PURE__ */ new Set([...baseFields, ...[]]);
|
|
1506
1255
|
for (const [fieldName, zodType] of Object.entries(shape)) {
|
|
1507
1256
|
if (!allExclusions.has(fieldName)) {
|
|
1508
1257
|
fields.push({ name: fieldName, zodType });
|
|
@@ -1715,16 +1464,15 @@ function generateFindCollectionCode(name, collectionIdMap) {
|
|
|
1715
1464
|
}
|
|
1716
1465
|
|
|
1717
1466
|
// src/migration/analyzer/converter.ts
|
|
1718
|
-
function isAuthCollection(fields) {
|
|
1719
|
-
const fieldNames = fields.map((f) => f.name.toLowerCase());
|
|
1720
|
-
const hasEmail = fieldNames.includes("email");
|
|
1721
|
-
const hasPassword = fieldNames.includes("password");
|
|
1722
|
-
return hasEmail && hasPassword;
|
|
1723
|
-
}
|
|
1724
1467
|
function buildFieldDefinition(fieldName, zodType) {
|
|
1725
1468
|
const unwrappedType = unwrapZodType(zodType);
|
|
1726
1469
|
const fieldMetadata = extractFieldMetadata(unwrappedType.description ?? zodType.description);
|
|
1727
1470
|
if (fieldMetadata) {
|
|
1471
|
+
if (!fieldMetadata.type || !POCKETBASE_FIELD_TYPES.includes(fieldMetadata.type)) {
|
|
1472
|
+
throw new Error(
|
|
1473
|
+
`Field "${fieldName}" carries __pocketbase_field__ metadata with unknown type "${fieldMetadata.type}". Use one of: ${POCKETBASE_FIELD_TYPES.join(", ")}.`
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1728
1476
|
let required2;
|
|
1729
1477
|
if (fieldMetadata.type === "number") {
|
|
1730
1478
|
if (fieldMetadata.options?.required !== void 0) {
|
|
@@ -1759,6 +1507,26 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1759
1507
|
}
|
|
1760
1508
|
return fieldDef2;
|
|
1761
1509
|
}
|
|
1510
|
+
const relationMetadata = extractRelationMetadata(unwrappedType.description ?? zodType.description);
|
|
1511
|
+
if (relationMetadata) {
|
|
1512
|
+
return {
|
|
1513
|
+
name: fieldName,
|
|
1514
|
+
id: generateFieldId("relation", fieldName),
|
|
1515
|
+
type: "relation",
|
|
1516
|
+
required: isFieldRequired(zodType),
|
|
1517
|
+
// Zod validators on the field (array min/max etc.) are already captured
|
|
1518
|
+
// in the relation metadata; the field itself carries no options
|
|
1519
|
+
options: void 0,
|
|
1520
|
+
zodType,
|
|
1521
|
+
relation: {
|
|
1522
|
+
collection: relationMetadata.collection,
|
|
1523
|
+
maxSelect: relationMetadata.maxSelect,
|
|
1524
|
+
minSelect: relationMetadata.minSelect,
|
|
1525
|
+
cascadeDelete: relationMetadata.cascadeDelete,
|
|
1526
|
+
displayFields: relationMetadata.displayFields
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1762
1530
|
const fieldType = mapZodTypeToPocketBase(zodType);
|
|
1763
1531
|
const required = isFieldRequired(zodType);
|
|
1764
1532
|
const options = extractFieldOptions(zodType);
|
|
@@ -1770,35 +1538,6 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1770
1538
|
options,
|
|
1771
1539
|
zodType
|
|
1772
1540
|
};
|
|
1773
|
-
const relationMetadata = extractRelationMetadata(unwrappedType.description ?? zodType.description);
|
|
1774
|
-
if (relationMetadata) {
|
|
1775
|
-
fieldDef.type = "relation";
|
|
1776
|
-
fieldDef.relation = {
|
|
1777
|
-
collection: relationMetadata.collection,
|
|
1778
|
-
maxSelect: relationMetadata.maxSelect,
|
|
1779
|
-
minSelect: relationMetadata.minSelect,
|
|
1780
|
-
cascadeDelete: relationMetadata.cascadeDelete,
|
|
1781
|
-
displayFields: relationMetadata.displayFields
|
|
1782
|
-
};
|
|
1783
|
-
fieldDef.options = void 0;
|
|
1784
|
-
} else if (isRelationField(fieldName, zodType)) {
|
|
1785
|
-
fieldDef.type = "relation";
|
|
1786
|
-
const targetCollection = resolveTargetCollection(fieldName);
|
|
1787
|
-
const maxSelect = getMaxSelect(fieldName, zodType);
|
|
1788
|
-
const minSelect = getMinSelect(fieldName, zodType);
|
|
1789
|
-
fieldDef.relation = {
|
|
1790
|
-
collection: targetCollection,
|
|
1791
|
-
maxSelect,
|
|
1792
|
-
minSelect,
|
|
1793
|
-
cascadeDelete: false,
|
|
1794
|
-
// Default to false, can be configured later
|
|
1795
|
-
displayFields: null
|
|
1796
|
-
};
|
|
1797
|
-
if (fieldDef.options) {
|
|
1798
|
-
const { min: _min, max: _max, pattern: _pattern, ...relationSafeOptions } = fieldDef.options;
|
|
1799
|
-
fieldDef.options = Object.keys(relationSafeOptions).length ? relationSafeOptions : void 0;
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
1541
|
if (fieldDef.type === "autodate") {
|
|
1803
1542
|
fieldDef.options = {
|
|
1804
1543
|
onCreate: true,
|
|
@@ -1822,7 +1561,7 @@ function extractChainedFieldOptions(fieldType, unwrappedType) {
|
|
|
1822
1561
|
function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
1823
1562
|
const rawFields = extractFieldDefinitions(zodSchema);
|
|
1824
1563
|
const explicitType = extractCollectionTypeFromSchema(zodSchema);
|
|
1825
|
-
const collectionType = explicitType ??
|
|
1564
|
+
const collectionType = explicitType ?? "base";
|
|
1826
1565
|
const isView = collectionType === "view";
|
|
1827
1566
|
const viewQuery = extractViewQueryFromSchema(zodSchema);
|
|
1828
1567
|
if (isView) {
|
|
@@ -1933,9 +1672,8 @@ async function ensureTsxLoader() {
|
|
|
1933
1672
|
}
|
|
1934
1673
|
}
|
|
1935
1674
|
function discoverSchemaFiles(config) {
|
|
1936
|
-
const
|
|
1937
|
-
const
|
|
1938
|
-
const schemaDir = resolveSchemaDir(normalizedConfig);
|
|
1675
|
+
const mergedConfig = mergeConfig(config);
|
|
1676
|
+
const schemaDir = resolveSchemaDir(config);
|
|
1939
1677
|
try {
|
|
1940
1678
|
if (!fs10__namespace.existsSync(schemaDir)) {
|
|
1941
1679
|
throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
|
|
@@ -1963,7 +1701,7 @@ function discoverSchemaFiles(config) {
|
|
|
1963
1701
|
`(${mergedConfig.includeExtensions.map((ext) => ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`
|
|
1964
1702
|
);
|
|
1965
1703
|
return schemaFiles.map((file) => {
|
|
1966
|
-
return
|
|
1704
|
+
return path7__namespace.join(schemaDir, file.replace(extensionRemovalRegex, ""));
|
|
1967
1705
|
});
|
|
1968
1706
|
} catch (error) {
|
|
1969
1707
|
if (error instanceof FileSystemError) {
|
|
@@ -2015,7 +1753,7 @@ Alternatively, compile your schema files to JavaScript first.`,
|
|
|
2015
1753
|
);
|
|
2016
1754
|
}
|
|
2017
1755
|
}
|
|
2018
|
-
const fileUrl = new URL(`file://${
|
|
1756
|
+
const fileUrl = new URL(`file://${path7__namespace.resolve(resolvedPath)}`);
|
|
2019
1757
|
const module = await import(fileUrl.href);
|
|
2020
1758
|
return module;
|
|
2021
1759
|
} catch (error) {
|
|
@@ -2042,13 +1780,13 @@ Alternatively, compile your schema files to JavaScript first.`,
|
|
|
2042
1780
|
}
|
|
2043
1781
|
|
|
2044
1782
|
// src/migration/analyzer/index.ts
|
|
2045
|
-
async function
|
|
2046
|
-
const
|
|
2047
|
-
const mergedConfig = mergeConfig(normalizedConfig);
|
|
1783
|
+
async function parseSchemaFiles(config) {
|
|
1784
|
+
const mergedConfig = mergeConfig(config);
|
|
2048
1785
|
const collections = /* @__PURE__ */ new Map();
|
|
2049
|
-
const
|
|
1786
|
+
const collectionSources = /* @__PURE__ */ new Map();
|
|
1787
|
+
const schemaFiles = discoverSchemaFiles(config);
|
|
2050
1788
|
if (schemaFiles.length === 0) {
|
|
2051
|
-
const schemaDir = resolveSchemaDir(
|
|
1789
|
+
const schemaDir = resolveSchemaDir(config);
|
|
2052
1790
|
throw new SchemaParsingError(
|
|
2053
1791
|
`No schema files found in ${schemaDir}. Make sure you have schema files in the directory.`,
|
|
2054
1792
|
schemaDir
|
|
@@ -2057,8 +1795,8 @@ async function buildSchemaDefinition(config) {
|
|
|
2057
1795
|
for (const filePath of schemaFiles) {
|
|
2058
1796
|
try {
|
|
2059
1797
|
let importPath = filePath;
|
|
2060
|
-
if (
|
|
2061
|
-
importPath =
|
|
1798
|
+
if (config.pathTransformer) {
|
|
1799
|
+
importPath = config.pathTransformer(filePath);
|
|
2062
1800
|
} else if (mergedConfig.useCompiledFiles) {
|
|
2063
1801
|
const distPath = filePath.replace(/\/src\//, "/dist/");
|
|
2064
1802
|
if (fs10__namespace.existsSync(`${distPath}.js`) || fs10__namespace.existsSync(`${distPath}.mjs`)) {
|
|
@@ -2067,17 +1805,25 @@ async function buildSchemaDefinition(config) {
|
|
|
2067
1805
|
importPath = filePath;
|
|
2068
1806
|
}
|
|
2069
1807
|
}
|
|
2070
|
-
const module = await importSchemaModule(importPath,
|
|
2071
|
-
const
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
1808
|
+
const module = await importSchemaModule(importPath, config);
|
|
1809
|
+
const collectionExport = selectCollectionSchema(module);
|
|
1810
|
+
if (!collectionExport) {
|
|
1811
|
+
console.warn(
|
|
1812
|
+
`${filePath}: no export carries collection metadata (use defineCollection()/defineView()); skipping. If this file previously produced a collection, the diff may now propose deleting it.`
|
|
1813
|
+
);
|
|
2075
1814
|
continue;
|
|
2076
1815
|
}
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2079
|
-
|
|
1816
|
+
const { collectionName, schema } = collectionExport;
|
|
1817
|
+
const existingSource = collectionSources.get(collectionName);
|
|
1818
|
+
if (existingSource) {
|
|
1819
|
+
throw new SchemaParsingError(
|
|
1820
|
+
`Collection "${collectionName}" is declared in both ${existingSource} and ${filePath}. Collection names must be unique across schema files.`,
|
|
1821
|
+
filePath
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
const collectionSchema = convertZodSchemaToCollectionSchema(collectionName, schema);
|
|
2080
1825
|
collections.set(collectionName, collectionSchema);
|
|
1826
|
+
collectionSources.set(collectionName, filePath);
|
|
2081
1827
|
} catch (error) {
|
|
2082
1828
|
if (error instanceof SchemaParsingError) {
|
|
2083
1829
|
throw error;
|
|
@@ -2091,9 +1837,6 @@ async function buildSchemaDefinition(config) {
|
|
|
2091
1837
|
}
|
|
2092
1838
|
return { collections };
|
|
2093
1839
|
}
|
|
2094
|
-
async function parseSchemaFiles(config) {
|
|
2095
|
-
return buildSchemaDefinition(config);
|
|
2096
|
-
}
|
|
2097
1840
|
|
|
2098
1841
|
// src/migration/engine/fields.ts
|
|
2099
1842
|
var Field = class {
|
|
@@ -4088,21 +3831,21 @@ function appliedMigrationsFromList(files, origin = "<list>") {
|
|
|
4088
3831
|
const entries = [];
|
|
4089
3832
|
const coreEntries = [];
|
|
4090
3833
|
for (const file of files) {
|
|
4091
|
-
const entry = { file:
|
|
3834
|
+
const entry = { file: path7__namespace.basename(file) };
|
|
4092
3835
|
(isCoreMigration(entry.file) ? coreEntries : entries).push(entry);
|
|
4093
3836
|
}
|
|
4094
3837
|
return { origin, entries, coreEntries };
|
|
4095
3838
|
}
|
|
4096
3839
|
function defaultDataDirectory(migrationsPath) {
|
|
4097
|
-
return
|
|
3840
|
+
return path7__namespace.resolve(path7__namespace.dirname(path7__namespace.resolve(migrationsPath)), "pb_data");
|
|
4098
3841
|
}
|
|
4099
3842
|
function resolveDatabasePath(dataPathOrFile) {
|
|
4100
|
-
const resolved =
|
|
3843
|
+
const resolved = path7__namespace.resolve(dataPathOrFile);
|
|
4101
3844
|
if (!fs10__namespace.existsSync(resolved)) {
|
|
4102
3845
|
return null;
|
|
4103
3846
|
}
|
|
4104
3847
|
if (fs10__namespace.statSync(resolved).isDirectory()) {
|
|
4105
|
-
const candidate =
|
|
3848
|
+
const candidate = path7__namespace.join(resolved, POCKETBASE_DATABASE_FILENAME);
|
|
4106
3849
|
return fs10__namespace.existsSync(candidate) ? candidate : null;
|
|
4107
3850
|
}
|
|
4108
3851
|
return resolved;
|
|
@@ -4111,7 +3854,7 @@ function readAppliedMigrations(dataPathOrFile) {
|
|
|
4111
3854
|
const databasePath = resolveDatabasePath(dataPathOrFile);
|
|
4112
3855
|
if (!databasePath) {
|
|
4113
3856
|
throw new AppliedMigrationsError(
|
|
4114
|
-
`No PocketBase database found at ${
|
|
3857
|
+
`No PocketBase database found at ${path7__namespace.resolve(dataPathOrFile)}. Expected a data.db file, or a pb_data directory containing one.`,
|
|
4115
3858
|
dataPathOrFile
|
|
4116
3859
|
);
|
|
4117
3860
|
}
|
|
@@ -4227,7 +3970,7 @@ function discoverMigrations(migrationsPath) {
|
|
|
4227
3970
|
continue;
|
|
4228
3971
|
}
|
|
4229
3972
|
discovered.push({
|
|
4230
|
-
path:
|
|
3973
|
+
path: path7__namespace.join(migrationsPath, name),
|
|
4231
3974
|
name,
|
|
4232
3975
|
timestamp,
|
|
4233
3976
|
isSnapshot: name.endsWith("_collections_snapshot.js") || name.endsWith("_snapshot.js")
|
|
@@ -4506,8 +4249,8 @@ function replayMigrationsDirectory(migrationsPath, options = {}) {
|
|
|
4506
4249
|
}
|
|
4507
4250
|
return replayMigrations(plan.filesToReplay, { ...engineOptions, plan });
|
|
4508
4251
|
}
|
|
4509
|
-
|
|
4510
|
-
|
|
4252
|
+
|
|
4253
|
+
// src/migration/snapshot.ts
|
|
4511
4254
|
function loadSnapshotWithMigrations(config = {}) {
|
|
4512
4255
|
const migrationsPath = config.migrationsPath;
|
|
4513
4256
|
if (!migrationsPath) {
|
|
@@ -4536,16 +4279,13 @@ function loadSnapshotWithMigrations(config = {}) {
|
|
|
4536
4279
|
}
|
|
4537
4280
|
|
|
4538
4281
|
// src/migration/diff/config.ts
|
|
4539
|
-
var
|
|
4540
|
-
warnOnDelete: true,
|
|
4541
|
-
requireForceForDestructive: true,
|
|
4542
|
-
severityThreshold: "high",
|
|
4282
|
+
var DEFAULT_CONFIG2 = {
|
|
4543
4283
|
systemCollections: ["_mfas", "_otps", "_externalAuths", "_authOrigins", "_superusers"],
|
|
4544
4284
|
usersSystemFields: ["id", "password", "tokenKey", "email", "emailVisibility", "verified", "created", "updated"]
|
|
4545
4285
|
};
|
|
4546
4286
|
function mergeConfig2(config) {
|
|
4547
4287
|
return {
|
|
4548
|
-
...
|
|
4288
|
+
...DEFAULT_CONFIG2,
|
|
4549
4289
|
...config
|
|
4550
4290
|
};
|
|
4551
4291
|
}
|
|
@@ -5000,57 +4740,6 @@ function buildCollectionModification(currentCollection, previousCollection, conf
|
|
|
5000
4740
|
};
|
|
5001
4741
|
}
|
|
5002
4742
|
|
|
5003
|
-
// src/migration/diff/summary.ts
|
|
5004
|
-
function categorizeChangesBySeverity(diff, _config) {
|
|
5005
|
-
const destructive = [];
|
|
5006
|
-
const nonDestructive = [];
|
|
5007
|
-
for (const collection of diff.collectionsToDelete) {
|
|
5008
|
-
if (collection.type === "view") {
|
|
5009
|
-
nonDestructive.push(`Delete view collection: ${collection.name}`);
|
|
5010
|
-
} else {
|
|
5011
|
-
destructive.push(`Delete collection: ${collection.name}`);
|
|
5012
|
-
}
|
|
5013
|
-
}
|
|
5014
|
-
for (const collection of diff.collectionsToCreate) {
|
|
5015
|
-
nonDestructive.push(`Create collection: ${collection.name}`);
|
|
5016
|
-
}
|
|
5017
|
-
for (const modification of diff.collectionsToModify) {
|
|
5018
|
-
const collectionName = modification.collection;
|
|
5019
|
-
for (const field of modification.fieldsToRemove) {
|
|
5020
|
-
destructive.push(`Delete field: ${collectionName}.${field.name}`);
|
|
5021
|
-
}
|
|
5022
|
-
for (const field of modification.fieldsToAdd) {
|
|
5023
|
-
nonDestructive.push(`Add field: ${collectionName}.${field.name}`);
|
|
5024
|
-
}
|
|
5025
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
5026
|
-
const hasTypeChange = fieldMod.changes.some((c) => c.property === "type");
|
|
5027
|
-
const hasRequiredChange = fieldMod.changes.some((c) => c.property === "required" && c.newValue === true);
|
|
5028
|
-
if (hasTypeChange) {
|
|
5029
|
-
destructive.push(
|
|
5030
|
-
`Change field type: ${collectionName}.${fieldMod.fieldName} (${fieldMod.changes.find((c) => c.property === "type")?.oldValue} \u2192 ${fieldMod.changes.find((c) => c.property === "type")?.newValue})`
|
|
5031
|
-
);
|
|
5032
|
-
} else if (hasRequiredChange) {
|
|
5033
|
-
destructive.push(`Make field required: ${collectionName}.${fieldMod.fieldName}`);
|
|
5034
|
-
} else {
|
|
5035
|
-
nonDestructive.push(`Modify field: ${collectionName}.${fieldMod.fieldName}`);
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
for (const _index of modification.indexesToAdd) {
|
|
5039
|
-
nonDestructive.push(`Add index: ${collectionName}`);
|
|
5040
|
-
}
|
|
5041
|
-
for (const _index of modification.indexesToRemove) {
|
|
5042
|
-
nonDestructive.push(`Remove index: ${collectionName}`);
|
|
5043
|
-
}
|
|
5044
|
-
for (const rule of modification.rulesToUpdate) {
|
|
5045
|
-
nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
|
|
5046
|
-
}
|
|
5047
|
-
if (modification.viewQueryUpdate) {
|
|
5048
|
-
nonDestructive.push(`Update view query: ${collectionName}`);
|
|
5049
|
-
}
|
|
5050
|
-
}
|
|
5051
|
-
return { destructive, nonDestructive };
|
|
5052
|
-
}
|
|
5053
|
-
|
|
5054
4743
|
// src/migration/diff/filter.ts
|
|
5055
4744
|
function matchesPattern(text, patterns) {
|
|
5056
4745
|
if (!patterns || patterns.length === 0) return true;
|
|
@@ -5127,11 +4816,62 @@ function filterDiff(diff, options) {
|
|
|
5127
4816
|
};
|
|
5128
4817
|
}
|
|
5129
4818
|
|
|
4819
|
+
// src/migration/diff/summary.ts
|
|
4820
|
+
function categorizeChangesBySeverity(diff, _config) {
|
|
4821
|
+
const destructive = [];
|
|
4822
|
+
const nonDestructive = [];
|
|
4823
|
+
for (const collection of diff.collectionsToDelete) {
|
|
4824
|
+
if (collection.type === "view") {
|
|
4825
|
+
nonDestructive.push(`Delete view collection: ${collection.name}`);
|
|
4826
|
+
} else {
|
|
4827
|
+
destructive.push(`Delete collection: ${collection.name}`);
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
for (const collection of diff.collectionsToCreate) {
|
|
4831
|
+
nonDestructive.push(`Create collection: ${collection.name}`);
|
|
4832
|
+
}
|
|
4833
|
+
for (const modification of diff.collectionsToModify) {
|
|
4834
|
+
const collectionName = modification.collection;
|
|
4835
|
+
for (const field of modification.fieldsToRemove) {
|
|
4836
|
+
destructive.push(`Delete field: ${collectionName}.${field.name}`);
|
|
4837
|
+
}
|
|
4838
|
+
for (const field of modification.fieldsToAdd) {
|
|
4839
|
+
nonDestructive.push(`Add field: ${collectionName}.${field.name}`);
|
|
4840
|
+
}
|
|
4841
|
+
for (const fieldMod of modification.fieldsToModify) {
|
|
4842
|
+
const hasTypeChange = fieldMod.changes.some((c) => c.property === "type");
|
|
4843
|
+
const hasRequiredChange = fieldMod.changes.some((c) => c.property === "required" && c.newValue === true);
|
|
4844
|
+
if (hasTypeChange) {
|
|
4845
|
+
destructive.push(
|
|
4846
|
+
`Change field type: ${collectionName}.${fieldMod.fieldName} (${fieldMod.changes.find((c) => c.property === "type")?.oldValue} \u2192 ${fieldMod.changes.find((c) => c.property === "type")?.newValue})`
|
|
4847
|
+
);
|
|
4848
|
+
} else if (hasRequiredChange) {
|
|
4849
|
+
destructive.push(`Make field required: ${collectionName}.${fieldMod.fieldName}`);
|
|
4850
|
+
} else {
|
|
4851
|
+
nonDestructive.push(`Modify field: ${collectionName}.${fieldMod.fieldName}`);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
for (const _index of modification.indexesToAdd) {
|
|
4855
|
+
nonDestructive.push(`Add index: ${collectionName}`);
|
|
4856
|
+
}
|
|
4857
|
+
for (const _index of modification.indexesToRemove) {
|
|
4858
|
+
nonDestructive.push(`Remove index: ${collectionName}`);
|
|
4859
|
+
}
|
|
4860
|
+
for (const rule of modification.rulesToUpdate) {
|
|
4861
|
+
nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
|
|
4862
|
+
}
|
|
4863
|
+
if (modification.viewQueryUpdate) {
|
|
4864
|
+
nonDestructive.push(`Update view query: ${collectionName}`);
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
return { destructive, nonDestructive };
|
|
4868
|
+
}
|
|
4869
|
+
|
|
5130
4870
|
// src/migration/diff/index.ts
|
|
5131
4871
|
function hasChanges(modification) {
|
|
5132
4872
|
return modification.fieldsToAdd.length > 0 || modification.fieldsToRemove.length > 0 || modification.fieldsToModify.length > 0 || modification.indexesToAdd.length > 0 || modification.indexesToRemove.length > 0 || modification.rulesToUpdate.length > 0 || modification.permissionsToUpdate.length > 0 || modification.viewQueryUpdate !== void 0;
|
|
5133
4873
|
}
|
|
5134
|
-
function
|
|
4874
|
+
function compare2(currentSchema, previousSnapshot, config) {
|
|
5135
4875
|
const collectionIdToName = /* @__PURE__ */ new Map();
|
|
5136
4876
|
if (previousSnapshot) {
|
|
5137
4877
|
for (const [name, collection] of previousSnapshot.collections) {
|
|
@@ -5183,8 +4923,153 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
|
|
|
5183
4923
|
existingCollectionIds
|
|
5184
4924
|
};
|
|
5185
4925
|
}
|
|
5186
|
-
|
|
5187
|
-
|
|
4926
|
+
|
|
4927
|
+
// src/migration/validation.ts
|
|
4928
|
+
function detectCollectionDeletions(diff) {
|
|
4929
|
+
const changes = [];
|
|
4930
|
+
for (const collection of diff.collectionsToDelete) {
|
|
4931
|
+
if (collection.type === "view") {
|
|
4932
|
+
continue;
|
|
4933
|
+
}
|
|
4934
|
+
changes.push({
|
|
4935
|
+
type: "collection_deletion" /* COLLECTION_DELETION */,
|
|
4936
|
+
description: `Delete collection: ${collection.name}`,
|
|
4937
|
+
collection: collection.name,
|
|
4938
|
+
severity: "high",
|
|
4939
|
+
warning: `All data in the "${collection.name}" collection will be permanently deleted.`
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
return changes;
|
|
4943
|
+
}
|
|
4944
|
+
function detectFieldDeletions(diff) {
|
|
4945
|
+
const changes = [];
|
|
4946
|
+
for (const modification of diff.collectionsToModify) {
|
|
4947
|
+
for (const field of modification.fieldsToRemove) {
|
|
4948
|
+
changes.push({
|
|
4949
|
+
type: "field_deletion" /* FIELD_DELETION */,
|
|
4950
|
+
description: `Delete field: ${modification.collection}.${field.name}`,
|
|
4951
|
+
collection: modification.collection,
|
|
4952
|
+
field: field.name,
|
|
4953
|
+
severity: "high",
|
|
4954
|
+
warning: `All data in the "${field.name}" field of "${modification.collection}" will be permanently deleted.`
|
|
4955
|
+
});
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
return changes;
|
|
4959
|
+
}
|
|
4960
|
+
function detectFieldTypeChanges(diff) {
|
|
4961
|
+
const changes = [];
|
|
4962
|
+
for (const modification of diff.collectionsToModify) {
|
|
4963
|
+
for (const fieldMod of modification.fieldsToModify) {
|
|
4964
|
+
const typeChange = fieldMod.changes.find((c) => c.property === "type");
|
|
4965
|
+
if (typeChange) {
|
|
4966
|
+
changes.push({
|
|
4967
|
+
type: "field_type_change" /* FIELD_TYPE_CHANGE */,
|
|
4968
|
+
description: `Change field type: ${modification.collection}.${fieldMod.fieldName}`,
|
|
4969
|
+
collection: modification.collection,
|
|
4970
|
+
field: fieldMod.fieldName,
|
|
4971
|
+
details: {
|
|
4972
|
+
oldValue: typeChange.oldValue,
|
|
4973
|
+
newValue: typeChange.newValue
|
|
4974
|
+
},
|
|
4975
|
+
severity: "high",
|
|
4976
|
+
warning: `Changing field type from "${typeChange.oldValue}" to "${typeChange.newValue}" may cause data loss or conversion errors.`
|
|
4977
|
+
});
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
return changes;
|
|
4982
|
+
}
|
|
4983
|
+
function detectFieldRequiredChanges(diff) {
|
|
4984
|
+
const changes = [];
|
|
4985
|
+
for (const modification of diff.collectionsToModify) {
|
|
4986
|
+
for (const fieldMod of modification.fieldsToModify) {
|
|
4987
|
+
const requiredChange = fieldMod.changes.find(
|
|
4988
|
+
(c) => c.property === "required" && c.newValue === true && c.oldValue === false
|
|
4989
|
+
);
|
|
4990
|
+
if (requiredChange) {
|
|
4991
|
+
changes.push({
|
|
4992
|
+
type: "field_required_change" /* FIELD_REQUIRED_CHANGE */,
|
|
4993
|
+
description: `Make field required: ${modification.collection}.${fieldMod.fieldName}`,
|
|
4994
|
+
collection: modification.collection,
|
|
4995
|
+
field: fieldMod.fieldName,
|
|
4996
|
+
details: {
|
|
4997
|
+
oldValue: false,
|
|
4998
|
+
newValue: true
|
|
4999
|
+
},
|
|
5000
|
+
severity: "medium",
|
|
5001
|
+
warning: `Making "${fieldMod.fieldName}" required may cause issues with existing records that have null/empty values.`
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
return changes;
|
|
5007
|
+
}
|
|
5008
|
+
function detectDestructiveChanges(diff) {
|
|
5009
|
+
const changes = [];
|
|
5010
|
+
changes.push(...detectCollectionDeletions(diff));
|
|
5011
|
+
changes.push(...detectFieldDeletions(diff));
|
|
5012
|
+
changes.push(...detectFieldTypeChanges(diff));
|
|
5013
|
+
changes.push(...detectFieldRequiredChanges(diff));
|
|
5014
|
+
return changes;
|
|
5015
|
+
}
|
|
5016
|
+
function formatDestructiveChanges(changes) {
|
|
5017
|
+
if (changes.length === 0) {
|
|
5018
|
+
return "No destructive changes detected.";
|
|
5019
|
+
}
|
|
5020
|
+
const lines = [];
|
|
5021
|
+
const highSeverity = changes.filter((c) => c.severity === "high");
|
|
5022
|
+
const mediumSeverity = changes.filter((c) => c.severity === "medium");
|
|
5023
|
+
const lowSeverity = changes.filter((c) => c.severity === "low");
|
|
5024
|
+
if (highSeverity.length > 0) {
|
|
5025
|
+
lines.push("\u{1F534} HIGH SEVERITY CHANGES (Data Loss Risk):");
|
|
5026
|
+
lines.push("");
|
|
5027
|
+
for (const change of highSeverity) {
|
|
5028
|
+
lines.push(` \u2022 ${change.description}`);
|
|
5029
|
+
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
5030
|
+
if (change.details) {
|
|
5031
|
+
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
5032
|
+
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
lines.push("");
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
if (mediumSeverity.length > 0) {
|
|
5039
|
+
lines.push("\u{1F7E1} MEDIUM SEVERITY CHANGES (Potential Issues):");
|
|
5040
|
+
lines.push("");
|
|
5041
|
+
for (const change of mediumSeverity) {
|
|
5042
|
+
lines.push(` \u2022 ${change.description}`);
|
|
5043
|
+
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
5044
|
+
if (change.details) {
|
|
5045
|
+
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
5046
|
+
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
lines.push("");
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
if (lowSeverity.length > 0) {
|
|
5053
|
+
lines.push("\u{1F7E2} LOW SEVERITY CHANGES:");
|
|
5054
|
+
lines.push("");
|
|
5055
|
+
for (const change of lowSeverity) {
|
|
5056
|
+
lines.push(` \u2022 ${change.description}`);
|
|
5057
|
+
lines.push(` \u2139\uFE0F ${change.warning}`);
|
|
5058
|
+
lines.push("");
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
return lines.join("\n");
|
|
5062
|
+
}
|
|
5063
|
+
function summarizeDestructiveChanges(changes) {
|
|
5064
|
+
return {
|
|
5065
|
+
total: changes.length,
|
|
5066
|
+
high: changes.filter((c) => c.severity === "high").length,
|
|
5067
|
+
medium: changes.filter((c) => c.severity === "medium").length,
|
|
5068
|
+
low: changes.filter((c) => c.severity === "low").length
|
|
5069
|
+
};
|
|
5070
|
+
}
|
|
5071
|
+
function requiresForceFlag(changes) {
|
|
5072
|
+
return changes.some((c) => c.severity === "high" || c.severity === "medium");
|
|
5188
5073
|
}
|
|
5189
5074
|
|
|
5190
5075
|
// src/migration/generator/config.ts
|
|
@@ -5195,7 +5080,7 @@ migrate((app) => {
|
|
|
5195
5080
|
{{DOWN_CODE}}
|
|
5196
5081
|
});
|
|
5197
5082
|
`;
|
|
5198
|
-
var
|
|
5083
|
+
var DEFAULT_CONFIG3 = {
|
|
5199
5084
|
workspaceRoot: process.cwd(),
|
|
5200
5085
|
timestampGenerator: () => Math.floor(Date.now() / 1e3).toString(),
|
|
5201
5086
|
template: DEFAULT_TEMPLATE,
|
|
@@ -5204,20 +5089,20 @@ var DEFAULT_CONFIG4 = {
|
|
|
5204
5089
|
};
|
|
5205
5090
|
function mergeConfig3(config) {
|
|
5206
5091
|
return {
|
|
5207
|
-
...
|
|
5092
|
+
...DEFAULT_CONFIG3,
|
|
5208
5093
|
force: false,
|
|
5209
5094
|
...config
|
|
5210
5095
|
};
|
|
5211
5096
|
}
|
|
5212
5097
|
function resolveMigrationDir(config) {
|
|
5213
5098
|
const workspaceRoot = config.workspaceRoot || process.cwd();
|
|
5214
|
-
if (
|
|
5099
|
+
if (path7__namespace.isAbsolute(config.migrationDir)) {
|
|
5215
5100
|
return config.migrationDir;
|
|
5216
5101
|
}
|
|
5217
|
-
return
|
|
5102
|
+
return path7__namespace.join(workspaceRoot, config.migrationDir);
|
|
5218
5103
|
}
|
|
5219
5104
|
function createMigrationFileStructure(upCode, downCode, config) {
|
|
5220
|
-
const mergedConfig = config ? mergeConfig3(config) :
|
|
5105
|
+
const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
|
|
5221
5106
|
let template = mergedConfig.template;
|
|
5222
5107
|
template = template.replace("{{TYPES_PATH}}", () => mergedConfig.typesPath);
|
|
5223
5108
|
template = template.replace("{{UP_CODE}}", () => upCode);
|
|
@@ -5252,7 +5137,7 @@ function writeMigrationFile(migrationDir, filename, content) {
|
|
|
5252
5137
|
);
|
|
5253
5138
|
}
|
|
5254
5139
|
}
|
|
5255
|
-
const filePath =
|
|
5140
|
+
const filePath = path7__namespace.join(migrationDir, filename);
|
|
5256
5141
|
fs10__namespace.writeFileSync(filePath, content, "utf-8");
|
|
5257
5142
|
return filePath;
|
|
5258
5143
|
} catch (error) {
|
|
@@ -5260,7 +5145,7 @@ function writeMigrationFile(migrationDir, filename, content) {
|
|
|
5260
5145
|
throw error;
|
|
5261
5146
|
}
|
|
5262
5147
|
const fsError = error;
|
|
5263
|
-
const filePath =
|
|
5148
|
+
const filePath = path7__namespace.join(migrationDir, filename);
|
|
5264
5149
|
if (fsError.code === "EACCES" || fsError.code === "EPERM") {
|
|
5265
5150
|
throw new FileSystemError(
|
|
5266
5151
|
`Permission denied writing migration file. Check file and directory permissions.`,
|
|
@@ -6027,7 +5912,7 @@ function planMigrations(diff, config) {
|
|
|
6027
5912
|
const planned = [];
|
|
6028
5913
|
let existingFiles = [];
|
|
6029
5914
|
if (!normalizedConfig.force && fs10__namespace.existsSync(migrationDir)) {
|
|
6030
|
-
existingFiles = fs10__namespace.readdirSync(migrationDir).filter((f) => f.endsWith(".js") || f.endsWith(".ts")).map((f) => fs10__namespace.readFileSync(
|
|
5915
|
+
existingFiles = fs10__namespace.readdirSync(migrationDir).filter((f) => f.endsWith(".js") || f.endsWith(".ts")).map((f) => fs10__namespace.readFileSync(path7__namespace.join(migrationDir, f), "utf-8"));
|
|
6031
5916
|
}
|
|
6032
5917
|
for (const operation of operations) {
|
|
6033
5918
|
const upCode = generateOperationUpMigration(operation, collectionIdMap);
|
|
@@ -6784,154 +6669,6 @@ function toPhaseError(error, fallbackPhase) {
|
|
|
6784
6669
|
}
|
|
6785
6670
|
return { phase: fallbackPhase, message: error instanceof Error ? error.message : String(error) };
|
|
6786
6671
|
}
|
|
6787
|
-
|
|
6788
|
-
// src/migration/validation.ts
|
|
6789
|
-
function detectCollectionDeletions(diff) {
|
|
6790
|
-
const changes = [];
|
|
6791
|
-
for (const collection of diff.collectionsToDelete) {
|
|
6792
|
-
if (collection.type === "view") {
|
|
6793
|
-
continue;
|
|
6794
|
-
}
|
|
6795
|
-
changes.push({
|
|
6796
|
-
type: "collection_deletion" /* COLLECTION_DELETION */,
|
|
6797
|
-
description: `Delete collection: ${collection.name}`,
|
|
6798
|
-
collection: collection.name,
|
|
6799
|
-
severity: "high",
|
|
6800
|
-
warning: `All data in the "${collection.name}" collection will be permanently deleted.`
|
|
6801
|
-
});
|
|
6802
|
-
}
|
|
6803
|
-
return changes;
|
|
6804
|
-
}
|
|
6805
|
-
function detectFieldDeletions(diff) {
|
|
6806
|
-
const changes = [];
|
|
6807
|
-
for (const modification of diff.collectionsToModify) {
|
|
6808
|
-
for (const field of modification.fieldsToRemove) {
|
|
6809
|
-
changes.push({
|
|
6810
|
-
type: "field_deletion" /* FIELD_DELETION */,
|
|
6811
|
-
description: `Delete field: ${modification.collection}.${field.name}`,
|
|
6812
|
-
collection: modification.collection,
|
|
6813
|
-
field: field.name,
|
|
6814
|
-
severity: "high",
|
|
6815
|
-
warning: `All data in the "${field.name}" field of "${modification.collection}" will be permanently deleted.`
|
|
6816
|
-
});
|
|
6817
|
-
}
|
|
6818
|
-
}
|
|
6819
|
-
return changes;
|
|
6820
|
-
}
|
|
6821
|
-
function detectFieldTypeChanges(diff) {
|
|
6822
|
-
const changes = [];
|
|
6823
|
-
for (const modification of diff.collectionsToModify) {
|
|
6824
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
6825
|
-
const typeChange = fieldMod.changes.find((c) => c.property === "type");
|
|
6826
|
-
if (typeChange) {
|
|
6827
|
-
changes.push({
|
|
6828
|
-
type: "field_type_change" /* FIELD_TYPE_CHANGE */,
|
|
6829
|
-
description: `Change field type: ${modification.collection}.${fieldMod.fieldName}`,
|
|
6830
|
-
collection: modification.collection,
|
|
6831
|
-
field: fieldMod.fieldName,
|
|
6832
|
-
details: {
|
|
6833
|
-
oldValue: typeChange.oldValue,
|
|
6834
|
-
newValue: typeChange.newValue
|
|
6835
|
-
},
|
|
6836
|
-
severity: "high",
|
|
6837
|
-
warning: `Changing field type from "${typeChange.oldValue}" to "${typeChange.newValue}" may cause data loss or conversion errors.`
|
|
6838
|
-
});
|
|
6839
|
-
}
|
|
6840
|
-
}
|
|
6841
|
-
}
|
|
6842
|
-
return changes;
|
|
6843
|
-
}
|
|
6844
|
-
function detectFieldRequiredChanges(diff) {
|
|
6845
|
-
const changes = [];
|
|
6846
|
-
for (const modification of diff.collectionsToModify) {
|
|
6847
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
6848
|
-
const requiredChange = fieldMod.changes.find(
|
|
6849
|
-
(c) => c.property === "required" && c.newValue === true && c.oldValue === false
|
|
6850
|
-
);
|
|
6851
|
-
if (requiredChange) {
|
|
6852
|
-
changes.push({
|
|
6853
|
-
type: "field_required_change" /* FIELD_REQUIRED_CHANGE */,
|
|
6854
|
-
description: `Make field required: ${modification.collection}.${fieldMod.fieldName}`,
|
|
6855
|
-
collection: modification.collection,
|
|
6856
|
-
field: fieldMod.fieldName,
|
|
6857
|
-
details: {
|
|
6858
|
-
oldValue: false,
|
|
6859
|
-
newValue: true
|
|
6860
|
-
},
|
|
6861
|
-
severity: "medium",
|
|
6862
|
-
warning: `Making "${fieldMod.fieldName}" required may cause issues with existing records that have null/empty values.`
|
|
6863
|
-
});
|
|
6864
|
-
}
|
|
6865
|
-
}
|
|
6866
|
-
}
|
|
6867
|
-
return changes;
|
|
6868
|
-
}
|
|
6869
|
-
function detectDestructiveChanges(diff) {
|
|
6870
|
-
const changes = [];
|
|
6871
|
-
changes.push(...detectCollectionDeletions(diff));
|
|
6872
|
-
changes.push(...detectFieldDeletions(diff));
|
|
6873
|
-
changes.push(...detectFieldTypeChanges(diff));
|
|
6874
|
-
changes.push(...detectFieldRequiredChanges(diff));
|
|
6875
|
-
return changes;
|
|
6876
|
-
}
|
|
6877
|
-
function formatDestructiveChanges(changes) {
|
|
6878
|
-
if (changes.length === 0) {
|
|
6879
|
-
return "No destructive changes detected.";
|
|
6880
|
-
}
|
|
6881
|
-
const lines = [];
|
|
6882
|
-
const highSeverity = changes.filter((c) => c.severity === "high");
|
|
6883
|
-
const mediumSeverity = changes.filter((c) => c.severity === "medium");
|
|
6884
|
-
const lowSeverity = changes.filter((c) => c.severity === "low");
|
|
6885
|
-
if (highSeverity.length > 0) {
|
|
6886
|
-
lines.push("\u{1F534} HIGH SEVERITY CHANGES (Data Loss Risk):");
|
|
6887
|
-
lines.push("");
|
|
6888
|
-
for (const change of highSeverity) {
|
|
6889
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6890
|
-
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
6891
|
-
if (change.details) {
|
|
6892
|
-
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
6893
|
-
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
6894
|
-
}
|
|
6895
|
-
}
|
|
6896
|
-
lines.push("");
|
|
6897
|
-
}
|
|
6898
|
-
}
|
|
6899
|
-
if (mediumSeverity.length > 0) {
|
|
6900
|
-
lines.push("\u{1F7E1} MEDIUM SEVERITY CHANGES (Potential Issues):");
|
|
6901
|
-
lines.push("");
|
|
6902
|
-
for (const change of mediumSeverity) {
|
|
6903
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6904
|
-
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
6905
|
-
if (change.details) {
|
|
6906
|
-
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
6907
|
-
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
6908
|
-
}
|
|
6909
|
-
}
|
|
6910
|
-
lines.push("");
|
|
6911
|
-
}
|
|
6912
|
-
}
|
|
6913
|
-
if (lowSeverity.length > 0) {
|
|
6914
|
-
lines.push("\u{1F7E2} LOW SEVERITY CHANGES:");
|
|
6915
|
-
lines.push("");
|
|
6916
|
-
for (const change of lowSeverity) {
|
|
6917
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6918
|
-
lines.push(` \u2139\uFE0F ${change.warning}`);
|
|
6919
|
-
lines.push("");
|
|
6920
|
-
}
|
|
6921
|
-
}
|
|
6922
|
-
return lines.join("\n");
|
|
6923
|
-
}
|
|
6924
|
-
function summarizeDestructiveChanges(changes) {
|
|
6925
|
-
return {
|
|
6926
|
-
total: changes.length,
|
|
6927
|
-
high: changes.filter((c) => c.severity === "high").length,
|
|
6928
|
-
medium: changes.filter((c) => c.severity === "medium").length,
|
|
6929
|
-
low: changes.filter((c) => c.severity === "low").length
|
|
6930
|
-
};
|
|
6931
|
-
}
|
|
6932
|
-
function requiresForceFlag(changes) {
|
|
6933
|
-
return changes.some((c) => c.severity === "high" || c.severity === "medium");
|
|
6934
|
-
}
|
|
6935
6672
|
var CONFIG_FILE_NAMES = [
|
|
6936
6673
|
"pocketbase-migrate.config.js",
|
|
6937
6674
|
"pocketbase-migrate.config.mjs",
|
|
@@ -6940,7 +6677,7 @@ var CONFIG_FILE_NAMES = [
|
|
|
6940
6677
|
"migrate.config.mjs",
|
|
6941
6678
|
"migrate.config.json"
|
|
6942
6679
|
];
|
|
6943
|
-
var
|
|
6680
|
+
var DEFAULT_CONFIG4 = {
|
|
6944
6681
|
schema: {
|
|
6945
6682
|
directory: "src/schema",
|
|
6946
6683
|
exclude: ["base.ts", "index.ts", "permissions.ts", "permission-templates.ts"]
|
|
@@ -6961,7 +6698,7 @@ var DEFAULT_CONFIG5 = {
|
|
|
6961
6698
|
};
|
|
6962
6699
|
function findConfigFile(directory) {
|
|
6963
6700
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
6964
|
-
const filePath =
|
|
6701
|
+
const filePath = path7__namespace.join(directory, fileName);
|
|
6965
6702
|
if (fs10__namespace.existsSync(filePath)) {
|
|
6966
6703
|
return filePath;
|
|
6967
6704
|
}
|
|
@@ -7002,7 +6739,7 @@ async function loadConfigFile(configPath) {
|
|
|
7002
6739
|
if (!fs10__namespace.existsSync(configPath)) {
|
|
7003
6740
|
return null;
|
|
7004
6741
|
}
|
|
7005
|
-
const ext =
|
|
6742
|
+
const ext = path7__namespace.extname(configPath).toLowerCase();
|
|
7006
6743
|
if (ext === ".json") {
|
|
7007
6744
|
return loadJsonConfig(configPath);
|
|
7008
6745
|
} else if (ext === ".js" || ext === ".mjs") {
|
|
@@ -7097,8 +6834,8 @@ function validateConfig(config, configPath) {
|
|
|
7097
6834
|
}
|
|
7098
6835
|
const cwd = process.cwd();
|
|
7099
6836
|
const possiblePaths = [
|
|
7100
|
-
|
|
7101
|
-
|
|
6837
|
+
path7__namespace.resolve(cwd, config.schema.directory),
|
|
6838
|
+
path7__namespace.resolve(cwd, "shared", config.schema.directory)
|
|
7102
6839
|
];
|
|
7103
6840
|
const schemaDir = possiblePaths.find((p) => fs10__namespace.existsSync(p));
|
|
7104
6841
|
if (!schemaDir) {
|
|
@@ -7108,17 +6845,17 @@ function validateConfig(config, configPath) {
|
|
|
7108
6845
|
}
|
|
7109
6846
|
}
|
|
7110
6847
|
async function loadConfig(options = {}) {
|
|
7111
|
-
let config = { ...
|
|
6848
|
+
let config = { ...DEFAULT_CONFIG4 };
|
|
7112
6849
|
let configFilePath;
|
|
7113
6850
|
const cwd = process.cwd();
|
|
7114
6851
|
if (options.config) {
|
|
7115
|
-
const explicitPath =
|
|
6852
|
+
const explicitPath = path7__namespace.resolve(cwd, options.config);
|
|
7116
6853
|
if (!fs10__namespace.existsSync(explicitPath)) {
|
|
7117
6854
|
throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
|
|
7118
6855
|
}
|
|
7119
6856
|
configFilePath = explicitPath;
|
|
7120
6857
|
} else {
|
|
7121
|
-
const searchDirs = [cwd,
|
|
6858
|
+
const searchDirs = [cwd, path7__namespace.join(cwd, "shared")];
|
|
7122
6859
|
for (const dir of searchDirs) {
|
|
7123
6860
|
if (fs10__namespace.existsSync(dir)) {
|
|
7124
6861
|
const found = findConfigFile(dir);
|
|
@@ -7149,23 +6886,23 @@ async function loadConfig(options = {}) {
|
|
|
7149
6886
|
function getSchemaDirectory(config) {
|
|
7150
6887
|
const cwd = process.cwd();
|
|
7151
6888
|
const possiblePaths = [
|
|
7152
|
-
|
|
7153
|
-
|
|
6889
|
+
path7__namespace.resolve(cwd, config.schema.directory),
|
|
6890
|
+
path7__namespace.resolve(cwd, "shared", config.schema.directory)
|
|
7154
6891
|
];
|
|
7155
6892
|
return possiblePaths.find((p) => fs10__namespace.existsSync(p)) || possiblePaths[0];
|
|
7156
6893
|
}
|
|
7157
6894
|
function getMigrationsDirectory(config) {
|
|
7158
6895
|
const cwd = process.cwd();
|
|
7159
6896
|
const possiblePaths = [
|
|
7160
|
-
|
|
7161
|
-
|
|
6897
|
+
path7__namespace.resolve(cwd, config.migrations.directory),
|
|
6898
|
+
path7__namespace.resolve(cwd, "shared", config.migrations.directory)
|
|
7162
6899
|
];
|
|
7163
6900
|
return possiblePaths.find((p) => fs10__namespace.existsSync(p)) || possiblePaths[0];
|
|
7164
6901
|
}
|
|
7165
6902
|
function getDataDirectory(config) {
|
|
7166
6903
|
const configured = config.migrations.dataDirectory.trim();
|
|
7167
6904
|
if (configured !== "") {
|
|
7168
|
-
return
|
|
6905
|
+
return path7__namespace.resolve(process.cwd(), configured);
|
|
7169
6906
|
}
|
|
7170
6907
|
return defaultDataDirectory(getMigrationsDirectory(config));
|
|
7171
6908
|
}
|
|
@@ -7464,8 +7201,7 @@ async function executeGenerate(filters, options) {
|
|
|
7464
7201
|
logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
|
|
7465
7202
|
logInfo("Loading previous snapshot...");
|
|
7466
7203
|
const previousSnapshot = loadSnapshotWithMigrations({
|
|
7467
|
-
migrationsPath: migrationsDir
|
|
7468
|
-
workspaceRoot: process.cwd()
|
|
7204
|
+
migrationsPath: migrationsDir
|
|
7469
7205
|
});
|
|
7470
7206
|
if (!previousSnapshot) {
|
|
7471
7207
|
logInfo("No previous snapshot found - treating as empty database (first-time generation)");
|
|
@@ -7518,7 +7254,7 @@ async function executeGenerate(filters, options) {
|
|
|
7518
7254
|
}
|
|
7519
7255
|
const migrationPaths = writePlannedMigrations(planned, migrationsDir);
|
|
7520
7256
|
if (migrationPaths.length === 1) {
|
|
7521
|
-
logSuccess(`Migration file created: ${
|
|
7257
|
+
logSuccess(`Migration file created: ${path7__namespace.basename(migrationPaths[0])}`);
|
|
7522
7258
|
} else {
|
|
7523
7259
|
logSuccess(`Created ${migrationPaths.length} migration files`);
|
|
7524
7260
|
}
|
|
@@ -7544,7 +7280,7 @@ async function executeGenerate(filters, options) {
|
|
|
7544
7280
|
logInfo("Suggestions:");
|
|
7545
7281
|
console.log(" \u2022 Make sure your schema files are valid Zod schemas");
|
|
7546
7282
|
console.log(' \u2022 Run "yarn build" in the shared workspace to compile TypeScript files');
|
|
7547
|
-
console.log(
|
|
7283
|
+
console.log(" \u2022 Check that each schema file exports a defineCollection()/defineView() result (default export preferred)");
|
|
7548
7284
|
} else if (error instanceof SnapshotError) {
|
|
7549
7285
|
logError("Snapshot Error");
|
|
7550
7286
|
console.error();
|
|
@@ -7894,13 +7630,13 @@ async function executeGenerateTypes(options) {
|
|
|
7894
7630
|
const generator = new TypeGenerator(currentSchema);
|
|
7895
7631
|
const output = await withProgress("Generating TypeScript definitions...", () => Promise.resolve(generator.generate()));
|
|
7896
7632
|
const outputPath = options.output || config.typeGen.outPath;
|
|
7897
|
-
const resolvedPath =
|
|
7633
|
+
const resolvedPath = path7__namespace.resolve(process.cwd(), outputPath);
|
|
7898
7634
|
fs10__namespace.writeFileSync(resolvedPath, output);
|
|
7899
7635
|
logSuccess(`Types generated successfully at: ${outputPath}`);
|
|
7900
7636
|
logSection("\u2705 Next Steps");
|
|
7901
7637
|
console.log();
|
|
7902
7638
|
console.log(" 1. Import types in your application:");
|
|
7903
|
-
console.log(` import { TypedPocketBase } from "./${
|
|
7639
|
+
console.log(` import { TypedPocketBase } from "./${path7__namespace.basename(outputPath).replace(/\.ts$/, "")}";`);
|
|
7904
7640
|
console.log();
|
|
7905
7641
|
} catch (error) {
|
|
7906
7642
|
logError(`Failed to generate types: ${error}`);
|
|
@@ -7922,14 +7658,14 @@ function collectExecutionWarnings(files) {
|
|
|
7922
7658
|
const result = executeMigrationFile(file, store);
|
|
7923
7659
|
byFile.set(file, result.warnings);
|
|
7924
7660
|
} catch (error) {
|
|
7925
|
-
logDebug(`Skipping execution of ${
|
|
7661
|
+
logDebug(`Skipping execution of ${path7__namespace.basename(file)}: ${error}`);
|
|
7926
7662
|
byFile.set(file, []);
|
|
7927
7663
|
}
|
|
7928
7664
|
}
|
|
7929
7665
|
return byFile;
|
|
7930
7666
|
}
|
|
7931
7667
|
function reportResult(result) {
|
|
7932
|
-
const name =
|
|
7668
|
+
const name = path7__namespace.basename(result.file);
|
|
7933
7669
|
if (result.findings.length === 0) {
|
|
7934
7670
|
logDebug(`\u2713 ${name}`);
|
|
7935
7671
|
return true;
|
|
@@ -7955,7 +7691,7 @@ async function executeLint(files, options) {
|
|
|
7955
7691
|
}
|
|
7956
7692
|
const config = await loadConfig(options);
|
|
7957
7693
|
const migrationsDir = getMigrationsDirectory(config);
|
|
7958
|
-
const targets = files.length > 0 ? files.map((file) =>
|
|
7694
|
+
const targets = files.length > 0 ? files.map((file) => path7__namespace.resolve(process.cwd(), file)) : discoverMigrations(migrationsDir).map((migration) => migration.path);
|
|
7959
7695
|
logSection("\u{1F50E} Checking goja Compatibility");
|
|
7960
7696
|
console.log();
|
|
7961
7697
|
if (targets.length === 0) {
|
|
@@ -8064,17 +7800,15 @@ function lookupAppliedMigrations(migrationsDir, dataPath) {
|
|
|
8064
7800
|
throw error;
|
|
8065
7801
|
}
|
|
8066
7802
|
}
|
|
8067
|
-
function loadStatusBaselines(currentSchema, migrationsDir,
|
|
7803
|
+
function loadStatusBaselines(currentSchema, migrationsDir, applied) {
|
|
8068
7804
|
const previousSnapshot = loadSnapshotWithMigrations({
|
|
8069
|
-
migrationsPath: migrationsDir
|
|
8070
|
-
workspaceRoot
|
|
7805
|
+
migrationsPath: migrationsDir
|
|
8071
7806
|
});
|
|
8072
7807
|
if (applied?.status !== "found" || applied.plan.inSync) {
|
|
8073
7808
|
return { previousSnapshot, appliedDiff: null };
|
|
8074
7809
|
}
|
|
8075
7810
|
const appliedSnapshot = loadSnapshotWithMigrations({
|
|
8076
7811
|
migrationsPath: migrationsDir,
|
|
8077
|
-
workspaceRoot,
|
|
8078
7812
|
appliedMigrations: applied.applied
|
|
8079
7813
|
});
|
|
8080
7814
|
return { previousSnapshot, appliedDiff: compare2(currentSchema, appliedSnapshot) };
|
|
@@ -8221,7 +7955,7 @@ async function executeStatus(options) {
|
|
|
8221
7955
|
process.exit(1);
|
|
8222
7956
|
}
|
|
8223
7957
|
logInfo("Loading previous snapshot...");
|
|
8224
|
-
const { previousSnapshot, appliedDiff } = loadStatusBaselines(currentSchema, migrationsDir,
|
|
7958
|
+
const { previousSnapshot, appliedDiff } = loadStatusBaselines(currentSchema, migrationsDir, applied);
|
|
8225
7959
|
if (applied?.status === "found" && !isJsonMode) {
|
|
8226
7960
|
const inSync = reportAppliedMigrations(applied.plan);
|
|
8227
7961
|
if (!inSync) {
|
|
@@ -8323,7 +8057,7 @@ async function executeStatus(options) {
|
|
|
8323
8057
|
console.error();
|
|
8324
8058
|
logInfo("Suggestions:");
|
|
8325
8059
|
console.log(" \u2022 Make sure your schema files are valid Zod schemas");
|
|
8326
|
-
console.log(
|
|
8060
|
+
console.log(" \u2022 Check that each schema file exports a defineCollection()/defineView() result (default export preferred)");
|
|
8327
8061
|
} else if (error instanceof SnapshotError) {
|
|
8328
8062
|
logError("Snapshot Error");
|
|
8329
8063
|
console.error();
|
|
@@ -8369,8 +8103,8 @@ Examples:
|
|
|
8369
8103
|
function getVersion() {
|
|
8370
8104
|
try {
|
|
8371
8105
|
const __filename2 = url.fileURLToPath(importMetaUrl);
|
|
8372
|
-
const __dirname =
|
|
8373
|
-
const packageJsonPath =
|
|
8106
|
+
const __dirname = path7.dirname(__filename2);
|
|
8107
|
+
const packageJsonPath = path7.join(__dirname, "../../package.json");
|
|
8374
8108
|
const packageJson = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
|
|
8375
8109
|
return packageJson.version || "0.0.0";
|
|
8376
8110
|
} catch {
|