@creativekit/server

中文文档
1.1.0发布于 2026/10/21MITTypeScriptESM + CJS零依赖近 30 天下载 3573

Server-side CreativeKit SDK with OAuth authentication

原始使用文档
文档目录29 节
  1. Quick Start
  2. API Reference
  3. Creatives
  4. Auth (Client Token Generation)
  5. Profiles
  6. Bundles
  7. Health
  8. Utils
  9. Debug
  10. End-to-End Flows
  11. Method → endpoint reference
  12. 1. Browser upload (recommended)
  13. 2. Fully server-side upload (scripts, workers, queues)
  14. 3. Carousel (dynamic slots)
  15. 4. Edit an existing creative (reopen → replace)
  16. 4b. "Edit" a finalized creative (clone → change subset → fi…
  17. 5. Pre-upload validation (fail fast, no upload)
  18. 6. Cancel, retry, delete, list
  19. SSE Events Proxy
  20. Completion Webhooks
  21. Finalize Workflow (Draft → Permanent)
  22. Preview URLs
  23. Handling Failed Creatives
  24. Validation Errors
  25. Pipeline Errors
  26. Express Endpoint Example
  27. Full Documentation
  28. Security
  29. License

@creativekit/server

Server-side CreativeKit SDK with OAuth2 authentication

⚠️ Node.js only - Never expose to browsers


Quick Start

npm install @creativekit/server
import { CreativeKitServer } from "@creativekit/server";

const ck = new CreativeKitServer({
  apiBase: "https://api.adswag.nl",
  clientId: process.env.CREATIVEKIT_CLIENT_ID!,
  clientSecret: process.env.CREATIVEKIT_CLIENT_SECRET!,
});

// Create creative with presigned upload URL (AWS-style)
const creative = await ck.creatives.create("audio:podcast-aac");

// Return to frontend (client uploads directly to presigned URL)
// IMPORTANT: After upload, client must call commit to start processing
res.json({
  creativeId: creative.id,
  uploadUrl: creative.uploadSlots?.[0]?.url,
  uploadHeaders: creative.uploadSlots?.[0]?.headers,
});

// Commit endpoint - triggers processing
await ck.creatives.commit(creative.id);

API Reference

Creatives

// Create creative + get presigned URL (AWS-style)
const creative = await ck.creatives.create(profile, options);
// Options:
//   multipart?: boolean     - Use multipart upload for large files
//   fileSize?: number       - File size (required for multipart)
//   metadata?: object       - Custom metadata to attach
//   autoFinalize?: boolean  - Auto-finalize when processing succeeds (skip draft workflow)

// Commit after upload (starts processing)
await ck.creatives.commit(id);

// Or fill the draft from an https link instead of uploading (scope creative:ingest).
// CreativeKit downloads the file server-side, then continues as if commit() ran.
const status = await ck.creatives.ingest(id, {
  sourceUrl: "https://assets.example.com/spot-30s.mp4",
  // slot?: "video"          - optional when the profile has one required slot
  // idempotencyKey?: string
});
// status.status === "QUEUED"; status.source.status === "PENDING"
// On a failed download the creative ends FAILED; failure names step "source_fetch"
// and status.source.cause gives the reason (HTTP_STATUS, TOO_LARGE, ...).

// Get creative status and artifacts
const creative = await ck.creatives.get(id);
// Status values: "AWAITING_UPLOAD" | "QUEUED" | "PROCESSING" | "SUCCEEDED" | "FAILED" | "CANCELED"
// AWAITING_UPLOAD = created with directUpload, waiting for file upload + commit
// Progress is 0.0-1.0 (multiply by 100 for percentage)

// List creatives with filtering
const list = await ck.creatives.list({ status: "SUCCEEDED", limit: 20 });
// Options: status?, profile?, finalized?, limit?, pageToken?

// Finalize (mark as permanent, prevent auto-cleanup)
await ck.creatives.finalize(id);

// Generate preview URL for completed creative
const preview = await ck.creatives.preview(id);
// Returns: { url: string, expiresAt: number }

// Management
await ck.creatives.delete(id);
await ck.creatives.cancel(id);
await ck.creatives.retry(id);

// Edit a draft: reopen one slot → (client uploads) → replace to reprocess
const { uploadSlots } = await ck.creatives.reopenSlot(id, "frame_2");
await ck.creatives.replaceSlot({ id, slot: "frame_2" }); // file already re-uploaded
// …or do the whole thing server-side in one call (reopen + upload + reprocess):
await ck.creatives.replaceSlot({ id, slot: "frame_2", file: newImage });

// Edit a FINALIZED creative: clone into a fresh draft, change a subset of
// slots, then commit + finalize (the original is never modified).
const draft = await ck.creatives.clone(finalizedId); // copies all raw uploads
// draft.uploadSlots covers every slot; PUT new files only for the changed ones
await ck.creatives.commit(draft.id);
await ck.creatives.finalize(draft.id);

// Server-side upload (Node.js scripts/workers); supports slotCounts + slotTransforms
const result = await ck.creatives.upload({ profile, files });

// SSE proxy to frontend (recommended)
await ck.creatives.getEventsStream(id, res);

Editing is draft-only. reopenSlot/replaceSlot require the creative to be SUCCEEDED or FAILED and not finalized. Reprocessing always re-runs from the original raw uploads, so there is no generational quality loss. See End-to-End Flows.

Auth (Client Token Generation)

Generate short-lived tokens for client-side use without exposing your client secret:

// Generate a scoped access token
const tokenData = await ck.auth.generateToken("creative:create creative:read creative:commit");

// Return to frontend
res.json({
  token: tokenData.accessToken,
  expiresIn: tokenData.expiresIn, // seconds
});

Profiles

// List available processing profiles
const profiles = await ck.profiles.list();
// Returns: Profile[] with id, type, description, etc.

// Pre-validate declared file metadata against a profile WITHOUT uploading.
// Advisory pre-flight check — the authoritative validation still runs on the
// real bytes after upload. Returns the same { errors, warnings } shapes the
// processing pipeline emits, namespaced per slot (e.g. "standard.aspectRatio").
const result = await ck.profiles.validateMetadata("native:image", {
  slots: {
    standard: {
      filename: "banner.png",
      mimeType: "image/png",
      sizeBytes: 1_048_576,
      width: 1200,
      height: 628,
    },
  },
});
// Returns: { passed: boolean, errors: ValidationError[], warnings?: ValidationWarning[] }

// For dynamic-slot profiles (e.g. native:carousel), pass slotCounts so per-frame
// slots (frame_1..frame_N) and slot-group rules resolve:
await ck.profiles.validateMetadata("native:carousel", {
  slotCounts: { frame: 4 },
  slots: { frame_1: { width: 1080, height: 1080, sizeBytes: 500_000 } },
});

Bundles

Bundles group related profiles sold together (e.g. the native ad pack: standard + large).

// List all bundles. Pass { expandProfiles: true } to inline each member's
// full profile spec (upload slots + validation rules) in one request.
const bundles = await ck.profiles.listBundles({ expandProfiles: true });

// Get a single bundle by id (throws ApiError with status 404 if missing)
const bundle = await ck.profiles.getBundle("native:standard", {
  expandProfiles: true,
});

Health

// Comprehensive health check (API + credentials)
const health = await ck.health.check();
// Returns: { isHealthy, apiReachable, credentialsValid, details }

// Basic connectivity test
const reachable = await ck.health.ping();
// Returns: boolean

// Validate OAuth credentials
const validation = await ck.health.validateCredentials();
// Returns: { valid: boolean, error?: string }

Utils

// Check if client has valid credentials configured
const authenticated = ck.utils.isAuthenticated();

// Get current API configuration
const config = ck.utils.getConfig();
// Returns: { apiBase, hasCredentials }

// Get last known connection status
const status = ck.utils.getConnectionStatus();

Debug

Troubleshooting methods for diagnosing authentication issues:

// Validate credentials with detailed error info
const result = await ck.debug.validateCredentials();
// Returns: { valid, error?, details? }

// Test API connection with diagnostics
const conn = await ck.debug.testConnection();
// Returns: { apiReachable, credentialsValid, responseTime?, error?, details? }

// Get current token information
const tokenInfo = await ck.debug.getTokenInfo();
// Returns: { hasCredentials, tokenCached, tokenExpiry?, error? }

End-to-End Flows

Every flow below is copy-pasteable. They cover the full creative lifecycle: create → upload → commit → watch → preview → finalize, plus editing (reopen/replace), multi-slot/carousel, pre-upload validation, and cancel/retry/delete.

Method → endpoint reference

SDK methodREST endpoint
ck.creatives.create(profile, opts)POST /v1/creatives
ck.creatives.commit(id, body?)POST /v1/creatives/{id}/commit
ck.creatives.ingest(id, opts)POST /v1/creatives/{id}/ingest
ck.creatives.upload(params)create + PUT + commit (server-side convenience)
ck.creatives.get(id)GET /v1/creatives/{id}
ck.creatives.list(opts)GET /v1/creatives
ck.creatives.cancel(id) / retry(id)PATCH /v1/creatives/{id}
ck.creatives.delete(id)DELETE /v1/creatives/{id}
ck.creatives.reopenSlot(id, slot)POST /v1/creatives/{id}/slots/{slot}/reopen
ck.creatives.replaceSlot(params)POST /v1/creatives/{id}/slots/{slot}/replace
ck.creatives.preview(id)POST /v1/creatives/{id}/preview
ck.creatives.finalize(id)POST /v1/creatives/{id}/finalize
ck.creatives.clone(id)POST /v1/creatives/{id}/clone
ck.creatives.getEventsStream(id, res)GET /v1/creatives/{id}/events (SSE)
ck.profiles.list(opts?)GET /v1/profiles
ck.profiles.validateMetadata(id, body)POST /v1/profiles/{id}/validate
ck.profiles.listBundles(opts?)GET /v1/bundles
ck.profiles.getBundle(id, opts?)GET /v1/bundles/{id}
ck.auth.generateToken(scopes)POST /oauth/token

The browser uploads directly to storage via presigned URLs; your backend only issues URLs and proxies lifecycle calls. Use this with @creativekit/client.

Backend (Express) — thin proxy:

// Create + return presigned upload slot(s)
app.post("/api/creatives", async (req, res) => {
  const { profile, slotCounts } = req.body;
  const creative = await ck.creatives.create(profile, { slotCounts });
  res.json({ creativeId: creative.id, uploadSlots: creative.uploadSlots });
});

// Commit to start processing (optionally pass slotTransforms for crops)
app.post("/api/creatives/:id/commit", async (req, res) => {
  await ck.creatives.commit(req.params.id, { slotTransforms: req.body?.slotTransforms });
  res.status(204).send();
});

// Status + live events + finalize
app.get("/api/creatives/:id", async (req, res) => res.json(await ck.creatives.get(req.params.id)));
app.get("/api/creatives/:id/events", (req, res) => ck.creatives.getEventsStream(req.params.id, res, { corsOrigin: req.headers.origin }));
app.post("/api/creatives/:id/finalize", async (req, res) => { await ck.creatives.finalize(req.params.id); res.status(204).send(); });

The browser side (client.upload(...), client.streamEvents(...)) is in the @creativekit/client README.

2. Fully server-side upload (scripts, workers, queues)

ck.creatives.upload() does create → PUT each file → commit → wait, all in Node. Pass slotCounts for dynamic profiles and slotTransforms for crops.

import { readFile } from "node:fs/promises";

// Single-file profile
const audio = await ck.creatives.upload({
  profile: "audio:podcast-aac",
  files: { audio: new File([await readFile("ep.mp3")], "ep.mp3", { type: "audio/mpeg" }) },
  // `progress` (0-100) is the canonical progress field across CreativeKit
  // SDKs (the client SDK's `percent` is a deprecated alias of it).
  onProgress: (p) => console.log(`${p.stage} ${p.progress}%`),
});

// Native ad pack (multi-slot): "standard" + "large" are sold as one bundle.
// Each slot is a separate file; optionally crop each to its target.
const native = await ck.creatives.upload({
  profile: "native:image",
  files: { standard: standardFile, large: largeFile },
  slotTransforms: {
    // crop rect is in SOURCE pixels; target is the output resolution after crop+scale
    standard: { aspectRatio: "1.91:1", cropX: 0, cropY: 0, cropW: 1200, cropH: 628, targetWidth: 1200, targetHeight: 628 },
  },
});

if (native.status === "SUCCEEDED") console.log(native.artifacts);

Carousels declare a frame template slot. slotCounts: { frame: N } expands it into frame_1 … frame_N. Works the same for server-side upload or presigned browser upload (pass slotCounts to create).

const carousel = await ck.creatives.upload({
  profile: "native:carousel",
  slotCounts: { frame: 4 },
  files: { frame_1: f1, frame_2: f2, frame_3: f3, frame_4: f4 },
});

Changing one frame later re-runs the whole pipeline (all frames re-encode from their raw uploads). See flow 4.

4. Edit an existing creative (reopen → replace)

Swap a single slot/frame of a draft (non-finalized) creative without recreating it. The creative must be SUCCEEDED or FAILED, and the other slots' raw uploads must still be within the draft TTL.

Option A — fully server-side (one call):

const updated = await ck.creatives.replaceSlot({
  id: creativeId,
  slot: "frame_2",
  file: newFrameFile,           // reopen + upload + reprocess + wait
  slotTransform: { aspectRatio: "1:1", cropX: 0, cropY: 0, cropW: 1080, cropH: 1080, targetWidth: 1080, targetHeight: 1080 }, // optional
});

Option B — browser-driven (presigned):

// 1) Backend reopens the slot and returns fresh presigned URLs
app.post("/api/creatives/:id/slots/:slot/reopen", async (req, res) => {
  const { uploadSlots } = await ck.creatives.reopenSlot(req.params.id, req.params.slot);
  res.json({ uploadSlots });
});

// 2) Browser PUTs the new file to that URL (client.replaceSlot handles this)

// 3) Backend triggers reprocessing (no file arg → just reprocess)
app.post("/api/creatives/:id/slots/:slot/replace", async (req, res) => {
  await ck.creatives.replaceSlot({
    id: req.params.id,
    slot: req.params.slot,
    slotTransform: req.body?.slotTransform, // optional crop from the browser
  });
  res.status(204).send();
});

What can be edited? Only slot contents of a draft (re-upload a file to a slot). Profile/type are fixed at create time. Once finalized, a creative is immutable — to change it, clone it (flow 4b). Public GET /c/{id}/{filename} only serves finalized creatives, so live assets never change underneath a viewer.

4b. "Edit" a finalized creative (clone → change subset → finalize)

Finalized creatives are immutable, so editing one means producing a new creative. clone makes that cheap: it duplicates the source into a fresh draft (new ID, AWAITING_UPLOAD) and server-side copies every raw upload, so the unchanged slots need no re-upload and reprocess losslessly from the original source. Change only the slots you want, then commit + finalize.

// 1) Clone the finalized creative → new draft seeded with all original frames
app.post("/api/creatives/:id/clone", async (req, res) => {
  const draft = await ck.creatives.clone(req.params.id);
  // draft.uploadSlots covers EVERY slot (e.g. frame_1..frame_8)
  res.status(201).json({ id: draft.id, uploadSlots: draft.uploadSlots });
});

// 2) Browser PUTs new files ONLY for the changed slots (e.g. 4 of 8 frames)
//    to their presigned URLs. The other slots keep the copied raw uploads.

// 3) Commit reprocesses all slots from raw (one clean pass), then finalize.
await ck.creatives.commit(draftId);
await ck.creatives.finalize(draftId);
// Result: a brand-new finalized creative (new ID/URL). The original is untouched;
// the platform should swap its reference to the new creative.

To remove or reorder carousel slides (not just swap file contents), pass slotPlan on clone:

// 5-frame source → 3-frame draft (drops frame_2 and frame_4)
const draft = await ck.creatives.clone(finalizedId, {
  slotPlan: { frame: ["frame_1", "frame_3", "frame_5"] },
});
// draft.uploadSlots = frame_1..frame_3, seeded from source frame_1, frame_3, frame_5

Clone relies on the source still having its raw uploads (finalized creatives retain them). If they are gone, clone returns 409 — create a fresh creative instead.

5. Pre-upload validation (fail fast, no upload)

Check declared metadata against a profile's rules before pushing bytes. Advisory only — the authoritative check still runs on the real bytes at commit.

const check = await ck.profiles.validateMetadata("native:image", {
  slots: { standard: { mimeType: "image/png", sizeBytes: 1_048_576, width: 1200, height: 628 } },
});

if (!check.passed) {
  // surface check.errors to the user and skip the upload
  return res.status(422).json({ errors: check.errors, warnings: check.warnings });
}
// else proceed with create/upload

6. Cancel, retry, delete, list

await ck.creatives.cancel(id);        // stop in-flight processing
await ck.creatives.retry(id);         // retry a FAILED creative
await ck.creatives.delete(id);        // remove a creative + its artifacts

// Paginate finalized creatives for a profile
let pageToken: string | undefined;
do {
  const page = await ck.creatives.list({ profile: "native:image", finalized: true, limit: 50, pageToken });
  page.items.forEach((c) => console.log(c.id, c.status));
  pageToken = page.nextPageToken;
} while (pageToken);

SSE Events Proxy

Proxy real-time processing events to your frontend with a single method call:

// NestJS
@Get(':id/events')
async streamEvents(@Param('id') id: string, @Res() res: Response) {
  await ck.creatives.getEventsStream(id, res);
}

// Express
app.get('/creatives/:id/events', async (req, res) => {
  await ck.creatives.getEventsStream(req.params.id, res);
});

The SDK handles:

  • ✅ SSE headers (Content-Type, Cache-Control, Connection)
  • ✅ Terminal state detection (auto-closes on SUCCEEDED, FAILED, CANCELED)
  • ✅ Automatic stream cleanup
  • ✅ Client disconnect handling
  • ✅ CORS headers (optional)

With CORS support and callbacks:

await ck.creatives.getEventsStream(id, res, {
  corsOrigin: req.headers.origin,
  onComplete: (status) => console.log(`Creative ${id}: ${status}`),
  onError: (error) => console.error(`Stream error: ${error.message}`),
});

Note: SSE events are minimal signals (id, status, progress). Clients should call GET /creatives/:id for full details on terminal states.


Completion Webhooks

Instead of polling or SSE, register a webhook at create time. The backend POSTs to your callbackUrl when the creative reaches a terminal status (SUCCEEDED / FAILED / CANCELED), signed with your callbackSecret.

const creative = await ck.creatives.create("video:web-1080p-h264", {
  callbackUrl: "https://your-backend.example.com/webhooks/creativekit",
  callbackSecret: process.env.CREATIVEKIT_WEBHOOK_SECRET!, // required with callbackUrl
});

callbackSecret is required whenever callbackUrl is set — the API rejects unsigned webhook registrations (400).

Receiving webhooks — verify the X-CreativeKit-Signature header (hex HMAC-SHA256 over the raw body) with the SDK helpers:

import express from "express";
import { parseWebhookEvent, WEBHOOK_SIGNATURE_HEADER } from "@creativekit/server";

// Use a raw body parser: the signature covers the exact bytes received.
app.post(
  "/webhooks/creativekit",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;
    try {
      event = parseWebhookEvent(req.body, {
        secret: process.env.CREATIVEKIT_WEBHOOK_SECRET!,
        signature: req.header(WEBHOOK_SIGNATURE_HEADER),
      });
    } catch {
      return res.sendStatus(401);
    }

    // Delivery is at-least-once — dedupe on event.eventId.
    // The payload is minimal ({ eventId, id, status, error }); on SUCCEEDED,
    // fetch the creative for artifacts and CDN URLs:
    if (event.status === "SUCCEEDED") {
      const creative = await ck.creatives.get(event.id);
      // ...store creative.artifacts...
    }

    res.sendStatus(204);
  }
);

