@ptolemy2002/ts-utils 3.5.1 → 4.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
@@ -41,6 +41,9 @@ This type returns a union of all the keys in `T` that have a value assignable to
41
41
  ### PartialBy<T, K extends keyof T>
42
42
  This type returns a type that is the same as `T` except that the keys in `K` are optional.
43
43
 
44
+ ### RequiredBy<T, K extends keyof T>
45
+ This type returns a type that is the same as `T` except that the keys in `K` are required.
46
+
44
47
  ### AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }>
45
48
  This type returns a type that is the same as `T` except that at least one key is required.
46
49
 
@@ -79,14 +82,14 @@ declare const advancedConditionSymbol: unique symbol;
79
82
 
80
83
  type AdvancedCondition<T> = Branded<{
81
84
  __isAdvancedCondition: true,
82
- include?: T | false | ((v: T) => boolean) | (T | false | ((v: T) => boolean) | false)[],
83
- exclude?: T | false | ((v: T) => boolean) | (T | false | ((v: T) => boolean | false))[],
85
+ include?: T | false | ((v: T) => boolean) | (T | false | ((v: T) => boolean))[],
86
+ exclude?: T | false | ((v: T) => boolean) | (T | false | ((v: T) => boolean))[],
84
87
  match?: (a: T, b: T) => boolean
85
88
  }, [typeof advancedConditionSymbol]>;
86
89
  ```
87
90
 
88
91
  ### SerializableAdvancedCondition<T>
89
- This type is the same as `AdvancedCondition<T>` except that the `include` and `exclude` fields cannot be functions and the `match` field is omit, making it safe for JSON serialization, assuming that `T` is also JSON-serializable. Thus, you lose the ability to use custom matching logic when using this type.
92
+ This type is the same as `AdvancedCondition<T>` except that the `include` and `exclude` fields cannot be functions and the `match` field is omitted, making it safe for JSON serialization, assuming that `T` is also JSON-serializable. Thus, you lose the ability to use custom matching logic when using this type.
90
93
 
91
94
  This is assignable to any field that accepts an `AdvancedCondition<T>`.
92
95
 
@@ -190,25 +193,62 @@ The same as `valueConditionType`, except that it specifically takes a `Serializa
190
193
  #### Returns
191
194
  `SerializableValueConditionType` - The type of the condition.
192
195
 
193
- ### zodSerializableAdvancedConditionSchemaTemplate<ZT extends ZodType>
196
+ ### zodAdvancedConditionSchemaTemplate<T>
197
+ #### Description
198
+ Given a Zod schema for `T`, this function returns a Zod schema that validates an `AdvancedCondition<T>`. The parsed output is created with `createAdvancedCondition`, so the `__isAdvancedCondition` tag and the usual defaults are always present.
199
+
200
+ Input rules:
201
+ - The input must be an object with at least one of `include`, `exclude`, or `match` defined.
202
+ - `__isAdvancedCondition` may be provided, but only as `true`. It is not required, as the schema adds it.
203
+ - Unknown keys are rejected. This prevents arbitrary objects from being interpreted as an empty condition that matches everything.
204
+
205
+ Functions in `include`, `exclude`, and `match` are checked using `zodFunctionSchema` from `@ptolemy2002/zod-utils`. This has the following consequences:
206
+ - The functions are **called during parsing** with the sample values. Avoid passing functions with side effects.
207
+ - An `include` or `exclude` predicate must return a boolean when called with `sample1`.
208
+ - A `match` function must return `true` for `(sample1, sample1)` and `false` for `(sample1, sample2)`. Choose samples that no reasonable match function would consider equal.
209
+ - The functions in the output are wrappers that validate their arguments and return value on every call, throwing a `ZodError` if either is invalid.
210
+
211
+ #### Parameters
212
+ - `zt` (`ZodType<T>`) - The Zod schema representing the type `T`.
213
+ - `sample1` (`T`) - A sample value used to test functions during parsing.
214
+ - `sample2` (`T`) - A second sample value, different from `sample1`, used to test `match` functions.
215
+
216
+ #### Returns
217
+ `ZodType<AdvancedCondition<T>>` - A Zod schema that can be used to validate an `AdvancedCondition<T>`.
218
+
219
+ ### zodSerializableAdvancedConditionSchemaTemplate<T>
220
+ #### Description
221
+ The same as `zodAdvancedConditionSchemaTemplate`, except that it validates a `SerializableAdvancedCondition<T>`. The parsed output is created with `createSerializableAdvancedCondition`. The input must have at least one of `include` or `exclude` defined. Functions are not allowed anywhere, and a `match` key is rejected like any other unknown key. Because no functions are ever called, no sample values are needed.
222
+
223
+ #### Parameters
224
+ - `zt` (`ZodType<T>`) - The Zod schema representing the type `T`.
225
+
226
+ #### Returns
227
+ `ZodType<SerializableAdvancedCondition<T>>` - A Zod schema that can be used to validate a `SerializableAdvancedCondition<T>`.
228
+
229
+ ### zodValueConditionSchemaTemplate<T>
194
230
  #### Description
