@powerhousedao/reactor 5.0.1-staging.15 → 5.0.1-staging.16

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/src/client/reactor-client.d.ts +4 -2
  2. package/dist/src/client/reactor-client.d.ts.map +1 -1
  3. package/dist/src/client/reactor-client.js +112 -31
  4. package/dist/src/client/reactor-client.js.map +1 -1
  5. package/dist/src/core/builder.d.ts +15 -2
  6. package/dist/src/core/builder.d.ts.map +1 -1
  7. package/dist/src/core/builder.js +48 -7
  8. package/dist/src/core/builder.js.map +1 -1
  9. package/dist/src/core/reactor-builder.d.ts +8 -0
  10. package/dist/src/core/reactor-builder.d.ts.map +1 -1
  11. package/dist/src/core/reactor-builder.js +26 -7
  12. package/dist/src/core/reactor-builder.js.map +1 -1
  13. package/dist/src/core/reactor.d.ts +12 -1
  14. package/dist/src/core/reactor.d.ts.map +1 -1
  15. package/dist/src/core/reactor.js +94 -92
  16. package/dist/src/core/reactor.js.map +1 -1
  17. package/dist/src/core/types.d.ts +20 -0
  18. package/dist/src/core/types.d.ts.map +1 -1
  19. package/dist/src/core/utils.d.ts +0 -4
  20. package/dist/src/core/utils.d.ts.map +1 -1
  21. package/dist/src/core/utils.js +0 -9
  22. package/dist/src/core/utils.js.map +1 -1
  23. package/dist/src/events/types.d.ts +28 -0
  24. package/dist/src/events/types.d.ts.map +1 -1
  25. package/dist/src/events/types.js +2 -0
  26. package/dist/src/events/types.js.map +1 -1
  27. package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
  28. package/dist/src/executor/simple-job-executor-manager.js +19 -3
  29. package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
  30. package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
  31. package/dist/src/executor/simple-job-executor.js +21 -7
  32. package/dist/src/executor/simple-job-executor.js.map +1 -1
  33. package/dist/src/index.d.ts +2 -0
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js +3 -0
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/job-tracker/in-memory-job-tracker.d.ts +11 -2
  38. package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -1
  39. package/dist/src/job-tracker/in-memory-job-tracker.js +54 -26
  40. package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -1
  41. package/dist/src/job-tracker/interfaces.d.ts +6 -9
  42. package/dist/src/job-tracker/interfaces.d.ts.map +1 -1
  43. package/dist/src/read-models/coordinator.d.ts.map +1 -1
  44. package/dist/src/read-models/coordinator.js +11 -0
  45. package/dist/src/read-models/coordinator.js.map +1 -1
  46. package/dist/src/read-models/document-view.d.ts +1 -0
  47. package/dist/src/read-models/document-view.d.ts.map +1 -1
  48. package/dist/src/read-models/document-view.js +40 -0
  49. package/dist/src/read-models/document-view.js.map +1 -1
  50. package/dist/src/shared/awaiter.d.ts +11 -8
  51. package/dist/src/shared/awaiter.d.ts.map +1 -1
  52. package/dist/src/shared/awaiter.js +66 -75
  53. package/dist/src/shared/awaiter.js.map +1 -1
  54. package/dist/src/shared/types.d.ts +4 -2
  55. package/dist/src/shared/types.d.ts.map +1 -1
  56. package/dist/src/shared/types.js +4 -2
  57. package/dist/src/shared/types.js.map +1 -1
  58. package/dist/src/storage/index.d.ts +4 -0
  59. package/dist/src/storage/index.d.ts.map +1 -0
  60. package/dist/src/storage/index.js +3 -0
  61. package/dist/src/storage/index.js.map +1 -0
  62. package/dist/src/storage/interfaces.d.ts +79 -0
  63. package/dist/src/storage/interfaces.d.ts.map +1 -1
  64. package/dist/src/storage/interfaces.js.map +1 -1
  65. package/dist/src/storage/kysely/sync-cursor-storage.d.ts +13 -0
  66. package/dist/src/storage/kysely/sync-cursor-storage.d.ts.map +1 -0
  67. package/dist/src/storage/kysely/sync-cursor-storage.js +93 -0
  68. package/dist/src/storage/kysely/sync-cursor-storage.js.map +1 -0
  69. package/dist/src/storage/kysely/sync-remote-storage.d.ts +13 -0
  70. package/dist/src/storage/kysely/sync-remote-storage.d.ts.map +1 -0
  71. package/dist/src/storage/kysely/sync-remote-storage.js +134 -0
  72. package/dist/src/storage/kysely/sync-remote-storage.js.map +1 -0
  73. package/dist/src/storage/kysely/types.d.ts +35 -0
  74. package/dist/src/storage/kysely/types.d.ts.map +1 -1
  75. package/dist/src/storage/migrations/010_create_sync_tables.d.ts +3 -0
  76. package/dist/src/storage/migrations/010_create_sync_tables.d.ts.map +1 -0
  77. package/dist/src/storage/migrations/010_create_sync_tables.js +43 -0
  78. package/dist/src/storage/migrations/010_create_sync_tables.js.map +1 -0
  79. package/dist/src/storage/migrations/migrator.d.ts.map +1 -1
  80. package/dist/src/storage/migrations/migrator.js +2 -0
  81. package/dist/src/storage/migrations/migrator.js.map +1 -1
  82. package/dist/src/sync/channels/index.d.ts +3 -0
  83. package/dist/src/sync/channels/index.d.ts.map +1 -0
  84. package/dist/src/sync/channels/index.js +3 -0
  85. package/dist/src/sync/channels/index.js.map +1 -0
  86. package/dist/src/sync/channels/internal-channel.d.ts +57 -0
  87. package/dist/src/sync/channels/internal-channel.d.ts.map +1 -0
  88. package/dist/src/sync/channels/internal-channel.js +106 -0
  89. package/dist/src/sync/channels/internal-channel.js.map +1 -0
  90. package/dist/src/sync/channels/utils.d.ts +15 -0
  91. package/dist/src/sync/channels/utils.d.ts.map +1 -0
  92. package/dist/src/sync/channels/utils.js +26 -0
  93. package/dist/src/sync/channels/utils.js.map +1 -0
  94. package/dist/src/sync/errors.d.ts +10 -0
  95. package/dist/src/sync/errors.d.ts.map +1 -0
  96. package/dist/src/sync/errors.js +17 -0
  97. package/dist/src/sync/errors.js.map +1 -0
  98. package/dist/src/sync/index.d.ts +12 -0
  99. package/dist/src/sync/index.d.ts.map +1 -0
  100. package/dist/src/sync/index.js +9 -0
  101. package/dist/src/sync/index.js.map +1 -0
  102. package/dist/src/sync/interfaces.d.ts +150 -0
  103. package/dist/src/sync/interfaces.d.ts.map +1 -0
  104. package/dist/src/sync/interfaces.js +2 -0
  105. package/dist/src/sync/interfaces.js.map +1 -0
  106. package/dist/src/sync/mailbox.d.ts +21 -0
  107. package/dist/src/sync/mailbox.d.ts.map +1 -0
  108. package/dist/src/sync/mailbox.js +59 -0
  109. package/dist/src/sync/mailbox.js.map +1 -0
  110. package/dist/src/sync/sync-builder.d.ts +17 -0
  111. package/dist/src/sync/sync-builder.d.ts.map +1 -0
  112. package/dist/src/sync/sync-builder.js +29 -0
  113. package/dist/src/sync/sync-builder.js.map +1 -0
  114. package/dist/src/sync/sync-manager.d.ts +33 -0
  115. package/dist/src/sync/sync-manager.d.ts.map +1 -0
  116. package/dist/src/sync/sync-manager.js +197 -0
  117. package/dist/src/sync/sync-manager.js.map +1 -0
  118. package/dist/src/sync/sync-operation.d.ts +28 -0
  119. package/dist/src/sync/sync-operation.d.ts.map +1 -0
  120. package/dist/src/sync/sync-operation.js +63 -0
  121. package/dist/src/sync/sync-operation.js.map +1 -0
  122. package/dist/src/sync/types.d.ts +61 -0
  123. package/dist/src/sync/types.d.ts.map +1 -0
  124. package/dist/src/sync/types.js +16 -0
  125. package/dist/src/sync/types.js.map +1 -0
  126. package/dist/src/sync/utils.d.ts +17 -0
  127. package/dist/src/sync/utils.d.ts.map +1 -0
  128. package/dist/src/sync/utils.js +34 -0
  129. package/dist/src/sync/utils.js.map +1 -0
  130. package/package.json +4 -3
