@clipboard-health/contract-core 2.3.47 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,8 +29,8 @@ This package provides four enum validation helpers to cover different use cases:
29
29
 
30
30
  **Fallback validation (with coalescing):**
31
31
 
32
- - `requiredEnumWithFallback(values, fallback)` - Invalid values are coerced to the fallback value. `undefined` fails validation.
33
- - `optionalEnumWithFallback(values, fallback)` - Invalid values are coerced to the fallback value. `undefined` passes through as `undefined`.
32
+ - `requiredEnumWithFallback(values)` - Invalid values are coerced to `ENUM_FALLBACK` (`"UNRECOGNIZED_"`), a business-context-neutral sentinel automatically appended to the enum type. `undefined` fails validation.
33
+ - `optionalEnumWithFallback(values)` - Invalid values are coerced to `ENUM_FALLBACK` (`"UNRECOGNIZED_"`). `undefined` passes through as `undefined`.
34
34
 
35
35
  **Strict validation (no fallback):**
36
36
 
@@ -44,6 +44,7 @@ import {
44
44
  apiErrors,
45
45
  booleanString,
46
46
  dateTimeSchema,
47
+ ENUM_FALLBACK,
47
48
  nonEmptyString,
48
49
  optionalEnum,
49
50
  optionalEnumWithFallback,
@@ -134,12 +135,13 @@ const someDate = schema.parse("2026-03-15T10:30:00.000Z");
134
135
  console.log(someDate);
135
136
 
136
137
  // Enum with fallback examples
138
+ // ENUM_FALLBACK is a neutral sentinel ("UNRECOGNIZED_") automatically appended
139
+ // to the enum type. Consumers cannot choose their own fallback value, preventing
140
+ // misuse where a business-meaningful value is treated as a default.
141
+
137
142
  /* -- required -- */
138
- const requiredStatusEnumSchema = requiredEnumWithFallback(
139
- ["unspecified", "pending", "completed", "failed"],
140
- "unspecified",
141
- );
142
- // type RequiredStatusEnum = "unspecified" | "pending" | "completed" | "failed"
143
+ const requiredStatusEnumSchema = requiredEnumWithFallback(["pending", "completed", "failed"]);
144
+ // type RequiredStatusEnum = "pending" | "completed" | "failed" | "UNRECOGNIZED_"
143
145
  type RequiredStatusEnum = z.infer<typeof requiredStatusEnumSchema>;
144
146
 
145
147
  const completedStatus: RequiredStatusEnum = requiredStatusEnumSchema.parse("completed");
@@ -147,8 +149,9 @@ const completedStatus: RequiredStatusEnum = requiredStatusEnumSchema.parse("comp
147
149
  console.log(completedStatus);
148
150
 
149
151
  const additionalStatus = requiredStatusEnumSchema.parse("additional");
150
- // => "unspecified"
152
+ // => "UNRECOGNIZED_" (ENUM_FALLBACK)
151
153
  console.log(additionalStatus);
154
+ console.log(additionalStatus === ENUM_FALLBACK); // true
152
155
 
153
156
  try {
154
157
  // eslint-disable-next-line unicorn/no-useless-undefined
@@ -159,11 +162,8 @@ try {
159
162
  }
160
163
 
161
164
  /* -- optional -- */
162
- const optionalStatusEnumSchema = optionalEnumWithFallback(
163
- ["unspecified", "pending", "completed", "failed"],
164
- "unspecified",
165
- );
166
- // type OptionalStatusEnum = "unspecified" | "pending" | "completed" | "failed" | undefined
165
+ const optionalStatusEnumSchema = optionalEnumWithFallback(["pending", "completed", "failed"]);
166
+ // type OptionalStatusEnum = "pending" | "completed" | "failed" | "UNRECOGNIZED_" | undefined
167
167
  type OptionalStatusEnum = z.infer<typeof optionalStatusEnumSchema>;
168
168
 
169
169
  const failedStatus: OptionalStatusEnum = optionalStatusEnumSchema.parse("failed");
@@ -171,7 +171,7 @@ const failedStatus: OptionalStatusEnum = optionalStatusEnumSchema.parse("failed"
171
171
  console.log(failedStatus);
172
172
 
173
173
  const extraStatus = optionalStatusEnumSchema.parse("extra");
174
- // => "unspecified"
174
+ // => "UNRECOGNIZED_" (ENUM_FALLBACK)
175
175
  console.log(extraStatus);
176
176
 
177
177
  // eslint-disable-next-line unicorn/no-useless-undefined
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/contract-core",
3
- "version": "2.3.47",
3
+ "version": "3.0.0",
4
4
  "description": "Shared Zod schemas for Clipboard's contracts.",
5
5
  "keywords": [
6
6
  "contract",
@@ -1,10 +1,16 @@
1
1
  import { z } from "zod";
2
2
  type EnumValues = readonly [string, ...string[]];
3
- export declare function enumWithFallback<const V extends EnumValues, const F extends V[number]>(values: V, fallback: F, options: {
3
+ /**
4
+ * A business-context-neutral sentinel returned when an enum field
5
+ * receives a value the consumer does not recognize.
6
+ */
7
+ export declare const ENUM_FALLBACK: "UNRECOGNIZED_";
8
+ type EnumFallback = typeof ENUM_FALLBACK;
9
+ export declare function enumWithFallback<const V extends EnumValues>(values: V, options: {
4
10
  optional: true;
5
- }): z.ZodEffects<z.ZodOptional<z.ZodEnum<[...V]>>, V[number] | undefined, unknown>;
6
- export declare const optionalEnumWithFallback: <const V extends EnumValues, const F extends V[number]>(values: V, fallback: F) => z.ZodEffects<z.ZodOptional<z.ZodEnum<[...V]>>, V[number] | undefined, unknown>;
7
- export declare const requiredEnumWithFallback: <const V extends EnumValues, const F extends V[number]>(values: V, fallback: F) => z.ZodEffects<z.ZodEnum<[...V]>, V[number], unknown>;
11
+ }): z.ZodEffects<z.ZodOptional<z.ZodEnum<[...V, EnumFallback]>>, V[number] | EnumFallback | undefined, unknown>;
12
+ export declare function requiredEnumWithFallback<const V extends EnumValues>(values: V): z.ZodEffects<z.ZodEnum<[...V, "UNRECOGNIZED_"]>, "UNRECOGNIZED_" | V[number], unknown>;
13
+ export declare function optionalEnumWithFallback<const V extends EnumValues>(values: V): z.ZodEffects<z.ZodOptional<z.ZodEnum<[...V, "UNRECOGNIZED_"]>>, "UNRECOGNIZED_" | V[number] | undefined, unknown>;
8
14
  export declare function requiredEnum<const V extends EnumValues>(values: V): z.ZodEnum<[...V]>;
9
15
  export declare function optionalEnum<const V extends EnumValues>(values: V): z.ZodOptional<z.ZodEnum<[...V]>>;
10
16
  export {};
@@ -1,25 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.requiredEnumWithFallback = exports.optionalEnumWithFallback = void 0;
3
+ exports.ENUM_FALLBACK = void 0;
4
4
  exports.enumWithFallback = enumWithFallback;
5
+ exports.requiredEnumWithFallback = requiredEnumWithFallback;
6
+ exports.optionalEnumWithFallback = optionalEnumWithFallback;
5
7
  exports.requiredEnum = requiredEnum;
6
8
  exports.optionalEnum = optionalEnum;
7
9
  const zod_1 = require("zod");
8
- function enumWithFallback(values, fallback, options = {}) {
9
- const Enum = zod_1.z.enum([...values]);
10
+ /**
11
+ * A business-context-neutral sentinel returned when an enum field
12
+ * receives a value the consumer does not recognize.
13
+ */
14
+ exports.ENUM_FALLBACK = "UNRECOGNIZED_";
15
+ function enumWithFallback(values, options = {}) {
16
+ if (values.includes(exports.ENUM_FALLBACK)) {
17
+ throw new Error(`Enum values must not include "${exports.ENUM_FALLBACK}". It is appended automatically.`);
18
+ }
19
+ const OriginalEnum = zod_1.z.enum([...values]);
20
+ const ExpandedEnum = zod_1.z.enum([...values, exports.ENUM_FALLBACK]);
10
21
  const optional = options.optional ?? false;
11
- const schema = optional ? Enum.optional() : Enum;
22
+ const schema = optional ? ExpandedEnum.optional() : ExpandedEnum;
23
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
12
24
  return zod_1.z.preprocess((value) => {
13
25
  if (value === undefined) {
14
26
  return optional ? undefined : value;
15
27
  }
16
- return Enum.safeParse(value).success ? value : fallback;
28
+ return OriginalEnum.safeParse(value).success ? value : exports.ENUM_FALLBACK;
17
29
  }, schema);
18
30
  }
19
- const optionalEnumWithFallback = (values, fallback) => enumWithFallback(values, fallback, { optional: true });
20
- exports.optionalEnumWithFallback = optionalEnumWithFallback;
21
- const requiredEnumWithFallback = (values, fallback) => enumWithFallback(values, fallback, { optional: false });
22
- exports.requiredEnumWithFallback = requiredEnumWithFallback;
31
+ function requiredEnumWithFallback(values) {
32
+ return enumWithFallback(values, { optional: false });
33
+ }
34
+ function optionalEnumWithFallback(values) {
35
+ return enumWithFallback(values, { optional: true });
36
+ }
23
37
  function requiredEnum(values) {
24
38
  return zod_1.z.enum([...values]);
25
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../../../../packages/contract-core/src/lib/schemas/enum.ts"],"names":[],"mappings":";;;AAwBA,4CAeC;AAYD,oCAEC;AAED,oCAIC;AA3DD,6BAAwB;AAwBxB,SAAgB,gBAAgB,CAC9B,MAAS,EACT,QAAW,EACX,UAAkC,EAAE;IAEpC,MAAM,IAAI,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjD,OAAO,OAAC,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1D,CAAC,EAAE,MAAM,CAAC,CAAC;AACb,CAAC;AAEM,MAAM,wBAAwB,GAAG,CACtC,MAAS,EACT,QAAW,EACX,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AAH/C,QAAA,wBAAwB,4BAGuB;AAErD,MAAM,wBAAwB,GAAG,CACtC,MAAS,EACT,QAAW,EACX,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAHhD,QAAA,wBAAwB,4BAGwB;AAE7D,SAAgB,YAAY,CAA6B,MAAS;IAChE,OAAO,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,YAAY,CAC1B,MAAS;IAET,OAAO,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"enum.js","sourceRoot":"","sources":["../../../../../../packages/contract-core/src/lib/schemas/enum.ts"],"names":[],"mappings":";;;AAiCA,4CAuBC;AAED,4DAEC;AAED,4DAEC;AAED,oCAEC;AAED,oCAIC;AA1ED,6BAAwB;AAIxB;;;GAGG;AACU,QAAA,aAAa,GAAG,eAAwB,CAAC;AAyBtD,SAAgB,gBAAgB,CAC9B,MAAS,EACT,UAAkC,EAAE;IAEpC,IAAK,MAA4B,CAAC,QAAQ,CAAC,qBAAa,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,iCAAiC,qBAAa,kCAAkC,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,EAAE,qBAAa,CAAC,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;IAEjE,+DAA+D;IAC/D,OAAO,OAAC,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QACtC,CAAC;QAED,OAAO,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,qBAAa,CAAC;IACvE,CAAC,EAAE,MAAM,CAAC,CAAC;AACb,CAAC;AAED,SAAgB,wBAAwB,CAA6B,MAAS;IAC5E,OAAO,gBAAgB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,SAAgB,wBAAwB,CAA6B,MAAS;IAC5E,OAAO,gBAAgB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,SAAgB,YAAY,CAA6B,MAAS;IAChE,OAAO,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,YAAY,CAC1B,MAAS;IAET,OAAO,OAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AACxC,CAAC"}