remult 3.0.6-next.1 → 3.0.7-next.0

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/README.md +2 -2
  2. package/async-hooks.d.ts +26 -1
  3. package/async-hooks.js +31 -2
  4. package/esm/async-hooks.js +31 -1
  5. package/esm/graphql.js +3 -3
  6. package/esm/index.js +7 -1
  7. package/esm/remult-d1-http.js +32 -0
  8. package/esm/remult-d1.js +90 -0
  9. package/esm/remult-express.js +13 -4
  10. package/esm/server/get-remult-admin-html.js +1 -1
  11. package/esm/server/initAsyncHooks.js +0 -25
  12. package/esm/server/remult-admin.js +9 -5
  13. package/esm/src/CompoundIdField.js +1 -0
  14. package/esm/src/remult-static.js +2 -1
  15. package/esm/src/remult3/Fields.js +49 -30
  16. package/esm/src/remult3/IdEntity.js +1 -1
  17. package/esm/src/remult3/RepositoryImplementation.js +37 -21
  18. package/esm/src/remult3/entity.js +1 -1
  19. package/graphql.js +3 -3
  20. package/index.d.ts +9 -1
  21. package/index.js +9 -2
  22. package/package.json +15 -3
  23. package/remult-d1-http.d.ts +16 -0
  24. package/remult-d1-http.js +46 -0
  25. package/remult-d1.d.ts +27 -0
  26. package/remult-d1.js +128 -0
  27. package/remult-express.js +13 -4
  28. package/server/get-remult-admin-html.js +1 -1
  29. package/server/initAsyncHooks.d.ts +0 -25
  30. package/server/initAsyncHooks.js +0 -25
  31. package/server/remult-admin.js +9 -5
  32. package/server/remult-api-server.d.ts +1 -1
  33. package/src/CompoundIdField.d.ts +1 -0
  34. package/src/CompoundIdField.js +1 -0
  35. package/src/column-interfaces.d.ts +16 -3
  36. package/src/entity.d.ts +5 -0
  37. package/src/remult-static.d.ts +2 -1
  38. package/src/remult-static.js +2 -1
  39. package/src/remult3/Fields.d.ts +34 -4
  40. package/src/remult3/Fields.js +50 -40
  41. package/src/remult3/IdEntity.js +1 -1
  42. package/src/remult3/RepositoryImplementation.d.ts +23 -17
  43. package/src/remult3/RepositoryImplementation.js +48 -22
  44. package/src/remult3/entity.d.ts +1 -1
  45. package/src/remult3/entity.js +1 -1
  46. package/src/remult3/remult3.d.ts +8 -2
package/README.md CHANGED
@@ -76,7 +76,7 @@ import { Entity, Fields } from 'remult'
76
76
  allowApiCrud: true,
77
77
  })
78
78
  export class Product {
79
- @Fields.cuid()
79
+ @Fields.id()
80
80
  id = ''
81
81
 
82
82
  @Fields.string()
@@ -166,7 +166,7 @@ import { Entity, Fields, Validators } from 'remult'
166
166
  allowApiCrud: true,
167
167
  })
