@take-out/helpers 0.0.31 → 0.0.33

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 (141) hide show
  1. package/dist/cjs/async/useAsync.native.js +1 -1
  2. package/dist/cjs/async/useAsyncEffect.js.map +1 -1
  3. package/dist/cjs/async/useLazyMount.js.map +1 -1
  4. package/dist/cjs/async/useLazyValue.js.map +1 -1
  5. package/dist/cjs/emitter.cjs +1 -1
  6. package/dist/cjs/emitter.js +1 -1
  7. package/dist/cjs/emitter.js.map +1 -1
  8. package/dist/cjs/emitter.native.js +1 -1
  9. package/dist/cjs/emitter.native.js.map +1 -1
  10. package/dist/cjs/index.cjs +1 -2
  11. package/dist/cjs/index.js +1 -2
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/index.native.js +2 -4
  14. package/dist/cjs/index.native.js.map +1 -1
  15. package/dist/cjs/react/createGlobalContext.js.map +1 -1
  16. package/dist/cjs/server/ensureEnv.cjs +1 -1
  17. package/dist/cjs/server/ensureEnv.js +1 -1
  18. package/dist/cjs/server/ensureEnv.js.map +1 -1
  19. package/dist/cjs/server/ensureEnv.native.js +1 -1
  20. package/dist/cjs/server/ensureEnv.native.js.map +1 -1
  21. package/dist/cjs/storage/createStorage.cjs +83 -0
  22. package/dist/cjs/storage/createStorage.js +81 -0
  23. package/dist/cjs/storage/createStorage.js.map +6 -0
  24. package/dist/cjs/storage/createStorage.native.js +118 -0
  25. package/dist/cjs/storage/createStorage.native.js.map +6 -0
  26. package/dist/cjs/storage/driver.cjs +38 -0
  27. package/dist/cjs/storage/driver.js +33 -0
  28. package/dist/cjs/storage/driver.js.map +6 -0
  29. package/dist/cjs/storage/driver.native.js +47 -0
  30. package/dist/cjs/storage/driver.native.js.map +6 -0
  31. package/dist/cjs/storage/index.cjs +30 -0
  32. package/dist/cjs/storage/index.js +24 -0
  33. package/dist/cjs/storage/index.js.map +6 -0
  34. package/dist/cjs/storage/index.native.js +32 -0
  35. package/dist/cjs/storage/index.native.js.map +6 -0
  36. package/dist/cjs/storage/types.cjs +16 -0
  37. package/dist/cjs/storage/types.js +14 -0
  38. package/dist/cjs/storage/types.js.map +6 -0
  39. package/dist/cjs/storage/types.native.js +15 -0
  40. package/dist/cjs/storage/types.native.js.map +6 -0
  41. package/dist/esm/async/useAsync.native.js +1 -1
  42. package/dist/esm/async/useAsyncEffect.js.map +1 -1
  43. package/dist/esm/async/useAsyncEffect.mjs.map +1 -1
  44. package/dist/esm/async/useAsyncEffect.native.js.map +1 -1
  45. package/dist/esm/async/useLazyMount.js.map +1 -1
  46. package/dist/esm/async/useLazyMount.mjs.map +1 -1
  47. package/dist/esm/async/useLazyMount.native.js.map +1 -1
  48. package/dist/esm/async/useLazyValue.js.map +1 -1
  49. package/dist/esm/async/useLazyValue.mjs.map +1 -1
  50. package/dist/esm/async/useLazyValue.native.js.map +1 -1
  51. package/dist/esm/emitter.js +1 -1
  52. package/dist/esm/emitter.js.map +1 -1
  53. package/dist/esm/emitter.mjs +1 -1
  54. package/dist/esm/emitter.mjs.map +1 -1
  55. package/dist/esm/emitter.native.js +1 -1
  56. package/dist/esm/emitter.native.js.map +1 -1
  57. package/dist/esm/index.js +1 -2
  58. package/dist/esm/index.js.map +1 -1
  59. package/dist/esm/index.mjs +1 -2
  60. package/dist/esm/index.mjs.map +1 -1
  61. package/dist/esm/index.native.js +1 -2
  62. package/dist/esm/index.native.js.map +1 -1
  63. package/dist/esm/react/createGlobalContext.js.map +1 -1
  64. package/dist/esm/react/createGlobalContext.mjs.map +1 -1
  65. package/dist/esm/react/createGlobalContext.native.js.map +1 -1
  66. package/dist/esm/server/ensureEnv.js +1 -1
  67. package/dist/esm/server/ensureEnv.js.map +1 -1
  68. package/dist/esm/server/ensureEnv.mjs +1 -1
  69. package/dist/esm/server/ensureEnv.mjs.map +1 -1
  70. package/dist/esm/server/ensureEnv.native.js +1 -1
  71. package/dist/esm/server/ensureEnv.native.js.map +1 -1
  72. package/dist/esm/storage/createStorage.js +65 -0
  73. package/dist/esm/storage/createStorage.js.map +6 -0
  74. package/dist/esm/storage/createStorage.mjs +59 -0
  75. package/dist/esm/storage/createStorage.mjs.map +1 -0
  76. package/dist/esm/storage/createStorage.native.js +94 -0
  77. package/dist/esm/storage/createStorage.native.js.map +1 -0
  78. package/dist/esm/storage/driver.js +17 -0
  79. package/dist/esm/storage/driver.js.map +6 -0
  80. package/dist/esm/storage/driver.mjs +14 -0
  81. package/dist/esm/storage/driver.mjs.map +1 -0
  82. package/dist/esm/storage/driver.native.js +22 -0
  83. package/dist/esm/storage/driver.native.js.map +1 -0
  84. package/dist/esm/storage/index.js +9 -0
  85. package/dist/esm/storage/index.js.map +6 -0
  86. package/dist/esm/storage/index.mjs +4 -0
  87. package/dist/esm/storage/index.mjs.map +1 -0
  88. package/dist/esm/storage/index.native.js +4 -0
  89. package/dist/esm/storage/index.native.js.map +1 -0
  90. package/dist/esm/storage/types.js +1 -0
  91. package/dist/esm/storage/types.js.map +6 -0
  92. package/dist/esm/storage/types.mjs +2 -0
  93. package/dist/esm/storage/types.mjs.map +1 -0
  94. package/dist/esm/storage/types.native.js +2 -0
  95. package/dist/esm/storage/types.native.js.map +1 -0
  96. package/package.json +6 -6
  97. package/src/async/asyncContext.ts +2 -2
  98. package/src/async/useAsync.ts +1 -1
  99. package/src/async/useAsyncEffect.ts +3 -2
  100. package/src/async/useLazyMount.ts +3 -1
  101. package/src/async/useLazyValue.ts +1 -0
  102. package/src/emitter.tsx +8 -7
  103. package/src/index.ts +3 -2
  104. package/src/object/object.ts +1 -1
  105. package/src/react/createGlobalContext.ts +1 -0
  106. package/src/server/ensureEnv.ts +1 -1
  107. package/src/storage/createStorage.ts +141 -0
  108. package/src/storage/driver.ts +20 -0
  109. package/src/storage/index.ts +4 -0
  110. package/src/storage/types.ts +6 -0
  111. package/types/async/asyncContext.d.ts.map +1 -1
  112. package/types/async/useAsync.d.ts.map +1 -1
  113. package/types/async/useAsyncEffect.d.ts.map +2 -2
  114. package/types/async/useLazyMount.d.ts +1 -1
  115. package/types/async/useLazyMount.d.ts.map +2 -2
  116. package/types/async/useLazyValue.d.ts.map +2 -2
  117. package/types/emitter.d.ts.map +2 -2
  118. package/types/index.d.ts +2 -2
  119. package/types/index.d.ts.map +2 -2
  120. package/types/object/object.d.ts +1 -1
  121. package/types/object/object.d.ts.map +2 -2
  122. package/types/react/createGlobalContext.d.ts.map +2 -2
  123. package/types/server/ensureEnv.d.ts.map +1 -1
  124. package/types/storage/createStorage.d.ts +65 -0
  125. package/types/storage/createStorage.d.ts.map +18 -0
  126. package/types/storage/driver.d.ts +5 -0
  127. package/types/storage/driver.d.ts.map +13 -0
  128. package/types/storage/index.d.ts +6 -0
  129. package/types/storage/index.d.ts.map +11 -0
  130. package/types/storage/types.d.ts +8 -0
  131. package/types/storage/types.d.ts.map +14 -0
  132. package/src/array/fuzzy.ts +0 -39
  133. package/src/browser/localStorage.ts +0 -311
  134. package/types/ensure/catchEnsureErrors.d.ts +0 -3
  135. package/types/ensure/catchEnsureErrors.d.ts.map +0 -13
  136. package/types/ensure/ensureSingleton.d.ts +0 -7
  137. package/types/ensure/ensureSingleton.d.ts.map +0 -14
  138. package/types/files/download.d.ts +0 -4
  139. package/types/files/download.d.ts.map +0 -16
  140. package/types/global/globalContext.d.ts +0 -6
  141. package/types/global/globalContext.d.ts.map +0 -14
