@furystack/core
Version:
Core FuryStack package
279 lines (160 loc) • 10.6 kB
Markdown
# Changelog
## [17.1.0] - 2026-05-21
### ✨ Features
### New `NotFoundError` and `NotSupportedError` exports
Two new error classes are exported from the package's `errors` barrel so adapters can signal narrow precondition failures by type instead of by message:
- `NotFoundError` — thrown by a `PhysicalStore` when an operation targets a missing entity (e.g. `update` called with an id that does not exist). Callers can branch on `instanceof NotFoundError` instead of parsing strings.
- `NotSupportedError` — thrown when an adapter cannot serve a given operation against its backing storage (e.g. `find` against a key/value store). Adapters throw at call time rather than at construction so the rest of the surface remains usable.
`InMemoryStore.update` now throws `NotFoundError` for the missing-entity case (was a plain `Error`); the message was tightened from `... cannot update!` to `... cannot update`. Sibling adapters (`@furystack/mongodb-store`, `@furystack/sequelize-store`, `@furystack/redis-store`) were updated in lockstep.
**Impact:** code that matched on the previous error message will keep working — `Error.message` still contains the entity id and the `cannot update` substring, only the trailing `!` was dropped. Callers can now switch to `instanceof NotFoundError`.
### 📚 Documentation
- Rewrote JSDoc across the public API (`defineStore`, `InMemoryStore`, `PhysicalStore`, the `errors/` family, `IdentityContext`, `globalDisposables`, `filterItems`, `User`) to follow the new value-test guidance: dropped restate-the-type narration, kept intent / trade-offs / constraints, and added type-only imports for cross-file `{@link}` targets.
### ⬆️ Dependencies
- Bump dev `vitest` to `^4.1.5`.
- Bumped `@types/node` to `^25.9.1` and `vitest` to `^4.1.7`. No source changes — dev-tooling bump only.
## [17.0.0] - 2026-04-25
### 💥 Breaking Changes
Stores are now first-class DI tokens. See the [v7 migration guide](../../docs/migrations/v7-functional-di.md) for rationale, recipes, and pitfalls.
- Removed `StoreManager`, `addStore(injector, store)`. Declare stores with `defineStore({ name, model, primaryKey, factory })` at module scope — the returned `StoreToken<T, PK>` is self-disposing on injector teardown and carries `model` + `primaryKey` metadata.
- Added `IdentityContext` as an interface + singleton token (previously a class). Bind a concrete implementation at app bootstrap; `useSystemIdentityContext` still returns a child injector with an elevated identity and is the recommended server-side elevation path.
- `Constructable` now lives in this package (moved from `@furystack/inject`). Any downstream package that used `Constructable` must switch its import.
### Quick reference
Before:
```ts
const sm = injector.getInstance(StoreManager)
sm.addStore(new InMemoryStore({ model: User, primaryKey: 'id' }))
const store = sm.getStoreFor(User, 'id')
```
After:
```ts
export const UserStore = defineStore({
name: 'app/UserStore',
model: User,
primaryKey: 'id',
factory: () => new InMemoryStore({ model: User, primaryKey: 'id' }),
})
const store = injector.get(UserStore)
```
## [16.0.4] - 2026-04-17
### ⬆️ Dependencies
- Raised `@types/node` to ^25.6.0, `typescript` to ^6.0.3, and `vitest` to ^4.1.4 so package development matches the workspace toolchain.
## [16.0.3] - 2026-03-27
### ⬆️ Dependencies
- Updated `vitest` to ^4.1.2
## [16.0.2] - 2026-03-25
### 📦 Build
- Removed deprecated `baseUrl` from tsconfig.json for TypeScript 6 compatibility
### ⬆️ Dependencies
- Upgraded `typescript` from ^5.9.3 to ^6.0.2
- Upgraded `vitest` from ^4.1.0 to ^4.1.1
## [16.0.1] - 2026-03-19
### ✨ Features
- Core package bump to 16.0.0 with the breaking change above and other internal refinements.
### 📚 Documentation
- Updated `IdentityContext` JSDoc to reflect the new explicit lifetime and parent‑chain inheritance.
### ⬆️ Dependencies
- Upgraded `vite` from ^7.3.1 to ^8.0.0 for improved build performance and new features
- Upgraded `vitest` from ^4.0.18 to ^4.1.0
- Upgraded `@vitest/coverage-istanbul` from ^4.0.18 to ^4.1.0
### 💥 Breaking Changes
### IdentityContext lifetime changed from `scoped` to `explicit`
`IdentityContext` now uses `@Injectable({ lifetime: 'explicit' })` so child injectors inherit the instance from their parent. Code that previously relied on the default scoped instance may now need to call `injector.setExplicitInstance` before accessing it. See migration notes in the package changelog.
## [16.0.0] - 2026-03-10
### 💥 Breaking Changes
### IdentityContext lifetime changed from `scoped` to `explicit`
`IdentityContext` now uses `@Injectable({ lifetime: 'explicit' })` instead of `@Injectable({ lifetime: 'scoped' })`.
**Why:** With `scoped` lifetime, child injectors created via `createChild()` could not inherit the `IdentityContext` from their parent. Each child silently created a new default instance whose methods returned `false` or threw `"No IdentityContext"`. This was a common source of confusion, especially in Shades frontend applications where nested component injectors should share the same identity.
With `explicit` lifetime, `getInstance(IdentityContext)` walks up the parent injector chain, so you only need to set it once on the root (or request-scoped) injector and all descendants will find it.
**Who is affected:** Code that called `injector.getInstance(IdentityContext)` without a prior `setExplicitInstance` call. Previously this returned a useless default instance; now it throws `CannotInstantiateExplicitLifetimeError`.
**Who is NOT affected:** All standard FuryStack server-side setups (`useHttpAuthentication`, `useJwtAuthentication`, `useSystemIdentityContext`, REST/WebSocket API managers) already call `setExplicitInstance` before accessing `IdentityContext` and will continue to work without changes.
**Migration:**
```typescript
// ❌ Before (scoped) — silently returned a broken default
const ctx = injector.getInstance(IdentityContext)
// ✅ After (explicit) — set it before accessing
injector.setExplicitInstance(myIdentityContext, IdentityContext)
const ctx = injector.getInstance(IdentityContext) // works
// ✅ Child injectors now inherit automatically
const child = injector.createChild()
const ctx = child.getInstance(IdentityContext) // same instance from parent
```
### 📚 Documentation
- Updated `IdentityContext` JSDoc to reflect the `explicit` lifetime, including setup instructions and parent inheritance behavior
## [15.2.5] - 2026-03-07
### ⬆️ Dependencies
- Updated `@types/node` from `^25.3.1` to `^25.3.5`
## [15.2.4] - 2026-03-06
### ⬆️ Dependencies
- Updated internal FuryStack dependencies
## [15.2.3] - 2026-03-03
### ⬆️ Dependencies
- Updated `@furystack/utils` with EventHub listener error handling and ObservableValue `onError` support
## [15.2.2] - 2026-02-26
### 🔧 Chores
- Normalized line endings in `store-manager.ts`
### ⬆️ Dependencies
- Bumped `@types/node` from ^25.3.0 to ^25.3.1
## [15.2.1] - 2026-02-26
### 📝 Documentation
- Added JSDoc recommendations to `PhysicalStore` and `StoreManager.getStoreFor()` pointing to `DataSet` as the preferred write gateway for application-level code
## [15.2.0] - 2026-02-22
### ✨ Features
### Public `filterItems()` function
Extracted the filter logic from `InMemoryStore` into a standalone `filterItems()` function, now exported from `@furystack/core`. This allows consumers to filter arrays using `FilterType` expressions without needing a store instance.
**Usage:**
```typescript
import { filterItems } from '@furystack/core'
const results = filterItems(myArray, {
name: { $startsWith: 'foo' },
age: { $gte: 18 },
})
```
### ♻️ Refactoring
- `InMemoryStore` now delegates to the public `filterItems()` function internally instead of using a private method
## [15.1.0] - 2026-02-19
### ✨ Features
### `SystemIdentityContext` -- elevated identity for trusted server-side operations
Added `SystemIdentityContext`, an `IdentityContext` subclass that is always authenticated and authorized. It is intended for background jobs, migrations, and seed scripts that need to write through the `DataSet` layer without an HTTP user session.
Also added the `useSystemIdentityContext()` helper that creates a scoped child injector with the elevated context. The returned injector is `AsyncDisposable` and works with `usingAsync()` for automatic cleanup.
**Usage:**
```typescript
import { useSystemIdentityContext } from '@furystack/core'
import { getDataSetFor } from '@furystack/repository'
import { usingAsync } from '@furystack/utils'
await usingAsync(useSystemIdentityContext({ injector, username: 'migration-job' }), async (systemInjector) => {
const dataSet = getDataSetFor(systemInjector, MyModel, 'id')
await dataSet.add(systemInjector, newEntity)
})
```
### 📚 Documentation
- Expanded JSDoc on `PhysicalStore` to warn that writing directly to the store bypasses DataSet authorization, hooks, and events
### 🧪 Tests
- Added tests for `SystemIdentityContext` (authentication, authorization, custom username)
- Added tests for `useSystemIdentityContext` (child injector scoping, disposal, identity resolution)
### ⬆️ Dependencies
- Updated `@furystack/inject` and `@furystack/utils`
## [15.0.36] - 2026-02-11
### ⬆️ Dependencies
- Bump `vitest` from `^4.0.17` to `^4.0.18`
- Bump `@types/node` from `^25.0.10` to `^25.2.3`
- Updated internal dependencies
## [15.0.35] - 2026-02-09
### 🐛 Bug Fixes
- Fixed `getPort()` to assign deterministic port ranges per Vitest worker using `VITEST_POOL_ID` instead of a random base port, preventing port collisions in parallel test runs
### 🧪 Tests
- Refactored `globalDisposable` tests to use `usingAsync` for proper `Injector` disposal
## [15.0.34] - 2026-01-26
### 🔧 Chores
- Standardized author format, improved keywords, removed obsolete `gitHead`, added `engines` (Node 22+) and `sideEffects: false`
## [15.0.33] - 2026-01-26
### ⬆️ Dependencies
- Updated `@furystack/inject` with fix for singleton injector reference being overwritten by child injectors
## [15.0.32] - 2026-01-22
### ⬆️ Dependencies
- Dependency updates
### 📚 Documentation
- Improved README with clearer examples and better structure
### 🐛 Bug Fixes
- Fixed `getPort()` returning duplicate ports by reusing a shared generator instance instead of creating a new one on each call
### 🔧 Chores
- Migrated to centralized changelog management system