@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
@@ -0,0 +1,109 @@
1
+ import { SyncKitError, asSyncKitError } from "../core/errors.js";
2
+ import { base64UrlToBytes, bytesToBase64Url } from "./base64url.js";
3
+ import { V1_ALGORITHM } from "./profiles.js";
4
+ const encoder = new TextEncoder();
5
+ const decoder = new TextDecoder();
6
+ export function parseSyncEnvelopeV1(value, profile) {
7
+ let parsed;
8
+ try {
9
+ parsed =
10
+ typeof value === "string"
11
+ ? JSON.parse(value)
12
+ : value;
13
+ }
14
+ catch (error) {
15
+ throw new SyncKitError("compatibility", `The ${profile.appId} snapshot is not valid JSON.`, { cause: error });
16
+ }
17
+ if (parsed?.schemaVersion !== 1 ||
18
+ parsed.algorithm !== V1_ALGORITHM ||
19
+ (parsed.compression !== undefined && parsed.compression !== "gzip") ||
20
+ (profile.compression === "none" && parsed.compression !== undefined) ||
21
+ !nonEmpty(parsed.credentialId) ||
22
+ !nonEmpty(parsed.rpId) ||
23
+ !nonEmpty(parsed.prfInput) ||
24
+ !nonEmpty(parsed.kdfSalt) ||
25
+ !nonEmpty(parsed.nonce) ||
26
+ !nonEmpty(parsed.ciphertext) ||
27
+ !nonEmpty(parsed.updatedAt)) {
28
+ throw new SyncKitError("compatibility", `The file is not a supported ${profile.appId} v1 encrypted snapshot.`);
29
+ }
30
+ validateEncodedLength(parsed.nonce, profile.nonceBytes, "nonce");
31
+ validateEncodedLength(parsed.kdfSalt, profile.kdfSaltBytes, "KDF salt");
32
+ return parsed;
33
+ }
34
+ export async function deriveContentKey(profile, inputKeyMaterial, salt, backend) {
35
+ return backend.deriveAesGcmKey(inputKeyMaterial, salt, encoder.encode(profile.hkdfInfo));
36
+ }
37
+ export async function encryptSyncEnvelopeV1(value, key, metadata, profile, codec, backend, options = {}) {
38
+ const nonce = options.nonce ?? backend.randomBytes(profile.nonceBytes);
39
+ if (nonce.length !== profile.nonceBytes) {
40
+ throw new SyncKitError("crypto", "AES-GCM nonce must be 12 bytes.");
41
+ }
42
+ let plaintext;
43
+ try {
44
+ plaintext = encoder.encode(JSON.stringify(codec.serialize(value)));
45
+ }
46
+ catch (error) {
47
+ throw new SyncKitError("serialization", "Snapshot serialization failed.", {
48
+ cause: error,
49
+ });
50
+ }
51
+ let compression;
52
+ if (profile.compression === "gzip-if-smaller") {
53
+ const compressed = await backend.gzip(plaintext);
54
+ if (compressed.length < plaintext.length) {
55
+ plaintext = compressed;
56
+ compression = "gzip";
57
+ }
58
+ }
59
+ const ciphertext = await backend.encryptAesGcm(key, nonce, encoder.encode(profile.aad), plaintext);
60
+ const updatedAt = codec.updatedAt?.(value) ??
61
+ options.now?.().toISOString() ??
62
+ new Date().toISOString();
63
+ return {
64
+ schemaVersion: 1,
65
+ algorithm: V1_ALGORITHM,
66
+ ...(compression ? { compression } : {}),
67
+ credentialId: metadata.credentialId,
68
+ rpId: metadata.rpId,
69
+ prfInput: bytesToBase64Url(metadata.prfInput),
70
+ kdfSalt: bytesToBase64Url(metadata.kdfSalt),
71
+ nonce: bytesToBase64Url(nonce),
72
+ ciphertext: bytesToBase64Url(ciphertext),
73
+ updatedAt,
74
+ };
75
+ }
76
+ export async function decryptSyncEnvelopeV1(input, key, profile, codec, backend) {
77
+ const envelope = parseSyncEnvelopeV1(input, profile);
78
+ try {
79
+ let plaintext = await backend.decryptAesGcm(key, base64UrlToBytes(envelope.nonce), encoder.encode(profile.aad), base64UrlToBytes(envelope.ciphertext));
80
+ if (envelope.compression === "gzip") {
81
+ plaintext = await backend.gunzip(plaintext);
82
+ }
83
+ return codec.parse(JSON.parse(decoder.decode(plaintext)));
84
+ }
85
+ catch (error) {
86
+ throw asSyncKitError(error, "crypto", `The key could not decrypt the ${profile.appId} snapshot.`);
87
+ }
88
+ }
89
+ export function createV1EnvelopeCrypto(profile, codec, backend) {
90
+ return {
91
+ encrypt: (value, key, metadata) => encryptSyncEnvelopeV1(value, key, metadata, profile, codec, backend),
92
+ decrypt: (envelope, key) => decryptSyncEnvelopeV1(envelope, key, profile, codec, backend),
93
+ metadataFromEnvelope: (envelope) => ({
94
+ credentialId: envelope.credentialId,
95
+ rpId: envelope.rpId,
96
+ prfInput: base64UrlToBytes(envelope.prfInput),
97
+ kdfSalt: base64UrlToBytes(envelope.kdfSalt),
98
+ }),
99
+ };
100
+ }
101
+ function nonEmpty(value) {
102
+ return typeof value === "string" && value.length > 0;
103
+ }
104
+ function validateEncodedLength(value, expected, label) {
105
+ if (base64UrlToBytes(value).length !== expected) {
106
+ throw new SyncKitError("compatibility", `The v1 envelope ${label} has an invalid length.`);
107
+ }
108
+ }
109
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/crypto/envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from "./base64url.js";
2
+ export * from "./canonical.js";
3
+ export * from "./envelope.js";
4
+ export * from "./profiles.js";
5
+ export * from "./runtime.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./base64url.js";
2
+ export * from "./canonical.js";
3
+ export * from "./envelope.js";
4
+ export * from "./profiles.js";
5
+ export * from "./runtime.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/crypto/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,73 @@
1
+ export declare const V1_ALGORITHM: "AES-256-GCM+HKDF-SHA-256";
2
+ export type V1Compression = "none" | "gzip-if-smaller";
3
+ export type PasskeyProfile = {
4
+ rpName: string;
5
+ userName: string;
6
+ userDisplayName: string;
7
+ algorithm: -7;
8
+ residentKey: "required";
9
+ userVerification: "required";
10
+ timeoutMs: number;
11
+ };
12
+ export type V1CompatibilityProfile = {
13
+ appId: string;
14
+ filename: string;
15
+ aad: string;
16
+ hkdfInfo: string;
17
+ algorithm: typeof V1_ALGORITHM;
18
+ readVersions: readonly [1];
19
+ writeVersion: 1;
20
+ compression: V1Compression;
21
+ nonceBytes: 12;
22
+ kdfSaltBytes: 32;
23
+ prfInputBytes: 32;
24
+ tagBits: 128;
25
+ passkey: PasskeyProfile;
26
+ };
27
+ export declare const easyBcV1Profile: {
28
+ readonly appId: "easy-bc";
29
+ readonly filename: "easybc-sync-v1.json";
30
+ readonly aad: "easy-bc-sync-envelope-v1";
31
+ readonly hkdfInfo: "easy-bc-cloud-content-key-v1";
32
+ readonly algorithm: "AES-256-GCM+HKDF-SHA-256";
33
+ readonly readVersions: readonly [1];
34
+ readonly writeVersion: 1;
35
+ readonly compression: "gzip-if-smaller";
36
+ readonly nonceBytes: 12;
37
+ readonly kdfSaltBytes: 32;
38
+ readonly prfInputBytes: 32;
39
+ readonly tagBits: 128;
40
+ readonly passkey: {
41
+ readonly rpName: "EasyBC";
42
+ readonly userName: "encrypted-sync";
43
+ readonly userDisplayName: "EasyBC encrypted sync";
44
+ readonly algorithm: -7;
45
+ readonly residentKey: "required";
46
+ readonly userVerification: "required";
47
+ readonly timeoutMs: 60000;
48
+ };
49
+ };
50
+ export declare const familyChoresV1Profile: {
51
+ readonly appId: "family-chores";
52
+ readonly filename: "family-chores-sync-v1.json";
53
+ readonly aad: "family-chores-sync-envelope-v1";
54
+ readonly hkdfInfo: "family-chores-cloud-content-key-v1";
55
+ readonly algorithm: "AES-256-GCM+HKDF-SHA-256";
56
+ readonly readVersions: readonly [1];
57
+ readonly writeVersion: 1;
58
+ readonly compression: "none";
59
+ readonly nonceBytes: 12;
60
+ readonly kdfSaltBytes: 32;
61
+ readonly prfInputBytes: 32;
62
+ readonly tagBits: 128;
63
+ readonly passkey: {
64
+ readonly rpName: "Family Chores";
65
+ readonly userName: "encrypted-sync";
66
+ readonly userDisplayName: "Family Chores encrypted sync";
67
+ readonly algorithm: -7;
68
+ readonly residentKey: "required";
69
+ readonly userVerification: "required";
70
+ readonly timeoutMs: 60000;
71
+ };
72
+ };
73
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/crypto/profiles.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,EAAG,0BAAmC,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,CAAC,CAAC,CAAC;IACd,WAAW,EAAE,UAAU,CAAC;IACxB,gBAAgB,EAAE,UAAU,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,YAAY,CAAC;IAC/B,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,YAAY,EAAE,CAAC,CAAC;IAChB,WAAW,EAAE,aAAa,CAAC;IAC3B,UAAU,EAAE,EAAE,CAAC;IACf,YAAY,EAAE,EAAE,CAAC;IACjB,aAAa,EAAE,EAAE,CAAC;IAClB,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAsBe,CAAC;AAE5C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;CAsBS,CAAC"}
@@ -0,0 +1,48 @@
1
+ export const V1_ALGORITHM = "AES-256-GCM+HKDF-SHA-256";
2
+ export const easyBcV1Profile = {
3
+ appId: "easy-bc",
4
+ filename: "easybc-sync-v1.json",
5
+ aad: "easy-bc-sync-envelope-v1",
6
+ hkdfInfo: "easy-bc-cloud-content-key-v1",
7
+ algorithm: V1_ALGORITHM,
8
+ readVersions: [1],
9
+ writeVersion: 1,
10
+ compression: "gzip-if-smaller",
11
+ nonceBytes: 12,
12
+ kdfSaltBytes: 32,
13
+ prfInputBytes: 32,
14
+ tagBits: 128,
15
+ passkey: {
16
+ rpName: "EasyBC",
17
+ userName: "encrypted-sync",
18
+ userDisplayName: "EasyBC encrypted sync",
19
+ algorithm: -7,
20
+ residentKey: "required",
21
+ userVerification: "required",
22
+ timeoutMs: 60_000,
23
+ },
24
+ };
25
+ export const familyChoresV1Profile = {
26
+ appId: "family-chores",
27
+ filename: "family-chores-sync-v1.json",
28
+ aad: "family-chores-sync-envelope-v1",
29
+ hkdfInfo: "family-chores-cloud-content-key-v1",
30
+ algorithm: V1_ALGORITHM,
31
+ readVersions: [1],
32
+ writeVersion: 1,
33
+ compression: "none",
34
+ nonceBytes: 12,
35
+ kdfSaltBytes: 32,
36
+ prfInputBytes: 32,
37
+ tagBits: 128,
38
+ passkey: {
39
+ rpName: "Family Chores",
40
+ userName: "encrypted-sync",
41
+ userDisplayName: "Family Chores encrypted sync",
42
+ algorithm: -7,
43
+ residentKey: "required",
44
+ userVerification: "required",
45
+ timeoutMs: 60_000,
46
+ },
47
+ };
48
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/crypto/profiles.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,0BAAmC,CAAC;AA8BhE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,qBAAqB;IAC/B,GAAG,EAAE,0BAA0B;IAC/B,QAAQ,EAAE,8BAA8B;IACxC,SAAS,EAAE,YAAY;IACvB,YAAY,EAAE,CAAC,CAAC,CAAC;IACjB,YAAY,EAAE,CAAC;IACf,WAAW,EAAE,iBAAiB;IAC9B,UAAU,EAAE,EAAE;IACd,YAAY,EAAE,EAAE;IAChB,aAAa,EAAE,EAAE;IACjB,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE;QACP,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,gBAAgB;QAC1B,eAAe,EAAE,uBAAuB;QACxC,SAAS,EAAE,CAAC,CAAC;QACb,WAAW,EAAE,UAAU;QACvB,gBAAgB,EAAE,UAAU;QAC5B,SAAS,EAAE,MAAM;KAClB;CACwC,CAAC;AAE5C,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,KAAK,EAAE,eAAe;IACtB,QAAQ,EAAE,4BAA4B;IACtC,GAAG,EAAE,gCAAgC;IACrC,QAAQ,EAAE,oCAAoC;IAC9C,SAAS,EAAE,YAAY;IACvB,YAAY,EAAE,CAAC,CAAC,CAAC;IACjB,YAAY,EAAE,CAAC;IACf,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,EAAE;IACd,YAAY,EAAE,EAAE;IAChB,aAAa,EAAE,EAAE;IACjB,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE;QACP,MAAM,EAAE,eAAe;QACvB,QAAQ,EAAE,gBAAgB;QAC1B,eAAe,EAAE,8BAA8B;QAC/C,SAAS,EAAE,CAAC,CAAC;QACb,WAAW,EAAE,UAAU;QACvB,gBAAgB,EAAE,UAAU;QAC5B,SAAS,EAAE,MAAM;KAClB;CACwC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface CryptoBackend<K> {
2
+ randomBytes(length: number): Uint8Array;
3
+ deriveAesGcmKey(inputKeyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array): Promise<K>;
4
+ encryptAesGcm(key: K, nonce: Uint8Array, aad: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array>;
5
+ decryptAesGcm(key: K, nonce: Uint8Array, aad: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array>;
6
+ gzip(plaintext: Uint8Array): Promise<Uint8Array>;
7
+ gunzip(compressed: Uint8Array): Promise<Uint8Array>;
8
+ }
9
+ export declare function createWebCryptoBackend(cryptoImplementation?: Crypto): CryptoBackend<CryptoKey>;
10
+ export declare function copyBuffer(bytes: Uint8Array): ArrayBuffer;
11
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/crypto/runtime.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IACxC,eAAe,CACb,gBAAgB,EAAE,UAAU,EAC5B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,aAAa,CACX,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,aAAa,CACX,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACrD;AAED,wBAAgB,sBAAsB,CACpC,oBAAoB,GAAE,MAA0B,GAC/C,aAAa,CAAC,SAAS,CAAC,CAmE1B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAEzD"}
@@ -0,0 +1,57 @@
1
+ import { SyncKitError } from "../core/errors.js";
2
+ export function createWebCryptoBackend(cryptoImplementation = globalThis.crypto) {
3
+ if (!cryptoImplementation?.subtle) {
4
+ throw new SyncKitError("configuration", "A WebCrypto implementation is required.");
5
+ }
6
+ return {
7
+ randomBytes(length) {
8
+ return cryptoImplementation.getRandomValues(new Uint8Array(length));
9
+ },
10
+ async deriveAesGcmKey(inputKeyMaterial, salt, info) {
11
+ const material = await cryptoImplementation.subtle.importKey("raw", copyBuffer(inputKeyMaterial), "HKDF", false, ["deriveKey"]);
12
+ return cryptoImplementation.subtle.deriveKey({
13
+ name: "HKDF",
14
+ hash: "SHA-256",
15
+ salt: copyBuffer(salt),
16
+ info: copyBuffer(info),
17
+ }, material, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
18
+ },
19
+ async encryptAesGcm(key, nonce, aad, plaintext) {
20
+ return new Uint8Array(await cryptoImplementation.subtle.encrypt({
21
+ name: "AES-GCM",
22
+ iv: copyBuffer(nonce),
23
+ additionalData: copyBuffer(aad),
24
+ tagLength: 128,
25
+ }, key, copyBuffer(plaintext)));
26
+ },
27
+ async decryptAesGcm(key, nonce, aad, ciphertext) {
28
+ return new Uint8Array(await cryptoImplementation.subtle.decrypt({
29
+ name: "AES-GCM",
30
+ iv: copyBuffer(nonce),
31
+ additionalData: copyBuffer(aad),
32
+ tagLength: 128,
33
+ }, key, copyBuffer(ciphertext)));
34
+ },
35
+ gzip(value) {
36
+ return transformBytes(value, new CompressionStream("gzip"));
37
+ },
38
+ gunzip(value) {
39
+ return transformBytes(value, new DecompressionStream("gzip"));
40
+ },
41
+ };
42
+ }
43
+ export function copyBuffer(bytes) {
44
+ return Uint8Array.from(bytes).buffer;
45
+ }
46
+ async function transformBytes(bytes, stream) {
47
+ try {
48
+ const input = new Blob([copyBuffer(bytes)]).stream().pipeThrough(stream);
49
+ return new Uint8Array(await new Response(input).arrayBuffer());
50
+ }
51
+ catch (error) {
52
+ throw new SyncKitError(stream instanceof DecompressionStream ? "decompression" : "crypto", stream instanceof DecompressionStream
53
+ ? "The encrypted snapshot contains invalid gzip data."
54
+ : "The snapshot could not be compressed.", { cause: error });
55
+ }
56
+ }
57
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/crypto/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAyBjD,MAAM,UAAU,sBAAsB,CACpC,uBAA+B,UAAU,CAAC,MAAM;IAEhD,IAAI,CAAC,oBAAoB,EAAE,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,yCAAyC,CAC1C,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,CAAC,MAAM;YAChB,OAAO,oBAAoB,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACtE,CAAC;QACD,KAAK,CAAC,eAAe,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI;YAChD,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAC1D,KAAK,EACL,UAAU,CAAC,gBAAgB,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,CAAC,WAAW,CAAC,CACd,CAAC;YACF,OAAO,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAC1C;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC;gBACtB,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC;aACvB,EACD,QAAQ,EACR,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAChC,KAAK,EACL,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS;YAC5C,OAAO,IAAI,UAAU,CACnB,MAAM,oBAAoB,CAAC,MAAM,CAAC,OAAO,CACvC;gBACE,IAAI,EAAE,SAAS;gBACf,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC;gBACrB,cAAc,EAAE,UAAU,CAAC,GAAG,CAAC;gBAC/B,SAAS,EAAE,GAAG;aACf,EACD,GAAG,EACH,UAAU,CAAC,SAAS,CAAC,CACtB,CACF,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU;YAC7C,OAAO,IAAI,UAAU,CACnB,MAAM,oBAAoB,CAAC,MAAM,CAAC,OAAO,CACvC;gBACE,IAAI,EAAE,SAAS;gBACf,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC;gBACrB,cAAc,EAAE,UAAU,CAAC,GAAG,CAAC;gBAC/B,SAAS,EAAE,GAAG;aACf,EACD,GAAG,EACH,UAAU,CAAC,UAAU,CAAC,CACvB,CACF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,KAAK;YACR,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,CAAC,KAAK;YACV,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,KAAiB,EACjB,MAA+C;IAE/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACzE,OAAO,IAAI,UAAU,CAAC,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CACpB,MAAM,YAAY,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,EAClE,MAAM,YAAY,mBAAmB;YACnC,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,uCAAuC,EAC3C,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./core/index.js";
2
+ export * from "./crypto/index.js";
3
+ export * from "./snapshot/index.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./core/index.js";
2
+ export * from "./crypto/index.js";
3
+ export * from "./snapshot/index.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { KeyProvider } from "../../core/types.js";
2
+ import { type CryptoBackend, type SyncEnvelopeV1, type V1CompatibilityProfile, type V1KeyMetadata } from "../../crypto/index.js";
3
+ export type WebPasskeyProviderOptions<K> = {
4
+ rpId: string;
5
+ deriveKey: (secret: Uint8Array, salt: Uint8Array) => Promise<K>;
6
+ crypto?: Pick<Crypto, "getRandomValues">;
7
+ navigator?: Navigator;
8
+ secureContext?: () => boolean;
9
+ };
10
+ export declare class WebPasskeyProvider<K> implements KeyProvider<SyncEnvelopeV1, K, V1KeyMetadata> {
11
+ private readonly profile;
12
+ private readonly options;
13
+ private cached;
14
+ private pending;
15
+ constructor(profile: V1CompatibilityProfile, options: WebPasskeyProviderOptions<K>);
16
+ create(): Promise<{
17
+ metadata: V1KeyMetadata;
18
+ key: K;
19
+ }>;
20
+ unlock(envelope: SyncEnvelopeV1): Promise<K>;
21
+ clear(): void;
22
+ supported(): boolean;
23
+ private unlockNow;
24
+ private evaluatePrf;
25
+ private deriveAndZero;
26
+ private assertSupported;
27
+ private crypto;
28
+ private navigator;
29
+ private tryNavigator;
30
+ }
31
+ export declare function createWebPasskeyProvider(profile: V1CompatibilityProfile, options: Omit<WebPasskeyProviderOptions<CryptoKey>, "deriveKey"> & {
32
+ backend?: CryptoBackend<CryptoKey>;
33
+ }): WebPasskeyProvider<CryptoKey>;
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/keys/web-passkey/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAe/B,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACzC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC;CAC/B,CAAC;AAEF,qBAAa,kBAAkB,CAAC,CAAC,CAC/B,YAAW,WAAW,CAAC,cAAc,EAAE,CAAC,EAAE,aAAa,CAAC;IAMtD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,OAAO,CAA0D;gBAGtD,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAGlD,MAAM,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,GAAG,EAAE,CAAC,CAAA;KAAE,CAAC;IAsDtD,MAAM,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBlD,KAAK,IAAI,IAAI;IAKb,SAAS,IAAI,OAAO;YASN,SAAS;YAkBT,WAAW;YA6BX,aAAa;IAW3B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,YAAY;CAMrB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,GAAG;IACjE,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACpC,GACA,kBAAkB,CAAC,SAAS,CAAC,CAO/B"}
@@ -0,0 +1,186 @@
1
+ import { SyncKitError } from "../../core/errors.js";
2
+ import { base64UrlToBytes, bytesToBase64Url, createWebCryptoBackend, deriveContentKey, } from "../../crypto/index.js";
3
+ import { copyBuffer } from "../../crypto/runtime.js";
4
+ export class WebPasskeyProvider {
5
+ profile;
6
+ options;
7
+ cached = null;
8
+ pending = null;
9
+ constructor(profile, options) {
10
+ this.profile = profile;
11
+ this.options = options;
12
+ }
13
+ async create() {
14
+ this.assertSupported();
15
+ const cryptoImplementation = this.crypto();
16
+ const prfInput = randomBytes(this.profile.prfInputBytes, cryptoImplementation);
17
+ const kdfSalt = randomBytes(this.profile.kdfSaltBytes, cryptoImplementation);
18
+ const credential = asPublicKeyCredential(await this.navigator().credentials.create({
19
+ publicKey: {
20
+ rp: {
21
+ id: this.options.rpId,
22
+ name: this.profile.passkey.rpName,
23
+ },
24
+ user: {
25
+ id: copyBuffer(randomBytes(32, cryptoImplementation)),
26
+ name: this.profile.passkey.userName,
27
+ displayName: this.profile.passkey.userDisplayName,
28
+ },
29
+ challenge: copyBuffer(randomBytes(32, cryptoImplementation)),
30
+ pubKeyCredParams: [
31
+ { type: "public-key", alg: this.profile.passkey.algorithm },
32
+ ],
33
+ authenticatorSelection: {
34
+ residentKey: this.profile.passkey.residentKey,
35
+ requireResidentKey: true,
36
+ userVerification: this.profile.passkey.userVerification,
37
+ },
38
+ timeout: this.profile.passkey.timeoutMs,
39
+ attestation: "none",
40
+ extensions: prfExtensions(prfInput),
41
+ },
42
+ }));
43
+ const credentialId = new Uint8Array(credential.rawId);
44
+ const returnedSecret = prfSecret(credential);
45
+ const secret = returnedSecret ??
46
+ (await this.evaluatePrf(credentialId, prfInput, this.options.rpId));
47
+ const key = await this.deriveAndZero(secret, kdfSalt);
48
+ const metadata = {
49
+ credentialId: bytesToBase64Url(credentialId),
50
+ rpId: this.options.rpId,
51
+ prfInput,
52
+ kdfSalt,
53
+ };
54
+ this.cached = { identity: metadataIdentity(metadata), key };
55
+ return { metadata, key };
56
+ }
57
+ async unlock(envelope) {
58
+ if (envelope.rpId !== this.options.rpId) {
59
+ throw new SyncKitError("compatibility", `The snapshot belongs to ${envelope.rpId}, not ${this.options.rpId}.`);
60
+ }
61
+ const identity = envelopeIdentity(envelope);
62
+ if (this.cached?.identity === identity)
63
+ return this.cached.key;
64
+ if (this.cached)
65
+ this.clear();
66
+ if (this.pending?.identity === identity)
67
+ return this.pending.promise;
68
+ const promise = this.unlockNow(envelope, identity);
69
+ this.pending = { identity, promise };
70
+ try {
71
+ return await promise;
72
+ }
73
+ finally {
74
+ if (this.pending?.promise === promise)
75
+ this.pending = null;
76
+ }
77
+ }
78
+ clear() {
79
+ this.cached = null;
80
+ this.pending = null;
81
+ }
82
+ supported() {
83
+ const navigatorImplementation = this.tryNavigator();
84
+ return ((this.options.secureContext?.() ??
85
+ (typeof window !== "undefined" && window.isSecureContext)) &&
86
+ Boolean(navigatorImplementation?.credentials));
87
+ }
88
+ async unlockNow(envelope, identity) {
89
+ this.assertSupported();
90
+ const secret = await this.evaluatePrf(base64UrlToBytes(envelope.credentialId), base64UrlToBytes(envelope.prfInput), envelope.rpId);
91
+ const key = await this.deriveAndZero(secret, base64UrlToBytes(envelope.kdfSalt));
92
+ this.cached = { identity, key };
93
+ return key;
94
+ }
95
+ async evaluatePrf(credentialId, prfInput, rpId) {
96
+ const credential = asPublicKeyCredential(await this.navigator().credentials.get({
97
+ publicKey: {
98
+ challenge: copyBuffer(randomBytes(32, this.crypto())),
99
+ rpId,
100
+ allowCredentials: [
101
+ { type: "public-key", id: copyBuffer(credentialId) },
102
+ ],
103
+ userVerification: this.profile.passkey.userVerification,
104
+ timeout: this.profile.passkey.timeoutMs,
105
+ extensions: prfExtensions(prfInput),
106
+ },
107
+ }));
108
+ const secret = prfSecret(credential);
109
+ if (!secret) {
110
+ throw new SyncKitError("key", "The passkey provider did not return a PRF secret.");
111
+ }
112
+ return secret;
113
+ }
114
+ async deriveAndZero(secret, salt) {
115
+ try {
116
+ return await this.options.deriveKey(secret, salt);
117
+ }
118
+ finally {
119
+ secret.fill(0);
120
+ }
121
+ }
122
+ assertSupported() {
123
+ if (!this.supported()) {
124
+ throw new SyncKitError("key", "Passkeys with PRF support require a secure, compatible runtime.");
125
+ }
126
+ }
127
+ crypto() {
128
+ const implementation = this.options.crypto ?? globalThis.crypto;
129
+ if (!implementation) {
130
+ throw new SyncKitError("configuration", "Crypto randomness is unavailable.");
131
+ }
132
+ return implementation;
133
+ }
134
+ navigator() {
135
+ const implementation = this.tryNavigator();
136
+ if (!implementation) {
137
+ throw new SyncKitError("configuration", "Credential APIs are unavailable.");
138
+ }
139
+ return implementation;
140
+ }
141
+ tryNavigator() {
142
+ return (this.options.navigator ??
143
+ (typeof navigator === "undefined" ? undefined : navigator));
144
+ }
145
+ }
146
+ export function createWebPasskeyProvider(profile, options) {
147
+ const backend = options.backend ?? createWebCryptoBackend();
148
+ return new WebPasskeyProvider(profile, {
149
+ ...options,
150
+ deriveKey: (secret, salt) => deriveContentKey(profile, secret, salt, backend),
151
+ });
152
+ }
153
+ function asPublicKeyCredential(value) {
154
+ if (!value ||
155
+ !("rawId" in value) ||
156
+ typeof value
157
+ .getClientExtensionResults !==
158
+ "function") {
159
+ throw new SyncKitError("key", "Passkey creation or selection was cancelled.");
160
+ }
161
+ return value;
162
+ }
163
+ function prfSecret(credential) {
164
+ const first = credential.getClientExtensionResults().prf
165
+ ?.results?.first;
166
+ return first ? new Uint8Array(first) : null;
167
+ }
168
+ function prfExtensions(prfInput) {
169
+ return {
170
+ prf: { eval: { first: copyBuffer(prfInput) } },
171
+ };
172
+ }
173
+ function randomBytes(length, cryptoImplementation) {
174
+ return cryptoImplementation.getRandomValues(new Uint8Array(length));
175
+ }
176
+ function envelopeIdentity(envelope) {
177
+ return [envelope.rpId, envelope.credentialId, envelope.kdfSalt].join("\n");
178
+ }
179
+ function metadataIdentity(metadata) {
180
+ return [
181
+ metadata.rpId,
182
+ metadata.credentialId,
183
+ bytesToBase64Url(metadata.kdfSalt),
184
+ ].join("\n");
185
+ }
186
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/keys/web-passkey/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AuthorizationProvider, CloudStore, EnvelopeCrypto, KeyProvider, SyncCodec, SyncReason, SyncResult } from "../core/types.js";
2
+ export type SnapshotSyncOptions<T, E, K, M, A> = {
3
+ appId: string;
4
+ codec: SyncCodec<T>;
5
+ envelopeCrypto: EnvelopeCrypto<T, E, K, M>;
6
+ keyProvider: KeyProvider<E, K, M>;
7
+ authorizationProvider: AuthorizationProvider<A>;
8
+ cloudStore: CloudStore<E, A>;
9
+ readLocal(): Promise<T> | T;
10
+ applyMerged(value: T): Promise<void> | void;
11
+ envelopeUpdatedAt(envelope: E): string;
12
+ };
13
+ export interface SnapshotSyncController<T> {
14
+ setup(): Promise<SyncResult<T>>;
15
+ enable(): Promise<SyncResult<T>>;
16
+ sync(reason: SyncReason): Promise<SyncResult<T>>;
17
+ reset(): Promise<SyncResult<T>>;
18
+ delete(): Promise<void>;
19
+ lock(): void;
20
+ operationInProgress(): boolean;
21
+ }
22
+ export declare function createSnapshotSync<T, E, K, M, A>(options: SnapshotSyncOptions<T, E, K, M, A>): SnapshotSyncController<T>;
23
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/snapshot/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,UAAU,EACV,UAAU,EACX,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAChD,UAAU,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5B,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5C,iBAAiB,CAAC,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC;CACxC,CAAC;AAEF,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC;IACb,mBAAmB,IAAI,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9C,OAAO,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC1C,sBAAsB,CAAC,CAAC,CAAC,CAsN3B"}