remult 0.25.8 → 0.26.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/README.md +194 -129
- package/esm/graphql.js +131 -59
- package/esm/index.js +1 -1
- package/esm/migrations/compare-migration-snapshots.js +61 -0
- package/esm/migrations/generate-migrations.js +68 -0
- package/esm/migrations/index.js +2 -0
- package/esm/migrations/migrate.js +90 -0
- package/esm/migrations/migration-types.js +36 -0
- package/esm/migrations/update-migrations-ts-file.js +115 -0
- package/esm/postgres/postgres-data-provider.js +24 -3
- package/esm/postgres/schema-builder.js +35 -26
- package/esm/remult-better-sqlite3.js +47 -0
- package/esm/remult-bun-sqlite.js +41 -0
- package/esm/remult-knex/index.js +180 -51
- package/esm/remult-mongo.js +2 -2
- package/esm/remult-sql-js.js +3 -78
- package/esm/remult-sqlite-core.js +104 -0
- package/esm/server/expressBridge.js +130 -29
- package/esm/server/initAsyncHooks.js +75 -1
- package/esm/src/CompoundIdField.js +8 -2
- package/esm/src/context.js +20 -24
- package/esm/src/data-api.js +71 -30
- package/esm/src/data-providers/rest-data-provider.js +17 -15
- package/esm/src/data-providers/sql-database.js +32 -7
- package/esm/src/data-providers/web-sql-data-provider.js +1 -9
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +37 -11
- package/esm/src/filter/filter-interfaces.js +229 -17
- package/esm/src/isOfType.js +9 -0
- package/esm/src/remult3/Fields.js +70 -18
- package/esm/src/remult3/RepositoryImplementation.js +47 -21
- package/esm/src/remult3/addValidator.js +15 -0
- package/esm/src/remult3/remult3.js +28 -57
- package/esm/src/server-action.js +4 -4
- package/esm/src/validators.js +6 -3
- package/esm/src/valueConverters.js +1 -1
- package/graphql.js +156 -71
- package/index.d.ts +4 -4
- package/index.js +2 -1
- package/migrations/compare-migration-snapshots.d.ts +27 -0
- package/migrations/compare-migration-snapshots.js +165 -0
- package/migrations/generate-migrations.d.ts +21 -0
- package/migrations/generate-migrations.js +104 -0
- package/migrations/index.d.ts +3 -0
- package/migrations/index.js +7 -0
- package/migrations/migrate.d.ts +27 -0
- package/migrations/migrate.js +175 -0
- package/migrations/migration-types.d.ts +48 -0
- package/migrations/migration-types.js +88 -0
- package/migrations/update-migrations-ts-file.d.ts +1 -0
- package/migrations/update-migrations-ts-file.js +141 -0
- package/package.json +21 -1
- package/postgres/postgres-data-provider.d.ts +8 -3
- package/postgres/postgres-data-provider.js +46 -3
- package/postgres/schema-builder.js +81 -48
- package/remult-better-sqlite3.d.ts +12 -0
- package/remult-better-sqlite3.js +64 -0
- package/remult-bun-sqlite.d.ts +11 -0
- package/remult-bun-sqlite.js +56 -0
- package/remult-knex/index.d.ts +12 -5
- package/remult-knex/index.js +257 -90
- package/remult-mongo.d.ts +2 -2
- package/remult-mongo.js +2 -2
- package/remult-sql-js.d.ts +2 -15
- package/remult-sql-js.js +11 -152
- package/remult-sqlite-core.d.ts +21 -0
- package/remult-sqlite-core.js +217 -0
- package/server/expressBridge.d.ts +30 -1
- package/server/expressBridge.js +140 -32
- package/server/initAsyncHooks.d.ts +16 -0
- package/server/initAsyncHooks.js +128 -2
- package/src/CompoundIdField.js +8 -2
- package/src/column-interfaces.d.ts +9 -3
- package/src/context.d.ts +8 -6
- package/src/context.js +27 -36
- package/src/data-api.d.ts +13 -9
- package/src/data-api.js +184 -135
- package/src/data-providers/rest-data-provider.js +26 -15
- package/src/data-providers/sql-database.d.ts +17 -5
- package/src/data-providers/sql-database.js +31 -7
- package/src/data-providers/web-sql-data-provider.d.ts +1 -2
- package/src/data-providers/web-sql-data-provider.js +4 -8
- package/src/entity.d.ts +64 -6
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +31 -7
- package/src/filter/filter-consumer-bridge-to-sql-request.js +46 -21
- package/src/filter/filter-interfaces.d.ts +106 -39
- package/src/filter/filter-interfaces.js +312 -32
- package/src/isOfType.d.ts +2 -0
- package/src/isOfType.js +14 -0
- package/src/remult3/Fields.d.ts +36 -0
- package/src/remult3/Fields.js +81 -19
- package/src/remult3/RepositoryImplementation.d.ts +7 -3
- package/src/remult3/RepositoryImplementation.js +106 -71
- package/src/remult3/addValidator.d.ts +2 -0
- package/src/remult3/addValidator.js +20 -0
- package/src/remult3/remult3.d.ts +6 -2
- package/src/remult3/remult3.js +28 -57
- package/src/server-action.d.ts +1 -1
- package/src/server-action.js +5 -4
- package/src/sql-command.d.ts +6 -1
- package/src/validators.d.ts +1 -0
- package/src/validators.js +10 -4
- package/src/valueConverters.js +1 -1
package/esm/remult-sql-js.js
CHANGED
|
@@ -1,82 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { isAutoIncrement } from './src/remult3/RepositoryImplementation.js';
|
|
4
|
-
export class SqlJsDataProvider {
|
|
5
|
-
db;
|
|
1
|
+
import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
|
|
2
|
+
export class SqlJsDataProvider extends SqliteCoreDataProvider {
|
|
6
3
|
constructor(db) {
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
getLimitSqlSyntax(limit, offset) {
|
|
10
|
-
return ' limit ' + limit + ' offset ' + offset;
|
|
11
|
-
}
|
|
12
|
-
afterMutation;
|
|
13
|
-
createCommand() {
|
|
14
|
-
return new SqlJsCommand(this.db);
|
|
15
|
-
}
|
|
16
|
-
transaction(action) {
|
|
17
|
-
throw new Error('Method not implemented.');
|
|
18
|
-
}
|
|
19
|
-
async entityIsUsedForTheFirstTime(entity) {
|
|
20
|
-
await this.createTable(entity);
|
|
21
|
-
}
|
|
22
|
-
async ensureSchema(entities) {
|
|
23
|
-
for (const entity of entities) {
|
|
24
|
-
await this.createTable(entity);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async dropTable(entity) {
|
|
28
|
-
let e = await dbNamesOf(entity);
|
|
29
|
-
let sql = 'drop table if exists ' + e.$entityName;
|
|
30
|
-
if (SqlDatabase.LogToConsole)
|
|
31
|
-
console.info(sql);
|
|
32
|
-
await this.createCommand().execute(sql);
|
|
33
|
-
}
|
|
34
|
-
addColumnSqlSyntax(x, dbName) {
|
|
35
|
-
let result = dbName;
|
|
36
|
-
const nullNumber = x.allowNull ? '' : ' default 0 not null';
|
|
37
|
-
if (x.valueType == Date)
|
|
38
|
-
result += ' integer';
|
|
39
|
-
else if (x.valueType == Boolean)
|
|
40
|
-
result += ' integer ' + nullNumber;
|
|
41
|
-
else if (x.valueType == Number) {
|
|
42
|
-
if (!x.valueConverter.fieldTypeInDb)
|
|
43
|
-
result += ' real ' + nullNumber;
|
|
44
|
-
else
|
|
45
|
-
result += ' ' + x.valueConverter.fieldTypeInDb + ' ' + nullNumber;
|
|
46
|
-
}
|
|
47
|
-
else
|
|
48
|
-
result += ' text' + (x.allowNull ? ' ' : " default '' not null ");
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
async createTable(entity) {
|
|
52
|
-
let result = '';
|
|
53
|
-
let e = await dbNamesOf(entity);
|
|
54
|
-
for (const x of entity.fields) {
|
|
55
|
-
if (!isDbReadonly(x, e) || isAutoIncrement(x)) {
|
|
56
|
-
if (result.length != 0)
|
|
57
|
-
result += ',';
|
|
58
|
-
result += '\r\n ';
|
|
59
|
-
if (isAutoIncrement(x)) {
|
|
60
|
-
if (x.key != entity.idMetadata.field.key)
|
|
61
|
-
throw 'in web sql, autoincrement is only allowed for primary key';
|
|
62
|
-
result += e.$dbNameOf(x) + ' integer primary key autoincrement';
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
result += this.addColumnSqlSyntax(x, e.$dbNameOf(x));
|
|
66
|
-
if (x.key == entity.idMetadata.field.key) {
|
|
67
|
-
result += ' primary key';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
let sql = 'create table if not exists ' + e.$entityName + ' (' + result + '\r\n)';
|
|
73
|
-
if (SqlDatabase.LogToConsole)
|
|
74
|
-
console.log(sql);
|
|
75
|
-
await this.createCommand().execute(sql);
|
|
76
|
-
}
|
|
77
|
-
supportsJsonColumnType;
|
|
78
|
-
wrapIdentifier(name) {
|
|
79
|
-
return name;
|
|
4
|
+
super(() => new SqlJsCommand(db), async () => (await db).close());
|
|
80
5
|
}
|
|
81
6
|
}
|
|
82
7
|
class SqlJsCommand {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { SqlDatabase } from './src/data-providers/sql-database.js';
|
|
2
|
+
import { dbNamesOf, shouldNotCreateField, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
3
|
+
import { isAutoIncrement } from './src/remult3/RepositoryImplementation.js';
|
|
4
|
+
export class SqliteCoreDataProvider {
|
|
5
|
+
createCommand;
|
|
6
|
+
end;
|
|
7
|
+
constructor(createCommand, end) {
|
|
8
|
+
this.createCommand = createCommand;
|
|
9
|
+
this.end = end;
|
|
10
|
+
}
|
|
11
|
+
orderByNullsFirst;
|
|
12
|
+
getLimitSqlSyntax(limit, offset) {
|
|
13
|
+
return ' limit ' + limit + ' offset ' + offset;
|
|
14
|
+
}
|
|
15
|
+
afterMutation;
|
|
16
|
+
provideMigrationBuilder(builder) {
|
|
17
|
+
let self = this;
|
|
18
|
+
return {
|
|
19
|
+
createTable: async (entity) => {
|
|
20
|
+
builder.addSql(await self.getCreateTableSql(entity));
|
|
21
|
+
},
|
|
22
|
+
addColumn: async (entity, field) => {
|
|
23
|
+
let e = await dbNamesOf(entity);
|
|
24
|
+
let sql = `alter table ${e.$entityName} add column ${self.addColumnSqlSyntax(field, e.$dbNameOf(field))}`;
|
|
25
|
+
builder.addSql(sql);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async transaction(action) {
|
|
30
|
+
this.createCommand().execute('Begin Transaction');
|
|
31
|
+
try {
|
|
32
|
+
await action(this);
|
|
33
|
+
this.createCommand().execute('Commit');
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
this.createCommand().execute('Rollback');
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async entityIsUsedForTheFirstTime(entity) { }
|
|
41
|
+
async ensureSchema(entities) {
|
|
42
|
+
for (const entity of entities) {
|
|
43
|
+
await this.createTableIfNotExist(entity);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async dropTable(entity) {
|
|
47
|
+
let e = await dbNamesOf(entity);
|
|
48
|
+
let sql = 'drop table if exists ' + e.$entityName;
|
|
49
|
+
if (SqlDatabase.LogToConsole)
|
|
50
|
+
console.info(sql);
|
|
51
|
+
await this.createCommand().execute(sql);
|
|
52
|
+
}
|
|
53
|
+
addColumnSqlSyntax(x, dbName) {
|
|
54
|
+
let result = dbName;
|
|
55
|
+
const nullNumber = x.allowNull ? '' : ' default 0 not null';
|
|
56
|
+
if (x.valueType == Date)
|
|
57
|
+
result += ' integer';
|
|
58
|
+
else if (x.valueType == Boolean)
|
|
59
|
+
result += ' integer ' + nullNumber;
|
|
60
|
+
else if (x.valueType == Number) {
|
|
61
|
+
if (!x.valueConverter.fieldTypeInDb)
|
|
62
|
+
result += ' numeric ' + nullNumber;
|
|
63
|
+
else
|
|
64
|
+
result += ' ' + x.valueConverter.fieldTypeInDb + ' ' + nullNumber;
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
result += ' text' + (x.allowNull ? ' ' : " default '' not null ");
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
async createTableIfNotExist(entity) {
|
|
71
|
+
let sql = await this.getCreateTableSql(entity);
|
|
72
|
+
if (SqlDatabase.LogToConsole)
|
|
73
|
+
console.log(sql);
|
|
74
|
+
await this.createCommand().execute(sql);
|
|
75
|
+
}
|
|
76
|
+
supportsJsonColumnType;
|
|
77
|
+
async getCreateTableSql(entity) {
|
|
78
|
+
let result = '';
|
|
79
|
+
let e = await dbNamesOf(entity);
|
|
80
|
+
for (const x of entity.fields) {
|
|
81
|
+
if (!shouldNotCreateField(x, e) || isAutoIncrement(x)) {
|
|
82
|
+
if (result.length != 0)
|
|
83
|
+
result += ',';
|
|
84
|
+
result += '\r\n ';
|
|
85
|
+
if (isAutoIncrement(x)) {
|
|
86
|
+
if (x.key != entity.idMetadata.field.key)
|
|
87
|
+
throw 'in web sql, autoincrement is only allowed for primary key';
|
|
88
|
+
result += e.$dbNameOf(x) + ' integer primary key autoincrement';
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
result += this.addColumnSqlSyntax(x, e.$dbNameOf(x));
|
|
92
|
+
if (x.key == entity.idMetadata.field.key) {
|
|
93
|
+
result += ' primary key';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let sql = 'create table if not exists ' + e.$entityName + ' (' + result + '\r\n)';
|
|
99
|
+
return sql;
|
|
100
|
+
}
|
|
101
|
+
wrapIdentifier(name) {
|
|
102
|
+
return name;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -13,6 +13,7 @@ import { serverActionField } from '../src/server-action-info.js';
|
|
|
13
13
|
import { initDataProvider } from './initDataProvider.js';
|
|
14
14
|
import { remultStatic } from '../src/remult-static.js';
|
|
15
15
|
import remultAdminHtml from './remult-admin.js';
|
|
16
|
+
import { isOfType } from '../src/isOfType.js';
|
|
16
17
|
export function createRemultServerCore(options, serverCoreOptions) {
|
|
17
18
|
if (!options) {
|
|
18
19
|
options = {};
|
|
@@ -249,8 +250,8 @@ export class RemultServerImplementation {
|
|
|
249
250
|
console.info('[remult] ' + myRoute);
|
|
250
251
|
r.route(myRoute)
|
|
251
252
|
.get(this.process((c, req, res, orig) => dataApiFactory(c).httpGet(res, req, () => this.serializeContext(c))))
|
|
252
|
-
.put(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).
|
|
253
|
-
.delete(this.process(async (c, req, res, orig) => dataApiFactory(c).
|
|
253
|
+
.put(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).updateManyThroughPutRequest(res, req, await this.coreOptions.getRequestBody(orig))))
|
|
254
|
+
.delete(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).deleteMany(res, req, undefined)))
|
|
254
255
|
.post(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).httpPost(res, req, await this.coreOptions.getRequestBody(orig), () => this.serializeContext(c))));
|
|
255
256
|
r.route(myRoute + '/:id')
|
|
256
257
|
//@ts-ignore
|
|
@@ -285,7 +286,7 @@ export class RemultServerImplementation {
|
|
|
285
286
|
genReq.params = req['_tempParams'];
|
|
286
287
|
}
|
|
287
288
|
let myReq = new ExpressRequestBridgeToDataApiRequest(genReq);
|
|
288
|
-
let myRes = new ExpressResponseBridgeToDataApiResponse(origRes, req);
|
|
289
|
+
let myRes = new ExpressResponseBridgeToDataApiResponse(origRes, req, this.options.error);
|
|
289
290
|
try {
|
|
290
291
|
await this.runWithRemult(async (remult) => {
|
|
291
292
|
if (req) {
|
|
@@ -315,7 +316,7 @@ export class RemultServerImplementation {
|
|
|
315
316
|
});
|
|
316
317
|
}
|
|
317
318
|
catch (err) {
|
|
318
|
-
myRes.error(
|
|
319
|
+
myRes.error(err, undefined);
|
|
319
320
|
}
|
|
320
321
|
};
|
|
321
322
|
}
|
|
@@ -466,6 +467,17 @@ export class RemultServerImplementation {
|
|
|
466
467
|
return secureBase(condition, def, item);
|
|
467
468
|
};
|
|
468
469
|
let apiPath = (spec.paths[this.options.rootPath + '/' + key] = {});
|
|
470
|
+
let itemInBody = {
|
|
471
|
+
requestBody: {
|
|
472
|
+
content: {
|
|
473
|
+
'application/json': {
|
|
474
|
+
schema: {
|
|
475
|
+
$ref: '#/components/schemas/' + mutationKey,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
};
|
|
469
481
|
let apiPathWithId = (spec.paths[this.options.rootPath + '/' + key + '/{id}'] = {});
|
|
470
482
|
//https://github.com/2fd/open-api.d.ts
|
|
471
483
|
apiPath.get = secure(meta.options.allowApiRead, true, {
|
|
@@ -518,6 +530,73 @@ export class RemultServerImplementation {
|
|
|
518
530
|
},
|
|
519
531
|
},
|
|
520
532
|
});
|
|
533
|
+
apiPath.delete = secure(meta.options.allowApiDelete, true, {
|
|
534
|
+
description: 'deletes row of ' +
|
|
535
|
+
key +
|
|
536
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)',
|
|
537
|
+
parameters: [...parameters],
|
|
538
|
+
responses: {
|
|
539
|
+
'400': {
|
|
540
|
+
description: 'Error: Bad Request',
|
|
541
|
+
content: {
|
|
542
|
+
'application/json': {
|
|
543
|
+
schema: {
|
|
544
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
'200': {
|
|
550
|
+
description: 'returns the number of deleted rows ' + key,
|
|
551
|
+
content: {
|
|
552
|
+
'application/json': {
|
|
553
|
+
schema: {
|
|
554
|
+
type: 'object',
|
|
555
|
+
properties: {
|
|
556
|
+
deleted: {
|
|
557
|
+
type: 'number',
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
apiPath.put = secure(meta.options.allowApiDelete, true, {
|
|
567
|
+
description: 'updates row of ' +
|
|
568
|
+
key +
|
|
569
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)',
|
|
570
|
+
parameters: [...parameters],
|
|
571
|
+
...itemInBody,
|
|
572
|
+
responses: {
|
|
573
|
+
'400': {
|
|
574
|
+
description: 'Error: Bad Request',
|
|
575
|
+
content: {
|
|
576
|
+
'application/json': {
|
|
577
|
+
schema: {
|
|
578
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
'200': {
|
|
584
|
+
description: 'returns the number of updated rows ' + key,
|
|
585
|
+
content: {
|
|
586
|
+
'application/json': {
|
|
587
|
+
schema: {
|
|
588
|
+
type: 'object',
|
|
589
|
+
properties: {
|
|
590
|
+
updated: {
|
|
591
|
+
type: 'number',
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
});
|
|
521
600
|
let idParameter = {
|
|
522
601
|
name: 'id',
|
|
523
602
|
in: 'path',
|
|
@@ -525,17 +604,6 @@ export class RemultServerImplementation {
|
|
|
525
604
|
required: true,
|
|
526
605
|
schema: { type: 'string' },
|
|
527
606
|
};
|
|
528
|
-
let itemInBody = {
|
|
529
|
-
requestBody: {
|
|
530
|
-
content: {
|
|
531
|
-
'application/json': {
|
|
532
|
-
schema: {
|
|
533
|
-
$ref: '#/components/schemas/' + mutationKey,
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
},
|
|
537
|
-
},
|
|
538
|
-
};
|
|
539
607
|
apiPath.post = secure(meta.options.allowApiInsert, false, {
|
|
540
608
|
//"summary": "insert a " + key,
|
|
541
609
|
//"description": "insert a " + key,
|
|
@@ -673,15 +741,17 @@ class ExpressRequestBridgeToDataApiRequest {
|
|
|
673
741
|
class ExpressResponseBridgeToDataApiResponse {
|
|
674
742
|
r;
|
|
675
743
|
req;
|
|
744
|
+
handleError;
|
|
676
745
|
forbidden() {
|
|
677
|
-
this.
|
|
746
|
+
this.error({ message: 'Forbidden' }, undefined, 403);
|
|
678
747
|
}
|
|
679
748
|
setStatus(status) {
|
|
680
749
|
return this.r.status(status);
|
|
681
750
|
}
|
|
682
|
-
constructor(r, req) {
|
|
751
|
+
constructor(r, req, handleError) {
|
|
683
752
|
this.r = r;
|
|
684
753
|
this.req = req;
|
|
754
|
+
this.handleError = handleError;
|
|
685
755
|
}
|
|
686
756
|
progress(progress) { }
|
|
687
757
|
success(data) {
|
|
@@ -694,22 +764,53 @@ class ExpressResponseBridgeToDataApiResponse {
|
|
|
694
764
|
this.setStatus(204).end();
|
|
695
765
|
}
|
|
696
766
|
notFound() {
|
|
697
|
-
this.
|
|
767
|
+
this.error({ message: 'Forbidden' }, undefined, 404);
|
|
698
768
|
}
|
|
699
|
-
error(
|
|
700
|
-
data = serializeError(
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
769
|
+
async error(exception, entity, httpStatusCode) {
|
|
770
|
+
let data = serializeError(exception);
|
|
771
|
+
if (!httpStatusCode) {
|
|
772
|
+
if (data.httpStatusCode) {
|
|
773
|
+
httpStatusCode = data.httpStatusCode;
|
|
774
|
+
}
|
|
775
|
+
else if (isOfType(exception, 'isForbiddenError') &&
|
|
776
|
+
exception.isForbiddenError) {
|
|
777
|
+
httpStatusCode = 403;
|
|
778
|
+
}
|
|
779
|
+
else if (data.modelState) {
|
|
780
|
+
httpStatusCode = 400;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
httpStatusCode = 500;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
let responseSent = false;
|
|
787
|
+
const sendError = (httpStatusCode, body) => {
|
|
788
|
+
if (responseSent) {
|
|
789
|
+
throw Error('Error response already sent');
|
|
790
|
+
}
|
|
791
|
+
responseSent = true;
|
|
792
|
+
console.error({
|
|
793
|
+
message: body.message,
|
|
794
|
+
httpStatusCode,
|
|
795
|
+
stack: data.stack?.split('\n'),
|
|
796
|
+
url: this.req?.url,
|
|
797
|
+
method: this.req?.method,
|
|
798
|
+
});
|
|
799
|
+
this.setStatus(httpStatusCode).json(body);
|
|
800
|
+
};
|
|
801
|
+
await this.handleError?.({
|
|
802
|
+
httpStatusCode,
|
|
803
|
+
req: this.req,
|
|
804
|
+
entity,
|
|
805
|
+
exception,
|
|
806
|
+
responseBody: data,
|
|
807
|
+
sendError,
|
|
706
808
|
});
|
|
707
|
-
|
|
809
|
+
if (!responseSent) {
|
|
810
|
+
sendError(httpStatusCode, data);
|
|
811
|
+
}
|
|
708
812
|
}
|
|
709
813
|
}
|
|
710
|
-
function throwError() {
|
|
711
|
-
throw 'Invalid';
|
|
712
|
-
}
|
|
713
814
|
class inProcessQueueHandler {
|
|
714
815
|
storage;
|
|
715
816
|
constructor(storage) {
|
|
@@ -6,5 +6,79 @@ export function initAsyncHooks() {
|
|
|
6
6
|
if (init)
|
|
7
7
|
return;
|
|
8
8
|
init = true;
|
|
9
|
-
remultStatic.asyncContext = new RemultAsyncLocalStorage(new
|
|
9
|
+
remultStatic.asyncContext = new RemultAsyncLocalStorage(new AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore());
|
|
10
|
+
let test = new AsyncLocalStorage();
|
|
11
|
+
test.run(1, async () => {
|
|
12
|
+
await Promise.resolve();
|
|
13
|
+
if (test.getStore() === undefined) {
|
|
14
|
+
console.log("async_hooks.AsyncLocalStorage not working, using sequential implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
|
|
15
|
+
remultStatic.asyncContext = new RemultAsyncLocalStorage(new SequentialRemultAsyncLocalStorageCore());
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
|
|
20
|
+
asyncLocalStorage = new AsyncLocalStorage();
|
|
21
|
+
wasImplemented;
|
|
22
|
+
run(store, callback) {
|
|
23
|
+
let r;
|
|
24
|
+
this.asyncLocalStorage.run(store, () => {
|
|
25
|
+
r = new Promise(async (res, rej) => {
|
|
26
|
+
try {
|
|
27
|
+
res(await callback());
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
rej(err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return r;
|
|
35
|
+
}
|
|
36
|
+
getStore() {
|
|
37
|
+
return this.asyncLocalStorage.getStore();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const ignoreInStack = ["AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run", "RemultAsyncLocalStorage.run", "Module.withRemult", "RemultServerImplementation.runWithRemult"];
|
|
41
|
+
export class SequentialRemultAsyncLocalStorageCore {
|
|
42
|
+
wasImplemented;
|
|
43
|
+
async run(store, callback) {
|
|
44
|
+
let log = (msg) => {
|
|
45
|
+
};
|
|
46
|
+
if (false) {
|
|
47
|
+
let stack = new Error().stack;
|
|
48
|
+
let y = stack.split('\n');
|
|
49
|
+
while (y.length > 0 && (y[0].trim().startsWith("Error") || ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
|
|
50
|
+
y.splice(0, 1);
|
|
51
|
+
}
|
|
52
|
+
stack = y.join('\n');
|
|
53
|
+
log = (msg) => {
|
|
54
|
+
console.log(msg, stack);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
log('waiting on ');
|
|
58
|
+
const nextPromise = this.lastPromise.then(async () => {
|
|
59
|
+
log('executing ');
|
|
60
|
+
const previousValue = this.currentValue;
|
|
61
|
+
this.currentValue = store;
|
|
62
|
+
const result = await callback();
|
|
63
|
+
this.currentValue = previousValue;
|
|
64
|
+
return result;
|
|
65
|
+
});
|
|
66
|
+
this.lastPromise = nextPromise.catch(() => {
|
|
67
|
+
log("Error on ");
|
|
68
|
+
return undefined;
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
return await nextPromise;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
log('completed ');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
getStore() {
|
|
78
|
+
return this.currentValue;
|
|
79
|
+
}
|
|
80
|
+
lastPromise = Promise.resolve(undefined);
|
|
81
|
+
currentValue;
|
|
82
|
+
}
|
|
83
|
+
export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore extends AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
|
|
10
84
|
}
|
|
@@ -22,11 +22,17 @@ export class CompoundIdField {
|
|
|
22
22
|
return Promise.resolve('');
|
|
23
23
|
}
|
|
24
24
|
getId(instance) {
|
|
25
|
+
let get = (field) => {
|
|
26
|
+
return instance[field.key];
|
|
27
|
+
};
|
|
28
|
+
if (typeof instance === 'function') {
|
|
29
|
+
get = instance;
|
|
30
|
+
}
|
|
25
31
|
let r = '';
|
|
26
32
|
this.fields.forEach((c) => {
|
|
27
33
|
if (r.length > 0)
|
|
28
34
|
r += ',';
|
|
29
|
-
r +=
|
|
35
|
+
r += c.valueConverter.toJson(get(c));
|
|
30
36
|
});
|
|
31
37
|
return r;
|
|
32
38
|
}
|
|
@@ -49,7 +55,7 @@ export class CompoundIdField {
|
|
|
49
55
|
let val = value.toString();
|
|
50
56
|
let id = val.split(',');
|
|
51
57
|
this.fields.forEach((c, i) => {
|
|
52
|
-
result[c.key] = id[i];
|
|
58
|
+
result[c.key] = c.valueConverter.fromJson(id[i]);
|
|
53
59
|
});
|
|
54
60
|
return result;
|
|
55
61
|
}
|
package/esm/src/context.js
CHANGED
|
@@ -24,11 +24,12 @@ export class RemultAsyncLocalStorage {
|
|
|
24
24
|
constructor(remultObjectStorage) {
|
|
25
25
|
this.remultObjectStorage = remultObjectStorage;
|
|
26
26
|
}
|
|
27
|
-
run(remult, callback) {
|
|
28
|
-
if (this.remultObjectStorage)
|
|
29
|
-
this.remultObjectStorage.run(remult, () => callback(remult));
|
|
27
|
+
async run(remult, callback) {
|
|
28
|
+
if (this.remultObjectStorage) {
|
|
29
|
+
return this.remultObjectStorage.run(remult, () => callback(remult));
|
|
30
|
+
}
|
|
30
31
|
else
|
|
31
|
-
callback(remult);
|
|
32
|
+
return callback(remult);
|
|
32
33
|
}
|
|
33
34
|
getRemult() {
|
|
34
35
|
if (!this.remultObjectStorage) {
|
|
@@ -233,14 +234,20 @@ export class EventSource {
|
|
|
233
234
|
export async function doTransaction(remult, what) {
|
|
234
235
|
const trans = new transactionLiveQueryPublisher(remult.liveQueryPublisher);
|
|
235
236
|
let ok = true;
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
remult.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
const prev = remult.dataProvider;
|
|
238
|
+
try {
|
|
239
|
+
await remult.dataProvider.transaction(async (ds) => {
|
|
240
|
+
remult.dataProvider = ds;
|
|
241
|
+
remult.liveQueryPublisher = trans;
|
|
242
|
+
await what(ds);
|
|
243
|
+
ok = true;
|
|
244
|
+
});
|
|
245
|
+
if (ok)
|
|
246
|
+
await trans.flush();
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
remult.dataProvider = prev;
|
|
250
|
+
}
|
|
244
251
|
}
|
|
245
252
|
class transactionLiveQueryPublisher {
|
|
246
253
|
orig;
|
|
@@ -279,16 +286,5 @@ export function withRemult(callback, options) {
|
|
|
279
286
|
const remult = new Remult();
|
|
280
287
|
if (options?.dataProvider)
|
|
281
288
|
remult.dataProvider = options.dataProvider;
|
|
282
|
-
|
|
283
|
-
remultStatic.asyncContext.run(remult, () => {
|
|
284
|
-
r = new Promise(async (res, rej) => {
|
|
285
|
-
try {
|
|
286
|
-
res(await callback(remult));
|
|
287
|
-
}
|
|
288
|
-
catch (err) {
|
|
289
|
-
rej(err);
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
return r;
|
|
289
|
+
return remultStatic.asyncContext.run(remult, r => callback(r));
|
|
294
290
|
}
|