cloesce 0.0.3-fix.6 → 0.0.4-unstable.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/README.md +154 -70
- package/dist/cli.js +65 -85
- package/dist/common.d.ts +49 -11
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +72 -11
- package/dist/extractor/extract.d.ts +2 -0
- package/dist/extractor/extract.d.ts.map +1 -1
- package/dist/extractor/extract.js +242 -43
- package/dist/generator.wasm +0 -0
- package/dist/orm.wasm +0 -0
- package/dist/router/crud.d.ts +22 -0
- package/dist/router/crud.d.ts.map +1 -0
- package/dist/router/crud.js +65 -0
- package/dist/router/router.d.ts +77 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/{runtime/runtime.js → router/router.js} +119 -161
- package/dist/router/wasm.d.ts +37 -0
- package/dist/router/wasm.d.ts.map +1 -0
- package/dist/router/wasm.js +98 -0
- package/dist/ui/backend.d.ts +124 -0
- package/dist/ui/backend.d.ts.map +1 -0
- package/dist/ui/backend.js +201 -0
- package/dist/{index → ui}/client.d.ts +1 -1
- package/dist/ui/client.d.ts.map +1 -0
- package/package.json +15 -13
- package/dist/LICENSE +0 -201
- package/dist/index/backend.d.ts +0 -22
- package/dist/index/backend.d.ts.map +0 -1
- package/dist/index/backend.js +0 -17
- package/dist/index/client.d.ts.map +0 -1
- package/dist/runtime/runtime.d.ts +0 -112
- package/dist/runtime/runtime.d.ts.map +0 -1
- package/dist/runtime.wasm +0 -0
- /package/dist/{index → ui}/client.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SyntaxKind, } from "ts-morph";
|
|
2
|
-
import { HttpVerb, left, right, ExtractorError, ExtractorErrorCode, } from "../common.js";
|
|
1
|
+
import { Node as MorphNode, SyntaxKind, Scope, } from "ts-morph";
|
|
2
|
+
import { HttpVerb, left, right, ExtractorError, ExtractorErrorCode, CloesceApp, } from "../common.js";
|
|
3
3
|
import { TypeFormatFlags } from "typescript";
|
|
4
4
|
var AttributeDecoratorKind;
|
|
5
5
|
(function (AttributeDecoratorKind) {
|
|
@@ -15,6 +15,7 @@ var ClassDecoratorKind;
|
|
|
15
15
|
ClassDecoratorKind["D1"] = "D1";
|
|
16
16
|
ClassDecoratorKind["WranglerEnv"] = "WranglerEnv";
|
|
17
17
|
ClassDecoratorKind["PlainOldObject"] = "PlainOldObject";
|
|
18
|
+
ClassDecoratorKind["CRUD"] = "CRUD";
|
|
18
19
|
})(ClassDecoratorKind || (ClassDecoratorKind = {}));
|
|
19
20
|
var ParameterDecoratorKind;
|
|
20
21
|
(function (ParameterDecoratorKind) {
|
|
@@ -30,43 +31,64 @@ export class CidlExtractor {
|
|
|
30
31
|
extract(project) {
|
|
31
32
|
const models = {};
|
|
32
33
|
const poos = {};
|
|
34
|
+
const wranglerEnvs = [];
|
|
35
|
+
let app_source = null;
|
|
33
36
|
for (const sourceFile of project.getSourceFiles()) {
|
|
37
|
+
if (sourceFile.getBaseName() === "app.cloesce.ts" ||
|
|
38
|
+
sourceFile.getBaseName() === "seed__app.cloesce.ts" // hardcoding for tests
|
|
39
|
+
) {
|
|
40
|
+
const app = CidlExtractor.app(sourceFile);
|
|
41
|
+
if (!app.ok) {
|
|
42
|
+
return app;
|
|
43
|
+
}
|
|
44
|
+
app_source = app.value;
|
|
45
|
+
}
|
|
34
46
|
for (const classDecl of sourceFile.getClasses()) {
|
|
47
|
+
const notExportedErr = err(ExtractorErrorCode.MissingExport, (e) => {
|
|
48
|
+
e.context = classDecl.getName();
|
|
49
|
+
e.snippet = classDecl.getText();
|
|
50
|
+
});
|
|
35
51
|
if (hasDecorator(classDecl, ClassDecoratorKind.D1)) {
|
|
52
|
+
if (!classDecl.isExported())
|
|
53
|
+
return notExportedErr;
|
|
36
54
|
const result = CidlExtractor.model(classDecl, sourceFile);
|
|
37
55
|
// Error: propogate from models
|
|
38
56
|
if (!result.ok) {
|
|
39
|
-
result.value.addContext((
|
|
57
|
+
result.value.addContext((prev) => `${classDecl.getName()}.${prev}`);
|
|
40
58
|
return result;
|
|
41
59
|
}
|
|
42
60
|
models[result.value.name] = result.value;
|
|
43
61
|
continue;
|
|
44
62
|
}
|
|
45
63
|
if (hasDecorator(classDecl, ClassDecoratorKind.PlainOldObject)) {
|
|
64
|
+
if (!classDecl.isExported())
|
|
65
|
+
return notExportedErr;
|
|
46
66
|
const result = CidlExtractor.poo(classDecl, sourceFile);
|
|
47
67
|
// Error: propogate from models
|
|
48
68
|
if (!result.ok) {
|
|
49
|
-
result.value.addContext((
|
|
69
|
+
result.value.addContext((prev) => `${classDecl.getName()}.${prev}`);
|
|
50
70
|
return result;
|
|
51
71
|
}
|
|
52
72
|
poos[result.value.name] = result.value;
|
|
53
73
|
continue;
|
|
54
74
|
}
|
|
75
|
+
if (hasDecorator(classDecl, ClassDecoratorKind.WranglerEnv)) {
|
|
76
|
+
if (!classDecl.isExported())
|
|
77
|
+
return notExportedErr;
|
|
78
|
+
// Error: invalid attribute modifier
|
|
79
|
+
for (const prop of classDecl.getProperties()) {
|
|
80
|
+
const modifierRes = checkAttributeModifier(prop);
|
|
81
|
+
if (modifierRes) {
|
|
82
|
+
return modifierRes;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
wranglerEnvs.push({
|
|
86
|
+
name: classDecl.getName(),
|
|
87
|
+
source_path: sourceFile.getFilePath().toString(),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
|
-
const wranglerEnvs = project
|
|
58
|
-
.getSourceFiles()
|
|
59
|
-
.flatMap((sourceFile) => {
|
|
60
|
-
return sourceFile
|
|
61
|
-
.getClasses()
|
|
62
|
-
.filter((classDecl) => hasDecorator(classDecl, ClassDecoratorKind.WranglerEnv))
|
|
63
|
-
.map((classDecl) => {
|
|
64
|
-
return {
|
|
65
|
-
name: classDecl.getName(),
|
|
66
|
-
source_path: sourceFile.getFilePath().toString(),
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
92
|
// Error: A wrangler environment is required
|
|
71
93
|
if (wranglerEnvs.length < 1) {
|
|
72
94
|
return err(ExtractorErrorCode.MissingWranglerEnv);
|
|
@@ -82,15 +104,46 @@ export class CidlExtractor {
|
|
|
82
104
|
wrangler_env: wranglerEnvs[0],
|
|
83
105
|
models,
|
|
84
106
|
poos,
|
|
107
|
+
app_source,
|
|
85
108
|
});
|
|
86
109
|
}
|
|
110
|
+
static app(sourceFile) {
|
|
111
|
+
const symbol = sourceFile.getDefaultExportSymbol();
|
|
112
|
+
const decl = symbol?.getDeclarations()[0];
|
|
113
|
+
if (!decl) {
|
|
114
|
+
return err(ExtractorErrorCode.AppMissingDefaultExport);
|
|
115
|
+
}
|
|
116
|
+
const getTypeText = () => {
|
|
117
|
+
if (MorphNode.isExportAssignment(decl)) {
|
|
118
|
+
return decl.getExpression()?.getType().getText();
|
|
119
|
+
}
|
|
120
|
+
if (MorphNode.isVariableDeclaration(decl)) {
|
|
121
|
+
return decl.getInitializer()?.getType().getText();
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
};
|
|
125
|
+
const typeText = getTypeText();
|
|
126
|
+
if (typeText === CloesceApp.name) {
|
|
127
|
+
return right(sourceFile.getFilePath().toString());
|
|
128
|
+
}
|
|
129
|
+
return err(ExtractorErrorCode.AppMissingDefaultExport);
|
|
130
|
+
}
|
|
87
131
|
static model(classDecl, sourceFile) {
|
|
88
132
|
const name = classDecl.getName();
|
|
89
133
|
const attributes = [];
|
|
90
134
|
const navigationProperties = [];
|
|
91
135
|
const dataSources = {};
|
|
92
136
|
const methods = {};
|
|
137
|
+
let cruds = [];
|
|
93
138
|
let primary_key = undefined;
|
|
139
|
+
// Extract crud methods
|
|
140
|
+
const crudDecorator = classDecl
|
|
141
|
+
.getDecorators()
|
|
142
|
+
.find((d) => getDecoratorName(d) === ClassDecoratorKind.CRUD);
|
|
143
|
+
if (crudDecorator) {
|
|
144
|
+
cruds = getCrudKinds(crudDecorator);
|
|
145
|
+
}
|
|
146
|
+
// Iterate attribtutes
|
|
94
147
|
for (const prop of classDecl.getProperties()) {
|
|
95
148
|
const decorators = prop.getDecorators();
|
|
96
149
|
const typeRes = CidlExtractor.cidlType(prop.getType());
|
|
@@ -100,8 +153,13 @@ export class CidlExtractor {
|
|
|
100
153
|
typeRes.value.snippet = prop.getText();
|
|
101
154
|
return typeRes;
|
|
102
155
|
}
|
|
156
|
+
const checkModifierRes = checkAttributeModifier(prop);
|
|
103
157
|
// No decorators means this is a standard attribute
|
|
104
158
|
if (decorators.length === 0) {
|
|
159
|
+
// Error: invalid attribute modifier
|
|
160
|
+
if (checkModifierRes !== undefined) {
|
|
161
|
+
return checkModifierRes;
|
|
162
|
+
}
|
|
105
163
|
const cidl_type = typeRes.value;
|
|
106
164
|
attributes.push({
|
|
107
165
|
foreign_key_reference: null,
|
|
@@ -115,6 +173,11 @@ export class CidlExtractor {
|
|
|
115
173
|
// TODO: Limiting to one decorator. Can't get too fancy on us.
|
|
116
174
|
const decorator = decorators[0];
|
|
117
175
|
const name = getDecoratorName(decorator);
|
|
176
|
+
// Error: invalid attribute modifier
|
|
177
|
+
if (checkModifierRes !== undefined &&
|
|
178
|
+
name !== AttributeDecoratorKind.DataSource) {
|
|
179
|
+
return checkModifierRes;
|
|
180
|
+
}
|
|
118
181
|
// Process decorators
|
|
119
182
|
const cidl_type = typeRes.value;
|
|
120
183
|
switch (name) {
|
|
@@ -207,10 +270,17 @@ export class CidlExtractor {
|
|
|
207
270
|
break;
|
|
208
271
|
}
|
|
209
272
|
case AttributeDecoratorKind.DataSource: {
|
|
273
|
+
// Error: data sources must be static
|
|
274
|
+
if (!prop.isStatic()) {
|
|
275
|
+
return err(ExtractorErrorCode.DataSourceMissingStatic, (e) => {
|
|
276
|
+
e.snippet = prop.getText();
|
|
277
|
+
e.context = prop.getName();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
210
280
|
const initializer = prop.getInitializer();
|
|
211
281
|
const treeRes = CidlExtractor.includeTree(initializer, classDecl, sourceFile);
|
|
212
282
|
if (!treeRes.ok) {
|
|
213
|
-
treeRes.value.addContext((
|
|
283
|
+
treeRes.value.addContext((prev) => `${prop.getName()} ${prev}`);
|
|
214
284
|
treeRes.value.snippet = prop.getText();
|
|
215
285
|
return treeRes;
|
|
216
286
|
}
|
|
@@ -229,13 +299,26 @@ export class CidlExtractor {
|
|
|
229
299
|
}
|
|
230
300
|
// Process methods
|
|
231
301
|
for (const m of classDecl.getMethods()) {
|
|
232
|
-
const
|
|
302
|
+
const httpVerb = m
|
|
303
|
+
.getDecorators()
|
|
304
|
+
.map((d) => getDecoratorName(d))
|
|
305
|
+
.find((name) => Object.values(HttpVerb).includes(name));
|
|
306
|
+
if (!httpVerb) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const result = CidlExtractor.method(name, m, httpVerb);
|
|
233
310
|
if (!result.ok) {
|
|
234
|
-
result.value.addContext((
|
|
311
|
+
result.value.addContext((prev) => `${m.getName()} ${prev}`);
|
|
235
312
|
return left(result.value);
|
|
236
313
|
}
|
|
237
314
|
methods[result.value.name] = result.value;
|
|
238
315
|
}
|
|
316
|
+
// Add CRUD methods
|
|
317
|
+
for (const crud of cruds) {
|
|
318
|
+
// TODO: This overwrites any exisiting impl-- is that what we want?
|
|
319
|
+
const crudMethod = CidlExtractor.crudMethod(crud, primary_key, name);
|
|
320
|
+
methods[crudMethod.name] = crudMethod;
|
|
321
|
+
}
|
|
239
322
|
return right({
|
|
240
323
|
name,
|
|
241
324
|
attributes,
|
|
@@ -257,6 +340,11 @@ export class CidlExtractor {
|
|
|
257
340
|
typeRes.value.snippet = prop.getText();
|
|
258
341
|
return typeRes;
|
|
259
342
|
}
|
|
343
|
+
// Error: invalid attribute modifier
|
|
344
|
+
const modifierRes = checkAttributeModifier(prop);
|
|
345
|
+
if (modifierRes) {
|
|
346
|
+
return modifierRes;
|
|
347
|
+
}
|
|
260
348
|
const cidl_type = typeRes.value;
|
|
261
349
|
attributes.push({
|
|
262
350
|
name: prop.getName(),
|
|
@@ -277,7 +365,6 @@ export class CidlExtractor {
|
|
|
277
365
|
String: "Text",
|
|
278
366
|
boolean: "Integer",
|
|
279
367
|
Boolean: "Integer",
|
|
280
|
-
Date: "Text",
|
|
281
368
|
};
|
|
282
369
|
static cidlType(type, inject = false) {
|
|
283
370
|
// Void
|
|
@@ -316,6 +403,30 @@ export class CidlExtractor {
|
|
|
316
403
|
const genericTy = generics[0];
|
|
317
404
|
const symbolName = unwrappedType.getSymbol()?.getName();
|
|
318
405
|
const aliasName = unwrappedType.getAliasSymbol()?.getName();
|
|
406
|
+
if (aliasName === "DataSourceOf") {
|
|
407
|
+
return right(wrapNullable({
|
|
408
|
+
DataSource: genericTy.getText(undefined, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope),
|
|
409
|
+
}, nullable));
|
|
410
|
+
}
|
|
411
|
+
if (aliasName === "DeepPartial") {
|
|
412
|
+
const [_, genericTyNullable] = unwrapNullable(genericTy);
|
|
413
|
+
const genericTyGenerics = [
|
|
414
|
+
...genericTy.getAliasTypeArguments(),
|
|
415
|
+
...genericTy.getTypeArguments(),
|
|
416
|
+
];
|
|
417
|
+
// Expect partials to be of the exact form DeepPartial<Model>
|
|
418
|
+
if (genericTyNullable ||
|
|
419
|
+
genericTy.isUnion() ||
|
|
420
|
+
genericTyGenerics.length > 0) {
|
|
421
|
+
return err(ExtractorErrorCode.InvalidPartialType);
|
|
422
|
+
}
|
|
423
|
+
return right(wrapNullable({
|
|
424
|
+
Partial: genericTy
|
|
425
|
+
.getText(undefined, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)
|
|
426
|
+
.split("|")[0]
|
|
427
|
+
.trim(),
|
|
428
|
+
}, nullable));
|
|
429
|
+
}
|
|
319
430
|
if (symbolName === "Promise" || aliasName === "IncludeTree") {
|
|
320
431
|
return wrapGeneric(genericTy, nullable, (inner) => inner);
|
|
321
432
|
}
|
|
@@ -413,10 +524,15 @@ export class CidlExtractor {
|
|
|
413
524
|
}
|
|
414
525
|
return right(result);
|
|
415
526
|
}
|
|
416
|
-
static method(method) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
527
|
+
static method(modelName, method, httpVerb) {
|
|
528
|
+
// Error: invalid method scope, must be public
|
|
529
|
+
if (method.getScope() != Scope.Public) {
|
|
530
|
+
return err(ExtractorErrorCode.InvalidApiMethodModifier, (e) => {
|
|
531
|
+
e.context = method.getName();
|
|
532
|
+
e.snippet = method.getText();
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
let needsDataSource = !method.isStatic();
|
|
420
536
|
const parameters = [];
|
|
421
537
|
for (const param of method.getParameters()) {
|
|
422
538
|
// Handle injected param
|
|
@@ -442,6 +558,10 @@ export class CidlExtractor {
|
|
|
442
558
|
typeRes.value.context = param.getName();
|
|
443
559
|
return typeRes;
|
|
444
560
|
}
|
|
561
|
+
const rootType = getRootType(typeRes.value);
|
|
562
|
+
if (typeof rootType !== "string" && "DataSource" in rootType) {
|
|
563
|
+
needsDataSource = false;
|
|
564
|
+
}
|
|
445
565
|
parameters.push({
|
|
446
566
|
name: param.getName(),
|
|
447
567
|
cidl_type: typeRes.value,
|
|
@@ -453,6 +573,13 @@ export class CidlExtractor {
|
|
|
453
573
|
typeRes.value.snippet = method.getText();
|
|
454
574
|
return typeRes;
|
|
455
575
|
}
|
|
576
|
+
// Sugaring: add data source
|
|
577
|
+
if (needsDataSource) {
|
|
578
|
+
parameters.push({
|
|
579
|
+
name: "__dataSource",
|
|
580
|
+
cidl_type: { DataSource: modelName },
|
|
581
|
+
});
|
|
582
|
+
}
|
|
456
583
|
return right({
|
|
457
584
|
name: method.getName(),
|
|
458
585
|
is_static: method.isStatic(),
|
|
@@ -461,6 +588,56 @@ export class CidlExtractor {
|
|
|
461
588
|
parameters,
|
|
462
589
|
});
|
|
463
590
|
}
|
|
591
|
+
static crudMethod(crud, primaryKey, modelName) {
|
|
592
|
+
// TODO: Should this impementation be in some JSON project file s.t. other
|
|
593
|
+
// langs can use it?
|
|
594
|
+
return {
|
|
595
|
+
POST: {
|
|
596
|
+
name: "post",
|
|
597
|
+
is_static: true,
|
|
598
|
+
http_verb: HttpVerb.POST,
|
|
599
|
+
return_type: { HttpResult: { Object: modelName } },
|
|
600
|
+
parameters: [
|
|
601
|
+
{
|
|
602
|
+
name: "obj",
|
|
603
|
+
cidl_type: { Partial: modelName },
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: "dataSource",
|
|
607
|
+
cidl_type: { DataSource: modelName },
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
GET: {
|
|
612
|
+
name: "get",
|
|
613
|
+
is_static: true,
|
|
614
|
+
http_verb: HttpVerb.GET,
|
|
615
|
+
return_type: { HttpResult: { Object: modelName } },
|
|
616
|
+
parameters: [
|
|
617
|
+
{
|
|
618
|
+
name: "id",
|
|
619
|
+
cidl_type: primaryKey.cidl_type,
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: "dataSource",
|
|
623
|
+
cidl_type: { DataSource: modelName },
|
|
624
|
+
},
|
|
625
|
+
],
|
|
626
|
+
},
|
|
627
|
+
LIST: {
|
|
628
|
+
name: "list",
|
|
629
|
+
is_static: true,
|
|
630
|
+
http_verb: HttpVerb.GET,
|
|
631
|
+
return_type: { HttpResult: { Array: { Object: modelName } } },
|
|
632
|
+
parameters: [
|
|
633
|
+
{
|
|
634
|
+
name: "dataSource",
|
|
635
|
+
cidl_type: { DataSource: modelName },
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
}[crud];
|
|
640
|
+
}
|
|
464
641
|
}
|
|
465
642
|
function err(code, fn) {
|
|
466
643
|
let e = new ExtractorError(code);
|
|
@@ -478,33 +655,46 @@ function getDecoratorArgument(decorator, index) {
|
|
|
478
655
|
if (!args[index])
|
|
479
656
|
return undefined;
|
|
480
657
|
const arg = args[index];
|
|
481
|
-
// Identifier
|
|
482
658
|
if (arg.getKind?.() === SyntaxKind.Identifier) {
|
|
483
659
|
return arg.getText();
|
|
484
660
|
}
|
|
485
|
-
|
|
486
|
-
const text = arg.getText?.();
|
|
487
|
-
if (!text)
|
|
488
|
-
return undefined;
|
|
489
|
-
const match = text.match(/^['"](.*)['"]$/);
|
|
490
|
-
return match ? match[1] : text;
|
|
661
|
+
return arg.getLiteralValue();
|
|
491
662
|
}
|
|
492
|
-
function
|
|
493
|
-
if (typeof t === "string")
|
|
494
|
-
return
|
|
495
|
-
if ("Object" in t) {
|
|
496
|
-
return t.Object;
|
|
663
|
+
function getRootType(t) {
|
|
664
|
+
if (typeof t === "string") {
|
|
665
|
+
return t;
|
|
497
666
|
}
|
|
498
|
-
|
|
499
|
-
return
|
|
667
|
+
if ("Nullable" in t) {
|
|
668
|
+
return getRootType(t.Nullable);
|
|
500
669
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
670
|
+
if ("Array" in t) {
|
|
671
|
+
return getRootType(t.Array);
|
|
672
|
+
}
|
|
673
|
+
if ("HttpResult" in t) {
|
|
674
|
+
return getRootType(t.HttpResult);
|
|
675
|
+
}
|
|
676
|
+
return t;
|
|
677
|
+
}
|
|
678
|
+
function getObjectName(t) {
|
|
679
|
+
const root = getRootType(t);
|
|
680
|
+
if (typeof root !== "string" && "Object" in root) {
|
|
681
|
+
return root["Object"];
|
|
505
682
|
}
|
|
506
683
|
return undefined;
|
|
507
684
|
}
|
|
685
|
+
function getCrudKinds(d) {
|
|
686
|
+
const arg = d.getArguments()[0];
|
|
687
|
+
if (!arg)
|
|
688
|
+
return [];
|
|
689
|
+
if (MorphNode.isArrayLiteralExpression(arg)) {
|
|
690
|
+
return arg
|
|
691
|
+
.getElements()
|
|
692
|
+
.map((e) => (MorphNode.isStringLiteral(e)
|
|
693
|
+
? e.getLiteralValue()
|
|
694
|
+
: e.getText()));
|
|
695
|
+
}
|
|
696
|
+
return [];
|
|
697
|
+
}
|
|
508
698
|
function findPropertyByName(cls, name) {
|
|
509
699
|
const exactMatch = cls.getProperties().find((p) => p.getName() === name);
|
|
510
700
|
return exactMatch;
|
|
@@ -515,3 +705,12 @@ function hasDecorator(node, name) {
|
|
|
515
705
|
return decoratorName === name || decoratorName.endsWith("." + name);
|
|
516
706
|
});
|
|
517
707
|
}
|
|
708
|
+
function checkAttributeModifier(prop) {
|
|
709
|
+
// Error: attributes must be just 'public'
|
|
710
|
+
if (prop.getScope() != Scope.Public || prop.isReadonly() || prop.isStatic()) {
|
|
711
|
+
return err(ExtractorErrorCode.InvalidAttributeModifier, (e) => {
|
|
712
|
+
e.context = prop.getName();
|
|
713
|
+
e.snippet = prop.getText();
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
package/dist/generator.wasm
CHANGED
|
Binary file
|
package/dist/orm.wasm
ADDED
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { D1Database } from "@cloudflare/workers-types/experimental";
|
|
2
|
+
import { HttpResult } from "../common.js";
|
|
3
|
+
/**
|
|
4
|
+
* A wrapper for Model Instances, containing definitions for built-in CRUD methods.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CrudContext {
|
|
7
|
+
private d1;
|
|
8
|
+
private instance;
|
|
9
|
+
private ctor;
|
|
10
|
+
private constructor();
|
|
11
|
+
static fromInstance(d1: D1Database, instance: any, ctor: new () => object): CrudContext;
|
|
12
|
+
static fromCtor(d1: D1Database, ctor: new () => object): CrudContext;
|
|
13
|
+
/**
|
|
14
|
+
* Invokes a method on the instance, intercepting built-in CRUD methods and injecting
|
|
15
|
+
* a default definition.
|
|
16
|
+
*/
|
|
17
|
+
interceptCrud(methodName: string): Function;
|
|
18
|
+
upsert(obj: object, dataSource: string): Promise<HttpResult<unknown>>;
|
|
19
|
+
get(id: any, dataSource: string): Promise<HttpResult<unknown>>;
|
|
20
|
+
list(dataSource: string): Promise<HttpResult<unknown>>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=crud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/router/crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAG1D;;GAEG;AACH,qBAAa,WAAW;IAEpB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,IAAI;IAHd,OAAO;IAMP,MAAM,CAAC,YAAY,CACjB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,UAAU,MAAM,GACrB,WAAW;IAId,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,MAAM,GAAG,WAAW;IAIpE;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAWrC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAkBrE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAS9D,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;CAQ7D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { NO_DATA_SOURCE } from "../common.js";
|
|
2
|
+
import { Orm } from "../ui/backend.js";
|
|
3
|
+
/**
|
|
4
|
+
* A wrapper for Model Instances, containing definitions for built-in CRUD methods.
|
|
5
|
+
*/
|
|
6
|
+
export class CrudContext {
|
|
7
|
+
d1;
|
|
8
|
+
instance;
|
|
9
|
+
ctor;
|
|
10
|
+
constructor(d1, instance, ctor) {
|
|
11
|
+
this.d1 = d1;
|
|
12
|
+
this.instance = instance;
|
|
13
|
+
this.ctor = ctor;
|
|
14
|
+
}
|
|
15
|
+
static fromInstance(d1, instance, ctor) {
|
|
16
|
+
return new this(d1, instance, ctor);
|
|
17
|
+
}
|
|
18
|
+
static fromCtor(d1, ctor) {
|
|
19
|
+
return new this(d1, ctor, ctor);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Invokes a method on the instance, intercepting built-in CRUD methods and injecting
|
|
23
|
+
* a default definition.
|
|
24
|
+
*/
|
|
25
|
+
interceptCrud(methodName) {
|
|
26
|
+
const map = {
|
|
27
|
+
post: this.upsert.bind(this),
|
|
28
|
+
get: this.get.bind(this),
|
|
29
|
+
list: this.list.bind(this),
|
|
30
|
+
};
|
|
31
|
+
const fn = this.instance && this.instance[methodName];
|
|
32
|
+
return fn ? fn.bind(this.instance) : map[methodName];
|
|
33
|
+
}
|
|
34
|
+
async upsert(obj, dataSource) {
|
|
35
|
+
const normalizedDs = dataSource === NO_DATA_SOURCE ? null : dataSource;
|
|
36
|
+
const includeTree = normalizedDs ? this.ctor[normalizedDs] : null;
|
|
37
|
+
// Upsert
|
|
38
|
+
const orm = Orm.fromD1(this.d1);
|
|
39
|
+
const upsert = await orm.upsert(this.ctor, obj, includeTree);
|
|
40
|
+
if (!upsert.ok) {
|
|
41
|
+
return { ok: false, status: 500, data: upsert.value }; // TODO: better status code?
|
|
42
|
+
}
|
|
43
|
+
// Get
|
|
44
|
+
const get = await orm.get(this.ctor, upsert.value, normalizedDs);
|
|
45
|
+
return get.ok
|
|
46
|
+
? { ok: true, status: 200, data: get.value }
|
|
47
|
+
: { ok: false, status: 500, data: get.value };
|
|
48
|
+
}
|
|
49
|
+
async get(id, dataSource) {
|
|
50
|
+
const normalizedDs = dataSource === NO_DATA_SOURCE ? null : dataSource;
|
|
51
|
+
const orm = Orm.fromD1(this.d1);
|
|
52
|
+
const res = await orm.get(this.ctor, id, normalizedDs);
|
|
53
|
+
return res.ok
|
|
54
|
+
? { ok: true, status: 200, data: res.value }
|
|
55
|
+
: { ok: false, status: 500, data: res.value };
|
|
56
|
+
}
|
|
57
|
+
async list(dataSource) {
|
|
58
|
+
const normalizedDs = dataSource === NO_DATA_SOURCE ? null : dataSource;
|
|
59
|
+
const orm = Orm.fromD1(this.d1);
|
|
60
|
+
const res = await orm.list(this.ctor, normalizedDs);
|
|
61
|
+
return res.ok
|
|
62
|
+
? { ok: true, status: 200, data: res.value }
|
|
63
|
+
: { ok: false, status: 500, data: res.value };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { HttpResult, Either, ModelMethod, CloesceAst, Model, CloesceApp, InstanceRegistry } from "../common.js";
|
|
2
|
+
import { OrmWasmExports } from "./wasm.js";
|
|
3
|
+
import { CrudContext } from "./crud.js";
|
|
4
|
+
/**
|
|
5
|
+
* Map of model names to their respective constructor.
|
|
6
|
+
*
|
|
7
|
+
* The value accepted into the `cloesce` function is generated by the Cloesce compiler, and
|
|
8
|
+
* is guaranteed to contain all model definitions.
|
|
9
|
+
*/
|
|
10
|
+
type ModelConstructorRegistry = Record<string, new () => any>;
|
|
11
|
+
/**
|
|
12
|
+
* Given a request, this represents a map of each body / url param name to
|
|
13
|
+
* its actual value. Unknown, as the a request can be anything.
|
|
14
|
+
*/
|
|
15
|
+
type RequestParamMap = Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Meta information on the wrangler env and db bindings
|
|
18
|
+
*/
|
|
19
|
+
interface MetaWranglerEnv {
|
|
20
|
+
envName: string;
|
|
21
|
+
dbName: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Singleton instance containing the cidl, constructor registry, and wasm binary.
|
|
25
|
+
* These values are guaranteed to never change throughout a workers lifetime.
|
|
26
|
+
*/
|
|
27
|
+
export declare class RuntimeContainer {
|
|
28
|
+
readonly ast: CloesceAst;
|
|
29
|
+
readonly constructorRegistry: ModelConstructorRegistry;
|
|
30
|
+
readonly wasm: OrmWasmExports;
|
|
31
|
+
private static instance;
|
|
32
|
+
private constructor();
|
|
33
|
+
static init(ast: CloesceAst, constructorRegistry: ModelConstructorRegistry, wasm?: WebAssembly.Instance): Promise<void>;
|
|
34
|
+
static get(): RuntimeContainer;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Runtime entry point. Given a request, undergoes: routing, validating,
|
|
38
|
+
* hydrating, and method dispatch.
|
|
39
|
+
*
|
|
40
|
+
* @returns A Response with an `HttpResult` JSON body.
|
|
41
|
+
*/
|
|
42
|
+
export declare function cloesce(request: Request, env: any, ast: CloesceAst, app: CloesceApp, constructorRegistry: ModelConstructorRegistry, envMeta: MetaWranglerEnv, apiRoute: string): Promise<Response>;
|
|
43
|
+
/**
|
|
44
|
+
* Matches a request to a method on a model.
|
|
45
|
+
* @param apiRoute The route from the domain to the actual API, ie https://foo.com/route/to/api => route/to/api/
|
|
46
|
+
* @returns 404 or a `MatchedRoute`
|
|
47
|
+
*/
|
|
48
|
+
declare function matchRoute(request: Request, ast: CloesceAst, apiRoute: string): Either<HttpResult, {
|
|
49
|
+
model: Model;
|
|
50
|
+
method: ModelMethod;
|
|
51
|
+
id: string | null;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Validates the request's body/search params against a ModelMethod
|
|
55
|
+
* @returns 400 or a `RequestParamMap` consisting of each parameters name mapped to its value, and
|
|
56
|
+
* a data source
|
|
57
|
+
*/
|
|
58
|
+
declare function validateRequest(request: Request, ast: CloesceAst, model: Model, method: ModelMethod, id: string | null): Promise<Either<HttpResult, {
|
|
59
|
+
params: RequestParamMap;
|
|
60
|
+
dataSource: string | null;
|
|
61
|
+
}>>;
|
|
62
|
+
/**
|
|
63
|
+
* Calls a method on a model given a list of parameters.
|
|
64
|
+
* @returns 500 on an uncaught client error, 200 with a result body on success
|
|
65
|
+
*/
|
|
66
|
+
declare function methodDispatch(crudCtx: CrudContext, instanceRegistry: InstanceRegistry, method: ModelMethod, params: Record<string, unknown>): Promise<HttpResult<unknown>>;
|
|
67
|
+
/**
|
|
68
|
+
* For testing purposes
|
|
69
|
+
*/
|
|
70
|
+
export declare const _cloesceInternal: {
|
|
71
|
+
matchRoute: typeof matchRoute;
|
|
72
|
+
validateRequest: typeof validateRequest;
|
|
73
|
+
methodDispatch: typeof methodDispatch;
|
|
74
|
+
RuntimeContainer: typeof RuntimeContainer;
|
|
75
|
+
};
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,MAAM,EACN,WAAW,EAIX,UAAU,EAEV,KAAK,EAGL,UAAU,EACV,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAwB,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;;;GAKG;AACH,KAAK,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAE9D;;;GAGG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C;;GAEG;AACH,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;aAGT,GAAG,EAAE,UAAU;aACf,mBAAmB,EAAE,wBAAwB;aAC7C,IAAI,EAAE,cAAc;IAJtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA+B;IACtD,OAAO;WAMM,IAAI,CACf,GAAG,EAAE,UAAU,EACf,mBAAmB,EAAE,wBAAwB,EAC7C,IAAI,CAAC,EAAE,WAAW,CAAC,QAAQ;IAO7B,MAAM,CAAC,GAAG,IAAI,gBAAgB;CAG/B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,mBAAmB,EAAE,wBAAwB,EAC7C,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAqFnB;AAED;;;;GAIG;AACH,iBAAS,UAAU,CACjB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CACP,UAAU,EACV;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB,CACF,CAyCA;AAED;;;;GAIG;AACH,iBAAe,eAAe,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,WAAW,EACnB,EAAE,EAAE,MAAM,GAAG,IAAI,GAChB,OAAO,CACR,MAAM,CAAC,UAAU,EAAE;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAC3E,CA4DA;AA2DD;;;GAGG;AACH,iBAAe,cAAc,CAC3B,OAAO,EAAE,WAAW,EACpB,gBAAgB,EAAE,gBAAgB,EAClC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAkD9B;AA+HD;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAC"}
|