@jlnstack/procedure 0.0.2 → 0.0.3
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/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/defineProperty.cjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/defineProperty.mjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/objectSpread2.cjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/objectSpread2.mjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/toPrimitive.cjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/toPrimitive.mjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/toPropertyKey.cjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/toPropertyKey.mjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/typeof.cjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.103.0 → _@oxc-project_runtime@0.106.0}/helpers/typeof.mjs +1 -1
- package/dist/core-internal.cjs +1 -1
- package/dist/core-internal.mjs +1 -1
- package/dist/core-internal.mjs.map +1 -1
- package/dist/next.cjs +1 -1
- package/dist/next.mjs +1 -1
- package/dist/next.mjs.map +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/package.json +6 -2
- package/CHANGELOG.md +0 -19
- package/src/core-internal.ts +0 -206
- package/src/core.ts +0 -29
- package/src/index.ts +0 -1
- package/src/next.ts +0 -161
- package/src/react.ts +0 -66
- package/src/types.ts +0 -4
- package/test/core.test-d.ts +0 -89
- package/test/core.test.ts +0 -57
- package/test/next.test-d.ts +0 -177
- package/test/next.test.ts +0 -230
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -5
- package/tsdown.config.ts +0 -23
- package/vitest.config.ts +0 -10
package/src/react.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
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
DELETED
package/test/core.test-d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
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
|
-
});
|
package/test/core.test.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
});
|
package/test/next.test-d.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
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
|
-
});
|
package/test/next.test.ts
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { init } from "../src/next";
|
|
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 createFailingSchema = <T>(): StandardSchemaV1<T> =>
|
|
15
|
-
({
|
|
16
|
-
"~standard": {
|
|
17
|
-
version: 1,
|
|
18
|
-
vendor: "test",
|
|
19
|
-
validate: () => ({ issues: [{ message: "Invalid" }] }),
|
|
20
|
-
},
|
|
21
|
-
}) as StandardSchemaV1<T>;
|
|
22
|
-
|
|
23
|
-
const factory = init({ ctx: () => ({ userId: "123" }) });
|
|
24
|
-
|
|
25
|
-
describe("init", () => {
|
|
26
|
-
it("provides initial context", async () => {
|
|
27
|
-
const fn = factory.procedure.run(({ ctx }) => ctx.userId);
|
|
28
|
-
expect(await (fn as () => Promise<string>)()).toBe("123");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("returns middleware factory", () => {
|
|
32
|
-
const mw = factory.middleware(({ next }) =>
|
|
33
|
-
next({ ctx: { role: "admin" } }),
|
|
34
|
-
);
|
|
35
|
-
expect(typeof mw).toBe("function");
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe("params", () => {
|
|
40
|
-
it("unwraps params promise and adds to context", async () => {
|
|
41
|
-
const fn = factory.procedure
|
|
42
|
-
.params<{ slug: string }>()
|
|
43
|
-
.run(({ ctx }) => ctx.params.slug);
|
|
44
|
-
|
|
45
|
-
const result = await fn({ params: Promise.resolve({ slug: "hello" }) });
|
|
46
|
-
expect(result).toBe("hello");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("works with schema validation", async () => {
|
|
50
|
-
const schema = createSchema<{ id: string }>();
|
|
51
|
-
const fn = factory.procedure
|
|
52
|
-
.params<{ id: string }, typeof schema>(schema)
|
|
53
|
-
.run(({ ctx }) => ctx.params.id);
|
|
54
|
-
|
|
55
|
-
const result = await fn({ params: Promise.resolve({ id: "42" }) });
|
|
56
|
-
expect(result).toBe("42");
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe("searchParams", () => {
|
|
61
|
-
it("validates and adds searchParams to context", async () => {
|
|
62
|
-
const schema = createSchema<{ page: string }>();
|
|
63
|
-
const fn = factory.procedure
|
|
64
|
-
.searchParams(schema)
|
|
65
|
-
.run(({ ctx }) => ctx.searchParams.page);
|
|
66
|
-
|
|
67
|
-
const result = await fn({ searchParams: Promise.resolve({ page: "1" }) });
|
|
68
|
-
expect(result).toBe("1");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("throws on invalid searchParams", async () => {
|
|
72
|
-
const schema = createFailingSchema<{ page: string }>();
|
|
73
|
-
const fn = factory.procedure
|
|
74
|
-
.searchParams(schema)
|
|
75
|
-
.run(({ ctx }) => ctx.searchParams.page);
|
|
76
|
-
|
|
77
|
-
await expect(
|
|
78
|
-
fn({ searchParams: Promise.resolve({ page: "1" }) }),
|
|
79
|
-
).rejects.toThrow("Invalid search params");
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe("page", () => {
|
|
84
|
-
it("is an alias for run", async () => {
|
|
85
|
-
const fn = factory.procedure.page(({ ctx }) => `user-${ctx.userId}`);
|
|
86
|
-
const result = await fn({
|
|
87
|
-
searchParams: Promise.resolve({}),
|
|
88
|
-
params: Promise.resolve({}),
|
|
89
|
-
});
|
|
90
|
-
expect(result).toBe("user-123");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("works with params and searchParams", async () => {
|
|
94
|
-
const paramsSchema = createSchema<{ slug: string }>();
|
|
95
|
-
const searchSchema = createSchema<{ q: string }>();
|
|
96
|
-
|
|
97
|
-
const fn = factory.procedure
|
|
98
|
-
.params<{ slug: string }, typeof paramsSchema>(paramsSchema)
|
|
99
|
-
.searchParams(searchSchema)
|
|
100
|
-
.page(({ ctx }) => `${ctx.params.slug}-${ctx.searchParams.q}`);
|
|
101
|
-
|
|
102
|
-
const result = await fn({
|
|
103
|
-
params: Promise.resolve({ slug: "post" }),
|
|
104
|
-
searchParams: Promise.resolve({ q: "test" }),
|
|
105
|
-
});
|
|
106
|
-
expect(result).toBe("post-test");
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe("rsc", () => {
|
|
111
|
-
it("executes and returns value", async () => {
|
|
112
|
-
const fn = factory.procedure.rsc(() => "RSC Content");
|
|
113
|
-
expect(await (fn as () => Promise<string>)()).toBe("RSC Content");
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe("metadata", () => {
|
|
118
|
-
it("returns Metadata object", async () => {
|
|
119
|
-
const fn = factory.procedure.metadata(() => ({ title: "My Page" }));
|
|
120
|
-
expect(await (fn as () => Promise<{ title: string }>)()).toEqual({
|
|
121
|
-
title: "My Page",
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("can access context for dynamic metadata", async () => {
|
|
126
|
-
const fn = factory.procedure.metadata(({ ctx }) => ({
|
|
127
|
-
title: `User ${ctx.userId}`,
|
|
128
|
-
}));
|
|
129
|
-
expect(await (fn as () => Promise<{ title: string }>)()).toEqual({
|
|
130
|
-
title: "User 123",
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe("layout", () => {
|
|
136
|
-
it("receives children in input", async () => {
|
|
137
|
-
const fn = factory.procedure.layout(
|
|
138
|
-
({ input }) => `wrapped: ${input.children}`,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const result = await fn({ children: "Content" });
|
|
142
|
-
expect(result).toBe("wrapped: Content");
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("layoutMetadata", () => {
|
|
147
|
-
it("returns Metadata for layouts", async () => {
|
|
148
|
-
const fn = factory.procedure.layoutMetadata(() => ({
|
|
149
|
-
description: "Layout desc",
|
|
150
|
-
}));
|
|
151
|
-
expect(await (fn as () => Promise<{ description: string }>)()).toEqual({
|
|
152
|
-
description: "Layout desc",
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe("staticParams", () => {
|
|
158
|
-
it("returns array of params for static generation", async () => {
|
|
159
|
-
const fn = factory.procedure
|
|
160
|
-
.params<{ slug: string }>()
|
|
161
|
-
.staticParams(() => [{ slug: "post-1" }, { slug: "post-2" }]);
|
|
162
|
-
|
|
163
|
-
const result = await fn({ params: Promise.resolve({ slug: "" }) });
|
|
164
|
-
expect(result).toEqual([{ slug: "post-1" }, { slug: "post-2" }]);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("chaining", () => {
|
|
169
|
-
it("chains params with middleware", async () => {
|
|
170
|
-
const authMiddleware = factory.middleware(({ ctx, next }) =>
|
|
171
|
-
next({ ctx: { ...ctx, isAuth: true } }),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const fn = factory.procedure
|
|
175
|
-
.use(authMiddleware)
|
|
176
|
-
.params<{ id: string }>()
|
|
177
|
-
.page(({ ctx }) => `${ctx.isAuth}-${ctx.params.id}`);
|
|
178
|
-
|
|
179
|
-
const result = await fn({
|
|
180
|
-
params: Promise.resolve({ id: "42" }),
|
|
181
|
-
searchParams: Promise.resolve({}),
|
|
182
|
-
});
|
|
183
|
-
expect(result).toBe("true-42");
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("chains multiple middlewares with next-specific methods", async () => {
|
|
187
|
-
const mw1 = factory.middleware(({ next }) => next({ ctx: { a: 1 } }));
|
|
188
|
-
const mw2 = factory.middleware(({ next }) => next({ ctx: { b: 2 } }));
|
|
189
|
-
|
|
190
|
-
const fn = factory.procedure
|
|
191
|
-
.use([mw1, mw2])
|
|
192
|
-
.params<{ slug: string }>()
|
|
193
|
-
.searchParams(createSchema<{ q: string }>())
|
|
194
|
-
.page(({ ctx }) => ctx.a + ctx.b);
|
|
195
|
-
|
|
196
|
-
const result = await (fn as any)({
|
|
197
|
-
params: Promise.resolve({ slug: "test" }),
|
|
198
|
-
searchParams: Promise.resolve({ q: "query" }),
|
|
199
|
-
});
|
|
200
|
-
expect(result).toBe(3);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("preserves context through entire chain", async () => {
|
|
204
|
-
const fn = factory.procedure
|
|
205
|
-
.use(({ next }) => next({ ctx: { step1: true } }))
|
|
206
|
-
.use(({ ctx, next }) => next({ ctx: { ...ctx, step2: true } }))
|
|
207
|
-
.params<{ id: string }>()
|
|
208
|
-
.page(({ ctx }) => `${ctx.step1}-${ctx.step2}-${ctx.params.id}`);
|
|
209
|
-
|
|
210
|
-
const result = await fn({
|
|
211
|
-
params: Promise.resolve({ id: "test" }),
|
|
212
|
-
searchParams: Promise.resolve({}),
|
|
213
|
-
});
|
|
214
|
-
expect(result).toBe("true-true-test");
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe("async context", () => {
|
|
219
|
-
it("handles async initial context", async () => {
|
|
220
|
-
const asyncFactory = init({
|
|
221
|
-
ctx: async () => {
|
|
222
|
-
await Promise.resolve();
|
|
223
|
-
return { asyncValue: "loaded" };
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
const fn = asyncFactory.procedure.run(({ ctx }) => ctx.asyncValue);
|
|
228
|
-
expect(await (fn as () => Promise<string>)()).toBe("loaded");
|
|
229
|
-
});
|
|
230
|
-
});
|
package/tsconfig.build.json
DELETED
package/tsconfig.json
DELETED
package/tsdown.config.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "tsdown";
|
|
2
|
-
|
|
3
|
-
export const input = [
|
|
4
|
-
"src/index.ts",
|
|
5
|
-
"src/core.ts",
|
|
6
|
-
"src/react.ts",
|
|
7
|
-
"src/next.ts",
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
export default defineConfig({
|
|
11
|
-
target: ["node18", "es2017"],
|
|
12
|
-
entry: input,
|
|
13
|
-
dts: {
|
|
14
|
-
sourcemap: true,
|
|
15
|
-
tsconfig: "./tsconfig.build.json",
|
|
16
|
-
},
|
|
17
|
-
unbundle: true,
|
|
18
|
-
format: ["cjs", "esm"],
|
|
19
|
-
outExtensions: (ctx) => ({
|
|
20
|
-
dts: ctx.format === "cjs" ? ".d.cts" : ".d.mts",
|
|
21
|
-
js: ctx.format === "cjs" ? ".cjs" : ".mjs",
|
|
22
|
-
}),
|
|
23
|
-
});
|