|
1 | 1 | # @fn-sphere/core |
2 | 2 |
|
| 3 | +## 1.0.0 |
| 4 | + |
| 5 | +### Major Changes |
| 6 | + |
| 7 | +- [`15f2755`](https://github.com/lawvs/fn-sphere/commit/15f275543f600969f2c8d724b1ecfed268d49e2b) Thanks [@lawvs](https://github.com/lawvs)! - ## Migrate to Zod 4 |
| 8 | + - Upgrade all filtering utilities to Zod 4, adopting the new `z.function({ input, output })` factory. |
| 9 | + - Update tests, docs, and playground examples to reflect the revised function schema API. |
| 10 | + - Surface tuple-based parameter metadata so filter UIs can materialise runtime validators without accessing private internals. |
| 11 | + - Prepare downstream apps for the breaking changes required when upgrading filter definitions to Zod 4. |
| 12 | + |
| 13 | + ### Migration guide |
| 14 | + - Make sure your project is already on Zod 4 before consuming this release. Review the upstream changes at <https://zod.dev/v4/changelog> to plan any schema updates. |
| 15 | + - Replace every `z.function().args(...).returns(...)` with the new factory signature. |
| 16 | + |
| 17 | + ```diff |
| 18 | + const equals = defineTypedFn({ |
| 19 | + name: "equals", |
| 20 | + - define: z.function().args(z.string(), z.string()).returns(z.boolean()), |
| 21 | + + define: z.function({ input: [z.string(), z.string()], output: z.boolean() }), |
| 22 | + implement: (value, target) => value === target, |
| 23 | + }); |
| 24 | + ``` |
| 25 | + |
| 26 | + - `getParametersExceptFirst` now returns a `$ZodTuple` rather than a plain array. Use `tuple._zod.def.items` when you need to inspect or parse arguments at runtime. |
| 27 | + - `DataInputViewProps`/`DataInputViewSpec` consume tuple schemas instead of loose arrays: |
| 28 | + |
| 29 | + ```ts |
| 30 | + // Before |
| 31 | + type DataInputViewProps = { |
| 32 | + rule: SingleFilter; |
| 33 | + requiredDataSchema: [] | [z.ZodTypeAny, ...z.ZodTypeAny[]]; |
| 34 | + updateInput: (...input: unknown[]) => void; |
| 35 | + }; |
| 36 | + |
| 37 | + type DataInputViewSpec = { |
| 38 | + name: string; |
| 39 | + match: |
| 40 | + | [] |
| 41 | + | [z.ZodTypeAny, ...z.ZodTypeAny[]] |
| 42 | + | (( |
| 43 | + parameterSchemas: [] | [z.ZodTypeAny, ...z.ZodTypeAny[]], |
| 44 | + fieldSchema?: z.ZodTypeAny, |
| 45 | + ) => boolean); |
| 46 | + view: ComponentType<DataInputViewProps>; |
| 47 | + }; |
| 48 | + |
| 49 | + // After |
| 50 | + type DataInputViewProps = { |
| 51 | + rule: SingleFilter; |
| 52 | + requiredDataSchema: $ZodTuple; |
| 53 | + updateInput: (...input: unknown[]) => void; |
| 54 | + }; |
| 55 | + |
| 56 | + type DataInputViewSpec = { |
| 57 | + name: string; |
| 58 | + match: |
| 59 | + | [] |
| 60 | + | [$ZodType, ...$ZodType[]] |
| 61 | + | $ZodTuple |
| 62 | + | ((parameterSchemas: $ZodTuple, fieldSchema?: $ZodType) => boolean); |
| 63 | + view: ComponentType<DataInputViewProps>; |
| 64 | + }; |
| 65 | + ``` |
| 66 | + |
| 67 | + - Update all usages of `DataInputViewSpec.match` to handle the new tuple schema. |
| 68 | + |
| 69 | + ```diff |
| 70 | + const literalUnionView: DataInputViewSpec = { |
| 71 | + name: "literal union", |
| 72 | + match: (parameterSchemas) => { |
| 73 | + - if (parameterSchemas.length !== 1) { |
| 74 | + + if (parameterSchemas._zod.def.items.length !== 1) { |
| 75 | + return false; |
| 76 | + } |
| 77 | + - const [item] = parameterSchemas; |
| 78 | + - const isUnion = item instanceof z.ZodUnion; |
| 79 | + + const theOnlyItem = parameterSchemas._zod.def.items.at(0); |
| 80 | + + const schemaDef = (theOnlyItem as $ZodTypes)._zod.def; |
| 81 | + + const isUnion = schemaDef.type === "union"; |
| 82 | + if (!isUnion) { |
| 83 | + return false; |
| 84 | + } |
| 85 | + |
| 86 | + - return item.options.every( |
| 87 | + - (option: unknown) => option instanceof z.ZodLiteral, |
| 88 | + + return schemaDef.options.every( |
| 89 | + + (option) => option._zod.def.type === "literal", |
| 90 | + + ); |
| 91 | + }, |
| 92 | + view: function View({ requiredDataSchema, rule, updateInput }) { |
| 93 | + const { Select } = useView("components"); |
| 94 | + const { getLocaleText } = useRootRule(); |
| 95 | + |
| 96 | + const options = useMemo(() => { |
| 97 | + - const unionSchema = requiredDataSchema[0] as z.ZodUnion< |
| 98 | + - [z.ZodLiteral<z.Primitive>] |
| 99 | + - >; |
| 100 | + + const unionSchema = requiredDataSchema._zod.def.items[0] as $ZodUnion< |
| 101 | + + $ZodLiteral[] |
| 102 | + + >; |
| 103 | + |
| 104 | + - return unionSchema.options.map((item: z.ZodLiteral<z.Primitive>) => ({ |
| 105 | + + return unionSchema._zod.def.options.map((item) => { |
| 106 | + const value = item._zod.def.values[0]; |
| 107 | + const meta = z.globalRegistry.get(item); |
| 108 | + const metaDesc = |
| 109 | + meta && meta.description ? meta.description : undefined; |
| 110 | + return { |
| 111 | + label: getLocaleText(metaDesc ?? String(value)), |
| 112 | + value, |
| 113 | + }; |
| 114 | + }); |
| 115 | + }, [getLocaleText, requiredDataSchema]); |
| 116 | + return ( |
| 117 | + <Select |
| 118 | + options={options} |
| 119 | + value={rule.args[0]} |
| 120 | + onChange={(value) => { |
| 121 | + updateInput(value); |
| 122 | + }} |
| 123 | + /> |
| 124 | + ); |
| 125 | + }, |
| 126 | + } |
| 127 | + ``` |
| 128 | + |
3 | 129 | ## 0.8.0 |
4 | 130 |
|
5 | 131 | ### Patch Changes |
|
0 commit comments