@livestore/livestore 0.0.54-dev.26 → 0.0.54-dev.27
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/__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 +14 -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 +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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/useRow.test.js +1 -1
- package/dist/react/useRow.test.js.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 +11 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +52 -120
- package/dist/store.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/react/fixture.tsx +2 -2
- package/src/effect/LiveStore.ts +48 -41
- package/src/effect/index.ts +2 -1
- package/src/index.ts +6 -2
- 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/useRow.test.tsx +1 -1
- package/src/reactiveQueries/sql.test.ts +6 -6
- package/src/store.ts +234 -284
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: {
|
|
@@ -535,268 +564,166 @@ export class Store<
|
|
|
535
564
|
})
|
|
536
565
|
|
|
537
566
|
// TODO shutdown behaviour
|
|
538
|
-
private bootDevtools = () =>
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
567
|
+
private bootDevtools = () =>
|
|
568
|
+
Effect.gen(this, function* () {
|
|
569
|
+
const sendToDevtoolsContentscript = (
|
|
570
|
+
message: typeof Devtools.DevtoolsWindowMessage.MessageForContentscript.Type,
|
|
571
|
+
) => {
|
|
572
|
+
window.postMessage(Schema.encodeSync(Devtools.DevtoolsWindowMessage.MessageForContentscript)(message), '*')
|
|
573
|
+
}
|
|
544
574
|
|
|
545
|
-
|
|
575
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.LoadIframe.make({}))
|
|
546
576
|
|
|
547
|
-
|
|
548
|
-
const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data)
|
|
549
|
-
if (decodedMessageRes._tag === 'None') return
|
|
577
|
+
const channelId = this.adapter.coordinator.devtools.channelId
|
|
550
578
|
|
|
551
|
-
|
|
579
|
+
window.addEventListener('message', (event) => {
|
|
580
|
+
const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data)
|
|
581
|
+
if (decodedMessageRes._tag === 'None') return
|
|
552
582
|
|
|
553
|
-
|
|
554
|
-
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
555
|
-
return
|
|
556
|
-
}
|
|
583
|
+
const message = decodedMessageRes.value
|
|
557
584
|
|
|
558
|
-
|
|
585
|
+
if (message._tag === 'LSD.WindowMessage.ContentscriptListening') {
|
|
586
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
587
|
+
return
|
|
588
|
+
}
|
|
559
589
|
|
|
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)
|
|
590
|
+
if (message.channelId !== channelId) return
|
|
567
591
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
592
|
+
if (message._tag === 'LSD.WindowMessage.MessagePortForStore') {
|
|
593
|
+
type Unsub = () => void
|
|
594
|
+
type RequestId = string
|
|
572
595
|
|
|
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
|
-
})
|
|
596
|
+
const reactivityGraphSubcriptions = new Map<RequestId, Unsub>()
|
|
597
|
+
const liveQueriesSubscriptions = new Map<RequestId, Unsub>()
|
|
675
598
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
599
|
+
this.adapter.coordinator.devtools
|
|
600
|
+
.connect({ port: message.port, connectionId: this.devtoolsConnectionId })
|
|
601
|
+
.pipe(
|
|
602
|
+
Effect.tapSync(({ storeMessagePort }) => {
|
|
603
|
+
// console.log('storeMessagePort', storeMessagePort)
|
|
604
|
+
storeMessagePort.addEventListener('message', (event) => {
|
|
605
|
+
const decodedMessage = Schema.decodeUnknownSync(Devtools.MessageToAppHostStore)(event.data)
|
|
606
|
+
// console.log('storeMessagePort message', decodedMessage)
|
|
680
607
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
608
|
+
if (decodedMessage.channelId !== this.adapter.coordinator.devtools.channelId) {
|
|
609
|
+
// console.log(`Unknown message`, event)
|
|
610
|
+
return
|
|
611
|
+
}
|
|
684
612
|
|
|
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
|
-
}
|
|
613
|
+
const requestId = decodedMessage.requestId
|
|
614
|
+
const sendToDevtools = (message: Devtools.MessageFromAppHostStore) =>
|
|
615
|
+
storeMessagePort.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message))
|
|
616
|
+
|
|
617
|
+
const requestIdleCallback = window.requestIdleCallback ?? ((cb: Function) => cb())
|
|
618
|
+
|
|
619
|
+
switch (decodedMessage._tag) {
|
|
620
|
+
case 'LSD.ReactivityGraphSubscribe': {
|
|
621
|
+
const includeResults = decodedMessage.includeResults
|
|
622
|
+
|
|
623
|
+
const send = () =>
|
|
624
|
+
// In order to not add more work to the current tick, we use requestIdleCallback
|
|
625
|
+
// to send the reactivity graph updates to the devtools
|
|
626
|
+
requestIdleCallback(
|
|
627
|
+
() =>
|
|
628
|
+
sendToDevtools(
|
|
629
|
+
Devtools.ReactivityGraphRes.make({
|
|
630
|
+
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
|
|
631
|
+
requestId,
|
|
632
|
+
liveStoreVersion,
|
|
633
|
+
}),
|
|
634
|
+
),
|
|
635
|
+
{ timeout: 500 },
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
send()
|
|
639
|
+
|
|
640
|
+
// In some cases, there can be A LOT of reactivity graph updates in a short period of time
|
|
641
|
+
// so we throttle the updates to avoid sending too much data
|
|
642
|
+
// This might need to be tweaked further and possibly be exposed to the user in some way.
|
|
643
|
+
const throttledSend = throttle(send, 20)
|
|
644
|
+
|
|
645
|
+
reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
646
|
+
|
|
647
|
+
break
|
|
648
|
+
}
|
|
649
|
+
case 'LSD.DebugInfoReq': {
|
|
650
|
+
sendToDevtools(
|
|
651
|
+
Devtools.DebugInfoRes.make({
|
|
652
|
+
debugInfo: this.mainDbWrapper.debugInfo,
|
|
653
|
+
requestId,
|
|
654
|
+
liveStoreVersion,
|
|
655
|
+
}),
|
|
656
|
+
)
|
|
657
|
+
break
|
|
658
|
+
}
|
|
659
|
+
case 'LSD.DebugInfoResetReq': {
|
|
660
|
+
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
661
|
+
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }))
|
|
662
|
+
break
|
|
663
|
+
}
|
|
664
|
+
case 'LSD.DebugInfoRerunQueryReq': {
|
|
665
|
+
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
666
|
+
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
667
|
+
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
|
|
668
|
+
break
|
|
669
|
+
}
|
|
670
|
+
case 'LSD.ReactivityGraphUnsubscribe': {
|
|
671
|
+
reactivityGraphSubcriptions.get(requestId)!()
|
|
672
|
+
break
|
|
673
|
+
}
|
|
674
|
+
case 'LSD.LiveQueriesSubscribe': {
|
|
675
|
+
const send = () =>
|
|
676
|
+
requestIdleCallback(
|
|
677
|
+
() =>
|
|
678
|
+
sendToDevtools(
|
|
679
|
+
Devtools.LiveQueriesRes.make({
|
|
680
|
+
liveQueries: [...this.activeQueries].map((q) => ({
|
|
681
|
+
_tag: q._tag,
|
|
682
|
+
id: q.id,
|
|
683
|
+
label: q.label,
|
|
684
|
+
runs: q.runs,
|
|
685
|
+
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
686
|
+
lastestResult:
|
|
687
|
+
q.results$.previousResult === NOT_REFRESHED_YET
|
|
688
|
+
? 'SYMBOL_NOT_REFRESHED_YET'
|
|
689
|
+
: q.results$.previousResult,
|
|
690
|
+
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
691
|
+
})),
|
|
692
|
+
requestId,
|
|
693
|
+
liveStoreVersion,
|
|
694
|
+
}),
|
|
695
|
+
),
|
|
696
|
+
{ timeout: 500 },
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
send()
|
|
700
|
+
|
|
701
|
+
// Same as in the reactivity graph subscription case above, we need to throttle the updates
|
|
702
|
+
const throttledSend = throttle(send, 20)
|
|
703
|
+
|
|
704
|
+
liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
705
|
+
|
|
706
|
+
break
|
|
707
|
+
}
|
|
708
|
+
case 'LSD.LiveQueriesUnsubscribe': {
|
|
709
|
+
liveQueriesSubscriptions.get(requestId)!()
|
|
710
|
+
break
|
|
711
|
+
}
|
|
712
|
+
// No default
|
|
713
|
+
}
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
storeMessagePort.start()
|
|
717
|
+
}),
|
|
718
|
+
runEffectFork,
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
return
|
|
722
|
+
}
|
|
723
|
+
})
|
|
724
|
+
|
|
725
|
+
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
726
|
+
})
|
|
800
727
|
|
|
801
728
|
__devDownloadDb = () => {
|
|
802
729
|
const data = this.mainDbWrapper.export()
|
|
@@ -825,14 +752,27 @@ export type CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSche
|
|
|
825
752
|
}
|
|
826
753
|
|
|
827
754
|
/** Create a new LiveStore Store */
|
|
828
|
-
export const
|
|
755
|
+
export const createStorePromise = async <
|
|
829
756
|
TGraphQLContext extends BaseGraphQLContext,
|
|
830
757
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
831
|
-
>(
|
|
832
|
-
|
|
833
|
-
|
|
758
|
+
>({
|
|
759
|
+
signal,
|
|
760
|
+
...options
|
|
761
|
+
}: CreateStoreOptions<TGraphQLContext, TSchema> & { signal?: AbortSignal }): Promise<Store<TGraphQLContext, TSchema>> =>
|
|
762
|
+
Effect.gen(function* () {
|
|
763
|
+
const scope = yield* Scope.make()
|
|
764
|
+
const runtime = yield* Effect.runtime()
|
|
765
|
+
|
|
766
|
+
if (signal !== undefined) {
|
|
767
|
+
signal.addEventListener('abort', () => {
|
|
768
|
+
Scope.close(scope, Exit.void).pipe(Effect.tapCauseLogPretty, Runtime.runFork(runtime))
|
|
769
|
+
})
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return yield* createStore({ ...options, storeScope: scope }).pipe(Scope.extend(scope))
|
|
773
|
+
}).pipe(Effect.withSpan('createStore'), runEffectPromise)
|
|
834
774
|
|
|
835
|
-
export const
|
|
775
|
+
export const createStore = <
|
|
836
776
|
TGraphQLContext extends BaseGraphQLContext,
|
|
837
777
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
838
778
|
>({
|
|
@@ -845,7 +785,12 @@ export const createStoreEff = <
|
|
|
845
785
|
batchUpdates,
|
|
846
786
|
disableDevtools,
|
|
847
787
|
onBootStatus,
|
|
848
|
-
|
|
788
|
+
storeScope,
|
|
789
|
+
}: CreateStoreOptions<TGraphQLContext, TSchema> & { storeScope: Scope.CloseableScope }): Effect.Effect<
|
|
790
|
+
Store<TGraphQLContext, TSchema>,
|
|
791
|
+
UnexpectedError,
|
|
792
|
+
Scope.Scope
|
|
793
|
+
> => {
|
|
849
794
|
const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
|
|
850
795
|
const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
|
|
851
796
|
|
|
@@ -869,6 +814,7 @@ export const createStoreEff = <
|
|
|
869
814
|
schema,
|
|
870
815
|
devtoolsEnabled: disableDevtools !== true,
|
|
871
816
|
bootStatusQueue,
|
|
817
|
+
shutdown: (cause) => Scope.close(storeScope, Exit.failCause(cause)),
|
|
872
818
|
}).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
|
|
873
819
|
|
|
874
820
|
if (batchUpdates !== undefined) {
|
|
@@ -946,10 +892,13 @@ export const createStoreEff = <
|
|
|
946
892
|
},
|
|
947
893
|
}
|
|
948
894
|
|
|
949
|
-
const booting =
|
|
895
|
+
const booting = yield* Effect.try({
|
|
896
|
+
try: () => boot(bootDbImpl, span),
|
|
897
|
+
catch: (cause) => new UnexpectedError({ cause }),
|
|
898
|
+
})
|
|
950
899
|
// NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
|
|
951
900
|
if (isPromise(booting)) {
|
|
952
|
-
yield* Effect.
|
|
901
|
+
yield* Effect.tryPromise({ try: () => booting, catch: (cause) => new UnexpectedError({ cause }) })
|
|
953
902
|
}
|
|
954
903
|
}
|
|
955
904
|
|
|
@@ -965,11 +914,12 @@ export const createStoreEff = <
|
|
|
965
914
|
reactivityGraph,
|
|
966
915
|
disableDevtools,
|
|
967
916
|
__processedMutationIds,
|
|
917
|
+
storeScope,
|
|
968
918
|
},
|
|
969
919
|
span,
|
|
970
920
|
)
|
|
971
921
|
}).pipe(
|
|
972
|
-
Effect.scoped,
|
|
922
|
+
// Effect.scoped,
|
|
973
923
|
Effect.withSpan('createStore', {
|
|
974
924
|
parent: otelOptions?.rootSpanContext
|
|
975
925
|
? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext)!)
|