diff --git a/.changeset/bright-cursors-click.md b/.changeset/bright-cursors-click.md deleted file mode 100644 index 765e2f25..00000000 --- a/.changeset/bright-cursors-click.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -"svforge": patch ---- -Use pointer and non-interactive cursors for enabled, disabled, and loading buttons. diff --git a/.changeset/bright-dialog-cursors.md b/.changeset/bright-dialog-cursors.md deleted file mode 100644 index f85a360f..00000000 --- a/.changeset/bright-dialog-cursors.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -'svforge': patch ---- -Show a pointer cursor on enabled dashboard dialog triggers. diff --git a/.changeset/clear-avatar-initial.md b/.changeset/clear-avatar-initial.md deleted file mode 100644 index 7131c819..00000000 --- a/.changeset/clear-avatar-initial.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -'svforge': patch ---- -Inline Dashboard avatar initials in the reactive markup without changing empty-name or variant behavior. diff --git a/.changeset/cursor-oauth-buttons.md b/.changeset/cursor-oauth-buttons.md deleted file mode 100644 index a7e3d883..00000000 --- a/.changeset/cursor-oauth-buttons.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@svforge/oauth': patch -'svforge': patch ---- -Give enabled OAuth provider buttons a pointer cursor and retain a not-allowed cursor while sign-in is pending. diff --git a/.changeset/plain-base-class-derivations.md b/.changeset/plain-base-class-derivations.md deleted file mode 100644 index 2932c7a0..00000000 --- a/.changeset/plain-base-class-derivations.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -'svforge': patch ---- -Simplify the single-use class expressions in Base Badge, Alert, and Card components while preserving reactive variants. diff --git a/.changeset/quiet-lib-imports.md b/.changeset/quiet-lib-imports.md deleted file mode 100644 index 2f041dc7..00000000 --- a/.changeset/quiet-lib-imports.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -"svforge": patch -"@svforge/addon-kit": patch -"@svforge/audit": patch -"@svforge/blog": patch -"@svforge/chat": patch -"@svforge/dnd": patch -"@svforge/email": patch -"@svforge/graph": patch -"@svforge/jobs": patch -"@svforge/notifications": patch -"@svforge/oauth": patch -"@svforge/realtime": patch -"@svforge/tiptap": patch -"@svforge/ui_toast": patch -"@svforge/uploads": patch ---- -Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. diff --git a/.changeset/release-candidates.json b/.changeset/release-candidates.json index aa4f691f..3ffcf032 100644 --- a/.changeset/release-candidates.json +++ b/.changeset/release-candidates.json @@ -1,9 +1,79 @@ { "schemaVersion": 1, "packages": [ + { + "name": "@svforge/addon-kit", + "version": "2.2.1", + "directory": "packages/addon-kit" + }, + { + "name": "@svforge/audit", + "version": "2.2.1", + "directory": "packages/audit" + }, + { + "name": "@svforge/blog", + "version": "2.2.1", + "directory": "packages/blog" + }, + { + "name": "@svforge/chat", + "version": "2.2.1", + "directory": "packages/chat" + }, + { + "name": "@svforge/dnd", + "version": "2.2.1", + "directory": "packages/dnd" + }, + { + "name": "@svforge/email", + "version": "2.2.1", + "directory": "packages/email" + }, + { + "name": "@svforge/graph", + "version": "2.2.1", + "directory": "packages/graph" + }, + { + "name": "@svforge/jobs", + "version": "2.2.1", + "directory": "packages/jobs" + }, + { + "name": "@svforge/notifications", + "version": "2.2.1", + "directory": "packages/notifications" + }, + { + "name": "@svforge/oauth", + "version": "2.2.1", + "directory": "packages/oauth" + }, + { + "name": "@svforge/realtime", + "version": "2.2.1", + "directory": "packages/realtime" + }, + { + "name": "@svforge/tiptap", + "version": "2.2.1", + "directory": "packages/tiptap" + }, + { + "name": "@svforge/ui_toast", + "version": "2.2.1", + "directory": "packages/ui_toast" + }, + { + "name": "@svforge/uploads", + "version": "2.2.1", + "directory": "packages/uploads" + }, { "name": "svforge", - "version": "2.2.2", + "version": "2.2.3", "directory": "packages/svforge" } ] diff --git a/.changeset/tidy-button-classes.md b/.changeset/tidy-button-classes.md deleted file mode 100644 index 968c8b69..00000000 --- a/.changeset/tidy-button-classes.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -"svforge": patch ---- -Simplify the shared Button class derivation without changing its rendered behavior. diff --git a/packages/addon-kit/CHANGELOG.md b/packages/addon-kit/CHANGELOG.md index 79f45ce2..ef7f91fe 100644 --- a/packages/addon-kit/CHANGELOG.md +++ b/packages/addon-kit/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/addon-kit +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/addon-kit/package.json b/packages/addon-kit/package.json index dfea8376..a49b829b 100644 --- a/packages/addon-kit/package.json +++ b/packages/addon-kit/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/addon-kit", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Addon Kit — shared capability contract + safe JSON merge helpers for @svforge/* addons (#323, #324)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/audit/CHANGELOG.md b/packages/audit/CHANGELOG.md index db063911..c4c53890 100644 --- a/packages/audit/CHANGELOG.md +++ b/packages/audit/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/audit +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/audit/package.json b/packages/audit/package.json index 50568b6e..99694445 100644 --- a/packages/audit/package.json +++ b/packages/audit/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/audit", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Audit — business action audit trail (append-only)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/blog/CHANGELOG.md b/packages/blog/CHANGELOG.md index c0d62b65..3804f9a4 100644 --- a/packages/blog/CHANGELOG.md +++ b/packages/blog/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/blog +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/blog/package.json b/packages/blog/package.json index 7a1a00f1..410b3e5e 100644 --- a/packages/blog/package.json +++ b/packages/blog/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/blog", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Blog — MDsveX blog with posts list and article pages", "author": "Ludo (https://lelab.dev)", diff --git a/packages/chat/CHANGELOG.md b/packages/chat/CHANGELOG.md index 2b031c1d..2b59a757 100644 --- a/packages/chat/CHANGELOG.md +++ b/packages/chat/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/chat +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/chat/package.json b/packages/chat/package.json index 3471ba7a..12f61ec5 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/chat", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Chat — composable app chat (conversations, messages, read-state)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/dnd/CHANGELOG.md b/packages/dnd/CHANGELOG.md index 21daf5b0..2451ec5c 100644 --- a/packages/dnd/CHANGELOG.md +++ b/packages/dnd/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/dnd +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/dnd/package.json b/packages/dnd/package.json index 853522ad..37b9055d 100644 --- a/packages/dnd/package.json +++ b/packages/dnd/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/dnd", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Drag & Drop — sortable lists via @thisux/sveltednd", "author": "Ludo (https://lelab.dev)", diff --git a/packages/email/CHANGELOG.md b/packages/email/CHANGELOG.md index 17bd7a83..8e916f55 100644 --- a/packages/email/CHANGELOG.md +++ b/packages/email/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/email +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/email/package.json b/packages/email/package.json index d88f6556..f038a48b 100644 --- a/packages/email/package.json +++ b/packages/email/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/email", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Email — transactional emails via Resend", "author": "Ludo (https://lelab.dev)", diff --git a/packages/graph/CHANGELOG.md b/packages/graph/CHANGELOG.md index e5316a3c..39bf892a 100644 --- a/packages/graph/CHANGELOG.md +++ b/packages/graph/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/graph +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/graph/package.json b/packages/graph/package.json index 347b36c3..fbe8739e 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/graph", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Graph — interactive knowledge graph / node-edge visualization (Obsidian-style)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/jobs/CHANGELOG.md b/packages/jobs/CHANGELOG.md index 5392775c..f03da7b7 100644 --- a/packages/jobs/CHANGELOG.md +++ b/packages/jobs/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/jobs +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/jobs/package.json b/packages/jobs/package.json index 9c9983fe..af5316a0 100644 --- a/packages/jobs/package.json +++ b/packages/jobs/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/jobs", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Jobs — background job foundation (encapsulated backend, retry, progress)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/notifications/CHANGELOG.md b/packages/notifications/CHANGELOG.md index 1433dfbf..f1fcf79a 100644 --- a/packages/notifications/CHANGELOG.md +++ b/packages/notifications/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/notifications +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/notifications/package.json b/packages/notifications/package.json index 4cbfa1f6..6701235b 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/notifications", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Notifications — persistent business notifications (read/unread)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/oauth/CHANGELOG.md b/packages/oauth/CHANGELOG.md index fb336975..f8fe52d0 100644 --- a/packages/oauth/CHANGELOG.md +++ b/packages/oauth/CHANGELOG.md @@ -1,5 +1,13 @@ # @svforge/oauth +## 2.2.1 + +### Patch Changes + +- [#600](https://github.com/lelabdev/svforge/pull/600) [`3032aee`](https://github.com/lelabdev/svforge/commit/3032aee83c8d2df87891f3cd35419e45a0397ac7) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Give enabled OAuth provider buttons a pointer cursor and retain a not-allowed cursor while sign-in is pending. + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/oauth/package.json b/packages/oauth/package.json index f2c3b14b..be1bc6d3 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/oauth", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge OAuth — social auth (Google, GitHub)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/realtime/CHANGELOG.md b/packages/realtime/CHANGELOG.md index 15d81f12..0ff668ad 100644 --- a/packages/realtime/CHANGELOG.md +++ b/packages/realtime/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/realtime +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/realtime/package.json b/packages/realtime/package.json index 1323a60e..28dbed63 100644 --- a/packages/realtime/package.json +++ b/packages/realtime/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/realtime", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Realtime — generic WebSocket transport (publish/subscribe)", "author": "Ludo (https://lelab.dev)", diff --git a/packages/svforge/CHANGELOG.md b/packages/svforge/CHANGELOG.md index 5343d03a..a970c81e 100644 --- a/packages/svforge/CHANGELOG.md +++ b/packages/svforge/CHANGELOG.md @@ -1,5 +1,23 @@ # svforge +## 2.2.3 + +### Patch Changes + +- [#591](https://github.com/lelabdev/svforge/pull/591) [`746b5de`](https://github.com/lelabdev/svforge/commit/746b5deea1b1308718fa4ab065819b27eee5e5ee) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Use pointer and non-interactive cursors for enabled, disabled, and loading buttons. + +- [#596](https://github.com/lelabdev/svforge/pull/596) [`d9d2e71`](https://github.com/lelabdev/svforge/commit/d9d2e713845329e3a84683708f15e8a4809b6ae2) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Show a pointer cursor on enabled dashboard dialog triggers. + +- [#599](https://github.com/lelabdev/svforge/pull/599) [`cf5ff96`](https://github.com/lelabdev/svforge/commit/cf5ff9607cac2a8fdec34a339ccac5f83377ee6a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Inline Dashboard avatar initials in the reactive markup without changing empty-name or variant behavior. + +- [#600](https://github.com/lelabdev/svforge/pull/600) [`3032aee`](https://github.com/lelabdev/svforge/commit/3032aee83c8d2df87891f3cd35419e45a0397ac7) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Give enabled OAuth provider buttons a pointer cursor and retain a not-allowed cursor while sign-in is pending. + +- [#598](https://github.com/lelabdev/svforge/pull/598) [`a7d9de6`](https://github.com/lelabdev/svforge/commit/a7d9de6ae192a67e2f8ba54ed0d5e2b787bc6a90) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Simplify the single-use class expressions in Base Badge, Alert, and Card components while preserving reactive variants. + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + +- [#595](https://github.com/lelabdev/svforge/pull/595) [`617e895`](https://github.com/lelabdev/svforge/commit/617e895fff1d549259b7f6b92da3a1dab2538724) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Simplify the shared Button class derivation without changing its rendered behavior. + ## 2.2.2 ### Patch Changes diff --git a/packages/svforge/package.json b/packages/svforge/package.json index 242476d7..5371eeaa 100644 --- a/packages/svforge/package.json +++ b/packages/svforge/package.json @@ -1,6 +1,6 @@ { "name": "svforge", - "version": "2.2.2", + "version": "2.2.3", "type": "module", "description": "Production-ready foundations for SvelteKit projects with Skeleton UI, Tailwind CSS, Better Auth, and Drizzle ORM", "author": "Ludo (https://lelab.dev)", diff --git a/packages/svforge/src/changelog.ts b/packages/svforge/src/changelog.ts index ce1a7d0c..5a108005 100644 --- a/packages/svforge/src/changelog.ts +++ b/packages/svforge/src/changelog.ts @@ -9,6 +9,12 @@ export interface ChangelogEntry { } export const RELEASE_NOTES: ChangelogEntry[] = [ + { + "package": "@svforge/addon-kit", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/addon-kit", "version": "2.2.0", @@ -27,6 +33,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/audit", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/audit", "version": "2.2.0", @@ -39,6 +51,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/blog", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/blog", "version": "2.2.0", @@ -57,6 +75,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/chat", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/chat", "version": "2.2.0", @@ -75,6 +99,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/dnd", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/dnd", "version": "2.2.0", @@ -93,6 +123,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/email", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/email", "version": "2.2.0", @@ -111,6 +147,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/graph", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/graph", "version": "2.2.0", @@ -129,6 +171,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/jobs", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/jobs", "version": "2.2.0", @@ -141,6 +189,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/notifications", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/notifications", "version": "2.2.0", @@ -159,6 +213,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/oauth", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#600](https://github.com/lelabdev/svforge/pull/600) [`3032aee`](https://github.com/lelabdev/svforge/commit/3032aee83c8d2df87891f3cd35419e45a0397ac7) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Give enabled OAuth provider buttons a pointer cursor and retain a not-allowed cursor while sign-in is pending.\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/oauth", "version": "2.2.0", @@ -177,6 +237,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/realtime", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/realtime", "version": "2.2.0", @@ -189,6 +255,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/tiptap", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/tiptap", "version": "2.2.0", @@ -207,6 +279,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/ui_toast", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/ui_toast", "version": "2.2.0", @@ -219,6 +297,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "@svforge/uploads", + "version": "2.2.1", + "date": "", + "body": "### Patch Changes\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility." + }, { "package": "@svforge/uploads", "version": "2.2.0", @@ -237,6 +321,12 @@ export const RELEASE_NOTES: ChangelogEntry[] = [ "date": "2026-09-12", "body": "### Breaking changes\n- None.\n\n### Migrations\n- None.\n\n### Fixes\n- Repository metadata republished against the renamed GitHub repo (`lelabdev/svforge`): `repository`, `homepage`, and `bugs` URLs corrected on npm.\n\n### Deprecations\n- None." }, + { + "package": "svforge", + "version": "2.2.3", + "date": "", + "body": "### Patch Changes\n\n- [#591](https://github.com/lelabdev/svforge/pull/591) [`746b5de`](https://github.com/lelabdev/svforge/commit/746b5deea1b1308718fa4ab065819b27eee5e5ee) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Use pointer and non-interactive cursors for enabled, disabled, and loading buttons.\n\n- [#596](https://github.com/lelabdev/svforge/pull/596) [`d9d2e71`](https://github.com/lelabdev/svforge/commit/d9d2e713845329e3a84683708f15e8a4809b6ae2) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Show a pointer cursor on enabled dashboard dialog triggers.\n\n- [#599](https://github.com/lelabdev/svforge/pull/599) [`cf5ff96`](https://github.com/lelabdev/svforge/commit/cf5ff9607cac2a8fdec34a339ccac5f83377ee6a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Inline Dashboard avatar initials in the reactive markup without changing empty-name or variant behavior.\n\n- [#600](https://github.com/lelabdev/svforge/pull/600) [`3032aee`](https://github.com/lelabdev/svforge/commit/3032aee83c8d2df87891f3cd35419e45a0397ac7) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Give enabled OAuth provider buttons a pointer cursor and retain a not-allowed cursor while sign-in is pending.\n\n- [#598](https://github.com/lelabdev/svforge/pull/598) [`a7d9de6`](https://github.com/lelabdev/svforge/commit/a7d9de6ae192a67e2f8ba54ed0d5e2b787bc6a90) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Simplify the single-use class expressions in Base Badge, Alert, and Card components while preserving reactive variants.\n\n- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility.\n\n- [#595](https://github.com/lelabdev/svforge/pull/595) [`617e895`](https://github.com/lelabdev/svforge/commit/617e895fff1d549259b7f6b92da3a1dab2538724) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Simplify the shared Button class derivation without changing its rendered behavior." + }, { "package": "svforge", "version": "2.2.2", diff --git a/packages/svforge/src/compat-manifest.ts b/packages/svforge/src/compat-manifest.ts index fa03ab02..f7f59d1f 100644 --- a/packages/svforge/src/compat-manifest.ts +++ b/packages/svforge/src/compat-manifest.ts @@ -12,23 +12,23 @@ export const COMPAT_MANIFEST: CompatManifest = { "schema": 1, "template": { "name": "svforge", - "version": "2.2.2" + "version": "2.2.3" }, "packages": { - "@svforge/addon-kit": "2.2.0", - "@svforge/audit": "2.2.0", - "@svforge/blog": "2.2.0", - "@svforge/chat": "2.2.0", - "@svforge/dnd": "2.2.0", - "@svforge/email": "2.2.0", - "@svforge/graph": "2.2.0", - "@svforge/jobs": "2.2.0", - "@svforge/notifications": "2.2.0", - "@svforge/oauth": "2.2.0", - "@svforge/realtime": "2.2.0", - "@svforge/tiptap": "2.2.0", - "@svforge/ui_toast": "2.2.0", - "@svforge/uploads": "2.2.0", - "svforge": "2.2.2" + "@svforge/addon-kit": "2.2.1", + "@svforge/audit": "2.2.1", + "@svforge/blog": "2.2.1", + "@svforge/chat": "2.2.1", + "@svforge/dnd": "2.2.1", + "@svforge/email": "2.2.1", + "@svforge/graph": "2.2.1", + "@svforge/jobs": "2.2.1", + "@svforge/notifications": "2.2.1", + "@svforge/oauth": "2.2.1", + "@svforge/realtime": "2.2.1", + "@svforge/tiptap": "2.2.1", + "@svforge/ui_toast": "2.2.1", + "@svforge/uploads": "2.2.1", + "svforge": "2.2.3" } }; diff --git a/packages/svforge/src/module-recipes.ts b/packages/svforge/src/module-recipes.ts index cf91f131..3af174d5 100644 --- a/packages/svforge/src/module-recipes.ts +++ b/packages/svforge/src/module-recipes.ts @@ -18,7 +18,7 @@ export const MODULE_RECIPE_DATA: Record = { "audit": { "id": "audit", "package": "@svforge/audit", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [], "files": { "/lib/server/audit/index.ts": "import { db } from '#lib/server/db/index.js';\nimport { auditLogs } from './schema';\nimport { desc, eq, and } from 'drizzle-orm';\n\n/**\n * SvelteForge audit trail (#232) — append-only business action logging.\n *\n * await audit.record({\n * actorId: user.id,\n * action: 'punch.corrected',\n * entityType: 'punch',\n * entityId: punch.id,\n * metadata: { before: { time: oldTime }, after: { time: newTime }, reason }\n * });\n *\n * Reads:\n * await audit.forEntity('punch', punchId);\n * await audit.byActor(userId, { limit: 50 });\n */\n\nexport interface AuditEntryInput {\n\tactorId?: string | null;\n\taction: string;\n\tentityType: string;\n\tentityId?: string | null;\n\tmetadata?: Record;\n\tipAddress?: string | null;\n\tuserAgent?: string | null;\n}\n\nexport interface AuditListOptions {\n\tlimit?: number;\n\toffset?: number;\n}\n\nexport interface AuditFilters extends AuditListOptions {\n\taction?: string;\n\tentityType?: string;\n}\n\nexport const audit = {\n\t/** Append an audit entry. Never updates/deletes existing rows. */\n\tasync record(input: AuditEntryInput): Promise {\n\t\tconst [row] = await db\n\t\t\t.insert(auditLogs)\n\t\t\t.values({\n\t\t\t\tactorId: input.actorId ?? null,\n\t\t\t\taction: input.action,\n\t\t\t\tentityType: input.entityType,\n\t\t\t\tentityId: input.entityId ?? null,\n\t\t\t\tmetadata: input.metadata ?? {},\n\t\t\t\tipAddress: input.ipAddress ?? null,\n\t\t\t\tuserAgent: input.userAgent ?? null,\n\t\t\t\tcreatedAt: new Date()\n\t\t\t})\n\t\t\t.returning();\n\t\treturn row;\n\t},\n\n\t/** Full history for one entity (newest first). */\n\tasync forEntity(entityType: string, entityId: string, opts: AuditListOptions = {}): Promise {\n\t\treturn db\n\t\t\t.select()\n\t\t\t.from(auditLogs)\n\t\t\t.where(and(eq(auditLogs.entityType, entityType), eq(auditLogs.entityId, entityId)))\n\t\t\t.orderBy(desc(auditLogs.createdAt))\n\t\t\t.limit(opts.limit ?? 50)\n\t\t\t.offset(opts.offset ?? 0);\n\t},\n\n\t/** Actions by one actor (newest first). */\n\tasync byActor(actorId: string, opts: AuditListOptions = {}): Promise {\n\t\treturn db\n\t\t\t.select()\n\t\t\t.from(auditLogs)\n\t\t\t.where(eq(auditLogs.actorId, actorId))\n\t\t\t.orderBy(desc(auditLogs.createdAt))\n\t\t\t.limit(opts.limit ?? 50)\n\t\t\t.offset(opts.offset ?? 0);\n\t},\n\n\t/** Recent actions with optional filters (admin view). */\n\tasync list(filters: AuditFilters = {}): Promise {\n\t\tconst conditions = [];\n\t\tif (filters.action) conditions.push(eq(auditLogs.action, filters.action));\n\t\tif (filters.entityType) conditions.push(eq(auditLogs.entityType, filters.entityType));\n\t\tconst where = conditions.length ? and(...conditions) : undefined;\n\t\tconst q = db.select().from(auditLogs);\n\t\tif (where) q.where(where);\n\t\treturn q.orderBy(desc(auditLogs.createdAt)).limit(filters.limit ?? 50).offset(filters.offset ?? 0);\n\t}\n};\n", @@ -31,7 +31,7 @@ export const MODULE_RECIPE_DATA: Record = { "blog": { "id": "blog", "package": "@svforge/blog", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "mdsvex", @@ -52,7 +52,7 @@ export const MODULE_RECIPE_DATA: Record = { "chat": { "id": "chat", "package": "@svforge/chat", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [], "files": { "/lib/server/chat/chat-security.test.ts": "import { describe, it, expect, beforeAll, vi } from 'vitest';\nimport type postgres from 'postgres';\n\n// $app/env/private is a SvelteKit virtual module — not resolvable by the\n// bare vitest environment. Read DATABASE_URL from the project .env (created\n// by scripts/setup.sh before the CI runs `bun run test`).\nvi.mock('$app/env/private', async () => {\n\tconst { readFileSync } = await import('node:fs');\n\tconst dotenv = readFileSync('.env', 'utf8');\n\tconst m = dotenv.match(/^DATABASE_URL=\"?([^\"\\n]+)\"?$/m);\n\treturn { DATABASE_URL: m ? m[1].trim() : undefined };\n});\n\n// #401 — thin recorder: a Proxy over the real drizzle instance that snapshots\n// every SELECT the service builds via toSQL() right before it executes. The\n// tests then replay a captured query raw (db.$client.unsafe) to prove facts\n// about the SQL itself (row volume, DISTINCT ON) that results alone cannot.\nconst recorded = vi.hoisted(() => [] as { sql: string; params: unknown[] }[]);\n\nvi.mock('#lib/server/db/index.js', async (importOriginal) => {\n\tconst actual = await importOriginal();\n\t// Wraps a drizzle query builder: chained builders (objects returned by\n\t// .from()/.where()/…) get wrapped too; awaiting (`then`) snapshots the\n\t// fully built query first, then delegates to the real execution.\n\tconst recordBuilder = (builder: object): object =>\n\t\tnew Proxy(builder, {\n\t\t\tget(target, prop) {\n\t\t\t\tif (prop === 'then') {\n\t\t\t\t\tconst query = (target as { toSQL(): { sql: string; params: unknown[] } }).toSQL();\n\t\t\t\t\trecorded.push(query);\n\t\t\t\t\treturn (target as { then: (...args: unknown[]) => unknown }).then.bind(target);\n\t\t\t\t}\n\t\t\t\tconst value = Reflect.get(target, prop, target);\n\t\t\t\tif (typeof value === 'function') {\n\t\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\t\tconst result = (value as (...a: unknown[]) => unknown).apply(target, args);\n\t\t\t\t\t\treturn result !== null && typeof result === 'object'\n\t\t\t\t\t\t\t? recordBuilder(result as object)\n\t\t\t\t\t\t\t: result;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t}\n\t\t});\n\treturn {\n\t\t...actual,\n\t\tdb: new Proxy(actual.db, {\n\t\t\tget(target, prop) {\n\t\t\t\tif (prop === 'select') {\n\t\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\t\trecordBuilder((target as { select: (...a: unknown[]) => object }).select(...args));\n\t\t\t\t}\n\t\t\t\treturn Reflect.get(target, prop, target);\n\t\t\t}\n\t\t})\n\t};\n});\n\nimport { chat } from './index';\nimport { db } from '#lib/server/db/index.js';\nimport { conversations } from './schema';\n\n/**\n * Membership & per-user read state (#281) — runs inside the\n * dashboard-foundations scaffold against the REAL PostgreSQL database\n * (the CI profile performs a drizzle push before `bun run test`).\n *\n * Users are opaque ids (the chat schema does not FK to the user table):\n * A and B are participants, OUTSIDER is not.\n */\nconst A = crypto.randomUUID();\nconst B = crypto.randomUUID();\nconst OUTSIDER = crypto.randomUUID();\n\ndescribe('chat membership & per-user read state (#281)', () => {\n\tbeforeAll(async () => {\n\t\t// Clean slate — FK cascades wipe participants/messages/reads.\n\t\tawait db.delete(conversations);\n\t});\n\n\tit('unread counts are per-user: reading as A never marks messages read for B', async () => {\n\t\tconst conv = await chat.createConversation({ participantIds: [A, B], type: 'direct' });\n\t\tawait chat.sendMessage({ conversationId: conv.id, authorId: A, content: 'hello from A' });\n\t\tawait chat.sendMessage({ conversationId: conv.id, authorId: B, content: 'hello from B' });\n\n\t\t// Nobody has read anything yet.\n\t\tconst before = await chat.listConversations(A);\n\t\texpect(before[0].unreadCount).toBe(2);\n\n\t\t// A marks the whole conversation as read.\n\t\tawait chat.markRead(conv.id, A);\n\n\t\tconst afterA = await chat.listConversations(A);\n\t\texpect(afterA[0].unreadCount).toBe(0);\n\n\t\t// B's counter is UNAFFECTED by A's reads (#281).\n\t\tconst afterB = await chat.listConversations(B);\n\t\texpect(afterB[0].unreadCount).toBe(2);\n\t});\n\n\tit('an outsider cannot read or modify a conversation through ANY public method', async () => {\n\t\tconst conv = await chat.createConversation({ participantIds: [A, B], type: 'direct' });\n\t\tawait chat.sendMessage({ conversationId: conv.id, authorId: A, content: 'secret' });\n\n\t\t// listMessages: read → refused\n\t\tawait expect(chat.listMessages(conv.id, OUTSIDER, { limit: 10 })).rejects.toThrow(/Forbidden/);\n\n\t\t// sendMessage: write → refused (identity is server-side, no spoof)\n\t\tawait expect(\n\t\t\tchat.sendMessage({ conversationId: conv.id, authorId: OUTSIDER, content: 'intrusion' })\n\t\t).rejects.toThrow(/Forbidden/);\n\n\t\t// markRead: state mutation → refused\n\t\tawait expect(chat.markRead(conv.id, OUTSIDER)).rejects.toThrow(/Forbidden/);\n\n\t\t// listConversations: the outsider does not see the conversation at all\n\t\tconst theirs = await chat.listConversations(OUTSIDER);\n\t\texpect(theirs.find((c) => c.id === conv.id)).toBeUndefined();\n\t});\n\n\tit('the last-message helper is not exposed as an unguarded public method', () => {\n\t\t// The public API surface is exactly the documented methods; helpers\n\t\t// backing listConversations must NOT be reachable without membership.\n\t\tconst api = Object.keys(chat).sort();\n\t\texpect(api).toEqual(['createConversation', 'listConversations', 'listMessages', 'markRead', 'sendMessage']);\n\t\texpect((chat as any).getLastMessage).toBeUndefined();\n\t\texpect((chat as any).unreadCount).toBeUndefined();\n\t});\n\n\tdescribe('#401 — bounded queries & pagination', () => {\n\t\tit('listConversations is bounded-query and paginated (#401)', async () => {\n\t\t\t// Clean slate — cascade wipes participants/messages/reads left by\n\t\t\t// the tests above.\n\t\t\tawait db.delete(conversations);\n\n\t\t\t// Small delays guarantee distinct created_at (ms precision), so\n\t\t\t// \"newest first\" and \"last message = 2nd\" are deterministic.\n\t\t\tconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\t\t\tconst expected: { id: string; last: string; unread: number }[] = [];\n\t\t\tfor (let i = 0; i < 3; i++) {\n\t\t\t\tconst conv = await chat.createConversation({ participantIds: [A, B], type: 'direct' });\n\t\t\t\tawait chat.sendMessage({ conversationId: conv.id, authorId: A, content: `conv${i}-first` });\n\t\t\t\tawait sleep(5);\n\t\t\t\tawait chat.sendMessage({ conversationId: conv.id, authorId: A, content: `conv${i}-second` });\n\t\t\t\tawait sleep(5);\n\t\t\t\texpected.push({ id: conv.id, last: `conv${i}-second`, unread: 2 });\n\t\t\t}\n\n\t\t\t// conv0 becomes a busy conversation: 6 messages total. The\n\t\t\t// last-message read must stay bounded by the CONVERSATION count,\n\t\t\t// never grow with the messages table (#401).\n\t\t\tconst busy = expected[0].id;\n\t\t\tawait chat.sendMessage({ conversationId: busy, authorId: A, content: 'conv0-extra1' });\n\t\t\tawait sleep(5);\n\t\t\tawait chat.sendMessage({ conversationId: busy, authorId: A, content: 'conv0-extra2' });\n\t\t\tawait sleep(5);\n\t\t\tawait chat.sendMessage({ conversationId: busy, authorId: A, content: 'conv0-extra3' });\n\t\t\tawait sleep(5);\n\t\t\tawait chat.sendMessage({ conversationId: busy, authorId: A, content: 'conv0-last' });\n\t\t\texpected[0].last = 'conv0-last';\n\t\t\texpected[0].unread = 6;\n\n\t\t\t// Capture the queries listConversations builds.\n\t\t\trecorded.length = 0;\n\t\t\tconst list = await chat.listConversations(A);\n\n\t\t\t// Exactly the 3 conversations.\n\t\t\texpect(list).toHaveLength(3);\n\n\t\t\t// Ordered newest conversation first.\n\t\t\texpect(list.map((c) => c.id)).toEqual([...expected].reverse().map((c) => c.id));\n\n\t\t\t// Last message per conversation = the newest message (conv0 has\n\t\t\t// 6, the others 2); unread = message count (no read entry BY A\n\t\t\t// yet — authorship is not a read).\n\t\t\tfor (const conv of list) {\n\t\t\t\tconst exp = expected.find((e) => e.id === conv.id);\n\t\t\t\texpect(conv.lastMessage?.content).toBe(exp!.last);\n\t\t\t\texpect(conv.unreadCount).toBe(exp!.unread);\n\t\t\t}\n\n\t\t\t// The last-message read is a DISTINCT ON query (one id per\n\t\t\t// conversation_id), so replaying it raw returns exactly 3 rows —\n\t\t\t// the conversation count — even though conv0 holds 6 messages and\n\t\t\t// the table holds 10. The old implementation returned EVERY\n\t\t\t// message and deduplicated in JS: unbounded rows (#401).\n\t\t\tconst lastQuery = recorded.find((q) => /distinct on/i.test(q.sql));\n\t\t\texpect(lastQuery).toBeDefined();\n\t\t\t// postgres.js `unsafe` expects ParameterOrJSON[] — the recorder stores\n\t\t// drizzle's toSQL() params as unknown[] (they are values like uuids and\n\t\t// counts here), so cast them at the replay call site.\n\t\tconst rawRows = await db.$client.unsafe(\n\t\t\tlastQuery!.sql,\n\t\t\tlastQuery!.params as postgres.ParameterOrJSON[]\n\t\t);\n\t\t\texpect(rawRows.length).toBe(3);\n\t\t});\n\t});\n});\n", @@ -67,7 +67,7 @@ export const MODULE_RECIPE_DATA: Record = { "dnd": { "id": "dnd", "package": "@svforge/dnd", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "@thisux/sveltednd", @@ -82,7 +82,7 @@ export const MODULE_RECIPE_DATA: Record = { "email": { "id": "email", "package": "@svforge/email", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "resend", @@ -101,7 +101,7 @@ export const MODULE_RECIPE_DATA: Record = { "graph": { "id": "graph", "package": "@svforge/graph", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "force-graph", @@ -117,7 +117,7 @@ export const MODULE_RECIPE_DATA: Record = { "jobs": { "id": "jobs", "package": "@svforge/jobs", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [], "files": { "/lib/server/jobs/index.ts": "import { db } from '#lib/server/db/index.js';\nimport { jobs } from './schema';\nimport { eq, and, or, lt, lte, isNull, inArray, sql, asc } from 'drizzle-orm';\nimport { DEFAULT_LEASE_MS, computeBackoffMs } from './leases';\n\n/**\n * SvelteForge jobs foundation (#231) — background tasks without coupling the\n * business to a queue provider (BullMQ/Redis/etc.).\n *\n * await jobs.enqueue('payroll.export', { organizationId, period });\n * jobs.define('payroll.export', async (payload, ctx) => {\n * await ctx.progress(10);\n * // long work\n * await ctx.progress(100);\n * return { fileId };\n * });\n *\n * Guarantees (v1, #328): at-least-once (a crashed handler may re-run →\n * handlers must be idempotent), bounded retries (maxAttempts, default 3) with\n * exponential backoff, atomic claims (FOR UPDATE SKIP LOCKED — many workers\n * can run concurrently without duplicate execution), lease-guarded running\n * jobs (a crashed worker's job is recoverable once the lease expires; long\n * handlers renew it through ctx.progress / ctx.heartbeat).\n *\n * Throw `NonRetryableJobError` from a handler to fail the job immediately.\n */\n\nexport type JobStatus = 'queued' | 'running' | 'completed' | 'failed';\n\nexport interface JobHandlerContext {\n\tjobId: string;\n\t/** Update progress 0–100 (also renews the claim lease). */\n\tprogress: (value: number) => Promise;\n\t/** Renew the claim lease without changing progress (long handlers). */\n\theartbeat: () => Promise;\n}\n\nexport type JobHandler> = (payload: T, ctx: JobHandlerContext) => Promise;\n\n/** A permanent failure: the job is failed immediately, without retrying. */\nexport class NonRetryableJobError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'NonRetryableJobError';\n\t}\n}\n\nconst handlers = new Map();\n\n/** Register a typed handler for a job type. */\nexport function define>(type: string, handler: JobHandler): void {\n\thandlers.set(type, handler as JobHandler);\n}\n\nexport const jobsApi = {\n\t/** Enqueue a job. Returns immediately (processing happens in background). */\n\tasync enqueue(type: string, payload: Record = {}, maxAttempts = 3) {\n\t\tconst [row] = await db\n\t\t\t.insert(jobs)\n\t\t\t.values({\n\t\t\t\ttype,\n\t\t\t\tstatus: 'queued',\n\t\t\t\tpayload,\n\t\t\t\tmaxAttempts,\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date()\n\t\t\t})\n\t\t\t.returning();\n\t\treturn row;\n\t},\n\n\t/** Progress update (0–100). With `leaseMs`, also renews the claim lease. */\n\tasync progress(jobId: string, value: number, leaseMs?: number) {\n\t\tawait db\n\t\t\t.update(jobs)\n\t\t\t.set({\n\t\t\t\tprogress: Math.min(100, Math.max(0, value)),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t...(leaseMs ? { leaseUntil: new Date(Date.now() + leaseMs) } : {})\n\t\t\t})\n\t\t\t.where(eq(jobs.id, jobId));\n\t},\n\n\t/** Renew the claim lease (long handlers, #328). */\n\tasync heartbeat(jobId: string, leaseMs = DEFAULT_LEASE_MS) {\n\t\tawait db\n\t\t\t.update(jobs)\n\t\t\t.set({ leaseUntil: new Date(Date.now() + leaseMs), updatedAt: new Date() })\n\t\t\t.where(eq(jobs.id, jobId));\n\t},\n\n\t/** Get a job by id (diagnostics). */\n\tasync get(jobId: string) {\n\t\tconst [row] = await db.select().from(jobs).where(eq(jobs.id, jobId));\n\t\treturn row;\n\t},\n\n\t/**\n\t * Atomically claim up to `batchSize` jobs (#328): queued jobs whose\n\t * backoff elapsed, or running jobs whose lease expired (crash recovery).\n\t * `FOR UPDATE SKIP LOCKED` inside a transaction lets many workers poll the\n\t * same queue concurrently — a claimed row is invisible to the next claim\n\t * until this transaction commits (the row is `running` by then).\n\t */\n\tasync claimBatch(batchSize: number, leaseMs = DEFAULT_LEASE_MS) {\n\t\tconst now = new Date();\n\t\treturn db.transaction(async (tx) => {\n\t\t\tconst claimable = await tx\n\t\t\t\t.select()\n\t\t\t\t.from(jobs)\n\t\t\t\t.where(\n\t\t\t\t\tor(\n\t\t\t\t\t\tand(eq(jobs.status, 'queued'), or(isNull(jobs.runAfter), lte(jobs.runAfter, now))),\n\t\t\t\t\t\tand(eq(jobs.status, 'running'), lt(jobs.leaseUntil, now))\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.orderBy(asc(jobs.createdAt))\n\t\t\t\t.limit(batchSize)\n\t\t\t\t.for('update', { skipLocked: true });\n\t\t\tif (claimable.length === 0) return [];\n\t\t\tconst ids = claimable.map((job) => job.id);\n\t\t\tawait tx\n\t\t\t\t.update(jobs)\n\t\t\t\t.set({\n\t\t\t\t\tstatus: 'running',\n\t\t\t\t\tattempts: sql`${jobs.attempts} + 1`,\n\t\t\t\t\tlockedAt: now,\n\t\t\t\t\tleaseUntil: new Date(now.getTime() + leaseMs),\n\t\t\t\t\tstartedAt: sql`coalesce(${jobs.startedAt}, now())`,\n\t\t\t\t\tupdatedAt: now\n\t\t\t\t})\n\t\t\t\t.where(inArray(jobs.id, ids));\n\t\t\t// The SELECT above read the rows BEFORE the increment (#391 review):\n\t\t\t// report the incremented attempt so retry bookkeeping in\n\t\t\t// runClaimed() sees 1..maxAttempts and a job runs at most\n\t\t\t// maxAttempts times.\n\t\t\treturn claimable.map((job) => ({ ...job, attempts: job.attempts + 1 }));\n\t\t});\n\t},\n\n\t/**\n\t * Claim and run one batch. Called by the runner — see `runner.ts`.\n\t * Exposed for tests and manual triggering. `leaseMs` bounds how long a\n\t * claim stays exclusive without a heartbeat.\n\t */\n\tasync processNextBatch(batchSize = 10, leaseMs = DEFAULT_LEASE_MS): Promise {\n\t\tconst claimed = await this.claimBatch(batchSize, leaseMs);\n\t\tlet processed = 0;\n\t\tfor (const job of claimed) {\n\t\t\tconst handler = handlers.get(job.type);\n\t\t\tif (!handler) {\n\t\t\t\tawait db\n\t\t\t\t\t.update(jobs)\n\t\t\t\t\t.set({ status: 'failed', error: 'Unknown handler', updatedAt: new Date(), finishedAt: new Date() })\n\t\t\t\t\t.where(eq(jobs.id, job.id));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait this.runClaimed(job.id, job.payload as Record, job.attempts, job.maxAttempts, handler, leaseMs);\n\t\t\tprocessed++;\n\t\t}\n\t\treturn processed;\n\t},\n\n\t/** Execute a claimed job (already `running`, attempts already incremented). */\n\tasync runClaimed(\n\t\tjobId: string,\n\t\tpayload: Record,\n\t\tattempts: number,\n\t\tmaxAttempts: number,\n\t\thandler: JobHandler,\n\t\tleaseMs: number\n\t) {\n\t\tconst context: JobHandlerContext = {\n\t\t\tjobId,\n\t\t\tprogress: (value) => this.progress(jobId, value, leaseMs),\n\t\t\theartbeat: () => this.heartbeat(jobId, leaseMs)\n\t\t};\n\t\ttry {\n\t\t\tconst result = await handler(payload, context);\n\t\t\tawait db\n\t\t\t\t.update(jobs)\n\t\t\t\t.set({\n\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\tprogress: 100,\n\t\t\t\t\tresult: (result ?? {}) as Record,\n\t\t\t\t\terror: null,\n\t\t\t\t\tfinishedAt: new Date(),\n\t\t\t\t\tupdatedAt: new Date()\n\t\t\t\t})\n\t\t\t\t.where(eq(jobs.id, jobId));\n\t\t} catch (e) {\n\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\tconst nonRetryable = e instanceof NonRetryableJobError;\n\t\t\tconst failed = nonRetryable || attempts >= maxAttempts;\n\t\t\tawait db\n\t\t\t\t.update(jobs)\n\t\t\t\t.set({\n\t\t\t\t\tstatus: failed ? 'failed' : 'queued',\n\t\t\t\t\terror: message,\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t// retryable: exponential backoff before the next attempt (#328)\n\t\t\t\t\t...(failed ? { finishedAt: new Date() } : { runAfter: new Date(Date.now() + computeBackoffMs(attempts)) })\n\t\t\t\t})\n\t\t\t\t.where(eq(jobs.id, jobId));\n\t\t}\n\t}\n};\n", @@ -131,7 +131,7 @@ export const MODULE_RECIPE_DATA: Record = { "notifications": { "id": "notifications", "package": "@svforge/notifications", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [], "files": { "/lib/components/svforge/ui/NotificationsBell.svelte": "\n\n (open = details.open)}\n\tcloseOnInteractOutside={true}\n\trestoreFocus={true}\n>\n\t\n\t\t\n\t\t{#if unreadCount > 0}\n\t\t\t{unreadCount}\n\t\t{/if}\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{m.notif_title()}\n\t\t\t\t\t{m.notif_mark_all()}\n\t\t\t\t
\n\n\t\t\t\t{#if items.length === 0}\n\t\t\t\t\t

{m.notif_empty()}

\n\t\t\t\t{:else}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\t
\n\n", @@ -144,7 +144,7 @@ export const MODULE_RECIPE_DATA: Record = { "oauth": { "id": "oauth", "package": "@svforge/oauth", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [], "files": { "/lib/components/svforge/ui/OAuthButtons.svelte": "\n\n
\n signIn('google')}\n disabled={loading !== null}\n class:cursor-pointer={loading === null}\n class:cursor-not-allowed={loading !== null}\n class=\"btn preset-filled-surface-400-600 w-full flex items-center justify-center gap-2\"\n >\n {#if loading === 'google'}\n \n {:else}\n \n {/if}\n Google\n \n\n signIn('github')}\n disabled={loading !== null}\n class:cursor-pointer={loading === null}\n class:cursor-not-allowed={loading !== null}\n class=\"btn preset-filled-surface-400-600 w-full flex items-center justify-center gap-2\"\n >\n {#if loading === 'github'}\n \n {:else}\n \n {/if}\n GitHub\n \n
\n" @@ -153,7 +153,7 @@ export const MODULE_RECIPE_DATA: Record = { "realtime": { "id": "realtime", "package": "@svforge/realtime", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "@types/ws", @@ -175,7 +175,7 @@ export const MODULE_RECIPE_DATA: Record = { "tiptap": { "id": "tiptap", "package": "@svforge/tiptap", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "@tiptap/core", @@ -212,7 +212,7 @@ export const MODULE_RECIPE_DATA: Record = { "ui_toast": { "id": "ui_toast", "package": "@svforge/ui_toast", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "@skeletonlabs/skeleton-svelte", @@ -228,7 +228,7 @@ export const MODULE_RECIPE_DATA: Record = { "uploads": { "id": "uploads", "package": "@svforge/uploads", - "version": "2.2.0", + "version": "2.2.1", "dependencies": [ { "name": "@aws-sdk/client-s3", diff --git a/packages/svforge/src/recipe-version.ts b/packages/svforge/src/recipe-version.ts index 292a7605..07815c6b 100644 --- a/packages/svforge/src/recipe-version.ts +++ b/packages/svforge/src/recipe-version.ts @@ -1,4 +1,4 @@ // AUTO-GENERATED - DO NOT EDIT // Run bun run prebuild to regenerate (canonical version = package.json) -export const SDFORGE_RECIPE_VERSION = "2.2.2"; +export const SDFORGE_RECIPE_VERSION = "2.2.3"; diff --git a/packages/tiptap/CHANGELOG.md b/packages/tiptap/CHANGELOG.md index 37872481..4a031433 100644 --- a/packages/tiptap/CHANGELOG.md +++ b/packages/tiptap/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/tiptap +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/tiptap/package.json b/packages/tiptap/package.json index d68e6765..456d8820 100644 --- a/packages/tiptap/package.json +++ b/packages/tiptap/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/tiptap", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Tiptap — rich text editor with toolbar and preview", "author": "Ludo (https://lelab.dev)", diff --git a/packages/ui_toast/CHANGELOG.md b/packages/ui_toast/CHANGELOG.md index 4c73e74a..7e49b82a 100644 --- a/packages/ui_toast/CHANGELOG.md +++ b/packages/ui_toast/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/ui_toast +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/ui_toast/package.json b/packages/ui_toast/package.json index 07ac60dd..17215e2d 100644 --- a/packages/ui_toast/package.json +++ b/packages/ui_toast/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/ui_toast", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Toast — notification toasts via Skeleton UI", "author": "Ludo (https://lelab.dev)", diff --git a/packages/uploads/CHANGELOG.md b/packages/uploads/CHANGELOG.md index b693b1e0..ab613356 100644 --- a/packages/uploads/CHANGELOG.md +++ b/packages/uploads/CHANGELOG.md @@ -1,5 +1,11 @@ # @svforge/uploads +## 2.2.1 + +### Patch Changes + +- [#597](https://github.com/lelabdev/svforge/pull/597) [`6304081`](https://github.com/lelabdev/svforge/commit/6304081b92bdd1fa50c5167bf150544391eec19a) Thanks [@LudoLoops](https://github.com/LudoLoops)! - Replace deprecated SvelteKit alias configuration with explicit `#lib` package subpath imports while retaining Kit 2 compatibility. + ## 2.2.0 No changes in this release. diff --git a/packages/uploads/package.json b/packages/uploads/package.json index 37b16c10..1673097f 100644 --- a/packages/uploads/package.json +++ b/packages/uploads/package.json @@ -1,6 +1,6 @@ { "name": "@svforge/uploads", - "version": "2.2.0", + "version": "2.2.1", "type": "module", "description": "SVForge Uploads — file uploads to S3/R2", "author": "Ludo (https://lelab.dev)",