@mainframework/api-request-worker 1.0.0 → 1.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.
@@ -1,7 +1,6 @@
1
- import type { Dispatch, SetStateAction } from "react";
2
1
  /** When "binary" or "stream", in-flight dedupe is skipped so we always run the request and return real response (no stale). */
3
- export type ResponseType = "json" | "binary" | "stream";
4
- export interface RequestConfig {
2
+ type ResponseType = "json" | "binary" | "stream";
3
+ interface RequestConfig {
5
4
  url: string;
6
5
  method: "GET" | "get" | "POST" | "post" | "PATCH" | "patch" | "DELETE" | "delete";
7
6
  mode?: "cors" | "no-cors" | "navigate" | "same-origin";
@@ -17,18 +16,20 @@ export interface RequestConfig {
17
16
  formDataKey?: string;
18
17
  /** For responseType "stream": number of retries on connection loss (default 3). Capped at 5 in the worker. */
19
18
  retries?: number;
19
+ /** For responseType "stream": flush batch to consumer every N chunks (default 5). */
20
+ streamChunkBatchSize?: number;
20
21
  }
21
- export type RunMode = "auto" | "manual" | "once";
22
- export interface UseApiWorkerConfig {
22
+ type RunMode = "auto" | "manual" | "once";
23
+ interface UseApiWorkerConfig {
23
24
  cacheName: string;
24
25
  request?: RequestConfig;
25
26
  data?: unknown;
26
27
  runMode?: RunMode;
27
28
  enabled?: boolean;
28
29
  }
29
- export type WorkerDataRequestType = "get" | "set" | "delete" | "cancel";
30
- export type WorkerApiRequest = RequestConfig;
31
- export interface DataRequest<T = unknown> {
30
+ type WorkerDataRequestType = "get" | "set" | "delete" | "cancel";
31
+ type WorkerApiRequest = RequestConfig;
32
+ interface DataRequest<T = unknown> {
32
33
  hookId?: string;
33
34
  cacheName?: string;
34
35
  type: WorkerDataRequestType;
@@ -37,41 +38,31 @@ export interface DataRequest<T = unknown> {
37
38
  /** Required for cancel, optional for set (enables cancellation). */
38
39
  requestId?: string | null;
39
40
  }
40
- export interface BinaryResponseMeta {
41
+ interface BinaryResponseMeta {
41
42
  contentType?: string;
42
43
  contentDisposition: string | null;
43
44
  }
44
45
  /** Type-only: binary parse results use Symbol.for("WorkerApiBinary"). Value lives in api.worker. */
45
46
  declare const BINARY_MARKER: unique symbol;
46
- export type BinaryParseResult = {
47
+ type BinaryParseResult = {
47
48
  [BINARY_MARKER]: true;
48
49
  data: ArrayBuffer;
49
50
  contentType: string;
50
51
  } & Pick<BinaryResponseMeta, "contentDisposition">;
51
52
  /** Worker always sends this shape; no error = { message: "" }. */
52
- export interface WorkerErrorPayload {
53
+ interface WorkerErrorPayload {
53
54
  message: string;
54
55
  }
55
- export interface QueueEntry<T> {
56
- hookId: string;
57
- cacheName: string;
58
- loading: boolean | null;
59
- data: T | null;
60
- meta: BinaryResponseMeta | null;
61
- error: string | null;
62
- setUpdateTrigger: Dispatch<SetStateAction<number>> | null;
63
- requestId: string | null;
64
- lastActivityAt: number | null;
65
- }
66
- export interface UseApiWorkerReturn<T> {
56
+ interface UseApiWorkerReturn<T> {
67
57
  data: T | null;
68
58
  meta: BinaryResponseMeta | null;
69
59
  loading: boolean;
70
60
  error: string | null;
71
61
  refetch: () => void;
72
62
  deleteCache: () => void;
63
+ /** For responseType "stream": batch of chunks since last flush. Undefined for non-stream. */
64
+ streamChunks?: ArrayBuffer[] | undefined;
73
65
  }
74
- export type AbortControllers = Map<string, AbortController>;
75
66
  /**
76
67
  * Worker response messages. Use in client onmessage handler:
77
68
  * - data + meta: binary response (data is ArrayBuffer). Reconstruct: new Blob([data], { type: meta?.contentType })
@@ -79,7 +70,7 @@ export type AbortControllers = Map<string, AbortController>;
79
70
  * - stream: "start" | "chunk" | "end" — client handles chunk sequence then finalizes
80
71
  * - error: error response with message and code
81
72
  */
82
- export type WorkerResponseMessage = {
73
+ type WorkerResponseMessage = {
83
74
  cacheName: string;
84
75
  data: unknown;
85
76
  error: WorkerErrorPayload;
@@ -123,7 +114,7 @@ export type WorkerResponseMessage = {
123
114
  * No error = { message: "" }. With error = { message: "..." }.
124
115
  * When stream is present, client receives start → chunk(s) → end; cancel via existing requestId/cancel.
125
116
  */
126
- export type WorkerMessagePayload = {
117
+ type WorkerMessagePayload = {
127
118
  cacheName?: string;
128
119
  data?: unknown;
129
120
  meta?: BinaryResponseMeta;
@@ -156,13 +147,9 @@ export type WorkerMessagePayload = {
156
147
  hookId?: string;
157
148
  error: WorkerErrorPayload;
158
149
  };
159
- export interface StackArray {
160
- key: string;
161
- value: unknown;
162
- }
163
- export type ContentType = "application/json" | "application/xml" | "application/x-www-form-urlencoded" | "application/pdf" | "application/zip" | "application/gzip" | "application/octet-stream" | "application/javascript" | "application/ld+json" | "application/vnd.api+json" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "text/plain" | "text/html" | "text/css" | "text/csv" | "text/javascript" | "text/xml" | "image/jpeg" | "image/png" | "image/gif" | "image/svg+xml" | "image/webp" | "image/bmp" | "image/tiff" | "image/x-icon" | "image/avif" | "audio/mpeg" | "audio/ogg" | "audio/wav" | "audio/webm" | "audio/aac" | "audio/midi" | "video/mp4" | "video/mpeg" | "video/webm" | "video/ogg" | "video/quicktime" | "video/x-msvideo" | "multipart/form-data" | "multipart/mixed" | "multipart/alternative" | "font/woff" | "font/woff2" | "font/ttf" | "font/otf";
164
- export type WorkerMessageData = {
150
+ type ContentType = "application/json" | "application/xml" | "application/x-www-form-urlencoded" | "application/pdf" | "application/zip" | "application/gzip" | "application/octet-stream" | "application/javascript" | "application/ld+json" | "application/vnd.api+json" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "text/plain" | "text/html" | "text/css" | "text/csv" | "text/javascript" | "text/xml" | "image/jpeg" | "image/png" | "image/gif" | "image/svg+xml" | "image/webp" | "image/bmp" | "image/tiff" | "image/x-icon" | "image/avif" | "audio/mpeg" | "audio/ogg" | "audio/wav" | "audio/webm" | "audio/aac" | "audio/midi" | "video/mp4" | "video/mpeg" | "video/webm" | "video/ogg" | "video/quicktime" | "video/x-msvideo" | "multipart/form-data" | "multipart/mixed" | "multipart/alternative" | "font/woff" | "font/woff2" | "font/ttf" | "font/otf";
151
+ type WorkerMessageData = {
165
152
  dataRequest?: DataRequest<unknown>;
166
153
  };
167
- export {};
168
- //# sourceMappingURL=types.d.ts.map
154
+
155
+ export type { BinaryParseResult as B, ContentType as C, DataRequest as D, RequestConfig as R, UseApiWorkerConfig as U, WorkerDataRequestType as W, BinaryResponseMeta as a, ResponseType as b, RunMode as c, WorkerErrorPayload as d, WorkerMessageData as e, WorkerMessagePayload as f, WorkerResponseMessage as g, UseApiWorkerReturn as h };
@@ -0,0 +1,433 @@
1
+ const BINARY_MARKER = /* @__PURE__ */ Symbol.for("WorkerApiBinary");
2
+ const DEFAULT_FILES_FIELD = "Files";
3
+ const NO_ERROR = { message: "" };
4
+ const callerResponse = (cacheName, data, hookId, httpStatus, error = NO_ERROR) => {
5
+ self.postMessage({
6
+ cacheName,
7
+ data: error.message !== "" ? null : data ?? null,
8
+ hookId,
9
+ httpStatus,
10
+ error
11
+ });
12
+ };
13
+ const makeError = (message) => ({ message });
14
+ const callerResponseBinary = (cacheName, data, meta, hookId, httpStatus, error = NO_ERROR) => {
15
+ const buffer = data?.byteLength ? data : new ArrayBuffer(0);
16
+ const payload = { cacheName, data: buffer, meta, hookId, httpStatus, error };
17
+ self.postMessage(payload, buffer.byteLength > 0 ? [buffer] : []);
18
+ };
19
+ const callerResponseStreamStart = (cacheName, meta, hookId, httpStatus, error = NO_ERROR) => {
20
+ self.postMessage({ cacheName, stream: "start", meta, hookId, httpStatus, error });
21
+ };
22
+ const callerResponseStreamResume = (cacheName, meta, hookId, httpStatus, error = NO_ERROR) => {
23
+ self.postMessage({ cacheName, stream: "resume", meta, hookId, httpStatus, error });
24
+ };
25
+ const callerResponseStreamChunk = (cacheName, data, hookId, error = NO_ERROR) => {
26
+ const buffer = data?.byteLength ? data : new ArrayBuffer(0);
27
+ const payload = { cacheName, stream: "chunk", data: buffer, hookId, error };
28
+ self.postMessage(payload, buffer.byteLength > 0 ? [buffer] : []);
29
+ };
30
+ const callerResponseStreamEnd = (cacheName, hookId, error = NO_ERROR) => {
31
+ self.postMessage({ cacheName, stream: "end", hookId, error });
32
+ };
33
+ const transferableBuffer = (view) => view.byteOffset === 0 && view.byteLength === view.buffer.byteLength ? view.buffer : view.slice(0).buffer;
34
+ const store = /* @__PURE__ */ Object.create(null);
35
+ const normalizeKey = (key) => key.toLocaleLowerCase();
36
+ const get = (key) => store[normalizeKey(key)];
37
+ const set = (key, value) => {
38
+ store[normalizeKey(key)] = value;
39
+ };
40
+ const remove = (key) => {
41
+ delete store[normalizeKey(key)];
42
+ };
43
+ const isNonEmptyString = (v) => typeof v === "string" && v.trim() !== "";
44
+ const omitContentType = (headers) => {
45
+ const rest = { ...headers };
46
+ delete rest["Content-Type"];
47
+ delete rest["content-type"];
48
+ return rest;
49
+ };
50
+ const isBinaryResponse = (r) => typeof r === "object" && r !== null && BINARY_MARKER in r && r.data instanceof ArrayBuffer;
51
+ const commit = (cacheName, data, hookId, httpStatus) => {
52
+ if (!cacheName) {
53
+ callerResponse("", null, hookId, void 0, makeError("Invalid commit: cacheName is required"));
54
+ return;
55
+ }
56
+ set(normalizeKey(cacheName), data);
57
+ callerResponse(cacheName, data, hookId, httpStatus);
58
+ };
59
+ const parseResponseByContentType = async (response) => {
60
+ const contentType = response.headers.get("content-type")?.toLocaleLowerCase() || "";
61
+ const contentLength = response.headers.get("content-length");
62
+ if (response.status === 204 || contentLength === "0") {
63
+ return null;
64
+ }
65
+ if (contentType.includes("json")) {
66
+ return await response.json();
67
+ }
68
+ if (contentType.startsWith("text/") || contentType.includes("xml") || contentType.includes("javascript") || contentType.includes("x-www-form-urlencoded")) {
69
+ return await response.text();
70
+ }
71
+ const arrayBuffer = await response.arrayBuffer();
72
+ return {
73
+ [BINARY_MARKER]: true,
74
+ data: arrayBuffer,
75
+ contentType,
76
+ contentDisposition: response.headers.get("content-disposition")
77
+ };
78
+ };
79
+ const appendToFormData = (formData, key, value, fileFieldName = DEFAULT_FILES_FIELD, visited) => {
80
+ if (value instanceof File) {
81
+ formData.append(fileFieldName, value, value.name);
82
+ return true;
83
+ }
84
+ if (value instanceof Blob) {
85
+ formData.append(fileFieldName, value, "blob");
86
+ return true;
87
+ }
88
+ if (value !== null && value !== void 0 && typeof value === "object") {
89
+ const set2 = visited ?? /* @__PURE__ */ new WeakSet();
90
+ if (set2.has(value)) return false;
91
+ set2.add(value);
92
+ if (Array.isArray(value)) {
93
+ let hasFile2 = false;
94
+ let i = 0;
95
+ while (i < value.length) {
96
+ hasFile2 = appendToFormData(formData, key ? `${key}.${i}` : String(i), value[i], fileFieldName, set2) || hasFile2;
97
+ i++;
98
+ }
99
+ return hasFile2;
100
+ }
101
+ let hasFile = false;
102
+ const keys = Object.keys(value);
103
+ let ki = 0;
104
+ while (ki < keys.length) {
105
+ const k = keys[ki];
106
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
107
+ hasFile = appendToFormData(
108
+ formData,
109
+ key ? `${key}.${k}` : k,
110
+ value[k],
111
+ fileFieldName,
112
+ set2
113
+ ) || hasFile;
114
+ }
115
+ ki++;
116
+ }
117
+ return hasFile;
118
+ }
119
+ if (value !== void 0 && value !== null) {
120
+ const primitive = value;
121
+ formData.append(key, primitive);
122
+ }
123
+ return false;
124
+ };
125
+ const createFormDataIfBlobOrFile = (payload, fileFieldName = DEFAULT_FILES_FIELD, formDataKey = DEFAULT_FILES_FIELD) => {
126
+ if (payload === null || payload === void 0 || typeof payload !== "object") {
127
+ return null;
128
+ }
129
+ const formData = new FormData();
130
+ const visited = /* @__PURE__ */ new WeakSet();
131
+ const hasFile = appendToFormData(formData, formDataKey, payload, fileFieldName, visited);
132
+ return hasFile ? formData : null;
133
+ };
134
+ const getContentType = (headers = {}) => headers["Content-Type"] || headers["content-type"] || "application/json";
135
+ const getPayloadType = (payload) => {
136
+ switch (true) {
137
+ case payload instanceof FormData:
138
+ return "formdata";
139
+ case payload instanceof Blob:
140
+ return "blob";
141
+ case payload instanceof ArrayBuffer:
142
+ return "arraybuffer";
143
+ case ArrayBuffer.isView(payload):
144
+ return "arraybufferview";
145
+ case (typeof ReadableStream !== "undefined" && payload instanceof ReadableStream):
146
+ return "stream";
147
+ case typeof payload === "string":
148
+ return "string";
149
+ default:
150
+ return "object";
151
+ }
152
+ };
153
+ const buildJsonBody = (payload) => JSON.stringify(payload);
154
+ const buildUrlEncodedBody = (payload) => new URLSearchParams(payload).toString();
155
+ const buildTextBody = (payload) => String(payload);
156
+ const prepareRequestBody = (payload, headers, options) => {
157
+ const payloadType = getPayloadType(payload);
158
+ switch (payloadType) {
159
+ case "formdata":
160
+ return { body: payload, headers: omitContentType({ ...headers }) };
161
+ case "blob":
162
+ return { body: payload, headers: { ...headers } };
163
+ case "arraybuffer":
164
+ return { body: payload, headers: { ...headers } };
165
+ case "arraybufferview":
166
+ return { body: payload, headers: { ...headers } };
167
+ case "stream":
168
+ return { body: payload, headers: { ...headers } };
169
+ case "string":
170
+ return { body: payload, headers: { ...headers } };
171
+ case "object": {
172
+ const h = { ...headers };
173
+ const contentType = getContentType(h);
174
+ const fileFieldName = options?.formDataFileFieldName ?? DEFAULT_FILES_FIELD;
175
+ const formDataKey = options?.formDataKey ?? DEFAULT_FILES_FIELD;
176
+ let body;
177
+ if (contentType.includes("application/json")) {
178
+ body = buildJsonBody(payload);
179
+ } else if (contentType.includes("application/x-www-form-urlencoded")) {
180
+ body = buildUrlEncodedBody(payload);
181
+ } else if (contentType.startsWith("text/") || contentType.includes("xml")) {
182
+ body = buildTextBody(payload);
183
+ } else if (contentType.includes("multipart/form-data")) {
184
+ const formData = createFormDataIfBlobOrFile(payload, fileFieldName, formDataKey);
185
+ if (formData) {
186
+ return { body: formData, headers: omitContentType(h) };
187
+ }
188
+ body = buildJsonBody(payload);
189
+ h["Content-Type"] = "application/json";
190
+ } else {
191
+ body = buildJsonBody(payload);
192
+ }
193
+ if (!h["Content-Type"] && !h["content-type"]) {
194
+ h["Content-Type"] = contentType;
195
+ }
196
+ return { body, headers: h };
197
+ }
198
+ default:
199
+ return { headers: { ...headers } };
200
+ }
201
+ };
202
+ const inFlightControllers = /* @__PURE__ */ new Map();
203
+ const inFlightByCacheName = /* @__PURE__ */ new Map();
204
+ const apiRequest = async (cacheName, payload, {
205
+ url,
206
+ method,
207
+ headers = {},
208
+ mode = "cors",
209
+ credentials = "same-origin",
210
+ responseType,
211
+ timeoutMs,
212
+ formDataFileFieldName,
213
+ formDataKey,
214
+ retries
215
+ }, requestId, hookId) => {
216
+ const methodLower = method.toLocaleLowerCase();
217
+ const responseTypeLower = responseType?.toLocaleLowerCase();
218
+ const skipInFlightDedupe = responseTypeLower === "binary" || responseTypeLower === "stream";
219
+ if (!skipInFlightDedupe) {
220
+ const existing = inFlightByCacheName.get(cacheName);
221
+ if (existing) {
222
+ const cached = get(cacheName);
223
+ if (cached !== void 0) callerResponse(cacheName, cached, hookId);
224
+ await existing;
225
+ const fresh = get(cacheName);
226
+ if (fresh !== void 0) callerResponse(cacheName, fresh, hookId);
227
+ return;
228
+ }
229
+ }
230
+ const controller = new AbortController();
231
+ if (requestId) {
232
+ inFlightControllers.set(requestId, controller);
233
+ }
234
+ let timeoutId;
235
+ if (timeoutMs != null && timeoutMs > 0) {
236
+ timeoutId = setTimeout(() => controller.abort(), timeoutMs);
237
+ }
238
+ const promise = (async () => {
239
+ const fetchOptions = {
240
+ method,
241
+ mode,
242
+ credentials,
243
+ signal: controller.signal
244
+ };
245
+ if (methodLower !== "get" && payload != null) {
246
+ const prepareOptions = {};
247
+ if (formDataFileFieldName != null && formDataFileFieldName !== "")
248
+ prepareOptions.formDataFileFieldName = formDataFileFieldName;
249
+ if (formDataKey != null && formDataKey !== "") prepareOptions.formDataKey = formDataKey;
250
+ const { body, headers: processedHeaders } = prepareRequestBody(payload, headers, prepareOptions);
251
+ if (body !== void 0) fetchOptions.body = body;
252
+ fetchOptions.headers = processedHeaders;
253
+ } else {
254
+ fetchOptions.headers = omitContentType({ ...headers });
255
+ }
256
+ try {
257
+ if (responseTypeLower === "stream") {
258
+ const maxRetries = Math.min(retries ?? 3, 5);
259
+ let bytesReceived = 0;
260
+ let streamError = NO_ERROR;
261
+ let attempt = 0;
262
+ while (attempt <= maxRetries) {
263
+ try {
264
+ const reqHeaders = methodLower === "get" ? omitContentType({ ...fetchOptions.headers }) : fetchOptions.headers;
265
+ if (bytesReceived > 0) reqHeaders["Range"] = `bytes=${bytesReceived}-`;
266
+ const streamResponse = await fetch(url, { ...fetchOptions, headers: reqHeaders });
267
+ if (streamResponse.status >= 400) {
268
+ streamError = makeError(streamResponse.statusText);
269
+ break;
270
+ }
271
+ if (streamResponse.status === 204) {
272
+ callerResponseStreamEnd(cacheName, hookId);
273
+ return;
274
+ }
275
+ if (streamResponse.status === 416) break;
276
+ const contentType = streamResponse.headers.get("content-type") ?? void 0;
277
+ const meta = {
278
+ contentDisposition: streamResponse.headers.get("content-disposition") ?? null,
279
+ ...contentType !== void 0 && { contentType }
280
+ };
281
+ if (bytesReceived === 0) callerResponseStreamStart(cacheName, meta, hookId, streamResponse.status);
282
+ else callerResponseStreamResume(cacheName, meta, hookId, streamResponse.status);
283
+ const body = streamResponse.body;
284
+ if (!body) {
285
+ callerResponseStreamEnd(cacheName, hookId);
286
+ return;
287
+ }
288
+ const reader = body.getReader();
289
+ let skipRemaining = streamResponse.status === 200 && bytesReceived > 0 ? bytesReceived : 0;
290
+ while (true) {
291
+ const { done, value } = await reader.read();
292
+ if (done) break;
293
+ if (!value || value.byteLength === 0) continue;
294
+ if (skipRemaining > 0) {
295
+ if (value.byteLength <= skipRemaining) {
296
+ skipRemaining -= value.byteLength;
297
+ continue;
298
+ }
299
+ const offset = skipRemaining;
300
+ skipRemaining = 0;
301
+ const tail = value.subarray(offset);
302
+ callerResponseStreamChunk(cacheName, transferableBuffer(tail), hookId);
303
+ bytesReceived += tail.byteLength;
304
+ } else {
305
+ callerResponseStreamChunk(cacheName, transferableBuffer(value), hookId);
306
+ bytesReceived += value.byteLength;
307
+ }
308
+ }
309
+ break;
310
+ } catch (err) {
311
+ streamError = err.name === "AbortError" ? makeError("Request aborted") : makeError(err.message);
312
+ if (attempt === maxRetries) break;
313
+ }
314
+ attempt++;
315
+ }
316
+ callerResponseStreamEnd(cacheName, hookId, streamError);
317
+ return;
318
+ }
319
+ const response = await fetch(url, fetchOptions);
320
+ if (response.status >= 400) {
321
+ callerResponse(cacheName, null, hookId, response.status, makeError(response.statusText));
322
+ return;
323
+ }
324
+ if (response.status === 204) {
325
+ set(normalizeKey(cacheName), null);
326
+ callerResponse(cacheName, null, hookId, 204);
327
+ return;
328
+ }
329
+ const responseData = await parseResponseByContentType(response);
330
+ if (isBinaryResponse(responseData)) {
331
+ callerResponseBinary(
332
+ cacheName,
333
+ responseData.data,
334
+ {
335
+ contentType: responseData.contentType,
336
+ contentDisposition: responseData.contentDisposition ?? null
337
+ },
338
+ hookId,
339
+ response.status
340
+ );
341
+ } else {
342
+ commit(cacheName, responseData, hookId, response.status);
343
+ }
344
+ } catch (error) {
345
+ if (error.name === "AbortError") {
346
+ callerResponse(cacheName, null, hookId, void 0, makeError("Request aborted"));
347
+ return;
348
+ }
349
+ const err = error;
350
+ callerResponse(cacheName, null, hookId, void 0, makeError(err.message));
351
+ } finally {
352
+ if (timeoutId != null) clearTimeout(timeoutId);
353
+ if (requestId) {
354
+ inFlightControllers.delete(requestId);
355
+ }
356
+ inFlightByCacheName.delete(cacheName);
357
+ }
358
+ })();
359
+ inFlightByCacheName.set(cacheName, promise);
360
+ await promise;
361
+ };
362
+ const onRequest = (dataRequest) => {
363
+ const { cacheName, type, payload, request, requestId, hookId } = dataRequest;
364
+ const responseCacheName = dataRequest.cacheName ?? "";
365
+ if (!isNonEmptyString(type)) {
366
+ callerResponse(responseCacheName, null, hookId, void 0, makeError("Invalid request: type is required"));
367
+ return;
368
+ }
369
+ const lowerType = normalizeKey(type);
370
+ if (lowerType === "cancel") {
371
+ if (requestId) onCancel(requestId);
372
+ return;
373
+ }
374
+ if (!isNonEmptyString(cacheName)) {
375
+ callerResponse(responseCacheName, null, hookId, void 0, makeError("Invalid request: cacheName is required"));
376
+ return;
377
+ }
378
+ const lowerCacheName = normalizeKey(cacheName);
379
+ if (lowerType === "get") {
380
+ const requestedData = get(lowerCacheName);
381
+ if (requestedData === void 0) {
382
+ callerResponse(lowerCacheName, null, hookId, void 0, makeError("Cache miss"));
383
+ } else {
384
+ callerResponse(lowerCacheName, requestedData, hookId);
385
+ }
386
+ } else if (lowerType === "set") {
387
+ if (!request) {
388
+ if (payload == null) {
389
+ callerResponse(
390
+ responseCacheName,
391
+ null,
392
+ hookId,
393
+ void 0,
394
+ makeError("Invalid request: payload is required for set")
395
+ );
396
+ return;
397
+ }
398
+ set(lowerCacheName, payload);
399
+ } else {
400
+ const methodLower = normalizeKey(request.method);
401
+ if (methodLower !== "get" && payload == null) {
402
+ callerResponse(
403
+ responseCacheName,
404
+ null,
405
+ hookId,
406
+ void 0,
407
+ makeError("Invalid request: payload is required for non-GET API request")
408
+ );
409
+ return;
410
+ }
411
+ void apiRequest(lowerCacheName, payload ?? null, request, requestId, hookId);
412
+ }
413
+ } else if (lowerType === "delete") {
414
+ remove(lowerCacheName);
415
+ callerResponse(lowerCacheName, { deleted: true }, hookId);
416
+ }
417
+ };
418
+ const onCancel = (requestId) => {
419
+ const controller = inFlightControllers.get(requestId);
420
+ if (controller) {
421
+ controller.abort();
422
+ inFlightControllers.delete(requestId);
423
+ }
424
+ };
425
+ onmessage = (event) => {
426
+ const payload = event.data;
427
+ if (payload === null || typeof payload !== "object") return;
428
+ const dataRequest = payload.dataRequest;
429
+ if (dataRequest !== void 0) onRequest(dataRequest);
430
+ };
431
+
432
+ export { BINARY_MARKER };
433
+ //# sourceMappingURL=api.worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.worker.js","sources":["../../../src/shared/workers/api/api.worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\r\n\r\nimport type {\r\n AbortControllers,\r\n BinaryParseResult,\r\n BinaryResponseMeta,\r\n DataRequest,\r\n WorkerApiRequest,\r\n WorkerErrorPayload,\r\n WorkerMessageData,\r\n} from \"../../types/types\";\r\n\r\nexport const BINARY_MARKER = Symbol.for(\"WorkerApiBinary\");\r\n\r\nconst DEFAULT_FILES_FIELD = \"Files\";\r\n\r\nconst NO_ERROR: WorkerErrorPayload = { message: \"\" };\r\n\r\nconst callerResponse = (\r\n cacheName: string,\r\n data: unknown,\r\n hookId?: string | null,\r\n httpStatus?: number,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n self.postMessage({\r\n cacheName,\r\n data: error.message !== \"\" ? null : (data ?? null),\r\n hookId,\r\n httpStatus,\r\n error,\r\n });\r\n};\r\n\r\nconst makeError = (message: string): WorkerErrorPayload => ({ message });\r\n\r\n/**\r\n * Binary response - transferred via postMessage, not stored in cache.\r\n * Client receives ArrayBuffer + meta for reconstruction (e.g. new Blob([data], { type })).\r\n * Always sends a message; empty/zero-length body uses an empty ArrayBuffer so the client does not hang.\r\n */\r\nconst callerResponseBinary = (\r\n cacheName: string,\r\n data: ArrayBuffer,\r\n meta: BinaryResponseMeta,\r\n hookId?: string | null,\r\n httpStatus?: number,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n const buffer = data?.byteLength ? data : new ArrayBuffer(0);\r\n const payload = { cacheName, data: buffer, meta, hookId, httpStatus, error };\r\n self.postMessage(payload, buffer.byteLength > 0 ? [buffer] : []);\r\n};\r\n\r\nconst callerResponseStreamStart = (\r\n cacheName: string,\r\n meta: BinaryResponseMeta | null,\r\n hookId?: string | null,\r\n httpStatus?: number,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n self.postMessage({ cacheName, stream: \"start\", meta, hookId, httpStatus, error });\r\n};\r\n\r\nconst callerResponseStreamResume = (\r\n cacheName: string,\r\n meta: BinaryResponseMeta | null,\r\n hookId?: string | null,\r\n httpStatus?: number,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n self.postMessage({ cacheName, stream: \"resume\", meta, hookId, httpStatus, error });\r\n};\r\n\r\nconst callerResponseStreamChunk = (\r\n cacheName: string,\r\n data: ArrayBuffer,\r\n hookId?: string | null,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n const buffer = data?.byteLength ? data : new ArrayBuffer(0);\r\n const payload = { cacheName, stream: \"chunk\", data: buffer, hookId, error };\r\n self.postMessage(payload, buffer.byteLength > 0 ? [buffer] : []);\r\n};\r\n\r\nconst callerResponseStreamEnd = (\r\n cacheName: string,\r\n hookId?: string | null,\r\n error: WorkerErrorPayload = NO_ERROR,\r\n): void => {\r\n self.postMessage({ cacheName, stream: \"end\", hookId, error });\r\n};\r\n\r\nconst transferableBuffer = (view: Uint8Array): ArrayBuffer =>\r\n view.byteOffset === 0 && view.byteLength === view.buffer.byteLength\r\n ? (view.buffer as ArrayBuffer)\r\n : view.slice(0).buffer;\r\n\r\nconst store = Object.create(null) as Record<string, unknown>;\r\nconst normalizeKey = (key: string) => key.toLocaleLowerCase();\r\nconst get = <TData>(key: string): TData | undefined => store[normalizeKey(key)] as TData | undefined;\r\nconst set = <TData>(key: string, value: TData): void => {\r\n store[normalizeKey(key)] = value;\r\n};\r\nconst remove = (key: string): void => {\r\n delete store[normalizeKey(key)];\r\n};\r\n\r\nconst isNonEmptyString = (v: unknown): v is string => typeof v === \"string\" && v.trim() !== \"\";\r\n\r\n/** Returns headers without Content-Type (either casing). Use when FormData sets it automatically. */\r\nconst omitContentType = (headers: Record<string, string>): Record<string, string> => {\r\n const rest = { ...headers };\r\n delete rest[\"Content-Type\"];\r\n delete rest[\"content-type\"];\r\n return rest;\r\n};\r\n\r\nconst isBinaryResponse = (r: unknown): r is BinaryParseResult =>\r\n typeof r === \"object\" &&\r\n r !== null &&\r\n BINARY_MARKER in r &&\r\n (r as unknown as BinaryParseResult).data instanceof ArrayBuffer;\r\n\r\nconst commit = <TData>(cacheName: string, data: TData, hookId?: string | null, httpStatus?: number): void => {\r\n if (!cacheName) {\r\n callerResponse(\"\", null, hookId, undefined, makeError(\"Invalid commit: cacheName is required\"));\r\n return;\r\n }\r\n set(normalizeKey(cacheName), data);\r\n callerResponse(cacheName, data, hookId, httpStatus);\r\n};\r\n\r\n/**\r\n * Parses response based on content-type.\r\n * Returns parsed data for JSON/text, or binary result with BINARY_MARKER, data (ArrayBuffer), contentType, contentDisposition.\r\n */\r\nconst parseResponseByContentType = async (response: Response): Promise<unknown> => {\r\n const contentType = response.headers.get(\"content-type\")?.toLocaleLowerCase() || \"\";\r\n const contentLength = response.headers.get(\"content-length\");\r\n\r\n // No content\r\n if (response.status === 204 || contentLength === \"0\") {\r\n return null;\r\n }\r\n\r\n // JSON types - let it throw if malformed\r\n if (contentType.includes(\"json\")) {\r\n return await response.json();\r\n }\r\n\r\n // Text-based types\r\n if (\r\n contentType.startsWith(\"text/\") ||\r\n contentType.includes(\"xml\") ||\r\n contentType.includes(\"javascript\") ||\r\n contentType.includes(\"x-www-form-urlencoded\")\r\n ) {\r\n return await response.text();\r\n }\r\n\r\n // Binary types (known or fallback) - use arrayBuffer for zero-copy transfer to client\r\n const arrayBuffer = await response.arrayBuffer();\r\n return {\r\n [BINARY_MARKER]: true as const,\r\n data: arrayBuffer,\r\n contentType,\r\n contentDisposition: response.headers.get(\"content-disposition\"),\r\n };\r\n};\r\n\r\n/** File/Blob use fileFieldName (default \"Files\"); all other fields use property name (key). */\r\nconst appendToFormData = (\r\n formData: FormData,\r\n key: string,\r\n value: unknown,\r\n fileFieldName: string = DEFAULT_FILES_FIELD,\r\n visited?: WeakSet<object>,\r\n): boolean => {\r\n if (value instanceof File) {\r\n formData.append(fileFieldName, value, value.name);\r\n return true;\r\n }\r\n\r\n if (value instanceof Blob) {\r\n formData.append(fileFieldName, value, \"blob\");\r\n return true;\r\n }\r\n\r\n if (value !== null && value !== undefined && typeof value === \"object\") {\r\n const set = visited ?? new WeakSet<object>();\r\n if (set.has(value)) return false;\r\n set.add(value);\r\n if (Array.isArray(value)) {\r\n let hasFile = false;\r\n let i = 0;\r\n while (i < value.length) {\r\n hasFile = appendToFormData(formData, key ? `${key}.${i}` : String(i), value[i], fileFieldName, set) || hasFile;\r\n i++;\r\n }\r\n return hasFile;\r\n }\r\n let hasFile = false;\r\n const keys = Object.keys(value);\r\n let ki = 0;\r\n while (ki < keys.length) {\r\n const k = keys[ki] as string;\r\n if (Object.prototype.hasOwnProperty.call(value, k)) {\r\n hasFile =\r\n appendToFormData(\r\n formData,\r\n key ? `${key}.${k}` : k,\r\n (value as Record<string, unknown>)[k],\r\n fileFieldName,\r\n set,\r\n ) || hasFile;\r\n }\r\n ki++;\r\n }\r\n return hasFile;\r\n }\r\n\r\n if (value !== undefined && value !== null) {\r\n const primitive = value as string | number | boolean | bigint | symbol;\r\n formData.append(key, primitive as string | Blob);\r\n }\r\n return false;\r\n};\r\n\r\nconst createFormDataIfBlobOrFile = (\r\n payload: unknown,\r\n fileFieldName: string = DEFAULT_FILES_FIELD,\r\n formDataKey: string = DEFAULT_FILES_FIELD,\r\n): FormData | null => {\r\n if (payload === null || payload === undefined || typeof payload !== \"object\") {\r\n return null;\r\n }\r\n\r\n const formData = new FormData();\r\n const visited = new WeakSet<object>();\r\n const hasFile = appendToFormData(formData, formDataKey, payload, fileFieldName, visited);\r\n return hasFile ? formData : null;\r\n};\r\n\r\nconst getContentType = (headers: Record<string, string> = {}): string =>\r\n headers[\"Content-Type\"] || headers[\"content-type\"] || \"application/json\";\r\n\r\nconst getPayloadType = (payload: unknown): string => {\r\n switch (true) {\r\n case payload instanceof FormData:\r\n return \"formdata\";\r\n case payload instanceof Blob:\r\n return \"blob\";\r\n case payload instanceof ArrayBuffer:\r\n return \"arraybuffer\";\r\n case ArrayBuffer.isView(payload):\r\n return \"arraybufferview\";\r\n case typeof ReadableStream !== \"undefined\" && payload instanceof ReadableStream:\r\n return \"stream\";\r\n case typeof payload === \"string\":\r\n return \"string\";\r\n default:\r\n return \"object\";\r\n }\r\n};\r\n\r\nconst buildJsonBody = (payload: unknown): BodyInit => JSON.stringify(payload);\r\nconst buildUrlEncodedBody = (payload: Record<string, string>): BodyInit => new URLSearchParams(payload).toString();\r\nconst buildTextBody = (payload: unknown): BodyInit => String(payload);\r\n\r\n/**\r\n * Builds body and headers for fetch. Each branch handles one payload type; add new cases here for new body types.\r\n */\r\nconst prepareRequestBody = (\r\n payload: unknown,\r\n headers: Record<string, string>,\r\n options?: { formDataFileFieldName?: string; formDataKey?: string },\r\n): { body?: BodyInit; headers: Record<string, string> } => {\r\n const payloadType = getPayloadType(payload);\r\n\r\n switch (payloadType) {\r\n case \"formdata\":\r\n return { body: payload as FormData, headers: omitContentType({ ...headers }) };\r\n case \"blob\":\r\n return { body: payload as Blob, headers: { ...headers } };\r\n case \"arraybuffer\":\r\n return { body: payload as ArrayBuffer, headers: { ...headers } };\r\n case \"arraybufferview\":\r\n return { body: payload as BodyInit, headers: { ...headers } };\r\n case \"stream\":\r\n return { body: payload as ReadableStream<Uint8Array>, headers: { ...headers } };\r\n case \"string\":\r\n return { body: payload as string, headers: { ...headers } };\r\n case \"object\": {\r\n const h = { ...headers };\r\n const contentType = getContentType(h);\r\n const fileFieldName = options?.formDataFileFieldName ?? DEFAULT_FILES_FIELD;\r\n const formDataKey = options?.formDataKey ?? DEFAULT_FILES_FIELD;\r\n\r\n let body: BodyInit;\r\n if (contentType.includes(\"application/json\")) {\r\n body = buildJsonBody(payload);\r\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\r\n body = buildUrlEncodedBody(payload as Record<string, string>);\r\n } else if (contentType.startsWith(\"text/\") || contentType.includes(\"xml\")) {\r\n body = buildTextBody(payload);\r\n } else if (contentType.includes(\"multipart/form-data\")) {\r\n const formData = createFormDataIfBlobOrFile(payload, fileFieldName, formDataKey);\r\n if (formData) {\r\n return { body: formData, headers: omitContentType(h) };\r\n }\r\n body = buildJsonBody(payload);\r\n h[\"Content-Type\"] = \"application/json\";\r\n } else {\r\n body = buildJsonBody(payload);\r\n }\r\n\r\n if (!h[\"Content-Type\"] && !h[\"content-type\"]) {\r\n h[\"Content-Type\"] = contentType;\r\n }\r\n return { body, headers: h };\r\n }\r\n\r\n default:\r\n return { headers: { ...headers } };\r\n }\r\n};\r\n\r\nconst inFlightControllers: AbortControllers = new Map();\r\nconst inFlightByCacheName = new Map<string, Promise<void>>();\r\n\r\nconst apiRequest = async <TData>(\r\n cacheName: string,\r\n payload: TData | FormData | null,\r\n {\r\n url,\r\n method,\r\n headers = {},\r\n mode = \"cors\",\r\n credentials = \"same-origin\",\r\n responseType,\r\n timeoutMs,\r\n formDataFileFieldName,\r\n formDataKey,\r\n retries,\r\n }: WorkerApiRequest,\r\n requestId?: string | null,\r\n hookId?: string | null,\r\n): Promise<void> => {\r\n const methodLower = method.toLocaleLowerCase();\r\n const responseTypeLower = responseType?.toLocaleLowerCase();\r\n\r\n // Early return (cached then fresh) allowed for any HTTP method, except when response is binary or streaming.\r\n const skipInFlightDedupe = responseTypeLower === \"binary\" || responseTypeLower === \"stream\";\r\n if (!skipInFlightDedupe) {\r\n const existing = inFlightByCacheName.get(cacheName);\r\n if (existing) {\r\n const cached = get(cacheName);\r\n if (cached !== undefined) callerResponse(cacheName, cached, hookId);\r\n await existing;\r\n const fresh = get(cacheName);\r\n if (fresh !== undefined) callerResponse(cacheName, fresh, hookId);\r\n return;\r\n }\r\n }\r\n\r\n const controller = new AbortController();\r\n if (requestId) {\r\n inFlightControllers.set(requestId, controller);\r\n }\r\n\r\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\r\n if (timeoutMs != null && timeoutMs > 0) {\r\n timeoutId = setTimeout(() => controller.abort(), timeoutMs);\r\n }\r\n\r\n const promise = (async (): Promise<void> => {\r\n const fetchOptions: RequestInit = {\r\n method,\r\n mode,\r\n credentials,\r\n signal: controller.signal,\r\n };\r\n\r\n if (methodLower !== \"get\" && payload != null) {\r\n const prepareOptions: { formDataFileFieldName?: string; formDataKey?: string } = {};\r\n if (formDataFileFieldName != null && formDataFileFieldName !== \"\")\r\n prepareOptions.formDataFileFieldName = formDataFileFieldName;\r\n if (formDataKey != null && formDataKey !== \"\") prepareOptions.formDataKey = formDataKey;\r\n const { body, headers: processedHeaders } = prepareRequestBody(payload, headers, prepareOptions);\r\n if (body !== undefined) fetchOptions.body = body;\r\n fetchOptions.headers = processedHeaders;\r\n } else {\r\n fetchOptions.headers = omitContentType({ ...headers });\r\n }\r\n\r\n try {\r\n if (responseTypeLower === \"stream\") {\r\n const maxRetries = Math.min(retries ?? 3, 5);\r\n let bytesReceived = 0;\r\n let streamError: WorkerErrorPayload = NO_ERROR;\r\n let attempt = 0;\r\n while (attempt <= maxRetries) {\r\n try {\r\n const reqHeaders =\r\n methodLower === \"get\" ? omitContentType({ ...fetchOptions.headers }) : fetchOptions.headers;\r\n if (bytesReceived > 0) reqHeaders[\"Range\"] = `bytes=${bytesReceived}-`;\r\n const streamResponse = await fetch(url, { ...fetchOptions, headers: reqHeaders });\r\n if (streamResponse.status >= 400) {\r\n streamError = makeError(streamResponse.statusText);\r\n break;\r\n }\r\n if (streamResponse.status === 204) {\r\n callerResponseStreamEnd(cacheName, hookId);\r\n return;\r\n }\r\n if (streamResponse.status === 416) break;\r\n const contentType = streamResponse.headers.get(\"content-type\") ?? undefined;\r\n const meta: BinaryResponseMeta = {\r\n contentDisposition: streamResponse.headers.get(\"content-disposition\") ?? null,\r\n ...(contentType !== undefined && { contentType }),\r\n };\r\n if (bytesReceived === 0) callerResponseStreamStart(cacheName, meta, hookId, streamResponse.status);\r\n else callerResponseStreamResume(cacheName, meta, hookId, streamResponse.status);\r\n const body = streamResponse.body;\r\n if (!body) {\r\n callerResponseStreamEnd(cacheName, hookId);\r\n return;\r\n }\r\n const reader = body.getReader();\r\n let skipRemaining = streamResponse.status === 200 && bytesReceived > 0 ? bytesReceived : 0;\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n if (!value || value.byteLength === 0) continue;\r\n if (skipRemaining > 0) {\r\n if (value.byteLength <= skipRemaining) {\r\n skipRemaining -= value.byteLength;\r\n continue;\r\n }\r\n const offset = skipRemaining;\r\n skipRemaining = 0;\r\n const tail = value.subarray(offset);\r\n callerResponseStreamChunk(cacheName, transferableBuffer(tail), hookId);\r\n bytesReceived += tail.byteLength;\r\n } else {\r\n callerResponseStreamChunk(cacheName, transferableBuffer(value), hookId);\r\n bytesReceived += value.byteLength;\r\n }\r\n }\r\n break;\r\n } catch (err) {\r\n streamError =\r\n (err as Error).name === \"AbortError\" ? makeError(\"Request aborted\") : makeError((err as Error).message);\r\n if (attempt === maxRetries) break;\r\n }\r\n attempt++;\r\n }\r\n callerResponseStreamEnd(cacheName, hookId, streamError);\r\n return;\r\n }\r\n\r\n const response = await fetch(url, fetchOptions);\r\n\r\n if (response.status >= 400) {\r\n callerResponse(cacheName, null, hookId, response.status, makeError(response.statusText));\r\n return;\r\n }\r\n\r\n if (response.status === 204) {\r\n set(normalizeKey(cacheName), null);\r\n callerResponse(cacheName, null, hookId, 204);\r\n return;\r\n }\r\n\r\n const responseData = await parseResponseByContentType(response);\r\n\r\n if (isBinaryResponse(responseData)) {\r\n callerResponseBinary(\r\n cacheName,\r\n responseData.data,\r\n {\r\n contentType: responseData.contentType,\r\n contentDisposition: responseData.contentDisposition ?? null,\r\n },\r\n hookId,\r\n response.status,\r\n );\r\n } else {\r\n commit(cacheName, responseData, hookId, response.status);\r\n }\r\n } catch (error) {\r\n if ((error as Error).name === \"AbortError\") {\r\n callerResponse(cacheName, null, hookId, undefined, makeError(\"Request aborted\"));\r\n return;\r\n }\r\n const err = error as Error;\r\n callerResponse(cacheName, null, hookId, undefined, makeError(err.message));\r\n } finally {\r\n if (timeoutId != null) clearTimeout(timeoutId);\r\n if (requestId) {\r\n inFlightControllers.delete(requestId);\r\n }\r\n inFlightByCacheName.delete(cacheName);\r\n }\r\n })();\r\n\r\n inFlightByCacheName.set(cacheName, promise);\r\n await promise;\r\n};\r\n\r\nconst onRequest = <TData>(dataRequest: DataRequest<TData>): void => {\r\n const { cacheName, type, payload, request, requestId, hookId } = dataRequest;\r\n const responseCacheName = dataRequest.cacheName ?? \"\";\r\n\r\n if (!isNonEmptyString(type)) {\r\n callerResponse(responseCacheName, null, hookId, undefined, makeError(\"Invalid request: type is required\"));\r\n return;\r\n }\r\n const lowerType = normalizeKey(type);\r\n\r\n if (lowerType === \"cancel\") {\r\n if (requestId) onCancel(requestId);\r\n return;\r\n }\r\n\r\n if (!isNonEmptyString(cacheName)) {\r\n callerResponse(responseCacheName, null, hookId, undefined, makeError(\"Invalid request: cacheName is required\"));\r\n return;\r\n }\r\n const lowerCacheName = normalizeKey(cacheName);\r\n\r\n if (lowerType === \"get\") {\r\n const requestedData = get(lowerCacheName);\r\n if (requestedData === undefined) {\r\n callerResponse(lowerCacheName, null, hookId, undefined, makeError(\"Cache miss\"));\r\n } else {\r\n callerResponse(lowerCacheName, requestedData, hookId);\r\n }\r\n } else if (lowerType === \"set\") {\r\n if (!request) {\r\n if (payload == null) {\r\n callerResponse(\r\n responseCacheName,\r\n null,\r\n hookId,\r\n undefined,\r\n makeError(\"Invalid request: payload is required for set\"),\r\n );\r\n return;\r\n }\r\n set(lowerCacheName, payload);\r\n } else {\r\n const methodLower = normalizeKey(request.method);\r\n if (methodLower !== \"get\" && payload == null) {\r\n callerResponse(\r\n responseCacheName,\r\n null,\r\n hookId,\r\n undefined,\r\n makeError(\"Invalid request: payload is required for non-GET API request\"),\r\n );\r\n return;\r\n }\r\n void apiRequest(lowerCacheName, payload ?? null, request, requestId, hookId);\r\n }\r\n } else if (lowerType === \"delete\") {\r\n remove(lowerCacheName);\r\n callerResponse(lowerCacheName, { deleted: true }, hookId);\r\n }\r\n};\r\n\r\nconst onCancel = (requestId: string): void => {\r\n const controller = inFlightControllers.get(requestId);\r\n if (controller) {\r\n controller.abort();\r\n inFlightControllers.delete(requestId);\r\n }\r\n};\r\n\r\n/**\r\n * Incoming messages from the main thread. Expects payload shape { dataRequest?: DataRequest }.\r\n * Responses via postMessage: { cacheName, data?, meta?, hookId?, httpStatus?, error }.\r\n * error is always present: { message: \"\" } when no error, { message: \"...\" } when the request failed.\r\n */\r\nonmessage = (event: MessageEvent<WorkerMessageData>): void => {\r\n const payload = event.data;\r\n if (payload === null || typeof payload !== \"object\") return;\r\n const dataRequest = payload.dataRequest;\r\n if (dataRequest !== undefined) onRequest(dataRequest);\r\n};\r\n"],"names":["set","hasFile"],"mappings":"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"}