remult 3.0.6-next.1 → 3.0.6
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/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/server/remult-admin.js +9 -5
- package/esm/src/CompoundIdField.js +1 -0
- package/esm/src/remult-static.js +1 -1
- package/esm/src/remult3/RepositoryImplementation.js +37 -21
- package/graphql.js +3 -3
- package/index.d.ts +9 -1
- package/index.js +9 -2
- package/package.json +11 -1
- 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/server/remult-admin.js +9 -5
- package/src/CompoundIdField.d.ts +1 -0
- package/src/CompoundIdField.js +1 -0
- package/src/column-interfaces.d.ts +12 -0
- package/src/entity.d.ts +5 -0
- package/src/remult-static.d.ts +1 -1
- package/src/remult-static.js +1 -1
- package/src/remult3/RepositoryImplementation.d.ts +23 -17
- package/src/remult3/RepositoryImplementation.js +48 -22
- package/src/remult3/remult3.d.ts +8 -2
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
|
+
}
|
|
@@ -8,7 +8,11 @@ export default function remultAdminHtml(options) {
|
|
|
8
8
|
return getHtml()
|
|
9
9
|
.replace('<!--PLACE_HERE_HEAD-->', head)
|
|
10
10
|
.replace('<!--PLACE_HERE_BODY-->', `<script>
|
|
11
|
-
window.optionsFromServer = ${JSON.stringify({
|
|
11
|
+
window.optionsFromServer = ${JSON.stringify({
|
|
12
|
+
rootPath,
|
|
13
|
+
requireAuthToken,
|
|
14
|
+
disableLiveQuery,
|
|
15
|
+
})}
|
|
12
16
|
</script>`);
|
|
13
17
|
}
|
|
14
18
|
export function buildEntityInfo(options) {
|
|
@@ -63,7 +67,7 @@ export function buildEntityInfo(options) {
|
|
|
63
67
|
where,
|
|
64
68
|
entityKey: relRepo.metadata.key,
|
|
65
69
|
key: x.key,
|
|
66
|
-
caption: x.
|
|
70
|
+
caption: x.label,
|
|
67
71
|
});
|
|
68
72
|
}
|
|
69
73
|
continue;
|
|
@@ -74,7 +78,7 @@ export function buildEntityInfo(options) {
|
|
|
74
78
|
readOnly: !x.apiUpdateAllowed(),
|
|
75
79
|
values: getValueList(x),
|
|
76
80
|
valFieldKey,
|
|
77
|
-
caption: x.
|
|
81
|
+
caption: x.label,
|
|
78
82
|
relationToOne: relation,
|
|
79
83
|
inputType: x.inputType,
|
|
80
84
|
type: x.inputType === InputTypes.json
|
|
@@ -95,11 +99,11 @@ export function buildEntityInfo(options) {
|
|
|
95
99
|
}
|
|
96
100
|
if (metadata.apiReadAllowed) {
|
|
97
101
|
let superKey = metadata.key;
|
|
98
|
-
let caption = metadata.
|
|
102
|
+
let caption = metadata.label;
|
|
99
103
|
const nbOfEntities = entities.filter((e) => e.key === metadata.key).length;
|
|
100
104
|
if (nbOfEntities > 0) {
|
|
101
105
|
superKey = metadata.key + '_ext_' + nbOfEntities;
|
|
102
|
-
caption = metadata.
|
|
106
|
+
caption = metadata.label + '*'.repeat(nbOfEntities);
|
|
103
107
|
}
|
|
104
108
|
entities.push({
|
|
105
109
|
superKey,
|
package/esm/src/remult-static.js
CHANGED
|
@@ -1191,7 +1191,7 @@ class rowHelperBase {
|
|
|
1191
1191
|
for (const col of this.fieldsMetadata) {
|
|
1192
1192
|
if (this.errors?.[col.key]) {
|
|
1193
1193
|
error.message =
|
|
1194
|
-
this.fields[col.key].metadata.
|
|
1194
|
+
this.fields[col.key].metadata.label +
|
|
1195
1195
|
': ' +
|
|
1196
1196
|
this.errors[col.key];
|
|
1197
1197
|
this.error = error.message;
|
|
@@ -1950,23 +1950,26 @@ export class FieldRefImplementation {
|
|
|
1950
1950
|
return !!!this.error;
|
|
1951
1951
|
}
|
|
1952
1952
|
}
|
|
1953
|
-
let
|
|
1954
|
-
|
|
1953
|
+
let tempLabelTransformer = {
|
|
1954
|
+
transformLabel: (remult, key, label, entityMetaData) => label,
|
|
1955
|
+
set transformCaption(val) {
|
|
1956
|
+
this.transformLabel = val;
|
|
1957
|
+
},
|
|
1955
1958
|
};
|
|
1956
1959
|
export const fieldOptionsEnricher = remultStatic.fieldOptionsEnricher || {
|
|
1957
1960
|
enrichFieldOptions: (options) => options,
|
|
1958
1961
|
};
|
|
1959
|
-
export const
|
|
1960
|
-
(remultStatic.
|
|
1961
|
-
export function
|
|
1962
|
+
export const LabelTransformer = remultStatic.labelTransformer ||
|
|
1963
|
+
(remultStatic.labelTransformer = tempLabelTransformer);
|
|
1964
|
+
export function buildLabel(label, key, remult, metaData) {
|
|
1962
1965
|
let result = undefined;
|
|
1963
|
-
if (typeof
|
|
1966
|
+
if (typeof label === 'function') {
|
|
1964
1967
|
if (remult)
|
|
1965
|
-
result =
|
|
1968
|
+
result = label(remult);
|
|
1966
1969
|
}
|
|
1967
|
-
else if (
|
|
1968
|
-
result =
|
|
1969
|
-
result =
|
|
1970
|
+
else if (label)
|
|
1971
|
+
result = label;
|
|
1972
|
+
result = LabelTransformer.transformLabel(remult, key, result ?? '', metaData);
|
|
1970
1973
|
if (result)
|
|
1971
1974
|
return result;
|
|
1972
1975
|
if (key)
|
|
@@ -1994,7 +1997,7 @@ export class columnDefsImpl {
|
|
|
1994
1997
|
catch (err) {
|
|
1995
1998
|
const error = `${String(prop)} failed for value ${args?.[0]}. Error: ${typeof err === 'string' ? err : err.message}`;
|
|
1996
1999
|
throw new EntityError({
|
|
1997
|
-
message: this.
|
|
2000
|
+
message: this.label + ': ' + error,
|
|
1998
2001
|
modelState: {
|
|
1999
2002
|
[this.key]: error,
|
|
2000
2003
|
},
|
|
@@ -2018,7 +2021,7 @@ export class columnDefsImpl {
|
|
|
2018
2021
|
this.dbName = settings.dbName;
|
|
2019
2022
|
if (this.dbName == undefined)
|
|
2020
2023
|
this.dbName = settings.key;
|
|
2021
|
-
this.
|
|
2024
|
+
this.label = buildLabel(settings.label ?? settings.caption, settings.key, remult, entityDefs);
|
|
2022
2025
|
}
|
|
2023
2026
|
apiUpdateAllowed(item) {
|
|
2024
2027
|
if (this.options.allowApiUpdate === undefined)
|
|
@@ -2049,7 +2052,10 @@ export class columnDefsImpl {
|
|
|
2049
2052
|
readonly = false;
|
|
2050
2053
|
valueConverter;
|
|
2051
2054
|
allowNull;
|
|
2052
|
-
|
|
2055
|
+
label;
|
|
2056
|
+
get caption() {
|
|
2057
|
+
return this.label;
|
|
2058
|
+
}
|
|
2053
2059
|
dbName;
|
|
2054
2060
|
inputType;
|
|
2055
2061
|
key;
|
|
@@ -2103,7 +2109,7 @@ class EntityFullInfo {
|
|
|
2103
2109
|
this.fieldsMetadata.push((r[x.key] = new columnDefsImpl(x, this, remult)));
|
|
2104
2110
|
}
|
|
2105
2111
|
this.fields = r;
|
|
2106
|
-
this.
|
|
2112
|
+
this.label = buildLabel(entityInfo.label ?? entityInfo.caption, this.key, remult, this);
|
|
2107
2113
|
if (entityInfo.id) {
|
|
2108
2114
|
let r = typeof entityInfo.id === 'string'
|
|
2109
2115
|
? this.fields.find(entityInfo.id)
|
|
@@ -2218,7 +2224,10 @@ class EntityFullInfo {
|
|
|
2218
2224
|
};
|
|
2219
2225
|
fields;
|
|
2220
2226
|
dbName;
|
|
2221
|
-
|
|
2227
|
+
label;
|
|
2228
|
+
get caption() {
|
|
2229
|
+
return this.label;
|
|
2230
|
+
}
|
|
2222
2231
|
}
|
|
2223
2232
|
export function FieldType(...options) {
|
|
2224
2233
|
return (target, context) => {
|
|
@@ -2238,7 +2247,7 @@ export function ValueListFieldType(...options) {
|
|
|
2238
2247
|
FieldType((o) => {
|
|
2239
2248
|
;
|
|
2240
2249
|
(o.valueConverter = ValueListInfo.get(type)),
|
|
2241
|
-
(o.displayValue = (item, val) => val?.caption);
|
|
2250
|
+
(o.displayValue = (item, val) => val?.label ?? val?.caption);
|
|
2242
2251
|
o.validate = (entity, ref) => {
|
|
2243
2252
|
const values = ValueListInfo.get(type).getValues();
|
|
2244
2253
|
if (ref.value && !values.find((v) => v === ref.value)) {
|
|
@@ -2271,8 +2280,7 @@ export class ValueListInfo {
|
|
|
2271
2280
|
s.id = member;
|
|
2272
2281
|
if (typeof s.id === 'number')
|
|
2273
2282
|
this.isNumeric = true;
|
|
2274
|
-
|
|
2275
|
-
s.caption = makeTitle(s.id !== undefined ? s.id.toString() : member);
|
|
2283
|
+
setLabelAndCaption(s, member);
|
|
2276
2284
|
this.byIdMap.set(s.id, s);
|
|
2277
2285
|
this.values.push(s);
|
|
2278
2286
|
}
|
|
@@ -2287,8 +2295,7 @@ export class ValueListInfo {
|
|
|
2287
2295
|
this.values.splice(0, this.values.length, ...op.getValues());
|
|
2288
2296
|
this.byIdMap.clear();
|
|
2289
2297
|
this.values.forEach((s) => {
|
|
2290
|
-
|
|
2291
|
-
s.caption = makeTitle(s.id);
|
|
2298
|
+
setLabelAndCaption(s, '');
|
|
2292
2299
|
this.byIdMap.set(s.id, s);
|
|
2293
2300
|
});
|
|
2294
2301
|
}
|
|
@@ -2337,6 +2344,15 @@ export class ValueListInfo {
|
|
|
2337
2344
|
inputType;
|
|
2338
2345
|
}
|
|
2339
2346
|
const typeCache = new Map();
|
|
2347
|
+
function setLabelAndCaption(s, member) {
|
|
2348
|
+
let label = s.label ?? s.caption;
|
|
2349
|
+
if (label === undefined)
|
|
2350
|
+
label = makeTitle(s.id !== undefined ? s.id.toString() : member);
|
|
2351
|
+
if (s.label === undefined)
|
|
2352
|
+
s.label = label;
|
|
2353
|
+
if (s.caption === undefined)
|
|
2354
|
+
s.caption = label;
|
|
2355
|
+
}
|
|
2340
2356
|
export function getValueList(type) {
|
|
2341
2357
|
let meta = type?.metadata;
|
|
2342
2358
|
if (!meta && isOfType(type, 'options'))
|
package/graphql.js
CHANGED
|
@@ -586,7 +586,7 @@ function remultGraphql(options) {
|
|
|
586
586
|
currentType.fields.push({
|
|
587
587
|
key: f.key,
|
|
588
588
|
value: "".concat(getMetaType(ref)).concat(f.allowNull ? '' : '!'),
|
|
589
|
-
comment: f.
|
|
589
|
+
comment: f.label,
|
|
590
590
|
});
|
|
591
591
|
currentType.query.resultProcessors.push(function (r, orig) {
|
|
592
592
|
r[f.key] = function (args, req, gqlInfo) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
@@ -661,7 +661,7 @@ function remultGraphql(options) {
|
|
|
661
661
|
currentType.fields.push({
|
|
662
662
|
key: f.key,
|
|
663
663
|
value: "".concat(getMetaType(ref)).concat(f.allowNull ? '' : '!'),
|
|
664
|
-
comment: f.
|
|
664
|
+
comment: f.label,
|
|
665
665
|
});
|
|
666
666
|
var refKey_1 = ref.key;
|
|
667
667
|
currentType.query.resultProcessors.push(function (r) {
|
|
@@ -713,7 +713,7 @@ function remultGraphql(options) {
|
|
|
713
713
|
currentType.fields.push({
|
|
714
714
|
key: f.key,
|
|
715
715
|
value: "".concat(type).concat(f.allowNull ? '' : '!'),
|
|
716
|
-
comment: f.
|
|
716
|
+
comment: f.label,
|
|
717
717
|
});
|
|
718
718
|
}
|
|
719
719
|
// sorting
|
package/index.d.ts
CHANGED
|
@@ -4,7 +4,15 @@ export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-
|
|
|
4
4
|
export type { ClassType } from './classType.js';
|
|
5
5
|
export { ValidateFieldEvent, MembersOnly, NumericKeys, FieldsMetadata, FieldRef, IdFieldRef, FieldsRef, EntityMetadata, EntityOrderBy, EntityFilter, FindOptions, FindOptionsBase, QueryResult, QueryOptions, UpsertOptions, Repository, GroupByOptions, GroupByResult, FindFirstOptions, ComparisonValueFilter, ValueFilter, IdFilter, ContainsStringValueFilter, EntityRef, SortSegments, Paginator, LiveQuery, LiveQueryChangeInfo, Subscribable, RefSubscriber, RefSubscriberBase, RelationOptions, ObjectMembersOnly, MembersToInclude, RepositoryRelations, EntityIdFields, ClassFieldDecorator, ClassFieldDecoratorContextStub, //n1 consider removing in ts5
|
|
6
6
|
LifecycleEvent, FieldsRefBase, FieldsRefForEntityBase, RepositoryRelationsForEntityBase, ControllerRefForControllerBase, ControllerRefBase, ControllerRef, EntityRefForEntityBase, IdMetadata, FindFirstOptionsBase, } from './src/remult3/remult3.js';
|
|
7
|
-
export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo,
|
|
7
|
+
export { EntityBase, ControllerBase, FieldType, getFields, ValueListFieldType, getValueList, ValueListFieldOptions, ValueListInfo, } from './src/remult3/RepositoryImplementation.js';
|
|
8
|
+
export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated use `LabelTransformer` instead
|
|
11
|
+
*/
|
|
12
|
+
export declare const CaptionTransformer: {
|
|
13
|
+
transformLabel: (remult: import("./src/context.js").Remult, key: string, label: string, entityMetaData: import("./src/remult3/remult3.js").EntityMetadata<any>) => string;
|
|
14
|
+
transformCaption: (remult: import("./src/context.js").Remult, key: string, label: string, entityMetaData: import("./src/remult3/remult3.js").EntityMetadata<any>) => string;
|
|
15
|
+
};
|
|
8
16
|
export { Entity } from './src/remult3/entity.js';
|
|
9
17
|
export { getEntityRef } from './src/remult3/getEntityRef.js';
|
|
10
18
|
export { Field, Fields, StringFieldOptions, Relations, } from './src/remult3/Fields.js';
|
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.InMemoryLiveQueryStorage = exports.remult = exports.ValueConverters = exports.createValueValidator = exports.createValueValidatorWithArgs = exports.createValidatorWithArgs = exports.createValidator = exports.valueValidator = exports.Validators = exports.UrlBuilder = exports.Filter = exports.CompoundIdField = exports.Sort = exports.EventSource = exports.isBackend = exports.withRemult = exports.Remult = exports.Allow = exports.ForbiddenError = exports.ProgressListener = exports.BackendMethod = exports.Controller = exports.JsonEntityIndexedDbStorage = exports.JsonEntityOpfsStorage = exports.JsonDataProvider = exports.dbNamesOf = exports.CustomSqlFilterBuilder = exports.SqlDatabase = exports.InMemoryDataProvider = exports.RestDataProvider = exports.EntityError = exports.describeEntity = exports.describeBackendMethods = exports.describeClass = exports.IdEntity = exports.Relations = exports.Fields = exports.Field = exports.getEntityRef = exports.Entity = exports.CaptionTransformer = exports.LabelTransformer = exports.ValueListInfo = exports.getValueList = exports.ValueListFieldType = exports.getFields = exports.FieldType = exports.ControllerBase = exports.EntityBase = exports.ArrayEntityDataProvider = void 0;
|
|
4
|
+
exports.SubscriptionChannel = void 0;
|
|
4
5
|
exports.repo = repo;
|
|
5
6
|
var array_entity_data_provider_js_1 = require("./src/data-providers/array-entity-data-provider.js");
|
|
6
7
|
Object.defineProperty(exports, "ArrayEntityDataProvider", { enumerable: true, get: function () { return array_entity_data_provider_js_1.ArrayEntityDataProvider; } });
|
|
@@ -12,7 +13,13 @@ Object.defineProperty(exports, "getFields", { enumerable: true, get: function ()
|
|
|
12
13
|
Object.defineProperty(exports, "ValueListFieldType", { enumerable: true, get: function () { return RepositoryImplementation_js_1.ValueListFieldType; } });
|
|
13
14
|
Object.defineProperty(exports, "getValueList", { enumerable: true, get: function () { return RepositoryImplementation_js_1.getValueList; } });
|
|
14
15
|
Object.defineProperty(exports, "ValueListInfo", { enumerable: true, get: function () { return RepositoryImplementation_js_1.ValueListInfo; } });
|
|
15
|
-
|
|
16
|
+
var RepositoryImplementation_js_2 = require("./src/remult3/RepositoryImplementation.js");
|
|
17
|
+
var RepositoryImplementation_js_3 = require("./src/remult3/RepositoryImplementation.js");
|
|
18
|
+
Object.defineProperty(exports, "LabelTransformer", { enumerable: true, get: function () { return RepositoryImplementation_js_3.LabelTransformer; } });
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated use `LabelTransformer` instead
|
|
21
|
+
*/
|
|
22
|
+
exports.CaptionTransformer = RepositoryImplementation_js_2.LabelTransformer;
|
|
16
23
|
var entity_js_1 = require("./src/remult3/entity.js");
|
|
17
24
|
Object.defineProperty(exports, "Entity", { enumerable: true, get: function () { return entity_js_1.Entity; } });
|
|
18
25
|
var getEntityRef_js_1 = require("./src/remult3/getEntityRef.js");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remult",
|
|
3
|
-
"version": "3.0.6
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"description": "A CRUD framework for full-stack TypeScript",
|
|
5
5
|
"homepage": "https://remult.dev",
|
|
6
6
|
"repository": {
|
|
@@ -131,6 +131,16 @@
|
|
|
131
131
|
"import": "./esm/remult-duckdb.js",
|
|
132
132
|
"types": "./remult-duckdb.d.ts"
|
|
133
133
|
},
|
|
134
|
+
"./remult-d1": {
|
|
135
|
+
"require": "./remult-d1.js",
|
|
136
|
+
"import": "./esm/remult-d1.js",
|
|
137
|
+
"types": "./remult-d1.d.ts"
|
|
138
|
+
},
|
|
139
|
+
"./remult-d1-http": {
|
|
140
|
+
"require": "./remult-d1-http.js",
|
|
141
|
+
"import": "./esm/remult-d1-http.js",
|
|
142
|
+
"types": "./remult-d1-http.d.ts"
|
|
143
|
+
},
|
|
134
144
|
"./remult-bun-sqlite": {
|
|
135
145
|
"require": "./remult-bun-sqlite.js",
|
|
136
146
|
"import": "./esm/remult-bun-sqlite.js",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SqlDatabase } from './index.js';
|
|
2
|
+
import { type D1Client, D1DataProvider, type D1RowObject } from './remult-d1.js';
|
|
3
|
+
export { D1DataProvider };
|
|
4
|
+
type D1Credentials = {
|
|
5
|
+
accountId: string;
|
|
6
|
+
apiToken: string;
|
|
7
|
+
databaseId: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function createD1HttpDataProvider(creds: D1Credentials): SqlDatabase;
|
|
10
|
+
export declare class D1HttpClient implements D1Client {
|
|
11
|
+
private d1;
|
|
12
|
+
private accountId;
|
|
13
|
+
private databaseId;
|
|
14
|
+
constructor({ accountId, databaseId, apiToken }: D1Credentials);
|
|
15
|
+
execute(sql: string, params?: unknown[]): Promise<D1RowObject[]>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.D1HttpClient = exports.D1DataProvider = void 0;
|
|
4
|
+
exports.createD1HttpDataProvider = createD1HttpDataProvider;
|
|
5
|
+
var tslib_1 = require("tslib");
|
|
6
|
+
var cloudflare_1 = tslib_1.__importDefault(require("cloudflare"));
|
|
7
|
+
var index_js_1 = require("./index.js");
|
|
8
|
+
var remult_d1_js_1 = require("./remult-d1.js");
|
|
9
|
+
Object.defineProperty(exports, "D1DataProvider", { enumerable: true, get: function () { return remult_d1_js_1.D1DataProvider; } });
|
|
10
|
+
function createD1HttpDataProvider(creds) {
|
|
11
|
+
return new index_js_1.SqlDatabase(new remult_d1_js_1.D1DataProvider(new D1HttpClient(creds)));
|
|
12
|
+
}
|
|
13
|
+
var D1HttpClient = /** @class */ (function () {
|
|
14
|
+
function D1HttpClient(_a) {
|
|
15
|
+
var accountId = _a.accountId, databaseId = _a.databaseId, apiToken = _a.apiToken;
|
|
16
|
+
this.d1 = new cloudflare_1.default({ apiToken: apiToken }).d1.database;
|
|
17
|
+
this.accountId = accountId;
|
|
18
|
+
this.databaseId = databaseId;
|
|
19
|
+
}
|
|
20
|
+
D1HttpClient.prototype.execute = function (sql_1) {
|
|
21
|
+
return tslib_1.__awaiter(this, arguments, void 0, function (sql, params) {
|
|
22
|
+
if (params === void 0) { params = []; }
|
|
23
|
+
return tslib_1.__generator(this, function (_a) {
|
|
24
|
+
return [2 /*return*/, this.d1
|
|
25
|
+
.query(this.databaseId, {
|
|
26
|
+
sql: sql,
|
|
27
|
+
params: params,
|
|
28
|
+
account_id: this.accountId,
|
|
29
|
+
})
|
|
30
|
+
.then(function (_a) {
|
|
31
|
+
var pages = _a.result;
|
|
32
|
+
return pages.flatMap(function (page) { return page.results; });
|
|
33
|
+
})
|
|
34
|
+
// NOTE: d1 query endpoint returns the result as array of object with keys as column names.
|
|
35
|
+
// We're returning that now because the rest of remult seems to expect that.
|
|
36
|
+
// D1 also has a more efficient raw() endpoint that returns {columns: string[], rows: any[][]} that we may want
|
|
37
|
+
// to use eventually, and write code like fromRowToSql()
|
|
38
|
+
// at https://github.com/tursodatabase/libsql-client-ts/blob/main/packages/libsql-client/src/sqlite3.ts#L398
|
|
39
|
+
// to adapt the data
|
|
40
|
+
];
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
return D1HttpClient;
|
|
45
|
+
}());
|
|
46
|
+
exports.D1HttpClient = D1HttpClient;
|
package/remult-d1.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SqlDatabase, type SqlImplementation } from './index.js';
|
|
2
|
+
import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
|
|
3
|
+
export declare function createD1DataProvider(d1: D1Database): SqlDatabase;
|
|
4
|
+
export declare class D1DataProvider extends SqliteCoreDataProvider {
|
|
5
|
+
private d1;
|
|
6
|
+
/**
|
|
7
|
+
* For production or local d1 using binding
|
|
8
|
+
*
|
|
9
|
+
* const dataProvider = new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)))
|
|
10
|
+
*/
|
|
11
|
+
constructor(d1: D1Client);
|
|
12
|
+
transaction(action: (sql: SqlImplementation) => Promise<void>): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export type D1RowObject = Record<string, unknown>;
|
|
15
|
+
export interface D1Client {
|
|
16
|
+
execute(sql: string, params?: unknown[]): Promise<D1RowObject[]>;
|
|
17
|
+
}
|
|
18
|
+
export declare class D1BindingClient implements D1Client {
|
|
19
|
+
private d1;
|
|
20
|
+
/**
|
|
21
|
+
* Simple d1 client that wraps the d1 binding directly
|
|
22
|
+
*
|
|
23
|
+
* const d1 = new D1BindingClient(env.DB)
|
|
24
|
+
*/
|
|
25
|
+
constructor(d1: D1Database);
|
|
26
|
+
execute(sql: string, params?: unknown[]): Promise<D1RowObject[]>;
|
|
27
|
+
}
|
package/remult-d1.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.D1BindingClient = exports.D1DataProvider = void 0;
|
|
5
|
+
exports.createD1DataProvider = createD1DataProvider;
|
|
6
|
+
var tslib_1 = require("tslib");
|
|
7
|
+
var index_js_1 = require("./index.js");
|
|
8
|
+
var remult_sqlite_core_js_1 = require("./remult-sqlite-core.js");
|
|
9
|
+
function createD1DataProvider(d1) {
|
|
10
|
+
return new index_js_1.SqlDatabase(new D1DataProvider(new D1BindingClient(d1)));
|
|
11
|
+
}
|
|
12
|
+
var D1DataProvider = /** @class */ (function (_super) {
|
|
13
|
+
tslib_1.__extends(D1DataProvider, _super);
|
|
14
|
+
/**
|
|
15
|
+
* For production or local d1 using binding
|
|
16
|
+
*
|
|
17
|
+
* const dataProvider = new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)))
|
|
18
|
+
*/
|
|
19
|
+
function D1DataProvider(d1) {
|
|
20
|
+
var _this = _super.call(this, function () { return new D1Command(_this.d1); }, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
21
|
+
return tslib_1.__generator(this, function (_a) {
|
|
22
|
+
return [2 /*return*/];
|
|
23
|
+
});
|
|
24
|
+
}); }) || this;
|
|
25
|
+
_this.d1 = d1;
|
|
26
|
+
return _this;
|
|
27
|
+
}
|
|
28
|
+
D1DataProvider.prototype.transaction = function (action) {
|
|
29
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
30
|
+
return tslib_1.__generator(this, function (_a) {
|
|
31
|
+
switch (_a.label) {
|
|
32
|
+
case 0:
|
|
33
|
+
// As of June 2025, D1 doesn't support transactions
|
|
34
|
+
// Here we simply run the action without wrapping it in a transaction
|
|
35
|
+
// It means queries in action() will be run individually (this is the same decision prisma made)
|
|
36
|
+
return [4 /*yield*/, action(this)];
|
|
37
|
+
case 1:
|
|
38
|
+
// As of June 2025, D1 doesn't support transactions
|
|
39
|
+
// Here we simply run the action without wrapping it in a transaction
|
|
40
|
+
// It means queries in action() will be run individually (this is the same decision prisma made)
|
|
41
|
+
_a.sent();
|
|
42
|
+
return [2 /*return*/];
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
return D1DataProvider;
|
|
48
|
+
}(remult_sqlite_core_js_1.SqliteCoreDataProvider));
|
|
49
|
+
exports.D1DataProvider = D1DataProvider;
|
|
50
|
+
var D1BindingClient = /** @class */ (function () {
|
|
51
|
+
/**
|
|
52
|
+
* Simple d1 client that wraps the d1 binding directly
|
|
53
|
+
*
|
|
54
|
+
* const d1 = new D1BindingClient(env.DB)
|
|
55
|
+
*/
|
|
56
|
+
function D1BindingClient(d1) {
|
|
57
|
+
this.d1 = d1;
|
|
58
|
+
}
|
|
59
|
+
D1BindingClient.prototype.execute = function (sql_1) {
|
|
60
|
+
return tslib_1.__awaiter(this, arguments, void 0, function (sql, params) {
|
|
61
|
+
var results;
|
|
62
|
+
var _a;
|
|
63
|
+
if (params === void 0) { params = []; }
|
|
64
|
+
return tslib_1.__generator(this, function (_b) {
|
|
65
|
+
switch (_b.label) {
|
|
66
|
+
case 0: return [4 /*yield*/, (_a = this.d1
|
|
67
|
+
.prepare(sql))
|
|
68
|
+
.bind.apply(_a, tslib_1.__spreadArray([], tslib_1.__read(params), false)).run()];
|
|
69
|
+
case 1:
|
|
70
|
+
results = (_b.sent()).results;
|
|
71
|
+
return [2 /*return*/, results];
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
return D1BindingClient;
|
|
77
|
+
}());
|
|
78
|
+
exports.D1BindingClient = D1BindingClient;
|
|
79
|
+
var D1Command = /** @class */ (function () {
|
|
80
|
+
function D1Command(d1) {
|
|
81
|
+
this.params = [];
|
|
82
|
+
this.d1 = d1;
|
|
83
|
+
}
|
|
84
|
+
D1Command.prototype.execute = function (sql) {
|
|
85
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
86
|
+
var results;
|
|
87
|
+
return tslib_1.__generator(this, function (_a) {
|
|
88
|
+
switch (_a.label) {
|
|
89
|
+
case 0: return [4 /*yield*/, this.d1.execute(sql, this.params)];
|
|
90
|
+
case 1:
|
|
91
|
+
results = _a.sent();
|
|
92
|
+
return [2 /*return*/, new D1SqlResult(results)];
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
/** @deprecated use `param` instead*/
|
|
98
|
+
D1Command.prototype.addParameterAndReturnSqlToken = function (val) {
|
|
99
|
+
return this.param(val);
|
|
100
|
+
};
|
|
101
|
+
D1Command.prototype.param = function (val) {
|
|
102
|
+
var p;
|
|
103
|
+
if (val instanceof Date)
|
|
104
|
+
p = val.valueOf();
|
|
105
|
+
else if (typeof val === 'boolean')
|
|
106
|
+
p = val ? 1 : 0;
|
|
107
|
+
else
|
|
108
|
+
p = val;
|
|
109
|
+
this.params.push(p);
|
|
110
|
+
// According to https://developers.cloudflare.com/d1/worker-api/prepared-statements/
|
|
111
|
+
// only the ordered "?NNN" and anonymous "?" param types are supported (although :AAAA seems to work too)
|
|
112
|
+
var key = "?".concat(this.params.length);
|
|
113
|
+
return key;
|
|
114
|
+
};
|
|
115
|
+
return D1Command;
|
|
116
|
+
}());
|
|
117
|
+
var D1SqlResult = /** @class */ (function () {
|
|
118
|
+
function D1SqlResult(rows) {
|
|
119
|
+
if (rows === void 0) { rows = []; }
|
|
120
|
+
this.rows = rows;
|
|
121
|
+
// NOTE: are we guaranteed that when this is reached, rows is not empty?
|
|
122
|
+
this.columns = rows.length === 0 ? [] : Object.keys(rows[0]);
|
|
123
|
+
}
|
|
124
|
+
D1SqlResult.prototype.getColumnKeyInResultForIndexInSelect = function (index) {
|
|
125
|
+
return this.columns[index];
|
|
126
|
+
};
|
|
127
|
+
return D1SqlResult;
|
|
128
|
+
}());
|
package/server/remult-admin.js
CHANGED
|
@@ -12,7 +12,11 @@ function remultAdminHtml(options) {
|
|
|
12
12
|
var head = options.head, rootPath = options.rootPath, requireAuthToken = options.requireAuthToken, disableLiveQuery = options.disableLiveQuery;
|
|
13
13
|
return (0, get_remult_admin_html_js_1.getHtml)()
|
|
14
14
|
.replace('<!--PLACE_HERE_HEAD-->', head)
|
|
15
|
-
.replace('<!--PLACE_HERE_BODY-->', "<script>\n window.optionsFromServer = ".concat(JSON.stringify({
|
|
15
|
+
.replace('<!--PLACE_HERE_BODY-->', "<script>\n window.optionsFromServer = ".concat(JSON.stringify({
|
|
16
|
+
rootPath: rootPath,
|
|
17
|
+
requireAuthToken: requireAuthToken,
|
|
18
|
+
disableLiveQuery: disableLiveQuery,
|
|
19
|
+
}), "\n</script>"));
|
|
16
20
|
}
|
|
17
21
|
function buildEntityInfo(options) {
|
|
18
22
|
var e_1, _a;
|
|
@@ -68,7 +72,7 @@ function buildEntityInfo(options) {
|
|
|
68
72
|
}
|
|
69
73
|
else if (info.type === 'toMany') {
|
|
70
74
|
if (relRepo.metadata.apiReadAllowed) {
|
|
71
|
-
relations.push(tslib_1.__assign(tslib_1.__assign({}, relInfo), { where: where, entityKey: relRepo.metadata.key, key: x.key, caption: x.
|
|
75
|
+
relations.push(tslib_1.__assign(tslib_1.__assign({}, relInfo), { where: where, entityKey: relRepo.metadata.key, key: x.key, caption: x.label }));
|
|
72
76
|
}
|
|
73
77
|
return "continue";
|
|
74
78
|
}
|
|
@@ -78,7 +82,7 @@ function buildEntityInfo(options) {
|
|
|
78
82
|
readOnly: !x.apiUpdateAllowed(),
|
|
79
83
|
values: (0, RepositoryImplementation_js_1.getValueList)(x),
|
|
80
84
|
valFieldKey: valFieldKey,
|
|
81
|
-
caption: x.
|
|
85
|
+
caption: x.label,
|
|
82
86
|
relationToOne: relation,
|
|
83
87
|
inputType: x.inputType,
|
|
84
88
|
type: x.inputType === inputTypes_js_1.InputTypes.json
|
|
@@ -112,11 +116,11 @@ function buildEntityInfo(options) {
|
|
|
112
116
|
}
|
|
113
117
|
if (metadata.apiReadAllowed) {
|
|
114
118
|
var superKey = metadata.key;
|
|
115
|
-
var caption = metadata.
|
|
119
|
+
var caption = metadata.label;
|
|
116
120
|
var nbOfEntities = entities.filter(function (e) { return e.key === metadata.key; }).length;
|
|
117
121
|
if (nbOfEntities > 0) {
|
|
118
122
|
superKey = metadata.key + '_ext_' + nbOfEntities;
|
|
119
|
-
caption = metadata.
|
|
123
|
+
caption = metadata.label + '*'.repeat(nbOfEntities);
|
|
120
124
|
}
|
|
121
125
|
entities.push({
|
|
122
126
|
superKey: superKey,
|
package/src/CompoundIdField.d.ts
CHANGED
package/src/CompoundIdField.js
CHANGED
|
@@ -7,6 +7,11 @@ export interface FieldOptions<entityType = unknown, valueType = unknown> {
|
|
|
7
7
|
* <input placeholder={taskRepo.metadata.fields.title.caption}/>
|
|
8
8
|
*/
|
|
9
9
|
caption?: string;
|
|
10
|
+
/** A human readable name for the field. Can be used to achieve a consistent label for a field throughout the app
|
|
11
|
+
* @example
|
|
12
|
+
* <input placeholder={taskRepo.metadata.fields.title.label}/>
|
|
13
|
+
*/
|
|
14
|
+
label?: string;
|
|
10
15
|
/** If it can store null in the database */
|
|
11
16
|
allowNull?: boolean;
|
|
12
17
|
/** If a value is required. Short-cut to say `validate: Validators.required`.
|
|
@@ -141,6 +146,12 @@ export interface FieldMetadata<valueType = unknown, entityType = unknown> {
|
|
|
141
146
|
* @see {@link FieldOptions#caption} for configuration details
|
|
142
147
|
*/
|
|
143
148
|
readonly caption: string;
|
|
149
|
+
/** A human readable label for the field. Can be used to achieve a consistent label for a field throughout the app
|
|
150
|
+
* @example
|
|
151
|
+
* <input placeholder={taskRepo.metadata.fields.title.label}/>
|
|
152
|
+
* @see {@link FieldOptions.label} for configuration details
|
|
153
|
+
*/
|
|
154
|
+
readonly label: string;
|
|
144
155
|
/** The name of the column in the database that holds the data for this field. If no name is set, the key will be used instead.
|
|
145
156
|
* @example
|
|
146
157
|
*
|
|
@@ -336,4 +347,5 @@ export declare function valueOrExpressionToValue<valueType>(f: ValueOrExpression
|
|
|
336
347
|
export interface ValueListItem {
|
|
337
348
|
id?: any;
|
|
338
349
|
caption?: any;
|
|
350
|
+
label?: any;
|
|
339
351
|
}
|
package/src/entity.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ import type { DataProvider } from './data-interfaces.js';
|
|
|
6
6
|
export interface EntityOptions<entityType = unknown> {
|
|
7
7
|
/**A human readable name for the entity */
|
|
8
8
|
caption?: string;
|
|
9
|
+
/** A human readable label for the entity. Can be used to achieve a consistent label for a field throughout the app
|
|
10
|
+
* @example
|
|
11
|
+
* <h1>Create a new item in {taskRepo.metadata.label}</h1>
|
|
12
|
+
*/
|
|
13
|
+
label?: string;
|
|
9
14
|
/**
|
|
10
15
|
* Determines if this Entity is available for get requests using Rest Api
|
|
11
16
|
* @description
|
package/src/remult-static.d.ts
CHANGED
package/src/remult-static.js
CHANGED
|
@@ -211,39 +211,43 @@ export declare class FieldRefImplementation<entityType, valueType> implements Fi
|
|
|
211
211
|
export declare const fieldOptionsEnricher: {
|
|
212
212
|
enrichFieldOptions: (options: FieldOptions) => void;
|
|
213
213
|
};
|
|
214
|
-
export declare const
|
|
214
|
+
export declare const LabelTransformer: {
|
|
215
215
|
/**
|
|
216
|
-
* Transforms the
|
|
216
|
+
* Transforms the label of a column based on custom rules or criteria.
|
|
217
217
|
*
|
|
218
218
|
* This method can be assigned an arrow function that dynamically alters the
|
|
219
|
-
*
|
|
220
|
-
* applying specific labeling conventions, or any other custom
|
|
219
|
+
* label of a column. It is particularly useful for internationalization,
|
|
220
|
+
* applying specific labeling conventions, or any other custom label transformation
|
|
221
221
|
* logic that your application requires.
|
|
222
222
|
*
|
|
223
223
|
* @param {Remult} remult - The Remult context, providing access to various framework features.
|
|
224
|
-
* @param {string} key - The key (name) of the field whose
|
|
225
|
-
* @param {string}
|
|
224
|
+
* @param {string} key - The key (name) of the field whose label is being transformed.
|
|
225
|
+
* @param {string} label - The original label of the field.
|
|
226
226
|
* @param {EntityMetadata<any>} entityMetaData - Metadata of the entity that the field belongs to.
|
|
227
|
-
* @returns {string} The transformed
|
|
228
|
-
* the original
|
|
227
|
+
* @returns {string} The transformed label for the field. If no transformation is applied,
|
|
228
|
+
* the original label is returned.
|
|
229
229
|
*
|
|
230
230
|
* @example
|
|
231
|
-
* // Example of translating a field
|
|
232
|
-
*
|
|
233
|
-
* remult, key,
|
|
231
|
+
* // Example of translating a field label to French
|
|
232
|
+
* LabelTransformer.transformLabel = (
|
|
233
|
+
* remult, key, label, entityMetaData
|
|
234
234
|
* ) => {
|
|
235
235
|
* if (key === 'firstName') {
|
|
236
236
|
* return 'Prénom'; // French translation for 'firstName'
|
|
237
237
|
* }
|
|
238
|
-
* return
|
|
238
|
+
* return label;
|
|
239
239
|
* };
|
|
240
240
|
*
|
|
241
241
|
* // Usage
|
|
242
|
-
* const
|
|
242
|
+
* const firstNameLabel = repo(Person).fields.firstName.label; // Returns 'Prénom'
|
|
243
243
|
*/
|
|
244
|
-
|
|
244
|
+
transformLabel: (remult: Remult, key: string, label: string, entityMetaData: EntityMetadata<any>) => string;
|
|
245
|
+
/**
|
|
246
|
+
* @obsolete use LabelTransformer.transformLabel instead
|
|
247
|
+
*/
|
|
248
|
+
transformCaption: (remult: Remult, key: string, label: string, entityMetaData: EntityMetadata<any>) => string;
|
|
245
249
|
};
|
|
246
|
-
export declare function
|
|
250
|
+
export declare function buildLabel(label: string | ((remult: Remult) => string) | undefined, key: string, remult: Remult, metaData: EntityMetadata<any>): string;
|
|
247
251
|
export declare class columnDefsImpl implements FieldMetadata {
|
|
248
252
|
private settings;
|
|
249
253
|
private entityDefs;
|
|
@@ -260,7 +264,8 @@ export declare class columnDefsImpl implements FieldMetadata {
|
|
|
260
264
|
readonly: boolean;
|
|
261
265
|
valueConverter: Required<ValueConverter<any>>;
|
|
262
266
|
allowNull: boolean;
|
|
263
|
-
|
|
267
|
+
label: string;
|
|
268
|
+
get caption(): string;
|
|
264
269
|
dbName: string;
|
|
265
270
|
inputType: string;
|
|
266
271
|
key: string;
|
|
@@ -285,7 +290,8 @@ declare class EntityFullInfo<T> implements EntityMetadata<T> {
|
|
|
285
290
|
idMetadata: IdMetadata<T>;
|
|
286
291
|
fields: FieldsMetadata<T>;
|
|
287
292
|
dbName: string;
|
|
288
|
-
|
|
293
|
+
label: string;
|
|
294
|
+
get caption(): string;
|
|
289
295
|
}
|
|
290
296
|
export declare function FieldType<valueType = unknown>(...options: (FieldOptions<any, valueType> | ((options: FieldOptions<any, valueType>, remult: Remult) => void))[]): (target: any, context?: any) => any;
|
|
291
297
|
export declare function isAutoIncrement(f: FieldMetadata): boolean;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ControllerBase = exports.EntityBase = exports.fieldOptionalValuesFunctionKey = exports.storableMember = exports.ValueListInfo = exports.columnDefsImpl = exports.
|
|
3
|
+
exports.ControllerBase = exports.EntityBase = exports.fieldOptionalValuesFunctionKey = exports.storableMember = exports.ValueListInfo = exports.columnDefsImpl = exports.LabelTransformer = exports.fieldOptionsEnricher = exports.FieldRefImplementation = exports.controllerRefImpl = exports.rowHelperImplementation = exports.RepositoryImplementation = void 0;
|
|
4
4
|
exports.createOldEntity = createOldEntity;
|
|
5
5
|
exports.getFields = getFields;
|
|
6
6
|
exports.getControllerRef = getControllerRef;
|
|
7
|
-
exports.
|
|
7
|
+
exports.buildLabel = buildLabel;
|
|
8
8
|
exports.FieldType = FieldType;
|
|
9
9
|
exports.isAutoIncrement = isAutoIncrement;
|
|
10
10
|
exports.ValueListFieldType = ValueListFieldType;
|
|
@@ -1955,7 +1955,7 @@ var rowHelperBase = /** @class */ (function () {
|
|
|
1955
1955
|
var col = _d.value;
|
|
1956
1956
|
if ((_b = this.errors) === null || _b === void 0 ? void 0 : _b[col.key]) {
|
|
1957
1957
|
error.message =
|
|
1958
|
-
this.fields[col.key].metadata.
|
|
1958
|
+
this.fields[col.key].metadata.label +
|
|
1959
1959
|
': ' +
|
|
1960
1960
|
this.errors[col.key];
|
|
1961
1961
|
this.error = error.message;
|
|
@@ -3334,23 +3334,26 @@ var FieldRefImplementation = /** @class */ (function () {
|
|
|
3334
3334
|
return FieldRefImplementation;
|
|
3335
3335
|
}());
|
|
3336
3336
|
exports.FieldRefImplementation = FieldRefImplementation;
|
|
3337
|
-
var
|
|
3338
|
-
|
|
3337
|
+
var tempLabelTransformer = {
|
|
3338
|
+
transformLabel: function (remult, key, label, entityMetaData) { return label; },
|
|
3339
|
+
set transformCaption(val) {
|
|
3340
|
+
this.transformLabel = val;
|
|
3341
|
+
},
|
|
3339
3342
|
};
|
|
3340
3343
|
exports.fieldOptionsEnricher = remult_static_js_1.remultStatic.fieldOptionsEnricher || {
|
|
3341
3344
|
enrichFieldOptions: function (options) { return options; },
|
|
3342
3345
|
};
|
|
3343
|
-
exports.
|
|
3344
|
-
(remult_static_js_1.remultStatic.
|
|
3345
|
-
function
|
|
3346
|
+
exports.LabelTransformer = remult_static_js_1.remultStatic.labelTransformer ||
|
|
3347
|
+
(remult_static_js_1.remultStatic.labelTransformer = tempLabelTransformer);
|
|
3348
|
+
function buildLabel(label, key, remult, metaData) {
|
|
3346
3349
|
var result = undefined;
|
|
3347
|
-
if (typeof
|
|
3350
|
+
if (typeof label === 'function') {
|
|
3348
3351
|
if (remult)
|
|
3349
|
-
result =
|
|
3352
|
+
result = label(remult);
|
|
3350
3353
|
}
|
|
3351
|
-
else if (
|
|
3352
|
-
result =
|
|
3353
|
-
result = exports.
|
|
3354
|
+
else if (label)
|
|
3355
|
+
result = label;
|
|
3356
|
+
result = exports.LabelTransformer.transformLabel(remult, key, result !== null && result !== void 0 ? result : '', metaData);
|
|
3354
3357
|
if (result)
|
|
3355
3358
|
return result;
|
|
3356
3359
|
if (key)
|
|
@@ -3360,7 +3363,7 @@ function buildCaption(caption, key, remult, metaData) {
|
|
|
3360
3363
|
var columnDefsImpl = /** @class */ (function () {
|
|
3361
3364
|
function columnDefsImpl(settings, entityDefs, remult) {
|
|
3362
3365
|
var _this = this;
|
|
3363
|
-
var _a;
|
|
3366
|
+
var _a, _b;
|
|
3364
3367
|
this.settings = settings;
|
|
3365
3368
|
this.entityDefs = entityDefs;
|
|
3366
3369
|
this.remult = remult;
|
|
@@ -3384,7 +3387,7 @@ var columnDefsImpl = /** @class */ (function () {
|
|
|
3384
3387
|
catch (err) {
|
|
3385
3388
|
var error = "".concat(String(prop), " failed for value ").concat(args === null || args === void 0 ? void 0 : args[0], ". Error: ").concat(typeof err === 'string' ? err : err.message);
|
|
3386
3389
|
throw new data_interfaces_js_1.EntityError({
|
|
3387
|
-
message: _this.
|
|
3390
|
+
message: _this.label + ': ' + error,
|
|
3388
3391
|
modelState: (_a = {},
|
|
3389
3392
|
_a[_this.key] = error,
|
|
3390
3393
|
_a),
|
|
@@ -3408,7 +3411,7 @@ var columnDefsImpl = /** @class */ (function () {
|
|
|
3408
3411
|
this.dbName = settings.dbName;
|
|
3409
3412
|
if (this.dbName == undefined)
|
|
3410
3413
|
this.dbName = settings.key;
|
|
3411
|
-
this.
|
|
3414
|
+
this.label = buildLabel((_b = settings.label) !== null && _b !== void 0 ? _b : settings.caption, settings.key, remult, entityDefs);
|
|
3412
3415
|
}
|
|
3413
3416
|
columnDefsImpl.prototype.apiUpdateAllowed = function (item) {
|
|
3414
3417
|
if (this.options.allowApiUpdate === undefined)
|
|
@@ -3438,6 +3441,13 @@ var columnDefsImpl = /** @class */ (function () {
|
|
|
3438
3441
|
});
|
|
3439
3442
|
});
|
|
3440
3443
|
};
|
|
3444
|
+
Object.defineProperty(columnDefsImpl.prototype, "caption", {
|
|
3445
|
+
get: function () {
|
|
3446
|
+
return this.label;
|
|
3447
|
+
},
|
|
3448
|
+
enumerable: false,
|
|
3449
|
+
configurable: true
|
|
3450
|
+
});
|
|
3441
3451
|
Object.defineProperty(columnDefsImpl.prototype, "dbReadOnly", {
|
|
3442
3452
|
get: function () {
|
|
3443
3453
|
return Boolean(this.settings.dbReadOnly);
|
|
@@ -3452,6 +3462,7 @@ var EntityFullInfo = /** @class */ (function () {
|
|
|
3452
3462
|
function EntityFullInfo(columnsInfo, entityInfo, remult, entityType, key) {
|
|
3453
3463
|
var _a, e_49, _b;
|
|
3454
3464
|
var _this = this;
|
|
3465
|
+
var _c;
|
|
3455
3466
|
this.entityInfo = entityInfo;
|
|
3456
3467
|
this.remult = remult;
|
|
3457
3468
|
this.entityType = entityType;
|
|
@@ -3562,7 +3573,7 @@ var EntityFullInfo = /** @class */ (function () {
|
|
|
3562
3573
|
finally { if (e_49) throw e_49.error; }
|
|
3563
3574
|
}
|
|
3564
3575
|
this.fields = r;
|
|
3565
|
-
this.
|
|
3576
|
+
this.label = buildLabel((_c = entityInfo.label) !== null && _c !== void 0 ? _c : entityInfo.caption, this.key, remult, this);
|
|
3566
3577
|
if (entityInfo.id) {
|
|
3567
3578
|
var r_2 = typeof entityInfo.id === 'string'
|
|
3568
3579
|
? this.fields.find(entityInfo.id)
|
|
@@ -3627,6 +3638,13 @@ var EntityFullInfo = /** @class */ (function () {
|
|
|
3627
3638
|
EntityFullInfo.prototype.getDbName = function () {
|
|
3628
3639
|
return (0, entityDbName_js_1.entityDbName)(this);
|
|
3629
3640
|
};
|
|
3641
|
+
Object.defineProperty(EntityFullInfo.prototype, "caption", {
|
|
3642
|
+
get: function () {
|
|
3643
|
+
return this.label;
|
|
3644
|
+
},
|
|
3645
|
+
enumerable: false,
|
|
3646
|
+
configurable: true
|
|
3647
|
+
});
|
|
3630
3648
|
return EntityFullInfo;
|
|
3631
3649
|
}());
|
|
3632
3650
|
function FieldType() {
|
|
@@ -3656,7 +3674,7 @@ function ValueListFieldType() {
|
|
|
3656
3674
|
FieldType.apply(void 0, tslib_1.__spreadArray([function (o) {
|
|
3657
3675
|
;
|
|
3658
3676
|
(o.valueConverter = ValueListInfo.get(type)),
|
|
3659
|
-
(o.displayValue = function (item, val) { return val === null || val === void 0 ? void 0 : val.caption; });
|
|
3677
|
+
(o.displayValue = function (item, val) { var _a; return (_a = val === null || val === void 0 ? void 0 : val.label) !== null && _a !== void 0 ? _a : val === null || val === void 0 ? void 0 : val.caption; });
|
|
3660
3678
|
o.validate = function (entity, ref) {
|
|
3661
3679
|
var values = ValueListInfo.get(type).getValues();
|
|
3662
3680
|
if (ref.value && !values.find(function (v) { return v === ref.value; })) {
|
|
@@ -3682,8 +3700,7 @@ var ValueListInfo = /** @class */ (function () {
|
|
|
3682
3700
|
s.id = member;
|
|
3683
3701
|
if (typeof s.id === 'number')
|
|
3684
3702
|
this.isNumeric = true;
|
|
3685
|
-
|
|
3686
|
-
s.caption = (0, column_js_1.makeTitle)(s.id !== undefined ? s.id.toString() : member);
|
|
3703
|
+
setLabelAndCaption(s, member);
|
|
3687
3704
|
this.byIdMap.set(s.id, s);
|
|
3688
3705
|
this.values.push(s);
|
|
3689
3706
|
}
|
|
@@ -3700,8 +3717,7 @@ var ValueListInfo = /** @class */ (function () {
|
|
|
3700
3717
|
(_b = this.values).splice.apply(_b, tslib_1.__spreadArray([0, this.values.length], tslib_1.__read(op.getValues()), false));
|
|
3701
3718
|
this.byIdMap.clear();
|
|
3702
3719
|
this.values.forEach(function (s) {
|
|
3703
|
-
|
|
3704
|
-
s.caption = (0, column_js_1.makeTitle)(s.id);
|
|
3720
|
+
setLabelAndCaption(s, '');
|
|
3705
3721
|
_this.byIdMap.set(s.id, s);
|
|
3706
3722
|
});
|
|
3707
3723
|
}
|
|
@@ -3766,6 +3782,16 @@ var ValueListInfo = /** @class */ (function () {
|
|
|
3766
3782
|
}());
|
|
3767
3783
|
exports.ValueListInfo = ValueListInfo;
|
|
3768
3784
|
var typeCache = new Map();
|
|
3785
|
+
function setLabelAndCaption(s, member) {
|
|
3786
|
+
var _a;
|
|
3787
|
+
var label = (_a = s.label) !== null && _a !== void 0 ? _a : s.caption;
|
|
3788
|
+
if (label === undefined)
|
|
3789
|
+
label = (0, column_js_1.makeTitle)(s.id !== undefined ? s.id.toString() : member);
|
|
3790
|
+
if (s.label === undefined)
|
|
3791
|
+
s.label = label;
|
|
3792
|
+
if (s.caption === undefined)
|
|
3793
|
+
s.caption = label;
|
|
3794
|
+
}
|
|
3769
3795
|
function getValueList(type) {
|
|
3770
3796
|
var meta = type === null || type === void 0 ? void 0 : type.metadata;
|
|
3771
3797
|
if (!meta && (0, isOfType_js_1.isOfType)(type, 'options'))
|
package/src/remult3/remult3.d.ts
CHANGED
|
@@ -198,6 +198,12 @@ export interface EntityMetadata<entityType = unknown> {
|
|
|
198
198
|
* @see {@link EntityOptions.caption}
|
|
199
199
|
*/
|
|
200
200
|
readonly caption: string;
|
|
201
|
+
/** A human readable label for the entity. Can be used to achieve a consistent label for a field throughout the app
|
|
202
|
+
* @example
|
|
203
|
+
* <h1>Create a new item in {taskRepo.metadata.label}</h1>
|
|
204
|
+
* @see {@link EntityOptions.label}
|
|
205
|
+
*/
|
|
206
|
+
readonly label: string;
|
|
201
207
|
/** The name of the table in the database that holds the data for this entity.
|
|
202
208
|
* If no name is set in the entity options, the `key` will be used instead.
|
|
203
209
|
* @see {@link EntityOptions.dbName}
|
|
@@ -641,7 +647,7 @@ export interface Repository<entityType> {
|
|
|
641
647
|
getEntityRef(item: entityType): EntityRef<entityType>;
|
|
642
648
|
/** Provides information about the fields of the Repository's entity
|
|
643
649
|
* @example
|
|
644
|
-
* console.log(repo.fields.title.
|
|
650
|
+
* console.log(repo.fields.title.label) // displays the label of a specific field
|
|
645
651
|
* console.log(repo.fields.title.options)// writes the options that were defined for this field
|
|
646
652
|
*/
|
|
647
653
|
fields: FieldsMetadata<entityType>;
|
|
@@ -1129,7 +1135,7 @@ export type Paginator<entityType, AggregateResult = EmptyAggregateResult> = {
|
|
|
1129
1135
|
* @template matchIdEntity The type used for matching IDs in the relation.
|
|
1130
1136
|
* @template optionsType The type of find options to apply to the relation (default is FindOptionsBase<toEntity>).
|
|
1131
1137
|
*/
|
|
1132
|
-
export interface RelationOptions<fromEntity, toEntity, matchIdEntity, optionsType extends FindOptionsBase<toEntity> = FindOptionsBase<toEntity>> extends Pick<FieldOptions, 'caption'> {
|
|
1138
|
+
export interface RelationOptions<fromEntity, toEntity, matchIdEntity, optionsType extends FindOptionsBase<toEntity> = FindOptionsBase<toEntity>> extends Pick<FieldOptions, 'caption' | 'label'> {
|
|
1133
1139
|
/**
|
|
1134
1140
|
* An object specifying custom field names for the relation.
|
|
1135
1141
|
* Each key represents a field in the related entity, and its value is the corresponding field in the source entity.
|