diff --git a/.claude/commands/audit-code.md b/.claude/commands/audit-code.md index eec5a8ad0..7d278d903 100644 --- a/.claude/commands/audit-code.md +++ b/.claude/commands/audit-code.md @@ -73,7 +73,7 @@ Check each package against internal rules AND latest API capabilities: - `packages/apple/Sources/` - iOS/macOS Swift code - `packages/google/openiap/src/{main,play,horizon,amazon}/` - Android Kotlin code -- `packages/gql/src/` - GraphQL schema (API definitions) +- `specs/client/src/` - GraphQL schema (API definitions) **Rules to check (from knowledge/internal/):** @@ -86,7 +86,7 @@ Check each package against internal rules AND latest API capabilities: Compare current implementation against latest platform APIs: -**Google Play Billing (check packages/gql/src/api-android.graphql):** +**Google Play Billing (check specs/client/src/api-android.graphql):** | Feature | Version | Check | | -------------------------------------- | ------- | -------------------------------------- | @@ -101,7 +101,7 @@ Compare current implementation against latest platform APIs: | Opt-in price increase in-app messages | 9.0 | showInAppMessagesAndroid implemented? | | Billing Choice | 9.1 | Info, dialog, and choice type wired? | -**StoreKit 2 (check packages/gql/src/api-ios.graphql):** +**StoreKit 2 (check specs/client/src/api-ios.graphql):** | Feature | Version | Check | | ------------------------------ | ---------------------------------- | ---------------------------------------- | @@ -165,7 +165,7 @@ packages/google (Kotlin): - [ ] Play, Horizon, and Amazon flavors compile - [ ] Shared code is store-agnostic; Play-only APIs stay in `src/play` -packages/gql (GraphQL): +specs/client (GraphQL): - [ ] Async operations have `# Future` comment - [ ] Generated types are not manually edited diff --git a/.claude/commands/audit-iapkit.md b/.claude/commands/audit-iapkit.md index 4839e7708..b01056ad4 100644 --- a/.claude/commands/audit-iapkit.md +++ b/.claude/commands/audit-iapkit.md @@ -44,7 +44,7 @@ does not decide the question (product positioning, support claims). Never ```bash # Spec and SDK movement since the kit surface was last reviewed. -git log --oneline -20 -- packages/gql/src/type.graphql openiap-versions.json +git log --oneline -20 -- specs/client/src/type.graphql openiap-versions.json # Least recently reviewed kit files first — that is where drift concentrates. for f in $(git ls-files packages/kit/src/pages/docs/sections packages/kit/src/content); do echo "$(git log -1 --format='%ad' --date=short -- "$f") $f" diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md index a56a7b7d9..a0a671bcb 100644 --- a/.claude/commands/commit.md +++ b/.claude/commands/commit.md @@ -18,13 +18,13 @@ Complete workflow: branch → commit → push → PR - `--push` or `-p`: Push to remote after commit - `--pr`: Create PR after push - `--all` or `-a`: Commit all changes at once -- ``: Commit only specific path (e.g., `packages/gql`) +- ``: Commit only specific path (e.g., `specs/client`) ## Examples ```bash # Full workflow: commit gql spec, push, create PR -/commit packages/gql/src/*.graphql --pr +/commit specs/client/src/*.graphql --pr # Commit all and create PR /commit --all --pr @@ -93,7 +93,7 @@ git checkout -b feat/ - `flutter` → flutter_inapp_purchase - `godot` → godot-iap - `kmp` → kmp-iap -- `gql` → packages/gql +- `gql` → specs/client - `apple` → packages/apple - `google` → packages/google - `docs` → packages/docs @@ -110,13 +110,13 @@ git diff --name-only **GQL schema only (FIRST COMMIT):** ```bash -git add packages/gql/src/*.graphql +git add specs/client/src/*.graphql ``` **Generated types (SECOND COMMIT):** ```bash -git add packages/gql/src/generated/ +git add specs/client/src/generated/ ``` **Specific path:** @@ -252,7 +252,7 @@ gh pr edit --add-label "," - Changes to `packages/apple/` → `📱 iOS` - Changes to `packages/google/` → `🤖 android` - Changes to `packages/docs/` → `📖 documentation` -- Changes to `packages/gql/` → `⬡ gql` +- Changes to `specs/client/` → `⬡ gql` - Changes to `libraries/react-native-iap/` → `react-native-iap` - Changes to `libraries/expo-iap/` → `expo-iap` - Changes to `libraries/flutter_inapp_purchase/` → `flutter-iap` @@ -274,8 +274,8 @@ When making cross-package changes, commit in this order: | Order | Path | Description | | ----- | ----------------------------- | ---------------------------------------- | -| 1 | `packages/gql/src/*.graphql` | GraphQL schema ONLY (no generated types) | -| 2 | `packages/gql/src/generated/` | Generated types (after schema review) | +| 1 | `specs/client/src/*.graphql` | GraphQL schema ONLY (no generated types) | +| 2 | `specs/client/src/generated/` | Generated types (after schema review) | | 3 | `packages/apple/` | iOS implementation | | 4 | `packages/google/` | Android implementation | | 5 | `packages/docs/` | Documentation updates | @@ -286,13 +286,13 @@ When making cross-package changes, commit in this order: ```bash # Stage ONLY .graphql files (not generated/) -git add packages/gql/src/*.graphql +git add specs/client/src/*.graphql # Verify - should only show .graphql files git diff --cached --name-only -# packages/gql/src/type-android.graphql -# packages/gql/src/type-ios.graphql -# packages/gql/src/type.graphql +# specs/client/src/type-android.graphql +# specs/client/src/type-ios.graphql +# specs/client/src/type.graphql # Commit schema changes git commit -m "feat(gql): add new types..." @@ -376,7 +376,7 @@ Co-Authored-By: Claude Opus 4.5 ## Changes -### GraphQL Schema (packages/gql) +### GraphQL Schema (specs/client) - `WinBackOfferInputIOS` - Win-back offer input type - `ProductStatusAndroid` - Product fetch status enum @@ -417,9 +417,9 @@ Co-Authored-By: Claude Opus 4.5 ```bash # Full workflow from main git checkout -b feat/my-feature -git add packages/gql/src/*.graphql +git add specs/client/src/*.graphql git commit -m "feat(gql): add new types" -git add packages/gql/src/generated/ +git add specs/client/src/generated/ git commit -m "chore(gql): regenerate types" git add packages/apple/ git commit -m "feat(apple): implement new types" diff --git a/.claude/commands/resolve-issue.md b/.claude/commands/resolve-issue.md index b2eee2500..94bc4cea6 100644 --- a/.claude/commands/resolve-issue.md +++ b/.claude/commands/resolve-issue.md @@ -63,7 +63,7 @@ gh issue edit $ISSUE_NUMBER --repo hyodotdev/openiap --add-label ",- | Package | Commands | |---------|----------| -| `packages/gql/` | `cd packages/gql && bun run test` | +| `specs/client/` | `cd specs/client && bun run test` | | `packages/docs/` | `cd packages/docs && bun run lint && bun run typecheck` | | `packages/apple/` | `cd packages/apple && swift build` | | `packages/google/` | `cd packages/google && ./gradlew :openiap:compilePlayDebugKotlin && ./gradlew :openiap:compileHorizonDebugKotlin && ./gradlew :openiap:compileAmazonDebugKotlin` | diff --git a/.claude/commands/review-pr.md b/.claude/commands/review-pr.md index 07510e9cd..6576773f7 100644 --- a/.claude/commands/review-pr.md +++ b/.claude/commands/review-pr.md @@ -17,13 +17,13 @@ Review and address PR review comments for this repository. Based on changed files, run these checks BEFORE committing: -| Package | Commands | -| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `scripts/agent/` | `cd scripts/agent && bun test` | -| `packages/gql/` | `cd packages/gql && bun run test` | -| `packages/docs/` | `cd packages/docs && bun run lint && bun run typecheck` | -| `packages/apple/` | `cd packages/apple && swift build` | -| `packages/google/` | `cd packages/google && ./gradlew :openiap:compilePlayDebugKotlin && ./gradlew :openiap:compileHorizonDebugKotlin && ./gradlew :openiap:compileAmazonDebugKotlin` | +| Package | Commands | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `scripts/agent/` | `cd scripts/agent && bun test` | +| `specs/client/` | `cd specs/client && bun run test` | +| `packages/docs/` | `cd packages/docs && bun run lint && bun run typecheck` | +| `packages/apple/` | `cd packages/apple && swift build` | +| `packages/google/` | `cd packages/google && ./gradlew :openiap:compilePlayDebugKotlin && ./gradlew :openiap:compileHorizonDebugKotlin && ./gradlew :openiap:compileAmazonDebugKotlin` | **Important:** For Android, test Play, Horizon, and Amazon flavors. diff --git a/.claude/commands/verify-all.md b/.claude/commands/verify-all.md index 5a5034775..7c71b76db 100644 --- a/.claude/commands/verify-all.md +++ b/.claude/commands/verify-all.md @@ -35,7 +35,7 @@ type sync target, or GQL root operation is not covered by the parity audit. set -euo pipefail # Regenerate the schema SSOT, run codegen tests, and sync every wrapper first. -(cd packages/gql && bun run generate && bun run test) +(cd specs/client && bun run generate && bun run test) # Docs formatting, typecheck, and production bundle (cd packages/docs && bun run format:check && bun run build) @@ -193,12 +193,12 @@ Verify the manifest-owned generated graph and cross-SDK contracts: ```bash set -euo pipefail -(cd packages/gql && bun run test) +(cd specs/client && bun run test) bun run audit:parity ``` The GQL suite derives source/target paths from -`packages/gql/generated-sync-manifest.mjs`; do not add a hard-coded file loop. +`specs/client/generated-sync-manifest.mjs`; do not add a hard-coded file loop. Also verify `COMMON_ERROR_CODE_MAP` in react-native-iap and expo-iap includes all ErrorCode entries: @@ -312,7 +312,7 @@ the complete platform matrix in step 1. ```bash set -euo pipefail -(cd packages/gql && bun run generate && bun run test) +(cd specs/client && bun run generate && bun run test) (cd packages/docs && bun run format:check && bun run build) (cd packages/apple && swift test) (cd packages/google && ./gradlew \ diff --git a/.claude/guides/01-overview.md b/.claude/guides/01-overview.md index f5e6b0930..cb78c4273 100644 --- a/.claude/guides/01-overview.md +++ b/.claude/guides/01-overview.md @@ -9,24 +9,27 @@ openiap/ ├── packages/ │ ├── apple/ # iOS/macOS library (Swift, StoreKit 2) │ ├── google/ # Android library (Kotlin, Play Billing) -│ ├── gql/ # GraphQL schema & type generation │ ├── docs/ # Documentation site (React/Vite) │ └── kit/ # Hosted receipt-validation SaaS (kit.openiap.dev) +├── specs/ +│ ├── client/ # Client GraphQL contract & type generation +│ └── commerce-protocol/ # Server-side Commerce Protocol ├── scripts/ # Monorepo-wide automation ├── .github/workflows/ # CI/CD workflows ├── AGENTS.md # Canonical shared agent guidelines └── openiap-versions.json # Version management ``` -## Package Responsibilities +## Directory Responsibilities -| Package | Purpose | Language | Output | -| -------- | --------------------------------------------------------- | ---------------- | ----------------------------- | -| `apple` | iOS/macOS IAP implementation | Swift | CocoaPods, SPM | -| `google` | Android IAP implementation | Kotlin | Maven Central | -| `gql` | Type definitions & generation | TypeScript | Swift, Kotlin, Dart, TS types | -| `docs` | Documentation website | React/TypeScript | Vercel deployment | -| `kit` | Hosted receipt-validation SaaS (free, MIT, self-hostable) | TypeScript | Fly.io app (`openiap-kit`) | +| Directory | Purpose | Language | Output | +| --------------------------------- | ----------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------- | +| `packages/apple` | iOS/macOS IAP implementation | Swift | CocoaPods, SPM | +| `packages/google` | Android IAP implementation | Kotlin | Maven Central | +| `specs/client` | Client contract and generated types | GraphQL | Swift, Kotlin, Dart, TS types; package name `@hyodotdev/openiap` | +| `specs/commerce-protocol` | Server-side Commerce Protocol contract, bindings, and conformance | GraphQL | Package name `openiap-commerce-protocol` | +| `packages/docs` | Documentation website | React/TypeScript | Vercel deployment | +| `packages/kit` | Hosted receipt-validation SaaS (free, MIT, self-hostable) | TypeScript | Fly.io app (`openiap-kit`) | ## Version Management @@ -36,7 +39,7 @@ All versions are tracked in `openiap-versions.json`: { "apple": "1.2.x", "google": "1.2.x", - "gql": "1.2.x" + "spec": "1.2.x" } ``` diff --git a/.claude/guides/04-apple-package.md b/.claude/guides/04-apple-package.md index 75acbd8c3..dbe0365ba 100644 --- a/.claude/guides/04-apple-package.md +++ b/.claude/guides/04-apple-package.md @@ -59,7 +59,7 @@ Types.swift is auto-generated from GraphQL schema. ```bash # From the monorepo root: generate all languages and sync manifest targets -cd packages/gql && bun run generate +cd specs/client && bun run generate ``` ## Version Management diff --git a/.claude/guides/06-gql-package.md b/.claude/guides/06-gql-package.md index 834191161..62bdaaca9 100644 --- a/.claude/guides/06-gql-package.md +++ b/.claude/guides/06-gql-package.md @@ -1,12 +1,12 @@ -# GQL Package Guide +# OpenIAP Client Specification Guide -The GraphQL package's canonical instructions live in: +The client specification's canonical instructions live in: -- `packages/gql/CONVENTION.md` — schema organization, marker/deprecation +- `specs/client/CONVENTION.md` — schema organization, marker/deprecation contracts, supported generation commands, and generated-file rules. - `knowledge/internal/04-platform-packages.md` — platform sync and SDK parity. - `knowledge/internal/07-docs-consistency.md` — documentation and generated API SSOT requirements. Do not duplicate those rules here. Read all three before changing -`packages/gql/`, then use the repository-owned `bun run generate` workflow. +`specs/client/`, then use the repository-owned `bun run generate` workflow. diff --git a/.claude/guides/08-deployment.md b/.claude/guides/08-deployment.md index cc2b8da42..c2aaffdf1 100644 --- a/.claude/guides/08-deployment.md +++ b/.claude/guides/08-deployment.md @@ -22,4 +22,4 @@ This file is a route map, not a second deployment specification. For the rare IAPKit manual fallback, follow the Convex-first sequence in `packages/kit/README.md#deployment-convex--flyio`. IAPKit has its own Convex -schema and is not part of the `packages/gql` generated-type sync chain. +schema and is not part of the `specs/client` generated-type sync chain. diff --git a/.claude/guides/09-kit-package.md b/.claude/guides/09-kit-package.md index c9a3eec3c..565316c61 100644 --- a/.claude/guides/09-kit-package.md +++ b/.claude/guides/09-kit-package.md @@ -6,14 +6,14 @@ Hosted receipt-validation SaaS at [kit.openiap.dev](https://kit.openiap.dev) — `packages/kit` is the only package in this monorepo that is **a deployable application, not a publishable library**. Treat it differently: -| Aspect | apple/google/gql/docs | kit | -| ------------------- | --------------------------- | ------------------------------------------------------ | -| Output | Library / static site | Running SaaS (Fly.io machine) | -| Type SSOT | `packages/gql` (GraphQL IR) | Independent Convex schema | -| Deploy trigger | Tagged release | `main` push (paths-filtered to `packages/kit/**`) | -| GQL type-sync chain | Yes | **No** — kit does not consume `@hyodotdev/openiap-gql` | -| `private: true` | Mixed | Yes — never publish to npm | -| User-facing brand | `openiap-*` | `IAPKit` (managed by OpenIAP) | +| Aspect | apple/google/client spec/docs | kit | +| ---------------------- | ----------------------------------- | ------------------------------------------------------------------ | +| Output | Library / static site | Running SaaS (Fly.io machine) | +| Type SSOT | `specs/client` (GraphQL IR) | Independent Convex schema | +| Deploy trigger | Tagged release | `main` push, paths-filtered (see `push.paths` in `deploy-kit.yml`) | +| Client type-sync chain | Yes | **No** — only a type import of `kit-api` via the MCP server | +| `private: true` | Mixed | Yes — never publish to npm | +| User-facing brand | `openiap-*` | `IAPKit` (managed by OpenIAP) | ## Internal Layout @@ -56,7 +56,7 @@ Sanctioned exception: an operator-only `internalMutation` may stay in ## Pre-commit Gate (Paths-Aware, CI-Equivalent) -The monorepo-root husky hook (`.husky/pre-commit`) runs the **full CI-equivalent gate** when staged changes touch `packages/kit/**`: `bun install --frozen-lockfile`, lint (tsc + eslint), prettier check, vitest, and `smoke:server` (Bun compile + boot probe). Mirrors the `verify` job in `deploy-kit.yml` exactly so issues that only surface on CI's fresh install are caught locally. ~15-20s on warm checkouts, ~30-60s after a clean install. +The monorepo-root husky hook (`.husky/pre-commit`) runs the **CI-equivalent gate** (the `verify` job's install, lint, format, test, and smoke steps; dependency audit, coverage, Docker, and Trivy stay CI-only) when staged changes touch `packages/kit/**`, `packages/mcp-server/**`, or `specs/commerce-protocol/**`: `bun install --frozen-lockfile`, lint (tsc + eslint), prettier check, vitest, the Commerce Protocol suite, `smoke:server` (Bun compile + boot probe), and the MCP server lint and tests. Mirrors the `verify` job in `deploy-kit.yml` plus the Commerce Protocol suite from `ci.yml`, so issues that only surface on CI's fresh install are caught locally. ~15-20s on warm checkouts, ~30-60s after a clean install. If the hook fails, fix the underlying issue and re-stage; never bypass with `--no-verify`. @@ -67,8 +67,8 @@ GitHub Actions secrets (set on `hyodotdev/openiap`): | Secret | Real secret? | Purpose | | ------------------------- | ------------ | -------------------------------------------------------- | | `KIT_FLY_API_TOKEN` | ✅ yes | `flyctl deploy` auth — keep private | -| `KIT_CONVEX_DEPLOY_KEY` | ✅ yes | Convex function deploy (optional — step skips if absent) | -| `VITE_KIT_CONVEX_URL` | ⚠️ public | Build arg for SPA — visible in deployed JS bundle | +| `KIT_CONVEX_DEPLOY_KEY` | ✅ yes | Convex function deploy (required — deploy fails without) | +| `VITE_KIT_CONVEX_URL` | ⚠️ public | Resolved by `convex deploy`, not a stored secret | | `VITE_KIT_SENTRY_DSN` | ⚠️ public | Build arg for SPA (optional) | | `VITE_KIT_MIXPANEL_TOKEN` | ⚠️ public | BuildKit secret for SPA (optional, analytics opt-in) | @@ -86,7 +86,7 @@ Monorepo `.vscode/launch.json` has a single kit entry: **🧰 Kit: Dev (Vite + H ## When You Touch Kit -- Stay paths-aware. The deploy workflow only fires on `packages/kit/**` changes. +- Stay paths-aware. `deploy-kit.yml` redeploys kit on `main` pushes that match its `push.paths`: `packages/kit/**`, `packages/mcp-server/**`, `specs/commerce-protocol/**`, the workflow itself, the security-tool installer, and the workspace manifests, because the kit binary embeds the MCP web entry and the Commerce Protocol artifacts. - Add new env vars to `.env.example` first (template), then `.env.local` (dev) and `.env.production` (manual prod fallback). For `VITE_KIT_*` vars, also update the Docker build-time injection in `Dockerfile`, the `Deploy` step in `deploy-kit.yml`, and the GitHub secrets. Use BuildKit secrets for TOKEN-named public SPA values to avoid Docker secret-name warnings. - For server-runtime-only secrets (Stripe / Resend / GitHub OAuth), use the Convex dashboard, not these files. - Keep dashboard text English-only. Inline string literals; do not reintroduce i18next. diff --git a/.codex/skills/loop-review/SKILL.md b/.codex/skills/loop-review/SKILL.md index 59b0b681b..563be0583 100644 --- a/.codex/skills/loop-review/SKILL.md +++ b/.codex/skills/loop-review/SKILL.md @@ -121,7 +121,7 @@ Require `$e2e-tests` when the diff touches any of: - `packages/apple/`, `packages/google/`, or `packages/kit/`; - any `libraries//` implementation, example app, or podspec/gradle/csproj manifest; -- `packages/gql/src/*.graphql` or the generated types synced from it; +- `specs/client/src/*.graphql` or the generated types synced from it; - native build configuration, dependency placement, config plugins, or store metadata for any of the above. diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md index 0e2d6b355..98bc183a0 100644 --- a/.gemini/styleguide.md +++ b/.gemini/styleguide.md @@ -34,7 +34,7 @@ MutationHandlers( ``` In other words, the `...Android` handler key is the generated API surface from -`packages/gql/src/api-android.graphql`; the implementation function it delegates +`specs/client/src/api-android.graphql`; the implementation function it delegates to remains suffix-free inside `packages/google`. ## Generated Files diff --git a/.github/workflows/ci-expo-iap.yml b/.github/workflows/ci-expo-iap.yml index 17a263fe6..03ae12d6c 100644 --- a/.github/workflows/ci-expo-iap.yml +++ b/.github/workflows/ci-expo-iap.yml @@ -5,7 +5,7 @@ on: branches: [main, next] paths: - "libraries/expo-iap/**" - - "packages/gql/src/generated/types.ts" + - "specs/client/src/generated/types.ts" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" @@ -18,7 +18,7 @@ on: branches: [main, next] paths: - "libraries/expo-iap/**" - - "packages/gql/src/generated/types.ts" + - "specs/client/src/generated/types.ts" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" diff --git a/.github/workflows/ci-flutter-inapp-purchase.yml b/.github/workflows/ci-flutter-inapp-purchase.yml index 7b30c791a..d776aeae9 100644 --- a/.github/workflows/ci-flutter-inapp-purchase.yml +++ b/.github/workflows/ci-flutter-inapp-purchase.yml @@ -5,7 +5,7 @@ on: branches: [main, next] paths: - "libraries/flutter_inapp_purchase/**" - - "packages/gql/src/generated/types.dart" + - "specs/client/src/generated/types.dart" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" @@ -18,7 +18,7 @@ on: branches: [main, next] paths: - "libraries/flutter_inapp_purchase/**" - - "packages/gql/src/generated/types.dart" + - "specs/client/src/generated/types.dart" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" diff --git a/.github/workflows/ci-godot-iap.yml b/.github/workflows/ci-godot-iap.yml index 805825457..d63963686 100644 --- a/.github/workflows/ci-godot-iap.yml +++ b/.github/workflows/ci-godot-iap.yml @@ -8,8 +8,8 @@ on: - "scripts/ci/retry-gradle.sh" - "scripts/fetch-godot-lib.sh" - "libraries/godot-iap/**" - - "packages/gql/codegen/plugins/gdscript.ts" - - "packages/gql/src/generated/types.gd" + - "specs/client/codegen/plugins/gdscript.ts" + - "specs/client/src/generated/types.gd" - "openiap-versions.json" - "!**/*.md" push: @@ -19,8 +19,8 @@ on: - "scripts/ci/retry-gradle.sh" - "scripts/fetch-godot-lib.sh" - "libraries/godot-iap/**" - - "packages/gql/codegen/plugins/gdscript.ts" - - "packages/gql/src/generated/types.gd" + - "specs/client/codegen/plugins/gdscript.ts" + - "specs/client/src/generated/types.gd" - "openiap-versions.json" - "!**/*.md" diff --git a/.github/workflows/ci-maui-iap.yml b/.github/workflows/ci-maui-iap.yml index 60d08f107..d7c4b1137 100644 --- a/.github/workflows/ci-maui-iap.yml +++ b/.github/workflows/ci-maui-iap.yml @@ -5,7 +5,7 @@ on: branches: [main, next] paths: - "libraries/maui-iap/**" - - "packages/gql/**" + - "specs/client/**" - "packages/google/**" - "packages/apple/Sources/**" - "packages/apple/wrapper/**" @@ -18,7 +18,7 @@ on: branches: [main, next] paths: - "libraries/maui-iap/**" - - "packages/gql/**" + - "specs/client/**" - "packages/google/**" - "packages/apple/Sources/**" - "packages/apple/wrapper/**" diff --git a/.github/workflows/ci-react-native-iap.yml b/.github/workflows/ci-react-native-iap.yml index 8b6971e07..8e02acb91 100644 --- a/.github/workflows/ci-react-native-iap.yml +++ b/.github/workflows/ci-react-native-iap.yml @@ -5,7 +5,7 @@ on: branches: [main, next] paths: - "libraries/react-native-iap/**" - - "packages/gql/src/generated/types.ts" + - "specs/client/src/generated/types.ts" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" @@ -18,7 +18,7 @@ on: branches: [main, next] paths: - "libraries/react-native-iap/**" - - "packages/gql/src/generated/types.ts" + - "specs/client/src/generated/types.ts" - "packages/apple/Sources/**" - "packages/apple/Package.swift" - "openiap-versions.json" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8d95b7a7..2e43d86fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -166,7 +166,7 @@ jobs: predicate-quantifier: some-with-excludes filters: | gql: - - 'packages/gql/**' + - 'specs/client/**' - 'packages/conformance/**' - 'scripts/**' - 'package.json' @@ -177,25 +177,25 @@ jobs: - '!**/*.md' android: - 'packages/google/**' - - 'packages/gql/**' + - 'specs/client/**' - 'scripts/**' - 'openiap-versions.json' - '.github/workflows/ci.yml' - '!**/*.md' ios: - 'packages/apple/**' - - 'packages/gql/**' + - 'specs/client/**' - 'scripts/**' - 'openiap-versions.json' - '.github/workflows/ci.yml' - '!**/*.md' docs: - 'packages/docs/**' - - 'packages/gql/src/generated/**' - - 'packages/gql/generated-sync-manifest.mjs' + - 'specs/client/src/generated/**' + - 'specs/client/generated-sync-manifest.mjs' # The docs link to the canonical contract and audit its derived # transport constants, so every protocol change must run this job. - - 'specs/openiap-kit/**' + - 'specs/commerce-protocol/**' - 'scripts/audit-docs.ts' - 'scripts/audit-docs.test.ts' - '.github/workflows/ci.yml' @@ -204,7 +204,7 @@ jobs: - 'packages/kit/**' # The kit server embeds the generated protocol artifacts at build # time, so a spec change must rebuild and probe the binary. - - 'specs/openiap-kit/**' + - 'specs/commerce-protocol/**' - 'scripts/e2e-web-sites.mjs' - 'package.json' - 'bun.lock' @@ -237,7 +237,7 @@ jobs: # Compiles the canonical SDL and byte-compares every generated artifact, # so an SDL edit that was not rebuilt cannot ship stale validators. - name: Run spec suite - working-directory: specs/openiap-kit + working-directory: specs/commerce-protocol run: bun run test - name: Verify IAPKit conformance @@ -318,12 +318,12 @@ jobs: - name: Install parity audit dependencies run: | # The root parity scripts use root devDependencies (for example, - # TypeScript) as well as GQL workspace tooling. Install both scopes + # TypeScript) as well as client-spec workspace tooling. Install both scopes # so Node can resolve them without installing unrelated workspaces. for i in 1 2 3; do bun install --frozen-lockfile \ - --filter @hyodotdev/openiap \ - --filter @hyodotdev/openiap-gql && break + --filter openiap-monorepo \ + --filter @hyodotdev/openiap && break [ $i -eq 3 ] && exit 1 echo "Attempt $i failed. Retrying..." sleep 5 @@ -367,7 +367,7 @@ jobs: run: npm run audit:sponsors test-gql: - name: Test GQL Types + name: Test OpenIAP Client Spec runs-on: ubuntu-latest needs: changes if: needs.changes.outputs.gql == 'true' @@ -396,18 +396,18 @@ jobs: done - name: Generate types - working-directory: packages/gql + working-directory: specs/client run: bun run generate - name: Run tests - working-directory: packages/gql + working-directory: specs/client run: bun run test # Breaking-change guard for the schema (knowledge/research backlog R1). # PR-only: after merge the base comparison is no longer actionable. - name: Audit schema semver claims if: github.event_name == 'pull_request' - working-directory: packages/gql + working-directory: specs/client env: BASE_REF: ${{ github.event.pull_request.base.ref }} run: bun run audit:schema-semver -- --base "origin/$BASE_REF" @@ -415,7 +415,7 @@ jobs: - name: Verify generated types are committed (no drift) run: node scripts/assert-clean-worktree.mjs - # test-gql owns regeneration, platform sync, and the clean-worktree drift + # test-gql owns client-spec regeneration, platform sync, and worktree drift # check. Consumer jobs compile the committed copies instead of invoking a # second generator path. test-android: diff --git a/.github/workflows/dependabot-bun-lockfile.yml b/.github/workflows/dependabot-bun-lockfile.yml index 51882ae29..8617ed210 100644 --- a/.github/workflows/dependabot-bun-lockfile.yml +++ b/.github/workflows/dependabot-bun-lockfile.yml @@ -11,6 +11,7 @@ on: - "package.json" - "packages/**/package.json" - "libraries/**/package.json" + - "specs/**/package.json" permissions: contents: read diff --git a/.github/workflows/deploy-kit.yml b/.github/workflows/deploy-kit.yml index aa07249a6..b6fe2da3a 100644 --- a/.github/workflows/deploy-kit.yml +++ b/.github/workflows/deploy-kit.yml @@ -17,7 +17,7 @@ on: # compiled binary embeds its generated artifacts (the route manifest, # error map, and capability descriptor), so a spec change must redeploy # kit or the served /commerce/v1 surface silently diverges from the spec. - - "specs/openiap-kit/**" + - "specs/commerce-protocol/**" - ".github/workflows/deploy-kit.yml" - "scripts/install-security-tool.sh" - "bun.lock" @@ -26,10 +26,14 @@ on: paths: - "packages/kit/**" - "packages/mcp-server/**" + # MCP compiles against the portable kit-api contract. These inputs run + # verification on PRs but do not trigger a production deploy by themselves. + - "specs/client/src/kit-api.ts" + - "specs/client/package.json" # Runtime dependency of the kit binary (see the push trigger above); its # generated artifacts are compiled in, so a spec change both runs kit's # conformance test and, on main, redeploys. - - "specs/openiap-kit/**" + - "specs/commerce-protocol/**" - "codecov.yml" - ".github/workflows/deploy-kit.yml" - "scripts/install-security-tool.sh" @@ -254,6 +258,7 @@ jobs: # to `bun install --filter @hyodotdev/openiap-kit`. Without this, # `COPY package.json bun.lock ./` in the Dockerfile can't find # the lockfile — it lives at root, not under packages/kit/. + working-directory: ${{ github.workspace }} env: FLY_API_TOKEN: ${{ secrets.KIT_FLY_API_TOKEN }} VITE_KIT_SENTRY_DSN: ${{ secrets.VITE_KIT_SENTRY_DSN }} diff --git a/.github/workflows/release-apple.yml b/.github/workflows/release-apple.yml index 17f78dde6..818d9a3f2 100644 --- a/.github/workflows/release-apple.yml +++ b/.github/workflows/release-apple.yml @@ -282,7 +282,7 @@ jobs: git add openiap-versions.json packages/*/openiap-versions.json git add packages/apple/Sources/OpenIapGeneratedVersion.swift git add packages/docs/src/generated/version-metadata.json - git add packages/gql/package.json packages/docs/package.json packages/google/package.json packages/apple/package.json + git add specs/client/package.json packages/docs/package.json packages/google/package.json packages/apple/package.json if git diff --staged --quiet; then echo "No version changes to commit" diff --git a/.github/workflows/release-commerce-protocol.yml b/.github/workflows/release-commerce-protocol.yml index 1cf242dc9..0f35e2a1b 100644 --- a/.github/workflows/release-commerce-protocol.yml +++ b/.github/workflows/release-commerce-protocol.yml @@ -91,7 +91,7 @@ jobs: done - name: Run protocol tests - working-directory: specs/openiap-kit + working-directory: specs/commerce-protocol run: bun run test - name: Verify IAPKit reference conformance @@ -99,7 +99,7 @@ jobs: run: bunx vitest run convex/commerce/spec.conformance.test.ts server/api/commerce/ - name: Inspect the published package - working-directory: specs/openiap-kit + working-directory: specs/commerce-protocol run: npm pack --dry-run deploy: @@ -109,9 +109,6 @@ jobs: actions: write contents: write runs-on: ubuntu-latest - defaults: - run: - working-directory: specs/openiap-kit env: RELEASE_BRANCH: ${{ github.ref_name }} outputs: @@ -144,6 +141,7 @@ jobs: - name: Bump version id: bump + working-directory: specs/commerce-protocol env: VERSION_TYPE: ${{ inputs.version }} IS_PRERELEASE: ${{ inputs.prerelease }} @@ -247,7 +245,7 @@ jobs: run: | gh auth setup-git TAG="openiap-commerce-protocol-$VERSION" - git add specs/openiap-kit/package.json + git add specs/commerce-protocol/package.json git commit -m "chore(release): openiap-commerce-protocol@$VERSION" git tag -a "$TAG" -m "Release $TAG" git push --atomic origin "HEAD:$RELEASE_BRANCH" --follow-tags @@ -308,6 +306,11 @@ jobs: env: VERSION: ${{ steps.bump.outputs.version }} run: | + TAG="openiap-commerce-protocol-$VERSION" + SPEC_PATH="specs/commerce-protocol/SPEC.md" + if ! git cat-file -e "$TAG:$SPEC_PATH"; then + SPEC_PATH="specs/openiap-kit/SPEC.md" + fi cat > /tmp/release-notes.md < + ## Sponsors

