@fragno-dev/corpus 0.0.2 → 0.0.4

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.
@@ -1,272 +0,0 @@
1
- # Defining Routes
2
-
3
- Routes are the core of a Fragno fragment, defining HTTP endpoints that handle requests and return
4
- responses. This guide covers the essential patterns for defining routes.
5
-
6
- ```typescript @fragno-imports
7
- import { defineRoute, defineRoutes, defineFragment, createFragment } from "@fragno-dev/core";
8
- import type { FragnoPublicConfig } from "@fragno-dev/core";
9
- import { z } from "zod";
10
- ```
11
-
12
- ## Basic GET Route
13
-
14
- A simple GET route with an output schema.
15
-
16
- ```typescript @fragno-test:route
17
- // should define a basic GET route
18
- const basicGetRoute = defineRoute({
19
- method: "GET",
20
- path: "/hello",
21
- outputSchema: z.string(),
22
- handler: async (_, { json }) => {
23
- return json("Hello, World!");
24
- },
25
- });
26
-
27
- expect(basicGetRoute.method).toBe("GET");
28
- expect(basicGetRoute.path).toBe("/hello");
29
- ```
30
-
31
- The `outputSchema` uses Zod (or any Standard Schema compatible library) to define the response type.
32
- The handler receives a context object and helpers like `json()` for sending responses.
33
-
34
- ## POST Route with Input Schema
35
-
36
- Routes can accept and validate request bodies using `inputSchema`.
37
-
38
- ```typescript @fragno-test:route
39
- // should define a POST route with input schema
40
- const createItemRoute = defineRoute({
41
- method: "POST",
42
- path: "/items",
43
- inputSchema: z.object({
44
- name: z.string(),
45
- description: z.string().optional(),
46
- }),
47
- outputSchema: z.object({
48
- id: z.string(),
49
- name: z.string(),
50
- description: z.string().optional(),
51
- }),
52
- handler: async ({ input }, { json }) => {
53
- const data = await input.valid();
54
-
55
- return json({
56
- id: "item-123",
57
- name: data.name,
58
- description: data.description,
59
- });
60
- },
61
- });
62
-
63
- expect(createItemRoute.method).toBe("POST");
64
- expect(createItemRoute.inputSchema).toBeDefined();
65
- ```
66
-
67
- The `input.valid()` method validates the request body against the schema and returns the typed data.
68
-
69
- ## Query Parameters
70
-
71
- Routes can declare query parameters they expect to receive.
72
-
73
- ```typescript @fragno-test:route
74
- // should define a route with query parameters
75
- const listItemsRoute = defineRoute({
76
- method: "GET",
77
- path: "/items",
78
- queryParameters: ["page", "limit", "filter"],
79
- outputSchema: z.array(
80
- z.object({
81
- id: z.string(),
82
- name: z.string(),
83
- }),
84
- ),
85
- handler: async ({ query }, { json }) => {
86
- const page = query.get("page") || "1";
87
- const limit = query.get("limit") || "10";
88
- const filter = query.get("filter");
89
-
90
- return json([
91
- { id: "1", name: "Item 1" },
92
- { id: "2", name: "Item 2" },
93
- ]);
94
- },
95
- });
96
-
97
- expect(listItemsRoute.queryParameters).toEqual(["page", "limit", "filter"]);
98
- ```
99
-
100
- Query parameters are accessed via `query.get(name)` from the context.
101
-
102
- ## Error Handling
103
-
104
- Routes can define custom error codes and return errors with appropriate status codes.
105
-
106
- ```typescript @fragno-test:route
107
- // should define a route with error codes
108
- const validateItemRoute = defineRoute({
109
- method: "POST",
110
- path: "/validate",
111
- inputSchema: z.object({
112
- value: z.string(),
113
- }),
114
- outputSchema: z.object({ valid: z.boolean() }),
115
- errorCodes: ["INVALID_VALUE", "VALUE_TOO_SHORT"],
116
- handler: async ({ input }, { json, error }) => {
117
- const data = await input.valid();
118
-
119
- if (data.value.length < 3) {
120
- return error(
121
- {
122
- message: "Value must be at least 3 characters",
123
- code: "VALUE_TOO_SHORT",
124
- },
125
- 400,
126
- );
127
- }
128
-
129
- if (!/^[a-z]+$/.test(data.value)) {
130
- return error(
131
- {
132
- message: "Value must contain only lowercase letters",
133
- code: "INVALID_VALUE",
134
- },
135
- 400,
136
- );
137
- }
138
-
139
- return json({ valid: true });
140
- },
141
- });
142
-
143
- expect(validateItemRoute.errorCodes).toEqual(["INVALID_VALUE", "VALUE_TOO_SHORT"]);
144
- ```
145
-
146
- The `error()` helper sends an error response with a custom error code and HTTP status.
147
-
148
- ## Using Dependencies
149
-
150
- Routes can access dependencies defined in `withDependencies` through route factories.
151
-
152
- ```typescript
153
- interface AppConfig {
154
- apiKey: string;
155
- }
156
-
157
- interface AppDeps {
158
- config: AppConfig;
159
- timestamp: number;
160
- }
161
-
162
- export const routesWithDeps = defineRoutes<AppConfig, AppDeps>().create(({ deps }) => {
163
- return [
164
- defineRoute({
165
- method: "GET",
166
- path: "/config-info",
167
- outputSchema: z.object({
168
- hasApiKey: z.boolean(),
169
- timestamp: z.number(),
170
- }),
171
- handler: async (_, { json }) => {
172
- return json({
173
- hasApiKey: !!deps.config.apiKey,
174
- timestamp: deps.timestamp,
175
- });
176
- },
177
- }),
178
- ];
179
- });
180
- ```
181
-
182
- Dependencies are passed to the route factory function and can be used in route handlers.
183
-
184
- ## Using Services
185
-
186
- Services defined in `withServices` can be used in routes for business logic.
187
-
188
- ```typescript
189
- interface DataService {
190
- getData: () => string;
191
- processData: (input: string) => Promise<string>;
192
- }
193
-
194
- export const routesWithServices = defineRoutes<{}, {}, DataService>().create(({ services }) => {
195
- return [
196
- defineRoute({
197
- method: "GET",
198
- path: "/data",
199
- outputSchema: z.string(),
200
- handler: async (_, { json }) => {
201
- const data = services.getData();
202
- return json(data);
203
- },
204
- }),
205
- defineRoute({
206
- method: "POST",
207
- path: "/process",
208
- inputSchema: z.object({ input: z.string() }),
209
- outputSchema: z.string(),
210
- handler: async ({ input }, { json }) => {
211
- const { input: inputData } = await input.valid();
212
- const result = await services.processData(inputData);
213
- return json(result);
214
- },
215
- }),
216
- ];
217
- });
218
- ```
219
-
220
- Services provide reusable business logic that can be shared across multiple routes.
221
-
222
- ## Complete Fragment Example
223
-
224
- A complete example showing how routes integrate with fragment definition.
225
-
226
- ```typescript
227
- interface MyFragmentConfig {
228
- apiKey: string;
229
- }
230
-
231
- interface MyFragmentDeps {
232
- config: MyFragmentConfig;
233
- }
234
-
235
- interface MyFragmentServices {
236
- getStatus: () => string;
237
- }
238
-
239
- const myFragmentDefinition = defineFragment<MyFragmentConfig>("my-fragment")
240
- .withDependencies(({ config }) => {
241
- return {
242
- config,
243
- };
244
- })
245
- .withServices(({ deps }) => {
246
- return {
247
- getStatus: () => `API Key: ${deps.config.apiKey.substring(0, 3)}...`,
248
- };
249
- });
250
-
251
- const myRoutes = defineRoutes<MyFragmentConfig, MyFragmentDeps, MyFragmentServices>().create(
252
- ({ services }) => {
253
- return [
254
- defineRoute({
255
- method: "GET",
256
- path: "/status",
257
- outputSchema: z.string(),
258
- handler: async (_, { json }) => {
259
- return json(services.getStatus());
260
- },
261
- }),
262
- ];
263
- },
264
- );
265
-
266
- export function createMyFragment(config: MyFragmentConfig, options: FragnoPublicConfig = {}) {
267
- return createFragment(myFragmentDefinition, config, [myRoutes], options);
268
- }
269
- ```
270
-
271
- This example shows the complete flow: fragment definition with dependencies and services, route
272
- factory using those services, and the fragment creation function.
@@ -1,60 +0,0 @@
1
- # Drizzle Adapter
2
-
3
- The DrizzleAdapter connects Fragno's database API to your Drizzle ORM instance.
4
-
5
- ```typescript @fragno-imports
6
- import { DrizzleAdapter } from "@fragno-dev/db/adapters/drizzle";
7
- import { drizzle } from "drizzle-orm/node-postgres";
8
- import type { NodePgDatabase } from "drizzle-orm/node-postgres";
9
- ```
10
-
11
- ## Basic Setup
12
-
13
- Create a DrizzleAdapter with your Drizzle database instance and provider.
14
-
15
- ```typescript
16
- interface MyDatabase {
17
- users: {
18
- id: string;
19
- email: string;
20
- name: string;
21
- };
22
- posts: {
23
- id: string;
24
- title: string;
25
- content: string;
26
- authorId: string;
27
- };
28
- }
29
-
30
- declare const db: NodePgDatabase<MyDatabase>;
31
-
32
- export const adapter = new DrizzleAdapter({
33
- db,
34
- provider: "postgresql",
35
- });
36
- ```
37
-
38
- The adapter requires your Drizzle instance and the database provider (`"postgresql"`, `"mysql"`, or
39
- `"sqlite"`).
40
-
41
- ## Factory Function
42
-
43
- For async or sync database initialization, pass a factory function instead of a direct instance.
44
-
45
- ```typescript
46
- import type { PgliteDatabase } from "drizzle-orm/pglite";
47
-
48
- async function createDatabase(): Promise<PgliteDatabase> {
49
- // Async initialization logic
50
- const db = {} as PgliteDatabase;
51
- return db;
52
- }
53
-
54
- export const adapter = new DrizzleAdapter({
55
- db: createDatabase,
56
- provider: "postgresql",
57
- });
58
- ```
59
-
60
- Factory functions can also be synchronous for lazy initialization scenarios.
@@ -1,59 +0,0 @@
1
- # Kysely Adapter
2
-
3
- The KyselyAdapter connects Fragno's database API to your Kysely database instance.
4
-
5
- ```typescript @fragno-imports
6
- import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
7
- import { Kysely } from "kysely";
8
- import type { Dialect } from "kysely";
9
- ```
10
-
11
- ## Basic Setup
12
-
13
- Create a KyselyAdapter with your Kysely database instance and provider.
14
-
15
- ```typescript
16
- interface MyDatabase {
17
- users: {
18
- id: string;
19
- email: string;
20
- name: string;
21
- };
22
- posts: {
23
- id: string;
24
- title: string;
25
- content: string;
26
- authorId: string;
27
- };
28
- }
29
-
30
- declare const dialect: Dialect;
31
-
32
- export const db = new Kysely<MyDatabase>({
33
- dialect,
34
- });
35
-
36
- export const adapter = new KyselyAdapter({
37
- db,
38
- provider: "postgresql",
39
- });
40
- ```
41
-
42
- The adapter requires your Kysely instance and the database provider (`"postgresql"`, `"mysql"`, or
43
- `"sqlite"`).
44
-
45
- ## Factory Function
46
-
47
- For async database initialization, pass a factory function instead of a direct instance.
48
-
49
- ```typescript
50
- async function createDatabase() {
51
- // Async initialization logic
52
- return new Kysely({ dialect: {} as Dialect });
53
- }
54
-
55
- export const adapter = new KyselyAdapter({
56
- db: createDatabase,
57
- provider: "postgresql",
58
- });
59
- ```
package/src/test-setup.ts DELETED
@@ -1,110 +0,0 @@
1
- import { writeFileSync, mkdirSync } from "node:fs";
2
- import { join, dirname } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { loadAllSubjects } from "./parser";
5
-
6
- const __dirname = fileURLToPath(new URL(".", import.meta.url));
7
- const TEMP_TEST_DIR = join(__dirname, "../.corpus-tests");
8
-
9
- /**
10
- * Global setup for vitest that parses all markdown files
11
- * and generates temporary test files from @fragno-test blocks
12
- */
13
- export async function setup() {
14
- // Create temp directory (if it doesn't exist)
15
- mkdirSync(TEMP_TEST_DIR, { recursive: true });
16
-
17
- try {
18
- // Load all subjects
19
- const subjects = loadAllSubjects();
20
-
21
- // Generate test files for each subject
22
- for (const subject of subjects) {
23
- // Skip test generation if examples contain declare statements (documentation only)
24
- const allCode = subject.examples.map((example) => example.code).join("\n");
25
- if (allCode.includes("declare ")) {
26
- console.log(`⊘ Skipped ${subject.id} (contains declare statements - documentation only)`);
27
- continue;
28
- }
29
-
30
- // Separate examples by test type
31
- const routeTests = subject.examples.filter((ex) => ex.testType === "route");
32
- const databaseTests = subject.examples.filter((ex) => ex.testType === "database");
33
-
34
- // Skip if no actual tests
35
- if (routeTests.length === 0 && databaseTests.length === 0) {
36
- console.log(`⊘ Skipped ${subject.id} (no test directives found)`);
37
- continue;
38
- }
39
-
40
- const testFileName = `${subject.id}.generated.test.ts`;
41
- const testFilePath = join(TEMP_TEST_DIR, testFileName);
42
-
43
- // Generate test file content
44
- let testFileContent = `/* eslint-disable */
45
- // Auto-generated test file for subject: ${subject.title}
46
- // DO NOT EDIT - This file is generated by test-setup.ts
47
-
48
- import { describe, it, expect } from "vitest";
49
- ${subject.imports}
50
-
51
- ${subject.init ? `${subject.init}\n` : ""}
52
- `;
53
-
54
- // Generate route tests
55
- if (routeTests.length > 0) {
56
- testFileContent += `\ndescribe("${subject.id} - route tests", () => {\n`;
57
-
58
- for (let i = 0; i < routeTests.length; i++) {
59
- const example = routeTests[i];
60
- const testName = example.testName || `route test ${i + 1}`;
61
-
62
- testFileContent += ` it("${testName}", () => {\n`;
63
- // Indent the code
64
- const indentedCode = example.code
65
- .split("\n")
66
- .map((line) => ` ${line}`)
67
- .join("\n");
68
- testFileContent += `${indentedCode}\n`;
69
- testFileContent += ` });\n\n`;
70
- }
71
-
72
- testFileContent += `});\n`;
73
- }
74
-
75
- // Generate database tests
76
- if (databaseTests.length > 0) {
77
- testFileContent += `\ndescribe("${subject.id} - database tests", () => {\n`;
78
-
79
- for (let i = 0; i < databaseTests.length; i++) {
80
- const example = databaseTests[i];
81
- const testName = example.testName || `database test ${i + 1}`;
82
-
83
- testFileContent += ` it("${testName}", async () => {\n`;
84
- // Indent the code
85
- const indentedCode = example.code
86
- .split("\n")
87
- .map((line) => ` ${line}`)
88
- .join("\n");
89
- testFileContent += `${indentedCode}\n`;
90
- testFileContent += ` });\n\n`;
91
- }
92
-
93
- testFileContent += `});\n`;
94
- }
95
-
96
- // Ensure directory exists
97
- mkdirSync(dirname(testFilePath), { recursive: true });
98
-
99
- // Write test file
100
- writeFileSync(testFilePath, testFileContent, "utf-8");
101
- }
102
-
103
- console.log(`✓ Generated ${subjects.length} test file(s) in ${TEMP_TEST_DIR}`);
104
- } catch (error) {
105
- if (error instanceof Error) {
106
- console.error("Error generating test files:", error.message);
107
- }
108
- // Don't throw - allow tests to continue even if generation fails
109
- }
110
- }
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "@fragno-private/typescript-config/tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": ".",
6
- "composite": true
7
- },
8
- "include": ["src"],
9
- "exclude": ["node_modules", "dist"]
10
- }
package/tsdown.config.ts DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from "tsdown";
2
-
3
- export default defineConfig({
4
- entry: "./src/index.ts",
5
- dts: true,
6
- outDir: "./dist",
7
- });
package/vitest.config.ts DELETED
@@ -1,13 +0,0 @@
1
- import { defineConfig, mergeConfig } from "vitest/config";
2
- import { baseConfig } from "@fragno-private/vitest-config";
3
-
4
- export default defineConfig(
5
- mergeConfig(baseConfig, {
6
- test: {
7
- globalSetup: "./src/test-setup.ts",
8
- coverage: {
9
- enabled: false,
10
- },
11
- },
12
- }),
13
- );