@hkdigital/lib-core 0.4.62 → 0.4.63
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/dist/generic/errors/generic.d.ts +8 -6
- package/dist/generic/errors/generic.js +8 -6
- package/dist/network/errors/api.d.ts +5 -6
- package/dist/network/errors/api.js +7 -5
- package/dist/network/http/http-request.js +2 -1
- package/dist/network/http/json-request.js +10 -15
- package/dist/network/http/response.js +4 -4
- package/dist/network/loaders/base/SceneBase.svelte.js +108 -43
- package/dist/util/svelte/wait/index.js +3 -1
- package/package.json +1 -1
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export class TypeOrValueError extends Error {
|
|
2
|
-
}
|
|
3
|
-
export class InternalError extends Error {
|
|
4
|
-
}
|
|
5
|
-
export class InternalEventOrLogError extends Error {
|
|
6
|
-
}
|
|
7
1
|
export class DetailedError extends Error {
|
|
8
2
|
/**
|
|
9
3
|
* @param {string} [message]
|
|
@@ -16,3 +10,11 @@ export class DetailedError extends Error {
|
|
|
16
10
|
} | null;
|
|
17
11
|
cause: unknown;
|
|
18
12
|
}
|
|
13
|
+
export class TypeOrValueError extends Error {
|
|
14
|
+
}
|
|
15
|
+
export class InternalError extends Error {
|
|
16
|
+
}
|
|
17
|
+
export class InternalEventOrLogError extends Error {
|
|
18
|
+
}
|
|
19
|
+
export class TimeoutError extends DetailedError {
|
|
20
|
+
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export class TypeOrValueError extends Error {}
|
|
2
|
-
|
|
3
|
-
export class InternalError extends Error {}
|
|
4
|
-
|
|
5
|
-
export class InternalEventOrLogError extends Error {}
|
|
6
|
-
|
|
7
1
|
export class DetailedError extends Error
|
|
8
2
|
{
|
|
9
3
|
/**
|
|
@@ -28,3 +22,11 @@ export class DetailedError extends Error
|
|
|
28
22
|
}
|
|
29
23
|
}
|
|
30
24
|
}
|
|
25
|
+
|
|
26
|
+
export class TypeOrValueError extends Error {}
|
|
27
|
+
|
|
28
|
+
export class InternalError extends Error {}
|
|
29
|
+
|
|
30
|
+
export class InternalEventOrLogError extends Error {}
|
|
31
|
+
|
|
32
|
+
export class TimeoutError extends DetailedError {}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export class ResponseError extends
|
|
1
|
+
export class ResponseError extends DetailedError {
|
|
2
2
|
}
|
|
3
|
-
export class AuthenticationError extends
|
|
3
|
+
export class AuthenticationError extends DetailedError {
|
|
4
4
|
}
|
|
5
|
-
export class BadRequestError extends
|
|
5
|
+
export class BadRequestError extends DetailedError {
|
|
6
6
|
}
|
|
7
|
-
export class AbortError extends
|
|
8
|
-
}
|
|
9
|
-
export class TimeoutError extends Error {
|
|
7
|
+
export class AbortError extends DetailedError {
|
|
10
8
|
}
|
|
9
|
+
import { DetailedError } from '../../generic/errors.js';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { DetailedError } from '../../generic/errors.js';
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class ResponseError extends DetailedError {}
|
|
4
4
|
|
|
5
|
-
export class
|
|
5
|
+
export class AuthenticationError extends DetailedError {}
|
|
6
6
|
|
|
7
|
-
export class
|
|
7
|
+
export class BadRequestError extends DetailedError {}
|
|
8
8
|
|
|
9
|
-
export class
|
|
9
|
+
export class AbortError extends DetailedError {}
|
|
10
|
+
|
|
11
|
+
// @note import TimeoutError from '../../generic/errors.js';
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
import { APPLICATION_JSON } from '../../constants/mime/application.js';
|
|
12
12
|
import { CONTENT_TYPE } from '../../constants/http/headers.js';
|
|
13
13
|
|
|
14
|
-
import { AbortError
|
|
14
|
+
import { AbortError } from '../errors/api.js';
|
|
15
|
+
import { TimeoutError } from '../../generic/errors.js';
|
|
15
16
|
|
|
16
17
|
import * as expect from '../../util/expect.js';
|
|
17
18
|
|
|
@@ -93,9 +93,8 @@ export async function jsonGet(options) {
|
|
|
93
93
|
} catch (e) {
|
|
94
94
|
throw new ResponseError(
|
|
95
95
|
`Failed to JSON decode server response from [${decodeURI(url.href)}]`,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
96
|
+
null,
|
|
97
|
+
e
|
|
99
98
|
);
|
|
100
99
|
}
|
|
101
100
|
|
|
@@ -206,9 +205,8 @@ export async function jsonPost(options) {
|
|
|
206
205
|
} catch (e) {
|
|
207
206
|
throw new ResponseError(
|
|
208
207
|
`Failed to JSON decode server response from [${decodeURI(url.href)}]`,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
208
|
+
null,
|
|
209
|
+
e
|
|
212
210
|
);
|
|
213
211
|
}
|
|
214
212
|
|
|
@@ -322,9 +320,8 @@ export async function jsonPut(options) {
|
|
|
322
320
|
} catch (e) {
|
|
323
321
|
throw new ResponseError(
|
|
324
322
|
`Failed to JSON decode server response from [${decodeURI(url.href)}]`,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
323
|
+
null,
|
|
324
|
+
e
|
|
328
325
|
);
|
|
329
326
|
}
|
|
330
327
|
|
|
@@ -437,9 +434,8 @@ export async function jsonPatch(options) {
|
|
|
437
434
|
} catch (e) {
|
|
438
435
|
throw new ResponseError(
|
|
439
436
|
`Failed to JSON decode server response from [${decodeURI(url.href)}]`,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
437
|
+
null,
|
|
438
|
+
e
|
|
443
439
|
);
|
|
444
440
|
}
|
|
445
441
|
|
|
@@ -529,9 +525,8 @@ export async function jsonDelete(options) {
|
|
|
529
525
|
} catch (e) {
|
|
530
526
|
throw new ResponseError(
|
|
531
527
|
`Failed to JSON decode server response from [${decodeURI(url.href)}]`,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
528
|
+
null,
|
|
529
|
+
e
|
|
535
530
|
);
|
|
536
531
|
}
|
|
537
532
|
|
|
@@ -91,7 +91,8 @@ export async function expectResponseOk(response, url) {
|
|
|
91
91
|
throw new ResponseError(
|
|
92
92
|
`Server returned - ${response.status} ${response.statusText} ` +
|
|
93
93
|
`[url=${href(url)}]`,
|
|
94
|
-
|
|
94
|
+
null,
|
|
95
|
+
error
|
|
95
96
|
);
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -186,9 +187,8 @@ export async function waitForAndCheckResponse(responsePromise, url) {
|
|
|
186
187
|
} else if (e instanceof TypeError || response?.ok === false) {
|
|
187
188
|
throw new ResponseError(
|
|
188
189
|
`A network error occurred for request [${href(url)}]`,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
190
|
+
null,
|
|
191
|
+
e
|
|
192
192
|
);
|
|
193
193
|
} else {
|
|
194
194
|
throw e;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DetailedError } from '../../../generic/errors.js';
|
|
2
|
+
|
|
1
3
|
import { LoadingStateMachine } from '../../../state/machines.js';
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
} from '../../../state/machines.js';
|
|
16
18
|
|
|
17
19
|
import { waitForState } from '../../../util/svelte.js';
|
|
20
|
+
import { TimeoutError } from '../../../generic/errors.js';
|
|
18
21
|
|
|
19
22
|
/** @typedef {import('./typedef.js').SceneLoadingProgress} SceneLoadingProgress */
|
|
20
23
|
|
|
@@ -39,7 +42,8 @@ export default class SceneBase {
|
|
|
39
42
|
// return this.state === STATE_ABORTED;
|
|
40
43
|
// });
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
/** @type {((progress:SceneLoadingProgress)=>void)[]} */
|
|
46
|
+
#preloadListeners = [];
|
|
43
47
|
|
|
44
48
|
/** @type {SceneLoadingProgress} */
|
|
45
49
|
progress = $derived.by(() => {
|
|
@@ -85,7 +89,6 @@ export default class SceneBase {
|
|
|
85
89
|
};
|
|
86
90
|
});
|
|
87
91
|
|
|
88
|
-
|
|
89
92
|
/**
|
|
90
93
|
* Construct SceneBase
|
|
91
94
|
*/
|
|
@@ -110,25 +113,48 @@ export default class SceneBase {
|
|
|
110
113
|
}
|
|
111
114
|
});
|
|
112
115
|
|
|
113
|
-
|
|
114
116
|
$effect(() => {
|
|
115
117
|
if (this.state === STATE_LOADING) {
|
|
116
|
-
|
|
117
118
|
// Check if any source failed during loading
|
|
118
119
|
const sources = this.sources;
|
|
119
120
|
|
|
120
121
|
for (const source of sources) {
|
|
121
122
|
const loader = this.getLoaderFromSource(source);
|
|
122
123
|
if (loader.state === STATE_ERROR) {
|
|
123
|
-
this.#state.send(
|
|
124
|
+
this.#state.send(
|
|
125
|
+
ERROR,
|
|
126
|
+
loader.error || new Error('Source loading failed')
|
|
127
|
+
);
|
|
124
128
|
break;
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
}
|
|
132
|
+
});
|
|
128
133
|
|
|
134
|
+
$effect(() => {
|
|
135
|
+
this.#updatePreloadProgressListeners(this.progress);
|
|
129
136
|
});
|
|
130
137
|
} // end constructor
|
|
131
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Call preload progress listeners
|
|
141
|
+
*
|
|
142
|
+
* @param {SceneLoadingProgress} progress
|
|
143
|
+
*/
|
|
144
|
+
#updatePreloadProgressListeners(progress) {
|
|
145
|
+
for (const fn of this.#preloadListeners) {
|
|
146
|
+
try {
|
|
147
|
+
fn(progress);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
throw new DetailedError(
|
|
150
|
+
'Error in progress listener',
|
|
151
|
+
null,
|
|
152
|
+
/** @type {Error} */ (e)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
132
158
|
/* ==== Abstract methods - must be implemented by subclasses */
|
|
133
159
|
|
|
134
160
|
/**
|
|
@@ -181,7 +207,6 @@ export default class SceneBase {
|
|
|
181
207
|
* Object with promise that resolves when loaded and abort function
|
|
182
208
|
*/
|
|
183
209
|
preload({ timeoutMs = 10000, onProgress } = {}) {
|
|
184
|
-
|
|
185
210
|
/** @type {number|NodeJS.Timeout|null} */
|
|
186
211
|
let timeoutId = null;
|
|
187
212
|
|
|
@@ -189,17 +214,17 @@ export default class SceneBase {
|
|
|
189
214
|
let progressIntervalId = null;
|
|
190
215
|
|
|
191
216
|
let isAborted = false;
|
|
192
|
-
|
|
193
|
-
/** @type {SceneLoadingProgress|null} */
|
|
194
|
-
let lastSentProgress = null;
|
|
195
217
|
|
|
196
218
|
const abort = () => {
|
|
197
219
|
if (isAborted) return;
|
|
198
220
|
isAborted = true;
|
|
199
221
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
222
|
+
// Remove progress listener
|
|
223
|
+
if (onProgress) {
|
|
224
|
+
const index = this.#preloadListeners.indexOf(onProgress);
|
|
225
|
+
if (index >= 0) {
|
|
226
|
+
this.#preloadListeners.splice(index, 1);
|
|
227
|
+
}
|
|
203
228
|
}
|
|
204
229
|
|
|
205
230
|
if (progressIntervalId) {
|
|
@@ -211,51 +236,64 @@ export default class SceneBase {
|
|
|
211
236
|
};
|
|
212
237
|
|
|
213
238
|
const promise = new Promise((resolve, reject) => {
|
|
214
|
-
// Set up progress tracking with
|
|
239
|
+
// Set up progress tracking with reactive listener
|
|
215
240
|
if (onProgress) {
|
|
216
|
-
|
|
217
|
-
if (!isAborted && this.state === STATE_LOADING) {
|
|
218
|
-
const currentProgress = this.progress;
|
|
219
|
-
lastSentProgress = currentProgress;
|
|
220
|
-
onProgress(currentProgress);
|
|
221
|
-
}
|
|
222
|
-
}, 50); // Poll every 50ms
|
|
241
|
+
this.#preloadListeners.push(onProgress);
|
|
223
242
|
}
|
|
224
243
|
|
|
225
|
-
// Set up
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
244
|
+
// // Set up progress tracking with polling (fallback if reactive doesn't work)
|
|
245
|
+
// if (onProgress) {
|
|
246
|
+
// progressIntervalId = setInterval(() => {
|
|
247
|
+
// if (!isAborted && this.state === STATE_LOADING) {
|
|
248
|
+
// const currentProgress = this.progress;
|
|
249
|
+
// onProgress(currentProgress);
|
|
250
|
+
// }
|
|
251
|
+
// }, 50); // Poll every 50ms
|
|
252
|
+
// }
|
|
253
|
+
|
|
254
|
+
// // Set up progress tracking with polling (fallback if reactive doesn't work)
|
|
255
|
+
// if (onProgress) {
|
|
256
|
+
// progressIntervalId = setInterval(() => {
|
|
257
|
+
// if (!isAborted && this.state === STATE_LOADING) {
|
|
258
|
+
// const currentProgress = this.progress;
|
|
259
|
+
// onProgress(currentProgress);
|
|
260
|
+
// }
|
|
261
|
+
// }, 50); // Poll every 50ms
|
|
262
|
+
// }
|
|
232
263
|
|
|
233
264
|
// Start loading
|
|
234
265
|
this.load();
|
|
235
266
|
|
|
236
|
-
// Wait for completion with
|
|
237
|
-
|
|
267
|
+
// Wait for completion with timeout
|
|
268
|
+
// 0 means no timeout, but we still need a reasonable value for waitForState
|
|
269
|
+
const waitTimeout = timeoutMs > 0 ? timeoutMs : 120000;
|
|
270
|
+
|
|
238
271
|
waitForState(() => {
|
|
239
|
-
return
|
|
240
|
-
|
|
241
|
-
|
|
272
|
+
return (
|
|
273
|
+
this.loaded ||
|
|
274
|
+
this.state === STATE_ABORTED ||
|
|
275
|
+
this.state === STATE_ERROR
|
|
276
|
+
);
|
|
242
277
|
}, waitTimeout)
|
|
243
278
|
.then(() => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
279
|
+
// Remove progress listener
|
|
280
|
+
if (onProgress) {
|
|
281
|
+
const index = this.#preloadListeners.indexOf(onProgress);
|
|
282
|
+
if (index >= 0) {
|
|
283
|
+
this.#preloadListeners.splice(index, 1);
|
|
284
|
+
}
|
|
285
|
+
|
|
247
286
|
}
|
|
248
287
|
|
|
288
|
+
// Cleanup polling (fallback if reactive doesn't work)
|
|
249
289
|
if (progressIntervalId) {
|
|
250
290
|
clearInterval(progressIntervalId);
|
|
251
291
|
progressIntervalId = null;
|
|
252
|
-
|
|
253
|
-
|
|
292
|
+
|
|
293
|
+
// Send final progress when loading completes (for polling fallback)
|
|
294
|
+
if (onProgress && this.loaded) {
|
|
254
295
|
const finalProgress = this.progress;
|
|
255
|
-
|
|
256
|
-
if (this.loaded) {
|
|
257
|
-
onProgress(finalProgress);
|
|
258
|
-
}
|
|
296
|
+
onProgress(finalProgress);
|
|
259
297
|
}
|
|
260
298
|
}
|
|
261
299
|
|
|
@@ -269,7 +307,34 @@ export default class SceneBase {
|
|
|
269
307
|
reject(new Error(`Preload failed: unexpected state ${this.state}`));
|
|
270
308
|
}
|
|
271
309
|
})
|
|
272
|
-
.catch(
|
|
310
|
+
.catch((error) => {
|
|
311
|
+
// Handle timeout errors from waitForState
|
|
312
|
+
if (error instanceof TimeoutError) {
|
|
313
|
+
abort();
|
|
314
|
+
reject(new Error(`Preload timed out after ${timeoutMs}ms`));
|
|
315
|
+
} else {
|
|
316
|
+
reject(error);
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
.finally(() => {
|
|
320
|
+
// Send final progress update regardless of success/failure
|
|
321
|
+
if (onProgress) {
|
|
322
|
+
const finalProgress = this.progress;
|
|
323
|
+
onProgress(finalProgress);
|
|
324
|
+
|
|
325
|
+
// Remove progress listener
|
|
326
|
+
const index = this.#preloadListeners.indexOf(onProgress);
|
|
327
|
+
if (index >= 0) {
|
|
328
|
+
this.#preloadListeners.splice(index, 1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// // Cleanup polling (fallback if reactive doesn't work)
|
|
333
|
+
// if (progressIntervalId) {
|
|
334
|
+
// clearInterval(progressIntervalId);
|
|
335
|
+
// progressIntervalId = null;
|
|
336
|
+
// }
|
|
337
|
+
});
|
|
273
338
|
});
|
|
274
339
|
|
|
275
340
|
return { promise, abort };
|
|
@@ -296,7 +361,7 @@ export default class SceneBase {
|
|
|
296
361
|
const loader = this.getLoaderFromSource(source);
|
|
297
362
|
loader.abort();
|
|
298
363
|
}
|
|
299
|
-
|
|
364
|
+
|
|
300
365
|
// Defer ABORTED transition to avoid re-entrant state machine calls
|
|
301
366
|
setTimeout(() => {
|
|
302
367
|
// Only transition to ABORTED if still in ABORTING state
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { tick } from 'svelte';
|
|
2
2
|
|
|
3
|
+
import { TimeoutError } from '../../../generic/errors.js';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Waits for a state condition to be met by running the checkFn
|
|
5
7
|
* function after each Svelte tick
|
|
@@ -25,7 +27,7 @@ export function waitForState(checkFn, maxWaitMs = 1000) {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
if (Date.now() - startedAt >= maxWaitMs) {
|
|
28
|
-
reject(new
|
|
30
|
+
reject(new TimeoutError(`State change timeout`));
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
31
33
|
|