offdex 2.0.0 → 4.0.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.
package/README.md CHANGED
@@ -1,68 +1,139 @@
1
- # Offdex
2
-
3
- Keyed object storage for the browser. Offdex wraps IndexedDB with a tiny, unbiased API so you can drop in `{ key: "<string>", ...data }` objects and have them shared across tabs, workers, and sessions without thinking about schema versions.
4
-
5
- ## Why use Offdex
6
-
7
- - Zero schema/versioning overhead: one object store keyed by `key`, no migrations to manage.
8
- - Minimal wrapper: no hooks or proxies in the storage API, just data in and data out.
9
- - Works everywhere IndexedDB works: tabs, workers, and other browser runtimes share the same underlying database.
10
- - Offline by default: IndexedDB persists across reloads and disconnects.
11
- - Typed surface: ships with TypeScript definitions for easy adoption.
12
-
13
- ## Install
14
-
15
- ```bash
16
- npm install offdex
17
- ```
18
-
19
- ## Quick start
20
-
21
- ```js
22
- import { storage } from "offdex";
23
-
24
- const profile = {
25
- key: "profile:ada",
26
- name: "Ada Lovelace",
27
- role: "analyst",
1
+ # Offdex
2
+
3
+ IndexedDB-backed envelope storage for browsers and workers. Store AES-GCM
4
+ envelopes (12-byte IV + ciphertext ArrayBuffer) by base64url identifier and
5
+ read them back directly. The store is shared per origin across tabs and workers.
6
+
7
+ ## Why use Offdex
8
+
9
+ - Minimal `put/get/has/delete` API for encrypted payload envelopes.
10
+ - Fixed-width base64url identifiers (43 chars, 32 random bytes).
11
+ - Single object store keyed by identifier; no migrations to manage.
12
+ - Data shared per origin across tabs and workers (instance per realm).
13
+ - Offline persistence via IndexedDB.
14
+ - TypeScript-first.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install offdex
20
+ # or
21
+ pnpm add offdex
22
+ # or
23
+ yarn add offdex
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```js
29
+ import storage from "offdex";
30
+
31
+ const makeId = () => {
32
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
33
+ let binary = "";
34
+ for (let i = 0; i < bytes.length; i += 1) {
35
+ binary += String.fromCharCode(bytes[i]);
36
+ }
37
+ return btoa(binary)
38
+ .replace(/\+/g, "-")
39
+ .replace(/\//g, "_")
40
+ .replace(/=+$/, "");
28
41
  };
29
-
30
- await storage.put(profile);
31
-
32
- const again = await storage.get(profile.key);
33
- // -> { key: "profile:ada", name: "Ada Lovelace", role: "analyst" }
34
-
35
- await storage.delete(profile.key);
36
- ```
37
-
38
- ## API
39
-
40
- ### `storage`
41
-
42
- - Ready-to-use singleton instance shared across every import in the same origin. Uses the `offdex` database and `objects` store under the hood.
43
-
44
- ### `class ObjectStore`
45
-
46
- - `constructor()` — opens (or creates) the `offdex` database with the `objects` store. Use this only if you need a separate instance.
47
- - `put(object: { key: string } & Record<string, unknown>): Promise<void>` - upserts an object keyed by `key`.
48
- - `putAll(objects: { key: string } & Record<string, unknown>[]): Promise<void>` - upserts multiple objects in a single transaction.
49
- - `get(key: string): Promise<{ key: string } & Record<string, unknown> | undefined>` - fetches by `key`, returning `undefined` when missing.
50
- - `delete(key: string): Promise<void>` - removes an object by `key`.
51
- - `getAllMatches(queryOrFilter: StorageQuery | (object) => boolean): Promise<object[]>` - returns objects that pass a query or predicate.
52
- - `deleteAllMatches(queryOrFilter: StorageQuery | (object) => boolean): Promise<void>` deletes objects that pass a query or predicate.
53
-
54
- ### Other exports
55
-
56
- - `StorageQuery` — helper for simple equality-based queries.
57
- - `ObservableValue` observable wrapper around a single value.
58
- - `ObservableObject` wraps an object in observable values keyed by its properties.
59
-
60
- ### Types
61
-
62
- - `StoredObject` - `{ key: string } & Record<string, unknown>`.
63
-
64
- ## Notes
65
-
66
- - Runs in any environment that exposes `indexedDB` (secure contexts in modern browsers).
67
- - Data is shared per origin; open multiple tabs or workers and you will see the same store.
68
- - There is no schema migration system; keep your stored objects backward compatible or manage migrations externally if you need them.
42
+
43
+ const envelope = {
44
+ iv: crypto.getRandomValues(new Uint8Array(12)),
45
+ ciphertext: new Uint8Array([1, 2, 3]).buffer,
46
+ };
47
+
48
+ const key = makeId();
49
+
50
+ await storage.put(key, envelope);
51
+
52
+ const stored = await storage.get(key);
53
+ if (stored) {
54
+ console.log(stored.iv, stored.ciphertext);
55
+ }
56
+
57
+ await storage.delete(key);
58
+ ```
59
+
60
+ ## API
61
+
62
+ ### `storage` (default export)
63
+
64
+ Singleton storage instance per JavaScript realm backed by the `offdex` database
65
+ and `envelopes` object store. The underlying store is shared per origin across
66
+ tabs and workers.
67
+
68
+ ### Methods
69
+
70
+ - `put(key, envelope)` -> `Promise<void>`
71
+ - `get(key)` -> `Promise<OffdexEnvelope | null>`
72
+ - `has(key)` -> `Promise<boolean>`
73
+ - `delete(key)` -> `Promise<void>`
74
+ - `destroy()` -> `Promise<void>`
75
+
76
+ ### Identifier requirements
77
+
78
+ - `key` must be a base64url string, exactly 43 chars (32 random bytes, no
79
+ padding).
80
+ - `put/get/has/delete` throw a `TypeError` if the identifier is invalid.
81
+
82
+ ### Envelope requirements
83
+
84
+ - `envelope` must be an object with:
85
+ - `iv`: `Uint8Array` exactly 12 bytes (AES-GCM nonce).
86
+ - `ciphertext`: non-empty `ArrayBuffer`.
87
+ - `put` throws a `TypeError` if the envelope is invalid.
88
+
89
+ ## Build
90
+
91
+ ```bash
92
+ npm run build
93
+ ```
94
+
95
+ ## Tests
96
+
97
+ ```bash
98
+ npx playwright install
99
+ npm test
100
+ ```
101
+
102
+ Unit, integration, and end-to-end runs are available as well:
103
+
104
+ ```bash
105
+ npm run test:unit
106
+ npm run test:integration
107
+ npm run test:e2e
108
+ ```
109
+
110
+ Playwright runs Chromium, Firefox, WebKit, plus mobile emulation projects for
111
+ integration and end-to-end coverage.
112
+
113
+ ## Benchmarks
114
+
115
+ ```bash
116
+ npx playwright install
117
+ npm run bench
118
+ ```
119
+
120
+ The benchmark runs in Chromium via Playwright and times a batch of envelope
121
+ `put/has/get/delete` operations.
122
+
123
+ ```text
124
+ [bench] put 200 envelopes: 148.6ms
125
+ [bench] has 200 envelopes: 101.6ms
126
+ [bench] get 200 envelopes: 100.3ms
127
+ [bench] delete 200 envelopes: 123.8ms
128
+ ```
129
+
130
+ ## Manual browser check
131
+
132
+ - Run `npm run build`, open `in-browser-testing.html`, then use `storage` from
133
+ the console with envelope values.
134
+
135
+ ## Notes
136
+
137
+ - Requires `indexedDB` (modern browsers / worker runtimes).
138
+ - Data is scoped per origin; all tabs/workers on the same origin share the same
139
+ store.
@@ -0,0 +1,10 @@
1
+ import type { R2GetOptions, R2ListOptions, R2Objects, R2PutOptions } from "@cloudflare/workers-types";
2
+ export declare class BlobStorage {
3
+ #private;
4
+ put: (key: string, value: unknown, _options?: R2PutOptions) => Promise<void>;
5
+ head: (key: string) => Promise<boolean>;
6
+ get: (key: string, _options?: R2GetOptions) => Promise<unknown | null>;
7
+ delete: (key: string) => Promise<void>;
8
+ list: (_options?: R2ListOptions) => Promise<R2Objects>;
9
+ }
10
+ //# sourceMappingURL=class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"","sources":["../../src/BlobStorage/class.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,YAAY,EACb,MAAM,2BAA2B,CAAC;AAInC,qBAAa,WAAW;;IAgItB,GAAG,GACD,KAAK,MAAM,EACX,OAAO,OAAO,EACd,WAAW,YAAY,KACtB,OAAO,CAAC,IAAI,CAAC,CAwBd;IAEF,IAAI,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAc1C;IAEF,GAAG,GACD,KAAK,MAAM,EACX,WAAW,YAAY,KACtB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAcxB;IAEF,MAAM,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CASzC;IAEF,IAAI,GAAU,WAAW,aAAa,KAAG,OAAO,CAAC,SAAS,CAAC,CAiBzD;CACH"}
@@ -0,0 +1,169 @@
1
+ var _a;
2
+ export class BlobStorage {
3
+ static #dbName = "offdex";
4
+ static #storeName = "blobs";
5
+ #dbPromise = _a.#open();
6
+ static #open() {
7
+ return new Promise((resolve, reject) => {
8
+ const request = indexedDB.open(_a.#dbName, 1);
9
+ request.onupgradeneeded = () => {
10
+ const db = request.result;
11
+ if (!db.objectStoreNames.contains(_a.#storeName)) {
12
+ db.createObjectStore(_a.#storeName, { keyPath: "key" });
13
+ }
14
+ };
15
+ request.onsuccess = () => resolve(request.result);
16
+ request.onerror = () => reject(request.error);
17
+ });
18
+ }
19
+ static #throwMsgpackError(path, reason) {
20
+ throw new TypeError(`{offdex} BlobStorage.put: value at ${path} is not msgpack-compatible (${reason})`);
21
+ }
22
+ static #assertMsgpackCompatible(value, path, seen) {
23
+ if (value === null || value === undefined)
24
+ return;
25
+ const valueType = typeof value;
26
+ if (valueType === "string" ||
27
+ valueType === "boolean" ||
28
+ valueType === "number")
29
+ return;
30
+ if (valueType === "bigint") {
31
+ _a.#throwMsgpackError(path, "bigint is not supported without custom msgpack options");
32
+ }
33
+ if (valueType === "symbol") {
34
+ _a.#throwMsgpackError(path, "symbol values are not supported");
35
+ }
36
+ if (valueType === "function") {
37
+ _a.#throwMsgpackError(path, "functions are not supported");
38
+ }
39
+ if (value instanceof Date)
40
+ return;
41
+ if (ArrayBuffer.isView(value))
42
+ return;
43
+ if (value instanceof ArrayBuffer) {
44
+ _a.#throwMsgpackError(path, "ArrayBuffer is not supported; use Uint8Array instead");
45
+ }
46
+ if (typeof Blob === "function" && value instanceof Blob) {
47
+ _a.#throwMsgpackError(path, "Blob/File values are not supported; use Uint8Array instead");
48
+ }
49
+ if (typeof File === "function" && value instanceof File) {
50
+ _a.#throwMsgpackError(path, "Blob/File values are not supported; use Uint8Array instead");
51
+ }
52
+ if (value instanceof Map) {
53
+ _a.#throwMsgpackError(path, "Map is not supported");
54
+ }
55
+ if (value instanceof Set) {
56
+ _a.#throwMsgpackError(path, "Set is not supported");
57
+ }
58
+ if (value instanceof RegExp) {
59
+ _a.#throwMsgpackError(path, "RegExp is not supported");
60
+ }
61
+ if (value instanceof Error) {
62
+ _a.#throwMsgpackError(path, "Error is not supported");
63
+ }
64
+ if (Array.isArray(value)) {
65
+ if (seen.has(value)) {
66
+ _a.#throwMsgpackError(path, "circular references are not supported");
67
+ }
68
+ seen.add(value);
69
+ for (let index = 0; index < value.length; index += 1) {
70
+ _a.#assertMsgpackCompatible(value[index], `${path}[${index}]`, seen);
71
+ }
72
+ seen.delete(value);
73
+ return;
74
+ }
75
+ if (value && typeof value === "object") {
76
+ const proto = Object.getPrototypeOf(value);
77
+ if (proto !== Object.prototype && proto !== null) {
78
+ _a.#throwMsgpackError(path, "only plain objects are supported");
79
+ }
80
+ if (Object.getOwnPropertySymbols(value).length > 0) {
81
+ _a.#throwMsgpackError(path, "symbol keys are not supported");
82
+ }
83
+ if (seen.has(value)) {
84
+ _a.#throwMsgpackError(path, "circular references are not supported");
85
+ }
86
+ seen.add(value);
87
+ for (const [key, entry] of Object.entries(value)) {
88
+ _a.#assertMsgpackCompatible(entry, `${path}.${key}`, seen);
89
+ }
90
+ seen.delete(value);
91
+ return;
92
+ }
93
+ _a.#throwMsgpackError(path, "unsupported value type");
94
+ }
95
+ put = async (key, value, _options) => {
96
+ _a.#assertMsgpackCompatible(value, "$", new WeakSet());
97
+ const db = await this.#dbPromise;
98
+ await new Promise((resolve, reject) => {
99
+ const tx = db.transaction(_a.#storeName, "readwrite");
100
+ try {
101
+ tx.objectStore(_a.#storeName).put({
102
+ key: String(key),
103
+ value,
104
+ });
105
+ }
106
+ catch (error) {
107
+ const message = error instanceof Error ? error.message : String(error);
108
+ reject(new TypeError(`{offdex} BlobStorage.put: value must be structured-cloneable (${message})`));
109
+ return;
110
+ }
111
+ tx.oncomplete = () => resolve();
112
+ tx.onerror = () => reject(tx.error);
113
+ tx.onabort = () => reject(tx.error);
114
+ });
115
+ };
116
+ head = async (key) => {
117
+ const db = await this.#dbPromise;
118
+ const exists = await new Promise((resolve, reject) => {
119
+ const tx = db.transaction(_a.#storeName, "readonly");
120
+ const store = tx.objectStore(_a.#storeName);
121
+ const req = typeof store.getKey === "function" ? store.getKey(key) : store.get(key);
122
+ tx.oncomplete = () => resolve(req.result !== undefined);
123
+ tx.onerror = () => reject(tx.error);
124
+ tx.onabort = () => reject(tx.error);
125
+ });
126
+ return exists;
127
+ };
128
+ get = async (key, _options) => {
129
+ const db = await this.#dbPromise;
130
+ const row = await new Promise((resolve, reject) => {
131
+ const tx = db.transaction(_a.#storeName, "readonly");
132
+ const req = tx.objectStore(_a.#storeName).get(key);
133
+ tx.oncomplete = () => resolve(req.result);
134
+ tx.onerror = () => reject(tx.error);
135
+ tx.onabort = () => reject(tx.error);
136
+ });
137
+ if (!row)
138
+ return null;
139
+ return row.value;
140
+ };
141
+ delete = async (key) => {
142
+ const db = await this.#dbPromise;
143
+ await new Promise((resolve, reject) => {
144
+ const tx = db.transaction(_a.#storeName, "readwrite");
145
+ tx.objectStore(_a.#storeName).delete(key);
146
+ tx.oncomplete = () => resolve();
147
+ tx.onerror = () => reject(tx.error);
148
+ tx.onabort = () => reject(tx.error);
149
+ });
150
+ };
151
+ list = async (_options) => {
152
+ const db = await this.#dbPromise;
153
+ const keys = await new Promise((resolve, reject) => {
154
+ const tx = db.transaction(_a.#storeName, "readonly");
155
+ const req = tx.objectStore(_a.#storeName).getAllKeys();
156
+ tx.oncomplete = () => resolve(req.result);
157
+ tx.onerror = () => reject(tx.error);
158
+ tx.onabort = () => reject(tx.error);
159
+ });
160
+ return {
161
+ objects: keys.map((key) => ({ key })),
162
+ truncated: false,
163
+ delimitedPrefixes: [],
164
+ cursor: undefined,
165
+ };
166
+ };
167
+ }
168
+ _a = BlobStorage;
169
+ //# sourceMappingURL=class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.js","sourceRoot":"","sources":["../../src/BlobStorage/class.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { R2GetOptions, R2ListOptions, R2Objects, R2PutOptions } from "@cloudflare/workers-types";
2
+ export declare class ObjectStorage {
3
+ #private;
4
+ put: (key: string, value: unknown, _options?: R2PutOptions) => Promise<void>;
5
+ head: (key: string) => Promise<boolean>;
6
+ get: (key: string, _options?: R2GetOptions) => Promise<unknown | null>;
7
+ delete: (key: string) => Promise<void>;
8
+ list: (_options?: R2ListOptions) => Promise<R2Objects>;
9
+ }
10
+ //# sourceMappingURL=class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"","sources":["../../src/ObjectStorage/class.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,YAAY,EACb,MAAM,2BAA2B,CAAC;AAInC,qBAAa,aAAa;;IAsIxB,GAAG,GACD,KAAK,MAAM,EACX,OAAO,OAAO,EACd,WAAW,YAAY,KACtB,OAAO,CAAC,IAAI,CAAC,CAwBd;IAEF,IAAI,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAc1C;IAEF,GAAG,GACD,KAAK,MAAM,EACX,WAAW,YAAY,KACtB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAcxB;IAEF,MAAM,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CASzC;IAEF,IAAI,GAAU,WAAW,aAAa,KAAG,OAAO,CAAC,SAAS,CAAC,CAiBzD;CACH"}
@@ -0,0 +1,169 @@
1
+ var _a;
2
+ export class ObjectStorage {
3
+ static #dbName = "offdex";
4
+ static #storeName = "blobs";
5
+ #dbPromise = _a.#open();
6
+ static #open() {
7
+ return new Promise((resolve, reject) => {
8
+ const request = indexedDB.open(_a.#dbName, 1);
9
+ request.onupgradeneeded = () => {
10
+ const db = request.result;
11
+ if (!db.objectStoreNames.contains(_a.#storeName)) {
12
+ db.createObjectStore(_a.#storeName, { keyPath: "key" });
13
+ }
14
+ };
15
+ request.onsuccess = () => resolve(request.result);
16
+ request.onerror = () => reject(request.error);
17
+ });
18
+ }
19
+ static #throwMsgpackError(path, reason) {
20
+ throw new TypeError(`{offdex} ObjectStorage.put: value at ${path} is not msgpack-compatible (${reason})`);
21
+ }
22
+ static #assertMsgpackCompatible(value, path, seen) {
23
+ if (value === null || value === undefined)
24
+ return;
25
+ const valueType = typeof value;
26
+ if (valueType === "string" ||
27
+ valueType === "boolean" ||
28
+ valueType === "number")
29
+ return;
30
+ if (valueType === "bigint") {
31
+ _a.#throwMsgpackError(path, "bigint is not supported without custom msgpack options");
32
+ }
33
+ if (valueType === "symbol") {
34
+ _a.#throwMsgpackError(path, "symbol values are not supported");
35
+ }
36
+ if (valueType === "function") {
37
+ _a.#throwMsgpackError(path, "functions are not supported");
38
+ }
39
+ if (value instanceof Date)
40
+ return;
41
+ if (ArrayBuffer.isView(value))
42
+ return;
43
+ if (value instanceof ArrayBuffer) {
44
+ _a.#throwMsgpackError(path, "ArrayBuffer is not supported; use Uint8Array instead");
45
+ }
46
+ if (typeof Blob === "function" && value instanceof Blob) {
47
+ _a.#throwMsgpackError(path, "Blob/File values are not supported; use Uint8Array instead");
48
+ }
49
+ if (typeof File === "function" && value instanceof File) {
50
+ _a.#throwMsgpackError(path, "Blob/File values are not supported; use Uint8Array instead");
51
+ }
52
+ if (value instanceof Map) {
53
+ _a.#throwMsgpackError(path, "Map is not supported");
54
+ }
55
+ if (value instanceof Set) {
56
+ _a.#throwMsgpackError(path, "Set is not supported");
57
+ }
58
+ if (value instanceof RegExp) {
59
+ _a.#throwMsgpackError(path, "RegExp is not supported");
60
+ }
61
+ if (value instanceof Error) {
62
+ _a.#throwMsgpackError(path, "Error is not supported");
63
+ }
64
+ if (Array.isArray(value)) {
65
+ if (seen.has(value)) {
66
+ _a.#throwMsgpackError(path, "circular references are not supported");
67
+ }
68
+ seen.add(value);
69
+ for (let index = 0; index < value.length; index += 1) {
70
+ _a.#assertMsgpackCompatible(value[index], `${path}[${index}]`, seen);
71
+ }
72
+ seen.delete(value);
73
+ return;
74
+ }
75
+ if (value && typeof value === "object") {
76
+ const proto = Object.getPrototypeOf(value);
77
+ if (proto !== Object.prototype && proto !== null) {
78
+ _a.#throwMsgpackError(path, "only plain objects are supported");
79
+ }
80
+ if (Object.getOwnPropertySymbols(value).length > 0) {
81
+ _a.#throwMsgpackError(path, "symbol keys are not supported");
82
+ }
83
+ if (seen.has(value)) {
84
+ _a.#throwMsgpackError(path, "circular references are not supported");
85
+ }
86
+ seen.add(value);
87
+ for (const [key, entry] of Object.entries(value)) {
88
+ _a.#assertMsgpackCompatible(entry, `${path}.${key}`, seen);
89
+ }
90
+ seen.delete(value);
91
+ return;
92
+ }
93
+ _a.#throwMsgpackError(path, "unsupported value type");
94
+ }
95
+ put = async (key, value, _options) => {
96
+ _a.#assertMsgpackCompatible(value, "$", new WeakSet());
97
+ const db = await this.#dbPromise;
98
+ await new Promise((resolve, reject) => {
99
+ const tx = db.transaction(_a.#storeName, "readwrite");
100
+ try {
101
+ tx.objectStore(_a.#storeName).put({
102
+ key: String(key),
103
+ value,
104
+ });
105
+ }
106
+ catch (error) {
107
+ const message = error instanceof Error ? error.message : String(error);
108
+ reject(new TypeError(`{offdex} ObjectStorage.put: value must be structured-cloneable (${message})`));
109
+ return;
110
+ }
111
+ tx.oncomplete = () => resolve();
112
+ tx.onerror = () => reject(tx.error);
113
+ tx.onabort = () => reject(tx.error);
114
+ });
115
+ };
116
+ head = async (key) => {
117
+ const db = await this.#dbPromise;
118
+ const exists = await new Promise((resolve, reject) => {
119
+ const tx = db.transaction(_a.#storeName, "readonly");
120
+ const store = tx.objectStore(_a.#storeName);
121
+ const req = typeof store.getKey === "function" ? store.getKey(key) : store.get(key);
122
+ tx.oncomplete = () => resolve(req.result !== undefined);
123
+ tx.onerror = () => reject(tx.error);
124
+ tx.onabort = () => reject(tx.error);
125
+ });
126
+ return exists;
127
+ };
128
+ get = async (key, _options) => {
129
+ const db = await this.#dbPromise;
130
+ const row = await new Promise((resolve, reject) => {
131
+ const tx = db.transaction(_a.#storeName, "readonly");
132
+ const req = tx.objectStore(_a.#storeName).get(key);
133
+ tx.oncomplete = () => resolve(req.result);
134
+ tx.onerror = () => reject(tx.error);
135
+ tx.onabort = () => reject(tx.error);
136
+ });
137
+ if (!row)
138
+ return null;
139
+ return row.value;
140
+ };
141
+ delete = async (key) => {
142
+ const db = await this.#dbPromise;
143
+ await new Promise((resolve, reject) => {
144
+ const tx = db.transaction(_a.#storeName, "readwrite");
145
+ tx.objectStore(_a.#storeName).delete(key);
146
+ tx.oncomplete = () => resolve();
147
+ tx.onerror = () => reject(tx.error);
148
+ tx.onabort = () => reject(tx.error);
149
+ });
150
+ };
151
+ list = async (_options) => {
152
+ const db = await this.#dbPromise;
153
+ const keys = await new Promise((resolve, reject) => {
154
+ const tx = db.transaction(_a.#storeName, "readonly");
155
+ const req = tx.objectStore(_a.#storeName).getAllKeys();
156
+ tx.oncomplete = () => resolve(req.result);
157
+ tx.onerror = () => reject(tx.error);
158
+ tx.onabort = () => reject(tx.error);
159
+ });
160
+ return {
161
+ objects: keys.map((key) => ({ key })),
162
+ truncated: false,
163
+ delimitedPrefixes: [],
164
+ cursor: undefined,
165
+ };
166
+ };
167
+ }
168
+ _a = ObjectStorage;
169
+ //# sourceMappingURL=class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.js","sourceRoot":"","sources":["../../src/ObjectStorage/class.ts"],"names":[],"mappings":";AAUA,MAAM,OAAO,aAAa;IACxB,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC;IACnC,MAAM,CAAU,UAAU,GAAG,OAAO,CAAC;IAC5B,UAAU,GAAyB,EAAa,CAAC,KAAK,EAAE,CAAC;IAElE,MAAM,CAAC,KAAK;QACV,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,EAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,eAAe,GAAG,GAAG,EAAE;gBAC7B,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;gBAC1B,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAa,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC5D,EAAE,CAAC,iBAAiB,CAAC,EAAa,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC,CAAC;YACF,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,kBAAkB,CAAC,IAAY,EAAE,MAAc;QACpD,MAAM,IAAI,SAAS,CACjB,wCAAwC,IAAI,+BAA+B,MAAM,GAAG,CACrF,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,wBAAwB,CAC7B,KAAc,EACd,IAAY,EACZ,IAAqB;QAErB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QAElD,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;QAC/B,IACE,SAAS,KAAK,QAAQ;YACtB,SAAS,KAAK,SAAS;YACvB,SAAS,KAAK,QAAQ;YAEtB,OAAO;QACT,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,wDAAwD,CACzD,CAAC;QACJ,CAAC;QACD,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,iCAAiC,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAC7B,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,KAAK,YAAY,IAAI;YAAE,OAAO;QAClC,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,OAAO;QACtC,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACjC,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,sDAAsD,CACvD,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YACxD,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YACxD,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,YAAY,GAAG,EAAE,CAAC;YACzB,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,KAAK,YAAY,GAAG,EAAE,CAAC;YACzB,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,KAAK,YAAY,MAAM,EAAE,CAAC;YAC5B,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpB,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,uCAAuC,CACxC,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACrD,EAAa,CAAC,wBAAwB,CACpC,KAAK,CAAC,KAAK,CAAC,EACZ,GAAG,IAAI,IAAI,KAAK,GAAG,EACnB,IAAI,CACL,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACjD,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,kCAAkC,CACnC,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,+BAA+B,CAAC,CAAC;YAC1E,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpB,EAAa,CAAC,kBAAkB,CAC9B,IAAI,EACJ,uCAAuC,CACxC,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,EAAa,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,EAAa,CAAC,kBAAkB,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;IACnE,CAAC;IAED,GAAG,GAAG,KAAK,EACT,GAAW,EACX,KAAc,EACd,QAAuB,EACR,EAAE;QACjB,EAAa,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;QAElE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QACjC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YACjE,IAAI,CAAC;gBACH,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC;oBAC3C,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;oBAChB,KAAK;iBACQ,CAAC,CAAC;YACnB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,MAAM,CACJ,IAAI,SAAS,CACX,mEAAmE,OAAO,GAAG,CAC9E,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;YAChC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,GAAG,KAAK,EAAE,GAAW,EAAoB,EAAE;QAC7C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QAEjC,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5D,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAChE,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,CAAC,CAAC;YACvD,MAAM,GAAG,GACP,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1E,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;YACxD,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,GAAG,GAAG,KAAK,EACT,GAAW,EACX,QAAuB,EACE,EAAE;QAC3B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QAEjC,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjE,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAChE,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9D,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAyB,CAAC,CAAC;YAC7D,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QAEtB,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAC5C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QACjC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YACjE,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;YAChC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,GAAG,KAAK,EAAE,QAAwB,EAAsB,EAAE;QAC5D,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QAEjC,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,CAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3D,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAChE,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,EAAa,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE,CAAC;YAClE,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAkB,CAAC,CAAC;YACtD,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAe,CAAA,CAAC;YACjD,SAAS,EAAE,KAAK;YAChB,iBAAiB,EAAE,EAAE;YACrB,MAAM,EAAE,SAAS;SACL,CAAC;IACjB,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { OffdexEnvelope, OffdexIdentifier } from "./types.js";
2
+ export type { OffdexEnvelope, OffdexIdentifier };
3
+ export declare class OffdexEnvelopeStorage {
4
+ #private;
5
+ static open(): Promise<OffdexEnvelopeStorage>;
6
+ static destroy(): Promise<void>;
7
+ static put(key: OffdexIdentifier, value: OffdexEnvelope): Promise<void>;
8
+ static has(key: OffdexIdentifier): Promise<boolean>;
9
+ static get(key: OffdexIdentifier): Promise<OffdexEnvelope | null>;
10
+ static delete(key: OffdexIdentifier): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/class.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAOjD,qBAAa,qBAAqB;;WAKnB,IAAI,IAAI,OAAO,CAAC,qBAAqB,CAAC;WAmBtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;WAsBxB,GAAG,CACd,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,IAAI,CAAC;WAeH,GAAG,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;WAiB5C,GAAG,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;WAgB1D,MAAM,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAa1D"}
@@ -0,0 +1,88 @@
1
+ import { assertEnvelope, assertIdentifier } from "./validation.js";
2
+ export class OffdexEnvelopeStorage {
3
+ static #dbName = "offdex";
4
+ static #storeName = "envelopes";
5
+ static #db = null;
6
+ static async open() {
7
+ if (this.#db)
8
+ return OffdexEnvelopeStorage;
9
+ this.#db = await new Promise((resolve, reject) => {
10
+ const request = indexedDB.open(this.#dbName, 1);
11
+ request.onupgradeneeded = () => {
12
+ const db = request.result;
13
+ if (!db.objectStoreNames.contains(this.#storeName)) {
14
+ db.createObjectStore(this.#storeName, { keyPath: "key" });
15
+ }
16
+ };
17
+ request.onsuccess = () => resolve(request.result);
18
+ request.onerror = () => reject(request.error);
19
+ });
20
+ return OffdexEnvelopeStorage;
21
+ }
22
+ static async destroy() {
23
+ if (this.#db) {
24
+ this.#db.close();
25
+ this.#db = null;
26
+ }
27
+ await new Promise((resolve, reject) => {
28
+ const req = indexedDB.deleteDatabase(this.#dbName);
29
+ req.onsuccess = () => resolve();
30
+ req.onerror = () => reject(req.error);
31
+ req.onblocked = () => reject(new Error("{offdex} database deletion blocked"));
32
+ });
33
+ }
34
+ static #requireDb() {
35
+ if (!this.#db) {
36
+ throw new Error("{offdex} storage not opened; call open() first");
37
+ }
38
+ return this.#db;
39
+ }
40
+ static async put(key, value) {
41
+ assertIdentifier(key);
42
+ assertEnvelope(value);
43
+ const db = this.#requireDb();
44
+ await new Promise((resolve, reject) => {
45
+ const tx = db.transaction(this.#storeName, "readwrite");
46
+ tx.objectStore(this.#storeName).put({ key, value });
47
+ tx.oncomplete = () => resolve();
48
+ tx.onerror = () => reject(tx.error);
49
+ tx.onabort = () => reject(tx.error);
50
+ });
51
+ }
52
+ static async has(key) {
53
+ assertIdentifier(key);
54
+ const db = this.#requireDb();
55
+ return new Promise((resolve, reject) => {
56
+ const tx = db.transaction(this.#storeName, "readonly");
57
+ const store = tx.objectStore(this.#storeName);
58
+ const req = typeof store.getKey === "function" ? store.getKey(key) : store.get(key);
59
+ tx.oncomplete = () => resolve(req.result !== undefined);
60
+ tx.onerror = () => reject(tx.error);
61
+ tx.onabort = () => reject(tx.error);
62
+ });
63
+ }
64
+ static async get(key) {
65
+ assertIdentifier(key);
66
+ const db = this.#requireDb();
67
+ const row = await new Promise((resolve, reject) => {
68
+ const tx = db.transaction(this.#storeName, "readonly");
69
+ const req = tx.objectStore(this.#storeName).get(key);
70
+ tx.oncomplete = () => resolve(req.result);
71
+ tx.onerror = () => reject(tx.error);
72
+ tx.onabort = () => reject(tx.error);
73
+ });
74
+ return row ? row.value : null;
75
+ }
76
+ static async delete(key) {
77
+ assertIdentifier(key);
78
+ const db = this.#requireDb();
79
+ await new Promise((resolve, reject) => {
80
+ const tx = db.transaction(this.#storeName, "readwrite");
81
+ tx.objectStore(this.#storeName).delete(key);
82
+ tx.oncomplete = () => resolve();
83
+ tx.onerror = () => reject(tx.error);
84
+ tx.onabort = () => reject(tx.error);
85
+ });
86
+ }
87
+ }
88
+ //# sourceMappingURL=class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.js","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/class.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type OffdexIdentifier = Base64URLString; /** 43 chars, no padding */
2
+ export type OffdexEnvelope = {
3
+ iv: Uint8Array;
4
+ ciphertext: ArrayBuffer;
5
+ };
6
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC,CAAC,2BAA2B;AAE3E,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,UAAU,CAAC;IACf,UAAU,EAAE,WAAW,CAAC;CACzB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { OffdexEnvelope, OffdexIdentifier } from "./types.js";
2
+ export declare const assertIdentifier: (key: string) => asserts key is OffdexIdentifier;
3
+ export declare const assertEnvelope: (value: unknown) => asserts value is OffdexEnvelope;
4
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE,eAAO,MAAM,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,IAAI,gBAQ9D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,CAC3B,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,KAAK,IAAI,cAmBrB,CAAC"}
@@ -0,0 +1,22 @@
1
+ export const assertIdentifier = (key) => {
2
+ if (key.length !== 43) {
3
+ throw new TypeError("{offdex} identifier must be exactly 43 chars");
4
+ }
5
+ if (!/^[A-Za-z0-9_-]+$/.test(key)) {
6
+ throw new TypeError("{offdex} identifier must be base64url (no padding)");
7
+ }
8
+ };
9
+ export const assertEnvelope = (value) => {
10
+ if (typeof value !== "object" || value === null) {
11
+ throw new TypeError("{offdex} envelope must be an object");
12
+ }
13
+ const env = value;
14
+ if (!(env.iv instanceof Uint8Array) || env.iv.byteLength !== 12) {
15
+ throw new TypeError("{offdex} iv must be 12-byte Uint8Array (AES-GCM)");
16
+ }
17
+ if (!(env.ciphertext instanceof ArrayBuffer) ||
18
+ env.ciphertext.byteLength === 0) {
19
+ throw new TypeError("{offdex} ciphertext must be non-empty ArrayBuffer");
20
+ }
21
+ };
22
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/OffdexEnvelopeStorage/validation.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAAC,GAAW,EAAmC,EAAE;IACjD,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAEY,CACrC,KAAc,EACmB,EAAE;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,GAAG,GAAG,KAAgC,CAAC;IAE7C,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,YAAY,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,UAAU,KAAK,EAAE,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC1E,CAAC;IAED,IACE,CAAC,CAAC,GAAG,CAAC,UAAU,YAAY,WAAW,CAAC;QACxC,GAAG,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC,EAC/B,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,mDAAmD,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { OffdexEnvelopeStorage } from "./OffdexEnvelopeStorage/class.js";
2
+ declare const offdex: OffdexEnvelopeStorage;
3
+ export default offdex;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,QAAA,MAAM,MAAM,uBAAqC,CAAC;AAClD,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { OffdexEnvelopeStorage } from "./OffdexEnvelopeStorage/class.js";
2
+ const offdex = await OffdexEnvelopeStorage.open();
3
+ export default offdex;
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,CAAC;AAClD,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,45 +1,58 @@
1
1
  {
2
2
  "name": "offdex",
3
- "version": "2.0.0",
4
- "description": "Minimal, unbiased IndexedDB object-store wrapper keyed by a string. Shared across browser threads, offline-persistent, simple API.",
3
+ "version": "4.0.0",
4
+ "description": "IndexedDB-backed envelope storage for AES-GCM payloads in browsers and workers.",
5
5
  "keywords": [
6
6
  "indexeddb",
7
- "offline",
7
+ "envelope",
8
+ "ciphertext",
9
+ "crypto",
10
+ "aes-gcm",
11
+ "base64url",
8
12
  "storage",
9
13
  "browser-storage",
10
14
  "web-storage",
11
- "object-store",
12
- "key-value",
13
- "key",
14
- "persist",
15
- "threads",
16
- "workers",
17
- "shared",
18
- "minimal"
15
+ "offline",
16
+ "worker",
17
+ "persist"
19
18
  ],
20
19
  "license": "MIT",
21
20
  "type": "module",
22
- "main": "src/index.js",
23
- "types": "src/index.d.ts",
21
+ "main": "dist/index.js",
22
+ "types": "dist/index.d.ts",
24
23
  "repository": {
25
24
  "type": "git",
26
- "url": "git+https://github.com/jortsupetterson/offdex.git"
25
+ "url": "git+https://github.com/z-base/offdex.git"
27
26
  },
28
27
  "bugs": {
29
- "url": "https://github.com/jortsupetterson/offdex/issues"
28
+ "url": "https://github.com/z-base/offdex/issues"
30
29
  },
31
- "homepage": "https://github.com/jortsupetterson/offdex#readme",
30
+ "homepage": "https://github.com/z-base/offdex#readme",
32
31
  "exports": {
33
32
  ".": {
34
- "types": "./src/index.d.ts",
35
- "import": "./src/index.js"
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js"
36
35
  },
37
36
  "./package.json": "./package.json"
38
37
  },
39
38
  "files": [
40
- "src",
39
+ "dist",
41
40
  "LICENSE",
42
41
  "README.md"
43
42
  ],
44
- "sideEffects": false
43
+ "sideEffects": false,
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.build.json",
46
+ "test": "npm run build && playwright test",
47
+ "test:unit": "playwright test --project=unit",
48
+ "test:integration": "npm run build && playwright test --project=chromium --project=firefox --project=webkit --project=chromium-mobile --project=webkit-mobile tests/integration",
49
+ "test:e2e": "npm run build && playwright test --project=chromium --project=firefox --project=webkit --project=chromium-mobile --project=webkit-mobile tests/e2e",
50
+ "bench": "npm run build && playwright test -c playwright.bench.config.ts",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "devDependencies": {
54
+ "@playwright/test": "^1.57.0",
55
+ "@types/node": "^25.0.9",
56
+ "typescript": "^5.9.3"
57
+ }
45
58
  }
@@ -1,184 +0,0 @@
1
- export class ObjectStore {
2
- static #idb = "offdex";
3
- static #store = "objects";
4
-
5
- /**
6
- * @returns {Promise<IDBDatabase>}
7
- */
8
- static #open() {
9
- return new Promise((resolve, reject) => {
10
- const request = indexedDB.open(ObjectStore.#idb);
11
-
12
- request.addEventListener("upgradeneeded", () => {
13
- const db = request.result;
14
- if (!db.objectStoreNames.contains(ObjectStore.#store)) {
15
- db.createObjectStore(ObjectStore.#store, { keyPath: "key" });
16
- }
17
- });
18
-
19
- request.addEventListener("success", () => {
20
- resolve(request.result);
21
- });
22
-
23
- request.addEventListener("error", () => {
24
- reject(request.error);
25
- });
26
- });
27
- }
28
-
29
- constructor() {
30
- this.instance = ObjectStore.#open();
31
- }
32
-
33
- /**
34
- * @param {import("../types").StoredObject} object
35
- * @returns {Promise<void>}
36
- */
37
- async put(object) {
38
- const db = await this.instance;
39
- return new Promise((resolve, reject) => {
40
- const transaction = db.transaction(ObjectStore.#store, "readwrite");
41
- const store = transaction.objectStore(ObjectStore.#store);
42
- store.put(object);
43
-
44
- transaction.oncomplete = () => resolve();
45
- transaction.onerror = () =>
46
- reject(new Error(`{offdex} ObjectStore: ${transaction.error}`));
47
- });
48
- }
49
-
50
- /**
51
- * @param {string} key
52
- * @returns {Promise<import("../types").StoredObject | undefined>}
53
- */
54
- async get(key) {
55
- const db = await this.instance;
56
-
57
- return new Promise((resolve, reject) => {
58
- const transaction = db.transaction(ObjectStore.#store, "readonly");
59
- const store = transaction.objectStore(ObjectStore.#store);
60
- const request = store.get(key);
61
-
62
- transaction.oncomplete = () => {
63
- if (!request.result) {
64
- resolve(undefined);
65
- return;
66
- }
67
-
68
- resolve(request.result);
69
- };
70
-
71
- transaction.onerror = () =>
72
- reject(new Error(`{offdex} ObjectStore: ${transaction.error}`));
73
- });
74
- }
75
-
76
- /**
77
- * @param {string} key
78
- * @returns {Promise<void>}
79
- */
80
- async delete(key) {
81
- const db = await this.instance;
82
- return new Promise((resolve, reject) => {
83
- const transaction = db.transaction(ObjectStore.#store, "readwrite");
84
- const store = transaction.objectStore(ObjectStore.#store);
85
- store.delete(key);
86
- transaction.oncomplete = () => resolve();
87
- transaction.onerror = () =>
88
- reject(new Error(`{offdex} ObjectStore: ${transaction.error}`));
89
- });
90
- }
91
-
92
- /**
93
- * @param {import("../types").StoredObject[]} objects
94
- * @returns {Promise<void>}
95
- */
96
- async putAll(objects) {
97
- const db = await this.instance;
98
- return new Promise((resolve, reject) => {
99
- const transaction = db.transaction(ObjectStore.#store, "readwrite");
100
- const store = transaction.objectStore(ObjectStore.#store);
101
-
102
- for (const object of objects) {
103
- store.put(object);
104
- }
105
-
106
- transaction.oncomplete = () => resolve();
107
- transaction.onerror = () =>
108
- reject(new Error(`{offdex} ObjectStore.putAll: ${transaction.error}`));
109
- });
110
- }
111
-
112
- /**
113
- * @param {import("../StorageQuery/index.js").StorageQuery | ((object: import("../types").StoredObject) => boolean)} queryOrPredicate
114
- * @returns {Promise<import("../types").StoredObject[]>}
115
- */
116
- async getAllMatches(queryOrPredicate) {
117
- const db = await this.instance;
118
- const objectStoreInstance = this;
119
-
120
- const predicate =
121
- typeof queryOrPredicate === "function"
122
- ? queryOrPredicate
123
- : (object) => queryOrPredicate.matches(object);
124
-
125
- return new Promise((resolve, reject) => {
126
- const transaction = db.transaction(ObjectStore.#store, "readonly");
127
- const store = transaction.objectStore(ObjectStore.#store);
128
- const request = store.openCursor();
129
-
130
- const results = [];
131
-
132
- request.onsuccess = () => {
133
- const cursor = request.result;
134
- if (!cursor) return;
135
-
136
- const value = cursor.value;
137
-
138
- if (predicate(value)) {
139
- results.push(value);
140
- }
141
-
142
- cursor.continue();
143
- };
144
-
145
- transaction.oncomplete = () => resolve(results);
146
- transaction.onerror = () =>
147
- reject(new Error(`{offdex} ObjectStore: ${transaction.error}`));
148
- });
149
- }
150
-
151
- /**
152
- * @param {import("../StorageQuery/index.js").StorageQuery | ((object: import("../types").StoredObject) => boolean)} queryOrPredicate
153
- * @returns {Promise<void>}
154
- */
155
- async deleteAllMatches(queryOrPredicate) {
156
- const db = await this.instance;
157
-
158
- const predicate =
159
- typeof queryOrPredicate === "function"
160
- ? queryOrPredicate
161
- : (object) => queryOrPredicate.matches(object);
162
-
163
- return new Promise((resolve, reject) => {
164
- const transaction = db.transaction(ObjectStore.#store, "readwrite");
165
- const store = transaction.objectStore(ObjectStore.#store);
166
- const request = store.openCursor();
167
-
168
- request.onsuccess = () => {
169
- const cursor = request.result;
170
- if (!cursor) return;
171
-
172
- if (predicate(cursor.value)) {
173
- cursor.delete();
174
- }
175
-
176
- cursor.continue();
177
- };
178
-
179
- transaction.oncomplete = () => resolve();
180
- transaction.onerror = () =>
181
- reject(new Error(`{offdex} ObjectStore: ${transaction.error}`));
182
- });
183
- }
184
- }
@@ -1,12 +0,0 @@
1
- import { ObservableValue } from "../ObservableValue/index.js";
2
-
3
- export class ObservableObject {
4
- /**
5
- * @param {import("../types").StoredObject} object
6
- */
7
- constructor(object) {
8
- for (const key in object) {
9
- this[key] = new ObservableValue(object[key]);
10
- }
11
- }
12
- }
@@ -1,58 +0,0 @@
1
- export class ObservableValue {
2
- /**
3
- * @param {any} value
4
- * @param {any} [fallback]
5
- */
6
- constructor(value, fallback = null) {
7
- this.value = value;
8
- this.fallback = fallback;
9
- /** @type {Set<{ event: "set" | "reset"; callback: (value: any) => void }>} */
10
- this.observers = new Set();
11
- }
12
- get() {
13
- return this.value;
14
- }
15
- /**
16
- * @param {any} newValue
17
- * @returns {void}
18
- */
19
- set(newValue) {
20
- if (newValue === this.value) return;
21
- this.value = newValue;
22
- const changeObservers = [...this.observers].filter(
23
- (observer) => observer.event === "set"
24
- );
25
- for (const observer of changeObservers) {
26
- observer.callback(newValue);
27
- }
28
- }
29
- reset() {
30
- this.value = this.fallback;
31
- const resetObservers = [...this.observers].filter(
32
- (observer) => observer.event === "reset"
33
- );
34
- for (const observer of resetObservers) {
35
- observer.callback(this.fallback);
36
- }
37
- }
38
- /**
39
- * @param {"set" | "reset"} event
40
- * @param {Function} callback
41
- * @returns {() => void}
42
- */
43
- observe(event, callback) {
44
- if (event !== "set" && event !== "reset")
45
- throw new Error(
46
- "{offdex} ObservableValue: Supported `event` parameters are [set, reset]"
47
- );
48
- if (typeof callback !== "function")
49
- throw new Error(
50
- "{offdex} ObservableValue: Paramater `callback` must be of type function"
51
- );
52
- const observer = { event, callback };
53
- this.observers.add(observer);
54
- return () => {
55
- this.observers.delete(observer);
56
- };
57
- }
58
- }
@@ -1,12 +0,0 @@
1
- export class StorageQuery {
2
- constructor(args = {}) {
3
- this.args = args;
4
- }
5
-
6
- matches(object) {
7
- for (const key in this.args) {
8
- if (object[key] !== this.args[key]) return false;
9
- }
10
- return true;
11
- }
12
- }
package/src/index.d.ts DELETED
@@ -1,36 +0,0 @@
1
- export type StoredObject = { key: string } & Record<string, unknown>;
2
-
3
- export class StorageQuery {
4
- constructor(args?: Record<string, unknown>);
5
- args: Record<string, unknown>;
6
- matches(object: StoredObject): boolean;
7
- }
8
-
9
- export class ObservableValue<T = unknown> {
10
- constructor(value: T, fallback?: T);
11
- get(): T;
12
- set(newValue: T): void;
13
- reset(): void;
14
- observe(event: "set" | "reset", callback: (value: T) => void): () => void;
15
- }
16
-
17
- export class ObservableObject<T extends StoredObject = StoredObject> {
18
- constructor(object: T);
19
- [key: string]: ObservableValue<unknown>;
20
- }
21
-
22
- export class ObjectStore {
23
- constructor();
24
- put(object: StoredObject): Promise<void>;
25
- get(key: string): Promise<StoredObject | undefined>;
26
- delete(key: string): Promise<void>;
27
- putAll(objects: StoredObject[]): Promise<void>;
28
- getAllMatches(
29
- queryOrFilter: StorageQuery | ((object: StoredObject) => boolean)
30
- ): Promise<StoredObject[]>;
31
- deleteAllMatches(
32
- queryOrFilter: StorageQuery | ((object: StoredObject) => boolean)
33
- ): Promise<void>;
34
- }
35
-
36
- export const storage: ObjectStore;
package/src/index.js DELETED
@@ -1,7 +0,0 @@
1
- import { ObjectStore } from "./ObjectStore/index.js";
2
- import { StorageQuery } from "./StorageQuery/index.js";
3
- import { ObservableValue } from "./ObservableValue/index.js";
4
- import { ObservableObject } from "./ObservableObject/index.js";
5
-
6
- export { ObjectStore, StorageQuery, ObservableValue, ObservableObject };
7
- export const storage = new ObjectStore();
package/src/types.d.ts DELETED
@@ -1 +0,0 @@
1
- export type StoredObject = { key: string } & Record<string, unknown>;