@tempmd/client
v0.2.0
Published
Runtime-neutral client for publishing temporary previews to temp.md
Downloads
522
Maintainers
Readme
@tempmd/client
Runtime-neutral preview publishing for browsers, Electron renderers, Workers,
and Node.js 20+. It uses web-standard Fetch, Web Crypto, Blob, and typed
arrays without importing Node built-ins.
Install
npm install @tempmd/clientPublish a preview
Anonymous publishing works without an account or API key:
import { TempmdPreviewClient } from "@tempmd/client";
const previews = new TempmdPreviewClient();
const result = await previews.publishPreview({
files: [
{
path: "index.html",
data: "<!doctype html><h1>Preview</h1>",
contentType: "text/html; charset=utf-8",
},
],
spaMode: true,
onProgress: (progress) => console.log(progress),
onState: saveToProtectedStorage,
});
console.log(result.record.canonicalUrl);onState receives portable resumable state before uploads begin and again
after finalization. Store it in the host application's protected storage. The
state may contain update and short-lived upload capabilities.
Publish with a delegated grant
Products using @tempmd/platform can mint a short-lived publish grant on their
server. Pass the grant to the low-level client as its account token; never pass
the Application key to a browser or renderer.
import { TempmdClient, TempmdPreviewClient } from "@tempmd/client";
const api = new TempmdClient({
accountToken: publishGrant,
clientIdentity: "my-builder/1.0.0",
});
const previews = new TempmdPreviewClient(api);
const result = await previews.publishPreview({
files: [{ path: "index.html", data: html }],
onState: saveToProtectedStorage,
});A publish grant is single-purpose, short-lived, and constrained by the limits chosen by the issuing Application.
A delegated publish returns record.ownershipState === "platform" and no
updateToken or claim link. A successful publish still clears pending state and
emits the ready progress event. Keep the record to identify the preview; it
does not grant permission to update it.
For the next update, mint a new operation: "update" grant on your server with
previewId: result.record.tempId. Construct a new client with that grant, then
call updatePreview(result.record, { files: nextFiles }). Interrupted uploads
can still use resumePreview(savedState, { files }) with the saved upload
capability. Read status and revoke platform previews through your server using
@tempmd/platform; the browser client's status and revoke methods require a
scoped update token.
Update the same URL
Pass the last finalized record to updatePreview(). Unchanged files are
skipped and the canonical URL remains stable.
const next = await previews.updatePreview(result.record, {
files: nextFiles,
onState: saveToProtectedStorage,
});
console.log(next.record.canonicalUrl === result.record.canonicalUrl); // trueResume an interrupted publish
Persist every value delivered to onState. If a session is interrupted, pass
the last state back to resumePreview() with the same files and settings:
const resumed = await previews.resumePreview(savedState, {
files,
spaMode: true,
onState: saveToProtectedStorage,
});The client validates that the saved session and local bundle still match before continuing.
Status and revoke
const status = await previews.getPreviewStatus(result.record);
await previews.revokePreview(result.record);Errors and cancellation
Use an AbortSignal to cancel a workflow. Validation failures throw
BundleValidationError; HTTP failures from the low-level client throw
ApiError with status, code, optional requestId, and optional
retryAfter fields.
import { ApiError, BundleValidationError } from "@tempmd/client";
try {
await previews.publishPreview({ files, signal: controller.signal });
} catch (error) {
if (error instanceof BundleValidationError) console.error(error.issues);
if (error instanceof ApiError) console.error(error.status, error.code);
}Security requirements
- Treat the entire
PreviewIntegrationStateandPreviewRecordas secrets. - Never put update tokens, upload tokens, or grants in URLs, logs, analytics, or rendered content.
- Do not send cookies or enable credentialed CORS requests.
- Tell users which files will be uploaded, that previews are public by default, and when they expire.
- Persist pending state before uploads begin so interrupted sessions can resume.
Persisted state follows
preview-state-v1.json.
Main exports
TempmdPreviewClient— high-level publish, update, resume, status, and revoke workflow.TempmdClient— low-level publish-session HTTP client.validateBundleandmanifestHash— bundle validation and hashing helpers.ApiErrorandBundleValidationError— structured failures.- Public TypeScript types for files, sessions, progress, records, and state.
See the integration guide and preview infrastructure overview.