verifyWebhookSignature(secret, rawBody, signature) is also exported if you only want the boolean check. Deliveries are retried with exponential backoff (~10 attempts) on 5xx/network errors; 2xx and 4xx both count as delivered, so reconcile terminal states with GET /v1/creatives/{id} as a fallback.


Finalize Workflow (Draft → Permanent)

Processed creatives start as "drafts" that can be previewed but will be automatically cleaned up after the draft TTL expires (default: 24h). To keep a creative permanently:

// After user reviews the preview and confirms
await ck.creatives.finalize(creativeId);

// Check finalization status
const creative = await ck.creatives.get(creativeId);
console.log(creative.finalized); // true
console.log(creative.finalizedAt); // timestamp in ms

Why finalize?

  • Cost control: Storage costs only for confirmed creatives
  • User confirmation: Let users preview before committing
  • Idempotent: Safe to call multiple times

Skip draft workflow with autoFinalize:

If you don't need user preview confirmation, auto-finalize on success:

const creative = await ck.creatives.create("video:web-1080p-h264", {
  autoFinalize: true, // Automatically finalized when processing succeeds
});

Express endpoint example:

app.post("/creatives/:id/finalize", async (req, res) => {
  await ck.creatives.finalize(req.params.id);
  res.status(204).send();
});

