pocketbase-zod-schema 1.0.0 → 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 +16 -0
- package/README.md +28 -69
- package/dist/cli/migrate.cjs +548 -674
- package/dist/cli/migrate.cjs.map +1 -1
- package/dist/cli/migrate.js +545 -671
- package/dist/cli/migrate.js.map +1 -1
- package/dist/index.cjs +37 -555
- 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 +38 -540
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +674 -2614
- 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 +673 -2481
- package/dist/server.js.map +1 -1
- package/package.json +2 -64
- package/dist/cli/index.cjs +0 -7873
- 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 -7819
- 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-B96iGprI.d.cts +0 -480
- package/dist/fields-B96iGprI.d.ts +0 -480
- package/dist/migration/analyzer.cjs +0 -1725
- 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 -1684
- package/dist/migration/analyzer.js.map +0 -1
- package/dist/migration/diff.cjs +0 -943
- 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 -911
- package/dist/migration/diff.js.map +0 -1
- package/dist/migration/engine.cjs +0 -3263
- 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 -3173
- package/dist/migration/engine.js.map +0 -1
- package/dist/migration/generator.cjs +0 -1596
- 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 -1529
- package/dist/migration/generator.js.map +0 -1
- package/dist/migration/index.cjs +0 -8190
- 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 -8015
- package/dist/migration/index.js.map +0 -1
- package/dist/migration/snapshot.cjs +0 -2814
- 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 -2779
- package/dist/migration/snapshot.js.map +0 -1
- package/dist/migration/utils/index.cjs +0 -891
- 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 -854
- 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 -715
- 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 -674
- package/dist/schema.js.map +0 -1
- package/dist/type-mapper-BSPTj8t5.d.cts +0 -216
- package/dist/type-mapper-D31WYDee.d.ts +0 -216
- package/dist/types-B_MLb1Ob.d.ts +0 -475
- package/dist/types-CnzfX6JH.d.cts +0 -148
- package/dist/types-Do3jyFBm.d.ts +0 -148
- package/dist/types-upZBVMfb.d.cts +0 -475
- package/dist/verify-BF72n2df.d.ts +0 -402
- package/dist/verify-DTzYpiXX.d.cts +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;
|
|
@@ -501,14 +488,15 @@ var CollectionIdRegistry = class {
|
|
|
501
488
|
};
|
|
502
489
|
|
|
503
490
|
// src/migration/rule-validator.ts
|
|
491
|
+
var BACK_RELATION_SEGMENT = /^[a-zA-Z_][a-zA-Z0-9_]*_via_[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
504
492
|
var RuleValidator = class {
|
|
505
493
|
fields;
|
|
506
494
|
collectionName;
|
|
507
495
|
isAuthCollection;
|
|
508
|
-
constructor(collectionName, fields,
|
|
496
|
+
constructor(collectionName, fields, isAuthCollection = false) {
|
|
509
497
|
this.collectionName = collectionName;
|
|
510
498
|
this.fields = new Map(fields.map((f) => [f.name, f]));
|
|
511
|
-
this.isAuthCollection =
|
|
499
|
+
this.isAuthCollection = isAuthCollection;
|
|
512
500
|
this.addSystemFields();
|
|
513
501
|
}
|
|
514
502
|
/**
|
|
@@ -576,7 +564,7 @@ var RuleValidator = class {
|
|
|
576
564
|
/**
|
|
577
565
|
* Extract field references from expression
|
|
578
566
|
*
|
|
579
|
-
* Matches field names that are not @request references.
|
|
567
|
+
* Matches field names that are not @request or @collection references.
|
|
580
568
|
* Handles dot notation for relations: user.email, post.author.name
|
|
581
569
|
*
|
|
582
570
|
* @param expression - The rule expression
|
|
@@ -586,6 +574,7 @@ var RuleValidator = class {
|
|
|
586
574
|
const refs = [];
|
|
587
575
|
let cleaned = expression2.replace(/"[^"]*"/g, '""').replace(/'[^']*'/g, "''");
|
|
588
576
|
cleaned = cleaned.replace(/@request\.[a-zA-Z_][a-zA-Z0-9_.]*/g, "");
|
|
577
|
+
cleaned = cleaned.replace(/@collection\.[a-zA-Z_][a-zA-Z0-9_.]*/g, "");
|
|
589
578
|
const fieldPattern = /(?:^|[^@\w])([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)(?=[^a-zA-Z0-9_.]|$)/g;
|
|
590
579
|
let match;
|
|
591
580
|
while ((match = fieldPattern.exec(cleaned)) !== null) {
|
|
@@ -606,12 +595,26 @@ var RuleValidator = class {
|
|
|
606
595
|
const keywords = ["true", "false", "null", "AND", "OR", "NOT", "LIKE", "IN"];
|
|
607
596
|
return keywords.includes(word.toUpperCase());
|
|
608
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Check whether a path segment is a back-relation rather than a field
|
|
600
|
+
*
|
|
601
|
+
* A declared field wins: a collection may legitimately contain a field
|
|
602
|
+
* literally named `foo_via_bar`, and it should be validated as one.
|
|
603
|
+
*
|
|
604
|
+
* @param segment - A single dot-separated path segment
|
|
605
|
+
* @returns True if the segment should be treated as a back-relation
|
|
606
|
+
*/
|
|
607
|
+
isBackRelationSegment(segment) {
|
|
608
|
+
return !this.fields.has(segment) && BACK_RELATION_SEGMENT.test(segment);
|
|
609
|
+
}
|
|
609
610
|
/**
|
|
610
611
|
* Validate a field reference exists in schema
|
|
611
612
|
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
613
|
+
* Only the root of the path is checked — the validator holds a single
|
|
614
|
+
* collection's fields, so anything past the first hop belongs to another
|
|
615
|
+
* collection and is accepted as-is. A root back-relation
|
|
616
|
+
* (`Members_via_WorkspaceRef.UserRef`) has no local field to check against
|
|
617
|
+
* either, so it is accepted too; PocketBase resolves it at runtime.
|
|
615
618
|
*
|
|
616
619
|
* @param fieldRef - The field reference to validate (e.g., "user" or "user.email")
|
|
617
620
|
* @param result - The validation result to update
|
|
@@ -619,6 +622,9 @@ var RuleValidator = class {
|
|
|
619
622
|
validateFieldReference(fieldRef, result) {
|
|
620
623
|
const parts = fieldRef.split(".");
|
|
621
624
|
const rootField = parts[0];
|
|
625
|
+
if (this.isBackRelationSegment(rootField)) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
622
628
|
if (!this.fields.has(rootField)) {
|
|
623
629
|
result.errors.push(`Field '${rootField}' does not exist in collection '${this.collectionName}'`);
|
|
624
630
|
result.valid = false;
|
|
@@ -629,10 +635,6 @@ var RuleValidator = class {
|
|
|
629
635
|
if (field.type !== "relation") {
|
|
630
636
|
result.errors.push(`Field '${rootField}' is not a relation field, cannot access nested property '${parts[1]}'`);
|
|
631
637
|
result.valid = false;
|
|
632
|
-
} else {
|
|
633
|
-
result.warnings.push(
|
|
634
|
-
`Nested field reference '${fieldRef}' - ensure target collection has field '${parts.slice(1).join(".")}'`
|
|
635
|
-
);
|
|
636
638
|
}
|
|
637
639
|
}
|
|
638
640
|
}
|
|
@@ -790,11 +792,11 @@ var PermissionAnalyzer = class {
|
|
|
790
792
|
* }
|
|
791
793
|
* ```
|
|
792
794
|
*/
|
|
793
|
-
validatePermissions(collectionName, permissions, fields,
|
|
794
|
-
const validator = new RuleValidator(collectionName, fields,
|
|
795
|
+
validatePermissions(collectionName, permissions, fields, isAuthCollection = false) {
|
|
796
|
+
const validator = new RuleValidator(collectionName, fields, isAuthCollection);
|
|
795
797
|
const results = /* @__PURE__ */ new Map();
|
|
796
798
|
const ruleTypes = ["listRule", "viewRule", "createRule", "updateRule", "deleteRule"];
|
|
797
|
-
if (
|
|
799
|
+
if (isAuthCollection) {
|
|
798
800
|
ruleTypes.push("manageRule");
|
|
799
801
|
}
|
|
800
802
|
for (const ruleType of ruleTypes) {
|
|
@@ -847,146 +849,22 @@ var PermissionAnalyzer = class {
|
|
|
847
849
|
};
|
|
848
850
|
}
|
|
849
851
|
};
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
mouse: "mice",
|
|
867
|
-
Mouse: "Mice",
|
|
868
|
-
goose: "geese",
|
|
869
|
-
Goose: "Geese",
|
|
870
|
-
// Words ending in -y
|
|
871
|
-
category: "categories",
|
|
872
|
-
Category: "Categories",
|
|
873
|
-
company: "companies",
|
|
874
|
-
Company: "Companies",
|
|
875
|
-
city: "cities",
|
|
876
|
-
City: "Cities",
|
|
877
|
-
country: "countries",
|
|
878
|
-
Country: "Countries",
|
|
879
|
-
story: "stories",
|
|
880
|
-
Story: "Stories",
|
|
881
|
-
party: "parties",
|
|
882
|
-
Party: "Parties",
|
|
883
|
-
family: "families",
|
|
884
|
-
Family: "Families",
|
|
885
|
-
activity: "activities",
|
|
886
|
-
Activity: "Activities",
|
|
887
|
-
priority: "priorities",
|
|
888
|
-
Priority: "Priorities",
|
|
889
|
-
// Words ending in -f or -fe
|
|
890
|
-
life: "lives",
|
|
891
|
-
Life: "Lives",
|
|
892
|
-
wife: "wives",
|
|
893
|
-
Wife: "Wives",
|
|
894
|
-
knife: "knives",
|
|
895
|
-
Knife: "Knives",
|
|
896
|
-
leaf: "leaves",
|
|
897
|
-
Leaf: "Leaves",
|
|
898
|
-
shelf: "shelves",
|
|
899
|
-
Shelf: "Shelves",
|
|
900
|
-
half: "halves",
|
|
901
|
-
Half: "Halves",
|
|
902
|
-
// Words ending in -is
|
|
903
|
-
analysis: "analyses",
|
|
904
|
-
Analysis: "Analyses",
|
|
905
|
-
basis: "bases",
|
|
906
|
-
Basis: "Bases",
|
|
907
|
-
crisis: "crises",
|
|
908
|
-
Crisis: "Crises",
|
|
909
|
-
thesis: "theses",
|
|
910
|
-
Thesis: "Theses",
|
|
911
|
-
// Words ending in -us
|
|
912
|
-
cactus: "cacti",
|
|
913
|
-
Cactus: "Cacti",
|
|
914
|
-
focus: "foci",
|
|
915
|
-
Focus: "Foci",
|
|
916
|
-
fungus: "fungi",
|
|
917
|
-
Fungus: "Fungi",
|
|
918
|
-
nucleus: "nuclei",
|
|
919
|
-
Nucleus: "Nuclei",
|
|
920
|
-
radius: "radii",
|
|
921
|
-
Radius: "Radii",
|
|
922
|
-
// Words ending in -on
|
|
923
|
-
phenomenon: "phenomena",
|
|
924
|
-
Phenomenon: "Phenomena",
|
|
925
|
-
criterion: "criteria",
|
|
926
|
-
Criterion: "Criteria",
|
|
927
|
-
// Words ending in -um
|
|
928
|
-
datum: "data",
|
|
929
|
-
Datum: "Data",
|
|
930
|
-
medium: "media",
|
|
931
|
-
Medium: "Media",
|
|
932
|
-
curriculum: "curricula",
|
|
933
|
-
Curriculum: "Curricula",
|
|
934
|
-
// Unchanged plurals
|
|
935
|
-
sheep: "sheep",
|
|
936
|
-
Sheep: "Sheep",
|
|
937
|
-
deer: "deer",
|
|
938
|
-
Deer: "Deer",
|
|
939
|
-
fish: "fish",
|
|
940
|
-
Fish: "Fish",
|
|
941
|
-
species: "species",
|
|
942
|
-
Species: "Species",
|
|
943
|
-
series: "series",
|
|
944
|
-
Series: "Series"
|
|
945
|
-
};
|
|
946
|
-
function pluralize(singular) {
|
|
947
|
-
if (SPECIAL_CASES[singular]) {
|
|
948
|
-
return SPECIAL_CASES[singular];
|
|
949
|
-
}
|
|
950
|
-
if (singular.length > 3 && singular.endsWith("s") && !singular.endsWith("ss")) {
|
|
951
|
-
return singular;
|
|
952
|
-
}
|
|
953
|
-
const lowerSingular = singular.toLowerCase();
|
|
954
|
-
let plural;
|
|
955
|
-
if (/(?:s|ss|sh|ch|x|z)$/.test(lowerSingular)) {
|
|
956
|
-
plural = singular + "es";
|
|
957
|
-
} else if (/[^aeiou]y$/.test(lowerSingular)) {
|
|
958
|
-
plural = singular.slice(0, -1) + "ies";
|
|
959
|
-
} else if (/[^aeiou]o$/.test(lowerSingular)) {
|
|
960
|
-
plural = singular + "es";
|
|
961
|
-
} else if (/fe?$/.test(lowerSingular)) {
|
|
962
|
-
if (lowerSingular.endsWith("fe")) {
|
|
963
|
-
plural = singular.slice(0, -2) + "ves";
|
|
964
|
-
} else {
|
|
965
|
-
plural = singular.slice(0, -1) + "ves";
|
|
966
|
-
}
|
|
967
|
-
} else {
|
|
968
|
-
plural = singular + "s";
|
|
969
|
-
}
|
|
970
|
-
return plural;
|
|
971
|
-
}
|
|
972
|
-
function toCollectionName(entityName) {
|
|
973
|
-
return pluralize(entityName);
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// src/migration/utils/relation-detector.ts
|
|
977
|
-
function unwrapType(zodType) {
|
|
978
|
-
let unwrappedType = zodType;
|
|
979
|
-
if (unwrappedType instanceof zod.z.ZodOptional) {
|
|
980
|
-
unwrappedType = unwrappedType.unwrap();
|
|
981
|
-
}
|
|
982
|
-
if (unwrappedType instanceof zod.z.ZodNullable) {
|
|
983
|
-
unwrappedType = unwrappedType.unwrap();
|
|
984
|
-
}
|
|
985
|
-
if (unwrappedType instanceof zod.z.ZodDefault) {
|
|
986
|
-
unwrappedType = unwrappedType.unwrap();
|
|
987
|
-
}
|
|
988
|
-
return unwrappedType;
|
|
989
|
-
}
|
|
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
|
+
];
|
|
990
868
|
function getChecks(zodType) {
|
|
991
869
|
const def = zodType.def ?? zodType._def;
|
|
992
870
|
return def?.checks ?? [];
|
|
@@ -999,94 +877,8 @@ function getJsonSchema(zodType) {
|
|
|
999
877
|
return null;
|
|
1000
878
|
}
|
|
1001
879
|
}
|
|
1002
|
-
function isSingleRelationField(fieldName, zodType) {
|
|
1003
|
-
const unwrappedType = unwrapType(zodType);
|
|
1004
|
-
if (!(unwrappedType instanceof zod.z.ZodString)) {
|
|
1005
|
-
return false;
|
|
1006
|
-
}
|
|
1007
|
-
const startsWithUppercase = /^[A-Z]/.test(fieldName);
|
|
1008
|
-
const commonStringFields = ["Title", "Name", "Description", "Content", "Summary", "Status", "Type"];
|
|
1009
|
-
const isCommonField = commonStringFields.includes(fieldName);
|
|
1010
|
-
return startsWithUppercase && !isCommonField;
|
|
1011
|
-
}
|
|
1012
|
-
function isMultipleRelationField(fieldName, zodType) {
|
|
1013
|
-
const unwrappedType = unwrapType(zodType);
|
|
1014
|
-
if (!(unwrappedType instanceof zod.z.ZodArray)) {
|
|
1015
|
-
return false;
|
|
1016
|
-
}
|
|
1017
|
-
const elementType = unwrappedType.element;
|
|
1018
|
-
if (!(elementType instanceof zod.z.ZodString)) {
|
|
1019
|
-
return false;
|
|
1020
|
-
}
|
|
1021
|
-
const hasUppercase = /[A-Z]/.test(fieldName);
|
|
1022
|
-
return hasUppercase;
|
|
1023
|
-
}
|
|
1024
|
-
function resolveTargetCollection(fieldName) {
|
|
1025
|
-
const matches = fieldName.match(/[A-Z][a-z]+/g);
|
|
1026
|
-
if (!matches || matches.length === 0) {
|
|
1027
|
-
return pluralize(fieldName);
|
|
1028
|
-
}
|
|
1029
|
-
const entityName = matches[matches.length - 1];
|
|
1030
|
-
return pluralize(entityName);
|
|
1031
|
-
}
|
|
1032
|
-
function isRelationField(fieldName, zodType) {
|
|
1033
|
-
return isSingleRelationField(fieldName, zodType) || isMultipleRelationField(fieldName, zodType);
|
|
1034
|
-
}
|
|
1035
|
-
function getMaxSelect(fieldName, zodType) {
|
|
1036
|
-
if (isSingleRelationField(fieldName, zodType)) {
|
|
1037
|
-
return 1;
|
|
1038
|
-
}
|
|
1039
|
-
if (isMultipleRelationField(fieldName, zodType)) {
|
|
1040
|
-
const unwrappedType = unwrapType(zodType);
|
|
1041
|
-
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1042
|
-
const checks = getChecks(unwrappedType);
|
|
1043
|
-
const maxCheck = checks.find((check) => check.kind === "max");
|
|
1044
|
-
if (maxCheck) {
|
|
1045
|
-
return maxCheck.value;
|
|
1046
|
-
}
|
|
1047
|
-
const schema = getJsonSchema(unwrappedType);
|
|
1048
|
-
if (schema && typeof schema.maxItems === "number") {
|
|
1049
|
-
return schema.maxItems;
|
|
1050
|
-
}
|
|
1051
|
-
return 999;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
return 1;
|
|
1055
|
-
}
|
|
1056
|
-
function getMinSelect(fieldName, zodType) {
|
|
1057
|
-
if (isSingleRelationField(fieldName, zodType)) {
|
|
1058
|
-
return 0;
|
|
1059
|
-
}
|
|
1060
|
-
if (isMultipleRelationField(fieldName, zodType)) {
|
|
1061
|
-
const unwrappedType = unwrapType(zodType);
|
|
1062
|
-
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1063
|
-
const checks = getChecks(unwrappedType);
|
|
1064
|
-
const minCheck = checks.find((check) => check.kind === "min");
|
|
1065
|
-
if (minCheck) {
|
|
1066
|
-
return minCheck.value;
|
|
1067
|
-
}
|
|
1068
|
-
const schema = getJsonSchema(unwrappedType);
|
|
1069
|
-
if (schema && typeof schema.minItems === "number") {
|
|
1070
|
-
return schema.minItems;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
return 0;
|
|
1075
|
-
}
|
|
1076
|
-
function getChecks2(zodType) {
|
|
1077
|
-
const def = zodType.def ?? zodType._def;
|
|
1078
|
-
return def?.checks ?? [];
|
|
1079
|
-
}
|
|
1080
|
-
function getJsonSchema2(zodType) {
|
|
1081
|
-
try {
|
|
1082
|
-
const toJSONSchema = zodType.toJSONSchema;
|
|
1083
|
-
return typeof toJSONSchema === "function" ? toJSONSchema.call(zodType) : null;
|
|
1084
|
-
} catch {
|
|
1085
|
-
return null;
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
880
|
function mapZodStringType(zodType) {
|
|
1089
|
-
const checks =
|
|
881
|
+
const checks = getChecks(zodType);
|
|
1090
882
|
const hasEmail = checks.some((check) => check.kind === "email" || check.def?.format === "email");
|
|
1091
883
|
if (hasEmail) {
|
|
1092
884
|
return "email";
|
|
@@ -1112,14 +904,11 @@ function mapZodBooleanType(_zodType) {
|
|
|
1112
904
|
function mapZodEnumType(_zodType) {
|
|
1113
905
|
return "select";
|
|
1114
906
|
}
|
|
1115
|
-
function mapZodArrayType(zodType
|
|
907
|
+
function mapZodArrayType(zodType) {
|
|
1116
908
|
const elementType = zodType.element;
|
|
1117
909
|
if (elementType instanceof zod.z.ZodFile) {
|
|
1118
910
|
return "file";
|
|
1119
911
|
}
|
|
1120
|
-
if (elementType instanceof zod.z.ZodString) {
|
|
1121
|
-
return "relation";
|
|
1122
|
-
}
|
|
1123
912
|
return "json";
|
|
1124
913
|
}
|
|
1125
914
|
function mapZodDateType(_zodType) {
|
|
@@ -1128,7 +917,7 @@ function mapZodDateType(_zodType) {
|
|
|
1128
917
|
function mapZodRecordType(_zodType) {
|
|
1129
918
|
return "json";
|
|
1130
919
|
}
|
|
1131
|
-
function mapZodTypeToPocketBase(zodType
|
|
920
|
+
function mapZodTypeToPocketBase(zodType) {
|
|
1132
921
|
const unwrappedType = unwrapZodType(zodType);
|
|
1133
922
|
const metadata = extractFieldMetadata(unwrappedType.description);
|
|
1134
923
|
if (metadata && metadata.type) {
|
|
@@ -1158,7 +947,7 @@ function mapZodTypeToPocketBase(zodType, fieldName) {
|
|
|
1158
947
|
function extractFieldOptions(zodType) {
|
|
1159
948
|
const options = {};
|
|
1160
949
|
const unwrappedType = unwrapZodType(zodType);
|
|
1161
|
-
const checks =
|
|
950
|
+
const checks = getChecks(unwrappedType);
|
|
1162
951
|
if (unwrappedType instanceof zod.z.ZodString) {
|
|
1163
952
|
for (const check of checks) {
|
|
1164
953
|
if (check.kind === "min") {
|
|
@@ -1172,7 +961,7 @@ function extractFieldOptions(zodType) {
|
|
|
1172
961
|
}
|
|
1173
962
|
}
|
|
1174
963
|
if (options.min === void 0 || options.max === void 0 || options.pattern === void 0) {
|
|
1175
|
-
const schema =
|
|
964
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1176
965
|
if (schema) {
|
|
1177
966
|
if (options.min === void 0 && typeof schema.minLength === "number") {
|
|
1178
967
|
options.min = schema.minLength;
|
|
@@ -1195,7 +984,7 @@ function extractFieldOptions(zodType) {
|
|
|
1195
984
|
options.max = check.value;
|
|
1196
985
|
}
|
|
1197
986
|
}
|
|
1198
|
-
const schema =
|
|
987
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1199
988
|
if (schema) {
|
|
1200
989
|
if (options.min === void 0 && typeof schema.minimum === "number") {
|
|
1201
990
|
options.min = schema.minimum;
|
|
@@ -1213,7 +1002,7 @@ function extractFieldOptions(zodType) {
|
|
|
1213
1002
|
options.values = unwrappedType.options.map(String);
|
|
1214
1003
|
}
|
|
1215
1004
|
if (unwrappedType instanceof zod.z.ZodArray) {
|
|
1216
|
-
const arrayChecks =
|
|
1005
|
+
const arrayChecks = getChecks(unwrappedType);
|
|
1217
1006
|
for (const check of arrayChecks) {
|
|
1218
1007
|
if (check.kind === "min") {
|
|
1219
1008
|
options.minSelect = check.value;
|
|
@@ -1223,7 +1012,7 @@ function extractFieldOptions(zodType) {
|
|
|
1223
1012
|
}
|
|
1224
1013
|
}
|
|
1225
1014
|
if (options.minSelect === void 0 || options.maxSelect === void 0) {
|
|
1226
|
-
const schema =
|
|
1015
|
+
const schema = getJsonSchema(unwrappedType);
|
|
1227
1016
|
if (schema) {
|
|
1228
1017
|
if (options.minSelect === void 0 && typeof schema.minItems === "number") {
|
|
1229
1018
|
options.minSelect = schema.minItems;
|
|
@@ -1301,6 +1090,39 @@ function filterSupportedFieldOptions(fieldType, options) {
|
|
|
1301
1090
|
}
|
|
1302
1091
|
return Object.keys(filtered).length > 0 ? filtered : void 0;
|
|
1303
1092
|
}
|
|
1093
|
+
var OPTION_UNSET_VALUES = {
|
|
1094
|
+
pattern: "",
|
|
1095
|
+
autogeneratePattern: "",
|
|
1096
|
+
cost: 0,
|
|
1097
|
+
maxSize: 0,
|
|
1098
|
+
maxSelect: 0,
|
|
1099
|
+
minSelect: 0,
|
|
1100
|
+
primaryKey: false,
|
|
1101
|
+
onlyInt: false,
|
|
1102
|
+
noDecimal: false,
|
|
1103
|
+
convertURLs: false,
|
|
1104
|
+
protected: false,
|
|
1105
|
+
hidden: false,
|
|
1106
|
+
presentable: false,
|
|
1107
|
+
system: false,
|
|
1108
|
+
values: [],
|
|
1109
|
+
mimeTypes: [],
|
|
1110
|
+
thumbs: [],
|
|
1111
|
+
exceptDomains: [],
|
|
1112
|
+
onlyDomains: []
|
|
1113
|
+
};
|
|
1114
|
+
function getFieldOptionUnsetValue(fieldType, key) {
|
|
1115
|
+
if (key === "min" || key === "max") {
|
|
1116
|
+
if (fieldType === "number") {
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
if (fieldType === "date") {
|
|
1120
|
+
return "";
|
|
1121
|
+
}
|
|
1122
|
+
return 0;
|
|
1123
|
+
}
|
|
1124
|
+
return OPTION_UNSET_VALUES[key];
|
|
1125
|
+
}
|
|
1304
1126
|
function dedentSql(value) {
|
|
1305
1127
|
const lines = value.replace(/\r\n/g, "\n").split("\n");
|
|
1306
1128
|
while (lines.length > 0 && lines[0].trim() === "") {
|
|
@@ -1355,10 +1177,6 @@ function validateViewQuery(collectionName, viewQuery) {
|
|
|
1355
1177
|
);
|
|
1356
1178
|
}
|
|
1357
1179
|
}
|
|
1358
|
-
function getCollectionNameFromFile(filePath) {
|
|
1359
|
-
const filename = path8__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1360
|
-
return toCollectionName(filename);
|
|
1361
|
-
}
|
|
1362
1180
|
function extractCollectionNameFromSchema(zodSchema) {
|
|
1363
1181
|
if (!zodSchema.description) {
|
|
1364
1182
|
return null;
|
|
@@ -1398,42 +1216,42 @@ function extractViewQueryFromSchema(zodSchema) {
|
|
|
1398
1216
|
}
|
|
1399
1217
|
return null;
|
|
1400
1218
|
}
|
|
1401
|
-
function
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
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);
|
|
1406
1234
|
for (const [key, value] of Object.entries(module)) {
|
|
1407
|
-
if (key
|
|
1408
|
-
|
|
1409
|
-
if (patterns.includes("InputSchema") && key.endsWith("InputSchema")) {
|
|
1410
|
-
result.inputSchema = value;
|
|
1411
|
-
} else if (!result.schema) {
|
|
1412
|
-
if (patterns.includes("Collection") && key.endsWith("Collection")) {
|
|
1413
|
-
result.schema = value;
|
|
1414
|
-
} else if (patterns.includes("Schema") && key.endsWith("Schema") && !key.endsWith("InputSchema")) {
|
|
1415
|
-
result.schema = value;
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1235
|
+
if (key !== "default") {
|
|
1236
|
+
consider(key, value);
|
|
1418
1237
|
}
|
|
1419
1238
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
function selectSchemaForCollection(schemas) {
|
|
1423
|
-
if (schemas.schema) {
|
|
1424
|
-
return schemas.schema;
|
|
1239
|
+
if (candidates.length === 0) {
|
|
1240
|
+
return null;
|
|
1425
1241
|
}
|
|
1426
|
-
if (
|
|
1427
|
-
|
|
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
|
+
);
|
|
1428
1247
|
}
|
|
1429
|
-
return
|
|
1248
|
+
return candidates[0];
|
|
1430
1249
|
}
|
|
1431
1250
|
function extractFieldDefinitions(zodSchema, excludeFields) {
|
|
1432
1251
|
const shape = zodSchema.shape;
|
|
1433
1252
|
const fields = [];
|
|
1434
1253
|
const baseFields = ["id", "collectionId", "collectionName", "created", "updated", "expand"];
|
|
1435
|
-
const
|
|
1436
|
-
const allExclusions = /* @__PURE__ */ new Set([...baseFields, ...defaultExcludeFields, ...[]]);
|
|
1254
|
+
const allExclusions = /* @__PURE__ */ new Set([...baseFields, ...[]]);
|
|
1437
1255
|
for (const [fieldName, zodType] of Object.entries(shape)) {
|
|
1438
1256
|
if (!allExclusions.has(fieldName)) {
|
|
1439
1257
|
fields.push({ name: fieldName, zodType });
|
|
@@ -1646,16 +1464,15 @@ function generateFindCollectionCode(name, collectionIdMap) {
|
|
|
1646
1464
|
}
|
|
1647
1465
|
|
|
1648
1466
|
// src/migration/analyzer/converter.ts
|
|
1649
|
-
function isAuthCollection(fields) {
|
|
1650
|
-
const fieldNames = fields.map((f) => f.name.toLowerCase());
|
|
1651
|
-
const hasEmail = fieldNames.includes("email");
|
|
1652
|
-
const hasPassword = fieldNames.includes("password");
|
|
1653
|
-
return hasEmail && hasPassword;
|
|
1654
|
-
}
|
|
1655
1467
|
function buildFieldDefinition(fieldName, zodType) {
|
|
1656
1468
|
const unwrappedType = unwrapZodType(zodType);
|
|
1657
1469
|
const fieldMetadata = extractFieldMetadata(unwrappedType.description ?? zodType.description);
|
|
1658
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
|
+
}
|
|
1659
1476
|
let required2;
|
|
1660
1477
|
if (fieldMetadata.type === "number") {
|
|
1661
1478
|
if (fieldMetadata.options?.required !== void 0) {
|
|
@@ -1666,7 +1483,8 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1666
1483
|
} else {
|
|
1667
1484
|
required2 = isFieldRequired(zodType);
|
|
1668
1485
|
}
|
|
1669
|
-
const { required: _required, ...
|
|
1486
|
+
const { required: _required, ...metadataOptions } = fieldMetadata.options || {};
|
|
1487
|
+
const options2 = { ...extractChainedFieldOptions(fieldMetadata.type, unwrappedType), ...metadataOptions };
|
|
1670
1488
|
const fieldDef2 = {
|
|
1671
1489
|
name: fieldName,
|
|
1672
1490
|
id: generateFieldId(fieldMetadata.type, fieldName),
|
|
@@ -1689,6 +1507,26 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1689
1507
|
}
|
|
1690
1508
|
return fieldDef2;
|
|
1691
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
|
+
}
|
|
1692
1530
|
const fieldType = mapZodTypeToPocketBase(zodType);
|
|
1693
1531
|
const required = isFieldRequired(zodType);
|
|
1694
1532
|
const options = extractFieldOptions(zodType);
|
|
@@ -1700,35 +1538,6 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1700
1538
|
options,
|
|
1701
1539
|
zodType
|
|
1702
1540
|
};
|
|
1703
|
-
const relationMetadata = extractRelationMetadata(unwrappedType.description ?? zodType.description);
|
|
1704
|
-
if (relationMetadata) {
|
|
1705
|
-
fieldDef.type = "relation";
|
|
1706
|
-
fieldDef.relation = {
|
|
1707
|
-
collection: relationMetadata.collection,
|
|
1708
|
-
maxSelect: relationMetadata.maxSelect,
|
|
1709
|
-
minSelect: relationMetadata.minSelect,
|
|
1710
|
-
cascadeDelete: relationMetadata.cascadeDelete,
|
|
1711
|
-
displayFields: relationMetadata.displayFields
|
|
1712
|
-
};
|
|
1713
|
-
fieldDef.options = void 0;
|
|
1714
|
-
} else if (isRelationField(fieldName, zodType)) {
|
|
1715
|
-
fieldDef.type = "relation";
|
|
1716
|
-
const targetCollection = resolveTargetCollection(fieldName);
|
|
1717
|
-
const maxSelect = getMaxSelect(fieldName, zodType);
|
|
1718
|
-
const minSelect = getMinSelect(fieldName, zodType);
|
|
1719
|
-
fieldDef.relation = {
|
|
1720
|
-
collection: targetCollection,
|
|
1721
|
-
maxSelect,
|
|
1722
|
-
minSelect,
|
|
1723
|
-
cascadeDelete: false,
|
|
1724
|
-
// Default to false, can be configured later
|
|
1725
|
-
displayFields: null
|
|
1726
|
-
};
|
|
1727
|
-
if (fieldDef.options) {
|
|
1728
|
-
const { min: _min, max: _max, pattern: _pattern, ...relationSafeOptions } = fieldDef.options;
|
|
1729
|
-
fieldDef.options = Object.keys(relationSafeOptions).length ? relationSafeOptions : void 0;
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
1541
|
if (fieldDef.type === "autodate") {
|
|
1733
1542
|
fieldDef.options = {
|
|
1734
1543
|
onCreate: true,
|
|
@@ -1742,10 +1551,17 @@ function buildFieldDefinition(fieldName, zodType) {
|
|
|
1742
1551
|
fieldDef.options = filterSupportedFieldOptions(fieldDef.type, fieldDef.options);
|
|
1743
1552
|
return fieldDef;
|
|
1744
1553
|
}
|
|
1554
|
+
var TYPES_READING_CHAINED_VALIDATORS = /* @__PURE__ */ new Set(["text", "password", "number", "select", "file"]);
|
|
1555
|
+
function extractChainedFieldOptions(fieldType, unwrappedType) {
|
|
1556
|
+
if (!TYPES_READING_CHAINED_VALIDATORS.has(fieldType)) {
|
|
1557
|
+
return {};
|
|
1558
|
+
}
|
|
1559
|
+
return extractFieldOptions(unwrappedType);
|
|
1560
|
+
}
|
|
1745
1561
|
function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
1746
1562
|
const rawFields = extractFieldDefinitions(zodSchema);
|
|
1747
1563
|
const explicitType = extractCollectionTypeFromSchema(zodSchema);
|
|
1748
|
-
const collectionType = explicitType ??
|
|
1564
|
+
const collectionType = explicitType ?? "base";
|
|
1749
1565
|
const isView = collectionType === "view";
|
|
1750
1566
|
const viewQuery = extractViewQueryFromSchema(zodSchema);
|
|
1751
1567
|
if (isView) {
|
|
@@ -1856,9 +1672,8 @@ async function ensureTsxLoader() {
|
|
|
1856
1672
|
}
|
|
1857
1673
|
}
|
|
1858
1674
|
function discoverSchemaFiles(config) {
|
|
1859
|
-
const
|
|
1860
|
-
const
|
|
1861
|
-
const schemaDir = resolveSchemaDir(normalizedConfig);
|
|
1675
|
+
const mergedConfig = mergeConfig(config);
|
|
1676
|
+
const schemaDir = resolveSchemaDir(config);
|
|
1862
1677
|
try {
|
|
1863
1678
|
if (!fs10__namespace.existsSync(schemaDir)) {
|
|
1864
1679
|
throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
|
|
@@ -1886,7 +1701,7 @@ function discoverSchemaFiles(config) {
|
|
|
1886
1701
|
`(${mergedConfig.includeExtensions.map((ext) => ext.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`
|
|
1887
1702
|
);
|
|
1888
1703
|
return schemaFiles.map((file) => {
|
|
1889
|
-
return
|
|
1704
|
+
return path7__namespace.join(schemaDir, file.replace(extensionRemovalRegex, ""));
|
|
1890
1705
|
});
|
|
1891
1706
|
} catch (error) {
|
|
1892
1707
|
if (error instanceof FileSystemError) {
|
|
@@ -1938,7 +1753,7 @@ Alternatively, compile your schema files to JavaScript first.`,
|
|
|
1938
1753
|
);
|
|
1939
1754
|
}
|
|
1940
1755
|
}
|
|
1941
|
-
const fileUrl = new URL(`file://${
|
|
1756
|
+
const fileUrl = new URL(`file://${path7__namespace.resolve(resolvedPath)}`);
|
|
1942
1757
|
const module = await import(fileUrl.href);
|
|
1943
1758
|
return module;
|
|
1944
1759
|
} catch (error) {
|
|
@@ -1965,13 +1780,13 @@ Alternatively, compile your schema files to JavaScript first.`,
|
|
|
1965
1780
|
}
|
|
1966
1781
|
|
|
1967
1782
|
// src/migration/analyzer/index.ts
|
|
1968
|
-
async function
|
|
1969
|
-
const
|
|
1970
|
-
const mergedConfig = mergeConfig(normalizedConfig);
|
|
1783
|
+
async function parseSchemaFiles(config) {
|
|
1784
|
+
const mergedConfig = mergeConfig(config);
|
|
1971
1785
|
const collections = /* @__PURE__ */ new Map();
|
|
1972
|
-
const
|
|
1786
|
+
const collectionSources = /* @__PURE__ */ new Map();
|
|
1787
|
+
const schemaFiles = discoverSchemaFiles(config);
|
|
1973
1788
|
if (schemaFiles.length === 0) {
|
|
1974
|
-
const schemaDir = resolveSchemaDir(
|
|
1789
|
+
const schemaDir = resolveSchemaDir(config);
|
|
1975
1790
|
throw new SchemaParsingError(
|
|
1976
1791
|
`No schema files found in ${schemaDir}. Make sure you have schema files in the directory.`,
|
|
1977
1792
|
schemaDir
|
|
@@ -1980,8 +1795,8 @@ async function buildSchemaDefinition(config) {
|
|
|
1980
1795
|
for (const filePath of schemaFiles) {
|
|
1981
1796
|
try {
|
|
1982
1797
|
let importPath = filePath;
|
|
1983
|
-
if (
|
|
1984
|
-
importPath =
|
|
1798
|
+
if (config.pathTransformer) {
|
|
1799
|
+
importPath = config.pathTransformer(filePath);
|
|
1985
1800
|
} else if (mergedConfig.useCompiledFiles) {
|
|
1986
1801
|
const distPath = filePath.replace(/\/src\//, "/dist/");
|
|
1987
1802
|
if (fs10__namespace.existsSync(`${distPath}.js`) || fs10__namespace.existsSync(`${distPath}.mjs`)) {
|
|
@@ -1990,17 +1805,25 @@ async function buildSchemaDefinition(config) {
|
|
|
1990
1805
|
importPath = filePath;
|
|
1991
1806
|
}
|
|
1992
1807
|
}
|
|
1993
|
-
const module = await importSchemaModule(importPath,
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
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
|
+
);
|
|
1998
1814
|
continue;
|
|
1999
1815
|
}
|
|
2000
|
-
const
|
|
2001
|
-
const
|
|
2002
|
-
|
|
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);
|
|
2003
1825
|
collections.set(collectionName, collectionSchema);
|
|
1826
|
+
collectionSources.set(collectionName, filePath);
|
|
2004
1827
|
} catch (error) {
|
|
2005
1828
|
if (error instanceof SchemaParsingError) {
|
|
2006
1829
|
throw error;
|
|
@@ -2014,9 +1837,6 @@ async function buildSchemaDefinition(config) {
|
|
|
2014
1837
|
}
|
|
2015
1838
|
return { collections };
|
|
2016
1839
|
}
|
|
2017
|
-
async function parseSchemaFiles(config) {
|
|
2018
|
-
return buildSchemaDefinition(config);
|
|
2019
|
-
}
|
|
2020
1840
|
|
|
2021
1841
|
// src/migration/engine/fields.ts
|
|
2022
1842
|
var Field = class {
|
|
@@ -2062,12 +1882,26 @@ var FIELD_CONSTRUCTORS = {
|
|
|
2062
1882
|
AutodateField,
|
|
2063
1883
|
PasswordField
|
|
2064
1884
|
};
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
1885
|
+
var crcTable = null;
|
|
1886
|
+
function crc32(input) {
|
|
1887
|
+
if (!crcTable) {
|
|
1888
|
+
crcTable = new Uint32Array(256);
|
|
1889
|
+
for (let i = 0; i < 256; i++) {
|
|
1890
|
+
let c = i;
|
|
1891
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
1892
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1893
|
+
}
|
|
1894
|
+
crcTable[i] = c >>> 0;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
let crc = 4294967295;
|
|
1898
|
+
for (const byte of new TextEncoder().encode(input)) {
|
|
1899
|
+
crc = crcTable[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
2069
1900
|
}
|
|
2070
|
-
return
|
|
1901
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1902
|
+
}
|
|
1903
|
+
function generateRuntimeFieldId(type, name) {
|
|
1904
|
+
return `${type || "field"}${crc32(name)}`;
|
|
2071
1905
|
}
|
|
2072
1906
|
|
|
2073
1907
|
// src/migration/engine/fields-list.ts
|
|
@@ -2075,22 +1909,19 @@ var FieldsList = class {
|
|
|
2075
1909
|
items = [];
|
|
2076
1910
|
constructor(fields) {
|
|
2077
1911
|
if (Array.isArray(fields)) {
|
|
2078
|
-
|
|
2079
|
-
this.items.push(field instanceof Field ? field : new Field(field));
|
|
2080
|
-
}
|
|
1912
|
+
this.add(...fields);
|
|
2081
1913
|
}
|
|
2082
1914
|
}
|
|
2083
1915
|
get length() {
|
|
2084
1916
|
return this.items.length;
|
|
2085
1917
|
}
|
|
2086
1918
|
/**
|
|
2087
|
-
* Appends fields; a field
|
|
2088
|
-
*
|
|
1919
|
+
* Appends fields; a field that matches an existing entry replaces it in
|
|
1920
|
+
* place (position preserved), matching PocketBase upsert semantics.
|
|
2089
1921
|
*/
|
|
2090
1922
|
add(...fields) {
|
|
2091
1923
|
for (const raw of fields) {
|
|
2092
|
-
const field
|
|
2093
|
-
const existing = this.indexOfId(field.id);
|
|
1924
|
+
const { field, existing } = this.resolveIncoming(raw);
|
|
2094
1925
|
if (existing >= 0) {
|
|
2095
1926
|
this.items[existing] = field;
|
|
2096
1927
|
} else {
|
|
@@ -2099,15 +1930,14 @@ var FieldsList = class {
|
|
|
2099
1930
|
}
|
|
2100
1931
|
}
|
|
2101
1932
|
/**
|
|
2102
|
-
* Inserts fields at a position. A field
|
|
1933
|
+
* Inserts fields at a position. A field matching an already-present entry is
|
|
2103
1934
|
* moved: the old entry is removed first, then the field is inserted at
|
|
2104
1935
|
* the requested position.
|
|
2105
1936
|
*/
|
|
2106
1937
|
addAt(position, ...fields) {
|
|
2107
1938
|
let insertAt = Math.max(0, Math.min(position, this.items.length));
|
|
2108
1939
|
for (const raw of fields) {
|
|
2109
|
-
const field
|
|
2110
|
-
const existing = this.indexOfId(field.id);
|
|
1940
|
+
const { field, existing } = this.resolveIncoming(raw);
|
|
2111
1941
|
if (existing >= 0) {
|
|
2112
1942
|
this.items.splice(existing, 1);
|
|
2113
1943
|
if (existing < insertAt) {
|
|
@@ -2119,6 +1949,36 @@ var FieldsList = class {
|
|
|
2119
1949
|
insertAt++;
|
|
2120
1950
|
}
|
|
2121
1951
|
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Materializes an incoming field and locates the entry it replaces.
|
|
1954
|
+
*
|
|
1955
|
+
* PocketBase's rule (documented on `FieldsList.Add` in types.d.ts): match by
|
|
1956
|
+
* id, "or by their name if the new field doesn't have an explicit id", and
|
|
1957
|
+
* autogenerate a missing id from the name. Matching by id alone turns the
|
|
1958
|
+
* idiomatic `fields.add(new TextField({name: "title", max: 500}))` — no id,
|
|
1959
|
+
* meant to rewrite `title` — into a second field also called `title`, which
|
|
1960
|
+
* PocketBase would reject on save but the engine used to accept, corrupting
|
|
1961
|
+
* the reconstructed state.
|
|
1962
|
+
*
|
|
1963
|
+
* On a name match the *existing* id is kept rather than replaced with the
|
|
1964
|
+
* derived one. For a collection PocketBase authored the two are identical
|
|
1965
|
+
* (its auto-ids already are `<type><crc32(name)>`); they diverge only when
|
|
1966
|
+
* the stored field carries a hand-assigned id, and there preserving it keeps
|
|
1967
|
+
* a later `removeById`/`getById` in the same migration working.
|
|
1968
|
+
*/
|
|
1969
|
+
resolveIncoming(raw) {
|
|
1970
|
+
const field = raw instanceof Field ? raw : new Field(raw);
|
|
1971
|
+
const hasExplicitId = typeof field.id === "string" && field.id !== "";
|
|
1972
|
+
if (hasExplicitId) {
|
|
1973
|
+
return { field, existing: this.indexOfId(field.id) };
|
|
1974
|
+
}
|
|
1975
|
+
const existing = this.items.findIndex((f) => f.name === field.name);
|
|
1976
|
+
field.id = existing >= 0 ? this.items[existing].id : generateRuntimeFieldId(
|
|
1977
|
+
typeof field.type === "string" ? field.type : "",
|
|
1978
|
+
typeof field.name === "string" ? field.name : ""
|
|
1979
|
+
);
|
|
1980
|
+
return { field, existing };
|
|
1981
|
+
}
|
|
2122
1982
|
removeById(id) {
|
|
2123
1983
|
const index = this.indexOfId(id);
|
|
2124
1984
|
if (index >= 0) {
|
|
@@ -2131,12 +1991,18 @@ var FieldsList = class {
|
|
|
2131
1991
|
this.items.splice(index, 1);
|
|
2132
1992
|
}
|
|
2133
1993
|
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Null rather than undefined for a miss: these two return a Go `Field`
|
|
1996
|
+
* interface, and goja surfaces a nil interface to JavaScript as `null`. A
|
|
1997
|
+
* migration written as `if (collection.fields.getByName("x") === null)` has
|
|
1998
|
+
* to take the same branch here as it does in production.
|
|
1999
|
+
*/
|
|
2134
2000
|
getById(id) {
|
|
2135
2001
|
const index = this.indexOfId(id);
|
|
2136
|
-
return index >= 0 ? this.items[index] :
|
|
2002
|
+
return index >= 0 ? this.items[index] : null;
|
|
2137
2003
|
}
|
|
2138
2004
|
getByName(name) {
|
|
2139
|
-
return this.items.find((f) => f.name === name);
|
|
2005
|
+
return this.items.find((f) => f.name === name) ?? null;
|
|
2140
2006
|
}
|
|
2141
2007
|
at(index) {
|
|
2142
2008
|
return this.items.at(index);
|
|
@@ -2161,9 +2027,14 @@ var FieldsList = class {
|
|
|
2161
2027
|
this.items = [];
|
|
2162
2028
|
this.add(...fields);
|
|
2163
2029
|
}
|
|
2164
|
-
/**
|
|
2030
|
+
/**
|
|
2031
|
+
* Plain objects, in order — copied deeply, so option arrays like a select
|
|
2032
|
+
* field's `values` are not shared with the live list. A shallow spread let a
|
|
2033
|
+
* snapshot handed to the diff engine alias engine state, where an in-place
|
|
2034
|
+
* sort or push downstream would silently rewrite the collection it came from.
|
|
2035
|
+
*/
|
|
2165
2036
|
serialize() {
|
|
2166
|
-
return this.items.map((field) => ({ ...field }));
|
|
2037
|
+
return this.items.map((field) => copyPlainData({ ...field }));
|
|
2167
2038
|
}
|
|
2168
2039
|
indexOfId(id) {
|
|
2169
2040
|
if (typeof id !== "string" || id === "") {
|
|
@@ -2172,6 +2043,22 @@ var FieldsList = class {
|
|
|
2172
2043
|
return this.items.findIndex((f) => f.id === id);
|
|
2173
2044
|
}
|
|
2174
2045
|
};
|
|
2046
|
+
function copyPlainData(value) {
|
|
2047
|
+
if (Array.isArray(value)) {
|
|
2048
|
+
return value.map(copyPlainData);
|
|
2049
|
+
}
|
|
2050
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
2051
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2052
|
+
if (prototype === Object.prototype || prototype === null) {
|
|
2053
|
+
const copy = {};
|
|
2054
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2055
|
+
copy[key] = copyPlainData(entry);
|
|
2056
|
+
}
|
|
2057
|
+
return copy;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
return value;
|
|
2061
|
+
}
|
|
2175
2062
|
|
|
2176
2063
|
// src/migration/engine/collection.ts
|
|
2177
2064
|
var AUTH_SYSTEM_FIELDS = [
|
|
@@ -3590,6 +3477,13 @@ function createInertStub(name, options, warn) {
|
|
|
3590
3477
|
if (prop === "then") {
|
|
3591
3478
|
return void 0;
|
|
3592
3479
|
}
|
|
3480
|
+
if (prop === "length") {
|
|
3481
|
+
return 0;
|
|
3482
|
+
}
|
|
3483
|
+
if (prop === Symbol.iterator) {
|
|
3484
|
+
return function* emptyIterator() {
|
|
3485
|
+
};
|
|
3486
|
+
}
|
|
3593
3487
|
if (typeof prop === "symbol") {
|
|
3594
3488
|
return void 0;
|
|
3595
3489
|
}
|
|
@@ -3648,7 +3542,10 @@ var SimulatedAppImpl = class {
|
|
|
3648
3542
|
ensureAuthSystemFields(collection);
|
|
3649
3543
|
for (const field of collection.fields) {
|
|
3650
3544
|
if (typeof field.id !== "string" || field.id === "") {
|
|
3651
|
-
field.id = generateRuntimeFieldId(
|
|
3545
|
+
field.id = generateRuntimeFieldId(
|
|
3546
|
+
typeof field.type === "string" ? field.type : "",
|
|
3547
|
+
typeof field.name === "string" ? field.name : ""
|
|
3548
|
+
);
|
|
3652
3549
|
}
|
|
3653
3550
|
}
|
|
3654
3551
|
this.store.upsert(collection);
|
|
@@ -3934,21 +3831,21 @@ function appliedMigrationsFromList(files, origin = "<list>") {
|
|
|
3934
3831
|
const entries = [];
|
|
3935
3832
|
const coreEntries = [];
|
|
3936
3833
|
for (const file of files) {
|
|
3937
|
-
const entry = { file:
|
|
3834
|
+
const entry = { file: path7__namespace.basename(file) };
|
|
3938
3835
|
(isCoreMigration(entry.file) ? coreEntries : entries).push(entry);
|
|
3939
3836
|
}
|
|
3940
3837
|
return { origin, entries, coreEntries };
|
|
3941
3838
|
}
|
|
3942
3839
|
function defaultDataDirectory(migrationsPath) {
|
|
3943
|
-
return
|
|
3840
|
+
return path7__namespace.resolve(path7__namespace.dirname(path7__namespace.resolve(migrationsPath)), "pb_data");
|
|
3944
3841
|
}
|
|
3945
3842
|
function resolveDatabasePath(dataPathOrFile) {
|
|
3946
|
-
const resolved =
|
|
3843
|
+
const resolved = path7__namespace.resolve(dataPathOrFile);
|
|
3947
3844
|
if (!fs10__namespace.existsSync(resolved)) {
|
|
3948
3845
|
return null;
|
|
3949
3846
|
}
|
|
3950
3847
|
if (fs10__namespace.statSync(resolved).isDirectory()) {
|
|
3951
|
-
const candidate =
|
|
3848
|
+
const candidate = path7__namespace.join(resolved, POCKETBASE_DATABASE_FILENAME);
|
|
3952
3849
|
return fs10__namespace.existsSync(candidate) ? candidate : null;
|
|
3953
3850
|
}
|
|
3954
3851
|
return resolved;
|
|
@@ -3957,7 +3854,7 @@ function readAppliedMigrations(dataPathOrFile) {
|
|
|
3957
3854
|
const databasePath = resolveDatabasePath(dataPathOrFile);
|
|
3958
3855
|
if (!databasePath) {
|
|
3959
3856
|
throw new AppliedMigrationsError(
|
|
3960
|
-
`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.`,
|
|
3961
3858
|
dataPathOrFile
|
|
3962
3859
|
);
|
|
3963
3860
|
}
|
|
@@ -4073,7 +3970,7 @@ function discoverMigrations(migrationsPath) {
|
|
|
4073
3970
|
continue;
|
|
4074
3971
|
}
|
|
4075
3972
|
discovered.push({
|
|
4076
|
-
path:
|
|
3973
|
+
path: path7__namespace.join(migrationsPath, name),
|
|
4077
3974
|
name,
|
|
4078
3975
|
timestamp,
|
|
4079
3976
|
isSnapshot: name.endsWith("_collections_snapshot.js") || name.endsWith("_snapshot.js")
|
|
@@ -4352,8 +4249,8 @@ function replayMigrationsDirectory(migrationsPath, options = {}) {
|
|
|
4352
4249
|
}
|
|
4353
4250
|
return replayMigrations(plan.filesToReplay, { ...engineOptions, plan });
|
|
4354
4251
|
}
|
|
4355
|
-
|
|
4356
|
-
|
|
4252
|
+
|
|
4253
|
+
// src/migration/snapshot.ts
|
|
4357
4254
|
function loadSnapshotWithMigrations(config = {}) {
|
|
4358
4255
|
const migrationsPath = config.migrationsPath;
|
|
4359
4256
|
if (!migrationsPath) {
|
|
@@ -4382,16 +4279,13 @@ function loadSnapshotWithMigrations(config = {}) {
|
|
|
4382
4279
|
}
|
|
4383
4280
|
|
|
4384
4281
|
// src/migration/diff/config.ts
|
|
4385
|
-
var
|
|
4386
|
-
warnOnDelete: true,
|
|
4387
|
-
requireForceForDestructive: true,
|
|
4388
|
-
severityThreshold: "high",
|
|
4282
|
+
var DEFAULT_CONFIG2 = {
|
|
4389
4283
|
systemCollections: ["_mfas", "_otps", "_externalAuths", "_authOrigins", "_superusers"],
|
|
4390
4284
|
usersSystemFields: ["id", "password", "tokenKey", "email", "emailVisibility", "verified", "created", "updated"]
|
|
4391
4285
|
};
|
|
4392
4286
|
function mergeConfig2(config) {
|
|
4393
4287
|
return {
|
|
4394
|
-
...
|
|
4288
|
+
...DEFAULT_CONFIG2,
|
|
4395
4289
|
...config
|
|
4396
4290
|
};
|
|
4397
4291
|
}
|
|
@@ -4420,40 +4314,23 @@ function areValuesEqual(a, b) {
|
|
|
4420
4314
|
}
|
|
4421
4315
|
return a === b;
|
|
4422
4316
|
}
|
|
4423
|
-
var OPTION_ZERO_VALUES = {
|
|
4424
|
-
autogeneratePattern: "",
|
|
4425
|
-
pattern: "",
|
|
4426
|
-
primaryKey: false,
|
|
4427
|
-
convertURLs: false,
|
|
4428
|
-
cost: 0,
|
|
4429
|
-
hidden: false,
|
|
4430
|
-
presentable: false,
|
|
4431
|
-
system: false
|
|
4432
|
-
};
|
|
4433
4317
|
function normalizeOptionValue(key, value, fieldType) {
|
|
4434
|
-
if (
|
|
4318
|
+
if (value === null || value === void 0) {
|
|
4435
4319
|
return void 0;
|
|
4436
4320
|
}
|
|
4437
|
-
|
|
4321
|
+
const unsetValue = getFieldOptionUnsetValue(fieldType, key);
|
|
4322
|
+
if (unsetValue !== void 0 && areValuesEqual(value, unsetValue)) {
|
|
4438
4323
|
return void 0;
|
|
4439
4324
|
}
|
|
4440
|
-
if (key === "
|
|
4325
|
+
if (key === "values" && fieldType === "select" && Array.isArray(value)) {
|
|
4326
|
+
return [...value].sort();
|
|
4327
|
+
}
|
|
4328
|
+
if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
|
|
4441
4329
|
return void 0;
|
|
4442
4330
|
}
|
|
4443
4331
|
if (key === "min" && value === 1 && fieldType === "number") {
|
|
4444
4332
|
return void 0;
|
|
4445
4333
|
}
|
|
4446
|
-
if (fieldType === "file") {
|
|
4447
|
-
if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
|
|
4448
|
-
return void 0;
|
|
4449
|
-
}
|
|
4450
|
-
if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
|
|
4451
|
-
return void 0;
|
|
4452
|
-
}
|
|
4453
|
-
if (key === "protected" && value === false) {
|
|
4454
|
-
return void 0;
|
|
4455
|
-
}
|
|
4456
|
-
}
|
|
4457
4334
|
if (fieldType === "autodate") {
|
|
4458
4335
|
if (key === "onCreate" && value === true) {
|
|
4459
4336
|
return void 0;
|
|
@@ -4863,57 +4740,6 @@ function buildCollectionModification(currentCollection, previousCollection, conf
|
|
|
4863
4740
|
};
|
|
4864
4741
|
}
|
|
4865
4742
|
|
|
4866
|
-
// src/migration/diff/summary.ts
|
|
4867
|
-
function categorizeChangesBySeverity(diff, _config) {
|
|
4868
|
-
const destructive = [];
|
|
4869
|
-
const nonDestructive = [];
|
|
4870
|
-
for (const collection of diff.collectionsToDelete) {
|
|
4871
|
-
if (collection.type === "view") {
|
|
4872
|
-
nonDestructive.push(`Delete view collection: ${collection.name}`);
|
|
4873
|
-
} else {
|
|
4874
|
-
destructive.push(`Delete collection: ${collection.name}`);
|
|
4875
|
-
}
|
|
4876
|
-
}
|
|
4877
|
-
for (const collection of diff.collectionsToCreate) {
|
|
4878
|
-
nonDestructive.push(`Create collection: ${collection.name}`);
|
|
4879
|
-
}
|
|
4880
|
-
for (const modification of diff.collectionsToModify) {
|
|
4881
|
-
const collectionName = modification.collection;
|
|
4882
|
-
for (const field of modification.fieldsToRemove) {
|
|
4883
|
-
destructive.push(`Delete field: ${collectionName}.${field.name}`);
|
|
4884
|
-
}
|
|
4885
|
-
for (const field of modification.fieldsToAdd) {
|
|
4886
|
-
nonDestructive.push(`Add field: ${collectionName}.${field.name}`);
|
|
4887
|
-
}
|
|
4888
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
4889
|
-
const hasTypeChange = fieldMod.changes.some((c) => c.property === "type");
|
|
4890
|
-
const hasRequiredChange = fieldMod.changes.some((c) => c.property === "required" && c.newValue === true);
|
|
4891
|
-
if (hasTypeChange) {
|
|
4892
|
-
destructive.push(
|
|
4893
|
-
`Change field type: ${collectionName}.${fieldMod.fieldName} (${fieldMod.changes.find((c) => c.property === "type")?.oldValue} \u2192 ${fieldMod.changes.find((c) => c.property === "type")?.newValue})`
|
|
4894
|
-
);
|
|
4895
|
-
} else if (hasRequiredChange) {
|
|
4896
|
-
destructive.push(`Make field required: ${collectionName}.${fieldMod.fieldName}`);
|
|
4897
|
-
} else {
|
|
4898
|
-
nonDestructive.push(`Modify field: ${collectionName}.${fieldMod.fieldName}`);
|
|
4899
|
-
}
|
|
4900
|
-
}
|
|
4901
|
-
for (const _index of modification.indexesToAdd) {
|
|
4902
|
-
nonDestructive.push(`Add index: ${collectionName}`);
|
|
4903
|
-
}
|
|
4904
|
-
for (const _index of modification.indexesToRemove) {
|
|
4905
|
-
nonDestructive.push(`Remove index: ${collectionName}`);
|
|
4906
|
-
}
|
|
4907
|
-
for (const rule of modification.rulesToUpdate) {
|
|
4908
|
-
nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
|
|
4909
|
-
}
|
|
4910
|
-
if (modification.viewQueryUpdate) {
|
|
4911
|
-
nonDestructive.push(`Update view query: ${collectionName}`);
|
|
4912
|
-
}
|
|
4913
|
-
}
|
|
4914
|
-
return { destructive, nonDestructive };
|
|
4915
|
-
}
|
|
4916
|
-
|
|
4917
4743
|
// src/migration/diff/filter.ts
|
|
4918
4744
|
function matchesPattern(text, patterns) {
|
|
4919
4745
|
if (!patterns || patterns.length === 0) return true;
|
|
@@ -4990,25 +4816,76 @@ function filterDiff(diff, options) {
|
|
|
4990
4816
|
};
|
|
4991
4817
|
}
|
|
4992
4818
|
|
|
4993
|
-
// src/migration/diff/
|
|
4994
|
-
function
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
collectionIdToName.set(collection.id, name);
|
|
5003
|
-
}
|
|
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}`);
|
|
5004
4828
|
}
|
|
5005
4829
|
}
|
|
5006
|
-
const
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
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
|
+
|
|
4870
|
+
// src/migration/diff/index.ts
|
|
4871
|
+
function hasChanges(modification) {
|
|
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;
|
|
4873
|
+
}
|
|
4874
|
+
function compare2(currentSchema, previousSnapshot, config) {
|
|
4875
|
+
const collectionIdToName = /* @__PURE__ */ new Map();
|
|
4876
|
+
if (previousSnapshot) {
|
|
4877
|
+
for (const [name, collection] of previousSnapshot.collections) {
|
|
4878
|
+
if (collection.id) {
|
|
4879
|
+
collectionIdToName.set(collection.id, name);
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
|
|
4884
|
+
const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
|
|
4885
|
+
const filteredCollectionsToCreate = collectionsToCreate.filter(
|
|
4886
|
+
(collection) => !isSystemCollection(collection.name, config)
|
|
4887
|
+
);
|
|
4888
|
+
const filteredCollectionsToDelete = collectionsToDelete.filter(
|
|
5012
4889
|
(collection) => !isSystemCollection(collection.name, config)
|
|
5013
4890
|
);
|
|
5014
4891
|
const registry = new CollectionIdRegistry();
|
|
@@ -5046,8 +4923,153 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
|
|
|
5046
4923
|
existingCollectionIds
|
|
5047
4924
|
};
|
|
5048
4925
|
}
|
|
5049
|
-
|
|
5050
|
-
|
|
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");
|
|
5051
5073
|
}
|
|
5052
5074
|
|
|
5053
5075
|
// src/migration/generator/config.ts
|
|
@@ -5058,7 +5080,7 @@ migrate((app) => {
|
|
|
5058
5080
|
{{DOWN_CODE}}
|
|
5059
5081
|
});
|
|
5060
5082
|
`;
|
|
5061
|
-
var
|
|
5083
|
+
var DEFAULT_CONFIG3 = {
|
|
5062
5084
|
workspaceRoot: process.cwd(),
|
|
5063
5085
|
timestampGenerator: () => Math.floor(Date.now() / 1e3).toString(),
|
|
5064
5086
|
template: DEFAULT_TEMPLATE,
|
|
@@ -5067,20 +5089,20 @@ var DEFAULT_CONFIG4 = {
|
|
|
5067
5089
|
};
|
|
5068
5090
|
function mergeConfig3(config) {
|
|
5069
5091
|
return {
|
|
5070
|
-
...
|
|
5092
|
+
...DEFAULT_CONFIG3,
|
|
5071
5093
|
force: false,
|
|
5072
5094
|
...config
|
|
5073
5095
|
};
|
|
5074
5096
|
}
|
|
5075
5097
|
function resolveMigrationDir(config) {
|
|
5076
5098
|
const workspaceRoot = config.workspaceRoot || process.cwd();
|
|
5077
|
-
if (
|
|
5099
|
+
if (path7__namespace.isAbsolute(config.migrationDir)) {
|
|
5078
5100
|
return config.migrationDir;
|
|
5079
5101
|
}
|
|
5080
|
-
return
|
|
5102
|
+
return path7__namespace.join(workspaceRoot, config.migrationDir);
|
|
5081
5103
|
}
|
|
5082
5104
|
function createMigrationFileStructure(upCode, downCode, config) {
|
|
5083
|
-
const mergedConfig = config ? mergeConfig3(config) :
|
|
5105
|
+
const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
|
|
5084
5106
|
let template = mergedConfig.template;
|
|
5085
5107
|
template = template.replace("{{TYPES_PATH}}", () => mergedConfig.typesPath);
|
|
5086
5108
|
template = template.replace("{{UP_CODE}}", () => upCode);
|
|
@@ -5115,7 +5137,7 @@ function writeMigrationFile(migrationDir, filename, content) {
|
|
|
5115
5137
|
);
|
|
5116
5138
|
}
|
|
5117
5139
|
}
|
|
5118
|
-
const filePath =
|
|
5140
|
+
const filePath = path7__namespace.join(migrationDir, filename);
|
|
5119
5141
|
fs10__namespace.writeFileSync(filePath, content, "utf-8");
|
|
5120
5142
|
return filePath;
|
|
5121
5143
|
} catch (error) {
|
|
@@ -5123,7 +5145,7 @@ function writeMigrationFile(migrationDir, filename, content) {
|
|
|
5123
5145
|
throw error;
|
|
5124
5146
|
}
|
|
5125
5147
|
const fsError = error;
|
|
5126
|
-
const filePath =
|
|
5148
|
+
const filePath = path7__namespace.join(migrationDir, filename);
|
|
5127
5149
|
if (fsError.code === "EACCES" || fsError.code === "EPERM") {
|
|
5128
5150
|
throw new FileSystemError(
|
|
5129
5151
|
`Permission denied writing migration file. Check file and directory permissions.`,
|
|
@@ -5273,13 +5295,16 @@ function generateFieldModification(collectionName, modification, varName, isLast
|
|
|
5273
5295
|
const lines = [];
|
|
5274
5296
|
const collectionVar = varName || `collection_${collectionName}_${modification.fieldName}`;
|
|
5275
5297
|
const fieldVar = `${collectionVar}_field`;
|
|
5298
|
+
const fieldType = modification.newDefinition?.type ?? modification.currentDefinition?.type;
|
|
5276
5299
|
lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
|
|
5277
5300
|
lines.push(` const ${fieldVar} = ${collectionVar}.fields.getByName("${modification.fieldName}");`);
|
|
5278
5301
|
lines.push(``);
|
|
5279
5302
|
for (const change of modification.changes) {
|
|
5280
5303
|
if (change.property.startsWith("options.")) {
|
|
5281
5304
|
const optionKey = change.property.replace("options.", "");
|
|
5282
|
-
|
|
5305
|
+
const newValue = change.newValue === void 0 ? getFieldOptionUnsetValue(fieldType, optionKey) : change.newValue;
|
|
5306
|
+
const pbKey = fieldType === "number" && optionKey === "noDecimal" ? "onlyInt" : optionKey;
|
|
5307
|
+
lines.push(` ${fieldVar}.${pbKey} = ${formatValue(newValue)};`);
|
|
5283
5308
|
} else if (change.property.startsWith("relation.")) {
|
|
5284
5309
|
const relationKey = change.property.replace("relation.", "");
|
|
5285
5310
|
if (relationKey === "collection") {
|
|
@@ -5887,7 +5912,7 @@ function planMigrations(diff, config) {
|
|
|
5887
5912
|
const planned = [];
|
|
5888
5913
|
let existingFiles = [];
|
|
5889
5914
|
if (!normalizedConfig.force && fs10__namespace.existsSync(migrationDir)) {
|
|
5890
|
-
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"));
|
|
5891
5916
|
}
|
|
5892
5917
|
for (const operation of operations) {
|
|
5893
5918
|
const upCode = generateOperationUpMigration(operation, collectionIdMap);
|
|
@@ -6644,154 +6669,6 @@ function toPhaseError(error, fallbackPhase) {
|
|
|
6644
6669
|
}
|
|
6645
6670
|
return { phase: fallbackPhase, message: error instanceof Error ? error.message : String(error) };
|
|
6646
6671
|
}
|
|
6647
|
-
|
|
6648
|
-
// src/migration/validation.ts
|
|
6649
|
-
function detectCollectionDeletions(diff) {
|
|
6650
|
-
const changes = [];
|
|
6651
|
-
for (const collection of diff.collectionsToDelete) {
|
|
6652
|
-
if (collection.type === "view") {
|
|
6653
|
-
continue;
|
|
6654
|
-
}
|
|
6655
|
-
changes.push({
|
|
6656
|
-
type: "collection_deletion" /* COLLECTION_DELETION */,
|
|
6657
|
-
description: `Delete collection: ${collection.name}`,
|
|
6658
|
-
collection: collection.name,
|
|
6659
|
-
severity: "high",
|
|
6660
|
-
warning: `All data in the "${collection.name}" collection will be permanently deleted.`
|
|
6661
|
-
});
|
|
6662
|
-
}
|
|
6663
|
-
return changes;
|
|
6664
|
-
}
|
|
6665
|
-
function detectFieldDeletions(diff) {
|
|
6666
|
-
const changes = [];
|
|
6667
|
-
for (const modification of diff.collectionsToModify) {
|
|
6668
|
-
for (const field of modification.fieldsToRemove) {
|
|
6669
|
-
changes.push({
|
|
6670
|
-
type: "field_deletion" /* FIELD_DELETION */,
|
|
6671
|
-
description: `Delete field: ${modification.collection}.${field.name}`,
|
|
6672
|
-
collection: modification.collection,
|
|
6673
|
-
field: field.name,
|
|
6674
|
-
severity: "high",
|
|
6675
|
-
warning: `All data in the "${field.name}" field of "${modification.collection}" will be permanently deleted.`
|
|
6676
|
-
});
|
|
6677
|
-
}
|
|
6678
|
-
}
|
|
6679
|
-
return changes;
|
|
6680
|
-
}
|
|
6681
|
-
function detectFieldTypeChanges(diff) {
|
|
6682
|
-
const changes = [];
|
|
6683
|
-
for (const modification of diff.collectionsToModify) {
|
|
6684
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
6685
|
-
const typeChange = fieldMod.changes.find((c) => c.property === "type");
|
|
6686
|
-
if (typeChange) {
|
|
6687
|
-
changes.push({
|
|
6688
|
-
type: "field_type_change" /* FIELD_TYPE_CHANGE */,
|
|
6689
|
-
description: `Change field type: ${modification.collection}.${fieldMod.fieldName}`,
|
|
6690
|
-
collection: modification.collection,
|
|
6691
|
-
field: fieldMod.fieldName,
|
|
6692
|
-
details: {
|
|
6693
|
-
oldValue: typeChange.oldValue,
|
|
6694
|
-
newValue: typeChange.newValue
|
|
6695
|
-
},
|
|
6696
|
-
severity: "high",
|
|
6697
|
-
warning: `Changing field type from "${typeChange.oldValue}" to "${typeChange.newValue}" may cause data loss or conversion errors.`
|
|
6698
|
-
});
|
|
6699
|
-
}
|
|
6700
|
-
}
|
|
6701
|
-
}
|
|
6702
|
-
return changes;
|
|
6703
|
-
}
|
|
6704
|
-
function detectFieldRequiredChanges(diff) {
|
|
6705
|
-
const changes = [];
|
|
6706
|
-
for (const modification of diff.collectionsToModify) {
|
|
6707
|
-
for (const fieldMod of modification.fieldsToModify) {
|
|
6708
|
-
const requiredChange = fieldMod.changes.find(
|
|
6709
|
-
(c) => c.property === "required" && c.newValue === true && c.oldValue === false
|
|
6710
|
-
);
|
|
6711
|
-
if (requiredChange) {
|
|
6712
|
-
changes.push({
|
|
6713
|
-
type: "field_required_change" /* FIELD_REQUIRED_CHANGE */,
|
|
6714
|
-
description: `Make field required: ${modification.collection}.${fieldMod.fieldName}`,
|
|
6715
|
-
collection: modification.collection,
|
|
6716
|
-
field: fieldMod.fieldName,
|
|
6717
|
-
details: {
|
|
6718
|
-
oldValue: false,
|
|
6719
|
-
newValue: true
|
|
6720
|
-
},
|
|
6721
|
-
severity: "medium",
|
|
6722
|
-
warning: `Making "${fieldMod.fieldName}" required may cause issues with existing records that have null/empty values.`
|
|
6723
|
-
});
|
|
6724
|
-
}
|
|
6725
|
-
}
|
|
6726
|
-
}
|
|
6727
|
-
return changes;
|
|
6728
|
-
}
|
|
6729
|
-
function detectDestructiveChanges(diff) {
|
|
6730
|
-
const changes = [];
|
|
6731
|
-
changes.push(...detectCollectionDeletions(diff));
|
|
6732
|
-
changes.push(...detectFieldDeletions(diff));
|
|
6733
|
-
changes.push(...detectFieldTypeChanges(diff));
|
|
6734
|
-
changes.push(...detectFieldRequiredChanges(diff));
|
|
6735
|
-
return changes;
|
|
6736
|
-
}
|
|
6737
|
-
function formatDestructiveChanges(changes) {
|
|
6738
|
-
if (changes.length === 0) {
|
|
6739
|
-
return "No destructive changes detected.";
|
|
6740
|
-
}
|
|
6741
|
-
const lines = [];
|
|
6742
|
-
const highSeverity = changes.filter((c) => c.severity === "high");
|
|
6743
|
-
const mediumSeverity = changes.filter((c) => c.severity === "medium");
|
|
6744
|
-
const lowSeverity = changes.filter((c) => c.severity === "low");
|
|
6745
|
-
if (highSeverity.length > 0) {
|
|
6746
|
-
lines.push("\u{1F534} HIGH SEVERITY CHANGES (Data Loss Risk):");
|
|
6747
|
-
lines.push("");
|
|
6748
|
-
for (const change of highSeverity) {
|
|
6749
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6750
|
-
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
6751
|
-
if (change.details) {
|
|
6752
|
-
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
6753
|
-
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
6754
|
-
}
|
|
6755
|
-
}
|
|
6756
|
-
lines.push("");
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
6759
|
-
if (mediumSeverity.length > 0) {
|
|
6760
|
-
lines.push("\u{1F7E1} MEDIUM SEVERITY CHANGES (Potential Issues):");
|
|
6761
|
-
lines.push("");
|
|
6762
|
-
for (const change of mediumSeverity) {
|
|
6763
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6764
|
-
lines.push(` \u26A0\uFE0F ${change.warning}`);
|
|
6765
|
-
if (change.details) {
|
|
6766
|
-
if (change.details.oldValue !== void 0 && change.details.newValue !== void 0) {
|
|
6767
|
-
lines.push(` Old: ${change.details.oldValue} \u2192 New: ${change.details.newValue}`);
|
|
6768
|
-
}
|
|
6769
|
-
}
|
|
6770
|
-
lines.push("");
|
|
6771
|
-
}
|
|
6772
|
-
}
|
|
6773
|
-
if (lowSeverity.length > 0) {
|
|
6774
|
-
lines.push("\u{1F7E2} LOW SEVERITY CHANGES:");
|
|
6775
|
-
lines.push("");
|
|
6776
|
-
for (const change of lowSeverity) {
|
|
6777
|
-
lines.push(` \u2022 ${change.description}`);
|
|
6778
|
-
lines.push(` \u2139\uFE0F ${change.warning}`);
|
|
6779
|
-
lines.push("");
|
|
6780
|
-
}
|
|
6781
|
-
}
|
|
6782
|
-
return lines.join("\n");
|
|
6783
|
-
}
|
|
6784
|
-
function summarizeDestructiveChanges(changes) {
|
|
6785
|
-
return {
|
|
6786
|
-
total: changes.length,
|
|
6787
|
-
high: changes.filter((c) => c.severity === "high").length,
|
|
6788
|
-
medium: changes.filter((c) => c.severity === "medium").length,
|
|
6789
|
-
low: changes.filter((c) => c.severity === "low").length
|
|
6790
|
-
};
|
|
6791
|
-
}
|
|
6792
|
-
function requiresForceFlag(changes) {
|
|
6793
|
-
return changes.some((c) => c.severity === "high" || c.severity === "medium");
|
|
6794
|
-
}
|
|
6795
6672
|
var CONFIG_FILE_NAMES = [
|
|
6796
6673
|
"pocketbase-migrate.config.js",
|
|
6797
6674
|
"pocketbase-migrate.config.mjs",
|
|
@@ -6800,7 +6677,7 @@ var CONFIG_FILE_NAMES = [
|
|
|
6800
6677
|
"migrate.config.mjs",
|
|
6801
6678
|
"migrate.config.json"
|
|
6802
6679
|
];
|
|
6803
|
-
var
|
|
6680
|
+
var DEFAULT_CONFIG4 = {
|
|
6804
6681
|
schema: {
|
|
6805
6682
|
directory: "src/schema",
|
|
6806
6683
|
exclude: ["base.ts", "index.ts", "permissions.ts", "permission-templates.ts"]
|
|
@@ -6821,7 +6698,7 @@ var DEFAULT_CONFIG5 = {
|
|
|
6821
6698
|
};
|
|
6822
6699
|
function findConfigFile(directory) {
|
|
6823
6700
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
6824
|
-
const filePath =
|
|
6701
|
+
const filePath = path7__namespace.join(directory, fileName);
|
|
6825
6702
|
if (fs10__namespace.existsSync(filePath)) {
|
|
6826
6703
|
return filePath;
|
|
6827
6704
|
}
|
|
@@ -6862,7 +6739,7 @@ async function loadConfigFile(configPath) {
|
|
|
6862
6739
|
if (!fs10__namespace.existsSync(configPath)) {
|
|
6863
6740
|
return null;
|
|
6864
6741
|
}
|
|
6865
|
-
const ext =
|
|
6742
|
+
const ext = path7__namespace.extname(configPath).toLowerCase();
|
|
6866
6743
|
if (ext === ".json") {
|
|
6867
6744
|
return loadJsonConfig(configPath);
|
|
6868
6745
|
} else if (ext === ".js" || ext === ".mjs") {
|
|
@@ -6957,8 +6834,8 @@ function validateConfig(config, configPath) {
|
|
|
6957
6834
|
}
|
|
6958
6835
|
const cwd = process.cwd();
|
|
6959
6836
|
const possiblePaths = [
|
|
6960
|
-
|
|
6961
|
-
|
|
6837
|
+
path7__namespace.resolve(cwd, config.schema.directory),
|
|
6838
|
+
path7__namespace.resolve(cwd, "shared", config.schema.directory)
|
|
6962
6839
|
];
|
|
6963
6840
|
const schemaDir = possiblePaths.find((p) => fs10__namespace.existsSync(p));
|
|
6964
6841
|
if (!schemaDir) {
|
|
@@ -6968,17 +6845,17 @@ function validateConfig(config, configPath) {
|
|
|
6968
6845
|
}
|
|
6969
6846
|
}
|
|
6970
6847
|
async function loadConfig(options = {}) {
|
|
6971
|
-
let config = { ...
|
|
6848
|
+
let config = { ...DEFAULT_CONFIG4 };
|
|
6972
6849
|
let configFilePath;
|
|
6973
6850
|
const cwd = process.cwd();
|
|
6974
6851
|
if (options.config) {
|
|
6975
|
-
const explicitPath =
|
|
6852
|
+
const explicitPath = path7__namespace.resolve(cwd, options.config);
|
|
6976
6853
|
if (!fs10__namespace.existsSync(explicitPath)) {
|
|
6977
6854
|
throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
|
|
6978
6855
|
}
|
|
6979
6856
|
configFilePath = explicitPath;
|
|
6980
6857
|
} else {
|
|
6981
|
-
const searchDirs = [cwd,
|
|
6858
|
+
const searchDirs = [cwd, path7__namespace.join(cwd, "shared")];
|
|
6982
6859
|
for (const dir of searchDirs) {
|
|
6983
6860
|
if (fs10__namespace.existsSync(dir)) {
|
|
6984
6861
|
const found = findConfigFile(dir);
|
|
@@ -7009,23 +6886,23 @@ async function loadConfig(options = {}) {
|
|
|
7009
6886
|
function getSchemaDirectory(config) {
|
|
7010
6887
|
const cwd = process.cwd();
|
|
7011
6888
|
const possiblePaths = [
|
|
7012
|
-
|
|
7013
|
-
|
|
6889
|
+
path7__namespace.resolve(cwd, config.schema.directory),
|
|
6890
|
+
path7__namespace.resolve(cwd, "shared", config.schema.directory)
|
|
7014
6891
|
];
|
|
7015
6892
|
return possiblePaths.find((p) => fs10__namespace.existsSync(p)) || possiblePaths[0];
|
|
7016
6893
|
}
|
|
7017
6894
|
function getMigrationsDirectory(config) {
|
|
7018
6895
|
const cwd = process.cwd();
|
|
7019
6896
|
const possiblePaths = [
|
|
7020
|
-
|
|
7021
|
-
|
|
6897
|
+
path7__namespace.resolve(cwd, config.migrations.directory),
|
|
6898
|
+
path7__namespace.resolve(cwd, "shared", config.migrations.directory)
|
|
7022
6899
|
];
|
|
7023
6900
|
return possiblePaths.find((p) => fs10__namespace.existsSync(p)) || possiblePaths[0];
|
|
7024
6901
|
}
|
|
7025
6902
|
function getDataDirectory(config) {
|
|
7026
6903
|
const configured = config.migrations.dataDirectory.trim();
|
|
7027
6904
|
if (configured !== "") {
|
|
7028
|
-
return
|
|
6905
|
+
return path7__namespace.resolve(process.cwd(), configured);
|
|
7029
6906
|
}
|
|
7030
6907
|
return defaultDataDirectory(getMigrationsDirectory(config));
|
|
7031
6908
|
}
|
|
@@ -7324,8 +7201,7 @@ async function executeGenerate(filters, options) {
|
|
|
7324
7201
|
logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
|
|
7325
7202
|
logInfo("Loading previous snapshot...");
|
|
7326
7203
|
const previousSnapshot = loadSnapshotWithMigrations({
|
|
7327
|
-
migrationsPath: migrationsDir
|
|
7328
|
-
workspaceRoot: process.cwd()
|
|
7204
|
+
migrationsPath: migrationsDir
|
|
7329
7205
|
});
|
|
7330
7206
|
if (!previousSnapshot) {
|
|
7331
7207
|
logInfo("No previous snapshot found - treating as empty database (first-time generation)");
|
|
@@ -7378,7 +7254,7 @@ async function executeGenerate(filters, options) {
|
|
|
7378
7254
|
}
|
|
7379
7255
|
const migrationPaths = writePlannedMigrations(planned, migrationsDir);
|
|
7380
7256
|
if (migrationPaths.length === 1) {
|
|
7381
|
-
logSuccess(`Migration file created: ${
|
|
7257
|
+
logSuccess(`Migration file created: ${path7__namespace.basename(migrationPaths[0])}`);
|
|
7382
7258
|
} else {
|
|
7383
7259
|
logSuccess(`Created ${migrationPaths.length} migration files`);
|
|
7384
7260
|
}
|
|
@@ -7404,7 +7280,7 @@ async function executeGenerate(filters, options) {
|
|
|
7404
7280
|
logInfo("Suggestions:");
|
|
7405
7281
|
console.log(" \u2022 Make sure your schema files are valid Zod schemas");
|
|
7406
7282
|
console.log(' \u2022 Run "yarn build" in the shared workspace to compile TypeScript files');
|
|
7407
|
-
console.log(
|
|
7283
|
+
console.log(" \u2022 Check that each schema file exports a defineCollection()/defineView() result (default export preferred)");
|
|
7408
7284
|
} else if (error instanceof SnapshotError) {
|
|
7409
7285
|
logError("Snapshot Error");
|
|
7410
7286
|
console.error();
|
|
@@ -7754,13 +7630,13 @@ async function executeGenerateTypes(options) {
|
|
|
7754
7630
|
const generator = new TypeGenerator(currentSchema);
|
|
7755
7631
|
const output = await withProgress("Generating TypeScript definitions...", () => Promise.resolve(generator.generate()));
|
|
7756
7632
|
const outputPath = options.output || config.typeGen.outPath;
|
|
7757
|
-
const resolvedPath =
|
|
7633
|
+
const resolvedPath = path7__namespace.resolve(process.cwd(), outputPath);
|
|
7758
7634
|
fs10__namespace.writeFileSync(resolvedPath, output);
|
|
7759
7635
|
logSuccess(`Types generated successfully at: ${outputPath}`);
|
|
7760
7636
|
logSection("\u2705 Next Steps");
|
|
7761
7637
|
console.log();
|
|
7762
7638
|
console.log(" 1. Import types in your application:");
|
|
7763
|
-
console.log(` import { TypedPocketBase } from "./${
|
|
7639
|
+
console.log(` import { TypedPocketBase } from "./${path7__namespace.basename(outputPath).replace(/\.ts$/, "")}";`);
|
|
7764
7640
|
console.log();
|
|
7765
7641
|
} catch (error) {
|
|
7766
7642
|
logError(`Failed to generate types: ${error}`);
|
|
@@ -7782,14 +7658,14 @@ function collectExecutionWarnings(files) {
|
|
|
7782
7658
|
const result = executeMigrationFile(file, store);
|
|
7783
7659
|
byFile.set(file, result.warnings);
|
|
7784
7660
|
} catch (error) {
|
|
7785
|
-
logDebug(`Skipping execution of ${
|
|
7661
|
+
logDebug(`Skipping execution of ${path7__namespace.basename(file)}: ${error}`);
|
|
7786
7662
|
byFile.set(file, []);
|
|
7787
7663
|
}
|
|
7788
7664
|
}
|
|
7789
7665
|
return byFile;
|
|
7790
7666
|
}
|
|
7791
7667
|
function reportResult(result) {
|
|
7792
|
-
const name =
|
|
7668
|
+
const name = path7__namespace.basename(result.file);
|
|
7793
7669
|
if (result.findings.length === 0) {
|
|
7794
7670
|
logDebug(`\u2713 ${name}`);
|
|
7795
7671
|
return true;
|
|
@@ -7815,7 +7691,7 @@ async function executeLint(files, options) {
|
|
|
7815
7691
|
}
|
|
7816
7692
|
const config = await loadConfig(options);
|
|
7817
7693
|
const migrationsDir = getMigrationsDirectory(config);
|
|
7818
|
-
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);
|
|
7819
7695
|
logSection("\u{1F50E} Checking goja Compatibility");
|
|
7820
7696
|
console.log();
|
|
7821
7697
|
if (targets.length === 0) {
|
|
@@ -7924,17 +7800,15 @@ function lookupAppliedMigrations(migrationsDir, dataPath) {
|
|
|
7924
7800
|
throw error;
|
|
7925
7801
|
}
|
|
7926
7802
|
}
|
|
7927
|
-
function loadStatusBaselines(currentSchema, migrationsDir,
|
|
7803
|
+
function loadStatusBaselines(currentSchema, migrationsDir, applied) {
|
|
7928
7804
|
const previousSnapshot = loadSnapshotWithMigrations({
|
|
7929
|
-
migrationsPath: migrationsDir
|
|
7930
|
-
workspaceRoot
|
|
7805
|
+
migrationsPath: migrationsDir
|
|
7931
7806
|
});
|
|
7932
7807
|
if (applied?.status !== "found" || applied.plan.inSync) {
|
|
7933
7808
|
return { previousSnapshot, appliedDiff: null };
|
|
7934
7809
|
}
|
|
7935
7810
|
const appliedSnapshot = loadSnapshotWithMigrations({
|
|
7936
7811
|
migrationsPath: migrationsDir,
|
|
7937
|
-
workspaceRoot,
|
|
7938
7812
|
appliedMigrations: applied.applied
|
|
7939
7813
|
});
|
|
7940
7814
|
return { previousSnapshot, appliedDiff: compare2(currentSchema, appliedSnapshot) };
|
|
@@ -8081,7 +7955,7 @@ async function executeStatus(options) {
|
|
|
8081
7955
|
process.exit(1);
|
|
8082
7956
|
}
|
|
8083
7957
|
logInfo("Loading previous snapshot...");
|
|
8084
|
-
const { previousSnapshot, appliedDiff } = loadStatusBaselines(currentSchema, migrationsDir,
|
|
7958
|
+
const { previousSnapshot, appliedDiff } = loadStatusBaselines(currentSchema, migrationsDir, applied);
|
|
8085
7959
|
if (applied?.status === "found" && !isJsonMode) {
|
|
8086
7960
|
const inSync = reportAppliedMigrations(applied.plan);
|
|
8087
7961
|
if (!inSync) {
|
|
@@ -8183,7 +8057,7 @@ async function executeStatus(options) {
|
|
|
8183
8057
|
console.error();
|
|
8184
8058
|
logInfo("Suggestions:");
|
|
8185
8059
|
console.log(" \u2022 Make sure your schema files are valid Zod schemas");
|
|
8186
|
-
console.log(
|
|
8060
|
+
console.log(" \u2022 Check that each schema file exports a defineCollection()/defineView() result (default export preferred)");
|
|
8187
8061
|
} else if (error instanceof SnapshotError) {
|
|
8188
8062
|
logError("Snapshot Error");
|
|
8189
8063
|
console.error();
|
|
@@ -8229,8 +8103,8 @@ Examples:
|
|
|
8229
8103
|
function getVersion() {
|
|
8230
8104
|
try {
|
|
8231
8105
|
const __filename2 = url.fileURLToPath(importMetaUrl);
|
|
8232
|
-
const __dirname =
|
|
8233
|
-
const packageJsonPath =
|
|
8106
|
+
const __dirname = path7.dirname(__filename2);
|
|
8107
|
+
const packageJsonPath = path7.join(__dirname, "../../package.json");
|
|
8234
8108
|
const packageJson = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
|
|
8235
8109
|
return packageJson.version || "0.0.0";
|
|
8236
8110
|
} catch {
|