diff --git a/CHANGELOG.md b/CHANGELOG.md index 99292ce5..1244ab2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,27 +4,77 @@ All notable Arkstack changes are documented in this file. The format follows semantic versioning principles. -## [Unreleased] - Upcoming features and changes that are currently in development or planned for the next release. +## [Unreleased] + +No unreleased changes are documented yet. + +## [0.22.0] - 2026-11-05 + +### Added + +- Added the Fastify runtime: `@arkstack/driver-fastify`, the `fastify` starter template, and a Fastify option in `create-arkstack`. Inertia is not available for Fastify yet. +- Added the `ServerDriver` base class to `@arkstack/common`, which the Express, H3 and Fastify drivers share for server startup. +- Added the Versioning and Releases and Upgrading guides to the docs. + +### Changed + +- Updated resora to `1.3.36`, `@resora/plugin-clear-router` to `1.0.73`, and clear-router to `2.9.4`. + +## [0.21.0 - 0.21.1] - 2026-10-28 to 2026-11-05 + +Released versions: `0.21.0`, `0.21.1`. + +### Added + +- Added `@arkstack/lang` with file-based translations under `src/lang/{locale}`, the `__()` helpers, plural forms, per-request locale middleware (`localeMiddleware()` and `h3LocaleMiddleware()`), and translation commands. The Express and H3 templates include it. + +### Changed + +- `@arkstack/console` and `@arkstack/console-slim` import resora's CLI classes from `resora/cli`, and the resora catalog version is `^1.3.35`. `@h3ravel/musket` is now an optional peer of resora. + +## [0.20.0 - 0.20.2] - 2026-10-21 to 2026-10-23 + +Released versions: `0.20.0`, `0.20.1`, `0.20.2`. ### Added - `Hook.emit(name, pos, ...args)` calls a hook position and returns its result (a Promise for async hooks); it is a no-op when nothing is registered, so call sites drop the `if (Hook.has(...))` guard. `Hook.on()`, `Hook.off()` and `Hook.all()` are the new names for `set()`, `unset()` and `getAll()`. - `server:start` hook, run by the Express and H3 drivers once the server is listening, after the startup banner and any tunnel. `after` always runs and receives `ServerStartInfo` (exported from `@arkstack/contract`): `host`, `port`, `scheme`, `secure`, a browsable `url`, and `tunnelUrl` (`TUNNEL_URL`, or the URL `ark dev -t` opened). `tunneled` runs next, only when a tunnel is open, with `TunneledServerStartInfo` (where `tunnelUrl` is always a string). A failing listener is logged and never stops the server or skips the other position. -- `ark queue:work --queue` accepts a comma-separated priority list (`--queue=webhook,ocr,default`) and the `*` wildcard for every queue on the connection (`--queue=*`, or `--queue=webhook,*` to put some first). `Worker.runNextJob()` and `daemon({ queue })` take a string or a list. `QueueContract.popAny()` backs the wildcard: the `database` driver takes the oldest due job across all queues, and the `redis` driver discovers queues with `SCAN` and works them round-robin. A bare `queue:work` still works only the default queue. -- `transports.pusher.public_host` / `public_port` / `public_use_tls` (`PUSHER_PUBLIC_*`) set the Pusher-protocol server address handed to browsers when it differs from the one the app uses, such as soketi inside Docker or behind a TLS proxy. Each defaults to its server-side counterpart. -- `RealtimeConfig.importModule` loads the transport SDKs (`pusher-js`, `firebase/*`) for every transport and for device tokens, so pages without a bundler can load them from a CDN. Defaults to a dynamic `import()`. The `createPusherTransport`, `createFirebaseTransport`, `createFirebaseDatabaseTransport` and `requestDeviceToken` helpers accept it as an extra argument. -- `RealtimeClient.deviceToken({ serviceWorker })` takes a service worker registration or script URL (registered with the Firebase web config as `firebaseConfig`), returns `null` where web push is unsupported, and falls back to the `firebase` config when streaming over another transport. `requestDeviceToken()` issues a token without a client. + +### Changed + +- `@arkstack/common` re-exports `Hook` and its types from `@arkstack/foundry` instead of shipping its own copy. Declare typed hook names by augmenting `HookRegistry` on `@arkstack/foundry`; an augmentation of `@arkstack/common` no longer has any effect. +- Deprecated `Hook.set()`, `Hook.unset()`, `Hook.getAll()`, and calling a hook through `Hook.get(name, pos, ...args)`; they keep working. `on()` accepts a partial set of positions for typed hooks. +- The templates are formatted with Prettier. + +### Fixed + +- `@arkstack/common` and `@arkstack/foundry` each had their own `Hook` registry, so hooks registered through one import never fired for code reading the other. The Express auth middleware read `common`'s, so `middleware:auth` hooks registered as documented (via `foundry`) never ran on Express. +- The `shutdown` hook checked for a `before` position but called `after`; it now runs `before`. +- `Hook.unset(name)` / `remove(name)` with a name that wasn't registered cleared every hook. +- H3's auth middleware now awaits `middleware:auth` hooks, as Express already did. +- Every JWT now carries a random `jti`. Two logins by the same user within one second produced identical tokens, and the second failed on the unique `personal_access_tokens.token` column. +- `Hash.otp` derives each label's TOTP secret from `APP_KEY`. It used one hardcoded secret for every label, so a code issued for one account was valid for all of them. + +## [0.19.0 - 0.19.6] - 2026-10-10 to 2026-10-21 + +Released versions: `0.19.0` through `0.19.6`. + +### Added + - Added the `Broadcast` facade to `@arkstack/notifications`: a channel authorization registry (`Broadcast.channel('private-team.{teamId}', authorizer)`) used by every transport, `Broadcast.broadcast()` for arbitrary events, `Broadcast.handleAuth()` for custom auth routes, `Broadcast.clientConfig()`, and `Broadcast.routes()` registering `POST /realtime/auth` and `GET /realtime/config`. - Added the `firebase-database` realtime transport (Firebase Realtime Database channels with per-user, time-limited grants checked by security rules, plus bounded `prune()`), the signal-only `polling` transport, and the `memory` transport for tests, on both server and client. - Added device push alongside any streaming transport: `drivers.realtime.push: 'firebase'` pushes notifications sent to a user to their devices, with `push_tokens` for resolving tokens, `.push(false)` and `.devices(tokens)` per send, and the multicast outcome on `result.push`. - Added self-hosted Pusher-protocol servers (soketi) through `transports.pusher.host` / `port` and the client's `wsHost` / `wsPort`. - Added `RealtimeClient.onPoll()`, `RealtimeClient.deviceToken()` and `RealtimeClient.transportName` to `@arkstack/realtime`, with `createFirebaseDatabaseTransport`, `createPollingTransport`, `createMemoryTransport` and `emitMemoryEvent`. +- `RealtimeClient.deviceToken({ serviceWorker })` takes a service worker registration or script URL (registered with the Firebase web config as `firebaseConfig`), returns `null` where web push is unsupported, and falls back to the `firebase` config when streaming over another transport. `requestDeviceToken()` issues a token without a client. +- `RealtimeConfig.importModule` loads the transport SDKs (`pusher-js`, `firebase/*`) for every transport and for device tokens, so pages without a bundler can load them from a CDN. Defaults to a dynamic `import()`. The `createPusherTransport`, `createFirebaseTransport`, `createFirebaseDatabaseTransport` and `requestDeviceToken` helpers accept it as an extra argument. +- `transports.pusher.public_host` / `public_port` / `public_use_tls` (`PUSHER_PUBLIC_*`) set the Pusher-protocol server address handed to browsers when it differs from the one the app uses, such as soketi inside Docker or behind a TLS proxy. Each defaults to its server-side counterpart. +- `ark queue:work --queue` accepts a comma-separated priority list (`--queue=webhook,ocr,default`) and the `*` wildcard for every queue on the connection (`--queue=*`, or `--queue=webhook,*` to put some first). `Worker.runNextJob()` and `daemon({ queue })` take a string or a list. `QueueContract.popAny()` backs the wildcard: the `database` driver takes the oldest due job across all queues, and the `redis` driver discovers queues with `SCAN` and works them round-robin. A bare `queue:work` still works only the default queue. ### Changed -- `@arkstack/common` re-exports `Hook` and its types from `@arkstack/foundry` instead of shipping its own copy. Declare typed hook names by augmenting `HookRegistry` on `@arkstack/foundry`; an augmentation of `@arkstack/common` no longer has any effect. -- Deprecated `Hook.set()`, `Hook.unset()`, `Hook.getAll()`, and calling a hook through `Hook.get(name, pos, ...args)`; they keep working. `on()` accepts a partial set of positions for typed hooks. -- `env()` now reads a value as a number only when the number says exactly what was written. `Number()` accepts hexadecimal, binary and octal literals, exponents, surrounding whitespace, and integers wider than a double can hold, and coercing those loses information silently. `'6379'` is still a number; `'0x1f'`, `'007'`, `'1e5'`, `' 12 '`, `'0b1010'`, `'Infinity'` and `'12345678901234567890'` are now the strings they were written as. +- The templates' `src/config/notifications.ts` includes the new transports, `realtime.push`, and the Pusher host options. ### Docs @@ -32,37 +82,353 @@ The format follows semantic versioning principles. ### Fixed -- `@arkstack/common` and `@arkstack/foundry` each had their own `Hook` registry, so hooks registered through one import never fired for code reading the other. The Express auth middleware read `common`'s, so `middleware:auth` hooks registered as documented (via `foundry`) never ran on Express. -- The `shutdown` hook checked for a `before` position but called `after`; it now runs `before`. -- `Hook.unset(name)` / `remove(name)` with a name that wasn't registered cleared every hook. -- H3's auth middleware now awaits `middleware:auth` hooks, as Express already did. -- A failing Express `after` middleware exited the process: `Logger.error` exits by default, and the post-response handler didn't pass `exit = false`. - `ark queue:work --queue` rejected every value in 0.19.5 ("argument '…' is invalid. Allowed choices are …"): musket reads a trailing `: a, b, c` in an option's description as its allowed choices, and the new `--queue` description had one. The description no longer does, and a test pins that `queue:work` options declare no choices. - `DotPath` (the key type behind `config()` and `configure()`) is now depth-bounded and stops at built-in object types, so configs holding deep or self-referencing library types (nodemailer transports) no longer fail to typecheck with "circularly references itself". The `@ts-expect-error` workarounds in the H3 template and the notifications config are gone. - `build:templates` builds the templates one at a time; run in parallel, each template's prebuild rebuilt the shared packages concurrently and the builds clobbered each other. - Fixed type errors in `@arkstack/auth` (`AuthenticationException` request typing and two tests) and in `@arkstack/common`'s `renderError`, which used the `view()` global without a type for it. - Express `after` middleware no longer resumes the router once the response has finished. It used to receive the real `next`, so a middleware such as `requestLogger()` sent each request on through the remaining routes after responding, raising "Cannot set headers after they are sent to the client". +- A failing Express `after` middleware exited the process: `Logger.error` exits by default, and the post-response handler didn't pass `exit = false`. - `UserNotificationCenter.markAllRead()` now marks every unread notification read; it used a single-row `update()` and marked only one. `unreadForUser(user, perPage)` now honours `perPage`, and both filter unread rows with `whereNull('readAt')`. - `UserNotificationCenter.send()` (and `Notification.db().broadcast()`) now broadcast on the user's channel. They previously sent to the user's push tokens as channels, so nothing was broadcast at all for users without tokens. Apps on the `firebase` transport still reach their users' devices without changes, because device push is on there unless `drivers.realtime.push` is `false`. - `Notification.db().broadcast()` now returns the builder, so it chains as documented. - The Pusher auth route registered by `registerAuthRoute()` now answers with HTTP 200 (pusher-js rejects any other status), registers on the H3 driver instead of the Express one, and authorizes through the channel registry. -- Fixed `env()` destroying hexadecimal secrets. A `0x`-prefixed 32-byte key parses as a number, so it was silently replaced by a float keeping roughly its first thirteen digits, with nothing downstream able to tell. Private keys, hashes and identifiers read through `env()` now survive verbatim. -## [0.12.36] - 2026-06-20 +## [0.18.0 - 0.18.12] - 2026-08-27 to 2026-09-28 + +Released versions: `0.18.0` through `0.18.12`. + +### Added + +- Added `@arkstack/encryption`, a zero-dependency isomorphic encryption package built on the Web Crypto API, with symmetric encryption, key helpers, end-to-end secure channels and sealed boxes. `@arkstack/common` re-exports it as `Encryption`. +- Added `.priority()`, `.ttl()` and `.collapseKey()` to the realtime notification builder, with defaults under `drivers.realtime.delivery`, so Firebase broadcasts can wake a device and replace earlier ones. +- Added `loadJobs()`, which registers the application's job classes in `queue:work` processes. +- Added `setModelResolver()` to `@arkstack/common`, so `getModel()` resolves models through the configured database integration instead of loading a specific ORM. + +### Changed + +- Moved the Express and H3 drivers into their own `ExpressDriver` and `H3Driver` modules, and streamlined the `TwoFactor` class. +- `queue:work` logs each job's outcome, including whether a failed job will be retried. +- The console works without arkormx installed. +- Scheduled tasks are due for the whole minute they are scheduled in. + +### Fixed + +- Fixed `env()` destroying hexadecimal secrets. A `0x`-prefixed 32-byte key parses as a number, so it was silently replaced by a float keeping roughly its first thirteen digits. `env()` now reads a value as a number only when the number says exactly what was written: `'6379'` is still a number; `'0x1f'`, `'007'`, `'1e5'`, `' 12 '`, `'0b1010'`, `'Infinity'` and `'12345678901234567890'` are now the strings they were written as. +- Fixed `queue:work` workers failing every job with "Job X is not registered", and deleting it without a record. +- `queue:work --stop-when-empty` and `--max-jobs` now take effect. +- Fixed `FormDataMiddleware` option types. + +## [0.17.4 - 0.17.24] - 2026-07-15 to 2026-08-25 + +Released versions: `0.17.4` through `0.17.24`. ### Added -- Added `rebuildOutput()` to `@arkstack/common` — wipes `outputDir()` and runs a build-only `tsdown` pass (no app boot, no dev watcher), inheriting the current `NODE_ENV`. Used by the console kernel's self-heal and reusable wherever a clean, boot-free rebuild is needed. +- Added `getModelSync()`. +- Added `sendmail` and `ses` mail transports, wider nodemailer config options, and custom realtime and mail notification transports. +- Added mail attachments and queued notifications. +- Added the `resolvePagination()` helper, with optional defaults for it and `perPage()`. +- Added the realtime `apiUrl` config, Pusher channel authorization, and `registerAuthRoute()`. +- Added realtime client events for the Pusher and Firebase transports. +- Added ngrok tunneling to `ark dev`. +- Added resource copying to the tsdown build configuration. ### Changed -- Express and H3 templates now clean the output dir on build/prepare (`clean: env !== 'dev' || CLI_BUILD` in `tsdown.config.ts`), so a renamed, removed, or added source file can't leave a stale emitted module behind. The live dev watcher (`ark dev`) is excluded so its running server isn't disrupted. +- Upgraded musket and used its CLI lifecycle for teardown. +- `env()` and `config()` are available globally to the prepare command and every subsystem. +- The console syncs arkormx artifacts during prepare, and generated tsconfigs include `.arkormx/*.d.ts`. + +### Fixed + +- Fixed the mail from-address being undefined for custom transports. +- `registerAuthRoute()` now validates requests. +- `requestLogger()` reports the full request duration. +- `MailNotification.attachment()` and `attachments()` return the notification, so they chain. +- The dev tunnel stays the same across restarts. +- Arkorm boots at every place that needs it. + +## [0.17.3] - 2026-07-14 + +### Added + +- Added request-scoped Clear Router container bindings for Arkstack `Request`, `Response`, and `Session` instances. +- Added automatic `arkstackHttpPlugin` registration to the Express and H3 drivers. +- Added `clearRequest` and `clearResponse` context values backed by the unified Arkstack HTTP classes. +- Added automatic Clear Router decorator setup through `@arkstack/http`, allowing controller arguments decorated with `@Bind()` to resolve without additional container setup. + +### Changed + +- Resolve the global `request()`, `response()`, and `session()` helpers from the active request scope while retaining fallback instances outside a routed request. +- Preserve unified request body, query, params, route, context, and original request data when creating an Arkstack `Request` from a framework request. +- Updated Clear Router to `2.9.2`, `@arkormx/plugin-clear-router` to `0.1.54`, and `@resora/plugin-clear-router` to `1.0.68`. + +### Fixed + +- Fixed concurrent requests sharing stale global request, response, or session instances. +- Fixed repeated dependency resolution returning different HTTP objects within the same request. +- Fixed the database queue driver eligibility query by comparing `available_at` against the database's current timestamp. + +### Tests + +- Added integration coverage proving bare `@Bind()` controller arguments resolve the same request-scoped Arkstack HTTP instances, including across concurrent requests. ### Docs +- Added integrated Arkstack guides and sidebar references for Resora responses, Arkormˣ database access, Kanun validation, and Parasito HTTP testing. +- Documented request-scoped HTTP objects, controller injection, global helpers, automatic driver registration, and native framework interoperability. +- Restored the missing changelog history from `0.4.1` through `0.17.2`. + +## [0.17.0 - 0.17.2] - 2026-07-09 + +Released versions: `0.17.0`, `0.17.1`, `0.17.2`. + +### Added + +- Added `@arkstack/scheduler` with Laravel-style scheduled tasks. +- Added support for both `per_page` and `per-page` query parameters in pagination helpers. + +### Fixed + +- Made the `per_page` argument optional in `perPage()`. +- Corrected queued-job `available_at` handling and resolved lint errors across the workspace. + +## [0.16.0 - 0.16.13] - 2026-06-28 to 2026-07-07 + +Released versions: `0.16.0` through `0.16.13`. + +### Added + +- Added a complete React and Inertia starter, Inertia stack selection in `create-arkstack`, React Refresh support, and production Vite asset resolution. +- Added `@arkstack/realtime` clients for core, React, and Vue applications. +- Added Pusher and Firebase realtime notification drivers, Firebase service-account configuration, and multicast broadcasting. +- Added `UserNotificationCenter.send()`, paginated unread notifications, database-notification push delivery, and `PhoneNumber` support for SMS recipients. +- Added interactive package selection and package config publishing to the console. + +### Changed + +- Boot Arkorm before console commands run. +- Published scaffolding stubs with their packages and made generated config interface keys valid TypeScript. +- Resolved the global `env()` helper through `EnvLoader` and allowed named mail from-addresses. + +### Fixed + +- Fixed production Inertia assets, database and realtime notification recipient checks, and realtime push-token resolution. + +## [0.15.0 - 0.15.5] - 2026-06-27 to 2026-06-28 + +Released versions: `0.15.0` through `0.15.5`. + +### Added + +- Added the `@arkstack/inertia` server adapter, server-side rendering, real HTTP and browser coverage, and the `ark inertia:ssr` command. +- Added `--host` and `--secure` options to `ark dev`. +- Added Edge tags for `@vite`, `@viteReactRefresh`, `@inertia`, and `@inertiaHead`. + +### Changed + +- Run tsdown directly from `ark dev` instead of through a package-manager wrapper. +- Made Arkorm an optional peer dependency of common, console, and HTTP packages. +- Moved shared dependencies into the workspace catalog. + +### Fixed + +- Fixed Inertia client hydration and loading common, console, and HTTP without Arkorm installed. + +## [0.14.0 - 0.14.22] - 2026-06-22 to 2026-06-27 + +Released versions: `0.14.0` through `0.14.3`, and `0.14.14` through `0.14.22`. + +### Added + +- Unified application secrets under `APP_KEY` and added guaranteed key generation. +- Added an ngrok-powered `tunnel` option and exposed the tunnel URL through the application globals and environment. +- Added runtime-directory resolution for production builds and deployment guidance. +- Added lazy `.env` loading, typed `env()` return values through `EnvRegistry`, and generated environment registry declarations. +- Added the built-in Faker integration with Pictwo image support. +- Allowed `AppException` to define a custom response body and `AppConfig` to be extended. + +### Changed + +- Replaced the key-generation ignore option with the global `--no-interaction` option. +- Updated application module, resource, and config loading to resolve from production build output. +- Updated Clear Router and its plugins to the `2.9` line. + +### Fixed + +- Made config loading resilient to individual module failures and alternate build layouts. +- Prevented duplicate `export {}` declarations in generated types. + +## [0.13.0 - 0.13.2] - 2026-06-21 to 2026-06-22 + +Released versions: `0.13.0`, `0.13.1`, `0.13.2`. + +### Added + +- Added the `@arkstack/cache`, `@arkstack/queue`, and `@arkstack/jobs` packages. +- Added native Arkorm and Resora database configuration. +- Added explicit `lean` and `full` project-creation options. + +### Fixed + +- Resolved workspace catalog dependencies when creating projects outside the monorepo. +- Preserved comments and spacing when creating `.env` files. +- Updated release preparation to keep Create Arkstack template metadata in sync. + +## [0.12.0 - 0.12.37] - 2026-05-28 to 2026-06-20 + +Released versions: `0.12.0`, `0.12.1`, and `0.12.3` through `0.12.37`. + +### Added + +- Added `@arkstack/foundry` and centralized application lifecycle hooks and globals. +- Added custom filesystem disk drivers, in-memory config updates, current disk and driver accessors, and Google Cloud Storage support. +- Added `APP_ENV`, build development mode, class-based middleware, stateless route session opt-in, and package-model aliases. +- Added `APP_HOST` with `HOST` fallback and support for the platform-provided `PORT` variable. +- Added `VERBOSITY` support to the prepare script. + +### Changed + +- Centralized config typing and improved generated config interfaces. +- Load custom console commands directly from TypeScript source through jiti, with build output retained as the production fallback. +- Bind application servers to `0.0.0.0` by default and prefer `PORT` over `APP_PORT`. +- Updated release tooling to synchronize workspace catalog dependency versions. + +### Fixed + +- Fixed authentication request synchronization and hydration of bound HTTP requests. +- Fixed filesystem disk registration when disk and driver names differ. +- Preserved conflicting trait methods and made invalid trait errors clearer. +- Made console builds recover from stale `.arkstack/build` output and ensured config is ready before tsdown runs. `rebuildOutput()` in `@arkstack/common` wipes the output directory and runs a build-only tsdown pass. +- Templates clean the output directory on build and prepare, so renamed or removed source files can't leave stale modules behind. +- Improved route-loading errors and prevented dot-path config updates from replacing the entire store. + +## [0.11.0 - 0.11.6] - 2026-05-28 + +Released versions: `0.11.0` through `0.11.6`. + +### Added + +- Added the `@arkstack/foundry` package and moved shared hook and lifecycle behavior into it. +- Added Twilio to the typed SMS transport configuration. +- Added options forwarding to `importFile()`. + +### Changed + +- Centralized application lifecycle handling, lifecycle globals, and generated config types. + +## [0.10.0 - 0.10.10] - 2026-05-27 + +Released versions: `0.10.0` through `0.10.10`. + +### Added + +- Added an application-scoped root directory and made default framework paths resolve from it. +- Expanded manual Create Arkstack options with kit choices, kit locking, and token requirements. + +### Changed + +- Moved request-body and session augmentations into `@arkstack/http` and shared Resora config types into common code. +- Removed duplicated starter boilerplate and the retired `arkstack-express` workspace package. + +### Fixed + +- Lazy-load optional auth dependencies in runtime drivers. +- Fixed manual project-creation overrides and lean-profile database setup. + +## [0.9.0 - 0.9.1] - 2026-05-26 + +Released versions: `0.9.0`, `0.9.1`. + +### Changed + +- Refactored framework bootstrapping around self-initializing core packages to reduce starter boilerplate. +- Hardened prepare and template build workflows for clean CI environments. + +## [0.8.0] - 2026-05-23 + +### Added + +- Added persistent sessions, flash data, validation error bags, and automatic view error sharing. +- Added encrypted session payloads and file-based SMTP transport support. + +### Changed + +- Restricted CORS origins to configured defaults and consolidated application configuration. + +## [0.7.0 - 0.7.20] - 2026-05-14 to 2026-05-22 + +Released versions: `0.7.0` through `0.7.20`. + +### Added + +- Added JWT auth utilities, request rate limiting, Clear Router integration coverage, and custom HTML and text mail templates. +- Added the trait composition system, including class roots, Arkorm model integration, and conflicting-method support. +- Added a single entry point for global request, response, and context initialization. + +### Changed + +- Renamed `CurrentSession` and `currentSession()` to `Session` and `session()`. +- Updated starter migrations to use UUID primary and foreign keys. +- Renamed auth device payload types and aligned auth, notification, Clear Router, Arkorm, and Resora integrations. + +### Fixed + +- Ensured placeholder global request and response values are callable before runtime initialization. + +## [0.6.0 - 0.6.4] - 2026-05-13 + +Released versions: `0.6.0` through `0.6.4`. + +### Added + +- Added the H3 application starter with routing and middleware. +- Added template resource mounting during project creation. + +### Changed + +- Reworked package builds and publishing around generated templates and minified ESM output. + +### Fixed + +- Included package resources in published artifacts and corrected console binary exports and release paths. + +## [0.5.0 - 0.5.2] - 2026-05-11 + +Released versions: `0.5.0`, `0.5.1`, `0.5.2`. + +### Added + +- Added the initial `@arkstack/database` package, migrations, model factories, and seeders. +- Added CORS and form-data middleware and controller binding coverage. +- Added project scope selection and clearer template hints to Create Arkstack. + +### Changed + +- Updated controllers to return Resora resources and collections. +- Moved application models into the database model directory and removed obsolete core helpers and drivers. + +### Fixed + +- Await auth middleware hooks and standardized model identifier declarations. + +## [0.4.1 - 0.4.2] - 2026-05-08 + +Released versions: `0.4.1`, `0.4.2`. + +### Added + +- Added authentication to global request and context objects. +- Integrated View bootstrapping into the shared application bootstrap and test setup. + +### Changed + +- Made `View.boot()` return the factory when no view name is supplied. +- Resolve router imports from the active runtime and improved package prepublish build handling. + ### Fixed -- Console: self-heal a stale or incomplete build artifact instead of wedging the CLI. `runConsoleKernel` boots the app from the emitted output (`.arkstack/build` in dev, `dist` in prod) for every command — including `build` — before it can regenerate, so a source change since the last build (a module moved, renamed, or added) left the artifact stale and the boot threw `Cannot find module '/...'` with no way to recover. `loadCoreApp` now catches a missing-module error and, when source is present, regenerates the output once via `rebuildOutput()` and retries. Applies to both `@arkstack/console` and `@arkstack/console-slim`. +- Corrected global view instance resolution and expanded notification test coverage. ## [0.4.0] - 2026-05-07 diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 495f78e0..4976cd34 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -194,6 +194,7 @@ export default defineConfig({ { text: 'Hooks', link: '/guide/hooks' }, { text: 'HTTP', link: '/guide/http' }, { text: 'Deployment', link: '/guide/deployment' }, + { text: 'Upgrading', link: '/guide/upgrading' }, { text: 'API Reference', link: '/api' }, ], }, @@ -221,6 +222,7 @@ export default defineConfig({ { text: 'AI Agents', link: '/more/agents' }, { text: 'Roadmap', link: '/more/roadmap' }, { text: 'Contributing', link: '/more/contributing' }, + { text: 'Versioning and Releases', link: '/more/releases' }, { text: 'Changelog', link: '/more/changelog' }, ], }, diff --git a/docs/guide/upgrading.md b/docs/guide/upgrading.md new file mode 100644 index 00000000..71970702 --- /dev/null +++ b/docs/guide/upgrading.md @@ -0,0 +1,113 @@ +# Upgrading + +An Arkstack application has two parts that age differently. The `@arkstack/*` packages are ordinary npm dependencies you can update. The files `create-arkstack` copied from a starter template (`src/core`, `src/config`, `src/routes` and so on) now belong to your project, and an update to the packages doesn't touch them. Upgrading means updating the packages and then bringing over the template changes your app needs. + +Each release has one version number for every `@arkstack/*` package and every template. See [Versioning and Releases](/more/releases) for how versions are chosen. + +## Find your current version + +Your Arkstack version is the version of the `@arkstack/*` packages in `package.json`: + +```json +{ + "dependencies": { + "@arkstack/common": "^0.21.0", + "@arkstack/driver-express": "^0.21.0" + } +} +``` + +The installed version is in `node_modules/@arkstack/common/package.json`. + +## Upgrade steps + +1. Read the release notes for every version between yours and the target in the [changelog](/more/changelog), and the matching sections in [Version notes](#version-notes) below. +2. Update every `@arkstack/*` package to the same version. Mixing versions is not supported. + + ::: code-group + + ```sh [pnpm] + pnpm up "@arkstack/*@^0.22.0" + ``` + + ```sh [npm] + npm i @arkstack/common@^0.22.0 @arkstack/console@^0.22.0 # ...every @arkstack package you use + ``` + + ```sh [yarn] + yarn up "@arkstack/*@^0.22.0" + ``` + + ::: + +3. Update the companion libraries the release was tested with. Their versions are in the `catalog` section of [`pnpm-workspace.yaml`](https://github.com/arkstack-hq/arkstack/blob/main/pnpm-workspace.yaml) at the release tag: resora, kanun, arkormx, clear-router, parasito and your runtime (express, h3 or fastify). +4. Compare the template at your old and new versions (see below) and copy over the changes your app needs. +5. Publish any new stubs or migrations the release added with `pnpm ark publish --list`, then run your migrations. +6. Run your type check, tests and a production build. + +Upgrade one minor version at a time when you are several versions behind. Breaking changes only land in minor releases, and each one has notes below. + +## Comparing template versions + +Every release is tagged in the [arkstack repository](https://github.com/arkstack-hq/arkstack/tags) by its version, without a `v` prefix. The templates live in `templates/express`, `templates/h3` and `templates/fastify`. + +Download both versions of your template and compare them: + +```sh +npx giget gh:arkstack-hq/arkstack/templates/express#0.21.0 .upgrade/old +npx giget gh:arkstack-hq/arkstack/templates/express#0.22.0 .upgrade/new +diff -ru .upgrade/old .upgrade/new +``` + +Or, in a clone of the repository: + +```sh +git diff 0.21.0 0.22.0 -- templates/express +``` + +A few things to keep in mind when reading the diff: + +- The template's `package.json` uses `workspace:^` and `catalog:` versions. Use the versions from step 2 and 3 instead. +- Lean projects don't have the database, API and auth files that `create-arkstack` removes, so skip changes to them. +- Inertia files come from `packages/inertia/stubs`. Compare that folder for changes to the Inertia setup. +- Formatting-only changes, like the move to Prettier in 0.20.0, can be ignored. + +## Version notes + +These notes cover what an existing application has to do. New features that need no change are in the [changelog](/more/changelog). + +### 0.21 to 0.22 + +No changes are needed for Express and H3 apps. + +- Fastify is a new runtime with its own template. There is no migration path from Express or H3 to Fastify; see [Fastify Runtime](/guide/fastify-runtime) to start one. +- Update resora to `^1.3.36`, `@resora/plugin-clear-router` to `^1.0.73` and clear-router to `^2.9.4`. +- Since resora 1.3.35 (Arkstack 0.21.1), resora's CLI is imported from `resora/cli`, and `@h3ravel/musket` is an optional peer dependency. Templates already list musket. If your code imported `CliApp`, `MakeResource` or `InitCommand` from `resora`, import them from `resora/cli`. + +### 0.20 to 0.21 + +0.21 adds [localization](/guide/localization) with `@arkstack/lang`. To use it: + +1. Install `@arkstack/lang`. +2. Register its helpers in `src/core/bootstrap.ts`: + + ```ts + import '@arkstack/lang/setup' + ``` + +3. Add the locale middleware to the `before` list in `src/config/middleware.ts`. Use `localeMiddleware()` on Express and `h3LocaleMiddleware()` on H3, both from `@arkstack/lang`. +4. Add your translations under `src/lang/{locale}`, starting with `src/lang/en/messages.ts`. + +### 0.19 to 0.20 + +- `Hook.set()`, `Hook.unset()`, `Hook.getAll()` and calling a hook through `Hook.get(name, pos, ...args)` are deprecated. Use `Hook.on()`, `Hook.off()`, `Hook.all()` and `Hook.emit()`. The old names still work. +- `@arkstack/common` now re-exports `Hook` from `@arkstack/foundry`. If you declare typed hook names by augmenting `HookRegistry`, augment `@arkstack/foundry`; an augmentation of `@arkstack/common` no longer has any effect. +- On H3, remove the `// @ts-expect-error` above `applyMiddleware()` in `src/core/app.ts`. Config types no longer need it since 0.19.1, and TypeScript reports the unused directive. +- The templates are formatted with Prettier. Reformatting your app is optional. + +### 0.18 to 0.19 + +- `src/config/notifications.ts` gained settings for the new realtime transports: `realtime.push`, the Firebase `web` block, the `firebase-database` transport and the `polling` transport. Copy the parts you use from the template. +- `UserNotificationCenter.send()` and `Notification.db().broadcast()` now broadcast on the user's channel instead of their push tokens. Apps on the `firebase` transport still reach devices, because device push stays on unless `drivers.realtime.push` is `false`. +- On H3, import `H3Middleware` from `@arkstack/driver-h3/types` in `src/core/app.ts`. +- Since 0.18.9, `env()` returns a number only when the value reads exactly as that number. Values such as `'0x1f'`, `'007'` or `'1e5'` are now returned as strings. Convert them yourself if your config relied on the old behaviour. diff --git a/docs/more/changelog.md b/docs/more/changelog.md index d7df1855..1244ab2a 100644 --- a/docs/more/changelog.md +++ b/docs/more/changelog.md @@ -8,6 +8,146 @@ The format follows semantic versioning principles. No unreleased changes are documented yet. +## [0.22.0] - 2026-11-05 + +### Added + +- Added the Fastify runtime: `@arkstack/driver-fastify`, the `fastify` starter template, and a Fastify option in `create-arkstack`. Inertia is not available for Fastify yet. +- Added the `ServerDriver` base class to `@arkstack/common`, which the Express, H3 and Fastify drivers share for server startup. +- Added the Versioning and Releases and Upgrading guides to the docs. + +### Changed + +- Updated resora to `1.3.36`, `@resora/plugin-clear-router` to `1.0.73`, and clear-router to `2.9.4`. + +## [0.21.0 - 0.21.1] - 2026-10-28 to 2026-11-05 + +Released versions: `0.21.0`, `0.21.1`. + +### Added + +- Added `@arkstack/lang` with file-based translations under `src/lang/{locale}`, the `__()` helpers, plural forms, per-request locale middleware (`localeMiddleware()` and `h3LocaleMiddleware()`), and translation commands. The Express and H3 templates include it. + +### Changed + +- `@arkstack/console` and `@arkstack/console-slim` import resora's CLI classes from `resora/cli`, and the resora catalog version is `^1.3.35`. `@h3ravel/musket` is now an optional peer of resora. + +## [0.20.0 - 0.20.2] - 2026-10-21 to 2026-10-23 + +Released versions: `0.20.0`, `0.20.1`, `0.20.2`. + +### Added + +- `Hook.emit(name, pos, ...args)` calls a hook position and returns its result (a Promise for async hooks); it is a no-op when nothing is registered, so call sites drop the `if (Hook.has(...))` guard. `Hook.on()`, `Hook.off()` and `Hook.all()` are the new names for `set()`, `unset()` and `getAll()`. +- `server:start` hook, run by the Express and H3 drivers once the server is listening, after the startup banner and any tunnel. `after` always runs and receives `ServerStartInfo` (exported from `@arkstack/contract`): `host`, `port`, `scheme`, `secure`, a browsable `url`, and `tunnelUrl` (`TUNNEL_URL`, or the URL `ark dev -t` opened). `tunneled` runs next, only when a tunnel is open, with `TunneledServerStartInfo` (where `tunnelUrl` is always a string). A failing listener is logged and never stops the server or skips the other position. + +### Changed + +- `@arkstack/common` re-exports `Hook` and its types from `@arkstack/foundry` instead of shipping its own copy. Declare typed hook names by augmenting `HookRegistry` on `@arkstack/foundry`; an augmentation of `@arkstack/common` no longer has any effect. +- Deprecated `Hook.set()`, `Hook.unset()`, `Hook.getAll()`, and calling a hook through `Hook.get(name, pos, ...args)`; they keep working. `on()` accepts a partial set of positions for typed hooks. +- The templates are formatted with Prettier. + +### Fixed + +- `@arkstack/common` and `@arkstack/foundry` each had their own `Hook` registry, so hooks registered through one import never fired for code reading the other. The Express auth middleware read `common`'s, so `middleware:auth` hooks registered as documented (via `foundry`) never ran on Express. +- The `shutdown` hook checked for a `before` position but called `after`; it now runs `before`. +- `Hook.unset(name)` / `remove(name)` with a name that wasn't registered cleared every hook. +- H3's auth middleware now awaits `middleware:auth` hooks, as Express already did. +- Every JWT now carries a random `jti`. Two logins by the same user within one second produced identical tokens, and the second failed on the unique `personal_access_tokens.token` column. +- `Hash.otp` derives each label's TOTP secret from `APP_KEY`. It used one hardcoded secret for every label, so a code issued for one account was valid for all of them. + +## [0.19.0 - 0.19.6] - 2026-10-10 to 2026-10-21 + +Released versions: `0.19.0` through `0.19.6`. + +### Added + +- Added the `Broadcast` facade to `@arkstack/notifications`: a channel authorization registry (`Broadcast.channel('private-team.{teamId}', authorizer)`) used by every transport, `Broadcast.broadcast()` for arbitrary events, `Broadcast.handleAuth()` for custom auth routes, `Broadcast.clientConfig()`, and `Broadcast.routes()` registering `POST /realtime/auth` and `GET /realtime/config`. +- Added the `firebase-database` realtime transport (Firebase Realtime Database channels with per-user, time-limited grants checked by security rules, plus bounded `prune()`), the signal-only `polling` transport, and the `memory` transport for tests, on both server and client. +- Added device push alongside any streaming transport: `drivers.realtime.push: 'firebase'` pushes notifications sent to a user to their devices, with `push_tokens` for resolving tokens, `.push(false)` and `.devices(tokens)` per send, and the multicast outcome on `result.push`. +- Added self-hosted Pusher-protocol servers (soketi) through `transports.pusher.host` / `port` and the client's `wsHost` / `wsPort`. +- Added `RealtimeClient.onPoll()`, `RealtimeClient.deviceToken()` and `RealtimeClient.transportName` to `@arkstack/realtime`, with `createFirebaseDatabaseTransport`, `createPollingTransport`, `createMemoryTransport` and `emitMemoryEvent`. +- `RealtimeClient.deviceToken({ serviceWorker })` takes a service worker registration or script URL (registered with the Firebase web config as `firebaseConfig`), returns `null` where web push is unsupported, and falls back to the `firebase` config when streaming over another transport. `requestDeviceToken()` issues a token without a client. +- `RealtimeConfig.importModule` loads the transport SDKs (`pusher-js`, `firebase/*`) for every transport and for device tokens, so pages without a bundler can load them from a CDN. Defaults to a dynamic `import()`. The `createPusherTransport`, `createFirebaseTransport`, `createFirebaseDatabaseTransport` and `requestDeviceToken` helpers accept it as an extra argument. +- `transports.pusher.public_host` / `public_port` / `public_use_tls` (`PUSHER_PUBLIC_*`) set the Pusher-protocol server address handed to browsers when it differs from the one the app uses, such as soketi inside Docker or behind a TLS proxy. Each defaults to its server-side counterpart. +- `ark queue:work --queue` accepts a comma-separated priority list (`--queue=webhook,ocr,default`) and the `*` wildcard for every queue on the connection (`--queue=*`, or `--queue=webhook,*` to put some first). `Worker.runNextJob()` and `daemon({ queue })` take a string or a list. `QueueContract.popAny()` backs the wildcard: the `database` driver takes the oldest due job across all queues, and the `redis` driver discovers queues with `SCAN` and works them round-robin. A bare `queue:work` still works only the default queue. + +### Changed + +- The templates' `src/config/notifications.ts` includes the new transports, `realtime.push`, and the Pusher host options. + +### Docs + +- Documented realtime transports, channel authorization, device push, Firebase Realtime Database rules and pruning, polling, and testing with the memory transport in the notifications guide. + +### Fixed + +- `ark queue:work --queue` rejected every value in 0.19.5 ("argument '…' is invalid. Allowed choices are …"): musket reads a trailing `: a, b, c` in an option's description as its allowed choices, and the new `--queue` description had one. The description no longer does, and a test pins that `queue:work` options declare no choices. +- `DotPath` (the key type behind `config()` and `configure()`) is now depth-bounded and stops at built-in object types, so configs holding deep or self-referencing library types (nodemailer transports) no longer fail to typecheck with "circularly references itself". The `@ts-expect-error` workarounds in the H3 template and the notifications config are gone. +- `build:templates` builds the templates one at a time; run in parallel, each template's prebuild rebuilt the shared packages concurrently and the builds clobbered each other. +- Fixed type errors in `@arkstack/auth` (`AuthenticationException` request typing and two tests) and in `@arkstack/common`'s `renderError`, which used the `view()` global without a type for it. +- Express `after` middleware no longer resumes the router once the response has finished. It used to receive the real `next`, so a middleware such as `requestLogger()` sent each request on through the remaining routes after responding, raising "Cannot set headers after they are sent to the client". +- A failing Express `after` middleware exited the process: `Logger.error` exits by default, and the post-response handler didn't pass `exit = false`. +- `UserNotificationCenter.markAllRead()` now marks every unread notification read; it used a single-row `update()` and marked only one. `unreadForUser(user, perPage)` now honours `perPage`, and both filter unread rows with `whereNull('readAt')`. +- `UserNotificationCenter.send()` (and `Notification.db().broadcast()`) now broadcast on the user's channel. They previously sent to the user's push tokens as channels, so nothing was broadcast at all for users without tokens. Apps on the `firebase` transport still reach their users' devices without changes, because device push is on there unless `drivers.realtime.push` is `false`. +- `Notification.db().broadcast()` now returns the builder, so it chains as documented. +- The Pusher auth route registered by `registerAuthRoute()` now answers with HTTP 200 (pusher-js rejects any other status), registers on the H3 driver instead of the Express one, and authorizes through the channel registry. + +## [0.18.0 - 0.18.12] - 2026-08-27 to 2026-09-28 + +Released versions: `0.18.0` through `0.18.12`. + +### Added + +- Added `@arkstack/encryption`, a zero-dependency isomorphic encryption package built on the Web Crypto API, with symmetric encryption, key helpers, end-to-end secure channels and sealed boxes. `@arkstack/common` re-exports it as `Encryption`. +- Added `.priority()`, `.ttl()` and `.collapseKey()` to the realtime notification builder, with defaults under `drivers.realtime.delivery`, so Firebase broadcasts can wake a device and replace earlier ones. +- Added `loadJobs()`, which registers the application's job classes in `queue:work` processes. +- Added `setModelResolver()` to `@arkstack/common`, so `getModel()` resolves models through the configured database integration instead of loading a specific ORM. + +### Changed + +- Moved the Express and H3 drivers into their own `ExpressDriver` and `H3Driver` modules, and streamlined the `TwoFactor` class. +- `queue:work` logs each job's outcome, including whether a failed job will be retried. +- The console works without arkormx installed. +- Scheduled tasks are due for the whole minute they are scheduled in. + +### Fixed + +- Fixed `env()` destroying hexadecimal secrets. A `0x`-prefixed 32-byte key parses as a number, so it was silently replaced by a float keeping roughly its first thirteen digits. `env()` now reads a value as a number only when the number says exactly what was written: `'6379'` is still a number; `'0x1f'`, `'007'`, `'1e5'`, `' 12 '`, `'0b1010'`, `'Infinity'` and `'12345678901234567890'` are now the strings they were written as. +- Fixed `queue:work` workers failing every job with "Job X is not registered", and deleting it without a record. +- `queue:work --stop-when-empty` and `--max-jobs` now take effect. +- Fixed `FormDataMiddleware` option types. + +## [0.17.4 - 0.17.24] - 2026-07-15 to 2026-08-25 + +Released versions: `0.17.4` through `0.17.24`. + +### Added + +- Added `getModelSync()`. +- Added `sendmail` and `ses` mail transports, wider nodemailer config options, and custom realtime and mail notification transports. +- Added mail attachments and queued notifications. +- Added the `resolvePagination()` helper, with optional defaults for it and `perPage()`. +- Added the realtime `apiUrl` config, Pusher channel authorization, and `registerAuthRoute()`. +- Added realtime client events for the Pusher and Firebase transports. +- Added ngrok tunneling to `ark dev`. +- Added resource copying to the tsdown build configuration. + +### Changed + +- Upgraded musket and used its CLI lifecycle for teardown. +- `env()` and `config()` are available globally to the prepare command and every subsystem. +- The console syncs arkormx artifacts during prepare, and generated tsconfigs include `.arkormx/*.d.ts`. + +### Fixed + +- Fixed the mail from-address being undefined for custom transports. +- `registerAuthRoute()` now validates requests. +- `requestLogger()` reports the full request duration. +- `MailNotification.attachment()` and `attachments()` return the notification, so they chain. +- The dev tunnel stays the same across restarts. +- Arkorm boots at every place that needs it. + ## [0.17.3] - 2026-07-14 ### Added @@ -159,7 +299,8 @@ Released versions: `0.12.0`, `0.12.1`, and `0.12.3` through `0.12.37`. - Fixed authentication request synchronization and hydration of bound HTTP requests. - Fixed filesystem disk registration when disk and driver names differ. - Preserved conflicting trait methods and made invalid trait errors clearer. -- Made console builds recover from stale `.arkstack/build` output and ensured config is ready before tsdown runs. +- Made console builds recover from stale `.arkstack/build` output and ensured config is ready before tsdown runs. `rebuildOutput()` in `@arkstack/common` wipes the output directory and runs a build-only tsdown pass. +- Templates clean the output directory on build and prepare, so renamed or removed source files can't leave stale modules behind. - Improved route-loading errors and prevented dot-path config updates from replacing the entire store. ## [0.11.0 - 0.11.6] - 2026-05-28 diff --git a/docs/more/releases.md b/docs/more/releases.md new file mode 100644 index 00000000..268b37b2 --- /dev/null +++ b/docs/more/releases.md @@ -0,0 +1,96 @@ +# Versioning and Releases + +This page is for maintainers. It covers how Arkstack versions its packages and starter templates, and how a release gets from `main` to npm. If you are upgrading an application, read [Upgrading](/guide/upgrading) instead. + +## What gets released + +One release publishes everything in the monorepo that has a public package: + +| Part | Location | Published as | +| ------------------------ | -------------------- | --------------------------------------- | +| Framework packages | `packages/*` | `@arkstack/*` on npm | +| Project generator | `create-arkstack` | `create-arkstack` on npm | +| Starter templates | `templates/*` | Not published; downloaded from GitHub | +| Workspace root and tests | `package.json`, etc. | Private, versioned for bookkeeping only | + +`create-arkstack` downloads the templates straight from the `arkstack-hq/arkstack` repository with giget, keeps the chosen `templates/` folder, and rewrites its `workspace:^` and `catalog:` dependencies to real version ranges. + +## One version for everything + +Arkstack uses a single, lockstep version. Every `@arkstack/*` package, `create-arkstack`, the templates and the workspace root carry the same version number, even when a package has no changes. Applications should therefore keep all `@arkstack/*` dependencies on the same version. + +Lockstep keeps upgrades simple: a project is "on Arkstack 0.22.0", not on a mix of package versions that were never tested together. + +The template version and the package version are the same number. A project scaffolded by `create-arkstack@0.22.0` depends on `^0.22.0` of every `@arkstack/*` package it uses. + +## Choosing the version number + +Arkstack follows [semantic versioning](https://semver.org). While it is below 1.0, the rules are: + +| Bump | When | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Minor (`0.21.x → 0.22.0`) | A new package, driver or template; a new feature that changes what a template contains; any breaking change to a public API, config shape or template file apps are expected to edit. | +| Patch (`0.22.0 → 0.22.1`) | Bug fixes, additive options with safe defaults, dependency bumps, docs. | +| Major (`1.0.0`) | Reserved for the first stable release. | + +Because applications depend on `^0.x.y`, a caret range never crosses a minor version below 1.0. A minor release is the only place where a breaking change may land, and it must come with an entry in the [upgrade guide](/guide/upgrading). + +Prefer deprecating over removing. When an API is renamed, keep the old name working and mark it deprecated in a minor release, as `Hook.set()` was when `Hook.on()` arrived in 0.20.0, then remove it in a later minor release. + +## Before releasing + +1. Make sure CI on `main` is green. `ci.yml` runs lint and the full test suite against PostgreSQL on Node 22, 24 and 25. +2. Release changes that templates depend on promptly. `create-arkstack` always downloads the templates from the tip of `main`, while the dependencies it writes point at the last published version. A template on `main` that uses a package API that hasn't been published yet produces broken new projects until the next release. +3. Bump the third-party versions in the `catalog` of `pnpm-workspace.yaml` if the release needs newer versions of resora, kanun, arkormx, clear-router or others. Templates and `create-arkstack` read their versions from there. +4. Update `CHANGELOG.md`: rename the `[Unreleased]` section to `[x.y.z] - YYYY-MM-DD` and start a new, empty `[Unreleased]` above it. Keep `docs/more/changelog.md`, the published copy, identical to it. +5. If the release has breaking changes or template changes apps should copy, add a section to [Upgrading](/guide/upgrading). + +Do not edit `version` fields by hand. The release workflow sets them. + +## Publishing a release + +Releases are cut from GitHub: + +1. Create a GitHub release on `main` with a tag named after the version, with no `v` prefix (for example `0.22.0`). +2. Publishing the release starts the **Publish to NPM** workflow (`.github/workflows/publish.yml`). + +The workflow can also be started by hand from the Actions tab with a `version` input, or by pushing a `v*` tag. + +The workflow then: + +1. Checks out the release's target branch. +2. Runs `pnpm prepare:release-version `, which sets `version` in the root, `create-arkstack`, every template and every package, and rewrites the `@arkstack/*` ranges and catalog versions in `create-arkstack/src/data.ts`. +3. Builds the templates, the console and `create-arkstack`, then runs lint, migrations and the full test suite. +4. Commits the version changes as `chore: prepare release v` and pushes them to the branch. +5. Runs `pnpm publish:packages`, which publishes every `@arkstack/*` package and `create-arkstack` with public access. + +If any check fails, nothing is committed or published. Fix the problem on `main`, then run the workflow again from the Actions tab with the same `version`. + +The workflow publishes under npm's default `latest` tag and has no prerelease channel yet. Don't release a prerelease version such as `0.23.0-beta.1` through it, because every user would get it. + +## Releasing by hand + +`scripts/release-order.mjs` works out the publish order from the workspace dependency graph, so dependencies are published before the packages that use them: + +```sh +node scripts/release-order.mjs order # print the order +node scripts/release-order.mjs publish # print the publish commands +node scripts/release-order.mjs publish --run +``` + +Use it only when the workflow cannot run. Outside CI, each package's `prepublishOnly` builds the package first. + +## Recovering from a bad release + +npm only allows unpublishing for a short time after a release, and never when other packages depend on the version. Point `latest` back at the last good version and deprecate the bad one instead: + +```sh +node scripts/release-order.mjs fix --bad 0.22.1 --good 0.22.0 # print the commands +node scripts/release-order.mjs fix --bad 0.22.1 --good 0.22.0 --run +``` + +Then ship the fix as a new patch release. If unpublishing is still possible, `unpublish --bad ` prints the commands in reverse dependency order. + +## Related packages + +resora, kanun, arkormx and clear-router are released from their own repositories on their own version lines. Arkstack picks up their new versions through the `catalog` in `pnpm-workspace.yaml`, and a catalog bump goes in the next Arkstack release like any other change. diff --git a/docs/more/roadmap.md b/docs/more/roadmap.md index fb4399f4..8ac662f9 100644 --- a/docs/more/roadmap.md +++ b/docs/more/roadmap.md @@ -29,6 +29,8 @@ This roadmap tracks Arkstack progress and upcoming priorities. - Removes `src/routes/api.ts` - Removes Prisma and database runtime/dependencies - ✅ Root-level test coverage for shared architecture and command surface +- ✅ [Package versioning and release workflow guidance](/more/releases) +- ✅ [Upgrade guides between template versions](/guide/upgrading) ### In Progress @@ -38,8 +40,6 @@ This roadmap tracks Arkstack progress and upcoming priorities. 1. 🔲 Add a plugin/extension model for driver and console integration 2. 🔲 Ship richer end-to-end command and scaffolding tests -3. 🔲 Publish package-level versioning/release workflow guidance -4. 🔲 Add migration and upgrade guides between template versions ## Future