@livestore/sync-electric 0.3.0-dev.12 → 0.3.0-dev.14
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/api-schema.d.ts +35 -0
- package/dist/api-schema.d.ts.map +1 -0
- package/dist/api-schema.js +15 -0
- package/dist/api-schema.js.map +1 -0
- package/dist/index.d.ts +28 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -47
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api-schema.ts +19 -0
- package/src/index.ts +167 -77
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Schema } from '@livestore/utils/effect';
|
|
2
|
+
export declare const PushPayload: Schema.TaggedStruct<"@livestore/sync-electric.Push", {
|
|
3
|
+
storeId: typeof Schema.String;
|
|
4
|
+
batch: Schema.Array$<Schema.Struct<{
|
|
5
|
+
mutation: typeof Schema.String;
|
|
6
|
+
args: typeof Schema.Any;
|
|
7
|
+
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
8
|
+
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
9
|
+
clientId: typeof Schema.String;
|
|
10
|
+
}>>;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const PullPayload: Schema.TaggedStruct<"@livestore/sync-electric.Pull", {
|
|
13
|
+
storeId: typeof Schema.String;
|
|
14
|
+
handle: Schema.Option<Schema.Struct<{
|
|
15
|
+
offset: typeof Schema.String;
|
|
16
|
+
handle: typeof Schema.String;
|
|
17
|
+
}>>;
|
|
18
|
+
}>;
|
|
19
|
+
export declare const ApiPayload: Schema.Union<[Schema.TaggedStruct<"@livestore/sync-electric.Pull", {
|
|
20
|
+
storeId: typeof Schema.String;
|
|
21
|
+
handle: Schema.Option<Schema.Struct<{
|
|
22
|
+
offset: typeof Schema.String;
|
|
23
|
+
handle: typeof Schema.String;
|
|
24
|
+
}>>;
|
|
25
|
+
}>, Schema.TaggedStruct<"@livestore/sync-electric.Push", {
|
|
26
|
+
storeId: typeof Schema.String;
|
|
27
|
+
batch: Schema.Array$<Schema.Struct<{
|
|
28
|
+
mutation: typeof Schema.String;
|
|
29
|
+
args: typeof Schema.Any;
|
|
30
|
+
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
31
|
+
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
32
|
+
clientId: typeof Schema.String;
|
|
33
|
+
}>>;
|
|
34
|
+
}>]>;
|
|
35
|
+
//# sourceMappingURL=api-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-schema.d.ts","sourceRoot":"","sources":["../src/api-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,eAAO,MAAM,WAAW;;;yBAgB2lE,OAAQ,MAAM;qBAAkB,OAAQ,GAAG;YAAS,OAAQ,WAAW,iBAAiB,cAAc;kBAAuD,OAAQ,WAAW,iBAAiB,cAAc;yBAA8D,OAAQ,MAAM;;EAb54E,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;EAQtB,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;;;;yBAC4lE,OAAQ,MAAM;qBAAkB,OAAQ,GAAG;YAAS,OAAQ,WAAW,iBAAiB,cAAc;kBAAuD,OAAQ,WAAW,iBAAiB,cAAc;yBAA8D,OAAQ,MAAM;;IAD90E,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MutationEvent } from '@livestore/common/schema';
|
|
2
|
+
import { Schema } from '@livestore/utils/effect';
|
|
3
|
+
export const PushPayload = Schema.TaggedStruct('@livestore/sync-electric.Push', {
|
|
4
|
+
storeId: Schema.String,
|
|
5
|
+
batch: Schema.Array(MutationEvent.AnyEncodedGlobal),
|
|
6
|
+
});
|
|
7
|
+
export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull', {
|
|
8
|
+
storeId: Schema.String,
|
|
9
|
+
handle: Schema.Option(Schema.Struct({
|
|
10
|
+
offset: Schema.String,
|
|
11
|
+
handle: Schema.String,
|
|
12
|
+
})),
|
|
13
|
+
});
|
|
14
|
+
export const ApiPayload = Schema.Union(PullPayload, PushPayload);
|
|
15
|
+
//# sourceMappingURL=api-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-schema.js","sourceRoot":"","sources":["../src/api-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,+BAA+B,EAAE;IAC9E,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC;CACpD,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,+BAA+B,EAAE;IAC9E,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CACnB,MAAM,CAAC,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CACH;CACF,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,51 +1,43 @@
|
|
|
1
1
|
import type { SyncBackend } from '@livestore/common';
|
|
2
2
|
import type { Scope } from '@livestore/utils/effect';
|
|
3
3
|
import { Effect, Schema } from '@livestore/utils/effect';
|
|
4
|
+
export * as ApiSchema from './api-schema.js';
|
|
4
5
|
export declare const syncBackend: any;
|
|
5
|
-
export declare const ApiPushEventPayload: Schema.TaggedStruct<"sync-electric.PushEvent", {
|
|
6
|
-
storeId: typeof Schema.String;
|
|
7
|
-
batch: Schema.Array$<Schema.Struct<{
|
|
8
|
-
mutation: typeof Schema.String;
|
|
9
|
-
args: typeof Schema.Any;
|
|
10
|
-
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
11
|
-
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
12
|
-
}>>;
|
|
13
|
-
}>;
|
|
14
|
-
export declare const ApiInitRoomPayload: Schema.TaggedStruct<"sync-electric.InitRoom", {
|
|
15
|
-
storeId: typeof Schema.String;
|
|
16
|
-
}>;
|
|
17
|
-
export declare const ApiPayload: Schema.Union<[Schema.TaggedStruct<"sync-electric.PushEvent", {
|
|
18
|
-
storeId: typeof Schema.String;
|
|
19
|
-
batch: Schema.Array$<Schema.Struct<{
|
|
20
|
-
mutation: typeof Schema.String;
|
|
21
|
-
args: typeof Schema.Any;
|
|
22
|
-
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
23
|
-
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
24
|
-
}>>;
|
|
25
|
-
}>, Schema.TaggedStruct<"sync-electric.InitRoom", {
|
|
26
|
-
storeId: typeof Schema.String;
|
|
27
|
-
}>]>;
|
|
28
6
|
export declare const syncBackendOptions: <TOptions extends SyncBackendOptions>(options: TOptions) => TOptions;
|
|
7
|
+
export declare const makeElectricUrl: (electricHost: string, searchParams: URLSearchParams) => {
|
|
8
|
+
url: string;
|
|
9
|
+
storeId: string;
|
|
10
|
+
needsInit: boolean;
|
|
11
|
+
};
|
|
29
12
|
export interface SyncBackendOptions {
|
|
30
|
-
/**
|
|
31
|
-
* The host of the Electric server
|
|
32
|
-
*
|
|
33
|
-
* @example "https://localhost:3000"
|
|
34
|
-
*/
|
|
35
|
-
electricHost: string;
|
|
36
13
|
storeId: string;
|
|
37
14
|
/**
|
|
38
|
-
* The
|
|
15
|
+
* The endpoint to pull/push events. Pull is a `GET` request, push is a `POST` request.
|
|
16
|
+
* Usually this endpoint is part of your API layer to proxy requests to the Electric server
|
|
17
|
+
* e.g. to implement auth, rate limiting, etc.
|
|
39
18
|
*
|
|
40
|
-
* @example "/api/
|
|
41
|
-
* @example "
|
|
19
|
+
* @example "/api/electric"
|
|
20
|
+
* @example { push: "/api/push-event", pull: "/api/pull-event" }
|
|
42
21
|
*/
|
|
43
|
-
|
|
22
|
+
endpoint: string | {
|
|
23
|
+
push: string;
|
|
24
|
+
pull: string;
|
|
25
|
+
};
|
|
44
26
|
}
|
|
27
|
+
export declare const SyncMetadata: Schema.Struct<{
|
|
28
|
+
offset: typeof Schema.String;
|
|
29
|
+
handle: typeof Schema.String;
|
|
30
|
+
}>;
|
|
45
31
|
type SyncMetadata = {
|
|
46
32
|
offset: string;
|
|
47
|
-
|
|
33
|
+
handle: string;
|
|
48
34
|
};
|
|
49
|
-
export declare const makeSyncBackend: ({
|
|
50
|
-
|
|
35
|
+
export declare const makeSyncBackend: ({ storeId, endpoint, }: SyncBackendOptions) => Effect.Effect<SyncBackend<SyncMetadata>, never, Scope.Scope>;
|
|
36
|
+
/**
|
|
37
|
+
* Needs to be bumped when the storage format changes (e.g. mutationLogTable schema changes)
|
|
38
|
+
*
|
|
39
|
+
* Changing this version number will lead to a "soft reset".
|
|
40
|
+
*/
|
|
41
|
+
export declare const PERSISTENCE_FORMAT_VERSION = 3;
|
|
42
|
+
export declare const toTableName: (storeId: string) => string;
|
|
51
43
|
//# sourceMappingURL=index.d.ts.map
|
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,EAAkB,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAKpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAGL,MAAM,EAKN,MAAM,EAGP,MAAM,yBAAyB,CAAA;AAIhC,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAA;AA8E5C,eAAO,MAAM,WAAW,EAAS,GAAG,CAAA;AAEpC,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,kBAAkB,WAAW,QAAQ,aAAY,CAAA;AAErG,eAAO,MAAM,eAAe,iBAAkB,MAAM,gBAAgB,eAAe;;;;CAkBlF,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,QAAQ,EACJ,MAAM,GACN;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACN;AAED,eAAO,MAAM,YAAY;;;EAIvB,CAAA;AAEF,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,MAAM,CAAA;IAEd,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,eAAe,2BAGzB,kBAAkB,KAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAwI/E,CAAA;AAEJ;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,eAAO,MAAM,WAAW,YAAa,MAAM,WAG1C,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,86 +1,150 @@
|
|
|
1
1
|
import { InvalidPullError, InvalidPushError } from '@livestore/common';
|
|
2
2
|
import { MutationEvent } from '@livestore/common/schema';
|
|
3
|
+
import { notYetImplemented, shouldNeverHappen } from '@livestore/utils';
|
|
3
4
|
import { Chunk, Deferred, Effect, HttpClient, HttpClientRequest, HttpClientResponse, Option, Schema, Stream, SubscriptionRef, } from '@livestore/utils/effect';
|
|
5
|
+
import * as ApiSchema from './api-schema.js';
|
|
6
|
+
export * as ApiSchema from './api-schema.js';
|
|
4
7
|
/*
|
|
5
8
|
Example data:
|
|
6
9
|
|
|
7
|
-
[
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
[
|
|
11
|
+
{
|
|
12
|
+
"value": {
|
|
13
|
+
"args": "{\"id\": \"127c3df4-0855-4587-ae75-14463f4a3aa0\", \"text\": \"1\"}",
|
|
14
|
+
"clientId": "S_YOa",
|
|
15
|
+
"id": "0",
|
|
16
|
+
"mutation": "todoCreated",
|
|
17
|
+
"parentId": "-1"
|
|
18
|
+
},
|
|
19
|
+
"key": "\"public\".\"events_9069baf0_b3e6_42f7_980f_188416eab3fx3\"/\"0\"",
|
|
20
|
+
"headers": {
|
|
21
|
+
"last": true,
|
|
22
|
+
"relation": [
|
|
23
|
+
"public",
|
|
24
|
+
"events_9069baf0_b3e6_42f7_980f_188416eab3fx3"
|
|
25
|
+
],
|
|
26
|
+
"operation": "insert",
|
|
27
|
+
"lsn": 27294160,
|
|
28
|
+
"op_position": 0,
|
|
29
|
+
"txids": [
|
|
30
|
+
753
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"headers": {
|
|
36
|
+
"control": "up-to-date",
|
|
37
|
+
"global_last_seen_lsn": 27294160
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
|
|
10
42
|
|
|
11
43
|
Also see: https://github.com/electric-sql/electric/blob/main/packages/typescript-client/src/client.ts
|
|
12
44
|
|
|
13
45
|
*/
|
|
46
|
+
const MutationEventGlobalFromStringRecord = Schema.Struct({
|
|
47
|
+
id: Schema.NumberFromString,
|
|
48
|
+
parentId: Schema.NumberFromString,
|
|
49
|
+
mutation: Schema.String,
|
|
50
|
+
args: Schema.parseJson(Schema.Any),
|
|
51
|
+
clientId: Schema.String,
|
|
52
|
+
sessionId: Schema.optional(Schema.String),
|
|
53
|
+
}).pipe(Schema.transform(MutationEvent.AnyEncodedGlobal, {
|
|
54
|
+
decode: (_) => _,
|
|
55
|
+
encode: (_) => _,
|
|
56
|
+
}));
|
|
14
57
|
const ResponseItem = Schema.Struct({
|
|
15
|
-
/** Postgres path (e.g. "public.
|
|
58
|
+
/** Postgres path (e.g. `"public"."events_9069baf0_b3e6_42f7_980f_188416eab3fx3"/"0"`) */
|
|
16
59
|
key: Schema.optional(Schema.String),
|
|
17
|
-
value: Schema.optional(
|
|
18
|
-
headers: Schema.
|
|
19
|
-
|
|
60
|
+
value: Schema.optional(MutationEventGlobalFromStringRecord),
|
|
61
|
+
headers: Schema.Union(Schema.Struct({
|
|
62
|
+
operation: Schema.Union(Schema.Literal('insert'), Schema.Literal('update'), Schema.Literal('delete')),
|
|
63
|
+
relation: Schema.Array(Schema.String),
|
|
64
|
+
}), Schema.Struct({
|
|
65
|
+
control: Schema.String,
|
|
66
|
+
})),
|
|
20
67
|
});
|
|
21
68
|
const ResponseHeaders = Schema.Struct({
|
|
22
|
-
'
|
|
23
|
-
// '
|
|
69
|
+
'electric-handle': Schema.String,
|
|
70
|
+
// 'electric-schema': Schema.parseJson(Schema.Any),
|
|
24
71
|
/** e.g. 26799576_0 */
|
|
25
|
-
'
|
|
72
|
+
'electric-offset': Schema.String,
|
|
26
73
|
});
|
|
27
74
|
export const syncBackend = {};
|
|
28
|
-
export const ApiPushEventPayload = Schema.TaggedStruct('sync-electric.PushEvent', {
|
|
29
|
-
storeId: Schema.String,
|
|
30
|
-
batch: Schema.Array(MutationEvent.AnyEncodedGlobal),
|
|
31
|
-
});
|
|
32
|
-
export const ApiInitRoomPayload = Schema.TaggedStruct('sync-electric.InitRoom', {
|
|
33
|
-
storeId: Schema.String,
|
|
34
|
-
});
|
|
35
|
-
export const ApiPayload = Schema.Union(ApiPushEventPayload, ApiInitRoomPayload);
|
|
36
75
|
export const syncBackendOptions = (options) => options;
|
|
37
|
-
export const
|
|
38
|
-
const endpointUrl = `${electricHost}/v1/shape
|
|
76
|
+
export const makeElectricUrl = (electricHost, searchParams) => {
|
|
77
|
+
const endpointUrl = `${electricHost}/v1/shape`;
|
|
78
|
+
const argsResult = Schema.decodeUnknownEither(Schema.Struct({ args: Schema.parseJson(ApiSchema.PullPayload) }))(Object.fromEntries(searchParams.entries()));
|
|
79
|
+
if (argsResult._tag === 'Left') {
|
|
80
|
+
return shouldNeverHappen('Invalid search params', searchParams);
|
|
81
|
+
}
|
|
82
|
+
const args = argsResult.right.args;
|
|
83
|
+
const tableName = toTableName(args.storeId);
|
|
84
|
+
const url = args.handle._tag === 'None'
|
|
85
|
+
? `${endpointUrl}?table=${tableName}&offset=-1`
|
|
86
|
+
: `${endpointUrl}?table=${tableName}&offset=${args.handle.value.offset}&handle=${args.handle.value.handle}&live=true`;
|
|
87
|
+
return { url, storeId: args.storeId, needsInit: args.handle._tag === 'None' };
|
|
88
|
+
};
|
|
89
|
+
export const SyncMetadata = Schema.Struct({
|
|
90
|
+
offset: Schema.String,
|
|
91
|
+
// TODO move this into some kind of "global" sync metadata as it's the same for each event
|
|
92
|
+
handle: Schema.String,
|
|
93
|
+
});
|
|
94
|
+
export const makeSyncBackend = ({ storeId, endpoint, }) => Effect.gen(function* () {
|
|
39
95
|
const isConnected = yield* SubscriptionRef.make(true);
|
|
40
|
-
const
|
|
96
|
+
const pullEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.pull;
|
|
97
|
+
const pushEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.push;
|
|
41
98
|
// TODO check whether we still need this
|
|
42
99
|
const pendingPushDeferredMap = new Map();
|
|
43
|
-
const pull = (
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const resp = yield* HttpClient.get(url).pipe(Effect.tapErrorTag('ResponseError', (error) =>
|
|
48
|
-
// TODO handle 409 error when the shapeId you request no longer exists for whatever reason.
|
|
49
|
-
// The correct behavior here is to refetch the shape from scratch and to reset the local state.
|
|
50
|
-
error.response.status === 400 ? initRoom : Effect.fail(error)), Effect.retry({ times: 1 }));
|
|
100
|
+
const pull = (handle) => Effect.gen(function* () {
|
|
101
|
+
const argsJson = yield* Schema.encode(Schema.parseJson(ApiSchema.PullPayload))(ApiSchema.PullPayload.make({ storeId, handle }));
|
|
102
|
+
const url = `${pullEndpoint}?args=${argsJson}`;
|
|
103
|
+
const resp = yield* HttpClient.get(url);
|
|
51
104
|
const headers = yield* HttpClientResponse.schemaHeaders(ResponseHeaders)(resp);
|
|
52
|
-
const
|
|
53
|
-
offset: headers['
|
|
54
|
-
|
|
105
|
+
const nextHandle = {
|
|
106
|
+
offset: headers['electric-offset'],
|
|
107
|
+
handle: headers['electric-handle'],
|
|
55
108
|
};
|
|
109
|
+
// TODO handle case where Electric shape is not found for a given handle
|
|
110
|
+
// https://electric-sql.com/openapi.html#/paths/~1v1~1shape/get
|
|
111
|
+
// {
|
|
112
|
+
// "message": "The shape associated with this shape_handle and offset was not found. Resync to fetch the latest shape",
|
|
113
|
+
// "shape_handle": "2494_84241",
|
|
114
|
+
// "offset": "-1"
|
|
115
|
+
// }
|
|
116
|
+
if (resp.status === 409) {
|
|
117
|
+
// TODO: implementation plan:
|
|
118
|
+
// start pulling events from scratch with the new handle and ignore the "old events"
|
|
119
|
+
// until we found a new event, then, continue with the new handle
|
|
120
|
+
return notYetImplemented(`Electric shape not found for handle ${nextHandle.handle}`);
|
|
121
|
+
}
|
|
56
122
|
// Electric completes the long-poll request after ~20 seconds with a 204 status
|
|
57
123
|
// In this case we just retry where we left off
|
|
58
124
|
if (resp.status === 204) {
|
|
59
|
-
return Option.some([Chunk.empty(), Option.some(
|
|
125
|
+
return Option.some([Chunk.empty(), Option.some(nextHandle)]);
|
|
60
126
|
}
|
|
61
|
-
const body = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem)
|
|
127
|
+
const body = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem), {
|
|
128
|
+
onExcessProperty: 'preserve',
|
|
129
|
+
})(resp);
|
|
62
130
|
const items = body
|
|
63
|
-
.filter((item) => item.value !== undefined)
|
|
131
|
+
.filter((item) => item.value !== undefined && item.headers.operation === 'insert')
|
|
64
132
|
.map((item) => ({
|
|
65
|
-
metadata: Option.some({ offset:
|
|
66
|
-
mutationEventEncoded:
|
|
67
|
-
mutation: item.value.mutation,
|
|
68
|
-
args: JSON.parse(item.value.args),
|
|
69
|
-
id: item.value.id,
|
|
70
|
-
parentId: item.value.parentId,
|
|
71
|
-
},
|
|
133
|
+
metadata: Option.some({ offset: nextHandle.offset, handle: nextHandle.handle }),
|
|
134
|
+
mutationEventEncoded: item.value,
|
|
72
135
|
}));
|
|
73
136
|
// // TODO implement proper `remaining` handling
|
|
74
137
|
// remaining: 0,
|
|
75
138
|
// if (listenForNew === false && items.length === 0) {
|
|
76
139
|
// return Option.none()
|
|
77
140
|
// }
|
|
78
|
-
const
|
|
79
|
-
for (const item of pendingPushItems) {
|
|
141
|
+
for (const item of items) {
|
|
80
142
|
const deferred = pendingPushDeferredMap.get(item.mutationEventEncoded.id);
|
|
81
|
-
|
|
143
|
+
if (deferred !== undefined) {
|
|
144
|
+
yield* Deferred.succeed(deferred, Option.getOrThrow(item.metadata));
|
|
145
|
+
}
|
|
82
146
|
}
|
|
83
|
-
return Option.some([
|
|
147
|
+
return Option.some([Chunk.fromIterable(items), Option.some(nextHandle)]);
|
|
84
148
|
}).pipe(Effect.scoped, Effect.mapError((cause) => InvalidPullError.make({ message: cause.toString() })));
|
|
85
149
|
return {
|
|
86
150
|
pull: (args) => Stream.unfoldChunkEffect(args.pipe(Option.map((_) => _.metadata), Option.flatten), (metadataOption) => pull(metadataOption)).pipe(Stream.chunks, Stream.map((chunk) => ({ batch: [...chunk], remaining: 0 }))),
|
|
@@ -91,7 +155,7 @@ export const makeSyncBackend = ({ electricHost, storeId, pushEventEndpoint, }) =
|
|
|
91
155
|
pendingPushDeferredMap.set(mutationEventEncoded.id, deferred);
|
|
92
156
|
deferreds.push(deferred);
|
|
93
157
|
}
|
|
94
|
-
const resp = yield* HttpClientRequest.schemaBodyJson(
|
|
158
|
+
const resp = yield* HttpClientRequest.schemaBodyJson(ApiSchema.PushPayload)(HttpClientRequest.post(pushEndpoint), ApiSchema.PushPayload.make({ storeId, 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() } })));
|
|
95
159
|
if (!resp.success) {
|
|
96
160
|
yield* InvalidPushError.make({ reason: { _tag: 'Unexpected', message: 'Push failed' } });
|
|
97
161
|
}
|
|
@@ -104,4 +168,14 @@ export const makeSyncBackend = ({ electricHost, storeId, pushEventEndpoint, }) =
|
|
|
104
168
|
isConnected,
|
|
105
169
|
};
|
|
106
170
|
});
|
|
171
|
+
/**
|
|
172
|
+
* Needs to be bumped when the storage format changes (e.g. mutationLogTable schema changes)
|
|
173
|
+
*
|
|
174
|
+
* Changing this version number will lead to a "soft reset".
|
|
175
|
+
*/
|
|
176
|
+
export const PERSISTENCE_FORMAT_VERSION = 3;
|
|
177
|
+
export const toTableName = (storeId) => {
|
|
178
|
+
const escapedStoreId = storeId.replaceAll(/[^a-zA-Z0-9_]/g, '_');
|
|
179
|
+
return `mutation_log_${PERSISTENCE_FORMAT_VERSION}_${escapedStoreId}`;
|
|
180
|
+
};
|
|
107
181
|
//# sourceMappingURL=index.js.map
|
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;AAEtE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,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.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@livestore/common": "0.3.0-dev.
|
|
14
|
-
"@livestore/utils": "0.3.0-dev.
|
|
13
|
+
"@livestore/common": "0.3.0-dev.14",
|
|
14
|
+
"@livestore/utils": "0.3.0-dev.14"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {},
|
|
17
17
|
"publishConfig": {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MutationEvent } from '@livestore/common/schema'
|
|
2
|
+
import { Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
4
|
+
export const PushPayload = Schema.TaggedStruct('@livestore/sync-electric.Push', {
|
|
5
|
+
storeId: Schema.String,
|
|
6
|
+
batch: Schema.Array(MutationEvent.AnyEncodedGlobal),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull', {
|
|
10
|
+
storeId: Schema.String,
|
|
11
|
+
handle: Schema.Option(
|
|
12
|
+
Schema.Struct({
|
|
13
|
+
offset: Schema.String,
|
|
14
|
+
handle: Schema.String,
|
|
15
|
+
}),
|
|
16
|
+
),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const ApiPayload = Schema.Union(PullPayload, PushPayload)
|