adapty 0.8.4 → 0.8.6

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 (209) hide show
  1. package/README.md +7 -2
  2. package/bin/dev.js +2 -2
  3. package/bin/run.js +2 -2
  4. package/dist/cli/base/adapty/adapty-command.d.ts +21 -0
  5. package/dist/cli/base/adapty/adapty-command.js +47 -0
  6. package/dist/cli/base/adapty/build.d.ts +11 -0
  7. package/dist/cli/base/adapty/build.js +13 -0
  8. package/dist/cli/base/adapty/index.d.ts +5 -0
  9. package/dist/cli/base/adapty/index.js +3 -0
  10. package/dist/cli/base/adapty/openSession.d.ts +22 -0
  11. package/dist/cli/base/adapty/openSession.js +25 -0
  12. package/dist/cli/base/base-command.d.ts +30 -0
  13. package/dist/cli/base/base-command.js +64 -0
  14. package/dist/cli/commands/apps/create.d.ts +19 -0
  15. package/dist/cli/commands/apps/create.js +68 -0
  16. package/dist/cli/commands/apps/get.d.ts +10 -0
  17. package/dist/cli/commands/apps/get.js +14 -0
  18. package/dist/cli/commands/apps/list.d.ts +11 -0
  19. package/dist/cli/commands/apps/list.js +14 -0
  20. package/dist/cli/commands/apps/update.d.ts +15 -0
  21. package/dist/cli/commands/apps/update.js +30 -0
  22. package/dist/cli/commands/auth/login.d.ts +11 -0
  23. package/dist/cli/commands/auth/login.js +68 -0
  24. package/dist/cli/commands/auth/logout.d.ts +12 -0
  25. package/dist/cli/commands/auth/logout.js +28 -0
  26. package/dist/cli/commands/auth/revoke.d.ts +13 -0
  27. package/dist/cli/commands/auth/revoke.js +32 -0
  28. package/dist/cli/commands/auth/status/index.d.ts +7 -0
  29. package/dist/cli/commands/auth/status/index.js +22 -0
  30. package/dist/cli/commands/auth/status/lib/render.d.ts +3 -0
  31. package/dist/cli/commands/auth/status/lib/render.js +14 -0
  32. package/dist/cli/commands/auth/status/lib/result.d.ts +15 -0
  33. package/dist/cli/commands/auth/status/lib/result.js +1 -0
  34. package/dist/cli/commands/auth/whoami.d.ts +10 -0
  35. package/dist/cli/commands/auth/whoami.js +16 -0
  36. package/dist/cli/errors.d.ts +39 -0
  37. package/dist/cli/errors.js +92 -0
  38. package/dist/cli/flags.d.ts +16 -0
  39. package/dist/cli/flags.js +27 -0
  40. package/dist/cli/views/list.d.ts +6 -0
  41. package/dist/cli/views/list.js +10 -0
  42. package/dist/cli/views/record.d.ts +9 -0
  43. package/dist/cli/views/record.js +14 -0
  44. package/dist/commands/access-levels/create.js +1 -1
  45. package/dist/commands/access-levels/list.d.ts +1 -1
  46. package/dist/commands/apps/create.d.ts +1 -14
  47. package/dist/commands/apps/create.js +2 -54
  48. package/dist/commands/apps/get.d.ts +1 -11
  49. package/dist/commands/apps/get.js +2 -22
  50. package/dist/commands/apps/list.d.ts +1 -13
  51. package/dist/commands/apps/list.js +2 -19
  52. package/dist/commands/apps/update.d.ts +1 -16
  53. package/dist/commands/apps/update.js +2 -38
  54. package/dist/commands/asa/ad-groups/create.js +9 -7
  55. package/dist/commands/asa/ad-groups/get.js +3 -2
  56. package/dist/commands/asa/ad-groups/list.d.ts +1 -1
  57. package/dist/commands/asa/ad-groups/list.js +1 -1
  58. package/dist/commands/asa/ad-groups/update.js +22 -12
  59. package/dist/commands/asa/ads/create.js +7 -5
  60. package/dist/commands/asa/ads/get.js +3 -2
  61. package/dist/commands/asa/ads/list.d.ts +1 -1
  62. package/dist/commands/asa/ads/list.js +1 -1
  63. package/dist/commands/asa/ads/update.js +9 -5
  64. package/dist/commands/asa/apps/list.d.ts +1 -1
  65. package/dist/commands/asa/apps/list.js +1 -1
  66. package/dist/commands/asa/automations/create.js +9 -6
  67. package/dist/commands/asa/automations/get.js +3 -2
  68. package/dist/commands/asa/automations/list.d.ts +1 -1
  69. package/dist/commands/asa/automations/list.js +1 -1
  70. package/dist/commands/asa/automations/run.js +5 -3
  71. package/dist/commands/asa/automations/runs.d.ts +1 -1
  72. package/dist/commands/asa/automations/runs.js +4 -4
  73. package/dist/commands/asa/automations/update.js +17 -9
  74. package/dist/commands/asa/campaigns/bulk-create.js +31 -18
  75. package/dist/commands/asa/campaigns/bulk-list.js +7 -5
  76. package/dist/commands/asa/campaigns/bulk-status.js +3 -2
  77. package/dist/commands/asa/campaigns/create.js +10 -8
  78. package/dist/commands/asa/campaigns/get.js +3 -2
  79. package/dist/commands/asa/campaigns/list.d.ts +1 -1
  80. package/dist/commands/asa/campaigns/list.js +1 -1
  81. package/dist/commands/asa/campaigns/update.js +25 -15
  82. package/dist/commands/asa/competitors/summary.js +5 -5
  83. package/dist/commands/asa/connect.js +5 -3
  84. package/dist/commands/asa/creatives/list.d.ts +1 -1
  85. package/dist/commands/asa/creatives/list.js +1 -1
  86. package/dist/commands/asa/keywords/add.js +13 -10
  87. package/dist/commands/asa/keywords/list.d.ts +1 -1
  88. package/dist/commands/asa/keywords/list.js +1 -1
  89. package/dist/commands/asa/keywords/update.js +15 -10
  90. package/dist/commands/asa/metrics/index.d.ts +2 -2
  91. package/dist/commands/asa/metrics/index.js +22 -8
  92. package/dist/commands/asa/metrics/overview.js +3 -3
  93. package/dist/commands/asa/negative-keywords/add.js +10 -7
  94. package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
  95. package/dist/commands/asa/negative-keywords/list.js +1 -1
  96. package/dist/commands/asa/orgs/list.d.ts +1 -1
  97. package/dist/commands/asa/orgs/list.js +1 -1
  98. package/dist/commands/asa/product-pages/list.d.ts +1 -1
  99. package/dist/commands/asa/product-pages/list.js +1 -1
  100. package/dist/commands/asa/product-pages/sync.js +1 -1
  101. package/dist/commands/asa/search-terms/list.d.ts +1 -1
  102. package/dist/commands/asa/search-terms/list.js +1 -1
  103. package/dist/commands/asa/whoami.js +13 -4
  104. package/dist/commands/auth/login.d.ts +1 -8
  105. package/dist/commands/auth/login.js +2 -120
  106. package/dist/commands/auth/logout.d.ts +1 -9
  107. package/dist/commands/auth/logout.js +2 -18
  108. package/dist/commands/auth/revoke.d.ts +1 -9
  109. package/dist/commands/auth/revoke.js +2 -21
  110. package/dist/commands/auth/status.d.ts +1 -7
  111. package/dist/commands/auth/status.js +2 -26
  112. package/dist/commands/auth/whoami.d.ts +1 -7
  113. package/dist/commands/auth/whoami.js +2 -15
  114. package/dist/commands/flows/config/get.js +2 -2
  115. package/dist/commands/flows/config/preview.d.ts +2 -2
  116. package/dist/commands/flows/config/preview.js +6 -4
  117. package/dist/commands/flows/config/update.js +5 -3
  118. package/dist/commands/flows/config/validate.js +5 -3
  119. package/dist/commands/flows/list.d.ts +1 -1
  120. package/dist/commands/flows/media/upload.js +1 -1
  121. package/dist/commands/flows/publish.js +5 -5
  122. package/dist/commands/paywalls/create.js +1 -1
  123. package/dist/commands/paywalls/list.d.ts +1 -1
  124. package/dist/commands/paywalls/placements.d.ts +2 -2
  125. package/dist/commands/paywalls/update.js +1 -1
  126. package/dist/commands/placements/create.js +17 -12
  127. package/dist/commands/placements/list.d.ts +1 -1
  128. package/dist/commands/placements/update.js +20 -15
  129. package/dist/commands/products/create.js +20 -13
  130. package/dist/commands/products/list.d.ts +1 -1
  131. package/dist/commands/products/update.js +1 -1
  132. package/dist/commands/segments/list.d.ts +1 -1
  133. package/dist/lib/api-client.d.ts +10 -6
  134. package/dist/lib/api-client.js +56 -35
  135. package/dist/lib/api-schemas.d.ts +63 -59
  136. package/dist/lib/app-url.js +2 -1
  137. package/dist/lib/asa-client.d.ts +13 -8
  138. package/dist/lib/asa-client.js +9 -4
  139. package/dist/lib/asa-flags.d.ts +19 -19
  140. package/dist/lib/asa-flags.js +38 -25
  141. package/dist/lib/asa-keyword-action.d.ts +10 -10
  142. package/dist/lib/asa-keyword-action.js +40 -25
  143. package/dist/lib/asa-schemas.d.ts +101 -81
  144. package/dist/lib/auth.js +2 -1
  145. package/dist/lib/client-from-config.d.ts +1 -1
  146. package/dist/lib/client-from-config.js +2 -1
  147. package/dist/lib/config.d.ts +2 -2
  148. package/dist/lib/confirm.d.ts +4 -4
  149. package/dist/lib/confirm.js +12 -7
  150. package/dist/lib/errors.d.ts +18 -18
  151. package/dist/lib/errors.js +12 -9
  152. package/dist/lib/flags.d.ts +3 -3
  153. package/dist/lib/flags.js +2 -1
  154. package/dist/lib/flow-help.js +11 -6
  155. package/dist/lib/output.js +23 -12
  156. package/dist/lib/preview.d.ts +5 -5
  157. package/dist/lib/preview.js +2 -1
  158. package/dist/sdk/adapty/access-levels.d.ts +15 -0
  159. package/dist/sdk/adapty/access-levels.js +4 -0
  160. package/dist/sdk/adapty/apps/create.d.ts +26 -0
  161. package/dist/sdk/adapty/apps/create.js +36 -0
  162. package/dist/sdk/adapty/apps/index.d.ts +13 -0
  163. package/dist/sdk/adapty/apps/index.js +9 -0
  164. package/dist/sdk/adapty/apps/model.d.ts +24 -0
  165. package/dist/sdk/adapty/apps/model.js +10 -0
  166. package/dist/sdk/adapty/apps/resource.d.ts +19 -0
  167. package/dist/sdk/adapty/apps/resource.js +23 -0
  168. package/dist/sdk/adapty/apps/update.d.ts +16 -0
  169. package/dist/sdk/adapty/apps/update.js +25 -0
  170. package/dist/sdk/adapty/auth/device-code.d.ts +8 -0
  171. package/dist/sdk/adapty/auth/device-code.js +19 -0
  172. package/dist/sdk/adapty/auth/index.d.ts +7 -0
  173. package/dist/sdk/adapty/auth/index.js +5 -0
  174. package/dist/sdk/adapty/auth/model.d.ts +15 -0
  175. package/dist/sdk/adapty/auth/model.js +6 -0
  176. package/dist/sdk/adapty/auth/poll-token.d.ts +8 -0
  177. package/dist/sdk/adapty/auth/poll-token.js +58 -0
  178. package/dist/sdk/adapty/auth/resource.d.ts +17 -0
  179. package/dist/sdk/adapty/auth/resource.js +21 -0
  180. package/dist/sdk/adapty/errors.d.ts +10 -0
  181. package/dist/sdk/adapty/errors.js +38 -0
  182. package/dist/sdk/adapty/index.d.ts +36 -0
  183. package/dist/sdk/adapty/index.js +27 -0
  184. package/dist/sdk/adapty/pagination.d.ts +23 -0
  185. package/dist/sdk/adapty/pagination.js +1 -0
  186. package/dist/sdk/core/auth/device-flow.d.ts +46 -0
  187. package/dist/sdk/core/auth/device-flow.js +62 -0
  188. package/dist/sdk/core/clock.d.ts +7 -0
  189. package/dist/sdk/core/clock.js +21 -0
  190. package/dist/sdk/core/errors.d.ts +81 -0
  191. package/dist/sdk/core/errors.js +84 -0
  192. package/dist/sdk/core/http/client.d.ts +46 -0
  193. package/dist/sdk/core/http/client.js +127 -0
  194. package/dist/sdk/core/http/index.d.ts +11 -0
  195. package/dist/sdk/core/http/index.js +7 -0
  196. package/dist/sdk/core/http/policies.d.ts +14 -0
  197. package/dist/sdk/core/http/policies.js +29 -0
  198. package/dist/sdk/core/http/retry.d.ts +27 -0
  199. package/dist/sdk/core/http/retry.js +22 -0
  200. package/dist/sdk/core/http/url.d.ts +3 -0
  201. package/dist/sdk/core/http/url.js +19 -0
  202. package/dist/sdk/core/session.d.ts +20 -0
  203. package/dist/sdk/core/session.js +69 -0
  204. package/dist/sdk/core/testing.d.ts +25 -0
  205. package/dist/sdk/core/testing.js +46 -0
  206. package/dist/sdk/core/validation.d.ts +10 -0
  207. package/dist/sdk/core/validation.js +14 -0
  208. package/oclif.manifest.json +766 -766
  209. package/package.json +19 -9
