altcha 2.2.2 → 2.2.4

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.
@@ -1,167 +1,163 @@
1
- var J = Object.defineProperty;
2
- var v = (t) => {
3
- throw TypeError(t);
1
+ const u = {
2
+ generateKey: v,
3
+ exportKey: S,
4
+ importKey: x,
5
+ decrypt: C,
6
+ encrypt: L
4
7
  };
5
- var X = (t, n, e) => n in t ? J(t, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[n] = e;
6
- var p = (t, n, e) => X(t, typeof n != "symbol" ? n + "" : n, e), S = (t, n, e) => n.has(t) || v("Cannot " + e);
7
- var f = (t, n, e) => (S(t, n, "read from private field"), e ? e.call(t) : n.get(t)), w = (t, n, e) => n.has(t) ? v("Cannot add the same private member more than once") : n instanceof WeakSet ? n.add(t) : n.set(t, e);
8
- var u = (t, n, e) => (S(t, n, "access private method"), e);
9
- const E = {
10
- generateKey: Q,
11
- exportKey: W,
12
- importKey: Z,
13
- decrypt: te,
14
- encrypt: ee
15
- };
16
- async function Q(t = 256) {
8
+ async function v(n = 256) {
17
9
  return crypto.subtle.generateKey({
18
10
  name: "AES-GCM",
19
- length: t
11
+ length: n
20
12
  }, !0, ["encrypt", "decrypt"]);
21
13
  }
22
- async function W(t) {
23
- return new Uint8Array(await crypto.subtle.exportKey("raw", t));
14
+ async function S(n) {
15
+ return new Uint8Array(await crypto.subtle.exportKey("raw", n));
24
16
  }
25
- async function Z(t) {
26
- return crypto.subtle.importKey("raw", t, {
17
+ async function x(n) {
18
+ return crypto.subtle.importKey("raw", n, {
27
19
  name: "AES-GCM"
28
20
  }, !0, ["encrypt", "decrypt"]);
29
21
  }
30
- async function ee(t, n, e = 16) {
31
- const i = crypto.getRandomValues(new Uint8Array(e));
22
+ async function L(n, e, t = 16) {
23
+ const r = crypto.getRandomValues(new Uint8Array(t));
32
24
  return {
33
25
  encrypted: new Uint8Array(await crypto.subtle.encrypt({
34
26
  name: "AES-GCM",
35
- iv: i
36
- }, t, n)),
37
- iv: i
27
+ iv: r
28
+ }, n, e)),
29
+ iv: r
38
30
  };
39
31
  }
40
- async function te(t, n, e) {
32
+ async function C(n, e, t) {
41
33
  return new Uint8Array(await crypto.subtle.decrypt({
42
34
  name: "AES-GCM",
43
- iv: e
44
- }, t, n));
35
+ iv: t
36
+ }, n, e));
45
37
  }
46
- function ne(t, n = !1) {
47
- return n && (t = t.replace(/_/g, "/").replace(/-/g, "+") + "=".repeat(3 - (3 + t.length) % 4)), Uint8Array.from(atob(t), (e) => e.charCodeAt(0));
38
+ function I(n, e = !1) {
39
+ return e && (n = n.replace(/_/g, "/").replace(/-/g, "+") + "=".repeat(3 - (3 + n.length) % 4)), Uint8Array.from(atob(n), (t) => t.charCodeAt(0));
48
40
  }
49
- function x(t, n = !1) {
50
- const e = btoa(String.fromCharCode(...t));
51
- return n ? e.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : e;
41
+ function h(n, e = !1) {
42
+ const t = btoa(String.fromCharCode(...n));
43
+ return e ? t.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : t;
52
44
  }
53
- function L(t, n = 80) {
54
- let e = "";
55
- for (; t.length > 0; )
56
- e += t.slice(0, n) + `
57
- `, t = t.slice(n);
58
- return e;
45
+ function f(n, e = 80) {
46
+ let t = "";
47
+ for (; n.length > 0; )
48
+ t += n.slice(0, e) + `
49
+ `, n = n.slice(e);
50
+ return t;
59
51
  }
60
- function C(t) {
61
- return ne(t.split(/\r?\n/).filter((n) => !n.startsWith("-----")).join(""));
52
+ function g(n) {
53
+ return I(n.split(/\r?\n/).filter((e) => !e.startsWith("-----")).join(""));
62
54
  }
63
- const h = "RSA-OAEP", b = "SHA-256", re = 2048, ie = new Uint8Array([1, 0, 1]), se = {
64
- generateKeyPair: oe,
65
- encrypt: ae,
66
- decrypt: ce,
67
- exportPrivateKey: I,
68
- exportPrivateKeyPem: pe,
69
- exportPublicKey: F,
70
- exportPublicKeyPem: le,
71
- exportPublicKeyFromPrivateKey: de,
72
- importPrivateKey: T,
73
- importPrivateKeyPem: ue,
74
- importPublicKey: N,
75
- importPublicKeyPem: z
55
+ const l = "RSA-OAEP", p = "SHA-256", N = 2048, z = new Uint8Array([1, 0, 1]), T = {
56
+ generateKeyPair: _,
57
+ encrypt: R,
58
+ decrypt: j,
59
+ exportPrivateKey: m,
60
+ exportPrivateKeyPem: H,
61
+ exportPublicKey: d,
62
+ exportPublicKeyPem: k,
63
+ exportPublicKeyFromPrivateKey: O,
64
+ importPrivateKey: E,
65
+ importPrivateKeyPem: q,
66
+ importPublicKey: w,
67
+ importPublicKeyPem: b
76
68
  };
77
- async function oe() {
69
+ async function _() {
78
70
  return crypto.subtle.generateKey({
79
- name: h,
80
- modulusLength: re,
81
- publicExponent: ie,
82
- hash: b
71
+ name: l,
72
+ modulusLength: N,
73
+ publicExponent: z,
74
+ hash: p
83
75
  }, !0, ["encrypt", "decrypt"]);
84
76
  }
85
- async function ae(t, n) {
77
+ async function R(n, e) {
86
78
  return new Uint8Array(await crypto.subtle.encrypt({
87
- name: h
88
- }, t, n));
79
+ name: l
80
+ }, n, e));
89
81
  }
90
- async function ce(t, n) {
82
+ async function j(n, e) {
91
83
  return new Uint8Array(await crypto.subtle.decrypt({
92
- name: h
93
- }, t, n));
84
+ name: l
85
+ }, n, e));
94
86
  }
95
- async function F(t) {
96
- return new Uint8Array(await crypto.subtle.exportKey("spki", t));
87
+ async function d(n) {
88
+ return new Uint8Array(await crypto.subtle.exportKey("spki", n));
97
89
  }
98
- async function I(t) {
99
- return new Uint8Array(await crypto.subtle.exportKey("pkcs8", t));
90
+ async function m(n) {
91
+ return new Uint8Array(await crypto.subtle.exportKey("pkcs8", n));
100
92
  }
101
- async function le(t) {
93
+ async function k(n) {
102
94
  return `-----BEGIN PUBLIC KEY-----
103
- ` + L(x(await F(t)), 64) + "-----END PUBLIC KEY-----";
95
+ ` + f(h(await d(n)), 64) + "-----END PUBLIC KEY-----";
104
96
  }
105
- async function pe(t) {
97
+ async function H(n) {
106
98
  return `-----BEGIN PRIVATE KEY-----
107
- ` + L(x(await I(t)), 64) + "-----END PRIVATE KEY-----";
99
+ ` + f(h(await m(n)), 64) + "-----END PRIVATE KEY-----";
108
100
  }
109
- async function N(t) {
110
- return crypto.subtle.importKey("spki", t, {
111
- name: h,
112
- hash: b
101
+ async function w(n) {
102
+ return crypto.subtle.importKey("spki", n, {
103
+ name: l,
104
+ hash: p
113
105
  }, !0, ["encrypt"]);
114
106
  }
115
- async function z(t) {
116
- return N(C(t));
107
+ async function b(n) {
108
+ return w(g(n));
117
109
  }
118
- async function T(t) {
119
- return crypto.subtle.importKey("pkcs8", t, {
120
- name: h,
121
- hash: b
110
+ async function E(n) {
111
+ return crypto.subtle.importKey("pkcs8", n, {
112
+ name: l,
113
+ hash: p
122
114
  }, !0, ["decrypt"]);
123
115
  }
124
- async function ue(t) {
125
- return T(C(t));
116
+ async function q(n) {
117
+ return E(g(n));
126
118
  }
127
- async function de(t) {
128
- const n = await crypto.subtle.exportKey("jwk", t);
129
- delete n.d, delete n.dp, delete n.dq, delete n.q, delete n.qi, n.key_ops = ["encrypt"];
130
- const e = await crypto.subtle.importKey("jwk", n, {
131
- name: h,
132
- hash: b
119
+ async function O(n) {
120
+ const e = await crypto.subtle.exportKey("jwk", n);
121
+ delete e.d, delete e.dp, delete e.dq, delete e.q, delete e.qi, e.key_ops = ["encrypt"];
122
+ const t = await crypto.subtle.importKey("jwk", e, {
123
+ name: l,
124
+ hash: p
133
125
  }, !0, ["encrypt"]);
134
- return F(e);
126
+ return d(t);
135
127
  }
136
- const ye = new Uint8Array([1, 0, 1]), he = 256, fe = 16;
137
- async function me(t, n, e = {}) {
138
- const { aesIVLength: i = fe, aesKeyLength: r = he } = e, o = await E.generateKey(r), { encrypted: a, iv: s } = await E.encrypt(o, n, i), l = await se.encrypt(t, await E.exportKey(o));
128
+ const $ = new Uint8Array([1, 0, 1]), B = 256, G = 16;
129
+ async function M(n, e, t = {}) {
130
+ const { aesIVLength: r = G, aesKeyLength: i = B } = t, s = await u.generateKey(i), { encrypted: o, iv: a } = await u.encrypt(s, e, r), c = await T.encrypt(n, await u.exportKey(s));
139
131
  return new Uint8Array([
140
- ...ye,
141
- ...new Uint8Array([l.length]),
142
- ...new Uint8Array([s.length]),
143
- ...l,
144
- ...s,
145
- ...a
132
+ ...$,
133
+ ...new Uint8Array([c.length]),
134
+ ...new Uint8Array([a.length]),
135
+ ...c,
136
+ ...a,
137
+ ...o
146
138
  ]);
147
139
  }
148
- class P {
140
+ class U {
149
141
  /**
150
142
  * Constructs a new instance of the Plugin.
151
143
  *
152
144
  * @param {PluginContext} context - The context provided to the plugin, containing necessary configurations and dependencies.
153
145
  */
154
- constructor(n) {
155
- this.context = n;
146
+ constructor(e) {
147
+ this.context = e;
156
148
  }
149
+ /**
150
+ * A distinct name of the plugin. Every plugin must have it's own name.
151
+ */
152
+ static pluginName;
157
153
  /**
158
154
  * Registers a plugin class in the global `altchaPlugins` array.
159
155
  * Ensures the plugin is added only once.
160
156
  *
161
157
  * @param {new(context: PluginContext) => Plugin} cls - The plugin class to register.
162
158
  */
163
- static register(n) {
164
- typeof globalThis.altchaPlugins != "object" && (globalThis.altchaPlugins = []), globalThis.altchaPlugins.includes(n) || globalThis.altchaPlugins.push(n);
159
+ static register(e) {
160
+ typeof globalThis.altchaPlugins != "object" && (globalThis.altchaPlugins = []), globalThis.altchaPlugins.includes(e) || globalThis.altchaPlugins.push(e);
165
161
  }
166
162
  /**
167
163
  * Clean up resources when the plugin is destroyed.
@@ -175,7 +171,7 @@ class P {
175
171
  *
176
172
  * @param {string | null} err - The error message or `null` if there's no error.
177
173
  */
178
- onErrorChange(n) {
174
+ onErrorChange(e) {
179
175
  }
180
176
  /**
181
177
  * Callback triggered when the plugin state changes.
@@ -183,29 +179,23 @@ class P {
183
179
  *
184
180
  * @param {State} state - The new state of the plugin.
185
181
  */
186
- onStateChange(n) {
182
+ onStateChange(e) {
187
183
  }
188
184
  }
189
- /**
190
- * A distinct name of the plugin. Every plugin must have it's own name.
191
- */
192
- p(P, "pluginName");
193
- var m, g, c, R, j, k, U, H, q, O, A, $, B, G;
194
- class _ extends P {
185
+ class Y extends U {
186
+ static pluginName = "upload";
187
+ pendingFiles = [];
188
+ uploadHandles = [];
189
+ elForm;
190
+ #e = this.#c.bind(this);
191
+ #t = this.#l.bind(this);
195
192
  /**
196
193
  * Constructor initializes the plugin, setting up event listeners on the form.
197
194
  *
198
195
  * @param {PluginContext} context - Plugin context providing access to the element, configuration, and utility methods.
199
196
  */
200
197
  constructor(e) {
201
- super(e);
202
- w(this, c);
203
- p(this, "pendingFiles", []);
204
- p(this, "uploadHandles", []);
205
- p(this, "elForm");
206
- w(this, m, u(this, c, q).bind(this));
207
- w(this, g, u(this, c, O).bind(this));
208
- this.elForm = this.context.el.closest("form"), this.elForm && (this.elForm.addEventListener("change", f(this, m)), this.elForm.addEventListener("submit", f(this, g), {
198
+ super(e), this.elForm = this.context.el.closest("form"), this.elForm && (this.elForm.addEventListener("change", this.#e), this.elForm.addEventListener("submit", this.#t, {
209
199
  capture: !0
210
200
  }));
211
201
  }
@@ -215,245 +205,283 @@ class _ extends P {
215
205
  * @param {string} fieldName - The field name associated with the file input.
216
206
  * @param {File} file - The file to be uploaded.
217
207
  */
218
- addFile(e, i) {
219
- this.pendingFiles.find(([r, o]) => r === e && o === i) || this.pendingFiles.push([e, i]);
208
+ addFile(e, t) {
209
+ this.pendingFiles.find(([r, i]) => r === e && i === t) || this.pendingFiles.push([e, t]);
220
210
  }
221
211
  /**
222
212
  * Cleans up event listeners and other resources when the plugin is destroyed.
223
213
  */
224
214
  destroy() {
225
- this.elForm && (this.elForm.removeEventListener("change", f(this, m)), this.elForm.removeEventListener("submit", f(this, g)));
215
+ this.elForm && (this.elForm.removeEventListener("change", this.#e), this.elForm.removeEventListener("submit", this.#t));
226
216
  }
227
217
  /**
228
218
  * Uploads all pending files in the list.
229
219
  */
230
220
  async uploadPendingFiles() {
231
- var i;
232
221
  const e = async () => {
233
- const r = this.pendingFiles[0];
234
- if (r && await u(this, c, A).call(this, u(this, c, j).call(this, r)), this.pendingFiles.length)
222
+ const t = this.pendingFiles[0];
223
+ if (t && await this.#r(this.#s(t)), this.pendingFiles.length)
235
224
  return e();
236
225
  };
237
226
  try {
238
227
  await e();
239
- } catch (r) {
240
- return this.context.log("upload failed", r), this.context.dispatch("uploaderror", {
241
- error: r
228
+ } catch (t) {
229
+ return this.context.log("upload failed", t), this.context.dispatch("uploaderror", {
230
+ error: t
242
231
  }), !1;
243
232
  }
244
- this.pendingFiles.length === 0 && (u(this, c, R).call(this), (i = this.elForm) == null || i.requestSubmit());
233
+ this.pendingFiles.length === 0 && (this.#i(), this.elForm?.requestSubmit());
245
234
  }
246
- }
247
- m = new WeakMap(), g = new WeakMap(), c = new WeakSet(), /**
248
- * Adds hidden input fields to the form containing the file IDs of uploaded files.
249
- */
250
- R = function() {
251
- var i, r, o;
252
- const e = this.uploadHandles.reduce(
253
- (a, s) => (a[s.fieldName] || (a[s.fieldName] = []), s.fileId && a[s.fieldName].push(s.fileId), a),
254
- {}
255
- );
256
- for (const a in e) {
257
- const s = document.createElement("input");
258
- s.name = a, s.type = "hidden", s.value = e[a].join(","), (r = (i = this.elForm) == null ? void 0 : i.querySelector(`[name="${a}"]`)) == null || r.setAttribute("disabled", "disabled"), (o = this.elForm) == null || o.appendChild(s);
235
+ /**
236
+ * Adds hidden input fields to the form containing the file IDs of uploaded files.
237
+ */
238
+ #i() {
239
+ const e = this.uploadHandles.reduce(
240
+ (t, r) => (t[r.fieldName] || (t[r.fieldName] = []), r.fileId && t[r.fieldName].push(r.fileId), t),
241
+ {}
242
+ );
243
+ for (const t in e) {
244
+ const r = document.createElement("input");
245
+ r.name = t, r.type = "hidden", r.value = e[t].join(","), this.elForm?.querySelector(`[name="${t}"]`)?.setAttribute("disabled", "disabled"), this.elForm?.appendChild(r);
246
+ }
259
247
  }
260
- }, /**
261
- * Creates an upload handle for the specified pending file.
262
- *
263
- * @param {[string, File]} pendingFile - The field name and file to be uploaded.
264
- * @returns {UploadHandle} The created upload handle.
265
- * @throws Will throw an error if the upload handle cannot be created.
266
- */
267
- j = function(e) {
268
- const i = this.pendingFiles.findIndex(
269
- ([o, a]) => o === e[0] && a === e[1]
270
- );
271
- if (i < 0)
272
- throw new Error("Cannot create upload handle.");
273
- const r = new ge(e[0], e[1]);
274
- return this.uploadHandles.push(r), this.pendingFiles.splice(i, 1), u(this, c, k).call(this, r), u(this, c, U).call(this), r;
275
- }, /**
276
- * Dispatches a custom event when a file upload starts.
277
- *
278
- * @param {UploadHandle} handle - The upload handle associated with the file upload.
279
- */
280
- k = function(e) {
281
- this.context.dispatch("upload", { handle: e });
282
- }, /**
283
- * Dispatches a custom event to track the progress of ongoing file uploads.
284
- */
285
- U = function() {
286
- const e = this.pendingFiles.reduce((r, [o, a]) => r + a.size, 0) + this.uploadHandles.reduce((r, { uploadSize: o }) => r + o, 0), i = this.uploadHandles.reduce(
287
- (r, { loaded: o }) => r + o,
288
- 0
289
- );
290
- this.context.dispatch("uploadprogress", {
291
- bytesLoaded: i,
292
- bytesTotal: e,
293
- pendingFiles: this.pendingFiles,
294
- uploadHandles: this.uploadHandles
295
- });
296
- }, /**
297
- * Retrieves the upload URL from the form's attributes.
298
- *
299
- * @returns {string | null} The upload URL, or null if not found.
300
- */
301
- H = function() {
302
- if (this.elForm) {
303
- const e = this.elForm.getAttribute("action"), i = this.elForm.getAttribute("data-upload-url");
304
- if (i)
305
- return i;
306
- const r = new URL(e || location.origin);
307
- return r.pathname = r.pathname + "/file", r.toString();
248
+ /**
249
+ * Creates an upload handle for the specified pending file.
250
+ *
251
+ * @param {[string, File]} pendingFile - The field name and file to be uploaded.
252
+ * @returns {UploadHandle} The created upload handle.
253
+ * @throws Will throw an error if the upload handle cannot be created.
254
+ */
255
+ #s(e) {
256
+ const t = this.pendingFiles.findIndex(
257
+ ([i, s]) => i === e[0] && s === e[1]
258
+ );
259
+ if (t < 0)
260
+ throw new Error("Cannot create upload handle.");
261
+ const r = new D(e[0], e[1]);
262
+ return this.uploadHandles.push(r), this.pendingFiles.splice(t, 1), this.#o(r), this.#n(), r;
308
263
  }
309
- return null;
310
- }, /**
311
- * Handles the form's change event, adding files to the pending files list.
312
- *
313
- * @param {Event} ev - The change event.
314
- */
315
- q = function(e) {
316
- const i = e.target;
317
- if (i && i.type === "file") {
318
- const r = i.files;
319
- if (r != null && r.length)
320
- for (const o of r)
321
- this.addFile(i.name, o);
264
+ /**
265
+ * Dispatches a custom event when a file upload starts.
266
+ *
267
+ * @param {UploadHandle} handle - The upload handle associated with the file upload.
268
+ */
269
+ #o(e) {
270
+ this.context.dispatch("upload", { handle: e });
322
271
  }
323
- }, /**
324
- * Handles the form's submit event, preventing submission until all pending files are uploaded.
325
- *
326
- * @param {SubmitEvent} ev - The submit event.
327
- */
328
- O = function(e) {
329
- const i = e.target;
330
- i != null && i.hasAttribute(
331
- "data-code-challenge-form"
332
- ) || this.pendingFiles.length && (e.preventDefault(), e.stopPropagation(), this.uploadPendingFiles());
333
- }, A = async function(e, i) {
334
- const r = u(this, c, H).call(this);
335
- if (!r)
336
- throw new Error("Upload url not specified.");
337
- const o = {
338
- "content-type": "application/json"
339
- };
340
- i && (o.authorization = "Altcha payload=" + i);
341
- const a = await fetch(r, {
342
- body: JSON.stringify({
343
- name: e.file.name || "file",
344
- size: e.file.size,
345
- type: e.file.type || "application/octet-stream"
346
- }),
347
- credentials: "include",
348
- headers: o,
349
- method: "POST"
350
- });
351
- if (a.status === 401)
352
- return u(this, c, $).call(this, a, e);
353
- if (a.status !== 200)
354
- throw new Error(`Unexpected server response ${a.status}.`);
355
- const s = await a.json();
356
- let l = e.file;
357
- if (s.encrypted && s.encryptionPublicKey) {
358
- const d = await z(s.encryptionPublicKey), M = await new Response(
359
- new ReadableStream({
360
- async start(K) {
361
- const Y = e.file.stream().getReader();
362
- for (; ; ) {
363
- const { done: D, value: V } = await Y.read();
364
- if (D)
365
- break;
366
- K.enqueue(V);
367
- }
368
- K.close();
369
- }
370
- })
371
- ).arrayBuffer();
372
- l = await me(d, new Uint8Array(M));
272
+ /**
273
+ * Dispatches a custom event to track the progress of ongoing file uploads.
274
+ */
275
+ #n() {
276
+ const e = this.pendingFiles.reduce((r, [i, s]) => r + s.size, 0) + this.uploadHandles.reduce((r, { uploadSize: i }) => r + i, 0), t = this.uploadHandles.reduce(
277
+ (r, { loaded: i }) => r + i,
278
+ 0
279
+ );
280
+ this.context.dispatch("uploadprogress", {
281
+ bytesLoaded: t,
282
+ bytesTotal: e,
283
+ pendingFiles: this.pendingFiles,
284
+ uploadHandles: this.uploadHandles
285
+ });
286
+ }
287
+ /**
288
+ * Retrieves the upload URL from the form's attributes.
289
+ *
290
+ * @returns {string | null} The upload URL, or null if not found.
291
+ */
292
+ #a() {
293
+ if (this.elForm) {
294
+ const e = this.elForm.getAttribute("action"), t = this.elForm.getAttribute("data-upload-url");
295
+ if (t)
296
+ return t;
297
+ const r = new URL(e || location.origin);
298
+ return r.pathname = r.pathname + "/file", r.toString();
299
+ }
300
+ return null;
373
301
  }
374
- return e.uploadSize = l instanceof Uint8Array ? l.byteLength : e.file.size, await u(this, c, G).call(this, s.uploadUrl, e, l, {
375
- "content-type": e.file.type || "application/octet-stream"
376
- }), s.finalizeUrl && await u(this, c, B).call(this, s.finalizeUrl, e.uploadSize), e.fileId = s.fileId, e.resolve({
377
- encrypted: s.encrypted,
378
- fileId: s.fileId
379
- }), e.promise;
380
- }, $ = async function(e, i) {
381
- var r;
382
- try {
383
- const o = e.headers.get("www-authenticate"), a = (r = o == null ? void 0 : o.match(/challenge=(.*),/)) == null ? void 0 : r[1];
384
- if (!a)
385
- throw new Error(
386
- "Unable to retrieve altcha challenge from www-authenticate header."
387
- );
388
- const s = JSON.parse(a);
389
- if (s && "challenge" in s) {
390
- const { solution: l } = await this.context.solve(s);
391
- if (l && "number" in l)
392
- return u(this, c, A).call(this, i, btoa(
393
- JSON.stringify({
394
- ...s,
395
- number: l.number
396
- })
397
- ));
398
- throw new Error("Invalid challenge solution.");
302
+ /**
303
+ * Handles the form's change event, adding files to the pending files list.
304
+ *
305
+ * @param {Event} ev - The change event.
306
+ */
307
+ #c(e) {
308
+ const t = e.target;
309
+ if (t && t.type === "file") {
310
+ const r = t.files;
311
+ if (r?.length)
312
+ for (const i of r)
313
+ this.addFile(t.name, i);
399
314
  }
400
- } catch (o) {
401
- throw this.context.log(o), new Error("Unable to solve altcha challenge for upload.");
402
315
  }
403
- }, B = async function(e, i) {
404
- const r = await fetch(e, {
405
- body: JSON.stringify({
406
- uploadedBytes: i
407
- }),
408
- headers: {
316
+ /**
317
+ * Handles the form's submit event, preventing submission until all pending files are uploaded.
318
+ *
319
+ * @param {SubmitEvent} ev - The submit event.
320
+ */
321
+ #l(e) {
322
+ e.target?.hasAttribute(
323
+ "data-code-challenge-form"
324
+ ) || this.pendingFiles.length && (e.preventDefault(), e.stopPropagation(), this.uploadPendingFiles());
325
+ }
326
+ /**
327
+ * Uploads a single file, handling encryption and ALTCHA challenges if necessary.
328
+ *
329
+ * @param {UploadHandle} handle - The upload handle associated with the file.
330
+ * @param {string} [altchaPayload] - The ALTCHA payload, if available.
331
+ * @returns {Promise<unknown>} A promise that resolves when the upload is complete.
332
+ * @throws Will throw an error if the upload fails or if an ALTCHA challenge cannot be solved.
333
+ */
334
+ async #r(e, t) {
335
+ const r = this.#a();
336
+ if (!r)
337
+ throw new Error("Upload url not specified.");
338
+ const i = {
409
339
  "content-type": "application/json"
410
- },
411
- method: "POST"
412
- });
413
- if (r.status > 204)
414
- throw new Error(`Unexpected server response ${r.status}.`);
415
- return !0;
416
- }, G = async function(e, i, r, o = {}) {
417
- var a;
418
- return e = new URL(
419
- e,
420
- ((a = this.elForm) == null ? void 0 : a.getAttribute("action")) || location.origin
421
- ).toString(), new Promise((s, l) => {
422
- const d = new XMLHttpRequest();
423
- i.controller.signal.addEventListener("abort", () => {
424
- d.abort();
425
- }), d.upload.addEventListener("progress", (y) => {
426
- i.setProgress(y.loaded), u(this, c, U).call(this);
427
- }), d.addEventListener("error", (y) => {
428
- l(new Error("Upload failed."));
429
- }), d.addEventListener("load", () => {
430
- d.status >= 400 ? l(new Error(`Server responded with ${d.status}`)) : s(void 0);
431
- }), d.open("PUT", e);
432
- for (const y in o)
433
- d.setRequestHeader(y, o[y]);
434
- d.send(r);
435
- });
436
- }, p(_, "pluginName", "upload");
437
- class ge {
340
+ };
341
+ t && (i.authorization = "Altcha payload=" + t);
342
+ const s = await fetch(r, {
343
+ body: JSON.stringify({
344
+ name: e.file.name || "file",
345
+ size: e.file.size,
346
+ type: e.file.type || "application/octet-stream"
347
+ }),
348
+ credentials: "include",
349
+ headers: i,
350
+ method: "POST"
351
+ });
352
+ if (s.status === 401)
353
+ return this.#p(s, e);
354
+ if (s.status !== 200)
355
+ throw new Error(`Unexpected server response ${s.status}.`);
356
+ const o = await s.json();
357
+ let a = e.file;
358
+ if (o.encrypted && o.encryptionPublicKey) {
359
+ const c = await b(o.encryptionPublicKey), P = await new Response(
360
+ new ReadableStream({
361
+ async start(y) {
362
+ const A = e.file.stream().getReader();
363
+ for (; ; ) {
364
+ const { done: F, value: K } = await A.read();
365
+ if (F)
366
+ break;
367
+ y.enqueue(K);
368
+ }
369
+ y.close();
370
+ }
371
+ })
372
+ ).arrayBuffer();
373
+ a = await M(c, new Uint8Array(P));
374
+ }
375
+ return e.uploadSize = a instanceof Uint8Array ? a.byteLength : e.file.size, await this.#d(o.uploadUrl, e, a, {
376
+ "content-type": e.file.type || "application/octet-stream"
377
+ }), o.finalizeUrl && await this.#u(o.finalizeUrl, e.uploadSize), e.fileId = o.fileId, e.resolve({
378
+ encrypted: o.encrypted,
379
+ fileId: o.fileId
380
+ }), e.promise;
381
+ }
382
+ /**
383
+ * Handles ALTCHA challenges during file upload, solving the challenge and retrying the upload.
384
+ *
385
+ * @param {Response} resp - The response from the server containing the ALTCHA challenge.
386
+ * @param {UploadHandle} handle - The upload handle associated with the file.
387
+ * @returns {Promise<unknown>} A promise that resolves when the challenge is solved and the upload is complete.
388
+ * @throws Will throw an error if the challenge cannot be solved.
389
+ */
390
+ async #p(e, t) {
391
+ try {
392
+ const i = e.headers.get("www-authenticate")?.match(/challenge=(.*),/)?.[1];
393
+ if (!i)
394
+ throw new Error(
395
+ "Unable to retrieve altcha challenge from www-authenticate header."
396
+ );
397
+ const s = JSON.parse(i);
398
+ if (s && "challenge" in s) {
399
+ const { solution: o } = await this.context.solve(s);
400
+ if (o && "number" in o)
401
+ return this.#r(
402
+ t,
403
+ btoa(
404
+ JSON.stringify({
405
+ ...s,
406
+ number: o.number
407
+ })
408
+ )
409
+ );
410
+ throw new Error("Invalid challenge solution.");
411
+ }
412
+ } catch (r) {
413
+ throw this.context.log(r), new Error("Unable to solve altcha challenge for upload.");
414
+ }
415
+ }
416
+ /**
417
+ * Finalizes the file upload by sending a request to the server.
418
+ *
419
+ * @param {string} finalizeUrl - The URL to finalize the upload.
420
+ * @param {number} uploadSize - The size of the uploaded file.
421
+ */
422
+ async #u(e, t) {
423
+ const r = await fetch(e, {
424
+ body: JSON.stringify({
425
+ uploadedBytes: t
426
+ }),
427
+ headers: {
428
+ "content-type": "application/json"
429
+ },
430
+ method: "POST"
431
+ });
432
+ if (r.status > 204)
433
+ throw new Error(`Unexpected server response ${r.status}.`);
434
+ return !0;
435
+ }
436
+ /**
437
+ * Uploads the file's contents to the server using the PUT method.
438
+ *
439
+ * @param {string} url - The URL to upload the file to.
440
+ * @param {UploadHandle} handle - The upload handle associated with the file.
441
+ * @param {Uint8Array | File} body - The file's contents.
442
+ * @param {Record<string, string>} headers - Additional headers for the upload request.
443
+ */
444
+ async #d(e, t, r, i = {}) {
445
+ return e = new URL(
446
+ e,
447
+ this.elForm?.getAttribute("action") || location.origin
448
+ ).toString(), new Promise((s, o) => {
449
+ const a = new XMLHttpRequest();
450
+ t.controller.signal.addEventListener("abort", () => {
451
+ a.abort();
452
+ }), a.upload.addEventListener("progress", (c) => {
453
+ t.setProgress(c.loaded), this.#n();
454
+ }), a.addEventListener("error", (c) => {
455
+ o(new Error("Upload failed."));
456
+ }), a.addEventListener("load", () => {
457
+ a.status >= 400 ? o(new Error(`Server responded with ${a.status}`)) : s(void 0);
458
+ }), a.open("PUT", e);
459
+ for (const c in i)
460
+ a.setRequestHeader(c, i[c]);
461
+ a.send(r);
462
+ });
463
+ }
464
+ }
465
+ class D {
438
466
  /**
439
467
  * Creates an instance of UploadHandle.
440
468
  *
441
469
  * @param {string} fieldName - The name of the field associated with the file upload.
442
470
  * @param {File} file - The file to be uploaded.
443
471
  */
444
- constructor(n, e) {
445
- p(this, "controller", new AbortController());
446
- p(this, "promise");
447
- p(this, "fileId");
448
- p(this, "loaded", 0);
449
- p(this, "progress", 0);
450
- p(this, "uploadSize", 0);
451
- p(this, "resolve");
452
- p(this, "reject");
453
- this.fieldName = n, this.file = e, this.uploadSize = this.file.size, this.promise = new Promise((i, r) => {
454
- this.resolve = i, this.reject = r;
472
+ constructor(e, t) {
473
+ this.fieldName = e, this.file = t, this.uploadSize = this.file.size, this.promise = new Promise((r, i) => {
474
+ this.resolve = r, this.reject = i;
455
475
  });
456
476
  }
477
+ controller = new AbortController();
478
+ promise;
479
+ fileId;
480
+ loaded = 0;
481
+ progress = 0;
482
+ uploadSize = 0;
483
+ resolve;
484
+ reject;
457
485
  /**
458
486
  * Aborts the file upload by invoking the AbortController's abort method.
459
487
  */
@@ -465,11 +493,11 @@ class ge {
465
493
  *
466
494
  * @param {number} loaded - The number of bytes that have been uploaded.
467
495
  */
468
- setProgress(n) {
469
- this.loaded = n, this.progress = this.file.size && n ? Math.min(1, n / this.file.size) : 0;
496
+ setProgress(e) {
497
+ this.loaded = e, this.progress = this.file.size && e ? Math.min(1, e / this.file.size) : 0;
470
498
  }
471
499
  }
472
- P.register(_);
500
+ U.register(Y);
473
501
  export {
474
- _ as PluginUpload
502
+ Y as PluginUpload
475
503
  };