@@ -129,6 +136,7 @@ Supported the project before the OpenIAP sponsor program. [openiap-opencollective]: https://opencollective.com/openiap [openiap-paypal]: https://www.paypal.me/dooboolab [openiap-company-contact]: mailto:hyo@hyo.dev + ## Contributing diff --git a/SECURITY.md b/SECURITY.md index 22e9540f9..503c73936 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,7 +22,8 @@ otherwise. This policy covers everything in this monorepo, including: -- The OpenIAP specification and generated types (`packages/gql`) +- Both OpenIAP specifications (`specs/client` and + `specs/commerce-protocol`) and their generated artifacts - Native packages (`packages/apple`, `packages/google`) - Framework SDKs under `libraries/` - IAPKit server and dashboard (`packages/kit`), including the community diff --git a/bun.lock b/bun.lock index a8383e81c..78cd59253 100644 --- a/bun.lock +++ b/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "@hyodotdev/openiap", + "name": "openiap-monorepo", "dependencies": { "fast-uri": "^3.1.7", "qs": "^6.16.0", @@ -19,7 +19,7 @@ "name": "@hyodotdev/openiap-ios", "version": "3.4.0", "dependencies": { - "@hyodotdev/openiap-gql": "workspace:*", + "@hyodotdev/openiap": "workspace:*", }, }, "packages/conformance": { @@ -74,19 +74,7 @@ "name": "@hyodotdev/openiap-android", "version": "3.5.0", "dependencies": { - "@hyodotdev/openiap-gql": "workspace:*", - }, - }, - "packages/gql": { - "name": "@hyodotdev/openiap-gql", - "version": "3.4.0", - "devDependencies": { - "@graphql-codegen/add": "^6.0.0", - "@graphql-codegen/cli": "^6.0.0", - "@graphql-codegen/typescript": "^5.0.0", - "graphql": "^16.11.0", - "typescript": "^5.9.2", - "vitest": "^4.1.5", + "@hyodotdev/openiap": "workspace:*", }, }, "packages/kit": { @@ -174,7 +162,7 @@ "openiap-mcp": "./dist/index.js", }, "dependencies": { - "@hyodotdev/openiap-gql": "workspace:*", + "@hyodotdev/openiap": "workspace:*", "@modelcontextprotocol/sdk": "^1.30.0", "zod": "^3.23.8", }, @@ -185,7 +173,19 @@ "vitest": "^4.1.5", }, }, - "specs/openiap-kit": { + "specs/client": { + "name": "@hyodotdev/openiap", + "version": "3.4.0", + "devDependencies": { + "@graphql-codegen/add": "^6.0.0", + "@graphql-codegen/cli": "^6.0.0", + "@graphql-codegen/typescript": "^5.0.0", + "graphql": "^16.11.0", + "typescript": "^5.9.2", + "vitest": "^4.1.5", + }, + }, + "specs/commerce-protocol": { "name": "openiap-commerce-protocol", "version": "0.1.0", "devDependencies": { @@ -480,12 +480,12 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + "@hyodotdev/openiap": ["@hyodotdev/openiap@workspace:specs/client"], + "@hyodotdev/openiap-android": ["@hyodotdev/openiap-android@workspace:packages/google"], "@hyodotdev/openiap-docs": ["@hyodotdev/openiap-docs@workspace:packages/docs"], - "@hyodotdev/openiap-gql": ["@hyodotdev/openiap-gql@workspace:packages/gql"], - "@hyodotdev/openiap-ios": ["@hyodotdev/openiap-ios@workspace:packages/apple"], "@hyodotdev/openiap-kit": ["@hyodotdev/openiap-kit@workspace:packages/kit"], @@ -1990,7 +1990,7 @@ "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], - "openiap-commerce-protocol": ["openiap-commerce-protocol@workspace:specs/openiap-kit"], + "openiap-commerce-protocol": ["openiap-commerce-protocol@workspace:specs/commerce-protocol"], "openiap-conformance": ["openiap-conformance@workspace:packages/conformance"], @@ -2160,7 +2160,7 @@ "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], @@ -2532,8 +2532,6 @@ "@graphql-tools/import/@graphql-tools/utils": ["@graphql-tools/utils@12.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-aMdIo/l+8j4lhamWAf+MWHr+lOW8zArSBKXspqtKHgt5I2JF9LS55KDLUe/L9AiJOV/O6qJJ60hgYydbnJHbxg=="], - "@graphql-tools/import/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - "@graphql-tools/json-file-loader/@graphql-tools/utils": ["@graphql-tools/utils@12.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-aMdIo/l+8j4lhamWAf+MWHr+lOW8zArSBKXspqtKHgt5I2JF9LS55KDLUe/L9AiJOV/O6qJJ60hgYydbnJHbxg=="], "@graphql-tools/load/@graphql-tools/utils": ["@graphql-tools/utils@12.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-aMdIo/l+8j4lhamWAf+MWHr+lOW8zArSBKXspqtKHgt5I2JF9LS55KDLUe/L9AiJOV/O6qJJ60hgYydbnJHbxg=="], @@ -2638,6 +2636,8 @@ "htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "listr2/eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], "load-json-file/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], diff --git a/knowledge/_agent-context/context.md b/knowledge/_agent-context/context.md index aea495a50..2910114f3 100644 --- a/knowledge/_agent-context/context.md +++ b/knowledge/_agent-context/context.md @@ -1,7 +1,7 @@ # OpenIAP Project Context > **Auto-generated shared context for AI assistants** -> Last updated: 2026-09-02T01:55:36.582Z +> Last updated: 2026-09-03T17:49:09.391Z > > Canonical file: `knowledge/_agent-context/context.md` @@ -72,7 +72,7 @@ fun buildModuleAndroid() the schema name exactly, including `Android` when the operation is Android-only. For example, `MutationHandlers.isBillingProgramAvailableAndroid` must be wired in `packages/google` because it is generated from -`packages/gql/src/api-android.graphql`; the hand-written implementation it +`specs/client/src/api-android.graphql`; the hand-written implementation it delegates to should still be suffix-free, such as `isBillingProgramAvailable()`. @@ -289,13 +289,14 @@ openiap/ ├── packages/ │ ├── conformance/ # Behavioral conformance spec, runner, and reports │ ├── docs/ # Documentation (React/Vite/Vercel) -│ ├── gql/ # GraphQL schema & type generation │ ├── google/ # Android library (Kotlin) │ ├── apple/ # iOS/macOS library (Swift) │ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app) │ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp) -├── specs/ # Interoperability specifications -│ └── openiap-kit/ # OpenIAP Commerce Protocol: server-side contract +├── specs/ # Publishable specifications; never deployed services +│ └── openiap/ +│ ├── client/ # Client GraphQL contract + multiplatform code generation +│ └── commerce-protocol/ # Vendor-neutral server-side commerce contract ├── plugins/ │ └── openiap/ # Codex + Claude Code plugin (skills + MCP config) ├── libraries/ # Framework SDK implementations @@ -327,14 +328,14 @@ Keep each project surface under its canonical owner: | Framework SDKs | `libraries//` | | Agent integrations distributed to users | `plugins//` | | Behavioral conformance spec, runner, and reports | `packages/conformance/` | -| Interoperability specifications | `specs//` | +| Specifications, generators, and conformance data | `specs//` | | Repository knowledge | `knowledge/` | | Repository-wide automation | `scripts/` | | Shared editor settings | `.vscode/` | - Never create a root directory that duplicates a child of `packages/`, `libraries/`, or `plugins/`. For example, use `packages/docs/` and - `packages/gql/`, never root `docs/` or `gql/`. + `specs/client/`, never root `docs/` or `gql/`. - Before adding a top-level directory, search for an existing owner and extend it. Add a new root only when no canonical owner fits, and document that owner in this section in the same change. @@ -343,19 +344,34 @@ Keep each project surface under its canonical owner: - Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce the same audit; do not weaken it to permit a duplicate owner. -## Package Responsibilities +### Specification Distribution Boundary -### packages/gql +`specs/` owns contracts and the tools and fixtures that derive portable +artifacts from them. A specification may publish an npm package so consumers +can install its types, schemas, or conformance runner. Publishing that artifact +is distribution, not a service deployment. -**Purpose:** Single source of truth for type definitions. +Nothing under `specs/` is a hosted runtime. Keep Docker, Fly.io, Vercel, and +other service deployment configuration with the implementation under +`packages/` or `libraries/`. A specification must not read production secrets, +own production data, or run a production migration. `bun run audit:layout` +rejects legacy schema ownership under `packages/gql` and service deployment +manifests under `specs/`. -- Contains GraphQL schema defining all OpenIAP types +## Directory Responsibilities + +### specs/client + +**Purpose:** Authored OpenIAP client API contract and multiplatform type +generation. The publishable package name is `@hyodotdev/openiap`. + +- Contains the GraphQL SDL defining the client API and its types - Generates types for: TypeScript, Swift, Kotlin, Dart, GDScript, C# - **RULE:** `Types.swift` / `Types.kt` are AUTO-GENERATED. Never edit directly. ```bash # Regenerate all types -cd packages/gql && bun run generate +cd specs/client && bun run generate ``` Generated files: @@ -367,7 +383,7 @@ Generated files: - GDScript: `src/generated/types.gd` - C#: `src/generated/Types.cs` -### specs/openiap-kit +### specs/commerce-protocol **Purpose:** OpenIAP Commerce Protocol — the vendor-neutral server-side commerce contract: portable operations (verify, status, entitlements, bind, @@ -391,9 +407,9 @@ their generated assembly and is never hand-edited. The SDL uses custom directives for JSON-only constraints and defines `Query` and `Mutation` operation roots for the portable server surface, but no `Subscription` root — the operation surface is bounded request/response, and the compiler rejects a -stream. Keep it outside `packages/gql`: the client SDK API and this -server-side commerce contract have independent owners and generation targets. -Never edit files under `generated/` directly. +stream. The client SDK API and server-side commerce contract are siblings under +the OpenIAP specification owner, but they keep independent schema inventories +and generation targets. Never edit files under `generated/` directly. ### packages/apple @@ -467,11 +483,11 @@ If an app needs immediate push delivery, its authenticated backend owns that policy and transport. ``` -┌─────────────┐ -│ packages/ │ -│ gql │ ──── Generates Types ────┐ -└─────────────┘ │ - ▼ +┌──────────────┐ +│ specs/ │ +│openiap/client│ ──── Generates Types ────┐ +└──────────────┘ │ + ▼ ┌──────────────────────────┐ │ │ ┌─────┴─────┐ ┌───────┴──────┐ @@ -1008,7 +1024,7 @@ The `Types.swift` file in `Sources/Models/` is **auto-generated** from the OpenI ```bash # From the monorepo root: regenerate all languages and sync manifest targets -cd packages/gql && bun run generate +cd specs/client && bun run generate ``` ### Version Management @@ -1025,8 +1041,8 @@ Version is managed in `openiap-versions.json`: **To update GQL types:** -1. Edit the canonical schema under `packages/gql/src/`. -2. Run `cd packages/gql && bun run generate`. +1. Edit the canonical schema under `specs/client/src/`. +2. Run `cd specs/client && bun run generate`. 3. Run `cd packages/apple && swift test` to verify compatibility. `"spec"` must always equal the lower semantic version of `"google"` and @@ -1117,7 +1133,7 @@ format `OpenIAP Spec / openiap-google (requires Play Billing +)`. Upstream-only labels such as `Billing 9.1.0+` do not tell OpenIAP consumers which library release contains the API. -When the GraphQL schema in [`packages/gql`](../../packages/gql) adds or changes an API, the regenerated `types.*` files **declare** the handler but do not **implement** it. Every wrapper library must wire the new API end-to-end or users will see silent nulls, phantom interfaces (GitHub issue #104), or `UnsupportedOperationException` at runtime. +When the GraphQL schema in [`specs/client`](../../specs/client) adds or changes an API, the regenerated `types.*` files **declare** the handler but do not **implement** it. Every wrapper library must wire the new API end-to-end or users will see silent nulls, phantom interfaces (GitHub issue #104), or `UnsupportedOperationException` at runtime. The mechanical guardrail for this checklist is: @@ -1144,7 +1160,7 @@ and fails when: `packages/google` flavor handler bundle (play / horizon / amazon `OpenIapModule.kt`) — the generated resolver interfaces stay green on their own because new bundle fields default to `null` -- generated types or shared TS runtime helpers drift from `packages/gql` +- generated types or shared TS runtime helpers drift from `specs/client` - framework/package version metadata or Godot Android GDAP dependencies drift from the package/version SSOTs @@ -1294,7 +1310,7 @@ The Google package supports **three build flavors**: 1. **DO NOT edit generated files**: `openiap/src/main/java/dev/hyo/openiap/Types.kt` is auto-generated 2. Put reusable Kotlin helpers in `openiap/src/main/java/dev/hyo/openiap/utils/` -3. Run `cd packages/gql && bun run generate` from the monorepo root +3. Run `cd specs/client && bun run generate` from the monorepo root 4. **Test ALL THREE flavors** when making changes to shared code 5. **Never persist local receipt-to-SKU aliases as entitlement identity**: store-specific adapters may cache data for performance or correlate an @@ -1391,12 +1407,12 @@ maps OpenIAP product queries, purchases, restore calls, and fulfillment to results and opt-in add-on subscriptions for selected partners. Do not expose those as generally available OpenIAP features without an end-to-end contract. -### Updating openiap-gql Types and Derived Version +### Updating `@hyodotdev/openiap` Types and the Derived Version 1. Update the canonical schema without directly changing the `spec` version. Native version writers keep `spec` equal to the lower semantic version of `google` and `apple`; sync fails instead of silently repairing drift. -2. Run `cd packages/gql && bun run generate` from the monorepo root. +2. Run `cd specs/client && bun run generate` from the monorepo root. 3. Compile ALL THREE flavors to verify: ```bash ./gradlew :openiap:compilePlayDebugKotlin @@ -1421,7 +1437,7 @@ Kotlin (or Swift) code references the affected symbol. Before committing any change that touches the following surfaces: - `packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt` -- `packages/gql/src/error.graphql` (ErrorCode enum additions — ripples +- `specs/client/src/error.graphql` (ErrorCode enum additions — ripples through every generated `Types.*`) - `packages/apple/Sources/Models/OpenIapError.swift` - `packages/apple/Sources/OpenIapModule.swift` (public function @@ -1477,17 +1493,18 @@ can depend on the new version), then framework libraries in any order. --- -## GQL Package (packages/gql) +## OpenIAP Client Specification (`specs/client`) ### Required Pre-Work Before writing or editing anything, **ALWAYS** review: -- [`packages/gql/CONVENTION.md`](../../packages/gql/CONVENTION.md) +- [`specs/client/CONVENTION.md`](../../specs/client/CONVENTION.md) ### Code Generation Architecture -The GQL package uses two guarded generation lanes over one schema inventory: +The `@hyodotdev/openiap` package uses two guarded generation lanes over one +authored schema inventory: ```text GraphQL Schema (src/*.graphql) @@ -1502,7 +1519,7 @@ GraphQL Schema (src/*.graphql) #### Directory Structure ```text -packages/gql/codegen/ +specs/client/codegen/ ├── index.ts # Main entry point ├── core/ │ ├── types.ts # IR type definitions @@ -1559,7 +1576,7 @@ Each plugin handles language-specific requirements: ### Generating Types ```bash -cd packages/gql +cd specs/client # Generate all platform types bun run generate @@ -1962,20 +1979,21 @@ from issue #206 without duplicating release history across package-local files: 1. Add new entry at the **top** of the `allNotes` array 2. Follow the existing pattern with `id`, `date`, and `element` -3. Use semantic IDs like `gql-1-3-16-apple-1-3-14` +3. Use semantic IDs like `spec-3-4-0-apple-3-4-0` 4. Verify every package version against its source of truth before writing it (see "Release package version verification" below) ```tsx const allNotes: Note[] = [ - // GQL 1.3.16 / Apple 1.3.14 - Jan 26, 2026 + // Client spec 3.4.0 / Apple 3.4.0 - Jan 26, 2026 { - id: "gql-1-3-16-apple-1-3-14", + id: "spec-3-4-0-apple-3-4-0", date: new Date("2026-01-26"), element: ( -

