@interocitor/core 0.0.0-beta.2
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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/adapters/cloudflare.d.ts +72 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -0
- package/dist/adapters/cloudflare.js +227 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/adapters/google-drive.d.ts +64 -0
- package/dist/adapters/google-drive.d.ts.map +1 -0
- package/dist/adapters/google-drive.js +340 -0
- package/dist/adapters/google-drive.js.map +1 -0
- package/dist/adapters/memory.d.ts +45 -0
- package/dist/adapters/memory.d.ts.map +1 -0
- package/dist/adapters/memory.js +129 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/webdav.d.ts +59 -0
- package/dist/adapters/webdav.d.ts.map +1 -0
- package/dist/adapters/webdav.js +247 -0
- package/dist/adapters/webdav.js.map +1 -0
- package/dist/core/codec.d.ts +20 -0
- package/dist/core/codec.d.ts.map +1 -0
- package/dist/core/codec.js +66 -0
- package/dist/core/codec.js.map +1 -0
- package/dist/core/compaction.d.ts +37 -0
- package/dist/core/compaction.d.ts.map +1 -0
- package/dist/core/compaction.js +134 -0
- package/dist/core/compaction.js.map +1 -0
- package/dist/core/crdt.d.ts +33 -0
- package/dist/core/crdt.d.ts.map +1 -0
- package/dist/core/crdt.js +188 -0
- package/dist/core/crdt.js.map +1 -0
- package/dist/core/flush.d.ts +9 -0
- package/dist/core/flush.d.ts.map +1 -0
- package/dist/core/flush.js +41 -0
- package/dist/core/flush.js.map +1 -0
- package/dist/core/hlc.d.ts +25 -0
- package/dist/core/hlc.d.ts.map +1 -0
- package/dist/core/hlc.js +76 -0
- package/dist/core/hlc.js.map +1 -0
- package/dist/core/internals.d.ts +25 -0
- package/dist/core/internals.d.ts.map +1 -0
- package/dist/core/internals.js +54 -0
- package/dist/core/internals.js.map +1 -0
- package/dist/core/manifest.d.ts +31 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +111 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/pull.d.ts +26 -0
- package/dist/core/pull.d.ts.map +1 -0
- package/dist/core/pull.js +98 -0
- package/dist/core/pull.js.map +1 -0
- package/dist/core/row-id.d.ts +12 -0
- package/dist/core/row-id.d.ts.map +1 -0
- package/dist/core/row-id.js +12 -0
- package/dist/core/row-id.js.map +1 -0
- package/dist/core/schema-types.d.ts +13 -0
- package/dist/core/schema-types.d.ts.map +1 -0
- package/dist/core/schema-types.js +18 -0
- package/dist/core/schema-types.js.map +1 -0
- package/dist/core/schema-types.type-test.d.ts +2 -0
- package/dist/core/schema-types.type-test.d.ts.map +1 -0
- package/dist/core/schema-types.type-test.js +149 -0
- package/dist/core/schema-types.type-test.js.map +1 -0
- package/dist/core/sync-engine.d.ts +158 -0
- package/dist/core/sync-engine.d.ts.map +1 -0
- package/dist/core/sync-engine.js +714 -0
- package/dist/core/sync-engine.js.map +1 -0
- package/dist/core/table.d.ts +60 -0
- package/dist/core/table.d.ts.map +1 -0
- package/dist/core/table.js +106 -0
- package/dist/core/table.js.map +1 -0
- package/dist/core/types.d.ts +478 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -0
- package/dist/crypto/encryption.d.ts +57 -0
- package/dist/crypto/encryption.d.ts.map +1 -0
- package/dist/crypto/encryption.js +195 -0
- package/dist/crypto/encryption.js.map +1 -0
- package/dist/crypto/keys.d.ts +48 -0
- package/dist/crypto/keys.d.ts.map +1 -0
- package/dist/crypto/keys.js +55 -0
- package/dist/crypto/keys.js.map +1 -0
- package/dist/handshake/channel.d.ts +117 -0
- package/dist/handshake/channel.d.ts.map +1 -0
- package/dist/handshake/channel.js +246 -0
- package/dist/handshake/channel.js.map +1 -0
- package/dist/handshake/index.d.ts +213 -0
- package/dist/handshake/index.d.ts.map +1 -0
- package/dist/handshake/index.js +182 -0
- package/dist/handshake/index.js.map +1 -0
- package/dist/handshake/qr.d.ts +100 -0
- package/dist/handshake/qr.d.ts.map +1 -0
- package/dist/handshake/qr.js +103 -0
- package/dist/handshake/qr.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/credential-store.d.ts +99 -0
- package/dist/storage/credential-store.d.ts.map +1 -0
- package/dist/storage/credential-store.js +309 -0
- package/dist/storage/credential-store.js.map +1 -0
- package/dist/storage/local-store.d.ts +56 -0
- package/dist/storage/local-store.d.ts.map +1 -0
- package/dist/storage/local-store.js +411 -0
- package/dist/storage/local-store.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Store — pluggable persistence for key material.
|
|
3
|
+
*
|
|
4
|
+
* Safari ITP wipes localStorage after 7 days of inactivity.
|
|
5
|
+
* That kills the passphrase and device ID. Lost key = lost data.
|
|
6
|
+
*
|
|
7
|
+
* This module provides:
|
|
8
|
+
* - `LocalStorageCredentialStore` — current behaviour, extracted.
|
|
9
|
+
* - `WebAuthnCredentialStore` — OS keychain via navigator.credentials + largeBlob.
|
|
10
|
+
* - `createCredentialStore()` — auto-detects best backend.
|
|
11
|
+
*
|
|
12
|
+
* The engine uses `CredentialStore` for all key material persistence.
|
|
13
|
+
* Apps never touch this directly unless they want a custom backend.
|
|
14
|
+
*/
|
|
15
|
+
// ─── localStorage backend ────────────────────────────────────────────
|
|
16
|
+
export class LocalStorageCredentialStore {
|
|
17
|
+
constructor(dbName) {
|
|
18
|
+
this.dbName = dbName;
|
|
19
|
+
}
|
|
20
|
+
keyKey() { return `interocitor-key:${this.dbName}`; }
|
|
21
|
+
get deviceKey() { return 'interocitor-device-id'; }
|
|
22
|
+
async save(creds) {
|
|
23
|
+
localStorage.setItem(this.keyKey(), creds.passphrase);
|
|
24
|
+
localStorage.setItem(this.deviceKey, creds.deviceId);
|
|
25
|
+
}
|
|
26
|
+
async load() {
|
|
27
|
+
const passphrase = localStorage.getItem(this.keyKey());
|
|
28
|
+
const deviceId = localStorage.getItem(this.deviceKey);
|
|
29
|
+
if (!passphrase || !deviceId)
|
|
30
|
+
return null;
|
|
31
|
+
return { passphrase, deviceId };
|
|
32
|
+
}
|
|
33
|
+
async clear() {
|
|
34
|
+
localStorage.removeItem(this.keyKey());
|
|
35
|
+
// Device ID intentionally kept — shared across meshes, survives credential clear.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ─── WebAuthn + largeBlob backend ────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Stores credentials in the OS keychain via WebAuthn `largeBlob` extension.
|
|
41
|
+
*
|
|
42
|
+
* Survives Safari ITP, browser data clears, and profile resets.
|
|
43
|
+
* Requires a platform authenticator with largeBlob support (Touch ID,
|
|
44
|
+
* Face ID, Windows Hello). Falls back gracefully — `save()` rejects
|
|
45
|
+
* if the authenticator doesn't support largeBlob.
|
|
46
|
+
*
|
|
47
|
+
* Flow:
|
|
48
|
+
* 1. `save()` → `navigator.credentials.create()` with largeBlob write.
|
|
49
|
+
* User sees a biometric prompt. Credential ID stored in localStorage
|
|
50
|
+
* as a hint for faster `load()`, but not required.
|
|
51
|
+
* 2. `load()` → `navigator.credentials.get()` with largeBlob read.
|
|
52
|
+
* User sees a biometric prompt. Returns the blob.
|
|
53
|
+
* 3. If localStorage hint is gone (ITP wipe), load uses an empty
|
|
54
|
+
* allowCredentials list — the authenticator picks the right one.
|
|
55
|
+
*/
|
|
56
|
+
export class WebAuthnCredentialStore {
|
|
57
|
+
constructor(dbName, rpId = globalThis.location?.hostname ?? 'localhost',
|
|
58
|
+
/** Human-readable app name shown in biometric prompts and OS keychain. */
|
|
59
|
+
displayName = 'Interocitor') {
|
|
60
|
+
this.dbName = dbName;
|
|
61
|
+
this.rpId = rpId;
|
|
62
|
+
this.displayName = displayName;
|
|
63
|
+
this.encoder = new TextEncoder();
|
|
64
|
+
this.decoder = new TextDecoder();
|
|
65
|
+
}
|
|
66
|
+
credIdKey() {
|
|
67
|
+
return `${WebAuthnCredentialStore.CRED_ID_KEY_PREFIX}${this.dbName}`;
|
|
68
|
+
}
|
|
69
|
+
encode(creds) {
|
|
70
|
+
return this.encoder.encode(JSON.stringify(creds));
|
|
71
|
+
}
|
|
72
|
+
decode(blob) {
|
|
73
|
+
return JSON.parse(this.decoder.decode(blob));
|
|
74
|
+
}
|
|
75
|
+
/** Read credential ID hint from localStorage (best-effort, survives only if ITP hasn't wiped). */
|
|
76
|
+
loadCredentialIdHint() {
|
|
77
|
+
try {
|
|
78
|
+
const stored = localStorage.getItem(this.credIdKey());
|
|
79
|
+
if (!stored)
|
|
80
|
+
return null;
|
|
81
|
+
const raw = Uint8Array.from(atob(stored), c => c.charCodeAt(0));
|
|
82
|
+
return raw.buffer;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
saveCredentialIdHint(rawId) {
|
|
89
|
+
try {
|
|
90
|
+
const bytes = new Uint8Array(rawId);
|
|
91
|
+
let binary = '';
|
|
92
|
+
for (let i = 0; i < bytes.length; i++)
|
|
93
|
+
binary += String.fromCharCode(bytes[i]);
|
|
94
|
+
const b64 = btoa(binary);
|
|
95
|
+
localStorage.setItem(this.credIdKey(), b64);
|
|
96
|
+
}
|
|
97
|
+
catch { /* best-effort */ }
|
|
98
|
+
}
|
|
99
|
+
async save(creds) {
|
|
100
|
+
const blob = this.encode(creds);
|
|
101
|
+
const challenge = crypto.getRandomValues(new Uint8Array(32));
|
|
102
|
+
const userId = crypto.getRandomValues(new Uint8Array(16));
|
|
103
|
+
const credential = await navigator.credentials.create({
|
|
104
|
+
publicKey: {
|
|
105
|
+
rp: { name: this.displayName, id: this.rpId },
|
|
106
|
+
user: {
|
|
107
|
+
id: userId,
|
|
108
|
+
name: `${this.displayName.toLowerCase().replaceAll(/\s+/g, '-')}:${this.dbName}`,
|
|
109
|
+
displayName: this.displayName,
|
|
110
|
+
},
|
|
111
|
+
challenge,
|
|
112
|
+
pubKeyCredParams: [
|
|
113
|
+
{ type: 'public-key', alg: -7 }, // ES256
|
|
114
|
+
{ type: 'public-key', alg: -257 }, // RS256
|
|
115
|
+
],
|
|
116
|
+
authenticatorSelection: {
|
|
117
|
+
authenticatorAttachment: 'platform',
|
|
118
|
+
residentKey: 'required',
|
|
119
|
+
userVerification: 'required',
|
|
120
|
+
},
|
|
121
|
+
extensions: {
|
|
122
|
+
largeBlob: { support: 'required' },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
if (!credential)
|
|
127
|
+
throw new Error('WebAuthn credential creation cancelled');
|
|
128
|
+
// Write the blob in a separate get() call — largeBlob write requires
|
|
129
|
+
// an assertion, not registration, on most platforms.
|
|
130
|
+
this.saveCredentialIdHint(credential.rawId);
|
|
131
|
+
await this.writeLargeBlob(credential.rawId, blob);
|
|
132
|
+
}
|
|
133
|
+
async writeLargeBlob(credentialId, blob) {
|
|
134
|
+
const challenge = crypto.getRandomValues(new Uint8Array(32));
|
|
135
|
+
const assertion = await navigator.credentials.get({
|
|
136
|
+
publicKey: {
|
|
137
|
+
challenge,
|
|
138
|
+
rpId: this.rpId,
|
|
139
|
+
allowCredentials: [{
|
|
140
|
+
type: 'public-key',
|
|
141
|
+
id: credentialId,
|
|
142
|
+
}],
|
|
143
|
+
userVerification: 'required',
|
|
144
|
+
extensions: {
|
|
145
|
+
largeBlob: { write: blob },
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
if (!assertion)
|
|
150
|
+
throw new Error('WebAuthn assertion cancelled');
|
|
151
|
+
const results = assertion.getClientExtensionResults?.();
|
|
152
|
+
if (!results?.largeBlob?.written) {
|
|
153
|
+
throw new Error('largeBlob write failed — authenticator may not support it');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async load() {
|
|
157
|
+
const challenge = crypto.getRandomValues(new Uint8Array(32));
|
|
158
|
+
const credentialIdHint = this.loadCredentialIdHint();
|
|
159
|
+
const assertion = await navigator.credentials.get({
|
|
160
|
+
publicKey: {
|
|
161
|
+
challenge,
|
|
162
|
+
rpId: this.rpId,
|
|
163
|
+
// If we have the hint, scope to it. Otherwise let the authenticator
|
|
164
|
+
// show all resident credentials for this RP (discoverable flow).
|
|
165
|
+
allowCredentials: credentialIdHint
|
|
166
|
+
? [{ type: 'public-key', id: credentialIdHint }]
|
|
167
|
+
: [],
|
|
168
|
+
userVerification: 'required',
|
|
169
|
+
extensions: {
|
|
170
|
+
largeBlob: { read: true },
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
if (!assertion)
|
|
175
|
+
return null;
|
|
176
|
+
const results = assertion.getClientExtensionResults?.();
|
|
177
|
+
const blob = results?.largeBlob?.blob;
|
|
178
|
+
if (!blob)
|
|
179
|
+
return null;
|
|
180
|
+
// Got credentials back — re-save the hint in case localStorage was wiped.
|
|
181
|
+
this.saveCredentialIdHint(assertion.rawId);
|
|
182
|
+
return this.decode(blob);
|
|
183
|
+
}
|
|
184
|
+
async clear() {
|
|
185
|
+
// Can't programmatically delete WebAuthn credentials.
|
|
186
|
+
// Remove the localStorage hint; the credential stays in the keychain
|
|
187
|
+
// but won't be found without the hint (or user manually picks it).
|
|
188
|
+
try {
|
|
189
|
+
localStorage.removeItem(this.credIdKey());
|
|
190
|
+
}
|
|
191
|
+
catch { /* ok */ }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
WebAuthnCredentialStore.CRED_ID_KEY_PREFIX = 'interocitor-cred:';
|
|
195
|
+
// ─── Auto-detection ──────────────────────────────────────────────────
|
|
196
|
+
/** Check if WebAuthn with largeBlob is likely available. */
|
|
197
|
+
async function isWebAuthnLargeBlobAvailable() {
|
|
198
|
+
if (globalThis.PublicKeyCredential === undefined)
|
|
199
|
+
return false;
|
|
200
|
+
try {
|
|
201
|
+
// Check platform authenticator availability
|
|
202
|
+
const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
|
203
|
+
if (!available)
|
|
204
|
+
return false;
|
|
205
|
+
// No standard way to check largeBlob support without creating a credential.
|
|
206
|
+
// We rely on the 'required' support flag during create() to fail fast.
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Create the best available credential store.
|
|
215
|
+
*
|
|
216
|
+
* Prefers WebAuthn (survives ITP) when available, falls back to
|
|
217
|
+
* localStorage. Returns a `FallbackCredentialStore` that tries
|
|
218
|
+
* WebAuthn first and uses localStorage as backup on every operation.
|
|
219
|
+
*/
|
|
220
|
+
export function createCredentialStore(dbName, displayName) {
|
|
221
|
+
return new FallbackCredentialStore(dbName, displayName);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Tries WebAuthn on save/load; falls back to localStorage on failure.
|
|
225
|
+
*
|
|
226
|
+
* Save: always writes localStorage (silent). WebAuthn only written via
|
|
227
|
+
* explicit `secureWithBiometrics()` call — never automatically on first
|
|
228
|
+
* key generation. This avoids a confusing biometric prompt on first open.
|
|
229
|
+
*
|
|
230
|
+
* Load: tries localStorage first (fast). If empty (ITP wipe?), tries
|
|
231
|
+
* WebAuthn recovery (biometric prompt — user understands why at this point).
|
|
232
|
+
*/
|
|
233
|
+
class FallbackCredentialStore {
|
|
234
|
+
constructor(dbName, displayName) {
|
|
235
|
+
this.dbName = dbName;
|
|
236
|
+
this.displayName = displayName;
|
|
237
|
+
this.webauthn = null;
|
|
238
|
+
this.webauthnChecked = false;
|
|
239
|
+
this.ls = new LocalStorageCredentialStore(dbName);
|
|
240
|
+
}
|
|
241
|
+
async getWebAuthn() {
|
|
242
|
+
if (this.webauthnChecked)
|
|
243
|
+
return this.webauthn;
|
|
244
|
+
this.webauthnChecked = true;
|
|
245
|
+
if (await isWebAuthnLargeBlobAvailable()) {
|
|
246
|
+
this.webauthn = new WebAuthnCredentialStore(this.dbName, undefined, this.displayName);
|
|
247
|
+
}
|
|
248
|
+
return this.webauthn;
|
|
249
|
+
}
|
|
250
|
+
async save(creds) {
|
|
251
|
+
// Save to localStorage only (silent, no prompt).
|
|
252
|
+
// WebAuthn enrollment happens via secureWithBiometrics().
|
|
253
|
+
await this.ls.save(creds);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Persist current credentials to the OS keychain via biometrics.
|
|
257
|
+
* Call this when the user takes an intentional action — after pairing,
|
|
258
|
+
* after a "Secure my keys" button press, etc. Not on first open.
|
|
259
|
+
*
|
|
260
|
+
* Returns true if saved, false if WebAuthn unavailable or user cancelled.
|
|
261
|
+
*/
|
|
262
|
+
async secureWithBiometrics() {
|
|
263
|
+
const creds = await this.ls.load();
|
|
264
|
+
if (!creds)
|
|
265
|
+
return false;
|
|
266
|
+
const wa = await this.getWebAuthn();
|
|
267
|
+
if (!wa)
|
|
268
|
+
return false;
|
|
269
|
+
try {
|
|
270
|
+
await wa.save(creds);
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async load() {
|
|
278
|
+
// Silent primary path only. No biometric prompt here.
|
|
279
|
+
return this.ls.load();
|
|
280
|
+
}
|
|
281
|
+
async restoreWithBiometrics() {
|
|
282
|
+
const wa = await this.getWebAuthn();
|
|
283
|
+
if (!wa)
|
|
284
|
+
return null;
|
|
285
|
+
try {
|
|
286
|
+
const restored = await wa.load();
|
|
287
|
+
if (restored) {
|
|
288
|
+
// Re-populate localStorage for normal silent opens after restore.
|
|
289
|
+
await this.ls.save(restored);
|
|
290
|
+
return restored;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// unavailable / cancelled / not found
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
async clear() {
|
|
299
|
+
await this.ls.clear();
|
|
300
|
+
const wa = await this.getWebAuthn();
|
|
301
|
+
if (wa) {
|
|
302
|
+
try {
|
|
303
|
+
await wa.clear();
|
|
304
|
+
}
|
|
305
|
+
catch { /* best-effort */ }
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=credential-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/storage/credential-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local storage layer — IndexedDB
|
|
3
|
+
*
|
|
4
|
+
* This is a cache, not the source of truth.
|
|
5
|
+
* If cleared, the app rehydrates from cloud.
|
|
6
|
+
*
|
|
7
|
+
* Stores:
|
|
8
|
+
* - rows: the current merged state of all tables
|
|
9
|
+
* - outbox: change entries pending upload
|
|
10
|
+
* - cursors: byte offsets into each device's change log
|
|
11
|
+
* - meta: device ID, last snapshot epoch, etc.
|
|
12
|
+
*/
|
|
13
|
+
import type { Row, ChangeEntry, LocalStoreAdapter, DatabaseSchemaDefinition, WhereClause } from '../core/types.ts';
|
|
14
|
+
/**
|
|
15
|
+
* Default IndexedDB-backed local persistence layer used by {@link SyncEngine}.
|
|
16
|
+
*
|
|
17
|
+
* Most applications do not need to interact with this class directly unless
|
|
18
|
+
* they are supplying a custom `localStoreFactory` or swapping local storage at
|
|
19
|
+
* runtime for testing or advanced integrations.
|
|
20
|
+
*/
|
|
21
|
+
export declare class LocalStore implements LocalStoreAdapter {
|
|
22
|
+
private db;
|
|
23
|
+
private readonly dbName;
|
|
24
|
+
private readonly dbVersion;
|
|
25
|
+
private readonly schema?;
|
|
26
|
+
/**
|
|
27
|
+
* @param dbName IndexedDB database name. Use distinct names to isolate
|
|
28
|
+
* multiple engine instances on the same origin.
|
|
29
|
+
* Default: "interocitor"
|
|
30
|
+
* @param dbVersion IndexedDB schema version. Default: 1
|
|
31
|
+
*/
|
|
32
|
+
constructor(dbName?: string, dbVersion?: number, schema?: DatabaseSchemaDefinition);
|
|
33
|
+
open(): Promise<void>;
|
|
34
|
+
close(): void;
|
|
35
|
+
private ensureDB;
|
|
36
|
+
private rowKey;
|
|
37
|
+
getRow(table: string, rowId: string): Promise<Row | undefined>;
|
|
38
|
+
putRow(row: Row): Promise<void>;
|
|
39
|
+
putRows(rows: Row[]): Promise<void>;
|
|
40
|
+
getTable(table: string): Promise<Row[]>;
|
|
41
|
+
queryWhere(table: string, clause: WhereClause): Promise<Row[]>;
|
|
42
|
+
getTableNames(): Promise<string[]>;
|
|
43
|
+
getAllRows(): Promise<Row[]>;
|
|
44
|
+
clearRows(): Promise<void>;
|
|
45
|
+
pushOutbox(entry: ChangeEntry): Promise<void>;
|
|
46
|
+
drainOutbox(): Promise<ChangeEntry[]>;
|
|
47
|
+
outboxSize(): Promise<number>;
|
|
48
|
+
getCursor(deviceId: string): Promise<number>;
|
|
49
|
+
setCursor(deviceId: string, offset: number): Promise<void>;
|
|
50
|
+
getAllCursors(): Promise<Record<string, number>>;
|
|
51
|
+
getMeta(key: string): Promise<unknown>;
|
|
52
|
+
setMeta(key: string, value: unknown): Promise<void>;
|
|
53
|
+
/** Nuke everything. Used before full rehydration. */
|
|
54
|
+
clearAll(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=local-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["../../src/storage/local-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,GAAG,EACH,WAAW,EACX,iBAAiB,EACjB,wBAAwB,EAGxB,WAAW,EAEZ,MAAM,kBAAkB,CAAC;AA0O1B;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,iBAAiB;IAClD,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA2B;IAEnD;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,wBAAwB;IAO5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,MAAM;IAIR,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAQ9D,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/B,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IASvC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAgC9D,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAelC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAO5B,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAS1B,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7C,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAUrC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ7B,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO5C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1D,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAehD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMtC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOzD,qDAAqD;IAC/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAQhC"}
|