diff --git a/CHANGELOG.md b/CHANGELOG.md index 499c0eda5..7c4c482dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ - Android accepts an FCM token string. - iOS accepts a continuous hex string representation of the APNS token. +### Internal + +- Tightened TurboModule spec type safety in `NativeRNIterableAPI.ts` (SDK-521, Phase 1). + - Replaced `{ [key: string]: string | number | boolean }` dictionaries on the fixed-shape bridge methods (`updateCart`, `trackPurchase`, `setAttributionInfo`, `getAttributionInfo`, `startSession`, `updateVisibleRows`) with inline interfaces (`CommerceItemSpec`, `AttributionInfoSpec`, `InboxImpressionRowSpec`). + - No runtime behavior change; native layers read by string key as before. Public facade signatures (`IterableCommerceItem`, `IterableAttributionInfo`, `IterableInboxImpressionRowInfo`) are unchanged and remain structurally compatible — no casts required. + ## 3.0.1 ### Updates diff --git a/src/api/NativeRNIterableAPI.ts b/src/api/NativeRNIterableAPI.ts index 01fa45113..7e96bc649 100644 --- a/src/api/NativeRNIterableAPI.ts +++ b/src/api/NativeRNIterableAPI.ts @@ -30,6 +30,39 @@ interface EmbeddedMessage { payload?: { [key: string]: string | number | boolean | null } | null; } +// Inbound fixed-shape structs. Keys are the source of truth and MUST stay in sync +// with the native decoders that read by string key. See the native `Serialization` +// files (iOS: ios/RNIterableAPI/Serialization.swift; Android: +// android/.../Serialization.java) for the authoritative key list per struct: +// - CommerceItemSpec ↔ CommerceItem +// - InboxImpressionRowSpec ↔ InboxImpressionTracker.RowInfo +// `dataFields` is `unknown` (not a typed dict): IterableCommerceItem.dataFields is +// typed `unknown` and is an open consumer-supplied data bag. Codegen accepts +// `unknown` (maps to GenericObjectTypeAnnotation, same as `Object`). +interface CommerceItemSpec { + id: string; + name: string; + price: number; + quantity: number; + sku?: string | null; + description?: string | null; + url?: string | null; + imageUrl?: string | null; + categories?: Array | null; + dataFields?: unknown; +} + +interface AttributionInfoSpec { + campaignId: number; + templateId: number; + messageId: string; +} + +interface InboxImpressionRowSpec { + messageId: string; + silentInbox: boolean; +} + export interface Spec extends TurboModule { // Initialization initializeWithApiKey( @@ -94,12 +127,10 @@ export interface Spec extends TurboModule { inAppConsume(messageId: string, location: number, source: number): void; // Commerce - updateCart( - items: Array<{ [key: string]: string | number | boolean }> - ): void; + updateCart(items: Array): void; trackPurchase( total: number, - items: Array<{ [key: string]: string | number | boolean }>, + items: Array, dataFields?: { [key: string]: string | number | boolean } ): void; @@ -111,12 +142,8 @@ export interface Spec extends TurboModule { updateEmail(email: string, authToken?: string): void; // Attribution - getAttributionInfo(): Promise<{ - [key: string]: string | number | boolean; - } | null>; - setAttributionInfo( - dict: { [key: string]: string | number | boolean } | null - ): void; + getAttributionInfo(): Promise; + setAttributionInfo(dict: AttributionInfoSpec | null): void; // Device management disableDeviceForCurrentUser(): void; @@ -144,12 +171,10 @@ export interface Spec extends TurboModule { ): void; // Session tracking - startSession( - visibleRows: Array<{ [key: string]: string | number | boolean }> - ): void; + startSession(visibleRows: Array): void; endSession(): void; updateVisibleRows( - visibleRows: Array<{ [key: string]: string | number | boolean }> + visibleRows: Array ): void; // Auth