@livestore/sync-electric 0.2.0 → 0.3.0-dev.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +40 -25
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Effect, Schema } from '@livestore/utils/effect';
|
|
|
4
4
|
export declare const syncBackend: any;
|
|
5
5
|
export declare const ApiPushEventPayload: Schema.TaggedStruct<"sync-electric.PushEvent", {
|
|
6
6
|
roomId: typeof Schema.String;
|
|
7
|
-
|
|
7
|
+
batch: Schema.Array$<Schema.SchemaClass<{
|
|
8
8
|
readonly id: {
|
|
9
9
|
readonly global: number;
|
|
10
10
|
readonly local: number;
|
|
@@ -26,15 +26,14 @@ export declare const ApiPushEventPayload: Schema.TaggedStruct<"sync-electric.Pus
|
|
|
26
26
|
readonly global: number;
|
|
27
27
|
readonly local: number;
|
|
28
28
|
};
|
|
29
|
-
}, never
|
|
30
|
-
persisted: typeof Schema.Boolean;
|
|
29
|
+
}, never>>;
|
|
31
30
|
}>;
|
|
32
31
|
export declare const ApiInitRoomPayload: Schema.TaggedStruct<"sync-electric.InitRoom", {
|
|
33
32
|
roomId: typeof Schema.String;
|
|
34
33
|
}>;
|
|
35
34
|
export declare const ApiPayload: Schema.Union<[Schema.TaggedStruct<"sync-electric.PushEvent", {
|
|
36
35
|
roomId: typeof Schema.String;
|
|
37
|
-
|
|
36
|
+
batch: Schema.Array$<Schema.SchemaClass<{
|
|
38
37
|
readonly id: {
|
|
39
38
|
readonly global: number;
|
|
40
39
|
readonly local: number;
|
|
@@ -56,8 +55,7 @@ export declare const ApiPayload: Schema.Union<[Schema.TaggedStruct<"sync-electri
|
|
|
56
55
|
readonly global: number;
|
|
57
56
|
readonly local: number;
|
|
58
57
|
};
|
|
59
|
-
}, never
|
|
60
|
-
persisted: typeof Schema.Boolean;
|
|
58
|
+
}, never>>;
|
|
61
59
|
}>, Schema.TaggedStruct<"sync-electric.InitRoom", {
|
|
62
60
|
roomId: typeof Schema.String;
|
|
63
61
|
}>]>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAI5E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAGL,MAAM,EAKN,MAAM,EAGP,MAAM,yBAAyB,CAAA;AA4BhC,eAAO,MAAM,WAAW,EAAS,GAAG,CAAA;AAEpC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;EAG9B,CAAA;AAEF,eAAO,MAAM,kBAAkB;;EAE7B,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAwD,CAAA;AAE/E,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,kBAAkB,WAAW,QAAQ,aAAY,CAAA;AAErG,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IAChE,IAAI,EAAE,UAAU,CAAA;IAChB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,WAAW,EAAE,kBAAkB,CAAA;CAChC;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,eAAgB,SAAQ,uBAAuB;KAAG;CAC7D;AAED,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,MAAM,CAAA;IAEd,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,eAAO,MAAM,eAAe,iDAIzB,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAgI/E,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InvalidPullError, InvalidPushError } from '@livestore/common';
|
|
2
|
-
import {
|
|
2
|
+
import { MutationEvent } from '@livestore/common/schema';
|
|
3
3
|
import { Chunk, Deferred, Effect, HttpClient, HttpClientRequest, HttpClientResponse, Option, Schema, Stream, SubscriptionRef, } from '@livestore/utils/effect';
|
|
4
4
|
/*
|
|
5
5
|
Example data:
|
|
@@ -14,7 +14,7 @@ Also see: https://github.com/electric-sql/electric/blob/main/packages/typescript
|
|
|
14
14
|
const ResponseItem = Schema.Struct({
|
|
15
15
|
/** Postgres path (e.g. "public.events/1") */
|
|
16
16
|
key: Schema.optional(Schema.String),
|
|
17
|
-
value: Schema.optional(
|
|
17
|
+
value: Schema.optional(MutationEvent.EncodedAny),
|
|
18
18
|
headers: Schema.Record({ key: Schema.String, value: Schema.Any }),
|
|
19
19
|
offset: Schema.optional(Schema.String),
|
|
20
20
|
});
|
|
@@ -27,8 +27,7 @@ const ResponseHeaders = Schema.Struct({
|
|
|
27
27
|
export const syncBackend = {};
|
|
28
28
|
export const ApiPushEventPayload = Schema.TaggedStruct('sync-electric.PushEvent', {
|
|
29
29
|
roomId: Schema.String,
|
|
30
|
-
|
|
31
|
-
persisted: Schema.Boolean,
|
|
30
|
+
batch: Schema.Array(MutationEvent.EncodedAny),
|
|
32
31
|
});
|
|
33
32
|
export const ApiInitRoomPayload = Schema.TaggedStruct('sync-electric.InitRoom', {
|
|
34
33
|
roomId: Schema.String,
|
|
@@ -39,12 +38,12 @@ export const makeSyncBackend = ({ electricHost, roomId, pushEventEndpoint, }) =>
|
|
|
39
38
|
const endpointUrl = `${electricHost}/v1/shape/events_${roomId}`;
|
|
40
39
|
const isConnected = yield* SubscriptionRef.make(true);
|
|
41
40
|
const initRoom = HttpClientRequest.schemaBodyJson(ApiInitRoomPayload)(HttpClientRequest.post(pushEventEndpoint), ApiInitRoomPayload.make({ roomId })).pipe(Effect.andThen(HttpClient.execute));
|
|
41
|
+
// TODO check whether we still need this
|
|
42
42
|
const pendingPushDeferredMap = new Map();
|
|
43
|
-
const pull = (args
|
|
44
|
-
const liveParam = listenForNew ? '&live=true' : '';
|
|
43
|
+
const pull = (args) => Effect.gen(function* () {
|
|
45
44
|
const url = args._tag === 'None'
|
|
46
45
|
? `${endpointUrl}?offset=-1`
|
|
47
|
-
: `${endpointUrl}?offset=${args.value.offset}&shape_id=${args.value.shapeId}
|
|
46
|
+
: `${endpointUrl}?offset=${args.value.offset}&shape_id=${args.value.shapeId}&live=true`;
|
|
48
47
|
const resp = yield* HttpClient.get(url).pipe(Effect.tapErrorTag('ResponseError', (error) =>
|
|
49
48
|
// TODO handle 409 error when the shapeId you request no longer exists for whatever reason.
|
|
50
49
|
// The correct behavior here is to refetch the shape from scratch and to reset the local state.
|
|
@@ -70,11 +69,12 @@ export const makeSyncBackend = ({ electricHost, roomId, pushEventEndpoint, }) =>
|
|
|
70
69
|
id: item.value.id,
|
|
71
70
|
parentId: item.value.parentId,
|
|
72
71
|
},
|
|
73
|
-
persisted: true,
|
|
74
72
|
}));
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
// // TODO implement proper `remaining` handling
|
|
74
|
+
// remaining: 0,
|
|
75
|
+
// if (listenForNew === false && items.length === 0) {
|
|
76
|
+
// return Option.none()
|
|
77
|
+
// }
|
|
78
78
|
const [newItems, pendingPushItems] = Chunk.fromIterable(items).pipe(Chunk.partition((item) => pendingPushDeferredMap.has(eventIdToString(item.mutationEventEncoded.id))));
|
|
79
79
|
for (const item of pendingPushItems) {
|
|
80
80
|
const deferred = pendingPushDeferredMap.get(eventIdToString(item.mutationEventEncoded.id));
|
|
@@ -83,17 +83,23 @@ export const makeSyncBackend = ({ electricHost, roomId, pushEventEndpoint, }) =>
|
|
|
83
83
|
return Option.some([newItems, Option.some(nextCursor)]);
|
|
84
84
|
}).pipe(Effect.scoped, Effect.mapError((cause) => InvalidPullError.make({ message: cause.toString() })));
|
|
85
85
|
return {
|
|
86
|
-
pull: (args
|
|
87
|
-
push: (
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
pull: (args) => Stream.unfoldChunkEffect(args.pipe(Option.map((_) => _.metadata), Option.flatten), (metadataOption) => pull(metadataOption)).pipe(Stream.chunks, Stream.map((chunk) => ({ batch: [...chunk], remaining: 0 }))),
|
|
87
|
+
push: (batch) => Effect.gen(function* () {
|
|
88
|
+
const deferreds = [];
|
|
89
|
+
for (const mutationEventEncoded of batch) {
|
|
90
|
+
const deferred = yield* Deferred.make();
|
|
91
|
+
pendingPushDeferredMap.set(eventIdToString(mutationEventEncoded.id), deferred);
|
|
92
|
+
deferreds.push(deferred);
|
|
93
|
+
}
|
|
94
|
+
const resp = yield* HttpClientRequest.schemaBodyJson(ApiPushEventPayload)(HttpClientRequest.post(pushEventEndpoint), ApiPushEventPayload.make({ roomId, batch })).pipe(Effect.andThen(HttpClient.execute), Effect.andThen(HttpClientResponse.schemaBodyJson(Schema.Struct({ success: Schema.Boolean }))), Effect.scoped, Effect.mapError((cause) => InvalidPushError.make({ reason: { _tag: 'Unexpected', message: cause.toString() } })));
|
|
91
95
|
if (!resp.success) {
|
|
92
|
-
yield* InvalidPushError.make({ message: 'Push failed' });
|
|
96
|
+
yield* InvalidPushError.make({ reason: { _tag: 'Unexpected', message: 'Push failed' } });
|
|
97
|
+
}
|
|
98
|
+
const metadata = yield* Effect.all(deferreds, { concurrency: 'unbounded' }).pipe(Effect.map((_) => _.map(Option.some)));
|
|
99
|
+
for (const mutationEventEncoded of batch) {
|
|
100
|
+
pendingPushDeferredMap.delete(eventIdToString(mutationEventEncoded.id));
|
|
93
101
|
}
|
|
94
|
-
|
|
95
|
-
pendingPushDeferredMap.delete(eventIdToString(mutationEventEncoded.id));
|
|
96
|
-
return { metadata: Option.some(metadata) };
|
|
102
|
+
return { metadata };
|
|
97
103
|
}),
|
|
98
104
|
isConnected,
|
|
99
105
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestore/sync-electric",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-dev.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@livestore/common": "0.
|
|
13
|
-
"@livestore/utils": "0.
|
|
12
|
+
"@livestore/common": "0.3.0-dev.1",
|
|
13
|
+
"@livestore/utils": "0.3.0-dev.1"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {},
|
|
16
16
|
"publishConfig": {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SyncBackend, SyncBackendOptionsBase } from '@livestore/common'
|
|
2
2
|
import { InvalidPullError, InvalidPushError } from '@livestore/common'
|
|
3
|
-
import {
|
|
3
|
+
import type { EventId } from '@livestore/common/schema'
|
|
4
|
+
import { MutationEvent } from '@livestore/common/schema'
|
|
4
5
|
import type { Scope } from '@livestore/utils/effect'
|
|
5
6
|
import {
|
|
6
7
|
Chunk,
|
|
@@ -29,7 +30,7 @@ Also see: https://github.com/electric-sql/electric/blob/main/packages/typescript
|
|
|
29
30
|
const ResponseItem = Schema.Struct({
|
|
30
31
|
/** Postgres path (e.g. "public.events/1") */
|
|
31
32
|
key: Schema.optional(Schema.String),
|
|
32
|
-
value: Schema.optional(
|
|
33
|
+
value: Schema.optional(MutationEvent.EncodedAny),
|
|
33
34
|
headers: Schema.Record({ key: Schema.String, value: Schema.Any }),
|
|
34
35
|
offset: Schema.optional(Schema.String),
|
|
35
36
|
})
|
|
@@ -45,8 +46,7 @@ export const syncBackend = {} as any
|
|
|
45
46
|
|
|
46
47
|
export const ApiPushEventPayload = Schema.TaggedStruct('sync-electric.PushEvent', {
|
|
47
48
|
roomId: Schema.String,
|
|
48
|
-
|
|
49
|
-
persisted: Schema.Boolean,
|
|
49
|
+
batch: Schema.Array(MutationEvent.EncodedAny),
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
export const ApiInitRoomPayload = Schema.TaggedStruct('sync-electric.InitRoom', {
|
|
@@ -85,7 +85,7 @@ declare global {
|
|
|
85
85
|
|
|
86
86
|
type SyncMetadata = {
|
|
87
87
|
offset: string
|
|
88
|
-
// TODO move this into "global" sync metadata as it's the same for each event
|
|
88
|
+
// TODO move this into some kind of "global" sync metadata as it's the same for each event
|
|
89
89
|
shapeId: string
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -104,15 +104,15 @@ export const makeSyncBackend = ({
|
|
|
104
104
|
ApiInitRoomPayload.make({ roomId }),
|
|
105
105
|
).pipe(Effect.andThen(HttpClient.execute))
|
|
106
106
|
|
|
107
|
+
// TODO check whether we still need this
|
|
107
108
|
const pendingPushDeferredMap = new Map<string, Deferred.Deferred<SyncMetadata>>()
|
|
108
109
|
|
|
109
|
-
const pull = (args: Option.Option<SyncMetadata
|
|
110
|
+
const pull = (args: Option.Option<SyncMetadata>) =>
|
|
110
111
|
Effect.gen(function* () {
|
|
111
|
-
const liveParam = listenForNew ? '&live=true' : ''
|
|
112
112
|
const url =
|
|
113
113
|
args._tag === 'None'
|
|
114
114
|
? `${endpointUrl}?offset=-1`
|
|
115
|
-
: `${endpointUrl}?offset=${args.value.offset}&shape_id=${args.value.shapeId}
|
|
115
|
+
: `${endpointUrl}?offset=${args.value.offset}&shape_id=${args.value.shapeId}&live=true`
|
|
116
116
|
|
|
117
117
|
const resp = yield* HttpClient.get(url).pipe(
|
|
118
118
|
Effect.tapErrorTag('ResponseError', (error) =>
|
|
@@ -147,12 +147,14 @@ export const makeSyncBackend = ({
|
|
|
147
147
|
id: item.value!.id,
|
|
148
148
|
parentId: item.value!.parentId,
|
|
149
149
|
},
|
|
150
|
-
persisted: true,
|
|
151
150
|
}))
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
// // TODO implement proper `remaining` handling
|
|
153
|
+
// remaining: 0,
|
|
154
|
+
|
|
155
|
+
// if (listenForNew === false && items.length === 0) {
|
|
156
|
+
// return Option.none()
|
|
157
|
+
// }
|
|
156
158
|
|
|
157
159
|
const [newItems, pendingPushItems] = Chunk.fromIterable(items).pipe(
|
|
158
160
|
Chunk.partition((item) => pendingPushDeferredMap.has(eventIdToString(item.mutationEventEncoded.id))),
|
|
@@ -170,42 +172,55 @@ export const makeSyncBackend = ({
|
|
|
170
172
|
)
|
|
171
173
|
|
|
172
174
|
return {
|
|
173
|
-
pull: (args
|
|
175
|
+
pull: (args) =>
|
|
174
176
|
Stream.unfoldChunkEffect(
|
|
175
177
|
args.pipe(
|
|
176
178
|
Option.map((_) => _.metadata),
|
|
177
179
|
Option.flatten,
|
|
178
180
|
),
|
|
179
|
-
(metadataOption) => pull(metadataOption
|
|
181
|
+
(metadataOption) => pull(metadataOption),
|
|
182
|
+
).pipe(
|
|
183
|
+
Stream.chunks,
|
|
184
|
+
Stream.map((chunk) => ({ batch: [...chunk], remaining: 0 })),
|
|
180
185
|
),
|
|
181
186
|
|
|
182
|
-
push: (
|
|
187
|
+
push: (batch) =>
|
|
183
188
|
Effect.gen(function* () {
|
|
184
|
-
const
|
|
185
|
-
|
|
189
|
+
const deferreds: Deferred.Deferred<SyncMetadata>[] = []
|
|
190
|
+
for (const mutationEventEncoded of batch) {
|
|
191
|
+
const deferred = yield* Deferred.make<SyncMetadata>()
|
|
192
|
+
pendingPushDeferredMap.set(eventIdToString(mutationEventEncoded.id), deferred)
|
|
193
|
+
deferreds.push(deferred)
|
|
194
|
+
}
|
|
186
195
|
|
|
187
196
|
const resp = yield* HttpClientRequest.schemaBodyJson(ApiPushEventPayload)(
|
|
188
197
|
HttpClientRequest.post(pushEventEndpoint),
|
|
189
|
-
ApiPushEventPayload.make({ roomId,
|
|
198
|
+
ApiPushEventPayload.make({ roomId, batch }),
|
|
190
199
|
).pipe(
|
|
191
200
|
Effect.andThen(HttpClient.execute),
|
|
192
201
|
Effect.andThen(HttpClientResponse.schemaBodyJson(Schema.Struct({ success: Schema.Boolean }))),
|
|
193
202
|
Effect.scoped,
|
|
194
|
-
Effect.mapError((cause) =>
|
|
203
|
+
Effect.mapError((cause) =>
|
|
204
|
+
InvalidPushError.make({ reason: { _tag: 'Unexpected', message: cause.toString() } }),
|
|
205
|
+
),
|
|
195
206
|
)
|
|
196
207
|
|
|
197
208
|
if (!resp.success) {
|
|
198
|
-
yield* InvalidPushError.make({ message: 'Push failed' })
|
|
209
|
+
yield* InvalidPushError.make({ reason: { _tag: 'Unexpected', message: 'Push failed' } })
|
|
199
210
|
}
|
|
200
211
|
|
|
201
|
-
const metadata = yield*
|
|
212
|
+
const metadata = yield* Effect.all(deferreds, { concurrency: 'unbounded' }).pipe(
|
|
213
|
+
Effect.map((_) => _.map(Option.some)),
|
|
214
|
+
)
|
|
202
215
|
|
|
203
|
-
|
|
216
|
+
for (const mutationEventEncoded of batch) {
|
|
217
|
+
pendingPushDeferredMap.delete(eventIdToString(mutationEventEncoded.id))
|
|
218
|
+
}
|
|
204
219
|
|
|
205
|
-
return { metadata
|
|
220
|
+
return { metadata }
|
|
206
221
|
}),
|
|
207
222
|
isConnected,
|
|
208
223
|
} satisfies SyncBackend<SyncMetadata>
|
|
209
224
|
})
|
|
210
225
|
|
|
211
|
-
const eventIdToString = (eventId: EventId) => `${eventId.global}_${eventId.local}`
|
|
226
|
+
const eventIdToString = (eventId: EventId.EventId) => `${eventId.global}_${eventId.local}`
|