Skip to content

Commit a28bc37

Browse files
authored
Make @standard-schema/spec be a regular dependency (#231)
* Make `@standard-schema/spec` be a regular dependency The Standard Schema docs explicitly say that it should be a regular dep, not a dev dep: https://standardschema.dev/#can-i-add-it-as-a-dev-dependency * Support input/output types
1 parent 5c0268b commit a28bc37

File tree

6 files changed

+36
-31
lines changed

6 files changed

+36
-31
lines changed

.changeset/easy-donkeys-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/core": patch
3+
---
4+
5+
Make `@standard-schema/spec` be a regular dependency

docs/content/docs/api-reference/workflow/define-hook.mdx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,21 @@ export async function POST(request: Request) {
116116
```
117117

118118
### Validate and Transform with Schema
119-
The optional `schema` accepts any validator that implements the [Standard Schema v1](https://standardschema.dev/) contract.
119+
120+
The optional `schema` accepts any validator that conforms to [Standard Schema v1](https://standardschema.dev).
121+
120122
Zod is shown below as one example, but libraries like Valibot, ArkType, Effect Schema, or your own custom validator work as well.
123+
121124
```typescript lineNumbers
122125
import { defineHook } from "workflow";
123126
import { z } from "zod";
124127

125-
const approvalSchema = z.object({
126-
approved: z.boolean(),
127-
comment: z.string().min(1).transform((value) => value.trim()),
128-
});
129-
130128
export const approvalHook = defineHook({
131129
// Provide a schema to validate/transform payloads.
132-
schema: approvalSchema, // [!code highlight]
130+
schema: z.object({ // [!code highlight]
131+
approved: z.boolean(), // [!code highlight]
132+
comment: z.string().min(1).transform((value) => value.trim()), // [!code highlight]
133+
}), // [!code highlight]
133134
});
134135

135136
export async function approvalWorkflow(approvalId: string) {

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
},
4848
"dependencies": {
4949
"@aws-sdk/credential-provider-web-identity": "3.609.0",
50+
"@standard-schema/spec": "^1.0.0",
5051
"@types/ms": "^2.1.0",
5152
"@vercel/functions": "catalog:",
5253
"@workflow/errors": "workspace:*",
@@ -64,7 +65,6 @@
6465
},
6566
"devDependencies": {
6667
"@opentelemetry/api": "^1.9.0",
67-
"@standard-schema/spec": "^1.0.0",
6868
"@types/debug": "^4.1.12",
6969
"@types/node": "catalog:",
7070
"@types/seedrandom": "^3.0.8",

packages/core/src/define-hook.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { resumeHook } from './runtime/resume-hook.js';
66
/**
77
* Defines a typed hook for type-safe hook creation and resumption.
88
*
9-
* This helper provides type safety by allowing you to define the payload type once
10-
* and reuse it when creating hooks and resuming them.
9+
* This helper provides type safety by allowing you to define the input and output types
10+
* for the hook's payload, with optional validation and transformation via a schema.
1111
*
12-
* @param schema - Schema used to validate and transform the payload before resuming
13-
* @returns An object with `create` and `resume` functions pre-typed with the payload type
12+
* @param schema - Schema used to validate and transform the input payload before resuming
13+
* @returns An object with `create` and `resume` functions pre-typed with the input and output types
1414
*
1515
* @example
1616
*
@@ -23,49 +23,48 @@ import { resumeHook } from './runtime/resume-hook.js';
2323
* "use workflow";
2424
*
2525
* const hook = approvalHook.create();
26-
* const result = await hook; // Fully typed as { approved: boolean; comment: string }
26+
* const result = await hook; // Fully typed as { approved: boolean; comment: string; }
2727
* }
2828
*
2929
* // In an API route
3030
* export async function POST(request: Request) {
3131
* const { token, approved, comment } = await request.json();
32-
* await approvalHook.resume(token, { approved, comment });
32+
* await approvalHook.resume(token, { approved, comment }); // Input type
3333
* return Response.json({ success: true });
3434
* }
3535
* ```
3636
*/
37-
export function defineHook<T>({
37+
export function defineHook<TInput, TOutput = TInput>({
3838
schema,
3939
}: {
40-
schema?: StandardSchemaV1<T, T>;
40+
schema?: StandardSchemaV1<TInput, TOutput>;
4141
} = {}) {
4242
return {
4343
/**
44-
* Creates a new hook with the defined payload type.
44+
* Creates a new hook with the defined output type.
4545
*
4646
* Note: This method is not available in runtime bundles. Use it from workflow contexts only.
4747
*
4848
* @param _options - Optional hook configuration
49-
* @returns A Hook that resolves to the defined payload type
49+
* @returns A Hook that resolves to the defined output type
5050
*/
51-
// @ts-expect-error `options` is here for types/docs
52-
create(options?: HookOptions): Hook<T> {
51+
create(_options?: HookOptions): Hook<TOutput> {
5352
throw new Error(
5453
'`defineHook().create()` can only be called inside a workflow function.'
5554
);
5655
},
5756

5857
/**
59-
* Resumes a hook by sending a payload with the defined type.
58+
* Resumes a hook by sending a payload with the defined input type.
6059
* This is a type-safe wrapper around the `resumeHook` runtime function.
6160
*
6261
* @param token - The unique token identifying the hook
6362
* @param payload - The payload to send; if a `schema` is configured it is validated/transformed before resuming
6463
* @returns Promise resolving to the hook entity, or null if the hook doesn't exist
6564
*/
66-
async resume(token: string, payload: T): Promise<HookEntity | null> {
65+
async resume(token: string, payload: TInput): Promise<HookEntity | null> {
6766
if (!schema?.['~standard']) {
68-
return await resumeHook<T>(token, payload);
67+
return await resumeHook(token, payload);
6968
}
7069

7170
let result = schema['~standard'].validate(payload);
@@ -78,7 +77,7 @@ export function defineHook<T>({
7877
throw new Error(JSON.stringify(result.issues, null, 2));
7978
}
8079

81-
return await resumeHook<T>(token, result.value);
80+
return await resumeHook<TOutput>(token, result.value);
8281
},
8382
};
8483
}

packages/core/src/workflow/define-hook.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import { createHook } from './create-hook.js';
55
/**
66
* NOTE: This is the implementation of `defineHook()` that is used in workflow contexts.
77
*/
8-
export function defineHook<T>() {
8+
export function defineHook<TInput, TOutput = TInput>() {
99
return {
10-
create(options?: HookOptions): Hook<T> {
11-
return createHook<T>(options);
10+
create(options?: HookOptions): Hook<TOutput> {
11+
return createHook<TOutput>(options);
1212
},
1313

14-
resume(_token: string, _payload: T): Promise<HookEntity | null> {
14+
resume(_token: string, _payload: TInput): Promise<HookEntity | null> {
1515
throw new Error(
1616
'`defineHook().resume()` can only be called from external contexts (e.g. API routes).'
1717
);

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)