remult 3.3.12 → 3.3.14

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.
@@ -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
  }));
@@ -113,9 +113,16 @@ export class DataApi {
113
113
  }
114
114
  try {
115
115
  let { aggregate, ...rest } = body;
116
+ // The aggregate summarizes the whole filtered set, so it must not inherit
117
+ // the items query's paging/sorting/selection.
118
+ const aggregateRequest = {
119
+ get: (key) => ['_limit', '_page', '_sort', '_order', '_select'].includes(key)
120
+ ? undefined
121
+ : request?.get(key),
122
+ };
116
123
  let [{ r }, [aggregates]] = await Promise.all([
117
124
  this.getArrayImpl(request, rest),
118
- this.groupBy(request, { ...aggregate, where: body.where }),
125
+ this.groupBy(aggregateRequest, { ...aggregate, where: body.where }),
119
126
  ]);
120
127
  return {
121
128
  items: r,
@@ -545,6 +545,7 @@ export function getRowAfterUpdate(meta, dataProvider, data, id, operation) {
545
545
  export async function groupByImpl(options, e, r, orderByNullFirst, limitSyntax) {
546
546
  let select = 'select count(*) as count';
547
547
  let groupBy = '';
548
+ const groupByCols = [];
548
549
  const processResultRow = [];
549
550
  processResultRow.push((sqlVal, theResult) => {
550
551
  theResult[GroupByCountMember] = Number(sqlVal);
@@ -562,6 +563,7 @@ export async function groupByImpl(options, e, r, orderByNullFirst, limitSyntax)
562
563
  else
563
564
  groupBy += ', ';
564
565
  groupBy += e.$dbNameOf(x);
566
+ groupByCols.push(e.$dbNameOf(x));
565
567
  }
566
568
  processResultRow.push((sqlResult, theResult) => {
567
569
  theResult[x.key] = x.valueConverter.fromDb(sqlResult);
@@ -625,7 +627,13 @@ export async function groupByImpl(options, e, r, orderByNullFirst, limitSyntax)
625
627
  if (orderBy)
626
628
  select += orderBy;
627
629
  }
628
- if (options?.limit) {
630
+ // Skip paging for a pure aggregate (single row); OFFSET/FETCH without an
631
+ // ORDER BY is also invalid on SQL Server.
632
+ if (options?.limit && groupByCols.length) {
633
+ // SQL Server requires an ORDER BY alongside OFFSET/FETCH; default to the
634
+ // group columns so paging is valid and deterministic on every dialect.
635
+ if (!orderBy)
636
+ select += ' order by ' + groupByCols.join(', ');
629
637
  let page = 1;
630
638
  if (options.page)
631
639
  page = options.page;
@@ -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() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.3.12",
3
+ "version": "3.3.14",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
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
@@ -187,7 +187,7 @@ var DataApi = /** @class */ (function () {
187
187
  };
188
188
  DataApi.prototype.query = function (response, request, body) {
189
189
  return tslib_1.__awaiter(this, void 0, void 0, function () {
190
- var aggregate, rest, _a, r, _b, aggregates, err_2;
190
+ var aggregate, rest, aggregateRequest, _a, r, _b, aggregates, err_2;
191
191
  return tslib_1.__generator(this, function (_c) {
192
192
  switch (_c.label) {
193
193
  case 0:
@@ -198,10 +198,20 @@ var DataApi = /** @class */ (function () {
198
198
  _c.label = 1;
199
199
  case 1:
200
200
  _c.trys.push([1, 3, , 4]);
201
- aggregate = body.aggregate, rest = tslib_1.__rest(body, ["aggregate"]);
201
+ aggregate = body.aggregate, rest = tslib_1.__rest(body
202
+ // The aggregate summarizes the whole filtered set, so it must not inherit
203
+ // the items query's paging/sorting/selection.
204
+ , ["aggregate"]);
205
+ aggregateRequest = {
206
+ get: function (key) {
207
+ return ['_limit', '_page', '_sort', '_order', '_select'].includes(key)
208
+ ? undefined
209
+ : request === null || request === void 0 ? void 0 : request.get(key);
210
+ },
211
+ };
202
212
  return [4 /*yield*/, Promise.all([
203
213
  this.getArrayImpl(request, rest),
204
- this.groupBy(request, tslib_1.__assign(tslib_1.__assign({}, aggregate), { where: body.where })),
214
+ this.groupBy(aggregateRequest, tslib_1.__assign(tslib_1.__assign({}, aggregate), { where: body.where })),
205
215
  ])];
206
216
  case 2:
207
217
  _a = tslib_1.__read.apply(void 0, [_c.sent(), 2]), r = _a[0].r, _b = tslib_1.__read(_a[1], 1), aggregates = _b[0];
@@ -843,13 +843,14 @@ function groupByImpl(options, e, r, orderByNullFirst, limitSyntax) {
843
843
  ? "count (distinct ".concat(dbName, ")")
844
844
  : "".concat(operator, "( ").concat(dbName, " )");
845
845
  }
846
- var select, groupBy, processResultRow, _loop_1, _a, _b, x, _loop_2, GroupByOperators_1, GroupByOperators_1_1, operator, e_8_1, where, _c, orderBy, _d, _e, x, field, _f, e_9_1, page, result;
846
+ var select, groupBy, groupByCols, processResultRow, _loop_1, _a, _b, x, _loop_2, GroupByOperators_1, GroupByOperators_1_1, operator, e_8_1, where, _c, orderBy, _d, _e, x, field, _f, e_9_1, page, result;
847
847
  var e_10, _g, e_8, _h, e_9, _j;
848
848
  return tslib_1.__generator(this, function (_k) {
849
849
  switch (_k.label) {
850
850
  case 0:
851
851
  select = 'select count(*) as count';
852
852
  groupBy = '';
853
+ groupByCols = [];
853
854
  processResultRow = [];
854
855
  processResultRow.push(function (sqlVal, theResult) {
855
856
  theResult[remult3_js_1.GroupByCountMember] = Number(sqlVal);
@@ -867,6 +868,7 @@ function groupByImpl(options, e, r, orderByNullFirst, limitSyntax) {
867
868
  else
868
869
  groupBy += ', ';
869
870
  groupBy += e.$dbNameOf(x);
871
+ groupByCols.push(e.$dbNameOf(x));
870
872
  }
871
873
  processResultRow.push(function (sqlResult, theResult) {
872
874
  theResult[x.key] = x.valueConverter.fromDb(sqlResult);
@@ -1049,7 +1051,13 @@ function groupByImpl(options, e, r, orderByNullFirst, limitSyntax) {
1049
1051
  select += orderBy;
1050
1052
  _k.label = 20;
1051
1053
  case 20:
1052
- if (options === null || options === void 0 ? void 0 : options.limit) {
1054
+ // Skip paging for a pure aggregate (single row); OFFSET/FETCH without an
1055
+ // ORDER BY is also invalid on SQL Server.
1056
+ if ((options === null || options === void 0 ? void 0 : options.limit) && groupByCols.length) {
1057
+ // SQL Server requires an ORDER BY alongside OFFSET/FETCH; default to the
1058
+ // group columns so paging is valid and deterministic on every dialect.
1059
+ if (!orderBy)
1060
+ select += ' order by ' + groupByCols.join(', ');
1053
1061
  page = 1;
1054
1062
  if (options.page)
1055
1063
  page = options.page;
@@ -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
  };