@entity-access/server-pages 1.0.7 → 1.0.10

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.
Files changed (62) hide show
  1. package/dist/ServerPages.d.ts +1 -0
  2. package/dist/ServerPages.d.ts.map +1 -1
  3. package/dist/ServerPages.js +5 -0
  4. package/dist/ServerPages.js.map +1 -1
  5. package/dist/routes/api/entity/get.d.ts +7 -0
  6. package/dist/routes/api/entity/get.d.ts.map +1 -0
  7. package/dist/routes/api/entity/get.js +33 -0
  8. package/dist/routes/api/entity/get.js.map +1 -0
  9. package/dist/routes/api/entity/index.d.ts +11 -0
  10. package/dist/routes/api/entity/index.d.ts.map +1 -0
  11. package/dist/routes/api/entity/index.js +167 -0
  12. package/dist/routes/api/entity/index.js.map +1 -0
  13. package/dist/routes/api/entity/model/get.d.ts +7 -0
  14. package/dist/routes/api/entity/model/get.d.ts.map +1 -0
  15. package/dist/routes/api/entity/model/get.js +23 -0
  16. package/dist/routes/api/entity/model/get.js.map +1 -0
  17. package/dist/routes/api/entity/model.ts/get.d.ts +7 -0
  18. package/dist/routes/api/entity/model.ts/get.d.ts.map +1 -0
  19. package/dist/routes/api/entity/model.ts/get.js +23 -0
  20. package/dist/routes/api/entity/model.ts/get.js.map +1 -0
  21. package/dist/routes/api/entity/query/get.d.ts +6 -0
  22. package/dist/routes/api/entity/query/get.d.ts.map +1 -0
  23. package/dist/routes/api/entity/query/get.js +27 -0
  24. package/dist/routes/api/entity/query/get.js.map +1 -0
  25. package/dist/services/EntityAccessServer.d.ts +19 -0
  26. package/dist/services/EntityAccessServer.d.ts.map +1 -0
  27. package/dist/services/EntityAccessServer.js +77 -0
  28. package/dist/services/EntityAccessServer.js.map +1 -0
  29. package/dist/services/GraphService.d.ts +7 -0
  30. package/dist/services/GraphService.d.ts.map +1 -0
  31. package/dist/services/GraphService.js +72 -0
  32. package/dist/services/GraphService.js.map +1 -0
  33. package/dist/services/IndentedStringWriter.d.ts +12 -0
  34. package/dist/services/IndentedStringWriter.d.ts.map +1 -0
  35. package/dist/services/IndentedStringWriter.js +28 -0
  36. package/dist/services/IndentedStringWriter.js.map +1 -0
  37. package/dist/services/ModelService.d.ts +36 -0
  38. package/dist/services/ModelService.d.ts.map +1 -0
  39. package/dist/services/ModelService.js +188 -0
  40. package/dist/services/ModelService.js.map +1 -0
  41. package/dist/services/StringHelper.d.ts +6 -0
  42. package/dist/services/StringHelper.d.ts.map +1 -0
  43. package/dist/services/StringHelper.js +30 -0
  44. package/dist/services/StringHelper.js.map +1 -0
  45. package/dist/sleep.d.ts +3 -0
  46. package/dist/sleep.d.ts.map +1 -0
  47. package/dist/sleep.js +13 -0
  48. package/dist/sleep.js.map +1 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/ServerPages.ts +6 -0
  52. package/src/routes/api/entity/get.tsx +32 -0
  53. package/src/routes/api/entity/index.tsx +178 -0
  54. package/src/routes/api/entity/model/get.tsx +16 -0
  55. package/src/routes/api/entity/model.ts/get.tsx +16 -0
  56. package/src/routes/api/entity/query/get.ts +19 -0
  57. package/src/services/EntityAccessServer.ts +121 -0
  58. package/src/services/GraphService.ts +86 -0
  59. package/src/services/IndentedStringWriter.ts +33 -0
  60. package/src/services/ModelService.ts +257 -0
  61. package/src/services/StringHelper.ts +31 -0
  62. package/src/sleep.ts +12 -0
