@clipboard-health/contract-core

中文文档
4.6.15发布于 2026/11/05MITTypeScriptESM + CJS近 30 天下载 12.4万

为剪贴板的合同共享Zod计划.

中文使用文档
文档目录5 节
  1. 目录
  2. 安装
  3. 使用量
  4. Zod 计划
  5. 地方发展指挥部

@ 剪贴板- 健康/ 合同核心

为剪贴板的合同共享Zod计划.

目录

安装

npm install @clipboard-health/contract-core

使用量

Zod 计划

逗号分隔阵列计划

commaSeparatedArray(itemSchema) 验证以逗号分隔的字符串或数组输入,并将其规范化为已键入数组。 为共享合同所设计,服务器接收逗号分隔的查询字符串,客户端通过打入的数组.

// z.input  → string | string[]
// z.output → string[]
commaSeparatedArray(nonEmptyString).optional();

// z.input  → string | ("CNA" | "RN" | "LVN")[]
// z.output → ("CNA" | "RN" | "LVN")[]
commaSeparatedArray(requiredEnum(["CNA", "RN", "LVN"]));

// z.input  → string | string[]
// z.output → string[]  (each validated as ObjectId)
commaSeparatedArray(objectId);

// z.input  → string | (string | Date)[]
// z.output → Date[]
commaSeparatedArray(dateTimeSchema());

由所有合同核心计划 和enum助手组成 替换 z.preprocess(splitString, z.array(...)) 与适当的 z.input 输入 splitString 模式去除输入类型到 unknown) (中文(简体) ).

日期时间计划

dateTimeSchema() 验证严格的 ISO-8601 日期时间字符串并将其转换为 Date 对象。 与 z.coerce.date(),它拒绝像时代数字和只使用日期字符串这样的松散输入。 可用 .optional(), (中文(简体) ), .nullable()等在呼叫现场.

歧视性结合与倒退

discriminatedUnionWithFallback(discriminator, variants) 使用与 z.discriminatedUnion 并返回一个响应计划 对于一个联盟,它的变体 设定随着时间的推移增长, 所以读容忍变体 这个消费者尚不知道.

它只用于答复。 要求宣布自己 z.discriminatedUnion 在同一个变体上,一个未知变体是一个客户端错误而不是可以容忍的东西.

z.discriminatedUnion (请求)discriminatedUnionWithFallback (答复)
未知歧视者拒绝崩溃后退
已知变体上未知的顶级密钥脱去脱去
缺少字段, 无效值拒绝拒绝

z.union 不能表达这一点。 它的倒置分枝匹配任何未知值, 所以一个已知的变体如果字段不好, 每个分枝都会失败, 并报告不透明 invalid_union 每个分支都有一个嵌入错误。 重写未知的歧义者首先意味着反应通常带有歧视性,并且只报告违法领域的问题,因此拒绝的反应可以从日志中诊断出来.

Enum 验证助手

这个软件包提供四个enum验证帮助,以覆盖不同的使用大小写:

退后验证( 有连接) :

  • requiredEnumWithFallback(values) - 无效的价值观被强迫 ENUM_FALLBACK (单位:千美元)"UNRECOGNIZED_"),一种商业文本中性的哨兵,自动附在enum类型上. undefined 无法验证 .
  • optionalEnumWithFallback(values) - 无效的价值观被强迫 ENUM_FALLBACK (单位:千美元)"UNRECOGNIZED_") (中文(简体) ). undefined 通过时为 undefined。 。 。 .

严格验证( 无倒计时 ) :

  • requiredEnum(values) - 包装 z.enum() 需要严格验证。 无效的值失败验证 .
  • optionalEnum(values) - 包装 z.enum() 可选的严格验证。 无效的值失败验证, 但是 undefined 允许.

缩小类型 : 所有帮助者拒绝加宽 string[] 编译时的数组。 当通过一个预先宣布的变量时,使用 as const 以保留文字类型:

// Inline arrays work as-is
requiredEnum(["a", "b"]);

// Pre-declared variables require `as const`
const VALUES = ["a", "b"] as const;
requiredEnum(VALUES);

// Without `as const`, the type widens to string[] and is rejected
const widened = ["a", "b"];
requiredEnum(widened); // TS error
import {
  apiErrors,
  booleanString,
  commaSeparatedArray,
  dateTimeSchema,
  discriminatedUnionWithFallback,
  ENUM_FALLBACK,
  nonEmptyString,
  objectId,
  optionalEnum,
  optionalEnumWithFallback,
  requiredEnum,
  requiredEnumWithFallback,
  uuid,
} from "@clipboard-health/contract-core";
import { z, type ZodError } from "zod";

