remult 3.0.6-next.0 → 3.0.6

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 (57) hide show
  1. package/esm/graphql.js +3 -3
  2. package/esm/index.js +7 -1
  3. package/esm/remult-d1-http.js +32 -0
  4. package/esm/remult-d1.js +90 -0
  5. package/esm/remult-express.js +4 -1
  6. package/esm/remult-fastify.js +9 -1
  7. package/esm/remult-fresh.js +5 -1
  8. package/esm/remult-hapi.js +7 -6
  9. package/esm/remult-hono.js +16 -14
  10. package/esm/remult-next.js +14 -8
  11. package/esm/remult-nuxt.js +7 -4
  12. package/esm/remult-solid-start.js +9 -6
  13. package/esm/remult-sveltekit.js +11 -8
  14. package/esm/server/index.js +4 -1
  15. package/esm/server/module.js +1 -1
  16. package/esm/server/remult-admin.js +9 -5
  17. package/esm/server/remult-api-server.js +14 -2
  18. package/esm/server/test-api-data-provider.js +4 -1
  19. package/esm/src/CompoundIdField.js +1 -0
  20. package/esm/src/context.js +9 -1
  21. package/esm/src/remult-static.js +1 -1
  22. package/esm/src/remult3/RepositoryImplementation.js +37 -21
  23. package/graphql.js +3 -3
  24. package/index.d.ts +9 -1
  25. package/index.js +9 -2
  26. package/package.json +11 -1
  27. package/remult-d1-http.d.ts +16 -0
  28. package/remult-d1-http.js +46 -0
  29. package/remult-d1.d.ts +27 -0
  30. package/remult-d1.js +128 -0
  31. package/remult-express.js +4 -1
  32. package/remult-fastify.js +6 -1
  33. package/remult-fresh.js +5 -1
  34. package/remult-hapi.js +4 -7
  35. package/remult-hono.js +16 -14
  36. package/remult-next.js +14 -8
  37. package/remult-nuxt.js +7 -4
  38. package/remult-solid-start.js +9 -6
  39. package/remult-sveltekit.js +11 -8
  40. package/server/core.d.ts +1 -1
  41. package/server/index.js +4 -1
  42. package/server/module.js +1 -1
  43. package/server/remult-admin.js +9 -5
  44. package/server/remult-api-server.d.ts +24 -11
  45. package/server/remult-api-server.js +23 -11
  46. package/server/test-api-data-provider.js +4 -1
  47. package/src/CompoundIdField.d.ts +1 -0
  48. package/src/CompoundIdField.js +1 -0
  49. package/src/column-interfaces.d.ts +12 -0
  50. package/src/context.d.ts +15 -2
  51. package/src/context.js +9 -1
  52. package/src/entity.d.ts +5 -0
  53. package/src/remult-static.d.ts +1 -1
  54. package/src/remult-static.js +1 -1
  55. package/src/remult3/RepositoryImplementation.d.ts +23 -17
  56. package/src/remult3/RepositoryImplementation.js +48 -22
  57. package/src/remult3/remult3.d.ts +8 -2
