@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.
- package/README.md +57 -22
- package/dist/index.d.ts +98 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +247 -0
- package/dist/index.js.map +1 -0
- package/package.json +10 -5
- package/.turbo/turbo-build.log +0 -15
- package/CHANGELOG.md +0 -7
- package/src/index.test.ts +0 -107
- package/src/index.ts +0 -51
- package/src/parser.test.ts +0 -115
- package/src/parser.ts +0 -182
- package/src/subjects/database-adapters.md +0 -68
- package/src/subjects/database-querying.md +0 -227
- package/src/subjects/defining-routes.md +0 -272
- package/src/subjects/drizzle-adapter.md +0 -60
- package/src/subjects/kysely-adapter.md +0 -59
- package/src/test-setup.ts +0 -110
- package/tsconfig.json +0 -10
- package/tsdown.config.ts +0 -7
- package/vitest.config.ts +0 -13
package/src/index.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAvailableSubjectIds,
|
|
3
|
-
loadSubject,
|
|
4
|
-
loadSubjects,
|
|
5
|
-
loadAllSubjects,
|
|
6
|
-
type SubjectInfo,
|
|
7
|
-
type Subject,
|
|
8
|
-
} from "./parser";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get basic information about all available subjects
|
|
12
|
-
* @returns Array of subject info (id and title)
|
|
13
|
-
*/
|
|
14
|
-
export function getSubjects(): SubjectInfo[] {
|
|
15
|
-
const ids = getAvailableSubjectIds();
|
|
16
|
-
return ids.map((id) => {
|
|
17
|
-
const subject = loadSubject(id);
|
|
18
|
-
return {
|
|
19
|
-
id: subject.id,
|
|
20
|
-
title: subject.title,
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get one or more subjects by their IDs
|
|
27
|
-
* @param ids Subject IDs to load
|
|
28
|
-
* @returns Array of complete subject data
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* // Get single subject
|
|
32
|
-
* const [routes] = getSubject("defining-routes");
|
|
33
|
-
*
|
|
34
|
-
* // Get multiple subjects for combined context
|
|
35
|
-
* const [adapters, kysely] = getSubject("database-adapters", "kysely-adapter");
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export function getSubject(...ids: string[]): Subject[] {
|
|
39
|
-
return loadSubjects(ids);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Get all available subjects
|
|
44
|
-
* @returns Array of all subjects with complete data
|
|
45
|
-
*/
|
|
46
|
-
export function getAllSubjects(): Subject[] {
|
|
47
|
-
return loadAllSubjects();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Re-export types
|
|
51
|
-
export type { Subject, SubjectInfo, Example } from "./parser.js";
|
package/src/parser.test.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parseMarkdownFile, markdownToSubject } from "./parser.js";
|
|
3
|
-
|
|
4
|
-
describe("parseMarkdownFile", () => {
|
|
5
|
-
it("should extract title from markdown", () => {
|
|
6
|
-
const content = `# Test Title
|
|
7
|
-
|
|
8
|
-
Description here`;
|
|
9
|
-
|
|
10
|
-
const result = parseMarkdownFile(content);
|
|
11
|
-
expect(result.title).toBe("Test Title");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should extract description", () => {
|
|
15
|
-
const content = `# Test Title
|
|
16
|
-
|
|
17
|
-
This is a description.
|
|
18
|
-
|
|
19
|
-
\`\`\`typescript @fragno-imports
|
|
20
|
-
import { test } from "test";
|
|
21
|
-
\`\`\``;
|
|
22
|
-
|
|
23
|
-
const result = parseMarkdownFile(content);
|
|
24
|
-
expect(result.description).toBe("This is a description.");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should extract imports block", () => {
|
|
28
|
-
const content = `# Test
|
|
29
|
-
|
|
30
|
-
\`\`\`typescript @fragno-imports
|
|
31
|
-
import { defineRoute } from "@fragno-dev/core";
|
|
32
|
-
import { z } from "zod";
|
|
33
|
-
\`\`\``;
|
|
34
|
-
|
|
35
|
-
const result = parseMarkdownFile(content);
|
|
36
|
-
expect(result.imports).toBe(`import { defineRoute } from "@fragno-dev/core";
|
|
37
|
-
import { z } from "zod";`);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("should extract init block when present", () => {
|
|
41
|
-
const content = `# Test
|
|
42
|
-
|
|
43
|
-
\`\`\`typescript @fragno-imports
|
|
44
|
-
import { x } from "y";
|
|
45
|
-
\`\`\`
|
|
46
|
-
|
|
47
|
-
\`\`\`typescript @fragno-init
|
|
48
|
-
const config = { key: "value" };
|
|
49
|
-
\`\`\``;
|
|
50
|
-
|
|
51
|
-
const result = parseMarkdownFile(content);
|
|
52
|
-
expect(result.init).toBe(`const config = { key: "value" };`);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should handle missing init block", () => {
|
|
56
|
-
const content = `# Test
|
|
57
|
-
|
|
58
|
-
\`\`\`typescript @fragno-imports
|
|
59
|
-
import { x } from "y";
|
|
60
|
-
\`\`\``;
|
|
61
|
-
|
|
62
|
-
const result = parseMarkdownFile(content);
|
|
63
|
-
expect(result.init).toBe("");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should extract multiple test blocks", () => {
|
|
67
|
-
const content = `# Test
|
|
68
|
-
|
|
69
|
-
\`\`\`typescript @fragno-imports
|
|
70
|
-
import { x } from "y";
|
|
71
|
-
\`\`\`
|
|
72
|
-
|
|
73
|
-
\`\`\`typescript @fragno-test
|
|
74
|
-
const test1 = "value1";
|
|
75
|
-
\`\`\`
|
|
76
|
-
|
|
77
|
-
This is explanation for test1.
|
|
78
|
-
|
|
79
|
-
\`\`\`typescript @fragno-test
|
|
80
|
-
const test2 = "value2";
|
|
81
|
-
\`\`\`
|
|
82
|
-
|
|
83
|
-
This is explanation for test2.`;
|
|
84
|
-
|
|
85
|
-
const result = parseMarkdownFile(content);
|
|
86
|
-
expect(result.testBlocks).toHaveLength(2);
|
|
87
|
-
expect(result.testBlocks[0].code).toBe(`const test1 = "value1";`);
|
|
88
|
-
expect(result.testBlocks[0].explanation).toContain("explanation for test1");
|
|
89
|
-
expect(result.testBlocks[1].code).toBe(`const test2 = "value2";`);
|
|
90
|
-
expect(result.testBlocks[1].explanation).toContain("explanation for test2");
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("markdownToSubject", () => {
|
|
95
|
-
it("should convert parsed markdown to Subject", () => {
|
|
96
|
-
const parsed = {
|
|
97
|
-
title: "Test Subject",
|
|
98
|
-
description: "Test description",
|
|
99
|
-
imports: "import { x } from 'y';",
|
|
100
|
-
init: "const config = {};",
|
|
101
|
-
testBlocks: [{ code: "const test = 1;", explanation: "Test explanation" }],
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const result = markdownToSubject("test-id", parsed);
|
|
105
|
-
|
|
106
|
-
expect(result.id).toBe("test-id");
|
|
107
|
-
expect(result.title).toBe("Test Subject");
|
|
108
|
-
expect(result.description).toBe("Test description");
|
|
109
|
-
expect(result.imports).toBe("import { x } from 'y';");
|
|
110
|
-
expect(result.init).toBe("const config = {};");
|
|
111
|
-
expect(result.examples).toHaveLength(1);
|
|
112
|
-
expect(result.examples[0].code).toBe("const test = 1;");
|
|
113
|
-
expect(result.examples[0].explanation).toBe("Test explanation");
|
|
114
|
-
});
|
|
115
|
-
});
|
package/src/parser.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
-
import { join, basename, dirname } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = dirname(__filename);
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Basic information about a subject
|
|
10
|
-
*/
|
|
11
|
-
export interface SubjectInfo {
|
|
12
|
-
id: string;
|
|
13
|
-
title: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A single example within a subject
|
|
18
|
-
*/
|
|
19
|
-
export interface Example {
|
|
20
|
-
code: string;
|
|
21
|
-
explanation: string;
|
|
22
|
-
testType?: "route" | "database" | "none";
|
|
23
|
-
testName?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Complete subject with all examples and metadata
|
|
28
|
-
*/
|
|
29
|
-
export interface Subject {
|
|
30
|
-
id: string;
|
|
31
|
-
title: string;
|
|
32
|
-
description: string;
|
|
33
|
-
imports: string;
|
|
34
|
-
init: string;
|
|
35
|
-
examples: Example[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Raw parsed data from markdown before processing
|
|
40
|
-
*/
|
|
41
|
-
export interface ParsedMarkdown {
|
|
42
|
-
title: string;
|
|
43
|
-
description: string;
|
|
44
|
-
imports: string;
|
|
45
|
-
init: string;
|
|
46
|
-
testBlocks: Array<{
|
|
47
|
-
code: string;
|
|
48
|
-
explanation: string;
|
|
49
|
-
testType?: "route" | "database" | "none";
|
|
50
|
-
testName?: string;
|
|
51
|
-
}>;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Look for subjects directory in source or relative to built dist
|
|
55
|
-
const SUBJECTS_DIR = (() => {
|
|
56
|
-
// Try dist/../src/subjects (when running from built code)
|
|
57
|
-
const distRelative = join(__dirname, "..", "src", "subjects");
|
|
58
|
-
try {
|
|
59
|
-
readdirSync(distRelative);
|
|
60
|
-
return distRelative;
|
|
61
|
-
} catch {
|
|
62
|
-
// Fall back to ./subjects (when running from source)
|
|
63
|
-
return join(__dirname, "subjects");
|
|
64
|
-
}
|
|
65
|
-
})();
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Parses a markdown file and extracts structured content
|
|
69
|
-
*/
|
|
70
|
-
export function parseMarkdownFile(content: string): ParsedMarkdown {
|
|
71
|
-
// Extract title (first # heading)
|
|
72
|
-
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
73
|
-
const title = titleMatch ? titleMatch[1].trim() : "Untitled";
|
|
74
|
-
|
|
75
|
-
// Extract imports block
|
|
76
|
-
const importsMatch = content.match(/```typescript @fragno-imports\n([\s\S]*?)```/);
|
|
77
|
-
const imports = importsMatch ? importsMatch[1].trim() : "";
|
|
78
|
-
|
|
79
|
-
// Extract init block (optional)
|
|
80
|
-
const initMatch = content.match(/```typescript @fragno-init\n([\s\S]*?)```/);
|
|
81
|
-
const init = initMatch ? initMatch[1].trim() : "";
|
|
82
|
-
|
|
83
|
-
// Extract all test blocks with their explanations and test type
|
|
84
|
-
const testBlockRegex =
|
|
85
|
-
/```typescript @fragno-test(?::(\w+))?\n([\s\S]*?)```([\s\S]*?)(?=```typescript @fragno-test|$)/g;
|
|
86
|
-
const testBlocks: Array<{
|
|
87
|
-
code: string;
|
|
88
|
-
explanation: string;
|
|
89
|
-
testType?: "route" | "database" | "none";
|
|
90
|
-
testName?: string;
|
|
91
|
-
}> = [];
|
|
92
|
-
|
|
93
|
-
let match;
|
|
94
|
-
while ((match = testBlockRegex.exec(content)) !== null) {
|
|
95
|
-
const testTypeRaw = match[1]; // route, database, or undefined
|
|
96
|
-
const testType = testTypeRaw === "route" || testTypeRaw === "database" ? testTypeRaw : "none";
|
|
97
|
-
|
|
98
|
-
const code = match[2].trim();
|
|
99
|
-
|
|
100
|
-
// Extract test name from first line if it's a comment
|
|
101
|
-
const lines = code.split("\n");
|
|
102
|
-
let testName: string | undefined;
|
|
103
|
-
if (lines[0]?.trim().startsWith("//")) {
|
|
104
|
-
testName = lines[0].replace(/^\/\/\s*/, "").trim();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Get explanation text after the code block until next code block or end
|
|
108
|
-
const afterBlock = match[3];
|
|
109
|
-
const explanation = afterBlock
|
|
110
|
-
.split(/```/)[0] // Stop at next code block
|
|
111
|
-
.trim();
|
|
112
|
-
|
|
113
|
-
testBlocks.push({ code, explanation, testType, testName });
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Extract description (everything between title and first code block or ## heading)
|
|
117
|
-
const afterTitle = content.substring(content.indexOf(title) + title.length);
|
|
118
|
-
const descriptionMatch = afterTitle.match(/\n\n([\s\S]*?)(?=```|##|$)/);
|
|
119
|
-
const description = descriptionMatch ? descriptionMatch[1].trim() : "";
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
title,
|
|
123
|
-
description,
|
|
124
|
-
imports,
|
|
125
|
-
init,
|
|
126
|
-
testBlocks,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Converts parsed markdown to a Subject
|
|
132
|
-
*/
|
|
133
|
-
export function markdownToSubject(id: string, parsed: ParsedMarkdown): Subject {
|
|
134
|
-
const examples: Example[] = parsed.testBlocks.map((block) => ({
|
|
135
|
-
code: block.code,
|
|
136
|
-
explanation: block.explanation,
|
|
137
|
-
testType: block.testType,
|
|
138
|
-
testName: block.testName,
|
|
139
|
-
}));
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
id,
|
|
143
|
-
title: parsed.title,
|
|
144
|
-
description: parsed.description,
|
|
145
|
-
imports: parsed.imports,
|
|
146
|
-
init: parsed.init,
|
|
147
|
-
examples,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Loads and parses a subject file by ID
|
|
153
|
-
*/
|
|
154
|
-
export function loadSubject(id: string): Subject {
|
|
155
|
-
const filePath = join(SUBJECTS_DIR, `${id}.md`);
|
|
156
|
-
const content = readFileSync(filePath, "utf-8");
|
|
157
|
-
const parsed = parseMarkdownFile(content);
|
|
158
|
-
return markdownToSubject(id, parsed);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Gets all available subject IDs from the subjects directory
|
|
163
|
-
*/
|
|
164
|
-
export function getAvailableSubjectIds(): string[] {
|
|
165
|
-
const files = readdirSync(SUBJECTS_DIR);
|
|
166
|
-
return files.filter((file) => file.endsWith(".md")).map((file) => basename(file, ".md"));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Loads multiple subjects by their IDs
|
|
171
|
-
*/
|
|
172
|
-
export function loadSubjects(ids: string[]): Subject[] {
|
|
173
|
-
return ids.map((id) => loadSubject(id));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Loads all available subjects
|
|
178
|
-
*/
|
|
179
|
-
export function loadAllSubjects(): Subject[] {
|
|
180
|
-
const ids = getAvailableSubjectIds();
|
|
181
|
-
return loadSubjects(ids);
|
|
182
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# Database Adapters
|
|
2
|
-
|
|
3
|
-
Database adapters connect Fragno's database API to your existing ORM (Kysely or Drizzle). They allow
|
|
4
|
-
fragments to work with your application's database without dictating which ORM you use.
|
|
5
|
-
|
|
6
|
-
```typescript @fragno-imports
|
|
7
|
-
import type { DatabaseAdapter } from "@fragno-dev/db";
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## What is a Database Adapter?
|
|
11
|
-
|
|
12
|
-
A database adapter is a bridge between Fragno's type-safe database API and your underlying ORM. It
|
|
13
|
-
translates Fragno's query operations into ORM-specific syntax.
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
// Adapters implement the DatabaseAdapter interface
|
|
17
|
-
declare const adapter: DatabaseAdapter;
|
|
18
|
-
|
|
19
|
-
// Fragments receive an adapter through their configuration
|
|
20
|
-
interface FragmentConfig {
|
|
21
|
-
databaseAdapter: DatabaseAdapter;
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
When a fragment needs database access, users pass an adapter configured with their ORM instance.
|
|
26
|
-
|
|
27
|
-
## Supported Providers
|
|
28
|
-
|
|
29
|
-
Both KyselyAdapter and DrizzleAdapter support three database providers:
|
|
30
|
-
|
|
31
|
-
- `"postgresql"` - PostgreSQL databases
|
|
32
|
-
- `"mysql"` - MySQL and MariaDB databases
|
|
33
|
-
- `"sqlite"` - SQLite databases
|
|
34
|
-
|
|
35
|
-
Choose the provider that matches your database type when creating an adapter.
|
|
36
|
-
|
|
37
|
-
## Shared Adapters
|
|
38
|
-
|
|
39
|
-
Multiple fragments can share the same adapter, meaning they all use your application's single
|
|
40
|
-
database connection.
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
declare const adapter: DatabaseAdapter;
|
|
44
|
-
|
|
45
|
-
// All fragments use the same adapter
|
|
46
|
-
export interface Fragment1Config {
|
|
47
|
-
databaseAdapter: typeof adapter;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface Fragment2Config {
|
|
51
|
-
databaseAdapter: typeof adapter;
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
This ensures fragments integrate seamlessly with your existing database infrastructure.
|
|
56
|
-
|
|
57
|
-
## Cleanup
|
|
58
|
-
|
|
59
|
-
Adapters manage connection lifecycle automatically. Call `close()` when shutting down your
|
|
60
|
-
application to properly release database connections.
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
declare const adapter: DatabaseAdapter;
|
|
64
|
-
|
|
65
|
-
export async function cleanup() {
|
|
66
|
-
await adapter.close();
|
|
67
|
-
}
|
|
68
|
-
```
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# Database Querying
|
|
2
|
-
|
|
3
|
-
Fragno provides a unified database query API that works across different ORMs. This guide covers
|
|
4
|
-
CRUD operations and querying with conditions.
|
|
5
|
-
|
|
6
|
-
```typescript @fragno-imports
|
|
7
|
-
import { defineFragnoDatabase, schema, idColumn, column } from "@fragno-dev/db";
|
|
8
|
-
import type { AbstractQuery } from "@fragno-dev/db";
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
```typescript @fragno-init
|
|
12
|
-
// Example schema for testing
|
|
13
|
-
const userSchema = schema((s) => {
|
|
14
|
-
return s
|
|
15
|
-
.addTable("users", (t) => {
|
|
16
|
-
return t
|
|
17
|
-
.addColumn("id", idColumn())
|
|
18
|
-
.addColumn("email", column("string"))
|
|
19
|
-
.addColumn("name", column("string"))
|
|
20
|
-
.addColumn("age", column("integer").nullable())
|
|
21
|
-
.createIndex("idx_email", ["email"], { unique: true });
|
|
22
|
-
})
|
|
23
|
-
.addTable("posts", (t) => {
|
|
24
|
-
return t
|
|
25
|
-
.addColumn("id", idColumn())
|
|
26
|
-
.addColumn("title", column("string"))
|
|
27
|
-
.addColumn("content", column("string"))
|
|
28
|
-
.addColumn("authorId", column("string"))
|
|
29
|
-
.addColumn("publishedAt", column("timestamp").nullable())
|
|
30
|
-
.createIndex("idx_author", ["authorId"]);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
type UserSchema = typeof userSchema;
|
|
35
|
-
declare const orm: AbstractQuery<UserSchema>;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Create
|
|
39
|
-
|
|
40
|
-
Create a single record in the database.
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
export async function createUser() {
|
|
44
|
-
const userId = await orm.create("users", {
|
|
45
|
-
id: "user-123",
|
|
46
|
-
email: "john@example.com",
|
|
47
|
-
name: "John Doe",
|
|
48
|
-
age: 30,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return userId; // Returns a FragnoId object
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
The `create` method returns a `FragnoId` object representing the created record's ID.
|
|
56
|
-
|
|
57
|
-
## Create Many
|
|
58
|
-
|
|
59
|
-
Create multiple records at once.
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
export async function createMultipleUsers() {
|
|
63
|
-
const userIds = await orm.createMany("users", [
|
|
64
|
-
{
|
|
65
|
-
id: "user-1",
|
|
66
|
-
email: "user1@example.com",
|
|
67
|
-
name: "User One",
|
|
68
|
-
age: 25,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: "user-2",
|
|
72
|
-
email: "user2@example.com",
|
|
73
|
-
name: "User Two",
|
|
74
|
-
age: 35,
|
|
75
|
-
},
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
return userIds; // Returns an array of FragnoId objects
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Find First
|
|
83
|
-
|
|
84
|
-
Query for a single record using an index.
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
export async function findUserByEmail(email: string) {
|
|
88
|
-
const user = await orm.findFirst("users", (b) =>
|
|
89
|
-
b.whereIndex("idx_email", (eb) => eb("email", "=", email)),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return user; // Returns the user object or null if not found
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Use `findFirst` when you expect a single result or want to get the first matching record.
|
|
97
|
-
|
|
98
|
-
## Find First with Select
|
|
99
|
-
|
|
100
|
-
Query a single record and select specific columns.
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
export async function findUserEmailOnly(userId: string) {
|
|
104
|
-
const user = await orm.findFirst("users", (b) =>
|
|
105
|
-
b.whereIndex("primary", (eb) => eb("id", "=", userId)).select(["id", "email"]),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
return user; // Returns only id and email fields
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Find Many
|
|
113
|
-
|
|
114
|
-
Query for multiple records matching conditions.
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
export async function findPostsByAuthor(authorId: string) {
|
|
118
|
-
const posts = await orm.find("posts", (b) =>
|
|
119
|
-
b.whereIndex("idx_author", (eb) => eb("authorId", "=", authorId)),
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
return posts; // Returns an array of posts
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
The `find` method returns all records matching the where clause.
|
|
127
|
-
|
|
128
|
-
## Find with Pagination
|
|
129
|
-
|
|
130
|
-
Limit the number of results returned.
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
export async function findUsersPaginated(page: number, pageSize: number) {
|
|
134
|
-
const users = await orm.find("users", (b) => b.whereIndex("primary").pageSize(pageSize));
|
|
135
|
-
|
|
136
|
-
return users;
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Find All
|
|
141
|
-
|
|
142
|
-
Query all records from a table.
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
export async function findAllUsers() {
|
|
146
|
-
const users = await orm.find("users", (b) => b.whereIndex("primary"));
|
|
147
|
-
|
|
148
|
-
return users;
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
When using `whereIndex("primary")` without conditions, it returns all records.
|
|
153
|
-
|
|
154
|
-
## Update
|
|
155
|
-
|
|
156
|
-
Update a single record by ID.
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
export async function updateUserEmail(userId: string, newEmail: string) {
|
|
160
|
-
await orm.update("users", userId, (b) =>
|
|
161
|
-
b.set({
|
|
162
|
-
email: newEmail,
|
|
163
|
-
}),
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
The `update` method modifies a single record identified by its ID.
|
|
169
|
-
|
|
170
|
-
## Update Many
|
|
171
|
-
|
|
172
|
-
Update multiple records matching a condition.
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
export async function updatePostsPublishedDate(authorId: string, publishedAt: Date) {
|
|
176
|
-
await orm.updateMany("posts", (b) =>
|
|
177
|
-
b.whereIndex("idx_author", (eb) => eb("authorId", "=", authorId)).set({ publishedAt }),
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
`updateMany` finds all matching records and updates them.
|
|
183
|
-
|
|
184
|
-
## Delete
|
|
185
|
-
|
|
186
|
-
Delete a single record by ID.
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
export async function deleteUser(userId: string) {
|
|
190
|
-
await orm.delete("users", userId);
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## Delete Many
|
|
195
|
-
|
|
196
|
-
Delete multiple records matching a condition.
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
export async function deletePostsByAuthor(authorId: string) {
|
|
200
|
-
await orm.deleteMany("posts", (b) =>
|
|
201
|
-
b.whereIndex("idx_author", (eb) => eb("authorId", "=", authorId)),
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
`deleteMany` finds all matching records and deletes them.
|
|
207
|
-
|
|
208
|
-
## Querying with Conditions
|
|
209
|
-
|
|
210
|
-
Use condition builders to create complex queries.
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
export async function findAdultUsers() {
|
|
214
|
-
const adults = await orm.find("users", (b) =>
|
|
215
|
-
b.whereIndex("primary").where((cb) => {
|
|
216
|
-
// Note: Condition builders depend on the underlying ORM
|
|
217
|
-
// This is a simplified example
|
|
218
|
-
return cb;
|
|
219
|
-
}),
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
return adults;
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Condition builders allow you to add additional filtering beyond index lookups, though the exact API
|
|
227
|
-
depends on your adapter (Kysely or Drizzle).
|