react-native-iap 14.0.1 → 14.1.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +81 -22
- package/app.plugin.js +1 -1
- package/ios/HybridRnIap.swift +167 -11
- package/ios/ProductStore.swift +10 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/module/helpers/subscription.js +9 -1
- package/lib/module/helpers/subscription.js.map +1 -1
- package/lib/module/hooks/useIAP.js +9 -13
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +43 -28
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +24 -16
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/type-bridge.js +64 -13
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/typescript/src/helpers/subscription.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +12 -22
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +15 -11
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +58 -58
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/nitro.json +5 -1
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +13 -4
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroProduct.hpp +40 -36
- package/nitrogen/generated/android/c++/JNitroPurchase.hpp +12 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +12 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +9 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +1 -1
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +13 -7
- package/nitrogen/generated/ios/swift/NitroProduct.swift +73 -43
- package/nitrogen/generated/ios/swift/NitroPurchase.swift +35 -2
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/NitroProduct.hpp +41 -37
- package/nitrogen/generated/shared/c++/NitroPurchase.hpp +13 -1
- package/package.json +9 -2
- package/plugin/build/src/withIAP.d.ts +3 -0
- package/plugin/build/src/withIAP.js +81 -0
- package/plugin/build/tsconfig.tsbuildinfo +1 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/helpers/subscription.ts +36 -25
- package/src/hooks/useIAP.ts +188 -201
- package/src/index.ts +377 -356
- package/src/specs/RnIap.nitro.ts +15 -11
- package/src/types.ts +66 -62
- package/src/utils/error.ts +19 -19
- package/src/utils/type-bridge.ts +138 -75
package/src/utils/type-bridge.ts
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* Purpose: Prevent type fragmentation between native (Nitro) and TypeScript sides
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
10
|
+
import type {NitroProduct, NitroPurchase} from '../specs/RnIap.nitro';
|
|
11
|
+
import type {Product, Purchase, SubscriptionProduct} from '../types';
|
|
12
|
+
import {PurchaseState, ProductTypeIOS} from '../types';
|
|
13
|
+
import {Platform} from 'react-native';
|
|
13
14
|
|
|
14
15
|
// ============================================================================
|
|
15
16
|
// PRODUCT CONVERSION
|
|
@@ -20,7 +21,7 @@ import { Platform } from 'react-native'
|
|
|
20
21
|
* This ensures all fields are properly mapped and accessible
|
|
21
22
|
*/
|
|
22
23
|
export function convertNitroProductToProduct(
|
|
23
|
-
nitroProduct: NitroProduct
|
|
24
|
+
nitroProduct: NitroProduct,
|
|
24
25
|
): Product {
|
|
25
26
|
// Create base product with common fields, handling platform casting
|
|
26
27
|
const product: any = {
|
|
@@ -33,62 +34,92 @@ export function convertNitroProductToProduct(
|
|
|
33
34
|
currency: nitroProduct.currency || '',
|
|
34
35
|
price: nitroProduct.price,
|
|
35
36
|
platform: nitroProduct.platform as 'ios' | 'android',
|
|
36
|
-
}
|
|
37
|
+
};
|
|
37
38
|
|
|
38
39
|
// Add platform-specific fields based on current platform
|
|
39
40
|
if (Platform.OS === 'ios') {
|
|
40
41
|
// Map iOS fields from Nitro to TypeScript types
|
|
41
|
-
const iosProduct = product as any // Temporarily cast to access iOS fields
|
|
42
|
-
iosProduct.isFamilyShareable = nitroProduct.
|
|
43
|
-
iosProduct.jsonRepresentation = nitroProduct.
|
|
42
|
+
const iosProduct = product as any; // Temporarily cast to access iOS fields
|
|
43
|
+
iosProduct.isFamilyShareable = (nitroProduct as any).isFamilyShareableIOS;
|
|
44
|
+
iosProduct.jsonRepresentation = (nitroProduct as any).jsonRepresentationIOS;
|
|
45
|
+
// Detailed iOS product type - directly from the native field
|
|
46
|
+
const typeIOSValue: string | undefined = (nitroProduct as any).typeIOS;
|
|
47
|
+
|
|
48
|
+
switch (typeIOSValue) {
|
|
49
|
+
case 'consumable':
|
|
50
|
+
iosProduct.typeIOS = ProductTypeIOS.consumable;
|
|
51
|
+
break;
|
|
52
|
+
case 'nonConsumable':
|
|
53
|
+
iosProduct.typeIOS = ProductTypeIOS.nonConsumable;
|
|
54
|
+
break;
|
|
55
|
+
case 'autoRenewableSubscription':
|
|
56
|
+
iosProduct.typeIOS = ProductTypeIOS.autoRenewableSubscription;
|
|
57
|
+
break;
|
|
58
|
+
case 'nonRenewingSubscription':
|
|
59
|
+
iosProduct.typeIOS = ProductTypeIOS.nonRenewingSubscription;
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
iosProduct.typeIOS = undefined;
|
|
63
|
+
}
|
|
44
64
|
iosProduct.subscriptionPeriodUnitIOS =
|
|
45
|
-
nitroProduct.subscriptionPeriodUnitIOS
|
|
65
|
+
nitroProduct.subscriptionPeriodUnitIOS;
|
|
46
66
|
iosProduct.subscriptionPeriodNumberIOS =
|
|
47
|
-
nitroProduct.subscriptionPeriodNumberIOS
|
|
48
|
-
iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS
|
|
67
|
+
nitroProduct.subscriptionPeriodNumberIOS;
|
|
68
|
+
iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS;
|
|
49
69
|
iosProduct.introductoryPriceAsAmountIOS =
|
|
50
|
-
nitroProduct.introductoryPriceAsAmountIOS
|
|
70
|
+
nitroProduct.introductoryPriceAsAmountIOS;
|
|
51
71
|
iosProduct.introductoryPricePaymentModeIOS =
|
|
52
|
-
nitroProduct.introductoryPricePaymentModeIOS
|
|
72
|
+
nitroProduct.introductoryPricePaymentModeIOS;
|
|
53
73
|
iosProduct.introductoryPriceNumberOfPeriodsIOS =
|
|
54
|
-
nitroProduct.introductoryPriceNumberOfPeriodsIOS
|
|
74
|
+
nitroProduct.introductoryPriceNumberOfPeriodsIOS;
|
|
55
75
|
iosProduct.introductoryPriceSubscriptionPeriodIOS =
|
|
56
|
-
nitroProduct.introductoryPriceSubscriptionPeriodIOS
|
|
76
|
+
nitroProduct.introductoryPriceSubscriptionPeriodIOS;
|
|
57
77
|
} else if (Platform.OS === 'android') {
|
|
58
78
|
// Map Android fields from Nitro to TypeScript types
|
|
59
|
-
const androidProduct = product as any // Temporarily cast to access Android fields
|
|
60
|
-
androidProduct.originalPrice = nitroProduct.
|
|
61
|
-
androidProduct.originalPriceAmountMicros =
|
|
62
|
-
nitroProduct
|
|
63
|
-
|
|
64
|
-
androidProduct.
|
|
65
|
-
nitroProduct
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
const androidProduct = product as any; // Temporarily cast to access Android fields
|
|
80
|
+
androidProduct.originalPrice = (nitroProduct as any).originalPriceAndroid;
|
|
81
|
+
androidProduct.originalPriceAmountMicros = (
|
|
82
|
+
nitroProduct as any
|
|
83
|
+
).originalPriceAmountMicrosAndroid;
|
|
84
|
+
androidProduct.introductoryPriceValue = (
|
|
85
|
+
nitroProduct as any
|
|
86
|
+
).introductoryPriceValueAndroid;
|
|
87
|
+
androidProduct.introductoryPriceCycles = (
|
|
88
|
+
nitroProduct as any
|
|
89
|
+
).introductoryPriceCyclesAndroid;
|
|
90
|
+
androidProduct.introductoryPricePeriod = (
|
|
91
|
+
nitroProduct as any
|
|
92
|
+
).introductoryPricePeriodAndroid;
|
|
93
|
+
androidProduct.subscriptionPeriod = (
|
|
94
|
+
nitroProduct as any
|
|
95
|
+
).subscriptionPeriodAndroid;
|
|
96
|
+
androidProduct.freeTrialPeriod = (
|
|
97
|
+
nitroProduct as any
|
|
98
|
+
).freeTrialPeriodAndroid;
|
|
70
99
|
|
|
71
100
|
// Map subscription offer details (parse from JSON string)
|
|
72
101
|
if (nitroProduct.subscriptionOfferDetailsAndroid) {
|
|
73
102
|
try {
|
|
74
103
|
androidProduct.subscriptionOfferDetailsAndroid = JSON.parse(
|
|
75
|
-
nitroProduct.subscriptionOfferDetailsAndroid
|
|
76
|
-
)
|
|
104
|
+
nitroProduct.subscriptionOfferDetailsAndroid,
|
|
105
|
+
);
|
|
77
106
|
} catch (e) {
|
|
78
|
-
console.warn('Failed to parse subscription offer details:', e)
|
|
79
|
-
androidProduct.subscriptionOfferDetailsAndroid = null
|
|
107
|
+
console.warn('Failed to parse subscription offer details:', e);
|
|
108
|
+
androidProduct.subscriptionOfferDetailsAndroid = null;
|
|
80
109
|
}
|
|
81
110
|
}
|
|
82
111
|
|
|
83
112
|
// Create flattened offer fields for easier access in example code
|
|
84
113
|
androidProduct.oneTimePurchaseOfferFormattedPrice =
|
|
85
|
-
nitroProduct.displayPrice
|
|
86
|
-
androidProduct.oneTimePurchaseOfferPriceAmountMicros =
|
|
87
|
-
nitroProduct
|
|
88
|
-
|
|
114
|
+
nitroProduct.displayPrice;
|
|
115
|
+
androidProduct.oneTimePurchaseOfferPriceAmountMicros = (
|
|
116
|
+
nitroProduct as any
|
|
117
|
+
).originalPriceAmountMicrosAndroid;
|
|
118
|
+
androidProduct.oneTimePurchaseOfferPriceCurrencyCode =
|
|
119
|
+
nitroProduct.currency;
|
|
89
120
|
}
|
|
90
121
|
|
|
91
|
-
return product as Product
|
|
122
|
+
return product as Product;
|
|
92
123
|
}
|
|
93
124
|
|
|
94
125
|
// Note: Use nitroProducts.map(convertNitroProductToProduct) instead of a separate function
|
|
@@ -97,19 +128,19 @@ export function convertNitroProductToProduct(
|
|
|
97
128
|
* Convert Product to SubscriptionProduct (type-safe casting)
|
|
98
129
|
*/
|
|
99
130
|
export function convertProductToSubscriptionProduct(
|
|
100
|
-
product: Product
|
|
131
|
+
product: Product,
|
|
101
132
|
): SubscriptionProduct {
|
|
102
133
|
if (product.type !== 'subs') {
|
|
103
134
|
console.warn(
|
|
104
135
|
'Converting non-subscription product to SubscriptionProduct:',
|
|
105
|
-
product.id
|
|
106
|
-
)
|
|
136
|
+
product.id,
|
|
137
|
+
);
|
|
107
138
|
}
|
|
108
139
|
// Since SubscriptionProduct is now an intersection type, we need to cast properly
|
|
109
140
|
return {
|
|
110
141
|
...product,
|
|
111
142
|
type: 'subs' as const,
|
|
112
|
-
} as SubscriptionProduct
|
|
143
|
+
} as SubscriptionProduct;
|
|
113
144
|
}
|
|
114
145
|
|
|
115
146
|
// ============================================================================
|
|
@@ -120,7 +151,7 @@ export function convertProductToSubscriptionProduct(
|
|
|
120
151
|
* Convert NitroPurchase (from native) to TypeScript Purchase (for library consumers)
|
|
121
152
|
*/
|
|
122
153
|
export function convertNitroPurchaseToPurchase(
|
|
123
|
-
nitroPurchase: NitroPurchase
|
|
154
|
+
nitroPurchase: NitroPurchase,
|
|
124
155
|
): Purchase {
|
|
125
156
|
// Create base purchase with common fields
|
|
126
157
|
const purchase: any = {
|
|
@@ -130,33 +161,59 @@ export function convertNitroPurchaseToPurchase(
|
|
|
130
161
|
transactionReceipt: '', // Will be set by native layer
|
|
131
162
|
purchaseToken: nitroPurchase.purchaseToken,
|
|
132
163
|
platform: nitroPurchase.platform as 'ios' | 'android',
|
|
133
|
-
|
|
164
|
+
// Common fields from NitroPurchase
|
|
165
|
+
quantity: nitroPurchase.quantity || 1,
|
|
166
|
+
purchaseState: nitroPurchase.purchaseState as PurchaseState || PurchaseState.unknown,
|
|
167
|
+
isAutoRenewing: nitroPurchase.isAutoRenewing || false,
|
|
168
|
+
};
|
|
134
169
|
|
|
135
170
|
// Add platform-specific fields
|
|
136
171
|
if (Platform.OS === 'ios') {
|
|
137
|
-
const iosPurchase = purchase as any
|
|
138
|
-
iosPurchase.quantityIOS = nitroPurchase.quantityIOS
|
|
172
|
+
const iosPurchase = purchase as any;
|
|
173
|
+
iosPurchase.quantityIOS = nitroPurchase.quantityIOS;
|
|
139
174
|
iosPurchase.originalTransactionDateIOS =
|
|
140
|
-
nitroPurchase.originalTransactionDateIOS
|
|
175
|
+
nitroPurchase.originalTransactionDateIOS;
|
|
141
176
|
iosPurchase.originalTransactionIdentifierIOS =
|
|
142
|
-
nitroPurchase.originalTransactionIdentifierIOS
|
|
143
|
-
iosPurchase.appAccountToken = nitroPurchase.appAccountToken
|
|
177
|
+
nitroPurchase.originalTransactionIdentifierIOS;
|
|
178
|
+
iosPurchase.appAccountToken = nitroPurchase.appAccountToken;
|
|
179
|
+
// Fill common quantity from iOS-specific quantity when available
|
|
180
|
+
if (typeof nitroPurchase.quantityIOS === 'number') {
|
|
181
|
+
purchase.quantity = nitroPurchase.quantityIOS;
|
|
182
|
+
}
|
|
144
183
|
} else if (Platform.OS === 'android') {
|
|
145
|
-
const androidPurchase = purchase as any
|
|
146
|
-
androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid
|
|
147
|
-
androidPurchase.dataAndroid = nitroPurchase.dataAndroid
|
|
148
|
-
androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid
|
|
149
|
-
|
|
150
|
-
androidPurchase.
|
|
151
|
-
|
|
152
|
-
androidPurchase.
|
|
184
|
+
const androidPurchase = purchase as any;
|
|
185
|
+
androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid;
|
|
186
|
+
androidPurchase.dataAndroid = nitroPurchase.dataAndroid;
|
|
187
|
+
androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid;
|
|
188
|
+
// Support both old and new field names for backward compatibility
|
|
189
|
+
androidPurchase.autoRenewingAndroid = nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing;
|
|
190
|
+
// no longer surface purchaseStateAndroid on TS side
|
|
191
|
+
androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid;
|
|
192
|
+
androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid;
|
|
153
193
|
androidPurchase.obfuscatedAccountIdAndroid =
|
|
154
|
-
nitroPurchase.obfuscatedAccountIdAndroid
|
|
194
|
+
nitroPurchase.obfuscatedAccountIdAndroid;
|
|
155
195
|
androidPurchase.obfuscatedProfileIdAndroid =
|
|
156
|
-
nitroPurchase.obfuscatedProfileIdAndroid
|
|
196
|
+
nitroPurchase.obfuscatedProfileIdAndroid;
|
|
197
|
+
|
|
198
|
+
// Use the common isAutoRenewing field from NitroPurchase
|
|
199
|
+
purchase.isAutoRenewing = nitroPurchase.isAutoRenewing;
|
|
200
|
+
|
|
201
|
+
// Map numeric Android purchase state to common PurchaseState
|
|
202
|
+
switch (nitroPurchase.purchaseStateAndroid) {
|
|
203
|
+
case 1:
|
|
204
|
+
purchase.purchaseState = PurchaseState.purchased;
|
|
205
|
+
break;
|
|
206
|
+
case 2:
|
|
207
|
+
purchase.purchaseState = PurchaseState.pending;
|
|
208
|
+
break;
|
|
209
|
+
case 0:
|
|
210
|
+
default:
|
|
211
|
+
purchase.purchaseState = PurchaseState.unknown;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
157
214
|
}
|
|
158
215
|
|
|
159
|
-
return purchase as Purchase
|
|
216
|
+
return purchase as Purchase;
|
|
160
217
|
}
|
|
161
218
|
|
|
162
219
|
// Note: Use nitroPurchases.map(convertNitroPurchaseToPurchase) instead of a separate function
|
|
@@ -169,7 +226,10 @@ export function convertNitroPurchaseToPurchase(
|
|
|
169
226
|
* Validate that a NitroProduct has all required fields for conversion
|
|
170
227
|
*/
|
|
171
228
|
export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
|
|
172
|
-
|
|
229
|
+
if (!nitroProduct || typeof nitroProduct !== 'object') {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
const required = ['id', 'title', 'description', 'type', 'platform'];
|
|
173
233
|
for (const field of required) {
|
|
174
234
|
if (
|
|
175
235
|
!(field in nitroProduct) ||
|
|
@@ -177,19 +237,22 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
|
|
|
177
237
|
) {
|
|
178
238
|
console.error(
|
|
179
239
|
`NitroProduct missing required field: ${field}`,
|
|
180
|
-
nitroProduct
|
|
181
|
-
)
|
|
182
|
-
return false
|
|
240
|
+
nitroProduct,
|
|
241
|
+
);
|
|
242
|
+
return false;
|
|
183
243
|
}
|
|
184
244
|
}
|
|
185
|
-
return true
|
|
245
|
+
return true;
|
|
186
246
|
}
|
|
187
247
|
|
|
188
248
|
/**
|
|
189
249
|
* Validate that a NitroPurchase has all required fields for conversion
|
|
190
250
|
*/
|
|
191
251
|
export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
192
|
-
|
|
252
|
+
if (!nitroPurchase || typeof nitroPurchase !== 'object') {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
const required = ['id', 'productId', 'transactionDate', 'platform'];
|
|
193
256
|
for (const field of required) {
|
|
194
257
|
if (
|
|
195
258
|
!(field in nitroPurchase) ||
|
|
@@ -197,12 +260,12 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
|
197
260
|
) {
|
|
198
261
|
console.error(
|
|
199
262
|
`NitroPurchase missing required field: ${field}`,
|
|
200
|
-
nitroPurchase
|
|
201
|
-
)
|
|
202
|
-
return false
|
|
263
|
+
nitroPurchase,
|
|
264
|
+
);
|
|
265
|
+
return false;
|
|
203
266
|
}
|
|
204
267
|
}
|
|
205
|
-
return true
|
|
268
|
+
return true;
|
|
206
269
|
}
|
|
207
270
|
|
|
208
271
|
// ============================================================================
|
|
@@ -214,10 +277,10 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
|
214
277
|
* This function can be run in development to detect type mismatches
|
|
215
278
|
*/
|
|
216
279
|
export function checkTypeSynchronization(): {
|
|
217
|
-
isSync: boolean
|
|
218
|
-
issues: string[]
|
|
280
|
+
isSync: boolean;
|
|
281
|
+
issues: string[];
|
|
219
282
|
} {
|
|
220
|
-
const issues: string[] = []
|
|
283
|
+
const issues: string[] = [];
|
|
221
284
|
|
|
222
285
|
try {
|
|
223
286
|
// Simple test: can we convert between types?
|
|
@@ -229,19 +292,19 @@ export function checkTypeSynchronization(): {
|
|
|
229
292
|
platform: 'ios',
|
|
230
293
|
displayPrice: '$1.00',
|
|
231
294
|
currency: 'USD',
|
|
232
|
-
}
|
|
295
|
+
};
|
|
233
296
|
|
|
234
|
-
const converted = convertNitroProductToProduct(testNitroProduct)
|
|
297
|
+
const converted = convertNitroProductToProduct(testNitroProduct);
|
|
235
298
|
|
|
236
299
|
if (!converted.id || !converted.title) {
|
|
237
|
-
issues.push('Type conversion failed')
|
|
300
|
+
issues.push('Type conversion failed');
|
|
238
301
|
}
|
|
239
302
|
} catch (error) {
|
|
240
|
-
issues.push(`Type conversion error: ${error}`)
|
|
303
|
+
issues.push(`Type conversion error: ${error}`);
|
|
241
304
|
}
|
|
242
305
|
|
|
243
306
|
return {
|
|
244
307
|
isSync: issues.length === 0,
|
|
245
308
|
issues,
|
|
246
|
-
}
|
|
309
|
+
};
|
|
247
310
|
}
|