remult 3.3.14 → 3.3.16

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.
@@ -0,0 +1,55 @@
1
+ import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
2
+ /**
3
+ * A Remult data provider for Node's built-in `node:sqlite` module.
4
+ *
5
+ * `node:sqlite` is available in Node.js 22.5.0 and newer. The database is
6
+ * accepted structurally so importing this adapter does not load a Node-only
7
+ * module until the application creates its `DatabaseSync` instance.
8
+ */
9
+ export class NodeSqliteDataProvider extends SqliteCoreDataProvider {
10
+ constructor(db) {
11
+ super(() => new NodeSqliteCommand(db), async () => {
12
+ db.close();
13
+ });
14
+ }
15
+ }
16
+ class NodeSqliteCommand {
17
+ db;
18
+ values = {};
19
+ i = 0;
20
+ constructor(db) {
21
+ this.db = db;
22
+ }
23
+ async execute(sql) {
24
+ const statement = this.db.prepare(sql);
25
+ statement.setAllowBareNamedParameters(true);
26
+ const rows = Object.keys(this.values).length
27
+ ? statement.all(this.values)
28
+ : statement.all();
29
+ return new NodeSqliteResult(rows);
30
+ }
31
+ addParameterAndReturnSqlToken(value) {
32
+ return this.param(value);
33
+ }
34
+ param(value) {
35
+ if (value instanceof Date)
36
+ value = value.valueOf();
37
+ if (typeof value === 'boolean')
38
+ value = value ? 1 : 0;
39
+ if (value === undefined)
40
+ value = null;
41
+ const key = String(this.i++);
42
+ this.values[key] = value;
43
+ return `:${key}`;
44
+ }
45
+ }
46
+ class NodeSqliteResult {
47
+ rows;
48
+ constructor(rows) {
49
+ this.rows = rows;
50
+ }
51
+ getColumnKeyInResultForIndexInSelect(index) {
52
+ const first = this.rows[0];
53
+ return first ? (Object.keys(first)[index] ?? '') : '';
54
+ }
55
+ }
@@ -195,6 +195,9 @@ export class DataApi {
195
195
  if (body?.orderBy) {
196
196
  for (const element of body?.orderBy) {
197
197
  const direction = element.isDescending ? 'desc' : 'asc';
198
+ if (element.operation !== 'count' &&
199
+ !this.repository.fields.find(element.field)?.includedInApi())
200
+ continue;
198
201
  switch (element.operation) {
199
202
  case undefined:
200
203
  orderBy[element.field] = direction;
@@ -3,7 +3,7 @@ import { CompoundIdField } from '../CompoundIdField.js';
3
3
  import { Remult } from '../context.js';
4
4
  import { Filter } from '../filter/filter-interfaces.js';
5
5
  import { Sort } from '../sort.js';
6
- import { GroupByForApiKey, } from './remult3.js';
6
+ import { GroupByCountMember, GroupByForApiKey, } from './remult3.js';
7
7
  import { assign } from '../../assign.js';
8
8
  import { EntityError, } from '../data-interfaces.js';
9
9
  import { ValueConverters } from '../valueConverters.js';
@@ -185,10 +185,14 @@ export class RepositoryImplementation {
185
185
  const element = options.orderBy[key];
186
186
  if (element)
187
187
  if (typeof element === 'string') {
188
+ // ordering by a raw column is only valid for grouped fields -
189
+ // skip others so behavior is consistent across data providers
190
+ if (key !== GroupByCountMember && !options?.group?.includes(key))
191
+ continue;
188
192
  dpOptions.orderBy.push({
189
- field: key === '$count' ? undefined : getField(key),
193
+ field: key === GroupByCountMember ? undefined : getField(key),
190
194
  isDescending: element === 'desc',
191
- operation: key === '$count' ? 'count' : undefined,
195
+ operation: key === GroupByCountMember ? 'count' : undefined,
192
196
  });
193
197
  }
194
198
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.3.14",
3
+ "version": "3.3.16",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -112,6 +112,11 @@
112
112
  "import": "./esm/remult-sqlite-core.js",
113
113
  "types": "./remult-sqlite-core.d.ts"
114
114
  },
115
+ "./remult-node-sqlite": {
116
+ "require": "./remult-node-sqlite.js",
117
+ "import": "./esm/remult-node-sqlite.js",
118
+ "types": "./remult-node-sqlite.d.ts"
119
+ },
115
120
  "./remult-better-sqlite3": {
116
121
  "require": "./remult-better-sqlite3.js",
117
122
  "import": "./esm/remult-better-sqlite3.js",
@@ -0,0 +1,19 @@
1
+ import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
2
+ export type NodeSqliteStatement = {
3
+ setAllowBareNamedParameters(enabled: boolean): void;
4
+ all(...anonymousParameters: any[]): Record<string, unknown>[];
5
+ };
6
+ export type NodeSqliteDatabase = {
7
+ prepare(sql: string): NodeSqliteStatement;
8
+ close(): void;
9
+ };
10
+ /**
11
+ * A Remult data provider for Node's built-in `node:sqlite` module.
12
+ *
13
+ * `node:sqlite` is available in Node.js 22.5.0 and newer. The database is
14
+ * accepted structurally so importing this adapter does not load a Node-only
15
+ * module until the application creates its `DatabaseSync` instance.
16
+ */
17
+ export declare class NodeSqliteDataProvider extends SqliteCoreDataProvider {
18
+ constructor(db: NodeSqliteDatabase);
19
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeSqliteDataProvider = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var remult_sqlite_core_js_1 = require("./remult-sqlite-core.js");
6
+ /**
7
+ * A Remult data provider for Node's built-in `node:sqlite` module.
8
+ *
9
+ * `node:sqlite` is available in Node.js 22.5.0 and newer. The database is
10
+ * accepted structurally so importing this adapter does not load a Node-only
11
+ * module until the application creates its `DatabaseSync` instance.
12
+ */
13
+ var NodeSqliteDataProvider = /** @class */ (function (_super) {
14
+ tslib_1.__extends(NodeSqliteDataProvider, _super);
15
+ function NodeSqliteDataProvider(db) {
16
+ var _this = _super.call(this, function () { return new NodeSqliteCommand(db); }, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
17
+ return tslib_1.__generator(this, function (_a) {
18
+ db.close();
19
+ return [2 /*return*/];
20
+ });
21
+ }); }) || this;
22
+ return _this;
23
+ }
24
+ return NodeSqliteDataProvider;
25
+ }(remult_sqlite_core_js_1.SqliteCoreDataProvider));
26
+ exports.NodeSqliteDataProvider = NodeSqliteDataProvider;
27
+ var NodeSqliteCommand = /** @class */ (function () {
28
+ function NodeSqliteCommand(db) {
29
+ this.db = db;
30
+ this.values = {};
31
+ this.i = 0;
32
+ }
33
+ NodeSqliteCommand.prototype.execute = function (sql) {
34
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
35
+ var statement, rows;
36
+ return tslib_1.__generator(this, function (_a) {
37
+ statement = this.db.prepare(sql);
38
+ statement.setAllowBareNamedParameters(true);
39
+ rows = Object.keys(this.values).length
40
+ ? statement.all(this.values)
41
+ : statement.all();
42
+ return [2 /*return*/, new NodeSqliteResult(rows)];
43
+ });
44
+ });
45
+ };
46
+ NodeSqliteCommand.prototype.addParameterAndReturnSqlToken = function (value) {
47
+ return this.param(value);
48
+ };
49
+ NodeSqliteCommand.prototype.param = function (value) {
50
+ if (value instanceof Date)
51
+ value = value.valueOf();
52
+ if (typeof value === 'boolean')
53
+ value = value ? 1 : 0;
54
+ if (value === undefined)
55
+ value = null;
56
+ var key = String(this.i++);
57
+ this.values[key] = value;
58
+ return ":".concat(key);
59
+ };
60
+ return NodeSqliteCommand;
61
+ }());
62
+ var NodeSqliteResult = /** @class */ (function () {
63
+ function NodeSqliteResult(rows) {
64
+ this.rows = rows;
65
+ }
66
+ NodeSqliteResult.prototype.getColumnKeyInResultForIndexInSelect = function (index) {
67
+ var _a;
68
+ var first = this.rows[0];
69
+ return first ? ((_a = Object.keys(first)[index]) !== null && _a !== void 0 ? _a : '') : '';
70
+ };
71
+ return NodeSqliteResult;
72
+ }());
@@ -111,8 +111,8 @@ export interface FieldOptions<entityType = unknown, valueType = unknown> {
111
111
  valueConverter?: ValueConverter<valueType>;
112
112
  /** an arrow function that translates the value to a display value */
113
113
  displayValue?: (entity: entityType, value: valueType) => string;
114
- /** an arrow function that determines the default value of the field, when the entity is created using the `repo.create` method */
115
- defaultValue?: (entity: entityType) => valueType;
114
+ /** The default value of the field, when the entity is created using the `repo.create` method — a value or an arrow function that computes it */
115
+ defaultValue?: valueType | ((entity: entityType) => valueType);
116
116
  /** The html input type for this field */
117
117
  inputType?: string;
118
118
  /**
package/src/data-api.js CHANGED
@@ -378,22 +378,25 @@ var DataApi = /** @class */ (function () {
378
378
  var findOptions, orderBy, _a, _b, element, direction, group, min, max, result, minMax, _loop_1, _c, _d, element;
379
379
  var e_3, _e, _f, _g, e_4, _h;
380
380
  var _this = this;
381
- var _j, _k, _l, _m, _o, _p;
382
- return tslib_1.__generator(this, function (_q) {
383
- switch (_q.label) {
381
+ var _j, _k, _l, _m, _o, _p, _q;
382
+ return tslib_1.__generator(this, function (_r) {
383
+ switch (_r.label) {
384
384
  case 0:
385
385
  if (!this.repository.metadata.apiReadAllowed) {
386
386
  throw new server_action_js_1.ForbiddenError();
387
387
  }
388
388
  return [4 /*yield*/, this.findOptionsFromRequest(request, body)];
389
389
  case 1:
390
- findOptions = _q.sent();
390
+ findOptions = _r.sent();
391
391
  orderBy = {};
392
392
  if (body === null || body === void 0 ? void 0 : body.orderBy) {
393
393
  try {
394
394
  for (_a = tslib_1.__values(body === null || body === void 0 ? void 0 : body.orderBy), _b = _a.next(); !_b.done; _b = _a.next()) {
395
395
  element = _b.value;
396
396
  direction = element.isDescending ? 'desc' : 'asc';
397
+ if (element.operation !== 'count' &&
398
+ !((_j = this.repository.fields.find(element.field)) === null || _j === void 0 ? void 0 : _j.includedInApi()))
399
+ continue;
397
400
  switch (element.operation) {
398
401
  case undefined:
399
402
  orderBy[element.field] = direction;
@@ -415,13 +418,13 @@ var DataApi = /** @class */ (function () {
415
418
  finally { if (e_3) throw e_3.error; }
416
419
  }
417
420
  }
418
- group = (_j = body === null || body === void 0 ? void 0 : body.groupBy) === null || _j === void 0 ? void 0 : _j.filter(function (x) {
421
+ group = (_k = body === null || body === void 0 ? void 0 : body.groupBy) === null || _k === void 0 ? void 0 : _k.filter(function (x) {
419
422
  return _this.repository.fields.find(x).includedInApi();
420
423
  });
421
- min = (_k = body === null || body === void 0 ? void 0 : body.min) === null || _k === void 0 ? void 0 : _k.filter(function (x) {
424
+ min = (_l = body === null || body === void 0 ? void 0 : body.min) === null || _l === void 0 ? void 0 : _l.filter(function (x) {
422
425
  return _this.repository.fields.find(x).includedInApi();
423
426
  });
424
- max = (_l = body === null || body === void 0 ? void 0 : body.max) === null || _l === void 0 ? void 0 : _l.filter(function (x) {
427
+ max = (_m = body === null || body === void 0 ? void 0 : body.max) === null || _m === void 0 ? void 0 : _m.filter(function (x) {
425
428
  return _this.repository.fields.find(x).includedInApi();
426
429
  });
427
430
  return [4 /*yield*/, this.repository.groupBy((_g = {
@@ -432,21 +435,21 @@ var DataApi = /** @class */ (function () {
432
435
  //@ts-expect-error internal key
433
436
  _g[remult3_js_1.GroupByForApiKey] = true,
434
437
  _g.group = group,
435
- _g.sum = (_m = body === null || body === void 0 ? void 0 : body.sum) === null || _m === void 0 ? void 0 : _m.filter(function (x) {
438
+ _g.sum = (_o = body === null || body === void 0 ? void 0 : body.sum) === null || _o === void 0 ? void 0 : _o.filter(function (x) {
436
439
  return _this.repository.fields.find(x).includedInApi();
437
440
  }),
438
- _g.avg = (_o = body === null || body === void 0 ? void 0 : body.avg) === null || _o === void 0 ? void 0 : _o.filter(function (x) {
441
+ _g.avg = (_p = body === null || body === void 0 ? void 0 : body.avg) === null || _p === void 0 ? void 0 : _p.filter(function (x) {
439
442
  return _this.repository.fields.find(x).includedInApi();
440
443
  }),
441
444
  _g.min = min,
442
445
  _g.max = max,
443
- _g.distinctCount = (_p = body === null || body === void 0 ? void 0 : body.distinctCount) === null || _p === void 0 ? void 0 : _p.filter(function (x) {
446
+ _g.distinctCount = (_q = body === null || body === void 0 ? void 0 : body.distinctCount) === null || _q === void 0 ? void 0 : _q.filter(function (x) {
444
447
  return _this.repository.fields.find(x).includedInApi();
445
448
  }),
446
449
  _g.orderBy = orderBy,
447
450
  _g))];
448
451
  case 2:
449
- result = _q.sent();
452
+ result = _r.sent();
450
453
  if (group)
451
454
  result.forEach(function (x) {
452
455
  var e_5, _a;
@@ -243,12 +243,12 @@ var RepositoryImplementation = /** @class */ (function () {
243
243
  return tslib_1.__awaiter(this, void 0, void 0, function () {
244
244
  var findOptions, getField, getFieldNotInGroupBy, dpOptions, key, element, operation, direction;
245
245
  var _this = this;
246
- var _a, _b, _c, _d, _e, _f;
247
- return tslib_1.__generator(this, function (_g) {
248
- switch (_g.label) {
246
+ var _a, _b, _c, _d, _e, _f, _g;
247
+ return tslib_1.__generator(this, function (_h) {
248
+ switch (_h.label) {
249
249
  case 0: return [4 /*yield*/, this._buildEntityDataProviderFindOptions(tslib_1.__assign({}, options))];
250
250
  case 1:
251
- findOptions = _g.sent();
251
+ findOptions = _h.sent();
252
252
  getField = function (key) {
253
253
  var r = _this.metadata.fields.find(key);
254
254
  if (r === undefined)
@@ -279,10 +279,14 @@ var RepositoryImplementation = /** @class */ (function () {
279
279
  element = options.orderBy[key];
280
280
  if (element)
281
281
  if (typeof element === 'string') {
282
+ // ordering by a raw column is only valid for grouped fields -
283
+ // skip others so behavior is consistent across data providers
284
+ if (key !== remult3_js_1.GroupByCountMember && !((_g = options === null || options === void 0 ? void 0 : options.group) === null || _g === void 0 ? void 0 : _g.includes(key)))
285
+ continue;
282
286
  dpOptions.orderBy.push({
283
- field: key === '$count' ? undefined : getField(key),
287
+ field: key === remult3_js_1.GroupByCountMember ? undefined : getField(key),
284
288
  isDescending: element === 'desc',
285
- operation: key === '$count' ? 'count' : undefined,
289
+ operation: key === remult3_js_1.GroupByCountMember ? 'count' : undefined,
286
290
  });
287
291
  }
288
292
  else {
@@ -1211,7 +1211,13 @@ export interface ClassDecoratorContextStub<Class extends new (...args: any) => a
1211
1211
  readonly name: string | undefined;
1212
1212
  addInitializer(initializer: (this: Class) => void): void;
1213
1213
  }
1214
- export type ClassFieldDecorator<entityType, valueType> = (target: any, context: string | ClassFieldDecoratorContextStub<entityType, valueType | undefined>, c?: any) => void;
1214
+ export type ClassFieldDecorator<entityType, valueType> = ((target: any, context: string | ClassFieldDecoratorContextStub<entityType, valueType | undefined>, c?: any) => void) & {
1215
+ /** Phantom type brand (never set at runtime). Without it the decorator is a
1216
+ * bare function type, which TypeScript skips as an inference candidate when
1217
+ * a `Fields.xxx()` call appears in a generically-inferred object alongside a
1218
+ * context-sensitive function — e.g. UI schemas built from field decorators. */
1219
+ readonly __fieldValueType?: valueType;
1220
+ };
1215
1221
  export declare const flags: {
1216
1222
  error500RetryCount: number;
1217
1223
  };