Skip to content

Add internal Checkout Kit telemetry foundations - #674

Open
tiagocandido wants to merge 1 commit into
mainfrom
telemetry/metrics-foundation
Open

Add internal Checkout Kit telemetry foundations#674
tiagocandido wants to merge 1 commit into
mainfrom
telemetry/metrics-foundation

Conversation

@tiagocandido

@tiagocandido tiagocandido commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Define the bounded metric and privacy contract used by Checkout Kit integrations
  • Keep every implementation internal to Checkout Kit:
    • Kotlin lives inside the Android library and is no longer a separate published module
    • Swift uses package visibility and no longer exposes a telemetry library product
    • TypeScript exposes only the Web-facing factory, client interface, and protocol-method normalizer
  • Align metric model naming across Swift, Kotlin, and TypeScript while retaining language-idiomatic lifecycle APIs
  • Standardize OTLP resources:
    • service.name=checkout-kit
    • service.version=<Checkout Kit version>
    • telemetry.sdk.language=java|swift|webjs
    • telemetry.sdk.name=checkout-kit-telemetry
    • telemetry.sdk.version=<Checkout Kit version>
  • Add closed identity dimensions to every metric:
    • product=checkout_kit|accelerated_checkouts|customer_auth
    • platform=android|swift|web|react-native-android|react-native-swift
    • Identity attributes are merged last in all three clients so metric attributes can never shadow them
  • Distinguish retry-attempt errors with the bounded is_retry attribute
  • Define initial main-frame navigation timing and keep-alive connection-loss semantics in the contract
  • Keep bounded buffering, deterministic payloads, backoff, shutdown isolation, and runtime value normalization
  • Let keepalive flushes bypass export backoff and start immediately even while an ordinary export is in flight, so page-terminal flushes are not silently dropped
  • Wire telemetry into CI: a dedicated Telemetry workflow with its own changed-file filter runs the TypeScript typecheck, lint, and tests

Validation

  • TypeScript telemetry typecheck and unit tests
  • Android full unit suite, Detekt, and public API compatibility check
  • Swift full unit test suite
  • Web unit tests and package checks

@tiagocandido
tiagocandido requested a review from a team as a code owner August 18, 2026 15:01
@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Aug 18, 2026
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

React Native — Coverage Report

Lines Statements Branches Functions
Coverage: 91%
90.97% (363/399) 87.29% (213/244) 100% (92/92)

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

Web — Coverage Report

Lines Statements Branches Functions
Coverage: 96%
94.62% (299/316) 81.72% (152/186) 98.71% (77/78)

@github-actions

Copy link
Copy Markdown

Package Size

Platform Artifact Base Head Delta
Web npm tarball 70.6 KiB 70.6 KiB +11 B
React Native npm tarball 107.3 KiB 107.3 KiB 0 B
Android release AAR 279.8 KiB 338.6 KiB +58.8 KiB
Web file breakdown
File Base Head Delta
dist/index.js.map 192.2 KiB 192.2 KiB 0 B
dist/index.d.ts 44.9 KiB 44.9 KiB 0 B
dist/custom-elements.json 44.0 KiB 44.0 KiB 0 B
dist/index.js 31.5 KiB 31.5 KiB 0 B
README.md 19.0 KiB 19.0 KiB 0 B
package.json 2.8 KiB 2.9 KiB +48 B
LICENSE 1.1 KiB 1.1 KiB 0 B
React Native file breakdown
File Base Head Delta
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.ts 85.4 KiB 85.4 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.d.ts 53.0 KiB 53.0 KiB 0 B
android/src/main/java/com/shopify/reactnative/checkoutkit/ShopifyCheckoutKitModule.java 16.0 KiB 16.0 KiB 0 B
ios/ShopifyCheckoutKit.swift 15.9 KiB 15.9 KiB 0 B
ios/AcceleratedCheckoutButtons.swift 14.1 KiB 14.1 KiB 0 B
src/components/AcceleratedCheckoutButtons.tsx 13.0 KiB 13.0 KiB 0 B
src/index.ts 12.7 KiB 12.7 KiB 0 B
lib/commonjs/index.js 12.4 KiB 12.4 KiB 0 B
lib/commonjs/components/AcceleratedCheckoutButtons.js 11.4 KiB 11.4 KiB 0 B
src/index.d.ts 11.1 KiB 11.1 KiB 0 B
lib/commonjs/components/AcceleratedCheckoutButtons.js.map 10.4 KiB 10.4 KiB 0 B
lib/module/index.js 10.4 KiB 10.4 KiB 0 B
lib/module/components/AcceleratedCheckoutButtons.js 10.2 KiB 10.2 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolNotifications.ts 9.5 KiB 9.5 KiB 0 B
lib/module/components/AcceleratedCheckoutButtons.js.map 9.1 KiB 9.1 KiB 0 B
lib/module/index.js.map 8.1 KiB 8.1 KiB 0 B
src/present-dispatcher.ts 8.0 KiB 8.0 KiB 0 B
lib/commonjs/index.js.map 7.9 KiB 7.9 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolNotifications.d.ts 7.6 KiB 7.6 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolRenameMap.ts 7.2 KiB 7.2 KiB 0 B
…and 117 smaller files
Android file breakdown
File Base Head Delta
classes.jar 295.6 KiB 359.5 KiB +63.9 KiB
res/layout/checkout_view_content.xml 2.6 KiB 2.6 KiB 0 B
res/layout/checkout_sheet_content.xml 2.0 KiB 2.0 KiB 0 B
res/values/values.xml 1.3 KiB 1.3 KiB 0 B
R.txt 1.2 KiB 1.2 KiB 0 B
AndroidManifest.xml 922 B 922 B 0 B
proguard.txt 798 B 798 B 0 B
res/drawable/close.xml 431 B 431 B 0 B
res/menu/checkout_menu.xml 354 B 354 B 0 B
META-INF/com/android/build/gradle/aar-metadata.properties 157 B 157 B 0 B

Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report.

