@gpc-cli/core 0.9.15 → 0.9.17

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/dist/index.d.ts CHANGED
@@ -158,6 +158,16 @@ declare function updateRollout(client: PlayApiClient, packageName: string, track
158
158
  declare function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]>;
159
159
  declare function createTrack(client: PlayApiClient, packageName: string, trackName: string): Promise<Track>;
160
160
  declare function updateTrackConfig(client: PlayApiClient, packageName: string, trackName: string, config: Record<string, unknown>): Promise<Track>;
161
+ interface ReleaseDiff {
162
+ field: string;
163
+ track1Value: string;
164
+ track2Value: string;
165
+ }
166
+ declare function diffReleases(client: PlayApiClient, packageName: string, fromTrack: string, toTrack: string): Promise<{
167
+ fromTrack: string;
168
+ toTrack: string;
169
+ diffs: ReleaseDiff[];
170
+ }>;
161
171
  declare function uploadExternallyHosted(client: PlayApiClient, packageName: string, data: ExternallyHostedApk): Promise<ExternallyHostedApkResponse>;
162
172
 
163
173
  interface ListingDiff {
@@ -345,6 +355,12 @@ declare function createOffer(client: PlayApiClient, packageName: string, product
345
355
  declare function updateOffer(client: PlayApiClient, packageName: string, productId: string, basePlanId: string, offerId: string, data: SubscriptionOffer, updateMask?: string): Promise<SubscriptionOffer>;
346
356
  declare function deleteOffer(client: PlayApiClient, packageName: string, productId: string, basePlanId: string, offerId: string): Promise<void>;
347
357
  declare function activateOffer(client: PlayApiClient, packageName: string, productId: string, basePlanId: string, offerId: string): Promise<SubscriptionOffer>;
358
+ interface SubscriptionDiff {
359
+ field: string;
360
+ local: string;
361
+ remote: string;
362
+ }
363
+ declare function diffSubscription(client: PlayApiClient, packageName: string, productId: string, localData: Subscription): Promise<SubscriptionDiff[]>;
348
364
  declare function deactivateOffer(client: PlayApiClient, packageName: string, productId: string, basePlanId: string, offerId: string): Promise<SubscriptionOffer>;
349
365
 
350
366
  interface ListIapOptions {
@@ -523,12 +539,18 @@ declare function createDeviceTier(client: PlayApiClient, packageName: string, co
523
539
  declare function listOneTimeProducts(client: PlayApiClient, packageName: string): Promise<OneTimeProductsListResponse>;
524
540
  declare function getOneTimeProduct(client: PlayApiClient, packageName: string, productId: string): Promise<OneTimeProduct>;
525
541
  declare function createOneTimeProduct(client: PlayApiClient, packageName: string, data: OneTimeProduct): Promise<OneTimeProduct>;
526
- declare function updateOneTimeProduct(client: PlayApiClient, packageName: string, productId: string, data: Partial<OneTimeProduct>): Promise<OneTimeProduct>;
542
+ declare function updateOneTimeProduct(client: PlayApiClient, packageName: string, productId: string, data: Partial<OneTimeProduct>, updateMask?: string): Promise<OneTimeProduct>;
527
543
  declare function deleteOneTimeProduct(client: PlayApiClient, packageName: string, productId: string): Promise<void>;
528
544
  declare function listOneTimeOffers(client: PlayApiClient, packageName: string, productId: string): Promise<OneTimeOffersListResponse>;
529
545
  declare function getOneTimeOffer(client: PlayApiClient, packageName: string, productId: string, offerId: string): Promise<OneTimeOffer>;
530
546
  declare function createOneTimeOffer(client: PlayApiClient, packageName: string, productId: string, data: OneTimeOffer): Promise<OneTimeOffer>;
531
- declare function updateOneTimeOffer(client: PlayApiClient, packageName: string, productId: string, offerId: string, data: Partial<OneTimeOffer>): Promise<OneTimeOffer>;
547
+ declare function updateOneTimeOffer(client: PlayApiClient, packageName: string, productId: string, offerId: string, data: Partial<OneTimeOffer>, updateMask?: string): Promise<OneTimeOffer>;
548
+ interface OneTimeProductDiff {
549
+ field: string;
550
+ local: string;
551
+ remote: string;
552
+ }
553
+ declare function diffOneTimeProduct(client: PlayApiClient, packageName: string, productId: string, localData: OneTimeProduct): Promise<OneTimeProductDiff[]>;
532
554
  declare function deleteOneTimeOffer(client: PlayApiClient, packageName: string, productId: string, offerId: string): Promise<void>;
533
555
 
534
556
  interface Spinner {
@@ -641,4 +663,4 @@ declare function createPurchaseOption(client: PlayApiClient, packageName: string
641
663
  declare function activatePurchaseOption(client: PlayApiClient, packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;
642
664
  declare function deactivatePurchaseOption(client: PlayApiClient, packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;
643
665
 
644
- export { ApiError, type AppInfo, type AuditEntry, type BatchSyncResult, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, type ScaffoldOptions, type ScaffoldResult, type Spinner, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WebhookPayload, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, checkThreshold, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createExternalTransaction, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, generateMigrationPlan, generateNotesFromGit, getAppInfo, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getProductPurchase, getPurchaseOption, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listDeviceTiers, listGeneratedApks, listImages, listInAppProducts, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchVitalsErrors, sendWebhook, sortResults, syncInAppProducts, updateAppDetails, updateDataSafety, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, writeAuditLog, writeListingsToDir, writeMigrationOutput };
666
+ export { ApiError, type AppInfo, type AuditEntry, type BatchSyncResult, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, type ScaffoldOptions, type ScaffoldResult, type Spinner, type SubscriptionDiff, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WebhookPayload, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, checkThreshold, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createExternalTransaction, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, generateMigrationPlan, generateNotesFromGit, getAppInfo, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getProductPurchase, getPurchaseOption, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listDeviceTiers, listGeneratedApks, listImages, listInAppProducts, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchVitalsErrors, sendWebhook, sortResults, syncInAppProducts, updateAppDetails, updateDataSafety, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -150,6 +150,19 @@ ${formatYaml(value, indent + 1)}`;
150
150
  }
151
151
  return String(data);
152
152
  }
153
+ var MAX_CELL_WIDTH = 60;
154
+ function truncateCell(value) {
155
+ if (value.length <= MAX_CELL_WIDTH) return value;
156
+ return value.slice(0, MAX_CELL_WIDTH - 3) + "...";
157
+ }
158
+ function cellValue(val) {
159
+ if (val === null || val === void 0) return "";
160
+ if (typeof val === "object") {
161
+ if (Array.isArray(val)) return val.length === 0 ? "" : JSON.stringify(val);
162
+ return JSON.stringify(val);
163
+ }
164
+ return String(val);
165
+ }
153
166
  function formatTable(data) {
154
167
  const rows = toRows(data);
155
168
  if (rows.length === 0) return "";
@@ -158,11 +171,11 @@ function formatTable(data) {
158
171
  const keys = Object.keys(firstRow);
159
172
  if (keys.length === 0) return "";
160
173
  const widths = keys.map(
161
- (key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
174
+ (key) => Math.max(key.length, ...rows.map((row) => truncateCell(cellValue(row[key])).length))
162
175
  );
163
176
  const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" ");
164
177
  const separator = widths.map((w) => "-".repeat(w)).join(" ");
165
- const body = rows.map((row) => keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i] ?? 0)).join(" ")).join("\n");
178
+ const body = rows.map((row) => keys.map((key, i) => truncateCell(cellValue(row[key])).padEnd(widths[i] ?? 0)).join(" ")).join("\n");
166
179
  return `${header}
167
180
  ${separator}
168
181
  ${body}`;
@@ -175,12 +188,12 @@ function formatMarkdown(data) {
175
188
  const keys = Object.keys(firstRow);
176
189
  if (keys.length === 0) return "";
177
190
  const widths = keys.map(
178
- (key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
191
+ (key) => Math.max(key.length, ...rows.map((row) => truncateCell(cellValue(row[key])).length))
179
192
  );
180
193
  const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" | ")} |`;
181
194
  const separator = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
182
195
  const body = rows.map(
183
- (row) => `| ${keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i] ?? 0)).join(" | ")} |`
196
+ (row) => `| ${keys.map((key, i) => truncateCell(cellValue(row[key])).padEnd(widths[i] ?? 0)).join(" | ")} |`
184
197
  ).join("\n");
185
198
  return `${header}
186
199
  ${separator}
@@ -819,6 +832,32 @@ async function updateTrackConfig(client, packageName, trackName, config) {
819
832
  throw error;
820
833
  }
821
834
  }
835
+ async function diffReleases(client, packageName, fromTrack, toTrack) {
836
+ const edit = await client.edits.insert(packageName);
837
+ try {
838
+ const [fromData, toData] = await Promise.all([
839
+ client.tracks.get(packageName, edit.id, fromTrack),
840
+ client.tracks.get(packageName, edit.id, toTrack)
841
+ ]);
842
+ await client.edits.delete(packageName, edit.id);
843
+ const fromRelease = fromData.releases?.[0];
844
+ const toRelease = toData.releases?.[0];
845
+ const diffs = [];
846
+ const fields = ["versionCodes", "status", "userFraction", "releaseNotes", "name"];
847
+ for (const field of fields) {
848
+ const v1 = fromRelease ? JSON.stringify(fromRelease[field] ?? null) : "null";
849
+ const v2 = toRelease ? JSON.stringify(toRelease[field] ?? null) : "null";
850
+ if (v1 !== v2) {
851
+ diffs.push({ field, track1Value: v1, track2Value: v2 });
852
+ }
853
+ }
854
+ return { fromTrack, toTrack, diffs };
855
+ } catch (error) {
856
+ await client.edits.delete(packageName, edit.id).catch(() => {
857
+ });
858
+ throw error;
859
+ }
860
+ }
822
861
  async function uploadExternallyHosted(client, packageName, data) {
823
862
  if (!data.externallyHostedUrl) {
824
863
  throw new GpcError(
@@ -2178,12 +2217,17 @@ async function createSubscription(client, packageName, data) {
2178
2217
  const sanitized = sanitizeSubscription(data);
2179
2218
  return client.subscriptions.create(packageName, sanitized, data.productId);
2180
2219
  }
2220
+ var SUBSCRIPTION_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
2221
+ function deriveSubscriptionUpdateMask(data) {
2222
+ return Object.keys(data).filter((k) => !SUBSCRIPTION_ID_FIELDS.has(k)).join(",");
2223
+ }
2181
2224
  async function updateSubscription(client, packageName, productId, data, updateMask) {
2182
2225
  validatePackageName(packageName);
2183
2226
  validateSku(productId);
2184
2227
  validateSubscriptionData(data);
2185
2228
  const sanitized = sanitizeSubscription(data);
2186
- return client.subscriptions.update(packageName, productId, sanitized, updateMask);
2229
+ const mask = updateMask || deriveSubscriptionUpdateMask(data);
2230
+ return client.subscriptions.update(packageName, productId, sanitized, mask);
2187
2231
  }
2188
2232
  async function deleteSubscription(client, packageName, productId) {
2189
2233
  validatePackageName(packageName);
@@ -2226,17 +2270,22 @@ async function createOffer(client, packageName, productId, basePlanId, data) {
2226
2270
  const sanitized = sanitizeOffer(data);
2227
2271
  return client.subscriptions.createOffer(packageName, productId, basePlanId, sanitized, data.offerId);
2228
2272
  }
2273
+ var OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "basePlanId", "offerId"]);
2274
+ function deriveOfferUpdateMask(data) {
2275
+ return Object.keys(data).filter((k) => !OFFER_ID_FIELDS.has(k)).join(",");
2276
+ }
2229
2277
  async function updateOffer(client, packageName, productId, basePlanId, offerId, data, updateMask) {
2230
2278
  validatePackageName(packageName);
2231
2279
  validateSku(productId);
2232
2280
  const sanitized = sanitizeOffer(data);
2281
+ const mask = updateMask || deriveOfferUpdateMask(data);
2233
2282
  return client.subscriptions.updateOffer(
2234
2283
  packageName,
2235
2284
  productId,
2236
2285
  basePlanId,
2237
2286
  offerId,
2238
2287
  sanitized,
2239
- updateMask
2288
+ mask
2240
2289
  );
2241
2290
  }
2242
2291
  async function deleteOffer(client, packageName, productId, basePlanId, offerId) {
@@ -2249,6 +2298,21 @@ async function activateOffer(client, packageName, productId, basePlanId, offerId
2249
2298
  validateSku(productId);
2250
2299
  return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);
2251
2300
  }
2301
+ async function diffSubscription(client, packageName, productId, localData) {
2302
+ validatePackageName(packageName);
2303
+ validateSku(productId);
2304
+ const remote = await client.subscriptions.get(packageName, productId);
2305
+ const diffs = [];
2306
+ const fieldsToCompare = ["listings", "basePlans", "taxAndComplianceSettings"];
2307
+ for (const field of fieldsToCompare) {
2308
+ const localVal = JSON.stringify(localData[field] ?? null);
2309
+ const remoteVal = JSON.stringify(remote[field] ?? null);
2310
+ if (localVal !== remoteVal) {
2311
+ diffs.push({ field, local: localVal, remote: remoteVal });
2312
+ }
2313
+ }
2314
+ return diffs;
2315
+ }
2252
2316
  async function deactivateOffer(client, packageName, productId, basePlanId, offerId) {
2253
2317
  validatePackageName(packageName);
2254
2318
  validateSku(productId);
@@ -2955,9 +3019,18 @@ async function createOneTimeProduct(client, packageName, data) {
2955
3019
  );
2956
3020
  }
2957
3021
  }
2958
- async function updateOneTimeProduct(client, packageName, productId, data) {
3022
+ var OTP_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
3023
+ function deriveOtpUpdateMask(data) {
3024
+ return Object.keys(data).filter((k) => !OTP_ID_FIELDS.has(k)).join(",");
3025
+ }
3026
+ var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "offerId"]);
3027
+ function deriveOtpOfferUpdateMask(data) {
3028
+ return Object.keys(data).filter((k) => !OTP_OFFER_ID_FIELDS.has(k)).join(",");
3029
+ }
3030
+ async function updateOneTimeProduct(client, packageName, productId, data, updateMask) {
2959
3031
  try {
2960
- return await client.oneTimeProducts.update(packageName, productId, data);
3032
+ const mask = updateMask || deriveOtpUpdateMask(data);
3033
+ return await client.oneTimeProducts.update(packageName, productId, data, mask);
2961
3034
  } catch (error) {
2962
3035
  throw new GpcError(
2963
3036
  `Failed to update one-time product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3015,9 +3088,10 @@ async function createOneTimeOffer(client, packageName, productId, data) {
3015
3088
  );
3016
3089
  }
3017
3090
  }
3018
- async function updateOneTimeOffer(client, packageName, productId, offerId, data) {
3091
+ async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask) {
3019
3092
  try {
3020
- return await client.oneTimeProducts.updateOffer(packageName, productId, offerId, data);
3093
+ const mask = updateMask || deriveOtpOfferUpdateMask(data);
3094
+ return await client.oneTimeProducts.updateOffer(packageName, productId, offerId, data, mask);
3021
3095
  } catch (error) {
3022
3096
  throw new GpcError(
3023
3097
  `Failed to update offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3027,6 +3101,19 @@ async function updateOneTimeOffer(client, packageName, productId, offerId, data)
3027
3101
  );
3028
3102
  }
3029
3103
  }
3104
+ async function diffOneTimeProduct(client, packageName, productId, localData) {
3105
+ const remote = await client.oneTimeProducts.get(packageName, productId);
3106
+ const diffs = [];
3107
+ const fieldsToCompare = ["listings", "purchaseType", "taxAndComplianceSettings"];
3108
+ for (const field of fieldsToCompare) {
3109
+ const localVal = JSON.stringify(localData[field] ?? null);
3110
+ const remoteVal = JSON.stringify(remote[field] ?? null);
3111
+ if (localVal !== remoteVal) {
3112
+ diffs.push({ field, local: localVal, remote: remoteVal });
3113
+ }
3114
+ }
3115
+ return diffs;
3116
+ }
3030
3117
  async function deleteOneTimeOffer(client, packageName, productId, offerId) {
3031
3118
  try {
3032
3119
  await client.oneTimeProducts.deleteOffer(packageName, productId, offerId);
@@ -3652,6 +3739,9 @@ export {
3652
3739
  detectOutputFormat,
3653
3740
  diffListings,
3654
3741
  diffListingsCommand,
3742
+ diffOneTimeProduct,
3743
+ diffReleases,
3744
+ diffSubscription,
3655
3745
  discoverPlugins,
3656
3746
  downloadGeneratedApk,
3657
3747
  downloadReport,