@verdant-web/store 4.6.1 → 5.0.1

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 (130) hide show
  1. package/dist/bundle/index.js +14 -12
  2. package/dist/bundle/index.js.map +4 -4
  3. package/dist/esm/__tests__/fixtures/testStorage.d.ts +1 -1
  4. package/dist/esm/__tests__/fixtures/testStorage.js +3 -3
  5. package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
  6. package/dist/esm/__tests__/queries.test.js +3 -3
  7. package/dist/esm/__tests__/queries.test.js.map +1 -1
  8. package/dist/esm/__tests__/schema.test.js +3 -3
  9. package/dist/esm/__tests__/schema.test.js.map +1 -1
  10. package/dist/esm/client/Client.d.ts +12 -10
  11. package/dist/esm/client/Client.js +40 -30
  12. package/dist/esm/client/Client.js.map +1 -1
  13. package/dist/esm/context/Time.d.ts +1 -1
  14. package/dist/esm/context/Time.js +1 -1
  15. package/dist/esm/context/Time.js.map +1 -1
  16. package/dist/esm/context/context.d.ts +84 -15
  17. package/dist/esm/context/context.js +98 -1
  18. package/dist/esm/context/context.js.map +1 -1
  19. package/dist/esm/entities/Entity.test.js +0 -1
  20. package/dist/esm/entities/Entity.test.js.map +1 -1
  21. package/dist/esm/entities/EntityMetadata.js +11 -5
  22. package/dist/esm/entities/EntityMetadata.js.map +1 -1
  23. package/dist/esm/entities/EntityStore.js +9 -7
  24. package/dist/esm/entities/EntityStore.js.map +1 -1
  25. package/dist/esm/files/EntityFile.js +1 -1
  26. package/dist/esm/files/EntityFile.js.map +1 -1
  27. package/dist/esm/files/FileManager.js +5 -5
  28. package/dist/esm/files/FileManager.js.map +1 -1
  29. package/dist/esm/index.d.ts +6 -4
  30. package/dist/esm/index.js +2 -3
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/internal.d.ts +3 -4
  33. package/dist/esm/internal.js +1 -2
  34. package/dist/esm/internal.js.map +1 -1
  35. package/dist/esm/persistence/PersistenceMetadata.d.ts +3 -6
  36. package/dist/esm/persistence/PersistenceMetadata.js +5 -6
  37. package/dist/esm/persistence/PersistenceMetadata.js.map +1 -1
  38. package/dist/esm/persistence/idb/IdbService.d.ts +3 -3
  39. package/dist/esm/persistence/idb/IdbService.js +0 -1
  40. package/dist/esm/persistence/idb/IdbService.js.map +1 -1
  41. package/dist/esm/persistence/idb/idbPersistence.d.ts +9 -10
  42. package/dist/esm/persistence/idb/idbPersistence.js +11 -4
  43. package/dist/esm/persistence/idb/idbPersistence.js.map +1 -1
  44. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.d.ts +2 -2
  45. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js +1 -1
  46. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js.map +1 -1
  47. package/dist/esm/persistence/idb/queries/IdbDocumentDb.d.ts +3 -2
  48. package/dist/esm/persistence/idb/queries/IdbDocumentDb.js +16 -15
  49. package/dist/esm/persistence/idb/queries/IdbDocumentDb.js.map +1 -1
  50. package/dist/esm/persistence/idb/queries/migration/db.js +7 -0
  51. package/dist/esm/persistence/idb/queries/migration/db.js.map +1 -1
  52. package/dist/esm/persistence/idb/util.js +27 -17
  53. package/dist/esm/persistence/idb/util.js.map +1 -1
  54. package/dist/esm/persistence/interfaces.d.ts +8 -8
  55. package/dist/esm/persistence/migration/engine.d.ts +5 -3
  56. package/dist/esm/persistence/migration/engine.js +23 -14
  57. package/dist/esm/persistence/migration/engine.js.map +1 -1
  58. package/dist/esm/persistence/migration/finalize.d.ts +5 -3
  59. package/dist/esm/persistence/migration/finalize.js +5 -4
  60. package/dist/esm/persistence/migration/finalize.js.map +1 -1
  61. package/dist/esm/persistence/migration/migrate.d.ts +8 -5
  62. package/dist/esm/persistence/migration/migrate.js +10 -4
  63. package/dist/esm/persistence/migration/migrate.js.map +1 -1
  64. package/dist/esm/persistence/persistence.d.ts +9 -2
  65. package/dist/esm/persistence/persistence.js +65 -32
  66. package/dist/esm/persistence/persistence.js.map +1 -1
  67. package/dist/esm/queries/FindAllQuery.js +1 -1
  68. package/dist/esm/queries/FindAllQuery.js.map +1 -1
  69. package/dist/esm/queries/FindInfiniteQuery.js +2 -2
  70. package/dist/esm/queries/FindInfiniteQuery.js.map +1 -1
  71. package/dist/esm/queries/FindOneQuery.js +1 -1
  72. package/dist/esm/queries/FindOneQuery.js.map +1 -1
  73. package/dist/esm/queries/FindPageQuery.js +1 -1
  74. package/dist/esm/queries/FindPageQuery.js.map +1 -1
  75. package/dist/esm/sync/PresenceManager.d.ts +2 -2
  76. package/dist/esm/sync/PresenceManager.js +5 -2
  77. package/dist/esm/sync/PresenceManager.js.map +1 -1
  78. package/dist/esm/sync/PushPullSync.js +4 -4
  79. package/dist/esm/sync/PushPullSync.js.map +1 -1
  80. package/dist/esm/sync/Sync.d.ts +2 -2
  81. package/dist/esm/sync/Sync.js +17 -14
  82. package/dist/esm/sync/Sync.js.map +1 -1
  83. package/dist/esm/sync/WebSocketSync.js +12 -7
  84. package/dist/esm/sync/WebSocketSync.js.map +1 -1
  85. package/dist/esm/sync/serviceWorker.d.ts +2 -2
  86. package/dist/esm/sync/serviceWorker.js +3 -4
  87. package/dist/esm/sync/serviceWorker.js.map +1 -1
  88. package/package.json +2 -2
  89. package/src/__tests__/fixtures/testStorage.ts +4 -8
  90. package/src/__tests__/queries.test.ts +3 -5
  91. package/src/__tests__/schema.test.ts +3 -3
  92. package/src/client/Client.ts +50 -34
  93. package/src/context/Time.ts +2 -2
  94. package/src/context/context.ts +189 -17
  95. package/src/entities/Entity.test.ts +0 -1
  96. package/src/entities/EntityMetadata.ts +16 -5
  97. package/src/entities/EntityStore.ts +14 -10
  98. package/src/files/EntityFile.ts +1 -1
  99. package/src/files/FileManager.ts +5 -5
  100. package/src/index.ts +6 -10
  101. package/src/internal.ts +10 -11
  102. package/src/persistence/PersistenceMetadata.ts +9 -11
  103. package/src/persistence/idb/IdbService.ts +2 -3
  104. package/src/persistence/idb/idbPersistence.ts +25 -19
  105. package/src/persistence/idb/metadata/IdbMetadataDb.ts +3 -3
  106. package/src/persistence/idb/queries/IdbDocumentDb.ts +31 -17
  107. package/src/persistence/idb/queries/migration/db.ts +10 -0
  108. package/src/persistence/idb/util.ts +46 -24
  109. package/src/persistence/interfaces.ts +21 -12
  110. package/src/persistence/migration/engine.ts +33 -18
  111. package/src/persistence/migration/finalize.ts +11 -5
  112. package/src/persistence/migration/migrate.ts +15 -8
  113. package/src/persistence/persistence.ts +78 -67
  114. package/src/queries/FindAllQuery.ts +3 -1
  115. package/src/queries/FindInfiniteQuery.ts +6 -2
  116. package/src/queries/FindOneQuery.ts +3 -1
  117. package/src/queries/FindPageQuery.ts +3 -1
  118. package/src/sync/PresenceManager.ts +10 -7
  119. package/src/sync/PushPullSync.ts +8 -6
  120. package/src/sync/Sync.ts +26 -16
  121. package/src/sync/WebSocketSync.ts +25 -9
  122. package/src/sync/serviceWorker.ts +4 -6
  123. package/dist/esm/client/ClientDescriptor.d.ts +0 -87
  124. package/dist/esm/client/ClientDescriptor.js +0 -133
  125. package/dist/esm/client/ClientDescriptor.js.map +0 -1
  126. package/dist/esm/persistence/migration/types.d.ts +0 -3
  127. package/dist/esm/persistence/migration/types.js +0 -2
  128. package/dist/esm/persistence/migration/types.js.map +0 -1
  129. package/src/client/ClientDescriptor.ts +0 -246
  130. package/src/persistence/migration/types.ts +0 -4
