remult 0.25.5-exp.2 → 0.25.5-exp.20

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 (41) hide show
  1. package/esm/graphql.js +87 -64
  2. package/esm/package.json +1 -1
  3. package/esm/remult-fastify.js +3 -0
  4. package/esm/remult-hapi.js +3 -0
  5. package/esm/remult-knex/index.js +22 -5
  6. package/esm/remult-next.js +8 -0
  7. package/esm/remult-nuxt.js +3 -0
  8. package/esm/remult-sveltekit.js +9 -0
  9. package/esm/server/expressBridge.js +25 -0
  10. package/esm/server/get-remult-admin-html.js +3 -0
  11. package/esm/server/remult-admin.js +92 -0
  12. package/esm/src/remult-static.js +2 -1
  13. package/esm/src/remult3/Fields.js +4 -1
  14. package/esm/src/remult3/RepositoryImplementation.js +9 -5
  15. package/esm/src/remult3/remult3.js +45 -12
  16. package/esm/src/server-action.js +16 -11
  17. package/esm/src/sort.js +1 -1
  18. package/graphql.js +154 -99
  19. package/internals.d.ts +1 -1
  20. package/package.json +1 -1
  21. package/remult-fastify.js +3 -0
  22. package/remult-hapi.js +3 -0
  23. package/remult-knex/index.js +73 -49
  24. package/remult-next.js +8 -0
  25. package/remult-nuxt.js +3 -0
  26. package/remult-sveltekit.js +9 -0
  27. package/server/expressBridge.d.ts +8 -0
  28. package/server/expressBridge.js +39 -0
  29. package/server/get-remult-admin-html.d.ts +1 -0
  30. package/server/get-remult-admin-html.js +7 -0
  31. package/server/remult-admin.d.ts +36 -0
  32. package/server/remult-admin.js +121 -0
  33. package/src/remult-static.js +2 -1
  34. package/src/remult3/Fields.js +4 -1
  35. package/src/remult3/RepositoryImplementation.d.ts +9 -2
  36. package/src/remult3/RepositoryImplementation.js +9 -5
  37. package/src/remult3/remult3.js +45 -12
  38. package/src/remult3/repository-internals.d.ts +6 -0
  39. package/src/server-action.d.ts +2 -2
  40. package/src/server-action.js +16 -11
  41. package/src/sort.js +1 -1
package/esm/graphql.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { getRelationFieldInfo } from './internals.js';
2
- import { Remult, remult } from './index.js';
2
+ import { CompoundIdField, Remult, remult } from './index.js';
3
3
  import { DataApi } from './src/data-api.js';
4
+ import { getRepositoryInternals } from './src/remult3/repository-internals.js';
4
5
  const v2ConnectionAndPagination = false;
5
6
  const andImplementation = false;
6
7
  let _removeComments = false;
@@ -151,6 +152,12 @@ export function remultGraphql(options) {
151
152
  });
152
153
  };
153
154
  };
