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.
@@ -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, () => this.rows[name]);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.3.19-next.2",
3
+ "version": "3.3.19-next.3",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -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 DataProvider, __RowsOfDataForTesting {
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 () { return _this.rows[name]; });
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 DataProvider {
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 {};