@gpc-cli/core 0.9.14 → 0.9.16

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.js CHANGED
@@ -150,6 +150,14 @@ ${formatYaml(value, indent + 1)}`;
150
150
  }
151
151
  return String(data);
152
152
  }
153
+ function cellValue(val) {
154
+ if (val === null || val === void 0) return "";
155
+ if (typeof val === "object") {
156
+ if (Array.isArray(val)) return val.length === 0 ? "" : JSON.stringify(val);
157
+ return JSON.stringify(val);
158
+ }
159
+ return String(val);
160
+ }
153
161
  function formatTable(data) {
154
162
  const rows = toRows(data);
155
163
  if (rows.length === 0) return "";
@@ -158,11 +166,11 @@ function formatTable(data) {
158
166
  const keys = Object.keys(firstRow);
159
167
  if (keys.length === 0) return "";
160
168
  const widths = keys.map(
161
- (key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
169
+ (key) => Math.max(key.length, ...rows.map((row) => cellValue(row[key]).length))
162
170
  );
163
171
  const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" ");
164
172
  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");
173
+ const body = rows.map((row) => keys.map((key, i) => cellValue(row[key]).padEnd(widths[i] ?? 0)).join(" ")).join("\n");
166
174
  return `${header}
167
175
  ${separator}
168
176
  ${body}`;
@@ -175,12 +183,12 @@ function formatMarkdown(data) {
175
183
  const keys = Object.keys(firstRow);
176
184
  if (keys.length === 0) return "";
177
185
  const widths = keys.map(
178
- (key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
186
+ (key) => Math.max(key.length, ...rows.map((row) => cellValue(row[key]).length))
179
187
  );
180
188
  const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" | ")} |`;
181
189
  const separator = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
182
190
  const body = rows.map(
183
- (row) => `| ${keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i] ?? 0)).join(" | ")} |`
191
+ (row) => `| ${keys.map((key, i) => cellValue(row[key]).padEnd(widths[i] ?? 0)).join(" | ")} |`
184
192
  ).join("\n");
