@adport/provider-microsoft 0.3.0 → 0.4.0
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 +11 -1
- package/dist/index.js +205 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ declare class MicrosoftAdsClient {
|
|
|
31
31
|
readonly sandbox: boolean;
|
|
32
32
|
constructor(credentials: MicrosoftCredentials, onRefreshTokenRotated?: ((token: string) => Promise<void>) | undefined, fetchImpl?: typeof fetch);
|
|
33
33
|
getAccessToken(): Promise<string>;
|
|
34
|
-
request<T>(service: MicrosoftService, method: 'GET' | 'POST' | 'PUT', path: string, body: unknown, scope?: RequestScope): Promise<T>;
|
|
34
|
+
request<T>(service: MicrosoftService, method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body: unknown, scope?: RequestScope): Promise<T>;
|
|
35
35
|
/** Plain authorized download (report files); returns raw bytes. */
|
|
36
36
|
download(url: string): Promise<Uint8Array>;
|
|
37
37
|
}
|
|
@@ -78,12 +78,22 @@ declare class MicrosoftAdsProvider implements AdProvider {
|
|
|
78
78
|
listAccounts(): Promise<Account[]>;
|
|
79
79
|
private scopeFor;
|
|
80
80
|
listCampaigns(accountId: string): Promise<MicrosoftCampaign[]>;
|
|
81
|
+
apiRead(input: {
|
|
82
|
+
account_id: string;
|
|
83
|
+
service: MicrosoftService;
|
|
84
|
+
path: string;
|
|
85
|
+
body?: Record<string, unknown>;
|
|
86
|
+
}): Promise<unknown>;
|
|
81
87
|
report(query: NormalizedQuery): Promise<Report>;
|
|
82
88
|
private campaignReport;
|
|
83
89
|
private parseReport;
|
|
84
90
|
previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview>;
|
|
85
91
|
applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult>;
|
|
86
92
|
private plan;
|
|
93
|
+
private planApiCreate;
|
|
94
|
+
private planApiUpdate;
|
|
95
|
+
private planApiDelete;
|
|
96
|
+
private executeMicrosoftMutation;
|
|
87
97
|
private planCreateCampaign;
|
|
88
98
|
private lookupCampaign;
|
|
89
99
|
private updateCampaign;
|
package/dist/index.js
CHANGED
|
@@ -236,6 +236,12 @@ var MicrosoftAdsProvider = class {
|
|
|
236
236
|
);
|
|
237
237
|
return data.Campaigns ?? [];
|
|
238
238
|
}
|
|
239
|
+
async apiRead(input) {
|
|
240
|
+
const path = validateMicrosoftReadPath(input.path);
|
|
241
|
+
validateMicrosoftAccountScope(input.body, input.account_id);
|
|
242
|
+
const scope = await this.scopeFor(input.account_id);
|
|
243
|
+
return this.client.request(input.service, "POST", path, input.body ?? {}, scope);
|
|
244
|
+
}
|
|
239
245
|
async report(query) {
|
|
240
246
|
if (query.level !== "campaign" && query.level !== "account") {
|
|
241
247
|
throw new AdportError2("INVALID_INPUT", "microsoft: report supports campaign and account levels in v0");
|
|
@@ -244,7 +250,12 @@ var MicrosoftAdsProvider = class {
|
|
|
244
250
|
const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);
|
|
245
251
|
const rows = [];
|
|
246
252
|
for (const accountId of accountIds) {
|
|
247
|
-
|
|
253
|
+
const providerRows = await this.campaignReport(accountId, range.start, range.end, query);
|
|
254
|
+
if (query.level === "account") {
|
|
255
|
+
if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, accountId));
|
|
256
|
+
} else {
|
|
257
|
+
rows.push(...providerRows);
|
|
258
|
+
}
|
|
248
259
|
}
|
|
249
260
|
return { rows };
|
|
250
261
|
}
|
|
@@ -365,10 +376,62 @@ var MicrosoftAdsProvider = class {
|
|
|
365
376
|
return this.planSetStatus(op.accountId, payload);
|
|
366
377
|
case "microsoft_set_budget":
|
|
367
378
|
return this.planSetBudget(op.accountId, payload);
|
|
379
|
+
case "microsoft_api_create":
|
|
380
|
+
return this.planApiCreate(op.accountId, payload, guard);
|
|
381
|
+
case "microsoft_api_update":
|
|
382
|
+
return this.planApiUpdate(op.accountId, payload);
|
|
383
|
+
case "microsoft_api_delete":
|
|
384
|
+
return this.planApiDelete(op.accountId, payload);
|
|
368
385
|
default:
|
|
369
386
|
throw new AdportError2("PROVIDER_ERROR", `microsoft: unsupported write tool ${op.tool}`);
|
|
370
387
|
}
|
|
371
388
|
}
|
|
389
|
+
async planApiCreate(accountId, payload, guard) {
|
|
390
|
+
const resource = validateMicrosoftResource(payload.resource);
|
|
391
|
+
const body = structuredClone(payload.body);
|
|
392
|
+
validateMicrosoftAccountScope(body, accountId);
|
|
393
|
+
const coercions = [];
|
|
394
|
+
if (guard.forcePausedCreation) coerceMicrosoftCreatedStatuses(body, coercions);
|
|
395
|
+
return {
|
|
396
|
+
summary: `Create Microsoft Advertising ${resource} resource(s)`,
|
|
397
|
+
changes: [`+ POST /${resource} ${JSON.stringify(body)}`],
|
|
398
|
+
coercions,
|
|
399
|
+
budgetDeltas: collectMicrosoftBudgets(body),
|
|
400
|
+
execute: async () => this.executeMicrosoftMutation(accountId, "POST", resource, body)
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
async planApiUpdate(accountId, payload) {
|
|
404
|
+
const resource = validateMicrosoftResource(payload.resource);
|
|
405
|
+
validateMicrosoftAccountScope(payload.body, accountId);
|
|
406
|
+
if (containsMicrosoftBudget(payload.body)) {
|
|
407
|
+
throw new AdportError2("INVALID_INPUT", "microsoft: budget updates require microsoft_set_budget for policy checks");
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
summary: `Update Microsoft Advertising ${resource} resource(s)`,
|
|
411
|
+
changes: [`~ PUT /${resource} ${JSON.stringify(payload.body)}`],
|
|
412
|
+
coercions: [],
|
|
413
|
+
budgetDeltas: [],
|
|
414
|
+
execute: async () => this.executeMicrosoftMutation(accountId, "PUT", resource, payload.body)
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
async planApiDelete(accountId, payload) {
|
|
418
|
+
const resource = validateMicrosoftResource(payload.resource);
|
|
419
|
+
validateMicrosoftAccountScope(payload.body, accountId);
|
|
420
|
+
return {
|
|
421
|
+
summary: `Permanently delete Microsoft Advertising ${resource} resource(s)`,
|
|
422
|
+
changes: [`- DELETE /${resource} ${JSON.stringify(payload.body)}`],
|
|
423
|
+
coercions: [],
|
|
424
|
+
budgetDeltas: [],
|
|
425
|
+
execute: async () => this.executeMicrosoftMutation(accountId, "DELETE", resource, payload.body)
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
async executeMicrosoftMutation(accountId, method, resource, body) {
|
|
429
|
+
const scope = await this.scopeFor(accountId);
|
|
430
|
+
const result = await this.client.request("campaign", method, resource, body, scope);
|
|
431
|
+
const partial = formatPartialErrors(result.PartialErrors);
|
|
432
|
+
if (partial) throw new AdportError2("PROVIDER_ERROR", `microsoft: ${resource} partial errors \u2014 ${partial}`);
|
|
433
|
+
return extractMicrosoftIds(result);
|
|
434
|
+
}
|
|
372
435
|
async planCreateCampaign(accountId, payload, guard) {
|
|
373
436
|
const coercions = [];
|
|
374
437
|
let status = payload.status ?? "Paused";
|
|
@@ -453,6 +516,107 @@ var MicrosoftAdsProvider = class {
|
|
|
453
516
|
};
|
|
454
517
|
}
|
|
455
518
|
};
|
|
519
|
+
function aggregateAccountRows(rows, provider, accountId) {
|
|
520
|
+
const metrics = Object.fromEntries(
|
|
521
|
+
Object.keys(rows[0].metrics).map((metric) => [
|
|
522
|
+
metric,
|
|
523
|
+
round2(rows.reduce((sum, row) => sum + (row.metrics[metric] ?? 0), 0))
|
|
524
|
+
])
|
|
525
|
+
);
|
|
526
|
+
const spend = metrics.spend ?? 0;
|
|
527
|
+
const impressions = metrics.impressions ?? 0;
|
|
528
|
+
const clicks = metrics.clicks ?? 0;
|
|
529
|
+
const conversions = metrics.conversions ?? 0;
|
|
530
|
+
const conversionValue = metrics.conversion_value ?? 0;
|
|
531
|
+
if ("ctr" in metrics) metrics.ctr = impressions > 0 ? round2(clicks / impressions * 100) : 0;
|
|
532
|
+
if ("cpc" in metrics) metrics.cpc = clicks > 0 ? round2(spend / clicks) : 0;
|
|
533
|
+
if ("cpm" in metrics) metrics.cpm = impressions > 0 ? round2(spend / impressions * 1e3) : 0;
|
|
534
|
+
if ("cpa" in metrics) metrics.cpa = conversions > 0 ? round2(spend / conversions) : 0;
|
|
535
|
+
if ("roas" in metrics) metrics.roas = spend > 0 ? round2(conversionValue / spend) : 0;
|
|
536
|
+
return { provider, accountId, entity: { level: "account", id: accountId, name: accountId }, metrics };
|
|
537
|
+
}
|
|
538
|
+
function validateMicrosoftReadPath(path) {
|
|
539
|
+
const normalized = path.replace(/^\/+|\/+$/g, "").trim();
|
|
540
|
+
const action = normalized.split("/").at(-1) ?? "";
|
|
541
|
+
if (!/^[A-Za-z][A-Za-z0-9]*(?:\/[A-Za-z][A-Za-z0-9]*){1,2}$/.test(normalized) || !/^(?:Query|Search|Get|Submit|Poll)/.test(action)) {
|
|
542
|
+
throw new AdportError2("INVALID_INPUT", `microsoft: unsupported read operation path "${path}"`);
|
|
543
|
+
}
|
|
544
|
+
return normalized;
|
|
545
|
+
}
|
|
546
|
+
function validateMicrosoftResource(resource) {
|
|
547
|
+
const normalized = resource.trim();
|
|
548
|
+
if (!/^[A-Z][A-Za-z0-9]{1,80}$/.test(normalized)) {
|
|
549
|
+
throw new AdportError2("INVALID_INPUT", `microsoft: invalid Campaign Management resource "${resource}"`);
|
|
550
|
+
}
|
|
551
|
+
return normalized;
|
|
552
|
+
}
|
|
553
|
+
function isRecord(value) {
|
|
554
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
555
|
+
}
|
|
556
|
+
function validateMicrosoftAccountScope(value, accountId) {
|
|
557
|
+
if (Array.isArray(value)) return value.forEach((item) => validateMicrosoftAccountScope(item, accountId));
|
|
558
|
+
if (!isRecord(value)) return;
|
|
559
|
+
for (const [key, child] of Object.entries(value)) {
|
|
560
|
+
if (key === "AccountId" && String(child) !== accountId) {
|
|
561
|
+
throw new AdportError2("INVALID_INPUT", `microsoft: body AccountId ${String(child)} does not match selected account ${accountId}`);
|
|
562
|
+
}
|
|
563
|
+
if (key === "AccountIds" && Array.isArray(child) && child.some((id) => String(id) !== accountId)) {
|
|
564
|
+
throw new AdportError2("INVALID_INPUT", `microsoft: body AccountIds must contain only selected account ${accountId}`);
|
|
565
|
+
}
|
|
566
|
+
validateMicrosoftAccountScope(child, accountId);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function coerceMicrosoftCreatedStatuses(value, coercions, path = "body") {
|
|
570
|
+
if (Array.isArray(value)) return value.forEach((item, index) => coerceMicrosoftCreatedStatuses(item, coercions, `${path}[${index}]`));
|
|
571
|
+
if (!isRecord(value)) return;
|
|
572
|
+
for (const [key, child] of Object.entries(value)) {
|
|
573
|
+
if (key === "Status" && child === "Active") {
|
|
574
|
+
value[key] = "Paused";
|
|
575
|
+
coercions.push(`${path}.${key} coerced to Paused by policy (paused_creation)`);
|
|
576
|
+
} else coerceMicrosoftCreatedStatuses(child, coercions, `${path}.${key}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function containsMicrosoftBudget(value) {
|
|
580
|
+
if (Array.isArray(value)) return value.some(containsMicrosoftBudget);
|
|
581
|
+
if (!isRecord(value)) return false;
|
|
582
|
+
return Object.entries(value).some(([key, child]) => /budget/i.test(key) || containsMicrosoftBudget(child));
|
|
583
|
+
}
|
|
584
|
+
function collectMicrosoftBudgets(value, path = "body") {
|
|
585
|
+
const deltas = [];
|
|
586
|
+
if (Array.isArray(value)) {
|
|
587
|
+
value.forEach((item, index) => deltas.push(...collectMicrosoftBudgets(item, `${path}[${index}]`)));
|
|
588
|
+
return deltas;
|
|
589
|
+
}
|
|
590
|
+
if (!isRecord(value)) return deltas;
|
|
591
|
+
for (const [key, child] of Object.entries(value)) {
|
|
592
|
+
const childPath = `${path}.${key}`;
|
|
593
|
+
if (/budget/i.test(key) && !/type|id/i.test(key)) {
|
|
594
|
+
const amount = Number(child);
|
|
595
|
+
if (Number.isFinite(amount) && amount > 0) {
|
|
596
|
+
deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
deltas.push(...collectMicrosoftBudgets(child, childPath));
|
|
600
|
+
}
|
|
601
|
+
return deltas;
|
|
602
|
+
}
|
|
603
|
+
function extractMicrosoftIds(value) {
|
|
604
|
+
const ids = [];
|
|
605
|
+
const visit = (item) => {
|
|
606
|
+
if (Array.isArray(item)) return item.forEach(visit);
|
|
607
|
+
if (!isRecord(item)) return;
|
|
608
|
+
for (const [key, child] of Object.entries(item)) {
|
|
609
|
+
if (/Ids?$/.test(key)) {
|
|
610
|
+
if (Array.isArray(child)) child.forEach((id) => {
|
|
611
|
+
if (id !== null) ids.push(String(id));
|
|
612
|
+
});
|
|
613
|
+
else if (typeof child === "string" || typeof child === "number") ids.push(String(child));
|
|
614
|
+
} else visit(child);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
visit(value);
|
|
618
|
+
return [...new Set(ids)];
|
|
619
|
+
}
|
|
456
620
|
function round2(n) {
|
|
457
621
|
return Math.round(n * 100) / 100;
|
|
458
622
|
}
|
|
@@ -463,6 +627,21 @@ import { z } from "zod";
|
|
|
463
627
|
var statusSchema = z.enum(["Active", "Paused"]);
|
|
464
628
|
function microsoftTools(provider) {
|
|
465
629
|
return [
|
|
630
|
+
defineTool({
|
|
631
|
+
name: "microsoft_api_read",
|
|
632
|
+
namespace: "microsoft",
|
|
633
|
+
description: "Call any documented Microsoft Advertising v13 query, search, get, or reporting submit/poll operation with selected-account headers.",
|
|
634
|
+
input: z.object({
|
|
635
|
+
account_id: z.string(),
|
|
636
|
+
service: z.enum(["campaign", "customer", "reporting"]),
|
|
637
|
+
path: z.string().min(3).describe("REST operation path, e.g. AdGroups/QueryByCampaignId or GenerateReport/Submit"),
|
|
638
|
+
body: z.record(z.string(), z.unknown()).optional()
|
|
639
|
+
}),
|
|
640
|
+
annotations: { readOnly: true },
|
|
641
|
+
async handler(input) {
|
|
642
|
+
return provider.apiRead(input);
|
|
643
|
+
}
|
|
644
|
+
}),
|
|
466
645
|
defineTool({
|
|
467
646
|
name: "microsoft_campaigns",
|
|
468
647
|
namespace: "microsoft",
|
|
@@ -502,6 +681,31 @@ function microsoftTools(provider) {
|
|
|
502
681
|
provider: "microsoft",
|
|
503
682
|
kind: "update",
|
|
504
683
|
payload: z.object({ campaign_id: z.string(), daily_budget: z.number().positive() })
|
|
684
|
+
}),
|
|
685
|
+
guardedWriteTool({
|
|
686
|
+
name: "microsoft_api_create",
|
|
687
|
+
namespace: "microsoft",
|
|
688
|
+
description: "Create through any Microsoft Advertising Campaign Management v13 collection using the documented API body. Active statuses are forced to Paused and budget fields are policy-checked.",
|
|
689
|
+
provider: "microsoft",
|
|
690
|
+
kind: "create",
|
|
691
|
+
payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) })
|
|
692
|
+
}),
|
|
693
|
+
guardedWriteTool({
|
|
694
|
+
name: "microsoft_api_update",
|
|
695
|
+
namespace: "microsoft",
|
|
696
|
+
description: "Update non-budget fields through any Microsoft Advertising Campaign Management v13 collection.",
|
|
697
|
+
provider: "microsoft",
|
|
698
|
+
kind: "update",
|
|
699
|
+
payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) })
|
|
700
|
+
}),
|
|
701
|
+
guardedWriteTool({
|
|
702
|
+
name: "microsoft_api_delete",
|
|
703
|
+
namespace: "microsoft",
|
|
704
|
+
description: "Permanently delete through any Microsoft Advertising Campaign Management v13 collection.",
|
|
705
|
+
provider: "microsoft",
|
|
706
|
+
kind: "remove",
|
|
707
|
+
destructive: true,
|
|
708
|
+
payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) })
|
|
505
709
|
})
|
|
506
710
|
];
|
|
507
711
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/csv.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\nexport interface MicrosoftCredentials {\n developerToken: string;\n clientId: string;\n refreshToken: string;\n /** Only for confidential (web) apps; native/CLI public clients have no secret. */\n clientSecret?: string;\n sandbox?: boolean;\n}\n\nconst TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';\nexport const MSADS_SCOPE = 'https://ads.microsoft.com/msads.manage offline_access';\n/** Published universal sandbox developer token (anyone may use it in sandbox). */\nexport const SANDBOX_DEVELOPER_TOKEN = 'BBD37VB98';\n\nfunction hosts(sandbox: boolean) {\n const infix = sandbox ? '.sandbox' : '';\n return {\n campaign: `https://campaign.api${infix}.bingads.microsoft.com/CampaignManagement/v13`,\n customer: `https://clientcenter.api${infix}.bingads.microsoft.com/CustomerManagement/v13`,\n reporting: `https://reporting.api${infix}.bingads.microsoft.com/Reporting/v13`,\n };\n}\n\nexport type MicrosoftService = 'campaign' | 'customer' | 'reporting';\n\nexport interface RequestScope {\n /** Manager account (customer) id header. */\n customerId?: string;\n /** Ad account id header. */\n customerAccountId?: string;\n}\n\n/**\n * Microsoft Advertising REST v13 client. Public-client refresh tokens rotate —\n * onRefreshTokenRotated lets the credential store persist the replacement.\n */\nexport class MicrosoftAdsClient {\n private accessToken?: { token: string; expiresAt: number };\n private refreshToken: string;\n readonly sandbox: boolean;\n\n constructor(\n private readonly credentials: MicrosoftCredentials,\n private readonly onRefreshTokenRotated?: (token: string) => Promise<void>,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {\n this.refreshToken = credentials.refreshToken;\n this.sandbox = credentials.sandbox ?? false;\n }\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n const body = new URLSearchParams({\n client_id: this.credentials.clientId,\n scope: 'https://ads.microsoft.com/msads.manage',\n refresh_token: this.refreshToken,\n grant_type: 'refresh_token',\n });\n if (this.credentials.clientSecret) body.set('client_secret', this.credentials.clientSecret);\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Microsoft OAuth refresh failed (${response.status}). Public-client refresh tokens expire after ~90 days — re-run \\`adport connect microsoft\\`.`,\n safeJson(raw),\n );\n }\n const data = JSON.parse(raw) as { access_token: string; expires_in: number; refresh_token?: string };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n if (data.refresh_token && data.refresh_token !== this.refreshToken) {\n this.refreshToken = data.refresh_token;\n await this.onRefreshTokenRotated?.(data.refresh_token);\n }\n return this.accessToken.token;\n }\n\n async request<T>(\n service: MicrosoftService,\n method: 'GET' | 'POST' | 'PUT',\n path: string,\n body: unknown,\n scope: RequestScope = {},\n ): Promise<T> {\n const token = await this.getAccessToken();\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n DeveloperToken: this.sandbox ? SANDBOX_DEVELOPER_TOKEN : this.credentials.developerToken,\n 'content-type': 'application/json',\n };\n if (scope.customerId) headers.CustomerId = scope.customerId;\n if (scope.customerAccountId) headers.CustomerAccountId = scope.customerAccountId;\n const response = await this.fetchImpl(`${hosts(this.sandbox)[service]}/${path.replace(/^\\//, '')}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatMicrosoftError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : {}) as T;\n }\n\n /** Plain authorized download (report files); returns raw bytes. */\n async download(url: string): Promise<Uint8Array> {\n const response = await this.fetchImpl(url, {});\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', `Microsoft report download failed (HTTP ${response.status})`);\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\ninterface BatchErrorItem {\n Code?: number;\n ErrorCode?: string;\n Message?: string;\n FieldPath?: string;\n Index?: number;\n Details?: string;\n Detail?: string;\n}\n\n/**\n * REST errors are serialized fault objects: ApiFaultDetail\n * {OperationErrors, BatchErrors}, AdApiFaultDetail {Errors}, plus TrackingId.\n */\nexport function formatMicrosoftError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n TrackingId?: string;\n Type?: string;\n OperationErrors?: BatchErrorItem[];\n BatchErrors?: BatchErrorItem[];\n Errors?: BatchErrorItem[];\n };\n if (!parsed || typeof parsed !== 'object' || (!parsed.OperationErrors && !parsed.Errors && !parsed.BatchErrors)) {\n return `Microsoft Advertising API error (HTTP ${status})`;\n }\n const items = [...(parsed.OperationErrors ?? []), ...(parsed.BatchErrors ?? []), ...(parsed.Errors ?? [])];\n const lines = items.map((e) => {\n const where = e.Index !== undefined ? ` [item ${e.Index}${e.FieldPath ? ` ${e.FieldPath}` : ''}]` : '';\n return `${e.Code ?? '?'} ${e.ErrorCode ?? ''}: ${e.Message ?? e.Details ?? e.Detail ?? ''}${where}`;\n });\n let message = `Microsoft Advertising API error (HTTP ${status}, ${parsed.Type ?? 'fault'}):\\n ${lines.join('\\n ')}`;\n if (items.some((e) => e.Code === 105)) {\n message += '\\n Code 105 InvalidCredentials: check the developer token matches the environment (sandbox vs production).';\n }\n if (items.some((e) => e.Code === 117)) {\n message += '\\n Code 117 CallRateExceeded: wait 60s before retrying.';\n }\n if (parsed.TrackingId) message += ` [TrackingId: ${parsed.TrackingId}]`;\n return message;\n}\n\nexport function formatPartialErrors(partialErrors: BatchErrorItem[] | undefined): string | undefined {\n if (!partialErrors || partialErrors.length === 0) return undefined;\n return partialErrors\n .map((e) => `item ${e.Index ?? '?'}: ${e.Code ?? ''} ${e.ErrorCode ?? ''} ${e.Message ?? ''}${e.FieldPath ? ` (${e.FieldPath})` : ''}`)\n .join('; ');\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 { unzipSync } from 'fflate';\nimport { formatPartialErrors, MicrosoftAdsClient } from './client.js';\nimport { parseCsv } from './csv.js';\n\n/** DailyBudget is a double in whole account-currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\ninterface MicrosoftCampaign {\n Id: number | string;\n Name: string;\n Status: string;\n DailyBudget?: number;\n BudgetType?: string;\n BudgetId?: number | string | null;\n TimeZone?: string;\n CampaignType?: string;\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\nexport class MicrosoftAdsProvider implements AdProvider {\n readonly id = 'microsoft';\n /** accountId → parent customer id, learned from SearchAccounts. */\n private customerIds = new Map<string, string>();\n\n constructor(\n private readonly client: MicrosoftAdsClient,\n private readonly options: { pollIntervalMs?: number; pollMaxAttempts?: number } = {},\n ) {}\n\n capabilities(): ProviderCapabilities {\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'microsoft_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'Paused' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const user = await this.client.request<{ User?: { Id?: number | string } }>(\n 'customer',\n 'POST',\n 'User/Query',\n { UserId: null },\n );\n const userId = user.User?.Id;\n if (userId === undefined) {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: GetUser returned no user id');\n }\n const search = await this.client.request<{\n Accounts?: Array<{\n Id: number | string;\n Name?: string;\n Number?: string;\n CurrencyCode?: string;\n AccountLifeCycleStatus?: string;\n ParentCustomerId?: number | string;\n }>;\n }>('customer', 'POST', 'Accounts/Search', {\n Predicates: [{ Field: 'UserId', Operator: 'Equals', Value: String(userId) }],\n PageInfo: { Index: 0, Size: 100 },\n });\n const accounts = search.Accounts ?? [];\n for (const account of accounts) {\n if (account.ParentCustomerId !== undefined) {\n this.customerIds.set(String(account.Id), String(account.ParentCustomerId));\n }\n }\n return accounts.map((a) => ({\n provider: this.id,\n id: String(a.Id),\n name: a.Name ?? a.Number ?? String(a.Id),\n currency: a.CurrencyCode,\n status: a.AccountLifeCycleStatus,\n }));\n }\n\n private async scopeFor(accountId: string) {\n if (!this.customerIds.has(accountId)) await this.listAccounts();\n return { customerId: this.customerIds.get(accountId), customerAccountId: accountId };\n }\n\n async listCampaigns(accountId: string): Promise<MicrosoftCampaign[]> {\n const scope = await this.scopeFor(accountId);\n const data = await this.client.request<{ Campaigns?: MicrosoftCampaign[] | null }>(\n 'campaign',\n 'POST',\n 'Campaigns/QueryByAccountId',\n { AccountId: accountId, ReturnAdditionalFields: null },\n scope,\n );\n return data.Campaigns ?? [];\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n if (query.level !== 'campaign' && query.level !== 'account') {\n throw new AdportError('INVALID_INPUT', 'microsoft: 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 accountId of accountIds) {\n rows.push(...(await this.campaignReport(accountId, range.start, range.end, query)));\n }\n return { rows };\n }\n\n private async campaignReport(\n accountId: string,\n start: string,\n end: string,\n query: NormalizedQuery,\n ): Promise<ReportRow[]> {\n const scope = await this.scopeFor(accountId);\n const toDate = (iso: string) => {\n const [year, month, day] = iso.split('-').map(Number);\n return { Day: day, Month: month, Year: year };\n };\n // Async flow: Submit → Poll until Success → download zipped CSV.\n const submit = await this.client.request<{ ReportRequestId?: string }>(\n 'reporting',\n 'POST',\n 'GenerateReport/Submit',\n {\n ReportRequest: {\n Type: 'CampaignPerformanceReportRequest',\n Format: 'Csv',\n FormatVersion: '2.0',\n ReportName: 'adport campaign report',\n ExcludeReportHeader: true,\n ExcludeReportFooter: true,\n ExcludeColumnHeaders: false,\n ReturnOnlyCompleteData: false,\n Aggregation: 'Summary',\n Columns: ['CampaignId', 'CampaignName', 'CampaignStatus', 'Spend', 'Impressions', 'Clicks', 'Conversions', 'Revenue'],\n Scope: { AccountIds: [accountId] },\n Time: { CustomDateRangeStart: toDate(start), CustomDateRangeEnd: toDate(end) },\n },\n },\n scope,\n );\n if (!submit.ReportRequestId) {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: SubmitGenerateReport returned no ReportRequestId');\n }\n\n const maxAttempts = this.options.pollMaxAttempts ?? 30;\n const interval = this.options.pollIntervalMs ?? 5_000;\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const poll = await this.client.request<{\n ReportRequestStatus?: { Status?: string; ReportDownloadUrl?: string | null };\n }>('reporting', 'POST', 'GenerateReport/Poll', { ReportRequestId: submit.ReportRequestId }, scope);\n const status = poll.ReportRequestStatus?.Status;\n if (status === 'Error') {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: report generation failed (Status: Error)');\n }\n if (status === 'Success') {\n const url = poll.ReportRequestStatus?.ReportDownloadUrl;\n if (!url) return []; // Success with null URL = no data for the range.\n return this.parseReport(await this.client.download(url), accountId, query);\n }\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n throw new AdportError('PROVIDER_ERROR', `microsoft: report not ready after ${maxAttempts} polls`);\n }\n\n private parseReport(zipBytes: Uint8Array, accountId: string, query: NormalizedQuery): ReportRow[] {\n const files = unzipSync(zipBytes);\n const first = Object.values(files)[0];\n if (!first) return [];\n const rows = parseCsv(new TextDecoder().decode(first));\n if (rows.length < 2) return [];\n const header = rows[0]!;\n const index = (name: string) => header.indexOf(name);\n const out: ReportRow[] = [];\n for (const row of rows.slice(1)) {\n const value = (name: string) => {\n const i = index(name);\n return i >= 0 ? (row[i] ?? '') : '';\n };\n const spend = Number(value('Spend') || 0);\n const impressions = Number(value('Impressions') || 0);\n const clicks = Number(value('Clicks') || 0);\n const conversions = Number(value('Conversions') || 0);\n const revenue = Number(value('Revenue') || 0);\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(revenue),\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: spend > 0 ? round2(revenue / spend) : 0,\n };\n out.push({\n provider: this.id,\n accountId,\n entity: {\n level: 'campaign',\n id: value('CampaignId'),\n name: value('CampaignName'),\n status: value('CampaignStatus') || undefined,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n });\n }\n return out;\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 'microsoft_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'microsoft_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'microsoft_set_budget':\n return this.planSetBudget(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `microsoft: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planCreateCampaign(\n accountId: string,\n payload: { name: string; daily_budget: number; time_zone?: string; status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n // The API's own default status on Add is Paused; we only coerce an explicit Active.\n let status = payload.status ?? 'Paused';\n if (guard.forcePausedCreation && status === 'Active') {\n status = 'Paused';\n coercions.push('status coerced to Paused by policy (paused_creation)');\n }\n const campaign = {\n Name: payload.name,\n CampaignType: 'Search',\n DailyBudget: payload.daily_budget,\n BudgetType: 'DailyBudgetStandard',\n TimeZone: payload.time_zone ?? 'PacificTimeUSCanadaTijuana',\n Status: status,\n };\n return {\n summary: `Create Microsoft Advertising campaign \"${payload.name}\" (${status}), daily budget ${payload.daily_budget}`,\n changes: [`+ campaign \"${payload.name}\" type=Search daily_budget=${payload.daily_budget} status=${status}`],\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 scope = await this.scopeFor(accountId);\n const result = await this.client.request<{\n CampaignIds?: Array<number | string | null>;\n PartialErrors?: Array<{ Index?: number; Code?: number; ErrorCode?: string; Message?: string; FieldPath?: string }>;\n }>('campaign', 'POST', 'Campaigns', { AccountId: accountId, Campaigns: [campaign] }, scope);\n const partial = formatPartialErrors(result.PartialErrors);\n if (partial) throw new AdportError('PROVIDER_ERROR', `microsoft: AddCampaigns partial errors — ${partial}`);\n return (result.CampaignIds ?? []).filter((id): id is number | string => id !== null).map(String);\n },\n };\n }\n\n private async lookupCampaign(accountId: string, campaignId: string): Promise<MicrosoftCampaign> {\n const campaigns = await this.listCampaigns(accountId);\n const campaign = campaigns.find((c) => String(c.Id) === campaignId);\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `microsoft: campaign ${campaignId} not found in account ${accountId}`);\n }\n return campaign;\n }\n\n private async updateCampaign(\n accountId: string,\n fields: Record<string, unknown> & { Id: number | string },\n ): Promise<void> {\n const scope = await this.scopeFor(accountId);\n // UpdateCampaigns is PUT; omitted fields stay unchanged.\n const result = await this.client.request<{\n PartialErrors?: Array<{ Index?: number; Code?: number; ErrorCode?: string; Message?: string }>;\n }>('campaign', 'PUT', 'Campaigns', { AccountId: accountId, Campaigns: [fields] }, scope);\n const partial = formatPartialErrors(result.PartialErrors);\n if (partial) throw new AdportError('PROVIDER_ERROR', `microsoft: UpdateCampaigns partial errors — ${partial}`);\n }\n\n private async planSetStatus(\n accountId: string,\n payload: { campaign_id: string; status: 'Active' | 'Paused' },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(accountId, payload.campaign_id);\n return {\n summary: `Set Microsoft 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.updateCampaign(accountId, { Id: current.Id, Status: payload.status });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planSetBudget(\n accountId: string,\n payload: { campaign_id: string; daily_budget: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(accountId, payload.campaign_id);\n if (current.BudgetId && Number(current.BudgetId) > 0) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `microsoft: campaign \"${current.Name}\" uses a SHARED budget (BudgetId ${current.BudgetId}) — ` +\n 'its DailyBudget cannot be changed per-campaign (CampaignServiceCannotUpdateSharedBudget).',\n );\n }\n return {\n summary: `Change Microsoft campaign \"${current.Name}\" daily budget ${current.DailyBudget ?? '(unset)'} → ${payload.daily_budget}`,\n changes: [`~ campaign ${payload.campaign_id} DailyBudget ${current.DailyBudget ?? '(unset)'} → ${payload.daily_budget}`],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.Name}\" daily budget`,\n ...(current.DailyBudget !== undefined ? { fromMicros: current.DailyBudget * UNITS_TO_MICROS } : {}),\n toMicros: payload.daily_budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.updateCampaign(accountId, { Id: current.Id, DailyBudget: payload.daily_budget });\n return [payload.campaign_id];\n },\n };\n }\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/** Minimal RFC-4180-ish CSV parser (quotes, escaped quotes, CRLF). */\nexport function parseCsv(text: string): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = '';\n let inQuotes = false;\n for (let i = 0; i < text.length; i++) {\n const char = text[i]!;\n if (inQuotes) {\n if (char === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += char;\n }\n continue;\n }\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n row.push(field);\n field = '';\n } else if (char === '\\n' || char === '\\r') {\n if (char === '\\r' && text[i + 1] === '\\n') i++;\n row.push(field);\n field = '';\n if (row.length > 1 || row[0] !== '') rows.push(row);\n row = [];\n } else {\n field += char;\n }\n }\n if (field !== '' || row.length > 0) {\n row.push(field);\n if (row.length > 1 || row[0] !== '') rows.push(row);\n }\n return rows;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { MicrosoftAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['Active', 'Paused']);\n\nexport function microsoftTools(provider: MicrosoftAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'microsoft_campaigns',\n namespace: 'microsoft',\n description:\n 'List Microsoft Advertising campaigns (Id, Name, Status Active/Paused/BudgetPaused, DailyBudget in account currency, BudgetId when shared).',\n input: z.object({ account_id: z.string().describe('Ad account id') }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id);\n return { campaigns };\n },\n }),\n guardedWriteTool({\n name: 'microsoft_create_campaign',\n namespace: 'microsoft',\n description:\n 'Create a Microsoft Advertising Search campaign. daily_budget is a float in account currency. ' +\n \"The API's own default status is Paused. No server dry-run — previews are client-side diffs.\",\n provider: 'microsoft',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1).max(128),\n daily_budget: z.number().positive(),\n time_zone: z.string().optional().describe('Bing Ads time zone name, default PacificTimeUSCanadaTijuana'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'microsoft_set_campaign_status',\n namespace: 'microsoft',\n description: 'Activate or pause a Microsoft Advertising campaign.',\n provider: 'microsoft',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'microsoft_set_budget',\n namespace: 'microsoft',\n description:\n \"Change a Microsoft campaign's DailyBudget (float, account currency). Fails clearly for shared budgets.\",\n provider: 'microsoft',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget: z.number().positive() }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { MicrosoftAdsClient, type MicrosoftCredentials } from './client.js';\nimport { MicrosoftAdsProvider } from './provider.js';\nimport { microsoftTools } from './tools.js';\n\nexport {\n MicrosoftAdsClient,\n MSADS_SCOPE,\n SANDBOX_DEVELOPER_TOKEN,\n formatMicrosoftError,\n formatPartialErrors,\n type MicrosoftCredentials,\n} from './client.js';\nexport { MicrosoftAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { microsoftTools } from './tools.js';\nexport { parseCsv } from './csv.js';\n\nexport async function resolveMicrosoftCredentials(store: CredentialStore): Promise<MicrosoftCredentials | undefined> {\n const record = await store.get('microsoft');\n if (record?.data.developer_token && record.data.client_id && record.data.refresh_token) {\n return {\n developerToken: record.data.developer_token,\n clientId: record.data.client_id,\n refreshToken: record.data.refresh_token,\n clientSecret: record.data.client_secret || undefined,\n sandbox: record.data.sandbox === 'true',\n };\n }\n const env = process.env;\n if (env.MICROSOFT_ADS_DEVELOPER_TOKEN && env.MICROSOFT_ADS_CLIENT_ID && env.MICROSOFT_ADS_REFRESH_TOKEN) {\n return {\n developerToken: env.MICROSOFT_ADS_DEVELOPER_TOKEN,\n clientId: env.MICROSOFT_ADS_CLIENT_ID,\n refreshToken: env.MICROSOFT_ADS_REFRESH_TOKEN,\n clientSecret: env.MICROSOFT_ADS_CLIENT_SECRET || undefined,\n sandbox: env.MICROSOFT_ADS_SANDBOX === 'true',\n };\n }\n return undefined;\n}\n\nexport async function createMicrosoftModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveMicrosoftCredentials(store);\n if (!credentials) return undefined;\n const client = new MicrosoftAdsClient(credentials, async (rotated) => {\n // Public-client refresh tokens rotate; persist the replacement immediately.\n const record = await store.get('microsoft');\n if (record) {\n await store.set({ ...record, data: { ...record.data, refresh_token: rotated } });\n }\n });\n const provider = new MicrosoftAdsProvider(client);\n return { provider, tools: microsoftTools(provider) };\n}\n"],"mappings":"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","names":["AdportError","AdportError"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/csv.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\nexport interface MicrosoftCredentials {\n developerToken: string;\n clientId: string;\n refreshToken: string;\n /** Only for confidential (web) apps; native/CLI public clients have no secret. */\n clientSecret?: string;\n sandbox?: boolean;\n}\n\nconst TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';\nexport const MSADS_SCOPE = 'https://ads.microsoft.com/msads.manage offline_access';\n/** Published universal sandbox developer token (anyone may use it in sandbox). */\nexport const SANDBOX_DEVELOPER_TOKEN = 'BBD37VB98';\n\nfunction hosts(sandbox: boolean) {\n const infix = sandbox ? '.sandbox' : '';\n return {\n campaign: `https://campaign.api${infix}.bingads.microsoft.com/CampaignManagement/v13`,\n customer: `https://clientcenter.api${infix}.bingads.microsoft.com/CustomerManagement/v13`,\n reporting: `https://reporting.api${infix}.bingads.microsoft.com/Reporting/v13`,\n };\n}\n\nexport type MicrosoftService = 'campaign' | 'customer' | 'reporting';\n\nexport interface RequestScope {\n /** Manager account (customer) id header. */\n customerId?: string;\n /** Ad account id header. */\n customerAccountId?: string;\n}\n\n/**\n * Microsoft Advertising REST v13 client. Public-client refresh tokens rotate —\n * onRefreshTokenRotated lets the credential store persist the replacement.\n */\nexport class MicrosoftAdsClient {\n private accessToken?: { token: string; expiresAt: number };\n private refreshToken: string;\n readonly sandbox: boolean;\n\n constructor(\n private readonly credentials: MicrosoftCredentials,\n private readonly onRefreshTokenRotated?: (token: string) => Promise<void>,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {\n this.refreshToken = credentials.refreshToken;\n this.sandbox = credentials.sandbox ?? false;\n }\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n const body = new URLSearchParams({\n client_id: this.credentials.clientId,\n scope: 'https://ads.microsoft.com/msads.manage',\n refresh_token: this.refreshToken,\n grant_type: 'refresh_token',\n });\n if (this.credentials.clientSecret) body.set('client_secret', this.credentials.clientSecret);\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Microsoft OAuth refresh failed (${response.status}). Public-client refresh tokens expire after ~90 days — re-run \\`adport connect microsoft\\`.`,\n safeJson(raw),\n );\n }\n const data = JSON.parse(raw) as { access_token: string; expires_in: number; refresh_token?: string };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n if (data.refresh_token && data.refresh_token !== this.refreshToken) {\n this.refreshToken = data.refresh_token;\n await this.onRefreshTokenRotated?.(data.refresh_token);\n }\n return this.accessToken.token;\n }\n\n async request<T>(\n service: MicrosoftService,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body: unknown,\n scope: RequestScope = {},\n ): Promise<T> {\n const token = await this.getAccessToken();\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n DeveloperToken: this.sandbox ? SANDBOX_DEVELOPER_TOKEN : this.credentials.developerToken,\n 'content-type': 'application/json',\n };\n if (scope.customerId) headers.CustomerId = scope.customerId;\n if (scope.customerAccountId) headers.CustomerAccountId = scope.customerAccountId;\n const response = await this.fetchImpl(`${hosts(this.sandbox)[service]}/${path.replace(/^\\//, '')}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatMicrosoftError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : {}) as T;\n }\n\n /** Plain authorized download (report files); returns raw bytes. */\n async download(url: string): Promise<Uint8Array> {\n const response = await this.fetchImpl(url, {});\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', `Microsoft report download failed (HTTP ${response.status})`);\n }\n return new Uint8Array(await response.arrayBuffer());\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\ninterface BatchErrorItem {\n Code?: number;\n ErrorCode?: string;\n Message?: string;\n FieldPath?: string;\n Index?: number;\n Details?: string;\n Detail?: string;\n}\n\n/**\n * REST errors are serialized fault objects: ApiFaultDetail\n * {OperationErrors, BatchErrors}, AdApiFaultDetail {Errors}, plus TrackingId.\n */\nexport function formatMicrosoftError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n TrackingId?: string;\n Type?: string;\n OperationErrors?: BatchErrorItem[];\n BatchErrors?: BatchErrorItem[];\n Errors?: BatchErrorItem[];\n };\n if (!parsed || typeof parsed !== 'object' || (!parsed.OperationErrors && !parsed.Errors && !parsed.BatchErrors)) {\n return `Microsoft Advertising API error (HTTP ${status})`;\n }\n const items = [...(parsed.OperationErrors ?? []), ...(parsed.BatchErrors ?? []), ...(parsed.Errors ?? [])];\n const lines = items.map((e) => {\n const where = e.Index !== undefined ? ` [item ${e.Index}${e.FieldPath ? ` ${e.FieldPath}` : ''}]` : '';\n return `${e.Code ?? '?'} ${e.ErrorCode ?? ''}: ${e.Message ?? e.Details ?? e.Detail ?? ''}${where}`;\n });\n let message = `Microsoft Advertising API error (HTTP ${status}, ${parsed.Type ?? 'fault'}):\\n ${lines.join('\\n ')}`;\n if (items.some((e) => e.Code === 105)) {\n message += '\\n Code 105 InvalidCredentials: check the developer token matches the environment (sandbox vs production).';\n }\n if (items.some((e) => e.Code === 117)) {\n message += '\\n Code 117 CallRateExceeded: wait 60s before retrying.';\n }\n if (parsed.TrackingId) message += ` [TrackingId: ${parsed.TrackingId}]`;\n return message;\n}\n\nexport function formatPartialErrors(partialErrors: BatchErrorItem[] | undefined): string | undefined {\n if (!partialErrors || partialErrors.length === 0) return undefined;\n return partialErrors\n .map((e) => `item ${e.Index ?? '?'}: ${e.Code ?? ''} ${e.ErrorCode ?? ''} ${e.Message ?? ''}${e.FieldPath ? ` (${e.FieldPath})` : ''}`)\n .join('; ');\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 { unzipSync } from 'fflate';\nimport { formatPartialErrors, MicrosoftAdsClient, type MicrosoftService } from './client.js';\nimport { parseCsv } from './csv.js';\n\n/** DailyBudget is a double in whole account-currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\ninterface MicrosoftCampaign {\n Id: number | string;\n Name: string;\n Status: string;\n DailyBudget?: number;\n BudgetType?: string;\n BudgetId?: number | string | null;\n TimeZone?: string;\n CampaignType?: string;\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\nexport class MicrosoftAdsProvider implements AdProvider {\n readonly id = 'microsoft';\n /** accountId → parent customer id, learned from SearchAccounts. */\n private customerIds = new Map<string, string>();\n\n constructor(\n private readonly client: MicrosoftAdsClient,\n private readonly options: { pollIntervalMs?: number; pollMaxAttempts?: number } = {},\n ) {}\n\n capabilities(): ProviderCapabilities {\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'microsoft_set_campaign_status',\n input: { account_id: accountId, campaign_id: campaignId, status: 'Paused' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const user = await this.client.request<{ User?: { Id?: number | string } }>(\n 'customer',\n 'POST',\n 'User/Query',\n { UserId: null },\n );\n const userId = user.User?.Id;\n if (userId === undefined) {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: GetUser returned no user id');\n }\n const search = await this.client.request<{\n Accounts?: Array<{\n Id: number | string;\n Name?: string;\n Number?: string;\n CurrencyCode?: string;\n AccountLifeCycleStatus?: string;\n ParentCustomerId?: number | string;\n }>;\n }>('customer', 'POST', 'Accounts/Search', {\n Predicates: [{ Field: 'UserId', Operator: 'Equals', Value: String(userId) }],\n PageInfo: { Index: 0, Size: 100 },\n });\n const accounts = search.Accounts ?? [];\n for (const account of accounts) {\n if (account.ParentCustomerId !== undefined) {\n this.customerIds.set(String(account.Id), String(account.ParentCustomerId));\n }\n }\n return accounts.map((a) => ({\n provider: this.id,\n id: String(a.Id),\n name: a.Name ?? a.Number ?? String(a.Id),\n currency: a.CurrencyCode,\n status: a.AccountLifeCycleStatus,\n }));\n }\n\n private async scopeFor(accountId: string) {\n if (!this.customerIds.has(accountId)) await this.listAccounts();\n return { customerId: this.customerIds.get(accountId), customerAccountId: accountId };\n }\n\n async listCampaigns(accountId: string): Promise<MicrosoftCampaign[]> {\n const scope = await this.scopeFor(accountId);\n const data = await this.client.request<{ Campaigns?: MicrosoftCampaign[] | null }>(\n 'campaign',\n 'POST',\n 'Campaigns/QueryByAccountId',\n { AccountId: accountId, ReturnAdditionalFields: null },\n scope,\n );\n return data.Campaigns ?? [];\n }\n\n async apiRead(input: {\n account_id: string;\n service: MicrosoftService;\n path: string;\n body?: Record<string, unknown>;\n }): Promise<unknown> {\n const path = validateMicrosoftReadPath(input.path);\n validateMicrosoftAccountScope(input.body, input.account_id);\n const scope = await this.scopeFor(input.account_id);\n return this.client.request(input.service, 'POST', path, input.body ?? {}, scope);\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n if (query.level !== 'campaign' && query.level !== 'account') {\n throw new AdportError('INVALID_INPUT', 'microsoft: 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 accountId of accountIds) {\n const providerRows = await this.campaignReport(accountId, range.start, range.end, query);\n if (query.level === 'account') {\n if (providerRows.length > 0) rows.push(aggregateAccountRows(providerRows, this.id, accountId));\n } else {\n rows.push(...providerRows);\n }\n }\n return { rows };\n }\n\n private async campaignReport(\n accountId: string,\n start: string,\n end: string,\n query: NormalizedQuery,\n ): Promise<ReportRow[]> {\n const scope = await this.scopeFor(accountId);\n const toDate = (iso: string) => {\n const [year, month, day] = iso.split('-').map(Number);\n return { Day: day, Month: month, Year: year };\n };\n // Async flow: Submit → Poll until Success → download zipped CSV.\n const submit = await this.client.request<{ ReportRequestId?: string }>(\n 'reporting',\n 'POST',\n 'GenerateReport/Submit',\n {\n ReportRequest: {\n Type: 'CampaignPerformanceReportRequest',\n Format: 'Csv',\n FormatVersion: '2.0',\n ReportName: 'adport campaign report',\n ExcludeReportHeader: true,\n ExcludeReportFooter: true,\n ExcludeColumnHeaders: false,\n ReturnOnlyCompleteData: false,\n Aggregation: 'Summary',\n Columns: ['CampaignId', 'CampaignName', 'CampaignStatus', 'Spend', 'Impressions', 'Clicks', 'Conversions', 'Revenue'],\n Scope: { AccountIds: [accountId] },\n Time: { CustomDateRangeStart: toDate(start), CustomDateRangeEnd: toDate(end) },\n },\n },\n scope,\n );\n if (!submit.ReportRequestId) {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: SubmitGenerateReport returned no ReportRequestId');\n }\n\n const maxAttempts = this.options.pollMaxAttempts ?? 30;\n const interval = this.options.pollIntervalMs ?? 5_000;\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const poll = await this.client.request<{\n ReportRequestStatus?: { Status?: string; ReportDownloadUrl?: string | null };\n }>('reporting', 'POST', 'GenerateReport/Poll', { ReportRequestId: submit.ReportRequestId }, scope);\n const status = poll.ReportRequestStatus?.Status;\n if (status === 'Error') {\n throw new AdportError('PROVIDER_ERROR', 'microsoft: report generation failed (Status: Error)');\n }\n if (status === 'Success') {\n const url = poll.ReportRequestStatus?.ReportDownloadUrl;\n if (!url) return []; // Success with null URL = no data for the range.\n return this.parseReport(await this.client.download(url), accountId, query);\n }\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n throw new AdportError('PROVIDER_ERROR', `microsoft: report not ready after ${maxAttempts} polls`);\n }\n\n private parseReport(zipBytes: Uint8Array, accountId: string, query: NormalizedQuery): ReportRow[] {\n const files = unzipSync(zipBytes);\n const first = Object.values(files)[0];\n if (!first) return [];\n const rows = parseCsv(new TextDecoder().decode(first));\n if (rows.length < 2) return [];\n const header = rows[0]!;\n const index = (name: string) => header.indexOf(name);\n const out: ReportRow[] = [];\n for (const row of rows.slice(1)) {\n const value = (name: string) => {\n const i = index(name);\n return i >= 0 ? (row[i] ?? '') : '';\n };\n const spend = Number(value('Spend') || 0);\n const impressions = Number(value('Impressions') || 0);\n const clicks = Number(value('Clicks') || 0);\n const conversions = Number(value('Conversions') || 0);\n const revenue = Number(value('Revenue') || 0);\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(revenue),\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: spend > 0 ? round2(revenue / spend) : 0,\n };\n out.push({\n provider: this.id,\n accountId,\n entity: {\n level: 'campaign',\n id: value('CampaignId'),\n name: value('CampaignName'),\n status: value('CampaignStatus') || undefined,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n });\n }\n return out;\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 'microsoft_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'microsoft_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'microsoft_set_budget':\n return this.planSetBudget(op.accountId, payload);\n case 'microsoft_api_create':\n return this.planApiCreate(op.accountId, payload, guard);\n case 'microsoft_api_update':\n return this.planApiUpdate(op.accountId, payload);\n case 'microsoft_api_delete':\n return this.planApiDelete(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `microsoft: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planApiCreate(\n accountId: string,\n payload: { resource: string; body: Record<string, unknown> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const resource = validateMicrosoftResource(payload.resource);\n const body = structuredClone(payload.body);\n validateMicrosoftAccountScope(body, accountId);\n const coercions: string[] = [];\n if (guard.forcePausedCreation) coerceMicrosoftCreatedStatuses(body, coercions);\n return {\n summary: `Create Microsoft Advertising ${resource} resource(s)`,\n changes: [`+ POST /${resource} ${JSON.stringify(body)}`],\n coercions,\n budgetDeltas: collectMicrosoftBudgets(body),\n execute: async () => this.executeMicrosoftMutation(accountId, 'POST', resource, body),\n };\n }\n\n private async planApiUpdate(\n accountId: string,\n payload: { resource: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const resource = validateMicrosoftResource(payload.resource);\n validateMicrosoftAccountScope(payload.body, accountId);\n if (containsMicrosoftBudget(payload.body)) {\n throw new AdportError('INVALID_INPUT', 'microsoft: budget updates require microsoft_set_budget for policy checks');\n }\n return {\n summary: `Update Microsoft Advertising ${resource} resource(s)`,\n changes: [`~ PUT /${resource} ${JSON.stringify(payload.body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => this.executeMicrosoftMutation(accountId, 'PUT', resource, payload.body),\n };\n }\n\n private async planApiDelete(\n accountId: string,\n payload: { resource: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const resource = validateMicrosoftResource(payload.resource);\n validateMicrosoftAccountScope(payload.body, accountId);\n return {\n summary: `Permanently delete Microsoft Advertising ${resource} resource(s)`,\n changes: [`- DELETE /${resource} ${JSON.stringify(payload.body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => this.executeMicrosoftMutation(accountId, 'DELETE', resource, payload.body),\n };\n }\n\n private async executeMicrosoftMutation(\n accountId: string,\n method: 'POST' | 'PUT' | 'DELETE',\n resource: string,\n body: Record<string, unknown>,\n ): Promise<string[]> {\n const scope = await this.scopeFor(accountId);\n const result = await this.client.request<Record<string, unknown>>('campaign', method, resource, body, scope);\n const partial = formatPartialErrors(result.PartialErrors as Parameters<typeof formatPartialErrors>[0]);\n if (partial) throw new AdportError('PROVIDER_ERROR', `microsoft: ${resource} partial errors — ${partial}`);\n return extractMicrosoftIds(result);\n }\n\n private async planCreateCampaign(\n accountId: string,\n payload: { name: string; daily_budget: number; time_zone?: string; status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n // The API's own default status on Add is Paused; we only coerce an explicit Active.\n let status = payload.status ?? 'Paused';\n if (guard.forcePausedCreation && status === 'Active') {\n status = 'Paused';\n coercions.push('status coerced to Paused by policy (paused_creation)');\n }\n const campaign = {\n Name: payload.name,\n CampaignType: 'Search',\n DailyBudget: payload.daily_budget,\n BudgetType: 'DailyBudgetStandard',\n TimeZone: payload.time_zone ?? 'PacificTimeUSCanadaTijuana',\n Status: status,\n };\n return {\n summary: `Create Microsoft Advertising campaign \"${payload.name}\" (${status}), daily budget ${payload.daily_budget}`,\n changes: [`+ campaign \"${payload.name}\" type=Search daily_budget=${payload.daily_budget} status=${status}`],\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 scope = await this.scopeFor(accountId);\n const result = await this.client.request<{\n CampaignIds?: Array<number | string | null>;\n PartialErrors?: Array<{ Index?: number; Code?: number; ErrorCode?: string; Message?: string; FieldPath?: string }>;\n }>('campaign', 'POST', 'Campaigns', { AccountId: accountId, Campaigns: [campaign] }, scope);\n const partial = formatPartialErrors(result.PartialErrors);\n if (partial) throw new AdportError('PROVIDER_ERROR', `microsoft: AddCampaigns partial errors — ${partial}`);\n return (result.CampaignIds ?? []).filter((id): id is number | string => id !== null).map(String);\n },\n };\n }\n\n private async lookupCampaign(accountId: string, campaignId: string): Promise<MicrosoftCampaign> {\n const campaigns = await this.listCampaigns(accountId);\n const campaign = campaigns.find((c) => String(c.Id) === campaignId);\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `microsoft: campaign ${campaignId} not found in account ${accountId}`);\n }\n return campaign;\n }\n\n private async updateCampaign(\n accountId: string,\n fields: Record<string, unknown> & { Id: number | string },\n ): Promise<void> {\n const scope = await this.scopeFor(accountId);\n // UpdateCampaigns is PUT; omitted fields stay unchanged.\n const result = await this.client.request<{\n PartialErrors?: Array<{ Index?: number; Code?: number; ErrorCode?: string; Message?: string }>;\n }>('campaign', 'PUT', 'Campaigns', { AccountId: accountId, Campaigns: [fields] }, scope);\n const partial = formatPartialErrors(result.PartialErrors);\n if (partial) throw new AdportError('PROVIDER_ERROR', `microsoft: UpdateCampaigns partial errors — ${partial}`);\n }\n\n private async planSetStatus(\n accountId: string,\n payload: { campaign_id: string; status: 'Active' | 'Paused' },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(accountId, payload.campaign_id);\n return {\n summary: `Set Microsoft 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.updateCampaign(accountId, { Id: current.Id, Status: payload.status });\n return [payload.campaign_id];\n },\n };\n }\n\n private async planSetBudget(\n accountId: string,\n payload: { campaign_id: string; daily_budget: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(accountId, payload.campaign_id);\n if (current.BudgetId && Number(current.BudgetId) > 0) {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `microsoft: campaign \"${current.Name}\" uses a SHARED budget (BudgetId ${current.BudgetId}) — ` +\n 'its DailyBudget cannot be changed per-campaign (CampaignServiceCannotUpdateSharedBudget).',\n );\n }\n return {\n summary: `Change Microsoft campaign \"${current.Name}\" daily budget ${current.DailyBudget ?? '(unset)'} → ${payload.daily_budget}`,\n changes: [`~ campaign ${payload.campaign_id} DailyBudget ${current.DailyBudget ?? '(unset)'} → ${payload.daily_budget}`],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.Name}\" daily budget`,\n ...(current.DailyBudget !== undefined ? { fromMicros: current.DailyBudget * UNITS_TO_MICROS } : {}),\n toMicros: payload.daily_budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.updateCampaign(accountId, { Id: current.Id, DailyBudget: payload.daily_budget });\n return [payload.campaign_id];\n },\n };\n }\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 validateMicrosoftReadPath(path: string): string {\n const normalized = path.replace(/^\\/+|\\/+$/g, '').trim();\n const action = normalized.split('/').at(-1) ?? '';\n if (\n !/^[A-Za-z][A-Za-z0-9]*(?:\\/[A-Za-z][A-Za-z0-9]*){1,2}$/.test(normalized) ||\n !/^(?:Query|Search|Get|Submit|Poll)/.test(action)\n ) {\n throw new AdportError('INVALID_INPUT', `microsoft: unsupported read operation path \"${path}\"`);\n }\n return normalized;\n}\n\nfunction validateMicrosoftResource(resource: string): string {\n const normalized = resource.trim();\n if (!/^[A-Z][A-Za-z0-9]{1,80}$/.test(normalized)) {\n throw new AdportError('INVALID_INPUT', `microsoft: invalid Campaign Management resource \"${resource}\"`);\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 validateMicrosoftAccountScope(value: unknown, accountId: string): void {\n if (Array.isArray(value)) return value.forEach((item) => validateMicrosoftAccountScope(item, accountId));\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if (key === 'AccountId' && String(child) !== accountId) {\n throw new AdportError('INVALID_INPUT', `microsoft: body AccountId ${String(child)} does not match selected account ${accountId}`);\n }\n if (key === 'AccountIds' && Array.isArray(child) && child.some((id) => String(id) !== accountId)) {\n throw new AdportError('INVALID_INPUT', `microsoft: body AccountIds must contain only selected account ${accountId}`);\n }\n validateMicrosoftAccountScope(child, accountId);\n }\n}\n\nfunction coerceMicrosoftCreatedStatuses(value: unknown, coercions: string[], path = 'body'): void {\n if (Array.isArray(value)) return value.forEach((item, index) => coerceMicrosoftCreatedStatuses(item, coercions, `${path}[${index}]`));\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if (key === 'Status' && child === 'Active') {\n value[key] = 'Paused';\n coercions.push(`${path}.${key} coerced to Paused by policy (paused_creation)`);\n } else coerceMicrosoftCreatedStatuses(child, coercions, `${path}.${key}`);\n }\n}\n\nfunction containsMicrosoftBudget(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(containsMicrosoftBudget);\n if (!isRecord(value)) return false;\n return Object.entries(value).some(([key, child]) => /budget/i.test(key) || containsMicrosoftBudget(child));\n}\n\nfunction collectMicrosoftBudgets(value: unknown, path = 'body'): WritePreview['budgetDeltas'] {\n const deltas: WritePreview['budgetDeltas'] = [];\n if (Array.isArray(value)) {\n value.forEach((item, index) => deltas.push(...collectMicrosoftBudgets(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 (/budget/i.test(key) && !/type|id/i.test(key)) {\n const 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(...collectMicrosoftBudgets(child, childPath));\n }\n return deltas;\n}\n\nfunction extractMicrosoftIds(value: unknown): string[] {\n const ids: string[] = [];\n const visit = (item: unknown): void => {\n if (Array.isArray(item)) return item.forEach(visit);\n if (!isRecord(item)) return;\n for (const [key, child] of Object.entries(item)) {\n if (/Ids?$/.test(key)) {\n if (Array.isArray(child)) child.forEach((id) => { if (id !== null) ids.push(String(id)); });\n else if (typeof child === 'string' || typeof child === 'number') ids.push(String(child));\n } else visit(child);\n }\n };\n visit(value);\n return [...new Set(ids)];\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","/** Minimal RFC-4180-ish CSV parser (quotes, escaped quotes, CRLF). */\nexport function parseCsv(text: string): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = '';\n let inQuotes = false;\n for (let i = 0; i < text.length; i++) {\n const char = text[i]!;\n if (inQuotes) {\n if (char === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += char;\n }\n continue;\n }\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n row.push(field);\n field = '';\n } else if (char === '\\n' || char === '\\r') {\n if (char === '\\r' && text[i + 1] === '\\n') i++;\n row.push(field);\n field = '';\n if (row.length > 1 || row[0] !== '') rows.push(row);\n row = [];\n } else {\n field += char;\n }\n }\n if (field !== '' || row.length > 0) {\n row.push(field);\n if (row.length > 1 || row[0] !== '') rows.push(row);\n }\n return rows;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { MicrosoftAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['Active', 'Paused']);\n\nexport function microsoftTools(provider: MicrosoftAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'microsoft_api_read',\n namespace: 'microsoft',\n description:\n 'Call any documented Microsoft Advertising v13 query, search, get, or reporting submit/poll operation with selected-account headers.',\n input: z.object({\n account_id: z.string(),\n service: z.enum(['campaign', 'customer', 'reporting']),\n path: z.string().min(3).describe('REST operation path, e.g. AdGroups/QueryByCampaignId or GenerateReport/Submit'),\n body: z.record(z.string(), z.unknown()).optional(),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n return provider.apiRead(input);\n },\n }),\n defineTool({\n name: 'microsoft_campaigns',\n namespace: 'microsoft',\n description:\n 'List Microsoft Advertising campaigns (Id, Name, Status Active/Paused/BudgetPaused, DailyBudget in account currency, BudgetId when shared).',\n input: z.object({ account_id: z.string().describe('Ad account id') }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id);\n return { campaigns };\n },\n }),\n guardedWriteTool({\n name: 'microsoft_create_campaign',\n namespace: 'microsoft',\n description:\n 'Create a Microsoft Advertising Search campaign. daily_budget is a float in account currency. ' +\n \"The API's own default status is Paused. No server dry-run — previews are client-side diffs.\",\n provider: 'microsoft',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1).max(128),\n daily_budget: z.number().positive(),\n time_zone: z.string().optional().describe('Bing Ads time zone name, default PacificTimeUSCanadaTijuana'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'microsoft_set_campaign_status',\n namespace: 'microsoft',\n description: 'Activate or pause a Microsoft Advertising campaign.',\n provider: 'microsoft',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'microsoft_set_budget',\n namespace: 'microsoft',\n description:\n \"Change a Microsoft campaign's DailyBudget (float, account currency). Fails clearly for shared budgets.\",\n provider: 'microsoft',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget: z.number().positive() }),\n }),\n guardedWriteTool({\n name: 'microsoft_api_create',\n namespace: 'microsoft',\n description:\n 'Create through any Microsoft Advertising Campaign Management v13 collection using the documented API body. Active statuses are forced to Paused and budget fields are policy-checked.',\n provider: 'microsoft',\n kind: 'create',\n payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) }),\n }),\n guardedWriteTool({\n name: 'microsoft_api_update',\n namespace: 'microsoft',\n description: 'Update non-budget fields through any Microsoft Advertising Campaign Management v13 collection.',\n provider: 'microsoft',\n kind: 'update',\n payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) }),\n }),\n guardedWriteTool({\n name: 'microsoft_api_delete',\n namespace: 'microsoft',\n description: 'Permanently delete through any Microsoft Advertising Campaign Management v13 collection.',\n provider: 'microsoft',\n kind: 'remove',\n destructive: true,\n payload: z.object({ resource: z.string().min(2), body: z.record(z.string(), z.unknown()) }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { MicrosoftAdsClient, type MicrosoftCredentials } from './client.js';\nimport { MicrosoftAdsProvider } from './provider.js';\nimport { microsoftTools } from './tools.js';\n\nexport {\n MicrosoftAdsClient,\n MSADS_SCOPE,\n SANDBOX_DEVELOPER_TOKEN,\n formatMicrosoftError,\n formatPartialErrors,\n type MicrosoftCredentials,\n} from './client.js';\nexport { MicrosoftAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { microsoftTools } from './tools.js';\nexport { parseCsv } from './csv.js';\n\nexport async function resolveMicrosoftCredentials(store: CredentialStore): Promise<MicrosoftCredentials | undefined> {\n const record = await store.get('microsoft');\n if (record?.data.developer_token && record.data.client_id && record.data.refresh_token) {\n return {\n developerToken: record.data.developer_token,\n clientId: record.data.client_id,\n refreshToken: record.data.refresh_token,\n clientSecret: record.data.client_secret || undefined,\n sandbox: record.data.sandbox === 'true',\n };\n }\n const env = process.env;\n if (env.MICROSOFT_ADS_DEVELOPER_TOKEN && env.MICROSOFT_ADS_CLIENT_ID && env.MICROSOFT_ADS_REFRESH_TOKEN) {\n return {\n developerToken: env.MICROSOFT_ADS_DEVELOPER_TOKEN,\n clientId: env.MICROSOFT_ADS_CLIENT_ID,\n refreshToken: env.MICROSOFT_ADS_REFRESH_TOKEN,\n clientSecret: env.MICROSOFT_ADS_CLIENT_SECRET || undefined,\n sandbox: env.MICROSOFT_ADS_SANDBOX === 'true',\n };\n }\n return undefined;\n}\n\nexport async function createMicrosoftModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveMicrosoftCredentials(store);\n if (!credentials) return undefined;\n const client = new MicrosoftAdsClient(credentials, async (rotated) => {\n // Public-client refresh tokens rotate; persist the replacement immediately.\n const record = await store.get('microsoft');\n if (record) {\n await store.set({ ...record, data: { ...record.data, refresh_token: rotated } });\n }\n });\n const provider = new MicrosoftAdsProvider(client);\n return { provider, tools: microsoftTools(provider) };\n}\n"],"mappings":"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","names":["AdportError","AdportError"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adport/provider-microsoft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Microsoft Advertising (Bing Ads) provider for adport — REST API v13 with sandbox support",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"fflate": "^0.8.2",
|
|
20
20
|
"zod": "^4.4.3",
|
|
21
|
-
"@adport/core": "0.
|
|
21
|
+
"@adport/core": "0.4.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^26.1.2",
|