@clipboard-health/contract-core 3.2.0 → 3.3.1

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
@@ -19,6 +19,30 @@ npm install @clipboard-health/contract-core
19
19
 
20
20
  ### Zod schemas
21
21
 
22
+ #### Comma-separated array schema
23
+
24
+ `commaSeparatedArray(itemSchema)` validates comma-separated string or array inputs and normalizes them to typed arrays. Designed for shared contracts where the server receives comma-separated query strings and the client passes typed arrays.
25
+
26
+ ```ts
27
+ // z.input → string | string[]
28
+ // z.output → string[]
29
+ commaSeparatedArray(nonEmptyString).optional();
30
+
31
+ // z.input → string | ("CNA" | "RN" | "LVN")[]
32
+ // z.output → ("CNA" | "RN" | "LVN")[]
33
+ commaSeparatedArray(requiredEnum(["CNA", "RN", "LVN"]));
34
+
35
+ // z.input → string | string[]
36
+ // z.output → string[] (each validated as ObjectId)
37
+ commaSeparatedArray(objectId);
38
+
39
+ // z.input → string | (string | Date)[]
40
+ // z.output → Date[]
41
+ commaSeparatedArray(dateTimeSchema());
42
+ ```
43
+
44
+ Composes with all contract-core schemas and enum helpers. Replaces `z.preprocess(splitString, z.array(...))` with proper `z.input` typing (the `splitString` pattern erases input types to `unknown`).
45
+
22
46
  #### DateTime schema
23
47
 
24
48
  `dateTimeSchema()` validates strict ISO-8601 datetime strings and transforms them to `Date` objects. Unlike `z.coerce.date()`, it rejects loose inputs like epoch numbers and date-only strings. Composable with `.optional()`, `.nullable()`, etc. at the call site.
@@ -58,9 +82,11 @@ requiredEnum(widened); // TS error
58
82
  import {
59
83
  apiErrors,
60
84
  booleanString,
85
+ commaSeparatedArray,
61
86
  dateTimeSchema,
62
87
  ENUM_FALLBACK,
63
88
  nonEmptyString,
89
+ objectId,
64
90
  optionalEnum,
65
91
  optionalEnumWithFallback,
66
92
  requiredEnum,
@@ -85,6 +111,35 @@ apiErrors.parse({
85
111
  ],
86
112
  });
87
113
 
114
+ // Comma-separated array examples
115
+ // Designed for shared contracts: server receives "CNA,RN" (string), client passes ["CNA", "RN"] (array).
116
+ // Both normalize to the same typed array output.
117
+ const workerTypes = commaSeparatedArray(requiredEnum(["CNA", "RN", "LVN"]));
118
+
119
+ // Server-side: comma-separated string from query params
120
+ const fromString = workerTypes.parse("CNA,RN");
121
+ // => ["CNA", "RN"]
122
+ console.log(fromString);
123
+
124
+ // Client-side: typed array
125
+ const fromArray = workerTypes.parse(["CNA", "LVN"]);
126
+ // => ["CNA", "LVN"]
127
+ console.log(fromArray);
128
+
129
+ // Composes with other schemas
130
+ const workerIds = commaSeparatedArray(objectId).optional();
131
+ const dates = commaSeparatedArray(dateTimeSchema());
132
+
133
+ // Works with .optional()
134
+ // eslint-disable-next-line unicorn/no-useless-undefined
135
+ const noIds = workerIds.parse(undefined);
136
+ // => undefined
137
+ console.log(noIds);
138
+
139
+ const someDates = dates.parse("2026-01-01T00:00:00.000Z,2026-01-02T00:00:00.000Z");
140
+ // => [Date, Date]
141
+ console.log(someDates[0] instanceof Date); // true
142
+
88
143
  booleanString.parse("true");
89
144
 
90
145
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/contract-core",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Shared Zod schemas for Clipboard's contracts.",
5
5
  "keywords": [
6
6
  "contract",
@@ -25,7 +25,7 @@
25
25
  "tslib": "2.8.1"
26
26
  },
27
27
  "devDependencies": {
28
- "@clipboard-health/testing-core": "2.2.90",
28
+ "@clipboard-health/testing-core": "2.2.91",
29
29
  "zod": "3.25.76"
30
30
  },
