remult 0.25.6-exp.1 → 0.25.6-exp.3

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.
@@ -316,6 +316,8 @@ export class RemultServerImplementation {
316
316
  }
317
317
  async getRemult(req) {
318
318
  let remult;
319
+ if (!req)
320
+ return await this.runWithRemult(async (c) => (remult = c));
319
321
  await this.process(async (c) => {
320
322
  remult = c;
321
323
  })(req, undefined);
@@ -1,18 +1,11 @@
1
1
  import { getEntityRef, getEntitySettings } from '../remult3/getEntityRef.js';
2
2
  import { getRelationFieldInfo } from '../remult3/relationInfoMember.js';
3
+ /**
4
+ * The `Filter` class is a helper class that focuses on filter-related concerns. It provides methods
5
+ * for creating and applying filters in queries.
6
+ */
3
7
  export class Filter {
4
8
  apply;
5
- constructor(apply) {
6
- this.apply = apply;
7
- }
8
- __applyToConsumer(add) {
9
- this.apply(add);
10
- }
11
- static async resolve(filter) {
12
- if (typeof filter === 'function')
13
- return await filter();
14
- return filter;
15
- }
16
9
  static createCustom(rawFilterTranslator, key = '') {
17
10
  let rawFilterInfo = { key: key, rawFilterTranslator };
18
11
  return Object.assign((x) => {
@@ -26,6 +19,58 @@ export class Filter {
26
19
  };
27
20
  }, { rawFilterInfo });
28
21
  }
22
+ /**
23
+ * Translates an `EntityFilter` to a plain JSON object that can be stored or transported.
24
+ *
25
+ * @template T The entity type for the filter.
26
+ * @param {EntityMetadata<T>} entityDefs The metadata of the entity associated with the filter.
27
+ * @param {EntityFilter<T>} where The `EntityFilter` to be translated.
28
+ * @returns {any} A plain JSON object representing the `EntityFilter`.
29
+ *
30
+ * @example
31
+ * // Assuming `Task` is an entity class
32
+ * const jsonFilter = Filter.entityFilterToJson(Task, { completed: true });
33
+ * // `jsonFilter` can now be stored or transported as JSON
34
+ */
35
+ static entityFilterToJson(entityDefs, where) {
36
+ return Filter.fromEntityFilter(entityDefs, where).toJson();
37
+ }
38
+ /**
39
+ * Translates a plain JSON object back into an `EntityFilter`.
40
+ *
41
+ * @template T The entity type for the filter.
42
+ * @param {EntityMetadata<T>} entityDefs The metadata of the entity associated with the filter.
43
+ * @param {any} packed The plain JSON object representing the `EntityFilter`.
44
+ * @returns {EntityFilter<T>} The reconstructed `EntityFilter`.
45
+ *
46
+ * @example
47
+ * // Assuming `Task` is an entity class and `jsonFilter` is a JSON object representing an EntityFilter
48
+ * const taskFilter = Filter.entityFilterFromJson(Task, jsonFilter);
49
+ * // Using the reconstructed `EntityFilter` in a query
50
+ * const tasks = await remult.repo(Task).find({ where: taskFilter });
51
+ * for (const task of tasks) {
52
+ * // Do something for each task based on the filter
53
+ * }
54
+ */
55
+ static entityFilterFromJson(entityDefs, packed) {
56
+ return buildFilterFromRequestParameters(entityDefs, {
57
+ get: (key) => packed[key],
58
+ });
59
+ }
60
+ /**
61
+ * Converts an `EntityFilter` to a `Filter` that can be used by the `DataProvider`. This method is
62
+ * mainly used internally.
63
+ *
64
+ * @template T The entity type for the filter.
65
+ * @param {EntityMetadata<T>} entity The metadata of the entity associated with the filter.
66
+ * @param {EntityFilter<T>} whereItem The `EntityFilter` to be converted.
67
+ * @returns {Filter} A `Filter` instance that can be used by the `DataProvider`.
68
+ *
69
+ * @example
70
+ * // Assuming `Task` is an entity class and `taskFilter` is an EntityFilter
71
+ * const filter = Filter.fromEntityFilter(Task, taskFilter);
72
+ * // `filter` can now be used with the DataProvider
73
+ */
29
74
  static fromEntityFilter(entity, whereItem) {
30
75
  let result = [];
31
76
  for (const key in whereItem) {
@@ -122,19 +167,25 @@ export class Filter {
122
167
  }
123
168
  return new AndFilter(...result);
124
169
  }
170
+ constructor(apply) {
171
+ this.apply = apply;
172
+ }
173
+ __applyToConsumer(add) {
174
+ this.apply(add);
175
+ }
176
+ //@internal
177
+ static async resolve(filter) {
178
+ if (typeof filter === 'function')
179
+ return await filter();
180
+ return filter;
181
+ }
182
+ //@internal
125
183
  toJson() {
126
184
  let r = new FilterSerializer();
127
185
  this.__applyToConsumer(r);
128
186
  return r.result;
129
187
  }
130
- static entityFilterToJson(entityDefs, where) {
131
- return Filter.fromEntityFilter(entityDefs, where).toJson();
132
- }
133
- static entityFilterFromJson(entityDefs, packed) {
134
- return buildFilterFromRequestParameters(entityDefs, {
135
- get: (key) => packed[key],
136
- });
137
- }
188
+ //@internal
138
189
  static async translateCustomWhere(r, entity, remult) {
139
190
  let f = new customTranslator(async (filterKey, custom) => {
140
191
  let r = [];
@@ -101,11 +101,41 @@ export class LiveQuerySubscriber {
101
101
  }
102
102
  }
103
103
  export const liveQueryKeepAliveRoute = '_liveQueryKeepAlive';
104
+ /**
105
+ * The `SubscriptionChannel` class is used to send messages from the backend to the frontend,
106
+ * using the same mechanism used by live queries.
107
+ *
108
+ * @template messageType The type of the message that the channel will handle.
109
+ * @example
110
+ * // Defined in code that is shared between the frontend and the backend
111
+ * const statusChange = new SubscriptionChannel<{ oldStatus: number, newStatus: number }>("statusChange");
112
+ *
113
+ * // Backend: Publishing a message
114
+ * statusChange.publish({ oldStatus: 1, newStatus: 2 });
115
+ *
116
+ * // Frontend: Subscribing to messages
117
+ * statusChange.subscribe((message) => {
118
+ * console.log(`Status changed from ${message.oldStatus} to ${message.newStatus}`);
119
+ * });
120
+ *
121
+ * // Note: If you want to publish from the frontend, use a BackendMethod for that.
122
+ */
104
123
  export class SubscriptionChannel {
105
124
  channelKey;
125
+ /**
126
+ * Constructs a new `SubscriptionChannel` instance.
127
+ *
128
+ * @param {string} channelKey The key that identifies the channel.
129
+ */
106
130
  constructor(channelKey) {
107
131
  this.channelKey = channelKey;
108
132
  }
133
+ /**
134
+ * Publishes a message to the channel. This method should only be used on the backend.
135
+ *
136
+ * @param {messageType} message The message to be published.
137
+ * @param {Remult} [remult] An optional instance of Remult to use for publishing the message.
138
+ */
109
139
  publish(message, remult) {
110
140
  remult = remult || defaultRemult;
111
141
  remult.subscriptionServer.publishMessage(this.channelKey, message);
@@ -9,116 +9,76 @@ export {};
9
9
  * review react summit
10
10
  #
11
11
 
12
- //y1 - support get with backend method, with url search params as the first parameter, & url as second parameter
13
- //y1 - admin url!
12
+ //y1 - getFields didn't work for kobi in the home component
13
+ //y1 - talk about export api is not possible in +server - how did we face that in others, and how do we doc it - ended up using _api
14
14
  //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
15
- //y1 - talk about the parameter issue with backend methods
15
+ //y1 - admin url!
16
+ //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
17
+ //y1 - talk about modules in init express with entities/controllers,initRequest,initApi
16
18
  //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
17
- //y1 - I think that the tests you've setup cover next app router - I added to the setup, but not sure where else
19
+ //y1 - I think that the tests you've setup don't cover next app router - I added to the setup, but not sure where else
20
+ //y1 - support get with backend method, with url search params as the first parameter, & url as second parameter
21
+ //y1 - talk about the parameter issue with backend methods
18
22
  //y1 - select data provider per entity https://discord.com/channels/975754286384418847/976006081748807690/1201415305885397003
19
- //y1 - talk about modules in init express with entities/controllers,initRequest,initApi
23
+ //y2 - live query refresh of view on table update
24
+ //y2 - livequery for findfirst (@JY)
20
25
 
26
+ /*y2 -
27
+ //y2 - allow api update only for new rows
28
+ @Fields.string<Category>({
29
+ allowApiUpdate: (c) => getEntityRef(c).isNew(),
30
+ })
31
+ Description = ""*/
32
+ //y2 - get backend methods to work when specifying types for date, and entities as poco's
33
+ //y2 - conside law-q db based on schema issue - I think that while running the dataProvider function, we should have a valid remult - maybe even have a valid remult, that will be valid until api is run
34
+ //y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
35
+ //y2 - constraints (@JY)
36
+ // remult admin
21
37
  //p1 - Small thing, I get SvelteKitError: Not found: /vite.svg
22
38
  //p1 - understand the to many relation for the admin, based on the to one
23
39
  //p1 - relation from order details to order gave a compound id info - and it is not true - same for the relation to product
24
- //p1 - live query with include
25
40
  //p1 - new row when there are relations, looks funny (see product)
26
41
  //p1 - the + row in the bottom should extend to the full width
42
+ //p1 - need a way to extract the fields from the relation - for generating relation based sql
43
+ //p1 - allow conditional admin - like allowed
44
+ //p1 - remult-admin doesn't handle primary key that has compound column
45
+ //p1 - remult-admin didn't show a update for a table with a uniqua that is numeric
46
+ //remult
47
+ //p1 - better support union types (status, etc... so it'll work also in filter etc...)
48
+ //p1 - add tablename parameter for dbNamesOf
49
+ //p1 -check if count doesn't respect allow read (I think)
50
+ //p1 - when a tasks table exists in a different schema - we get many errors
51
+ //p1 - live query with include
27
52
  //p1 - adjust angular tutorial starter kit for latest angular (as is in tutorial)
28
-
29
- /*y2 -
30
- @Fields.string<Category>({
31
- allowApiUpdate: (c) => getEntityRef(c).isNew(),
32
- })
33
- Description = ""*/
53
+ //p2 - I think there should be a way to throw a forbidden exception
54
+ //p2 - processError in remult express
55
+ //p2 - allow find options preprocessor for api calls, to use for authorization
56
+ //p2 - when subscribe is forbidden - the query still runs after the renew process
57
+ //p2 - 'update tasks set where id = $1
58
+ //p1 - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
59
+ //p2 - type metadata.key - to keyof entity - based on cwis input
60
+ //y2 - remove __dirname from tutorials
61
+ //p2 - when value changes for field with error, clear error - so the user will feel comfortable
62
+ //p2 - allowApiUpdate should be false for include in api false
63
+ //docs
64
+ //p2 - make sure that internal members do not appear in the documentation - try running it on the d.ts instead of the code itself.
65
+ //------
34
66
  //y2 - from the crm-demo(https://crm-demo.up.railway.app/deals), after editing a deal: - _updateEntityBasedOnApi
35
67
  //y2 - Backend methods are transactions, it's not intuitive and maybe should be optional / opt in
36
68
  //y2 - how to run a transaction as a user
37
69
  //p2 - enum column
38
- //p2 - I think there should be a way to throw a forbidden exception
39
- //y2 - get backend methods to work when specifying types for date, and entities as poco's
40
- //y2 - required
41
- //y2 - reconsider validators
42
70
  //y2 - message for invalid value
43
71
  //y2 - message for relation that is missing
44
- //p2 - more column types
45
- //p2 - processError in remult express
46
- //p2 - allow find options preprocessor for api calls, to use for authorization
47
- //p2 - write doc about controlling updates of rows...
48
- //p1 - need a way to extract the fields from the relation - for generating relation based sql
49
- //p1 - when doing this, work on graphql relation with fields not just field
50
- //p2 - add docs for apply changes
51
- //p2 - when subscribe is forbidden - the query still runs after the renew process
52
- //p2 - 'update tasks set where id = $1
53
72
  //y2 - consider multi tenancies
54
73
  //y2 - allow api read to also support instance and filter. - problem with promise
55
- //p2 - doc this:
56
- /**
57
- * //p2 - doc this
58
- remult.apiClient.httpClient = (
59
- input: RequestInfo | URL,
60
- init?: RequestInit
61
- ) => {
62
- return fetch(input, {
63
- ...init,
64
- headers: {
65
- authorization: 'Bearer ' + sessionStorage.sessionId,
66
- },
67
- cache: 'no-store',
68
- })
69
- }
70
-
71
- &&
72
- remult.apiClient.url='localhost:3007/api
73
-
74
-
75
- */
76
- //p1 - allow conditional admin - like allowed
77
- //p1 - remult-admin doesn't handle primary key that has compound column
78
- //p1 - remult-admin didn't show a update for a table with a uniqua that is numeric
79
- //p1 -check if checouknt doesn't respect allow read (I think)
80
- //p1 - when a tasks table exists in a different schema - we get many errors
81
- //p1 - allow api update only for new rows
82
- //p1 - getFields didn't work for kobi in the home component
83
- //p1 - add to docs https://codesandbox.io/p/devbox/demo-sql-expression-wqvj4g
84
- //p1 - ValueListFieldType - the decorator gives an error in react vite project - see langulage yedidya
85
- //y1 - talk about export api is not possible in +server - how did we face that in others, and how do we doc it
74
+ //p2 - more column types
86
75
  //p2 - document validators
87
76
  //p2 - and validators to reference
88
77
  //y2 - discuss a default date formatter
89
78
  //y2 - add some api testing framework for user unit tests (will help with codesandbox based discussions)
90
- //y2 - live query refresh of view on table update
91
- //y2 - livequery for findfirst (@JY)
92
- //y2 - #239 - (@JY) add a way to get from fieldMetadata back to entity repo (like we have in fieldRef)
93
- //p2 - constraints (@JY)
94
- //p2 - type metadata.key - to keyof entity - based on cwis input
95
- //y2 - remove __dirname from tutorials
96
- //p2 - when value changes for field with error, clear error - so the user will feel comfortable
97
- //p2 - conside law-q db based on schema issue - I think that while running the dataProvider function, we should have a valid remult - maybe even have a valid remult, that will be valid until api is run
98
- //y2 - we should really reconsider allow null by default to be true - I think that what we're doing is confusing for most . In my case I added a volunteer relation, and didn't set any value to it - and I get an error that null value is not allowed for it
99
- //p1 - add a follow up doc to the tutorial on how to user a real user table for the users
100
- /**
101
- * our default is allow null false.
102
- * for sql databases we add a db default of 0/''/false
103
- * when we set a relation to null - we update the related field to null
104
- * we do not enforce allow null false (mongo, json it'll get stored)
105
-
106
- */
107
- //p2 - allowApiUpdate should be false for include in api false
108
79
  //[ ] V2 - what to do about for relations count?
109
80
  //[ ] V2 - condition? not to fetch if null etc....
110
81
  //[ ] V3 - all these fields will also appear in the where etc... in the typescript api - but we will not enforce them
111
82
  //y2 - consider if field types should include validation in them by default (string,number that it's not NaN etc...) and if so, what message?
112
83
  //y2 - should enforce integer - currently we probably round / truncate it
113
84
  //p1 - adjust react tutorial to esm
114
- //p1 - write docs on with remult - and usage in init api
115
- //p1 - document value converter - including field type in the db
116
- /*y1 - discuss sql stuff:
117
- https://discord.com/channels/975754286384418847/975754286384418852/1198997156678357043
118
- https://codesandbox.io/p/devbox/demo-sql-expression-forked-f94p6m?file=%2Fsrc%2Findex.ts%3A21%2C3-21%2C12&workspaceId=c0f7eccf-c151-423b-ac8e-0f0f3e1ad350
119
- https://codesandbox.io/p/devbox/demo-sql-expression-forked-c2jhyc?file=%2Fsrc%2Findex.ts%3A37%2C50&workspaceId=7e9d0505-ea79-46f1-8973-50cadde4fa96
120
-
121
-
122
- */
123
- //p1 - make dbNames of have a default wrapIdentifier based on the current data provider
124
- //p1 - prevent circular in sql expression
package/esm/src/sort.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { CompoundIdField } from './CompoundIdField.js';
2
2
  import { getRelationFieldInfo } from './remult3/relationInfoMember.js';
3
+ /**
4
+ * The `Sort` class is used to describe sorting criteria for queries. It is mainly used internally,
5
+ * but it provides a few useful functions for working with sorting.
6
+ */
3
7
  export class Sort {
8
+ /**
9
+ * Translates the current `Sort` instance into an `EntityOrderBy` object.
10
+ *
11
+ * @returns {EntityOrderBy<any>} An `EntityOrderBy` object representing the sort criteria.
12
+ */
4
13
  toEntityOrderBy() {
5
14
  let result = {};
6
15
  for (const seg of this.Segments) {
@@ -12,10 +21,25 @@ export class Sort {
12
21
  }
13
22
  return result;
14
23
  }
24
+ /**
25
+ * Constructs a `Sort` instance with the provided sort segments.
26
+ *
27
+ * @param {...SortSegment[]} segments The sort segments to be included in the sort criteria.
28
+ */
15
29
  constructor(...segments) {
16
30
  this.Segments = segments;
17
31
  }
32
+ /**
33
+ * The segments of the sort criteria.
34
+ *
35
+ * @type {SortSegment[]}
36
+ */
18
37
  Segments;
38
+ /**
39
+ * Reverses the sort order of the current sort criteria.
40
+ *
41
+ * @returns {Sort} A new `Sort` instance with the reversed sort order.
42
+ */
19
43
  reverse() {
20
44
  let r = new Sort();
21
45
  for (const s of this.Segments) {
@@ -23,6 +47,14 @@ export class Sort {
23
47
  }
24
48
  return r;
25
49
  }
50
+ /**
51
+ * Compares two objects based on the current sort criteria.
52
+ *
53
+ * @param {any} a The first object to compare.
54
+ * @param {any} b The second object to compare.
55
+ * @param {function(FieldMetadata): string} [getFieldKey] An optional function to get the field key for comparison.
56
+ * @returns {number} A negative value if `a` should come before `b`, a positive value if `a` should come after `b`, or zero if they are equal.
57
+ */
26
58
  compare(a, b, getFieldKey) {
27
59
  if (!getFieldKey)
28
60
  getFieldKey = (x) => x.key;
@@ -43,6 +75,14 @@ export class Sort {
43
75
  }
44
76
  return r;
45
77
  }
78
+ /**
79
+ * Translates an `EntityOrderBy` to a `Sort` instance.
80
+ *
81
+ * @template T The entity type for the order by.
82
+ * @param {EntityMetadata<T>} entityDefs The metadata of the entity associated with the order by.
83
+ * @param {EntityOrderBy<T>} [orderBy] The `EntityOrderBy` to be translated.
84
+ * @returns {Sort} A `Sort` instance representing the translated order by.
85
+ */
46
86
  static translateOrderByToSort(entityDefs, orderBy) {
47
87
  if (!orderBy)
48
88
  return undefined;
@@ -86,6 +126,15 @@ export class Sort {
86
126
  }
87
127
  return sort;
88
128
  }
129
+ /**
130
+ * Creates a unique `Sort` instance based on the provided `Sort` and the entity metadata.
131
+ * This ensures that the sort criteria result in a unique ordering of entities.
132
+ *
133
+ * @template T The entity type for the sort.
134
+ * @param {EntityMetadata<T>} entityMetadata The metadata of the entity associated with the sort.
135
+ * @param {Sort} [orderBy] The `Sort` instance to be made unique.
136
+ * @returns {Sort} A `Sort` instance representing the unique sort criteria.
137
+ */
89
138
  static createUniqueSort(entityMetadata, orderBy) {
90
139
  if ((!orderBy || Object.keys(orderBy).length === 0) &&
91
140
  entityMetadata.options.defaultOrderBy)
@@ -104,6 +153,15 @@ export class Sort {
104
153
  }
105
154
  return orderBy;
106
155
  }
156
+ /**
157
+ * Creates a unique `EntityOrderBy` based on the provided `EntityOrderBy` and the entity metadata.
158
+ * This ensures that the order by criteria result in a unique ordering of entities.
159
+ *
160
+ * @template T The entity type for the order by.
161
+ * @param {EntityMetadata<T>} entityMetadata The metadata of the entity associated with the order by.
162
+ * @param {EntityOrderBy<T>} [orderBy] The `EntityOrderBy` to be made unique.
163
+ * @returns {EntityOrderBy<T>} An `EntityOrderBy` representing the unique order by criteria.
164
+ */
107
165
  static createUniqueEntityOrderBy(entityMetadata, orderBy) {
108
166
  if (!orderBy || Object.keys(orderBy).length === 0)
109
167
  orderBy = entityMetadata.options.defaultOrderBy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.25.6-exp.1",
3
+ "version": "0.25.6-exp.3",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -74,7 +74,7 @@ export interface RemultServer<RequestType> extends RemultServerCore<RequestType>
74
74
  withRemultAsync<T>(request: RequestType | undefined, what: () => Promise<T>): Promise<T>;
75
75
  }
76
76
  export interface RemultServerCore<RequestType> {
77
- getRemult(req: RequestType): Promise<Remult>;
77
+ getRemult(req?: RequestType): Promise<Remult>;
78
78
  openApiDoc(options: {
79
79
  title: string;
80
80
  version?: string;
@@ -597,13 +597,19 @@ var RemultServerImplementation = /** @class */ (function () {
597
597
  var _this = this;
598
598
  return tslib_1.__generator(this, function (_a) {
599
599
  switch (_a.label) {
600
- case 0: return [4 /*yield*/, this.process(function (c) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
600
+ case 0:
601
+ if (!!req) return [3 /*break*/, 2];
602
+ return [4 /*yield*/, this.runWithRemult(function (c) { return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) {
603
+ return [2 /*return*/, (remult = c)];
604
+ }); }); })];
605
+ case 1: return [2 /*return*/, _a.sent()];
606
+ case 2: return [4 /*yield*/, this.process(function (c) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
601
607
  return tslib_1.__generator(this, function (_a) {
602
608
  remult = c;
603
609
  return [2 /*return*/];
604
610
  });
605
611
  }); })(req, undefined)];
606
- case 1:
612
+ case 3:
607
613
  _a.sent();
608
614
  return [2 /*return*/, remult];
609
615
  }
@@ -165,15 +165,100 @@ export interface FieldMetadata<valueType = any, entityType = any> {
165
165
  */
166
166
  fromInput(inputValue: string, inputType?: string): valueType;
167
167
  }
168
+ /**
169
+ * Interface for converting values between different formats, such as in-memory objects, database storage,
170
+ * JSON data transfer objects (DTOs), and HTML input elements.
171
+ *
172
+ * @template valueType The type of the value that the converter handles.
173
+ */
168
174
  export interface ValueConverter<valueType> {
175
+ /**
176
+ * Converts a value from a JSON DTO to the valueType. This method is typically used when receiving data
177
+ * from a REST API call or deserializing a JSON payload.
178
+ *
179
+ * @param val The value to convert.
180
+ * @returns The converted value.
181
+ *
182
+ * @example
183
+ * fromJson: val => new Date(val)
184
+ */
169
185
  fromJson?(val: any): valueType;
186
+ /**
187
+ * Converts a value of valueType to a JSON DTO. This method is typically used when sending data
188
+ * to a REST API or serializing an object to a JSON payload.
189
+ *
190
+ * @param val The value to convert.
191
+ * @returns The converted value.
192
+ *
193
+ * @example
194
+ * toJson: val => val?.toISOString()
195
+ */
170
196
  toJson?(val: valueType): any;
197
+ /**
198
+ * Converts a value from the database format to the valueType.
199
+ *
200
+ * @param val The value to convert.
201
+ * @returns The converted value.
202
+ *
203
+ * @example
204
+ * fromDb: val => new Date(val)
205
+ */
171
206
  fromDb?(val: any): valueType;
207
+ /**
208
+ * Converts a value of valueType to the database format.
209
+ *
210
+ * @param val The value to convert.
211
+ * @returns The converted value.
212
+ *
213
+ * @example
214
+ * toDb: val => val?.toISOString()
215
+ */
172
216
  toDb?(val: valueType): any;
217
+ /**
218
+ * Converts a value of valueType to a string suitable for an HTML input element.
219
+ *
220
+ * @param val The value to convert.
221
+ * @param inputType The type of the input element (optional).
222
+ * @returns The converted value as a string.
223
+ *
224
+ * @example
225
+ * toInput: (val, inputType) => val?.toISOString().substring(0, 10)
226
+ */
173
227
  toInput?(val: valueType, inputType?: string): string;
228
+ /**
229
+ * Converts a string from an HTML input element to the valueType.
230
+ *
231
+ * @param val The value to convert.
232
+ * @param inputType The type of the input element (optional).
233
+ * @returns The converted value.
234
+ *
235
+ * @example
236
+ * fromInput: (val, inputType) => new Date(val)
237
+ */
174
238
  fromInput?(val: string, inputType?: string): valueType;
239
+ /**
240
+ * Returns a displayable string representation of a value of valueType.
241
+ *
242
+ * @param val The value to convert.
243
+ * @returns The displayable string.
244
+ *
245
+ * @example
246
+ * displayValue: val => val?.toLocaleDateString()
247
+ */
175
248
  displayValue?(val: valueType): string;
249
+ /**
250
+ * Specifies the storage type used in the database for this field.
251
+ *
252
+ * @example
253
+ * readonly fieldTypeInDb = 'decimal(18,2)';
254
+ */
176
255
  readonly fieldTypeInDb?: string;
256
+ /**
257
+ * Specifies the type of HTML input element suitable for values of valueType.
258
+ *
259
+ * @example
260
+ * readonly inputType = 'date';
261
+ */
177
262
  readonly inputType?: string;
178
263
  }
179
264
  export declare type FieldValidator<entityType = any, valueType = any> = (entity: entityType, event: ValidateFieldEvent<entityType, valueType>) => boolean | string | void | undefined | Promise<boolean | string | void | undefined>;
package/src/context.d.ts CHANGED
@@ -76,19 +76,76 @@ export declare class Remult {
76
76
  export type GetArguments<T> = T extends (...args: infer FirstArgument) => any ? FirstArgument : never;
77
77
  export interface RemultContext {
78
78
  }
79
+ /**
80
+ * Interface for configuring the API client used by Remult to perform HTTP calls to the backend.
81
+ */
79
82
  export interface ApiClient {
80
- /** The http client to use when making api calls.
83
+ /**
84
+ * The HTTP client to use when making API calls. It can be set to a function with the `fetch` signature
85
+ * or an object that has `post`, `put`, `delete`, and `get` methods. This can also be used to inject
86
+ * logic before each HTTP call, such as adding authorization headers.
87
+ *
81
88
  * @example
89
+ * // Using Axios
82
90
  * remult.apiClient.httpClient = axios;
91
+ *
92
+ * @example
93
+ * // Using Angular HttpClient
94
+ * remult.apiClient.httpClient = httpClient;
95
+ *
83
96
  * @example
84
- * remult.apiClient.httpClient = httpClient;//angular http client
97
+ * // Using fetch (default)
98
+ * remult.apiClient.httpClient = fetch;
99
+ *
85
100
  * @example
86
- * remult.apiClient.httpClient = fetch; //this is the default
101
+ * // Adding bearer token authorization
102
+ * remult.apiClient.httpClient = (input: RequestInfo | URL, init?: RequestInit) => {
103
+ * return fetch(input, {
104
+ * ...init,
105
+ * headers: {
106
+ * authorization: 'Bearer ' + sessionStorage.sessionId,
107
+ * },
108
+ * cache: 'no-store',
109
+ * });
110
+ * };
87
111
  */
88
112
  httpClient?: ExternalHttpProvider | typeof fetch;
89
- /** The base url to for making api calls */
113
+ /**
114
+ * The base URL for making API calls. By default, it is set to '/api'. It can be modified to be relative
115
+ * or to use a different domain for the server.
116
+ *
117
+ * @example
118
+ * // Relative URL
119
+ * remult.apiClient.url = './api';
120
+ *
121
+ * @example
122
+ * // Different domain
123
+ * remult.apiClient.url = 'https://example.com/api';
124
+ */
90
125
  url?: string;
126
+ /**
127
+ * The subscription client used for real-time data updates. By default, it is set to use Server-Sent Events (SSE).
128
+ * It can be set to any subscription provider as illustrated in the Remult tutorial for deploying to a serverless environment.
129
+ *
130
+ * @see https://remult.dev/tutorials/react-next/deployment.html#deploying-to-a-serverless-environment
131
+ */
91
132
  subscriptionClient?: SubscriptionClient;
133
+ /**
134
+ * A function that wraps message handling for subscriptions. This is useful for executing some code before
135
+ * or after any message arrives from the subscription. For example, in Angular, this can be used to trigger
136
+ * a render by calling the `NgZone` run method.
137
+ *
138
+ * @example
139
+ * // Angular example
140
+ * import { Component, NgZone } from '@angular/core';
141
+ * import { remult } from "remult";
142
+ *
143
+ * export class AppComponent {
144
+ * constructor(zone: NgZone) {
145
+ * remult.apiClient.wrapMessageHandling = handler => zone.run(() => handler());
146
+ * }
147
+ * }
148
+ */
92
149
  wrapMessageHandling?: (x: VoidFunction) => void;
93
150
  }
94
151
  export interface ControllerOptions {