create-cloesce 0.3.3 → 0.4.0

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": "create-cloesce",
3
3
  "description": "Create Cloesce App",
4
- "version": "0.3.3",
4
+ "version": "0.4.0",
5
5
  "author": "Ben Schreiber <bpschreiber2003@gmail.com>",
6
6
  "repository": {
7
7
  "url": "https://github.com/bens-schreiber/create-cloesce-app"
@@ -1,5 +1,5 @@
1
1
  # Cloesce
2
- .generated
2
+ .cloesce
3
3
  dist
4
4
 
5
5
  ### Node ###
@@ -10,7 +10,7 @@
10
10
  "test": "vitest"
11
11
  },
12
12
  "dependencies": {
13
- "cloesce": ">=0.3.0",
13
+ "cloesce": ">=0.4.0",
14
14
  "wrangler": "^4.61.1"
15
15
  },
16
16
  "devDependencies": {
@@ -1,36 +1,45 @@
1
1
  // Here, we import the generated backend code, which includes all the types
2
2
  // defined in the `schema.clo` file
3
- import * as Cloesce from "@cloesce/backend.js";
3
+ import * as clo from "@cloesce/backend.js";
4
4
  import { CfReadableStream } from "@cloesce/backend.js";
5
5
 
6
6
  // The "cloesce" library provides basic types and utilities for building a Cloesce backend
7
7
  import { HttpResult } from "cloesce";
8
8
 
9
- // All API routes defined under a model in `schema.clo` are generated under their
10
- // respective models namespace.
9
+
10
+ // To implement the API routes of a model or service defined in `schema.clo`,
11
+ // we can use the `impl` method on it's respective generated namespace.
12
+ //
13
+ // In addition to defining the routes, `Weather` will inherit all of the static utility methods
14
+ // from the generated `Orm`, `Key` and `Source`.
11
15
  //
12
- // To implement an API route, simply extend the generated Api class.
13
- // If not implemented, the route will return a 501 Not Implemented error by default.
14
- export class Weather extends Cloesce.Weather.Api {
15
- async uploadPhoto(self: Cloesce.Weather.Self, e: Cloesce.Env, s: CfReadableStream): Promise<void> {
16
- // All models have a `KeyFormat` namespace which provides utilities for generating
16
+ // The only place where generated code should be directly used is in `impl` blocks like this.
17
+ export const Weather = clo.Weather.impl({
18
+ async uploadPhoto(self, e, s: CfReadableStream) {
19
+ // All models have a `Key` namespace which provides utilities for generating
17
20
  // KV and R2 keys for that model.
18
- const key = Cloesce.Weather.KeyFormat.photo(self.id);
21
+ const key = this.Key.photo(self.id);
19
22
  await e.bucket.put(key, s);
20
- }
23
+ },
21
24
 
22
- // Any method can return an HttpResult, which allows you to specify the
23
- // Response status code, body, and headers.
24
- downloadPhoto(self: Cloesce.Weather.Self): HttpResult<CfReadableStream> {
25
+ downloadPhoto(self) {
26
+ // Any method can return an HttpResult, which allows you to specify the
27
+ // Response status code, body, and headers.
25
28
  if (!self.photo) {
26
29
  return HttpResult.fail(404, "Photo not found");
27
30
  }
28
31
  return HttpResult.ok(200, self.photo.body);
29
32
  }
30
- }
33
+ });
34
+
35
+ // `WeatherReport` has no API routes defined.
36
+ //
37
+ // Instead of using the generated namespace directly, we can still create an implementation with `impl`
38
+ // to avoid passing the generated code around the rest of our application.
39
+ export const WeatherReport = clo.WeatherReport.impl({});
31
40
 
32
41
  export default {
33
- async fetch(request: Request, env: Cloesce.Env): Promise<Response> {
42
+ async fetch(request: Request, env: clo.Env): Promise<Response> {
34
43
  // preflight
35
44
  if (request.method === "OPTIONS") {
36
45
  return HttpResult.ok(200, undefined, {
@@ -41,8 +50,8 @@ export default {
41
50
  }
42
51
 
43
52
  // Run Cloesce app
44
- const app = (await Cloesce.cloesce())
45
- .register(new Weather());
53
+ const app = (await clo.cloesce())
54
+ .register(Weather);
46
55
 
47
56
  const result = await app.run(request, env);
48
57
 
@@ -16,9 +16,8 @@ env {
16
16
 
17
17
  // If a model is to be stored in a D1 database as a table, it must be decorated with `[use <binding>]`
18
18
  // The `list`, `get`, and `save` operations are used to generate CRUD operations for the model.
19
- // Optionally, these tags can be combined, e.g. `[use db, list, get, save]`
20
19
  [use db]
21
- [use list, save, get]
20
+ [crud list, save, get]
22
21
  model WeatherReport {
23
22
  // The `primary` block describes the primary key of the table.
24
23
  // It can be composed of several fields. Any number of primary blocks can be defined,
@@ -26,6 +25,12 @@ model WeatherReport {
26
25
  primary {
27
26
  id: int
28
27
  }
28
+
29
+ // Define regular SQL columns under the `column` block.
30
+ column {
31
+ title: string
32
+ description: string
33
+ }
29
34
 
30
35
  // A `nav` block describes a relationship between two models.
31
36
  // In this case, it is a one-to-many relationship between WeatherReport and Weather,
@@ -39,25 +44,27 @@ model WeatherReport {
39
44
  nav (Weather::weatherReportId) {
40
45
  weatherEntries
41
46
  }
42
-
43
- // All fields that are not apart of a block are just regular fields in the table.
44
- title: string
45
- description: string
46
47
  }
47
48
 
48
49
  [use db]
49
- [use get, list, save]
50
+ [crud get, list, save]
50
51
  model Weather {
51
52
  primary {
52
53
  id: int
53
54
  }
55
+
56
+ column {
57
+ dateTime: date
58
+ location: string
59
+ temperature: int
60
+ condition: string
61
+ }
54
62
 
55
63
  // A `foreign` block describes a one to one relationship between two models.
56
64
  // It translates directly to a foreign key constraint in the database (`weatherReportId` references `id` in WeatherReport).
57
65
  foreign (WeatherReport::id) {
58
66
  weatherReportId
59
67
 
60
-
61
68
  // A `nav` block inside a `foreign` block generates a navigation field,
62
69
  // meaning the backend and client will have a WeatherReport object named `weatherReport` nested inside the Weather model.
63
70
  nav {
@@ -68,11 +75,6 @@ model Weather {
68
75
  r2 (bucket, "weather/photos/{id}.jpg") {
69
76
  photo
70
77
  }
71
-
72
- dateTime: date
73
- location: string
74
- temperature: int
75
- condition: string
76
78
  }
77
79
 
78
80
  // An `api` block describes what endpoints must be implemented in the backend
@@ -85,7 +87,8 @@ api Weather {
85
87
  //
86
88
  // In this case, `uploadPhoto` accepts the Wrangler environment (dependency injected),
87
89
  // and a stream (the photo to be uploaded).
88
- post uploadPhoto(self, e: env, s: stream) -> void
90
+ [inject bucket]
91
+ post uploadPhoto(self, s: stream)
89
92
 
90
93
  // By default, Cloesce will hydrate a `self` instance with all 1:1, KV, R2,
91
94
  // and the near side of 1:M/M:M relationships.
@@ -23,10 +23,7 @@ const showResult = (outputId: string, result: any) => {
23
23
  };
24
24
 
25
25
  window.listReports = async () => {
26
- const result = await WeatherReport.$list({
27
- lastSeen_id: 0,
28
- limit: 100
29
- })
26
+ const result = await WeatherReport.$list(0, 100);
30
27
  showResult('list-output', result);
31
28
  };
32
29
 
@@ -51,9 +48,7 @@ window.addWeatherEntry = async () => {
51
48
  return;
52
49
  }
53
50
 
54
- const getResult = await WeatherReport.$get({
55
- id: reportId
56
- });
51
+ const getResult = await WeatherReport.$get(reportId);
57
52
  if (!getResult.ok) {
58
53
  showResult('entry-output', getResult);
59
54
  return;
@@ -1,7 +1,6 @@
1
1
  import { Miniflare } from "miniflare";
2
2
  import { describe, test, expect, beforeAll } from "vitest";
3
- import * as Cloesce from "@cloesce/backend.js";
4
- import { Weather } from "@api/main.js"
3
+ import { Weather, WeatherReport } from "@api/main.js"
5
4
  import { cloesce } from "@cloesce/backend.js";
6
5
 
7
6
  async function createTestEnv() {
@@ -56,7 +55,7 @@ describe("Miniflare Integration Tests", () => {
56
55
  const env = await createTestEnv();
57
56
  const testData = "test-data";
58
57
 
59
- const report = (await Cloesce.WeatherReport.save(env, {
58
+ const report = (await WeatherReport.Orm.save(env, {
60
59
  title: "Test Report",
61
60
  description: "This is a test weather report.",
62
61
  weatherEntries: [{
@@ -65,13 +64,13 @@ describe("Miniflare Integration Tests", () => {
65
64
  temperature: 25,
66
65
  condition: "Sunny"
67
66
  }]
68
- }))!;
67
+ })).value!;
69
68
 
70
- await new Weather().uploadPhoto(report.weatherEntries[0], env, testData as any);
69
+ await Weather.uploadPhoto(report.weatherEntries[0], env, testData as any);
71
70
 
72
71
  // Act
73
- const weatherEntries = (await Cloesce.Weather.DataSources.Default.list(env, 0, 100))!;
74
- const photo = new Weather().downloadPhoto(weatherEntries[0]);
72
+ const weatherEntries = (await Weather.Default.list(env, 0, 100)).value!;
73
+ const photo = Weather.downloadPhoto(weatherEntries[0]);
75
74
 
76
75
  // Assert
77
76
  expect(photo.ok).toBe(true);