195
- A function that allows you to pass in a Zod schema and wrap it such that it can be used to validate a `SerializableAdvancedCondition<T>` where `T` is the type represented by the Zod schema. Note: not the full extent of recursion in `SerializableAdvancedCondition<T>` is supported, nor are functions.
231
+ Given a Zod schema for `T`, this function returns a Zod schema that validates a `ValueCondition<T>`, including arrays nested to any depth. Advanced conditions are validated with `zodAdvancedConditionSchemaTemplate`, so the same input rules apply to them. Function conditions are called once with `sample1` during parsing, must return a boolean, and are wrapped in the same way.
232
+
233
+ Options are tried in the same order `valueConditionMatches` checks them: array, function, advanced condition, then a plain value of type `T`. As a result, an object with at least one of `include`, `exclude`, or `match` and no other keys is always treated as an advanced condition, even if it would also be a valid `T`.
196
234
 
197
235
  #### Parameters
198
- - `zt` (`ZT`) - The Zod schema representing the type `T`.
236
+ - `zt` (`ZodType<T>`) - The Zod schema representing the type `T`.
237
+ - `sample1` (`T`) - A sample value used to test functions during parsing.
238
+ - `sample2` (`T`) - A second sample value, different from `sample1`, used to test `match` functions.
199
239
 
200
240
  #### Returns
201
- `ZodObject<...>` - A Zod schema that can be used to validate a `SerializableAdvancedCondition<T>`.
241
+ `ZodType<ValueCondition<T>>` - A Zod schema that can be used to validate a `ValueCondition<T>`.
202
242
 
203
- ### zodSerializableValueConditionSchemaTemplate<ZT extends ZodType>
243
+ ### zodSerializableValueConditionSchemaTemplate<T>
204
244
  #### Description
205
- A function that allows you to pass in a Zod schema and wrap it such that it can be used to validate a `SerializableValueCondition<T>` where `T` is the type represented by the Zod schema. Note: not the full extent of recursion in `SerializableValueCondition<T>` is supported, nor are functions.
245
+ The same as `zodValueConditionSchemaTemplate`, except that it validates a `SerializableValueCondition<T>`. Function conditions are not allowed, and advanced conditions are validated with `zodSerializableAdvancedConditionSchemaTemplate`. Arrays may be nested to any depth.
206
246
 
207
247
  #### Parameters
208
- - `zt` (`ZT`) - The Zod schema representing the type `T`.
248
+ - `zt` (`ZodType<T>`) - The Zod schema representing the type `T`.
209
249
 
210
250
  #### Returns
211
- `ZodUnion<...>` - A Zod schema that can be used to validate a `SerializableValueCondition<T>`.
251
+ `ZodType<SerializableValueCondition<T>>` - A Zod schema that can be used to validate a `SerializableValueCondition<T>`.
212
252
 
