offdex 3.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 +69 -50
- package/dist/OffdexEnvelopeStorage/class.d.ts +12 -0
- package/dist/OffdexEnvelopeStorage/class.d.ts.map +1 -0
- package/dist/OffdexEnvelopeStorage/class.js +88 -0
- package/dist/OffdexEnvelopeStorage/class.js.map +1 -0
- package/dist/OffdexEnvelopeStorage/types.d.ts +6 -0
- package/dist/OffdexEnvelopeStorage/types.d.ts.map +1 -0
- package/dist/OffdexEnvelopeStorage/types.js +2 -0
- package/dist/OffdexEnvelopeStorage/types.js.map +1 -0
- package/dist/OffdexEnvelopeStorage/validation.d.ts +4 -0
- package/dist/OffdexEnvelopeStorage/validation.d.ts.map +1 -0
- package/dist/OffdexEnvelopeStorage/validation.js +22 -0
- package/dist/OffdexEnvelopeStorage/validation.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +14 -15
package/README.md
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
# Offdex
|
|
2
2
|
|
|
3
|
-
IndexedDB-backed
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
tabs and workers without schema migrations.
|
|
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.
|
|
7
6
|
|
|
8
7
|
## Why use Offdex
|
|
9
8
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- Data shared per origin across tabs and workers (instance per JavaScript realm).
|
|
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).
|
|
15
13
|
- Offline persistence via IndexedDB.
|
|
16
14
|
- TypeScript-first.
|
|
17
|
-
- Singleton-only API: one shared `storage` instance per JavaScript realm (per thread).
|
|
18
15
|
|
|
19
16
|
## Install
|
|
20
17
|
|
|
21
18
|
```bash
|
|
22
19
|
npm install offdex
|
|
20
|
+
# or
|
|
21
|
+
pnpm add offdex
|
|
22
|
+
# or
|
|
23
|
+
yarn add offdex
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
## Quick start
|
|
@@ -27,16 +28,33 @@ npm install offdex
|
|
|
27
28
|
```js
|
|
28
29
|
import storage from "offdex";
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(/=+$/, "");
|
|
41
|
+
};
|
|
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);
|
|
35
55
|
}
|
|
36
56
|
|
|
37
|
-
await storage.
|
|
38
|
-
const bytes = await storage.get("bytes");
|
|
39
|
-
console.log(bytes instanceof Uint8Array, bytes);
|
|
57
|
+
await storage.delete(key);
|
|
40
58
|
```
|
|
41
59
|
|
|
42
60
|
## API
|
|
@@ -44,36 +62,29 @@ console.log(bytes instanceof Uint8Array, bytes);
|
|
|
44
62
|
### `storage` (default export)
|
|
45
63
|
|
|
46
64
|
Singleton storage instance per JavaScript realm backed by the `offdex` database
|
|
47
|
-
and `
|
|
48
|
-
and workers.
|
|
65
|
+
and `envelopes` object store. The underlying store is shared per origin across
|
|
66
|
+
tabs and workers.
|
|
49
67
|
|
|
50
68
|
### Methods
|
|
51
69
|
|
|
52
|
-
- `put(key,
|
|
53
|
-
- `get(key
|
|
54
|
-
- `
|
|
70
|
+
- `put(key, envelope)` -> `Promise<void>`
|
|
71
|
+
- `get(key)` -> `Promise<OffdexEnvelope | null>`
|
|
72
|
+
- `has(key)` -> `Promise<boolean>`
|
|
55
73
|
- `delete(key)` -> `Promise<void>`
|
|
56
|
-
- `
|
|
74
|
+
- `destroy()` -> `Promise<void>`
|
|
57
75
|
|
|
58
|
-
###
|
|
76
|
+
### Identifier requirements
|
|
59
77
|
|
|
60
|
-
- `
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
objects, `Date`, and `ArrayBufferView` (typed arrays/DataView).
|
|
64
|
-
- Not allowed: `BigInt`, `Map`, `Set`, `RegExp`, `Error`, `Blob`/`File`,
|
|
65
|
-
`ArrayBuffer`, functions, symbols, or circular references.
|
|
66
|
-
- `put` throws a `TypeError` if a value is not msgpack-compatible.
|
|
67
|
-
- MessagePack note: `undefined` decodes as `null`, and binary decodes as
|
|
68
|
-
`Uint8Array`.
|
|
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.
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
### Envelope requirements
|
|
71
83
|
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
- `
|
|
76
|
-
- `list` currently returns all keys; prefix/limit/cursor options are ignored.
|
|
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.
|
|
77
88
|
|
|
78
89
|
## Build
|
|
79
90
|
|
|
@@ -88,7 +99,16 @@ npx playwright install
|
|
|
88
99
|
npm test
|
|
89
100
|
```
|
|
90
101
|
|
|
91
|
-
|
|
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.
|
|
92
112
|
|
|
93
113
|
## Benchmarks
|
|
94
114
|
|
|
@@ -97,24 +117,23 @@ npx playwright install
|
|
|
97
117
|
npm run bench
|
|
98
118
|
```
|
|
99
119
|
|
|
100
|
-
The benchmark runs in Chromium via Playwright and times a batch of
|
|
101
|
-
`put/
|
|
120
|
+
The benchmark runs in Chromium via Playwright and times a batch of envelope
|
|
121
|
+
`put/has/get/delete` operations.
|
|
102
122
|
|
|
103
123
|
```text
|
|
104
|
-
[bench] put 200
|
|
105
|
-
[bench]
|
|
106
|
-
[bench] get 200
|
|
107
|
-
[bench]
|
|
108
|
-
[bench] delete 200 items: 123.8ms
|
|
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
|
|
109
128
|
```
|
|
110
129
|
|
|
111
130
|
## Manual browser check
|
|
112
131
|
|
|
113
132
|
- Run `npm run build`, open `in-browser-testing.html`, then use `storage` from
|
|
114
|
-
the console with
|
|
133
|
+
the console with envelope values.
|
|
115
134
|
|
|
116
135
|
## Notes
|
|
117
136
|
|
|
118
137
|
- Requires `indexedDB` (modern browsers / worker runtimes).
|
|
119
138
|
- Data is scoped per origin; all tabs/workers on the same origin share the same
|
|
120
|
-
store.
|
|
139
|
+
store.
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare const
|
|
3
|
-
export default
|
|
1
|
+
import { OffdexEnvelopeStorage } from "./OffdexEnvelopeStorage/class.js";
|
|
2
|
+
declare const offdex: OffdexEnvelopeStorage;
|
|
3
|
+
export default offdex;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
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
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { OffdexEnvelopeStorage } from "./OffdexEnvelopeStorage/class.js";
|
|
2
|
+
const offdex = await OffdexEnvelopeStorage.open();
|
|
3
|
+
export default offdex;
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
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,19 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "offdex",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "IndexedDB-backed
|
|
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
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
7
|
+
"envelope",
|
|
8
|
+
"ciphertext",
|
|
9
|
+
"crypto",
|
|
10
|
+
"aes-gcm",
|
|
11
|
+
"base64url",
|
|
11
12
|
"storage",
|
|
12
13
|
"browser-storage",
|
|
13
14
|
"web-storage",
|
|
14
|
-
"r2",
|
|
15
|
-
"cloudflare",
|
|
16
|
-
"bucket",
|
|
17
15
|
"offline",
|
|
18
16
|
"worker",
|
|
19
17
|
"persist"
|
|
@@ -24,12 +22,12 @@
|
|
|
24
22
|
"types": "dist/index.d.ts",
|
|
25
23
|
"repository": {
|
|
26
24
|
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/
|
|
25
|
+
"url": "git+https://github.com/z-base/offdex.git"
|
|
28
26
|
},
|
|
29
27
|
"bugs": {
|
|
30
|
-
"url": "https://github.com/
|
|
28
|
+
"url": "https://github.com/z-base/offdex/issues"
|
|
31
29
|
},
|
|
32
|
-
"homepage": "https://github.com/
|
|
30
|
+
"homepage": "https://github.com/z-base/offdex#readme",
|
|
33
31
|
"exports": {
|
|
34
32
|
".": {
|
|
35
33
|
"types": "./dist/index.d.ts",
|
|
@@ -46,14 +44,15 @@
|
|
|
46
44
|
"scripts": {
|
|
47
45
|
"build": "tsc -p tsconfig.build.json",
|
|
48
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",
|
|
49
50
|
"bench": "npm run build && playwright test -c playwright.bench.config.ts",
|
|
50
51
|
"prepublishOnly": "npm run build"
|
|
51
52
|
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@cloudflare/workers-types": "^4.20260101.0"
|
|
54
|
-
},
|
|
55
53
|
"devDependencies": {
|
|
56
54
|
"@playwright/test": "^1.57.0",
|
|
55
|
+
"@types/node": "^25.0.9",
|
|
57
56
|
"typescript": "^5.9.3"
|
|
58
57
|
}
|
|
59
58
|
}
|