remult 0.25.5-exp.2 → 0.25.5-exp.21
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/esm/graphql.js +87 -64
- package/esm/index.js +1 -0
- package/esm/package.json +1 -1
- package/esm/remult-fastify.js +3 -0
- package/esm/remult-hapi.js +3 -0
- package/esm/remult-knex/index.js +22 -5
- package/esm/remult-next.js +8 -0
- package/esm/remult-nuxt.js +3 -0
- package/esm/remult-sveltekit.js +63 -42
- package/esm/server/expressBridge.js +25 -0
- package/esm/server/get-remult-admin-html.js +3 -0
- package/esm/server/remult-admin.js +92 -0
- package/esm/src/remult-static.js +2 -1
- package/esm/src/remult3/Fields.js +7 -1
- package/esm/src/remult3/RepositoryImplementation.js +9 -5
- package/esm/src/remult3/remult3.js +47 -12
- package/esm/src/server-action.js +16 -11
- package/esm/src/sort.js +1 -1
- package/graphql.js +154 -99
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/internals.d.ts +1 -1
- package/package.json +1 -1
- package/remult-fastify.js +3 -0
- package/remult-hapi.js +3 -0
- package/remult-knex/index.js +73 -49
- package/remult-next.js +8 -0
- package/remult-nuxt.js +3 -0
- package/remult-sveltekit.d.ts +5 -1
- package/remult-sveltekit.js +85 -56
- package/server/expressBridge.d.ts +8 -0
- package/server/expressBridge.js +39 -0
- package/server/get-remult-admin-html.d.ts +1 -0
- package/server/get-remult-admin-html.js +7 -0
- package/server/remult-admin.d.ts +36 -0
- package/server/remult-admin.js +121 -0
- package/src/remult-static.js +2 -1
- package/src/remult3/Fields.d.ts +1 -0
- package/src/remult3/Fields.js +7 -1
- package/src/remult3/RepositoryImplementation.d.ts +9 -2
- package/src/remult3/RepositoryImplementation.js +9 -5
- package/src/remult3/remult3.js +47 -12
- package/src/remult3/repository-internals.d.ts +6 -0
- package/src/server-action.d.ts +2 -2
- package/src/server-action.js +16 -11
- package/src/sort.js +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { CompoundIdField } from '../src/CompoundIdField.js';
|
|
2
|
+
import { getRelationFieldInfo, } from '../src/remult3/relationInfoMember.js';
|
|
3
|
+
import { Filter } from '../src/filter/filter-interfaces.js';
|
|
4
|
+
import { getHtml } from './get-remult-admin-html.js';
|
|
5
|
+
export default function remultAdminHtml(options) {
|
|
6
|
+
let optionsFromServer = { ...options };
|
|
7
|
+
//@ts-ignore
|
|
8
|
+
delete optionsFromServer.entities;
|
|
9
|
+
delete optionsFromServer.remult;
|
|
10
|
+
return getHtml().replace('<!--PLACE_HERE-->', `<script >const entities = ${JSON.stringify(buildEntityInfo(options))}
|
|
11
|
+
const optionsFromServer = ${JSON.stringify(optionsFromServer)}
|
|
12
|
+
</script>`);
|
|
13
|
+
}
|
|
14
|
+
export function buildEntityInfo(options) {
|
|
15
|
+
const entities = [];
|
|
16
|
+
for (const metadata of options.entities.map((e) => options.remult.repo(e).metadata)) {
|
|
17
|
+
let fields = [];
|
|
18
|
+
let relations = [];
|
|
19
|
+
let ids = {};
|
|
20
|
+
if (metadata.idMetadata.field instanceof CompoundIdField) {
|
|
21
|
+
for (const field of metadata.idMetadata.field.fields) {
|
|
22
|
+
ids[field.key] = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else
|
|
26
|
+
ids[metadata.idMetadata.field.key] = true;
|
|
27
|
+
for (const x of metadata.fields.toArray()) {
|
|
28
|
+
if (!x.includedInApi(undefined))
|
|
29
|
+
continue;
|
|
30
|
+
let relation;
|
|
31
|
+
let valFieldKey = x.key;
|
|
32
|
+
const info = getRelationFieldInfo(x);
|
|
33
|
+
if (info) {
|
|
34
|
+
const relInfo = info.getFields();
|
|
35
|
+
const relRepo = options.remult.repo(info.toEntity);
|
|
36
|
+
const where = typeof info.options.findOptions === 'object' &&
|
|
37
|
+
info.options.findOptions.where
|
|
38
|
+
? Filter.entityFilterToJson(relRepo.metadata, info.options.findOptions.where)
|
|
39
|
+
: undefined;
|
|
40
|
+
const idField = relRepo.metadata.idMetadata.field.key;
|
|
41
|
+
if (info.type === 'reference' || info.type === 'toOne') {
|
|
42
|
+
if (info.type == 'toOne') {
|
|
43
|
+
for (const key in relInfo.fields) {
|
|
44
|
+
if (Object.prototype.hasOwnProperty.call(relInfo.fields, key)) {
|
|
45
|
+
const element = relInfo.fields[key];
|
|
46
|
+
valFieldKey = element;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
relation = {
|
|
51
|
+
...relInfo,
|
|
52
|
+
where,
|
|
53
|
+
entityKey: relRepo.metadata.key,
|
|
54
|
+
idField,
|
|
55
|
+
captionField: relRepo.metadata.fields
|
|
56
|
+
.toArray()
|
|
57
|
+
.find((x) => x.key != idField && x.valueType == String)?.key,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else if (info.type === 'toMany') {
|
|
61
|
+
relations.push({
|
|
62
|
+
...relInfo,
|
|
63
|
+
where,
|
|
64
|
+
entityKey: relRepo.metadata.key,
|
|
65
|
+
});
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
fields.push({
|
|
70
|
+
key: x.key,
|
|
71
|
+
valFieldKey,
|
|
72
|
+
caption: x.caption,
|
|
73
|
+
relationToOne: relation,
|
|
74
|
+
type: x.valueConverter.fieldTypeInDb == 'json'
|
|
75
|
+
? 'json'
|
|
76
|
+
: x.valueType === Number
|
|
77
|
+
? 'number'
|
|
78
|
+
: x.valueType === Boolean
|
|
79
|
+
? 'boolean'
|
|
80
|
+
: 'string',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
entities.push({
|
|
84
|
+
key: metadata.key,
|
|
85
|
+
caption: metadata.caption,
|
|
86
|
+
ids,
|
|
87
|
+
fields,
|
|
88
|
+
relations,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return entities;
|
|
92
|
+
}
|
package/esm/src/remult-static.js
CHANGED
|
@@ -19,7 +19,8 @@ let x = {
|
|
|
19
19
|
},
|
|
20
20
|
captionTransformer: undefined,
|
|
21
21
|
};
|
|
22
|
-
if (process
|
|
22
|
+
if ((typeof process !== 'undefined' &&
|
|
23
|
+
process.env['IGNORE_GLOBAL_REMULT_IN_TESTS']) ||
|
|
23
24
|
typeof globalThis[remultStaticKey] === 'undefined') {
|
|
24
25
|
globalThis[remultStaticKey] = x;
|
|
25
26
|
}
|
|
@@ -191,6 +191,9 @@ export function Field(valueType, ...options) {
|
|
|
191
191
|
if (r.maxLength) {
|
|
192
192
|
r.validate = addValidator(r.validate, Validators.maxLength(r.maxLength));
|
|
193
193
|
}
|
|
194
|
+
if (r.minLength) {
|
|
195
|
+
r.validate = addValidator(r.validate, Validators.minLength(r.minLength));
|
|
196
|
+
}
|
|
194
197
|
if (!r.valueType && valueType) {
|
|
195
198
|
r.valueType = valueType();
|
|
196
199
|
}
|
|
@@ -201,7 +204,10 @@ export function Field(valueType, ...options) {
|
|
|
201
204
|
r.dbName = r.key;
|
|
202
205
|
let type = r.valueType;
|
|
203
206
|
if (!type) {
|
|
204
|
-
type =
|
|
207
|
+
type =
|
|
208
|
+
typeof Reflect.getMetadata == 'function'
|
|
209
|
+
? Reflect.getMetadata('design:type', target, key)
|
|
210
|
+
: [];
|
|
205
211
|
r.valueType = type;
|
|
206
212
|
}
|
|
207
213
|
if (!r.target)
|
|
@@ -82,9 +82,7 @@ export class RepositoryImplementation {
|
|
|
82
82
|
const rel = getRelationFieldInfo(field);
|
|
83
83
|
if (!rel)
|
|
84
84
|
throw Error(key + ' is not a relation');
|
|
85
|
-
|
|
86
|
-
let { findOptions, returnNull, returnUndefined } = this.findOptionsBasedOnRelation(rel, field, undefined, item, repo);
|
|
87
|
-
const toRepo = new RepositoryImplementation(repo.entity, repo.remult, repo.dataProvider, repo._info, findOptions);
|
|
85
|
+
const { toRepo, returnNull, returnUndefined } = this.getFocusedRelationRepo(field, item);
|
|
88
86
|
if (rel.type === 'toMany')
|
|
89
87
|
return toRepo;
|
|
90
88
|
else
|
|
@@ -100,6 +98,13 @@ export class RepositoryImplementation {
|
|
|
100
98
|
},
|
|
101
99
|
});
|
|
102
100
|
}
|
|
101
|
+
getFocusedRelationRepo(field, item) {
|
|
102
|
+
const rel = getRelationFieldInfo(field);
|
|
103
|
+
let repo = rel.toRepo;
|
|
104
|
+
let { findOptions, returnNull, returnUndefined } = this.findOptionsBasedOnRelation(rel, field, undefined, item, repo);
|
|
105
|
+
const toRepo = new RepositoryImplementation(repo.entity, repo.remult, repo.dataProvider, repo._info, findOptions);
|
|
106
|
+
return { toRepo, returnNull, returnUndefined };
|
|
107
|
+
}
|
|
103
108
|
__edp;
|
|
104
109
|
get edp() {
|
|
105
110
|
return this.__edp
|
|
@@ -700,7 +705,6 @@ export class RepositoryImplementation {
|
|
|
700
705
|
where: this.metadata.idMetadata.getIdFilter(id),
|
|
701
706
|
}, true);
|
|
702
707
|
}
|
|
703
|
-
/* @internal*/
|
|
704
708
|
async translateWhereToFilter(where) {
|
|
705
709
|
if (!where)
|
|
706
710
|
where = {};
|
|
@@ -1773,7 +1777,7 @@ class EntityFullInfo {
|
|
|
1773
1777
|
else
|
|
1774
1778
|
this.idMetadata.field = r;
|
|
1775
1779
|
}
|
|
1776
|
-
|
|
1780
|
+
if (!this.idMetadata.field) {
|
|
1777
1781
|
if (this.fields['id'])
|
|
1778
1782
|
this.idMetadata.field = this.fields['id'];
|
|
1779
1783
|
else
|
|
@@ -1,29 +1,57 @@
|
|
|
1
1
|
export {};
|
|
2
|
+
/*y1 - workshop
|
|
3
|
+
|
|
4
|
+
* Experiment with doing this with version control - so the students can follow changes
|
|
5
|
+
* Prepare stackblitz
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# influencer:
|
|
9
|
+
* review react summit
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
//y1 - admin url!
|
|
2
13
|
//y1 - number default storage in knex is decimal and it by default has 8,2 - meaning only 999,999 - I think it should be way bigger than that. in firefly we do at least 18 - https://discord.com/channels/975754286384418847/1195162533447876658
|
|
14
|
+
//y1 - talk about the parameter issue with backend methods
|
|
3
15
|
//y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
|
|
16
|
+
//y1 - I think that the tests you've setup cover next app router - I added to the setup, but not sure where else
|
|
17
|
+
//y1 - select data provider per entity https://discord.com/channels/975754286384418847/976006081748807690/1201415305885397003
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
//p1 - Small thing, I get SvelteKitError: Not found: /vite.svg
|
|
21
|
+
//p1 - understand the to many relation for the admin, based on the to one
|
|
22
|
+
//p1 - relation from order details to order gave a compound id info - and it is not true - same for the relation to product
|
|
23
|
+
//p1 - live query with include
|
|
24
|
+
//p1 - new row when there are relations, looks funny (see product)
|
|
25
|
+
//p1 - the + row in the bottom should extend to the full width
|
|
26
|
+
//p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
|
|
27
|
+
|
|
28
|
+
/*y2 -
|
|
29
|
+
@Fields.string<Category>({
|
|
30
|
+
allowApiUpdate: (c) => getEntityRef(c).isNew(),
|
|
31
|
+
})
|
|
32
|
+
Description = ""*/
|
|
4
33
|
//y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
|
|
5
34
|
//y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
|
|
6
35
|
//y2 - how to run a transaction as a user
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//p1 - Write some ctrl g
|
|
36
|
+
//p2 - enum column
|
|
37
|
+
//p2 - I think there should be a way to throw a forbidden exception
|
|
10
38
|
//y2 - get backend methods to work when specifying types for date, and entities as poco's
|
|
11
39
|
//y2 - required
|
|
12
40
|
//y2 - reconsider validators
|
|
13
41
|
//y2 - message for invalid value
|
|
14
42
|
//y2 - message for relation that is missing
|
|
15
|
-
//
|
|
43
|
+
//p2 - more column types
|
|
16
44
|
//p2 - processError in remult express
|
|
17
45
|
//p2 - allow find options preprocessor for api calls, to use for authorization
|
|
18
|
-
//
|
|
46
|
+
//p2 - write doc about controlling updates of rows...
|
|
19
47
|
//p1 - need a way to extract the fields from the relation - for generating relation based sql
|
|
20
48
|
//p1 - when doing this, work on graphql relation with fields not just field
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
49
|
+
//p2 - add docs for apply changes
|
|
50
|
+
//p2 - when subscribe is forbidden - the query still runs after the renew process
|
|
51
|
+
//p2 - 'update tasks set where id = $1
|
|
24
52
|
//y2 - consider multi tenancies
|
|
25
53
|
//y2 - allow api read to also support instance and filter. - problem with promise
|
|
26
|
-
//
|
|
54
|
+
//p2 - doc this:
|
|
27
55
|
/**
|
|
28
56
|
* //p2 - doc this
|
|
29
57
|
remult.apiClient.httpClient = (
|
|
@@ -46,12 +74,12 @@ remult.apiClient.url='localhost:3007/api
|
|
|
46
74
|
*/
|
|
47
75
|
//p1 - remult-admin doesn't handle primary key that has compound column
|
|
48
76
|
//p1 - remult-admin didn't show a update for a table with a uniqua that is numeric
|
|
49
|
-
//p1 - crm demo graphql error
|
|
50
|
-
//p1 - fix multiple instances of remult: the defaultRemult and async local hook - and columnsOfType - all can be tests with a vite project loading `remult-admin`
|
|
51
77
|
//p1 -check if checouknt doesn't respect allow read (I think)
|
|
78
|
+
//p1 - when a tasks table exists in a different schema - we get many errors
|
|
52
79
|
//p1 - allow api update only for new rows
|
|
53
80
|
//p1 - getFields didn't work for kobi in the home component
|
|
54
|
-
//p1 - https://codesandbox.io/p/devbox/demo-sql-expression-wqvj4g
|
|
81
|
+
//p1 - add to docs https://codesandbox.io/p/devbox/demo-sql-expression-wqvj4g
|
|
82
|
+
//y1 - talk about export api is not possible in +server - how did we face that in others, and how do we doc it
|
|
55
83
|
//p2 - document validators
|
|
56
84
|
//p2 - and validators to reference
|
|
57
85
|
//y2 - discuss a default date formatter
|
|
@@ -81,3 +109,10 @@ remult.apiClient.url='localhost:3007/api
|
|
|
81
109
|
//y2 - should enforce integer - currently we probably round / truncate it
|
|
82
110
|
//p1 - adjust react tutorial to esm
|
|
83
111
|
//p1 - write docs on with remult - and usage in init api
|
|
112
|
+
/*y1 - discuss sql stuff:
|
|
113
|
+
https://discord.com/channels/975754286384418847/975754286384418852/1198997156678357043
|
|
114
|
+
https://codesandbox.io/p/devbox/demo-sql-expression-forked-f94p6m?file=%2Fsrc%2Findex.ts%3A21%2C3-21%2C12&workspaceId=c0f7eccf-c151-423b-ac8e-0f0f3e1ad350
|
|
115
|
+
https://codesandbox.io/p/devbox/demo-sql-expression-forked-c2jhyc?file=%2Fsrc%2Findex.ts%3A37%2C50&workspaceId=7e9d0505-ea79-46f1-8973-50cadde4fa96
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
*/
|
package/esm/src/server-action.js
CHANGED
|
@@ -92,7 +92,7 @@ export class myServerAction extends Action {
|
|
|
92
92
|
await doTransaction(remult, async () => {
|
|
93
93
|
if (!remult.isAllowedForInstance(undefined, this.options.allowed))
|
|
94
94
|
throw new ForbiddenError();
|
|
95
|
-
info.args = await prepareReceivedArgs(this.types, info.args, remult, ds, res);
|
|
95
|
+
info.args = await prepareReceivedArgs(this.types(), info.args, remult, ds, res);
|
|
96
96
|
try {
|
|
97
97
|
result.data = await this.originalMethod(info.args);
|
|
98
98
|
}
|
|
@@ -119,14 +119,22 @@ export function BackendMethod(options) {
|
|
|
119
119
|
const originalMethod = descriptor ? descriptor.value : target;
|
|
120
120
|
let result = originalMethod;
|
|
121
121
|
checkTarget(target);
|
|
122
|
-
|
|
123
|
-
var types = Reflect.getMetadata
|
|
122
|
+
function getTypes() {
|
|
123
|
+
var types = typeof Reflect.getMetadata == 'function'
|
|
124
|
+
? Reflect.getMetadata('design:paramtypes', target, key)
|
|
125
|
+
: [];
|
|
124
126
|
if (options.paramTypes)
|
|
125
|
-
types =
|
|
127
|
+
types =
|
|
128
|
+
typeof options.paramTypes === 'function'
|
|
129
|
+
? options.paramTypes()
|
|
130
|
+
: options.paramTypes;
|
|
131
|
+
return types;
|
|
132
|
+
}
|
|
133
|
+
if (target.prototype !== undefined) {
|
|
126
134
|
// if types are undefined - you've forgot to set: "emitDecoratorMetadata":true
|
|
127
|
-
let serverAction = new myServerAction((options?.apiPrefix ? options.apiPrefix + '/' : '') + key,
|
|
135
|
+
let serverAction = new myServerAction((options?.apiPrefix ? options.apiPrefix + '/' : '') + key, () => getTypes(), options, (args) => originalMethod.apply(undefined, args));
|
|
128
136
|
serverAction.doWork = async (args, self, url, http) => {
|
|
129
|
-
args = prepareArgsToSend(
|
|
137
|
+
args = prepareArgsToSend(getTypes(), args);
|
|
130
138
|
if (options.blockUser === false) {
|
|
131
139
|
return await remultStatic.actionInfo.runActionWithoutBlockingUI(async () => (await serverAction.run({ args }, url, http)).data);
|
|
132
140
|
}
|
|
@@ -149,9 +157,6 @@ export function BackendMethod(options) {
|
|
|
149
157
|
else
|
|
150
158
|
return result;
|
|
151
159
|
}
|
|
152
|
-
var types = Reflect.getMetadata('design:paramtypes', target, key);
|
|
153
|
-
if (options.paramTypes)
|
|
154
|
-
types = options.paramTypes;
|
|
155
160
|
let x = remultStatic.classHelpers.get(target.constructor);
|
|
156
161
|
if (!x) {
|
|
157
162
|
x = new ClassHelper();
|
|
@@ -175,7 +180,7 @@ export function BackendMethod(options) {
|
|
|
175
180
|
let remult = req;
|
|
176
181
|
let r;
|
|
177
182
|
await doTransaction(remult, async () => {
|
|
178
|
-
d.args = await prepareReceivedArgs(
|
|
183
|
+
d.args = await prepareReceivedArgs(getTypes(), d.args, remult, remult.dataProvider, res);
|
|
179
184
|
if (remultStatic.allEntities.includes(constructor)) {
|
|
180
185
|
let repo = remult.repo(constructor);
|
|
181
186
|
let y;
|
|
@@ -246,7 +251,7 @@ export function BackendMethod(options) {
|
|
|
246
251
|
}
|
|
247
252
|
},
|
|
248
253
|
doWork: async function (args, self, baseUrl, http) {
|
|
249
|
-
args = prepareArgsToSend(
|
|
254
|
+
args = prepareArgsToSend(getTypes(), args);
|
|
250
255
|
if (remultStatic.allEntities.includes(target.constructor)) {
|
|
251
256
|
let defs = getEntityRef(self);
|
|
252
257
|
await defs.__validateEntity();
|
package/esm/src/sort.js
CHANGED
|
@@ -63,8 +63,8 @@ export class Sort {
|
|
|
63
63
|
};
|
|
64
64
|
if (field) {
|
|
65
65
|
const rel = getRelationFieldInfo(field);
|
|
66
|
-
const op = field.options; //p1 - fix
|
|
67
66
|
if (rel?.type === 'toOne') {
|
|
67
|
+
const op = rel.options;
|
|
68
68
|
if (typeof op.field === 'string') {
|
|
69
69
|
addSegment(entityDefs.fields.find(op.field));
|
|
70
70
|
}
|