remult 3.4.0-next.2 → 3.4.0-next.5
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/index.js +7 -6
- package/esm/internals.js +1 -0
- package/esm/remult-d1.js +1 -0
- package/esm/remult-knex/index.js +112 -2
- package/esm/remult-mongo.js +17 -8
- package/esm/remult-sqlite-core.js +1 -0
- package/esm/src/data-api.js +13 -2
- package/esm/src/data-interfaces.js +4 -4
- package/esm/src/data-providers/array-entity-data-provider.js +14 -2
- package/esm/src/data-providers/in-memory-database.js +12 -1
- package/esm/src/data-providers/indexed-db-data-provider.js +844 -0
- package/esm/src/data-providers/json-data-provider.js +2 -2
- package/esm/src/data-providers/rest-data-provider.js +11 -12
- package/esm/src/data-providers/sql-database.js +110 -43
- package/esm/src/live-query/LiveQueryClient.js +53 -25
- package/esm/src/live-query/SseSubscriptionClient.js +49 -20
- package/esm/src/live-query/SubscriptionChannel.js +34 -10
- package/esm/src/remult3/RepositoryImplementation.js +135 -60
- package/esm/src/remult3/remult3.js +2 -2
- package/index.d.ts +31 -17
- package/index.js +5 -2
- package/internals.d.ts +1 -0
- package/internals.js +3 -1
- package/package.json +1 -1
- package/remult-d1.d.ts +1 -0
- package/remult-d1.js +1 -0
- package/remult-knex/index.js +329 -44
- package/remult-mongo.js +80 -36
- package/remult-sqlite-core.d.ts +1 -0
- package/remult-sqlite-core.js +1 -0
- package/src/data-api.js +38 -21
- package/src/data-interfaces.d.ts +9 -3
- package/src/data-interfaces.js +4 -4
- package/src/data-providers/array-entity-data-provider.d.ts +2 -2
- package/src/data-providers/array-entity-data-provider.js +92 -14
- package/src/data-providers/in-memory-database.d.ts +5 -2
- package/src/data-providers/in-memory-database.js +22 -1
- package/src/data-providers/indexed-db-data-provider.d.ts +61 -0
- package/src/data-providers/indexed-db-data-provider.js +1459 -0
- package/src/data-providers/json-data-provider.js +2 -2
- package/src/data-providers/rest-data-provider.d.ts +2 -3
- package/src/data-providers/rest-data-provider.js +18 -17
- package/src/data-providers/sql-database.js +254 -105
- package/src/entity.d.ts +12 -0
- package/src/live-query/LiveQueryClient.js +87 -37
- package/src/live-query/SseSubscriptionClient.js +75 -44
- package/src/live-query/SubscriptionChannel.d.ts +12 -0
- package/src/live-query/SubscriptionChannel.js +36 -11
- package/src/remult3/RepositoryImplementation.d.ts +7 -4
- package/src/remult3/RepositoryImplementation.js +521 -326
- package/src/remult3/remult3.d.ts +7 -5
- package/src/remult3/remult3.js +2 -2
- package/src/sql-command.d.ts +2 -0
- package/src/standard-schema/index.d.ts +1 -1
package/esm/index.js
CHANGED
|
@@ -9,31 +9,32 @@ export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
|
9
9
|
export const CaptionTransformer = LabelTransformer;
|
|
10
10
|
export { Entity } from './src/remult3/entity.js';
|
|
11
11
|
export { getEntityRef } from './src/remult3/getEntityRef.js';
|
|
12
|
-
export { Field, Fields, Relations
|
|
12
|
+
export { Field, Fields, Relations } from './src/remult3/Fields.js';
|
|
13
13
|
export { IdEntity } from './src/remult3/IdEntity.js';
|
|
14
14
|
export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
|
|
15
|
-
export { EntityError
|
|
15
|
+
export { EntityError } from './src/data-interfaces.js'; //V
|
|
16
16
|
export { RestDataProvider } from './src/data-providers/rest-data-provider.js'; //V
|
|
17
17
|
export { InMemoryDataProvider } from './src/data-providers/in-memory-database.js'; //V
|
|
18
18
|
export { SqlDatabase } from './src/data-providers/sql-database.js'; //V
|
|
19
19
|
export { CustomSqlFilterBuilder, dbNamesOf, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
20
|
-
export { JsonDataProvider
|
|
20
|
+
export { JsonDataProvider } from './src/data-providers/json-data-provider.js'; //V
|
|
21
21
|
export { JsonEntityOpfsStorage } from './src/data-providers/json-entity-opfs-storage.js';
|
|
22
22
|
export { JsonEntityIndexedDbStorage } from './src/data-providers/json-entity-indexed-db-data-provider.js';
|
|
23
|
+
export { IndexedDbDataProvider, IndexedDbIndexBuilder, } from './src/data-providers/indexed-db-data-provider.js';
|
|
23
24
|
//export * from './src/data-api'; //reconsider if to make internal
|
|
24
25
|
export { Controller, BackendMethod, ProgressListener, ForbiddenError, } from './src/server-action.js';
|
|
25
26
|
export { Allow, Remult, withRemult, isBackend, EventSource, } from './src/context.js';
|
|
26
27
|
export { Sort } from './src/sort.js';
|
|
27
28
|
export { CompoundIdField } from './src/CompoundIdField.js';
|
|
28
|
-
export { Filter
|
|
29
|
+
export { Filter } from './src/filter/filter-interfaces.js';
|
|
29
30
|
export { UrlBuilder } from './urlBuilder.js';
|
|
30
31
|
export { Validators, valueValidator, createValidator, createValidatorWithArgs, createValueValidatorWithArgs, createValueValidator, } from './src/validators.js';
|
|
31
32
|
export { ValueConverters } from './src/valueConverters.js';
|
|
32
33
|
export { remult } from './src/remult-proxy.js';
|
|
33
34
|
import { remult } from './src/remult-proxy.js';
|
|
34
35
|
//export { getId } from './src/remult3/getId';
|
|
35
|
-
export { InMemoryLiveQueryStorage
|
|
36
|
-
export { SubscriptionChannel
|
|
36
|
+
export { InMemoryLiveQueryStorage } from './src/live-query/SubscriptionServer.js';
|
|
37
|
+
export { SubscriptionChannel } from './src/live-query/SubscriptionChannel.js';
|
|
37
38
|
/**
|
|
38
39
|
* A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
|
|
39
40
|
*
|
package/esm/internals.js
CHANGED
|
@@ -7,5 +7,6 @@ export { __updateEntityBasedOnWhere } from './src/filter/filter-interfaces.js';
|
|
|
7
7
|
export const actionInfo = remultStatic.actionInfo;
|
|
8
8
|
export { isOfType } from './src/isOfType.js';
|
|
9
9
|
export { flags } from './src/remult3/remult3.js';
|
|
10
|
+
export { SseSubscriptionClient } from './src/live-query/SseSubscriptionClient.js';
|
|
10
11
|
export { DataProviderPromiseWrapper } from './src/data-interfaces.js';
|
|
11
12
|
export { pagedQueryResult } from './src/remult3/pagedQueryResult.js';
|
package/esm/remult-d1.js
CHANGED
package/esm/remult-knex/index.js
CHANGED
|
@@ -234,16 +234,87 @@ class KnexEntityDataProvider {
|
|
|
234
234
|
return undefined;
|
|
235
235
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
236
236
|
}
|
|
237
|
-
async delete(
|
|
237
|
+
async delete(ids) {
|
|
238
|
+
if (ids.length === 0)
|
|
239
|
+
return;
|
|
238
240
|
const e = await this.init();
|
|
239
241
|
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
240
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
242
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
|
|
241
243
|
let where = await f.resolveWhere();
|
|
242
244
|
await this.getEntityFrom(e)
|
|
243
245
|
.delete()
|
|
244
246
|
.where((b) => where.forEach((w) => w(b)));
|
|
245
247
|
}
|
|
246
248
|
async insert(data, options) {
|
|
249
|
+
if (data.length === 0)
|
|
250
|
+
return [];
|
|
251
|
+
if (isMysqlKnex(this.knex) &&
|
|
252
|
+
isAutoIncrement(this.entity.idMetadata.field) &&
|
|
253
|
+
options?.select !== 'none') {
|
|
254
|
+
const result = [];
|
|
255
|
+
for (const row of data)
|
|
256
|
+
result.push(await this.insertOne(row, options));
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
const e = await this.init();
|
|
260
|
+
const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row), knexMaxParametersInOneSqlStatement(this.knex));
|
|
261
|
+
const result = [];
|
|
262
|
+
for (const batch of batches)
|
|
263
|
+
result.push(...(await this.insertBatch(batch, e, options)));
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
async insertBatch(batch, e, options) {
|
|
267
|
+
const cols = [];
|
|
268
|
+
for (const x of this.entity.fields) {
|
|
269
|
+
if (isDbReadonly(x, e))
|
|
270
|
+
continue;
|
|
271
|
+
if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key]) !=
|
|
272
|
+
undefined))
|
|
273
|
+
cols.push(x);
|
|
274
|
+
}
|
|
275
|
+
const insertObjects = [];
|
|
276
|
+
for (const row of batch) {
|
|
277
|
+
const insertObject = {};
|
|
278
|
+
for (const x of cols) {
|
|
279
|
+
const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key]);
|
|
280
|
+
const key = await e.$dbNameOf(x);
|
|
281
|
+
if (v != undefined)
|
|
282
|
+
insertObject[key] = v;
|
|
283
|
+
else if (x.allowNull)
|
|
284
|
+
insertObject[key] = null;
|
|
285
|
+
else
|
|
286
|
+
insertObject[key] = this.knex.raw('DEFAULT');
|
|
287
|
+
}
|
|
288
|
+
insertObjects.push(insertObject);
|
|
289
|
+
}
|
|
290
|
+
let insert = this.getEntityFrom(e).insert(insertObjects);
|
|
291
|
+
const wantRows = options?.select !== 'none';
|
|
292
|
+
if (isAutoIncrement(this.entity.idMetadata.field) && wantRows) {
|
|
293
|
+
const result = await insert.returning(this.entity.idMetadata.field.dbName);
|
|
294
|
+
const ids = result.map((r) => r && typeof r === 'object'
|
|
295
|
+
? r[this.entity.idMetadata.field.dbName]
|
|
296
|
+
: r);
|
|
297
|
+
return this.loadRowsByIds(ids);
|
|
298
|
+
}
|
|
299
|
+
await insert;
|
|
300
|
+
if (!wantRows)
|
|
301
|
+
return batch.map(() => undefined);
|
|
302
|
+
return this.loadRowsByIds(batch.map((row) => this.entity.idMetadata.getId(row)));
|
|
303
|
+
}
|
|
304
|
+
async loadRowsByIds(ids) {
|
|
305
|
+
const found = await this.find({
|
|
306
|
+
where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
|
|
307
|
+
});
|
|
308
|
+
const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
|
|
309
|
+
return ids.map((id) => {
|
|
310
|
+
const row = byId.get(id + '');
|
|
311
|
+
if (!row)
|
|
312
|
+
throw new Error(`Failed to insert row - result contained ${found.length} rows`);
|
|
313
|
+
return row;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
//@internal
|
|
317
|
+
async insertOne(data, options) {
|
|
247
318
|
const e = await this.init();
|
|
248
319
|
let insertObject = {};
|
|
249
320
|
for (const x of this.entity.fields) {
|
|
@@ -591,6 +662,45 @@ export async function createKnexDataProvider(config) {
|
|
|
591
662
|
let result = new KnexDataProvider(k);
|
|
592
663
|
return result;
|
|
593
664
|
}
|
|
665
|
+
const defaultMaxParametersInOneSqlStatement = 2000;
|
|
666
|
+
function isMysqlKnex(knex) {
|
|
667
|
+
const client = knex.client.config.client;
|
|
668
|
+
return client === 'mysql2' || client === 'mysql';
|
|
669
|
+
}
|
|
670
|
+
function knexMaxParametersInOneSqlStatement(knex) {
|
|
671
|
+
const client = String(knex.client.config.client ?? '');
|
|
672
|
+
if (client.includes('sqlite'))
|
|
673
|
+
return 999;
|
|
674
|
+
return defaultMaxParametersInOneSqlStatement;
|
|
675
|
+
}
|
|
676
|
+
function splitByMaxParameters(items, parametersInItem, maxParameters) {
|
|
677
|
+
const batches = [];
|
|
678
|
+
let batch = [];
|
|
679
|
+
let params = 0;
|
|
680
|
+
for (const item of items) {
|
|
681
|
+
const n = parametersInItem(item);
|
|
682
|
+
if (batch.length > 0 && params + n > maxParameters) {
|
|
683
|
+
batches.push(batch);
|
|
684
|
+
batch = [];
|
|
685
|
+
params = 0;
|
|
686
|
+
}
|
|
687
|
+
batch.push(item);
|
|
688
|
+
params += n;
|
|
689
|
+
}
|
|
690
|
+
if (batch.length)
|
|
691
|
+
batches.push(batch);
|
|
692
|
+
return batches;
|
|
693
|
+
}
|
|
694
|
+
function countKnexInsertBindParameters(entity, e, row) {
|
|
695
|
+
let n = 0;
|
|
696
|
+
for (const x of entity.fields) {
|
|
697
|
+
if (isDbReadonly(x, e))
|
|
698
|
+
continue;
|
|
699
|
+
if (translateValueAndHandleArrayAndHandleArray(x, row[x.key]) != undefined)
|
|
700
|
+
n++;
|
|
701
|
+
}
|
|
702
|
+
return n;
|
|
703
|
+
}
|
|
594
704
|
function translateValueAndHandleArrayAndHandleArray(field, val) {
|
|
595
705
|
let result = field.valueConverter.toDb(val);
|
|
596
706
|
if (Array.isArray(result))
|
package/esm/remult-mongo.js
CHANGED
|
@@ -266,22 +266,31 @@ class MongoEntityDataProvider {
|
|
|
266
266
|
return undefined;
|
|
267
267
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
268
268
|
}
|
|
269
|
-
async delete(
|
|
269
|
+
async delete(ids) {
|
|
270
|
+
if (ids.length === 0)
|
|
271
|
+
return;
|
|
270
272
|
const { e, collection } = await this.collection();
|
|
271
273
|
let f = new FilterConsumerBridgeToMongo(e);
|
|
272
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
273
|
-
await collection.
|
|
274
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
|
|
275
|
+
await collection.deleteMany(await f.resolveWhere(), {
|
|
274
276
|
session: this.session,
|
|
275
277
|
});
|
|
276
278
|
}
|
|
277
279
|
async insert(data, options) {
|
|
280
|
+
if (data.length === 0)
|
|
281
|
+
return [];
|
|
278
282
|
let { collection, e } = await this.collection();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
const docs = [];
|
|
284
|
+
for (const row of data)
|
|
285
|
+
docs.push(await this.translateToDb(row, e));
|
|
286
|
+
const r = await collection.insertMany(docs, { session: this.session });
|
|
282
287
|
if (options?.select === 'none')
|
|
283
|
-
return undefined;
|
|
284
|
-
|
|
288
|
+
return data.map(() => undefined);
|
|
289
|
+
const result = [];
|
|
290
|
+
for (let i = 0; i < data.length; i++) {
|
|
291
|
+
result.push(await this.translateFromDb(await collection.findOne({ _id: r.insertedIds[i] }, { session: this.session }), e));
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
285
294
|
}
|
|
286
295
|
async collection() {
|
|
287
296
|
const e = await dbNamesOf(this.entity);
|
|
@@ -13,6 +13,7 @@ export class SqliteCoreDataProvider {
|
|
|
13
13
|
this.doesNotSupportReturningSyntaxOnlyForUpdate = doesNotSupportReturningSyntaxOnlyForUpdate;
|
|
14
14
|
}
|
|
15
15
|
orderByNullsFirst;
|
|
16
|
+
maxParametersInOneSqlStatement = 999;
|
|
16
17
|
getLimitSqlSyntax(limit, offset) {
|
|
17
18
|
return ' limit ' + limit + ' offset ' + offset;
|
|
18
19
|
}
|
package/esm/src/data-api.js
CHANGED
|
@@ -513,11 +513,22 @@ export class DataApi {
|
|
|
513
513
|
return this.repository.getEntityRef(newr).toApiJson();
|
|
514
514
|
};
|
|
515
515
|
if (Array.isArray(body)) {
|
|
516
|
-
|
|
516
|
+
let result = [];
|
|
517
517
|
await doTransaction(this.remult, async () => {
|
|
518
|
+
const items = [];
|
|
518
519
|
for (const item of body) {
|
|
519
|
-
|
|
520
|
+
let newr = this.repository.create();
|
|
521
|
+
await this.repository.getEntityRef(newr)._updateEntityBasedOnApi(item);
|
|
522
|
+
if (!this.repository.getEntityRef(newr).apiInsertAllowed) {
|
|
523
|
+
throw new ForbiddenError();
|
|
524
|
+
}
|
|
525
|
+
items.push(newr);
|
|
520
526
|
}
|
|
527
|
+
const inserted = await this.repository.insert(items, options);
|
|
528
|
+
if (options?.select === 'none')
|
|
529
|
+
result = items.map(() => undefined);
|
|
530
|
+
else
|
|
531
|
+
result = inserted.map((row) => this.repository.getEntityRef(row).toApiJson());
|
|
521
532
|
});
|
|
522
533
|
return result;
|
|
523
534
|
}
|
|
@@ -40,11 +40,11 @@ class EntityDataProviderPromiseWrapper {
|
|
|
40
40
|
constructor(dataProvider) {
|
|
41
41
|
this.dataProvider = dataProvider;
|
|
42
42
|
}
|
|
43
|
-
delete(
|
|
44
|
-
return this.dataProvider.then((dp) => dp.delete(
|
|
43
|
+
delete(ids) {
|
|
44
|
+
return this.dataProvider.then((dp) => dp.delete(ids));
|
|
45
45
|
}
|
|
46
|
-
insert(data) {
|
|
47
|
-
return this.dataProvider.then((dp) => dp.insert(data));
|
|
46
|
+
insert(data, options) {
|
|
47
|
+
return this.dataProvider.then((dp) => dp.insert(data, options));
|
|
48
48
|
}
|
|
49
49
|
count(where) {
|
|
50
50
|
return this.dataProvider.then((dp) => dp.count(where));
|
|
@@ -318,18 +318,30 @@ export class ArrayEntityDataProvider {
|
|
|
318
318
|
}
|
|
319
319
|
throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to update`);
|
|
320
320
|
}
|
|
321
|
-
async delete(
|
|
321
|
+
async delete(ids) {
|
|
322
|
+
for (const id of ids)
|
|
323
|
+
await this.deleteOne(id);
|
|
324
|
+
}
|
|
325
|
+
//@internal
|
|
326
|
+
async deleteOne(id) {
|
|
322
327
|
const names = await this.init();
|
|
323
328
|
let idMatches = this.idMatches(id, names);
|
|
324
329
|
for (let i = 0; i < this.rows().length; i++) {
|
|
325
330
|
if (idMatches(this.rows()[i])) {
|
|
326
331
|
this.rows().splice(i, 1);
|
|
327
|
-
return
|
|
332
|
+
return;
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
335
|
throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to delete`);
|
|
331
336
|
}
|
|
332
337
|
async insert(data) {
|
|
338
|
+
const result = [];
|
|
339
|
+
for (const row of data)
|
|
340
|
+
result.push(await this.insertOne(row));
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
//@internal
|
|
344
|
+
async insertOne(data) {
|
|
333
345
|
const names = await this.init();
|
|
334
346
|
let j = this.translateToJson(data, names);
|
|
335
347
|
let idf = this.entity.idMetadata.field;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getEntityMetadata, } from '../remult3/RepositoryImplementation.js';
|
|
1
2
|
import { ArrayEntityDataProvider } from './array-entity-data-provider.js';
|
|
2
3
|
export class InMemoryDataProvider {
|
|
3
4
|
async transaction(action) {
|
|
@@ -15,7 +16,17 @@ export class InMemoryDataProvider {
|
|
|
15
16
|
let name = entity.dbName;
|
|
16
17
|
if (!this.rows[name])
|
|
17
18
|
this.rows[name] = [];
|
|
18
|
-
return new ArrayEntityDataProvider(entity, () =>
|
|
19
|
+
return new ArrayEntityDataProvider(entity, () => {
|
|
20
|
+
if (!this.rows[name])
|
|
21
|
+
this.rows[name] = [];
|
|
22
|
+
return this.rows[name];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async dropDatabase() {
|
|
26
|
+
this.rows = {};
|
|
27
|
+
}
|
|
28
|
+
async dropTable(entity) {
|
|
29
|
+
delete this.rows[getEntityMetadata(entity).dbName];
|
|
19
30
|
}
|
|
20
31
|
toString() {
|
|
21
32
|
return 'InMemoryDataProvider';
|