@needle-tools/gltf-progressive 3.4.0-next.dbcee85 → 4.0.0-alpha

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.
@@ -1,68 +0,0 @@
1
- type PromiseType = "texture" | "mesh";
2
- export type PromiseGroupOptions = {
3
- /** Name for debugging purposes */
4
- name?: string;
5
- /** Define how many frames new LOD promises will at least be captured and awaited. The group will resolve after all promises captured during this time have resolved (or when the abort signal is triggered).
6
- * @default 2 frames, which means the group will capture promises for 2 frames before resolving.
7
- */
8
- frames?: number;
9
- /** If set to true at least one LOD loading promise must be captured before this promise will resolve.
10
- * After the first promise has been captured the group will wait for the amount of frames specified in the `frames` option.
11
- */
12
- waitForFirstCapture?: boolean;
13
- /** An optional signal to abort the promise */
14
- signal?: AbortSignal;
15
- /**
16
- * If set to true, the group will only await one promise per object.
17
- * @default 1
18
- */
19
- maxPromisesPerObject?: number;
20
- };
21
- type PromiseGroupResolveResult = {
22
- /**
23
- * `true` if the group was cancelled, `false` if it was resolved normally.
24
- */
25
- cancelled: boolean;
26
- /**
27
- * The number of promises that started to being awaited
28
- */
29
- awaited_count: number;
30
- /**
31
- * The number of promises that were resolved
32
- */
33
- resolved_count: number;
34
- };
35
- /**
36
- * A group of promises that can be awaited together.
37
- * This is used for awaiting LOD
38
- */
39
- export declare class PromiseGroup {
40
- static readonly addPromise: (type: PromiseType, object: object, promise: Promise<any>, groups: PromiseGroup[]) => void;
41
- readonly ready: Promise<PromiseGroupResolveResult>;
42
- /** The number of promises that have been added to this group so far */
43
- get awaitedCount(): number;
44
- /** The number of promises that have been resolved */
45
- get resolvedCount(): number;
46
- /** The number of promises that are in-flight */
47
- get currentlyAwaiting(): number;
48
- private _resolve;
49
- private readonly _signal?;
50
- /** start frame can be undefined if the user configured this group to wait for the first promise.
51
- * Then the start frame will be set when the first promise has been added to the group */
52
- private _frame_start;
53
- /** How many frames to capture since the start frame */
54
- private _frames_to_capture;
55
- private _resolved;
56
- private _addedCount;
57
- private _resolvedCount;
58
- /** These promises are currently being awaited */
59
- private readonly _awaiting;
60
- private _maxPromisesPerObject;
61
- constructor(frame: number, options: PromiseGroupOptions);
62
- private _currentFrame;
63
- update(frame: number): void;
64
- private readonly _seen;
65
- private add;
66
- private resolveNow;
67
- }
68
- export {};
@@ -1,108 +0,0 @@
1
- import { debug } from "./lods.debug.js";
2
- /**
3
- * A group of promises that can be awaited together.
4
- * This is used for awaiting LOD
5
- */
6
- export class PromiseGroup {
7
- static addPromise = (type, object, promise, groups) => {
8
- groups.forEach(group => {
9
- group.add(type, object, promise);
10
- });
11
- };
12
- ready;
13
- /** The number of promises that have been added to this group so far */
14
- get awaitedCount() {
15
- return this._addedCount;
16
- }
17
- /** The number of promises that have been resolved */
18
- get resolvedCount() {
19
- return this._resolvedCount;
20
- }
21
- /** The number of promises that are in-flight */
22
- get currentlyAwaiting() {
23
- return this._awaiting.length;
24
- }
25
- _resolve;
26
- _signal;
27
- /** start frame can be undefined if the user configured this group to wait for the first promise.
28
- * Then the start frame will be set when the first promise has been added to the group */
29
- _frame_start;
30
- /** How many frames to capture since the start frame */
31
- _frames_to_capture;
32
- _resolved = false;
33
- _addedCount = 0;
34
- _resolvedCount = 0;
35
- /** These promises are currently being awaited */
36
- _awaiting = [];
37
- _maxPromisesPerObject = 1;
38
- constructor(frame, options) {
39
- const minFrames = 2; // wait at least 2 frames to capture promises
40
- const framesToCapture = Math.max(options.frames ?? minFrames, minFrames); // default to 2 frames and make sure it's at least 2 frames
41
- this._frame_start = options.waitForFirstCapture ? undefined : frame;
42
- this._frames_to_capture = framesToCapture;
43
- this.ready = new Promise((resolve) => {
44
- this._resolve = resolve;
45
- });
46
- this.ready.finally(() => {
47
- this._resolved = true;
48
- this._awaiting.length = 0;
49
- });
50
- this._signal = options.signal;
51
- this._signal?.addEventListener("abort", () => {
52
- this.resolveNow();
53
- });
54
- this._maxPromisesPerObject = Math.max(1, options.maxPromisesPerObject ?? 1);
55
- }
56
- _currentFrame = 0;
57
- update(frame) {
58
- this._currentFrame = frame;
59
- // Check if start frame is not defined yet but we have added objects since the last update
60
- if (this._frame_start === undefined && this._addedCount > 0) {
61
- this._frame_start = frame;
62
- }
63
- // If we've passes the frame capture end frame and didn't add any promises, we resolve immediately
64
- if (this._signal?.aborted || (this._awaiting.length === 0 && (this._frame_start !== undefined && (frame > this._frame_start + this._frames_to_capture)))) {
65
- this.resolveNow();
66
- }
67
- }
68
- _seen = new WeakMap();
69
- add(_type, object, promise) {
70
- if (this._resolved) {
71
- if (debug)
72
- console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
73
- return;
74
- }
75
- if (this._frame_start !== undefined && this._currentFrame > this._frame_start + this._frames_to_capture) {
76
- return; // we are not capturing any more promises
77
- }
78
- if (this._maxPromisesPerObject >= 1) {
79
- if (this._seen.has(object)) {
80
- let count = this._seen.get(object);
81
- if (count >= this._maxPromisesPerObject) {
82
- if (debug)
83
- console.warn(`PromiseGroup: Already awaiting object ignoring new promise for it.`);
84
- return;
85
- }
86
- this._seen.set(object, count + 1);
87
- }
88
- else {
89
- this._seen.set(object, 1);
90
- }
91
- }
92
- this._awaiting.push(promise);
93
- this._addedCount++;
94
- promise.finally(() => {
95
- this._resolvedCount++;
96
- this._awaiting.splice(this._awaiting.indexOf(promise), 1);
97
- });
98
- }
99
- resolveNow() {
100
- if (this._resolved)
101
- return;
102
- this._resolve?.({
103
- awaited_count: this._addedCount,
104
- resolved_count: this._resolvedCount,
105
- cancelled: this._signal?.aborted ?? false,
106
- });
107
- }
108
- }
@@ -1,2 +0,0 @@
1
- export { patchModelViewer } from "./modelviewer.js";
2
- export { type NEEDLE_progressive_plugin } from "./plugin.js";
@@ -1 +0,0 @@
1
- export { patchModelViewer } from "./modelviewer.js";
@@ -1 +0,0 @@
1
- export declare function patchModelViewer(): void;
@@ -1,223 +0,0 @@
1
- import { LODsManager } from "../lods.manager.js";
2
- import { EXTENSION_NAME, NEEDLE_progressive, } from "../extension.js";
3
- const $meshLODSymbol = Symbol("NEEDLE_mesh_lod");
4
- const $textureLODSymbol = Symbol("NEEDLE_texture_lod");
5
- let documentObserver = null;
6
- export function patchModelViewer() {
7
- const ModelViewerElement = tryGetModelViewerConstructor();
8
- if (!ModelViewerElement) {
9
- return;
10
- }
11
- ModelViewerElement.mapURLs(function (url) {
12
- searchModelViewers();
13
- return url;
14
- });
15
- searchModelViewers();
16
- // observe the document for new model-viewers
17
- documentObserver?.disconnect();
18
- documentObserver = new MutationObserver((mutations) => {
19
- mutations.forEach((mutation) => {
20
- mutation.addedNodes.forEach((node) => {
21
- if (node instanceof HTMLElement && node.tagName.toLowerCase() === "model-viewer") {
22
- _patchModelViewer(node);
23
- }
24
- });
25
- });
26
- });
27
- documentObserver.observe(document, { childList: true, subtree: true });
28
- }
29
- /**
30
- * Tries to get the mode-viewer constructor from the custom element registry. If it doesnt exist yet we will wait for it to be loaded in case it's added to the document at a later point
31
- */
32
- function tryGetModelViewerConstructor() {
33
- if (typeof customElements === 'undefined')
34
- return null;
35
- // If model-viewer is already registered we can ignore this
36
- const ModelViewerElement = customElements.get('model-viewer');
37
- if (ModelViewerElement)
38
- return ModelViewerElement;
39
- // wait for model-viewer to be defined
40
- customElements.whenDefined('model-viewer').then(() => {
41
- console.debug("[gltf-progressive] model-viewer defined");
42
- patchModelViewer();
43
- });
44
- return null;
45
- }
46
- function searchModelViewers() {
47
- if (typeof document === 'undefined')
48
- return;
49
- // Query once for model viewer. If a user does not have model-viewer in their page, this will return null.
50
- const modelviewers = document.querySelectorAll("model-viewer");
51
- modelviewers.forEach((modelviewer) => {
52
- _patchModelViewer(modelviewer);
53
- });
54
- }
55
- const foundModelViewers = new WeakSet();
56
- let modelViewerCount = 0;
57
- /** Patch modelviewer to support NEEDLE progressive system
58
- * @returns a function to remove the patch
59
- */
60
- function _patchModelViewer(modelviewer) {
61
- if (!modelviewer)
62
- return null;
63
- if (foundModelViewers.has(modelviewer))
64
- return null;
65
- foundModelViewers.add(modelviewer);
66
- console.debug("[gltf-progressive] found new model-viewer..." + (++modelViewerCount) + "\n", modelviewer.getAttribute("src"));
67
- // Find the necessary internal methods and properties. We need access to the scene, renderer
68
- let renderer = null;
69
- let scene = null;
70
- let needsRender = null; // < used to force render updates for a few frames
71
- for (let p = modelviewer; p != null; p = Object.getPrototypeOf(p)) {
72
- const privateAPI = Object.getOwnPropertySymbols(p);
73
- const rendererSymbol = privateAPI.find((value) => value.toString() == 'Symbol(renderer)');
74
- const sceneSymbol = privateAPI.find((value) => value.toString() == 'Symbol(scene)');
75
- const needsRenderSymbol = privateAPI.find((value) => value.toString() == 'Symbol(needsRender)');
76
- if (!renderer && rendererSymbol != null) {
77
- renderer = modelviewer[rendererSymbol].threeRenderer;
78
- }
79
- if (!scene && sceneSymbol != null) {
80
- scene = modelviewer[sceneSymbol];
81
- }
82
- if (!needsRender && needsRenderSymbol != null) {
83
- needsRender = modelviewer[needsRenderSymbol];
84
- }
85
- }
86
- if (renderer && scene) {
87
- console.debug("[gltf-progressive] setup model-viewer");
88
- const lod = LODsManager.get(renderer, { engine: "model-viewer" });
89
- LODsManager.addPlugin(new RegisterModelviewerDataPlugin());
90
- lod.enable();
91
- // Trigger a render when a LOD has changed
92
- lod.addEventListener("changed", () => {
93
- needsRender?.call(modelviewer);
94
- });
95
- // Trigger a render when the model viewer visibility changes
96
- modelviewer.addEventListener("model-visibility", (evt) => {
97
- const visible = evt.detail.visible;
98
- if (visible)
99
- needsRender?.call(modelviewer);
100
- });
101
- modelviewer.addEventListener("load", () => {
102
- renderFrames();
103
- });
104
- /**
105
- * For model viewer to immediately update without interaction we need to trigger a few renders
106
- * We do this so that the LODs are loaded
107
- */
108
- function renderFrames() {
109
- if (needsRender) {
110
- let forcedFrames = 0;
111
- let interval = setInterval(() => {
112
- if (forcedFrames++ > 5) {
113
- clearInterval(interval);
114
- return;
115
- }
116
- needsRender?.call(modelviewer);
117
- }, 300);
118
- }
119
- }
120
- return () => {
121
- lod.disable();
122
- };
123
- }
124
- return null;
125
- }
126
- /**
127
- * LODs manager plugin that registers LOD data to the NEEDLE progressive system
128
- */
129
- class RegisterModelviewerDataPlugin {
130
- _didWarnAboutMissingUrl = false;
131
- onBeforeUpdateLOD(_renderer, scene, _camera, object) {
132
- this.tryParseMeshLOD(scene, object);
133
- this.tryParseTextureLOD(scene, object);
134
- }
135
- getUrl(element) {
136
- if (!element) {
137
- return null;
138
- }
139
- let url = element.getAttribute("src");
140
- // fallback in case the attribute is not set but the src property is
141
- if (!url) {
142
- url = element["src"];
143
- }
144
- if (!url) {
145
- if (!this._didWarnAboutMissingUrl)
146
- console.warn("No url found in modelviewer", element);
147
- this._didWarnAboutMissingUrl = true;
148
- }
149
- return url;
150
- }
151
- tryGetCurrentGLTF(scene) {
152
- return scene._currentGLTF;
153
- }
154
- tryGetCurrentModelViewer(scene) {
155
- return scene.element;
156
- }
157
- tryParseTextureLOD(scene, object) {
158
- if (object[$textureLODSymbol] == true)
159
- return;
160
- object[$textureLODSymbol] = true;
161
- const currentGLTF = this.tryGetCurrentGLTF(scene);
162
- const element = this.tryGetCurrentModelViewer(scene);
163
- const url = this.getUrl(element);
164
- if (!url) {
165
- return;
166
- }
167
- if (currentGLTF) {
168
- if (object.material) {
169
- const mat = object.material;
170
- if (Array.isArray(mat))
171
- for (const m of mat)
172
- handleMaterial(m);
173
- else
174
- handleMaterial(mat);
175
- function handleMaterial(mat) {
176
- if (mat[$textureLODSymbol] == true)
177
- return;
178
- mat[$textureLODSymbol] = true;
179
- // make sure to force the material to be updated
180
- if (mat.userData)
181
- mat.userData.LOD = -1;
182
- const keys = Object.keys(mat);
183
- for (let i = 0; i < keys.length; i++) {
184
- const key = keys[i];
185
- const value = mat[key];
186
- if (value?.isTexture === true) {
187
- const textureIndex = value.userData?.associations?.textures;
188
- if (textureIndex == null)
189
- continue;
190
- const textureData = currentGLTF.parser.json.textures[textureIndex];
191
- if (!textureData) {
192
- console.warn("Texture data not found for texture index " + textureIndex);
193
- continue;
194
- }
195
- if (textureData?.extensions?.[EXTENSION_NAME]) {
196
- const ext = textureData.extensions[EXTENSION_NAME];
197
- if (ext && url) {
198
- NEEDLE_progressive.registerTexture(url, value, ext.lods.length, textureIndex, ext);
199
- }
200
- }
201
- }
202
- }
203
- }
204
- }
205
- }
206
- }
207
- tryParseMeshLOD(scene, object) {
208
- if (object[$meshLODSymbol] == true)
209
- return;
210
- object[$meshLODSymbol] = true;
211
- const element = this.tryGetCurrentModelViewer(scene);
212
- const url = this.getUrl(element);
213
- if (!url) {
214
- return;
215
- }
216
- // modelviewer has all the information we need in the userData (associations + gltfExtensions)
217
- const ext = object.userData?.["gltfExtensions"]?.[EXTENSION_NAME];
218
- if (ext && url) {
219
- const lodKey = object.uuid;
220
- NEEDLE_progressive.registerMesh(url, lodKey, object, 0, ext.lods.length, ext);
221
- }
222
- }
223
- }
@@ -1,23 +0,0 @@
1
- import { WebGLRenderer, Scene, Camera, Mesh } from 'three';
2
- import { NEEDLE_ext_progressive_mesh } from '../extension.model.js';
3
- /**
4
- * This interface is used to define a plugin for the progressive extension. It can be registered using the `registerPlugin` function.
5
- */
6
- export interface NEEDLE_progressive_plugin {
7
- /** Called before the LOD level will be requested/updated for a object */
8
- onBeforeUpdateLOD?(renderer: WebGLRenderer, scene: Scene, camera: Camera, object: Mesh): void;
9
- /** Called after the LOD level has been requested */
10
- onAfterUpdatedLOD?(renderer: WebGLRenderer, scene: Scene, camera: Camera, object: Mesh, level: {
11
- mesh_lod: number;
12
- texture_lod: number;
13
- }): void;
14
- /** Called when a new mesh is registered */
15
- onRegisteredNewMesh?(mesh: Mesh, ext: NEEDLE_ext_progressive_mesh): void;
16
- /** Called before the LOD mesh is fetched */
17
- onBeforeGetLODMesh?(mesh: Mesh, level: number): void;
18
- }
19
- /**
20
- * List of registered plugins for the progressive extension. Please use the `registerPlugin` function to add a plugin.
21
- * @internal
22
- */
23
- export declare const plugins: NEEDLE_progressive_plugin[];
@@ -1,5 +0,0 @@
1
- /**
2
- * List of registered plugins for the progressive extension. Please use the `registerPlugin` function to add a plugin.
3
- * @internal
4
- */
5
- export const plugins = new Array();
package/lib/utils.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import { BufferGeometry, Object3D } from "three";
2
- export declare const isSSR: boolean;
3
- /**
4
- * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
5
- * @param obj the object to get the raycast mesh from
6
- * @returns the raycast mesh or null if not set
7
- */
8
- export declare function getRaycastMesh(obj: Object3D): BufferGeometry<any> | null;
9
- /**
10
- * Set the raycast mesh for an object.
11
- * The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
12
- * @param obj the object to set the raycast mesh for
13
- * @param geom the raycast mesh
14
- */
15
- export declare function registerRaycastMesh(obj: Object3D, geom: BufferGeometry): void;
16
- /**
17
- * Call this method to enable raycasting with the lowpoly raycast meshes (if available) for all meshes in the scene.
18
- * This is useful for performance optimization when the scene contains high poly meshes that are not visible to the camera.
19
- * @example
20
- * ```ts
21
- * // call to enable raycasting with low poly raycast meshes
22
- * useRaycastMeshes();
23
- *
24
- * // then use the raycaster as usual
25
- * const raycaster = new Raycaster();
26
- * raycaster.setFromCamera(mouse, camera);
27
- * const intersects = raycaster.intersectObjects(scene.children, true);
28
- * ```
29
- */
30
- export declare function useRaycastMeshes(enabled?: boolean): void;
@@ -1,35 +0,0 @@
1
- export declare function isDebugMode(): string | boolean;
2
- export declare function getParam(name: string): boolean | string;
3
- export declare function resolveUrl(source: string | undefined, uri: string): string;
4
- /** Check if the current device is a mobile device.
5
- * @returns `true` if it's a phone or tablet
6
- */
7
- export declare function isMobileDevice(): boolean;
8
- /**
9
- * Check if we are running in a development server (localhost or ip address).
10
- * @returns `true` if we are running in a development server (localhost or ip address).
11
- */
12
- export declare function isDevelopmentServer(): boolean;
13
- export type SlotReturnValue<T = any> = {
14
- use?: ((promise: Promise<T>) => void);
15
- };
16
- /**
17
- * A promise queue that limits the number of concurrent promises.
18
- * Use the `slot` method to request a slot for a promise with a specific key. The returned promise resolves to an object with a `use` method that can be called to add the promise to the queue.
19
- */
20
- export declare class PromiseQueue<T = any> {
21
- readonly maxConcurrent: number;
22
- private readonly _running;
23
- private readonly _queue;
24
- debug: boolean;
25
- constructor(maxConcurrent?: number, opts?: {
26
- debug?: boolean;
27
- });
28
- private tick;
29
- /**
30
- * Request a slot for a promise with a specific key. This function returns a promise with a `use` method that can be called to add the promise to the queue.
31
- */
32
- slot(key: string): Promise<SlotReturnValue<T>>;
33
- private add;
34
- private internalUpdate;
35
- }
@@ -1,117 +0,0 @@
1
- const debug = getParam("debugprogressive");
2
- export function isDebugMode() {
3
- return debug;
4
- }
5
- export function getParam(name) {
6
- if (typeof window === "undefined")
7
- return false;
8
- const url = new URL(window.location.href);
9
- const param = url.searchParams.get(name);
10
- if (param == null || param === "0" || param === "false")
11
- return false;
12
- if (param === "")
13
- return true;
14
- return param;
15
- }
16
- export function resolveUrl(source, uri) {
17
- if (uri === undefined || source === undefined) {
18
- return uri;
19
- }
20
- if (uri.startsWith("./") ||
21
- uri.startsWith("http") ||
22
- uri.startsWith("data:") ||
23
- uri.startsWith("blob:")) {
24
- return uri;
25
- }
26
- // TODO: why not just use new URL(uri, source).href ?
27
- const pathIndex = source.lastIndexOf("/");
28
- if (pathIndex >= 0) {
29
- // Take the source uri as the base path
30
- const basePath = source.substring(0, pathIndex + 1);
31
- // make sure we don't have double slashes
32
- while (basePath.endsWith("/") && uri.startsWith("/"))
33
- uri = uri.substring(1);
34
- // Append the relative uri
35
- const newUri = basePath + uri;
36
- // newUri = new URL(newUri, globalThis.location.href).href;
37
- return newUri;
38
- }
39
- return uri;
40
- }
41
- /** Check if the current device is a mobile device.
42
- * @returns `true` if it's a phone or tablet
43
- */
44
- export function isMobileDevice() {
45
- if (_ismobile !== undefined)
46
- return _ismobile;
47
- _ismobile = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent);
48
- if (getParam("debugprogressive"))
49
- console.log("[glTF Progressive]: isMobileDevice", _ismobile);
50
- return _ismobile;
51
- }
52
- let _ismobile;
53
- /**
54
- * Check if we are running in a development server (localhost or ip address).
55
- * @returns `true` if we are running in a development server (localhost or ip address).
56
- */
57
- export function isDevelopmentServer() {
58
- if (typeof window === "undefined")
59
- return false;
60
- const url = new URL(window.location.href);
61
- const isLocalhostOrIpAddress = url.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(url.hostname);
62
- const isDevelopment = url.hostname === "127.0.0.1" || isLocalhostOrIpAddress;
63
- return isDevelopment;
64
- }
65
- /**
66
- * A promise queue that limits the number of concurrent promises.
67
- * Use the `slot` method to request a slot for a promise with a specific key. The returned promise resolves to an object with a `use` method that can be called to add the promise to the queue.
68
- */
69
- export class PromiseQueue {
70
- maxConcurrent;
71
- _running = new Map();
72
- _queue = [];
73
- debug = false;
74
- constructor(maxConcurrent = 100, opts = {}) {
75
- this.maxConcurrent = maxConcurrent;
76
- this.debug = opts.debug ?? false;
77
- window.requestAnimationFrame(this.tick);
78
- }
79
- tick = () => {
80
- this.internalUpdate();
81
- setTimeout(this.tick, 10);
82
- };
83
- /**
84
- * Request a slot for a promise with a specific key. This function returns a promise with a `use` method that can be called to add the promise to the queue.
85
- */
86
- slot(key) {
87
- if (this.debug)
88
- console.debug(`[PromiseQueue]: Requesting slot for key ${key}, running: ${this._running.size}, waiting: ${this._queue.length}`);
89
- return new Promise((resolve) => {
90
- this._queue.push({ key, resolve });
91
- });
92
- }
93
- add(key, promise) {
94
- if (this._running.has(key))
95
- return;
96
- this._running.set(key, promise);
97
- promise.finally(() => {
98
- this._running.delete(key);
99
- if (this.debug)
100
- console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${key})`);
101
- });
102
- if (this.debug)
103
- console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${key})`);
104
- }
105
- internalUpdate() {
106
- // Run for as many free slots as we can
107
- const diff = this.maxConcurrent - this._running.size;
108
- for (let i = 0; i < diff && this._queue.length > 0; i++) {
109
- if (this.debug)
110
- console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);
111
- const { key, resolve } = this._queue.shift();
112
- resolve({
113
- use: (promise) => this.add(key, promise)
114
- });
115
- }
116
- }
117
- }