@buildcores/render-client 1.0.12 → 1.2.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.
- package/README.md +26 -0
- package/dist/api.d.ts +3 -3
- package/dist/index.d.ts +70 -6
- package/dist/index.esm.js +23 -184
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +87 -248
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +67 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,9 @@ interface RenderBuildRequest {
|
|
|
69
69
|
parts: {
|
|
70
70
|
[K in PartCategory]?: string[];
|
|
71
71
|
};
|
|
72
|
+
format?: "video" | "sprite";
|
|
73
|
+
width?: number; // Optional: Canvas pixel width (256-2000)
|
|
74
|
+
height?: number; // Optional: Canvas pixel height (256-2000)
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
// Available part categories
|
|
@@ -86,6 +89,8 @@ enum PartCategory {
|
|
|
86
89
|
|
|
87
90
|
**Current Limitation**: Each category array must contain exactly one part ID. Multiple parts per category will be supported in future versions.
|
|
88
91
|
|
|
92
|
+
**Resolution Control**: You can specify custom `width` and `height` (both must be provided together, 256-2000 pixels) for higher or lower quality renders. If not specified, the default resolution is used.
|
|
93
|
+
|
|
89
94
|
#### Examples
|
|
90
95
|
|
|
91
96
|
**Complete Build (All Components)**
|
|
@@ -119,6 +124,27 @@ const caseOnly = {
|
|
|
119
124
|
<BuildRender parts={caseOnly} size={500} />;
|
|
120
125
|
```
|
|
121
126
|
|
|
127
|
+
**Custom Resolution (High Quality)**
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
const highResBuild = {
|
|
131
|
+
parts: {
|
|
132
|
+
CPU: ["7xjqsomhr"],
|
|
133
|
+
GPU: ["z7pyphm9k"],
|
|
134
|
+
RAM: ["dpl1iyvb5"],
|
|
135
|
+
Motherboard: ["iwin2u9vx"],
|
|
136
|
+
PSU: ["m4kilv190"],
|
|
137
|
+
Storage: ["0bkvs17po"],
|
|
138
|
+
PCCase: ["qq9jamk7c"],
|
|
139
|
+
CPUCooler: ["62d8zelr5"],
|
|
140
|
+
},
|
|
141
|
+
width: 1920, // Custom resolution width
|
|
142
|
+
height: 1080, // Custom resolution height
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
<BuildRender parts={highResBuild} size={500} />;
|
|
146
|
+
```
|
|
147
|
+
|
|
122
148
|
### `getAvailableParts()` Function
|
|
123
149
|
|
|
124
150
|
Fetches all available PC parts from the BuildCores API.
|
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RenderBuildRequest, AvailablePartsResponse, ApiConfig } from "./types";
|
|
1
|
+
import { RenderBuildRequest, AvailablePartsResponse, ApiConfig, PartCategory, GetAvailablePartsOptions } from "./types";
|
|
2
2
|
declare const API_BASE_URL = "https://www.renderapi.buildcores.com";
|
|
3
3
|
export declare const API_ENDPOINTS: {
|
|
4
4
|
readonly RENDER_BUILD_EXPERIMENTAL: "/render-build-experimental";
|
|
@@ -65,7 +65,7 @@ export interface RenderAPIService {
|
|
|
65
65
|
* @param config - API configuration (environment, auth token) - required
|
|
66
66
|
* @returns Promise with available parts by category
|
|
67
67
|
*/
|
|
68
|
-
getAvailableParts(config: ApiConfig): Promise<AvailablePartsResponse>;
|
|
68
|
+
getAvailableParts(category: PartCategory, config: ApiConfig, options?: GetAvailablePartsOptions): Promise<AvailablePartsResponse>;
|
|
69
69
|
}
|
|
70
70
|
export declare const buildApiUrl: (endpoint: string, config: ApiConfig) => string;
|
|
71
71
|
export declare const buildHeaders: (config: ApiConfig) => Record<string, string>;
|
|
@@ -77,5 +77,5 @@ export declare const renderBuild: (request: RenderBuildRequest, config: ApiConfi
|
|
|
77
77
|
timeoutMs?: number;
|
|
78
78
|
}) => Promise<RenderBuildAsyncResponse>;
|
|
79
79
|
export declare const renderSpriteExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderSpriteResponse>;
|
|
80
|
-
export declare const getAvailableParts: (config: ApiConfig) => Promise<AvailablePartsResponse>;
|
|
80
|
+
export declare const getAvailableParts: (category: PartCategory, config: ApiConfig, options?: GetAvailablePartsOptions) => Promise<AvailablePartsResponse>;
|
|
81
81
|
export { API_BASE_URL };
|
package/dist/index.d.ts
CHANGED
|
@@ -340,6 +340,34 @@ interface RenderBuildRequest {
|
|
|
340
340
|
* @default "video"
|
|
341
341
|
*/
|
|
342
342
|
format?: "video" | "sprite";
|
|
343
|
+
/**
|
|
344
|
+
* Desired canvas pixel width (256-2000).
|
|
345
|
+
* Must be provided together with height.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```tsx
|
|
349
|
+
* const request: RenderBuildRequest = {
|
|
350
|
+
* parts: { CPU: ["7xjqsomhr"] },
|
|
351
|
+
* width: 1920,
|
|
352
|
+
* height: 1080
|
|
353
|
+
* };
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
width?: number;
|
|
357
|
+
/**
|
|
358
|
+
* Desired canvas pixel height (256-2000).
|
|
359
|
+
* Must be provided together with width.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```tsx
|
|
363
|
+
* const request: RenderBuildRequest = {
|
|
364
|
+
* parts: { CPU: ["7xjqsomhr"] },
|
|
365
|
+
* width: 1920,
|
|
366
|
+
* height: 1080
|
|
367
|
+
* };
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
height?: number;
|
|
343
371
|
}
|
|
344
372
|
/**
|
|
345
373
|
* Response structure containing all available parts for each category.
|
|
@@ -398,9 +426,45 @@ interface PartDetails {
|
|
|
398
426
|
/** URL to part image */
|
|
399
427
|
image: string;
|
|
400
428
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
429
|
+
/**
|
|
430
|
+
* Pagination metadata for available parts responses
|
|
431
|
+
*/
|
|
432
|
+
interface AvailablePartsPagination {
|
|
433
|
+
/** Total number of parts available for this category */
|
|
434
|
+
total: number;
|
|
435
|
+
/** Number of parts returned in this response */
|
|
436
|
+
limit: number;
|
|
437
|
+
/** Number of parts skipped */
|
|
438
|
+
skip: number;
|
|
439
|
+
/** Whether there are more parts available */
|
|
440
|
+
hasNext: boolean;
|
|
441
|
+
/** Whether there are previous parts available */
|
|
442
|
+
hasPrev: boolean;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Response envelope for the available parts endpoint.
|
|
446
|
+
* Returns parts for the requested category under `data` keyed by category name.
|
|
447
|
+
*/
|
|
448
|
+
interface AvailablePartsResponse {
|
|
449
|
+
/**
|
|
450
|
+
* Parts grouped by category. Only the requested category key is expected
|
|
451
|
+
* to be present in the response.
|
|
452
|
+
*/
|
|
453
|
+
data: Partial<Record<PartCategory, PartDetails[]>>;
|
|
454
|
+
/** The requested category */
|
|
455
|
+
category: PartCategory;
|
|
456
|
+
/** Optional pagination information */
|
|
457
|
+
pagination?: AvailablePartsPagination;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Query options for fetching available parts
|
|
461
|
+
*/
|
|
462
|
+
interface GetAvailablePartsOptions {
|
|
463
|
+
/** Number of parts to return (default 20, min 1, max 100) */
|
|
464
|
+
limit?: number;
|
|
465
|
+
/** Number of parts to skip for pagination (default 0) */
|
|
466
|
+
skip?: number;
|
|
467
|
+
}
|
|
404
468
|
|
|
405
469
|
declare const BuildRender: React.FC<BuildRenderProps>;
|
|
406
470
|
|
|
@@ -551,13 +615,13 @@ interface RenderAPIService {
|
|
|
551
615
|
* @param config - API configuration (environment, auth token) - required
|
|
552
616
|
* @returns Promise with available parts by category
|
|
553
617
|
*/
|
|
554
|
-
getAvailableParts(config: ApiConfig): Promise<AvailablePartsResponse>;
|
|
618
|
+
getAvailableParts(category: PartCategory, config: ApiConfig, options?: GetAvailablePartsOptions): Promise<AvailablePartsResponse>;
|
|
555
619
|
}
|
|
556
620
|
declare const buildApiUrl: (endpoint: string, config: ApiConfig) => string;
|
|
557
621
|
declare const buildHeaders: (config: ApiConfig) => Record<string, string>;
|
|
558
622
|
declare const renderBuildExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderBuildResponse>;
|
|
559
623
|
declare const renderSpriteExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderSpriteResponse>;
|
|
560
|
-
declare const getAvailableParts: (config: ApiConfig) => Promise<AvailablePartsResponse>;
|
|
624
|
+
declare const getAvailableParts: (category: PartCategory, config: ApiConfig, options?: GetAvailablePartsOptions) => Promise<AvailablePartsResponse>;
|
|
561
625
|
|
|
562
626
|
export { API_BASE_URL, API_ENDPOINTS, BuildRender, BuildRenderVideo, DragIcon, InstructionTooltip, LoadingErrorOverlay, PartCategory, arePartsEqual, buildApiUrl, buildHeaders, calculateCircularFrame, calculateCircularTime, getAvailableParts, renderBuildExperimental, renderSpriteExperimental, useBouncePatternProgress, useBuildRender, useSpriteRender, useSpriteScrubbing, useVideoScrubbing };
|
|
563
|
-
export type { ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderOptions, UseBuildRenderReturn, UseSpriteRenderOptions, UseSpriteRenderReturn };
|
|
627
|
+
export type { ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, GetAvailablePartsOptions, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderOptions, UseBuildRenderReturn, UseSpriteRenderOptions, UseSpriteRenderReturn };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useRef, useCallback, useEffect
|
|
2
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useAnimationFrame } from 'framer-motion';
|
|
3
4
|
|
|
4
5
|
// Helper to extract clientX from mouse or touch events
|
|
5
6
|
const getClientX$1 = (e) => {
|
|
@@ -102,186 +103,6 @@ const useSpriteScrubbing = (canvasRef, totalFrames, options = {}) => {
|
|
|
102
103
|
};
|
|
103
104
|
};
|
|
104
105
|
|
|
105
|
-
/**
|
|
106
|
-
* @public
|
|
107
|
-
*/
|
|
108
|
-
const MotionConfigContext = createContext({
|
|
109
|
-
transformPagePoint: (p) => p,
|
|
110
|
-
isStatic: false,
|
|
111
|
-
reducedMotion: "never",
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
/*#__NO_SIDE_EFFECTS__*/
|
|
115
|
-
const noop = (any) => any;
|
|
116
|
-
|
|
117
|
-
if (process.env.NODE_ENV !== "production") ;
|
|
118
|
-
|
|
119
|
-
function createRenderStep(runNextFrame) {
|
|
120
|
-
/**
|
|
121
|
-
* We create and reuse two queues, one to queue jobs for the current frame
|
|
122
|
-
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
123
|
-
*/
|
|
124
|
-
let thisFrame = new Set();
|
|
125
|
-
let nextFrame = new Set();
|
|
126
|
-
/**
|
|
127
|
-
* Track whether we're currently processing jobs in this step. This way
|
|
128
|
-
* we can decide whether to schedule new jobs for this frame or next.
|
|
129
|
-
*/
|
|
130
|
-
let isProcessing = false;
|
|
131
|
-
let flushNextFrame = false;
|
|
132
|
-
/**
|
|
133
|
-
* A set of processes which were marked keepAlive when scheduled.
|
|
134
|
-
*/
|
|
135
|
-
const toKeepAlive = new WeakSet();
|
|
136
|
-
let latestFrameData = {
|
|
137
|
-
delta: 0.0,
|
|
138
|
-
timestamp: 0.0,
|
|
139
|
-
isProcessing: false,
|
|
140
|
-
};
|
|
141
|
-
function triggerCallback(callback) {
|
|
142
|
-
if (toKeepAlive.has(callback)) {
|
|
143
|
-
step.schedule(callback);
|
|
144
|
-
runNextFrame();
|
|
145
|
-
}
|
|
146
|
-
callback(latestFrameData);
|
|
147
|
-
}
|
|
148
|
-
const step = {
|
|
149
|
-
/**
|
|
150
|
-
* Schedule a process to run on the next frame.
|
|
151
|
-
*/
|
|
152
|
-
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
153
|
-
const addToCurrentFrame = immediate && isProcessing;
|
|
154
|
-
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
|
155
|
-
if (keepAlive)
|
|
156
|
-
toKeepAlive.add(callback);
|
|
157
|
-
if (!queue.has(callback))
|
|
158
|
-
queue.add(callback);
|
|
159
|
-
return callback;
|
|
160
|
-
},
|
|
161
|
-
/**
|
|
162
|
-
* Cancel the provided callback from running on the next frame.
|
|
163
|
-
*/
|
|
164
|
-
cancel: (callback) => {
|
|
165
|
-
nextFrame.delete(callback);
|
|
166
|
-
toKeepAlive.delete(callback);
|
|
167
|
-
},
|
|
168
|
-
/**
|
|
169
|
-
* Execute all schedule callbacks.
|
|
170
|
-
*/
|
|
171
|
-
process: (frameData) => {
|
|
172
|
-
latestFrameData = frameData;
|
|
173
|
-
/**
|
|
174
|
-
* If we're already processing we've probably been triggered by a flushSync
|
|
175
|
-
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
176
|
-
* as true and ensure we flush the following frame at the end of this one.
|
|
177
|
-
*/
|
|
178
|
-
if (isProcessing) {
|
|
179
|
-
flushNextFrame = true;
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
isProcessing = true;
|
|
183
|
-
[thisFrame, nextFrame] = [nextFrame, thisFrame];
|
|
184
|
-
// Execute this frame
|
|
185
|
-
thisFrame.forEach(triggerCallback);
|
|
186
|
-
// Clear the frame so no callbacks remain. This is to avoid
|
|
187
|
-
// memory leaks should this render step not run for a while.
|
|
188
|
-
thisFrame.clear();
|
|
189
|
-
isProcessing = false;
|
|
190
|
-
if (flushNextFrame) {
|
|
191
|
-
flushNextFrame = false;
|
|
192
|
-
step.process(frameData);
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
return step;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const stepsOrder = [
|
|
200
|
-
"read", // Read
|
|
201
|
-
"resolveKeyframes", // Write/Read/Write/Read
|
|
202
|
-
"update", // Compute
|
|
203
|
-
"preRender", // Compute
|
|
204
|
-
"render", // Write
|
|
205
|
-
"postRender", // Compute
|
|
206
|
-
];
|
|
207
|
-
const maxElapsed = 40;
|
|
208
|
-
function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
209
|
-
let runNextFrame = false;
|
|
210
|
-
let useDefaultElapsed = true;
|
|
211
|
-
const state = {
|
|
212
|
-
delta: 0.0,
|
|
213
|
-
timestamp: 0.0,
|
|
214
|
-
isProcessing: false,
|
|
215
|
-
};
|
|
216
|
-
const flagRunNextFrame = () => (runNextFrame = true);
|
|
217
|
-
const steps = stepsOrder.reduce((acc, key) => {
|
|
218
|
-
acc[key] = createRenderStep(flagRunNextFrame);
|
|
219
|
-
return acc;
|
|
220
|
-
}, {});
|
|
221
|
-
const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
|
|
222
|
-
const processBatch = () => {
|
|
223
|
-
const timestamp = performance.now();
|
|
224
|
-
runNextFrame = false;
|
|
225
|
-
state.delta = useDefaultElapsed
|
|
226
|
-
? 1000 / 60
|
|
227
|
-
: Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
|
|
228
|
-
state.timestamp = timestamp;
|
|
229
|
-
state.isProcessing = true;
|
|
230
|
-
// Unrolled render loop for better per-frame performance
|
|
231
|
-
read.process(state);
|
|
232
|
-
resolveKeyframes.process(state);
|
|
233
|
-
update.process(state);
|
|
234
|
-
preRender.process(state);
|
|
235
|
-
render.process(state);
|
|
236
|
-
postRender.process(state);
|
|
237
|
-
state.isProcessing = false;
|
|
238
|
-
if (runNextFrame && allowKeepAlive) {
|
|
239
|
-
useDefaultElapsed = false;
|
|
240
|
-
scheduleNextBatch(processBatch);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
const wake = () => {
|
|
244
|
-
runNextFrame = true;
|
|
245
|
-
useDefaultElapsed = true;
|
|
246
|
-
if (!state.isProcessing) {
|
|
247
|
-
scheduleNextBatch(processBatch);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
const schedule = stepsOrder.reduce((acc, key) => {
|
|
251
|
-
const step = steps[key];
|
|
252
|
-
acc[key] = (process, keepAlive = false, immediate = false) => {
|
|
253
|
-
if (!runNextFrame)
|
|
254
|
-
wake();
|
|
255
|
-
return step.schedule(process, keepAlive, immediate);
|
|
256
|
-
};
|
|
257
|
-
return acc;
|
|
258
|
-
}, {});
|
|
259
|
-
const cancel = (process) => {
|
|
260
|
-
for (let i = 0; i < stepsOrder.length; i++) {
|
|
261
|
-
steps[stepsOrder[i]].cancel(process);
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
return { schedule, cancel, state, steps };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const { schedule: frame, cancel: cancelFrame} = createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
|
268
|
-
|
|
269
|
-
function useAnimationFrame(callback) {
|
|
270
|
-
const initialTimestamp = useRef(0);
|
|
271
|
-
const { isStatic } = useContext(MotionConfigContext);
|
|
272
|
-
useEffect(() => {
|
|
273
|
-
if (isStatic)
|
|
274
|
-
return;
|
|
275
|
-
const provideTimeSinceStart = ({ timestamp, delta }) => {
|
|
276
|
-
if (!initialTimestamp.current)
|
|
277
|
-
initialTimestamp.current = timestamp;
|
|
278
|
-
callback(timestamp - initialTimestamp.current, delta);
|
|
279
|
-
};
|
|
280
|
-
frame.update(provideTimeSinceStart, true);
|
|
281
|
-
return () => cancelFrame(provideTimeSinceStart);
|
|
282
|
-
}, [callback]);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
106
|
function useBouncePatternProgress(enabled = true) {
|
|
286
107
|
const [value, setValue] = useState(0);
|
|
287
108
|
const [isBouncing, setIsBouncing] = useState(false);
|
|
@@ -353,6 +174,9 @@ const renderBuildExperimental = async (request, config) => {
|
|
|
353
174
|
const requestWithFormat = {
|
|
354
175
|
...request,
|
|
355
176
|
format: request.format || "video", // Default to video format
|
|
177
|
+
// Include width and height if provided
|
|
178
|
+
...(request.width !== undefined ? { width: request.width } : {}),
|
|
179
|
+
...(request.height !== undefined ? { height: request.height } : {}),
|
|
356
180
|
};
|
|
357
181
|
const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
|
|
358
182
|
method: "POST",
|
|
@@ -377,6 +201,9 @@ const createRenderBuildJob = async (request, config) => {
|
|
|
377
201
|
parts: request.parts,
|
|
378
202
|
// If provided, forward format; default handled server-side but we keep explicit default
|
|
379
203
|
...(request.format ? { format: request.format } : {}),
|
|
204
|
+
// Include width and height if provided
|
|
205
|
+
...(request.width !== undefined ? { width: request.width } : {}),
|
|
206
|
+
...(request.height !== undefined ? { height: request.height } : {}),
|
|
380
207
|
};
|
|
381
208
|
const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD, config), {
|
|
382
209
|
method: "POST",
|
|
@@ -437,6 +264,9 @@ const renderSpriteExperimental = async (request, config) => {
|
|
|
437
264
|
const requestWithFormat = {
|
|
438
265
|
...request,
|
|
439
266
|
format: "sprite",
|
|
267
|
+
// Include width and height if provided
|
|
268
|
+
...(request.width !== undefined ? { width: request.width } : {}),
|
|
269
|
+
...(request.height !== undefined ? { height: request.height } : {}),
|
|
440
270
|
};
|
|
441
271
|
const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
|
|
442
272
|
method: "POST",
|
|
@@ -458,15 +288,24 @@ const renderSpriteExperimental = async (request, config) => {
|
|
|
458
288
|
},
|
|
459
289
|
};
|
|
460
290
|
};
|
|
461
|
-
const getAvailableParts = async (config) => {
|
|
462
|
-
const
|
|
291
|
+
const getAvailableParts = async (category, config, options) => {
|
|
292
|
+
const base = buildApiUrl(API_ENDPOINTS.AVAILABLE_PARTS, config);
|
|
293
|
+
const params = new URLSearchParams();
|
|
294
|
+
params.set("category", category);
|
|
295
|
+
if (typeof options?.limit === "number")
|
|
296
|
+
params.set("limit", String(options.limit));
|
|
297
|
+
if (typeof options?.skip === "number")
|
|
298
|
+
params.set("skip", String(options.skip));
|
|
299
|
+
const separator = base.includes("?") ? "&" : "?";
|
|
300
|
+
const url = `${base}${separator}${params.toString()}`;
|
|
301
|
+
const response = await fetch(url, {
|
|
463
302
|
method: "GET",
|
|
464
303
|
headers: buildHeaders(config),
|
|
465
304
|
});
|
|
466
305
|
if (!response.ok) {
|
|
467
306
|
throw new Error(`Get available parts failed: ${response.status} ${response.statusText}`);
|
|
468
307
|
}
|
|
469
|
-
return response.json();
|
|
308
|
+
return (await response.json());
|
|
470
309
|
};
|
|
471
310
|
|
|
472
311
|
/**
|