@keyneom/sync-kit 0.2.0-rc.0 → 0.2.0-rc.10

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 (68) hide show
  1. package/README.md +15 -11
  2. package/dist/auth/google-web/cache.d.ts +46 -0
  3. package/dist/auth/google-web/cache.d.ts.map +1 -0
  4. package/dist/auth/google-web/cache.js +111 -0
  5. package/dist/auth/google-web/cache.js.map +1 -0
  6. package/dist/sharing/appdata-identity-store.d.ts +36 -0
  7. package/dist/sharing/appdata-identity-store.d.ts.map +1 -0
  8. package/dist/sharing/appdata-identity-store.js +49 -0
  9. package/dist/sharing/appdata-identity-store.js.map +1 -0
  10. package/dist/sharing/change-detector.d.ts +40 -0
  11. package/dist/sharing/change-detector.d.ts.map +1 -0
  12. package/dist/sharing/change-detector.js +96 -0
  13. package/dist/sharing/change-detector.js.map +1 -0
  14. package/dist/sharing/checkpoint.d.ts +32 -0
  15. package/dist/sharing/checkpoint.d.ts.map +1 -0
  16. package/dist/sharing/checkpoint.js +2 -0
  17. package/dist/sharing/checkpoint.js.map +1 -0
  18. package/dist/sharing/controller.d.ts +88 -3
  19. package/dist/sharing/controller.d.ts.map +1 -1
  20. package/dist/sharing/controller.js +453 -110
  21. package/dist/sharing/controller.js.map +1 -1
  22. package/dist/sharing/index.d.ts +5 -1
  23. package/dist/sharing/index.d.ts.map +1 -1
  24. package/dist/sharing/index.js +3 -0
  25. package/dist/sharing/index.js.map +1 -1
  26. package/dist/sharing/join.d.ts +56 -0
  27. package/dist/sharing/join.d.ts.map +1 -0
  28. package/dist/sharing/join.js +153 -0
  29. package/dist/sharing/join.js.map +1 -0
  30. package/dist/sharing/lifecycle.d.ts +20 -0
  31. package/dist/sharing/lifecycle.d.ts.map +1 -0
  32. package/dist/sharing/lifecycle.js +55 -0
  33. package/dist/sharing/lifecycle.js.map +1 -0
  34. package/dist/sharing/link-exchange.d.ts +51 -0
  35. package/dist/sharing/link-exchange.d.ts.map +1 -0
  36. package/dist/sharing/link-exchange.js +131 -0
  37. package/dist/sharing/link-exchange.js.map +1 -0
  38. package/dist/sharing/migrating-identity-store.d.ts +29 -0
  39. package/dist/sharing/migrating-identity-store.d.ts.map +1 -0
  40. package/dist/sharing/migrating-identity-store.js +37 -0
  41. package/dist/sharing/migrating-identity-store.js.map +1 -0
  42. package/dist/sharing/registry-indexeddb.d.ts +21 -0
  43. package/dist/sharing/registry-indexeddb.d.ts.map +1 -0
  44. package/dist/sharing/registry-indexeddb.js +60 -0
  45. package/dist/sharing/registry-indexeddb.js.map +1 -0
  46. package/dist/sharing/transport.d.ts +12 -0
  47. package/dist/sharing/transport.d.ts.map +1 -1
  48. package/dist/stores/google-drive/app-folders.d.ts +19 -0
  49. package/dist/stores/google-drive/app-folders.d.ts.map +1 -0
  50. package/dist/stores/google-drive/app-folders.js +38 -0
  51. package/dist/stores/google-drive/app-folders.js.map +1 -0
  52. package/dist/stores/google-drive/folder-name.d.ts +16 -0
  53. package/dist/stores/google-drive/folder-name.d.ts.map +1 -0
  54. package/dist/stores/google-drive/folder-name.js +37 -0
  55. package/dist/stores/google-drive/folder-name.js.map +1 -0
  56. package/dist/stores/google-drive/index.d.ts +22 -0
  57. package/dist/stores/google-drive/index.d.ts.map +1 -1
  58. package/dist/stores/google-drive/index.js +66 -0
  59. package/dist/stores/google-drive/index.js.map +1 -1
  60. package/dist/stores/google-drive/picker.d.ts +20 -0
  61. package/dist/stores/google-drive/picker.d.ts.map +1 -1
  62. package/dist/stores/google-drive/picker.js +64 -0
  63. package/dist/stores/google-drive/picker.js.map +1 -1
  64. package/dist/stores/google-drive/sharing.d.ts +5 -1
  65. package/dist/stores/google-drive/sharing.d.ts.map +1 -1
  66. package/dist/stores/google-drive/sharing.js +113 -6
  67. package/dist/stores/google-drive/sharing.js.map +1 -1
  68. package/package.json +27 -4
