Add X-YVP-Sdk header and additionalHeaders override#237
Conversation
🦋 Changeset detectedLatest commit: 21e3152 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
cameronapak
left a comment
There was a problem hiding this comment.
Everything looks good to me. I'm considering the script and if it's needed, but... nothing blocking. Good work!
There was a problem hiding this comment.
thinking about this. Everything else is perfecto!
There was a problem hiding this comment.
I asked DeepWiki... What do you think @Kyleasmth ?
Is this script necessary? A simpler alternative would be importing the version directly from package.json:
// packages/core/src/version.ts
import pkg from '../package.json' with { type: 'json' };
export const SDK_VERSION = pkg.version;The bundler inlines the version at build time — no script, no regex, no release-pipeline coupling. This is the pattern most SDKs use (Stripe, Sentry, AWS, etc.).
The current approach has a few fragile bits:
- The regex in scripts/stamp-sdk-version.mjs:29 is tied to the exact source formatting of version.ts — changing quotes, adding as const, or reformatting could break it.
version.ts is left dirty on disk after a local pnpm release run (already noted in the Greptile summary). - Requires turbo build --force to bust the cache from the prior unstamped build.
- Implicit ordering dependency: only works because Changesets bumps packages/core/package.json before release runs.
Stamping would be justified if the version had to land somewhere a bundler can't reach (a Markdown file, a non-JS asset), but version.ts is plain TS consumed by the same bundler as the rest of core — so the JSON import should work cleanly and removes ~50 lines of release-pipeline glue.
Replace the fragile stamp-sdk-version.mjs script with tsup's define plugin, which injects the version from package.json at build time. This approach: - Eliminates regex fragility - Removes the need for --force rebuilds - Matches the pattern used by Stripe, Sentry, AWS SDKs - Simplifies the release pipeline The version is injected at build time and falls back to 'Dev' at runtime for test environments.
d96d92c to
21e3152
Compare
Summary
X-YVP-Sdk: ReactSDK={version}on every API call alongsideX-YVP-App-Key, so the data team can attribute traffic to this SDK.packages/core/src/version.ts). Local/dev builds keepDevso they're filterable in the data lake; thereleasescript stamps the precise version frompackages/core/package.jsonbefore the publishing build runs.additionalHeadersprop onYouVersionProvider(andadditionalHeadersfield onApiConfig) merges into every request and overrides built-in headers on key collision. This unblocks the React Native Expo wrapper Cameron flagged on Teams — they can swapX-YVP-Sdk: ReactSDK=...forX-YVP-Sdk: ReactNativeSDK={their version}.Implementation notes
ApiClient.defaultHeaders(the main HTTP path) andYouVersionAPI.addStandardHeaders(the static helper, used by the PKCE auth flow). Both call sites that previously setX-YVP-App-Keynow also setX-YVP-Sdk.config.additionalHeadersis spread last intodefaultHeaders, so caller-supplied values win.YouVersionContext→useBibleClient/useLanguageClient/useHighlights. Provider memoizes the headers object via JSON.stringify so inline object literals don't churn the hook memos.releasescript, withturbo build --forceto bypass the cache from the workflow's earlier (unstamped) build.Known scope gap
Auth requests via
SignInWithYouVersionPKCEuse the staticYouVersionPlatformConfiguration, notApiConfig. They sendX-YVP-Sdkcorrectly but don't honor a provider-leveladditionalHeadersoverride. Out of scope for this ticket auth is typically an OS-level redirect for the React Native case anyway.Test plan
pnpm typecheckclean across all packagespnpm lintclean across all packagespnpm test— 510 tests pass (includes 5 new core cases and 1 new hooks case)pnpm dev:web, open the Next.js example, hit any Bible API call, verify in DevTools → Network:X-Yvp-Sdk: ReactSDK=DevX-Yvp-App-Key: <your key>additionalHeaders={{ 'X-YVP-Sdk': 'ReactNativeSDK=test' }}to<YouVersionProvider>in the example app, confirm header value swaps on the wirenode scripts/stamp-sdk-version.mjsreadspackages/core/package.jsonversion and rewritespackages/core/src/version.ts. The first published version after merge should land with the real semver instead ofDev.Changeset
.changeset/x-yvp-sdk-header.md— minor bump for core / hooks / ui (kept in lockstep perfixed:config).Greptile Summary
This PR adds
X-YVP-Sdk: ReactSDK={version}to every API request for traffic attribution and introduces anadditionalHeadersescape hatch on bothApiConfigandYouVersionProviderso SDK wrappers (e.g. the React Native Expo SDK) can override that header with their own identifier.packages/core/src/version.tsis a new file that reads the version directly frompackage.jsonat build time and exports the header name, SDK name, and a builder function; a release-time stamping script is responsible for keepingpackage.jsonin sync before the publish build.additionalHeadersis spread last intoApiClient.defaultHeaders, so caller-supplied values win on key collision; the same field is threaded throughYouVersionContextto all three hook-built clients (useBibleClient,useLanguageClient,useHighlights).YouVersionProviderstabilises the headers object reference via auseMemokeyed on a sortedJSON.stringify, preventing unnecessaryApiClientreconstructions from inline object literals.Confidence Score: 5/5
The change is additive and backward-compatible: new headers and an optional override prop with no breaking changes to existing API surfaces.
All three hook-built clients consistently thread additionalHeaders through context; the memoisation approach correctly prevents spurious ApiClient reconstructions; tests cover both the header-on-the-wire path and the override semantics. The only open item (PKCE auth path not honouring additionalHeaders) is explicitly documented as out of scope.
No files require special attention; packages/core/src/version.ts is the only new file and its behaviour is well-covered by the existing test suite.
Important Files Changed
Sequence Diagram
sequenceDiagram participant App participant YouVersionProvider participant YouVersionContext participant Hooks as useBibleClient/useHighlights/useLanguageClient participant ApiClient participant API as YouVersion API App->>YouVersionProvider: additionalHeaders prop YouVersionProvider->>YouVersionProvider: "stableAdditionalHeaders = useMemo(sorted JSON.stringify key)" YouVersionProvider->>YouVersionContext: "value { additionalHeaders: stableAdditionalHeaders }" YouVersionContext->>Hooks: context.additionalHeaders Hooks->>ApiClient: "new ApiClient({ additionalHeaders })" ApiClient->>ApiClient: "defaultHeaders = { Content-Type, X-YVP-App-Key, X-YVP-Sdk, ...additionalHeaders }" ApiClient->>API: GET/POST/DELETE + merged headers note over ApiClient,API: X-YVP-Sdk: ReactSDK={version} (or override value) note over App,YouVersionProvider: PKCE auth path (out of scope) App->>YouVersionAPI: addStandardHeaders(url) YouVersionAPI->>API: Request with X-YVP-Sdk (no additionalHeaders support)Prompt To Fix All With AI
Reviews (4): Last reviewed commit: "YPE-2295: Import SDK version from packag..." | Re-trigger Greptile