Skip to content

Commit 4dddfc5

Browse files
Copilothotlong
andcommitted
Add hook.zod implementation with Zod schemas and comprehensive tests
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 359b863 commit 4dddfc5

5 files changed

Lines changed: 578 additions & 0 deletions

File tree

packages/foundation/types/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"generate:schemas": "node scripts/generate-schemas.js",
2727
"test": "jest --passWithNoTests"
2828
},
29+
"dependencies": {
30+
"zod": "^3.22.4"
31+
},
2932
"peerDependencies": {
3033
"@objectstack/spec": "^0.3.1",
3134
"@objectql/runtime": "^0.2.0"
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import { z } from 'zod';
10+
11+
/**
12+
* Zod schema for HookOperation.
13+
* Standard CRUD operations supported by hooks.
14+
*/
15+
export const HookOperationSchema = z.enum(['find', 'count', 'create', 'update', 'delete']);
16+
17+
/**
18+
* Zod schema for HookTiming.
19+
* Execution timing relative to the database operation.
20+
*/
21+
export const HookTimingSchema = z.enum(['before', 'after']);
22+
23+
/**
24+
* Zod schema for HookAPI.
25+
* Minimal API surface exposed to hooks for performing side-effects or checks.
26+
*/
27+
export const HookAPISchema = z.object({
28+
find: z.function()
29+
.args(z.string(), z.any().optional())
30+
.returns(z.promise(z.array(z.any()))),
31+
findOne: z.function()
32+
.args(z.string(), z.union([z.string(), z.number()]))
33+
.returns(z.promise(z.any())),
34+
count: z.function()
35+
.args(z.string(), z.any().optional())
36+
.returns(z.promise(z.number())),
37+
create: z.function()
38+
.args(z.string(), z.any())
39+
.returns(z.promise(z.any())),
40+
update: z.function()
41+
.args(z.string(), z.union([z.string(), z.number()]), z.any())
42+
.returns(z.promise(z.any())),
43+
delete: z.function()
44+
.args(z.string(), z.union([z.string(), z.number()]))
45+
.returns(z.promise(z.any())),
46+
});
47+
48+
/**
49+
* Zod schema for BaseHookContext.
50+
* Base context available in all hooks.
51+
*/
52+
export const BaseHookContextSchema = z.object({
53+
objectName: z.string(),
54+
operation: HookOperationSchema,
55+
api: HookAPISchema,
56+
user: z.object({
57+
id: z.union([z.string(), z.number()]),
58+
}).catchall(z.any()).optional(),
59+
state: z.record(z.any()),
60+
});
61+
62+
/**
63+
* Zod schema for RetrievalHookContext.
64+
* Context for Retrieval operations (Find, Count).
65+
*/
66+
export const RetrievalHookContextSchema = BaseHookContextSchema.extend({
67+
operation: z.enum(['find', 'count']),
68+
query: z.any(),
69+
result: z.union([z.array(z.any()), z.number()]).optional(),
70+
});
71+
72+
/**
73+
* Zod schema for MutationHookContext.
74+
* Context for Modification operations (Create, Update, Delete).
75+
*/
76+
export const MutationHookContextSchema = BaseHookContextSchema.extend({
77+
operation: z.enum(['create', 'update', 'delete']),
78+
id: z.union([z.string(), z.number()]).optional(),
79+
data: z.any().optional(),
80+
result: z.any().optional(),
81+
previousData: z.any().optional(),
82+
});
83+
84+
/**
85+
* Zod schema for UpdateHookContext.
86+
* Specialized context for Updates, including change tracking.
87+
*/
88+
export const UpdateHookContextSchema = MutationHookContextSchema.extend({
89+
operation: z.literal('update'),
90+
isModified: z.function()
91+
.args(z.any())
92+
.returns(z.boolean()),
93+
});
94+
95+
/**
96+
* Zod schema for a single hook handler function.
97+
*/
98+
export const HookHandlerSchema = z.function()
99+
.args(z.union([
100+
RetrievalHookContextSchema,
101+
MutationHookContextSchema,
102+
UpdateHookContextSchema,
103+
]))
104+
.returns(z.union([z.promise(z.void()), z.void()]));
105+
106+
/**
107+
* Zod schema for ObjectHookDefinition.
108+
* Definition interface for a set of hooks for a specific object.
109+
*/
110+
export const ObjectHookDefinitionSchema = z.object({
111+
beforeFind: z.function()
112+
.args(RetrievalHookContextSchema)
113+
.returns(z.union([z.promise(z.void()), z.void()]))
114+
.optional(),
115+
afterFind: z.function()
116+
.args(RetrievalHookContextSchema)
117+
.returns(z.union([z.promise(z.void()), z.void()]))
118+
.optional(),
119+
beforeCount: z.function()
120+
.args(RetrievalHookContextSchema)
121+
.returns(z.union([z.promise(z.void()), z.void()]))
122+
.optional(),
123+
afterCount: z.function()
124+
.args(RetrievalHookContextSchema)
125+
.returns(z.union([z.promise(z.void()), z.void()]))
126+
.optional(),
127+
beforeDelete: z.function()
128+
.args(MutationHookContextSchema)
129+
.returns(z.union([z.promise(z.void()), z.void()]))
130+
.optional(),
131+
afterDelete: z.function()
132+
.args(MutationHookContextSchema)
133+
.returns(z.union([z.promise(z.void()), z.void()]))
134+
.optional(),
135+
beforeCreate: z.function()
136+
.args(MutationHookContextSchema)
137+
.returns(z.union([z.promise(z.void()), z.void()]))
138+
.optional(),
139+
afterCreate: z.function()
140+
.args(MutationHookContextSchema)
141+
.returns(z.union([z.promise(z.void()), z.void()]))
142+
.optional(),
143+
beforeUpdate: z.function()
144+
.args(UpdateHookContextSchema)
145+
.returns(z.union([z.promise(z.void()), z.void()]))
146+
.optional(),
147+
afterUpdate: z.function()
148+
.args(UpdateHookContextSchema)
149+
.returns(z.union([z.promise(z.void()), z.void()]))
150+
.optional(),
151+
});
152+
153+
/**
154+
* Infer TypeScript types from Zod schemas for runtime validation.
155+
*/
156+
export type HookOperationZod = z.infer<typeof HookOperationSchema>;
157+
export type HookTimingZod = z.infer<typeof HookTimingSchema>;
158+
export type HookAPIZod = z.infer<typeof HookAPISchema>;
159+
export type BaseHookContextZod = z.infer<typeof BaseHookContextSchema>;
160+
export type RetrievalHookContextZod = z.infer<typeof RetrievalHookContextSchema>;
161+
export type MutationHookContextZod = z.infer<typeof MutationHookContextSchema>;
162+
export type UpdateHookContextZod = z.infer<typeof UpdateHookContextSchema>;
163+
export type ObjectHookDefinitionZod = z.infer<typeof ObjectHookDefinitionSchema>;
164+
export type HookHandlerZod = z.infer<typeof HookHandlerSchema>;

packages/foundation/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from './driver';
1919
export * from './query';
2020
export * from './registry';
2121
export * from './hook';
22+
export * from './hook.zod';
2223
export * from './action';
2324
export * from './repository';
2425
export * from './app';

0 commit comments

Comments
 (0)