@keyneom/sync-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/dist/auth/google-web/index.d.ts +53 -0
  4. package/dist/auth/google-web/index.d.ts.map +1 -0
  5. package/dist/auth/google-web/index.js +115 -0
  6. package/dist/auth/google-web/index.js.map +1 -0
  7. package/dist/core/errors.d.ts +11 -0
  8. package/dist/core/errors.d.ts.map +1 -0
  9. package/dist/core/errors.js +19 -0
  10. package/dist/core/errors.js.map +1 -0
  11. package/dist/core/index.d.ts +3 -0
  12. package/dist/core/index.d.ts.map +1 -0
  13. package/dist/core/index.js +3 -0
  14. package/dist/core/index.js.map +1 -0
  15. package/dist/core/types.d.ts +52 -0
  16. package/dist/core/types.d.ts.map +1 -0
  17. package/dist/core/types.js +2 -0
  18. package/dist/core/types.js.map +1 -0
  19. package/dist/crypto/base64url.d.ts +3 -0
  20. package/dist/crypto/base64url.d.ts.map +1 -0
  21. package/dist/crypto/base64url.js +34 -0
  22. package/dist/crypto/base64url.js.map +1 -0
  23. package/dist/crypto/canonical.d.ts +3 -0
  24. package/dist/crypto/canonical.d.ts.map +1 -0
  25. package/dist/crypto/canonical.js +30 -0
  26. package/dist/crypto/canonical.js.map +1 -0
  27. package/dist/crypto/envelope.d.ts +31 -0
  28. package/dist/crypto/envelope.d.ts.map +1 -0
  29. package/dist/crypto/envelope.js +109 -0
  30. package/dist/crypto/envelope.js.map +1 -0
  31. package/dist/crypto/index.d.ts +6 -0
  32. package/dist/crypto/index.d.ts.map +1 -0
  33. package/dist/crypto/index.js +6 -0
  34. package/dist/crypto/index.js.map +1 -0
  35. package/dist/crypto/profiles.d.ts +73 -0
  36. package/dist/crypto/profiles.d.ts.map +1 -0
  37. package/dist/crypto/profiles.js +48 -0
  38. package/dist/crypto/profiles.js.map +1 -0
  39. package/dist/crypto/runtime.d.ts +11 -0
  40. package/dist/crypto/runtime.d.ts.map +1 -0
  41. package/dist/crypto/runtime.js +57 -0
  42. package/dist/crypto/runtime.js.map +1 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +4 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/keys/web-passkey/index.d.ts +34 -0
  48. package/dist/keys/web-passkey/index.d.ts.map +1 -0
  49. package/dist/keys/web-passkey/index.js +186 -0
  50. package/dist/keys/web-passkey/index.js.map +1 -0
  51. package/dist/snapshot/controller.d.ts +23 -0
  52. package/dist/snapshot/controller.d.ts.map +1 -0
  53. package/dist/snapshot/controller.js +150 -0
  54. package/dist/snapshot/controller.js.map +1 -0
  55. package/dist/snapshot/index.d.ts +2 -0
  56. package/dist/snapshot/index.d.ts.map +1 -0
  57. package/dist/snapshot/index.js +2 -0
  58. package/dist/snapshot/index.js.map +1 -0
  59. package/dist/snapshot/lifecycle.d.ts +11 -0
  60. package/dist/snapshot/lifecycle.d.ts.map +1 -0
  61. package/dist/snapshot/lifecycle.js +37 -0
  62. package/dist/snapshot/lifecycle.js.map +1 -0
  63. package/dist/stores/google-drive/index.d.ts +43 -0
  64. package/dist/stores/google-drive/index.d.ts.map +1 -0
  65. package/dist/stores/google-drive/index.js +136 -0
  66. package/dist/stores/google-drive/index.js.map +1 -0
  67. package/fixtures/v1/easybc-web-android-gzip.json +41 -0
  68. package/fixtures/v1/easybc-web-uncompressed.json +35 -0
  69. package/fixtures/v1/failures.json +42 -0
  70. package/fixtures/v1/family-chores-web-uncompressed.json +73 -0
  71. package/fixtures/v1/profiles.json +51 -0
  72. package/package.json +76 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Keyneom
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,157 @@
1
+ # @keyneom/sync-kit
2
+
3
+ Compatibility-preserving encrypted application-data synchronization for web,
4
+ desktop, JavaScript-native, and native protocol consumers.
5
+
6
+ The package extracts the behavior already implemented by EasyBC and Family
7
+ Chores. It does not own application schemas, merge policy, persistence, UI, or
8
+ lifecycle policy.
9
+
10
+ ## Status
11
+
12
+ Version `0.1.0` is implemented and configured as a public MIT-licensed package.
13
+
14
+ The tests in this repository cover frozen compatibility vectors, mocked
15
+ browser providers, package orchestration, and installation/imports from the
16
+ packed tarball. They do not install this package into EasyBC, Family Chores, or
17
+ Keynote.
18
+
19
+ As separate baseline checks, the existing sync-related tests in EasyBC and
20
+ Family Chores were run unchanged from those repositories. No consumer
21
+ repository was modified. Consumer migrations and their post-migration test
22
+ runs remain separate release gates.
23
+
24
+ ## Install
25
+
26
+ ```sh
27
+ npm install @keyneom/sync-kit
28
+ ```
29
+
30
+ The package has no runtime dependencies and is ESM-only.
31
+
32
+ ## Subpath exports
33
+
34
+ ```ts
35
+ import {
36
+ easyBcV1Profile,
37
+ familyChoresV1Profile,
38
+ } from "@keyneom/sync-kit/crypto";
39
+ import { createSnapshotSync } from "@keyneom/sync-kit/snapshot";
40
+ import { createWebPasskeyProvider } from "@keyneom/sync-kit/keys/web-passkey";
41
+ import { GoogleWebAuthorizationProvider } from "@keyneom/sync-kit/auth/google-web";
42
+ import {
43
+ GoogleDriveAppDataStore,
44
+ GoogleDriveSnapshotStore,
45
+ } from "@keyneom/sync-kit/stores/google-drive";
46
+ ```
47
+
48
+ Available exports:
49
+
50
+ - `/core` — provider contracts and standardized errors
51
+ - `/crypto` — v1 envelopes, profiles, AES-GCM/HKDF backends, base64url,
52
+ optional gzip, and canonical JSON/AAD helpers
53
+ - `/snapshot` — serialized snapshot synchronization
54
+ - `/snapshot/lifecycle` — opt-in browser lifecycle binding
55
+ - `/keys/web-passkey` — WebAuthn PRF keys with exact credential selection,
56
+ unlock coalescing, raw-secret zeroing, and explicit cache clearing
57
+ - `/auth/google-web` — Google Identity Services with memory-only token reuse,
58
+ expiry skew, request coalescing, and explicit invalidation
59
+ - `/stores/google-drive` — low-level `appDataFolder` objects plus a typed
60
+ snapshot wrapper
61
+
62
+ The root, `/core`, `/crypto`, and `/snapshot` expose no browser adapter.
63
+ Browser globals and script loading are used only after an explicit browser
64
+ subpath method is called.
65
+
66
+ ## Snapshot integration
67
+
68
+ Applications provide their own codec and state callbacks:
69
+
70
+ ```ts
71
+ const controller = createSnapshotSync({
72
+ appId: "family-chores",
73
+ codec: {
74
+ serialize: (value) => value,
75
+ parse: parseFamilyChoresPayload,
76
+ merge: mergeFamilyChoresPayloads,
77
+ fingerprint: stableFamilyChoresFingerprint,
78
+ updatedAt: (value) => value.exportedAt,
79
+ },
80
+ envelopeCrypto,
81
+ keyProvider,
82
+ authorizationProvider,
83
+ cloudStore,
84
+ readLocal,
85
+ applyMerged,
86
+ envelopeUpdatedAt: (envelope) => envelope.updatedAt,
87
+ });
88
+
89
+ await controller.setup();
90
+ await controller.sync("change");
91
+ controller.lock();
92
+ ```
93
+
94
+ The controller serializes cloud operations, queues at most one real local
95
+ change behind active work, ignores foreground feedback during OAuth/passkey
96
+ operations, and avoids encryption/upload when the merged stable fingerprint
97
+ matches the remote value.
98
+
99
+ ## Native applications
100
+
101
+ The wire protocol is the native compatibility boundary; the browser adapter is
102
+ not.
103
+
104
+ - EasyBC Android's Kotlin implementation reads the same v1 AES-GCM/HKDF/gzip
105
+ fixtures as this package.
106
+ - React Native and other JavaScript-native runtimes can inject a
107
+ `CryptoBackend<K>` backed by platform crypto.
108
+ - Kotlin and Swift applications implement the small key, authorization, store,
109
+ and crypto contracts natively while consuming the shared fixtures.
110
+ - Tauri applications can use `/core`, `/crypto`, and the low-level Google Drive
111
+ object store, but should perform OAuth in the system browser through
112
+ Authorization Code + PKCE.
113
+
114
+ See [native consumer guidance](docs/native-consumers.md) and the exact
115
+ [v1 compatibility contract](docs/compatibility-v1.md).
116
+
117
+ ## Compatibility and isolation
118
+
119
+ The explicit profiles preserve both applications' v1 filenames, AAD, HKDF
120
+ labels, compression behavior, and passkey names. V1 readers remain separate
121
+ because their cryptographic contexts are intentionally incompatible.
122
+
123
+ The Drive snapshot wrapper rejects the wrong `appId` before making a request.
124
+ WebAuthn unlock requests include only the envelope's exact credential ID and
125
+ reject the wrong RP ID before opening passkey UI.
126
+
127
+ Do not change EasyBC's writer to v2 until Android can read v2. V2 envelopes and
128
+ desktop-specific authorization/key adapters remain deferred.
129
+
130
+ ## Security boundary
131
+
132
+ The package:
133
+
134
+ - keeps OAuth tokens and derived keys in memory only;
135
+ - makes AES-GCM keys non-extractable in the WebCrypto backend;
136
+ - zeroes raw PRF output after derivation;
137
+ - clears key and authorization caches on explicit lock/delete;
138
+ - authenticates each v1 ciphertext with its application-specific AAD.
139
+
140
+ Applications still control provider configuration, metadata exposure, merge
141
+ correctness, local persistence, lifecycle timing, and access to the JavaScript
142
+ runtime. The package does not make a compromised application private.
143
+
144
+ ## Development
145
+
146
+ ```sh
147
+ npm install
148
+ npm run check
149
+ ```
150
+
151
+ `npm run check` verifies deterministic fixtures, lint, types, tests, build
152
+ output, packed contents, npm installation, pnpm installation, and every
153
+ documented subpath import from the installed tarball.
154
+
155
+ The extraction plan and phase gates are in
156
+ [docs/implementation-plan.md](docs/implementation-plan.md) and
157
+ [docs/execution-checklist.md](docs/execution-checklist.md).
@@ -0,0 +1,53 @@
1
+ import type { Authorization, AuthorizationProvider } from "../../core/types.js";
2
+ export declare const GOOGLE_DRIVE_APPDATA_SCOPE = "https://www.googleapis.com/auth/drive.appdata";
3
+ type TokenResponse = {
4
+ access_token?: string;
5
+ expires_in?: number;
6
+ error?: string;
7
+ error_description?: string;
8
+ };
9
+ type TokenClient = {
10
+ requestAccessToken(options?: {
11
+ prompt?: string;
12
+ }): void;
13
+ };
14
+ type GoogleIdentity = {
15
+ accounts: {
16
+ oauth2: {
17
+ initTokenClient(config: {
18
+ client_id: string;
19
+ scope: string;
20
+ callback(response: TokenResponse): void;
21
+ error_callback?(error: {
22
+ type?: string;
23
+ }): void;
24
+ }): TokenClient;
25
+ };
26
+ };
27
+ };
28
+ export type GoogleWebAuthorizationOptions = {
29
+ clientId: string;
30
+ scope?: string;
31
+ expirySkewMs?: number;
32
+ now?: () => number;
33
+ window?: Window & {
34
+ google?: GoogleIdentity;
35
+ };
36
+ document?: Document;
37
+ };
38
+ export declare class GoogleWebAuthorizationProvider implements AuthorizationProvider<Authorization> {
39
+ private readonly options;
40
+ private scriptPromise;
41
+ private cached;
42
+ private pending;
43
+ private generation;
44
+ constructor(options: GoogleWebAuthorizationOptions);
45
+ authorize(): Promise<Authorization>;
46
+ clear(): void;
47
+ private requestFresh;
48
+ private loadScript;
49
+ private window;
50
+ private document;
51
+ }
52
+ export {};
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/google-web/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACtB,MAAM,qBAAqB,CAAC;AAG7B,eAAO,MAAM,0BAA0B,kDACU,CAAC;AAElD,KAAK,aAAa,GAAG;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,kBAAkB,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACzD,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE;QACR,MAAM,EAAE;YACN,eAAe,CAAC,MAAM,EAAE;gBACtB,SAAS,EAAE,MAAM,CAAC;gBAClB,KAAK,EAAE,MAAM,CAAC;gBACd,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;gBACxC,cAAc,CAAC,CAAC,KAAK,EAAE;oBAAE,IAAI,CAAC,EAAE,MAAM,CAAA;iBAAE,GAAG,IAAI,CAAC;aACjD,GAAG,WAAW,CAAC;SACjB,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IAC9C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF,qBAAa,8BACX,YAAW,qBAAqB,CAAC,aAAa,CAAC;IAOnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,UAAU,CAAK;gBAEM,OAAO,EAAE,6BAA6B;IAE7D,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC;IAwBzC,KAAK,IAAI,IAAI;YAMC,YAAY;IAyD1B,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,QAAQ;CAYjB"}
@@ -0,0 +1,115 @@
1
+ import { SyncKitError } from "../../core/errors.js";
2
+ export const GOOGLE_DRIVE_APPDATA_SCOPE = "https://www.googleapis.com/auth/drive.appdata";
3
+ export class GoogleWebAuthorizationProvider {
4
+ options;
5
+ scriptPromise = null;
6
+ cached = null;
7
+ pending = null;
8
+ generation = 0;
9
+ constructor(options) {
10
+ this.options = options;
11
+ }
12
+ async authorize() {
13
+ const now = this.options.now?.() ?? Date.now();
14
+ if (this.cached?.clientId === this.options.clientId &&
15
+ (this.cached.expiresAt ?? 0) > now) {
16
+ return {
17
+ accessToken: this.cached.accessToken,
18
+ ...(this.cached.expiresAt === undefined
19
+ ? {}
20
+ : { expiresAt: this.cached.expiresAt }),
21
+ };
22
+ }
23
+ if (this.pending)
24
+ return this.pending;
25
+ const generation = this.generation;
26
+ const promise = this.requestFresh(generation);
27
+ this.pending = promise;
28
+ try {
29
+ return await promise;
30
+ }
31
+ finally {
32
+ if (this.pending === promise)
33
+ this.pending = null;
34
+ }
35
+ }
36
+ clear() {
37
+ this.cached = null;
38
+ this.pending = null;
39
+ this.generation += 1;
40
+ }
41
+ async requestFresh(generation) {
42
+ await this.loadScript();
43
+ const google = this.window().google;
44
+ if (!google?.accounts.oauth2) {
45
+ throw new SyncKitError("authorization", "Google authorization is unavailable.");
46
+ }
47
+ return new Promise((resolve, reject) => {
48
+ const client = google.accounts.oauth2.initTokenClient({
49
+ client_id: this.options.clientId,
50
+ scope: this.options.scope ?? GOOGLE_DRIVE_APPDATA_SCOPE,
51
+ callback: (response) => {
52
+ if (!response.access_token) {
53
+ reject(new SyncKitError("authorization", response.error_description ??
54
+ response.error ??
55
+ "Google authorization failed."));
56
+ return;
57
+ }
58
+ const now = this.options.now?.() ?? Date.now();
59
+ const expiresInMs = Math.max(0, (response.expires_in ?? 0) * 1_000);
60
+ const expiresAt = now +
61
+ Math.max(0, expiresInMs - (this.options.expirySkewMs ?? 60_000));
62
+ const authorization = {
63
+ accessToken: response.access_token,
64
+ expiresAt,
65
+ };
66
+ if (generation === this.generation && expiresAt > now) {
67
+ this.cached = {
68
+ ...authorization,
69
+ clientId: this.options.clientId,
70
+ };
71
+ }
72
+ resolve(authorization);
73
+ },
74
+ error_callback: (error) => reject(new SyncKitError("authorization", error.type ?? "Google authorization was cancelled.")),
75
+ });
76
+ client.requestAccessToken({ prompt: "" });
77
+ });
78
+ }
79
+ loadScript() {
80
+ if (this.window().google?.accounts.oauth2)
81
+ return Promise.resolve();
82
+ if (this.scriptPromise)
83
+ return this.scriptPromise;
84
+ this.scriptPromise = new Promise((resolve, reject) => {
85
+ const script = this.document().createElement("script");
86
+ script.src = "https://accounts.google.com/gsi/client";
87
+ script.async = true;
88
+ script.defer = true;
89
+ script.onload = () => resolve();
90
+ script.onerror = () => {
91
+ this.scriptPromise = null;
92
+ reject(new SyncKitError("authorization", "Google authorization could not be loaded."));
93
+ };
94
+ this.document().head.append(script);
95
+ });
96
+ return this.scriptPromise;
97
+ }
98
+ window() {
99
+ const value = this.options.window ??
100
+ (typeof window === "undefined" ? undefined : window);
101
+ if (!value) {
102
+ throw new SyncKitError("configuration", "Google web authorization requires a browser window.");
103
+ }
104
+ return value;
105
+ }
106
+ document() {
107
+ const value = this.options.document ??
108
+ (typeof document === "undefined" ? undefined : document);
109
+ if (!value) {
110
+ throw new SyncKitError("configuration", "Google web authorization requires a browser document.");
111
+ }
112
+ return value;
113
+ }
114
+ }
115
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/google-web/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type SyncErrorCode = "authorization" | "compatibility" | "configuration" | "crypto" | "decompression" | "key" | "not-found" | "provider" | "serialization" | "state";
2
+ export declare class SyncKitError extends Error {
3
+ readonly code: SyncErrorCode;
4
+ readonly status: number | undefined;
5
+ constructor(code: SyncErrorCode, message: string, options?: ErrorOptions & {
6
+ status?: number;
7
+ });
8
+ }
9
+ export declare function isSyncKitError(value: unknown): value is SyncKitError;
10
+ export declare function asSyncKitError(value: unknown, code: SyncErrorCode, message: string): SyncKitError;
11
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,eAAe,GACf,eAAe,GACf,QAAQ,GACR,eAAe,GACf,KAAK,GACL,WAAW,GACX,UAAU,GACV,eAAe,GACf,OAAO,CAAC;AAEZ,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAGlC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAY,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;CAOnD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,GACd,YAAY,CAId"}
@@ -0,0 +1,19 @@
1
+ export class SyncKitError extends Error {
2
+ code;
3
+ status;
4
+ constructor(code, message, options = {}) {
5
+ super(message, options);
6
+ this.name = "SyncKitError";
7
+ this.code = code;
8
+ this.status = options.status;
9
+ }
10
+ }
11
+ export function isSyncKitError(value) {
12
+ return value instanceof SyncKitError;
13
+ }
14
+ export function asSyncKitError(value, code, message) {
15
+ return isSyncKitError(value)
16
+ ? value
17
+ : new SyncKitError(code, message, { cause: value });
18
+ }
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,CAAgB;IACpB,MAAM,CAAqB;IAEpC,YACE,IAAmB,EACnB,OAAe,EACf,UAA8C,EAAE;QAEhD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,KAAK,YAAY,YAAY,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,KAAc,EACd,IAAmB,EACnB,OAAe;IAEf,OAAO,cAAc,CAAC,KAAK,CAAC;QAC1B,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./errors.js";
2
+ export * from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./errors.js";
2
+ export * from "./types.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,52 @@
1
+ export type SyncReason = "startup" | "foreground" | "change" | "manual";
2
+ export type SnapshotOperation = "setup" | "enable" | "sync" | "reset";
3
+ export type Authorization = {
4
+ accessToken: string;
5
+ expiresAt?: number;
6
+ };
7
+ export interface AuthorizationProvider<A = Authorization> {
8
+ authorize(): Promise<A>;
9
+ clear(): void;
10
+ }
11
+ export type StoredEnvelope<E> = {
12
+ fileId: string;
13
+ envelope: E;
14
+ };
15
+ export interface CloudStore<E, A = Authorization> {
16
+ find(appId: string, authorization: A): Promise<StoredEnvelope<E> | null>;
17
+ write(appId: string, envelope: E, authorization: A, existingId?: string): Promise<string>;
18
+ delete?(appId: string, fileId: string, authorization: A): Promise<void>;
19
+ }
20
+ export type KeyCreationContext = {
21
+ appId: string;
22
+ };
23
+ export type CreatedKey<M, K> = {
24
+ metadata: M;
25
+ key: K;
26
+ };
27
+ export interface KeyProvider<E, K, M> {
28
+ create(context: KeyCreationContext): Promise<CreatedKey<M, K>>;
29
+ unlock(envelope: E): Promise<K>;
30
+ clear(): void;
31
+ }
32
+ export interface EnvelopeCrypto<T, E, K, M> {
33
+ encrypt(value: T, key: K, metadata: M): Promise<E>;
34
+ decrypt(envelope: E, key: K): Promise<T>;
35
+ metadataFromEnvelope(envelope: E): M;
36
+ }
37
+ export interface SyncCodec<T> {
38
+ serialize(value: T): unknown;
39
+ parse(value: unknown): T;
40
+ merge(local: T, remote: T): T;
41
+ fingerprint(value: T): string;
42
+ updatedAt?(value: T): string;
43
+ }
44
+ export type SyncOutcome = "created" | "merged" | "unchanged" | "reset" | "coalesced";
45
+ export type SyncResult<T> = {
46
+ operation: SnapshotOperation;
47
+ outcome: SyncOutcome;
48
+ fileId: string | null;
49
+ syncedAt: string | null;
50
+ value: T | null;
51
+ };
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export declare function bytesToBase64Url(bytes: Uint8Array): string;
2
+ export declare function base64UrlToBytes(value: string): Uint8Array;
3
+ //# sourceMappingURL=base64url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../src/crypto/base64url.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAU1D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAiB1D"}
@@ -0,0 +1,34 @@
1
+ import { SyncKitError } from "../core/errors.js";
2
+ const base64UrlPattern = /^[A-Za-z0-9_-]*$/u;
3
+ export function bytesToBase64Url(bytes) {
4
+ if (typeof Buffer !== "undefined") {
5
+ return Buffer.from(bytes).toString("base64url");
6
+ }
7
+ let binary = "";
8
+ for (const byte of bytes)
9
+ binary += String.fromCharCode(byte);
10
+ return btoa(binary)
11
+ .replaceAll("+", "-")
12
+ .replaceAll("/", "_")
13
+ .replace(/=+$/u, "");
14
+ }
15
+ export function base64UrlToBytes(value) {
16
+ if (!base64UrlPattern.test(value) || value.length % 4 === 1) {
17
+ throw new SyncKitError("serialization", "Invalid base64url value.");
18
+ }
19
+ try {
20
+ if (typeof Buffer !== "undefined") {
21
+ return new Uint8Array(Buffer.from(value, "base64url"));
22
+ }
23
+ const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
24
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
25
+ const binary = atob(padded);
26
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
27
+ }
28
+ catch (error) {
29
+ throw new SyncKitError("serialization", "Invalid base64url value.", {
30
+ cause: error,
31
+ });
32
+ }
33
+ }
34
+ //# sourceMappingURL=base64url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64url.js","sourceRoot":"","sources":["../../src/crypto/base64url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAE7C,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC;SAChB,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC;SACpB,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC;QACH,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,0BAA0B,EAAE;YAClE,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function canonicalJson(value: unknown): string;
2
+ export declare function canonicalAad(value: unknown): Uint8Array;
3
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../../src/crypto/canonical.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAEvD"}
@@ -0,0 +1,30 @@
1
+ import { SyncKitError } from "../core/errors.js";
2
+ export function canonicalJson(value) {
3
+ return JSON.stringify(canonicalValue(value));
4
+ }
5
+ export function canonicalAad(value) {
6
+ return new TextEncoder().encode(canonicalJson(value));
7
+ }
8
+ function canonicalValue(value) {
9
+ if (value === null ||
10
+ typeof value === "string" ||
11
+ typeof value === "boolean") {
12
+ return value;
13
+ }
14
+ if (typeof value === "number") {
15
+ if (!Number.isFinite(value)) {
16
+ throw new SyncKitError("serialization", "Canonical JSON does not support non-finite numbers.");
17
+ }
18
+ return value;
19
+ }
20
+ if (Array.isArray(value))
21
+ return value.map(canonicalValue);
22
+ if (typeof value === "object") {
23
+ return Object.fromEntries(Object.entries(value)
24
+ .filter(([, child]) => child !== undefined)
25
+ .sort(([left], [right]) => left.localeCompare(right))
26
+ .map(([key, child]) => [key, canonicalValue(child)]));
27
+ }
28
+ throw new SyncKitError("serialization", `Canonical JSON does not support ${typeof value}.`);
29
+ }
30
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../src/crypto/canonical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,SAAS,EAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,qDAAqD,CACtD,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7C,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CACvD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,mCAAmC,OAAO,KAAK,GAAG,CACnD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { EnvelopeCrypto, SyncCodec } from "../core/types.js";
2
+ import type { CryptoBackend } from "./runtime.js";
3
+ import type { V1CompatibilityProfile } from "./profiles.js";
4
+ import { V1_ALGORITHM } from "./profiles.js";
5
+ export type SyncEnvelopeV1 = {
6
+ schemaVersion: 1;
7
+ algorithm: typeof V1_ALGORITHM;
8
+ compression?: "gzip";
9
+ credentialId: string;
10
+ rpId: string;
11
+ prfInput: string;
12
+ kdfSalt: string;
13
+ nonce: string;
14
+ ciphertext: string;
15
+ updatedAt: string;
16
+ };
17
+ export type V1KeyMetadata = {
18
+ credentialId: string;
19
+ rpId: string;
20
+ prfInput: Uint8Array;
21
+ kdfSalt: Uint8Array;
22
+ };
23
+ export declare function parseSyncEnvelopeV1(value: unknown, profile: V1CompatibilityProfile): SyncEnvelopeV1;
24
+ export declare function deriveContentKey<K>(profile: V1CompatibilityProfile, inputKeyMaterial: Uint8Array, salt: Uint8Array, backend: CryptoBackend<K>): Promise<K>;
25
+ export declare function encryptSyncEnvelopeV1<T, K>(value: T, key: K, metadata: V1KeyMetadata, profile: V1CompatibilityProfile, codec: Pick<SyncCodec<T>, "serialize" | "updatedAt">, backend: CryptoBackend<K>, options?: {
26
+ nonce?: Uint8Array;
27
+ now?: () => Date;
28
+ }): Promise<SyncEnvelopeV1>;
29
+ export declare function decryptSyncEnvelopeV1<T, K>(input: SyncEnvelopeV1, key: K, profile: V1CompatibilityProfile, codec: Pick<SyncCodec<T>, "parse">, backend: CryptoBackend<K>): Promise<T>;
30
+ export declare function createV1EnvelopeCrypto<T, K>(profile: V1CompatibilityProfile, codec: SyncCodec<T>, backend: CryptoBackend<K>): EnvelopeCrypto<T, SyncEnvelopeV1, K, V1KeyMetadata>;
31
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/crypto/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAK7C,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,CAAC,CAAC;IACjB,SAAS,EAAE,OAAO,YAAY,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,CAAC;IACrB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,GAC9B,cAAc,CAmChB;AAED,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,OAAO,EAAE,sBAAsB,EAC/B,gBAAgB,EAAE,UAAU,EAC5B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAC9C,KAAK,EAAE,CAAC,EACR,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC,EACpD,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GACrD,OAAO,CAAC,cAAc,CAAC,CA2CzB;AAED,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAC9C,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,CAAC,EACN,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,EACzC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,cAAc,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,aAAa,CAAC,CAarD"}