@gpc-cli/core 0.9.16 → 0.9.18

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,11 @@ ${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
+ }
153
158
  function cellValue(val) {
154
159
  if (val === null || val === void 0) return "";
155
160
  if (typeof val === "object") {
@@ -166,11 +171,11 @@ function formatTable(data) {
166
171
  const keys = Object.keys(firstRow);
167
172
  if (keys.length === 0) return "";
168
173
  const widths = keys.map(
169
- (key) => Math.max(key.length, ...rows.map((row) => cellValue(row[key]).length))
174
+ (key) => Math.max(key.length, ...rows.map((row) => truncateCell(cellValue(row[key])).length))
170
175
  );
171
176
  const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" ");
172
177
  const separator = widths.map((w) => "-".repeat(w)).join(" ");
173
- const body = rows.map((row) => keys.map((key, i) => cellValue(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");
174
179
  return `${header}
175
180
  ${separator}
176
181
  ${body}`;
@@ -183,12 +188,12 @@ function formatMarkdown(data) {
183
188
  const keys = Object.keys(firstRow);
184
189
  if (keys.length === 0) return "";
185
190
  const widths = keys.map(
186
- (key) => Math.max(key.length, ...rows.map((row) => cellValue(row[key]).length))
191
+ (key) => Math.max(key.length, ...rows.map((row) => truncateCell(cellValue(row[key])).length))
187
192
  );
188
193
  const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" | ")} |`;
189
194
  const separator = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
190
195
  const body = rows.map(
191
- (row) => `| ${keys.map((key, i) => cellValue(row[key]).padEnd(widths[i] ?? 0)).join(" | ")} |`
196
+ (row) => `| ${keys.map((key, i) => truncateCell(cellValue(row[key])).padEnd(widths[i] ?? 0)).join(" | ")} |`
192
197
  ).join("\n");
193
198
  return `${header}
194
199
  ${separator}
@@ -827,6 +832,32 @@ async function updateTrackConfig(client, packageName, trackName, config) {
827
832
  throw error;
828
833
  }
829
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
+ }
830
861
  async function uploadExternallyHosted(client, packageName, data) {
831
862
  if (!data.externallyHostedUrl) {
832
863
  throw new GpcError(
@@ -1943,12 +1974,12 @@ async function queryMetric(reporting, packageName, metricSet, options) {
1943
1974
  }
1944
1975
  async function getVitalsOverview(reporting, packageName) {
1945
1976
  const metricSets = [
1946
- ["vitals.crashrate", "crashRate"],
1947
- ["vitals.anrrate", "anrRate"],
1948
- ["vitals.slowstartrate", "slowStartRate"],
1949
- ["vitals.slowrenderingrate", "slowRenderingRate"],
1950
- ["vitals.excessivewakeuprate", "excessiveWakeupRate"],
1951
- ["vitals.stuckbackgroundwakelockrate", "stuckWakelockRate"]
1977
+ ["crashRateMetricSet", "crashRate"],
1978
+ ["anrRateMetricSet", "anrRate"],
1979
+ ["slowStartRateMetricSet", "slowStartRate"],
1980
+ ["slowRenderingRateMetricSet", "slowRenderingRate"],
1981
+ ["excessiveWakeupRateMetricSet", "excessiveWakeupRate"],
1982
+ ["stuckBackgroundWakelockRateMetricSet", "stuckWakelockRate"]
1952
1983
  ];
1953
1984
  const results = await Promise.allSettled(
1954
1985
  metricSets.map(
@@ -1971,22 +2002,22 @@ async function getVitalsOverview(reporting, packageName) {
1971
2002
  return overview;
1972
2003
  }
1973
2004
  async function getVitalsCrashes(reporting, packageName, options) {
1974
- return queryMetric(reporting, packageName, "vitals.crashrate", options);
2005
+ return queryMetric(reporting, packageName, "crashRateMetricSet", options);
1975
2006
  }
1976
2007
  async function getVitalsAnr(reporting, packageName, options) {
1977
- return queryMetric(reporting, packageName, "vitals.anrrate", options);
2008
+ return queryMetric(reporting, packageName, "anrRateMetricSet", options);
1978
2009
  }
1979
2010
  async function getVitalsStartup(reporting, packageName, options) {
1980
- return queryMetric(reporting, packageName, "vitals.slowstartrate", options);
2011
+ return queryMetric(reporting, packageName, "slowStartRateMetricSet", options);
1981
2012
  }
1982
2013
  async function getVitalsRendering(reporting, packageName, options) {
1983
- return queryMetric(reporting, packageName, "vitals.slowrenderingrate", options);
2014
+ return queryMetric(reporting, packageName, "slowRenderingRateMetricSet", options);
1984
2015
  }
1985
2016
  async function getVitalsBattery(reporting, packageName, options) {
1986
- return queryMetric(reporting, packageName, "vitals.excessivewakeuprate", options);
2017
+ return queryMetric(reporting, packageName, "excessiveWakeupRateMetricSet", options);
1987
2018
  }
1988
2019
  async function getVitalsMemory(reporting, packageName, options) {
1989
- return queryMetric(reporting, packageName, "vitals.stuckbackgroundwakelockrate", options);
2020
+ return queryMetric(reporting, packageName, "stuckBackgroundWakelockRateMetricSet", options);
1990
2021
  }
1991
2022
  async function getVitalsAnomalies(reporting, packageName) {
1992
2023
  return reporting.getAnomalies(packageName);
@@ -2186,12 +2217,17 @@ async function createSubscription(client, packageName, data) {
2186
2217
  const sanitized = sanitizeSubscription(data);
2187
2218
  return client.subscriptions.create(packageName, sanitized, data.productId);
2188
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
+ }
2189
2224
  async function updateSubscription(client, packageName, productId, data, updateMask) {
2190
2225
  validatePackageName(packageName);
2191
2226
  validateSku(productId);
2192
2227
  validateSubscriptionData(data);
2193
2228
  const sanitized = sanitizeSubscription(data);
2194
- return client.subscriptions.update(packageName, productId, sanitized, updateMask);
2229
+ const mask = updateMask || deriveSubscriptionUpdateMask(data);
2230
+ return client.subscriptions.update(packageName, productId, sanitized, mask);
2195
2231
  }
2196
2232
  async function deleteSubscription(client, packageName, productId) {
2197
2233
  validatePackageName(packageName);
@@ -2234,17 +2270,22 @@ async function createOffer(client, packageName, productId, basePlanId, data) {
2234
2270
  const sanitized = sanitizeOffer(data);
2235
2271
  return client.subscriptions.createOffer(packageName, productId, basePlanId, sanitized, data.offerId);
2236
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
+ }
2237
2277
  async function updateOffer(client, packageName, productId, basePlanId, offerId, data, updateMask) {
2238
2278
  validatePackageName(packageName);
2239
2279
  validateSku(productId);
2240
2280
  const sanitized = sanitizeOffer(data);
2281
+ const mask = updateMask || deriveOfferUpdateMask(data);
2241
2282
  return client.subscriptions.updateOffer(
2242
2283
  packageName,
2243
2284
  productId,
2244
2285
  basePlanId,
2245
2286
  offerId,
2246
2287
  sanitized,
2247
- updateMask
2288
+ mask
2248
2289
  );
2249
2290
  }
2250
2291
  async function deleteOffer(client, packageName, productId, basePlanId, offerId) {
@@ -2257,6 +2298,21 @@ async function activateOffer(client, packageName, productId, basePlanId, offerId
2257
2298
  validateSku(productId);
2258
2299
  return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);
2259
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
+ }
2260
2316
  async function deactivateOffer(client, packageName, productId, basePlanId, offerId) {
2261
2317
  validatePackageName(packageName);
2262
2318
  validateSku(productId);
@@ -2963,9 +3019,18 @@ async function createOneTimeProduct(client, packageName, data) {
2963
3019
  );
2964
3020
  }
2965
3021
  }
2966
- 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) {
2967
3031
  try {
2968
- return await client.oneTimeProducts.update(packageName, productId, data);
3032
+ const mask = updateMask || deriveOtpUpdateMask(data);
3033
+ return await client.oneTimeProducts.update(packageName, productId, data, mask);
2969
3034
  } catch (error) {
2970
3035
  throw new GpcError(
2971
3036
  `Failed to update one-time product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3023,9 +3088,10 @@ async function createOneTimeOffer(client, packageName, productId, data) {
3023
3088
  );
3024
3089
  }
3025
3090
  }
3026
- async function updateOneTimeOffer(client, packageName, productId, offerId, data) {
3091
+ async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask) {
3027
3092
  try {
3028
- 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);
3029
3095
  } catch (error) {
3030
3096
  throw new GpcError(
3031
3097
  `Failed to update offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3035,6 +3101,19 @@ async function updateOneTimeOffer(client, packageName, productId, offerId, data)
3035
3101
  );
3036
3102
  }
3037
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
+ }
3038
3117
  async function deleteOneTimeOffer(client, packageName, productId, offerId) {
3039
3118
  try {
3040
3119
  await client.oneTimeProducts.deleteOffer(packageName, productId, offerId);
@@ -3660,6 +3739,9 @@ export {
3660
3739
  detectOutputFormat,
3661
3740
  diffListings,
3662
3741
  diffListingsCommand,
3742
+ diffOneTimeProduct,
3743
+ diffReleases,
3744
+ diffSubscription,
3663
3745
  discoverPlugins,
3664
3746
  downloadGeneratedApk,
3665
3747
  downloadReport,