@prism-draft/sdk 0.0.0-stage → 0.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.
package/README.md CHANGED
@@ -1,3 +1,172 @@
1
- # Temporary Holding Version
1
+ # @prism-draft/sdk
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 typed TypeScript client for the PrismDraft API, with webhook verification. It is
4
+ generated from the API's OpenAPI document, so a route that changes shape changes the
5
+ types and the tests fail until the SDK is regenerated.
6
+
7
+ Runs on any runtime with global `fetch` and Web Crypto. The pack test installs the packed
8
+ tarball and runs a client, a request, a timeout and a webhook signature under Bun and Node
9
+ 20, 22 and 24 (Node 20.20, 22.23 and 24.21 were the versions run; `engines` says
10
+ `node >= 20`). It imports nothing from `node:` or `bun:`. A custom `fetch` must honour the
11
+ request's `signal`, which `timeoutMs` and cancellation rely on.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install @prism-draft/sdk
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ `apiKey` is a workspace API key (`pd_live_…`) created in the web app, under Settings, API
22
+ keys. It carries exactly the permissions of its role.
23
+
24
+ ```ts
25
+ import { PrismDraft } from "@prism-draft/sdk";
26
+
27
+ const pd = new PrismDraft({ apiKey: process.env.PRISMDRAFT_API_KEY! });
28
+
29
+ const { articles, total } = await pd.articles.list({ workspaceId, status: "PUBLISHED" });
30
+ console.log(`${articles.length} of ${total} published articles`);
31
+ ```
32
+
33
+ The example assumes `workspaceId` is in scope: the id after `/w/` in the web app's address.
34
+
35
+ ## Resources
36
+
37
+ One property per resource, one method per operation. Each method has `<Resource><Method>Input`
38
+ and `<Resource><Method>Output` types, exported from the package (`ArticlesListInput`).
39
+
40
+ | Property | Methods |
41
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
42
+ | `pd.articles` | `create` `list` `duplicate` `get` `update` `delete` `submit` `approve` `reject` `requestChanges` `publish` `unpublish` `transitionTranslations` `listReviews` `listVersions` `getVersion` `getSources` `restoreVersion` `listComments` `addComment` `resolveComment` `reopenComment` `checkDuplicates` `setHeroFromUrl` `setHeroFromFile` `detachHeroFromBody` |
43
+ | `pd.articleGroups` | `list` `get` `delete` `approve` `publish` `unpublish` |
44
+ | `pd.categories` | `list` `create` `update` `delete` `setForArticle` `suggestForArticle` `suggestForProject` `translate` |
45
+ | `pd.projects` | `list` `create` `get` `update` `delete` `archive` `restore` `listMembers` `addMember` `setMemberRole` `removeMember` |
46
+ | `pd.generation` | `estimate` `getOutline` `approveOutline` `aiEdit` `start` `getStatus` `cancel` `regenerateImage` `updateAsset` `deleteAsset` |
47
+ | `pd.translations` | `estimate` `create` |
48
+ | `pd.webhooks` | `list` `create` `delete` |
49
+ | `pd.integrations` | `list` `create` `update` `delete` |
50
+ | `pd.knowledge` | `list` `create` `uploadContent` `reingest` `delete` |
51
+ | `pd.brandVoices` | `list` `create` `update` `delete` |
52
+ | `pd.contentTemplates` | `list` `create` `update` `delete` |
53
+ | `pd.briefs` | `draft` `list` `create` `get` `update` `delete` |
54
+ | `pd.ideas` | `list` `create` `update` `delete` `generate` |
55
+ | `pd.calendar` | `list` `create` `update` `delete` |
56
+ | `pd.links` | `list` `generate` `decide` |
57
+ | `pd.social` | `generate` `list` |
58
+ | `pd.siteAnalyses` | `start` `get` `apply` |
59
+ | `pd.dashboard` | `getAnalytics` `getTrends` `getArticleCounts` `listJobs` `listAudit` `getCreditBurn` |
60
+ | `pd.usage` | `listTransactions` `get` `listPayments` `listQuotas` `setQuota` |
61
+ | `pd.billing` | `listCreditPackages` `getCredits` `adjustCredits` `listPlans` `getSubscription` `startSubscriptionCheckout` `openPortal` `startCheckout` |
62
+ | `pd.workspaces` | `list` `create` `get` `update` `listMembers` `addMember` `setMemberRoles` `removeMember` |
63
+ | `pd.roles` | `list` `create` `update` `delete` `duplicate` |
64
+ | `pd.permissions` | `list` |
65
+ | `pd.invitations` | `list` `revoke` `resend` |
66
+ | `pd.apiKeys` | `list` `create` `revoke` `rotate` |
67
+ | `pd.blog` | `listPosts` `listCategories` `getPost` `getPostEdit` (PrismDraft's public blog: the first three need no valid key) |
68
+
69
+ ## Documentation
70
+
71
+ The guides and the reference for every method are at <https://docs.prism-draft.com/sdk>.
72
+
73
+ - [Authentication](https://docs.prism-draft.com/sdk/guides/authentication): keys, roles, the workspace id, rotation.
74
+ - [Conventions](https://docs.prism-draft.com/sdk/guides/conventions): input, output, pagination, `pd.raw`.
75
+ - [Errors](https://docs.prism-draft.com/sdk/guides/errors): `PrismDraftError` and every error code.
76
+ - [Retries and timeouts](https://docs.prism-draft.com/sdk/guides/retries-and-timeouts)
77
+ - [Uploads](https://docs.prism-draft.com/sdk/guides/uploads)
78
+ - [Webhooks](https://docs.prism-draft.com/sdk/guides/webhooks): verifying a delivery and every event.
79
+ - [Versioning](https://docs.prism-draft.com/sdk/guides/versioning)
80
+ - [Recipes](https://docs.prism-draft.com/sdk/guides/recipes): publish an imported article, sync a site, paginate, bulk-update categories.
81
+
82
+ The constructor throws a `TypeError` for an empty `apiKey`, so an unset environment
83
+ variable fails at start-up instead of as a 401 later.
84
+
85
+ ## License
86
+
87
+ `UNLICENSED`: the package is public to install, and no permission is granted to copy or
88
+ redistribute its code. Using it to call the PrismDraft API is what it is for.
89
+
90
+ ## Releasing
91
+
92
+ Published from `main`, by a maintainer logged in to npm (`npm whoami`) with publish rights
93
+ on the `@prism-draft` scope and a 2FA device.
94
+
95
+ ```sh
96
+ git switch main && git pull
97
+ # bump "version" in packages/sdk/package.json (0.x: minor for new API surface or
98
+ # SDK changes, patch for fixes), commit it, and merge it through a pull request
99
+ bun install
100
+ bun run --cwd apps/api openapi:export && bun run --cwd packages/sdk generate # must leave no diff
101
+ cd packages/sdk && bun --env-file=../../.env test
102
+ npm publish --dry-run # `prepublishOnly` builds; check the file list: dist/, README, package.json
103
+ npm publish --access public # asks for the 2FA code
104
+ git tag sdk-v<version> && git push origin sdk-v<version>
105
+ ```
106
+
107
+ A published version cannot be replaced. Fix forward with a patch release; `npm deprecate`
108
+ marks a bad one.
109
+
110
+ ## Development
111
+
112
+ The package exports its TypeScript source inside the workspace (`"exports":
113
+ "./src/index.ts"`) and `dist/` only in the packed tarball.
114
+
115
+ **What is generated.** `openapi.json` is a snapshot of the API's OpenAPI document,
116
+ filtered to what an API key can use. `src/generated/schema.ts` (types, by
117
+ `openapi-typescript`) and `src/generated/resources.ts` (one class per resource) come
118
+ from it. All three are committed. After any route change:
119
+
120
+ ```sh
121
+ bun run --cwd apps/api openapi:export
122
+ bun run --cwd packages/sdk generate
123
+ ```
124
+
125
+ Two tests fail on a stale file: `apps/api/test/openapi-snapshot.test.ts` (the snapshot
126
+ against the live document) and `packages/sdk/test/generated.test.ts` (the generated
127
+ files against the snapshot).
128
+
129
+ **Coverage gate.** `test/response-coverage.test.ts` requires every operation in the
130
+ surface to declare a JSON 2xx `response` schema (a 204-only operation is exempt) and an
131
+ `operationId` of the form `resource.method`, unique across the API. The
132
+ `resource` part becomes the class (`articleGroups` is `pd.articleGroups`) and
133
+ `method` the method. `bun run --cwd packages/sdk coverage [Tag …]` lists what is still
134
+ missing, read from the live app, not the snapshot.
135
+
136
+ **The surface is a denylist.** `scripts/surface.ts` lists `SDK_EXCLUDED_TAGS` and
137
+ `SDK_EXCLUDED_OPERATIONS` (`"METHOD /path"`, each with its reason) and drops every
138
+ `/events` stream and every untagged operation. Everything else is in the SDK by default,
139
+ so a new route needs its `operationId` and response schema or the gate fails. A route
140
+ that cannot be a typed method (a download, a redirect, a person's browser flow) goes on
141
+ the denylist, with a reason; `test/surface.test.ts` checks that every entry still exists.
142
+
143
+ **To add an operation:** declare `operationId` and the 2xx `response` schema in the
144
+ route, add a test that parses a real answer with that schema (see
145
+ `apps/api/test/sdk-responses-*.test.ts`), run the two commands above, commit the
146
+ regenerated files. A request body the document cannot describe (an upload read by a
147
+ content-type parser) goes in `RAW_BODY_OPERATIONS` in `scripts/generate.ts`.
148
+
149
+ **`typescript-classic`.** A devDependency, an alias of `typescript@5.9`, used only by
150
+ `scripts/generate.ts`. `openapi-typescript` builds its output with the classic compiler
151
+ API (`ts.factory`), which the workspace's TypeScript 7 (the Go compiler) does not
152
+ have; the generator registers the alias in place of `typescript` before importing it.
153
+ `tsc` everywhere else is still TypeScript 7. It is a devDependency, so it is not in the
154
+ tarball's dependencies.
155
+
156
+ **Build.** `bun run build` writes `dist/index.js` (one ESM file, not minified, with
157
+ `openapi-fetch` left as an import) and the `.d.ts` files. Three points about
158
+ TypeScript 7:
159
+
160
+ - Declarations are emitted by `tsc` (`tsconfig.build.json`, `emitDeclarationOnly`), which
161
+ works, but needs an explicit `rootDir` (TS5011 otherwise).
162
+ - `rewriteRelativeImportExtensions` rewrites `./x.ts` to `./x.js` in emitted JavaScript,
163
+ not in declarations, which kept the `.ts` specifiers. `scripts/build.ts` rewrites
164
+ them in `dist/**/*.d.ts` after `tsc` runs. The declarations are still the compiler's.
165
+ - npm and Bun ignore `publishConfig.exports` (pnpm applies it), so a plain `npm pack`
166
+ would export `./src/index.ts`, which the tarball does not contain. `prepack` and
167
+ `postpack` (`scripts/pack-manifest.ts`) swap the published `exports` in for the
168
+ pack and put the workspace form back, and drop `scripts` and `devDependencies`.
169
+
170
+ `test/pack.test.ts` builds, packs, installs the tarball into an empty project, typechecks a
171
+ consumer and every example in this README and in `docs/guides` with `tsc`, and imports it
172
+ under Bun and Node.
@@ -0,0 +1,36 @@
1
+ import { PrismDraftResources } from "./generated/resources.js";
2
+ import type { PrismDraftClient } from "./transport.js";
3
+ export declare const DEFAULT_BASE_URL = "https://api.prism-draft.com";
4
+ export interface PrismDraftOptions {
5
+ /** A workspace API key (`pd_live_…`). Must not be empty. */
6
+ apiKey: string;
7
+ /** An absolute `http(s)` URL; default `https://api.prism-draft.com`. */
8
+ baseUrl?: string;
9
+ fetch?: typeof fetch;
10
+ /** Retries for GET/HEAD on network errors and 429/502/503/504. Default 2; 0 disables. */
11
+ maxRetries?: number;
12
+ /**
13
+ * Default for every call: an attempt that takes longer fails with a `PrismDraftTimeoutError`.
14
+ * Per attempt, so a retried read gets a fresh timer. Unset: no timeout. A finite number above 0;
15
+ * a call's `init.timeoutMs` overrides it.
16
+ */
17
+ timeoutMs?: number;
18
+ /**
19
+ * The longest a retry waits, in ms; default 8000. A `Retry-After` above it is not retried
20
+ * (the answer is returned), so 60000 rides out the API's rate-limit window. A finite number above 0.
21
+ */
22
+ maxRetryDelayMs?: number;
23
+ }
24
+ /**
25
+ * The API client: one property per resource (`pd.articles.list({ workspaceId })`),
26
+ * and `raw`, the underlying `openapi-fetch` client, for anything the methods do not cover.
27
+ * Every non-2xx answer throws a `PrismDraftError`.
28
+ */
29
+ export declare class PrismDraft extends PrismDraftResources {
30
+ readonly raw: PrismDraftClient;
31
+ /**
32
+ * @throws {TypeError} for an empty `apiKey`, a `baseUrl` that is not an absolute http(s) URL,
33
+ * or a `timeoutMs` or `maxRetryDelayMs` that is not a finite number above 0.
34
+ */
35
+ constructor(options: PrismDraftOptions);
36
+ }
@@ -0,0 +1,25 @@
1
+ /** The API's structured error (spec §71): `{ error: { code, message, details? } }`. */
2
+ export declare class PrismDraftError extends Error {
3
+ readonly status: number;
4
+ readonly code: string;
5
+ readonly details: Record<string, unknown> | undefined;
6
+ constructor(status: number, code: string, message: string, details?: Record<string, unknown>);
7
+ }
8
+ /**
9
+ * A request that did not finish within `timeoutMs` (the client option or the per-call one).
10
+ * Not a `PrismDraftError`: the API never answered. A call the caller cancelled with its own
11
+ * `signal` rejects with that signal's reason instead.
12
+ */
13
+ export declare class PrismDraftTimeoutError extends Error {
14
+ readonly timeoutMs: number;
15
+ constructor(timeoutMs: number);
16
+ }
17
+ /** A webhook delivery whose signature is missing, malformed, stale or wrong. */
18
+ export declare class WebhookSignatureError extends Error {
19
+ constructor(message?: string);
20
+ }
21
+ /** A signed delivery for an event this SDK version does not know. Answer 2xx and ignore it. */
22
+ export declare class UnknownWebhookEventError extends Error {
23
+ readonly event: string;
24
+ constructor(event: string);
25
+ }