@verdant-web/common 1.11.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.
Files changed (241) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/EventSubscriber.d.ts +17 -0
  3. package/dist/cjs/EventSubscriber.js +67 -0
  4. package/dist/cjs/EventSubscriber.js.map +1 -0
  5. package/dist/cjs/baseline.d.ts +5 -0
  6. package/dist/cjs/baseline.js +3 -0
  7. package/dist/cjs/baseline.js.map +1 -0
  8. package/dist/cjs/batching.d.ts +41 -0
  9. package/dist/cjs/batching.js +89 -0
  10. package/dist/cjs/batching.js.map +1 -0
  11. package/dist/cjs/files.d.ts +19 -0
  12. package/dist/cjs/files.js +19 -0
  13. package/dist/cjs/files.js.map +1 -0
  14. package/dist/cjs/files.test.d.ts +1 -0
  15. package/dist/cjs/files.test.js +81 -0
  16. package/dist/cjs/files.test.js.map +1 -0
  17. package/dist/cjs/index.d.ts +18 -0
  18. package/dist/cjs/index.js +36 -0
  19. package/dist/cjs/index.js.map +1 -0
  20. package/dist/cjs/indexes.d.ts +14 -0
  21. package/dist/cjs/indexes.js +116 -0
  22. package/dist/cjs/indexes.js.map +1 -0
  23. package/dist/cjs/indexes.test.d.ts +1 -0
  24. package/dist/cjs/indexes.test.js +128 -0
  25. package/dist/cjs/indexes.test.js.map +1 -0
  26. package/dist/cjs/migration.d.ts +98 -0
  27. package/dist/cjs/migration.js +128 -0
  28. package/dist/cjs/migration.js.map +1 -0
  29. package/dist/cjs/oids.d.ts +87 -0
  30. package/dist/cjs/oids.js +375 -0
  31. package/dist/cjs/oids.js.map +1 -0
  32. package/dist/cjs/oids.test.d.ts +1 -0
  33. package/dist/cjs/oids.test.js +282 -0
  34. package/dist/cjs/oids.test.js.map +1 -0
  35. package/dist/cjs/operation.d.ts +139 -0
  36. package/dist/cjs/operation.js +533 -0
  37. package/dist/cjs/operation.js.map +1 -0
  38. package/dist/cjs/operation.test.d.ts +1 -0
  39. package/dist/cjs/operation.test.js +827 -0
  40. package/dist/cjs/operation.test.js.map +1 -0
  41. package/dist/cjs/patch.d.ts +26 -0
  42. package/dist/cjs/patch.js +227 -0
  43. package/dist/cjs/patch.js.map +1 -0
  44. package/dist/cjs/presence.d.ts +27 -0
  45. package/dist/cjs/presence.js +3 -0
  46. package/dist/cjs/presence.js.map +1 -0
  47. package/dist/cjs/protocol.d.ts +145 -0
  48. package/dist/cjs/protocol.js +4 -0
  49. package/dist/cjs/protocol.js.map +1 -0
  50. package/dist/cjs/refs.d.ts +5 -0
  51. package/dist/cjs/refs.js +22 -0
  52. package/dist/cjs/refs.js.map +1 -0
  53. package/dist/cjs/replica.d.ts +28 -0
  54. package/dist/cjs/replica.js +29 -0
  55. package/dist/cjs/replica.js.map +1 -0
  56. package/dist/cjs/schema/defaults.d.ts +4 -0
  57. package/dist/cjs/schema/defaults.js +72 -0
  58. package/dist/cjs/schema/defaults.js.map +1 -0
  59. package/dist/cjs/schema/defaults.test.d.ts +1 -0
  60. package/dist/cjs/schema/defaults.test.js +21 -0
  61. package/dist/cjs/schema/defaults.test.js.map +1 -0
  62. package/dist/cjs/schema/index.d.ts +8 -0
  63. package/dist/cjs/schema/index.js +29 -0
  64. package/dist/cjs/schema/index.js.map +1 -0
  65. package/dist/cjs/schema/indexFilters.d.ts +6 -0
  66. package/dist/cjs/schema/indexFilters.js +31 -0
  67. package/dist/cjs/schema/indexFilters.js.map +1 -0
  68. package/dist/cjs/schema/types/collection.d.ts +51 -0
  69. package/dist/cjs/schema/types/collection.js +3 -0
  70. package/dist/cjs/schema/types/collection.js.map +1 -0
  71. package/dist/cjs/schema/types/compounds.d.ts +12 -0
  72. package/dist/cjs/schema/types/compounds.js +3 -0
  73. package/dist/cjs/schema/types/compounds.js.map +1 -0
  74. package/dist/cjs/schema/types/fields.d.ts +59 -0
  75. package/dist/cjs/schema/types/fields.js +3 -0
  76. package/dist/cjs/schema/types/fields.js.map +1 -0
  77. package/dist/cjs/schema/types/filters.d.ts +29 -0
  78. package/dist/cjs/schema/types/filters.js +3 -0
  79. package/dist/cjs/schema/types/filters.js.map +1 -0
  80. package/dist/cjs/schema/types/shapes.d.ts +21 -0
  81. package/dist/cjs/schema/types/shapes.js +3 -0
  82. package/dist/cjs/schema/types/shapes.js.map +1 -0
  83. package/dist/cjs/schema/types/synthetics.d.ts +28 -0
  84. package/dist/cjs/schema/types/synthetics.js +3 -0
  85. package/dist/cjs/schema/types/synthetics.js.map +1 -0
  86. package/dist/cjs/schema/types.d.ts +18 -0
  87. package/dist/cjs/schema/types.js +23 -0
  88. package/dist/cjs/schema/types.js.map +1 -0
  89. package/dist/cjs/timestamp.d.ts +48 -0
  90. package/dist/cjs/timestamp.js +244 -0
  91. package/dist/cjs/timestamp.js.map +1 -0
  92. package/dist/cjs/timestamp.test.d.ts +1 -0
  93. package/dist/cjs/timestamp.test.js +94 -0
  94. package/dist/cjs/timestamp.test.js.map +1 -0
  95. package/dist/cjs/undo.d.ts +3 -0
  96. package/dist/cjs/undo.js +199 -0
  97. package/dist/cjs/undo.js.map +1 -0
  98. package/dist/cjs/utils.d.ts +15 -0
  99. package/dist/cjs/utils.js +128 -0
  100. package/dist/cjs/utils.js.map +1 -0
  101. package/dist/cjs/utils.test.d.ts +1 -0
  102. package/dist/cjs/utils.test.js +37 -0
  103. package/dist/cjs/utils.test.js.map +1 -0
  104. package/dist/esm/EventSubscriber.d.ts +17 -0
  105. package/dist/esm/EventSubscriber.js +63 -0
  106. package/dist/esm/EventSubscriber.js.map +1 -0
  107. package/dist/esm/baseline.d.ts +5 -0
  108. package/dist/esm/baseline.js +2 -0
  109. package/dist/esm/baseline.js.map +1 -0
  110. package/dist/esm/batching.d.ts +41 -0
  111. package/dist/esm/batching.js +84 -0
  112. package/dist/esm/batching.js.map +1 -0
  113. package/dist/esm/files.d.ts +19 -0
  114. package/dist/esm/files.js +13 -0
  115. package/dist/esm/files.js.map +1 -0
  116. package/dist/esm/files.test.d.ts +1 -0
  117. package/dist/esm/files.test.js +79 -0
  118. package/dist/esm/files.test.js.map +1 -0
  119. package/dist/esm/index.d.ts +18 -0
  120. package/dist/esm/index.js +16 -0
  121. package/dist/esm/index.js.map +1 -0
  122. package/dist/esm/indexes.d.ts +14 -0
  123. package/dist/esm/indexes.js +106 -0
  124. package/dist/esm/indexes.js.map +1 -0
  125. package/dist/esm/indexes.test.d.ts +1 -0
  126. package/dist/esm/indexes.test.js +126 -0
  127. package/dist/esm/indexes.test.js.map +1 -0
  128. package/dist/esm/migration.d.ts +98 -0
  129. package/dist/esm/migration.js +122 -0
  130. package/dist/esm/migration.js.map +1 -0
  131. package/dist/esm/oids.d.ts +87 -0
  132. package/dist/esm/oids.js +348 -0
  133. package/dist/esm/oids.js.map +1 -0
  134. package/dist/esm/oids.test.d.ts +1 -0
  135. package/dist/esm/oids.test.js +280 -0
  136. package/dist/esm/oids.test.js.map +1 -0
  137. package/dist/esm/operation.d.ts +139 -0
  138. package/dist/esm/operation.js +518 -0
  139. package/dist/esm/operation.js.map +1 -0
  140. package/dist/esm/operation.test.d.ts +1 -0
  141. package/dist/esm/operation.test.js +825 -0
  142. package/dist/esm/operation.test.js.map +1 -0
  143. package/dist/esm/patch.d.ts +26 -0
  144. package/dist/esm/patch.js +223 -0
  145. package/dist/esm/patch.js.map +1 -0
  146. package/dist/esm/presence.d.ts +27 -0
  147. package/dist/esm/presence.js +2 -0
  148. package/dist/esm/presence.js.map +1 -0
  149. package/dist/esm/protocol.d.ts +145 -0
  150. package/dist/esm/protocol.js +3 -0
  151. package/dist/esm/protocol.js.map +1 -0
  152. package/dist/esm/refs.d.ts +5 -0
  153. package/dist/esm/refs.js +17 -0
  154. package/dist/esm/refs.js.map +1 -0
  155. package/dist/esm/replica.d.ts +28 -0
  156. package/dist/esm/replica.js +26 -0
  157. package/dist/esm/replica.js.map +1 -0
  158. package/dist/esm/schema/defaults.d.ts +4 -0
  159. package/dist/esm/schema/defaults.js +66 -0
  160. package/dist/esm/schema/defaults.js.map +1 -0
  161. package/dist/esm/schema/defaults.test.d.ts +1 -0
  162. package/dist/esm/schema/defaults.test.js +19 -0
  163. package/dist/esm/schema/defaults.test.js.map +1 -0
  164. package/dist/esm/schema/index.d.ts +8 -0
  165. package/dist/esm/schema/index.js +10 -0
  166. package/dist/esm/schema/index.js.map +1 -0
  167. package/dist/esm/schema/indexFilters.d.ts +6 -0
  168. package/dist/esm/schema/indexFilters.js +23 -0
  169. package/dist/esm/schema/indexFilters.js.map +1 -0
  170. package/dist/esm/schema/types/collection.d.ts +51 -0
  171. package/dist/esm/schema/types/collection.js +2 -0
  172. package/dist/esm/schema/types/collection.js.map +1 -0
  173. package/dist/esm/schema/types/compounds.d.ts +12 -0
  174. package/dist/esm/schema/types/compounds.js +2 -0
  175. package/dist/esm/schema/types/compounds.js.map +1 -0
  176. package/dist/esm/schema/types/fields.d.ts +59 -0
  177. package/dist/esm/schema/types/fields.js +2 -0
  178. package/dist/esm/schema/types/fields.js.map +1 -0
  179. package/dist/esm/schema/types/filters.d.ts +29 -0
  180. package/dist/esm/schema/types/filters.js +2 -0
  181. package/dist/esm/schema/types/filters.js.map +1 -0
  182. package/dist/esm/schema/types/shapes.d.ts +21 -0
  183. package/dist/esm/schema/types/shapes.js +2 -0
  184. package/dist/esm/schema/types/shapes.js.map +1 -0
  185. package/dist/esm/schema/types/synthetics.d.ts +28 -0
  186. package/dist/esm/schema/types/synthetics.js +2 -0
  187. package/dist/esm/schema/types/synthetics.js.map +1 -0
  188. package/dist/esm/schema/types.d.ts +18 -0
  189. package/dist/esm/schema/types.js +7 -0
  190. package/dist/esm/schema/types.js.map +1 -0
  191. package/dist/esm/timestamp.d.ts +48 -0
  192. package/dist/esm/timestamp.js +229 -0
  193. package/dist/esm/timestamp.js.map +1 -0
  194. package/dist/esm/timestamp.test.d.ts +1 -0
  195. package/dist/esm/timestamp.test.js +92 -0
  196. package/dist/esm/timestamp.test.js.map +1 -0
  197. package/dist/esm/undo.d.ts +3 -0
  198. package/dist/esm/undo.js +195 -0
  199. package/dist/esm/undo.js.map +1 -0
  200. package/dist/esm/utils.d.ts +15 -0
  201. package/dist/esm/utils.js +114 -0
  202. package/dist/esm/utils.js.map +1 -0
  203. package/dist/esm/utils.test.d.ts +1 -0
  204. package/dist/esm/utils.test.js +35 -0
  205. package/dist/esm/utils.test.js.map +1 -0
  206. package/dist/tsconfig-cjs.tsbuildinfo +1 -0
  207. package/dist/tsconfig.tsbuildinfo +1 -0
  208. package/package.json +38 -0
  209. package/src/EventSubscriber.ts +81 -0
  210. package/src/baseline.ts +9 -0
  211. package/src/batching.ts +135 -0
  212. package/src/files.ts +34 -0
  213. package/src/index.ts +26 -0
  214. package/src/indexes.test.ts +158 -0
  215. package/src/indexes.ts +151 -0
  216. package/src/migration.ts +391 -0
  217. package/src/oids.test.ts +395 -0
  218. package/src/oids.ts +442 -0
  219. package/src/operation.test.ts +966 -0
  220. package/src/operation.ts +755 -0
  221. package/src/patch.ts +277 -0
  222. package/src/presence.ts +29 -0
  223. package/src/protocol.ts +184 -0
  224. package/src/refs.ts +16 -0
  225. package/src/replica.ts +29 -0
  226. package/src/schema/defaults.test.ts +19 -0
  227. package/src/schema/defaults.ts +72 -0
  228. package/src/schema/index.ts +28 -0
  229. package/src/schema/indexFilters.ts +49 -0
  230. package/src/schema/types/collection.ts +133 -0
  231. package/src/schema/types/compounds.ts +33 -0
  232. package/src/schema/types/fields.ts +85 -0
  233. package/src/schema/types/filters.ts +39 -0
  234. package/src/schema/types/shapes.ts +83 -0
  235. package/src/schema/types/synthetics.ts +45 -0
  236. package/src/schema/types.ts +28 -0
  237. package/src/timestamp.test.ts +113 -0
  238. package/src/timestamp.ts +284 -0
  239. package/src/undo.ts +205 -0
  240. package/src/utils.test.ts +38 -0
  241. package/src/utils.ts +137 -0
