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
package/dist/lib/output.js
CHANGED
|
@@ -3,10 +3,12 @@ function formatLabel(snakeKey) {
|
|
|
3
3
|
return snakeKey
|
|
4
4
|
.split('_')
|
|
5
5
|
.map((w) => {
|
|
6
|
-
if (w === 'ids')
|
|
6
|
+
if (w === 'ids') {
|
|
7
7
|
return 'IDs';
|
|
8
|
-
|
|
8
|
+
}
|
|
9
|
+
if (UPPER_WORDS.has(w)) {
|
|
9
10
|
return w.toUpperCase();
|
|
11
|
+
}
|
|
10
12
|
return w.charAt(0).toUpperCase() + w.slice(1);
|
|
11
13
|
})
|
|
12
14
|
.join(' ');
|
|
@@ -18,19 +20,22 @@ function renderArrayItems(items, indent) {
|
|
|
18
20
|
const childIndent = indent + ' ';
|
|
19
21
|
const lines = [];
|
|
20
22
|
for (const item of items) {
|
|
21
|
-
if (item === undefined || item === null)
|
|
23
|
+
if (item === undefined || item === null) {
|
|
22
24
|
continue;
|
|
25
|
+
}
|
|
23
26
|
if (Array.isArray(item)) {
|
|
24
27
|
lines.push(...renderArrayItems(item, childIndent));
|
|
25
28
|
}
|
|
26
29
|
else if (isScalar(item)) {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- FIXME if you see this
|
|
27
31
|
lines.push(`${indent}- ${String(item)}`);
|
|
28
32
|
}
|
|
29
33
|
else {
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
34
|
+
const [head, ...rest] = renderObject(item, childIndent);
|
|
35
|
+
if (head === undefined) {
|
|
32
36
|
continue;
|
|
33
|
-
|
|
37
|
+
}
|
|
38
|
+
lines.push(`${indent}- ${head.slice(childIndent.length)}`, ...rest);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
return lines;
|
|
@@ -38,13 +43,15 @@ function renderArrayItems(items, indent) {
|
|
|
38
43
|
function renderObject(data, indent) {
|
|
39
44
|
const lines = [];
|
|
40
45
|
for (const [key, value] of Object.entries(data)) {
|
|
41
|
-
if (value === undefined || value === null)
|
|
46
|
+
if (value === undefined || value === null) {
|
|
42
47
|
continue;
|
|
48
|
+
}
|
|
43
49
|
const label = `${indent}${formatLabel(key)}:`;
|
|
44
50
|
if (Array.isArray(value)) {
|
|
45
|
-
if (value.length === 0)
|
|
51
|
+
if (value.length === 0) {
|
|
46
52
|
continue;
|
|
47
|
-
|
|
53
|
+
}
|
|
54
|
+
if (value.every(v => isScalar(v))) {
|
|
48
55
|
lines.push(`${label} ${value.map(String).join(', ')}`);
|
|
49
56
|
}
|
|
50
57
|
else {
|
|
@@ -52,25 +59,29 @@ function renderObject(data, indent) {
|
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
else if (isScalar(value)) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- FIXME if you see this
|
|
55
63
|
lines.push(`${label} ${String(value)}`);
|
|
56
64
|
}
|
|
57
65
|
else {
|
|
58
66
|
const nested = renderObject(value, indent + ' ');
|
|
59
|
-
if (nested.length > 0)
|
|
67
|
+
if (nested.length > 0) {
|
|
60
68
|
lines.push(label, ...nested);
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
return lines;
|
|
64
73
|
}
|
|
65
74
|
export function printResponse(data, log) {
|
|
66
|
-
for (const line of renderObject(data, ''))
|
|
75
|
+
for (const line of renderObject(data, '')) {
|
|
67
76
|
log(line);
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
export function printList(items, log, pagination) {
|
|
70
80
|
for (const [i, item] of items.entries()) {
|
|
71
81
|
printResponse(item, log);
|
|
72
|
-
if (i < items.length - 1)
|
|
82
|
+
if (i < items.length - 1) {
|
|
73
83
|
log('---');
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
if (pagination) {
|
|
76
87
|
log('');
|
package/dist/lib/preview.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ export declare const DEFAULT_DEVICE_ID = "iphone-14";
|
|
|
5
5
|
export declare const ORIENTATIONS: readonly ["landscape", "portrait"];
|
|
6
6
|
export declare const DEFAULT_ORIENTATION = "portrait";
|
|
7
7
|
/** Payload the render page expects. Wire format shared with the UI, hence camelCase. */
|
|
8
|
-
export
|
|
8
|
+
export type PreviewPayload = {
|
|
9
9
|
flow: Record<string, unknown>;
|
|
10
10
|
remoteConfigs: unknown[];
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
/**
|
|
13
13
|
* Accepts either a dashboard-api envelope (`{config, remote_configs, ...}`) or a bare
|
|
14
14
|
* builder config, and returns the shape the render page injects.
|
|
@@ -18,10 +18,10 @@ export interface PreviewPayload {
|
|
|
18
18
|
* rejected there instead — better to say so here, against the file the user named.
|
|
19
19
|
*/
|
|
20
20
|
export declare function normalizePreviewConfig(raw: unknown): PreviewPayload;
|
|
21
|
-
export
|
|
21
|
+
export type RenderTarget = {
|
|
22
22
|
device: string;
|
|
23
23
|
orientation: string;
|
|
24
24
|
/** Omitted lets the render page fall back to the flow's first screen. */
|
|
25
|
-
screen?: string;
|
|
26
|
-
}
|
|
25
|
+
screen?: string | undefined;
|
|
26
|
+
};
|
|
27
27
|
export declare function buildRenderUrl(target: RenderTarget, payload: PreviewPayload): string;
|
package/dist/lib/preview.js
CHANGED
|
@@ -39,8 +39,9 @@ function encodeConfigFragment(payload) {
|
|
|
39
39
|
}
|
|
40
40
|
export function buildRenderUrl(target, payload) {
|
|
41
41
|
const url = appUrl(PREVIEW_PATH);
|
|
42
|
-
if (target.screen)
|
|
42
|
+
if (target.screen) {
|
|
43
43
|
url.searchParams.set('screen', target.screen);
|
|
44
|
+
}
|
|
44
45
|
url.searchParams.set('device', target.device);
|
|
45
46
|
url.searchParams.set('orientation', target.orientation);
|
|
46
47
|
url.hash = `config=${encodeConfigFragment(payload)}`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Http } from '../core/http/index.js';
|
|
2
|
+
export type AccessLevel = {
|
|
3
|
+
id: string;
|
|
4
|
+
sdk_id: string;
|
|
5
|
+
title: null | string;
|
|
6
|
+
};
|
|
7
|
+
/** Not paginated: this endpoint answers with a bare `items` list, and may omit it entirely. */
|
|
8
|
+
export type AccessLevelList = {
|
|
9
|
+
items?: AccessLevel[];
|
|
10
|
+
};
|
|
11
|
+
/** Only the read `apps create` needs; the rest arrives with the access-levels commands. */
|
|
12
|
+
export declare const accessLevels: (http: Http) => {
|
|
13
|
+
list: (appId: string) => Promise<AccessLevelList>;
|
|
14
|
+
};
|
|
15
|
+
export type AccessLevelsApi = ReturnType<typeof accessLevels>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Platform } from './model.js';
|
|
2
|
+
import type { Issue } from '../../core/errors.js';
|
|
3
|
+
export type CreateAppInput = {
|
|
4
|
+
appleBundleId?: string | undefined;
|
|
5
|
+
googleBundleId?: string | undefined;
|
|
6
|
+
platforms: readonly Platform[];
|
|
7
|
+
title: string;
|
|
8
|
+
};
|
|
9
|
+
type CreateAppRequest = {
|
|
10
|
+
apple_bundle_id?: string;
|
|
11
|
+
google_bundle_id?: string;
|
|
12
|
+
platforms: Platform[];
|
|
13
|
+
title: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The business rules of creating an app: a pure function returning a list of problems, knowing
|
|
17
|
+
* nothing about flags or the network. An Issue path names an input field (camelCase) and the
|
|
18
|
+
* adapter turns it into the flag the user typed (src/cli/errors.ts).
|
|
19
|
+
*
|
|
20
|
+
* A list instead of a throw, so a table test walks the rules without an environment and the user
|
|
21
|
+
* sees every problem at once.
|
|
22
|
+
*/
|
|
23
|
+
export declare const validateCreateApp: (input: CreateAppInput) => Issue[];
|
|
24
|
+
/** An absent field is left out of the body: the server tells "not given" from "set to empty". */
|
|
25
|
+
export declare const toCreateRequest: (input: CreateAppInput) => CreateAppRequest;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The business rules of creating an app: a pure function returning a list of problems, knowing
|
|
3
|
+
* nothing about flags or the network. An Issue path names an input field (camelCase) and the
|
|
4
|
+
* adapter turns it into the flag the user typed (src/cli/errors.ts).
|
|
5
|
+
*
|
|
6
|
+
* A list instead of a throw, so a table test walks the rules without an environment and the user
|
|
7
|
+
* sees every problem at once.
|
|
8
|
+
*/
|
|
9
|
+
export const validateCreateApp = (input) => {
|
|
10
|
+
const issues = [];
|
|
11
|
+
if (input.title.trim() === '') {
|
|
12
|
+
issues.push({ message: 'must not be empty', path: 'title' });
|
|
13
|
+
}
|
|
14
|
+
if (input.platforms.length === 0) {
|
|
15
|
+
issues.push({ message: 'at least one platform is required', path: 'platform' });
|
|
16
|
+
}
|
|
17
|
+
// An empty string counts as missing: `--apple-bundle-id ""` is not a bundle id
|
|
18
|
+
if (input.platforms.includes('ios') && (input.appleBundleId ?? '') === '') {
|
|
19
|
+
issues.push({ message: 'required when platforms include ios', path: 'appleBundleId' });
|
|
20
|
+
}
|
|
21
|
+
if (input.platforms.includes('android') && (input.googleBundleId ?? '') === '') {
|
|
22
|
+
issues.push({ message: 'required when platforms include android', path: 'googleBundleId' });
|
|
23
|
+
}
|
|
24
|
+
return issues;
|
|
25
|
+
};
|
|
26
|
+
/** An absent field is left out of the body: the server tells "not given" from "set to empty". */
|
|
27
|
+
export const toCreateRequest = (input) => {
|
|
28
|
+
const body = { platforms: [...input.platforms], title: input.title };
|
|
29
|
+
if (input.appleBundleId !== undefined) {
|
|
30
|
+
body.apple_bundle_id = input.appleBundleId;
|
|
31
|
+
}
|
|
32
|
+
if (input.googleBundleId !== undefined) {
|
|
33
|
+
body.google_bundle_id = input.googleBundleId;
|
|
34
|
+
}
|
|
35
|
+
return body;
|
|
36
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door of the apps resource: re-exports only, no code of its own. Everything outside the
|
|
3
|
+
* directory imports from here, which is what lets the files behind it be rearranged. Only what a
|
|
4
|
+
* consumer uses passes through — the request builders stay private to their operation.
|
|
5
|
+
*/
|
|
6
|
+
export { platforms } from './model.js';
|
|
7
|
+
export { validateCreateApp } from './create.js';
|
|
8
|
+
export { apps } from './resource.js';
|
|
9
|
+
export { validateUpdateApp } from './update.js';
|
|
10
|
+
export type { AppDetail, AppSummary } from './model.js';
|
|
11
|
+
export type { CreateAppInput } from './create.js';
|
|
12
|
+
export type { AppsApi } from './resource.js';
|
|
13
|
+
export type { UpdateAppInput } from './update.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door of the apps resource: re-exports only, no code of its own. Everything outside the
|
|
3
|
+
* directory imports from here, which is what lets the files behind it be rearranged. Only what a
|
|
4
|
+
* consumer uses passes through — the request builders stay private to their operation.
|
|
5
|
+
*/
|
|
6
|
+
export { platforms } from './model.js';
|
|
7
|
+
export { validateCreateApp } from './create.js';
|
|
8
|
+
export { apps } from './resource.js';
|
|
9
|
+
export { validateUpdateApp } from './update.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every operation of the resource shares: the entity as the server sends it, and the
|
|
3
|
+
* platform vocabulary. Answers pass through exactly as they arrive, in snake_case — that is what
|
|
4
|
+
* --json has printed since the first release.
|
|
5
|
+
*
|
|
6
|
+
* An input shape is not shared and does not belong here: it is camelCase, it is our API rather
|
|
7
|
+
* than the server's, and it lives in the file of the operation that takes it.
|
|
8
|
+
*/
|
|
9
|
+
/** In the order the published help lists them, so `--platform` keeps reading `(ios|android)`. */
|
|
10
|
+
export declare const platforms: readonly ["ios", "android"];
|
|
11
|
+
export type Platform = (typeof platforms)[number];
|
|
12
|
+
export type AppSummary = {
|
|
13
|
+
id: string;
|
|
14
|
+
sdk_key: null | string;
|
|
15
|
+
title: string;
|
|
16
|
+
};
|
|
17
|
+
/** What get and update answer with: the summary plus the fields only the detail view carries. */
|
|
18
|
+
export type AppDetail = AppSummary & {
|
|
19
|
+
apple_bundle_id: null | string;
|
|
20
|
+
google_bundle_id: null | string;
|
|
21
|
+
/** Not the Platform union: the server owns this list and may grow it without asking us. */
|
|
22
|
+
platforms: string[];
|
|
23
|
+
secret_key: null | string;
|
|
24
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every operation of the resource shares: the entity as the server sends it, and the
|
|
3
|
+
* platform vocabulary. Answers pass through exactly as they arrive, in snake_case — that is what
|
|
4
|
+
* --json has printed since the first release.
|
|
5
|
+
*
|
|
6
|
+
* An input shape is not shared and does not belong here: it is camelCase, it is our API rather
|
|
7
|
+
* than the server's, and it lives in the file of the operation that takes it.
|
|
8
|
+
*/
|
|
9
|
+
/** In the order the published help lists them, so `--platform` keeps reading `(ios|android)`. */
|
|
10
|
+
export const platforms = ['ios', 'android'];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CreateAppInput } from './create.js';
|
|
2
|
+
import type { AppDetail, AppSummary } from './model.js';
|
|
3
|
+
import type { UpdateAppInput } from './update.js';
|
|
4
|
+
import type { Http } from '../../core/http/index.js';
|
|
5
|
+
import type { PageParams, Paginated } from '../pagination.js';
|
|
6
|
+
/**
|
|
7
|
+
* Every path of the apps resource in one place, so the endpoints can be read as a list. What each
|
|
8
|
+
* operation needs of its own — input shape, rules, request body — lives in its own file.
|
|
9
|
+
*
|
|
10
|
+
* The validating methods are async on purpose — a broken rule then arrives as a rejected promise,
|
|
11
|
+
* like a network failure, and one await covers both.
|
|
12
|
+
*/
|
|
13
|
+
export declare const apps: (http: Http) => {
|
|
14
|
+
get: (appId: string) => Promise<AppDetail>;
|
|
15
|
+
list: (params?: PageParams) => Promise<Paginated<AppSummary>>;
|
|
16
|
+
create: (input: CreateAppInput) => Promise<AppSummary>;
|
|
17
|
+
update: (appId: string, input: UpdateAppInput) => Promise<AppDetail>;
|
|
18
|
+
};
|
|
19
|
+
export type AppsApi = ReturnType<typeof apps>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { assertValid } from '../../core/validation.js';
|
|
2
|
+
import { pageQuery } from '../pagination.js';
|
|
3
|
+
import { toCreateRequest, validateCreateApp } from './create.js';
|
|
4
|
+
import { toUpdateRequest, validateUpdateApp } from './update.js';
|
|
5
|
+
/**
|
|
6
|
+
* Every path of the apps resource in one place, so the endpoints can be read as a list. What each
|
|
7
|
+
* operation needs of its own — input shape, rules, request body — lives in its own file.
|
|
8
|
+
*
|
|
9
|
+
* The validating methods are async on purpose — a broken rule then arrives as a rejected promise,
|
|
10
|
+
* like a network failure, and one await covers both.
|
|
11
|
+
*/
|
|
12
|
+
export const apps = (http) => ({
|
|
13
|
+
get: (appId) => http.get(`/apps/${appId}`),
|
|
14
|
+
list: (params) => http.get('/apps', { query: pageQuery(params) }),
|
|
15
|
+
create: async (input) => {
|
|
16
|
+
assertValid(validateCreateApp(input));
|
|
17
|
+
return http.post('/apps', toCreateRequest(input));
|
|
18
|
+
},
|
|
19
|
+
update: async (appId, input) => {
|
|
20
|
+
assertValid(validateUpdateApp(input));
|
|
21
|
+
return http.put(`/apps/${appId}`, toUpdateRequest(input));
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Issue } from '../../core/errors.js';
|
|
2
|
+
export type UpdateAppInput = {
|
|
3
|
+
appleBundleId?: string | undefined;
|
|
4
|
+
googleBundleId?: string | undefined;
|
|
5
|
+
title?: string | undefined;
|
|
6
|
+
};
|
|
7
|
+
type UpdateAppRequest = {
|
|
8
|
+
apple_bundle_id?: string;
|
|
9
|
+
google_bundle_id?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Same contract as the create rule: problems as a list, paths naming input fields. */
|
|
13
|
+
export declare const validateUpdateApp: (input: UpdateAppInput) => Issue[];
|
|
14
|
+
/** An absent field is left out of the body: the server tells "not given" from "set to empty". */
|
|
15
|
+
export declare const toUpdateRequest: (input: UpdateAppInput) => UpdateAppRequest;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Same contract as the create rule: problems as a list, paths naming input fields. */
|
|
2
|
+
export const validateUpdateApp = (input) => {
|
|
3
|
+
const fields = [input.title, input.appleBundleId, input.googleBundleId];
|
|
4
|
+
if (fields.every(value => value === undefined)) {
|
|
5
|
+
return [{ message: 'nothing to update: pass a title or a bundle id' }];
|
|
6
|
+
}
|
|
7
|
+
if (input.title?.trim() === '') {
|
|
8
|
+
return [{ message: 'must not be empty', path: 'title' }];
|
|
9
|
+
}
|
|
10
|
+
return [];
|
|
11
|
+
};
|
|
12
|
+
/** An absent field is left out of the body: the server tells "not given" from "set to empty". */
|
|
13
|
+
export const toUpdateRequest = (input) => {
|
|
14
|
+
const body = {};
|
|
15
|
+
if (input.title !== undefined) {
|
|
16
|
+
body.title = input.title;
|
|
17
|
+
}
|
|
18
|
+
if (input.appleBundleId !== undefined) {
|
|
19
|
+
body.apple_bundle_id = input.appleBundleId;
|
|
20
|
+
}
|
|
21
|
+
if (input.googleBundleId !== undefined) {
|
|
22
|
+
body.google_bundle_id = input.googleBundleId;
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DeviceCode } from '../../core/auth/device-flow.js';
|
|
2
|
+
import type { Http } from '../../core/http/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Step one of the device flow: ask for the code the user will type. The path sits with the
|
|
5
|
+
* reading of the answer, because here they are one thing — what core's port receives is this
|
|
6
|
+
* server's response renamed, and the two cannot be understood apart.
|
|
7
|
+
*/
|
|
8
|
+
export declare const requestDeviceCode: (http: Http) => Promise<DeviceCode>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CLIENT_ID } from './model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Step one of the device flow: ask for the code the user will type. The path sits with the
|
|
4
|
+
* reading of the answer, because here they are one thing — what core's port receives is this
|
|
5
|
+
* server's response renamed, and the two cannot be understood apart.
|
|
6
|
+
*/
|
|
7
|
+
export const requestDeviceCode = async (http) => {
|
|
8
|
+
const response = await http.post('/auth/device', { client_id: CLIENT_ID });
|
|
9
|
+
return {
|
|
10
|
+
deviceCode: response.device_code,
|
|
11
|
+
expiresInSec: response.expires_in,
|
|
12
|
+
// optional in the protocol; the default is knowledge about this server, so it
|
|
13
|
+
// belongs here and not in the orchestration
|
|
14
|
+
intervalSec: response.interval_seconds ?? 5,
|
|
15
|
+
userCode: response.user_code,
|
|
16
|
+
verificationUri: response.verification_uri,
|
|
17
|
+
verificationUriComplete: response.verification_uri_complete,
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door of the auth resource: re-exports only, no code of its own. The two device flow steps
|
|
3
|
+
* are reached through the port `auth()` returns, not imported directly.
|
|
4
|
+
*/
|
|
5
|
+
export { auth } from './resource.js';
|
|
6
|
+
export type { AuthUser, IssuedToken } from './model.js';
|
|
7
|
+
export type { AuthApi } from './resource.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the auth operations share: the shapes a successful login yields, and the name this client
|
|
3
|
+
* gives itself. An input shape belongs to its operation, not here.
|
|
4
|
+
*/
|
|
5
|
+
export type AuthUser = {
|
|
6
|
+
email: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
export type IssuedToken = {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
expiresInSec: number;
|
|
12
|
+
user: AuthUser;
|
|
13
|
+
};
|
|
14
|
+
/** The device flow identifies the client rather than authenticating it: there is no secret. */
|
|
15
|
+
export declare const CLIENT_ID = "adapty-cli";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the auth operations share: the shapes a successful login yields, and the name this client
|
|
3
|
+
* gives itself. An input shape belongs to its operation, not here.
|
|
4
|
+
*/
|
|
5
|
+
/** The device flow identifies the client rather than authenticating it: there is no secret. */
|
|
6
|
+
export const CLIENT_ID = 'adapty-cli';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IssuedToken } from './model.js';
|
|
2
|
+
import type { PollResult } from '../../core/auth/device-flow.js';
|
|
3
|
+
import type { Http } from '../../core/http/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Step two, asked over and over until the user is done: the answer is a token, a state of the
|
|
6
|
+
* protocol, or a failure — and telling the three apart is the whole of this operation.
|
|
7
|
+
*/
|
|
8
|
+
export declare const pollToken: (http: Http, deviceCode: string) => Promise<PollResult<IssuedToken>>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ApiError } from '../../core/errors.js';
|
|
2
|
+
import { CLIENT_ID } from './model.js';
|
|
3
|
+
/** A 400 carrying one of these codes is a state of the protocol, not a failure. */
|
|
4
|
+
const pollResultByCode = {
|
|
5
|
+
access_denied: { status: 'denied' },
|
|
6
|
+
authorization_pending: { status: 'pending' },
|
|
7
|
+
expired_token: { status: 'expired' },
|
|
8
|
+
slow_down: { status: 'slow_down' },
|
|
9
|
+
};
|
|
10
|
+
/** A protocol code as a state, or a real error when the code is not one of the protocol's. */
|
|
11
|
+
const toPollResult = (code, body) => {
|
|
12
|
+
const result = pollResultByCode[code];
|
|
13
|
+
if (result === undefined) {
|
|
14
|
+
throw new ApiError({
|
|
15
|
+
code,
|
|
16
|
+
details: body,
|
|
17
|
+
message: `Unexpected device flow response: ${code}`,
|
|
18
|
+
status: 200,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Step two, asked over and over until the user is done: the answer is a token, a state of the
|
|
25
|
+
* protocol, or a failure — and telling the three apart is the whole of this operation.
|
|
26
|
+
*/
|
|
27
|
+
export const pollToken = async (http, deviceCode) => {
|
|
28
|
+
try {
|
|
29
|
+
const response = await http.post('/auth/token', {
|
|
30
|
+
client_id: CLIENT_ID,
|
|
31
|
+
device_code: deviceCode,
|
|
32
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
33
|
+
});
|
|
34
|
+
// A protocol state normally arrives as a 400 and lands in the catch below, but the
|
|
35
|
+
// shipped CLI's types also anticipate a 200 whose body is { error: <code> }, and
|
|
36
|
+
// reading only the thrown case would call a pending login authorized.
|
|
37
|
+
if (!('access_token' in response)) {
|
|
38
|
+
return toPollResult(response.error, response);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
status: 'authorized',
|
|
42
|
+
token: {
|
|
43
|
+
accessToken: response.access_token,
|
|
44
|
+
expiresInSec: response.expires_in,
|
|
45
|
+
user: response.user,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof ApiError && error.code !== undefined) {
|
|
51
|
+
const result = pollResultByCode[error.code];
|
|
52
|
+
if (result) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IssuedToken } from './model.js';
|
|
2
|
+
import type { Http } from '../../core/http/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The auth resource: core's DeviceAuthApi port filled in by the two device flow steps, plus the
|
|
5
|
+
* endpoints that only need a path. A missing or stale token surfaces as AuthRequiredError.
|
|
6
|
+
*
|
|
7
|
+
* Unlike apps, not every path is listed here: a device flow step is its path and the reading of
|
|
8
|
+
* its answer together, so each keeps its own. What is left in this file is the shape of the
|
|
9
|
+
* resource — which port it satisfies, and what else it answers.
|
|
10
|
+
*/
|
|
11
|
+
export declare const auth: (http: Http) => {
|
|
12
|
+
me: () => Promise<Record<string, unknown>>;
|
|
13
|
+
revokeToken: (token: string) => Promise<undefined>;
|
|
14
|
+
pollToken(deviceCode: string): Promise<import("../../core/auth/device-flow.js").PollResult<IssuedToken>>;
|
|
15
|
+
requestDeviceCode(): Promise<import("../../core/auth/device-flow.js").DeviceCode>;
|
|
16
|
+
};
|
|
17
|
+
export type AuthApi = ReturnType<typeof auth>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { requestDeviceCode } from './device-code.js';
|
|
2
|
+
import { pollToken } from './poll-token.js';
|
|
3
|
+
/**
|
|
4
|
+
* The auth resource: core's DeviceAuthApi port filled in by the two device flow steps, plus the
|
|
5
|
+
* endpoints that only need a path. A missing or stale token surfaces as AuthRequiredError.
|
|
6
|
+
*
|
|
7
|
+
* Unlike apps, not every path is listed here: a device flow step is its path and the reading of
|
|
8
|
+
* its answer together, so each keeps its own. What is left in this file is the shape of the
|
|
9
|
+
* resource — which port it satisfies, and what else it answers.
|
|
10
|
+
*/
|
|
11
|
+
export const auth = (http) => {
|
|
12
|
+
const deviceAuth = {
|
|
13
|
+
pollToken: (deviceCode) => pollToken(http, deviceCode),
|
|
14
|
+
requestDeviceCode: () => requestDeviceCode(http),
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
...deviceAuth,
|
|
18
|
+
me: () => http.get('/me'),
|
|
19
|
+
revokeToken: (token) => http.post('/auth/tokens/revoke', { token }),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ErrorParser } from '../core/http/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* How this service words a rejection: `{ error_code, errors: { field: [message] } }`, sometimes
|
|
4
|
+
* just `{ error: 'code' }`. Ported from src/lib/errors.ts so a migrated command keeps saying
|
|
5
|
+
* "apple_bundle_id: already used" instead of "POST /apps failed with HTTP 400".
|
|
6
|
+
*
|
|
7
|
+
* Wired once in createAdapty: which shapes a service speaks is product knowledge, and ASA speaks
|
|
8
|
+
* another — which is why the transport takes the parser as a parameter.
|
|
9
|
+
*/
|
|
10
|
+
export declare const developerErrorParser: ErrorParser;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How this service words a rejection: `{ error_code, errors: { field: [message] } }`, sometimes
|
|
3
|
+
* just `{ error: 'code' }`. Ported from src/lib/errors.ts so a migrated command keeps saying
|
|
4
|
+
* "apple_bundle_id: already used" instead of "POST /apps failed with HTTP 400".
|
|
5
|
+
*
|
|
6
|
+
* Wired once in createAdapty: which shapes a service speaks is product knowledge, and ASA speaks
|
|
7
|
+
* another — which is why the transport takes the parser as a parameter.
|
|
8
|
+
*/
|
|
9
|
+
export const developerErrorParser = (_status, body) => {
|
|
10
|
+
if (typeof body !== 'object' || body === null) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
const { error, error_code: errorCode, errors } = body;
|
|
14
|
+
if (typeof errorCode === 'string' && errorCode !== '') {
|
|
15
|
+
// The bare code as the fallback message: what the published CLI prints, and still better
|
|
16
|
+
// than an HTTP status when the server sends no field errors
|
|
17
|
+
return { code: errorCode, message: fieldMessages(errors) ?? errorCode };
|
|
18
|
+
}
|
|
19
|
+
if (typeof error === 'string' && error !== '') {
|
|
20
|
+
return { code: error, message: error };
|
|
21
|
+
}
|
|
22
|
+
return {};
|
|
23
|
+
};
|
|
24
|
+
/** `non_field_errors` is the server's name for "about the request as a whole": printed bare. */
|
|
25
|
+
const fieldMessages = (errors) => {
|
|
26
|
+
if (typeof errors !== 'object' || errors === null) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const parts = [];
|
|
30
|
+
for (const [field, messages] of Object.entries(errors)) {
|
|
31
|
+
for (const message of Array.isArray(messages) ? messages : [messages]) {
|
|
32
|
+
if (typeof message === 'string') {
|
|
33
|
+
parts.push(field === 'non_field_errors' ? message : `${field}: ${message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return parts.length > 0 ? parts.join('; ') : undefined;
|
|
38
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AccessLevelsApi } from './access-levels.js';
|
|
2
|
+
import type { AppsApi } from './apps/index.js';
|
|
3
|
+
import type { AuthApi } from './auth/index.js';
|
|
4
|
+
import type { Clock } from '../core/clock.js';
|
|
5
|
+
import type { RetryAttempt } from '../core/http/index.js';
|
|
6
|
+
export { platforms } from './apps/index.js';
|
|
7
|
+
export { developerErrorParser } from './errors.js';
|
|
8
|
+
export type { AccessLevel, AccessLevelList, AccessLevelsApi } from './access-levels.js';
|
|
9
|
+
export type { AppDetail, AppsApi, AppSummary, CreateAppInput, UpdateAppInput } from './apps/index.js';
|
|
10
|
+
export type { AuthApi, AuthUser, IssuedToken } from './auth/index.js';
|
|
11
|
+
export type { PageParams, Paginated, Pagination } from './pagination.js';
|
|
12
|
+
/** Exported because the adapter compares the resolved URL with it and warns when they differ. */
|
|
13
|
+
export declare const DEFAULT_ADAPTY_API_URL = "https://api-admin.adapty.io/api/v1/developer";
|
|
14
|
+
/**
|
|
15
|
+
* Narrower than HttpOptions on purpose: the transport seams (parseError, shouldRetry, the retry
|
|
16
|
+
* policy, trailingSlash) are product knowledge, and two adapters overriding them would read the
|
|
17
|
+
* same answers differently. What is left is what a consumer really owns.
|
|
18
|
+
*/
|
|
19
|
+
export type AdaptyOptions = {
|
|
20
|
+
baseUrl?: string | undefined;
|
|
21
|
+
clock?: Clock | undefined;
|
|
22
|
+
fetch?: typeof globalThis.fetch | undefined;
|
|
23
|
+
onRetry?: ((info: RetryAttempt) => void) | undefined;
|
|
24
|
+
signal?: AbortSignal | undefined;
|
|
25
|
+
/** Without a token only auth is usable — that is how login builds the sdk. */
|
|
26
|
+
token?: string | undefined;
|
|
27
|
+
/** Comes from the adapter, so server logs can tell the CLI from an MCP server. */
|
|
28
|
+
userAgent?: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
export type Adapty = {
|
|
31
|
+
accessLevels: AccessLevelsApi;
|
|
32
|
+
apps: AppsApi;
|
|
33
|
+
auth: AuthApi;
|
|
34
|
+
};
|
|
35
|
+
/** The assembly point of the developer API: one transport, resources on top of it. */
|
|
36
|
+
export declare const createAdapty: (options?: AdaptyOptions) => Adapty;
|