function logError(error: unknown) {
  console.error((error as ZodError).issues[0]!.message);
}

apiErrors.parse({
  errors: [
    {
      code: "NotFound",
      detail: "Resource 'b146a790-9ed1-499f-966d-6c4905dc667f' not found",
      id: "6191a8a0-96ff-4d4b-8e0f-746a5ab215f9",
      status: "404",
      title: "Not Found",
    },
  ],
});

// Comma-separated array examples
// Designed for shared contracts: server receives "CNA,RN" (string), client passes ["CNA", "RN"] (array).
// Both normalize to the same typed array output.
const workerTypes = commaSeparatedArray(requiredEnum(["CNA", "RN", "LVN"]));

// Server-side: comma-separated string from query params
const fromString = workerTypes.parse("CNA,RN");
// => ["CNA", "RN"]
console.log(fromString);

// Client-side: typed array
const fromArray = workerTypes.parse(["CNA", "LVN"]);
// => ["CNA", "LVN"]
console.log(fromArray);

// Composes with other schemas
const workerIds = commaSeparatedArray(objectId).optional();
const dates = commaSeparatedArray(dateTimeSchema());

// Works with .optional()
const noIds = workerIds.parse(undefined);
// => undefined
console.log(noIds);

const someDates = dates.parse("2026-01-01T00:00:00.000Z,2026-01-02T00:00:00.000Z");
// => [Date, Date]
console.log(someDates[0] instanceof Date); // true

booleanString.parse("true");

try {
  booleanString.parse("invalid");
} catch (error) {
  logError(error);
  // => Invalid enum value. Expected 'true' | 'false', received 'invalid'
}

nonEmptyString.parse("hello");
try {
  nonEmptyString.parse("");
} catch (error) {
  logError(error);
  // => String must contain at least 1 character(s)
}

// UUID validation examples
uuid.parse("b8d617bb-edef-4262-a6e3-6cc807fa1b26");
try {
  uuid.parse("invalid");
} catch (error) {
  logError(error);
  // => Invalid UUID format
}

// DateTime schema examples
// Accepts ISO-8601 datetime strings and Date objects, normalizes to Date.
// Composable with .optional(), .nullable(), etc.
const createdAt = dateTimeSchema().parse("2026-03-15T10:30:00.000Z");
// => Date object
console.log(createdAt instanceof Date); // true
console.log(createdAt.toISOString()); // "2026-03-15T10:30:00.000Z"

// Date objects pass through as-is
const fromDate = dateTimeSchema().parse(new Date("2026-03-15T10:30:00.000Z"));
// => Date object
console.log(fromDate instanceof Date); // true

try {
  dateTimeSchema().parse("2026-03-15"); // date-only string
} catch (error) {
  logError(error);
  // => Invalid datetime
}

try {
  dateTimeSchema().parse(1_773_340_050_000); // epoch number
} catch (error) {
  logError(error);
  // => Invalid union
}

// Optional usage — compose at the call site
const schema = dateTimeSchema().optional();
const noDate = schema.parse(undefined);
// => undefined
console.log(noDate);

const someDate = schema.parse("2026-03-15T10:30:00.000Z");
// => Date object
console.log(someDate);

// Enum with fallback examples
// ENUM_FALLBACK is a neutral sentinel ("UNRECOGNIZED_") automatically appended
// to the enum type. Consumers cannot choose their own fallback value, preventing
// misuse where a business-meaningful value is treated as a default.

/* -- required -- */
const requiredStatusEnumSchema = requiredEnumWithFallback(["pending", "completed", "failed"]);
// type RequiredStatusEnum = "pending" | "completed" | "failed" | "UNRECOGNIZED_"
type RequiredStatusEnum = z.infer<typeof requiredStatusEnumSchema>;

const completedStatus: RequiredStatusEnum = requiredStatusEnumSchema.parse("completed");
// => "completed"
console.log(completedStatus);

const additionalStatus = requiredStatusEnumSchema.parse("additional");
// => "UNRECOGNIZED_" (ENUM_FALLBACK)
console.log(additionalStatus);
console.log(additionalStatus === ENUM_FALLBACK); // true

try {
  requiredStatusEnumSchema.parse(undefined);
} catch (error) {
  logError(error);
  // => Validation error
}

/* -- optional -- */
const optionalStatusEnumSchema = optionalEnumWithFallback(["pending", "completed", "failed"]);
// type OptionalStatusEnum = "pending" | "completed" | "failed" | "UNRECOGNIZED_" | undefined
type OptionalStatusEnum = z.infer<typeof optionalStatusEnumSchema>;

const failedStatus: OptionalStatusEnum = optionalStatusEnumSchema.parse("failed");
// => "failed"
console.log(failedStatus);

