@ptolemy2002/ts-utils 3.6.0 → 4.0.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
@@ -82,14 +82,14 @@ declare const advancedConditionSymbol: unique symbol;
82
82
 
83
83
  type AdvancedCondition<T> = Branded<{
84
84
  __isAdvancedCondition: true,
85
- include?: T | false | ((v: T) => boolean) | (T | false | ((v: T) => boolean) | false)[],
86
- 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))[],
87
87
  match?: (a: T, b: T) => boolean
88
88
  }, [typeof advancedConditionSymbol]>;
89
89
  ```
90
90
 
91
91
  ### SerializableAdvancedCondition<T>
92
- 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.
93
93
 
94
94
  This is assignable to any field that accepts an `AdvancedCondition<T>`.
95
95
 
@@ -193,25 +193,62 @@ The same as `valueConditionType`, except that it specifically takes a `Serializa
193
193
  #### Returns
194
194
  `SerializableValueConditionType` - The type of the condition.
195
195
 
196
- ### zodSerializableAdvancedConditionSchemaTemplate<ZT extends ZodType>
196
+ ### zodAdvancedConditionSchemaTemplate<T>
197
197
  #### Description
198
- 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.
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>
230
+ #### Description
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`.
199
234
 
200
235
  #### Parameters
201
- - `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.
202
239
 
203
240
  #### Returns
204
- `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>`.
205
242
 
206
- ### zodSerializableValueConditionSchemaTemplate<ZT extends ZodType>
243
+ ### zodSerializableValueConditionSchemaTemplate<T>
207
244
  #### Description
208
- 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.
209
246
 
210
247
  #### Parameters
211
- - `zt` (`ZT`) - The Zod schema representing the type `T`.
248
+ - `zt` (`ZodType<T>`) - The Zod schema representing the type `T`.
212
249
 
213
250
  #### Returns
214
- `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>`.
215
252
 
216
253
  ### omit<T, K extends keyof T>
217
254
  #### Description
@@ -231,9 +268,6 @@ This library exports the following classes:
231
268
  An abstract class representing an object that can be treated as an array, but with some methods overwritten to use a collection of this type rather than a standard array. No methods are implemented here, so it is up to the extending class to do it all. This is just a template for the extending class to ensure that it has all the necessary methods and that they return the correct types.
232
269
 
233
270
  ## Peer Dependencies
234
- - `is-callable^1.2.7`
235
- - `@ptolemy2002/ts-brand-utils^1.0.0`
236
- - `@ptolemy2002/regex-utils^4.2.0`
237
271
  - `zod^4.3.6`
238
272
 
239
273
  ## 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[] = []> = {
@@ -51,26 +51,10 @@ export type ValueConditionType = "advanced" | "function" | "value" | (ValueCondi
51
51
  export type SerializableValueConditionType = "advanced" | "value" | (SerializableValueConditionType | "false")[];
52
52
  export declare function valueConditionType<T>(condition: ValueCondition<T>): ValueConditionType;
53
53
  export declare function serializableValueConditionType<T>(condition: SerializableValueCondition<T>): SerializableValueConditionType;
54
- export declare function zodSerializableAdvancedConditionSchemaTemplate<ZT extends ZodType>(zt: ZT): z.ZodPipe<z.ZodObject<{
55
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
56
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
57
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
58
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
59
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
60
- }>>;
61
- 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<{
62
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
63
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
64
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
65
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
66
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
67
- }>>]>>, z.ZodPipe<z.ZodObject<{
68
- include: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
69
- exclude: z.ZodOptional<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>, z.ZodArray<z.ZodUnion<readonly [ZT, z.ZodLiteral<false>]>>]>>;
70
- }, z.core.$strip>, z.ZodTransform<SerializableAdvancedCondition<z.core.$InferUnionOutput<ZT>>, {
71
- include?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
72
- exclude?: false | z.core.$InferUnionOutput<ZT> | (false | z.core.$InferUnionOutput<ZT>)[];
73
- }>>]>;
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>>;
74
58
  export type Rename<T, K extends keyof T, N extends string> = Pick<T, Exclude<keyof T, K>> & {
75
59
  [P in N]: T[K];
76
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.6.0",
3
+ "version": "4.0.1",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "tsc --project ./tsconfig.json",
17
- "postinstall": "npx typesync",
17
+ "prepare": "npx typesync",
18
18
  "uninstall": "bash ./scripts/uninstall.sh",
19
19
  "reinstall": "bash ./scripts/reinstall.sh",
20
20
  "example-uninstall": "bash ./scripts/example-uninstall.sh",
@@ -26,17 +26,16 @@
26
26
  "release-minor": "bash ./scripts/release.sh minor",
27
27
  "release-major": "bash ./scripts/release.sh major"
28
28
  },
29
- "peerDependencies": {
30
- "@ptolemy2002/regex-utils": "^4.2.0",
29
+ "dependencies": {
31
30
  "@ptolemy2002/ts-brand-utils": "^1.0.0",
32
- "is-callable": "^1.2.7",
31
+ "@ptolemy2002/zod-utils": "^1.7.0",
32
+ "is-callable": "^1.2.7"
33
+ },
34
+ "peerDependencies": {
33
35
  "zod": "^4.3.6"
34
36
  },
35
37
  "devDependencies": {
36
- "@ptolemy2002/regex-utils": "^4.2.0",
37
- "@ptolemy2002/ts-brand-utils": "^1.0.0",
38
38
  "@types/is-callable": "~1.1.2",
39
- "is-callable": "^1.2.7",
40
39
  "zod": "^4.3.6"
41
40
  }
42
41
  }