@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +1 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.cjs +14 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.mjs +14 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs +27 -0
  6. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs +27 -0
  7. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.cjs +16 -0
  8. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.mjs +16 -0
  9. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.cjs +11 -0
  10. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.mjs +11 -0
  11. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.cjs +18 -0
  12. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.mjs +12 -0
  13. package/dist/core-internal.cjs +71 -0
  14. package/dist/core-internal.d.cts +33 -0
  15. package/dist/core-internal.d.cts.map +1 -0
  16. package/dist/core-internal.d.mts +33 -0
  17. package/dist/core-internal.d.mts.map +1 -0
  18. package/dist/core-internal.mjs +71 -0
  19. package/dist/core-internal.mjs.map +1 -0
  20. package/dist/core.cjs +12 -0
  21. package/dist/core.d.cts +15 -0
  22. package/dist/core.d.cts.map +1 -0
  23. package/dist/core.d.mts +15 -0
  24. package/dist/core.d.mts.map +1 -0
  25. package/dist/core.mjs +13 -0
  26. package/dist/core.mjs.map +1 -0
  27. package/dist/index.cjs +3 -0
  28. package/dist/index.d.cts +2 -0
  29. package/dist/index.d.mts +2 -0
  30. package/dist/index.mjs +3 -0
  31. package/dist/next.cjs +45 -0
  32. package/dist/next.d.cts +47 -0
  33. package/dist/next.d.cts.map +1 -0
  34. package/dist/next.d.mts +47 -0
  35. package/dist/next.d.mts.map +1 -0
  36. package/dist/next.mjs +46 -0
  37. package/dist/next.mjs.map +1 -0
  38. package/dist/react.cjs +25 -0
  39. package/dist/react.d.cts +20 -0
  40. package/dist/react.d.cts.map +1 -0
  41. package/dist/react.d.mts +20 -0
  42. package/dist/react.d.mts.map +1 -0
  43. package/dist/react.mjs +26 -0
  44. package/dist/react.mjs.map +1 -0
  45. package/dist/types.d.cts +6 -0
  46. package/dist/types.d.cts.map +1 -0
  47. package/dist/types.d.mts +6 -0
  48. package/dist/types.d.mts.map +1 -0
  49. package/package.json +64 -0
  50. package/src/core-internal.ts +206 -0
  51. package/src/core.ts +29 -0
  52. package/src/index.ts +1 -0
  53. package/src/next.ts +161 -0
  54. package/src/react.ts +66 -0
  55. package/src/types.ts +4 -0
  56. package/test/core.test-d.ts +89 -0
  57. package/test/core.test.ts +57 -0
  58. package/test/next.test-d.ts +177 -0
  59. package/test/next.test.ts +230 -0
  60. package/tsconfig.build.json +7 -0
  61. package/tsconfig.json +5 -0
  62. package/tsdown.config.ts +23 -0
  63. package/vitest.config.ts +10 -0
@@ -0,0 +1,230 @@
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
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "declarationDir": "dist"
5
+ },
6
+ "include": ["src"]
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["src", "test"],
4
+ "compilerOptions": {}
5
+ }
@@ -0,0 +1,23 @@
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
+ });
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ exclude: ["node_modules", "dist"],
6
+ typecheck: {
7
+ enabled: true,
8
+ },
9
+ },
10
+ });