const extraStatus = optionalStatusEnumSchema.parse("extra");
// => "UNRECOGNIZED_" (ENUM_FALLBACK)
console.log(extraStatus);

const undefinedStatus = optionalStatusEnumSchema.parse(undefined);
// => undefined
console.log(undefinedStatus);

// Strict enum examples (no fallback behavior)
// Use these when invalid values should fail validation rather than being coerced.
// Ideal for user types, authentication, and critical business logic.

/* -- required strict -- */
const requiredUserTypeSchema = requiredEnum(["admin", "worker", "workplace"]);
// type RequiredUserType = "admin" | "worker" | "workplace"
type RequiredUserType = z.infer<typeof requiredUserTypeSchema>;

const adminUser: RequiredUserType = requiredUserTypeSchema.parse("admin");
// => "admin"
console.log(adminUser);

try {
  requiredUserTypeSchema.parse("invalid");
} catch (error) {
  logError(error);
  // => Invalid enum value. Expected 'admin' | 'worker' | 'workplace', received 'invalid'
}

try {
  requiredUserTypeSchema.parse(undefined);
} catch (error) {
  logError(error);
  // => Required
}

/* -- optional strict -- */
const optionalUserTypeSchema = optionalEnum(["admin", "worker", "workplace"]);
// type OptionalUserType = "admin" | "worker" | "workplace" | undefined
type OptionalUserType = z.infer<typeof optionalUserTypeSchema>;

const workerUser: OptionalUserType = optionalUserTypeSchema.parse("worker");
// => "worker"
console.log(workerUser);

const noUserType = optionalUserTypeSchema.parse(undefined);
// => undefined
console.log(noUserType);

try {
  optionalUserTypeSchema.parse("invalid");
} catch (error) {
  logError(error);
  // => Invalid enum value. Expected 'admin' | 'worker' | 'workplace', received 'invalid'
}

// Discriminated union with fallback examples
// Takes the same arguments as z.discriminatedUnion and returns a response schema
// that tolerates variants this consumer does not know yet.
const notificationVariants = [
  z.object({
    channel: z.literal("EMAIL"),
    emailAddress: z.string().email(),
  }),
  z.object({
    channel: z.literal("SMS"),
    phoneNumber: nonEmptyString,
  }),
] as const;

const notificationResponseSchema = discriminatedUnionWithFallback("channel", notificationVariants);

const emailNotification = notificationResponseSchema.parse({
  channel: "EMAIL",
  emailAddress: "[email protected]",
});
// => { channel: "EMAIL", emailAddress: "[email protected]" }
console.log(emailNotification);

// A variant this consumer does not recognize yet collapses to ENUM_FALLBACK.
const unrecognizedNotification = notificationResponseSchema.parse({
  channel: "PUSH",
  deviceToken: "abc123",
});
// => { channel: "UNRECOGNIZED_" }
console.log(unrecognizedNotification);

// A known variant with a bad field still fails, naming the field.
try {
  notificationResponseSchema.parse({ channel: "EMAIL", emailAddress: 3 });
} catch (error) {
  logError(error);
  // => Expected string, received number
}

// Requests use z.discriminatedUnion directly: an unknown variant is a client
// error, not something to tolerate.
const notificationRequestSchema = z.discriminatedUnion("channel", [...notificationVariants]);

try {
  notificationRequestSchema.parse({ channel: "PUSH", deviceToken: "abc123" });
} catch (error) {
  logError(error);
  // => Invalid discriminator value. Expected 'EMAIL' | 'SMS'
}

地方发展指挥部

见 package.json scripts 用于命令列表.

版本与发布记录

@clipboard-health/contract-core 共发布 490 个版本,首次发布于 2024/10/23;过去 12 个月发布了 391 个版本。

版本发布日期距今
4.6.15latest2026/11/054 天前
4.6.142026/11/054 天前
4.6.132026/11/045 天前
4.6.122026/10/3110 天前
4.6.112026/10/2812 天前
4.6.102026/10/2813 天前

关于 @clipboard-health/contract-core 的常见问题

@clipboard-health/contract-core 是什么?

为剪贴板的合同共享Zod计划.

如何安装 @clipboard-health/contract-core?

在项目目录运行 npm install @clipboard-health/contract-core,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:zod(^3)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@clipboard-health/contract-core 支持 TypeScript 吗?

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

@clipboard-health/contract-core 支持 ESM 还是 CommonJS?

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

@clipboard-health/contract-core 可以商用吗?

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

@clipboard-health/contract-core 的下载量有多少?

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

@clipboard-health/contract-core 还在维护吗?

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