Skip to content

Commit 10ee030

Browse files
authored
feat: implement sorting functionality with createSorterSphere and utilities (#207)
* feat: implement sorting functionality with createSorterSphere and related utilities * refactor: move filter and sort fn * refactor: streamline sorting and filtering utilities, remove unused functions * fix: format
1 parent be76207 commit 10ee030

File tree

14 files changed

+1036
-369
lines changed

14 files changed

+1036
-369
lines changed

packages/core/src/filter/field.ts

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,9 @@
11
import { isSameType } from "zod-compare";
2-
import type { $ZodTuple, $ZodType, $ZodTypes } from "zod/v4/core";
3-
import { isGenericFilter } from "../fn-helpers.js";
2+
import type { $ZodTuple, $ZodType } from "zod/v4/core";
3+
import { isFilterFn, isGenericFilter } from "../fn-helpers.js";
44
import type { FnSchema, StandardFnSchema } from "../types.js";
55
import type { FilterField, FilterPath } from "./types.js";
6-
import { instantiateGenericFn } from "./utils.js";
7-
8-
const bfsSchemaField = (
9-
schema: $ZodType,
10-
maxDeep: number,
11-
walk: (field: $ZodType, path: FilterPath) => void,
12-
) => {
13-
const queue = [
14-
{
15-
schema,
16-
path: [] as FilterPath,
17-
deep: 0,
18-
},
19-
];
20-
while (queue.length > 0) {
21-
const current = queue.shift();
22-
if (!current) break;
23-
if (current.deep > maxDeep) break;
24-
25-
walk(current.schema, current.path);
26-
27-
const currentSchema = current.schema as $ZodTypes;
28-
if (currentSchema._zod.def.type !== "object") continue;
29-
30-
const fields = currentSchema._zod.def.shape;
31-
for (const key in fields) {
32-
const field = fields[key];
33-
if (!field) continue;
34-
queue.push({
35-
schema: field,
36-
path: [...current.path, key] as FilterPath,
37-
deep: current.deep + 1,
38-
});
39-
}
40-
}
41-
};
6+
import { bfsSchemaField, instantiateGenericFn } from "./utils.js";
427

438
/**
449
* Find all fields that can be filtered based on the given schema and filterFnList.
@@ -64,7 +29,8 @@ export const findFilterableFields = <Data>({
6429
const genericFilter = fnSchema;
6530
return instantiateGenericFn(fieldSchema, genericFilter);
6631
})
67-
.filter((fn): fn is StandardFnSchema => !!fn);
32+
.filter((fn): fn is StandardFnSchema => !!fn)
33+
.filter(isFilterFn);
6834

6935
const availableFilter = instantiationFilter.filter((filter) => {
7036
const { define } = filter;

packages/core/src/filter/utils.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ describe("instantiateGenericFn", () => {
295295
).toBe(true);
296296
});
297297

298-
it("should return undefined if instantiated function is not a filter", () => {
298+
it("should return instantiated function even if it is not a filter (caller validates)", () => {
299299
const schema = z.string(); // Example schema
300300
const genericFn = defineGenericFn({
301301
name: "Non-Filter Function",
@@ -305,7 +305,8 @@ describe("instantiateGenericFn", () => {
305305
});
306306

307307
const result = instantiateGenericFn(schema, genericFn);
308-
expect(result).toBeUndefined();
308+
expect(result).toBeDefined();
309+
expect(result?.name).toBe("Non-Filter Function");
309310
});
310311

311312
it("should keep meta information when instantiating a function", () => {

packages/core/src/filter/utils.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
type $ZodType,
55
type $ZodTypes,
66
} from "zod/v4/core";
7-
import { isFilterFn } from "../fn-helpers.js";
87
import type { FnSchema, GenericFnSchema, StandardFnSchema } from "../types.js";
98
import { unreachable } from "../utils.js";
109
import type {
@@ -36,7 +35,7 @@ export const instantiateGenericFn = (
3635
if (!genericFn.genericLimit(schema as $ZodTypes)) {
3736
return;
3837
}
39-
const instantiationFn: StandardFnSchema = {
38+
return {
4039
name: genericFn.name,
4140
define: genericFn.define(schema),
4241
implement: genericFn.implement,
@@ -47,11 +46,6 @@ export const instantiateGenericFn = (
4746
genericFn: genericFn,
4847
},
4948
};
50-
const isFilter = isFilterFn(instantiationFn);
51-
if (!isFilter) {
52-
return;
53-
}
54-
return instantiationFn;
5549
};
5650

5751
export const getFirstParameters = (fnSchema: StandardFnSchema) => {
@@ -181,6 +175,41 @@ export const isEqualPath = (a: FilterPath, b: FilterPath): boolean => {
181175
return a.every((v, i) => v === b[i]);
182176
};
183177

178+
export const bfsSchemaField = (
179+
schema: $ZodType,
180+
maxDeep: number,
181+
walk: (field: $ZodType, path: FilterPath) => void,
182+
) => {
183+
const queue = [
184+
{
185+
schema,
186+
path: [] as FilterPath,
187+
deep: 0,
188+
},
189+
];
190+
while (queue.length > 0) {
191+
const current = queue.shift();
192+
if (!current) break;
193+
if (current.deep > maxDeep) break;
194+
195+
walk(current.schema, current.path);
196+
197+
const currentSchema = current.schema as $ZodTypes;
198+
if (currentSchema._zod.def.type !== "object") continue;
199+
200+
const fields = currentSchema._zod.def.shape;
201+
for (const key in fields) {
202+
const field = fields[key];
203+
if (!field) continue;
204+
queue.push({
205+
schema: field,
206+
path: [...current.path, key] as FilterPath,
207+
deep: current.deep + 1,
208+
});
209+
}
210+
}
211+
};
212+
184213
/**
185214
* Simple get function
186215
* See https://gist.github.com/jeneg/9767afdcca45601ea44930ea03e0febf

packages/core/src/fn-sphere.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import type {
66
$ZodUnknown,
77
} from "zod/v4/core";
88
import { createFilterSphere } from "./filter/index.js";
9-
import { isFilterFn } from "./fn-helpers.js";
9+
import { isCompareFn, isFilterFn } from "./fn-helpers.js";
10+
import { createSorterSphere } from "./sort/index.js";
1011
import type { GenericFnSchema, StandardFnSchema } from "./types.js";
1112

1213
export const createFnSphere = () => {
@@ -75,6 +76,12 @@ export const createFnSphere = () => {
7576
return zFilter;
7677
};
7778

79+
const setupSort = <S>(schema: $ZodType<S>) => {
80+
const compareFn = findFn(isCompareFn);
81+
const zSort = createSorterSphere(schema, compareFn);
82+
return zSort;
83+
};
84+
7885
return {
7986
_state: state,
8087

@@ -85,5 +92,7 @@ export const createFnSphere = () => {
8592
findFn,
8693

8794
setupFilter,
95+
96+
setupSort,
8897
};
8998
};

0 commit comments

Comments
 (0)