@adport/provider-apple 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -7,28 +7,21 @@ interface AppleCredentials {
7
7
  /** PEM contents of the .p8 EC private key. */
8
8
  privateKeyPem: string;
9
9
  }
10
- /**
11
- * VERSION ISOLATION: everything version-specific lives in these constants and
12
- * the envelope helpers. The Campaign Management API v5 sunsets 2027-01-26; the
13
- * successor "Apple Ads Platform API" (v1, preview July 2026) changes, per
14
- * Apple's preview guide: base URL → https://api.ads.apple.com/v1, envelope
15
- * `data` → `result`, X-AP-Context orgId= → adAccountId=, GET-all//find →
16
- * POST /{entity}/query, error shape → {error:{code,message,details[]}}.
17
- * Migrate here when the Platform API reference goes live.
18
- */
19
- declare const APPLE_ADS_BASE = "https://api.searchads.apple.com/api/v5";
10
+ /** Apple Ads Platform API v1. The legacy Campaign Management API v5 retires 2027-01-26. */
11
+ declare const APPLE_ADS_BASE = "https://api.ads.apple.com/v1";
20
12
  interface AppleEnvelope<T> {
21
- data: T;
13
+ result: T;
22
14
  pagination?: {
23
- totalResults: number;
24
- startIndex: number;
25
- itemsPerPage: number;
15
+ offset?: number;
16
+ pageSize?: number;
17
+ totalCount?: number;
26
18
  } | null;
27
19
  error?: {
28
- errors?: Array<{
29
- messageCode?: string;
20
+ code?: string;
21
+ message?: string;
22
+ details?: Array<{
23
+ code?: string;
30
24
  message?: string;
31
- field?: string;
32
25
  }>;
33
26
  } | null;
34
27
  }
@@ -38,13 +31,14 @@ declare class AppleAdsClient {
38
31
  private accessToken?;
39
32
  constructor(credentials: AppleCredentials, fetchImpl?: typeof fetch);
40
33
  getAccessToken(): Promise<string>;
41
- /** orgId (as X-AP-Context) is required on all endpoints except /acls and /me. */
34
+ /** adAccountId context is required on account-scoped endpoints. */
42
35
  request<T>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, options?: {
43
- orgId?: string;
36
+ adAccountId?: string;
44
37
  body?: unknown;
38
+ form?: FormData;
45
39
  }): Promise<AppleEnvelope<T>>;
46
40
  }
47
- /** v5 error shape: {"error": {"errors": [{"messageCode", "message", "field"}]}} */
41
+ /** v1 error shape: {"error":{"code","message","details":[{"code","message"}]}}. */
48
42
  declare function formatAppleError(status: number, raw: string): string;
49
43
 
