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.
Files changed (46) hide show
  1. package/esm/graphql.js +87 -64
  2. package/esm/index.js +1 -0
  3. package/esm/package.json +1 -1
  4. package/esm/remult-fastify.js +3 -0
  5. package/esm/remult-hapi.js +3 -0
  6. package/esm/remult-knex/index.js +22 -5
  7. package/esm/remult-next.js +8 -0
  8. package/esm/remult-nuxt.js +3 -0
  9. package/esm/remult-sveltekit.js +63 -42
  10. package/esm/server/expressBridge.js +25 -0
  11. package/esm/server/get-remult-admin-html.js +3 -0
  12. package/esm/server/remult-admin.js +92 -0
  13. package/esm/src/remult-static.js +2 -1
  14. package/esm/src/remult3/Fields.js +7 -1
  15. package/esm/src/remult3/RepositoryImplementation.js +9 -5
  16. package/esm/src/remult3/remult3.js +47 -12
  17. package/esm/src/server-action.js +16 -11
  18. package/esm/src/sort.js +1 -1
  19. package/graphql.js +154 -99
  20. package/index.d.ts +1 -0
  21. package/index.js +3 -1
  22. package/internals.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/remult-fastify.js +3 -0
  25. package/remult-hapi.js +3 -0
  26. package/remult-knex/index.js +73 -49
  27. package/remult-next.js +8 -0
  28. package/remult-nuxt.js +3 -0
  29. package/remult-sveltekit.d.ts +5 -1
  30. package/remult-sveltekit.js +85 -56
  31. package/server/expressBridge.d.ts +8 -0
  32. package/server/expressBridge.js +39 -0
  33. package/server/get-remult-admin-html.d.ts +1 -0
  34. package/server/get-remult-admin-html.js +7 -0
  35. package/server/remult-admin.d.ts +36 -0
  36. package/server/remult-admin.js +121 -0
  37. package/src/remult-static.js +2 -1
  38. package/src/remult3/Fields.d.ts +1 -0
  39. package/src/remult3/Fields.js +7 -1
  40. package/src/remult3/RepositoryImplementation.d.ts +9 -2
  41. package/src/remult3/RepositoryImplementation.js +9 -5
  42. package/src/remult3/remult3.js +47 -12
  43. package/src/remult3/repository-internals.d.ts +6 -0
  44. package/src/server-action.d.ts +2 -2
  45. package/src/server-action.js +16 -11
  46. 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
+ }
@@ -19,7 +19,8 @@ let x = {
19
19
  },
20
20
  captionTransformer: undefined,
21
21
  };
22
- if (process.env['IGNORE_GLOBAL_REMULT_IN_TESTS'] ||
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 = Reflect.getMetadata('design:type', target, key);
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
- let repo = rel.toRepo;
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
- else {
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
- //p1 - enum column
8
- //p1 - I think there should be a way to throw a forbidden exception
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
- //p1 - more column types
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
- //p1 - write doc about controlling updates of rows...
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
- //p1 - add docs for apply changes
22
- //p1 - when subscribe is forbidden - the query still runs after the renew process
23
- //p1 - 'update tasks set where id = $1
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
- //p1 - doc this:
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
+ */
@@ -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
- if (target.prototype !== undefined) {
123
- var types = Reflect.getMetadata('design:paramtypes', target, key);
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 = options.paramTypes;
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, types, options, (args) => originalMethod.apply(undefined, args));
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(types, args);
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(types, d.args, remult, remult.dataProvider, res);
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(types, args);
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
  }