e2ee-client-backend 0.1.1
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 +202 -0
- package/README.md +94 -0
- package/dist/index.d.ts +408 -0
- package/dist/index.js +1026 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1026 @@
|
|
|
1
|
+
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
2
|
+
import { scryptAsync } from '@noble/hashes/scrypt.js';
|
|
3
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
4
|
+
import { utf8ToBytes as utf8ToBytes$1, bytesToHex } from '@noble/hashes/utils.js';
|
|
5
|
+
import Loki from 'lokijs';
|
|
6
|
+
import mlkem from 'mlkem-wasm';
|
|
7
|
+
|
|
8
|
+
// src/adapters/graphql.ts
|
|
9
|
+
var FunctionGraphqlTransport = class {
|
|
10
|
+
constructor(executor) {
|
|
11
|
+
this.executor = executor;
|
|
12
|
+
}
|
|
13
|
+
executor;
|
|
14
|
+
mutate(document, variables) {
|
|
15
|
+
return this.executor(
|
|
16
|
+
variables === void 0 ? {
|
|
17
|
+
document,
|
|
18
|
+
kind: "mutation"
|
|
19
|
+
} : {
|
|
20
|
+
document,
|
|
21
|
+
kind: "mutation",
|
|
22
|
+
variables
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
query(document, variables) {
|
|
27
|
+
return this.executor(
|
|
28
|
+
variables === void 0 ? {
|
|
29
|
+
document,
|
|
30
|
+
kind: "query"
|
|
31
|
+
} : {
|
|
32
|
+
document,
|
|
33
|
+
kind: "query",
|
|
34
|
+
variables
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
function resolveVariables(variables, result) {
|
|
40
|
+
if (!variables) {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
return typeof variables === "function" ? variables(result) : variables;
|
|
44
|
+
}
|
|
45
|
+
var GraphqlCrudAdapter = class {
|
|
46
|
+
constructor(transport, config) {
|
|
47
|
+
this.transport = transport;
|
|
48
|
+
this.config = config;
|
|
49
|
+
}
|
|
50
|
+
transport;
|
|
51
|
+
config;
|
|
52
|
+
async create(input) {
|
|
53
|
+
const config = this.config.create;
|
|
54
|
+
if (!config) {
|
|
55
|
+
throw new Error("This GraphQL adapter does not implement create().");
|
|
56
|
+
}
|
|
57
|
+
const result = await this.transport.mutate(
|
|
58
|
+
config.document,
|
|
59
|
+
config.buildVariables(input)
|
|
60
|
+
);
|
|
61
|
+
return config.select(result);
|
|
62
|
+
}
|
|
63
|
+
async delete(id) {
|
|
64
|
+
const config = this.config.delete;
|
|
65
|
+
if (!config) {
|
|
66
|
+
throw new Error("This GraphQL adapter does not implement delete().");
|
|
67
|
+
}
|
|
68
|
+
await this.transport.mutate(config.document, config.buildVariables(id));
|
|
69
|
+
}
|
|
70
|
+
async getById(id) {
|
|
71
|
+
const config = this.config.getById;
|
|
72
|
+
if (!config) {
|
|
73
|
+
throw new Error("This GraphQL adapter does not implement getById().");
|
|
74
|
+
}
|
|
75
|
+
const result = await this.transport.query(
|
|
76
|
+
config.document,
|
|
77
|
+
config.buildVariables(id)
|
|
78
|
+
);
|
|
79
|
+
return config.select(result);
|
|
80
|
+
}
|
|
81
|
+
async list() {
|
|
82
|
+
const config = this.config.list;
|
|
83
|
+
if (!config) {
|
|
84
|
+
throw new Error("This GraphQL adapter does not implement list().");
|
|
85
|
+
}
|
|
86
|
+
const result = await this.transport.query(
|
|
87
|
+
config.document,
|
|
88
|
+
resolveVariables(config.variables)
|
|
89
|
+
);
|
|
90
|
+
return config.select(result);
|
|
91
|
+
}
|
|
92
|
+
async update(id, input) {
|
|
93
|
+
const config = this.config.update;
|
|
94
|
+
if (!config) {
|
|
95
|
+
throw new Error("This GraphQL adapter does not implement update().");
|
|
96
|
+
}
|
|
97
|
+
const result = await this.transport.mutate(
|
|
98
|
+
config.document,
|
|
99
|
+
config.buildVariables(id, input)
|
|
100
|
+
);
|
|
101
|
+
return config.select(result);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
function createGraphqlTransport(executor) {
|
|
105
|
+
return new FunctionGraphqlTransport(executor);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/adapters/rest.ts
|
|
109
|
+
function identity(value) {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
function normalizeBaseUrl(baseUrl) {
|
|
113
|
+
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
114
|
+
}
|
|
115
|
+
function resolvePath(path) {
|
|
116
|
+
return typeof path === "function" ? path() : path;
|
|
117
|
+
}
|
|
118
|
+
function withBody(method, path, body) {
|
|
119
|
+
if (body === void 0) {
|
|
120
|
+
return {
|
|
121
|
+
method,
|
|
122
|
+
path
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
body,
|
|
127
|
+
method,
|
|
128
|
+
path
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
var FetchRestTransport = class {
|
|
132
|
+
constructor(options) {
|
|
133
|
+
this.options = options;
|
|
134
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
135
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
136
|
+
}
|
|
137
|
+
options;
|
|
138
|
+
baseUrl;
|
|
139
|
+
fetchImpl;
|
|
140
|
+
async request(request) {
|
|
141
|
+
const url = new URL(request.path.replace(/^\//, ""), this.baseUrl);
|
|
142
|
+
if (request.query) {
|
|
143
|
+
for (const [key, value] of Object.entries(request.query)) {
|
|
144
|
+
if (value === void 0 || value === null) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
url.searchParams.set(key, String(value));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const headers = new Headers(this.options.defaultHeaders);
|
|
151
|
+
if (request.headers) {
|
|
152
|
+
for (const [key, value] of Object.entries(request.headers)) {
|
|
153
|
+
headers.set(key, value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const init = {
|
|
157
|
+
method: request.method,
|
|
158
|
+
headers
|
|
159
|
+
};
|
|
160
|
+
if (request.body !== void 0) {
|
|
161
|
+
if (!headers.has("Content-Type")) {
|
|
162
|
+
headers.set("Content-Type", "application/json");
|
|
163
|
+
}
|
|
164
|
+
init.body = JSON.stringify(request.body);
|
|
165
|
+
}
|
|
166
|
+
const response = await this.fetchImpl(url, init);
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error(`REST request failed with ${response.status}.`);
|
|
169
|
+
}
|
|
170
|
+
if (response.status === 204) {
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
174
|
+
if (contentType.includes("application/json")) {
|
|
175
|
+
return await response.json();
|
|
176
|
+
}
|
|
177
|
+
return await response.text();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
var RestCrudAdapter = class {
|
|
181
|
+
constructor(transport, config) {
|
|
182
|
+
this.transport = transport;
|
|
183
|
+
this.config = config;
|
|
184
|
+
}
|
|
185
|
+
transport;
|
|
186
|
+
config;
|
|
187
|
+
async create(input) {
|
|
188
|
+
const config = this.config.create;
|
|
189
|
+
if (!config) {
|
|
190
|
+
throw new Error("This REST adapter does not implement create().");
|
|
191
|
+
}
|
|
192
|
+
const result = await this.transport.request({
|
|
193
|
+
...withBody(
|
|
194
|
+
"POST",
|
|
195
|
+
resolvePath(config.path),
|
|
196
|
+
(config.serialize ?? identity)(input)
|
|
197
|
+
)
|
|
198
|
+
});
|
|
199
|
+
return (config.select ?? identity)(result);
|
|
200
|
+
}
|
|
201
|
+
async delete(id) {
|
|
202
|
+
const config = this.config.delete;
|
|
203
|
+
if (!config) {
|
|
204
|
+
throw new Error("This REST adapter does not implement delete().");
|
|
205
|
+
}
|
|
206
|
+
await this.transport.request({
|
|
207
|
+
method: "DELETE",
|
|
208
|
+
path: config.path(id)
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async getById(id) {
|
|
212
|
+
const config = this.config.getById;
|
|
213
|
+
if (!config) {
|
|
214
|
+
throw new Error("This REST adapter does not implement getById().");
|
|
215
|
+
}
|
|
216
|
+
const result = await this.transport.request({
|
|
217
|
+
method: "GET",
|
|
218
|
+
path: config.path(id)
|
|
219
|
+
});
|
|
220
|
+
return (config.select ?? identity)(result);
|
|
221
|
+
}
|
|
222
|
+
async list() {
|
|
223
|
+
const config = this.config.list;
|
|
224
|
+
if (!config) {
|
|
225
|
+
throw new Error("This REST adapter does not implement list().");
|
|
226
|
+
}
|
|
227
|
+
const result = await this.transport.request({
|
|
228
|
+
method: "GET",
|
|
229
|
+
path: resolvePath(config.path),
|
|
230
|
+
...config.query ? { query: config.query } : {}
|
|
231
|
+
});
|
|
232
|
+
return (config.select ?? identity)(result);
|
|
233
|
+
}
|
|
234
|
+
async update(id, input) {
|
|
235
|
+
const config = this.config.update;
|
|
236
|
+
if (!config) {
|
|
237
|
+
throw new Error("This REST adapter does not implement update().");
|
|
238
|
+
}
|
|
239
|
+
const result = await this.transport.request({
|
|
240
|
+
...withBody(
|
|
241
|
+
"PUT",
|
|
242
|
+
config.path(id),
|
|
243
|
+
(config.serialize ?? identity)(input)
|
|
244
|
+
)
|
|
245
|
+
});
|
|
246
|
+
return (config.select ?? identity)(result);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
function createFetchRestTransport(options) {
|
|
250
|
+
return new FetchRestTransport(options);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/encoding/base64.ts
|
|
254
|
+
var encoder = new TextEncoder();
|
|
255
|
+
var decoder = new TextDecoder();
|
|
256
|
+
function toUint8Array(value) {
|
|
257
|
+
if (value instanceof Uint8Array) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
if (ArrayBuffer.isView(value)) {
|
|
261
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
262
|
+
}
|
|
263
|
+
return new Uint8Array(value);
|
|
264
|
+
}
|
|
265
|
+
function toArrayBuffer(value) {
|
|
266
|
+
const bytes = toUint8Array(value);
|
|
267
|
+
return bytes.buffer.slice(
|
|
268
|
+
bytes.byteOffset,
|
|
269
|
+
bytes.byteOffset + bytes.byteLength
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
function bytesToBase64(value) {
|
|
273
|
+
const bytes = toUint8Array(value);
|
|
274
|
+
if (typeof Buffer !== "undefined") {
|
|
275
|
+
return Buffer.from(bytes).toString("base64");
|
|
276
|
+
}
|
|
277
|
+
let binary = "";
|
|
278
|
+
for (const byte of bytes) {
|
|
279
|
+
binary += String.fromCodePoint(byte);
|
|
280
|
+
}
|
|
281
|
+
return btoa(binary);
|
|
282
|
+
}
|
|
283
|
+
function base64ToBytes(base64) {
|
|
284
|
+
if (typeof Buffer !== "undefined") {
|
|
285
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
286
|
+
}
|
|
287
|
+
const binary = atob(base64);
|
|
288
|
+
const bytes = new Uint8Array(binary.length);
|
|
289
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
290
|
+
bytes[index] = binary.codePointAt(index) ?? 0;
|
|
291
|
+
}
|
|
292
|
+
return bytes;
|
|
293
|
+
}
|
|
294
|
+
function utf8ToBytes(value) {
|
|
295
|
+
return encoder.encode(value);
|
|
296
|
+
}
|
|
297
|
+
function bytesToUtf8(value) {
|
|
298
|
+
return decoder.decode(toUint8Array(value));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/crypto/key-derivation.ts
|
|
302
|
+
var AUTH_INFO = utf8ToBytes$1("dashboard.auth.v1");
|
|
303
|
+
var DEFAULT_SCRYPT_PARAMS = {
|
|
304
|
+
N: 1 << 15,
|
|
305
|
+
r: 8,
|
|
306
|
+
p: 1,
|
|
307
|
+
dkLen: 32,
|
|
308
|
+
maxmem: 128 * 1024 * 1024
|
|
309
|
+
};
|
|
310
|
+
function kdfSaltFromBase64(kdfSaltBase64) {
|
|
311
|
+
return base64ToBytes(kdfSaltBase64.trim());
|
|
312
|
+
}
|
|
313
|
+
async function deriveAes256KeyFromPassword(password, salt) {
|
|
314
|
+
return scryptAsync(utf8ToBytes$1(password), salt, DEFAULT_SCRYPT_PARAMS);
|
|
315
|
+
}
|
|
316
|
+
function deriveAuthKeyMaterial(kEnc) {
|
|
317
|
+
return hkdf(sha256, kEnc, new Uint8Array(0), AUTH_INFO, 32);
|
|
318
|
+
}
|
|
319
|
+
async function deriveClientKeyMaterial(password, kdfSaltBase64) {
|
|
320
|
+
const salt = kdfSaltFromBase64(kdfSaltBase64);
|
|
321
|
+
const kEnc = await deriveAes256KeyFromPassword(password, salt);
|
|
322
|
+
return {
|
|
323
|
+
kAuthHex: bytesToHex(deriveAuthKeyMaterial(kEnc)),
|
|
324
|
+
kEnc
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
async function authKeyMaterialHex(password, kdfSaltBase64) {
|
|
328
|
+
const { kAuthHex } = await deriveClientKeyMaterial(password, kdfSaltBase64);
|
|
329
|
+
return kAuthHex;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/auth/password-auth-client.ts
|
|
333
|
+
function normalizeAuthEmail(email) {
|
|
334
|
+
return email.trim().toLowerCase();
|
|
335
|
+
}
|
|
336
|
+
var PasswordAuthClient = class {
|
|
337
|
+
constructor(adapter) {
|
|
338
|
+
this.adapter = adapter;
|
|
339
|
+
}
|
|
340
|
+
adapter;
|
|
341
|
+
async beginRegistration(email) {
|
|
342
|
+
const response = await this.adapter.registerBegin(normalizeAuthEmail(email));
|
|
343
|
+
return response.kdfSaltBase64;
|
|
344
|
+
}
|
|
345
|
+
async completeRegistrationWithPassword(email, password, kdfSaltBase64) {
|
|
346
|
+
const normalizedEmail = normalizeAuthEmail(email);
|
|
347
|
+
const { kAuthHex, kEnc } = await deriveClientKeyMaterial(
|
|
348
|
+
password,
|
|
349
|
+
kdfSaltBase64
|
|
350
|
+
);
|
|
351
|
+
return {
|
|
352
|
+
kAuthHex,
|
|
353
|
+
kEnc,
|
|
354
|
+
normalizedEmail,
|
|
355
|
+
result: await this.adapter.registerComplete(normalizedEmail, kAuthHex)
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
async loginWithPassword(email, password) {
|
|
359
|
+
const normalizedEmail = normalizeAuthEmail(email);
|
|
360
|
+
const kdfSaltBase64 = await this.adapter.getKdfSalt(normalizedEmail);
|
|
361
|
+
const { kAuthHex, kEnc } = await deriveClientKeyMaterial(
|
|
362
|
+
password,
|
|
363
|
+
kdfSaltBase64
|
|
364
|
+
);
|
|
365
|
+
return {
|
|
366
|
+
kAuthHex,
|
|
367
|
+
kEnc,
|
|
368
|
+
normalizedEmail,
|
|
369
|
+
result: await this.adapter.login(normalizedEmail, kAuthHex)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
logout() {
|
|
373
|
+
return this.adapter.logout();
|
|
374
|
+
}
|
|
375
|
+
refreshSession() {
|
|
376
|
+
return this.adapter.refresh();
|
|
377
|
+
}
|
|
378
|
+
async registerWithPassword(email, password) {
|
|
379
|
+
const kdfSaltBase64 = await this.beginRegistration(email);
|
|
380
|
+
return this.completeRegistrationWithPassword(email, password, kdfSaltBase64);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
function createPasswordAuthClient(adapter) {
|
|
384
|
+
return new PasswordAuthClient(adapter);
|
|
385
|
+
}
|
|
386
|
+
function cloneValue(value) {
|
|
387
|
+
if (value === null || value === void 0) {
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
if (typeof value !== "object") {
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
return structuredClone(value);
|
|
394
|
+
}
|
|
395
|
+
var LokiCacheStore = class {
|
|
396
|
+
collections = /* @__PURE__ */ new Map();
|
|
397
|
+
db = new Loki("e2ee-client-backend", {
|
|
398
|
+
autoload: false,
|
|
399
|
+
autosave: false,
|
|
400
|
+
persistenceMethod: "memory"
|
|
401
|
+
});
|
|
402
|
+
clear() {
|
|
403
|
+
for (const name of this.collections.keys()) {
|
|
404
|
+
this.clearCollection(name);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
clearCollection(collectionName) {
|
|
408
|
+
this.collections.delete(collectionName);
|
|
409
|
+
if (this.db.getCollection(collectionName)) {
|
|
410
|
+
this.db.removeCollection(collectionName);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
get(collectionName, id) {
|
|
414
|
+
const collection = this.ensureCollection(collectionName);
|
|
415
|
+
const entry = collection.findOne({ id: String(id) });
|
|
416
|
+
return entry ? cloneValue(entry.value) : null;
|
|
417
|
+
}
|
|
418
|
+
list(collectionName) {
|
|
419
|
+
const collection = this.ensureCollection(collectionName);
|
|
420
|
+
return collection.chain().simplesort("updatedAt").data().map((entry) => cloneValue(entry.value));
|
|
421
|
+
}
|
|
422
|
+
put(collectionName, id, value) {
|
|
423
|
+
const collection = this.ensureCollection(collectionName);
|
|
424
|
+
const normalizedId = String(id);
|
|
425
|
+
const existing = collection.findOne({ id: normalizedId });
|
|
426
|
+
if (existing) {
|
|
427
|
+
existing.updatedAt = Date.now();
|
|
428
|
+
existing.value = cloneValue(value);
|
|
429
|
+
collection.update(existing);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
collection.insert({
|
|
433
|
+
id: normalizedId,
|
|
434
|
+
updatedAt: Date.now(),
|
|
435
|
+
value: cloneValue(value)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
remove(collectionName, id) {
|
|
439
|
+
const collection = this.ensureCollection(collectionName);
|
|
440
|
+
const existing = collection.findOne({ id: String(id) });
|
|
441
|
+
if (existing) {
|
|
442
|
+
collection.remove(existing);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
ensureCollection(collectionName) {
|
|
446
|
+
const existing = this.collections.get(collectionName);
|
|
447
|
+
if (existing) {
|
|
448
|
+
return existing;
|
|
449
|
+
}
|
|
450
|
+
const created = this.db.addCollection(collectionName, {
|
|
451
|
+
indices: ["updatedAt"],
|
|
452
|
+
unique: ["id"]
|
|
453
|
+
});
|
|
454
|
+
this.collections.set(collectionName, created);
|
|
455
|
+
return created;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
function createLokiCacheStore() {
|
|
459
|
+
return new LokiCacheStore();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/crypto/aes-gcm-strategy.ts
|
|
463
|
+
var Aes256GcmStrategy = class {
|
|
464
|
+
id = "aes-256-gcm";
|
|
465
|
+
nonceLength = 12;
|
|
466
|
+
async encrypt(plaintext, context) {
|
|
467
|
+
const key = await this.importKey(context.key);
|
|
468
|
+
const nonce = crypto.getRandomValues(new Uint8Array(this.nonceLength));
|
|
469
|
+
const params = {
|
|
470
|
+
iv: toArrayBuffer(nonce),
|
|
471
|
+
name: "AES-GCM"
|
|
472
|
+
};
|
|
473
|
+
if (context.additionalData) {
|
|
474
|
+
params.additionalData = toArrayBuffer(context.additionalData);
|
|
475
|
+
}
|
|
476
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
477
|
+
params,
|
|
478
|
+
key,
|
|
479
|
+
toArrayBuffer(plaintext)
|
|
480
|
+
);
|
|
481
|
+
return {
|
|
482
|
+
version: 1,
|
|
483
|
+
algorithm: this.id,
|
|
484
|
+
ciphertextBase64: bytesToBase64(ciphertext),
|
|
485
|
+
nonceBase64: bytesToBase64(nonce)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
async decrypt(payload, context) {
|
|
489
|
+
if (!payload.nonceBase64) {
|
|
490
|
+
throw new Error("AES-GCM payload is missing nonceBase64.");
|
|
491
|
+
}
|
|
492
|
+
const key = await this.importKey(context.key);
|
|
493
|
+
const params = {
|
|
494
|
+
iv: toArrayBuffer(base64ToBytes(payload.nonceBase64)),
|
|
495
|
+
name: "AES-GCM"
|
|
496
|
+
};
|
|
497
|
+
if (context.additionalData) {
|
|
498
|
+
params.additionalData = toArrayBuffer(context.additionalData);
|
|
499
|
+
}
|
|
500
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
501
|
+
params,
|
|
502
|
+
key,
|
|
503
|
+
toArrayBuffer(base64ToBytes(payload.ciphertextBase64))
|
|
504
|
+
);
|
|
505
|
+
return new Uint8Array(plaintext);
|
|
506
|
+
}
|
|
507
|
+
async importKey(keyBytes) {
|
|
508
|
+
if (keyBytes.length !== 32) {
|
|
509
|
+
throw new Error("AES-256-GCM expects a 32-byte key.");
|
|
510
|
+
}
|
|
511
|
+
return crypto.subtle.importKey(
|
|
512
|
+
"raw",
|
|
513
|
+
toArrayBuffer(keyBytes),
|
|
514
|
+
"AES-GCM",
|
|
515
|
+
false,
|
|
516
|
+
["encrypt", "decrypt"]
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
function createAes256GcmStrategy() {
|
|
521
|
+
return new Aes256GcmStrategy();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/compat/legacy-json-blob.ts
|
|
525
|
+
var legacyAesStrategy = createAes256GcmStrategy();
|
|
526
|
+
async function decryptJsonFromLegacyBlob(blob, key) {
|
|
527
|
+
const plaintext = await legacyAesStrategy.decrypt(
|
|
528
|
+
legacyBlobToEncryptedField(blob),
|
|
529
|
+
{ key }
|
|
530
|
+
);
|
|
531
|
+
return JSON.parse(bytesToUtf8(plaintext));
|
|
532
|
+
}
|
|
533
|
+
function encryptedFieldToLegacyBlob(payload) {
|
|
534
|
+
if (!payload.nonceBase64) {
|
|
535
|
+
throw new Error("Legacy blob conversion requires nonceBase64.");
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
ciphertextBase64: payload.ciphertextBase64,
|
|
539
|
+
nonceBase64: payload.nonceBase64
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
async function encryptJsonToLegacyBlob(value, key) {
|
|
543
|
+
const payload = await legacyAesStrategy.encrypt(
|
|
544
|
+
utf8ToBytes(JSON.stringify(value)),
|
|
545
|
+
{ key }
|
|
546
|
+
);
|
|
547
|
+
return encryptedFieldToLegacyBlob(payload);
|
|
548
|
+
}
|
|
549
|
+
function legacyBlobToEncryptedField(blob, algorithm = "aes-256-gcm") {
|
|
550
|
+
return {
|
|
551
|
+
algorithm,
|
|
552
|
+
ciphertextBase64: blob.ciphertextBase64,
|
|
553
|
+
nonceBase64: blob.nonceBase64,
|
|
554
|
+
version: 1
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
var ML_KEM_ALGORITHM = { name: "ML-KEM-768" };
|
|
558
|
+
var SHARED_KEY_ALGORITHM = { name: "AES-GCM", length: 256 };
|
|
559
|
+
async function importPublicKey(publicKey) {
|
|
560
|
+
return mlkem.importKey(
|
|
561
|
+
"raw-public",
|
|
562
|
+
toArrayBuffer(publicKey),
|
|
563
|
+
ML_KEM_ALGORITHM,
|
|
564
|
+
false,
|
|
565
|
+
["encapsulateKey"]
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
async function importPrivateKey(privateKeySeed) {
|
|
569
|
+
return mlkem.importKey(
|
|
570
|
+
"raw-seed",
|
|
571
|
+
toArrayBuffer(privateKeySeed),
|
|
572
|
+
ML_KEM_ALGORITHM,
|
|
573
|
+
false,
|
|
574
|
+
["decapsulateKey"]
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
async function generateMlKemKeyPair() {
|
|
578
|
+
const { privateKey, publicKey } = await mlkem.generateKey(ML_KEM_ALGORITHM, true, [
|
|
579
|
+
"encapsulateKey",
|
|
580
|
+
"decapsulateKey"
|
|
581
|
+
]);
|
|
582
|
+
const exportedPublicKey = await mlkem.exportKey("raw-public", publicKey);
|
|
583
|
+
const exportedPrivateKeySeed = await mlkem.exportKey("raw-seed", privateKey);
|
|
584
|
+
return {
|
|
585
|
+
publicKey: new Uint8Array(exportedPublicKey),
|
|
586
|
+
privateKeySeed: new Uint8Array(exportedPrivateKeySeed)
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
var MlKemAes256GcmStrategy = class {
|
|
590
|
+
id = "ml-kem-768-aes-256-gcm";
|
|
591
|
+
nonceLength = 12;
|
|
592
|
+
async encrypt(plaintext, context) {
|
|
593
|
+
const recipientPublicKey = await importPublicKey(context.recipientPublicKey);
|
|
594
|
+
const encapsulated = await mlkem.encapsulateKey(
|
|
595
|
+
ML_KEM_ALGORITHM,
|
|
596
|
+
recipientPublicKey,
|
|
597
|
+
SHARED_KEY_ALGORITHM,
|
|
598
|
+
false,
|
|
599
|
+
["encrypt", "decrypt"]
|
|
600
|
+
);
|
|
601
|
+
const nonce = crypto.getRandomValues(new Uint8Array(this.nonceLength));
|
|
602
|
+
const params = {
|
|
603
|
+
iv: toArrayBuffer(nonce),
|
|
604
|
+
name: "AES-GCM"
|
|
605
|
+
};
|
|
606
|
+
if (context.additionalData) {
|
|
607
|
+
params.additionalData = toArrayBuffer(context.additionalData);
|
|
608
|
+
}
|
|
609
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
610
|
+
params,
|
|
611
|
+
encapsulated.sharedKey,
|
|
612
|
+
toArrayBuffer(plaintext)
|
|
613
|
+
);
|
|
614
|
+
return {
|
|
615
|
+
version: 1,
|
|
616
|
+
algorithm: this.id,
|
|
617
|
+
ciphertextBase64: bytesToBase64(ciphertext),
|
|
618
|
+
nonceBase64: bytesToBase64(nonce),
|
|
619
|
+
encapsulatedKeyCiphertextBase64: bytesToBase64(encapsulated.ciphertext),
|
|
620
|
+
metadata: {
|
|
621
|
+
kem: "ML-KEM-768"
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
async decrypt(payload, context) {
|
|
626
|
+
if (!payload.nonceBase64) {
|
|
627
|
+
throw new Error("ML-KEM AES payload is missing nonceBase64.");
|
|
628
|
+
}
|
|
629
|
+
if (!payload.encapsulatedKeyCiphertextBase64) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
"ML-KEM AES payload is missing encapsulatedKeyCiphertextBase64."
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
const recipientPrivateKey = await importPrivateKey(context.recipientPrivateKey);
|
|
635
|
+
const sharedKey = await mlkem.decapsulateKey(
|
|
636
|
+
ML_KEM_ALGORITHM,
|
|
637
|
+
recipientPrivateKey,
|
|
638
|
+
toArrayBuffer(base64ToBytes(payload.encapsulatedKeyCiphertextBase64)),
|
|
639
|
+
SHARED_KEY_ALGORITHM,
|
|
640
|
+
false,
|
|
641
|
+
["decrypt"]
|
|
642
|
+
);
|
|
643
|
+
const params = {
|
|
644
|
+
iv: toArrayBuffer(base64ToBytes(payload.nonceBase64)),
|
|
645
|
+
name: "AES-GCM"
|
|
646
|
+
};
|
|
647
|
+
if (context.additionalData) {
|
|
648
|
+
params.additionalData = toArrayBuffer(context.additionalData);
|
|
649
|
+
}
|
|
650
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
651
|
+
params,
|
|
652
|
+
sharedKey,
|
|
653
|
+
toArrayBuffer(base64ToBytes(payload.ciphertextBase64))
|
|
654
|
+
);
|
|
655
|
+
return new Uint8Array(plaintext);
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
function createMlKemAes256GcmStrategy() {
|
|
659
|
+
return new MlKemAes256GcmStrategy();
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/crypto/strategy-registry.ts
|
|
663
|
+
var StrategyRegistry = class {
|
|
664
|
+
strategies = /* @__PURE__ */ new Map();
|
|
665
|
+
constructor(strategies = []) {
|
|
666
|
+
for (const strategy of strategies) {
|
|
667
|
+
this.register(strategy);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
register(strategy) {
|
|
671
|
+
this.strategies.set(strategy.id, strategy);
|
|
672
|
+
}
|
|
673
|
+
get(id) {
|
|
674
|
+
const strategy = this.strategies.get(id);
|
|
675
|
+
if (!strategy) {
|
|
676
|
+
throw new Error(`No encryption strategy registered for "${id}".`);
|
|
677
|
+
}
|
|
678
|
+
return strategy;
|
|
679
|
+
}
|
|
680
|
+
encrypt(id, plaintext, context) {
|
|
681
|
+
return this.get(id).encrypt(plaintext, context);
|
|
682
|
+
}
|
|
683
|
+
decrypt(payload, context) {
|
|
684
|
+
return this.get(payload.algorithm).decrypt(
|
|
685
|
+
payload,
|
|
686
|
+
context
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
function createStrategyRegistry(...strategies) {
|
|
691
|
+
return new StrategyRegistry(strategies);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/crypto/types.ts
|
|
695
|
+
function isEncryptedFieldValue(value) {
|
|
696
|
+
if (!value || typeof value !== "object") {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
const candidate = value;
|
|
700
|
+
return candidate.version === 1 && typeof candidate.algorithm === "string" && typeof candidate.ciphertextBase64 === "string";
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/external-e2ee/client.ts
|
|
704
|
+
var ExternalE2eeApiClient = class {
|
|
705
|
+
constructor(provider) {
|
|
706
|
+
this.provider = provider;
|
|
707
|
+
}
|
|
708
|
+
provider;
|
|
709
|
+
authenticate(config) {
|
|
710
|
+
return this.provider.authenticate(config);
|
|
711
|
+
}
|
|
712
|
+
async ensureAccess(config) {
|
|
713
|
+
const session = await this.provider.authenticate(config);
|
|
714
|
+
if (this.provider.validateAccess) {
|
|
715
|
+
await this.provider.validateAccess(config, session);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
async listProjects(config, query) {
|
|
719
|
+
const session = await this.provider.authenticate(config);
|
|
720
|
+
return this.provider.listProjects(config, session, query);
|
|
721
|
+
}
|
|
722
|
+
async listTasks(config, query) {
|
|
723
|
+
const session = await this.provider.authenticate(config);
|
|
724
|
+
return this.provider.listTasks(config, session, query);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
function createExternalE2eeApiClient(provider) {
|
|
728
|
+
return new ExternalE2eeApiClient(provider);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/repositories/entity-repository.ts
|
|
732
|
+
function cloneValue2(value) {
|
|
733
|
+
if (value === null || value === void 0) {
|
|
734
|
+
return value;
|
|
735
|
+
}
|
|
736
|
+
if (typeof value !== "object") {
|
|
737
|
+
return value;
|
|
738
|
+
}
|
|
739
|
+
return structuredClone(value);
|
|
740
|
+
}
|
|
741
|
+
function getByPath(target, path) {
|
|
742
|
+
return path.split(".").reduce((current, segment) => {
|
|
743
|
+
if (!current || typeof current !== "object") {
|
|
744
|
+
return void 0;
|
|
745
|
+
}
|
|
746
|
+
return current[segment];
|
|
747
|
+
}, target);
|
|
748
|
+
}
|
|
749
|
+
function setByPath(target, path, value) {
|
|
750
|
+
const segments = path.split(".");
|
|
751
|
+
const lastSegment = segments.at(-1);
|
|
752
|
+
if (!lastSegment) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
let current = target;
|
|
756
|
+
for (const segment of segments.slice(0, -1)) {
|
|
757
|
+
const existing = current[segment];
|
|
758
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
759
|
+
current[segment] = {};
|
|
760
|
+
}
|
|
761
|
+
current = current[segment];
|
|
762
|
+
}
|
|
763
|
+
current[lastSegment] = value;
|
|
764
|
+
}
|
|
765
|
+
function deleteByPath(target, path) {
|
|
766
|
+
const segments = path.split(".");
|
|
767
|
+
const lastSegment = segments.at(-1);
|
|
768
|
+
if (!lastSegment) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
let current = target;
|
|
772
|
+
for (const segment of segments.slice(0, -1)) {
|
|
773
|
+
const next = current[segment];
|
|
774
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
current = next;
|
|
778
|
+
}
|
|
779
|
+
delete current[lastSegment];
|
|
780
|
+
}
|
|
781
|
+
function stringifyValue(value) {
|
|
782
|
+
return utf8ToBytes(JSON.stringify(value));
|
|
783
|
+
}
|
|
784
|
+
function parseValue(bytes) {
|
|
785
|
+
return JSON.parse(bytesToUtf8(bytes));
|
|
786
|
+
}
|
|
787
|
+
var EntityRepository = class {
|
|
788
|
+
constructor(options) {
|
|
789
|
+
this.options = options;
|
|
790
|
+
this.cacheCollection = options.schema.cacheCollection ?? options.schema.name;
|
|
791
|
+
this.idPath = options.schema.idPath ?? "id";
|
|
792
|
+
}
|
|
793
|
+
options;
|
|
794
|
+
cacheCollection;
|
|
795
|
+
idPath;
|
|
796
|
+
async create(entity) {
|
|
797
|
+
const remote = await this.serializeEntity(entity);
|
|
798
|
+
const created = await this.options.adapter.create(remote);
|
|
799
|
+
return this.hydrateRemote(created, true);
|
|
800
|
+
}
|
|
801
|
+
async delete(id) {
|
|
802
|
+
await this.options.adapter.delete(id);
|
|
803
|
+
this.options.cache?.remove(this.cacheCollection, id);
|
|
804
|
+
}
|
|
805
|
+
async getById(id, options = {}) {
|
|
806
|
+
const cacheMode = options.cacheMode ?? "network-first";
|
|
807
|
+
if (cacheMode === "cache-first") {
|
|
808
|
+
const cached = this.options.cache?.get(this.cacheCollection, id) ?? null;
|
|
809
|
+
if (cached) {
|
|
810
|
+
return cached;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (cacheMode === "no-cache") {
|
|
814
|
+
const remote2 = await this.options.adapter.getById(id);
|
|
815
|
+
return remote2 ? this.hydrateRemote(remote2, false) : null;
|
|
816
|
+
}
|
|
817
|
+
const remote = await this.options.adapter.getById(id);
|
|
818
|
+
return remote ? this.hydrateRemote(remote, true) : null;
|
|
819
|
+
}
|
|
820
|
+
async list(options = {}) {
|
|
821
|
+
const cacheMode = options.cacheMode ?? "network-first";
|
|
822
|
+
if (cacheMode === "cache-first") {
|
|
823
|
+
const cached = this.options.cache?.list(this.cacheCollection) ?? [];
|
|
824
|
+
if (cached.length > 0) {
|
|
825
|
+
return cached;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
const remoteItems = await this.options.adapter.list();
|
|
829
|
+
const items = await Promise.all(
|
|
830
|
+
remoteItems.map((remote) => this.hydrateRemote(remote, cacheMode !== "no-cache"))
|
|
831
|
+
);
|
|
832
|
+
if (cacheMode !== "no-cache" && this.options.cache) {
|
|
833
|
+
this.options.cache.clearCollection(this.cacheCollection);
|
|
834
|
+
for (const entity of items) {
|
|
835
|
+
this.options.cache.put(
|
|
836
|
+
this.cacheCollection,
|
|
837
|
+
this.resolveEntityId(entity),
|
|
838
|
+
entity
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return items;
|
|
843
|
+
}
|
|
844
|
+
async update(id, entity) {
|
|
845
|
+
const remote = await this.serializeEntity(entity);
|
|
846
|
+
const updated = await this.options.adapter.update(id, remote);
|
|
847
|
+
return this.hydrateRemote(updated, true);
|
|
848
|
+
}
|
|
849
|
+
async hydrateRemote(remote, storeInCache) {
|
|
850
|
+
const workingRemote = cloneValue2(remote);
|
|
851
|
+
for (const field of this.options.schema.fields) {
|
|
852
|
+
await this.hydrateField(remote, workingRemote, field);
|
|
853
|
+
}
|
|
854
|
+
const entity = this.options.schema.createEntity(workingRemote);
|
|
855
|
+
if (storeInCache && this.options.cache) {
|
|
856
|
+
this.options.cache.put(
|
|
857
|
+
this.cacheCollection,
|
|
858
|
+
this.resolveEntityId(entity),
|
|
859
|
+
entity
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
return entity;
|
|
863
|
+
}
|
|
864
|
+
resolveEntityId(entity) {
|
|
865
|
+
return getByPath(entity, this.idPath);
|
|
866
|
+
}
|
|
867
|
+
async hydrateField(remote, workingRemote, field) {
|
|
868
|
+
const remotePath = field.remotePath ?? field.entityPath;
|
|
869
|
+
const currentValue = getByPath(workingRemote, remotePath);
|
|
870
|
+
if (field.encrypted) {
|
|
871
|
+
if (currentValue === null || currentValue === void 0) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (!isEncryptedFieldValue(currentValue)) {
|
|
875
|
+
throw new Error(
|
|
876
|
+
`Field "${remotePath}" on entity "${this.options.schema.name}" is not an encrypted payload.`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
const context = await this.options.contextResolver.resolve({
|
|
880
|
+
field,
|
|
881
|
+
payload: currentValue,
|
|
882
|
+
phase: "decrypt",
|
|
883
|
+
remote,
|
|
884
|
+
schema: this.options.schema
|
|
885
|
+
});
|
|
886
|
+
const plaintext = await this.options.strategies.decrypt(currentValue, context);
|
|
887
|
+
const parsed = parseValue(plaintext);
|
|
888
|
+
const nextValue = field.deserialize ? field.deserialize(parsed, remote) : parsed;
|
|
889
|
+
setByPath(workingRemote, remotePath, nextValue);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (field.deserialize) {
|
|
893
|
+
setByPath(workingRemote, remotePath, field.deserialize(currentValue, remote));
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
async serializeEntity(entity) {
|
|
897
|
+
const remote = cloneValue2(this.options.schema.createRemote(entity));
|
|
898
|
+
for (const field of this.options.schema.fields) {
|
|
899
|
+
const entityValue = getByPath(entity, field.entityPath);
|
|
900
|
+
const remotePath = field.remotePath ?? field.entityPath;
|
|
901
|
+
const serializedValue = field.serialize ? field.serialize(entityValue, entity) : cloneValue2(entityValue);
|
|
902
|
+
if (serializedValue === void 0) {
|
|
903
|
+
deleteByPath(remote, remotePath);
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (field.encrypted) {
|
|
907
|
+
const strategyId = field.strategyId ?? this.options.schema.defaultStrategyId;
|
|
908
|
+
if (!strategyId) {
|
|
909
|
+
throw new Error(
|
|
910
|
+
`Field "${field.entityPath}" requires an encryption strategy, but none was configured.`
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
const context = await this.options.contextResolver.resolve({
|
|
914
|
+
entity,
|
|
915
|
+
field,
|
|
916
|
+
phase: "encrypt",
|
|
917
|
+
remote,
|
|
918
|
+
schema: this.options.schema
|
|
919
|
+
});
|
|
920
|
+
const payload = await this.options.strategies.encrypt(
|
|
921
|
+
strategyId,
|
|
922
|
+
stringifyValue(serializedValue),
|
|
923
|
+
context
|
|
924
|
+
);
|
|
925
|
+
setByPath(remote, remotePath, payload);
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
setByPath(remote, remotePath, serializedValue);
|
|
929
|
+
}
|
|
930
|
+
return remote;
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
function createEntityRepository(options) {
|
|
934
|
+
return new EntityRepository(options);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// src/schemas/dashboard.ts
|
|
938
|
+
function createDashboardSchema(strategyId = "aes-256-gcm") {
|
|
939
|
+
return {
|
|
940
|
+
cacheCollection: "dashboards",
|
|
941
|
+
createEntity(remote) {
|
|
942
|
+
return {
|
|
943
|
+
config: remote.configEnvelope ?? null,
|
|
944
|
+
createdAt: remote.createdAt,
|
|
945
|
+
id: remote.id,
|
|
946
|
+
name: remote.name,
|
|
947
|
+
updatedAt: remote.updatedAt
|
|
948
|
+
};
|
|
949
|
+
},
|
|
950
|
+
createRemote(entity) {
|
|
951
|
+
return {
|
|
952
|
+
configEnvelope: entity.config,
|
|
953
|
+
createdAt: entity.createdAt,
|
|
954
|
+
id: entity.id,
|
|
955
|
+
name: entity.name,
|
|
956
|
+
updatedAt: entity.updatedAt
|
|
957
|
+
};
|
|
958
|
+
},
|
|
959
|
+
defaultStrategyId: strategyId,
|
|
960
|
+
fields: [
|
|
961
|
+
{
|
|
962
|
+
encrypted: true,
|
|
963
|
+
entityPath: "config",
|
|
964
|
+
remotePath: "configEnvelope"
|
|
965
|
+
}
|
|
966
|
+
],
|
|
967
|
+
name: "dashboard"
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/schemas/integration.ts
|
|
972
|
+
function createIntegrationSchema(strategyId = "aes-256-gcm") {
|
|
973
|
+
return {
|
|
974
|
+
cacheCollection: "integrations",
|
|
975
|
+
createEntity(remote) {
|
|
976
|
+
return {
|
|
977
|
+
apiUrl: remote.apiUrl,
|
|
978
|
+
authHash: remote.authHash ?? null,
|
|
979
|
+
credentialMode: remote.credentialMode,
|
|
980
|
+
displayName: remote.displayName,
|
|
981
|
+
encryptionKey: remote.encryptionKey ?? null,
|
|
982
|
+
id: remote.id,
|
|
983
|
+
lastSyncedAt: remote.lastSyncedAt,
|
|
984
|
+
provider: remote.provider,
|
|
985
|
+
providerSecret: remote.providerSecret ?? null,
|
|
986
|
+
status: remote.status,
|
|
987
|
+
username: remote.username
|
|
988
|
+
};
|
|
989
|
+
},
|
|
990
|
+
createRemote(entity) {
|
|
991
|
+
return {
|
|
992
|
+
apiUrl: entity.apiUrl,
|
|
993
|
+
authHash: entity.authHash,
|
|
994
|
+
credentialMode: entity.credentialMode,
|
|
995
|
+
displayName: entity.displayName,
|
|
996
|
+
encryptionKey: entity.encryptionKey,
|
|
997
|
+
id: entity.id,
|
|
998
|
+
lastSyncedAt: entity.lastSyncedAt,
|
|
999
|
+
provider: entity.provider,
|
|
1000
|
+
providerSecret: entity.providerSecret,
|
|
1001
|
+
status: entity.status,
|
|
1002
|
+
username: entity.username
|
|
1003
|
+
};
|
|
1004
|
+
},
|
|
1005
|
+
defaultStrategyId: strategyId,
|
|
1006
|
+
fields: [
|
|
1007
|
+
{
|
|
1008
|
+
encrypted: true,
|
|
1009
|
+
entityPath: "authHash"
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
encrypted: true,
|
|
1013
|
+
entityPath: "providerSecret"
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
encrypted: true,
|
|
1017
|
+
entityPath: "encryptionKey"
|
|
1018
|
+
}
|
|
1019
|
+
],
|
|
1020
|
+
name: "integration"
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
export { Aes256GcmStrategy, DEFAULT_SCRYPT_PARAMS, EntityRepository, ExternalE2eeApiClient, FetchRestTransport, FunctionGraphqlTransport, GraphqlCrudAdapter, LokiCacheStore, MlKemAes256GcmStrategy, PasswordAuthClient, RestCrudAdapter, StrategyRegistry, authKeyMaterialHex, base64ToBytes, bytesToBase64, bytesToUtf8, createAes256GcmStrategy, createDashboardSchema, createEntityRepository, createExternalE2eeApiClient, createFetchRestTransport, createGraphqlTransport, createIntegrationSchema, createLokiCacheStore, createMlKemAes256GcmStrategy, createPasswordAuthClient, createStrategyRegistry, decryptJsonFromLegacyBlob, deriveAes256KeyFromPassword, deriveAuthKeyMaterial, deriveClientKeyMaterial, encryptJsonToLegacyBlob, encryptedFieldToLegacyBlob, generateMlKemKeyPair, isEncryptedFieldValue, kdfSaltFromBase64, legacyBlobToEncryptedField, normalizeAuthEmail, toArrayBuffer, toUint8Array, utf8ToBytes };
|
|
1025
|
+
//# sourceMappingURL=index.js.map
|
|
1026
|
+
//# sourceMappingURL=index.js.map
|