cloesce 0.0.4-unstable.9 → 0.0.5-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/ast.d.ts +141 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +30 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +375 -279
- package/dist/extractor/err.d.ts +32 -0
- package/dist/extractor/err.d.ts.map +1 -0
- package/dist/extractor/err.js +138 -0
- package/dist/extractor/extract.d.ts +62 -15
- package/dist/extractor/extract.d.ts.map +1 -1
- package/dist/extractor/extract.js +835 -647
- package/dist/generator.wasm +0 -0
- package/dist/orm.wasm +0 -0
- package/dist/router/crud.d.ts +5 -20
- package/dist/router/crud.d.ts.map +1 -1
- package/dist/router/crud.js +53 -64
- package/dist/router/router.d.ts +153 -95
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +451 -407
- package/dist/router/validator.d.ts +32 -0
- package/dist/router/validator.d.ts.map +1 -0
- package/dist/router/validator.js +193 -0
- package/dist/router/wasm.d.ts +59 -24
- package/dist/router/wasm.d.ts.map +1 -1
- package/dist/router/wasm.js +93 -79
- package/dist/ui/backend.d.ts +224 -180
- package/dist/ui/backend.d.ts.map +1 -1
- package/dist/ui/backend.js +264 -250
- package/dist/ui/client.d.ts +7 -5
- package/dist/ui/client.d.ts.map +1 -1
- package/dist/ui/client.js +2 -7
- package/dist/ui/common.d.ts +120 -0
- package/dist/ui/common.d.ts.map +1 -0
- package/dist/ui/common.js +210 -0
- package/package.json +4 -3
- package/README.md +0 -632
- package/dist/common.d.ts +0 -237
- package/dist/common.d.ts.map +0 -1
- package/dist/common.js +0 -169
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CidlType, CloesceAst } from "../ast";
|
|
2
|
+
import { Either } from "../ui/common";
|
|
3
|
+
import { ConstructorRegistry } from "./router";
|
|
4
|
+
/**
|
|
5
|
+
* Runtime type validation, asserting that the structure of a value follows the
|
|
6
|
+
* correlated CidlType.
|
|
7
|
+
*
|
|
8
|
+
* All values must be defined unless `isPartial` is true.
|
|
9
|
+
*
|
|
10
|
+
* Arrays can be left undefined, which will be interpreted as empty.
|
|
11
|
+
*
|
|
12
|
+
* Types will be instantiated in place.
|
|
13
|
+
*
|
|
14
|
+
* If partial, no child types will be instantaited aside from primitives (as of now, just Dates).
|
|
15
|
+
*
|
|
16
|
+
* Blob types will be assumed to be b64 encoded
|
|
17
|
+
*
|
|
18
|
+
* @returns the instantiated value (if applicable). On error, returns null.
|
|
19
|
+
*/
|
|
20
|
+
export declare class RuntimeValidator {
|
|
21
|
+
private ast;
|
|
22
|
+
private ctorReg;
|
|
23
|
+
constructor(ast: CloesceAst, ctorReg: ConstructorRegistry);
|
|
24
|
+
static validate(
|
|
25
|
+
value: any,
|
|
26
|
+
cidlType: CidlType,
|
|
27
|
+
ast: CloesceAst,
|
|
28
|
+
ctorReg: ConstructorRegistry,
|
|
29
|
+
): Either<null, any>;
|
|
30
|
+
private recurse;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/router/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,UAAU,EAIX,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,MAAM,EAAW,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,OAAO;gBADP,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,mBAAmB;IAGtC,MAAM,CAAC,QAAQ,CACb,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,mBAAmB,GAC3B,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC;IAIpB,OAAO,CAAC,OAAO;CA8KhB"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NO_DATA_SOURCE,
|
|
3
|
+
getNavigationPropertyCidlType,
|
|
4
|
+
isNullableType,
|
|
5
|
+
} from "../ast";
|
|
6
|
+
import { Either, b64ToU8 } from "../ui/common";
|
|
7
|
+
/**
|
|
8
|
+
* Runtime type validation, asserting that the structure of a value follows the
|
|
9
|
+
* correlated CidlType.
|
|
10
|
+
*
|
|
11
|
+
* All values must be defined unless `isPartial` is true.
|
|
12
|
+
*
|
|
13
|
+
* Arrays can be left undefined, which will be interpreted as empty.
|
|
14
|
+
*
|
|
15
|
+
* Types will be instantiated in place.
|
|
16
|
+
*
|
|
17
|
+
* If partial, no child types will be instantaited aside from primitives (as of now, just Dates).
|
|
18
|
+
*
|
|
19
|
+
* Blob types will be assumed to be b64 encoded
|
|
20
|
+
*
|
|
21
|
+
* @returns the instantiated value (if applicable). On error, returns null.
|
|
22
|
+
*/
|
|
23
|
+
export class RuntimeValidator {
|
|
24
|
+
ast;
|
|
25
|
+
ctorReg;
|
|
26
|
+
constructor(ast, ctorReg) {
|
|
27
|
+
this.ast = ast;
|
|
28
|
+
this.ctorReg = ctorReg;
|
|
29
|
+
}
|
|
30
|
+
static validate(value, cidlType, ast, ctorReg) {
|
|
31
|
+
return new RuntimeValidator(ast, ctorReg).recurse(value, cidlType, false);
|
|
32
|
+
}
|
|
33
|
+
recurse(value, cidlType, isPartial) {
|
|
34
|
+
isPartial ||= typeof cidlType !== "string" && "Partial" in cidlType;
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
// We will let arrays be undefined and interpret that as an empty array.
|
|
37
|
+
if (typeof cidlType !== "string" && "Array" in cidlType) {
|
|
38
|
+
return Either.right([]);
|
|
39
|
+
}
|
|
40
|
+
return rightIf(() => value, isPartial === true);
|
|
41
|
+
}
|
|
42
|
+
// TODO: consequences of null checking like this? 'null' is passed in
|
|
43
|
+
// as a string for GET requests
|
|
44
|
+
const nullable = isNullableType(cidlType);
|
|
45
|
+
if (value == null || value === "null") {
|
|
46
|
+
return rightIf(() => null, nullable);
|
|
47
|
+
}
|
|
48
|
+
// Unwrap nullable types
|
|
49
|
+
if (nullable) {
|
|
50
|
+
cidlType = cidlType.Nullable;
|
|
51
|
+
}
|
|
52
|
+
// Primitives
|
|
53
|
+
if (typeof cidlType === "string") {
|
|
54
|
+
switch (cidlType) {
|
|
55
|
+
case "Integer":
|
|
56
|
+
return rightIf(() => Number(value), Number.isInteger(Number(value)));
|
|
57
|
+
case "Real":
|
|
58
|
+
return rightIf(() => Number(value), !Number.isNaN(Number(value)));
|
|
59
|
+
case "Text":
|
|
60
|
+
return rightIf(() => String(value), typeof value === "string");
|
|
61
|
+
case "Boolean": {
|
|
62
|
+
if (typeof value === "boolean") return Either.right(value);
|
|
63
|
+
if (value === "true") return Either.right(true);
|
|
64
|
+
if (value === "false") return Either.right(false);
|
|
65
|
+
return Either.left(null);
|
|
66
|
+
}
|
|
67
|
+
case "DateIso": {
|
|
68
|
+
// Instantiate
|
|
69
|
+
const date = new Date(value);
|
|
70
|
+
return rightIf(() => date, !isNaN(date.getTime()));
|
|
71
|
+
}
|
|
72
|
+
case "Blob": {
|
|
73
|
+
// Instantiate
|
|
74
|
+
return Either.right(b64ToU8(value));
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
return Either.left(null);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Data Sources
|
|
81
|
+
if ("DataSource" in cidlType) {
|
|
82
|
+
const objectName = cidlType.DataSource;
|
|
83
|
+
return rightIf(
|
|
84
|
+
() => value,
|
|
85
|
+
typeof value === "string" &&
|
|
86
|
+
(value === NO_DATA_SOURCE ||
|
|
87
|
+
this.ast.models[objectName]?.data_sources[value] !== undefined),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const objName = getObjectName(cidlType);
|
|
91
|
+
// Models
|
|
92
|
+
if (objName && this.ast.models[objName]) {
|
|
93
|
+
const model = this.ast.models[objName];
|
|
94
|
+
if (!model || typeof value !== "object") return Either.left(null);
|
|
95
|
+
const valueObj = value;
|
|
96
|
+
// Validate + instantiate PK
|
|
97
|
+
{
|
|
98
|
+
const pk = model.primary_key;
|
|
99
|
+
const res = this.recurse(valueObj[pk.name], pk.cidl_type, isPartial);
|
|
100
|
+
if (res.isLeft()) {
|
|
101
|
+
return res;
|
|
102
|
+
}
|
|
103
|
+
value[pk.name] = res.unwrap();
|
|
104
|
+
}
|
|
105
|
+
// Validate + instantiate attributes
|
|
106
|
+
for (let i = 0; i < model.attributes.length; i++) {
|
|
107
|
+
const attr = model.attributes[i];
|
|
108
|
+
const res = this.recurse(
|
|
109
|
+
valueObj[attr.value.name],
|
|
110
|
+
attr.value.cidl_type,
|
|
111
|
+
isPartial,
|
|
112
|
+
);
|
|
113
|
+
if (res.isLeft()) {
|
|
114
|
+
return res;
|
|
115
|
+
}
|
|
116
|
+
value[attr.value.name] = res.unwrap();
|
|
117
|
+
}
|
|
118
|
+
// Validate + instantiate navigation properties
|
|
119
|
+
for (let i = 0; i < model.navigation_properties.length; i++) {
|
|
120
|
+
const nav = model.navigation_properties[i];
|
|
121
|
+
const res = this.recurse(
|
|
122
|
+
valueObj[nav.var_name],
|
|
123
|
+
getNavigationPropertyCidlType(nav),
|
|
124
|
+
isPartial,
|
|
125
|
+
);
|
|
126
|
+
if (res.isLeft()) {
|
|
127
|
+
return res;
|
|
128
|
+
}
|
|
129
|
+
value[nav.var_name] = res.unwrap();
|
|
130
|
+
}
|
|
131
|
+
// Don't instantiate partials
|
|
132
|
+
if (isPartial) {
|
|
133
|
+
return Either.right(value);
|
|
134
|
+
}
|
|
135
|
+
// Instantiate
|
|
136
|
+
return Either.right(Object.assign(new this.ctorReg[objName](), value));
|
|
137
|
+
}
|
|
138
|
+
// Plain old Objects
|
|
139
|
+
if (objName && this.ast.poos[objName]) {
|
|
140
|
+
const poo = this.ast.poos[objName];
|
|
141
|
+
if (!poo || typeof value !== "object") return Either.left(null);
|
|
142
|
+
const valueObj = value;
|
|
143
|
+
// Validate + instantiate attributes
|
|
144
|
+
for (let i = 0; i < poo.attributes.length; i++) {
|
|
145
|
+
const attr = poo.attributes[i];
|
|
146
|
+
const res = this.recurse(
|
|
147
|
+
valueObj[attr.name],
|
|
148
|
+
attr.cidl_type,
|
|
149
|
+
isPartial,
|
|
150
|
+
);
|
|
151
|
+
if (res.isLeft()) {
|
|
152
|
+
return res;
|
|
153
|
+
}
|
|
154
|
+
value[attr.name] = res.unwrap();
|
|
155
|
+
}
|
|
156
|
+
if (isPartial) {
|
|
157
|
+
return Either.right(value);
|
|
158
|
+
}
|
|
159
|
+
// Instantiate
|
|
160
|
+
return Either.right(Object.assign(new this.ctorReg[objName](), value));
|
|
161
|
+
}
|
|
162
|
+
// Arrays
|
|
163
|
+
if ("Array" in cidlType) {
|
|
164
|
+
if (!Array.isArray(value)) {
|
|
165
|
+
return Either.left(null);
|
|
166
|
+
}
|
|
167
|
+
for (let i = 0; i < value.length; i++) {
|
|
168
|
+
const res = this.recurse(value[i], cidlType.Array, isPartial);
|
|
169
|
+
if (res.isLeft()) {
|
|
170
|
+
return res;
|
|
171
|
+
}
|
|
172
|
+
value[i] = res.unwrap();
|
|
173
|
+
}
|
|
174
|
+
return Either.right(value);
|
|
175
|
+
}
|
|
176
|
+
return Either.left(null);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function getObjectName(ty) {
|
|
180
|
+
if (typeof ty === "string") {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
if ("Partial" in ty) {
|
|
184
|
+
return ty.Partial;
|
|
185
|
+
}
|
|
186
|
+
if ("Object" in ty) {
|
|
187
|
+
return ty.Object;
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
function rightIf(value, cond) {
|
|
192
|
+
return cond ? Either.right(value()) : Either.left(null);
|
|
193
|
+
}
|
package/dist/router/wasm.d.ts
CHANGED
|
@@ -1,44 +1,79 @@
|
|
|
1
|
-
import { CidlIncludeTree, CloesceAst
|
|
1
|
+
import { CidlIncludeTree, CloesceAst } from "../ast.js";
|
|
2
2
|
import { IncludeTree } from "../ui/backend.js";
|
|
3
|
+
import { Either } from "../ui/common.js";
|
|
3
4
|
/**
|
|
4
5
|
* WASM ABI
|
|
5
6
|
*/
|
|
6
7
|
export interface OrmWasmExports {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
memory: WebAssembly.Memory;
|
|
9
|
+
get_return_len(): number;
|
|
10
|
+
get_return_ptr(): number;
|
|
11
|
+
set_meta_ptr(ptr: number, len: number): number;
|
|
12
|
+
alloc(len: number): number;
|
|
13
|
+
dealloc(ptr: number, len: number): void;
|
|
14
|
+
map_sql(
|
|
15
|
+
model_name_ptr: number,
|
|
16
|
+
model_name_len: number,
|
|
17
|
+
sql_rows_ptr: number,
|
|
18
|
+
sql_rows_len: number,
|
|
19
|
+
include_tree_ptr: number,
|
|
20
|
+
include_tree_len: number,
|
|
21
|
+
): boolean;
|
|
22
|
+
upsert_model(
|
|
23
|
+
model_name_ptr: number,
|
|
24
|
+
model_name_len: number,
|
|
25
|
+
new_model_ptr: number,
|
|
26
|
+
new_model_len: number,
|
|
27
|
+
include_tree_ptr: number,
|
|
28
|
+
include_tree_len: number,
|
|
29
|
+
): boolean;
|
|
30
|
+
list_models(
|
|
31
|
+
model_name_ptr: number,
|
|
32
|
+
model_name_len: number,
|
|
33
|
+
include_tree_ptr: number,
|
|
34
|
+
include_tree_len: number,
|
|
35
|
+
tag_cte_ptr: number,
|
|
36
|
+
tag_cte_len: number,
|
|
37
|
+
custom_from_ptr: number,
|
|
38
|
+
custom_from_len: number,
|
|
39
|
+
): boolean;
|
|
16
40
|
}
|
|
17
41
|
/**
|
|
18
42
|
* RAII for wasm memory
|
|
19
43
|
*/
|
|
20
44
|
export declare class WasmResource {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
private wasm;
|
|
46
|
+
ptr: number;
|
|
47
|
+
len: number;
|
|
48
|
+
constructor(wasm: OrmWasmExports, ptr: number, len: number);
|
|
49
|
+
free(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Copies a value from TS memory to WASM memory. A subsequent `free` is necessary.
|
|
52
|
+
*/
|
|
53
|
+
static fromString(str: string, wasm: OrmWasmExports): WasmResource;
|
|
30
54
|
}
|
|
31
|
-
export declare function loadOrmWasm(
|
|
55
|
+
export declare function loadOrmWasm(
|
|
56
|
+
ast: CloesceAst,
|
|
57
|
+
wasm?: WebAssembly.Instance,
|
|
58
|
+
): Promise<OrmWasmExports>;
|
|
32
59
|
/**
|
|
33
60
|
* Invokes a WASM ORM function with the provided arguments, handling memory
|
|
34
61
|
* allocation and deallocation.
|
|
35
62
|
*
|
|
36
63
|
* Returns an Either where Left is an error message and Right the raw string result.
|
|
37
64
|
*/
|
|
38
|
-
export declare function invokeOrmWasm(
|
|
65
|
+
export declare function invokeOrmWasm(
|
|
66
|
+
fn: (...args: number[]) => boolean,
|
|
67
|
+
args: WasmResource[],
|
|
68
|
+
wasm: OrmWasmExports,
|
|
69
|
+
): Either<string, string>;
|
|
39
70
|
/**
|
|
40
|
-
* Calls
|
|
71
|
+
* Calls the object relational mapping function to turn a row of SQL records into
|
|
41
72
|
* an instantiated object.
|
|
42
73
|
*/
|
|
43
|
-
export declare function mapSql<T extends object>(
|
|
44
|
-
|
|
74
|
+
export declare function mapSql<T extends object>(
|
|
75
|
+
ctor: new () => T,
|
|
76
|
+
records: Record<string, any>[],
|
|
77
|
+
includeTree: IncludeTree<T> | CidlIncludeTree | null,
|
|
78
|
+
): Either<string, T[]>;
|
|
79
|
+
//# sourceMappingURL=wasm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../../src/router/wasm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../../src/router/wasm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAS,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;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,CA0DrB"}
|
package/dist/router/wasm.js
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
|
-
import { Either } from "../common.js";
|
|
2
1
|
import { RuntimeContainer } from "./router.js";
|
|
3
2
|
// Requires the ORM binary to have been built
|
|
4
3
|
import mod from "../orm.wasm";
|
|
4
|
+
import { Either } from "../ui/common.js";
|
|
5
5
|
/**
|
|
6
6
|
* RAII for wasm memory
|
|
7
7
|
*/
|
|
8
8
|
export class WasmResource {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
wasm;
|
|
10
|
+
ptr;
|
|
11
|
+
len;
|
|
12
|
+
constructor(wasm, ptr, len) {
|
|
13
|
+
this.wasm = wasm;
|
|
14
|
+
this.ptr = ptr;
|
|
15
|
+
this.len = len;
|
|
16
|
+
}
|
|
17
|
+
free() {
|
|
18
|
+
this.wasm.dealloc(this.ptr, this.len);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Copies a value from TS memory to WASM memory. A subsequent `free` is necessary.
|
|
22
|
+
*/
|
|
23
|
+
static fromString(str, wasm) {
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
const bytes = encoder.encode(str);
|
|
26
|
+
const ptr = wasm.alloc(bytes.length);
|
|
27
|
+
const mem = new Uint8Array(wasm.memory.buffer, ptr, bytes.length);
|
|
28
|
+
mem.set(bytes);
|
|
29
|
+
return new this(wasm, ptr, bytes.length);
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
export async function loadOrmWasm(ast, wasm) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
// Load WASM
|
|
34
|
+
const wasmInstance = wasm ?? (await WebAssembly.instantiate(mod));
|
|
35
|
+
const modelMeta = WasmResource.fromString(
|
|
36
|
+
JSON.stringify(ast.models),
|
|
37
|
+
wasmInstance.exports,
|
|
38
|
+
);
|
|
39
|
+
if (wasmInstance.exports.set_meta_ptr(modelMeta.ptr, modelMeta.len) != 0) {
|
|
40
|
+
modelMeta.free();
|
|
41
|
+
throw Error("The WASM Module failed to load due to an invalid CIDL");
|
|
42
|
+
}
|
|
43
|
+
// Intentionally leak `modelMeta`, it should exist for the programs lifetime.
|
|
44
|
+
return wasmInstance.exports;
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
45
47
|
* Invokes a WASM ORM function with the provided arguments, handling memory
|
|
@@ -48,57 +50,69 @@ export async function loadOrmWasm(ast, wasm) {
|
|
|
48
50
|
* Returns an Either where Left is an error message and Right the raw string result.
|
|
49
51
|
*/
|
|
50
52
|
export function invokeOrmWasm(fn, args, wasm) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
let resPtr;
|
|
54
|
+
let resLen;
|
|
55
|
+
try {
|
|
56
|
+
const failed = fn(...args.flatMap((a) => [a.ptr, a.len]));
|
|
57
|
+
resPtr = wasm.get_return_ptr();
|
|
58
|
+
resLen = wasm.get_return_len();
|
|
59
|
+
const result = new TextDecoder().decode(
|
|
60
|
+
new Uint8Array(wasm.memory.buffer, resPtr, resLen),
|
|
61
|
+
);
|
|
62
|
+
return failed ? Either.left(result) : Either.right(result);
|
|
63
|
+
} finally {
|
|
64
|
+
args.forEach((a) => a.free());
|
|
65
|
+
if (resPtr && resLen) wasm.dealloc(resPtr, resLen);
|
|
66
|
+
}
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
|
-
* Calls
|
|
69
|
+
* Calls the object relational mapping function to turn a row of SQL records into
|
|
68
70
|
* an instantiated object.
|
|
69
71
|
*/
|
|
70
72
|
export function mapSql(ctor, records, includeTree) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
73
|
+
const { ast, constructorRegistry, wasm } = RuntimeContainer.get();
|
|
74
|
+
const args = [
|
|
75
|
+
WasmResource.fromString(ctor.name, wasm),
|
|
76
|
+
WasmResource.fromString(JSON.stringify(records), wasm),
|
|
77
|
+
WasmResource.fromString(JSON.stringify(includeTree), wasm),
|
|
78
|
+
];
|
|
79
|
+
const jsonResults = invokeOrmWasm(wasm.map_sql, args, wasm);
|
|
80
|
+
if (jsonResults.isLeft()) return jsonResults;
|
|
81
|
+
const parsed = JSON.parse(jsonResults.value);
|
|
82
|
+
return Either.right(
|
|
83
|
+
parsed.map((obj) =>
|
|
84
|
+
instantiateDepthFirst(obj, ast.models[ctor.name], includeTree),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
// TODO: Lazy instantiation via Proxy?
|
|
88
|
+
function instantiateDepthFirst(m, meta, includeTree) {
|
|
89
|
+
m = Object.assign(new constructorRegistry[meta.name](), m);
|
|
90
|
+
if (!includeTree) {
|
|
91
|
+
return m;
|
|
92
|
+
}
|
|
93
|
+
for (const navProp of meta.navigation_properties) {
|
|
94
|
+
const nestedIncludeTree = includeTree[navProp.var_name];
|
|
95
|
+
if (!nestedIncludeTree) continue;
|
|
96
|
+
const nestedMeta = ast.models[navProp.model_name];
|
|
97
|
+
// One to Many, Many to Many
|
|
98
|
+
if (Array.isArray(m[navProp.var_name])) {
|
|
99
|
+
for (let i = 0; i < m[navProp.var_name].length; i++) {
|
|
100
|
+
m[navProp.var_name][i] = instantiateDepthFirst(
|
|
101
|
+
m[navProp.var_name][i],
|
|
102
|
+
nestedMeta,
|
|
103
|
+
nestedIncludeTree,
|
|
104
|
+
);
|
|
101
105
|
}
|
|
102
|
-
|
|
106
|
+
}
|
|
107
|
+
// One to one
|
|
108
|
+
else if (m[navProp.var_name]) {
|
|
109
|
+
m[navProp.var_name] = instantiateDepthFirst(
|
|
110
|
+
m[navProp.var_name],
|
|
111
|
+
nestedMeta,
|
|
112
|
+
nestedIncludeTree,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
103
115
|
}
|
|
116
|
+
return m;
|
|
117
|
+
}
|
|
104
118
|
}
|