@flighthq/assets 0.2.0 → 0.2.1-next.410.a23f189
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/package.json +4 -4
- package/src/assetLibrary.test.ts +80 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/assets",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-next.410.a23f189",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@flighthq/loader": "0.2.
|
|
36
|
-
"@flighthq/signals": "0.2.
|
|
37
|
-
"@flighthq/types": "0.2.
|
|
35
|
+
"@flighthq/loader": "0.2.1-next.410.a23f189",
|
|
36
|
+
"@flighthq/signals": "0.2.1-next.410.a23f189",
|
|
37
|
+
"@flighthq/types": "0.2.1-next.410.a23f189"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^5.3.0"
|
package/src/assetLibrary.test.ts
CHANGED
|
@@ -168,6 +168,53 @@ describe('getAssetRefCount', () => {
|
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
describe('loadAssetGroup', () => {
|
|
171
|
+
it('makes successfully loaded assets accessible even when another group member fails', async () => {
|
|
172
|
+
const library = createAssetLibrary();
|
|
173
|
+
let failNextId: string | null = null;
|
|
174
|
+
const disposed: unknown[] = [];
|
|
175
|
+
const adapter = {
|
|
176
|
+
load(descriptor: Readonly<AssetDescriptor>): Promise<{ id: string }> {
|
|
177
|
+
if (descriptor.id === failNextId) {
|
|
178
|
+
return Promise.reject(new Error(`load failed: ${descriptor.id}`));
|
|
179
|
+
}
|
|
180
|
+
return Promise.resolve({ id: descriptor.id });
|
|
181
|
+
},
|
|
182
|
+
dispose(value: { id: string }): void {
|
|
183
|
+
disposed.push(value);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
registerAssetLoader(library, 'image', adapter);
|
|
187
|
+
loadAssetManifest(library, [
|
|
188
|
+
{ id: 'ok-1', url: 'ok-1.bin', type: 'image', group: 'mixed' },
|
|
189
|
+
{ id: 'bad', url: 'bad.bin', type: 'image', group: 'mixed' },
|
|
190
|
+
{ id: 'ok-2', url: 'ok-2.bin', type: 'image', group: 'mixed' },
|
|
191
|
+
]);
|
|
192
|
+
failNextId = 'bad';
|
|
193
|
+
|
|
194
|
+
// loadAssetGroup routes each acquire through @flighthq/loader, which rejects the per-item handle on
|
|
195
|
+
// failure. That handle is void'd by loadAssetGroup (error-policy: continue), so the rejection
|
|
196
|
+
// surfaces as an unhandled rejection. Suppress it for this test.
|
|
197
|
+
const rejections: unknown[] = [];
|
|
198
|
+
const capture = (reason: unknown) => {
|
|
199
|
+
rejections.push(reason);
|
|
200
|
+
};
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
|
+
(globalThis as any).process.on('unhandledRejection', capture);
|
|
203
|
+
|
|
204
|
+
await loadAssetGroup(library, 'mixed');
|
|
205
|
+
// Drain the microtask queue so the rejection fires within the listener's window.
|
|
206
|
+
await tick();
|
|
207
|
+
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
+
(globalThis as any).process.removeListener('unhandledRejection', capture);
|
|
210
|
+
expect(rejections).toHaveLength(1);
|
|
211
|
+
|
|
212
|
+
expect(getAsset(library, 'ok-1')).toEqual({ id: 'ok-1' });
|
|
213
|
+
expect(getAsset(library, 'ok-2')).toEqual({ id: 'ok-2' });
|
|
214
|
+
// The failed asset never became resident.
|
|
215
|
+
expect(getAsset(library, 'bad')).toBeNull();
|
|
216
|
+
});
|
|
217
|
+
|
|
171
218
|
it('preloads a group through the loader with bounded concurrency and aggregate progress', async () => {
|
|
172
219
|
const library = createAssetLibrary();
|
|
173
220
|
const mock = createMockAdapter();
|
|
@@ -254,6 +301,23 @@ describe('registerAssetLoader', () => {
|
|
|
254
301
|
});
|
|
255
302
|
|
|
256
303
|
describe('releaseAsset', () => {
|
|
304
|
+
it('disposes the orphaned value when released while the load is still in flight', async () => {
|
|
305
|
+
const { library, mock } = libraryWith('hero');
|
|
306
|
+
const promise = acquireAsset(library, 'hero');
|
|
307
|
+
// Release before the load settles — refcount drops to zero, entry removed.
|
|
308
|
+
releaseAsset(library, 'hero');
|
|
309
|
+
expect(getAssetRefCount(library, 'hero')).toBe(0);
|
|
310
|
+
expect(getAsset(library, 'hero')).toBeNull();
|
|
311
|
+
|
|
312
|
+
// Let the load resolve — the continuation detects the orphan and disposes the value.
|
|
313
|
+
mock.flush();
|
|
314
|
+
await promise;
|
|
315
|
+
expect(mock.disposed).toHaveLength(1);
|
|
316
|
+
expect(mock.disposed[0]).toEqual({ id: 'hero' });
|
|
317
|
+
// The value is not retained.
|
|
318
|
+
expect(getAsset(library, 'hero')).toBeNull();
|
|
319
|
+
});
|
|
320
|
+
|
|
257
321
|
it('disposes and drops the asset at reference count zero', async () => {
|
|
258
322
|
const { library, mock } = libraryWith('hero');
|
|
259
323
|
const promise = acquireAsset(library, 'hero');
|
|
@@ -279,6 +343,22 @@ describe('releaseAsset', () => {
|
|
|
279
343
|
expect(mock.disposed).toEqual([]);
|
|
280
344
|
expect(getAssetRefCount(library, 'hero')).toBe(0);
|
|
281
345
|
});
|
|
346
|
+
|
|
347
|
+
it('keeps the asset loaded when one of two holders releases', async () => {
|
|
348
|
+
const { library, mock } = libraryWith('hero');
|
|
349
|
+
const first = acquireAsset<{ id: string }>(library, 'hero');
|
|
350
|
+
const second = acquireAsset<{ id: string }>(library, 'hero');
|
|
351
|
+
expect(mock.loadCalls).toBe(1);
|
|
352
|
+
mock.flush();
|
|
353
|
+
const [a, b] = await Promise.all([first, second]);
|
|
354
|
+
expect(a).toBe(b);
|
|
355
|
+
expect(getAssetRefCount(library, 'hero')).toBe(2);
|
|
356
|
+
|
|
357
|
+
releaseAsset(library, 'hero');
|
|
358
|
+
expect(getAssetRefCount(library, 'hero')).toBe(1);
|
|
359
|
+
expect(mock.disposed).toEqual([]);
|
|
360
|
+
expect(getAsset(library, 'hero')).toBe(a);
|
|
361
|
+
});
|
|
282
362
|
});
|
|
283
363
|
|
|
284
364
|
describe('releaseAssetGroup', () => {
|