cloesce 0.0.4-unstable.4 → 0.0.4-unstable.5

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/dist/cli.js CHANGED
@@ -29,12 +29,11 @@ const cmds = subcommands({
29
29
  // Creates a `cidl.json` file. Exits the process on failure.
30
30
  await extract({ debug: args.debug });
31
31
  const outputDir = config.outputDir ?? ".generated";
32
- const allConfig = {
33
- name: "all",
32
+ const generateConfig = {
33
+ name: "generate",
34
34
  wasmFile: "generator.wasm",
35
35
  args: [
36
36
  "generate",
37
- "all",
38
37
  path.join(outputDir, "cidl.pre.json"),
39
38
  path.join(outputDir, "cidl.json"),
40
39
  "wrangler.toml",
@@ -45,7 +44,7 @@ const cmds = subcommands({
45
44
  ],
46
45
  };
47
46
  // Runs a generator command. Exits the process on failure.
48
- await generate(allConfig);
47
+ await generate(generateConfig);
49
48
  },
50
49
  }),
51
50
  extract: command({
@@ -1,5 +1,5 @@
1
1
  import { D1Database } from "@cloudflare/workers-types/experimental/index.js";
2
- import { CrudKind, Either, KeysOfType } from "../common.js";
2
+ import { CrudKind, DeepPartial, Either, KeysOfType } from "../common.js";
3
3
  export { cloesce } from "../router/router.js";
4
4
  export type { HttpResult, Either, DeepPartial, InstanceRegistry, CrudKind, } from "../common.js";
5
5
  export { CloesceApp } from "../common.js";
@@ -375,7 +375,7 @@ export declare class Orm {
375
375
  * @param records D1 Result records
376
376
  * @param includeTree Include tree to define the relationships to join.
377
377
  */
378
- static mapSql<T extends object>(ctor: new () => T, records: Record<string, any>[], includeTree: IncludeTree<T> | null): Either<string, T[]>;
378
+ static mapSql<T extends object>(ctor: new () => T, records: Record<string, any>[], includeTree?: IncludeTree<T> | null): Either<string, T[]>;
379
379
  /**
380
380
  * Returns a SQL query to insert a model into the database. Uses an IncludeTree as a guide for
381
381
  * foreign key relationships, only inserting the explicitly stated pattern in the tree.
@@ -389,7 +389,7 @@ export declare class Orm {
389
389
  * @param includeTree An include tree describing which foreign keys to join.
390
390
  * @returns Either an error string, or the insert query string.
391
391
  */
392
- static upsertQuery<T extends object>(ctor: new () => T, newModel: T, includeTree: IncludeTree<T> | null): Either<string, string>;
392
+ static upsertQuery<T extends object>(ctor: new () => T, newModel: DeepPartial<T>, includeTree?: IncludeTree<T> | null): Either<string, string>;
393
393
  /**
394
394
  * Executes an "upsert" query, adding or augmenting a model in the database.
395
395
  * If a model's primary key is not defined in `newModel`, the query is assumed to be an insert.
@@ -431,25 +431,25 @@ export declare class Orm {
431
431
  * @param includeTree An include tree describing which foreign keys to join.
432
432
  * @returns An error string, or the primary key of the inserted model.
433
433
  */
434
- upsert<T extends object>(ctor: new () => T, newModel: T, includeTree: IncludeTree<T> | null): Promise<Either<string, any>>;
434
+ upsert<T extends object>(ctor: new () => T, newModel: DeepPartial<T>, includeTree?: IncludeTree<T> | null): Promise<Either<string, any>>;
435
435
  /**
436
436
  * Returns a query of the form `SELECT * FROM [Model.DataSource]`
437
437
  */
438
- static listQuery<T extends object>(ctor: new () => T, includeTree: KeysOfType<T, IncludeTree<T>> | null): string;
438
+ static listQuery<T extends object>(ctor: new () => T, includeTree?: KeysOfType<T, IncludeTree<T>> | null): string;
439
439
  /**
440
440
  * Returns a query of the form `SELECT * FROM [Model.DataSource] WHERE [PrimaryKey] = ?`.
441
441
  * Requires the id parameter to be bound (use db.prepare().bind)
442
442
  */
443
- static getQuery<T extends object>(ctor: new () => T, includeTree: KeysOfType<T, IncludeTree<T>> | null): string;
443
+ static getQuery<T extends object>(ctor: new () => T, includeTree?: KeysOfType<T, IncludeTree<T>> | null): string;
444
444
  /**
445
445
  * Executes a query of the form `SELECT * FROM [Model.DataSource]`, returning all results
446
446
  * as instantiated models.
447
447
  */
448
- list<T extends object>(ctor: new () => T, includeTreeKey: KeysOfType<T, IncludeTree<T>> | null): Promise<Either<string, T[]>>;
448
+ list<T extends object>(ctor: new () => T, includeTreeKey?: KeysOfType<T, IncludeTree<T>> | null): Promise<Either<string, T[]>>;
449
449
  /**
450
450
  * Executes a query of the form `SELECT * FROM [Model.DataSource] WHERE [Model.PrimaryKey] = ?`
451
451
  * returning all results as instantiated models.
452
452
  */
453
- get<T extends object>(ctor: new () => T, id: any, includeTreeKey: KeysOfType<T, IncludeTree<T>> | null): Promise<Either<string, T>>;
453
+ get<T extends object>(ctor: new () => T, id: any, includeTreeKey?: KeysOfType<T, IncludeTree<T>> | null): Promise<Either<string, T>>;
454
454
  }
455
455
  //# sourceMappingURL=backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/ui/backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/ui/backend.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { left, right } from "../common.js";
1
+ import { left, right, } 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";
@@ -303,7 +303,7 @@ export class Orm {
303
303
  * @param records D1 Result records
304
304
  * @param includeTree Include tree to define the relationships to join.
305
305
  */
306
- static mapSql(ctor, records, includeTree) {
306
+ static mapSql(ctor, records, includeTree = null) {
307
307
  return mapSql(ctor, records, includeTree);
308
308
  }
309
309
  /**
@@ -319,7 +319,7 @@ export class Orm {
319
319
  * @param includeTree An include tree describing which foreign keys to join.
320
320
  * @returns Either an error string, or the insert query string.
321
321
  */
322
- static upsertQuery(ctor, newModel, includeTree) {
322
+ static upsertQuery(ctor, newModel, includeTree = null) {
323
323
  const { wasm } = RuntimeContainer.get();
324
324
  const args = [
325
325
  WasmResource.fromString(ctor.name, wasm),
@@ -369,7 +369,7 @@ export class Orm {
369
369
  * @param includeTree An include tree describing which foreign keys to join.
370
370
  * @returns An error string, or the primary key of the inserted model.
371
371
  */
372
- async upsert(ctor, newModel, includeTree) {
372
+ async upsert(ctor, newModel, includeTree = null) {
373
373
  let upsertQueryRes = Orm.upsertQuery(ctor, newModel, includeTree);
374
374
  if (!upsertQueryRes.ok) {
375
375
  return upsertQueryRes;
@@ -400,7 +400,7 @@ export class Orm {
400
400
  /**
401
401
  * Returns a query of the form `SELECT * FROM [Model.DataSource]`
402
402
  */
403
- static listQuery(ctor, includeTree) {
403
+ static listQuery(ctor, includeTree = null) {
404
404
  if (includeTree) {
405
405
  return `SELECT * FROM [${ctor.name}.${includeTree.toString()}]`;
406
406
  }
@@ -410,7 +410,7 @@ export class Orm {
410
410
  * Returns a query of the form `SELECT * FROM [Model.DataSource] WHERE [PrimaryKey] = ?`.
411
411
  * Requires the id parameter to be bound (use db.prepare().bind)
412
412
  */
413
- static getQuery(ctor, includeTree) {
413
+ static getQuery(ctor, includeTree = null) {
414
414
  const { ast } = RuntimeContainer.get();
415
415
  if (includeTree) {
416
416
  return `${this.listQuery(ctor, includeTree)} WHERE [${ast.models[ctor.name].primary_key.name}] = ?`;
@@ -421,7 +421,7 @@ export class Orm {
421
421
  * Executes a query of the form `SELECT * FROM [Model.DataSource]`, returning all results
422
422
  * as instantiated models.
423
423
  */
424
- async list(ctor, includeTreeKey) {
424
+ async list(ctor, includeTreeKey = null) {
425
425
  const q = Orm.listQuery(ctor, includeTreeKey);
426
426
  const res = await this.db.prepare(q).run();
427
427
  if (!res.success) {
@@ -441,7 +441,7 @@ export class Orm {
441
441
  * Executes a query of the form `SELECT * FROM [Model.DataSource] WHERE [Model.PrimaryKey] = ?`
442
442
  * returning all results as instantiated models.
443
443
  */
444
- async get(ctor, id, includeTreeKey) {
444
+ async get(ctor, id, includeTreeKey = null) {
445
445
  const q = Orm.getQuery(ctor, includeTreeKey);
446
446
  const res = await this.db.prepare(q).bind(id).run();
447
447
  if (!res.success) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloesce",
3
- "version": "0.0.4-unstable.4",
3
+ "version": "0.0.4-unstable.5",
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",
package/README.md DELETED
@@ -1,649 +0,0 @@
1
- # cloesce (unstable, `v0.0.4`)
2
-
3
- Cloesce is a full stack compiler for the Cloudflare developer platform, allowing class definitions in high level languages to serve as a metadata basis to create a database schema, backend REST API, frontend API client, and Cloudflare infrastructure (as of v0.0.4, D1 + Workers).
4
-
5
- Cloesce is working towards a stable alpha MVP (v0.1.0), with the general milestones being [here](https://cloesce.pages.dev/schreiber/v0.1.0_milestones/).
6
-
7
- Internal documentation going over design decisions and general thoughts for each milestone can be found [here](https://cloesce.pages.dev/).
8
-
9
- # Documentation `v0.0.4`
10
-
11
- ## Getting Started
12
-
13
- `v0.0.4` supports only Typescript-to-Typescript projects. An example project is shown [here](https://github.com/bens-schreiber/cloesce/tree/main/examples).
14
-
15
- 1. NPM
16
-
17
- - Create an NPM project and install cloesce
18
-
19
- ```sh
20
- npm i cloesce@0.0.4-unstable.3
21
- ```
22
-
23
- 2. TypeScript
24
-
25
- - Create a `tsconfig.json` with the following values:
26
-
27
- ```json
28
- {
29
- "compilerOptions": {
30
- // ...
31
- "resolveJsonModule": true,
32
- "strict": true,
33
- "strictPropertyInitialization": false,
34
- "experimentalDecorators": true,
35
- "emitDecoratorMetadata": true
36
- },
37
- "include": ["<your_src_dir>/**/*.ts", ".generated/*.ts"]
38
- }
39
- ```
40
-
41
- 3. Cloesce Config
42
-
43
- - Create a `cloesce.config.json` with the following keys:
44
-
45
- ```json
46
- {
47
- "source": "./src",
48
- "workersUrl": "http://localhost:5000/api",
49
- "clientUrl": "http://localhost:5173/api"
50
- }
51
- ```
52
-
53
- 4. Vite
54
-
55
- To prevent CORS issues, a Vite proxy can be used for the frontend:
56
-
57
- ```ts
58
- import { defineConfig } from "vite";
59
-
60
- export default defineConfig({
61
- server: {
62
- port: 5173,
63
- proxy: {
64
- "/api": {
65
- target: "http://localhost:5000",
66
- changeOrigin: true,
67
- },
68
- },
69
- },
70
- });
71
- ```
72
-
73
- 5. Wrangler Config
74
-
75
- - `v0.0.4` will generate the required areas of your wrangler config. A full config looks like this:
76
-
77
- ```toml
78
- compatibility_date = "2025-10-02"
79
- main = ".generated/workers.ts"
80
- name = "example"
81
-
82
- [[d1_databases]]
83
- binding = "db"
84
- database_id = "..."
85
- database_name = "example"
86
- ```
87
-
88
- ## A Simple Model
89
-
90
- A model is a type which represents:
91
-
92
- - a database table,
93
- - database views
94
- - REST API
95
- - Client API
96
- - Cloudflare infrastructure (D1 + Workers)
97
-
98
- Suprisingly, it's pretty compact. A basic model looks like this:
99
-
100
- ```ts
101
- // horse.cloesce.ts
102
- import { D1, GET, POST, PrimaryKey } from "cloesce/backend";
103
-
104
- @D1
105
- export class Horse {
106
- @PrimaryKey
107
- id: number;
108
-
109
- name: string | null;
110
-
111
- @POST
112
- neigh(): string {
113
- return `i am ${this.name}, this is my horse noise`;
114
- }
115
- }
116
- ```
117
-
118
- - `@D1` denotes that this is a SQL Table
119
- - `@PrimaryKey` sets the SQL primary key. All models require a primary key.
120
- - `@POST` reveals the method as an API endpoint with the `POST` HTTP Verb.
121
- - All Cloesce models need to be under a `.cloesce.ts` file.
122
-
123
- To compile this model into a working full stack application, Cloesce must undergo both **compilation** and **migrations**. Compilation is the process of extracting the metadata language that powers Cloesce, ensuring it is a valid program, and then producing code to orchestrate the program across different domains (database, backend, frontend, cloud). Migrations utilize the history of validated metadata to create SQL code, translating the evolution of your models.
124
-
125
- To compile, run `npx cloesce compile`.
126
-
127
- To create a migration, run `npx cloesce migrate <name>`.
128
-
129
- After running the above commands, you will have a full project capable of being ran with:
130
-
131
- ```sh
132
- # Apply the generated migrations
133
- npx wrangler d1 migrations apply <db-name>
134
-
135
- # Run the backend
136
- npx wrangler dev
137
- ```
138
-
139
- Note the output in the `.generated/` dir. These values should not be committed to git, as they depend on the file system of the machine running it.
140
-
141
- - `client.ts` is an importable API with all of your backend types and endpoints
142
- - `workers.ts` is the workers entrypoint.
143
- - `cidl.json` is the working metadata for the project
144
-
145
- Note the output in `migrations`, ex after running `npx cloesce migrate Initial`
146
-
147
- - `<date>_Initial.json` contains all model information necessary from SQL
148
- - `<date>_Initial.sql` contains the acual SQL migrations. In this early version of Cloesce, it's important to check migrations every time.
149
-
150
- #### Supported Column Types
151
-
152
- Model columns must directly map to SQLite columns. The supported TypeScript types are:
153
-
154
- - `number` => `Real` not null
155
- - `string` => `Text` not null
156
- - `boolean` and `Boolean` => `Integer` not null
157
- - `Integer` => `Integer` not null
158
- - `Date` => `Text` (ISO formatted) not null
159
- - `N | null` => `N` (nullable)
160
-
161
- Blob types will be added in v0.0.5 when R2 support is added.
162
-
163
- ## Features
164
-
165
- ### Wrangler Environment
166
-
167
- In order to interact with your database, you will need to define a WranglerEnv
168
-
169
- ```ts
170
- import { WranglerEnv } from "cloesce/backend";
171
-
172
- @WranglerEnv
173
- export class Env {
174
- db: D1Database; // only one DB is supported for now-- make sure it matches the name in `wrangler.toml`
175
-
176
- // you can also define values in the toml under [[vars]]
177
- motd: string;
178
- }
179
- ```
180
-
181
- The wrangler environment is dependency injected into your method calls:
182
-
183
- ```ts
184
- @D1
185
- export class Horse {
186
- @PrimaryKey
187
- id: number;
188
-
189
- @POST
190
- async neigh(@Inject env: WranglerEnv): Promise<string> {
191
- await env.db.prepare(...);
192
-
193
- return `i am ${this.name}, this is my horse noise`;
194
- }
195
- }
196
- ```
197
-
198
- ### Foreign Keys, One to One, Data Sources
199
-
200
- Complex model relationships are permitted via the `@ForeignKey`, `@OneToOne / @OneToMany @ManyToMany` and `@DataSource` decorators.
201
- Foreign keys are scalar attributes which must reference some other model's primary key:
202
-
203
- ```ts
204
- @D1
205
- export class Dog {
206
- @PrimaryKey
207
- id: number;
208
- }
209
-
210
- @D1
211
- export class Person {
212
- @PrimaryKey
213
- id: number;
214
-
215
- @ForeignKey(Dog)
216
- dogId: number;
217
- }
218
- ```
219
-
220
- This representation is true to the underlying SQL table: `Person` has a column `dogId` which is a foreign key to `Dog`. Cloesce allows you to actually join these tables together in your model representation:
221
-
222
- ```ts
223
- @D1
224
- export class Dog {
225
- @PrimaryKey
226
- id: number;
227
- }
228
-
229
- @D1
230
- export class Person {
231
- @PrimaryKey
232
- id: number;
233
-
234
- @ForeignKey(Dog)
235
- dogId: number;
236
-
237
- @OneToOne("dogId") // references Person.dogId
238
- dog: Dog | undefined; // This value is a "navigation property", which may or may not exist at runtime
239
- }
240
- ```
241
-
242
- In `v0.0.4`, there are no defaults, only very explicit decisons. Because of that, navigation properties won't exist at runtime unless you tell them to. Cloesce does this via a `DataSource`, which describes the foreign key dependencies you wish to include. All scalar properties are included by default and cannot be excluded.
243
-
244
- ```ts
245
- @D1
246
- export class Dog {
247
- @PrimaryKey
248
- id: number;
249
- }
250
-
251
- @D1
252
- export class Person {
253
- @PrimaryKey
254
- id: number;
255
-
256
- @ForeignKey(Dog)
257
- dogId: number;
258
-
259
- @OneToOne("dogId")
260
- dog: Dog | undefined;
261
-
262
- @DataSource
263
- static readonly default: IncludeTree<Person> = {
264
- dog: {}, // says: on model population, join Persons's Dog
265
- };
266
- }
267
- ```
268
-
269
- Data sources are just SQL views and can be invoked in your queries. They are aliased in such a way that its similiar to object properties. The frontend chooses which datasource to use in it's API client (all instantiated methods have an implicit DataSource parameter). `null` is a valid option, meaning no joins will occur.
270
-
271
- ```ts
272
- @D1
273
- export class Person {
274
- @PrimaryKey
275
- id: number;
276
-
277
- @ForeignKey(Dog)
278
- dogId: number;
279
-
280
- @OneToOne("dogId")
281
- dog: Dog | undefined;
282
-
283
- @DataSource
284
- static readonly default: IncludeTree<Person> = {
285
- dog: {},
286
- };
287
-
288
- @GET
289
- static async get(id: number, @Inject env: WranglerEnv): Promise<Person> {
290
- let records = await env.db
291
- .prepare("SELECT * FROM [Person.default] WHERE [id] = ?") // Person.default is the SQL view generated from the IncludeTree
292
- .bind(id)
293
- .run();
294
-
295
- let persons = Orm.mapSql(Person, records.results, Person.default);
296
- return persons.value[0];
297
- }
298
- }
299
- ```
300
-
301
- Note that the `get` code can be simplified using CRUD methods or the ORM primitive `get`.
302
-
303
- #### View Aliasing
304
-
305
- The generated views will always be aliased so that they can be accessed in an object like notation. For example, given some `Horse` that has a relationship with `Like`:
306
-
307
- ```ts
308
- @D1
309
- export class Horse {
310
- @PrimaryKey
311
- id: Integer;
312
-
313
- name: string;
314
- bio: string | null;
315
-
316
- @OneToMany("horseId1")
317
- likes: Like[];
318
-
319
- @DataSource
320
- static readonly default: IncludeTree<Horse> = {
321
- likes: { horse2: {} },
322
- };
323
- }
324
-
325
- @D1
326
- export class Like {
327
- @PrimaryKey
328
- id: Integer;
329
-
330
- @ForeignKey(Horse)
331
- horseId1: Integer;
332
-
333
- @ForeignKey(Horse)
334
- horseId2: Integer;
335
-
336
- @OneToOne("horseId2")
337
- horse2: Horse | undefined;
338
- }
339
- ```
340
-
341
- If you wanted to find all horses that like one another, a valid SQL query using the `default` data source would look like:
342
-
343
- ```sql
344
- SELECT * FROM [Horse.default] as H1
345
- WHERE
346
- H1.[id] = ?
347
- AND EXISTS (
348
- SELECT 1
349
- FROM [Horse.default] AS H2
350
- WHERE H2.[id] = H1.[likes.horse2.id]
351
- AND H2.[likes.horse2.id] = H1.[id]
352
- );
353
- ```
354
-
355
- The actual generated view for `default` looks like:
356
-
357
- ```sql
358
- CREATE VIEW IF NOT EXISTS "Horse.default" AS
359
- SELECT
360
- "Horse"."id" AS "id",
361
- "Horse"."name" AS "name",
362
- "Horse"."bio" AS "bio",
363
- "Like"."id" AS "likes.id",
364
- "Like"."horseId1" AS "likes.horseId1",
365
- "Like"."horseId2" AS "likes.horseId2",
366
- "Horse1"."id" AS "likes.horse2.id",
367
- "Horse1"."name" AS "likes.horse2.name",
368
- "Horse1"."bio" AS "likes.horse2.bio"
369
- FROM
370
- "Horse"
371
- LEFT JOIN
372
- "Like" ON "Horse"."id" = "Like"."horseId1"
373
- LEFT JOIN
374
- "Horse" AS "Horse1" ON "Like"."horseId2" = "Horse1"."id";
375
- ```
376
-
377
- #### DataSourceOf<T>
378
-
379
- If it is important to determine what data source the frontend called the instantiated method with, the type `DataSourceOf<T>` allows explicit data source parameters:
380
-
381
- ```ts
382
- @D1
383
- class Foo {
384
- ...
385
-
386
- @POST
387
- bar(ds: DataSourceOf<Foo>) {
388
- // ds = "DataSource1" | "DataSource2" | ... | "none"
389
- }
390
- }
391
- ```
392
-
393
- ### One to Many
394
-
395
- Cloesce supports models with `1:M` relationships:
396
-
397
- ```ts
398
- @D1
399
- export class Person {
400
- @PrimaryKey
401
- id: number;
402
-
403
- @OneToMany("personId") // directly references the FK on Dog
404
- dogs: Dog[];
405
-
406
- @DataSource
407
- static readonly default: IncludeTree<Person> = {
408
- dogs: {
409
- person: {
410
- dogs: {
411
- // essentially means: "When you get a person, get their dogs, and get all of those dog's Person, ..."
412
- // we could go on as long as we want
413
- },
414
- },
415
- },
416
- };
417
- }
418
-
419
- @D1
420
- export class Dog {
421
- @PrimaryKey
422
- id: number;
423
-
424
- @ForeignKey(Person)
425
- personId: number;
426
-
427
- // optional navigation property, not needed.
428
- @OneToOne("personId")
429
- person: Person | undefined;
430
- }
431
- ```
432
-
433
- ### Many to Many
434
-
435
- ```ts
436
- @D1
437
- export class Student {
438
- @PrimaryKey
439
- id: number;
440
-
441
- @ManyToMany("StudentsCourses") // unique ID for the generated junction table
442
- courses: Course[];
443
- }
444
-
445
- @D1
446
- export class Course {
447
- @PrimaryKey
448
- id: number;
449
-
450
- @ManyToMany("StudentsCourses") // same unique id => same jct table.
451
- students: Student[];
452
- }
453
- ```
454
-
455
- ### ORM Methods
456
-
457
- Cloesce provides a suite of ORM methods for getting, listing, updating and inserting models.
458
-
459
- #### Upsert
460
-
461
- ```ts
462
- @D1
463
- class Horse {
464
- // ...
465
-
466
- @POST
467
- static async post(@Inject { db }: Env, horse: Horse): Promise<Horse> {
468
- const orm = Orm.fromD1(db);
469
- await orm.upsert(Horse, horse, null);
470
- return (await orm.get(Horse, horse.id, null)).value;
471
- }
472
- }
473
- ```
474
-
475
- #### List, Get
476
-
477
- ```ts
478
- @D1
479
- class Horse {
480
- // ...
481
- @GET
482
- static async get(@Inject { db }: Env, id: number): Promise<Horse> {
483
- const orm = Orm.fromD1(db);
484
- return (await orm.get(Horse, id, "default")).value;
485
- }
486
-
487
- @GET
488
- static async list(@Inject { db }: Env): Promise<Horse[]> {
489
- const orm = Orm.fromD1(db);
490
- return (await orm.list(Horse, "default")).value;
491
- }
492
- }
493
- ```
494
-
495
- ### CRUD Methods
496
-
497
- Generic `GET, POST, PATCH` (and in a future version, DEL) boilerplate methods do not need to be copied around. Cloesce supports CRUD generation, a syntactic sugar that adds the methods to the compiler output.
498
-
499
- The `SAVE` method is an `upsert`, meaning it both inserts and updates in the same query.
500
-
501
- ```ts
502
- @CRUD(["SAVE", "GET", "LIST"])
503
- @D1
504
- export class CrudHaver {
505
- @PrimaryKey
506
- id: number;
507
- name: string;
508
- }
509
- ```
510
-
511
- which will generate client API methods like:
512
-
513
- ```ts
514
- static async get(
515
- id: number,
516
- dataSource: "none" = "none",
517
- ): Promise<HttpResult<CrudHaver>>
518
- ```
519
-
520
- ### Middleware
521
-
522
- Cloesce supports middleware at the global level (before routing to a model+method), the model level (before hydration) and the method level (before hydration). Middleware also exposes read/write access to the dependency injection instance that all models use.
523
-
524
- Middleware is capable of exiting from the Cloesce Router early with an HTTP Result.
525
-
526
- An example of all levels of middleware is below. All middleware must be defined in the file `app.cloesce.ts`.
527
-
528
- ```ts
529
- @PlainOldObject
530
- export class InjectedThing {
531
- value: string;
532
- }
533
-
534
- @WranglerEnv
535
- export class Env {
536
- db: D1Database;
537
- }
538
-
539
- @D1
540
- @CRUD(["POST"])
541
- export class Model {
542
- @PrimaryKey
543
- id: number;
544
-
545
- @GET
546
- static blockedMethod() {}
547
-
548
- @GET
549
- static getInjectedThing(@Inject thing: InjectedThing): InjectedThing {
550
- return thing;
551
- }
552
- }
553
-
554
- // Middleware instance
555
- const app: CloesceApp = new CloesceApp();
556
-
557
- app.useGlobal((request: Request, env, ir) => {
558
- if (request.method === "POST") {
559
- return { ok: false, status: 401, message: "POST methods aren't allowed." };
560
- }
561
- });
562
-
563
- app.useModel(Model, (request, env, ir) => {
564
- ir.set(InjectedThing.name, {
565
- value: "hello world",
566
- });
567
- });
568
-
569
- app.useMethod(Model, "blockedMethod", (request, env, ir) => {
570
- return { ok: false, status: 401, message: "Blocked method" };
571
- });
572
-
573
- // Exporting the instance is required
574
- export default app;
575
- ```
576
-
577
- With this middleware, all POST methods will be blocked, and all methods for the model `Model` will be able to inject `InjectedThing`. Additionally, on the method level, `blockedMethod` will return a 401.
578
-
579
- ### Plain Old Objects
580
-
581
- Simple non-model objects can be returned and serialized from a model method:
582
-
583
- ```ts
584
- @PlainOldObject
585
- export class CatStuff {
586
- catFacts: string[],
587
- catNames: string[],
588
- }
589
-
590
- @D1
591
- export class Cat {
592
- @PrimaryKey
593
- id: number;
594
-
595
- @GET
596
- query(): CatStuff {
597
- return {
598
- catFacts: ["cats r cool"],
599
- catNames: ["reginald"]
600
- }
601
- }
602
- }
603
- ```
604
-
605
- ### HttpResult
606
-
607
- Methods can return any kind of status code via the `HttpResult` wrapper:
608
-
609
- ```ts
610
- @D1
611
- class Foo {
612
- ...
613
-
614
- @GET
615
- async foo(): Promise<HttpResult<number>> {
616
- return { ok: false, status: 500, message: "divided by 0"};
617
- }
618
- }
619
- ```
620
-
621
- # Testing the Compiler
622
-
623
- ## Unit Tests
624
-
625
- - `src/frontend/ts` run `npm test`
626
- - `src/generator` run `cargo test`
627
-
628
- ## Integration Tests
629
-
630
- - Regression tests: `cargo run --bin test regression`
631
- - Pass fail extractor tests: `cargo run --bin test run-fail`
632
-
633
- Optionally, pass `--check` if new snapshots should not be created.
634
-
635
- To update integration snapshots, run:
636
-
637
- - `cargo run --bin update`
638
-
639
- To delete any generated snapshots run:
640
-
641
- - `cargo run --bin update -- -d`
642
-
643
- ## E2E
644
-
645
- - `tests/e2e` run `npm test`
646
-
647
- ## Code Formatting
648
-
649
- - `cargo fmt`, `cargo clippy`, `npm run format:fix`