@@ -0,0 +1,26 @@
1
+ import { SyncOperation } from "../sync-operation.js";
2
+ let syncOpCounter = 0;
3
+ /**
4
+ * Converts a SyncEnvelope containing operations into a SyncOperation.
5
+ *
6
+ * Extracts the necessary metadata from the envelope's operations to create
7
+ * a sync operation that can be processed by the receiving channel.
8
+ *
9
+ * @param envelope - The sync envelope containing operations
10
+ * @param remoteName - The name of the remote this sync operation is associated with
11
+ * @returns A new SyncOperation containing the envelope's operations
12
+ * @throws Error if envelope has no operations or operations array is empty
13
+ */
14
+ export function envelopeToSyncOperation(envelope, remoteName) {
15
+ if (!envelope.operations || envelope.operations.length === 0) {
16
+ throw new Error("Cannot create SyncOperation from envelope without operations");
17
+ }
18
+ const operations = envelope.operations;
19
+ const firstOp = operations[0];
20
+ const documentId = firstOp.context.documentId;
21
+ const branch = firstOp.context.branch;
22
+ const scopes = [...new Set(operations.map((op) => op.context.scope))];
23
+ const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;
24
+ return new SyncOperation(syncOpId, remoteName, documentId, scopes, branch, operations);
25
+ }
26
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/sync/channels/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,IAAI,aAAa,GAAG,CAAC,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAsB,EACtB,UAAkB;IAElB,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IACvC,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;IACtC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEtE,MAAM,QAAQ,GAAG,UAAU,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,aAAa,EAAE,EAAE,CAAC;IAEtF,OAAO,IAAI,aAAa,CACtB,QAAQ,EACR,UAAU,EACV,UAAU,EACV,MAAM,EACN,MAAM,EACN,UAAU,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ChannelErrorSource } from "./types.js";
2
+ export declare class InternalChannelError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare class ChannelError extends Error {
6
+ source: ChannelErrorSource;
7
+ error: Error;
8
+ constructor(source: ChannelErrorSource, error: Error);
9
+ }
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/sync/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;gBAED,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,KAAK;CAMrD"}
@@ -0,0 +1,17 @@
1
+ export class InternalChannelError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "InternalChannelError";
5
+ }
6
+ }
7
+ export class ChannelError extends Error {
8
+ source;
9
+ error;
10
+ constructor(source, error) {
11
+ super(`ChannelError[${source}]: ${error.message}`);
12
+ this.name = "ChannelError";
13
+ this.source = source;
14
+ this.error = error;
15
+ }
16
+ }
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/sync/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,MAAM,CAAqB;IAC3B,KAAK,CAAQ;IAEb,YAAY,MAA0B,EAAE,KAAY;QAClD,KAAK,CAAC,gBAAgB,MAAM,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ export type { IChannel, IChannelFactory, ISyncManager, Remote, } from "./interfaces.js";
2
+ export type { ShutdownStatus } from "../shared/types.js";
3
+ export type { ChannelConfig, ChannelHealth, SyncOperationErrorType, RemoteFilter, RemoteOptions, RemoteRecord, RemoteCursor, RemoteStatus, SyncEnvelope, SyncEnvelopeType, ChannelMeta, } from "./types.js";
4
+ export { ChannelErrorSource, SyncOperationStatus } from "./types.js";
5
+ export { SyncOperation, SyncOperationAggregateError, } from "./sync-operation.js";
6
+ export { Mailbox, type MailboxItem } from "./mailbox.js";
7
+ export { ChannelError, InternalChannelError } from "./errors.js";
8
+ export { InternalChannel } from "./channels/index.js";
9
+ export { SyncManager } from "./sync-manager.js";
10
+ export { SyncBuilder } from "./sync-builder.js";
11
+ export { createIdleHealth, filterOperations } from "./utils.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sync/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,MAAM,GACP,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,YAAY,EACV,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,aAAa,EACb,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { ChannelErrorSource, SyncOperationStatus } from "./types.js";
2
+ export { SyncOperation, SyncOperationAggregateError, } from "./sync-operation.js";
3
+ export { Mailbox } from "./mailbox.js";
4
+ export { ChannelError, InternalChannelError } from "./errors.js";
5
+ export { InternalChannel } from "./channels/index.js";
6
+ export { SyncManager } from "./sync-manager.js";
7
+ export { SyncBuilder } from "./sync-builder.js";
8
+ export { createIdleHealth, filterOperations } from "./utils.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sync/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,aAAa,EACb,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAoB,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,150 @@
1
+ import type { ShutdownStatus } from "../shared/types.js";
2
+ import type { ISyncCursorStorage } from "../storage/interfaces.js";
3
+ import type { SyncOperation } from "./sync-operation.js";
4
+ import type { Mailbox } from "./mailbox.js";
5
+ import type { ChannelConfig, RemoteFilter, RemoteOptions } from "./types.js";
6
+ /**
7
+ * Represents a bidirectional synchronization channel between two reactor instances.
8
+ *
9
+ * A channel manages three mailboxes:
10
+ * - inbox: Sync operations received from the remote that need to be applied locally
11
+ * - outbox: Sync operations to be sent to the remote
12
+ * - deadLetter: Sync operations that failed and cannot be retried
13
+ *
14
+ * Channels are responsible for:
15
+ * - Transporting sync envelopes between reactor instances
16
+ * - Managing sync operation lifecycle through status transitions
17
+ * - Handling errors and moving failed sync operations to dead letter queue
18
+ * - Updating cursors as operations are applied
19
+ */
20
+ export interface IChannel {
21
+ /**
22
+ * Mailbox containing sync operations received from the remote that need to be applied locally.
23
+ * Consumers should register callbacks via onAdded to process incoming sync operations.
24
+ */
25
+ inbox: Mailbox<SyncOperation>;
26
+ /**
27
+ * Mailbox containing sync operations that need to be sent to the remote.
28
+ * The channel is responsible for transporting these sync operations and handling ACKs.
29
+ */
30
+ outbox: Mailbox<SyncOperation>;
31
+ /**
32
+ * Mailbox containing sync operations that failed and cannot be retried.
33
+ * These sync operations require manual intervention or should be logged for debugging.
34
+ */
35
+ deadLetter: Mailbox<SyncOperation>;
36
+ /**
37
+ * Shuts down the channel and prevents further operations.
38
+ */
39
+ shutdown(): void;
40
+ }
41
+ /**
42
+ * Factory for creating channel instances.
43
+ *
44
+ * Different channel implementations (InternalChannel, GqlChannel, etc.) will have
45
+ * their own factories that implement this interface.
46
+ */
47
+ export interface IChannelFactory {
48
+ /**
49
+ * Creates a new channel instance with the given configuration.
50
+ *
51
+ * @param config - Channel configuration including type and parameters
52
+ * @param cursorStorage - Storage for persisting synchronization cursors
53
+ * @returns A new channel instance
54
+ */
55
+ instance(config: ChannelConfig, cursorStorage: ISyncCursorStorage): IChannel;
56
+ }
57
+ /**
58
+ * Represents a configured remote with an active channel.
59
+ *
60
+ * A remote defines what to synchronize (collectionId, filter) and how to synchronize it (channel).
61
+ * The remote name is used as a unique identifier across the system.
62
+ */
63
+ export type Remote = {
64
+ /**
65
+ * Unique name for this remote.
66
+ */
67
+ name: string;
68
+ /**
69
+ * Collection ID to synchronize.
70
+ * Typically created via driveCollectionId(branch, documentId) for drive-level sync.
71
+ */
72
+ collectionId: string;
73
+ /**
74
+ * Filter to apply to operations.
75
+ * Can filter by documentId, scope, and branch.
76
+ */
77
+ filter: RemoteFilter;
78
+ /**
79
+ * Additional configuration options for the remote.
80
+ */
81
+ options: RemoteOptions;
82
+ /**
83
+ * Active channel for bidirectional communication with the remote.
84
+ */
85
+ channel: IChannel;
86
+ };
87
+ /**
88
+ * Orchestrates all synchronization activity for a reactor instance.
89
+ */
90
+ export interface ISyncManager {
91
+ /**
92
+ * Starts the synchronization manager.
93
+ *
94
+ * This recreates all remotes from storage and prepares channels for synchronization.
95
+ * Each remote's channel will begin processing its inbox/outbox mailboxes.
96
+ *
97
+ * @returns Promise that resolves when the manager is started
98
+ */
99
+ startup(): Promise<void>;
100
+ /**
101
+ * Shuts down the synchronization manager.
102
+ *
103
+ * This stops all channels, flushes pending operations, and releases resources.
104
+ * The shutdown status indicates whether the system was cleanly shut down.
105
+ *
106
+ * @returns Status object with shutdown information
107
+ */
108
+ shutdown(): ShutdownStatus;
109
+ /**
110
+ * Gets a remote by name.
111
+ *
112
+ * @param name - The name of the remote
113
+ * @returns The remote
114
+ * @throws Error if the remote does not exist
115
+ */
116
+ get(name: string): Remote;
117
+ /**
118
+ * Adds a new remote and starts its channel.
119
+ *
120
+ * The remote configuration is persisted to storage and a channel is created
121
+ * using the appropriate factory. The channel begins processing immediately.
122
+ *
123
+ * @param name - Unique name for the remote
124
+ * @param collectionId - Collection ID to synchronize
125
+ * @param channelConfig - Configuration for the channel type and parameters
126
+ * @param filter - Optional filter for operations (defaults to no filtering)
127
+ * @param options - Optional remote configuration options
128
+ * @returns Promise that resolves with the created remote
129
+ * @throws Error if a remote with this name already exists
130
+ */
131
+ add(name: string, collectionId: string, channelConfig: ChannelConfig, filter?: RemoteFilter, options?: RemoteOptions): Promise<Remote>;
132
+ /**
133
+ * Removes a remote and stops its channel.
134
+ *
135
+ * The remote configuration is removed from storage and the channel is shut down.
136
+ * Any pending sync operations in the channel's mailboxes will be processed before shutdown.
137
+ *
138
+ * @param name - The name of the remote to remove
139
+ * @returns Promise that resolves when the remote is removed
140
+ * @throws Error if the remote does not exist
141
+ */
142
+ remove(name: string): Promise<void>;
143
+ /**
144
+ * Lists all configured remotes.
145
+ *
146
+ * @returns Array of all remotes
147
+ */
148
+ list(): Remote[];
149
+ }
150
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/sync/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7E;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAE9B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAE/B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAEnC;;OAEG;IACH,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,GAAG,QAAQ,CAAC;CAC9E;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IAErB;;OAEG;IACH,OAAO,EAAE,aAAa,CAAC;IAEvB;;OAEG;IACH,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB;;;;;;;OAOG;IACH,QAAQ,IAAI,cAAc,CAAC;IAE3B;;;;;;OAMG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1B;;;;;;;;;;;;;OAaG;IACH,GAAG,CACD,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,EAC5B,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;;;;;;OASG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;;;OAIG;IACH,IAAI,IAAI,MAAM,EAAE,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../../src/sync/interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ export type MailboxItem = {
2
+ id: string;
3
+ };
4
+ type MailboxCallback<T extends MailboxItem> = (item: T) => void;
5
+ export declare class MailboxAggregateError extends Error {
6
+ errors: Error[];
7
+ constructor(errors: Error[]);
8
+ }
9
+ export declare class Mailbox<T extends MailboxItem> {
10
+ private itemsMap;
11
+ private addedCallbacks;
12
+ private removedCallbacks;
13
+ get items(): ReadonlyArray<T>;
14
+ get(id: string): T | undefined;
15
+ add(item: T): void;
16
+ remove(item: T): void;
17
+ onAdded(callback: MailboxCallback<T>): void;
18
+ onRemoved(callback: MailboxCallback<T>): void;
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=mailbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailbox.d.ts","sourceRoot":"","sources":["../../../src/sync/mailbox.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,eAAe,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAEhE,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,MAAM,EAAE,KAAK,EAAE,CAAC;gBAEJ,MAAM,EAAE,KAAK,EAAE;CAQ5B;AAED,qBAAa,OAAO,CAAC,CAAC,SAAS,WAAW;IACxC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,gBAAgB,CAA4B;IAEpD,IAAI,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,CAE5B;IAED,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI9B,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAgBlB,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAgBrB,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI;IAI3C,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI;CAG9C"}
@@ -0,0 +1,59 @@
1
+ export class MailboxAggregateError extends Error {
2
+ errors;
3
+ constructor(errors) {
4
+ const messages = errors.map((e) => e.message).join("; ");
5
+ super(`Mailbox callback failed with ${errors.length} error(s): ${messages}`);
6
+ this.name = "MailboxAggregateError";
7
+ this.errors = errors;
8
+ }
9
+ }
10
+ export class Mailbox {
11
+ itemsMap = new Map();
12
+ addedCallbacks = [];
13
+ removedCallbacks = [];
14
+ get items() {
15
+ return Array.from(this.itemsMap.values());
16
+ }
17
+ get(id) {
18
+ return this.itemsMap.get(id);
19
+ }
20
+ add(item) {
21
+ this.itemsMap.set(item.id, item);
22
+ const callbacks = [...this.addedCallbacks];
23
+ const errors = [];
24
+ for (const callback of callbacks) {
25
+ try {
26
+ callback(item);
27
+ }
28
+ catch (error) {
29
+ errors.push(error instanceof Error ? error : new Error(String(error)));
30
+ }
31
+ }
32
+ if (errors.length > 0) {
33
+ throw new MailboxAggregateError(errors);
34
+ }
35
+ }
36
+ remove(item) {
37
+ this.itemsMap.delete(item.id);
38
+ const callbacks = [...this.removedCallbacks];
39
+ const errors = [];
40
+ for (const callback of callbacks) {
41
+ try {
42
+ callback(item);
43
+ }
44
+ catch (error) {
45
+ errors.push(error instanceof Error ? error : new Error(String(error)));
46
+ }
47
+ }
48
+ if (errors.length > 0) {
49
+ throw new MailboxAggregateError(errors);
50
+ }
51
+ }
52
+ onAdded(callback) {
53
+ this.addedCallbacks.push(callback);
54
+ }
55
+ onRemoved(callback) {
56
+ this.removedCallbacks.push(callback);
57
+ }
58
+ }
59
+ //# sourceMappingURL=mailbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailbox.js","sourceRoot":"","sources":["../../../src/sync/mailbox.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,MAAM,CAAU;IAEhB,YAAY,MAAe;QACzB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,KAAK,CACH,gCAAgC,MAAM,CAAC,MAAM,cAAc,QAAQ,EAAE,CACtE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,OAAO,OAAO;IACV,QAAQ,GAAmB,IAAI,GAAG,EAAE,CAAC;IACrC,cAAc,GAAyB,EAAE,CAAC;IAC1C,gBAAgB,GAAyB,EAAE,CAAC;IAEpD,IAAI,KAAK;QACP,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,IAAO;QACT,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAO;QACZ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO,CAAC,QAA4B;QAClC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,SAAS,CAAC,QAA4B;QACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { Kysely } from "kysely";
2
+ import type { IOperationIndex } from "../cache/operation-index-types.js";
3
+ import type { IReactor } from "../core/types.js";
4
+ import type { IEventBus } from "../events/interfaces.js";
5
+ import type { ISyncCursorStorage, ISyncRemoteStorage } from "../storage/interfaces.js";
6
+ import type { Database } from "../storage/kysely/types.js";
7
+ import type { IChannelFactory, ISyncManager } from "./interfaces.js";
8
+ export declare class SyncBuilder {
9
+ private channelFactory?;
10
+ private remoteStorage?;
11
+ private cursorStorage?;
12
+ withChannelFactory(factory: IChannelFactory): this;
13
+ withRemoteStorage(storage: ISyncRemoteStorage): this;
14
+ withCursorStorage(storage: ISyncCursorStorage): this;
15
+ build(reactor: IReactor, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database>): ISyncManager;
16
+ }
17
+ //# sourceMappingURL=sync-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-builder.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGrE,qBAAa,WAAW;IACtB,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAC,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAqB;IAE3C,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAKlD,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAKpD,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAKpD,KAAK,CACH,OAAO,EAAE,QAAQ,EACjB,cAAc,EAAE,eAAe,EAC/B,QAAQ,EAAE,SAAS,EACnB,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,GACnB,YAAY;CAiBhB"}
@@ -0,0 +1,29 @@
1
+ import { KyselySyncCursorStorage } from "../storage/kysely/sync-cursor-storage.js";
2
+ import { KyselySyncRemoteStorage } from "../storage/kysely/sync-remote-storage.js";
3
+ import { SyncManager } from "./sync-manager.js";
4
+ export class SyncBuilder {
5
+ channelFactory;
6
+ remoteStorage;
7
+ cursorStorage;
8
+ withChannelFactory(factory) {
9
+ this.channelFactory = factory;
10
+ return this;
11
+ }
12
+ withRemoteStorage(storage) {
13
+ this.remoteStorage = storage;
14
+ return this;
15
+ }
16
+ withCursorStorage(storage) {
17
+ this.cursorStorage = storage;
18
+ return this;
19
+ }
20
+ build(reactor, operationIndex, eventBus, db) {
21
+ if (!this.channelFactory) {
22
+ throw new Error("Channel factory is required");
23
+ }
24
+ const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);
25
+ const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);
26
+ return new SyncManager(remoteStorage, cursorStorage, this.channelFactory, operationIndex, reactor, eventBus);
27
+ }
28
+ }
29
+ //# sourceMappingURL=sync-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-builder.js","sourceRoot":"","sources":["../../../src/sync/sync-builder.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,OAAO,WAAW;IACd,cAAc,CAAmB;IACjC,aAAa,CAAsB;IACnC,aAAa,CAAsB;IAE3C,kBAAkB,CAAC,OAAwB;QACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iBAAiB,CAAC,OAA2B;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iBAAiB,CAAC,OAA2B;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CACH,OAAiB,EACjB,cAA+B,EAC/B,QAAmB,EACnB,EAAoB;QAEpB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAE5E,OAAO,IAAI,WAAW,CACpB,aAAa,EACb,aAAa,EACb,IAAI,CAAC,cAAc,EACnB,cAAc,EACd,OAAO,EACP,QAAQ,CACT,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,33 @@
1
+ import type { IOperationIndex } from "../cache/operation-index-types.js";
2
+ import type { IReactor } from "../core/types.js";
3
+ import type { IEventBus } from "../events/interfaces.js";
4
+ import { type JobInfo, type ShutdownStatus } from "../shared/types.js";
5
+ import type { ISyncCursorStorage, ISyncRemoteStorage } from "../storage/interfaces.js";
6
+ import type { IChannelFactory, ISyncManager, Remote } from "./interfaces.js";
7
+ import type { ChannelConfig, RemoteFilter, RemoteOptions } from "./types.js";
8
+ export declare class SyncManager implements ISyncManager {
9
+ private readonly remoteStorage;
10
+ private readonly cursorStorage;
11
+ private readonly channelFactory;
12
+ private readonly _operationIndex;
13
+ private readonly reactor;
14
+ private readonly eventBus;
15
+ private readonly remotes;
16
+ private readonly awaiter;
17
+ private isShutdown;
18
+ private eventUnsubscribe?;
19
+ loadJobs: Map<string, JobInfo>;
20
+ constructor(remoteStorage: ISyncRemoteStorage, cursorStorage: ISyncCursorStorage, channelFactory: IChannelFactory, operationIndex: IOperationIndex, reactor: IReactor, eventBus: IEventBus);
21
+ startup(): Promise<void>;
22
+ shutdown(): ShutdownStatus;
23
+ get(name: string): Remote;
24
+ add(name: string, collectionId: string, channelConfig: ChannelConfig, filter?: RemoteFilter, options?: RemoteOptions): Promise<Remote>;
25
+ remove(name: string): Promise<void>;
26
+ list(): Remote[];
27
+ private wireChannelCallbacks;
28
+ private handleOperationWritten;
29
+ private handleInboxJob;
30
+ private handleOutboxJob;
31
+ private applyInboxJob;
32
+ }
33
+ //# sourceMappingURL=sync-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-manager.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAKzD,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EAGd,MAAM,YAAY,CAAC;AAIpB,qBAAa,WAAY,YAAW,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkB;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,gBAAgB,CAAC,CAAa;IAE/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAa;gBAGhD,aAAa,EAAE,kBAAkB,EACjC,aAAa,EAAE,kBAAkB,EACjC,cAAc,EAAE,eAAe,EAC/B,cAAc,EAAE,eAAe,EAC/B,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,SAAS;IAef,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B9B,QAAQ,IAAI,cAAc;IA2B1B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAQnB,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,EAC5B,MAAM,GAAE,YAAwD,EAChE,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC;IA2CZ,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzC,IAAI,IAAI,MAAM,EAAE;IAIhB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,eAAe;YAUT,aAAa;CAoD5B"}
@@ -0,0 +1,197 @@
1
+ import { OperationEventTypes, } from "../events/types.js";
2
+ import { JobStatus, } from "../shared/types.js";
3
+ import { JobAwaiter } from "../shared/awaiter.js";
4
+ import { ChannelError } from "./errors.js";
5
+ import { SyncOperation } from "./sync-operation.js";
6
+ import { ChannelErrorSource, SyncOperationStatus } from "./types.js";
7
+ import { createIdleHealth, filterOperations } from "./utils.js";
8
+ export class SyncManager {
9
+ remoteStorage;
10
+ cursorStorage;
11
+ channelFactory;
12
+ _operationIndex;
13
+ reactor;
14
+ eventBus;
15
+ remotes;
16
+ awaiter;
17
+ isShutdown;
18
+ eventUnsubscribe;
19
+ loadJobs = new Map();
20
+ constructor(remoteStorage, cursorStorage, channelFactory, operationIndex, reactor, eventBus) {
21
+ this.remoteStorage = remoteStorage;
22
+ this.cursorStorage = cursorStorage;
23
+ this.channelFactory = channelFactory;
24
+ this._operationIndex = operationIndex;
25
+ this.reactor = reactor;
26
+ this.eventBus = eventBus;
27
+ this.remotes = new Map();
28
+ this.awaiter = new JobAwaiter(eventBus, (jobId, signal) => reactor.getJobStatus(jobId, signal));
29
+ this.isShutdown = false;
30
+ }
31
+ async startup() {
32
+ if (this.isShutdown) {
33
+ throw new Error("SyncManager is already shutdown and cannot be started");
34
+ }
35
+ const remoteRecords = await this.remoteStorage.list();
36
+ for (const record of remoteRecords) {
37
+ const channel = this.channelFactory.instance(record.channelConfig, this.cursorStorage);
38
+ const remote = {
39
+ name: record.name,
40
+ collectionId: record.collectionId,
41
+ filter: record.filter,
42
+ options: record.options,
43
+ channel,
44
+ };
45
+ this.remotes.set(record.name, remote);
46
+ this.wireChannelCallbacks(remote);
47
+ }
48
+ this.eventUnsubscribe = this.eventBus.subscribe(OperationEventTypes.OPERATION_WRITTEN, (_type, event) => this.handleOperationWritten(event));
49
+ }
50
+ shutdown() {
51
+ this.isShutdown = true;
52
+ if (this.eventUnsubscribe) {
53
+ this.eventUnsubscribe();
54
+ this.eventUnsubscribe = undefined;
55
+ }
56
+ this.awaiter.shutdown();
57
+ for (const remote of this.remotes.values()) {
58
+ try {
59
+ remote.channel.shutdown();
60
+ }
61
+ catch (error) {
62
+ console.error(`Error shutting down channel for remote ${remote.name}: ${error instanceof Error ? error.message : String(error)}`);
63
+ }
64
+ }
65
+ this.remotes.clear();
66
+ return {
67
+ isShutdown: true,
68
+ };
69
+ }
70
+ get(name) {
71
+ const remote = this.remotes.get(name);
72
+ if (!remote) {
73
+ throw new Error(`Remote with name '${name}' does not exist`);
74
+ }
75
+ return remote;
76
+ }
77
+ async add(name, collectionId, channelConfig, filter = { documentId: [], scope: [], branch: "" }, options = {}) {
78
+ if (this.isShutdown) {
79
+ throw new Error("SyncManager is shutdown and cannot add remotes");
80
+ }
81
+ if (this.remotes.has(name)) {
82
+ throw new Error(`Remote with name '${name}' already exists`);
83
+ }
84
+ const status = {
85
+ push: createIdleHealth(),
86
+ pull: createIdleHealth(),
87
+ };
88
+ const remoteRecord = {
89
+ name,
90
+ collectionId,
91
+ channelConfig,
92
+ filter,
93
+ options,
94
+ status,
95
+ };
96
+ await this.remoteStorage.upsert(remoteRecord);
97
+ const channel = this.channelFactory.instance(channelConfig, this.cursorStorage);
98
+ const remote = {
99
+ name,
100
+ collectionId,
101
+ filter,
102
+ options,
103
+ channel,
104
+ };
105
+ this.remotes.set(name, remote);
106
+ this.wireChannelCallbacks(remote);
107
+ return remote;
108
+ }
109
+ async remove(name) {
110
+ const remote = this.remotes.get(name);
111
+ if (!remote) {
112
+ throw new Error(`Remote with name '${name}' does not exist`);
113
+ }
114
+ await this.remoteStorage.remove(name);
115
+ remote.channel.shutdown();
116
+ this.remotes.delete(name);
117
+ }
118
+ list() {
119
+ return Array.from(this.remotes.values());
120
+ }
121
+ wireChannelCallbacks(remote) {
122
+ remote.channel.inbox.onAdded((syncOp) => {
123
+ this.handleInboxJob(remote, syncOp);
124
+ });
125
+ remote.channel.outbox.onAdded((syncOp) => {
126
+ this.handleOutboxJob(remote, syncOp);
127
+ });
128
+ }
129
+ handleOperationWritten(event) {
130
+ if (this.isShutdown) {
131
+ return;
132
+ }
133
+ for (const remote of this.remotes.values()) {
134
+ const filteredOps = filterOperations(event.operations, remote.filter);
135
+ if (filteredOps.length === 0) {
136
+ continue;
137
+ }
138
+ const syncOp = new SyncOperation(crypto.randomUUID(), remote.name, filteredOps[0].context.documentId, [...new Set(filteredOps.map((op) => op.context.scope))], filteredOps[0].context.branch, filteredOps);
139
+ remote.channel.outbox.add(syncOp);
140
+ }
141
+ }
142
+ handleInboxJob(remote, syncOp) {
143
+ if (this.isShutdown) {
144
+ return;
145
+ }
146
+ void this.applyInboxJob(remote, syncOp);
147
+ }
148
+ handleOutboxJob(remote, syncOp) {
149
+ syncOp.on((syncOp, _prev, next) => {
150
+ if (next === SyncOperationStatus.Applied) {
151
+ remote.channel.outbox.remove(syncOp);
152
+ }
153
+ else if (next === SyncOperationStatus.Error) {
154
+ remote.channel.outbox.remove(syncOp);
155
+ }
156
+ });
157
+ }
158
+ async applyInboxJob(remote, syncOp) {
159
+ const operations = syncOp.operations.map((op) => op.operation);
160
+ let jobInfo;
161
+ try {
162
+ jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations);
163
+ }
164
+ catch (error) {
165
+ const err = error instanceof Error ? error : new Error(String(error));
166
+ const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
167
+ syncOp.failed(channelError);
168
+ remote.channel.deadLetter.add(syncOp);
169
+ remote.channel.inbox.remove(syncOp);
170
+ return;
171
+ }
172
+ let completedJobInfo;
173
+ try {
174
+ completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
175
+ }
176
+ catch (error) {
177
+ const err = error instanceof Error ? error : new Error(String(error));
178
+ const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
179
+ syncOp.failed(channelError);
180
+ remote.channel.deadLetter.add(syncOp);
181
+ remote.channel.inbox.remove(syncOp);
182
+ return;
183
+ }
184
+ const jobKey = `${syncOp.documentId}:${syncOp.branch}`;
185
+ this.loadJobs.set(jobKey, completedJobInfo);
186
+ if (completedJobInfo.status === JobStatus.FAILED) {
187
+ const error = new ChannelError(ChannelErrorSource.Inbox, new Error(`Failed to apply operations: ${completedJobInfo.error?.message || "Unknown error"}`));
188
+ syncOp.failed(error);
189
+ remote.channel.deadLetter.add(syncOp);
190
+ }
191
+ else {
192
+ syncOp.executed();
193
+ }
194
+ remote.channel.inbox.remove(syncOp);
195
+ }
196
+ }
197
+ //# sourceMappingURL=sync-manager.js.map