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.
- package/README.md +7 -2
- package/bin/dev.js +2 -2
- package/bin/run.js +2 -2
- package/dist/cli/base/adapty/adapty-command.d.ts +21 -0
- package/dist/cli/base/adapty/adapty-command.js +47 -0
- package/dist/cli/base/adapty/build.d.ts +11 -0
- package/dist/cli/base/adapty/build.js +13 -0
- package/dist/cli/base/adapty/index.d.ts +5 -0
- package/dist/cli/base/adapty/index.js +3 -0
- package/dist/cli/base/adapty/openSession.d.ts +22 -0
- package/dist/cli/base/adapty/openSession.js +25 -0
- package/dist/cli/base/base-command.d.ts +30 -0
- package/dist/cli/base/base-command.js +64 -0
- package/dist/cli/commands/apps/create.d.ts +19 -0
- package/dist/cli/commands/apps/create.js +68 -0
- package/dist/cli/commands/apps/get.d.ts +10 -0
- package/dist/cli/commands/apps/get.js +14 -0
- package/dist/cli/commands/apps/list.d.ts +11 -0
- package/dist/cli/commands/apps/list.js +14 -0
- package/dist/cli/commands/apps/update.d.ts +15 -0
- package/dist/cli/commands/apps/update.js +30 -0
- package/dist/cli/commands/auth/login.d.ts +11 -0
- package/dist/cli/commands/auth/login.js +68 -0
- package/dist/cli/commands/auth/logout.d.ts +12 -0
- package/dist/cli/commands/auth/logout.js +28 -0
- package/dist/cli/commands/auth/revoke.d.ts +13 -0
- package/dist/cli/commands/auth/revoke.js +32 -0
- package/dist/cli/commands/auth/status/index.d.ts +7 -0
- package/dist/cli/commands/auth/status/index.js +22 -0
- package/dist/cli/commands/auth/status/lib/render.d.ts +3 -0
- package/dist/cli/commands/auth/status/lib/render.js +14 -0
- package/dist/cli/commands/auth/status/lib/result.d.ts +15 -0
- package/dist/cli/commands/auth/status/lib/result.js +1 -0
- package/dist/cli/commands/auth/whoami.d.ts +10 -0
- package/dist/cli/commands/auth/whoami.js +16 -0
- package/dist/cli/errors.d.ts +39 -0
- package/dist/cli/errors.js +92 -0
- package/dist/cli/flags.d.ts +16 -0
- package/dist/cli/flags.js +27 -0
- package/dist/cli/views/list.d.ts +6 -0
- package/dist/cli/views/list.js +10 -0
- package/dist/cli/views/record.d.ts +9 -0
- package/dist/cli/views/record.js +14 -0
- package/dist/commands/access-levels/create.js +1 -1
- package/dist/commands/access-levels/list.d.ts +1 -1
- package/dist/commands/apps/create.d.ts +1 -14
- package/dist/commands/apps/create.js +2 -54
- package/dist/commands/apps/get.d.ts +1 -11
- package/dist/commands/apps/get.js +2 -22
- package/dist/commands/apps/list.d.ts +1 -13
- package/dist/commands/apps/list.js +2 -19
- package/dist/commands/apps/update.d.ts +1 -16
- package/dist/commands/apps/update.js +2 -38
- package/dist/commands/asa/ad-groups/create.js +9 -7
- package/dist/commands/asa/ad-groups/get.js +3 -2
- package/dist/commands/asa/ad-groups/list.d.ts +1 -1
- package/dist/commands/asa/ad-groups/list.js +1 -1
- package/dist/commands/asa/ad-groups/update.js +22 -12
- package/dist/commands/asa/ads/create.js +7 -5
- package/dist/commands/asa/ads/get.js +3 -2
- package/dist/commands/asa/ads/list.d.ts +1 -1
- package/dist/commands/asa/ads/list.js +1 -1
- package/dist/commands/asa/ads/update.js +9 -5
- package/dist/commands/asa/apps/list.d.ts +1 -1
- package/dist/commands/asa/apps/list.js +1 -1
- package/dist/commands/asa/automations/create.js +9 -6
- package/dist/commands/asa/automations/get.js +3 -2
- package/dist/commands/asa/automations/list.d.ts +1 -1
- package/dist/commands/asa/automations/list.js +1 -1
- package/dist/commands/asa/automations/run.js +5 -3
- package/dist/commands/asa/automations/runs.d.ts +1 -1
- package/dist/commands/asa/automations/runs.js +4 -4
- package/dist/commands/asa/automations/update.js +17 -9
- package/dist/commands/asa/campaigns/bulk-create.js +31 -18
- package/dist/commands/asa/campaigns/bulk-list.js +7 -5
- package/dist/commands/asa/campaigns/bulk-status.js +3 -2
- package/dist/commands/asa/campaigns/create.js +10 -8
- package/dist/commands/asa/campaigns/get.js +3 -2
- package/dist/commands/asa/campaigns/list.d.ts +1 -1
- package/dist/commands/asa/campaigns/list.js +1 -1
- package/dist/commands/asa/campaigns/update.js +25 -15
- package/dist/commands/asa/competitors/summary.js +5 -5
- package/dist/commands/asa/connect.js +5 -3
- package/dist/commands/asa/creatives/list.d.ts +1 -1
- package/dist/commands/asa/creatives/list.js +1 -1
- package/dist/commands/asa/keywords/add.js +13 -10
- package/dist/commands/asa/keywords/list.d.ts +1 -1
- package/dist/commands/asa/keywords/list.js +1 -1
- package/dist/commands/asa/keywords/update.js +15 -10
- package/dist/commands/asa/metrics/index.d.ts +2 -2
- package/dist/commands/asa/metrics/index.js +22 -8
- package/dist/commands/asa/metrics/overview.js +3 -3
- package/dist/commands/asa/negative-keywords/add.js +10 -7
- package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
- package/dist/commands/asa/negative-keywords/list.js +1 -1
- package/dist/commands/asa/orgs/list.d.ts +1 -1
- package/dist/commands/asa/orgs/list.js +1 -1
- package/dist/commands/asa/product-pages/list.d.ts +1 -1
- package/dist/commands/asa/product-pages/list.js +1 -1
- package/dist/commands/asa/product-pages/sync.js +1 -1
- package/dist/commands/asa/search-terms/list.d.ts +1 -1
- package/dist/commands/asa/search-terms/list.js +1 -1
- package/dist/commands/asa/whoami.js +13 -4
- package/dist/commands/auth/login.d.ts +1 -8
- package/dist/commands/auth/login.js +2 -120
- package/dist/commands/auth/logout.d.ts +1 -9
- package/dist/commands/auth/logout.js +2 -18
- package/dist/commands/auth/revoke.d.ts +1 -9
- package/dist/commands/auth/revoke.js +2 -21
- package/dist/commands/auth/status.d.ts +1 -7
- package/dist/commands/auth/status.js +2 -26
- package/dist/commands/auth/whoami.d.ts +1 -7
- package/dist/commands/auth/whoami.js +2 -15
- package/dist/commands/flows/config/get.js +2 -2
- package/dist/commands/flows/config/preview.d.ts +2 -2
- package/dist/commands/flows/config/preview.js +6 -4
- package/dist/commands/flows/config/update.js +5 -3
- package/dist/commands/flows/config/validate.js +5 -3
- package/dist/commands/flows/list.d.ts +1 -1
- package/dist/commands/flows/media/upload.js +1 -1
- package/dist/commands/flows/publish.js +5 -5
- package/dist/commands/paywalls/create.js +1 -1
- package/dist/commands/paywalls/list.d.ts +1 -1
- package/dist/commands/paywalls/placements.d.ts +2 -2
- package/dist/commands/paywalls/update.js +1 -1
- package/dist/commands/placements/create.js +17 -12
- package/dist/commands/placements/list.d.ts +1 -1
- package/dist/commands/placements/update.js +20 -15
- package/dist/commands/products/create.js +20 -13
- package/dist/commands/products/list.d.ts +1 -1
- package/dist/commands/products/update.js +1 -1
- package/dist/commands/segments/list.d.ts +1 -1
- package/dist/lib/api-client.d.ts +10 -6
- package/dist/lib/api-client.js +56 -35
- package/dist/lib/api-schemas.d.ts +63 -59
- package/dist/lib/app-url.js +2 -1
- package/dist/lib/asa-client.d.ts +13 -8
- package/dist/lib/asa-client.js +9 -4
- package/dist/lib/asa-flags.d.ts +19 -19
- package/dist/lib/asa-flags.js +38 -25
- package/dist/lib/asa-keyword-action.d.ts +10 -10
- package/dist/lib/asa-keyword-action.js +40 -25
- package/dist/lib/asa-schemas.d.ts +101 -81
- package/dist/lib/auth.js +2 -1
- package/dist/lib/client-from-config.d.ts +1 -1
- package/dist/lib/client-from-config.js +2 -1
- package/dist/lib/config.d.ts +2 -2
- package/dist/lib/confirm.d.ts +4 -4
- package/dist/lib/confirm.js +12 -7
- package/dist/lib/errors.d.ts +18 -18
- package/dist/lib/errors.js +12 -9
- package/dist/lib/flags.d.ts +3 -3
- package/dist/lib/flags.js +2 -1
- package/dist/lib/flow-help.js +11 -6
- package/dist/lib/output.js +23 -12
- package/dist/lib/preview.d.ts +5 -5
- package/dist/lib/preview.js +2 -1
- package/dist/sdk/adapty/access-levels.d.ts +15 -0
- package/dist/sdk/adapty/access-levels.js +4 -0
- package/dist/sdk/adapty/apps/create.d.ts +26 -0
- package/dist/sdk/adapty/apps/create.js +36 -0
- package/dist/sdk/adapty/apps/index.d.ts +13 -0
- package/dist/sdk/adapty/apps/index.js +9 -0
- package/dist/sdk/adapty/apps/model.d.ts +24 -0
- package/dist/sdk/adapty/apps/model.js +10 -0
- package/dist/sdk/adapty/apps/resource.d.ts +19 -0
- package/dist/sdk/adapty/apps/resource.js +23 -0
- package/dist/sdk/adapty/apps/update.d.ts +16 -0
- package/dist/sdk/adapty/apps/update.js +25 -0
- package/dist/sdk/adapty/auth/device-code.d.ts +8 -0
- package/dist/sdk/adapty/auth/device-code.js +19 -0
- package/dist/sdk/adapty/auth/index.d.ts +7 -0
- package/dist/sdk/adapty/auth/index.js +5 -0
- package/dist/sdk/adapty/auth/model.d.ts +15 -0
- package/dist/sdk/adapty/auth/model.js +6 -0
- package/dist/sdk/adapty/auth/poll-token.d.ts +8 -0
- package/dist/sdk/adapty/auth/poll-token.js +58 -0
- package/dist/sdk/adapty/auth/resource.d.ts +17 -0
- package/dist/sdk/adapty/auth/resource.js +21 -0
- package/dist/sdk/adapty/errors.d.ts +10 -0
- package/dist/sdk/adapty/errors.js +38 -0
- package/dist/sdk/adapty/index.d.ts +36 -0
- package/dist/sdk/adapty/index.js +27 -0
- package/dist/sdk/adapty/pagination.d.ts +23 -0
- package/dist/sdk/adapty/pagination.js +1 -0
- package/dist/sdk/core/auth/device-flow.d.ts +46 -0
- package/dist/sdk/core/auth/device-flow.js +62 -0
- package/dist/sdk/core/clock.d.ts +7 -0
- package/dist/sdk/core/clock.js +21 -0
- package/dist/sdk/core/errors.d.ts +81 -0
- package/dist/sdk/core/errors.js +84 -0
- package/dist/sdk/core/http/client.d.ts +46 -0
- package/dist/sdk/core/http/client.js +127 -0
- package/dist/sdk/core/http/index.d.ts +11 -0
- package/dist/sdk/core/http/index.js +7 -0
- package/dist/sdk/core/http/policies.d.ts +14 -0
- package/dist/sdk/core/http/policies.js +29 -0
- package/dist/sdk/core/http/retry.d.ts +27 -0
- package/dist/sdk/core/http/retry.js +22 -0
- package/dist/sdk/core/http/url.d.ts +3 -0
- package/dist/sdk/core/http/url.js +19 -0
- package/dist/sdk/core/session.d.ts +20 -0
- package/dist/sdk/core/session.js +69 -0
- package/dist/sdk/core/testing.d.ts +25 -0
- package/dist/sdk/core/testing.js +46 -0
- package/dist/sdk/core/validation.d.ts +10 -0
- package/dist/sdk/core/validation.js +14 -0
- package/oclif.manifest.json +766 -766
- 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
|
-
|
|
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 =
|
|
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
|
-
|
|
65
|
+
}
|
|
66
|
+
if (flags['android-product-id']) {
|
|
66
67
|
body.android_product_id = flags['android-product-id'];
|
|
67
|
-
|
|
68
|
+
}
|
|
69
|
+
if (flags['android-base-plan-id']) {
|
|
68
70
|
body.android_base_plan_id = flags['android-base-plan-id'];
|
|
69
|
-
|
|
71
|
+
}
|
|
72
|
+
if (flags['stripe-product-id']) {
|
|
70
73
|
body.stripe_product_id = flags['stripe-product-id'];
|
|
71
|
-
|
|
74
|
+
}
|
|
75
|
+
if (flags['stripe-price-id']) {
|
|
72
76
|
body.stripe_price_id = flags['stripe-price-id'];
|
|
73
|
-
|
|
77
|
+
}
|
|
78
|
+
if (flags['paddle-product-id']) {
|
|
74
79
|
body.paddle_product_id = flags['paddle-product-id'];
|
|
75
|
-
|
|
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 {
|
|
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 {
|
|
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;
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ApiErrorFormat } from './errors.js';
|
|
2
2
|
export type QueryParams = Record<string, string | string[] | undefined>;
|
|
3
|
-
export
|
|
3
|
+
export type RequestOptions = {
|
|
4
4
|
headers?: Record<string, string>;
|
|
5
5
|
onResponse?: (headers: Headers) => void;
|
|
6
|
-
}
|
|
7
|
-
export
|
|
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
|
|
29
|
+
private isRetryableRefusal;
|
|
30
|
+
private readBody;
|
|
27
31
|
private request;
|
|
28
32
|
}
|
package/dist/lib/api-client.js
CHANGED
|
@@ -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) :
|
|
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) :
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
return (this.errorFormat === 'asa'
|
|
56
|
-
error.statusCode
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
119
|
+
if (!retried.refused && this.isRetryableRefusal(error)) {
|
|
102
120
|
const seconds = error.retryAfterSeconds ?? 0;
|
|
103
|
-
|
|
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
|
|
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
|
|
6
|
+
};
|
|
7
|
+
export type AppSummaryDTO = {
|
|
8
8
|
id: string;
|
|
9
9
|
sdk_key: null | string;
|
|
10
10
|
title: string;
|
|
11
|
-
}
|
|
12
|
-
export
|
|
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
|
|
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
|
|
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
|
|
30
|
+
};
|
|
31
|
+
export type AccessLevelDTO = {
|
|
32
32
|
id: string;
|
|
33
33
|
sdk_id: string;
|
|
34
34
|
title: null | string;
|
|
35
|
-
}
|
|
36
|
-
export
|
|
35
|
+
};
|
|
36
|
+
export type AccessLevelCreateRequestDTO = {
|
|
37
37
|
sdk_id: string;
|
|
38
38
|
title?: null | string;
|
|
39
|
-
}
|
|
40
|
-
export
|
|
39
|
+
};
|
|
40
|
+
export type AccessLevelUpdateRequestDTO = {
|
|
41
41
|
title?: null | string;
|
|
42
|
-
}
|
|
43
|
-
export
|
|
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
|
|
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
|
|
62
|
+
};
|
|
63
|
+
export type ProductUpdateRequestDTO = {
|
|
64
64
|
access_level_id: string;
|
|
65
65
|
title: string;
|
|
66
|
-
}
|
|
67
|
-
export
|
|
66
|
+
};
|
|
67
|
+
export type PaywallDTO = {
|
|
68
68
|
id: string;
|
|
69
69
|
product_ids: string[];
|
|
70
70
|
title: string;
|
|
71
|
-
}
|
|
72
|
-
export
|
|
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
|
|
78
|
+
export type FlowDTO = {
|
|
79
79
|
id: string;
|
|
80
80
|
name: string;
|
|
81
|
-
|
|
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
|
|
84
|
+
};
|
|
85
|
+
export type FlowRemoteConfigDTO = {
|
|
85
86
|
data: string;
|
|
86
87
|
locale: string;
|
|
87
|
-
}
|
|
88
|
-
export
|
|
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
|
|
101
|
+
};
|
|
102
|
+
export type FlowWriteRequestDTO = {
|
|
102
103
|
name: string;
|
|
103
|
-
}
|
|
104
|
-
export
|
|
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
|
|
110
|
+
};
|
|
111
|
+
export type FlowConfigValidateRequestDTO = {
|
|
111
112
|
config: Record<string, unknown>;
|
|
112
|
-
}
|
|
113
|
-
export
|
|
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
|
|
121
|
+
};
|
|
122
|
+
export type FlowConfigValidationDTO = {
|
|
122
123
|
issues: FlowConfigIssueDTO[];
|
|
123
124
|
valid: boolean;
|
|
124
|
-
}
|
|
125
|
-
export
|
|
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
|
|
133
|
+
};
|
|
134
|
+
export type SegmentDTO = {
|
|
134
135
|
description: null | string;
|
|
135
136
|
id: string;
|
|
136
137
|
title: string;
|
|
137
|
-
}
|
|
138
|
-
export
|
|
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
|
|
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
|
|
153
|
+
export type PlacementSummaryDTO = {
|
|
153
154
|
developer_id: string;
|
|
154
155
|
id: string;
|
|
155
|
-
/**
|
|
156
|
-
|
|
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
|
|
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
|
|
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
|
+
};
|
package/dist/lib/app-url.js
CHANGED
|
@@ -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);
|
package/dist/lib/asa-client.d.ts
CHANGED
|
@@ -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
|
|
6
|
-
|
|
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
|
|
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;
|