@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.
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/dist/auth/google-web/index.d.ts +53 -0
- package/dist/auth/google-web/index.d.ts.map +1 -0
- package/dist/auth/google-web/index.js +115 -0
- package/dist/auth/google-web/index.js.map +1 -0
- package/dist/core/errors.d.ts +11 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +19 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +52 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/crypto/base64url.d.ts +3 -0
- package/dist/crypto/base64url.d.ts.map +1 -0
- package/dist/crypto/base64url.js +34 -0
- package/dist/crypto/base64url.js.map +1 -0
- package/dist/crypto/canonical.d.ts +3 -0
- package/dist/crypto/canonical.d.ts.map +1 -0
- package/dist/crypto/canonical.js +30 -0
- package/dist/crypto/canonical.js.map +1 -0
- package/dist/crypto/envelope.d.ts +31 -0
- package/dist/crypto/envelope.d.ts.map +1 -0
- package/dist/crypto/envelope.js +109 -0
- package/dist/crypto/envelope.js.map +1 -0
- package/dist/crypto/index.d.ts +6 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +6 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/profiles.d.ts +73 -0
- package/dist/crypto/profiles.d.ts.map +1 -0
- package/dist/crypto/profiles.js +48 -0
- package/dist/crypto/profiles.js.map +1 -0
- package/dist/crypto/runtime.d.ts +11 -0
- package/dist/crypto/runtime.d.ts.map +1 -0
- package/dist/crypto/runtime.js +57 -0
- package/dist/crypto/runtime.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/keys/web-passkey/index.d.ts +34 -0
- package/dist/keys/web-passkey/index.d.ts.map +1 -0
- package/dist/keys/web-passkey/index.js +186 -0
- package/dist/keys/web-passkey/index.js.map +1 -0
- package/dist/snapshot/controller.d.ts +23 -0
- package/dist/snapshot/controller.d.ts.map +1 -0
- package/dist/snapshot/controller.js +150 -0
- package/dist/snapshot/controller.js.map +1 -0
- package/dist/snapshot/index.d.ts +2 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +2 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/lifecycle.d.ts +11 -0
- package/dist/snapshot/lifecycle.d.ts.map +1 -0
- package/dist/snapshot/lifecycle.js +37 -0
- package/dist/snapshot/lifecycle.js.map +1 -0
- package/dist/stores/google-drive/index.d.ts +43 -0
- package/dist/stores/google-drive/index.d.ts.map +1 -0
- package/dist/stores/google-drive/index.js +136 -0
- package/dist/stores/google-drive/index.js.map +1 -0
- package/fixtures/v1/easybc-web-android-gzip.json +41 -0
- package/fixtures/v1/easybc-web-uncompressed.json +35 -0
- package/fixtures/v1/failures.json +42 -0
- package/fixtures/v1/family-chores-web-uncompressed.json +73 -0
- package/fixtures/v1/profiles.json +51 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
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"}
|