Preview URLs

Generate signed preview URLs for completed creatives:

// Get a signed preview URL (expires after a period)
const { url, expiresAt } = await ck.creatives.preview(creativeId);

// Return to frontend for display
res.json({ previewUrl: url, expiresAt });

Express endpoint example:

// Note: The SDK uses POST internally to generate the preview URL
app.post("/creatives/:id/preview", async (req, res) => {
  const preview = await ck.creatives.preview(req.params.id);
  res.json(preview);
});

Handling Failed Creatives

When a creative fails, check both results.validation (for validation errors) and failure (for pipeline errors):

Validation Errors

Detailed validation errors are in results.validation.errors:

const creative = await ck.creatives.get(id);

if (creative.status === "FAILED" && creative.results?.validation) {
  console.log("Validation failed:");
  for (const error of creative.results.validation.errors) {
    console.log(`  ${error.field}: ${error.message}`);
    console.log(`    Expected: ${error.expected}, Got: ${error.actual}`);
    console.log(`    Code: ${error.code}`);
  }
}

Validation Error Fields:

FieldDescription
fieldProperty that failed (e.g., aspectRatio, duration)
ruleValidation rule violated (e.g., allowedValues, range)
expectedWhat the profile requires
actualWhat was found in the uploaded file
codeMachine-readable error code
messageHuman-readable error message
profileHintReference to the profile constraint