50
44
  interface ClientSecretInput {
@@ -74,15 +68,18 @@ interface Money {
74
68
  }
75
69
  interface AppleCampaign {
76
70
  id: number;
71
+ adAccountId?: number;
77
72
  name: string;
78
73
  status: 'ENABLED' | 'PAUSED';
79
- servingStatus?: string;
74
+ systemStatus?: string;
80
75
  displayStatus?: string;
81
- dailyBudgetAmount?: Money;
82
- adamId?: number;
83
- countriesOrRegions?: string[];
76
+ dailyBudget?: {
77
+ value?: Money;
78
+ };
79
+ promotedObjectId?: string;
80
+ promotedObjectType?: string;
84
81
  billingEvent?: string;
85
- supplySources?: string[];
82
+ targeting?: Record<string, unknown>;
86
83
  }
87
84
  declare class AppleAdsProvider implements AdProvider {
88
85
  private readonly client;
@@ -90,10 +87,10 @@ declare class AppleAdsProvider implements AdProvider {
90
87
  constructor(client: AppleAdsClient);
91
88
  capabilities(): ProviderCapabilities;
92
89
  standardActions(): StandardActions;
93
- /** Apple "accounts" are organizations from the user ACL. */
90
+ /** v1 exposes one ACL entry per ad account. */
94
91
  listAccounts(): Promise<Account[]>;
95
- listCampaigns(orgId: string, limit?: number): Promise<AppleCampaign[]>;
96
- getCampaign(orgId: string, campaignId: string): Promise<AppleCampaign>;
92
+ listCampaigns(adAccountId: string, limit?: number): Promise<AppleCampaign[]>;
93
+ getCampaign(adAccountId: string, campaignId: string): Promise<AppleCampaign>;
97
94
  /**
98
95
  * Safe low-level access to the documented read surface. Apple uses POST for
99
96
  * selectors and reports, so method alone cannot determine mutability.
@@ -115,6 +112,11 @@ declare class AppleAdsProvider implements AdProvider {
115
112
  private planApiCreate;
116
113
  private planApiUpdate;
117
114
  private planApiDelete;
115
+ private planUploadAsset;
116
+ private planApplyRecommendations;
117
+ private planDismissRecommendations;
118
+ private planSetBid;
119
+ private planSetSharedBudget;
118
120
  }
119
121
 
120
122
  declare function appleTools(provider: AppleAdsProvider): AnyToolDefinition[];
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ function createClientSecret(input, now = /* @__PURE__ */ new Date()) {
25
25
 
26
26
  // src/client.ts
27
27
  var TOKEN_URL = "https://appleid.apple.com/auth/oauth2/token";
28
- var APPLE_ADS_BASE = "https://api.searchads.apple.com/api/v5";
28
+ var APPLE_ADS_BASE = "https://api.ads.apple.com/v1";
29
29
  var AppleAdsClient = class {
30
30
  constructor(credentials, fetchImpl = fetch) {
31
31
  this.credentials = credentials;
@@ -61,23 +61,27 @@ var AppleAdsClient = class {
61
61
  this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1e3 };
62
62
  return this.accessToken.token;
63
63
  }
64
- /** orgId (as X-AP-Context) is required on all endpoints except /acls and /me. */
64
+ /** adAccountId context is required on account-scoped endpoints. */
65
65
  async request(method, path, options = {}) {
66
66
  const token = await this.getAccessToken();
67
+ if (options.body !== void 0 && options.form !== void 0) {
68
+ throw new AdportError("INVALID_INPUT", "Apple Ads request cannot contain both JSON and multipart bodies");
69
+ }
67
70
  const response = await this.fetchImpl(`${APPLE_ADS_BASE}/${path.replace(/^\//, "")}`, {
68
71
  method,
69
72
  headers: {
70
73
  authorization: `Bearer ${token}`,
71
- ...options.orgId ? { "X-AP-Context": `orgId=${options.orgId}` } : {},
74
+ accept: "application/json",
75
+ ...options.adAccountId ? { "X-AP-Context": `adAccountId=${options.adAccountId}` } : {},
72
76
  ...options.body !== void 0 ? { "content-type": "application/json" } : {}
73
77
  },
74
- body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
78
+ body: options.form ?? (options.body === void 0 ? void 0 : JSON.stringify(options.body))
75
79
  });
76
80
  const raw = await response.text();
77
81
  if (!response.ok) {
78
82
  throw new AdportError("PROVIDER_ERROR", formatAppleError(response.status, raw), safeJson(raw));
79
83
  }
80
- return raw ? JSON.parse(raw) : { data: void 0 };
84
+ return raw ? JSON.parse(raw) : { result: void 0 };
81
85
  }
82
86
  };
83
87
  function safeJson(raw) {
@@ -89,15 +93,16 @@ function safeJson(raw) {
89
93
  }
90
94
  function formatAppleError(status, raw) {
91
95
  const parsed = safeJson(raw);
92
- const items = parsed?.error?.errors ?? [];
93
96
  const head = `Apple Ads API error (HTTP ${status})`;
94
- if (items.length === 0) return head;
95
- const lines = items.map(
96
- (e) => `${e.messageCode ?? "?"}: ${e.message ?? ""}${e.field && e.field !== "null" ? ` (field: ${e.field})` : ""}`
97
- );
98
- const hint = status === 401 ? "\n Token or org access problem \u2014 check credentials and org roles." : "";
99
- return `${head}
100
- ${lines.join("\n ")}${hint}`;
97
+ const error = parsed?.error;
98
+ if (!error) return head;
99
+ const lines = [
100
+ error.code || error.message ? `${error.code ?? "?"}: ${error.message ?? ""}` : void 0,
101
+ ...(error.details ?? []).map((detail) => `${detail.code ?? "?"}: ${detail.message ?? ""}`)
102
+ ].filter((line) => Boolean(line));
103
+ const hint = status === 401 ? "\n Token or ad-account access problem \u2014 check credentials and account roles." : "";
104
+ return lines.length > 0 ? `${head}
105
+ ${lines.join("\n ")}${hint}` : `${head}${hint}`;
101
106
  }
102
107
 
103
108
  // src/provider.ts
@@ -105,9 +110,15 @@ import {
105
110
  AdportError as AdportError2,
106
111
  resolveDateRange
107
112
  } from "@adport/core";
113
+ import { createHash } from "crypto";
114
+ import { readFile } from "fs/promises";
115
+ import { basename, extname } from "path";
108
116
  var UNITS_TO_MICROS = 1e6;
109
- var APPLE_READ_ROOTS = /^(?:acls|me|apps(?:\/.*)?|campaigns(?:\/.*)?|adgroups(?:\/.*)?|targetingkeywords(?:\/.*)?|negativekeywords(?:\/.*)?|ads(?:\/.*)?|creatives(?:\/.*)?|product-pages(?:\/.*)?|search(?:\/.*)?|geolocations(?:\/.*)?|budgetorders(?:\/.*)?|reports(?:\/.*)?|customreports(?:\/.*)?)(?:\?.*)?$/;
110
- var APPLE_MUTATION_ROOTS = /^(?:campaigns|adgroups|targetingkeywords|negativekeywords|ads|creatives|budgetorders)(?:\/[^?#]+)*$/;
117
+ var APPLE_READ_GET_ROOTS = /^(?:acls|me|advertiser-resources|ad-accounts\/[^/?]+|adgroups\/[^/?]+|ads\/[^/?]+|apps\/[^/?]+|assets\/[^/?]+|business-brands\/[^/?]+|business-categories\/[^/?]+|campaigns\/[^/?]+(?:\/legacy-app-limited-status-reason-details)?|change-history\/[^/?]+|creatives\/[^/?]+|keywords\/[^/?]+|location-groups\/[^/?]+|locations\/[^/?]+|negative-keywords\/[^/?]+|orgs\/[^/?]+|product-pages\/[^/?]+|rejection-reasons\/apps\/[^/?]+|shared-budgets\/[^/?]+|search\/(?:apps|geo))(?:\?.*)?$/;
118
+ var APPLE_READ_POST_ROOTS = /^(?:(?:adgroups|ads|assets|business-brands|business-categories|campaigns|change-history|creatives|eligibilities\/apps|insights\/apps\/(?:impression-share|search-term-popularity)|keywords|location-groups|locations|metadata\/apps\/supported-languages|negative-keywords|product-pages(?:\/locale-details)?|recommendations\/(?:daily-budgets|target-cpas)|rejection-reasons\/(?:apps|business-brands)|reports\/(?:apps|business-brands)\/(?:adgroups|ads|campaigns|keywords|searchterms)|shared-budgets|suggestions\/(?:categories|keywords|phrases|target-cpas))\/query|apps\/[^/?]+\/locale-details\/query|search\/geo)$/;
119
+ var APPLE_CREATE_PATHS = /^(?:ad-accounts|adgroups|ads|campaigns|creatives|keywords|keywords\/bulk-create|location-groups|negative-keywords|negative-keywords\/bulk-create|shared-budgets)$/;
120
+ var APPLE_UPDATE_PATHS = /^(?:ad-accounts|adgroups|ads|campaigns|creatives|keywords|location-groups|negative-keywords|shared-budgets)\/[^/?]+$|^(?:keywords|negative-keywords)\/bulk-update$/;
121
+ var APPLE_DELETE_PATHS = /^(?:adgroups|ads|assets|campaigns|creatives|keywords|location-groups|negative-keywords|shared-budgets)\/[^/?]+$/;
111
122
  var AppleAdsProvider = class {
112
123
  constructor(client) {
113
124
  this.client = client;
@@ -125,34 +136,28 @@ var AppleAdsProvider = class {
125
136
  })
126
137
  };
127
138
  }
128
- /** Apple "accounts" are organizations from the user ACL. */
139
+ /** v1 exposes one ACL entry per ad account. */
129
140
  async listAccounts() {
130
141
  const envelope = await this.client.request("GET", "acls");
131
- return (envelope.data ?? []).map((acl) => ({
142
+ return (envelope.result?.acls ?? []).flatMap((acl) => acl.adAccount?.id === void 0 ? [] : [{
132
143
  provider: this.id,
133
- id: String(acl.orgId),
134
- name: acl.orgName,
135
- currency: acl.currency,
136
- status: acl.roleNames?.join(",") || void 0
137
- }));
144
+ id: String(acl.adAccount.id),
145
+ name: acl.adAccount.name ?? String(acl.adAccount.id),
146
+ status: acl.roles?.join(",") || void 0
147
+ }]);
138
148
  }
139
- async listCampaigns(orgId, limit = 100) {
140
- const envelope = await this.client.request("GET", `campaigns?limit=${limit}&offset=0`, {
141
- orgId
149
+ async listCampaigns(adAccountId, limit = 100) {
150
+ const envelope = await this.client.request("POST", "campaigns/query", {
151
+ adAccountId,
152
+ body: { pagination: { offset: 0, pageSize: limit, fetchTotalCount: false } }
142
153
  });
143
- return envelope.data ?? [];
154
+ return envelope.result ?? [];
144
155
  }
145
- async getCampaign(orgId, campaignId) {
146
- const envelope = await this.client.request("POST", "campaigns/find", {
147
- orgId,
148
- body: {
149
- conditions: [{ field: "id", operator: "EQUALS", values: [String(campaignId)] }],
150
- pagination: { offset: 0, limit: 1 }
151
- }
152
- });
153
- const campaign = envelope.data?.[0];
156
+ async getCampaign(adAccountId, campaignId) {
157
+ const envelope = await this.client.request("GET", `campaigns/${campaignId}`, { adAccountId });
158
+ const campaign = envelope.result;
154
159
  if (!campaign) {
155
- throw new AdportError2("PROVIDER_ERROR", `apple: campaign ${campaignId} not found in org ${orgId}`);
160
+ throw new AdportError2("PROVIDER_ERROR", `apple: campaign ${campaignId} not found in ad account ${adAccountId}`);
156
161
  }
157
162
  return campaign;
158
163
  }
@@ -162,17 +167,12 @@ var AppleAdsProvider = class {
162
167
  */
163
168
  async apiRead(input) {
164
169
  const path = normalizeApiPath(input.path);
165
- if (!APPLE_READ_ROOTS.test(path)) {
170
+ const allowed = input.method === "GET" ? APPLE_READ_GET_ROOTS.test(path) : APPLE_READ_POST_ROOTS.test(path);
171
+ if (!allowed) {
166
172
  throw new AdportError2("INVALID_INPUT", `apple: unsupported read path "${path}"`);
167
173
  }
168
- if (input.method === "POST" && !path.includes("/find") && !path.startsWith("reports/") && !path.startsWith("customreports")) {
169
- throw new AdportError2(
170
- "INVALID_INPUT",
171
- "apple: POST is read-only here only for /find, reports/*, and customreports endpoints"
172
- );
173
- }
174
174
  const envelope = await this.client.request(input.method, path, {
175
- ...input.account_id ? { orgId: input.account_id } : {},
175
+ ...input.account_id ? { adAccountId: input.account_id } : {},
176
176
  ...input.body ? { body: input.body } : {}
177
177
  });
178
178
  return envelope;
@@ -184,39 +184,33 @@ var AppleAdsProvider = class {
184
184
  const range = resolveDateRange(query.dateRange);
185
185
  const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);
186
186
  const rows = [];
187
- for (const orgId of accountIds) {
187
+ for (const adAccountId of accountIds) {
188
188
  const envelope = await this.client.request(
189
189
  "POST",
190
- "reports/campaigns",
190
+ "reports/apps/campaigns/query",
191
191
  {
192
- orgId,
192
+ adAccountId,
193
193
  body: {
194
- startTime: range.start,
195
- endTime: range.end,
196
- selector: {
197
- orderBy: [{ field: "campaignId", sortOrder: "ASCENDING" }],
198
- pagination: { offset: 0, limit: Math.min(query.limit ?? 200, 1e3) }
199
- },
200
- timeZone: "UTC",
201
- returnRowTotals: true,
202
- returnGrandTotals: true,
203
- returnRecordsWithNoMetrics: false
194
+ pagination: { offset: 0, pageSize: Math.min(query.limit ?? 200, 5e3) },
195
+ sorting: [{ field: "id", order: "ASC" }],
196
+ timeRange: { start: range.start, end: range.end, timeZone: "UTC" },
197
+ options: { includeRows: ["GRAND_TOTAL"] }
204
198
  }
205
199
  }
206
200
  );
207
- const providerRows = (envelope.data?.reportingDataResponse?.row ?? []).map(
208
- (row) => this.toReportRow(row, orgId, query)
201
+ const providerRows = (envelope.result?.rows ?? []).map(
202
+ (row) => this.toReportRow(row, adAccountId, query)
209
203
  );
210
204
  if (query.level === "account") {
211
- if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, orgId));
205
+ if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, adAccountId));
212
206
  } else {
213
207
  rows.push(...providerRows);
214
208
  }
215
209
  }
216
210
  return { rows };
217
211
  }
218
- toReportRow(row, orgId, query) {
219
- const total = row.total ?? {};
212
+ toReportRow(row, adAccountId, query) {
213
+ const total = row.totalMetrics ?? {};
220
214
  const spend = Number(total.localSpend?.amount ?? 0);
221
215
  const impressions = total.impressions ?? 0;
222
216
  const clicks = total.taps ?? 0;
@@ -235,12 +229,12 @@ var AppleAdsProvider = class {
235
229
  };
236
230
  return {
237
231
  provider: this.id,
238
- accountId: orgId,
232
+ accountId: adAccountId,
239
233
  entity: {
240
234
  level: "campaign",
241
- id: String(row.metadata?.campaignId ?? ""),
242
- name: row.metadata?.campaignName ?? "",
243
- status: row.metadata?.campaignStatus
235
+ id: String(row.metadata?.id ?? ""),
236
+ name: row.metadata?.name ?? "",
237
+ status: row.metadata?.status
244
238
  },
245
239
  metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]]))
246
240
  };
@@ -275,11 +269,21 @@ var AppleAdsProvider = class {
275
269
  return this.planApiUpdate(op.accountId, payload);
276
270
  case "apple_api_delete":
277
271
  return this.planApiDelete(op.accountId, payload);
272
+ case "apple_upload_asset":
273
+ return this.planUploadAsset(op.accountId, payload);
274
+ case "apple_apply_recommendations":
275
+ return this.planApplyRecommendations(op.accountId, payload);
276
+ case "apple_dismiss_recommendations":
277
+ return this.planDismissRecommendations(op.accountId, payload);
278
+ case "apple_set_bid":
279
+ return this.planSetBid(op.accountId, payload);
280
+ case "apple_set_shared_budget":
281
+ return this.planSetSharedBudget(op.accountId, payload);
278
282
  default:
279
283
  throw new AdportError2("PROVIDER_ERROR", `apple: unsupported write tool ${op.tool}`);
280
284
  }
281
285
  }
282
- async planCreateCampaign(orgId, payload, guard) {
286
+ async planCreateCampaign(adAccountId, payload, guard) {
283
287
  const coercions = [];
284
288
  let status = payload.status ?? "ENABLED";
285
289
  if (guard.forcePausedCreation && status === "ENABLED") {
@@ -287,14 +291,17 @@ var AppleAdsProvider = class {
287
291
  coercions.push("status coerced to PAUSED by policy (paused_creation)");
288
292
  }
289
293
  const body = {
290
- orgId: Number(orgId),
294
+ adAccountId: Number(adAccountId),
291
295
  name: payload.name,
292
- adamId: payload.adam_id,
293
- countriesOrRegions: payload.countries_or_regions,
294
- dailyBudgetAmount: { amount: String(payload.daily_budget), currency: payload.currency },
295
- adChannelType: "SEARCH",
296
+ promotedObjectType: "APPSTORE_APP",
297
+ promotedObjectId: String(payload.adam_id),
298
+ dailyBudget: { value: { amount: String(payload.daily_budget), currency: payload.currency } },
296
299
  billingEvent: "TAPS",
297
- supplySources: ["APPSTORE_SEARCH_RESULTS"],
300
+ bidStrategy: { bidStrategyType: "MANUAL_CPT", bidStrategyGoal: "TAP" },
301
+ targeting: {
302
+ countryOrRegion: { include: payload.countries_or_regions },
303
+ supplyPlacement: { include: ["APPSTORE_SEARCH_RESULTS"] }
304
+ },
298
305
  status
299
306
  };
300
307
  return {
@@ -307,13 +314,13 @@ var AppleAdsProvider = class {
307
314
  { target: `new campaign "${payload.name}" daily budget`, toMicros: payload.daily_budget * UNITS_TO_MICROS }
308
315
  ],
309
316
  execute: async () => {
310
- const envelope = await this.client.request("POST", "campaigns", { orgId, body });
311
- return envelope.data?.id ? [String(envelope.data.id)] : [];
317
+ const envelope = await this.client.request("POST", "campaigns", { adAccountId, body });
318
+ return envelope.result?.id ? [String(envelope.result.id)] : [];
312
319
  }
313
320
  };
314
321
  }
315
- async planSetStatus(orgId, payload) {
316
- const current = await this.getCampaign(orgId, payload.campaign_id);
322
+ async planSetStatus(adAccountId, payload) {
323
+ const current = await this.getCampaign(adAccountId, payload.campaign_id);
317
324
  return {
318
325
  summary: `Set Apple campaign "${current.name}" status ${current.status} \u2192 ${payload.status}`,
319
326
  changes: [`~ campaign ${payload.campaign_id} status ${current.status} \u2192 ${payload.status}`],
@@ -321,20 +328,20 @@ var AppleAdsProvider = class {
321
328
  budgetDeltas: [],
322
329
  execute: async () => {
323
330
  await this.client.request("PUT", `campaigns/${payload.campaign_id}`, {
324
- orgId,
325
- body: { campaign: { status: payload.status } }
331
+ adAccountId,
332
+ body: { status: payload.status }
326
333
  });
327
334
  return [payload.campaign_id];
328
335
  }
329
336
  };
330
337
  }
331
- async planSetBudget(orgId, payload) {
332
- const current = await this.getCampaign(orgId, payload.campaign_id);
333
- const fromAmount = current.dailyBudgetAmount ? Number(current.dailyBudgetAmount.amount) : void 0;
334
- const currency = current.dailyBudgetAmount?.currency ?? "USD";
338
+ async planSetBudget(adAccountId, payload) {
339
+ const current = await this.getCampaign(adAccountId, payload.campaign_id);
340
+ const fromAmount = current.dailyBudget?.value ? Number(current.dailyBudget.value.amount) : void 0;
341
+ const currency = current.dailyBudget?.value?.currency ?? "USD";
335
342
  return {
336
343
  summary: `Change Apple campaign "${current.name}" daily budget ${fromAmount ?? "(unset)"} \u2192 ${payload.daily_budget} ${currency}`,
337
- changes: [`~ campaign ${payload.campaign_id} dailyBudgetAmount ${fromAmount ?? "(unset)"} \u2192 ${payload.daily_budget}`],
344
+ changes: [`~ campaign ${payload.campaign_id} dailyBudget.value ${fromAmount ?? "(unset)"} \u2192 ${payload.daily_budget}`],
338
345
  coercions: [],
339
346
  budgetDeltas: [
340
347
  {
@@ -345,15 +352,15 @@ var AppleAdsProvider = class {
345
352
  ],
346
353
  execute: async () => {
347
354
  await this.client.request("PUT", `campaigns/${payload.campaign_id}`, {
348
- orgId,
349
- body: { campaign: { dailyBudgetAmount: { amount: String(payload.daily_budget), currency } } }
355
+ adAccountId,
356
+ body: { dailyBudget: { value: { amount: String(payload.daily_budget), currency } } }
350
357
  });
351
358
  return [payload.campaign_id];
352
359
  }
353
360
  };
354
361
  }
355
- async planApiCreate(orgId, payload, guard) {
356
- const path = validateMutationPath(payload.path);
362
+ async planApiCreate(adAccountId, payload, guard) {
363
+ const path = validateMutationPath(payload.path, "create");
357
364
  const body = structuredClone(payload.body);
358
365
  const coercions = [];
359
366
  if (guard.forcePausedCreation) coerceCreatedStatuses(body, coercions);
@@ -364,13 +371,13 @@ var AppleAdsProvider = class {
364
371
  coercions,
365
372
  budgetDeltas,
366
373
  execute: async () => {
367
- const envelope = await this.client.request("POST", path, { orgId, body });
368
- return extractResourceIds(envelope.data);
374
+ const envelope = await this.client.request("POST", path, { adAccountId, body });
375
+ return extractResourceIds(envelope.result);
369
376
  }
370
377
  };
371
378
  }
372
- async planApiUpdate(orgId, payload) {
373
- const path = validateMutationPath(payload.path);
379
+ async planApiUpdate(adAccountId, payload) {
380
+ const path = validateMutationPath(payload.path, "update");
374
381
  if (containsMoneyField(payload.body)) {
375
382
  throw new AdportError2(
376
383
  "INVALID_INPUT",
@@ -383,25 +390,191 @@ var AppleAdsProvider = class {
383
390
  coercions: [],
384
391
  budgetDeltas: [],
385
392
  execute: async () => {
386
- const envelope = await this.client.request("PUT", path, { orgId, body: payload.body });
387
- return extractResourceIds(envelope.data, path);
393
+ const method = path.endsWith("/bulk-update") ? "POST" : "PUT";
394
+ const envelope = await this.client.request(method, path, { adAccountId, body: payload.body });
395
+ return extractResourceIds(envelope.result, path);
388
396
  }
389
397
  };
390
398
  }
391
- async planApiDelete(orgId, payload) {
392
- const path = validateMutationPath(payload.path);
399
+ async planApiDelete(adAccountId, payload) {
400
+ const path = validateMutationPath(payload.path, "delete");
393
401
  return {
394
402
  summary: `Delete Apple Ads resource via DELETE /${path}`,
395
403
  changes: [`- DELETE /${path}`],
396
404
  coercions: [],
397
405
  budgetDeltas: [],
398
406
  execute: async () => {
399
- await this.client.request("DELETE", path, { orgId });
407
+ await this.client.request("DELETE", path, { adAccountId });
400
408
  return [path.split("/").at(-1)];
401
409
  }
402
410
  };
403
411
  }
412
+ async planUploadAsset(adAccountId, payload) {
413
+ const file = await readFile(payload.file_path);
414
+ const actualSha256 = createHash("sha256").update(file).digest("hex");
415
+ if (actualSha256 !== payload.expected_sha256.toLowerCase()) {
416
+ throw new AdportError2(
417
+ "INVALID_INPUT",
418
+ `apple: asset SHA-256 mismatch for ${payload.file_path}; expected ${payload.expected_sha256}, got ${actualSha256}`
419
+ );
420
+ }
421
+ const contentType = assetContentType(payload.file_path);
422
+ return {
423
+ summary: `Upload Apple Ads asset ${basename(payload.file_path)} (${file.byteLength} bytes)`,
424
+ changes: [`+ asset sha256=${actualSha256} promotedObject=${payload.promoted_object_id}`],
425
+ coercions: [],
426
+ budgetDeltas: [],
427
+ execute: async () => {
428
+ const form = new FormData();
429
+ form.append("file", new Blob([file], { type: contentType }), basename(payload.file_path));
430
+ form.append("promotedObjectId", payload.promoted_object_id);
431
+ form.append("promotedObjectType", payload.promoted_object_type);
432
+ const envelope = await this.client.request("POST", "assets/upload", { adAccountId, form });
433
+ return extractResourceIds(envelope.result);
434
+ }
435
+ };
436
+ }
437
+ async planApplyRecommendations(adAccountId, payload) {
438
+ const apiCategory = payload.category === "daily_budget" ? "daily-budgets" : "target-cpas";
439
+ const envelope = await this.client.request("POST", `recommendations/${apiCategory}/query`, {
440
+ adAccountId,
441
+ body: {
442
+ filters: [
443
+ { field: "promotedObjectId", operator: "EQUALS", value: [payload.promoted_object_id] },
444
+ { field: "promotedObjectType", operator: "EQUALS", value: [payload.promoted_object_type] }
445
+ ],
446
+ pagination: { offset: 0, pageSize: 100 }
447
+ }
448
+ });
449
+ const byId = new Map((envelope.result ?? []).map((recommendation) => [recommendation.id, recommendation]));
450
+ const budgetDeltas = [];
451
+ const requestBody = payload.recommendations.map((item) => {
452
+ const recommendation = byId.get(item.id);
453
+ if (!recommendation) {
454
+ throw new AdportError2("INVALID_INPUT", `apple: recommendation ${item.id} was not returned by the current v1 query`);
455
+ }
456
+ const current = payload.category === "daily_budget" ? recommendation.dailyBudget : recommendation.bidStrategy?.bidAmount;
457
+ const suggested = payload.category === "daily_budget" ? recommendation.suggestedDailyBudgetAmount : recommendation.recommendedTargetCPA;
458
+ const amount = item.applied_amount ?? Number(suggested?.amount);
459
+ const currency = item.currency ?? suggested?.currency ?? current?.currency;
460
+ if (!Number.isFinite(amount) || amount <= 0 || !currency) {
461
+ throw new AdportError2("INVALID_INPUT", `apple: recommendation ${item.id} has no usable proposed Money value`);
462
+ }
463
+ budgetDeltas.push({
464
+ target: `${payload.category} recommendation ${item.id}${recommendation.campaignName ? ` (${recommendation.campaignName})` : ""}`,
465
+ ...current?.amount !== void 0 ? { fromMicros: Number(current.amount) * UNITS_TO_MICROS } : {},
466
+ toMicros: amount * UNITS_TO_MICROS
467
+ });
468
+ return {
469
+ id: item.id,
470
+ promotedObjectId: payload.promoted_object_id,
471
+ promotedObjectType: payload.promoted_object_type,
472
+ ...payload.category === "daily_budget" ? { appliedDailyBudget: { amount: String(amount), currency } } : { appliedTargetCPA: { amount: String(amount), currency } }
473
+ };
474
+ });
475
+ return {
476
+ summary: `Apply ${requestBody.length} Apple Ads ${payload.category.replace("_", " ")} recommendation(s)`,
477
+ changes: requestBody.map((item) => `~ apply recommendation ${item.id}`),
478
+ coercions: [],
479
+ budgetDeltas,
480
+ execute: async () => {
481
+ const applied = await this.client.request("POST", `recommendations/${apiCategory}/apply`, {
482
+ adAccountId,
483
+ body: requestBody
484
+ });
485
+ return extractResourceIds(applied.result).length > 0 ? extractResourceIds(applied.result) : requestBody.map((item) => item.id);
486
+ }
487
+ };
488
+ }
489
+ async planDismissRecommendations(adAccountId, payload) {
490
+ const apiCategory = payload.category === "daily_budget" ? "daily-budgets" : "target-cpas";
491
+ const body = payload.recommendation_ids.map((id) => ({
492
+ id,
493
+ promotedObjectId: payload.promoted_object_id,
494
+ promotedObjectType: payload.promoted_object_type
495
+ }));
496
+ return {
497
+ summary: `Dismiss ${body.length} Apple Ads ${payload.category.replace("_", " ")} recommendation(s)`,
498
+ changes: body.map((item) => `~ dismiss recommendation ${item.id}`),
499
+ coercions: [],
500
+ budgetDeltas: [],
501
+ execute: async () => {
502
+ await this.client.request("POST", `recommendations/${apiCategory}/dismiss`, { adAccountId, body });
503
+ return payload.recommendation_ids;
504
+ }
505
+ };
506
+ }
507
+ async planSetBid(adAccountId, payload) {
508
+ const collection = payload.resource_type === "ad_group" ? "adgroups" : `${payload.resource_type}s`;
509
+ const envelope = await this.client.request("GET", `${collection}/${payload.resource_id}`, {
510
+ adAccountId
511
+ });
512
+ const current = envelope.result;
513
+ if (!current) throw new AdportError2("PROVIDER_ERROR", `apple: ${payload.resource_type} ${payload.resource_id} not found`);
514
+ const currentMoney = payload.resource_type === "keyword" ? current.bid : current.bidStrategy?.bid;
515
+ const currency = payload.currency ?? currentMoney?.currency;
516
+ if (!currency) {
517
+ throw new AdportError2("INVALID_INPUT", "apple: currency is required when the resource has no existing bid");
518
+ }
519
+ const money = { amount: String(payload.amount), currency };
520
+ const body = payload.resource_type === "keyword" ? { bid: money } : { bidStrategy: { ...current.bidStrategy, bid: money } };
521
+ return {
522
+ summary: `Change Apple ${payload.resource_type.replace("_", " ")} "${current.name ?? payload.resource_id}" bid ${currentMoney?.amount ?? "(unset)"} \u2192 ${payload.amount} ${currency}`,
523
+ changes: [`~ ${collection}/${payload.resource_id} bid ${currentMoney?.amount ?? "(unset)"} \u2192 ${payload.amount}`],
524
+ coercions: [],
525
+ budgetDeltas: [{
526
+ target: `${payload.resource_type} ${payload.resource_id} bid`,
527
+ ...currentMoney?.amount !== void 0 ? { fromMicros: Number(currentMoney.amount) * UNITS_TO_MICROS } : {},
528
+ toMicros: payload.amount * UNITS_TO_MICROS
529
+ }],
530
+ execute: async () => {
531
+ await this.client.request("PUT", `${collection}/${payload.resource_id}`, { adAccountId, body });
532
+ return [payload.resource_id];
533
+ }
534
+ };
535
+ }
536
+ async planSetSharedBudget(adAccountId, payload) {
537
+ const envelope = await this.client.request(
538
+ "GET",
539
+ `shared-budgets/${payload.shared_budget_id}`,
540
+ { adAccountId }
541
+ );
542
+ const current = envelope.result;
543
+ if (!current) throw new AdportError2("PROVIDER_ERROR", `apple: shared budget ${payload.shared_budget_id} not found`);
544
+ const currency = payload.currency ?? current.value?.currency;
545
+ if (!currency) throw new AdportError2("INVALID_INPUT", "apple: currency is required when the shared budget has no existing value");
546
+ return {
547
+ summary: `Change Apple shared budget "${current.name ?? payload.shared_budget_id}" ${current.value?.amount ?? "(unset)"} \u2192 ${payload.amount} ${currency}`,
548
+ changes: [`~ shared-budgets/${payload.shared_budget_id} value ${current.value?.amount ?? "(unset)"} \u2192 ${payload.amount}`],
549
+ coercions: [],
550
+ budgetDeltas: [{
551
+ target: `shared budget ${payload.shared_budget_id}`,
552
+ ...current.value?.amount !== void 0 ? { fromMicros: Number(current.value.amount) * UNITS_TO_MICROS } : {},
553
+ toMicros: payload.amount * UNITS_TO_MICROS
554
+ }],
555
+ execute: async () => {
556
+ await this.client.request("PUT", `shared-budgets/${payload.shared_budget_id}`, {
557
+ adAccountId,
558
+ body: { value: { amount: String(payload.amount), currency } }
559
+ });
560
+ return [payload.shared_budget_id];
561
+ }
562
+ };
563
+ }
404
564
  };
565
+ function assetContentType(path) {
566
+ switch (extname(path).toLowerCase()) {
567
+ case ".png":
568
+ return "image/png";
569
+ case ".jpg":
570
+ case ".jpeg":
571
+ return "image/jpeg";
572
+ case ".heic":
573
+ return "image/heic";
574
+ default:
575
+ throw new AdportError2("INVALID_INPUT", "apple: asset file must be PNG, JPG/JPEG, or HEIC");
576
+ }
577
+ }
405
578
  function normalizeApiPath(path) {
406
579
  const normalized = path.replace(/^\/+/, "").trim();
407
580
  if (!normalized || normalized.includes("://") || normalized.includes("..") || normalized.includes("#")) {
@@ -409,9 +582,10 @@ function normalizeApiPath(path) {
409
582
  }
410
583
  return normalized;
411
584
  }
412
- function validateMutationPath(path) {
585
+ function validateMutationPath(path, operation) {
413
586
  const normalized = normalizeApiPath(path);
414
- if (!APPLE_MUTATION_ROOTS.test(normalized) || normalized.includes("/find") || normalized.startsWith("reports/")) {
587
+ const allowed = operation === "create" ? APPLE_CREATE_PATHS.test(normalized) : operation === "update" ? APPLE_UPDATE_PATHS.test(normalized) : APPLE_DELETE_PATHS.test(normalized);
588
+ if (!allowed) {
415
589
  throw new AdportError2("INVALID_INPUT", `apple: unsupported mutation path "${normalized}"`);
416
590
  }
417
591
  return normalized;
@@ -437,9 +611,13 @@ function coerceCreatedStatuses(value, coercions, path = "body") {
437
611
  function isMoneyKey(key) {
438
612
  return /(budget|bidAmount|cpaGoal)/i.test(key);
439
613
  }
614
+ function isMoneyRecord(value) {
615
+ return isRecord(value) && (typeof value.amount === "string" || typeof value.amount === "number") && (value.currency === void 0 || typeof value.currency === "string");
616
+ }
440
617
  function containsMoneyField(value) {
441
618
  if (Array.isArray(value)) return value.some(containsMoneyField);
442
619
  if (!isRecord(value)) return false;
620
+ if (isMoneyRecord(value)) return true;
443
621
  return Object.entries(value).some(([key, child]) => isMoneyKey(key) || containsMoneyField(child));
444
622
  }
445
623
  function collectCreatedMoney(value, path = "body") {
@@ -449,9 +627,13 @@ function collectCreatedMoney(value, path = "body") {
449
627
  return deltas;
450
628
  }
451
629
  if (!isRecord(value)) return deltas;
630
+ if (isMoneyRecord(value)) {
631
+ const amount = Number(value.amount);
632
+ return Number.isFinite(amount) && amount >= 0 ? [{ target: path, toMicros: Math.round(amount * UNITS_TO_MICROS) }] : [];
633
+ }
452
634
  for (const [key, child] of Object.entries(value)) {
453
635
  const childPath = `${path}.${key}`;
454
- if (isMoneyKey(key)) {
636
+ if (isMoneyKey(key) && !isMoneyRecord(child)) {
455
637
  const amount = isRecord(child) ? Number(child.amount) : Number(child);
456
638
  if (Number.isFinite(amount) && amount >= 0) {
457
639
  deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });
@@ -504,12 +686,12 @@ function appleTools(provider) {
504
686
  defineTool({
505
687
  name: "apple_api_read",
506
688
  namespace: "apple",
507
- description: "Call any documented Apple Ads v5 read/query/report endpoint. GET supports public entity reads; POST is restricted to selector /find endpoints and report endpoints.",
689
+ description: "Call any documented Apple Ads Platform API v1 read, query, insight, suggestion, recommendation-query, or report endpoint.",
508
690
  input: z.object({
509
- account_id: z.string().optional().describe("Apple Ads orgId; omit only for unscoped endpoints such as acls"),
691
+ account_id: z.string().optional().describe("Apple Ads adAccountId; omit only for unscoped endpoints such as acls and me"),
510
692
  method: z.enum(["GET", "POST"]).default("GET"),
511
- path: z.string().min(1).describe("Relative v5 path, for example campaigns/123/adgroups or reports/campaigns/123/keywords"),
512
- body: z.record(z.string(), z.unknown()).optional().describe("Selector or reporting request for POST reads")
693
+ path: z.string().min(1).describe("Relative v1 path, for example campaigns/query or reports/apps/campaigns/query"),
694
+ body: z.record(z.string(), z.unknown()).optional().describe("Query, reporting, insight, or suggestion request for POST reads")
513
695
  }),
514
696
  annotations: { readOnly: true },
515
697
  async handler(input) {
@@ -519,10 +701,10 @@ function appleTools(provider) {
519
701
  defineTool({
520
702
  name: "apple_campaigns",
521
703
  namespace: "apple",
522
- description: "List Apple Ads campaigns for an organization (id, name, status, servingStatus, dailyBudgetAmount as string Money).",
704
+ description: "List Apple Ads Platform API v1 campaigns for an ad account.",
523
705
  input: z.object({
524
- account_id: z.string().describe("Apple Ads organization id (orgId)"),
525
- limit: z.number().int().positive().max(1e3).default(100)
706
+ account_id: z.string().describe("Apple Ads ad account id (adAccountId)"),
707
+ limit: z.number().int().positive().max(5e3).default(100)
526
708
  }),
527
709
  annotations: { readOnly: true },
528
710
  async handler(input) {
@@ -533,7 +715,7 @@ function appleTools(provider) {
533
715
  guardedWriteTool({
534
716
  name: "apple_create_campaign",
535
717
  namespace: "apple",
536
- description: "Create an Apple Ads search campaign (App Store search results, TAPS billing). daily_budget is a float in whole currency units; adam_id is the App Store app id. Apple has no dry run \u2014 previews are client-side diffs.",
718
+ description: "Create an Apple Ads Platform API v1 App Store campaign (search results, TAPS billing). daily_budget is a float in whole currency units; adam_id is the App Store app id. Apple has no dry run \u2014 previews are client-side diffs.",
537
719
  provider: "apple",
538
720
  kind: "create",
539
721
  payload: z.object({
@@ -564,7 +746,7 @@ function appleTools(provider) {
564
746
  guardedWriteTool({
565
747
  name: "apple_api_create",
566
748
  namespace: "apple",
567
- description: "Create a documented Apple Ads v5 entity using a relative endpoint and API-shaped body. Created ENABLED statuses are coerced to PAUSED; budget/bid Money fields are reported to policy.",
749
+ description: "Create a documented Apple Ads Platform API v1 entity using a relative endpoint and API-shaped body. Also supports keyword and negative-keyword bulk-create. Created ENABLED statuses are coerced to PAUSED; budget/bid Money fields are reported to policy.",
568
750
  provider: "apple",
569
751
  kind: "create",
570
752
  payload: z.object({
@@ -575,7 +757,7 @@ function appleTools(provider) {
575
757
  guardedWriteTool({
576
758
  name: "apple_api_update",
577
759
  namespace: "apple",
578
- description: "Update a documented non-monetary Apple Ads v5 entity. Budget/bid changes are rejected here and require typed tools with current-value policy checks.",
760
+ description: "Update a documented non-monetary Apple Ads Platform API v1 entity. Also supports keyword and negative-keyword bulk-update. Budget/bid changes are rejected here and require typed tools with current-value policy checks.",
579
761
  provider: "apple",
580
762
  kind: "update",
581
763
  payload: z.object({
@@ -586,11 +768,79 @@ function appleTools(provider) {
586
768
  guardedWriteTool({
587
769
  name: "apple_api_delete",
588
770
  namespace: "apple",
589
- description: "Delete a documented Apple Ads v5 campaign-management entity by relative path.",
771
+ description: "Delete a documented Apple Ads Platform API v1 campaign-management entity by relative path.",
590
772
  provider: "apple",
591
773
  kind: "remove",
592
774
  destructive: true,
593
775
  payload: z.object({ path: z.string().min(1) })
776
+ }),
777
+ guardedWriteTool({
778
+ name: "apple_upload_asset",
779
+ namespace: "apple",
780
+ description: "Upload a PNG, JPG/JPEG, or HEIC creative asset for a Business Brand. The required SHA-256 binds the exact local file contents to the preview/apply approval.",
781
+ provider: "apple",
782
+ kind: "create",
783
+ payload: z.object({
784
+ file_path: z.string().min(1),
785
+ expected_sha256: z.string().regex(/^[a-fA-F0-9]{64}$/),
786
+ promoted_object_id: z.string().min(1),
787
+ promoted_object_type: z.literal("BUSINESS_BRAND").default("BUSINESS_BRAND")
788
+ })
789
+ }),
790
+ guardedWriteTool({
791
+ name: "apple_apply_recommendations",
792
+ namespace: "apple",
793
+ description: "Apply Apple Ads v1 daily-budget or target-CPA recommendations. The provider re-queries every recommendation and policy-checks the current and proposed Money values before apply.",
794
+ provider: "apple",
795
+ kind: "update",
796
+ payload: z.object({
797
+ category: z.enum(["daily_budget", "target_cpa"]),
798
+ promoted_object_id: z.string().min(1),
799
+ promoted_object_type: z.enum(["APPSTORE_APP", "BUSINESS_BRAND"]),
800
+ recommendations: z.array(z.object({
801
+ id: z.string().min(1),
802
+ applied_amount: z.number().positive().optional(),
803
+ currency: z.string().length(3).optional()
804
+ })).min(1)
805
+ })
806
+ }),
807
+ guardedWriteTool({
808
+ name: "apple_dismiss_recommendations",
809
+ namespace: "apple",
810
+ description: "Dismiss Apple Ads v1 daily-budget or target-CPA recommendations through the normal preview/apply gate.",
811
+ provider: "apple",
812
+ kind: "update",
813
+ payload: z.object({
814
+ category: z.enum(["daily_budget", "target_cpa"]),
815
+ promoted_object_id: z.string().min(1),
816
+ promoted_object_type: z.enum(["APPSTORE_APP", "BUSINESS_BRAND"]),
817
+ recommendation_ids: z.array(z.string().min(1)).min(1)
818
+ })
819
+ }),
820
+ guardedWriteTool({
821
+ name: "apple_set_bid",
822
+ namespace: "apple",
823
+ description: "Change a v1 campaign, ad-group, or keyword bid after fetching the current Money value for policy enforcement.",
824
+ provider: "apple",
825
+ kind: "update",
826
+ payload: z.object({
827
+ resource_type: z.enum(["campaign", "ad_group", "keyword"]),
828
+ resource_id: z.string().min(1),
829
+ amount: z.number().positive(),
830
+ currency: z.string().length(3).optional()
831
+ })
832
+ }),
833
+ guardedWriteTool({
834
+ name: "apple_set_shared_budget",
835
+ namespace: "apple",
836
+ description: "Change an Apple Ads v1 shared-budget value after fetching its current Money value.",
837
+ provider: "apple",
838
+ kind: "update",
839
+ payload: z.object({
840
+ shared_budget_id: z.string().min(1),
841
+ amount: z.number().positive(),
842
+ currency: z.string().length(3).optional()
843
+ })
594
844
  })
595
845
  ];
596
846
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/jwt.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\nimport { createClientSecret } from './jwt.js';\n\nexport interface AppleCredentials {\n clientId: string;\n teamId: string;\n keyId: string;\n /** PEM contents of the .p8 EC private key. */\n privateKeyPem: string;\n}\n\nconst TOKEN_URL = 'https://appleid.apple.com/auth/oauth2/token';\n\n/**\n * VERSION ISOLATION: everything version-specific lives in these constants and\n * the envelope helpers. The Campaign Management API v5 sunsets 2027-01-26; the\n * successor \"Apple Ads Platform API\" (v1, preview July 2026) changes, per\n * Apple's preview guide: base URL → https://api.ads.apple.com/v1, envelope\n * `data` → `result`, X-AP-Context orgId= → adAccountId=, GET-all//find →\n * POST /{entity}/query, error shape → {error:{code,message,details[]}}.\n * Migrate here when the Platform API reference goes live.\n */\nexport const APPLE_ADS_BASE = 'https://api.searchads.apple.com/api/v5';\n\nexport interface AppleEnvelope<T> {\n data: T;\n pagination?: { totalResults: number; startIndex: number; itemsPerPage: number } | null;\n error?: { errors?: Array<{ messageCode?: string; message?: string; field?: string }> } | null;\n}\n\nexport class AppleAdsClient {\n private accessToken?: { token: string; expiresAt: number };\n\n constructor(\n private readonly credentials: AppleCredentials,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {}\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n // Mint a fresh short-lived client secret per request — no 180-day rotation to manage.\n const clientSecret = createClientSecret(this.credentials);\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: clientSecret,\n scope: 'searchadsorg',\n }),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Apple Ads OAuth failed (${response.status}). Check clientId/teamId/keyId and that the public key is uploaded in Account Settings > API.`,\n safeJson(raw),\n );\n }\n const data = JSON.parse(raw) as { access_token: string; expires_in: number };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n return this.accessToken.token;\n }\n\n /** orgId (as X-AP-Context) is required on all endpoints except /acls and /me. */\n async request<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n options: { orgId?: string; body?: unknown } = {},\n ): Promise<AppleEnvelope<T>> {\n const token = await this.getAccessToken();\n const response = await this.fetchImpl(`${APPLE_ADS_BASE}/${path.replace(/^\\//, '')}`, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n ...(options.orgId ? { 'X-AP-Context': `orgId=${options.orgId}` } : {}),\n ...(options.body !== undefined ? { 'content-type': 'application/json' } : {}),\n },\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatAppleError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : { data: undefined }) as AppleEnvelope<T>;\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** v5 error shape: {\"error\": {\"errors\": [{\"messageCode\", \"message\", \"field\"}]}} */\nexport function formatAppleError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n error?: { errors?: Array<{ messageCode?: string; message?: string; field?: string }> };\n };\n const items = parsed?.error?.errors ?? [];\n const head = `Apple Ads API error (HTTP ${status})`;\n if (items.length === 0) return head;\n const lines = items.map(\n (e) => `${e.messageCode ?? '?'}: ${e.message ?? ''}${e.field && e.field !== 'null' ? ` (field: ${e.field})` : ''}`,\n );\n const hint = status === 401 ? '\\n Token or org access problem — check credentials and org roles.' : '';\n return `${head}\\n ${lines.join('\\n ')}${hint}`;\n}\n","import { createPrivateKey, sign } from 'node:crypto';\n\nexport interface ClientSecretInput {\n /** SEARCHADS.<uuid> */\n clientId: string;\n /** SEARCHADS.<uuid> — equals clientId for self-managed orgs. */\n teamId: string;\n /** keyId of the public key uploaded in the Apple Ads UI. */\n keyId: string;\n /** EC prime256v1 private key, PEM (.p8 contents). */\n privateKeyPem: string;\n /** Secret lifetime in seconds; Apple caps exp at iat + 180 days. */\n lifetimeSeconds?: number;\n}\n\nfunction b64url(data: Buffer | string): string {\n return Buffer.from(data).toString('base64url');\n}\n\n/**\n * Apple Ads client secret: a self-signed ES256 JWT (header {alg, kid}; claims\n * iss=teamId, sub=clientId, aud=https://appleid.apple.com, iat, exp≤iat+180d).\n * We mint a short-lived secret per token request instead of storing one.\n */\nexport function createClientSecret(input: ClientSecretInput, now = new Date()): string {\n const iat = Math.floor(now.getTime() / 1000);\n const lifetime = Math.min(input.lifetimeSeconds ?? 3600, 180 * 86_400 - 60);\n const header = { alg: 'ES256', kid: input.keyId };\n const payload = {\n iss: input.teamId,\n iat,\n exp: iat + lifetime,\n aud: 'https://appleid.apple.com',\n sub: input.clientId,\n };\n const signingInput = `${b64url(JSON.stringify(header))}.${b64url(JSON.stringify(payload))}`;\n const key = createPrivateKey(input.privateKeyPem);\n // JWS ES256 requires the raw r||s signature form, not ASN.1/DER.\n const signature = sign('sha256', Buffer.from(signingInput), { key, dsaEncoding: 'ieee-p1363' });\n return `${signingInput}.${b64url(signature)}`;\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { AppleAdsClient } from './client.js';\n\n/** Apple Money amounts are strings in whole currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\ninterface Money {\n amount: string;\n currency: string;\n}\n\ninterface AppleCampaign {\n id: number;\n name: string;\n status: 'ENABLED' | 'PAUSED';\n servingStatus?: string;\n displayStatus?: string;\n dailyBudgetAmount?: Money;\n adamId?: number;\n countriesOrRegions?: string[];\n billingEvent?: string;\n supplySources?: string[];\n}\n\ninterface SpendRow {\n impressions?: number;\n taps?: number;\n ttr?: number;\n totalInstalls?: number;\n localSpend?: Money;\n tapInstallCPI?: Money;\n tapInstallRate?: number;\n}\n\ninterface ReportingRow {\n other?: boolean;\n total?: SpendRow;\n metadata?: { campaignId?: number; campaignName?: string; campaignStatus?: string };\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\nconst APPLE_READ_ROOTS = /^(?:acls|me|apps(?:\\/.*)?|campaigns(?:\\/.*)?|adgroups(?:\\/.*)?|targetingkeywords(?:\\/.*)?|negativekeywords(?:\\/.*)?|ads(?:\\/.*)?|creatives(?:\\/.*)?|product-pages(?:\\/.*)?|search(?:\\/.*)?|geolocations(?:\\/.*)?|budgetorders(?:\\/.*)?|reports(?:\\/.*)?|customreports(?:\\/.*)?)(?:\\?.*)?$/;\nconst APPLE_MUTATION_ROOTS = /^(?:campaigns|adgroups|targetingkeywords|negativekeywords|ads|creatives|budgetorders)(?:\\/[^?#]+)*$/;\n\nexport class AppleAdsProvider implements AdProvider {\n readonly id = 'apple';\n\n constructor(private readonly client: AppleAdsClient) {}\n\n capabilities(): ProviderCapabilities {\n // No validate-only mode in the Campaign Management API — client-side previews.\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'apple_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'PAUSED' },\n }),\n };\n }\n\n /** Apple \"accounts\" are organizations from the user ACL. */\n async listAccounts(): Promise<Account[]> {\n const envelope = await this.client.request<\n Array<{ orgId: number; orgName: string; currency?: string; roleNames?: string[]; timeZone?: string }>\n >('GET', 'acls');\n return (envelope.data ?? []).map((acl) => ({\n provider: this.id,\n id: String(acl.orgId),\n name: acl.orgName,\n currency: acl.currency,\n status: acl.roleNames?.join(',') || undefined,\n }));\n }\n\n async listCampaigns(orgId: string, limit = 100): Promise<AppleCampaign[]> {\n const envelope = await this.client.request<AppleCampaign[]>('GET', `campaigns?limit=${limit}&offset=0`, {\n orgId,\n });\n return envelope.data ?? [];\n }\n\n async getCampaign(orgId: string, campaignId: string): Promise<AppleCampaign> {\n const envelope = await this.client.request<AppleCampaign[]>('POST', 'campaigns/find', {\n orgId,\n body: {\n conditions: [{ field: 'id', operator: 'EQUALS', values: [String(campaignId)] }],\n pagination: { offset: 0, limit: 1 },\n },\n });\n const campaign = envelope.data?.[0];\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `apple: campaign ${campaignId} not found in org ${orgId}`);\n }\n return campaign;\n }\n\n /**\n * Safe low-level access to the documented read surface. Apple uses POST for\n * selectors and reports, so method alone cannot determine mutability.\n */\n async apiRead(input: {\n account_id?: string;\n method: 'GET' | 'POST';\n path: string;\n body?: Record<string, unknown>;\n }): Promise<unknown> {\n const path = normalizeApiPath(input.path);\n if (!APPLE_READ_ROOTS.test(path)) {\n throw new AdportError('INVALID_INPUT', `apple: unsupported read path \"${path}\"`);\n }\n if (\n input.method === 'POST' &&\n !path.includes('/find') &&\n !path.startsWith('reports/') &&\n !path.startsWith('customreports')\n ) {\n throw new AdportError(\n 'INVALID_INPUT',\n 'apple: POST is read-only here only for /find, reports/*, and customreports endpoints',\n );\n }\n const envelope = await this.client.request(input.method, path, {\n ...(input.account_id ? { orgId: input.account_id } : {}),\n ...(input.body ? { body: input.body } : {}),\n });\n return envelope;\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n if (query.level !== 'campaign' && query.level !== 'account') {\n throw new AdportError('INVALID_INPUT', 'apple: report supports campaign and account levels in v0');\n }\n const range = resolveDateRange(query.dateRange);\n const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);\n const rows: ReportRow[] = [];\n for (const orgId of accountIds) {\n const envelope = await this.client.request<{ reportingDataResponse?: { row?: ReportingRow[] } }>(\n 'POST',\n 'reports/campaigns',\n {\n orgId,\n body: {\n startTime: range.start,\n endTime: range.end,\n selector: {\n orderBy: [{ field: 'campaignId', sortOrder: 'ASCENDING' }],\n pagination: { offset: 0, limit: Math.min(query.limit ?? 200, 1000) },\n },\n timeZone: 'UTC',\n returnRowTotals: true,\n returnGrandTotals: true,\n returnRecordsWithNoMetrics: false,\n },\n },\n );\n const providerRows = (envelope.data?.reportingDataResponse?.row ?? []).map((row) =>\n this.toReportRow(row, orgId, query),\n );\n if (query.level === 'account') {\n if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, orgId));\n } else {\n rows.push(...providerRows);\n }\n }\n return { rows };\n }\n\n private toReportRow(row: ReportingRow, orgId: string, query: NormalizedQuery): ReportRow {\n const total = row.total ?? {};\n const spend = Number(total.localSpend?.amount ?? 0);\n const impressions = total.impressions ?? 0;\n // Apple has taps, not clicks; installs are the conversion event. No revenue metric exists.\n const clicks = total.taps ?? 0;\n const conversions = total.totalInstalls ?? 0;\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: 0,\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: 0,\n };\n return {\n provider: this.id,\n accountId: orgId,\n entity: {\n level: 'campaign',\n id: String(row.metadata?.campaignId ?? ''),\n name: row.metadata?.campaignName ?? '',\n status: row.metadata?.campaignStatus,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: false,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute();\n return { applied: true, resourceIds };\n }\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const payload = op.payload as never;\n switch (op.tool) {\n case 'apple_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'apple_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'apple_set_budget':\n return this.planSetBudget(op.accountId, payload);\n case 'apple_api_create':\n return this.planApiCreate(op.accountId, payload, guard);\n case 'apple_api_update':\n return this.planApiUpdate(op.accountId, payload);\n case 'apple_api_delete':\n return this.planApiDelete(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `apple: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planCreateCampaign(\n orgId: string,\n payload: {\n name: string;\n adam_id: number;\n countries_or_regions: string[];\n daily_budget: number;\n currency: string;\n status?: string;\n },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n let status = payload.status ?? 'ENABLED';\n if (guard.forcePausedCreation && status === 'ENABLED') {\n status = 'PAUSED';\n coercions.push('status coerced to PAUSED by policy (paused_creation)');\n }\n // budgetAmount (lifetime) was removed in v5.6 — dailyBudgetAmount only.\n const body = {\n orgId: Number(orgId),\n name: payload.name,\n adamId: payload.adam_id,\n countriesOrRegions: payload.countries_or_regions,\n dailyBudgetAmount: { amount: String(payload.daily_budget), currency: payload.currency },\n adChannelType: 'SEARCH',\n billingEvent: 'TAPS',\n supplySources: ['APPSTORE_SEARCH_RESULTS'],\n status,\n };\n return {\n summary: `Create Apple Ads campaign \"${payload.name}\" (${status}) — daily budget ${payload.daily_budget} ${payload.currency}`,\n changes: [\n `+ campaign \"${payload.name}\" app=${payload.adam_id} regions=[${payload.countries_or_regions.join(', ')}] status=${status}`,\n ],\n coercions,\n budgetDeltas: [\n { target: `new campaign \"${payload.name}\" daily budget`, toMicros: payload.daily_budget * UNITS_TO_MICROS },\n ],\n execute: async () => {\n const envelope = await this.client.request<AppleCampaign>('POST', 'campaigns', { orgId, body });\n return envelope.data?.id ? [String(envelope.data.id)] : [];\n },\n };\n }\n\n private async planSetStatus(\n orgId: string,\n payload: { campaign_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const current = await this.getCampaign(orgId, payload.campaign_id);\n return {\n summary: `Set Apple campaign \"${current.name}\" status ${current.status} → ${payload.status}`,\n changes: [`~ campaign ${payload.campaign_id} status ${current.status} → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.request('PUT', `campaigns/${payload.campaign_id}`, {\n orgId,\n body: { campaign: { status: payload.status } },\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planSetBudget(\n orgId: string,\n payload: { campaign_id: string; daily_budget: number },\n ): Promise<WritePlan> {\n const current = await this.getCampaign(orgId, payload.campaign_id);\n const fromAmount = current.dailyBudgetAmount ? Number(current.dailyBudgetAmount.amount) : undefined;\n const currency = current.dailyBudgetAmount?.currency ?? 'USD';\n return {\n summary: `Change Apple campaign \"${current.name}\" daily budget ${fromAmount ?? '(unset)'} → ${payload.daily_budget} ${currency}`,\n changes: [`~ campaign ${payload.campaign_id} dailyBudgetAmount ${fromAmount ?? '(unset)'} → ${payload.daily_budget}`],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.name}\" daily budget`,\n ...(fromAmount !== undefined ? { fromMicros: fromAmount * UNITS_TO_MICROS } : {}),\n toMicros: payload.daily_budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.client.request('PUT', `campaigns/${payload.campaign_id}`, {\n orgId,\n body: { campaign: { dailyBudgetAmount: { amount: String(payload.daily_budget), currency } } },\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planApiCreate(\n orgId: string,\n payload: { path: string; body: Record<string, unknown> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const path = validateMutationPath(payload.path);\n const body = structuredClone(payload.body);\n const coercions: string[] = [];\n if (guard.forcePausedCreation) coerceCreatedStatuses(body, coercions);\n const budgetDeltas = collectCreatedMoney(body);\n return {\n summary: `Create Apple Ads resource via POST /${path}`,\n changes: [`+ POST /${path} ${JSON.stringify(body)}`],\n coercions,\n budgetDeltas,\n execute: async () => {\n const envelope = await this.client.request<unknown>('POST', path, { orgId, body });\n return extractResourceIds(envelope.data);\n },\n };\n }\n\n private async planApiUpdate(\n orgId: string,\n payload: { path: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const path = validateMutationPath(payload.path);\n if (containsMoneyField(payload.body)) {\n throw new AdportError(\n 'INVALID_INPUT',\n 'apple: monetary updates require a typed budget or bid tool so current and proposed values can be policy-checked',\n );\n }\n return {\n summary: `Update Apple Ads resource via PUT /${path}`,\n changes: [`~ PUT /${path} ${JSON.stringify(payload.body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n const envelope = await this.client.request<unknown>('PUT', path, { orgId, body: payload.body });\n return extractResourceIds(envelope.data, path);\n },\n };\n }\n\n private async planApiDelete(orgId: string, payload: { path: string }): Promise<WritePlan> {\n const path = validateMutationPath(payload.path);\n return {\n summary: `Delete Apple Ads resource via DELETE /${path}`,\n changes: [`- DELETE /${path}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.request('DELETE', path, { orgId });\n return [path.split('/').at(-1)!];\n },\n };\n }\n}\n\nfunction normalizeApiPath(path: string): string {\n const normalized = path.replace(/^\\/+/, '').trim();\n if (!normalized || normalized.includes('://') || normalized.includes('..') || normalized.includes('#')) {\n throw new AdportError('INVALID_INPUT', 'apple: path must be a relative API path without traversal or fragments');\n }\n return normalized;\n}\n\nfunction validateMutationPath(path: string): string {\n const normalized = normalizeApiPath(path);\n if (!APPLE_MUTATION_ROOTS.test(normalized) || normalized.includes('/find') || normalized.startsWith('reports/')) {\n throw new AdportError('INVALID_INPUT', `apple: unsupported mutation path \"${normalized}\"`);\n }\n return normalized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction coerceCreatedStatuses(value: unknown, coercions: string[], path = 'body'): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) => coerceCreatedStatuses(item, coercions, `${path}[${index}]`));\n return;\n }\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if ((key === 'status' || key === 'campaignStatus' || key === 'adGroupStatus') && child === 'ENABLED') {\n value[key] = 'PAUSED';\n coercions.push(`${path}.${key} coerced to PAUSED by policy (paused_creation)`);\n } else {\n coerceCreatedStatuses(child, coercions, `${path}.${key}`);\n }\n }\n}\n\nfunction isMoneyKey(key: string): boolean {\n return /(budget|bidAmount|cpaGoal)/i.test(key);\n}\n\nfunction containsMoneyField(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(containsMoneyField);\n if (!isRecord(value)) return false;\n return Object.entries(value).some(([key, child]) => isMoneyKey(key) || containsMoneyField(child));\n}\n\nfunction collectCreatedMoney(value: unknown, path = 'body'): WritePreview['budgetDeltas'] {\n const deltas: WritePreview['budgetDeltas'] = [];\n if (Array.isArray(value)) {\n value.forEach((item, index) => deltas.push(...collectCreatedMoney(item, `${path}[${index}]`)));\n return deltas;\n }\n if (!isRecord(value)) return deltas;\n for (const [key, child] of Object.entries(value)) {\n const childPath = `${path}.${key}`;\n if (isMoneyKey(key)) {\n const amount = isRecord(child) ? Number(child.amount) : Number(child);\n if (Number.isFinite(amount) && amount >= 0) {\n deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });\n }\n }\n deltas.push(...collectCreatedMoney(child, childPath));\n }\n return deltas;\n}\n\nfunction extractResourceIds(value: unknown, fallback?: string): string[] {\n const ids: string[] = [];\n const visit = (item: unknown) => {\n if (Array.isArray(item)) return item.forEach(visit);\n if (!isRecord(item)) return;\n if (item.id !== undefined) ids.push(String(item.id));\n for (const child of Object.values(item)) visit(child);\n };\n visit(value);\n return ids.length > 0 ? [...new Set(ids)] : fallback ? [fallback.split('/').at(-1)!] : [];\n}\n\nfunction aggregateAccountRows(rows: ReportRow[], provider: string, accountId: string): ReportRow {\n const metrics = Object.fromEntries(\n Object.keys(rows[0]!.metrics).map((metric) => [\n metric,\n round2(rows.reduce((sum, row) => sum + (row.metrics[metric as MetricName] ?? 0), 0)),\n ]),\n ) as Partial<Record<MetricName, number>>;\n const spend = metrics.spend ?? 0;\n const impressions = metrics.impressions ?? 0;\n const clicks = metrics.clicks ?? 0;\n const conversions = metrics.conversions ?? 0;\n const conversionValue = metrics.conversion_value ?? 0;\n if ('ctr' in metrics) metrics.ctr = impressions > 0 ? round2((clicks / impressions) * 100) : 0;\n if ('cpc' in metrics) metrics.cpc = clicks > 0 ? round2(spend / clicks) : 0;\n if ('cpm' in metrics) metrics.cpm = impressions > 0 ? round2((spend / impressions) * 1000) : 0;\n if ('cpa' in metrics) metrics.cpa = conversions > 0 ? round2(spend / conversions) : 0;\n if ('roas' in metrics) metrics.roas = spend > 0 ? round2(conversionValue / spend) : 0;\n return { provider, accountId, entity: { level: 'account', id: accountId, name: accountId }, metrics };\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { AppleAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['ENABLED', 'PAUSED']);\n\nexport function appleTools(provider: AppleAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'apple_api_read',\n namespace: 'apple',\n description:\n 'Call any documented Apple Ads v5 read/query/report endpoint. GET supports public entity reads; POST is restricted to selector /find endpoints and report endpoints.',\n input: z.object({\n account_id: z.string().optional().describe('Apple Ads orgId; omit only for unscoped endpoints such as acls'),\n method: z.enum(['GET', 'POST']).default('GET'),\n path: z.string().min(1).describe('Relative v5 path, for example campaigns/123/adgroups or reports/campaigns/123/keywords'),\n body: z.record(z.string(), z.unknown()).optional().describe('Selector or reporting request for POST reads'),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n return provider.apiRead(input);\n },\n }),\n defineTool({\n name: 'apple_campaigns',\n namespace: 'apple',\n description:\n 'List Apple Ads campaigns for an organization (id, name, status, servingStatus, dailyBudgetAmount as string Money).',\n input: z.object({\n account_id: z.string().describe('Apple Ads organization id (orgId)'),\n limit: z.number().int().positive().max(1000).default(100),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id, input.limit);\n return { campaigns };\n },\n }),\n guardedWriteTool({\n name: 'apple_create_campaign',\n namespace: 'apple',\n description:\n 'Create an Apple Ads search campaign (App Store search results, TAPS billing). ' +\n 'daily_budget is a float in whole currency units; adam_id is the App Store app id. ' +\n 'Apple has no dry run — previews are client-side diffs.',\n provider: 'apple',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1),\n adam_id: z.number().int().positive().describe('App Store app id (adamId)'),\n countries_or_regions: z.array(z.string().length(2)).min(1),\n daily_budget: z.number().positive(),\n currency: z.string().length(3).describe('Org currency, e.g. USD/EUR'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'apple_set_campaign_status',\n namespace: 'apple',\n description: 'Enable or pause an Apple Ads campaign.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'apple_set_budget',\n namespace: 'apple',\n description: \"Change an Apple Ads campaign's daily budget (float, whole currency units).\",\n provider: 'apple',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget: z.number().positive() }),\n }),\n guardedWriteTool({\n name: 'apple_api_create',\n namespace: 'apple',\n description:\n 'Create a documented Apple Ads v5 entity using a relative endpoint and API-shaped body. Created ENABLED statuses are coerced to PAUSED; budget/bid Money fields are reported to policy.',\n provider: 'apple',\n kind: 'create',\n payload: z.object({\n path: z.string().min(1),\n body: z.record(z.string(), z.unknown()),\n }),\n }),\n guardedWriteTool({\n name: 'apple_api_update',\n namespace: 'apple',\n description:\n 'Update a documented non-monetary Apple Ads v5 entity. Budget/bid changes are rejected here and require typed tools with current-value policy checks.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n path: z.string().min(1),\n body: z.record(z.string(), z.unknown()),\n }),\n }),\n guardedWriteTool({\n name: 'apple_api_delete',\n namespace: 'apple',\n description: 'Delete a documented Apple Ads v5 campaign-management entity by relative path.',\n provider: 'apple',\n kind: 'remove',\n destructive: true,\n payload: z.object({ path: z.string().min(1) }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { AppleAdsClient, type AppleCredentials } from './client.js';\nimport { AppleAdsProvider } from './provider.js';\nimport { appleTools } from './tools.js';\n\nexport { AppleAdsClient, APPLE_ADS_BASE, formatAppleError, type AppleCredentials, type AppleEnvelope } from './client.js';\nexport { createClientSecret, type ClientSecretInput } from './jwt.js';\nexport { AppleAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { appleTools } from './tools.js';\n\nexport async function resolveAppleCredentials(store: CredentialStore): Promise<AppleCredentials | undefined> {\n const record = await store.get('apple');\n if (record?.data.client_id && record.data.team_id && record.data.key_id && record.data.private_key) {\n return {\n clientId: record.data.client_id,\n teamId: record.data.team_id,\n keyId: record.data.key_id,\n privateKeyPem: record.data.private_key,\n };\n }\n const env = process.env;\n if (env.APPLE_ADS_CLIENT_ID && env.APPLE_ADS_TEAM_ID && env.APPLE_ADS_KEY_ID && env.APPLE_ADS_PRIVATE_KEY) {\n return {\n clientId: env.APPLE_ADS_CLIENT_ID,\n teamId: env.APPLE_ADS_TEAM_ID,\n keyId: env.APPLE_ADS_KEY_ID,\n privateKeyPem: env.APPLE_ADS_PRIVATE_KEY,\n };\n }\n return undefined;\n}\n\nexport async function createAppleModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveAppleCredentials(store);\n if (!credentials) return undefined;\n const provider = new AppleAdsProvider(new AppleAdsClient(credentials));\n return { provider, tools: appleTools(provider) };\n}\n"],"mappings":";AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,kBAAkB,YAAY;AAevC,SAAS,OAAO,MAA+B;AAC7C,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,WAAW;AAC/C;AAOO,SAAS,mBAAmB,OAA0B,MAAM,oBAAI,KAAK,GAAW;AACrF,QAAM,MAAM,KAAK,MAAM,IAAI,QAAQ,IAAI,GAAI;AAC3C,QAAM,WAAW,KAAK,IAAI,MAAM,mBAAmB,MAAM,MAAM,QAAS,EAAE;AAC1E,QAAM,SAAS,EAAE,KAAK,SAAS,KAAK,MAAM,MAAM;AAChD,QAAM,UAAU;AAAA,IACd,KAAK,MAAM;AAAA,IACX;AAAA,IACA,KAAK,MAAM;AAAA,IACX,KAAK;AAAA,IACL,KAAK,MAAM;AAAA,EACb;AACA,QAAM,eAAe,GAAG,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC,IAAI,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AACzF,QAAM,MAAM,iBAAiB,MAAM,aAAa;AAEhD,QAAM,YAAY,KAAK,UAAU,OAAO,KAAK,YAAY,GAAG,EAAE,KAAK,aAAa,aAAa,CAAC;AAC9F,SAAO,GAAG,YAAY,IAAI,OAAO,SAAS,CAAC;AAC7C;;;AD7BA,IAAM,YAAY;AAWX,IAAM,iBAAiB;AAQvB,IAAM,iBAAN,MAAqB;AAAA,EAG1B,YACmB,aACA,YAA0B,OAC3C;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJX;AAAA,EAOR,MAAM,iBAAkC;AACtC,QAAI,KAAK,eAAe,KAAK,YAAY,YAAY,KAAK,IAAI,GAAG;AAC/D,aAAO,KAAK,YAAY;AAAA,IAC1B;AAEA,UAAM,eAAe,mBAAmB,KAAK,WAAW;AACxD,UAAM,WAAW,MAAM,KAAK,UAAU,WAAW;AAAA,MAC/C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D,MAAM,IAAI,gBAAgB;AAAA,QACxB,YAAY;AAAA,QACZ,WAAW,KAAK,YAAY;AAAA,QAC5B,eAAe;AAAA,QACf,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,UAAM,MAAM,MAAM,SAAS,KAAK;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,2BAA2B,SAAS,MAAM;AAAA,QAC1C,SAAS,GAAG;AAAA,MACd;AAAA,IACF;AACA,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAK,cAAc,EAAE,OAAO,KAAK,cAAc,WAAW,KAAK,IAAI,KAAK,KAAK,aAAa,MAAM,IAAK;AACrG,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAM,QACJ,QACA,MACA,UAA8C,CAAC,GACpB;AAC3B,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,UAAM,WAAW,MAAM,KAAK,UAAU,GAAG,cAAc,IAAI,KAAK,QAAQ,OAAO,EAAE,CAAC,IAAI;AAAA,MACpF;AAAA,MACA,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,GAAI,QAAQ,QAAQ,EAAE,gBAAgB,SAAS,QAAQ,KAAK,GAAG,IAAI,CAAC;AAAA,QACpE,GAAI,QAAQ,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MAC7E;AAAA,MACA,MAAM,QAAQ,SAAS,SAAY,SAAY,KAAK,UAAU,QAAQ,IAAI;AAAA,IAC5E,CAAC;AACD,UAAM,MAAM,MAAM,SAAS,KAAK;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,YAAY,kBAAkB,iBAAiB,SAAS,QAAQ,GAAG,GAAG,SAAS,GAAG,CAAC;AAAA,IAC/F;AACA,WAAQ,MAAM,KAAK,MAAM,GAAG,IAAI,EAAE,MAAM,OAAU;AAAA,EACpD;AACF;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAiB,QAAgB,KAAqB;AACpE,QAAM,SAAS,SAAS,GAAG;AAG3B,QAAM,QAAQ,QAAQ,OAAO,UAAU,CAAC;AACxC,QAAM,OAAO,6BAA6B,MAAM;AAChD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,QAAQ,MAAM;AAAA,IAClB,CAAC,MAAM,GAAG,EAAE,eAAe,GAAG,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,SAAS,YAAY,EAAE,KAAK,MAAM,EAAE;AAAA,EAClH;AACA,QAAM,OAAO,WAAW,MAAM,4EAAuE;AACrG,SAAO,GAAG,IAAI;AAAA,IAAO,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI;AAChD;;;AEhHA;AAAA,EACE,eAAAA;AAAA,EACA;AAAA,OAaK;AAIA,IAAM,kBAAkB;AA4C/B,IAAM,mBAAmB;AACzB,IAAM,uBAAuB;AAEtB,IAAM,mBAAN,MAA6C;AAAA,EAGlD,YAA6B,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAAzB;AAAA,EAFpB,KAAK;AAAA,EAId,eAAqC;AAEnC,WAAO,EAAE,cAAc,MAAM;AAAA,EAC/B;AAAA,EAEA,kBAAmC;AACjC,WAAO;AAAA,MACL,eAAe,CAAC,WAAW,gBAAgB;AAAA,QACzC,MAAM;AAAA,QACN,OAAO,EAAE,YAAY,WAAW,aAAa,YAAY,QAAQ,SAAS;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,eAAmC;AACvC,UAAM,WAAW,MAAM,KAAK,OAAO,QAEjC,OAAO,MAAM;AACf,YAAQ,SAAS,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS;AAAA,MACzC,UAAU,KAAK;AAAA,MACf,IAAI,OAAO,IAAI,KAAK;AAAA,MACpB,MAAM,IAAI;AAAA,MACV,UAAU,IAAI;AAAA,MACd,QAAQ,IAAI,WAAW,KAAK,GAAG,KAAK;AAAA,IACtC,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,cAAc,OAAe,QAAQ,KAA+B;AACxE,UAAM,WAAW,MAAM,KAAK,OAAO,QAAyB,OAAO,mBAAmB,KAAK,aAAa;AAAA,MACtG;AAAA,IACF,CAAC;AACD,WAAO,SAAS,QAAQ,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,YAAY,OAAe,YAA4C;AAC3E,UAAM,WAAW,MAAM,KAAK,OAAO,QAAyB,QAAQ,kBAAkB;AAAA,MACpF;AAAA,MACA,MAAM;AAAA,QACJ,YAAY,CAAC,EAAE,OAAO,MAAM,UAAU,UAAU,QAAQ,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC;AAAA,QAC9E,YAAY,EAAE,QAAQ,GAAG,OAAO,EAAE;AAAA,MACpC;AAAA,IACF,CAAC;AACD,UAAM,WAAW,SAAS,OAAO,CAAC;AAClC,QAAI,CAAC,UAAU;AACb,YAAM,IAAIA,aAAY,kBAAkB,mBAAmB,UAAU,qBAAqB,KAAK,EAAE;AAAA,IACnG;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,OAKO;AACnB,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAI,CAAC,iBAAiB,KAAK,IAAI,GAAG;AAChC,YAAM,IAAIA,aAAY,iBAAiB,iCAAiC,IAAI,GAAG;AAAA,IACjF;AACA,QACE,MAAM,WAAW,UACjB,CAAC,KAAK,SAAS,OAAO,KACtB,CAAC,KAAK,WAAW,UAAU,KAC3B,CAAC,KAAK,WAAW,eAAe,GAChC;AACA,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,MAAM,QAAQ,MAAM;AAAA,MAC7D,GAAI,MAAM,aAAa,EAAE,OAAO,MAAM,WAAW,IAAI,CAAC;AAAA,MACtD,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IAC3C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,OAAyC;AACpD,QAAI,MAAM,UAAU,cAAc,MAAM,UAAU,WAAW;AAC3D,YAAM,IAAIA,aAAY,iBAAiB,0DAA0D;AAAA,IACnG;AACA,UAAM,QAAQ,iBAAiB,MAAM,SAAS;AAC9C,UAAM,aAAa,MAAM,eAAe,MAAM,KAAK,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE;AAClF,UAAM,OAAoB,CAAC;AAC3B,eAAW,SAAS,YAAY;AAC9B,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,MAAM;AAAA,YACJ,WAAW,MAAM;AAAA,YACjB,SAAS,MAAM;AAAA,YACf,UAAU;AAAA,cACR,SAAS,CAAC,EAAE,OAAO,cAAc,WAAW,YAAY,CAAC;AAAA,cACzD,YAAY,EAAE,QAAQ,GAAG,OAAO,KAAK,IAAI,MAAM,SAAS,KAAK,GAAI,EAAE;AAAA,YACrE;AAAA,YACA,UAAU;AAAA,YACV,iBAAiB;AAAA,YACjB,mBAAmB;AAAA,YACnB,4BAA4B;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AACA,YAAM,gBAAgB,SAAS,MAAM,uBAAuB,OAAO,CAAC,GAAG;AAAA,QAAI,CAAC,QAC1E,KAAK,YAAY,KAAK,OAAO,KAAK;AAAA,MACpC;AACA,UAAI,MAAM,UAAU,WAAW;AAC7B,YAAI,aAAa,SAAS,EAAG,MAAK,KAAK,qBAAqB,cAAc,KAAK,IAAI,KAAK,CAAC;AAAA,MAC3F,OAAO;AACL,aAAK,KAAK,GAAG,YAAY;AAAA,MAC3B;AAAA,IACF;AACA,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA,EAEQ,YAAY,KAAmB,OAAe,OAAmC;AACvF,UAAM,QAAQ,IAAI,SAAS,CAAC;AAC5B,UAAM,QAAQ,OAAO,MAAM,YAAY,UAAU,CAAC;AAClD,UAAM,cAAc,MAAM,eAAe;AAEzC,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,cAAc,MAAM,iBAAiB;AAC3C,UAAM,MAAkC;AAAA,MACtC,OAAO,OAAO,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,KAAK,cAAc,IAAI,OAAQ,SAAS,cAAe,GAAG,IAAI;AAAA,MAC9D,KAAK,SAAS,IAAI,OAAO,QAAQ,MAAM,IAAI;AAAA,MAC3C,KAAK,cAAc,IAAI,OAAQ,QAAQ,cAAe,GAAI,IAAI;AAAA,MAC9D,KAAK,cAAc,IAAI,OAAO,QAAQ,WAAW,IAAI;AAAA,MACrD,MAAM;AAAA,IACR;AACA,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,WAAW;AAAA,MACX,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,IAAI,OAAO,IAAI,UAAU,cAAc,EAAE;AAAA,QACzC,MAAM,IAAI,UAAU,gBAAgB;AAAA,QACpC,QAAQ,IAAI,UAAU;AAAA,MACxB;AAAA,MACA,SAAS,OAAO,YAAY,MAAM,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,IAAoB,OAA0C;AAC/E,UAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK;AACtC,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,IAAoB,OAAyC;AAC5E,UAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK;AACtC,UAAM,cAAc,MAAM,KAAK,QAAQ;AACvC,WAAO,EAAE,SAAS,MAAM,YAAY;AAAA,EACtC;AAAA,EAEA,MAAc,KAAK,IAAoB,OAAuC;AAC5E,UAAM,UAAU,GAAG;AACnB,YAAQ,GAAG,MAAM;AAAA,MACf,KAAK;AACH,eAAO,KAAK,mBAAmB,GAAG,WAAW,SAAS,KAAK;AAAA,MAC7D,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,SAAS,KAAK;AAAA,MACxD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD;AACE,cAAM,IAAIA,aAAY,kBAAkB,iCAAiC,GAAG,IAAI,EAAE;AAAA,IACtF;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,OACA,SAQA,OACoB;AACpB,UAAM,YAAsB,CAAC;AAC7B,QAAI,SAAS,QAAQ,UAAU;AAC/B,QAAI,MAAM,uBAAuB,WAAW,WAAW;AACrD,eAAS;AACT,gBAAU,KAAK,sDAAsD;AAAA,IACvE;AAEA,UAAM,OAAO;AAAA,MACX,OAAO,OAAO,KAAK;AAAA,MACnB,MAAM,QAAQ;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,oBAAoB,QAAQ;AAAA,MAC5B,mBAAmB,EAAE,QAAQ,OAAO,QAAQ,YAAY,GAAG,UAAU,QAAQ,SAAS;AAAA,MACtF,eAAe;AAAA,MACf,cAAc;AAAA,MACd,eAAe,CAAC,yBAAyB;AAAA,MACzC;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS,8BAA8B,QAAQ,IAAI,MAAM,MAAM,yBAAoB,QAAQ,YAAY,IAAI,QAAQ,QAAQ;AAAA,MAC3H,SAAS;AAAA,QACP,eAAe,QAAQ,IAAI,SAAS,QAAQ,OAAO,aAAa,QAAQ,qBAAqB,KAAK,IAAI,CAAC,YAAY,MAAM;AAAA,MAC3H;AAAA,MACA;AAAA,MACA,cAAc;AAAA,QACZ,EAAE,QAAQ,iBAAiB,QAAQ,IAAI,kBAAkB,UAAU,QAAQ,eAAe,gBAAgB;AAAA,MAC5G;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,WAAW,MAAM,KAAK,OAAO,QAAuB,QAAQ,aAAa,EAAE,OAAO,KAAK,CAAC;AAC9F,eAAO,SAAS,MAAM,KAAK,CAAC,OAAO,SAAS,KAAK,EAAE,CAAC,IAAI,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,OACA,SACoB;AACpB,UAAM,UAAU,MAAM,KAAK,YAAY,OAAO,QAAQ,WAAW;AACjE,WAAO;AAAA,MACL,SAAS,uBAAuB,QAAQ,IAAI,YAAY,QAAQ,MAAM,WAAM,QAAQ,MAAM;AAAA,MAC1F,SAAS,CAAC,cAAc,QAAQ,WAAW,WAAW,QAAQ,MAAM,WAAM,QAAQ,MAAM,EAAE;AAAA,MAC1F,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,aAAa,QAAQ,WAAW,IAAI;AAAA,UACnE;AAAA,UACA,MAAM,EAAE,UAAU,EAAE,QAAQ,QAAQ,OAAO,EAAE;AAAA,QAC/C,CAAC;AACD,eAAO,CAAC,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,OACA,SACoB;AACpB,UAAM,UAAU,MAAM,KAAK,YAAY,OAAO,QAAQ,WAAW;AACjE,UAAM,aAAa,QAAQ,oBAAoB,OAAO,QAAQ,kBAAkB,MAAM,IAAI;AAC1F,UAAM,WAAW,QAAQ,mBAAmB,YAAY;AACxD,WAAO;AAAA,MACL,SAAS,0BAA0B,QAAQ,IAAI,kBAAkB,cAAc,SAAS,WAAM,QAAQ,YAAY,IAAI,QAAQ;AAAA,MAC9H,SAAS,CAAC,cAAc,QAAQ,WAAW,sBAAsB,cAAc,SAAS,WAAM,QAAQ,YAAY,EAAE;AAAA,MACpH,WAAW,CAAC;AAAA,MACZ,cAAc;AAAA,QACZ;AAAA,UACE,QAAQ,aAAa,QAAQ,IAAI;AAAA,UACjC,GAAI,eAAe,SAAY,EAAE,YAAY,aAAa,gBAAgB,IAAI,CAAC;AAAA,UAC/E,UAAU,QAAQ,eAAe;AAAA,QACnC;AAAA,MACF;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,aAAa,QAAQ,WAAW,IAAI;AAAA,UACnE;AAAA,UACA,MAAM,EAAE,UAAU,EAAE,mBAAmB,EAAE,QAAQ,OAAO,QAAQ,YAAY,GAAG,SAAS,EAAE,EAAE;AAAA,QAC9F,CAAC;AACD,eAAO,CAAC,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,OACA,SACA,OACoB;AACpB,UAAM,OAAO,qBAAqB,QAAQ,IAAI;AAC9C,UAAM,OAAO,gBAAgB,QAAQ,IAAI;AACzC,UAAM,YAAsB,CAAC;AAC7B,QAAI,MAAM,oBAAqB,uBAAsB,MAAM,SAAS;AACpE,UAAM,eAAe,oBAAoB,IAAI;AAC7C,WAAO;AAAA,MACL,SAAS,uCAAuC,IAAI;AAAA,MACpD,SAAS,CAAC,WAAW,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,EAAE;AAAA,MACnD;AAAA,MACA;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,WAAW,MAAM,KAAK,OAAO,QAAiB,QAAQ,MAAM,EAAE,OAAO,KAAK,CAAC;AACjF,eAAO,mBAAmB,SAAS,IAAI;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,OACA,SACoB;AACpB,UAAM,OAAO,qBAAqB,QAAQ,IAAI;AAC9C,QAAI,mBAAmB,QAAQ,IAAI,GAAG;AACpC,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS,sCAAsC,IAAI;AAAA,MACnD,SAAS,CAAC,UAAU,IAAI,IAAI,KAAK,UAAU,QAAQ,IAAI,CAAC,EAAE;AAAA,MAC1D,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,WAAW,MAAM,KAAK,OAAO,QAAiB,OAAO,MAAM,EAAE,OAAO,MAAM,QAAQ,KAAK,CAAC;AAC9F,eAAO,mBAAmB,SAAS,MAAM,IAAI;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,OAAe,SAA+C;AACxF,UAAM,OAAO,qBAAqB,QAAQ,IAAI;AAC9C,WAAO;AAAA,MACL,SAAS,yCAAyC,IAAI;AAAA,MACtD,SAAS,CAAC,aAAa,IAAI,EAAE;AAAA,MAC7B,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,UAAU,MAAM,EAAE,MAAM,CAAC;AACnD,eAAO,CAAC,KAAK,MAAM,GAAG,EAAE,GAAG,EAAE,CAAE;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,QAAM,aAAa,KAAK,QAAQ,QAAQ,EAAE,EAAE,KAAK;AACjD,MAAI,CAAC,cAAc,WAAW,SAAS,KAAK,KAAK,WAAW,SAAS,IAAI,KAAK,WAAW,SAAS,GAAG,GAAG;AACtG,UAAM,IAAIA,aAAY,iBAAiB,wEAAwE;AAAA,EACjH;AACA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAsB;AAClD,QAAM,aAAa,iBAAiB,IAAI;AACxC,MAAI,CAAC,qBAAqB,KAAK,UAAU,KAAK,WAAW,SAAS,OAAO,KAAK,WAAW,WAAW,UAAU,GAAG;AAC/G,UAAM,IAAIA,aAAY,iBAAiB,qCAAqC,UAAU,GAAG;AAAA,EAC3F;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,sBAAsB,OAAgB,WAAqB,OAAO,QAAc;AACvF,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,CAAC,MAAM,UAAU,sBAAsB,MAAM,WAAW,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;AAC1F;AAAA,EACF;AACA,MAAI,CAAC,SAAS,KAAK,EAAG;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,SAAK,QAAQ,YAAY,QAAQ,oBAAoB,QAAQ,oBAAoB,UAAU,WAAW;AACpG,YAAM,GAAG,IAAI;AACb,gBAAU,KAAK,GAAG,IAAI,IAAI,GAAG,gDAAgD;AAAA,IAC/E,OAAO;AACL,4BAAsB,OAAO,WAAW,GAAG,IAAI,IAAI,GAAG,EAAE;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,SAAS,WAAW,KAAsB;AACxC,SAAO,8BAA8B,KAAK,GAAG;AAC/C;AAEA,SAAS,mBAAmB,OAAyB;AACnD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,kBAAkB;AAC9D,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,SAAO,OAAO,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,KAAK,MAAM,WAAW,GAAG,KAAK,mBAAmB,KAAK,CAAC;AAClG;AAEA,SAAS,oBAAoB,OAAgB,OAAO,QAAsC;AACxF,QAAM,SAAuC,CAAC;AAC9C,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,CAAC,MAAM,UAAU,OAAO,KAAK,GAAG,oBAAoB,MAAM,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC;AAC7F,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAM,YAAY,GAAG,IAAI,IAAI,GAAG;AAChC,QAAI,WAAW,GAAG,GAAG;AACnB,YAAM,SAAS,SAAS,KAAK,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,KAAK;AACpE,UAAI,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC1C,eAAO,KAAK,EAAE,QAAQ,WAAW,UAAU,KAAK,MAAM,SAAS,eAAe,EAAE,CAAC;AAAA,MACnF;AAAA,IACF;AACA,WAAO,KAAK,GAAG,oBAAoB,OAAO,SAAS,CAAC;AAAA,EACtD;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAgB,UAA6B;AACvE,QAAM,MAAgB,CAAC;AACvB,QAAM,QAAQ,CAAC,SAAkB;AAC/B,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,KAAK,QAAQ,KAAK;AAClD,QAAI,CAAC,SAAS,IAAI,EAAG;AACrB,QAAI,KAAK,OAAO,OAAW,KAAI,KAAK,OAAO,KAAK,EAAE,CAAC;AACnD,eAAW,SAAS,OAAO,OAAO,IAAI,EAAG,OAAM,KAAK;AAAA,EACtD;AACA,QAAM,KAAK;AACX,SAAO,IAAI,SAAS,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,IAAI,WAAW,CAAC,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,CAAE,IAAI,CAAC;AAC1F;AAEA,SAAS,qBAAqB,MAAmB,UAAkB,WAA8B;AAC/F,QAAM,UAAU,OAAO;AAAA,IACrB,OAAO,KAAK,KAAK,CAAC,EAAG,OAAO,EAAE,IAAI,CAAC,WAAW;AAAA,MAC5C;AAAA,MACA,OAAO,KAAK,OAAO,CAAC,KAAK,QAAQ,OAAO,IAAI,QAAQ,MAAoB,KAAK,IAAI,CAAC,CAAC;AAAA,IACrF,CAAC;AAAA,EACH;AACA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,kBAAkB,QAAQ,oBAAoB;AACpD,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAQ,SAAS,cAAe,GAAG,IAAI;AAC7F,MAAI,SAAS,QAAS,SAAQ,MAAM,SAAS,IAAI,OAAO,QAAQ,MAAM,IAAI;AAC1E,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAQ,QAAQ,cAAe,GAAI,IAAI;AAC7F,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAO,QAAQ,WAAW,IAAI;AACpF,MAAI,UAAU,QAAS,SAAQ,OAAO,QAAQ,IAAI,OAAO,kBAAkB,KAAK,IAAI;AACpF,SAAO,EAAE,UAAU,WAAW,QAAQ,EAAE,OAAO,WAAW,IAAI,WAAW,MAAM,UAAU,GAAG,QAAQ;AACtG;AAEA,SAAS,OAAO,GAAmB;AACjC,SAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAC/B;;;ACngBA,SAAS,YAAY,wBAAgD;AACrE,SAAS,SAAS;AAGlB,IAAM,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC;AAE1C,SAAS,WAAW,UAAiD;AAC1E,SAAO;AAAA,IACL,WAAW;AAAA,MACT,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,OAAO,EAAE,OAAO;AAAA,QACd,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAAgE;AAAA,QAC3G,QAAQ,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,QAAQ,KAAK;AAAA,QAC7C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,wFAAwF;AAAA,QACzH,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,MAC5G,CAAC;AAAA,MACD,aAAa,EAAE,UAAU,KAAK;AAAA,MAC9B,MAAM,QAAQ,OAAO;AACnB,eAAO,SAAS,QAAQ,KAAK;AAAA,MAC/B;AAAA,IACF,CAAC;AAAA,IACD,WAAW;AAAA,MACT,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,OAAO,EAAE,OAAO;AAAA,QACd,YAAY,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,QACnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAI,EAAE,QAAQ,GAAG;AAAA,MAC1D,CAAC;AAAA,MACD,aAAa,EAAE,UAAU,KAAK;AAAA,MAC9B,MAAM,QAAQ,OAAO;AACnB,cAAM,YAAY,MAAM,SAAS,cAAc,MAAM,YAAY,MAAM,KAAK;AAC5E,eAAO,EAAE,UAAU;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MAGF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,QACzE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC;AAAA,QACzD,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,QAClC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,4BAA4B;AAAA,QACpE,QAAQ,aAAa,SAAS;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAG,QAAQ,aAAa,CAAC;AAAA,IACrE,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAG,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,IACpF,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAAA,IAC/C,CAAC;AAAA,EACH;AACF;;;ACjGA,eAAsB,wBAAwB,OAA+D;AAC3G,QAAM,SAAS,MAAM,MAAM,IAAI,OAAO;AACtC,MAAI,QAAQ,KAAK,aAAa,OAAO,KAAK,WAAW,OAAO,KAAK,UAAU,OAAO,KAAK,aAAa;AAClG,WAAO;AAAA,MACL,UAAU,OAAO,KAAK;AAAA,MACtB,QAAQ,OAAO,KAAK;AAAA,MACpB,OAAO,OAAO,KAAK;AAAA,MACnB,eAAe,OAAO,KAAK;AAAA,IAC7B;AAAA,EACF;AACA,QAAM,MAAM,QAAQ;AACpB,MAAI,IAAI,uBAAuB,IAAI,qBAAqB,IAAI,oBAAoB,IAAI,uBAAuB;AACzG,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI;AAAA,MACX,eAAe,IAAI;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,kBAAkB,OAA6D;AACnG,QAAM,cAAc,MAAM,wBAAwB,KAAK;AACvD,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,WAAW,IAAI,iBAAiB,IAAI,eAAe,WAAW,CAAC;AACrE,SAAO,EAAE,UAAU,OAAO,WAAW,QAAQ,EAAE;AACjD;","names":["AdportError"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/jwt.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\nimport { createClientSecret } from './jwt.js';\n\nexport interface AppleCredentials {\n clientId: string;\n teamId: string;\n keyId: string;\n /** PEM contents of the .p8 EC private key. */\n privateKeyPem: string;\n}\n\nconst TOKEN_URL = 'https://appleid.apple.com/auth/oauth2/token';\n\n/** Apple Ads Platform API v1. The legacy Campaign Management API v5 retires 2027-01-26. */\nexport const APPLE_ADS_BASE = 'https://api.ads.apple.com/v1';\n\nexport interface AppleEnvelope<T> {\n result: T;\n pagination?: { offset?: number; pageSize?: number; totalCount?: number } | null;\n error?: {\n code?: string;\n message?: string;\n details?: Array<{ code?: string; message?: string }>;\n } | null;\n}\n\nexport class AppleAdsClient {\n private accessToken?: { token: string; expiresAt: number };\n\n constructor(\n private readonly credentials: AppleCredentials,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {}\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n // Mint a fresh short-lived client secret per request — no 180-day rotation to manage.\n const clientSecret = createClientSecret(this.credentials);\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: clientSecret,\n scope: 'searchadsorg',\n }),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Apple Ads OAuth failed (${response.status}). Check clientId/teamId/keyId and that the public key is uploaded in Account Settings > API.`,\n safeJson(raw),\n );\n }\n const data = JSON.parse(raw) as { access_token: string; expires_in: number };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n return this.accessToken.token;\n }\n\n /** adAccountId context is required on account-scoped endpoints. */\n async request<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n options: { adAccountId?: string; body?: unknown; form?: FormData } = {},\n ): Promise<AppleEnvelope<T>> {\n const token = await this.getAccessToken();\n if (options.body !== undefined && options.form !== undefined) {\n throw new AdportError('INVALID_INPUT', 'Apple Ads request cannot contain both JSON and multipart bodies');\n }\n const response = await this.fetchImpl(`${APPLE_ADS_BASE}/${path.replace(/^\\//, '')}`, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n accept: 'application/json',\n ...(options.adAccountId ? { 'X-AP-Context': `adAccountId=${options.adAccountId}` } : {}),\n ...(options.body !== undefined ? { 'content-type': 'application/json' } : {}),\n },\n body: options.form ?? (options.body === undefined ? undefined : JSON.stringify(options.body)),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatAppleError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : { result: undefined }) as AppleEnvelope<T>;\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** v1 error shape: {\"error\":{\"code\",\"message\",\"details\":[{\"code\",\"message\"}]}}. */\nexport function formatAppleError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n error?: { code?: string; message?: string; details?: Array<{ code?: string; message?: string }> };\n };\n const head = `Apple Ads API error (HTTP ${status})`;\n const error = parsed?.error;\n if (!error) return head;\n const lines = [\n error.code || error.message ? `${error.code ?? '?'}: ${error.message ?? ''}` : undefined,\n ...(error.details ?? []).map((detail) => `${detail.code ?? '?'}: ${detail.message ?? ''}`),\n ].filter((line): line is string => Boolean(line));\n const hint = status === 401 ? '\\n Token or ad-account access problem — check credentials and account roles.' : '';\n return lines.length > 0 ? `${head}\\n ${lines.join('\\n ')}${hint}` : `${head}${hint}`;\n}\n","import { createPrivateKey, sign } from 'node:crypto';\n\nexport interface ClientSecretInput {\n /** SEARCHADS.<uuid> */\n clientId: string;\n /** SEARCHADS.<uuid> — equals clientId for self-managed orgs. */\n teamId: string;\n /** keyId of the public key uploaded in the Apple Ads UI. */\n keyId: string;\n /** EC prime256v1 private key, PEM (.p8 contents). */\n privateKeyPem: string;\n /** Secret lifetime in seconds; Apple caps exp at iat + 180 days. */\n lifetimeSeconds?: number;\n}\n\nfunction b64url(data: Buffer | string): string {\n return Buffer.from(data).toString('base64url');\n}\n\n/**\n * Apple Ads client secret: a self-signed ES256 JWT (header {alg, kid}; claims\n * iss=teamId, sub=clientId, aud=https://appleid.apple.com, iat, exp≤iat+180d).\n * We mint a short-lived secret per token request instead of storing one.\n */\nexport function createClientSecret(input: ClientSecretInput, now = new Date()): string {\n const iat = Math.floor(now.getTime() / 1000);\n const lifetime = Math.min(input.lifetimeSeconds ?? 3600, 180 * 86_400 - 60);\n const header = { alg: 'ES256', kid: input.keyId };\n const payload = {\n iss: input.teamId,\n iat,\n exp: iat + lifetime,\n aud: 'https://appleid.apple.com',\n sub: input.clientId,\n };\n const signingInput = `${b64url(JSON.stringify(header))}.${b64url(JSON.stringify(payload))}`;\n const key = createPrivateKey(input.privateKeyPem);\n // JWS ES256 requires the raw r||s signature form, not ASN.1/DER.\n const signature = sign('sha256', Buffer.from(signingInput), { key, dsaEncoding: 'ieee-p1363' });\n return `${signingInput}.${b64url(signature)}`;\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport { AppleAdsClient } from './client.js';\n\n/** Apple Money amounts are strings in whole currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\ninterface Money {\n amount: string;\n currency: string;\n}\n\ninterface AppleCampaign {\n id: number;\n adAccountId?: number;\n name: string;\n status: 'ENABLED' | 'PAUSED';\n systemStatus?: string;\n displayStatus?: string;\n dailyBudget?: { value?: Money };\n promotedObjectId?: string;\n promotedObjectType?: string;\n billingEvent?: string;\n targeting?: Record<string, unknown>;\n}\n\ninterface SpendRow {\n impressions?: number;\n taps?: number;\n ttr?: number;\n totalInstalls?: number;\n localSpend?: Money;\n tapInstallCPI?: Money;\n tapInstallRate?: number;\n}\n\ninterface ReportingRow {\n totalMetrics?: SpendRow;\n metadata?: { id?: number; name?: string; status?: string };\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\ninterface AppleRecommendation {\n id?: string;\n campaignId?: number;\n campaignName?: string;\n dailyBudget?: Money;\n suggestedDailyBudgetAmount?: Money;\n bidStrategy?: { bidAmount?: Money };\n recommendedTargetCPA?: Money;\n}\n\ninterface AppleBiddableResource {\n id?: number;\n name?: string;\n bid?: Money;\n bidStrategy?: { bidStrategyType?: string; bidStrategyGoal?: string; bid?: Money };\n}\n\nconst APPLE_READ_GET_ROOTS = /^(?:acls|me|advertiser-resources|ad-accounts\\/[^/?]+|adgroups\\/[^/?]+|ads\\/[^/?]+|apps\\/[^/?]+|assets\\/[^/?]+|business-brands\\/[^/?]+|business-categories\\/[^/?]+|campaigns\\/[^/?]+(?:\\/legacy-app-limited-status-reason-details)?|change-history\\/[^/?]+|creatives\\/[^/?]+|keywords\\/[^/?]+|location-groups\\/[^/?]+|locations\\/[^/?]+|negative-keywords\\/[^/?]+|orgs\\/[^/?]+|product-pages\\/[^/?]+|rejection-reasons\\/apps\\/[^/?]+|shared-budgets\\/[^/?]+|search\\/(?:apps|geo))(?:\\?.*)?$/;\nconst APPLE_READ_POST_ROOTS = /^(?:(?:adgroups|ads|assets|business-brands|business-categories|campaigns|change-history|creatives|eligibilities\\/apps|insights\\/apps\\/(?:impression-share|search-term-popularity)|keywords|location-groups|locations|metadata\\/apps\\/supported-languages|negative-keywords|product-pages(?:\\/locale-details)?|recommendations\\/(?:daily-budgets|target-cpas)|rejection-reasons\\/(?:apps|business-brands)|reports\\/(?:apps|business-brands)\\/(?:adgroups|ads|campaigns|keywords|searchterms)|shared-budgets|suggestions\\/(?:categories|keywords|phrases|target-cpas))\\/query|apps\\/[^/?]+\\/locale-details\\/query|search\\/geo)$/;\nconst APPLE_CREATE_PATHS = /^(?:ad-accounts|adgroups|ads|campaigns|creatives|keywords|keywords\\/bulk-create|location-groups|negative-keywords|negative-keywords\\/bulk-create|shared-budgets)$/;\nconst APPLE_UPDATE_PATHS = /^(?:ad-accounts|adgroups|ads|campaigns|creatives|keywords|location-groups|negative-keywords|shared-budgets)\\/[^/?]+$|^(?:keywords|negative-keywords)\\/bulk-update$/;\nconst APPLE_DELETE_PATHS = /^(?:adgroups|ads|assets|campaigns|creatives|keywords|location-groups|negative-keywords|shared-budgets)\\/[^/?]+$/;\n\nexport class AppleAdsProvider implements AdProvider {\n readonly id = 'apple';\n\n constructor(private readonly client: AppleAdsClient) {}\n\n capabilities(): ProviderCapabilities {\n // The Platform API has no validate-only mode — previews remain client-side.\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'apple_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'PAUSED' },\n }),\n };\n }\n\n /** v1 exposes one ACL entry per ad account. */\n async listAccounts(): Promise<Account[]> {\n const envelope = await this.client.request<{\n acls?: Array<{ adAccount?: { id?: number; name?: string; orgId?: number }; roles?: string[] }>;\n }>('GET', 'acls');\n return (envelope.result?.acls ?? []).flatMap((acl) => (acl.adAccount?.id === undefined ? [] : [{\n provider: this.id,\n id: String(acl.adAccount.id),\n name: acl.adAccount.name ?? String(acl.adAccount.id),\n status: acl.roles?.join(',') || undefined,\n }]));\n }\n\n async listCampaigns(adAccountId: string, limit = 100): Promise<AppleCampaign[]> {\n const envelope = await this.client.request<AppleCampaign[]>('POST', 'campaigns/query', {\n adAccountId,\n body: { pagination: { offset: 0, pageSize: limit, fetchTotalCount: false } },\n });\n return envelope.result ?? [];\n }\n\n async getCampaign(adAccountId: string, campaignId: string): Promise<AppleCampaign> {\n const envelope = await this.client.request<AppleCampaign>('GET', `campaigns/${campaignId}`, { adAccountId });\n const campaign = envelope.result;\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `apple: campaign ${campaignId} not found in ad account ${adAccountId}`);\n }\n return campaign;\n }\n\n /**\n * Safe low-level access to the documented read surface. Apple uses POST for\n * selectors and reports, so method alone cannot determine mutability.\n */\n async apiRead(input: {\n account_id?: string;\n method: 'GET' | 'POST';\n path: string;\n body?: Record<string, unknown>;\n }): Promise<unknown> {\n const path = normalizeApiPath(input.path);\n const allowed = input.method === 'GET' ? APPLE_READ_GET_ROOTS.test(path) : APPLE_READ_POST_ROOTS.test(path);\n if (!allowed) {\n throw new AdportError('INVALID_INPUT', `apple: unsupported read path \"${path}\"`);\n }\n const envelope = await this.client.request(input.method, path, {\n ...(input.account_id ? { adAccountId: input.account_id } : {}),\n ...(input.body ? { body: input.body } : {}),\n });\n return envelope;\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n if (query.level !== 'campaign' && query.level !== 'account') {\n throw new AdportError('INVALID_INPUT', 'apple: report supports campaign and account levels in v0');\n }\n const range = resolveDateRange(query.dateRange);\n const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);\n const rows: ReportRow[] = [];\n for (const adAccountId of accountIds) {\n const envelope = await this.client.request<{ rows?: ReportingRow[] }>(\n 'POST',\n 'reports/apps/campaigns/query',\n {\n adAccountId,\n body: {\n pagination: { offset: 0, pageSize: Math.min(query.limit ?? 200, 5000) },\n sorting: [{ field: 'id', order: 'ASC' }],\n timeRange: { start: range.start, end: range.end, timeZone: 'UTC' },\n options: { includeRows: ['GRAND_TOTAL'] },\n },\n },\n );\n const providerRows = (envelope.result?.rows ?? []).map((row) =>\n this.toReportRow(row, adAccountId, query),\n );\n if (query.level === 'account') {\n if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, adAccountId));\n } else {\n rows.push(...providerRows);\n }\n }\n return { rows };\n }\n\n private toReportRow(row: ReportingRow, adAccountId: string, query: NormalizedQuery): ReportRow {\n const total = row.totalMetrics ?? {};\n const spend = Number(total.localSpend?.amount ?? 0);\n const impressions = total.impressions ?? 0;\n // Apple has taps, not clicks; installs are the conversion event. No revenue metric exists.\n const clicks = total.taps ?? 0;\n const conversions = total.totalInstalls ?? 0;\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: 0,\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: 0,\n };\n return {\n provider: this.id,\n accountId: adAccountId,\n entity: {\n level: 'campaign',\n id: String(row.metadata?.id ?? ''),\n name: row.metadata?.name ?? '',\n status: row.metadata?.status,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: false,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute();\n return { applied: true, resourceIds };\n }\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const payload = op.payload as never;\n switch (op.tool) {\n case 'apple_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'apple_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'apple_set_budget':\n return this.planSetBudget(op.accountId, payload);\n case 'apple_api_create':\n return this.planApiCreate(op.accountId, payload, guard);\n case 'apple_api_update':\n return this.planApiUpdate(op.accountId, payload);\n case 'apple_api_delete':\n return this.planApiDelete(op.accountId, payload);\n case 'apple_upload_asset':\n return this.planUploadAsset(op.accountId, payload);\n case 'apple_apply_recommendations':\n return this.planApplyRecommendations(op.accountId, payload);\n case 'apple_dismiss_recommendations':\n return this.planDismissRecommendations(op.accountId, payload);\n case 'apple_set_bid':\n return this.planSetBid(op.accountId, payload);\n case 'apple_set_shared_budget':\n return this.planSetSharedBudget(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `apple: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planCreateCampaign(\n adAccountId: string,\n payload: {\n name: string;\n adam_id: number;\n countries_or_regions: string[];\n daily_budget: number;\n currency: string;\n status?: string;\n },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n let status = payload.status ?? 'ENABLED';\n if (guard.forcePausedCreation && status === 'ENABLED') {\n status = 'PAUSED';\n coercions.push('status coerced to PAUSED by policy (paused_creation)');\n }\n const body = {\n adAccountId: Number(adAccountId),\n name: payload.name,\n promotedObjectType: 'APPSTORE_APP',\n promotedObjectId: String(payload.adam_id),\n dailyBudget: { value: { amount: String(payload.daily_budget), currency: payload.currency } },\n billingEvent: 'TAPS',\n bidStrategy: { bidStrategyType: 'MANUAL_CPT', bidStrategyGoal: 'TAP' },\n targeting: {\n countryOrRegion: { include: payload.countries_or_regions },\n supplyPlacement: { include: ['APPSTORE_SEARCH_RESULTS'] },\n },\n status,\n };\n return {\n summary: `Create Apple Ads campaign \"${payload.name}\" (${status}) — daily budget ${payload.daily_budget} ${payload.currency}`,\n changes: [\n `+ campaign \"${payload.name}\" app=${payload.adam_id} regions=[${payload.countries_or_regions.join(', ')}] status=${status}`,\n ],\n coercions,\n budgetDeltas: [\n { target: `new campaign \"${payload.name}\" daily budget`, toMicros: payload.daily_budget * UNITS_TO_MICROS },\n ],\n execute: async () => {\n const envelope = await this.client.request<AppleCampaign>('POST', 'campaigns', { adAccountId, body });\n return envelope.result?.id ? [String(envelope.result.id)] : [];\n },\n };\n }\n\n private async planSetStatus(\n adAccountId: string,\n payload: { campaign_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const current = await this.getCampaign(adAccountId, payload.campaign_id);\n return {\n summary: `Set Apple campaign \"${current.name}\" status ${current.status} → ${payload.status}`,\n changes: [`~ campaign ${payload.campaign_id} status ${current.status} → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.request('PUT', `campaigns/${payload.campaign_id}`, {\n adAccountId,\n body: { status: payload.status },\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planSetBudget(\n adAccountId: string,\n payload: { campaign_id: string; daily_budget: number },\n ): Promise<WritePlan> {\n const current = await this.getCampaign(adAccountId, payload.campaign_id);\n const fromAmount = current.dailyBudget?.value ? Number(current.dailyBudget.value.amount) : undefined;\n const currency = current.dailyBudget?.value?.currency ?? 'USD';\n return {\n summary: `Change Apple campaign \"${current.name}\" daily budget ${fromAmount ?? '(unset)'} → ${payload.daily_budget} ${currency}`,\n changes: [`~ campaign ${payload.campaign_id} dailyBudget.value ${fromAmount ?? '(unset)'} → ${payload.daily_budget}`],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.name}\" daily budget`,\n ...(fromAmount !== undefined ? { fromMicros: fromAmount * UNITS_TO_MICROS } : {}),\n toMicros: payload.daily_budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.client.request('PUT', `campaigns/${payload.campaign_id}`, {\n adAccountId,\n body: { dailyBudget: { value: { amount: String(payload.daily_budget), currency } } },\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planApiCreate(\n adAccountId: string,\n payload: { path: string; body: Record<string, unknown> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const path = validateMutationPath(payload.path, 'create');\n const body = structuredClone(payload.body);\n const coercions: string[] = [];\n if (guard.forcePausedCreation) coerceCreatedStatuses(body, coercions);\n const budgetDeltas = collectCreatedMoney(body);\n return {\n summary: `Create Apple Ads resource via POST /${path}`,\n changes: [`+ POST /${path} ${JSON.stringify(body)}`],\n coercions,\n budgetDeltas,\n execute: async () => {\n const envelope = await this.client.request<unknown>('POST', path, { adAccountId, body });\n return extractResourceIds(envelope.result);\n },\n };\n }\n\n private async planApiUpdate(\n adAccountId: string,\n payload: { path: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const path = validateMutationPath(payload.path, 'update');\n if (containsMoneyField(payload.body)) {\n throw new AdportError(\n 'INVALID_INPUT',\n 'apple: monetary updates require a typed budget or bid tool so current and proposed values can be policy-checked',\n );\n }\n return {\n summary: `Update Apple Ads resource via PUT /${path}`,\n changes: [`~ PUT /${path} ${JSON.stringify(payload.body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n const method = path.endsWith('/bulk-update') ? 'POST' : 'PUT';\n const envelope = await this.client.request<unknown>(method, path, { adAccountId, body: payload.body });\n return extractResourceIds(envelope.result, path);\n },\n };\n }\n\n private async planApiDelete(adAccountId: string, payload: { path: string }): Promise<WritePlan> {\n const path = validateMutationPath(payload.path, 'delete');\n return {\n summary: `Delete Apple Ads resource via DELETE /${path}`,\n changes: [`- DELETE /${path}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.request('DELETE', path, { adAccountId });\n return [path.split('/').at(-1)!];\n },\n };\n }\n\n private async planUploadAsset(\n adAccountId: string,\n payload: {\n file_path: string;\n expected_sha256: string;\n promoted_object_id: string;\n promoted_object_type: 'BUSINESS_BRAND';\n },\n ): Promise<WritePlan> {\n const file = await readFile(payload.file_path);\n const actualSha256 = createHash('sha256').update(file).digest('hex');\n if (actualSha256 !== payload.expected_sha256.toLowerCase()) {\n throw new AdportError(\n 'INVALID_INPUT',\n `apple: asset SHA-256 mismatch for ${payload.file_path}; expected ${payload.expected_sha256}, got ${actualSha256}`,\n );\n }\n const contentType = assetContentType(payload.file_path);\n return {\n summary: `Upload Apple Ads asset ${basename(payload.file_path)} (${file.byteLength} bytes)`,\n changes: [`+ asset sha256=${actualSha256} promotedObject=${payload.promoted_object_id}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n const form = new FormData();\n form.append('file', new Blob([file], { type: contentType }), basename(payload.file_path));\n form.append('promotedObjectId', payload.promoted_object_id);\n form.append('promotedObjectType', payload.promoted_object_type);\n const envelope = await this.client.request<unknown>('POST', 'assets/upload', { adAccountId, form });\n return extractResourceIds(envelope.result);\n },\n };\n }\n\n private async planApplyRecommendations(\n adAccountId: string,\n payload: {\n category: 'daily_budget' | 'target_cpa';\n promoted_object_id: string;\n promoted_object_type: 'APPSTORE_APP' | 'BUSINESS_BRAND';\n recommendations: Array<{ id: string; applied_amount?: number; currency?: string }>;\n },\n ): Promise<WritePlan> {\n const apiCategory = payload.category === 'daily_budget' ? 'daily-budgets' : 'target-cpas';\n const envelope = await this.client.request<AppleRecommendation[]>('POST', `recommendations/${apiCategory}/query`, {\n adAccountId,\n body: {\n filters: [\n { field: 'promotedObjectId', operator: 'EQUALS', value: [payload.promoted_object_id] },\n { field: 'promotedObjectType', operator: 'EQUALS', value: [payload.promoted_object_type] },\n ],\n pagination: { offset: 0, pageSize: 100 },\n },\n });\n const byId = new Map((envelope.result ?? []).map((recommendation) => [recommendation.id, recommendation]));\n const budgetDeltas: WritePreview['budgetDeltas'] = [];\n const requestBody = payload.recommendations.map((item) => {\n const recommendation = byId.get(item.id);\n if (!recommendation) {\n throw new AdportError('INVALID_INPUT', `apple: recommendation ${item.id} was not returned by the current v1 query`);\n }\n const current = payload.category === 'daily_budget'\n ? recommendation.dailyBudget\n : recommendation.bidStrategy?.bidAmount;\n const suggested = payload.category === 'daily_budget'\n ? recommendation.suggestedDailyBudgetAmount\n : recommendation.recommendedTargetCPA;\n const amount = item.applied_amount ?? Number(suggested?.amount);\n const currency = item.currency ?? suggested?.currency ?? current?.currency;\n if (!Number.isFinite(amount) || amount <= 0 || !currency) {\n throw new AdportError('INVALID_INPUT', `apple: recommendation ${item.id} has no usable proposed Money value`);\n }\n budgetDeltas.push({\n target: `${payload.category} recommendation ${item.id}${recommendation.campaignName ? ` (${recommendation.campaignName})` : ''}`,\n ...(current?.amount !== undefined ? { fromMicros: Number(current.amount) * UNITS_TO_MICROS } : {}),\n toMicros: amount * UNITS_TO_MICROS,\n });\n return {\n id: item.id,\n promotedObjectId: payload.promoted_object_id,\n promotedObjectType: payload.promoted_object_type,\n ...(payload.category === 'daily_budget'\n ? { appliedDailyBudget: { amount: String(amount), currency } }\n : { appliedTargetCPA: { amount: String(amount), currency } }),\n };\n });\n return {\n summary: `Apply ${requestBody.length} Apple Ads ${payload.category.replace('_', ' ')} recommendation(s)`,\n changes: requestBody.map((item) => `~ apply recommendation ${item.id}`),\n coercions: [],\n budgetDeltas,\n execute: async () => {\n const applied = await this.client.request<unknown[]>('POST', `recommendations/${apiCategory}/apply`, {\n adAccountId,\n body: requestBody,\n });\n return extractResourceIds(applied.result).length > 0\n ? extractResourceIds(applied.result)\n : requestBody.map((item) => item.id);\n },\n };\n }\n\n private async planDismissRecommendations(\n adAccountId: string,\n payload: {\n category: 'daily_budget' | 'target_cpa';\n promoted_object_id: string;\n promoted_object_type: 'APPSTORE_APP' | 'BUSINESS_BRAND';\n recommendation_ids: string[];\n },\n ): Promise<WritePlan> {\n const apiCategory = payload.category === 'daily_budget' ? 'daily-budgets' : 'target-cpas';\n const body = payload.recommendation_ids.map((id) => ({\n id,\n promotedObjectId: payload.promoted_object_id,\n promotedObjectType: payload.promoted_object_type,\n }));\n return {\n summary: `Dismiss ${body.length} Apple Ads ${payload.category.replace('_', ' ')} recommendation(s)`,\n changes: body.map((item) => `~ dismiss recommendation ${item.id}`),\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.request('POST', `recommendations/${apiCategory}/dismiss`, { adAccountId, body });\n return payload.recommendation_ids;\n },\n };\n }\n\n private async planSetBid(\n adAccountId: string,\n payload: {\n resource_type: 'campaign' | 'ad_group' | 'keyword';\n resource_id: string;\n amount: number;\n currency?: string;\n },\n ): Promise<WritePlan> {\n const collection = payload.resource_type === 'ad_group' ? 'adgroups' : `${payload.resource_type}s`;\n const envelope = await this.client.request<AppleBiddableResource>('GET', `${collection}/${payload.resource_id}`, {\n adAccountId,\n });\n const current = envelope.result;\n if (!current) throw new AdportError('PROVIDER_ERROR', `apple: ${payload.resource_type} ${payload.resource_id} not found`);\n const currentMoney = payload.resource_type === 'keyword' ? current.bid : current.bidStrategy?.bid;\n const currency = payload.currency ?? currentMoney?.currency;\n if (!currency) {\n throw new AdportError('INVALID_INPUT', 'apple: currency is required when the resource has no existing bid');\n }\n const money = { amount: String(payload.amount), currency };\n const body = payload.resource_type === 'keyword'\n ? { bid: money }\n : { bidStrategy: { ...current.bidStrategy, bid: money } };\n return {\n summary: `Change Apple ${payload.resource_type.replace('_', ' ')} \"${current.name ?? payload.resource_id}\" bid ${currentMoney?.amount ?? '(unset)'} → ${payload.amount} ${currency}`,\n changes: [`~ ${collection}/${payload.resource_id} bid ${currentMoney?.amount ?? '(unset)'} → ${payload.amount}`],\n coercions: [],\n budgetDeltas: [{\n target: `${payload.resource_type} ${payload.resource_id} bid`,\n ...(currentMoney?.amount !== undefined ? { fromMicros: Number(currentMoney.amount) * UNITS_TO_MICROS } : {}),\n toMicros: payload.amount * UNITS_TO_MICROS,\n }],\n execute: async () => {\n await this.client.request('PUT', `${collection}/${payload.resource_id}`, { adAccountId, body });\n return [payload.resource_id];\n },\n };\n }\n\n private async planSetSharedBudget(\n adAccountId: string,\n payload: { shared_budget_id: string; amount: number; currency?: string },\n ): Promise<WritePlan> {\n const envelope = await this.client.request<{ id?: number; name?: string; value?: Money }>(\n 'GET', `shared-budgets/${payload.shared_budget_id}`, { adAccountId },\n );\n const current = envelope.result;\n if (!current) throw new AdportError('PROVIDER_ERROR', `apple: shared budget ${payload.shared_budget_id} not found`);\n const currency = payload.currency ?? current.value?.currency;\n if (!currency) throw new AdportError('INVALID_INPUT', 'apple: currency is required when the shared budget has no existing value');\n return {\n summary: `Change Apple shared budget \"${current.name ?? payload.shared_budget_id}\" ${current.value?.amount ?? '(unset)'} → ${payload.amount} ${currency}`,\n changes: [`~ shared-budgets/${payload.shared_budget_id} value ${current.value?.amount ?? '(unset)'} → ${payload.amount}`],\n coercions: [],\n budgetDeltas: [{\n target: `shared budget ${payload.shared_budget_id}`,\n ...(current.value?.amount !== undefined ? { fromMicros: Number(current.value.amount) * UNITS_TO_MICROS } : {}),\n toMicros: payload.amount * UNITS_TO_MICROS,\n }],\n execute: async () => {\n await this.client.request('PUT', `shared-budgets/${payload.shared_budget_id}`, {\n adAccountId,\n body: { value: { amount: String(payload.amount), currency } },\n });\n return [payload.shared_budget_id];\n },\n };\n }\n}\n\nfunction assetContentType(path: string): string {\n switch (extname(path).toLowerCase()) {\n case '.png': return 'image/png';\n case '.jpg':\n case '.jpeg': return 'image/jpeg';\n case '.heic': return 'image/heic';\n default:\n throw new AdportError('INVALID_INPUT', 'apple: asset file must be PNG, JPG/JPEG, or HEIC');\n }\n}\n\nfunction normalizeApiPath(path: string): string {\n const normalized = path.replace(/^\\/+/, '').trim();\n if (!normalized || normalized.includes('://') || normalized.includes('..') || normalized.includes('#')) {\n throw new AdportError('INVALID_INPUT', 'apple: path must be a relative API path without traversal or fragments');\n }\n return normalized;\n}\n\nfunction validateMutationPath(path: string, operation: 'create' | 'update' | 'delete'): string {\n const normalized = normalizeApiPath(path);\n const allowed = operation === 'create'\n ? APPLE_CREATE_PATHS.test(normalized)\n : operation === 'update'\n ? APPLE_UPDATE_PATHS.test(normalized)\n : APPLE_DELETE_PATHS.test(normalized);\n if (!allowed) {\n throw new AdportError('INVALID_INPUT', `apple: unsupported mutation path \"${normalized}\"`);\n }\n return normalized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction coerceCreatedStatuses(value: unknown, coercions: string[], path = 'body'): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) => coerceCreatedStatuses(item, coercions, `${path}[${index}]`));\n return;\n }\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if ((key === 'status' || key === 'campaignStatus' || key === 'adGroupStatus') && child === 'ENABLED') {\n value[key] = 'PAUSED';\n coercions.push(`${path}.${key} coerced to PAUSED by policy (paused_creation)`);\n } else {\n coerceCreatedStatuses(child, coercions, `${path}.${key}`);\n }\n }\n}\n\nfunction isMoneyKey(key: string): boolean {\n return /(budget|bidAmount|cpaGoal)/i.test(key);\n}\n\nfunction isMoneyRecord(value: unknown): value is Record<string, unknown> & { amount: string | number } {\n return isRecord(value) && (typeof value.amount === 'string' || typeof value.amount === 'number') &&\n (value.currency === undefined || typeof value.currency === 'string');\n}\n\nfunction containsMoneyField(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(containsMoneyField);\n if (!isRecord(value)) return false;\n if (isMoneyRecord(value)) return true;\n return Object.entries(value).some(([key, child]) => isMoneyKey(key) || containsMoneyField(child));\n}\n\nfunction collectCreatedMoney(value: unknown, path = 'body'): WritePreview['budgetDeltas'] {\n const deltas: WritePreview['budgetDeltas'] = [];\n if (Array.isArray(value)) {\n value.forEach((item, index) => deltas.push(...collectCreatedMoney(item, `${path}[${index}]`)));\n return deltas;\n }\n if (!isRecord(value)) return deltas;\n if (isMoneyRecord(value)) {\n const amount = Number(value.amount);\n return Number.isFinite(amount) && amount >= 0\n ? [{ target: path, toMicros: Math.round(amount * UNITS_TO_MICROS) }]\n : [];\n }\n for (const [key, child] of Object.entries(value)) {\n const childPath = `${path}.${key}`;\n if (isMoneyKey(key) && !isMoneyRecord(child)) {\n const amount = isRecord(child) ? Number(child.amount) : Number(child);\n if (Number.isFinite(amount) && amount >= 0) {\n deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });\n }\n }\n deltas.push(...collectCreatedMoney(child, childPath));\n }\n return deltas;\n}\n\nfunction extractResourceIds(value: unknown, fallback?: string): string[] {\n const ids: string[] = [];\n const visit = (item: unknown) => {\n if (Array.isArray(item)) return item.forEach(visit);\n if (!isRecord(item)) return;\n if (item.id !== undefined) ids.push(String(item.id));\n for (const child of Object.values(item)) visit(child);\n };\n visit(value);\n return ids.length > 0 ? [...new Set(ids)] : fallback ? [fallback.split('/').at(-1)!] : [];\n}\n\nfunction aggregateAccountRows(rows: ReportRow[], provider: string, accountId: string): ReportRow {\n const metrics = Object.fromEntries(\n Object.keys(rows[0]!.metrics).map((metric) => [\n metric,\n round2(rows.reduce((sum, row) => sum + (row.metrics[metric as MetricName] ?? 0), 0)),\n ]),\n ) as Partial<Record<MetricName, number>>;\n const spend = metrics.spend ?? 0;\n const impressions = metrics.impressions ?? 0;\n const clicks = metrics.clicks ?? 0;\n const conversions = metrics.conversions ?? 0;\n const conversionValue = metrics.conversion_value ?? 0;\n if ('ctr' in metrics) metrics.ctr = impressions > 0 ? round2((clicks / impressions) * 100) : 0;\n if ('cpc' in metrics) metrics.cpc = clicks > 0 ? round2(spend / clicks) : 0;\n if ('cpm' in metrics) metrics.cpm = impressions > 0 ? round2((spend / impressions) * 1000) : 0;\n if ('cpa' in metrics) metrics.cpa = conversions > 0 ? round2(spend / conversions) : 0;\n if ('roas' in metrics) metrics.roas = spend > 0 ? round2(conversionValue / spend) : 0;\n return { provider, accountId, entity: { level: 'account', id: accountId, name: accountId }, metrics };\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { AppleAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['ENABLED', 'PAUSED']);\n\nexport function appleTools(provider: AppleAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'apple_api_read',\n namespace: 'apple',\n description:\n 'Call any documented Apple Ads Platform API v1 read, query, insight, suggestion, recommendation-query, or report endpoint.',\n input: z.object({\n account_id: z.string().optional().describe('Apple Ads adAccountId; omit only for unscoped endpoints such as acls and me'),\n method: z.enum(['GET', 'POST']).default('GET'),\n path: z.string().min(1).describe('Relative v1 path, for example campaigns/query or reports/apps/campaigns/query'),\n body: z.record(z.string(), z.unknown()).optional().describe('Query, reporting, insight, or suggestion request for POST reads'),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n return provider.apiRead(input);\n },\n }),\n defineTool({\n name: 'apple_campaigns',\n namespace: 'apple',\n description:\n 'List Apple Ads Platform API v1 campaigns for an ad account.',\n input: z.object({\n account_id: z.string().describe('Apple Ads ad account id (adAccountId)'),\n limit: z.number().int().positive().max(5000).default(100),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id, input.limit);\n return { campaigns };\n },\n }),\n guardedWriteTool({\n name: 'apple_create_campaign',\n namespace: 'apple',\n description:\n 'Create an Apple Ads Platform API v1 App Store campaign (search results, TAPS billing). ' +\n 'daily_budget is a float in whole currency units; adam_id is the App Store app id. ' +\n 'Apple has no dry run — previews are client-side diffs.',\n provider: 'apple',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1),\n adam_id: z.number().int().positive().describe('App Store app id (adamId)'),\n countries_or_regions: z.array(z.string().length(2)).min(1),\n daily_budget: z.number().positive(),\n currency: z.string().length(3).describe('Org currency, e.g. USD/EUR'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'apple_set_campaign_status',\n namespace: 'apple',\n description: 'Enable or pause an Apple Ads campaign.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'apple_set_budget',\n namespace: 'apple',\n description: \"Change an Apple Ads campaign's daily budget (float, whole currency units).\",\n provider: 'apple',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget: z.number().positive() }),\n }),\n guardedWriteTool({\n name: 'apple_api_create',\n namespace: 'apple',\n description:\n 'Create a documented Apple Ads Platform API v1 entity using a relative endpoint and API-shaped body. Also supports keyword and negative-keyword bulk-create. Created ENABLED statuses are coerced to PAUSED; budget/bid Money fields are reported to policy.',\n provider: 'apple',\n kind: 'create',\n payload: z.object({\n path: z.string().min(1),\n body: z.record(z.string(), z.unknown()),\n }),\n }),\n guardedWriteTool({\n name: 'apple_api_update',\n namespace: 'apple',\n description:\n 'Update a documented non-monetary Apple Ads Platform API v1 entity. Also supports keyword and negative-keyword bulk-update. Budget/bid changes are rejected here and require typed tools with current-value policy checks.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n path: z.string().min(1),\n body: z.record(z.string(), z.unknown()),\n }),\n }),\n guardedWriteTool({\n name: 'apple_api_delete',\n namespace: 'apple',\n description: 'Delete a documented Apple Ads Platform API v1 campaign-management entity by relative path.',\n provider: 'apple',\n kind: 'remove',\n destructive: true,\n payload: z.object({ path: z.string().min(1) }),\n }),\n guardedWriteTool({\n name: 'apple_upload_asset',\n namespace: 'apple',\n description:\n 'Upload a PNG, JPG/JPEG, or HEIC creative asset for a Business Brand. The required SHA-256 binds the exact local file contents to the preview/apply approval.',\n provider: 'apple',\n kind: 'create',\n payload: z.object({\n file_path: z.string().min(1),\n expected_sha256: z.string().regex(/^[a-fA-F0-9]{64}$/),\n promoted_object_id: z.string().min(1),\n promoted_object_type: z.literal('BUSINESS_BRAND').default('BUSINESS_BRAND'),\n }),\n }),\n guardedWriteTool({\n name: 'apple_apply_recommendations',\n namespace: 'apple',\n description:\n 'Apply Apple Ads v1 daily-budget or target-CPA recommendations. The provider re-queries every recommendation and policy-checks the current and proposed Money values before apply.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n category: z.enum(['daily_budget', 'target_cpa']),\n promoted_object_id: z.string().min(1),\n promoted_object_type: z.enum(['APPSTORE_APP', 'BUSINESS_BRAND']),\n recommendations: z.array(z.object({\n id: z.string().min(1),\n applied_amount: z.number().positive().optional(),\n currency: z.string().length(3).optional(),\n })).min(1),\n }),\n }),\n guardedWriteTool({\n name: 'apple_dismiss_recommendations',\n namespace: 'apple',\n description: 'Dismiss Apple Ads v1 daily-budget or target-CPA recommendations through the normal preview/apply gate.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n category: z.enum(['daily_budget', 'target_cpa']),\n promoted_object_id: z.string().min(1),\n promoted_object_type: z.enum(['APPSTORE_APP', 'BUSINESS_BRAND']),\n recommendation_ids: z.array(z.string().min(1)).min(1),\n }),\n }),\n guardedWriteTool({\n name: 'apple_set_bid',\n namespace: 'apple',\n description:\n 'Change a v1 campaign, ad-group, or keyword bid after fetching the current Money value for policy enforcement.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n resource_type: z.enum(['campaign', 'ad_group', 'keyword']),\n resource_id: z.string().min(1),\n amount: z.number().positive(),\n currency: z.string().length(3).optional(),\n }),\n }),\n guardedWriteTool({\n name: 'apple_set_shared_budget',\n namespace: 'apple',\n description: 'Change an Apple Ads v1 shared-budget value after fetching its current Money value.',\n provider: 'apple',\n kind: 'update',\n payload: z.object({\n shared_budget_id: z.string().min(1),\n amount: z.number().positive(),\n currency: z.string().length(3).optional(),\n }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { AppleAdsClient, type AppleCredentials } from './client.js';\nimport { AppleAdsProvider } from './provider.js';\nimport { appleTools } from './tools.js';\n\nexport { AppleAdsClient, APPLE_ADS_BASE, formatAppleError, type AppleCredentials, type AppleEnvelope } from './client.js';\nexport { createClientSecret, type ClientSecretInput } from './jwt.js';\nexport { AppleAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { appleTools } from './tools.js';\n\nexport async function resolveAppleCredentials(store: CredentialStore): Promise<AppleCredentials | undefined> {\n const record = await store.get('apple');\n if (record?.data.client_id && record.data.team_id && record.data.key_id && record.data.private_key) {\n return {\n clientId: record.data.client_id,\n teamId: record.data.team_id,\n keyId: record.data.key_id,\n privateKeyPem: record.data.private_key,\n };\n }\n const env = process.env;\n if (env.APPLE_ADS_CLIENT_ID && env.APPLE_ADS_TEAM_ID && env.APPLE_ADS_KEY_ID && env.APPLE_ADS_PRIVATE_KEY) {\n return {\n clientId: env.APPLE_ADS_CLIENT_ID,\n teamId: env.APPLE_ADS_TEAM_ID,\n keyId: env.APPLE_ADS_KEY_ID,\n privateKeyPem: env.APPLE_ADS_PRIVATE_KEY,\n };\n }\n return undefined;\n}\n\nexport async function createAppleModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveAppleCredentials(store);\n if (!credentials) return undefined;\n const provider = new AppleAdsProvider(new AppleAdsClient(credentials));\n return { provider, tools: appleTools(provider) };\n}\n"],"mappings":";AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,kBAAkB,YAAY;AAevC,SAAS,OAAO,MAA+B;AAC7C,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,WAAW;AAC/C;AAOO,SAAS,mBAAmB,OAA0B,MAAM,oBAAI,KAAK,GAAW;AACrF,QAAM,MAAM,KAAK,MAAM,IAAI,QAAQ,IAAI,GAAI;AAC3C,QAAM,WAAW,KAAK,IAAI,MAAM,mBAAmB,MAAM,MAAM,QAAS,EAAE;AAC1E,QAAM,SAAS,EAAE,KAAK,SAAS,KAAK,MAAM,MAAM;AAChD,QAAM,UAAU;AAAA,IACd,KAAK,MAAM;AAAA,IACX;AAAA,IACA,KAAK,MAAM;AAAA,IACX,KAAK;AAAA,IACL,KAAK,MAAM;AAAA,EACb;AACA,QAAM,eAAe,GAAG,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC,IAAI,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AACzF,QAAM,MAAM,iBAAiB,MAAM,aAAa;AAEhD,QAAM,YAAY,KAAK,UAAU,OAAO,KAAK,YAAY,GAAG,EAAE,KAAK,aAAa,aAAa,CAAC;AAC9F,SAAO,GAAG,YAAY,IAAI,OAAO,SAAS,CAAC;AAC7C;;;AD7BA,IAAM,YAAY;AAGX,IAAM,iBAAiB;AAYvB,IAAM,iBAAN,MAAqB;AAAA,EAG1B,YACmB,aACA,YAA0B,OAC3C;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJX;AAAA,EAOR,MAAM,iBAAkC;AACtC,QAAI,KAAK,eAAe,KAAK,YAAY,YAAY,KAAK,IAAI,GAAG;AAC/D,aAAO,KAAK,YAAY;AAAA,IAC1B;AAEA,UAAM,eAAe,mBAAmB,KAAK,WAAW;AACxD,UAAM,WAAW,MAAM,KAAK,UAAU,WAAW;AAAA,MAC/C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D,MAAM,IAAI,gBAAgB;AAAA,QACxB,YAAY;AAAA,QACZ,WAAW,KAAK,YAAY;AAAA,QAC5B,eAAe;AAAA,QACf,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,UAAM,MAAM,MAAM,SAAS,KAAK;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,2BAA2B,SAAS,MAAM;AAAA,QAC1C,SAAS,GAAG;AAAA,MACd;AAAA,IACF;AACA,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAK,cAAc,EAAE,OAAO,KAAK,cAAc,WAAW,KAAK,IAAI,KAAK,KAAK,aAAa,MAAM,IAAK;AACrG,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAM,QACJ,QACA,MACA,UAAqE,CAAC,GAC3C;AAC3B,UAAM,QAAQ,MAAM,KAAK,eAAe;AACxC,QAAI,QAAQ,SAAS,UAAa,QAAQ,SAAS,QAAW;AAC5D,YAAM,IAAI,YAAY,iBAAiB,iEAAiE;AAAA,IAC1G;AACA,UAAM,WAAW,MAAM,KAAK,UAAU,GAAG,cAAc,IAAI,KAAK,QAAQ,OAAO,EAAE,CAAC,IAAI;AAAA,MACpF;AAAA,MACA,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,QAAQ;AAAA,QACR,GAAI,QAAQ,cAAc,EAAE,gBAAgB,eAAe,QAAQ,WAAW,GAAG,IAAI,CAAC;AAAA,QACtF,GAAI,QAAQ,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MAC7E;AAAA,MACA,MAAM,QAAQ,SAAS,QAAQ,SAAS,SAAY,SAAY,KAAK,UAAU,QAAQ,IAAI;AAAA,IAC7F,CAAC;AACD,UAAM,MAAM,MAAM,SAAS,KAAK;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,YAAY,kBAAkB,iBAAiB,SAAS,QAAQ,GAAG,GAAG,SAAS,GAAG,CAAC;AAAA,IAC/F;AACA,WAAQ,MAAM,KAAK,MAAM,GAAG,IAAI,EAAE,QAAQ,OAAU;AAAA,EACtD;AACF;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAiB,QAAgB,KAAqB;AACpE,QAAM,SAAS,SAAS,GAAG;AAG3B,QAAM,OAAO,6BAA6B,MAAM;AAChD,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ;AAAA,IACZ,MAAM,QAAQ,MAAM,UAAU,GAAG,MAAM,QAAQ,GAAG,KAAK,MAAM,WAAW,EAAE,KAAK;AAAA,IAC/E,IAAI,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,WAAW,GAAG,OAAO,QAAQ,GAAG,KAAK,OAAO,WAAW,EAAE,EAAE;AAAA,EAC3F,EAAE,OAAO,CAAC,SAAyB,QAAQ,IAAI,CAAC;AAChD,QAAM,OAAO,WAAW,MAAM,uFAAkF;AAChH,SAAO,MAAM,SAAS,IAAI,GAAG,IAAI;AAAA,IAAO,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;AACtF;;;AEjHA;AAAA,EACE,eAAAA;AAAA,EACA;AAAA,OAaK;AACP,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,UAAU,eAAe;AAI3B,IAAM,kBAAkB;AA6D/B,IAAM,uBAAuB;AAC7B,IAAM,wBAAwB;AAC9B,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAEpB,IAAM,mBAAN,MAA6C;AAAA,EAGlD,YAA6B,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAAzB;AAAA,EAFpB,KAAK;AAAA,EAId,eAAqC;AAEnC,WAAO,EAAE,cAAc,MAAM;AAAA,EAC/B;AAAA,EAEA,kBAAmC;AACjC,WAAO;AAAA,MACL,eAAe,CAAC,WAAW,gBAAgB;AAAA,QACzC,MAAM;AAAA,QACN,OAAO,EAAE,YAAY,WAAW,aAAa,YAAY,QAAQ,SAAS;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,eAAmC;AACvC,UAAM,WAAW,MAAM,KAAK,OAAO,QAEhC,OAAO,MAAM;AAChB,YAAQ,SAAS,QAAQ,QAAQ,CAAC,GAAG,QAAQ,CAAC,QAAS,IAAI,WAAW,OAAO,SAAY,CAAC,IAAI,CAAC;AAAA,MAC7F,UAAU,KAAK;AAAA,MACf,IAAI,OAAO,IAAI,UAAU,EAAE;AAAA,MAC3B,MAAM,IAAI,UAAU,QAAQ,OAAO,IAAI,UAAU,EAAE;AAAA,MACnD,QAAQ,IAAI,OAAO,KAAK,GAAG,KAAK;AAAA,IAClC,CAAC,CAAE;AAAA,EACL;AAAA,EAEA,MAAM,cAAc,aAAqB,QAAQ,KAA+B;AAC9E,UAAM,WAAW,MAAM,KAAK,OAAO,QAAyB,QAAQ,mBAAmB;AAAA,MACrF;AAAA,MACA,MAAM,EAAE,YAAY,EAAE,QAAQ,GAAG,UAAU,OAAO,iBAAiB,MAAM,EAAE;AAAA,IAC7E,CAAC;AACD,WAAO,SAAS,UAAU,CAAC;AAAA,EAC7B;AAAA,EAEA,MAAM,YAAY,aAAqB,YAA4C;AACjF,UAAM,WAAW,MAAM,KAAK,OAAO,QAAuB,OAAO,aAAa,UAAU,IAAI,EAAE,YAAY,CAAC;AAC3G,UAAM,WAAW,SAAS;AAC1B,QAAI,CAAC,UAAU;AACb,YAAM,IAAIA,aAAY,kBAAkB,mBAAmB,UAAU,4BAA4B,WAAW,EAAE;AAAA,IAChH;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,OAKO;AACnB,UAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,UAAM,UAAU,MAAM,WAAW,QAAQ,qBAAqB,KAAK,IAAI,IAAI,sBAAsB,KAAK,IAAI;AAC1G,QAAI,CAAC,SAAS;AACZ,YAAM,IAAIA,aAAY,iBAAiB,iCAAiC,IAAI,GAAG;AAAA,IACjF;AACA,UAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,MAAM,QAAQ,MAAM;AAAA,MAC7D,GAAI,MAAM,aAAa,EAAE,aAAa,MAAM,WAAW,IAAI,CAAC;AAAA,MAC5D,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IAC3C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,OAAyC;AACpD,QAAI,MAAM,UAAU,cAAc,MAAM,UAAU,WAAW;AAC3D,YAAM,IAAIA,aAAY,iBAAiB,0DAA0D;AAAA,IACnG;AACA,UAAM,QAAQ,iBAAiB,MAAM,SAAS;AAC9C,UAAM,aAAa,MAAM,eAAe,MAAM,KAAK,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE;AAClF,UAAM,OAAoB,CAAC;AAC3B,eAAW,eAAe,YAAY;AACpC,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,MAAM;AAAA,YACJ,YAAY,EAAE,QAAQ,GAAG,UAAU,KAAK,IAAI,MAAM,SAAS,KAAK,GAAI,EAAE;AAAA,YACtE,SAAS,CAAC,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;AAAA,YACvC,WAAW,EAAE,OAAO,MAAM,OAAO,KAAK,MAAM,KAAK,UAAU,MAAM;AAAA,YACjE,SAAS,EAAE,aAAa,CAAC,aAAa,EAAE;AAAA,UAC1C;AAAA,QACF;AAAA,MACF;AACA,YAAM,gBAAgB,SAAS,QAAQ,QAAQ,CAAC,GAAG;AAAA,QAAI,CAAC,QACtD,KAAK,YAAY,KAAK,aAAa,KAAK;AAAA,MAC1C;AACA,UAAI,MAAM,UAAU,WAAW;AAC7B,YAAI,aAAa,SAAS,EAAG,MAAK,KAAK,qBAAqB,cAAc,KAAK,IAAI,WAAW,CAAC;AAAA,MACjG,OAAO;AACL,aAAK,KAAK,GAAG,YAAY;AAAA,MAC3B;AAAA,IACF;AACA,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA,EAEQ,YAAY,KAAmB,aAAqB,OAAmC;AAC7F,UAAM,QAAQ,IAAI,gBAAgB,CAAC;AACnC,UAAM,QAAQ,OAAO,MAAM,YAAY,UAAU,CAAC;AAClD,UAAM,cAAc,MAAM,eAAe;AAEzC,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,cAAc,MAAM,iBAAiB;AAC3C,UAAM,MAAkC;AAAA,MACtC,OAAO,OAAO,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,KAAK,cAAc,IAAI,OAAQ,SAAS,cAAe,GAAG,IAAI;AAAA,MAC9D,KAAK,SAAS,IAAI,OAAO,QAAQ,MAAM,IAAI;AAAA,MAC3C,KAAK,cAAc,IAAI,OAAQ,QAAQ,cAAe,GAAI,IAAI;AAAA,MAC9D,KAAK,cAAc,IAAI,OAAO,QAAQ,WAAW,IAAI;AAAA,MACrD,MAAM;AAAA,IACR;AACA,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,WAAW;AAAA,MACX,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,IAAI,OAAO,IAAI,UAAU,MAAM,EAAE;AAAA,QACjC,MAAM,IAAI,UAAU,QAAQ;AAAA,QAC5B,QAAQ,IAAI,UAAU;AAAA,MACxB;AAAA,MACA,SAAS,OAAO,YAAY,MAAM,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,IAAoB,OAA0C;AAC/E,UAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK;AACtC,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,IAAoB,OAAyC;AAC5E,UAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK;AACtC,UAAM,cAAc,MAAM,KAAK,QAAQ;AACvC,WAAO,EAAE,SAAS,MAAM,YAAY;AAAA,EACtC;AAAA,EAEA,MAAc,KAAK,IAAoB,OAAuC;AAC5E,UAAM,UAAU,GAAG;AACnB,YAAQ,GAAG,MAAM;AAAA,MACf,KAAK;AACH,eAAO,KAAK,mBAAmB,GAAG,WAAW,SAAS,KAAK;AAAA,MAC7D,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,SAAS,KAAK;AAAA,MACxD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,cAAc,GAAG,WAAW,OAAO;AAAA,MACjD,KAAK;AACH,eAAO,KAAK,gBAAgB,GAAG,WAAW,OAAO;AAAA,MACnD,KAAK;AACH,eAAO,KAAK,yBAAyB,GAAG,WAAW,OAAO;AAAA,MAC5D,KAAK;AACH,eAAO,KAAK,2BAA2B,GAAG,WAAW,OAAO;AAAA,MAC9D,KAAK;AACH,eAAO,KAAK,WAAW,GAAG,WAAW,OAAO;AAAA,MAC9C,KAAK;AACH,eAAO,KAAK,oBAAoB,GAAG,WAAW,OAAO;AAAA,MACvD;AACE,cAAM,IAAIA,aAAY,kBAAkB,iCAAiC,GAAG,IAAI,EAAE;AAAA,IACtF;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,aACA,SAQA,OACoB;AACpB,UAAM,YAAsB,CAAC;AAC7B,QAAI,SAAS,QAAQ,UAAU;AAC/B,QAAI,MAAM,uBAAuB,WAAW,WAAW;AACrD,eAAS;AACT,gBAAU,KAAK,sDAAsD;AAAA,IACvE;AACA,UAAM,OAAO;AAAA,MACX,aAAa,OAAO,WAAW;AAAA,MAC/B,MAAM,QAAQ;AAAA,MACd,oBAAoB;AAAA,MACpB,kBAAkB,OAAO,QAAQ,OAAO;AAAA,MACxC,aAAa,EAAE,OAAO,EAAE,QAAQ,OAAO,QAAQ,YAAY,GAAG,UAAU,QAAQ,SAAS,EAAE;AAAA,MAC3F,cAAc;AAAA,MACd,aAAa,EAAE,iBAAiB,cAAc,iBAAiB,MAAM;AAAA,MACrE,WAAW;AAAA,QACT,iBAAiB,EAAE,SAAS,QAAQ,qBAAqB;AAAA,QACzD,iBAAiB,EAAE,SAAS,CAAC,yBAAyB,EAAE;AAAA,MAC1D;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS,8BAA8B,QAAQ,IAAI,MAAM,MAAM,yBAAoB,QAAQ,YAAY,IAAI,QAAQ,QAAQ;AAAA,MAC3H,SAAS;AAAA,QACP,eAAe,QAAQ,IAAI,SAAS,QAAQ,OAAO,aAAa,QAAQ,qBAAqB,KAAK,IAAI,CAAC,YAAY,MAAM;AAAA,MAC3H;AAAA,MACA;AAAA,MACA,cAAc;AAAA,QACZ,EAAE,QAAQ,iBAAiB,QAAQ,IAAI,kBAAkB,UAAU,QAAQ,eAAe,gBAAgB;AAAA,MAC5G;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,WAAW,MAAM,KAAK,OAAO,QAAuB,QAAQ,aAAa,EAAE,aAAa,KAAK,CAAC;AACpG,eAAO,SAAS,QAAQ,KAAK,CAAC,OAAO,SAAS,OAAO,EAAE,CAAC,IAAI,CAAC;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,aACA,SACoB;AACpB,UAAM,UAAU,MAAM,KAAK,YAAY,aAAa,QAAQ,WAAW;AACvE,WAAO;AAAA,MACL,SAAS,uBAAuB,QAAQ,IAAI,YAAY,QAAQ,MAAM,WAAM,QAAQ,MAAM;AAAA,MAC1F,SAAS,CAAC,cAAc,QAAQ,WAAW,WAAW,QAAQ,MAAM,WAAM,QAAQ,MAAM,EAAE;AAAA,MAC1F,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,aAAa,QAAQ,WAAW,IAAI;AAAA,UACnE;AAAA,UACA,MAAM,EAAE,QAAQ,QAAQ,OAAO;AAAA,QACjC,CAAC;AACD,eAAO,CAAC,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,aACA,SACoB;AACpB,UAAM,UAAU,MAAM,KAAK,YAAY,aAAa,QAAQ,WAAW;AACvE,UAAM,aAAa,QAAQ,aAAa,QAAQ,OAAO,QAAQ,YAAY,MAAM,MAAM,IAAI;AAC3F,UAAM,WAAW,QAAQ,aAAa,OAAO,YAAY;AACzD,WAAO;AAAA,MACL,SAAS,0BAA0B,QAAQ,IAAI,kBAAkB,cAAc,SAAS,WAAM,QAAQ,YAAY,IAAI,QAAQ;AAAA,MAC9H,SAAS,CAAC,cAAc,QAAQ,WAAW,sBAAsB,cAAc,SAAS,WAAM,QAAQ,YAAY,EAAE;AAAA,MACpH,WAAW,CAAC;AAAA,MACZ,cAAc;AAAA,QACZ;AAAA,UACE,QAAQ,aAAa,QAAQ,IAAI;AAAA,UACjC,GAAI,eAAe,SAAY,EAAE,YAAY,aAAa,gBAAgB,IAAI,CAAC;AAAA,UAC/E,UAAU,QAAQ,eAAe;AAAA,QACnC;AAAA,MACF;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,aAAa,QAAQ,WAAW,IAAI;AAAA,UACnE;AAAA,UACA,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,OAAO,QAAQ,YAAY,GAAG,SAAS,EAAE,EAAE;AAAA,QACrF,CAAC;AACD,eAAO,CAAC,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,aACA,SACA,OACoB;AACpB,UAAM,OAAO,qBAAqB,QAAQ,MAAM,QAAQ;AACxD,UAAM,OAAO,gBAAgB,QAAQ,IAAI;AACzC,UAAM,YAAsB,CAAC;AAC7B,QAAI,MAAM,oBAAqB,uBAAsB,MAAM,SAAS;AACpE,UAAM,eAAe,oBAAoB,IAAI;AAC7C,WAAO;AAAA,MACL,SAAS,uCAAuC,IAAI;AAAA,MACpD,SAAS,CAAC,WAAW,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,EAAE;AAAA,MACnD;AAAA,MACA;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,WAAW,MAAM,KAAK,OAAO,QAAiB,QAAQ,MAAM,EAAE,aAAa,KAAK,CAAC;AACvF,eAAO,mBAAmB,SAAS,MAAM;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,aACA,SACoB;AACpB,UAAM,OAAO,qBAAqB,QAAQ,MAAM,QAAQ;AACxD,QAAI,mBAAmB,QAAQ,IAAI,GAAG;AACpC,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS,sCAAsC,IAAI;AAAA,MACnD,SAAS,CAAC,UAAU,IAAI,IAAI,KAAK,UAAU,QAAQ,IAAI,CAAC,EAAE;AAAA,MAC1D,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,SAAS,KAAK,SAAS,cAAc,IAAI,SAAS;AACxD,cAAM,WAAW,MAAM,KAAK,OAAO,QAAiB,QAAQ,MAAM,EAAE,aAAa,MAAM,QAAQ,KAAK,CAAC;AACrG,eAAO,mBAAmB,SAAS,QAAQ,IAAI;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,aAAqB,SAA+C;AAC9F,UAAM,OAAO,qBAAqB,QAAQ,MAAM,QAAQ;AACxD,WAAO;AAAA,MACL,SAAS,yCAAyC,IAAI;AAAA,MACtD,SAAS,CAAC,aAAa,IAAI,EAAE;AAAA,MAC7B,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,UAAU,MAAM,EAAE,YAAY,CAAC;AACzD,eAAO,CAAC,KAAK,MAAM,GAAG,EAAE,GAAG,EAAE,CAAE;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,gBACZ,aACA,SAMoB;AACpB,UAAM,OAAO,MAAM,SAAS,QAAQ,SAAS;AAC7C,UAAM,eAAe,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACnE,QAAI,iBAAiB,QAAQ,gBAAgB,YAAY,GAAG;AAC1D,YAAM,IAAIA;AAAA,QACR;AAAA,QACA,qCAAqC,QAAQ,SAAS,cAAc,QAAQ,eAAe,SAAS,YAAY;AAAA,MAClH;AAAA,IACF;AACA,UAAM,cAAc,iBAAiB,QAAQ,SAAS;AACtD,WAAO;AAAA,MACL,SAAS,0BAA0B,SAAS,QAAQ,SAAS,CAAC,KAAK,KAAK,UAAU;AAAA,MAClF,SAAS,CAAC,kBAAkB,YAAY,mBAAmB,QAAQ,kBAAkB,EAAE;AAAA,MACvF,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,OAAO,IAAI,SAAS;AAC1B,aAAK,OAAO,QAAQ,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC,GAAG,SAAS,QAAQ,SAAS,CAAC;AACxF,aAAK,OAAO,oBAAoB,QAAQ,kBAAkB;AAC1D,aAAK,OAAO,sBAAsB,QAAQ,oBAAoB;AAC9D,cAAM,WAAW,MAAM,KAAK,OAAO,QAAiB,QAAQ,iBAAiB,EAAE,aAAa,KAAK,CAAC;AAClG,eAAO,mBAAmB,SAAS,MAAM;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,yBACZ,aACA,SAMoB;AACpB,UAAM,cAAc,QAAQ,aAAa,iBAAiB,kBAAkB;AAC5E,UAAM,WAAW,MAAM,KAAK,OAAO,QAA+B,QAAQ,mBAAmB,WAAW,UAAU;AAAA,MAChH;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,UACP,EAAE,OAAO,oBAAoB,UAAU,UAAU,OAAO,CAAC,QAAQ,kBAAkB,EAAE;AAAA,UACrF,EAAE,OAAO,sBAAsB,UAAU,UAAU,OAAO,CAAC,QAAQ,oBAAoB,EAAE;AAAA,QAC3F;AAAA,QACA,YAAY,EAAE,QAAQ,GAAG,UAAU,IAAI;AAAA,MACzC;AAAA,IACF,CAAC;AACD,UAAM,OAAO,IAAI,KAAK,SAAS,UAAU,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,eAAe,IAAI,cAAc,CAAC,CAAC;AACzG,UAAM,eAA6C,CAAC;AACpD,UAAM,cAAc,QAAQ,gBAAgB,IAAI,CAAC,SAAS;AACxD,YAAM,iBAAiB,KAAK,IAAI,KAAK,EAAE;AACvC,UAAI,CAAC,gBAAgB;AACnB,cAAM,IAAIA,aAAY,iBAAiB,yBAAyB,KAAK,EAAE,2CAA2C;AAAA,MACpH;AACA,YAAM,UAAU,QAAQ,aAAa,iBACjC,eAAe,cACf,eAAe,aAAa;AAChC,YAAM,YAAY,QAAQ,aAAa,iBACnC,eAAe,6BACf,eAAe;AACnB,YAAM,SAAS,KAAK,kBAAkB,OAAO,WAAW,MAAM;AAC9D,YAAM,WAAW,KAAK,YAAY,WAAW,YAAY,SAAS;AAClE,UAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,KAAK,CAAC,UAAU;AACxD,cAAM,IAAIA,aAAY,iBAAiB,yBAAyB,KAAK,EAAE,qCAAqC;AAAA,MAC9G;AACA,mBAAa,KAAK;AAAA,QAChB,QAAQ,GAAG,QAAQ,QAAQ,mBAAmB,KAAK,EAAE,GAAG,eAAe,eAAe,KAAK,eAAe,YAAY,MAAM,EAAE;AAAA,QAC9H,GAAI,SAAS,WAAW,SAAY,EAAE,YAAY,OAAO,QAAQ,MAAM,IAAI,gBAAgB,IAAI,CAAC;AAAA,QAChG,UAAU,SAAS;AAAA,MACrB,CAAC;AACD,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,kBAAkB,QAAQ;AAAA,QAC1B,oBAAoB,QAAQ;AAAA,QAC5B,GAAI,QAAQ,aAAa,iBACrB,EAAE,oBAAoB,EAAE,QAAQ,OAAO,MAAM,GAAG,SAAS,EAAE,IAC3D,EAAE,kBAAkB,EAAE,QAAQ,OAAO,MAAM,GAAG,SAAS,EAAE;AAAA,MAC/D;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL,SAAS,SAAS,YAAY,MAAM,cAAc,QAAQ,SAAS,QAAQ,KAAK,GAAG,CAAC;AAAA,MACpF,SAAS,YAAY,IAAI,CAAC,SAAS,0BAA0B,KAAK,EAAE,EAAE;AAAA,MACtE,WAAW,CAAC;AAAA,MACZ;AAAA,MACA,SAAS,YAAY;AACnB,cAAM,UAAU,MAAM,KAAK,OAAO,QAAmB,QAAQ,mBAAmB,WAAW,UAAU;AAAA,UACnG;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AACD,eAAO,mBAAmB,QAAQ,MAAM,EAAE,SAAS,IAC/C,mBAAmB,QAAQ,MAAM,IACjC,YAAY,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,2BACZ,aACA,SAMoB;AACpB,UAAM,cAAc,QAAQ,aAAa,iBAAiB,kBAAkB;AAC5E,UAAM,OAAO,QAAQ,mBAAmB,IAAI,CAAC,QAAQ;AAAA,MACnD;AAAA,MACA,kBAAkB,QAAQ;AAAA,MAC1B,oBAAoB,QAAQ;AAAA,IAC9B,EAAE;AACF,WAAO;AAAA,MACL,SAAS,WAAW,KAAK,MAAM,cAAc,QAAQ,SAAS,QAAQ,KAAK,GAAG,CAAC;AAAA,MAC/E,SAAS,KAAK,IAAI,CAAC,SAAS,4BAA4B,KAAK,EAAE,EAAE;AAAA,MACjE,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,MACf,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,QAAQ,mBAAmB,WAAW,YAAY,EAAE,aAAa,KAAK,CAAC;AACjG,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,aACA,SAMoB;AACpB,UAAM,aAAa,QAAQ,kBAAkB,aAAa,aAAa,GAAG,QAAQ,aAAa;AAC/F,UAAM,WAAW,MAAM,KAAK,OAAO,QAA+B,OAAO,GAAG,UAAU,IAAI,QAAQ,WAAW,IAAI;AAAA,MAC/G;AAAA,IACF,CAAC;AACD,UAAM,UAAU,SAAS;AACzB,QAAI,CAAC,QAAS,OAAM,IAAIA,aAAY,kBAAkB,UAAU,QAAQ,aAAa,IAAI,QAAQ,WAAW,YAAY;AACxH,UAAM,eAAe,QAAQ,kBAAkB,YAAY,QAAQ,MAAM,QAAQ,aAAa;AAC9F,UAAM,WAAW,QAAQ,YAAY,cAAc;AACnD,QAAI,CAAC,UAAU;AACb,YAAM,IAAIA,aAAY,iBAAiB,mEAAmE;AAAA,IAC5G;AACA,UAAM,QAAQ,EAAE,QAAQ,OAAO,QAAQ,MAAM,GAAG,SAAS;AACzD,UAAM,OAAO,QAAQ,kBAAkB,YACnC,EAAE,KAAK,MAAM,IACb,EAAE,aAAa,EAAE,GAAG,QAAQ,aAAa,KAAK,MAAM,EAAE;AAC1D,WAAO;AAAA,MACL,SAAS,gBAAgB,QAAQ,cAAc,QAAQ,KAAK,GAAG,CAAC,KAAK,QAAQ,QAAQ,QAAQ,WAAW,SAAS,cAAc,UAAU,SAAS,WAAM,QAAQ,MAAM,IAAI,QAAQ;AAAA,MAClL,SAAS,CAAC,KAAK,UAAU,IAAI,QAAQ,WAAW,QAAQ,cAAc,UAAU,SAAS,WAAM,QAAQ,MAAM,EAAE;AAAA,MAC/G,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,QACb,QAAQ,GAAG,QAAQ,aAAa,IAAI,QAAQ,WAAW;AAAA,QACvD,GAAI,cAAc,WAAW,SAAY,EAAE,YAAY,OAAO,aAAa,MAAM,IAAI,gBAAgB,IAAI,CAAC;AAAA,QAC1G,UAAU,QAAQ,SAAS;AAAA,MAC7B,CAAC;AAAA,MACD,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,GAAG,UAAU,IAAI,QAAQ,WAAW,IAAI,EAAE,aAAa,KAAK,CAAC;AAC9F,eAAO,CAAC,QAAQ,WAAW;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,oBACZ,aACA,SACoB;AACpB,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC;AAAA,MAAO,kBAAkB,QAAQ,gBAAgB;AAAA,MAAI,EAAE,YAAY;AAAA,IACrE;AACA,UAAM,UAAU,SAAS;AACzB,QAAI,CAAC,QAAS,OAAM,IAAIA,aAAY,kBAAkB,wBAAwB,QAAQ,gBAAgB,YAAY;AAClH,UAAM,WAAW,QAAQ,YAAY,QAAQ,OAAO;AACpD,QAAI,CAAC,SAAU,OAAM,IAAIA,aAAY,iBAAiB,0EAA0E;AAChI,WAAO;AAAA,MACL,SAAS,+BAA+B,QAAQ,QAAQ,QAAQ,gBAAgB,KAAK,QAAQ,OAAO,UAAU,SAAS,WAAM,QAAQ,MAAM,IAAI,QAAQ;AAAA,MACvJ,SAAS,CAAC,oBAAoB,QAAQ,gBAAgB,UAAU,QAAQ,OAAO,UAAU,SAAS,WAAM,QAAQ,MAAM,EAAE;AAAA,MACxH,WAAW,CAAC;AAAA,MACZ,cAAc,CAAC;AAAA,QACb,QAAQ,iBAAiB,QAAQ,gBAAgB;AAAA,QACjD,GAAI,QAAQ,OAAO,WAAW,SAAY,EAAE,YAAY,OAAO,QAAQ,MAAM,MAAM,IAAI,gBAAgB,IAAI,CAAC;AAAA,QAC5G,UAAU,QAAQ,SAAS;AAAA,MAC7B,CAAC;AAAA,MACD,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,QAAQ,OAAO,kBAAkB,QAAQ,gBAAgB,IAAI;AAAA,UAC7E;AAAA,UACA,MAAM,EAAE,OAAO,EAAE,QAAQ,OAAO,QAAQ,MAAM,GAAG,SAAS,EAAE;AAAA,QAC9D,CAAC;AACD,eAAO,CAAC,QAAQ,gBAAgB;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,UAAQ,QAAQ,IAAI,EAAE,YAAY,GAAG;AAAA,IACnC,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAA,IACL,KAAK;AAAS,aAAO;AAAA,IACrB,KAAK;AAAS,aAAO;AAAA,IACrB;AACE,YAAM,IAAIA,aAAY,iBAAiB,kDAAkD;AAAA,EAC7F;AACF;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,QAAM,aAAa,KAAK,QAAQ,QAAQ,EAAE,EAAE,KAAK;AACjD,MAAI,CAAC,cAAc,WAAW,SAAS,KAAK,KAAK,WAAW,SAAS,IAAI,KAAK,WAAW,SAAS,GAAG,GAAG;AACtG,UAAM,IAAIA,aAAY,iBAAiB,wEAAwE;AAAA,EACjH;AACA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAc,WAAmD;AAC7F,QAAM,aAAa,iBAAiB,IAAI;AACxC,QAAM,UAAU,cAAc,WAC1B,mBAAmB,KAAK,UAAU,IAClC,cAAc,WACZ,mBAAmB,KAAK,UAAU,IAClC,mBAAmB,KAAK,UAAU;AACxC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAIA,aAAY,iBAAiB,qCAAqC,UAAU,GAAG;AAAA,EAC3F;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,sBAAsB,OAAgB,WAAqB,OAAO,QAAc;AACvF,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,CAAC,MAAM,UAAU,sBAAsB,MAAM,WAAW,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC;AAC1F;AAAA,EACF;AACA,MAAI,CAAC,SAAS,KAAK,EAAG;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,SAAK,QAAQ,YAAY,QAAQ,oBAAoB,QAAQ,oBAAoB,UAAU,WAAW;AACpG,YAAM,GAAG,IAAI;AACb,gBAAU,KAAK,GAAG,IAAI,IAAI,GAAG,gDAAgD;AAAA,IAC/E,OAAO;AACL,4BAAsB,OAAO,WAAW,GAAG,IAAI,IAAI,GAAG,EAAE;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,SAAS,WAAW,KAAsB;AACxC,SAAO,8BAA8B,KAAK,GAAG;AAC/C;AAEA,SAAS,cAAc,OAAgF;AACrG,SAAO,SAAS,KAAK,MAAM,OAAO,MAAM,WAAW,YAAY,OAAO,MAAM,WAAW,cACpF,MAAM,aAAa,UAAa,OAAO,MAAM,aAAa;AAC/D;AAEA,SAAS,mBAAmB,OAAyB;AACnD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,kBAAkB;AAC9D,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,MAAI,cAAc,KAAK,EAAG,QAAO;AACjC,SAAO,OAAO,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,KAAK,MAAM,WAAW,GAAG,KAAK,mBAAmB,KAAK,CAAC;AAClG;AAEA,SAAS,oBAAoB,OAAgB,OAAO,QAAsC;AACxF,QAAM,SAAuC,CAAC;AAC9C,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,QAAQ,CAAC,MAAM,UAAU,OAAO,KAAK,GAAG,oBAAoB,MAAM,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC;AAC7F,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,MAAI,cAAc,KAAK,GAAG;AACxB,UAAM,SAAS,OAAO,MAAM,MAAM;AAClC,WAAO,OAAO,SAAS,MAAM,KAAK,UAAU,IACxC,CAAC,EAAE,QAAQ,MAAM,UAAU,KAAK,MAAM,SAAS,eAAe,EAAE,CAAC,IACjE,CAAC;AAAA,EACP;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAM,YAAY,GAAG,IAAI,IAAI,GAAG;AAChC,QAAI,WAAW,GAAG,KAAK,CAAC,cAAc,KAAK,GAAG;AAC5C,YAAM,SAAS,SAAS,KAAK,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,KAAK;AACpE,UAAI,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC1C,eAAO,KAAK,EAAE,QAAQ,WAAW,UAAU,KAAK,MAAM,SAAS,eAAe,EAAE,CAAC;AAAA,MACnF;AAAA,IACF;AACA,WAAO,KAAK,GAAG,oBAAoB,OAAO,SAAS,CAAC;AAAA,EACtD;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAgB,UAA6B;AACvE,QAAM,MAAgB,CAAC;AACvB,QAAM,QAAQ,CAAC,SAAkB;AAC/B,QAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,KAAK,QAAQ,KAAK;AAClD,QAAI,CAAC,SAAS,IAAI,EAAG;AACrB,QAAI,KAAK,OAAO,OAAW,KAAI,KAAK,OAAO,KAAK,EAAE,CAAC;AACnD,eAAW,SAAS,OAAO,OAAO,IAAI,EAAG,OAAM,KAAK;AAAA,EACtD;AACA,QAAM,KAAK;AACX,SAAO,IAAI,SAAS,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,IAAI,WAAW,CAAC,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,CAAE,IAAI,CAAC;AAC1F;AAEA,SAAS,qBAAqB,MAAmB,UAAkB,WAA8B;AAC/F,QAAM,UAAU,OAAO;AAAA,IACrB,OAAO,KAAK,KAAK,CAAC,EAAG,OAAO,EAAE,IAAI,CAAC,WAAW;AAAA,MAC5C;AAAA,MACA,OAAO,KAAK,OAAO,CAAC,KAAK,QAAQ,OAAO,IAAI,QAAQ,MAAoB,KAAK,IAAI,CAAC,CAAC;AAAA,IACrF,CAAC;AAAA,EACH;AACA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,kBAAkB,QAAQ,oBAAoB;AACpD,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAQ,SAAS,cAAe,GAAG,IAAI;AAC7F,MAAI,SAAS,QAAS,SAAQ,MAAM,SAAS,IAAI,OAAO,QAAQ,MAAM,IAAI;AAC1E,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAQ,QAAQ,cAAe,GAAI,IAAI;AAC7F,MAAI,SAAS,QAAS,SAAQ,MAAM,cAAc,IAAI,OAAO,QAAQ,WAAW,IAAI;AACpF,MAAI,UAAU,QAAS,SAAQ,OAAO,QAAQ,IAAI,OAAO,kBAAkB,KAAK,IAAI;AACpF,SAAO,EAAE,UAAU,WAAW,QAAQ,EAAE,OAAO,WAAW,IAAI,WAAW,MAAM,UAAU,GAAG,QAAQ;AACtG;AAEA,SAAS,OAAO,GAAmB;AACjC,SAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAC/B;;;ACrvBA,SAAS,YAAY,wBAAgD;AACrE,SAAS,SAAS;AAGlB,IAAM,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC;AAE1C,SAAS,WAAW,UAAiD;AAC1E,SAAO;AAAA,IACL,WAAW;AAAA,MACT,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,OAAO,EAAE,OAAO;AAAA,QACd,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6EAA6E;AAAA,QACxH,QAAQ,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,QAAQ,KAAK;AAAA,QAC7C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,+EAA+E;AAAA,QAChH,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,iEAAiE;AAAA,MAC/H,CAAC;AAAA,MACD,aAAa,EAAE,UAAU,KAAK;AAAA,MAC9B,MAAM,QAAQ,OAAO;AACnB,eAAO,SAAS,QAAQ,KAAK;AAAA,MAC/B;AAAA,IACF,CAAC;AAAA,IACD,WAAW;AAAA,MACT,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,OAAO,EAAE,OAAO;AAAA,QACd,YAAY,EAAE,OAAO,EAAE,SAAS,uCAAuC;AAAA,QACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAI,EAAE,QAAQ,GAAG;AAAA,MAC1D,CAAC;AAAA,MACD,aAAa,EAAE,UAAU,KAAK;AAAA,MAC9B,MAAM,QAAQ,OAAO;AACnB,cAAM,YAAY,MAAM,SAAS,cAAc,MAAM,YAAY,MAAM,KAAK;AAC5E,eAAO,EAAE,UAAU;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MAGF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,QACzE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC;AAAA,QACzD,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,QAClC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,4BAA4B;AAAA,QACpE,QAAQ,aAAa,SAAS;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAG,QAAQ,aAAa,CAAC;AAAA,IACrE,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAG,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,IACpF,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACtB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAAA,IAC/C,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QAC3B,iBAAiB,EAAE,OAAO,EAAE,MAAM,mBAAmB;AAAA,QACrD,oBAAoB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACpC,sBAAsB,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,gBAAgB;AAAA,MAC5E,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,UAAU,EAAE,KAAK,CAAC,gBAAgB,YAAY,CAAC;AAAA,QAC/C,oBAAoB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACpC,sBAAsB,EAAE,KAAK,CAAC,gBAAgB,gBAAgB,CAAC;AAAA,QAC/D,iBAAiB,EAAE,MAAM,EAAE,OAAO;AAAA,UAChC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,UACpB,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,UAC/C,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,QAC1C,CAAC,CAAC,EAAE,IAAI,CAAC;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,UAAU,EAAE,KAAK,CAAC,gBAAgB,YAAY,CAAC;AAAA,QAC/C,oBAAoB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACpC,sBAAsB,EAAE,KAAK,CAAC,gBAAgB,gBAAgB,CAAC;AAAA,QAC/D,oBAAoB,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC;AAAA,MACtD,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aACE;AAAA,MACF,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,eAAe,EAAE,KAAK,CAAC,YAAY,YAAY,SAAS,CAAC;AAAA,QACzD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QAC7B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,QAC5B,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,MAC1C,CAAC;AAAA,IACH,CAAC;AAAA,IACD,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,QAChB,kBAAkB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QAClC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,QAC5B,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,MAC1C,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;;;ACxKA,eAAsB,wBAAwB,OAA+D;AAC3G,QAAM,SAAS,MAAM,MAAM,IAAI,OAAO;AACtC,MAAI,QAAQ,KAAK,aAAa,OAAO,KAAK,WAAW,OAAO,KAAK,UAAU,OAAO,KAAK,aAAa;AAClG,WAAO;AAAA,MACL,UAAU,OAAO,KAAK;AAAA,MACtB,QAAQ,OAAO,KAAK;AAAA,MACpB,OAAO,OAAO,KAAK;AAAA,MACnB,eAAe,OAAO,KAAK;AAAA,IAC7B;AAAA,EACF;AACA,QAAM,MAAM,QAAQ;AACpB,MAAI,IAAI,uBAAuB,IAAI,qBAAqB,IAAI,oBAAoB,IAAI,uBAAuB;AACzG,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI;AAAA,MACX,eAAe,IAAI;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,kBAAkB,OAA6D;AACnG,QAAM,cAAc,MAAM,wBAAwB,KAAK;AACvD,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,WAAW,IAAI,iBAAiB,IAAI,eAAe,WAAW,CAAC;AACrE,SAAO,EAAE,UAAU,OAAO,WAAW,QAAQ,EAAE;AACjD;","names":["AdportError"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adport/provider-apple",
3
- "version": "0.4.0",
4
- "description": "Apple Ads (Apple Search Ads) provider for adport — Campaign Management API with ES256 OAuth",
3
+ "version": "0.5.1",
4
+ "description": "Apple Ads Platform API v1 provider for adport with ES256 OAuth",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "zod": "^4.4.3",
20
- "@adport/core": "0.4.0"
20
+ "@adport/core": "0.5.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^26.1.2",