@electric-sql/client 1.0.5 → 1.0.7
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/cjs/index.cjs +30 -31
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -3
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.legacy-esm.js +30 -31
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +30 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +21 -22
- package/src/fetch.ts +1 -8
- package/src/helpers.ts +4 -3
- package/src/parser.ts +16 -17
- package/src/types.ts +1 -1
package/src/client.ts
CHANGED
|
@@ -398,22 +398,12 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
398
398
|
backOffOpts
|
|
399
399
|
)
|
|
400
400
|
|
|
401
|
-
this.#fetchClient = createFetchWithConsumedMessages(
|
|
402
|
-
createFetchWithResponseHeadersCheck(
|
|
403
|
-
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
404
|
-
)
|
|
405
|
-
)
|
|
406
|
-
|
|
407
|
-
const sseFetchWithBackoffClient = createFetchWithBackoff(
|
|
408
|
-
baseFetchClient,
|
|
409
|
-
backOffOpts,
|
|
410
|
-
true
|
|
411
|
-
)
|
|
412
|
-
|
|
413
401
|
this.#sseFetchClient = createFetchWithResponseHeadersCheck(
|
|
414
|
-
createFetchWithChunkBuffer(
|
|
402
|
+
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
415
403
|
)
|
|
416
404
|
|
|
405
|
+
this.#fetchClient = createFetchWithConsumedMessages(this.#sseFetchClient)
|
|
406
|
+
|
|
417
407
|
this.#subscribeToVisibilityChanges()
|
|
418
408
|
}
|
|
419
409
|
|
|
@@ -498,7 +488,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
498
488
|
fetchUrl,
|
|
499
489
|
requestAbortController,
|
|
500
490
|
headers: requestHeaders,
|
|
501
|
-
resumingFromPause
|
|
491
|
+
resumingFromPause,
|
|
502
492
|
})
|
|
503
493
|
} catch (e) {
|
|
504
494
|
// Handle abort error triggered by refresh
|
|
@@ -671,9 +661,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
671
661
|
}
|
|
672
662
|
}
|
|
673
663
|
|
|
674
|
-
async #onMessages(
|
|
675
|
-
const batch = this.#messageParser.parse(messages, schema)
|
|
676
|
-
|
|
664
|
+
async #onMessages(batch: Array<Message<T>>, isSseMessage = false) {
|
|
677
665
|
// Update isUpToDate
|
|
678
666
|
if (batch.length > 0) {
|
|
679
667
|
const lastMessage = batch[batch.length - 1]
|
|
@@ -738,8 +726,9 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
738
726
|
const schema = this.#schema! // we know that it is not undefined because it is set by `this.#onInitialResponse`
|
|
739
727
|
const res = await response.text()
|
|
740
728
|
const messages = res || `[]`
|
|
729
|
+
const batch = this.#messageParser.parse<Array<Message<T>>>(messages, schema)
|
|
741
730
|
|
|
742
|
-
await this.#onMessages(
|
|
731
|
+
await this.#onMessages(batch)
|
|
743
732
|
}
|
|
744
733
|
|
|
745
734
|
async #requestShapeSSE(opts: {
|
|
@@ -750,6 +739,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
750
739
|
const { fetchUrl, requestAbortController, headers } = opts
|
|
751
740
|
const fetch = this.#sseFetchClient
|
|
752
741
|
try {
|
|
742
|
+
let buffer: Array<Message<T>> = []
|
|
753
743
|
await fetchEventSource(fetchUrl.toString(), {
|
|
754
744
|
headers,
|
|
755
745
|
fetch,
|
|
@@ -759,11 +749,20 @@ export class ShapeStream<T extends Row<unknown> = Row>
|
|
|
759
749
|
},
|
|
760
750
|
onmessage: (event: EventSourceMessage) => {
|
|
761
751
|
if (event.data) {
|
|
762
|
-
//
|
|
763
|
-
// The event.data is a single JSON object, so we wrap it in an array
|
|
764
|
-
const messages = `[${event.data}]`
|
|
752
|
+
// event.data is a single JSON object
|
|
765
753
|
const schema = this.#schema! // we know that it is not undefined because it is set in onopen when we call this.#onInitialResponse
|
|
766
|
-
this.#
|
|
754
|
+
const message = this.#messageParser.parse<Message<T>>(
|
|
755
|
+
event.data,
|
|
756
|
+
schema
|
|
757
|
+
)
|
|
758
|
+
buffer.push(message)
|
|
759
|
+
|
|
760
|
+
if (isUpToDateMessage(message)) {
|
|
761
|
+
// Flush the buffer on up-to-date message.
|
|
762
|
+
// Ensures that we only process complete batches of operations.
|
|
763
|
+
this.#onMessages(buffer, true)
|
|
764
|
+
buffer = []
|
|
765
|
+
}
|
|
767
766
|
}
|
|
768
767
|
},
|
|
769
768
|
onerror: (error: Error) => {
|
package/src/fetch.ts
CHANGED
|
@@ -38,8 +38,7 @@ export const BackoffDefaults = {
|
|
|
38
38
|
|
|
39
39
|
export function createFetchWithBackoff(
|
|
40
40
|
fetchClient: typeof fetch,
|
|
41
|
-
backoffOptions: BackoffOptions = BackoffDefaults
|
|
42
|
-
sseMode: boolean = false
|
|
41
|
+
backoffOptions: BackoffOptions = BackoffDefaults
|
|
43
42
|
): typeof fetch {
|
|
44
43
|
const {
|
|
45
44
|
initialDelay,
|
|
@@ -67,12 +66,6 @@ export function createFetchWithBackoff(
|
|
|
67
66
|
if (result.ok) return result
|
|
68
67
|
|
|
69
68
|
const err = await FetchError.fromResponse(result, url.toString())
|
|
70
|
-
if (err.status === 409 && sseMode) {
|
|
71
|
-
// The json body is [ { headers: { control: 'must-refetch' } } ] in normal mode
|
|
72
|
-
// and is { headers: { control: 'must-refetch' } } in SSE mode
|
|
73
|
-
// So in SSE mode we need to wrap it in an array
|
|
74
|
-
err.json = [err.json]
|
|
75
|
-
}
|
|
76
69
|
|
|
77
70
|
throw err
|
|
78
71
|
} catch (e) {
|
package/src/helpers.ts
CHANGED
|
@@ -58,8 +58,9 @@ export function isUpToDateMessage<T extends Row<unknown> = Row>(
|
|
|
58
58
|
* If we are not in SSE mode this function will return undefined.
|
|
59
59
|
*/
|
|
60
60
|
export function getOffset(message: ControlMessage): Offset | undefined {
|
|
61
|
-
const lsn =
|
|
62
|
-
if (
|
|
63
|
-
return
|
|
61
|
+
const lsn = message.headers.global_last_seen_lsn
|
|
62
|
+
if (!lsn) {
|
|
63
|
+
return
|
|
64
64
|
}
|
|
65
|
+
return `${lsn}_0` as Offset
|
|
65
66
|
}
|
package/src/parser.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { ColumnInfo, GetExtensions,
|
|
1
|
+
import { ColumnInfo, GetExtensions, Row, Schema, Value } from './types'
|
|
2
2
|
import { ParserNullValueError } from './error'
|
|
3
3
|
|
|
4
|
-
type
|
|
5
|
-
type
|
|
6
|
-
type NullableToken = Token | NullToken
|
|
4
|
+
type Token = string
|
|
5
|
+
type NullableToken = Token | null
|
|
7
6
|
export type ParseFunction<Extensions = never> = (
|
|
8
7
|
value: Token,
|
|
9
8
|
additionalInfo?: Omit<ColumnInfo, `type` | `dims`>
|
|
@@ -40,7 +39,7 @@ export const defaultParser: Parser = {
|
|
|
40
39
|
// Taken from: https://github.com/electric-sql/pglite/blob/main/packages/pglite/src/types.ts#L233-L279
|
|
41
40
|
export function pgArrayParser<Extensions>(
|
|
42
41
|
value: Token,
|
|
43
|
-
parser?:
|
|
42
|
+
parser?: NullableParseFunction<Extensions>
|
|
44
43
|
): Value<Extensions> {
|
|
45
44
|
let i = 0
|
|
46
45
|
let char = null
|
|
@@ -49,6 +48,12 @@ export function pgArrayParser<Extensions>(
|
|
|
49
48
|
let last = 0
|
|
50
49
|
let p: string | undefined = undefined
|
|
51
50
|
|
|
51
|
+
function extractValue(x: Token, start: number, end: number) {
|
|
52
|
+
let val: Token | null = x.slice(start, end)
|
|
53
|
+
val = val === `NULL` ? null : val
|
|
54
|
+
return parser ? parser(val) : val
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
function loop(x: string): Array<Value<Extensions>> {
|
|
53
58
|
const xs = []
|
|
54
59
|
for (; i < x.length; i++) {
|
|
@@ -71,18 +76,16 @@ export function pgArrayParser<Extensions>(
|
|
|
71
76
|
xs.push(loop(x))
|
|
72
77
|
} else if (char === `}`) {
|
|
73
78
|
quoted = false
|
|
74
|
-
last < i &&
|
|
75
|
-
xs.push(parser ? parser(x.slice(last, i)) : x.slice(last, i))
|
|
79
|
+
last < i && xs.push(extractValue(x, last, i))
|
|
76
80
|
last = i + 1
|
|
77
81
|
break
|
|
78
82
|
} else if (char === `,` && p !== `}` && p !== `"`) {
|
|
79
|
-
xs.push(
|
|
83
|
+
xs.push(extractValue(x, last, i))
|
|
80
84
|
last = i + 1
|
|
81
85
|
}
|
|
82
86
|
p = char
|
|
83
87
|
}
|
|
84
|
-
last < i &&
|
|
85
|
-
xs.push(parser ? parser(x.slice(last, i + 1)) : x.slice(last, i + 1))
|
|
88
|
+
last < i && xs.push(xs.push(extractValue(x, last, i + 1)))
|
|
86
89
|
return xs
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -98,7 +101,7 @@ export class MessageParser<T extends Row<unknown>> {
|
|
|
98
101
|
this.parser = { ...defaultParser, ...parser }
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
parse(messages: string, schema: Schema):
|
|
104
|
+
parse<Result>(messages: string, schema: Schema): Result {
|
|
102
105
|
return JSON.parse(messages, (key, value) => {
|
|
103
106
|
// typeof value === `object` && value !== null
|
|
104
107
|
// is needed because there could be a column named `value`
|
|
@@ -117,7 +120,7 @@ export class MessageParser<T extends Row<unknown>> {
|
|
|
117
120
|
})
|
|
118
121
|
}
|
|
119
122
|
return value
|
|
120
|
-
}) as
|
|
123
|
+
}) as Result
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
// Parses the message values using the provided parser based on the schema information
|
|
@@ -166,7 +169,7 @@ function makeNullableParser<Extensions>(
|
|
|
166
169
|
// but if the column value is an array that contains a NULL value
|
|
167
170
|
// then it will be included in the array string as `NULL`, e.g.: `"{1,NULL,3}"`
|
|
168
171
|
return (value: NullableToken) => {
|
|
169
|
-
if (
|
|
172
|
+
if (value === null) {
|
|
170
173
|
if (!isNullable) {
|
|
171
174
|
throw new ParserNullValueError(columnName ?? `unknown`)
|
|
172
175
|
}
|
|
@@ -175,7 +178,3 @@ function makeNullableParser<Extensions>(
|
|
|
175
178
|
return parser(value, columnInfo)
|
|
176
179
|
}
|
|
177
180
|
}
|
|
178
|
-
|
|
179
|
-
function isPgNull(value: NullableToken): value is NullToken {
|
|
180
|
-
return value === null || value === `NULL`
|
|
181
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type Row<Extensions = never> = Record<string, Value<Extensions>>
|
|
|
17
17
|
export type GetExtensions<T extends Row<unknown>> =
|
|
18
18
|
T extends Row<infer Extensions> ? Extensions : never
|
|
19
19
|
|
|
20
|
-
export type Offset = `-1` | `${number}_${number}`
|
|
20
|
+
export type Offset = `-1` | `${number}_${number}` | `${bigint}_${number}`
|
|
21
21
|
|
|
22
22
|
interface Header {
|
|
23
23
|
[key: Exclude<string, `operation` | `control`>]: Value
|