@hkdigital/lib-sveltekit 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -135
- package/dist/assets/autospuiten/car-paint-picker.js +41 -41
- package/dist/assets/autospuiten/labels.js +7 -7
- package/dist/classes/cache/IndexedDbCache.js +1407 -1407
- package/dist/classes/cache/MemoryResponseCache.js +138 -138
- package/dist/classes/cache/index.js +5 -5
- package/dist/classes/cache/typedef.js +41 -41
- package/dist/classes/data/IterableTree.js +243 -243
- package/dist/classes/data/Selector.js +190 -190
- package/dist/classes/data/index.js +2 -2
- package/dist/classes/events/EventEmitter.js +275 -275
- package/dist/classes/events/index.js +2 -2
- package/dist/classes/index.js +4 -4
- package/dist/classes/logging/Logger.js +158 -158
- package/dist/classes/logging/constants.js +18 -18
- package/dist/classes/logging/index.js +4 -4
- package/dist/classes/promise/HkPromise.js +377 -377
- package/dist/classes/promise/index.js +1 -1
- package/dist/classes/services/ServiceBase.js +409 -409
- package/dist/classes/services/ServiceManager.js +1114 -1114
- package/dist/classes/services/constants.js +12 -12
- package/dist/classes/services/index.js +5 -5
- package/dist/classes/stores/SubscribersCount.js +107 -107
- package/dist/classes/stores/index.js +1 -1
- package/dist/classes/streams/LogTransformStream.js +19 -19
- package/dist/classes/streams/ServerEventsStore.js +110 -110
- package/dist/classes/streams/TimeStampSource.js +26 -26
- package/dist/classes/streams/index.js +3 -3
- package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
- package/dist/classes/svelte/audio/AudioScene.svelte.js +324 -324
- package/dist/classes/svelte/audio/mocks.js +35 -35
- package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
- package/dist/classes/svelte/finite-state-machine/index.js +1 -1
- package/dist/classes/svelte/image/ImageLoader.svelte.js +45 -45
- package/dist/classes/svelte/image/ImageScene.svelte.js +249 -249
- package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
- package/dist/classes/svelte/image/index.js +4 -4
- package/dist/classes/svelte/image/mocks.js +35 -35
- package/dist/classes/svelte/image/typedef.js +8 -8
- package/dist/classes/svelte/index.js +14 -14
- package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
- package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
- package/dist/classes/svelte/loading-state-machine/index.js +3 -3
- package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +338 -338
- package/dist/classes/svelte/network-loader/constants.js +3 -3
- package/dist/classes/svelte/network-loader/index.js +3 -3
- package/dist/classes/svelte/network-loader/mocks.js +30 -30
- package/dist/classes/svelte/network-loader/typedef.js +8 -8
- package/dist/components/area/HkArea.svelte +49 -49
- package/dist/components/area/HkGridArea.svelte +77 -77
- package/dist/components/area/index.js +2 -2
- package/dist/components/buttons/button/Button.svelte +82 -82
- package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
- package/dist/components/buttons/button-text/TextButton.svelte +21 -21
- package/dist/components/buttons/index.js +3 -3
- package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
- package/dist/components/debug/index.js +1 -1
- package/dist/components/drag-drop/DragController.js +44 -44
- package/dist/components/drag-drop/DragDropContext.svelte +112 -110
- package/dist/components/drag-drop/Draggable.svelte +498 -512
- package/dist/components/drag-drop/{Dropzone.svelte → DropZone.svelte} +258 -258
- package/dist/components/drag-drop/DropZoneArea.svelte +119 -119
- package/dist/components/drag-drop/DropZoneList.svelte +125 -125
- package/dist/components/drag-drop/actions.d.ts +6 -0
- package/dist/components/drag-drop/actions.js +18 -0
- package/dist/components/drag-drop/drag-state.svelte.js +319 -319
- package/dist/components/drag-drop/index.js +7 -7
- package/dist/components/drag-drop/util.js +85 -85
- package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
- package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
- package/dist/components/icons/HkIcon.svelte +86 -86
- package/dist/components/icons/HkTabIcon.svelte +116 -116
- package/dist/components/icons/SteezeIcon.svelte +97 -97
- package/dist/components/icons/index.js +6 -6
- package/dist/components/icons/typedef.js +16 -16
- package/dist/components/index.js +2 -2
- package/dist/components/inputs/index.js +1 -1
- package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
- package/dist/components/inputs/text-input/TextInput.svelte +223 -223
- package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
- package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
- package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
- package/dist/components/layout/grid-layers/GridLayers.svelte +63 -63
- package/dist/components/layout/grid-layers/GridLayers.svelte__heightFrom__ +372 -0
- package/dist/components/layout/grid-layers/util.js +74 -74
- package/dist/components/layout/index.js +1 -1
- package/dist/components/panels/index.js +1 -1
- package/dist/components/panels/panel/Panel.svelte +43 -43
- package/dist/components/rows/index.js +3 -3
- package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
- package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
- package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
- package/dist/components/tab-bar/HkTabBar.svelte +74 -74
- package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
- package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
- package/dist/components/tab-bar/index.js +17 -17
- package/dist/components/tab-bar/typedef.js +11 -11
- package/dist/config/imagetools-config.js +189 -189
- package/dist/config/imagetools.d.ts +72 -72
- package/dist/constants/bases.js +13 -13
- package/dist/constants/errors/api.js +9 -9
- package/dist/constants/errors/generic.js +5 -5
- package/dist/constants/errors/index.js +3 -3
- package/dist/constants/errors/jwt.js +5 -5
- package/dist/constants/http/headers.js +6 -6
- package/dist/constants/http/index.js +2 -2
- package/dist/constants/http/methods.js +2 -2
- package/dist/constants/index.js +3 -3
- package/dist/constants/mime/application.js +5 -5
- package/dist/constants/mime/audio.js +13 -13
- package/dist/constants/mime/image.js +3 -3
- package/dist/constants/mime/index.js +4 -4
- package/dist/constants/mime/text.js +2 -2
- package/dist/constants/regexp/index.js +31 -31
- package/dist/constants/regexp/inspiratie.js__ +95 -95
- package/dist/constants/regexp/text.js +49 -49
- package/dist/constants/regexp/user.js +32 -32
- package/dist/constants/regexp/web.js +3 -3
- package/dist/constants/state-labels/drag-states.js +6 -6
- package/dist/constants/state-labels/drop-states.js +6 -6
- package/dist/constants/state-labels/input-states.js +11 -11
- package/dist/constants/state-labels/submit-states.js +4 -4
- package/dist/constants/time.js +28 -28
- package/dist/css/utilities.css +43 -43
- package/dist/design/design-config.js +73 -73
- package/dist/design/tailwind-theme-extend.js +158 -158
- package/dist/features/button-group/ButtonGroup.svelte +82 -82
- package/dist/features/button-group/typedef.js +10 -10
- package/dist/features/compare-left-right/CompareLeftRight.svelte +179 -179
- package/dist/features/compare-left-right/index.js +1 -1
- package/dist/features/game-box/GameBox.svelte +577 -577
- package/dist/features/game-box/gamebox.util.js +83 -83
- package/dist/features/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
- package/dist/features/hk-app-layout/HkAppLayout.svelte +251 -251
- package/dist/features/image-box/ImageBox.svelte +210 -210
- package/dist/features/image-box/index.js +5 -5
- package/dist/features/image-box/typedef.js +32 -32
- package/dist/features/index.js +23 -23
- package/dist/features/presenter/ImageSlide.svelte +64 -64
- package/dist/features/presenter/Presenter.state.svelte.js +638 -638
- package/dist/features/presenter/Presenter.svelte +142 -142
- package/dist/features/presenter/constants.js +7 -7
- package/dist/features/presenter/index.js +10 -10
- package/dist/features/presenter/typedef.js +106 -106
- package/dist/features/presenter/util.js +210 -210
- package/dist/features/virtual-viewport/VirtualViewport.svelte +196 -196
- package/dist/schemas/index.js +1 -1
- package/dist/schemas/validate-url.js +180 -180
- package/dist/server/index.js +1 -1
- package/dist/server/logger.js +94 -94
- package/dist/states/index.js +1 -1
- package/dist/states/navigation.svelte.js +55 -55
- package/dist/stores/index.js +1 -1
- package/dist/stores/theme.js +80 -80
- package/dist/themes/hkdev/components/blocks/text-block.css +41 -41
- package/dist/themes/hkdev/components/boxes/game-box.css +12 -12
- package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
- package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
- package/dist/themes/hkdev/components/buttons/button.css +146 -146
- package/dist/themes/hkdev/components/buttons/skip-button.css +6 -6
- package/dist/themes/hkdev/components/drag-drop/draggable.css +73 -73
- package/dist/themes/hkdev/components/drag-drop/drop-zone.css +48 -48
- package/dist/themes/hkdev/components/icons/icon-steeze.css +22 -22
- package/dist/themes/hkdev/components/inputs/text-input.css +104 -104
- package/dist/themes/hkdev/components/panels/panel.css +27 -27
- package/dist/themes/hkdev/components/rows/panel-grid-row.css +6 -6
- package/dist/themes/hkdev/components/rows/panel-row-2.css +7 -7
- package/dist/themes/hkdev/components.css +53 -53
- package/dist/themes/hkdev/debug.css +1 -1
- package/dist/themes/hkdev/global/layout.css +39 -39
- package/dist/themes/hkdev/global/on-colors.css +53 -53
- package/dist/themes/hkdev/globals.css +11 -11
- package/dist/themes/hkdev/responsive.css +12 -12
- package/dist/themes/hkdev/theme-ext.js +15 -15
- package/dist/themes/hkdev/theme.js +235 -235
- package/dist/themes/index.js +1 -1
- package/dist/typedef/context.js +6 -6
- package/dist/typedef/drag.js +25 -25
- package/dist/typedef/drop.js +12 -12
- package/dist/typedef/image.js +38 -38
- package/dist/typedef/index.js +4 -4
- package/dist/util/array/index.js +436 -436
- package/dist/util/bases/base58.js +262 -262
- package/dist/util/bases/index.js +1 -1
- package/dist/util/compare/index.js +247 -247
- package/dist/util/css/css-vars.js +83 -83
- package/dist/util/css/index.js +1 -1
- package/dist/util/design-system/components/states.js +22 -22
- package/dist/util/design-system/css/clamp.js +66 -66
- package/dist/util/design-system/css/root-design-vars.js +102 -102
- package/dist/util/design-system/index.js +5 -5
- package/dist/util/design-system/layout/scaling.js +228 -228
- package/dist/util/design-system/skeleton.js +208 -208
- package/dist/util/design-system/tailwind.js +288 -288
- package/dist/util/env/index.js +9 -9
- package/dist/util/expect/arrays.js +47 -47
- package/dist/util/expect/index.js +259 -259
- package/dist/util/expect/primitives.js +55 -55
- package/dist/util/expect/url.js +60 -60
- package/dist/util/function/index.js +218 -218
- package/dist/util/geo/index.js +26 -26
- package/dist/util/http/caching.js +263 -263
- package/dist/util/http/errors.js +97 -97
- package/dist/util/http/headers.js +75 -75
- package/dist/util/http/http-request.js +379 -379
- package/dist/util/http/index.js +22 -22
- package/dist/util/http/json-request.js +224 -224
- package/dist/util/http/mocks.js +65 -65
- package/dist/util/http/response.js +294 -294
- package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
- package/dist/util/http/typedef.js +93 -93
- package/dist/util/http/url.js +52 -52
- package/dist/util/image/index.js +86 -86
- package/dist/util/index.js +2 -2
- package/dist/util/is/index.js +140 -140
- package/dist/util/iterate/index.js +234 -234
- package/dist/util/object/index.js +1361 -1361
- package/dist/util/singleton/index.js +97 -97
- package/dist/util/string/array-path.js +75 -75
- package/dist/util/string/convert.js +54 -54
- package/dist/util/string/fs.js +226 -226
- package/dist/util/string/index.js +5 -5
- package/dist/util/string/interpolate.js +61 -61
- package/dist/util/string/pad.js +10 -10
- package/dist/util/svelte/index.js +4 -4
- package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
- package/dist/util/svelte/observe/index.js +49 -49
- package/dist/util/svelte/state-context/index.js +117 -117
- package/dist/util/svelte/wait/index.js +38 -38
- package/dist/util/sveltekit/index.js +1 -1
- package/dist/util/sveltekit/route-folders/index.js +101 -101
- package/dist/util/time/index.js +323 -323
- package/dist/util/unique/index.js +249 -249
- package/dist/valibot/date.js__ +10 -10
- package/dist/valibot/index.js +9 -9
- package/dist/valibot/url.js +95 -95
- package/dist/valibot/user.js +23 -23
- package/dist/zod/all.js +33 -33
- package/dist/zod/generic.js +11 -11
- package/dist/zod/javascript.js +32 -32
- package/dist/zod/user.js +16 -16
- package/dist/zod/web.js +52 -52
- package/package.json +112 -112
@@ -1,294 +1,294 @@
|
|
1
|
-
import { ResponseError } from '../../constants/errors/index.js';
|
2
|
-
import * as expect from '../expect/index.js';
|
3
|
-
import { toURL } from './url.js';
|
4
|
-
|
5
|
-
import {
|
6
|
-
WWW_AUTHENTICATE,
|
7
|
-
CONTENT_LENGTH
|
8
|
-
} from '../../constants/http/headers.js';
|
9
|
-
|
10
|
-
import { href } from './url.js';
|
11
|
-
|
12
|
-
import { getErrorFromResponse } from './errors.js';
|
13
|
-
|
14
|
-
// > Types
|
15
|
-
|
16
|
-
/**
|
17
|
-
* Callback function that reports progress of data loading
|
18
|
-
*
|
19
|
-
* @callback progressCallback
|
20
|
-
*
|
21
|
-
* @param {object} _
|
22
|
-
* @param {number} _.bytesLoaded - Number of bytes loaded so far
|
23
|
-
* @param {number} _.size - Total size of the response in bytes (0 if unknown)
|
24
|
-
*/
|
25
|
-
|
26
|
-
// > Exports
|
27
|
-
|
28
|
-
/**
|
29
|
-
* Check if the response status is ok (in 200-299 range)
|
30
|
-
* This function examines HTTP status codes and throws appropriate errors for
|
31
|
-
* non-successful responses, with special handling for 401 Unauthorized.
|
32
|
-
*
|
33
|
-
* @param {object} response - Fetch Response object to check
|
34
|
-
*
|
35
|
-
* @param {string} url - The URL used for the request (for error messages)
|
36
|
-
*
|
37
|
-
* @throws {Error} When response has 401 status with authorization details
|
38
|
-
* @throws {ResponseError} When response has other non-successful status codes
|
39
|
-
*
|
40
|
-
* @example
|
41
|
-
* // Check if response was successful
|
42
|
-
* try {
|
43
|
-
* await expectResponseOk(response, 'https://api.example.com/data');
|
44
|
-
* // Process successful response here
|
45
|
-
* } catch (error) {
|
46
|
-
* // Handle specific error types
|
47
|
-
* if (error.message.includes('401')) {
|
48
|
-
* // Handle unauthorized error
|
49
|
-
* }
|
50
|
-
* }
|
51
|
-
*/
|
52
|
-
export async function expectResponseOk(response, url) {
|
53
|
-
expect.object(response);
|
54
|
-
|
55
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200
|
56
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
|
57
|
-
|
58
|
-
if (200 === response.status || 201 === response.status) {
|
59
|
-
if (!response.ok) {
|
60
|
-
throw new ResponseError(
|
61
|
-
`Server returned - ${response.status} ${response.statusText} ` +
|
62
|
-
`[response.ok=false] [url=${href(url)}]`
|
63
|
-
);
|
64
|
-
}
|
65
|
-
|
66
|
-
// All ok
|
67
|
-
return;
|
68
|
-
}
|
69
|
-
|
70
|
-
// > Handle 401 Unauthorized
|
71
|
-
|
72
|
-
if (401 === response.status) {
|
73
|
-
let errorMessage = 'Server returned [401] Unauthorized';
|
74
|
-
|
75
|
-
const authValue = response.headers.get(WWW_AUTHENTICATE);
|
76
|
-
|
77
|
-
if (authValue) {
|
78
|
-
// Add WWW_AUTHENTICATE response to error message
|
79
|
-
errorMessage += ` (${authValue})`;
|
80
|
-
}
|
81
|
-
|
82
|
-
errorMessage += ` [url=${href(url)}]`;
|
83
|
-
|
84
|
-
throw new Error(errorMessage);
|
85
|
-
}
|
86
|
-
|
87
|
-
// > Handle all other error responses
|
88
|
-
|
89
|
-
const error = await getErrorFromResponse(response);
|
90
|
-
|
91
|
-
throw new ResponseError(
|
92
|
-
`Server returned - ${response.status} ${response.statusText} ` +
|
93
|
-
`[url=${href(url)}]`,
|
94
|
-
{ cause: error }
|
95
|
-
);
|
96
|
-
}
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Get the response size from the content-length response header
|
100
|
-
*
|
101
|
-
* @param {Response} response - Fetch Response object
|
102
|
-
*
|
103
|
-
* @returns {number} Response size in bytes, or 0 if content-length is not set
|
104
|
-
*
|
105
|
-
* @example
|
106
|
-
* const response = await fetch('https://example.com/large-file.zip');
|
107
|
-
* const size = getResponseSize(response);
|
108
|
-
* console.log(`Download size: ${size} bytes`);
|
109
|
-
*/
|
110
|
-
export function getResponseSize(response) {
|
111
|
-
const sizeStr = response.headers.get(CONTENT_LENGTH);
|
112
|
-
|
113
|
-
if (!sizeStr) {
|
114
|
-
return 0;
|
115
|
-
}
|
116
|
-
|
117
|
-
return parseInt(sizeStr, 10);
|
118
|
-
}
|
119
|
-
|
120
|
-
/**
|
121
|
-
* Wait for a response and check if the response is ok
|
122
|
-
* This function awaits a response promise and performs error checking,
|
123
|
-
* wrapping network errors in a standardized ResponseError format.
|
124
|
-
*
|
125
|
-
* @param {Promise<Response>} responsePromise - Promise that resolves to a Response
|
126
|
-
*
|
127
|
-
* @param {string|URL} url - URL used for the request (for error messages)
|
128
|
-
*
|
129
|
-
* @throws {ResponseError} When a network error occurs or response is not ok
|
130
|
-
*
|
131
|
-
* @returns {Promise<Response>} The response if successful
|
132
|
-
*
|
133
|
-
* @example
|
134
|
-
* // Handle a fetch promise with proper error handling
|
135
|
-
* const responsePromise = fetch('https://api.example.com/data');
|
136
|
-
* try {
|
137
|
-
* const response = await waitForAndCheckResponse(
|
138
|
-
* responsePromise,
|
139
|
-
* 'https://api.example.com/data'
|
140
|
-
* );
|
141
|
-
* // Process response
|
142
|
-
* } catch (error) {
|
143
|
-
* // Handle standardized ResponseError
|
144
|
-
* console.error(error.message);
|
145
|
-
* }
|
146
|
-
*/
|
147
|
-
export async function waitForAndCheckResponse(responsePromise, url) {
|
148
|
-
expect.promise(responsePromise);
|
149
|
-
|
150
|
-
url = toURL(url);
|
151
|
-
|
152
|
-
let response;
|
153
|
-
|
154
|
-
try {
|
155
|
-
response = await responsePromise;
|
156
|
-
|
157
|
-
if (response && false === response.ok) {
|
158
|
-
// if response.ok is false, it also indicates a network error
|
159
|
-
throw new Error(`Response failed [response.ok=false]`);
|
160
|
-
}
|
161
|
-
} catch (e) {
|
162
|
-
if (e instanceof TypeError || response?.ok === false) {
|
163
|
-
throw new ResponseError(
|
164
|
-
`A network error occurred for request [${href(url)}]`,
|
165
|
-
{
|
166
|
-
cause: e
|
167
|
-
}
|
168
|
-
);
|
169
|
-
} else {
|
170
|
-
throw e;
|
171
|
-
}
|
172
|
-
}
|
173
|
-
|
174
|
-
return response;
|
175
|
-
}
|
176
|
-
|
177
|
-
/**
|
178
|
-
* Load response body as ArrayBuffer with progress monitoring and abort capability
|
179
|
-
*
|
180
|
-
* This function reads a response body stream chunk by chunk, with optional
|
181
|
-
* progress reporting. It provides an abort mechanism to cancel an in-progress
|
182
|
-
* download.
|
183
|
-
*
|
184
|
-
* @param {Response} response - Fetch Response object to read
|
185
|
-
*
|
186
|
-
* @param {progressCallback} [onProgress] - Optional callback for progress updates
|
187
|
-
*
|
188
|
-
* @returns {{
|
189
|
-
* bufferPromise: Promise<ArrayBuffer>,
|
190
|
-
* abort: () => void
|
191
|
-
* }} Object containing the buffer promise and abort function
|
192
|
-
*
|
193
|
-
* @example
|
194
|
-
* // Download a file with progress monitoring and abort capability
|
195
|
-
* const response = await fetch('https://example.com/large-file.zip');
|
196
|
-
*
|
197
|
-
* const { bufferPromise, abort } = loadResponseBuffer(
|
198
|
-
* response,
|
199
|
-
* ({ bytesLoaded, size }) => {
|
200
|
-
* // Update progress UI
|
201
|
-
* const percent = size ? Math.round((bytesLoaded / size) * 100) : 0;
|
202
|
-
* console.log(`Downloaded ${bytesLoaded} bytes (${percent}%)`);
|
203
|
-
* }
|
204
|
-
* );
|
205
|
-
*
|
206
|
-
* // To abort the download:
|
207
|
-
* // abort();
|
208
|
-
*
|
209
|
-
* try {
|
210
|
-
* const buffer = await bufferPromise;
|
211
|
-
* // Process the complete buffer
|
212
|
-
* } catch (error) {
|
213
|
-
* console.error('Download failed or was aborted', error);
|
214
|
-
* }
|
215
|
-
*/
|
216
|
-
export function loadResponseBuffer(response, onProgress) {
|
217
|
-
// @note size might be 0
|
218
|
-
// @note might not be send by server in dev mode
|
219
|
-
const size = getResponseSize(response);
|
220
|
-
|
221
|
-
let bytesLoaded = 0;
|
222
|
-
|
223
|
-
if (onProgress /*&& size*/) {
|
224
|
-
onProgress({ bytesLoaded, size });
|
225
|
-
}
|
226
|
-
|
227
|
-
if (!response.body) {
|
228
|
-
throw new Error('Missing [response.body]');
|
229
|
-
}
|
230
|
-
|
231
|
-
let reader;
|
232
|
-
let aborted = false;
|
233
|
-
|
234
|
-
/**
|
235
|
-
* Read chunks from response body using reader
|
236
|
-
*
|
237
|
-
* @returns {Promise<ArrayBuffer>}
|
238
|
-
*/
|
239
|
-
async function read() {
|
240
|
-
reader = response.body.getReader();
|
241
|
-
let chunks = [];
|
242
|
-
|
243
|
-
for (;;) {
|
244
|
-
const { done, value } = await reader.read();
|
245
|
-
|
246
|
-
if (value) {
|
247
|
-
// @note value is an ArrayBuffer
|
248
|
-
bytesLoaded += value.byteLength;
|
249
|
-
|
250
|
-
chunks.push(value);
|
251
|
-
|
252
|
-
if (onProgress /*&& size*/) {
|
253
|
-
onProgress({ bytesLoaded, size });
|
254
|
-
}
|
255
|
-
}
|
256
|
-
|
257
|
-
if (done || aborted) {
|
258
|
-
// Loading complete or aborted by user
|
259
|
-
break;
|
260
|
-
}
|
261
|
-
} // end for
|
262
|
-
|
263
|
-
if (size && bytesLoaded !== size) {
|
264
|
-
console.error(`Received [${bytesLoaded}], but expected [${size}] bytes`);
|
265
|
-
}
|
266
|
-
|
267
|
-
// Concat the chunks into a single array
|
268
|
-
let buffer = new ArrayBuffer(bytesLoaded);
|
269
|
-
let body = new Uint8Array(buffer);
|
270
|
-
|
271
|
-
let offset = 0;
|
272
|
-
|
273
|
-
// Place the chunks in the buffer
|
274
|
-
for (let chunk of chunks) {
|
275
|
-
body.set(chunk, offset);
|
276
|
-
offset += chunk.byteLength;
|
277
|
-
} // end for
|
278
|
-
|
279
|
-
return buffer;
|
280
|
-
}
|
281
|
-
|
282
|
-
const bufferPromise = read();
|
283
|
-
|
284
|
-
return {
|
285
|
-
bufferPromise,
|
286
|
-
abort: () => {
|
287
|
-
aborted = true;
|
288
|
-
|
289
|
-
if (reader) {
|
290
|
-
reader.cancel('Aborted by user');
|
291
|
-
}
|
292
|
-
}
|
293
|
-
};
|
294
|
-
} // end fn
|
1
|
+
import { ResponseError } from '../../constants/errors/index.js';
|
2
|
+
import * as expect from '../expect/index.js';
|
3
|
+
import { toURL } from './url.js';
|
4
|
+
|
5
|
+
import {
|
6
|
+
WWW_AUTHENTICATE,
|
7
|
+
CONTENT_LENGTH
|
8
|
+
} from '../../constants/http/headers.js';
|
9
|
+
|
10
|
+
import { href } from './url.js';
|
11
|
+
|
12
|
+
import { getErrorFromResponse } from './errors.js';
|
13
|
+
|
14
|
+
// > Types
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Callback function that reports progress of data loading
|
18
|
+
*
|
19
|
+
* @callback progressCallback
|
20
|
+
*
|
21
|
+
* @param {object} _
|
22
|
+
* @param {number} _.bytesLoaded - Number of bytes loaded so far
|
23
|
+
* @param {number} _.size - Total size of the response in bytes (0 if unknown)
|
24
|
+
*/
|
25
|
+
|
26
|
+
// > Exports
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Check if the response status is ok (in 200-299 range)
|
30
|
+
* This function examines HTTP status codes and throws appropriate errors for
|
31
|
+
* non-successful responses, with special handling for 401 Unauthorized.
|
32
|
+
*
|
33
|
+
* @param {object} response - Fetch Response object to check
|
34
|
+
*
|
35
|
+
* @param {string} url - The URL used for the request (for error messages)
|
36
|
+
*
|
37
|
+
* @throws {Error} When response has 401 status with authorization details
|
38
|
+
* @throws {ResponseError} When response has other non-successful status codes
|
39
|
+
*
|
40
|
+
* @example
|
41
|
+
* // Check if response was successful
|
42
|
+
* try {
|
43
|
+
* await expectResponseOk(response, 'https://api.example.com/data');
|
44
|
+
* // Process successful response here
|
45
|
+
* } catch (error) {
|
46
|
+
* // Handle specific error types
|
47
|
+
* if (error.message.includes('401')) {
|
48
|
+
* // Handle unauthorized error
|
49
|
+
* }
|
50
|
+
* }
|
51
|
+
*/
|
52
|
+
export async function expectResponseOk(response, url) {
|
53
|
+
expect.object(response);
|
54
|
+
|
55
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200
|
56
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
|
57
|
+
|
58
|
+
if (200 === response.status || 201 === response.status) {
|
59
|
+
if (!response.ok) {
|
60
|
+
throw new ResponseError(
|
61
|
+
`Server returned - ${response.status} ${response.statusText} ` +
|
62
|
+
`[response.ok=false] [url=${href(url)}]`
|
63
|
+
);
|
64
|
+
}
|
65
|
+
|
66
|
+
// All ok
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
70
|
+
// > Handle 401 Unauthorized
|
71
|
+
|
72
|
+
if (401 === response.status) {
|
73
|
+
let errorMessage = 'Server returned [401] Unauthorized';
|
74
|
+
|
75
|
+
const authValue = response.headers.get(WWW_AUTHENTICATE);
|
76
|
+
|
77
|
+
if (authValue) {
|
78
|
+
// Add WWW_AUTHENTICATE response to error message
|
79
|
+
errorMessage += ` (${authValue})`;
|
80
|
+
}
|
81
|
+
|
82
|
+
errorMessage += ` [url=${href(url)}]`;
|
83
|
+
|
84
|
+
throw new Error(errorMessage);
|
85
|
+
}
|
86
|
+
|
87
|
+
// > Handle all other error responses
|
88
|
+
|
89
|
+
const error = await getErrorFromResponse(response);
|
90
|
+
|
91
|
+
throw new ResponseError(
|
92
|
+
`Server returned - ${response.status} ${response.statusText} ` +
|
93
|
+
`[url=${href(url)}]`,
|
94
|
+
{ cause: error }
|
95
|
+
);
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Get the response size from the content-length response header
|
100
|
+
*
|
101
|
+
* @param {Response} response - Fetch Response object
|
102
|
+
*
|
103
|
+
* @returns {number} Response size in bytes, or 0 if content-length is not set
|
104
|
+
*
|
105
|
+
* @example
|
106
|
+
* const response = await fetch('https://example.com/large-file.zip');
|
107
|
+
* const size = getResponseSize(response);
|
108
|
+
* console.log(`Download size: ${size} bytes`);
|
109
|
+
*/
|
110
|
+
export function getResponseSize(response) {
|
111
|
+
const sizeStr = response.headers.get(CONTENT_LENGTH);
|
112
|
+
|
113
|
+
if (!sizeStr) {
|
114
|
+
return 0;
|
115
|
+
}
|
116
|
+
|
117
|
+
return parseInt(sizeStr, 10);
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Wait for a response and check if the response is ok
|
122
|
+
* This function awaits a response promise and performs error checking,
|
123
|
+
* wrapping network errors in a standardized ResponseError format.
|
124
|
+
*
|
125
|
+
* @param {Promise<Response>} responsePromise - Promise that resolves to a Response
|
126
|
+
*
|
127
|
+
* @param {string|URL} url - URL used for the request (for error messages)
|
128
|
+
*
|
129
|
+
* @throws {ResponseError} When a network error occurs or response is not ok
|
130
|
+
*
|
131
|
+
* @returns {Promise<Response>} The response if successful
|
132
|
+
*
|
133
|
+
* @example
|
134
|
+
* // Handle a fetch promise with proper error handling
|
135
|
+
* const responsePromise = fetch('https://api.example.com/data');
|
136
|
+
* try {
|
137
|
+
* const response = await waitForAndCheckResponse(
|
138
|
+
* responsePromise,
|
139
|
+
* 'https://api.example.com/data'
|
140
|
+
* );
|
141
|
+
* // Process response
|
142
|
+
* } catch (error) {
|
143
|
+
* // Handle standardized ResponseError
|
144
|
+
* console.error(error.message);
|
145
|
+
* }
|
146
|
+
*/
|
147
|
+
export async function waitForAndCheckResponse(responsePromise, url) {
|
148
|
+
expect.promise(responsePromise);
|
149
|
+
|
150
|
+
url = toURL(url);
|
151
|
+
|
152
|
+
let response;
|
153
|
+
|
154
|
+
try {
|
155
|
+
response = await responsePromise;
|
156
|
+
|
157
|
+
if (response && false === response.ok) {
|
158
|
+
// if response.ok is false, it also indicates a network error
|
159
|
+
throw new Error(`Response failed [response.ok=false]`);
|
160
|
+
}
|
161
|
+
} catch (e) {
|
162
|
+
if (e instanceof TypeError || response?.ok === false) {
|
163
|
+
throw new ResponseError(
|
164
|
+
`A network error occurred for request [${href(url)}]`,
|
165
|
+
{
|
166
|
+
cause: e
|
167
|
+
}
|
168
|
+
);
|
169
|
+
} else {
|
170
|
+
throw e;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
return response;
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Load response body as ArrayBuffer with progress monitoring and abort capability
|
179
|
+
*
|
180
|
+
* This function reads a response body stream chunk by chunk, with optional
|
181
|
+
* progress reporting. It provides an abort mechanism to cancel an in-progress
|
182
|
+
* download.
|
183
|
+
*
|
184
|
+
* @param {Response} response - Fetch Response object to read
|
185
|
+
*
|
186
|
+
* @param {progressCallback} [onProgress] - Optional callback for progress updates
|
187
|
+
*
|
188
|
+
* @returns {{
|
189
|
+
* bufferPromise: Promise<ArrayBuffer>,
|
190
|
+
* abort: () => void
|
191
|
+
* }} Object containing the buffer promise and abort function
|
192
|
+
*
|
193
|
+
* @example
|
194
|
+
* // Download a file with progress monitoring and abort capability
|
195
|
+
* const response = await fetch('https://example.com/large-file.zip');
|
196
|
+
*
|
197
|
+
* const { bufferPromise, abort } = loadResponseBuffer(
|
198
|
+
* response,
|
199
|
+
* ({ bytesLoaded, size }) => {
|
200
|
+
* // Update progress UI
|
201
|
+
* const percent = size ? Math.round((bytesLoaded / size) * 100) : 0;
|
202
|
+
* console.log(`Downloaded ${bytesLoaded} bytes (${percent}%)`);
|
203
|
+
* }
|
204
|
+
* );
|
205
|
+
*
|
206
|
+
* // To abort the download:
|
207
|
+
* // abort();
|
208
|
+
*
|
209
|
+
* try {
|
210
|
+
* const buffer = await bufferPromise;
|
211
|
+
* // Process the complete buffer
|
212
|
+
* } catch (error) {
|
213
|
+
* console.error('Download failed or was aborted', error);
|
214
|
+
* }
|
215
|
+
*/
|
216
|
+
export function loadResponseBuffer(response, onProgress) {
|
217
|
+
// @note size might be 0
|
218
|
+
// @note might not be send by server in dev mode
|
219
|
+
const size = getResponseSize(response);
|
220
|
+
|
221
|
+
let bytesLoaded = 0;
|
222
|
+
|
223
|
+
if (onProgress /*&& size*/) {
|
224
|
+
onProgress({ bytesLoaded, size });
|
225
|
+
}
|
226
|
+
|
227
|
+
if (!response.body) {
|
228
|
+
throw new Error('Missing [response.body]');
|
229
|
+
}
|
230
|
+
|
231
|
+
let reader;
|
232
|
+
let aborted = false;
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Read chunks from response body using reader
|
236
|
+
*
|
237
|
+
* @returns {Promise<ArrayBuffer>}
|
238
|
+
*/
|
239
|
+
async function read() {
|
240
|
+
reader = response.body.getReader();
|
241
|
+
let chunks = [];
|
242
|
+
|
243
|
+
for (;;) {
|
244
|
+
const { done, value } = await reader.read();
|
245
|
+
|
246
|
+
if (value) {
|
247
|
+
// @note value is an ArrayBuffer
|
248
|
+
bytesLoaded += value.byteLength;
|
249
|
+
|
250
|
+
chunks.push(value);
|
251
|
+
|
252
|
+
if (onProgress /*&& size*/) {
|
253
|
+
onProgress({ bytesLoaded, size });
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
if (done || aborted) {
|
258
|
+
// Loading complete or aborted by user
|
259
|
+
break;
|
260
|
+
}
|
261
|
+
} // end for
|
262
|
+
|
263
|
+
if (size && bytesLoaded !== size) {
|
264
|
+
console.error(`Received [${bytesLoaded}], but expected [${size}] bytes`);
|
265
|
+
}
|
266
|
+
|
267
|
+
// Concat the chunks into a single array
|
268
|
+
let buffer = new ArrayBuffer(bytesLoaded);
|
269
|
+
let body = new Uint8Array(buffer);
|
270
|
+
|
271
|
+
let offset = 0;
|
272
|
+
|
273
|
+
// Place the chunks in the buffer
|
274
|
+
for (let chunk of chunks) {
|
275
|
+
body.set(chunk, offset);
|
276
|
+
offset += chunk.byteLength;
|
277
|
+
} // end for
|
278
|
+
|
279
|
+
return buffer;
|
280
|
+
}
|
281
|
+
|
282
|
+
const bufferPromise = read();
|
283
|
+
|
284
|
+
return {
|
285
|
+
bufferPromise,
|
286
|
+
abort: () => {
|
287
|
+
aborted = true;
|
288
|
+
|
289
|
+
if (reader) {
|
290
|
+
reader.cancel('Aborted by user');
|
291
|
+
}
|
292
|
+
}
|
293
|
+
};
|
294
|
+
} // end fn
|