@livestore/livestore 0.0.54-dev.0 → 0.0.54-dev.13
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/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 +4 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +101 -54
- 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 +125 -69
- package/src/utils/util.ts +4 -2
- package/tsconfig.json +1 -0
- package/src/utils/bounded-collections.ts +0 -113
|
@@ -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,9 @@
|
|
|
1
1
|
import type { BootDb, PreparedBindValues, ResetMode, StoreAdapter, StoreAdapterFactory } from '@livestore/common'
|
|
2
2
|
import { Devtools, getExecArgsFromMutation } from '@livestore/common'
|
|
3
|
+
import { version as liveStoreVersion } from '@livestore/common/package.json'
|
|
3
4
|
import type { LiveStoreSchema, MutationEvent, MutationEventSchema } from '@livestore/common/schema'
|
|
4
5
|
import { makeMutationEventSchema } from '@livestore/common/schema'
|
|
5
|
-
import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
|
|
6
|
+
import { assertNever, isPromise, makeNoopTracer, ref, shouldNeverHappen } from '@livestore/utils'
|
|
6
7
|
import { Effect, Schema, Stream } from '@livestore/utils/effect'
|
|
7
8
|
import * as otel from '@opentelemetry/api'
|
|
8
9
|
import type { GraphQLSchema } from 'graphql'
|
|
@@ -39,6 +40,7 @@ export type StoreOptions<
|
|
|
39
40
|
otelRootSpanContext: otel.Context
|
|
40
41
|
dbGraph: DbGraph
|
|
41
42
|
mutationEventSchema: MutationEventSchema<any>
|
|
43
|
+
disableDevtools?: boolean
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export type RefreshReason =
|
|
@@ -126,6 +128,7 @@ export class Store<
|
|
|
126
128
|
otelTracer,
|
|
127
129
|
otelRootSpanContext,
|
|
128
130
|
mutationEventSchema,
|
|
131
|
+
disableDevtools,
|
|
129
132
|
}: StoreOptions<TGraphQLContext, TSchema>) {
|
|
130
133
|
this.mainDbWrapper = new MainDatabaseWrapper({ otelTracer, otelRootSpanContext, db: adapter.mainDb })
|
|
131
134
|
this.adapter = adapter
|
|
@@ -157,73 +160,9 @@ export class Store<
|
|
|
157
160
|
Effect.runFork,
|
|
158
161
|
)
|
|
159
162
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let alreadySubscribedToLiveQueries = false
|
|
164
|
-
devtoolsChannel.addEventListener('message', async (event) => {
|
|
165
|
-
const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data)
|
|
166
|
-
if (decoded._tag === 'None') {
|
|
167
|
-
console.log(`Unknown message`, event)
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const requestId = decoded.value.requestId
|
|
172
|
-
const sendToDevtools = (message: Devtools.MessageFromAppHost) =>
|
|
173
|
-
devtoolsChannel.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message))
|
|
174
|
-
|
|
175
|
-
switch (decoded.value._tag) {
|
|
176
|
-
case 'LSD.SubscribeSignalsReq': {
|
|
177
|
-
const includeResults = decoded.value.includeResults
|
|
178
|
-
const send = () =>
|
|
179
|
-
sendToDevtools(
|
|
180
|
-
Devtools.SubscribeSignalsRes.make({ signals: this.graph.getSnapshot({ includeResults }), requestId }),
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
send()
|
|
184
|
-
|
|
185
|
-
if (!alreadySubscribedToSignals) {
|
|
186
|
-
this.graph.subscribeToRefresh(() => send())
|
|
187
|
-
alreadySubscribedToSignals = true
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
break
|
|
191
|
-
}
|
|
192
|
-
case 'LSD.SubscribeLiveQueriesReq': {
|
|
193
|
-
const send = () =>
|
|
194
|
-
sendToDevtools(
|
|
195
|
-
Devtools.SubscribeLiveQueriesRes.make({
|
|
196
|
-
liveQueries: [...this.activeQueries].map((q) => ({
|
|
197
|
-
_tag: q._tag,
|
|
198
|
-
id: q.id,
|
|
199
|
-
label: q.label,
|
|
200
|
-
runs: q.runs,
|
|
201
|
-
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
202
|
-
lastestResult: q.results$.previousResult,
|
|
203
|
-
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
204
|
-
})),
|
|
205
|
-
requestId,
|
|
206
|
-
}),
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
send()
|
|
210
|
-
|
|
211
|
-
if (!alreadySubscribedToLiveQueries) {
|
|
212
|
-
this.graph.subscribeToRefresh(() => send())
|
|
213
|
-
alreadySubscribedToLiveQueries = true
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
break
|
|
217
|
-
}
|
|
218
|
-
case 'LSD.ResetAllDataReq': {
|
|
219
|
-
await this.adapter.coordinator.dangerouslyReset(decoded.value.mode)
|
|
220
|
-
sendToDevtools(Devtools.ResetAllDataRes.make({ requestId }))
|
|
221
|
-
|
|
222
|
-
break
|
|
223
|
-
}
|
|
224
|
-
// No default
|
|
225
|
-
}
|
|
226
|
-
})
|
|
163
|
+
if (disableDevtools !== true) {
|
|
164
|
+
this.bootDevtools()
|
|
165
|
+
}
|
|
227
166
|
|
|
228
167
|
this.otel = {
|
|
229
168
|
tracer: otelTracer,
|
|
@@ -585,6 +524,112 @@ export class Store<
|
|
|
585
524
|
meta: { liveStoreRefType: 'table' },
|
|
586
525
|
})
|
|
587
526
|
|
|
527
|
+
private bootDevtools = () => {
|
|
528
|
+
const devtoolsChannel = Devtools.makeBroadcastChannels()
|
|
529
|
+
|
|
530
|
+
const signalsSubcriptionRef = ref<undefined | (() => void)>(undefined)
|
|
531
|
+
// let alreadySubscribedToLiveQueries = false
|
|
532
|
+
const liveQueriesSubscriptionRef = ref<undefined | (() => void)>(undefined)
|
|
533
|
+
devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
|
|
534
|
+
const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data)
|
|
535
|
+
if (
|
|
536
|
+
decoded._tag === 'None' ||
|
|
537
|
+
decoded.value._tag === 'LSD.DevtoolsReadyBroadcast' ||
|
|
538
|
+
decoded.value._tag === 'LSD.DevtoolsConnected' ||
|
|
539
|
+
decoded.value.channelId !== this.adapter.coordinator.devtools.channelId
|
|
540
|
+
) {
|
|
541
|
+
// console.log(`Unknown message`, event)
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const requestId = decoded.value.requestId
|
|
546
|
+
const sendToDevtools = (message: Devtools.MessageFromAppHost) =>
|
|
547
|
+
devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message))
|
|
548
|
+
|
|
549
|
+
switch (decoded.value._tag) {
|
|
550
|
+
case 'LSD.SignalsSubscribe': {
|
|
551
|
+
const includeResults = decoded.value.includeResults
|
|
552
|
+
const send = () =>
|
|
553
|
+
sendToDevtools(
|
|
554
|
+
Devtools.SignalsRes.make({
|
|
555
|
+
signals: this.graph.getSnapshot({ includeResults }),
|
|
556
|
+
requestId,
|
|
557
|
+
liveStoreVersion,
|
|
558
|
+
}),
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
send()
|
|
562
|
+
|
|
563
|
+
if (signalsSubcriptionRef.current === undefined) {
|
|
564
|
+
signalsSubcriptionRef.current = this.graph.subscribeToRefresh(() => send())
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
break
|
|
568
|
+
}
|
|
569
|
+
case 'LSD.DebugInfoReq': {
|
|
570
|
+
sendToDevtools(
|
|
571
|
+
Devtools.DebugInfoRes.make({ debugInfo: this.mainDbWrapper.debugInfo, requestId, liveStoreVersion }),
|
|
572
|
+
)
|
|
573
|
+
break
|
|
574
|
+
}
|
|
575
|
+
case 'LSD.DebugInfoResetReq': {
|
|
576
|
+
this.mainDbWrapper.debugInfo.slowQueries.clear()
|
|
577
|
+
sendToDevtools(Devtools.DebugInfoResetRes.make({ requestId, liveStoreVersion }))
|
|
578
|
+
break
|
|
579
|
+
}
|
|
580
|
+
case 'LSD.DebugInfoRerunQueryReq': {
|
|
581
|
+
const { queryStr, bindValues, queriedTables } = decoded.value
|
|
582
|
+
this.mainDbWrapper.select(queryStr, { bindValues, queriedTables, skipCache: true })
|
|
583
|
+
sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
|
|
584
|
+
break
|
|
585
|
+
}
|
|
586
|
+
case 'LSD.SignalsUnsubscribe': {
|
|
587
|
+
signalsSubcriptionRef.current!()
|
|
588
|
+
signalsSubcriptionRef.current = undefined
|
|
589
|
+
break
|
|
590
|
+
}
|
|
591
|
+
case 'LSD.LiveQueriesSubscribe': {
|
|
592
|
+
const send = () =>
|
|
593
|
+
sendToDevtools(
|
|
594
|
+
Devtools.LiveQueriesRes.make({
|
|
595
|
+
liveQueries: [...this.activeQueries].map((q) => ({
|
|
596
|
+
_tag: q._tag,
|
|
597
|
+
id: q.id,
|
|
598
|
+
label: q.label,
|
|
599
|
+
runs: q.runs,
|
|
600
|
+
executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
|
|
601
|
+
lastestResult: q.results$.previousResult,
|
|
602
|
+
activeSubscriptions: Array.from(q.activeSubscriptions),
|
|
603
|
+
})),
|
|
604
|
+
requestId,
|
|
605
|
+
liveStoreVersion,
|
|
606
|
+
}),
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
send()
|
|
610
|
+
|
|
611
|
+
if (liveQueriesSubscriptionRef.current === undefined) {
|
|
612
|
+
liveQueriesSubscriptionRef.current = this.graph.subscribeToRefresh(() => send())
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
break
|
|
616
|
+
}
|
|
617
|
+
case 'LSD.LiveQueriesUnsubscribe': {
|
|
618
|
+
liveQueriesSubscriptionRef.current!()
|
|
619
|
+
liveQueriesSubscriptionRef.current = undefined
|
|
620
|
+
break
|
|
621
|
+
}
|
|
622
|
+
case 'LSD.ResetAllDataReq': {
|
|
623
|
+
await this.adapter.coordinator.dangerouslyReset(decoded.value.mode)
|
|
624
|
+
sendToDevtools(Devtools.ResetAllDataRes.make({ requestId, liveStoreVersion }))
|
|
625
|
+
|
|
626
|
+
break
|
|
627
|
+
}
|
|
628
|
+
// No default
|
|
629
|
+
}
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
|
|
588
633
|
__devDownloadDb = () => {
|
|
589
634
|
const data = this.mainDbWrapper.export()
|
|
590
635
|
downloadBlob(data, `livestore-${Date.now()}.db`)
|
|
@@ -612,6 +657,7 @@ export const createStore = async <
|
|
|
612
657
|
boot,
|
|
613
658
|
dbGraph = globalDbGraph,
|
|
614
659
|
batchUpdates,
|
|
660
|
+
disableDevtools,
|
|
615
661
|
}: {
|
|
616
662
|
schema: TSchema
|
|
617
663
|
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
@@ -621,6 +667,7 @@ export const createStore = async <
|
|
|
621
667
|
boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
622
668
|
dbGraph?: DbGraph
|
|
623
669
|
batchUpdates?: (run: () => void) => void
|
|
670
|
+
disableDevtools?: boolean
|
|
624
671
|
}): Promise<Store<TGraphQLContext, TSchema>> => {
|
|
625
672
|
return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
|
|
626
673
|
try {
|
|
@@ -711,7 +758,16 @@ export const createStore = async <
|
|
|
711
758
|
// Think about what to do about this case.
|
|
712
759
|
// await applySchema(db, schema)
|
|
713
760
|
return Store.createStore<TGraphQLContext, TSchema>(
|
|
714
|
-
{
|
|
761
|
+
{
|
|
762
|
+
adapter: adapter,
|
|
763
|
+
schema,
|
|
764
|
+
graphQLOptions,
|
|
765
|
+
otelTracer,
|
|
766
|
+
otelRootSpanContext,
|
|
767
|
+
dbGraph,
|
|
768
|
+
mutationEventSchema,
|
|
769
|
+
disableDevtools,
|
|
770
|
+
},
|
|
715
771
|
span,
|
|
716
772
|
)
|
|
717
773
|
} 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)) {
|
package/tsconfig.json
CHANGED
|
@@ -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
|
-
}
|