@raubjo/architect 0.5.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.
Files changed (68) hide show
  1. package/README.md +860 -0
  2. package/package.json +121 -0
  3. package/src/cache/cache.ts +46 -0
  4. package/src/cache/contract.ts +9 -0
  5. package/src/cache/manager.ts +110 -0
  6. package/src/cache/provider.ts +11 -0
  7. package/src/config/contract.ts +63 -0
  8. package/src/config/discovery.ts +99 -0
  9. package/src/config/env.global.d.ts +6 -0
  10. package/src/config/env.ts +68 -0
  11. package/src/config/index.ts +5 -0
  12. package/src/config/provider.ts +17 -0
  13. package/src/config/repository.ts +164 -0
  14. package/src/container/adapters/builtin.ts +323 -0
  15. package/src/container/contract.ts +43 -0
  16. package/src/container/runtime.ts +29 -0
  17. package/src/events/bus.ts +174 -0
  18. package/src/events/concerns/dispatchable.ts +10 -0
  19. package/src/events/provider.ts +9 -0
  20. package/src/events/types.ts +9 -0
  21. package/src/foundation/application.ts +136 -0
  22. package/src/foundation/current-application.ts +20 -0
  23. package/src/index.ts +58 -0
  24. package/src/log/contract.ts +21 -0
  25. package/src/log/drivers/console.ts +54 -0
  26. package/src/log/drivers/null.ts +23 -0
  27. package/src/log/drivers/stack.ts +46 -0
  28. package/src/log/manager.ts +76 -0
  29. package/src/log/provider.ts +11 -0
  30. package/src/react.ts +2 -0
  31. package/src/renderers/adapters/react.tsx +25 -0
  32. package/src/renderers/adapters/solid.tsx +26 -0
  33. package/src/renderers/adapters/svelte.ts +73 -0
  34. package/src/renderers/adapters/vue.ts +22 -0
  35. package/src/renderers/contract.ts +12 -0
  36. package/src/runtimes/react.tsx +81 -0
  37. package/src/runtimes/solid.tsx +47 -0
  38. package/src/runtimes/svelte.ts +17 -0
  39. package/src/runtimes/vue.ts +34 -0
  40. package/src/solid.ts +2 -0
  41. package/src/store/adapters/contract.ts +11 -0
  42. package/src/store/adapters/indexed-db.ts +187 -0
  43. package/src/store/adapters/local-storage.ts +48 -0
  44. package/src/store/adapters/memory.ts +35 -0
  45. package/src/store/manager.ts +68 -0
  46. package/src/store/provider.ts +10 -0
  47. package/src/store/store.ts +1 -0
  48. package/src/support/arr.ts +372 -0
  49. package/src/support/collection.ts +889 -0
  50. package/src/support/facades/cache.ts +6 -0
  51. package/src/support/facades/config.ts +6 -0
  52. package/src/support/facades/event.ts +6 -0
  53. package/src/support/facades/facade.ts +146 -0
  54. package/src/support/facades/index.ts +5 -0
  55. package/src/support/facades/log.ts +6 -0
  56. package/src/support/facades/store.ts +6 -0
  57. package/src/support/fluent.ts +56 -0
  58. package/src/support/globals.ts +8 -0
  59. package/src/support/lazy-collection.ts +341 -0
  60. package/src/support/manager.ts +53 -0
  61. package/src/support/num.ts +50 -0
  62. package/src/support/pipeline.ts +29 -0
  63. package/src/support/service-provider.ts +19 -0
  64. package/src/support/str.ts +682 -0
  65. package/src/svelte.ts +2 -0
  66. package/src/types/peer-deps.d.ts +10 -0
  67. package/src/vue.ts +2 -0
  68. package/tsconfig.json +15 -0
