@prism-draft/sdk 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/README.md +196 -2
- package/dist/client.d.ts +47 -0
- package/dist/errors.d.ts +35 -0
- package/dist/generated/resources.d.ts +1460 -0
- package/dist/generated/schema.d.ts +16240 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1926 -0
- package/dist/middleware.d.ts +15 -0
- package/dist/retry.d.ts +22 -0
- package/dist/transport.d.ts +15 -0
- package/dist/types.d.ts +9 -0
- package/dist/webhook-events.d.ts +99 -0
- package/dist/webhooks.d.ts +27 -0
- package/package.json +39 -4
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Middleware } from "openapi-fetch";
|
|
2
|
+
/** Turns every non-2xx answer into a `PrismDraftError` (decision D5). */
|
|
3
|
+
export declare const throwOnError: Middleware;
|
|
4
|
+
/**
|
|
5
|
+
* A path value of `""`, `.` or `..` survives URL-encoding and then `new Request`
|
|
6
|
+
* collapses it as a dot segment, so the call would reach another route (`..` turns
|
|
7
|
+
* `/api/articles/..` into `/api/`). It is refused before any request is made.
|
|
8
|
+
*/
|
|
9
|
+
export declare const rejectUnsafePathParams: Middleware;
|
|
10
|
+
/**
|
|
11
|
+
* A 2xx answer whose body is not JSON (an HTML error page from a proxy, say) becomes a
|
|
12
|
+
* `PrismDraftError`; `openapi-fetch` would throw a bare `SyntaxError`. The message carries
|
|
13
|
+
* neither the body nor the URL.
|
|
14
|
+
*/
|
|
15
|
+
export declare const rejectNonJsonBody: Middleware;
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The default for `maxRetryDelayMs`. A server that asks for more is not retried: see `retryAfterMs`. */
|
|
2
|
+
export declare const DEFAULT_MAX_RETRY_DELAY_MS = 8000;
|
|
3
|
+
/**
|
|
4
|
+
* What the client needs of `fetch`: a call with a `Request`, `URL` or string and optional init.
|
|
5
|
+
* Looser than `typeof fetch`, which under Bun's types also demands `preconnect`, so a plain
|
|
6
|
+
* wrapper function is accepted without a cast. The SDK itself only calls `fetch(request)`
|
|
7
|
+
* (and `fetch(request, requestInit)` when the client has a `requestInit`).
|
|
8
|
+
*/
|
|
9
|
+
export type FetchLike = (input: Request | URL | string, init?: RequestInit) => Promise<Response>;
|
|
10
|
+
export interface RetryOptions {
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
/** Fails an attempt after this many ms with a `PrismDraftTimeoutError`; a request's own `timeoutMs` overrides it. Unset: none. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
/** The longest a retry waits; a `Retry-After` above it is not retried. Default 8000. */
|
|
15
|
+
maxRetryDelayMs?: number;
|
|
16
|
+
/** Waits `ms`. The wait is abandoned when the caller's `signal` aborts, whether or not this honours it. */
|
|
17
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/** One signal that aborts with the reason of whichever of `signals` aborts first. */
|
|
20
|
+
export declare function anySignal(signals: AbortSignal[]): AbortSignal;
|
|
21
|
+
/** Retries idempotent reads only; a write that may have landed is never replayed (decision D6). */
|
|
22
|
+
export declare function retryingFetch(inner: FetchLike, options?: RetryOptions): typeof fetch;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Client } from "openapi-fetch";
|
|
2
|
+
import type { paths } from "./generated/schema.js";
|
|
3
|
+
/** The `openapi-fetch` client over the whole API, which every resource class calls. */
|
|
4
|
+
export type PrismDraftClient = Client<paths>;
|
|
5
|
+
/** What a call takes besides its input. */
|
|
6
|
+
export interface RequestOptions {
|
|
7
|
+
/** Aborts the request when it fires; an aborted read is not retried. */
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
/**
|
|
10
|
+
* Fails the call with a `PrismDraftTimeoutError` when an attempt takes longer than this many
|
|
11
|
+
* milliseconds. Per attempt: a retried read gets a fresh timer. Overrides the client's
|
|
12
|
+
* `timeoutMs`. A finite number above 0.
|
|
13
|
+
*/
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ArticlesGetOutput, ArticlesListOutput } from "./generated/resources.js";
|
|
2
|
+
/** An article as `articles.list` returns it. */
|
|
3
|
+
export type Article = ArticlesListOutput["articles"][number];
|
|
4
|
+
/** `DRAFT`, `QUEUED`, `PROCESSING`, `NEEDS_REVIEW`, `CHANGES_REQUESTED`, `APPROVED` or `PUBLISHED`. */
|
|
5
|
+
export type ArticleStatus = Article["status"];
|
|
6
|
+
/** A category as it is embedded in an article of `articles.list`. */
|
|
7
|
+
export type Category = Article["categories"][number];
|
|
8
|
+
/** A language version of an article, as listed under `translations.versions` by `articles.get`. */
|
|
9
|
+
export type ArticleVersion = ArticlesGetOutput["translations"]["versions"][number];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export declare const WEBHOOK_EVENTS: readonly ["article.created", "article.generation.started", "article.generation.completed", "article.generation.failed", "article.approved", "article.rejected", "article.published", "article.updated", "article.unpublished", "image.generated", "image.regenerated", "credits.purchased", "credits.granted", "credits.consumed"];
|
|
2
|
+
export type WebhookEventName = (typeof WEBHOOK_EVENTS)[number];
|
|
3
|
+
export type ArticleChangedField = "title" | "content" | "slug" | "categories" | "metaTitle" | "metaDescription" | "targetKeywords" | "secondaryKeywords";
|
|
4
|
+
interface ArticleSnapshot {
|
|
5
|
+
articleId: string;
|
|
6
|
+
workspaceId: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
status: string;
|
|
10
|
+
primaryLanguage: string;
|
|
11
|
+
translationGroupId: string;
|
|
12
|
+
title: string;
|
|
13
|
+
}
|
|
14
|
+
export interface WebhookEventData {
|
|
15
|
+
"article.created": {
|
|
16
|
+
articleId: string;
|
|
17
|
+
projectId: string;
|
|
18
|
+
title: string;
|
|
19
|
+
};
|
|
20
|
+
"article.generation.started": {
|
|
21
|
+
articleId: string;
|
|
22
|
+
jobId: string;
|
|
23
|
+
estimatedCredits: number;
|
|
24
|
+
};
|
|
25
|
+
"article.generation.completed": {
|
|
26
|
+
articleId: string;
|
|
27
|
+
jobId: string;
|
|
28
|
+
consumedCredits: number;
|
|
29
|
+
};
|
|
30
|
+
"article.generation.failed": {
|
|
31
|
+
articleId: string;
|
|
32
|
+
jobId: string;
|
|
33
|
+
error: string;
|
|
34
|
+
};
|
|
35
|
+
"article.approved": {
|
|
36
|
+
articleId: string;
|
|
37
|
+
reviewId: string;
|
|
38
|
+
previousStatus: string;
|
|
39
|
+
};
|
|
40
|
+
"article.rejected": {
|
|
41
|
+
articleId: string;
|
|
42
|
+
reviewId: string;
|
|
43
|
+
decision: "rejected" | "changes_requested";
|
|
44
|
+
};
|
|
45
|
+
"article.published": ArticleSnapshot;
|
|
46
|
+
"article.updated": ArticleSnapshot & {
|
|
47
|
+
changed: ArticleChangedField[];
|
|
48
|
+
/** Present when the slug changed. */
|
|
49
|
+
previousSlug?: string;
|
|
50
|
+
};
|
|
51
|
+
"article.unpublished": ArticleSnapshot & {
|
|
52
|
+
previousStatus: "PUBLISHED";
|
|
53
|
+
reason: "status_changed" | "deleted";
|
|
54
|
+
};
|
|
55
|
+
"image.generated": {
|
|
56
|
+
articleId: string;
|
|
57
|
+
assetId: string;
|
|
58
|
+
type: "hero" | "inline";
|
|
59
|
+
};
|
|
60
|
+
"image.regenerated": {
|
|
61
|
+
articleId: string;
|
|
62
|
+
assetId: string;
|
|
63
|
+
/**
|
|
64
|
+
* Sent when a hero is replaced by a URL or an upload (`"hero"`). Absent when the
|
|
65
|
+
* worker regenerates an image in place: that delivery is `articleId` and `assetId`.
|
|
66
|
+
*/
|
|
67
|
+
type?: "hero" | "inline";
|
|
68
|
+
};
|
|
69
|
+
"credits.purchased": {
|
|
70
|
+
creditAmount: number;
|
|
71
|
+
stripeEventId: string;
|
|
72
|
+
};
|
|
73
|
+
"credits.granted": {
|
|
74
|
+
creditAmount: number;
|
|
75
|
+
stripeEventId: string;
|
|
76
|
+
};
|
|
77
|
+
"credits.consumed": {
|
|
78
|
+
articleId: string;
|
|
79
|
+
jobId: string;
|
|
80
|
+
credits: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export type WebhookEvent = {
|
|
84
|
+
[E in WebhookEventName]: {
|
|
85
|
+
event: E;
|
|
86
|
+
data: WebhookEventData[E];
|
|
87
|
+
};
|
|
88
|
+
}[WebhookEventName];
|
|
89
|
+
export declare const WEBHOOK_EVENT_KEYS: {
|
|
90
|
+
[E in WebhookEventName]: readonly (keyof WebhookEventData[E])[];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* The keys of `WEBHOOK_EVENT_KEYS` that some emitter leaves out; every other key is
|
|
94
|
+
* always sent. Pinned against the emitters by `test/webhook-drift.test.ts`.
|
|
95
|
+
*/
|
|
96
|
+
export declare const WEBHOOK_OPTIONAL_KEYS: {
|
|
97
|
+
[E in WebhookEventName]?: readonly (keyof WebhookEventData[E])[];
|
|
98
|
+
};
|
|
99
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type WebhookEvent } from "./webhook-events.js";
|
|
2
|
+
/** The header a delivery carries its signature in: `t=<unix seconds>,v1=<hex>`. */
|
|
3
|
+
export declare const WEBHOOK_SIGNATURE_HEADER = "x-prism-draft-signature";
|
|
4
|
+
export interface VerifyInput {
|
|
5
|
+
/** The string returned when the endpoint was registered, used as is. */
|
|
6
|
+
secret: string;
|
|
7
|
+
/** The request body exactly as received. Never JSON.parse and re-stringify it first. */
|
|
8
|
+
rawBody: string | Uint8Array;
|
|
9
|
+
signature: string;
|
|
10
|
+
toleranceSeconds?: number;
|
|
11
|
+
/** Unix seconds; for tests. */
|
|
12
|
+
now?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Whether the delivery is signed with `secret` and recent. Returns `false` for
|
|
16
|
+
* anything wrong with the delivery (including an empty secret) and throws a
|
|
17
|
+
* `RangeError` for a `toleranceSeconds` that is not a finite number >= 0, which
|
|
18
|
+
* is a mistake in the caller's code.
|
|
19
|
+
*/
|
|
20
|
+
export declare function verifyWebhookSignature(input: VerifyInput): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Verifies a delivery and returns it typed. Throws `WebhookSignatureError` for a
|
|
23
|
+
* bad or stale signature, `WebhookPayloadError` (a subclass of it) for a signed body that is
|
|
24
|
+
* not JSON or not `{ event, data }`, and `UnknownWebhookEventError` for an event this version
|
|
25
|
+
* does not know: answer 2xx to that one, or the API retries it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function constructWebhookEvent(input: VerifyInput): Promise<WebhookEvent>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prism-draft/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Typed TypeScript client for the PrismDraft API, with webhook verification.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"api-client",
|
|
7
|
+
"openapi",
|
|
8
|
+
"prismdraft",
|
|
9
|
+
"sdk",
|
|
10
|
+
"webhooks"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://docs.prism-draft.com/sdk",
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"openapi-fetch": "0.17.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=20"
|
|
40
|
+
}
|
|
41
|
+
}
|