@bitrise

bitrise Bot commented Aug 18, 2026

Copy link
Copy Markdown

Install this build

Open Tophat, select your target device, then click Install. Links open on the Mac running Tophat.

SDK Install
React Native Install with Tophat
Swift Install with Tophat
Kotlin Install with Tophat

Checkout Kit E2E results

Status Suite Target Platform OS version tag Device
react-native-ios react-native ios latest iPhone 15
iOS 27 Beta
react-native-android react-native android latest Google Pixel 9
Android 17.0
kotlin-android kotlin android latest Google Pixel 9
Android 17.0
swift-ios swift ios latest iPhone 15
iOS 27 Beta

@kieran-osgood-shopify kieran-osgood-shopify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These foundations are looking good - left a few small comments

I'm interested to see what metrics we integrate and whether we need to look at integrating any metrics in the react native side specifically (wondering if we can detect errors that prevent react-native contacting the native sdks?)


One thing that came up with an AI review was around the sentiment of how the keepAlive flush works on TS - sounds like it might contract the PR bodies statement, but could also be a conscious choice?

   Given:  Request A is active: `{keepalive: false}`
   And:    Metric B is queued
   When:   Pagehide calls `flush({keepalive: true})`
   Then:   no keepalive request is started at that moment
   Instead: B waits for A's promise to settle

Here is the failing test it was verifying with

   it('starts a keepalive export immediately when an ordinary export is active', async () => {
     let resolveFirstFetch: ((value: {ok: boolean}) => void) | undefined;

     const fetch = vi
       .fn()
       .mockImplementationOnce(
         () =>
           new Promise<{ok: boolean}>((resolve) => {
             resolveFirstFetch = resolve;
           }),
       )
       .mockResolvedValue({ok: true});

     const telemetry = createCheckoutKitTelemetryForTesting({
       sdkVersion: '1.2.3',
       fetch,
       now: () => BigInt(1),
     });

     telemetry.recordError({
       category: 'http',
       stage: 'load',
       code: '5xx',
       retryable: true,
     });

     const ordinaryFlush = telemetry.flush();

     telemetry.recordError({
       category: 'protocol',
       stage: 'message',
       code: 'unknown',
       retryable: false,
     });

     const terminalFlush = telemetry.flush({keepalive: true});

     expect(fetch).toHaveBeenCalledTimes(2);
     expect(fetch.mock.calls[0]![1].keepalive).toBe(false);
     expect(fetch.mock.calls[1]![1].keepalive).toBe(true);

     await expect(terminalFlush).resolves.toBe(true);

     resolveFirstFetch?.({ok: true});
     await expect(ordinaryFlush).resolves.toBe(true);
   });

Fixing this would get us to

  ordinary request A active
  metric B queued
  pagehide calls flush({keepalive: true})
  keepalive request B starts immediately

Comment thread .github/workflows/web.yml Outdated

- name: Telemetry package checks
run: |
pnpm --filter @shopify/checkout-kit-telemetry run typecheck

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These scripts seem to be part of the telemetry package not web - think we've been splitting jobs by domain, worth moving these to their own job file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — moved these to a dedicated Telemetry workflow with its own changed-file filter, and dropped the step from the web job. Web still runs its own suite on telemetry changes since the package is bundled into it.

Comment thread dev.yml
typecheck:
desc: "Type-check the TypeScript telemetry package"
run: cd telemetry/languages/typescript && pnpm typecheck
check:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should update all of the dev.yml changes to include kotlin/swift, test / check / lint etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — dev telemetry test now takes typescript/swift/kotlin and runs all three by default, and check aggregates typecheck + lint + tests. On lint: the Swift telemetry sources weren't in SwiftLint's scope at all, so I've added them; Kotlin was already covered by detekt inside the lib, and the TS package now has its own oxlint script.

Comment thread telemetry/README.md
@tiagocandido
tiagocandido force-pushed the telemetry/metrics-foundation branch from 8c0ff67 to e36641b Compare August 19, 2026 13:02
@tiagocandido

Copy link
Copy Markdown
Contributor Author

Good catch on the keepalive path — that serialization was real. A pagehide flush would chain behind an in-flight export, and the page could be gone before it ever started. Keepalive flushes now grab the queued batch and start their own request immediately (ordinary flushes still serialize), and I've added your test pretty much as written. Updated the description bullet to match.

On react-native: today RN metrics come entirely from the native SDKs (reported as react-native-swift / react-native-android), so anything that fails before the bridge reaches native is invisible. Catching that would need a small JS-side recorder — I think it's worth exploring as a follow-up once we see what the native metrics surface in practice.

Comment on lines +16 to +17
# The TypeScript telemetry package is a member of the web pnpm
# workspace, so dependency install and filtered runs happen there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This broke my brain for a second - not introduced by your PR but that workspace file shouldn't sit in the web package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

#gsd:50662 Rebase Checkout Kit on UCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants