@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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +178 -0
  3. package/dist/adapters/cloudflare.d.ts +72 -0
  4. package/dist/adapters/cloudflare.d.ts.map +1 -0
  5. package/dist/adapters/cloudflare.js +227 -0
  6. package/dist/adapters/cloudflare.js.map +1 -0
  7. package/dist/adapters/google-drive.d.ts +64 -0
  8. package/dist/adapters/google-drive.d.ts.map +1 -0
  9. package/dist/adapters/google-drive.js +340 -0
  10. package/dist/adapters/google-drive.js.map +1 -0
  11. package/dist/adapters/memory.d.ts +45 -0
  12. package/dist/adapters/memory.d.ts.map +1 -0
  13. package/dist/adapters/memory.js +129 -0
  14. package/dist/adapters/memory.js.map +1 -0
  15. package/dist/adapters/webdav.d.ts +59 -0
  16. package/dist/adapters/webdav.d.ts.map +1 -0
  17. package/dist/adapters/webdav.js +247 -0
  18. package/dist/adapters/webdav.js.map +1 -0
  19. package/dist/core/codec.d.ts +20 -0
  20. package/dist/core/codec.d.ts.map +1 -0
  21. package/dist/core/codec.js +66 -0
  22. package/dist/core/codec.js.map +1 -0
  23. package/dist/core/compaction.d.ts +37 -0
  24. package/dist/core/compaction.d.ts.map +1 -0
  25. package/dist/core/compaction.js +134 -0
  26. package/dist/core/compaction.js.map +1 -0
  27. package/dist/core/crdt.d.ts +33 -0
  28. package/dist/core/crdt.d.ts.map +1 -0
  29. package/dist/core/crdt.js +188 -0
  30. package/dist/core/crdt.js.map +1 -0
  31. package/dist/core/flush.d.ts +9 -0
  32. package/dist/core/flush.d.ts.map +1 -0
  33. package/dist/core/flush.js +41 -0
  34. package/dist/core/flush.js.map +1 -0
  35. package/dist/core/hlc.d.ts +25 -0
  36. package/dist/core/hlc.d.ts.map +1 -0
  37. package/dist/core/hlc.js +76 -0
  38. package/dist/core/hlc.js.map +1 -0
  39. package/dist/core/internals.d.ts +25 -0
  40. package/dist/core/internals.d.ts.map +1 -0
  41. package/dist/core/internals.js +54 -0
  42. package/dist/core/internals.js.map +1 -0
  43. package/dist/core/manifest.d.ts +31 -0
  44. package/dist/core/manifest.d.ts.map +1 -0
  45. package/dist/core/manifest.js +111 -0
  46. package/dist/core/manifest.js.map +1 -0
  47. package/dist/core/pull.d.ts +26 -0
  48. package/dist/core/pull.d.ts.map +1 -0
  49. package/dist/core/pull.js +98 -0
  50. package/dist/core/pull.js.map +1 -0
  51. package/dist/core/row-id.d.ts +12 -0
  52. package/dist/core/row-id.d.ts.map +1 -0
  53. package/dist/core/row-id.js +12 -0
  54. package/dist/core/row-id.js.map +1 -0
  55. package/dist/core/schema-types.d.ts +13 -0
  56. package/dist/core/schema-types.d.ts.map +1 -0
  57. package/dist/core/schema-types.js +18 -0
  58. package/dist/core/schema-types.js.map +1 -0
  59. package/dist/core/schema-types.type-test.d.ts +2 -0
  60. package/dist/core/schema-types.type-test.d.ts.map +1 -0
  61. package/dist/core/schema-types.type-test.js +149 -0
  62. package/dist/core/schema-types.type-test.js.map +1 -0
  63. package/dist/core/sync-engine.d.ts +158 -0
  64. package/dist/core/sync-engine.d.ts.map +1 -0
  65. package/dist/core/sync-engine.js +714 -0
  66. package/dist/core/sync-engine.js.map +1 -0
  67. package/dist/core/table.d.ts +60 -0
  68. package/dist/core/table.d.ts.map +1 -0
  69. package/dist/core/table.js +106 -0
  70. package/dist/core/table.js.map +1 -0
  71. package/dist/core/types.d.ts +478 -0
  72. package/dist/core/types.d.ts.map +1 -0
  73. package/dist/core/types.js +7 -0
  74. package/dist/core/types.js.map +1 -0
  75. package/dist/crypto/encryption.d.ts +57 -0
  76. package/dist/crypto/encryption.d.ts.map +1 -0
  77. package/dist/crypto/encryption.js +195 -0
  78. package/dist/crypto/encryption.js.map +1 -0
  79. package/dist/crypto/keys.d.ts +48 -0
  80. package/dist/crypto/keys.d.ts.map +1 -0
  81. package/dist/crypto/keys.js +55 -0
  82. package/dist/crypto/keys.js.map +1 -0
  83. package/dist/handshake/channel.d.ts +117 -0
  84. package/dist/handshake/channel.d.ts.map +1 -0
  85. package/dist/handshake/channel.js +246 -0
  86. package/dist/handshake/channel.js.map +1 -0
  87. package/dist/handshake/index.d.ts +213 -0
  88. package/dist/handshake/index.d.ts.map +1 -0
  89. package/dist/handshake/index.js +182 -0
  90. package/dist/handshake/index.js.map +1 -0
  91. package/dist/handshake/qr.d.ts +100 -0
  92. package/dist/handshake/qr.d.ts.map +1 -0
  93. package/dist/handshake/qr.js +103 -0
  94. package/dist/handshake/qr.js.map +1 -0
  95. package/dist/index.d.ts +46 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +46 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/storage/credential-store.d.ts +99 -0
  100. package/dist/storage/credential-store.d.ts.map +1 -0
  101. package/dist/storage/credential-store.js +309 -0
  102. package/dist/storage/credential-store.js.map +1 -0
  103. package/dist/storage/local-store.d.ts +56 -0
  104. package/dist/storage/local-store.d.ts.map +1 -0
  105. package/dist/storage/local-store.js +411 -0
  106. package/dist/storage/local-store.js.map +1 -0
  107. package/package.json +68 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * interocitor/handshake/qr
