@gpc-cli/core 0.9.4 → 0.9.6
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 +29 -3
- package/dist/index.js +53 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -113,6 +113,25 @@ interface ReleaseStatusResult {
|
|
|
113
113
|
text: string;
|
|
114
114
|
}[];
|
|
115
115
|
}
|
|
116
|
+
interface DryRunUploadResult {
|
|
117
|
+
dryRun: true;
|
|
118
|
+
file: {
|
|
119
|
+
path: string;
|
|
120
|
+
valid: boolean;
|
|
121
|
+
errors: string[];
|
|
122
|
+
warnings: string[];
|
|
123
|
+
};
|
|
124
|
+
track: string;
|
|
125
|
+
currentReleases: {
|
|
126
|
+
versionCodes: string[];
|
|
127
|
+
status: string;
|
|
128
|
+
userFraction?: number;
|
|
129
|
+
}[];
|
|
130
|
+
plannedRelease: {
|
|
131
|
+
status: string;
|
|
132
|
+
userFraction?: number;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
116
135
|
declare function uploadRelease(client: PlayApiClient, packageName: string, filePath: string, options: {
|
|
117
136
|
track: string;
|
|
118
137
|
status?: string;
|
|
@@ -123,7 +142,8 @@ declare function uploadRelease(client: PlayApiClient, packageName: string, fileP
|
|
|
123
142
|
}[];
|
|
124
143
|
releaseName?: string;
|
|
125
144
|
mappingFile?: string;
|
|
126
|
-
|
|
145
|
+
dryRun?: boolean;
|
|
146
|
+
}): Promise<UploadResult | DryRunUploadResult>;
|
|
127
147
|
declare function getReleasesStatus(client: PlayApiClient, packageName: string, trackFilter?: string): Promise<ReleaseStatusResult[]>;
|
|
128
148
|
declare function promoteRelease(client: PlayApiClient, packageName: string, fromTrack: string, toTrack: string, options?: {
|
|
129
149
|
userFraction?: number;
|
|
@@ -220,12 +240,18 @@ interface PublishOptions {
|
|
|
220
240
|
notesDir?: string;
|
|
221
241
|
releaseName?: string;
|
|
222
242
|
mappingFile?: string;
|
|
243
|
+
dryRun?: boolean;
|
|
223
244
|
}
|
|
224
245
|
interface PublishResult {
|
|
225
246
|
validation: ValidateResult;
|
|
226
247
|
upload?: UploadResult;
|
|
227
248
|
}
|
|
228
|
-
|
|
249
|
+
interface DryRunPublishResult {
|
|
250
|
+
dryRun: true;
|
|
251
|
+
validation: ValidateResult;
|
|
252
|
+
upload: DryRunUploadResult;
|
|
253
|
+
}
|
|
254
|
+
declare function publish(client: PlayApiClient, packageName: string, filePath: string, options: PublishOptions): Promise<PublishResult | DryRunPublishResult>;
|
|
229
255
|
|
|
230
256
|
interface ReviewsFilterOptions {
|
|
231
257
|
stars?: number;
|
|
@@ -453,4 +479,4 @@ declare function writeAuditLog(entry: AuditEntry): Promise<void>;
|
|
|
453
479
|
*/
|
|
454
480
|
declare function createAuditEntry(command: string, args: Record<string, unknown>, app?: string): AuditEntry;
|
|
455
481
|
|
|
456
|
-
export { ApiError, type AppInfo, type AuditEntry, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunResult, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, GpcError, type ImageValidationResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, NetworkError, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, type ScaffoldOptions, type ScaffoldResult, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, acknowledgeProductPurchase, activateBasePlan, activateOffer, addTesters, cancelSubscriptionPurchase, checkThreshold, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createInAppProduct, createOffer, createSubscription, deactivateBasePlan, deactivateOffer, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteSubscription, detectOutputFormat, diffListings, discoverPlugins, downloadReport, exportReviews, formatOutput, getAppInfo, getCountryAvailability, getInAppProduct, getListings, getOffer, getProductPurchase, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listImages, listInAppProducts, listOffers, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactSensitive, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchVitalsErrors, syncInAppProducts, updateAppDetails, updateInAppProduct, updateListing, updateOffer, updateRollout, updateSubscription, updateUser, uploadImage, uploadRelease, validateImage, validatePreSubmission, validateReleaseNotes, validateUploadFile, writeAuditLog, writeListingsToDir };
|
|
482
|
+
export { ApiError, type AppInfo, type AuditEntry, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, GpcError, type ImageValidationResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, NetworkError, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, type ScaffoldOptions, type ScaffoldResult, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, acknowledgeProductPurchase, activateBasePlan, activateOffer, addTesters, cancelSubscriptionPurchase, checkThreshold, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createInAppProduct, createOffer, createSubscription, deactivateBasePlan, deactivateOffer, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteSubscription, detectOutputFormat, diffListings, discoverPlugins, downloadReport, exportReviews, formatOutput, getAppInfo, getCountryAvailability, getInAppProduct, getListings, getOffer, getProductPurchase, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listImages, listInAppProducts, listOffers, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactSensitive, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchVitalsErrors, syncInAppProducts, updateAppDetails, updateInAppProduct, updateListing, updateOffer, updateRollout, updateSubscription, updateUser, uploadImage, uploadRelease, validateImage, validatePreSubmission, validateReleaseNotes, validateUploadFile, writeAuditLog, writeListingsToDir };
|
package/dist/index.js
CHANGED
|
@@ -459,6 +459,38 @@ function formatSize(bytes) {
|
|
|
459
459
|
// src/commands/releases.ts
|
|
460
460
|
async function uploadRelease(client, packageName, filePath, options) {
|
|
461
461
|
const validation = await validateUploadFile(filePath);
|
|
462
|
+
if (options.dryRun) {
|
|
463
|
+
const plannedStatus = options.status || (options.userFraction ? "inProgress" : "completed");
|
|
464
|
+
let currentReleases = [];
|
|
465
|
+
const edit2 = await client.edits.insert(packageName);
|
|
466
|
+
try {
|
|
467
|
+
const trackData = await client.tracks.get(packageName, edit2.id, options.track);
|
|
468
|
+
currentReleases = (trackData.releases || []).map((r) => ({
|
|
469
|
+
versionCodes: r.versionCodes || [],
|
|
470
|
+
status: r.status,
|
|
471
|
+
...r.userFraction !== void 0 && { userFraction: r.userFraction }
|
|
472
|
+
}));
|
|
473
|
+
} catch {
|
|
474
|
+
} finally {
|
|
475
|
+
await client.edits.delete(packageName, edit2.id).catch(() => {
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
dryRun: true,
|
|
480
|
+
file: {
|
|
481
|
+
path: filePath,
|
|
482
|
+
valid: validation.valid,
|
|
483
|
+
errors: validation.errors,
|
|
484
|
+
warnings: validation.warnings
|
|
485
|
+
},
|
|
486
|
+
track: options.track,
|
|
487
|
+
currentReleases,
|
|
488
|
+
plannedRelease: {
|
|
489
|
+
status: plannedStatus,
|
|
490
|
+
...options.userFraction !== void 0 && { userFraction: options.userFraction }
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
462
494
|
if (!validation.valid) {
|
|
463
495
|
throw new Error(`File validation failed:
|
|
464
496
|
${validation.errors.join("\n")}`);
|
|
@@ -529,6 +561,9 @@ async function promoteRelease(client, packageName, fromTrack, toTrack, options)
|
|
|
529
561
|
if (!currentRelease) {
|
|
530
562
|
throw new Error(`No active release found on track "${fromTrack}"`);
|
|
531
563
|
}
|
|
564
|
+
if (options?.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {
|
|
565
|
+
throw new Error("Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)");
|
|
566
|
+
}
|
|
532
567
|
const release = {
|
|
533
568
|
versionCodes: currentRelease.versionCodes,
|
|
534
569
|
status: options?.userFraction ? "inProgress" : "completed",
|
|
@@ -565,6 +600,9 @@ async function updateRollout(client, packageName, track, action, userFraction) {
|
|
|
565
600
|
switch (action) {
|
|
566
601
|
case "increase":
|
|
567
602
|
if (!userFraction) throw new Error("--to <percentage> is required for rollout increase");
|
|
603
|
+
if (userFraction <= 0 || userFraction > 1) {
|
|
604
|
+
throw new Error("Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)");
|
|
605
|
+
}
|
|
568
606
|
newStatus = "inProgress";
|
|
569
607
|
newFraction = userFraction;
|
|
570
608
|
break;
|
|
@@ -785,7 +823,9 @@ async function readListingsFromDir(dir) {
|
|
|
785
823
|
const listings = [];
|
|
786
824
|
if (!await exists(dir)) return listings;
|
|
787
825
|
const entries = await readdir(dir);
|
|
826
|
+
const SAFE_LANG = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
788
827
|
for (const lang of entries) {
|
|
828
|
+
if (!SAFE_LANG.test(lang)) continue;
|
|
789
829
|
const langDir = join(dir, lang);
|
|
790
830
|
const langStat = await stat3(langDir);
|
|
791
831
|
if (!langStat.isDirectory()) continue;
|
|
@@ -1177,6 +1217,14 @@ async function publish(client, packageName, filePath, options) {
|
|
|
1177
1217
|
track: options.track || "internal",
|
|
1178
1218
|
notes: releaseNotes
|
|
1179
1219
|
});
|
|
1220
|
+
if (options.dryRun) {
|
|
1221
|
+
const upload2 = await uploadRelease(client, packageName, filePath, {
|
|
1222
|
+
track: options.track || "internal",
|
|
1223
|
+
userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : void 0,
|
|
1224
|
+
dryRun: true
|
|
1225
|
+
});
|
|
1226
|
+
return { dryRun: true, validation, upload: upload2 };
|
|
1227
|
+
}
|
|
1180
1228
|
if (!validation.valid) {
|
|
1181
1229
|
return { validation };
|
|
1182
1230
|
}
|
|
@@ -1560,7 +1608,11 @@ async function syncInAppProducts(client, packageName, dir, options) {
|
|
|
1560
1608
|
const localProducts = [];
|
|
1561
1609
|
for (const file of jsonFiles) {
|
|
1562
1610
|
const content = await readFile4(join3(dir, file), "utf-8");
|
|
1563
|
-
|
|
1611
|
+
try {
|
|
1612
|
+
localProducts.push(JSON.parse(content));
|
|
1613
|
+
} catch {
|
|
1614
|
+
throw new Error(`Failed to parse ${file}: invalid JSON`);
|
|
1615
|
+
}
|
|
1564
1616
|
}
|
|
1565
1617
|
const response = await client.inappproducts.list(packageName);
|
|
1566
1618
|
const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/output.ts","../src/plugins.ts","../src/commands/apps.ts","../src/utils/file-validation.ts","../src/commands/releases.ts","../src/utils/bcp47.ts","../src/utils/image-validation.ts","../src/utils/fastlane.ts","../src/commands/listings.ts","../src/utils/release-notes.ts","../src/commands/validate.ts","../src/commands/publish.ts","../src/commands/reviews.ts","../src/commands/vitals.ts","../src/commands/subscriptions.ts","../src/commands/iap.ts","../src/commands/purchases.ts","../src/commands/pricing.ts","../src/commands/reports.ts","../src/commands/users.ts","../src/commands/testers.ts","../src/utils/safe-path.ts","../src/commands/plugin-scaffold.ts","../src/audit.ts"],"sourcesContent":["export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\n\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import type { OutputFormat } from \"@gpc-cli/config\";\nimport process from \"node:process\";\n\nexport function detectOutputFormat(): OutputFormat {\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nexport function formatOutput(data: unknown, format: OutputFormat, redact = true): string {\n const safe = redact ? redactSensitive(data) : data;\n switch (format) {\n case \"json\":\n return formatJson(safe);\n case \"yaml\":\n return formatYaml(safe);\n case \"markdown\":\n return formatMarkdown(safe);\n case \"table\":\n return formatTable(safe);\n default:\n return formatJson(safe);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field redaction\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_KEYS = new Set([\n \"private_key\",\n \"privateKey\",\n \"private_key_id\",\n \"privateKeyId\",\n \"accessToken\",\n \"access_token\",\n \"refreshToken\",\n \"refresh_token\",\n \"client_secret\",\n \"clientSecret\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n]);\n\nconst REDACTED = \"[REDACTED]\";\n\n/** Recursively redact sensitive fields from data before output. */\nexport function redactSensitive(data: unknown): unknown {\n if (data === null || data === undefined) return data;\n\n if (typeof data === \"string\") return data;\n if (typeof data === \"number\" || typeof data === \"boolean\") return data;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitive(item));\n }\n\n if (typeof data === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (SENSITIVE_KEYS.has(key) && typeof value === \"string\") {\n result[key] = REDACTED;\n } else {\n result[key] = redactSensitive(value);\n }\n }\n return result;\n }\n\n return data;\n}\n\nfunction formatJson(data: unknown): string {\n return JSON.stringify(data, null, 2);\n}\n\nfunction formatYaml(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return \"null\";\n }\n\n if (typeof data === \"string\") {\n return data.includes(\"\\n\")\n ? `|\\n${data\n .split(\"\\n\")\n .map((l) => `${\" \".repeat(indent + 1)}${l}`)\n .join(\"\\n\")}`\n : data;\n }\n\n if (typeof data === \"number\" || typeof data === \"boolean\") {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return \"[]\";\n return data\n .map((item) => {\n const value = formatYaml(item, indent + 1);\n const prefix = `${\" \".repeat(indent)}- `;\n if (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n const lines = value.split(\"\\n\");\n return `${prefix}${lines[0]}\\n${lines\n .slice(1)\n .map((l) => `${\" \".repeat(indent)} ${l}`)\n .join(\"\\n\")}`;\n }\n return `${prefix}${value}`;\n })\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return \"{}\";\n return entries\n .map(([key, value]) => {\n if (typeof value === \"object\" && value !== null) {\n return `${\" \".repeat(indent)}${key}:\\n${formatYaml(value, indent + 1)}`;\n }\n return `${\" \".repeat(indent)}${key}: ${formatYaml(value, indent)}`;\n })\n .join(\"\\n\");\n }\n\n return String(data);\n}\n\nfunction formatTable(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const body = rows\n .map((row) => keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" \"))\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction formatMarkdown(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" | \")} |`;\n const separator = `| ${widths.map((w) => \"-\".repeat(w)).join(\" | \")} |`;\n const body = rows\n .map(\n (row) =>\n `| ${keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" | \")} |`,\n )\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction toRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) {\n return data.filter(\n (item): item is Record<string, unknown> => typeof item === \"object\" && item !== null,\n );\n }\n if (typeof data === \"object\" && data !== null) {\n return [data as Record<string, unknown>];\n }\n return [];\n}\n","import type {\n GpcPlugin,\n PluginHooks,\n BeforeCommandHandler,\n AfterCommandHandler,\n ErrorHandler,\n BeforeRequestHandler,\n AfterResponseHandler,\n CommandEvent,\n CommandResult,\n PluginError,\n PluginCommand,\n PluginManifest,\n PluginPermission,\n RequestEvent,\n ResponseEvent,\n} from \"@gpc-cli/plugin-sdk\";\nimport { GpcError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Plugin Manager — orchestrates discovery, loading, and lifecycle\n// ---------------------------------------------------------------------------\n\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private beforeHandlers: BeforeCommandHandler[] = [];\n private afterHandlers: AfterCommandHandler[] = [];\n private errorHandlers: ErrorHandler[] = [];\n private beforeRequestHandlers: BeforeRequestHandler[] = [];\n private afterResponseHandlers: AfterResponseHandler[] = [];\n private registeredCommands: PluginCommand[] = [];\n\n /** Load and register a plugin */\n async load(plugin: GpcPlugin, manifest?: PluginManifest): Promise<void> {\n const isTrusted = manifest?.trusted ?? plugin.name.startsWith(\"@gpc-cli/\");\n\n if (!isTrusted && manifest?.permissions) {\n validatePermissions(manifest.permissions);\n }\n\n const hooks = createHooks(\n this.beforeHandlers,\n this.afterHandlers,\n this.errorHandlers,\n this.beforeRequestHandlers,\n this.afterResponseHandlers,\n this.registeredCommands,\n );\n\n await plugin.register(hooks);\n\n this.plugins.push({\n name: plugin.name,\n version: plugin.version,\n trusted: isTrusted,\n });\n }\n\n /** Run all beforeCommand handlers */\n async runBeforeCommand(event: CommandEvent): Promise<void> {\n for (const handler of this.beforeHandlers) {\n await handler(event);\n }\n }\n\n /** Run all afterCommand handlers */\n async runAfterCommand(event: CommandEvent, result: CommandResult): Promise<void> {\n for (const handler of this.afterHandlers) {\n await handler(event, result);\n }\n }\n\n /** Run all onError handlers */\n async runOnError(event: CommandEvent, error: PluginError): Promise<void> {\n for (const handler of this.errorHandlers) {\n try {\n await handler(event, error);\n } catch {\n // Don't let error handlers crash the process\n }\n }\n }\n\n /** Run all beforeRequest handlers */\n async runBeforeRequest(event: RequestEvent): Promise<void> {\n for (const handler of this.beforeRequestHandlers) {\n try {\n await handler(event);\n } catch {\n // Don't let request hooks block API calls\n }\n }\n }\n\n /** Run all afterResponse handlers */\n async runAfterResponse(event: RequestEvent, response: ResponseEvent): Promise<void> {\n for (const handler of this.afterResponseHandlers) {\n try {\n await handler(event, response);\n } catch {\n // Don't let response hooks crash the process\n }\n }\n }\n\n /** Get commands registered by plugins */\n getRegisteredCommands(): PluginCommand[] {\n return [...this.registeredCommands];\n }\n\n /** Get list of loaded plugins */\n getLoadedPlugins(): LoadedPlugin[] {\n return [...this.plugins];\n }\n\n /** Whether any request/response hooks are registered */\n hasRequestHooks(): boolean {\n return this.beforeRequestHandlers.length > 0 || this.afterResponseHandlers.length > 0;\n }\n\n /** Reset (for testing) */\n reset(): void {\n this.plugins = [];\n this.beforeHandlers = [];\n this.afterHandlers = [];\n this.errorHandlers = [];\n this.beforeRequestHandlers = [];\n this.afterResponseHandlers = [];\n this.registeredCommands = [];\n }\n}\n\nexport interface LoadedPlugin {\n name: string;\n version: string;\n trusted: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Hook factory\n// ---------------------------------------------------------------------------\n\nfunction createHooks(\n beforeHandlers: BeforeCommandHandler[],\n afterHandlers: AfterCommandHandler[],\n errorHandlers: ErrorHandler[],\n beforeRequestHandlers: BeforeRequestHandler[],\n afterResponseHandlers: AfterResponseHandler[],\n registeredCommands: PluginCommand[],\n): PluginHooks {\n return {\n beforeCommand(handler) {\n beforeHandlers.push(handler);\n },\n afterCommand(handler) {\n afterHandlers.push(handler);\n },\n onError(handler) {\n errorHandlers.push(handler);\n },\n beforeRequest(handler) {\n beforeRequestHandlers.push(handler);\n },\n afterResponse(handler) {\n afterResponseHandlers.push(handler);\n },\n registerCommands(registrar) {\n const registry = {\n add(cmd: PluginCommand) {\n registeredCommands.push(cmd);\n },\n };\n registrar(registry);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Permission validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PERMISSIONS: ReadonlySet<string> = new Set<PluginPermission>([\n \"read:config\",\n \"write:config\",\n \"read:auth\",\n \"api:read\",\n \"api:write\",\n \"commands:register\",\n \"hooks:beforeCommand\",\n \"hooks:afterCommand\",\n \"hooks:onError\",\n \"hooks:beforeRequest\",\n \"hooks:afterResponse\",\n]);\n\nfunction validatePermissions(permissions: PluginPermission[]): void {\n for (const perm of permissions) {\n if (!VALID_PERMISSIONS.has(perm)) {\n throw new GpcError(\n `Unknown plugin permission: \"${perm}\"`,\n \"PLUGIN_INVALID_PERMISSION\",\n 10,\n `Valid permissions: ${[...VALID_PERMISSIONS].join(\", \")}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin discovery\n// ---------------------------------------------------------------------------\n\nexport interface DiscoverPluginsOptions {\n /** Plugin names from config file */\n configPlugins?: string[];\n\n /** Working directory for node_modules scanning */\n cwd?: string;\n}\n\n/**\n * Discover plugins from multiple sources:\n * 1. Explicit config: gpc.config.ts → plugins: [...]\n * 2. Convention: node_modules/@gpc-cli/plugin-*\n * 3. Convention: node_modules/gpc-plugin-*\n */\nexport async function discoverPlugins(options?: DiscoverPluginsOptions): Promise<GpcPlugin[]> {\n const plugins: GpcPlugin[] = [];\n const seen = new Set<string>();\n\n // Source 1: Explicit config plugins\n if (options?.configPlugins) {\n for (const name of options.configPlugins) {\n if (seen.has(name)) continue;\n try {\n const mod = await import(name);\n const plugin = resolvePlugin(mod);\n if (plugin) {\n plugins.push(plugin);\n seen.add(name);\n }\n } catch {\n // Plugin not found — skip silently\n }\n }\n }\n\n return plugins;\n}\n\n/**\n * Resolve a plugin from a module.\n * Supports: default export, named `plugin` export, or the module itself as a plugin.\n */\nfunction resolvePlugin(mod: unknown): GpcPlugin | undefined {\n if (!mod || typeof mod !== \"object\") return undefined;\n\n const m = mod as Record<string, unknown>;\n\n // Check default export\n if (isPlugin(m[\"default\"])) return m[\"default\"];\n\n // Check named `plugin` export\n if (isPlugin(m[\"plugin\"])) return m[\"plugin\"];\n\n // Check if module itself is a plugin\n if (isPlugin(m)) return m as unknown as GpcPlugin;\n\n return undefined;\n}\n\nfunction isPlugin(obj: unknown): obj is GpcPlugin {\n if (!obj || typeof obj !== \"object\") return false;\n const p = obj as Record<string, unknown>;\n return (\n typeof p[\"name\"] === \"string\" &&\n typeof p[\"version\"] === \"string\" &&\n typeof p[\"register\"] === \"function\"\n );\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\n\nexport interface AppInfo {\n packageName: string;\n title?: string;\n defaultLanguage?: string;\n contactEmail?: string;\n}\n\nexport async function getAppInfo(client: PlayApiClient, packageName: string): Promise<AppInfo> {\n // Create an edit to read app details (Google Play requires an edit context)\n const edit = await client.edits.insert(packageName);\n try {\n const details = await client.details.get(packageName, edit.id);\n // Delete the edit since we're only reading\n await client.edits.delete(packageName, edit.id);\n return {\n packageName,\n title: details.title,\n defaultLanguage: details.defaultLanguage,\n contactEmail: details.contactEmail,\n };\n } catch (error) {\n // Clean up edit on failure\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 150 * 1024 * 1024; // 150 MB\nconst MAX_AAB_SIZE = 500 * 1024 * 1024; // 500 MB (Play Store limit for AABs)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 150 MB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 500 MB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes (ZIP format — both AAB and APK are ZIP-based)\n if (sizeBytes > 0) {\n try {\n const fd = await readFile(filePath, { flag: \"r\" });\n const header = fd.subarray(0, 4);\n\n if (!header.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, Release, Track } from \"@gpc-cli/api\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n },\n): Promise<UploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n if (!validation.valid) {\n throw new Error(`File validation failed:\\n${validation.errors.join(\"\\n\")}`);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n // Upload the bundle\n const bundle = await client.bundles.upload(packageName, edit.id, filePath);\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n );\n }\n\n // Create release and assign to track\n const release: Release = {\n versionCodes: [String(bundle.versionCode)],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n // Validate and commit\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: { userFraction?: number; releaseNotes?: { language: string; text: string }[] },\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active release found on track \"${fromTrack}\"`);\n }\n\n // Create release on target track\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.userFraction ? \"inProgress\" : \"completed\") as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active rollout found on track \"${track}\"`);\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction) throw new Error(\"--to <percentage> is required for rollout increase\");\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","export const GOOGLE_PLAY_LANGUAGES: string[] = [\n \"af\",\n \"am\",\n \"ar\",\n \"hy-AM\",\n \"az-AZ\",\n \"eu-ES\",\n \"be\",\n \"bn-BD\",\n \"bg\",\n \"my-MM\",\n \"ca\",\n \"zh-HK\",\n \"zh-CN\",\n \"zh-TW\",\n \"hr\",\n \"cs-CZ\",\n \"da-DK\",\n \"nl-NL\",\n \"en-AU\",\n \"en-CA\",\n \"en-IN\",\n \"en-SG\",\n \"en-GB\",\n \"en-US\",\n \"et\",\n \"fil\",\n \"fi-FI\",\n \"fr-FR\",\n \"fr-CA\",\n \"gl-ES\",\n \"ka-GE\",\n \"de-DE\",\n \"el-GR\",\n \"gu\",\n \"iw-IL\",\n \"hi-IN\",\n \"hu-HU\",\n \"is-IS\",\n \"id\",\n \"it-IT\",\n \"ja-JP\",\n \"kn-IN\",\n \"kk\",\n \"km-KH\",\n \"ko-KR\",\n \"ky-KG\",\n \"lo-LA\",\n \"lv\",\n \"lt\",\n \"mk-MK\",\n \"ms\",\n \"ms-MY\",\n \"ml-IN\",\n \"mr-IN\",\n \"mn-MN\",\n \"ne-NP\",\n \"no-NO\",\n \"fa\",\n \"pl-PL\",\n \"pt-BR\",\n \"pt-PT\",\n \"pa\",\n \"ro\",\n \"rm\",\n \"ru-RU\",\n \"sr\",\n \"si-LK\",\n \"sk\",\n \"sl\",\n \"es-419\",\n \"es-ES\",\n \"es-US\",\n \"sw\",\n \"sv-SE\",\n \"ta-IN\",\n \"te-IN\",\n \"th\",\n \"tr-TR\",\n \"uk\",\n \"ur\",\n \"vi\",\n \"zu\",\n];\n\nexport function isValidBcp47(tag: string): boolean {\n return GOOGLE_PLAY_LANGUAGES.includes(tag);\n}\n","import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface ImageValidationResult {\n valid: boolean;\n warnings: string[];\n errors: string[];\n}\n\n// Google Play image size limits\nconst IMAGE_SIZE_LIMITS: Record<string, { maxBytes: number; label: string }> = {\n icon: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n featureGraphic: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n tvBanner: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n phoneScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n sevenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tvScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n wearScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n};\n\nconst VALID_EXTENSIONS = new Set([\".png\", \".jpg\", \".jpeg\"]);\nconst LARGE_IMAGE_THRESHOLD = 2 * 1024 * 1024; // 2 MB\n\nexport async function validateImage(\n filePath: string,\n imageType?: string,\n): Promise<ImageValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n if (!VALID_EXTENSIONS.has(ext)) {\n errors.push(`Unsupported image format \"${ext}\". Use PNG or JPEG.`);\n }\n\n // Check file exists and size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"Image file is empty (0 bytes)\");\n }\n } catch {\n errors.push(`Image file not found: ${filePath}`);\n return { valid: false, errors, warnings };\n }\n\n // Check size limits per image type\n if (imageType && sizeBytes > 0) {\n const limit = IMAGE_SIZE_LIMITS[imageType];\n if (limit && sizeBytes > limit.maxBytes) {\n errors.push(`Image exceeds ${limit.label} limit for ${imageType} (${formatSize(sizeBytes)})`);\n }\n }\n\n // Warn about large images\n if (sizeBytes > LARGE_IMAGE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large image (${formatSize(sizeBytes)}). Consider optimizing for faster upload and better store performance.`,\n );\n }\n\n // PNG optimization warning\n if (ext === \".png\" && sizeBytes > 512 * 1024) {\n warnings.push(\n \"PNG file is over 512 KB. Consider compressing with tools like pngquant or optipng.\",\n );\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import { readFile, writeFile, mkdir, readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Listing } from \"@gpc-cli/api\";\n\nconst FILE_MAP: Record<string, keyof Omit<Listing, \"language\">> = {\n \"title.txt\": \"title\",\n \"short_description.txt\": \"shortDescription\",\n \"full_description.txt\": \"fullDescription\",\n \"video.txt\": \"video\",\n};\n\nconst FIELD_TO_FILE: Record<string, string> = Object.fromEntries(\n Object.entries(FILE_MAP).map(([file, field]) => [field, file]),\n);\n\nexport interface ListingDiff {\n language: string;\n field: string;\n local: string;\n remote: string;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readListingsFromDir(dir: string): Promise<Listing[]> {\n const listings: Listing[] = [];\n\n if (!(await exists(dir))) return listings;\n\n const entries = await readdir(dir);\n for (const lang of entries) {\n const langDir = join(dir, lang);\n const langStat = await stat(langDir);\n if (!langStat.isDirectory()) continue;\n\n const listing: Listing = {\n language: lang,\n title: \"\",\n shortDescription: \"\",\n fullDescription: \"\",\n };\n\n for (const [fileName, field] of Object.entries(FILE_MAP)) {\n const filePath = join(langDir, fileName);\n if (await exists(filePath)) {\n const content = await readFile(filePath, \"utf-8\");\n (listing as unknown as Record<string, string>)[field] = content.trimEnd();\n }\n }\n\n listings.push(listing);\n }\n\n return listings;\n}\n\nexport async function writeListingsToDir(dir: string, listings: Listing[]): Promise<void> {\n for (const listing of listings) {\n const langDir = join(dir, listing.language);\n await mkdir(langDir, { recursive: true });\n\n for (const [field, fileName] of Object.entries(FIELD_TO_FILE)) {\n const value = (listing as unknown as Record<string, string>)[field];\n if (value !== undefined && value !== \"\") {\n await writeFile(join(langDir, fileName), value + \"\\n\", \"utf-8\");\n }\n }\n }\n}\n\nexport function diffListings(local: Listing[], remote: Listing[]): ListingDiff[] {\n const diffs: ListingDiff[] = [];\n const remoteMap = new Map(remote.map((l) => [l.language, l]));\n const localMap = new Map(local.map((l) => [l.language, l]));\n\n // Check all local listings against remote\n for (const localListing of local) {\n const remoteListing = remoteMap.get(localListing.language);\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const localVal = (\n (localListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n const remoteVal = remoteListing\n ? ((remoteListing as unknown as Record<string, string>)[field] ?? \"\").toString()\n : \"\";\n if (localVal !== remoteVal) {\n diffs.push({\n language: localListing.language,\n field,\n local: localVal,\n remote: remoteVal,\n });\n }\n }\n }\n\n // Check for remote-only languages\n for (const remoteListing of remote) {\n if (!localMap.has(remoteListing.language)) {\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const remoteVal = (\n (remoteListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n if (remoteVal) {\n diffs.push({\n language: remoteListing.language,\n field,\n local: \"\",\n remote: remoteVal,\n });\n }\n }\n }\n }\n\n return diffs;\n}\n","import type {\n PlayApiClient,\n Listing,\n Image,\n ImageType,\n AppDetails,\n CountryAvailability,\n} from \"@gpc-cli/api\";\nimport { isValidBcp47 } from \"../utils/bcp47.js\";\nimport { validateImage } from \"../utils/image-validation.js\";\nimport { readListingsFromDir, writeListingsToDir, diffListings } from \"../utils/fastlane.js\";\nimport type { ListingDiff } from \"../utils/fastlane.js\";\n\nexport interface ListingsResult {\n listings: Listing[];\n}\n\nexport interface PushResult {\n updated: number;\n languages: string[];\n}\n\nexport interface DryRunResult {\n diffs: ListingDiff[];\n}\n\nfunction validateLanguage(lang: string): void {\n if (!isValidBcp47(lang)) {\n throw new Error(`Invalid language tag \"${lang}\". Must be a valid Google Play BCP 47 code.`);\n }\n}\n\nexport async function getListings(\n client: PlayApiClient,\n packageName: string,\n language?: string,\n): Promise<Listing[]> {\n const edit = await client.edits.insert(packageName);\n try {\n let listings: Listing[];\n if (language) {\n validateLanguage(language);\n const listing = await client.listings.get(packageName, edit.id, language);\n listings = [listing];\n } else {\n listings = await client.listings.list(packageName, edit.id);\n }\n await client.edits.delete(packageName, edit.id);\n return listings;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n data: Partial<Omit<Listing, \"language\">>,\n): Promise<Listing> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const listing = await client.listings.patch(packageName, edit.id, language, data);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return listing;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.listings.delete(packageName, edit.id, language);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pullListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n): Promise<ListingsResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const listings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n await writeListingsToDir(dir, listings);\n return { listings };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pushListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<PushResult | DryRunResult> {\n const localListings = await readListingsFromDir(dir);\n\n if (localListings.length === 0) {\n throw new Error(`No listings found in directory \"${dir}\"`);\n }\n\n // Validate all languages\n for (const listing of localListings) {\n validateLanguage(listing.language);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n if (options?.dryRun) {\n const remoteListings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n const diffs = diffListings(localListings, remoteListings);\n return { diffs };\n }\n\n for (const listing of localListings) {\n const { language, ...data } = listing;\n await client.listings.update(packageName, edit.id, language, data);\n }\n\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n updated: localListings.length,\n languages: localListings.map((l) => l.language),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listImages(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n): Promise<Image[]> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const images = await client.images.list(packageName, edit.id, language, imageType);\n await client.edits.delete(packageName, edit.id);\n return images;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n): Promise<Image> {\n validateLanguage(language);\n\n // Validate image before upload\n const imageCheck = await validateImage(filePath, imageType);\n if (!imageCheck.valid) {\n throw new Error(`Image validation failed: ${imageCheck.errors.join(\"; \")}`);\n }\n if (imageCheck.warnings.length > 0) {\n for (const w of imageCheck.warnings) {\n console.warn(`Warning: ${w}`);\n }\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return image;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.images.delete(packageName, edit.id, language, imageType, imageId);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getCountryAvailability(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<CountryAvailability> {\n const edit = await client.edits.insert(packageName);\n try {\n const availability = await client.countryAvailability.get(packageName, edit.id, track);\n await client.edits.delete(packageName, edit.id);\n return availability;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateAppDetails(\n client: PlayApiClient,\n packageName: string,\n details: Partial<AppDetails>,\n): Promise<AppDetails> {\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.details.patch(packageName, edit.id, details);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { extname, basename, join } from \"node:path\";\n\nexport interface ReleaseNote {\n language: string;\n text: string;\n}\n\nexport interface ReleaseNotesValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nconst MAX_NOTES_LENGTH = 500;\n\nexport async function readReleaseNotesFromDir(dir: string): Promise<ReleaseNote[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new Error(`Release notes directory not found: ${dir}`);\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n if (extname(entry) !== \".txt\") continue;\n\n const language = basename(entry, \".txt\");\n const filePath = join(dir, entry);\n\n const stats = await stat(filePath);\n if (!stats.isFile()) continue;\n\n const text = (await readFile(filePath, \"utf-8\")).trim();\n if (text.length === 0) continue;\n\n notes.push({ language, text });\n }\n\n return notes;\n}\n\nexport function validateReleaseNotes(notes: ReleaseNote[]): ReleaseNotesValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const seen = new Set<string>();\n for (const note of notes) {\n if (seen.has(note.language)) {\n errors.push(`Duplicate language code: ${note.language}`);\n }\n seen.add(note.language);\n\n if (note.text.length > MAX_NOTES_LENGTH) {\n errors.push(\n `Release notes for \"${note.language}\" exceed ${MAX_NOTES_LENGTH} chars (${note.text.length} chars)`,\n );\n }\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n","import { stat } from \"node:fs/promises\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { readReleaseNotesFromDir, validateReleaseNotes } from \"../utils/release-notes.js\";\n\nexport interface ValidateOptions {\n filePath: string;\n mappingFile?: string;\n track?: string;\n notes?: { language: string; text: string }[];\n notesDir?: string;\n}\n\nexport interface ValidateCheck {\n name: string;\n passed: boolean;\n message: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n checks: ValidateCheck[];\n}\n\nconst STANDARD_TRACKS = new Set([\n \"internal\",\n \"alpha\",\n \"beta\",\n \"production\",\n // Form factor tracks\n \"wear:internal\",\n \"wear:alpha\",\n \"wear:beta\",\n \"wear:production\",\n \"automotive:internal\",\n \"automotive:alpha\",\n \"automotive:beta\",\n \"automotive:production\",\n \"tv:internal\",\n \"tv:alpha\",\n \"tv:beta\",\n \"tv:production\",\n \"android_xr:internal\",\n \"android_xr:alpha\",\n \"android_xr:beta\",\n \"android_xr:production\",\n]);\nconst TRACK_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_:-]*$/;\n\nexport async function validatePreSubmission(options: ValidateOptions): Promise<ValidateResult> {\n const checks: ValidateCheck[] = [];\n\n // 1. File validation\n const fileResult = await validateUploadFile(options.filePath);\n checks.push({\n name: \"file\",\n passed: fileResult.valid,\n message: fileResult.valid\n ? `Valid ${fileResult.fileType} file (${formatSize(fileResult.sizeBytes)})`\n : fileResult.errors.join(\"; \"),\n });\n\n // 2. Mapping file\n if (options.mappingFile) {\n try {\n const stats = await stat(options.mappingFile);\n checks.push({\n name: \"mapping\",\n passed: stats.isFile(),\n message: stats.isFile()\n ? `Mapping file found (${formatSize(stats.size)})`\n : \"Mapping path is not a file\",\n });\n } catch {\n checks.push({\n name: \"mapping\",\n passed: false,\n message: `Mapping file not found: ${options.mappingFile}`,\n });\n }\n }\n\n // 3. Track validation\n if (options.track) {\n const isValid = STANDARD_TRACKS.has(options.track) || TRACK_PATTERN.test(options.track);\n checks.push({\n name: \"track\",\n passed: isValid,\n message: isValid\n ? `Track \"${options.track}\" is valid`\n : `Invalid track name \"${options.track}\". Use: internal, alpha, beta, production, or a custom track ID`,\n });\n }\n\n // 4. Release notes validation\n let resolvedNotes = options.notes;\n if (options.notesDir) {\n try {\n resolvedNotes = await readReleaseNotesFromDir(options.notesDir);\n checks.push({\n name: \"notes-dir\",\n passed: true,\n message: `Read release notes for ${resolvedNotes.length} language(s)`,\n });\n } catch (err) {\n checks.push({\n name: \"notes-dir\",\n passed: false,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n if (resolvedNotes && resolvedNotes.length > 0) {\n const notesResult = validateReleaseNotes(resolvedNotes);\n checks.push({\n name: \"notes\",\n passed: notesResult.valid,\n message: notesResult.valid\n ? `Release notes valid (${resolvedNotes.length} language(s))`\n : notesResult.errors.join(\"; \"),\n });\n }\n\n return {\n valid: checks.every((c) => c.passed),\n checks,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\nimport { uploadRelease } from \"./releases.js\";\nimport type { UploadResult } from \"./releases.js\";\nimport { validatePreSubmission } from \"./validate.js\";\nimport type { ValidateResult } from \"./validate.js\";\nimport { readReleaseNotesFromDir } from \"../utils/release-notes.js\";\n\nexport interface PublishOptions {\n track?: string;\n rolloutPercent?: number;\n notes?: string;\n notesDir?: string;\n releaseName?: string;\n mappingFile?: string;\n}\n\nexport interface PublishResult {\n validation: ValidateResult;\n upload?: UploadResult;\n}\n\nexport async function publish(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: PublishOptions,\n): Promise<PublishResult> {\n // Resolve release notes\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.notesDir) {\n releaseNotes = await readReleaseNotesFromDir(options.notesDir);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n // Validate\n const validation = await validatePreSubmission({\n filePath,\n mappingFile: options.mappingFile,\n track: options.track || \"internal\",\n notes: releaseNotes,\n });\n\n if (!validation.valid) {\n return { validation };\n }\n\n // Upload\n const upload = await uploadRelease(client, packageName, filePath, {\n track: options.track || \"internal\",\n userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : undefined,\n releaseNotes,\n releaseName: options.releaseName,\n mappingFile: options.mappingFile,\n });\n\n return { validation, upload };\n}\n","import type { PlayApiClient, Review, ReviewsListOptions, ReviewReplyResponse } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ReviewsFilterOptions {\n stars?: number;\n language?: string;\n since?: string;\n translationLanguage?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport interface ReviewExportOptions extends ReviewsFilterOptions {\n format?: \"json\" | \"csv\";\n}\n\nexport async function listReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewsFilterOptions,\n): Promise<Review[]> {\n const apiOptions: ReviewsListOptions = {};\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n if (options?.maxResults) apiOptions.maxResults = options.maxResults;\n\n const response = await client.reviews.list(packageName, apiOptions);\n let reviews = response.reviews || [];\n\n // Client-side filters\n if (options?.stars !== undefined) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && userComment.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && Number(userComment.lastModified.seconds) >= sinceTime;\n });\n }\n\n return reviews;\n}\n\nexport async function getReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n translationLanguage?: string,\n): Promise<Review> {\n return client.reviews.get(packageName, reviewId, translationLanguage);\n}\n\nconst MAX_REPLY_LENGTH = 350;\n\nexport async function replyToReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n replyText: string,\n): Promise<ReviewReplyResponse> {\n if (replyText.length > MAX_REPLY_LENGTH) {\n throw new Error(\n `Reply text exceeds ${MAX_REPLY_LENGTH} characters (${replyText.length}). Google Play limits replies to ${MAX_REPLY_LENGTH} characters.`,\n );\n }\n if (replyText.length === 0) {\n throw new Error(\"Reply text cannot be empty.\");\n }\n return client.reviews.reply(packageName, reviewId, replyText);\n}\n\nexport async function exportReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewExportOptions,\n): Promise<string> {\n const { items: allReviews } = await paginateAll<Review>(async (pageToken) => {\n const apiOptions: ReviewsListOptions = { token: pageToken };\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n const response = await client.reviews.list(packageName, apiOptions);\n return {\n items: response.reviews || [],\n nextPageToken: response.tokenPagination?.nextPageToken,\n };\n });\n\n let filtered = allReviews;\n\n if (options?.stars !== undefined) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && uc.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && Number(uc.lastModified.seconds) >= sinceTime;\n });\n }\n\n if (options?.format === \"csv\") {\n return reviewsToCsv(filtered);\n }\n\n return JSON.stringify(filtered, null, 2);\n}\n\nfunction reviewsToCsv(reviews: Review[]): string {\n const header = \"reviewId,authorName,starRating,text,language,date,device,appVersionName\";\n const rows = reviews.map((r) => {\n const uc = r.comments?.[0]?.userComment;\n const fields = [\n r.reviewId,\n csvEscape(r.authorName),\n uc?.starRating ?? \"\",\n csvEscape(uc?.text ?? \"\"),\n uc?.reviewerLanguage ?? \"\",\n uc ? new Date(Number(uc.lastModified.seconds) * 1000).toISOString() : \"\",\n csvEscape(uc?.device ?? \"\"),\n csvEscape(uc?.appVersionName ?? \"\"),\n ];\n return fields.join(\",\");\n });\n return [header, ...rows].join(\"\\n\");\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(\",\") || value.includes('\"') || value.includes(\"\\n\")) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","import type {\n ReportingApiClient,\n VitalsMetricSet,\n MetricSetQuery,\n MetricSetResponse,\n MetricRow,\n AnomalyDetectionResponse,\n ErrorIssuesResponse,\n ReportingDimension,\n ReportingAggregation,\n} from \"@gpc-cli/api\";\n\nexport interface VitalsQueryOptions {\n dimension?: ReportingDimension;\n days?: number;\n aggregation?: ReportingAggregation;\n}\n\nexport interface VitalsOverview {\n crashRate?: MetricRow[];\n anrRate?: MetricRow[];\n slowStartRate?: MetricRow[];\n slowRenderingRate?: MetricRow[];\n excessiveWakeupRate?: MetricRow[];\n stuckWakelockRate?: MetricRow[];\n}\n\nexport interface ThresholdResult {\n breached: boolean;\n value: number | undefined;\n threshold: number;\n}\n\nfunction buildQuery(options?: VitalsQueryOptions): MetricSetQuery {\n const query: MetricSetQuery = {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n };\n\n if (options?.dimension) {\n query.dimensions = [options.dimension];\n }\n\n if (options?.days) {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - options.days);\n query.timelineSpec = {\n aggregationPeriod: options.aggregation ?? \"DAILY\",\n startTime: {\n year: start.getFullYear(),\n month: start.getMonth() + 1,\n day: start.getDate(),\n },\n endTime: {\n year: end.getFullYear(),\n month: end.getMonth() + 1,\n day: end.getDate(),\n },\n };\n }\n\n return query;\n}\n\nasync function queryMetric(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n const query = buildQuery(options);\n return reporting.queryMetricSet(packageName, metricSet, query);\n}\n\nexport async function getVitalsOverview(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<VitalsOverview> {\n const metricSets: [VitalsMetricSet, keyof VitalsOverview][] = [\n [\"vitals.crashrate\", \"crashRate\"],\n [\"vitals.anrrate\", \"anrRate\"],\n [\"vitals.slowstartrate\", \"slowStartRate\"],\n [\"vitals.slowrenderingrate\", \"slowRenderingRate\"],\n [\"vitals.excessivewakeuprate\", \"excessiveWakeupRate\"],\n [\"vitals.stuckbackgroundwakelockrate\", \"stuckWakelockRate\"],\n ];\n\n const results = await Promise.allSettled(\n metricSets.map(([metric]) =>\n reporting.queryMetricSet(packageName, metric, {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n }),\n ),\n );\n\n const overview: VitalsOverview = {};\n for (let i = 0; i < metricSets.length; i++) {\n const entry = metricSets[i];\n if (!entry) continue;\n const key = entry[1];\n const result = results[i];\n if (!result) continue;\n if (result.status === \"fulfilled\") {\n overview[key] = result.value.rows || [];\n }\n }\n\n return overview;\n}\n\nexport async function getVitalsCrashes(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.crashrate\", options);\n}\n\nexport async function getVitalsAnr(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.anrrate\", options);\n}\n\nexport async function getVitalsStartup(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowstartrate\", options);\n}\n\nexport async function getVitalsRendering(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowrenderingrate\", options);\n}\n\nexport async function getVitalsBattery(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.excessivewakeuprate\", options);\n}\n\nexport async function getVitalsMemory(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.stuckbackgroundwakelockrate\", options);\n}\n\nexport async function getVitalsAnomalies(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<AnomalyDetectionResponse> {\n return reporting.getAnomalies(packageName);\n}\n\nexport async function searchVitalsErrors(\n reporting: ReportingApiClient,\n packageName: string,\n options?: { filter?: string; maxResults?: number },\n): Promise<ErrorIssuesResponse> {\n return reporting.searchErrorIssues(packageName, options?.filter, options?.maxResults);\n}\n\nexport interface VitalsTrendComparison {\n metric: string;\n current: number | undefined;\n previous: number | undefined;\n changePercent: number | undefined;\n direction: \"improved\" | \"degraded\" | \"unchanged\" | \"unknown\";\n}\n\nexport async function compareVitalsTrend(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n days: number = 7,\n): Promise<VitalsTrendComparison> {\n const now = new Date();\n\n // Current period\n const currentEnd = new Date(now);\n const currentStart = new Date(now);\n currentStart.setDate(currentStart.getDate() - days);\n\n // Previous period\n const previousEnd = new Date(currentStart);\n const previousStart = new Date(previousEnd);\n previousStart.setDate(previousStart.getDate() - days);\n\n const makeQuery = (start: Date, end: Date): MetricSetQuery => ({\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n timelineSpec: {\n aggregationPeriod: \"DAILY\",\n startTime: { year: start.getFullYear(), month: start.getMonth() + 1, day: start.getDate() },\n endTime: { year: end.getFullYear(), month: end.getMonth() + 1, day: end.getDate() },\n },\n });\n\n const [currentResult, previousResult] = await Promise.all([\n reporting.queryMetricSet(packageName, metricSet, makeQuery(currentStart, currentEnd)),\n reporting.queryMetricSet(packageName, metricSet, makeQuery(previousStart, previousEnd)),\n ]);\n\n const extractAvg = (rows: MetricRow[] | undefined): number | undefined => {\n if (!rows || rows.length === 0) return undefined;\n const values = rows\n .map((r) => {\n const keys = Object.keys(r.metrics);\n const first = keys[0];\n return first ? Number(r.metrics[first]?.decimalValue?.value) : NaN;\n })\n .filter((v) => !isNaN(v));\n if (values.length === 0) return undefined;\n return values.reduce((a, b) => a + b, 0) / values.length;\n };\n\n const current = extractAvg(currentResult.rows);\n const previous = extractAvg(previousResult.rows);\n\n let changePercent: number | undefined;\n let direction: VitalsTrendComparison[\"direction\"] = \"unknown\";\n\n if (current !== undefined && previous !== undefined && previous !== 0) {\n changePercent = ((current - previous) / previous) * 100;\n if (Math.abs(changePercent) < 1) {\n direction = \"unchanged\";\n } else if (changePercent < 0) {\n direction = \"improved\"; // lower error rate = better\n } else {\n direction = \"degraded\";\n }\n }\n\n return {\n metric: metricSet,\n current,\n previous,\n changePercent: changePercent !== undefined ? Math.round(changePercent * 10) / 10 : undefined,\n direction,\n };\n}\n\nexport function checkThreshold(value: number | undefined, threshold: number): ThresholdResult {\n return {\n breached: value !== undefined && value > threshold,\n value,\n threshold,\n };\n}\n","import type {\n PlayApiClient,\n Subscription,\n BasePlanMigratePricesRequest,\n SubscriptionOffer,\n OffersListResponse,\n} from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListSubscriptionsOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listSubscriptions(\n client: PlayApiClient,\n packageName: string,\n options?: ListSubscriptionsOptions,\n): Promise<{ subscriptions: Subscription[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<Subscription>(\n async (pageToken) => {\n const resp = await client.subscriptions.list(packageName, {\n pageToken,\n pageSize: options?.pageSize,\n });\n return { items: resp.subscriptions || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { subscriptions: result.items, nextPageToken: result.nextPageToken };\n }\n return client.subscriptions.list(packageName, {\n pageToken: options?.pageToken,\n pageSize: options?.pageSize,\n });\n}\n\nexport async function getSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<Subscription> {\n return client.subscriptions.get(packageName, productId);\n}\n\nexport async function createSubscription(\n client: PlayApiClient,\n packageName: string,\n data: Subscription,\n): Promise<Subscription> {\n return client.subscriptions.create(packageName, data);\n}\n\nexport async function updateSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n): Promise<Subscription> {\n return client.subscriptions.update(packageName, productId, data, updateMask);\n}\n\nexport async function deleteSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<void> {\n return client.subscriptions.delete(packageName, productId);\n}\n\nexport async function activateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.activateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deactivateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.deactivateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deleteBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<void> {\n return client.subscriptions.deleteBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function migratePrices(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: BasePlanMigratePricesRequest,\n): Promise<Subscription> {\n return client.subscriptions.migratePrices(packageName, productId, basePlanId, data);\n}\n\nexport async function listOffers(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<OffersListResponse> {\n return client.subscriptions.listOffers(packageName, productId, basePlanId);\n}\n\nexport async function getOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.getOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function createOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.createOffer(packageName, productId, basePlanId, data);\n}\n\nexport async function updateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.updateOffer(\n packageName,\n productId,\n basePlanId,\n offerId,\n data,\n updateMask,\n );\n}\n\nexport async function deleteOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<void> {\n return client.subscriptions.deleteOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function activateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function deactivateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.deactivateOffer(packageName, productId, basePlanId, offerId);\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { PlayApiClient, InAppProduct } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListIapOptions {\n token?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listInAppProducts(\n client: PlayApiClient,\n packageName: string,\n options?: ListIapOptions,\n): Promise<{ inappproduct: InAppProduct[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<InAppProduct>(\n async (pageToken) => {\n const resp = await client.inappproducts.list(packageName, {\n token: pageToken,\n maxResults: options?.maxResults,\n });\n return {\n items: resp.inappproduct || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { inappproduct: result.items, nextPageToken: result.nextPageToken };\n }\n return client.inappproducts.list(packageName, {\n token: options?.token,\n maxResults: options?.maxResults,\n });\n}\n\nexport async function getInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<InAppProduct> {\n return client.inappproducts.get(packageName, sku);\n}\n\nexport async function createInAppProduct(\n client: PlayApiClient,\n packageName: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.create(packageName, data);\n}\n\nexport async function updateInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.update(packageName, sku, data);\n}\n\nexport async function deleteInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<void> {\n return client.inappproducts.delete(packageName, sku);\n}\n\nexport interface SyncResult {\n created: number;\n updated: number;\n unchanged: number;\n skus: string[];\n}\n\nexport async function syncInAppProducts(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<SyncResult> {\n const files = await readdir(dir);\n const jsonFiles = files.filter((f) => f.endsWith(\".json\"));\n\n if (jsonFiles.length === 0) {\n return { created: 0, updated: 0, unchanged: 0, skus: [] };\n }\n\n const localProducts: InAppProduct[] = [];\n for (const file of jsonFiles) {\n const content = await readFile(join(dir, file), \"utf-8\");\n localProducts.push(JSON.parse(content) as InAppProduct);\n }\n\n const response = await client.inappproducts.list(packageName);\n const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));\n\n let created = 0;\n let updated = 0;\n const unchanged = 0;\n const skus: string[] = [];\n\n for (const product of localProducts) {\n skus.push(product.sku);\n if (remoteSkus.has(product.sku)) {\n if (!options?.dryRun) {\n await client.inappproducts.update(packageName, product.sku, product);\n }\n updated++;\n } else {\n if (!options?.dryRun) {\n await client.inappproducts.create(packageName, product);\n }\n created++;\n }\n }\n\n return { created, updated, unchanged, skus };\n}\n","import type {\n PlayApiClient,\n ProductPurchase,\n SubscriptionPurchaseV2,\n SubscriptionDeferResponse,\n} from \"@gpc-cli/api\";\n\nexport async function getProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<ProductPurchase> {\n return client.purchases.getProduct(packageName, productId, token);\n}\n\nexport async function acknowledgeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n payload?: string,\n): Promise<void> {\n const body = payload ? { developerPayload: payload } : undefined;\n return client.purchases.acknowledgeProduct(packageName, productId, token, body);\n}\n\nexport async function consumeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<void> {\n return client.purchases.consumeProduct(packageName, productId, token);\n}\n\nexport async function getSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<SubscriptionPurchaseV2> {\n return client.purchases.getSubscriptionV2(packageName, token);\n}\n\nexport async function cancelSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n): Promise<void> {\n return client.purchases.cancelSubscription(packageName, subscriptionId, token);\n}\n\nexport async function deferSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n desiredExpiry: string,\n): Promise<SubscriptionDeferResponse> {\n const sub = await client.purchases.getSubscriptionV1(packageName, subscriptionId, token);\n return client.purchases.deferSubscription(packageName, subscriptionId, token, {\n deferralInfo: {\n expectedExpiryTimeMillis: sub.expiryTimeMillis,\n desiredExpiryTimeMillis: String(new Date(desiredExpiry).getTime()),\n },\n });\n}\n\nexport async function revokeSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<void> {\n return client.purchases.revokeSubscriptionV2(packageName, token);\n}\n\nimport type { VoidedPurchase } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListVoidedOptions {\n startTime?: string;\n endTime?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listVoidedPurchases(\n client: PlayApiClient,\n packageName: string,\n options?: ListVoidedOptions,\n): Promise<{ voidedPurchases: VoidedPurchase[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<VoidedPurchase>(\n async (pageToken) => {\n const resp = await client.purchases.listVoided(packageName, {\n startTime: options?.startTime,\n endTime: options?.endTime,\n maxResults: options?.maxResults,\n token: pageToken,\n });\n return {\n items: resp.voidedPurchases || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { voidedPurchases: result.items, nextPageToken: result.nextPageToken };\n }\n return client.purchases.listVoided(packageName, options);\n}\n\nexport async function refundOrder(\n client: PlayApiClient,\n packageName: string,\n orderId: string,\n options?: { fullRefund?: boolean; proratedRefund?: boolean },\n): Promise<void> {\n return client.orders.refund(packageName, orderId, options);\n}\n","import type { PlayApiClient, ConvertRegionPricesResponse } from \"@gpc-cli/api\";\n\nexport async function convertRegionPrices(\n client: PlayApiClient,\n packageName: string,\n currencyCode: string,\n amount: string,\n): Promise<ConvertRegionPricesResponse> {\n const units = amount.split(\".\")[0] || \"0\";\n const fractional = amount.split(\".\")[1] || \"0\";\n const nanos = Number(fractional.padEnd(9, \"0\").slice(0, 9));\n\n return client.monetization.convertRegionPrices(packageName, {\n price: {\n currencyCode,\n units,\n nanos,\n },\n });\n}\n","import type { PlayApiClient, ReportBucket, ReportType, StatsDimension } from \"@gpc-cli/api\";\n\nconst FINANCIAL_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"earnings\",\n \"sales\",\n \"estimated_sales\",\n \"play_balance\",\n]);\n\nconst STATS_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"installs\",\n \"crashes\",\n \"ratings\",\n \"reviews\",\n \"store_performance\",\n \"subscriptions\",\n]);\n\nconst VALID_DIMENSIONS: ReadonlySet<string> = new Set([\n \"country\",\n \"language\",\n \"os_version\",\n \"device\",\n \"app_version\",\n \"carrier\",\n \"overview\",\n]);\n\nexport function isFinancialReportType(type: string): boolean {\n return FINANCIAL_REPORT_TYPES.has(type);\n}\n\nexport function isStatsReportType(type: string): boolean {\n return STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidReportType(type: string): type is ReportType {\n return FINANCIAL_REPORT_TYPES.has(type) || STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidStatsDimension(dim: string): dim is StatsDimension {\n return VALID_DIMENSIONS.has(dim);\n}\n\nexport interface ParsedMonth {\n year: number;\n month: number;\n}\n\nexport function parseMonth(monthStr: string): ParsedMonth {\n const match = /^(\\d{4})-(\\d{2})$/.exec(monthStr);\n if (!match) {\n throw new Error(`Invalid month format \"${monthStr}\". Expected YYYY-MM (e.g., 2026-03).`);\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n if (month < 1 || month > 12) {\n throw new Error(`Invalid month \"${month}\". Must be between 01 and 12.`);\n }\n return { year, month };\n}\n\nexport async function listReports(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<ReportBucket[]> {\n const response = await client.reports.list(packageName, reportType, year, month);\n return response.reports || [];\n}\n\nexport async function downloadReport(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<string> {\n const reports = await listReports(client, packageName, reportType, year, month);\n\n if (reports.length === 0) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n\n // Download the first report bucket (signed URI — no auth needed)\n const bucket = reports[0];\n if (!bucket) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n const uri = bucket.uri;\n const response = await fetch(uri);\n\n if (!response.ok) {\n throw new Error(`Failed to download report from signed URI: HTTP ${response.status}`);\n }\n\n return response.text();\n}\n","import type { UsersApiClient, User, DeveloperPermission, Grant } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport const PERMISSION_PROPAGATION_WARNING =\n \"Note: Permission changes may take up to 48 hours to propagate.\";\n\nexport interface ListUsersOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listUsers(\n client: UsersApiClient,\n developerId: string,\n options?: ListUsersOptions,\n): Promise<{ users: User[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<User>(\n async (pageToken) => {\n const resp = await client.list(developerId, { pageToken, pageSize: options?.pageSize });\n return { items: resp.users || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { users: result.items, nextPageToken: result.nextPageToken };\n }\n const response = await client.list(developerId, options);\n return { users: response.users || [], nextPageToken: response.nextPageToken };\n}\n\nexport async function getUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<User> {\n return client.get(developerId, userId);\n}\n\nexport async function inviteUser(\n client: UsersApiClient,\n developerId: string,\n email: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const user: Partial<User> = { email };\n if (permissions) user.developerAccountPermission = permissions;\n if (grants) user.grants = grants;\n return client.create(developerId, user);\n}\n\nexport async function updateUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const updates: Partial<User> = {};\n const masks: string[] = [];\n\n if (permissions) {\n updates.developerAccountPermission = permissions;\n masks.push(\"developerAccountPermission\");\n }\n if (grants) {\n updates.grants = grants;\n masks.push(\"grants\");\n }\n\n const updateMask = masks.length > 0 ? masks.join(\",\") : undefined;\n return client.update(developerId, userId, updates, updateMask);\n}\n\nexport async function removeUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<void> {\n return client.delete(developerId, userId);\n}\n\nexport function parseGrantArg(grantStr: string): Grant {\n const colonIdx = grantStr.indexOf(\":\");\n if (colonIdx === -1) {\n throw new Error(\n `Invalid grant format \"${grantStr}\". Expected <packageName>:<PERMISSION>[,<PERMISSION>...]`,\n );\n }\n const packageName = grantStr.slice(0, colonIdx);\n const perms = grantStr.slice(colonIdx + 1).split(\",\") as DeveloperPermission[];\n return { packageName, appLevelPermissions: perms };\n}\n","import type { PlayApiClient, Testers } from \"@gpc-cli/api\";\nimport { readFile } from \"node:fs/promises\";\n\nexport async function listTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const testers = await client.testers.get(packageName, edit.id, track);\n return testers;\n } finally {\n await client.edits.delete(packageName, edit.id);\n }\n}\n\nexport async function addTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const existing = new Set(current.googleGroups || []);\n for (const email of groupEmails) {\n existing.add(email.trim());\n }\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: [...existing],\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function removeTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const toRemove = new Set(groupEmails.map((e) => e.trim()));\n const filtered = (current.googleGroups || []).filter((g) => !toRemove.has(g));\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: filtered,\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function importTestersFromCsv(\n client: PlayApiClient,\n packageName: string,\n track: string,\n csvPath: string,\n): Promise<{ added: number; testers: Testers }> {\n const content = await readFile(csvPath, \"utf-8\");\n const emails = content\n .split(/[,\\n\\r]+/)\n .map((e) => e.trim())\n .filter((e) => e.length > 0 && e.includes(\"@\"));\n\n if (emails.length === 0) {\n throw new Error(`No valid email addresses found in ${csvPath}.`);\n }\n\n const testers = await addTesters(client, packageName, track, emails);\n return { added: emails.length, testers };\n}\n","import { resolve, normalize } from \"node:path\";\n\n/**\n * Normalize and resolve a user-supplied path.\n * Prevents path traversal by normalizing `.` and `..` components.\n */\nexport function safePath(userPath: string): string {\n return resolve(normalize(userPath));\n}\n\n/**\n * Validate that a resolved path is within an expected base directory.\n * Returns the resolved path or throws if it escapes the base.\n */\nexport function safePathWithin(userPath: string, baseDir: string): string {\n const resolved = safePath(userPath);\n const base = safePath(baseDir);\n\n if (!resolved.startsWith(base + \"/\") && resolved !== base) {\n throw new Error(`Path \"${userPath}\" resolves outside the expected directory \"${baseDir}\"`);\n }\n\n return resolved;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface ScaffoldOptions {\n name: string;\n dir: string;\n description?: string;\n}\n\nexport interface ScaffoldResult {\n dir: string;\n files: string[];\n}\n\n/**\n * Scaffold a new GPC plugin project.\n */\nexport async function scaffoldPlugin(options: ScaffoldOptions): Promise<ScaffoldResult> {\n const { name, dir, description = `GPC plugin: ${name}` } = options;\n\n // Ensure name follows convention\n const pluginName = name.startsWith(\"gpc-plugin-\") ? name : `gpc-plugin-${name}`;\n const shortName = pluginName.replace(/^gpc-plugin-/, \"\");\n\n const srcDir = join(dir, \"src\");\n const testDir = join(dir, \"tests\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n const files: string[] = [];\n\n // package.json\n const pkg = {\n name: pluginName,\n version: \"0.1.0\",\n description,\n type: \"module\",\n main: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n exports: {\n \".\": {\n import: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n },\n },\n files: [\"dist\"],\n scripts: {\n build: \"tsup src/index.ts --format esm --dts\",\n dev: \"tsup src/index.ts --format esm --dts --watch\",\n test: \"vitest run\",\n \"test:watch\": \"vitest\",\n },\n keywords: [\"gpc\", \"gpc-plugin\", \"google-play\"],\n license: \"MIT\",\n peerDependencies: {\n \"@gpc-cli/plugin-sdk\": \">=0.8.0\",\n },\n devDependencies: {\n \"@gpc-cli/plugin-sdk\": \"^0.8.0\",\n tsup: \"^8.0.0\",\n typescript: \"^5.0.0\",\n vitest: \"^3.0.0\",\n },\n };\n await writeFile(join(dir, \"package.json\"), JSON.stringify(pkg, null, 2) + \"\\n\");\n files.push(\"package.json\");\n\n // tsconfig.json\n const tsconfig = {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n declaration: true,\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"./dist\",\n rootDir: \"./src\",\n },\n include: [\"src\"],\n };\n await writeFile(join(dir, \"tsconfig.json\"), JSON.stringify(tsconfig, null, 2) + \"\\n\");\n files.push(\"tsconfig.json\");\n\n // src/index.ts\n const srcContent = `import { definePlugin } from \"@gpc-cli/plugin-sdk\";\nimport type { CommandEvent, CommandResult } from \"@gpc-cli/plugin-sdk\";\n\nexport const plugin = definePlugin({\n name: \"${pluginName}\",\n version: \"0.1.0\",\n\n register(hooks) {\n hooks.beforeCommand(async (event: CommandEvent) => {\n // Called before every gpc command\n // Example: log command usage, validate prerequisites, etc.\n });\n\n hooks.afterCommand(async (event: CommandEvent, result: CommandResult) => {\n // Called after every successful gpc command\n // Example: send notifications, update dashboards, etc.\n });\n\n // Uncomment to add custom commands:\n // hooks.registerCommands((registry) => {\n // registry.add({\n // name: \"${shortName}\",\n // description: \"${description}\",\n // action: async (args, opts) => {\n // console.log(\"Hello from ${pluginName}!\");\n // },\n // });\n // });\n },\n});\n`;\n await writeFile(join(srcDir, \"index.ts\"), srcContent);\n files.push(\"src/index.ts\");\n\n // tests/plugin.test.ts\n const testContent = `import { describe, it, expect, vi } from \"vitest\";\nimport { plugin } from \"../src/index\";\n\ndescribe(\"${pluginName}\", () => {\n it(\"has correct name and version\", () => {\n expect(plugin.name).toBe(\"${pluginName}\");\n expect(plugin.version).toBe(\"0.1.0\");\n });\n\n it(\"registers without errors\", () => {\n const hooks = {\n beforeCommand: vi.fn(),\n afterCommand: vi.fn(),\n onError: vi.fn(),\n beforeRequest: vi.fn(),\n afterResponse: vi.fn(),\n registerCommands: vi.fn(),\n };\n\n expect(() => plugin.register(hooks)).not.toThrow();\n });\n});\n`;\n await writeFile(join(testDir, \"plugin.test.ts\"), testContent);\n files.push(\"tests/plugin.test.ts\");\n\n return { dir, files };\n}\n","import { appendFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface AuditEntry {\n timestamp: string;\n command: string;\n app?: string;\n args: Record<string, unknown>;\n user?: string;\n success?: boolean;\n durationMs?: number;\n error?: string;\n}\n\nlet auditDir: string | null = null;\n\n/**\n * Initialize audit logging with a directory path.\n * Typically ~/.config/gpc/ or the XDG config dir.\n */\nexport function initAudit(configDir: string): void {\n auditDir = configDir;\n}\n\n/**\n * Write an audit log entry. Non-blocking — errors are silently ignored.\n */\nexport async function writeAuditLog(entry: AuditEntry): Promise<void> {\n if (!auditDir) return;\n\n try {\n await mkdir(auditDir, { recursive: true, mode: 0o700 });\n const logPath = join(auditDir, \"audit.log\");\n const redactedEntry = redactAuditArgs(entry);\n const line = JSON.stringify(redactedEntry) + \"\\n\";\n await appendFile(logPath, line, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(logPath, 0o600).catch(() => {});\n } catch {\n // Audit logging must never break the CLI\n }\n}\n\nconst SENSITIVE_ARG_KEYS = new Set([\n \"key\",\n \"keyFile\",\n \"key-file\",\n \"serviceAccount\",\n \"service-account\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n \"private_key\",\n \"client_secret\",\n]);\n\nfunction redactAuditArgs(entry: AuditEntry): AuditEntry {\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(entry.args)) {\n redacted[k] = SENSITIVE_ARG_KEYS.has(k) ? \"[REDACTED]\" : v;\n }\n return { ...entry, args: redacted };\n}\n\n/**\n * Convenience: create an audit entry for a write command.\n */\nexport function createAuditEntry(\n command: string,\n args: Record<string, unknown>,\n app?: string,\n): AuditEntry {\n return {\n timestamp: new Date().toISOString(),\n command,\n app,\n args,\n };\n}\n"],"mappings":";AAAO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YACE,SACgB,MACA,UACA,YAChB;AACA,UAAM,OAAO;AAJG;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,SAAS;AACP,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,QACL,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,cAAN,cAA0B,SAAS;AAAA,EACxC,YAAY,SAAiB,MAAc,YAAqB;AAC9D,UAAM,SAAS,MAAM,GAAG,UAAU;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,WAAN,cAAuB,SAAS;AAAA,EACrC,YACE,SACA,MACgB,YAChB,YACA;AACA,UAAM,SAAS,MAAM,GAAG,UAAU;AAHlB;AAIhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,SAAS;AAAA,EACzC,YAAY,SAAiB,YAAqB;AAChD,UAAM,SAAS,iBAAiB,GAAG,UAAU;AAC7C,SAAK,OAAO;AAAA,EACd;AACF;;;AC9CA,OAAO,aAAa;AAEb,SAAS,qBAAmC;AACjD,SAAO,QAAQ,OAAO,QAAQ,UAAU;AAC1C;AAEO,SAAS,aAAa,MAAe,QAAsB,SAAS,MAAc;AACvF,QAAM,OAAO,SAAS,gBAAgB,IAAI,IAAI;AAC9C,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,WAAW,IAAI;AAAA,IACxB,KAAK;AACH,aAAO,WAAW,IAAI;AAAA,IACxB,KAAK;AACH,aAAO,eAAe,IAAI;AAAA,IAC5B,KAAK;AACH,aAAO,YAAY,IAAI;AAAA,IACzB;AACE,aAAO,WAAW,IAAI;AAAA,EAC1B;AACF;AAMA,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,WAAW;AAGV,SAAS,gBAAgB,MAAwB;AACtD,MAAI,SAAS,QAAQ,SAAS,OAAW,QAAO;AAEhD,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAW,QAAO;AAElE,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,CAAC,SAAS,gBAAgB,IAAI,CAAC;AAAA,EACjD;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAA+B,GAAG;AAC1E,UAAI,eAAe,IAAI,GAAG,KAAK,OAAO,UAAU,UAAU;AACxD,eAAO,GAAG,IAAI;AAAA,MAChB,OAAO;AACL,eAAO,GAAG,IAAI,gBAAgB,KAAK;AAAA,MACrC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,MAAuB;AACzC,SAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AACrC;AAEA,SAAS,WAAW,MAAe,SAAS,GAAW;AACrD,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,KAAK,SAAS,IAAI,IACrB;AAAA,EAAM,KACH,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,GAAG,KAAK,OAAO,SAAS,CAAC,CAAC,GAAG,CAAC,EAAE,EAC3C,KAAK,IAAI,CAAC,KACb;AAAA,EACN;AAEA,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW;AACzD,WAAO,OAAO,IAAI;AAAA,EACpB;AAEA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,WAAO,KACJ,IAAI,CAAC,SAAS;AACb,YAAM,QAAQ,WAAW,MAAM,SAAS,CAAC;AACzC,YAAM,SAAS,GAAG,KAAK,OAAO,MAAM,CAAC;AACrC,UAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG;AACrE,cAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,eAAO,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC;AAAA,EAAK,MAC7B,MAAM,CAAC,EACP,IAAI,CAAC,MAAM,GAAG,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,EACzC,KAAK,IAAI,CAAC;AAAA,MACf;AACA,aAAO,GAAG,MAAM,GAAG,KAAK;AAAA,IAC1B,CAAC,EACA,KAAK,IAAI;AAAA,EACd;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,UAAU,OAAO,QAAQ,IAA+B;AAC9D,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WAAO,QACJ,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACrB,UAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,eAAO,GAAG,KAAK,OAAO,MAAM,CAAC,GAAG,GAAG;AAAA,EAAM,WAAW,OAAO,SAAS,CAAC,CAAC;AAAA,MACxE;AACA,aAAO,GAAG,KAAK,OAAO,MAAM,CAAC,GAAG,GAAG,KAAK,WAAW,OAAO,MAAM,CAAC;AAAA,IACnE,CAAC,EACA,KAAK,IAAI;AAAA,EACd;AAEA,SAAO,OAAO,IAAI;AACpB;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,IAAI;AACxB,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,WAAW,KAAK,CAAC;AACvB,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,SAAS,KAAK;AAAA,IAAI,CAAC,QACvB,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,IAAI,CAAC,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,EAAE,MAAM,CAAC;AAAA,EAC1E;AAEA,QAAM,SAAS,KAAK,IAAI,CAAC,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI;AACzE,QAAM,YAAY,OAAO,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI;AAC5D,QAAM,OAAO,KACV,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,KAAK,MAAM,OAAO,IAAI,GAAG,KAAK,EAAE,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAC3F,KAAK,IAAI;AAEZ,SAAO,GAAG,MAAM;AAAA,EAAK,SAAS;AAAA,EAAK,IAAI;AACzC;AAEA,SAAS,eAAe,MAAuB;AAC7C,QAAM,OAAO,OAAO,IAAI;AACxB,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,WAAW,KAAK,CAAC;AACvB,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,SAAS,KAAK;AAAA,IAAI,CAAC,QACvB,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,IAAI,CAAC,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,EAAE,MAAM,CAAC;AAAA,EAC1E;AAEA,QAAM,SAAS,KAAK,KAAK,IAAI,CAAC,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;AAChF,QAAM,YAAY,KAAK,OAAO,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;AACnE,QAAM,OAAO,KACV;AAAA,IACC,CAAC,QACC,KAAK,KAAK,IAAI,CAAC,KAAK,MAAM,OAAO,IAAI,GAAG,KAAK,EAAE,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;AAAA,EACxF,EACC,KAAK,IAAI;AAEZ,SAAO,GAAG,MAAM;AAAA,EAAK,SAAS;AAAA,EAAK,IAAI;AACzC;AAEA,SAAS,OAAO,MAA0C;AACxD,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK;AAAA,MACV,CAAC,SAA0C,OAAO,SAAS,YAAY,SAAS;AAAA,IAClF;AAAA,EACF;AACA,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,WAAO,CAAC,IAA+B;AAAA,EACzC;AACA,SAAO,CAAC;AACV;;;AClKO,IAAM,gBAAN,MAAoB;AAAA,EACjB,UAA0B,CAAC;AAAA,EAC3B,iBAAyC,CAAC;AAAA,EAC1C,gBAAuC,CAAC;AAAA,EACxC,gBAAgC,CAAC;AAAA,EACjC,wBAAgD,CAAC;AAAA,EACjD,wBAAgD,CAAC;AAAA,EACjD,qBAAsC,CAAC;AAAA;AAAA,EAG/C,MAAM,KAAK,QAAmB,UAA0C;AACtE,UAAM,YAAY,UAAU,WAAW,OAAO,KAAK,WAAW,WAAW;AAEzE,QAAI,CAAC,aAAa,UAAU,aAAa;AACvC,0BAAoB,SAAS,WAAW;AAAA,IAC1C;AAEA,UAAM,QAAQ;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAEA,UAAM,OAAO,SAAS,KAAK;AAE3B,SAAK,QAAQ,KAAK;AAAA,MAChB,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,iBAAiB,OAAoC;AACzD,eAAW,WAAW,KAAK,gBAAgB;AACzC,YAAM,QAAQ,KAAK;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBAAgB,OAAqB,QAAsC;AAC/E,eAAW,WAAW,KAAK,eAAe;AACxC,YAAM,QAAQ,OAAO,MAAM;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAAW,OAAqB,OAAmC;AACvE,eAAW,WAAW,KAAK,eAAe;AACxC,UAAI;AACF,cAAM,QAAQ,OAAO,KAAK;AAAA,MAC5B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,iBAAiB,OAAoC;AACzD,eAAW,WAAW,KAAK,uBAAuB;AAChD,UAAI;AACF,cAAM,QAAQ,KAAK;AAAA,MACrB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,iBAAiB,OAAqB,UAAwC;AAClF,eAAW,WAAW,KAAK,uBAAuB;AAChD,UAAI;AACF,cAAM,QAAQ,OAAO,QAAQ;AAAA,MAC/B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,wBAAyC;AACvC,WAAO,CAAC,GAAG,KAAK,kBAAkB;AAAA,EACpC;AAAA;AAAA,EAGA,mBAAmC;AACjC,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA;AAAA,EAGA,kBAA2B;AACzB,WAAO,KAAK,sBAAsB,SAAS,KAAK,KAAK,sBAAsB,SAAS;AAAA,EACtF;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,UAAU,CAAC;AAChB,SAAK,iBAAiB,CAAC;AACvB,SAAK,gBAAgB,CAAC;AACtB,SAAK,gBAAgB,CAAC;AACtB,SAAK,wBAAwB,CAAC;AAC9B,SAAK,wBAAwB,CAAC;AAC9B,SAAK,qBAAqB,CAAC;AAAA,EAC7B;AACF;AAYA,SAAS,YACP,gBACA,eACA,eACA,uBACA,uBACA,oBACa;AACb,SAAO;AAAA,IACL,cAAc,SAAS;AACrB,qBAAe,KAAK,OAAO;AAAA,IAC7B;AAAA,IACA,aAAa,SAAS;AACpB,oBAAc,KAAK,OAAO;AAAA,IAC5B;AAAA,IACA,QAAQ,SAAS;AACf,oBAAc,KAAK,OAAO;AAAA,IAC5B;AAAA,IACA,cAAc,SAAS;AACrB,4BAAsB,KAAK,OAAO;AAAA,IACpC;AAAA,IACA,cAAc,SAAS;AACrB,4BAAsB,KAAK,OAAO;AAAA,IACpC;AAAA,IACA,iBAAiB,WAAW;AAC1B,YAAM,WAAW;AAAA,QACf,IAAI,KAAoB;AACtB,6BAAmB,KAAK,GAAG;AAAA,QAC7B;AAAA,MACF;AACA,gBAAU,QAAQ;AAAA,IACpB;AAAA,EACF;AACF;AAMA,IAAM,oBAAyC,oBAAI,IAAsB;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,oBAAoB,aAAuC;AAClE,aAAW,QAAQ,aAAa;AAC9B,QAAI,CAAC,kBAAkB,IAAI,IAAI,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,+BAA+B,IAAI;AAAA,QACnC;AAAA,QACA;AAAA,QACA,sBAAsB,CAAC,GAAG,iBAAiB,EAAE,KAAK,IAAI,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAoBA,eAAsB,gBAAgB,SAAwD;AAC5F,QAAM,UAAuB,CAAC;AAC9B,QAAM,OAAO,oBAAI,IAAY;AAG7B,MAAI,SAAS,eAAe;AAC1B,eAAW,QAAQ,QAAQ,eAAe;AACxC,UAAI,KAAK,IAAI,IAAI,EAAG;AACpB,UAAI;AACF,cAAM,MAAM,MAAM,OAAO;AACzB,cAAM,SAAS,cAAc,GAAG;AAChC,YAAI,QAAQ;AACV,kBAAQ,KAAK,MAAM;AACnB,eAAK,IAAI,IAAI;AAAA,QACf;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,cAAc,KAAqC;AAC1D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAE5C,QAAM,IAAI;AAGV,MAAI,SAAS,EAAE,SAAS,CAAC,EAAG,QAAO,EAAE,SAAS;AAG9C,MAAI,SAAS,EAAE,QAAQ,CAAC,EAAG,QAAO,EAAE,QAAQ;AAG5C,MAAI,SAAS,CAAC,EAAG,QAAO;AAExB,SAAO;AACT;AAEA,SAAS,SAAS,KAAgC;AAChD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,SACE,OAAO,EAAE,MAAM,MAAM,YACrB,OAAO,EAAE,SAAS,MAAM,YACxB,OAAO,EAAE,UAAU,MAAM;AAE7B;;;AC9QA,eAAsB,WAAW,QAAuB,aAAuC;AAE7F,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,QAAQ,IAAI,aAAa,KAAK,EAAE;AAE7D,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,MACL;AAAA,MACA,OAAO,QAAQ;AAAA,MACf,iBAAiB,QAAQ;AAAA,MACzB,cAAc,QAAQ;AAAA,IACxB;AAAA,EACF,SAAS,OAAO;AAEd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;;;AC3BA,SAAS,UAAU,YAAY;AAC/B,SAAS,eAAe;AAWxB,IAAM,YAAY,OAAO,KAAK,CAAC,IAAM,IAAM,GAAM,CAAI,CAAC;AAEtD,IAAM,eAAe,MAAM,OAAO;AAClC,IAAM,eAAe,MAAM,OAAO;AAClC,IAAM,uBAAuB,MAAM,OAAO;AAE1C,eAAsB,mBAAmB,UAAiD;AACxF,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAG5B,QAAM,MAAM,QAAQ,QAAQ,EAAE,YAAY;AAC1C,MAAI,WAA6C;AAEjD,MAAI,QAAQ,QAAQ;AAClB,eAAW;AAAA,EACb,WAAW,QAAQ,QAAQ;AACzB,eAAW;AAAA,EACb,OAAO;AACL,WAAO,KAAK,+BAA+B,GAAG,0BAA0B;AAAA,EAC1E;AAGA,MAAI;AACJ,MAAI;AACF,UAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,gBAAY,MAAM;AAElB,QAAI,cAAc,GAAG;AACnB,aAAO,KAAK,yBAAyB;AAAA,IACvC;AAAA,EACF,QAAQ;AACN,WAAO,KAAK,mBAAmB,QAAQ,EAAE;AACzC,WAAO,EAAE,OAAO,OAAO,UAAU,WAAW,GAAG,QAAQ,SAAS;AAAA,EAClE;AAGA,MAAI,aAAa,SAAS,YAAY,cAAc;AAClD,WAAO;AAAA,MACL,6BAA6B,WAAW,SAAS,CAAC;AAAA,IACpD;AAAA,EACF;AACA,MAAI,aAAa,SAAS,YAAY,cAAc;AAClD,WAAO,KAAK,6BAA6B,WAAW,SAAS,CAAC,IAAI;AAAA,EACpE;AAEA,MAAI,YAAY,wBAAwB,OAAO,WAAW,GAAG;AAC3D,aAAS;AAAA,MACP,eAAe,WAAW,SAAS,CAAC;AAAA,IACtC;AAAA,EACF;AAGA,MAAI,YAAY,GAAG;AACjB,QAAI;AACF,YAAM,KAAK,MAAM,SAAS,UAAU,EAAE,MAAM,IAAI,CAAC;AACjD,YAAM,SAAS,GAAG,SAAS,GAAG,CAAC;AAE/B,UAAI,CAAC,OAAO,OAAO,SAAS,GAAG;AAC7B,eAAO;AAAA,UACL;AAAA,QAEF;AAAA,MACF;AAAA,IACF,QAAQ;AACN,aAAO,KAAK,2CAA2C;AAAA,IACzD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,WAAW,OAAuB;AACzC,MAAI,SAAS,OAAO,OAAO,MAAM;AAC/B,WAAO,IAAI,SAAS,OAAO,OAAO,OAAO,QAAQ,CAAC,CAAC;AAAA,EACrD;AACA,MAAI,SAAS,OAAO,MAAM;AACxB,WAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAAA,EAC9C;AACA,MAAI,SAAS,MAAM;AACjB,WAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAAA,EACrC;AACA,SAAO,GAAG,KAAK;AACjB;;;ACpFA,eAAsB,cACpB,QACA,aACA,UACA,SAQuB;AAEvB,QAAM,aAAa,MAAM,mBAAmB,QAAQ;AACpD,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,IAAI,MAAM;AAAA,EAA4B,WAAW,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EAC5E;AAEA,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AAEF,UAAM,SAAS,MAAM,OAAO,QAAQ,OAAO,aAAa,KAAK,IAAI,QAAQ;AAGzE,QAAI,QAAQ,aAAa;AACvB,YAAM,OAAO,cAAc;AAAA,QACzB;AAAA,QACA,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAGA,UAAM,UAAmB;AAAA,MACvB,cAAc,CAAC,OAAO,OAAO,WAAW,CAAC;AAAA,MACzC,QAAS,QAAQ,WACd,QAAQ,eAAe,eAAe;AAAA,MACzC,GAAI,QAAQ,gBAAgB,EAAE,cAAc,QAAQ,aAAa;AAAA,MACjE,GAAI,QAAQ,gBAAgB,EAAE,cAAc,QAAQ,aAAa;AAAA,MACjE,GAAI,QAAQ,eAAe,EAAE,MAAM,QAAQ,YAAY;AAAA,IACzD;AAEA,UAAM,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,QAAQ,OAAO,OAAO;AAGvE,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAE9C,WAAO;AAAA,MACL,aAAa,OAAO;AAAA,MACpB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,kBACpB,QACA,aACA,aACgC;AAChC,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,SAAS,cACX,CAAC,MAAM,OAAO,OAAO,IAAI,aAAa,KAAK,IAAI,WAAW,CAAC,IAC3D,MAAM,OAAO,OAAO,KAAK,aAAa,KAAK,EAAE;AAEjD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAE9C,UAAM,UAAiC,CAAC;AACxC,eAAW,SAAS,QAAQ;AAC1B,iBAAW,WAAW,MAAM,YAAY,CAAC,GAAG;AAC1C,gBAAQ,KAAK;AAAA,UACX,OAAO,MAAM;AAAA,UACb,QAAQ,QAAQ;AAAA,UAChB,cAAc,QAAQ,gBAAgB,CAAC;AAAA,UACvC,cAAc,QAAQ;AAAA,UACtB,cAAc,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,QACA,aACA,WACA,SACA,SAC8B;AAC9B,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AAEF,UAAM,cAAc,MAAM,OAAO,OAAO,IAAI,aAAa,KAAK,IAAI,SAAS;AAC3E,UAAM,iBAAiB,YAAY,UAAU;AAAA,MAC3C,CAAC,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW;AAAA,IAClD;AAEA,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,qCAAqC,SAAS,GAAG;AAAA,IACnE;AAGA,UAAM,UAAmB;AAAA,MACvB,cAAc,eAAe;AAAA,MAC7B,QAAS,SAAS,eAAe,eAAe;AAAA,MAChD,GAAI,SAAS,gBAAgB,EAAE,cAAc,QAAQ,aAAa;AAAA,MAClE,cAAc,SAAS,gBAAgB,eAAe,gBAAgB,CAAC;AAAA,IACzE;AAEA,UAAM,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,SAAS,OAAO;AACjE,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAE9C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,QAAQ;AAAA,MAChB,cAAc,QAAQ;AAAA,MACtB,cAAc,QAAQ;AAAA,IACxB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cACpB,QACA,aACA,OACA,QACA,cAC8B;AAC9B,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,YAAY,MAAM,OAAO,OAAO,IAAI,aAAa,KAAK,IAAI,KAAK;AACrE,UAAM,iBAAiB,UAAU,UAAU;AAAA,MACzC,CAAC,MAAM,EAAE,WAAW,gBAAgB,EAAE,WAAW;AAAA,IACnD;AAEA,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,qCAAqC,KAAK,GAAG;AAAA,IAC/D;AAEA,QAAI;AACJ,QAAI;AAEJ,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,YAAI,CAAC,aAAc,OAAM,IAAI,MAAM,oDAAoD;AACvF,oBAAY;AACZ,sBAAc;AACd;AAAA,MACF,KAAK;AACH,oBAAY;AACZ,sBAAc,eAAe;AAC7B;AAAA,MACF,KAAK;AACH,oBAAY;AACZ,sBAAc,eAAe;AAC7B;AAAA,MACF,KAAK;AACH,oBAAY;AACZ,sBAAc;AACd;AAAA,IACJ;AAEA,UAAM,UAAmB;AAAA,MACvB,cAAc,eAAe;AAAA,MAC7B,QAAQ;AAAA,MACR,GAAI,gBAAgB,UAAa,EAAE,cAAc,YAAY;AAAA,MAC7D,cAAc,eAAe,gBAAgB,CAAC;AAAA,IAChD;AAEA,UAAM,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,OAAO,OAAO;AAC/D,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAE9C,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,cAAc,QAAQ;AAAA,MACtB,cAAc;AAAA,IAChB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,WAAW,QAAuB,aAAuC;AAC7F,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,OAAO,KAAK,aAAa,KAAK,EAAE;AAC5D,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;;;ACnOO,IAAM,wBAAkC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,aAAa,KAAsB;AACjD,SAAO,sBAAsB,SAAS,GAAG;AAC3C;;;ACvFA,SAAS,QAAAA,aAAY;AACrB,SAAS,WAAAC,gBAAe;AASxB,IAAM,oBAAyE;AAAA,EAC7E,MAAM,EAAE,UAAU,OAAO,MAAM,OAAO,OAAO;AAAA,EAC7C,gBAAgB,EAAE,UAAU,OAAO,MAAM,OAAO,OAAO;AAAA,EACvD,UAAU,EAAE,UAAU,OAAO,MAAM,OAAO,OAAO;AAAA,EACjD,kBAAkB,EAAE,UAAU,IAAI,OAAO,MAAM,OAAO,OAAO;AAAA,EAC7D,sBAAsB,EAAE,UAAU,IAAI,OAAO,MAAM,OAAO,OAAO;AAAA,EACjE,oBAAoB,EAAE,UAAU,IAAI,OAAO,MAAM,OAAO,OAAO;AAAA,EAC/D,eAAe,EAAE,UAAU,IAAI,OAAO,MAAM,OAAO,OAAO;AAAA,EAC1D,iBAAiB,EAAE,UAAU,IAAI,OAAO,MAAM,OAAO,OAAO;AAC9D;AAEA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,QAAQ,QAAQ,OAAO,CAAC;AAC1D,IAAM,wBAAwB,IAAI,OAAO;AAEzC,eAAsB,cACpB,UACA,WACgC;AAChC,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAG5B,QAAM,MAAMA,SAAQ,QAAQ,EAAE,YAAY;AAC1C,MAAI,CAAC,iBAAiB,IAAI,GAAG,GAAG;AAC9B,WAAO,KAAK,6BAA6B,GAAG,qBAAqB;AAAA,EACnE;AAGA,MAAI;AACJ,MAAI;AACF,UAAM,QAAQ,MAAMD,MAAK,QAAQ;AACjC,gBAAY,MAAM;AAElB,QAAI,cAAc,GAAG;AACnB,aAAO,KAAK,+BAA+B;AAAA,IAC7C;AAAA,EACF,QAAQ;AACN,WAAO,KAAK,yBAAyB,QAAQ,EAAE;AAC/C,WAAO,EAAE,OAAO,OAAO,QAAQ,SAAS;AAAA,EAC1C;AAGA,MAAI,aAAa,YAAY,GAAG;AAC9B,UAAM,QAAQ,kBAAkB,SAAS;AACzC,QAAI,SAAS,YAAY,MAAM,UAAU;AACvC,aAAO,KAAK,iBAAiB,MAAM,KAAK,cAAc,SAAS,KAAKE,YAAW,SAAS,CAAC,GAAG;AAAA,IAC9F;AAAA,EACF;AAGA,MAAI,YAAY,yBAAyB,OAAO,WAAW,GAAG;AAC5D,aAAS;AAAA,MACP,gBAAgBA,YAAW,SAAS,CAAC;AAAA,IACvC;AAAA,EACF;AAGA,MAAI,QAAQ,UAAU,YAAY,MAAM,MAAM;AAC5C,aAAS;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,QAAQ,SAAS;AACxD;AAEA,SAASA,YAAW,OAAuB;AACzC,MAAI,SAAS,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AACtE,MAAI,SAAS,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AACtD,SAAO,GAAG,KAAK;AACjB;;;AChFA,SAAS,YAAAC,WAAU,WAAW,OAAO,SAAS,QAAAC,aAAY;AAC1D,SAAS,YAAY;AAGrB,IAAM,WAA4D;AAAA,EAChE,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,aAAa;AACf;AAEA,IAAM,gBAAwC,OAAO;AAAA,EACnD,OAAO,QAAQ,QAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,IAAI,CAAC;AAC/D;AASA,eAAe,OAAO,MAAgC;AACpD,MAAI;AACF,UAAMA,MAAK,IAAI;AACf,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,oBAAoB,KAAiC;AACzE,QAAM,WAAsB,CAAC;AAE7B,MAAI,CAAE,MAAM,OAAO,GAAG,EAAI,QAAO;AAEjC,QAAM,UAAU,MAAM,QAAQ,GAAG;AACjC,aAAW,QAAQ,SAAS;AAC1B,UAAM,UAAU,KAAK,KAAK,IAAI;AAC9B,UAAM,WAAW,MAAMA,MAAK,OAAO;AACnC,QAAI,CAAC,SAAS,YAAY,EAAG;AAE7B,UAAM,UAAmB;AAAA,MACvB,UAAU;AAAA,MACV,OAAO;AAAA,MACP,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,IACnB;AAEA,eAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACxD,YAAM,WAAW,KAAK,SAAS,QAAQ;AACvC,UAAI,MAAM,OAAO,QAAQ,GAAG;AAC1B,cAAM,UAAU,MAAMD,UAAS,UAAU,OAAO;AAChD,QAAC,QAA8C,KAAK,IAAI,QAAQ,QAAQ;AAAA,MAC1E;AAAA,IACF;AAEA,aAAS,KAAK,OAAO;AAAA,EACvB;AAEA,SAAO;AACT;AAEA,eAAsB,mBAAmB,KAAa,UAAoC;AACxF,aAAW,WAAW,UAAU;AAC9B,UAAM,UAAU,KAAK,KAAK,QAAQ,QAAQ;AAC1C,UAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,eAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC7D,YAAM,QAAS,QAA8C,KAAK;AAClE,UAAI,UAAU,UAAa,UAAU,IAAI;AACvC,cAAM,UAAU,KAAK,SAAS,QAAQ,GAAG,QAAQ,MAAM,OAAO;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,OAAkB,QAAkC;AAC/E,QAAM,QAAuB,CAAC;AAC9B,QAAM,YAAY,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAC5D,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAG1D,aAAW,gBAAgB,OAAO;AAChC,UAAM,gBAAgB,UAAU,IAAI,aAAa,QAAQ;AACzD,eAAW,CAAC,KAAK,KAAK,OAAO,QAAQ,aAAa,GAAG;AACnD,YAAM,YACH,aAAmD,KAAK,KAAK,IAC9D,SAAS;AACX,YAAM,YAAY,iBACZ,cAAoD,KAAK,KAAK,IAAI,SAAS,IAC7E;AACJ,UAAI,aAAa,WAAW;AAC1B,cAAM,KAAK;AAAA,UACT,UAAU,aAAa;AAAA,UACvB;AAAA,UACA,OAAO;AAAA,UACP,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,iBAAiB,QAAQ;AAClC,QAAI,CAAC,SAAS,IAAI,cAAc,QAAQ,GAAG;AACzC,iBAAW,CAAC,KAAK,KAAK,OAAO,QAAQ,aAAa,GAAG;AACnD,cAAM,aACH,cAAoD,KAAK,KAAK,IAC/D,SAAS;AACX,YAAI,WAAW;AACb,gBAAM,KAAK;AAAA,YACT,UAAU,cAAc;AAAA,YACxB;AAAA,YACA,OAAO;AAAA,YACP,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACjGA,SAAS,iBAAiB,MAAoB;AAC5C,MAAI,CAAC,aAAa,IAAI,GAAG;AACvB,UAAM,IAAI,MAAM,yBAAyB,IAAI,6CAA6C;AAAA,EAC5F;AACF;AAEA,eAAsB,YACpB,QACA,aACA,UACoB;AACpB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,QAAI;AACJ,QAAI,UAAU;AACZ,uBAAiB,QAAQ;AACzB,YAAM,UAAU,MAAM,OAAO,SAAS,IAAI,aAAa,KAAK,IAAI,QAAQ;AACxE,iBAAW,CAAC,OAAO;AAAA,IACrB,OAAO;AACL,iBAAW,MAAM,OAAO,SAAS,KAAK,aAAa,KAAK,EAAE;AAAA,IAC5D;AACA,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cACpB,QACA,aACA,UACA,MACkB;AAClB,mBAAiB,QAAQ;AACzB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,SAAS,MAAM,aAAa,KAAK,IAAI,UAAU,IAAI;AAChF,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cACpB,QACA,aACA,UACe;AACf,mBAAiB,QAAQ;AACzB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,OAAO,SAAS,OAAO,aAAa,KAAK,IAAI,QAAQ;AAC3D,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAAA,EAChD,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,aACpB,QACA,aACA,KACyB;AACzB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,WAAW,MAAM,OAAO,SAAS,KAAK,aAAa,KAAK,EAAE;AAChE,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,UAAM,mBAAmB,KAAK,QAAQ;AACtC,WAAO,EAAE,SAAS;AAAA,EACpB,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,aACpB,QACA,aACA,KACA,SACoC;AACpC,QAAM,gBAAgB,MAAM,oBAAoB,GAAG;AAEnD,MAAI,cAAc,WAAW,GAAG;AAC9B,UAAM,IAAI,MAAM,mCAAmC,GAAG,GAAG;AAAA,EAC3D;AAGA,aAAW,WAAW,eAAe;AACnC,qBAAiB,QAAQ,QAAQ;AAAA,EACnC;AAEA,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,QAAI,SAAS,QAAQ;AACnB,YAAM,iBAAiB,MAAM,OAAO,SAAS,KAAK,aAAa,KAAK,EAAE;AACtE,YAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,YAAM,QAAQ,aAAa,eAAe,cAAc;AACxD,aAAO,EAAE,MAAM;AAAA,IACjB;AAEA,eAAW,WAAW,eAAe;AACnC,YAAM,EAAE,UAAU,GAAG,KAAK,IAAI;AAC9B,YAAM,OAAO,SAAS,OAAO,aAAa,KAAK,IAAI,UAAU,IAAI;AAAA,IACnE;AAEA,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAE9C,WAAO;AAAA,MACL,SAAS,cAAc;AAAA,MACvB,WAAW,cAAc,IAAI,CAAC,MAAM,EAAE,QAAQ;AAAA,IAChD;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,WACpB,QACA,aACA,UACA,WACkB;AAClB,mBAAiB,QAAQ;AACzB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,OAAO,KAAK,aAAa,KAAK,IAAI,UAAU,SAAS;AACjF,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,YACpB,QACA,aACA,UACA,WACA,UACgB;AAChB,mBAAiB,QAAQ;AAGzB,QAAM,aAAa,MAAM,cAAc,UAAU,SAAS;AAC1D,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,IAAI,MAAM,4BAA4B,WAAW,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EAC5E;AACA,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,eAAW,KAAK,WAAW,UAAU;AACnC,cAAQ,KAAK,YAAY,CAAC,EAAE;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,QAAQ,MAAM,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,UAAU,WAAW,QAAQ;AAC5F,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,YACpB,QACA,aACA,UACA,WACA,SACe;AACf,mBAAiB,QAAQ;AACzB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,UAAU,WAAW,OAAO;AAC7E,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAAA,EAChD,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,uBACpB,QACA,aACA,OAC8B;AAC9B,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,eAAe,MAAM,OAAO,oBAAoB,IAAI,aAAa,KAAK,IAAI,KAAK;AACrF,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,iBACpB,QACA,aACA,SACqB;AACrB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,QAAQ,MAAM,aAAa,KAAK,IAAI,OAAO;AACvE,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;;;AC5PA,SAAS,WAAAE,UAAS,YAAAC,WAAU,QAAAC,aAAY;AACxC,SAAS,WAAAC,UAAS,UAAU,QAAAC,aAAY;AAaxC,IAAM,mBAAmB;AAEzB,eAAsB,wBAAwB,KAAqC;AACjF,MAAI;AACJ,MAAI;AACF,cAAU,MAAMJ,SAAQ,GAAG;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI,MAAM,sCAAsC,GAAG,EAAE;AAAA,EAC7D;AAEA,QAAM,QAAuB,CAAC;AAE9B,aAAW,SAAS,SAAS;AAC3B,QAAIG,SAAQ,KAAK,MAAM,OAAQ;AAE/B,UAAM,WAAW,SAAS,OAAO,MAAM;AACvC,UAAM,WAAWC,MAAK,KAAK,KAAK;AAEhC,UAAM,QAAQ,MAAMF,MAAK,QAAQ;AACjC,QAAI,CAAC,MAAM,OAAO,EAAG;AAErB,UAAM,QAAQ,MAAMD,UAAS,UAAU,OAAO,GAAG,KAAK;AACtD,QAAI,KAAK,WAAW,EAAG;AAEvB,UAAM,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,EAC/B;AAEA,SAAO;AACT;AAEO,SAAS,qBAAqB,OAA8C;AACjF,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAE5B,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,IAAI,KAAK,QAAQ,GAAG;AAC3B,aAAO,KAAK,4BAA4B,KAAK,QAAQ,EAAE;AAAA,IACzD;AACA,SAAK,IAAI,KAAK,QAAQ;AAEtB,QAAI,KAAK,KAAK,SAAS,kBAAkB;AACvC,aAAO;AAAA,QACL,sBAAsB,KAAK,QAAQ,YAAY,gBAAgB,WAAW,KAAK,KAAK,MAAM;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,QAAQ,SAAS;AACxD;;;AC/DA,SAAS,QAAAI,aAAY;AAuBrB,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,gBAAgB;AAEtB,eAAsB,sBAAsB,SAAmD;AAC7F,QAAM,SAA0B,CAAC;AAGjC,QAAM,aAAa,MAAM,mBAAmB,QAAQ,QAAQ;AAC5D,SAAO,KAAK;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,WAAW;AAAA,IACnB,SAAS,WAAW,QAChB,SAAS,WAAW,QAAQ,UAAUC,YAAW,WAAW,SAAS,CAAC,MACtE,WAAW,OAAO,KAAK,IAAI;AAAA,EACjC,CAAC;AAGD,MAAI,QAAQ,aAAa;AACvB,QAAI;AACF,YAAM,QAAQ,MAAMC,MAAK,QAAQ,WAAW;AAC5C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ,MAAM,OAAO;AAAA,QACrB,SAAS,MAAM,OAAO,IAClB,uBAAuBD,YAAW,MAAM,IAAI,CAAC,MAC7C;AAAA,MACN,CAAC;AAAA,IACH,QAAQ;AACN,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,2BAA2B,QAAQ,WAAW;AAAA,MACzD,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,QAAQ,OAAO;AACjB,UAAM,UAAU,gBAAgB,IAAI,QAAQ,KAAK,KAAK,cAAc,KAAK,QAAQ,KAAK;AACtF,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,UACL,UAAU,QAAQ,KAAK,eACvB,uBAAuB,QAAQ,KAAK;AAAA,IAC1C,CAAC;AAAA,EACH;AAGA,MAAI,gBAAgB,QAAQ;AAC5B,MAAI,QAAQ,UAAU;AACpB,QAAI;AACF,sBAAgB,MAAM,wBAAwB,QAAQ,QAAQ;AAC9D,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,0BAA0B,cAAc,MAAM;AAAA,MACzD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,UAAM,cAAc,qBAAqB,aAAa;AACtD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,YAAY;AAAA,MACpB,SAAS,YAAY,QACjB,wBAAwB,cAAc,MAAM,kBAC5C,YAAY,OAAO,KAAK,IAAI;AAAA,IAClC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM;AAAA,IACnC;AAAA,EACF;AACF;AAEA,SAASA,YAAW,OAAuB;AACzC,MAAI,SAAS,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AACtE,MAAI,SAAS,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AACtD,SAAO,GAAG,KAAK;AACjB;;;AChHA,eAAsB,QACpB,QACA,aACA,UACA,SACwB;AAExB,MAAI;AACJ,MAAI,QAAQ,UAAU;AACpB,mBAAe,MAAM,wBAAwB,QAAQ,QAAQ;AAAA,EAC/D,WAAW,QAAQ,OAAO;AACxB,mBAAe,CAAC,EAAE,UAAU,SAAS,MAAM,QAAQ,MAAM,CAAC;AAAA,EAC5D;AAGA,QAAM,aAAa,MAAM,sBAAsB;AAAA,IAC7C;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,OAAO,QAAQ,SAAS;AAAA,IACxB,OAAO;AAAA,EACT,CAAC;AAED,MAAI,CAAC,WAAW,OAAO;AACrB,WAAO,EAAE,WAAW;AAAA,EACtB;AAGA,QAAM,SAAS,MAAM,cAAc,QAAQ,aAAa,UAAU;AAAA,IAChE,OAAO,QAAQ,SAAS;AAAA,IACxB,cAAc,QAAQ,iBAAiB,QAAQ,iBAAiB,MAAM;AAAA,IACtE;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,aAAa,QAAQ;AAAA,EACvB,CAAC;AAED,SAAO,EAAE,YAAY,OAAO;AAC9B;;;ACxDA,SAAS,mBAAmB;AAgB5B,eAAsB,YACpB,QACA,aACA,SACmB;AACnB,QAAM,aAAiC,CAAC;AACxC,MAAI,SAAS,oBAAqB,YAAW,sBAAsB,QAAQ;AAC3E,MAAI,SAAS,WAAY,YAAW,aAAa,QAAQ;AAEzD,QAAM,WAAW,MAAM,OAAO,QAAQ,KAAK,aAAa,UAAU;AAClE,MAAI,UAAU,SAAS,WAAW,CAAC;AAGnC,MAAI,SAAS,UAAU,QAAW;AAChC,cAAU,QAAQ,OAAO,CAAC,MAAM;AAC9B,YAAM,cAAc,EAAE,WAAW,CAAC,GAAG;AACrC,aAAO,eAAe,YAAY,eAAe,QAAQ;AAAA,IAC3D,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,UAAU;AACrB,cAAU,QAAQ,OAAO,CAAC,MAAM;AAC9B,YAAM,cAAc,EAAE,WAAW,CAAC,GAAG;AACrC,aAAO,aAAa,qBAAqB,QAAQ;AAAA,IACnD,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,OAAO;AAClB,UAAM,YAAY,IAAI,KAAK,QAAQ,KAAK,EAAE,QAAQ,IAAI;AACtD,cAAU,QAAQ,OAAO,CAAC,MAAM;AAC9B,YAAM,cAAc,EAAE,WAAW,CAAC,GAAG;AACrC,aAAO,eAAe,OAAO,YAAY,aAAa,OAAO,KAAK;AAAA,IACpE,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,eAAsB,UACpB,QACA,aACA,UACA,qBACiB;AACjB,SAAO,OAAO,QAAQ,IAAI,aAAa,UAAU,mBAAmB;AACtE;AAEA,IAAM,mBAAmB;AAEzB,eAAsB,cACpB,QACA,aACA,UACA,WAC8B;AAC9B,MAAI,UAAU,SAAS,kBAAkB;AACvC,UAAM,IAAI;AAAA,MACR,sBAAsB,gBAAgB,gBAAgB,UAAU,MAAM,oCAAoC,gBAAgB;AAAA,IAC5H;AAAA,EACF;AACA,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AACA,SAAO,OAAO,QAAQ,MAAM,aAAa,UAAU,SAAS;AAC9D;AAEA,eAAsB,cACpB,QACA,aACA,SACiB;AACjB,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,YAAoB,OAAO,cAAc;AAC3E,UAAM,aAAiC,EAAE,OAAO,UAAU;AAC1D,QAAI,SAAS,oBAAqB,YAAW,sBAAsB,QAAQ;AAC3E,UAAM,WAAW,MAAM,OAAO,QAAQ,KAAK,aAAa,UAAU;AAClE,WAAO;AAAA,MACL,OAAO,SAAS,WAAW,CAAC;AAAA,MAC5B,eAAe,SAAS,iBAAiB;AAAA,IAC3C;AAAA,EACF,CAAC;AAED,MAAI,WAAW;AAEf,MAAI,SAAS,UAAU,QAAW;AAChC,eAAW,SAAS,OAAO,CAAC,MAAM;AAChC,YAAM,KAAK,EAAE,WAAW,CAAC,GAAG;AAC5B,aAAO,MAAM,GAAG,eAAe,QAAQ;AAAA,IACzC,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,UAAU;AACrB,eAAW,SAAS,OAAO,CAAC,MAAM;AAChC,YAAM,KAAK,EAAE,WAAW,CAAC,GAAG;AAC5B,aAAO,IAAI,qBAAqB,QAAQ;AAAA,IAC1C,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,OAAO;AAClB,UAAM,YAAY,IAAI,KAAK,QAAQ,KAAK,EAAE,QAAQ,IAAI;AACtD,eAAW,SAAS,OAAO,CAAC,MAAM;AAChC,YAAM,KAAK,EAAE,WAAW,CAAC,GAAG;AAC5B,aAAO,MAAM,OAAO,GAAG,aAAa,OAAO,KAAK;AAAA,IAClD,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,OAAO;AAC7B,WAAO,aAAa,QAAQ;AAAA,EAC9B;AAEA,SAAO,KAAK,UAAU,UAAU,MAAM,CAAC;AACzC;AAEA,SAAS,aAAa,SAA2B;AAC/C,QAAM,SAAS;AACf,QAAM,OAAO,QAAQ,IAAI,CAAC,MAAM;AAC9B,UAAM,KAAK,EAAE,WAAW,CAAC,GAAG;AAC5B,UAAM,SAAS;AAAA,MACb,EAAE;AAAA,MACF,UAAU,EAAE,UAAU;AAAA,MACtB,IAAI,cAAc;AAAA,MAClB,UAAU,IAAI,QAAQ,EAAE;AAAA,MACxB,IAAI,oBAAoB;AAAA,MACxB,KAAK,IAAI,KAAK,OAAO,GAAG,aAAa,OAAO,IAAI,GAAI,EAAE,YAAY,IAAI;AAAA,MACtE,UAAU,IAAI,UAAU,EAAE;AAAA,MAC1B,UAAU,IAAI,kBAAkB,EAAE;AAAA,IACpC;AACA,WAAO,OAAO,KAAK,GAAG;AAAA,EACxB,CAAC;AACD,SAAO,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI;AACpC;AAEA,SAAS,UAAU,OAAuB;AACxC,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,GAAG;AACtE,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;;;ACxHA,SAAS,WAAW,SAA8C;AAChE,QAAM,QAAwB;AAAA,IAC5B,SAAS,CAAC,oBAAoB,eAAe;AAAA,EAC/C;AAEA,MAAI,SAAS,WAAW;AACtB,UAAM,aAAa,CAAC,QAAQ,SAAS;AAAA,EACvC;AAEA,MAAI,SAAS,MAAM;AACjB,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,QAAQ,oBAAI,KAAK;AACvB,UAAM,QAAQ,MAAM,QAAQ,IAAI,QAAQ,IAAI;AAC5C,UAAM,eAAe;AAAA,MACnB,mBAAmB,QAAQ,eAAe;AAAA,MAC1C,WAAW;AAAA,QACT,MAAM,MAAM,YAAY;AAAA,QACxB,OAAO,MAAM,SAAS,IAAI;AAAA,QAC1B,KAAK,MAAM,QAAQ;AAAA,MACrB;AAAA,MACA,SAAS;AAAA,QACP,MAAM,IAAI,YAAY;AAAA,QACtB,OAAO,IAAI,SAAS,IAAI;AAAA,QACxB,KAAK,IAAI,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,YACb,WACA,aACA,WACA,SAC4B;AAC5B,QAAM,QAAQ,WAAW,OAAO;AAChC,SAAO,UAAU,eAAe,aAAa,WAAW,KAAK;AAC/D;AAEA,eAAsB,kBACpB,WACA,aACyB;AACzB,QAAM,aAAwD;AAAA,IAC5D,CAAC,oBAAoB,WAAW;AAAA,IAChC,CAAC,kBAAkB,SAAS;AAAA,IAC5B,CAAC,wBAAwB,eAAe;AAAA,IACxC,CAAC,4BAA4B,mBAAmB;AAAA,IAChD,CAAC,8BAA8B,qBAAqB;AAAA,IACpD,CAAC,sCAAsC,mBAAmB;AAAA,EAC5D;AAEA,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,WAAW;AAAA,MAAI,CAAC,CAAC,MAAM,MACrB,UAAU,eAAe,aAAa,QAAQ;AAAA,QAC5C,SAAS,CAAC,oBAAoB,eAAe;AAAA,MAC/C,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,WAA2B,CAAC;AAClC,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,QAAQ,WAAW,CAAC;AAC1B,QAAI,CAAC,MAAO;AACZ,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,SAAS,QAAQ,CAAC;AACxB,QAAI,CAAC,OAAQ;AACb,QAAI,OAAO,WAAW,aAAa;AACjC,eAAS,GAAG,IAAI,OAAO,MAAM,QAAQ,CAAC;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,iBACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,oBAAoB,OAAO;AACxE;AAEA,eAAsB,aACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,kBAAkB,OAAO;AACtE;AAEA,eAAsB,iBACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,wBAAwB,OAAO;AAC5E;AAEA,eAAsB,mBACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,4BAA4B,OAAO;AAChF;AAEA,eAAsB,iBACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,8BAA8B,OAAO;AAClF;AAEA,eAAsB,gBACpB,WACA,aACA,SAC4B;AAC5B,SAAO,YAAY,WAAW,aAAa,sCAAsC,OAAO;AAC1F;AAEA,eAAsB,mBACpB,WACA,aACmC;AACnC,SAAO,UAAU,aAAa,WAAW;AAC3C;AAEA,eAAsB,mBACpB,WACA,aACA,SAC8B;AAC9B,SAAO,UAAU,kBAAkB,aAAa,SAAS,QAAQ,SAAS,UAAU;AACtF;AAUA,eAAsB,mBACpB,WACA,aACA,WACA,OAAe,GACiB;AAChC,QAAM,MAAM,oBAAI,KAAK;AAGrB,QAAM,aAAa,IAAI,KAAK,GAAG;AAC/B,QAAM,eAAe,IAAI,KAAK,GAAG;AACjC,eAAa,QAAQ,aAAa,QAAQ,IAAI,IAAI;AAGlD,QAAM,cAAc,IAAI,KAAK,YAAY;AACzC,QAAM,gBAAgB,IAAI,KAAK,WAAW;AAC1C,gBAAc,QAAQ,cAAc,QAAQ,IAAI,IAAI;AAEpD,QAAM,YAAY,CAAC,OAAa,SAA+B;AAAA,IAC7D,SAAS,CAAC,oBAAoB,eAAe;AAAA,IAC7C,cAAc;AAAA,MACZ,mBAAmB;AAAA,MACnB,WAAW,EAAE,MAAM,MAAM,YAAY,GAAG,OAAO,MAAM,SAAS,IAAI,GAAG,KAAK,MAAM,QAAQ,EAAE;AAAA,MAC1F,SAAS,EAAE,MAAM,IAAI,YAAY,GAAG,OAAO,IAAI,SAAS,IAAI,GAAG,KAAK,IAAI,QAAQ,EAAE;AAAA,IACpF;AAAA,EACF;AAEA,QAAM,CAAC,eAAe,cAAc,IAAI,MAAM,QAAQ,IAAI;AAAA,IACxD,UAAU,eAAe,aAAa,WAAW,UAAU,cAAc,UAAU,CAAC;AAAA,IACpF,UAAU,eAAe,aAAa,WAAW,UAAU,eAAe,WAAW,CAAC;AAAA,EACxF,CAAC;AAED,QAAM,aAAa,CAAC,SAAsD;AACxE,QAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AACvC,UAAM,SAAS,KACZ,IAAI,CAAC,MAAM;AACV,YAAM,OAAO,OAAO,KAAK,EAAE,OAAO;AAClC,YAAM,QAAQ,KAAK,CAAC;AACpB,aAAO,QAAQ,OAAO,EAAE,QAAQ,KAAK,GAAG,cAAc,KAAK,IAAI;AAAA,IACjE,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC1B,QAAI,OAAO,WAAW,EAAG,QAAO;AAChC,WAAO,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO;AAAA,EACpD;AAEA,QAAM,UAAU,WAAW,cAAc,IAAI;AAC7C,QAAM,WAAW,WAAW,eAAe,IAAI;AAE/C,MAAI;AACJ,MAAI,YAAgD;AAEpD,MAAI,YAAY,UAAa,aAAa,UAAa,aAAa,GAAG;AACrE,qBAAkB,UAAU,YAAY,WAAY;AACpD,QAAI,KAAK,IAAI,aAAa,IAAI,GAAG;AAC/B,kBAAY;AAAA,IACd,WAAW,gBAAgB,GAAG;AAC5B,kBAAY;AAAA,IACd,OAAO;AACL,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,eAAe,kBAAkB,SAAY,KAAK,MAAM,gBAAgB,EAAE,IAAI,KAAK;AAAA,IACnF;AAAA,EACF;AACF;AAEO,SAAS,eAAe,OAA2B,WAAoC;AAC5F,SAAO;AAAA,IACL,UAAU,UAAU,UAAa,QAAQ;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AACF;;;AC3PA,SAAS,eAAAE,oBAAmB;AAS5B,eAAsB,kBACpB,QACA,aACA,SACoE;AACpE,MAAI,SAAS,SAAS,SAAS,UAAU;AACvC,UAAM,SAAS,MAAMA;AAAA,MACnB,OAAO,cAAc;AACnB,cAAM,OAAO,MAAM,OAAO,cAAc,KAAK,aAAa;AAAA,UACxD;AAAA,UACA,UAAU,SAAS;AAAA,QACrB,CAAC;AACD,eAAO,EAAE,OAAO,KAAK,iBAAiB,CAAC,GAAG,eAAe,KAAK,cAAc;AAAA,MAC9E;AAAA,MACA,EAAE,OAAO,QAAQ,OAAO,gBAAgB,QAAQ,SAAS;AAAA,IAC3D;AACA,WAAO,EAAE,eAAe,OAAO,OAAO,eAAe,OAAO,cAAc;AAAA,EAC5E;AACA,SAAO,OAAO,cAAc,KAAK,aAAa;AAAA,IAC5C,WAAW,SAAS;AAAA,IACpB,UAAU,SAAS;AAAA,EACrB,CAAC;AACH;AAEA,eAAsB,gBACpB,QACA,aACA,WACuB;AACvB,SAAO,OAAO,cAAc,IAAI,aAAa,SAAS;AACxD;AAEA,eAAsB,mBACpB,QACA,aACA,MACuB;AACvB,SAAO,OAAO,cAAc,OAAO,aAAa,IAAI;AACtD;AAEA,eAAsB,mBACpB,QACA,aACA,WACA,MACA,YACuB;AACvB,SAAO,OAAO,cAAc,OAAO,aAAa,WAAW,MAAM,UAAU;AAC7E;AAEA,eAAsB,mBACpB,QACA,aACA,WACe;AACf,SAAO,OAAO,cAAc,OAAO,aAAa,SAAS;AAC3D;AAEA,eAAsB,iBACpB,QACA,aACA,WACA,YACuB;AACvB,SAAO,OAAO,cAAc,iBAAiB,aAAa,WAAW,UAAU;AACjF;AAEA,eAAsB,mBACpB,QACA,aACA,WACA,YACuB;AACvB,SAAO,OAAO,cAAc,mBAAmB,aAAa,WAAW,UAAU;AACnF;AAEA,eAAsB,eACpB,QACA,aACA,WACA,YACe;AACf,SAAO,OAAO,cAAc,eAAe,aAAa,WAAW,UAAU;AAC/E;AAEA,eAAsB,cACpB,QACA,aACA,WACA,YACA,MACuB;AACvB,SAAO,OAAO,cAAc,cAAc,aAAa,WAAW,YAAY,IAAI;AACpF;AAEA,eAAsB,WACpB,QACA,aACA,WACA,YAC6B;AAC7B,SAAO,OAAO,cAAc,WAAW,aAAa,WAAW,UAAU;AAC3E;AAEA,eAAsB,SACpB,QACA,aACA,WACA,YACA,SAC4B;AAC5B,SAAO,OAAO,cAAc,SAAS,aAAa,WAAW,YAAY,OAAO;AAClF;AAEA,eAAsB,YACpB,QACA,aACA,WACA,YACA,MAC4B;AAC5B,SAAO,OAAO,cAAc,YAAY,aAAa,WAAW,YAAY,IAAI;AAClF;AAEA,eAAsB,YACpB,QACA,aACA,WACA,YACA,SACA,MACA,YAC4B;AAC5B,SAAO,OAAO,cAAc;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,YACpB,QACA,aACA,WACA,YACA,SACe;AACf,SAAO,OAAO,cAAc,YAAY,aAAa,WAAW,YAAY,OAAO;AACrF;AAEA,eAAsB,cACpB,QACA,aACA,WACA,YACA,SAC4B;AAC5B,SAAO,OAAO,cAAc,cAAc,aAAa,WAAW,YAAY,OAAO;AACvF;AAEA,eAAsB,gBACpB,QACA,aACA,WACA,YACA,SAC4B;AAC5B,SAAO,OAAO,cAAc,gBAAgB,aAAa,WAAW,YAAY,OAAO;AACzF;;;AC3LA,SAAS,WAAAC,UAAS,YAAAC,iBAAgB;AAClC,SAAS,QAAAC,aAAY;AAErB,SAAS,eAAAC,oBAAmB;AAS5B,eAAsB,kBACpB,QACA,aACA,SACmE;AACnE,MAAI,SAAS,SAAS,SAAS,UAAU;AACvC,UAAM,SAAS,MAAMA;AAAA,MACnB,OAAO,cAAc;AACnB,cAAM,OAAO,MAAM,OAAO,cAAc,KAAK,aAAa;AAAA,UACxD,OAAO;AAAA,UACP,YAAY,SAAS;AAAA,QACvB,CAAC;AACD,eAAO;AAAA,UACL,OAAO,KAAK,gBAAgB,CAAC;AAAA,UAC7B,eAAe,KAAK,iBAAiB;AAAA,QACvC;AAAA,MACF;AAAA,MACA,EAAE,OAAO,QAAQ,OAAO,gBAAgB,QAAQ,SAAS;AAAA,IAC3D;AACA,WAAO,EAAE,cAAc,OAAO,OAAO,eAAe,OAAO,cAAc;AAAA,EAC3E;AACA,SAAO,OAAO,cAAc,KAAK,aAAa;AAAA,IAC5C,OAAO,SAAS;AAAA,IAChB,YAAY,SAAS;AAAA,EACvB,CAAC;AACH;AAEA,eAAsB,gBACpB,QACA,aACA,KACuB;AACvB,SAAO,OAAO,cAAc,IAAI,aAAa,GAAG;AAClD;AAEA,eAAsB,mBACpB,QACA,aACA,MACuB;AACvB,SAAO,OAAO,cAAc,OAAO,aAAa,IAAI;AACtD;AAEA,eAAsB,mBACpB,QACA,aACA,KACA,MACuB;AACvB,SAAO,OAAO,cAAc,OAAO,aAAa,KAAK,IAAI;AAC3D;AAEA,eAAsB,mBACpB,QACA,aACA,KACe;AACf,SAAO,OAAO,cAAc,OAAO,aAAa,GAAG;AACrD;AASA,eAAsB,kBACpB,QACA,aACA,KACA,SACqB;AACrB,QAAM,QAAQ,MAAMH,SAAQ,GAAG;AAC/B,QAAM,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAEzD,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC,EAAE;AAAA,EAC1D;AAEA,QAAM,gBAAgC,CAAC;AACvC,aAAW,QAAQ,WAAW;AAC5B,UAAM,UAAU,MAAMC,UAASC,MAAK,KAAK,IAAI,GAAG,OAAO;AACvD,kBAAc,KAAK,KAAK,MAAM,OAAO,CAAiB;AAAA,EACxD;AAEA,QAAM,WAAW,MAAM,OAAO,cAAc,KAAK,WAAW;AAC5D,QAAM,aAAa,IAAI,KAAK,SAAS,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;AAE1E,MAAI,UAAU;AACd,MAAI,UAAU;AACd,QAAM,YAAY;AAClB,QAAM,OAAiB,CAAC;AAExB,aAAW,WAAW,eAAe;AACnC,SAAK,KAAK,QAAQ,GAAG;AACrB,QAAI,WAAW,IAAI,QAAQ,GAAG,GAAG;AAC/B,UAAI,CAAC,SAAS,QAAQ;AACpB,cAAM,OAAO,cAAc,OAAO,aAAa,QAAQ,KAAK,OAAO;AAAA,MACrE;AACA;AAAA,IACF,OAAO;AACL,UAAI,CAAC,SAAS,QAAQ;AACpB,cAAM,OAAO,cAAc,OAAO,aAAa,OAAO;AAAA,MACxD;AACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,WAAW,KAAK;AAC7C;;;AC5CA,SAAS,eAAAE,oBAAmB;AAvE5B,eAAsB,mBACpB,QACA,aACA,WACA,OAC0B;AAC1B,SAAO,OAAO,UAAU,WAAW,aAAa,WAAW,KAAK;AAClE;AAEA,eAAsB,2BACpB,QACA,aACA,WACA,OACA,SACe;AACf,QAAM,OAAO,UAAU,EAAE,kBAAkB,QAAQ,IAAI;AACvD,SAAO,OAAO,UAAU,mBAAmB,aAAa,WAAW,OAAO,IAAI;AAChF;AAEA,eAAsB,uBACpB,QACA,aACA,WACA,OACe;AACf,SAAO,OAAO,UAAU,eAAe,aAAa,WAAW,KAAK;AACtE;AAEA,eAAsB,wBACpB,QACA,aACA,OACiC;AACjC,SAAO,OAAO,UAAU,kBAAkB,aAAa,KAAK;AAC9D;AAEA,eAAsB,2BACpB,QACA,aACA,gBACA,OACe;AACf,SAAO,OAAO,UAAU,mBAAmB,aAAa,gBAAgB,KAAK;AAC/E;AAEA,eAAsB,0BACpB,QACA,aACA,gBACA,OACA,eACoC;AACpC,QAAM,MAAM,MAAM,OAAO,UAAU,kBAAkB,aAAa,gBAAgB,KAAK;AACvF,SAAO,OAAO,UAAU,kBAAkB,aAAa,gBAAgB,OAAO;AAAA,IAC5E,cAAc;AAAA,MACZ,0BAA0B,IAAI;AAAA,MAC9B,yBAAyB,OAAO,IAAI,KAAK,aAAa,EAAE,QAAQ,CAAC;AAAA,IACnE;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,2BACpB,QACA,aACA,OACe;AACf,SAAO,OAAO,UAAU,qBAAqB,aAAa,KAAK;AACjE;AAaA,eAAsB,oBACpB,QACA,aACA,SACwE;AACxE,MAAI,SAAS,SAAS,SAAS,UAAU;AACvC,UAAM,SAAS,MAAMA;AAAA,MACnB,OAAO,cAAc;AACnB,cAAM,OAAO,MAAM,OAAO,UAAU,WAAW,aAAa;AAAA,UAC1D,WAAW,SAAS;AAAA,UACpB,SAAS,SAAS;AAAA,UAClB,YAAY,SAAS;AAAA,UACrB,OAAO;AAAA,QACT,CAAC;AACD,eAAO;AAAA,UACL,OAAO,KAAK,mBAAmB,CAAC;AAAA,UAChC,eAAe,KAAK,iBAAiB;AAAA,QACvC;AAAA,MACF;AAAA,MACA,EAAE,OAAO,QAAQ,OAAO,gBAAgB,QAAQ,SAAS;AAAA,IAC3D;AACA,WAAO,EAAE,iBAAiB,OAAO,OAAO,eAAe,OAAO,cAAc;AAAA,EAC9E;AACA,SAAO,OAAO,UAAU,WAAW,aAAa,OAAO;AACzD;AAEA,eAAsB,YACpB,QACA,aACA,SACA,SACe;AACf,SAAO,OAAO,OAAO,OAAO,aAAa,SAAS,OAAO;AAC3D;;;ACvHA,eAAsB,oBACpB,QACA,aACA,cACA,QACsC;AACtC,QAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,QAAM,aAAa,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK;AAC3C,QAAM,QAAQ,OAAO,WAAW,OAAO,GAAG,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC;AAE1D,SAAO,OAAO,aAAa,oBAAoB,aAAa;AAAA,IAC1D,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACjBA,IAAM,yBAA8C,oBAAI,IAAI;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,qBAA0C,oBAAI,IAAI;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,mBAAwC,oBAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,sBAAsB,MAAuB;AAC3D,SAAO,uBAAuB,IAAI,IAAI;AACxC;AAEO,SAAS,kBAAkB,MAAuB;AACvD,SAAO,mBAAmB,IAAI,IAAI;AACpC;AAEO,SAAS,kBAAkB,MAAkC;AAClE,SAAO,uBAAuB,IAAI,IAAI,KAAK,mBAAmB,IAAI,IAAI;AACxE;AAEO,SAAS,sBAAsB,KAAoC;AACxE,SAAO,iBAAiB,IAAI,GAAG;AACjC;AAOO,SAAS,WAAW,UAA+B;AACxD,QAAM,QAAQ,oBAAoB,KAAK,QAAQ;AAC/C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,yBAAyB,QAAQ,sCAAsC;AAAA,EACzF;AACA,QAAM,OAAO,OAAO,MAAM,CAAC,CAAC;AAC5B,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AAC7B,MAAI,QAAQ,KAAK,QAAQ,IAAI;AAC3B,UAAM,IAAI,MAAM,kBAAkB,KAAK,+BAA+B;AAAA,EACxE;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,eAAsB,YACpB,QACA,aACA,YACA,MACA,OACyB;AACzB,QAAM,WAAW,MAAM,OAAO,QAAQ,KAAK,aAAa,YAAY,MAAM,KAAK;AAC/E,SAAO,SAAS,WAAW,CAAC;AAC9B;AAEA,eAAsB,eACpB,QACA,aACA,YACA,MACA,OACiB;AACjB,QAAM,UAAU,MAAM,YAAY,QAAQ,aAAa,YAAY,MAAM,KAAK;AAE9E,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI;AAAA,MACR,MAAM,UAAU,sBAAsB,IAAI,IAAI,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IAC9E;AAAA,EACF;AAGA,QAAM,SAAS,QAAQ,CAAC;AACxB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,MAAM,UAAU,sBAAsB,IAAI,IAAI,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IAC9E;AAAA,EACF;AACA,QAAM,MAAM,OAAO;AACnB,QAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,mDAAmD,SAAS,MAAM,EAAE;AAAA,EACtF;AAEA,SAAO,SAAS,KAAK;AACvB;;;ACtGA,SAAS,eAAAC,oBAAmB;AAErB,IAAM,iCACX;AASF,eAAsB,UACpB,QACA,aACA,SACoD;AACpD,MAAI,SAAS,SAAS,SAAS,UAAU;AACvC,UAAM,SAAS,MAAMA;AAAA,MACnB,OAAO,cAAc;AACnB,cAAM,OAAO,MAAM,OAAO,KAAK,aAAa,EAAE,WAAW,UAAU,SAAS,SAAS,CAAC;AACtF,eAAO,EAAE,OAAO,KAAK,SAAS,CAAC,GAAG,eAAe,KAAK,cAAc;AAAA,MACtE;AAAA,MACA,EAAE,OAAO,QAAQ,OAAO,gBAAgB,QAAQ,SAAS;AAAA,IAC3D;AACA,WAAO,EAAE,OAAO,OAAO,OAAO,eAAe,OAAO,cAAc;AAAA,EACpE;AACA,QAAM,WAAW,MAAM,OAAO,KAAK,aAAa,OAAO;AACvD,SAAO,EAAE,OAAO,SAAS,SAAS,CAAC,GAAG,eAAe,SAAS,cAAc;AAC9E;AAEA,eAAsB,QACpB,QACA,aACA,QACe;AACf,SAAO,OAAO,IAAI,aAAa,MAAM;AACvC;AAEA,eAAsB,WACpB,QACA,aACA,OACA,aACA,QACe;AACf,QAAM,OAAsB,EAAE,MAAM;AACpC,MAAI,YAAa,MAAK,6BAA6B;AACnD,MAAI,OAAQ,MAAK,SAAS;AAC1B,SAAO,OAAO,OAAO,aAAa,IAAI;AACxC;AAEA,eAAsB,WACpB,QACA,aACA,QACA,aACA,QACe;AACf,QAAM,UAAyB,CAAC;AAChC,QAAM,QAAkB,CAAC;AAEzB,MAAI,aAAa;AACf,YAAQ,6BAA6B;AACrC,UAAM,KAAK,4BAA4B;AAAA,EACzC;AACA,MAAI,QAAQ;AACV,YAAQ,SAAS;AACjB,UAAM,KAAK,QAAQ;AAAA,EACrB;AAEA,QAAM,aAAa,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,IAAI;AACxD,SAAO,OAAO,OAAO,aAAa,QAAQ,SAAS,UAAU;AAC/D;AAEA,eAAsB,WACpB,QACA,aACA,QACe;AACf,SAAO,OAAO,OAAO,aAAa,MAAM;AAC1C;AAEO,SAAS,cAAc,UAAyB;AACrD,QAAM,WAAW,SAAS,QAAQ,GAAG;AACrC,MAAI,aAAa,IAAI;AACnB,UAAM,IAAI;AAAA,MACR,yBAAyB,QAAQ;AAAA,IACnC;AAAA,EACF;AACA,QAAM,cAAc,SAAS,MAAM,GAAG,QAAQ;AAC9C,QAAM,QAAQ,SAAS,MAAM,WAAW,CAAC,EAAE,MAAM,GAAG;AACpD,SAAO,EAAE,aAAa,qBAAqB,MAAM;AACnD;;;AC7FA,SAAS,YAAAC,iBAAgB;AAEzB,eAAsB,YACpB,QACA,aACA,OACkB;AAClB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,QAAQ,IAAI,aAAa,KAAK,IAAI,KAAK;AACpE,WAAO;AAAA,EACT,UAAE;AACA,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAAA,EAChD;AACF;AAEA,eAAsB,WACpB,QACA,aACA,OACA,aACkB;AAClB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,QAAQ,IAAI,aAAa,KAAK,IAAI,KAAK;AACpE,UAAM,WAAW,IAAI,IAAI,QAAQ,gBAAgB,CAAC,CAAC;AACnD,eAAW,SAAS,aAAa;AAC/B,eAAS,IAAI,MAAM,KAAK,CAAC;AAAA,IAC3B;AACA,UAAM,UAAU,MAAM,OAAO,QAAQ,OAAO,aAAa,KAAK,IAAI,OAAO;AAAA,MACvE,cAAc,CAAC,GAAG,QAAQ;AAAA,IAC5B,CAAC;AACD,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,cACpB,QACA,aACA,OACA,aACkB;AAClB,QAAM,OAAO,MAAM,OAAO,MAAM,OAAO,WAAW;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,QAAQ,IAAI,aAAa,KAAK,IAAI,KAAK;AACpE,UAAM,WAAW,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACzD,UAAM,YAAY,QAAQ,gBAAgB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAC5E,UAAM,UAAU,MAAM,OAAO,QAAQ,OAAO,aAAa,KAAK,IAAI,OAAO;AAAA,MACvE,cAAc;AAAA,IAChB,CAAC;AACD,UAAM,OAAO,MAAM,SAAS,aAAa,KAAK,EAAE;AAChD,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE;AAC9C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,OAAO,MAAM,OAAO,aAAa,KAAK,EAAE,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,qBACpB,QACA,aACA,OACA,SAC8C;AAC9C,QAAM,UAAU,MAAMA,UAAS,SAAS,OAAO;AAC/C,QAAM,SAAS,QACZ,MAAM,UAAU,EAChB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,SAAS,GAAG,CAAC;AAEhD,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,qCAAqC,OAAO,GAAG;AAAA,EACjE;AAEA,QAAM,UAAU,MAAM,WAAW,QAAQ,aAAa,OAAO,MAAM;AACnE,SAAO,EAAE,OAAO,OAAO,QAAQ,QAAQ;AACzC;;;ACnFA,SAAS,SAAS,iBAAiB;AAM5B,SAAS,SAAS,UAA0B;AACjD,SAAO,QAAQ,UAAU,QAAQ,CAAC;AACpC;AAMO,SAAS,eAAe,UAAkB,SAAyB;AACxE,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,OAAO,SAAS,OAAO;AAE7B,MAAI,CAAC,SAAS,WAAW,OAAO,GAAG,KAAK,aAAa,MAAM;AACzD,UAAM,IAAI,MAAM,SAAS,QAAQ,8CAA8C,OAAO,GAAG;AAAA,EAC3F;AAEA,SAAO;AACT;;;ACvBA,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;AACjC,SAAS,QAAAC,aAAY;AAgBrB,eAAsB,eAAe,SAAmD;AACtF,QAAM,EAAE,MAAM,KAAK,cAAc,eAAe,IAAI,GAAG,IAAI;AAG3D,QAAM,aAAa,KAAK,WAAW,aAAa,IAAI,OAAO,cAAc,IAAI;AAC7E,QAAM,YAAY,WAAW,QAAQ,gBAAgB,EAAE;AAEvD,QAAM,SAASA,MAAK,KAAK,KAAK;AAC9B,QAAM,UAAUA,MAAK,KAAK,OAAO;AAEjC,QAAMF,OAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,QAAMA,OAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,QAAM,QAAkB,CAAC;AAGzB,QAAM,MAAM;AAAA,IACV,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,MACP,KAAK;AAAA,QACH,QAAQ;AAAA,QACR,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,OAAO,CAAC,MAAM;AAAA,IACd,SAAS;AAAA,MACP,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,IACA,UAAU,CAAC,OAAO,cAAc,aAAa;AAAA,IAC7C,SAAS;AAAA,IACT,kBAAkB;AAAA,MAChB,uBAAuB;AAAA,IACzB;AAAA,IACA,iBAAiB;AAAA,MACf,uBAAuB;AAAA,MACvB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,EACF;AACA,QAAMC,WAAUC,MAAK,KAAK,cAAc,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAC9E,QAAM,KAAK,cAAc;AAGzB,QAAM,WAAW;AAAA,IACf,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,kBAAkB;AAAA,MAClB,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,SAAS,CAAC,KAAK;AAAA,EACjB;AACA,QAAMD,WAAUC,MAAK,KAAK,eAAe,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AACpF,QAAM,KAAK,eAAe;AAG1B,QAAM,aAAa;AAAA;AAAA;AAAA;AAAA,WAIV,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAiBD,SAAS;AAAA,2BACF,WAAW;AAAA;AAAA,uCAEC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAO/C,QAAMD,WAAUC,MAAK,QAAQ,UAAU,GAAG,UAAU;AACpD,QAAM,KAAK,cAAc;AAGzB,QAAM,cAAc;AAAA;AAAA;AAAA,YAGV,UAAU;AAAA;AAAA,gCAEU,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBxC,QAAMD,WAAUC,MAAK,SAAS,gBAAgB,GAAG,WAAW;AAC5D,QAAM,KAAK,sBAAsB;AAEjC,SAAO,EAAE,KAAK,MAAM;AACtB;;;ACrJA,SAAS,YAAY,OAAO,SAAAC,cAAa;AACzC,SAAS,QAAAC,aAAY;AAarB,IAAI,WAA0B;AAMvB,SAAS,UAAU,WAAyB;AACjD,aAAW;AACb;AAKA,eAAsB,cAAc,OAAkC;AACpE,MAAI,CAAC,SAAU;AAEf,MAAI;AACF,UAAMD,OAAM,UAAU,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACtD,UAAM,UAAUC,MAAK,UAAU,WAAW;AAC1C,UAAM,gBAAgB,gBAAgB,KAAK;AAC3C,UAAM,OAAO,KAAK,UAAU,aAAa,IAAI;AAC7C,UAAM,WAAW,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,IAAM,CAAC;AAClE,UAAM,MAAM,SAAS,GAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAER;AACF;AAEA,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,gBAAgB,OAA+B;AACtD,QAAM,WAAoC,CAAC;AAC3C,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,IAAI,GAAG;AAC/C,aAAS,CAAC,IAAI,mBAAmB,IAAI,CAAC,IAAI,eAAe;AAAA,EAC3D;AACA,SAAO,EAAE,GAAG,OAAO,MAAM,SAAS;AACpC;AAKO,SAAS,iBACd,SACA,MACA,KACY;AACZ,SAAO;AAAA,IACL,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["stat","extname","formatSize","readFile","stat","readdir","readFile","stat","extname","join","stat","formatSize","stat","paginateAll","readdir","readFile","join","paginateAll","paginateAll","paginateAll","readFile","mkdir","writeFile","join","mkdir","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/output.ts","../src/plugins.ts","../src/commands/apps.ts","../src/utils/file-validation.ts","../src/commands/releases.ts","../src/utils/bcp47.ts","../src/utils/image-validation.ts","../src/utils/fastlane.ts","../src/commands/listings.ts","../src/utils/release-notes.ts","../src/commands/validate.ts","../src/commands/publish.ts","../src/commands/reviews.ts","../src/commands/vitals.ts","../src/commands/subscriptions.ts","../src/commands/iap.ts","../src/commands/purchases.ts","../src/commands/pricing.ts","../src/commands/reports.ts","../src/commands/users.ts","../src/commands/testers.ts","../src/utils/safe-path.ts","../src/commands/plugin-scaffold.ts","../src/audit.ts"],"sourcesContent":["export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\n\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import type { OutputFormat } from \"@gpc-cli/config\";\nimport process from \"node:process\";\n\nexport function detectOutputFormat(): OutputFormat {\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nexport function formatOutput(data: unknown, format: OutputFormat, redact = true): string {\n const safe = redact ? redactSensitive(data) : data;\n switch (format) {\n case \"json\":\n return formatJson(safe);\n case \"yaml\":\n return formatYaml(safe);\n case \"markdown\":\n return formatMarkdown(safe);\n case \"table\":\n return formatTable(safe);\n default:\n return formatJson(safe);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field redaction\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_KEYS = new Set([\n \"private_key\",\n \"privateKey\",\n \"private_key_id\",\n \"privateKeyId\",\n \"accessToken\",\n \"access_token\",\n \"refreshToken\",\n \"refresh_token\",\n \"client_secret\",\n \"clientSecret\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n]);\n\nconst REDACTED = \"[REDACTED]\";\n\n/** Recursively redact sensitive fields from data before output. */\nexport function redactSensitive(data: unknown): unknown {\n if (data === null || data === undefined) return data;\n\n if (typeof data === \"string\") return data;\n if (typeof data === \"number\" || typeof data === \"boolean\") return data;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitive(item));\n }\n\n if (typeof data === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (SENSITIVE_KEYS.has(key) && typeof value === \"string\") {\n result[key] = REDACTED;\n } else {\n result[key] = redactSensitive(value);\n }\n }\n return result;\n }\n\n return data;\n}\n\nfunction formatJson(data: unknown): string {\n return JSON.stringify(data, null, 2);\n}\n\nfunction formatYaml(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return \"null\";\n }\n\n if (typeof data === \"string\") {\n return data.includes(\"\\n\")\n ? `|\\n${data\n .split(\"\\n\")\n .map((l) => `${\" \".repeat(indent + 1)}${l}`)\n .join(\"\\n\")}`\n : data;\n }\n\n if (typeof data === \"number\" || typeof data === \"boolean\") {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return \"[]\";\n return data\n .map((item) => {\n const value = formatYaml(item, indent + 1);\n const prefix = `${\" \".repeat(indent)}- `;\n if (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n const lines = value.split(\"\\n\");\n return `${prefix}${lines[0]}\\n${lines\n .slice(1)\n .map((l) => `${\" \".repeat(indent)} ${l}`)\n .join(\"\\n\")}`;\n }\n return `${prefix}${value}`;\n })\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return \"{}\";\n return entries\n .map(([key, value]) => {\n if (typeof value === \"object\" && value !== null) {\n return `${\" \".repeat(indent)}${key}:\\n${formatYaml(value, indent + 1)}`;\n }\n return `${\" \".repeat(indent)}${key}: ${formatYaml(value, indent)}`;\n })\n .join(\"\\n\");\n }\n\n return String(data);\n}\n\nfunction formatTable(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const body = rows\n .map((row) => keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" \"))\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction formatMarkdown(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" | \")} |`;\n const separator = `| ${widths.map((w) => \"-\".repeat(w)).join(\" | \")} |`;\n const body = rows\n .map(\n (row) =>\n `| ${keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" | \")} |`,\n )\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction toRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) {\n return data.filter(\n (item): item is Record<string, unknown> => typeof item === \"object\" && item !== null,\n );\n }\n if (typeof data === \"object\" && data !== null) {\n return [data as Record<string, unknown>];\n }\n return [];\n}\n","import type {\n GpcPlugin,\n PluginHooks,\n BeforeCommandHandler,\n AfterCommandHandler,\n ErrorHandler,\n BeforeRequestHandler,\n AfterResponseHandler,\n CommandEvent,\n CommandResult,\n PluginError,\n PluginCommand,\n PluginManifest,\n PluginPermission,\n RequestEvent,\n ResponseEvent,\n} from \"@gpc-cli/plugin-sdk\";\nimport { GpcError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Plugin Manager — orchestrates discovery, loading, and lifecycle\n// ---------------------------------------------------------------------------\n\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private beforeHandlers: BeforeCommandHandler[] = [];\n private afterHandlers: AfterCommandHandler[] = [];\n private errorHandlers: ErrorHandler[] = [];\n private beforeRequestHandlers: BeforeRequestHandler[] = [];\n private afterResponseHandlers: AfterResponseHandler[] = [];\n private registeredCommands: PluginCommand[] = [];\n\n /** Load and register a plugin */\n async load(plugin: GpcPlugin, manifest?: PluginManifest): Promise<void> {\n const isTrusted = manifest?.trusted ?? plugin.name.startsWith(\"@gpc-cli/\");\n\n if (!isTrusted && manifest?.permissions) {\n validatePermissions(manifest.permissions);\n }\n\n const hooks = createHooks(\n this.beforeHandlers,\n this.afterHandlers,\n this.errorHandlers,\n this.beforeRequestHandlers,\n this.afterResponseHandlers,\n this.registeredCommands,\n );\n\n await plugin.register(hooks);\n\n this.plugins.push({\n name: plugin.name,\n version: plugin.version,\n trusted: isTrusted,\n });\n }\n\n /** Run all beforeCommand handlers */\n async runBeforeCommand(event: CommandEvent): Promise<void> {\n for (const handler of this.beforeHandlers) {\n await handler(event);\n }\n }\n\n /** Run all afterCommand handlers */\n async runAfterCommand(event: CommandEvent, result: CommandResult): Promise<void> {\n for (const handler of this.afterHandlers) {\n await handler(event, result);\n }\n }\n\n /** Run all onError handlers */\n async runOnError(event: CommandEvent, error: PluginError): Promise<void> {\n for (const handler of this.errorHandlers) {\n try {\n await handler(event, error);\n } catch {\n // Don't let error handlers crash the process\n }\n }\n }\n\n /** Run all beforeRequest handlers */\n async runBeforeRequest(event: RequestEvent): Promise<void> {\n for (const handler of this.beforeRequestHandlers) {\n try {\n await handler(event);\n } catch {\n // Don't let request hooks block API calls\n }\n }\n }\n\n /** Run all afterResponse handlers */\n async runAfterResponse(event: RequestEvent, response: ResponseEvent): Promise<void> {\n for (const handler of this.afterResponseHandlers) {\n try {\n await handler(event, response);\n } catch {\n // Don't let response hooks crash the process\n }\n }\n }\n\n /** Get commands registered by plugins */\n getRegisteredCommands(): PluginCommand[] {\n return [...this.registeredCommands];\n }\n\n /** Get list of loaded plugins */\n getLoadedPlugins(): LoadedPlugin[] {\n return [...this.plugins];\n }\n\n /** Whether any request/response hooks are registered */\n hasRequestHooks(): boolean {\n return this.beforeRequestHandlers.length > 0 || this.afterResponseHandlers.length > 0;\n }\n\n /** Reset (for testing) */\n reset(): void {\n this.plugins = [];\n this.beforeHandlers = [];\n this.afterHandlers = [];\n this.errorHandlers = [];\n this.beforeRequestHandlers = [];\n this.afterResponseHandlers = [];\n this.registeredCommands = [];\n }\n}\n\nexport interface LoadedPlugin {\n name: string;\n version: string;\n trusted: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Hook factory\n// ---------------------------------------------------------------------------\n\nfunction createHooks(\n beforeHandlers: BeforeCommandHandler[],\n afterHandlers: AfterCommandHandler[],\n errorHandlers: ErrorHandler[],\n beforeRequestHandlers: BeforeRequestHandler[],\n afterResponseHandlers: AfterResponseHandler[],\n registeredCommands: PluginCommand[],\n): PluginHooks {\n return {\n beforeCommand(handler) {\n beforeHandlers.push(handler);\n },\n afterCommand(handler) {\n afterHandlers.push(handler);\n },\n onError(handler) {\n errorHandlers.push(handler);\n },\n beforeRequest(handler) {\n beforeRequestHandlers.push(handler);\n },\n afterResponse(handler) {\n afterResponseHandlers.push(handler);\n },\n registerCommands(registrar) {\n const registry = {\n add(cmd: PluginCommand) {\n registeredCommands.push(cmd);\n },\n };\n registrar(registry);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Permission validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PERMISSIONS: ReadonlySet<string> = new Set<PluginPermission>([\n \"read:config\",\n \"write:config\",\n \"read:auth\",\n \"api:read\",\n \"api:write\",\n \"commands:register\",\n \"hooks:beforeCommand\",\n \"hooks:afterCommand\",\n \"hooks:onError\",\n \"hooks:beforeRequest\",\n \"hooks:afterResponse\",\n]);\n\nfunction validatePermissions(permissions: PluginPermission[]): void {\n for (const perm of permissions) {\n if (!VALID_PERMISSIONS.has(perm)) {\n throw new GpcError(\n `Unknown plugin permission: \"${perm}\"`,\n \"PLUGIN_INVALID_PERMISSION\",\n 10,\n `Valid permissions: ${[...VALID_PERMISSIONS].join(\", \")}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin discovery\n// ---------------------------------------------------------------------------\n\nexport interface DiscoverPluginsOptions {\n /** Plugin names from config file */\n configPlugins?: string[];\n\n /** Working directory for node_modules scanning */\n cwd?: string;\n}\n\n/**\n * Discover plugins from multiple sources:\n * 1. Explicit config: gpc.config.ts → plugins: [...]\n * 2. Convention: node_modules/@gpc-cli/plugin-*\n * 3. Convention: node_modules/gpc-plugin-*\n */\nexport async function discoverPlugins(options?: DiscoverPluginsOptions): Promise<GpcPlugin[]> {\n const plugins: GpcPlugin[] = [];\n const seen = new Set<string>();\n\n // Source 1: Explicit config plugins\n if (options?.configPlugins) {\n for (const name of options.configPlugins) {\n if (seen.has(name)) continue;\n try {\n const mod = await import(name);\n const plugin = resolvePlugin(mod);\n if (plugin) {\n plugins.push(plugin);\n seen.add(name);\n }\n } catch {\n // Plugin not found — skip silently\n }\n }\n }\n\n return plugins;\n}\n\n/**\n * Resolve a plugin from a module.\n * Supports: default export, named `plugin` export, or the module itself as a plugin.\n */\nfunction resolvePlugin(mod: unknown): GpcPlugin | undefined {\n if (!mod || typeof mod !== \"object\") return undefined;\n\n const m = mod as Record<string, unknown>;\n\n // Check default export\n if (isPlugin(m[\"default\"])) return m[\"default\"];\n\n // Check named `plugin` export\n if (isPlugin(m[\"plugin\"])) return m[\"plugin\"];\n\n // Check if module itself is a plugin\n if (isPlugin(m)) return m as unknown as GpcPlugin;\n\n return undefined;\n}\n\nfunction isPlugin(obj: unknown): obj is GpcPlugin {\n if (!obj || typeof obj !== \"object\") return false;\n const p = obj as Record<string, unknown>;\n return (\n typeof p[\"name\"] === \"string\" &&\n typeof p[\"version\"] === \"string\" &&\n typeof p[\"register\"] === \"function\"\n );\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\n\nexport interface AppInfo {\n packageName: string;\n title?: string;\n defaultLanguage?: string;\n contactEmail?: string;\n}\n\nexport async function getAppInfo(client: PlayApiClient, packageName: string): Promise<AppInfo> {\n // Create an edit to read app details (Google Play requires an edit context)\n const edit = await client.edits.insert(packageName);\n try {\n const details = await client.details.get(packageName, edit.id);\n // Delete the edit since we're only reading\n await client.edits.delete(packageName, edit.id);\n return {\n packageName,\n title: details.title,\n defaultLanguage: details.defaultLanguage,\n contactEmail: details.contactEmail,\n };\n } catch (error) {\n // Clean up edit on failure\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 150 * 1024 * 1024; // 150 MB\nconst MAX_AAB_SIZE = 500 * 1024 * 1024; // 500 MB (Play Store limit for AABs)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 150 MB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 500 MB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes (ZIP format — both AAB and APK are ZIP-based)\n if (sizeBytes > 0) {\n try {\n const fd = await readFile(filePath, { flag: \"r\" });\n const header = fd.subarray(0, 4);\n\n if (!header.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, Release, Track } from \"@gpc-cli/api\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n}\n\nexport interface DryRunUploadResult {\n dryRun: true;\n file: { path: string; valid: boolean; errors: string[]; warnings: string[] };\n track: string;\n currentReleases: { versionCodes: string[]; status: string; userFraction?: number }[];\n plannedRelease: { status: string; userFraction?: number };\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n dryRun?: boolean;\n },\n): Promise<UploadResult | DryRunUploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n\n if (options.dryRun) {\n const plannedStatus = options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\");\n\n // Fetch current track state without modifying anything\n let currentReleases: DryRunUploadResult[\"currentReleases\"] = [];\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, options.track);\n currentReleases = (trackData.releases || []).map((r) => ({\n versionCodes: r.versionCodes || [],\n status: r.status,\n ...(r.userFraction !== undefined && { userFraction: r.userFraction }),\n }));\n } catch {\n // Track may not exist yet — that's fine for dry-run\n } finally {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n }\n\n return {\n dryRun: true,\n file: {\n path: filePath,\n valid: validation.valid,\n errors: validation.errors,\n warnings: validation.warnings,\n },\n track: options.track,\n currentReleases,\n plannedRelease: {\n status: plannedStatus,\n ...(options.userFraction !== undefined && { userFraction: options.userFraction }),\n },\n };\n }\n\n if (!validation.valid) {\n throw new Error(`File validation failed:\\n${validation.errors.join(\"\\n\")}`);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n // Upload the bundle\n const bundle = await client.bundles.upload(packageName, edit.id, filePath);\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n );\n }\n\n // Create release and assign to track\n const release: Release = {\n versionCodes: [String(bundle.versionCode)],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n // Validate and commit\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: { userFraction?: number; releaseNotes?: { language: string; text: string }[] },\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active release found on track \"${fromTrack}\"`);\n }\n\n // Create release on target track\n if (options?.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {\n throw new Error(\"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\");\n }\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.userFraction ? \"inProgress\" : \"completed\") as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active rollout found on track \"${track}\"`);\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction) throw new Error(\"--to <percentage> is required for rollout increase\");\n if (userFraction <= 0 || userFraction > 1) {\n throw new Error(\"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\");\n }\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","export const GOOGLE_PLAY_LANGUAGES: string[] = [\n \"af\",\n \"am\",\n \"ar\",\n \"hy-AM\",\n \"az-AZ\",\n \"eu-ES\",\n \"be\",\n \"bn-BD\",\n \"bg\",\n \"my-MM\",\n \"ca\",\n \"zh-HK\",\n \"zh-CN\",\n \"zh-TW\",\n \"hr\",\n \"cs-CZ\",\n \"da-DK\",\n \"nl-NL\",\n \"en-AU\",\n \"en-CA\",\n \"en-IN\",\n \"en-SG\",\n \"en-GB\",\n \"en-US\",\n \"et\",\n \"fil\",\n \"fi-FI\",\n \"fr-FR\",\n \"fr-CA\",\n \"gl-ES\",\n \"ka-GE\",\n \"de-DE\",\n \"el-GR\",\n \"gu\",\n \"iw-IL\",\n \"hi-IN\",\n \"hu-HU\",\n \"is-IS\",\n \"id\",\n \"it-IT\",\n \"ja-JP\",\n \"kn-IN\",\n \"kk\",\n \"km-KH\",\n \"ko-KR\",\n \"ky-KG\",\n \"lo-LA\",\n \"lv\",\n \"lt\",\n \"mk-MK\",\n \"ms\",\n \"ms-MY\",\n \"ml-IN\",\n \"mr-IN\",\n \"mn-MN\",\n \"ne-NP\",\n \"no-NO\",\n \"fa\",\n \"pl-PL\",\n \"pt-BR\",\n \"pt-PT\",\n \"pa\",\n \"ro\",\n \"rm\",\n \"ru-RU\",\n \"sr\",\n \"si-LK\",\n \"sk\",\n \"sl\",\n \"es-419\",\n \"es-ES\",\n \"es-US\",\n \"sw\",\n \"sv-SE\",\n \"ta-IN\",\n \"te-IN\",\n \"th\",\n \"tr-TR\",\n \"uk\",\n \"ur\",\n \"vi\",\n \"zu\",\n];\n\nexport function isValidBcp47(tag: string): boolean {\n return GOOGLE_PLAY_LANGUAGES.includes(tag);\n}\n","import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface ImageValidationResult {\n valid: boolean;\n warnings: string[];\n errors: string[];\n}\n\n// Google Play image size limits\nconst IMAGE_SIZE_LIMITS: Record<string, { maxBytes: number; label: string }> = {\n icon: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n featureGraphic: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n tvBanner: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n phoneScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n sevenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tvScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n wearScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n};\n\nconst VALID_EXTENSIONS = new Set([\".png\", \".jpg\", \".jpeg\"]);\nconst LARGE_IMAGE_THRESHOLD = 2 * 1024 * 1024; // 2 MB\n\nexport async function validateImage(\n filePath: string,\n imageType?: string,\n): Promise<ImageValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n if (!VALID_EXTENSIONS.has(ext)) {\n errors.push(`Unsupported image format \"${ext}\". Use PNG or JPEG.`);\n }\n\n // Check file exists and size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"Image file is empty (0 bytes)\");\n }\n } catch {\n errors.push(`Image file not found: ${filePath}`);\n return { valid: false, errors, warnings };\n }\n\n // Check size limits per image type\n if (imageType && sizeBytes > 0) {\n const limit = IMAGE_SIZE_LIMITS[imageType];\n if (limit && sizeBytes > limit.maxBytes) {\n errors.push(`Image exceeds ${limit.label} limit for ${imageType} (${formatSize(sizeBytes)})`);\n }\n }\n\n // Warn about large images\n if (sizeBytes > LARGE_IMAGE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large image (${formatSize(sizeBytes)}). Consider optimizing for faster upload and better store performance.`,\n );\n }\n\n // PNG optimization warning\n if (ext === \".png\" && sizeBytes > 512 * 1024) {\n warnings.push(\n \"PNG file is over 512 KB. Consider compressing with tools like pngquant or optipng.\",\n );\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import { readFile, writeFile, mkdir, readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Listing } from \"@gpc-cli/api\";\n\nconst FILE_MAP: Record<string, keyof Omit<Listing, \"language\">> = {\n \"title.txt\": \"title\",\n \"short_description.txt\": \"shortDescription\",\n \"full_description.txt\": \"fullDescription\",\n \"video.txt\": \"video\",\n};\n\nconst FIELD_TO_FILE: Record<string, string> = Object.fromEntries(\n Object.entries(FILE_MAP).map(([file, field]) => [field, file]),\n);\n\nexport interface ListingDiff {\n language: string;\n field: string;\n local: string;\n remote: string;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readListingsFromDir(dir: string): Promise<Listing[]> {\n const listings: Listing[] = [];\n\n if (!(await exists(dir))) return listings;\n\n const entries = await readdir(dir);\n // Validate directory names to prevent path traversal\n const SAFE_LANG = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;\n for (const lang of entries) {\n if (!SAFE_LANG.test(lang)) continue;\n const langDir = join(dir, lang);\n const langStat = await stat(langDir);\n if (!langStat.isDirectory()) continue;\n\n const listing: Listing = {\n language: lang,\n title: \"\",\n shortDescription: \"\",\n fullDescription: \"\",\n };\n\n for (const [fileName, field] of Object.entries(FILE_MAP)) {\n const filePath = join(langDir, fileName);\n if (await exists(filePath)) {\n const content = await readFile(filePath, \"utf-8\");\n (listing as unknown as Record<string, string>)[field] = content.trimEnd();\n }\n }\n\n listings.push(listing);\n }\n\n return listings;\n}\n\nexport async function writeListingsToDir(dir: string, listings: Listing[]): Promise<void> {\n for (const listing of listings) {\n const langDir = join(dir, listing.language);\n await mkdir(langDir, { recursive: true });\n\n for (const [field, fileName] of Object.entries(FIELD_TO_FILE)) {\n const value = (listing as unknown as Record<string, string>)[field];\n if (value !== undefined && value !== \"\") {\n await writeFile(join(langDir, fileName), value + \"\\n\", \"utf-8\");\n }\n }\n }\n}\n\nexport function diffListings(local: Listing[], remote: Listing[]): ListingDiff[] {\n const diffs: ListingDiff[] = [];\n const remoteMap = new Map(remote.map((l) => [l.language, l]));\n const localMap = new Map(local.map((l) => [l.language, l]));\n\n // Check all local listings against remote\n for (const localListing of local) {\n const remoteListing = remoteMap.get(localListing.language);\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const localVal = (\n (localListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n const remoteVal = remoteListing\n ? ((remoteListing as unknown as Record<string, string>)[field] ?? \"\").toString()\n : \"\";\n if (localVal !== remoteVal) {\n diffs.push({\n language: localListing.language,\n field,\n local: localVal,\n remote: remoteVal,\n });\n }\n }\n }\n\n // Check for remote-only languages\n for (const remoteListing of remote) {\n if (!localMap.has(remoteListing.language)) {\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const remoteVal = (\n (remoteListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n if (remoteVal) {\n diffs.push({\n language: remoteListing.language,\n field,\n local: \"\",\n remote: remoteVal,\n });\n }\n }\n }\n }\n\n return diffs;\n}\n","import type {\n PlayApiClient,\n Listing,\n Image,\n ImageType,\n AppDetails,\n CountryAvailability,\n} from \"@gpc-cli/api\";\nimport { isValidBcp47 } from \"../utils/bcp47.js\";\nimport { validateImage } from \"../utils/image-validation.js\";\nimport { readListingsFromDir, writeListingsToDir, diffListings } from \"../utils/fastlane.js\";\nimport type { ListingDiff } from \"../utils/fastlane.js\";\n\nexport interface ListingsResult {\n listings: Listing[];\n}\n\nexport interface PushResult {\n updated: number;\n languages: string[];\n}\n\nexport interface DryRunResult {\n diffs: ListingDiff[];\n}\n\nfunction validateLanguage(lang: string): void {\n if (!isValidBcp47(lang)) {\n throw new Error(`Invalid language tag \"${lang}\". Must be a valid Google Play BCP 47 code.`);\n }\n}\n\nexport async function getListings(\n client: PlayApiClient,\n packageName: string,\n language?: string,\n): Promise<Listing[]> {\n const edit = await client.edits.insert(packageName);\n try {\n let listings: Listing[];\n if (language) {\n validateLanguage(language);\n const listing = await client.listings.get(packageName, edit.id, language);\n listings = [listing];\n } else {\n listings = await client.listings.list(packageName, edit.id);\n }\n await client.edits.delete(packageName, edit.id);\n return listings;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n data: Partial<Omit<Listing, \"language\">>,\n): Promise<Listing> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const listing = await client.listings.patch(packageName, edit.id, language, data);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return listing;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.listings.delete(packageName, edit.id, language);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pullListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n): Promise<ListingsResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const listings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n await writeListingsToDir(dir, listings);\n return { listings };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pushListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<PushResult | DryRunResult> {\n const localListings = await readListingsFromDir(dir);\n\n if (localListings.length === 0) {\n throw new Error(`No listings found in directory \"${dir}\"`);\n }\n\n // Validate all languages\n for (const listing of localListings) {\n validateLanguage(listing.language);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n if (options?.dryRun) {\n const remoteListings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n const diffs = diffListings(localListings, remoteListings);\n return { diffs };\n }\n\n for (const listing of localListings) {\n const { language, ...data } = listing;\n await client.listings.update(packageName, edit.id, language, data);\n }\n\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n updated: localListings.length,\n languages: localListings.map((l) => l.language),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listImages(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n): Promise<Image[]> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const images = await client.images.list(packageName, edit.id, language, imageType);\n await client.edits.delete(packageName, edit.id);\n return images;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n): Promise<Image> {\n validateLanguage(language);\n\n // Validate image before upload\n const imageCheck = await validateImage(filePath, imageType);\n if (!imageCheck.valid) {\n throw new Error(`Image validation failed: ${imageCheck.errors.join(\"; \")}`);\n }\n if (imageCheck.warnings.length > 0) {\n for (const w of imageCheck.warnings) {\n console.warn(`Warning: ${w}`);\n }\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return image;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.images.delete(packageName, edit.id, language, imageType, imageId);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getCountryAvailability(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<CountryAvailability> {\n const edit = await client.edits.insert(packageName);\n try {\n const availability = await client.countryAvailability.get(packageName, edit.id, track);\n await client.edits.delete(packageName, edit.id);\n return availability;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateAppDetails(\n client: PlayApiClient,\n packageName: string,\n details: Partial<AppDetails>,\n): Promise<AppDetails> {\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.details.patch(packageName, edit.id, details);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { extname, basename, join } from \"node:path\";\n\nexport interface ReleaseNote {\n language: string;\n text: string;\n}\n\nexport interface ReleaseNotesValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nconst MAX_NOTES_LENGTH = 500;\n\nexport async function readReleaseNotesFromDir(dir: string): Promise<ReleaseNote[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new Error(`Release notes directory not found: ${dir}`);\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n if (extname(entry) !== \".txt\") continue;\n\n const language = basename(entry, \".txt\");\n const filePath = join(dir, entry);\n\n const stats = await stat(filePath);\n if (!stats.isFile()) continue;\n\n const text = (await readFile(filePath, \"utf-8\")).trim();\n if (text.length === 0) continue;\n\n notes.push({ language, text });\n }\n\n return notes;\n}\n\nexport function validateReleaseNotes(notes: ReleaseNote[]): ReleaseNotesValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const seen = new Set<string>();\n for (const note of notes) {\n if (seen.has(note.language)) {\n errors.push(`Duplicate language code: ${note.language}`);\n }\n seen.add(note.language);\n\n if (note.text.length > MAX_NOTES_LENGTH) {\n errors.push(\n `Release notes for \"${note.language}\" exceed ${MAX_NOTES_LENGTH} chars (${note.text.length} chars)`,\n );\n }\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n","import { stat } from \"node:fs/promises\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { readReleaseNotesFromDir, validateReleaseNotes } from \"../utils/release-notes.js\";\n\nexport interface ValidateOptions {\n filePath: string;\n mappingFile?: string;\n track?: string;\n notes?: { language: string; text: string }[];\n notesDir?: string;\n}\n\nexport interface ValidateCheck {\n name: string;\n passed: boolean;\n message: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n checks: ValidateCheck[];\n}\n\nconst STANDARD_TRACKS = new Set([\n \"internal\",\n \"alpha\",\n \"beta\",\n \"production\",\n // Form factor tracks\n \"wear:internal\",\n \"wear:alpha\",\n \"wear:beta\",\n \"wear:production\",\n \"automotive:internal\",\n \"automotive:alpha\",\n \"automotive:beta\",\n \"automotive:production\",\n \"tv:internal\",\n \"tv:alpha\",\n \"tv:beta\",\n \"tv:production\",\n \"android_xr:internal\",\n \"android_xr:alpha\",\n \"android_xr:beta\",\n \"android_xr:production\",\n]);\nconst TRACK_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_:-]*$/;\n\nexport async function validatePreSubmission(options: ValidateOptions): Promise<ValidateResult> {\n const checks: ValidateCheck[] = [];\n\n // 1. File validation\n const fileResult = await validateUploadFile(options.filePath);\n checks.push({\n name: \"file\",\n passed: fileResult.valid,\n message: fileResult.valid\n ? `Valid ${fileResult.fileType} file (${formatSize(fileResult.sizeBytes)})`\n : fileResult.errors.join(\"; \"),\n });\n\n // 2. Mapping file\n if (options.mappingFile) {\n try {\n const stats = await stat(options.mappingFile);\n checks.push({\n name: \"mapping\",\n passed: stats.isFile(),\n message: stats.isFile()\n ? `Mapping file found (${formatSize(stats.size)})`\n : \"Mapping path is not a file\",\n });\n } catch {\n checks.push({\n name: \"mapping\",\n passed: false,\n message: `Mapping file not found: ${options.mappingFile}`,\n });\n }\n }\n\n // 3. Track validation\n if (options.track) {\n const isValid = STANDARD_TRACKS.has(options.track) || TRACK_PATTERN.test(options.track);\n checks.push({\n name: \"track\",\n passed: isValid,\n message: isValid\n ? `Track \"${options.track}\" is valid`\n : `Invalid track name \"${options.track}\". Use: internal, alpha, beta, production, or a custom track ID`,\n });\n }\n\n // 4. Release notes validation\n let resolvedNotes = options.notes;\n if (options.notesDir) {\n try {\n resolvedNotes = await readReleaseNotesFromDir(options.notesDir);\n checks.push({\n name: \"notes-dir\",\n passed: true,\n message: `Read release notes for ${resolvedNotes.length} language(s)`,\n });\n } catch (err) {\n checks.push({\n name: \"notes-dir\",\n passed: false,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n if (resolvedNotes && resolvedNotes.length > 0) {\n const notesResult = validateReleaseNotes(resolvedNotes);\n checks.push({\n name: \"notes\",\n passed: notesResult.valid,\n message: notesResult.valid\n ? `Release notes valid (${resolvedNotes.length} language(s))`\n : notesResult.errors.join(\"; \"),\n });\n }\n\n return {\n valid: checks.every((c) => c.passed),\n checks,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\nimport { uploadRelease } from \"./releases.js\";\nimport type { UploadResult, DryRunUploadResult } from \"./releases.js\";\nimport { validatePreSubmission } from \"./validate.js\";\nimport type { ValidateResult } from \"./validate.js\";\nimport { readReleaseNotesFromDir } from \"../utils/release-notes.js\";\n\nexport interface PublishOptions {\n track?: string;\n rolloutPercent?: number;\n notes?: string;\n notesDir?: string;\n releaseName?: string;\n mappingFile?: string;\n dryRun?: boolean;\n}\n\nexport interface PublishResult {\n validation: ValidateResult;\n upload?: UploadResult;\n}\n\nexport interface DryRunPublishResult {\n dryRun: true;\n validation: ValidateResult;\n upload: DryRunUploadResult;\n}\n\nexport async function publish(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: PublishOptions,\n): Promise<PublishResult | DryRunPublishResult> {\n // Resolve release notes\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.notesDir) {\n releaseNotes = await readReleaseNotesFromDir(options.notesDir);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n // Validate\n const validation = await validatePreSubmission({\n filePath,\n mappingFile: options.mappingFile,\n track: options.track || \"internal\",\n notes: releaseNotes,\n });\n\n if (options.dryRun) {\n const upload = await uploadRelease(client, packageName, filePath, {\n track: options.track || \"internal\",\n userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : undefined,\n dryRun: true,\n }) as DryRunUploadResult;\n\n return { dryRun: true, validation, upload };\n }\n\n if (!validation.valid) {\n return { validation };\n }\n\n // Upload\n const upload = await uploadRelease(client, packageName, filePath, {\n track: options.track || \"internal\",\n userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : undefined,\n releaseNotes,\n releaseName: options.releaseName,\n mappingFile: options.mappingFile,\n });\n\n return { validation, upload } as PublishResult;\n}\n","import type { PlayApiClient, Review, ReviewsListOptions, ReviewReplyResponse } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ReviewsFilterOptions {\n stars?: number;\n language?: string;\n since?: string;\n translationLanguage?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport interface ReviewExportOptions extends ReviewsFilterOptions {\n format?: \"json\" | \"csv\";\n}\n\nexport async function listReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewsFilterOptions,\n): Promise<Review[]> {\n const apiOptions: ReviewsListOptions = {};\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n if (options?.maxResults) apiOptions.maxResults = options.maxResults;\n\n const response = await client.reviews.list(packageName, apiOptions);\n let reviews = response.reviews || [];\n\n // Client-side filters\n if (options?.stars !== undefined) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && userComment.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && Number(userComment.lastModified.seconds) >= sinceTime;\n });\n }\n\n return reviews;\n}\n\nexport async function getReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n translationLanguage?: string,\n): Promise<Review> {\n return client.reviews.get(packageName, reviewId, translationLanguage);\n}\n\nconst MAX_REPLY_LENGTH = 350;\n\nexport async function replyToReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n replyText: string,\n): Promise<ReviewReplyResponse> {\n if (replyText.length > MAX_REPLY_LENGTH) {\n throw new Error(\n `Reply text exceeds ${MAX_REPLY_LENGTH} characters (${replyText.length}). Google Play limits replies to ${MAX_REPLY_LENGTH} characters.`,\n );\n }\n if (replyText.length === 0) {\n throw new Error(\"Reply text cannot be empty.\");\n }\n return client.reviews.reply(packageName, reviewId, replyText);\n}\n\nexport async function exportReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewExportOptions,\n): Promise<string> {\n const { items: allReviews } = await paginateAll<Review>(async (pageToken) => {\n const apiOptions: ReviewsListOptions = { token: pageToken };\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n const response = await client.reviews.list(packageName, apiOptions);\n return {\n items: response.reviews || [],\n nextPageToken: response.tokenPagination?.nextPageToken,\n };\n });\n\n let filtered = allReviews;\n\n if (options?.stars !== undefined) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && uc.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && Number(uc.lastModified.seconds) >= sinceTime;\n });\n }\n\n if (options?.format === \"csv\") {\n return reviewsToCsv(filtered);\n }\n\n return JSON.stringify(filtered, null, 2);\n}\n\nfunction reviewsToCsv(reviews: Review[]): string {\n const header = \"reviewId,authorName,starRating,text,language,date,device,appVersionName\";\n const rows = reviews.map((r) => {\n const uc = r.comments?.[0]?.userComment;\n const fields = [\n r.reviewId,\n csvEscape(r.authorName),\n uc?.starRating ?? \"\",\n csvEscape(uc?.text ?? \"\"),\n uc?.reviewerLanguage ?? \"\",\n uc ? new Date(Number(uc.lastModified.seconds) * 1000).toISOString() : \"\",\n csvEscape(uc?.device ?? \"\"),\n csvEscape(uc?.appVersionName ?? \"\"),\n ];\n return fields.join(\",\");\n });\n return [header, ...rows].join(\"\\n\");\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(\",\") || value.includes('\"') || value.includes(\"\\n\")) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","import type {\n ReportingApiClient,\n VitalsMetricSet,\n MetricSetQuery,\n MetricSetResponse,\n MetricRow,\n AnomalyDetectionResponse,\n ErrorIssuesResponse,\n ReportingDimension,\n ReportingAggregation,\n} from \"@gpc-cli/api\";\n\nexport interface VitalsQueryOptions {\n dimension?: ReportingDimension;\n days?: number;\n aggregation?: ReportingAggregation;\n}\n\nexport interface VitalsOverview {\n crashRate?: MetricRow[];\n anrRate?: MetricRow[];\n slowStartRate?: MetricRow[];\n slowRenderingRate?: MetricRow[];\n excessiveWakeupRate?: MetricRow[];\n stuckWakelockRate?: MetricRow[];\n}\n\nexport interface ThresholdResult {\n breached: boolean;\n value: number | undefined;\n threshold: number;\n}\n\nfunction buildQuery(options?: VitalsQueryOptions): MetricSetQuery {\n const query: MetricSetQuery = {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n };\n\n if (options?.dimension) {\n query.dimensions = [options.dimension];\n }\n\n if (options?.days) {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - options.days);\n query.timelineSpec = {\n aggregationPeriod: options.aggregation ?? \"DAILY\",\n startTime: {\n year: start.getFullYear(),\n month: start.getMonth() + 1,\n day: start.getDate(),\n },\n endTime: {\n year: end.getFullYear(),\n month: end.getMonth() + 1,\n day: end.getDate(),\n },\n };\n }\n\n return query;\n}\n\nasync function queryMetric(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n const query = buildQuery(options);\n return reporting.queryMetricSet(packageName, metricSet, query);\n}\n\nexport async function getVitalsOverview(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<VitalsOverview> {\n const metricSets: [VitalsMetricSet, keyof VitalsOverview][] = [\n [\"vitals.crashrate\", \"crashRate\"],\n [\"vitals.anrrate\", \"anrRate\"],\n [\"vitals.slowstartrate\", \"slowStartRate\"],\n [\"vitals.slowrenderingrate\", \"slowRenderingRate\"],\n [\"vitals.excessivewakeuprate\", \"excessiveWakeupRate\"],\n [\"vitals.stuckbackgroundwakelockrate\", \"stuckWakelockRate\"],\n ];\n\n const results = await Promise.allSettled(\n metricSets.map(([metric]) =>\n reporting.queryMetricSet(packageName, metric, {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n }),\n ),\n );\n\n const overview: VitalsOverview = {};\n for (let i = 0; i < metricSets.length; i++) {\n const entry = metricSets[i];\n if (!entry) continue;\n const key = entry[1];\n const result = results[i];\n if (!result) continue;\n if (result.status === \"fulfilled\") {\n overview[key] = result.value.rows || [];\n }\n }\n\n return overview;\n}\n\nexport async function getVitalsCrashes(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.crashrate\", options);\n}\n\nexport async function getVitalsAnr(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.anrrate\", options);\n}\n\nexport async function getVitalsStartup(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowstartrate\", options);\n}\n\nexport async function getVitalsRendering(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowrenderingrate\", options);\n}\n\nexport async function getVitalsBattery(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.excessivewakeuprate\", options);\n}\n\nexport async function getVitalsMemory(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.stuckbackgroundwakelockrate\", options);\n}\n\nexport async function getVitalsAnomalies(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<AnomalyDetectionResponse> {\n return reporting.getAnomalies(packageName);\n}\n\nexport async function searchVitalsErrors(\n reporting: ReportingApiClient,\n packageName: string,\n options?: { filter?: string; maxResults?: number },\n): Promise<ErrorIssuesResponse> {\n return reporting.searchErrorIssues(packageName, options?.filter, options?.maxResults);\n}\n\nexport interface VitalsTrendComparison {\n metric: string;\n current: number | undefined;\n previous: number | undefined;\n changePercent: number | undefined;\n direction: \"improved\" | \"degraded\" | \"unchanged\" | \"unknown\";\n}\n\nexport async function compareVitalsTrend(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n days: number = 7,\n): Promise<VitalsTrendComparison> {\n const now = new Date();\n\n // Current period\n const currentEnd = new Date(now);\n const currentStart = new Date(now);\n currentStart.setDate(currentStart.getDate() - days);\n\n // Previous period\n const previousEnd = new Date(currentStart);\n const previousStart = new Date(previousEnd);\n previousStart.setDate(previousStart.getDate() - days);\n\n const makeQuery = (start: Date, end: Date): MetricSetQuery => ({\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n timelineSpec: {\n aggregationPeriod: \"DAILY\",\n startTime: { year: start.getFullYear(), month: start.getMonth() + 1, day: start.getDate() },\n endTime: { year: end.getFullYear(), month: end.getMonth() + 1, day: end.getDate() },\n },\n });\n\n const [currentResult, previousResult] = await Promise.all([\n reporting.queryMetricSet(packageName, metricSet, makeQuery(currentStart, currentEnd)),\n reporting.queryMetricSet(packageName, metricSet, makeQuery(previousStart, previousEnd)),\n ]);\n\n const extractAvg = (rows: MetricRow[] | undefined): number | undefined => {\n if (!rows || rows.length === 0) return undefined;\n const values = rows\n .map((r) => {\n const keys = Object.keys(r.metrics);\n const first = keys[0];\n return first ? Number(r.metrics[first]?.decimalValue?.value) : NaN;\n })\n .filter((v) => !isNaN(v));\n if (values.length === 0) return undefined;\n return values.reduce((a, b) => a + b, 0) / values.length;\n };\n\n const current = extractAvg(currentResult.rows);\n const previous = extractAvg(previousResult.rows);\n\n let changePercent: number | undefined;\n let direction: VitalsTrendComparison[\"direction\"] = \"unknown\";\n\n if (current !== undefined && previous !== undefined && previous !== 0) {\n changePercent = ((current - previous) / previous) * 100;\n if (Math.abs(changePercent) < 1) {\n direction = \"unchanged\";\n } else if (changePercent < 0) {\n direction = \"improved\"; // lower error rate = better\n } else {\n direction = \"degraded\";\n }\n }\n\n return {\n metric: metricSet,\n current,\n previous,\n changePercent: changePercent !== undefined ? Math.round(changePercent * 10) / 10 : undefined,\n direction,\n };\n}\n\nexport function checkThreshold(value: number | undefined, threshold: number): ThresholdResult {\n return {\n breached: value !== undefined && value > threshold,\n value,\n threshold,\n };\n}\n","import type {\n PlayApiClient,\n Subscription,\n BasePlanMigratePricesRequest,\n SubscriptionOffer,\n OffersListResponse,\n} from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListSubscriptionsOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listSubscriptions(\n client: PlayApiClient,\n packageName: string,\n options?: ListSubscriptionsOptions,\n): Promise<{ subscriptions: Subscription[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<Subscription>(\n async (pageToken) => {\n const resp = await client.subscriptions.list(packageName, {\n pageToken,\n pageSize: options?.pageSize,\n });\n return { items: resp.subscriptions || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { subscriptions: result.items, nextPageToken: result.nextPageToken };\n }\n return client.subscriptions.list(packageName, {\n pageToken: options?.pageToken,\n pageSize: options?.pageSize,\n });\n}\n\nexport async function getSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<Subscription> {\n return client.subscriptions.get(packageName, productId);\n}\n\nexport async function createSubscription(\n client: PlayApiClient,\n packageName: string,\n data: Subscription,\n): Promise<Subscription> {\n return client.subscriptions.create(packageName, data);\n}\n\nexport async function updateSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n): Promise<Subscription> {\n return client.subscriptions.update(packageName, productId, data, updateMask);\n}\n\nexport async function deleteSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<void> {\n return client.subscriptions.delete(packageName, productId);\n}\n\nexport async function activateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.activateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deactivateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.deactivateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deleteBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<void> {\n return client.subscriptions.deleteBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function migratePrices(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: BasePlanMigratePricesRequest,\n): Promise<Subscription> {\n return client.subscriptions.migratePrices(packageName, productId, basePlanId, data);\n}\n\nexport async function listOffers(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<OffersListResponse> {\n return client.subscriptions.listOffers(packageName, productId, basePlanId);\n}\n\nexport async function getOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.getOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function createOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.createOffer(packageName, productId, basePlanId, data);\n}\n\nexport async function updateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.updateOffer(\n packageName,\n productId,\n basePlanId,\n offerId,\n data,\n updateMask,\n );\n}\n\nexport async function deleteOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<void> {\n return client.subscriptions.deleteOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function activateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function deactivateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.deactivateOffer(packageName, productId, basePlanId, offerId);\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { PlayApiClient, InAppProduct } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListIapOptions {\n token?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listInAppProducts(\n client: PlayApiClient,\n packageName: string,\n options?: ListIapOptions,\n): Promise<{ inappproduct: InAppProduct[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<InAppProduct>(\n async (pageToken) => {\n const resp = await client.inappproducts.list(packageName, {\n token: pageToken,\n maxResults: options?.maxResults,\n });\n return {\n items: resp.inappproduct || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { inappproduct: result.items, nextPageToken: result.nextPageToken };\n }\n return client.inappproducts.list(packageName, {\n token: options?.token,\n maxResults: options?.maxResults,\n });\n}\n\nexport async function getInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<InAppProduct> {\n return client.inappproducts.get(packageName, sku);\n}\n\nexport async function createInAppProduct(\n client: PlayApiClient,\n packageName: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.create(packageName, data);\n}\n\nexport async function updateInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.update(packageName, sku, data);\n}\n\nexport async function deleteInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<void> {\n return client.inappproducts.delete(packageName, sku);\n}\n\nexport interface SyncResult {\n created: number;\n updated: number;\n unchanged: number;\n skus: string[];\n}\n\nexport async function syncInAppProducts(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<SyncResult> {\n const files = await readdir(dir);\n const jsonFiles = files.filter((f) => f.endsWith(\".json\"));\n\n if (jsonFiles.length === 0) {\n return { created: 0, updated: 0, unchanged: 0, skus: [] };\n }\n\n const localProducts: InAppProduct[] = [];\n for (const file of jsonFiles) {\n const content = await readFile(join(dir, file), \"utf-8\");\n try {\n localProducts.push(JSON.parse(content) as InAppProduct);\n } catch {\n throw new Error(`Failed to parse ${file}: invalid JSON`);\n }\n }\n\n const response = await client.inappproducts.list(packageName);\n const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));\n\n let created = 0;\n let updated = 0;\n const unchanged = 0;\n const skus: string[] = [];\n\n for (const product of localProducts) {\n skus.push(product.sku);\n if (remoteSkus.has(product.sku)) {\n if (!options?.dryRun) {\n await client.inappproducts.update(packageName, product.sku, product);\n }\n updated++;\n } else {\n if (!options?.dryRun) {\n await client.inappproducts.create(packageName, product);\n }\n created++;\n }\n }\n\n return { created, updated, unchanged, skus };\n}\n","import type {\n PlayApiClient,\n ProductPurchase,\n SubscriptionPurchaseV2,\n SubscriptionDeferResponse,\n} from \"@gpc-cli/api\";\n\nexport async function getProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<ProductPurchase> {\n return client.purchases.getProduct(packageName, productId, token);\n}\n\nexport async function acknowledgeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n payload?: string,\n): Promise<void> {\n const body = payload ? { developerPayload: payload } : undefined;\n return client.purchases.acknowledgeProduct(packageName, productId, token, body);\n}\n\nexport async function consumeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<void> {\n return client.purchases.consumeProduct(packageName, productId, token);\n}\n\nexport async function getSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<SubscriptionPurchaseV2> {\n return client.purchases.getSubscriptionV2(packageName, token);\n}\n\nexport async function cancelSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n): Promise<void> {\n return client.purchases.cancelSubscription(packageName, subscriptionId, token);\n}\n\nexport async function deferSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n desiredExpiry: string,\n): Promise<SubscriptionDeferResponse> {\n const sub = await client.purchases.getSubscriptionV1(packageName, subscriptionId, token);\n return client.purchases.deferSubscription(packageName, subscriptionId, token, {\n deferralInfo: {\n expectedExpiryTimeMillis: sub.expiryTimeMillis,\n desiredExpiryTimeMillis: String(new Date(desiredExpiry).getTime()),\n },\n });\n}\n\nexport async function revokeSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<void> {\n return client.purchases.revokeSubscriptionV2(packageName, token);\n}\n\nimport type { VoidedPurchase } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListVoidedOptions {\n startTime?: string;\n endTime?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listVoidedPurchases(\n client: PlayApiClient,\n packageName: string,\n options?: ListVoidedOptions,\n): Promise<{ voidedPurchases: VoidedPurchase[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<VoidedPurchase>(\n async (pageToken) => {\n const resp = await client.purchases.listVoided(packageName, {\n startTime: options?.startTime,\n endTime: options?.endTime,\n maxResults: options?.maxResults,\n token: pageToken,\n });\n return {\n items: resp.voidedPurchases || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { voidedPurchases: result.items, nextPageToken: result.nextPageToken };\n }\n return client.purchases.listVoided(packageName, options);\n}\n\nexport async function refundOrder(\n client: PlayApiClient,\n packageName: string,\n orderId: string,\n options?: { fullRefund?: boolean; proratedRefund?: boolean },\n): Promise<void> {\n return client.orders.refund(packageName, orderId, options);\n}\n","import type { PlayApiClient, ConvertRegionPricesResponse } from \"@gpc-cli/api\";\n\nexport async function convertRegionPrices(\n client: PlayApiClient,\n packageName: string,\n currencyCode: string,\n amount: string,\n): Promise<ConvertRegionPricesResponse> {\n const units = amount.split(\".\")[0] || \"0\";\n const fractional = amount.split(\".\")[1] || \"0\";\n const nanos = Number(fractional.padEnd(9, \"0\").slice(0, 9));\n\n return client.monetization.convertRegionPrices(packageName, {\n price: {\n currencyCode,\n units,\n nanos,\n },\n });\n}\n","import type { PlayApiClient, ReportBucket, ReportType, StatsDimension } from \"@gpc-cli/api\";\n\nconst FINANCIAL_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"earnings\",\n \"sales\",\n \"estimated_sales\",\n \"play_balance\",\n]);\n\nconst STATS_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"installs\",\n \"crashes\",\n \"ratings\",\n \"reviews\",\n \"store_performance\",\n \"subscriptions\",\n]);\n\nconst VALID_DIMENSIONS: ReadonlySet<string> = new Set([\n \"country\",\n \"language\",\n \"os_version\",\n \"device\",\n \"app_version\",\n \"carrier\",\n \"overview\",\n]);\n\nexport function isFinancialReportType(type: string): boolean {\n return FINANCIAL_REPORT_TYPES.has(type);\n}\n\nexport function isStatsReportType(type: string): boolean {\n return STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidReportType(type: string): type is ReportType {\n return FINANCIAL_REPORT_TYPES.has(type) || STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidStatsDimension(dim: string): dim is StatsDimension {\n return VALID_DIMENSIONS.has(dim);\n}\n\nexport interface ParsedMonth {\n year: number;\n month: number;\n}\n\nexport function parseMonth(monthStr: string): ParsedMonth {\n const match = /^(\\d{4})-(\\d{2})$/.exec(monthStr);\n if (!match) {\n throw new Error(`Invalid month format \"${monthStr}\". Expected YYYY-MM (e.g., 2026-03).`);\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n if (month < 1 || month > 12) {\n throw new Error(`Invalid month \"${month}\". Must be between 01 and 12.`);\n }\n return { year, month };\n}\n\nexport async function listReports(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<ReportBucket[]> {\n const response = await client.reports.list(packageName, reportType, year, month);\n return response.reports || [];\n}\n\nexport async function downloadReport(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<string> {\n const reports = await listReports(client, packageName, reportType, year, month);\n\n if (reports.length === 0) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n\n // Download the first report bucket (signed URI — no auth needed)\n const bucket = reports[0];\n if (!bucket) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n const uri = bucket.uri;\n const response = await fetch(uri);\n\n if (!response.ok) {\n throw new Error(`Failed to download report from signed URI: HTTP ${response.status}`);\n }\n\n return response.text();\n}\n","import type { UsersApiClient, User, DeveloperPermission, Grant } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport const PERMISSION_PROPAGATION_WARNING =\n \"Note: Permission changes may take up to 48 hours to propagate.\";\n\nexport interface ListUsersOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listUsers(\n client: UsersApiClient,\n developerId: string,\n options?: ListUsersOptions,\n): Promise<{ users: User[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<User>(\n async (pageToken) => {\n const resp = await client.list(developerId, { pageToken, pageSize: options?.pageSize });\n return { items: resp.users || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { users: result.items, nextPageToken: result.nextPageToken };\n }\n const response = await client.list(developerId, options);\n return { users: response.users || [], nextPageToken: response.nextPageToken };\n}\n\nexport async function getUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<User> {\n return client.get(developerId, userId);\n}\n\nexport async function inviteUser(\n client: UsersApiClient,\n developerId: string,\n email: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const user: Partial<User> = { email };\n if (permissions) user.developerAccountPermission = permissions;\n if (grants) user.grants = grants;\n return client.create(developerId, user);\n}\n\nexport async function updateUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const updates: Partial<User> = {};\n const masks: string[] = [];\n\n if (permissions) {\n updates.developerAccountPermission = permissions;\n masks.push(\"developerAccountPermission\");\n }\n if (grants) {\n updates.grants = grants;\n masks.push(\"grants\");\n }\n\n const updateMask = masks.length > 0 ? masks.join(\",\") : undefined;\n return client.update(developerId, userId, updates, updateMask);\n}\n\nexport async function removeUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<void> {\n return client.delete(developerId, userId);\n}\n\nexport function parseGrantArg(grantStr: string): Grant {\n const colonIdx = grantStr.indexOf(\":\");\n if (colonIdx === -1) {\n throw new Error(\n `Invalid grant format \"${grantStr}\". Expected <packageName>:<PERMISSION>[,<PERMISSION>...]`,\n );\n }\n const packageName = grantStr.slice(0, colonIdx);\n const perms = grantStr.slice(colonIdx + 1).split(\",\") as DeveloperPermission[];\n return { packageName, appLevelPermissions: perms };\n}\n","import type { PlayApiClient, Testers } from \"@gpc-cli/api\";\nimport { readFile } from \"node:fs/promises\";\n\nexport async function listTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const testers = await client.testers.get(packageName, edit.id, track);\n return testers;\n } finally {\n await client.edits.delete(packageName, edit.id);\n }\n}\n\nexport async function addTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const existing = new Set(current.googleGroups || []);\n for (const email of groupEmails) {\n existing.add(email.trim());\n }\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: [...existing],\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function removeTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const toRemove = new Set(groupEmails.map((e) => e.trim()));\n const filtered = (current.googleGroups || []).filter((g) => !toRemove.has(g));\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: filtered,\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function importTestersFromCsv(\n client: PlayApiClient,\n packageName: string,\n track: string,\n csvPath: string,\n): Promise<{ added: number; testers: Testers }> {\n const content = await readFile(csvPath, \"utf-8\");\n const emails = content\n .split(/[,\\n\\r]+/)\n .map((e) => e.trim())\n .filter((e) => e.length > 0 && e.includes(\"@\"));\n\n if (emails.length === 0) {\n throw new Error(`No valid email addresses found in ${csvPath}.`);\n }\n\n const testers = await addTesters(client, packageName, track, emails);\n return { added: emails.length, testers };\n}\n","import { resolve, normalize } from \"node:path\";\n\n/**\n * Normalize and resolve a user-supplied path.\n * Prevents path traversal by normalizing `.` and `..` components.\n */\nexport function safePath(userPath: string): string {\n return resolve(normalize(userPath));\n}\n\n/**\n * Validate that a resolved path is within an expected base directory.\n * Returns the resolved path or throws if it escapes the base.\n */\nexport function safePathWithin(userPath: string, baseDir: string): string {\n const resolved = safePath(userPath);\n const base = safePath(baseDir);\n\n if (!resolved.startsWith(base + \"/\") && resolved !== base) {\n throw new Error(`Path \"${userPath}\" resolves outside the expected directory \"${baseDir}\"`);\n }\n\n return resolved;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface ScaffoldOptions {\n name: string;\n dir: string;\n description?: string;\n}\n\nexport interface ScaffoldResult {\n dir: string;\n files: string[];\n}\n\n/**\n * Scaffold a new GPC plugin project.\n */\nexport async function scaffoldPlugin(options: ScaffoldOptions): Promise<ScaffoldResult> {\n const { name, dir, description = `GPC plugin: ${name}` } = options;\n\n // Ensure name follows convention\n const pluginName = name.startsWith(\"gpc-plugin-\") ? name : `gpc-plugin-${name}`;\n const shortName = pluginName.replace(/^gpc-plugin-/, \"\");\n\n const srcDir = join(dir, \"src\");\n const testDir = join(dir, \"tests\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n const files: string[] = [];\n\n // package.json\n const pkg = {\n name: pluginName,\n version: \"0.1.0\",\n description,\n type: \"module\",\n main: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n exports: {\n \".\": {\n import: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n },\n },\n files: [\"dist\"],\n scripts: {\n build: \"tsup src/index.ts --format esm --dts\",\n dev: \"tsup src/index.ts --format esm --dts --watch\",\n test: \"vitest run\",\n \"test:watch\": \"vitest\",\n },\n keywords: [\"gpc\", \"gpc-plugin\", \"google-play\"],\n license: \"MIT\",\n peerDependencies: {\n \"@gpc-cli/plugin-sdk\": \">=0.8.0\",\n },\n devDependencies: {\n \"@gpc-cli/plugin-sdk\": \"^0.8.0\",\n tsup: \"^8.0.0\",\n typescript: \"^5.0.0\",\n vitest: \"^3.0.0\",\n },\n };\n await writeFile(join(dir, \"package.json\"), JSON.stringify(pkg, null, 2) + \"\\n\");\n files.push(\"package.json\");\n\n // tsconfig.json\n const tsconfig = {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n declaration: true,\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"./dist\",\n rootDir: \"./src\",\n },\n include: [\"src\"],\n };\n await writeFile(join(dir, \"tsconfig.json\"), JSON.stringify(tsconfig, null, 2) + \"\\n\");\n files.push(\"tsconfig.json\");\n\n // src/index.ts\n const srcContent = `import { definePlugin } from \"@gpc-cli/plugin-sdk\";\nimport type { CommandEvent, CommandResult } from \"@gpc-cli/plugin-sdk\";\n\nexport const plugin = definePlugin({\n name: \"${pluginName}\",\n version: \"0.1.0\",\n\n register(hooks) {\n hooks.beforeCommand(async (event: CommandEvent) => {\n // Called before every gpc command\n // Example: log command usage, validate prerequisites, etc.\n });\n\n hooks.afterCommand(async (event: CommandEvent, result: CommandResult) => {\n // Called after every successful gpc command\n // Example: send notifications, update dashboards, etc.\n });\n\n // Uncomment to add custom commands:\n // hooks.registerCommands((registry) => {\n // registry.add({\n // name: \"${shortName}\",\n // description: \"${description}\",\n // action: async (args, opts) => {\n // console.log(\"Hello from ${pluginName}!\");\n // },\n // });\n // });\n },\n});\n`;\n await writeFile(join(srcDir, \"index.ts\"), srcContent);\n files.push(\"src/index.ts\");\n\n // tests/plugin.test.ts\n const testContent = `import { describe, it, expect, vi } from \"vitest\";\nimport { plugin } from \"../src/index\";\n\ndescribe(\"${pluginName}\", () => {\n it(\"has correct name and version\", () => {\n expect(plugin.name).toBe(\"${pluginName}\");\n expect(plugin.version).toBe(\"0.1.0\");\n });\n\n it(\"registers without errors\", () => {\n const hooks = {\n beforeCommand: vi.fn(),\n afterCommand: vi.fn(),\n onError: vi.fn(),\n beforeRequest: vi.fn(),\n afterResponse: vi.fn(),\n registerCommands: vi.fn(),\n };\n\n expect(() => plugin.register(hooks)).not.toThrow();\n });\n});\n`;\n await writeFile(join(testDir, \"plugin.test.ts\"), testContent);\n files.push(\"tests/plugin.test.ts\");\n\n return { dir, files };\n}\n","import { appendFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface AuditEntry {\n timestamp: string;\n command: string;\n app?: string;\n args: Record<string, unknown>;\n user?: string;\n success?: boolean;\n durationMs?: number;\n error?: string;\n}\n\nlet auditDir: string | null = null;\n\n/**\n * Initialize audit logging with a directory path.\n * Typically ~/.config/gpc/ or the XDG config dir.\n */\nexport function initAudit(configDir: string): void {\n auditDir = configDir;\n}\n\n/**\n * Write an audit log entry. Non-blocking — errors are silently ignored.\n */\nexport async function writeAuditLog(entry: AuditEntry): Promise<void> {\n if (!auditDir) return;\n\n try {\n await mkdir(auditDir, { recursive: true, mode: 0o700 });\n const logPath = join(auditDir, \"audit.log\");\n const redactedEntry = redactAuditArgs(entry);\n const line = JSON.stringify(redactedEntry) + \"\\n\";\n await appendFile(logPath, line, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(logPath, 0o600).catch(() => {});\n } catch {\n // Audit logging must never break the CLI\n }\n}\n\nconst SENSITIVE_ARG_KEYS = new Set([\n \"key\",\n \"keyFile\",\n \"key-file\",\n \"serviceAccount\",\n \"service-account\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n \"private_key\",\n \"client_secret\",\n]);\n\nfunction redactAuditArgs(entry: AuditEntry): AuditEntry {\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(entry.args)) {\n redacted[k] = SENSITIVE_ARG_KEYS.has(k) ? \"[REDACTED]\" : v;\n }\n return { ...entry, args: redacted };\n}\n\n/**\n * Convenience: create an audit entry for a write command.\n */\nexport function createAuditEntry(\n command: string,\n args: Record<string, unknown>,\n app?: string,\n): AuditEntry {\n return {\n timestamp: new Date().toISOString(),\n command,\n app,\n args,\n };\n}\n"],"mappings":"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","names":["edit","stat","extname","formatSize","readFile","stat","readdir","readFile","stat","extname","join","stat","formatSize","stat","upload","paginateAll","readdir","readFile","join","paginateAll","paginateAll","paginateAll","readFile","mkdir","writeFile","join","mkdir","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gpc-cli/core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Business logic and command orchestration for GPC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@gpc-cli/api": "1.0.
|
|
19
|
-
"@gpc-cli/
|
|
20
|
-
"@gpc-cli/
|
|
21
|
-
"@gpc-cli/
|
|
18
|
+
"@gpc-cli/api": "1.0.5",
|
|
19
|
+
"@gpc-cli/plugin-sdk": "0.9.5",
|
|
20
|
+
"@gpc-cli/auth": "0.9.6",
|
|
21
|
+
"@gpc-cli/config": "0.9.5"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"google-play",
|