213
253
  ### omit<T, K extends keyof T>
214
254
  #### Description
@@ -230,7 +270,7 @@ An abstract class representing an object that can be treated as an array, but wi
230
270
  ## Peer Dependencies
231
271
  - `is-callable^1.2.7`
232
272
  - `@ptolemy2002/ts-brand-utils^1.0.0`
233
- - `@ptolemy2002/regex-utils^4.2.0`
273
+ - `@ptolemy2002/zod-utils^1.7.0`
234
274
  - `zod^4.3.6`
235
275
 
236
276
  ## Commands
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Branded, WithoutBrand } from "@ptolemy2002/ts-brand-utils";
2
- import z, { ZodType } from "zod";
2
+ import { ZodType } from "zod";
3
3
  export type ValueOf<T> = T[keyof T];
4
4
  export type MaybeTransformer<T, Args extends any[] = []> = T | ((...args: Args) => T);
5
5
  export type MaybeTransformerRecord<T, Args extends any[] = []> = {
@@ -22,6 +22,7 @@ export type KeysNotMatchingEqualTypes<T, V> = {
22
22
  [K in keyof T]-?: EqualTypes<T[K], V, never, K>;
23
23
  }[keyof T];
24
24
  export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
25
+ export type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
25
26
  export type AtLeastOne<T, U = {
26
27
  [K in keyof T]: Pick<T, K>;
27
28
  }> = Partial<T> & U[keyof U];
@@ -50,26 +51,10 @@ export type ValueConditionType = "advanced" | "function" | "value" | (ValueCondi
50
51
  export type SerializableValueConditionType = "advanced" | "value" | (SerializableValueConditionType | "false")[];
51
52
  export declare function valueConditionType<T>(condition: ValueCondition<T>): ValueConditionType;
52
53
  export declare function serializableValueConditionType<T>(condition: SerializableValueCondition<T>): SerializableValueConditionType;
53
- export declare function zodSerializableAdvancedConditionSchemaTemplate<ZT extends ZodType>(zt: ZT): z.ZodPipe<z.ZodObject<{
54
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
55
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
56
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
57
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
58
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
59
- }>>;
60
- export declare function zodSerializableValueConditionSchemaTemplate<ZT extends ZodType>(zt: ZT): z.ZodUnion<readonly [ZT, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodPipe<z.ZodObject<{
61
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
62
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
63
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
64
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
65
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
66
- }>>]>>, z.ZodPipe<z.ZodObject<{
67
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
68
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
69
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
70
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
71
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
72
- }>>]>;
54
+ export declare function zodAdvancedConditionSchemaTemplate<T>(zt: ZodType<T>, sample1: T, sample2: T): ZodType<AdvancedCondition<T>>;
55
+ export declare function zodSerializableAdvancedConditionSchemaTemplate<T>(zt: ZodType<T>): ZodType<SerializableAdvancedCondition<T>>;
56
+ export declare function zodValueConditionSchemaTemplate<T>(zt: ZodType<T>, sample1: T, sample2: T): ZodType<ValueCondition<T>>;
57
+ export declare function zodSerializableValueConditionSchemaTemplate<T>(zt: ZodType<T>): ZodType<SerializableValueCondition<T>>;
73
58
  export type Rename<T, K extends keyof T, N extends string> = Pick<T, Exclude<keyof T, K>> & {
74
59
  [P in N]: T[K];
75
60
  };
package/dist/index.js CHANGED
@@ -10,12 +10,14 @@ exports.createSerializableAdvancedCondition = createSerializableAdvancedConditio
10
10
  exports.valueConditionMatches = valueConditionMatches;
11
11
  exports.valueConditionType = valueConditionType;
12
12
  exports.serializableValueConditionType = serializableValueConditionType;
13
+ exports.zodAdvancedConditionSchemaTemplate = zodAdvancedConditionSchemaTemplate;
13
14
  exports.zodSerializableAdvancedConditionSchemaTemplate = zodSerializableAdvancedConditionSchemaTemplate;
15
+ exports.zodValueConditionSchemaTemplate = zodValueConditionSchemaTemplate;
14
16
  exports.zodSerializableValueConditionSchemaTemplate = zodSerializableValueConditionSchemaTemplate;
15
17
  exports.omit = omit;
16
18
  const is_callable_1 = __importDefault(require("is-callable"));
17
19
  const ts_brand_utils_1 = require("@ptolemy2002/ts-brand-utils");
18
- const regex_utils_1 = require("@ptolemy2002/regex-utils");
20
+ const zod_utils_1 = require("@ptolemy2002/zod-utils");
19
21
  const zod_1 = __importDefault(require("zod"));
20
22
  function isAdvancedCondition(value) {
21
23
  return (typeof value === "object" &&
@@ -83,29 +85,94 @@ function serializableValueConditionType(condition) {
83
85
  return "advanced";
84
86
  return "value";
85
87
  }
86
- const zodValueConditionGenericFactory = (0, regex_utils_1.zodGenericFactory)();
88
+ function zodAdvancedConditionSchemaTemplate(zt, sample1, sample2) {
89
+ const validatorFunctionSchema = (0, zod_utils_1.zodFunctionSchema)({
90
+ input: zod_1.default.tuple([zt]),
91
+ output: zod_1.default.boolean(),
92
+ trials: [
93
+ {
94
+ input: [sample1],
95
+ outputSchema: zod_1.default.boolean(),
96
+ error: "forbid"
97
+ }
98
+ ]
99
+ });
100
+ // Options are ordered to mirror the checks in valueConditionMatches,
101
+ // since z.union returns the first option that succeeds.
102
+ const validatorItemSchema = zod_1.default.union([zod_1.default.literal(false), validatorFunctionSchema, zt]);
103
+ const validatorUnionSchema = zod_1.default.union([zod_1.default.array(validatorItemSchema), validatorItemSchema]);
104
+ // Strict and requiring at least one key so that arbitrary objects are not interpreted
105
+ // as an empty (match-all) condition.
106
+ // The tag is accepted but not required, as the transform always injects it.
107
+ return zod_1.default.strictObject({
108
+ __isAdvancedCondition: zod_1.default.literal(true).optional(),
109
+ include: validatorUnionSchema.optional(),
110
+ exclude: validatorUnionSchema.optional(),
111
+ match: (0, zod_utils_1.zodFunctionSchema)({
112
+ input: zod_1.default.tuple([zt, zt]),
113
+ output: zod_1.default.boolean(),
114
+ trials: [
115
+ {
116
+ id: "matches_same_object",
117
+ input: [sample1, sample1],
118
+ outputSchema: zod_1.default.literal(true),
119
+ error: "forbid"
120
+ },
121
+ // Requires sample1 and sample2 to be values that no reasonable
122
+ // match function would consider equal
123
+ {
124
+ id: "does_not_match_different_objects",
125
+ input: [sample1, sample2],
126
+ outputSchema: zod_1.default.literal(false),
127
+ error: "forbid"
128
+ }
129
+ ]
130
+ }).optional()
131
+ }).refine((data) => data.include !== undefined || data.exclude !== undefined || data.match !== undefined, { message: "At least one of include, exclude, or match must be specified" }).transform(({ __isAdvancedCondition, ...data }) => createAdvancedCondition(data));
132
+ }
87
133
  function zodSerializableAdvancedConditionSchemaTemplate(zt) {
88
- return zod_1.default.object({
89
- include: zod_1.default.union([
90
- zt,
91
- zod_1.default.literal(false),
92
- zod_1.default.array(zod_1.default.union([zt, zod_1.default.literal(false)]))
93
- ]),
94
- exclude: zod_1.default.union([
95
- zt,
96
- zod_1.default.literal(false),
97
- zod_1.default.array(zod_1.default.union([zt, zod_1.default.literal(false)]))
98
- ])
99
- }).partial().transform((data) => createSerializableAdvancedCondition(data));
134
+ const valueItemSchema = zod_1.default.union([zod_1.default.literal(false), zt]);
135
+ const valueUnionSchema = zod_1.default.union([zod_1.default.array(valueItemSchema), valueItemSchema]);
136
+ // Strict, so a "match" key (or any other unknown key) is rejected
137
+ return zod_1.default.strictObject({
138
+ __isAdvancedCondition: zod_1.default.literal(true).optional(),
139
+ include: valueUnionSchema.optional(),
140
+ exclude: valueUnionSchema.optional()
141
+ }).refine((data) => data.include !== undefined || data.exclude !== undefined, { message: "At least one of include or exclude must be specified" }).transform(({ __isAdvancedCondition, ...data }) => createSerializableAdvancedCondition(data));
142
+ }
143
+ function zodValueConditionSchemaTemplate(zt, sample1, sample2) {
144
+ // Options are ordered to mirror the checks in valueConditionMatches,
145
+ // since z.union returns the first option that succeeds.
146
+ const schema = zod_1.default.union([
147
+ zod_1.default.array(zod_1.default.union([zod_1.default.literal(false),
148
+ // This lazy evaluation is what allows recursion
149
+ zod_1.default.lazy(() => schema)])),
150
+ (0, zod_utils_1.zodFunctionSchema)({
151
+ input: zod_1.default.tuple([zt]),
152
+ output: zod_1.default.boolean(),
153
+ trials: [
154
+ {
155
+ input: [sample1],
156
+ outputSchema: zod_1.default.boolean()
157
+ }
158
+ ]
159
+ }),
160
+ zodAdvancedConditionSchemaTemplate(zt, sample1, sample2),
161
+ zt
162
+ ]);
163
+ return schema;
100
164
  }
101
165
  function zodSerializableValueConditionSchemaTemplate(zt) {
102
- return zodValueConditionGenericFactory(zt)((s) => {
103
- return zod_1.default.union([
104
- s,
105
- zod_1.default.array(zod_1.default.union([s, zod_1.default.literal(false), zodSerializableAdvancedConditionSchemaTemplate(s)])),
106
- zodSerializableAdvancedConditionSchemaTemplate(s)
107
- ]);
108
- });
166
+ // Essentially same as above, but with no function option and deferring to the serializable advanced condition schema template
167
+ // instead of the regular advanced condition schema template
168
+ const schema = zod_1.default.union([
169
+ zod_1.default.array(zod_1.default.union([zod_1.default.literal(false),
170
+ // This lazy evaluation is what allows recursion
171
+ zod_1.default.lazy(() => schema)])),
172
+ zodSerializableAdvancedConditionSchemaTemplate(zt),
173
+ zt
174
+ ]);
175
+ return schema;
109
176
  }
110
177
  function omit(obj, ...keys) {
111
178
  const _ = { ...obj };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptolemy2002/ts-utils",
3
- "version": "3.5.1",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,14 +27,14 @@
27
27
  "release-major": "bash ./scripts/release.sh major"
28
28
  },
29
29
  "peerDependencies": {
30
- "@ptolemy2002/regex-utils": "^4.2.0",
31
30
  "@ptolemy2002/ts-brand-utils": "^1.0.0",
31
+ "@ptolemy2002/zod-utils": "^1.7.0",
32
32
  "is-callable": "^1.2.7",
33
33
  "zod": "^4.3.6"
34
34
  },
35
35
  "devDependencies": {
36
- "@ptolemy2002/regex-utils": "^4.2.0",
37
36
  "@ptolemy2002/ts-brand-utils": "^1.0.0",
37
+ "@ptolemy2002/zod-utils": "^1.7.0",
38
38
  "@types/is-callable": "~1.1.2",
39
39
  "is-callable": "^1.2.7",
40
40
  "zod": "^4.3.6"