remult 3.0.6-next.1 → 3.0.7-next.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 +2 -2
- package/async-hooks.d.ts +26 -1
- package/async-hooks.js +31 -2
- package/esm/async-hooks.js +31 -1
- package/esm/graphql.js +3 -3
- package/esm/index.js +7 -1
- package/esm/remult-d1-http.js +32 -0
- package/esm/remult-d1.js +90 -0
- package/esm/remult-express.js +13 -4
- package/esm/server/get-remult-admin-html.js +1 -1
- package/esm/server/initAsyncHooks.js +0 -25
- package/esm/server/remult-admin.js +9 -5
- package/esm/src/CompoundIdField.js +1 -0
- package/esm/src/remult-static.js +2 -1
- package/esm/src/remult3/Fields.js +49 -30
- package/esm/src/remult3/IdEntity.js +1 -1
- package/esm/src/remult3/RepositoryImplementation.js +37 -21
- package/esm/src/remult3/entity.js +1 -1
- package/graphql.js +3 -3
- package/index.d.ts +9 -1
- package/index.js +9 -2
- package/package.json +15 -3
- package/remult-d1-http.d.ts +16 -0
- package/remult-d1-http.js +46 -0
- package/remult-d1.d.ts +27 -0
- package/remult-d1.js +128 -0
- package/remult-express.js +13 -4
- package/server/get-remult-admin-html.js +1 -1
- package/server/initAsyncHooks.d.ts +0 -25
- package/server/initAsyncHooks.js +0 -25
- package/server/remult-admin.js +9 -5
- package/server/remult-api-server.d.ts +1 -1
- package/src/CompoundIdField.d.ts +1 -0
- package/src/CompoundIdField.js +1 -0
- package/src/column-interfaces.d.ts +16 -3
- package/src/entity.d.ts +5 -0
- package/src/remult-static.d.ts +2 -1
- package/src/remult-static.js +2 -1
- package/src/remult3/Fields.d.ts +34 -4
- package/src/remult3/Fields.js +50 -40
- package/src/remult3/IdEntity.js +1 -1
- package/src/remult3/RepositoryImplementation.d.ts +23 -17
- package/src/remult3/RepositoryImplementation.js +48 -22
- package/src/remult3/entity.d.ts +1 -1
- package/src/remult3/entity.js +1 -1
- package/src/remult3/remult3.d.ts +8 -2
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ import { Entity, Fields } from 'remult'
|
|
|
76
76
|
allowApiCrud: true,
|
|
77
77
|
})
|
|
78
78
|
export class Product {
|
|
79
|
-
@Fields.
|
|
79
|
+
@Fields.id()
|
|
80
80
|
id = ''
|
|
81
81
|
|
|
82
82
|
@Fields.string()
|
|
@@ -166,7 +166,7 @@ import { Entity, Fields, Validators } from 'remult'
|
|
|
166
166
|
allowApiCrud: true,
|
|
167
167
|
})
|
|
168
168
|
export class Product {
|
|
169
|
-
@Fields.
|
|
169
|
+
@Fields.id()
|
|
170
170
|
id = ''
|
|
171
171
|
|
|
172
172
|
@Fields.string({
|
package/async-hooks.d.ts
CHANGED
|
@@ -1 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Initializes and enables async context tracking for the server.
|
|
3
|
+
*
|
|
4
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
5
|
+
* @example
|
|
6
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
7
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
8
|
+
*
|
|
9
|
+
* import { Task } from './entities/Task.js';
|
|
10
|
+
*
|
|
11
|
+
* initAsyncHooks();
|
|
12
|
+
*
|
|
13
|
+
* // Thx to the `initAsyncHooks` above,
|
|
14
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
15
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
16
|
+
* withRemult(async () => {
|
|
17
|
+
* remult.user = { id: '42' };
|
|
18
|
+
* repo(Task).find()
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* withRemult(async () => {
|
|
22
|
+
* remult.user = { id: '21' };
|
|
23
|
+
* repo(Task).find()
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
export declare function initAsyncHooks(): void;
|
package/async-hooks.js
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initAsyncHooks =
|
|
3
|
+
exports.initAsyncHooks = initAsyncHooks;
|
|
4
|
+
var context_js_1 = require("./src/context.js");
|
|
4
5
|
var initAsyncHooks_js_1 = require("./server/initAsyncHooks.js");
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Initializes and enables async context tracking for the server.
|
|
8
|
+
*
|
|
9
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
10
|
+
* @example
|
|
11
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
12
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
13
|
+
*
|
|
14
|
+
* import { Task } from './entities/Task.js';
|
|
15
|
+
*
|
|
16
|
+
* initAsyncHooks();
|
|
17
|
+
*
|
|
18
|
+
* // Thx to the `initAsyncHooks` above,
|
|
19
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
20
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
21
|
+
* withRemult(async () => {
|
|
22
|
+
* remult.user = { id: '42' };
|
|
23
|
+
* repo(Task).find()
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* withRemult(async () => {
|
|
27
|
+
* remult.user = { id: '21' };
|
|
28
|
+
* repo(Task).find()
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
function initAsyncHooks() {
|
|
32
|
+
(0, initAsyncHooks_js_1.initAsyncHooks)();
|
|
33
|
+
context_js_1.RemultAsyncLocalStorage.enable();
|
|
34
|
+
}
|
package/esm/async-hooks.js
CHANGED
|
@@ -1 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
import { RemultAsyncLocalStorage } from './src/context.js';
|
|
2
|
+
import { initAsyncHooks as _initAsyncHooks } from './server/initAsyncHooks.js';
|
|
3
|
+
/**
|
|
4
|
+
* Initializes and enables async context tracking for the server.
|
|
5
|
+
*
|
|
6
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
7
|
+
* @example
|
|
8
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
9
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
10
|
+
*
|
|
11
|
+
* import { Task } from './entities/Task.js';
|
|
12
|
+
*
|
|
13
|
+
* initAsyncHooks();
|
|
14
|
+
*
|
|
15
|
+
* // Thx to the `initAsyncHooks` above,
|
|
16
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
17
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
18
|
+
* withRemult(async () => {
|
|
19
|
+
* remult.user = { id: '42' };
|
|
20
|
+
* repo(Task).find()
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* withRemult(async () => {
|
|
24
|
+
* remult.user = { id: '21' };
|
|
25
|
+
* repo(Task).find()
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
export function initAsyncHooks() {
|
|
29
|
+
_initAsyncHooks();
|
|
30
|
+
RemultAsyncLocalStorage.enable();
|
|
31
|
+
}
|
package/esm/graphql.js
CHANGED
|
@@ -479,7 +479,7 @@ export function remultGraphql(options) {
|
|
|
479
479
|
currentType.fields.push({
|
|
480
480
|
key: f.key,
|
|
481
481
|
value: `${getMetaType(ref)}${f.allowNull ? '' : '!'}`,
|
|
482
|
-
comment: f.
|
|
482
|
+
comment: f.label,
|
|
483
483
|
});
|
|
484
484
|
currentType.query.resultProcessors.push((r, orig) => {
|
|
485
485
|
r[f.key] = async (args, req, gqlInfo) => {
|
|
@@ -524,7 +524,7 @@ export function remultGraphql(options) {
|
|
|
524
524
|
currentType.fields.push({
|
|
525
525
|
key: f.key,
|
|
526
526
|
value: `${getMetaType(ref)}${f.allowNull ? '' : '!'}`,
|
|
527
|
-
comment: f.
|
|
527
|
+
comment: f.label,
|
|
528
528
|
});
|
|
529
529
|
const refKey = ref.key;
|
|
530
530
|
currentType.query.resultProcessors.push((r) => {
|
|
@@ -566,7 +566,7 @@ export function remultGraphql(options) {
|
|
|
566
566
|
currentType.fields.push({
|
|
567
567
|
key: f.key,
|
|
568
568
|
value: `${type}${f.allowNull ? '' : '!'}`,
|
|
569
|
-
comment: f.
|
|
569
|
+
comment: f.label,
|
|
570
570
|
});
|
|
571
571
|
}
|
|
572
572
|
// sorting
|
package/esm/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider.js';
|
|
2
|
-
export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListInfo,
|
|
2
|
+
export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListInfo, } from './src/remult3/RepositoryImplementation.js';
|
|
3
|
+
import { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
4
|
+
export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated use `LabelTransformer` instead
|
|
7
|
+
*/
|
|
8
|
+
export const CaptionTransformer = LabelTransformer;
|
|
3
9
|
export { Entity } from './src/remult3/entity.js';
|
|
4
10
|
export { getEntityRef } from './src/remult3/getEntityRef.js';
|
|
5
11
|
export { Field, Fields, Relations, } from './src/remult3/Fields.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Cloudflare from 'cloudflare';
|
|
2
|
+
import { SqlDatabase } from './index.js';
|
|
3
|
+
import { D1DataProvider } from './remult-d1.js';
|
|
4
|
+
export { D1DataProvider };
|
|
5
|
+
export function createD1HttpDataProvider(creds) {
|
|
6
|
+
return new SqlDatabase(new D1DataProvider(new D1HttpClient(creds)));
|
|
7
|
+
}
|
|
8
|
+
export class D1HttpClient {
|
|
9
|
+
d1;
|
|
10
|
+
accountId;
|
|
11
|
+
databaseId;
|
|
12
|
+
constructor({ accountId, databaseId, apiToken }) {
|
|
13
|
+
this.d1 = new Cloudflare({ apiToken }).d1.database;
|
|
14
|
+
this.accountId = accountId;
|
|
15
|
+
this.databaseId = databaseId;
|
|
16
|
+
}
|
|
17
|
+
async execute(sql, params = []) {
|
|
18
|
+
return this.d1
|
|
19
|
+
.query(this.databaseId, {
|
|
20
|
+
sql,
|
|
21
|
+
params: params,
|
|
22
|
+
account_id: this.accountId,
|
|
23
|
+
})
|
|
24
|
+
.then(({ result: pages }) => pages.flatMap((page) => page.results));
|
|
25
|
+
// NOTE: d1 query endpoint returns the result as array of object with keys as column names.
|
|
26
|
+
// We're returning that now because the rest of remult seems to expect that.
|
|
27
|
+
// D1 also has a more efficient raw() endpoint that returns {columns: string[], rows: any[][]} that we may want
|
|
28
|
+
// to use eventually, and write code like fromRowToSql()
|
|
29
|
+
// at https://github.com/tursodatabase/libsql-client-ts/blob/main/packages/libsql-client/src/sqlite3.ts#L398
|
|
30
|
+
// to adapt the data
|
|
31
|
+
}
|
|
32
|
+
}
|
package/esm/remult-d1.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import { SqlDatabase, } from './index.js';
|
|
3
|
+
import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
|
|
4
|
+
export function createD1DataProvider(d1) {
|
|
5
|
+
return new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)));
|
|
6
|
+
}
|
|
7
|
+
export class D1DataProvider extends SqliteCoreDataProvider {
|
|
8
|
+
d1;
|
|
9
|
+
/**
|
|
10
|
+
* For production or local d1 using binding
|
|
11
|
+
*
|
|
12
|
+
* const dataProvider = new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)))
|
|
13
|
+
*/
|
|
14
|
+
constructor(d1) {
|
|
15
|
+
super(() => new D1Command(this.d1), async () => {
|
|
16
|
+
// afaik: d1 connection doesn't need closing,
|
|
17
|
+
// so this is just a noop
|
|
18
|
+
});
|
|
19
|
+
this.d1 = d1;
|
|
20
|
+
}
|
|
21
|
+
async transaction(action) {
|
|
22
|
+
// As of June 2025, D1 doesn't support transactions
|
|
23
|
+
// Here we simply run the action without wrapping it in a transaction
|
|
24
|
+
// It means queries in action() will be run individually (this is the same decision prisma made)
|
|
25
|
+
await action(this);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class D1BindingClient {
|
|
29
|
+
d1;
|
|
30
|
+
/**
|
|
31
|
+
* Simple d1 client that wraps the d1 binding directly
|
|
32
|
+
*
|
|
33
|
+
* const d1 = new D1BindingClient(env.DB)
|
|
34
|
+
*/
|
|
35
|
+
constructor(d1) {
|
|
36
|
+
this.d1 = d1;
|
|
37
|
+
}
|
|
38
|
+
async execute(sql, params = []) {
|
|
39
|
+
// https://developers.cloudflare.com/d1/worker-api/d1-database/
|
|
40
|
+
// https://developers.cloudflare.com/d1/worker-api/prepared-statements/
|
|
41
|
+
//
|
|
42
|
+
// Note: see if we should eventually take advantage of the raw() end point too.
|
|
43
|
+
const { results } = await this.d1
|
|
44
|
+
.prepare(sql)
|
|
45
|
+
.bind(...params)
|
|
46
|
+
.run();
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class D1Command {
|
|
51
|
+
d1;
|
|
52
|
+
params = [];
|
|
53
|
+
constructor(d1) {
|
|
54
|
+
this.d1 = d1;
|
|
55
|
+
}
|
|
56
|
+
async execute(sql) {
|
|
57
|
+
const results = await this.d1.execute(sql, this.params);
|
|
58
|
+
return new D1SqlResult(results);
|
|
59
|
+
}
|
|
60
|
+
/** @deprecated use `param` instead*/
|
|
61
|
+
addParameterAndReturnSqlToken(val) {
|
|
62
|
+
return this.param(val);
|
|
63
|
+
}
|
|
64
|
+
param(val) {
|
|
65
|
+
let p;
|
|
66
|
+
if (val instanceof Date)
|
|
67
|
+
p = val.valueOf();
|
|
68
|
+
else if (typeof val === 'boolean')
|
|
69
|
+
p = val ? 1 : 0;
|
|
70
|
+
else
|
|
71
|
+
p = val;
|
|
72
|
+
this.params.push(p);
|
|
73
|
+
// According to https://developers.cloudflare.com/d1/worker-api/prepared-statements/
|
|
74
|
+
// only the ordered "?NNN" and anonymous "?" param types are supported (although :AAAA seems to work too)
|
|
75
|
+
const key = `?${this.params.length}`;
|
|
76
|
+
return key;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class D1SqlResult {
|
|
80
|
+
rows;
|
|
81
|
+
columns;
|
|
82
|
+
constructor(rows = []) {
|
|
83
|
+
this.rows = rows;
|
|
84
|
+
// NOTE: are we guaranteed that when this is reached, rows is not empty?
|
|
85
|
+
this.columns = rows.length === 0 ? [] : Object.keys(rows[0]);
|
|
86
|
+
}
|
|
87
|
+
getColumnKeyInResultForIndexInSelect(index) {
|
|
88
|
+
return this.columns[index];
|
|
89
|
+
}
|
|
90
|
+
}
|
package/esm/remult-express.js
CHANGED
|
@@ -8,15 +8,24 @@ export function remultApi(options) {
|
|
|
8
8
|
if (options.bodySizeLimit === undefined) {
|
|
9
9
|
options.bodySizeLimit = '10mb';
|
|
10
10
|
}
|
|
11
|
+
const expressVersion = typeof app.del === 'function' ? 4 : 5;
|
|
11
12
|
if (options?.bodyParser !== false) {
|
|
12
13
|
app.use(express.json({ limit: options.bodySizeLimit }));
|
|
13
14
|
app.use(express.urlencoded({ extended: true, limit: options.bodySizeLimit }));
|
|
14
15
|
}
|
|
15
16
|
const server = createRemultServer(options, {
|
|
16
|
-
buildGenericRequestInfo: (req) =>
|
|
17
|
-
internal
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
buildGenericRequestInfo: (req) => {
|
|
18
|
+
const internal = { ...req, on: req.on };
|
|
19
|
+
if (expressVersion === 5) {
|
|
20
|
+
if (!internal.query) {
|
|
21
|
+
internal.query = req.query;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
internal,
|
|
26
|
+
public: { headers: new Headers(req.headers) },
|
|
27
|
+
};
|
|
28
|
+
},
|
|
20
29
|
getRequestBody: async (req) => req.body,
|
|
21
30
|
});
|
|
22
31
|
server.registerRouter(app);
|