185
193
  return `${header}
186
194
  ${separator}
@@ -2048,6 +2056,105 @@ function checkThreshold(value, threshold) {
2048
2056
 
2049
2057
  // src/commands/subscriptions.ts
2050
2058
  import { paginateAll as paginateAll2 } from "@gpc-cli/api";
2059
+ function coerceMoneyUnits(money) {
2060
+ if (money.units !== void 0 && typeof money.units !== "string") {
2061
+ return { ...money, units: String(money.units) };
2062
+ }
2063
+ return money;
2064
+ }
2065
+ function sanitizeSubscription(data) {
2066
+ const { ...cleaned } = data;
2067
+ delete cleaned["state"];
2068
+ delete cleaned["archived"];
2069
+ if (cleaned.basePlans) {
2070
+ cleaned.basePlans = cleaned.basePlans.map((bp) => {
2071
+ const { state: _s, archived: _a, ...cleanBp } = bp;
2072
+ if (cleanBp.regionalConfigs) {
2073
+ cleanBp.regionalConfigs = cleanBp.regionalConfigs.map((rc) => ({
2074
+ ...rc,
2075
+ price: coerceMoneyUnits(rc.price)
2076
+ }));
2077
+ }
2078
+ return cleanBp;
2079
+ });
2080
+ }
2081
+ return cleaned;
2082
+ }
2083
+ function sanitizeOffer(data) {
2084
+ const { state: _s, ...cleaned } = data;
2085
+ delete cleaned["archived"];
2086
+ return cleaned;
2087
+ }
2088
+ function parseDuration(iso) {
2089
+ const match = iso.match(/^P(\d+)D$/);
2090
+ return match?.[1] ? parseInt(match[1], 10) : 0;
2091
+ }
2092
+ var PRORATION_MODE_PREFIX = "SUBSCRIPTION_PRORATION_MODE_";
2093
+ var VALID_PRORATION_MODES = [
2094
+ "SUBSCRIPTION_PRORATION_MODE_CHARGE_ON_NEXT_BILLING_DATE",
2095
+ "SUBSCRIPTION_PRORATION_MODE_CHARGE_FULL_PRICE_IMMEDIATELY"
2096
+ ];
2097
+ function autoFixProrationMode(data) {
2098
+ if (!data.basePlans) return;
2099
+ for (const bp of data.basePlans) {
2100
+ const mode = bp.autoRenewingBasePlanType?.prorationMode;
2101
+ if (mode && !mode.startsWith(PRORATION_MODE_PREFIX)) {
2102
+ bp.autoRenewingBasePlanType.prorationMode = `${PRORATION_MODE_PREFIX}${mode}`;
2103
+ }
2104
+ if (bp.autoRenewingBasePlanType?.prorationMode) {
2105
+ const fullMode = bp.autoRenewingBasePlanType.prorationMode;
2106
+ if (!VALID_PRORATION_MODES.includes(fullMode)) {
2107
+ throw new GpcError(
2108
+ `Invalid prorationMode: "${fullMode}"`,
2109
+ "INVALID_SUBSCRIPTION_DATA",
2110
+ 2,
2111
+ `Valid values: ${VALID_PRORATION_MODES.join(", ")}`
2112
+ );
2113
+ }
2114
+ }
2115
+ }
2116
+ }
2117
+ function validateSubscriptionData(data) {
2118
+ autoFixProrationMode(data);
2119
+ if (data.listings) {
2120
+ for (const [lang, listing] of Object.entries(data.listings)) {
2121
+ if (listing.benefits && listing.benefits.length > 4) {
2122
+ throw new GpcError(
2123
+ `Listing "${lang}" has ${listing.benefits.length} benefits (max 4)`,
2124
+ "INVALID_SUBSCRIPTION_DATA",
2125
+ 2,
2126
+ "Google Play allows a maximum of 4 benefits per subscription listing"
2127
+ );
2128
+ }
2129
+ if (listing.description && listing.description.length > 80) {
2130
+ throw new GpcError(
2131
+ `Listing "${lang}" description is ${listing.description.length} chars (max 80)`,
2132
+ "INVALID_SUBSCRIPTION_DATA",
2133
+ 2,
2134
+ "Google Play limits subscription descriptions to 80 characters"
2135
+ );
2136
+ }
2137
+ }
2138
+ }
2139
+ if (data.basePlans) {
2140
+ for (const bp of data.basePlans) {
2141
+ const autoType = bp.autoRenewingBasePlanType;
2142
+ if (autoType?.gracePeriodDuration && autoType?.accountHoldDuration) {
2143
+ const grace = parseDuration(autoType.gracePeriodDuration);
2144
+ const hold = parseDuration(autoType.accountHoldDuration);
2145
+ const sum = grace + hold;
2146
+ if (sum < 30 || sum > 60) {
2147
+ throw new GpcError(
2148
+ `Base plan "${bp.basePlanId}": gracePeriodDuration (${grace}d) + accountHoldDuration (${hold}d) = ${sum}d (must be 30-60)`,
2149
+ "INVALID_SUBSCRIPTION_DATA",
2150
+ 2,
2151
+ "gracePeriodDuration + accountHoldDuration must sum to between P30D and P60D"
2152
+ );
2153
+ }
2154
+ }
2155
+ }
2156
+ }
2157
+ }
2051
2158
  async function listSubscriptions(client, packageName, options) {
2052
2159
  validatePackageName(packageName);
2053
2160
  if (options?.limit || options?.nextPage) {
@@ -2075,12 +2182,16 @@ async function getSubscription(client, packageName, productId) {
2075
2182
  }
2076
2183
  async function createSubscription(client, packageName, data) {
2077
2184
  validatePackageName(packageName);
2078
- return client.subscriptions.create(packageName, data, data.productId);
2185
+ validateSubscriptionData(data);
2186
+ const sanitized = sanitizeSubscription(data);
2187
+ return client.subscriptions.create(packageName, sanitized, data.productId);
2079
2188
  }
2080
2189
  async function updateSubscription(client, packageName, productId, data, updateMask) {
2081
2190
  validatePackageName(packageName);
2082
2191
  validateSku(productId);
2083
- return client.subscriptions.update(packageName, productId, data, updateMask);
2192
+ validateSubscriptionData(data);
2193
+ const sanitized = sanitizeSubscription(data);
2194
+ return client.subscriptions.update(packageName, productId, sanitized, updateMask);
2084
2195
  }
2085
2196
  async function deleteSubscription(client, packageName, productId) {
2086
2197
  validatePackageName(packageName);
@@ -2120,17 +2231,19 @@ async function getOffer(client, packageName, productId, basePlanId, offerId) {
2120
2231
  async function createOffer(client, packageName, productId, basePlanId, data) {
2121
2232
  validatePackageName(packageName);
2122
2233
  validateSku(productId);
2123
- return client.subscriptions.createOffer(packageName, productId, basePlanId, data, data.offerId);
2234
+ const sanitized = sanitizeOffer(data);
2235
+ return client.subscriptions.createOffer(packageName, productId, basePlanId, sanitized, data.offerId);
2124
2236
  }
2125
2237
  async function updateOffer(client, packageName, productId, basePlanId, offerId, data, updateMask) {
2126
2238
  validatePackageName(packageName);
2127
2239
  validateSku(productId);
2240
+ const sanitized = sanitizeOffer(data);
2128
2241
  return client.subscriptions.updateOffer(
2129
2242
  packageName,
2130
2243
  productId,
2131
2244
  basePlanId,
2132
2245
  offerId,
2133
- data,
2246
+ sanitized,
2134
2247
  updateMask
2135
2248
  );
2136
2249
  }