@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
@@ -3,30 +3,117 @@ import {
3
3
  EventSubscriber,
4
4
  FileData,
5
5
  FileRef,
6
+ HybridLogicalClockTimestampProvider,
6
7
  Migration,
7
8
  ObjectIdentifier,
8
9
  Operation,
9
10
  PatchCreator,
10
11
  StorageSchema,
12
+ VerdantError,
11
13
  } from '@verdant-web/common';
14
+ import { FakeWeakRef } from '../FakeWeakRef.js';
12
15
  import { UndoHistory } from '../UndoHistory.js';
13
16
  import type { Client } from '../client/Client.js';
14
- import { VerdantLogger } from '../logger.js';
17
+ import { debugLogger, noLogger, VerdantLogger } from '../logger.js';
15
18
  import { PersistenceFiles } from '../persistence/PersistenceFiles.js';
16
19
  import type { PersistenceMetadata } from '../persistence/PersistenceMetadata.js';
17
20
  import type { PersistenceDocuments } from '../persistence/PersistenceQueries.js';
21
+ import { IdbPersistence } from '../persistence/idb/idbPersistence.js';
18
22
  import {
19
23
  PersistedFileData,
20
24
  PersistenceImplementation,
21
25
  } from '../persistence/interfaces.js';
26
+ import { initializePersistence } from '../persistence/persistence.js';
27
+ import { ServerSyncOptions } from '../sync/Sync.js';
22
28
  import { ShutdownHandler } from './ShutdownHandler.js';
23
29
  import { Time } from './Time.js';
24
30
 
31
+ export interface ContextEnvironment {
32
+ WebSocket: typeof WebSocket;
33
+ fetch: typeof fetch;
34
+ indexedDB: typeof indexedDB;
35
+ location: Location;
36
+ history: History;
37
+ }
38
+
39
+ export const defaultBrowserEnvironment: ContextEnvironment = {
40
+ WebSocket: typeof WebSocket !== 'undefined' ? WebSocket : (undefined as any),
41
+ fetch: typeof window !== 'undefined' ? window.fetch.bind(window) : fetch!,
42
+ indexedDB: typeof indexedDB !== 'undefined' ? indexedDB : (undefined as any),
43
+ location:
44
+ typeof window !== 'undefined' ? window.location : (undefined as any),
45
+ history: typeof window !== 'undefined' ? window.history : (undefined as any),
46
+ };
47
+
48
+ export interface ContextInit {
49
+ /** The schema used to create this client */
50
+ schema: StorageSchema<any>;
51
+ oldSchemas: StorageSchema<any>[];
52
+ /** Migrations, in order, to upgrade to each successive version of the schema */
53
+ migrations: Migration<any>[];
54
+ /** Provide a sync config to turn on synchronization with a server */
55
+ sync?: ServerSyncOptions;
56
+ /**
57
+ * Namespaces are used to separate data from different clients in IndexedDB.
58
+ */
59
+ namespace: string;
60
+ /**
61
+ * Provide your own UndoHistory to have a unified undo system across multiple
62
+ * clients if you so desire.
63
+ */
64
+ undoHistory?: UndoHistory;
65
+ /**
66
+ * Provide a log function to log internal debug messages
67
+ */
68
+ log?: VerdantLogger | false;
69
+ disableRebasing?: boolean;
70
+ rebaseTimeout?: number;
71
+ /**
72
+ * Provide a specific schema number to override the schema version
73
+ * in the database. This is useful for testing migrations or recovering
74
+ * from a mistakenly deployed incorrect schema. A specific version is required
75
+ * so that you don't leave this on accidentally for all new schemas.
76
+ */
77
+ overrideSchemaConflict?: number;
78
+ /**
79
+ * Configuration for file management
80
+ */
81
+ files?: FileConfig;
82
+
83
+ /**
84
+ * Override the default IndexedDB persistence implementation.
85
+ */
86
+ persistence?: PersistenceImplementation;
87
+
88
+ /**
89
+ * Specify the environment dependencies needed for the client.
90
+ * Normally these are provided by the browser, but in other
91
+ * runtimes you may need to provide your own.
92
+ */
93
+ environment?: Partial<ContextEnvironment>;
94
+
95
+ /**
96
+ * Enables experimental WeakRef usage to cull documents
97
+ * from cache that aren't being used. This is a performance
98
+ * optimization which has been tested under all Verdant's test
99
+ * suites but I still want to keep testing it in the real world
100
+ * before turning it on.
101
+ */
102
+ EXPERIMENTAL_weakRefs?: boolean;
103
+
104
+ /**
105
+ * Customize querying behavior.
106
+ */
107
+ queries?: QueryConfig;
108
+
109
+ persistenceShutdownHandler?: ShutdownHandler;
110
+ }
111
+
25
112
  /**
26
113
  * Common components utilized across various client
27
114
  * services.
28
115
  */
