@victorylabs/params 0.1.0

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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/chunk-43PUAYQP.js +573 -0
  4. package/dist/chunk-43PUAYQP.js.map +1 -0
  5. package/dist/chunk-4T4THPFW.js +100 -0
  6. package/dist/chunk-4T4THPFW.js.map +1 -0
  7. package/dist/chunk-5NSLHAHG.js +26 -0
  8. package/dist/chunk-5NSLHAHG.js.map +1 -0
  9. package/dist/chunk-NHCH2WKC.js +96 -0
  10. package/dist/chunk-NHCH2WKC.js.map +1 -0
  11. package/dist/chunk-NUO3GOXV.js +72 -0
  12. package/dist/chunk-NUO3GOXV.js.map +1 -0
  13. package/dist/devtools.cjs +41 -0
  14. package/dist/devtools.cjs.map +1 -0
  15. package/dist/devtools.d.cts +45 -0
  16. package/dist/devtools.d.ts +45 -0
  17. package/dist/devtools.js +16 -0
  18. package/dist/devtools.js.map +1 -0
  19. package/dist/index.cjs +777 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.cts +133 -0
  22. package/dist/index.d.ts +133 -0
  23. package/dist/index.js +83 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/integrations/forms-reverse.cjs +777 -0
  26. package/dist/integrations/forms-reverse.cjs.map +1 -0
  27. package/dist/integrations/forms-reverse.d.cts +32 -0
  28. package/dist/integrations/forms-reverse.d.ts +32 -0
  29. package/dist/integrations/forms-reverse.js +73 -0
  30. package/dist/integrations/forms-reverse.js.map +1 -0
  31. package/dist/integrations/forms.cjs +771 -0
  32. package/dist/integrations/forms.cjs.map +1 -0
  33. package/dist/integrations/forms.d.cts +25 -0
  34. package/dist/integrations/forms.d.ts +25 -0
  35. package/dist/integrations/forms.js +65 -0
  36. package/dist/integrations/forms.js.map +1 -0
  37. package/dist/params-store-Cgbtn53j.d.cts +115 -0
  38. package/dist/params-store-CguA9-yr.d.ts +115 -0
  39. package/dist/react.cjs +910 -0
  40. package/dist/react.cjs.map +1 -0
  41. package/dist/react.d.cts +75 -0
  42. package/dist/react.d.ts +75 -0
  43. package/dist/react.js +202 -0
  44. package/dist/react.js.map +1 -0
  45. package/dist/snapshot.cjs +75 -0
  46. package/dist/snapshot.cjs.map +1 -0
  47. package/dist/snapshot.d.cts +42 -0
  48. package/dist/snapshot.d.ts +42 -0
  49. package/dist/snapshot.js +42 -0
  50. package/dist/snapshot.js.map +1 -0
  51. package/dist/storage/compose.cjs +196 -0
  52. package/dist/storage/compose.cjs.map +1 -0
  53. package/dist/storage/compose.d.cts +35 -0
  54. package/dist/storage/compose.d.ts +35 -0
  55. package/dist/storage/compose.js +123 -0
  56. package/dist/storage/compose.js.map +1 -0
  57. package/dist/storage/cookie.cjs +136 -0
  58. package/dist/storage/cookie.cjs.map +1 -0
  59. package/dist/storage/cookie.d.cts +57 -0
  60. package/dist/storage/cookie.d.ts +57 -0
  61. package/dist/storage/cookie.js +111 -0
  62. package/dist/storage/cookie.js.map +1 -0
  63. package/dist/storage/idb.cjs +144 -0
  64. package/dist/storage/idb.cjs.map +1 -0
  65. package/dist/storage/idb.d.cts +31 -0
  66. package/dist/storage/idb.d.ts +31 -0
  67. package/dist/storage/idb.js +119 -0
  68. package/dist/storage/idb.js.map +1 -0
  69. package/dist/storage/local.cjs +121 -0
  70. package/dist/storage/local.cjs.map +1 -0
  71. package/dist/storage/local.d.cts +23 -0
  72. package/dist/storage/local.d.ts +23 -0
  73. package/dist/storage/local.js +9 -0
  74. package/dist/storage/local.js.map +1 -0
  75. package/dist/storage/server.cjs +158 -0
  76. package/dist/storage/server.cjs.map +1 -0
  77. package/dist/storage/server.d.cts +57 -0
  78. package/dist/storage/server.d.ts +57 -0
  79. package/dist/storage/server.js +133 -0
  80. package/dist/storage/server.js.map +1 -0
  81. package/dist/storage/session.cjs +123 -0
  82. package/dist/storage/session.cjs.map +1 -0
  83. package/dist/storage/session.d.cts +14 -0
  84. package/dist/storage/session.d.ts +14 -0
  85. package/dist/storage/session.js +12 -0
  86. package/dist/storage/session.js.map +1 -0
  87. package/dist/storage/url.cjs +132 -0
  88. package/dist/storage/url.cjs.map +1 -0
  89. package/dist/storage/url.d.cts +37 -0
  90. package/dist/storage/url.d.ts +37 -0
  91. package/dist/storage/url.js +100 -0
  92. package/dist/storage/url.js.map +1 -0
  93. package/dist/storage-DBLIRR-4.d.cts +59 -0
  94. package/dist/storage-DBLIRR-4.d.ts +59 -0
  95. package/dist/types-BSWKH-jw.d.cts +68 -0
  96. package/dist/types-BUmNpSyP.d.ts +68 -0
  97. package/package.json +114 -0
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/storage/cookie/index.ts
21
+ var cookie_exports = {};
22
+ __export(cookie_exports, {
23
+ cookieStorage: () => cookieStorage
24
+ });
25
+ module.exports = __toCommonJS(cookie_exports);
26
+ var isClient = typeof window !== "undefined" && typeof document !== "undefined";
27
+ var DEFAULT_KEY = "params";
28
+ function cookieStorage(options = {}) {
29
+ const key = options.key ?? DEFAULT_KEY;
30
+ const serialize = options.serialize ?? JSON.stringify;
31
+ const deserialize = options.deserialize ?? JSON.parse;
32
+ const attrs = options.attributes ?? { path: "/", sameSite: "Lax" };
33
+ const readImpl = () => {
34
+ const raw = readRawCookie(key, options.readCookie);
35
+ if (raw === void 0) return void 0;
36
+ try {
37
+ const parsed = deserialize(raw);
38
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
39
+ return void 0;
40
+ }
41
+ return parsed;
42
+ } catch {
43
+ return void 0;
44
+ }
45
+ };
46
+ return {
47
+ name: "cookie",
48
+ // Cookies WORK on the server — that's the whole point of this backend.
49
+ // The store reads on construction, which on the server uses the
50
+ // closure-bound readCookie. clientOnly stays false.
51
+ clientOnly: false,
52
+ read: readImpl,
53
+ write: (values) => {
54
+ try {
55
+ const existing = readImpl() ?? {};
56
+ const merged = { ...existing };
57
+ for (const [path, value] of Object.entries(values)) {
58
+ if (value === void 0) {
59
+ delete merged[path];
60
+ } else {
61
+ merged[path] = value;
62
+ }
63
+ }
64
+ if (Object.keys(merged).length === 0) {
65
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
66
+ return;
67
+ }
68
+ writeRawCookie(key, serialize(merged), attrs, options.writeCookie);
69
+ } catch {
70
+ }
71
+ },
72
+ clear: (paths) => {
73
+ try {
74
+ if (paths.length === 0) {
75
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
76
+ return;
77
+ }
78
+ const existing = readImpl();
79
+ if (!existing) return;
80
+ const next = { ...existing };
81
+ for (const path of paths) delete next[path];
82
+ if (Object.keys(next).length === 0) {
83
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
84
+ return;
85
+ }
86
+ writeRawCookie(key, serialize(next), attrs, options.writeCookie);
87
+ } catch {
88
+ }
89
+ }
90
+ // No subscribe — cookies don't have change events. Cross-tab sync would
91
+ // require BroadcastChannel; out of scope for v0.2.
92
+ };
93
+ }
94
+ function readRawCookie(key, readCookie) {
95
+ if (readCookie) {
96
+ const v = readCookie();
97
+ return v === void 0 || v === null ? void 0 : decodeURIComponent(v);
98
+ }
99
+ if (!isClient) return void 0;
100
+ return parseCookieJar(document.cookie, key);
101
+ }
102
+ function writeRawCookie(key, value, attrs, writeCookie) {
103
+ const setCookieValue = formatSetCookie(key, value, attrs);
104
+ if (writeCookie) {
105
+ writeCookie(setCookieValue);
106
+ return;
107
+ }
108
+ if (!isClient) return;
109
+ document.cookie = setCookieValue;
110
+ }
111
+ function parseCookieJar(jar, key) {
112
+ if (!jar) return void 0;
113
+ for (const part of jar.split(";")) {
114
+ const eq = part.indexOf("=");
115
+ if (eq === -1) continue;
116
+ const k = part.slice(0, eq).trim();
117
+ if (k !== key) continue;
118
+ return decodeURIComponent(part.slice(eq + 1).trim());
119
+ }
120
+ return void 0;
121
+ }
122
+ function formatSetCookie(key, value, attrs) {
123
+ const segments = [`${key}=${encodeURIComponent(value)}`];
124
+ if (attrs.path !== void 0) segments.push(`Path=${attrs.path}`);
125
+ if (attrs.maxAge !== void 0) segments.push(`Max-Age=${attrs.maxAge}`);
126
+ if (attrs.expires !== void 0) segments.push(`Expires=${attrs.expires.toUTCString()}`);
127
+ if (attrs.domain !== void 0) segments.push(`Domain=${attrs.domain}`);
128
+ if (attrs.sameSite !== void 0) segments.push(`SameSite=${attrs.sameSite}`);
129
+ if (attrs.secure) segments.push("Secure");
130
+ return segments.join("; ");
131
+ }
132
+ // Annotate the CommonJS export names for ESM import in node:
133
+ 0 && (module.exports = {
134
+ cookieStorage
135
+ });
136
+ //# sourceMappingURL=cookie.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/cookie/index.ts"],"sourcesContent":["import type { ParamsStorage } from '../../storage'\n\nexport interface CookieStorageAttributes {\n path?: string\n maxAge?: number\n expires?: Date\n domain?: string\n sameSite?: 'Lax' | 'Strict' | 'None'\n secure?: boolean\n}\n\nexport interface CookieStorageOptions<T> {\n /** Cookie name. Default: `'params'`. */\n readonly key?: string\n\n /**\n * Server-side: read from the incoming request's cookies. Closure-bound at\n * factory time. Receives nothing; returns the raw cookie value (already\n * URL-decoded) or `undefined`.\n *\n * If omitted, the backend assumes a client environment and reads from\n * `document.cookie`.\n */\n readonly readCookie?: () => string | undefined\n\n /**\n * Server-side: append to the outgoing response. Closure-bound at factory\n * time. Receives the full `Set-Cookie` header value (e.g. `\"params=foo;\n * Path=/; SameSite=Lax\"`); the consumer is responsible for actually\n * sending it to the browser via their framework's response API.\n *\n * If omitted, the backend assumes a client environment and writes to\n * `document.cookie`.\n */\n readonly writeCookie?: (setCookieValue: string) => void\n\n /** Whole-blob serializer. Default: `JSON.stringify`. */\n readonly serialize?: (values: Partial<T>) => string\n\n /** Whole-blob deserializer. Default: `JSON.parse`. */\n readonly deserialize?: (raw: string) => Partial<T> | undefined\n\n /** Cookie attributes appended to writes. */\n readonly attributes?: CookieStorageAttributes\n}\n\nconst isClient = typeof window !== 'undefined' && typeof document !== 'undefined'\nconst DEFAULT_KEY = 'params'\n\n/**\n * Cookie-backed storage. Works on both the server (via closure-bound\n * `readCookie` / `writeCookie` adapters) and the client (via\n * `document.cookie`). The whole values blob is JSON-encoded under one\n * cookie key.\n *\n * Use cases:\n * - User preferences that should survive a hard refresh (theme, locale).\n * - SSR snapshot transport when paired with `paramsSnapshot()` /\n * `hydrateParams()`.\n *\n * Cookies have a ~4 KB limit per domain — for filter state, prefer\n * `urlStorage`; for app config not tied to a user account, prefer\n * `localStorage`.\n */\nexport function cookieStorage<T = Record<string, unknown>>(\n options: CookieStorageOptions<T> = {},\n): ParamsStorage<T> {\n const key = options.key ?? DEFAULT_KEY\n const serialize = options.serialize ?? (JSON.stringify as (values: Partial<T>) => string)\n const deserialize = options.deserialize ?? (JSON.parse as (raw: string) => Partial<T> | undefined)\n const attrs = options.attributes ?? { path: '/', sameSite: 'Lax' as const }\n\n const readImpl = (): Partial<T> | undefined => {\n const raw = readRawCookie(key, options.readCookie)\n if (raw === undefined) return undefined\n try {\n const parsed = deserialize(raw)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined\n }\n return parsed\n } catch {\n return undefined\n }\n }\n\n return {\n name: 'cookie',\n // Cookies WORK on the server — that's the whole point of this backend.\n // The store reads on construction, which on the server uses the\n // closure-bound readCookie. clientOnly stays false.\n clientOnly: false,\n read: readImpl,\n write: (values) => {\n try {\n const existing = readImpl() ?? ({} as Partial<T>)\n const merged: Record<string, unknown> = { ...existing }\n for (const [path, value] of Object.entries(values as Record<string, unknown>)) {\n if (value === undefined) {\n delete merged[path]\n } else {\n merged[path] = value\n }\n }\n if (Object.keys(merged).length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n writeRawCookie(key, serialize(merged as Partial<T>), attrs, options.writeCookie)\n } catch {\n // Silent fallback — same contract as other backends.\n }\n },\n clear: (paths) => {\n try {\n if (paths.length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n const existing = readImpl()\n if (!existing) return\n const next: Record<string, unknown> = { ...(existing as Record<string, unknown>) }\n for (const path of paths) delete next[path]\n if (Object.keys(next).length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n writeRawCookie(key, serialize(next as Partial<T>), attrs, options.writeCookie)\n } catch {\n // Silent fallback\n }\n },\n // No subscribe — cookies don't have change events. Cross-tab sync would\n // require BroadcastChannel; out of scope for v0.2.\n }\n}\n\nfunction readRawCookie(\n key: string,\n readCookie: (() => string | undefined) | undefined,\n): string | undefined {\n if (readCookie) {\n const v = readCookie()\n return v === undefined || v === null ? undefined : decodeURIComponent(v)\n }\n if (!isClient) return undefined\n return parseCookieJar(document.cookie, key)\n}\n\nfunction writeRawCookie(\n key: string,\n value: string,\n attrs: CookieStorageAttributes,\n writeCookie: ((setCookieValue: string) => void) | undefined,\n): void {\n const setCookieValue = formatSetCookie(key, value, attrs)\n if (writeCookie) {\n writeCookie(setCookieValue)\n return\n }\n if (!isClient) return\n document.cookie = setCookieValue\n}\n\nfunction parseCookieJar(jar: string, key: string): string | undefined {\n if (!jar) return undefined\n for (const part of jar.split(';')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const k = part.slice(0, eq).trim()\n if (k !== key) continue\n return decodeURIComponent(part.slice(eq + 1).trim())\n }\n return undefined\n}\n\nfunction formatSetCookie(key: string, value: string, attrs: CookieStorageAttributes): string {\n const segments: string[] = [`${key}=${encodeURIComponent(value)}`]\n if (attrs.path !== undefined) segments.push(`Path=${attrs.path}`)\n if (attrs.maxAge !== undefined) segments.push(`Max-Age=${attrs.maxAge}`)\n if (attrs.expires !== undefined) segments.push(`Expires=${attrs.expires.toUTCString()}`)\n if (attrs.domain !== undefined) segments.push(`Domain=${attrs.domain}`)\n if (attrs.sameSite !== undefined) segments.push(`SameSite=${attrs.sameSite}`)\n if (attrs.secure) segments.push('Secure')\n return segments.join('; ')\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,57 @@
1
+ import { P as ParamsStorage } from '../storage-DBLIRR-4.cjs';
2
+
3
+ interface CookieStorageAttributes {
4
+ path?: string;
5
+ maxAge?: number;
6
+ expires?: Date;
7
+ domain?: string;
8
+ sameSite?: 'Lax' | 'Strict' | 'None';
9
+ secure?: boolean;
10
+ }
11
+ interface CookieStorageOptions<T> {
12
+ /** Cookie name. Default: `'params'`. */
13
+ readonly key?: string;
14
+ /**
15
+ * Server-side: read from the incoming request's cookies. Closure-bound at
16
+ * factory time. Receives nothing; returns the raw cookie value (already
17
+ * URL-decoded) or `undefined`.
18
+ *
19
+ * If omitted, the backend assumes a client environment and reads from
20
+ * `document.cookie`.
21
+ */
22
+ readonly readCookie?: () => string | undefined;
23
+ /**
24
+ * Server-side: append to the outgoing response. Closure-bound at factory
25
+ * time. Receives the full `Set-Cookie` header value (e.g. `"params=foo;
26
+ * Path=/; SameSite=Lax"`); the consumer is responsible for actually
27
+ * sending it to the browser via their framework's response API.
28
+ *
29
+ * If omitted, the backend assumes a client environment and writes to
30
+ * `document.cookie`.
31
+ */
32
+ readonly writeCookie?: (setCookieValue: string) => void;
33
+ /** Whole-blob serializer. Default: `JSON.stringify`. */
34
+ readonly serialize?: (values: Partial<T>) => string;
35
+ /** Whole-blob deserializer. Default: `JSON.parse`. */
36
+ readonly deserialize?: (raw: string) => Partial<T> | undefined;
37
+ /** Cookie attributes appended to writes. */
38
+ readonly attributes?: CookieStorageAttributes;
39
+ }
40
+ /**
41
+ * Cookie-backed storage. Works on both the server (via closure-bound
42
+ * `readCookie` / `writeCookie` adapters) and the client (via
43
+ * `document.cookie`). The whole values blob is JSON-encoded under one
44
+ * cookie key.
45
+ *
46
+ * Use cases:
47
+ * - User preferences that should survive a hard refresh (theme, locale).
48
+ * - SSR snapshot transport when paired with `paramsSnapshot()` /
49
+ * `hydrateParams()`.
50
+ *
51
+ * Cookies have a ~4 KB limit per domain — for filter state, prefer
52
+ * `urlStorage`; for app config not tied to a user account, prefer
53
+ * `localStorage`.
54
+ */
55
+ declare function cookieStorage<T = Record<string, unknown>>(options?: CookieStorageOptions<T>): ParamsStorage<T>;
56
+
57
+ export { type CookieStorageAttributes, type CookieStorageOptions, cookieStorage };
@@ -0,0 +1,57 @@
1
+ import { P as ParamsStorage } from '../storage-DBLIRR-4.js';
2
+
3
+ interface CookieStorageAttributes {
4
+ path?: string;
5
+ maxAge?: number;
6
+ expires?: Date;
7
+ domain?: string;
8
+ sameSite?: 'Lax' | 'Strict' | 'None';
9
+ secure?: boolean;
10
+ }
11
+ interface CookieStorageOptions<T> {
12
+ /** Cookie name. Default: `'params'`. */
13
+ readonly key?: string;
14
+ /**
15
+ * Server-side: read from the incoming request's cookies. Closure-bound at
16
+ * factory time. Receives nothing; returns the raw cookie value (already
17
+ * URL-decoded) or `undefined`.
18
+ *
19
+ * If omitted, the backend assumes a client environment and reads from
20
+ * `document.cookie`.
21
+ */
22
+ readonly readCookie?: () => string | undefined;
23
+ /**
24
+ * Server-side: append to the outgoing response. Closure-bound at factory
25
+ * time. Receives the full `Set-Cookie` header value (e.g. `"params=foo;
26
+ * Path=/; SameSite=Lax"`); the consumer is responsible for actually
27
+ * sending it to the browser via their framework's response API.
28
+ *
29
+ * If omitted, the backend assumes a client environment and writes to
30
+ * `document.cookie`.
31
+ */
32
+ readonly writeCookie?: (setCookieValue: string) => void;
33
+ /** Whole-blob serializer. Default: `JSON.stringify`. */
34
+ readonly serialize?: (values: Partial<T>) => string;
35
+ /** Whole-blob deserializer. Default: `JSON.parse`. */
36
+ readonly deserialize?: (raw: string) => Partial<T> | undefined;
37
+ /** Cookie attributes appended to writes. */
38
+ readonly attributes?: CookieStorageAttributes;
39
+ }
40
+ /**
41
+ * Cookie-backed storage. Works on both the server (via closure-bound
42
+ * `readCookie` / `writeCookie` adapters) and the client (via
43
+ * `document.cookie`). The whole values blob is JSON-encoded under one
44
+ * cookie key.
45
+ *
46
+ * Use cases:
47
+ * - User preferences that should survive a hard refresh (theme, locale).
48
+ * - SSR snapshot transport when paired with `paramsSnapshot()` /
49
+ * `hydrateParams()`.
50
+ *
51
+ * Cookies have a ~4 KB limit per domain — for filter state, prefer
52
+ * `urlStorage`; for app config not tied to a user account, prefer
53
+ * `localStorage`.
54
+ */
55
+ declare function cookieStorage<T = Record<string, unknown>>(options?: CookieStorageOptions<T>): ParamsStorage<T>;
56
+
57
+ export { type CookieStorageAttributes, type CookieStorageOptions, cookieStorage };
@@ -0,0 +1,111 @@
1
+ // src/storage/cookie/index.ts
2
+ var isClient = typeof window !== "undefined" && typeof document !== "undefined";
3
+ var DEFAULT_KEY = "params";
4
+ function cookieStorage(options = {}) {
5
+ const key = options.key ?? DEFAULT_KEY;
6
+ const serialize = options.serialize ?? JSON.stringify;
7
+ const deserialize = options.deserialize ?? JSON.parse;
8
+ const attrs = options.attributes ?? { path: "/", sameSite: "Lax" };
9
+ const readImpl = () => {
10
+ const raw = readRawCookie(key, options.readCookie);
11
+ if (raw === void 0) return void 0;
12
+ try {
13
+ const parsed = deserialize(raw);
14
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
15
+ return void 0;
16
+ }
17
+ return parsed;
18
+ } catch {
19
+ return void 0;
20
+ }
21
+ };
22
+ return {
23
+ name: "cookie",
24
+ // Cookies WORK on the server — that's the whole point of this backend.
25
+ // The store reads on construction, which on the server uses the
26
+ // closure-bound readCookie. clientOnly stays false.
27
+ clientOnly: false,
28
+ read: readImpl,
29
+ write: (values) => {
30
+ try {
31
+ const existing = readImpl() ?? {};
32
+ const merged = { ...existing };
33
+ for (const [path, value] of Object.entries(values)) {
34
+ if (value === void 0) {
35
+ delete merged[path];
36
+ } else {
37
+ merged[path] = value;
38
+ }
39
+ }
40
+ if (Object.keys(merged).length === 0) {
41
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
42
+ return;
43
+ }
44
+ writeRawCookie(key, serialize(merged), attrs, options.writeCookie);
45
+ } catch {
46
+ }
47
+ },
48
+ clear: (paths) => {
49
+ try {
50
+ if (paths.length === 0) {
51
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
52
+ return;
53
+ }
54
+ const existing = readImpl();
55
+ if (!existing) return;
56
+ const next = { ...existing };
57
+ for (const path of paths) delete next[path];
58
+ if (Object.keys(next).length === 0) {
59
+ writeRawCookie(key, "", { ...attrs, maxAge: 0 }, options.writeCookie);
60
+ return;
61
+ }
62
+ writeRawCookie(key, serialize(next), attrs, options.writeCookie);
63
+ } catch {
64
+ }
65
+ }
66
+ // No subscribe — cookies don't have change events. Cross-tab sync would
67
+ // require BroadcastChannel; out of scope for v0.2.
68
+ };
69
+ }
70
+ function readRawCookie(key, readCookie) {
71
+ if (readCookie) {
72
+ const v = readCookie();
73
+ return v === void 0 || v === null ? void 0 : decodeURIComponent(v);
74
+ }
75
+ if (!isClient) return void 0;
76
+ return parseCookieJar(document.cookie, key);
77
+ }
78
+ function writeRawCookie(key, value, attrs, writeCookie) {
79
+ const setCookieValue = formatSetCookie(key, value, attrs);
80
+ if (writeCookie) {
81
+ writeCookie(setCookieValue);
82
+ return;
83
+ }
84
+ if (!isClient) return;
85
+ document.cookie = setCookieValue;
86
+ }
87
+ function parseCookieJar(jar, key) {
88
+ if (!jar) return void 0;
89
+ for (const part of jar.split(";")) {
90
+ const eq = part.indexOf("=");
91
+ if (eq === -1) continue;
92
+ const k = part.slice(0, eq).trim();
93
+ if (k !== key) continue;
94
+ return decodeURIComponent(part.slice(eq + 1).trim());
95
+ }
96
+ return void 0;
97
+ }
98
+ function formatSetCookie(key, value, attrs) {
99
+ const segments = [`${key}=${encodeURIComponent(value)}`];
100
+ if (attrs.path !== void 0) segments.push(`Path=${attrs.path}`);
101
+ if (attrs.maxAge !== void 0) segments.push(`Max-Age=${attrs.maxAge}`);
102
+ if (attrs.expires !== void 0) segments.push(`Expires=${attrs.expires.toUTCString()}`);
103
+ if (attrs.domain !== void 0) segments.push(`Domain=${attrs.domain}`);
104
+ if (attrs.sameSite !== void 0) segments.push(`SameSite=${attrs.sameSite}`);
105
+ if (attrs.secure) segments.push("Secure");
106
+ return segments.join("; ");
107
+ }
108
+ export {
109
+ cookieStorage
110
+ };
111
+ //# sourceMappingURL=cookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/cookie/index.ts"],"sourcesContent":["import type { ParamsStorage } from '../../storage'\n\nexport interface CookieStorageAttributes {\n path?: string\n maxAge?: number\n expires?: Date\n domain?: string\n sameSite?: 'Lax' | 'Strict' | 'None'\n secure?: boolean\n}\n\nexport interface CookieStorageOptions<T> {\n /** Cookie name. Default: `'params'`. */\n readonly key?: string\n\n /**\n * Server-side: read from the incoming request's cookies. Closure-bound at\n * factory time. Receives nothing; returns the raw cookie value (already\n * URL-decoded) or `undefined`.\n *\n * If omitted, the backend assumes a client environment and reads from\n * `document.cookie`.\n */\n readonly readCookie?: () => string | undefined\n\n /**\n * Server-side: append to the outgoing response. Closure-bound at factory\n * time. Receives the full `Set-Cookie` header value (e.g. `\"params=foo;\n * Path=/; SameSite=Lax\"`); the consumer is responsible for actually\n * sending it to the browser via their framework's response API.\n *\n * If omitted, the backend assumes a client environment and writes to\n * `document.cookie`.\n */\n readonly writeCookie?: (setCookieValue: string) => void\n\n /** Whole-blob serializer. Default: `JSON.stringify`. */\n readonly serialize?: (values: Partial<T>) => string\n\n /** Whole-blob deserializer. Default: `JSON.parse`. */\n readonly deserialize?: (raw: string) => Partial<T> | undefined\n\n /** Cookie attributes appended to writes. */\n readonly attributes?: CookieStorageAttributes\n}\n\nconst isClient = typeof window !== 'undefined' && typeof document !== 'undefined'\nconst DEFAULT_KEY = 'params'\n\n/**\n * Cookie-backed storage. Works on both the server (via closure-bound\n * `readCookie` / `writeCookie` adapters) and the client (via\n * `document.cookie`). The whole values blob is JSON-encoded under one\n * cookie key.\n *\n * Use cases:\n * - User preferences that should survive a hard refresh (theme, locale).\n * - SSR snapshot transport when paired with `paramsSnapshot()` /\n * `hydrateParams()`.\n *\n * Cookies have a ~4 KB limit per domain — for filter state, prefer\n * `urlStorage`; for app config not tied to a user account, prefer\n * `localStorage`.\n */\nexport function cookieStorage<T = Record<string, unknown>>(\n options: CookieStorageOptions<T> = {},\n): ParamsStorage<T> {\n const key = options.key ?? DEFAULT_KEY\n const serialize = options.serialize ?? (JSON.stringify as (values: Partial<T>) => string)\n const deserialize = options.deserialize ?? (JSON.parse as (raw: string) => Partial<T> | undefined)\n const attrs = options.attributes ?? { path: '/', sameSite: 'Lax' as const }\n\n const readImpl = (): Partial<T> | undefined => {\n const raw = readRawCookie(key, options.readCookie)\n if (raw === undefined) return undefined\n try {\n const parsed = deserialize(raw)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return undefined\n }\n return parsed\n } catch {\n return undefined\n }\n }\n\n return {\n name: 'cookie',\n // Cookies WORK on the server — that's the whole point of this backend.\n // The store reads on construction, which on the server uses the\n // closure-bound readCookie. clientOnly stays false.\n clientOnly: false,\n read: readImpl,\n write: (values) => {\n try {\n const existing = readImpl() ?? ({} as Partial<T>)\n const merged: Record<string, unknown> = { ...existing }\n for (const [path, value] of Object.entries(values as Record<string, unknown>)) {\n if (value === undefined) {\n delete merged[path]\n } else {\n merged[path] = value\n }\n }\n if (Object.keys(merged).length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n writeRawCookie(key, serialize(merged as Partial<T>), attrs, options.writeCookie)\n } catch {\n // Silent fallback — same contract as other backends.\n }\n },\n clear: (paths) => {\n try {\n if (paths.length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n const existing = readImpl()\n if (!existing) return\n const next: Record<string, unknown> = { ...(existing as Record<string, unknown>) }\n for (const path of paths) delete next[path]\n if (Object.keys(next).length === 0) {\n writeRawCookie(key, '', { ...attrs, maxAge: 0 }, options.writeCookie)\n return\n }\n writeRawCookie(key, serialize(next as Partial<T>), attrs, options.writeCookie)\n } catch {\n // Silent fallback\n }\n },\n // No subscribe — cookies don't have change events. Cross-tab sync would\n // require BroadcastChannel; out of scope for v0.2.\n }\n}\n\nfunction readRawCookie(\n key: string,\n readCookie: (() => string | undefined) | undefined,\n): string | undefined {\n if (readCookie) {\n const v = readCookie()\n return v === undefined || v === null ? undefined : decodeURIComponent(v)\n }\n if (!isClient) return undefined\n return parseCookieJar(document.cookie, key)\n}\n\nfunction writeRawCookie(\n key: string,\n value: string,\n attrs: CookieStorageAttributes,\n writeCookie: ((setCookieValue: string) => void) | undefined,\n): void {\n const setCookieValue = formatSetCookie(key, value, attrs)\n if (writeCookie) {\n writeCookie(setCookieValue)\n return\n }\n if (!isClient) return\n document.cookie = setCookieValue\n}\n\nfunction parseCookieJar(jar: string, key: string): string | undefined {\n if (!jar) return undefined\n for (const part of jar.split(';')) {\n const eq = part.indexOf('=')\n if (eq === -1) continue\n const k = part.slice(0, eq).trim()\n if (k !== key) continue\n return decodeURIComponent(part.slice(eq + 1).trim())\n }\n return undefined\n}\n\nfunction formatSetCookie(key: string, value: string, attrs: CookieStorageAttributes): string {\n const segments: string[] = [`${key}=${encodeURIComponent(value)}`]\n if (attrs.path !== undefined) segments.push(`Path=${attrs.path}`)\n if (attrs.maxAge !== undefined) segments.push(`Max-Age=${attrs.maxAge}`)\n if (attrs.expires !== undefined) segments.push(`Expires=${attrs.expires.toUTCString()}`)\n if (attrs.domain !== undefined) segments.push(`Domain=${attrs.domain}`)\n if (attrs.sameSite !== undefined) segments.push(`SameSite=${attrs.sameSite}`)\n if (attrs.secure) segments.push('Secure')\n return segments.join('; ')\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/storage/idb/index.ts
21
+ var idb_exports = {};
22
+ __export(idb_exports, {
23
+ idbStorage: () => idbStorage
24
+ });
25
+ module.exports = __toCommonJS(idb_exports);
26
+ var isClient = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
27
+ function idbStorage(opts) {
28
+ const dbName = opts.db;
29
+ const storeName = opts.store ?? "params";
30
+ const recordKey = opts.key ?? "default";
31
+ const version = opts.version ?? 1;
32
+ let dbHandle;
33
+ const openDb = async () => {
34
+ if (!isClient) throw new Error("idbStorage: window.indexedDB unavailable");
35
+ if (dbHandle && dbHandle.version === version) return dbHandle;
36
+ return new Promise((resolve, reject) => {
37
+ const req = window.indexedDB.open(dbName, version);
38
+ req.onupgradeneeded = () => {
39
+ const db = req.result;
40
+ if (!db.objectStoreNames.contains(storeName)) {
41
+ db.createObjectStore(storeName);
42
+ }
43
+ };
44
+ req.onsuccess = () => {
45
+ dbHandle = req.result;
46
+ dbHandle.onclose = () => {
47
+ dbHandle = void 0;
48
+ };
49
+ resolve(req.result);
50
+ };
51
+ req.onerror = () => reject(req.error);
52
+ });
53
+ };
54
+ const withVersionRetry = async (op) => {
55
+ try {
56
+ return await op(await openDb());
57
+ } catch (err) {
58
+ const isVersionError = typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "VersionError";
59
+ if (!isVersionError) throw err;
60
+ try {
61
+ dbHandle?.close();
62
+ } catch {
63
+ }
64
+ dbHandle = void 0;
65
+ return op(await openDb());
66
+ }
67
+ };
68
+ return {
69
+ name: "idbStorage",
70
+ clientOnly: true,
71
+ // IDB has no synchronous API. Engine consults `readAsync` for hydration;
72
+ // `read()` always returns undefined.
73
+ read: () => void 0,
74
+ readAsync: async () => {
75
+ if (!isClient) return void 0;
76
+ try {
77
+ return await withVersionRetry(
78
+ (db) => new Promise((resolve, reject) => {
79
+ const tx = db.transaction(storeName, "readonly");
80
+ const req = tx.objectStore(storeName).get(recordKey);
81
+ req.onsuccess = () => resolve(req.result ?? void 0);
82
+ req.onerror = () => reject(req.error);
83
+ })
84
+ );
85
+ } catch {
86
+ return void 0;
87
+ }
88
+ },
89
+ write: async (values, _changed, _options) => {
90
+ if (!isClient) return;
91
+ try {
92
+ await withVersionRetry(
93
+ (db) => new Promise((resolve, reject) => {
94
+ const tx = db.transaction(storeName, "readwrite");
95
+ const req = tx.objectStore(storeName).put(values, recordKey);
96
+ req.onsuccess = () => resolve();
97
+ req.onerror = () => reject(req.error);
98
+ })
99
+ );
100
+ } catch {
101
+ }
102
+ },
103
+ clear: async (paths, _options) => {
104
+ if (!isClient) return;
105
+ try {
106
+ if (paths.length === 0) {
107
+ await withVersionRetry(
108
+ (db) => new Promise((resolve, reject) => {
109
+ const tx = db.transaction(storeName, "readwrite");
110
+ const req = tx.objectStore(storeName).delete(recordKey);
111
+ req.onsuccess = () => resolve();
112
+ req.onerror = () => reject(req.error);
113
+ })
114
+ );
115
+ return;
116
+ }
117
+ const current = await withVersionRetry(
118
+ (db) => new Promise((resolve, reject) => {
119
+ const tx = db.transaction(storeName, "readonly");
120
+ const req = tx.objectStore(storeName).get(recordKey);
121
+ req.onsuccess = () => resolve(req.result ?? {});
122
+ req.onerror = () => reject(req.error);
123
+ })
124
+ );
125
+ for (const p of paths) delete current[p];
126
+ await withVersionRetry(
127
+ (db) => new Promise((resolve, reject) => {
128
+ const tx = db.transaction(storeName, "readwrite");
129
+ const req = tx.objectStore(storeName).put(current, recordKey);
130
+ req.onsuccess = () => resolve();
131
+ req.onerror = () => reject(req.error);
132
+ })
133
+ );
134
+ } catch {
135
+ }
136
+ }
137
+ // No `subscribe` — IDB doesn't fire native change events. v0.6+ via BroadcastChannel.
138
+ };
139
+ }
140
+ // Annotate the CommonJS export names for ESM import in node:
141
+ 0 && (module.exports = {
142
+ idbStorage
143
+ });
144
+ //# sourceMappingURL=idb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/idb/index.ts"],"sourcesContent":["import type { ParamsStorage } from '../../storage'\n\nexport interface IdbStorageOptions {\n /** Database name. Required. */\n readonly db: string\n /** Object store name within the database. Default: `'params'`. */\n readonly store?: string\n /** Record key within the store. Default: `'default'`. */\n readonly key?: string\n /** Schema version. Bump if you change the value shape across releases. Default: `1`. */\n readonly version?: number\n}\n\nconst isClient = typeof window !== 'undefined' && typeof window.indexedDB !== 'undefined'\n\n/**\n * IndexedDB storage backend.\n *\n * Async-only — `read()` returns `undefined` synchronously; the engine should\n * consult `readAsync()` for hydration. Single record per definition (the\n * `key` option), stored as a structured-clonable object — no JSON\n * serialization layer.\n *\n * v0.5 limitations (likely v0.6+ candidates if a consumer asks):\n * - **No cross-tab sync** (no BroadcastChannel). The same database is shared\n * across tabs, but writes from one tab don't push to another. Subscribe is\n * intentionally absent.\n * - **No consumer-supplied `onupgradeneeded` hook.** The `version` option\n * triggers an upgrade transaction and the lib creates the object store on\n * first open, but you can't migrate from a previous shape.\n */\nexport function idbStorage<T = Record<string, unknown>>(opts: IdbStorageOptions): ParamsStorage<T> {\n const dbName = opts.db\n const storeName = opts.store ?? 'params'\n const recordKey = opts.key ?? 'default'\n const version = opts.version ?? 1\n\n // Cache the database handle. Re-opens on demand if closed.\n let dbHandle: IDBDatabase | undefined\n const openDb = async (): Promise<IDBDatabase> => {\n if (!isClient) throw new Error('idbStorage: window.indexedDB unavailable')\n if (dbHandle && dbHandle.version === version) return dbHandle\n return new Promise((resolve, reject) => {\n const req = window.indexedDB.open(dbName, version)\n req.onupgradeneeded = () => {\n const db = req.result\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName)\n }\n }\n req.onsuccess = () => {\n dbHandle = req.result\n dbHandle.onclose = () => {\n dbHandle = undefined\n }\n resolve(req.result)\n }\n req.onerror = () => reject(req.error)\n })\n }\n\n /**\n * v0.5: retry once on `VersionError`. Another tab opening the DB at a\n * different version invalidates our cached handle. Close it, re-open, and\n * retry the operation. After one retry, fall back to silent (existing\n * contract).\n */\n const withVersionRetry = async <R>(op: (db: IDBDatabase) => Promise<R>): Promise<R> => {\n try {\n return await op(await openDb())\n } catch (err) {\n const isVersionError =\n typeof DOMException !== 'undefined' &&\n err instanceof DOMException &&\n err.name === 'VersionError'\n if (!isVersionError) throw err\n try {\n dbHandle?.close()\n } catch {\n // ignore — handle may already be invalid\n }\n dbHandle = undefined\n return op(await openDb())\n }\n }\n\n return {\n name: 'idbStorage',\n clientOnly: true,\n\n // IDB has no synchronous API. Engine consults `readAsync` for hydration;\n // `read()` always returns undefined.\n read: () => undefined,\n\n readAsync: async () => {\n if (!isClient) return undefined\n try {\n return await withVersionRetry(\n (db) =>\n new Promise<Partial<T> | undefined>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readonly')\n const req = tx.objectStore(storeName).get(recordKey)\n req.onsuccess = () => resolve((req.result as Partial<T> | undefined) ?? undefined)\n req.onerror = () => reject(req.error)\n }),\n )\n } catch {\n return undefined\n }\n },\n\n write: async (values, _changed, _options) => {\n if (!isClient) return\n try {\n await withVersionRetry(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readwrite')\n const req = tx.objectStore(storeName).put(values, recordKey)\n req.onsuccess = () => resolve()\n req.onerror = () => reject(req.error)\n }),\n )\n } catch {\n // Silent fallback per the storage error contract — values still live in memory.\n }\n },\n\n clear: async (paths, _options) => {\n if (!isClient) return\n try {\n if (paths.length === 0) {\n // Clear everything — delete the record.\n await withVersionRetry(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readwrite')\n const req = tx.objectStore(storeName).delete(recordKey)\n req.onsuccess = () => resolve()\n req.onerror = () => reject(req.error)\n }),\n )\n return\n }\n // Per-path clear: read current, omit cleared paths, write back.\n const current = await withVersionRetry(\n (db) =>\n new Promise<Record<string, unknown>>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readonly')\n const req = tx.objectStore(storeName).get(recordKey)\n req.onsuccess = () =>\n resolve((req.result as Record<string, unknown> | undefined) ?? {})\n req.onerror = () => reject(req.error)\n }),\n )\n for (const p of paths) delete current[p]\n await withVersionRetry(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readwrite')\n const req = tx.objectStore(storeName).put(current, recordKey)\n req.onsuccess = () => resolve()\n req.onerror = () => reject(req.error)\n }),\n )\n } catch {\n // Silent fallback.\n }\n },\n\n // No `subscribe` — IDB doesn't fire native change events. v0.6+ via BroadcastChannel.\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,31 @@
1
+ import { P as ParamsStorage } from '../storage-DBLIRR-4.cjs';
2
+
3
+ interface IdbStorageOptions {
4
+ /** Database name. Required. */
5
+ readonly db: string;
6
+ /** Object store name within the database. Default: `'params'`. */
7
+ readonly store?: string;
8
+ /** Record key within the store. Default: `'default'`. */
9
+ readonly key?: string;
10
+ /** Schema version. Bump if you change the value shape across releases. Default: `1`. */
11
+ readonly version?: number;
12
+ }
13
+ /**
14
+ * IndexedDB storage backend.
15
+ *
16
+ * Async-only — `read()` returns `undefined` synchronously; the engine should
17
+ * consult `readAsync()` for hydration. Single record per definition (the
18
+ * `key` option), stored as a structured-clonable object — no JSON
19
+ * serialization layer.
20
+ *
21
+ * v0.5 limitations (likely v0.6+ candidates if a consumer asks):
22
+ * - **No cross-tab sync** (no BroadcastChannel). The same database is shared
23
+ * across tabs, but writes from one tab don't push to another. Subscribe is
24
+ * intentionally absent.
25
+ * - **No consumer-supplied `onupgradeneeded` hook.** The `version` option
26
+ * triggers an upgrade transaction and the lib creates the object store on
27
+ * first open, but you can't migrate from a previous shape.
28
+ */
29
+ declare function idbStorage<T = Record<string, unknown>>(opts: IdbStorageOptions): ParamsStorage<T>;
30
+
31
+ export { type IdbStorageOptions, idbStorage };
@@ -0,0 +1,31 @@
1
+ import { P as ParamsStorage } from '../storage-DBLIRR-4.js';
2
+
3
+ interface IdbStorageOptions {
4
+ /** Database name. Required. */
5
+ readonly db: string;
6
+ /** Object store name within the database. Default: `'params'`. */
7
+ readonly store?: string;
8
+ /** Record key within the store. Default: `'default'`. */
9
+ readonly key?: string;
10
+ /** Schema version. Bump if you change the value shape across releases. Default: `1`. */
11
+ readonly version?: number;
12
+ }
13
+ /**
14
+ * IndexedDB storage backend.
15
+ *
16
+ * Async-only — `read()` returns `undefined` synchronously; the engine should
17
+ * consult `readAsync()` for hydration. Single record per definition (the
18
+ * `key` option), stored as a structured-clonable object — no JSON
19
+ * serialization layer.
20
+ *
21
+ * v0.5 limitations (likely v0.6+ candidates if a consumer asks):
22
+ * - **No cross-tab sync** (no BroadcastChannel). The same database is shared
23
+ * across tabs, but writes from one tab don't push to another. Subscribe is
24
+ * intentionally absent.
25
+ * - **No consumer-supplied `onupgradeneeded` hook.** The `version` option
26
+ * triggers an upgrade transaction and the lib creates the object store on
27
+ * first open, but you can't migrate from a previous shape.
28
+ */
29
+ declare function idbStorage<T = Record<string, unknown>>(opts: IdbStorageOptions): ParamsStorage<T>;
30
+
31
+ export { type IdbStorageOptions, idbStorage };