@@ -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,34 @@ 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
- status: FlowStatus;
81
+ /** Optional: the flows-update (rename) response leaves status out. Read it from flows get. */
82
+ status?: FlowStatus;
82
83
  updated_at: string;
83
- }
84
- export interface FlowRemoteConfigDTO {
84
+ };
85
+ export type FlowRemoteConfigDTO = {
85
86
  data: string;
86
87
  locale: string;
87
- }
88
- export interface FlowConfigDTO {
88
+ };
89
+ export type FlowConfigDTO = {
89
90
  config: Record<string, unknown>;
90
91
  /** Human message for a failed publication (raw passthrough from the flow version). */
91
92
  publication_error?: null | string;
@@ -97,77 +98,80 @@ export interface FlowConfigDTO {
97
98
  transform_error?: null | string;
98
99
  /** Millisecond timestamp of the last content change; the value `expected_updated_at` is compared against on write. */
99
100
  updated_at: number;
100
- }
101
- export interface FlowWriteRequestDTO {
101
+ };
102
+ export type FlowWriteRequestDTO = {
102
103
  name: string;
103
- }
104
- export interface FlowConfigWriteRequestDTO {
104
+ };
105
+ export type FlowConfigWriteRequestDTO = {
105
106
  config: Record<string, unknown>;
106
107
  /** Optimistic lock: the `updated_at` from a prior config read. Omit for last-write-wins. */
107
108
  expected_updated_at?: null | number;
108
109
  remote_configs?: FlowRemoteConfigDTO[];
109
- }
110
- export interface FlowConfigValidateRequestDTO {
110
+ };
111
+ export type FlowConfigValidateRequestDTO = {
111
112
  config: Record<string, unknown>;
112
- }
113
- export interface FlowConfigIssueDTO {
113
+ };
114
+ export type FlowConfigIssueDTO = {
114
115
  /** Machine code, relayed from the transformer; absent until it reports path-level diagnostics. */
115
116
  code?: null | string;
116
117
  message: string;
117
118
  /** Location of the issue; absent until the transformer reports path-level diagnostics. */
118
119
  path?: null | string;
119
120
  severity: string;
120
- }
121
- export interface FlowConfigValidationDTO {
121
+ };
122
+ export type FlowConfigValidationDTO = {
122
123
  issues: FlowConfigIssueDTO[];
123
124
  valid: boolean;
124
- }
125
- export interface MediaDTO {
125
+ };
126
+ export type MediaDTO = {
126
127
  id: number;
127
128
  name: string;
128
129
  /** Base64-encoded preview thumbnail; absent when no preview was generated. */
129
130
  preview_base64?: null | string;
130
131
  /** CDN URL to reference from a flow config. */
131
132
  url: string;
132
- }
133
- export interface SegmentDTO {
133
+ };
134
+ export type SegmentDTO = {
134
135
  description: null | string;
135
136
  id: string;
136
137
  title: string;
137
- }
138
- export interface PlacementPaywallAudienceEntryDTO {
138
+ };
139
+ export type PlacementPaywallAudienceEntryDTO = {
139
140
  content_type: 'paywall';
140
141
  paywall_id: string;
141
142
  priority: number;
142
143
  segment_ids?: string[];
143
- }
144
- export interface PlacementFlowAudienceEntryDTO {
144
+ };
145
+ export type PlacementFlowAudienceEntryDTO = {
145
146
  content_type: 'flow';
146
147
  /** The flow must be `published` (not draft) — the backend returns 400 otherwise. */
147
148
  flow_id: string;
148
149
  priority: number;
149
150
  segment_ids?: string[];
150
- }
151
+ };
151
152
  export type PlacementAudienceEntryDTO = PlacementFlowAudienceEntryDTO | PlacementPaywallAudienceEntryDTO;
152
- export interface PlacementSummaryDTO {
153
+ export type PlacementSummaryDTO = {
153
154
  developer_id: string;
154
155
  id: string;
155
- /** Placement activation state: true = Live, false = Inactive. */
156
- is_active: boolean;
156
+ /**
157
+ * Placement activation state: true = Live, false = Inactive.
158
+ * Optional — the paywall placements list leaves it out.
159
+ */
160
+ is_active?: boolean;
157
161
  title: string;
158
- }
159
- export interface PlacementDetailDTO {
162
+ };
163
+ export type PlacementDetailDTO = {
160
164
  audiences?: PlacementAudienceEntryDTO[];
161
165
  developer_id: string;
162
166
  id: string;
163
167
  /** Placement activation state: true = Live, false = Inactive. */
164
168
  is_active: boolean;
165
169
  title: string;
166
- }
167
- export interface PlacementWriteRequestDTO {
170
+ };
171
+ export type PlacementWriteRequestDTO = {
168
172
  audiences: null | PlacementAudienceEntryDTO[];
169
173
  developer_id: string;
170
174
  /** @deprecated use `audiences` */
171
175
  paywall_id: null | string;
172
176
  title: string;
173
- }
177
+ };
@@ -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;