adapty 0.8.4-beta.1 → 0.8.5

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.
Files changed (107) hide show
  1. package/README.md +133 -3
  2. package/bin/dev.js +2 -2
  3. package/bin/run.js +2 -2
  4. package/dist/commands/access-levels/create.js +1 -1
  5. package/dist/commands/access-levels/list.d.ts +1 -1
  6. package/dist/commands/apps/create.js +9 -6
  7. package/dist/commands/apps/list.d.ts +1 -1
  8. package/dist/commands/apps/update.js +7 -4
  9. package/dist/commands/asa/ad-groups/create.js +9 -7
  10. package/dist/commands/asa/ad-groups/get.js +3 -2
  11. package/dist/commands/asa/ad-groups/list.d.ts +1 -1
  12. package/dist/commands/asa/ad-groups/list.js +1 -1
  13. package/dist/commands/asa/ad-groups/update.js +22 -12
  14. package/dist/commands/asa/ads/create.js +7 -5
  15. package/dist/commands/asa/ads/get.js +3 -2
  16. package/dist/commands/asa/ads/list.d.ts +1 -1
  17. package/dist/commands/asa/ads/list.js +1 -1
  18. package/dist/commands/asa/ads/update.js +9 -5
  19. package/dist/commands/asa/apps/list.d.ts +1 -1
  20. package/dist/commands/asa/apps/list.js +1 -1
  21. package/dist/commands/asa/automations/create.d.ts +9 -0
  22. package/dist/commands/asa/automations/create.js +26 -7
  23. package/dist/commands/asa/automations/get.js +3 -2
  24. package/dist/commands/asa/automations/list.d.ts +1 -1
  25. package/dist/commands/asa/automations/list.js +1 -1
  26. package/dist/commands/asa/automations/run.js +5 -3
  27. package/dist/commands/asa/automations/runs.d.ts +1 -1
  28. package/dist/commands/asa/automations/runs.js +4 -4
  29. package/dist/commands/asa/automations/update.d.ts +9 -0
  30. package/dist/commands/asa/automations/update.js +36 -12
  31. package/dist/commands/asa/campaigns/bulk-create.js +31 -18
  32. package/dist/commands/asa/campaigns/bulk-list.js +7 -5
  33. package/dist/commands/asa/campaigns/bulk-status.js +3 -2
  34. package/dist/commands/asa/campaigns/create.js +10 -8
  35. package/dist/commands/asa/campaigns/get.js +3 -2
  36. package/dist/commands/asa/campaigns/list.d.ts +1 -1
  37. package/dist/commands/asa/campaigns/list.js +1 -1
  38. package/dist/commands/asa/campaigns/update.js +25 -15
  39. package/dist/commands/asa/competitors/summary.js +5 -5
  40. package/dist/commands/asa/connect.js +5 -3
  41. package/dist/commands/asa/creatives/list.d.ts +1 -1
  42. package/dist/commands/asa/creatives/list.js +1 -1
  43. package/dist/commands/asa/keywords/add.js +13 -10
  44. package/dist/commands/asa/keywords/list.d.ts +1 -1
  45. package/dist/commands/asa/keywords/list.js +1 -1
  46. package/dist/commands/asa/keywords/update.js +15 -10
  47. package/dist/commands/asa/metrics/index.d.ts +6 -3
  48. package/dist/commands/asa/metrics/index.js +38 -14
  49. package/dist/commands/asa/metrics/overview.js +3 -3
  50. package/dist/commands/asa/negative-keywords/add.js +10 -7
  51. package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
  52. package/dist/commands/asa/negative-keywords/list.js +1 -1
  53. package/dist/commands/asa/orgs/list.d.ts +1 -1
  54. package/dist/commands/asa/orgs/list.js +1 -1
  55. package/dist/commands/asa/product-pages/list.d.ts +1 -1
  56. package/dist/commands/asa/product-pages/list.js +1 -1
  57. package/dist/commands/asa/product-pages/sync.js +1 -1
  58. package/dist/commands/asa/search-terms/list.d.ts +1 -1
  59. package/dist/commands/asa/search-terms/list.js +1 -1
  60. package/dist/commands/asa/whoami.js +7 -2
  61. package/dist/commands/auth/login.js +3 -1
  62. package/dist/commands/flows/config/get.js +2 -2
  63. package/dist/commands/flows/config/preview.d.ts +2 -2
  64. package/dist/commands/flows/config/preview.js +6 -4
  65. package/dist/commands/flows/config/update.js +5 -3
  66. package/dist/commands/flows/config/validate.js +5 -3
  67. package/dist/commands/flows/list.d.ts +1 -1
  68. package/dist/commands/flows/media/upload.js +1 -1
  69. package/dist/commands/flows/publish.js +5 -5
  70. package/dist/commands/paywalls/create.js +1 -1
  71. package/dist/commands/paywalls/list.d.ts +1 -1
  72. package/dist/commands/paywalls/placements.d.ts +2 -2
  73. package/dist/commands/paywalls/update.js +1 -1
  74. package/dist/commands/placements/create.js +17 -12
  75. package/dist/commands/placements/list.d.ts +1 -1
  76. package/dist/commands/placements/update.js +20 -15
  77. package/dist/commands/products/create.js +20 -13
  78. package/dist/commands/products/list.d.ts +1 -1
  79. package/dist/commands/products/update.js +1 -1
  80. package/dist/commands/segments/list.d.ts +1 -1
  81. package/dist/lib/api-client.d.ts +10 -6
  82. package/dist/lib/api-client.js +56 -35
  83. package/dist/lib/api-schemas.d.ts +56 -56
  84. package/dist/lib/app-url.js +2 -1
  85. package/dist/lib/asa-client.d.ts +13 -8
  86. package/dist/lib/asa-client.js +9 -4
  87. package/dist/lib/asa-flags.d.ts +37 -13
  88. package/dist/lib/asa-flags.js +87 -20
  89. package/dist/lib/asa-keyword-action.d.ts +18 -0
  90. package/dist/lib/asa-keyword-action.js +175 -0
  91. package/dist/lib/asa-schemas.d.ts +100 -80
  92. package/dist/lib/auth.js +2 -1
  93. package/dist/lib/client-from-config.d.ts +1 -1
  94. package/dist/lib/client-from-config.js +2 -1
  95. package/dist/lib/config.d.ts +2 -2
  96. package/dist/lib/confirm.d.ts +4 -4
  97. package/dist/lib/confirm.js +12 -7
  98. package/dist/lib/errors.d.ts +18 -18
  99. package/dist/lib/errors.js +14 -10
  100. package/dist/lib/flags.d.ts +3 -3
  101. package/dist/lib/flags.js +2 -1
  102. package/dist/lib/flow-help.js +11 -6
  103. package/dist/lib/output.js +23 -12
  104. package/dist/lib/preview.d.ts +5 -5
  105. package/dist/lib/preview.js +2 -1
  106. package/oclif.manifest.json +372 -200
  107. package/package.json +10 -8