- - 📅 openiap-gql v1.3.16 / openiap-apple v1.3.14 - Feature Description +
+ + 📅 @hyodotdev/openiap v3.4.0 / openiap-apple v3.4.0 - Feature + Description {/* Content here */}
@@ -2154,7 +2172,7 @@ Fix purchase validation error | --------- | ---------------------------------- | | `apple` | `packages/apple` | | `google` | `packages/google` | -| `spec` | `packages/gql` | +| `spec` | `specs/client` | | `docs` | `packages/docs` | | `rn` | `libraries/react-native-iap` | | `expo` | `libraries/expo-iap` | @@ -2489,7 +2507,7 @@ Version ownership is split: - The shared `spec` is always the lower semantic version of `google` and `apple` - Native version writers update their native key and derive `spec` atomically; - sync then verifies the invariant and refreshes `packages/gql/package.json`, + sync then verifies the invariant and refreshes `specs/client/package.json`, `packages/docs/package.json`, and other derived copies - Production docs deployment consumes the derived current `spec`; it must not accept an independently selected spec version @@ -2537,27 +2555,27 @@ When two places disagree, the upstream wins: ``` GraphQL schema → generated Types → hand-written wrapper SDK → docs page -(packages/gql (libraries/*/src (Swift / Kotlin / (packages/docs/ +(specs/client (libraries/*/src (Swift / Kotlin / (packages/docs/ /src/*.graphql) /types.{ts,kt,...}) Dart / TS / GDScript) src/pages/...) ``` -- `packages/gql/schema-files.mjs` — ordered inventory of every production SDL +- `specs/client/schema-files.mjs` — ordered inventory of every production SDL input. Every repository-owned generator imports it directly. Do not add another hard-coded schema list or an unverified external generator manifest. -- `packages/gql/schema-source-utils.mjs` — shared source identity normalization +- `specs/client/schema-source-utils.mjs` — shared source identity normalization and block-string line detection. Metadata extractors must not duplicate this lexical bookkeeping. -- `packages/gql/src/*.graphql` — schema descriptions ARE the canonical doc +- `specs/client/src/*.graphql` — schema descriptions ARE the canonical doc string. Edits propagate via `bun run generate` to every generated `types.ts`, `Types.kt`, `Types.swift`, `types.dart`, `types.gd`, and `Types.cs`. -- `packages/gql/schema-markers.mjs` — the only parser for the SDL comment +- `specs/client/schema-markers.mjs` — the only parser for the SDL comment contracts `# Future` and `# => Union`. Generators and the schema linter must consume it rather than maintaining independent line-state machines. A union wrapper must be a non-root object with at least one field and all fields nullable; operation roots, empty wrappers, and required fields fail generation instead of silently degrading to an object. -- `packages/gql/schema-deprecations.mjs` — the only extractor and validator for +- `specs/client/schema-deprecations.mjs` — the only extractor and validator for canonical deprecation ownership. Standard GraphQL declarations use `@deprecated(reason: ...)`; named types use the project-scoped `@openiapDeprecated(reason: ...)` directive declared in `schema.graphql`. @@ -2577,11 +2595,11 @@ GraphQL schema → generated Types → hand-written wrapper SDK → docs p metadata rather than declarations, so wrapper-variant docs have no generated declaration target there; every language that emits a wrapper or variant declaration must preserve the canonical reason. -- `packages/gql/custom-input-contracts.ts` — typed +- `specs/client/custom-input-contracts.ts` — typed field/type/nullability/default contracts for inputs that custom generators alias or project. The shared IR transformer validates these before any language plugin runs. -- `packages/gql/generated-sync-manifest.mjs` — generated source/target mapping +- `specs/client/generated-sync-manifest.mjs` — generated source/target mapping shared by canonical platform sync and the pre-commit drift guard. - `libraries/*/src/types.ts` (or equivalent) — generated; never hand-edit. When a docs page mentions a field name, that field MUST exist in the @@ -2629,7 +2647,7 @@ When changing a default, update: When a Type doc page lists fields in a `` or `
    `, every field name MUST exist in the canonical generated -`packages/gql/src/generated/types.ts` shape, which is synchronized into Expo +`specs/client/src/generated/types.ts` shape, which is synchronized into Expo and React Native. The audit parses that TypeScript SSOT with the compiler AST and flags fields that do not appear in the declaration. @@ -2966,7 +2984,7 @@ unless the stray file is the intended new value. | Domain | Owner | | ----------------------------------- | --------------------------------------------- | -| Generated type files source→targets | `packages/gql/generated-sync-manifest.mjs` | +| Generated type files source→targets | `specs/client/generated-sync-manifest.mjs` | | Package/spec version floor | `openiap-versions.json` + release-state audit | | API surface parity across languages | `scripts/audit-non-godot-parity.mjs` | | Change→job routing | `scripts/audit-ci-path-filters.mjs` | @@ -6363,33 +6381,9 @@ storage or tooling. --- -# 📁 PROJECT STRUCTURE - -``` -openiap/ -├── packages/ -│ ├── apple/ # iOS/macOS StoreKit 2 (Swift) -│ │ └── Sources/ -│ │ ├── Models/ # Official types -│ │ ├── Helpers/ # Internal helpers -│ │ └── OpenIapModule.swift -│ ├── google/ # Android store implementations (Kotlin) -│ │ └── openiap/src/ -│ │ ├── main/java/dev/hyo/openiap/ # Shared code + generated Types.kt -│ │ ├── play/java/dev/hyo/openiap/ # Google Play Billing -│ │ ├── horizon/java/dev/hyo/openiap/ # Meta Horizon Billing -│ │ └── amazon/java/dev/hyo/openiap/ # Amazon Appstore -│ ├── gql/ # GraphQL schema & type generation -│ └── docs/ # Documentation site -├── knowledge/ # Shared knowledge base -│ ├── internal/ # Project philosophy -│ └── external/ # External API reference -└── scripts/agent/ # RAG agent scripts -``` - -## Key Reminders +# Key Reminders - **packages/apple**: iOS functions MUST end with `IOS` suffix - **packages/google**: DO NOT add `Android` suffix (it's Android-only package) -- **packages/gql**: Types.kt and Types.swift are AUTO-GENERATED, never edit directly +- **specs/client**: Types.kt and Types.swift are AUTO-GENERATED, never edit directly - **Cross-platform functions**: NO platform suffix diff --git a/knowledge/internal/01-naming-conventions.md b/knowledge/internal/01-naming-conventions.md index 34f2dd2af..42eb7d8ac 100644 --- a/knowledge/internal/01-naming-conventions.md +++ b/knowledge/internal/01-naming-conventions.md @@ -54,7 +54,7 @@ fun buildModuleAndroid() the schema name exactly, including `Android` when the operation is Android-only. For example, `MutationHandlers.isBillingProgramAvailableAndroid` must be wired in `packages/google` because it is generated from -`packages/gql/src/api-android.graphql`; the hand-written implementation it +`specs/client/src/api-android.graphql`; the hand-written implementation it delegates to should still be suffix-free, such as `isBillingProgramAvailable()`. diff --git a/knowledge/internal/02-architecture.md b/knowledge/internal/02-architecture.md index 29c1bc575..d717571a7 100644 --- a/knowledge/internal/02-architecture.md +++ b/knowledge/internal/02-architecture.md @@ -10,13 +10,14 @@ openiap/ ├── packages/ │ ├── conformance/ # Behavioral conformance spec, runner, and reports │ ├── docs/ # Documentation (React/Vite/Vercel) -│ ├── gql/ # GraphQL schema & type generation │ ├── google/ # Android library (Kotlin) │ ├── apple/ # iOS/macOS library (Swift) │ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app) │ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp) -├── specs/ # Interoperability specifications -│ └── openiap-kit/ # OpenIAP Commerce Protocol: server-side contract +├── specs/ # Publishable specifications; never deployed services +│ └── openiap/ +│ ├── client/ # Client GraphQL contract + multiplatform code generation +│ └── commerce-protocol/ # Vendor-neutral server-side commerce contract ├── plugins/ │ └── openiap/ # Codex + Claude Code plugin (skills + MCP config) ├── libraries/ # Framework SDK implementations @@ -48,14 +49,14 @@ Keep each project surface under its canonical owner: | Framework SDKs | `libraries//` | | Agent integrations distributed to users | `plugins//` | | Behavioral conformance spec, runner, and reports | `packages/conformance/` | -| Interoperability specifications | `specs//` | +| Specifications, generators, and conformance data | `specs//` | | Repository knowledge | `knowledge/` | | Repository-wide automation | `scripts/` | | Shared editor settings | `.vscode/` | - Never create a root directory that duplicates a child of `packages/`, `libraries/`, or `plugins/`. For example, use `packages/docs/` and - `packages/gql/`, never root `docs/` or `gql/`. + `specs/client/`, never root `docs/` or `gql/`. - Before adding a top-level directory, search for an existing owner and extend it. Add a new root only when no canonical owner fits, and document that owner in this section in the same change. @@ -64,19 +65,34 @@ Keep each project surface under its canonical owner: - Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce the same audit; do not weaken it to permit a duplicate owner. -## Package Responsibilities +### Specification Distribution Boundary -### packages/gql +`specs/` owns contracts and the tools and fixtures that derive portable +artifacts from them. A specification may publish an npm package so consumers +can install its types, schemas, or conformance runner. Publishing that artifact +is distribution, not a service deployment. -**Purpose:** Single source of truth for type definitions. +Nothing under `specs/` is a hosted runtime. Keep Docker, Fly.io, Vercel, and +other service deployment configuration with the implementation under +`packages/` or `libraries/`. A specification must not read production secrets, +own production data, or run a production migration. `bun run audit:layout` +rejects legacy schema ownership under `packages/gql` and service deployment +manifests under `specs/`. -- Contains GraphQL schema defining all OpenIAP types +## Directory Responsibilities + +### specs/client + +**Purpose:** Authored OpenIAP client API contract and multiplatform type +generation. The publishable package name is `@hyodotdev/openiap`. + +- Contains the GraphQL SDL defining the client API and its types - Generates types for: TypeScript, Swift, Kotlin, Dart, GDScript, C# - **RULE:** `Types.swift` / `Types.kt` are AUTO-GENERATED. Never edit directly. ```bash # Regenerate all types -cd packages/gql && bun run generate +cd specs/client && bun run generate ``` Generated files: @@ -88,7 +104,7 @@ Generated files: - GDScript: `src/generated/types.gd` - C#: `src/generated/Types.cs` -### specs/openiap-kit +### specs/commerce-protocol **Purpose:** OpenIAP Commerce Protocol — the vendor-neutral server-side commerce contract: portable operations (verify, status, entitlements, bind, @@ -112,9 +128,9 @@ their generated assembly and is never hand-edited. The SDL uses custom directives for JSON-only constraints and defines `Query` and `Mutation` operation roots for the portable server surface, but no `Subscription` root — the operation surface is bounded request/response, and the compiler rejects a -stream. Keep it outside `packages/gql`: the client SDK API and this -server-side commerce contract have independent owners and generation targets. -Never edit files under `generated/` directly. +stream. The client SDK API and server-side commerce contract are siblings under +the OpenIAP specification owner, but they keep independent schema inventories +and generation targets. Never edit files under `generated/` directly. ### packages/apple @@ -188,11 +204,11 @@ If an app needs immediate push delivery, its authenticated backend owns that policy and transport. ``` -┌─────────────┐ -│ packages/ │ -│ gql │ ──── Generates Types ────┐ -└─────────────┘ │ - ▼ +┌──────────────┐ +│ specs/ │ +│openiap/client│ ──── Generates Types ────┐ +└──────────────┘ │ + ▼ ┌──────────────────────────┐ │ │ ┌─────┴─────┐ ┌───────┴──────┐ diff --git a/knowledge/internal/04-platform-packages.md b/knowledge/internal/04-platform-packages.md index 06fa6dd25..5234db8db 100644 --- a/knowledge/internal/04-platform-packages.md +++ b/knowledge/internal/04-platform-packages.md @@ -17,7 +17,7 @@ The `Types.swift` file in `Sources/Models/` is **auto-generated** from the OpenI ```bash # From the monorepo root: regenerate all languages and sync manifest targets -cd packages/gql && bun run generate +cd specs/client && bun run generate ``` ### Version Management @@ -34,8 +34,8 @@ Version is managed in `openiap-versions.json`: **To update GQL types:** -1. Edit the canonical schema under `packages/gql/src/`. -2. Run `cd packages/gql && bun run generate`. +1. Edit the canonical schema under `specs/client/src/`. +2. Run `cd specs/client && bun run generate`. 3. Run `cd packages/apple && swift test` to verify compatibility. `"spec"` must always equal the lower semantic version of `"google"` and @@ -126,7 +126,7 @@ format `OpenIAP Spec / openiap-google (requires Play Billing +)`. Upstream-only labels such as `Billing 9.1.0+` do not tell OpenIAP consumers which library release contains the API. -When the GraphQL schema in [`packages/gql`](../../packages/gql) adds or changes an API, the regenerated `types.*` files **declare** the handler but do not **implement** it. Every wrapper library must wire the new API end-to-end or users will see silent nulls, phantom interfaces (GitHub issue #104), or `UnsupportedOperationException` at runtime. +When the GraphQL schema in [`specs/client`](../../specs/client) adds or changes an API, the regenerated `types.*` files **declare** the handler but do not **implement** it. Every wrapper library must wire the new API end-to-end or users will see silent nulls, phantom interfaces (GitHub issue #104), or `UnsupportedOperationException` at runtime. The mechanical guardrail for this checklist is: @@ -153,7 +153,7 @@ and fails when: `packages/google` flavor handler bundle (play / horizon / amazon `OpenIapModule.kt`) — the generated resolver interfaces stay green on their own because new bundle fields default to `null` -- generated types or shared TS runtime helpers drift from `packages/gql` +- generated types or shared TS runtime helpers drift from `specs/client` - framework/package version metadata or Godot Android GDAP dependencies drift from the package/version SSOTs @@ -303,7 +303,7 @@ The Google package supports **three build flavors**: 1. **DO NOT edit generated files**: `openiap/src/main/java/dev/hyo/openiap/Types.kt` is auto-generated 2. Put reusable Kotlin helpers in `openiap/src/main/java/dev/hyo/openiap/utils/` -3. Run `cd packages/gql && bun run generate` from the monorepo root +3. Run `cd specs/client && bun run generate` from the monorepo root 4. **Test ALL THREE flavors** when making changes to shared code 5. **Never persist local receipt-to-SKU aliases as entitlement identity**: store-specific adapters may cache data for performance or correlate an @@ -400,12 +400,12 @@ maps OpenIAP product queries, purchases, restore calls, and fulfillment to results and opt-in add-on subscriptions for selected partners. Do not expose those as generally available OpenIAP features without an end-to-end contract. -### Updating openiap-gql Types and Derived Version +### Updating `@hyodotdev/openiap` Types and the Derived Version 1. Update the canonical schema without directly changing the `spec` version. Native version writers keep `spec` equal to the lower semantic version of `google` and `apple`; sync fails instead of silently repairing drift. -2. Run `cd packages/gql && bun run generate` from the monorepo root. +2. Run `cd specs/client && bun run generate` from the monorepo root. 3. Compile ALL THREE flavors to verify: ```bash ./gradlew :openiap:compilePlayDebugKotlin @@ -430,7 +430,7 @@ Kotlin (or Swift) code references the affected symbol. Before committing any change that touches the following surfaces: - `packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt` -- `packages/gql/src/error.graphql` (ErrorCode enum additions — ripples +- `specs/client/src/error.graphql` (ErrorCode enum additions — ripples through every generated `Types.*`) - `packages/apple/Sources/Models/OpenIapError.swift` - `packages/apple/Sources/OpenIapModule.swift` (public function @@ -486,17 +486,18 @@ can depend on the new version), then framework libraries in any order. --- -## GQL Package (packages/gql) +## OpenIAP Client Specification (`specs/client`) ### Required Pre-Work Before writing or editing anything, **ALWAYS** review: -- [`packages/gql/CONVENTION.md`](../../packages/gql/CONVENTION.md) +- [`specs/client/CONVENTION.md`](../../specs/client/CONVENTION.md) ### Code Generation Architecture -The GQL package uses two guarded generation lanes over one schema inventory: +The `@hyodotdev/openiap` package uses two guarded generation lanes over one +authored schema inventory: ```text GraphQL Schema (src/*.graphql) @@ -511,7 +512,7 @@ GraphQL Schema (src/*.graphql) #### Directory Structure ```text -packages/gql/codegen/ +specs/client/codegen/ ├── index.ts # Main entry point ├── core/ │ ├── types.ts # IR type definitions @@ -568,7 +569,7 @@ Each plugin handles language-specific requirements: ### Generating Types ```bash -cd packages/gql +cd specs/client # Generate all platform types bun run generate diff --git a/knowledge/internal/05-docs-patterns.md b/knowledge/internal/05-docs-patterns.md index 682e1751b..2ae1c47ff 100644 --- a/knowledge/internal/05-docs-patterns.md +++ b/knowledge/internal/05-docs-patterns.md @@ -314,20 +314,21 @@ from issue #206 without duplicating release history across package-local files: 1. Add new entry at the **top** of the `allNotes` array 2. Follow the existing pattern with `id`, `date`, and `element` -3. Use semantic IDs like `gql-1-3-16-apple-1-3-14` +3. Use semantic IDs like `spec-3-4-0-apple-3-4-0` 4. Verify every package version against its source of truth before writing it (see "Release package version verification" below) ```tsx const allNotes: Note[] = [ - // GQL 1.3.16 / Apple 1.3.14 - Jan 26, 2026 + // Client spec 3.4.0 / Apple 3.4.0 - Jan 26, 2026 { - id: "gql-1-3-16-apple-1-3-14", + id: "spec-3-4-0-apple-3-4-0", date: new Date("2026-01-26"), element: ( -
    - - 📅 openiap-gql v1.3.16 / openiap-apple v1.3.14 - Feature Description +
    + + 📅 @hyodotdev/openiap v3.4.0 / openiap-apple v3.4.0 - Feature + Description {/* Content here */}
    diff --git a/knowledge/internal/06-git-deployment.md b/knowledge/internal/06-git-deployment.md index 56b611f13..aaf2750b7 100644 --- a/knowledge/internal/06-git-deployment.md +++ b/knowledge/internal/06-git-deployment.md @@ -106,7 +106,7 @@ Fix purchase validation error | --------- | ---------------------------------- | | `apple` | `packages/apple` | | `google` | `packages/google` | -| `spec` | `packages/gql` | +| `spec` | `specs/client` | | `docs` | `packages/docs` | | `rn` | `libraries/react-native-iap` | | `expo` | `libraries/expo-iap` | @@ -441,7 +441,7 @@ Version ownership is split: - The shared `spec` is always the lower semantic version of `google` and `apple` - Native version writers update their native key and derive `spec` atomically; - sync then verifies the invariant and refreshes `packages/gql/package.json`, + sync then verifies the invariant and refreshes `specs/client/package.json`, `packages/docs/package.json`, and other derived copies - Production docs deployment consumes the derived current `spec`; it must not accept an independently selected spec version diff --git a/knowledge/internal/07-docs-consistency.md b/knowledge/internal/07-docs-consistency.md index f516011fc..064a64dca 100644 --- a/knowledge/internal/07-docs-consistency.md +++ b/knowledge/internal/07-docs-consistency.md @@ -12,27 +12,27 @@ When two places disagree, the upstream wins: ``` GraphQL schema → generated Types → hand-written wrapper SDK → docs page -(packages/gql (libraries/*/src (Swift / Kotlin / (packages/docs/ +(specs/client (libraries/*/src (Swift / Kotlin / (packages/docs/ /src/*.graphql) /types.{ts,kt,...}) Dart / TS / GDScript) src/pages/...) ``` -- `packages/gql/schema-files.mjs` — ordered inventory of every production SDL +- `specs/client/schema-files.mjs` — ordered inventory of every production SDL input. Every repository-owned generator imports it directly. Do not add another hard-coded schema list or an unverified external generator manifest. -- `packages/gql/schema-source-utils.mjs` — shared source identity normalization +- `specs/client/schema-source-utils.mjs` — shared source identity normalization and block-string line detection. Metadata extractors must not duplicate this lexical bookkeeping. -- `packages/gql/src/*.graphql` — schema descriptions ARE the canonical doc +- `specs/client/src/*.graphql` — schema descriptions ARE the canonical doc string. Edits propagate via `bun run generate` to every generated `types.ts`, `Types.kt`, `Types.swift`, `types.dart`, `types.gd`, and `Types.cs`. -- `packages/gql/schema-markers.mjs` — the only parser for the SDL comment +- `specs/client/schema-markers.mjs` — the only parser for the SDL comment contracts `# Future` and `# => Union`. Generators and the schema linter must consume it rather than maintaining independent line-state machines. A union wrapper must be a non-root object with at least one field and all fields nullable; operation roots, empty wrappers, and required fields fail generation instead of silently degrading to an object. -- `packages/gql/schema-deprecations.mjs` — the only extractor and validator for +- `specs/client/schema-deprecations.mjs` — the only extractor and validator for canonical deprecation ownership. Standard GraphQL declarations use `@deprecated(reason: ...)`; named types use the project-scoped `@openiapDeprecated(reason: ...)` directive declared in `schema.graphql`. @@ -52,11 +52,11 @@ GraphQL schema → generated Types → hand-written wrapper SDK → docs p metadata rather than declarations, so wrapper-variant docs have no generated declaration target there; every language that emits a wrapper or variant declaration must preserve the canonical reason. -- `packages/gql/custom-input-contracts.ts` — typed +- `specs/client/custom-input-contracts.ts` — typed field/type/nullability/default contracts for inputs that custom generators alias or project. The shared IR transformer validates these before any language plugin runs. -- `packages/gql/generated-sync-manifest.mjs` — generated source/target mapping +- `specs/client/generated-sync-manifest.mjs` — generated source/target mapping shared by canonical platform sync and the pre-commit drift guard. - `libraries/*/src/types.ts` (or equivalent) — generated; never hand-edit. When a docs page mentions a field name, that field MUST exist in the @@ -104,7 +104,7 @@ When changing a default, update: When a Type doc page lists fields in a `
` or `