rayzee 9.0.0 → 9.1.0

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 (94) hide show
  1. package/README.md +139 -8
  2. package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
  3. package/dist/{NeuralSuperRes-CQLCLpcC.js → NeuralSuperRes-CbNvQjCw.js} +3 -2
  4. package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
  5. package/dist/rayzee.es.js +10344 -5334
  6. package/dist/rayzee.umd.js +265 -265
  7. package/package.json +1 -1
  8. package/src/AssetConfig.js +4 -0
  9. package/src/EngineDefaults.js +38 -56
  10. package/src/EngineEvents.js +3 -0
  11. package/src/EngineIssues.js +6 -0
  12. package/src/Headless.js +3 -1
  13. package/src/Passes/AIUpscaler.js +2 -1
  14. package/src/Passes/OIDNDenoiser.js +3 -6
  15. package/src/PathTracerApp.js +290 -19
  16. package/src/Processor/ArchiveCache.js +120 -0
  17. package/src/Processor/ArchiveReader.js +179 -32
  18. package/src/Processor/AssetLoader.js +514 -76
  19. package/src/Processor/AtmosphereModel.js +465 -0
  20. package/src/Processor/BVHBuilder.js +4 -1
  21. package/src/Processor/BVHLeafFold.js +100 -0
  22. package/src/Processor/BVHRefitter.js +98 -214
  23. package/src/Processor/ChunkedRecords.js +189 -4
  24. package/src/Processor/EmissiveTriangleBuilder.js +8 -0
  25. package/src/Processor/GeometryExtractor.js +238 -40
  26. package/src/Processor/InstanceTable.js +55 -37
  27. package/src/Processor/PBRT/PBRTCurves.js +104 -27
  28. package/src/Processor/PBRT/PBRTParser.js +132 -22
  29. package/src/Processor/PBRT/PBRTSceneBuilder.js +192 -49
  30. package/src/Processor/PBRT/buffers.js +27 -0
  31. package/src/Processor/PBRT/index.js +55 -15
  32. package/src/Processor/PackedRayBuffer.js +10 -13
  33. package/src/Processor/ParallelBVHBuilder.js +36 -15
  34. package/src/Processor/PhysicalSky.js +308 -0
  35. package/src/Processor/QueueManager.js +1 -0
  36. package/src/Processor/SceneProcessor.js +1132 -181
  37. package/src/Processor/SplitBLAS.js +249 -0
  38. package/src/Processor/SunPosition.js +41 -0
  39. package/src/Processor/TextureCreator.js +21 -0
  40. package/src/Processor/Workers/AIUpscalerWorker.js +79 -52
  41. package/src/Processor/Workers/BVHWorker.js +17 -13
  42. package/src/Processor/ZipReader.js +207 -0
  43. package/src/RenderSettings.js +38 -0
  44. package/src/SceneState/SceneState.js +367 -0
  45. package/src/SceneState/portable.js +52 -0
  46. package/src/Stages/NormalDepth.js +7 -6
  47. package/src/Stages/PathTracer.js +108 -4
  48. package/src/Stages/PathTracerStage.js +251 -104
  49. package/src/Storage/BLASCache.js +190 -0
  50. package/src/Storage/CDFCache.js +77 -0
  51. package/src/Storage/DownloadCache.js +266 -0
  52. package/src/Storage/GeometrySpill.js +217 -0
  53. package/src/Storage/SceneGraphCodec.js +503 -0
  54. package/src/Storage/SpillStore.js +112 -0
  55. package/src/Storage/StorageManager.js +934 -0
  56. package/src/Storage/StorageOps.js +137 -0
  57. package/src/Storage/StorageWorker.js +37 -0
  58. package/src/Storage/events.js +29 -0
  59. package/src/Storage/identity.js +77 -0
  60. package/src/Storage/index.js +9 -0
  61. package/src/Storage/inlineTransport.js +24 -0
  62. package/src/Storage/locks.js +99 -0
  63. package/src/Storage/openStorage.js +87 -0
  64. package/src/Storage/sceneCachePolicy.js +12 -0
  65. package/src/Storage/shared.js +9 -0
  66. package/src/Storage/transport.js +62 -0
  67. package/src/TSL/Atmosphere.js +684 -0
  68. package/src/TSL/BVHTraversal.js +288 -239
  69. package/src/TSL/Common.js +11 -2
  70. package/src/TSL/Displacement.js +6 -6
  71. package/src/TSL/EmissiveSampling.js +5 -5
  72. package/src/TSL/EnvironmentCDF.js +176 -0
  73. package/src/TSL/GenerateKernel.js +3 -3
  74. package/src/TSL/HitFacet.js +4 -4
  75. package/src/TSL/LightsDirect.js +8 -8
  76. package/src/TSL/LightsSampling.js +55 -37
  77. package/src/TSL/Random.js +1 -0
  78. package/src/TSL/ShadeKernel.js +75 -19
  79. package/src/TSL/Sun.js +40 -0
  80. package/src/TSL/TextureSampling.js +6 -7
  81. package/src/index.js +18 -0
  82. package/src/managers/CameraManager.js +169 -1
  83. package/src/managers/EnvironmentManager.js +263 -33
  84. package/src/managers/GoboManager.js +19 -8
  85. package/src/managers/IESManager.js +3 -3
  86. package/src/managers/LightManager.js +98 -5
  87. package/src/managers/MaterialDataManager.js +22 -2
  88. package/src/managers/TransformManager.js +26 -0
  89. package/src/managers/UniformManager.js +5 -3
  90. package/src/managers/VideoRenderManager.js +3 -1
  91. package/src/managers/timeline/CameraTrack.js +22 -0
  92. package/src/managers/timeline/TimelineManager.js +14 -0
  93. package/src/version.js +3 -0
  94. package/src/Processor/ProceduralSky.js +0 -291
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The write side of storage, run where sync access handles exist: inside StorageWorker, or
3
+ * inline when the caller is itself a worker (or a test with a fake root).
4
+ */
5
+ export class StorageOps {
6
+
7
+ constructor( root ) {
8
+
9
+ this._root = root;
10
+ this._writers = new Map();
11
+ this._nextId = 1;
12
+
13
+ }
14
+
15
+ async _dir( path, create ) {
16
+
17
+ let dir = await this._root;
18
+ for ( const part of path ) dir = await dir.getDirectoryHandle( part, { create } );
19
+ return dir;
20
+
21
+ }
22
+
23
+ async openWriter( { path, name, truncate = true, length } ) {
24
+
25
+ const dir = await this._dir( path, true );
26
+ const handle = await ( await dir.getFileHandle( name, { create: true } ) ).createSyncAccessHandle();
27
+ if ( truncate ) handle.truncate( 0 );
28
+ else if ( length !== undefined && handle.getSize() > length ) handle.truncate( length );
29
+ const id = this._nextId ++;
30
+ this._writers.set( id, { handle, end: handle.getSize() } );
31
+ return { id };
32
+
33
+ }
34
+
35
+ write( { id, buffer, byteOffset = 0, byteLength, at } ) {
36
+
37
+ const writer = this._writers.get( id );
38
+ if ( ! writer ) throw new Error( `storage: writer ${id} is not open` );
39
+
40
+ const view = new Uint8Array( buffer, byteOffset, byteLength ?? buffer.byteLength - byteOffset );
41
+ const position = at ?? writer.end;
42
+ let written = 0;
43
+ while ( written < view.length ) {
44
+
45
+ const n = writer.handle.write( view.subarray( written ), { at: position + written } );
46
+ if ( n <= 0 ) throw new Error( 'storage: write made no progress' );
47
+ written += n;
48
+
49
+ }
50
+
51
+ writer.end = Math.max( writer.end, position + view.length );
52
+ return { bytes: view.length };
53
+
54
+ }
55
+
56
+ closeWriter( { id, abort = false } ) {
57
+
58
+ const writer = this._writers.get( id );
59
+ if ( ! writer ) return { size: 0 };
60
+ this._writers.delete( id );
61
+
62
+ try {
63
+
64
+ if ( ! abort ) writer.handle.flush();
65
+ return { size: writer.handle.getSize() };
66
+
67
+ } finally {
68
+
69
+ writer.handle.close();
70
+
71
+ }
72
+
73
+ }
74
+
75
+ async writeFile( { path, name, buffer, byteOffset = 0, byteLength } ) {
76
+
77
+ const { id } = await this.openWriter( { path, name } );
78
+ try {
79
+
80
+ this.write( { id, buffer, byteOffset, byteLength } );
81
+ return this.closeWriter( { id } );
82
+
83
+ } catch ( error ) {
84
+
85
+ this.closeWriter( { id, abort: true } );
86
+ throw error;
87
+
88
+ }
89
+
90
+ }
91
+
92
+ async readRange( { path, name, offset = 0, length } ) {
93
+
94
+ const fileHandle = await ( await this._dir( path, false ) ).getFileHandle( name );
95
+
96
+ let handle = null;
97
+ try {
98
+
99
+ handle = await fileHandle.createSyncAccessHandle();
100
+
101
+ } catch ( error ) {
102
+
103
+ if ( error.name !== 'NoModificationAllowedError' ) throw error;
104
+
105
+ }
106
+
107
+ if ( ! handle ) {
108
+
109
+ const file = await fileHandle.getFile();
110
+ const end = length === undefined ? file.size : offset + length;
111
+ return { buffer: await file.slice( offset, end ).arrayBuffer() };
112
+
113
+ }
114
+
115
+ try {
116
+
117
+ const size = handle.getSize();
118
+ const n = Math.max( 0, Math.min( length ?? size - offset, size - offset ) );
119
+ const buffer = new ArrayBuffer( n );
120
+ handle.read( buffer, { at: offset } );
121
+ return { buffer };
122
+
123
+ } finally {
124
+
125
+ handle.close();
126
+
127
+ }
128
+
129
+ }
130
+
131
+ dispose() {
132
+
133
+ for ( const id of [ ...this._writers.keys() ] ) this.closeWriter( { id, abort: true } );
134
+
135
+ }
136
+
137
+ }
@@ -0,0 +1,37 @@
1
+ import { StorageOps } from './StorageOps.js';
2
+
3
+ let ops = null;
4
+
5
+ async function rootFor( namespace, root ) {
6
+
7
+ if ( root ) return root;
8
+ const top = await navigator.storage.getDirectory();
9
+ return top.getDirectoryHandle( namespace, { create: true } );
10
+
11
+ }
12
+
13
+ self.onmessage = async ( { data } ) => {
14
+
15
+ const { id, op, args } = data;
16
+
17
+ try {
18
+
19
+ if ( op === 'init' ) {
20
+
21
+ ops = new StorageOps( rootFor( args.namespace, args.root ) );
22
+ postMessage( { id, result: {} } );
23
+ return;
24
+
25
+ }
26
+
27
+ const result = await ops[ op ]( args );
28
+ const transfer = result?.buffer instanceof ArrayBuffer ? [ result.buffer ] : [];
29
+ postMessage( { id, result }, transfer );
30
+
31
+ } catch ( error ) {
32
+
33
+ postMessage( { id, error: { name: error.name, message: error.message } } );
34
+
35
+ }
36
+
37
+ };
@@ -0,0 +1,29 @@
1
+ /** Minimal addEventListener/dispatchEvent, so the storage core runs in workers without three.js. */
2
+ export class Emitter {
3
+
4
+ constructor() {
5
+
6
+ this._handlers = new Map();
7
+
8
+ }
9
+
10
+ addEventListener( type, handler ) {
11
+
12
+ if ( ! this._handlers.has( type ) ) this._handlers.set( type, new Set() );
13
+ this._handlers.get( type ).add( handler );
14
+
15
+ }
16
+
17
+ removeEventListener( type, handler ) {
18
+
19
+ this._handlers.get( type )?.delete( handler );
20
+
21
+ }
22
+
23
+ dispatchEvent( event ) {
24
+
25
+ for ( const handler of [ ...( this._handlers.get( event.type ) ?? [] ) ] ) handler.call( this, event );
26
+
27
+ }
28
+
29
+ }
@@ -0,0 +1,77 @@
1
+ const encoder = new TextEncoder();
2
+
3
+ const HEAD_BYTES = 1 << 20;
4
+ const TAIL_BYTES = 1 << 20;
5
+ const PROBE_BYTES = 64 << 10;
6
+ const PROBES = 14;
7
+
8
+ function hex( bytes ) {
9
+
10
+ let out = '';
11
+ for ( const b of bytes ) out += b.toString( 16 ).padStart( 2, '0' );
12
+ return out;
13
+
14
+ }
15
+
16
+ export async function sha256Hex( data ) {
17
+
18
+ const bytes = typeof data === 'string' ? encoder.encode( data ) : data;
19
+ return hex( new Uint8Array( await crypto.subtle.digest( 'SHA-256', bytes ) ) );
20
+
21
+ }
22
+
23
+ export async function entryIdFor( key ) {
24
+
25
+ return ( await sha256Hex( key ) ).slice( 0, 32 );
26
+
27
+ }
28
+
29
+ /**
30
+ * SHA-256 over the head, tail and 14 evenly spaced probes of a Blob — ~3 MB read whatever its
31
+ * size. WebCrypto cannot stream, so a full hash of a multi-GB file would need it all in memory.
32
+ */
33
+ export async function sampleHash( blob ) {
34
+
35
+ if ( blob.size <= HEAD_BYTES + TAIL_BYTES + PROBES * PROBE_BYTES ) {
36
+
37
+ return sha256Hex( new Uint8Array( await blob.arrayBuffer() ) );
38
+
39
+ }
40
+
41
+ const parts = [ blob.slice( 0, HEAD_BYTES ) ];
42
+ const span = blob.size - HEAD_BYTES - TAIL_BYTES - PROBE_BYTES;
43
+ for ( let i = 0; i < PROBES; i ++ ) {
44
+
45
+ const at = HEAD_BYTES + Math.floor( span * ( i + 0.5 ) / PROBES );
46
+ parts.push( blob.slice( at, at + PROBE_BYTES ) );
47
+
48
+ }
49
+
50
+ parts.push( blob.slice( blob.size - TAIL_BYTES ) );
51
+ return sha256Hex( new Uint8Array( await new Blob( parts ).arrayBuffer() ) );
52
+
53
+ }
54
+
55
+ /** @returns {Promise<{name: string, size: number, lastModified: number, sample: string}>} */
56
+ export async function fileIdentity( file ) {
57
+
58
+ return {
59
+ name: file.name ?? '',
60
+ size: file.size,
61
+ lastModified: file.lastModified ?? 0,
62
+ sample: await sampleHash( file ),
63
+ };
64
+
65
+ }
66
+
67
+ export function identityKey( identity ) {
68
+
69
+ return `file:${identity.name}|${identity.size}|${identity.lastModified}|${identity.sample}`;
70
+
71
+ }
72
+
73
+ export function sameIdentity( a, b ) {
74
+
75
+ return !! a && !! b && a.size === b.size && a.sample === b.sample && a.name === b.name;
76
+
77
+ }
@@ -0,0 +1,9 @@
1
+ export {
2
+ StorageManager, StorageArea, StorageEntry, EntryWriter, STORAGE_KIND, ENGINE_AREAS, openInlineStorage,
3
+ } from './StorageManager.js';
4
+ export { openStorage, acquireSharedStorage } from './openStorage.js';
5
+ export { sharedStorage } from './shared.js';
6
+ export { fileIdentity, identityKey, sameIdentity, sampleHash, sha256Hex } from './identity.js';
7
+ export { DownloadCache, DOWNLOAD_POLICY, fetchFile, nameFromUrl, cachedObjectURL, sharedDownloads } from './DownloadCache.js';
8
+ export { acquireLock, heldLockNames } from './locks.js';
9
+ export { SpillStore } from './SpillStore.js';
@@ -0,0 +1,24 @@
1
+ import { StorageOps } from './StorageOps.js';
2
+
3
+ /** Writes on this thread through sync access handles: a worker's own storage, or tests with a fake root. */
4
+ export class InlineTransport {
5
+
6
+ constructor( root ) {
7
+
8
+ this._ops = new StorageOps( root );
9
+
10
+ }
11
+
12
+ async call( op, args ) {
13
+
14
+ return this._ops[ op ]( args );
15
+
16
+ }
17
+
18
+ dispose() {
19
+
20
+ this._ops.dispose();
21
+
22
+ }
23
+
24
+ }
@@ -0,0 +1,99 @@
1
+ const local = new Map();
2
+
3
+ function tryGrant( state, mode ) {
4
+
5
+ if ( state.exclusive ) return false;
6
+ if ( mode === 'exclusive' && state.shared > 0 ) return false;
7
+ if ( mode === 'exclusive' ) state.exclusive = true;
8
+ else state.shared ++;
9
+ return true;
10
+
11
+ }
12
+
13
+ function localAcquire( name, mode, ifAvailable ) {
14
+
15
+ let state = local.get( name );
16
+ if ( ! state ) {
17
+
18
+ state = { shared: 0, exclusive: false, queue: [] };
19
+ local.set( name, state );
20
+
21
+ }
22
+
23
+ const release = once( () => {
24
+
25
+ if ( mode === 'exclusive' ) state.exclusive = false;
26
+ else state.shared --;
27
+
28
+ while ( state.queue.length && tryGrant( state, state.queue[ 0 ].mode ) ) state.queue.shift().grant();
29
+ if ( ! state.exclusive && state.shared === 0 && state.queue.length === 0 ) local.delete( name );
30
+
31
+ } );
32
+
33
+ if ( state.queue.length === 0 && tryGrant( state, mode ) ) return Promise.resolve( release );
34
+
35
+ if ( ifAvailable ) {
36
+
37
+ if ( ! state.exclusive && state.shared === 0 && state.queue.length === 0 ) local.delete( name );
38
+ return Promise.resolve( null );
39
+
40
+ }
41
+
42
+ return new Promise( ( resolve ) => state.queue.push( { mode, grant: () => resolve( release ) } ) );
43
+
44
+ }
45
+
46
+ function once( fn ) {
47
+
48
+ let done = false;
49
+ return () => {
50
+
51
+ if ( done ) return;
52
+ done = true;
53
+ fn();
54
+
55
+ };
56
+
57
+ }
58
+
59
+ const webLocks = () => ( typeof navigator !== 'undefined' && navigator.locks?.request ? navigator.locks : null );
60
+
61
+ /**
62
+ * Holds a Web Lock until the returned function is called; resolves null when `ifAvailable` and taken.
63
+ * Falls back to an in-process lock where Web Locks are missing (Node).
64
+ * @returns {Promise<?function(): void>}
65
+ */
66
+ export function acquireLock( name, { mode = 'exclusive', ifAvailable = false } = {} ) {
67
+
68
+ const locks = webLocks();
69
+ if ( ! locks ) return localAcquire( name, mode, ifAvailable );
70
+
71
+ return new Promise( ( resolve, reject ) => {
72
+
73
+ locks.request( name, { mode, ifAvailable }, ( lock ) => {
74
+
75
+ if ( ! lock ) {
76
+
77
+ resolve( null );
78
+ return undefined;
79
+
80
+ }
81
+
82
+ return new Promise( ( done ) => resolve( once( done ) ) );
83
+
84
+ } ).catch( reject );
85
+
86
+ } );
87
+
88
+ }
89
+
90
+ /** @returns {Promise<Set<string>>} names of locks currently held by any tab of this origin */
91
+ export async function heldLockNames() {
92
+
93
+ const locks = webLocks();
94
+ if ( ! locks ) return new Set( local.keys() );
95
+
96
+ const { held = [] } = await locks.query();
97
+ return new Set( held.map( ( lock ) => lock.name ) );
98
+
99
+ }
@@ -0,0 +1,87 @@
1
+ import { StorageManager, openInlineStorage } from './StorageManager.js';
2
+ import { WorkerTransport, inWorkerContext } from './transport.js';
3
+ import { sharedByNamespace } from './shared.js';
4
+
5
+ async function namespaceRoot( namespace ) {
6
+
7
+ const storage = typeof navigator !== 'undefined' ? navigator.storage : null;
8
+ if ( ! storage?.getDirectory ) return { root: null, reason: 'this browser has no origin private file system' };
9
+
10
+ try {
11
+
12
+ const top = await storage.getDirectory();
13
+ return { root: await top.getDirectoryHandle( namespace, { create: true } ) };
14
+
15
+ } catch ( error ) {
16
+
17
+ return { root: null, reason: `${error.name}: ${error.message}` };
18
+
19
+ }
20
+
21
+ }
22
+
23
+ /**
24
+ * Opens storage for a namespace (`cacheNamespace`). Resolves `{ storage: null, reason }` where the
25
+ * browser offers none — private windows, old browsers, Node without a fake.
26
+ * @param {Object} [options]
27
+ * @param {string} options.namespace
28
+ * @param {FileSystemDirectoryHandle} [options.root] - use this directory instead of the OPFS one
29
+ * @param {'auto'|'inline'|'worker'} [options.transport] - 'inline' writes on this thread (needs sync handles here)
30
+ * @returns {Promise<{storage: ?StorageManager, reason?: string}>}
31
+ */
32
+ export async function openStorage( { namespace, root = null, transport = 'auto' } = {} ) {
33
+
34
+ const resolved = root ? { root } : await namespaceRoot( namespace );
35
+ if ( ! resolved.root ) return { storage: null, reason: resolved.reason };
36
+
37
+ const inline = transport === 'inline' || ( transport === 'auto' && ( inWorkerContext() || typeof Worker === 'undefined' ) );
38
+ if ( inline ) return { storage: openInlineStorage( { namespace, root: resolved.root } ) };
39
+
40
+ const channel = new WorkerTransport( { namespace, root } );
41
+ const storage = new StorageManager( { root: resolved.root, transport: channel, namespace } );
42
+ storage.sweep().catch( () => {} );
43
+ return { storage };
44
+
45
+ }
46
+
47
+ /**
48
+ * One manager per namespace per page, shared by every app on it; the last `release()` disposes it.
49
+ * @returns {Promise<{storage: ?StorageManager, reason?: string, release: function(): void}>}
50
+ */
51
+ export async function acquireSharedStorage( namespace ) {
52
+
53
+ let slot = sharedByNamespace.get( namespace );
54
+ if ( ! slot ) {
55
+
56
+ slot = { refs: 0, opened: openStorage( { namespace } ) };
57
+ sharedByNamespace.set( namespace, slot );
58
+
59
+ }
60
+
61
+ slot.refs ++;
62
+ const { storage, reason } = await slot.opened;
63
+ slot.storage = storage;
64
+
65
+ let released = false;
66
+ const release = () => {
67
+
68
+ if ( released ) return;
69
+ released = true;
70
+ slot.refs --;
71
+ if ( slot.refs > 0 ) return;
72
+ if ( sharedByNamespace.get( namespace ) === slot ) sharedByNamespace.delete( namespace );
73
+ storage?.dispose();
74
+
75
+ };
76
+
77
+ if ( ! storage ) {
78
+
79
+ release();
80
+ return { storage: null, reason, release: () => {} };
81
+
82
+ }
83
+
84
+ return { storage, release };
85
+
86
+ }
87
+
@@ -0,0 +1,12 @@
1
+ /** A scene is stored only when rebuilding it took at least this long (decision D6). */
2
+ export const SCENE_CACHE_MIN_BUILD_MS = 10_000;
3
+
4
+ // File.slice reads measured 1.7–2.2 GB/s in Chrome (phase 0); kept conservative.
5
+ const READ_BYTES_PER_MS = 1.5e6;
6
+
7
+ /** Worth storing: the build was slow, and reading it back would take under a third as long. */
8
+ export function worthStoring( buildMs, bytes, minBuildMs = SCENE_CACHE_MIN_BUILD_MS ) {
9
+
10
+ return buildMs >= minBuildMs && bytes / READ_BYTES_PER_MS < buildMs / 3;
11
+
12
+ }
@@ -0,0 +1,9 @@
1
+ /** Per-namespace managers opened on this page, shared by every app on it. */
2
+ export const sharedByNamespace = new Map();
3
+
4
+ /** The storage an app on this page opened for `namespace`, or null — for code that holds no app. */
5
+ export function sharedStorage( namespace ) {
6
+
7
+ return sharedByNamespace.get( namespace )?.storage ?? null;
8
+
9
+ }
@@ -0,0 +1,62 @@
1
+ import StorageWorker from './StorageWorker.js?worker&inline';
2
+
3
+ function toError( { name, message } ) {
4
+
5
+ const error = new Error( message );
6
+ error.name = name;
7
+ return error;
8
+
9
+ }
10
+
11
+ export class WorkerTransport {
12
+
13
+ constructor( { namespace, root = null } ) {
14
+
15
+ this._worker = new StorageWorker();
16
+ this._pending = new Map();
17
+ this._nextId = 0;
18
+
19
+ this._worker.onmessage = ( { data } ) => {
20
+
21
+ const request = this._pending.get( data.id );
22
+ if ( ! request ) return;
23
+ this._pending.delete( data.id );
24
+ if ( data.error ) request.reject( toError( data.error ) );
25
+ else request.resolve( data.result );
26
+
27
+ };
28
+
29
+ this._ready = this.call( 'init', { namespace, root } );
30
+
31
+ }
32
+
33
+ call( op, args, transfer = [] ) {
34
+
35
+ if ( ! this._worker ) return Promise.reject( new Error( 'storage: transport disposed' ) );
36
+
37
+ return new Promise( ( resolve, reject ) => {
38
+
39
+ const id = this._nextId ++;
40
+ this._pending.set( id, { resolve, reject } );
41
+ this._worker.postMessage( { id, op, args }, transfer );
42
+
43
+ } );
44
+
45
+ }
46
+
47
+ dispose() {
48
+
49
+ this._worker?.terminate();
50
+ this._worker = null;
51
+ for ( const { reject } of this._pending.values() ) reject( new Error( 'storage: transport disposed' ) );
52
+ this._pending.clear();
53
+
54
+ }
55
+
56
+ }
57
+
58
+ export function inWorkerContext() {
59
+
60
+ return typeof WorkerGlobalScope !== 'undefined' && globalThis instanceof WorkerGlobalScope; // eslint-disable-line no-undef
61
+
62
+ }