Upgrade @arki/contracts 0.1.0 to 0.2.0
For @arki/[email protected] · source docs/upgrades/0.1.0-to-0.2.0.md
This records the original 0.2.0 additions. Current callers must also follow the typed ID migration and handle returned cursor failures; the older exception-only cursor examples below describe the superseded API.
What changed
-
Adds prefixed ids declared once and keyset pagination schemas (RFC
docs/rfcs/2026-10-15-dot-feature-builder.md, phase 1c). Both are browser-safe (zod only).defineId('sty_')returns{ prefix, schema, create, is };IdOf<typeof storyId>is`sty_${string}`. The schema is a zod template literal (it exports a JSON Schema pattern);create()returnsprefix + 24cuid2-shaped characters with Web Crypto or an exactIdGenerationFailure; checkisFailurebefore using the ID. Missing/malformed providers returnRandomSourceUnavailable; provider defects returnUnexpectedDefectwith private causes.ID_GENERATION_FAILURESdeclares both definitions. Prefixes are kept exactly as given, so stored ids stay valid.assertUniqueIdPrefixes(...ids)rejects a repeated prefix at compile time and runtime, and a prefix that starts another at runtime.keyset({ createdAt: 'desc', id: 'desc' })declares a list order and its opaque base64url cursor (encode,decode,cursorschema; dates round-trip as dates).pageInput(keyset, { defaultLimit, maxLimit })andpage(itemSchema)are the contract's input and output;@arki/db/paginationbuilds the query from the same declaration.
-
Coded errors and Hermes support. Static definition misuse raises
ContractsError(aTypeError) with a stableARKI_CONTRACTS_E00ncodeand aremediation; error messages start with the code instead of[contracts]. Operational ID and cursor failures are returned. Keyset cursors no longer useTextDecoder, which React Native (Hermes) lacks, so non-ASCII cursors decode there. The package uses the shared library lint preset and a cross-runtime smoke test. -
New
@arki/contracts/orpcentry: one typed identity per oRPC contract procedure.operations(contract)returns a frozen tree with anOperation(path,name,meta, phantom input/output/error types) at every leaf;operationAt(contract, path)finds one;OPERATION('~op'),Identified<Op>andoperationOf(carrier)let client utils carry it. Types are inferred from the contract (OperationOf<C>,InputOf,OutputOf,ErrorOf,MetaOf).@orpc/contractis a new optional peer; the main entry still does not import it.Fix:
pageInput's client input now types the cursor asstring(it wasunknown), becauseKeyset.cursorisz.ZodType<string, string>.The package now runs
test:runtimes(Node, Bun, Deno, browser, Hermes).
Public API changes
Nothing is removed. These are new:
defineId,IdOf,IdDeclaration,assertUniqueIdPrefixes: prefixed ids declared once.keyset,pageInput,page,Page,Keyset,CursorValue: keyset pagination.ContractsError,CONTRACTS_ERROR_CODES,ContractsErrorCode: coded errors.@arki/contracts/orpc:operations,operationAt,operationOf,OPERATION,Identifiedand theOperationtypes. It needs the optional peer@orpc/contract.
Two things changed in behaviour:
- Error messages start with the code (
ARKI_CONTRACTS_E004: ...) instead of[contracts]. pageInput's client input types the cursor asstring(it wasunknown).
Steps
-
Update the package.
bun add @arki/[email protected] -
Stop matching error text. Match the code. A
ContractsErroris still aTypeError, soinstanceof TypeErrorkeeps working.Before:
try { keyset.decode(input); } catch (error) { if (error instanceof Error && error.message.startsWith('[contracts]')) return null; throw error; }After:
import { CONTRACTS_ERROR_CODES, ContractsError } from '@arki/contracts'; try { keyset.decode(input); } catch (error) { if (error instanceof ContractsError && error.code === CONTRACTS_ERROR_CODES.invalidCursor) return null; throw error; } -
If typed client code gave
pageInputa cursor of typeunknown, pass astring(the cursor the last page returned) or leave it out. -
Optional. Use the new tools:
- Declare ids once with
defineId('sty_')instead of writing the prefix in a schema, a type and a generator. - Declare list order once with
keyset({ createdAt: 'desc', id: 'desc' }).@arki/db/paginationbuilds the query from it. - Give each oRPC procedure an identity with
operations(contract)from@arki/contracts/orpc. Install@orpc/contractfirst.
- Declare ids once with
Verify
bun run typecheck && bun run testExpected: the type check ends with no errors, and Vitest reports Test Files ... passed with no failed tests.