package/README.md CHANGED
@@ -36,9 +36,9 @@ Version `0.1.1` was published to npm on 2026-06-30. It removes the
36
36
  application-specific runtime presets from `0.1.0` and keeps compatibility
37
37
  profiles consumer-owned.
38
38
 
39
- The current worktree is the unpublished `0.2.0-rc.0` sharing release
40
- candidate. Publication remains gated on live Google validation and one
41
- external consumer integration.
39
+ Version `0.2.0-rc.1` publishes shared encrypted backups, Tier A background
40
+ detection helpers, and the Android sharing port. Live Google validation and
41
+ one external consumer integration remain release gates for a stable `0.2.0`.
42
42
 
43
43
  The tests in this repository cover frozen compatibility vectors, mocked
44
44
  browser providers, package orchestration, and installation/imports from the
@@ -115,11 +115,12 @@ Available exports:
115
115
  - `/snapshot` — serialized snapshot synchronization
116
116
  - `/snapshot/lifecycle` — opt-in browser lifecycle binding
117
117
  - `/sharing` — browser-independent shared-backup protocol types, validation,
118
- roles, invitations, and signed multi-recipient envelopes
118
+ roles, invitations, signed multi-recipient envelopes, and join-link helpers
119
119
  - `/sharing/web-crypto` — P-256 identities, ECDH key grants, signed revisions,
120
120
  invitation/key-response proofs, and decryption
121
121
  - `/sharing/controller` — headless multi-dataset orchestration, owner pinning,
122
- serialized writes, exchange processing, roles, and revocation
122
+ serialized writes, exchange processing, roles, revocation, Drive permission
123
+ reconciliation, and optional IndexedDB registry persistence
123
124
  - `/sharing/web-passkey` — passkey-encrypted sharing identities with
124
125
  non-extractable runtime keys and optional IndexedDB ciphertext storage
125
126
  - `/sharing/account-binding` — backendless Google ID-token and WebAuthn
@@ -131,7 +132,8 @@ Available exports:
131
132
  - `/auth/google-web/identity` — uncached nonce-bound Google ID tokens for
132
133
  account-binding exchanges
133
134
  - `/stores/google-drive` — low-level `appDataFolder` objects, a typed snapshot
134
- wrapper, and normal-Drive folder/per-file stores
135
+ wrapper, normal-Drive folder/per-file stores, folder-name helpers, and
136
+ accessible app-folder discovery
135
137
  - `/stores/google-drive/sharing` — managed app/exchange folders, conditional
136
138
  dataset writes, provenance checks, and per-dataset permissions
137
139
  - `/stores/google-drive/picker` — explicit folder Picker and Drive Open-with
@@ -182,7 +184,8 @@ not.
182
184
  - **Android:** `com.keyneom:sync-kit-android` (module `android/synckit`) provides
183
185
  private v1 snapshot crypto, `SnapshotSyncController`, Drive `appDataFolder`
