@printhive/api 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -4,13 +4,20 @@ import {
4
4
  apiError,
5
5
  apiKey,
6
6
  createRequest,
7
+ filamentSearch,
8
+ jobSnapshot,
7
9
  model,
8
10
  page,
11
+ preparation,
12
+ preparationDownload,
13
+ preparationInput,
9
14
  printJob,
10
15
  printer,
16
+ printerSearch,
11
17
  printers,
18
+ reviewApproval,
12
19
  source
13
- } from "./chunk-7UULMDS5.js";
20
+ } from "./chunk-GOUMDFJR.js";
14
21
 
15
22
  // src/transport.ts
16
23
  var MAX_RETRY_AFTER_MS = 6e4;
@@ -117,13 +124,93 @@ var Transport = class {
117
124
  }
118
125
  };
119
126
  function retryDelay(failure, replayable, attempt, maxRetries) {
120
- const retryable = failure instanceof PrintHiveApiError ? failure.status === 429 || failure.status === 503 : failure.reason === "network";
127
+ const retryable = failure instanceof PrintHiveApiError ? failure.code !== "preparation_disabled" && (failure.status === 429 || failure.status === 503) : failure.reason === "network";
121
128
  if (!retryable || !replayable || attempt >= maxRetries) throw failure;
122
129
  const retryAfterMs = failure instanceof PrintHiveApiError ? failure.retryAfterMs ?? 0 : 0;
123
130
  if (retryAfterMs > MAX_RETRY_AFTER_MS) throw failure;
124
131
  const backoff = Math.min(3e4, 250 * 2 ** attempt) * (0.5 + Math.random());
125
132
  return Math.max(backoff, retryAfterMs);
126
133
  }
134
+ async function decode(response, validate) {
135
+ const value = await response.json().catch((error) => {
136
+ if (error instanceof SyntaxError)
137
+ throw new TypeError("Invalid Print Hive API response");
138
+ throw error;
139
+ });
140
+ if (!validate(value)) throw new TypeError("Invalid Print Hive API response");
141
+ return value;
142
+ }
143
+
144
+ // src/search.ts
145
+ function searchQuery(input) {
146
+ const query = new URLSearchParams({ q: input.query });
147
+ if (input.limit !== void 0) query.set("limit", String(input.limit));
148
+ if (input.offset !== void 0) query.set("offset", String(input.offset));
149
+ return query;
150
+ }
151
+ var ResourceSearch = class {
152
+ constructor(transport) {
153
+ this.transport = transport;
154
+ }
155
+ async printers(input) {
156
+ const query = searchQuery(input);
157
+ const page2 = await this.transport.request({
158
+ method: "GET",
159
+ path: "/v1/printers/search",
160
+ query,
161
+ signal: input.signal,
162
+ decode: (response) => decode(response, printerSearch)
163
+ });
164
+ return page2;
165
+ }
166
+ async filaments(input) {
167
+ const page2 = await this.transport.request({
168
+ method: "GET",
169
+ path: "/v1/filaments/search",
170
+ query: searchQuery(input),
171
+ signal: input.signal,
172
+ decode: (response) => decode(response, filamentSearch)
173
+ });
174
+ return page2;
175
+ }
176
+ };
177
+
178
+ // src/filament-catalog.ts
179
+ var FilamentCatalog = class {
180
+ constructor(transport) {
181
+ this.transport = transport;
182
+ }
183
+ upsert(input) {
184
+ const snapshot = JSON.stringify(input);
185
+ return this.transport.request({
186
+ method: "POST",
187
+ path: "/v1/filament-catalog",
188
+ contentType: "application/json",
189
+ body: async () => snapshot,
190
+ idempotencyKey: encodeURIComponent(input.externalId),
191
+ replayable: true,
192
+ decode: async (response) => response.json()
193
+ });
194
+ }
195
+ list(input = {}) {
196
+ const query = new URLSearchParams();
197
+ for (const [key, value] of Object.entries(input))
198
+ if (value !== void 0) query.set(key, String(value));
199
+ return this.transport.request({
200
+ method: "GET",
201
+ path: "/v1/filament-catalog",
202
+ query,
203
+ decode: async (response) => response.json()
204
+ });
205
+ }
206
+ get(id) {
207
+ return this.transport.request({
208
+ method: "GET",
209
+ path: `/v1/filament-catalog/${encodeURIComponent(id)}`,
210
+ decode: async (response) => response.json()
211
+ });
212
+ }
213
+ };
127
214
 
128
215
  // src/print-jobs.ts
129
216
  var PrintJobCreateStoppedError = class extends Error {
@@ -133,7 +220,7 @@ var PrintJobCreateStoppedError = class extends Error {
133
220
  this.name = "PrintJobCreateStoppedError";
134
221
  }
135
222
  };
