cli-meta-ads 0.1.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/AGENTS.md +188 -0
- package/AI_CONTEXT.md +144 -0
- package/CLAUDE.md +183 -0
- package/README.md +590 -0
- package/REQUIREMENTS.md +148 -0
- package/dist/auth/constants.d.ts +1 -0
- package/dist/auth/constants.js +1 -0
- package/dist/auth/guards.d.ts +5 -0
- package/dist/auth/guards.js +16 -0
- package/dist/auth/login.d.ts +28 -0
- package/dist/auth/login.js +222 -0
- package/dist/cli/action.d.ts +11 -0
- package/dist/cli/action.js +77 -0
- package/dist/cli/build-cli.d.ts +2 -0
- package/dist/cli/build-cli.js +110 -0
- package/dist/cli/context.d.ts +24 -0
- package/dist/cli/context.js +19 -0
- package/dist/client/meta-api-client.d.ts +50 -0
- package/dist/client/meta-api-client.js +258 -0
- package/dist/client/meta-discovery.d.ts +13 -0
- package/dist/client/meta-discovery.js +88 -0
- package/dist/commands/accounts.d.ts +4 -0
- package/dist/commands/accounts.js +42 -0
- package/dist/commands/ads.d.ts +4 -0
- package/dist/commands/ads.js +148 -0
- package/dist/commands/adsets.d.ts +4 -0
- package/dist/commands/adsets.js +49 -0
- package/dist/commands/anomalies.d.ts +4 -0
- package/dist/commands/anomalies.js +44 -0
- package/dist/commands/assets.d.ts +4 -0
- package/dist/commands/assets.js +116 -0
- package/dist/commands/audiences.d.ts +4 -0
- package/dist/commands/audiences.js +40 -0
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.js +139 -0
- package/dist/commands/campaigns.d.ts +4 -0
- package/dist/commands/campaigns.js +273 -0
- package/dist/commands/capi.d.ts +4 -0
- package/dist/commands/capi.js +64 -0
- package/dist/commands/creatives.d.ts +4 -0
- package/dist/commands/creatives.js +49 -0
- package/dist/commands/diagnostics.d.ts +4 -0
- package/dist/commands/diagnostics.js +88 -0
- package/dist/commands/helpers.d.ts +13 -0
- package/dist/commands/helpers.js +50 -0
- package/dist/commands/launch.d.ts +4 -0
- package/dist/commands/launch.js +109 -0
- package/dist/commands/performance.d.ts +4 -0
- package/dist/commands/performance.js +55 -0
- package/dist/commands/pixel.d.ts +4 -0
- package/dist/commands/pixel.js +68 -0
- package/dist/commands/report.d.ts +4 -0
- package/dist/commands/report.js +30 -0
- package/dist/config/file-config.d.ts +6 -0
- package/dist/config/file-config.js +174 -0
- package/dist/config/types.d.ts +32 -0
- package/dist/config/types.js +1 -0
- package/dist/domain/account-scope.d.ts +7 -0
- package/dist/domain/account-scope.js +28 -0
- package/dist/domain/analytics.d.ts +52 -0
- package/dist/domain/analytics.js +125 -0
- package/dist/domain/approval-service.d.ts +10 -0
- package/dist/domain/approval-service.js +48 -0
- package/dist/domain/asset-feed-compiler.d.ts +43 -0
- package/dist/domain/asset-feed-compiler.js +104 -0
- package/dist/domain/launch-service.d.ts +200 -0
- package/dist/domain/launch-service.js +558 -0
- package/dist/domain/meta-ads-service.d.ts +620 -0
- package/dist/domain/meta-ads-service.js +841 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -0
- package/dist/output/render.d.ts +3 -0
- package/dist/output/render.js +103 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.js +1 -0
- package/dist/utils/currency.d.ts +4 -0
- package/dist/utils/currency.js +40 -0
- package/dist/utils/date-range.d.ts +20 -0
- package/dist/utils/date-range.js +115 -0
- package/dist/utils/errors.d.ts +35 -0
- package/dist/utils/errors.js +68 -0
- package/dist/utils/ids.d.ts +4 -0
- package/dist/utils/ids.js +23 -0
- package/dist/utils/meta-placement-assets.d.ts +44 -0
- package/dist/utils/meta-placement-assets.js +315 -0
- package/dist/utils/security.d.ts +5 -0
- package/dist/utils/security.js +104 -0
- package/dist/validators/common.d.ts +10 -0
- package/dist/validators/common.js +56 -0
- package/dist/validators/create-spec.d.ts +373 -0
- package/dist/validators/create-spec.js +394 -0
- package/dist/validators/launch-spec.d.ts +229 -0
- package/dist/validators/launch-spec.js +371 -0
- package/docs/TECHNICAL.md +480 -0
- package/examples/README.md +29 -0
- package/examples/launch/assets/feed4x5.png +0 -0
- package/examples/launch/assets/story9x16.png +0 -0
- package/examples/launch/multi-format-launch.json +90 -0
- package/examples/single-object/ad.json +6 -0
- package/examples/single-object/adset.json +30 -0
- package/examples/single-object/campaign.json +6 -0
- package/examples/single-object/creative.json +19 -0
- package/package.json +62 -0
- package/skills/meta-cli-operator/SKILL.md +105 -0
- package/skills/meta-cli-operator/agents/openai.yaml +4 -0
- package/skills/meta-cli-operator/references/update-matrix.md +117 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
function isScalar(value) {
|
|
2
|
+
return value === null || value === undefined || typeof value !== "object";
|
|
3
|
+
}
|
|
4
|
+
function stringifyScalar(value) {
|
|
5
|
+
if (value === null || value === undefined) {
|
|
6
|
+
return "-";
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === "string") {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
12
|
+
return String(value);
|
|
13
|
+
}
|
|
14
|
+
return JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
function stringWidth(value) {
|
|
17
|
+
return Array.from(value).length;
|
|
18
|
+
}
|
|
19
|
+
function padCell(value, width) {
|
|
20
|
+
const padding = Math.max(0, width - stringWidth(value));
|
|
21
|
+
return `${value}${" ".repeat(padding)}`;
|
|
22
|
+
}
|
|
23
|
+
function renderTable(rows) {
|
|
24
|
+
if (rows.length === 0) {
|
|
25
|
+
return "(empty)";
|
|
26
|
+
}
|
|
27
|
+
const headers = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
28
|
+
const widths = headers.map((header) => Math.max(stringWidth(header), ...rows.map((row) => stringWidth(stringifyScalar(row[header])))));
|
|
29
|
+
const headerLine = headers
|
|
30
|
+
.map((header, index) => padCell(header, widths[index] ?? stringWidth(header)))
|
|
31
|
+
.join(" ");
|
|
32
|
+
const divider = widths.map((width) => "-".repeat(width)).join(" ");
|
|
33
|
+
const body = rows
|
|
34
|
+
.map((row) => headers
|
|
35
|
+
.map((header, index) => padCell(stringifyScalar(row[header]), widths[index] ?? stringWidth(header)))
|
|
36
|
+
.join(" "))
|
|
37
|
+
.join("\n");
|
|
38
|
+
return [headerLine, divider, body].join("\n");
|
|
39
|
+
}
|
|
40
|
+
function renderValue(value, indent = 0) {
|
|
41
|
+
const prefix = " ".repeat(indent);
|
|
42
|
+
if (isScalar(value)) {
|
|
43
|
+
return `${prefix}${stringifyScalar(value)}`;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
if (value.length === 0) {
|
|
47
|
+
return `${prefix}(empty)`;
|
|
48
|
+
}
|
|
49
|
+
if (value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry))) {
|
|
50
|
+
return renderTable(value)
|
|
51
|
+
.split("\n")
|
|
52
|
+
.map((line) => `${prefix}${line}`)
|
|
53
|
+
.join("\n");
|
|
54
|
+
}
|
|
55
|
+
return value
|
|
56
|
+
.map((entry) => `${prefix}- ${renderValue(entry, indent + 2).trimStart()}`)
|
|
57
|
+
.join("\n");
|
|
58
|
+
}
|
|
59
|
+
const entries = Object.entries(value);
|
|
60
|
+
if (entries.length === 0) {
|
|
61
|
+
return `${prefix}(empty)`;
|
|
62
|
+
}
|
|
63
|
+
return entries
|
|
64
|
+
.map(([key, entryValue]) => {
|
|
65
|
+
if (isScalar(entryValue)) {
|
|
66
|
+
return `${prefix}${key}: ${stringifyScalar(entryValue)}`;
|
|
67
|
+
}
|
|
68
|
+
return `${prefix}${key}:\n${renderValue(entryValue, indent + 2)}`;
|
|
69
|
+
})
|
|
70
|
+
.join("\n");
|
|
71
|
+
}
|
|
72
|
+
export function renderSuccess(result, asJson) {
|
|
73
|
+
if (asJson) {
|
|
74
|
+
return `${JSON.stringify(result, null, 2)}\n`;
|
|
75
|
+
}
|
|
76
|
+
const sections = [
|
|
77
|
+
`command: ${result.command}`,
|
|
78
|
+
renderValue(result.data)
|
|
79
|
+
];
|
|
80
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
81
|
+
sections.push(`warnings:\n${renderValue(result.warnings, 2)}`);
|
|
82
|
+
}
|
|
83
|
+
if (result.partialFailures && result.partialFailures.length > 0) {
|
|
84
|
+
sections.push(`partialFailures:\n${renderValue(result.partialFailures, 2)}`);
|
|
85
|
+
}
|
|
86
|
+
if (result.meta && Object.keys(result.meta).length > 0) {
|
|
87
|
+
sections.push(`meta:\n${renderValue(result.meta, 2)}`);
|
|
88
|
+
}
|
|
89
|
+
return `${sections.join("\n\n")}\n`;
|
|
90
|
+
}
|
|
91
|
+
export function renderFailure(result, asJson) {
|
|
92
|
+
if (asJson) {
|
|
93
|
+
return `${JSON.stringify(result, null, 2)}\n`;
|
|
94
|
+
}
|
|
95
|
+
const sections = [
|
|
96
|
+
`command: ${result.command}`,
|
|
97
|
+
`error: ${result.error}`
|
|
98
|
+
];
|
|
99
|
+
if (result.meta && Object.keys(result.meta).length > 0) {
|
|
100
|
+
sections.push(`meta:\n${renderValue(result.meta, 2)}`);
|
|
101
|
+
}
|
|
102
|
+
return `${sections.join("\n\n")}\n`;
|
|
103
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type PermissionMode = "read" | "write" | "admin";
|
|
2
|
+
export type OutputFormat = "text" | "json";
|
|
3
|
+
export interface PartialFailure {
|
|
4
|
+
scope: string;
|
|
5
|
+
message: string;
|
|
6
|
+
code?: number | string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface CommandMeta {
|
|
9
|
+
command: string;
|
|
10
|
+
warnings?: string[] | undefined;
|
|
11
|
+
meta?: Record<string, unknown> | undefined;
|
|
12
|
+
partialFailures?: PartialFailure[] | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface CommandSuccess<T> extends CommandMeta {
|
|
15
|
+
ok: true;
|
|
16
|
+
data: T;
|
|
17
|
+
}
|
|
18
|
+
export interface CommandFailureShape extends CommandMeta {
|
|
19
|
+
ok: false;
|
|
20
|
+
error: string;
|
|
21
|
+
}
|
|
22
|
+
export type CommandResult<T> = CommandSuccess<T>;
|
|
23
|
+
export interface GlobalOptions {
|
|
24
|
+
apiVersion?: string | undefined;
|
|
25
|
+
apply?: boolean | undefined;
|
|
26
|
+
config?: string | undefined;
|
|
27
|
+
debug?: boolean | undefined;
|
|
28
|
+
json?: boolean | undefined;
|
|
29
|
+
mode?: PermissionMode | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface ApprovalRequestPayload {
|
|
32
|
+
requestedAt: string;
|
|
33
|
+
action: string;
|
|
34
|
+
command: string;
|
|
35
|
+
resourceType: string;
|
|
36
|
+
resourceId: string;
|
|
37
|
+
accountId?: string | undefined;
|
|
38
|
+
approvalReason: string;
|
|
39
|
+
requestedMode: PermissionMode;
|
|
40
|
+
argv: string[];
|
|
41
|
+
changeSummary: Record<string, unknown>;
|
|
42
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function currencyMinorUnitFactor(currency: string): number;
|
|
2
|
+
export declare function toMinorUnits(amount: number, currency: string): number;
|
|
3
|
+
export declare function fromMinorUnits(amount: number | string, currency: string): number;
|
|
4
|
+
export declare function formatCurrency(amount: number, currency: string): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const ZERO_DECIMAL_CURRENCIES = new Set([
|
|
2
|
+
"BIF",
|
|
3
|
+
"CLP",
|
|
4
|
+
"DJF",
|
|
5
|
+
"GNF",
|
|
6
|
+
"JPY",
|
|
7
|
+
"KMF",
|
|
8
|
+
"KRW",
|
|
9
|
+
"MGA",
|
|
10
|
+
"PYG",
|
|
11
|
+
"RWF",
|
|
12
|
+
"UGX",
|
|
13
|
+
"VND",
|
|
14
|
+
"VUV",
|
|
15
|
+
"XAF",
|
|
16
|
+
"XOF",
|
|
17
|
+
"XPF"
|
|
18
|
+
]);
|
|
19
|
+
export function currencyMinorUnitFactor(currency) {
|
|
20
|
+
return ZERO_DECIMAL_CURRENCIES.has(currency.toUpperCase()) ? 1 : 100;
|
|
21
|
+
}
|
|
22
|
+
export function toMinorUnits(amount, currency) {
|
|
23
|
+
return Math.round(amount * currencyMinorUnitFactor(currency));
|
|
24
|
+
}
|
|
25
|
+
export function fromMinorUnits(amount, currency) {
|
|
26
|
+
const numericAmount = typeof amount === "string" ? Number(amount) : amount;
|
|
27
|
+
return numericAmount / currencyMinorUnitFactor(currency);
|
|
28
|
+
}
|
|
29
|
+
export function formatCurrency(amount, currency) {
|
|
30
|
+
try {
|
|
31
|
+
return new Intl.NumberFormat("en-US", {
|
|
32
|
+
style: "currency",
|
|
33
|
+
currency,
|
|
34
|
+
maximumFractionDigits: currencyMinorUnitFactor(currency) === 1 ? 0 : 2
|
|
35
|
+
}).format(amount);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return `${amount.toFixed(2)} ${currency}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DateWindowInput {
|
|
2
|
+
last?: string | undefined;
|
|
3
|
+
from?: string | undefined;
|
|
4
|
+
to?: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export interface DateWindow {
|
|
7
|
+
from: string;
|
|
8
|
+
to: string;
|
|
9
|
+
days: number;
|
|
10
|
+
}
|
|
11
|
+
export interface TimeWindow {
|
|
12
|
+
since: Date;
|
|
13
|
+
until: Date;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildRecentDateWindow(days: number, now?: Date): DateWindow;
|
|
17
|
+
export declare function parseDateWindow(input: DateWindowInput): DateWindow;
|
|
18
|
+
export declare function toInsightsTimeRange(window: DateWindow): string;
|
|
19
|
+
export declare function parseTimeWindow(last: string): TimeWindow;
|
|
20
|
+
export declare function percentChange(previous: number, current: number): number | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AppError, ExitCode } from "./errors.js";
|
|
2
|
+
const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
|
3
|
+
const HOUR_IN_MS = 60 * 60 * 1000;
|
|
4
|
+
const ISO_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
5
|
+
function toLocalIsoDate(date) {
|
|
6
|
+
const year = date.getFullYear();
|
|
7
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
8
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
9
|
+
return `${year}-${month}-${day}`;
|
|
10
|
+
}
|
|
11
|
+
function parseStrictIsoDate(value, label) {
|
|
12
|
+
const match = ISO_DATE_PATTERN.exec(value);
|
|
13
|
+
if (!match) {
|
|
14
|
+
throw new AppError(`${label} must use YYYY-MM-DD.`, ExitCode.Usage);
|
|
15
|
+
}
|
|
16
|
+
const year = Number(match[1]);
|
|
17
|
+
const month = Number(match[2]);
|
|
18
|
+
const day = Number(match[3]);
|
|
19
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
20
|
+
if (Number.isNaN(date.getTime())
|
|
21
|
+
|| date.getUTCFullYear() !== year
|
|
22
|
+
|| date.getUTCMonth() !== month - 1
|
|
23
|
+
|| date.getUTCDate() !== day) {
|
|
24
|
+
throw new AppError(`${label} must be a valid calendar date.`, ExitCode.Usage);
|
|
25
|
+
}
|
|
26
|
+
return date;
|
|
27
|
+
}
|
|
28
|
+
export function buildRecentDateWindow(days, now = new Date()) {
|
|
29
|
+
if (!Number.isInteger(days) || days < 1) {
|
|
30
|
+
throw new AppError("Relative date windows must be at least 1 day.", ExitCode.Usage);
|
|
31
|
+
}
|
|
32
|
+
const until = new Date(now);
|
|
33
|
+
until.setHours(0, 0, 0, 0);
|
|
34
|
+
const fromDate = new Date(until);
|
|
35
|
+
fromDate.setDate(fromDate.getDate() - (days - 1));
|
|
36
|
+
return {
|
|
37
|
+
from: toLocalIsoDate(fromDate),
|
|
38
|
+
to: toLocalIsoDate(until),
|
|
39
|
+
days
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function parseDateWindow(input) {
|
|
43
|
+
const { last, from, to } = input;
|
|
44
|
+
if (last && (from || to)) {
|
|
45
|
+
throw new AppError("Use either --last or --from/--to, not both.", ExitCode.Usage);
|
|
46
|
+
}
|
|
47
|
+
if (!last && !from && !to) {
|
|
48
|
+
throw new AppError("A time window is required. Provide --last or --from/--to.", ExitCode.Usage);
|
|
49
|
+
}
|
|
50
|
+
if (last) {
|
|
51
|
+
const match = /^(\d+)(d|w)$/i.exec(last.trim());
|
|
52
|
+
if (!match) {
|
|
53
|
+
throw new AppError("Invalid --last value. Use values like 7d or 4w.", ExitCode.Usage);
|
|
54
|
+
}
|
|
55
|
+
const amountValue = match[1];
|
|
56
|
+
const unitValue = match[2];
|
|
57
|
+
if (!amountValue || !unitValue) {
|
|
58
|
+
throw new AppError("Invalid --last value. Use values like 7d or 4w.", ExitCode.Usage);
|
|
59
|
+
}
|
|
60
|
+
const amount = Number(amountValue);
|
|
61
|
+
const unit = unitValue.toLowerCase();
|
|
62
|
+
const days = unit === "w" ? amount * 7 : amount;
|
|
63
|
+
return buildRecentDateWindow(days);
|
|
64
|
+
}
|
|
65
|
+
if (!from || !to) {
|
|
66
|
+
throw new AppError("Both --from and --to are required.", ExitCode.Usage);
|
|
67
|
+
}
|
|
68
|
+
const fromDate = parseStrictIsoDate(from, "--from");
|
|
69
|
+
const toDate = parseStrictIsoDate(to, "--to");
|
|
70
|
+
if (fromDate > toDate) {
|
|
71
|
+
throw new AppError("--from must be before or equal to --to.", ExitCode.Usage);
|
|
72
|
+
}
|
|
73
|
+
const days = Math.floor((toDate.getTime() - fromDate.getTime()) / DAY_IN_MS) + 1;
|
|
74
|
+
return {
|
|
75
|
+
from,
|
|
76
|
+
to,
|
|
77
|
+
days
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function toInsightsTimeRange(window) {
|
|
81
|
+
return JSON.stringify({
|
|
82
|
+
since: window.from,
|
|
83
|
+
until: window.to
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export function parseTimeWindow(last) {
|
|
87
|
+
const match = /^(\d+)(h|d)$/i.exec(last.trim());
|
|
88
|
+
if (!match) {
|
|
89
|
+
throw new AppError("Invalid --last value. Use values like 24h or 7d.", ExitCode.Usage);
|
|
90
|
+
}
|
|
91
|
+
const amountValue = match[1];
|
|
92
|
+
const unitValue = match[2];
|
|
93
|
+
if (!amountValue || !unitValue) {
|
|
94
|
+
throw new AppError("Invalid --last value. Use values like 24h or 7d.", ExitCode.Usage);
|
|
95
|
+
}
|
|
96
|
+
const amount = Number(amountValue);
|
|
97
|
+
const unit = unitValue.toLowerCase();
|
|
98
|
+
if (amount < 1) {
|
|
99
|
+
throw new AppError("Invalid --last value. Use values like 24h or 7d.", ExitCode.Usage);
|
|
100
|
+
}
|
|
101
|
+
const durationMs = unit === "h" ? amount * HOUR_IN_MS : amount * DAY_IN_MS;
|
|
102
|
+
const until = new Date();
|
|
103
|
+
const since = new Date(until.getTime() - durationMs);
|
|
104
|
+
return {
|
|
105
|
+
since,
|
|
106
|
+
until,
|
|
107
|
+
label: last
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function percentChange(previous, current) {
|
|
111
|
+
if (previous === 0) {
|
|
112
|
+
return current === 0 ? 0 : null;
|
|
113
|
+
}
|
|
114
|
+
return ((current - previous) / previous) * 100;
|
|
115
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CommandFailureShape } from "../types.js";
|
|
2
|
+
export declare enum ExitCode {
|
|
3
|
+
Ok = 0,
|
|
4
|
+
Usage = 2,
|
|
5
|
+
Config = 3,
|
|
6
|
+
Auth = 4,
|
|
7
|
+
Permission = 5,
|
|
8
|
+
RateLimit = 6,
|
|
9
|
+
Provider = 7,
|
|
10
|
+
PartialFailure = 8,
|
|
11
|
+
ApprovalRequired = 9,
|
|
12
|
+
UnsafeBlocked = 10,
|
|
13
|
+
VerificationFailed = 11
|
|
14
|
+
}
|
|
15
|
+
export declare class AppError extends Error {
|
|
16
|
+
readonly exitCode: ExitCode;
|
|
17
|
+
readonly details: Record<string, unknown> | undefined;
|
|
18
|
+
constructor(message: string, exitCode: ExitCode, details?: Record<string, unknown>);
|
|
19
|
+
}
|
|
20
|
+
export interface MetaApiErrorOptions {
|
|
21
|
+
status: number;
|
|
22
|
+
metaCode?: number | undefined;
|
|
23
|
+
metaSubcode?: number | undefined;
|
|
24
|
+
fbtraceId?: string | undefined;
|
|
25
|
+
details?: Record<string, unknown> | undefined;
|
|
26
|
+
}
|
|
27
|
+
export declare class MetaApiError extends AppError {
|
|
28
|
+
readonly status: number;
|
|
29
|
+
readonly metaCode: number | undefined;
|
|
30
|
+
readonly metaSubcode: number | undefined;
|
|
31
|
+
readonly fbtraceId: string | undefined;
|
|
32
|
+
constructor(message: string, exitCode: ExitCode, options: MetaApiErrorOptions);
|
|
33
|
+
}
|
|
34
|
+
export declare function isAppError(value: unknown): value is AppError;
|
|
35
|
+
export declare function toFailureShape(error: unknown, command: string, meta?: Record<string, unknown>): CommandFailureShape;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export var ExitCode;
|
|
2
|
+
(function (ExitCode) {
|
|
3
|
+
ExitCode[ExitCode["Ok"] = 0] = "Ok";
|
|
4
|
+
ExitCode[ExitCode["Usage"] = 2] = "Usage";
|
|
5
|
+
ExitCode[ExitCode["Config"] = 3] = "Config";
|
|
6
|
+
ExitCode[ExitCode["Auth"] = 4] = "Auth";
|
|
7
|
+
ExitCode[ExitCode["Permission"] = 5] = "Permission";
|
|
8
|
+
ExitCode[ExitCode["RateLimit"] = 6] = "RateLimit";
|
|
9
|
+
ExitCode[ExitCode["Provider"] = 7] = "Provider";
|
|
10
|
+
ExitCode[ExitCode["PartialFailure"] = 8] = "PartialFailure";
|
|
11
|
+
ExitCode[ExitCode["ApprovalRequired"] = 9] = "ApprovalRequired";
|
|
12
|
+
ExitCode[ExitCode["UnsafeBlocked"] = 10] = "UnsafeBlocked";
|
|
13
|
+
ExitCode[ExitCode["VerificationFailed"] = 11] = "VerificationFailed";
|
|
14
|
+
})(ExitCode || (ExitCode = {}));
|
|
15
|
+
export class AppError extends Error {
|
|
16
|
+
exitCode;
|
|
17
|
+
details;
|
|
18
|
+
constructor(message, exitCode, details) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = new.target.name;
|
|
21
|
+
this.exitCode = exitCode;
|
|
22
|
+
this.details = details;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class MetaApiError extends AppError {
|
|
26
|
+
status;
|
|
27
|
+
metaCode;
|
|
28
|
+
metaSubcode;
|
|
29
|
+
fbtraceId;
|
|
30
|
+
constructor(message, exitCode, options) {
|
|
31
|
+
super(message, exitCode, options.details);
|
|
32
|
+
this.status = options.status;
|
|
33
|
+
this.metaCode = options.metaCode;
|
|
34
|
+
this.metaSubcode = options.metaSubcode;
|
|
35
|
+
this.fbtraceId = options.fbtraceId;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function isAppError(value) {
|
|
39
|
+
return value instanceof AppError;
|
|
40
|
+
}
|
|
41
|
+
export function toFailureShape(error, command, meta) {
|
|
42
|
+
if (isAppError(error)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
command,
|
|
46
|
+
error: error.message,
|
|
47
|
+
meta: {
|
|
48
|
+
...(meta ?? {}),
|
|
49
|
+
exitCode: error.exitCode,
|
|
50
|
+
...(error.details ?? {})
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
command,
|
|
58
|
+
error: error.message,
|
|
59
|
+
meta
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
command,
|
|
65
|
+
error: "Unknown error",
|
|
66
|
+
meta
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isAllAccountsSelection(value: string): boolean;
|
|
2
|
+
export declare function normalizeAdAccountId(value: string): string;
|
|
3
|
+
export declare function toAdAccountNodeId(value: string): string;
|
|
4
|
+
export declare function normalizeObjectId(value: string, label: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AppError, ExitCode } from "./errors.js";
|
|
2
|
+
const ACCOUNT_ALL = "all";
|
|
3
|
+
export function isAllAccountsSelection(value) {
|
|
4
|
+
return value.toLowerCase() === ACCOUNT_ALL;
|
|
5
|
+
}
|
|
6
|
+
export function normalizeAdAccountId(value) {
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
const numeric = trimmed.startsWith("act_") ? trimmed.slice(4) : trimmed;
|
|
9
|
+
if (!/^\d+$/.test(numeric)) {
|
|
10
|
+
throw new AppError(`Invalid ad account id: ${value}`, ExitCode.Usage);
|
|
11
|
+
}
|
|
12
|
+
return numeric;
|
|
13
|
+
}
|
|
14
|
+
export function toAdAccountNodeId(value) {
|
|
15
|
+
return `act_${normalizeAdAccountId(value)}`;
|
|
16
|
+
}
|
|
17
|
+
export function normalizeObjectId(value, label) {
|
|
18
|
+
const trimmed = value.trim();
|
|
19
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
20
|
+
throw new AppError(`Invalid ${label}: ${value}`, ExitCode.Usage);
|
|
21
|
+
}
|
|
22
|
+
return trimmed;
|
|
23
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const launchCreativeFormatKeys: readonly ["feed4x5", "square1x1", "story9x16"];
|
|
2
|
+
export type LaunchCreativeFormatKey = typeof launchCreativeFormatKeys[number];
|
|
3
|
+
export interface PlacementAssetOverride {
|
|
4
|
+
assetRef: string;
|
|
5
|
+
captionIds?: string[] | undefined;
|
|
6
|
+
imageCrops?: Record<string, number[]> | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface PlacementAssetFormats {
|
|
9
|
+
feed4x5?: PlacementAssetOverride | undefined;
|
|
10
|
+
square1x1?: PlacementAssetOverride | undefined;
|
|
11
|
+
story9x16?: PlacementAssetOverride | undefined;
|
|
12
|
+
}
|
|
13
|
+
type PublisherPlatform = "facebook" | "instagram" | "messenger" | "audience_network" | "threads";
|
|
14
|
+
type PlacementId = `${PublisherPlatform}.${string}`;
|
|
15
|
+
export declare const defaultCreativeFormatPlacementTargets: Record<LaunchCreativeFormatKey, string[]>;
|
|
16
|
+
export interface ExpandedPlacementTargeting {
|
|
17
|
+
automatic: boolean;
|
|
18
|
+
automaticPublisherPlatforms: PublisherPlatform[];
|
|
19
|
+
audienceNetworkPositions: string[];
|
|
20
|
+
explicitPlacementIds: PlacementId[];
|
|
21
|
+
facebookPositions: string[];
|
|
22
|
+
feedPlacementIds: PlacementId[];
|
|
23
|
+
instagramPositions: string[];
|
|
24
|
+
knownPlacementIds: PlacementId[];
|
|
25
|
+
messengerPositions: string[];
|
|
26
|
+
placementIds: PlacementId[];
|
|
27
|
+
publisherPlatforms: PublisherPlatform[];
|
|
28
|
+
publisherPlatformsWereExplicit: boolean;
|
|
29
|
+
squarePlacementIds: PlacementId[];
|
|
30
|
+
threadsPositions: string[];
|
|
31
|
+
unknownPlacementIds: PlacementId[];
|
|
32
|
+
unsupportedPlacementIds: PlacementId[];
|
|
33
|
+
verticalPlacementIds: PlacementId[];
|
|
34
|
+
}
|
|
35
|
+
export declare function expandPlacementTargeting(targeting: unknown): ExpandedPlacementTargeting;
|
|
36
|
+
export declare function hasPlacementFormats(formats: Partial<Record<LaunchCreativeFormatKey, unknown>> | undefined): boolean;
|
|
37
|
+
export declare function hasOnlyInstagramVerticalPlacements(targeting: ExpandedPlacementTargeting): boolean;
|
|
38
|
+
export declare function formatPlacementIds(placementIds: readonly string[]): string;
|
|
39
|
+
export declare function pickVerticalPlacementIds(placementIds: readonly string[]): string[];
|
|
40
|
+
export declare function pickFeedPlacementIds(placementIds: readonly string[]): string[];
|
|
41
|
+
export declare function pickUnsupportedPlacementIds(placementIds: readonly string[]): string[];
|
|
42
|
+
export declare function buildPlacementCustomizationSpec(placementIds: readonly string[]): Record<string, unknown>;
|
|
43
|
+
export declare function coversFallbackPlacements(placementId: string): boolean;
|
|
44
|
+
export {};
|