@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fragno-dev/create",
3
3
  "description": "A library for creating Fragno fragments",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "exports": {
6
6
  ".": {
7
7
  "bun": "./src/index.ts",
@@ -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: 40000 }, async () => {
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",
@@ -1,9 +1,9 @@
1
1
  import type { BuildTools } from "./index";
2
2
 
3
- const fragnoCoreVersion = "^0.1.1";
4
- const fragnoDbVersion = "^0.1.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.2";
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**: Full documentation is available at https://fragno.dev/docs
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("createdAt", column("timestamp").defaultTo$("now"))
9
+ .addColumn(
10
+ "createdAt",
11
+ column("timestamp").defaultTo((b) => b.now()),
12
+ )
10
13
  .createIndex("idx_note_user", ["userId"]);
11
14
  });
12
15
  });