@livestore/sync-electric 0.3.0-dev.23 → 0.3.0-dev.24
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 +14 -4
- package/dist/api-schema.d.ts.map +1 -1
- package/dist/api-schema.js +2 -2
- package/dist/api-schema.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api-schema.ts +2 -2
- package/src/index.ts +145 -134
- package/tmp/pack.tgz +0 -0
package/dist/api-schema.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Schema } from '@livestore/utils/effect';
|
|
2
|
-
export declare const PushPayload: Schema.
|
|
2
|
+
export declare const PushPayload: Schema.Struct<{
|
|
3
|
+
_tag: Schema.tag<"@livestore/sync-electric.Push">;
|
|
4
|
+
} & {
|
|
3
5
|
storeId: typeof Schema.String;
|
|
4
6
|
batch: Schema.Array$<Schema.Struct<{
|
|
5
7
|
mutation: typeof Schema.String;
|
|
@@ -7,22 +9,29 @@ export declare const PushPayload: Schema.TaggedStruct<"@livestore/sync-electric.
|
|
|
7
9
|
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
8
10
|
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
9
11
|
clientId: typeof Schema.String;
|
|
12
|
+
sessionId: typeof Schema.String;
|
|
10
13
|
}>>;
|
|
11
14
|
}>;
|
|
12
|
-
export declare const PullPayload: Schema.
|
|
15
|
+
export declare const PullPayload: Schema.Struct<{
|
|
16
|
+
_tag: Schema.tag<"@livestore/sync-electric.Pull">;
|
|
17
|
+
} & {
|
|
13
18
|
storeId: typeof Schema.String;
|
|
14
19
|
handle: Schema.Option<Schema.Struct<{
|
|
15
20
|
offset: typeof Schema.String;
|
|
16
21
|
handle: typeof Schema.String;
|
|
17
22
|
}>>;
|
|
18
23
|
}>;
|
|
19
|
-
export declare const ApiPayload: Schema.Union<[Schema.
|
|
24
|
+
export declare const ApiPayload: Schema.Union<[Schema.Struct<{
|
|
25
|
+
_tag: Schema.tag<"@livestore/sync-electric.Pull">;
|
|
26
|
+
} & {
|
|
20
27
|
storeId: typeof Schema.String;
|
|
21
28
|
handle: Schema.Option<Schema.Struct<{
|
|
22
29
|
offset: typeof Schema.String;
|
|
23
30
|
handle: typeof Schema.String;
|
|
24
31
|
}>>;
|
|
25
|
-
}>, Schema.
|
|
32
|
+
}>, Schema.Struct<{
|
|
33
|
+
_tag: Schema.tag<"@livestore/sync-electric.Push">;
|
|
34
|
+
} & {
|
|
26
35
|
storeId: typeof Schema.String;
|
|
27
36
|
batch: Schema.Array$<Schema.Struct<{
|
|
28
37
|
mutation: typeof Schema.String;
|
|
@@ -30,6 +39,7 @@ export declare const ApiPayload: Schema.Union<[Schema.TaggedStruct<"@livestore/s
|
|
|
30
39
|
id: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
31
40
|
parentId: Schema.BrandSchema<number & import("effect/Brand").Brand<"GlobalEventId">, number, never>;
|
|
32
41
|
clientId: typeof Schema.String;
|
|
42
|
+
sessionId: typeof Schema.String;
|
|
33
43
|
}>>;
|
|
34
44
|
}>]>;
|
|
35
45
|
//# sourceMappingURL=api-schema.d.ts.map
|
package/dist/api-schema.d.ts.map
CHANGED
|
@@ -1 +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
|
|
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;;;;;yBAgB65D,OAAQ,MAAM;qBAAkB,OAAQ,GAAG;YAAS,OAAQ,WAAW,iBAAiB,cAAc;kBAAuD,OAAQ,WAAW,iBAAiB,cAAc;yBAA8D,OAAQ,MAAM;0BAAuB,OAAQ,MAAM;;EAbprE,CAAA;AAEjE,eAAO,MAAM,WAAW;;;;;;;;EAQyC,CAAA;AAEjE,eAAO,MAAM,UAAU;;;;;;;;;;;;;yBAC85D,OAAQ,MAAM;qBAAkB,OAAQ,GAAG;YAAS,OAAQ,WAAW,iBAAiB,cAAc;kBAAuD,OAAQ,WAAW,iBAAiB,cAAc;yBAA8D,OAAQ,MAAM;0BAAuB,OAAQ,MAAM;;IADrrE,CAAA"}
|
package/dist/api-schema.js
CHANGED
|
@@ -3,13 +3,13 @@ import { Schema } from '@livestore/utils/effect';
|
|
|
3
3
|
export const PushPayload = Schema.TaggedStruct('@livestore/sync-electric.Push', {
|
|
4
4
|
storeId: Schema.String,
|
|
5
5
|
batch: Schema.Array(MutationEvent.AnyEncodedGlobal),
|
|
6
|
-
});
|
|
6
|
+
}).annotations({ title: '@livestore/sync-electric.PushPayload' });
|
|
7
7
|
export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull', {
|
|
8
8
|
storeId: Schema.String,
|
|
9
9
|
handle: Schema.Option(Schema.Struct({
|
|
10
10
|
offset: Schema.String,
|
|
11
11
|
handle: Schema.String,
|
|
12
12
|
})),
|
|
13
|
-
});
|
|
13
|
+
}).annotations({ title: '@livestore/sync-electric.PullPayload' });
|
|
14
14
|
export const ApiPayload = Schema.Union(PullPayload, PushPayload);
|
|
15
15
|
//# sourceMappingURL=api-schema.js.map
|
package/dist/api-schema.js.map
CHANGED
|
@@ -1 +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;
|
|
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,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC,CAAA;AAEjE,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,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC,CAAA;AAEjE,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import { Effect, Schema } from '@livestore/utils/effect';
|
|
1
|
+
import type { SyncBackendConstructor } from '@livestore/common';
|
|
2
|
+
import { Schema } from '@livestore/utils/effect';
|
|
4
3
|
export * as ApiSchema from './api-schema.js';
|
|
5
4
|
export declare const syncBackend: any;
|
|
6
5
|
export declare const syncBackendOptions: <TOptions extends SyncBackendOptions>(options: TOptions) => TOptions;
|
|
@@ -23,7 +22,6 @@ export declare const makeElectricUrl: ({ electricHost, searchParams: providedSea
|
|
|
23
22
|
needsInit: boolean;
|
|
24
23
|
};
|
|
25
24
|
export interface SyncBackendOptions {
|
|
26
|
-
storeId: string;
|
|
27
25
|
/**
|
|
28
26
|
* The endpoint to pull/push events. Pull is a `GET` request, push is a `POST` request.
|
|
29
27
|
* Usually this endpoint is part of your API layer to proxy requests to the Electric server
|
|
@@ -45,12 +43,12 @@ type SyncMetadata = {
|
|
|
45
43
|
offset: string;
|
|
46
44
|
handle: string;
|
|
47
45
|
};
|
|
48
|
-
export declare const makeSyncBackend: ({
|
|
46
|
+
export declare const makeSyncBackend: ({ endpoint }: SyncBackendOptions) => SyncBackendConstructor<SyncMetadata>;
|
|
49
47
|
/**
|
|
50
48
|
* Needs to be bumped when the storage format changes (e.g. mutationLogTable schema changes)
|
|
51
49
|
*
|
|
52
50
|
* Changing this version number will lead to a "soft reset".
|
|
53
51
|
*/
|
|
54
|
-
export declare const PERSISTENCE_FORMAT_VERSION =
|
|
52
|
+
export declare const PERSISTENCE_FORMAT_VERSION = 4;
|
|
55
53
|
export declare const toTableName: (storeId: string) => string;
|
|
56
54
|
//# 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,EAA+B,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAM5F,OAAO,EAQL,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,EAAE,SAAS,QAAQ,aAAY,CAAA;AAErG,eAAO,MAAM,eAAe,GAAI,+EAK7B;IACD,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAA;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;;;;CAmCA,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;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,GACzB,cAAc,kBAAkB,KAAG,sBAAsB,CAAC,YAAY,CAkJnE,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,WAG1C,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ const MutationEventGlobalFromStringRecord = Schema.Struct({
|
|
|
49
49
|
mutation: Schema.String,
|
|
50
50
|
args: Schema.parseJson(Schema.Any),
|
|
51
51
|
clientId: Schema.String,
|
|
52
|
-
sessionId: Schema.
|
|
52
|
+
sessionId: Schema.String,
|
|
53
53
|
}).pipe(Schema.transform(MutationEvent.AnyEncodedGlobal, {
|
|
54
54
|
decode: (_) => _,
|
|
55
55
|
encode: (_) => _,
|
|
@@ -77,7 +77,7 @@ export const makeElectricUrl = ({ electricHost, searchParams: providedSearchPara
|
|
|
77
77
|
const endpointUrl = `${electricHost}/v1/shape`;
|
|
78
78
|
const argsResult = Schema.decodeUnknownEither(Schema.Struct({ args: Schema.parseJson(ApiSchema.PullPayload) }))(Object.fromEntries(providedSearchParams.entries()));
|
|
79
79
|
if (argsResult._tag === 'Left') {
|
|
80
|
-
return shouldNeverHappen('Invalid search params provided to makeElectricUrl', providedSearchParams);
|
|
80
|
+
return shouldNeverHappen('Invalid search params provided to makeElectricUrl', providedSearchParams, Object.fromEntries(providedSearchParams.entries()));
|
|
81
81
|
}
|
|
82
82
|
const args = argsResult.right.args;
|
|
83
83
|
const tableName = toTableName(args.storeId);
|
|
@@ -105,7 +105,7 @@ export const SyncMetadata = Schema.Struct({
|
|
|
105
105
|
// TODO move this into some kind of "global" sync metadata as it's the same for each event
|
|
106
106
|
handle: Schema.String,
|
|
107
107
|
});
|
|
108
|
-
export const makeSyncBackend = ({ storeId,
|
|
108
|
+
export const makeSyncBackend = ({ endpoint }) => ({ storeId, payload }) => Effect.gen(function* () {
|
|
109
109
|
const isConnected = yield* SubscriptionRef.make(true);
|
|
110
110
|
const pullEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.pull;
|
|
111
111
|
const pushEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.push;
|
|
@@ -180,6 +180,12 @@ export const makeSyncBackend = ({ storeId, endpoint, }) => Effect.gen(function*
|
|
|
180
180
|
return { metadata };
|
|
181
181
|
}),
|
|
182
182
|
isConnected,
|
|
183
|
+
metadata: {
|
|
184
|
+
name: '@livestore/sync-electric',
|
|
185
|
+
description: 'LiveStore sync backend implementation using ElectricSQL',
|
|
186
|
+
protocol: 'http',
|
|
187
|
+
endpoint,
|
|
188
|
+
},
|
|
183
189
|
};
|
|
184
190
|
});
|
|
185
191
|
/**
|
|
@@ -187,7 +193,7 @@ export const makeSyncBackend = ({ storeId, endpoint, }) => Effect.gen(function*
|
|
|
187
193
|
*
|
|
188
194
|
* Changing this version number will lead to a "soft reset".
|
|
189
195
|
*/
|
|
190
|
-
export const PERSISTENCE_FORMAT_VERSION =
|
|
196
|
+
export const PERSISTENCE_FORMAT_VERSION = 4;
|
|
191
197
|
export const toTableName = (storeId) => {
|
|
192
198
|
const escapedStoreId = storeId.replaceAll(/[^a-zA-Z0-9_]/g, '_');
|
|
193
199
|
return `mutation_log_${PERSISTENCE_FORMAT_VERSION}_${escapedStoreId}`;
|
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;AACxD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEvE,OAAO,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,MAAM,EACN,MAAM,EACN,MAAM,EACN,eAAe,GAChB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAA;AAE5C,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAA;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCE;AAEF,MAAM,mCAAmC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxD,EAAE,EAAE,MAAM,CAAC,gBAAgB;IAC3B,QAAQ,EAAE,MAAM,CAAC,gBAAgB;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM;IACvB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM;IACvB,SAAS,EAAE,MAAM,CAAC,
|
|
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.24",
|
|
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.24",
|
|
14
|
+
"@livestore/utils": "0.3.0-dev.24"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {},
|
|
17
17
|
"publishConfig": {
|
package/src/api-schema.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Schema } from '@livestore/utils/effect'
|
|
|
4
4
|
export const PushPayload = Schema.TaggedStruct('@livestore/sync-electric.Push', {
|
|
5
5
|
storeId: Schema.String,
|
|
6
6
|
batch: Schema.Array(MutationEvent.AnyEncodedGlobal),
|
|
7
|
-
})
|
|
7
|
+
}).annotations({ title: '@livestore/sync-electric.PushPayload' })
|
|
8
8
|
|
|
9
9
|
export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull', {
|
|
10
10
|
storeId: Schema.String,
|
|
@@ -14,6 +14,6 @@ export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull',
|
|
|
14
14
|
handle: Schema.String,
|
|
15
15
|
}),
|
|
16
16
|
),
|
|
17
|
-
})
|
|
17
|
+
}).annotations({ title: '@livestore/sync-electric.PullPayload' })
|
|
18
18
|
|
|
19
19
|
export const ApiPayload = Schema.Union(PullPayload, PushPayload)
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IsOfflineError, SyncBackend } from '@livestore/common'
|
|
1
|
+
import type { IsOfflineError, SyncBackend, SyncBackendConstructor } from '@livestore/common'
|
|
2
2
|
import { InvalidPullError, InvalidPushError } from '@livestore/common'
|
|
3
3
|
import type { EventId } from '@livestore/common/schema'
|
|
4
4
|
import { MutationEvent } from '@livestore/common/schema'
|
|
@@ -67,7 +67,7 @@ const MutationEventGlobalFromStringRecord = Schema.Struct({
|
|
|
67
67
|
mutation: Schema.String,
|
|
68
68
|
args: Schema.parseJson(Schema.Any),
|
|
69
69
|
clientId: Schema.String,
|
|
70
|
-
sessionId: Schema.
|
|
70
|
+
sessionId: Schema.String,
|
|
71
71
|
}).pipe(
|
|
72
72
|
Schema.transform(MutationEvent.AnyEncodedGlobal, {
|
|
73
73
|
decode: (_) => _,
|
|
@@ -126,7 +126,11 @@ export const makeElectricUrl = ({
|
|
|
126
126
|
)
|
|
127
127
|
|
|
128
128
|
if (argsResult._tag === 'Left') {
|
|
129
|
-
return shouldNeverHappen(
|
|
129
|
+
return shouldNeverHappen(
|
|
130
|
+
'Invalid search params provided to makeElectricUrl',
|
|
131
|
+
providedSearchParams,
|
|
132
|
+
Object.fromEntries(providedSearchParams.entries()),
|
|
133
|
+
)
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
const args = argsResult.right.args
|
|
@@ -153,7 +157,6 @@ export const makeElectricUrl = ({
|
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
export interface SyncBackendOptions {
|
|
156
|
-
storeId: string
|
|
157
160
|
/**
|
|
158
161
|
* The endpoint to pull/push events. Pull is a `GET` request, push is a `POST` request.
|
|
159
162
|
* Usually this endpoint is part of your API layer to proxy requests to the Electric server
|
|
@@ -182,153 +185,161 @@ type SyncMetadata = {
|
|
|
182
185
|
handle: string
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
export const makeSyncBackend =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const headers = yield* HttpClientResponse.schemaHeaders(ResponseHeaders)(resp)
|
|
218
|
-
const nextHandle = {
|
|
219
|
-
offset: headers['electric-offset'],
|
|
220
|
-
handle: headers['electric-handle'],
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// TODO handle case where Electric shape is not found for a given handle
|
|
224
|
-
// https://electric-sql.com/openapi.html#/paths/~1v1~1shape/get
|
|
225
|
-
// {
|
|
226
|
-
// "message": "The shape associated with this shape_handle and offset was not found. Resync to fetch the latest shape",
|
|
227
|
-
// "shape_handle": "2494_84241",
|
|
228
|
-
// "offset": "-1"
|
|
229
|
-
// }
|
|
230
|
-
if (resp.status === 409) {
|
|
231
|
-
// TODO: implementation plan:
|
|
232
|
-
// start pulling events from scratch with the new handle and ignore the "old events"
|
|
233
|
-
// until we found a new event, then, continue with the new handle
|
|
234
|
-
return notYetImplemented(`Electric shape not found for handle ${nextHandle.handle}`)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Electric completes the long-poll request after ~20 seconds with a 204 status
|
|
238
|
-
// In this case we just retry where we left off
|
|
239
|
-
if (resp.status === 204) {
|
|
240
|
-
return Option.some([Chunk.empty(), Option.some(nextHandle)] as const)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const body = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem), {
|
|
244
|
-
onExcessProperty: 'preserve',
|
|
245
|
-
})(resp)
|
|
246
|
-
|
|
247
|
-
const items = body
|
|
248
|
-
.filter((item) => item.value !== undefined && (item.headers as any).operation === 'insert')
|
|
249
|
-
.map((item) => ({
|
|
250
|
-
metadata: Option.some({ offset: nextHandle.offset!, handle: nextHandle.handle }),
|
|
251
|
-
mutationEventEncoded: item.value! as MutationEvent.AnyEncodedGlobal,
|
|
252
|
-
}))
|
|
253
|
-
|
|
254
|
-
// // TODO implement proper `remaining` handling
|
|
255
|
-
// remaining: 0,
|
|
188
|
+
export const makeSyncBackend =
|
|
189
|
+
({ endpoint }: SyncBackendOptions): SyncBackendConstructor<SyncMetadata> =>
|
|
190
|
+
({ storeId, payload }) =>
|
|
191
|
+
Effect.gen(function* () {
|
|
192
|
+
const isConnected = yield* SubscriptionRef.make(true)
|
|
193
|
+
const pullEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.pull
|
|
194
|
+
const pushEndpoint = typeof endpoint === 'string' ? endpoint : endpoint.push
|
|
195
|
+
|
|
196
|
+
// TODO check whether we still need this
|
|
197
|
+
const pendingPushDeferredMap = new Map<EventId.GlobalEventId, Deferred.Deferred<SyncMetadata>>()
|
|
198
|
+
|
|
199
|
+
const pull = (
|
|
200
|
+
handle: Option.Option<SyncMetadata>,
|
|
201
|
+
): Effect.Effect<
|
|
202
|
+
Option.Option<
|
|
203
|
+
readonly [
|
|
204
|
+
Chunk.Chunk<{
|
|
205
|
+
metadata: Option.Option<SyncMetadata>
|
|
206
|
+
mutationEventEncoded: MutationEvent.AnyEncodedGlobal
|
|
207
|
+
}>,
|
|
208
|
+
Option.Option<SyncMetadata>,
|
|
209
|
+
]
|
|
210
|
+
>,
|
|
211
|
+
InvalidPullError | IsOfflineError,
|
|
212
|
+
HttpClient.HttpClient
|
|
213
|
+
> =>
|
|
214
|
+
Effect.gen(function* () {
|
|
215
|
+
const argsJson = yield* Schema.encode(Schema.parseJson(ApiSchema.PullPayload))(
|
|
216
|
+
ApiSchema.PullPayload.make({ storeId, handle }),
|
|
217
|
+
)
|
|
218
|
+
const url = `${pullEndpoint}?args=${argsJson}`
|
|
256
219
|
|
|
257
|
-
|
|
258
|
-
// return Option.none()
|
|
259
|
-
// }
|
|
220
|
+
const resp = yield* HttpClient.get(url)
|
|
260
221
|
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
222
|
+
const headers = yield* HttpClientResponse.schemaHeaders(ResponseHeaders)(resp)
|
|
223
|
+
const nextHandle = {
|
|
224
|
+
offset: headers['electric-offset'],
|
|
225
|
+
handle: headers['electric-handle'],
|
|
265
226
|
}
|
|
266
|
-
}
|
|
267
227
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
),
|
|
281
|
-
(metadataOption) => pull(metadataOption),
|
|
282
|
-
).pipe(
|
|
283
|
-
Stream.chunks,
|
|
284
|
-
Stream.map((chunk) => ({ batch: [...chunk], remaining: 0 })),
|
|
285
|
-
),
|
|
286
|
-
|
|
287
|
-
push: (batch) =>
|
|
288
|
-
Effect.gen(function* () {
|
|
289
|
-
const deferreds: Deferred.Deferred<SyncMetadata>[] = []
|
|
290
|
-
for (const mutationEventEncoded of batch) {
|
|
291
|
-
const deferred = yield* Deferred.make<SyncMetadata>()
|
|
292
|
-
pendingPushDeferredMap.set(mutationEventEncoded.id, deferred)
|
|
293
|
-
deferreds.push(deferred)
|
|
228
|
+
// TODO handle case where Electric shape is not found for a given handle
|
|
229
|
+
// https://electric-sql.com/openapi.html#/paths/~1v1~1shape/get
|
|
230
|
+
// {
|
|
231
|
+
// "message": "The shape associated with this shape_handle and offset was not found. Resync to fetch the latest shape",
|
|
232
|
+
// "shape_handle": "2494_84241",
|
|
233
|
+
// "offset": "-1"
|
|
234
|
+
// }
|
|
235
|
+
if (resp.status === 409) {
|
|
236
|
+
// TODO: implementation plan:
|
|
237
|
+
// start pulling events from scratch with the new handle and ignore the "old events"
|
|
238
|
+
// until we found a new event, then, continue with the new handle
|
|
239
|
+
return notYetImplemented(`Electric shape not found for handle ${nextHandle.handle}`)
|
|
294
240
|
}
|
|
295
241
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
Effect.andThen(HttpClientResponse.schemaBodyJson(Schema.Struct({ success: Schema.Boolean }))),
|
|
302
|
-
Effect.scoped,
|
|
303
|
-
Effect.mapError((cause) =>
|
|
304
|
-
InvalidPushError.make({ reason: { _tag: 'Unexpected', message: cause.toString() } }),
|
|
305
|
-
),
|
|
306
|
-
)
|
|
242
|
+
// Electric completes the long-poll request after ~20 seconds with a 204 status
|
|
243
|
+
// In this case we just retry where we left off
|
|
244
|
+
if (resp.status === 204) {
|
|
245
|
+
return Option.some([Chunk.empty(), Option.some(nextHandle)] as const)
|
|
246
|
+
}
|
|
307
247
|
|
|
308
|
-
|
|
309
|
-
|
|
248
|
+
const body = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem), {
|
|
249
|
+
onExcessProperty: 'preserve',
|
|
250
|
+
})(resp)
|
|
251
|
+
|
|
252
|
+
const items = body
|
|
253
|
+
.filter((item) => item.value !== undefined && (item.headers as any).operation === 'insert')
|
|
254
|
+
.map((item) => ({
|
|
255
|
+
metadata: Option.some({ offset: nextHandle.offset!, handle: nextHandle.handle }),
|
|
256
|
+
mutationEventEncoded: item.value! as MutationEvent.AnyEncodedGlobal,
|
|
257
|
+
}))
|
|
258
|
+
|
|
259
|
+
// // TODO implement proper `remaining` handling
|
|
260
|
+
// remaining: 0,
|
|
261
|
+
|
|
262
|
+
// if (listenForNew === false && items.length === 0) {
|
|
263
|
+
// return Option.none()
|
|
264
|
+
// }
|
|
265
|
+
|
|
266
|
+
for (const item of items) {
|
|
267
|
+
const deferred = pendingPushDeferredMap.get(item.mutationEventEncoded.id)
|
|
268
|
+
if (deferred !== undefined) {
|
|
269
|
+
yield* Deferred.succeed(deferred, Option.getOrThrow(item.metadata))
|
|
270
|
+
}
|
|
310
271
|
}
|
|
311
272
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
273
|
+
return Option.some([Chunk.fromIterable(items), Option.some(nextHandle)] as const)
|
|
274
|
+
}).pipe(
|
|
275
|
+
Effect.scoped,
|
|
276
|
+
Effect.mapError((cause) => InvalidPullError.make({ message: cause.toString() })),
|
|
277
|
+
)
|
|
315
278
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
279
|
+
return {
|
|
280
|
+
pull: (args) =>
|
|
281
|
+
Stream.unfoldChunkEffect(
|
|
282
|
+
args.pipe(
|
|
283
|
+
Option.map((_) => _.metadata),
|
|
284
|
+
Option.flatten,
|
|
285
|
+
),
|
|
286
|
+
(metadataOption) => pull(metadataOption),
|
|
287
|
+
).pipe(
|
|
288
|
+
Stream.chunks,
|
|
289
|
+
Stream.map((chunk) => ({ batch: [...chunk], remaining: 0 })),
|
|
290
|
+
),
|
|
319
291
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
292
|
+
push: (batch) =>
|
|
293
|
+
Effect.gen(function* () {
|
|
294
|
+
const deferreds: Deferred.Deferred<SyncMetadata>[] = []
|
|
295
|
+
for (const mutationEventEncoded of batch) {
|
|
296
|
+
const deferred = yield* Deferred.make<SyncMetadata>()
|
|
297
|
+
pendingPushDeferredMap.set(mutationEventEncoded.id, deferred)
|
|
298
|
+
deferreds.push(deferred)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const resp = yield* HttpClientRequest.schemaBodyJson(ApiSchema.PushPayload)(
|
|
302
|
+
HttpClientRequest.post(pushEndpoint),
|
|
303
|
+
ApiSchema.PushPayload.make({ storeId, batch }),
|
|
304
|
+
).pipe(
|
|
305
|
+
Effect.andThen(HttpClient.execute),
|
|
306
|
+
Effect.andThen(HttpClientResponse.schemaBodyJson(Schema.Struct({ success: Schema.Boolean }))),
|
|
307
|
+
Effect.scoped,
|
|
308
|
+
Effect.mapError((cause) =>
|
|
309
|
+
InvalidPushError.make({ reason: { _tag: 'Unexpected', message: cause.toString() } }),
|
|
310
|
+
),
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
if (!resp.success) {
|
|
314
|
+
yield* InvalidPushError.make({ reason: { _tag: 'Unexpected', message: 'Push failed' } })
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const metadata = yield* Effect.all(deferreds, { concurrency: 'unbounded' }).pipe(
|
|
318
|
+
Effect.map((_) => _.map(Option.some)),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
for (const mutationEventEncoded of batch) {
|
|
322
|
+
pendingPushDeferredMap.delete(mutationEventEncoded.id)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return { metadata }
|
|
326
|
+
}),
|
|
327
|
+
isConnected,
|
|
328
|
+
metadata: {
|
|
329
|
+
name: '@livestore/sync-electric',
|
|
330
|
+
description: 'LiveStore sync backend implementation using ElectricSQL',
|
|
331
|
+
protocol: 'http',
|
|
332
|
+
endpoint,
|
|
333
|
+
},
|
|
334
|
+
} satisfies SyncBackend<SyncMetadata>
|
|
335
|
+
})
|
|
325
336
|
|
|
326
337
|
/**
|
|
327
338
|
* Needs to be bumped when the storage format changes (e.g. mutationLogTable schema changes)
|
|
328
339
|
*
|
|
329
340
|
* Changing this version number will lead to a "soft reset".
|
|
330
341
|
*/
|
|
331
|
-
export const PERSISTENCE_FORMAT_VERSION =
|
|
342
|
+
export const PERSISTENCE_FORMAT_VERSION = 4
|
|
332
343
|
|
|
333
344
|
export const toTableName = (storeId: string) => {
|
|
334
345
|
const escapedStoreId = storeId.replaceAll(/[^a-zA-Z0-9_]/g, '_')
|
package/tmp/pack.tgz
CHANGED
|
Binary file
|