@furystack/core 15.2.1 → 15.2.2
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/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/src/store-manager.ts +74 -74
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/core",
|
|
3
|
-
"version": "15.2.
|
|
3
|
+
"version": "15.2.2",
|
|
4
4
|
"description": "Core FuryStack package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://github.com/furystack/furystack",
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@types/node": "^25.3.
|
|
49
|
+
"@types/node": "^25.3.1",
|
|
50
50
|
"typescript": "^5.9.3",
|
|
51
51
|
"vitest": "^4.0.18"
|
|
52
52
|
},
|
package/src/store-manager.ts
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import type { Constructable } from '@furystack/inject'
|
|
2
|
-
import { Injectable } from '@furystack/inject'
|
|
3
|
-
import { isAsyncDisposable, isDisposable } from '@furystack/utils'
|
|
4
|
-
import { AggregatedError } from './errors/aggregated-error.js'
|
|
5
|
-
import type { PhysicalStore } from './models/physical-store.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Manager class for store instances
|
|
9
|
-
*/
|
|
10
|
-
@Injectable({ lifetime: 'singleton' })
|
|
11
|
-
export class StoreManager implements AsyncDisposable {
|
|
12
|
-
/**
|
|
13
|
-
* Disposes the StoreManager and all store instances
|
|
14
|
-
*/
|
|
15
|
-
public async [Symbol.asyncDispose]() {
|
|
16
|
-
const result = await Promise.allSettled(
|
|
17
|
-
[...this.stores.entries()].map(async ([_model, store]) => {
|
|
18
|
-
if (isDisposable(store)) {
|
|
19
|
-
store[Symbol.dispose]()
|
|
20
|
-
}
|
|
21
|
-
if (isAsyncDisposable(store)) {
|
|
22
|
-
await store[Symbol.asyncDispose]()
|
|
23
|
-
}
|
|
24
|
-
}),
|
|
25
|
-
)
|
|
26
|
-
const fails = result.filter((r) => r.status === 'rejected')
|
|
27
|
-
if (fails && fails.length) {
|
|
28
|
-
const error = new AggregatedError(
|
|
29
|
-
`There was an error during disposing ${fails.length} stores: ${fails.map((f) => f.reason as string).join(', ')}`,
|
|
30
|
-
fails,
|
|
31
|
-
)
|
|
32
|
-
throw error
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
private stores: Map<Constructable<unknown>, PhysicalStore<any, any>> = new Map()
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Returns a store model for a constructable object.
|
|
39
|
-
* Throws error if no store is registered
|
|
40
|
-
*
|
|
41
|
-
* **Note:** For application-level data access, prefer `getDataSetFor` from `@furystack/repository`.
|
|
42
|
-
* Writing through the DataSet ensures authorization, modification hooks, and change events
|
|
43
|
-
* (required for entity sync) are properly triggered.
|
|
44
|
-
* This method is intended for internal use by `Repository.createDataSet` and for physical store tests.
|
|
45
|
-
*
|
|
46
|
-
* @param model The Constructable object
|
|
47
|
-
* @param primaryKey The Primary Key field
|
|
48
|
-
* @throws if the Store is not registered
|
|
49
|
-
* @returns a Store object
|
|
50
|
-
*/
|
|
51
|
-
public getStoreFor<T, TPrimaryKey extends keyof T, TType extends PhysicalStore<T, TPrimaryKey>>(
|
|
52
|
-
model: Constructable<T>,
|
|
53
|
-
primaryKey: TPrimaryKey,
|
|
54
|
-
) {
|
|
55
|
-
const instance = this.stores.get(model)
|
|
56
|
-
if (!instance) {
|
|
57
|
-
throw Error(`Store not found for '${model.name}'`)
|
|
58
|
-
}
|
|
59
|
-
if (primaryKey !== instance.primaryKey) {
|
|
60
|
-
throw Error('Primary keys not match')
|
|
61
|
-
}
|
|
62
|
-
return instance as TType
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Adds a store instance to the StoreManager class
|
|
67
|
-
* @param store The store to add
|
|
68
|
-
* @returns the StoreManager instance for chaining
|
|
69
|
-
*/
|
|
70
|
-
public addStore<T, TPrimaryKey extends keyof T>(store: PhysicalStore<T, TPrimaryKey>) {
|
|
71
|
-
this.stores.set(store.model, store as PhysicalStore<any, any>)
|
|
72
|
-
return this
|
|
73
|
-
}
|
|
74
|
-
}
|
|
1
|
+
import type { Constructable } from '@furystack/inject'
|
|
2
|
+
import { Injectable } from '@furystack/inject'
|
|
3
|
+
import { isAsyncDisposable, isDisposable } from '@furystack/utils'
|
|
4
|
+
import { AggregatedError } from './errors/aggregated-error.js'
|
|
5
|
+
import type { PhysicalStore } from './models/physical-store.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Manager class for store instances
|
|
9
|
+
*/
|
|
10
|
+
@Injectable({ lifetime: 'singleton' })
|
|
11
|
+
export class StoreManager implements AsyncDisposable {
|
|
12
|
+
/**
|
|
13
|
+
* Disposes the StoreManager and all store instances
|
|
14
|
+
*/
|
|
15
|
+
public async [Symbol.asyncDispose]() {
|
|
16
|
+
const result = await Promise.allSettled(
|
|
17
|
+
[...this.stores.entries()].map(async ([_model, store]) => {
|
|
18
|
+
if (isDisposable(store)) {
|
|
19
|
+
store[Symbol.dispose]()
|
|
20
|
+
}
|
|
21
|
+
if (isAsyncDisposable(store)) {
|
|
22
|
+
await store[Symbol.asyncDispose]()
|
|
23
|
+
}
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
const fails = result.filter((r) => r.status === 'rejected')
|
|
27
|
+
if (fails && fails.length) {
|
|
28
|
+
const error = new AggregatedError(
|
|
29
|
+
`There was an error during disposing ${fails.length} stores: ${fails.map((f) => f.reason as string).join(', ')}`,
|
|
30
|
+
fails,
|
|
31
|
+
)
|
|
32
|
+
throw error
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
private stores: Map<Constructable<unknown>, PhysicalStore<any, any>> = new Map()
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns a store model for a constructable object.
|
|
39
|
+
* Throws error if no store is registered
|
|
40
|
+
*
|
|
41
|
+
* **Note:** For application-level data access, prefer `getDataSetFor` from `@furystack/repository`.
|
|
42
|
+
* Writing through the DataSet ensures authorization, modification hooks, and change events
|
|
43
|
+
* (required for entity sync) are properly triggered.
|
|
44
|
+
* This method is intended for internal use by `Repository.createDataSet` and for physical store tests.
|
|
45
|
+
*
|
|
46
|
+
* @param model The Constructable object
|
|
47
|
+
* @param primaryKey The Primary Key field
|
|
48
|
+
* @throws if the Store is not registered
|
|
49
|
+
* @returns a Store object
|
|
50
|
+
*/
|
|
51
|
+
public getStoreFor<T, TPrimaryKey extends keyof T, TType extends PhysicalStore<T, TPrimaryKey>>(
|
|
52
|
+
model: Constructable<T>,
|
|
53
|
+
primaryKey: TPrimaryKey,
|
|
54
|
+
) {
|
|
55
|
+
const instance = this.stores.get(model)
|
|
56
|
+
if (!instance) {
|
|
57
|
+
throw Error(`Store not found for '${model.name}'`)
|
|
58
|
+
}
|
|
59
|
+
if (primaryKey !== instance.primaryKey) {
|
|
60
|
+
throw Error('Primary keys not match')
|
|
61
|
+
}
|
|
62
|
+
return instance as TType
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Adds a store instance to the StoreManager class
|
|
67
|
+
* @param store The store to add
|
|
68
|
+
* @returns the StoreManager instance for chaining
|
|
69
|
+
*/
|
|
70
|
+
public addStore<T, TPrimaryKey extends keyof T>(store: PhysicalStore<T, TPrimaryKey>) {
|
|
71
|
+
this.stores.set(store.model, store as PhysicalStore<any, any>)
|
|
72
|
+
return this
|
|
73
|
+
}
|
|
74
|
+
}
|