Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.5.5] - 2026-06-02

### Fixed

- **`actionError` swallowed non-`Error` throws** — when an action threw a plain object (e.g. an HTTP error response), `actionError` was set to `undefined` instead of wrapping the value; it now reads `.message` then `.body.message` from the thrown object and wraps the result in a proper `Error`, so callers always receive a usable error or `undefined`

## [1.5.4] - 2026-06-02

### Changed
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const { data, execute, state, rollback, rollbackTo, reset, destroy } = createSvS
- `isDirty: Readable<boolean>` - Whether state has been modified (derived from `isDirtyByField`)
- `isDirtyByField: Readable<DirtyFields>` - Per-field dirty tracking; keys are dot-notation property paths. When a nested field changes, all parent paths are also marked dirty (e.g., changing `customer.address.street` marks `customer.address` and `customer` as dirty). Cleared on `reset()`, `rollback()`, and successful action (respecting `resetDirtyOnAction`).
- `actionInProgress: Readable<boolean>` - Action execution status
- `actionError: Readable<Error | undefined>` - Last action error
- `actionError: Readable<Error | undefined>` - Last action error; non-`Error` thrown objects are wrapped by reading `.message` then `.body.message` before falling back to `String()`
- `snapshots: Readable<Snapshot<T>[]>` - Snapshot history for undo
- `asyncErrors: Readable<AsyncErrors>` - Async validation errors (keyed by property path)
- `hasAsyncErrors: Readable<boolean>` - Whether any async validation errors exist
Expand Down
697 changes: 355 additions & 342 deletions demo/package-lock.json

Large diffs are not rendered by default.

38 changes: 19 additions & 19 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,30 @@
"all": "node --run format:fix && node --run lint:fix && node --run ts:check && node --run build"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/vite": "^4.2.2",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@tsconfig/svelte": "^5.0.8",
"@typescript-eslint/eslint-plugin": "^8.58.0",
"@typescript-eslint/parser": "^8.58.0",
"eslint": "^10.1.0",
"@typescript-eslint/eslint-plugin": "^8.60.1",
"@typescript-eslint/parser": "^8.60.1",
"eslint": "^10.4.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-svelte": "^3.16.0",
"eslint-plugin-simple-import-sort": "^13.0.0",
"eslint-plugin-svelte": "^3.19.0",
"eslint-plugin-unicorn": "^64.0.0",
"postcss": "^8.5.8",
"prettier": "^3.8.1",
"prettier-plugin-svelte": "^3.5.1",
"svelte-check": "^4.4.6",
"svelte-eslint-parser": "^1.6.0",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.2.2",
"postcss": "^8.5.15",
"prettier": "^3.8.3",
"prettier-plugin-svelte": "^4.1.0",
"svelte-check": "^4.5.0",
"svelte-eslint-parser": "^1.7.1",
"svelte-preprocess": "^6.0.5",
"tailwindcss": "^4.3.0",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"vite": "^8.0.3"
"typescript": "^6.0.3",
"vite": "^8.0.16"
},
"dependencies": {
"svelte": "^5.55.1",
"zod": "^4.3.6"
"svelte": "^5.56.1",
"zod": "^4.4.3"
}
}
5 changes: 5 additions & 0 deletions demo/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
declare module '*.postcss' {
const content: string;
export default content;
}

declare global {
namespace App {
// interface Error {}
Expand Down
537 changes: 537 additions & 0 deletions docs/assets/index-BxuovIe5.js

Large diffs are not rendered by default.

512 changes: 0 additions & 512 deletions docs/assets/index-CXrV2wig.js

This file was deleted.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<link rel="icon" href="favicon.png" />
<title>svstate demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="module" crossorigin src="/svstate/assets/index-CXrV2wig.js"></script>
<link rel="stylesheet" crossorigin href="/svstate/assets/index-Ck8pee3s.css">
<script type="module" crossorigin src="/svstate/assets/index-BxuovIe5.js"></script>
<link rel="stylesheet" crossorigin href="/svstate/assets/index-GOI4QFiT.css">
</head>

<body>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svstate",
"version": "1.5.4",
"version": "1.5.5",
"description": "Supercharged $state() for Svelte 5: deep reactive proxy with validation, cross-field rules, computed & side-effects",
"author": "BCsabaEngine",
"license": "ISC",
Expand Down
15 changes: 14 additions & 1 deletion src/state.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,20 @@ export function createSvState<T extends Record<string, unknown>, V extends Valid
callPlugins('onAction', { phase: 'after', params: parameters });
} catch (caughtError) {
await actuators?.actionCompleted?.(caughtError);
const actionError_ = caughtError instanceof Error ? caughtError : undefined;
const actionError_ =
caughtError instanceof Error
? caughtError
: caughtError && typeof caughtError === 'object'
? new Error(
String(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(caughtError as any).message ??
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(caughtError as any).body?.message ??
caughtError
)
)
: undefined;
actionError.set(actionError_);
callPlugins('onAction', { phase: 'after', params: parameters, error: actionError_ });
} finally {
Expand Down
Loading