package/esm/graphql.js CHANGED
@@ -479,7 +479,7 @@ export function remultGraphql(options) {
479
479
  currentType.fields.push({
480
480
  key: f.key,
481
481
  value: `${getMetaType(ref)}${f.allowNull ? '' : '!'}`,
482
- comment: f.caption,
482
+ comment: f.label,
483
483
  });
484
484
  currentType.query.resultProcessors.push((r, orig) => {
485
485
  r[f.key] = async (args, req, gqlInfo) => {
@@ -524,7 +524,7 @@ export function remultGraphql(options) {
524
524
  currentType.fields.push({
525
525
  key: f.key,
526
526
  value: `${getMetaType(ref)}${f.allowNull ? '' : '!'}`,
527
- comment: f.caption,
527
+ comment: f.label,
528
528
  });
529
529
  const refKey = ref.key;
530
530
  currentType.query.resultProcessors.push((r) => {
@@ -566,7 +566,7 @@ export function remultGraphql(options) {
566
566
  currentType.fields.push({
567
567
  key: f.key,
568
568
  value: `${type}${f.allowNull ? '' : '!'}`,
569
- comment: f.caption,
569
+ comment: f.label,
570
570
  });
571
571
  }
572
572
  // sorting
package/esm/index.js CHANGED
@@ -1,5 +1,11 @@
1
1
  export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider.js';
2
- export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListInfo, CaptionTransformer, } from './src/remult3/RepositoryImplementation.js';
2
+ export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListInfo, } from './src/remult3/RepositoryImplementation.js';
3
+ import { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
4
+ export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
5
+ /**
6
+ * @deprecated use `LabelTransformer` instead
7
+ */
8
+ export const CaptionTransformer = LabelTransformer;
3
9
  export { Entity } from './src/remult3/entity.js';
4
10
  export { getEntityRef } from './src/remult3/getEntityRef.js';
5
11
  export { Field, Fields, Relations, } from './src/remult3/Fields.js';
@@ -0,0 +1,32 @@
1
+ import Cloudflare from 'cloudflare';
2
+ import { SqlDatabase } from './index.js';
3
+ import { D1DataProvider } from './remult-d1.js';
4
+ export { D1DataProvider };
5
+ export function createD1HttpDataProvider(creds) {
6
+ return new SqlDatabase(new D1DataProvider(new D1HttpClient(creds)));
7
+ }
8
+ export class D1HttpClient {
9
+ d1;
10
+ accountId;
11
+ databaseId;
12
+ constructor({ accountId, databaseId, apiToken }) {
13
+ this.d1 = new Cloudflare({ apiToken }).d1.database;
14
+ this.accountId = accountId;
15
+ this.databaseId = databaseId;
16
+ }
17
+ async execute(sql, params = []) {
18
+ return this.d1
19
+ .query(this.databaseId, {
20
+ sql,
21
+ params: params,
22
+ account_id: this.accountId,
23
+ })
24
+ .then(({ result: pages }) => pages.flatMap((page) => page.results));
25
+ // NOTE: d1 query endpoint returns the result as array of object with keys as column names.
26
+ // We're returning that now because the rest of remult seems to expect that.
27
+ // D1 also has a more efficient raw() endpoint that returns {columns: string[], rows: any[][]} that we may want
28
+ // to use eventually, and write code like fromRowToSql()
29
+ // at https://github.com/tursodatabase/libsql-client-ts/blob/main/packages/libsql-client/src/sqlite3.ts#L398
30
+ // to adapt the data
31
+ }
32
+ }
@@ -0,0 +1,90 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+ import { SqlDatabase, } from './index.js';
3
+ import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
4
+ export function createD1DataProvider(d1) {
5
+ return new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)));
6
+ }
7
+ export class D1DataProvider extends SqliteCoreDataProvider {
8
+ d1;
9
+ /**
10
+ * For production or local d1 using binding
11
+ *
12
+ * const dataProvider = new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)))
13
+ */
14
+ constructor(d1) {
15
+ super(() => new D1Command(this.d1), async () => {
16
+ // afaik: d1 connection doesn't need closing,
17
+ // so this is just a noop
18
+ });
19
+ this.d1 = d1;
20
+ }
21
+ async transaction(action) {
22
+ // As of June 2025, D1 doesn't support transactions
23
+ // Here we simply run the action without wrapping it in a transaction
24
+ // It means queries in action() will be run individually (this is the same decision prisma made)
25
+ await action(this);
26
+ }
27
+ }
28
+ export class D1BindingClient {
29
+ d1;
30
+ /**
31
+ * Simple d1 client that wraps the d1 binding directly
32
+ *
33
+ * const d1 = new D1BindingClient(env.DB)
34
+ */
35
+ constructor(d1) {
36
+ this.d1 = d1;
37
+ }
38
+ async execute(sql, params = []) {
39
+ // https://developers.cloudflare.com/d1/worker-api/d1-database/
40
+ // https://developers.cloudflare.com/d1/worker-api/prepared-statements/
41
+ //
42
+ // Note: see if we should eventually take advantage of the raw() end point too.
43
+ const { results } = await this.d1
44
+ .prepare(sql)
45
+ .bind(...params)
46
+ .run();
47
+ return results;
48
+ }
49
+ }
50
+ class D1Command {
51
+ d1;
52
+ params = [];
53
+ constructor(d1) {
54
+ this.d1 = d1;
55
+ }
56
+ async execute(sql) {
57
+ const results = await this.d1.execute(sql, this.params);
58
+ return new D1SqlResult(results);
59
+ }
60
+ /** @deprecated use `param` instead*/
61
+ addParameterAndReturnSqlToken(val) {
62
+ return this.param(val);
63
+ }
64
+ param(val) {
65
+ let p;
66
+ if (val instanceof Date)
67
+ p = val.valueOf();
68
+ else if (typeof val === 'boolean')
69
+ p = val ? 1 : 0;
70
+ else
71
+ p = val;
72
+ this.params.push(p);
73
+ // According to https://developers.cloudflare.com/d1/worker-api/prepared-statements/
74
+ // only the ordered "?NNN" and anonymous "?" param types are supported (although :AAAA seems to work too)
75
+ const key = `?${this.params.length}`;
76
+ return key;
77
+ }
78
+ }
79
+ class D1SqlResult {
80
+ rows;
81
+ columns;
82
+ constructor(rows = []) {
83
+ this.rows = rows;
84
+ // NOTE: are we guaranteed that when this is reached, rows is not empty?
85
+ this.columns = rows.length === 0 ? [] : Object.keys(rows[0]);
86
+ }
87
+ getColumnKeyInResultForIndexInSelect(index) {
88
+ return this.columns[index];
89
+ }
90
+ }
@@ -13,7 +13,10 @@ export function remultApi(options) {
13
13
  app.use(express.urlencoded({ extended: true, limit: options.bodySizeLimit }));
14
14
  }
15
15
  const server = createRemultServer(options, {
16
- buildGenericRequestInfo: (req) => req,
16
+ buildGenericRequestInfo: (req) => ({
17
+ internal: { ...req, on: req.on },
18
+ public: { headers: new Headers(req.headers) },
19
+ }),
17
20
  getRequestBody: async (req) => req.body,
18
21
  });
19
22
  server.registerRouter(app);
@@ -33,7 +33,15 @@ export function remultApi(options) {
33
33
  return response;
34
34
  }
35
35
  const api = createRemultServer(options, {
36
- buildGenericRequestInfo: (req) => req,
36
+ buildGenericRequestInfo: (req) => ({
37
+ internal: {
38
+ ...req,
39
+ url: req.originalUrl,
40
+ // @ts-ignore
41
+ on: req.on,
42
+ },
43
+ public: { headers: new Headers(req.headers) },
44
+ }),
37
45
  getRequestBody: async (req) => req.body,
38
46
  });
39
47
  const pluginFunction = async (instance, op) => {
@@ -1,7 +1,11 @@
1
1
  import { createRemultServerCore } from './server/remult-api-server.js';
2
2
  export function remultApi(options, response) {
3
3
  const server = createRemultServerCore(options, {
4
- buildGenericRequestInfo: (r) => r,
4
+ buildGenericRequestInfo: (r) => ({
5
+ // @ts-ignore
6
+ internal: { ...r, on: r.on },
7
+ public: { headers: r.headers },
8
+ }),
5
9
  getRequestBody: (req) => req.json(),
6
10
  });
7
11
  return {
@@ -3,13 +3,14 @@ import { PassThrough } from 'stream';
3
3
  export function remultApi(options) {
4
4
  const api = createRemultServer(options, {
5
5
  buildGenericRequestInfo: (req) => ({
6
- method: req.method,
7
- params: req.params,
8
- query: req.query,
9
- url: req.url.pathname,
10
- on: (e, do1) => {
11
- req.raw.req.on('close', do1);
6
+ internal: {
7
+ ...req,
8
+ url: req.url.pathname,
9
+ on: (e, do1) => {
10
+ req.raw.req.on('close', do1);
11
+ },
12
12
  },
13
+ public: { headers: new Headers(req.headers) },
13
14
  }),
14
15
  getRequestBody: async (req) => req.payload,
15
16
  });
@@ -6,22 +6,24 @@ export function remultApi(options) {
6
6
  const api = createRemultServer(options, {
7
7
  buildGenericRequestInfo: (c) => {
8
8
  return {
9
- method: c.req.method,
10
- params: c.req.param(),
11
- query: new Proxy(c.req, {
12
- get: (target, prop) => {
13
- const r = c.req.queries(prop);
14
- if (r?.length == 1)
15
- return r[0];
16
- return r;
9
+ internal: {
10
+ method: c.req.method,
11
+ params: c.req.param(),
12
+ query: new Proxy(c.req, {
13
+ get: (target, prop) => {
14
+ const r = c.req.queries(prop);
15
+ if (r?.length == 1)
16
+ return r[0];
17
+ return r;
18
+ },
19
+ }),
20
+ url: c.req.url,
21
+ on: (e, do1) => {
22
+ ;
23
+ c['_tempOnClose'](() => do1());
17
24
  },
18
- }),
19
- url: c.req.url,
20
- on: (e, do1) => {
21
- ;
22
- c['_tempOnClose'](() => do1());
23
- // c.req.on('close', do1)
24
25
  },
26
+ public: { headers: new Headers(c.req.header()) },
25
27
  };
26
28
  },
27
29
  getRequestBody: async (c) => {
@@ -1,7 +1,10 @@
1
1
  import { createRemultServer } from './server/index.js';
2
2
  export function remultNext(options) {
3
3
  let result = createRemultServer(options, {
4
- buildGenericRequestInfo: (req) => req,
4
+ buildGenericRequestInfo: (req) => ({
5
+ internal: { ...req, on: req.on },
6
+ public: { headers: new Headers(req.headers) },
7
+ }),
5
8
  getRequestBody: async (req) => req.body,
6
9
  });
7
10
  return Object.assign((req, res) => result.handle(req, res).then(() => { }), result, {
@@ -41,14 +44,17 @@ export function remultApi(options) {
41
44
  let result = createRemultServer(options, {
42
45
  getRequestBody: (req) => req.json(),
43
46
  buildGenericRequestInfo: (req) => ({
44
- url: req?.url,
45
- method: req?.method,
46
- on: (e, do1) => {
47
- if (e === 'close') {
48
- ;
49
- req['_tempOnClose'] = do1;
50
- }
47
+ internal: {
48
+ url: req?.url,
49
+ method: req?.method,
50
+ on: (e, do1) => {
51
+ if (e === 'close') {
52
+ ;
53
+ req['_tempOnClose'] = do1;
54
+ }
55
+ },
51
56
  },
57
+ public: { headers: req.headers },
52
58
  }),
53
59
  });
54
60
  const handler = async (req) => {
@@ -2,15 +2,18 @@ import { readBody, setResponseStatus } from 'h3';
2
2
  import { createRemultServer } from './server/index.js';
3
3
  export function remultApi(options) {
4
4
  const result = createRemultServer(options, {
5
- buildGenericRequestInfo: (event) => {
6
- return {
5
+ buildGenericRequestInfo: (event) => ({
6
+ internal: {
7
7
  method: event.node.req.method,
8
8
  url: event.node.req.url,
9
9
  on: (a, b) => event.node.req.on('close', () => {
10
10
  b();
11
11
  }),
12
- };
13
- },
12
+ },
13
+ public: {
14
+ headers: new Headers(event.node.req.headers),
15
+ },
16
+ }),
14
17
  getRequestBody: async (event) => await readBody(event),
15
18
  });
16
19
  const handler = async (event) => {
@@ -3,13 +3,16 @@ import { createRemultServer } from './server/index.js';
3
3
  export function remultApi(options) {
4
4
  let result = createRemultServer(options, {
5
5
  buildGenericRequestInfo: (event) => ({
6
- url: event.request.url,
7
- method: event.request.method,
8
- on: (e, do1) => {
9
- if (e === 'close') {
10
- event.locals['_tempOnClose'] = do1;
11
- }
6
+ internal: {
7
+ url: event.request.url,
8
+ method: event.request.method,
9
+ on: (e, do1) => {
10
+ if (e === 'close') {
11
+ event.locals['_tempOnClose'] = do1;
12
+ }
13
+ },
12
14
  },
15
+ public: { headers: new Headers(event.request.headers) },
13
16
  }),
14
17
  getRequestBody: (event) => event.request.json(),
15
18
  });
@@ -2,14 +2,17 @@ import { createRemultServer } from './server/index.js';
2
2
  export function remultApi(options) {
3
3
  let result = createRemultServer(options, {
4
4
  buildGenericRequestInfo: (event) => ({
5
- url: event.request.url,
6
- method: event.request.method,
7
- on: (e, do1) => {
8
- if (e === 'close') {
9
- ;
10
- event.locals['_tempOnClose'] = do1;
11
- }
5
+ internal: {
6
+ url: event.request.url,
7
+ method: event.request.method,
8
+ on: (e, do1) => {
9
+ if (e === 'close') {
10
+ ;
11
+ event.locals['_tempOnClose'] = do1;
12
+ }
13
+ },
12
14
  },
15
+ public: { headers: event.request.headers },
13
16
  }),
14
17
  getRequestBody: (event) => event.request.json(),
15
18
  });
@@ -65,7 +68,7 @@ export function remultApi(options) {
65
68
  status: responseFromRemultHandler.statusCode,
66
69
  headers: {
67
70
  'Content-Type': 'application/json',
68
- }
71
+ },
69
72
  });
70
73
  return res;
71
74
  }
@@ -9,7 +9,10 @@ export { Module } from './module.js';
9
9
  export function createRemultServer(options, serverCoreOptions) {
10
10
  initAsyncHooks();
11
11
  return createRemultServerCore(options, serverCoreOptions || {
12
- buildGenericRequestInfo: (req) => cast(req, 'method'),
12
+ buildGenericRequestInfo: (req) => ({
13
+ internal: cast(req, 'method'),
14
+ public: { headers: new Headers(req.headers) },
15
+ }),
13
16
  getRequestBody: async (req) => req.body,
14
17
  });
15
18
  }
@@ -11,8 +11,8 @@ export class Module {
11
11
  this.priority = options.priority ?? 0;
12
12
  this.entities = options.entities;
13
13
  this.controllers = options.controllers;
14
- this.initRequest = options.initRequest;
15
14
  this.initApi = options.initApi;
15
+ this.initRequest = options.initRequest;
16
16
  this.modules = options.modules;
17
17
  }
18
18
  }
@@ -8,7 +8,11 @@ export default function remultAdminHtml(options) {
8
8
  return getHtml()
9
9
  .replace('<!--PLACE_HERE_HEAD-->', head)
10
10
  .replace('<!--PLACE_HERE_BODY-->', `<script>
11
- window.optionsFromServer = ${JSON.stringify({ rootPath, requireAuthToken, disableLiveQuery })}
11
+ window.optionsFromServer = ${JSON.stringify({
12
+ rootPath,
13
+ requireAuthToken,
14
+ disableLiveQuery,
15
+ })}
12
16
  </script>`);
13
17
  }
14
18
  export function buildEntityInfo(options) {
@@ -63,7 +67,7 @@ export function buildEntityInfo(options) {
63
67
  where,
64
68
  entityKey: relRepo.metadata.key,
65
69
  key: x.key,
66
- caption: x.caption,
70
+ caption: x.label,
67
71
  });
68
72
  }
69
73
  continue;
@@ -74,7 +78,7 @@ export function buildEntityInfo(options) {
74
78
  readOnly: !x.apiUpdateAllowed(),
75
79
  values: getValueList(x),
76
80
  valFieldKey,
77
- caption: x.caption,
81
+ caption: x.label,
78
82
  relationToOne: relation,
79
83
  inputType: x.inputType,
80
84
  type: x.inputType === InputTypes.json
@@ -95,11 +99,11 @@ export function buildEntityInfo(options) {
95
99
  }
96
100
  if (metadata.apiReadAllowed) {
97
101
  let superKey = metadata.key;
98
- let caption = metadata.caption;
102
+ let caption = metadata.label;
99
103
  const nbOfEntities = entities.filter((e) => e.key === metadata.key).length;
100
104
  if (nbOfEntities > 0) {
101
105
  superKey = metadata.key + '_ext_' + nbOfEntities;
102
- caption = metadata.caption + '*'.repeat(nbOfEntities);
106
+ caption = metadata.label + '*'.repeat(nbOfEntities);
103
107
  }
104
108
  entities.push({
105
109
  superKey,
@@ -339,7 +339,9 @@ export class RemultServerImplementation {
339
339
  }
340
340
  process(what, doNotReuseInitRequest) {
341
341
  return async (req, origRes) => {
342
- const genReq = req ? this.coreOptions.buildGenericRequestInfo(req) : {};
342
+ const { internal: genReq, public: genReqPublic, } = req
343
+ ? this.coreOptions.buildGenericRequestInfo(req)
344
+ : { internal: {}, public: { headers: new Headers() } };
343
345
  if (req) {
344
346
  if (!genReq.query) {
345
347
  genReq.query = req['_tempQuery'];
@@ -358,6 +360,16 @@ export class RemultServerImplementation {
358
360
  if (req) {
359
361
  ;
360
362
  remult.context.request = req;
363
+ remult.context.headers = {
364
+ get: (key) => genReqPublic.headers.get(key) ?? undefined,
365
+ getAll: () => {
366
+ const result = {};
367
+ genReqPublic.headers.forEach((value, key) => {
368
+ result[key] = value;
369
+ });
370
+ return result;
371
+ },
372
+ };
361
373
  remultStatic.asyncContext.setInInitRequest(true);
362
374
  try {
363
375
  let user;
@@ -1109,7 +1121,7 @@ export class RouteImplementation {
1109
1121
  });
1110
1122
  }
1111
1123
  middleware(origReq, res, next) {
1112
- const req = this.coreOptions.buildGenericRequestInfo(origReq);
1124
+ const { internal: req } = this.coreOptions.buildGenericRequestInfo(origReq);
1113
1125
  let theUrl = req.url?.toString() || '';
1114
1126
  if (theUrl.startsWith('/'))
1115
1127
  //next sends a partial url '/api/tasks' and not the full url
@@ -11,7 +11,10 @@ export function TestApiDataProvider(options) {
11
11
  });
12
12
  const server = createRemultServerCore({ ...options, dataProvider: dp }, {
13
13
  getRequestBody: async (req) => req.body,
14
- buildGenericRequestInfo: (req) => req,
14
+ buildGenericRequestInfo: (req) => ({
15
+ internal: req,
16
+ public: { headers: new Headers() },
17
+ }),
15
18
  ignoreAsyncStorage: true,
16
19
  });
17
20
  const lock = new AsyncLock();
@@ -46,6 +46,7 @@ export class CompoundIdField {
46
46
  dbReadOnly = false;
47
47
  isServerExpression = false;
48
48
  key = '';
49
+ label = '';
49
50
  caption = '';
50
51
  inputType = '';
51
52
  dbName = '';
@@ -231,7 +231,15 @@ export class Remult {
231
231
  }
232
232
  /** A helper callback that is called whenever an entity is created. */
233
233
  static entityRefInit;
234
- /** context information that can be used to store custom information that will be disposed as part of the `remult` object */
234
+ /**
235
+ * context that can be used to store custom information that will be disposed as part of the `remult` object.
236
+ *
237
+ * `remult.context` is pre-filled in a framework-agnostic way with:
238
+ * - `headers.get(key: string)` _of request_
239
+ * - `headers.getAll()` _of request_
240
+ *
241
+ * Check out the [extensibility section](/docs/custom-options#enhancing-field-and-entity-definitions-with-custom-options) for more custom options.
242
+ */
235
243
  context = {};
236
244
  /** The api client that will be used by `remult` to perform calls to the `api` */
237
245
  apiClient = {
@@ -19,7 +19,7 @@ let x = {
19
19
  }),
20
20
  },
21
21
  fieldOptionsEnricher: undefined,
22
- captionTransformer: undefined,
22
+ labelTransformer: undefined,
23
23
  defaultDataProvider: () => undefined,
24
24
  };
25
25
  if ((typeof process !== 'undefined' &&