cloesce 0.0.4-unstable.2 → 0.0.4-unstable.4

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.2
20
+ npm i cloesce@0.0.4-unstable.3
21
21
  ```
22
22
 
23
23
  2. TypeScript
@@ -266,7 +266,7 @@ export class Person {
266
266
  }
267
267
  ```
268
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. `null` is a valid option, meaning no joins will occur.
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
270
 
271
271
  ```ts
272
272
  @D1
@@ -288,17 +288,107 @@ export class Person {
288
288
  @GET
289
289
  static async get(id: number, @Inject env: WranglerEnv): Promise<Person> {
290
290
  let records = await env.db
291
- .prepare("SELECT * FROM [Person.default] WHERE [Person.id] = ?") // Person.default is the SQL view generated from the IncludeTree
291
+ .prepare("SELECT * FROM [Person.default] WHERE [id] = ?") // Person.default is the SQL view generated from the IncludeTree
292
292
  .bind(id)
293
293
  .run();
294
294
 
295
- let persons = Orm.fromSql(Person, records.results, Person.default);
295
+ let persons = Orm.mapSql(Person, records.results, Person.default);
296
296
  return persons.value[0];
297
297
  }
298
298
  }
299
299
  ```
300
300
 
301
- Note that the `get` code can be simplified using CRUD methods or ORM primitives.
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
+ ```
302
392
 
303
393
  ### One to Many
304
394
 
@@ -404,10 +494,12 @@ class Horse {
404
494
 
405
495
  ### CRUD Methods
406
496
 
407
- 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.
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.
408
500
 
409
501
  ```ts
410
- @CRUD(["POST", "GET", "LIST"])
502
+ @CRUD(["SAVE", "GET", "LIST"])
411
503
  @D1
