@entity-access/entity-access 1.0.110 → 1.0.112

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.
@@ -2,6 +2,7 @@
2
2
  import EntityAccessError from "./EntityAccessError.js";
3
3
  import TimedCache from "./cache/TimedCache.js";
4
4
  import sleep from "./sleep.js";
5
+ import "./IDisposable.js";
5
6
 
6
7
  interface IObjectPool<T> {
7
8
  factory?: () => T;
@@ -22,12 +23,16 @@ interface IObjectPool<T> {
22
23
 
23
24
  /**
24
25
  * Max wait in milliseconds before creating
25
- * new object, default is 15 seconds with gap of 1 second each
26
+ * new object, default is 5 seconds
26
27
  */
27
28
  maxWait?: number;
29
+
30
+ logger?: (text: string) => void;
28
31
  }
29
32
 
30
- export type IPooledObject<T> = T & { [Symbol.asyncDisposable](): Promise<any>; };
33
+ export type IPooledObject<T> = T & {
34
+ [Symbol.asyncDisposable](): Promise<any>;
35
+ };
31
36
 
32
37
  /**
33
38
  * Most pool implementations are poor and are having too many errors.
@@ -63,10 +68,13 @@ export default class ObjectPool<T> {
63
68
 
64
69
  private awaited: Set<AbortController> = new Set();
65
70
 
71
+ private logger: (text: string) => void;
72
+
66
73
  constructor({
67
74
  maxSize = 40,
68
75
  maxWait = 5000,
69
76
  poolSize = 20,
77
+ logger,
70
78
  asyncFactory,
71
79
  factory,
72
80
  destroy,
@@ -79,6 +87,7 @@ export default class ObjectPool<T> {
79
87
  this.factory = factory;
80
88
  this.destroy = destroy;
81
89
  this.subscribeForRemoval = subscribeForRemoval;
90
+ this.logger = logger;
82
91
  }
83
92
 
84
93
  public async dispose() {
@@ -94,51 +103,58 @@ export default class ObjectPool<T> {
94
103
  }
95
104
 
96
105
  public async acquire(): Promise<IPooledObject<T>> {
97
- let item = this.free.pop();
98
- if(!item) {
106
+ let existing = this.free.pop();
107
+ if(!existing) {
99
108
  if (this.total >= this.poolSize) {
100
109
  const a = new AbortController();
101
110
  this.awaited.add(a);
102
111
  await sleep(this.maxWait, a.signal);
103
112
  this.awaited.delete(a);
104
- item = this.free.pop();
113
+ existing = this.free.pop();
105
114
  }
106
115
  }
107
- if(!item) {
108
-
109
- if (this.total >= this.maxSize) {
110
- throw new EntityAccessError(`Maximum size of pool reached. Retry after sometime.`);
111
- }
112
- this.total++;
116
+ if(existing) {
117
+ return existing as IPooledObject<T>;
118
+ }
113
119
 
114
- // create new..
115
- if (this.factory) {
116
- item = this.factory();
117
- } else {
118
- item = await this.asyncFactory();
119
- }
120
- this.subscribeForRemoval(item as unknown as any, () => {
121
- const index = this.free.indexOf(item);
122
- this.free.splice(index, 1);
123
- });
120
+ if (this.total >= this.maxSize) {
121
+ throw new EntityAccessError(`Maximum size of pool reached. Retry after sometime.`);
124
122
  }
123
+ this.total++;
124
+
125
+ // create new..
126
+ const item = this.factory?.() ?? (await this.asyncFactory());
127
+ this.subscribeForRemoval(item as unknown as any, () => {
128
+ const index = this.free.indexOf(item);
129
+ this.free.splice(index, 1);
130
+ });
131
+
132
+ return this.setupItem(item);
133
+ }
134
+
135
+
136
+ private setupItem(item: T) {
125
137
  const pooledItem = item as IPooledObject<T>;
126
138
  pooledItem[Symbol.asyncDisposable] = async () => {
139
+ delete this[Symbol.asyncDisposable];
127
140
  if (this.free.length < this.poolSize) {
128
- this.free.push(item);
129
- for (const iterator of this.awaited) {
141
+ this.logger?.(`Pooled item ${pooledItem} freed.`);
142
+ this.free.push(pooledItem);
143
+ for (const [iterator] of this.awaited.entries()) {
130
144
  this.awaited.delete(iterator);
131
145
  iterator.abort();
132
- break;
146
+ return;
133
147
  }
134
- } else {
135
- await this.destroy(item);
136
- this.total--;
148
+ return;
137
149
  }
150
+ this.total--;
151
+ this.logger?.(`Pooled item ${pooledItem} destoryed.`);
152
+ void this.destroy(pooledItem)?.catch(console.error);
138
153
  };
154
+ this.logger?.(`Pooled item ${pooledItem} acquired.`);
155
+ this.logger?.(`Item ${pooledItem} has disposable ${typeof pooledItem[Symbol.asyncDisposable]}`);
139
156
  return item as IPooledObject<T>;
140
157
  }
141
-
142
158
  }
143
159
 
144
160
  export class NamedObjectPool<T> {
@@ -3,14 +3,35 @@ export default async function sleep(n: number, signal?: AbortSignal, throwOnAbor
3
3
  return;
4
4
  }
5
5
  return new Promise<void>((resolve, reject) => {
6
+ if (!signal) {
7
+ setTimeout(resolve, n);
8
+ return;
9
+ }
10
+ let resolved = false;
11
+ const old = resolve;
12
+ resolve = () => {
13
+ if (resolved) {
14
+ return;
15
+ }
16
+ resolved = true;
17
+ old();
18
+ };
19
+ const oldReject = reject;
20
+ reject = (r) => {
21
+ if (resolved) {
22
+ return;
23
+ }
24
+ resolved = true;
25
+ oldReject(r);
26
+ };
6
27
  const id = setTimeout(resolve, n);
7
- signal?.addEventListener("abort", () => {
28
+ signal.onabort = () => {
8
29
  clearTimeout(id);
9
30
  if (throwOnAbort) {
10
31
  reject("cancelled");
11
32
  return;
12
33
  }
13
34
  resolve();
14
- });
35
+ };
15
36
  });
16
37
  }
@@ -2,95 +2,58 @@ import type { IClassOf } from "./IClassOf.js";
2
2
  import SchemaRegistry from "./SchemaRegistry.js";
3
3
  import NameParser from "./parser/NameParser.js";
4
4
 
5
-
6
-
7
- export default function Relate<T, TRelated>(c: IClassOf<TRelated>,
8
- {
9
- foreignKey: name, inverseProperty: inv, inverseKey: invKey, dotNotCreateIndex
10
- }: {
11
-
12
- foreignKey: (item: T) => any;
13
-
14
- inverseProperty: (item: TRelated) => T[];
15
-
16
- inverseKey?: (item: TRelated) => any;
17
- dotNotCreateIndex?: boolean;
18
- }
19
-
20
- ) {
21
- return (target: T, key: string): any => {
22
-
23
- const cn = target.constructor ?? target;
24
- const type = SchemaRegistry.model(cn);
25
-
26
- type.addRelation({
27
- type,
28
- name: key,
29
- foreignKey: NameParser.parseMember(name),
30
- relatedTypeClass: c,
31
- relatedName: NameParser.parseMember(inv),
32
- relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
33
- dotNotCreateIndex
34
- });
35
-
36
- };
5
+ export interface IRelatedType<T, TRelated> {
6
+ property: (item: T) => TRelated;
7
+ inverseProperty: (item: TRelated) => T[];
8
+ inverseKey?: (item: TRelated) => any;
9
+ dotNotCreateIndex?: boolean;
37
10
  }
38
11
 
39
- export function RelateOne<T, TRelated>(c: IClassOf<TRelated>,
40
- {
41
- foreignKey: name, inverseProperty: inv, inverseKey: invKey, dotNotCreateIndex
42
- }: {
43
-
44
- foreignKey: (item: T) => any;
45
-
46
- inverseProperty: (item: TRelated) => T;
47
-
48
- inverseKey?: (item: TRelated) => any;
49
- dotNotCreateIndex?: boolean;
50
- }
51
-
52
- ) {
53
- return (target: T, key: string): any => {
12
+ export interface IRelatedTypeOne<T, TRelated> {
13
+ property: (item: T) => TRelated;
14
+ inverseProperty: (item: TRelated) => T;
15
+ inverseKey?: (item: TRelated) => any;
16
+ dotNotCreateIndex?: boolean;
17
+ }
54
18
 
55
- const cn = target.constructor ?? target;
56
- const type = SchemaRegistry.model(cn);
57
-
58
- const r = type.addRelation({
59
- type,
60
- name: key,
61
- foreignKey: NameParser.parseMember(name),
62
- relatedTypeClass: c,
63
- relatedName: NameParser.parseMember(inv),
64
- relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
65
- dotNotCreateIndex
66
- });
67
- r.relatedRelation.isCollection = false;
68
- };
19
+ export interface IRelatedTypeWithType<T, TRelated> {
20
+ type: () => IClassOf<TRelated>,
21
+ property: (item: T) => TRelated;
22
+ inverseProperty: (item: TRelated) => T[];
23
+ inverseKey?: (item: TRelated) => any;
24
+ dotNotCreateIndex?: boolean;
69
25
  }
70
26
 
71
- export function RelateTo<T, TRelated>(c: IClassOf<TRelated>,
72
- {
73
- property,
74
- inverseProperty, inverseKey: invKey, dotNotCreateIndex
75
- }: {
27
+ export interface IRelatedTypeOneWithType<T, TRelated> {
28
+ type: () => IClassOf<TRelated>,
29
+ property: (item: T) => TRelated;
30
+ inverseProperty: (item: TRelated) => T;
31
+ inverseKey?: (item: TRelated) => any;
32
+ dotNotCreateIndex?: boolean;
33
+ }
34
+ export function RelateTo<T, TRelated>(p: IRelatedTypeWithType<T, TRelated>): (target: T, key: string) => any;
35
+ export function RelateTo<T, TRelated>(c: IClassOf<TRelated>, p: IRelatedType<T, TRelated>): (target: T, key: string) => any;
36
+ export function RelateTo(c, p?): any {
76
37
 
77
- property: (item: T) => TRelated;
78
- inverseProperty: (item: TRelated) => T[];
79
- inverseKey?: (item: TRelated) => any;
80
- dotNotCreateIndex?: boolean;
38
+ if (p === void 0) {
39
+ p = c;
40
+ c = p.type?.();
81
41
  }
82
42
 
83
- ) {
84
- return (target: T, foreignKey: string): any => {
43
+ const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex } = p;
44
+
45
+ return (target: any, foreignKey: string): any => {
85
46
 
86
47
  const cn = target.constructor ?? target;
87
- const type = SchemaRegistry.model(cn);
48
+ const entityType = SchemaRegistry.model(cn);
49
+
50
+ const name = NameParser.parseMember(property);
88
51
 
89
- type.addRelation({
90
- type,
91
- name: NameParser.parseMember(property),
52
+ entityType.addRelation({
53
+ type: entityType,
54
+ name,
92
55
  foreignKey,
93
- relatedTypeClass: c,
56
+ relatedTypeClass: c ?? (Reflect as any).getMetadata("design:type", target, name),
94
57
  relatedName: NameParser.parseMember(inverseProperty),
95
58
  relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
96
59
  dotNotCreateIndex
@@ -99,34 +62,65 @@ export function RelateTo<T, TRelated>(c: IClassOf<TRelated>,
99
62
  };
100
63
  }
101
64
 
65
+ export function RelateToOne<T, TRelated>(p: IRelatedTypeOneWithType<T, TRelated>): (target: T, key: string) => any;
66
+ export function RelateToOne<T, TRelated>(c: IClassOf<TRelated>, p: IRelatedTypeOne<T, TRelated>): (target: T, key: string) => any;
67
+ export function RelateToOne(c, p?): any {
102
68
 
103
- export function RelateToOne<T, TRelated>(c: IClassOf<TRelated>,
104
- {
105
- property: name, inverseProperty: inv, inverseKey: invKey, dotNotCreateIndex
106
- }: {
107
-
108
- property: (item: T) => TRelated;
109
- inverseProperty: (item: TRelated) => T;
110
-
111
- inverseKey?: (item: TRelated) => any;
112
- dotNotCreateIndex?: boolean;
69
+ if (p === void 0) {
70
+ p = c;
71
+ c = p.type?.();
113
72
  }
114
73
 
115
- ) {
116
- return (target: T, key: string): any => {
74
+ const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex } = p;
75
+
76
+ return (target: any, foreignKey: string): any => {
117
77
 
118
78
  const cn = target.constructor ?? target;
119
- const type = SchemaRegistry.model(cn);
120
-
121
- const r = type.addRelation({
122
- type,
123
- name: NameParser.parseMember(name),
124
- foreignKey: key,
125
- relatedTypeClass: c,
126
- relatedName: NameParser.parseMember(inv),
79
+ const entityType = SchemaRegistry.model(cn);
80
+
81
+ const name = NameParser.parseMember(property);
82
+
83
+ entityType.addRelation({
84
+ type: entityType,
85
+ name,
86
+ foreignKey,
87
+ relatedTypeClass: c ?? (Reflect as any).getMetadata("design:type", target, name),
88
+ relatedName: NameParser.parseMember(inverseProperty),
127
89
  relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
128
90
  dotNotCreateIndex,
129
91
  singleInverseRelation: true
130
92
  });
93
+
131
94
  };
132
95
  }
96
+
97
+ // export function RelateToOne<T, TRelated>(c: IClassOf<TRelated>,
98
+ // {
99
+ // property: name, inverseProperty: inv, inverseKey: invKey, dotNotCreateIndex
100
+ // }: {
101
+
102
+ // property: (item: T) => TRelated;
103
+ // inverseProperty: (item: TRelated) => T;
104
+
105
+ // inverseKey?: (item: TRelated) => any;
106
+ // dotNotCreateIndex?: boolean;
107
+ // }
108
+
109
+ // ) {
110
+ // return (target: T, key: string): any => {
111
+
112
+ // const cn = target.constructor ?? target;
113
+ // const type = SchemaRegistry.model(cn);
114
+
115
+ // const r = type.addRelation({
116
+ // type,
117
+ // name: NameParser.parseMember(name),
118
+ // foreignKey: key,
119
+ // relatedTypeClass: c,
120
+ // relatedName: NameParser.parseMember(inv),
121
+ // relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
122
+ // dotNotCreateIndex,
123
+ // singleInverseRelation: true
124
+ // });
125
+ // };
126
+ // }
@@ -116,11 +116,12 @@ export default class ChangeEntry<T = any> implements IChanges {
116
116
  }
117
117
  }
118
118
 
119
+ if (this.status === "inserted") {
120
+ return;
121
+ }
119
122
 
120
123
  if (this.modified.size > 0) {
121
- if (this.status !== "inserted") {
122
- this.status = "modified";
123
- }
124
+ this.status = "modified";
124
125
  } else {
125
126
  this.status = "unchanged";
126
127
  }
@@ -1,6 +1,6 @@
1
1
  import EntityContext from "../../model/EntityContext.js";
2
2
  import Column from "../../decorators/Column.js";
3
- import Relate, { RelateTo, RelateToOne } from "../../decorators/Relate.js";
3
+ import { RelateTo, RelateToOne } from "../../decorators/Relate.js";
4
4
  import Table from "../../decorators/Table.js";
5
5
  import Index from "../../decorators/Index.js";
6
6
  import DateTime from "../../types/DateTime.js";
@@ -263,7 +263,8 @@ export class OrderItem {
263
263
  public orderItemID: number;
264
264
 
265
265
  @Column()
266
- @RelateTo(Order, {
266
+ @RelateTo({
267
+ type: () => Order,
267
268
  property: (orderItem) => orderItem.order,
268
269
  inverseProperty: (order) => order.orderItems
269
270
  })
@@ -3,70 +3,77 @@ import assert from "assert";
3
3
  import ObjectPool from "../../common/ObjectPool.js";
4
4
  import sleep from "../../common/sleep.js";
5
5
 
6
- export default async function () {
7
- const pool = new ObjectPool({
8
- asyncFactory: async () => {
9
- await sleep(10);
10
- return Promise.resolve({});
11
- },
12
- subscribeForRemoval: (po, clear) => void 0,
13
- destroy(item) {
14
- return sleep(10);
15
- },
16
- maxSize: 5,
17
- poolSize: 2,
18
- maxWait: 100
19
- });
20
6
 
21
- const c1 = await pool.acquire();
22
- const c2 = await pool.acquire();
7
+ export default async function () {
8
+ let id = 1;
9
+ const logs = [] as string[];
10
+ try {
11
+ const pool = new ObjectPool({
12
+ asyncFactory: async () => {
13
+ await sleep(1);
14
+ return Promise.resolve({ id: id++, toString() { return `item-${this.id}`; } });
15
+ },
16
+ subscribeForRemoval: (po, clear) => void 0,
17
+ destroy(item) {
18
+ return Promise.resolve();
19
+ },
20
+ maxSize: 5,
21
+ poolSize: 2,
22
+ maxWait: 5000,
23
+ logger: (t) => logs.push(t)
24
+ });
23
25
 
24
- await c1[Symbol.asyncDisposable]();
26
+ const c1 = await pool.acquire();
27
+ const c2 = await pool.acquire();
25
28
 
26
- assert.equal(pool.freeSize, 1);
29
+ assert.equal(2, c2.id);
27
30
 
28
- const c3 = await pool.acquire();
29
- assert.equal(c1, c3);
31
+ assert.notStrictEqual(c1, c2);
30
32
 
31
- assert.equal(pool.freeSize, 0);
33
+ await c1[Symbol.asyncDisposable]();
32
34
 
33
- const c4 = await pool.acquire();
34
- assert.notEqual(c4, c1);
35
- assert.notEqual(c4, c2);
35
+ assert.equal(pool.freeSize, 1);
36
36
 
37
- assert.equal(pool.currentSize, 3);
37
+ const c3 = await pool.acquire();
38
+ assert.strictEqual(c1, c3);
39
+ assert.notStrictEqual(c2, c3);
40
+ assert.strictEqual(pool.freeSize, 0);
38
41
 
39
- await c3[Symbol.asyncDisposable]();
42
+ const c4 = await pool.acquire();
43
+ assert.notStrictEqual(c4, c1);
44
+ assert.notStrictEqual(c4, c2);
40
45
 
41
- assert.equal(pool.currentSize, 3);
46
+ assert.strictEqual(pool.currentSize, 3);
42
47
 
43
- await c4[Symbol.asyncDisposable]();
44
- await c2[Symbol.asyncDisposable]();
48
+ await c4[Symbol.asyncDisposable]();
49
+ await c2[Symbol.asyncDisposable]();
45
50
 
46
- assert.equal(pool.currentSize, 2);
51
+ assert.equal(pool.currentSize, 3);
47
52
 
48
- assert.equal(pool.freeSize, 2);
53
+ assert.equal(pool.freeSize, 2);
49
54
 
50
- await pool.acquire();
51
- await pool.acquire();
52
- await pool.acquire();
53
- await pool.acquire();
54
- const last = await pool.acquire();
55
- let lastError;
56
- try {
57
55
  await pool.acquire();
58
- } catch (error) {
59
- lastError = error;
60
- }
61
- if (!lastError) {
62
- assert.fail("Failed");
56
+ await pool.acquire();
57
+ await pool.acquire();
58
+ const last = await pool.acquire();
59
+ let lastError;
60
+ try {
61
+ await pool.acquire();
62
+ } catch (error) {
63
+ lastError = error;
64
+ }
65
+ if (!lastError) {
66
+ assert.fail("Failed");
67
+ }
68
+
69
+ // free last after few milliseconds
70
+ setTimeout(() => {
71
+ last[Symbol.asyncDisposable]().catch(console.error);
72
+ }, 10);
73
+
74
+ // this should not fail
75
+ await pool.acquire();
76
+ } finally {
77
+ console.log(logs.join("\n"));
63
78
  }
64
-
65
- // free last after few milliseconds
66
- setTimeout(() => {
67
- last[Symbol.asyncDisposable]().catch(console.error);
68
- }, 10);
69
-
70
- // this should not fail
71
- await pool.acquire();
72
79
  }