@@ -1,15 +1,15 @@
1
1
  export type ProductPeriod = 'annual' | 'consumable' | 'lifetime' | 'monthly' | 'nonsubscriptions' | 'semiannual' | 'trimonthly' | 'two_months' | 'uncategorised' | 'weekly';
2
- export interface VendorProductDTO {
2
+ export type VendorProductDTO = {
3
3
  base_plan_id: null | string;
4
4
  id: null | string;
5
5
  product_id: null | string;
6
- }
7
- export interface AppSummaryDTO {
6
+ };
7
+ export type AppSummaryDTO = {
8
8
  id: string;
9
9
  sdk_key: null | string;
10
10
  title: string;
11
- }
12
- export interface AppDetailDTO {
11
+ };
12
+ export type AppDetailDTO = {
13
13
  apple_bundle_id: null | string;
14
14
  google_bundle_id: null | string;
15
15
  id: string;
@@ -17,37 +17,37 @@ export interface AppDetailDTO {
17
17
  sdk_key: null | string;
18
18
  secret_key: null | string;
19
19
  title: string;
20
- }
21
- export interface AppCreateRequestDTO {
20
+ };
21
+ export type AppCreateRequestDTO = {
22
22
  apple_bundle_id?: null | string;
23
23
  google_bundle_id?: null | string;
24
24
  title: string;
25
- }
26
- export interface AppUpdateRequestDTO {
25
+ };
26
+ export type AppUpdateRequestDTO = {
27
27
  apple_bundle_id?: null | string;
28
28
  google_bundle_id?: null | string;
29
29
  title?: null | string;
30
- }
31
- export interface AccessLevelDTO {
30
+ };
31
+ export type AccessLevelDTO = {
32
32
  id: string;
33
33
  sdk_id: string;
34
34
  title: null | string;
35
- }
36
- export interface AccessLevelCreateRequestDTO {
35
+ };
36
+ export type AccessLevelCreateRequestDTO = {
37
37
  sdk_id: string;
38
38
  title?: null | string;
39
- }
40
- export interface AccessLevelUpdateRequestDTO {
39
+ };
40
+ export type AccessLevelUpdateRequestDTO = {
41
41
  title?: null | string;
42
- }
43
- export interface ProductDTO {
42
+ };
43
+ export type ProductDTO = {
44
44
  access_level_id: string;
45
45
  id: string;
46
46
  period: ProductPeriod;
47
47
  title: string;
48
48
  vendor_products: Record<string, VendorProductDTO>;
49
- }
50
- export interface ProductCreateRequestDTO {
49
+ };
50
+ export type ProductCreateRequestDTO = {
51
51
  access_level_id: string;
52
52
  android_base_plan_id?: null | string;
53
53
  android_product_id?: null | string;
@@ -59,33 +59,33 @@ export interface ProductCreateRequestDTO {
59
59
  stripe_price_id?: null | string;
60
60
  stripe_product_id?: null | string;
61
61
  title: string;
62
- }
63
- export interface ProductUpdateRequestDTO {
62
+ };
63
+ export type ProductUpdateRequestDTO = {
64
64
  access_level_id: string;
65
65
  title: string;
66
- }
67
- export interface PaywallDTO {
66
+ };
67
+ export type PaywallDTO = {
68
68
  id: string;
69
69
  product_ids: string[];
70
70
  title: string;
71
- }
72
- export interface PaywallWriteRequestDTO {
71
+ };
72
+ export type PaywallWriteRequestDTO = {
73
73
  product_ids?: string[];
74
74
  title: string;
75
- }
75
+ };
76
76
  /** Flow lifecycle status. Values come from the server-side `FlowStatus` enum (e.g. draft, dirty, published). */
77
77
  export type FlowStatus = string;
78
- export interface FlowDTO {
78
+ export type FlowDTO = {
79
79
  id: string;
80
80
  name: string;
81
81
  status: FlowStatus;
82
82
  updated_at: string;
83
- }
84
- export interface FlowRemoteConfigDTO {
83
+ };
84
+ export type FlowRemoteConfigDTO = {
85
85
  data: string;
86
86
  locale: string;
87
- }
88
- export interface FlowConfigDTO {
87
+ };
88
+ export type FlowConfigDTO = {
89
89
  config: Record<string, unknown>;
90
90
  /** Human message for a failed publication (raw passthrough from the flow version). */
91
91
  publication_error?: null | string;
@@ -97,77 +97,77 @@ export interface FlowConfigDTO {
97
97
  transform_error?: null | string;
98
98
  /** Millisecond timestamp of the last content change; the value `expected_updated_at` is compared against on write. */
99
99
  updated_at: number;
100
- }
101
- export interface FlowWriteRequestDTO {
100
+ };
101
+ export type FlowWriteRequestDTO = {
102
102
  name: string;
103
- }
104
- export interface FlowConfigWriteRequestDTO {
103
+ };
104
+ export type FlowConfigWriteRequestDTO = {
105
105
  config: Record<string, unknown>;
106
106
  /** Optimistic lock: the `updated_at` from a prior config read. Omit for last-write-wins. */
107
107
  expected_updated_at?: null | number;
108
108
  remote_configs?: FlowRemoteConfigDTO[];
109
- }
110
- export interface FlowConfigValidateRequestDTO {
109
+ };
110
+ export type FlowConfigValidateRequestDTO = {
111
111
  config: Record<string, unknown>;
112
- }
113
- export interface FlowConfigIssueDTO {
112
+ };
113
+ export type FlowConfigIssueDTO = {
114
114
  /** Machine code, relayed from the transformer; absent until it reports path-level diagnostics. */
115
115
  code?: null | string;
116
116
  message: string;
117
117
  /** Location of the issue; absent until the transformer reports path-level diagnostics. */
118
118
  path?: null | string;
119
119
  severity: string;
120
- }
121
- export interface FlowConfigValidationDTO {
120
+ };
121
+ export type FlowConfigValidationDTO = {
122
122
  issues: FlowConfigIssueDTO[];
123
123
  valid: boolean;
124
- }
125
- export interface MediaDTO {
124
+ };
125
+ export type MediaDTO = {
126
126
  id: number;
127
127
  name: string;
128
128
  /** Base64-encoded preview thumbnail; absent when no preview was generated. */
129
129
  preview_base64?: null | string;
130
130
  /** CDN URL to reference from a flow config. */
131
131
  url: string;
132
- }
133
- export interface SegmentDTO {
132
+ };
133
+ export type SegmentDTO = {
134
134
  description: null | string;
135
135
  id: string;
136
136
  title: string;
137
- }
138
- export interface PlacementPaywallAudienceEntryDTO {
137
+ };
138
+ export type PlacementPaywallAudienceEntryDTO = {
139
139
  content_type: 'paywall';
140
140
  paywall_id: string;
141
141
  priority: number;
142
142
  segment_ids?: string[];
143
- }
144
- export interface PlacementFlowAudienceEntryDTO {
143
+ };
144
+ export type PlacementFlowAudienceEntryDTO = {
145
145
  content_type: 'flow';
146
146
  /** The flow must be `published` (not draft) — the backend returns 400 otherwise. */
147
147
  flow_id: string;
148
148
  priority: number;
149
149
  segment_ids?: string[];
150
- }
150
+ };
151
151
  export type PlacementAudienceEntryDTO = PlacementFlowAudienceEntryDTO | PlacementPaywallAudienceEntryDTO;
152
- export interface PlacementSummaryDTO {
152
+ export type PlacementSummaryDTO = {
153
153
  developer_id: string;
154
154
  id: string;
155
155
  /** Placement activation state: true = Live, false = Inactive. */
156
156
  is_active: boolean;
157
157
  title: string;
158
- }
159
- export interface PlacementDetailDTO {
158
+ };
159
+ export type PlacementDetailDTO = {
160
160
  audiences?: PlacementAudienceEntryDTO[];
161
161
  developer_id: string;
162
162
  id: string;
163
163
  /** Placement activation state: true = Live, false = Inactive. */
164
164
  is_active: boolean;
165
165
  title: string;
166
- }
167
- export interface PlacementWriteRequestDTO {
166
+ };
167
+ export type PlacementWriteRequestDTO = {
168
168
  audiences: null | PlacementAudienceEntryDTO[];
169
169
  developer_id: string;
170
170
  /** @deprecated use `audiences` */
171
171
  paywall_id: null | string;
172
172
  title: string;
173
- }
173
+ };
@@ -20,8 +20,9 @@ export function appUrl(path) {
20
20
  * ADAPTY_APP_URL the link is left exactly as issued — the API is free to serve it from any host.
21
21
  */
22
22
  export function onAppHost(issuedUrl) {
23
- if (!process.env[APP_URL_ENV_VAR])
23
+ if (!process.env[APP_URL_ENV_VAR]) {
24
24
  return issuedUrl;
25
+ }
25
26
  let issued;
26
27
  try {
27
28
  issued = new URL(issuedUrl);
@@ -1,16 +1,21 @@
1
+ import { ApiClient } from './api-client.js';
2
+ import type { QueryParams } from './api-client.js';
1
3
  import type { Config } from '@oclif/core';
2
- import { ApiClient, type QueryParams } from './api-client.js';
3
4
  export declare const ASA_API_URL = "https://api-asa-admin.adapty.io/api/v1/cli";
4
5
  export declare const ASA_API_URL_ENV_VAR = "ADAPTY_ASA_API_URL";
5
- export declare function createAsaClient(config: Config): Promise<ApiClient>;
6
- export interface AsaWriteOptions {
6
+ export type AsaCommandContext = {
7
+ config: Config;
8
+ jsonEnabled: () => boolean;
9
+ };
10
+ export declare function createAsaClient(command: AsaCommandContext): Promise<ApiClient>;
11
+ export type AsaWriteOptions = {
7
12
  body?: unknown;
8
- idempotencyKey?: string;
9
- params?: QueryParams;
10
- }
11
- export interface AsaWriteOutcome<T> {
13
+ idempotencyKey?: string | undefined;
14
+ params?: QueryParams | undefined;
15
+ };
16
+ export type AsaWriteOutcome<T> = {
12
17
  replayed: boolean;
13
18
  result: T;
14
- }
19
+ };
15
20
  export declare function asaWrite<T>(client: ApiClient, method: 'post' | 'put', path: string, opts?: AsaWriteOptions): Promise<AsaWriteOutcome<T>>;
16
21
  export declare function noteReplay(replayed: boolean, log: (msg: string) => void): void;
@@ -5,13 +5,16 @@ import { buildUserAgent } from './client-from-config.js';
5
5
  import { AuthRequiredError, NetworkError } from './errors.js';
6
6
  export const ASA_API_URL = 'https://api-asa-admin.adapty.io/api/v1/cli';
7
7
  export const ASA_API_URL_ENV_VAR = 'ADAPTY_ASA_API_URL';
8
- export async function createAsaClient(config) {
8
+ export async function createAsaClient(command) {
9
+ const { config } = command;
9
10
  const token = await resolveToken(config.configDir);
10
- if (!token)
11
+ if (!token) {
11
12
  throw new AuthRequiredError();
13
+ }
12
14
  return new ApiClient({
13
15
  defaultBaseUrl: ASA_API_URL,
14
16
  errorFormat: 'asa',
17
+ quiet: command.jsonEnabled(),
15
18
  token,
16
19
  urlEnvVar: ASA_API_URL_ENV_VAR,
17
20
  userAgent: buildUserAgent(config),
@@ -34,13 +37,15 @@ export async function asaWrite(client, method, path, opts = {}) {
34
37
  return { replayed, result };
35
38
  }
36
39
  catch (error) {
37
- if (!(error instanceof NetworkError))
40
+ if (!(error instanceof NetworkError)) {
38
41
  throw error;
42
+ }
39
43
  const result = await send();
40
44
  return { replayed, result };
41
45
  }
42
46
  }
43
47
  export function noteReplay(replayed, log) {
44
- if (replayed)
48
+ if (replayed) {
45
49
  log('Already applied earlier — showing the stored result.');
50
+ }
46
51
  }
@@ -44,17 +44,31 @@ export declare const adScopeFlags: {
44
44
  campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
45
45
  'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
46
46
  };
47
+ export declare const metricsScopeFlags: {
48
+ 'ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
49
+ app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
50
+ campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
51
+ };
52
+ export declare function metricsScopeBody(flags: {
53
+ 'ad-group'?: string[] | undefined;
54
+ 'app'?: string[] | undefined;
55
+ 'campaign'?: string[] | undefined;
56
+ }): {
57
+ campaign_id?: string[];
58
+ app_id?: string[];
59
+ ad_group_id?: string[];
60
+ };
47
61
  export declare const statusFilter: (options: string[]) => {
48
62
  status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
49
63
  };
50
- interface ScopeFlags {
51
- 'ad-group'?: string[];
52
- app?: string[];
53
- campaign?: string[];
54
- 'campaign-group'?: string[];
55
- search?: string;
56
- status?: string;
57
- }
64
+ type ScopeFlags = {
65
+ 'ad-group'?: string[] | undefined;
66
+ 'app'?: string[] | undefined;
67
+ 'campaign'?: string[] | undefined;
68
+ 'campaign-group'?: string[] | undefined;
69
+ 'search'?: string | undefined;
70
+ 'status'?: string | undefined;
71
+ };
58
72
  export declare function scopeParams(flags: ScopeFlags): QueryParams;
59
73
  export declare const scheduleFlags: {
60
74
  'end-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -66,8 +80,8 @@ export declare const pricingModelFlag: {
66
80
  export declare function startOfDayUtc(date: string | undefined): string | undefined;
67
81
  export declare function todayUtc(): string;
68
82
  export declare function periodParams(flags: {
69
- 'date-from'?: string;
70
- 'date-to'?: string;
83
+ 'date-from'?: string | undefined;
84
+ 'date-to'?: string | undefined;
71
85
  }): Record<string, string>;
72
86
  export declare function moneyFlag(description: string, opts?: {
73
87
  required?: boolean;
@@ -75,6 +89,16 @@ export declare function moneyFlag(description: string, opts?: {
75
89
  export declare const currencyFlag: {
76
90
  currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
77
91
  };
92
+ export declare const addKeywordActionFlags: {
93
+ 'cpt-bid': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
94
+ 'cpt-bid-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
95
+ 'match-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
96
+ negate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
97
+ 'no-negate': import("@oclif/core/interfaces").BooleanFlag<boolean>;
98
+ 'pause-original': import("@oclif/core/interfaces").BooleanFlag<boolean>;
99
+ 'skip-enable-duplicates': import("@oclif/core/interfaces").BooleanFlag<boolean>;
100
+ 'target-ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
101
+ };
78
102
  export declare const idempotencyFlags: {
79
103
  'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
80
104
  };
@@ -94,17 +118,17 @@ export declare const invoiceFlags: {
94
118
  'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
95
119
  'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
96
120
  };
97
- export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
121
+ export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string | undefined>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
98
122
  export declare function reportServingState(campaign: null | {
99
123
  internal_id: string;
100
124
  serving_state_reasons?: null | string[];
101
125
  serving_status?: null | string;
102
126
  }, warn: (msg: string) => void): void;
103
- interface BulkOutcome {
127
+ type BulkOutcome = {
104
128
  applied: unknown[];
105
129
  errors: AsaMutationError[];
106
130
  isValidationFailure: boolean;
107
131
  kind: string;
108
- }
132
+ };
109
133
  export declare function reportBulkOutcome({ applied, errors, isValidationFailure, kind }: BulkOutcome, log: (msg: string) => void): void;
110
134
  export {};
@@ -1,4 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
+ import { CPT_BID_TYPES, KEYWORD_MATCH_TYPES, NEGATE_TYPES } from './asa-keyword-action.js';
2
3
  import { describeListedError } from './errors.js';
3
4
  import { isValidUuid } from './flags.js';
4
5
  const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
@@ -8,7 +9,7 @@ export const MAX_BY_DAYS = 16;
8
9
  export const ASA_METRIC_ENTITIES = ['ad', 'ad-group', 'campaign', 'keyword'];
9
10
  export const ASA_GROUP_BY_DIMENSIONS = ['country', 'day', 'month', 'quarter', 'week', 'year'];
10
11
  export const asaPaginationFlags = {
11
- page: Flags.integer({
12
+ 'page': Flags.integer({
12
13
  default: 1,
13
14
  description: 'Page number',
14
15
  min: 1,
@@ -26,9 +27,11 @@ export const byDaysFlag = {
26
27
  multiple: true,
27
28
  }),
28
29
  };
30
+ // eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
29
31
  export async function parseDate(input) {
30
- if (!DATE_REGEX.test(input))
32
+ if (!DATE_REGEX.test(input)) {
31
33
  throw new Error('Dates must be written as YYYY-MM-DD.');
34
+ }
32
35
  return input;
33
36
  }
34
37
  export const periodFlags = {
@@ -41,9 +44,11 @@ export const periodFlags = {
41
44
  parse: parseDate,
42
45
  }),
43
46
  };
47
+ // eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
44
48
  async function parseId(input) {
45
- if (!isValidUuid(input))
49
+ if (!isValidUuid(input)) {
46
50
  throw new Error('Ids are the UUIDs printed by the matching list command.');
51
+ }
47
52
  return input;
48
53
  }
49
54
  function idFilter(entity) {
@@ -51,7 +56,7 @@ function idFilter(entity) {
51
56
  }
52
57
  const searchFilter = { search: Flags.string({ description: 'Case-insensitive substring match on the name' }) };
53
58
  export const assetScopeFlags = {
54
- app: idFilter('app'),
59
+ 'app': idFilter('app'),
55
60
  'campaign-group': idFilter('campaign group'),
56
61
  };
57
62
  export const orgScopeFlags = { ...assetScopeFlags, ...searchFilter };
@@ -59,10 +64,22 @@ export const campaignScopeFlags = { ...orgScopeFlags, campaign: idFilter('campai
59
64
  export const adGroupScopeFlags = { ...campaignScopeFlags, 'ad-group': idFilter('ad group') };
60
65
  export const adScopeFlags = {
61
66
  'ad-group': idFilter('ad group'),
62
- campaign: idFilter('campaign'),
67
+ 'campaign': idFilter('campaign'),
63
68
  'campaign-group': idFilter('campaign group'),
64
69
  ...searchFilter,
65
70
  };
71
+ export const metricsScopeFlags = {
72
+ 'ad-group': idFilter('ad group'),
73
+ 'app': idFilter('app'),
74
+ 'campaign': idFilter('campaign'),
75
+ };
76
+ export function metricsScopeBody(flags) {
77
+ return {
78
+ ...(flags['ad-group'] === undefined ? {} : { ad_group_id: flags['ad-group'] }),
79
+ ...(flags.app === undefined ? {} : { app_id: flags.app }),
80
+ ...(flags.campaign === undefined ? {} : { campaign_id: flags.campaign }),
81
+ };
82
+ }
66
83
  export const statusFilter = (options) => ({
67
84
  status: Flags.string({ description: 'Keep only rows in this state', options }),
68
85
  });
@@ -95,27 +112,72 @@ export function todayUtc() {
95
112
  }
96
113
  export function periodParams(flags) {
97
114
  const params = {};
98
- if (flags['date-from'])
115
+ if (flags['date-from']) {
99
116
  params.date_from = flags['date-from'];
100
- if (flags['date-to'])
117
+ }
118
+ if (flags['date-to']) {
101
119
  params.date_to = flags['date-to'];
120
+ }
102
121
  return params;
103
122
  }
123
+ // eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
104
124
  async function parseMoney(input) {
105
- if (!MONEY_REGEX.test(input))
125
+ if (!MONEY_REGEX.test(input)) {
106
126
  throw new Error('Amounts must be plain numbers, e.g. 50 or 12.50.');
127
+ }
107
128
  return input;
108
129
  }
109
130
  export function moneyFlag(description, opts = {}) {
110
131
  return Flags.string({
111
132
  description: `${description} (amount, e.g. 50 or 12.50)`,
112
133
  parse: parseMoney,
113
- required: opts.required,
134
+ ...(opts.required === undefined ? {} : { required: opts.required }),
114
135
  });
115
136
  }
116
137
  export const currencyFlag = {
117
138
  currency: Flags.string({ default: 'USD', description: 'Currency code for the amounts in this call' }),
118
139
  };
140
+ // Params of the add-as-keyword-to action, shared by `automations create` and `automations update`.
141
+ // The API picks the params variant by shape, so which of these apply depends on the rule's
142
+ // operate_with: --negate/--no-negate/--skip-enable-duplicates are search-term only,
143
+ // --pause-original is targeting-keyword only. See lib/asa-keyword-action.ts.
144
+ export const addKeywordActionFlags = {
145
+ 'cpt-bid': Flags.string({
146
+ description: 'cpt_bid.value: the bid itself with --cpt-bid-type set_to, or a percent markup on the entity bid with '
147
+ + 'search_term_current_cpt / keyword_current_bid; not accepted with ad_group_default_bid',
148
+ parse: parseMoney,
149
+ }),
150
+ 'cpt-bid-type': Flags.string({
151
+ description: 'Where the bid of the created keyword comes from; the API has no default',
152
+ options: CPT_BID_TYPES,
153
+ }),
154
+ 'match-type': Flags.string({
155
+ description: 'Match type of the created keyword; the API has no default',
156
+ options: KEYWORD_MATCH_TYPES,
157
+ }),
158
+ 'negate': Flags.string({
159
+ description: 'Also add the search term as a negative keyword at this level (search-term rules)',
160
+ exclusive: ['no-negate'],
161
+ options: NEGATE_TYPES,
162
+ }),
163
+ 'no-negate': Flags.boolean({
164
+ description: 'Leave no negative keyword behind (search-term rules)',
165
+ exclusive: ['negate'],
166
+ }),
167
+ 'pause-original': Flags.boolean({
168
+ allowNo: true,
169
+ description: 'Pause the source keyword in its own ad group (targeting-keyword rules)',
170
+ }),
171
+ 'skip-enable-duplicates': Flags.boolean({
172
+ allowNo: true,
173
+ description: 'Skip a keyword that already exists in the target ad group instead of enabling it (search-term rules)',
174
+ }),
175
+ 'target-ad-group': Flags.string({
176
+ description: 'Ad group the keyword is added to (UUID), repeatable; a rule without one does nothing',
177
+ multiple: true,
178
+ parse: parseId,
179
+ }),
180
+ };
119
181
  export const idempotencyFlags = {
120
182
  'idempotency-key': Flags.string({
121
183
  description: 'Idempotency key for this write; re-running with the same key replays the stored result instead of applying twice',
@@ -144,32 +206,37 @@ export const invoiceFlags = {
144
206
  };
145
207
  export function locInvoiceDetails(flags) {
146
208
  const names = Object.keys(INVOICE_FIELDS);
147
- const missing = names.filter((name) => flags[name] === undefined);
148
- if (missing.length === names.length)
209
+ const missing = names.filter(name => flags[name] === undefined);
210
+ if (missing.length === names.length) {
149
211
  return undefined;
212
+ }
150
213
  if (missing.length > 0) {
151
- throw new Error(`Invoicing Options need all five flags; missing: ${missing.map((name) => `--${name}`).join(', ')}`);
214
+ throw new Error(`Invoicing Options need all five flags; missing: ${missing.map(name => `--${name}`).join(', ')}`);
152
215
  }
153
- return Object.fromEntries(names.map((name) => [INVOICE_FIELDS[name], flags[name]]));
216
+ return Object.fromEntries(names.map(name => [INVOICE_FIELDS[name], flags[name]]));
154
217
  }
155
218
  const SERVING_HINTS = {
156
- AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING: (campaignId) => `Max Conversions campaign needs an automated ad group: adapty asa ad-groups create --campaign ${campaignId} --name "Automated Max Conv" --automated`,
157
- MISSING_BO_OR_INVOICING_FIELDS: (campaignId) => `This organization bills by line of credit — add Invoicing Options: adapty asa campaigns update ${campaignId} --invoice-advertiser ... --invoice-order-number ... --invoice-contact-name ... --invoice-contact-email ... --invoice-billing-email ...`,
219
+ AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING: campaignId => `Max Conversions campaign needs an automated ad group: adapty asa ad-groups create --campaign ${campaignId} --name "Automated Max Conv" --automated`,
220
+ MISSING_BO_OR_INVOICING_FIELDS: campaignId => `This organization bills by line of credit — add Invoicing Options: adapty asa campaigns update ${campaignId} --invoice-advertiser ... --invoice-order-number ... --invoice-contact-name ... --invoice-contact-email ... --invoice-billing-email ...`,
158
221
  };
159
222
  export function reportServingState(campaign, warn) {
160
- if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length)
223
+ if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length) {
161
224
  return;
225
+ }
162
226
  const reasons = campaign.serving_state_reasons;
163
- if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER')
227
+ if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER') {
164
228
  return;
229
+ }
165
230
  for (const reason of reasons) {
166
231
  const hint = SERVING_HINTS[reason];
167
- if (hint)
232
+ if (hint) {
168
233
  warn(hint(campaign.internal_id));
234
+ }
169
235
  }
170
- const other = reasons.filter((reason) => !SERVING_HINTS[reason]);
171
- if (other.length > 0)
236
+ const other = reasons.filter(reason => !SERVING_HINTS[reason]);
237
+ if (other.length > 0) {
172
238
  warn(`Not serving: ${other.join(', ')}`);
239
+ }
173
240
  }
174
241
  export function reportBulkOutcome({ applied, errors, isValidationFailure, kind }, log) {
175
242
  if (isValidationFailure) {
@@ -0,0 +1,18 @@
1
+ export declare const ADD_AS_KEYWORD_ACTION = "add-as-keyword-to";
2
+ export declare const CPT_BID_TYPES: string[];
3
+ export declare const KEYWORD_MATCH_TYPES: string[];
4
+ export declare const NEGATE_TYPES: string[];
5
+ export type AddKeywordActionFlags = {
6
+ 'cpt-bid'?: string | undefined;
7
+ 'cpt-bid-type'?: string | undefined;
8
+ 'match-type'?: string | undefined;
9
+ 'negate'?: string | undefined;
10
+ 'no-negate'?: boolean | undefined;
11
+ 'pause-original'?: boolean | undefined;
12
+ 'skip-enable-duplicates'?: boolean | undefined;
13
+ 'target-ad-group'?: string[] | undefined;
14
+ };
15
+ export declare function hasAddKeywordActionFlags(flags: AddKeywordActionFlags): boolean;
16
+ export declare function buildAddKeywordParams(operateWith: unknown, stored: unknown, flags: AddKeywordActionFlags): Record<string, unknown>;
17
+ export declare function requireSingleAction(actions: unknown): Record<string, unknown>;
18
+ export declare function rebuildAddKeywordAction(actions: unknown, operateWith: unknown, flags: AddKeywordActionFlags): Record<string, unknown>[];