Pipeline Errors

For unrecoverable errors (transcode failures, etc.), check failure:

if (creative.status === "FAILED" && creative.failure) {
  console.log(`Pipeline failed at step: ${creative.failure.step}`);
  console.log(`Error: ${creative.failure.message}`);
  console.log(`Retriable: ${creative.failure.retriable}`);
}

Express Endpoint Example

app.get("/api/creatives/:id", async (req, res) => {
  const creative = await ck.creatives.get(req.params.id);

  // Return validation errors in a client-friendly format
  if (creative.status === "FAILED") {
    const validationErrors = creative.results?.validation?.errors || [];
    const pipelineError = creative.failure;

    return res.json({
      ...creative,
      // Flatten for easier client consumption
      errorSummary: validationErrors.length
        ? validationErrors.map((e) => e.message).join("; ")
        : pipelineError?.message || "Unknown error",
    });
  }

  res.json(creative);
});

Full Documentation

See the main SDK documentation for:

  • Complete API reference
  • Architecture patterns
  • Full examples (Express, Next.js, NestJS)
  • Security best practices
  • Troubleshooting

Security

  • ✅ Use environment variables for credentials
  • ✅ Never expose this SDK to browsers
  • ✅ Keep clientSecret secure
  • ✅ Add auth middleware to your backend

License

MIT

版本与发布记录

@creativekit/server 共发布 40 个版本,首次发布于 2025/11/12;过去 12 个月发布了 40 个版本。

版本发布日期距今
1.1.0latest2026/10/2120 天前
1.0.422026/08/112 个月前
1.0.412026/07/153 个月前
1.0.402026/07/034 个月前
1.0.392026/07/034 个月前
1.0.382026/06/304 个月前

关于 @creativekit/server 的常见问题

@creativekit/server 是什么?

Server-side CreativeKit SDK with OAuth authentication

如何安装 @creativekit/server?

在项目目录运行 npm install @creativekit/server,或使用 pnpm add / yarn add / bun add。

@creativekit/server 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@creativekit/server 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@creativekit/server 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@creativekit/server 可以商用吗?

@creativekit/server 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@creativekit/server 的下载量有多少?

根据 npm 官方下载统计,@creativekit/server 在 2026/10/11 – 2026/11/09 这 30 天内被下载 3,573 次,日均约 119 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@creativekit/server 还在维护吗?

最新版本 1.1.0 发布于 2026/10/21(20 天前);至今共发布 40 个版本,其中过去 12 个月发布了 40 个;首次发布于 2025/11/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。