@@ -0,0 +1,391 @@
1
+ import {
2
+ stableStringify,
3
+ StorageCollectionSchema,
4
+ StorageSchema,
5
+ addFieldDefaults,
6
+ StorageDocument,
7
+ CollectionFilter,
8
+ StorageDocumentInit,
9
+ } from './index.js';
10
+
11
+ export interface DroppedCollectionMigrationStrategy<
12
+ Old extends StorageCollectionSchema<any, any, any>,
13
+ > {
14
+ (old: Old): void | Promise<void>;
15
+ }
16
+ export interface PreservedCollectionMigrationStrategy<
17
+ Old extends StorageCollectionSchema<any, any, any>,
18
+ New extends StorageCollectionSchema<any, any, any>,
19
+ > {
20
+ (old: StorageDocument<Old>):
21
+ | StorageDocument<New>
22
+ | Promise<StorageDocument<New>>;
23
+ }
24
+
25
+ type MigrationStrategy<
26
+ Old extends StorageCollectionSchema<any, any, any>,
27
+ New extends StorageCollectionSchema<any, any, any>,
28
+ > =
29
+ | DroppedCollectionMigrationStrategy<Old>
30
+ | PreservedCollectionMigrationStrategy<Old, New>;
31
+
32
+ export type MigrationsKeyedOnCollection<
33
+ Old extends StorageSchema<any>,
34
+ New extends StorageSchema<any>,
35
+ > =
36
+ | PreservedCollectionMigrations<Old, New>
37
+ | DroppedCollectionMigrations<Old, New>;
38
+
39
+ type NotInSchema<
40
+ Name extends string | number | symbol,
41
+ Schema extends StorageSchema<any>,
42
+ > = Name extends keyof Schema['collections'] ? never : Name;
43
+ type InSchema<
44
+ Name extends string | number | symbol,
45
+ Schema extends StorageSchema<any>,
46
+ > = Name extends keyof Schema['collections'] ? Name : never;
47
+
48
+ type NewCollections<
49
+ Old extends StorageSchema<any>,
50
+ New extends StorageSchema<any>,
51
+ > = {
52
+ [Key in keyof New['collections'] as NotInSchema<
53
+ Key,
54
+ Old
55
+ >]: StorageCollectionSchema<any, any, any>;
56
+ };
57
+
58
+ type DroppedCollections<
59
+ Old extends StorageSchema<any>,
60
+ New extends StorageSchema<any>,
61
+ > = {
62
+ [Key in keyof Old['collections'] as NotInSchema<
63
+ Key,
64
+ New
65
+ >]: StorageCollectionSchema<any, any, any>;
66
+ };
67
+
68
+ type PreservedCollections<
69
+ Old extends StorageSchema<any>,
70
+ New extends StorageSchema<any>,
71
+ > = {
72
+ [Key in keyof Old['collections'] as InSchema<
73
+ Key,
74
+ New
75
+ >]: StorageCollectionSchema<any, any, any>;
76
+ };
77
+
78
+ type DroppedCollectionMigrations<
79
+ Old extends StorageSchema<any>,
80
+ New extends StorageSchema<any>,
81
+ > = {
82
+ [Key in keyof DroppedCollections<
83
+ Old,
84
+ New
85
+ >]: DroppedCollectionMigrationStrategy<Old['collections'][Key]>;
86
+ };
87
+ type PreservedCollectionMigrations<
88
+ Old extends StorageSchema<any>,
89
+ New extends StorageSchema<any>,
90
+ > = {
91
+ [Key in keyof PreservedCollections<
92
+ Old,
93
+ New
94
+ >]: PreservedCollectionMigrationStrategy<
95
+ Old['collections'][Key],
96
+ New['collections'][Key]
97
+ >;
98
+ };
99
+
100
+ type StrategyFor<
101
+ Key extends string,
102
+ Old extends StorageSchema<any>,
103
+ New extends StorageSchema<any>,
104
+ > = Key extends keyof New['collections']
105
+ ? PreservedCollectionMigrationStrategy<
106
+ Old['collections'][Key],
107
+ New['collections'][Key]
108
+ >
109
+ : DroppedCollectionMigrationStrategy<Old['collections'][Key]>;
110
+
111
+ type MigrationRunner<
112
+ Old extends StorageSchema<any>,
113
+ New extends StorageSchema<any>,
114
+ > = <Collection extends Extract<keyof Old['collections'], string>>(
115
+ collection: Collection,
116
+ strategy: StrategyFor<Collection, Old, New>,
117
+ ) => Promise<void>;
118
+
119
+ type MigrationQueryMaker<
120
+ Collection extends StorageCollectionSchema<any, any, any>,
121
+ > = {
122
+ get(primaryKey: string): Promise<StorageDocument<Collection> | undefined>;
123
+ findOne(
124
+ query: CollectionFilter,
125
+ ): Promise<StorageDocument<Collection> | undefined>;
126
+ findAll(query?: CollectionFilter): Promise<StorageDocument<Collection>[]>;
127
+ };
128
+
129
+ type MigrationQueries<Old extends StorageSchema<any>> = {
130
+ [Key in keyof Old['collections']]: MigrationQueryMaker<
131
+ Old['collections'][Key]
132
+ >;
133
+ };
134
+
135
+ type MigrationMutations<New extends StorageSchema> = {
136
+ [Key in keyof New['collections']]: {
137
+ put(
138
+ document: StorageDocumentInit<New['collections'][Key]>,
139
+ ): Promise<StorageDocument<New['collections'][Key]>>;
140
+ delete(primaryKey: string): Promise<void>;
141
+ };
142
+ };
143
+
144
+ export interface MigrationTools<
145
+ Old extends StorageSchema<any>,
146
+ New extends StorageSchema<any>,
147
+ > {
148
+ migrate: MigrationRunner<Old, New>;
149
+ identity: <T>(val: T) => T;
150
+ withDefaults: (collectionName: string, value: any) => any;
151
+ queries: MigrationQueries<Old>;
152
+ mutations: MigrationMutations<New>;
153
+ info: {
154
+ changedCollections: keyof Old['collections'][];
155
+ addedCollections: keyof New['collections'][];
156
+ removedCollections: keyof Old['collections'][];
157
+ };
158
+ }
159
+
160
+ export interface MigrationEngine<
161
+ Old extends StorageSchema,
162
+ New extends StorageSchema,
163
+ > {
164
+ migrate: (
165
+ collection: string,
166
+ strategy: MigrationStrategy<any, any>,
167
+ ) => Promise<void>;
168
+ queries: MigrationQueries<Old>;
169
+ mutations: MigrationMutations<New>;
170
+ /** OIDs of any new documents created during the migration */
171
+ newOids: string[];
172
+ /** Promises that should be resolved before completing the migration */
173
+ awaitables: Promise<any>[];
174
+ }
175
+
176
+ type MigrationProcedure<
177
+ Old extends StorageSchema,
178
+ New extends StorageSchema,
179
+ > = (tools: MigrationTools<Old, New>) => Promise<void>;
180
+
181
+ type EmptySchema = {
182
+ version: 0;
183
+ collections: {};
184
+ };
185
+ const emptySchema: EmptySchema = {
186
+ version: 0,
187
+ collections: {},
188
+ };
189
+
190
+ export function migrate<Schema extends StorageSchema>(
191
+ schema: Schema,
192
+ procedure: MigrationProcedure<EmptySchema, Schema>,
193
+ ): Migration<EmptySchema, Schema>;
194
+ export function migrate<Old extends StorageSchema, New extends StorageSchema>(
195
+ oldSchema: Old,
196
+ newSchema: New,
197
+ procedure: MigrationProcedure<Old, New>,
198
+ ): Migration<Old, New>;
199
+ export function migrate(
200
+ oldSchemaOrNewSchema: any,
201
+ newSchemaOrProcedure: any,
202
+ procedureIfTwoSchemas?: any,
203
+ ) {
204
+ const isProcedureSecondArgument = typeof newSchemaOrProcedure === 'function';
205
+ const oldSchema = isProcedureSecondArgument
206
+ ? emptySchema
207
+ : oldSchemaOrNewSchema;
208
+ const newSchema = isProcedureSecondArgument
209
+ ? oldSchemaOrNewSchema
210
+ : newSchemaOrProcedure;
211
+ const procedure = isProcedureSecondArgument
212
+ ? newSchemaOrProcedure
213
+ : procedureIfTwoSchemas;
214
+ // diff to determine changed collections
215
+ const changedCollections: string[] = Object.keys(
216
+ newSchema.collections,
217
+ ).filter(
218
+ (key) =>
219
+ oldSchema.collections[key] &&
220
+ stableStringify(oldSchema.collections[key]) !==
221
+ stableStringify(newSchema.collections[key]),
222
+ );
223
+ const removedCollections: string[] = Object.keys(
224
+ oldSchema.collections,
225
+ ).filter((key) => !newSchema.collections[key]);
226
+ const addedCollections = Object.keys(newSchema.collections).filter(
227
+ (key) => !oldSchema.collections[key],
228
+ );
229
+
230
+ const addedIndexes: Record<string, MigrationIndexDescription[]> = {};
231
+ const removedIndexes: Record<string, MigrationIndexDescription[]> = {};
232
+ for (const changed of [...changedCollections, ...addedCollections]) {
233
+ const oldIndexes = getIndexes(oldSchema.collections[changed]);
234
+ const newIndexes = getIndexes(newSchema.collections[changed]);
235
+ const added = newIndexes.filter(
236
+ (index) => !oldIndexes.find((i) => i.name === index.name),
237
+ );
238
+ const removed = oldIndexes.filter(
239
+ (index) => !newIndexes.find((i) => i.name === index.name),
240
+ );
241
+ if (added.length > 0) {
242
+ addedIndexes[changed] = added;
243
+ }
244
+ if (removed.length > 0) {
245
+ removedIndexes[changed] = removed;
246
+ }
247
+ }
248
+
249
+ return {
250
+ version: newSchema.version,
251
+ migrate: async (engine: MigrationEngine<any, any>) => {
252
+ const migratedCollections: string[] = [];
253
+ await procedure({
254
+ migrate: async (collection: any, strategy: any) => {
255
+ // @ts-ignore
256
+ await engine.migrate(collection, strategy);
257
+ migratedCollections.push(collection);
258
+ },
259
+ identity: (val: any) => val,
260
+ withDefaults: (collectionName: string, val: any) => {
261
+ return addFieldDefaults(newSchema.collections[collectionName], val);
262
+ },
263
+ info: {
264
+ changedCollections,
265
+ addedCollections,
266
+ removedCollections,
267
+ },
268
+ queries: engine.queries,
269
+ mutations: engine.mutations,
270
+ });
271
+
272
+ const unmigrated = changedCollections.filter(
273
+ (collection) => !migratedCollections.includes(collection),
274
+ );
275
+ if (unmigrated.length > 0) {
276
+ console.error('Unmigrated changed collections:', unmigrated);
277
+ }
278
+ },
279
+ removedCollections,
280
+ addedIndexes,
281
+ removedIndexes,
282
+ allCollections: Object.keys(newSchema.collections),
283
+ changedCollections,
284
+ addedCollections,
285
+ oldCollections: Object.keys(oldSchema.collections),
286
+ oldSchema,
287
+ newSchema,
288
+ };
289
+ }
290
+
291
+ export interface MigrationIndexDescription {
292
+ name: string;
293
+ multiEntry: boolean;
294
+ synthetic: boolean;
295
+ compound: boolean;
296
+ }
297
+
298
+ export interface Migration<
299
+ Old extends StorageSchema = any,
300
+ New extends StorageSchema = any,
301
+ > {
302
+ version: number;
303
+ oldSchema: Old;
304
+ newSchema: New;
305
+ migrate: (engine: MigrationEngine<Old, New>) => Promise<void>;
306
+ /** Collections which are added in the new schema and not present in the old */
307
+ addedCollections: string[];
308
+ /** Collections which were removed from the old schema */
309
+ removedCollections: string[];
310
+ /** All collections which exist after the migration has completed - i.e. the ones in the new schema */
311
+ allCollections: string[];
312
+ /** Only the collections which were in the old schema */
313
+ oldCollections: string[];
314
+ /** Collections whose fields or indexes changed between schemas */
315
+ changedCollections: string[];
316
+ // new indexes mapped by collection name
317
+ addedIndexes: Record<string, MigrationIndexDescription[]>;
318
+ // removed indexes mapped by collection name
319
+ removedIndexes: Record<string, MigrationIndexDescription[]>;
320
+ }
321
+
322
+ export function migrationRange(from: number, to: number) {
323
+ return [...Array(to - from).keys()].map((i) => 1 + i + from);
324
+ }
325
+
326
+ function getIndexes<Coll extends StorageCollectionSchema<any, any, any>>(
327
+ collection: Coll | undefined,
328
+ ): MigrationIndexDescription[] {
329
+ if (!collection) return [];
330
+ const fields = Object.keys(collection.fields)
331
+ .filter((key) => collection.fields[key].indexed)
332
+ .map((key) => ({
333
+ name: key,
334
+ multiEntry: collection.fields[key].type === 'array',
335
+ synthetic: false,
336
+ compound: false,
337
+ }));
338
+
339
+ return [
340
+ ...fields,
341
+ ...Object.keys(collection.synthetics || {}).map((key) => ({
342
+ name: key,
343
+ multiEntry: ['array', 'string[]', 'number[]', 'boolean[]'].includes(
344
+ collection.synthetics[key].type,
345
+ ),
346
+ synthetic: true,
347
+ compound: false,
348
+ })),
349
+ ...Object.keys(collection.compounds || {}).map((key) => ({
350
+ name: key,
351
+ multiEntry: collection.compounds[key].of.some(
352
+ (fieldName: string) =>
353
+ (collection.fields[fieldName] || collection.synthetics[fieldName])
354
+ .type === 'array',
355
+ ),
356
+ synthetic: false,
357
+ compound: true,
358
+ })),
359
+ ];
360
+ }
361
+
362
+ export function createDefaultMigration(
363
+ schema: StorageSchema,
364
+ ): Migration<{ version: 0; collections: {} }>;
365
+ export function createDefaultMigration<Old extends StorageSchema>(
366
+ oldSchema: Old,
367
+ newSchema: StorageSchema,
368
+ ): Migration<Old>;
369
+ export function createDefaultMigration(
370
+ schema: StorageSchema,
371
+ newSchema?: StorageSchema<any>,
372
+ ) {
373
+ let oldSchema = newSchema
374
+ ? schema
375
+ : {
376
+ version: 0,
377
+ collections: {},
378
+ };
379
+ return migrate(
380
+ oldSchema,
381
+ newSchema || schema,
382
+ async ({ migrate, withDefaults, info }) => {
383
+ if ((newSchema || schema).version === 1) return;
384
+
385
+ for (const collection of info.changedCollections as any) {
386
+ // @ts-ignore indefinite type resolution
387
+ await migrate(collection, (old) => withDefaults(collection, old));
388
+ }
389
+ },
390
+ );
391
+ }