adapty 0.8.4 → 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 (105) hide show
  1. package/README.md +1 -1
  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.js +9 -6
  22. package/dist/commands/asa/automations/get.js +3 -2
  23. package/dist/commands/asa/automations/list.d.ts +1 -1
  24. package/dist/commands/asa/automations/list.js +1 -1
  25. package/dist/commands/asa/automations/run.js +5 -3
  26. package/dist/commands/asa/automations/runs.d.ts +1 -1
  27. package/dist/commands/asa/automations/runs.js +4 -4
  28. package/dist/commands/asa/automations/update.js +17 -9
  29. package/dist/commands/asa/campaigns/bulk-create.js +31 -18
  30. package/dist/commands/asa/campaigns/bulk-list.js +7 -5
  31. package/dist/commands/asa/campaigns/bulk-status.js +3 -2
  32. package/dist/commands/asa/campaigns/create.js +10 -8
  33. package/dist/commands/asa/campaigns/get.js +3 -2
  34. package/dist/commands/asa/campaigns/list.d.ts +1 -1
  35. package/dist/commands/asa/campaigns/list.js +1 -1
  36. package/dist/commands/asa/campaigns/update.js +25 -15
  37. package/dist/commands/asa/competitors/summary.js +5 -5
  38. package/dist/commands/asa/connect.js +5 -3
  39. package/dist/commands/asa/creatives/list.d.ts +1 -1
  40. package/dist/commands/asa/creatives/list.js +1 -1
  41. package/dist/commands/asa/keywords/add.js +13 -10
  42. package/dist/commands/asa/keywords/list.d.ts +1 -1
  43. package/dist/commands/asa/keywords/list.js +1 -1
  44. package/dist/commands/asa/keywords/update.js +15 -10
  45. package/dist/commands/asa/metrics/index.d.ts +2 -2
  46. package/dist/commands/asa/metrics/index.js +22 -8
  47. package/dist/commands/asa/metrics/overview.js +3 -3
  48. package/dist/commands/asa/negative-keywords/add.js +10 -7
  49. package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
  50. package/dist/commands/asa/negative-keywords/list.js +1 -1
  51. package/dist/commands/asa/orgs/list.d.ts +1 -1
  52. package/dist/commands/asa/orgs/list.js +1 -1
  53. package/dist/commands/asa/product-pages/list.d.ts +1 -1
  54. package/dist/commands/asa/product-pages/list.js +1 -1
  55. package/dist/commands/asa/product-pages/sync.js +1 -1
  56. package/dist/commands/asa/search-terms/list.d.ts +1 -1
  57. package/dist/commands/asa/search-terms/list.js +1 -1
  58. package/dist/commands/asa/whoami.js +7 -2
  59. package/dist/commands/auth/login.js +3 -1
  60. package/dist/commands/flows/config/get.js +2 -2
  61. package/dist/commands/flows/config/preview.d.ts +2 -2
  62. package/dist/commands/flows/config/preview.js +6 -4
  63. package/dist/commands/flows/config/update.js +5 -3
  64. package/dist/commands/flows/config/validate.js +5 -3
  65. package/dist/commands/flows/list.d.ts +1 -1
  66. package/dist/commands/flows/media/upload.js +1 -1
  67. package/dist/commands/flows/publish.js +5 -5
  68. package/dist/commands/paywalls/create.js +1 -1
  69. package/dist/commands/paywalls/list.d.ts +1 -1
  70. package/dist/commands/paywalls/placements.d.ts +2 -2
  71. package/dist/commands/paywalls/update.js +1 -1
  72. package/dist/commands/placements/create.js +17 -12
  73. package/dist/commands/placements/list.d.ts +1 -1
  74. package/dist/commands/placements/update.js +20 -15
  75. package/dist/commands/products/create.js +20 -13
  76. package/dist/commands/products/list.d.ts +1 -1
  77. package/dist/commands/products/update.js +1 -1
  78. package/dist/commands/segments/list.d.ts +1 -1
  79. package/dist/lib/api-client.d.ts +10 -6
  80. package/dist/lib/api-client.js +56 -35
  81. package/dist/lib/api-schemas.d.ts +56 -56
  82. package/dist/lib/app-url.js +2 -1
  83. package/dist/lib/asa-client.d.ts +13 -8
  84. package/dist/lib/asa-client.js +9 -4
  85. package/dist/lib/asa-flags.d.ts +19 -19
  86. package/dist/lib/asa-flags.js +38 -25
  87. package/dist/lib/asa-keyword-action.d.ts +10 -10
  88. package/dist/lib/asa-keyword-action.js +40 -25
  89. package/dist/lib/asa-schemas.d.ts +100 -80
  90. package/dist/lib/auth.js +2 -1
  91. package/dist/lib/client-from-config.d.ts +1 -1
  92. package/dist/lib/client-from-config.js +2 -1
  93. package/dist/lib/config.d.ts +2 -2
  94. package/dist/lib/confirm.d.ts +4 -4
  95. package/dist/lib/confirm.js +12 -7
  96. package/dist/lib/errors.d.ts +18 -18
  97. package/dist/lib/errors.js +12 -9
  98. package/dist/lib/flags.d.ts +3 -3
  99. package/dist/lib/flags.js +2 -1
  100. package/dist/lib/flow-help.js +11 -6
  101. package/dist/lib/output.js +23 -12
  102. package/dist/lib/preview.d.ts +5 -5
  103. package/dist/lib/preview.js +2 -1
  104. package/oclif.manifest.json +131 -131
  105. package/package.json +10 -8