@@ -0,0 +1,121 @@
1
+ /* eslint-disable no-console */
2
+ import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
3
+ import EntityContext from "@entity-access/entity-access/dist/model/EntityContext.js";
4
+ import { StringHelper } from "./StringHelper.js";
5
+ import EntityQuery from "@entity-access/entity-access/dist/model/EntityQuery.js";
6
+ import GraphService from "./GraphService.js";
7
+
8
+ export type IQueryMethod = [string, string, ... any[]];
9
+
10
+ const replaceArgs = (code: string, p: any, args: any[]) => {
11
+ let index = 0;
12
+ for (const iterator of args) {
13
+ const name = "p" + index;
14
+ code = StringHelper.replaceAll(code, "@" + index, "p." + name);
15
+ p[name] = iterator;
16
+ index++;
17
+ }
18
+ return StringHelper.replaceAll(code, "Sql_1.Sql", "Sql");
19
+ };
20
+
21
+ export interface IEntityQueryOptions {
22
+ entity: string;
23
+ methods: string | IQueryMethod[];
24
+ start: number;
25
+ size: number;
26
+ split: boolean;
27
+ trace: boolean;
28
+ cache: number;
29
+ count: boolean;
30
+ function: string;
31
+ args: string | any[];
32
+ traceFunc?(text: string);
33
+ };
34
+
35
+ export default class EntityAccessServer {
36
+
37
+ public static async query(db: EntityContext, options: IEntityQueryOptions) {
38
+
39
+ db.verifyFilters = true;
40
+ db.raiseEvents = true;
41
+
42
+ const {
43
+ entity: name,
44
+ start = 0,
45
+ size = 100,
46
+ trace,
47
+ function: queryFunction
48
+ } = options;
49
+ let {
50
+ count = false,
51
+ methods,
52
+ args = "[]"
53
+ } = options;
54
+ const entityClass = SchemaRegistry.classForName(name);
55
+
56
+ if (!entityClass) {
57
+ return;
58
+ }
59
+
60
+ if (typeof methods === "string") {
61
+ methods = JSON.parse(methods);
62
+ }
63
+
64
+ if (typeof args === "string") {
65
+ args = JSON.parse(args);
66
+ }
67
+
68
+ if (typeof count === "string") {
69
+ count = count === "true";
70
+ }
71
+
72
+ const events = db.eventsFor(entityClass, true);
73
+
74
+ let q = queryFunction
75
+ ? events[queryFunction](... args) as EntityQuery<any>
76
+ : events.filter(db.query(entityClass));
77
+
78
+ if (methods) {
79
+ for (const [method, code, ... methodArgs] of methods) {
80
+ const p = {};
81
+ if (method === "include") {
82
+ q = q[method](code);
83
+ continue;
84
+ }
85
+ const arrow = replaceArgs(code, p, methodArgs);
86
+ q = q[method](p, `(p) => ${arrow}`);
87
+ }
88
+ }
89
+
90
+ const oq = q;
91
+
92
+ if (start > 0) {
93
+ q = q.offset(start);
94
+ count = true;
95
+ }
96
+ if (size > 0) {
97
+ q = q.limit(size);
98
+ }
99
+
100
+ if (count) {
101
+ const total = await oq.count();
102
+ if (trace) {
103
+ q = q.trace(console.log);
104
+ }
105
+ return GraphService.prepareGraph({
106
+ total,
107
+ items: await q.toArray()
108
+ });
109
+ }
110
+
111
+ if (trace) {
112
+ q = q.trace(console.log);
113
+ }
114
+ return GraphService.prepareGraph({
115
+ total: 0,
116
+ items: await q.toArray()
117
+ });
118
+
119
+ }
120
+
121
+ }
@@ -0,0 +1,86 @@
1
+ import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
2
+ import ModelService from "./ModelService.js";
3
+
4
+ export default class GraphService {
5
+
6
+ static toGraph = Symbol("toGraph");
7
+
8
+ static appendToGraph = Symbol("toGraph");
9
+
10
+ static prepareGraph(body) {
11
+ const r = this.prepare(body, new Map());
12
+ return r;
13
+ }
14
+
15
+ private static prepare(body: any, visited: Map<any, any>) {
16
+
17
+ if (Array.isArray(body)) {
18
+ const r = [];
19
+ for (const iterator of body) {
20
+ r.push(this.prepare(iterator, visited));
21
+ }
22
+ return r;
23
+ }
24
+
25
+ if(!body) {
26
+ return body;
27
+ }
28
+
29
+ if (typeof body !== "object") {
30
+ return body;
31
+ }
32
+
33
+ if (body instanceof Date) {
34
+ return body;
35
+ }
36
+
37
+ let $id = visited.get(body);
38
+ if ($id) {
39
+ return { $ref: $id };
40
+ }
41
+ $id = visited.size + 1;
42
+ visited.set(body, $id);
43
+
44
+ const appendToGraph = body[this.appendToGraph]?.() ?? {};
45
+
46
+ const copy = {
47
+ $id,
48
+ ... appendToGraph
49
+ };
50
+
51
+ // check constructor
52
+ const { constructor } = Object.getPrototypeOf(body);
53
+ if(constructor !== Object) {
54
+ copy["$type"] = SchemaRegistry.entityNameForClass(constructor);
55
+ }
56
+
57
+ body = body[this.toGraph]?.() ?? body;
58
+ for (const key in body) {
59
+
60
+ if (ModelService.ignore(constructor, key)) {
61
+ continue;
62
+ }
63
+
64
+ if (Object.prototype.hasOwnProperty.call(body, key)) {
65
+ const element = body[key];
66
+ copy[key] = this.prepare(element, visited);
67
+ continue;
68
+ }
69
+ const e = body[key];
70
+ switch(typeof e) {
71
+ case "number":
72
+ case "boolean":
73
+ case "bigint":
74
+ case "string":
75
+ copy[key] = e;
76
+ continue;
77
+ case "object":
78
+ if (e instanceof Date) {
79
+ copy[key] = e;
80
+ }
81
+ continue;
82
+ }
83
+ }
84
+ return copy;
85
+ }
86
+ }
@@ -0,0 +1,33 @@
1
+ export default class IndentedStringWriter {
2
+
3
+ public get indent(): number {
4
+ return this.indention;
5
+ }
6
+ public set indent(value: number) {
7
+ this.indention = value;
8
+ let p = "";
9
+ for (let index = 0; index < value; index++) {
10
+ p += this.indentChar;
11
+ }
12
+ this.prefix = p;
13
+ }
14
+
15
+ private indention: number = 0;
16
+ private prefix = "";
17
+ private content = "";
18
+
19
+ constructor(private indentChar = " ") {
20
+
21
+ }
22
+
23
+ public writeLine(text: string = "") {
24
+ for (const iterator of text.split("\n")) {
25
+ this.content = this.content.concat(this.prefix, iterator.trimStart(), "\n");
26
+ }
27
+ }
28
+
29
+ public toString() {
30
+ return this.content;
31
+ }
32
+
33
+ }
@@ -0,0 +1,257 @@
1
+ import { IColumn, IEntityRelation } from "@entity-access/entity-access/dist/decorators/IColumn.js";
2
+ import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
3
+ import EntityType, { addColumnSymbol } from "@entity-access/entity-access/dist/entity-query/EntityType.js";
4
+ import EntityContext from "@entity-access/entity-access/dist/model/EntityContext.js";
5
+ import IndentedStringWriter from "./IndentedStringWriter.js";
6
+ import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
7
+ import { IClassOf } from "@entity-access/entity-access/dist/decorators/IClassOf.js";
8
+
9
+ const modelProperties = Symbol("modelProperty");
10
+
11
+ interface IModelProperty {
12
+ type?: string;
13
+ name?: string;
14
+ enum?: readonly string[];
15
+ help?: string;
16
+ ignore?: boolean;
17
+ readonly?: boolean;
18
+ }
19
+
20
+ interface IModelProperties {
21
+ [key: string]: IModelProperty;
22
+ }
23
+
24
+ export function JsonProperty(mp: IModelProperty = {}) {
25
+ return (target, name) => {
26
+ mp.type ??= (Reflect as any).getMetadata("design:type", target, name);
27
+ mp.name ??= name;
28
+ (target[modelProperties] ??= {})[name] = mp;;
29
+ };
30
+ }
31
+
32
+ export const IgnoreJsonProperty= JsonProperty({ ignore: true });
33
+
34
+ export const ReadOnlyJsonProperty = JsonProperty({ readonly: true });
35
+
36
+ export interface IEntityKey {
37
+ name: string;
38
+ type: string;
39
+ }
40
+
41
+ export interface IEntityPropertyInfo extends IEntityKey {
42
+ isNullable?: boolean;
43
+ }
44
+
45
+ export interface IEntityNavigationProperty extends IEntityKey {
46
+ isCollection?: boolean;
47
+ }
48
+
49
+ export interface IEntityModel {
50
+ name?: string;
51
+ keys?: IEntityKey[];
52
+ properties?: IEntityPropertyInfo[];
53
+ navigationProperties?: IEntityNavigationProperty[];
54
+ }
55
+
56
+ const columnFrom = (c: IColumn): IEntityPropertyInfo => {
57
+ return {
58
+ name: c.name,
59
+ type: c.type?.name ?? c.type.toString(),
60
+ isNullable: !!c.nullable
61
+ };
62
+ };
63
+
64
+ const relationFrom = (r: IEntityRelation): IEntityNavigationProperty => {
65
+ return {
66
+ name: r.name,
67
+ isCollection: r.isCollection,
68
+ type: r.relatedEntity.entityName
69
+ };
70
+ };
71
+
72
+ const modelFrom = (type: EntityType): IEntityModel => {
73
+ const model: IEntityModel = {
74
+ name: type.entityName,
75
+ keys: type.keys.map(columnFrom),
76
+ properties: type.columns.map(columnFrom),
77
+ navigationProperties: type.relations.map(relationFrom)
78
+ };
79
+ return model;
80
+ };
81
+
82
+ const getJSType = (column: { type?: any, enum?: readonly string[]}) => {
83
+ const { type, enum: enumValues } = column;
84
+ if (enumValues) {
85
+ return enumValues.map((x) => JSON.stringify(x)).join(" | ");
86
+ }
87
+ switch(type) {
88
+ case Number:
89
+ return "number";
90
+ case BigInt:
91
+ return "bigint";
92
+ case String:
93
+ return "string";
94
+ case Boolean:
95
+ return "boolean";
96
+ case Date:
97
+ case DateTime:
98
+ return "DateTime";
99
+ }
100
+ if (typeof type == "function") {
101
+ const mps = type.prototype[modelProperties];
102
+ if (mps) {
103
+ return `{ ${ Object.entries<IModelProperty>(mps).map(([key, p]) => `${key}?: ${getJSType(p)}`).join(";") } }`;
104
+ }
105
+ return type.name;
106
+ }
107
+ return "any";
108
+ };
109
+
110
+ const getDefaults = (column: IColumn): [string, any] => {
111
+ if (column.generated) {
112
+ return;
113
+ }
114
+ if (column.key) {
115
+ return;
116
+ }
117
+ if (column.fkRelation) {
118
+ return;
119
+ }
120
+ const { type, enum: enumValues } = column;
121
+ if (enumValues) {
122
+ return [ column.name, JSON.stringify(enumValues[0])];
123
+ }
124
+ switch(type) {
125
+ case Number:
126
+ return [column.name, "0"];
127
+ case BigInt:
128
+ return [column.name, "0n"];
129
+ case String:
130
+ return [column.name, `""`];
131
+ case Boolean:
132
+ return [column.name, "false"];
133
+ case Date:
134
+ return [column.name, "new DefaultFactory(() => new Date())"];
135
+ case DateTime:
136
+ return [column.name, "new DefaultFactory(() => new DateTime())"];
137
+ }
138
+ return;
139
+ };
140
+
141
+ export default class ModelService {
142
+
143
+ public static ignore(t: IClassOf<any>, key: string) {
144
+ return (t.prototype?.[modelProperties] as IModelProperties)?.[key]?.ignore;
145
+ }
146
+
147
+ public static getModel(context: EntityContext) {
148
+ const model = [] as IEntityModel[];
149
+
150
+ for (const [type] of context.model.sources) {
151
+ const entityType = context.model.getEntityType(type);
152
+ model.push(modelFrom(entityType));
153
+ }
154
+ return model;
155
+ }
156
+
157
+ public static getModelDeclaration(context: EntityContext) {
158
+ const writer = new IndentedStringWriter("\t");
159
+ writer.writeLine(`import DateTime from "@web-atoms/date-time/dist/DateTime";
160
+ import type IClrEntity from "@web-atoms/entity/dist/models/IClrEntity";
161
+ import { ICollection, IGeometry, IModel, Model, DefaultFactory } from "@web-atoms/entity/dist/services/BaseEntityService";
162
+ `);
163
+
164
+ for (const [type] of context.model.sources) {
165
+ const entityType = context.model.getEntityType(type);
166
+
167
+ const entityName = entityType.entityName;
168
+ const name = entityType.typeClass.name;
169
+
170
+ const defaults = [] as string[];
171
+
172
+ writer.writeLine(`export interface I${name} extends IClrEntity {`);
173
+ writer.indent++;
174
+
175
+ const enums = [] as string[];
176
+
177
+ const set = new Set<string>();
178
+
179
+ const keys = [] as string[];
180
+
181
+ const mps = type.prototype[modelProperties] as IModelProperties;
182
+ for (const column of entityType.columns) {
183
+
184
+ if (column.key) {
185
+ keys.push(column.name);
186
+ }
187
+
188
+ set.add(column.name);
189
+
190
+ const jsonProperties = mps?.[column.name];
191
+
192
+ if (jsonProperties?.ignore) {
193
+ continue;
194
+ }
195
+
196
+ const isReadonly = column.generated || jsonProperties?.readonly ? " readonly " : "";
197
+
198
+ if (jsonProperties?.help) {
199
+ writer.writeLine(`/** ${jsonProperties.help} */`);
200
+ }
201
+
202
+ if (column.enum) {
203
+ enums.push(`export const ${name}${column.name[0].toUpperCase()}${column.name.substring(1)}Array = ${JSON.stringify(column.enum.map((x) => ({label: x, value: x})))};`);
204
+ }
205
+ const jsType = getJSType(column);
206
+ if (column.nullable) {
207
+ writer.writeLine(`${isReadonly}${column.name}?: ${jsType} | null;`);
208
+ } else {
209
+ writer.writeLine(`${isReadonly}${column.name}?: ${jsType};`);
210
+ const defs = getDefaults(column);
211
+ if (defs) {
212
+ const [k, v] = defs;
213
+ defaults.push(`${k}: ${v}`);
214
+ }
215
+ }
216
+ }
217
+
218
+ // additional model properties...
219
+ if (mps) {
220
+ for (const [key, property] of Object.entries(mps)) {
221
+ if (set.has(key)) {
222
+ continue;
223
+ }
224
+ const jsType = getJSType(property);
225
+ if (property.help) {
226
+ writer.writeLine(`/** ${property.help} */`);
227
+ }
228
+ writer.writeLine(`${key}?: ${jsType};`);
229
+ }
230
+ }
231
+
232
+ for (const relation of entityType.relations) {
233
+ if (relation.isCollection) {
234
+ writer.writeLine(`${relation.name}?: ICollection<I${relation.relatedTypeClass.name}>;`);
235
+ } else {
236
+ writer.writeLine(`${relation.name}?: I${relation.relatedTypeClass.name};`);
237
+ }
238
+ }
239
+ writer.indent--;
240
+ writer.writeLine(`}`);
241
+
242
+ writer.writeLine();
243
+ if (enums.length) {
244
+ for (const iterator of enums) {
245
+ writer.writeLine(iterator);
246
+ }
247
+ writer.writeLine();
248
+ }
249
+
250
+ writer.writeLine(`export const ${name}: IModel<I${name}> = new Model<I${name}>("${entityName}", ${JSON.stringify(keys)}, { ${defaults.join(",")} });`);
251
+ writer.writeLine();
252
+ }
253
+
254
+ return writer.toString();
255
+ }
256
+
257
+ }
@@ -0,0 +1,31 @@
1
+ export const StringHelper = {
2
+ replaceAll: (text: string, search: string, replace: string) => {
3
+ if (!text || !search) {
4
+ return text;
5
+ }
6
+ let index = 0;
7
+ do {
8
+ index = text.indexOf(search, index);
9
+ if (index === -1) {
10
+ break;
11
+ }
12
+ text = text.substring(0, index) + replace + text.substring(index + search.length);
13
+ } while(true);
14
+ return text;
15
+ },
16
+
17
+ remove00: (text: string) => {
18
+ if (!text) {
19
+ return text;
20
+ }
21
+ return text.replace(/\x00/g, "");
22
+ },
23
+
24
+ extractFloat: (text: string, def: number = 0) => {
25
+ const group = /(\d+)/.exec(text);
26
+ if (group?.length) {
27
+ return parseFloat( group[0]);
28
+ }
29
+ return def;
30
+ }
31
+ };
package/src/sleep.ts ADDED
@@ -0,0 +1,12 @@
1
+ export default function sleep(ms: number, signal?: AbortSignal) {
2
+ if (signal?.aborted) {
3
+ return 1;
4
+ }
5
+ return new Promise<void>((resolve, reject) => {
6
+ const id = setTimeout(resolve, ms);
7
+ signal?.addEventListener("abort", () => {
8
+ clearTimeout(id);
9
+ resolve();
10
+ });
11
+ });
12
+ }