cloesce 0.0.4-unstable.7 → 0.0.4-unstable.9

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 CHANGED
@@ -17,7 +17,7 @@ Internal documentation going over design decisions and general thoughts for each
17
17
  Create an NPM project and install cloesce
18
18
 
19
19
  ```sh
20
- npm i cloesce@0.0.4-unstable.7
20
+ npm i cloesce@0.0.4-unstable.9
21
21
  ```
22
22
 
23
23
  ### 2) TypeScript
@@ -70,6 +70,8 @@ export default defineConfig({
70
70
  });
71
71
  ```
72
72
 
73
+ Middleware support for CORS is also supported (see Middleware section).
74
+
73
75
  ### 5) Wrangler Config
74
76
 
75
77
  Cloesce will generate any missing `wrangler.toml` values (or the file if missing). A minimal `wrangler.toml` looks like this:
@@ -445,7 +447,7 @@ class Horse {
445
447
  await orm.list(
446
448
  Horse,
447
449
  Horse.default,
448
- "SELECT * FROM Horse ORDER BY name LIMIT 10"
450
+ "SELECT * FROM Horse ORDER BY name LIMIT 10",
449
451
  );
450
452
  ```
451
453
 
@@ -488,11 +490,11 @@ static async get(
488
490
 
489
491
  ### Middleware
490
492
 
491
- 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.
493
+ Cloesce supports middleware at the global level (before routing to a model+method), the model level (before validation) and the method level (before hydration). Middleware also exposes read/write access to the dependency injection instance that all models use.
492
494
 
493
495
  Middleware is capable of exiting from the Cloesce Router early with an HTTP Result.
494
496
 
495
- An example of all levels of middleware is below. All middleware must be defined in the file `app.cloesce.ts`.
497
+ An example of all levels of middleware is below. All middleware must be defined in the file `app.cloesce.ts` which exports a `CloesceApp` instance as default.
496
498
 
497
499
  ```ts
498
500
  @PlainOldObject
@@ -520,30 +522,41 @@ export class Model {
520
522
  }
521
523
  }
522
524
 
523
- // Middleware instance
524
525
  const app: CloesceApp = new CloesceApp();
525
526
 
526
- app.useGlobal((request: Request, env, ir) => {
527
+ app.onRequest((request: Request, env, ir) => {
527
528
  if (request.method === "POST") {
528
529
  return { ok: false, status: 401, message: "POST methods aren't allowed." };
529
530
  }
530
531
  });
531
532
 
532
- app.useModel(Model, (request, env, ir) => {
533
+ app.onModel(Model, (request, env, ir) => {
533
534
  ir.set(InjectedThing.name, {
534
535
  value: "hello world",
535
536
  });
536
537
  });
537
538
 
538
- app.useMethod(Model, "blockedMethod", (request, env, ir) => {
539
+ app.onMethod(Model, "blockedMethod", (request, env, ir) => {
539
540
  return { ok: false, status: 401, message: "Blocked method" };
540
541
  });
541
542
 
542
- // Exporting the instance is required
543
+ app.onResponse(async (request, env, di, response: Response) => {
544
+ // basic CORS, allow all origins
545
+ response.headers.set("Access-Control-Allow-Origin", "*");
546
+ response.headers.set(
547
+ "Access-Control-Allow-Methods",
548
+ "GET, POST, PUT, DELETE, OPTIONS",
549
+ );
550
+ response.headers.set(
551
+ "Access-Control-Allow-Headers",
552
+ "Content-Type, Authorization",
553
+ );
554
+ });
555
+
543
556
  export default app;
544
557
  ```
545
558
 
546
- 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.
559
+ With this middleware, all POST methods will be blocked, and all methods for the model `Model` will be able to inject `InjectedThing`,and `blockedMethod` will return a 401. Additionally, all responses will have CORS headers.
547
560
 
548
561
  ### Plain Old Objects
549
562
 
package/dist/cli.js CHANGED
@@ -22,8 +22,8 @@ const cmds = subcommands({
22
22
  },
23
23
  handler: async (args) => {
24
24
  const config = loadCloesceConfig(process.cwd(), args.debug);
25
- if (!config.workersUrl || !config.clientUrl) {
26
- console.error("Error: `workersUrl` and `clientUrl` must be defined in cloesce.config.json");
25
+ if (!config.workersUrl) {
26
+ console.error("Error: `workersUrl`` must be defined in cloesce.config.json");
27
27
  process.exit(1);
28
28
  }
29
29
  // Creates a `cidl.json` file. Exits the process on failure.
@@ -39,7 +39,6 @@ const cmds = subcommands({
39
39
  "wrangler.toml",
40
40
  path.join(outputDir, "workers.ts"),
41
41
  path.join(outputDir, "client.ts"),
42
- config.clientUrl,
43
42
  config.workersUrl,
44
43
  ],
45
44
  };
package/dist/common.d.ts CHANGED
@@ -134,103 +134,9 @@ export type HttpResult<T = unknown> = {
134
134
  data?: T;
135
135
  message?: string;
136
136
  };
137
- /**
138
- * Dependency injection container, mapping an object type name to an instance of that object.
139
- *
140
- * Comes with the WranglerEnv and Request by default.
141
- */
142
- export type InstanceRegistry = Map<string, any>;
143
- export type MiddlewareFn = (request: Request, env: any, ir: InstanceRegistry) => Promise<HttpResult | undefined>;
144
137
  export type KeysOfType<T, U> = {
145
138
  [K in keyof T]: T[K] extends U ? (K extends string ? K : never) : never;
146
139
  }[keyof T];
147
- /**
148
- * Represents the core middleware container for a Cloesce application.
149
- *
150
- * The `CloesceApp` class provides scoped middleware registration and
151
- * management across three primary levels of execution:
152
- *
153
- * 1. **Global Middleware** — Executed before any routing or model resolution occurs.
154
- * 2. **Model-Level Middleware** — Executed for requests targeting a specific model type.
155
- * 3. **Method-Level Middleware** — Executed for requests targeting a specific method on a model.
156
- *
157
- * When an instance of `CloesceApp` is exported from `app.cloesce.ts`,
158
- * it becomes the central container that the Cloesce runtime uses to
159
- * assemble and apply middleware in the correct execution order.
160
- *
161
- * ### Middleware Execution Order
162
- * Middleware is executed in FIFO order per scope. For example:
163
- * ```ts
164
- * app.use(Foo, A);
165
- * app.use(Foo, B);
166
- * app.use(Foo, C);
167
- * // Executed in order: A → B → C
168
- * ```
169
- *
170
- * Each middleware function (`MiddlewareFn`) can optionally short-circuit
171
- * execution by returning a result, in which case subsequent middleware
172
- * at the same or lower scope will not run.
173
- *
174
- * ### Example Usage
175
- * ```ts
176
- * import { app } from "cloesce";
177
- *
178
- * // Global authentication middleware
179
- * app.useGlobal((request, env, di) => {
180
- * // ... authenticate and inject user
181
- * });
182
- *
183
- * // Model-level authorization
184
- * app.useModel(User, (user) => user.hasPermissions([UserPermissions.canUseFoo]));
185
- *
186
- * // Method-level middleware (e.g., CRUD operation)
187
- * app.useMethod(Foo, "someMethod", (user) => user.hasPermissions([UserPermissions.canUseFooMethod]));
188
- * ```
189
- */
190
- export declare class CloesceApp {
191
- global: MiddlewareFn[];
192
- model: Map<string, MiddlewareFn[]>;
193
- method: Map<string, Map<string, MiddlewareFn[]>>;
194
- /**
195
- * Registers a new global middleware function.
196
- *
197
- * Global middleware runs before all routing and model resolution.
198
- * It is the ideal place to perform tasks such as:
199
- * - Authentication (e.g., JWT verification)
200
- * - Global request logging
201
- * - Dependency injection of shared context
202
- *
203
- * @param m - The middleware function to register.
204
- */
205
- useGlobal(m: MiddlewareFn): void;
206
- /**
207
- * Registers middleware for a specific model type.
208
- *
209
- * Model-level middleware runs after all global middleware,
210
- * but before method-specific middleware. This scope allows
211
- * logic to be applied consistently across all endpoints
212
- * associated with a given model (e.g., authorization).
213
- *
214
- * @typeParam T - The model type.
215
- * @param ctor - The model constructor (used to derive its name).
216
- * @param m - The middleware function to register.
217
- */
218
- useModel<T>(ctor: new () => T, m: MiddlewareFn): void;
219
- /**
220
- * Registers middleware for a specific method on a model.
221
- *
222
- * Method-level middleware is executed after model middleware,
223
- * and before the method implementation itself. It can be used for:
224
- * - Fine-grained permission checks
225
- * - Custom logging or tracing per endpoint
226
- *
227
- * @typeParam T - The model type.
228
- * @param ctor - The model constructor (used to derive its name).
229
- * @param method - The method name on the model.
230
- * @param m - The middleware function to register.
231
- */
232
- useMethod<T>(ctor: new () => T, method: KeysOfType<T, (...args: any) => any>, m: MiddlewareFn): void;
233
- }
234
140
  export type CrudKind = "SAVE" | "GET" | "LIST";
235
141
  export type CidlType = "Void" | "Integer" | "Real" | "Text" | "Blob" | "DateIso" | "Boolean" | {
236
142
  DataSource: string;
@@ -316,6 +222,7 @@ export interface WranglerEnv {
316
222
  name: string;
317
223
  source_path: string;
318
224
  db_binding: string;
225
+ vars: Record<string, CidlType>;
319
226
  }
320
227
  export interface CloesceAst {
321
228
  version: string;
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"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"}
package/dist/common.js CHANGED
@@ -150,111 +150,6 @@ export class Either {
150
150
  : Either.left(fn(this.inner.left));
151
151
  }
152
152
  }
153
- /**
154
- * Represents the core middleware container for a Cloesce application.
155
- *
156
- * The `CloesceApp` class provides scoped middleware registration and
157
- * management across three primary levels of execution:
158
- *
159
- * 1. **Global Middleware** — Executed before any routing or model resolution occurs.
160
- * 2. **Model-Level Middleware** — Executed for requests targeting a specific model type.
161
- * 3. **Method-Level Middleware** — Executed for requests targeting a specific method on a model.
162
- *
163
- * When an instance of `CloesceApp` is exported from `app.cloesce.ts`,
164
- * it becomes the central container that the Cloesce runtime uses to
165
- * assemble and apply middleware in the correct execution order.
166
- *
167
- * ### Middleware Execution Order
168
- * Middleware is executed in FIFO order per scope. For example:
169
- * ```ts
170
- * app.use(Foo, A);
171
- * app.use(Foo, B);
172
- * app.use(Foo, C);
173
- * // Executed in order: A → B → C
174
- * ```
175
- *
176
- * Each middleware function (`MiddlewareFn`) can optionally short-circuit
177
- * execution by returning a result, in which case subsequent middleware
178
- * at the same or lower scope will not run.
179
- *
180
- * ### Example Usage
181
- * ```ts
182
- * import { app } from "cloesce";
183
- *
184
- * // Global authentication middleware
185
- * app.useGlobal((request, env, di) => {
186
- * // ... authenticate and inject user
187
- * });
188
- *
189
- * // Model-level authorization
190
- * app.useModel(User, (user) => user.hasPermissions([UserPermissions.canUseFoo]));
191
- *
192
- * // Method-level middleware (e.g., CRUD operation)
193
- * app.useMethod(Foo, "someMethod", (user) => user.hasPermissions([UserPermissions.canUseFooMethod]));
194
- * ```
195
- */
196
- export class CloesceApp {
197
- global = [];
198
- model = new Map();
199
- method = new Map();
200
- /**
201
- * Registers a new global middleware function.
202
- *
203
- * Global middleware runs before all routing and model resolution.
204
- * It is the ideal place to perform tasks such as:
205
- * - Authentication (e.g., JWT verification)
206
- * - Global request logging
207
- * - Dependency injection of shared context
208
- *
209
- * @param m - The middleware function to register.
210
- */
211
- useGlobal(m) {
212
- this.global.push(m);
213
- }
214
- /**
215
- * Registers middleware for a specific model type.
216
- *
217
- * Model-level middleware runs after all global middleware,
218
- * but before method-specific middleware. This scope allows
219
- * logic to be applied consistently across all endpoints
220
- * associated with a given model (e.g., authorization).
221
- *
222
- * @typeParam T - The model type.
223
- * @param ctor - The model constructor (used to derive its name).
224
- * @param m - The middleware function to register.
225
- */
226
- useModel(ctor, m) {
227
- if (this.model.has(ctor.name)) {
228
- this.model.get(ctor.name).push(m);
229
- }
230
- else {
231
- this.model.set(ctor.name, [m]);
232
- }
233
- }
234
- /**
235
- * Registers middleware for a specific method on a model.
236
- *
237
- * Method-level middleware is executed after model middleware,
238
- * and before the method implementation itself. It can be used for:
239
- * - Fine-grained permission checks
240
- * - Custom logging or tracing per endpoint
241
- *
242
- * @typeParam T - The model type.
243
- * @param ctor - The model constructor (used to derive its name).
244
- * @param method - The method name on the model.
245
- * @param m - The middleware function to register.
246
- */
247
- useMethod(ctor, method, m) {
248
- if (!this.method.has(ctor.name)) {
249
- this.method.set(ctor.name, new Map());
250
- }
251
- const methods = this.method.get(ctor.name);
252
- if (!methods.has(method)) {
253
- methods.set(method, []);
254
- }
255
- methods.get(method).push(m);
256
- }
257
- }
258
153
  export function isNullableType(ty) {
259
154
  return typeof ty === "object" && ty !== null && "Nullable" in ty;
260
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/extractor/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,OAAO,EACP,IAAI,EACJ,UAAU,EAEV,iBAAiB,EAEjB,gBAAgB,EAEhB,UAAU,EAEX,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,eAAe,EACf,UAAU,EACV,QAAQ,EAER,MAAM,EACN,QAAQ,EACR,KAAK,EAEL,WAAW,EAGX,WAAW,EACX,cAAc,EAEd,cAAc,EAGf,MAAM,cAAc,CAAC;AAuBtB,qBAAa,aAAa;IAEf,WAAW,EAAE,MAAM;IACnB,OAAO,EAAE,MAAM;gBADf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM;IAGxB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC;IA8F7D,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC;IA8BlE,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,KAAK,CAAC;IAgQhC,MAAM,CAAC,GAAG,CACR,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC;IAmCzC,MAAM,CAAC,GAAG,CACR,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC;IAuBtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CASjC;IAEF,MAAM,CAAC,QAAQ,CACb,IAAI,EAAE,IAAI,EACV,MAAM,GAAE,OAAe,GACtB,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC;IAkJnC,MAAM,CAAC,WAAW,CAChB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,YAAY,EAAE,gBAAgB,EAC9B,EAAE,EAAE,UAAU,GACb,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC;IAiF1C,MAAM,CAAC,MAAM,CACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,QAAQ,GACjB,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC;CA0EvC"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/extractor/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,OAAO,EACP,IAAI,EACJ,UAAU,EAEV,iBAAiB,EAEjB,gBAAgB,EAEhB,UAAU,EAEX,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,eAAe,EACf,UAAU,EACV,QAAQ,EAER,MAAM,EACN,QAAQ,EACR,KAAK,EAEL,WAAW,EAGX,WAAW,EACX,cAAc,EAEd,cAAc,EAEf,MAAM,cAAc,CAAC;AAuBtB,qBAAa,aAAa;IAEf,WAAW,EAAE,MAAM;IACnB,OAAO,EAAE,MAAM;gBADf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM;IAGxB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC;IA8F7D,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC;IA8BlE,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,KAAK,CAAC;IAgQhC,MAAM,CAAC,GAAG,CACR,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC;IAmCzC,MAAM,CAAC,GAAG,CACR,SAAS,EAAE,gBAAgB,EAC3B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC;IAuCtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CASjC;IAEF,MAAM,CAAC,QAAQ,CACb,IAAI,EAAE,IAAI,EACV,MAAM,GAAE,OAAe,GACtB,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC;IAkJnC,MAAM,CAAC,WAAW,CAChB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,YAAY,EAAE,gBAAgB,EAC9B,EAAE,EAAE,UAAU,GACb,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC;IAiF1C,MAAM,CAAC,MAAM,CACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,QAAQ,GACjB,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC;CA0EvC"}
@@ -1,5 +1,5 @@
1
1
  import { Node as MorphNode, SyntaxKind, Scope, } from "ts-morph";
2
- import { Either, HttpVerb, ExtractorError, ExtractorErrorCode, CloesceApp, } from "../common.js";
2
+ import { Either, HttpVerb, ExtractorError, ExtractorErrorCode, } from "../common.js";
3
3
  import { TypeFormatFlags } from "typescript";
4
4
  var AttributeDecoratorKind;
5
5
  (function (AttributeDecoratorKind) {
@@ -123,7 +123,7 @@ export class CidlExtractor {
123
123
  return type?.getText(undefined, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
124
124
  };
125
125
  const typeText = getTypeText();
126
- if (typeText === CloesceApp.name) {
126
+ if (typeText === "CloesceApp") {
127
127
  return Either.right(sourceFile.getFilePath().toString());
128
128
  }
129
129
  return err(ExtractorErrorCode.AppMissingDefaultExport);
@@ -357,18 +357,31 @@ export class CidlExtractor {
357
357
  });
358
358
  }
359
359
  static env(classDecl, sourceFile) {
360
- const binding = classDecl.getProperties().find((p) => {
361
- return (p
360
+ const vars = {};
361
+ let binding;
362
+ for (const prop of classDecl.getProperties()) {
363
+ if (prop
362
364
  .getType()
363
- .getText(undefined, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope) === "D1Database");
364
- });
365
+ .getText(undefined, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope) === "D1Database") {
366
+ binding = prop.getName();
367
+ continue;
368
+ }
369
+ const ty = CidlExtractor.cidlType(prop.getType());
370
+ if (ty.isLeft()) {
371
+ ty.value.context = prop.getName();
372
+ ty.value.snippet = prop.getText();
373
+ return ty;
374
+ }
375
+ vars[prop.getName()] = ty.unwrap();
376
+ }
365
377
  if (!binding) {
366
378
  return err(ExtractorErrorCode.MissingDatabaseBinding);
367
379
  }
368
380
  return Either.right({
369
381
  name: classDecl.getName(),
370
382
  source_path: sourceFile.getFilePath().toString(),
371
- db_binding: binding.getName(),
383
+ db_binding: binding,
384
+ vars,
372
385
  });
373
386
  }
374
387
  static primTypeMap = {
Binary file
package/dist/orm.wasm CHANGED
Binary file
@@ -1,6 +1,12 @@
1
- import { HttpResult, Either, ModelMethod, CloesceAst, Model, CloesceApp, InstanceRegistry } from "../common.js";
1
+ import { HttpResult, Either, ModelMethod, CloesceAst, Model, KeysOfType } from "../common.js";
2
2
  import { OrmWasmExports } from "./wasm.js";
3
3
  import { CrudContext } from "./crud.js";
4
+ /**
5
+ * Dependency injection container, mapping an object type name to an instance of that object.
6
+ *
7
+ * Comes with the WranglerEnv and Request by default.
8
+ */
9
+ export type DependencyInjector = Map<string, any>;
4
10
  /**
5
11
  * Map of model names to their respective constructor.
6
12
  *
@@ -8,11 +14,6 @@ import { CrudContext } from "./crud.js";
8
14
  * is guaranteed to contain all model definitions.
9
15
  */
10
16
  type ModelConstructorRegistry = Record<string, new () => any>;
11
- /**
12
- * Given a request, this represents a map of each body / url param name to
13
- * its actual value. Unknown, as the a request can be anything.
14
- */
15
- type RequestParamMap = Record<string, unknown>;
16
17
  /**
17
18
  * Meta information on the wrangler env and db bindings
18
19
  */
@@ -20,6 +21,68 @@ interface MetaWranglerEnv {
20
21
  envName: string;
21
22
  dbName: string;
22
23
  }
24
+ export type MiddlewareFn = (request: Request, env: any, di: DependencyInjector) => Promise<HttpResult | void>;
25
+ export type ResponseMiddlewareFn = (request: Request, env: any, di: DependencyInjector, response: Response) => Promise<HttpResult | void>;
26
+ export declare class CloesceApp {
27
+ private globalMiddleware;
28
+ private modelMiddleware;
29
+ private methodMiddleware;
30
+ private responseMiddleware;
31
+ routePrefix: string;
32
+ /**
33
+ * Registers global middleware which runs before any route matching.
34
+ *
35
+ * @param m - The middleware function to register.
36
+ */
37
+ onRequest(m: MiddlewareFn): void;
38
+ /**
39
+ * Registers middleware which runs after the response is generated, but before
40
+ * it is returned to the client.
41
+ *
42
+ * Optionally, return a new HttpResult to short-circuit the response.
43
+ *
44
+ * Errors thrown in response middleware are caught and returned as a 500 response.
45
+ *
46
+ * Errors thrown in earlier middleware or route processing are not caught here.
47
+ *
48
+ * @param m - The middleware function to register.
49
+ */
50
+ onResponse(m: ResponseMiddlewareFn): void;
51
+ /**
52
+ * Registers middleware for a specific model type.
53
+ *
54
+ * Runs before request validation and method middleware.
55
+ *
56
+ * @typeParam T - The model type.
57
+ * @param ctor - The model constructor (used to derive its name).
58
+ * @param m - The middleware function to register.
59
+ */
60
+ onModel<T>(ctor: new () => T, m: MiddlewareFn): void;
61
+ /**
62
+ * Registers middleware for a specific method on a model.
63
+ *
64
+ * Runs after model middleware and request validation.
65
+ *
66
+ * @typeParam T - The model type.
67
+ * @param ctor - The model constructor (used to derive its name).
68
+ * @param method - The method name on the model.
69
+ * @param m - The middleware function to register.
70
+ */
71
+ onMethod<T>(ctor: new () => T, method: KeysOfType<T, (...args: any) => any>, m: MiddlewareFn): void;
72
+ /**
73
+ * Router entry point. Undergoes route matching, request validation, hydration, and method dispatch.
74
+ */
75
+ private cloesce;
76
+ /**
77
+ * Runs the Cloesce app. Intended to be called from the generated workers code.
78
+ */
79
+ run(request: Request, env: any, ast: CloesceAst, constructorRegistry: ModelConstructorRegistry, envMeta: MetaWranglerEnv): Promise<Response>;
80
+ }
81
+ /**
82
+ * Given a request, this represents a map of each body / url param name to
83
+ * its actual value. Unknown, as the a request can be anything.
84
+ */
85
+ export type RequestParamMap = Record<string, unknown>;
23
86
  /**
24
87
  * Singleton instance containing the cidl, constructor registry, and wasm binary.
25
88
  * These values are guaranteed to never change throughout a workers lifetime.
@@ -33,19 +96,12 @@ export declare class RuntimeContainer {
33
96
  static init(ast: CloesceAst, constructorRegistry: ModelConstructorRegistry, wasm?: WebAssembly.Instance): Promise<void>;
34
97
  static get(): RuntimeContainer;
35
98
  }
36
- /**
37
- * Runtime entry point. Given a request, undergoes: routing, validating,
38
- * hydrating, and method dispatch.
39
- *
40
- * @returns A Response with an `HttpResult` JSON body.
41
- */
42
- export declare function cloesce(request: Request, env: any, ast: CloesceAst, app: CloesceApp, constructorRegistry: ModelConstructorRegistry, envMeta: MetaWranglerEnv, apiRoute: string): Promise<Response>;
43
99
  /**
44
100
  * Matches a request to a method on a model.
45
101
  * @param apiRoute The route from the domain to the actual API, ie https://foo.com/route/to/api => route/to/api/
46
102
  * @returns 404 or a `MatchedRoute`
47
103
  */
48
- declare function matchRoute(request: Request, ast: CloesceAst, apiRoute: string): Either<HttpResult, {
104
+ declare function matchRoute(request: Request, ast: CloesceAst, routePrefix: string): Either<HttpResult, {
49
105
  model: Model;
50
106
  method: ModelMethod;
51
107
  id: string | null;
@@ -63,7 +119,7 @@ declare function validateRequest(request: Request, ast: CloesceAst, model: Model
63
119
  * Calls a method on a model given a list of parameters.
64
120
  * @returns 500 on an uncaught client error, 200 with a result body on success
65
121
  */
66
- declare function methodDispatch(crudCtx: CrudContext, instanceRegistry: InstanceRegistry, method: ModelMethod, params: Record<string, unknown>): Promise<HttpResult<unknown>>;
122
+ declare function methodDispatch(crudCtx: CrudContext, instanceRegistry: DependencyInjector, method: ModelMethod, params: Record<string, unknown>): Promise<HttpResult<unknown>>;
67
123
  /**
68
124
  * For testing purposes
69
125
  */
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,MAAM,EACN,WAAW,EAEX,UAAU,EAEV,KAAK,EAGL,UAAU,EACV,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAuB,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC;;;;;GAKG;AACH,KAAK,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAE9D;;;GAGG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C;;GAEG;AACH,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;aAGT,GAAG,EAAE,UAAU;aACf,mBAAmB,EAAE,wBAAwB;aAC7C,IAAI,EAAE,cAAc;IAJtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA+B;IACtD,OAAO;WAMM,IAAI,CACf,GAAG,EAAE,UAAU,EACf,mBAAmB,EAAE,wBAAwB,EAC7C,IAAI,CAAC,EAAE,WAAW,CAAC,QAAQ;IAO7B,MAAM,CAAC,GAAG,IAAI,gBAAgB;CAG/B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,mBAAmB,EAAE,wBAAwB,EAC7C,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAmFnB;AAED;;;;GAIG;AACH,iBAAS,UAAU,CACjB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CACP,UAAU,EACV;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB,CACF,CAyCA;AAED;;;;GAIG;AACH,iBAAe,eAAe,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,WAAW,EACnB,EAAE,EAAE,MAAM,GAAG,IAAI,GAChB,OAAO,CACR,MAAM,CAAC,UAAU,EAAE;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAC3E,CA4DA;AAgED;;;GAGG;AACH,iBAAe,cAAc,CAC3B,OAAO,EAAE,WAAW,EACpB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAkD9B;AAoID;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,159 @@ import { Either, isNullableType, getNavigationPropertyCidlType, NO_DATA_SOURCE,
2
2
  import { mapSql, loadOrmWasm } from "./wasm.js";
3
3
  import { CrudContext } from "./crud.js";
4
4
  import { Orm } from "../ui/backend.js";
5
+ export class CloesceApp {
6
+ globalMiddleware = [];
7
+ modelMiddleware = new Map();
8
+ methodMiddleware = new Map();
9
+ responseMiddleware = [];
10
+ routePrefix = "api";
11
+ /**
12
+ * Registers global middleware which runs before any route matching.
13
+ *
14
+ * @param m - The middleware function to register.
15
+ */
16
+ onRequest(m) {
17
+ this.globalMiddleware.push(m);
18
+ }
19
+ /**
20
+ * Registers middleware which runs after the response is generated, but before
21
+ * it is returned to the client.
22
+ *
23
+ * Optionally, return a new HttpResult to short-circuit the response.
24
+ *
25
+ * Errors thrown in response middleware are caught and returned as a 500 response.
26
+ *
27
+ * Errors thrown in earlier middleware or route processing are not caught here.
28
+ *
29
+ * @param m - The middleware function to register.
30
+ */
31
+ onResponse(m) {
32
+ this.responseMiddleware.push(m);
33
+ }
34
+ /**
35
+ * Registers middleware for a specific model type.
36
+ *
37
+ * Runs before request validation and method middleware.
38
+ *
39
+ * @typeParam T - The model type.
40
+ * @param ctor - The model constructor (used to derive its name).
41
+ * @param m - The middleware function to register.
42
+ */
43
+ onModel(ctor, m) {
44
+ if (this.modelMiddleware.has(ctor.name)) {
45
+ this.modelMiddleware.get(ctor.name).push(m);
46
+ }
47
+ else {
48
+ this.modelMiddleware.set(ctor.name, [m]);
49
+ }
50
+ }
51
+ /**
52
+ * Registers middleware for a specific method on a model.
53
+ *
54
+ * Runs after model middleware and request validation.
55
+ *
56
+ * @typeParam T - The model type.
57
+ * @param ctor - The model constructor (used to derive its name).
58
+ * @param method - The method name on the model.
59
+ * @param m - The middleware function to register.
60
+ */
61
+ onMethod(ctor, method, m) {
62
+ if (!this.methodMiddleware.has(ctor.name)) {
63
+ this.methodMiddleware.set(ctor.name, new Map());
64
+ }
65
+ const methods = this.methodMiddleware.get(ctor.name);
66
+ if (!methods.has(method)) {
67
+ methods.set(method, []);
68
+ }
69
+ methods.get(method).push(m);
70
+ }
71
+ /**
72
+ * Router entry point. Undergoes route matching, request validation, hydration, and method dispatch.
73
+ */
74
+ async cloesce(request, env, ast, constructorRegistry, di, d1) {
75
+ // Global middleware
76
+ for (const m of this.globalMiddleware) {
77
+ const res = await m(request, env, di);
78
+ if (res) {
79
+ return toResponse(res);
80
+ }
81
+ }
82
+ // Route match
83
+ const route = matchRoute(request, ast, this.routePrefix);
84
+ if (route.isLeft()) {
85
+ return toResponse(route.value);
86
+ }
87
+ const { method, model, id } = route.unwrap();
88
+ // Model middleware
89
+ for (const m of this.modelMiddleware.get(model.name) ?? []) {
90
+ const res = await m(request, env, di);
91
+ if (res) {
92
+ return toResponse(res);
93
+ }
94
+ }
95
+ // Request validation
96
+ const validation = await validateRequest(request, ast, model, method, id);
97
+ if (validation.isLeft()) {
98
+ return toResponse(validation.value);
99
+ }
100
+ const { params, dataSource } = validation.unwrap();
101
+ // Method middleware
102
+ for (const m of this.methodMiddleware.get(model.name)?.get(method.name) ??
103
+ []) {
104
+ const res = await m(request, env, di);
105
+ if (res) {
106
+ return toResponse(res);
107
+ }
108
+ }
109
+ // Hydration
110
+ const crudCtx = await (async () => {
111
+ if (method.is_static) {
112
+ return Either.right(CrudContext.fromCtor(d1, constructorRegistry[model.name]));
113
+ }
114
+ const hydratedModel = await hydrateModel(constructorRegistry, d1, model, id, // id must exist after matchRoute
115
+ dataSource);
116
+ return hydratedModel.map((_) => CrudContext.fromInstance(d1, hydratedModel.value, constructorRegistry[model.name]));
117
+ })();
118
+ if (crudCtx.isLeft()) {
119
+ return toResponse(crudCtx.value);
120
+ }
121
+ // Method dispatch
122
+ return toResponse(await methodDispatch(crudCtx.unwrap(), di, method, params));
123
+ }
124
+ /**
125
+ * Runs the Cloesce app. Intended to be called from the generated workers code.
126
+ */
127
+ async run(request, env, ast, constructorRegistry, envMeta) {
128
+ const di = new Map();
129
+ di.set(envMeta.envName, env);
130
+ di.set("Request", request);
131
+ await RuntimeContainer.init(ast, constructorRegistry);
132
+ const d1 = env[envMeta.dbName]; // TODO: multiple dbs
133
+ try {
134
+ // Core cloesce processing
135
+ const response = await this.cloesce(request, env, ast, constructorRegistry, di, d1);
136
+ // Response middleware
137
+ for (const m of this.responseMiddleware) {
138
+ const res = await m(request, env, di, response);
139
+ if (res) {
140
+ return toResponse(res);
141
+ }
142
+ }
143
+ return response;
144
+ }
145
+ catch (e) {
146
+ console.error(JSON.stringify(e));
147
+ return new Response(JSON.stringify({
148
+ ok: false,
149
+ status: 500,
150
+ message: e.toString(),
151
+ }), {
152
+ status: 500,
153
+ headers: { "Content-Type": "application/json" },
154
+ });
155
+ }
156
+ }
157
+ }
5
158
  /**
6
159
  * Singleton instance containing the cidl, constructor registry, and wasm binary.
7
160
  * These values are guaranteed to never change throughout a workers lifetime.
@@ -26,94 +179,29 @@ export class RuntimeContainer {
26
179
  return this.instance;
27
180
  }
28
181
  }
29
- /**
30
- * Runtime entry point. Given a request, undergoes: routing, validating,
31
- * hydrating, and method dispatch.
32
- *
33
- * @returns A Response with an `HttpResult` JSON body.
34
- */
35
- export async function cloesce(request, env, ast, app, constructorRegistry, envMeta, apiRoute) {
36
- //#region Initialization
37
- const ir = new Map();
38
- ir.set(envMeta.envName, env);
39
- ir.set("Request", request);
40
- await RuntimeContainer.init(ast, constructorRegistry);
41
- const d1 = env[envMeta.dbName]; // TODO: multiple dbs
42
- //#endregion
43
- //#region Global Middleware
44
- for (const m of app.global) {
45
- const res = await m(request, env, ir);
46
- if (res) {
47
- return toResponse(res);
48
- }
49
- }
50
- //#endregion
51
- //#region Match the route to a model method
52
- const route = matchRoute(request, ast, apiRoute);
53
- if (route.isLeft()) {
54
- return toResponse(route.value);
55
- }
56
- const { method, model, id } = route.unwrap();
57
- //#endregion
58
- //#region Model Middleware
59
- for (const m of app.model.get(model.name) ?? []) {
60
- const res = await m(request, env, ir);
61
- if (res) {
62
- return toResponse(res);
63
- }
64
- }
65
- //#endregion
66
- //#region Validate request body to the model method
67
- const validation = await validateRequest(request, ast, model, method, id);
68
- if (validation.isLeft()) {
69
- return toResponse(validation.value);
70
- }
71
- const { params, dataSource } = validation.unwrap();
72
- //#endregion
73
- //#region Method Middleware
74
- for (const m of app.method.get(model.name)?.get(method.name) ?? []) {
75
- const res = await m(request, env, ir);
76
- if (res) {
77
- return toResponse(res);
78
- }
79
- }
80
- //#endregion
81
- //#region Instantatiate the model
82
- const crudCtx = await (async () => {
83
- if (method.is_static) {
84
- return Either.right(CrudContext.fromCtor(d1, constructorRegistry[model.name]));
85
- }
86
- const hydratedModel = await hydrateModel(constructorRegistry, d1, model, id, // id must exist after matchRoute
87
- dataSource);
88
- return hydratedModel.map((_) => CrudContext.fromInstance(d1, hydratedModel.value, constructorRegistry[model.name]));
89
- })();
90
- if (crudCtx.isLeft()) {
91
- return toResponse(crudCtx.value);
92
- }
93
- //#endregion
94
- return toResponse(await methodDispatch(crudCtx.unwrap(), ir, method, params));
95
- }
96
182
  /**
97
183
  * Matches a request to a method on a model.
98
184
  * @param apiRoute The route from the domain to the actual API, ie https://foo.com/route/to/api => route/to/api/
99
185
  * @returns 404 or a `MatchedRoute`
100
186
  */
101
- function matchRoute(request, ast, apiRoute) {
187
+ function matchRoute(request, ast, routePrefix) {
102
188
  const url = new URL(request.url);
189
+ const parts = url.pathname.split("/").filter(Boolean);
190
+ const prefix = routePrefix.split("/").filter(Boolean);
103
191
  // Error state: We expect an exact request format, and expect that the model
104
192
  // and are apart of the CIDL
105
193
  const notFound = (e) => Either.left(errorState(404, `Path not found: ${e} ${url.pathname}`));
106
- const routeParts = url.pathname
107
- .slice(apiRoute.length)
108
- .split("/")
109
- .filter(Boolean);
110
- if (routeParts.length < 2) {
194
+ for (const p of prefix) {
195
+ if (parts.shift() !== p)
196
+ return notFound(`Missing prefix segment "${p}"`);
197
+ }
198
+ if (parts.length < 2) {
111
199
  return notFound("Expected /model/method or /model/:id/method");
112
200
  }
113
201
  // Attempt to extract from routeParts
114
- const modelName = routeParts[0];
115
- const methodName = routeParts[routeParts.length - 1];
116
- const id = routeParts.length === 3 ? routeParts[1] : null;
202
+ const modelName = parts[0];
203
+ const methodName = parts[parts.length - 1];
204
+ const id = parts.length === 3 ? parts[1] : null;
117
205
  const model = ast.models[modelName];
118
206
  if (!model) {
119
207
  return notFound(`Unknown model ${modelName}`);
@@ -203,7 +291,7 @@ async function hydrateModel(constructorRegistry, d1, model, id, dataSource) {
203
291
  ? null
204
292
  : constructorRegistry[model.name][dataSource];
205
293
  records = await d1
206
- .prepare(Orm.getQuery(constructorRegistry[model.name], includeTree).unwrap())
294
+ .prepare(Orm.getQuery(constructorRegistry[model.name], includeTree))
207
295
  .bind(id)
208
296
  .run();
209
297
  if (!records?.results) {
@@ -261,6 +349,11 @@ async function methodDispatch(crudCtx, instanceRegistry, method, params) {
261
349
  return uncaughtException(e);
262
350
  }
263
351
  }
352
+ /**
353
+ * Runtime type validation for CIDL types.
354
+ *
355
+ * Returns true if the value matches the CIDL type, false otherwise.
356
+ */
264
357
  function validateCidlType(ast, value, cidlType, isPartial) {
265
358
  if (value === undefined)
266
359
  return isPartial;
@@ -1,8 +1,7 @@
1
1
  import { D1Database } from "@cloudflare/workers-types/experimental/index.js";
2
2
  import { CrudKind, DeepPartial, Either, KeysOfType } from "../common.js";
3
- export { cloesce } from "../router/router.js";
4
- export type { HttpResult, Either, DeepPartial, InstanceRegistry, CrudKind, } from "../common.js";
5
- export { CloesceApp } from "../common.js";
3
+ export { CloesceApp, MiddlewareFn, ResponseMiddlewareFn, DependencyInjector, } from "../router/router.js";
4
+ export type { HttpResult, Either, DeepPartial, CrudKind } from "../common.js";
6
5
  /**
7
6
  * Marks a class as a D1-backed SQL model.
8
7
  *
@@ -209,7 +208,7 @@ export declare const ManyToMany: (_uniqueId: string) => PropertyDecorator;
209
208
  * dogId: number;
210
209
  * ```
211
210
  */
212
- export declare const ForeignKey: <T>(_model: T | string) => PropertyDecorator;
211
+ export declare const ForeignKey: <T>(_Model: T | string) => PropertyDecorator;
213
212
  /**
214
213
  * Marks a method parameter for dependency injection.
215
214
  *
@@ -434,7 +433,7 @@ export declare class Orm {
434
433
  includeTree?: IncludeTree<T> | null;
435
434
  from?: string;
436
435
  tagCte?: string;
437
- }): Either<string, string>;
436
+ }): string;
438
437
  /**
439
438
  * Returns a select query for a single model by primary key, creating a CTE view using the provided include tree.
440
439
  *
@@ -460,7 +459,7 @@ export declare class Orm {
460
459
  * SELECT * FROM Person_view WHERE [Person].[id] = ?
461
460
  * ```
462
461
  */
463
- static getQuery<T extends object>(ctor: new () => T, includeTree?: IncludeTree<T> | null): Either<string, string>;
462
+ static getQuery<T extends object>(ctor: new () => T, includeTree?: IncludeTree<T> | null): string;
464
463
  /**
465
464
  * Retrieves all instances of a model from the database.
466
465
  * @param ctor The model constructor.
@@ -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,8 +1,7 @@
1
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
- export { cloesce } from "../router/router.js";
5
- export { CloesceApp } from "../common.js";
4
+ export { CloesceApp, } from "../router/router.js";
6
5
  /**
7
6
  * Marks a class as a D1-backed SQL model.
8
7
  *
@@ -23,7 +22,7 @@ export { CloesceApp } from "../common.js";
23
22
  * }
24
23
  * ```
25
24
  */
26
- export const D1 = (_) => { };
25
+ export const D1 = () => { };
27
26
  /**
28
27
  * Marks a class as a plain serializable object.
29
28
  *
@@ -50,7 +49,7 @@ export const D1 = (_) => { };
50
49
  * async function foo(): Promise<HttpResult<CatStuff>> { ... }
51
50
  * ```
52
51
  */
53
- export const PlainOldObject = (_) => { };
52
+ export const PlainOldObject = () => { };
54
53
  /**
55
54
  * Declares a Wrangler environment definition.
56
55
  *
@@ -72,7 +71,7 @@ export const PlainOldObject = (_) => { };
72
71
  * foo(@Inject env: WranglerEnv) {...}
73
72
  * ```
74
73
  */
75
- export const WranglerEnv = (_) => { };
74
+ export const WranglerEnv = () => { };
76
75
  /**
77
76
  * Marks a property as the SQL primary key for a model.
78
77
  *
@@ -89,32 +88,32 @@ export const WranglerEnv = (_) => { };
89
88
  * }
90
89
  * ```
91
90
  */
92
- export const PrimaryKey = (_) => { };
91
+ export const PrimaryKey = () => { };
93
92
  /**
94
93
  * Exposes a class method as an HTTP GET endpoint.
95
94
  * The method will appear in both backend and generated client APIs.
96
95
  */
97
- export const GET = (_) => { };
96
+ export const GET = () => { };
98
97
  /**
99
98
  * Exposes a class method as an HTTP POST endpoint.
100
99
  * The method will appear in both backend and generated client APIs.
101
100
  */
102
- export const POST = (_) => { };
101
+ export const POST = () => { };
103
102
  /**
104
103
  * Exposes a class method as an HTTP PUT endpoint.
105
104
  * The method will appear in both backend and generated client APIs.
106
105
  */
107
- export const PUT = (_) => { };
106
+ export const PUT = () => { };
108
107
  /**
109
108
  * Exposes a class method as an HTTP PATCH endpoint.
110
109
  * The method will appear in both backend and generated client APIs.
111
110
  */
112
- export const PATCH = (_) => { };
111
+ export const PATCH = () => { };
113
112
  /**
114
113
  * Exposes a class method as an HTTP DEL endpoint.
115
114
  * The method will appear in both backend and generated client APIs.
116
115
  */
117
- export const DELETE = (_) => { };
116
+ export const DELETE = () => { };
118
117
  /**
119
118
  * Declares a static property as a data source.
120
119
  *
@@ -155,7 +154,7 @@ export const DELETE = (_) => { };
155
154
  * // => Person { id: 1, dogId: 2, dog: { id: 2, name: "Fido" } }[]
156
155
  * ```
157
156
  */
158
- export const DataSource = (_) => { };
157
+ export const DataSource = () => { };
159
158
  /**
160
159
  * Declares a one-to-many relationship between models.
161
160
  *
@@ -209,7 +208,7 @@ export const ManyToMany = (_uniqueId) => () => { };
209
208
  * dogId: number;
210
209
  * ```
211
210
  */
212
- export const ForeignKey = (_model) => () => { };
211
+ export const ForeignKey = (_Model) => () => { };
213
212
  /**
214
213
  * Marks a method parameter for dependency injection.
215
214
  *
@@ -393,7 +392,11 @@ export class Orm {
393
392
  WasmResource.fromString(JSON.stringify(opts.tagCte ?? null), wasm),
394
393
  WasmResource.fromString(JSON.stringify(opts.from ?? null), wasm),
395
394
  ];
396
- return invokeOrmWasm(wasm.list_models, args, wasm);
395
+ const res = invokeOrmWasm(wasm.list_models, args, wasm);
396
+ if (res.isLeft()) {
397
+ throw new Error(`Error invoking the Cloesce WASM Binary: ${res.value}`);
398
+ }
399
+ return res.unwrap();
397
400
  }
398
401
  /**
399
402
  * Returns a select query for a single model by primary key, creating a CTE view using the provided include tree.
@@ -422,9 +425,7 @@ export class Orm {
422
425
  */
423
426
  static getQuery(ctor, includeTree) {
424
427
  const { ast } = RuntimeContainer.get();
425
- return this.listQuery(ctor, {
426
- includeTree,
427
- }).map((inner) => `${inner} WHERE [${ast.models[ctor.name].primary_key.name}] = ?`);
428
+ return `${this.listQuery(ctor, { includeTree })} WHERE [${ast.models[ctor.name].primary_key.name}] = ?`;
428
429
  }
429
430
  /**
430
431
  * Retrieves all instances of a model from the database.
@@ -457,11 +458,8 @@ export class Orm {
457
458
  *
458
459
  */
459
460
  async list(ctor, opts) {
460
- const queryRes = Orm.listQuery(ctor, opts);
461
- if (queryRes.isLeft()) {
462
- return Either.left(queryRes.value);
463
- }
464
- const stmt = this.db.prepare(queryRes.value);
461
+ const sql = Orm.listQuery(ctor, opts);
462
+ const stmt = this.db.prepare(sql);
465
463
  const records = await stmt.all();
466
464
  if (!records.success) {
467
465
  return Either.left(records.error ?? "D1 query failed, but no error was returned.");
@@ -486,11 +484,8 @@ export class Orm {
486
484
  * ```
487
485
  */
488
486
  async get(ctor, id, includeTree) {
489
- const queryRes = Orm.getQuery(ctor, includeTree);
490
- if (queryRes.isLeft()) {
491
- return Either.left(queryRes.value);
492
- }
493
- const record = await this.db.prepare(queryRes.value).bind(id).run();
487
+ const sql = Orm.getQuery(ctor, includeTree);
488
+ const record = await this.db.prepare(sql).bind(id).run();
494
489
  if (!record.success) {
495
490
  return Either.left(record.error ?? "D1 query failed, but no error was returned.");
496
491
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloesce",
3
- "version": "0.0.4-unstable.7",
3
+ "version": "0.0.4-unstable.9",
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",