136
- async function decode(response, validate) {
223
+ async function decode2(response, validate) {
137
224
  const value = await response.json().catch((error) => {
138
225
  if (error instanceof SyntaxError)
139
226
  throw new TypeError("Invalid Print Hive API response");
@@ -146,6 +233,19 @@ var PrintJobs = class {
146
233
  constructor(transport) {
147
234
  this.transport = transport;
148
235
  }
236
+ async get(id, signal) {
237
+ if (!Number.isSafeInteger(id) || id < 1)
238
+ throw new RangeError("Print job id must be a positive safe integer");
239
+ const response = await this.transport.request({
240
+ method: "GET",
241
+ path: `/v1/print-jobs/${id}`,
242
+ signal,
243
+ decode: (response2) => decode2(response2, jobSnapshot)
244
+ });
245
+ if (response.job.id !== id)
246
+ throw new TypeError("Invalid Print Hive API response");
247
+ return response.job;
248
+ }
149
249
  async create(input) {
150
250
  const { idempotencyKey: suppliedKey, ...bodyInput } = input;
151
251
  const idempotencyKey = suppliedKey ?? crypto.randomUUID();
@@ -171,6 +271,11 @@ var PrintJobs = class {
171
271
  })) {
172
272
  if (value !== void 0) form.set(field, value);
173
273
  }
274
+ if (metadata.acceptEngineSettingUpdates !== void 0)
275
+ form.set(
276
+ "acceptEngineSettingUpdates",
277
+ JSON.stringify(metadata.acceptEngineSettingUpdates)
278
+ );
174
279
  form.set("plate", String(metadata.plate));
175
280
  form.set("filaments", JSON.stringify(metadata.filaments));
176
281
  if (metadata.print_options !== void 0)
@@ -178,7 +283,7 @@ var PrintJobs = class {
178
283
  form.set("file", new Blob([bytes]), filename);
179
284
  return form;
180
285
  },
181
- decode: (response3) => decode(response3, createRequest)
286
+ decode: (response3) => decode2(response3, createRequest)
182
287
  });
183
288
  return { ...response2, idempotencyKey };
184
289
  }
@@ -189,7 +294,7 @@ var PrintJobs = class {
189
294
  replayable: true,
190
295
  contentType: "application/json",
191
296
  body: async () => JSON.stringify(bodyInput),
192
- decode: (response2) => decode(response2, printJob)
297
+ decode: (response2) => decode2(response2, printJob)
193
298
  });
194
299
  return { ...response, idempotencyKey };
195
300
  }
@@ -198,7 +303,19 @@ var PrintJobs = class {
198
303
  method: "GET",
199
304
  path: `/v1/print-jobs/create-requests/${encodeURIComponent(requestId)}`,
200
305
  signal,
201
- decode: (response) => decode(response, createRequest)
306
+ decode: (response) => decode2(response, createRequest)
307
+ });
308
+ }
309
+ approveReview(requestId, review, signal) {
310
+ return this.transport.request({
311
+ method: "POST",
312
+ path: `/v1/print-jobs/create-requests/${encodeURIComponent(requestId)}/approve-review`,
313
+ signal,
314
+ idempotencyKey: crypto.randomUUID(),
315
+ replayable: false,
316
+ contentType: "application/json",
317
+ body: async () => JSON.stringify(review),
318
+ decode: (response) => decode2(response, reviewApproval)
202
319
  });
203
320
  }
