@hkdigital/lib-sveltekit 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +135 -135
  2. package/dist/assets/autospuiten/car-paint-picker.js +41 -41
  3. package/dist/assets/autospuiten/labels.js +7 -7
  4. package/dist/classes/cache/IndexedDbCache.js +1407 -1407
  5. package/dist/classes/cache/MemoryResponseCache.js +138 -138
  6. package/dist/classes/cache/index.js +5 -5
  7. package/dist/classes/cache/typedef.js +41 -41
  8. package/dist/classes/data/IterableTree.js +243 -243
  9. package/dist/classes/data/Selector.js +190 -190
  10. package/dist/classes/data/index.js +2 -2
  11. package/dist/classes/events/EventEmitter.js +275 -275
  12. package/dist/classes/events/index.js +2 -2
  13. package/dist/classes/index.js +4 -4
  14. package/dist/classes/logging/Logger.js +158 -158
  15. package/dist/classes/logging/constants.js +18 -18
  16. package/dist/classes/logging/index.js +4 -4
  17. package/dist/classes/promise/HkPromise.js +377 -377
  18. package/dist/classes/promise/index.js +1 -1
  19. package/dist/classes/services/ServiceBase.js +409 -409
  20. package/dist/classes/services/ServiceManager.js +1114 -1114
  21. package/dist/classes/services/constants.js +12 -12
  22. package/dist/classes/services/index.js +5 -5
  23. package/dist/classes/stores/SubscribersCount.js +107 -107
  24. package/dist/classes/stores/index.js +1 -1
  25. package/dist/classes/streams/LogTransformStream.js +19 -19
  26. package/dist/classes/streams/ServerEventsStore.js +110 -110
  27. package/dist/classes/streams/TimeStampSource.js +26 -26
  28. package/dist/classes/streams/index.js +3 -3
  29. package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
  30. package/dist/classes/svelte/audio/AudioScene.svelte.js +324 -324
  31. package/dist/classes/svelte/audio/mocks.js +35 -35
  32. package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
  33. package/dist/classes/svelte/finite-state-machine/index.js +1 -1
  34. package/dist/classes/svelte/image/ImageLoader.svelte.js +45 -45
  35. package/dist/classes/svelte/image/ImageScene.svelte.js +249 -249
  36. package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
  37. package/dist/classes/svelte/image/index.js +4 -4
  38. package/dist/classes/svelte/image/mocks.js +35 -35
  39. package/dist/classes/svelte/image/typedef.js +8 -8
  40. package/dist/classes/svelte/index.js +14 -14
  41. package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
  42. package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
  43. package/dist/classes/svelte/loading-state-machine/index.js +3 -3
  44. package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +338 -338
  45. package/dist/classes/svelte/network-loader/constants.js +3 -3
  46. package/dist/classes/svelte/network-loader/index.js +3 -3
  47. package/dist/classes/svelte/network-loader/mocks.js +30 -30
  48. package/dist/classes/svelte/network-loader/typedef.js +8 -8
  49. package/dist/components/area/HkArea.svelte +49 -49
  50. package/dist/components/area/HkGridArea.svelte +77 -77
  51. package/dist/components/area/index.js +2 -2
  52. package/dist/components/buttons/button/Button.svelte +82 -82
  53. package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
  54. package/dist/components/buttons/button-text/TextButton.svelte +21 -21
  55. package/dist/components/buttons/index.js +3 -3
  56. package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
  57. package/dist/components/debug/index.js +1 -1
  58. package/dist/components/drag-drop/DragController.d.ts +0 -20
  59. package/dist/components/drag-drop/DragController.js +44 -113
  60. package/dist/components/drag-drop/DragDropContext.svelte +110 -103
  61. package/dist/components/drag-drop/Draggable.svelte +512 -492
  62. package/dist/components/drag-drop/Draggable.svelte.d.ts +8 -2
  63. package/dist/components/drag-drop/DropZoneArea.svelte +119 -119
  64. package/dist/components/drag-drop/DropZoneList.svelte +125 -125
  65. package/dist/components/drag-drop/{DropZone.svelte → Dropzone.svelte} +258 -258
  66. package/dist/components/drag-drop/drag-state.svelte.js +319 -323
  67. package/dist/components/drag-drop/index.js +7 -7
  68. package/dist/components/drag-drop/util.js +85 -85
  69. package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
  70. package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
  71. package/dist/components/icons/HkIcon.svelte +86 -86
  72. package/dist/components/icons/HkTabIcon.svelte +116 -116
  73. package/dist/components/icons/SteezeIcon.svelte +97 -97
  74. package/dist/components/icons/index.js +6 -6
  75. package/dist/components/icons/typedef.js +16 -16
  76. package/dist/components/index.js +2 -2
  77. package/dist/components/inputs/index.js +1 -1
  78. package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
  79. package/dist/components/inputs/text-input/TextInput.svelte +223 -223
  80. package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
  81. package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
  82. package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
  83. package/dist/components/layout/grid-layers/GridLayers.svelte +63 -63
  84. package/dist/components/layout/grid-layers/util.js +74 -74
  85. package/dist/components/layout/index.js +1 -1
  86. package/dist/components/panels/index.js +1 -1
  87. package/dist/components/panels/panel/Panel.svelte +43 -43
  88. package/dist/components/rows/index.js +3 -3
  89. package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
  90. package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
  91. package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
  92. package/dist/components/tab-bar/HkTabBar.svelte +74 -74
  93. package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
  94. package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
  95. package/dist/components/tab-bar/index.js +17 -17
  96. package/dist/components/tab-bar/typedef.js +11 -11
  97. package/dist/config/imagetools-config.js +189 -189
  98. package/dist/config/imagetools.d.ts +72 -72
  99. package/dist/constants/bases.js +13 -13
  100. package/dist/constants/errors/api.js +9 -9
  101. package/dist/constants/errors/generic.js +5 -5
  102. package/dist/constants/errors/index.js +3 -3
  103. package/dist/constants/errors/jwt.js +5 -5
  104. package/dist/constants/http/headers.js +6 -6
  105. package/dist/constants/http/index.js +2 -2
  106. package/dist/constants/http/methods.js +2 -2
  107. package/dist/constants/index.js +3 -3
  108. package/dist/constants/mime/application.js +5 -5
  109. package/dist/constants/mime/audio.js +13 -13
  110. package/dist/constants/mime/image.js +3 -3
  111. package/dist/constants/mime/index.js +4 -4
  112. package/dist/constants/mime/text.js +2 -2
  113. package/dist/constants/regexp/index.js +31 -31
  114. package/dist/constants/regexp/inspiratie.js__ +95 -95
  115. package/dist/constants/regexp/text.js +49 -49
  116. package/dist/constants/regexp/user.js +32 -32
  117. package/dist/constants/regexp/web.js +3 -3
  118. package/dist/constants/state-labels/drag-states.js +6 -6
  119. package/dist/constants/state-labels/drop-states.js +6 -6
  120. package/dist/constants/state-labels/input-states.js +11 -11
  121. package/dist/constants/state-labels/submit-states.js +4 -4
  122. package/dist/constants/time.js +28 -28
  123. package/dist/css/utilities.css +43 -43
  124. package/dist/design/design-config.js +73 -73
  125. package/dist/design/tailwind-theme-extend.js +158 -158
  126. package/dist/features/button-group/ButtonGroup.svelte +82 -82
  127. package/dist/features/button-group/typedef.js +10 -10
  128. package/dist/features/compare-left-right/CompareLeftRight.svelte +179 -179
  129. package/dist/features/compare-left-right/index.js +1 -1
  130. package/dist/features/game-box/GameBox.svelte +577 -577
  131. package/dist/features/game-box/gamebox.util.js +83 -83
  132. package/dist/features/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
  133. package/dist/features/hk-app-layout/HkAppLayout.svelte +251 -251
  134. package/dist/features/image-box/ImageBox.svelte +210 -210
  135. package/dist/features/image-box/index.js +5 -5
  136. package/dist/features/image-box/typedef.js +32 -32
  137. package/dist/features/index.js +23 -23
  138. package/dist/features/presenter/ImageSlide.svelte +64 -64
  139. package/dist/features/presenter/Presenter.state.svelte.js +638 -638
  140. package/dist/features/presenter/Presenter.svelte +142 -142
  141. package/dist/features/presenter/constants.js +7 -7
  142. package/dist/features/presenter/index.js +10 -10
  143. package/dist/features/presenter/typedef.js +106 -106
  144. package/dist/features/presenter/util.js +210 -210
  145. package/dist/features/virtual-viewport/VirtualViewport.svelte +196 -196
  146. package/dist/schemas/index.js +1 -1
  147. package/dist/schemas/validate-url.js +180 -180
  148. package/dist/server/index.js +1 -1
  149. package/dist/server/logger.js +94 -94
  150. package/dist/states/index.js +1 -1
  151. package/dist/states/navigation.svelte.js +55 -55
  152. package/dist/stores/index.js +1 -1
  153. package/dist/stores/theme.js +80 -80
  154. package/dist/themes/hkdev/components/blocks/text-block.css +41 -41
  155. package/dist/themes/hkdev/components/boxes/game-box.css +12 -12
  156. package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
  157. package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
  158. package/dist/themes/hkdev/components/buttons/button.css +146 -146
  159. package/dist/themes/hkdev/components/buttons/skip-button.css +6 -6
  160. package/dist/themes/hkdev/components/drag-drop/draggable.css +73 -73
  161. package/dist/themes/hkdev/components/drag-drop/drop-zone.css +48 -48
  162. package/dist/themes/hkdev/components/icons/icon-steeze.css +22 -22
  163. package/dist/themes/hkdev/components/inputs/text-input.css +104 -104
  164. package/dist/themes/hkdev/components/panels/panel.css +27 -27
  165. package/dist/themes/hkdev/components/rows/panel-grid-row.css +6 -6
  166. package/dist/themes/hkdev/components/rows/panel-row-2.css +7 -7
  167. package/dist/themes/hkdev/components.css +53 -53
  168. package/dist/themes/hkdev/debug.css +1 -1
  169. package/dist/themes/hkdev/global/layout.css +39 -39
  170. package/dist/themes/hkdev/global/on-colors.css +53 -53
  171. package/dist/themes/hkdev/globals.css +11 -11
  172. package/dist/themes/hkdev/responsive.css +12 -12
  173. package/dist/themes/hkdev/theme-ext.js +15 -15
  174. package/dist/themes/hkdev/theme.js +235 -235
  175. package/dist/themes/index.js +1 -1
  176. package/dist/typedef/context.js +6 -6
  177. package/dist/typedef/drag.js +25 -25
  178. package/dist/typedef/drop.js +12 -12
  179. package/dist/typedef/image.js +38 -38
  180. package/dist/typedef/index.js +4 -4
  181. package/dist/util/array/index.js +436 -436
  182. package/dist/util/bases/base58.js +262 -262
  183. package/dist/util/bases/index.js +1 -1
  184. package/dist/util/compare/index.js +247 -247
  185. package/dist/util/css/css-vars.js +83 -83
  186. package/dist/util/css/index.js +1 -1
  187. package/dist/util/design-system/components/states.js +22 -22
  188. package/dist/util/design-system/css/clamp.js +66 -66
  189. package/dist/util/design-system/css/root-design-vars.js +102 -102
  190. package/dist/util/design-system/index.js +5 -5
  191. package/dist/util/design-system/layout/scaling.js +228 -228
  192. package/dist/util/design-system/skeleton.js +208 -208
  193. package/dist/util/design-system/tailwind.js +288 -288
  194. package/dist/util/env/index.js +9 -9
  195. package/dist/util/expect/arrays.js +47 -47
  196. package/dist/util/expect/index.js +259 -259
  197. package/dist/util/expect/primitives.js +55 -55
  198. package/dist/util/expect/url.js +60 -60
  199. package/dist/util/function/index.js +218 -218
  200. package/dist/util/geo/index.js +26 -26
  201. package/dist/util/http/caching.js +263 -263
  202. package/dist/util/http/errors.js +97 -97
  203. package/dist/util/http/headers.js +75 -75
  204. package/dist/util/http/http-request.js +379 -379
  205. package/dist/util/http/index.js +22 -22
  206. package/dist/util/http/json-request.js +224 -224
  207. package/dist/util/http/mocks.js +65 -65
  208. package/dist/util/http/response.js +294 -294
  209. package/dist/util/http/typedef.js +93 -93
  210. package/dist/util/http/url.js +52 -52
  211. package/dist/util/image/index.js +86 -86
  212. package/dist/util/index.js +2 -2
  213. package/dist/util/is/index.js +140 -140
  214. package/dist/util/iterate/index.js +234 -234
  215. package/dist/util/object/index.js +1361 -1361
  216. package/dist/util/singleton/index.js +97 -97
  217. package/dist/util/string/array-path.js +75 -75
  218. package/dist/util/string/convert.js +54 -54
  219. package/dist/util/string/fs.js +226 -226
  220. package/dist/util/string/index.js +5 -5
  221. package/dist/util/string/interpolate.js +61 -61
  222. package/dist/util/string/pad.js +10 -10
  223. package/dist/util/svelte/index.js +4 -4
  224. package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
  225. package/dist/util/svelte/observe/index.js +49 -49
  226. package/dist/util/svelte/state-context/index.js +117 -117
  227. package/dist/util/svelte/wait/index.js +38 -38
  228. package/dist/util/sveltekit/index.js +1 -1
  229. package/dist/util/sveltekit/route-folders/index.js +101 -101
  230. package/dist/util/time/index.js +323 -323
  231. package/dist/util/unique/index.js +249 -249
  232. package/dist/valibot/date.js__ +10 -10
  233. package/dist/valibot/index.js +9 -9
  234. package/dist/valibot/url.js +95 -95
  235. package/dist/valibot/user.js +23 -23
  236. package/dist/zod/all.js +33 -33
  237. package/dist/zod/generic.js +11 -11
  238. package/dist/zod/javascript.js +32 -32
  239. package/dist/zod/user.js +16 -16
  240. package/dist/zod/web.js +52 -52
  241. package/package.json +112 -112
  242. package/dist/components/layout/grid-layers/GridLayers.svelte__heightFrom__ +0 -372
  243. package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
@@ -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