@@ -17,11 +17,11 @@ export default class PlacementsUpdate extends Command {
17
17
  ];
18
18
  static flags = {
19
19
  ...appFlag,
20
- audiences: Flags.string({
21
- description: 'JSON array of audience entries. Every entry needs an explicit content_type. ' +
22
- 'Paywall: {content_type:"paywall", segment_ids, paywall_id, priority}. ' +
23
- 'Flow: {content_type:"flow", segment_ids, flow_id, priority}. ' +
24
- 'A flow must be published first (flows publish) — attaching a draft flow returns 400.',
20
+ 'audiences': Flags.string({
21
+ description: 'JSON array of audience entries. Every entry needs an explicit content_type. '
22
+ + 'Paywall: {content_type:"paywall", segment_ids, paywall_id, priority}. '
23
+ + 'Flow: {content_type:"flow", segment_ids, flow_id, priority}. '
24
+ + 'A flow must be published first (flows publish) — attaching a draft flow returns 400.',
25
25
  exactlyOne: ['paywall-id', 'audiences'],
26
26
  }),
27
27
  'developer-id': Flags.string({ description: 'Developer ID for the placement', required: true }),
@@ -29,7 +29,7 @@ export default class PlacementsUpdate extends Command {
29
29
  description: 'Paywall ID (UUID). DEPRECATED: use --audiences.',
30
30
  exactlyOne: ['paywall-id', 'audiences'],
31
31
  }),
32
- title: Flags.string({ description: 'Placement title', required: true }),
32
+ 'title': Flags.string({ description: 'Placement title', required: true }),
33
33
  };
34
34
  async run() {
35
35
  const { args, flags } = await this.parse(PlacementsUpdate);
@@ -43,16 +43,18 @@ export default class PlacementsUpdate extends Command {
43
43
  title: flags.title,
44
44
  };
45
45
  if (flags['paywall-id']) {
46
- process.stderr.write('⚠️ --paywall-id is deprecated. Use --audiences instead.\n' +
47
- ' `paywall_id` will be removed from the API in a future release.\n');
48
- process.stderr.write('⚠️ --paywall-id will rewrite all audiences on this placement.\n' +
49
- ' If the placement has segment-specific paywalls, they will be replaced\n' +
50
- ' by a single default audience. Use --audiences to preserve them.\n');
46
+ process.stderr.write('⚠️ --paywall-id is deprecated. Use --audiences instead.\n'
47
+ + ' `paywall_id` will be removed from the API in a future release.\n');
48
+ process.stderr.write('⚠️ --paywall-id will rewrite all audiences on this placement.\n'
49
+ + ' If the placement has segment-specific paywalls, they will be replaced\n'
50
+ + ' by a single default audience. Use --audiences to preserve them.\n');
51
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- FIXME if you see this
51
52
  body.paywall_id = flags['paywall-id'];
52
53
  }
53
54
  else {
54
55
  let parsed;
55
56
  try {
57
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- FIXME if you see this
56
58
  parsed = JSON.parse(flags.audiences);
57
59
  }
58
60
  catch (error) {
@@ -61,12 +63,14 @@ export default class PlacementsUpdate extends Command {
61
63
  if (!Array.isArray(parsed)) {
62
64
  this.error('--audiences must be a JSON array of audience entries.', { exit: 2 });
63
65
  }
64
- for (const [index, entry] of parsed.entries()) {
66
+ const entries = parsed;
67
+ for (const [index, entry] of entries.entries()) {
65
68
  const problem = audienceEntryProblem(entry);
66
- if (problem)
69
+ if (problem) {
67
70
  this.error(`--audiences[${index}]: ${problem}`, { exit: 2 });
71
+ }
68
72
  }
69
- body.audiences = parsed;
73
+ body.audiences = entries;
70
74
  }
71
75
  const client = await createAuthenticatedClient(this.config);
72
76
  let result;
@@ -75,8 +79,9 @@ export default class PlacementsUpdate extends Command {
75
79
  }
76
80
  catch (error) {
77
81
  const message = draftFlowError(error, flags.app, body.audiences);
78
- if (message)
82
+ if (message) {
79
83
  this.error(message, { exit: 2 });
84
+ }
80
85
  throw error;
81
86
  }
82
87
  this.log('Placement updated!');
@@ -26,13 +26,13 @@ export default class ProductsCreate extends Command {
26
26
  'ios-product-id': Flags.string({ description: 'iOS product ID', helpGroup: 'STORE BINDINGS' }),
27
27
  'paddle-price-id': Flags.string({ description: 'Paddle price ID (requires --paddle-product-id)', helpGroup: 'STORE BINDINGS' }),
28
28
  'paddle-product-id': Flags.string({ description: 'Paddle product ID (requires --paddle-price-id)', helpGroup: 'STORE BINDINGS' }),
29
- period: Flags.string({
29
+ 'period': Flags.string({
30
30
  description: 'Subscription period (weekly, monthly, two_months, trimonthly, semiannual, annual, lifetime)',
31
31
  required: true,
32
32
  }),
33
33
  'stripe-price-id': Flags.string({ description: 'Stripe price ID (requires --stripe-product-id)', helpGroup: 'STORE BINDINGS' }),
34
34
  'stripe-product-id': Flags.string({ description: 'Stripe product ID (requires --stripe-price-id)', helpGroup: 'STORE BINDINGS' }),
35
- title: Flags.string({ description: 'Product title', required: true }),
35
+ 'title': Flags.string({ description: 'Product title', required: true }),
36
36
  };
37
37
  async run() {
38
38
  const { flags } = await this.parse(ProductsCreate);
@@ -45,10 +45,10 @@ export default class ProductsCreate extends Command {
45
45
  if (xor(flags['paddle-product-id'], flags['paddle-price-id'])) {
46
46
  this.error('--paddle-product-id and --paddle-price-id are required together', { exit: 2 });
47
47
  }
48
- if (!flags['ios-product-id'] &&
49
- !flags['android-product-id'] &&
50
- !flags['stripe-product-id'] &&
51
- !flags['paddle-product-id']) {
48
+ if (!flags['ios-product-id']
49
+ && !flags['android-product-id']
50
+ && !flags['stripe-product-id']
51
+ && !flags['paddle-product-id']) {
52
52
  this.error('At least one store binding is required (ios/android/stripe/paddle)', { exit: 2 });
53
53
  }
54
54
  if (flags['android-product-id'] && !flags['android-base-plan-id'] && flags.period !== 'lifetime') {
@@ -60,20 +60,27 @@ export default class ProductsCreate extends Command {
60
60
  period: flags.period,
61
61
  title: flags.title,
62
62
  };
63
- if (flags['ios-product-id'])
63
+ if (flags['ios-product-id']) {
64
64
  body.ios_product_id = flags['ios-product-id'];
65
- if (flags['android-product-id'])
65
+ }
66
+ if (flags['android-product-id']) {
66
67
  body.android_product_id = flags['android-product-id'];
67
- if (flags['android-base-plan-id'])
68
+ }
69
+ if (flags['android-base-plan-id']) {
68
70
  body.android_base_plan_id = flags['android-base-plan-id'];
69
- if (flags['stripe-product-id'])
71
+ }
72
+ if (flags['stripe-product-id']) {
70
73
  body.stripe_product_id = flags['stripe-product-id'];
71
- if (flags['stripe-price-id'])
74
+ }
75
+ if (flags['stripe-price-id']) {
72
76
  body.stripe_price_id = flags['stripe-price-id'];
73
- if (flags['paddle-product-id'])
77
+ }
78
+ if (flags['paddle-product-id']) {
74
79
  body.paddle_product_id = flags['paddle-product-id'];
75
- if (flags['paddle-price-id'])
80
+ }
81
+ if (flags['paddle-price-id']) {
76
82
  body.paddle_price_id = flags['paddle-price-id'];
83
+ }
77
84
  const result = await client.post(`/apps/${flags.app}/products`, body);
78
85
  this.log('Product created!');
79
86
  printResponse(result, this.log.bind(this));
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import type { ProductDTO } from '../../lib/api-schemas.js';
3
- import { type PaginatedResponse } from '../../lib/flags.js';
3
+ import type { PaginatedResponse } from '../../lib/flags.js';
4
4
  export default class ProductsList extends Command {
5
5
  static description: string;
6
6
  static enableJsonFlag: boolean;
@@ -14,7 +14,7 @@ export default class ProductsUpdate extends Command {
14
14
  static flags = {
15
15
  ...appFlag,
16
16
  'access-level-id': Flags.string({ description: 'Access level ID (UUID)', required: true }),
17
- title: Flags.string({ description: 'Product title', required: true }),
17
+ 'title': Flags.string({ description: 'Product title', required: true }),
18
18
  };
19
19
  async run() {
20
20
  const { args, flags } = await this.parse(ProductsUpdate);
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import type { SegmentDTO } from '../../lib/api-schemas.js';
3
- import { type PaginatedResponse } from '../../lib/flags.js';
3
+ import type { PaginatedResponse } from '../../lib/flags.js';
4
4
  export default class SegmentsList extends Command {
5
5
  static description: string;
6
6
  static enableJsonFlag: boolean;
@@ -1,20 +1,22 @@
1
- import { type ApiErrorFormat } from './errors.js';
1
+ import type { ApiErrorFormat } from './errors.js';
2
2
  export type QueryParams = Record<string, string | string[] | undefined>;
3
- export interface RequestOptions {
3
+ export type RequestOptions = {
4
4
  headers?: Record<string, string>;
5
5
  onResponse?: (headers: Headers) => void;
6
- }
7
- export interface ApiClientOptions {
6
+ };
7
+ export type ApiClientOptions = {
8
8
  baseUrl?: string;
9
9
  defaultBaseUrl?: string;
10
10
  errorFormat?: ApiErrorFormat;
11
+ quiet?: boolean;
11
12
  token?: null | string;
12
13
  urlEnvVar?: string;
13
14
  userAgent?: string;
14
- }
15
+ };
15
16
  export declare class ApiClient {
16
17
  private baseUrl;
17
18
  private errorFormat;
19
+ private quiet;
18
20
  private token;
19
21
  private userAgent;
20
22
  constructor(opts?: ApiClientOptions);
@@ -22,7 +24,9 @@ export declare class ApiClient {
22
24
  post<T = unknown>(path: string, body?: unknown, params?: QueryParams, opts?: RequestOptions): Promise<T>;
23
25
  postForm<T = unknown>(path: string, form: FormData, params?: QueryParams, opts?: RequestOptions): Promise<T>;
24
26
  put<T = unknown>(path: string, body?: unknown, params?: QueryParams, opts?: RequestOptions): Promise<T>;
27
+ private buildHeaders;
25
28
  private buildUrl;
26
- private isRetryableRateLimit;
29
+ private isRetryableRefusal;
30
+ private readBody;
27
31
  private request;
28
32
  }
@@ -1,12 +1,16 @@
1
1
  import { ApiError, NetworkError, parseApiError } from './errors.js';
2
2
  const DEFAULT_API_URL = 'https://api-admin.adapty.io/api/v1/developer';
3
3
  const MAX_RETRY_AFTER_SECONDS = 60;
4
+ // 429 is the caller going too fast; 503 is a dependency being down. Both name a wait and clear up on their own.
5
+ const RETRYABLE_STATUSES = new Set([429, 503]);
4
6
  function ensureTrailingSlash(path) {
5
7
  return path.endsWith('/') ? path : `${path}/`;
6
8
  }
9
+ const NO_RETRIES = { network: false, refused: false };
7
10
  export class ApiClient {
8
11
  baseUrl;
9
12
  errorFormat;
13
+ quiet;
10
14
  token;
11
15
  userAgent;
12
16
  constructor(opts = {}) {
@@ -17,6 +21,7 @@ export class ApiClient {
17
21
  process.stderr.write(`Warning: using non-default API URL: ${this.baseUrl}\n`);
18
22
  }
19
23
  this.errorFormat = opts.errorFormat ?? 'developer';
24
+ this.quiet = opts.quiet ?? false;
20
25
  this.token = opts.token ?? null;
21
26
  this.userAgent = opts.userAgent ?? 'adapty-cli';
22
27
  }
@@ -25,7 +30,7 @@ export class ApiClient {
25
30
  }
26
31
  async post(path, body, params, opts) {
27
32
  return this.request(this.buildUrl(path, params), {
28
- body: body ? JSON.stringify(body) : undefined,
33
+ body: body ? JSON.stringify(body) : null,
29
34
  method: 'POST',
30
35
  }, opts);
31
36
  }
@@ -34,48 +39,70 @@ export class ApiClient {
34
39
  }
35
40
  async put(path, body, params, opts) {
36
41
  return this.request(this.buildUrl(path, params), {
37
- body: body ? JSON.stringify(body) : undefined,
42
+ body: body ? JSON.stringify(body) : null,
38
43
  method: 'PUT',
39
44
  }, opts);
40
45
  }
46
+ buildHeaders(init, opts) {
47
+ const headers = { 'User-Agent': this.userAgent };
48
+ if (init.body && !(init.body instanceof FormData)) {
49
+ headers['Content-Type'] = 'application/json';
50
+ }
51
+ if (this.token) {
52
+ headers.Authorization = `Bearer ${this.token}`;
53
+ }
54
+ return { ...headers, ...opts.headers };
55
+ }
41
56
  buildUrl(path, params) {
42
57
  const url = `${this.baseUrl}${ensureTrailingSlash(path)}`;
43
- if (!params)
58
+ if (!params) {
44
59
  return url;
60
+ }
45
61
  const search = new URLSearchParams();
46
62
  for (const [key, value] of Object.entries(params)) {
47
- if (value === undefined)
63
+ if (value === undefined) {
48
64
  continue;
49
- for (const item of Array.isArray(value) ? value : [value])
65
+ }
66
+ for (const item of Array.isArray(value) ? value : [value]) {
50
67
  search.append(key, item);
68
+ }
51
69
  }
52
70
  return search.size === 0 ? url : `${url}?${search.toString()}`;
53
71
  }
54
- isRetryableRateLimit(error) {
55
- return (this.errorFormat === 'asa' &&
56
- error.statusCode === 429 &&
57
- error.errorCode !== 'cli_cooldown_active' &&
58
- error.retryAfterSeconds !== undefined &&
59
- error.retryAfterSeconds <= MAX_RETRY_AFTER_SECONDS);
72
+ isRetryableRefusal(error) {
73
+ return (this.errorFormat === 'asa'
74
+ && RETRYABLE_STATUSES.has(error.statusCode)
75
+ && error.errorCode !== 'cli_cooldown_active'
76
+ && error.retryAfterSeconds !== undefined
77
+ && error.retryAfterSeconds <= MAX_RETRY_AFTER_SECONDS);
60
78
  }
61
- // eslint-disable-next-line no-undef
62
- async request(url, init, opts = {}, retried = false) {
63
- const headers = {
64
- 'User-Agent': this.userAgent,
65
- };
66
- if (init.body && !(init.body instanceof FormData)) {
67
- headers['Content-Type'] = 'application/json';
79
+ async readBody(response, errorOptions) {
80
+ try {
81
+ return await response.json();
68
82
  }
69
- if (this.token) {
70
- headers.Authorization = `Bearer ${this.token}`;
83
+ catch {
84
+ if (!response.ok) {
85
+ throw new ApiError(response.status, `http_${response.status}`, {}, errorOptions);
86
+ }
87
+ throw new ApiError(response.status, 'malformed_response', {}, {
88
+ ...errorOptions,
89
+ detail: `The server answered ${response.status} with a body that is not JSON, so the response was cut short `
90
+ + 'rather than refused. Nothing was read; retry the request.',
91
+ });
71
92
  }
72
- Object.assign(headers, opts.headers);
93
+ }
94
+ async request(url, init, opts = {}, retried = NO_RETRIES) {
95
+ const headers = this.buildHeaders(init, opts);
73
96
  let response;
74
97
  try {
75
98
  response = await fetch(url, { ...init, headers });
76
99
  }
77
100
  catch (error) {
78
- throw new NetworkError(error instanceof Error ? error.message : 'Connection failed');
101
+ const failure = new NetworkError(error instanceof Error ? error.message : 'Connection failed');
102
+ if (retried.network || init.method !== 'GET') {
103
+ throw failure;
104
+ }
105
+ return this.request(url, init, opts, { ...retried, network: true });
79
106
  }
80
107
  opts.onResponse?.(response.headers);
81
108
  if (response.status === 204) {
@@ -83,28 +110,22 @@ export class ApiClient {
83
110
  }
84
111
  const retryAfter = Number.parseInt(response.headers.get('Retry-After') ?? '', 10);
85
112
  const errorOptions = this.errorFormat === 'asa' && !Number.isNaN(retryAfter) ? { retryAfterSeconds: retryAfter } : {};
86
- let body;
87
- try {
88
- body = await response.json();
89
- }
90
- catch {
91
- if (!response.ok) {
92
- throw new ApiError(response.status, `http_${response.status}`, {}, errorOptions);
93
- }
94
- return undefined;
95
- }
113
+ const body = await this.readBody(response, errorOptions);
96
114
  if (!response.ok) {
97
115
  const error = parseApiError(response.status, body, errorOptions, this.errorFormat);
98
116
  if (response.status === 401) {
99
117
  error.message = 'Token expired or invalid. Run `adapty auth login`.';
100
118
  }
101
- if (!retried && this.isRetryableRateLimit(error)) {
119
+ if (!retried.refused && this.isRetryableRefusal(error)) {
102
120
  const seconds = error.retryAfterSeconds ?? 0;
103
- process.stderr.write(`Rate limited (${error.errorCode}); waiting ${seconds}s per Retry-After, then retrying once.\n`);
121
+ if (!this.quiet) {
122
+ const reason = error.statusCode === 429 ? 'Rate limited' : 'Temporarily unavailable';
123
+ process.stderr.write(`${reason} (${error.errorCode}); waiting ${seconds}s per Retry-After, then retrying once.\n`);
124
+ }
104
125
  await new Promise((resolve) => {
105
126
  setTimeout(resolve, seconds * 1000);
106
127
  });
107
- return this.request(url, init, opts, true);
128
+ return this.request(url, init, opts, { ...retried, refused: true });
108
129
  }
109
130
  throw error;
110
131
  }
@@ -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;