@livestore/livestore 0.0.53 → 0.0.54-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/MainDatabaseWrapper.d.ts +6 -21
- package/dist/MainDatabaseWrapper.d.ts.map +1 -1
- package/dist/MainDatabaseWrapper.js +30 -31
- package/dist/MainDatabaseWrapper.js.map +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +3 -1
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/effect/LiveStore.d.ts +3 -1
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +2 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +2 -1
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +5 -3
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/reactive.d.ts +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +25 -3
- package/dist/reactive.js.map +1 -1
- package/dist/store.d.ts +5 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +99 -8
- package/dist/store.js.map +1 -1
- package/dist/utils/util.d.ts +1 -1
- package/dist/utils/util.d.ts.map +1 -1
- package/dist/utils/util.js.map +1 -1
- package/package.json +5 -5
- package/src/MainDatabaseWrapper.ts +34 -50
- package/src/QueryCache.ts +2 -1
- package/src/effect/LiveStore.ts +4 -0
- package/src/index.ts +2 -2
- package/src/react/LiveStoreProvider.tsx +6 -1
- package/src/reactive.ts +7 -4
- package/src/store.ts +120 -6
- package/src/utils/util.ts +4 -2
- package/src/utils/bounded-collections.ts +0 -113
package/src/effect/LiveStore.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type LiveStoreCreateStoreOptions<GraphQLContext extends BaseGraphQLContex
|
|
|
25
25
|
boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
26
26
|
adapter: StoreAdapterFactory
|
|
27
27
|
batchUpdates?: (run: () => void) => void
|
|
28
|
+
disableDevtools?: boolean
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export const LiveStoreContext = Context.GenericTag<LiveStoreContext>('@livestore/livestore/LiveStoreContext')
|
|
@@ -44,6 +45,7 @@ export type LiveStoreContextProps<GraphQLContext extends BaseGraphQLContext> = {
|
|
|
44
45
|
}
|
|
45
46
|
boot?: (db: BootDb) => Effect.Effect<void>
|
|
46
47
|
adapter: StoreAdapterFactory
|
|
48
|
+
disableDevtools?: boolean
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export const LiveStoreContextLayer = <GraphQLContext extends BaseGraphQLContext>(
|
|
@@ -61,6 +63,7 @@ export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>(
|
|
|
61
63
|
graphQLOptions: graphQLOptions_,
|
|
62
64
|
boot: boot_,
|
|
63
65
|
adapter,
|
|
66
|
+
disableDevtools,
|
|
64
67
|
}: LiveStoreContextProps<GraphQLContext>): Effect.Effect<
|
|
65
68
|
LiveStoreContext,
|
|
66
69
|
never,
|
|
@@ -93,6 +96,7 @@ export const makeLiveStoreContext = <GraphQLContext extends BaseGraphQLContext>(
|
|
|
93
96
|
otelRootSpanContext,
|
|
94
97
|
boot,
|
|
95
98
|
adapter,
|
|
99
|
+
disableDevtools,
|
|
96
100
|
}),
|
|
97
101
|
),
|
|
98
102
|
Effect.acquireRelease((store) => Effect.sync(() => store.destroy())),
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { BaseGraphQLContext, QueryDebugInfo, RefreshReason } from './store.
|
|
|
3
3
|
|
|
4
4
|
export type { QueryDefinition, LiveStoreCreateStoreOptions, LiveStoreContext } from './effect/LiveStore.js'
|
|
5
5
|
|
|
6
|
-
export { MainDatabaseWrapper,
|
|
6
|
+
export { MainDatabaseWrapper, emptyDebugInfo } from './MainDatabaseWrapper.js'
|
|
7
7
|
|
|
8
8
|
export type {
|
|
9
9
|
GetAtom,
|
|
@@ -27,7 +27,7 @@ export { globalDbGraph, dynamicallyRegisteredTables } from './global-state.js'
|
|
|
27
27
|
export { type RowResult, type RowResultEncoded, rowQuery, deriveColQuery } from './row-query.js'
|
|
28
28
|
|
|
29
29
|
export * from '@livestore/common/schema'
|
|
30
|
-
export { sql, type BootDb, type InMemoryDatabase } from '@livestore/common'
|
|
30
|
+
export { sql, type BootDb, type InMemoryDatabase, type DebugInfo, type MutableDebugInfo } from '@livestore/common'
|
|
31
31
|
|
|
32
32
|
export { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
33
33
|
|
|
@@ -20,6 +20,7 @@ interface LiveStoreProviderProps<GraphQLContext> {
|
|
|
20
20
|
fallback: ReactElement
|
|
21
21
|
adapter: StoreAdapterFactory
|
|
22
22
|
batchUpdates?: (run: () => void) => void
|
|
23
|
+
disableDevtools?: boolean
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
@@ -32,6 +33,7 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
32
33
|
boot,
|
|
33
34
|
adapter,
|
|
34
35
|
batchUpdates,
|
|
36
|
+
disableDevtools,
|
|
35
37
|
}: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): JSX.Element => {
|
|
36
38
|
const storeCtx = useCreateStore({
|
|
37
39
|
schema,
|
|
@@ -41,6 +43,7 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
41
43
|
boot,
|
|
42
44
|
adapter,
|
|
43
45
|
batchUpdates,
|
|
46
|
+
disableDevtools,
|
|
44
47
|
})
|
|
45
48
|
|
|
46
49
|
if (storeCtx === undefined) {
|
|
@@ -60,6 +63,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
60
63
|
boot,
|
|
61
64
|
adapter,
|
|
62
65
|
batchUpdates,
|
|
66
|
+
disableDevtools,
|
|
63
67
|
}: LiveStoreCreateStoreOptions<GraphQLContext>) => {
|
|
64
68
|
const [_, rerender] = React.useState(0)
|
|
65
69
|
const ctxValueRef = React.useRef<StoreContext_ | undefined>(undefined)
|
|
@@ -110,6 +114,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
110
114
|
boot,
|
|
111
115
|
adapter,
|
|
112
116
|
batchUpdates,
|
|
117
|
+
disableDevtools,
|
|
113
118
|
})
|
|
114
119
|
ctxValueRef.current = { store }
|
|
115
120
|
oldStoreAlreadyDestroyedRef.current = false
|
|
@@ -124,7 +129,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
124
129
|
store?.destroy()
|
|
125
130
|
}
|
|
126
131
|
}
|
|
127
|
-
}, [schema, graphQLOptions, otelTracer, otelRootSpanContext, boot, adapter, batchUpdates])
|
|
132
|
+
}, [schema, graphQLOptions, otelTracer, otelRootSpanContext, boot, adapter, batchUpdates, disableDevtools])
|
|
128
133
|
|
|
129
134
|
return ctxValueRef.current
|
|
130
135
|
}
|
package/src/reactive.ts
CHANGED
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
|
25
25
|
|
|
26
|
+
import { BoundArray } from '@livestore/common'
|
|
26
27
|
import type { PrettifyFlat } from '@livestore/utils'
|
|
27
28
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
28
29
|
import type * as otel from '@opentelemetry/api'
|
|
29
30
|
import { isEqual } from 'lodash-es'
|
|
30
|
-
|
|
31
|
-
import { BoundArray } from './utils/bounded-collections.js'
|
|
32
31
|
// import { getDurationMsFromSpan } from './otel.js'
|
|
33
32
|
|
|
34
33
|
export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
|
|
@@ -520,7 +519,9 @@ export class ReactiveGraph<
|
|
|
520
519
|
superComp.sub.add(subComp)
|
|
521
520
|
subComp.super.add(superComp)
|
|
522
521
|
|
|
523
|
-
this.
|
|
522
|
+
if (this.currentDebugRefresh === undefined) {
|
|
523
|
+
this.runRefreshCallbacks()
|
|
524
|
+
}
|
|
524
525
|
}
|
|
525
526
|
|
|
526
527
|
removeEdge(
|
|
@@ -537,7 +538,9 @@ export class ReactiveGraph<
|
|
|
537
538
|
|
|
538
539
|
subComp.super.delete(superComp)
|
|
539
540
|
|
|
540
|
-
this.
|
|
541
|
+
if (this.currentDebugRefresh === undefined) {
|
|
542
|
+
this.runRefreshCallbacks()
|
|
543
|
+
}
|
|
541
544
|
}
|
|
542
545
|
|
|
543
546
|
// NOTE This function is performance-optimized (i.e. not using `Array.from`)
|
package/src/store.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { BootDb, PreparedBindValues, ResetMode, StoreAdapter, StoreAdapterFactory } from '@livestore/common'
|
|
2
|
-
import { getExecArgsFromMutation } from '@livestore/common'
|
|
2
|
+
import { Devtools, getExecArgsFromMutation } from '@livestore/common'
|
|
3
3
|
import type { LiveStoreSchema, MutationEvent, MutationEventSchema } from '@livestore/common/schema'
|
|
4
4
|
import { makeMutationEventSchema } from '@livestore/common/schema'
|
|
5
|
-
import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
|
|
5
|
+
import { assertNever, isPromise, makeNoopTracer, ref, shouldNeverHappen } from '@livestore/utils'
|
|
6
6
|
import { Effect, Schema, Stream } from '@livestore/utils/effect'
|
|
7
7
|
import * as otel from '@opentelemetry/api'
|
|
8
8
|
import type { GraphQLSchema } from 'graphql'
|
|
@@ -39,6 +39,7 @@ export type StoreOptions<
|
|
|
39
39
|
otelRootSpanContext: otel.Context
|
|
40
40
|
dbGraph: DbGraph
|
|
41
41
|
mutationEventSchema: MutationEventSchema<any>
|
|
42
|
+
disableDevtools?: boolean
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export type RefreshReason =
|
|
@@ -116,7 +117,7 @@ export class Store<
|
|
|
116
117
|
/** RC-based set to see which queries are currently subscribed to */
|
|
117
118
|
activeQueries: ReferenceCountedSet<LiveQuery<any>>
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
readonly __mutationEventSchema
|
|
120
121
|
|
|
121
122
|
private constructor({
|
|
122
123
|
adapter,
|
|
@@ -126,13 +127,14 @@ export class Store<
|
|
|
126
127
|
otelTracer,
|
|
127
128
|
otelRootSpanContext,
|
|
128
129
|
mutationEventSchema,
|
|
130
|
+
disableDevtools,
|
|
129
131
|
}: StoreOptions<TGraphQLContext, TSchema>) {
|
|
130
132
|
this.mainDbWrapper = new MainDatabaseWrapper({ otelTracer, otelRootSpanContext, db: adapter.mainDb })
|
|
131
133
|
this.adapter = adapter
|
|
132
134
|
this.schema = schema
|
|
133
135
|
|
|
134
136
|
// TODO refactor
|
|
135
|
-
this.
|
|
137
|
+
this.__mutationEventSchema = mutationEventSchema
|
|
136
138
|
// this.mutationEventSchema = makeMutationEventSchema(Object.fromEntries(schema.mutations.entries()) as any)
|
|
137
139
|
|
|
138
140
|
// TODO generalize the `tableRefs` concept to allow finer-grained refs
|
|
@@ -157,6 +159,10 @@ export class Store<
|
|
|
157
159
|
Effect.runFork,
|
|
158
160
|
)
|
|
159
161
|
|
|
162
|
+
if (disableDevtools !== true) {
|
|
163
|
+
this.bootDevtools()
|
|
164
|
+
}
|
|
165
|
+
|
|
160
166
|
this.otel = {
|
|
161
167
|
tracer: otelTracer,
|
|
162
168
|
mutationsSpanContext: otelMuationsSpanContext,
|
|
@@ -469,7 +475,7 @@ export class Store<
|
|
|
469
475
|
writeTables.forEach((table) => allWriteTables.add(table))
|
|
470
476
|
}
|
|
471
477
|
|
|
472
|
-
const mutationEventEncoded = Schema.encodeUnknownSync(this.
|
|
478
|
+
const mutationEventEncoded = Schema.encodeUnknownSync(this.__mutationEventSchema)(mutationEventDecoded)
|
|
473
479
|
|
|
474
480
|
if (coordinatorMode !== 'skip-coordinator') {
|
|
475
481
|
// Asynchronously apply mutation to a persistent storage (we're not awaiting this promise here)
|
|
@@ -517,6 +523,103 @@ export class Store<
|
|
|
517
523
|
meta: { liveStoreRefType: 'table' },
|
|
518
524
|
})
|
|
519
525
|
|
|
526
|
+
private bootDevtools = () => {
|
|
527
|
+
const devtoolsChannel = Devtools.makeBroadcastChannels()
|
|
528
|
+
|
|
529
|
+
const signalsSubcriptionRef = ref<undefined | (() => void)>(undefined)
|
|
530
|
+
// let alreadySubscribedToLiveQueries = false
|
|
531
|
+
const liveQueriesSubscriptionRef = ref<undefined | (() => void)>(undefined)
|
|
532
|
+
devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
|
|
533
|
+
const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data)
|
|
534
|
+
if (
|
|
535
|
+
decoded._tag === 'None' ||
|
|
536
|
+
decoded.value._tag === 'LSD.DevtoolsReadyBroadcast' ||
|
|
537
|
+
decoded.value._tag === 'LSD.DevtoolsConnected' ||
|
|
538
|
+
decoded.value.channelId !== this.adapter.coordinator.devtools.channelId
|
|
539
|
+
) {
|
|
540
|
+
// console.log(`Unknown message`, event)
|
|
541
|
+
return
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const requestId = decoded.value.requestId
|
|
545
|
+
const sendToDevtools = (message: Devtools.MessageFromAppHost) =>
|
|
546
|
+
devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message))
|
|
547
|
+
|
|
548
|
+
switch (decoded.value._tag) {
|
|
549
|
+
case 'LSD.SignalsSubscribe': {
|
|
550
|
+
const includeResults = decoded.value.includeResults
|
|
551
|
+
const send = () =>
|
|
552
|
+
sendToDevtools(Devtools.SignalsRes.make({ signals: this.graph.getSnapshot({ includeResults }), requestId }))
|
|
553
|
+
|
|
554
|
+
send()
|
|
555
|
+
|
|
556
|
+
if (signalsSubcriptionRef.current === undefined) {
|
|
557
|
+
signalsSubcriptionRef.current = this.graph.subscribeToRefresh(() => send())
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
break
|
|
561
|
+
}
|
|
562
|
+
case 'LSD.DebugInfoReq': {
|
|
563
|
+
sendToDevtools(Devtools.DebugInfoRes.make({ debugInfo: this.mainDbWrapper.debugInfo, requestId }))
|
|
564
|
+
break
|
|
565
|
+
}
|
|
566
|
+
case 'LSD.DebugInfoResetReq': {
|
|
567
|
+
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
568
|
+
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId }))
|
|
569
|
+
break
|
|
570
|
+
}
|
|
571
|
+
case 'LSD.DebugInfoRerunQueryReq': {
|
|
572
|
+
const { queryStr, bindValues, queriedTables } = decoded.value
|
|
573
|
+
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
574
|
+
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId }))
|
|
575
|
+
break
|
|
576
|
+
}
|
|
577
|
+
case 'LSD.SignalsUnsubscribe': {
|
|
578
|
+
signalsSubcriptionRef.current!()
|
|
579
|
+
signalsSubcriptionRef.current = undefined
|
|
580
|
+
break
|
|
581
|
+
}
|
|
582
|
+
case 'LSD.LiveQueriesSubscribe': {
|
|
583
|
+
const send = () =>
|
|
584
|
+
sendToDevtools(
|
|
585
|
+
Devtools.LiveQueriesRes.make({
|
|
586
|
+
liveQueries: [...this.activeQueries].map((q) => ({
|
|
587
|
+
_tag: q._tag,
|
|
588
|
+
id: q.id,
|
|
589
|
+
label: q.label,
|
|
590
|
+
runs: q.runs,
|
|
591
|
+
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
592
|
+
lastestResult: q.results$.previousResult,
|
|
593
|
+
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
594
|
+
})),
|
|
595
|
+
requestId,
|
|
596
|
+
}),
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
send()
|
|
600
|
+
|
|
601
|
+
if (liveQueriesSubscriptionRef.current === undefined) {
|
|
602
|
+
liveQueriesSubscriptionRef.current = this.graph.subscribeToRefresh(() => send())
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
break
|
|
606
|
+
}
|
|
607
|
+
case 'LSD.LiveQueriesUnsubscribe': {
|
|
608
|
+
liveQueriesSubscriptionRef.current!()
|
|
609
|
+
liveQueriesSubscriptionRef.current = undefined
|
|
610
|
+
break
|
|
611
|
+
}
|
|
612
|
+
case 'LSD.ResetAllDataReq': {
|
|
613
|
+
await this.adapter.coordinator.dangerouslyReset(decoded.value.mode)
|
|
614
|
+
sendToDevtools(Devtools.ResetAllDataRes.make({ requestId }))
|
|
615
|
+
|
|
616
|
+
break
|
|
617
|
+
}
|
|
618
|
+
// No default
|
|
619
|
+
}
|
|
620
|
+
})
|
|
621
|
+
}
|
|
622
|
+
|
|
520
623
|
__devDownloadDb = () => {
|
|
521
624
|
const data = this.mainDbWrapper.export()
|
|
522
625
|
downloadBlob(data, `livestore-${Date.now()}.db`)
|
|
@@ -544,6 +647,7 @@ export const createStore = async <
|
|
|
544
647
|
boot,
|
|
545
648
|
dbGraph = globalDbGraph,
|
|
546
649
|
batchUpdates,
|
|
650
|
+
disableDevtools,
|
|
547
651
|
}: {
|
|
548
652
|
schema: TSchema
|
|
549
653
|
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
@@ -553,6 +657,7 @@ export const createStore = async <
|
|
|
553
657
|
boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
554
658
|
dbGraph?: DbGraph
|
|
555
659
|
batchUpdates?: (run: () => void) => void
|
|
660
|
+
disableDevtools?: boolean
|
|
556
661
|
}): Promise<Store<TGraphQLContext, TSchema>> => {
|
|
557
662
|
return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
|
|
558
663
|
try {
|
|
@@ -643,7 +748,16 @@ export const createStore = async <
|
|
|
643
748
|
// Think about what to do about this case.
|
|
644
749
|
// await applySchema(db, schema)
|
|
645
750
|
return Store.createStore<TGraphQLContext, TSchema>(
|
|
646
|
-
{
|
|
751
|
+
{
|
|
752
|
+
adapter: adapter,
|
|
753
|
+
schema,
|
|
754
|
+
graphQLOptions,
|
|
755
|
+
otelTracer,
|
|
756
|
+
otelRootSpanContext,
|
|
757
|
+
dbGraph,
|
|
758
|
+
mutationEventSchema,
|
|
759
|
+
disableDevtools,
|
|
760
|
+
},
|
|
647
761
|
span,
|
|
648
762
|
)
|
|
649
763
|
} finally {
|
package/src/utils/util.ts
CHANGED
|
@@ -5,7 +5,9 @@ import type { Brand } from '@livestore/utils/effect'
|
|
|
5
5
|
export type ParamsObject = Record<string, SqlValue>
|
|
6
6
|
export type SqlValue = string | number | Uint8Array | null
|
|
7
7
|
|
|
8
|
-
export type Bindable = SqlValue
|
|
8
|
+
export type Bindable = ReadonlyArray<SqlValue> | ParamsObject
|
|
9
|
+
|
|
10
|
+
type XXX_TODO_REMOVE_REDUDANCY = 1
|
|
9
11
|
|
|
10
12
|
export type PreparedBindValues = Brand.Branded<Bindable, 'PreparedBindValues'>
|
|
11
13
|
|
|
@@ -16,7 +18,7 @@ export type PreparedBindValues = Brand.Branded<Bindable, 'PreparedBindValues'>
|
|
|
16
18
|
/* TODO: Search for unused params via proper parsing, not string search
|
|
17
19
|
**/
|
|
18
20
|
export const prepareBindValues = (values: Bindable, statement: string): PreparedBindValues => {
|
|
19
|
-
if (Array.isArray(values)) return values as PreparedBindValues
|
|
21
|
+
if (Array.isArray(values)) return values as any as PreparedBindValues
|
|
20
22
|
|
|
21
23
|
const result: ParamsObject = {}
|
|
22
24
|
for (const [key, value] of Object.entries(values)) {
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a map that has a fixed number of entries.
|
|
3
|
-
* Once hitting the bound, earliest insertions are removed
|
|
4
|
-
*/
|
|
5
|
-
export default class BoundMap<K, V> {
|
|
6
|
-
#map = new Map<K, V>()
|
|
7
|
-
#sizeLimit: number
|
|
8
|
-
|
|
9
|
-
constructor(sizeLimit: number) {
|
|
10
|
-
this.#sizeLimit = sizeLimit
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
onEvict: ((key: K, value: V) => void) | undefined
|
|
14
|
-
|
|
15
|
-
set = (key: K, value: V) => {
|
|
16
|
-
this.#map.set(key, value)
|
|
17
|
-
// console.log(this.#map.size, this.#sizeLimit);
|
|
18
|
-
if (this.#map.size > this.#sizeLimit) {
|
|
19
|
-
const firstKey = this.#map.keys().next().value
|
|
20
|
-
const deletedValue = this.#map.get(firstKey)!
|
|
21
|
-
this.#map.delete(firstKey)
|
|
22
|
-
if (this.onEvict) {
|
|
23
|
-
this.onEvict(firstKey, deletedValue)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
get = (key: K): V | undefined => {
|
|
29
|
-
return this.#map.get(key)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
delete = (key: K) => {
|
|
33
|
-
this.#map.delete(key)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
keys = () => {
|
|
37
|
-
return this.#map.keys()
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class BoundSet<V> {
|
|
42
|
-
#map: BoundMap<V, V>
|
|
43
|
-
|
|
44
|
-
constructor(sizeLimit: number) {
|
|
45
|
-
this.#map = new BoundMap(sizeLimit)
|
|
46
|
-
this.#map.onEvict = this.#onEvict
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
#onEvict = (v: V) => {
|
|
50
|
-
if (this.onEvict) {
|
|
51
|
-
this.onEvict(v)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
onEvict: ((key: V) => void) | undefined
|
|
56
|
-
|
|
57
|
-
add = (v: V) => {
|
|
58
|
-
this.#map.set(v, v)
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
[Symbol.iterator] = () => {
|
|
62
|
-
return this.#map.keys()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class BoundArray<V> {
|
|
67
|
-
#array: V[] = []
|
|
68
|
-
#sizeLimit: number
|
|
69
|
-
|
|
70
|
-
constructor(sizeLimit: number) {
|
|
71
|
-
this.#sizeLimit = sizeLimit
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
onEvict: ((key: V) => void) | undefined
|
|
75
|
-
|
|
76
|
-
push = (v: V) => {
|
|
77
|
-
this.#array.push(v)
|
|
78
|
-
if (this.#array.length > this.#sizeLimit) {
|
|
79
|
-
const first = this.#array.shift()
|
|
80
|
-
if (first && this.onEvict) {
|
|
81
|
-
this.onEvict(first)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
get = (index: number): V | undefined => {
|
|
87
|
-
return this.#array[index]
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
delete = (index: number) => {
|
|
91
|
-
this.#array.splice(index, 1)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
get length() {
|
|
95
|
-
return this.#array.length
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
[Symbol.iterator] = () => {
|
|
99
|
-
return this.#array[Symbol.iterator]()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
map = <T>(fn: (v: V) => T): T[] => {
|
|
103
|
-
return this.#array.map(fn)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
clear = () => {
|
|
107
|
-
this.#array = []
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
sort = (fn?: (a: V, b: V) => number) => {
|
|
111
|
-
return this.#array.sort(fn)
|
|
112
|
-
}
|
|
113
|
-
}
|