selfstore 1.8.20 → 1.8.21

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 (89) hide show
  1. package/README.md +107 -12
  2. package/dist/advanced.d.ts +23 -8
  3. package/dist/advanced.js +23 -12
  4. package/dist/advanced.js.map +1 -1
  5. package/dist/backups.d.ts +4 -4
  6. package/dist/backups.js +8 -6
  7. package/dist/{cache-lock-KWJUORTS.js → cache-lock-5RNG6CIK.js} +4 -3
  8. package/dist/cache-lock-5RNG6CIK.js.map +1 -0
  9. package/dist/{chunk-SKWHKM25.js → chunk-23UAUYSW.js} +40 -4
  10. package/dist/chunk-23UAUYSW.js.map +1 -0
  11. package/dist/chunk-3BWBBGFW.js +29 -0
  12. package/dist/chunk-3BWBBGFW.js.map +1 -0
  13. package/dist/{chunk-W22V5CRM.js → chunk-3KAAPYPO.js} +3 -3
  14. package/dist/{chunk-W22V5CRM.js.map → chunk-3KAAPYPO.js.map} +1 -1
  15. package/dist/chunk-757ZOSUI.js +27 -0
  16. package/dist/chunk-757ZOSUI.js.map +1 -0
  17. package/dist/{chunk-DCEXWSM6.js → chunk-AUH35AAQ.js} +3 -3
  18. package/dist/{chunk-DCEXWSM6.js.map → chunk-AUH35AAQ.js.map} +1 -1
  19. package/dist/{chunk-ULPGGZ6L.js → chunk-DJZ5S3FI.js} +11 -10
  20. package/dist/chunk-DJZ5S3FI.js.map +1 -0
  21. package/dist/{chunk-HOG2IKEP.js → chunk-EBCQ7ZG7.js} +4 -4
  22. package/dist/{chunk-HOG2IKEP.js.map → chunk-EBCQ7ZG7.js.map} +1 -1
  23. package/dist/chunk-FJGWTBAL.js +6 -0
  24. package/dist/chunk-FJGWTBAL.js.map +1 -0
  25. package/dist/chunk-GW5CYP4C.js +212 -0
  26. package/dist/chunk-GW5CYP4C.js.map +1 -0
  27. package/dist/{chunk-MGDXR3RB.js → chunk-HHPJPEEG.js} +6 -5
  28. package/dist/chunk-HHPJPEEG.js.map +1 -0
  29. package/dist/{chunk-J5MFXJU6.js → chunk-JTNFZV2B.js} +78 -26
  30. package/dist/chunk-JTNFZV2B.js.map +1 -0
  31. package/dist/chunk-JZHWAX34.js +178 -0
  32. package/dist/chunk-JZHWAX34.js.map +1 -0
  33. package/dist/chunk-LUNYYH6D.js +223 -0
  34. package/dist/chunk-LUNYYH6D.js.map +1 -0
  35. package/dist/{chunk-LKIRWVXM.js → chunk-LYO36VYB.js} +12 -11
  36. package/dist/chunk-LYO36VYB.js.map +1 -0
  37. package/dist/{chunk-O3Y6FORN.js → chunk-OY2A5DCB.js} +4 -3
  38. package/dist/chunk-OY2A5DCB.js.map +1 -0
  39. package/dist/chunk-QSLHEE2L.js +13 -0
  40. package/dist/chunk-QSLHEE2L.js.map +1 -0
  41. package/dist/{chunk-3OBJOMQ2.js → chunk-RREJ2MFN.js} +4 -28
  42. package/dist/chunk-RREJ2MFN.js.map +1 -0
  43. package/dist/chunk-XMC67RNM.js +29 -0
  44. package/dist/chunk-XMC67RNM.js.map +1 -0
  45. package/dist/{connect-GY1R9c6Q.d.ts → connect-Bob2mofK.d.ts} +3 -3
  46. package/dist/{drive-BLRsK32w.d.ts → drive-86CHuTij.d.ts} +63 -2
  47. package/dist/flows.d.ts +5 -5
  48. package/dist/flows.js +17 -15
  49. package/dist/flows.js.map +1 -1
  50. package/dist/groups.js +4 -3
  51. package/dist/groups.js.map +1 -1
  52. package/dist/households.d.ts +1 -1
  53. package/dist/index.d.ts +50 -8
  54. package/dist/index.js +72 -17
  55. package/dist/index.js.map +1 -1
  56. package/dist/kdf-worker.js +7 -2
  57. package/dist/kdf-worker.js.map +1 -1
  58. package/dist/{local-DrziuI_t.d.ts → local-8H61XxbL.d.ts} +2 -2
  59. package/dist/local-XET4G32B.js +7 -0
  60. package/dist/{local-DPS35F5C.js.map → local-XET4G32B.js.map} +1 -1
  61. package/dist/{manager-BlSs6Q5S.d.ts → manager-Di6lNE4E.d.ts} +1 -1
  62. package/dist/manager-drive-YLSLQ63O.js +10 -0
  63. package/dist/{manager-drive-4WLKOOXO.js.map → manager-drive-YLSLQ63O.js.map} +1 -1
  64. package/dist/s3-43RWNCV3.js +11 -0
  65. package/dist/s3-43RWNCV3.js.map +1 -0
  66. package/dist/{s3-BLgf6hqR.d.ts → s3-BsbVn3fw.d.ts} +1 -1
  67. package/dist/{store-DhrWMYg_.d.ts → store-CIbiNoiE.d.ts} +1 -1
  68. package/dist/sync.d.ts +7 -6
  69. package/dist/sync.js +3 -1
  70. package/dist/webdav-EEQKX5TZ.js +9 -0
  71. package/dist/webdav-EEQKX5TZ.js.map +1 -0
  72. package/dist/widgets.d.ts +19 -5
  73. package/dist/widgets.js +17 -10
  74. package/dist/widgets.js.map +1 -1
  75. package/llms.txt +51 -4
  76. package/package.json +12 -5
  77. package/skills/selfstore/SKILL.md +115 -0
  78. package/dist/cache-lock-KWJUORTS.js.map +0 -1
  79. package/dist/chunk-3OBJOMQ2.js.map +0 -1
  80. package/dist/chunk-J5MFXJU6.js.map +0 -1
  81. package/dist/chunk-LKIRWVXM.js.map +0 -1
  82. package/dist/chunk-MGDXR3RB.js.map +0 -1
  83. package/dist/chunk-O3Y6FORN.js.map +0 -1
  84. package/dist/chunk-SKWHKM25.js.map +0 -1
  85. package/dist/chunk-TKWU52LK.js +0 -607
  86. package/dist/chunk-TKWU52LK.js.map +0 -1
  87. package/dist/chunk-ULPGGZ6L.js.map +0 -1
  88. package/dist/local-DPS35F5C.js +0 -6
  89. package/dist/manager-drive-4WLKOOXO.js +0 -8