412
504
  export class CrudHaver {
413
505
  @PrimaryKey
package/dist/cli.js CHANGED
@@ -189,7 +189,7 @@ async function extract(opts) {
189
189
  const json = JSON.stringify(ast, null, 4);
190
190
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
191
191
  fs.writeFileSync(outPath, json);
192
- console.log(`CIDL generated successfully at ${outPath}`);
192
+ console.log(`CIDL extracted to ${outPath}`);
193
193
  return { outPath, projectName: cloesceProjectName };
194
194
  }
195
195
  catch (err) {
@@ -293,10 +293,13 @@ function formatErr(e) {
293
293
  const { description, suggestion } = getErrorInfo(e.code);
294
294
  const contextLine = e.context ? `Context: ${e.context}\n` : "";
295
295
  const snippetLine = e.snippet ? `${e.snippet}\n\n` : "";
296
- return `==== CLOESCE ERROR ====
296
+ return `
297
+ ==== CLOESCE ERROR ====
297
298
  Error [${ExtractorErrorCode[e.code]}]: ${description}
298
299
  Phase: TypeScript IDL Extraction
299
- ${contextLine}${snippetLine}Suggested fix: ${suggestion}`;
300
+ ${contextLine}${snippetLine}Suggested fix: ${suggestion}
301
+
302
+ `;
300
303
  }
301
304
  run(cmds, process.argv.slice(2)).catch((err) => {
302
305
  console.error(err);
package/dist/common.d.ts CHANGED
@@ -1,21 +1,9 @@
1
- export type DeepPartial<T> = T extends (infer U)[] ? DeepPartial<U>[] : T extends object ? {
2
- [K in keyof T]?: DeepPartial<T[K]>;
3
- } : T;
4
- export type Either<L, R> = {
5
- ok: false;
6
- value: L;
7
- } | {
8
- ok: true;
9
- value: R;
10
- };
11
- export declare function left<L>(value: L): Either<L, never>;
12
- export declare function right<R>(value: R): Either<never, R>;
13
1
  export declare enum ExtractorErrorCode {
14
2
  MissingExport = 0,
15
3
  AppMissingDefaultExport = 1,
16
4
  UnknownType = 2,
17
5
  MultipleGenericType = 3,
18
- DataSourceMissingStatic = 4,
6
+ InvalidDataSourceDefinition = 4,
19
7
  InvalidPartialType = 5,
20
8
  InvalidIncludeTree = 6,
21
9
  InvalidAttributeModifier = 7,
@@ -25,9 +13,10 @@ export declare enum ExtractorErrorCode {
25
13
  MissingNavigationPropertyReference = 11,
26
14
  MissingManyToManyUniqueId = 12,
27
15
  MissingPrimaryKey = 13,
28
- MissingWranglerEnv = 14,
29
- TooManyWranglerEnvs = 15,
30
- MissingFile = 16
16
+ MissingDatabaseBinding = 14,
17
+ MissingWranglerEnv = 15,
18
+ TooManyWranglerEnvs = 16,
19
+ MissingFile = 17
31
20
  }
32
21
  export declare function getErrorInfo(code: ExtractorErrorCode): {
33
22
  description: string;
@@ -40,6 +29,141 @@ export declare class ExtractorError {
40
29
  constructor(code: ExtractorErrorCode);
41
30
  addContext(fn: (val: string | undefined) => string | undefined): void;
42
31
  }
32
+ type DeepPartialInner<T> = T extends (infer U)[] ? DeepPartialInner<U>[] : T extends object ? {
33
+ [K in keyof T]?: DeepPartialInner<T[K]>;
34
+ } : T | (null extends T ? null : never);
35
+ /**
36
+ * Recursively makes all properties of a type optional — including nested objects and arrays.
37
+ *
38
+ * Similar to TypeScript's built-in `Partial<T>`, but applies the transformation deeply across
39
+ * all nested structures. Useful for defining "patch" or "update" objects where only a subset
40
+ * of properties may be provided.
41
+ *
42
+ * **Apart of the Cloesce method grammar**, meaning the type can be apart of method parameters
43
+ * or return types and the generated workers and client API will act accordingly.
44
+ *
45
+ * @template T
46
+ * The target type to make deeply partial.
47
+ *
48
+ * @remarks
49
+ * - **Objects:** All properties become optional, and their values are recursively wrapped in `DeepPartial`.
50
+ * - **Arrays:** Arrays are preserved, but their elements are recursively made partial.
51
+ * - **Scalars:** Primitive values (string, number, boolean, etc.) remain unchanged.
52
+ * - **Nullable types:** If `null` is assignable to the type, it remains allowed.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * class User {
57
+ * id: string;
58
+ * profile: {
59
+ * name: string;
60
+ * age: number;
61
+ * };
62
+ * tags: string[];
63
+ * }
64
+ *
65
+ * // The resulting type:
66
+ * // {
67
+ * // id?: string;
68
+ * // profile?: { name?: string; age?: number };
69
+ * // tags?: (string | undefined)[];
70
+ * // }
71
+ * type PartialUser = DeepPartial<User>;
72
+ *
73
+ * const patch: PartialUser = {
74
+ * profile: { age: 30 } // ok
75
+ * };
76
+ * ```
77
+ */
78
+ export type DeepPartial<T> = DeepPartialInner<T> & {
79
+ __brand?: "Partial";
80
+ };
81
+ /**
82
+ * A functional result type representing a computation that can either succeed (`ok: true`)
83
+ * or fail (`ok: false`).
84
+ *
85
+ * `Either<L, R>` is used throughout Cloesce to return structured success/error values
86
+ * instead of throwing exceptions.
87
+ * - When `ok` is `true`, `value` contains the success result of type `R`.
88
+ * - When `ok` is `false`, `value` contains the error information of type `L`.
89
+ *
90
+ * This pattern makes control flow predictable and encourages explicit handling
91
+ * of failure cases.
92
+ *
93
+ * Example:
94
+ * ```ts
95
+ * const result: Either<string, number> = compute();
96
+ *
97
+ * if (!result.ok) {
98
+ * console.error("Failed:", result.value);
99
+ * } else {
100
+ * console.log("Success:", result.value);
101
+ * }
102
+ * ```
103
+ */
104
+ export type Either<L, R> = {
105
+ ok: false;
106
+ value: L;
107
+ } | {
108
+ ok: true;
109
+ value: R;
110
+ };
111
+ /**
112
+ * Creates a failed `Either` result.
113
+ *
114
+ * Typically used to represent an error condition or unsuccessful operation.
115
+ *
116
+ * @param value The error or failure value to wrap.
117
+ * @returns An `Either` with `ok: false` and the given value.
118
+ */
119
+ export declare function left<L>(value: L): Either<L, never>;
120
+ /**
121
+ * Creates a successful `Either` result.
122
+ *
123
+ * Typically used to represent a successful operation while maintaining
124
+ * a consistent `Either`-based return type.
125
+ *
126
+ * @param value The success value to wrap.
127
+ * @returns An `Either` with `ok: true` and the given value.
128
+ */
129
+ export declare function right<R>(value: R): Either<never, R>;
130
+ /**
131
+ * Represents the result of an HTTP operation in a monadic style.
132
+ *
133
+ * This type provides a uniform way to handle both success and error
134
+ * outcomes of HTTP requests, similar to a `Result` or `Either` monad.
135
+ *
136
+ * It ensures that every HTTP response can be handled in a type-safe,
137
+ * predictable way without throwing exceptions.
138
+ *
139
+ * @template T The type of the successful response data.
140
+ *
141
+ * @property {boolean} ok
142
+ * Indicates whether the HTTP request was successful (`true` for success, `false` for error).
143
+ * This is analogous to `Response.ok` in the Fetch API.
144
+ *
145
+ * @property {number} status
146
+ * The numeric HTTP status code (e.g., 200, 404, 500).
147
+ *
148
+ * @property {T} [data]
149
+ * The parsed response payload, present only when `ok` is `true`.
150
+ *
151
+ * @property {string} [message]
152
+ * An optional human-readable error message or diagnostic information,
153
+ * typically provided when `ok` is `false`.
154
+ *
155
+ * ## Worker APIs
156
+ *
157
+ * HttpResult is a first-class-citizen in the grammar in Cloesce. Methods can return HttpResults
158
+ * which will be serialized on the client api.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * bar(): HttpResult<Integer> {
163
+ * return { ok: false, status: 401, message: "forbidden"}
164
+ * }
165
+ * ```
166
+ */
43
167
  export type HttpResult<T = unknown> = {
44
168
  ok: boolean;
45
169
  status: number;
@@ -57,18 +181,93 @@ export type KeysOfType<T, U> = {
57
181
  [K in keyof T]: T[K] extends U ? (K extends string ? K : never) : never;
58
182
  }[keyof T];
59
183
  /**
60
- * A container for middleware. If an instance is exported from `app.cloesce.ts`, it will be used in the
61
- * appropriate location, with global middleware happening before any routing occurs.
184
+ * Represents the core middleware container for a Cloesce application.
185
+ *
186
+ * The `CloesceApp` class provides scoped middleware registration and
187
+ * management across three primary levels of execution:
188
+ *
189
+ * 1. **Global Middleware** — Executed before any routing or model resolution occurs.
190
+ * 2. **Model-Level Middleware** — Executed for requests targeting a specific model type.
191
+ * 3. **Method-Level Middleware** — Executed for requests targeting a specific method on a model.
192
+ *
193
+ * When an instance of `CloesceApp` is exported from `app.cloesce.ts`,
194
+ * it becomes the central container that the Cloesce runtime uses to
195
+ * assemble and apply middleware in the correct execution order.
196
+ *
197
+ * ### Middleware Execution Order
198
+ * Middleware is executed in FIFO order per scope. For example:
199
+ * ```ts
200
+ * app.use(Foo, A);
201
+ * app.use(Foo, B);
202
+ * app.use(Foo, C);
203
+ * // Executed in order: A → B → C
204
+ * ```
205
+ *
206
+ * Each middleware function (`MiddlewareFn`) can optionally short-circuit
207
+ * execution by returning a result, in which case subsequent middleware
208
+ * at the same or lower scope will not run.
209
+ *
210
+ * ### Example Usage
211
+ * ```ts
212
+ * import { app } from "cloesce";
213
+ *
214
+ * // Global authentication middleware
215
+ * app.useGlobal((request, env, di) => {
216
+ * // ... authenticate and inject user
217
+ * });
218
+ *
219
+ * // Model-level authorization
220
+ * app.useModel(User, (user) => user.hasPermissions([UserPermissions.canUseFoo]));
221
+ *
222
+ * // Method-level middleware (e.g., CRUD operation)
223
+ * app.useMethod(Foo, "someMethod", (user) => user.hasPermissions([UserPermissions.canUseFooMethod]));
224
+ * ```
62
225
  */
63
226
  export declare class CloesceApp {
64
227
  global: MiddlewareFn[];
65
228
  model: Map<string, MiddlewareFn[]>;
66
229
  method: Map<string, Map<string, MiddlewareFn[]>>;
230
+ /**
231
+ * Registers a new global middleware function.
232
+ *
233
+ * Global middleware runs before all routing and model resolution.
234
+ * It is the ideal place to perform tasks such as:
235
+ * - Authentication (e.g., JWT verification)
236
+ * - Global request logging
237
+ * - Dependency injection of shared context
238
+ *
239
+ * @param m - The middleware function to register.
240
+ */
67
241
  useGlobal(m: MiddlewareFn): void;
242
+ /**
243
+ * Registers middleware for a specific model type.
244
+ *
245
+ * Model-level middleware runs after all global middleware,
246
+ * but before method-specific middleware. This scope allows
247
+ * logic to be applied consistently across all endpoints
248
+ * associated with a given model (e.g., authorization).
249
+ *
250
+ * @typeParam T - The model type.
251
+ * @param ctor - The model constructor (used to derive its name).
252
+ * @param m - The middleware function to register.
253
+ */
68
254
  useModel<T>(ctor: new () => T, m: MiddlewareFn): void;
255
+ /**
256
+ * Registers middleware for a specific method on a model.
257
+ *
258
+ * Method-level middleware is executed after model middleware,
259
+ * and before the method implementation itself. It can be used for:
260
+ * - Fine-grained permission checks
261
+ * - Custom logging or tracing per endpoint
262
+ *
263
+ * @typeParam T - The model type.
264
+ * @param ctor - The model constructor (used to derive its name).
265
+ * @param method - The method name on the model.
266
+ * @param m - The middleware function to register.
267
+ */
69
268
  useMethod<T>(ctor: new () => T, method: KeysOfType<T, (...args: any) => any>, m: MiddlewareFn): void;
70
269
  }
71
- export type CrudKind = "POST" | "GET" | "LIST";
270
+ export type CrudKind = "SAVE" | "GET" | "LIST";
72
271
  export type CidlType = "Void" | "Integer" | "Real" | "Text" | "Blob" | "DateIso" | "Boolean" | {
73
272
  DataSource: string;
74
273
  } | {
@@ -133,6 +332,7 @@ export interface Model {
133
332
  navigation_properties: NavigationProperty[];
134
333
  methods: Record<string, ModelMethod>;
135
334
  data_sources: Record<string, DataSource>;
335
+ cruds: CrudKind[];
136
336
  source_path: string;
137
337
  }
138
338
  export interface PlainOldObject {
@@ -151,6 +351,7 @@ export interface DataSource {
151
351
  export interface WranglerEnv {
152
352
  name: string;
153
353
  source_path: string;
354
+ db_binding: string;
154
355
  }
155
356
  export interface CloesceAst {
156
357
  version: string;
@@ -161,4 +362,5 @@ export interface CloesceAst {
161
362
  poos: Record<string, PlainOldObject>;
162
363
  app_source: string | null;
163
364
  }
365
+ export {};
164
366
  //# sourceMappingURL=common.d.ts.map
@@ -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
@@ -1,16 +1,10 @@
1
- export function left(value) {
2
- return { ok: false, value };
3
- }
4
- export function right(value) {
5
- return { ok: true, value };
6
- }
7
1
  export var ExtractorErrorCode;
8
2
  (function (ExtractorErrorCode) {
9
3
  ExtractorErrorCode[ExtractorErrorCode["MissingExport"] = 0] = "MissingExport";
10
4
  ExtractorErrorCode[ExtractorErrorCode["AppMissingDefaultExport"] = 1] = "AppMissingDefaultExport";
11
5
  ExtractorErrorCode[ExtractorErrorCode["UnknownType"] = 2] = "UnknownType";
12
6
  ExtractorErrorCode[ExtractorErrorCode["MultipleGenericType"] = 3] = "MultipleGenericType";
13
- ExtractorErrorCode[ExtractorErrorCode["DataSourceMissingStatic"] = 4] = "DataSourceMissingStatic";
7
+ ExtractorErrorCode[ExtractorErrorCode["InvalidDataSourceDefinition"] = 4] = "InvalidDataSourceDefinition";
14
8
  ExtractorErrorCode[ExtractorErrorCode["InvalidPartialType"] = 5] = "InvalidPartialType";
15
9
  ExtractorErrorCode[ExtractorErrorCode["InvalidIncludeTree"] = 6] = "InvalidIncludeTree";
16
10
  ExtractorErrorCode[ExtractorErrorCode["InvalidAttributeModifier"] = 7] = "InvalidAttributeModifier";
@@ -20,9 +14,10 @@ export var ExtractorErrorCode;
20
14
  ExtractorErrorCode[ExtractorErrorCode["MissingNavigationPropertyReference"] = 11] = "MissingNavigationPropertyReference";
21
15
  ExtractorErrorCode[ExtractorErrorCode["MissingManyToManyUniqueId"] = 12] = "MissingManyToManyUniqueId";
22
16
  ExtractorErrorCode[ExtractorErrorCode["MissingPrimaryKey"] = 13] = "MissingPrimaryKey";
23
- ExtractorErrorCode[ExtractorErrorCode["MissingWranglerEnv"] = 14] = "MissingWranglerEnv";
24
- ExtractorErrorCode[ExtractorErrorCode["TooManyWranglerEnvs"] = 15] = "TooManyWranglerEnvs";
25
- ExtractorErrorCode[ExtractorErrorCode["MissingFile"] = 16] = "MissingFile";
17
+ ExtractorErrorCode[ExtractorErrorCode["MissingDatabaseBinding"] = 14] = "MissingDatabaseBinding";
18
+ ExtractorErrorCode[ExtractorErrorCode["MissingWranglerEnv"] = 15] = "MissingWranglerEnv";
19
+ ExtractorErrorCode[ExtractorErrorCode["TooManyWranglerEnvs"] = 16] = "TooManyWranglerEnvs";
20
+ ExtractorErrorCode[ExtractorErrorCode["MissingFile"] = 17] = "MissingFile";
26
21
  })(ExtractorErrorCode || (ExtractorErrorCode = {}));
27
22
  const errorInfoMap = {
28
23
  [ExtractorErrorCode.MissingExport]: {
@@ -45,9 +40,9 @@ const errorInfoMap = {
45
40
  description: "Cloesce does not yet support types with multiple generics",
46
41
  suggestion: "Simplify your type to use only a single generic parameter, ie Foo<T>",
47
42
  },
48
- [ExtractorErrorCode.DataSourceMissingStatic]: {
49
- description: "Data Sources must be declared as static",
50
- suggestion: "Declare your data source as `static readonly`",
43
+ [ExtractorErrorCode.InvalidDataSourceDefinition]: {
44
+ description: "Data Sources must be explicitly typed as a static Include Tree",
45
+ suggestion: "Declare your data source as `static readonly _: IncludeTree<Model>`",
51
46
  },
52
47
  [ExtractorErrorCode.InvalidIncludeTree]: {
53
48
  description: "Invalid Include Tree",
@@ -81,6 +76,10 @@ const errorInfoMap = {
81
76
  description: "Missing primary key on a model",
82
77
  suggestion: "Add a primary key field to your model (e.g., `id: number`)",
83
78
  },
79
+ [ExtractorErrorCode.MissingDatabaseBinding]: {
80
+ description: "Missing a database binding in the WranglerEnv definition",
81
+ suggestion: "Add a `D1Database` to your WranglerEnv",
82
+ },
84
83
  [ExtractorErrorCode.MissingWranglerEnv]: {
85
84
  description: "Missing a wrangler environment definition in the project",
86
85
  suggestion: "Add a @WranglerEnv class in your project.",
@@ -109,16 +108,101 @@ export class ExtractorError {
109
108
  }
110
109
  }
111
110
  /**
112
- * A container for middleware. If an instance is exported from `app.cloesce.ts`, it will be used in the
113
- * appropriate location, with global middleware happening before any routing occurs.
111
+ * Creates a failed `Either` result.
112
+ *
113
+ * Typically used to represent an error condition or unsuccessful operation.
114
+ *
115
+ * @param value The error or failure value to wrap.
116
+ * @returns An `Either` with `ok: false` and the given value.
117
+ */
118
+ export function left(value) {
119
+ return { ok: false, value };
120
+ }
121
+ /**
122
+ * Creates a successful `Either` result.
123
+ *
124
+ * Typically used to represent a successful operation while maintaining
125
+ * a consistent `Either`-based return type.
126
+ *
127
+ * @param value The success value to wrap.
128
+ * @returns An `Either` with `ok: true` and the given value.
129
+ */
130
+ export function right(value) {
131
+ return { ok: true, value };
132
+ }
133
+ /**
134
+ * Represents the core middleware container for a Cloesce application.
135
+ *
136
+ * The `CloesceApp` class provides scoped middleware registration and
137
+ * management across three primary levels of execution:
138
+ *
139
+ * 1. **Global Middleware** — Executed before any routing or model resolution occurs.
140
+ * 2. **Model-Level Middleware** — Executed for requests targeting a specific model type.
141
+ * 3. **Method-Level Middleware** — Executed for requests targeting a specific method on a model.
142
+ *
143
+ * When an instance of `CloesceApp` is exported from `app.cloesce.ts`,
144
+ * it becomes the central container that the Cloesce runtime uses to
145
+ * assemble and apply middleware in the correct execution order.
146
+ *
147
+ * ### Middleware Execution Order
148
+ * Middleware is executed in FIFO order per scope. For example:
149
+ * ```ts
150
+ * app.use(Foo, A);
151
+ * app.use(Foo, B);
152
+ * app.use(Foo, C);
153
+ * // Executed in order: A → B → C
154
+ * ```
155
+ *
156
+ * Each middleware function (`MiddlewareFn`) can optionally short-circuit
157
+ * execution by returning a result, in which case subsequent middleware
158
+ * at the same or lower scope will not run.
159
+ *
160
+ * ### Example Usage
161
+ * ```ts
162
+ * import { app } from "cloesce";
163
+ *
164
+ * // Global authentication middleware
165
+ * app.useGlobal((request, env, di) => {
166
+ * // ... authenticate and inject user
167
+ * });
168
+ *
169
+ * // Model-level authorization
170
+ * app.useModel(User, (user) => user.hasPermissions([UserPermissions.canUseFoo]));
171
+ *
172
+ * // Method-level middleware (e.g., CRUD operation)
173
+ * app.useMethod(Foo, "someMethod", (user) => user.hasPermissions([UserPermissions.canUseFooMethod]));
174
+ * ```
114
175
  */
115
176
  export class CloesceApp {
116
177
  global = [];
117
178
  model = new Map();
118
179
  method = new Map();
180
+ /**
181
+ * Registers a new global middleware function.
182
+ *
183
+ * Global middleware runs before all routing and model resolution.
184
+ * It is the ideal place to perform tasks such as:
185
+ * - Authentication (e.g., JWT verification)
186
+ * - Global request logging
187
+ * - Dependency injection of shared context
188
+ *
189
+ * @param m - The middleware function to register.
190
+ */
119
191
  useGlobal(m) {
120
192
  this.global.push(m);
121
193
  }
194
+ /**
195
+ * Registers middleware for a specific model type.
196
+ *
197
+ * Model-level middleware runs after all global middleware,
198
+ * but before method-specific middleware. This scope allows
199
+ * logic to be applied consistently across all endpoints
200
+ * associated with a given model (e.g., authorization).
201
+ *
202
+ * @typeParam T - The model type.
203
+ * @param ctor - The model constructor (used to derive its name).
204
+ * @param m - The middleware function to register.
205
+ */
122
206
  useModel(ctor, m) {
123
207
  if (this.model.has(ctor.name)) {
124
208
  this.model.get(ctor.name).push(m);
@@ -127,6 +211,19 @@ export class CloesceApp {
127
211
  this.model.set(ctor.name, [m]);
128
212
  }
129
213
  }
214
+ /**
215
+ * Registers middleware for a specific method on a model.
216
+ *
217
+ * Method-level middleware is executed after model middleware,
218
+ * and before the method implementation itself. It can be used for:
219
+ * - Fine-grained permission checks
220
+ * - Custom logging or tracing per endpoint
221
+ *
222
+ * @typeParam T - The model type.
223
+ * @param ctor - The model constructor (used to derive its name).
224
+ * @param method - The method name on the model.
225
+ * @param m - The middleware function to register.
226
+ */
130
227
  useMethod(ctor, method, m) {
131
228
  if (!this.method.has(ctor.name)) {
132
229
  this.method.set(ctor.name, new Map());