remult 3.4.0-next.9 → 3.4.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.
- package/esm/internals.js +1 -0
- package/esm/remult-knex/index.js +46 -73
- package/esm/src/data-api.js +42 -31
- package/esm/src/data-providers/sql-database.js +44 -39
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +35 -34
- package/esm/src/filter/walk-filter.js +29 -0
- package/esm/src/remult-proxy.js +6 -6
- package/esm/src/remult3/RepositoryImplementation.js +29 -14
- package/esm/src/valueConverters.js +11 -0
- package/index.d.ts +1 -1
- package/internals.d.ts +1 -0
- package/internals.js +4 -1
- package/package.json +1 -1
- package/remult-knex/index.d.ts +3 -2
- package/remult-knex/index.js +118 -164
- package/src/data-api.d.ts +4 -0
- package/src/data-api.js +51 -48
- package/src/data-providers/sql-database.d.ts +16 -7
- package/src/data-providers/sql-database.js +100 -90
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +8 -3
- package/src/filter/filter-consumer-bridge-to-sql-request.js +40 -47
- package/src/filter/walk-filter.d.ts +3 -0
- package/src/filter/walk-filter.js +56 -0
- package/src/remult-proxy.js +12 -27
- package/src/remult3/RepositoryImplementation.d.ts +1 -0
- package/src/remult3/RepositoryImplementation.js +29 -14
- package/src/remult3/remult3.d.ts +37 -0
- package/src/sql-command.d.ts +5 -0
- package/src/valueConverters.d.ts +4 -1
- package/src/valueConverters.js +13 -0
package/src/data-api.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ export declare class DataApi<T = unknown> {
|
|
|
24
24
|
getArray(response: DataApiResponse, request: DataApiRequest, body?: any): Promise<void>;
|
|
25
25
|
liveQuery(response: DataApiResponse, request: DataApiRequest, body: any, serializeContext: () => Promise<any>, queryChannel: string): Promise<void>;
|
|
26
26
|
private buildWhere;
|
|
27
|
+
private fieldIsInApi;
|
|
28
|
+
private fieldsInApi;
|
|
29
|
+
private omitFieldsHiddenFromApi;
|
|
30
|
+
private handleError;
|
|
27
31
|
private doOnId;
|
|
28
32
|
updateManyThroughPutRequest(response: DataApiResponse, request: DataApiRequest, body: any): Promise<void>;
|
|
29
33
|
updateManyImplementation(response: DataApiResponse, request: DataApiRequest, body: {
|
package/src/data-api.js
CHANGED
|
@@ -8,6 +8,7 @@ var context_js_1 = require("./context.js");
|
|
|
8
8
|
var data_interfaces_js_1 = require("./data-interfaces.js");
|
|
9
9
|
var rest_data_provider_js_1 = require("./data-providers/rest-data-provider.js");
|
|
10
10
|
var filter_interfaces_js_1 = require("./filter/filter-interfaces.js");
|
|
11
|
+
var walk_filter_js_1 = require("./filter/walk-filter.js");
|
|
11
12
|
var relationInfoMember_js_1 = require("./remult3/relationInfoMember.js");
|
|
12
13
|
var remult3_js_1 = require("./remult3/remult3.js");
|
|
13
14
|
var server_action_js_1 = require("./server-action.js");
|
|
@@ -41,10 +42,7 @@ var DataApi = /** @class */ (function () {
|
|
|
41
42
|
case 4: return [2 /*return*/, this.getArray(res, req, undefined)];
|
|
42
43
|
case 5:
|
|
43
44
|
err_1 = _d.sent();
|
|
44
|
-
|
|
45
|
-
res.forbidden(err_1.message);
|
|
46
|
-
else
|
|
47
|
-
res.error(err_1, this.repository.metadata);
|
|
45
|
+
this.handleError(res, err_1);
|
|
48
46
|
return [3 /*break*/, 6];
|
|
49
47
|
case 6: return [2 /*return*/];
|
|
50
48
|
}
|
|
@@ -115,10 +113,7 @@ var DataApi = /** @class */ (function () {
|
|
|
115
113
|
case 14: return [3 /*break*/, 16];
|
|
116
114
|
case 15:
|
|
117
115
|
err_2 = _f.sent();
|
|
118
|
-
|
|
119
|
-
res.forbidden(err_2.message);
|
|
120
|
-
else
|
|
121
|
-
res.error(err_2, this.repository.metadata);
|
|
116
|
+
this.handleError(res, err_2);
|
|
122
117
|
return [3 /*break*/, 16];
|
|
123
118
|
case 16: return [2 /*return*/];
|
|
124
119
|
}
|
|
@@ -232,10 +227,7 @@ var DataApi = /** @class */ (function () {
|
|
|
232
227
|
})];
|
|
233
228
|
case 3:
|
|
234
229
|
err_3 = _c.sent();
|
|
235
|
-
|
|
236
|
-
response.forbidden();
|
|
237
|
-
else
|
|
238
|
-
response.error(err_3, this.repository.metadata);
|
|
230
|
+
this.handleError(response, err_3);
|
|
239
231
|
return [3 /*break*/, 4];
|
|
240
232
|
case 4: return [2 /*return*/];
|
|
241
233
|
}
|
|
@@ -287,7 +279,7 @@ var DataApi = /** @class */ (function () {
|
|
|
287
279
|
return [3 /*break*/, 5];
|
|
288
280
|
case 4:
|
|
289
281
|
err_4 = _f.sent();
|
|
290
|
-
|
|
282
|
+
this.handleError(response, err_4);
|
|
291
283
|
return [3 /*break*/, 5];
|
|
292
284
|
case 5: return [2 /*return*/];
|
|
293
285
|
}
|
|
@@ -359,7 +351,7 @@ var DataApi = /** @class */ (function () {
|
|
|
359
351
|
case 2: return [2 /*return*/, _a.sent()];
|
|
360
352
|
case 3:
|
|
361
353
|
err_5 = _a.sent();
|
|
362
|
-
|
|
354
|
+
this.handleError(response, err_5);
|
|
363
355
|
return [3 /*break*/, 4];
|
|
364
356
|
case 4: return [2 /*return*/];
|
|
365
357
|
}
|
|
@@ -389,16 +381,15 @@ var DataApi = /** @class */ (function () {
|
|
|
389
381
|
var findOptions, orderBy, _a, _b, element, direction, group, min, max, result, minMax, _loop_1, _c, _d, element;
|
|
390
382
|
var e_3, _e, _f, _g, e_4, _h;
|
|
391
383
|
var _this = this;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
switch (_r.label) {
|
|
384
|
+
return tslib_1.__generator(this, function (_j) {
|
|
385
|
+
switch (_j.label) {
|
|
395
386
|
case 0:
|
|
396
387
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
397
388
|
throw new server_action_js_1.ForbiddenError();
|
|
398
389
|
}
|
|
399
390
|
return [4 /*yield*/, this.findOptionsFromRequest(request, body)];
|
|
400
391
|
case 1:
|
|
401
|
-
findOptions =
|
|
392
|
+
findOptions = _j.sent();
|
|
402
393
|
orderBy = {};
|
|
403
394
|
if (body === null || body === void 0 ? void 0 : body.orderBy) {
|
|
404
395
|
try {
|
|
@@ -406,7 +397,7 @@ var DataApi = /** @class */ (function () {
|
|
|
406
397
|
element = _b.value;
|
|
407
398
|
direction = element.isDescending ? 'desc' : 'asc';
|
|
408
399
|
if (element.operation !== 'count' &&
|
|
409
|
-
!
|
|
400
|
+
!this.fieldIsInApi(element.field))
|
|
410
401
|
continue;
|
|
411
402
|
switch (element.operation) {
|
|
412
403
|
case undefined:
|
|
@@ -429,15 +420,9 @@ var DataApi = /** @class */ (function () {
|
|
|
429
420
|
finally { if (e_3) throw e_3.error; }
|
|
430
421
|
}
|
|
431
422
|
}
|
|
432
|
-
group = (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
min = (_l = body === null || body === void 0 ? void 0 : body.min) === null || _l === void 0 ? void 0 : _l.filter(function (x) {
|
|
436
|
-
return _this.repository.fields.find(x).includedInApi();
|
|
437
|
-
});
|
|
438
|
-
max = (_m = body === null || body === void 0 ? void 0 : body.max) === null || _m === void 0 ? void 0 : _m.filter(function (x) {
|
|
439
|
-
return _this.repository.fields.find(x).includedInApi();
|
|
440
|
-
});
|
|
423
|
+
group = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.groupBy);
|
|
424
|
+
min = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.min);
|
|
425
|
+
max = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.max);
|
|
441
426
|
return [4 /*yield*/, this.repository.groupBy((_g = {
|
|
442
427
|
where: findOptions.where,
|
|
443
428
|
limit: findOptions.limit,
|
|
@@ -446,21 +431,15 @@ var DataApi = /** @class */ (function () {
|
|
|
446
431
|
//@ts-expect-error internal key
|
|
447
432
|
_g[remult3_js_1.GroupByForApiKey] = true,
|
|
448
433
|
_g.group = group,
|
|
449
|
-
_g.sum = (
|
|
450
|
-
|
|
451
|
-
}),
|
|
452
|
-
_g.avg = (_p = body === null || body === void 0 ? void 0 : body.avg) === null || _p === void 0 ? void 0 : _p.filter(function (x) {
|
|
453
|
-
return _this.repository.fields.find(x).includedInApi();
|
|
454
|
-
}),
|
|
434
|
+
_g.sum = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.sum),
|
|
435
|
+
_g.avg = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.avg),
|
|
455
436
|
_g.min = min,
|
|
456
437
|
_g.max = max,
|
|
457
|
-
_g.distinctCount = (
|
|
458
|
-
return _this.repository.fields.find(x).includedInApi();
|
|
459
|
-
}),
|
|
438
|
+
_g.distinctCount = this.fieldsInApi(body === null || body === void 0 ? void 0 : body.distinctCount),
|
|
460
439
|
_g.orderBy = orderBy,
|
|
461
440
|
_g))];
|
|
462
441
|
case 2:
|
|
463
|
-
result =
|
|
442
|
+
result = _j.sent();
|
|
464
443
|
if (group)
|
|
465
444
|
result.forEach(function (x) {
|
|
466
445
|
var e_5, _a;
|
|
@@ -566,7 +545,7 @@ var DataApi = /** @class */ (function () {
|
|
|
566
545
|
sort = request.get('_sort');
|
|
567
546
|
if (sort != undefined) {
|
|
568
547
|
dir = request.get('_order');
|
|
569
|
-
findOptions.orderBy = determineSort(sort, dir);
|
|
548
|
+
findOptions.orderBy = this.omitFieldsHiddenFromApi(determineSort(sort, dir));
|
|
570
549
|
}
|
|
571
550
|
limit = +request.get('_limit');
|
|
572
551
|
if (!limit && DataApi.defaultGetLimit)
|
|
@@ -664,10 +643,7 @@ var DataApi = /** @class */ (function () {
|
|
|
664
643
|
return [3 /*break*/, 4];
|
|
665
644
|
case 3:
|
|
666
645
|
err_6 = _a.sent();
|
|
667
|
-
|
|
668
|
-
response.forbidden(err_6.message);
|
|
669
|
-
else
|
|
670
|
-
response.error(err_6, this.repository.metadata);
|
|
646
|
+
this.handleError(response, err_6);
|
|
671
647
|
return [3 /*break*/, 4];
|
|
672
648
|
case 4: return [2 /*return*/];
|
|
673
649
|
}
|
|
@@ -711,10 +687,7 @@ var DataApi = /** @class */ (function () {
|
|
|
711
687
|
return [3 /*break*/, 6];
|
|
712
688
|
case 5:
|
|
713
689
|
err_7 = _b.sent();
|
|
714
|
-
|
|
715
|
-
response.forbidden();
|
|
716
|
-
else
|
|
717
|
-
response.error(err_7, this.repository.metadata);
|
|
690
|
+
this.handleError(response, err_7);
|
|
718
691
|
return [3 /*break*/, 6];
|
|
719
692
|
case 6: return [2 /*return*/];
|
|
720
693
|
}
|
|
@@ -755,6 +728,7 @@ var DataApi = /** @class */ (function () {
|
|
|
755
728
|
return result;
|
|
756
729
|
},
|
|
757
730
|
});
|
|
731
|
+
f_1 = this.omitFieldsHiddenFromApi(f_1);
|
|
758
732
|
if (!this.repository.metadata.options.apiPreprocessFilter) return [3 /*break*/, 5];
|
|
759
733
|
return [4 /*yield*/, this.repository.metadata.options.apiPreprocessFilter(f_1, {
|
|
760
734
|
metadata: this.repository.metadata,
|
|
@@ -775,6 +749,35 @@ var DataApi = /** @class */ (function () {
|
|
|
775
749
|
});
|
|
776
750
|
});
|
|
777
751
|
};
|
|
752
|
+
// A hidden field is not on the api, same as a field that is not on the entity.
|
|
753
|
+
DataApi.prototype.fieldIsInApi = function (key) {
|
|
754
|
+
var _a;
|
|
755
|
+
return ((_a = this.repository.fields.find(key)) === null || _a === void 0 ? void 0 : _a.includedInApi()) === true;
|
|
756
|
+
};
|
|
757
|
+
DataApi.prototype.fieldsInApi = function (keys) {
|
|
758
|
+
var _this = this;
|
|
759
|
+
return keys === null || keys === void 0 ? void 0 : keys.filter(function (key) {
|
|
760
|
+
return _this.fieldIsInApi(key);
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
// Drop a field the api hides. Same as a field that is not on the entity:
|
|
764
|
+
// refusing it would confirm the name exists.
|
|
765
|
+
DataApi.prototype.omitFieldsHiddenFromApi = function (filterOrSort) {
|
|
766
|
+
var fields = this.repository.metadata.fields;
|
|
767
|
+
return (0, walk_filter_js_1.walkFilter)(filterOrSort, function (key, value) {
|
|
768
|
+
var _a;
|
|
769
|
+
if (!key.startsWith(filter_interfaces_js_1.customUrlToken) &&
|
|
770
|
+
((_a = fields.find(key)) === null || _a === void 0 ? void 0 : _a.includedInApi()) === false)
|
|
771
|
+
return walk_filter_js_1.omitFilterField;
|
|
772
|
+
return value;
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
DataApi.prototype.handleError = function (response, err) {
|
|
776
|
+
if (err.isForbiddenError)
|
|
777
|
+
response.forbidden(err.message);
|
|
778
|
+
else
|
|
779
|
+
response.error(err, this.repository.metadata);
|
|
780
|
+
};
|
|
778
781
|
DataApi.prototype.doOnId = function (response, id, what) {
|
|
779
782
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
780
783
|
var where, _a, _b, err_8;
|
|
@@ -917,7 +920,7 @@ var DataApi = /** @class */ (function () {
|
|
|
917
920
|
case 2: return [2 /*return*/, _a.sent()];
|
|
918
921
|
case 3:
|
|
919
922
|
err_9 = _a.sent();
|
|
920
|
-
|
|
923
|
+
this.handleError(response, err_9);
|
|
921
924
|
return [3 /*break*/, 4];
|
|
922
925
|
case 4: return [2 /*return*/];
|
|
923
926
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DataProvider, EntityDataProvider, EntityDataProviderGroupByOptions, EntityDataProviderFindOptions } from '../data-interfaces.js';
|
|
2
|
-
import type { HasWrapIdentifier, SqlCommand, SqlCommandFactory, SqlCommandWithParameters, SqlImplementation, SqlResult } from '../sql-command.js';
|
|
2
|
+
import type { HasJsonColumnStorage, HasWrapIdentifier, JsonColumnStorage, SqlCommand, SqlCommandFactory, SqlCommandWithParameters, SqlImplementation, SqlResult } from '../sql-command.js';
|
|
3
3
|
import type { FieldMetadata } from '../column-interfaces.js';
|
|
4
4
|
import type { CustomSqlFilterBuilderFunction, EntityDbNamesBase } from '../filter/filter-consumer-bridge-to-sql-request.js';
|
|
5
|
-
import { type EntityFilter, type EntityMetadata, type FindOptions, type GroupByOptions, type GroupByResult, type MembersOnly, type NumericKeys } from '../remult3/remult3.js';
|
|
5
|
+
import { type EntityFilter, type EntityMetadata, type FindOptions, type GroupByOptions, type GroupByResult, type MembersOnly, type NumericKeys, type Repository } from '../remult3/remult3.js';
|
|
6
6
|
import type { RepositoryOverloads } from '../remult3/RepositoryImplementation.js';
|
|
7
7
|
import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from '../../migrations/migration-types.js';
|
|
8
8
|
/**
|
|
@@ -12,7 +12,7 @@ import type { CanBuildMigrations, MigrationBuilder, MigrationCode } from '../../
|
|
|
12
12
|
* @see [Connecting a Database](https://remult.dev/docs/quickstart#connecting-a-database)
|
|
13
13
|
|
|
14
14
|
*/
|
|
15
|
-
export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, CanBuildMigrations, SqlCommandFactory {
|
|
15
|
+
export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, HasJsonColumnStorage, CanBuildMigrations, SqlCommandFactory {
|
|
16
16
|
private sql;
|
|
17
17
|
/**
|
|
18
18
|
* Gets the SQL database from the data provider.
|
|
@@ -38,6 +38,7 @@ export declare class SqlDatabase implements DataProvider, HasWrapIdentifier, Can
|
|
|
38
38
|
* Wraps an identifier with the database's identifier syntax.
|
|
39
39
|
*/
|
|
40
40
|
wrapIdentifier: (name: string) => string;
|
|
41
|
+
get jsonColumnStorage(): JsonColumnStorage;
|
|
41
42
|
ensureSchema(entities: EntityMetadata[]): Promise<void>;
|
|
42
43
|
/**
|
|
43
44
|
* Gets the entity data provider.
|
|
@@ -121,20 +122,28 @@ export interface RawSelectOptions {
|
|
|
121
122
|
wrapIdentifier?: (name: string) => string;
|
|
122
123
|
/** Required only with `limit`: `(limit, offset) => 'limit 10 offset 20'`. */
|
|
123
124
|
limitSyntax?: (limit: number, offset: number) => string;
|
|
125
|
+
/**
|
|
126
|
+
* JSON columns are stored as text (SQL Server, sqlite, mysql).
|
|
127
|
+
* Defaults to the repository's data provider.
|
|
128
|
+
* Set when `sqlCommand` runs against a different database than that provider.
|
|
129
|
+
*/
|
|
130
|
+
jsonAsString?: boolean;
|
|
124
131
|
}
|
|
132
|
+
export declare function jsonColumnStorageOf(r: Repository<any>): JsonColumnStorage;
|
|
125
133
|
export declare function getRowAfterUpdate<entityType>(meta: EntityMetadata<entityType>, dataProvider: EntityDataProvider, data: any, id: any, operation: string): any;
|
|
126
134
|
/** The select that `find` runs, and the mapping of one result row to field values. With `aliasColumns`, every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
|
|
127
|
-
export declare function buildFindSql(entity: EntityMetadata, options: EntityDataProviderFindOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullsFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, { aliasColumns }?: {
|
|
128
|
-
aliasColumns?: boolean
|
|
135
|
+
export declare function buildFindSql(entity: EntityMetadata, options: EntityDataProviderFindOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullsFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, { aliasColumns, jsonColumnStorage, }?: {
|
|
136
|
+
aliasColumns?: boolean;
|
|
137
|
+
jsonColumnStorage?: JsonColumnStorage;
|
|
129
138
|
}): Promise<{
|
|
130
139
|
select: string;
|
|
131
140
|
colKeys: FieldMetadata<unknown, unknown>[];
|
|
132
141
|
/** `valueAt` reads one column of a result row, by its position in the select or by its alias (the field key). */
|
|
133
142
|
toResult(valueAt: (indexInSelect: number, alias: string) => any): any;
|
|
134
143
|
}>;
|
|
135
|
-
export declare function groupByImpl(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommand, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<any[]>;
|
|
144
|
+
export declare function groupByImpl(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommand, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, jsonColumnStorage?: JsonColumnStorage): Promise<any[]>;
|
|
136
145
|
/** The select that `groupBy` runs, and the mapping of one result row back to a `GroupByResult`. Every column is aliased through `wrapIdentifier`, so a row can be read by alias as well as by position. */
|
|
137
|
-
export declare function buildGroupBySql(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string): Promise<{
|
|
146
|
+
export declare function buildGroupBySql(options: EntityDataProviderGroupByOptions | undefined, e: EntityDbNamesBase, r: SqlCommandWithParameters, orderByNullFirst: boolean | undefined, limitSyntax: (limit: number, offset: number) => string, jsonColumnStorage?: JsonColumnStorage): Promise<{
|
|
138
147
|
select: string;
|
|
139
148
|
/** `valueAt` reads one column of a result row, by its position in the select or by its alias. */
|
|
140
149
|
toResult(valueAt: (indexInSelect: number, alias: string) => any): any;
|