refactor: attach structured cause to v0 throws#247
Merged
Conversation
|
commit: |
This was referenced May 28, 2026
Adopt Error.cause on the two user-facing throws where a stable
discriminant beats regex-matching the message:
- useContext: cause = { code: 'V0_CONTEXT_MISSING', key }
- useDate: cause = { code: 'V0_PLUGIN_MISSING', plugin: 'createDatePlugin' }
Tooling (devtools panels, error overlays, error trackers) can now
identify v0 errors stably without parsing strings.
Introduce V0ErrorCause as the central registry of every `Error.cause` payload v0 throws, with V0ErrorCode as a convenience alias for the discriminant field. Updated throws use `satisfies V0ErrorCause` so the compiler verifies the discriminant and the payload shape at the call site — typos in 'V0_*' codes no longer compile, and adding a new code requires extending the union (a single place to audit). Consumers gain narrowed payload typing on `err.cause` per code.
…rapping
Drop the `cause: { code, ... }` discriminant pattern. Code lives on a
new V0Error class as a top-level field; Error.cause stays reserved for
wrapping the genuine upstream error so Sentry's LinkedErrors, Datadog,
and Rollbar render the chain correctly (the previous design dropped or
mis-attached the payload because cause-as-data isn't an Error instance).
Modeled on tRPC's TRPCError and Node's error code registry. Discriminated
union (renamed V0ErrorCause -> V0ErrorDetails) is now the constructor
input shape, and exports are surfaced from the package root for typed
consumer catches.
isV0Error(err, code?) collapses the manual instanceof + property checks
into one call and intersects the instance with the matching details arm,
so per-code fields (key, plugin) narrow to required after the guard.
Refs:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
- https://nodejs.org/api/errors.html#nodejs-error-codes
- https://trpc.io/docs/server/error-handling
- https://docs.sentry.io/platforms/javascript/configuration/integrations/linkederrors/
Comply with the project rule that type guards from #v0/utilities replace raw === undefined comparisons (style.md, PHILOSOPHY §2.3). Same-directory sibling import (./helpers) rather than the #v0/utilities barrel to avoid a circular dependency through utilities/index.ts.
…ters Sweep the remaining bare `throw new Error(...)` sites in packages/0/src so isV0Error(err) holds for every error v0 throws. Adds three V0ErrorDetails arms: - V0_PALETTE_INVALID_SEED → ant, leonardo, material seed validation - V0_PALETTE_UNKNOWN_VARIANT → material variant lookup - V0_ADAPTER_INSTANCE_MISSING → Pino/Consola logger adapter constructors Pattern is now uniform: 8/8 throw sites in packages/0/src use V0Error with a code drawn from the typed registry. The only bare Errors that remain are in node_modules / third-party APIs we don't own.
8f3d294 to
27f8130
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every
throwinpackages/0/srcnow uses a typedV0Errorclass with a stablecodediscriminant, so consumers (and error trackers) can identify v0 errors without parsing message strings.useContext— missing providerV0_CONTEXT_MISSING(key)useDate— plugin not installedV0_PLUGIN_MISSING(plugin)palettes/{ant,leonardo,material}— bad seed hexV0_PALETTE_INVALID_SEED(palette,seed)palettes/material— unknown variantV0_PALETTE_UNKNOWN_VARIANT(palette,variant)useLogger/adapters/{pino,consola}— missing instanceV0_ADAPTER_INSTANCE_MISSING(adapter)Design
Pattern modeled on tRPC's
TRPCErrorand Node'serror.coderegistry:V0Error extends Errorwithcode: V0ErrorCodeas a top-level field.Error.causereserved for wrapping the genuine upstream error so Sentry'sLinkedErrorsintegration renders the chain correctly.causeis forwarded throughErrorOptionsand only set when supplied.V0ErrorDetailsdiscriminated union (packages/0/src/types/index.ts) is the source of truth for what codes exist and what payload each carries. Adding a code = extending the union.isV0Error(err, code?)type guard intersects the instance with the matchingV0ErrorDetailsarm, so post-narrowingerr.key/err.plugin/ etc. are required (not optional).Initial design used
cause: { code, ... }as the discriminant payload. Review surfaced that this conflicts with the standard semantic ofError.cause— every precedent (Node, tRPC, Stripe SDK, OpenAI/Anthropic SDKs, Sentry's chain walker) usescausefor upstream wrapping, not as a discriminant. Pivoted before merging.Public surface
Re-exported from
@vuetify/v0:V0Error(class)isV0Error(type guard)V0ErrorCode(union of codes)V0ErrorDetails(discriminated union of constructor inputs)Follow-ups (not in this PR)
eslint-plugin-vuetify) — enforce that newthrowstatements underpackages/0/srcuseV0Error, with documented carve-outs.isV0Errornarrowing), etc.Verification
pnpm typecheck:0— cleanpnpm build:0— cleanpnpm test:run packages/0— 5862 passed / 18 skipped (added 2 cause-shape assertions to existing throw tests)grep "throw new Error" packages/0/src— 0 results outside test/bench files