@@ -4,17 +4,20 @@ import {
4
4
  Migration,
5
5
  MigrationEngine,
6
6
  } from '@verdant-web/common';
7
+ import { ContextWithoutPersistence } from '../../context/context.js';
7
8
  import { ClientOperation, PersistenceDocumentDb } from '../interfaces.js';
8
- import { OpenDocumentDbContext } from './types.js';
9
+ import { PersistenceMetadata } from '../PersistenceMetadata.js';
9
10
 
10
11
  export async function finalizeMigration({
11
12
  ctx,
12
13
  documents,
13
14
  migration,
15
+ meta,
14
16
  engine,
15
17
  }: {
16
- ctx: OpenDocumentDbContext;
18
+ ctx: ContextWithoutPersistence;
17
19
  documents: PersistenceDocumentDb;
20
+ meta: PersistenceMetadata;
18
21
  migration: Migration<any>;
19
22
  engine: MigrationEngine;
20
23
  }) {
@@ -37,6 +40,7 @@ export async function finalizeMigration({
37
40
  currentVersion: migration.oldSchema.version,
38
41
  newVersion: migration.newSchema.version,
39
42
  ctx,
43
+ meta,
40
44
  });
41
45
 
42
46
  // once the schema is ready, we can write back the migrated documents
@@ -58,7 +62,7 @@ export async function finalizeMigration({
58
62
  const snapshots = await Promise.all(
59
63
  oids.map(async (oid) => {
60
64
  try {
61
- const snap = await ctx.meta.getDocumentSnapshot(oid);
65
+ const snap = await meta.getDocumentSnapshot(oid);
62
66
  return [oid, snap];
63
67
  } catch (e) {
64
68
  // this seems to happen with baselines/ops which are not fully
@@ -103,16 +107,18 @@ async function getDocsWithUnappliedMigrations({
103
107
  currentVersion,
104
108
  newVersion: _,
105
109
  ctx,
110
+ meta,
106
111
  }: {
107
112
  currentVersion: number;
108
113
  newVersion: number;
109
- ctx: OpenDocumentDbContext;
114
+ ctx: ContextWithoutPersistence;
115
+ meta: PersistenceMetadata;
110
116
  }) {
111
117
  // scan for all operations in metadata after the current version.
112
118
  // this could be more efficient if also filtering below or equal newVersion but
113
119
  // that seems so unlikely in practice...
114
120
  const unappliedOperations: ClientOperation[] = [];
115
- await ctx.meta.iterateAllOperations(
121
+ await meta.iterateAllOperations(
116
122
  (op) => {
117
123
  unappliedOperations.push(op);
118
124
  },
@@ -1,16 +1,19 @@
1
1
  import { Migration } from '@verdant-web/common';
2
+ import { ContextWithoutPersistence } from '../../context/context.js';
2
3
  import { ShutdownHandler } from '../../context/ShutdownHandler.js';
3
4
  import { PersistenceNamespace } from '../interfaces.js';
5
+ import { PersistenceMetadata } from '../PersistenceMetadata.js';
4
6
  import { getMigrationEngine } from './engine.js';
5
7
  import { finalizeMigration } from './finalize.js';
6
8
  import { getMigrationPath } from './paths.js';
7
- import { OpenDocumentDbContext } from './types.js';
8
9
 
9
10
  export async function migrate({
10
11
  context,
11
12
  version,
13
+ meta,
12
14
  }: {
13
- context: OpenDocumentDbContext;
15
+ context: ContextWithoutPersistence;
16
+ meta: PersistenceMetadata;
14
17
  version: number;
15
18
  }) {
16
19
  const ns = await context.persistence.openNamespace(
@@ -45,7 +48,7 @@ export async function migrate({
45
48
  'Migrations to run:',
46
49
  toRun.map((m) => m.version),
47
50
  );
48
- await runMigrations({ context, ns, toRun });
51
+ await runMigrations({ context, ns, toRun, meta });
49
52
  }
50
53
  });
51
54
  }
@@ -63,10 +66,12 @@ export async function runMigrations({
63
66
  context,
64
67
  toRun,
65
68
  ns,
69
+ meta,
66
70
  }: {
67
- context: OpenDocumentDbContext;
71
+ context: ContextWithoutPersistence;
68
72
  toRun: Migration<any>[];
69
73
  ns: PersistenceNamespace;
74
+ meta: PersistenceMetadata;
70
75
  }) {
71
76
  // disable rebasing for the duration of migrations
72
77
  context.pauseRebasing = true;
@@ -76,16 +81,16 @@ export async function runMigrations({
76
81
  'info',
77
82
  `🚀 Running migration v${migration.oldSchema.version} -> v${migration.newSchema.version}`,
78
83
  );
79
- const migrationContext = {
80
- ...context,
84
+ const migrationContext = context.cloneWithOptions({
81
85
  schema: migration.oldSchema,
82
- shutdownHandler: new ShutdownHandler(context.log),
83
- };
86
+ persistenceShutdownHandler: new ShutdownHandler(context.log),
87
+ });
84
88
  // this will only write to our metadata store via operations!
85
89
  const engine = await getMigrationEngine({
86
90
  migration,
87
91
  context: migrationContext,
88
92
  ns,
93
+ meta,
89
94
  });
90
95
  try {
91
96
  context.log(
@@ -98,6 +103,7 @@ export async function runMigrations({
98
103
  migration.newSchema.version,
99
104
  );
100
105
  await migration.migrate(engine);
106
+ context.log('debug', 'Awaiting remaining migration tasks');
101
107
  // wait on any out-of-band async operations to complete
102
108
  await Promise.all(engine.awaitables);
103
109
  } catch (err) {
@@ -136,6 +142,7 @@ export async function runMigrations({
136
142
  migration,
137
143
  engine,
138
144
  documents: upgradedDocuments,
145
+ meta,
139
146
  });
140
147
  await upgradedDocuments.close();
141
148
 
@@ -1,5 +1,5 @@
1
- import { EventSubscriber, getOidRoot, VerdantError } from '@verdant-web/common';
2
- import { Context, InitialContext } from '../context/context.js';
1
+ import { getOidRoot, VerdantError } from '@verdant-web/common';
2
+ import { Context } from '../context/context.js';
3
3
  import { ShutdownHandler } from '../context/ShutdownHandler.js';
4
4
  import { getWipNamespace } from '../utils/wip.js';
5
5
  import { ExportedData } from './interfaces.js';
@@ -8,28 +8,26 @@ import { PersistenceFiles } from './PersistenceFiles.js';
8
8
  import { PersistenceMetadata } from './PersistenceMetadata.js';
9
9
  import { PersistenceDocuments } from './PersistenceQueries.js';
10
10
 
11
- export async function initializePersistence(
12
- ctx: InitialContext,
13
- ): Promise<Context> {
14
- let context = ctx as any as Context;
11
+ export async function initializePersistence(ctx: Context) {
12
+ const initialSchema = ctx.schema;
15
13
  if (ctx.schema.wip) {
16
14
  // this is a WIP database, so we need to create a new namespace for the WIP data.
17
- context.namespace = getWipNamespace(ctx.originalNamespace, ctx.schema);
18
- context.log('info', 'Switched to WIP namespace', context.namespace);
15
+ ctx.namespace = getWipNamespace(ctx.originalNamespace, ctx.schema);
16
+ ctx.log('info', '🔨', 'Switched to WIP namespace', ctx.namespace);
19
17
  // check if this WIP database is already in use
20
- const namespaces = await context.persistence.getNamespaces();
18
+ const namespaces = await ctx.persistence.getNamespaces();
21
19
 
22
- if (!namespaces.includes(context.namespace)) {
20
+ if (!namespaces.includes(ctx.namespace)) {
23
21
  // copy all data to WIP namespace -- from the current version of local
24
22
  // data, not the WIP schema version. this may not be n-1, we might
25
23
  // be loading a WIP schema over older data.
26
- const currentVersion = await context.persistence.getNamespaceVersion(
27
- context.originalNamespace,
24
+ const currentVersion = await ctx.persistence.getNamespaceVersion(
25
+ ctx.originalNamespace,
28
26
  );
29
27
 
30
28
  if (currentVersion === 0) {
31
29
  // there is no existing data. nothing to copy.
32
- context.log('debug', 'No existing data to copy to WIP namespace');
30
+ ctx.log('debug', 'No existing data to copy to WIP namespace');
33
31
  } else {
34
32
  const currentSchema = ctx.oldSchemas?.find(
35
33
  (s) => s.version === currentVersion,
@@ -41,52 +39,62 @@ export async function initializePersistence(
41
39
  `Trying to open WIP database for version ${ctx.schema.version}, but the current local data is version ${currentVersion} and a historical schema for that version is not available.`,
42
40
  );
43
41
  }
44
- context.log(
42
+ ctx.log(
45
43
  'info',
46
- `Copying data from ${context.originalNamespace} to ${context.namespace}`,
44
+ `Copying data from ${ctx.originalNamespace} to ${ctx.namespace}`,
47
45
  );
48
- await context.persistence.copyNamespace(
49
- context.originalNamespace,
50
- context.namespace,
46
+ await ctx.persistence.copyNamespace(
47
+ ctx.originalNamespace,
48
+ ctx.namespace,
51
49
  // needs to be the original schema; the copy should be of the original
52
50
  // data and schema structure; the WIP schema migration application happens
53
51
  // below.
54
- {
55
- ...context,
52
+ ctx.cloneWithOptions({
56
53
  schema: currentSchema,
57
- },
54
+ }),
58
55
  );
59
56
  }
60
57
  }
61
58
  }
62
59
 
63
- const namespace = await ctx.persistence.openNamespace(
64
- context.namespace,
65
- context,
66
- );
60
+ const namespace = await ctx.persistence.openNamespace(ctx.namespace, ctx);
67
61
 
68
- context.log('info', 'Opening persistence metadata');
69
- context.meta = new PersistenceMetadata(
70
- await namespace.openMetadata(ctx),
71
- ctx,
72
- );
62
+ ctx.log('info', 'Opening persistence metadata', ctx.namespace);
63
+ const meta = new PersistenceMetadata(await namespace.openMetadata(ctx), ctx);
73
64
 
74
- context.log('info', 'Opening persistence files');
75
- context.files = new PersistenceFiles(
76
- await namespace.openFiles(context),
77
- context,
78
- );
65
+ ctx.log('info', 'Opening persistence files', ctx.namespace);
66
+ const files = new PersistenceFiles(await namespace.openFiles(ctx), ctx);
79
67
 
80
- context.log('info', 'Migrating document database');
68
+ ctx.log('info', 'Migrating document database');
81
69
  await migrate({
82
- context,
70
+ context: ctx,
83
71
  version: ctx.schema.version,
72
+ meta,
84
73
  });
85
74
 
86
- context.log('info', 'Opening persistence documents');
87
- context.documents = new PersistenceDocuments(
88
- await namespace.openDocuments(context),
89
- context,
75
+ ctx.log('info', 'Opening persistence documents');
76
+ if (ctx.schema.version <= 0) {
77
+ // debugging....
78
+ if (ctx.schema !== initialSchema) {
79
+ ctx.log(
80
+ 'critical',
81
+ 'Schema at initialization does not match original schema. This is likely a bug in Verdant!',
82
+ );
83
+ throw new VerdantError(
84
+ VerdantError.Code.ConfigurationError,
85
+ undefined,
86
+ `Schema at initialization does not match original schema. This is likely a bug in Verdant!`,
87
+ );
88
+ }
89
+ throw new VerdantError(
90
+ VerdantError.Code.ConfigurationError,
91
+ undefined,
92
+ `Schema version must be greater than 0. Found version ${ctx.schema.version} with collections [${Object.keys(ctx.schema.collections).join(', ')}]\n${JSON.stringify(ctx.schema)}`,
93
+ );
94
+ }
95
+ const documents = new PersistenceDocuments(
96
+ await namespace.openDocuments(ctx),
97
+ ctx,
90
98
  );
91
99
 
92
100
  if (!ctx.schema.wip) {
@@ -100,7 +108,7 @@ export async function initializePersistence(
100
108
  }
101
109
  }
102
110
 
103
- return context;
111
+ return { meta, files, documents };
104
112
  }
105
113
 
106
114
  export async function importPersistence(
@@ -123,26 +131,17 @@ export async function importPersistence(
123
131
  // using a new namespace to put all of this into a temporary zone
124
132
  const importedNamespace = `@@import_${Date.now()}`;
125
133
 
126
- const importedContext = await initializePersistence({
127
- ...ctx,
134
+ const importedContext = ctx.cloneWithOptions({
128
135
  schema: exportedSchema,
129
136
  namespace: importedNamespace,
130
- originalNamespace: importedNamespace,
131
- // no-op entity events -- don't need to inform queries of changes.
132
- entityEvents: new EventSubscriber(),
133
- internalEvents: new EventSubscriber(),
134
- globalEvents: new EventSubscriber(),
135
- config: {
136
- ...ctx.config,
137
- persistence: {
138
- ...ctx.config.persistence,
139
- disableRebasing: true,
140
- },
141
- },
137
+ disableRebasing: true,
142
138
  persistenceShutdownHandler: new ShutdownHandler(ctx.log),
143
139
  });
140
+ await importedContext.reinitialize();
141
+ const importedMeta = await importedContext.meta;
142
+
144
143
  // load imported data into persistence
145
- await importedContext.meta.resetFrom(exportedData.data);
144
+ await importedMeta.resetFrom(exportedData.data);
146
145
  // need to write indexes here!
147
146
  const affectedOids = new Set<string>();
148
147
  for (const baseline of exportedData.data.baselines) {
@@ -153,15 +152,15 @@ export async function importPersistence(
153
152
  }
154
153
  const toSave = await Promise.all(
155
154
  Array.from(affectedOids).map(async (oid) => {
156
- const snapshot = await importedContext.meta.getDocumentSnapshot(oid);
155
+ const snapshot = await importedMeta.getDocumentSnapshot(oid);
157
156
  return {
158
157
  oid,
159
158
  getSnapshot: () => snapshot,
160
159
  };
161
160
  }),
162
161
  );
163
- await importedContext.documents.saveEntities(toSave);
164
- await importedContext.files.import(exportedData);
162
+ await (await importedContext.documents).saveEntities(toSave);
163
+ await (await importedContext.files).import(exportedData);
165
164
 
166
165
  ctx.log('debug', 'Imported data into temporary namespace', importedNamespace);
167
166
 
@@ -172,13 +171,14 @@ export async function importPersistence(
172
171
  // an upgrade of the imported data is needed ; it's an older version
173
172
  // of the schema.
174
173
 
175
- // upgrade the imported data to the latest schema
176
- const currentSchema = ctx.schema;
177
- const upgradedContext = await initializePersistence({
178
- ...importedContext,
174
+ // upgrade the imported data to the latest schema by re-initializing
175
+ // a context at the latest version, pointing at the imported namespace
176
+ const upgradedContext = importedContext.cloneWithOptions({
179
177
  persistenceShutdownHandler: new ShutdownHandler(ctx.log),
180
- schema: currentSchema,
178
+ schema: ctx.schema,
179
+ oldSchemas: ctx.oldSchemas,
181
180
  });
181
+ await upgradedContext.reinitialize();
182
182
 
183
183
  ctx.log('debug', 'Upgraded imported data to current schema');
184
184
 
@@ -192,15 +192,17 @@ export async function importPersistence(
192
192
 
193
193
  // copy the imported data into the current namespace
194
194
  await ctx.persistence.copyNamespace(importedNamespace, ctx.namespace, ctx);
195
+ ctx.log('debug', 'Copied imported data to primary namespace');
195
196
 
196
197
  // restart the persistence layer
197
- await initializePersistence(ctx);
198
+ await ctx.reinitialize();
199
+ ctx.log('debug', 'Reinitialized primary persistence layer');
198
200
 
199
201
  // verify integrity -- this can only be done if imported data was same
200
202
  // version as current schema, because migrations could add or remove
201
203
  // operations. still, it's a good sanity check.
202
204
  if (exportedData.data.schemaVersion === ctx.schema.version) {
203
- const stats = await ctx.meta.stats();
205
+ const stats = await (await ctx.meta).stats();
204
206
  if (stats.operationsSize.count !== exportedData.data.operations.length) {
205
207
  ctx.log(
206
208
  'critical',
@@ -231,6 +233,15 @@ export async function importPersistence(
231
233
  'Imported documents count mismatch',
232
234
  );
233
235
  }
236
+ } else {
237
+ ctx.log(
238
+ 'debug',
239
+ 'Skipping integrity check due to schema version mismatch (not an error)',
240
+ {
241
+ exportedVersion: exportedData.data.schemaVersion,
242
+ currentVersion: ctx.schema.version,
243
+ },
244
+ );
234
245
  }
235
246
 
236
247
  ctx.log('debug', 'Data copied to primary namespace');
@@ -23,7 +23,9 @@ export class FindAllQuery<T> extends BaseQuery<T[]> {
23
23
  }
24
24
 
25
25
  protected run = async () => {
26
- const { result: oids } = await this.context.documents.findAllOids({
26
+ const { result: oids } = await (
27
+ await this.context.documents
28
+ ).findAllOids({
27
29
  collection: this.collection,
28
30
  index: this.index,
29
31
  });
@@ -37,7 +37,9 @@ export class FindInfiniteQuery<T> extends BaseQuery<T[]> {
37
37
  }
38
38
 
39
39
  protected run = async () => {
40
- const { result, hasNextPage } = await this.context.documents.findAllOids({
40
+ const { result, hasNextPage } = await (
41
+ await this.context.documents
42
+ ).findAllOids({
41
43
  collection: this.collection,
42
44
  limit: this._pageSize * this._upToPage,
43
45
  offset: 0,
@@ -48,7 +50,9 @@ export class FindInfiniteQuery<T> extends BaseQuery<T[]> {
48
50
  };
49
51
 
50
52
  public loadMore = async () => {
51
- const { result, hasNextPage } = await this.context.documents.findAllOids({
53
+ const { result, hasNextPage } = await (
54
+ await this.context.documents
55
+ ).findAllOids({
52
56
  collection: this.collection,
53
57
  limit: this._pageSize,
54
58
  offset: this._pageSize * this._upToPage,
@@ -23,7 +23,9 @@ export class FindOneQuery<T> extends BaseQuery<T | null> {
23
23
  }
24
24
 
25
25
  protected run = async () => {
26
- const oid = await this.context.documents.findOneOid({
26
+ const oid = await (
27
+ await this.context.documents
28
+ ).findOneOid({
27
29
  collection: this.collection,
28
30
  index: this.index,
29
31
  });
@@ -48,7 +48,9 @@ export class FindPageQuery<T> extends BaseQuery<T[]> {
48
48
  }
49
49
 
50
50
  protected run = async () => {
51
- const { result, hasNextPage } = await this.context.documents.findAllOids({
51
+ const { result, hasNextPage } = await (
52
+ await this.context.documents
53
+ ).findAllOids({
52
54
  collection: this.collection,
53
55
  index: this.index,
54
56
  limit: this._pageSize,
@@ -1,13 +1,13 @@
1
1
  import {
2
- ServerMessage,
3
- EventSubscriber,
4
- Batcher,
5
2
  Batch,
3
+ Batcher,
4
+ EventSubscriber,
5
+ ServerMessage,
6
6
  VerdantInternalPresence,
7
7
  initialInternalPresence,
8
8
  } from '@verdant-web/common';
9
- import type { UserInfo } from '../index.js';
10
9
  import { Context } from '../context/context.js';
10
+ import type { UserInfo } from '../index.js';
11
11
  import { LocalReplicaInfo } from '../persistence/interfaces.js';
12
12
 
13
13
  export const HANDLE_MESSAGE = Symbol('handleMessage');
@@ -90,9 +90,12 @@ export class PresenceManager<
90
90
  this.self.replicaId = '';
91
91
 
92
92
  // set the local replica ID as soon as it's loaded
93
- ctx.meta.getLocalReplica().then((info) => {
94
- this.self.replicaId = info.id;
95
- });
93
+ ctx.waitForInitialization
94
+ .then(() => ctx.meta)
95
+ .then((meta) => meta.getLocalReplica())
96
+ .then((info) => {
97
+ this.self.replicaId = info.id;
98
+ });
96
99
 
97
100
  this._updateBatcher = new Batcher(this.flushPresenceUpdates);
98
101
  this._updateBatch = this._updateBatcher.add({
@@ -140,7 +140,9 @@ export class PushPullSync
140
140
  if (message.ackThisNonce) {
141
141
  this.ctx.log('debug', 'Sending sync ack', message.ackThisNonce);
142
142
  await this.sendRequest([
143
- await this.ctx.meta.messageCreator.createAck(message.ackThisNonce),
143
+ await (
144
+ await this.ctx.meta
145
+ ).messageCreator.createAck(message.ackThisNonce),
144
146
  ]);
145
147
  }
146
148
  }
@@ -210,10 +212,10 @@ export class PushPullSync
210
212
  // will include the client's own presence info and fill in missing profile
211
213
  // data on the first request. otherwise it would have to wait for the second.
212
214
  this.sendRequest([
213
- await this.ctx.meta.messageCreator.createPresenceUpdate(
214
- this.presence.self,
215
- ),
216
- await this.ctx.meta.messageCreator.createSyncStep1(),
215
+ await (
216
+ await this.ctx.meta
217
+ ).messageCreator.createPresenceUpdate(this.presence.self),
218
+ await (await this.ctx.meta).messageCreator.createSyncStep1(),
217
219
  ]);
218
220
  };
219
221
 
@@ -227,7 +229,7 @@ export class PushPullSync
227
229
 
228
230
  syncOnce = async () => {
229
231
  await this.sendRequest([
230
- await this.ctx.meta.messageCreator.createSyncStep1(),
232
+ await (await this.ctx.meta).messageCreator.createSyncStep1(),
231
233
  ]);
232
234
  };
233
235
 
package/src/sync/Sync.ts CHANGED
@@ -10,16 +10,16 @@ import {
10
10
  VerdantError,
11
11
  VerdantInternalPresence,
12
12
  } from '@verdant-web/common';
13
- import { HANDLE_MESSAGE, PresenceManager } from './PresenceManager.js';
13
+ import { Context } from '../context/context.js';
14
+ import { attemptToRegisterBackgroundSync } from './background.js';
14
15
  import { FilePullResult, FileSync, FileUploadResult } from './FileSync.js';
16
+ import { HANDLE_MESSAGE, PresenceManager } from './PresenceManager.js';
15
17
  import { PushPullSync } from './PushPullSync.js';
16
18
  import {
17
19
  ServerSyncEndpointProvider,
18
20
  ServerSyncEndpointProviderConfig,
19
21
  } from './ServerSyncEndpointProvider.js';
20
22
  import { WebSocketSync } from './WebSocketSync.js';
21
- import { Context } from '../context/context.js';
22
- import { attemptToRegisterBackgroundSync } from './background.js';
23
23
 
24
24
  type SyncEvents = {
25
25
  onlineChange: (isOnline: boolean) => void;
@@ -282,6 +282,11 @@ export class ServerSync<Presence = any, Profile = any>
282
282
  this.handleBroadcastChannelMessage,
283
283
  );
284
284
  }
285
+ ctx.log(
286
+ 'info',
287
+ 'Sync initialized with transport:',
288
+ initialTransport ?? 'pull',
289
+ );
285
290
  if (initialTransport === 'realtime') {
286
291
  this.activeSync = this.webSocketSync;
287
292
  } else {
@@ -290,8 +295,7 @@ export class ServerSync<Presence = any, Profile = any>
290
295
 
291
296
  this.presence.subscribe('update', this.handlePresenceUpdate);
292
297
 
293
- ctx.meta.events.subscribe('syncMessage', this.send);
294
-
298
+ ctx.internalEvents.subscribe('outgoingSyncMessage', this.send);
295
299
  this.webSocketSync.subscribe('message', this.handleMessage);
296
300
  this.webSocketSync.subscribe('onlineChange', this.handleOnlineChange);
297
301
 
@@ -384,11 +388,11 @@ export class ServerSync<Presence = any, Profile = any>
384
388
  baselines: message.baselines,
385
389
  });
386
390
  if (message.globalAckTimestamp) {
387
- await this.ctx.meta.setGlobalAck(message.globalAckTimestamp);
391
+ await (await this.ctx.meta).setGlobalAck(message.globalAckTimestamp);
388
392
  }
389
393
  break;
390
394
  case 'global-ack':
391
- await this.ctx.meta.setGlobalAck(message.timestamp);
395
+ await (await this.ctx.meta).setGlobalAck(message.timestamp);
392
396
  break;
393
397
  case 'sync-resp':
394
398
  this._activelySyncing = true;
@@ -400,10 +404,10 @@ export class ServerSync<Presence = any, Profile = any>
400
404
  });
401
405
 
402
406
  if (message.globalAckTimestamp) {
403
- await this.ctx.meta.setGlobalAck(message.globalAckTimestamp);
407
+ await (await this.ctx.meta).setGlobalAck(message.globalAckTimestamp);
404
408
  }
405
409
 
406
- await this.ctx.meta.updateLastSynced(message.ackedTimestamp);
410
+ await (await this.ctx.meta).updateLastSynced(message.ackedTimestamp);
407
411
  this._activelySyncing = false;
408
412
  this.emit('syncingChange', false);
409
413
  this._hasSynced = true;
@@ -411,13 +415,15 @@ export class ServerSync<Presence = any, Profile = any>
411
415
  break;
412
416
  case 'need-since':
413
417
  this.emit('serverReset', message.since);
414
- this.ctx.files.onServerReset(message.since);
418
+ (await this.ctx.files).onServerReset(message.since);
415
419
  this.activeSync.send(
416
- await this.ctx.meta.messageCreator.createSyncStep1(message.since),
420
+ await (
421
+ await this.ctx.meta
422
+ ).messageCreator.createSyncStep1(message.since),
417
423
  );
418
424
  break;
419
425
  case 'server-ack':
420
- await this.ctx.meta.updateLastSynced(message.timestamp);
426
+ await (await this.ctx.meta).updateLastSynced(message.timestamp);
421
427
  }
422
428
 
423
429
  // avoid rebroadcasting messages
@@ -430,7 +436,7 @@ export class ServerSync<Presence = any, Profile = any>
430
436
 
431
437
  // update presence if necessary
432
438
  this.presence[HANDLE_MESSAGE](
433
- await this.ctx.meta.getLocalReplica(),
439
+ await (await this.ctx.meta).getLocalReplica(),
434
440
  message,
435
441
  );
436
442
  };
@@ -439,7 +445,7 @@ export class ServerSync<Presence = any, Profile = any>
439
445
 
440
446
  // if online, attempt to upload any unsynced files.
441
447
  if (online) {
442
- const unsyncedFiles = await this.ctx.files.listUnsynced();
448
+ const unsyncedFiles = await (await this.ctx.files).listUnsynced();
443
449
  const results = await Promise.allSettled(
444
450
  unsyncedFiles.map((file) => this.fileSync.uploadFile(file)),
445
451
  );
@@ -458,7 +464,9 @@ export class ServerSync<Presence = any, Profile = any>
458
464
  presence?: Presence;
459
465
  internal?: VerdantInternalPresence;
460
466
  }) => {
461
- this.send(await this.ctx.meta.messageCreator.createPresenceUpdate(data));
467
+ this.send(
468
+ await (await this.ctx.meta).messageCreator.createPresenceUpdate(data),
469
+ );
462
470
  };
463
471
 
464
472
  setMode = (transport: SyncTransportMode) => {
@@ -512,7 +520,7 @@ export class ServerSync<Presence = any, Profile = any>
512
520
  if (message.type === 'sync' || message.type === 'op') {
513
521
  rewriteAuthzOriginator(message, userId);
514
522
  }
515
- await this.activeSync.send(message);
523
+ this.activeSync.send(message);
516
524
  this.onOutgoingMessage?.(message);
517
525
  }
518
526
  };
@@ -570,10 +578,12 @@ export class ServerSync<Presence = any, Profile = any>
570
578
  };
571
579
 
572
580
  public start = () => {
581
+ this.ctx.log('info', 'Starting sync');
573
582
  return this.activeSync.start();
574
583
  };
575
584
 
576
585
  public stop = () => {
586
+ this.ctx.log('info', 'Stopping sync');
577
587
  return this.activeSync.stop();
578
588
  };
579
589