@livestore/livestore 0.0.55-dev.1 → 0.0.55-dev.3
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 +2 -0
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +11 -1
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +45 -29
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +3 -0
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +3 -0
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/store-devtools.d.ts +18 -0
- package/dist/store-devtools.d.ts.map +1 -0
- package/dist/store-devtools.js +141 -0
- package/dist/store-devtools.js.map +1 -0
- package/dist/store.d.ts +12 -12
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +44 -211
- package/dist/store.js.map +1 -1
- package/dist/utils/data-structures.d.ts +10 -0
- package/dist/utils/data-structures.d.ts.map +1 -0
- package/dist/utils/data-structures.js +32 -0
- package/dist/utils/data-structures.js.map +1 -0
- package/package.json +5 -5
- package/src/react/LiveStoreProvider.tsx +52 -32
- package/src/reactiveQueries/sql.ts +3 -0
- package/src/store-devtools.ts +208 -0
- package/src/store.ts +67 -283
- package/src/utils/data-structures.ts +36 -0
package/src/store.ts
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
BootDb,
|
|
3
3
|
BootStatus,
|
|
4
|
-
DebugInfo,
|
|
5
4
|
ParamsObject,
|
|
6
5
|
PreparedBindValues,
|
|
7
6
|
ResetMode,
|
|
8
7
|
StoreAdapter,
|
|
9
8
|
StoreAdapterFactory,
|
|
10
9
|
} from '@livestore/common'
|
|
11
|
-
import {
|
|
12
|
-
Devtools,
|
|
13
|
-
getExecArgsFromMutation,
|
|
14
|
-
liveStoreVersion,
|
|
15
|
-
prepareBindValues,
|
|
16
|
-
UnexpectedError,
|
|
17
|
-
} from '@livestore/common'
|
|
10
|
+
import { getExecArgsFromMutation, prepareBindValues, UnexpectedError } from '@livestore/common'
|
|
18
11
|
import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
|
|
19
|
-
import { makeMutationEventSchemaMemo } from '@livestore/common/schema'
|
|
20
|
-
import { assertNever, makeNoopTracer, shouldNeverHappen
|
|
21
|
-
import {
|
|
12
|
+
import { makeMutationEventSchemaMemo, SCHEMA_META_TABLE, SCHEMA_MUTATIONS_META_TABLE } from '@livestore/common/schema'
|
|
13
|
+
import { assertNever, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
|
|
14
|
+
import type { Cause } from '@livestore/utils/effect'
|
|
22
15
|
import {
|
|
16
|
+
Deferred,
|
|
17
|
+
Duration,
|
|
23
18
|
Effect,
|
|
24
19
|
Exit,
|
|
25
20
|
FiberSet,
|
|
@@ -38,11 +33,12 @@ import * as otel from '@opentelemetry/api'
|
|
|
38
33
|
import type { GraphQLSchema } from 'graphql'
|
|
39
34
|
|
|
40
35
|
import { globalReactivityGraph } from './global-state.js'
|
|
41
|
-
import {
|
|
36
|
+
import { MainDatabaseWrapper } from './MainDatabaseWrapper.js'
|
|
42
37
|
import type { StackInfo } from './react/utils/stack-info.js'
|
|
43
38
|
import type { DebugRefreshReasonBase, Ref } from './reactive.js'
|
|
44
|
-
import { NOT_REFRESHED_YET } from './reactive.js'
|
|
45
39
|
import type { LiveQuery, QueryContext, ReactivityGraph } from './reactiveQueries/base-class.js'
|
|
40
|
+
import { connectDevtoolsToStore } from './store-devtools.js'
|
|
41
|
+
import { ReferenceCountedSet } from './utils/data-structures.js'
|
|
46
42
|
import { downloadBlob } from './utils/dev.js'
|
|
47
43
|
import { getDurationMsFromSpan } from './utils/otel.js'
|
|
48
44
|
|
|
@@ -62,6 +58,9 @@ export type OtelOptions = {
|
|
|
62
58
|
rootSpanContext: otel.Context
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
export class ForceStoreShutdown extends Schema.TaggedError<ForceStoreShutdown>()('LiveStore.ForceStoreShutdown', {}) {}
|
|
62
|
+
export class StoreShutdown extends Schema.TaggedError<StoreShutdown>()('LiveStore.StoreShutdown', {}) {}
|
|
63
|
+
|
|
65
64
|
export type StoreOptions<
|
|
66
65
|
TGraphQLContext extends BaseGraphQLContext,
|
|
67
66
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
@@ -130,7 +129,6 @@ export class Store<
|
|
|
130
129
|
TSchema extends LiveStoreSchema = LiveStoreSchema,
|
|
131
130
|
> extends Inspectable.Class {
|
|
132
131
|
id = uniqueStoreId()
|
|
133
|
-
readonly devtoolsConnectionId = cuid()
|
|
134
132
|
private fiberSet: FiberSet.FiberSet
|
|
135
133
|
reactivityGraph: ReactivityGraph
|
|
136
134
|
mainDbWrapper: MainDatabaseWrapper
|
|
@@ -154,6 +152,7 @@ export class Store<
|
|
|
154
152
|
|
|
155
153
|
readonly __mutationEventSchema
|
|
156
154
|
|
|
155
|
+
// #region constructor
|
|
157
156
|
private constructor({
|
|
158
157
|
adapter,
|
|
159
158
|
schema,
|
|
@@ -201,8 +200,20 @@ export class Store<
|
|
|
201
200
|
queriesSpanContext: otelQueriesSpanContext,
|
|
202
201
|
}
|
|
203
202
|
|
|
203
|
+
// TODO find a better way to detect if we're running LiveStore in the LiveStore devtools
|
|
204
|
+
// But for now this is a good enough approximation with little downsides
|
|
205
|
+
const isRunningInDevtools = disableDevtools === true
|
|
206
|
+
|
|
204
207
|
// Need a set here since `schema.tables` might contain duplicates and some componentStateTables
|
|
205
|
-
const allTableNames = new Set(
|
|
208
|
+
const allTableNames = new Set(
|
|
209
|
+
// NOTE we're excluding the LiveStore schema and mutations tables as they are not user-facing
|
|
210
|
+
// unless LiveStore is running in the devtools
|
|
211
|
+
isRunningInDevtools
|
|
212
|
+
? this.schema.tables.keys()
|
|
213
|
+
: Array.from(this.schema.tables.keys()).filter(
|
|
214
|
+
(_) => _ !== SCHEMA_META_TABLE && _ !== SCHEMA_MUTATIONS_META_TABLE,
|
|
215
|
+
),
|
|
216
|
+
)
|
|
206
217
|
const existingTableRefs = new Map(
|
|
207
218
|
Array.from(this.reactivityGraph.atoms.values())
|
|
208
219
|
.filter((_): _ is Ref<any, any, any> => _._tag === 'ref' && _.label?.startsWith('tableRef:') === true)
|
|
@@ -223,14 +234,11 @@ export class Store<
|
|
|
223
234
|
this.mutate({ wasSyncMessage: true }, mutationEventDecoded)
|
|
224
235
|
}),
|
|
225
236
|
Stream.runDrain,
|
|
237
|
+
Effect.interruptible,
|
|
226
238
|
Effect.withSpan('LiveStore:syncMutations'),
|
|
227
239
|
Effect.forkScoped,
|
|
228
240
|
)
|
|
229
241
|
|
|
230
|
-
if (disableDevtools !== true) {
|
|
231
|
-
yield* this.bootDevtools().pipe(Effect.forkScoped)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
242
|
yield* Effect.addFinalizer(() =>
|
|
235
243
|
Effect.sync(() => {
|
|
236
244
|
for (const tableRef of Object.values(this.tableRefs)) {
|
|
@@ -247,6 +255,7 @@ export class Store<
|
|
|
247
255
|
yield* Effect.never
|
|
248
256
|
}).pipe(Effect.scoped, Effect.withSpan('LiveStore:store-constructor'), FiberSet.run(fiberSet), runEffectFork)
|
|
249
257
|
}
|
|
258
|
+
// #endregion constructor
|
|
250
259
|
|
|
251
260
|
static createStore = <TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema = LiveStoreSchema>(
|
|
252
261
|
storeOptions: StoreOptions<TGraphQLContext, TSchema>,
|
|
@@ -314,6 +323,7 @@ export class Store<
|
|
|
314
323
|
await FiberSet.clear(this.fiberSet).pipe(Effect.withSpan('Store:destroy'), runEffectPromise)
|
|
315
324
|
}
|
|
316
325
|
|
|
326
|
+
// #region mutate
|
|
317
327
|
mutate: {
|
|
318
328
|
<const TMutationArg extends ReadonlyArray<MutationEvent.ForSchema<TSchema>>>(...list: TMutationArg): void
|
|
319
329
|
(
|
|
@@ -549,6 +559,7 @@ export class Store<
|
|
|
549
559
|
},
|
|
550
560
|
)
|
|
551
561
|
}
|
|
562
|
+
// #endregion mutate
|
|
552
563
|
|
|
553
564
|
/**
|
|
554
565
|
* Directly execute a SQL query on the Store.
|
|
@@ -577,226 +588,6 @@ export class Store<
|
|
|
577
588
|
meta: { liveStoreRefType: 'table' },
|
|
578
589
|
})
|
|
579
590
|
|
|
580
|
-
// #region devtools
|
|
581
|
-
// TODO shutdown behaviour
|
|
582
|
-
private bootDevtools = () =>
|
|
583
|
-
Effect.gen(this, function* () {
|
|
584
|
-
const sendToDevtoolsContentscript = (
|
|
585
|
-
message: typeof Devtools.DevtoolsWindowMessage.MessageForContentscript.Type,
|
|
586
|
-
) => {
|
|
587
|
-
window.postMessage(Schema.encodeSync(Devtools.DevtoolsWindowMessage.MessageForContentscript)(message), '*')
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.LoadIframe.make({}))
|
|
591
|
-
|
|
592
|
-
const channelId = this.adapter.coordinator.devtools.channelId
|
|
593
|
-
|
|
594
|
-
const runtime = yield* Effect.runtime()
|
|
595
|
-
|
|
596
|
-
window.addEventListener('message', (event) => {
|
|
597
|
-
const decodedMessageRes = Schema.decodeOption(Devtools.DevtoolsWindowMessage.MessageForStore)(event.data)
|
|
598
|
-
if (decodedMessageRes._tag === 'None') return
|
|
599
|
-
|
|
600
|
-
const message = decodedMessageRes.value
|
|
601
|
-
|
|
602
|
-
if (message._tag === 'LSD.WindowMessage.ContentscriptListening') {
|
|
603
|
-
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
604
|
-
return
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (message.channelId !== channelId) return
|
|
608
|
-
|
|
609
|
-
if (message._tag === 'LSD.WindowMessage.MessagePortForStore') {
|
|
610
|
-
type Unsub = () => void
|
|
611
|
-
type RequestId = string
|
|
612
|
-
|
|
613
|
-
const reactivityGraphSubcriptions = new Map<RequestId, Unsub>()
|
|
614
|
-
const liveQueriesSubscriptions = new Map<RequestId, Unsub>()
|
|
615
|
-
const debugInfoHistorySubscriptions = new Map<RequestId, Unsub>()
|
|
616
|
-
|
|
617
|
-
this.adapter.coordinator.devtools
|
|
618
|
-
.connect({ port: message.port, connectionId: this.devtoolsConnectionId })
|
|
619
|
-
.pipe(
|
|
620
|
-
Effect.tapSync(({ storeMessagePort }) => {
|
|
621
|
-
// console.log('storeMessagePort', storeMessagePort)
|
|
622
|
-
storeMessagePort.addEventListener('message', (event) => {
|
|
623
|
-
const decodedMessage = Schema.decodeUnknownSync(Devtools.MessageToAppHostStore)(event.data)
|
|
624
|
-
// console.log('storeMessagePort message', decodedMessage)
|
|
625
|
-
|
|
626
|
-
if (decodedMessage.channelId !== this.adapter.coordinator.devtools.channelId) {
|
|
627
|
-
// console.log(`Unknown message`, event)
|
|
628
|
-
return
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const requestId = decodedMessage.requestId
|
|
632
|
-
const sendToDevtools = (message: Devtools.MessageFromAppHostStore) =>
|
|
633
|
-
storeMessagePort.postMessage(Schema.encodeSync(Devtools.MessageFromAppHostStore)(message))
|
|
634
|
-
|
|
635
|
-
const requestIdleCallback = window.requestIdleCallback ?? ((cb: Function) => cb())
|
|
636
|
-
|
|
637
|
-
switch (decodedMessage._tag) {
|
|
638
|
-
case 'LSD.ReactivityGraphSubscribe': {
|
|
639
|
-
const includeResults = decodedMessage.includeResults
|
|
640
|
-
|
|
641
|
-
const send = () =>
|
|
642
|
-
// In order to not add more work to the current tick, we use requestIdleCallback
|
|
643
|
-
// to send the reactivity graph updates to the devtools
|
|
644
|
-
requestIdleCallback(
|
|
645
|
-
() =>
|
|
646
|
-
sendToDevtools(
|
|
647
|
-
Devtools.ReactivityGraphRes.make({
|
|
648
|
-
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
|
|
649
|
-
requestId,
|
|
650
|
-
channelId,
|
|
651
|
-
liveStoreVersion,
|
|
652
|
-
}),
|
|
653
|
-
),
|
|
654
|
-
{ timeout: 500 },
|
|
655
|
-
)
|
|
656
|
-
|
|
657
|
-
send()
|
|
658
|
-
|
|
659
|
-
// In some cases, there can be A LOT of reactivity graph updates in a short period of time
|
|
660
|
-
// so we throttle the updates to avoid sending too much data
|
|
661
|
-
// This might need to be tweaked further and possibly be exposed to the user in some way.
|
|
662
|
-
const throttledSend = throttle(send, 20)
|
|
663
|
-
|
|
664
|
-
reactivityGraphSubcriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
665
|
-
|
|
666
|
-
break
|
|
667
|
-
}
|
|
668
|
-
case 'LSD.DebugInfoReq': {
|
|
669
|
-
sendToDevtools(
|
|
670
|
-
Devtools.DebugInfoRes.make({
|
|
671
|
-
debugInfo: this.mainDbWrapper.debugInfo,
|
|
672
|
-
requestId,
|
|
673
|
-
channelId,
|
|
674
|
-
liveStoreVersion,
|
|
675
|
-
}),
|
|
676
|
-
)
|
|
677
|
-
break
|
|
678
|
-
}
|
|
679
|
-
case 'LSD.DebugInfoHistorySubscribe': {
|
|
680
|
-
const buffer: DebugInfo[] = []
|
|
681
|
-
let hasStopped = false
|
|
682
|
-
let rafHandle: number | undefined
|
|
683
|
-
|
|
684
|
-
const tick = () => {
|
|
685
|
-
buffer.push(this.mainDbWrapper.debugInfo)
|
|
686
|
-
|
|
687
|
-
// NOTE this resets the debug info, so all other "readers" e.g. in other `requestAnimationFrame` loops,
|
|
688
|
-
// will get the empty debug info
|
|
689
|
-
// TODO We need to come up with a more graceful way to do this. Probably via a single global
|
|
690
|
-
// `requestAnimationFrame` loop that is passed in somehow.
|
|
691
|
-
this.mainDbWrapper.debugInfo = makeEmptyDebugInfo()
|
|
692
|
-
|
|
693
|
-
if (buffer.length > 10) {
|
|
694
|
-
sendToDevtools(
|
|
695
|
-
Devtools.DebugInfoHistoryRes.make({
|
|
696
|
-
debugInfoHistory: buffer,
|
|
697
|
-
requestId,
|
|
698
|
-
channelId,
|
|
699
|
-
liveStoreVersion,
|
|
700
|
-
}),
|
|
701
|
-
)
|
|
702
|
-
buffer.length = 0
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
if (hasStopped === false) {
|
|
706
|
-
rafHandle = requestAnimationFrame(tick)
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
rafHandle = requestAnimationFrame(tick)
|
|
711
|
-
|
|
712
|
-
const unsub = () => {
|
|
713
|
-
hasStopped = true
|
|
714
|
-
if (rafHandle !== undefined) {
|
|
715
|
-
cancelAnimationFrame(rafHandle)
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
debugInfoHistorySubscriptions.set(requestId, unsub)
|
|
720
|
-
|
|
721
|
-
break
|
|
722
|
-
}
|
|
723
|
-
case 'LSD.DebugInfoHistoryUnsubscribe': {
|
|
724
|
-
debugInfoHistorySubscriptions.get(requestId)!()
|
|
725
|
-
debugInfoHistorySubscriptions.delete(requestId)
|
|
726
|
-
break
|
|
727
|
-
}
|
|
728
|
-
case 'LSD.DebugInfoResetReq': {
|
|
729
|
-
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
730
|
-
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, channelId, liveStoreVersion }))
|
|
731
|
-
break
|
|
732
|
-
}
|
|
733
|
-
case 'LSD.DebugInfoRerunQueryReq': {
|
|
734
|
-
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
735
|
-
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
736
|
-
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, channelId, liveStoreVersion }))
|
|
737
|
-
break
|
|
738
|
-
}
|
|
739
|
-
case 'LSD.ReactivityGraphUnsubscribe': {
|
|
740
|
-
reactivityGraphSubcriptions.get(requestId)!()
|
|
741
|
-
break
|
|
742
|
-
}
|
|
743
|
-
case 'LSD.LiveQueriesSubscribe': {
|
|
744
|
-
const send = () =>
|
|
745
|
-
requestIdleCallback(
|
|
746
|
-
() =>
|
|
747
|
-
sendToDevtools(
|
|
748
|
-
Devtools.LiveQueriesRes.make({
|
|
749
|
-
liveQueries: [...this.activeQueries].map((q) => ({
|
|
750
|
-
_tag: q._tag,
|
|
751
|
-
id: q.id,
|
|
752
|
-
label: q.label,
|
|
753
|
-
runs: q.runs,
|
|
754
|
-
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
755
|
-
lastestResult:
|
|
756
|
-
q.results$.previousResult === NOT_REFRESHED_YET
|
|
757
|
-
? 'SYMBOL_NOT_REFRESHED_YET'
|
|
758
|
-
: q.results$.previousResult,
|
|
759
|
-
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
760
|
-
})),
|
|
761
|
-
requestId,
|
|
762
|
-
liveStoreVersion,
|
|
763
|
-
channelId,
|
|
764
|
-
}),
|
|
765
|
-
),
|
|
766
|
-
{ timeout: 500 },
|
|
767
|
-
)
|
|
768
|
-
|
|
769
|
-
send()
|
|
770
|
-
|
|
771
|
-
// Same as in the reactivity graph subscription case above, we need to throttle the updates
|
|
772
|
-
const throttledSend = throttle(send, 20)
|
|
773
|
-
|
|
774
|
-
liveQueriesSubscriptions.set(requestId, this.reactivityGraph.subscribeToRefresh(throttledSend))
|
|
775
|
-
|
|
776
|
-
break
|
|
777
|
-
}
|
|
778
|
-
case 'LSD.LiveQueriesUnsubscribe': {
|
|
779
|
-
liveQueriesSubscriptions.get(requestId)!()
|
|
780
|
-
liveQueriesSubscriptions.delete(requestId)
|
|
781
|
-
break
|
|
782
|
-
}
|
|
783
|
-
// No default
|
|
784
|
-
}
|
|
785
|
-
})
|
|
786
|
-
|
|
787
|
-
storeMessagePort.start()
|
|
788
|
-
}),
|
|
789
|
-
Runtime.runFork(runtime),
|
|
790
|
-
)
|
|
791
|
-
|
|
792
|
-
return
|
|
793
|
-
}
|
|
794
|
-
})
|
|
795
|
-
|
|
796
|
-
sendToDevtoolsContentscript(Devtools.DevtoolsWindowMessage.StoreReady.make({ channelId }))
|
|
797
|
-
})
|
|
798
|
-
// #endregion devtools
|
|
799
|
-
|
|
800
591
|
__devDownloadDb = () => {
|
|
801
592
|
const data = this.mainDbWrapper.export()
|
|
802
593
|
downloadBlob(data, `livestore-${Date.now()}.db`)
|
|
@@ -810,6 +601,7 @@ export class Store<
|
|
|
810
601
|
// TODO allow for graceful store reset without requiring a full page reload (which should also call .boot)
|
|
811
602
|
dangerouslyResetStorage = (mode: ResetMode) => this.adapter.coordinator.dangerouslyReset(mode).pipe(runEffectPromise)
|
|
812
603
|
|
|
604
|
+
// NOTE This is needed because when booting a Store via Effect it seems to call `toJSON` in the error path
|
|
813
605
|
toJSON = () => {
|
|
814
606
|
return {
|
|
815
607
|
_tag: 'Store',
|
|
@@ -888,21 +680,44 @@ export const createStore = <
|
|
|
888
680
|
|
|
889
681
|
yield* Queue.take(bootStatusQueue).pipe(
|
|
890
682
|
Effect.tapSync((status) => onBootStatus?.(status)),
|
|
683
|
+
Effect.tap((status) => (status.stage === 'done' ? Queue.shutdown(bootStatusQueue) : Effect.void)),
|
|
891
684
|
Effect.forever,
|
|
892
685
|
Effect.tapCauseLogPretty,
|
|
893
686
|
Effect.forkScoped,
|
|
894
687
|
)
|
|
895
688
|
|
|
689
|
+
const storeDeferred = yield* Deferred.make<Store>()
|
|
690
|
+
|
|
691
|
+
const connectDevtoolsToStore_ = ({ storeMessagePort }: { storeMessagePort: MessagePort }) =>
|
|
692
|
+
Effect.gen(function* () {
|
|
693
|
+
const store = yield* Deferred.await(storeDeferred)
|
|
694
|
+
yield* connectDevtoolsToStore({ storeMessagePort, store })
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
// TODO close parent scope? (Needs refactor with Mike A)
|
|
698
|
+
const shutdown = (cause: Cause.Cause<unknown>) =>
|
|
699
|
+
Effect.gen(function* () {
|
|
700
|
+
yield* Effect.logWarning(`Shutting down LiveStore`, cause)
|
|
701
|
+
|
|
702
|
+
FiberSet.clear(fiberSet).pipe(
|
|
703
|
+
Effect.andThen(() => FiberSet.run(fiberSet, Effect.fail(StoreShutdown.make()))),
|
|
704
|
+
Effect.timeout(Duration.seconds(1)),
|
|
705
|
+
Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown:clear-fiber-set', duration: 500 }),
|
|
706
|
+
Effect.catchTag('TimeoutException', () =>
|
|
707
|
+
Effect.logWarning('Store shutdown timed out. Forcing shutdown.').pipe(
|
|
708
|
+
Effect.andThen(FiberSet.run(fiberSet, Effect.fail(ForceStoreShutdown.make()))),
|
|
709
|
+
),
|
|
710
|
+
),
|
|
711
|
+
runEffectFork, // NOTE we need to fork this separately otherwise it will also be interrupted
|
|
712
|
+
)
|
|
713
|
+
}).pipe(Effect.withSpan('livestore:shutdown'))
|
|
714
|
+
|
|
896
715
|
const adapter: StoreAdapter = yield* adapterFactory({
|
|
897
716
|
schema,
|
|
898
717
|
devtoolsEnabled: disableDevtools !== true,
|
|
899
718
|
bootStatusQueue,
|
|
900
|
-
shutdown
|
|
901
|
-
|
|
902
|
-
yield* Effect.logWarning(`Shutting down LiveStore`, cause)
|
|
903
|
-
// TODO close parent scope? (Needs refactor with Mike A)
|
|
904
|
-
yield* FiberSet.clear(fiberSet)
|
|
905
|
-
}).pipe(Effect.withSpan('livestore:shutdown')),
|
|
719
|
+
shutdown,
|
|
720
|
+
connectDevtoolsToStore: connectDevtoolsToStore_,
|
|
906
721
|
}).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
|
|
907
722
|
|
|
908
723
|
if (batchUpdates !== undefined) {
|
|
@@ -986,7 +801,7 @@ export const createStore = <
|
|
|
986
801
|
)
|
|
987
802
|
}
|
|
988
803
|
|
|
989
|
-
|
|
804
|
+
const store = Store.createStore<TGraphQLContext, TSchema>(
|
|
990
805
|
{
|
|
991
806
|
adapter,
|
|
992
807
|
schema,
|
|
@@ -999,6 +814,10 @@ export const createStore = <
|
|
|
999
814
|
},
|
|
1000
815
|
span,
|
|
1001
816
|
)
|
|
817
|
+
|
|
818
|
+
yield* Deferred.succeed(storeDeferred, store as any as Store)
|
|
819
|
+
|
|
820
|
+
return store
|
|
1002
821
|
}).pipe(
|
|
1003
822
|
Effect.withSpan('createStore', {
|
|
1004
823
|
parent: otelOptions?.rootSpanContext
|
|
@@ -1010,43 +829,7 @@ export const createStore = <
|
|
|
1010
829
|
}
|
|
1011
830
|
// #endregion createStore
|
|
1012
831
|
|
|
1013
|
-
// TODO
|
|
1014
|
-
class ReferenceCountedSet<T> {
|
|
1015
|
-
private map: Map<T, number>
|
|
1016
|
-
|
|
1017
|
-
constructor() {
|
|
1018
|
-
this.map = new Map<T, number>()
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
add = (key: T) => {
|
|
1022
|
-
const count = this.map.get(key) ?? 0
|
|
1023
|
-
this.map.set(key, count + 1)
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
remove = (key: T) => {
|
|
1027
|
-
const count = this.map.get(key) ?? 0
|
|
1028
|
-
if (count === 1) {
|
|
1029
|
-
this.map.delete(key)
|
|
1030
|
-
} else {
|
|
1031
|
-
this.map.set(key, count - 1)
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
has = (key: T) => {
|
|
1036
|
-
return this.map.has(key)
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
get size() {
|
|
1040
|
-
return this.map.size
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
*[Symbol.iterator]() {
|
|
1044
|
-
for (const key of this.map.keys()) {
|
|
1045
|
-
yield key
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
832
|
+
// TODO propagate runtime
|
|
1050
833
|
const runEffectFork = <A, E>(effect: Effect.Effect<A, E, never>) =>
|
|
1051
834
|
effect.pipe(
|
|
1052
835
|
Effect.tapCauseLogPretty,
|
|
@@ -1056,6 +839,7 @@ const runEffectFork = <A, E>(effect: Effect.Effect<A, E, never>) =>
|
|
|
1056
839
|
Effect.runFork,
|
|
1057
840
|
)
|
|
1058
841
|
|
|
842
|
+
// TODO propagate runtime
|
|
1059
843
|
const runEffectPromise = <A, E>(effect: Effect.Effect<A, E, never>) =>
|
|
1060
844
|
effect.pipe(
|
|
1061
845
|
Effect.tapCauseLogPretty,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// TODO consider replacing with Effect's RC data structures
|
|
2
|
+
export class ReferenceCountedSet<T> {
|
|
3
|
+
private map: Map<T, number>
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
this.map = new Map<T, number>()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
add = (key: T) => {
|
|
10
|
+
const count = this.map.get(key) ?? 0
|
|
11
|
+
this.map.set(key, count + 1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
remove = (key: T) => {
|
|
15
|
+
const count = this.map.get(key) ?? 0
|
|
16
|
+
if (count === 1) {
|
|
17
|
+
this.map.delete(key)
|
|
18
|
+
} else {
|
|
19
|
+
this.map.set(key, count - 1)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
has = (key: T) => {
|
|
24
|
+
return this.map.has(key)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get size() {
|
|
28
|
+
return this.map.size
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
*[Symbol.iterator]() {
|
|
32
|
+
for (const key of this.map.keys()) {
|
|
33
|
+
yield key
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|