31
31
  "peerDependencies": {
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Validates comma-separated string or array inputs and normalizes them to typed arrays.
4
+ *
5
+ * Accepts:
6
+ * - Comma-separated strings (split and validated per item)
7
+ * - Arrays of items (validated per item)
8
+ *
9
+ * Designed for shared contracts where the server receives comma-separated query strings
10
+ * and the client passes typed arrays.
11
+ *
12
+ * Composable with `.optional()`, `.nullable()`, etc. at the call site:
13
+ * ```ts
14
+ * z.object({
15
+ * workerTypes: commaSeparatedArray(z.string().min(1)).optional(),
16
+ * dates: commaSeparatedArray(dateTimeSchema()).optional(),
17
+ * });
18
+ * ```
19
+ *
20
+ * z.input → string | T[] (where T is z.input of the item schema)
21
+ * z.output → T[] (where T is z.output of the item schema)
22
+ */
23
+ export declare function commaSeparatedArray<T extends z.ZodTypeAny>(itemSchema: T): z.ZodPipeline<z.ZodUnion<[z.ZodEffects<z.ZodString, string[], string>, z.ZodArray<T, "many">]>, z.ZodArray<T, "many">>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.commaSeparatedArray = commaSeparatedArray;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Validates comma-separated string or array inputs and normalizes them to typed arrays.
7
+ *
8
+ * Accepts:
9
+ * - Comma-separated strings (split and validated per item)
10
+ * - Arrays of items (validated per item)
11
+ *
12
+ * Designed for shared contracts where the server receives comma-separated query strings
13
+ * and the client passes typed arrays.
14
+ *
15
+ * Composable with `.optional()`, `.nullable()`, etc. at the call site:
16
+ * ```ts
17
+ * z.object({
18
+ * workerTypes: commaSeparatedArray(z.string().min(1)).optional(),
19
+ * dates: commaSeparatedArray(dateTimeSchema()).optional(),
20
+ * });
21
+ * ```
22
+ *
23
+ * z.input → string | T[] (where T is z.input of the item schema)
24
+ * z.output → T[] (where T is z.output of the item schema)
25
+ */
26
+ function commaSeparatedArray(itemSchema) {
27
+ return zod_1.z
28
+ .union([zod_1.z.string().transform((value) => value.split(",")), zod_1.z.array(itemSchema)])
29
+ .pipe(zod_1.z.array(itemSchema));
30
+ }
31
+ //# sourceMappingURL=commaSeparatedArray.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commaSeparatedArray.js","sourceRoot":"","sources":["../../../../../../packages/contract-core/src/lib/schemas/commaSeparatedArray.ts"],"names":[],"mappings":";;AAuBA,kDAIC;AA3BD,6BAAwB;AAExB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,mBAAmB,CAAyB,UAAa;IACvE,OAAO,OAAC;SACL,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;SAC/E,IAAI,CAAC,OAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAC/B,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export * from "./apiError";
2
2
  export * from "./booleanString";
3
+ export * from "./commaSeparatedArray";
3
4
  export * from "./dateTime";
4
5
  export * from "./enum";
5
6
  export * from "./money";
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./apiError"), exports);
5
5
  tslib_1.__exportStar(require("./booleanString"), exports);
6
+ tslib_1.__exportStar(require("./commaSeparatedArray"), exports);
6
7
  tslib_1.__exportStar(require("./dateTime"), exports);
7
8
  tslib_1.__exportStar(require("./enum"), exports);
8
9
  tslib_1.__exportStar(require("./money"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/contract-core/src/lib/schemas/index.ts"],"names":[],"mappings":";;;AAAA,qDAA2B;AAC3B,0DAAgC;AAChC,qDAA2B;AAC3B,iDAAuB;AACvB,kDAAwB;AACxB,2DAAiC;AACjC,qDAA2B;AAC3B,iDAAuB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/contract-core/src/lib/schemas/index.ts"],"names":[],"mappings":";;;AAAA,qDAA2B;AAC3B,0DAAgC;AAChC,gEAAsC;AACtC,qDAA2B;AAC3B,iDAAuB;AACvB,kDAAwB;AACxB,2DAAiC;AACjC,qDAA2B;AAC3B,iDAAuB"}