3
+ *
4
+ * QR code payload encoding and decoding for the handshake protocol.
5
+ *
6
+ * ## Two intents
7
+ *
8
+ * A QR code is always generated by one device and scanned by another.
9
+ * The intent declares what the QR *generator* wants:
10
+ *
11
+ * "share" — I already belong to a mesh. Scan me: I will push
12
+ * remotePath + master key to you via the relay.
13
+ *
14
+ * "join" — I want to join a mesh. Scan me: push your mesh
15
+ * credentials to me via the relay.
16
+ *
17
+ * In both cases the *scanner* is the one that ends up sending data
18
+ * through the cloud relay; the QR generator always receives.
19
+ *
20
+ * ## Two pieces in the QR
21
+ *
22
+ * Cloud piece → handshakeId
23
+ * Scopes two short-lived relay files on the shared
24
+ * backend. Anyone with cloud access can see these
25
+ * files — but cannot decrypt them (see below).
26
+ *
27
+ * Eyes-only → generatorPub (ephemeral ECDH-P256 public key)
28
+ * Used by the scanner to derive a wrapping key via
29
+ * ECDH. The corresponding private key never leaves
30
+ * the generating device. Without physically seeing
31
+ * the QR you cannot derive the wrapping key, so
32
+ * the relay payload is opaque even to someone with
33
+ * full cloud-folder access.
34
+ *
35
+ * ## What is NOT in the QR
36
+ *
37
+ * remotePath — travels via the relay only, encrypted with the
38
+ * ECDH-derived wrapping key.
39
+ * master key — same.
40
+ *
41
+ * ## Encoding
42
+ *
43
+ * The payload is a compact base64url JSON string, safe to embed in a
44
+ * URL fragment (never reaches any server):
45
+ *
46
+ * https://yourapp.com/pair#hs=<base64url>
47
+ */
48
+ export type HandshakeIntent = 'share' | 'join';
49
+ export interface HandshakeQRPayload {
50
+ /**
51
+ * Intent of the QR *generator*:
52
+ * "share" → generator has the mesh credentials and will push them.
53
+ * "join" → generator wants credentials pushed to it.
54
+ */
55
+ intent: HandshakeIntent;
56
+ /**
57
+ * Random hex string scoping the relay files on the shared backend.
58
+ * This is the "cloud piece" — visible to anyone with backend access,
59
+ * but useless without the eyes-only ECDH private key.
60
+ */
61
+ handshakeId: string;
62
+ /**
63
+ * Generator's ephemeral ECDH-P256 public key, base64url-encoded SPKI.
64
+ * This is the "eyes-only piece" — must be physically scanned to be useful.
65
+ * The scanner uses it to derive the shared wrapping key.
66
+ */
67
+ generatorPub: string;
68
+ /**
69
+ * Opaque backend connection config produced by adapter.getHandshakeConfig().
70
+ *
71
+ * Tells the scanner how to reach the same backend as the generator so
72
+ * they can exchange relay files. Must NOT contain credentials.
73
+ *
74
+ * Present for adapters where the backend address is not known to the app
75
+ * in advance (WebDAV, Google Drive, sharded CF workers, …).
76
+ * Absent when the backend is fixed and app-global (e.g. a single CF worker
77
+ * whose URL is baked into the binary).
78
+ *
79
+ * The scanner passes this to their adapter via the adapter's own
80
+ * fromHandshakeConfig() static factory or equivalent before starting
81
+ * the relay exchange.
82
+ */
83
+ adapterConfig?: string;
84
+ }
85
+ /** Encode a HandshakeQRPayload to a compact URL-safe base64 string. */
86
+ export declare function encodeQRPayload(payload: HandshakeQRPayload): string;
87
+ /** Decode a compact URL-safe base64 string back to HandshakeQRPayload. */
88
+ export declare function decodeQRPayload(encoded: string): HandshakeQRPayload;
89
+ /**
90
+ * Build a full join URL with the handshake payload in the fragment.
91
+ * The fragment is never sent to any server.
92
+ */
93
+ export declare function buildPairUrl(baseUrl: string, payload: HandshakeQRPayload): string;
94
+ /**
95
+ * Extract and decode a HandshakeQRPayload from a URL fragment string.
96
+ * Pass `window.location.hash` directly.
97
+ * Returns null if no #hs= fragment is present.
98
+ */
99
+ export declare function parseQRFromUrl(hash: string): HandshakeQRPayload | null;
100
+ //# sourceMappingURL=qr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/handshake/qr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,eAAe,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAQnE;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAuBnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAQtE"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * interocitor/handshake/qr
3
+ *
4
+ * QR code payload encoding and decoding for the handshake protocol.
5
+ *
6
+ * ## Two intents
7
+ *
8
+ * A QR code is always generated by one device and scanned by another.
9
+ * The intent declares what the QR *generator* wants:
10
+ *
11
+ * "share" — I already belong to a mesh. Scan me: I will push
12
+ * remotePath + master key to you via the relay.
13
+ *
14
+ * "join" — I want to join a mesh. Scan me: push your mesh
15
+ * credentials to me via the relay.
16
+ *
17
+ * In both cases the *scanner* is the one that ends up sending data
18
+ * through the cloud relay; the QR generator always receives.
19
+ *
20
+ * ## Two pieces in the QR
21
+ *
22
+ * Cloud piece → handshakeId
23
+ * Scopes two short-lived relay files on the shared
24
+ * backend. Anyone with cloud access can see these
25
+ * files — but cannot decrypt them (see below).
26
+ *
27
+ * Eyes-only → generatorPub (ephemeral ECDH-P256 public key)
28
+ * Used by the scanner to derive a wrapping key via
29
+ * ECDH. The corresponding private key never leaves
30
+ * the generating device. Without physically seeing
31
+ * the QR you cannot derive the wrapping key, so
32
+ * the relay payload is opaque even to someone with
33
+ * full cloud-folder access.
34
+ *
35
+ * ## What is NOT in the QR
36
+ *
37
+ * remotePath — travels via the relay only, encrypted with the
38
+ * ECDH-derived wrapping key.
39
+ * master key — same.
40
+ *
41
+ * ## Encoding
42
+ *
43
+ * The payload is a compact base64url JSON string, safe to embed in a
44
+ * URL fragment (never reaches any server):
45
+ *
46
+ * https://yourapp.com/pair#hs=<base64url>
47
+ */
48
+ /** Encode a HandshakeQRPayload to a compact URL-safe base64 string. */
49
+ export function encodeQRPayload(payload) {
50
+ const json = JSON.stringify(payload);
51
+ const bytes = new TextEncoder().encode(json);
52
+ let binary = '';
53
+ for (let i = 0; i < bytes.length; i++) {
54
+ binary += String.fromCodePoint(bytes[i]);
55
+ }
56
+ return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
57
+ }
58
+ /** Decode a compact URL-safe base64 string back to HandshakeQRPayload. */
59
+ export function decodeQRPayload(encoded) {
60
+ const padded = encoded.replaceAll('-', '+').replaceAll('_', '/');
61
+ const pad = (4 - (padded.length % 4)) % 4;
62
+ const b64 = padded + '='.repeat(pad);
63
+ const binary = atob(b64);
64
+ const bytes = new Uint8Array(binary.length);
65
+ for (let i = 0; i < binary.length; i++) {
66
+ bytes[i] = binary.charCodeAt(i);
67
+ }
68
+ const json = new TextDecoder().decode(bytes);
69
+ const payload = JSON.parse(json);
70
+ if ((payload.intent !== 'share' && payload.intent !== 'join') ||
71
+ typeof payload.handshakeId !== 'string' ||
72
+ typeof payload.generatorPub !== 'string' ||
73
+ (payload.adapterConfig !== undefined && typeof payload.adapterConfig !== 'string')) {
74
+ throw new Error('Invalid handshake QR payload');
75
+ }
76
+ return payload;
77
+ }
78
+ /**
79
+ * Build a full join URL with the handshake payload in the fragment.
80
+ * The fragment is never sent to any server.
81
+ */
82
+ export function buildPairUrl(baseUrl, payload) {
83
+ const encoded = encodeQRPayload(payload);
84
+ const base = baseUrl.replace(/#.*$/, '');
85
+ return `${base}#hs=${encoded}`;
86
+ }
87
+ /**
88
+ * Extract and decode a HandshakeQRPayload from a URL fragment string.
89
+ * Pass `window.location.hash` directly.
90
+ * Returns null if no #hs= fragment is present.
91
+ */
92
+ export function parseQRFromUrl(hash) {
93
+ const match = hash.match(/(?:^|[#&])hs=([A-Za-z0-9_-]+)/);
94
+ if (!match)
95
+ return null;
96
+ try {
97
+ return decodeQRPayload(match[1]);
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ //# sourceMappingURL=qr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.js","sourceRoot":"","sources":["../../src/handshake/qr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * interocitor
3
+ *
4
+ * Encrypted local-first CRDT database that syncs over cloud storage.
5
+ * Google Drive is the default path; server-managed compaction is optional.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { SyncEngine } from 'interocitor';
10
+ * import { GoogleDriveAdapter } from 'interocitor/adapters/google-drive';
11
+ *
12
+ * const adapter = new GoogleDriveAdapter({
13
+ * clientId: 'YOUR_GOOGLE_CLIENT_ID',
14
+ * });
15
+ * const engine = new SyncEngine(adapter, {
16
+ * remotePath: '/Interocitor',
17
+ * encrypted: true, // generates key automatically
18
+ * });
19
+ *
20
+ * await engine.init();
21
+ * await engine.connect();
22
+ *
23
+ * // Share this passphrase with other devices:
24
+ * console.log('Passphrase:', engine.getPassphrase());
25
+ *
26
+ * await engine.put('meals', 'meal_1', { name: 'Butter Chicken', servings: 4 });
27
+ *
28
+ * engine.on((event) => {
29
+ * if (event.type === 'change') {
30
+ * console.log(`${event.table}/${event.rowId} updated`);
31
+ * }
32
+ * });
33
+ * ```
34
+ */
35
+ export { generateShareQR, generateJoinQR, handleScannedQR, buildPairUrl, parseQRFromUrl, } from './handshake/index.ts';
36
+ export type { HandshakeCredentials, GenerateShareQROptions, GenerateShareQRResult, GenerateJoinQROptions, GenerateJoinQRResult, HandleScannedQROptions, } from './handshake/index.ts';
37
+ export { SyncEngine } from './core/sync-engine.ts';
38
+ export { LocalStore } from './storage/local-store.ts';
39
+ export { type CredentialStore, type StoredCredentials, LocalStorageCredentialStore, WebAuthnCredentialStore, createCredentialStore, } from './storage/credential-store.ts';
40
+ export { Table } from './core/table.ts';
41
+ export { types } from './core/schema-types.ts';
42
+ export { readColumn, rowToPlain } from './core/crdt.ts';
43
+ export { createRowId } from './core/row-id.ts';
44
+ export type { CreateRowIdOptions } from './core/row-id.ts';
45
+ export type { StorageAdapter, FileEntry, LocalStoreAdapter, LocalStoreFactory, SyncConfig, DatabaseSchemaDefinition, TableSchemaDefinition, InferSchemaType, InferTableType, TableIndexDefinition, SchemaFieldKind, IndexableSchemaFieldKind, SchemaField, IndexableSchemaField, BuiltinMergeStrategy, MergeStrategy, MergeFunction, MergeContext, TableMergeConfig, WhereClause, WherePrimitive, WhereOperator, SyncEvent, SyncEventListener, Row, Manifest, ManifestPointer, ServerConfig, MeshChangePayload, MeshSnapshotPayload, DeviceInfo, DeviceMetadata, DeviceHead, ChangesHead, ReplicaConfig, } from './core/types.ts';
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EACL,eAAe,EACf,cAAc,EACd,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAI/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAI3D,YAAY,EAEV,cAAc,EACd,SAAS,EAGT,iBAAiB,EACjB,iBAAiB,EAGjB,UAAU,EACV,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,wBAAwB,EACxB,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,aAAa,EAGb,SAAS,EACT,iBAAiB,EAGjB,GAAG,EAGH,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,GACd,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * interocitor
3
+ *
4
+ * Encrypted local-first CRDT database that syncs over cloud storage.
5
+ * Google Drive is the default path; server-managed compaction is optional.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { SyncEngine } from 'interocitor';
10
+ * import { GoogleDriveAdapter } from 'interocitor/adapters/google-drive';
11
+ *
12
+ * const adapter = new GoogleDriveAdapter({
13
+ * clientId: 'YOUR_GOOGLE_CLIENT_ID',
14
+ * });
15
+ * const engine = new SyncEngine(adapter, {
16
+ * remotePath: '/Interocitor',
17
+ * encrypted: true, // generates key automatically
18
+ * });
19
+ *
20
+ * await engine.init();
21
+ * await engine.connect();
22
+ *
23
+ * // Share this passphrase with other devices:
24
+ * console.log('Passphrase:', engine.getPassphrase());
25
+ *
26
+ * await engine.put('meals', 'meal_1', { name: 'Butter Chicken', servings: 4 });
27
+ *
28
+ * engine.on((event) => {
29
+ * if (event.type === 'change') {
30
+ * console.log(`${event.table}/${event.rowId} updated`);
31
+ * }
32
+ * });
33
+ * ```
34
+ */
35
+ // ─── Handshake (public API) ───────────────────────────────────────────
36
+ export { generateShareQR, generateJoinQR, handleScannedQR, buildPairUrl, parseQRFromUrl, } from "./handshake/index.js";
37
+ // ─── Engine ───────────────────────────────────────────────────────────
38
+ export { SyncEngine } from "./core/sync-engine.js";
39
+ export { LocalStore } from "./storage/local-store.js";
40
+ export { LocalStorageCredentialStore, WebAuthnCredentialStore, createCredentialStore, } from "./storage/credential-store.js";
41
+ export { Table } from "./core/table.js";
42
+ export { types } from "./core/schema-types.js";
43
+ // ─── Row utilities ────────────────────────────────────────────────────
44
+ export { readColumn, rowToPlain } from "./core/crdt.js";
45
+ export { createRowId } from "./core/row-id.js";
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,yEAAyE;AAEzE,OAAO,EACL,eAAe,EACf,cAAc,EACd,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,sBAAsB,CAAC;AAW9B,yEAAyE;AAEzE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAGL,2BAA2B,EAC3B,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAE/C,yEAAyE;AAEzE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,99 @@
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
+ export interface StoredCredentials {
16
+ /** Base58 passphrase for the mesh encryption key. */
17
+ passphrase: string;
18
+ /** Stable device identifier. */
19
+ deviceId: string;
20
+ }
21
+ export interface CredentialStore {
22
+ /** Persist credentials to the primary store (localStorage). Silent. */
23
+ save(creds: StoredCredentials): Promise<void>;
24
+ /** Load persisted credentials from the primary store only. Silent. */
25
+ load(): Promise<StoredCredentials | null>;
26
+ /** Wipe stored credentials. */
27
+ clear(): Promise<void>;
28
+ /**
29
+ * Persist credentials to the OS keychain via biometrics (WebAuthn).
30
+ * Call on intentional user action (pairing, "secure my keys" button).
31
+ * Returns true if saved, false if unavailable or cancelled.
32
+ * Optional — stores that don't support biometrics return false.
33
+ */
34
+ secureWithBiometrics?(): Promise<boolean>;
35
+ /**
36
+ * Restore credentials from the OS keychain via biometrics (WebAuthn).
37
+ * Call on intentional user action only: "try restore purchases",
38
+ * "restore access", etc. Never automatic.
39
+ * Returns restored credentials or null if unavailable/cancelled/not found.
40
+ */
41
+ restoreWithBiometrics?(): Promise<StoredCredentials | null>;
42
+ }
43
+ export declare class LocalStorageCredentialStore implements CredentialStore {
44
+ private readonly dbName;
45
+ constructor(dbName: string);
46
+ private keyKey;
47
+ private get deviceKey();
48
+ save(creds: StoredCredentials): Promise<void>;
49
+ load(): Promise<StoredCredentials | null>;
50
+ clear(): Promise<void>;
51
+ }
52
+ /**
53
+ * Stores credentials in the OS keychain via WebAuthn `largeBlob` extension.
54
+ *
55
+ * Survives Safari ITP, browser data clears, and profile resets.
56
+ * Requires a platform authenticator with largeBlob support (Touch ID,
57
+ * Face ID, Windows Hello). Falls back gracefully — `save()` rejects
58
+ * if the authenticator doesn't support largeBlob.
59
+ *
60
+ * Flow:
61
+ * 1. `save()` → `navigator.credentials.create()` with largeBlob write.
62
+ * User sees a biometric prompt. Credential ID stored in localStorage
63
+ * as a hint for faster `load()`, but not required.
64
+ * 2. `load()` → `navigator.credentials.get()` with largeBlob read.
65
+ * User sees a biometric prompt. Returns the blob.
66
+ * 3. If localStorage hint is gone (ITP wipe), load uses an empty
67
+ * allowCredentials list — the authenticator picks the right one.
68
+ */
69
+ export declare class WebAuthnCredentialStore implements CredentialStore {
70
+ private readonly dbName;
71
+ private readonly rpId;
72
+ /** Human-readable app name shown in biometric prompts and OS keychain. */
73
+ private readonly displayName;
74
+ private static readonly CRED_ID_KEY_PREFIX;
75
+ private readonly encoder;
76
+ private readonly decoder;
77
+ constructor(dbName: string, rpId?: string,
78
+ /** Human-readable app name shown in biometric prompts and OS keychain. */
79
+ displayName?: string);
80
+ private credIdKey;
81
+ private encode;
82
+ private decode;
83
+ /** Read credential ID hint from localStorage (best-effort, survives only if ITP hasn't wiped). */
84
+ private loadCredentialIdHint;
85
+ private saveCredentialIdHint;
86
+ save(creds: StoredCredentials): Promise<void>;
87
+ private writeLargeBlob;
88
+ load(): Promise<StoredCredentials | null>;
89
+ clear(): Promise<void>;
90
+ }
91
+ /**
92
+ * Create the best available credential store.
93
+ *
94
+ * Prefers WebAuthn (survives ITP) when available, falls back to
95
+ * localStorage. Returns a `FallbackCredentialStore` that tries
96
+ * WebAuthn first and uses localStorage as backup on every operation.
97
+ */
98
+ export declare function createCredentialStore(dbName: string, displayName?: string): CredentialStore;
99
+ //# sourceMappingURL=credential-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../../src/storage/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sEAAsE;IACtE,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC1C,+BAA+B;IAC/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,qBAAqB,CAAC,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;CAC7D;AAID,qBAAa,2BAA4B,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C,OAAO,CAAC,MAAM;IACd,OAAO,KAAK,SAAS,GAA8C;IAE7D,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAOzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAM3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW;IAR9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAuB;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAG1B,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAqD;IAC5E,0EAA0E;IACzD,WAAW,GAAE,MAAsB;IAGtD,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;IAId,kGAAkG;IAClG,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,oBAAoB;IAUtB,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;YAsCrC,cAAc;IA0BtB,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAgCzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B;AAmBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,CAE3F"}