feat(gql): add discriminated union type support - ProductOrSubscription#33
feat(gql): add discriminated union type support - ProductOrSubscription#33
Conversation
Add ProductOrSubscription union type to GraphQL schema and implement discriminated union type narrowing across all platforms (TypeScript, Swift, Kotlin, Dart). Changes: - Add `union ProductOrSubscription = Product | ProductSubscription` to GraphQL schema - Update type generators to handle union of unions pattern - Optimize platform-specific type extraction with nested pattern matching - Fix TypeScript FetchProductsResult to support mixed arrays - Add ProductOrSubscription interface implementation to Product and ProductSubscription TypeScript: - Extend FetchProductsResult type to include `(Product | ProductSubscription)[]` - Fix Query.fetchProducts to use FetchProductsResult type alias Swift: - Use nested pattern matching for direct type extraction - Change `.productSubscription(let sub), case .productSubscriptionIos(let val)` to `.productSubscription(.productSubscriptionIos(let val))` Kotlin: - Add ProductOrSubscription interface implementation with override modifiers - Optimize type extraction to filter platform-specific types directly - Change from wrapper types to direct sealed interface casting Dart: - Add implements clause for Product and ProductSubscription - Handle union of unions in type generation Generator scripts: - Add getInterfaces() type check for union members in all generators - Remove manual ProductOrSubscription post-processing (now auto-generated) - Add post-processing to make unions implement ProductOrSubscription Benefits: - Enables type-safe discriminated union narrowing - Eliminates unnecessary intermediate object creation - Platform-specific code only handles its own types - Better IDE autocomplete and compile-time type safety
|
Warning Rate limit exceeded@hyochan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 15 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughAdds a ProductOrSubscription union and an Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant GraphQL
participant Gen as Generators
participant Runtime as PlatformRuntime
Client->>GraphQL: fetchProducts()
GraphQL-->>Gen: schema includes ProductOrSubscription + FetchProductsResult.all
Gen->>Runtime: generate types (wrapper ProductOrSubscription, FetchProductsResult alias)
Runtime->>Runtime: parse `all` items -> dispatch via __typename -> Product | ProductSubscription wrappers
alt item -> Product
Runtime-->>Client: include Product in all
else item -> ProductSubscription
Runtime-->>Client: include ProductSubscription in all
end
note right of Gen: union-flattening + getInterfaces guards added
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt (1)
286-297: Consider using Kotlin's filterIsInstance for cleaner type filtering.The current casting approach works correctly but could be simplified using Kotlin's built-in type filtering idioms.
Consider this more idiomatic Kotlin approach:
- val allProducts = items.mapNotNull { - (it as? Product)?.let { product -> - if (product is ProductAndroid) product else null - } - } - val allSubs = items.mapNotNull { - (it as? ProductSubscription)?.let { subscription -> - if (subscription is ProductSubscriptionAndroid) subscription else null - } - } + val allProducts = items.filterIsInstance<Product>() + .filterIsInstance<ProductAndroid>() + val allSubs = items.filterIsInstance<ProductSubscription>() + .filterIsInstance<ProductSubscriptionAndroid>()Both approaches are functionally equivalent, but
filterIsInstanceis more concise and idiomatic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/Types.swiftis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**packages/gql/src/generated/types.tsis excluded by!**/generated/**
📒 Files selected for processing (12)
openiap-versions.json(1 hunks)packages/apple/Sources/Models/OpenIapSerialization.swift(1 hunks)packages/apple/Sources/Models/Types.swift(2 hunks)packages/apple/Sources/OpenIapModule+ObjC.swift(1 hunks)packages/apple/Sources/OpenIapStore.swift(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(3 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt(1 hunks)packages/gql/scripts/fix-generated-types.mjs(2 hunks)packages/gql/scripts/generate-dart-types.mjs(2 hunks)packages/gql/scripts/generate-kotlin-types.mjs(2 hunks)packages/gql/scripts/generate-swift-types.mjs(2 hunks)packages/gql/src/type.graphql(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/apple/Sources/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
packages/apple/Sources/**/*.swift: iOS-specific functions must have IOS suffix; cross-platform functions have no suffix
Swift acronym naming: acronyms are ALL CAPS only as a suffix; when at beginning/middle, use Pascal case (e.g., IapManager, ProductIAP)
Files:
packages/apple/Sources/OpenIapStore.swiftpackages/apple/Sources/Models/OpenIapSerialization.swiftpackages/apple/Sources/OpenIapModule+ObjC.swiftpackages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Files:
packages/apple/Sources/Models/OpenIapSerialization.swiftpackages/apple/Sources/Models/Types.swift
packages/google/openiap/src/main/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/openiap/src/main/**/*.kt: In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
packages/apple/Sources/Models/Types.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Do not edit auto-generated Types.swift in Sources/Models/
Files:
packages/apple/Sources/Models/Types.swift
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Applied to files:
packages/apple/Sources/OpenIapStore.swiftpackages/gql/scripts/generate-kotlin-types.mjspackages/apple/Sources/Models/OpenIapSerialization.swiftpackages/gql/scripts/generate-dart-types.mjspackages/apple/Sources/OpenIapModule+ObjC.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/apple/Sources/Models/Types.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-swift-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Helpers/**/*.swift : Place internal helper classes (not official OpenIAP types) under Sources/Helpers/ (e.g., ProductManager.swift, IapStatus.swift)
Applied to files:
packages/apple/Sources/OpenIapStore.swiftpackages/apple/Sources/Models/OpenIapSerialization.swiftpackages/apple/Sources/OpenIapModule+ObjC.swiftpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/**/*.swift : Swift acronym naming: acronyms are ALL CAPS only as a suffix; when at beginning/middle, use Pascal case (e.g., IapManager, ProductIAP)
Applied to files:
packages/apple/Sources/OpenIapStore.swiftpackages/apple/Sources/OpenIapModule+ObjC.swift
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/kotlin/Types.kt : Generated Kotlin types location in GQL package: dist/kotlin/Types.kt
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/src/generated/types.ts : Generated TypeScript types location in GQL package: src/generated/types.ts
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/src/type.graphqlpackages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/fix-generated-types.mjspackages/gql/scripts/generate-swift-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : Do not edit generated file openiap/src/main/Types.kt in Android package
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/swift/Types.swift : Generated Swift types location in GQL package: dist/swift/Types.swift
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/fix-generated-types.mjspackages/gql/scripts/generate-swift-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/CONVENTION.md : Before editing anything in GraphQL Types package, review packages/gql/CONVENTION.md
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/src/type.graphqlpackages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/fix-generated-types.mjspackages/gql/scripts/generate-swift-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/dart/types.dart : Generated Dart types location in GQL package: dist/dart/types.dart
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/src/type.graphqlpackages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/fix-generated-types.mjspackages/gql/scripts/generate-swift-types.mjs
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helpers in openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : Do not edit auto-generated Types.swift in Sources/Models/
Applied to files:
packages/apple/Sources/Models/Types.swiftpackages/gql/scripts/generate-swift-types.mjs
🧬 Code graph analysis (7)
packages/apple/Sources/OpenIapStore.swift (1)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (1)
productSubscription(10-13)
packages/gql/scripts/generate-kotlin-types.mjs (2)
packages/gql/scripts/generate-dart-types.mjs (6)
firstMember(670-670)firstInterfaces(673-673)memberInterfaces(676-676)sharedInterfaceNames(668-668)output(999-999)lines(380-380)packages/gql/scripts/generate-swift-types.mjs (6)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)output(783-783)lines(193-193)
packages/apple/Sources/Models/OpenIapSerialization.swift (1)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (1)
productSubscription(10-13)
packages/gql/scripts/generate-dart-types.mjs (2)
packages/gql/scripts/generate-kotlin-types.mjs (6)
firstMember(635-635)firstInterfaces(638-638)memberInterfaces(641-641)sharedInterfaceNames(633-633)output(803-803)lines(407-407)packages/gql/scripts/generate-swift-types.mjs (6)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)output(783-783)lines(193-193)
packages/apple/Sources/OpenIapModule+ObjC.swift (1)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (1)
productSubscription(10-13)
packages/apple/Sources/Models/Types.swift (1)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (2)
product(6-8)productSubscription(10-13)
packages/gql/scripts/generate-swift-types.mjs (2)
packages/gql/scripts/generate-dart-types.mjs (4)
firstMember(670-670)firstInterfaces(673-673)memberInterfaces(676-676)sharedInterfaceNames(668-668)packages/gql/scripts/generate-kotlin-types.mjs (4)
firstMember(635-635)firstInterfaces(638-638)memberInterfaces(641-641)sharedInterfaceNames(633-633)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (7)
packages/apple/Sources/Models/OpenIapSerialization.swift (1)
215-219: LGTM! Cleaner nested pattern matching.The refactor from two-step pattern matching to direct nested matching simplifies the extraction logic while maintaining the same functionality. This aligns well with the new
ProductOrSubscriptionunion type structure.packages/apple/Sources/OpenIapModule+ObjC.swift (1)
76-80: LGTM! Consistent pattern matching refactor.The nested pattern matching aligns with the changes in
OpenIapSerialization.swiftand properly extracts iOS subscriptions from the new union structure.packages/apple/Sources/OpenIapStore.swift (1)
195-200: LGTM! Correct extraction and type conversion.The nested pattern matching extracts the iOS subscription and properly re-wraps it for the
subscriptionsarray. The re-wrapping is necessary to convert from theProductOrSubscriptionunion context to theProductSubscriptiontype.packages/gql/src/type.graphql (1)
80-88: LGTM! Well-structured union type addition.The new
ProductOrSubscriptionunion and theallfield inFetchProductsResultenable type-safe handling of mixed product/subscription collections. The schema change is well-commented and follows GraphQL conventions.packages/apple/Sources/Models/Types.swift (2)
1002-1005: LGTM! Auto-generated ProductOrSubscription union.The new
ProductOrSubscriptionenum correctly implements the discriminated union defined in the GraphQL schema. As an auto-generated file, these changes properly reflect the schema updates.Based on coding guidelines
249-249: LGTM! FetchProductsResult updated to use ProductOrSubscription.The
allcase now properly uses the newProductOrSubscriptionunion type instead of an inline representation, improving type safety and consistency across platforms.Based on coding guidelines
openiap-versions.json (1)
2-2: Verify the intentionality of the gql version downgrade.The downgrade from 1.2.5 to 1.2.4 is confirmed as an isolated, explicit commit with no accompanying code changes. However, the minimal commit message ("chore: gql to 1.2.4") provides no explanation. Given that:
- New union type functionality typically warrants version increments, not decrements
- The downgrade contradicts standard versioning practices
- No documentation explains why 1.2.5 was reverted
Confirm whether this downgrade is intentional (e.g., due to incompatibility between 1.2.5 and the union type changes, or a bug in 1.2.5) or if it was inadvertent.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/gql/scripts/generate-dart-types.mjs (1)
992-997: Minor: Use correct enum case in productTypeMapping.The mapping uses lowercase enum values (
IapPlatform.ios) that are later corrected via global replacement (lines 1043-1046). While this works, it would be clearer to use the correct PascalCase values directly in the mapping.const productTypeMapping = { - ProductIOS: { platform: 'IapPlatform.ios', type: 'ProductType.inApp' }, - ProductAndroid: { platform: 'IapPlatform.android', type: 'ProductType.inApp' }, - ProductSubscriptionIOS: { platform: 'IapPlatform.ios', type: 'ProductType.subs' }, - ProductSubscriptionAndroid: { platform: 'IapPlatform.android', type: 'ProductType.subs' }, + ProductIOS: { platform: 'IapPlatform.IOS', type: 'ProductType.InApp' }, + ProductAndroid: { platform: 'IapPlatform.Android', type: 'ProductType.InApp' }, + ProductSubscriptionIOS: { platform: 'IapPlatform.IOS', type: 'ProductType.Subs' }, + ProductSubscriptionAndroid: { platform: 'IapPlatform.Android', type: 'ProductType.Subs' }, };Note: Verify if the global replacements at lines 1043-1046 serve other purposes before removing them.
packages/gql/scripts/generate-kotlin-types.mjs (1)
837-854: Post-processing regex replacements are fragile and lack validation.The string replacements assume exact spacing and formatting in generated code. If the generation logic changes or the schema evolves, these could silently fail or match incorrectly.
Consider one of the following improvements:
- Add validation to confirm replacements succeeded:
output = output.replace( /public sealed interface Product : ProductCommon \{/, 'public sealed interface Product : ProductCommon, ProductOrSubscription {' ); +if (!output.includes('Product : ProductCommon, ProductOrSubscription')) { + throw new Error('Failed to add ProductOrSubscription to Product interface'); +}
Generate the implements clause directly in
printUnionorprintDataClassrather than post-processing, by checkingunionMembershipor schema metadata.Use more flexible regex that handles varying whitespace:
-/public sealed interface Product : ProductCommon \{/ +/public\s+sealed\s+interface\s+Product\s*:\s*ProductCommon\s*\{/
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**
📒 Files selected for processing (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(3 hunks)packages/gql/scripts/generate-dart-types.mjs(3 hunks)packages/gql/scripts/generate-kotlin-types.mjs(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/google/openiap/src/main/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/openiap/src/main/**/*.kt: In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/docs/src/**/*.{md,mdx,ts,tsx} : Respect deprecations in documentation: replace buy-promoted-product-ios with requestPurchaseOnPromotedProductIOS; requestProducts with fetchProducts; get-storefront-ios with getStorefront
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/dart/types.dart : Generated Dart types location in GQL package: dist/dart/types.dart
Applied to files:
packages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/src/generated/types.ts : Generated TypeScript types location in GQL package: src/generated/types.ts
Applied to files:
packages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/CONVENTION.md : Before editing anything in GraphQL Types package, review packages/gql/CONVENTION.md
Applied to files:
packages/gql/scripts/generate-dart-types.mjspackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Applied to files:
packages/gql/scripts/generate-dart-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Run platform package tests/builds after updating versions and regenerating types (Android: Gradle; Apple: swift test/build; GQL: generation scripts)
Applied to files:
packages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : Do not edit generated file openiap/src/main/Types.kt in Android package
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helpers in openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/kotlin/Types.kt : Generated Kotlin types location in GQL package: dist/kotlin/Types.kt
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/swift/Types.swift : Generated Swift types location in GQL package: dist/swift/Types.swift
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjs
🧬 Code graph analysis (2)
packages/gql/scripts/generate-dart-types.mjs (2)
packages/gql/scripts/generate-kotlin-types.mjs (11)
firstMember(635-635)firstInterfaces(638-638)memberInterfaces(641-641)sharedInterfaceNames(633-633)concreteMembers(661-661)memberTypes(630-630)nestedMembers(665-665)nestedMembers(682-682)sortedConcreteMembers(676-676)delegateTo(679-679)lines(407-407)packages/gql/scripts/generate-swift-types.mjs (6)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)
packages/gql/scripts/generate-kotlin-types.mjs (2)
packages/gql/scripts/generate-dart-types.mjs (12)
firstMember(670-670)firstInterfaces(673-673)memberInterfaces(676-676)sharedInterfaceNames(668-668)concreteMembers(697-697)memberTypes(665-665)nestedMembers(701-701)nestedMembers(718-718)sortedConcreteMembers(712-712)delegateTo(715-715)lines(380-380)output(1029-1029)packages/gql/scripts/generate-swift-types.mjs (7)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)output(783-783)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (8)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (4)
717-718: LGTM! Clean discriminated union container.FetchProductsResultAll correctly uses
List<ProductOrSubscription>?to represent mixed product/subscription collections, enabling type-safe narrowing in client code.
2152-2164: LGTM! Product correctly implements the union member contract.The dual inheritance (
ProductCommon, ProductOrSubscription) properly represents that Product is both a union variant and shares common fields with ProductSubscription. ThefromJsonrouting on concrete typenames (ProductAndroid,ProductIOS) is correct for GraphQL union deserialization.
2166-2181: LGTM! Discriminated union correctly resolves concrete types.The
ProductOrSubscription.fromJsonimplementation correctly routes based on concrete platform-specific typenames (ProductAndroid,ProductIOS,ProductSubscriptionAndroid,ProductSubscriptionIOS) rather than abstract union names. This ensures GraphQL responses deserialize successfully at runtime.The previously flagged concern about routing only on
"Product"/"ProductSubscription"has been fully addressed—the current implementation matches exactly what was recommended.
2182-2194: LGTM! ProductSubscription mirrors Product's correct pattern.The structure is consistent with
Product: extends bothProductCommonandProductOrSubscription, andfromJsoncorrectly routes on concrete typenames (ProductSubscriptionAndroid,ProductSubscriptionIOS). The discriminated union implementation is complete and type-safe.packages/gql/scripts/generate-dart-types.mjs (2)
671-686: LGTM - Defensive handling of union members without getInterfaces.The runtime type checking correctly handles the "union of unions" scenario where union members may themselves be unions (which don't have a
getInterfacesmethod). The intersection logic properly identifies interfaces shared across all concrete members.
695-726: LGTM - Correct handling of nested union flattening.The logic properly expands nested unions to their concrete members and generates appropriate delegation in the
fromJsonmethod. When a concrete member belongs to a nested union, it correctly delegates to the parent union type rather than directly constructing the concrete type.packages/gql/scripts/generate-kotlin-types.mjs (2)
636-650: LGTM: Proper type guards for union-of-unions.The runtime checks correctly prevent errors when union members are themselves unions (which lack
getInterfaces). The logic ensures shared interfaces are computed only when all members expose the method.
660-690: The verification confirms that concrete types do not currently overlap across nested unions. The schema shows:
ProductOrSubscription(union) containsProduct | ProductSubscription(both unions)Productcontains onlyProductAndroid | ProductIOS(concrete types)ProductSubscriptioncontains onlyProductSubscriptionAndroid | ProductSubscriptionIOS(concrete types)- Similar pattern for
ReceiptValidationResultvariantsEach concrete type belongs to exactly one nested union, so the
delegateTologic at lines 679-688 works correctly. However, the code lacks explicit validation or documentation of this assumption. If the schema evolves to include overlapping unions (e.g., a concrete type in multiple unions), the code would silently pick the first match via the break at line 685, potentially causing incorrect deserialization. Consider adding a comment or compile-time check to document or enforce this constraint.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**
📒 Files selected for processing (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(2 hunks)packages/gql/scripts/generate-dart-types.mjs(3 hunks)packages/gql/scripts/generate-kotlin-types.mjs(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/google/openiap/src/main/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/openiap/src/main/**/*.kt: In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧠 Learnings (11)
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/kotlin/Types.kt : Generated Kotlin types location in GQL package: dist/kotlin/Types.kt
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/src/generated/types.ts : Generated TypeScript types location in GQL package: src/generated/types.ts
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/swift/Types.swift : Generated Swift types location in GQL package: dist/swift/Types.swift
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/CONVENTION.md : Before editing anything in GraphQL Types package, review packages/gql/CONVENTION.md
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/dart/types.dart : Generated Dart types location in GQL package: dist/dart/types.dart
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : Do not edit generated file openiap/src/main/Types.kt in Android package
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Applied to files:
packages/gql/scripts/generate-dart-types.mjspackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helpers in openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧬 Code graph analysis (2)
packages/gql/scripts/generate-kotlin-types.mjs (2)
packages/gql/scripts/generate-dart-types.mjs (14)
firstMember(670-670)firstInterfaces(673-673)memberInterfaces(676-676)sharedInterfaceNames(668-668)concreteMembers(697-697)memberTypes(665-665)nestedMembers(701-701)nestedMembers(718-718)sortedConcreteMembers(712-712)delegateTo(715-715)lines(380-380)output(1028-1028)productOrSubscriptionPattern(1032-1032)replacement(1034-1067)packages/gql/scripts/generate-swift-types.mjs (7)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)output(783-783)
packages/gql/scripts/generate-dart-types.mjs (2)
packages/gql/scripts/generate-kotlin-types.mjs (14)
firstMember(635-635)firstInterfaces(638-638)memberInterfaces(641-641)sharedInterfaceNames(633-633)concreteMembers(661-661)memberTypes(630-630)nestedMembers(665-665)nestedMembers(682-682)sortedConcreteMembers(676-676)delegateTo(679-679)lines(407-407)output(833-833)productOrSubscriptionPattern(837-837)replacement(839-859)packages/gql/scripts/generate-swift-types.mjs (7)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)output(783-783)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test iOS
- GitHub Check: Test Android
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**
📒 Files selected for processing (4)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(2 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt(1 hunks)packages/gql/scripts/generate-dart-types.mjs(4 hunks)packages/gql/scripts/generate-kotlin-types.mjs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt
🧰 Additional context used
📓 Path-based instructions (1)
packages/google/openiap/src/main/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/openiap/src/main/**/*.kt: In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧠 Learnings (11)
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : Only use Android suffix for types that are part of a cross-platform API (e.g., ProductAndroid, PurchaseAndroid)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : Keep official OpenIAP types in Sources/Models/ matching openiap.dev/docs/types naming (e.g., Product.swift, Purchase.swift, ActiveSubscription.swift)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : Do not edit generated file openiap/src/main/Types.kt in Android package
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/**/*.kt : In Android-only package, do not add Android suffix to function names (e.g., acknowledgePurchase, not acknowledgePurchaseAndroid)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/kotlin/Types.kt : Generated Kotlin types location in GQL package: dist/kotlin/Types.kt
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helpers in openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/src/generated/types.ts : Generated TypeScript types location in GQL package: src/generated/types.ts
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/swift/Types.swift : Generated Swift types location in GQL package: dist/swift/Types.swift
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/CONVENTION.md : Before editing anything in GraphQL Types package, review packages/gql/CONVENTION.md
Applied to files:
packages/gql/scripts/generate-kotlin-types.mjspackages/gql/scripts/generate-dart-types.mjs
📚 Learning: 2025-10-18T05:54:54.802Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-18T05:54:54.802Z
Learning: Applies to packages/gql/dist/dart/types.dart : Generated Dart types location in GQL package: dist/dart/types.dart
Applied to files:
packages/gql/scripts/generate-dart-types.mjs
🧬 Code graph analysis (2)
packages/gql/scripts/generate-kotlin-types.mjs (2)
packages/gql/scripts/generate-dart-types.mjs (15)
firstMember(670-670)firstInterfaces(673-673)memberInterfaces(676-676)sharedInterfaceNames(668-668)concreteMembers(697-697)memberTypes(665-665)nestedMembers(701-701)nestedMembers(723-723)nestedUnions(712-712)sortedConcreteMembers(715-715)delegateTo(718-718)isNestedUnion(719-719)wrapperName(735-735)wrapperName(778-778)lines(380-380)packages/gql/scripts/generate-swift-types.mjs (6)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)
packages/gql/scripts/generate-dart-types.mjs (2)
packages/gql/scripts/generate-kotlin-types.mjs (15)
firstMember(635-635)firstInterfaces(638-638)memberInterfaces(641-641)sharedInterfaceNames(633-633)concreteMembers(661-661)memberTypes(630-630)nestedMembers(665-665)nestedMembers(687-687)nestedUnions(676-676)sortedConcreteMembers(679-679)delegateTo(682-682)isNestedUnion(683-683)wrapperName(699-699)wrapperName(714-714)lines(407-407)packages/gql/scripts/generate-swift-types.mjs (6)
firstMember(543-543)firstInterfaces(546-546)memberInterfaces(549-549)sharedInterfaceNames(541-541)memberTypes(535-535)lines(193-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (9)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
717-718: LGTM!The
FetchProductsResultAllvariant correctly models mixed product/subscription results using the newProductOrSubscriptionunion type, aligning with the PR's discriminated-union goals.
2166-2188: Correctly addresses previous typename routing concern.The
fromJsonimplementation now properly routes on concrete platform types (ProductAndroid,ProductIOS,ProductSubscriptionAndroid,ProductSubscriptionIOS) rather than abstract union names, ensuring mixed product arrays deserialize successfully. The wrapper classes (ProductItem,ProductSubscriptionItem) provide type-safe discrimination for this union-of-unions pattern.Note: This is a generated file—any future changes should be made in the generator scripts.
Based on learnings.
packages/gql/scripts/generate-dart-types.mjs (4)
671-686: LGTM – Safe runtime guard for interface extraction.The
typeofcheck correctly handles unions (which lackgetInterfaces()) as members. When the first member or any subsequent member is a union, the code safely skips interface extraction, ensuringsharedInterfaceNamesremains accurate without runtime errors.
695-741: LGTM – Nested union flattening with type-safe wrappers.The flattening logic correctly expands union-of-unions (e.g., ProductOrSubscription = Product | ProductSubscription where both are unions) into concrete members. By wrapping nested union results in typed subclasses (line 736), the factory maintains the sealed-class contract—each returned instance properly implements the outer union type. This addresses the type-safety concern from the previous review.
775-786: LGTM – Clean wrapper implementation for nested unions.The generated wrapper classes correctly extend the outer union type and delegate serialization to the wrapped value. This minimal design maintains type safety while avoiding duplication—each
${outerUnion}${nestedUnion}wrapper is scoped to its parent union, preventing naming collisions.
1054-1054: LGTM – Comment accurately reflects the wrapper-based approach.packages/gql/scripts/generate-kotlin-types.mjs (3)
660-705: Excellent solution for nested union type handling.The flattening and wrapper approach correctly addresses the concern from the previous review. When a union member is itself a union, the code:
- Flattens to collect all concrete types (lines 660-673)
- Routes each concrete type's
__typenameto the appropriate nested union'sfromJson(lines 685-695)- Wraps the nested union instance in a typed wrapper class (line 700) that implements the outer union interface
This ensures the return type is correct—the wrapper implements
ProductOrSubscriptionwhile delegating to the nested union's serialization logic. The pattern mirrors the existingProductItem/SubscriptionItemapproach mentioned in the PR objectives.Based on past review comments, this directly resolves the previously flagged issue about nested union
.fromJsonreturning the wrong type.
712-719: Well-structured wrapper implementation.The generated wrapper classes correctly implement the outer union interface while delegating serialization to the nested union value. The naming convention (
${nestedUnionName}Item) follows the existing pattern referenced in the PR objectives and is scoped within the sealed interface, minimizing naming conflicts.
856-857: Accurate documentation of the new auto-generation approach.The updated comments correctly reflect that
ProductOrSubscriptionandFetchProductsResultAllare now auto-generated from the GraphQL schema, aligning with the PR's objective to remove manual post-processing.
Add ProductOrSubscription union type to GraphQL schema and implement discriminated union type narrowing across all platforms (TypeScript, Swift, Kotlin, Dart).
Changes:
union ProductOrSubscription = Product | ProductSubscriptionto GraphQL schemaTypeScript:
(Product | ProductSubscription)[]Swift:
.productSubscription(let sub), case .productSubscriptionIos(let val)to.productSubscription(.productSubscriptionIos(let val))Kotlin:
Dart:
Generator scripts:
Benefits:
Summary by CodeRabbit
New Features
Chores
Refactor