@livestore/sync-electric 0.4.0 → 0.5.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/api-schema.d.ts +45 -50
- package/dist/api-schema.d.ts.map +1 -1
- package/dist/api-schema.js +13 -7
- package/dist/api-schema.js.map +1 -1
- package/dist/api-schema.test.d.ts +2 -0
- package/dist/api-schema.test.d.ts.map +1 -0
- package/dist/api-schema.test.js +42 -0
- package/dist/api-schema.test.js.map +1 -0
- package/dist/index.d.ts +8 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -28
- package/dist/index.js.map +1 -1
- package/dist/make-electric-url.d.ts +1 -1
- package/dist/make-electric-url.d.ts.map +1 -1
- package/dist/make-electric-url.js +5 -5
- package/dist/make-electric-url.js.map +1 -1
- package/package.json +37 -54
- package/src/api-schema.test.ts +49 -0
- package/src/api-schema.ts +18 -10
- package/src/index.ts +31 -30
- package/src/make-electric-url.ts +5 -5
package/package.json
CHANGED
|
@@ -1,80 +1,63 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$genie": {
|
|
3
|
+
"source": "package.json.genie.ts",
|
|
4
|
+
"warning": "DO NOT EDIT - changes will be overwritten",
|
|
5
|
+
"workspaceClosureDirs": [
|
|
6
|
+
"packages/@livestore/sync-electric",
|
|
7
|
+
"repos/livestore/packages/@livestore/common",
|
|
8
|
+
"repos/livestore/packages/@livestore/utils",
|
|
9
|
+
"repos/livestore/packages/@livestore/utils-dev",
|
|
10
|
+
"repos/livestore/packages/@livestore/webmesh"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
2
13
|
"name": "@livestore/sync-electric",
|
|
3
|
-
"version": "0.
|
|
14
|
+
"version": "0.5.0-dev.1",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
4
17
|
"license": "Apache-2.0",
|
|
5
18
|
"repository": {
|
|
6
19
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/livestorejs/livestore.git"
|
|
20
|
+
"url": "git+https://github.com/livestorejs/livestore-contrib.git"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./dist/index.js"
|
|
8
24
|
},
|
|
9
25
|
"files": [
|
|
10
26
|
"dist",
|
|
11
27
|
"package.json",
|
|
12
28
|
"src"
|
|
13
29
|
],
|
|
14
|
-
"type": "module",
|
|
15
|
-
"sideEffects": false,
|
|
16
|
-
"exports": {
|
|
17
|
-
".": "./dist/index.js"
|
|
18
|
-
},
|
|
19
|
-
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
},
|
|
22
30
|
"dependencies": {
|
|
23
|
-
"@livestore/common": "
|
|
24
|
-
"@livestore/utils": "
|
|
31
|
+
"@livestore/common": "0.5.0-dev.1",
|
|
32
|
+
"@livestore/utils": "0.5.0-dev.1"
|
|
25
33
|
},
|
|
26
34
|
"devDependencies": {
|
|
27
|
-
"@effect/
|
|
28
|
-
"@effect/
|
|
29
|
-
"@effect/
|
|
30
|
-
"@effect/
|
|
31
|
-
"@effect/
|
|
32
|
-
"@effect/
|
|
33
|
-
"@effect/platform-browser": "0.76.0",
|
|
34
|
-
"@effect/platform-bun": "0.89.0",
|
|
35
|
-
"@effect/platform-node": "0.106.0",
|
|
36
|
-
"@effect/printer": "0.49.0",
|
|
37
|
-
"@effect/printer-ansi": "0.49.0",
|
|
38
|
-
"@effect/rpc": "0.75.1",
|
|
39
|
-
"@effect/sql": "0.51.1",
|
|
40
|
-
"@effect/typeclass": "0.40.0",
|
|
41
|
-
"@effect/vitest": "0.29.0",
|
|
35
|
+
"@effect/opentelemetry": "4.0.3",
|
|
36
|
+
"@effect/platform-browser": "4.0.3",
|
|
37
|
+
"@effect/platform-bun": "4.0.3",
|
|
38
|
+
"@effect/platform-node": "4.0.3",
|
|
39
|
+
"@effect/platform-node-shared": "4.0.3",
|
|
40
|
+
"@effect/vitest": "4.0.3",
|
|
42
41
|
"@opentelemetry/api": "1.9.0",
|
|
43
42
|
"@opentelemetry/resources": "2.2.0",
|
|
44
43
|
"@standard-schema/spec": "1.1.0",
|
|
45
|
-
"effect": "
|
|
44
|
+
"effect": "4.0.3",
|
|
45
|
+
"vitest": "4.1.9"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@effect/
|
|
49
|
-
"@effect/
|
|
50
|
-
"@effect/
|
|
51
|
-
"@effect/
|
|
52
|
-
"@effect/
|
|
53
|
-
"@effect/
|
|
54
|
-
"@effect/platform-browser": "^0.76.0",
|
|
55
|
-
"@effect/platform-bun": "^0.89.0",
|
|
56
|
-
"@effect/platform-node": "^0.106.0",
|
|
57
|
-
"@effect/printer": "^0.49.0",
|
|
58
|
-
"@effect/printer-ansi": "^0.49.0",
|
|
59
|
-
"@effect/rpc": "^0.75.1",
|
|
60
|
-
"@effect/sql": "^0.51.1",
|
|
61
|
-
"@effect/typeclass": "^0.40.0",
|
|
62
|
-
"@effect/vitest": "^0.29.0",
|
|
48
|
+
"@effect/opentelemetry": "^4.0.3",
|
|
49
|
+
"@effect/platform-browser": "^4.0.3",
|
|
50
|
+
"@effect/platform-bun": "^4.0.3",
|
|
51
|
+
"@effect/platform-node": "^4.0.3",
|
|
52
|
+
"@effect/platform-node-shared": "^4.0.3",
|
|
53
|
+
"@effect/vitest": "^4.0.3",
|
|
63
54
|
"@opentelemetry/api": "^1.9.0",
|
|
64
55
|
"@opentelemetry/resources": "^2.2.0",
|
|
65
56
|
"@standard-schema/spec": "^1.1.0",
|
|
66
|
-
"effect": "^
|
|
57
|
+
"effect": "^4.0.3"
|
|
67
58
|
},
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"warning": "DO NOT EDIT - changes will be overwritten",
|
|
71
|
-
"workspaceClosureDirs": [
|
|
72
|
-
"packages/@livestore/common",
|
|
73
|
-
"packages/@livestore/sync-electric",
|
|
74
|
-
"packages/@livestore/utils",
|
|
75
|
-
"packages/@livestore/utils-dev",
|
|
76
|
-
"packages/@livestore/webmesh"
|
|
77
|
-
]
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
78
61
|
},
|
|
79
62
|
"scripts": {
|
|
80
63
|
"build": "",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Option, Schema } from '@livestore/utils/effect'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import * as ApiSchema from './api-schema.ts'
|
|
5
|
+
import { makeElectricUrl } from './make-electric-url.ts'
|
|
6
|
+
|
|
7
|
+
const HandleStruct = { offset: 'off-1', handle: 'h-1' } as const
|
|
8
|
+
|
|
9
|
+
const cases = [
|
|
10
|
+
{ name: 'none + no payload', storeId: 's1', payload: undefined, handle: Option.none(), live: false },
|
|
11
|
+
{ name: 'some + payload', storeId: 's2', payload: { foo: 'bar' }, handle: Option.some(HandleStruct), live: true },
|
|
12
|
+
{ name: 'none + payload present', storeId: 's3', payload: { a: 1 }, handle: Option.none(), live: false },
|
|
13
|
+
{ name: 'some + no payload', storeId: 's4', payload: undefined, handle: Option.some(HandleStruct), live: true },
|
|
14
|
+
{ name: 'none + null payload', storeId: 's5', payload: null, handle: Option.none(), live: false },
|
|
15
|
+
] as const
|
|
16
|
+
|
|
17
|
+
describe('sync-electric ArgsSchema round-trip', () => {
|
|
18
|
+
for (const c of cases) {
|
|
19
|
+
it(`round-trips ${c.name}`, () => {
|
|
20
|
+
const input = ApiSchema.PullPayload.make({
|
|
21
|
+
storeId: c.storeId,
|
|
22
|
+
payload: c.payload,
|
|
23
|
+
handle: c.handle,
|
|
24
|
+
live: c.live,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const encoded = Schema.encodeSync(ApiSchema.ArgsSchema)(input)
|
|
28
|
+
expect(typeof encoded).toBe('string')
|
|
29
|
+
|
|
30
|
+
const decoded = Schema.decodeSync(ApiSchema.ArgsSchema)(encoded)
|
|
31
|
+
|
|
32
|
+
expect(Option.isOption(decoded.handle)).toBe(true)
|
|
33
|
+
expect(decoded.handle._tag).toBe(c.handle._tag)
|
|
34
|
+
if (Option.isSome(c.handle) === true) {
|
|
35
|
+
expect((decoded.handle as Option.Some<typeof HandleStruct>).value).toEqual(HandleStruct)
|
|
36
|
+
}
|
|
37
|
+
expect(decoded.payload).toEqual(c.payload)
|
|
38
|
+
expect(decoded.storeId).toBe(c.storeId)
|
|
39
|
+
expect(decoded.live).toBe(c.live)
|
|
40
|
+
|
|
41
|
+
// Full path through makeElectricUrl: URLSearchParams -> Struct({ args }) -> ArgsSchema
|
|
42
|
+
const searchParams = new URLSearchParams({ args: encoded })
|
|
43
|
+
const result = makeElectricUrl({ electricHost: 'http://electric.test', searchParams })
|
|
44
|
+
expect(result.storeId).toBe(c.storeId)
|
|
45
|
+
expect(result.needsInit).toBe(Option.isNone(c.handle))
|
|
46
|
+
expect(result.payload).toEqual(c.payload)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
})
|
package/src/api-schema.ts
CHANGED
|
@@ -4,21 +4,29 @@ 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(LiveStoreEvent.Global.Encoded),
|
|
7
|
-
}).
|
|
7
|
+
}).annotate({ title: '@livestore/sync-electric.PushPayload' })
|
|
8
8
|
|
|
9
9
|
export const PullPayload = Schema.TaggedStruct('@livestore/sync-electric.Pull', {
|
|
10
10
|
storeId: Schema.String,
|
|
11
|
-
payload
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
// `optional` so an absent payload is omitted from the JSON entirely (a required key with an
|
|
12
|
+
// `undefined` value would be dropped by `JSON.stringify` and then fail decoding as "Missing key").
|
|
13
|
+
payload: Schema.optional(Schema.Json),
|
|
14
|
+
// `toCodecJson` makes the `Option` encode to its JSON-safe struct form (`{ _tag: "None" }` /
|
|
15
|
+
// `{ _tag: "Some", value }`). A bare `Schema.Option` encodes to a runtime `Option`, whose
|
|
16
|
+
// `toJSON` leaks `{"_id":"Option",...}` when `fromJsonString` runs `JSON.stringify`, and that
|
|
17
|
+
// shape then fails to decode back into an `Option`.
|
|
18
|
+
handle: Schema.toCodecJson(
|
|
19
|
+
Schema.Option(
|
|
20
|
+
Schema.Struct({
|
|
21
|
+
offset: Schema.String,
|
|
22
|
+
handle: Schema.String,
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
17
25
|
),
|
|
18
26
|
live: Schema.Boolean,
|
|
19
|
-
}).
|
|
27
|
+
}).annotate({ title: '@livestore/sync-electric.PullPayload' })
|
|
20
28
|
|
|
21
|
-
export const ApiPayload = Schema.Union(PullPayload, PushPayload)
|
|
29
|
+
export const ApiPayload = Schema.Union([PullPayload, PushPayload])
|
|
22
30
|
|
|
23
31
|
// Format for the query params
|
|
24
|
-
export const ArgsSchema = Schema.
|
|
32
|
+
export const ArgsSchema = Schema.StringFromUriComponent.pipe(Schema.decodeTo(Schema.fromJsonString(PullPayload)))
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
HttpClientResponse,
|
|
10
10
|
Option,
|
|
11
11
|
ReadonlyArray,
|
|
12
|
+
Result,
|
|
12
13
|
Schedule,
|
|
13
14
|
Schema,
|
|
14
15
|
Stream,
|
|
@@ -17,8 +18,10 @@ import {
|
|
|
17
18
|
|
|
18
19
|
import * as ApiSchema from './api-schema.ts'
|
|
19
20
|
|
|
20
|
-
export class InvalidOperationError extends Schema.TaggedError<InvalidOperationError>(
|
|
21
|
-
|
|
21
|
+
export class InvalidOperationError extends Schema.TaggedError<InvalidOperationError>(
|
|
22
|
+
'~@livestore/sync-electric/InvalidOperationError',
|
|
23
|
+
)('InvalidOperationError', {
|
|
24
|
+
operation: Schema.Literals(['delete', 'update']),
|
|
22
25
|
message: Schema.String,
|
|
23
26
|
}) {}
|
|
24
27
|
|
|
@@ -66,37 +69,37 @@ Also see: https://github.com/electric-sql/electric/blob/main/packages/typescript
|
|
|
66
69
|
*/
|
|
67
70
|
|
|
68
71
|
const LiveStoreEventGlobalFromStringRecord = Schema.Struct({
|
|
69
|
-
seqNum: Schema.
|
|
70
|
-
parentSeqNum: Schema.
|
|
72
|
+
seqNum: Schema.FiniteFromString,
|
|
73
|
+
parentSeqNum: Schema.FiniteFromString,
|
|
71
74
|
name: Schema.String,
|
|
72
|
-
args: Schema.
|
|
75
|
+
args: Schema.fromJsonString(Schema.Any),
|
|
73
76
|
clientId: Schema.String,
|
|
74
77
|
sessionId: Schema.String,
|
|
75
78
|
})
|
|
76
|
-
.pipe(Schema.
|
|
77
|
-
.
|
|
79
|
+
.pipe(Schema.decodeTo(LiveStoreEvent.Global.Encoded))
|
|
80
|
+
.annotate({ title: '@livestore/sync-electric:LiveStoreEventGlobalFromStringRecord' })
|
|
78
81
|
|
|
79
82
|
const ResponseItemInsert = Schema.Struct({
|
|
80
83
|
/** Postgres path (e.g. `"public"."events_9069baf0_b3e6_42f7_980f_188416eab3fx3"/"0"`) */
|
|
81
84
|
key: Schema.optional(Schema.String),
|
|
82
85
|
value: LiveStoreEventGlobalFromStringRecord,
|
|
83
86
|
headers: Schema.Struct({ operation: Schema.Literal('insert'), relation: Schema.Array(Schema.String) }),
|
|
84
|
-
}).
|
|
87
|
+
}).annotate({ title: '@livestore/sync-electric:ResponseItemInsert' })
|
|
85
88
|
|
|
86
89
|
const ResponseItemInvalid = Schema.Struct({
|
|
87
90
|
/** Postgres path (e.g. `"public"."events_9069baf0_b3e6_42f7_980f_188416eab3fx3"/"0"`) */
|
|
88
91
|
key: Schema.optional(Schema.String),
|
|
89
92
|
value: Schema.Any,
|
|
90
|
-
headers: Schema.Struct({ operation: Schema.
|
|
91
|
-
}).
|
|
93
|
+
headers: Schema.Struct({ operation: Schema.Literals(['update', 'delete']), relation: Schema.Array(Schema.String) }),
|
|
94
|
+
}).annotate({ title: '@livestore/sync-electric:ResponseItemInvalid' })
|
|
92
95
|
|
|
93
96
|
const ResponseItemControl = Schema.Struct({
|
|
94
97
|
key: Schema.optional(Schema.String),
|
|
95
98
|
value: Schema.optional(Schema.Any),
|
|
96
99
|
headers: Schema.Struct({ control: Schema.String }),
|
|
97
|
-
}).
|
|
100
|
+
}).annotate({ title: '@livestore/sync-electric:ResponseItemControl' })
|
|
98
101
|
|
|
99
|
-
const ResponseItem = Schema.Union(ResponseItemInsert, ResponseItemInvalid, ResponseItemControl)
|
|
102
|
+
const ResponseItem = Schema.Union([ResponseItemInsert, ResponseItemInvalid, ResponseItemControl])
|
|
100
103
|
|
|
101
104
|
const ResponseHeaders = Schema.Struct({
|
|
102
105
|
'electric-handle': Schema.String,
|
|
@@ -133,12 +136,12 @@ export interface SyncBackendOptions {
|
|
|
133
136
|
* How long to wait for a ping response before timing out
|
|
134
137
|
* @default 10 seconds
|
|
135
138
|
*/
|
|
136
|
-
requestTimeout?: Duration.
|
|
139
|
+
requestTimeout?: Duration.Input
|
|
137
140
|
/**
|
|
138
141
|
* How often to send ping requests
|
|
139
142
|
* @default 10 seconds
|
|
140
143
|
*/
|
|
141
|
-
requestInterval?: Duration.
|
|
144
|
+
requestInterval?: Duration.Input
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
|
|
@@ -220,7 +223,7 @@ export const makeSyncBackend =
|
|
|
220
223
|
UnknownError | IsOfflineError
|
|
221
224
|
> =>
|
|
222
225
|
Effect.gen(function* () {
|
|
223
|
-
const argsJson = yield* Schema.
|
|
226
|
+
const argsJson = yield* Schema.encodeEffect(ApiSchema.ArgsSchema)(
|
|
224
227
|
ApiSchema.PullPayload.make({ storeId, handle, payload, live }),
|
|
225
228
|
)
|
|
226
229
|
const url = `${pullEndpoint}?args=${argsJson}`
|
|
@@ -228,7 +231,7 @@ export const makeSyncBackend =
|
|
|
228
231
|
const resp = yield* httpClient.get(url)
|
|
229
232
|
|
|
230
233
|
if (resp.status === 401) {
|
|
231
|
-
const body = yield* resp.text.pipe(Effect.
|
|
234
|
+
const body = yield* resp.text.pipe(Effect.orElseSucceed(() => '-'))
|
|
232
235
|
return yield* new UnknownError({
|
|
233
236
|
cause: new Error(`Unauthorized (401): Couldn't connect to ElectricSQL: ${body}`),
|
|
234
237
|
})
|
|
@@ -265,13 +268,11 @@ export const makeSyncBackend =
|
|
|
265
268
|
return Option.some([[], Option.some(nextHandle)] as const)
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
const allItems = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem)
|
|
269
|
-
onExcessProperty: 'preserve',
|
|
270
|
-
})(resp)
|
|
271
|
+
const allItems = yield* HttpClientResponse.schemaBodyJson(Schema.Array(ResponseItem))(resp)
|
|
271
272
|
|
|
272
273
|
// Check for delete/update operations and throw descriptive error
|
|
273
274
|
const invalidOperations = ReadonlyArray.filterMap(allItems, (item) =>
|
|
274
|
-
Schema.is(ResponseItemInvalid)(item) === true ?
|
|
275
|
+
Schema.is(ResponseItemInvalid)(item) === true ? Result.succeed(item.headers.operation) : Result.failVoid,
|
|
275
276
|
)
|
|
276
277
|
|
|
277
278
|
if (invalidOperations.length > 0) {
|
|
@@ -292,9 +293,7 @@ export const makeSyncBackend =
|
|
|
292
293
|
return Option.some([items, Option.some(nextHandle)] as const)
|
|
293
294
|
}).pipe(
|
|
294
295
|
Effect.scoped,
|
|
295
|
-
Effect.mapError((cause) =>
|
|
296
|
-
cause._tag === 'UnknownError' ? cause : new UnknownError({ cause }),
|
|
297
|
-
),
|
|
296
|
+
Effect.mapError((cause) => (Schema.is(UnknownError)(cause) === true ? cause : new UnknownError({ cause }))),
|
|
298
297
|
Effect.withSpan('electric-provider:runPull', { attributes: { handle, live } }),
|
|
299
298
|
)
|
|
300
299
|
|
|
@@ -310,8 +309,10 @@ export const makeSyncBackend =
|
|
|
310
309
|
yield* SubscriptionRef.set(isConnected, true)
|
|
311
310
|
}).pipe(
|
|
312
311
|
UnknownError.mapToUnknownError,
|
|
313
|
-
Effect.
|
|
314
|
-
|
|
312
|
+
Effect.timeoutOrElse({
|
|
313
|
+
duration: pingTimeout,
|
|
314
|
+
orElse: () => SubscriptionRef.set(isConnected, false),
|
|
315
|
+
}),
|
|
315
316
|
Effect.withSpan('electric-provider:ping'),
|
|
316
317
|
)
|
|
317
318
|
|
|
@@ -332,27 +333,27 @@ export const makeSyncBackend =
|
|
|
332
333
|
pull: (cursor, options) => {
|
|
333
334
|
let hasEmittedAtLeastOnce = false
|
|
334
335
|
|
|
335
|
-
return Stream.
|
|
336
|
+
return Stream.unfold(cursor.pipe(Option.flatMap((_) => _.metadata)), (metadataOption) =>
|
|
336
337
|
Effect.gen(function* () {
|
|
337
338
|
const result = yield* runPull(metadataOption, { live: options?.live ?? false })
|
|
338
|
-
if (Option.isNone(result) === true) return
|
|
339
|
+
if (Option.isNone(result) === true) return undefined
|
|
339
340
|
|
|
340
341
|
const [batch, nextMetadataOption] = result.value
|
|
341
342
|
|
|
342
343
|
// Continue pagination if we have data
|
|
343
344
|
if (batch.length > 0) {
|
|
344
345
|
hasEmittedAtLeastOnce = true
|
|
345
|
-
return
|
|
346
|
+
return [{ batch, hasMore: true as boolean }, nextMetadataOption] as const
|
|
346
347
|
}
|
|
347
348
|
|
|
348
349
|
// Make sure we emit at least once even if there's no data or we're live-pulling
|
|
349
350
|
if (hasEmittedAtLeastOnce === false || options?.live === true) {
|
|
350
351
|
hasEmittedAtLeastOnce = true
|
|
351
|
-
return
|
|
352
|
+
return [{ batch, hasMore: false as boolean }, nextMetadataOption] as const
|
|
352
353
|
}
|
|
353
354
|
|
|
354
355
|
// Stop on empty batch (when not live)
|
|
355
|
-
return
|
|
356
|
+
return undefined
|
|
356
357
|
}),
|
|
357
358
|
).pipe(
|
|
358
359
|
Stream.map(({ batch, hasMore }) => ({
|
package/src/make-electric-url.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
|
-
import { Hash, Schema } from '@livestore/utils/effect'
|
|
2
|
+
import { Hash, Result, Schema } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
import * as ApiSchema from './api-schema.ts'
|
|
5
5
|
|
|
@@ -40,13 +40,13 @@ export const makeElectricUrl = ({
|
|
|
40
40
|
*/
|
|
41
41
|
needsInit: boolean
|
|
42
42
|
/** Sync payload provided by the client */
|
|
43
|
-
payload: Schema.
|
|
43
|
+
payload: Schema.Json | undefined
|
|
44
44
|
} => {
|
|
45
45
|
const endpointUrl = `${electricHost}/v1/shape`
|
|
46
46
|
const UrlParamsSchema = Schema.Struct({ args: ApiSchema.ArgsSchema })
|
|
47
|
-
const argsResult = Schema.
|
|
47
|
+
const argsResult = Schema.decodeUnknownResult(UrlParamsSchema)(Object.fromEntries(providedSearchParams.entries()))
|
|
48
48
|
|
|
49
|
-
if (argsResult
|
|
49
|
+
if (Result.isFailure(argsResult) === true) {
|
|
50
50
|
return shouldNeverHappen(
|
|
51
51
|
'Invalid search params provided to makeElectricUrl',
|
|
52
52
|
providedSearchParams,
|
|
@@ -54,7 +54,7 @@ export const makeElectricUrl = ({
|
|
|
54
54
|
)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const args = argsResult.
|
|
57
|
+
const args = argsResult.success.args
|
|
58
58
|
const tableName = toTableName(args.storeId)
|
|
59
59
|
// TODO refactor with Effect URLSearchParams schema
|
|
60
60
|
// https://electric-sql.com/openapi.html
|