cloesce 0.0.4-unstable.5 → 0.0.4-unstable.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +619 -0
- package/dist/cli.js +23 -10
- package/dist/common.d.ts +14 -50
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +42 -22
- package/dist/extractor/extract.d.ts.map +1 -1
- package/dist/extractor/extract.js +44 -47
- package/dist/generator.wasm +0 -0
- package/dist/orm.wasm +0 -0
- package/dist/router/crud.d.ts.map +1 -1
- package/dist/router/crud.js +16 -11
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +25 -24
- package/dist/router/wasm.d.ts +9 -2
- package/dist/router/wasm.d.ts.map +1 -1
- package/dist/router/wasm.js +11 -5
- package/dist/ui/backend.d.ts +139 -81
- package/dist/ui/backend.d.ts.map +1 -1
- package/dist/ui/backend.js +175 -129
- package/package.json +1 -1
package/dist/router/router.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Either, isNullableType, getNavigationPropertyCidlType, NO_DATA_SOURCE, } from "../common.js";
|
|
2
2
|
import { mapSql, loadOrmWasm } from "./wasm.js";
|
|
3
3
|
import { CrudContext } from "./crud.js";
|
|
4
|
+
import { Orm } from "../ui/backend.js";
|
|
4
5
|
/**
|
|
5
6
|
* Singleton instance containing the cidl, constructor registry, and wasm binary.
|
|
6
7
|
* These values are guaranteed to never change throughout a workers lifetime.
|
|
@@ -49,10 +50,10 @@ export async function cloesce(request, env, ast, app, constructorRegistry, envMe
|
|
|
49
50
|
//#endregion
|
|
50
51
|
//#region Match the route to a model method
|
|
51
52
|
const route = matchRoute(request, ast, apiRoute);
|
|
52
|
-
if (
|
|
53
|
+
if (route.isLeft()) {
|
|
53
54
|
return toResponse(route.value);
|
|
54
55
|
}
|
|
55
|
-
const { method, model, id } = route.
|
|
56
|
+
const { method, model, id } = route.unwrap();
|
|
56
57
|
//#endregion
|
|
57
58
|
//#region Model Middleware
|
|
58
59
|
for (const m of app.model.get(model.name) ?? []) {
|
|
@@ -64,10 +65,10 @@ export async function cloesce(request, env, ast, app, constructorRegistry, envMe
|
|
|
64
65
|
//#endregion
|
|
65
66
|
//#region Validate request body to the model method
|
|
66
67
|
const validation = await validateRequest(request, ast, model, method, id);
|
|
67
|
-
if (
|
|
68
|
+
if (validation.isLeft()) {
|
|
68
69
|
return toResponse(validation.value);
|
|
69
70
|
}
|
|
70
|
-
const { params, dataSource } = validation.
|
|
71
|
+
const { params, dataSource } = validation.unwrap();
|
|
71
72
|
//#endregion
|
|
72
73
|
//#region Method Middleware
|
|
73
74
|
for (const m of app.method.get(model.name)?.get(method.name) ?? []) {
|
|
@@ -80,20 +81,17 @@ export async function cloesce(request, env, ast, app, constructorRegistry, envMe
|
|
|
80
81
|
//#region Instantatiate the model
|
|
81
82
|
const crudCtx = await (async () => {
|
|
82
83
|
if (method.is_static) {
|
|
83
|
-
return right(CrudContext.fromCtor(d1, constructorRegistry[model.name]));
|
|
84
|
+
return Either.right(CrudContext.fromCtor(d1, constructorRegistry[model.name]));
|
|
84
85
|
}
|
|
85
86
|
const hydratedModel = await hydrateModel(constructorRegistry, d1, model, id, // id must exist after matchRoute
|
|
86
87
|
dataSource);
|
|
87
|
-
|
|
88
|
-
return hydratedModel;
|
|
89
|
-
}
|
|
90
|
-
return right(CrudContext.fromInstance(d1, hydratedModel.value, constructorRegistry[model.name]));
|
|
88
|
+
return hydratedModel.map((_) => CrudContext.fromInstance(d1, hydratedModel.value, constructorRegistry[model.name]));
|
|
91
89
|
})();
|
|
92
|
-
if (
|
|
90
|
+
if (crudCtx.isLeft()) {
|
|
93
91
|
return toResponse(crudCtx.value);
|
|
94
92
|
}
|
|
95
93
|
//#endregion
|
|
96
|
-
return toResponse(await methodDispatch(crudCtx.
|
|
94
|
+
return toResponse(await methodDispatch(crudCtx.unwrap(), ir, method, params));
|
|
97
95
|
}
|
|
98
96
|
/**
|
|
99
97
|
* Matches a request to a method on a model.
|
|
@@ -104,7 +102,7 @@ function matchRoute(request, ast, apiRoute) {
|
|
|
104
102
|
const url = new URL(request.url);
|
|
105
103
|
// Error state: We expect an exact request format, and expect that the model
|
|
106
104
|
// and are apart of the CIDL
|
|
107
|
-
const notFound = (e) => left(errorState(404, `Path not found: ${e} ${url.pathname}`));
|
|
105
|
+
const notFound = (e) => Either.left(errorState(404, `Path not found: ${e} ${url.pathname}`));
|
|
108
106
|
const routeParts = url.pathname
|
|
109
107
|
.slice(apiRoute.length)
|
|
110
108
|
.split("/")
|
|
@@ -127,7 +125,7 @@ function matchRoute(request, ast, apiRoute) {
|
|
|
127
125
|
if (request.method !== method.http_verb) {
|
|
128
126
|
return notFound("Unmatched HTTP method");
|
|
129
127
|
}
|
|
130
|
-
return right({
|
|
128
|
+
return Either.right({
|
|
131
129
|
model,
|
|
132
130
|
method,
|
|
133
131
|
id,
|
|
@@ -140,7 +138,7 @@ function matchRoute(request, ast, apiRoute) {
|
|
|
140
138
|
*/
|
|
141
139
|
async function validateRequest(request, ast, model, method, id) {
|
|
142
140
|
// Error state: any missing parameter, body, or malformed input will exit with 400.
|
|
143
|
-
const invalidRequest = (e) => left(errorState(400, `Invalid Request Body: ${e}`));
|
|
141
|
+
const invalidRequest = (e) => Either.left(errorState(400, `Invalid Request Body: ${e}`));
|
|
144
142
|
if (!method.is_static && id == null) {
|
|
145
143
|
return invalidRequest("Id's are required for instantiated methods.");
|
|
146
144
|
}
|
|
@@ -183,7 +181,7 @@ async function validateRequest(request, ast, model, method, id) {
|
|
|
183
181
|
!(dataSource in model.data_sources)) {
|
|
184
182
|
return invalidRequest(`Unknown data source ${dataSource}`);
|
|
185
183
|
}
|
|
186
|
-
return right({ params, dataSource });
|
|
184
|
+
return Either.right({ params, dataSource });
|
|
187
185
|
}
|
|
188
186
|
/**
|
|
189
187
|
* Queries D1 for a particular model's ID, then transforms the SQL column output into
|
|
@@ -195,17 +193,19 @@ async function validateRequest(request, ast, model, method, id) {
|
|
|
195
193
|
async function hydrateModel(constructorRegistry, d1, model, id, dataSource) {
|
|
196
194
|
// Error state: If the D1 database has been tweaked outside of Cloesce
|
|
197
195
|
// resulting in a malformed query, exit with a 500.
|
|
198
|
-
const malformedQuery = (e) => left(errorState(500, `Error in hydration query, is the database out of sync with the backend?: ${e instanceof Error ? e.message : String(e)}`));
|
|
196
|
+
const malformedQuery = (e) => Either.left(errorState(500, `Error in hydration query, is the database out of sync with the backend?: ${e instanceof Error ? e.message : String(e)}`));
|
|
199
197
|
// Error state: If no record is found for the id, return a 404
|
|
200
|
-
const missingRecord = left(errorState(404, "Record not found"));
|
|
201
|
-
const pk = model.primary_key.name;
|
|
202
|
-
const query = dataSource !== NO_DATA_SOURCE
|
|
203
|
-
? `SELECT * FROM "${model.name}.${dataSource}" WHERE "${pk}" = ?`
|
|
204
|
-
: `SELECT * FROM "${model.name}" WHERE "${pk}" = ?`;
|
|
198
|
+
const missingRecord = Either.left(errorState(404, "Record not found"));
|
|
205
199
|
// Query DB
|
|
206
200
|
let records;
|
|
207
201
|
try {
|
|
208
|
-
|
|
202
|
+
let includeTree = dataSource === NO_DATA_SOURCE
|
|
203
|
+
? null
|
|
204
|
+
: constructorRegistry[model.name][dataSource];
|
|
205
|
+
records = await d1
|
|
206
|
+
.prepare(Orm.getQuery(constructorRegistry[model.name], includeTree).unwrap())
|
|
207
|
+
.bind(id)
|
|
208
|
+
.run();
|
|
209
209
|
if (!records?.results) {
|
|
210
210
|
return missingRecord;
|
|
211
211
|
}
|
|
@@ -218,7 +218,7 @@ async function hydrateModel(constructorRegistry, d1, model, id, dataSource) {
|
|
|
218
218
|
}
|
|
219
219
|
// Hydrate
|
|
220
220
|
const models = mapSql(constructorRegistry[model.name], records.results, model.data_sources[dataSource]?.tree ?? {}).value;
|
|
221
|
-
return right(models[0]);
|
|
221
|
+
return Either.right(models[0]);
|
|
222
222
|
}
|
|
223
223
|
/**
|
|
224
224
|
* Calls a method on a model given a list of parameters.
|
|
@@ -326,6 +326,7 @@ function validateCidlType(ast, value, cidlType, isPartial) {
|
|
|
326
326
|
if (!poo.attributes.every((attr) => validateCidlType(ast, valueObj[attr.name], attr.cidl_type, isPartial))) {
|
|
327
327
|
return false;
|
|
328
328
|
}
|
|
329
|
+
return true;
|
|
329
330
|
}
|
|
330
331
|
if ("Array" in cidlType) {
|
|
331
332
|
const arr = cidlType.Array;
|
package/dist/router/wasm.d.ts
CHANGED
|
@@ -10,8 +10,9 @@ export interface OrmWasmExports {
|
|
|
10
10
|
set_meta_ptr(ptr: number, len: number): number;
|
|
11
11
|
alloc(len: number): number;
|
|
12
12
|
dealloc(ptr: number, len: number): void;
|
|
13
|
-
|
|
13
|
+
map_sql(model_name_ptr: number, model_name_len: number, sql_rows_ptr: number, sql_rows_len: number, include_tree_ptr: number, include_tree_len: number): boolean;
|
|
14
14
|
upsert_model(model_name_ptr: number, model_name_len: number, new_model_ptr: number, new_model_len: number, include_tree_ptr: number, include_tree_len: number): boolean;
|
|
15
|
+
list_models(model_name_ptr: number, model_name_len: number, include_tree_ptr: number, include_tree_len: number, tag_cte_ptr: number, tag_cte_len: number, custom_from_ptr: number, custom_from_len: number): boolean;
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* RAII for wasm memory
|
|
@@ -28,7 +29,13 @@ export declare class WasmResource {
|
|
|
28
29
|
static fromString(str: string, wasm: OrmWasmExports): WasmResource;
|
|
29
30
|
}
|
|
30
31
|
export declare function loadOrmWasm(ast: CloesceAst, wasm?: WebAssembly.Instance): Promise<OrmWasmExports>;
|
|
31
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Invokes a WASM ORM function with the provided arguments, handling memory
|
|
34
|
+
* allocation and deallocation.
|
|
35
|
+
*
|
|
36
|
+
* Returns an Either where Left is an error message and Right the raw string result.
|
|
37
|
+
*/
|
|
38
|
+
export declare function invokeOrmWasm(fn: (...args: number[]) => boolean, args: WasmResource[], wasm: OrmWasmExports): Either<string, string>;
|
|
32
39
|
/**
|
|
33
40
|
* Calls `object_relational_mapping` to turn a row of SQL records into
|
|
34
41
|
* an instantiated object.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../../src/router/wasm.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../../src/router/wasm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,EAAS,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;IAC3B,cAAc,IAAI,MAAM,CAAC;IACzB,cAAc,IAAI,MAAM,CAAC;IACzB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC,OAAO,CACL,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC;IAEX,YAAY,CACV,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC;IAEX,WAAW,CACT,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,IAAI;IACL,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;gBAFV,IAAI,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM;IAEpB,IAAI;IAIJ;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,YAAY;CAQnE;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,IAAI,CAAC,EAAE,WAAW,CAAC,QAAQ,GAC1B,OAAO,CAAC,cAAc,CAAC,CAmBzB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,EAClC,IAAI,EAAE,YAAY,EAAE,EACpB,IAAI,EAAE,cAAc,GACnB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxB;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC9B,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,GAAG,IAAI,GACnD,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAsDrB"}
|
package/dist/router/wasm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Either } from "../common.js";
|
|
2
2
|
import { RuntimeContainer } from "./router.js";
|
|
3
3
|
// Requires the ORM binary to have been built
|
|
4
4
|
import mod from "../orm.wasm";
|
|
@@ -41,6 +41,12 @@ export async function loadOrmWasm(ast, wasm) {
|
|
|
41
41
|
// Intentionally leak `modelMeta`, it should exist for the programs lifetime.
|
|
42
42
|
return wasmInstance.exports;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Invokes a WASM ORM function with the provided arguments, handling memory
|
|
46
|
+
* allocation and deallocation.
|
|
47
|
+
*
|
|
48
|
+
* Returns an Either where Left is an error message and Right the raw string result.
|
|
49
|
+
*/
|
|
44
50
|
export function invokeOrmWasm(fn, args, wasm) {
|
|
45
51
|
let resPtr;
|
|
46
52
|
let resLen;
|
|
@@ -49,7 +55,7 @@ export function invokeOrmWasm(fn, args, wasm) {
|
|
|
49
55
|
resPtr = wasm.get_return_ptr();
|
|
50
56
|
resLen = wasm.get_return_len();
|
|
51
57
|
const result = new TextDecoder().decode(new Uint8Array(wasm.memory.buffer, resPtr, resLen));
|
|
52
|
-
return failed ? left(result) : right(result);
|
|
58
|
+
return failed ? Either.left(result) : Either.right(result);
|
|
53
59
|
}
|
|
54
60
|
finally {
|
|
55
61
|
args.forEach((a) => a.free());
|
|
@@ -68,11 +74,11 @@ export function mapSql(ctor, records, includeTree) {
|
|
|
68
74
|
WasmResource.fromString(JSON.stringify(records), wasm),
|
|
69
75
|
WasmResource.fromString(JSON.stringify(includeTree), wasm),
|
|
70
76
|
];
|
|
71
|
-
const jsonResults = invokeOrmWasm(wasm.
|
|
72
|
-
if (
|
|
77
|
+
const jsonResults = invokeOrmWasm(wasm.map_sql, args, wasm);
|
|
78
|
+
if (jsonResults.isLeft())
|
|
73
79
|
return jsonResults;
|
|
74
80
|
const parsed = JSON.parse(jsonResults.value);
|
|
75
|
-
return right(parsed.map((obj) => instantiateDepthFirst(obj, ast.models[ctor.name], includeTree)));
|
|
81
|
+
return Either.right(parsed.map((obj) => instantiateDepthFirst(obj, ast.models[ctor.name], includeTree)));
|
|
76
82
|
function instantiateDepthFirst(m, meta, includeTree) {
|
|
77
83
|
m = Object.assign(new constructorRegistry[meta.name](), m);
|
|
78
84
|
if (!includeTree) {
|
package/dist/ui/backend.d.ts
CHANGED
|
@@ -31,8 +31,6 @@ export declare const D1: ClassDecorator;
|
|
|
31
31
|
* returned from a model method or API endpoint without being
|
|
32
32
|
* treated as a database model.
|
|
33
33
|
*
|
|
34
|
-
* These are often used for DTOs or view models.
|
|
35
|
-
*
|
|
36
34
|
* Example:
|
|
37
35
|
* ```ts
|
|
38
36
|
* @PlainOldObject
|
|
@@ -40,6 +38,16 @@ export declare const D1: ClassDecorator;
|
|
|
40
38
|
* catFacts: string[];
|
|
41
39
|
* catNames: string[];
|
|
42
40
|
* }
|
|
41
|
+
*
|
|
42
|
+
* // in a method...
|
|
43
|
+
* foo(): CatStuff {
|
|
44
|
+
* return {
|
|
45
|
+
* catFacts: ["cats sleep 16 hours a day"],
|
|
46
|
+
* catNames: ["Whiskers", "Fluffy"]
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* // which generates an API like:
|
|
50
|
+
* async function foo(): Promise<HttpResult<CatStuff>> { ... }
|
|
43
51
|
* ```
|
|
44
52
|
*/
|
|
45
53
|
export declare const PlainOldObject: ClassDecorator;
|
|
@@ -110,11 +118,8 @@ export declare const DELETE: MethodDecorator;
|
|
|
110
118
|
/**
|
|
111
119
|
* Declares a static property as a data source.
|
|
112
120
|
*
|
|
113
|
-
* Data sources describe SQL
|
|
114
|
-
*
|
|
115
|
-
* are automatically included when querying. Data sources
|
|
116
|
-
* can only reference navigation properties, not scalar
|
|
117
|
-
* attributes.
|
|
121
|
+
* Data sources describe SQL left joins related to each
|
|
122
|
+
* models navigation properties.
|
|
118
123
|
*
|
|
119
124
|
* Example:
|
|
120
125
|
* ```ts
|
|
@@ -137,27 +142,17 @@ export declare const DELETE: MethodDecorator;
|
|
|
137
142
|
* @OneToOne("dogId")
|
|
138
143
|
* dog: Dog | undefined;
|
|
139
144
|
*
|
|
140
|
-
* // 👇 Defines a data source that joins the related Dog record
|
|
141
145
|
* @DataSource
|
|
142
146
|
* static readonly default: IncludeTree<Person> = {
|
|
143
|
-
* dog: {},
|
|
147
|
+
* dog: {}, // join Dog table when querying Person with `default` data source
|
|
144
148
|
* };
|
|
145
149
|
* }
|
|
146
150
|
*
|
|
147
|
-
* // The above will generate an SQL view similar to:
|
|
148
|
-
* // CREATE VIEW "Person.default" AS
|
|
149
|
-
* // SELECT
|
|
150
|
-
* // "Person"."id" AS "id",
|
|
151
|
-
* // "Person"."dogId" AS "dogId",
|
|
152
|
-
* // "Dog"."id" AS "dog.id",
|
|
153
|
-
* // "Dog"."name" AS "dog.name"
|
|
154
|
-
* // FROM "Person"
|
|
155
|
-
* // LEFT JOIN "Dog" ON "Person"."dogId" = "Dog"."id";
|
|
156
|
-
*
|
|
157
151
|
* // When queried via the ORM or client API:
|
|
158
152
|
* const orm = Orm.fromD1(env.db);
|
|
159
|
-
* const people =
|
|
160
|
-
*
|
|
153
|
+
* const people = await orm.list(Person, Person.default);
|
|
154
|
+
*
|
|
155
|
+
* // => Person { id: 1, dogId: 2, dog: { id: 2, name: "Fido" } }[]
|
|
161
156
|
* ```
|
|
162
157
|
*/
|
|
163
158
|
export declare const DataSource: PropertyDecorator;
|
|
@@ -173,7 +168,7 @@ export declare const DataSource: PropertyDecorator;
|
|
|
173
168
|
* dogs: Dog[];
|
|
174
169
|
* ```
|
|
175
170
|
*/
|
|
176
|
-
export declare const OneToMany: (
|
|
171
|
+
export declare const OneToMany: (_foreignKeyColumn: string) => PropertyDecorator;
|
|
177
172
|
/**
|
|
178
173
|
* Declares a one-to-one relationship between models.
|
|
179
174
|
*
|
|
@@ -186,7 +181,7 @@ export declare const OneToMany: (_: string) => PropertyDecorator;
|
|
|
186
181
|
* dog: Dog | undefined;
|
|
187
182
|
* ```
|
|
188
183
|
*/
|
|
189
|
-
export declare const OneToOne: (
|
|
184
|
+
export declare const OneToOne: (_foreignKeyColumn: string) => PropertyDecorator;
|
|
190
185
|
/**
|
|
191
186
|
* Declares a many-to-many relationship between models.
|
|
192
187
|
*
|
|
@@ -199,7 +194,7 @@ export declare const OneToOne: (_: string) => PropertyDecorator;
|
|
|
199
194
|
* courses: Course[];
|
|
200
195
|
* ```
|
|
201
196
|
*/
|
|
202
|
-
export declare const ManyToMany: (
|
|
197
|
+
export declare const ManyToMany: (_uniqueId: string) => PropertyDecorator;
|
|
203
198
|
/**
|
|
204
199
|
* Declares a foreign key relationship between models.
|
|
205
200
|
* Directly translates to a SQLite foreign key.
|
|
@@ -214,13 +209,16 @@ export declare const ManyToMany: (_: string) => PropertyDecorator;
|
|
|
214
209
|
* dogId: number;
|
|
215
210
|
* ```
|
|
216
211
|
*/
|
|
217
|
-
export declare const ForeignKey: <T>(
|
|
212
|
+
export declare const ForeignKey: <T>(_Model: T | string) => PropertyDecorator;
|
|
218
213
|
/**
|
|
219
214
|
* Marks a method parameter for dependency injection.
|
|
220
215
|
*
|
|
221
216
|
* Injected parameters can receive environment bindings,
|
|
222
217
|
* middleware-provided objects, or other registered values.
|
|
223
218
|
*
|
|
219
|
+
* Note that injected parameters will not appear in the client
|
|
220
|
+
* API.
|
|
221
|
+
*
|
|
224
222
|
* Example:
|
|
225
223
|
* ```ts
|
|
226
224
|
* @POST
|
|
@@ -240,14 +238,13 @@ export declare const Inject: ParameterDecorator;
|
|
|
240
238
|
* client bindings automatically, removing the need to manually
|
|
241
239
|
* define common API operations.
|
|
242
240
|
*
|
|
243
|
-
*
|
|
244
|
-
* - **"SAVE"** — Performs an *upsert* (insert or
|
|
241
|
+
* CRUD Operations:
|
|
242
|
+
* - **"SAVE"** — Performs an *upsert* (insert, update, or both) for a model instance.
|
|
245
243
|
* - **"GET"** — Retrieves a single record by its primary key, optionally using a `DataSource`.
|
|
246
244
|
* - **"LIST"** — Retrieves all records for the model, using the specified `DataSource`.
|
|
247
|
-
* - **(future)** `"DELETE"` — Will remove a record by primary key once implemented.
|
|
248
245
|
*
|
|
249
246
|
* The generated methods are static, exposed through both the backend
|
|
250
|
-
*
|
|
247
|
+
* and the frontend client API.
|
|
251
248
|
*
|
|
252
249
|
* Example:
|
|
253
250
|
* ```ts
|
|
@@ -257,11 +254,6 @@ export declare const Inject: ParameterDecorator;
|
|
|
257
254
|
* @PrimaryKey id: number;
|
|
258
255
|
* name: string;
|
|
259
256
|
* }
|
|
260
|
-
*
|
|
261
|
-
* // Generated methods (conceptually):
|
|
262
|
-
* // static async save(item: CrudHaver): Promise<HttpResult<CrudHaver>>
|
|
263
|
-
* // static async get(id: number, dataSource?: string): Promise<HttpResult<CrudHaver>>
|
|
264
|
-
* // static async list(dataSource?: string): Promise<HttpResult<CrudHaver[]>>
|
|
265
257
|
* ```
|
|
266
258
|
*/
|
|
267
259
|
export declare const CRUD: (_kinds: CrudKind[]) => ClassDecorator;
|
|
@@ -301,8 +293,7 @@ export type IncludeTree<T> = (T extends Primitive ? never : {
|
|
|
301
293
|
* Represents the name of a `@DataSource` available on a model type `T`,
|
|
302
294
|
* or `"none"` when no data source (no joins) should be applied.
|
|
303
295
|
*
|
|
304
|
-
*
|
|
305
|
-
* data source names to the actual static properties declared on the model.
|
|
296
|
+
* All instantiated model methods implicitly have a Data Source param `__dataSource`.
|
|
306
297
|
*
|
|
307
298
|
* Example:
|
|
308
299
|
* ```ts
|
|
@@ -312,10 +303,15 @@ export type IncludeTree<T> = (T extends Primitive ? never : {
|
|
|
312
303
|
*
|
|
313
304
|
* @DataSource
|
|
314
305
|
* static readonly default: IncludeTree<Person> = { dogs: {} };
|
|
306
|
+
*
|
|
307
|
+
* @POST
|
|
308
|
+
* foo(ds: DataSourceOf<Person>) {
|
|
309
|
+
* // Cloesce won't append an implicit data source param here since it's explicit
|
|
310
|
+
* }
|
|
315
311
|
* }
|
|
316
312
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
313
|
+
* // on the API client:
|
|
314
|
+
* async foo(ds: "default" | "none"): Promise<void> {...}
|
|
319
315
|
* ```
|
|
320
316
|
*/
|
|
321
317
|
export type DataSourceOf<T extends object> = (KeysOfType<T, IncludeTree<T>> | "none") & {
|
|
@@ -342,22 +338,7 @@ export type Integer = number & {
|
|
|
342
338
|
__brand?: "Integer";
|
|
343
339
|
};
|
|
344
340
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
* The `Orm` class uses the Cloesce metadata system to generate, execute,
|
|
348
|
-
* and map SQL queries for model classes decorated with `@D1`.
|
|
349
|
-
*
|
|
350
|
-
* Typical operations include:
|
|
351
|
-
* - `fromD1(db)` — create an ORM instance bound to a `D1Database`
|
|
352
|
-
* - `upsert()` — insert or update a model
|
|
353
|
-
* - `list()` — fetch all instances of a model
|
|
354
|
-
* - `get()` — fetch one instance by primary key
|
|
355
|
-
*
|
|
356
|
-
* Example:
|
|
357
|
-
* ```ts
|
|
358
|
-
* const orm = Orm.fromD1(env.db);
|
|
359
|
-
* const horses = (await orm.list(Horse, "default")).value;
|
|
360
|
-
* ```
|
|
341
|
+
* Exposes the ORM primitives Cloesce uses to interact with D1 databases.
|
|
361
342
|
*/
|
|
362
343
|
export declare class Orm {
|
|
363
344
|
private db;
|
|
@@ -376,28 +357,16 @@ export declare class Orm {
|
|
|
376
357
|
* @param includeTree Include tree to define the relationships to join.
|
|
377
358
|
*/
|
|
378
359
|
static mapSql<T extends object>(ctor: new () => T, records: Record<string, any>[], includeTree?: IncludeTree<T> | null): Either<string, T[]>;
|
|
379
|
-
/**
|
|
380
|
-
* Returns a SQL query to insert a model into the database. Uses an IncludeTree as a guide for
|
|
381
|
-
* foreign key relationships, only inserting the explicitly stated pattern in the tree.
|
|
382
|
-
*
|
|
383
|
-
* TODO: We should be able to leave primary keys and foreign keys undefined, with
|
|
384
|
-
* primary keys being auto incremented and foreign keys being assumed by navigation property
|
|
385
|
-
* context.
|
|
386
|
-
*
|
|
387
|
-
* @param ctor A model constructor.
|
|
388
|
-
* @param newModel The new model to insert.
|
|
389
|
-
* @param includeTree An include tree describing which foreign keys to join.
|
|
390
|
-
* @returns Either an error string, or the insert query string.
|
|
391
|
-
*/
|
|
392
|
-
static upsertQuery<T extends object>(ctor: new () => T, newModel: DeepPartial<T>, includeTree?: IncludeTree<T> | null): Either<string, string>;
|
|
393
360
|
/**
|
|
394
361
|
* Executes an "upsert" query, adding or augmenting a model in the database.
|
|
362
|
+
*
|
|
395
363
|
* If a model's primary key is not defined in `newModel`, the query is assumed to be an insert.
|
|
364
|
+
*
|
|
396
365
|
* If a model's primary key _is_ defined, but some attributes are missing, the query is assumed to be an update.
|
|
366
|
+
*
|
|
397
367
|
* Finally, if the primary key is defined, but all attributes are included, a SQLite upsert will be performed.
|
|
398
368
|
*
|
|
399
|
-
*
|
|
400
|
-
* only if the primary key is an integer, in which case it will be auto incremented and assigned.
|
|
369
|
+
* In any other case, an error string will be returned.
|
|
401
370
|
*
|
|
402
371
|
* ### Inserting a new Model
|
|
403
372
|
* ```ts
|
|
@@ -433,23 +402,112 @@ export declare class Orm {
|
|
|
433
402
|
*/
|
|
434
403
|
upsert<T extends object>(ctor: new () => T, newModel: DeepPartial<T>, includeTree?: IncludeTree<T> | null): Promise<Either<string, any>>;
|
|
435
404
|
/**
|
|
436
|
-
* Returns a query
|
|
405
|
+
* Returns a select query, creating a CTE view for the model using the provided include tree.
|
|
406
|
+
*
|
|
407
|
+
* @param ctor The model constructor.
|
|
408
|
+
* @param includeTree An include tree describing which related models to join.
|
|
409
|
+
* @param from An optional custom `FROM` clause to use instead of the base table.
|
|
410
|
+
* @param tagCte An optional CTE name to tag the query with. Defaults to "Model.view".
|
|
411
|
+
*
|
|
412
|
+
* ### Example:
|
|
413
|
+
* ```ts
|
|
414
|
+
* // Using a data source
|
|
415
|
+
* const query = Orm.listQuery(Person, "default");
|
|
416
|
+
*
|
|
417
|
+
* // Using a custom from statement
|
|
418
|
+
* const query = Orm.listQuery(Person, null, "SELECT * FROM Person WHERE age > 18");
|
|
419
|
+
* ```
|
|
420
|
+
*
|
|
421
|
+
* ### Example SQL output:
|
|
422
|
+
* ```sql
|
|
423
|
+
* WITH Person_view AS (
|
|
424
|
+
* SELECT
|
|
425
|
+
* "Person"."id" AS "id",
|
|
426
|
+
* ...
|
|
427
|
+
* FROM "Person"
|
|
428
|
+
* LEFT JOIN ...
|
|
429
|
+
* )
|
|
430
|
+
* SELECT * FROM Person_view
|
|
431
|
+
* ```
|
|
437
432
|
*/
|
|
438
|
-
static listQuery<T extends object>(ctor: new () => T,
|
|
433
|
+
static listQuery<T extends object>(ctor: new () => T, opts: {
|
|
434
|
+
includeTree?: IncludeTree<T> | null;
|
|
435
|
+
from?: string;
|
|
436
|
+
tagCte?: string;
|
|
437
|
+
}): Either<string, string>;
|
|
439
438
|
/**
|
|
440
|
-
* Returns a query
|
|
441
|
-
*
|
|
439
|
+
* Returns a select query for a single model by primary key, creating a CTE view using the provided include tree.
|
|
440
|
+
*
|
|
441
|
+
* @param ctor The model constructor.
|
|
442
|
+
* @param includeTree An include tree describing which related models to join.
|
|
443
|
+
*
|
|
444
|
+
* ### Example:
|
|
445
|
+
* ```ts
|
|
446
|
+
* // Using a data source
|
|
447
|
+
* const query = Orm.getQuery(Person, "default");
|
|
448
|
+
* ```
|
|
449
|
+
*
|
|
450
|
+
* ### Example SQL output:
|
|
451
|
+
*
|
|
452
|
+
* ```sql
|
|
453
|
+
* WITH Person_view AS (
|
|
454
|
+
* SELECT
|
|
455
|
+
* "Person"."id" AS "id",
|
|
456
|
+
* ...
|
|
457
|
+
* FROM "Person"
|
|
458
|
+
* LEFT JOIN ...
|
|
459
|
+
* )
|
|
460
|
+
* SELECT * FROM Person_view WHERE [Person].[id] = ?
|
|
461
|
+
* ```
|
|
442
462
|
*/
|
|
443
|
-
static getQuery<T extends object>(ctor: new () => T, includeTree?:
|
|
463
|
+
static getQuery<T extends object>(ctor: new () => T, includeTree?: IncludeTree<T> | null): Either<string, string>;
|
|
444
464
|
/**
|
|
445
|
-
*
|
|
446
|
-
*
|
|
465
|
+
* Retrieves all instances of a model from the database.
|
|
466
|
+
* @param ctor The model constructor.
|
|
467
|
+
* @param includeTree An include tree describing which related models to join.
|
|
468
|
+
* @param from An optional custom `FROM` clause to use instead of the base table.
|
|
469
|
+
* @returns Either an error string, or an array of model instances.
|
|
470
|
+
*
|
|
471
|
+
* ### Example:
|
|
472
|
+
* ```ts
|
|
473
|
+
* const orm = Orm.fromD1(env.db);
|
|
474
|
+
* const horses = await orm.list(Horse, Horse.default);
|
|
475
|
+
* ```
|
|
476
|
+
*
|
|
477
|
+
* ### Example with custom from:
|
|
478
|
+
* ```ts
|
|
479
|
+
* const orm = Orm.fromD1(env.db);
|
|
480
|
+
* const adultHorses = await orm.list(Horse, Horse.default, "SELECT * FROM Horse ORDER BY age DESC LIMIT 10");
|
|
481
|
+
* ```
|
|
482
|
+
*
|
|
483
|
+
* =>
|
|
484
|
+
*
|
|
485
|
+
* ```sql
|
|
486
|
+
* SELECT
|
|
487
|
+
* "Horse"."id" AS "id",
|
|
488
|
+
* ...
|
|
489
|
+
* FROM (SELECT * FROM Horse ORDER BY age DESC LIMIT 10)
|
|
490
|
+
* LEFT JOIN ...
|
|
491
|
+
* ```
|
|
492
|
+
*
|
|
447
493
|
*/
|
|
448
|
-
list<T extends object>(ctor: new () => T,
|
|
494
|
+
list<T extends object>(ctor: new () => T, opts: {
|
|
495
|
+
includeTree?: IncludeTree<T> | null;
|
|
496
|
+
from?: string;
|
|
497
|
+
}): Promise<Either<string, T[]>>;
|
|
449
498
|
/**
|
|
450
|
-
*
|
|
451
|
-
*
|
|
499
|
+
* Retrieves a single model by primary key.
|
|
500
|
+
* @param ctor The model constructor.
|
|
501
|
+
* @param id The primary key value.
|
|
502
|
+
* @param includeTree An include tree describing which related models to join.
|
|
503
|
+
* @returns Either an error string, or the model instance (null if not found).
|
|
504
|
+
*
|
|
505
|
+
* ### Example:
|
|
506
|
+
* ```ts
|
|
507
|
+
* const orm = Orm.fromD1(env.db);
|
|
508
|
+
* const horse = await orm.get(Horse, 1, Horse.default);
|
|
509
|
+
* ```
|
|
452
510
|
*/
|
|
453
|
-
get<T extends object>(ctor: new () => T, id: any,
|
|
511
|
+
get<T extends object>(ctor: new () => T, id: any, includeTree?: IncludeTree<T> | null): Promise<Either<string, T | null>>;
|
|
454
512
|
}
|
|
455
513
|
//# sourceMappingURL=backend.d.ts.map
|
package/dist/ui/backend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/ui/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iDAAiD,CAAC;AAC7E,OAAO,
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/ui/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iDAAiD,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQzE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,YAAY,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,gBAAgB,EAChB,QAAQ,GACT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,EAAE,EAAE,cAAyB,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,cAAc,EAAE,cAAyB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,WAAW,EAAE,cAAyB,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,EAAE,iBAA4B,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,GAAG,EAAE,eAA0B,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,eAA0B,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,GAAG,EAAE,eAA0B,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,eAA0B,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,eAA0B,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,eAAO,MAAM,UAAU,EAAE,iBAA4B,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GACnB,mBAAmB,MAAM,KAAG,iBACrB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ,GAClB,mBAAmB,MAAM,KAAG,iBACrB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,GACpB,WAAW,MAAM,KAAG,iBACb,CAAC;AAEX;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,GACpB,CAAC,EAAE,QAAQ,CAAC,GAAG,MAAM,KAAG,iBACjB,CAAC;AAEX;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,MAAM,EAAE,kBAA6B,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,IAAI,GACd,QAAQ,QAAQ,EAAE,KAAG,cACd,CAAC;AAEX,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,SAAS,GAC7C,KAAK,GACL;KACG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACrC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAC3B,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI,CACzC,UAAU,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,GAC7B,MAAM,CACT,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC;AAE/B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AAEvD;;GAEG;AACH,qBAAa,GAAG;IACM,OAAO,CAAC,EAAE;IAA9B,OAAO;IAEP;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,GAAG,GAAG;IAIlC;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,EAC5B,IAAI,EAAE,UAAU,CAAC,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC9B,WAAW,GAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAW,GACxC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IAItB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACG,MAAM,CAAC,CAAC,SAAS,MAAM,EAC3B,IAAI,EAAE,UAAU,CAAC,EACjB,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EACxB,WAAW,GAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAW,GACxC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IA6C/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,MAAM,EAC/B,IAAI,EAAE,UAAU,CAAC,EACjB,IAAI,EAAE;QACJ,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GACA,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAWzB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,MAAM,EAC9B,IAAI,EAAE,UAAU,CAAC,EACjB,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,GAClC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAUzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,IAAI,CAAC,CAAC,SAAS,MAAM,EACzB,IAAI,EAAE,UAAU,CAAC,EACjB,IAAI,EAAE;QACJ,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IAsB/B;;;;;;;;;;;;OAYG;IACG,GAAG,CAAC,CAAC,SAAS,MAAM,EACxB,IAAI,EAAE,UAAU,CAAC,EACjB,EAAE,EAAE,GAAG,EACP,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;CAyBrC"}
|