@adgytec/adgytec-web-utils 0.0.1 → 0.0.2

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.
@@ -0,0 +1,24 @@
1
+ import * as r from "zod";
2
+ class t extends Error {
3
+ }
4
+ const a = r.object({
5
+ message: r.string().optional(),
6
+ fieldErrors: r.record(r.string(), r.union([r.string(), r.array(r.string())])).optional()
7
+ }), n = (o) => {
8
+ const s = a.safeParse(o), e = s.success && !s.data.message && !s.data.fieldErrors;
9
+ return !s.success || e ? {
10
+ message: "Unexpected server error"
11
+ } : s.data;
12
+ };
13
+ class c extends t {
14
+ data;
15
+ response;
16
+ constructor(s, e) {
17
+ super(e.message ?? "API Error"), this.name = "API Error", this.data = e, this.response = s;
18
+ }
19
+ }
20
+ export {
21
+ c as A,
22
+ t as B,
23
+ n
24
+ };
@@ -0,0 +1,39 @@
1
+ import { A as r, n as f } from "./apiError";
2
+ import "zod";
3
+ async function i(e, s) {
4
+ const n = await e.text();
5
+ if (!n) {
6
+ if (e.ok) {
7
+ if (s)
8
+ throw new r(e, {
9
+ message: "Expected response body but received empty response"
10
+ });
11
+ return null;
12
+ }
13
+ throw new r(e, {
14
+ message: "Empty error response from server"
15
+ });
16
+ }
17
+ let t;
18
+ try {
19
+ t = JSON.parse(n);
20
+ } catch {
21
+ const p = e.ok ? "Malformed JSON response from server" : "Malformed error response from server";
22
+ throw new r(e, { message: p });
23
+ }
24
+ if (e.ok) {
25
+ if (!s)
26
+ return null;
27
+ const o = s.safeParse(t);
28
+ if (!o.success)
29
+ throw new r(e, {
30
+ message: `Invalid response shape from server: ${o.error.message}`
31
+ });
32
+ return o.data;
33
+ }
34
+ const a = f(t);
35
+ throw new r(e, a);
36
+ }
37
+ export {
38
+ i as d
39
+ };
@@ -0,0 +1,88 @@
1
+ import { A as o, B as n } from "./apiError";
2
+ const d = 1e3, c = (e) => {
3
+ if (e instanceof TypeError && (e.message === "Failed to fetch" || e.message === "NetworkError when attempting to fetch resource." || e.message === "Load failed" || // Safari
4
+ e.message.includes("NetworkError") || e.message.includes("fetch")))
5
+ return {
6
+ errorCode: "network-error",
7
+ message: "Check your network connection or try again later."
8
+ };
9
+ if (e instanceof o) {
10
+ if (e.response.status >= 500)
11
+ return {
12
+ errorCode: "server-error",
13
+ message: e.data?.message ?? "Internal server error. Please try again later."
14
+ };
15
+ switch (e.response.status) {
16
+ case 422:
17
+ return {
18
+ errorCode: "form-field-error",
19
+ fieldErrors: e.data.fieldErrors ?? {}
20
+ };
21
+ case 400:
22
+ return {
23
+ errorCode: "user-action-error",
24
+ message: e.data?.message ?? "Something went wrong while processing your request."
25
+ };
26
+ case 401:
27
+ return {
28
+ errorCode: "authentication-error",
29
+ message: e.data?.message ?? "User login required."
30
+ };
31
+ case 403:
32
+ return {
33
+ errorCode: "authorization-error",
34
+ message: e.data?.message ?? "You don't have necessary permissions to perform the requested action."
35
+ };
36
+ case 404:
37
+ return {
38
+ errorCode: "not-found-error",
39
+ message: e.data?.message ?? "Requested action not found."
40
+ };
41
+ case 405:
42
+ return {
43
+ errorCode: "method-not-allowed-error",
44
+ message: e.data?.message ?? "Requested action method not allowed."
45
+ };
46
+ case 429:
47
+ const r = e.response.headers.get("retry-after");
48
+ let s = d;
49
+ if (r) {
50
+ const t = parseInt(r, 10);
51
+ if (!isNaN(t))
52
+ s = t * 1e3;
53
+ else {
54
+ const a = new Date(r);
55
+ isNaN(a.getTime()) || (s = Math.max(
56
+ 0,
57
+ a.getTime() - Date.now()
58
+ ));
59
+ }
60
+ }
61
+ return {
62
+ errorCode: "too-many-requests-error",
63
+ retryAfter: s,
64
+ message: "Too many requests. Try again later."
65
+ };
66
+ case 413:
67
+ return {
68
+ errorCode: "content-too-large-error",
69
+ message: e.data?.message ?? "Request content too large."
70
+ };
71
+ default:
72
+ return {
73
+ errorCode: "unknown-error",
74
+ message: e.data?.message ?? "Unexpected error occurred."
75
+ };
76
+ }
77
+ }
78
+ return e instanceof n ? {
79
+ errorCode: "unknown-error",
80
+ message: e.message
81
+ } : {
82
+ errorCode: "unknown-error",
83
+ message: "Unknown error occurred. Please refresh this page or try again later."
84
+ };
85
+ };
86
+ export {
87
+ c as p
88
+ };
@@ -0,0 +1,369 @@
1
+ import r from "zod";
2
+ import { v7 as c } from "uuid";
3
+ import { B as n } from "./apiError";
4
+ import { Queue as p } from "@datastructures-js/queue";
5
+ import { p as l } from "./parse";
6
+ import { d as h } from "./decode";
7
+ import { HTTP_PUT as u, HTTP_HEADER_USER_LOCALE as m, HTTP_REQUEST_CREDENTIALS_INCLUDE as f, HTTP_POST as y, HTTP_HEADER_CONTENT_TYPE_APPLICATION_JSON as w, HTTP_HEADER_CONTENT_TYPE as g } from "../constants/index.js";
8
+ const U = r.object({
9
+ presignPut: r.url(),
10
+ partNumber: r.int().positive().lte(1e4),
11
+ partSize: r.int().positive().gt(0)
12
+ }), P = r.object({
13
+ mediaID: r.uuidv7(),
14
+ uploadType: r.literal("multipart"),
15
+ multipartPresignPart: r.array(U).nonempty(),
16
+ multipartSuccessCallback: r.url()
17
+ }), C = r.object({
18
+ mediaID: r.uuidv7(),
19
+ uploadType: r.literal("singlepart"),
20
+ presignPut: r.url(),
21
+ singlepartSuccessCallback: r.url()
22
+ }), B = r.discriminatedUnion("uploadType", [
23
+ P,
24
+ C
25
+ ]), T = (s) => ({
26
+ id: c(),
27
+ name: s.name,
28
+ size: s.size,
29
+ file: s
30
+ }), $ = (s) => s.map(T), E = (s) => ({
31
+ id: s.id,
32
+ size: s.size,
33
+ name: s.name
34
+ }), k = (s) => s.map(E), R = (s, t) => {
35
+ if (s.id !== t.mediaID)
36
+ throw new n("media item and response id mismatch");
37
+ return {
38
+ ...t,
39
+ file: s.file,
40
+ size: s.size
41
+ };
42
+ }, q = (s, t) => {
43
+ if (s.length !== t.length)
44
+ throw new n(
45
+ `items mismatch: mediaInfos=${s.length}, apiResponses=${t.length}`
46
+ );
47
+ const e = new Map(
48
+ t.map((i) => [i.mediaID, i])
49
+ );
50
+ return s.map((i) => {
51
+ const a = e.get(i.id);
52
+ if (!a)
53
+ throw new n(
54
+ `Could not find a matching API response for media info with id: ${i.id}`
55
+ );
56
+ return R(i, a);
57
+ });
58
+ };
59
+ class S {
60
+ #s;
61
+ #t;
62
+ #a;
63
+ #i;
64
+ #e;
65
+ #n;
66
+ #o;
67
+ #r;
68
+ constructor(t, e, i, a) {
69
+ this.#s = t, this.#t = e, this.#a = [], this.#i = /* @__PURE__ */ new Set(), this.#e = a, this.#n = i, this.#o = !1, this.#r = !1;
70
+ }
71
+ get failed() {
72
+ return this.#o;
73
+ }
74
+ fail() {
75
+ this.#o = !0;
76
+ }
77
+ get id() {
78
+ return this.#s;
79
+ }
80
+ get completeURL() {
81
+ return this.#n;
82
+ }
83
+ get blob() {
84
+ return this.#t;
85
+ }
86
+ add(t) {
87
+ this.contains(t.partNumber) || (this.#a.push(t), this.#i.add(t.partNumber));
88
+ }
89
+ contains(t) {
90
+ return this.#i.has(t);
91
+ }
92
+ get list() {
93
+ return this.#a.slice().sort((t, e) => t.partNumber - e.partNumber);
94
+ }
95
+ get canComplete() {
96
+ return !this.failed && this.#i.size === this.#e;
97
+ }
98
+ tryStartComplete() {
99
+ return this.#r || !this.canComplete ? !1 : (this.#r = !0, !0);
100
+ }
101
+ resetComplete() {
102
+ this.#r = !1;
103
+ }
104
+ get totalPartsCount() {
105
+ return this.#e;
106
+ }
107
+ get uploadedPartsCount() {
108
+ return this.#i.size;
109
+ }
110
+ }
111
+ class N {
112
+ #s;
113
+ #t;
114
+ #a;
115
+ #i;
116
+ #e;
117
+ constructor(t, e, i, a) {
118
+ this.#s = t, this.#t = e, this.#i = a, this.#a = i, this.#e = !1;
119
+ }
120
+ get canComplete() {
121
+ return this.#e;
122
+ }
123
+ allowComplete() {
124
+ this.#e = !0;
125
+ }
126
+ get id() {
127
+ return this.#s;
128
+ }
129
+ get blob() {
130
+ return this.#t;
131
+ }
132
+ get completeURL() {
133
+ return this.#i;
134
+ }
135
+ get uploadURL() {
136
+ return this.#a;
137
+ }
138
+ }
139
+ const L = {
140
+ concurrentUploads: 4,
141
+ retryLimit: 3
142
+ };
143
+ class _ {
144
+ #s;
145
+ #t;
146
+ #a;
147
+ #i;
148
+ #e;
149
+ #n;
150
+ constructor(t, e, i = L, a) {
151
+ this.#s = t, this.#t = e, this.#a = i.concurrentUploads, this.#i = i.retryLimit, this.#e = new p(), this.#n = a;
152
+ }
153
+ #o(t) {
154
+ return t < this.#i;
155
+ }
156
+ #r(t) {
157
+ this.#e.enqueue(t);
158
+ }
159
+ async init() {
160
+ this.#t.init(this.#s);
161
+ const t = /* @__PURE__ */ new Set();
162
+ for await (const e of this.#m()) {
163
+ const i = e().finally(() => t.delete(i));
164
+ t.add(i), t.size >= this.#a && await Promise.race(t);
165
+ }
166
+ await Promise.all(t), await this.#y(), this.#t.completed();
167
+ }
168
+ async #l(t, e) {
169
+ return await fetch(t, {
170
+ method: u,
171
+ body: e
172
+ });
173
+ }
174
+ async #d(t, e = 0) {
175
+ if (!t.canComplete)
176
+ try {
177
+ if (!(await this.#l(
178
+ t.uploadURL,
179
+ t.blob
180
+ )).ok)
181
+ throw new n(
182
+ `Failed to upload file blob for media item ${t.id}`
183
+ );
184
+ t.allowComplete(), await this.#h(t);
185
+ } catch (i) {
186
+ const a = l(i);
187
+ this.#o(e) ? (this.#t.uploadRetrying(t.id), this.#r({
188
+ type: "singlepart-upload",
189
+ retryCount: e + 1,
190
+ singlepartObj: t
191
+ })) : this.#t.failed(t.id, a);
192
+ }
193
+ }
194
+ async #c(t, e, i = 0) {
195
+ if (!(t.failed || t.contains(e.partNumber)))
196
+ try {
197
+ const a = await this.#l(
198
+ e.uploadURL,
199
+ t.blob.slice(e.startByte, e.endByte)
200
+ );
201
+ if (!a.ok)
202
+ throw new n(
203
+ `Failed to upload part ${e.partNumber} for media item ${t.id}`
204
+ );
205
+ const o = a.headers.get("ETag");
206
+ if (!o)
207
+ throw new n("missing etag");
208
+ if (t.add({
209
+ partNumber: e.partNumber,
210
+ etag: o
211
+ }), this.#t.multipartPartUploaded(
212
+ t.id,
213
+ t.uploadedPartsCount,
214
+ t.totalPartsCount
215
+ ), !t.canComplete) return;
216
+ await this.#u(t);
217
+ } catch (a) {
218
+ const o = l(a);
219
+ this.#o(i) ? (this.#t.multipartPartUploadRetrying(
220
+ t.id,
221
+ e.partNumber
222
+ ), this.#r({
223
+ type: "multipart-part-upload",
224
+ retryCount: i + 1,
225
+ multipartObj: t,
226
+ partInfo: e
227
+ })) : (t.fail(), this.#t.failed(t.id, o));
228
+ }
229
+ }
230
+ async #p(t, e) {
231
+ let i, a;
232
+ e && (i = JSON.stringify({
233
+ partsInfo: e
234
+ }), a = {
235
+ [g]: w
236
+ }), this.#n && (a = a || {}, a[m] = this.#n);
237
+ const o = await fetch(t, {
238
+ method: y,
239
+ body: i,
240
+ headers: a,
241
+ credentials: f
242
+ });
243
+ await h(o);
244
+ }
245
+ async #h(t, e = 0) {
246
+ if (t.canComplete)
247
+ try {
248
+ await this.#p(t.completeURL), this.#t.itemUploaded(t.id);
249
+ } catch (i) {
250
+ const a = l(i);
251
+ this.#o(e) ? (this.#t.uploadRetrying(t.id), this.#r({
252
+ type: "singlepart-complete",
253
+ retryCount: e + 1,
254
+ singlepartObj: t
255
+ })) : this.#t.failed(t.id, a);
256
+ }
257
+ }
258
+ async #u(t, e = 0) {
259
+ if (t.tryStartComplete())
260
+ try {
261
+ await this.#p(
262
+ t.completeURL,
263
+ t.list
264
+ ), this.#t.itemUploaded(t.id);
265
+ } catch (i) {
266
+ t.resetComplete();
267
+ const a = l(i);
268
+ this.#o(e) ? (this.#t.uploadRetrying(t.id), this.#r({
269
+ type: "multipart-complete",
270
+ retryCount: e + 1,
271
+ multipartObj: t
272
+ })) : (t.fail(), this.#t.failed(t.id, a));
273
+ }
274
+ }
275
+ async *#m() {
276
+ for (const t of this.#s) {
277
+ if (t.uploadType === "singlepart") {
278
+ const o = new N(
279
+ t.mediaID,
280
+ t.file,
281
+ t.presignPut,
282
+ t.singlepartSuccessCallback
283
+ );
284
+ yield async () => {
285
+ await this.#d(o);
286
+ };
287
+ continue;
288
+ }
289
+ const e = new S(
290
+ t.mediaID,
291
+ t.file,
292
+ t.multipartSuccessCallback,
293
+ t.multipartPresignPart.length
294
+ );
295
+ let i = 0, a = 0;
296
+ for (const o of t.multipartPresignPart) {
297
+ a += o.partSize;
298
+ const d = {
299
+ partNumber: o.partNumber,
300
+ uploadURL: o.presignPut,
301
+ startByte: i,
302
+ endByte: a
303
+ };
304
+ yield async () => {
305
+ await this.#c(e, d);
306
+ }, i = a;
307
+ }
308
+ }
309
+ }
310
+ #f(t) {
311
+ switch (t.type) {
312
+ case "singlepart-upload":
313
+ return this.#d(
314
+ t.singlepartObj,
315
+ t.retryCount
316
+ );
317
+ case "singlepart-complete":
318
+ return this.#h(
319
+ t.singlepartObj,
320
+ t.retryCount
321
+ );
322
+ case "multipart-part-upload":
323
+ return this.#c(
324
+ t.multipartObj,
325
+ t.partInfo,
326
+ t.retryCount
327
+ );
328
+ case "multipart-complete":
329
+ return this.#u(
330
+ t.multipartObj,
331
+ t.retryCount
332
+ );
333
+ default:
334
+ const e = t;
335
+ throw new n(
336
+ `Unhandled retry task type: ${e.type}`
337
+ );
338
+ }
339
+ }
340
+ async #y() {
341
+ const t = /* @__PURE__ */ new Set();
342
+ for (; !this.#e.isEmpty(); ) {
343
+ for (; !this.#e.isEmpty(); ) {
344
+ const e = this.#e.dequeue();
345
+ let i = this.#f(e).finally(
346
+ () => t.delete(i)
347
+ );
348
+ t.add(i), t.size >= this.#a && await Promise.race(t);
349
+ }
350
+ await Promise.all(t);
351
+ }
352
+ }
353
+ }
354
+ const Q = async (s, t, e, i) => {
355
+ await D([s], t, e, i);
356
+ }, D = async (s, t, e, i) => {
357
+ await new _(s, t, e, i).init();
358
+ };
359
+ export {
360
+ B as U,
361
+ $ as a,
362
+ R as b,
363
+ q as c,
364
+ k as d,
365
+ D as e,
366
+ T as n,
367
+ E as t,
368
+ Q as u
369
+ };
@@ -0,0 +1,5 @@
1
+ export declare const HTTP_GET = "GET";
2
+ export declare const HTTP_POST = "POST";
3
+ export declare const HTTP_PUT = "PUT";
4
+ export declare const HTTP_PATCH = "PATCH";
5
+ export declare const HTTP_DELETE = "DELETE";
@@ -0,0 +1,3 @@
1
+ export declare const HTTP_REQUEST_CREDENTIALS_INCLUDE = "include";
2
+ export declare const HTTP_REQUEST_CREDENTIALS_SAME_ORIGIN = "same-origin";
3
+ export declare const HTTP_REQUEST_CREDENTIALS_NONE = "none";
@@ -0,0 +1,6 @@
1
+ export declare const HTTP_HEADER_CONTENT_TYPE = "Content-Type";
2
+ export declare const HTTP_HEADER_CONTENT_TYPE_APPLICATION_JSON = "application/json";
3
+ export declare const HTTP_HEADER_AUTHORIZATION = "Authorization";
4
+ export declare const HTTP_HEADER_AUTHORIZATION_BEARER_SCHEME = "Bearer";
5
+ export declare const HTTP_HEADER_AUTHORIZATION_BASIC_SCHEME = "Basic";
6
+ export declare const HTTP_HEADER_USER_LOCALE = "x-user-locale";
@@ -0,0 +1,3 @@
1
+ export * from './http';
2
+ export * from './httpRequestHeaders';
3
+ export * from './httpRequest';
@@ -0,0 +1,17 @@
1
+ const T = "GET", E = "POST", _ = "PUT", H = "PATCH", A = "DELETE", P = "Content-Type", n = "application/json", o = "Authorization", t = "Bearer", R = "Basic", c = "x-user-locale", s = "include", N = "same-origin", I = "none";
2
+ export {
3
+ A as HTTP_DELETE,
4
+ T as HTTP_GET,
5
+ o as HTTP_HEADER_AUTHORIZATION,
6
+ R as HTTP_HEADER_AUTHORIZATION_BASIC_SCHEME,
7
+ t as HTTP_HEADER_AUTHORIZATION_BEARER_SCHEME,
8
+ P as HTTP_HEADER_CONTENT_TYPE,
9
+ n as HTTP_HEADER_CONTENT_TYPE_APPLICATION_JSON,
10
+ c as HTTP_HEADER_USER_LOCALE,
11
+ H as HTTP_PATCH,
12
+ E as HTTP_POST,
13
+ _ as HTTP_PUT,
14
+ s as HTTP_REQUEST_CREDENTIALS_INCLUDE,
15
+ I as HTTP_REQUEST_CREDENTIALS_NONE,
16
+ N as HTTP_REQUEST_CREDENTIALS_SAME_ORIGIN
17
+ };
@@ -0,0 +1,8 @@
1
+ import { A as s, B as e, n as a } from "../chunks/apiError";
2
+ import { p as E } from "../chunks/parse";
3
+ export {
4
+ s as APIError,
5
+ e as BaseError,
6
+ a as newAPIErrorResponse,
7
+ E as parseError
8
+ };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
+ export * from './constants';
1
2
  export * from './errors';
3
+ export * from './media';
2
4
  export * from './response';