npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yichong6/canva-core

v0.1.0-alpha.6

Published

YiChong Canva 应用的公司后端 SDK:账户、额度、R2 上传、模型任务与文件工具;不依赖 React 和 @canva/*

Readme

@yichong6/canva-core

YiChong Canva 应用共用的浏览器 SDK,只封装公司自己的后端。应用创建一个客户端,账户、额度、R2 上传、文本审核与图像/视频任务共用同一份身份。

本包不依赖 React,也不依赖任何 @canva/* 包:

  • 插入设计、读取选中内容、打开外链等 Canva 能力,应用直接调用 Canva 官方 SDK。
  • 额度 footer、错误提示等界面组件放在应用自己的代码里;本包提供它们需要的数据和纯函数(deriveQuota、formatQuotaCount、buildPaymentUrl、canShowPaymentLink、getUserFacingErrorMessage)。
  • 登录需要的 Canva 用户令牌由应用传入 account.getCanvaUserToken。

安装与授权

公开 npm 发布完成后,已获 YiChong 使用授权的开发者可以直接安装,无需 npm 私有包读取 Token:

npm install --save-exact @yichong6/[email protected] --registry=https://registry.npmjs.org/

当前仓库已完成 public / alpha 发布配置;该配置不代表版本已上传。版本是否可下载以 npm registry 为准。公开分发保留版权,license: UNLICENSED,具体见 版权说明。下载包不授予公司后端访问权限;每个应用仍需自己的 App ID、已登记渠道及运行时账户授权。浏览器中不得配置服务端密钥。

仓库内联调可以继续用 file:../../package/core。在 core 目录执行:

npm ci
npm run check:release
npm pack

check:release 依次执行 TypeScript 检查、干净 ESM/CJS 构建、测试和实际 tarball 的离线安装、入口及类型消费者验证。lint 当前是 TypeScript 检查的别名,不表示执行了 ESLint。prepack 和源码目录发布的 prepublishOnly 都运行完整门禁。

包内只分发构建产物、类型声明、README 和许可证;不带源码映射、测试、开发脚本或凭据配置。JavaScript 仍然可读,这不构成代码保密措施。直接发布 .tgz 不会重跑仓库中的检查脚本,必须使用已经完整验证的同一压缩包。正式发布前先完成真实渠道联调;发布流程见源码仓库 docs/Canva-npm公开发布.md。

应用里的基本流程

import { auth } from "@canva/user";
import { createCanvaClient } from "@yichong6/canva-core";

declare const file: File; // 用户在界面中选择的文件
declare function addImageToDesign(options: { url: string }): Promise<void>; // 应用自己的代码,内部直接用 @canva/asset 和 @canva/design

const client = createCanvaClient({
  appId: "你的 Canva App ID",
  appChannel: "后端已登记的产品渠道",
  baseUrl: "https://api.imgkits.com",
  account: { getCanvaUserToken: () => auth.getCanvaUserToken() },
  getLocale: () => "zh-CN",
});

await client.account.login();

const uploaded = await client.uploads.uploadImage(file);
const job = await client.jobs.create({
  protocol: "legacy",
  owner: "openai",
  name: "gpt-image-2",
  input: { prompt: "一只猫", image_input: [uploaded.url] },
});
const result = await client.jobs.wait(job, {
  pollIntervalMs: 3000,
  timeoutMs: 180000,
  onUpdate: (status) => console.log(status.status),
});

// output 的形状由模型协议决定。这里仅演示返回单张图片 URL 的模型。
if (typeof result.output !== "string") throw new Error("模型返回了意外的结果形状");
await addImageToDesign({ url: result.output });

如结果 URL 是临时地址,先用 client.uploads.uploadFromUrl(url) 转成稳定 R2 地址,再交给应用用 Canva 官方 SDK 插入设计。不同模型的输入、输出与计价仍由应用和后端目录确定;SDK 不猜测模型参数或把所有输出当图片。

对供应商临时图片的网络读取,可以显式开启有限重试:

const saved = await client.uploads.uploadFromUrl(imageUrl, {
  fileName: "cover.png",
  acceptedTypes: ["image/png", "image/jpeg", "image/webp"],
  download: { maxAttempts: 3, retryDelayMs: 750 },
  timeoutMs: 120_000,
});

maxAttempts 包含第一次,默认 1、范围 1–5;retryDelayMs 默认 750,范围 0–60000ms。只重试 GET 网络/正文读取错误和 HTTP 408、429、5xx;不重试 404、大小/类型校验、主动取消或总超时。失败正文从零重下。下载、间隔、签名和 PUT 共用外层总超时,但签名/PUT 本身不自动重试。独立 downloadFile 直接接收 maxAttempts、retryDelayMs。此选项不重建模型任务,也不重复消费。

文本内容审核

复用当前账户的业务 JWT,调用 client.moderation.check({ text, scene: "default" }, { signal, timeoutMs: 30_000 })。scene 可省略。返回 decision: "pass" | "review" | "block",以及 requestId、categories、flaggedCategories、policyVersion、model、durationMs。包只封装接口,是否继续生成由应用决定;封面应用仅 pass 继续。

公开入口为 ${baseUrl}/api/public/text-moderation/check,baseUrl 配网关根地址。无需新前端密钥或 Canva 权限,不用内部服务 Bearer。输入必须完整且同时不超过 1024 码点和 1024 UTF-8 字节;moderationTextLimits、measureModerationText(text) 可供页面校验。SDK 不截断、拆段或缓存通过结果。

失败抛 ApiError,保留 code/status/requestId/userMessage,并提供可选 retryable/retryAfterSeconds;取消为 AbortError。每次只请求一次,不自动重试,不调用生成或扣费。后端初始策略质量与真实部署待验收;该独立接口不代表生成端点已强制审核。完整合同见源码仓库 docs/Canva-文本审核接入.md。

任务协议

| protocol | 创建端点 | 查询端点 | 适用场景 | | --- | --- | --- | --- | | legacy | /replicate/createSpecifyPrediction | /replicate/getPrediction | 当前多数图像、视频应用;视频目录的 provider_requests 也走这里 | | legacy-version | /replicate/createPrediction | /replicate/getPrediction | 需要 version hash 的存量模型 | | catalog-image | /replicate/ai-image/create | /replicate/ai-image/status | 图像目录的 catalogModelId / mode / selection | | catalog-video | /replicate/ai-video/create | /replicate/ai-video/status | 视频目录新意图接口 |

jobs.create() 只在后端返回任务 ID 时成功。jobs.wait() 轮询到首次成功立即停止;失败终态抛 ApiError。AbortSignal 只取消本地等待或请求,不代表后端任务已取消。创建请求不会自动重试,避免付费任务重复创建。

账户和额度由 usa 管理。SDK 的 quota.check(cost) 只是界面预检,成本由业务给出;后端仍须独立校验身份、权限和计费。浏览器包里不得放服务端密钥。

显式消费积分

已完成的任务可以调用:

const receipt = await client.quota.consume(1, {
  taskId: `${job.protocol}:${job.id}`,
});
// receipt.refresh === "failed" 只表示余额读取失败,消费已确认。
// 可以单独调用 account.refresh();不要重新调用模型,也不要换 taskId 再扣一次。

调用前必须登录,并按产品约定确认任务成功及结果可用。这里的 1 是示例产品单价,不是包的默认价格。taskId 是必填的本实例防重复标识,不会作为幂等字段发送给不支持它的 usa 旧端点。

| 账户/配置 | usa 操作 | | --- | --- | | 非订阅、server_credits 且 free_cnt 为 null | add-free-usage-cnt,增加免费已用次数 | | 非订阅、server_credits 数值余额,或旧 normal 模式 | reduce-free-cnt,余额减去本次用量;normal 保留负数试用偏移 | | 有订阅 | 优先 add-monthly-usage,不足部分 reduce-free-cnt | | 免费无限 | 不记账;订阅无限仍累计月度用量 |

消费请求使用当前渠道及闭包内的 passport,必须收到 HTTP 成功且 ok: 0,然后刷新账户。数量必须为正,余额/月度支持最多两位小数,免费已用量支持一位;拒绝会被服务端舍入掉的数量。

同实例、同用户、同 taskId 的并发或重复调用复用同一个 Promise,成本不同则拒绝。不同任务同时消费会返回 quota_consumption_pending。失败返回 QuotaConsumptionError,区分明确拒绝、结果未知和拆分后的部分成功;confirmedCost 只表示已经明确成功的部分。失败不自动重试,账户预检会暂停新消费,普通余额刷新不能证明某个任务已记账,也不会清除这个状态。

**边界:**这不是服务端消费流水。实例重建、页面刷新、多个标签页不能靠内存锁去重。拆分消费也不是后端事务。正式上线前仍须由 usa/external_api 校验额度、任务归属,提供按任务只记账一次和状态查询能力;前端预检和任务 ID 不能替代这些校验。

目前 apps/demo 只接入账户和额度,尚未配置 baseUrl 或生成页面。接入新应用时应先用一个真实模型完成上传、创建、查询、结果插入 Canva 的预览验证。