155
+ const handleRequestWithDataApiContextBasedOnRepo = (repo, work) => {
156
+ return createResultPromise(async (response, setResult, arg1, req) => {
157
+ const dApi = new DataApi(repo, remult);
158
+ await work(dApi, response, setResult, arg1, repo.metadata);
159
+ });
160
+ };
154
161
  const handleRequestWithDataApiContext = (work) => {
155
162
  return createResultPromise(async (response, setResult, arg1, req) => {
156
163
  const remult = await options.getRemultFromRequest(req);
@@ -219,7 +226,8 @@ export function remultGraphql(options) {
219
226
  await dApi.get({
220
227
  ...response,
221
228
  success: (y) => {
222
- currentType.query.resultProcessors.forEach((z) => z(y));
229
+ const orig = { ...y };
230
+ currentType.query.resultProcessors.forEach((z) => z(y, orig));
223
231
  setResult(y);
224
232
  },
225
233
  }, arg1.id);
@@ -269,29 +277,7 @@ export function remultGraphql(options) {
269
277
  });
270
278
  }
271
279
  root[key] = handleRequestWithDataApiContext(async (dApi, response, setResult, arg1, meta) => {
272
- setResult({
273
- [itemsKey]: createResultPromise(async (response, setResult) => {
274
- await dApi.getArray({
275
- ...response,
276
- success: (x) => {
277
- setResult(x.map((y) => {
278
- currentType.query.resultProcessors.forEach((z) => z(y));
279
- return y;
280
- }));
281
- },
282
- }, {
283
- get: bridgeQueryOptionsToDataApiGet(arg1),
284
- }, translateWhereToRestBody(meta.fields, arg1));
285
- }),
286
- [totalCountKey]: createResultPromise(async (response, setResult) => {
287
- await dApi.count({
288
- ...response,
289
- success: (x) => setResult(x.count),
290
- }, {
291
- get: bridgeQueryOptionsToDataApiGet(arg1),
292
- }, translateWhereToRestBody(meta.fields, arg1));
293
- }),
294
- });
280
+ connectionImplementation(setResult, itemsKey, createResultPromise, dApi, currentType, arg1, meta, totalCountKey);
295
281
  });
296
282
  resolversQuery[key] = (origItem, args, req, gqlInfo) => {
297
283
  return root[key](args, req, gqlInfo);
@@ -326,7 +312,8 @@ export function remultGraphql(options) {
326
312
  await dApi.post({
327
313
  ...response,
328
314
  created: (y) => {
329
- currentType.query.resultProcessors.forEach((z) => z(y));
315
+ const orig = { ...y };
316
+ currentType.query.resultProcessors.forEach((z) => z(y, orig));
330
317
  setResult({
331
318
  [toCamelCase(getMetaType(meta))]: y,
332
319
  });
@@ -360,7 +347,8 @@ export function remultGraphql(options) {
360
347
  await dApi.put({
361
348
  ...response,
362
349
  success: (y) => {
363
- currentType.query.resultProcessors.forEach((z) => z(y));
350
+ const orig = { ...y };
351
+ currentType.query.resultProcessors.forEach((z) => z(y, orig));
364
352
  setResult({
365
353
  [toCamelCase(getMetaType(meta))]: y,
366
354
  });
@@ -381,9 +369,12 @@ export function remultGraphql(options) {
381
369
  });
382
370
  currentType.mutation.delete.payload = upsertTypes(deletePayload);
383
371
  const deletedResultKey = `id`;
372
+ const type = meta.idMetadata.field instanceof CompoundIdField
373
+ ? 'String'
374
+ : getGraphqlBaseType(meta.idMetadata.field);
384
375
  currentType.mutation.delete.payload.fields.push({
385
376
  key: deletedResultKey,
386
- value: 'ID',
377
+ value: `${type}`,
387
378
  }, argErrorDetail, argClientMutationId);
388
379
  root[deleteResolverKey] = handleMutationWithErrors(async (dApi, response, setResult, arg1, req) => {
389
380
  await dApi.delete({
@@ -400,21 +391,7 @@ export function remultGraphql(options) {
400
391
  const ri = getRelationFieldInfo(f);
401
392
  if (f.options.includeInApi === false)
402
393
  continue;
403
- let type = 'String';
404
- switch (f.valueType) {
405
- case Boolean:
406
- type = 'Boolean';
407
- break;
408
- case Number:
409
- {
410
- if (f.valueConverter?.fieldTypeInDb === 'integer' ||
411
- f.valueConverter?.fieldTypeInDb === 'autoincrement')
412
- type = 'Int';
413
- else
414
- type = 'Float';
415
- }
416
- break;
417
- }
394
+ const type = getGraphqlBaseType(f);
418
395
  currentType.query.resultProcessors.push((r) => {
419
396
  r[nodeIdKey] = () => getMetaType(meta) + ':' + meta.idMetadata.getId(r);
420
397
  });
@@ -425,6 +402,9 @@ export function remultGraphql(options) {
425
402
  const refType = ri.toEntity;
426
403
  ref = entities.find((i) => i.entityType === refType);
427
404
  }
405
+ if (!ref) {
406
+ throw new Error(`Entity "${ri.toEntity.name}" that is used by the relation "${f.key}" in "${meta.entityType.name}" was not found in the 'entities' array.`);
407
+ }
428
408
  notARealField = ri.type === 'toOne' || ri.type === 'toMany';
429
409
  const refKey = ref.key;
430
410
  switch (ri.type) {
@@ -435,19 +415,19 @@ export function remultGraphql(options) {
435
415
  value: `${getMetaType(ref)}${f.allowNull ? '' : '!'}`,
436
416
  comment: f.caption,
437
417
  });
438
- currentType.query.resultProcessors.push((r) => {
439
- const val = r[ri.type == 'reference' ? f.key : f.options.field]; //p1 - fix
440
- if (val === null || val === undefined)
441
- return null;
418
+ currentType.query.resultProcessors.push((r, orig) => {
442
419
  r[f.key] = async (args, req, gqlInfo) => {
443
- const queryResult = await (await root[refKey]({
444
- ...args.where,
445
- where: { id: { eq: val } },
446
- options: { limit: 1 },
447
- }, req, gqlInfo)).items();
448
- if (queryResult.length > 0)
449
- return queryResult[0];
450
- return null;
420
+ const remult = await options.getRemultFromRequest(req);
421
+ const myRepo = remult.repo(meta.entityType);
422
+ const item = myRepo.fromJson(orig);
423
+ let { toRepo, returnNull, returnUndefined } = getRepositoryInternals(myRepo).getFocusedRelationRepo(myRepo.fields.find(f), item);
424
+ if (returnNull || returnUndefined)
425
+ return null;
426
+ const result = await handleRequestWithDataApiContextBasedOnRepo(toRepo, async (dApi, response, setResult, arg1, meta) => {
427
+ return await connectionImplementation(setResult, itemsKey, createResultPromise, dApi, upsertTypes(getMetaType(ref)), arg1, meta, totalCountKey);
428
+ })({ ...args, options: { limit: 1 } }, req);
429
+ const resultItems = await result.items();
430
+ return resultItems[0];
451
431
  };
452
432
  });
453
433
  break;
@@ -460,16 +440,15 @@ export function remultGraphql(options) {
460
440
  order: 10,
461
441
  comment: `List all \`${getMetaType(meta)}\` of \`${refKey}\``,
462
442
  });
463
- currentType.query.resultProcessors.push((r) => {
464
- const val = r.id;
443
+ currentType.query.resultProcessors.push((r, y) => {
465
444
  r[f.key] = async (args, req, gqlInfo) => {
466
- return await root[ref.key]({
467
- where: {
468
- ...args.where,
469
- [f.options.field /*//p1 to fix*/]: { eq: val },
470
- },
471
- options: { ...args.limit, ...args.page, ...args.orderBy },
472
- }, req, gqlInfo);
445
+ const remult = await options.getRemultFromRequest(req);
446
+ const myRepo = remult.repo(meta.entityType);
447
+ const item = myRepo.fromJson(y);
448
+ let relRepo = myRepo.relations(item)[f.key];
449
+ return handleRequestWithDataApiContextBasedOnRepo(relRepo, async (dApi, response, setResult, arg1, meta) => {
450
+ return await connectionImplementation(setResult, itemsKey, createResultPromise, dApi, upsertTypes(getMetaType(ref)), arg1, meta, totalCountKey);
451
+ })(args, req);
473
452
  };
474
453
  });
475
454
  }
@@ -678,6 +657,32 @@ export function remultGraphql(options) {
678
657
  .join(`\n\n`)}
679
658
  `,
680
659
  };
660
+ function connectionImplementation(setResult, itemsKey, createResultPromise, dApi, currentType, arg1, meta, totalCountKey) {
661
+ setResult({
662
+ [itemsKey]: createResultPromise(async (response, setResult) => {
663
+ await dApi.getArray({
664
+ ...response,
665
+ success: (x) => {
666
+ setResult(x.map((y) => {
667
+ const orig = { ...y };
668
+ currentType.query.resultProcessors.forEach((z) => z(y, orig));
669
+ return y;
670
+ }));
671
+ },
672
+ }, {
673
+ get: bridgeQueryOptionsToDataApiGet(arg1),
674
+ }, translateWhereToRestBody(meta.fields, arg1));
675
+ }),
676
+ [totalCountKey]: createResultPromise(async (response, setResult) => {
677
+ await dApi.count({
678
+ ...response,
679
+ success: (x) => setResult(x.count),
680
+ }, {
681
+ get: bridgeQueryOptionsToDataApiGet(arg1),
682
+ }, translateWhereToRestBody(meta.fields, arg1));
683
+ }),
684
+ });
685
+ }
681
686
  }
682
687
  function getQueryArgsConnection(key) {
683
688
  const queryArgsConnection = [
@@ -812,6 +817,24 @@ function fieldFormat(field) {
812
817
  }
813
818
  return `${strComment} ${key_value}`;
814
819
  }
820
+ function getGraphqlBaseType(field) {
821
+ let type = 'String';
822
+ switch (field.valueType) {
823
+ case Boolean:
824
+ type = 'Boolean';
825
+ break;
826
+ case Number:
827
+ {
828
+ if (field.valueConverter?.fieldTypeInDb === 'integer' ||
829
+ field.valueConverter?.fieldTypeInDb === 'autoincrement')
830
+ type = 'Int';
831
+ else
832
+ type = 'Float';
833
+ }
834
+ break;
835
+ }
836
+ return type;
837
+ }
815
838
  function getMetaType(entityMeta) {
816
839
  return entityMeta.entityType.name;
817
840
  }
package/esm/package.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "type": "module"
2
+ "type": "module"
3
3
  }
@@ -10,6 +10,9 @@ export function remultFastify(options) {
10
10
  end() {
11
11
  res.send();
12
12
  },
13
+ send(html) {
14
+ res.type('text/html').send(html);
15
+ },
13
16
  json(data) {
14
17
  res.send(data);
15
18
  },
@@ -29,6 +29,9 @@ export function remultHapi(options) {
29
29
  end() {
30
30
  res(h.response().code(status));
31
31
  },
32
+ send(html) {
33
+ res(h.response(html).code(status));
34
+ },
32
35
  json(data) {
33
36
  res(h.response(data).code(status));
34
37
  },
@@ -20,7 +20,7 @@ export class KnexDataProvider {
20
20
  return r.knex;
21
21
  }
22
22
  getEntityDataProvider(entity) {
23
- if (!supportsJson(this.knex))
23
+ if (!supportsJsonLoadingAndSaving(this.knex))
24
24
  for (const f of entity.fields.toArray()) {
25
25
  if (f.valueConverter.fieldTypeInDb === 'json') {
26
26
  //@ts-ignore
@@ -31,6 +31,17 @@ export class KnexDataProvider {
31
31
  };
32
32
  }
33
33
  }
34
+ else if (this.knex.client.config.client === 'mysql2') {
35
+ for (const f of entity.fields.toArray()) {
36
+ if (f.valueConverter.fieldTypeInDb === 'json') {
37
+ //@ts-ignore
38
+ f.valueConverter = {
39
+ ...f.valueConverter,
40
+ toDb: ValueConverters.JsonString.toDb,
41
+ };
42
+ }
43
+ }
44
+ }
34
45
  return new KnexEntityDataProvider(entity, this.knex);
35
46
  }
36
47
  async transaction(action) {
@@ -367,7 +378,7 @@ export class KnexSchemaBuilder {
367
378
  if (isAutoIncrement(x))
368
379
  b.increments(cols.get(x).name);
369
380
  else {
370
- buildColumn(x, cols.get(x).name, b, supportsJson(this.knex));
381
+ buildColumn(x, cols.get(x).name, b, supportsJsonDataStorage(this.knex));
371
382
  if (x == entity.idMetadata.field)
372
383
  b.primary([cols.get(x).name]);
373
384
  }
@@ -384,7 +395,7 @@ export class KnexSchemaBuilder {
384
395
  let colName = e.$dbNameOf(col);
385
396
  if (!(await this.knex.schema.hasColumn(e.$entityName, colName))) {
386
397
  await logSql(this.knex.schema.alterTable(e.$entityName, (b) => {
387
- buildColumn(col, colName, b, supportsJson(this.knex));
398
+ buildColumn(col, colName, b, supportsJsonDataStorage(this.knex));
388
399
  }));
389
400
  }
390
401
  }
@@ -406,11 +417,17 @@ export class KnexSchemaBuilder {
406
417
  this.knex = knex;
407
418
  }
408
419
  }
409
- function supportsJson(knex) {
420
+ function supportsJsonDataStorage(knex) {
421
+ const client = knex.client.config.client;
422
+ if (client?.includes('sqlite3') || client?.includes('mssql'))
423
+ return false;
424
+ return true;
425
+ }
426
+ function supportsJsonLoadingAndSaving(knex) {
410
427
  const client = knex.client.config.client;
411
428
  if (client?.includes('sqlite3') ||
412
429
  client?.includes('mssql') ||
413
- client?.includes('mysql'))
430
+ client == 'mysql')
414
431
  return false;
415
432
  return true;
416
433
  }
@@ -57,6 +57,7 @@ export function remultNextApp(options) {
57
57
  const response = {
58
58
  end: () => { },
59
59
  json: () => { },
60
+ send: () => { },
60
61
  status: () => {
61
62
  return response;
62
63
  },
@@ -91,6 +92,13 @@ export function remultNextApp(options) {
91
92
  return sseResponse;
92
93
  }
93
94
  if (responseFromRemultHandler) {
95
+ if (responseFromRemultHandler.html)
96
+ return new Response(responseFromRemultHandler.html, {
97
+ status: responseFromRemultHandler.statusCode,
98
+ headers: {
99
+ 'Content-Type': 'text/html',
100
+ },
101
+ });
94
102
  return new Response(JSON.stringify(responseFromRemultHandler.data), {
95
103
  status: responseFromRemultHandler.statusCode,
96
104
  });
@@ -17,6 +17,7 @@ export function remultNuxt(options) {
17
17
  let sse = false;
18
18
  const response = {
19
19
  end: () => { },
20
+ send: () => { },
20
21
  json: () => { },
21
22
  status: () => {
22
23
  return response;
@@ -38,6 +39,8 @@ export function remultNuxt(options) {
38
39
  if (r) {
39
40
  if (r.statusCode !== 200)
40
41
  setResponseStatus(event, r.statusCode);
42
+ if (r.html)
43
+ return r.html;
41
44
  return r.data;
42
45
  }
43
46
  };
@@ -19,6 +19,7 @@ export function remultSveltekit(options) {
19
19
  const response = {
20
20
  end: () => { },
21
21
  json: () => { },
22
+ send: () => { },
22
23
  status: () => {
23
24
  return response;
24
25
  },
@@ -53,8 +54,16 @@ export function remultSveltekit(options) {
53
54
  return sseResponse;
54
55
  }
55
56
  if (responseFromRemultHandler) {
57
+ if (responseFromRemultHandler.html)
58
+ return new Response(responseFromRemultHandler.html, {
59
+ status: responseFromRemultHandler.statusCode,
60
+ headers: {
61
+ 'Content-Type': 'text/html',
62
+ },
63
+ });
56
64
  return new Response(JSON.stringify(responseFromRemultHandler.data), {
57
65
  status: responseFromRemultHandler.statusCode,
66
+ // headers: (await resolve(event)).headers,
58
67
  });
59
68
  }
60
69
  }
@@ -12,6 +12,7 @@ import { Action, classBackendMethodsArray } from '../src/server-action.js';
12
12
  import { serverActionField } from '../src/server-action-info.js';
13
13
  import { initDataProvider } from './initDataProvider.js';
14
14
  import { remultStatic } from '../src/remult-static.js';
15
+ import remultAdminHtml from './remult-admin.js';
15
16
  export function createRemultServerCore(options, serverCoreOptions) {
16
17
  if (!options) {
17
18
  options = {};
@@ -194,6 +195,15 @@ export class RemultServerImplementation {
194
195
  },
195
196
  doWork: undefined,
196
197
  }, r);
198
+ if (this.options.admin) {
199
+ r.route(this.options.rootPath + '/admin*').get(this.process(async (remult, req, res, orig, origResponse) => {
200
+ origResponse.send(remultAdminHtml({
201
+ remult: remult,
202
+ entities: this.options.entities,
203
+ baseUrl: this.options.rootPath + '/admin',
204
+ }));
205
+ }));
206
+ }
197
207
  if (this.options.subscriptionServer instanceof SseSubscriptionServer) {
198
208
  const streamPath = this.options.rootPath + '/' + streamUrl;
199
209
  r.route(streamPath).get(this.process(async (remult, req, res, origReq, origRes) => {
@@ -814,6 +824,7 @@ export class RouteImplementation {
814
824
  this.coreOptions = coreOptions;
815
825
  }
816
826
  map = new Map();
827
+ starRoutes = [];
817
828
  route(path) {
818
829
  //consider using:
819
830
  //* https://raw.githubusercontent.com/cmorten/opine/main/src/utils/pathToRegex.ts
@@ -821,6 +832,8 @@ export class RouteImplementation {
821
832
  let r = path.toLowerCase();
822
833
  let m = new Map();
823
834
  this.map.set(r, m);
835
+ if (path.endsWith('*'))
836
+ this.starRoutes.push({ route: r.substring(0, r.length - 1), handler: m });
824
837
  const route = {
825
838
  get: (h) => {
826
839
  m.set('get', h);
@@ -864,6 +877,11 @@ export class RouteImplementation {
864
877
  gRes.json(data);
865
878
  res({ statusCode: this.statusCode, data });
866
879
  }
880
+ send(html) {
881
+ if (gRes !== undefined)
882
+ gRes.send(html);
883
+ res({ statusCode: this.statusCode, html });
884
+ }
867
885
  status(statusCode) {
868
886
  if (gRes !== undefined)
869
887
  gRes.status(statusCode);
@@ -913,6 +931,13 @@ export class RouteImplementation {
913
931
  }
914
932
  let lowerPath = path.toLowerCase();
915
933
  let m = this.map.get(lowerPath);
934
+ if (!m)
935
+ for (const route of this.starRoutes) {
936
+ if (lowerPath.startsWith(route.route)) {
937
+ m = route.handler;
938
+ break;
939
+ }
940
+ }
916
941
  if (m) {
917
942
  let h = m.get(req.method.toLowerCase());
918
943
  if (h) {