remult 3.3.19-next.2 → 3.3.19-next.3
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/src/data-providers/in-memory-database.js +12 -1
- package/esm/src/data-providers/indexed-db-data-provider.js +34 -1
- package/esm/src/remult3/RepositoryImplementation.js +8 -0
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/data-interfaces.d.ts +7 -0
- 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 +24 -2
- package/src/data-providers/indexed-db-data-provider.js +72 -0
- package/src/remult3/RepositoryImplementation.d.ts +2 -2
- package/src/remult3/RepositoryImplementation.js +245 -211
- package/src/remult3/remult3.d.ts +15 -2
|
@@ -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';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dbNamesOf, isDbReadonly, } from '../filter/filter-consumer-bridge-to-sql-request.js';
|
|
2
|
-
import { isAutoIncrement } from '../remult3/RepositoryImplementation.js';
|
|
2
|
+
import { getEntityMetadata, isAutoIncrement, } from '../remult3/RepositoryImplementation.js';
|
|
3
3
|
import { ArrayEntityDataProvider } from './array-entity-data-provider.js';
|
|
4
4
|
export class IndexedDbIndexBuilder {
|
|
5
5
|
//@internal
|
|
@@ -55,6 +55,29 @@ export class IndexedDbDataProvider {
|
|
|
55
55
|
this.db?.close();
|
|
56
56
|
this.db = undefined;
|
|
57
57
|
}
|
|
58
|
+
async dropDatabase() {
|
|
59
|
+
await this.enqueue(async () => {
|
|
60
|
+
this.close();
|
|
61
|
+
this.dek = undefined;
|
|
62
|
+
await idbDeleteDatabase(this.dbName);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async dropTable(entity) {
|
|
66
|
+
await this.enqueue(async () => {
|
|
67
|
+
const name = storeNameOf(getEntityMetadata(entity));
|
|
68
|
+
const db = await this.open();
|
|
69
|
+
if (!db.objectStoreNames.contains(name))
|
|
70
|
+
return;
|
|
71
|
+
const nextVersion = db.version + 1;
|
|
72
|
+
this.forget(db);
|
|
73
|
+
db.close();
|
|
74
|
+
await this.open(nextVersion, (upgradeDb) => {
|
|
75
|
+
if (upgradeDb.objectStoreNames.contains(name)) {
|
|
76
|
+
upgradeDb.deleteObjectStore(name);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
58
81
|
//@internal
|
|
59
82
|
enqueue(fn) {
|
|
60
83
|
const run = this.tail.then(fn, fn);
|
|
@@ -759,6 +782,16 @@ function idbReq(request) {
|
|
|
759
782
|
request.onerror = () => reject(request.error);
|
|
760
783
|
});
|
|
761
784
|
}
|
|
785
|
+
function idbDeleteDatabase(name) {
|
|
786
|
+
return new Promise((resolve, reject) => {
|
|
787
|
+
const req = indexedDB.deleteDatabase(name);
|
|
788
|
+
req.onsuccess = () => resolve();
|
|
789
|
+
req.onerror = () => reject(req.error);
|
|
790
|
+
req.onblocked = () => {
|
|
791
|
+
/* wait for onsuccess after other connections close */
|
|
792
|
+
};
|
|
793
|
+
});
|
|
794
|
+
}
|
|
762
795
|
function encAad(storeName, id) {
|
|
763
796
|
return new TextEncoder().encode(storeName + '\0' + JSON.stringify(id));
|
|
764
797
|
}
|
|
@@ -323,6 +323,14 @@ export class RepositoryImplementation {
|
|
|
323
323
|
}
|
|
324
324
|
async insert(entity, options) {
|
|
325
325
|
if (Array.isArray(entity)) {
|
|
326
|
+
if (!options?.bulk) {
|
|
327
|
+
const r = [];
|
|
328
|
+
for (const item of entity)
|
|
329
|
+
r.push(await this.insert(item, options));
|
|
330
|
+
if (options?.select === 'none')
|
|
331
|
+
return undefined;
|
|
332
|
+
return r;
|
|
333
|
+
}
|
|
326
334
|
let refs = [];
|
|
327
335
|
let raw = [];
|
|
328
336
|
for (const item of entity) {
|
package/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export { IdEntity } from './src/remult3/IdEntity.js';
|
|
|
23
23
|
export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
|
|
24
24
|
export type { EntityOptions, PreprocessFilterEvent } from './src/entity.js';
|
|
25
25
|
export { EntityError } from './src/data-interfaces.js';
|
|
26
|
-
export type { DataProvider, EntityDataProvider, EntityDataProviderGroupByOptions, EntityDataProviderFindOptions, ErrorInfo, RestDataProviderHttpProvider, } from './src/data-interfaces.js';
|
|
26
|
+
export type { DataProvider, DroppableDataProvider, EntityDataProvider, EntityDataProviderGroupByOptions, EntityDataProviderFindOptions, ErrorInfo, RestDataProviderHttpProvider, } from './src/data-interfaces.js';
|
|
27
27
|
export type { SqlCommand, SqlCommandWithParameters, SqlImplementation, SqlResult, } from './src/sql-command.js';
|
|
28
28
|
export type { FieldMetadata, FieldOptions, FieldValidator, ValueConverter, ValueListItem, // reconsider, maybe it should go to remult angular as the abstraction ?
|
|
29
29
|
ValueOrExpression, } from './src/column-interfaces.js';
|
package/package.json
CHANGED
package/src/data-interfaces.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FieldMetadata } from '../index.js';
|
|
2
2
|
import type { Filter } from './filter/filter-interfaces.js';
|
|
3
3
|
import type { GroupByOperators, EntityMetadata, MembersOnly, InsertOrUpdateOptions } from './remult3/remult3.js';
|
|
4
|
+
import type { EntityMetadataOverloads } from './remult3/RepositoryImplementation.js';
|
|
4
5
|
import { Sort } from './sort.js';
|
|
5
6
|
export interface DataProvider {
|
|
6
7
|
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
@@ -8,6 +9,12 @@ export interface DataProvider {
|
|
|
8
9
|
ensureSchema?(entities: EntityMetadata[]): Promise<void>;
|
|
9
10
|
isProxy?: boolean;
|
|
10
11
|
}
|
|
12
|
+
/** `InMemoryDataProvider` + `IndexedDbDataProvider` — swap in tests. */
|
|
13
|
+
export interface DroppableDataProvider extends DataProvider {
|
|
14
|
+
dropDatabase(): Promise<void>;
|
|
15
|
+
/** Recreated with indexes on next use (`ensureSchema` / first write). */
|
|
16
|
+
dropTable(entity: EntityMetadataOverloads): Promise<void>;
|
|
17
|
+
}
|
|
11
18
|
export interface Storage {
|
|
12
19
|
ensureSchema(): Promise<void>;
|
|
13
20
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { __RowsOfDataForTesting } from '../__RowsOfDataForTesting.js';
|
|
2
|
-
import type { DataProvider, EntityDataProvider } from '../data-interfaces.js';
|
|
2
|
+
import type { DataProvider, DroppableDataProvider, EntityDataProvider } from '../data-interfaces.js';
|
|
3
|
+
import { type EntityMetadataOverloads } from '../remult3/RepositoryImplementation.js';
|
|
3
4
|
import type { EntityMetadata } from '../remult3/remult3.js';
|
|
4
|
-
export declare class InMemoryDataProvider implements
|
|
5
|
+
export declare class InMemoryDataProvider implements DroppableDataProvider, __RowsOfDataForTesting {
|
|
5
6
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
6
7
|
rows: any;
|
|
7
8
|
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
9
|
+
dropDatabase(): Promise<void>;
|
|
10
|
+
dropTable(entity: EntityMetadataOverloads): Promise<void>;
|
|
8
11
|
toString(): string;
|
|
9
12
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InMemoryDataProvider = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
|
+
var RepositoryImplementation_js_1 = require("../remult3/RepositoryImplementation.js");
|
|
5
6
|
var array_entity_data_provider_js_1 = require("./array-entity-data-provider.js");
|
|
6
7
|
var InMemoryDataProvider = /** @class */ (function () {
|
|
7
8
|
function InMemoryDataProvider() {
|
|
@@ -35,7 +36,27 @@ var InMemoryDataProvider = /** @class */ (function () {
|
|
|
35
36
|
var name = entity.dbName;
|
|
36
37
|
if (!this.rows[name])
|
|
37
38
|
this.rows[name] = [];
|
|
38
|
-
return new array_entity_data_provider_js_1.ArrayEntityDataProvider(entity, function () {
|
|
39
|
+
return new array_entity_data_provider_js_1.ArrayEntityDataProvider(entity, function () {
|
|
40
|
+
if (!_this.rows[name])
|
|
41
|
+
_this.rows[name] = [];
|
|
42
|
+
return _this.rows[name];
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
InMemoryDataProvider.prototype.dropDatabase = function () {
|
|
46
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
47
|
+
return tslib_1.__generator(this, function (_a) {
|
|
48
|
+
this.rows = {};
|
|
49
|
+
return [2 /*return*/];
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
InMemoryDataProvider.prototype.dropTable = function (entity) {
|
|
54
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
55
|
+
return tslib_1.__generator(this, function (_a) {
|
|
56
|
+
delete this.rows[(0, RepositoryImplementation_js_1.getEntityMetadata)(entity).dbName];
|
|
57
|
+
return [2 /*return*/];
|
|
58
|
+
});
|
|
59
|
+
});
|
|
39
60
|
};
|
|
40
61
|
InMemoryDataProvider.prototype.toString = function () {
|
|
41
62
|
return 'InMemoryDataProvider';
|
|
@@ -1,22 +1,44 @@
|
|
|
1
|
-
import type { DataProvider, EntityDataProvider } from '../data-interfaces.js';
|
|
1
|
+
import type { DataProvider, DroppableDataProvider, EntityDataProvider } from '../data-interfaces.js';
|
|
2
2
|
import type { ClassType } from '../../classType.js';
|
|
3
3
|
import type { EntityMetadata, MembersOnly } from '../remult3/remult3.js';
|
|
4
|
+
import { type EntityMetadataOverloads } from '../remult3/RepositoryImplementation.js';
|
|
4
5
|
export type IndexedDbIndexDef<entityType> = keyof MembersOnly<entityType> | readonly (keyof MembersOnly<entityType>)[];
|
|
5
6
|
export declare class IndexedDbIndexBuilder {
|
|
6
7
|
ensureIndexes<entityType>(entity: ClassType<entityType>, indexes: readonly IndexedDbIndexDef<entityType>[]): this;
|
|
7
8
|
}
|
|
8
9
|
export type IndexedDbDataProviderOptions = {
|
|
10
|
+
/** IDB-only indexes (not unique). PK is skipped. Compound names join db names with `_`. */
|
|
9
11
|
indexes?: (x: IndexedDbIndexBuilder) => void;
|
|
12
|
+
/**
|
|
13
|
+
* AES-GCM 256 on non-indexed fields (`_enc` + `_iv`). Requires `crypto.subtle`.
|
|
14
|
+
*
|
|
15
|
+
* Not full security. Stops casual disk/profile dump and other origins
|
|
16
|
+
* (non-extractable `CryptoKey` wrapped by the browser). Does **not** protect
|
|
17
|
+
* against same-origin XSS / any JS that can use the stored `CryptoKey` to decrypt.
|
|
18
|
+
*
|
|
19
|
+
* PK + `ensureIndexes` fields stay plaintext (required for IDB keyPath/indexes).
|
|
20
|
+
* Only non-indexed fields go into `_enc`.
|
|
21
|
+
*
|
|
22
|
+
* Default auto-key is origin-bound theater vs XSS. Pass `getEncryptionKey` for
|
|
23
|
+
* a stronger secret you control.
|
|
24
|
+
*/
|
|
10
25
|
encrypt?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Your AES-GCM `CryptoKey` (or Promise). Skips `__remult_keys`. Stronger than
|
|
28
|
+
* the default origin-bound auto-key — still not XSS-safe if same-origin JS can
|
|
29
|
+
* call this and decrypt.
|
|
30
|
+
*/
|
|
11
31
|
getEncryptionKey?: () => CryptoKey | Promise<CryptoKey>;
|
|
12
32
|
};
|
|
13
|
-
export declare class IndexedDbDataProvider implements
|
|
33
|
+
export declare class IndexedDbDataProvider implements DroppableDataProvider {
|
|
14
34
|
private dbName;
|
|
15
35
|
constructor(dbName?: string, options?: IndexedDbDataProviderOptions);
|
|
16
36
|
getEntityDataProvider(entity: EntityMetadata): EntityDataProvider;
|
|
17
37
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
18
38
|
ensureSchema(entities: EntityMetadata[]): Promise<void>;
|
|
19
39
|
close(): void;
|
|
40
|
+
dropDatabase(): Promise<void>;
|
|
41
|
+
dropTable(entity: EntityMetadataOverloads): Promise<void>;
|
|
20
42
|
}
|
|
21
43
|
export type IdbFetchOp = {
|
|
22
44
|
type: 'keys';
|
|
@@ -74,6 +74,68 @@ var IndexedDbDataProvider = /** @class */ (function () {
|
|
|
74
74
|
(_a = this.db) === null || _a === void 0 ? void 0 : _a.close();
|
|
75
75
|
this.db = undefined;
|
|
76
76
|
};
|
|
77
|
+
IndexedDbDataProvider.prototype.dropDatabase = function () {
|
|
78
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
79
|
+
var _this = this;
|
|
80
|
+
return tslib_1.__generator(this, function (_a) {
|
|
81
|
+
switch (_a.label) {
|
|
82
|
+
case 0: return [4 /*yield*/, this.enqueue(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
83
|
+
return tslib_1.__generator(this, function (_a) {
|
|
84
|
+
switch (_a.label) {
|
|
85
|
+
case 0:
|
|
86
|
+
this.close();
|
|
87
|
+
this.dek = undefined;
|
|
88
|
+
return [4 /*yield*/, idbDeleteDatabase(this.dbName)];
|
|
89
|
+
case 1:
|
|
90
|
+
_a.sent();
|
|
91
|
+
return [2 /*return*/];
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}); })];
|
|
95
|
+
case 1:
|
|
96
|
+
_a.sent();
|
|
97
|
+
return [2 /*return*/];
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
IndexedDbDataProvider.prototype.dropTable = function (entity) {
|
|
103
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
104
|
+
var _this = this;
|
|
105
|
+
return tslib_1.__generator(this, function (_a) {
|
|
106
|
+
switch (_a.label) {
|
|
107
|
+
case 0: return [4 /*yield*/, this.enqueue(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
108
|
+
var name, db, nextVersion;
|
|
109
|
+
return tslib_1.__generator(this, function (_a) {
|
|
110
|
+
switch (_a.label) {
|
|
111
|
+
case 0:
|
|
112
|
+
name = storeNameOf((0, RepositoryImplementation_js_1.getEntityMetadata)(entity));
|
|
113
|
+
return [4 /*yield*/, this.open()];
|
|
114
|
+
case 1:
|
|
115
|
+
db = _a.sent();
|
|
116
|
+
if (!db.objectStoreNames.contains(name))
|
|
117
|
+
return [2 /*return*/];
|
|
118
|
+
nextVersion = db.version + 1;
|
|
119
|
+
this.forget(db);
|
|
120
|
+
db.close();
|
|
121
|
+
return [4 /*yield*/, this.open(nextVersion, function (upgradeDb) {
|
|
122
|
+
if (upgradeDb.objectStoreNames.contains(name)) {
|
|
123
|
+
upgradeDb.deleteObjectStore(name);
|
|
124
|
+
}
|
|
125
|
+
})];
|
|
126
|
+
case 2:
|
|
127
|
+
_a.sent();
|
|
128
|
+
return [2 /*return*/];
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}); })];
|
|
132
|
+
case 1:
|
|
133
|
+
_a.sent();
|
|
134
|
+
return [2 /*return*/];
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
};
|
|
77
139
|
//@internal
|
|
78
140
|
IndexedDbDataProvider.prototype.enqueue = function (fn) {
|
|
79
141
|
var run = this.tail.then(fn, fn);
|
|
@@ -1299,6 +1361,16 @@ function idbReq(request) {
|
|
|
1299
1361
|
request.onerror = function () { return reject(request.error); };
|
|
1300
1362
|
});
|
|
1301
1363
|
}
|
|
1364
|
+
function idbDeleteDatabase(name) {
|
|
1365
|
+
return new Promise(function (resolve, reject) {
|
|
1366
|
+
var req = indexedDB.deleteDatabase(name);
|
|
1367
|
+
req.onsuccess = function () { return resolve(); };
|
|
1368
|
+
req.onerror = function () { return reject(req.error); };
|
|
1369
|
+
req.onblocked = function () {
|
|
1370
|
+
/* wait for onsuccess after other connections close */
|
|
1371
|
+
};
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1302
1374
|
function encAad(storeName, id) {
|
|
1303
1375
|
return new TextEncoder().encode(storeName + '\0' + JSON.stringify(id));
|
|
1304
1376
|
}
|
|
@@ -341,8 +341,8 @@ export declare class ControllerBase {
|
|
|
341
341
|
get $(): FieldsRefForEntityBase<this>;
|
|
342
342
|
get _(): ControllerRefForControllerBase<this>;
|
|
343
343
|
}
|
|
344
|
-
export declare function getEntityMetadata<entityType>(entity: EntityMetadataOverloads<entityType>): EntityMetadata<entityType>;
|
|
344
|
+
export declare function getEntityMetadata<entityType = unknown>(entity: EntityMetadataOverloads<entityType>): EntityMetadata<entityType>;
|
|
345
345
|
export declare function getRepository<entityType>(entity: RepositoryOverloads<entityType>): Repository<entityType>;
|
|
346
|
-
export type EntityMetadataOverloads<entityType> = Repository<entityType> | EntityMetadata<entityType> | ClassType<entityType>;
|
|
346
|
+
export type EntityMetadataOverloads<entityType = unknown> = Repository<entityType> | EntityMetadata<entityType> | ClassType<entityType>;
|
|
347
347
|
export type RepositoryOverloads<entityType> = Repository<entityType> | ClassType<entityType>;
|
|
348
348
|
export {};
|