@fragno-dev/create 0.1.0 → 0.1.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/integration.test.ts +1 -1
- package/src/package-json.ts +3 -3
- package/templates/optional/agent/AGENTS.md +104 -74
- package/templates/optional/database/schema.ts +4 -1
package/package.json
CHANGED
package/src/integration.test.ts
CHANGED
|
@@ -76,7 +76,7 @@ function createFragmentTestSuite(buildTool: BuildTool, withDatabase: boolean) {
|
|
|
76
76
|
but somehow when running through vitest the module resolution mechanism changes causing
|
|
77
77
|
the build to fail.
|
|
78
78
|
*/
|
|
79
|
-
test.skipIf(buildTool === "rollup")("builds", { timeout:
|
|
79
|
+
test.skipIf(buildTool === "rollup")("builds", { timeout: 50000 }, async () => {
|
|
80
80
|
const result = await execAsync("bun run build", {
|
|
81
81
|
cwd: tempDir,
|
|
82
82
|
encoding: "utf8",
|
package/src/package-json.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { BuildTools } from "./index";
|
|
2
2
|
|
|
3
|
-
const fragnoCoreVersion = "^0.1.
|
|
4
|
-
const fragnoDbVersion = "^0.1.
|
|
3
|
+
const fragnoCoreVersion = "^0.1.2";
|
|
4
|
+
const fragnoDbVersion = "^0.1.2";
|
|
5
5
|
const unpluginFragnoVersion = "^0.0.2";
|
|
6
|
-
const fragnoCliVersion = "^0.1.
|
|
6
|
+
const fragnoCliVersion = "^0.1.3";
|
|
7
7
|
|
|
8
8
|
export const basePkg: Record<string, unknown> = {
|
|
9
9
|
dependencies: {
|
|
@@ -14,7 +14,9 @@ provide:
|
|
|
14
14
|
- Built-in state management with reactive stores (TanStack Query-style:
|
|
15
15
|
`const {data, loading, error} = useData()`)
|
|
16
16
|
|
|
17
|
-
**Documentation**:
|
|
17
|
+
**Documentation**: https://fragno.dev/docs
|
|
18
|
+
|
|
19
|
+
All docs are also available with a `.md` extension: https://fragno.dev/docs.md
|
|
18
20
|
|
|
19
21
|
## Architecture
|
|
20
22
|
|
|
@@ -24,79 +26,6 @@ Fragments follow a core pattern:
|
|
|
24
26
|
2. **Client-side**: Auto-generated type-safe hooks for each route
|
|
25
27
|
3. **Code splitting**: Server-only code (handlers, dependencies) is stripped from client bundles
|
|
26
28
|
|
|
27
|
-
## Database Integration (Optional)
|
|
28
|
-
|
|
29
|
-
Some Fragments require persistent storage. Fragno provides an optional database layer via
|
|
30
|
-
`@fragno-dev/db` that integrates with your users' existing databases.
|
|
31
|
-
|
|
32
|
-
### When to Use Database Integration
|
|
33
|
-
|
|
34
|
-
Use `defineFragmentWithDatabase()` when your Fragment needs to:
|
|
35
|
-
|
|
36
|
-
- Store persistent data (comments, likes, user preferences, etc.)
|
|
37
|
-
- Query structured data efficiently
|
|
38
|
-
- Maintain data integrity with indexes and constraints
|
|
39
|
-
- Provide users with full control over their data
|
|
40
|
-
|
|
41
|
-
### Schema Definition
|
|
42
|
-
|
|
43
|
-
Database schemas are defined in a separate `schema.ts` file using the Fragno schema builder:
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import { column, idColumn, schema } from "@fragno-dev/db/schema";
|
|
47
|
-
|
|
48
|
-
export const noteSchema = schema((s) => {
|
|
49
|
-
return s.addTable("note", (t) => {
|
|
50
|
-
return t
|
|
51
|
-
.addColumn("id", idColumn()) // Auto-generated ID
|
|
52
|
-
.addColumn("content", column("string"))
|
|
53
|
-
.addColumn("userId", column("string"))
|
|
54
|
-
.addColumn("createdAt", column("timestamp").defaultTo$("now"))
|
|
55
|
-
.createIndex("idx_note_user", ["userId"]); // Index for efficient queries
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**Key concepts**:
|
|
61
|
-
|
|
62
|
-
- **Append-only**: Schemas use an append-only log approach. Never modify existing operations -
|
|
63
|
-
always add new ones
|
|
64
|
-
- **Versioning**: Each schema operation increments the version number
|
|
65
|
-
- **Indexes**: Create indexes on columns you'll frequently query (e.g., foreign keys, user IDs)
|
|
66
|
-
- **Defaults**: Use `.defaultTo(value)` or `.defaultTo$("now")` for timestamps
|
|
67
|
-
|
|
68
|
-
### Using the ORM
|
|
69
|
-
|
|
70
|
-
The ORM is available in both `withDependencies()` and `withServices()` via the `orm` parameter:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
const fragmentDef = defineFragmentWithDatabase<Config>("my-fragment")
|
|
74
|
-
.withDatabase(mySchema)
|
|
75
|
-
.withServices(({ orm }) => {
|
|
76
|
-
return {
|
|
77
|
-
createNote: async (note) => {
|
|
78
|
-
const id = await orm.create("note", note);
|
|
79
|
-
return { id: id.toJSON(), ...note };
|
|
80
|
-
},
|
|
81
|
-
getNotesByUser: (userId: string) => {
|
|
82
|
-
// Use whereIndex for efficient indexed queries
|
|
83
|
-
return orm.find("note", (b) =>
|
|
84
|
-
b.whereIndex("idx_note_user", (eb) => eb("userId", "=", userId)),
|
|
85
|
-
);
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**ORM methods**:
|
|
92
|
-
|
|
93
|
-
- `orm.create(table, data)` - Insert a row, returns FragnoId
|
|
94
|
-
- `orm.find(table, builder)` - Query rows with filtering
|
|
95
|
-
- `orm.findOne(table, builder)` - Query a single row
|
|
96
|
-
- `orm.update(table, id, data)` - Update a row by ID
|
|
97
|
-
- `orm.delete(table, id)` - Delete a row by ID
|
|
98
|
-
- `.whereIndex(indexName, condition)` - Use indexes for efficient queries
|
|
99
|
-
|
|
100
29
|
### Fragment Configuration
|
|
101
30
|
|
|
102
31
|
Database-enabled Fragments require `FragnoPublicConfigWithDatabase`:
|
|
@@ -183,6 +112,107 @@ points:
|
|
|
183
112
|
- Production uses built files from `dist/`
|
|
184
113
|
- When adding new framework exports, add corresponding client files in `src/client/`
|
|
185
114
|
|
|
115
|
+
## Database Integration (Optional)
|
|
116
|
+
|
|
117
|
+
Some Fragments require persistent storage. Fragno provides an optional database layer via
|
|
118
|
+
`@fragno-dev/db` that integrates with your users' existing databases.
|
|
119
|
+
|
|
120
|
+
### When to Use Database Integration
|
|
121
|
+
|
|
122
|
+
Use `defineFragmentWithDatabase()` when your Fragment needs to:
|
|
123
|
+
|
|
124
|
+
- Store persistent data (comments, likes, user preferences, etc.)
|
|
125
|
+
- Query structured data efficiently
|
|
126
|
+
- Maintain data integrity with indexes and constraints
|
|
127
|
+
- Provide users with full control over their data
|
|
128
|
+
|
|
129
|
+
### Schema Definition
|
|
130
|
+
|
|
131
|
+
Database schemas are defined in a separate `schema.ts` file using the Fragno schema builder:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { column, idColumn, schema } from "@fragno-dev/db/schema";
|
|
135
|
+
|
|
136
|
+
export const noteSchema = schema((s) => {
|
|
137
|
+
return s.addTable("note", (t) => {
|
|
138
|
+
return t
|
|
139
|
+
.addColumn("id", idColumn()) // Auto-generated ID
|
|
140
|
+
.addColumn("content", column("string"))
|
|
141
|
+
.addColumn("userId", column("string"))
|
|
142
|
+
.addColumn("createdAt", column("timestamp").defaultTo$("now"))
|
|
143
|
+
.createIndex("idx_note_user", ["userId"]); // Index for efficient queries
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Key concepts**:
|
|
149
|
+
|
|
150
|
+
- **Append-only**: Schemas use an append-only log approach. Never modify existing operations -
|
|
151
|
+
always add new ones
|
|
152
|
+
- **Versioning**: Each schema operation increments the version number
|
|
153
|
+
- **Indexes**: Create indexes on columns you'll frequently query (e.g., foreign keys, user IDs)
|
|
154
|
+
- **Defaults**: `.defaultTo(value)` or `.defaultTo((b) => b.now())` for DB defaults;
|
|
155
|
+
`.defaultTo$((b) => b.cuid())` for runtime defaults
|
|
156
|
+
|
|
157
|
+
### Using the ORM
|
|
158
|
+
|
|
159
|
+
The ORM is available in both `withDependencies()` and `withServices()` via the `orm` parameter:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const fragmentDef = defineFragmentWithDatabase<Config>("my-fragment")
|
|
163
|
+
.withDatabase(mySchema)
|
|
164
|
+
.withServices(({ orm }) => {
|
|
165
|
+
return {
|
|
166
|
+
createNote: async (note) => {
|
|
167
|
+
const id = await orm.create("note", note);
|
|
168
|
+
return { id: id.toJSON(), ...note };
|
|
169
|
+
},
|
|
170
|
+
getNotesByUser: (userId: string) => {
|
|
171
|
+
// Use whereIndex for efficient indexed queries
|
|
172
|
+
return orm.find("note", (b) =>
|
|
173
|
+
b.whereIndex("idx_note_user", (eb) => eb("userId", "=", userId)),
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**ORM methods**:
|
|
181
|
+
|
|
182
|
+
- `orm.create(table, data)` - Insert a row, returns FragnoId
|
|
183
|
+
- `orm.find(table, builder)` - Query rows with filtering
|
|
184
|
+
- `orm.findOne(table, builder)` - Query a single row
|
|
185
|
+
- `orm.update(table, id, data)` - Update a row by ID
|
|
186
|
+
- `orm.delete(table, id)` - Delete a row by ID
|
|
187
|
+
- `.whereIndex(indexName, condition)` - Use indexes for efficient queries
|
|
188
|
+
|
|
189
|
+
### Transactions (Unit of Work)
|
|
190
|
+
|
|
191
|
+
Two-phase pattern for atomic operations (optimistic concurrency control):
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Phase 1: Retrieve with version tracking
|
|
195
|
+
const uow = orm
|
|
196
|
+
.createUnitOfWork()
|
|
197
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userId)))
|
|
198
|
+
.find("accounts", (b) => b.whereIndex("idx_user", (eb) => eb("userId", "=", userId)));
|
|
199
|
+
const [users, accounts] = await uow.executeRetrieve();
|
|
200
|
+
|
|
201
|
+
// Phase 2: Mutate atomically
|
|
202
|
+
uow.update("users", users[0].id, (b) => b.set({ lastLogin: new Date() }).check());
|
|
203
|
+
uow.update("accounts", accounts[0].id, (b) =>
|
|
204
|
+
b.set({ balance: accounts[0].balance + 100 }).check(),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const { success } = await uow.executeMutations();
|
|
208
|
+
if (!success) {
|
|
209
|
+
/* Version conflict - retry */
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Notes**: `.check()` enables optimistic concurrency control; requires `FragnoId` objects (not
|
|
214
|
+
string IDs); use `uow.getCreatedIds()` for new record IDs
|
|
215
|
+
|
|
186
216
|
## Strategies for Building Fragments
|
|
187
217
|
|
|
188
218
|
### OpenAPI/Swagger Spec → Fragno Routes
|
|
@@ -6,7 +6,10 @@ export const noteSchema = schema((s) => {
|
|
|
6
6
|
.addColumn("id", idColumn())
|
|
7
7
|
.addColumn("content", column("string"))
|
|
8
8
|
.addColumn("userId", column("string"))
|
|
9
|
-
.addColumn(
|
|
9
|
+
.addColumn(
|
|
10
|
+
"createdAt",
|
|
11
|
+
column("timestamp").defaultTo((b) => b.now()),
|
|
12
|
+
)
|
|
10
13
|
.createIndex("idx_note_user", ["userId"]);
|
|
11
14
|
});
|
|
12
15
|
});
|