remult 3.3.13 → 3.3.15

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
+ }
@@ -170,7 +170,15 @@ export class Remult {
170
170
  return this.isAllowed(allowed);
171
171
  return undefined;
172
172
  }
173
+ /**
174
+ * @deprecated In SvelteKit, loads run in parallel, so reassigning the shared
175
+ * `remult` data provider here leaks across loads. Scope the fetch to the read
176
+ * with `withRemult` instead, e.g.
177
+ * `withRemult((r) => r.repo(X).find(), { dataProvider: new RestDataProvider(() => ({ httpClient: event.fetch })) })`.
178
+ * See the SvelteKit "Universal load & SSR" doc.
179
+ */
173
180
  useFetch(fetch) {
181
+ console.warn('[remult] `useFetch` is deprecated, see https://remult.dev/docs/installation/framework/sveltekit#extra-universal-load-ssr');
174
182
  this.dataProvider = new RestDataProvider(() => ({
175
183
  httpClient: fetch,
176
184
  }));
@@ -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;
@@ -62,6 +62,11 @@ export class RemultProxy {
62
62
  clearAllCache() {
63
63
  return remultStatic.remultFactory().clearAllCache();
64
64
  }
65
+ /**
66
+ * @deprecated Mutating the shared request `remult` is unsafe (it can affect a
67
+ * concurrent `+page.server.ts` on the server). Scope the rest fetch to the read
68
+ * with `withRemult` instead - see the SvelteKit "Universal load & SSR" doc.
69
+ */
65
70
  useFetch(args) {
66
71
  return remultStatic.remultFactory().useFetch(args);
67
72
  }
@@ -71,9 +76,11 @@ export class RemultProxy {
71
76
  let self = remultStatic;
72
77
  let entityCache = this.repoCache.get(args[0]);
73
78
  if (!entityCache) {
74
- this.repoCache.set(args[0], (entityCache = new Map()));
79
+ this.repoCache.set(args[0], (entityCache = { byDataProvider: new WeakMap() }));
75
80
  }
76
- let result = entityCache.get(args[1]);
81
+ let result = args[1]
82
+ ? entityCache.byDataProvider.get(args[1])
83
+ : entityCache.default;
77
84
  if (result)
78
85
  return result;
79
86
  result = {
@@ -184,7 +191,10 @@ export class RemultProxy {
184
191
  .repo(...args)
185
192
  .update(a, b, c),
186
193
  };
187
- entityCache.set(args[1], result);
194
+ if (args[1])
195
+ entityCache.byDataProvider.set(args[1], result);
196
+ else
197
+ entityCache.default = result;
188
198
  return result;
189
199
  };
190
200
  get user() {
@@ -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.13",
3
+ "version": "3.3.15",
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
+ }());
package/src/context.d.ts CHANGED
@@ -63,6 +63,13 @@ export declare class Remult {
63
63
  * [Allowed](https://remult.dev/docs/allowed.html)
64
64
  */
65
65
  isAllowedForInstance(instance: any, allowed?: AllowedForInstance<any>): boolean;
66
+ /**
67
+ * @deprecated In SvelteKit, loads run in parallel, so reassigning the shared
68
+ * `remult` data provider here leaks across loads. Scope the fetch to the read
69
+ * with `withRemult` instead, e.g.
70
+ * `withRemult((r) => r.repo(X).find(), { dataProvider: new RestDataProvider(() => ({ httpClient: event.fetch })) })`.
71
+ * See the SvelteKit "Universal load & SSR" doc.
72
+ */
66
73
  useFetch(fetch: ApiClient['httpClient']): void;
67
74
  /** The current data provider */
68
75
  dataProvider: DataProvider;
package/src/context.js CHANGED
@@ -274,7 +274,15 @@ var Remult = /** @class */ (function () {
274
274
  return this.isAllowed(allowed);
275
275
  return undefined;
276
276
  };
277
+ /**
278
+ * @deprecated In SvelteKit, loads run in parallel, so reassigning the shared
279
+ * `remult` data provider here leaks across loads. Scope the fetch to the read
280
+ * with `withRemult` instead, e.g.
281
+ * `withRemult((r) => r.repo(X).find(), { dataProvider: new RestDataProvider(() => ({ httpClient: event.fetch })) })`.
282
+ * See the SvelteKit "Universal load & SSR" doc.
283
+ */
277
284
  Remult.prototype.useFetch = function (fetch) {
285
+ console.warn('[remult] `useFetch` is deprecated, see https://remult.dev/docs/installation/framework/sveltekit#extra-universal-load-ssr');
278
286
  this.dataProvider = new rest_data_provider_js_1.RestDataProvider(function () { return ({
279
287
  httpClient: fetch,
280
288
  }); });
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;
@@ -21,9 +21,11 @@ var RemultProxy = /** @class */ (function () {
21
21
  var self = remult_static_js_1.remultStatic;
22
22
  var entityCache = _this.repoCache.get(args[0]);
23
23
  if (!entityCache) {
24
- _this.repoCache.set(args[0], (entityCache = new Map()));
24
+ _this.repoCache.set(args[0], (entityCache = { byDataProvider: new WeakMap() }));
25
25
  }
26
- var result = entityCache.get(args[1]);
26
+ var result = args[1]
27
+ ? entityCache.byDataProvider.get(args[1])
28
+ : entityCache.default;
27
29
  if (result)
28
30
  return result;
29
31
  result = (_a = {
@@ -245,7 +247,10 @@ var RemultProxy = /** @class */ (function () {
245
247
  .repo.apply(_a, tslib_1.__spreadArray([], tslib_1.__read(args), false)).update(a, b, c);
246
248
  },
247
249
  _a);
248
- entityCache.set(args[1], result);
250
+ if (args[1])
251
+ entityCache.byDataProvider.set(args[1], result);
252
+ else
253
+ entityCache.default = result;
249
254
  return result;
250
255
  };
251
256
  }
@@ -336,6 +341,11 @@ var RemultProxy = /** @class */ (function () {
336
341
  RemultProxy.prototype.clearAllCache = function () {
337
342
  return remult_static_js_1.remultStatic.remultFactory().clearAllCache();
338
343
  };
344
+ /**
345
+ * @deprecated Mutating the shared request `remult` is unsafe (it can affect a
346
+ * concurrent `+page.server.ts` on the server). Scope the rest fetch to the read
347
+ * with `withRemult` instead - see the SvelteKit "Universal load & SSR" doc.
348
+ */
339
349
  RemultProxy.prototype.useFetch = function (args) {
340
350
  return remult_static_js_1.remultStatic.remultFactory().useFetch(args);
341
351
  };
@@ -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 {