@verdant-web/common 1.14.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/indexes.d.ts +3 -1
- package/dist/cjs/indexes.js +26 -3
- package/dist/cjs/indexes.js.map +1 -1
- package/dist/cjs/migration.d.ts +85 -18
- package/dist/cjs/migration.js +137 -13
- package/dist/cjs/migration.js.map +1 -1
- package/dist/cjs/oids.d.ts +2 -2
- package/dist/cjs/oids.js +6 -5
- package/dist/cjs/oids.js.map +1 -1
- package/dist/cjs/oids.test.js +4 -3
- package/dist/cjs/oids.test.js.map +1 -1
- package/dist/cjs/schema/fields.d.ts +2 -0
- package/dist/cjs/schema/fields.js +29 -1
- package/dist/cjs/schema/fields.js.map +1 -1
- package/dist/cjs/schema/index.d.ts +2 -2
- package/dist/cjs/schema/index.js +25 -2
- package/dist/cjs/schema/index.js.map +1 -1
- package/dist/cjs/schema/types/collection.d.ts +12 -40
- package/dist/cjs/schema/types/compounds.d.ts +6 -7
- package/dist/cjs/schema/types/fields.d.ts +12 -2
- package/dist/cjs/schema/types/shapes.d.ts +6 -6
- package/dist/cjs/schema/types/synthetics.d.ts +11 -2
- package/dist/cjs/schema/types.d.ts +3 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes.d.ts +3 -1
- package/dist/esm/indexes.js +23 -2
- package/dist/esm/indexes.js.map +1 -1
- package/dist/esm/migration.d.ts +85 -18
- package/dist/esm/migration.js +136 -13
- package/dist/esm/migration.js.map +1 -1
- package/dist/esm/oids.d.ts +2 -2
- package/dist/esm/oids.js +4 -3
- package/dist/esm/oids.js.map +1 -1
- package/dist/esm/oids.test.js +5 -4
- package/dist/esm/oids.test.js.map +1 -1
- package/dist/esm/replica.js +1 -1
- package/dist/esm/replica.js.map +1 -1
- package/dist/esm/schema/fields.d.ts +2 -0
- package/dist/esm/schema/fields.js +26 -0
- package/dist/esm/schema/fields.js.map +1 -1
- package/dist/esm/schema/index.d.ts +2 -2
- package/dist/esm/schema/index.js +25 -2
- package/dist/esm/schema/index.js.map +1 -1
- package/dist/esm/schema/types/collection.d.ts +12 -40
- package/dist/esm/schema/types/compounds.d.ts +6 -7
- package/dist/esm/schema/types/fields.d.ts +12 -2
- package/dist/esm/schema/types/shapes.d.ts +6 -6
- package/dist/esm/schema/types/synthetics.d.ts +11 -2
- package/dist/esm/schema/types.d.ts +3 -4
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/index.ts +1 -0
- package/src/indexes.ts +51 -12
- package/src/migration.ts +300 -57
- package/src/oids.test.ts +5 -4
- package/src/oids.ts +4 -3
- package/src/schema/fields.ts +18 -0
- package/src/schema/index.ts +30 -3
- package/src/schema/types/collection.ts +14 -112
- package/src/schema/types/compounds.ts +29 -18
- package/src/schema/types/fields.ts +12 -2
- package/src/schema/types/shapes.ts +13 -14
- package/src/schema/types/synthetics.ts +31 -2
- package/src/schema/types.ts +3 -5
package/src/migration.ts
CHANGED
|
@@ -11,29 +11,32 @@ import {
|
|
|
11
11
|
hasOid,
|
|
12
12
|
assignOid,
|
|
13
13
|
getOid,
|
|
14
|
+
hasDefault,
|
|
14
15
|
} from './index.js';
|
|
15
16
|
|
|
17
|
+
/**@deprecated */
|
|
16
18
|
export interface DroppedCollectionMigrationStrategy<
|
|
17
19
|
Old extends StorageCollectionSchema<any, any, any>,
|
|
18
20
|
> {
|
|
19
21
|
(old: Old): void | Promise<void>;
|
|
20
22
|
}
|
|
23
|
+
/**@deprecated */
|
|
21
24
|
export interface PreservedCollectionMigrationStrategy<
|
|
22
25
|
Old extends StorageCollectionSchema<any, any, any>,
|
|
23
26
|
New extends StorageCollectionSchema<any, any, any>,
|
|
24
27
|
> {
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
(
|
|
29
|
+
old: StorageDocument<Old>,
|
|
30
|
+
): StorageDocument<New> | Promise<StorageDocument<New>>;
|
|
28
31
|
}
|
|
29
|
-
|
|
32
|
+
/** @deprecated */
|
|
30
33
|
type MigrationStrategy<
|
|
31
34
|
Old extends StorageCollectionSchema<any, any, any>,
|
|
32
35
|
New extends StorageCollectionSchema<any, any, any>,
|
|
33
36
|
> =
|
|
34
37
|
| DroppedCollectionMigrationStrategy<Old>
|
|
35
38
|
| PreservedCollectionMigrationStrategy<Old, New>;
|
|
36
|
-
|
|
39
|
+
/** @deprecated */
|
|
37
40
|
export type MigrationsKeyedOnCollection<
|
|
38
41
|
Old extends StorageSchema<any>,
|
|
39
42
|
New extends StorageSchema<any>,
|
|
@@ -41,25 +44,17 @@ export type MigrationsKeyedOnCollection<
|
|
|
41
44
|
| PreservedCollectionMigrations<Old, New>
|
|
42
45
|
| DroppedCollectionMigrations<Old, New>;
|
|
43
46
|
|
|
47
|
+
/**@deprecated */
|
|
44
48
|
type NotInSchema<
|
|
45
49
|
Name extends string | number | symbol,
|
|
46
50
|
Schema extends StorageSchema<any>,
|
|
47
51
|
> = Name extends keyof Schema['collections'] ? never : Name;
|
|
52
|
+
/** @deprecated */
|
|
48
53
|
type InSchema<
|
|
49
54
|
Name extends string | number | symbol,
|
|
50
55
|
Schema extends StorageSchema<any>,
|
|
51
56
|
> = Name extends keyof Schema['collections'] ? Name : never;
|
|
52
|
-
|
|
53
|
-
type NewCollections<
|
|
54
|
-
Old extends StorageSchema<any>,
|
|
55
|
-
New extends StorageSchema<any>,
|
|
56
|
-
> = {
|
|
57
|
-
[Key in keyof New['collections'] as NotInSchema<
|
|
58
|
-
Key,
|
|
59
|
-
Old
|
|
60
|
-
>]: StorageCollectionSchema<any, any, any>;
|
|
61
|
-
};
|
|
62
|
-
|
|
57
|
+
/** @deprecated */
|
|
63
58
|
type DroppedCollections<
|
|
64
59
|
Old extends StorageSchema<any>,
|
|
65
60
|
New extends StorageSchema<any>,
|
|
@@ -69,7 +64,7 @@ type DroppedCollections<
|
|
|
69
64
|
New
|
|
70
65
|
>]: StorageCollectionSchema<any, any, any>;
|
|
71
66
|
};
|
|
72
|
-
|
|
67
|
+
/** @deprecated */
|
|
73
68
|
type PreservedCollections<
|
|
74
69
|
Old extends StorageSchema<any>,
|
|
75
70
|
New extends StorageSchema<any>,
|
|
@@ -79,7 +74,7 @@ type PreservedCollections<
|
|
|
79
74
|
New
|
|
80
75
|
>]: StorageCollectionSchema<any, any, any>;
|
|
81
76
|
};
|
|
82
|
-
|
|
77
|
+
/** @deprecated */
|
|
83
78
|
type DroppedCollectionMigrations<
|
|
84
79
|
Old extends StorageSchema<any>,
|
|
85
80
|
New extends StorageSchema<any>,
|
|
@@ -89,6 +84,7 @@ type DroppedCollectionMigrations<
|
|
|
89
84
|
New
|
|
90
85
|
>]: DroppedCollectionMigrationStrategy<Old['collections'][Key]>;
|
|
91
86
|
};
|
|
87
|
+
/** @deprecated */
|
|
92
88
|
type PreservedCollectionMigrations<
|
|
93
89
|
Old extends StorageSchema<any>,
|
|
94
90
|
New extends StorageSchema<any>,
|
|
@@ -102,6 +98,7 @@ type PreservedCollectionMigrations<
|
|
|
102
98
|
>;
|
|
103
99
|
};
|
|
104
100
|
|
|
101
|
+
/** @deprecated */
|
|
105
102
|
type StrategyFor<
|
|
106
103
|
Key extends string,
|
|
107
104
|
Old extends StorageSchema<any>,
|
|
@@ -113,7 +110,8 @@ type StrategyFor<
|
|
|
113
110
|
>
|
|
114
111
|
: DroppedCollectionMigrationStrategy<Old['collections'][Key]>;
|
|
115
112
|
|
|
116
|
-
|
|
113
|
+
/** @deprecated */
|
|
114
|
+
type DeprecatedMigrationRunner<
|
|
117
115
|
Old extends StorageSchema<any>,
|
|
118
116
|
New extends StorageSchema<any>,
|
|
119
117
|
> = <Collection extends Extract<keyof Old['collections'], string>>(
|
|
@@ -121,7 +119,8 @@ type MigrationRunner<
|
|
|
121
119
|
strategy: StrategyFor<Collection, Old, New>,
|
|
122
120
|
) => Promise<void>;
|
|
123
121
|
|
|
124
|
-
|
|
122
|
+
/** @deprecated */
|
|
123
|
+
type DeprecatedMigrationQueryMaker<
|
|
125
124
|
Collection extends StorageCollectionSchema<any, any, any>,
|
|
126
125
|
> = {
|
|
127
126
|
get(primaryKey: string): Promise<StorageDocument<Collection> | undefined>;
|
|
@@ -131,13 +130,15 @@ type MigrationQueryMaker<
|
|
|
131
130
|
findAll(query?: CollectionFilter): Promise<StorageDocument<Collection>[]>;
|
|
132
131
|
};
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
/** @deprecated */
|
|
134
|
+
type DeprecatedMigrationQueries<Old extends StorageSchema<any>> = {
|
|
135
|
+
[Key in keyof Old['collections']]: DeprecatedMigrationQueryMaker<
|
|
136
136
|
Old['collections'][Key]
|
|
137
137
|
>;
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
/** @deprecated */
|
|
141
|
+
type DeprecatedMigrationMutations<New extends StorageSchema> = {
|
|
141
142
|
[Key in keyof New['collections']]: {
|
|
142
143
|
put(
|
|
143
144
|
document: StorageDocumentInit<New['collections'][Key]>,
|
|
@@ -145,12 +146,12 @@ type MigrationMutations<New extends StorageSchema> = {
|
|
|
145
146
|
delete(primaryKey: string): Promise<void>;
|
|
146
147
|
};
|
|
147
148
|
};
|
|
148
|
-
|
|
149
|
-
export interface
|
|
149
|
+
/** @deprecated */
|
|
150
|
+
export interface DeprecatedMigrationTools<
|
|
150
151
|
Old extends StorageSchema<any>,
|
|
151
152
|
New extends StorageSchema<any>,
|
|
152
153
|
> {
|
|
153
|
-
migrate:
|
|
154
|
+
migrate: DeprecatedMigrationRunner<Old, New>;
|
|
154
155
|
identity: <T>(val: T) => T;
|
|
155
156
|
/**
|
|
156
157
|
* @deprecated - default field values are automatically
|
|
@@ -159,8 +160,8 @@ export interface MigrationTools<
|
|
|
159
160
|
* (old migrations can be updated!)
|
|
160
161
|
*/
|
|
161
162
|
withDefaults: (collectionName: string, value: any) => any;
|
|
162
|
-
queries:
|
|
163
|
-
mutations:
|
|
163
|
+
queries: DeprecatedMigrationQueries<Old>;
|
|
164
|
+
mutations: DeprecatedMigrationMutations<New>;
|
|
164
165
|
info: {
|
|
165
166
|
changedCollections: keyof Old['collections'][];
|
|
166
167
|
addedCollections: keyof New['collections'][];
|
|
@@ -168,27 +169,21 @@ export interface MigrationTools<
|
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
export interface MigrationEngine
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
migrate: (
|
|
176
|
-
collection: string,
|
|
177
|
-
strategy: MigrationStrategy<any, any>,
|
|
178
|
-
) => Promise<void>;
|
|
179
|
-
queries: MigrationQueries<Old>;
|
|
180
|
-
mutations: MigrationMutations<New>;
|
|
172
|
+
export interface MigrationEngine {
|
|
173
|
+
migrate: (collection: string, strategy: (val: any) => any) => Promise<void>;
|
|
174
|
+
queries: MigrationQueries<any>;
|
|
175
|
+
mutations: MigrationMutations<any>;
|
|
181
176
|
/** OIDs of any new documents created during the migration */
|
|
182
177
|
newOids: string[];
|
|
183
178
|
/** Promises that should be resolved before completing the migration */
|
|
184
179
|
awaitables: Promise<any>[];
|
|
185
180
|
log: (...messages: any[]) => void;
|
|
186
181
|
}
|
|
187
|
-
|
|
188
|
-
type
|
|
182
|
+
/** @deprecated */
|
|
183
|
+
type DeprecatedMigrationProcedure<
|
|
189
184
|
Old extends StorageSchema,
|
|
190
185
|
New extends StorageSchema,
|
|
191
|
-
> = (tools:
|
|
186
|
+
> = (tools: DeprecatedMigrationTools<Old, New>) => Promise<void>;
|
|
192
187
|
|
|
193
188
|
type EmptySchema = {
|
|
194
189
|
version: 0;
|
|
@@ -199,14 +194,16 @@ const emptySchema: EmptySchema = {
|
|
|
199
194
|
collections: {},
|
|
200
195
|
};
|
|
201
196
|
|
|
197
|
+
/** @deprecated - use createMigration */
|
|
202
198
|
export function migrate<Schema extends StorageSchema>(
|
|
203
199
|
schema: Schema,
|
|
204
|
-
procedure:
|
|
200
|
+
procedure: DeprecatedMigrationProcedure<EmptySchema, Schema>,
|
|
205
201
|
): Migration<EmptySchema, Schema>;
|
|
202
|
+
/** @deprecated = use createMigration */
|
|
206
203
|
export function migrate<Old extends StorageSchema, New extends StorageSchema>(
|
|
207
204
|
oldSchema: Old,
|
|
208
205
|
newSchema: New,
|
|
209
|
-
procedure:
|
|
206
|
+
procedure: DeprecatedMigrationProcedure<Old, New>,
|
|
210
207
|
): Migration<Old, New>;
|
|
211
208
|
export function migrate(
|
|
212
209
|
oldSchemaOrNewSchema: any,
|
|
@@ -294,7 +291,7 @@ export function migrate(
|
|
|
294
291
|
|
|
295
292
|
return {
|
|
296
293
|
version: newSchema.version,
|
|
297
|
-
migrate: async (engine: MigrationEngine
|
|
294
|
+
migrate: async (engine: MigrationEngine) => {
|
|
298
295
|
const migratedCollections: string[] = [];
|
|
299
296
|
await procedure({
|
|
300
297
|
migrate: async (collection: any, strategy: any) => {
|
|
@@ -378,7 +375,7 @@ export interface Migration<
|
|
|
378
375
|
version: number;
|
|
379
376
|
oldSchema: Old;
|
|
380
377
|
newSchema: New;
|
|
381
|
-
migrate: (engine: MigrationEngine
|
|
378
|
+
migrate: (engine: MigrationEngine) => Promise<void>;
|
|
382
379
|
/** Collections which are added in the new schema and not present in the old */
|
|
383
380
|
addedCollections: string[];
|
|
384
381
|
/** Collections which were removed from the old schema */
|
|
@@ -403,21 +400,12 @@ function getIndexes<Coll extends StorageCollectionSchema<any, any, any>>(
|
|
|
403
400
|
collection: Coll | undefined,
|
|
404
401
|
): MigrationIndexDescription[] {
|
|
405
402
|
if (!collection) return [];
|
|
406
|
-
const fields = Object.keys(collection.fields)
|
|
407
|
-
.filter((key) => collection.fields[key].indexed)
|
|
408
|
-
.map((key) => ({
|
|
409
|
-
name: key,
|
|
410
|
-
multiEntry: collection.fields[key].type === 'array',
|
|
411
|
-
synthetic: false,
|
|
412
|
-
compound: false,
|
|
413
|
-
}));
|
|
414
403
|
|
|
415
404
|
return [
|
|
416
|
-
...
|
|
417
|
-
...Object.keys(collection.synthetics || {}).map((key) => ({
|
|
405
|
+
...Object.keys(collection.indexes || {}).map((key) => ({
|
|
418
406
|
name: key,
|
|
419
407
|
multiEntry: ['array', 'string[]', 'number[]', 'boolean[]'].includes(
|
|
420
|
-
collection.
|
|
408
|
+
collection.indexes[key].type,
|
|
421
409
|
),
|
|
422
410
|
synthetic: true,
|
|
423
411
|
compound: false,
|
|
@@ -426,7 +414,7 @@ function getIndexes<Coll extends StorageCollectionSchema<any, any, any>>(
|
|
|
426
414
|
name: key,
|
|
427
415
|
multiEntry: collection.compounds[key].of.some(
|
|
428
416
|
(fieldName: string) =>
|
|
429
|
-
(collection.fields[fieldName] || collection.
|
|
417
|
+
(collection.fields[fieldName] || collection.indexes[fieldName])
|
|
430
418
|
.type === 'array',
|
|
431
419
|
),
|
|
432
420
|
synthetic: false,
|
|
@@ -435,9 +423,11 @@ function getIndexes<Coll extends StorageCollectionSchema<any, any, any>>(
|
|
|
435
423
|
];
|
|
436
424
|
}
|
|
437
425
|
|
|
426
|
+
/** @deprecated - use createMigration with no procedure function */
|
|
438
427
|
export function createDefaultMigration(
|
|
439
428
|
schema: StorageSchema,
|
|
440
429
|
): Migration<{ version: 0; collections: {} }>;
|
|
430
|
+
/** @deprecated - use createMigration with no procedure function */
|
|
441
431
|
export function createDefaultMigration<Old extends StorageSchema>(
|
|
442
432
|
oldSchema: Old,
|
|
443
433
|
newSchema: StorageSchema,
|
|
@@ -461,3 +451,256 @@ export function createDefaultMigration(
|
|
|
461
451
|
}
|
|
462
452
|
});
|
|
463
453
|
}
|
|
454
|
+
|
|
455
|
+
/** New, simpler type-safety migration tools */
|
|
456
|
+
type DocumentShape<Init = any, Snapshot = any> = {
|
|
457
|
+
init: Init;
|
|
458
|
+
snapshot: Snapshot;
|
|
459
|
+
};
|
|
460
|
+
type SchemaDocuments = Record<string, DocumentShape>;
|
|
461
|
+
type CollectionProcedure<OldSnapshot, NewInit> = {
|
|
462
|
+
(old: OldSnapshot): NewInit | Promise<NewInit>;
|
|
463
|
+
};
|
|
464
|
+
type MigrationQueries<Old extends SchemaDocuments> = {
|
|
465
|
+
[Key in keyof Old]: {
|
|
466
|
+
get(primaryKey: string): Promise<Old[Key]['snapshot'] | undefined>;
|
|
467
|
+
findOne(query: CollectionFilter): Promise<Old[Key]['snapshot'] | undefined>;
|
|
468
|
+
findAll(query?: CollectionFilter): Promise<Old[Key]['snapshot'][]>;
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
type MigrationMutations<New extends SchemaDocuments> = {
|
|
472
|
+
[Key in keyof New]: {
|
|
473
|
+
put(document: New[Key]['init']): Promise<New[Key]['snapshot']>;
|
|
474
|
+
delete(primaryKey: string): Promise<void>;
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
type MigrationTools<
|
|
478
|
+
Old extends SchemaDocuments,
|
|
479
|
+
New extends SchemaDocuments,
|
|
480
|
+
> = {
|
|
481
|
+
/**
|
|
482
|
+
* Process a change in a collection's documents by taking in each existing
|
|
483
|
+
* document and returning its new shape. This is typed so you can be
|
|
484
|
+
* confident the proper transformations are made.
|
|
485
|
+
*/
|
|
486
|
+
migrate: <Collection extends keyof Old & keyof New>(
|
|
487
|
+
collection: Collection,
|
|
488
|
+
strategy: CollectionProcedure<
|
|
489
|
+
Old[Collection]['snapshot'],
|
|
490
|
+
New[Collection]['init']
|
|
491
|
+
>,
|
|
492
|
+
) => Promise<void>;
|
|
493
|
+
mutations: MigrationMutations<New>;
|
|
494
|
+
queries: MigrationQueries<Old>;
|
|
495
|
+
info: {
|
|
496
|
+
changedCollections: (keyof Old & keyof New)[];
|
|
497
|
+
addedCollections: (keyof New)[];
|
|
498
|
+
removedCollections: (keyof Old)[];
|
|
499
|
+
};
|
|
500
|
+
};
|
|
501
|
+
type MigrationProcedure<
|
|
502
|
+
Old extends SchemaDocuments,
|
|
503
|
+
New extends SchemaDocuments,
|
|
504
|
+
> = {
|
|
505
|
+
(tools: MigrationTools<Old, New>): Promise<void>;
|
|
506
|
+
};
|
|
507
|
+
type InitialMigrationTools<New extends SchemaDocuments> = {
|
|
508
|
+
mutations: MigrationMutations<New>;
|
|
509
|
+
};
|
|
510
|
+
type InitialMigrationProcedure<New extends SchemaDocuments> = {
|
|
511
|
+
(tools: InitialMigrationTools<New>): Promise<void>;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
export function createMigration<New extends SchemaDocuments>(
|
|
515
|
+
newSchema: StorageSchema,
|
|
516
|
+
procedure?: InitialMigrationProcedure<New>,
|
|
517
|
+
): any;
|
|
518
|
+
export function createMigration<
|
|
519
|
+
Old extends SchemaDocuments,
|
|
520
|
+
New extends SchemaDocuments,
|
|
521
|
+
>(
|
|
522
|
+
oldSchema: StorageSchema,
|
|
523
|
+
newSchema: StorageSchema,
|
|
524
|
+
procedure?: MigrationProcedure<Old, New>,
|
|
525
|
+
): any;
|
|
526
|
+
export function createMigration(
|
|
527
|
+
maybeOldSchema: StorageSchema,
|
|
528
|
+
maybeNewSchemaOrProcedure?:
|
|
529
|
+
| StorageSchema
|
|
530
|
+
| InitialMigrationProcedure<SchemaDocuments>,
|
|
531
|
+
maybeProcedure?: MigrationProcedure<SchemaDocuments, SchemaDocuments>,
|
|
532
|
+
): any {
|
|
533
|
+
const isProcedureSecondArgument =
|
|
534
|
+
typeof maybeNewSchemaOrProcedure === 'function' ||
|
|
535
|
+
maybeNewSchemaOrProcedure === undefined;
|
|
536
|
+
const oldSchema = isProcedureSecondArgument ? emptySchema : maybeOldSchema;
|
|
537
|
+
const newSchema = isProcedureSecondArgument
|
|
538
|
+
? maybeOldSchema
|
|
539
|
+
: maybeNewSchemaOrProcedure;
|
|
540
|
+
const procedure = isProcedureSecondArgument
|
|
541
|
+
? maybeNewSchemaOrProcedure
|
|
542
|
+
: maybeProcedure;
|
|
543
|
+
assert(oldSchema, 'Invalid arguments to createMigration');
|
|
544
|
+
assert(newSchema, 'Invalid arguments to createMigration');
|
|
545
|
+
const {
|
|
546
|
+
changedCollections,
|
|
547
|
+
addedCollections,
|
|
548
|
+
removedCollections,
|
|
549
|
+
addedIndexes,
|
|
550
|
+
removedIndexes,
|
|
551
|
+
autoMigratedCollections,
|
|
552
|
+
autoMigration,
|
|
553
|
+
} = getMigrationInfo(oldSchema, newSchema);
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
version: newSchema.version,
|
|
557
|
+
migrate: async (engine: MigrationEngine) => {
|
|
558
|
+
const migratedCollections: string[] = [];
|
|
559
|
+
const migrate = async (collection: any, strategy: any) => {
|
|
560
|
+
const auto = autoMigration(collection);
|
|
561
|
+
const wrapped = async (val: any) => {
|
|
562
|
+
const baseValue = await strategy(val);
|
|
563
|
+
// assign OID from original value in case user's strategy
|
|
564
|
+
// involves cloning
|
|
565
|
+
assignOid(baseValue, getOid(val));
|
|
566
|
+
const result = auto(baseValue);
|
|
567
|
+
return result;
|
|
568
|
+
};
|
|
569
|
+
// @ts-ignore
|
|
570
|
+
await engine.migrate(collection, wrapped);
|
|
571
|
+
migratedCollections.push(collection);
|
|
572
|
+
// since the user migrated this one and we wrap their
|
|
573
|
+
// strategy with auto-migration, we can remove it from
|
|
574
|
+
// the auto-migration list
|
|
575
|
+
autoMigratedCollections.delete(collection);
|
|
576
|
+
};
|
|
577
|
+
await procedure?.({
|
|
578
|
+
migrate,
|
|
579
|
+
info: {
|
|
580
|
+
changedCollections,
|
|
581
|
+
addedCollections,
|
|
582
|
+
removedCollections,
|
|
583
|
+
},
|
|
584
|
+
queries: engine.queries,
|
|
585
|
+
mutations: engine.mutations,
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// mandatory migration of fields which had defaults added or
|
|
589
|
+
// fields removed but weren't migrated by the user
|
|
590
|
+
|
|
591
|
+
if (newSchema.version > 1) {
|
|
592
|
+
engine.log(
|
|
593
|
+
'debug',
|
|
594
|
+
'auto-migrating collections with new defaults',
|
|
595
|
+
autoMigratedCollections,
|
|
596
|
+
);
|
|
597
|
+
for (const name of autoMigratedCollections) {
|
|
598
|
+
await engine.migrate(name, autoMigration(name));
|
|
599
|
+
migratedCollections.push(name);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const unmigrated = changedCollections.filter(
|
|
603
|
+
(collection) => !migratedCollections.includes(collection),
|
|
604
|
+
);
|
|
605
|
+
if (unmigrated.length > 0) {
|
|
606
|
+
// TODO: does this deserve a full-on error?
|
|
607
|
+
console.error(
|
|
608
|
+
`Unmigrated changed collections from version ${oldSchema.version} to version ${newSchema.version}:`,
|
|
609
|
+
unmigrated,
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
removedCollections,
|
|
615
|
+
addedIndexes,
|
|
616
|
+
removedIndexes,
|
|
617
|
+
allCollections: Object.keys(newSchema.collections),
|
|
618
|
+
changedCollections,
|
|
619
|
+
addedCollections,
|
|
620
|
+
oldCollections: Object.keys(oldSchema.collections),
|
|
621
|
+
oldSchema,
|
|
622
|
+
newSchema,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// common tools
|
|
627
|
+
function getMigrationInfo(oldSchema: StorageSchema, newSchema: StorageSchema) {
|
|
628
|
+
const changedCollections: string[] = Object.keys(
|
|
629
|
+
newSchema.collections,
|
|
630
|
+
).filter(
|
|
631
|
+
(key) =>
|
|
632
|
+
oldSchema.collections[key] &&
|
|
633
|
+
stableStringify(oldSchema.collections[key]) !==
|
|
634
|
+
stableStringify(newSchema.collections[key]),
|
|
635
|
+
);
|
|
636
|
+
const removedCollections: string[] = Object.keys(
|
|
637
|
+
oldSchema.collections,
|
|
638
|
+
).filter((key) => !newSchema.collections[key]);
|
|
639
|
+
const addedCollections = Object.keys(newSchema.collections).filter(
|
|
640
|
+
(key) => !oldSchema.collections[key],
|
|
641
|
+
);
|
|
642
|
+
// collections which added one or more field defaults
|
|
643
|
+
const autoMigratedCollections = new Set<string>();
|
|
644
|
+
for (const collection of changedCollections) {
|
|
645
|
+
const oldFields = oldSchema.collections[collection].fields;
|
|
646
|
+
const newFields = newSchema.collections[collection].fields;
|
|
647
|
+
// a new default was added - we can auto-migrate it
|
|
648
|
+
if (
|
|
649
|
+
Object.keys(newFields).some(
|
|
650
|
+
(key) => !hasDefault(oldFields[key]) && hasDefault(newFields[key]),
|
|
651
|
+
)
|
|
652
|
+
) {
|
|
653
|
+
autoMigratedCollections.add(collection);
|
|
654
|
+
}
|
|
655
|
+
// a field was removed - we can auto-migrate it
|
|
656
|
+
if (Object.keys(oldFields).some((key) => !newFields[key])) {
|
|
657
|
+
autoMigratedCollections.add(collection);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const addedIndexes: Record<string, MigrationIndexDescription[]> = {};
|
|
662
|
+
const removedIndexes: Record<string, MigrationIndexDescription[]> = {};
|
|
663
|
+
for (const changed of [...changedCollections, ...addedCollections]) {
|
|
664
|
+
const oldIndexes = getIndexes(oldSchema.collections[changed]);
|
|
665
|
+
const newIndexes = getIndexes(newSchema.collections[changed]);
|
|
666
|
+
const added = newIndexes.filter(
|
|
667
|
+
(index) => !oldIndexes.find((i) => i.name === index.name),
|
|
668
|
+
);
|
|
669
|
+
const removed = oldIndexes.filter(
|
|
670
|
+
(index) => !newIndexes.find((i) => i.name === index.name),
|
|
671
|
+
);
|
|
672
|
+
if (added.length > 0) {
|
|
673
|
+
addedIndexes[changed] = added;
|
|
674
|
+
// FIXME: don't o(n^2) this
|
|
675
|
+
if (changedCollections.includes(changed)) {
|
|
676
|
+
autoMigratedCollections.add(changed);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (removed.length > 0) {
|
|
680
|
+
removedIndexes[changed] = removed;
|
|
681
|
+
// FIXME: don't o(n^2) this
|
|
682
|
+
if (changedCollections.includes(changed)) {
|
|
683
|
+
autoMigratedCollections.add(changed);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const withDefaults = (collectionName: string, val: any) => {
|
|
689
|
+
return addFieldDefaults(newSchema.collections[collectionName], val);
|
|
690
|
+
};
|
|
691
|
+
const autoMigration = (collectionName: string) => (val: any) => {
|
|
692
|
+
const collection = newSchema.collections[collectionName];
|
|
693
|
+
return addFieldDefaults(collection, removeExtraProperties(collection, val));
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
return {
|
|
697
|
+
changedCollections,
|
|
698
|
+
addedCollections,
|
|
699
|
+
removedCollections,
|
|
700
|
+
addedIndexes,
|
|
701
|
+
removedIndexes,
|
|
702
|
+
autoMigratedCollections,
|
|
703
|
+
withDefaults,
|
|
704
|
+
autoMigration,
|
|
705
|
+
};
|
|
706
|
+
}
|
package/src/oids.test.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
createSubOid,
|
|
11
11
|
decomposeOid,
|
|
12
12
|
getOid,
|
|
13
|
-
|
|
13
|
+
getOidSubIdRange,
|
|
14
14
|
getOidRoot,
|
|
15
15
|
hasOid,
|
|
16
16
|
maybeGetOidProperty,
|
|
@@ -262,11 +262,10 @@ describe('computing a range of oids for a whole object set', () => {
|
|
|
262
262
|
return oid >= start && oid <= end;
|
|
263
263
|
}
|
|
264
264
|
it('should include the root object and any possible sub object oid', () => {
|
|
265
|
-
const [start, end] =
|
|
266
|
-
expect(start).toEqual('test/a');
|
|
265
|
+
const [start, end] = getOidSubIdRange('test/a');
|
|
266
|
+
expect(start).toEqual('test/a:');
|
|
267
267
|
expect(end).toEqual('test/a:\uffff');
|
|
268
268
|
expect(start < end).toBe(true);
|
|
269
|
-
expect(isWithin('test/a', start, end)).toBe(true);
|
|
270
269
|
expect(isWithin('test/a:0', start, end)).toBe(true);
|
|
271
270
|
expect(isWithin('test/a:1', start, end)).toBe(true);
|
|
272
271
|
expect(isWithin('test/a:zzzzzzzzzzzzzzzzzzzzzzz', start, end)).toBe(true);
|
|
@@ -274,6 +273,8 @@ describe('computing a range of oids for a whole object set', () => {
|
|
|
274
273
|
expect(isWithin('test1/a', start, end)).toBe(false);
|
|
275
274
|
expect(isWithin('test/b', start, end)).toBe(false);
|
|
276
275
|
expect(isWithin('test/ ', start, end)).toBe(false);
|
|
276
|
+
expect(isWithin('test/a1', start, end)).toBe(false);
|
|
277
|
+
expect(isWithin('test/a1:3', start, end)).toBe(false);
|
|
277
278
|
});
|
|
278
279
|
});
|
|
279
280
|
|
package/src/oids.ts
CHANGED
|
@@ -437,11 +437,12 @@ export function getOidRoot(oid: ObjectIdentifier) {
|
|
|
437
437
|
|
|
438
438
|
/**
|
|
439
439
|
* Returns an inclusive range of OIDs that represent
|
|
440
|
-
*
|
|
440
|
+
* all of an OID's possible sub-objects.
|
|
441
441
|
*/
|
|
442
|
-
export function
|
|
442
|
+
export function getOidSubIdRange(oid: ObjectIdentifier) {
|
|
443
443
|
const root = getOidRoot(oid);
|
|
444
|
-
|
|
444
|
+
const lastSubId = createSubOid(root, () => '\uffff');
|
|
445
|
+
return [`${root}${RANDOM_SEPARATOR}`, lastSubId];
|
|
445
446
|
}
|
|
446
447
|
|
|
447
448
|
export function getRoots(oids: ObjectIdentifier[]) {
|
package/src/schema/fields.ts
CHANGED
|
@@ -8,6 +8,24 @@ export function isNullable(field: StorageFieldSchema) {
|
|
|
8
8
|
return field.nullable;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export function hasDefault(field: StorageFieldSchema | undefined) {
|
|
12
|
+
if (!field) return false;
|
|
13
|
+
if (field.type === 'map') return true;
|
|
14
|
+
if (field.type === 'array') return true;
|
|
15
|
+
if (field.type === 'file') return false;
|
|
16
|
+
if (field.type === 'object') return true;
|
|
17
|
+
return field.default !== undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isIndexed(field: StorageFieldSchema) {
|
|
21
|
+
if (field.type === 'map') return false;
|
|
22
|
+
if (field.type === 'object') return false;
|
|
23
|
+
if (field.type === 'array') return false;
|
|
24
|
+
if (field.type === 'file') return false;
|
|
25
|
+
if (field.type === 'any') return false;
|
|
26
|
+
return field.indexed;
|
|
27
|
+
}
|
|
28
|
+
|
|
11
29
|
export function addFieldDefaults(
|
|
12
30
|
collection: StorageCollectionSchema,
|
|
13
31
|
value: any,
|
package/src/schema/index.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { isIndexed } from './fields.js';
|
|
1
2
|
import {
|
|
2
3
|
CollectionCompoundIndices,
|
|
3
4
|
StorageCollectionSchema,
|
|
5
|
+
StorageDirectSyntheticSchema,
|
|
4
6
|
StorageFieldsSchema,
|
|
5
7
|
StorageSchema,
|
|
8
|
+
StorageSyntheticIndexSchema,
|
|
6
9
|
StorageSyntheticIndices,
|
|
7
10
|
} from './types.js';
|
|
8
11
|
|
|
@@ -10,15 +13,39 @@ export function collection<
|
|
|
10
13
|
Fields extends StorageFieldsSchema,
|
|
11
14
|
Synthetics extends StorageSyntheticIndices<Fields>,
|
|
12
15
|
Compounds extends CollectionCompoundIndices<Fields, Synthetics>,
|
|
13
|
-
>(
|
|
14
|
-
|
|
16
|
+
>({
|
|
17
|
+
synthetics,
|
|
18
|
+
indexes,
|
|
19
|
+
...input
|
|
20
|
+
}: StorageCollectionSchema<
|
|
21
|
+
Fields,
|
|
22
|
+
Synthetics,
|
|
23
|
+
Compounds
|
|
24
|
+
>): StorageCollectionSchema<Fields, Synthetics, Compounds> {
|
|
25
|
+
// back compat - copy synthetics in with indexes
|
|
26
|
+
const finalIndexes = { ...synthetics, ...indexes };
|
|
27
|
+
// add all indexed fields into the synthetic indices (back compat)
|
|
28
|
+
for (const [key, field] of Object.entries(input.fields)) {
|
|
29
|
+
if (isIndexed(field)) {
|
|
30
|
+
finalIndexes[key] = {
|
|
31
|
+
field: key,
|
|
32
|
+
} as StorageDirectSyntheticSchema<Fields>;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...input,
|
|
37
|
+
indexes: finalIndexes as Synthetics,
|
|
38
|
+
};
|
|
15
39
|
}
|
|
16
40
|
|
|
17
41
|
export function schema<
|
|
42
|
+
// Fields extends StorageFieldsSchema,
|
|
43
|
+
// Indexes extends StorageSyntheticIndices<Fields>,
|
|
44
|
+
// Compounds extends CollectionCompoundIndices<Fields, Indexes>,
|
|
18
45
|
Schema extends StorageSchema<{
|
|
19
46
|
[key: string]: StorageCollectionSchema<any, any, any>;
|
|
20
47
|
}>,
|
|
21
|
-
>(input: Schema) {
|
|
48
|
+
>(input: Schema): StorageSchema {
|
|
22
49
|
return input;
|
|
23
50
|
}
|
|
24
51
|
|