cloesce 0.0.4-unstable.5 → 0.0.4-unstable.7
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 +619 -0
- package/dist/cli.js +25 -10
- package/dist/common.d.ts +14 -50
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +42 -22
- package/dist/extractor/extract.d.ts.map +1 -1
- package/dist/extractor/extract.js +44 -47
- package/dist/generator.wasm +0 -0
- package/dist/orm.wasm +0 -0
- package/dist/router/crud.d.ts.map +1 -1
- package/dist/router/crud.js +16 -11
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +25 -24
- package/dist/router/wasm.d.ts +9 -2
- package/dist/router/wasm.d.ts.map +1 -1
- package/dist/router/wasm.js +11 -5
- package/dist/ui/backend.d.ts +139 -81
- package/dist/ui/backend.d.ts.map +1 -1
- package/dist/ui/backend.js +185 -139
- package/package.json +1 -1
package/dist/ui/backend.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Either } from "../common.js";
|
|
2
2
|
import { RuntimeContainer } from "../router/router.js";
|
|
3
3
|
import { WasmResource, mapSql as mapSql, invokeOrmWasm, } from "../router/wasm.js";
|
|
4
4
|
export { cloesce } from "../router/router.js";
|
|
@@ -23,7 +23,7 @@ export { CloesceApp } from "../common.js";
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
export const D1 = () => { };
|
|
26
|
+
export const D1 = (_) => { };
|
|
27
27
|
/**
|
|
28
28
|
* Marks a class as a plain serializable object.
|
|
29
29
|
*
|
|
@@ -31,8 +31,6 @@ export const D1 = () => { };
|
|
|
31
31
|
* returned from a model method or API endpoint without being
|
|
32
32
|
* treated as a database model.
|
|
33
33
|
*
|
|
34
|
-
* These are often used for DTOs or view models.
|
|
35
|
-
*
|
|
36
34
|
* Example:
|
|
37
35
|
* ```ts
|
|
38
36
|
* @PlainOldObject
|
|
@@ -40,9 +38,19 @@ export const D1 = () => { };
|
|
|
40
38
|
* catFacts: string[];
|
|
41
39
|
* catNames: string[];
|
|
42
40
|
* }
|
|
41
|
+
*
|
|
42
|
+
* // in a method...
|
|
43
|
+
* foo(): CatStuff {
|
|
44
|
+
* return {
|
|
45
|
+
* catFacts: ["cats sleep 16 hours a day"],
|
|
46
|
+
* catNames: ["Whiskers", "Fluffy"]
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* // which generates an API like:
|
|
50
|
+
* async function foo(): Promise<HttpResult<CatStuff>> { ... }
|
|
43
51
|
* ```
|
|
44
52
|
*/
|
|
45
|
-
export const PlainOldObject = () => { };
|
|
53
|
+
export const PlainOldObject = (_) => { };
|
|
46
54
|
/**
|
|
47
55
|
* Declares a Wrangler environment definition.
|
|
48
56
|
*
|
|
@@ -64,7 +72,7 @@ export const PlainOldObject = () => { };
|
|
|
64
72
|
* foo(@Inject env: WranglerEnv) {...}
|
|
65
73
|
* ```
|
|
66
74
|
*/
|
|
67
|
-
export const WranglerEnv = () => { };
|
|
75
|
+
export const WranglerEnv = (_) => { };
|
|
68
76
|
/**
|
|
69
77
|
* Marks a property as the SQL primary key for a model.
|
|
70
78
|
*
|
|
@@ -81,40 +89,37 @@ export const WranglerEnv = () => { };
|
|
|
81
89
|
* }
|
|
82
90
|
* ```
|
|
83
91
|
*/
|
|
84
|
-
export const PrimaryKey = () => { };
|
|
92
|
+
export const PrimaryKey = (_) => { };
|
|
85
93
|
/**
|
|
86
94
|
* Exposes a class method as an HTTP GET endpoint.
|
|
87
95
|
* The method will appear in both backend and generated client APIs.
|
|
88
96
|
*/
|
|
89
|
-
export const GET = () => { };
|
|
97
|
+
export const GET = (_) => { };
|
|
90
98
|
/**
|
|
91
99
|
* Exposes a class method as an HTTP POST endpoint.
|
|
92
100
|
* The method will appear in both backend and generated client APIs.
|
|
93
101
|
*/
|
|
94
|
-
export const POST = () => { };
|
|
102
|
+
export const POST = (_) => { };
|
|
95
103
|
/**
|
|
96
104
|
* Exposes a class method as an HTTP PUT endpoint.
|
|
97
105
|
* The method will appear in both backend and generated client APIs.
|
|
98
106
|
*/
|
|
99
|
-
export const PUT = () => { };
|
|
107
|
+
export const PUT = (_) => { };
|
|
100
108
|
/**
|
|
101
109
|
* Exposes a class method as an HTTP PATCH endpoint.
|
|
102
110
|
* The method will appear in both backend and generated client APIs.
|
|
103
111
|
*/
|
|
104
|
-
export const PATCH = () => { };
|
|
112
|
+
export const PATCH = (_) => { };
|
|
105
113
|
/**
|
|
106
114
|
* Exposes a class method as an HTTP DEL endpoint.
|
|
107
115
|
* The method will appear in both backend and generated client APIs.
|
|
108
116
|
*/
|
|
109
|
-
export const DELETE = () => { };
|
|
117
|
+
export const DELETE = (_) => { };
|
|
110
118
|
/**
|
|
111
119
|
* Declares a static property as a data source.
|
|
112
120
|
*
|
|
113
|
-
* Data sources describe SQL
|
|
114
|
-
*
|
|
115
|
-
* are automatically included when querying. Data sources
|
|
116
|
-
* can only reference navigation properties, not scalar
|
|
117
|
-
* attributes.
|
|
121
|
+
* Data sources describe SQL left joins related to each
|
|
122
|
+
* models navigation properties.
|
|
118
123
|
*
|
|
119
124
|
* Example:
|
|
120
125
|
* ```ts
|
|
@@ -137,30 +142,20 @@ export const DELETE = () => { };
|
|
|
137
142
|
* @OneToOne("dogId")
|
|
138
143
|
* dog: Dog | undefined;
|
|
139
144
|
*
|
|
140
|
-
* // 👇 Defines a data source that joins the related Dog record
|
|
141
145
|
* @DataSource
|
|
142
146
|
* static readonly default: IncludeTree<Person> = {
|
|
143
|
-
* dog: {},
|
|
147
|
+
* dog: {}, // join Dog table when querying Person with `default` data source
|
|
144
148
|
* };
|
|
145
149
|
* }
|
|
146
150
|
*
|
|
147
|
-
* // The above will generate an SQL view similar to:
|
|
148
|
-
* // CREATE VIEW "Person.default" AS
|
|
149
|
-
* // SELECT
|
|
150
|
-
* // "Person"."id" AS "id",
|
|
151
|
-
* // "Person"."dogId" AS "dogId",
|
|
152
|
-
* // "Dog"."id" AS "dog.id",
|
|
153
|
-
* // "Dog"."name" AS "dog.name"
|
|
154
|
-
* // FROM "Person"
|
|
155
|
-
* // LEFT JOIN "Dog" ON "Person"."dogId" = "Dog"."id";
|
|
156
|
-
*
|
|
157
151
|
* // When queried via the ORM or client API:
|
|
158
152
|
* const orm = Orm.fromD1(env.db);
|
|
159
|
-
* const people =
|
|
160
|
-
*
|
|
153
|
+
* const people = await orm.list(Person, Person.default);
|
|
154
|
+
*
|
|
155
|
+
* // => Person { id: 1, dogId: 2, dog: { id: 2, name: "Fido" } }[]
|
|
161
156
|
* ```
|
|
162
157
|
*/
|
|
163
|
-
export const DataSource = () => { };
|
|
158
|
+
export const DataSource = (_) => { };
|
|
164
159
|
/**
|
|
165
160
|
* Declares a one-to-many relationship between models.
|
|
166
161
|
*
|
|
@@ -173,7 +168,7 @@ export const DataSource = () => { };
|
|
|
173
168
|
* dogs: Dog[];
|
|
174
169
|
* ```
|
|
175
170
|
*/
|
|
176
|
-
export const OneToMany = (
|
|
171
|
+
export const OneToMany = (_foreignKeyColumn) => () => { };
|
|
177
172
|
/**
|
|
178
173
|
* Declares a one-to-one relationship between models.
|
|
179
174
|
*
|
|
@@ -186,7 +181,7 @@ export const OneToMany = (_) => () => { };
|
|
|
186
181
|
* dog: Dog | undefined;
|
|
187
182
|
* ```
|
|
188
183
|
*/
|
|
189
|
-
export const OneToOne = (
|
|
184
|
+
export const OneToOne = (_foreignKeyColumn) => () => { };
|
|
190
185
|
/**
|
|
191
186
|
* Declares a many-to-many relationship between models.
|
|
192
187
|
*
|
|
@@ -199,7 +194,7 @@ export const OneToOne = (_) => () => { };
|
|
|
199
194
|
* courses: Course[];
|
|
200
195
|
* ```
|
|
201
196
|
*/
|
|
202
|
-
export const ManyToMany = (
|
|
197
|
+
export const ManyToMany = (_uniqueId) => () => { };
|
|
203
198
|
/**
|
|
204
199
|
* Declares a foreign key relationship between models.
|
|
205
200
|
* Directly translates to a SQLite foreign key.
|
|
@@ -214,13 +209,16 @@ export const ManyToMany = (_) => () => { };
|
|
|
214
209
|
* dogId: number;
|
|
215
210
|
* ```
|
|
216
211
|
*/
|
|
217
|
-
export const ForeignKey = (
|
|
212
|
+
export const ForeignKey = (_model) => () => { };
|
|
218
213
|
/**
|
|
219
214
|
* Marks a method parameter for dependency injection.
|
|
220
215
|
*
|
|
221
216
|
* Injected parameters can receive environment bindings,
|
|
222
217
|
* middleware-provided objects, or other registered values.
|
|
223
218
|
*
|
|
219
|
+
* Note that injected parameters will not appear in the client
|
|
220
|
+
* API.
|
|
221
|
+
*
|
|
224
222
|
* Example:
|
|
225
223
|
* ```ts
|
|
226
224
|
* @POST
|
|
@@ -240,14 +238,13 @@ export const Inject = () => { };
|
|
|
240
238
|
* client bindings automatically, removing the need to manually
|
|
241
239
|
* define common API operations.
|
|
242
240
|
*
|
|
243
|
-
*
|
|
244
|
-
* - **"SAVE"** — Performs an *upsert* (insert or
|
|
241
|
+
* CRUD Operations:
|
|
242
|
+
* - **"SAVE"** — Performs an *upsert* (insert, update, or both) for a model instance.
|
|
245
243
|
* - **"GET"** — Retrieves a single record by its primary key, optionally using a `DataSource`.
|
|
246
244
|
* - **"LIST"** — Retrieves all records for the model, using the specified `DataSource`.
|
|
247
|
-
* - **(future)** `"DELETE"` — Will remove a record by primary key once implemented.
|
|
248
245
|
*
|
|
249
246
|
* The generated methods are static, exposed through both the backend
|
|
250
|
-
*
|
|
247
|
+
* and the frontend client API.
|
|
251
248
|
*
|
|
252
249
|
* Example:
|
|
253
250
|
* ```ts
|
|
@@ -257,31 +254,11 @@ export const Inject = () => { };
|
|
|
257
254
|
* @PrimaryKey id: number;
|
|
258
255
|
* name: string;
|
|
259
256
|
* }
|
|
260
|
-
*
|
|
261
|
-
* // Generated methods (conceptually):
|
|
262
|
-
* // static async save(item: CrudHaver): Promise<HttpResult<CrudHaver>>
|
|
263
|
-
* // static async get(id: number, dataSource?: string): Promise<HttpResult<CrudHaver>>
|
|
264
|
-
* // static async list(dataSource?: string): Promise<HttpResult<CrudHaver[]>>
|
|
265
257
|
* ```
|
|
266
258
|
*/
|
|
267
259
|
export const CRUD = (_kinds) => () => { };
|
|
268
260
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* The `Orm` class uses the Cloesce metadata system to generate, execute,
|
|
272
|
-
* and map SQL queries for model classes decorated with `@D1`.
|
|
273
|
-
*
|
|
274
|
-
* Typical operations include:
|
|
275
|
-
* - `fromD1(db)` — create an ORM instance bound to a `D1Database`
|
|
276
|
-
* - `upsert()` — insert or update a model
|
|
277
|
-
* - `list()` — fetch all instances of a model
|
|
278
|
-
* - `get()` — fetch one instance by primary key
|
|
279
|
-
*
|
|
280
|
-
* Example:
|
|
281
|
-
* ```ts
|
|
282
|
-
* const orm = Orm.fromD1(env.db);
|
|
283
|
-
* const horses = (await orm.list(Horse, "default")).value;
|
|
284
|
-
* ```
|
|
261
|
+
* Exposes the ORM primitives Cloesce uses to interact with D1 databases.
|
|
285
262
|
*/
|
|
286
263
|
export class Orm {
|
|
287
264
|
db;
|
|
@@ -306,36 +283,16 @@ export class Orm {
|
|
|
306
283
|
static mapSql(ctor, records, includeTree = null) {
|
|
307
284
|
return mapSql(ctor, records, includeTree);
|
|
308
285
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Returns a SQL query to insert a model into the database. Uses an IncludeTree as a guide for
|
|
311
|
-
* foreign key relationships, only inserting the explicitly stated pattern in the tree.
|
|
312
|
-
*
|
|
313
|
-
* TODO: We should be able to leave primary keys and foreign keys undefined, with
|
|
314
|
-
* primary keys being auto incremented and foreign keys being assumed by navigation property
|
|
315
|
-
* context.
|
|
316
|
-
*
|
|
317
|
-
* @param ctor A model constructor.
|
|
318
|
-
* @param newModel The new model to insert.
|
|
319
|
-
* @param includeTree An include tree describing which foreign keys to join.
|
|
320
|
-
* @returns Either an error string, or the insert query string.
|
|
321
|
-
*/
|
|
322
|
-
static upsertQuery(ctor, newModel, includeTree = null) {
|
|
323
|
-
const { wasm } = RuntimeContainer.get();
|
|
324
|
-
const args = [
|
|
325
|
-
WasmResource.fromString(ctor.name, wasm),
|
|
326
|
-
WasmResource.fromString(JSON.stringify(newModel), wasm),
|
|
327
|
-
WasmResource.fromString(JSON.stringify(includeTree), wasm),
|
|
328
|
-
];
|
|
329
|
-
return invokeOrmWasm(wasm.upsert_model, args, wasm);
|
|
330
|
-
}
|
|
331
286
|
/**
|
|
332
287
|
* Executes an "upsert" query, adding or augmenting a model in the database.
|
|
288
|
+
*
|
|
333
289
|
* If a model's primary key is not defined in `newModel`, the query is assumed to be an insert.
|
|
290
|
+
*
|
|
334
291
|
* If a model's primary key _is_ defined, but some attributes are missing, the query is assumed to be an update.
|
|
292
|
+
*
|
|
335
293
|
* Finally, if the primary key is defined, but all attributes are included, a SQLite upsert will be performed.
|
|
336
294
|
*
|
|
337
|
-
*
|
|
338
|
-
* only if the primary key is an integer, in which case it will be auto incremented and assigned.
|
|
295
|
+
* In any other case, an error string will be returned.
|
|
339
296
|
*
|
|
340
297
|
* ### Inserting a new Model
|
|
341
298
|
* ```ts
|
|
@@ -370,91 +327,180 @@ export class Orm {
|
|
|
370
327
|
* @returns An error string, or the primary key of the inserted model.
|
|
371
328
|
*/
|
|
372
329
|
async upsert(ctor, newModel, includeTree = null) {
|
|
373
|
-
|
|
374
|
-
|
|
330
|
+
const { wasm } = RuntimeContainer.get();
|
|
331
|
+
const args = [
|
|
332
|
+
WasmResource.fromString(ctor.name, wasm),
|
|
333
|
+
WasmResource.fromString(JSON.stringify(newModel), wasm),
|
|
334
|
+
WasmResource.fromString(JSON.stringify(includeTree), wasm),
|
|
335
|
+
];
|
|
336
|
+
const upsertQueryRes = invokeOrmWasm(wasm.upsert_model, args, wasm);
|
|
337
|
+
if (upsertQueryRes.isLeft()) {
|
|
375
338
|
return upsertQueryRes;
|
|
376
339
|
}
|
|
377
|
-
|
|
378
|
-
const statements = upsertQueryRes.value
|
|
379
|
-
.split(";")
|
|
380
|
-
.map((s) => s.trim())
|
|
381
|
-
.filter((s) => s.length > 0);
|
|
340
|
+
const statements = JSON.parse(upsertQueryRes.unwrap());
|
|
382
341
|
// One of these statements is a "SELECT", which is the root model id stmt.
|
|
383
342
|
let selectIndex;
|
|
384
343
|
for (let i = statements.length - 1; i >= 0; i--) {
|
|
385
|
-
if (/^SELECT/i.test(statements[i])) {
|
|
344
|
+
if (/^SELECT/i.test(statements[i].query)) {
|
|
386
345
|
selectIndex = i;
|
|
387
346
|
break;
|
|
388
347
|
}
|
|
389
348
|
}
|
|
390
349
|
// Execute all statements in a batch.
|
|
391
|
-
const batchRes = await this.db.batch(statements.map((s) => this.db.prepare(s)));
|
|
350
|
+
const batchRes = await this.db.batch(statements.map((s) => this.db.prepare(s.query).bind(...s.values)));
|
|
392
351
|
if (!batchRes.every((r) => r.success)) {
|
|
393
352
|
const failed = batchRes.find((r) => !r.success);
|
|
394
|
-
return left(failed?.error ?? "D1 batch failed, but no error was returned.");
|
|
353
|
+
return Either.left(failed?.error ?? "D1 batch failed, but no error was returned.");
|
|
395
354
|
}
|
|
396
355
|
// Return the result of the SELECT statement
|
|
397
356
|
const selectResult = batchRes[selectIndex].results[0];
|
|
398
|
-
return right(selectResult.id);
|
|
357
|
+
return Either.right(selectResult.id);
|
|
399
358
|
}
|
|
400
359
|
/**
|
|
401
|
-
* Returns a query
|
|
360
|
+
* Returns a select query, creating a CTE view for the model using the provided include tree.
|
|
361
|
+
*
|
|
362
|
+
* @param ctor The model constructor.
|
|
363
|
+
* @param includeTree An include tree describing which related models to join.
|
|
364
|
+
* @param from An optional custom `FROM` clause to use instead of the base table.
|
|
365
|
+
* @param tagCte An optional CTE name to tag the query with. Defaults to "Model.view".
|
|
366
|
+
*
|
|
367
|
+
* ### Example:
|
|
368
|
+
* ```ts
|
|
369
|
+
* // Using a data source
|
|
370
|
+
* const query = Orm.listQuery(Person, "default");
|
|
371
|
+
*
|
|
372
|
+
* // Using a custom from statement
|
|
373
|
+
* const query = Orm.listQuery(Person, null, "SELECT * FROM Person WHERE age > 18");
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* ### Example SQL output:
|
|
377
|
+
* ```sql
|
|
378
|
+
* WITH Person_view AS (
|
|
379
|
+
* SELECT
|
|
380
|
+
* "Person"."id" AS "id",
|
|
381
|
+
* ...
|
|
382
|
+
* FROM "Person"
|
|
383
|
+
* LEFT JOIN ...
|
|
384
|
+
* )
|
|
385
|
+
* SELECT * FROM Person_view
|
|
386
|
+
* ```
|
|
402
387
|
*/
|
|
403
|
-
static listQuery(ctor,
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
388
|
+
static listQuery(ctor, opts) {
|
|
389
|
+
const { wasm } = RuntimeContainer.get();
|
|
390
|
+
const args = [
|
|
391
|
+
WasmResource.fromString(ctor.name, wasm),
|
|
392
|
+
WasmResource.fromString(JSON.stringify(opts.includeTree ?? null), wasm),
|
|
393
|
+
WasmResource.fromString(JSON.stringify(opts.tagCte ?? null), wasm),
|
|
394
|
+
WasmResource.fromString(JSON.stringify(opts.from ?? null), wasm),
|
|
395
|
+
];
|
|
396
|
+
return invokeOrmWasm(wasm.list_models, args, wasm);
|
|
408
397
|
}
|
|
409
398
|
/**
|
|
410
|
-
* Returns a query
|
|
411
|
-
*
|
|
399
|
+
* Returns a select query for a single model by primary key, creating a CTE view using the provided include tree.
|
|
400
|
+
*
|
|
401
|
+
* @param ctor The model constructor.
|
|
402
|
+
* @param includeTree An include tree describing which related models to join.
|
|
403
|
+
*
|
|
404
|
+
* ### Example:
|
|
405
|
+
* ```ts
|
|
406
|
+
* // Using a data source
|
|
407
|
+
* const query = Orm.getQuery(Person, "default");
|
|
408
|
+
* ```
|
|
409
|
+
*
|
|
410
|
+
* ### Example SQL output:
|
|
411
|
+
*
|
|
412
|
+
* ```sql
|
|
413
|
+
* WITH Person_view AS (
|
|
414
|
+
* SELECT
|
|
415
|
+
* "Person"."id" AS "id",
|
|
416
|
+
* ...
|
|
417
|
+
* FROM "Person"
|
|
418
|
+
* LEFT JOIN ...
|
|
419
|
+
* )
|
|
420
|
+
* SELECT * FROM Person_view WHERE [Person].[id] = ?
|
|
421
|
+
* ```
|
|
412
422
|
*/
|
|
413
|
-
static getQuery(ctor, includeTree
|
|
423
|
+
static getQuery(ctor, includeTree) {
|
|
414
424
|
const { ast } = RuntimeContainer.get();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
return `${this.listQuery(ctor, includeTree)} WHERE [${ast.models[ctor.name].primary_key.name}] = ?`;
|
|
425
|
+
return this.listQuery(ctor, {
|
|
426
|
+
includeTree,
|
|
427
|
+
}).map((inner) => `${inner} WHERE [${ast.models[ctor.name].primary_key.name}] = ?`);
|
|
419
428
|
}
|
|
420
429
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
430
|
+
* Retrieves all instances of a model from the database.
|
|
431
|
+
* @param ctor The model constructor.
|
|
432
|
+
* @param includeTree An include tree describing which related models to join.
|
|
433
|
+
* @param from An optional custom `FROM` clause to use instead of the base table.
|
|
434
|
+
* @returns Either an error string, or an array of model instances.
|
|
435
|
+
*
|
|
436
|
+
* ### Example:
|
|
437
|
+
* ```ts
|
|
438
|
+
* const orm = Orm.fromD1(env.db);
|
|
439
|
+
* const horses = await orm.list(Horse, Horse.default);
|
|
440
|
+
* ```
|
|
441
|
+
*
|
|
442
|
+
* ### Example with custom from:
|
|
443
|
+
* ```ts
|
|
444
|
+
* const orm = Orm.fromD1(env.db);
|
|
445
|
+
* const adultHorses = await orm.list(Horse, Horse.default, "SELECT * FROM Horse ORDER BY age DESC LIMIT 10");
|
|
446
|
+
* ```
|
|
447
|
+
*
|
|
448
|
+
* =>
|
|
449
|
+
*
|
|
450
|
+
* ```sql
|
|
451
|
+
* SELECT
|
|
452
|
+
* "Horse"."id" AS "id",
|
|
453
|
+
* ...
|
|
454
|
+
* FROM (SELECT * FROM Horse ORDER BY age DESC LIMIT 10)
|
|
455
|
+
* LEFT JOIN ...
|
|
456
|
+
* ```
|
|
457
|
+
*
|
|
423
458
|
*/
|
|
424
|
-
async list(ctor,
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
return left(res.error ?? "D1 failed but no error was returned.");
|
|
459
|
+
async list(ctor, opts) {
|
|
460
|
+
const queryRes = Orm.listQuery(ctor, opts);
|
|
461
|
+
if (queryRes.isLeft()) {
|
|
462
|
+
return Either.left(queryRes.value);
|
|
429
463
|
}
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
464
|
+
const stmt = this.db.prepare(queryRes.value);
|
|
465
|
+
const records = await stmt.all();
|
|
466
|
+
if (!records.success) {
|
|
467
|
+
return Either.left(records.error ?? "D1 query failed, but no error was returned.");
|
|
468
|
+
}
|
|
469
|
+
const mapRes = Orm.mapSql(ctor, records.results, opts.includeTree ?? null);
|
|
470
|
+
if (mapRes.isLeft()) {
|
|
471
|
+
return Either.left(mapRes.value);
|
|
437
472
|
}
|
|
438
|
-
return right(
|
|
473
|
+
return Either.right(mapRes.value);
|
|
439
474
|
}
|
|
440
475
|
/**
|
|
441
|
-
*
|
|
442
|
-
*
|
|
476
|
+
* Retrieves a single model by primary key.
|
|
477
|
+
* @param ctor The model constructor.
|
|
478
|
+
* @param id The primary key value.
|
|
479
|
+
* @param includeTree An include tree describing which related models to join.
|
|
480
|
+
* @returns Either an error string, or the model instance (null if not found).
|
|
481
|
+
*
|
|
482
|
+
* ### Example:
|
|
483
|
+
* ```ts
|
|
484
|
+
* const orm = Orm.fromD1(env.db);
|
|
485
|
+
* const horse = await orm.get(Horse, 1, Horse.default);
|
|
486
|
+
* ```
|
|
443
487
|
*/
|
|
444
|
-
async get(ctor, id,
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return left(res.error ?? "D1 failed but no error was returned.");
|
|
488
|
+
async get(ctor, id, includeTree) {
|
|
489
|
+
const queryRes = Orm.getQuery(ctor, includeTree);
|
|
490
|
+
if (queryRes.isLeft()) {
|
|
491
|
+
return Either.left(queryRes.value);
|
|
449
492
|
}
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
493
|
+
const record = await this.db.prepare(queryRes.value).bind(id).run();
|
|
494
|
+
if (!record.success) {
|
|
495
|
+
return Either.left(record.error ?? "D1 query failed, but no error was returned.");
|
|
496
|
+
}
|
|
497
|
+
if (record.results.length === 0) {
|
|
498
|
+
return Either.right(null);
|
|
499
|
+
}
|
|
500
|
+
const mapRes = Orm.mapSql(ctor, record.results, includeTree);
|
|
501
|
+
if (mapRes.isLeft()) {
|
|
502
|
+
return Either.left(mapRes.value);
|
|
457
503
|
}
|
|
458
|
-
return right(
|
|
504
|
+
return Either.right(mapRes.value[0]);
|
|
459
505
|
}
|
|
460
506
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloesce",
|
|
3
|
-
"version": "0.0.4-unstable.
|
|
3
|
+
"version": "0.0.4-unstable.7",
|
|
4
4
|
"description": "A tool to extract and compile TypeScript code into something wrangler can consume and deploy for D1 Databases and Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|