@jlnstack/procedure 0.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/CHANGELOG.md +7 -0
- package/README.md +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.cjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.mjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.cjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.mjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.mjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.cjs +18 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.mjs +12 -0
- package/dist/core-internal.cjs +71 -0
- package/dist/core-internal.d.cts +33 -0
- package/dist/core-internal.d.cts.map +1 -0
- package/dist/core-internal.d.mts +33 -0
- package/dist/core-internal.d.mts.map +1 -0
- package/dist/core-internal.mjs +71 -0
- package/dist/core-internal.mjs.map +1 -0
- package/dist/core.cjs +12 -0
- package/dist/core.d.cts +15 -0
- package/dist/core.d.cts.map +1 -0
- package/dist/core.d.mts +15 -0
- package/dist/core.d.mts.map +1 -0
- package/dist/core.mjs +13 -0
- package/dist/core.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +3 -0
- package/dist/next.cjs +45 -0
- package/dist/next.d.cts +47 -0
- package/dist/next.d.cts.map +1 -0
- package/dist/next.d.mts +47 -0
- package/dist/next.d.mts.map +1 -0
- package/dist/next.mjs +46 -0
- package/dist/next.mjs.map +1 -0
- package/dist/react.cjs +25 -0
- package/dist/react.d.cts +20 -0
- package/dist/react.d.cts.map +1 -0
- package/dist/react.d.mts +20 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.mjs +26 -0
- package/dist/react.mjs.map +1 -0
- package/dist/types.d.cts +6 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +6 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +64 -0
- package/src/core-internal.ts +206 -0
- package/src/core.ts +29 -0
- package/src/index.ts +1 -0
- package/src/next.ts +161 -0
- package/src/react.ts +66 -0
- package/src/types.ts +4 -0
- package/test/core.test-d.ts +89 -0
- package/test/core.test.ts +57 -0
- package/test/next.test-d.ts +177 -0
- package/test/next.test.ts +230 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +5 -0
- package/tsdown.config.ts +23 -0
- package/vitest.config.ts +10 -0
package/src/next.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { Metadata } from "next";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { init as coreInit, type InitOptions } from "./core";
|
|
5
|
+
import {
|
|
6
|
+
createMiddleware,
|
|
7
|
+
createProcedureBuilder,
|
|
8
|
+
type InferMiddlewareArrayInput,
|
|
9
|
+
type InferMiddlewareArrayOutput,
|
|
10
|
+
type InferMiddlewareOutput,
|
|
11
|
+
type InternalMiddleware,
|
|
12
|
+
type MergeInput,
|
|
13
|
+
type Middleware,
|
|
14
|
+
type ProcedureBuilderFactory,
|
|
15
|
+
type Run,
|
|
16
|
+
} from "./core-internal";
|
|
17
|
+
import type { Prettify } from "./types";
|
|
18
|
+
|
|
19
|
+
type ExtractParams<TInput> = TInput extends { params: infer P } ? P : never;
|
|
20
|
+
|
|
21
|
+
interface NextProcedureBuilder<TContext, TInput = {}> {
|
|
22
|
+
use<TInputExt, TMiddleware extends Middleware<TInputExt, TContext, any>>(
|
|
23
|
+
middleware: TMiddleware,
|
|
24
|
+
): NextProcedureBuilder<
|
|
25
|
+
InferMiddlewareOutput<TMiddleware>,
|
|
26
|
+
MergeInput<TInput, TInputExt>
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
use<TMiddlewares extends readonly Middleware<any, TContext, any>[]>(
|
|
30
|
+
middlewares: [...TMiddlewares],
|
|
31
|
+
): NextProcedureBuilder<
|
|
32
|
+
InferMiddlewareArrayOutput<TMiddlewares>,
|
|
33
|
+
MergeInput<TInput, InferMiddlewareArrayInput<TMiddlewares>>
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
input<TSchema extends StandardSchemaV1>(
|
|
37
|
+
schema: TSchema,
|
|
38
|
+
): NextProcedureBuilder<
|
|
39
|
+
TContext,
|
|
40
|
+
TInput & StandardSchemaV1.InferInput<TSchema>
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
params<T, TSchema extends StandardSchemaV1 | undefined = undefined>(
|
|
44
|
+
schema?: TSchema,
|
|
45
|
+
): NextProcedureBuilder<
|
|
46
|
+
Prettify<
|
|
47
|
+
TContext & {
|
|
48
|
+
params: TSchema extends StandardSchemaV1
|
|
49
|
+
? StandardSchemaV1.InferOutput<TSchema>
|
|
50
|
+
: T;
|
|
51
|
+
}
|
|
52
|
+
>,
|
|
53
|
+
Prettify<
|
|
54
|
+
TInput & {
|
|
55
|
+
params: Promise<
|
|
56
|
+
TSchema extends StandardSchemaV1
|
|
57
|
+
? StandardSchemaV1.InferOutput<TSchema>
|
|
58
|
+
: T
|
|
59
|
+
>;
|
|
60
|
+
}
|
|
61
|
+
>
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
searchParams<TSchema extends StandardSchemaV1>(
|
|
65
|
+
schema: TSchema,
|
|
66
|
+
): NextProcedureBuilder<
|
|
67
|
+
Prettify<
|
|
68
|
+
TContext & { searchParams: StandardSchemaV1.InferOutput<TSchema> }
|
|
69
|
+
>,
|
|
70
|
+
Prettify<
|
|
71
|
+
TInput & {
|
|
72
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
73
|
+
}
|
|
74
|
+
>
|
|
75
|
+
>;
|
|
76
|
+
|
|
77
|
+
run: Run<TContext, TInput>;
|
|
78
|
+
page: Run<
|
|
79
|
+
TContext,
|
|
80
|
+
Prettify<
|
|
81
|
+
TInput & {
|
|
82
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
83
|
+
params: Promise<{ [key: string]: string }>;
|
|
84
|
+
}
|
|
85
|
+
>,
|
|
86
|
+
ReactNode
|
|
87
|
+
>;
|
|
88
|
+
rsc: Run<TContext, TInput, ReactNode>;
|
|
89
|
+
metadata: Run<TContext, TInput, Metadata>;
|
|
90
|
+
layout: Run<TContext, TInput & { children: ReactNode }, ReactNode>;
|
|
91
|
+
layoutMetadata: Run<
|
|
92
|
+
Prettify<Omit<TContext, "searchParams">>,
|
|
93
|
+
Prettify<Omit<TInput, "searchParams">>,
|
|
94
|
+
Metadata
|
|
95
|
+
>;
|
|
96
|
+
|
|
97
|
+
staticParams: Run<TContext, TInput, ExtractParams<TContext>[]>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const createNextBuilder: ProcedureBuilderFactory = (
|
|
101
|
+
middlewares: InternalMiddleware[],
|
|
102
|
+
factory?: ProcedureBuilderFactory,
|
|
103
|
+
) => {
|
|
104
|
+
const core = createProcedureBuilder(
|
|
105
|
+
middlewares,
|
|
106
|
+
factory ?? createNextBuilder,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
...core,
|
|
111
|
+
|
|
112
|
+
params() {
|
|
113
|
+
const mw = createMiddleware(
|
|
114
|
+
"NEXT_PARAMS",
|
|
115
|
+
async ({ input, ctx, next }: any) => {
|
|
116
|
+
const params = await input.params;
|
|
117
|
+
|
|
118
|
+
return next({ ctx: { ...ctx, params } });
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
return createNextBuilder([...middlewares, mw], factory) as any;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
searchParams(schema: StandardSchemaV1) {
|
|
125
|
+
const mw = createMiddleware(
|
|
126
|
+
"NEXT_SEARCH_PARAMS",
|
|
127
|
+
async ({ input, ctx, next }: any) => {
|
|
128
|
+
const searchParams = await input.searchParams;
|
|
129
|
+
|
|
130
|
+
const result = await schema["~standard"].validate(searchParams);
|
|
131
|
+
if (result.issues) throw new Error("Invalid search params");
|
|
132
|
+
return next({ ctx: { ...ctx, searchParams: result.value } });
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
return createNextBuilder([...middlewares, mw], factory) as any;
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
page: core.run,
|
|
139
|
+
rsc: core.run,
|
|
140
|
+
metadata: core.run,
|
|
141
|
+
layout: core.run,
|
|
142
|
+
layoutMetadata: core.run,
|
|
143
|
+
staticParams: core.run,
|
|
144
|
+
} as any;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const init = <TContext>(options: InitOptions<TContext>) => {
|
|
148
|
+
const core = coreInit(options);
|
|
149
|
+
const initialMiddlewares: InternalMiddleware[] = [
|
|
150
|
+
createMiddleware("INITIAL_CONTEXT", async ({ next }: any) => {
|
|
151
|
+
return next({ ctx: await options.ctx() });
|
|
152
|
+
}),
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
middleware: core.middleware,
|
|
157
|
+
procedure: createNextBuilder(
|
|
158
|
+
initialMiddlewares,
|
|
159
|
+
) as unknown as NextProcedureBuilder<TContext>,
|
|
160
|
+
};
|
|
161
|
+
};
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { init as coreInit, type InitOptions } from "./core";
|
|
4
|
+
import {
|
|
5
|
+
createMiddleware,
|
|
6
|
+
createProcedureBuilder,
|
|
7
|
+
type InferMiddlewareOutput,
|
|
8
|
+
type InternalMiddleware,
|
|
9
|
+
type MergeInput,
|
|
10
|
+
type Middleware,
|
|
11
|
+
type ProcedureBuilderFactory,
|
|
12
|
+
type Run,
|
|
13
|
+
} from "./core-internal";
|
|
14
|
+
|
|
15
|
+
interface ReactProcedureBuilder<TContext, TInput = Record<string, never>> {
|
|
16
|
+
use<TInputExt, TMiddleware extends Middleware<TInputExt, TContext, any>>(
|
|
17
|
+
middleware: TMiddleware,
|
|
18
|
+
): ReactProcedureBuilder<
|
|
19
|
+
InferMiddlewareOutput<TMiddleware>,
|
|
20
|
+
MergeInput<TInput, TInputExt>
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
input<TSchema extends StandardSchemaV1>(
|
|
24
|
+
schema: TSchema,
|
|
25
|
+
): ReactProcedureBuilder<
|
|
26
|
+
TContext,
|
|
27
|
+
MergeInput<TInput, StandardSchemaV1.InferInput<TSchema>>
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
run: Run<TContext, TInput>;
|
|
31
|
+
rsc: Run<TContext, TInput, ReactNode>;
|
|
32
|
+
serverFn: Run<TContext, TInput>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const createReactBuilder: ProcedureBuilderFactory = (
|
|
36
|
+
middlewares: InternalMiddleware[],
|
|
37
|
+
factory?: ProcedureBuilderFactory,
|
|
38
|
+
) => {
|
|
39
|
+
const core = createProcedureBuilder(
|
|
40
|
+
middlewares,
|
|
41
|
+
factory ?? createReactBuilder,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
...core,
|
|
46
|
+
rsc: core.run,
|
|
47
|
+
serverFn: core.run,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const init = <TContext>(options: InitOptions<TContext>) => {
|
|
52
|
+
const core = coreInit(options);
|
|
53
|
+
const initialMiddlewares: InternalMiddleware[] = [
|
|
54
|
+
createMiddleware("INITIAL_CONTEXT", async ({ next }: any) => {
|
|
55
|
+
return next({ ctx: await options.ctx() });
|
|
56
|
+
}),
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
middleware: core.middleware,
|
|
61
|
+
procedure: createReactBuilder(initialMiddlewares) as ReactProcedureBuilder<
|
|
62
|
+
TContext,
|
|
63
|
+
Record<string, never>
|
|
64
|
+
>,
|
|
65
|
+
};
|
|
66
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { expectTypeOf, test } from "vitest";
|
|
3
|
+
import { init } from "../src/core";
|
|
4
|
+
import type { Middleware } from "../src/core-internal";
|
|
5
|
+
|
|
6
|
+
const createSchema = <T>(): StandardSchemaV1<T> =>
|
|
7
|
+
({
|
|
8
|
+
"~standard": {
|
|
9
|
+
version: 1,
|
|
10
|
+
vendor: "test",
|
|
11
|
+
validate: () => ({ value: {} as T }),
|
|
12
|
+
},
|
|
13
|
+
}) as StandardSchemaV1<T>;
|
|
14
|
+
|
|
15
|
+
const factory = init({ ctx: () => ({ userId: "123" }) });
|
|
16
|
+
|
|
17
|
+
test("init - context propagates to procedure", () => {
|
|
18
|
+
factory.procedure.run(({ ctx }) => {
|
|
19
|
+
expectTypeOf(ctx).toEqualTypeOf<{ userId: string }>();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("use - single middleware updates context", () => {
|
|
24
|
+
const mw: Middleware<
|
|
25
|
+
unknown,
|
|
26
|
+
{ userId: string },
|
|
27
|
+
{ role: "admin" | "user" }
|
|
28
|
+
> = ({ next }) => next({ ctx: { role: "admin" } });
|
|
29
|
+
|
|
30
|
+
factory.procedure.use(mw).run(({ ctx }) => {
|
|
31
|
+
expectTypeOf(ctx).toEqualTypeOf<{ role: "admin" | "user" }>();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("use - array of middlewares merges contexts", () => {
|
|
36
|
+
const mw1: Middleware<unknown, { userId: string }, { a: number }> = ({
|
|
37
|
+
next,
|
|
38
|
+
}) => next({ ctx: { a: 1 } });
|
|
39
|
+
const mw2: Middleware<unknown, { userId: string }, { b: string }> = ({
|
|
40
|
+
next,
|
|
41
|
+
}) => next({ ctx: { b: "hello" } });
|
|
42
|
+
|
|
43
|
+
factory.procedure.use([mw1, mw2]).run(({ ctx }) => {
|
|
44
|
+
expectTypeOf(ctx).toEqualTypeOf<{ a: number; b: string }>();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("input - schema type added to input", () => {
|
|
49
|
+
const schema = createSchema<{ name: string }>();
|
|
50
|
+
|
|
51
|
+
factory.procedure.input(schema).run(({ input }) => {
|
|
52
|
+
expectTypeOf(input).toEqualTypeOf<{ name: string }>();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("run - without input returns no-arg function", () => {
|
|
57
|
+
const fn = factory.procedure.run(() => "result");
|
|
58
|
+
expectTypeOf(fn).toEqualTypeOf<() => Promise<string>>();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("run - with input returns function with arg", () => {
|
|
62
|
+
const schema = createSchema<{ id: number }>();
|
|
63
|
+
const fn = factory.procedure.input(schema).run(({ input }) => input.id);
|
|
64
|
+
expectTypeOf(fn).toEqualTypeOf<(input: { id: number }) => Promise<number>>();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("run - with partial input returns function with optional arg", () => {
|
|
68
|
+
const schema = createSchema<{ id?: number }>();
|
|
69
|
+
const fn = factory.procedure.input(schema).run(() => "result");
|
|
70
|
+
expectTypeOf(fn).toEqualTypeOf<
|
|
71
|
+
(input?: { id?: number }) => Promise<string>
|
|
72
|
+
>();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("middleware factory - preserves types", () => {
|
|
76
|
+
const mw = factory.middleware(({ ctx, input, next }) => {
|
|
77
|
+
expectTypeOf(ctx).toEqualTypeOf<{ userId: string }>();
|
|
78
|
+
expectTypeOf(input).toEqualTypeOf<unknown>();
|
|
79
|
+
return next({ ctx: { role: "admin" as const } });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expectTypeOf(mw).toEqualTypeOf<
|
|
83
|
+
Middleware<unknown, { userId: string }, { role: "admin" }>
|
|
84
|
+
>();
|
|
85
|
+
|
|
86
|
+
factory.procedure.use(mw).run(({ ctx }) => {
|
|
87
|
+
expectTypeOf(ctx).toEqualTypeOf<{ role: "admin" }>();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { init } from "../src/core";
|
|
4
|
+
|
|
5
|
+
const createSchema = <T>(): StandardSchemaV1<T> =>
|
|
6
|
+
({
|
|
7
|
+
"~standard": {
|
|
8
|
+
version: 1,
|
|
9
|
+
vendor: "test",
|
|
10
|
+
validate: (value: unknown) => ({ value: value as T }),
|
|
11
|
+
},
|
|
12
|
+
}) as StandardSchemaV1<T>;
|
|
13
|
+
|
|
14
|
+
const factory = init({ ctx: () => ({ userId: "123" }) });
|
|
15
|
+
|
|
16
|
+
describe("init", () => {
|
|
17
|
+
it("provides initial context", async () => {
|
|
18
|
+
const fn = factory.procedure.run(({ ctx }) => ctx.userId);
|
|
19
|
+
expect(await fn()).toBe("123");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("use", () => {
|
|
24
|
+
it("single middleware extends context", async () => {
|
|
25
|
+
const fn = factory.procedure
|
|
26
|
+
.use(({ next }) => next({ ctx: { role: "admin" } }))
|
|
27
|
+
.run(({ ctx }) => ctx.role);
|
|
28
|
+
|
|
29
|
+
expect(await fn()).toBe("admin");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("array of middlewares merges contexts", async () => {
|
|
33
|
+
const fn = factory.procedure
|
|
34
|
+
.use([
|
|
35
|
+
({ next }) => next({ ctx: { a: 1 } }),
|
|
36
|
+
({ next }) => next({ ctx: { b: 2 } }),
|
|
37
|
+
])
|
|
38
|
+
.run(({ ctx }) => ctx.a + ctx.b);
|
|
39
|
+
|
|
40
|
+
expect(await fn()).toBe(3);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("input", () => {
|
|
45
|
+
it("passes input to run function", async () => {
|
|
46
|
+
const schema = createSchema<{ x: number }>();
|
|
47
|
+
const fn = factory.procedure.input(schema).run(({ input }) => input.x * 2);
|
|
48
|
+
expect(await fn({ x: 5 })).toBe(10);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("run", () => {
|
|
53
|
+
it("returns the result", async () => {
|
|
54
|
+
const fn = factory.procedure.run(() => "hello");
|
|
55
|
+
expect(await fn()).toBe("hello");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { Metadata } from "next";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { expectTypeOf, test } from "vitest";
|
|
5
|
+
import { init } from "../src/next";
|
|
6
|
+
|
|
7
|
+
const createSchema = <T>(): StandardSchemaV1<T> =>
|
|
8
|
+
({
|
|
9
|
+
"~standard": {
|
|
10
|
+
version: 1,
|
|
11
|
+
vendor: "test",
|
|
12
|
+
validate: () => ({ value: {} as T }),
|
|
13
|
+
},
|
|
14
|
+
}) as StandardSchemaV1<T>;
|
|
15
|
+
|
|
16
|
+
const factory = init({ ctx: () => ({ userId: "123" }) });
|
|
17
|
+
|
|
18
|
+
test("init - context propagates to procedure", () => {
|
|
19
|
+
factory.procedure.run(({ ctx }) => {
|
|
20
|
+
expectTypeOf(ctx).toEqualTypeOf<{ userId: string }>();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("params - adds params to context without schema", () => {
|
|
25
|
+
factory.procedure.params<{ slug: string }>().run(({ ctx, input }) => {
|
|
26
|
+
expectTypeOf(ctx).toExtend<{ userId: string; params: { slug: string } }>();
|
|
27
|
+
return input;
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("params - adds params to context with schema", () => {
|
|
32
|
+
const schema = createSchema<{ id: number }>();
|
|
33
|
+
|
|
34
|
+
factory.procedure
|
|
35
|
+
.params<{ id: number }, typeof schema>(schema)
|
|
36
|
+
.run(({ ctx }) => {
|
|
37
|
+
expectTypeOf(ctx).toExtend<{ userId: string; params: { id: number } }>();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("params - input requires Promise<params>", () => {
|
|
42
|
+
const fn = factory.procedure
|
|
43
|
+
.params<{ slug: string }>()
|
|
44
|
+
.run(({ ctx }) => ctx.params.slug);
|
|
45
|
+
|
|
46
|
+
expectTypeOf(fn).toEqualTypeOf<
|
|
47
|
+
(input: { params: Promise<{ slug: string }> }) => Promise<string>
|
|
48
|
+
>();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("searchParams - adds validated searchParams to context", () => {
|
|
52
|
+
const schema = createSchema<{ page: number; limit: number }>();
|
|
53
|
+
|
|
54
|
+
factory.procedure.searchParams(schema).run(({ ctx, input }) => {
|
|
55
|
+
expectTypeOf(ctx).toExtend<{
|
|
56
|
+
userId: string;
|
|
57
|
+
searchParams: { page: number; limit: number };
|
|
58
|
+
}>();
|
|
59
|
+
return input;
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("searchParams - input requires Promise<Record<string, ...>>", () => {
|
|
64
|
+
const schema = createSchema<{ q: string }>();
|
|
65
|
+
const fn = factory.procedure
|
|
66
|
+
.searchParams(schema)
|
|
67
|
+
.run(({ ctx }) => ctx.searchParams.q);
|
|
68
|
+
|
|
69
|
+
expectTypeOf(fn).toEqualTypeOf<
|
|
70
|
+
(input: {
|
|
71
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
72
|
+
}) => Promise<string>
|
|
73
|
+
>();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("page - returns ReactNode and accepts page props", () => {
|
|
77
|
+
const fn = factory.procedure.page(() => null as ReactNode);
|
|
78
|
+
|
|
79
|
+
expectTypeOf(fn).toBeFunction();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("page - context available in page handler", () => {
|
|
83
|
+
factory.procedure.page(({ ctx }) => {
|
|
84
|
+
expectTypeOf(ctx).toEqualTypeOf<{ userId: string }>();
|
|
85
|
+
return null;
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("rsc - returns ReactNode", () => {
|
|
90
|
+
const fn = factory.procedure.rsc(() => null as ReactNode);
|
|
91
|
+
|
|
92
|
+
expectTypeOf(fn).toBeFunction();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("metadata - returns Metadata type", () => {
|
|
96
|
+
const fn = factory.procedure.metadata(() => ({ title: "Test" }));
|
|
97
|
+
|
|
98
|
+
expectTypeOf(fn).toBeFunction();
|
|
99
|
+
expectTypeOf(fn).returns.resolves.toExtend<Metadata>();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("layout - receives children in input", () => {
|
|
103
|
+
factory.procedure.layout(({ input }) => {
|
|
104
|
+
expectTypeOf(input).toEqualTypeOf<{ children: ReactNode }>();
|
|
105
|
+
return null;
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("layout - function signature", () => {
|
|
110
|
+
const fn = factory.procedure.layout(() => null as ReactNode);
|
|
111
|
+
|
|
112
|
+
expectTypeOf(fn).toBeFunction();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("layoutMetadata - excludes searchParams from context", () => {
|
|
116
|
+
const searchSchema = createSchema<{ q: string }>();
|
|
117
|
+
|
|
118
|
+
factory.procedure.searchParams(searchSchema).layoutMetadata(({ ctx }) => {
|
|
119
|
+
expectTypeOf(ctx).toEqualTypeOf<{ userId: string }>();
|
|
120
|
+
return { title: "Layout" };
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("staticParams - returns array of params", () => {
|
|
125
|
+
const fn = factory.procedure
|
|
126
|
+
.params<{ slug: string }>()
|
|
127
|
+
.staticParams(() => [{ slug: "a" }, { slug: "b" }]);
|
|
128
|
+
|
|
129
|
+
type ParamsArray = { slug: string }[];
|
|
130
|
+
expectTypeOf(fn).returns.resolves.toEqualTypeOf<ParamsArray>();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("chaining params and searchParams", () => {
|
|
134
|
+
const paramsSchema = createSchema<{ id: string }>();
|
|
135
|
+
const searchSchema = createSchema<{ tab: string }>();
|
|
136
|
+
|
|
137
|
+
factory.procedure
|
|
138
|
+
.params<{ id: string }, typeof paramsSchema>(paramsSchema)
|
|
139
|
+
.searchParams(searchSchema)
|
|
140
|
+
.page(({ ctx }) => {
|
|
141
|
+
expectTypeOf(ctx).toExtend<{
|
|
142
|
+
userId: string;
|
|
143
|
+
params: { id: string };
|
|
144
|
+
searchParams: { tab: string };
|
|
145
|
+
}>();
|
|
146
|
+
return null;
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("middleware preserves input requirements through chain", () => {
|
|
151
|
+
const mw = factory.middleware(({ next }) =>
|
|
152
|
+
next({ ctx: { role: "admin" as const } }),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
factory.procedure
|
|
156
|
+
.use(mw)
|
|
157
|
+
.params<{ slug: string }>()
|
|
158
|
+
.page(({ ctx }) => {
|
|
159
|
+
expectTypeOf(ctx).toExtend<{ role: "admin"; params: { slug: string } }>();
|
|
160
|
+
return null;
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("middleware chain - function signature with middleware and params", () => {
|
|
165
|
+
const mw = factory.middleware(({ next }) =>
|
|
166
|
+
next({ ctx: { role: "admin" as const } }),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const fn = factory.procedure
|
|
170
|
+
.use(mw)
|
|
171
|
+
.params<{ slug: string }>()
|
|
172
|
+
.run(({ ctx }) => ctx.params.slug);
|
|
173
|
+
|
|
174
|
+
expectTypeOf(fn).toEqualTypeOf<
|
|
175
|
+
(input: { params: Promise<{ slug: string }> }) => Promise<string>
|
|
176
|
+
>();
|
|
177
|
+
});
|