Skip to content

feat!: read Bastion's { data } envelope and domain codes (1.0.0) - #1

Merged
andreacw5 merged 2 commits into
mainfrom
feat/bastion-contract-v1
Nov 2, 2026
Merged

andreacw5 merged 2 commits into
mainfrom
feat/bastion-contract-v1

Conversation

@andreacw5

Copy link
Copy Markdown
Contributor

What

@heyatom/bastion-client 1.0.0: a client that works against both today's Bastion (1.11.x, bare bodies, domain codes in the error code) and Bastion 2.0 ({ data } / { data, meta } envelope). It is meant to be published and rolled out to every consumer before Bastion changes format, so the Bastion cutover can be rolled back to 1.11 without breaking anyone.

  • BastionHttp.call unwraps a success body whose only key is data (unwrapData). Pages, arrays, other objects and empty bodies pass through. Single exit point, so it covers BastionApiClient, BastionService.call, ServiceTokenProvider and BastionHttpException.
  • listMyEvents returns { data, meta } (new BastionPageMeta) and normalises the pre-2.0 { data, total, page, limit }.
  • BastionErrorCode gains PROVIDER_TAKEN, ALREADY_LINKED, OAUTH_EMAIL_TAKEN, OAUTH_USER_INACTIVE, OAUTH_UNDER_MINIMUM_AGE, AGE_ALREADY_VERIFIED, AGE_GATE_NOT_CONFIGURED, CURRENT_PASSWORD_REQUIRED, TWO_FACTOR_CODE_REQUIRED.
  • Error precedence: a code in message still wins, then Bastion's code when it is a BastionErrorCode, then VALIDATION_FAILED, then message classification as fallback.

Reviewer notes

  • "Bastion's code always wins" would be wrong: an older Bastion sends ALREADY_LINKED under code: CONFLICT, which is also a BastionErrorCode. The message-first order is kept and tested.
  • Three Bastion routes answer { data } alone today (GET /auth/me/tenants, GET / PUT /admin/apps/:slug/…/permissions): through call they now return the inner array. Callers read Array.isArray(res) ? res : res.data. The Bastion 2.0 step must return the bare array on these routes, or they get enveloped twice. No consumer on its default branch calls the two admin routes through call, so bumping doesn't break any.
  • Breaking changes (unwrap, listMyEvents type, err.code for the nine codes, also forwarded by BastionHttpException) are listed in the CHANGELOG. ^0.9.0 consumers don't pick this up until bumped.
  • An independent review was done; its findings are addressed in 73e30b1.

Checks

  • pnpm lint ✅, pnpm test 56/56 ✅, pnpm build && pnpm test:di ✅

Publishing is a separate step: tag v1.0.0 after merge triggers publish.yml.

🤖 Generated with Claude Code

andreacw5 and others added 2 commits November 2, 2026 15:12
Platform plan 03, W1: a client that works against both today's Bastion
and the 2.0 envelope, published before Bastion changes format.

- BastionHttp.call unwraps a body whose only key is `data`; pages,
  arrays and other bodies pass through
- listMyEvents returns { data, meta }, normalising the pre-2.0 page
- Bastion's code is used when it is a BastionErrorCode and the message
  is not itself one; new codes for the W0 domain errors (PROVIDER_TAKEN,
  ALREADY_LINKED, AGE_*, CURRENT_PASSWORD_REQUIRED, ...)
- The plan's "code always wins" was wrong: an older Bastion sends
  ALREADY_LINKED under code CONFLICT, so a code-as-message still wins

BREAKING CHANGE: `call` returns the inner value of a `{ data }`-only
body (e.g. GET /auth/me/tenants); listMyEvents moves the page fields
into `meta`; err.code is the domain code for the codes above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- listMyEvents returns undefined on an empty body instead of throwing
- tests: double envelope, empty events page, two conflicting codes,
  BastionHttpException forwarding a message-only domain code
- CHANGELOG names the three { data }-only routes and the code change
  visible through BastionHttpException

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@andreacw5
andreacw5 merged commit 61aa371 into main Nov 2, 2026
2 checks passed
@andreacw5
andreacw5 deleted the feat/bastion-contract-v1 branch November 2, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant