@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The UI Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ <p align="center">
2
+ <a href="https://github.com/TheUiTeam/interocitor">
3
+ <img src="https://raw.githubusercontent.com/TheUiTeam/interocitor/main/docs/assets/hero.svg" alt="interocitor" width="560"/>
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>The JavaScript mailbox that can't read your mail.</strong>
9
+ </p>
10
+
11
+ # interocitor
12
+
13
+ Encrypted local-first CRDT sync for browser apps.
14
+
15
+ ## Why
16
+
17
+ Interocitor is your app's __personal keychain__.
18
+ Your devices hold the key. The cloud is only a mailbox. It carries encrypted sync artifacts and cannot read your mail.
19
+
20
+ What this means:
21
+ - encryption on by default
22
+ - reads and writes are local-first
23
+ - sync uses a remote mailbox, not a trusted database
24
+ - restore is explicit app UI, not automatic magic
25
+
26
+ ## Quick start
27
+
28
+ ```ts
29
+ import { SyncEngine, createRowId } from 'interocitor';
30
+ import { WebDAVAdapter } from 'interocitor/adapters/webdav';
31
+
32
+ const adapter = new WebDAVAdapter({
33
+ baseUrl: 'https://your-webdav-server.example.com',
34
+ auth: { username: 'user', password: 'pass' },
35
+ });
36
+
37
+ const engine = new SyncEngine(adapter, {
38
+ remotePath: '/MyApp',
39
+ dbName: 'my-app',
40
+ appName: 'My App',
41
+ // encrypted by default; set encrypted: false to opt out
42
+ });
43
+
44
+ await engine.init();
45
+ await engine.connect();
46
+
47
+ const id = createRowId({ prefix: 'todo' });
48
+ await engine.put('todos', id, {
49
+ text: 'Ship privacy-first sync',
50
+ done: false,
51
+ });
52
+ ```
53
+
54
+ ## How sync works
55
+
56
+ ```mermaid
57
+ flowchart LR
58
+ A[Browser app] --> B[Interocitor engine]
59
+ B --> C[Local store]
60
+ B --> D[Encrypt changes locally]
61
+ D --> E[Adapter]
62
+ E --> F[Remote mailbox\n(ciphertext only)]
63
+ F --> E
64
+ E --> G[Download ciphertext]
65
+ G --> H[Decrypt locally]
66
+ H --> B
67
+ ```
68
+
69
+ ## Core API at a glance
70
+
71
+ ```ts
72
+ await engine.init();
73
+ await engine.connect();
74
+ await engine.put(table, id, data);
75
+ await engine.get(table, id);
76
+ await engine.query(table);
77
+ await engine.sync();
78
+
79
+ await engine.secureWithBiometrics(); // optional, explicit keychain enrollment
80
+ await engine.restoreWithBiometrics(); // explicit recovery flow
81
+ ```
82
+
83
+ ## Row IDs
84
+
85
+ Use stable string IDs for synced rows. Do not use auto-increment IDs.
86
+
87
+ Good default:
88
+
89
+ ```ts
90
+ import { createRowId } from 'interocitor';
91
+
92
+ const id = createRowId({ prefix: 'task' });
93
+ ```
94
+
95
+ `createRowId()` uses platform crypto. No extra dependency needed.
96
+
97
+ ## Offline guarantee
98
+
99
+ Every read and write hits the local store. No network required.
100
+ `sync()` is the only call that touches the remote mailbox.
101
+
102
+ | Operation | Network? |
103
+ | --- | --- |
104
+ | `init()` | No |
105
+ | `put()` | No |
106
+ | `delete()` | No |
107
+ | `get()` / `query()` | No |
108
+ | `sync()` | Yes, if adapter configured |
109
+
110
+ ## Adapters
111
+
112
+ ### Google Drive
113
+ For zero new backend infrastructure where Google Drive is acceptable as the encrypted mailbox.
114
+
115
+ ### WebDAV
116
+ For self-hosted sync, local demos, and inspectable remote artifacts.
117
+
118
+ ### Cloudflare (experimental)
119
+ For Interocitor-native transport flows with invalidation fanout and maintenance endpoints while keeping decryption client-side.
120
+
121
+ ### Memory
122
+ For tests and adapter-contract validation.
123
+
124
+ ## Local store
125
+
126
+ Interocitor is a sync engine, not a database. The local store is a pluggable abstraction (`LocalStoreAdapter`). The browser default uses IndexedDB. The Swift package uses SQLite. You don't need to care which — reads, writes, and queries go through the engine API.
127
+
128
+ ## CRDT strategy
129
+
130
+ Interocitor uses per-column CRDTs with hybrid logical clocks (HLC). All merge happens on the client. Remote storage is just a byte pipe.
131
+
132
+ Conflict default: `'remote-wins'`.
133
+ Override at database, table, or field level.
134
+
135
+ ## Events
136
+
137
+ ```ts
138
+ engine.on('change', (event) => {
139
+ console.log(event.type, event.table, event.id);
140
+ });
141
+ ```
142
+
143
+ ## Local TODO demo (WebDAV)
144
+
145
+ From the monorepo root:
146
+
147
+ ```bash
148
+ yarn demo:todo
149
+ ```
150
+
151
+ ## What this is not
152
+
153
+ - not Firebase
154
+ - not Fireproof
155
+ - not PowerSync
156
+ - not Replicache
157
+ - not a hosted backend
158
+ - not a query engine over the cloud
159
+ - not a server-trusted merge layer
160
+
161
+ ## Tests
162
+
163
+ From the monorepo root:
164
+
165
+ ```bash
166
+ yarn workspace interocitor test
167
+ ```
168
+
169
+ ## Package context
170
+
171
+ This package is the main JavaScript/TypeScript runtime in the monorepo. See also:
172
+ - root `README.md` — monorepo overview
173
+ - `packages/interocitor-swift` — Swift client
174
+ - `examples/todo-webdav` — local demo app
175
+
176
+ ## License
177
+
178
+ MIT
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Cloudflare Worker + D1 Storage Adapter (Interocitor-native protocol)
3
+ *
4
+ * This adapter is a perfect-fit backend for Interocitor only.
5
+ * It does NOT parse XML and does NOT depend on WebDAV compatibility.
6
+ *
7
+ * Base URL shape:
8
+ * https://<worker>/io/<prefix>
9
+ *
10
+ * The adapter derives:
11
+ * wss://<worker>/notify/<prefix> (WebSocket invalidations via InterocitorRelay DO)
12
+ */
13
+ import type { StorageAdapter, FileEntry } from '../core/types.ts';
14
+ export interface CloudflareAdapterConfig {
15
+ /** Worker IO base URL that includes prefix, e.g. https://worker/io/team-a */
16
+ baseUrl: string;
17
+ /** Optional bearer for server/cost protection (INTEROCITOR_ACCESS_TOKEN). */
18
+ token?: string;
19
+ }
20
+ /**
21
+ * Interocitor-native Cloudflare adapter for Worker + D1 based deployments.
22
+ *
23
+ * Use this when you want a purpose-fit backend with optional WebSocket-driven
24
+ * invalidation instead of a generic file protocol like WebDAV.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const adapter = new CloudflareAdapter({
29
+ * baseUrl: 'https://example.com/io/team-a',
30
+ * token: 'optional-bearer-token',
31
+ * });
32
+ * ```
33
+ */
34
+ /** Config shape embedded in QR payloads for CloudflareAdapter. Credentials excluded. */
35
+ export interface CloudflareHandshakeConfig {
36
+ /** Worker IO base URL including the /io/<prefix> path segment. */
37
+ baseUrl: string;
38
+ }
39
+ export declare class CloudflareAdapter implements StorageAdapter {
40
+ readonly name = "cloudflare";
41
+ private readonly config;
42
+ private authenticated;
43
+ constructor(config: CloudflareAdapterConfig);
44
+ private headers;
45
+ private get ioBaseUrl();
46
+ private get notifyUrl();
47
+ private ioUrl;
48
+ private fileUrl;
49
+ authenticate(): Promise<void>;
50
+ /**
51
+ * Returns the worker base URL (without credentials) for embedding in a QR payload.
52
+ * The scanner uses this to point their CloudflareAdapter at the same worker shard.
53
+ */
54
+ getHandshakeConfig(): string;
55
+ isAuthenticated(): boolean;
56
+ subscribeToInvalidations(onInvalidate: (payload: {
57
+ type: string;
58
+ path: string;
59
+ ts: number;
60
+ }) => void, hooks?: {
61
+ onReady?: () => void;
62
+ onError?: () => void;
63
+ }): () => void;
64
+ ensureFolder(path: string): Promise<void>;
65
+ listFiles(path: string): Promise<FileEntry[]>;
66
+ listFolders(path: string): Promise<string[]>;
67
+ readFile(path: string): Promise<Uint8Array>;
68
+ writeFile(path: string, data: Uint8Array | string): Promise<void>;
69
+ deleteFile(path: string): Promise<void>;
70
+ getFileMetadata(path: string): Promise<FileEntry | null>;
71
+ }
72
+ //# sourceMappingURL=cloudflare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACtC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAUD;;;;;;;;;;;;;GAaG;AACH,wFAAwF;AACxF,MAAM,WAAW,yBAAyB;IACxC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,IAAI,gBAAgB;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,aAAa,CAAS;gBAElB,MAAM,EAAE,uBAAuB;IAI3C,OAAO,CAAC,OAAO;IAQf,OAAO,KAAK,SAAS,GAMpB;IAED,OAAO,KAAK,SAAS,GAWpB;IAED,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,OAAO;IAMT,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBnC;;;OAGG;IACH,kBAAkB,IAAI,MAAM;IAK5B,eAAe,IAAI,OAAO;IAI1B,wBAAwB,CACtB,YAAY,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,EAC3E,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,GACrD,MAAM,IAAI;IAwDP,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAqB7C,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAe5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAa3C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;CAsB/D"}
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Cloudflare Worker + D1 Storage Adapter (Interocitor-native protocol)
3
+ *
4
+ * This adapter is a perfect-fit backend for Interocitor only.
5
+ * It does NOT parse XML and does NOT depend on WebDAV compatibility.
6
+ *
7
+ * Base URL shape:
8
+ * https://<worker>/io/<prefix>
9
+ *
10
+ * The adapter derives:
11
+ * wss://<worker>/notify/<prefix> (WebSocket invalidations via InterocitorRelay DO)
12
+ */
13
+ export class CloudflareAdapter {
14
+ constructor(config) {
15
+ this.name = 'cloudflare';
16
+ this.authenticated = false;
17
+ this.config = { ...config, baseUrl: config.baseUrl.replace(/\/$/, '') };
18
+ }
19
+ headers(extra) {
20
+ const base = {};
21
+ if (this.config.token) {
22
+ base.Authorization = `Bearer ${this.config.token}`;
23
+ }
24
+ return { ...base, ...extra };
25
+ }
26
+ get ioBaseUrl() {
27
+ const u = new URL(this.config.baseUrl);
28
+ if (!u.pathname.includes('/io/')) {
29
+ throw new Error('CloudflareAdapter baseUrl must include /io/<prefix>');
30
+ }
31
+ return u.toString().replace(/\/$/, '');
32
+ }
33
+ get notifyUrl() {
34
+ const u = new URL(this.config.baseUrl);
35
+ if (!u.pathname.includes('/io/')) {
36
+ throw new Error('CloudflareAdapter baseUrl must include /io/<prefix>');
37
+ }
38
+ u.pathname = u.pathname.replace('/io/', '/notify/');
39
+ u.protocol = u.protocol === 'https:' ? 'wss:' : 'ws:';
40
+ if (this.config.token) {
41
+ u.searchParams.set('access_token', this.config.token);
42
+ }
43
+ return u.toString().replace(/\/$/, '');
44
+ }
45
+ ioUrl(pathname) {
46
+ const clean = pathname.startsWith('/') ? pathname : `/${pathname}`;
47
+ return `${this.ioBaseUrl}${clean}`;
48
+ }
49
+ fileUrl(path) {
50
+ const u = new URL(this.ioUrl('/file'));
51
+ u.searchParams.set('path', path);
52
+ return u.toString();
53
+ }
54
+ async authenticate() {
55
+ const res = await fetch(this.ioUrl('/health'), {
56
+ method: 'GET',
57
+ headers: this.headers(),
58
+ });
59
+ if (res.ok) {
60
+ this.authenticated = true;
61
+ return;
62
+ }
63
+ if (res.status === 401 || res.status === 403) {
64
+ throw new Error('Cloudflare Worker auth failed — check your access token');
65
+ }
66
+ throw new Error(`Cloudflare Worker unreachable: HTTP ${res.status}`);
67
+ }
68
+ /**
69
+ * Returns the worker base URL (without credentials) for embedding in a QR payload.
70
+ * The scanner uses this to point their CloudflareAdapter at the same worker shard.
71
+ */
72
+ getHandshakeConfig() {
73
+ const cfg = { baseUrl: this.config.baseUrl };
74
+ return JSON.stringify(cfg);
75
+ }
76
+ isAuthenticated() {
77
+ return this.authenticated;
78
+ }
79
+ subscribeToInvalidations(onInvalidate, hooks) {
80
+ let ws = null;
81
+ let cancelled = false;
82
+ let backoffMs = 1000;
83
+ const MAX_BACKOFF_MS = 30000;
84
+ const connect = () => {
85
+ if (cancelled)
86
+ return;
87
+ try {
88
+ ws = new WebSocket(this.notifyUrl);
89
+ }
90
+ catch {
91
+ hooks?.onError?.();
92
+ scheduleReconnect();
93
+ return;
94
+ }
95
+ ws.onopen = () => {
96
+ backoffMs = 1000;
97
+ hooks?.onReady?.();
98
+ };
99
+ ws.onmessage = (e) => {
100
+ try {
101
+ const msg = JSON.parse(e.data);
102
+ if (msg.type === 'invalidation' || msg.type === 'invalidate' || msg.type === 'compact') {
103
+ onInvalidate(msg);
104
+ }
105
+ }
106
+ catch {
107
+ onInvalidate({ type: 'unknown', path: '/', ts: Date.now() });
108
+ }
109
+ };
110
+ ws.onerror = () => {
111
+ hooks?.onError?.();
112
+ };
113
+ ws.onclose = () => {
114
+ if (!cancelled)
115
+ scheduleReconnect();
116
+ };
117
+ };
118
+ const scheduleReconnect = () => {
119
+ if (cancelled)
120
+ return;
121
+ setTimeout(() => connect(), backoffMs);
122
+ backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
123
+ };
124
+ connect();
125
+ return () => {
126
+ cancelled = true;
127
+ try {
128
+ ws?.close(1000, 'unsubscribed');
129
+ }
130
+ catch { }
131
+ ws = null;
132
+ };
133
+ }
134
+ async ensureFolder(path) {
135
+ const res = await fetch(this.ioUrl('/ensure-folder'), {
136
+ method: 'POST',
137
+ headers: this.headers({ 'Content-Type': 'application/json; charset=utf-8' }),
138
+ body: JSON.stringify({ path }),
139
+ });
140
+ if (!res.ok && res.status !== 405) {
141
+ throw new Error(`Failed to ensure folder ${path}: HTTP ${res.status}`);
142
+ }
143
+ }
144
+ async listFiles(path) {
145
+ const res = await fetch(this.ioUrl('/list-files'), {
146
+ method: 'POST',
147
+ headers: this.headers({ 'Content-Type': 'application/json; charset=utf-8' }),
148
+ body: JSON.stringify({ path }),
149
+ });
150
+ if (!res.ok) {
151
+ throw new Error(`Failed to list files for ${path}: HTTP ${res.status}`);
152
+ }
153
+ const payload = await res.json();
154
+ return (payload.files ?? []).map((f) => ({
155
+ name: f.name,
156
+ path: f.path,
157
+ size: f.size,
158
+ modifiedTime: f.modifiedTime,
159
+ etag: f.etag,
160
+ }));
161
+ }
162
+ async listFolders(path) {
163
+ const res = await fetch(this.ioUrl('/list-folders'), {
164
+ method: 'POST',
165
+ headers: this.headers({ 'Content-Type': 'application/json; charset=utf-8' }),
166
+ body: JSON.stringify({ path }),
167
+ });
168
+ if (!res.ok) {
169
+ throw new Error(`Failed to list folders for ${path}: HTTP ${res.status}`);
170
+ }
171
+ const payload = await res.json();
172
+ return payload.folders ?? [];
173
+ }
174
+ async readFile(path) {
175
+ const res = await fetch(this.fileUrl(path), {
176
+ method: 'GET',
177
+ headers: this.headers(),
178
+ });
179
+ if (!res.ok) {
180
+ throw new Error(`Failed to read ${path}: HTTP ${res.status}`);
181
+ }
182
+ return new Uint8Array(await res.arrayBuffer());
183
+ }
184
+ async writeFile(path, data) {
185
+ const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
186
+ const res = await fetch(this.fileUrl(path), {
187
+ method: 'PUT',
188
+ headers: this.headers({ 'Content-Type': 'application/octet-stream' }),
189
+ body: bytes,
190
+ });
191
+ if (!res.ok && res.status !== 201 && res.status !== 204) {
192
+ throw new Error(`Failed to write ${path}: HTTP ${res.status}`);
193
+ }
194
+ }
195
+ async deleteFile(path) {
196
+ const res = await fetch(this.fileUrl(path), {
197
+ method: 'DELETE',
198
+ headers: this.headers(),
199
+ });
200
+ if (!res.ok && res.status !== 404 && res.status !== 405) {
201
+ throw new Error(`Failed to delete ${path}: HTTP ${res.status}`);
202
+ }
203
+ }
204
+ async getFileMetadata(path) {
205
+ const res = await fetch(this.ioUrl('/metadata'), {
206
+ method: 'POST',
207
+ headers: this.headers({ 'Content-Type': 'application/json; charset=utf-8' }),
208
+ body: JSON.stringify({ path }),
209
+ });
210
+ if (res.status === 404)
211
+ return null;
212
+ if (!res.ok)
213
+ return null;
214
+ const payload = await res.json();
215
+ const f = payload.file;
216
+ if (!f)
217
+ return null;
218
+ return {
219
+ name: f.name,
220
+ path: f.path,
221
+ size: f.size,
222
+ modifiedTime: f.modifiedTime,
223
+ etag: f.etag,
224
+ };
225
+ }
226
+ }
227
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Google Drive Storage Adapter
3
+ *
4
+ * Uses Google Drive API v3 via OAuth2 with `drive.file` scope.
5
+ * The app can only see files it created or the user explicitly shared.
6
+ *
7
+ * Requires: Google API client ID from Google Cloud Console.
8
+ * Auth flow: Google Identity Services (GIS) popup or redirect.
9
+ */
10
+ import type { StorageAdapter, FileEntry } from '../core/types.ts';
11
+ interface GoogleDriveConfig {
12
+ clientId: string;
13
+ /** Redirect URI for OAuth (default: current origin) */
14
+ redirectUri?: string;
15
+ }
16
+ /**
17
+ * Google Drive adapter using the browser OAuth token flow.
18
+ *
19
+ * This is the easiest zero-infrastructure option when your users already live
20
+ * in Google Workspace or personal Drive.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const adapter = new GoogleDriveAdapter({ clientId: 'YOUR_GOOGLE_CLIENT_ID' });
25
+ * const engine = new SyncEngine(adapter, { remotePath: '/MyApp' });
26
+ * ```
27
+ */
28
+ export declare class GoogleDriveAdapter implements StorageAdapter {
29
+ readonly name = "google-drive";
30
+ private config;
31
+ private accessToken;
32
+ private fileIdCache;
33
+ private folderIdCache;
34
+ constructor(config: GoogleDriveConfig);
35
+ authenticate(): Promise<void>;
36
+ /** Set token directly (for apps that handle their own OAuth flow). */
37
+ setAccessToken(token: string): void;
38
+ /**
39
+ * Returns the Google OAuth clientId for embedding in a QR payload.
40
+ * The scanner uses this to configure their GoogleDriveAdapter.
41
+ * The OAuth flow (and resulting access token) is performed separately by the user.
42
+ */
43
+ getHandshakeConfig(): string;
44
+ isAuthenticated(): boolean;
45
+ private verifyToken;
46
+ private headers;
47
+ /**
48
+ * Resolve a path like "/Interocitor/changes/dev_abc.ndjson"
49
+ * to a Google Drive file ID by walking the folder tree.
50
+ */
51
+ private resolveFileId;
52
+ private resolveFolderId;
53
+ ensureFolder(path: string): Promise<void>;
54
+ listFiles(folderPath: string): Promise<FileEntry[]>;
55
+ listFolders(folderPath: string): Promise<string[]>;
56
+ readFile(path: string): Promise<Uint8Array>;
57
+ writeFile(path: string, data: Uint8Array | string): Promise<void>;
58
+ deleteFile(path: string): Promise<void>;
59
+ getFileMetadata(path: string): Promise<FileEntry | null>;
60
+ /** Clear caches (useful after compaction deletes files). */
61
+ clearCache(): void;
62
+ }
63
+ export {};
64
+ //# sourceMappingURL=google-drive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-drive.d.ts","sourceRoot":"","sources":["../../src/adapters/google-drive.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAMlE,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,IAAI,kBAAkB;IAE/B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,WAAW,CAAuB;IAG1C,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,aAAa,CAAkC;gBAE3C,MAAM,EAAE,iBAAiB;IAM/B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDnC,sEAAsE;IACtE,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKnC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM;IAI5B,eAAe,IAAI,OAAO;YAIZ,WAAW;IAWzB,OAAO,CAAC,OAAO;IAOf;;;OAGG;YACW,aAAa;YAyCb,eAAe;IAiCvB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CzC,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAwBnD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAclD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAc3C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4EjE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAoB9D,4DAA4D;IAC5D,UAAU,IAAI,IAAI;CAInB"}