Skip to content

Commit 9fe76da

Browse files
hyochanclaude
andauthored
docs: cross-link wrapper SDKs + doc pages to openiap.dev API references (#107)
## Summary - Add doc comments on every public OpenIAP API across all wrapper SDKs (apple, google, react-native-iap, expo-iap, flutter, kmp, godot) linking to www.openiap.dev/docs/apis/<symbol>; full @param/@returns/@throws/@example for the 5 core funcs (initConnection, fetchProducts, requestPurchase, finishTransaction, getAvailablePurchases). Preserves original Apple StoreKit / @platform / @example content. - GraphQL schema descriptions get canonical URLs, propagated to every generated Types file via codegen. - Docs site (~70 API + Type pages) get a one-paragraph iOS/Android behaviour blurb with Apple/Google reference links. - Mobile/tablet UI fixes: CodeBlock badge attached as tab-bar (no overlap with scrolling code), sidebar drawer below sticky nav (no z-index collision with hamburger), sidebar margin-left clamped so the centering offset doesn't shove it right at 768–1400px. ## Test plan - [x] `swift build` — packages/apple - [x] `./gradlew :openiap:compilePlayDebugKotlin` — packages/google - [x] `./gradlew :library:compileKotlinMetadata` — kmp-iap - [x] `bun run lint:tsc` — expo-iap - [x] `yarn typecheck` — react-native-iap (pre-existing example-expo errors unrelated) - [x] `dart analyze lib` — flutter_inapp_purchase - [x] `bun run build` — packages/docs (vite + tsc) - [ ] Visual QA: mobile sidebar opens, drawer items clickable, code block badge stays inside the tab bar at narrow widths - [ ] Visual QA: tablet 768–1300px viewport — sidebar stays at left edge, content fills the rest 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Massive cross-SDK docs refresh: clearer API semantics, platform-specific behavior notes, event-driven purchase flow guidance (listener-delivered outcomes), timing/acknowledgement rules, deprecation guidance (prefer verifyPurchase), expanded examples, and many OpenIAP/Apple/Google reference links. * **Style** * Docs UI improvements: revamped code-block header/tabs and sidebar/responsive layout for better navigation. * **Bug Fixes** * Adjusted fetchProducts default behavior: omitting type now defaults to in-app products. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6bd5d34 commit 9fe76da

110 files changed

Lines changed: 8778 additions & 1660 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This document provides an overview for AI agents working across the OpenIAP mono
1414
| Platform Packages | [`knowledge/internal/04-platform-packages.md`](knowledge/internal/04-platform-packages.md) |
1515
| Docs Patterns | [`knowledge/internal/05-docs-patterns.md`](knowledge/internal/05-docs-patterns.md) |
1616
| Git & Deployment | [`knowledge/internal/06-git-deployment.md`](knowledge/internal/06-git-deployment.md) |
17+
| Docs Consistency / SSOT | [`knowledge/internal/07-docs-consistency.md`](knowledge/internal/07-docs-consistency.md) (run `bun audit:docs` before pushing API/Type doc edits) |
1718

1819
## Monorepo Structure
1920

bun.lock

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Docs Consistency Rules — Single Source of Truth (SSOT)
2+
3+
This document captures the consistency rules for OpenIAP documentation, code
4+
comments, and generated types. PR #107 (and earlier rounds) repeatedly
5+
surfaced the same class of drift — the docs claimed one field/default/type,
6+
but the SDK code actually used another. These rules + the companion audit
7+
script (`scripts/audit-docs.ts`) catch those before review.
8+
9+
## Sources of truth
10+
11+
When two places disagree, the upstream wins:
12+
13+
```
14+
GraphQL schema → generated Types → hand-written wrapper SDK → docs page
15+
(packages/gql (libraries/*/src (Swift / Kotlin / (packages/docs/
16+
/src/*.graphql) /types.{ts,kt,...}) Dart / TS / GDScript) src/pages/...)
17+
```
18+
19+
- `packages/gql/src/*.graphql` — schema descriptions ARE the canonical doc
20+
string. Edits propagate via `bun run generate` to every generated
21+
`Types.{ts,kt,swift,dart,gd}`.
22+
- `libraries/*/src/types.ts` (or equivalent) — generated; never hand-edit.
23+
When a docs page mentions a field name, that field MUST exist in the
24+
generated TS type. The audit script enforces this.
25+
- Wrapper SDK source (e.g. `libraries/expo-iap/src/index.ts`) — JSDoc
26+
parameter names MUST match the actual function-signature parameter
27+
names. ESLint rule `tsdoc/syntax` + the audit script catch drift.
28+
- Doc pages — the surface visible to users. Must reflect what each upstream
29+
layer actually exposes.
30+
31+
## Rules
32+
33+
### R1 — JSDoc / KDoc / Dartdoc / Swift `@param` names match the signature
34+
35+
If the function declares `(args) =>`, the JSDoc tag is `@param args …`.
36+
If it declares `(request) =>`, the tag is `@param request …`. Don't carry
37+
over the schema field name (`props`, `params`) when the wrapper destructured
38+
or renamed.
39+
40+
```ts
41+
// ✅ wrapper destructures from `args`
42+
/** @param args Purchase request. … */
43+
export const requestPurchase = async (args) => { … };
44+
45+
// ❌ JSDoc says `props`; signature says `args`
46+
/** @param props*/
47+
export const requestPurchase = async (args) => { … };
48+
```
49+
50+
### R2 — Defaults match across SDKs
51+
52+
If `fetchProducts.type` defaults to `'in-app'` in Flutter / expo-iap /
53+
react-native-iap / godot-iap, then the Apple wrapper must also default to
54+
`.inApp` — and the Apple doc comment must say `.inApp`. The schema
55+
description is the canonical statement.
56+
57+
When changing a default, update:
58+
1. The GraphQL schema description.
59+
2. Re-run `bun run generate`.
60+
3. Every wrapper SDK's `?? <default>` expression and JSDoc / KDoc / etc.
61+
4. Every API doc page (`packages/docs/src/pages/docs/apis/<symbol>.tsx`).
62+
63+
### R3 — Doc pages reference real fields only
64+
65+
When a Type doc page lists fields in a `<table>` or `<ul>`, every field name
66+
MUST exist in the generated `libraries/expo-iap/src/types.ts` (or
67+
`libraries/react-native-iap/src/types.ts` — they're identical in shape).
68+
The audit script greps for fields that don't appear in the type definition
69+
and flags them.
70+
71+
Example failure modes already encountered:
72+
- `BillingProgramAvailabilityResultAndroid` doc listed
73+
`responseCode` + `debugMessage` — neither field exists; the type has
74+
`billingProgram` + `isAvailable`.
75+
- `LaunchExternalLinkParamsAndroid` doc listed `program` + `url` — neither
76+
exists; the type has `billingProgram` + `launchMode` + `linkType` +
77+
`linkUri`.
78+
- `ExternalPurchaseCustomLinkNoticeResultIOS` doc listed `result` +
79+
`noticeType` — neither exists; the type has `continued` + `error`.
80+
81+
### R4 — Enum values listed in docs must exist
82+
83+
When a doc page mentions enum values (e.g.
84+
`'continue' | 'cancelled'`, `.acquisition`, `.services`), they must
85+
appear in the generated enum definition. The audit script extracts string
86+
literals from `<code>'…'</code>` blocks in doc pages and checks them
87+
against the generated TypeScript union types.
88+
89+
`ExternalPurchaseCustomLinkNoticeTypeIOS` is the canonical recent miss —
90+
the union is `'browser'` only, but the doc claimed
91+
`'continue' | 'cancelled' | …`.
92+
93+
### R5 — `<Link to="/docs/...">` targets must resolve
94+
95+
Anchor links should point to existing pages and section anchors. Common
96+
recent failures:
97+
- "Use verifyPurchase" link pointed to `/docs/apis/get-active-subscriptions`
98+
(totally unrelated).
99+
- `getExternalPurchaseCustomLinkTokenIOS` Returns linked to the
100+
`external-purchase-link` page without an anchor — but that page
101+
documents only `ExternalPurchaseNoticeResultIOS`, so users land in the
102+
wrong section. Add a precise `#external-purchase-custom-link-token-result-ios`
103+
anchor on the type page AND link to it.
104+
105+
The audit script crawls every internal `<Link to="/docs/...">` and asserts
106+
the target file (and anchor when given) exists.
107+
108+
### R6 — Native version constraints are honest
109+
110+
`enableBillingProgramAndroid: 'external-payments'` is gated to Play Billing
111+
8.3.0+ (Japan only); the 8.2.0+ programs are `EXTERNAL_CONTENT_LINK` /
112+
`EXTERNAL_OFFER`. A doc page that mixes these up misleads readers about
113+
what works on which SDK.
114+
115+
When you write `<X> 8.2.0+`, you should be able to point to the matching
116+
release-notes line. Don't paraphrase — quote the version requirement
117+
exactly as Google / Apple states it.
118+
119+
### R7 — Code-example snippets compile-check
120+
121+
Code examples in doc pages should at minimum parse / type-check against
122+
the wrapper they target. The audit script does NOT yet run a full
123+
TypeScript / Kotlin / Dart parser, but it does:
124+
- Verify imports (`import {…} from 'expo-iap'`) reference symbols that
125+
expo-iap actually exports.
126+
- Verify field accesses on shown objects (e.g. `purchase.purchaseToken`)
127+
exist on the corresponding generated type.
128+
129+
When in doubt, run the example in a real example app before publishing.
130+
131+
### R8 — Platform-only callouts use the right wrapper
132+
133+
iOS-suffixed APIs (`syncIOS`, `getStorefrontIOS`, …) and Android-suffixed
134+
APIs (`acknowledgePurchaseAndroid`, …) are exposed via every framework
135+
wrapper (expo-iap, rn-iap, kmp-iap, flutter, godot-iap). The TS / Dart /
136+
KMP / GDScript example tabs MUST show how to call the function from each
137+
wrapper, with a `Platform.OS === 'ios'` (or `Platform.isIOS` / etc.)
138+
guard so readers don't accidentally call iOS-only methods on Android.
139+
140+
The native Swift / Kotlin tab keeps the platform-native call. The
141+
wrapper tabs use the suffixed name (`syncIOS()`, etc.) — except in
142+
`packages/google` Kotlin (the Android-only native), where convention
143+
strips the `Android` suffix from method names.
144+
145+
## Pre-commit checklist
146+
147+
Run before every `git push` on docs / SDK changes:
148+
149+
```bash
150+
# 1. Format + lint the docs site
151+
cd packages/docs
152+
bunx prettier --check "src/**/*.{ts,tsx,css}"
153+
bun run lint
154+
155+
# 2. Cross-library typecheck for SDKs you touched
156+
cd libraries/expo-iap && bun run lint:tsc
157+
cd libraries/react-native-iap && yarn typecheck # ignore example-expo errors
158+
cd libraries/flutter_inapp_purchase && dart analyze lib
159+
cd packages/apple && swift build
160+
cd packages/google && ./gradlew :openiap:compilePlayDebugKotlin
161+
162+
# 3. SSOT audit — run the docs-consistency audit script
163+
cd scripts && bun run audit-docs.ts
164+
```
165+
166+
Auto-mode users: the `commit-push-pr` skill runs steps 1 + 2 automatically
167+
before pushing. Step 3 is opt-in until the audit script has zero false
168+
positives in CI.
169+
170+
## Audit script
171+
172+
`scripts/audit-docs.ts` is the executable companion to this guide. It
173+
parses every `/docs/apis/*.tsx` and `/docs/types/*.tsx` page, extracts:
174+
- `<Link to="/docs/...">` targets
175+
- `<code>fieldName</code>` mentions inside Returns / Parameters tables
176+
- String-literal enum values in `<code>'…'</code>` blocks
177+
- `@see {@link openiap.dev/...}` URLs
178+
179+
…and cross-references each against the generated TypeScript types in
180+
`libraries/expo-iap/src/types.ts`. Failures print as a punch-list with the
181+
file, line, and the offending mention.
182+
183+
Run with:
184+
185+
```bash
186+
cd /Users/hyo/Github/hyodotdev/openiap
187+
bun run scripts/audit-docs.ts
188+
```
189+
190+
Exit code 1 means at least one drift; 0 means clean.

0 commit comments

Comments
 (0)