@furystack/repository 10.1.11 → 11.0.1
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 +21 -0
- package/README.md +74 -36
- package/esm/data-set-setting.d.ts +30 -44
- package/esm/data-set-setting.d.ts.map +1 -1
- package/esm/data-set.d.ts +17 -68
- package/esm/data-set.d.ts.map +1 -1
- package/esm/data-set.js +18 -69
- package/esm/data-set.js.map +1 -1
- package/esm/data-set.spec.d.ts +2 -0
- package/esm/data-set.spec.d.ts.map +1 -0
- package/esm/data-set.spec.js +348 -0
- package/esm/data-set.spec.js.map +1 -0
- package/esm/define-data-set.d.ts +71 -0
- package/esm/define-data-set.d.ts.map +1 -0
- package/esm/define-data-set.js +65 -0
- package/esm/define-data-set.js.map +1 -0
- package/esm/define-data-set.spec.d.ts +2 -0
- package/esm/define-data-set.spec.d.ts.map +1 -0
- package/esm/define-data-set.spec.js +134 -0
- package/esm/define-data-set.spec.js.map +1 -0
- package/esm/helpers.d.ts +12 -29
- package/esm/helpers.d.ts.map +1 -1
- package/esm/helpers.js +8 -28
- package/esm/helpers.js.map +1 -1
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/src/data-set-setting.ts +32 -46
- package/src/data-set.spec.ts +397 -0
- package/src/data-set.ts +28 -72
- package/src/define-data-set.spec.ts +145 -0
- package/src/define-data-set.ts +122 -0
- package/src/helpers.ts +14 -33
- package/src/index.ts +2 -2
- package/esm/dataset.spec.d.ts +0 -2
- package/esm/dataset.spec.d.ts.map +0 -1
- package/esm/dataset.spec.js +0 -552
- package/esm/dataset.spec.js.map +0 -1
- package/esm/repository.d.ts +0 -15
- package/esm/repository.d.ts.map +0 -1
- package/esm/repository.js +0 -50
- package/esm/repository.js.map +0 -1
- package/esm/repository.spec.d.ts +0 -2
- package/esm/repository.spec.d.ts.map +0 -1
- package/esm/repository.spec.js +0 -18
- package/esm/repository.spec.js.map +0 -1
- package/src/dataset.spec.ts +0 -641
- package/src/repository.spec.ts +0 -19
- package/src/repository.ts +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [11.0.1] - 2026-05-21
|
|
4
|
+
|
|
5
|
+
### 📚 Documentation
|
|
6
|
+
|
|
7
|
+
- Rewrote JSDoc on `DataSet`, `DataSetSetting`, `defineDataSet`, and the helpers to follow the new value-test guidance: dropped restate-the-type narration, kept intent / trade-offs / constraints around authorization, validation, and the lifecycle hooks.
|
|
8
|
+
|
|
9
|
+
### ⬆️ Dependencies
|
|
10
|
+
|
|
11
|
+
- Bump dev `vitest` to `^4.1.5`.
|
|
12
|
+
- Bumped `vitest` to `^4.1.7`. No source changes — dev-tooling bump only.
|
|
13
|
+
|
|
14
|
+
## [11.0.0] - 2026-04-25
|
|
15
|
+
|
|
16
|
+
### 💥 Breaking Changes
|
|
17
|
+
|
|
18
|
+
The `Repository` class is gone — DataSets are now first-class DI tokens. See the [v7 migration guide](../../docs/migrations/v7-functional-di.md) for rationale, recipes, and pitfalls.
|
|
19
|
+
|
|
20
|
+
- Removed `Repository`, `getRepository(injector)`, `Repository.createDataSet(Model, 'pk', settings)`. Declare DataSets with `defineDataSet({ name, store, settings? })` at module scope; the returned `DataSetToken<T, PK>` resolves a configured `DataSet`.
|
|
21
|
+
- `getDataSetFor(injector, DataSetToken)` now takes a single token argument. The old `(injector, Model, 'pk')` overload is gone.
|
|
22
|
+
- Every consumer that used to take `{ model, primaryKey }` (endpoint generators, `registerModel`, etc.) now takes a `DataSetToken` directly.
|
|
23
|
+
|
|
3
24
|
## [10.1.11] - 2026-04-17
|
|
4
25
|
|
|
5
26
|
### ⬆️ Dependencies
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @furystack/repository
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
DataSet implementation for FuryStack. A DataSet wraps a physical store with
|
|
4
|
+
entity-level business logic — authorization, modification hooks, and change
|
|
5
|
+
events — in a structured way.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,14 +12,17 @@ npm install @furystack/repository
|
|
|
12
12
|
yarn add @furystack/repository
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
## Setting Up a
|
|
15
|
+
## Setting Up a DataSet
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
A DataSet is declared with `defineDataSet`. It takes the underlying
|
|
18
|
+
`StoreToken` (from `@furystack/core` or a backend adapter) and optional
|
|
19
|
+
settings (authorizers, modifiers, event callbacks). The returned
|
|
20
|
+
`DataSetToken` is a DI token that resolves to a ready-to-use `DataSet`.
|
|
18
21
|
|
|
19
22
|
```ts
|
|
20
|
-
import {
|
|
21
|
-
import { InMemoryStore,
|
|
22
|
-
import {
|
|
23
|
+
import { createInjector } from '@furystack/inject'
|
|
24
|
+
import { InMemoryStore, defineStore } from '@furystack/core'
|
|
25
|
+
import { defineDataSet, getDataSetFor } from '@furystack/repository'
|
|
23
26
|
import { getLogger } from '@furystack/logging'
|
|
24
27
|
|
|
25
28
|
class MyModel {
|
|
@@ -27,57 +30,88 @@ class MyModel {
|
|
|
27
30
|
declare value: string
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const MyStore = defineStore({
|
|
34
|
+
name: 'my-app/MyStore',
|
|
35
|
+
model: MyModel,
|
|
36
|
+
primaryKey: 'id',
|
|
37
|
+
factory: () => new InMemoryStore({ model: MyModel, primaryKey: 'id' }),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const MyDataSet = defineDataSet({
|
|
41
|
+
name: 'my-app/MyDataSet',
|
|
42
|
+
store: MyStore,
|
|
43
|
+
settings: {
|
|
44
|
+
onEntityAdded: ({ injector, entity }) => {
|
|
45
|
+
getLogger(injector).verbose({ message: `An entity was added with value '${entity.value}'` })
|
|
46
|
+
},
|
|
47
|
+
authorizeUpdate: async () => ({
|
|
48
|
+
isAllowed: false,
|
|
49
|
+
message: 'This is a read-only dataset. No update is allowed. :(',
|
|
50
|
+
}),
|
|
35
51
|
},
|
|
36
|
-
authorizeUpdate: async () => ({
|
|
37
|
-
isAllowed: false,
|
|
38
|
-
message: 'This is a read-only dataset. No update is allowed. :(',
|
|
39
|
-
}),
|
|
40
52
|
})
|
|
41
|
-
```
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
const myInjector = createInjector()
|
|
55
|
+
```
|
|
45
56
|
|
|
46
57
|
### Working with the DataSet
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
Resolve via `injector.get(MyDataSet)` or the convenience helper
|
|
60
|
+
`getDataSetFor(injector, MyDataSet)`:
|
|
50
61
|
|
|
51
62
|
```ts
|
|
52
|
-
const dataSet = getDataSetFor(myInjector,
|
|
53
|
-
dataSet.add(myInjector, { id: 1, value: 'foo' }) // <--
|
|
54
|
-
dataSet.update(myInjector, 1, { id: 1, value: 'bar' }) //
|
|
63
|
+
const dataSet = getDataSetFor(myInjector, MyDataSet)
|
|
64
|
+
await dataSet.add(myInjector, { id: 1, value: 'foo' }) // <-- logs via onEntityAdded
|
|
65
|
+
await dataSet.update(myInjector, 1, { id: 1, value: 'bar' }) // <-- rejected by authorizeUpdate
|
|
55
66
|
```
|
|
56
67
|
|
|
57
68
|
### Events
|
|
58
69
|
|
|
59
|
-
Events are great for logging
|
|
70
|
+
Events are great for logging, monitoring DataSet changes, or distributing
|
|
71
|
+
changes to clients. They are optional callbacks — if defined, they are called
|
|
72
|
+
on a specific event. Supported events: `onEntityAdded`, `onEntityUpdated`,
|
|
73
|
+
`onEntityRemoved`.
|
|
60
74
|
|
|
61
75
|
### Authorizing operations
|
|
62
76
|
|
|
63
|
-
**Authorizers** are similar callbacks
|
|
77
|
+
**Authorizers** are similar callbacks that return a promise with an
|
|
78
|
+
`AuthorizationResult`. You can allow or deny CRUD operations, or add
|
|
79
|
+
additional filters to collections. Supported authorizers: `authorizeAdd`,
|
|
80
|
+
`authorizeUpdate`, `authorizeUpdateEntity` (reloads the entity, compares with
|
|
81
|
+
the original), `authorizeRemove`, `authorizeRemoveEntity`, `authorizeGet`,
|
|
82
|
+
`authorizeGetEntity`.
|
|
64
83
|
|
|
65
84
|
### Modifiers and additional filters
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
`modifyOnAdd` / `modifyOnUpdate` transform entities before persisting (e.g.
|
|
87
|
+
fill `createdByUser` / `lastModifiedByUser`). `addFilter` injects a
|
|
88
|
+
pre-filter condition **before** a user-supplied filter expression is
|
|
89
|
+
evaluated, ensuring the caller only ever sees entities they have permission
|
|
90
|
+
for.
|
|
69
91
|
|
|
70
92
|
### Getting the Context
|
|
71
93
|
|
|
72
|
-
|
|
94
|
+
Every callback receives an `injector` — use it to resolve request-scoped
|
|
95
|
+
services like `HttpUserContext` to identify the caller.
|
|
73
96
|
|
|
74
97
|
### Server-side writes and the elevated IdentityContext
|
|
75
98
|
|
|
76
|
-
The DataSet is the **recommended write gateway** for all entity mutations.
|
|
99
|
+
The DataSet is the **recommended write gateway** for all entity mutations.
|
|
100
|
+
Writing through the DataSet ensures that authorization rules, modification
|
|
101
|
+
hooks, and change events (`onEntityAdded`, `onEntityUpdated`,
|
|
102
|
+
`onEntityRemoved`) all fire. These events are required for features like
|
|
103
|
+
[entity sync](./../entity-sync-service/README.md) to work correctly.
|
|
77
104
|
|
|
78
|
-
> **Warning:** Writing directly to the underlying physical store bypasses
|
|
105
|
+
> **Warning:** Writing directly to the underlying physical store bypasses
|
|
106
|
+
> the DataSet layer entirely. No authorization checks, hooks, or events
|
|
107
|
+
> fire, and downstream consumers (such as entity sync) will **not** be
|
|
108
|
+
> notified of the change. The `furystack/no-direct-store-token` lint rule
|
|
109
|
+
> guards against this in application code.
|
|
79
110
|
|
|
80
|
-
For server-side or background operations that don't originate from an HTTP
|
|
111
|
+
For server-side or background operations that don't originate from an HTTP
|
|
112
|
+
request (e.g. scheduled jobs, migrations, seed scripts), you won't have a
|
|
113
|
+
user session. Use `useSystemIdentityContext` from `@furystack/core` to
|
|
114
|
+
create a scoped child injector with elevated privileges:
|
|
81
115
|
|
|
82
116
|
```ts
|
|
83
117
|
import { useSystemIdentityContext } from '@furystack/core'
|
|
@@ -85,12 +119,16 @@ import { getDataSetFor } from '@furystack/repository'
|
|
|
85
119
|
import { usingAsync } from '@furystack/utils'
|
|
86
120
|
|
|
87
121
|
await usingAsync(useSystemIdentityContext({ injector, username: 'background-job' }), async (systemInjector) => {
|
|
88
|
-
const dataSet = getDataSetFor(systemInjector,
|
|
122
|
+
const dataSet = getDataSetFor(systemInjector, MyDataSet)
|
|
89
123
|
await dataSet.add(systemInjector, { value: 'created by background job' })
|
|
90
124
|
})
|
|
91
125
|
// systemInjector is disposed here -- all scoped instances cleaned up
|
|
92
126
|
```
|
|
93
127
|
|
|
94
|
-
> **Warning:** `useSystemIdentityContext` bypasses **all** authorization
|
|
128
|
+
> **Warning:** `useSystemIdentityContext` bypasses **all** authorization
|
|
129
|
+
> checks. Only use it in trusted server-side contexts. Never pass the
|
|
130
|
+
> returned injector to user-facing request handlers.
|
|
95
131
|
|
|
96
|
-
This pattern ensures that all writes go through the same pipeline, keeping
|
|
132
|
+
This pattern ensures that all writes go through the same pipeline, keeping
|
|
133
|
+
authorization, hooks, and event-driven features consistent regardless of
|
|
134
|
+
the caller.
|
|
@@ -1,98 +1,84 @@
|
|
|
1
1
|
import type { FindOptions, PhysicalStore, WithOptionalId } from '@furystack/core';
|
|
2
2
|
import type { Injector } from '@furystack/inject';
|
|
3
|
-
/**
|
|
4
|
-
* The result model returned by authorizers
|
|
5
|
-
*/
|
|
3
|
+
/** Discriminated union returned by authorizer callbacks. */
|
|
6
4
|
export type AuthorizationResult = SuccessfullyValidationResult | FailedValidationResult;
|
|
7
5
|
export interface SuccessfullyValidationResult {
|
|
8
|
-
/**
|
|
9
|
-
* Indicates if the validation was successful
|
|
10
|
-
*/
|
|
11
6
|
isAllowed: true;
|
|
12
7
|
}
|
|
13
8
|
export interface FailedValidationResult {
|
|
14
|
-
/**
|
|
15
|
-
* Indicates if the validation was successful
|
|
16
|
-
*/
|
|
17
9
|
isAllowed: false;
|
|
18
|
-
/**
|
|
19
|
-
* The error message string
|
|
20
|
-
*/
|
|
10
|
+
/** Surfaced as the message of the thrown {@link AuthorizationError}. */
|
|
21
11
|
message: string;
|
|
22
12
|
}
|
|
23
13
|
/**
|
|
24
|
-
*
|
|
14
|
+
* Configuration for a {@link DataSet}: backing store + authorization +
|
|
15
|
+
* modification hooks + filter post-processing. Every callback is optional —
|
|
16
|
+
* an empty `DataSetSettings` performs no checks and forwards calls straight
|
|
17
|
+
* to the physical store.
|
|
18
|
+
*
|
|
19
|
+
* Authorization callbacks come in two flavours:
|
|
20
|
+
*
|
|
21
|
+
* - `authorizeX` — runs **before** the entity is loaded. Use for cheap,
|
|
22
|
+
* identity-only checks ("is the user logged in?", "is the user an admin?").
|
|
23
|
+
* When omitted, no pre-check is performed.
|
|
24
|
+
* - `authorizeXEntity` — runs **after** the entity is loaded, with the
|
|
25
|
+
* loaded entity in scope. Use for ownership / row-level checks. Missing
|
|
26
|
+
* entities skip the per-entity check entirely.
|
|
27
|
+
*
|
|
28
|
+
* A callback returning `{ isAllowed: false }` makes the operation throw
|
|
29
|
+
* {@link AuthorizationError}.
|
|
25
30
|
*/
|
|
26
31
|
export interface DataSetSettings<T, TPrimaryKey extends keyof T, TWritableData = WithOptionalId<T, TPrimaryKey>> {
|
|
27
|
-
/**
|
|
28
|
-
* An instance of a physical store
|
|
29
|
-
*/
|
|
30
32
|
physicalStore: PhysicalStore<T, TPrimaryKey, TWritableData>;
|
|
31
|
-
/**
|
|
32
|
-
* Authorizes the entity creation
|
|
33
|
-
*/
|
|
33
|
+
/** Pre-persist authorization for `add`. */
|
|
34
34
|
authorizeAdd?: (options: {
|
|
35
35
|
injector: Injector;
|
|
36
36
|
entity: TWritableData;
|
|
37
37
|
}) => Promise<AuthorizationResult>;
|
|
38
|
-
/**
|
|
39
|
-
* modifies an entity before persisting on creation
|
|
40
|
-
*/
|
|
38
|
+
/** Pre-persist mutation for `add` (default fields, hashing, normalisation). */
|
|
41
39
|
modifyOnAdd?: (options: {
|
|
42
40
|
injector: Injector;
|
|
43
41
|
entity: TWritableData;
|
|
44
42
|
}) => Promise<TWritableData>;
|
|
45
|
-
/**
|
|
46
|
-
* Authorizes entity updates (before the entity gets loaded from the store)
|
|
47
|
-
*/
|
|
43
|
+
/** Pre-load authorization for `update`. Sees the change, not the entity. */
|
|
48
44
|
authorizeUpdate?: (options: {
|
|
49
45
|
injector: Injector;
|
|
50
46
|
change: Partial<T>;
|
|
51
47
|
}) => Promise<AuthorizationResult>;
|
|
52
|
-
/**
|
|
53
|
-
* Authorizes entity updates per loaded entity
|
|
54
|
-
*/
|
|
48
|
+
/** Post-load authorization for `update`. Sees both the loaded entity and the change. */
|
|
55
49
|
authorizeUpdateEntity?: (options: {
|
|
56
50
|
injector: Injector;
|
|
57
51
|
entity: T;
|
|
58
52
|
change: Partial<T>;
|
|
59
53
|
}) => Promise<AuthorizationResult>;
|
|
60
|
-
/**
|
|
61
|
-
* modifies an entity before persisting on update
|
|
62
|
-
*/
|
|
54
|
+
/** Pre-persist mutation for `update`. */
|
|
63
55
|
modifyOnUpdate?: (options: {
|
|
64
56
|
injector: Injector;
|
|
65
57
|
id: T[keyof T];
|
|
66
58
|
entity: Partial<T>;
|
|
67
59
|
}) => Promise<Partial<T>>;
|
|
68
|
-
/**
|
|
69
|
-
* Authorizes entity removal (before the entity gets loaded from the store)
|
|
70
|
-
*/
|
|
60
|
+
/** Pre-load authorization for `remove`. */
|
|
71
61
|
authorizeRemove?: (options: {
|
|
72
62
|
injector: Injector;
|
|
73
63
|
}) => Promise<AuthorizationResult>;
|
|
74
|
-
/**
|
|
75
|
-
* Authorizes entity removal per loaded entity
|
|
76
|
-
*/
|
|
64
|
+
/** Post-load authorization for `remove`, per loaded entity. */
|
|
77
65
|
authorizeRemoveEntity?: (options: {
|
|
78
66
|
injector: Injector;
|
|
79
67
|
entity: T;
|
|
80
68
|
}) => Promise<AuthorizationResult>;
|
|
81
|
-
/**
|
|
82
|
-
* Authorizes entity retrieval without entity loading
|
|
83
|
-
*/
|
|
69
|
+
/** Pre-load authorization for `get` / `find` / `count`. No entity in scope. */
|
|
84
70
|
authorizeGet?: (options: {
|
|
85
71
|
injector: Injector;
|
|
86
72
|
}) => Promise<AuthorizationResult>;
|
|
87
|
-
/**
|
|
88
|
-
* Authorizes entity retrieval
|
|
89
|
-
*/
|
|
73
|
+
/** Post-load authorization for `get`, per loaded entity. */
|
|
90
74
|
authorizeGetEntity?: (options: {
|
|
91
75
|
injector: Injector;
|
|
92
76
|
entity: T;
|
|
93
77
|
}) => Promise<AuthorizationResult>;
|
|
94
78
|
/**
|
|
95
|
-
*
|
|
79
|
+
* Filter post-processor. Receives the caller's {@link FindOptions} and
|
|
80
|
+
* returns a (potentially augmented) one. Use to inject mandatory clauses
|
|
81
|
+
* (tenant scoping, soft-delete masks) the caller cannot bypass.
|
|
96
82
|
*/
|
|
97
83
|
addFilter?: <TFields extends Array<keyof T>>(options: {
|
|
98
84
|
injector: Injector;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-set-setting.d.ts","sourceRoot":"","sources":["../src/data-set-setting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"data-set-setting.d.ts","sourceRoot":"","sources":["../src/data-set-setting.ts"],"names":[],"mappings":"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"}
|
package/esm/data-set.d.ts
CHANGED
|
@@ -3,30 +3,25 @@ import type { Injector } from '@furystack/inject';
|
|
|
3
3
|
import { EventHub, type ListenerErrorPayload } from '@furystack/utils';
|
|
4
4
|
import type { DataSetSettings } from './data-set-setting.js';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Authorization-enforcing wrapper around a {@link PhysicalStore}. The
|
|
7
|
+
* recommended write gateway for application code — `furystack/no-direct-store-token`
|
|
8
|
+
* enforces this. Each mutation runs the relevant `authorize*` and
|
|
9
|
+
* `modify*` callbacks from {@link DataSetSettings}, persists, then emits
|
|
10
|
+
* `onEntityAdded` / `onEntityUpdated` / `onEntityRemoved` (consumed by
|
|
11
|
+
* entity sync, audit logs).
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* All mutating methods require an `injector` parameter that provides the caller's context (e.g. the current
|
|
15
|
-
* user's identity). For server-side or background operations that don't originate from an HTTP request,
|
|
16
|
-
* use {@link useSystemIdentityContext} to create a scoped child injector with elevated privileges.
|
|
13
|
+
* Mutating methods take an `injector` parameter to surface caller identity
|
|
14
|
+
* to the authorizers. For server-side / background work without an HTTP
|
|
15
|
+
* request, wrap the injector with `useSystemIdentityContext` from
|
|
16
|
+
* `@furystack/core`.
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
-
* import { useSystemIdentityContext } from '@furystack/core'
|
|
21
|
-
* import { getDataSetFor } from '@furystack/repository'
|
|
22
|
-
* import { usingAsync } from '@furystack/utils'
|
|
23
|
-
*
|
|
24
|
-
* // Server-side write with an elevated identity
|
|
25
20
|
* await usingAsync(
|
|
26
21
|
* useSystemIdentityContext({ injector, username: 'background-job' }),
|
|
27
22
|
* async (systemInjector) => {
|
|
28
|
-
* const dataSet = getDataSetFor(systemInjector,
|
|
29
|
-
* await dataSet.add(systemInjector,
|
|
23
|
+
* const dataSet = getDataSetFor(systemInjector, UserDataSet)
|
|
24
|
+
* await dataSet.add(systemInjector, { username: 'alice', roles: [] })
|
|
30
25
|
* },
|
|
31
26
|
* )
|
|
32
27
|
* ```
|
|
@@ -48,64 +43,18 @@ export declare class DataSet<T, TPrimaryKey extends keyof T, TWritableData = Wit
|
|
|
48
43
|
onListenerError: ListenerErrorPayload;
|
|
49
44
|
}> implements Disposable {
|
|
50
45
|
readonly settings: DataSetSettings<T, TPrimaryKey, TWritableData>;
|
|
51
|
-
/**
|
|
52
|
-
* Primary key of the contained entity
|
|
53
|
-
*/
|
|
54
46
|
primaryKey: TPrimaryKey;
|
|
55
|
-
/**
|
|
56
|
-
* Adds an entity to the DataSet.
|
|
57
|
-
* Runs authorization checks, applies modification hooks, persists to the physical store,
|
|
58
|
-
* and emits an `onEntityAdded` event for each created entity.
|
|
59
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
60
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
61
|
-
* @param entities The entities to add
|
|
62
|
-
* @returns The CreateResult with the created entities
|
|
63
|
-
*/
|
|
64
47
|
add(injector: Injector, ...entities: TWritableData[]): Promise<CreateResult<T>>;
|
|
65
|
-
/**
|
|
66
|
-
* Updates an entity in the store.
|
|
67
|
-
* Runs authorization checks, applies modification hooks, persists to the physical store,
|
|
68
|
-
* and emits an `onEntityUpdated` event.
|
|
69
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
70
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
71
|
-
* @param id The identifier of the entity
|
|
72
|
-
* @param change The update
|
|
73
|
-
*/
|
|
74
48
|
update(injector: Injector, id: T[TPrimaryKey], change: Partial<T>): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Returns a Promise with the entity count
|
|
77
|
-
* @param injector The Injector from the context
|
|
78
|
-
* @param filter The Filter that will be applied
|
|
79
|
-
* @returns the Count
|
|
80
|
-
*/
|
|
81
49
|
count(injector: Injector, filter?: FilterType<T>): Promise<number>;
|
|
82
|
-
/**
|
|
83
|
-
* Returns a filtered subset of the entity
|
|
84
|
-
* @param injector The Injector from the context
|
|
85
|
-
* @param filter The Filter definition
|
|
86
|
-
* @returns A result with the current items
|
|
87
|
-
*/
|
|
88
50
|
find<TFields extends Array<keyof T>>(injector: Injector, filter: FindOptions<T, TFields>): Promise<Array<PartialResult<T, TFields>>>;
|
|
89
|
-
/**
|
|
90
|
-
* Returns an entity based on its primary key
|
|
91
|
-
* @param injector The injector from the context
|
|
92
|
-
* @param key The identifier of the entity
|
|
93
|
-
* @param select A field list used for projection
|
|
94
|
-
* @returns An item with the current unique key or Undefined
|
|
95
|
-
*/
|
|
96
51
|
get<TSelect extends Array<keyof T>>(injector: Injector, key: T[TPrimaryKey], select?: TSelect): Promise<PartialResult<T, TSelect> | undefined>;
|
|
97
52
|
/**
|
|
98
|
-
* Removes
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* are authorized. Keys that don't match any entity are silently forwarded to the
|
|
104
|
-
* physical store's `remove()` call.
|
|
105
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
106
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
107
|
-
* @param keys The primary keys of the entities to remove
|
|
108
|
-
* @returns A promise that will be resolved / rejected based on the remove success
|
|
53
|
+
* Removes by primary key. Pre-load `authorizeRemove` and per-entity
|
|
54
|
+
* `authorizeRemoveEntity` are all-or-nothing — any rejection aborts the
|
|
55
|
+
* whole batch before any persist call. When `authorizeRemoveEntity` is
|
|
56
|
+
* configured, missing keys are silently forwarded to the physical store
|
|
57
|
+
* (no entity to authorize).
|
|
109
58
|
*/
|
|
110
59
|
remove(injector: Injector, ...keys: Array<T[TPrimaryKey]>): Promise<void>;
|
|
111
60
|
constructor(settings: DataSetSettings<T, TPrimaryKey, TWritableData>);
|
package/esm/data-set.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-set.d.ts","sourceRoot":"","sources":["../src/data-set.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"data-set.d.ts","sourceRoot":"","sources":["../src/data-set.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EAEb,cAAc,EACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,OAAO,CAAC,CAAC,EAAE,WAAW,SAAS,MAAM,CAAC,EAAE,aAAa,GAAG,cAAc,CAAC,CAAC,EAAE,WAAW,CAAC,CACjG,SAAQ,QAAQ,CAAC;IACf,aAAa,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;IAChD,eAAe,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAA;IAC/E,eAAe,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,CAAA;KAAE,CAAA;IAC5D,eAAe,EAAE,oBAAoB,CAAA;CACtC,CACD,YAAW,UAAU;aA0IO,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC;IAxI7E,UAAU,EAAE,WAAW,CAAA;IAEjB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAyB/E,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBjF,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAUlE,IAAI,CAAC,OAAO,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,EAC9C,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAW/B,GAAG,CAAC,OAAO,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,EAC7C,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,EACnB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAwBjD;;;;;;OAMG;IACU,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;gBA2B1D,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC;CAIrF"}
|
package/esm/data-set.js
CHANGED
|
@@ -1,49 +1,32 @@
|
|
|
1
1
|
import { AuthorizationError, selectFields } from '@furystack/core';
|
|
2
2
|
import { EventHub } from '@furystack/utils';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Authorization-enforcing wrapper around a {@link PhysicalStore}. The
|
|
5
|
+
* recommended write gateway for application code — `furystack/no-direct-store-token`
|
|
6
|
+
* enforces this. Each mutation runs the relevant `authorize*` and
|
|
7
|
+
* `modify*` callbacks from {@link DataSetSettings}, persists, then emits
|
|
8
|
+
* `onEntityAdded` / `onEntityUpdated` / `onEntityRemoved` (consumed by
|
|
9
|
+
* entity sync, audit logs).
|
|
6
10
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* All mutating methods require an `injector` parameter that provides the caller's context (e.g. the current
|
|
13
|
-
* user's identity). For server-side or background operations that don't originate from an HTTP request,
|
|
14
|
-
* use {@link useSystemIdentityContext} to create a scoped child injector with elevated privileges.
|
|
11
|
+
* Mutating methods take an `injector` parameter to surface caller identity
|
|
12
|
+
* to the authorizers. For server-side / background work without an HTTP
|
|
13
|
+
* request, wrap the injector with `useSystemIdentityContext` from
|
|
14
|
+
* `@furystack/core`.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* ```ts
|
|
18
|
-
* import { useSystemIdentityContext } from '@furystack/core'
|
|
19
|
-
* import { getDataSetFor } from '@furystack/repository'
|
|
20
|
-
* import { usingAsync } from '@furystack/utils'
|
|
21
|
-
*
|
|
22
|
-
* // Server-side write with an elevated identity
|
|
23
18
|
* await usingAsync(
|
|
24
19
|
* useSystemIdentityContext({ injector, username: 'background-job' }),
|
|
25
20
|
* async (systemInjector) => {
|
|
26
|
-
* const dataSet = getDataSetFor(systemInjector,
|
|
27
|
-
* await dataSet.add(systemInjector,
|
|
21
|
+
* const dataSet = getDataSetFor(systemInjector, UserDataSet)
|
|
22
|
+
* await dataSet.add(systemInjector, { username: 'alice', roles: [] })
|
|
28
23
|
* },
|
|
29
24
|
* )
|
|
30
25
|
* ```
|
|
31
26
|
*/
|
|
32
27
|
export class DataSet extends EventHub {
|
|
33
28
|
settings;
|
|
34
|
-
/**
|
|
35
|
-
* Primary key of the contained entity
|
|
36
|
-
*/
|
|
37
29
|
primaryKey;
|
|
38
|
-
/**
|
|
39
|
-
* Adds an entity to the DataSet.
|
|
40
|
-
* Runs authorization checks, applies modification hooks, persists to the physical store,
|
|
41
|
-
* and emits an `onEntityAdded` event for each created entity.
|
|
42
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
43
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
44
|
-
* @param entities The entities to add
|
|
45
|
-
* @returns The CreateResult with the created entities
|
|
46
|
-
*/
|
|
47
30
|
async add(injector, ...entities) {
|
|
48
31
|
await Promise.all(entities.map(async (entity) => {
|
|
49
32
|
if (this.settings.authorizeAdd) {
|
|
@@ -62,15 +45,6 @@ export class DataSet extends EventHub {
|
|
|
62
45
|
});
|
|
63
46
|
return createResult;
|
|
64
47
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Updates an entity in the store.
|
|
67
|
-
* Runs authorization checks, applies modification hooks, persists to the physical store,
|
|
68
|
-
* and emits an `onEntityUpdated` event.
|
|
69
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
70
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
71
|
-
* @param id The identifier of the entity
|
|
72
|
-
* @param change The update
|
|
73
|
-
*/
|
|
74
48
|
async update(injector, id, change) {
|
|
75
49
|
if (this.settings.authorizeUpdate) {
|
|
76
50
|
const result = await this.settings.authorizeUpdate({ injector, change });
|
|
@@ -93,12 +67,6 @@ export class DataSet extends EventHub {
|
|
|
93
67
|
await this.settings.physicalStore.update(id, parsed);
|
|
94
68
|
this.emit('onEntityUpdated', { injector, change: parsed, id });
|
|
95
69
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Returns a Promise with the entity count
|
|
98
|
-
* @param injector The Injector from the context
|
|
99
|
-
* @param filter The Filter that will be applied
|
|
100
|
-
* @returns the Count
|
|
101
|
-
*/
|
|
102
70
|
async count(injector, filter) {
|
|
103
71
|
if (this.settings.authorizeGet) {
|
|
104
72
|
const result = await this.settings.authorizeGet({ injector });
|
|
@@ -108,12 +76,6 @@ export class DataSet extends EventHub {
|
|
|
108
76
|
}
|
|
109
77
|
return await this.settings.physicalStore.count(filter);
|
|
110
78
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Returns a filtered subset of the entity
|
|
113
|
-
* @param injector The Injector from the context
|
|
114
|
-
* @param filter The Filter definition
|
|
115
|
-
* @returns A result with the current items
|
|
116
|
-
*/
|
|
117
79
|
async find(injector, filter) {
|
|
118
80
|
if (this.settings.authorizeGet) {
|
|
119
81
|
const result = await this.settings.authorizeGet({ injector });
|
|
@@ -124,13 +86,6 @@ export class DataSet extends EventHub {
|
|
|
124
86
|
const parsedFilter = this.settings.addFilter ? await this.settings.addFilter({ injector, filter }) : filter;
|
|
125
87
|
return this.settings.physicalStore.find(parsedFilter);
|
|
126
88
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Returns an entity based on its primary key
|
|
129
|
-
* @param injector The injector from the context
|
|
130
|
-
* @param key The identifier of the entity
|
|
131
|
-
* @param select A field list used for projection
|
|
132
|
-
* @returns An item with the current unique key or Undefined
|
|
133
|
-
*/
|
|
134
89
|
async get(injector, key, select) {
|
|
135
90
|
if (this.settings.authorizeGet) {
|
|
136
91
|
const result = await this.settings.authorizeGet({ injector });
|
|
@@ -155,17 +110,11 @@ export class DataSet extends EventHub {
|
|
|
155
110
|
return await this.settings.physicalStore.get(key, select);
|
|
156
111
|
}
|
|
157
112
|
/**
|
|
158
|
-
* Removes
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* are authorized. Keys that don't match any entity are silently forwarded to the
|
|
164
|
-
* physical store's `remove()` call.
|
|
165
|
-
* @param injector The injector from the caller's context. For server-side or background operations
|
|
166
|
-
* without an HTTP request, use a child injector with an elevated {@link IdentityContext}.
|
|
167
|
-
* @param keys The primary keys of the entities to remove
|
|
168
|
-
* @returns A promise that will be resolved / rejected based on the remove success
|
|
113
|
+
* Removes by primary key. Pre-load `authorizeRemove` and per-entity
|
|
114
|
+
* `authorizeRemoveEntity` are all-or-nothing — any rejection aborts the
|
|
115
|
+
* whole batch before any persist call. When `authorizeRemoveEntity` is
|
|
116
|
+
* configured, missing keys are silently forwarded to the physical store
|
|
117
|
+
* (no entity to authorize).
|
|
169
118
|
*/
|
|
170
119
|
async remove(injector, ...keys) {
|
|
171
120
|
if (keys.length === 0) {
|
|
@@ -182,7 +131,7 @@ export class DataSet extends EventHub {
|
|
|
182
131
|
filter: { [this.primaryKey]: { $in: keys } },
|
|
183
132
|
});
|
|
184
133
|
await Promise.all(entities.map(async (entity) => {
|
|
185
|
-
const removeResult = await this.settings.authorizeRemoveEntity({ injector, entity
|
|
134
|
+
const removeResult = await this.settings.authorizeRemoveEntity({ injector, entity });
|
|
186
135
|
if (!removeResult.isAllowed) {
|
|
187
136
|
throw new AuthorizationError(removeResult.message);
|
|
188
137
|
}
|
package/esm/data-set.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-set.js","sourceRoot":"","sources":["../src/data-set.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"data-set.js","sourceRoot":"","sources":["../src/data-set.ts"],"names":[],"mappings":"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"}
|