168
168
  export class Product {
169
- @Fields.cuid()
169
+ @Fields.id()
170
170
  id = ''
171
171
 
172
172
  @Fields.string({
package/async-hooks.d.ts CHANGED
@@ -1 +1,26 @@
1
- export { initAsyncHooks } from './server/initAsyncHooks.js';
1
+ /**
2
+ * Initializes and enables async context tracking for the server.
3
+ *
4
+ * This should be called before handling any incoming requests or calling `withRemult()`.
5
+ * @example
6
+ * import { remult, repo, withRemult } from 'remult';
7
+ * import { initAsyncHooks } from 'remult/async-hooks';
8
+ *
9
+ * import { Task } from './entities/Task.js';
10
+ *
11
+ * initAsyncHooks();
12
+ *
13
+ * // Thx to the `initAsyncHooks` above,
14
+ * // we have isolated async contexts with multiple `withRemult()`,
15
+ * // without needing to initialize a `remultApi` all the time!
16
+ * withRemult(async () => {
17
+ * remult.user = { id: '42' };
18
+ * repo(Task).find()
19
+ * });
20
+ *
21
+ * withRemult(async () => {
22
+ * remult.user = { id: '21' };
23
+ * repo(Task).find()
24
+ * });
25
+ */
26
+ export declare function initAsyncHooks(): void;
package/async-hooks.js CHANGED
@@ -1,5 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initAsyncHooks = void 0;
3
+ exports.initAsyncHooks = initAsyncHooks;
4
+ var context_js_1 = require("./src/context.js");
4
5
  var initAsyncHooks_js_1 = require("./server/initAsyncHooks.js");
5
- Object.defineProperty(exports, "initAsyncHooks", { enumerable: true, get: function () { return initAsyncHooks_js_1.initAsyncHooks; } });
6
+ /**
7
+ * Initializes and enables async context tracking for the server.
8
+ *
9
+ * This should be called before handling any incoming requests or calling `withRemult()`.
10
+ * @example
11
+ * import { remult, repo, withRemult } from 'remult';
12
+ * import { initAsyncHooks } from 'remult/async-hooks';
13
+ *
14
+ * import { Task } from './entities/Task.js';
15
+ *
16
+ * initAsyncHooks();
17
+ *
18
+ * // Thx to the `initAsyncHooks` above,
19
+ * // we have isolated async contexts with multiple `withRemult()`,
20
+ * // without needing to initialize a `remultApi` all the time!
21
+ * withRemult(async () => {
22
+ * remult.user = { id: '42' };
23
+ * repo(Task).find()
24
+ * });
25
+ *
26
+ * withRemult(async () => {
27
+ * remult.user = { id: '21' };
28
+ * repo(Task).find()
29
+ * });
30
+ */
31
+ function initAsyncHooks() {
32
+ (0, initAsyncHooks_js_1.initAsyncHooks)();
33
+ context_js_1.RemultAsyncLocalStorage.enable();
34
+ }
@@ -1 +1,31 @@
1
- export { initAsyncHooks } from './server/initAsyncHooks.js';
1
+ import { RemultAsyncLocalStorage } from './src/context.js';
2
+ import { initAsyncHooks as _initAsyncHooks } from './server/initAsyncHooks.js';
3
+ /**
4
+ * Initializes and enables async context tracking for the server.
5
+ *
6
+ * This should be called before handling any incoming requests or calling `withRemult()`.
7
+ * @example
8
+ * import { remult, repo, withRemult } from 'remult';
9
+ * import { initAsyncHooks } from 'remult/async-hooks';
10
+ *
11
+ * import { Task } from './entities/Task.js';
12
+ *
13
+ * initAsyncHooks();
14
+ *
15
+ * // Thx to the `initAsyncHooks` above,
16
+ * // we have isolated async contexts with multiple `withRemult()`,
17
+ * // without needing to initialize a `remultApi` all the time!
18
+ * withRemult(async () => {
19
+ * remult.user = { id: '42' };
20
+ * repo(Task).find()
21
+ * });
22
+ *
23
+ * withRemult(async () => {
24
+ * remult.user = { id: '21' };
25
+ * repo(Task).find()
26
+ * });
27
+ */
28
+ export function initAsyncHooks() {
29
+ _initAsyncHooks();
30
+ RemultAsyncLocalStorage.enable();
31
+ }
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
+ }
@@ -8,15 +8,24 @@ export function remultApi(options) {
8
8
  if (options.bodySizeLimit === undefined) {
9
9
  options.bodySizeLimit = '10mb';
10
10
  }
11
+ const expressVersion = typeof app.del === 'function' ? 4 : 5;
11
12
  if (options?.bodyParser !== false) {
12
13
  app.use(express.json({ limit: options.bodySizeLimit }));
13
14
  app.use(express.urlencoded({ extended: true, limit: options.bodySizeLimit }));
14
15
  }
15
16
  const server = createRemultServer(options, {
16
- buildGenericRequestInfo: (req) => ({
17
- internal: { ...req, on: req.on },
18
- public: { headers: new Headers(req.headers) },
19
- }),
17
+ buildGenericRequestInfo: (req) => {
18
+ const internal = { ...req, on: req.on };
19
+ if (expressVersion === 5) {
20
+ if (!internal.query) {
21
+ internal.query = req.query;
22
+ }
23
+ }
24
+ return {
25
+ internal,
26
+ public: { headers: new Headers(req.headers) },
27
+ };
28
+ },
20
29
  getRequestBody: async (req) => req.body,
21
30
  });
22
31
  server.registerRouter(app);