@livestore/livestore 0.0.54-dev.26 → 0.0.54-dev.28
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/QueryCache.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +5 -15
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +2 -2
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +15 -8
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +15 -16
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +1 -1
- package/dist/effect/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/react/LiveStoreContext.d.ts +5 -2
- package/dist/react/LiveStoreContext.d.ts.map +1 -1
- package/dist/react/LiveStoreContext.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +3 -2
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +63 -39
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/LiveStoreProvider.test.js +28 -9
- package/dist/react/LiveStoreProvider.test.js.map +1 -1
- package/dist/react/components/LiveList.d.ts.map +1 -1
- package/dist/react/useAtom.d.ts +1 -1
- package/dist/react/useAtom.d.ts.map +1 -1
- package/dist/react/useLocalId.d.ts.map +1 -1
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js +2 -2
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/useRow.d.ts.map +1 -1
- package/dist/react/useRow.js +2 -2
- package/dist/react/useRow.js.map +1 -1
- package/dist/react/useRow.test.js +1 -1
- package/dist/react/useRow.test.js.map +1 -1
- package/dist/react/useTemporaryQuery.d.ts.map +1 -1
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
- package/dist/reactive.d.ts +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +3 -4
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.test.js +6 -6
- package/dist/reactiveQueries/sql.test.js.map +1 -1
- package/dist/store.d.ts +12 -5
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +59 -122
- package/dist/store.js.map +1 -1
- package/dist/utils/otel.d.ts.map +1 -1
- package/package.json +8 -10
- package/src/__tests__/react/fixture.tsx +2 -2
- package/src/effect/LiveStore.ts +50 -43
- package/src/effect/index.ts +2 -1
- package/src/index.ts +7 -3
- package/src/react/LiveStoreContext.ts +3 -2
- package/src/react/LiveStoreProvider.test.tsx +47 -10
- package/src/react/LiveStoreProvider.tsx +95 -38
- package/src/react/useQuery.ts +2 -2
- package/src/react/useRow.test.tsx +1 -1
- package/src/react/useRow.ts +2 -5
- package/src/reactive.ts +3 -4
- package/src/reactiveQueries/sql.test.ts +6 -6
- package/src/store.ts +243 -286
package/src/store.ts
CHANGED
|
@@ -6,14 +6,31 @@ import type {
|
|
|
6
6
|
ResetMode,
|
|
7
7
|
StoreAdapter,
|
|
8
8
|
StoreAdapterFactory,
|
|
9
|
+
} from '@livestore/common'
|
|
10
|
+
import {
|
|
11
|
+
Devtools,
|
|
12
|
+
getExecArgsFromMutation,
|
|
13
|
+
liveStoreVersion,
|
|
14
|
+
prepareBindValues,
|
|
9
15
|
UnexpectedError,
|
|
10
16
|
} from '@livestore/common'
|
|
11
|
-
import { Devtools, getExecArgsFromMutation, liveStoreVersion, prepareBindValues } from '@livestore/common'
|
|
12
17
|
import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
|
|
13
18
|
import { makeMutationEventSchemaMemo } from '@livestore/common/schema'
|
|
14
19
|
import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen, throttle } from '@livestore/utils'
|
|
15
20
|
import { cuid } from '@livestore/utils/cuid'
|
|
16
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
Effect,
|
|
23
|
+
Exit,
|
|
24
|
+
Layer,
|
|
25
|
+
Logger,
|
|
26
|
+
LogLevel,
|
|
27
|
+
OtelTracer,
|
|
28
|
+
Queue,
|
|
29
|
+
Runtime,
|
|
30
|
+
Schema,
|
|
31
|
+
Scope,
|
|
32
|
+
Stream,
|
|
33
|
+
} from '@livestore/utils/effect'
|
|
17
34
|
import * as otel from '@opentelemetry/api'
|
|
18
35
|
import type { GraphQLSchema } from 'graphql'
|
|
19
36
|
|
|
@@ -53,6 +70,7 @@ export type StoreOptions<
|
|
|
53
70
|
otelOptions: OtelOptions
|
|
54
71
|
reactivityGraph: ReactivityGraph
|
|
55
72
|
disableDevtools?: boolean
|
|
73
|
+
storeScope: Scope.CloseableScope
|
|
56
74
|
// TODO remove this temporary solution and find a better way to avoid re-processing the same mutation
|
|
57
75
|
__processedMutationIds: Set<string>
|
|
58
76
|
}
|
|
@@ -110,6 +128,7 @@ export class Store<
|
|
|
110
128
|
> {
|
|
111
129
|
id = uniqueStoreId()
|
|
112
130
|
readonly devtoolsConnectionId = cuid()
|
|
131
|
+
private storeScope: Scope.CloseableScope
|
|
113
132
|
reactivityGraph: ReactivityGraph
|
|
114
133
|
mainDbWrapper: MainDatabaseWrapper
|
|
115
134
|
adapter: StoreAdapter
|
|
@@ -140,11 +159,14 @@ export class Store<
|
|
|
140
159
|
otelOptions,
|
|
141
160
|
disableDevtools,
|
|
142
161
|
__processedMutationIds,
|
|
162
|
+
storeScope,
|
|
143
163
|
}: StoreOptions<TGraphQLContext, TSchema>) {
|
|
144
164
|
this.mainDbWrapper = new MainDatabaseWrapper({ otel: otelOptions, db: adapter.mainDb })
|
|
145
165
|
this.adapter = adapter
|
|
146
166
|
this.schema = schema
|
|
147
167
|
|
|
168
|
+
this.storeScope = storeScope
|
|
169
|
+
|
|
148
170
|
// TODO refactor
|
|
149
171
|
this.__mutationEventSchema = makeMutationEventSchemaMemo(schema)
|
|
150
172
|
|
|
@@ -168,18 +190,6 @@ export class Store<
|
|
|
168
190
|
rootOtelContext: otelQueriesSpanContext,
|
|
169
191
|
}
|
|
170
192
|
|
|
171
|
-
this.adapter.coordinator.syncMutations.pipe(
|
|
172
|
-
Stream.tapSync((mutationEventDecoded) => {
|
|
173
|
-
this.mutate({ wasSyncMessage: true }, mutationEventDecoded)
|
|
174
|
-
}),
|
|
175
|
-
Stream.runDrain,
|
|
176
|
-
runEffectFork,
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
if (disableDevtools !== true) {
|
|
180
|
-
this.bootDevtools()
|
|
181
|
-
}
|
|
182
|
-
|
|
183
193
|
this.otel = {
|
|
184
194
|
tracer: otelOptions.tracer,
|
|
185
195
|
mutationsSpanContext: otelMuationsSpanContext,
|
|
@@ -201,6 +211,34 @@ export class Store<
|
|
|
201
211
|
this.graphQLSchema = graphQLOptions.schema
|
|
202
212
|
this.graphQLContext = graphQLOptions.makeContext(this.mainDbWrapper, this.otel.tracer)
|
|
203
213
|
}
|
|
214
|
+
|
|
215
|
+
Effect.gen(this, function* () {
|
|
216
|
+
yield* this.adapter.coordinator.syncMutations.pipe(
|
|
217
|
+
Stream.tapSync((mutationEventDecoded) => {
|
|
218
|
+
this.mutate({ wasSyncMessage: true }, mutationEventDecoded)
|
|
219
|
+
}),
|
|
220
|
+
Stream.runDrain,
|
|
221
|
+
Effect.withSpan('LiveStore:syncMutations'),
|
|
222
|
+
Effect.forkScoped,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if (disableDevtools !== true) {
|
|
226
|
+
yield* this.bootDevtools().pipe(Effect.forkScoped)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
yield* Effect.addFinalizer(() =>
|
|
230
|
+
Effect.sync(() => {
|
|
231
|
+
for (const tableRef of Object.values(this.tableRefs)) {
|
|
232
|
+
for (const superComp of tableRef.super) {
|
|
233
|
+
this.reactivityGraph.removeEdge(superComp, tableRef)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
otel.trace.getSpan(this.otel.mutationsSpanContext)!.end()
|
|
238
|
+
otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
|
|
239
|
+
}),
|
|
240
|
+
)
|
|
241
|
+
}).pipe(Scope.extend(storeScope), Effect.forkIn(storeScope), Effect.scoped, runEffectFork)
|
|
204
242
|
}
|
|
205
243
|
|
|
206
244
|
static createStore = <TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema = LiveStoreSchema>(
|
|
@@ -266,16 +304,7 @@ export class Store<
|
|
|
266
304
|
* Currently only used when shutting down the app for debugging purposes (e.g. to close Otel spans).
|
|
267
305
|
*/
|
|
268
306
|
destroy = async () => {
|
|
269
|
-
|
|
270
|
-
for (const superComp of tableRef.super) {
|
|
271
|
-
this.reactivityGraph.removeEdge(superComp, tableRef)
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
otel.trace.getSpan(this.otel.mutationsSpanContext)!.end()
|
|
276
|
-
otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
|
|
277
|
-
|
|
278
|
-
await this.adapter.coordinator.shutdown.pipe(runEffectPromise)
|
|
307
|
+
await Scope.close(this.storeScope, Exit.void).pipe(Effect.withSpan('Store:destroy'), runEffectPromise)
|
|
279
308
|
}
|
|
280
309
|
|
|
281
310
|
mutate: {
|
|
@@ -335,6 +364,8 @@ export class Store<
|
|
|
335
364
|
// mutationsEvents.forEach((_) => console.log(_.mutation, _.id, _.args))
|
|
336
365
|
// console.groupEnd()
|
|
337
366
|
|
|
367
|
+
let durationMs: number
|
|
368
|
+
|
|
338
369
|
return this.otel.tracer.startActiveSpan(
|
|
339
370
|
'LiveStore:mutate',
|
|
340
371
|
{ attributes: { 'livestore.mutateLabel': label } },
|
|
@@ -365,8 +396,8 @@ export class Store<
|
|
|
365
396
|
writeTables.add(tableName)
|
|
366
397
|
}
|
|
367
398
|
} catch (e: any) {
|
|
368
|
-
debugger
|
|
369
399
|
console.error(e, mutationEvent)
|
|
400
|
+
throw e
|
|
370
401
|
}
|
|
371
402
|
}
|
|
372
403
|
}
|
|
@@ -380,6 +411,7 @@ export class Store<
|
|
|
380
411
|
} catch (e: any) {
|
|
381
412
|
console.error(e)
|
|
382
413
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
414
|
+
throw e
|
|
383
415
|
} finally {
|
|
384
416
|
span.end()
|
|
385
417
|
}
|
|
@@ -402,12 +434,16 @@ export class Store<
|
|
|
402
434
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
403
435
|
this.reactivityGraph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext, skipRefresh })
|
|
404
436
|
} catch (e: any) {
|
|
437
|
+
console.error(e)
|
|
405
438
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
439
|
+
throw e
|
|
406
440
|
} finally {
|
|
407
441
|
span.end()
|
|
408
442
|
|
|
409
|
-
|
|
443
|
+
durationMs = getDurationMsFromSpan(span)
|
|
410
444
|
}
|
|
445
|
+
|
|
446
|
+
return { durationMs }
|
|
411
447
|
},
|
|
412
448
|
)
|
|
413
449
|
}
|
|
@@ -535,268 +571,166 @@ export class Store<
|
|
|
535
571
|
})
|
|
536
572
|
|
|
537
573
|
// TODO shutdown behaviour
|
|
538
|
-
private bootDevtools = () =>
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
574
|
+
private bootDevtools = () =>
|
|
575
|
+
Effect.gen(this, function* () {
|
|
576
|
+
const sendToDevtoolsContentscript = (
|
|
577
|
+
message: typeof Devtools.DevtoolsWindowMessage.MessageForContentscript.Type,
|
|
578
|
+
) => {
|
|
579
|
+
window.postMessage(Schema.encodeSync(Devtools.DevtoolsWindowMessage.MessageForContentscript)(message), '*')
|
|
580
|
+
}
|
|
544
581
|
|
|
545
|
-
|
|
582
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.LoadIframe.make({}))
|
|
546
583
|
|
|
547
|
-
|
|
548
|
-
const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data)
|
|
549
|
-
if (decodedMessageRes._tag === 'None') return
|
|
584
|
+
const channelId = this.adapter.coordinator.devtools.channelId
|
|
550
585
|
|
|
551
|
-
|
|
586
|
+
window.addEventListener('message', (event) => {
|
|
587
|
+
const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data)
|
|
588
|
+
if (decodedMessageRes._tag === 'None') return
|
|
552
589
|
|
|
553
|
-
|
|
554
|
-
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
555
|
-
return
|
|
556
|
-
}
|
|
590
|
+
const message = decodedMessageRes.value
|
|
557
591
|
|
|
558
|
-
|
|
592
|
+
if (message._tag === 'LSD.WindowMessage.ContentscriptListening') {
|
|
593
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
594
|
+
return
|
|
595
|
+
}
|
|
559
596
|
|
|
560
|
-
|
|
561
|
-
this.adapter.coordinator.devtools.connect({ port: message.port, connectionId: this.devtoolsConnectionId }).pipe(
|
|
562
|
-
Effect.tapSync(({ storeMessagePort }) => {
|
|
563
|
-
// console.log('storeMessagePort', storeMessagePort)
|
|
564
|
-
storeMessagePort.addEventListener('message', (event) => {
|
|
565
|
-
const decodedMessage = Schema.decodeUnknownSync(Devtools.MessageToAppHostStore)(event.data)
|
|
566
|
-
// console.log('storeMessagePort message', decodedMessage)
|
|
597
|
+
if (message.channelId !== channelId) return
|
|
567
598
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
599
|
+
if (message._tag === 'LSD.WindowMessage.MessagePortForStore') {
|
|
600
|
+
type Unsub = () => void
|
|
601
|
+
type RequestId = string
|
|
572
602
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
storeMessagePort.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message))
|
|
576
|
-
|
|
577
|
-
const requestIdleCallback = window.requestIdleCallback ?? ((cb: Function) => cb())
|
|
578
|
-
|
|
579
|
-
switch (decodedMessage._tag) {
|
|
580
|
-
case 'LSD.ReactivityGraphSubscribe': {
|
|
581
|
-
const includeResults = decodedMessage.includeResults
|
|
582
|
-
|
|
583
|
-
const send = () =>
|
|
584
|
-
// In order to not add more work to the current tick, we use requestIdleCallback
|
|
585
|
-
// to send the reactivity graph updates to the devtools
|
|
586
|
-
requestIdleCallback(
|
|
587
|
-
() =>
|
|
588
|
-
sendToDevtools(
|
|
589
|
-
Devtools.ReactivityGraphRes.make({
|
|
590
|
-
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
|
|
591
|
-
requestId,
|
|
592
|
-
liveStoreVersion,
|
|
593
|
-
}),
|
|
594
|
-
),
|
|
595
|
-
{ timeout: 500 },
|
|
596
|
-
)
|
|
597
|
-
|
|
598
|
-
send()
|
|
599
|
-
|
|
600
|
-
// In some cases, there can be A LOT of reactivity graph updates in a short period of time
|
|
601
|
-
// so we throttle the updates to avoid sending too much data
|
|
602
|
-
// This might need to be tweaked further and possibly be exposed to the user in some way.
|
|
603
|
-
const throttledSend = throttle(send, 20)
|
|
604
|
-
|
|
605
|
-
reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
606
|
-
|
|
607
|
-
break
|
|
608
|
-
}
|
|
609
|
-
case 'LSD.DebugInfoReq': {
|
|
610
|
-
sendToDevtools(
|
|
611
|
-
Devtools.DebugInfoRes.make({
|
|
612
|
-
debugInfo: this.mainDbWrapper.debugInfo,
|
|
613
|
-
requestId,
|
|
614
|
-
liveStoreVersion,
|
|
615
|
-
}),
|
|
616
|
-
)
|
|
617
|
-
break
|
|
618
|
-
}
|
|
619
|
-
case 'LSD.DebugInfoResetReq': {
|
|
620
|
-
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
621
|
-
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }))
|
|
622
|
-
break
|
|
623
|
-
}
|
|
624
|
-
case 'LSD.DebugInfoRerunQueryReq': {
|
|
625
|
-
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
626
|
-
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
627
|
-
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
|
|
628
|
-
break
|
|
629
|
-
}
|
|
630
|
-
case 'LSD.ReactivityGraphUnsubscribe': {
|
|
631
|
-
reactivityGraphSubcriptions.get(requestId)!()
|
|
632
|
-
break
|
|
633
|
-
}
|
|
634
|
-
case 'LSD.LiveQueriesSubscribe': {
|
|
635
|
-
const send = () =>
|
|
636
|
-
requestIdleCallback(
|
|
637
|
-
() =>
|
|
638
|
-
sendToDevtools(
|
|
639
|
-
Devtools.LiveQueriesRes.make({
|
|
640
|
-
liveQueries: [...this.activeQueries].map((q) => ({
|
|
641
|
-
_tag: q._tag,
|
|
642
|
-
id: q.id,
|
|
643
|
-
label: q.label,
|
|
644
|
-
runs: q.runs,
|
|
645
|
-
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
646
|
-
lastestResult:
|
|
647
|
-
q.results$.previousResult === NOT_REFRESHED_YET
|
|
648
|
-
? 'SYMBOL_NOT_REFRESHED_YET'
|
|
649
|
-
: q.results$.previousResult,
|
|
650
|
-
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
651
|
-
})),
|
|
652
|
-
requestId,
|
|
653
|
-
liveStoreVersion,
|
|
654
|
-
}),
|
|
655
|
-
),
|
|
656
|
-
{ timeout: 500 },
|
|
657
|
-
)
|
|
658
|
-
|
|
659
|
-
send()
|
|
660
|
-
|
|
661
|
-
// Same as in the reactivity graph subscription case above, we need to throttle the updates
|
|
662
|
-
const throttledSend = throttle(send, 20)
|
|
663
|
-
|
|
664
|
-
liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
665
|
-
|
|
666
|
-
break
|
|
667
|
-
}
|
|
668
|
-
case 'LSD.LiveQueriesUnsubscribe': {
|
|
669
|
-
liveQueriesSubscriptions.get(requestId)!()
|
|
670
|
-
break
|
|
671
|
-
}
|
|
672
|
-
// No default
|
|
673
|
-
}
|
|
674
|
-
})
|
|
603
|
+
const reactivityGraphSubcriptions = new Map<RequestId, Unsub>()
|
|
604
|
+
const liveQueriesSubscriptions = new Map<RequestId, Unsub>()
|
|
675
605
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
606
|
+
this.adapter.coordinator.devtools
|
|
607
|
+
.connect({ port: message.port, connectionId: this.devtoolsConnectionId })
|
|
608
|
+
.pipe(
|
|
609
|
+
Effect.tapSync(({ storeMessagePort }) => {
|
|
610
|
+
// console.log('storeMessagePort', storeMessagePort)
|
|
611
|
+
storeMessagePort.addEventListener('message', (event) => {
|
|
612
|
+
const decodedMessage = Schema.decodeUnknownSync(Devtools.MessageToAppHostStore)(event.data)
|
|
613
|
+
// console.log('storeMessagePort message', decodedMessage)
|
|
680
614
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
615
|
+
if (decodedMessage.channelId !== this.adapter.coordinator.devtools.channelId) {
|
|
616
|
+
// console.log(`Unknown message`, event)
|
|
617
|
+
return
|
|
618
|
+
}
|
|
684
619
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}
|
|
620
|
+
const requestId = decodedMessage.requestId
|
|
621
|
+
const sendToDevtools = (message: Devtools.MessageFromAppHostStore) =>
|
|
622
|
+
storeMessagePort.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message))
|
|
623
|
+
|
|
624
|
+
const requestIdleCallback = window.requestIdleCallback ?? ((cb: Function) => cb())
|
|
625
|
+
|
|
626
|
+
switch (decodedMessage._tag) {
|
|
627
|
+
case 'LSD.ReactivityGraphSubscribe': {
|
|
628
|
+
const includeResults = decodedMessage.includeResults
|
|
629
|
+
|
|
630
|
+
const send = () =>
|
|
631
|
+
// In order to not add more work to the current tick, we use requestIdleCallback
|
|
632
|
+
// to send the reactivity graph updates to the devtools
|
|
633
|
+
requestIdleCallback(
|
|
634
|
+
() =>
|
|
635
|
+
sendToDevtools(
|
|
636
|
+
Devtools.ReactivityGraphRes.make({
|
|
637
|
+
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
|
|
638
|
+
requestId,
|
|
639
|
+
liveStoreVersion,
|
|
640
|
+
}),
|
|
641
|
+
),
|
|
642
|
+
{ timeout: 500 },
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
send()
|
|
646
|
+
|
|
647
|
+
// In some cases, there can be A LOT of reactivity graph updates in a short period of time
|
|
648
|
+
// so we throttle the updates to avoid sending too much data
|
|
649
|
+
// This might need to be tweaked further and possibly be exposed to the user in some way.
|
|
650
|
+
const throttledSend = throttle(send, 20)
|
|
651
|
+
|
|
652
|
+
reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
653
|
+
|
|
654
|
+
break
|
|
655
|
+
}
|
|
656
|
+
case 'LSD.DebugInfoReq': {
|
|
657
|
+
sendToDevtools(
|
|
658
|
+
Devtools.DebugInfoRes.make({
|
|
659
|
+
debugInfo: this.mainDbWrapper.debugInfo,
|
|
660
|
+
requestId,
|
|
661
|
+
liveStoreVersion,
|
|
662
|
+
}),
|
|
663
|
+
)
|
|
664
|
+
break
|
|
665
|
+
}
|
|
666
|
+
case 'LSD.DebugInfoResetReq': {
|
|
667
|
+
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
668
|
+
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }))
|
|
669
|
+
break
|
|
670
|
+
}
|
|
671
|
+
case 'LSD.DebugInfoRerunQueryReq': {
|
|
672
|
+
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
673
|
+
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
674
|
+
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
|
|
675
|
+
break
|
|
676
|
+
}
|
|
677
|
+
case 'LSD.ReactivityGraphUnsubscribe': {
|
|
678
|
+
reactivityGraphSubcriptions.get(requestId)!()
|
|
679
|
+
break
|
|
680
|
+
}
|
|
681
|
+
case 'LSD.LiveQueriesSubscribe': {
|
|
682
|
+
const send = () =>
|
|
683
|
+
requestIdleCallback(
|
|
684
|
+
() =>
|
|
685
|
+
sendToDevtools(
|
|
686
|
+
Devtools.LiveQueriesRes.make({
|
|
687
|
+
liveQueries: [...this.activeQueries].map((q) => ({
|
|
688
|
+
_tag: q._tag,
|
|
689
|
+
id: q.id,
|
|
690
|
+
label: q.label,
|
|
691
|
+
runs: q.runs,
|
|
692
|
+
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
693
|
+
lastestResult:
|
|
694
|
+
q.results$.previousResult === NOT_REFRESHED_YET
|
|
695
|
+
? 'SYMBOL_NOT_REFRESHED_YET'
|
|
696
|
+
: q.results$.previousResult,
|
|
697
|
+
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
698
|
+
})),
|
|
699
|
+
requestId,
|
|
700
|
+
liveStoreVersion,
|
|
701
|
+
}),
|
|
702
|
+
),
|
|
703
|
+
{ timeout: 500 },
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
send()
|
|
707
|
+
|
|
708
|
+
// Same as in the reactivity graph subscription case above, we need to throttle the updates
|
|
709
|
+
const throttledSend = throttle(send, 20)
|
|
710
|
+
|
|
711
|
+
liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
712
|
+
|
|
713
|
+
break
|
|
714
|
+
}
|
|
715
|
+
case 'LSD.LiveQueriesUnsubscribe': {
|
|
716
|
+
liveQueriesSubscriptions.get(requestId)!()
|
|
717
|
+
break
|
|
718
|
+
}
|
|
719
|
+
// No default
|
|
720
|
+
}
|
|
721
|
+
})
|
|
722
|
+
|
|
723
|
+
storeMessagePort.start()
|
|
724
|
+
}),
|
|
725
|
+
runEffectFork,
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
return
|
|
729
|
+
}
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
733
|
+
})
|
|
800
734
|
|
|
801
735
|
__devDownloadDb = () => {
|
|
802
736
|
const data = this.mainDbWrapper.export()
|
|
@@ -825,14 +759,27 @@ export type CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSche
|
|
|
825
759
|
}
|
|
826
760
|
|
|
827
761
|
/** Create a new LiveStore Store */
|
|
828
|
-
export const
|
|
762
|
+
export const createStorePromise = async <
|
|
829
763
|
TGraphQLContext extends BaseGraphQLContext,
|
|
830
764
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
831
|
-
>(
|
|
832
|
-
|
|
833
|
-
|
|
765
|
+
>({
|
|
766
|
+
signal,
|
|
767
|
+
...options
|
|
768
|
+
}: CreateStoreOptions<TGraphQLContext, TSchema> & { signal?: AbortSignal }): Promise<Store<TGraphQLContext, TSchema>> =>
|
|
769
|
+
Effect.gen(function* () {
|
|
770
|
+
const scope = yield* Scope.make()
|
|
771
|
+
const runtime = yield* Effect.runtime()
|
|
772
|
+
|
|
773
|
+
if (signal !== undefined) {
|
|
774
|
+
signal.addEventListener('abort', () => {
|
|
775
|
+
Scope.close(scope, Exit.void).pipe(Effect.tapCauseLogPretty, Runtime.runFork(runtime))
|
|
776
|
+
})
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
return yield* createStore({ ...options, storeScope: scope }).pipe(Scope.extend(scope))
|
|
780
|
+
}).pipe(Effect.withSpan('createStore'), runEffectPromise)
|
|
834
781
|
|
|
835
|
-
export const
|
|
782
|
+
export const createStore = <
|
|
836
783
|
TGraphQLContext extends BaseGraphQLContext,
|
|
837
784
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
838
785
|
>({
|
|
@@ -845,7 +792,12 @@ export const createStoreEff = <
|
|
|
845
792
|
batchUpdates,
|
|
846
793
|
disableDevtools,
|
|
847
794
|
onBootStatus,
|
|
848
|
-
|
|
795
|
+
storeScope,
|
|
796
|
+
}: CreateStoreOptions<TGraphQLContext, TSchema> & { storeScope: Scope.CloseableScope }): Effect.Effect<
|
|
797
|
+
Store<TGraphQLContext, TSchema>,
|
|
798
|
+
UnexpectedError,
|
|
799
|
+
Scope.Scope
|
|
800
|
+
> => {
|
|
849
801
|
const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
|
|
850
802
|
const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
|
|
851
803
|
|
|
@@ -869,6 +821,7 @@ export const createStoreEff = <
|
|
|
869
821
|
schema,
|
|
870
822
|
devtoolsEnabled: disableDevtools !== true,
|
|
871
823
|
bootStatusQueue,
|
|
824
|
+
shutdown: (cause) => Scope.close(storeScope, Exit.failCause(cause)),
|
|
872
825
|
}).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
|
|
873
826
|
|
|
874
827
|
if (batchUpdates !== undefined) {
|
|
@@ -946,10 +899,13 @@ export const createStoreEff = <
|
|
|
946
899
|
},
|
|
947
900
|
}
|
|
948
901
|
|
|
949
|
-
const booting =
|
|
902
|
+
const booting = yield* Effect.try({
|
|
903
|
+
try: () => boot(bootDbImpl, span),
|
|
904
|
+
catch: (cause) => new UnexpectedError({ cause }),
|
|
905
|
+
})
|
|
950
906
|
// NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
|
|
951
907
|
if (isPromise(booting)) {
|
|
952
|
-
yield* Effect.
|
|
908
|
+
yield* Effect.tryPromise({ try: () => booting, catch: (cause) => new UnexpectedError({ cause }) })
|
|
953
909
|
}
|
|
954
910
|
}
|
|
955
911
|
|
|
@@ -965,11 +921,12 @@ export const createStoreEff = <
|
|
|
965
921
|
reactivityGraph,
|
|
966
922
|
disableDevtools,
|
|
967
923
|
__processedMutationIds,
|
|
924
|
+
storeScope,
|
|
968
925
|
},
|
|
969
926
|
span,
|
|
970
927
|
)
|
|
971
928
|
}).pipe(
|
|
972
|
-
Effect.scoped,
|
|
929
|
+
// Effect.scoped,
|
|
973
930
|
Effect.withSpan('createStore', {
|
|
974
931
|
parent: otelOptions?.rootSpanContext
|
|
975
932
|
? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext)!)
|