29
- export interface Context {
116
+ export class Context {
30
117
  namespace: string;
31
118
  /**
32
119
  * when in WIP mode, namespace might be set to a temporary value. This will always point to the
@@ -35,9 +122,16 @@ export interface Context {
35
122
  originalNamespace: string;
36
123
  time: Time;
37
124
 
38
- meta: PersistenceMetadata;
39
- documents: PersistenceDocuments;
40
- files: PersistenceFiles;
125
+ // async initialized services
126
+ get meta(): Promise<PersistenceMetadata> {
127
+ return this.initializedPromise.then((init) => init.meta);
128
+ }
129
+ get documents(): Promise<PersistenceDocuments> {
130
+ return this.initializedPromise.then((init) => init.documents);
131
+ }
132
+ get files(): Promise<PersistenceFiles> {
133
+ return this.initializedPromise.then((init) => init.files);
134
+ }
41
135
 
42
136
  undoHistory: UndoHistory;
43
137
  schema: StorageSchema;
@@ -57,6 +151,7 @@ export interface Context {
57
151
  fileAdded: (file: FileData) => void;
58
152
  [ev: `fileUploaded:${string}`]: (file: FileData) => void;
59
153
  fileUploaded: (file: FileData) => void;
154
+ outgoingSyncMessage: (message: ClientMessage) => void;
60
155
  }>;
61
156
  globalEvents: EventSubscriber<{
62
157
  /**
@@ -86,15 +181,22 @@ export interface Context {
86
181
  rebase: () => void;
87
182
  fileSaved: (file: FileData) => void;
88
183
  }>;
89
- weakRef<T extends object>(value: T): WeakRef<T>;
184
+ weakRef = <T extends object>(value: T): WeakRef<T> => {
185
+ if (this.init.EXPERIMENTAL_weakRefs) {
186
+ return new WeakRef(value);
187
+ }
188
+ return new FakeWeakRef(value) as any;
189
+ };
90
190
  migrations: Migration<any>[];
91
- closing: boolean;
92
191
  /** If this is present, any attempt to close the client should await it first. */
93
192
  closeLock?: Promise<void>;
94
- pauseRebasing: boolean;
95
193
  patchCreator: PatchCreator;
96
194
  persistenceShutdownHandler: ShutdownHandler;
97
195
 
196
+ // state
197
+ closing: boolean = false;
198
+ pauseRebasing: boolean = false;
199
+
98
200
  config: {
99
201
  files?: FileConfig;
100
202
  sync?: SyncConfig;
@@ -102,13 +204,7 @@ export interface Context {
102
204
  queries?: QueryConfig;
103
205
  };
104
206
 
105
- environment: {
106
- WebSocket: typeof WebSocket;
107
- fetch: typeof fetch;
108
- indexedDB: typeof indexedDB;
109
- location: Location;
110
- history: History;
111
- };
207
+ environment: ContextEnvironment;
112
208
 
113
209
  persistence: PersistenceImplementation;
114
210
 
@@ -116,7 +212,80 @@ export interface Context {
116
212
  * Must be defined by the Client once it exists. Attempts to use this before
117
213
  * it's ready will rightfully throw an error.
118
214
  */
119
- getClient: () => Client;
215
+ getClient = (): Client => {
216
+ throw new VerdantError(
217
+ VerdantError.Code.Unexpected,
218
+ undefined,
219
+ 'Client not yet initialized. This is a Verdant bug, please report it.',
220
+ );
221
+ };
222
+
223
+ constructor(
224
+ private init: ContextInit,
225
+ initPersistence?: ReturnType<typeof initializePersistence>,
226
+ ) {
227
+ // if server-side and no alternative IndexedDB implementation was provided,
228
+ // we can't initialize the storage
229
+ if (typeof window === 'undefined' && !this.init.environment) {
230
+ throw new Error(
231
+ 'A Verdant client was initialized in an environment without a global Window or `environment` configuration. If you are using verdant in a server-rendered framework, you must enforce that all clients are initialized on the client-side, or you must provide some mock interface of the environment to the Client options.',
232
+ );
233
+ }
234
+ // set static values
235
+ this.namespace = this.init.namespace;
236
+ this.originalNamespace = this.init.namespace;
237
+ this.time = new Time(
238
+ new HybridLogicalClockTimestampProvider(),
239
+ this.init.schema.version,
240
+ );
241
+ this.log =
242
+ this.init.log === false ? noLogger : this.init.log || debugLogger('🌿');
243
+ this.migrations = init.migrations;
244
+ this.undoHistory = init.undoHistory || new UndoHistory();
245
+ this.entityEvents = new EventSubscriber();
246
+ this.internalEvents = new EventSubscriber();
247
+ this.globalEvents = new EventSubscriber();
248
+ this.schema = init.schema;
249
+ this.oldSchemas = init.oldSchemas;
250
+ this.patchCreator = new PatchCreator(() => this.time.now);
251
+ this.persistenceShutdownHandler =
252
+ init.persistenceShutdownHandler || new ShutdownHandler(this.log);
253
+ this.config = {
254
+ files: init.files,
255
+ sync: init.sync,
256
+ persistence: {
257
+ disableRebasing: init.disableRebasing,
258
+ rebaseTimeout: init.rebaseTimeout,
259
+ },
260
+ queries: init.queries,
261
+ };
262
+ this.environment = {
263
+ ...defaultBrowserEnvironment,
264
+ ...init.environment,
265
+ };
266
+ this.persistence =
267
+ init.persistence || new IdbPersistence(this.environment.indexedDB);
268
+ this.initializedPromise = initPersistence || initializePersistence(this);
269
+ this.initializedPromise.then(() => {
270
+ this.log('info', 'Persistence initialized');
271
+ });
272
+ }
273
+ private initializedPromise;
274
+ get waitForInitialization(): Promise<void> {
275
+ return this.initializedPromise.then(() => {});
276
+ }
277
+ reinitialize = async (): Promise<void> => {
278
+ this.initializedPromise = initializePersistence(this);
279
+ await this.initializedPromise;
280
+ };
281
+
282
+ cloneWithOptions(options: Partial<ContextInit>): Context {
283
+ const copy = new Context(
284
+ { ...this.init, ...options },
285
+ this.initializedPromise,
286
+ );
287
+ return copy;
288
+ }
120
289
  }
121
290
 
122
291
  export interface FileConfig {
@@ -231,4 +400,7 @@ export interface QueryConfig {
231
400
  evictionTime?: number;
232
401
  }
233
402
 
234
- export type InitialContext = Omit<Context, 'documents' | 'meta' | 'files'>;
403
+ export type ContextWithoutPersistence = Omit<
404
+ Context,
405
+ 'meta' | 'documents' | 'files'
406
+ >;
@@ -261,7 +261,6 @@ describe('Entity', () => {
261
261
  entity.update({ string: 'new world' });
262
262
  expect(onPendingOperations).toHaveBeenCalledTimes(1);
263
263
  const operation = onPendingOperations.mock.calls[0][0][0];
264
- console.log(operation);
265
264
 
266
265
  entity.__discardPendingOperation__(operation);
267
266
 
@@ -274,12 +274,23 @@ export class EntityMetadata {
274
274
  if (op.data.op === 'delete') {
275
275
  deleted = true;
276
276
  } else {
277
- base = applyPatch(base, op.data);
278
- if (op.data.op === 'initialize') {
279
- deleted = false;
280
- if (op.authz) {
281
- authz = op.authz;
277
+ try {
278
+ base = applyPatch(base, op.data);
279
+ if (op.data.op === 'initialize') {
280
+ deleted = false;
281
+ if (op.authz) {
282
+ authz = op.authz;
283
+ }
282
284
  }
285
+ } catch (err) {
286
+ this.ctx.log(
287
+ 'critical',
288
+ `Failed to apply operation to entity ${this.oid}: ${JSON.stringify(
289
+ op,
290
+ )}`,
291
+ err,
292
+ );
293
+ throw err;
283
294
  }
284
295
  }
285
296
 
@@ -134,7 +134,7 @@ export class EntityStore extends Disposable {
134
134
  };
135
135
 
136
136
  empty = async () => {
137
- await this.ctx.documents.reset();
137
+ await (await this.ctx.documents).reset();
138
138
  this.events.resetAll.invoke(this);
139
139
  this.cache.clear();
140
140
  };
@@ -144,8 +144,8 @@ export class EntityStore extends Disposable {
144
144
  this.ctx.log('warn', 'EntityStore is disposed, not resetting local data');
145
145
  return;
146
146
  }
147
- await this.ctx.meta.reset();
148
- await this.ctx.documents.reset();
147
+ await (await this.ctx.meta).reset();
148
+ await (await this.ctx.documents).reset();
149
149
  this.events.resetAll.invoke(this);
150
150
  };
151
151
 
@@ -223,7 +223,7 @@ export class EntityStore extends Disposable {
223
223
  // TODO: could messages be sent to sync before storage,
224
224
  // so that realtime is lower latency? What would happen
225
225
  // if the storage failed?
226
- await this.ctx.meta.insertData(data, abortOptions);
226
+ await (await this.ctx.meta).insertData(data, abortOptions);
227
227
  this.ctx.log(
228
228
  'debug',
229
229
  'Data processing complete, all data saved to metadata db.',
@@ -247,7 +247,7 @@ export class EntityStore extends Disposable {
247
247
  );
248
248
  try {
249
249
  this.ctx.log('debug', 'Saving entities to queryable storage');
250
- await this.ctx.documents.saveEntities(entities, abortOptions);
250
+ await (await this.ctx.documents).saveEntities(entities, abortOptions);
251
251
  } catch (err) {
252
252
  if (this.disposed) {
253
253
  this.ctx.log(
@@ -319,6 +319,7 @@ export class EntityStore extends Disposable {
319
319
  };
320
320
 
321
321
  destroy = async () => {
322
+ this.ctx.log('warn', 'Disposing EntityStore');
322
323
  this.dispose();
323
324
  await this.batcher.flushAll();
324
325
  };
@@ -469,7 +470,11 @@ export class EntityStore extends Disposable {
469
470
  }
470
471
 
471
472
  if (this.disposed) {
472
- throw new Error('Cannot hydrate entity after store has been disposed');
473
+ this.ctx.log(
474
+ 'warn',
475
+ 'Cannot hydrate entity after store has been disposed',
476
+ );
477
+ return null;
473
478
  }
474
479
 
475
480
  const metadataFamily = new EntityFamilyMetadata({
@@ -522,10 +527,9 @@ export class EntityStore extends Disposable {
522
527
  entity: Entity,
523
528
  opts?: { abort: AbortSignal },
524
529
  ) => {
525
- const { operations, baselines } = await this.ctx.meta.getDocumentData(
526
- entity.oid,
527
- opts,
528
- );
530
+ const { operations, baselines } = await (
531
+ await this.ctx.meta
532
+ ).getDocumentData(entity.oid, opts);
529
533
 
530
534
  if (!baselines.length && !Object.keys(operations).length) {
531
535
  this.ctx.log('debug', 'No data found for entity', entity.oid);
@@ -138,7 +138,7 @@ export class EntityFile extends EventSubscriber<EntityFileEvents> {
138
138
  getSnapshot(): FileData {
139
139
  return {
140
140
  id: this.id,
141
- url: this._objectUrl ?? this._fileData?.url ?? undefined,
141
+ url: this._fileData?.url ?? this._objectUrl ?? undefined,
142
142
  name: this.name ?? 'unknown-file',
143
143
  remote: this._fileData?.remote ?? false,
144
144
  type: this.type ?? '',
@@ -37,7 +37,7 @@ export class FileManager extends Disposable {
37
37
  }
38
38
  // this will download any original remote file and trigger a re-upload to the
39
39
  // new file's identity, in addition to storing it on disk
40
- const processedFile = await this.context.files.add(file);
40
+ const processedFile = await (await this.context.files).add(file);
41
41
  entityFile[UPDATE](processedFile);
42
42
  };
43
43
 
@@ -59,7 +59,7 @@ export class FileManager extends Disposable {
59
59
  };
60
60
 
61
61
  private load = async (file: EntityFile) => {
62
- const fileData = await this.context.files.get(file.id);
62
+ const fileData = await (await this.context.files).get(file.id);
63
63
  if (fileData) {
64
64
  file[UPDATE](fileData);
65
65
  } else {
@@ -84,7 +84,7 @@ export class FileManager extends Disposable {
84
84
 
85
85
  const result = await this.sync.getFile(file.id);
86
86
  if (result.success) {
87
- await this.context.files.add(result.data);
87
+ await (await this.context.files).add(result.data);
88
88
  file[UPDATE](result.data);
89
89
  } else {
90
90
  this.context.log('error', 'Failed to load file', result);
@@ -97,8 +97,8 @@ export class FileManager extends Disposable {
97
97
  }
98
98
  };
99
99
 
100
- private onFileUploaded = (data: FileData) => {
100
+ private onFileUploaded = async (data: FileData) => {
101
101
  this.context.log('debug', 'Marking file as uploaded', data.id);
102
- this.context.files.onUploaded(data.id);
102
+ (await this.context.files).onUploaded(data.id);
103
103
  };
104
104
  }
package/src/index.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Client } from './client/Client.js';
2
- import {
3
- ClientDescriptor,
4
- ClientDescriptorOptions,
5
- } from './client/ClientDescriptor.js';
2
+ import type { ContextInit } from './context/context.js';
6
3
  export type { ClientWithCollections } from './client/Client.js';
7
- export { Client, ClientDescriptor };
4
+ export { Client, type ClientDescriptorOptions, type ClientInitOptions };
5
+ type ClientInitOptions = ContextInit;
6
+ /** @deprecated - use ClientInitOptions alias */
7
+ type ClientDescriptorOptions = ClientInitOptions;
8
8
  // backward compat
9
9
  export { createMigration, schema } from '@verdant-web/common';
10
10
  export type {
@@ -54,8 +54,4 @@ export { ServerSync, type ServerSyncOptions } from './sync/Sync.js';
54
54
  export type { SyncTransportMode } from './sync/Sync.js';
55
55
  export { UndoHistory } from './UndoHistory.js';
56
56
  export * from './utils/id.js';
57
- export { Client as Storage, ClientDescriptor as StorageDescriptor };
58
- export type {
59
- ClientDescriptorOptions,
60
- ClientDescriptorOptions as StorageInitOptions,
61
- };
57
+ export { Client as Storage };
package/src/internal.ts CHANGED
@@ -1,27 +1,26 @@
1
- export type { Context, InitialContext } from './context/context.js';
2
1
  export {
3
- decomposeOid,
2
+ assert,
3
+ createCompoundIndexValue,
4
+ createLowerBoundIndexValue,
4
5
  createOid,
6
+ createUpperBoundIndexValue,
7
+ decomposeOid,
5
8
  generateId,
6
- isRangeIndexFilter,
9
+ getIndexValues,
7
10
  isCompoundIndexFilter,
8
11
  isDirectSynthetic,
9
12
  isMatchIndexFilter,
13
+ isMultiValueIndex,
14
+ isRangeIndexFilter,
10
15
  isSortIndexFilter,
11
16
  isStartsWithIndexFilter,
12
- isMultiValueIndex,
13
- assert,
14
- createCompoundIndexValue,
15
- createLowerBoundIndexValue,
16
- createUpperBoundIndexValue,
17
- getIndexValues,
18
17
  } from '@verdant-web/common';
19
18
  export type {
20
19
  CollectionCompoundIndexFilter,
20
+ CollectionIndexFilter,
21
21
  MatchCollectionIndexFilter,
22
22
  RangeCollectionIndexFilter,
23
- CollectionIndexFilter,
24
23
  } from '@verdant-web/common';
24
+ export type { Context, ContextWithoutPersistence } from './context/context.js';
25
25
  export * from './persistence/migration/paths.js';
26
- export * from './persistence/migration/types.js';
27
26
  export { Disposable } from './utils/Disposable.js';
@@ -2,14 +2,14 @@ import {
2
2
  applyPatch,
3
3
  assert,
4
4
  assignOid,
5
- ClientMessage,
6
5
  DocumentBaseline,
7
- EventSubscriber,
8
6
  getOidRoot,
9
7
  ObjectIdentifier,
10
8
  Operation,
11
9
  substituteRefsWithObjects,
12
10
  } from '@verdant-web/common';
11
+ import cuid from 'cuid';
12
+ import { ContextWithoutPersistence } from '../context/context.js';
13
13
  import {
14
14
  AbstractTransaction,
15
15
  ClientOperation,
@@ -18,20 +18,18 @@ import {
18
18
  MetadataExport,
19
19
  PersistenceMetadataDb,
20
20
  } from './interfaces.js';
21
- import { InitialContext } from '../context/context.js';
22
- import { PersistenceRebaser } from './PersistenceRebaser.js';
23
21
  import { MessageCreator } from './MessageCreator.js';
24
- import cuid from 'cuid';
22
+ import { PersistenceRebaser } from './PersistenceRebaser.js';
25
23
 
26
24
  export class PersistenceMetadata {
27
25
  private rebaser: PersistenceRebaser;
28
26
  /** Available to others, like sync... */
29
27
  readonly messageCreator: MessageCreator;
30
- readonly events = new EventSubscriber<{
31
- syncMessage: (message: ClientMessage) => void;
32
- }>();
33
28
 
34
- constructor(private db: PersistenceMetadataDb, private ctx: InitialContext) {
29
+ constructor(
30
+ private db: PersistenceMetadataDb,
31
+ private ctx: ContextWithoutPersistence,
32
+ ) {
35
33
  this.rebaser = new PersistenceRebaser(db, this, ctx);
36
34
  this.messageCreator = new MessageCreator(db, this, ctx);
37
35
  }
@@ -82,7 +80,7 @@ export class PersistenceMetadata {
82
80
  `Inserted ${operations.length} local operations; sending sync message`,
83
81
  );
84
82
  const message = await this.messageCreator.createOperation({ operations });
85
- this.events.emit('syncMessage', message);
83
+ this.ctx.internalEvents.emit('outgoingSyncMessage', message);
86
84
  };
87
85
 
88
86
  private insertRemoteOperations = async (
@@ -500,7 +498,7 @@ export class PersistenceMetadata {
500
498
  // can't ack timestamps from the future.
501
499
  if (timestamp > this.ctx.time.now) return;
502
500
 
503
- this.events.emit('syncMessage', {
501
+ this.ctx.internalEvents.emit('outgoingSyncMessage', {
504
502
  type: 'ack',
505
503
  replicaId: localReplicaInfo.id,
506
504
  timestamp,
@@ -1,4 +1,4 @@
1
- import { Context, InitialContext } from '../../context/context.js';
1
+ import { Context } from '../../context/context.js';
2
2
  import { Disposable } from '../../utils/Disposable.js';
3
3
  import {
4
4
  createAbortableTransaction,
@@ -13,11 +13,10 @@ export class IdbService extends Disposable {
13
13
 
14
14
  constructor(
15
15
  protected db: IDBDatabase,
16
- protected readonly ctx: InitialContext,
16
+ protected readonly ctx: Pick<Context, 'log' | 'environment'>,
17
17
  ) {
18
18
  super();
19
19
  const abortController = new AbortController();
20
- const abort = abortController.abort.bind(abortController);
21
20
  this.globalAbortController = abortController;
22
21
  // FIXME: replace with event? I can't get this to work.
23
22
  // this.addDispose(abort);