184
186
  storage, and Credential Manager passkey PRF — the same contracts as the npm
185
- `/crypto`, `/snapshot`, and appData store paths. See
187
+ `/crypto`, `/snapshot`, and appData store paths. Published to
188
+ [GitHub Packages](https://github.com/keyneom/sync-kit/packages). See
186
189
  [android-library.md](docs/android-library.md).
187
190
  - Shared backups (`/sharing`) are TypeScript-only for now.
188
191
  - React Native and other JavaScript-native runtimes can inject a
@@ -229,10 +232,11 @@ metadata, and ciphertext to app participants while cryptographic grants control
229
232
  decryption. Applications needing metadata isolation can instead use direct file
230
233
  sharing or limited-access subfolders.
231
234
 
232
- See the [protocol and threat model](docs/shared-backups.md) and
233
- [authoritative implementation handoff](docs/sharing-implementation-handoff.md).
234
- The phased gates remain in the
235
- [sharing execution plan](docs/sharing-execution-plan.md). The crypto and
235
+ See the [protocol and threat model](docs/shared-backups.md),
236
+ [authoritative implementation handoff](docs/sharing-implementation-handoff.md),
237
+ [consumer responsibilities](docs/consumer-responsibilities.md), and
238
+ [background notifications](docs/background-notifications.md). The phased gates
239
+ remain in the [sharing execution plan](docs/sharing-execution-plan.md). The crypto and
236
240
  per-file transport, Picker, protected identity, account binding, signed
237
241
  ancestry/fork policy, key rotation, and controller primitives are implemented.
238
242
  The same sharing fixture is consumed by Java, and the packed package completes
@@ -0,0 +1,46 @@
1
+ import type { Authorization, AuthorizationProvider } from "../../core/types.js";
2
+ export type CachedAuthorizationRecord = {
3
+ profileId: string;
4
+ accessToken: string;
5
+ expiresAt: number;
6
+ accountHint?: string;
7
+ };
8
+ /**
9
+ * Opt-in persistence for short-lived Google access tokens used by Tier A
10
+ * background polling. Not enabled by default; memory-only auth remains the
11
+ * default policy.
12
+ */
13
+ export declare class IndexedDbAuthorizationCache {
14
+ private readonly options;
15
+ constructor(options?: {
16
+ databaseName?: string;
17
+ storeName?: string;
18
+ indexedDB?: IDBFactory;
19
+ });
20
+ load(profileId: string): Promise<CachedAuthorizationRecord | null>;
21
+ save(record: CachedAuthorizationRecord): Promise<void>;
22
+ delete(profileId: string): Promise<void>;
23
+ private transaction;
24
+ private open;
25
+ private storeName;
26
+ }
27
+ export type CachingAuthorizationProviderOptions = {
28
+ profileId: string;
29
+ inner: AuthorizationProvider<Authorization>;
30
+ cache: IndexedDbAuthorizationCache;
31
+ accountHint?: string;
32
+ expirySkewMs?: number;
33
+ now?: () => number;
34
+ };
35
+ /**
36
+ * Wraps an authorization provider and mirrors valid access tokens into the
37
+ * opt-in IndexedDB cache for service-worker polling.
38
+ */
39
+ export declare class CachingAuthorizationProvider implements AuthorizationProvider<Authorization> {
40
+ private readonly options;
41
+ constructor(options: CachingAuthorizationProviderOptions);
42
+ authorize(): Promise<Authorization>;
43
+ authorizeFromCache(): Promise<Authorization | null>;
44
+ clear(): void;
45
+ }
46
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/auth/google-web/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGhF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,2BAA2B;IAEpC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,UAAU,CAAC;KACnB;IAGF,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAelE,IAAI,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIhC,WAAW;IAoBzB,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,SAAS;CAGlB;AAED,MAAM,MAAM,mCAAmC,GAAG;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAC5C,KAAK,EAAE,2BAA2B,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,qBAAa,4BACX,YAAW,qBAAqB,CAAC,aAAa,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mCAAmC;IAEnE,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC;IAmBnC,kBAAkB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAezD,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,111 @@
1
+ import { SyncKitError } from "../../core/errors.js";
2
+ /**
3
+ * Opt-in persistence for short-lived Google access tokens used by Tier A
4
+ * background polling. Not enabled by default; memory-only auth remains the
5
+ * default policy.
6
+ */
7
+ export class IndexedDbAuthorizationCache {
8
+ options;
9
+ constructor(options = {}) {
10
+ this.options = options;
11
+ }
12
+ async load(profileId) {
13
+ const stored = await this.transaction("readonly", (store) => store.get(profileId));
14
+ if (!stored || typeof stored !== "object")
15
+ return null;
16
+ const record = stored;
17
+ if (typeof record.accessToken !== "string" ||
18
+ typeof record.expiresAt !== "number") {
19
+ return null;
20
+ }
21
+ return structuredClone(record);
22
+ }
23
+ async save(record) {
24
+ await this.transaction("readwrite", (store) => store.put(structuredClone(record)));
25
+ }
26
+ async delete(profileId) {
27
+ await this.transaction("readwrite", (store) => store.delete(profileId));
28
+ }
29
+ async transaction(mode, operation) {
30
+ const database = await this.open();
31
+ return new Promise((resolve, reject) => {
32
+ const transaction = database.transaction(this.storeName(), mode);
33
+ const request = operation(transaction.objectStore(this.storeName()));
34
+ request.onsuccess = () => resolve(request.result);
35
+ request.onerror = () => reject(new SyncKitError("state", "IndexedDB authorization cache failed.", {
36
+ cause: request.error,
37
+ }));
38
+ transaction.oncomplete = () => database.close();
39
+ transaction.onabort = () => database.close();
40
+ });
41
+ }
42
+ open() {
43
+ const indexedDBImplementation = this.options.indexedDB ??
44
+ (typeof indexedDB === "undefined" ? undefined : indexedDB);
45
+ if (!indexedDBImplementation) {
46
+ return Promise.reject(new SyncKitError("configuration", "IndexedDB is required for authorization cache storage."));
47
+ }
48
+ return new Promise((resolve, reject) => {
49
+ const request = indexedDBImplementation.open(this.options.databaseName ?? "sync-kit-auth-cache", 1);
50
+ request.onupgradeneeded = () => {
51
+ if (!request.result.objectStoreNames.contains(this.storeName())) {
52
+ request.result.createObjectStore(this.storeName(), {
53
+ keyPath: "profileId",
54
+ });
55
+ }
56
+ };
57
+ request.onsuccess = () => resolve(request.result);
58
+ request.onerror = () => reject(new SyncKitError("state", "IndexedDB authorization cache could not be opened.", { cause: request.error }));
59
+ });
60
+ }
61
+ storeName() {
62
+ return this.options.storeName ?? "google-authorization";
63
+ }
64
+ }
65
+ /**
66
+ * Wraps an authorization provider and mirrors valid access tokens into the
67
+ * opt-in IndexedDB cache for service-worker polling.
68
+ */
69
+ export class CachingAuthorizationProvider {
70
+ options;
71
+ constructor(options) {
72
+ this.options = options;
73
+ }
74
+ async authorize() {
75
+ const authorization = await this.options.inner.authorize();
76
+ const now = this.options.now?.() ?? Date.now();
77
+ const skew = this.options.expirySkewMs ?? 60_000;
78
+ const expiresAt = authorization.expiresAt ?? now + 3_600_000;
79
+ if (expiresAt > now + skew) {
80
+ await this.options.cache.save({
81
+ profileId: this.options.profileId,
82
+ accessToken: authorization.accessToken,
83
+ expiresAt,
84
+ ...(this.options.accountHint
85
+ ? { accountHint: this.options.accountHint }
86
+ : {}),
87
+ });
88
+ }
89
+ return authorization;
90
+ }
91
+ async authorizeFromCache() {
92
+ const record = await this.options.cache.load(this.options.profileId);
93
+ if (!record)
94
+ return null;
95
+ const now = this.options.now?.() ?? Date.now();
96
+ const skew = this.options.expirySkewMs ?? 60_000;
97
+ if (record.expiresAt <= now + skew) {
98
+ await this.options.cache.delete(this.options.profileId);
99
+ return null;
100
+ }
101
+ return {
102
+ accessToken: record.accessToken,
103
+ expiresAt: record.expiresAt,
104
+ };
105
+ }
106
+ clear() {
107
+ this.options.inner.clear();
108
+ void this.options.cache.delete(this.options.profileId);
109
+ }
110
+ }
111
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/auth/google-web/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { Authorization } from "../core/types.js";
2
+ import { GoogleDriveAppDataStore } from "../stores/google-drive/index.js";
3
+ import { type ProtectedSharingIdentityStore, type ProtectedSharingIdentityV1 } from "./web-passkey.js";
4
+ export type DriveAppDataProtectedSharingIdentityStoreOptions = {
5
+ /**
6
+ * Supplies a Drive authorization scoped to `drive.appdata`. The same Google
7
+ * account on any device returns the same private app-data folder, which is
8
+ * how a single sharing identity follows the user across devices.
9
+ */
10
+ authorization(): Promise<Authorization>;
11
+ /** Overrides the app-data filename derived from the app id. */
12
+ filename?(appId: string): string;
13
+ drive?: GoogleDriveAppDataStore;
14
+ };
15
+ /**
16
+ * Hosts the passkey-wrapped sharing identity in the signed-in Google account's
17
+ * private `drive.appdata` folder instead of device-local storage. Because the
18
+ * record is already AES-GCM-encrypted with the passkey PRF secret, app-data is
19
+ * a plaintext-safe transport: Drive only ever sees an opaque JSON blob whose
20
+ * `encryptedPrivateKeys` field it cannot read.
21
+ *
22
+ * The single-user, multi-device continuity this restores is deliberately
23
+ * distinct from the sharing (multi-user) protocol: the identity here is one
24
+ * account's own key, replicated to its own devices, never granted to another
25
+ * participant.
26
+ */
27
+ export declare class DriveAppDataProtectedSharingIdentityStore implements ProtectedSharingIdentityStore {
28
+ private readonly options;
29
+ private readonly drive;
30
+ constructor(options: DriveAppDataProtectedSharingIdentityStoreOptions);
31
+ load(appId: string): Promise<ProtectedSharingIdentityV1 | null>;
32
+ save(record: ProtectedSharingIdentityV1): Promise<void>;
33
+ delete(appId: string): Promise<void>;
34
+ private filename;
35
+ }
36
+ //# sourceMappingURL=appdata-identity-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appdata-identity-store.d.ts","sourceRoot":"","sources":["../../src/sharing/appdata-identity-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAChC,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,gDAAgD,GAAG;IAC7D;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IACxC,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,yCACX,YAAW,6BAA6B;IAKtC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;gBAG7B,OAAO,EAAE,gDAAgD;IAKtE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAQ/D,IAAI,CAAC,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,49 @@
1
+ import { GoogleDriveAppDataStore } from "../stores/google-drive/index.js";
2
+ import { parseProtectedSharingIdentityV1, } from "./web-passkey.js";
3
+ /**
4
+ * Hosts the passkey-wrapped sharing identity in the signed-in Google account's
5
+ * private `drive.appdata` folder instead of device-local storage. Because the
6
+ * record is already AES-GCM-encrypted with the passkey PRF secret, app-data is
7
+ * a plaintext-safe transport: Drive only ever sees an opaque JSON blob whose
8
+ * `encryptedPrivateKeys` field it cannot read.
9
+ *
10
+ * The single-user, multi-device continuity this restores is deliberately
11
+ * distinct from the sharing (multi-user) protocol: the identity here is one
12
+ * account's own key, replicated to its own devices, never granted to another
13
+ * participant.
14
+ */
15
+ export class DriveAppDataProtectedSharingIdentityStore {
16
+ options;
17
+ drive;
18
+ constructor(options) {
19
+ this.options = options;
20
+ this.drive = options.drive ?? new GoogleDriveAppDataStore();
21
+ }
22
+ async load(appId) {
23
+ const authorization = await this.options.authorization();
24
+ const found = await this.drive.find(this.filename(appId), authorization);
25
+ if (!found)
26
+ return null;
27
+ const text = await this.drive.readText(found.fileId, authorization);
28
+ return parseProtectedSharingIdentityV1(text);
29
+ }
30
+ async save(record) {
31
+ const authorization = await this.options.authorization();
32
+ const name = this.filename(record.appId);
33
+ const found = await this.drive.find(name, authorization);
34
+ await this.drive.write(name, JSON.stringify(record), authorization, {
35
+ ...(found ? { existingId: found.fileId } : {}),
36
+ contentType: "application/json",
37
+ });
38
+ }
39
+ async delete(appId) {
40
+ const authorization = await this.options.authorization();
41
+ const found = await this.drive.find(this.filename(appId), authorization);
42
+ if (found)
43
+ await this.drive.delete(found.fileId, authorization);
44
+ }
45
+ filename(appId) {
46
+ return (this.options.filename?.(appId) ?? `sync-kit-sharing-identity-${appId}.json`);
47
+ }
48
+ }
49
+ //# sourceMappingURL=appdata-identity-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appdata-identity-store.js","sourceRoot":"","sources":["../../src/sharing/appdata-identity-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EACL,+BAA+B,GAGhC,MAAM,kBAAkB,CAAC;AAc1B;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,yCAAyC;IAMjC;IAHF,KAAK,CAA0B;IAEhD,YACmB,OAAyD;QAAzD,YAAO,GAAP,OAAO,CAAkD;QAE1E,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,uBAAuB,EAAE,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAa;QACtB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACpE,OAAO,+BAA+B,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAkC;QAC3C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE;YAClE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,WAAW,EAAE,kBAAkB;SAChC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,CAAC;QACzE,IAAI,KAAK;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAClE,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC5B,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,6BAA6B,KAAK,OAAO,CAC5E,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,40 @@
1
+ import type { SharingChangeDetectionResult, SharingSyncCheckpoint, SharedDatasetHead } from "./checkpoint.js";
2
+ export type { SharedDatasetHead, SharingChangeDetectionResult, SharingNotificationEvent, SharingSyncCheckpoint, } from "./checkpoint.js";
3
+ export type SharingChangeDetectorOptions = {
4
+ now?: () => Date;
5
+ tokenExpiresAt?: number;
6
+ tokenExpiringSoonMs?: number;
7
+ };
8
+ /**
9
+ * Metadata-only poll for Tier A notifications. Does not decrypt, sign, or
10
+ * mutate Drive state.
11
+ */
12
+ export declare function detectSharingChanges(listKeyResponses: () => Promise<readonly {
13
+ fileId: string;
14
+ exchangeId: string;
15
+ }[]>, listDatasetHeads: () => Promise<readonly SharedDatasetHead[]>, checkpoint: SharingSyncCheckpoint, options?: SharingChangeDetectorOptions): Promise<SharingChangeDetectionResult>;
16
+ export declare class SharingChangeDetector {
17
+ private readonly options;
18
+ constructor(options: {
19
+ listKeyResponses: () => Promise<readonly {
20
+ fileId: string;
21
+ exchangeId: string;
22
+ }[]>;
23
+ listDatasetHeads: () => Promise<readonly SharedDatasetHead[]>;
24
+ tokenExpiresAt?: () => number | undefined;
25
+ now?: () => Date;
26
+ tokenExpiringSoonMs?: number;
27
+ });
28
+ detect(checkpoint: SharingSyncCheckpoint): Promise<SharingChangeDetectionResult>;
29
+ }
30
+ export declare function createSharingChangeDetectorFromTransport(transport: {
31
+ listExchanges(options?: {
32
+ kind?: "invitation" | "key-response";
33
+ }): Promise<readonly {
34
+ fileId: string;
35
+ exchangeId: string;
36
+ kind: string;
37
+ }[]>;
38
+ listDatasetHeads(): Promise<readonly SharedDatasetHead[]>;
39
+ }, options?: Omit<ConstructorParameters<typeof SharingChangeDetector>[0], "listKeyResponses" | "listDatasetHeads">): SharingChangeDetector;
40
+ //# sourceMappingURL=change-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-detector.d.ts","sourceRoot":"","sources":["../../src/sharing/change-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAE5B,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAWzB,MAAM,MAAM,4BAA4B,GAAG;IACzC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,OAAO,CAC7B,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAClD,EACD,gBAAgB,EAAE,MAAM,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,EAC7D,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,4BAA4B,CAAC,CAuDvC;AAED,qBAAa,qBAAqB;IAE9B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE;QACxB,gBAAgB,EAAE,MAAM,OAAO,CAC7B,SAAS;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,EAAE,CAClD,CAAC;QACF,gBAAgB,EAAE,MAAM,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAAC;QAC9D,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;QAC1C,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;QACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B;IAGH,MAAM,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,4BAA4B,CAAC;CAejF;AAED,wBAAgB,wCAAwC,CACtD,SAAS,EAAE;IACT,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC;KACtC,GAAG,OAAO,CACT,SAAS;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAChE,CAAC;IACF,gBAAgB,IAAI,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAAC;CAC3D,EACD,OAAO,GAAE,IAAI,CACX,qBAAqB,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,EACtD,kBAAkB,GAAG,kBAAkB,CACnC,GACL,qBAAqB,CAYvB"}
@@ -0,0 +1,96 @@
1
+ function datasetHeadSignature(head) {
2
+ return [
3
+ head.etag ?? "",
4
+ head.version ?? "",
5
+ head.headRevisionId ?? "",
6
+ head.modifiedTime ?? "",
7
+ ].join("|");
8
+ }
9
+ /**
10
+ * Metadata-only poll for Tier A notifications. Does not decrypt, sign, or
11
+ * mutate Drive state.
12
+ */
13
+ export async function detectSharingChanges(listKeyResponses, listDatasetHeads, checkpoint, options = {}) {
14
+ const now = options.now ?? (() => new Date());
15
+ const events = [];
16
+ const tokenExpiringSoonMs = options.tokenExpiringSoonMs ?? 5 * 60_000;
17
+ if (options.tokenExpiresAt !== undefined) {
18
+ if (options.tokenExpiresAt <= now().getTime()) {
19
+ events.push({ kind: "token-expired" });
20
+ return {
21
+ checkpoint: { ...checkpoint, lastPollAt: now().toISOString() },
22
+ events,
23
+ };
24
+ }
25
+ if (options.tokenExpiresAt - now().getTime() <= tokenExpiringSoonMs) {
26
+ events.push({
27
+ kind: "token-expiring-soon",
28
+ expiresAt: new Date(options.tokenExpiresAt).toISOString(),
29
+ });
30
+ }
31
+ }
32
+ const seenResponses = new Set(checkpoint.lastSeenKeyResponseFileIds ?? []);
33
+ const responses = await listKeyResponses();
34
+ for (const response of responses) {
35
+ if (!seenResponses.has(response.fileId)) {
36
+ events.push({
37
+ kind: "pending-key-response",
38
+ exchangeId: response.exchangeId,
39
+ fileId: response.fileId,
40
+ });
41
+ }
42
+ }
43
+ const previousHeads = checkpoint.datasetHeads ?? {};
44
+ const nextHeads = {};
45
+ const heads = await listDatasetHeads();
46
+ for (const head of heads) {
47
+ nextHeads[head.datasetId] = head;
48
+ const previous = previousHeads[head.datasetId];
49
+ if (previous &&
50
+ datasetHeadSignature(previous) !== datasetHeadSignature(head)) {
51
+ events.push({
52
+ kind: "shared-dataset-changed",
53
+ datasetId: head.datasetId,
54
+ fileId: head.fileId,
55
+ });
56
+ }
57
+ }
58
+ return {
59
+ checkpoint: {
60
+ lastPollAt: now().toISOString(),
61
+ lastSeenKeyResponseFileIds: responses.map((response) => response.fileId),
62
+ datasetHeads: nextHeads,
63
+ },
64
+ events,
65
+ };
66
+ }
67
+ export class SharingChangeDetector {
68
+ options;
69
+ constructor(options) {
70
+ this.options = options;
71
+ }
72
+ detect(checkpoint) {
73
+ const tokenExpiresAt = this.options.tokenExpiresAt?.();
74
+ return detectSharingChanges(this.options.listKeyResponses, this.options.listDatasetHeads, checkpoint, {
75
+ ...(this.options.now ? { now: this.options.now } : {}),
76
+ ...(tokenExpiresAt !== undefined ? { tokenExpiresAt } : {}),
77
+ ...(this.options.tokenExpiringSoonMs !== undefined
78
+ ? { tokenExpiringSoonMs: this.options.tokenExpiringSoonMs }
79
+ : {}),
80
+ });
81
+ }
82
+ }
83
+ export function createSharingChangeDetectorFromTransport(transport, options = {}) {
84
+ return new SharingChangeDetector({
85
+ listKeyResponses: async () => {
86
+ const exchanges = await transport.listExchanges({ kind: "key-response" });
87
+ return exchanges.map((exchange) => ({
88
+ fileId: exchange.fileId,
89
+ exchangeId: exchange.exchangeId,
90
+ }));
91
+ },
92
+ listDatasetHeads: () => transport.listDatasetHeads(),
93
+ ...options,
94
+ });
95
+ }
96
+ //# sourceMappingURL=change-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-detector.js","sourceRoot":"","sources":["../../src/sharing/change-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export type SharedDatasetHead = {
2
+ datasetId: string;
3
+ fileId: string;
4
+ modifiedTime?: string;
5
+ version?: string;
6
+ headRevisionId?: string;
7
+ etag?: string;
8
+ };
9
+ export type SharingSyncCheckpoint = {
10
+ lastPollAt?: string;
11
+ lastSeenKeyResponseFileIds?: string[];
12
+ datasetHeads?: Record<string, SharedDatasetHead>;
13
+ };
14
+ export type SharingNotificationEvent = {
15
+ kind: "pending-key-response";
16
+ exchangeId: string;
17
+ fileId: string;
18
+ } | {
19
+ kind: "shared-dataset-changed";
20
+ datasetId: string;
21
+ fileId: string;
22
+ } | {
23
+ kind: "token-expiring-soon";
24
+ expiresAt: string;
25
+ } | {
26
+ kind: "token-expired";
27
+ };
28
+ export type SharingChangeDetectionResult = {
29
+ checkpoint: SharingSyncCheckpoint;
30
+ events: SharingNotificationEvent[];
31
+ };
32
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/sharing/checkpoint.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAChC;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,wBAAwB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEN,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,EAAE,qBAAqB,CAAC;IAClC,MAAM,EAAE,wBAAwB,EAAE,CAAC;CACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/sharing/checkpoint.ts"],"names":[],"mappings":""}