@@ -1,607 +0,0 @@
1
- import { lifeLine, boundedSignal } from './chunk-J5MFXJU6.js';
2
- import { seal, isEnvelope, unseal, newDeviceKey } from './chunk-QBCU7OOP.js';
3
- import { AuthExpiredError, SelfstoreError } from './chunk-3OBJOMQ2.js';
4
-
5
- // src/persistence/targets/desktop.ts
6
- var PATH_KEY = "desktopFilePath";
7
- var bridge = null;
8
- function useDesktopFiles(b) {
9
- bridge = b;
10
- }
11
- function hasDesktopFiles() {
12
- return bridge != null;
13
- }
14
- var FILTERS = [{ name: "Backup", extensions: ["zip"] }];
15
- function baseName(path) {
16
- const parts = path.split(/[\\/]/);
17
- return parts[parts.length - 1] || path;
18
- }
19
- async function markerOf(b, path) {
20
- try {
21
- const info = await b.stat(path);
22
- const mtime = info?.mtime;
23
- if (mtime == null) return null;
24
- const ms = mtime instanceof Date ? mtime.getTime() : Number(mtime);
25
- return Number.isFinite(ms) ? String(ms) : null;
26
- } catch {
27
- return null;
28
- }
29
- }
30
- function firstPath(picked) {
31
- if (picked == null) return null;
32
- return Array.isArray(picked) ? picked[0] ?? null : picked;
33
- }
34
- function fromPath(path, b, kv) {
35
- let current = path;
36
- return {
37
- kind: "file",
38
- get label() {
39
- return baseName(current);
40
- },
41
- async save(blob) {
42
- try {
43
- await b.writeFile(current, new Uint8Array(await blob.arrayBuffer()));
44
- } catch (e) {
45
- throw new SelfstoreError("TARGET_WRITE_FAILED", `File write failed: ${String(e)}`);
46
- }
47
- return markerOf(b, current);
48
- },
49
- async load() {
50
- try {
51
- const bytes = await b.readFile(current);
52
- return new Blob([new Uint8Array(bytes)]);
53
- } catch {
54
- return null;
55
- }
56
- },
57
- async stat() {
58
- return markerOf(b, current);
59
- },
60
- async isReady() {
61
- return true;
62
- },
63
- async reconnect() {
64
- const picked = await b.save({ defaultPath: current, filters: FILTERS });
65
- if (!picked) return false;
66
- current = picked;
67
- await kv.set(PATH_KEY, picked);
68
- return true;
69
- },
70
- async disconnect() {
71
- await kv.del(PATH_KEY);
72
- }
73
- };
74
- }
75
- async function connect(opts) {
76
- if (!bridge) return null;
77
- const picked = await bridge.save({ defaultPath: opts.fileName, filters: FILTERS });
78
- if (!picked) return null;
79
- await opts.kv.set(PATH_KEY, picked);
80
- return fromPath(picked, bridge, opts.kv);
81
- }
82
- async function openExisting(opts) {
83
- if (!bridge) return null;
84
- const picked = firstPath(await bridge.open({ multiple: false, filters: FILTERS }));
85
- if (!picked) return null;
86
- await opts.kv.set(PATH_KEY, picked);
87
- return fromPath(picked, bridge, opts.kv);
88
- }
89
- async function fromSession(opts) {
90
- if (!bridge) return null;
91
- const path = await opts.kv.get(PATH_KEY);
92
- return path ? fromPath(path, bridge, opts.kv) : null;
93
- }
94
-
95
- // src/persistence/targets/file.ts
96
- var HANDLE_KEY = "fileHandle";
97
- var refused = false;
98
- function insideGesture() {
99
- const ua = typeof navigator !== "undefined" ? navigator.userActivation : void 0;
100
- return ua ? ua.isActive : true;
101
- }
102
- function isSupported() {
103
- if (hasDesktopFiles()) return true;
104
- return !refused && typeof window !== "undefined" && "showSaveFilePicker" in window;
105
- }
106
- function isOpenSupported() {
107
- if (hasDesktopFiles()) return true;
108
- return !refused && typeof window !== "undefined" && "showOpenFilePicker" in window;
109
- }
110
- async function ask(open) {
111
- const asked = insideGesture();
112
- try {
113
- return await open();
114
- } catch (e) {
115
- if (e instanceof DOMException && e.name === "AbortError") return null;
116
- refused = asked;
117
- return null;
118
- }
119
- }
120
- function asTargetError(e, transient, what) {
121
- const name = e instanceof DOMException ? e.name : "";
122
- if (name === "NotAllowedError" || name === "SecurityError") {
123
- return new AuthExpiredError("File permission was revoked.");
124
- }
125
- if (name === "NotFoundError") {
126
- return new SelfstoreError("TARGET_GONE", `${what}: the backup file is no longer there.`);
127
- }
128
- return new SelfstoreError(transient, `${what}: ${String(e)}`);
129
- }
130
- function fromHandle(handle, kv) {
131
- return {
132
- kind: "file",
133
- label: handle.name,
134
- async save(blob) {
135
- try {
136
- const writable = await handle.createWritable();
137
- await writable.write(blob);
138
- await writable.close();
139
- } catch (e) {
140
- throw asTargetError(e, "TARGET_WRITE_FAILED", "File write failed");
141
- }
142
- try {
143
- return String((await handle.getFile()).lastModified);
144
- } catch {
145
- return null;
146
- }
147
- },
148
- async load() {
149
- try {
150
- return await handle.getFile();
151
- } catch (e) {
152
- throw asTargetError(e, "TARGET_UNAVAILABLE", "File read failed");
153
- }
154
- },
155
- async stat() {
156
- try {
157
- return String((await handle.getFile()).lastModified);
158
- } catch {
159
- return null;
160
- }
161
- },
162
- async isReady() {
163
- return await handle.queryPermission({ mode: "readwrite" }) === "granted";
164
- },
165
- async reconnect() {
166
- return await handle.requestPermission({ mode: "readwrite" }) === "granted";
167
- },
168
- async disconnect() {
169
- await kv.del(HANDLE_KEY);
170
- }
171
- };
172
- }
173
- async function connect2(opts) {
174
- if (hasDesktopFiles()) return connect(opts);
175
- const picker = window.showSaveFilePicker;
176
- if (!picker) return null;
177
- const handle = await ask(
178
- () => picker({
179
- suggestedName: opts.fileName,
180
- types: [{ description: "Backup", accept: { "application/zip": [".zip"] } }]
181
- })
182
- );
183
- if (!handle) return null;
184
- await opts.kv.set(HANDLE_KEY, handle);
185
- return fromHandle(handle, opts.kv);
186
- }
187
- async function openExisting2(opts) {
188
- if (hasDesktopFiles()) return openExisting(opts);
189
- const picker = window.showOpenFilePicker;
190
- if (!picker) return null;
191
- const picked = await ask(
192
- () => picker({
193
- multiple: false,
194
- types: [{ description: "Backup", accept: { "application/zip": [".zip"] } }]
195
- })
196
- );
197
- const handle = picked?.[0];
198
- if (!handle) return null;
199
- try {
200
- await handle.requestPermission({ mode: "readwrite" });
201
- } catch {
202
- }
203
- await opts.kv.set(HANDLE_KEY, handle);
204
- return fromHandle(handle, opts.kv);
205
- }
206
- async function fromSession2(opts) {
207
- if (hasDesktopFiles()) return fromSession(opts);
208
- const handle = await opts.kv.get(HANDLE_KEY);
209
- return handle ? fromHandle(handle, opts.kv) : null;
210
- }
211
-
212
- // src/persistence/targets/webdav.ts
213
- var CONFIG_KEY = "webdavConfig";
214
- var KEY_KEY = "webdavKey";
215
- var DATA_DEADLINE_MS = 3e4;
216
- var META_DEADLINE_MS = 15e3;
217
- async function configKey(kv) {
218
- const existing = await kv.get(KEY_KEY);
219
- if (existing) return existing;
220
- const fresh = await newDeviceKey();
221
- await kv.set(KEY_KEY, fresh);
222
- return fresh;
223
- }
224
- async function saveConfig(kv, c) {
225
- const bytes = new TextEncoder().encode(JSON.stringify(c));
226
- await kv.set(CONFIG_KEY, await seal(await configKey(kv), bytes));
227
- }
228
- async function loadConfig(kv) {
229
- const stored = await kv.get(CONFIG_KEY);
230
- if (!isEnvelope(stored)) return null;
231
- const bytes = await unseal(await configKey(kv), stored);
232
- return JSON.parse(new TextDecoder().decode(bytes));
233
- }
234
- function labelOf(url) {
235
- try {
236
- return new URL(url).host;
237
- } catch {
238
- return "WebDAV";
239
- }
240
- }
241
- function assertSecureUrl(url) {
242
- let u;
243
- try {
244
- u = new URL(url);
245
- } catch {
246
- throw new TypeError(`webdavTarget: invalid URL "${url}".`);
247
- }
248
- const loopback = u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]";
249
- if (u.protocol !== "https:" && !(u.protocol === "http:" && loopback)) {
250
- throw new TypeError(
251
- `webdavTarget: refusing "${u.protocol}" - Basic-auth credentials need https (http is allowed only for localhost).`
252
- );
253
- }
254
- }
255
- function versionOf(res) {
256
- return res.headers.get("ETag") ?? res.headers.get("Last-Modified") ?? null;
257
- }
258
- function fromConfig(c, kv) {
259
- const headers = { Authorization: "Basic " + btoa(`${c.username}:${c.password}`) };
260
- const life = lifeLine();
261
- const bounded = (ms) => boundedSignal(ms, life.current());
262
- async function probe() {
263
- try {
264
- return await fetch(c.url, {
265
- method: "HEAD",
266
- headers,
267
- signal: bounded(META_DEADLINE_MS)
268
- });
269
- } catch {
270
- return null;
271
- }
272
- }
273
- async function stat() {
274
- const res = await probe();
275
- return res && res.ok ? versionOf(res) : null;
276
- }
277
- async function ready() {
278
- const res = await probe();
279
- if (!res) return false;
280
- if (res.status === 401 || res.status === 403) {
281
- throw new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);
282
- }
283
- return res.ok || res.status === 404;
284
- }
285
- async function reconnectProbe() {
286
- const res = await probe();
287
- return !!res && (res.ok || res.status === 404);
288
- }
289
- return {
290
- kind: "webdav",
291
- label: labelOf(c.url),
292
- abortInFlight: life.cut,
293
- async save(blob) {
294
- const res = await fetch(c.url, {
295
- method: "PUT",
296
- headers,
297
- body: blob,
298
- signal: bounded(DATA_DEADLINE_MS)
299
- });
300
- if (res.status === 401 || res.status === 403) {
301
- throw new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);
302
- }
303
- if (!res.ok)
304
- throw new SelfstoreError("TARGET_WRITE_FAILED", `WebDAV PUT failed: ${res.status}`);
305
- return versionOf(res) ?? await stat();
306
- },
307
- async load() {
308
- const res = await fetch(c.url, {
309
- method: "GET",
310
- headers,
311
- signal: bounded(DATA_DEADLINE_MS)
312
- });
313
- if (res.status === 404) return null;
314
- if (res.status === 401 || res.status === 403) {
315
- throw new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);
316
- }
317
- if (!res.ok)
318
- throw new SelfstoreError("TARGET_UNAVAILABLE", `WebDAV GET failed: ${res.status}`);
319
- return res.blob();
320
- },
321
- stat,
322
- isReady: ready,
323
- reconnect: reconnectProbe,
324
- // stored credentials: re-check reachability, no gesture
325
- async disconnect() {
326
- await kv.del(CONFIG_KEY);
327
- await kv.del(KEY_KEY);
328
- }
329
- };
330
- }
331
- function peer(opts) {
332
- if (opts.username !== void 0) assertSecureUrl(opts.url);
333
- const headers = opts.username !== void 0 ? { Authorization: "Basic " + btoa(`${opts.username}:${opts.password ?? ""}`) } : void 0;
334
- return {
335
- label: opts.label ?? labelOf(opts.url),
336
- async load() {
337
- const res = await fetch(opts.url, {
338
- method: "GET",
339
- headers,
340
- signal: AbortSignal.timeout(DATA_DEADLINE_MS)
341
- });
342
- if (res.status === 404) return null;
343
- if (res.status === 401 || res.status === 403) {
344
- throw new AuthExpiredError(`WebDAV peer rejected the credentials (${res.status}).`);
345
- }
346
- if (!res.ok) {
347
- throw new SelfstoreError("TARGET_UNAVAILABLE", `WebDAV peer GET failed: ${res.status}`);
348
- }
349
- return res.blob();
350
- },
351
- async stat() {
352
- try {
353
- const res = await fetch(opts.url, {
354
- method: "HEAD",
355
- headers,
356
- signal: AbortSignal.timeout(META_DEADLINE_MS)
357
- });
358
- return res.ok ? versionOf(res) : null;
359
- } catch {
360
- return null;
361
- }
362
- }
363
- };
364
- }
365
- async function connect3(opts) {
366
- assertSecureUrl(opts.config.url);
367
- const target = fromConfig(opts.config, opts.kv);
368
- let ok;
369
- try {
370
- ok = await target.isReady();
371
- } catch {
372
- ok = false;
373
- }
374
- if (!ok) return null;
375
- await saveConfig(opts.kv, opts.config);
376
- return target;
377
- }
378
- async function fromSession3(opts) {
379
- const config = await loadConfig(opts.kv);
380
- return config ? fromConfig(config, opts.kv) : null;
381
- }
382
-
383
- // src/persistence/targets/sigv4.ts
384
- var encoder = new TextEncoder();
385
- function hex(bytes) {
386
- let out = "";
387
- for (const b of bytes) out += b.toString(16).padStart(2, "0");
388
- return out;
389
- }
390
- async function sha256Hex(bytes) {
391
- return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
392
- }
393
- var EMPTY_PAYLOAD_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
394
- async function hmac(key, data) {
395
- const k = await crypto.subtle.importKey(
396
- "raw",
397
- key,
398
- { name: "HMAC", hash: "SHA-256" },
399
- false,
400
- ["sign"]
401
- );
402
- return new Uint8Array(await crypto.subtle.sign("HMAC", k, data));
403
- }
404
- function encodeSegment(s) {
405
- return encodeURIComponent(s).replace(
406
- /[!*'()]/g,
407
- (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
408
- );
409
- }
410
- function encodeS3Path(path) {
411
- return path.split("/").map((seg) => encodeSegment(seg)).join("/");
412
- }
413
- function basicAmzDate() {
414
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:-]|\.\d{3}/g, "");
415
- }
416
- async function signS3(input) {
417
- const service = input.service ?? "s3";
418
- const amzDate = input.amzDate ?? basicAmzDate();
419
- const dateStamp = amzDate.slice(0, 8);
420
- const { host } = new URL(input.origin);
421
- const canonicalUri = encodeS3Path(input.path);
422
- const headers = {
423
- host,
424
- "x-amz-content-sha256": input.payloadHashHex,
425
- "x-amz-date": amzDate
426
- };
427
- for (const [name, value] of Object.entries({})) {
428
- headers[name.toLowerCase()] = value;
429
- }
430
- const signedNames = Object.keys(headers).sort();
431
- const canonicalHeaders = signedNames.map((n) => `${n}:${headers[n].trim()}
432
- `).join("");
433
- const signedHeaders = signedNames.join(";");
434
- const canonicalRequest = [
435
- input.method,
436
- canonicalUri,
437
- "",
438
- // no query string on any of our single-object requests
439
- canonicalHeaders,
440
- signedHeaders,
441
- input.payloadHashHex
442
- ].join("\n");
443
- const scope = `${dateStamp}/${input.region}/${service}/aws4_request`;
444
- const stringToSign = [
445
- "AWS4-HMAC-SHA256",
446
- amzDate,
447
- scope,
448
- await sha256Hex(encoder.encode(canonicalRequest))
449
- ].join("\n");
450
- const kDate = await hmac(
451
- encoder.encode("AWS4" + input.secretAccessKey),
452
- encoder.encode(dateStamp)
453
- );
454
- const kRegion = await hmac(kDate, encoder.encode(input.region));
455
- const kService = await hmac(kRegion, encoder.encode(service));
456
- const kSigning = await hmac(kService, encoder.encode("aws4_request"));
457
- const signature = hex(await hmac(kSigning, encoder.encode(stringToSign)));
458
- const authorization = `AWS4-HMAC-SHA256 Credential=${input.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
459
- const sendHeaders = { authorization };
460
- for (const n of signedNames) if (n !== "host") sendHeaders[n] = headers[n];
461
- return { url: input.origin.replace(/\/$/, "") + canonicalUri, headers: sendHeaders };
462
- }
463
-
464
- // src/persistence/targets/s3.ts
465
- var CONFIG_KEY2 = "s3Config";
466
- var KEY_KEY2 = "s3Key";
467
- var DATA_DEADLINE_MS2 = 3e4;
468
- var META_DEADLINE_MS2 = 15e3;
469
- async function configKey2(kv) {
470
- const existing = await kv.get(KEY_KEY2);
471
- if (existing) return existing;
472
- const fresh = await newDeviceKey();
473
- await kv.set(KEY_KEY2, fresh);
474
- return fresh;
475
- }
476
- async function saveConfig2(kv, c) {
477
- const bytes = new TextEncoder().encode(JSON.stringify(c));
478
- await kv.set(CONFIG_KEY2, await seal(await configKey2(kv), bytes));
479
- }
480
- async function loadConfig2(kv) {
481
- const stored = await kv.get(CONFIG_KEY2);
482
- if (!isEnvelope(stored)) return null;
483
- const bytes = await unseal(await configKey2(kv), stored);
484
- return JSON.parse(new TextDecoder().decode(bytes));
485
- }
486
- function assertSecureEndpoint(endpoint) {
487
- let u;
488
- try {
489
- u = new URL(endpoint);
490
- } catch {
491
- throw new TypeError(`s3Target: invalid endpoint "${endpoint}".`);
492
- }
493
- const loopback = u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]";
494
- if (u.protocol !== "https:" && !(u.protocol === "http:" && loopback)) {
495
- throw new TypeError(
496
- `s3Target: refusing "${u.protocol}" - S3 requests need https (http is allowed only for localhost).`
497
- );
498
- }
499
- return u;
500
- }
501
- function labelOf2(c) {
502
- try {
503
- return `${c.bucket} (${new URL(c.endpoint).host})`;
504
- } catch {
505
- return c.bucket || "S3";
506
- }
507
- }
508
- function versionOf2(res) {
509
- return res.headers.get("ETag") ?? res.headers.get("Last-Modified") ?? null;
510
- }
511
- function fromConfig2(c, kv) {
512
- const endpointUrl = assertSecureEndpoint(c.endpoint);
513
- const pathStyle = c.forcePathStyle ?? true;
514
- const origin = pathStyle ? `${endpointUrl.protocol}//${endpointUrl.host}` : `${endpointUrl.protocol}//${c.bucket}.${endpointUrl.host}`;
515
- const objectPath = pathStyle ? `/${c.bucket}/${c.key}` : `/${c.key}`;
516
- const life = lifeLine();
517
- async function request(method, payloadHashHex, body, extraHeaders, deadlineMs = DATA_DEADLINE_MS2) {
518
- const signed = await signS3({
519
- method,
520
- origin,
521
- path: objectPath,
522
- region: c.region,
523
- accessKeyId: c.accessKeyId,
524
- secretAccessKey: c.secretAccessKey,
525
- payloadHashHex});
526
- return fetch(signed.url, {
527
- method,
528
- headers: signed.headers,
529
- body,
530
- signal: boundedSignal(deadlineMs, life.current())
531
- });
532
- }
533
- async function probe() {
534
- try {
535
- return await request("HEAD", EMPTY_PAYLOAD_SHA256, void 0, void 0, META_DEADLINE_MS2);
536
- } catch {
537
- return null;
538
- }
539
- }
540
- async function stat() {
541
- const res = await probe();
542
- return res && res.ok ? versionOf2(res) : null;
543
- }
544
- async function ready() {
545
- const res = await probe();
546
- if (!res) return false;
547
- if (res.status === 403) {
548
- throw new AuthExpiredError("S3 rejected the credentials (403).");
549
- }
550
- return res.ok || res.status === 404;
551
- }
552
- async function reconnectProbe() {
553
- const res = await probe();
554
- return !!res && (res.ok || res.status === 404);
555
- }
556
- return {
557
- kind: "s3",
558
- label: labelOf2(c),
559
- abortInFlight: life.cut,
560
- async save(blob) {
561
- const bytes = new Uint8Array(await blob.arrayBuffer());
562
- const res = await request("PUT", await sha256Hex(bytes), bytes);
563
- if (res.status === 403) {
564
- throw new AuthExpiredError("S3 rejected the credentials (403).");
565
- }
566
- if (!res.ok) throw new SelfstoreError("TARGET_WRITE_FAILED", `S3 PUT failed: ${res.status}`);
567
- return versionOf2(res) ?? await stat();
568
- },
569
- async load() {
570
- const res = await request("GET", EMPTY_PAYLOAD_SHA256);
571
- if (res.status === 404) return null;
572
- if (res.status === 403) {
573
- throw new AuthExpiredError("S3 rejected the credentials (403).");
574
- }
575
- if (!res.ok) throw new SelfstoreError("TARGET_UNAVAILABLE", `S3 GET failed: ${res.status}`);
576
- return res.blob();
577
- },
578
- stat,
579
- isReady: ready,
580
- reconnect: reconnectProbe,
581
- async disconnect() {
582
- await kv.del(CONFIG_KEY2);
583
- await kv.del(KEY_KEY2);
584
- }
585
- };
586
- }
587
- async function connect4(opts) {
588
- assertSecureEndpoint(opts.config.endpoint);
589
- const target = fromConfig2(opts.config, opts.kv);
590
- let ok;
591
- try {
592
- ok = await target.isReady();
593
- } catch {
594
- ok = false;
595
- }
596
- if (!ok) return null;
597
- await saveConfig2(opts.kv, opts.config);
598
- return target;
599
- }
600
- async function fromSession4(opts) {
601
- const config = await loadConfig2(opts.kv);
602
- return config ? fromConfig2(config, opts.kv) : null;
603
- }
604
-
605
- export { connect2 as connect, connect3 as connect2, connect4 as connect3, fromSession2 as fromSession, fromSession3 as fromSession2, fromSession4 as fromSession3, hasDesktopFiles, isOpenSupported, isSupported, openExisting2 as openExisting, peer, useDesktopFiles };
606
- //# sourceMappingURL=chunk-TKWU52LK.js.map
607
- //# sourceMappingURL=chunk-TKWU52LK.js.map