@@ -0,0 +1,25 @@
1
+ import type { ReactElement } from "react"
2
+ import { createElement } from "react"
3
+ import ReactDOM from "react-dom/client"
4
+ import { ApplicationProvider } from "../../runtimes/react"
5
+ import type { Cleanup } from "../../support/service-provider"
6
+ import type Contract from "../contract"
7
+ import type { RendererContext } from "../contract"
8
+
9
+ type ReactRootComponent = () => ReactElement | null
10
+
11
+ export default class ReactRenderer implements Contract {
12
+ render({ RootComponent, container, rootElementId }: RendererContext): Cleanup {
13
+ const mountNode = document.getElementById(rootElementId)
14
+ if (!mountNode) {
15
+ throw new Error(`Missing mount node #${rootElementId}.`)
16
+ }
17
+
18
+ const root = ReactDOM.createRoot(mountNode)
19
+ root.render(
20
+ createElement(ApplicationProvider, { container }, createElement(RootComponent as ReactRootComponent)),
21
+ )
22
+
23
+ return () => root.unmount()
24
+ }
25
+ }
@@ -0,0 +1,26 @@
1
+ import { createComponent, type JSX } from "solid-js"
2
+ import { render } from "solid-js/web"
3
+ import { ApplicationProvider } from "../../runtimes/solid"
4
+ import type { Cleanup } from "../../support/service-provider"
5
+ import type Contract from "../contract"
6
+ import type { RendererContext } from "../contract"
7
+
8
+ type SolidRootComponent = () => JSX.Element
9
+
10
+ export default class SolidRenderer implements Contract {
11
+ render({ RootComponent, container, rootElementId }: RendererContext): Cleanup {
12
+ const mountNode = document.getElementById(rootElementId)
13
+ if (!mountNode) {
14
+ throw new Error(`Missing mount node #${rootElementId}.`)
15
+ }
16
+
17
+ return render(
18
+ () =>
19
+ createComponent(ApplicationProvider, {
20
+ container,
21
+ children: () => (RootComponent as SolidRootComponent)(),
22
+ }),
23
+ mountNode,
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,73 @@
1
+ import { mount, unmount } from "svelte"
2
+ import type { Cleanup } from "../../support/service-provider"
3
+ import type Contract from "../contract"
4
+ import type { RendererContext } from "../contract"
5
+
6
+ type SvelteComponentInstance = {
7
+ $destroy?: () => void
8
+ destroy?: () => void
9
+ }
10
+
11
+ type SvelteComponentConstructor = new (options: {
12
+ target: Element
13
+ props?: Record<string, unknown>
14
+ }) => SvelteComponentInstance
15
+
16
+ type SvelteMount = (component: unknown, options: { target: Element; props?: Record<string, unknown> }) => unknown
17
+ type SvelteUnmount = (instance: unknown) => void | Promise<void>
18
+
19
+ export default class SvelteRenderer implements Contract {
20
+ render({ RootComponent, container, rootElementId }: RendererContext): Cleanup {
21
+ const mountNode = document.getElementById(rootElementId)
22
+ if (!mountNode) {
23
+ throw new Error(`Missing mount node #${rootElementId}.`)
24
+ }
25
+
26
+ const props = { container }
27
+ const modernCleanup = tryRenderModernSvelte(RootComponent, mountNode, props)
28
+
29
+ return modernCleanup ?? renderLegacySvelte(RootComponent, mountNode, props)
30
+ }
31
+ }
32
+
33
+ function tryRenderModernSvelte(
34
+ RootComponent: unknown,
35
+ mountNode: Element,
36
+ props: Record<string, unknown>,
37
+ ): Cleanup | null {
38
+ const svelteMount = mount as unknown as SvelteMount | undefined
39
+ const svelteUnmount = unmount as unknown as SvelteUnmount | undefined
40
+
41
+ if (typeof svelteMount !== "function" || typeof svelteUnmount !== "function") {
42
+ return null
43
+ }
44
+
45
+ try {
46
+ const instance = svelteMount(RootComponent, {
47
+ target: mountNode,
48
+ props,
49
+ })
50
+
51
+ return () => {
52
+ void svelteUnmount(instance)
53
+ }
54
+ } catch (_error) {
55
+ return null
56
+ }
57
+ }
58
+
59
+ function renderLegacySvelte(RootComponent: unknown, mountNode: Element, props: Record<string, unknown>): Cleanup {
60
+ const Component = RootComponent as SvelteComponentConstructor
61
+ const instance = new Component({ target: mountNode, props })
62
+
63
+ return () => {
64
+ if (typeof instance.$destroy === "function") {
65
+ instance.$destroy()
66
+ return
67
+ }
68
+
69
+ if (typeof instance.destroy === "function") {
70
+ instance.destroy()
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,22 @@
1
+ import { type Component, createApp } from "vue"
2
+ import { containerKey } from "../../runtimes/vue"
3
+ import type { Cleanup } from "../../support/service-provider"
4
+ import type Contract from "../contract"
5
+ import type { RendererContext } from "../contract"
6
+
7
+ type VueRootComponent = Component
8
+
9
+ export default class VueRenderer implements Contract {
10
+ render({ RootComponent, container, rootElementId }: RendererContext): Cleanup {
11
+ const mountNode = document.getElementById(rootElementId)
12
+ if (!mountNode) {
13
+ throw new Error(`Missing mount node #${rootElementId}.`)
14
+ }
15
+
16
+ const app = createApp(RootComponent as VueRootComponent)
17
+ app.provide(containerKey, container)
18
+ app.mount(mountNode)
19
+
20
+ return () => app.unmount()
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ import type { Cleanup, ServiceProviderContext } from "../support/service-provider"
2
+
3
+ export type RootComponent = unknown
4
+
5
+ export type RendererContext = ServiceProviderContext & {
6
+ RootComponent: RootComponent
7
+ rootElementId: string
8
+ }
9
+
10
+ export default interface Contract {
11
+ render(context: RendererContext): void | Cleanup
12
+ }
@@ -0,0 +1,81 @@
1
+ import { createContext, type ReactNode, useContext, useEffect, useMemo, useRef, useState } from "react"
2
+ import type { ContainerContract, ContainerIdentifier } from "../container/contract"
3
+ import type { Application } from "../foundation/application"
4
+
5
+ const Context = createContext<ContainerContract | null>(null)
6
+
7
+ type ApplicationProviderProps = {
8
+ container: ContainerContract
9
+ children?: ReactNode
10
+ }
11
+
12
+ export function ApplicationProvider({ container, children }: ApplicationProviderProps) {
13
+ return <Context.Provider value={container}>{children}</Context.Provider>
14
+ }
15
+
16
+ export type ContextProviderProps = {
17
+ application?: Application
18
+ container?: ContainerContract
19
+ fallback?: ReactNode
20
+ children?: ReactNode
21
+ }
22
+
23
+ export function ContextProvider({ application, container, fallback = null, children }: ContextProviderProps) {
24
+ if (!application && !container) {
25
+ throw new Error("ContextProvider requires either `application` or `container`.")
26
+ }
27
+
28
+ const externalRuntime = useMemo(() => (container ? { container, stop: () => {} } : null), [container])
29
+ const [runtime, setRuntime] = useState<{ container: ContainerContract; stop: () => void } | null>(externalRuntime)
30
+
31
+ const stopRef = useRef<null | (() => void)>(null)
32
+ const startedRef = useRef(false)
33
+
34
+ useEffect(() => {
35
+ if (externalRuntime) {
36
+ setRuntime(externalRuntime)
37
+ return
38
+ }
39
+
40
+ if (startedRef.current) {
41
+ return
42
+ }
43
+
44
+ startedRef.current = true
45
+
46
+ const running = application?.run()
47
+ stopRef.current = running.stop
48
+ setRuntime(running)
49
+
50
+ return () => {
51
+ stopRef.current?.()
52
+ stopRef.current = null
53
+ startedRef.current = false
54
+ setRuntime(null)
55
+ }
56
+ }, [application, externalRuntime])
57
+
58
+ if (!runtime) {
59
+ return <>{fallback}</>
60
+ }
61
+
62
+ return <ApplicationProvider container={runtime.container}>{children}</ApplicationProvider>
63
+ }
64
+
65
+ export function useService<T>(identifier: ContainerIdentifier<T>): T {
66
+ const container = useContext(Context)
67
+ if (!container) {
68
+ throw new Error("You must use `useService` inside the Application Context.")
69
+ }
70
+
71
+ return container.make<T>(identifier)
72
+ }
73
+
74
+ export function useContainer(): ContainerContract {
75
+ const container = useContext(Context)
76
+ if (!container) {
77
+ throw new Error("You must use `useContainer` inside the Application Context.")
78
+ }
79
+
80
+ return container
81
+ }
@@ -0,0 +1,47 @@
1
+ import { createComponent, createContext, type JSX, onCleanup, useContext } from "solid-js"
2
+ import type { ContainerContract, ContainerIdentifier } from "../container/contract"
3
+ import type { Application } from "../foundation/application"
4
+
5
+ const ContainerContext = createContext<ContainerContract | null>(null)
6
+
7
+ type ApplicationProviderProps = {
8
+ container: ContainerContract
9
+ children?: JSX.Element | (() => JSX.Element)
10
+ }
11
+
12
+ export function ApplicationProvider(props: ApplicationProviderProps) {
13
+ return createComponent(ContainerContext.Provider, {
14
+ value: props.container,
15
+ children: props.children as never,
16
+ })
17
+ }
18
+
19
+ export type ContextProviderProps = {
20
+ application?: Application
21
+ container?: ContainerContract
22
+ children?: JSX.Element | (() => JSX.Element)
23
+ }
24
+
25
+ export function ContextProvider(props: ContextProviderProps) {
26
+ if (props.container) {
27
+ return createComponent(ApplicationProvider, { container: props.container, children: props.children as never })
28
+ }
29
+
30
+ if (!props.application) {
31
+ throw new Error("ContextProvider requires either `application` or `container`.")
32
+ }
33
+
34
+ const runtime = props.application.run()
35
+ onCleanup(() => runtime.stop())
36
+
37
+ return createComponent(ApplicationProvider, { container: runtime.container, children: props.children as never })
38
+ }
39
+
40
+ export function useService<T>(identifier: ContainerIdentifier<T>): T {
41
+ const container = useContext(ContainerContext)
42
+ if (!container) {
43
+ throw new Error("Application container is not available in Solid context.")
44
+ }
45
+
46
+ return container.make<T>(identifier)
47
+ }
@@ -0,0 +1,17 @@
1
+ import { getContext, setContext } from "svelte"
2
+ import type { ContainerContract, ContainerIdentifier } from "../container/contract"
3
+
4
+ export const containerKey: unique symbol = Symbol("application.container")
5
+
6
+ export function provideContainer(container: ContainerContract): void {
7
+ setContext(containerKey, container)
8
+ }
9
+
10
+ export function useService<T>(identifier: ContainerIdentifier<T>): T {
11
+ const container = getContext<ContainerContract | null>(containerKey) ?? null
12
+ if (!container) {
13
+ throw new Error("Application container is not available in Svelte context.")
14
+ }
15
+
16
+ return container.make<T>(identifier)
17
+ }
@@ -0,0 +1,34 @@
1
+ import { defineComponent, type InjectionKey, inject, onUnmounted, provide } from "vue"
2
+ import type { ContainerContract, ContainerIdentifier } from "../container/contract"
3
+ import type { Application } from "../foundation/application"
4
+
5
+ export const containerKey: InjectionKey<ContainerContract> = Symbol("application.container")
6
+
7
+ export const ContextProvider = defineComponent({
8
+ name: "ArchitectContextProvider",
9
+ props: {
10
+ application: { type: Object as () => Application, required: false },
11
+ container: { type: Object as () => ContainerContract, required: false },
12
+ },
13
+ setup(props, { slots }) {
14
+ if (!props.application && !props.container) {
15
+ throw new Error("ContextProvider requires either `application` or `container`.")
16
+ }
17
+
18
+ const runtime = props.container ? { container: props.container, stop: () => {} } : props.application?.run()
19
+
20
+ provide(containerKey, runtime.container)
21
+ onUnmounted(() => runtime.stop())
22
+
23
+ return () => slots.default?.() ?? []
24
+ },
25
+ })
26
+
27
+ export function useService<T>(identifier: ContainerIdentifier<T>): T {
28
+ const container = inject(containerKey, null)
29
+ if (!container) {
30
+ throw new Error("Application container is not available in Vue context.")
31
+ }
32
+
33
+ return container.make<T>(identifier)
34
+ }
package/src/solid.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default as Renderer } from "./renderers/adapters/solid"
2
+ export { ApplicationProvider, ContextProvider, useService } from "./runtimes/solid"
@@ -0,0 +1,11 @@
1
+ export interface Adapter {
2
+ get(key: string): Promise<unknown>
3
+ get<T>(key: string): Promise<T | null>
4
+ set<T = unknown>(key: string, value: T): Promise<void>
5
+ has(key: string): Promise<boolean>
6
+ delete(key: string): Promise<void>
7
+ clear(): Promise<void>
8
+ keys(): Promise<string[]>
9
+ }
10
+
11
+ export type Contract = Adapter
@@ -0,0 +1,187 @@
1
+ import type { Adapter } from "./contract"
2
+ import MemoryStoreAdapter from "./memory"
3
+
4
+ type OpenFactory = Pick<IDBFactory, "open">
5
+
6
+ export default class IndexedDbAdapter implements Adapter {
7
+ protected fallback: Adapter
8
+ protected name: string
9
+ protected factory: OpenFactory | null
10
+ protected dbPromise: Promise<IDBDatabase> | null
11
+
12
+ constructor(
13
+ options: {
14
+ factory?: OpenFactory | null
15
+ name?: string
16
+ fallback?: Adapter
17
+ } = {},
18
+ ) {
19
+ this.factory = resolveOpenFactory(options.factory)
20
+ this.name = resolveDatabaseName(options.name)
21
+ this.fallback = resolveFallbackAdapter(options.fallback)
22
+ this.dbPromise = null
23
+ }
24
+
25
+ protected req<T>(request: IDBRequest<T>): Promise<T> {
26
+ return new Promise<T>((resolve, reject) => {
27
+ request.onsuccess = () => resolve(request.result)
28
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed."))
29
+ })
30
+ }
31
+
32
+ protected openDb(): Promise<IDBDatabase> {
33
+ if (this.dbPromise) {
34
+ return this.dbPromise
35
+ }
36
+
37
+ if (!this.factory) {
38
+ return Promise.reject(new Error("IndexedDB is not available."))
39
+ }
40
+
41
+ this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {
42
+ const request = this.factory?.open(this.name, 1)
43
+ if (!request) {
44
+ reject(new Error("IndexedDB open request could not be created."))
45
+ return
46
+ }
47
+
48
+ request.onupgradeneeded = () => {
49
+ const db = request.result
50
+ if (!db.objectStoreNames.contains("kv")) {
51
+ db.createObjectStore("kv")
52
+ }
53
+ }
54
+ request.onsuccess = () => resolve(request.result)
55
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB database could not be opened."))
56
+ }).catch((error) => {
57
+ this.dbPromise = null
58
+ throw error
59
+ })
60
+
61
+ return this.dbPromise
62
+ }
63
+
64
+ protected async withStore<T>(mode: IDBTransactionMode, action: (store: IDBObjectStore) => Promise<T>): Promise<T> {
65
+ try {
66
+ const db = await this.openDb()
67
+ const tx = db.transaction("kv", mode)
68
+ const store = tx.objectStore("kv")
69
+ return await action(store)
70
+ } catch (_error) {
71
+ return actionFallback(this.fallback, mode, action)
72
+ }
73
+ }
74
+
75
+ async get(key: string): Promise<unknown>
76
+ async get<T>(key: string): Promise<T | null>
77
+ async get<T = unknown>(key: string): Promise<T | null> {
78
+ return this.withStore("readonly", async (store) => {
79
+ const value = await this.req<unknown>(store.get(key))
80
+ return value === undefined ? null : (value as T)
81
+ })
82
+ }
83
+
84
+ async set<T = unknown>(key: string, value: T): Promise<void> {
85
+ await this.withStore("readwrite", async (store) => {
86
+ await this.req(store.put(value, key))
87
+ return undefined
88
+ })
89
+ }
90
+
91
+ async has(key: string): Promise<boolean> {
92
+ return this.withStore("readonly", async (store) => {
93
+ const count = await this.req<number>(store.count(key))
94
+ return count > 0
95
+ })
96
+ }
97
+
98
+ async delete(key: string): Promise<void> {
99
+ await this.withStore("readwrite", async (store) => {
100
+ await this.req(store.delete(key))
101
+ return undefined
102
+ })
103
+ }
104
+
105
+ async clear(): Promise<void> {
106
+ await this.withStore("readwrite", async (store) => {
107
+ await this.req(store.clear())
108
+ return undefined
109
+ })
110
+ }
111
+
112
+ async keys(): Promise<string[]> {
113
+ return this.withStore("readonly", async (store) => {
114
+ const keys = await this.req<Array<IDBValidKey>>(store.getAllKeys())
115
+ return keys.map(String)
116
+ })
117
+ }
118
+ }
119
+
120
+ function resolveOpenFactory(factory?: OpenFactory | null): OpenFactory | null {
121
+ return factory ?? globalThis.indexedDB ?? null
122
+ }
123
+
124
+ function resolveDatabaseName(name?: string): string {
125
+ return name ?? "ioc-store"
126
+ }
127
+
128
+ function resolveFallbackAdapter(fallback?: Adapter): Adapter {
129
+ return fallback ?? new MemoryStoreAdapter()
130
+ }
131
+
132
+ async function actionFallback<T>(
133
+ fallback: Adapter,
134
+ mode: IDBTransactionMode,
135
+ action: (store: IDBObjectStore) => Promise<T>,
136
+ ): Promise<T> {
137
+ // Keep call sites small: map IDB actions to the same short storage contract.
138
+ if (mode === "readonly") {
139
+ const store = createReadonlyProxy(fallback)
140
+ return action(store as unknown as IDBObjectStore)
141
+ }
142
+
143
+ const store = createReadWriteProxy(fallback)
144
+ return action(store as unknown as IDBObjectStore)
145
+ }
146
+
147
+ function createReadonlyProxy(fallback: Adapter): Partial<IDBObjectStore> {
148
+ return {
149
+ get: (key: IDBValidKey) => wrapPromiseRequest(fallback.get(String(key))),
150
+ count: (key?: IDBValidKey | IDBKeyRange) =>
151
+ wrapPromiseRequest(fallback.has(String(key as IDBValidKey)).then((exists) => (exists ? 1 : 0))),
152
+ getAllKeys: () => wrapPromiseRequest(fallback.keys().then((keys) => keys as Array<IDBValidKey>)),
153
+ }
154
+ }
155
+
156
+ function createReadWriteProxy(fallback: Adapter): Partial<IDBObjectStore> {
157
+ return {
158
+ ...createReadonlyProxy(fallback),
159
+ put: (value: unknown, key?: IDBValidKey) =>
160
+ wrapPromiseRequest(fallback.set(String(key as IDBValidKey), value)) as unknown as IDBRequest<IDBValidKey>,
161
+ delete: (key: IDBValidKey | IDBKeyRange) =>
162
+ wrapPromiseRequest(fallback.delete(String(key as IDBValidKey))) as unknown as IDBRequest<undefined>,
163
+ clear: () => wrapPromiseRequest(fallback.clear()) as unknown as IDBRequest<undefined>,
164
+ }
165
+ }
166
+
167
+ function wrapPromiseRequest<T>(promise: Promise<T>): IDBRequest<T> {
168
+ const request = {
169
+ onsuccess: null as ((this: IDBRequest<T>, ev: Event) => unknown) | null,
170
+ onerror: null as ((this: IDBRequest<T>, ev: Event) => unknown) | null,
171
+ result: undefined as T | undefined,
172
+ error: null as DOMException | null,
173
+ }
174
+
175
+ promise.then(
176
+ (result) => {
177
+ request.result = result
178
+ request.onsuccess?.call(request as unknown as IDBRequest<T>, new Event("success"))
179
+ },
180
+ (error) => {
181
+ request.error = error as DOMException
182
+ request.onerror?.call(request as unknown as IDBRequest<T>, new Event("error"))
183
+ },
184
+ )
185
+
186
+ return request as unknown as IDBRequest<T>
187
+ }
@@ -0,0 +1,48 @@
1
+ import type { Adapter } from "./contract"
2
+
3
+ export default class LocalStorageAdapter implements Adapter {
4
+ protected storage: Storage
5
+
6
+ constructor(storage: Storage = window.localStorage) {
7
+ this.storage = storage
8
+ }
9
+
10
+ async get(key: string): Promise<unknown>
11
+ async get<T>(key: string): Promise<T | null>
12
+ async get<T = unknown>(key: string): Promise<T | null> {
13
+ const value = this.storage.getItem(key)
14
+ if (value === null) {
15
+ return null
16
+ }
17
+
18
+ return JSON.parse(value) as T
19
+ }
20
+
21
+ async set<T = unknown>(key: string, value: T): Promise<void> {
22
+ this.storage.setItem(key, JSON.stringify(value))
23
+ }
24
+
25
+ async has(key: string): Promise<boolean> {
26
+ return this.storage.getItem(key) !== null
27
+ }
28
+
29
+ async delete(key: string): Promise<void> {
30
+ this.storage.removeItem(key)
31
+ }
32
+
33
+ async clear(): Promise<void> {
34
+ this.storage.clear()
35
+ }
36
+
37
+ async keys(): Promise<string[]> {
38
+ const keys: string[] = []
39
+ for (let i = 0; i < this.storage.length; i += 1) {
40
+ const key = this.storage.key(i)
41
+ if (key !== null) {
42
+ keys.push(key)
43
+ }
44
+ }
45
+
46
+ return keys
47
+ }
48
+ }
@@ -0,0 +1,35 @@
1
+ import type { Adapter } from "./contract"
2
+
3
+ export default class MemoryStoreAdapter implements Adapter {
4
+ protected items = new Map<string, unknown>()
5
+
6
+ async get(key: string): Promise<unknown>
7
+ async get<T>(key: string): Promise<T | null>
8
+ async get<T = unknown>(key: string): Promise<T | null> {
9
+ if (!this.items.has(key)) {
10
+ return null
11
+ }
12
+
13
+ return this.items.get(key) as T
14
+ }
15
+
16
+ async set<T = unknown>(key: string, value: T): Promise<void> {
17
+ this.items.set(key, value)
18
+ }
19
+
20
+ async has(key: string): Promise<boolean> {
21
+ return this.items.has(key)
22
+ }
23
+
24
+ async delete(key: string): Promise<void> {
25
+ this.items.delete(key)
26
+ }
27
+
28
+ async clear(): Promise<void> {
29
+ this.items.clear()
30
+ }
31
+
32
+ async keys(): Promise<string[]> {
33
+ return Array.from(this.items.keys())
34
+ }
35
+ }