Parent: #4
Depends on: #17, #23
Goal
Create packages/post-kit-client — a thin, typed npm package for trusted server-side Node.js/TypeScript consumers to call the PostKit API. Consumers use this SDK instead of hand-rolling HTTP calls, auth headers, or error handling.
Scope
Package setup
- Path:
packages/post-kit-client
- npm name:
@singleton-sd/post-kit-client
- TypeScript ESM, Node.js >= 20,
node --test, src/**/*.spec.ts convention
- Runtime dependencies:
@singleton-sd/post-kit-types only — use native fetch (no extra HTTP libraries)
- Public npm publication under Singleton SD scope
- Mirror tsconfig and package.json conventions of
packages/post-kit-email
Desired API
import { PostKitClient } from '@singleton-sd/post-kit-client';
const postKit = new PostKitClient({
endpoint: process.env.POSTKIT_URL!,
apiKey: process.env.POSTKIT_API_KEY!,
});
await postKit.send({
template: 'marketing.contact-us',
to: 'hello@example.com',
variables: { name, email, message },
});
Client constructor options
interface PostKitClientOptions {
endpoint: string; // Base URL of the PostKit API
apiKey: string; // Bearer token for Authorization header
timeout?: number; // Request timeout ms (default: 30_000)
fetch?: typeof globalThis.fetch; // Injectable for testing
}
send(request: SendRequest): Promise<SendResponse>
- Sends
POST {endpoint}/emails/send with Authorization: Bearer {apiKey}
- Request body:
SendRequest from @singleton-sd/post-kit-types
- Success: returns
SendResponse
- Non-2xx: throws
PostKitRequestError (see below)
- Timeout: throws
PostKitRequestError with code TIMEOUT
- Network failure: throws
PostKitRequestError with code NETWORK_ERROR
Error class
class PostKitRequestError extends Error {
readonly status: number | undefined; // HTTP status if available
readonly code: string; // PostKitErrorCode or 'TIMEOUT' | 'NETWORK_ERROR'
readonly correlationId: string | undefined;
}
AbortSignal / timeout
- Accept an
AbortSignal on send() options: send(request, { signal?: AbortSignal })
- If neither
signal nor client-level timeout produces a timeout, the request runs indefinitely — document this.
Browser safety
- No automatic secret sourcing from browser globals.
- README must clearly state this package is for trusted server-side use and include a note about public forms using a server endpoint intermediary.
Constraints
- Keep the package thin — no business logic, no template rendering, no provider details.
- Do not add HTTP libraries (axios, got, etc.) — native
fetch only.
- Do not duplicate type definitions — import all request/response types from
@singleton-sd/post-kit-types.
- Auth strategy (Bearer header) must be configurable without breaking the
send() API surface — define it in PostKitClientOptions so it can evolve.
- Tests must not make real HTTP calls — inject a mock
fetch.
Acceptance criteria
Agent implementation notes
Read packages/post-kit-email/package.json and tsconfig before scaffolding. Read the send endpoint issue (#23) to confirm the exact request/response shape before finalising the client. The client should not be published until issue #23 (send endpoint) is merged — the API contract should be stable. Branch: feat/24-post-kit-client.
Parent: #4
Depends on: #17, #23
Goal
Create
packages/post-kit-client— a thin, typed npm package for trusted server-side Node.js/TypeScript consumers to call the PostKit API. Consumers use this SDK instead of hand-rolling HTTP calls, auth headers, or error handling.Scope
Package setup
packages/post-kit-client@singleton-sd/post-kit-clientnode --test,src/**/*.spec.tsconvention@singleton-sd/post-kit-typesonly — use nativefetch(no extra HTTP libraries)packages/post-kit-emailDesired API
Client constructor options
send(request: SendRequest): Promise<SendResponse>POST {endpoint}/emails/sendwithAuthorization: Bearer {apiKey}SendRequestfrom@singleton-sd/post-kit-typesSendResponsePostKitRequestError(see below)PostKitRequestErrorwith codeTIMEOUTPostKitRequestErrorwith codeNETWORK_ERRORError class
AbortSignal / timeout
AbortSignalonsend()options:send(request, { signal?: AbortSignal })signalnor client-leveltimeoutproduces a timeout, the request runs indefinitely — document this.Browser safety
Constraints
fetchonly.@singleton-sd/post-kit-types.send()API surface — define it inPostKitClientOptionsso it can evolve.fetch.Acceptance criteria
PostKitClient.send()serialises aSendRequestand returns a typedSendResponse.Authorization: Bearerheader is set fromapiKey.PostKitRequestErrorwith correctstatus,code, andcorrelationId.PostKitRequestErrorwith codeTIMEOUT.AbortSignalonsend()is threaded through tofetch.fetchis used in tests — no real HTTP calls.pnpm -r --if-present run testpasses.Agent implementation notes
Read
packages/post-kit-email/package.jsonand tsconfig before scaffolding. Read the send endpoint issue (#23) to confirm the exact request/response shape before finalising the client. The client should not be published until issue #23 (send endpoint) is merged — the API contract should be stable. Branch:feat/24-post-kit-client.