Upgrade @arki/dot 0.6.1 to 0.7.0
For @arki/[email protected] · source docs/upgrades/0.6.1-to-0.7.0.md
Everyone who calls .configure(), .boot(), .start() or .stop(), writes plugin() or feature() units, or reads dot CLI output must act; apps that only call defineApp(...).start() and never touch these can skip the plugin steps but still need the first and last steps.
What changed
This is a breaking minor. @arki/dot is below 1.0.
Added, nothing to do:
defineFeature(name)chain builder in@arki/dot/feature, withrouterFor,feature.instance(name, rebind?)and nested.features().@arki/dot/orpc:needsMiddleware,rpc,errorCaptureMiddleware.@arki/dot/runtime:createAppRuntime, one started app per server process, safe under Vite hot updates.dot devcommand. It checks the database, applies migrations and runs Vite.- Every usecase from
defineFeatureruns in an OpenTelemetry span namedfeature.<feature>.<usecase>. $signal(anAbortSignal) in every hook andprovidesfactory context. It aborts when the hook passeshookTimeoutMs.$describeActionhook.dot explainprints action summaries and contract meta.- Failures at compile time: wiring mistakes now fail on the argument, with a
DOT:label. - Dot now runs on Node, Bun, Deno, browsers and Hermes.
Breaking, see the steps:
configure(),boot(),start(),stop()anddispose()return failures instead of throwing.plugin()andfeature()publish throughprovidesfactories.bootreturns nothing.plugin({ version })is gone.- Kernel error codes are renamed to
ARKI_DOT_E001toARKI_DOT_E032. defineFeaturechanges:.needs()takes aliases only,.usecases()takes an object of factories,rpc()takes three arguments.testApp<TServices>()lost its type argument.- Adapter authors:
driver()anddriven()receiveMountedFeature[]. - The
dotCLI uses thearki.cli/1JSON envelope, new exit codes anddot.*failure codes.
Public API changes
Removed:
DotCliError,DotCliErrorCode,dotCliDocsUrl,DotCliEnvelope,DotCliEnvelopeStatus,DotNewEnvelope,main,parseArgsfrom@arki/dot/cli. Useprogram.runExplain,runDoctorandrunNewnow return data or a failure and print nothing.InferredProvides.plugin({ version })andPluginManifest.version.- The
--openapiflag. Use--as openapi. - The single-factory
.usecases(create)form. - The explicit
testApp<TServices>()type argument. - Error code strings
DOT_LIFECYCLE_E0nnandFEATURE_E0nn.
Added: defineFeature, routerFor, handleOf, AnyHandle, Handle, LazyHandle, Handles, Provided, ProvidesFactories, providesOf, servicesOf, resolveNeeds, entrypointsOf, fake, createAppRuntime, needsMiddleware, errorCaptureMiddleware, rpcFailureDefinitions, runDev and its event and failure types, UsecaseFactories, ProcedureFactories, ListGuard.
Behaviour changes: see the steps below.
Steps
-
Rebuild
@arki/dotand every package that emits its types. Then run your typecheck. The compiler will list most breaks below. -
Check failures from the app phases. They no longer throw.
Before:
const manifest = defineApp('shop').use(catalog).configure().manifest; const app = await defineApp('shop').use(catalog).boot(); await app.dispose();After:
import { isFailure } from '@arki/failure'; const configured = defineApp('shop').use(catalog).configure(); if (isFailure(configured)) return configured; // or exit at a process boundary const manifest = configured.manifest; const app = await configured.boot(); if (isFailure(app)) return app; const disposed = await app.dispose(); if (isFailure(disposed)) return disposed;Do the same for
start()andstop(). Hooks returnundefinedor a typed failure.configure()stays synchronous, so aconfigurehook must not return a Promise. Details:docs/upgrade-phases.mdanddocs/upgrade-ownership.md. -
Move published services into
providesfactories. This applies toplugin()andfeature().Before:
plugin({ name: 'database', boot: async () => ({ db: await openDb() }), });After:
plugin({ name: 'database', provides: { db: () => openDb() }, });bootis for side effects only. Abootthat returns a value no longer compiles. Each factory gets the unit's needs and may be async. Consumers now writeneeds: { db: database.provides.db }and get the type fromProvided<typeof database.provides.db>. -
Delete
versionfrom everyplugin()call.Before:
plugin({ name: 'database', version: '1.0.0', provides: { db: () => openDb() } });After:
plugin({ name: 'database', provides: { db: () => openDb() } });The app
versionindefineApp(name, { version })stays.PluginManifest.version, thedot explainVERSION column and thedot.plugin.versionspan attribute are gone too. -
Rename error codes you match on.
DOT_LIFECYCLE_E0nnbecomesARKI_DOT_E0nn(same number).FEATURE_E001toFEATURE_E010becomeARKI_DOT_E022toARKI_DOT_E031.- The constants
DotLifecycleErrorCodeandFEATURE_ERROR_CODESkeep their names.
Before:
if (error.code === 'DOT_LIFECYCLE_E020') { /* ... */ }After:
if (error.code === 'ARKI_DOT_E020') { /* ... */ } -
Fix
testAppandbootTestAppcalls. Drop the type argument. The services are read from the plugins.Before:
const app = testApp<{ db: Db }>([database]);After:
const app = testApp([database]); -
If you use
defineFeature, update the chain:.needs()takes a record of aliases only..needs({ db: database.provides.db }), not a bare handle..usecases()takes an object of factories, one per usecase.- Feature services are published under one key,
<name>.services. Code that read per-service keys of a feature from the services bag must read that key.needsMiddlewarealready does. rpc(contract, router)becomesrpc(contract, implementer, factories).
Before:
.usecases(createUsecases) .entrypoints(rpc(waitlistRpcContract, procedures(os)))After:
.usecases({ signup, markInvited }) .entrypoints(rpc(waitlistRpcContract, rpcImplementer, waitlistProcedures))Put the factory tree in one
entrypoints/procedures.ts, shaped like the contract:export const waitlistProcedures = { signup: (impl: typeof rpcImplementer) => impl.signup.handler(...) }. An extra or missing key does not compile. -
Adapter authors only:
driver(...)anddriven(...)now receiveMountedFeature[]. Read declarations withentrypointsOf(f)and a chain feature's units withf.plugins(). HTTP boundaries and auth policies return typed failure values; update code that inspects legacy strings or expects thrown refusals. Schema parsing and Journal, queue and mail calls now return typed failures too; see@arki/failure/docs/upgrade-typed-validation.md. -
Scripts and agents that read
dotoutput:- Output is the
arki.cli/1envelope{ ok, command, data, error, meta }. It is JSON when stdout is piped. Use--jsonor-o jsonto force it. - Exit codes:
0ok,1bug,2usage error,3returned failure. DOT_CLI_E001toE011are replaced bydot.*codes (dot.app_not_found,dot.doctor_failed, and so on).E004andE005are nowcli.unknown_commandandcli.invalid_input.dot doctorreads.data.status. Error issues returndot.doctor_failedwith the report in.error.data.dot newreturns.data.app,.data.operations[]and.data.nextSteps.- Managed preflight failures return
dot.preflight_failedin.error.data.{app,migrations,report,failure}.
Before:
dot explain --openapiAfter:
dot explain --as openapi - Output is the
Verify
Run this in the consumer app:
bun run typecheck && dot doctor --app <path> --json | jq '.ok, .data.status'It passes when typecheck exits 0, .ok is true and .data.status is "success" (or "warning" with no error issues).