@@ -0,0 +1,65 @@
1
+ /**
2
+ * namespaced storage interface with JSON serialization
3
+ * @template K - key type (string literal union for type-safe keys)
4
+ * @template V - value type (automatically JSON serialized/deserialized)
5
+ */
6
+ export interface Storage<
7
+ K extends string = string,
8
+ V = unknown
9
+ > {
10
+ /** get a JSON-parsed value by key */
11
+ get(key: K): V | undefined;
12
+ /** set a value (JSON serialized) */
13
+ set(key: K, value: V): void;
14
+ /** remove a key */
15
+ remove(key: K): void;
16
+ /** check if key exists */
17
+ has(key: K): boolean;
18
+ /** get all keys in this namespace */
19
+ keys(): K[];
20
+ /** remove all keys in this namespace */
21
+ clear(): void;
22
+ /** get raw string value (no JSON parsing) - localStorage compatible */
23
+ getItem(key: K): string | null;
24
+ /** set raw string value (no JSON serialization) - localStorage compatible */
25
+ setItem(key: K, value: string): void;
26
+ }
27
+ /**
28
+ * create a namespaced storage instance
29
+ * @param namespace - unique prefix for all keys (throws if already used)
30
+ * @returns storage instance with get/set (JSON) and getItem/setItem (raw) methods
31
+ * @example
32
+ * const store = createStorage<'token' | 'user', string>('auth')
33
+ * store.set('token', 'abc123')
34
+ * store.get('token') // 'abc123'
35
+ */
36
+ export declare function createStorage<
37
+ K extends string,
38
+ V
39
+ >(namespace: string): Storage<K, V>;
40
+ /**
41
+ * single-value storage interface
42
+ * @template T - value type (automatically JSON serialized/deserialized)
43
+ */
44
+ export interface StorageValue<T> {
45
+ /** get the stored value */
46
+ get(): T | undefined;
47
+ /** set the value */
48
+ set(value: T): void;
49
+ /** remove the value */
50
+ remove(): void;
51
+ /** check if value exists */
52
+ has(): boolean;
53
+ }
54
+ /**
55
+ * create a single-value storage (wrapper around createStorage)
56
+ * @param key - unique storage key
57
+ * @returns storage value instance
58
+ * @example
59
+ * const token = createStorageValue<string>('auth-token')
60
+ * token.set('abc123')
61
+ * token.get() // 'abc123'
62
+ */
63
+ export declare function createStorageValue<T>(key: string): StorageValue<T>;
64
+
65
+ //# sourceMappingURL=createStorage.d.ts.map
@@ -0,0 +1,18 @@
1
+ {
2
+ "mappings": ";;;;;AASA,iBAAiB;CAAQ;CAA2B;EAAa;;CAE/D,IAAIA,KAAK,IAAI;;CAEb,IAAIA,KAAK,GAAGC,OAAO;;CAEnB,OAAOD,KAAK;;CAEZ,IAAIA,KAAK;;CAET,QAAQ;;CAER;;CAEA,QAAQA,KAAK;;CAEb,QAAQA,KAAK,GAAGE;AACjB;;;;;;;;;;AAWD,OAAO,iBAAS;CAAc;CAAkB;EAAGC,oBAAoB,QAAQ,GAAG;;;;;AA2ElF,iBAAiB,aAAa,GAAG;;CAE/B,OAAO;;CAEP,IAAIC,OAAO;;CAEX;;CAEA;AACD;;;;;;;;;;AAWD,OAAO,iBAAS,mBAAmB,GAAGC,cAAc,aAAa",
3
+ "names": [
4
+ "key: K",
5
+ "value: V",
6
+ "value: string",
7
+ "namespace: string",
8
+ "value: T",
9
+ "key: string"
10
+ ],
11
+ "sources": [
12
+ "src/storage/createStorage.ts"
13
+ ],
14
+ "sourcesContent": [
15
+ "import { getStorageDriver } from './driver'\n\nconst namespaces = new Set<string>()\n\n/**\n * namespaced storage interface with JSON serialization\n * @template K - key type (string literal union for type-safe keys)\n * @template V - value type (automatically JSON serialized/deserialized)\n */\nexport interface Storage<K extends string = string, V = unknown> {\n /** get a JSON-parsed value by key */\n get(key: K): V | undefined\n /** set a value (JSON serialized) */\n set(key: K, value: V): void\n /** remove a key */\n remove(key: K): void\n /** check if key exists */\n has(key: K): boolean\n /** get all keys in this namespace */\n keys(): K[]\n /** remove all keys in this namespace */\n clear(): void\n /** get raw string value (no JSON parsing) - localStorage compatible */\n getItem(key: K): string | null\n /** set raw string value (no JSON serialization) - localStorage compatible */\n setItem(key: K, value: string): void\n}\n\n/**\n * create a namespaced storage instance\n * @param namespace - unique prefix for all keys (throws if already used)\n * @returns storage instance with get/set (JSON) and getItem/setItem (raw) methods\n * @example\n * const store = createStorage<'token' | 'user', string>('auth')\n * store.set('token', 'abc123')\n * store.get('token') // 'abc123'\n */\nexport function createStorage<K extends string, V>(namespace: string): Storage<K, V> {\n if (namespaces.has(namespace)) {\n throw new Error(`storage namespace already exists: ${namespace}`)\n }\n namespaces.add(namespace)\n\n const prefix = `${namespace}:`\n const prefixKey = (key: string) => `${prefix}${key}`\n\n return {\n get(key: K): V | undefined {\n const driver = getStorageDriver()\n if (!driver) return undefined\n const raw = driver.getItem(prefixKey(key))\n if (raw == null) return undefined\n try {\n return JSON.parse(raw)\n } catch {\n return undefined\n }\n },\n\n set(key: K, value: V): void {\n const driver = getStorageDriver()\n if (!driver) return\n driver.setItem(prefixKey(key), JSON.stringify(value))\n },\n\n remove(key: K): void {\n const driver = getStorageDriver()\n if (!driver) return\n driver.removeItem(prefixKey(key))\n },\n\n has(key: K): boolean {\n const driver = getStorageDriver()\n if (!driver) return false\n return driver.getItem(prefixKey(key)) != null\n },\n\n keys(): K[] {\n const driver = getStorageDriver()\n if (!driver) return []\n return driver\n .getAllKeys()\n .filter((k) => k.startsWith(prefix))\n .map((k) => k.slice(prefix.length) as K)\n },\n\n clear(): void {\n const driver = getStorageDriver()\n if (!driver) return\n for (const key of this.keys()) {\n driver.removeItem(prefixKey(key))\n }\n },\n\n getItem(key: K): string | null {\n const driver = getStorageDriver()\n if (!driver) return null\n return driver.getItem(prefixKey(key)) ?? null\n },\n\n setItem(key: K, value: string): void {\n const driver = getStorageDriver()\n if (!driver) return\n driver.setItem(prefixKey(key), value)\n },\n }\n}\n\n/**\n * single-value storage interface\n * @template T - value type (automatically JSON serialized/deserialized)\n */\nexport interface StorageValue<T> {\n /** get the stored value */\n get(): T | undefined\n /** set the value */\n set(value: T): void\n /** remove the value */\n remove(): void\n /** check if value exists */\n has(): boolean\n}\n\n/**\n * create a single-value storage (wrapper around createStorage)\n * @param key - unique storage key\n * @returns storage value instance\n * @example\n * const token = createStorageValue<string>('auth-token')\n * token.set('abc123')\n * token.get() // 'abc123'\n */\nexport function createStorageValue<T>(key: string): StorageValue<T> {\n const storage = createStorage<'value', T>(`_v:${key}`)\n return {\n get: (): T | undefined => storage.get('value'),\n set: (value: T): void => storage.set('value', value),\n remove: (): void => storage.remove('value'),\n has: (): boolean => storage.has('value'),\n }\n}\n"
16
+ ],
17
+ "version": 3
18
+ }
@@ -0,0 +1,5 @@
1
+ import type { StorageDriver } from "./types";
2
+ export declare function setStorageDriver(d: StorageDriver): void;
3
+ export declare function getStorageDriver(): StorageDriver | null;
4
+
5
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAAA,cAAc,qBAAqB,SAAS;AAI5C,OAAO,iBAAS,iBAAiBA,GAAG;AAIpC,OAAO,iBAAS,oBAAoB",
3
+ "names": [
4
+ "d: StorageDriver"
5
+ ],
6
+ "sources": [
7
+ "src/storage/driver.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "import type { StorageDriver } from './types'\n\nlet driver: StorageDriver | null = null\n\nexport function setStorageDriver(d: StorageDriver): void {\n driver = d\n}\n\nexport function getStorageDriver(): StorageDriver | null {\n if (driver) return driver\n if (typeof localStorage !== 'undefined') {\n return {\n getItem: (key) => localStorage.getItem(key),\n setItem: (key, value) => localStorage.setItem(key, value),\n removeItem: (key) => localStorage.removeItem(key),\n getAllKeys: () => Object.keys(localStorage),\n }\n }\n return null\n}\n"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,6 @@
1
+ export { createStorage, createStorageValue } from "./createStorage";
2
+ export type { Storage, StorageValue } from "./createStorage";
3
+ export { getStorageDriver, setStorageDriver } from "./driver";
4
+ export type { StorageDriver } from "./types";
5
+
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,SAAS,eAAe,0BAA0B;AAClD,cAAc,SAAS,oBAAoB;AAC3C,SAAS,kBAAkB,wBAAwB;AACnD,cAAc,qBAAqB",
3
+ "names": [],
4
+ "sources": [
5
+ "src/storage/index.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "export { createStorage, createStorageValue } from './createStorage'\nexport type { Storage, StorageValue } from './createStorage'\nexport { getStorageDriver, setStorageDriver } from './driver'\nexport type { StorageDriver } from './types'\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,8 @@
1
+ export interface StorageDriver {
2
+ getItem(key: string): string | null | undefined;
3
+ setItem(key: string, value: string): void;
4
+ removeItem(key: string): void;
5
+ getAllKeys(): string[];
6
+ }
7
+
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,14 @@
1
+ {
2
+ "mappings": "AAAA,iBAAiB,cAAc;CAC7B,QAAQA;CACR,QAAQA,aAAaC;CACrB,WAAWD;CACX;AACD",
3
+ "names": [
4
+ "key: string",
5
+ "value: string"
6
+ ],
7
+ "sources": [
8
+ "src/storage/types.ts"
9
+ ],
10
+ "sourcesContent": [
11
+ "export interface StorageDriver {\n getItem(key: string): string | null | undefined\n setItem(key: string, value: string): void\n removeItem(key: string): void\n getAllKeys(): string[]\n}\n"
12
+ ],
13
+ "version": 3
14
+ }
@@ -1,39 +0,0 @@
1
- import Fuse, { type FuseSearchOptions, type IFuseOptions } from 'fuse.js'
2
-
3
- export type FuzzyOptions = IFuseOptions<unknown>
4
-
5
- // fuse does some insane staleness in getFn that doesn't make sense so pulled it all the way out here
6
- let lastSearch = ''
7
-
8
- export function createFuzzy<
9
- A extends Record<string, any>,
10
- Results extends A[] | readonly A[],
11
- >(
12
- items: Results,
13
- options?: IFuseOptions<A>
14
- ): {
15
- search: (val: string, options?: FuseSearchOptions) => Results
16
- } {
17
- const fuse = new Fuse(items, {
18
- isCaseSensitive: false,
19
- threshold: 0.5,
20
- ...options,
21
- getFn(obj, path, ...rest) {
22
- if (obj.skipFuzzy) {
23
- return lastSearch
24
- }
25
- return Fuse.config.getFn(obj, path)
26
- },
27
- })
28
-
29
- return {
30
- search: (val: string, options?: FuseSearchOptions): Results => {
31
- if (!val) {
32
- return items
33
- }
34
- lastSearch = val
35
- const results = fuse.search(val, options).map((result) => result.item)
36
- return results as Results
37
- },
38
- }
39
- }
@@ -1,311 +0,0 @@
1
- const created = new Set<string>()
2
-
3
- const STORAGE_TRACKING_KEY = '__localStorage_tracking__'
4
- const STORAGE_ORDER_KEY = '__localStorage_order__'
5
-
6
- interface StorageTracking {
7
- [key: string]: number // key -> size in bytes
8
- }
9
-
10
- interface LocalStorageOptions {
11
- synced?: boolean
12
- disableSetOnBlur?: boolean
13
-
14
- /**
15
- * Limit in kB
16
- */
17
- storageLimit?: number
18
- }
19
-
20
- export function createLocalStorage<K extends string, V>(
21
- name: string,
22
- { synced = false, disableSetOnBlur = false, storageLimit }: LocalStorageOptions = {}
23
- ) {
24
- if (created.has(name)) {
25
- throw new Error(`Already defined this localStorage namespace: ${name}`)
26
- }
27
- created.add(name)
28
-
29
- let isWindowActive = true
30
- if (typeof window !== 'undefined' && disableSetOnBlur) {
31
- const handleFocus = () => {
32
- isWindowActive = true
33
- }
34
- const handleBlur = () => {
35
- isWindowActive = false
36
- }
37
-
38
- window.addEventListener('focus', handleFocus)
39
- window.addEventListener('blur', handleBlur)
40
-
41
- isWindowActive = document.hasFocus()
42
- }
43
-
44
- const listeners = new Map<string, Set<(value: any) => void>>()
45
-
46
- if (synced && typeof window !== 'undefined') {
47
- const handleStorageChange = (event: StorageEvent) => {
48
- if (event.key?.startsWith(`${name}:`) && event.storageArea === localStorage) {
49
- const key = event.key.slice(name.length + 1)
50
- const newValue = event.newValue ? JSON.parse(event.newValue) : undefined
51
- const keyListeners = listeners.get(key)
52
- if (keyListeners) {
53
- keyListeners.forEach((listener) => listener(newValue))
54
- }
55
- }
56
- }
57
-
58
- window.addEventListener('storage', handleStorageChange)
59
- }
60
-
61
- const getFullKey = (key: string) => `${name}:${key}`
62
-
63
- return {
64
- get: (key: K): V | undefined => {
65
- if (typeof localStorage === 'undefined') {
66
- console.warn(`No localStorage`)
67
- return
68
- }
69
-
70
- const fullKey = getFullKey(key)
71
- const item = localStorage.getItem(fullKey)
72
- if (item && storageLimit) {
73
- updateKeyAccess(fullKey)
74
- }
75
- return item ? JSON.parse(item) : undefined
76
- },
77
-
78
- set: (key: K, value: V): void => {
79
- if (typeof localStorage === 'undefined') {
80
- console.warn(`No localStorage`)
81
- return
82
- }
83
-
84
- if (disableSetOnBlur && !isWindowActive) {
85
- return
86
- }
87
-
88
- const fullKey = getFullKey(key)
89
- const serializedValue = JSON.stringify(value)
90
-
91
- if (storageLimit) {
92
- const newSize = getStringSizeInBytes(serializedValue)
93
- enforceStorageLimit(fullKey, newSize, storageLimit)
94
- const tracking = getStorageTracking()
95
- tracking[fullKey] = newSize
96
- setStorageTracking(tracking)
97
- updateKeyAccess(fullKey)
98
- }
99
-
100
- localStorage.setItem(fullKey, serializedValue)
101
- },
102
-
103
- remove: (key: K): void => {
104
- if (typeof localStorage === 'undefined') {
105
- console.warn(`No localStorage`)
106
- return
107
- }
108
-
109
- // don't remove if window is not active and disablesetonblur is true
110
- if (disableSetOnBlur && !isWindowActive) {
111
- return
112
- }
113
-
114
- const fullKey = getFullKey(key)
115
- localStorage.removeItem(fullKey)
116
-
117
- // clean up tracking data if storage limit is enabled
118
- if (storageLimit) {
119
- const tracking = getStorageTracking()
120
- delete tracking[fullKey]
121
- setStorageTracking(tracking)
122
-
123
- const order = getStorageOrder()
124
- const index = order.indexOf(fullKey)
125
- if (index > -1) {
126
- order.splice(index, 1)
127
- setStorageOrder(order)
128
- }
129
- }
130
- },
131
-
132
- exists: (key: K): boolean => {
133
- const fullKey = getFullKey(key)
134
- return localStorage.getItem(fullKey) != null
135
- },
136
-
137
- clear: (): void => {
138
- if (typeof localStorage === 'undefined') {
139
- console.warn(`No localStorage`)
140
- return
141
- }
142
-
143
- // Remove all keys with this namespace
144
- const keysToRemove: string[] = []
145
- for (let i = 0; i < localStorage.length; i++) {
146
- const key = localStorage.key(i)
147
- if (key?.startsWith(`${name}:`)) {
148
- keysToRemove.push(key)
149
- }
150
- }
151
-
152
- keysToRemove.forEach((key) => localStorage.removeItem(key))
153
-
154
- // Clean up tracking data
155
- if (storageLimit) {
156
- const tracking = getStorageTracking()
157
- const order = getStorageOrder()
158
-
159
- keysToRemove.forEach((key) => {
160
- delete tracking[key]
161
- const index = order.indexOf(key)
162
- if (index > -1) {
163
- order.splice(index, 1)
164
- }
165
- })
166
-
167
- setStorageTracking(tracking)
168
- setStorageOrder(order)
169
- }
170
- },
171
-
172
- subscribe: (key: string, listener: (value: V | undefined) => void): (() => void) => {
173
- if (!synced) {
174
- console.warn(`Subscribe only works when synced option is enabled`)
175
- return () => {}
176
- }
177
-
178
- if (!listeners.has(key)) {
179
- listeners.set(key, new Set())
180
- }
181
- const keyListeners = listeners.get(key)!
182
- keyListeners.add(listener)
183
-
184
- return () => {
185
- keyListeners.delete(listener)
186
- if (keyListeners.size === 0) {
187
- listeners.delete(key)
188
- }
189
- }
190
- },
191
-
192
- keys: (): string[] => {
193
- if (typeof localStorage === 'undefined') {
194
- return []
195
- }
196
-
197
- const keys: string[] = []
198
- const prefix = `${name}:`
199
-
200
- for (let i = 0; i < localStorage.length; i++) {
201
- const key = localStorage.key(i)
202
- if (key?.startsWith(prefix)) {
203
- keys.push(key.slice(prefix.length))
204
- }
205
- }
206
-
207
- return keys
208
- },
209
- }
210
- }
211
-
212
- export function createLocalStorageValue<T>(
213
- key: string,
214
- options: LocalStorageOptions = {}
215
- ) {
216
- const storage = createLocalStorage<string, T>(`single:${key}`, options)
217
- const storageKey = 'value'
218
-
219
- return {
220
- get: (): T | undefined => {
221
- return storage.get(storageKey)
222
- },
223
-
224
- set: (value: T): void => {
225
- storage.set(storageKey, value)
226
- },
227
-
228
- remove: (): void => {
229
- storage.remove(storageKey)
230
- },
231
-
232
- exists: (): boolean => {
233
- return storage.exists(storageKey)
234
- },
235
-
236
- clear: (): void => {
237
- storage.clear()
238
- },
239
-
240
- subscribe: (listener: (value: T | undefined) => void): (() => void) => {
241
- return storage.subscribe(storageKey, listener)
242
- },
243
- }
244
- }
245
-
246
- function getStorageTracking(): StorageTracking {
247
- if (typeof localStorage === 'undefined') return {}
248
- const tracking = localStorage.getItem(STORAGE_TRACKING_KEY)
249
- return tracking ? JSON.parse(tracking) : {}
250
- }
251
-
252
- function setStorageTracking(tracking: StorageTracking): void {
253
- if (typeof localStorage === 'undefined') return
254
- localStorage.setItem(STORAGE_TRACKING_KEY, JSON.stringify(tracking))
255
- }
256
-
257
- function getStorageOrder(): string[] {
258
- if (typeof localStorage === 'undefined') return []
259
- const order = localStorage.getItem(STORAGE_ORDER_KEY)
260
- return order ? JSON.parse(order) : []
261
- }
262
-
263
- function setStorageOrder(order: string[]): void {
264
- if (typeof localStorage === 'undefined') return
265
- localStorage.setItem(STORAGE_ORDER_KEY, JSON.stringify(order))
266
- }
267
-
268
- function getStringSizeInBytes(str: string): number {
269
- return new Blob([str]).size
270
- }
271
-
272
- function updateKeyAccess(key: string): void {
273
- const order = getStorageOrder()
274
- const index = order.indexOf(key)
275
- if (index > -1) {
276
- order.splice(index, 1)
277
- }
278
- order.push(key) // move to end (most recent)
279
- setStorageOrder(order)
280
- }
281
-
282
- function enforceStorageLimit(newKey: string, newSize: number, limitKB: number): void {
283
- const tracking = getStorageTracking()
284
- const order = getStorageOrder()
285
-
286
- let totalSize = Object.values(tracking).reduce((sum, size) => sum + size, 0)
287
- totalSize += newSize // add the new item size
288
-
289
- const limitBytes = limitKB * 1024
290
-
291
- while (totalSize > limitBytes && order.length > 0) {
292
- const oldestKey = order.shift()!
293
-
294
- if (
295
- oldestKey === newKey ||
296
- oldestKey === STORAGE_TRACKING_KEY ||
297
- oldestKey === STORAGE_ORDER_KEY
298
- ) {
299
- continue
300
- }
301
-
302
- const oldSize = tracking[oldestKey] || 0
303
- totalSize -= oldSize
304
-
305
- localStorage.removeItem(oldestKey)
306
- delete tracking[oldestKey]
307
- }
308
-
309
- setStorageTracking(tracking)
310
- setStorageOrder(order)
311
- }
@@ -1,3 +0,0 @@
1
- export declare function catchEnsureErrors<T extends Function>(fn: T): T;
2
-
3
- //# sourceMappingURL=catchEnsureErrors.d.ts.map
@@ -1,13 +0,0 @@
1
- {
2
- "mappings": "AAEA,OAAO,iBAAS,kBAAkB,UAAU,UAAUA,IAAI,IAAI",
3
- "names": [
4
- "fn: T"
5
- ],
6
- "sources": [
7
- "src/ensure/catchEnsureErrors.ts"
8
- ],
9
- "sourcesContent": [
10
- "import { EnsureError } from '../error/errors'\n\nexport function catchEnsureErrors<T extends Function>(fn: T): T {\n return ((...args: any[]) => {\n try {\n return fn(...args)\n } catch (error) {\n if (error instanceof EnsureError) {\n console.error(`EnsureError: ${error.message}`)\n return\n }\n throw error\n }\n }) as unknown as T\n}\n"
11
- ],
12
- "version": 3
13
- }
@@ -1,7 +0,0 @@
1
- // Ensures a singleton value is stored on `globalThis` using a symbol key.
2
- // This creates a stable singleton that survives hot-reloads during development.
3
- // We store values on globalThis using `Symbol.for` keys to avoid collisions
4
- // across independently bundled modules.
5
- export declare function ensureSingleton<T>(key: string, factory: () => T): T;
6
-
7
- //# sourceMappingURL=ensureSingleton.d.ts.map
@@ -1,14 +0,0 @@
1
- {
2
- "mappings": ";;;;AAMA,OAAO,iBAAS,gBAAgB,GAAGA,aAAaC,eAAe,IAAI",
3
- "names": [
4
- "key: string",
5
- "factory: () => T"
6
- ],
7
- "sources": [
8
- "src/ensure/ensureSingleton.ts"
9
- ],
10
- "sourcesContent": [
11
- "// Ensures a singleton value is stored on `globalThis` using a symbol key.\n// This creates a stable singleton that survives hot-reloads during development.\n\n// We store values on globalThis using `Symbol.for` keys to avoid collisions\n// across independently bundled modules.\n\nexport function ensureSingleton<T>(key: string, factory: () => T): T {\n const symbolKey = Symbol.for(key)\n const g = globalThis as Record<symbol, unknown>\n\n if (!g[symbolKey]) {\n g[symbolKey] = factory()\n }\n\n return g[symbolKey] as T\n}\n"
12
- ],
13
- "version": 3
14
- }
@@ -1,4 +0,0 @@
1
- export declare function downloadFile(data: string, filename: string, mimeType?: string);
2
- export declare function downloadFromUrl(url: string, filename?: string);
3
-
4
- //# sourceMappingURL=download.d.ts.map
@@ -1,16 +0,0 @@
1
- {
2
- "mappings": "AAuCA,OAAO,iBAAe,aACpBA,cACAC,kBACA;AAYF,OAAO,iBAAe,gBAAgBC,aAAaC",
3
- "names": [
4
- "data: string",
5
- "filename: string",
6
- "url: string",
7
- "filename?: string"
8
- ],
9
- "sources": [
10
- "src/files/download.ts"
11
- ],
12
- "sourcesContent": [
13
- "import { isTauri } from '../constants'\n\nasync function saveInTauri(data: string | Blob, filename: string) {\n // @ts-ignore - tauri plugins only available in tauri environment\n const { save } = await import('@tauri-apps/plugin-dialog')\n // @ts-ignore - tauri plugins only available in tauri environment\n const { writeTextFile, writeBinaryFile } = await import('@tauri-apps/plugin-fs')\n\n const extension = filename.split('.').pop() || ''\n const filePath = await save({\n defaultPath: filename,\n filters: [\n {\n name: `${extension.toUpperCase()} files`,\n extensions: [extension],\n },\n ],\n })\n\n if (filePath) {\n if (typeof data === 'string') {\n await writeTextFile(filePath, data)\n } else {\n const arrayBuffer = await data.arrayBuffer()\n await writeBinaryFile(filePath, new Uint8Array(arrayBuffer))\n }\n }\n}\n\nfunction downloadInBrowser(url: string, filename: string) {\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n a.target = '_blank'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n}\n\nexport async function downloadFile(\n data: string,\n filename: string,\n mimeType = 'application/json'\n) {\n if (isTauri) {\n await saveInTauri(data, filename)\n } else {\n const blob = new Blob([data], { type: mimeType })\n const url = URL.createObjectURL(blob)\n downloadInBrowser(url, filename)\n URL.revokeObjectURL(url)\n }\n}\n\nexport async function downloadFromUrl(url: string, filename?: string) {\n const resolvedFilename = filename || url.split('/').pop() || 'download'\n\n if (isTauri) {\n try {\n const response = await fetch(url)\n const blob = await response.blob()\n await saveInTauri(blob, resolvedFilename)\n } catch (error) {\n console.error('Failed to download file:', error)\n downloadInBrowser(url, resolvedFilename)\n }\n } else {\n downloadInBrowser(url, resolvedFilename)\n }\n}\n"
14
- ],
15
- "version": 3
16
- }
@@ -1,6 +0,0 @@
1
- import { type Context } from "react";
2
- // create or retrieve a React context that is stored on `globalThis`.
3
- // this ensures a stable singleton that survives hot-reloads during development.
4
- export declare function createGlobalContext<T>(key: string, defaultValue: T): Context<T>;
5
-
6
- //# sourceMappingURL=globalContext.d.ts.map
@@ -1,14 +0,0 @@
1
- {
2
- "mappings": "AAAA,cAAc,eAA8B,OAAO;;;AAMnD,OAAO,iBAAS,oBAAoB,GAAGA,aAAaC,cAAc,IAAI,QAAQ",
3
- "names": [
4
- "key: string",
5
- "defaultValue: T"
6
- ],
7
- "sources": [
8
- "src/global/globalContext.ts"
9
- ],
10
- "sourcesContent": [
11
- "import { type Context, createContext } from 'react'\nimport { ensureSingleton } from '../ensure/ensureSingleton'\n\n// create or retrieve a React context that is stored on `globalThis`.\n// this ensures a stable singleton that survives hot-reloads during development.\n\nexport function createGlobalContext<T>(key: string, defaultValue: T): Context<T> {\n return ensureSingleton(key, () => createContext<T>(defaultValue))\n}\n"
12
- ],
13
- "version": 3
14
- }