cloesce 0.0.3
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/LICENSE +201 -0
- package/README.md +23 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +337 -0
- package/dist/cli.wasm +0 -0
- package/dist/cloesce.d.ts +108 -0
- package/dist/cloesce.d.ts.map +1 -0
- package/dist/cloesce.js +453 -0
- package/dist/common.d.ts +96 -0
- package/dist/common.d.ts.map +1 -0
- package/dist/common.js +96 -0
- package/dist/decorators.d.ts +13 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +13 -0
- package/dist/dog.cloesce.js +111 -0
- package/dist/extract.d.ts +14 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +517 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloesce.d.ts","sourceRoot":"","sources":["../src/cloesce.ts"],"names":[],"mappings":"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"}
|
package/dist/cloesce.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { left, right, isNullableType, getNavigationPropertyCidlType, } from "./common.js";
|
|
2
|
+
/**
|
|
3
|
+
* Singleton instances of the MetaCidl and Constructor Registry.
|
|
4
|
+
* These values are guaranteed to never change throughout a workers lifetime.
|
|
5
|
+
*/
|
|
6
|
+
class MetaContainer {
|
|
7
|
+
ast;
|
|
8
|
+
constructorRegistry;
|
|
9
|
+
static instance;
|
|
10
|
+
constructor(ast, constructorRegistry) {
|
|
11
|
+
this.ast = ast;
|
|
12
|
+
this.constructorRegistry = constructorRegistry;
|
|
13
|
+
}
|
|
14
|
+
static init(ast, constructorRegistry) {
|
|
15
|
+
if (!this.instance) {
|
|
16
|
+
this.instance = new MetaContainer(ast, constructorRegistry);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
static get() {
|
|
20
|
+
return this.instance;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates model instances given a properly formatted SQL record
|
|
25
|
+
* (either a foreign-key-less model or derived from a Cloesce generated view)
|
|
26
|
+
* @param ctor The type of the model
|
|
27
|
+
* @param records SQL records
|
|
28
|
+
* @param includeTree The include tree to use when parsing the records
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
export function modelsFromSql(ctor, records, includeTree) {
|
|
32
|
+
const { ast, constructorRegistry } = MetaContainer.get();
|
|
33
|
+
return _modelsFromSql(ctor.name, ast, constructorRegistry, records, includeTree);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Cloesce entry point. Given a request, undergoes routing, validating,
|
|
37
|
+
* hydrating, and method dispatch.
|
|
38
|
+
* @param ast The CIDL AST
|
|
39
|
+
* @param constructorRegistry A mapping of user defined class names to their respective constructor
|
|
40
|
+
* @param instanceRegistry A mapping of a dependency class name to its instantiated object.
|
|
41
|
+
* @param request An incoming request to the workers server
|
|
42
|
+
* @param api_route The url's path to the api, e.g. api/v1/fooapi/
|
|
43
|
+
* @param envMeta Meta information on the wrangler env and D1 databases
|
|
44
|
+
* @returns A Response with an `HttpResult` JSON body.
|
|
45
|
+
*/
|
|
46
|
+
export async function cloesce(request, ast, constructorRegistry, instanceRegistry, envMeta, api_route) {
|
|
47
|
+
MetaContainer.init(ast, constructorRegistry);
|
|
48
|
+
const d1 = instanceRegistry.get(envMeta.envName)[envMeta.dbName];
|
|
49
|
+
// Match the route to a model method
|
|
50
|
+
const route = matchRoute(request, ast, api_route);
|
|
51
|
+
if (!route.ok) {
|
|
52
|
+
return toResponse(route.value);
|
|
53
|
+
}
|
|
54
|
+
const { method, model, id } = route.value;
|
|
55
|
+
// Validate request body to the model method
|
|
56
|
+
const isValidRequest = await validateRequest(request, ast, model, method, id);
|
|
57
|
+
if (!isValidRequest.ok) {
|
|
58
|
+
return toResponse(isValidRequest.value);
|
|
59
|
+
}
|
|
60
|
+
const [requestParamMap, dataSource] = isValidRequest.value;
|
|
61
|
+
// Instantatiate the model
|
|
62
|
+
let instance;
|
|
63
|
+
if (method.is_static) {
|
|
64
|
+
instance = constructorRegistry[model.name];
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const successfulModel = await hydrateModel(ast, constructorRegistry, d1, model, id, dataSource);
|
|
68
|
+
if (!successfulModel.ok) {
|
|
69
|
+
return toResponse(successfulModel.value);
|
|
70
|
+
}
|
|
71
|
+
instance = successfulModel.value;
|
|
72
|
+
}
|
|
73
|
+
// Dispatch a method on the model and return the result
|
|
74
|
+
return toResponse(await methodDispatch(instance, instanceRegistry, envMeta, method, requestParamMap));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Matches a request to a method on a model.
|
|
78
|
+
* @param api_route The route from the domain to the actual API, ie https://foo.com/route/to/api => route/to/api/
|
|
79
|
+
* @returns 404 or a `MatchedRoute`
|
|
80
|
+
*/
|
|
81
|
+
function matchRoute(request, ast, api_route) {
|
|
82
|
+
const url = new URL(request.url);
|
|
83
|
+
const notFound = (e) => left(errorState(404, `Path not found: ${e} ${url.pathname}`));
|
|
84
|
+
const routeParts = url.pathname
|
|
85
|
+
.slice(api_route.length)
|
|
86
|
+
.split("/")
|
|
87
|
+
.filter(Boolean);
|
|
88
|
+
if (routeParts.length < 2) {
|
|
89
|
+
return notFound("Expected /model/method or /model/:id/method");
|
|
90
|
+
}
|
|
91
|
+
// Attempt to extract from routeParts
|
|
92
|
+
const modelName = routeParts[0];
|
|
93
|
+
const methodName = routeParts[routeParts.length - 1];
|
|
94
|
+
const id = routeParts.length === 3 ? routeParts[1] : null;
|
|
95
|
+
const model = ast.models[modelName];
|
|
96
|
+
if (!model) {
|
|
97
|
+
return notFound(`Unknown model ${modelName}`);
|
|
98
|
+
}
|
|
99
|
+
const method = model.methods[methodName];
|
|
100
|
+
if (!method) {
|
|
101
|
+
return notFound(`Unknown method ${modelName}.${methodName}`);
|
|
102
|
+
}
|
|
103
|
+
if (request.method !== method.http_verb) {
|
|
104
|
+
return notFound("Unmatched HTTP method");
|
|
105
|
+
}
|
|
106
|
+
return right({
|
|
107
|
+
model,
|
|
108
|
+
method,
|
|
109
|
+
id,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validates the request's body/search params against a ModelMethod
|
|
114
|
+
* @returns 400 or a `RequestParamMap` consisting of each parameters name mapped to its value, and
|
|
115
|
+
* a data source
|
|
116
|
+
*/
|
|
117
|
+
async function validateRequest(request, ast, model, method, id) {
|
|
118
|
+
// Error state: any missing parameter, body, or malformed input will exit with 400.
|
|
119
|
+
const invalidRequest = (e) => left(errorState(400, `Invalid Request Body: ${e}`));
|
|
120
|
+
if (!method.is_static && id == null) {
|
|
121
|
+
return invalidRequest("Id's are required for instantiated methods.");
|
|
122
|
+
}
|
|
123
|
+
// Filter out any injected parameters that will not be passed
|
|
124
|
+
// by the query.
|
|
125
|
+
const requiredParams = method.parameters.filter((p) => !(typeof p.cidl_type === "object" &&
|
|
126
|
+
p.cidl_type !== null &&
|
|
127
|
+
"Inject" in p.cidl_type));
|
|
128
|
+
// Extract data source
|
|
129
|
+
const url = new URL(request.url);
|
|
130
|
+
let dataSource = url.searchParams.get("dataSource");
|
|
131
|
+
// Extract url or body parameters
|
|
132
|
+
let requestBodyMap;
|
|
133
|
+
if (method.http_verb === "GET") {
|
|
134
|
+
requestBodyMap = Object.fromEntries(url.searchParams.entries());
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
try {
|
|
138
|
+
requestBodyMap = await request.json();
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return invalidRequest("Could not retrieve JSON body.");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Validate data source if exists
|
|
145
|
+
if (dataSource && !(dataSource in model.data_sources)) {
|
|
146
|
+
return invalidRequest(`Unknown data source ${dataSource}`);
|
|
147
|
+
}
|
|
148
|
+
// Ensure all required params exist
|
|
149
|
+
if (!requiredParams.every((p) => p.name in requestBodyMap)) {
|
|
150
|
+
return invalidRequest(`Missing parameters.`);
|
|
151
|
+
}
|
|
152
|
+
// Validate all parameters type
|
|
153
|
+
for (const p of requiredParams) {
|
|
154
|
+
const value = requestBodyMap[p.name];
|
|
155
|
+
if (!validateCidlType(ast, value, p.cidl_type)) {
|
|
156
|
+
return invalidRequest("Invalid parameters.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return right([requestBodyMap, dataSource]);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Queries D1 for a particular model's ID, then transforms the SQL column output into
|
|
163
|
+
* an instance of a model using the provided include tree and metadata as a guide.
|
|
164
|
+
* @returns 404 if no record was found for the provided ID
|
|
165
|
+
* @returns 500 if the D1 database is not synced with Cloesce and yields an error
|
|
166
|
+
* @returns The instantiated model on success
|
|
167
|
+
*/
|
|
168
|
+
async function hydrateModel(ast, constructorRegistry, d1, model, id, dataSource) {
|
|
169
|
+
// Error state: If the D1 database has been tweaked outside of Cloesce
|
|
170
|
+
// resulting in a malformed query, exit with a 500.
|
|
171
|
+
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)}`));
|
|
172
|
+
// Error state: If no record is found for the id, return a 404
|
|
173
|
+
const missingRecord = left(errorState(404, "Record not found"));
|
|
174
|
+
const pk = model.primary_key.name;
|
|
175
|
+
const query = dataSource !== null
|
|
176
|
+
? `SELECT * FROM "${model.name}.${dataSource}" WHERE "${model.name}.${pk}" = ?`
|
|
177
|
+
: `SELECT * FROM "${model.name}" WHERE "${pk}" = ?`;
|
|
178
|
+
// Query DB
|
|
179
|
+
let records;
|
|
180
|
+
try {
|
|
181
|
+
records = await d1.prepare(query).bind(id).run();
|
|
182
|
+
if (!records) {
|
|
183
|
+
return missingRecord;
|
|
184
|
+
}
|
|
185
|
+
if (records.error) {
|
|
186
|
+
return malformedQuery(records.error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
return malformedQuery(e);
|
|
191
|
+
}
|
|
192
|
+
// Get include tree
|
|
193
|
+
const includeTree = dataSource !== null ? model.data_sources[dataSource].tree : {};
|
|
194
|
+
// Hydrate
|
|
195
|
+
const models = _modelsFromSql(model.name, ast, constructorRegistry, records.results, includeTree);
|
|
196
|
+
console.log(JSON.stringify(models));
|
|
197
|
+
return right(models[0]);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Calls a method on a model given a list of parameters.
|
|
201
|
+
* @returns 500 on an uncaught client error, 200 with a result body on success
|
|
202
|
+
*/
|
|
203
|
+
async function methodDispatch(instance, instanceRegistry, envMeta, method, params) {
|
|
204
|
+
// Error state: Client code ran into an uncaught exception.
|
|
205
|
+
const uncaughtException = (e) => errorState(500, `Uncaught exception in method dispatch: ${e instanceof Error ? e.message : String(e)}`);
|
|
206
|
+
// For now, the only injected dependency is the wrangler env,
|
|
207
|
+
// so we will assume that is what this is
|
|
208
|
+
const paramArray = method.parameters.map((p) => params[p.name] == undefined
|
|
209
|
+
? instanceRegistry.get(envMeta.envName)
|
|
210
|
+
: params[p.name]);
|
|
211
|
+
// Ensure the result is always some HttpResult
|
|
212
|
+
const resultWrapper = (res) => {
|
|
213
|
+
const rt = method.return_type;
|
|
214
|
+
if (rt === null) {
|
|
215
|
+
return { ok: true, status: 200 };
|
|
216
|
+
}
|
|
217
|
+
if (typeof rt === "object" && rt !== null && "HttpResult" in rt) {
|
|
218
|
+
return res;
|
|
219
|
+
}
|
|
220
|
+
return { ok: true, status: 200, data: res };
|
|
221
|
+
};
|
|
222
|
+
try {
|
|
223
|
+
return resultWrapper(await instance[method.name](...paramArray));
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
return uncaughtException(e);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function validateCidlType(ast, value, cidlType) {
|
|
230
|
+
if (value === undefined)
|
|
231
|
+
return false;
|
|
232
|
+
// TODO: consequences of null checking like this? 'null' is passed in
|
|
233
|
+
// as a string for GET requests...
|
|
234
|
+
const nullable = isNullableType(cidlType);
|
|
235
|
+
if (value == null || value === "null")
|
|
236
|
+
return nullable;
|
|
237
|
+
if (nullable) {
|
|
238
|
+
cidlType = cidlType.Nullable; // Unwrap the nullable type
|
|
239
|
+
}
|
|
240
|
+
// Handle primitive string types with switch
|
|
241
|
+
if (typeof cidlType === "string") {
|
|
242
|
+
switch (cidlType) {
|
|
243
|
+
case "Integer":
|
|
244
|
+
return Number.isInteger(Number(value));
|
|
245
|
+
case "Real":
|
|
246
|
+
return !Number.isNaN(Number(value));
|
|
247
|
+
case "Text":
|
|
248
|
+
return typeof value === "string";
|
|
249
|
+
case "Blob":
|
|
250
|
+
return value instanceof Blob || value instanceof ArrayBuffer;
|
|
251
|
+
default:
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Handle Models
|
|
256
|
+
if ("Object" in cidlType && ast.models[cidlType.Object]) {
|
|
257
|
+
const model = ast.models[cidlType.Object];
|
|
258
|
+
if (!model || typeof value !== "object")
|
|
259
|
+
return false;
|
|
260
|
+
const valueObj = value;
|
|
261
|
+
// Validate attributes
|
|
262
|
+
if (!model.attributes.every((attr) => validateCidlType(ast, valueObj[attr.value.name], attr.value.cidl_type))) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
// Validate navigation properties
|
|
266
|
+
return model.navigation_properties.every((nav) => {
|
|
267
|
+
const navValue = valueObj[nav.var_name];
|
|
268
|
+
return (navValue == null ||
|
|
269
|
+
validateCidlType(ast, navValue, getNavigationPropertyCidlType(nav)));
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Handle Plain Old Objects
|
|
273
|
+
if ("Object" in cidlType && ast.poos[cidlType.Object]) {
|
|
274
|
+
const poo = ast.poos[cidlType.Object];
|
|
275
|
+
if (!poo || typeof value !== "object")
|
|
276
|
+
return false;
|
|
277
|
+
const valueObj = value;
|
|
278
|
+
// Validate attributes
|
|
279
|
+
if (!poo.attributes.every((attr) => validateCidlType(ast, valueObj[attr.name], attr.cidl_type))) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if ("Array" in cidlType) {
|
|
284
|
+
const arr = cidlType.Array;
|
|
285
|
+
return (Array.isArray(value) && value.every((v) => validateCidlType(ast, v, arr)));
|
|
286
|
+
}
|
|
287
|
+
if ("HttpResult" in cidlType) {
|
|
288
|
+
if (value === null)
|
|
289
|
+
return cidlType.HttpResult === null;
|
|
290
|
+
if (cidlType.HttpResult === null)
|
|
291
|
+
return false;
|
|
292
|
+
return validateCidlType(ast, value, cidlType.HttpResult);
|
|
293
|
+
}
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Actual implementation of sql to model mapping.
|
|
298
|
+
*
|
|
299
|
+
* TODO: If we don't want to write this in every language, would it be possible to create a
|
|
300
|
+
* single WASM binary for this method?
|
|
301
|
+
*
|
|
302
|
+
* @throws generic errors if the metadata is missing some value
|
|
303
|
+
*/
|
|
304
|
+
// Main function that creates instances from SQL records
|
|
305
|
+
function _modelsFromSql(modelName, ast, constructorRegistry, records, includeTree) {
|
|
306
|
+
const model = ast.models[modelName];
|
|
307
|
+
if (!model)
|
|
308
|
+
return [];
|
|
309
|
+
const Constructor = constructorRegistry[modelName];
|
|
310
|
+
if (!Constructor)
|
|
311
|
+
return [];
|
|
312
|
+
const pkName = model.primary_key.name;
|
|
313
|
+
const resultMap = new Map();
|
|
314
|
+
for (const record of records) {
|
|
315
|
+
const pkValue = record[`${modelName}.${pkName}`] ?? record[pkName];
|
|
316
|
+
if (pkValue == null)
|
|
317
|
+
continue;
|
|
318
|
+
let instance = resultMap.get(pkValue);
|
|
319
|
+
if (!instance) {
|
|
320
|
+
instance = new Constructor();
|
|
321
|
+
instance[pkName] = pkValue;
|
|
322
|
+
// Set scalar attributes
|
|
323
|
+
for (const attr of model.attributes) {
|
|
324
|
+
const attrName = attr.value.name;
|
|
325
|
+
const prefixedKey = `${modelName}.${attrName}`;
|
|
326
|
+
const nonPrefixedKey = attrName;
|
|
327
|
+
if (prefixedKey in record) {
|
|
328
|
+
instance[attrName] = record[prefixedKey];
|
|
329
|
+
}
|
|
330
|
+
else if (nonPrefixedKey in record) {
|
|
331
|
+
instance[attrName] = record[nonPrefixedKey];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Initialize ALL navigation properties at root level
|
|
335
|
+
// If not in include tree, initialize OneToMany and ManyToMany as empty arrays
|
|
336
|
+
for (const navProp of model.navigation_properties) {
|
|
337
|
+
if ("OneToMany" in navProp.kind || "ManyToMany" in navProp.kind) {
|
|
338
|
+
// Always initialize OneToMany and ManyToMany as empty arrays
|
|
339
|
+
instance[navProp.var_name] = [];
|
|
340
|
+
}
|
|
341
|
+
// OneToOne properties left as undefined unless populated
|
|
342
|
+
}
|
|
343
|
+
resultMap.set(pkValue, instance);
|
|
344
|
+
}
|
|
345
|
+
// Process navigation properties that are in the include tree
|
|
346
|
+
if (includeTree) {
|
|
347
|
+
processNavigationProperties(instance, model, modelName, includeTree, record, ast, constructorRegistry);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return Array.from(resultMap.values());
|
|
351
|
+
}
|
|
352
|
+
function processNavigationProperties(instance, model, prefix, includeTree, record, ast, constructorRegistry) {
|
|
353
|
+
for (const navProp of model.navigation_properties) {
|
|
354
|
+
if (!(navProp.var_name in includeTree)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const nestedModel = ast.models[navProp.model_name];
|
|
358
|
+
if (!nestedModel) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
// Extract nested model's primary key - check both prefixed and non-prefixed
|
|
362
|
+
const nestedPkName = nestedModel.primary_key.name;
|
|
363
|
+
const prefixedNestedPkKey = `${prefix}.${navProp.var_name}.${nestedPkName}`;
|
|
364
|
+
const nonPrefixedNestedPkKey = `${navProp.var_name}.${nestedPkName}`;
|
|
365
|
+
const nestedPkValue = record[prefixedNestedPkKey] ?? record[nonPrefixedNestedPkKey];
|
|
366
|
+
if (nestedPkValue == null) {
|
|
367
|
+
continue; // No nested object in this row
|
|
368
|
+
}
|
|
369
|
+
// Determine if this is OneToMany/ManyToMany or OneToOne
|
|
370
|
+
const isOneToMany = "OneToMany" in navProp.kind || "ManyToMany" in navProp.kind;
|
|
371
|
+
// Check if we already added this nested object (for OneToMany)
|
|
372
|
+
if (isOneToMany) {
|
|
373
|
+
const navArray = instance[navProp.var_name];
|
|
374
|
+
const alreadyExists = navArray.some((item) => item[nestedPkName] === nestedPkValue);
|
|
375
|
+
if (alreadyExists) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
// For OneToOne, check if already set
|
|
381
|
+
if (instance[navProp.var_name] != null) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const NestedConstructor = constructorRegistry[navProp.model_name];
|
|
386
|
+
if (!NestedConstructor) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const nestedInstance = new NestedConstructor();
|
|
390
|
+
nestedInstance[nestedPkName] = nestedPkValue;
|
|
391
|
+
// Assign nested scalar attributes - check both prefixed and non-prefixed
|
|
392
|
+
for (const nestedAttr of nestedModel.attributes) {
|
|
393
|
+
const nestedAttrName = nestedAttr.value.name;
|
|
394
|
+
const prefixedKey = `${prefix}.${navProp.var_name}.${nestedAttrName}`;
|
|
395
|
+
const nonPrefixedKey = `${navProp.var_name}.${nestedAttrName}`;
|
|
396
|
+
// Check prefixed key first, then non-prefixed
|
|
397
|
+
if (prefixedKey in record) {
|
|
398
|
+
nestedInstance[nestedAttrName] = record[prefixedKey];
|
|
399
|
+
}
|
|
400
|
+
else if (nonPrefixedKey in record) {
|
|
401
|
+
nestedInstance[nestedAttrName] = record[nonPrefixedKey];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Initialize ALL navigation properties on the nested instance
|
|
405
|
+
// If not in include tree, initialize OneToMany and ManyToMany as empty arrays
|
|
406
|
+
const nestedIncludeTree = includeTree[navProp.var_name];
|
|
407
|
+
for (const nestedNavProp of nestedModel.navigation_properties) {
|
|
408
|
+
const isInIncludeTree = nestedIncludeTree &&
|
|
409
|
+
typeof nestedIncludeTree === "object" &&
|
|
410
|
+
nestedNavProp.var_name in nestedIncludeTree;
|
|
411
|
+
if ("OneToMany" in nestedNavProp.kind ||
|
|
412
|
+
"ManyToMany" in nestedNavProp.kind) {
|
|
413
|
+
// Always initialize OneToMany and ManyToMany as arrays (empty if not in include tree)
|
|
414
|
+
nestedInstance[nestedNavProp.var_name] = [];
|
|
415
|
+
}
|
|
416
|
+
else if (!isInIncludeTree) {
|
|
417
|
+
// OneToOne not in include tree - leave as undefined or null
|
|
418
|
+
// Will be set during recursive processing if in include tree
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// Recursively process nested navigation properties that are in the include tree
|
|
422
|
+
if (nestedIncludeTree && typeof nestedIncludeTree === "object") {
|
|
423
|
+
processNavigationProperties(nestedInstance, nestedModel, `${prefix}.${navProp.var_name}`, nestedIncludeTree, record, ast, constructorRegistry);
|
|
424
|
+
}
|
|
425
|
+
// Assign the nested instance based on relationship type
|
|
426
|
+
if (isOneToMany) {
|
|
427
|
+
instance[navProp.var_name].push(nestedInstance);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
// OneToOne - assign directly
|
|
431
|
+
instance[navProp.var_name] = nestedInstance;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function errorState(status, message) {
|
|
436
|
+
return { ok: false, status, message };
|
|
437
|
+
}
|
|
438
|
+
function toResponse(r) {
|
|
439
|
+
return new Response(JSON.stringify(r), {
|
|
440
|
+
status: r.status,
|
|
441
|
+
headers: { "Content-Type": "application/json" },
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Each individual state of the `cloesce` function for testing purposes.
|
|
446
|
+
*/
|
|
447
|
+
export const _cloesceInternal = {
|
|
448
|
+
matchRoute,
|
|
449
|
+
validateRequest,
|
|
450
|
+
hydrateModel,
|
|
451
|
+
methodDispatch,
|
|
452
|
+
_modelsFromSql,
|
|
453
|
+
};
|
package/dist/common.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export type Either<L, R> = {
|
|
2
|
+
ok: false;
|
|
3
|
+
value: L;
|
|
4
|
+
} | {
|
|
5
|
+
ok: true;
|
|
6
|
+
value: R;
|
|
7
|
+
};
|
|
8
|
+
export declare function left<L>(value: L): Either<L, never>;
|
|
9
|
+
export declare function right<R>(value: R): Either<never, R>;
|
|
10
|
+
export type HttpResult<T = unknown> = {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
status: number;
|
|
13
|
+
data?: T;
|
|
14
|
+
message?: string;
|
|
15
|
+
};
|
|
16
|
+
export type CidlType = "Void" | "Integer" | "Real" | "Text" | "Blob" | {
|
|
17
|
+
Inject: string;
|
|
18
|
+
} | {
|
|
19
|
+
Model: string;
|
|
20
|
+
} | {
|
|
21
|
+
Nullable: CidlType;
|
|
22
|
+
} | {
|
|
23
|
+
Array: CidlType;
|
|
24
|
+
} | {
|
|
25
|
+
HttpResult: CidlType;
|
|
26
|
+
};
|
|
27
|
+
export declare function isNullableType(ty: CidlType): boolean;
|
|
28
|
+
export declare enum HttpVerb {
|
|
29
|
+
GET = "GET",
|
|
30
|
+
POST = "POST",
|
|
31
|
+
PUT = "PUT",
|
|
32
|
+
PATCH = "PATCH",
|
|
33
|
+
DELETE = "DELETE"
|
|
34
|
+
}
|
|
35
|
+
export interface NamedTypedValue {
|
|
36
|
+
name: string;
|
|
37
|
+
cidl_type: CidlType;
|
|
38
|
+
}
|
|
39
|
+
export interface ModelAttribute {
|
|
40
|
+
value: NamedTypedValue;
|
|
41
|
+
foreign_key_reference: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface ModelMethod {
|
|
44
|
+
name: string;
|
|
45
|
+
is_static: boolean;
|
|
46
|
+
http_verb: HttpVerb;
|
|
47
|
+
return_type: CidlType | null;
|
|
48
|
+
parameters: NamedTypedValue[];
|
|
49
|
+
}
|
|
50
|
+
export type NavigationPropertyKind = {
|
|
51
|
+
OneToOne: {
|
|
52
|
+
reference: string;
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
OneToMany: {
|
|
56
|
+
reference: string;
|
|
57
|
+
};
|
|
58
|
+
} | {
|
|
59
|
+
ManyToMany: {
|
|
60
|
+
unique_id: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export interface NavigationProperty {
|
|
64
|
+
var_name: string;
|
|
65
|
+
model_name: string;
|
|
66
|
+
kind: NavigationPropertyKind;
|
|
67
|
+
}
|
|
68
|
+
export declare function getNavigationPropertyCidlType(nav: NavigationProperty): CidlType;
|
|
69
|
+
export interface Model {
|
|
70
|
+
name: string;
|
|
71
|
+
primary_key: NamedTypedValue;
|
|
72
|
+
attributes: ModelAttribute[];
|
|
73
|
+
navigation_properties: NavigationProperty[];
|
|
74
|
+
methods: Record<string, ModelMethod>;
|
|
75
|
+
data_sources: Record<string, DataSource>;
|
|
76
|
+
source_path: string;
|
|
77
|
+
}
|
|
78
|
+
export interface CidlIncludeTree {
|
|
79
|
+
[key: string]: CidlIncludeTree;
|
|
80
|
+
}
|
|
81
|
+
export interface DataSource {
|
|
82
|
+
name: string;
|
|
83
|
+
tree: CidlIncludeTree;
|
|
84
|
+
}
|
|
85
|
+
export interface WranglerEnv {
|
|
86
|
+
name: string;
|
|
87
|
+
source_path: string;
|
|
88
|
+
}
|
|
89
|
+
export interface CloesceAst {
|
|
90
|
+
version: string;
|
|
91
|
+
project_name: string;
|
|
92
|
+
language: "TypeScript";
|
|
93
|
+
wrangler_env: WranglerEnv;
|
|
94
|
+
models: Record<string, Model>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"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"}
|
package/dist/common.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export function left(value) {
|
|
2
|
+
return { ok: false, value };
|
|
3
|
+
}
|
|
4
|
+
export function right(value) {
|
|
5
|
+
return { ok: true, value };
|
|
6
|
+
}
|
|
7
|
+
export var ExtractorErrorCode;
|
|
8
|
+
(function (ExtractorErrorCode) {
|
|
9
|
+
ExtractorErrorCode[ExtractorErrorCode["UnknownType"] = 0] = "UnknownType";
|
|
10
|
+
ExtractorErrorCode[ExtractorErrorCode["MultipleGenericType"] = 1] = "MultipleGenericType";
|
|
11
|
+
ExtractorErrorCode[ExtractorErrorCode["InvalidIncludeTree"] = 2] = "InvalidIncludeTree";
|
|
12
|
+
ExtractorErrorCode[ExtractorErrorCode["UnknownNavigationPropertyReference"] = 3] = "UnknownNavigationPropertyReference";
|
|
13
|
+
ExtractorErrorCode[ExtractorErrorCode["InvalidNavigationPropertyReference"] = 4] = "InvalidNavigationPropertyReference";
|
|
14
|
+
ExtractorErrorCode[ExtractorErrorCode["MissingNavigationPropertyReference"] = 5] = "MissingNavigationPropertyReference";
|
|
15
|
+
ExtractorErrorCode[ExtractorErrorCode["MissingManyToManyUniqueId"] = 6] = "MissingManyToManyUniqueId";
|
|
16
|
+
ExtractorErrorCode[ExtractorErrorCode["MissingPrimaryKey"] = 7] = "MissingPrimaryKey";
|
|
17
|
+
ExtractorErrorCode[ExtractorErrorCode["MissingWranglerEnv"] = 8] = "MissingWranglerEnv";
|
|
18
|
+
ExtractorErrorCode[ExtractorErrorCode["TooManyWranglerEnvs"] = 9] = "TooManyWranglerEnvs";
|
|
19
|
+
ExtractorErrorCode[ExtractorErrorCode["MissingFile"] = 10] = "MissingFile";
|
|
20
|
+
})(ExtractorErrorCode || (ExtractorErrorCode = {}));
|
|
21
|
+
const errorInfoMap = {
|
|
22
|
+
[ExtractorErrorCode.UnknownType]: {
|
|
23
|
+
description: "Encountered an unknown or unsupported type",
|
|
24
|
+
suggestion: "Refer to the documentation on valid Cloesce TS types",
|
|
25
|
+
},
|
|
26
|
+
[ExtractorErrorCode.MultipleGenericType]: {
|
|
27
|
+
description: "Cloesce does not yet support types with multiple generics",
|
|
28
|
+
suggestion: "Simplify your type to use only a single generic parameter, ie Foo<T>",
|
|
29
|
+
},
|
|
30
|
+
[ExtractorErrorCode.InvalidIncludeTree]: {
|
|
31
|
+
description: "Invalid Include Tree",
|
|
32
|
+
suggestion: "Include trees must only contain references to a model's navigation properties.",
|
|
33
|
+
},
|
|
34
|
+
[ExtractorErrorCode.UnknownNavigationPropertyReference]: {
|
|
35
|
+
description: "Unknown Navigation Property Reference",
|
|
36
|
+
suggestion: "Verify that the navigation property reference model exists, or create a model.",
|
|
37
|
+
},
|
|
38
|
+
[ExtractorErrorCode.InvalidNavigationPropertyReference]: {
|
|
39
|
+
description: "Invalid Navigation Property Reference",
|
|
40
|
+
suggestion: "Ensure the navigation property points to a valid model field",
|
|
41
|
+
},
|
|
42
|
+
[ExtractorErrorCode.MissingNavigationPropertyReference]: {
|
|
43
|
+
description: "Missing Navigation Property Reference",
|
|
44
|
+
suggestion: "Navigation properties require a foreign key model attribute reference",
|
|
45
|
+
},
|
|
46
|
+
[ExtractorErrorCode.MissingManyToManyUniqueId]: {
|
|
47
|
+
description: "Missing unique id on Many to Many navigation property",
|
|
48
|
+
suggestion: "Define a unique identifier field for the Many-to-Many relationship",
|
|
49
|
+
},
|
|
50
|
+
[ExtractorErrorCode.MissingPrimaryKey]: {
|
|
51
|
+
description: "Missing primary key on a model",
|
|
52
|
+
suggestion: "Add a primary key field to your model (e.g., `id: number`)",
|
|
53
|
+
},
|
|
54
|
+
[ExtractorErrorCode.MissingWranglerEnv]: {
|
|
55
|
+
description: "Missing a wrangler environment definition in the project",
|
|
56
|
+
suggestion: "Add a @WranglerEnv class in your project.",
|
|
57
|
+
},
|
|
58
|
+
[ExtractorErrorCode.TooManyWranglerEnvs]: {
|
|
59
|
+
description: "Too many wrangler environments defined in the project",
|
|
60
|
+
suggestion: "Consolidate or remove unused @WranglerEnv's",
|
|
61
|
+
},
|
|
62
|
+
[ExtractorErrorCode.MissingFile]: {
|
|
63
|
+
description: "A specified input file could not be found",
|
|
64
|
+
suggestion: "Verify the input file path is correct",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export function getErrorInfo(code) {
|
|
68
|
+
return errorInfoMap[code];
|
|
69
|
+
}
|
|
70
|
+
export class ExtractorError {
|
|
71
|
+
code;
|
|
72
|
+
context;
|
|
73
|
+
snippet;
|
|
74
|
+
constructor(code) {
|
|
75
|
+
this.code = code;
|
|
76
|
+
}
|
|
77
|
+
addContext(fn) {
|
|
78
|
+
this.context = fn(this.context ?? "");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function isNullableType(ty) {
|
|
82
|
+
return typeof ty === "object" && ty !== null && "Nullable" in ty;
|
|
83
|
+
}
|
|
84
|
+
export var HttpVerb;
|
|
85
|
+
(function (HttpVerb) {
|
|
86
|
+
HttpVerb["GET"] = "GET";
|
|
87
|
+
HttpVerb["POST"] = "POST";
|
|
88
|
+
HttpVerb["PUT"] = "PUT";
|
|
89
|
+
HttpVerb["PATCH"] = "PATCH";
|
|
90
|
+
HttpVerb["DELETE"] = "DELETE";
|
|
91
|
+
})(HttpVerb || (HttpVerb = {}));
|
|
92
|
+
export function getNavigationPropertyCidlType(nav) {
|
|
93
|
+
return "OneToOne" in nav.kind
|
|
94
|
+
? { Object: nav.model_name }
|
|
95
|
+
: { Array: { Object: nav.model_name } };
|
|
96
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Handler } from "./types.js";
|
|
2
|
+
/** Use as @GET (no parentheses) */
|
|
3
|
+
export declare function GET(_value: Handler, _ctx: ClassMethodDecoratorContext): void;
|
|
4
|
+
/** Use as @POST (no parentheses) */
|
|
5
|
+
export declare function POST(_value: Handler, _ctx: ClassMethodDecoratorContext): void;
|
|
6
|
+
export declare function PUT(_value: Handler, _ctx: ClassMethodDecoratorContext): void;
|
|
7
|
+
export declare function PATCH(_value: Handler, _ctx: ClassMethodDecoratorContext): void;
|
|
8
|
+
export declare function DELETE(_value: Handler, _ctx: ClassMethodDecoratorContext): void;
|
|
9
|
+
/** Class decorator (no-op) */
|
|
10
|
+
export declare function D1<T extends new (...a: any[]) => object>(value: T, _ctx: ClassDecoratorContext<T>): T;
|
|
11
|
+
/** Field decorator (no-op) */
|
|
12
|
+
export declare function PrimaryKey(_v: undefined, _ctx: ClassFieldDecoratorContext): void;
|
|
13
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,mCAAmC;AACnC,wBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,QAAI;AAE1E,oCAAoC;AACpC,wBAAgB,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,QAAI;AAE3E,wBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,QAAI;AAE1E,wBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,QAAI;AAE5E,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,QAAI;AAE7E,8BAA8B;AAC9B,wBAAgB,EAAE,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,MAAM,EACtD,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAG/B;AAED,8BAA8B;AAC9B,wBAAgB,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,0BAA0B,QAAI"}
|