@printhive/api 0.0.0-stage → 0.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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - Add `printJobs.create` for multipart project preparation and existing JSON artifact creation.
6
+ - Add creation receipt reads and `waitUntilCreated`, including typed stopped-request errors.
7
+ - Return created receipts even when their referenced job has been deleted.
8
+ - Preserve idempotency keys across retries and include public creation routes in the generated OpenAPI contract.
9
+
10
+ ## 0.1.0
11
+
12
+ Initial partner-production client for server-side integrations such as Printie. Version 0.1.0 covers uploads and model reads only, including source inspection polling and source-file listing. Preparation methods (#4764) and order methods (#4631) will come in later versions.
13
+
14
+ - Upload immutable 3MF sources with external IDs and idempotency keys.
15
+ - Read sources, wait for inspection, look up models, and iterate source files.
16
+ - Expose typed API restrictions, bounded safe retries, timeouts, and cancellation.
17
+ - Generate types from a committed, drift-checked partner-production OpenAPI snapshot.
18
+
19
+ Coverage is limited to `/v1/partner-production/`. Future releases can register more route plugins and widen the snapshot prefix filter without replacing the generation pipeline. They must add explicit resource methods and tests before claiming broader REST API coverage.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HyperFocus LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,134 @@
1
- # Temporary Holding Version
1
+ # @printhive/api
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A TypeScript client for Print Hive's REST API. Version 0.2.0 has zero runtime dependencies and uses global `fetch`. It supports Node 20+, Bun, and Deno with npm compatibility. File paths require the runtime's Node filesystem compatibility. ESM, CommonJS, and TypeScript declarations ship together.
4
+
5
+ Version 0.2.0 adds public single-plate print creation and creation-request polling alongside customer printer reads, partner source uploads, and model reads.
6
+
7
+ Partner API keys belong on your server. Never bundle a key into a browser, mobile app, or other distributed client. Printie's browser calls Printie's backend; that backend calls Print Hive.
8
+
9
+ ## Create a print job
10
+
11
+ Use an API key with `control` to create and `read` to poll. See [ADR 0071](../../docs/adr/0071-public-print-job-requests.md) for target selection and filament semantics.
12
+
13
+ ```ts
14
+ import { PrintHive } from "@printhive/api";
15
+
16
+ const hive = new PrintHive({ apiKey: process.env.PRINTHIVE_API_KEY! });
17
+ const request = await hive.printJobs.create({
18
+ file: "/srv/designs/project.3mf",
19
+ filename: "project.3mf",
20
+ plate: 2,
21
+ filaments: [{ sourceSlot: 0, material: "PETG", color: "#FF0000" }],
22
+ printer_id: "00000000-0000-4000-8000-000000000001",
23
+ idempotencyKey: "storefront:print-1234",
24
+ });
25
+ const created = await hive.printJobs.waitUntilCreated(request.requestId);
26
+ console.log(created.job);
27
+ ```
28
+
29
+ `printJobs.create` sends multipart to `/v1/print-jobs/create` for a file path or `Uint8Array` plus `filename`. `plate` and typed `filaments` are required; `targetSlot` is optional. Other inputs include `printer_id`, `name`, `externalId`, `release`, `on_filament_mismatch`, and existing `print_options`. This method reads file paths into memory, copies byte inputs, and can retry the multipart body safely. It returns the server's `202` request fields plus `idempotencyKey`. Persist your own key before calling when retries must survive process restarts. Changed metadata or bytes with the same key conflict.
30
+
31
+ For an existing artifact, `printJobs.create({ name, model_file_id, ... })` sends JSON and returns the existing `201` response with `job` and eligibility fields plus `idempotencyKey`. The client generates an idempotency key once per call if omitted.
32
+
33
+ `printJobs.getCreateRequest(requestId)` reads the current receipt. `waitUntilCreated(requestId, { intervalMs?, timeoutMs?, signal? })` polls while `preparing`, defaults to a 120-second total deadline, and returns when `created`, including a retained receipt with `job: null` after the job was deleted. `PrintJobCreateStoppedError.request` exposes a `held`, `failed`, or `cancelled` receipt, including its required actions or failure reasons. It does not authorize a held request automatically. Use the HTTP release/cancel operations described in the [integration reference](../../docs/integrations/printie/planned-production.md) when needed.
34
+
35
+ ## Upload a Printie design
36
+
37
+ Install `@printhive/api` after its first npm release. Before release, build and pack this workspace for local installation.
38
+
39
+ ```ts
40
+ import { PrintHive } from "@printhive/api";
41
+
42
+ const hive = new PrintHive({ apiKey: process.env.PRINTHIVE_API_KEY! });
43
+ const source = await hive.partnerSources.upload({
44
+ file: "/srv/designs/project.3mf",
45
+ filename: "project.3mf",
46
+ externalId: "printie:design-1234",
47
+ idempotencyKey: "printie:design-1234:revision-1",
48
+ });
49
+ const settled = await hive.partnerSources.waitUntilSettled(
50
+ source.sourceVersionId,
51
+ {
52
+ intervalMs: 1000,
53
+ timeoutMs: 120_000,
54
+ }
55
+ );
56
+ if (settled.status === "ready") {
57
+ console.log(settled.inspection);
58
+ } else {
59
+ console.error(settled.error.code, settled.error.limit);
60
+ }
61
+ ```
62
+
63
+ The key needs `production:upload` and `production:read` scopes. The organization must have partner source intake enabled. `ready` means inspection completed; it does not authorize printing or establish printer compatibility.
64
+
65
+ ## Client options and resources
66
+
67
+ `new PrintHive({ apiKey, baseUrl?, fetch?, timeoutMs?, maxRetries? })` defaults to `https://api.printhiv3d.com`, global `fetch`, 30 seconds per request attempt, and two retries. `baseUrl` can include a reverse-proxy path prefix. Redirects fail to prevent credentials moving to another origin. Missing, blank, or invalid header values for `apiKey` throw `TypeError` during construction.
68
+
69
+ `partnerSources.upload` accepts a filesystem path, Buffer, Uint8Array, or `ReadableStream<Uint8Array>`. Streams require an explicit byte `size`. Every upload sends `Content-Length` and a raw `model/3mf` body. Byte inputs are copied and path inputs are read into memory before sending. Upload returns the source fields plus `idempotencyKey`. When omitted, the client generates that key once per call. Persist a caller-supplied key before the first call when retries must survive process restarts. Use a new key when the bytes or binding change. An optional `modelId` binds to an existing model.
70
+
71
+ `partnerSources.get(sourceId)` returns one typed source. `partnerSources.waitUntilSettled(sourceId, { intervalMs?, timeoutMs?, signal? })` resolves on `ready` or `failed`. Its default total polling deadline is 120 seconds; cancellation stops active reads and delays.
72
+
73
+ `models.findByExternalId(externalId)` returns the bound model and latest source. `models.listSourceFiles(modelId)` is an async iterator over sources across cursor pages.
74
+
75
+ ```ts
76
+ const model = await hive.models.findByExternalId("printie:design-1234");
77
+ for await (const source of hive.models.listSourceFiles(model.modelId)) {
78
+ console.log(source.sourceVersionId, source.status);
79
+ }
80
+ ```
81
+
82
+ ## Handle failures and retries
83
+
84
+ `PrintHiveApiError` exposes HTTP `status`, the response's `error` as `code`, optional `limit` with `name`, `max`, and `observed`, `retryAfterMs`, and `requestId`. Its optional `body` exposes the validated error envelope, preserving additional fields as JSON values for callers to narrow. Invalid envelopes leave `body` undefined. Error messages omit response bodies and credentials. `PrintHiveNetworkError.reason` distinguishes `network`, `timeout`, and `aborted` failures.
85
+
86
+ The API can answer `411 content_length_required`, `413 upload_too_large`, `429 upload_capacity_reached`, `409 external_id_conflict`, or `409 idempotency_conflict`. Inspection failures resolve through the source's `failed` status and include their own error and optional restriction limit.
87
+
88
+ Only 429, 503, and network failures retry. GET requests are idempotent. Upload retries retain their idempotency key and require replayable bytes or a path. One-shot streams never retry. Timeouts and caller aborts never retry. Retries use exponential backoff with jitter and wait at least `Retry-After`, supporting both seconds and HTTP dates. If `Retry-After` exceeds 60 seconds, the client throws the API error with `retryAfterMs` so the caller can schedule the retry. It never retries before that delay. `maxRetries: 0` disables retries. Request timeouts apply per attempt; polling also has a total deadline.
89
+
90
+ ## Coverage and contract maintenance
91
+
92
+ Version 0.2.0 covers creation and request polling under `/v1/print-jobs/create`, customer printer list and detail reads at `/v1/printers` and `/v1/printers/{id}`, plus uploads, source reads and polling, external-id model lookup, and source-file listing under `/v1/partner-production/`. It offers no generic request method for other routes.
93
+
94
+ `apps/api/scripts/export-openapi.ts` builds a schema-only Fastify app from the real printer, file-compatibility, print-job, and partner route plugins, without listening, worker startup, database calls, or queue startup. It uses stub environment values. This intentionally exports authenticated partner routes directly. The Swagger UI's public-operation filter would remove them, and generic error enrichment would lose partner restriction details.
95
+
96
+ `bun run generate` filters that export by the prefix list in `scripts/contracts.mjs`, commits `openapi.json`, and generates TypeScript types and standalone response validators. AJV compiles the validators during development; the build bundles their small runtime helpers. `openapi-typescript` is a development dependency because it generates OpenAPI 3 union types without adding runtime dependencies. `bun run check:drift` reexports and compares the snapshot. Types and standalone validators regenerate in ignored `.temp` build output on every check. `bun run test` builds the distribution and runs its HTTP tests against `dist`, including in the release workflow. `api-client-check.yml` also runs when API or client sources change, so drift checks do not depend on Turbo selecting the client as affected.
97
+
98
+ To backfill more API capabilities, register their existing route plugins in the schema app and widen the prefix list. The snapshot and generation pipeline stay the same. Add resource methods and behavior tests for those routes before advertising coverage. Build API workspace dependencies first with `bun run build --filter='@hiv3d/api^...'` from the repository root.
99
+
100
+ Run `bun run build`, `bun run check-types`, and `bun run test` in this package. The integration test skips by default. To read a source from an owned loopback API, set `PRINTHIVE_LOCAL_API_KEY`, `PRINTHIVE_LOCAL_SOURCE_ID`, and optionally `PRINTHIVE_LOCAL_API_URL`.
101
+
102
+ ## Release
103
+
104
+ The manual `api-client-publish.yml` workflow builds, verifies, checks npm version availability, and publishes with `npm publish --access public`. It never runs on push. npm trusted publishing authorizes the release: npm trusts that workflow file in `PrintHive/hive`, so the workflow needs no stored npm token. Package maintainers can still publish by hand with their own npm credentials. To bump the version, change `version` in `package.json`, merge, and dispatch the workflow. Dispatching a release is a maintainer action.
105
+
106
+ Provenance is disabled because the source repository is private. It can be enabled later through a matching public source mirror and its publishing workflow. The package's `repository` field names `PrintHive/hive`, because trusted publishing from GitHub requires `repository.url` to match the publishing repository. Outside readers see only the URL.
107
+
108
+ ### Read organization printers
109
+
110
+ Use a customer API key with the `read` tier or higher. Keys with any
111
+ `production:*` scope cannot call these routes, including mixed-scope keys.
112
+
113
+ ```ts
114
+ const customer = new PrintHive({
115
+ apiKey: process.env.PRINT_HIVE_CUSTOMER_API_KEY!,
116
+ });
117
+ const page = await customer.printers.list({ limit: 50 });
118
+ if (page.printers[0]) {
119
+ const printer = await customer.printers.get(page.printers[0].id);
120
+ }
121
+ ```
122
+
123
+ List returns `{ printers, nextCursor }`. Pass `nextCursor` as `cursor` until
124
+ it is null. Detail returns 404 for missing or foreign-organization printers.
125
+ Each printer includes model, connection, status, and `readyMarked`, the stored
126
+ readiness flag. It does not guarantee an empty bed or authorize dispatch.
127
+ `loadedFilament` uses Smart Queue's material source. Each slot includes
128
+ `address: { ams_id, slot_id }`, material, color, and a `state` of `loaded`,
129
+ `empty`, or `unknown`. AMS IDs and tray IDs are zero based; external spools
130
+ use AMS ID 255 and slot ID 254 or 255. Reads return stored observations and
131
+ never contact a printer.
132
+
133
+ `readiness.ready` reports the stored ready flag. `readiness.bedClear` reports
134
+ the flag only when its source is `bed_occupancy_verified`; otherwise it is null.