204
321
  async waitUntilCreated(requestId, options = {}) {
@@ -224,15 +341,6 @@ var PrintJobs = class {
224
341
  };
225
342
 
226
343
  // src/index.ts
227
- async function decode2(response, validate) {
228
- const value = await response.json().catch((error) => {
229
- if (error instanceof SyntaxError)
230
- throw new TypeError("Invalid Print Hive API response");
231
- throw error;
232
- });
233
- if (!validate(value)) throw new TypeError("Invalid Print Hive API response");
234
- return value;
235
- }
236
344
  function isPath(file) {
237
345
  return !(file instanceof Uint8Array) && !(file instanceof ReadableStream);
238
346
  }
@@ -242,29 +350,93 @@ var PrintHive = class {
242
350
  models;
243
351
  printers;
244
352
  printJobs;
353
+ filamentCatalog;
354
+ filaments;
355
+ preparations;
245
356
  constructor(options) {
246
357
  const transport = new Transport(options);
358
+ const search = new ResourceSearch(transport);
359
+ this.filaments = {
360
+ search: (input) => search.filaments(input)
361
+ };
247
362
  this.printJobs = new PrintJobs(transport);
363
+ this.filamentCatalog = new FilamentCatalog(transport);
248
364
  const get = (sourceId, signal) => transport.request({
249
- decode: (response) => decode2(response, source),
365
+ decode: (response) => decode(response, source),
250
366
  method: "GET",
251
367
  path: `${prefix}/uploads/${encodeURIComponent(sourceId)}`,
252
368
  signal
253
369
  });
370
+ const getPreparation = (id, signal) => transport.request({
371
+ decode: (response) => decode(response, preparation),
372
+ method: "GET",
373
+ path: `${prefix}/preparations/${encodeURIComponent(id)}`,
374
+ signal
375
+ });
376
+ this.preparations = {
377
+ get: getPreparation,
378
+ async create(input) {
379
+ const idempotencyKey = input.idempotencyKey ?? crypto.randomUUID();
380
+ const snapshot = JSON.stringify({ items: input.items });
381
+ if (!preparationInput(JSON.parse(snapshot)))
382
+ throw new TypeError("Invalid preparation input");
383
+ const preparation2 = await transport.request({
384
+ decode: (response) => decode(response, preparation),
385
+ method: "POST",
386
+ path: `${prefix}/preparations`,
387
+ contentType: "application/json",
388
+ body: async () => snapshot,
389
+ size: new TextEncoder().encode(snapshot).byteLength,
390
+ idempotencyKey,
391
+ replayable: true,
392
+ signal: input.signal
393
+ });
394
+ return { ...preparation2, idempotencyKey };
395
+ },
396
+ async waitUntilSettled(id, options2 = {}) {
397
+ const interval = positive(options2.intervalMs ?? 1e3, "intervalMs");
398
+ const deadline = AbortSignal.timeout(
399
+ positive(options2.timeoutMs ?? 12e4, "timeoutMs")
400
+ );
401
+ const signal = options2.signal ? AbortSignal.any([options2.signal, deadline]) : deadline;
402
+ try {
403
+ for (; ; ) {
404
+ const preparation2 = await getPreparation(id, signal);
405
+ const status = preparation2.status;
406
+ if (status === "ready" || status === "held" || status === "failed")
407
+ return { ...preparation2, status };
408
+ await pause(interval, signal);
409
+ }
410
+ } catch (error) {
411
+ if (deadline.aborted && !options2.signal?.aborted) {
412
+ const { PrintHiveNetworkError: PrintHiveNetworkError2 } = await import("./errors-4M56UUFV.js");
413
+ throw new PrintHiveNetworkError2("timeout");
414
+ }
415
+ throw error;
416
+ }
417
+ },
418
+ downloadInfo: (id, plateId, signal) => transport.request({
419
+ decode: (response) => decode(response, preparationDownload),
420
+ method: "GET",
421
+ path: `${prefix}/preparations/${encodeURIComponent(id)}/plates/${encodeURIComponent(plateId)}/download`,
422
+ signal
423
+ })
424
+ };
254
425
  this.printers = {
426
+ search: (input) => search.printers(input),
255
427
  list: (input = {}) => {
256
428
  const query = new URLSearchParams();
257
429
  if (input.limit !== void 0) query.set("limit", String(input.limit));
258
430
  if (input.cursor !== void 0) query.set("cursor", input.cursor);
259
431
  return transport.request({
260
- decode: (response) => decode2(response, printers),
432
+ decode: (response) => decode(response, printers),
261
433
  method: "GET",
262
434
  path: "/v1/printers",
263
435
  query
264
436
  });
265
437
  },
266
438
  get: (id) => transport.request({
267
- decode: (response) => decode2(response, printer),
439
+ decode: (response) => decode(response, printer),
268
440
  method: "GET",
269
441
  path: `/v1/printers/${encodeURIComponent(id)}`
270
442
  })
@@ -302,7 +474,7 @@ var PrintHive = class {
302
474
  if (!Number.isSafeInteger(size) || size < 0)
303
475
  throw new RangeError("size must be a nonnegative safe integer");
304
476
  const source2 = await transport.request({
305
- decode: (response) => decode2(response, source),
477
+ decode: (response) => decode(response, source),
306
478
  method: "POST",
307
479
  path: `${prefix}/uploads`,
308
480
  query,
@@ -328,7 +500,7 @@ var PrintHive = class {
328
500
  }
329
501
  } catch (error) {
330
502
  if (deadline.aborted && !options2.signal?.aborted) {
331
- const { PrintHiveNetworkError: PrintHiveNetworkError2 } = await import("./errors-TJFN2M7S.js");
503
+ const { PrintHiveNetworkError: PrintHiveNetworkError2 } = await import("./errors-4M56UUFV.js");
332
504
  throw new PrintHiveNetworkError2("timeout");
333
505
  }
334
506
  throw error;
@@ -337,7 +509,7 @@ var PrintHive = class {
337
509
  };
338
510
  this.models = {
339
511
  findByExternalId: (externalId) => transport.request({
340
- decode: (response) => decode2(response, model),
512
+ decode: (response) => decode(response, model),
341
513
  method: "GET",
342
514
  path: `${prefix}/models`,
343
515
  query: new URLSearchParams({ externalId })
@@ -348,7 +520,7 @@ var PrintHive = class {
348
520
  const query = new URLSearchParams();
349
521
  if (cursor !== null) query.set("cursor", cursor);
350
522
  const page2 = await transport.request({
351
- decode: (response) => decode2(response, page),
523
+ decode: (response) => decode(response, page),
352
524
  method: "GET",
353
525
  path: `${prefix}/models/${encodeURIComponent(modelId)}/files`,
354
526
  query
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@printhive/api",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Print Hive REST API client",