adapty 0.8.5 → 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 (114) hide show
  1. package/README.md +6 -1
  2. package/dist/cli/base/adapty/adapty-command.d.ts +21 -0
  3. package/dist/cli/base/adapty/adapty-command.js +47 -0
  4. package/dist/cli/base/adapty/build.d.ts +11 -0
  5. package/dist/cli/base/adapty/build.js +13 -0
  6. package/dist/cli/base/adapty/index.d.ts +5 -0
  7. package/dist/cli/base/adapty/index.js +3 -0
  8. package/dist/cli/base/adapty/openSession.d.ts +22 -0
  9. package/dist/cli/base/adapty/openSession.js +25 -0
  10. package/dist/cli/base/base-command.d.ts +30 -0
  11. package/dist/cli/base/base-command.js +64 -0
  12. package/dist/cli/commands/apps/create.d.ts +19 -0
  13. package/dist/cli/commands/apps/create.js +68 -0
  14. package/dist/cli/commands/apps/get.d.ts +10 -0
  15. package/dist/cli/commands/apps/get.js +14 -0
  16. package/dist/cli/commands/apps/list.d.ts +11 -0
  17. package/dist/cli/commands/apps/list.js +14 -0
  18. package/dist/cli/commands/apps/update.d.ts +15 -0
  19. package/dist/cli/commands/apps/update.js +30 -0
  20. package/dist/cli/commands/auth/login.d.ts +11 -0
  21. package/dist/cli/commands/auth/login.js +68 -0
  22. package/dist/cli/commands/auth/logout.d.ts +12 -0
  23. package/dist/cli/commands/auth/logout.js +28 -0
  24. package/dist/cli/commands/auth/revoke.d.ts +13 -0
  25. package/dist/cli/commands/auth/revoke.js +32 -0
  26. package/dist/cli/commands/auth/status/index.d.ts +7 -0
  27. package/dist/cli/commands/auth/status/index.js +22 -0
  28. package/dist/cli/commands/auth/status/lib/render.d.ts +3 -0
  29. package/dist/cli/commands/auth/status/lib/render.js +14 -0
  30. package/dist/cli/commands/auth/status/lib/result.d.ts +15 -0
  31. package/dist/cli/commands/auth/status/lib/result.js +1 -0
  32. package/dist/cli/commands/auth/whoami.d.ts +10 -0
  33. package/dist/cli/commands/auth/whoami.js +16 -0
  34. package/dist/cli/errors.d.ts +39 -0
  35. package/dist/cli/errors.js +92 -0
  36. package/dist/cli/flags.d.ts +16 -0
  37. package/dist/cli/flags.js +27 -0
  38. package/dist/cli/views/list.d.ts +6 -0
  39. package/dist/cli/views/list.js +10 -0
  40. package/dist/cli/views/record.d.ts +9 -0
  41. package/dist/cli/views/record.js +14 -0
  42. package/dist/commands/apps/create.d.ts +1 -14
  43. package/dist/commands/apps/create.js +2 -57
  44. package/dist/commands/apps/get.d.ts +1 -11
  45. package/dist/commands/apps/get.js +2 -22
  46. package/dist/commands/apps/list.d.ts +1 -13
  47. package/dist/commands/apps/list.js +2 -19
  48. package/dist/commands/apps/update.d.ts +1 -16
  49. package/dist/commands/apps/update.js +2 -41
  50. package/dist/commands/asa/whoami.js +6 -2
  51. package/dist/commands/auth/login.d.ts +1 -8
  52. package/dist/commands/auth/login.js +2 -122
  53. package/dist/commands/auth/logout.d.ts +1 -9
  54. package/dist/commands/auth/logout.js +2 -18
  55. package/dist/commands/auth/revoke.d.ts +1 -9
  56. package/dist/commands/auth/revoke.js +2 -21
  57. package/dist/commands/auth/status.d.ts +1 -7
  58. package/dist/commands/auth/status.js +2 -26
  59. package/dist/commands/auth/whoami.d.ts +1 -7
  60. package/dist/commands/auth/whoami.js +2 -15
  61. package/dist/lib/api-schemas.d.ts +7 -3
  62. package/dist/lib/asa-schemas.d.ts +1 -1
  63. package/dist/sdk/adapty/access-levels.d.ts +15 -0
  64. package/dist/sdk/adapty/access-levels.js +4 -0
  65. package/dist/sdk/adapty/apps/create.d.ts +26 -0
  66. package/dist/sdk/adapty/apps/create.js +36 -0
  67. package/dist/sdk/adapty/apps/index.d.ts +13 -0
  68. package/dist/sdk/adapty/apps/index.js +9 -0
  69. package/dist/sdk/adapty/apps/model.d.ts +24 -0
  70. package/dist/sdk/adapty/apps/model.js +10 -0
  71. package/dist/sdk/adapty/apps/resource.d.ts +19 -0
  72. package/dist/sdk/adapty/apps/resource.js +23 -0
  73. package/dist/sdk/adapty/apps/update.d.ts +16 -0
  74. package/dist/sdk/adapty/apps/update.js +25 -0
  75. package/dist/sdk/adapty/auth/device-code.d.ts +8 -0
  76. package/dist/sdk/adapty/auth/device-code.js +19 -0
  77. package/dist/sdk/adapty/auth/index.d.ts +7 -0
  78. package/dist/sdk/adapty/auth/index.js +5 -0
  79. package/dist/sdk/adapty/auth/model.d.ts +15 -0
  80. package/dist/sdk/adapty/auth/model.js +6 -0
  81. package/dist/sdk/adapty/auth/poll-token.d.ts +8 -0
  82. package/dist/sdk/adapty/auth/poll-token.js +58 -0
  83. package/dist/sdk/adapty/auth/resource.d.ts +17 -0
  84. package/dist/sdk/adapty/auth/resource.js +21 -0
  85. package/dist/sdk/adapty/errors.d.ts +10 -0
  86. package/dist/sdk/adapty/errors.js +38 -0
  87. package/dist/sdk/adapty/index.d.ts +36 -0
  88. package/dist/sdk/adapty/index.js +27 -0
  89. package/dist/sdk/adapty/pagination.d.ts +23 -0
  90. package/dist/sdk/adapty/pagination.js +1 -0
  91. package/dist/sdk/core/auth/device-flow.d.ts +46 -0
  92. package/dist/sdk/core/auth/device-flow.js +62 -0
  93. package/dist/sdk/core/clock.d.ts +7 -0
  94. package/dist/sdk/core/clock.js +21 -0
  95. package/dist/sdk/core/errors.d.ts +81 -0
  96. package/dist/sdk/core/errors.js +84 -0
  97. package/dist/sdk/core/http/client.d.ts +46 -0
  98. package/dist/sdk/core/http/client.js +127 -0
  99. package/dist/sdk/core/http/index.d.ts +11 -0
  100. package/dist/sdk/core/http/index.js +7 -0
  101. package/dist/sdk/core/http/policies.d.ts +14 -0
  102. package/dist/sdk/core/http/policies.js +29 -0
  103. package/dist/sdk/core/http/retry.d.ts +27 -0
  104. package/dist/sdk/core/http/retry.js +22 -0
  105. package/dist/sdk/core/http/url.d.ts +3 -0
  106. package/dist/sdk/core/http/url.js +19 -0
  107. package/dist/sdk/core/session.d.ts +20 -0
  108. package/dist/sdk/core/session.js +69 -0
  109. package/dist/sdk/core/testing.d.ts +25 -0
  110. package/dist/sdk/core/testing.js +46 -0
  111. package/dist/sdk/core/validation.d.ts +10 -0
  112. package/dist/sdk/core/validation.js +14 -0
  113. package/oclif.manifest.json +813 -813
  114. package/package.json +10 -2
@@ -1,122 +1,2 @@
1
- import { Command } from '@oclif/core';
2
- import open from 'open';
3
- import { ApiClient } from '../../lib/api-client.js';
4
- import { onAppHost } from '../../lib/app-url.js';
5
- import { buildUserAgent } from '../../lib/client-from-config.js';
6
- import { readConfig, writeConfig } from '../../lib/config.js';
7
- import { ApiError } from '../../lib/errors.js';
8
- function sleep(ms) {
9
- return new Promise((resolve) => {
10
- setTimeout(resolve, ms);
11
- });
12
- }
13
- export default class AuthLogin extends Command {
14
- static description = 'Authenticate with Adapty via browser';
15
- static examples = ['<%= config.bin %> auth login'];
16
- async run() {
17
- await this.parse(AuthLogin);
18
- const config = await readConfig(this.config.configDir);
19
- if (config.access_token && config.user) {
20
- this.log(`Already authenticated as ${config.user.email}. Re-authenticating...`);
21
- }
22
- const client = new ApiClient({
23
- userAgent: buildUserAgent(this.config),
24
- });
25
- let device;
26
- try {
27
- device = await client.post('/auth/device', { client_id: 'adapty-cli' });
28
- }
29
- catch (error) {
30
- this.error(error instanceof Error ? error.message : 'Failed to initiate auth flow', { exit: 1 });
31
- }
32
- const verificationUrl = this.verificationUrl(device.verification_uri_complete);
33
- this.log(`\nYour code: ${device.user_code}\n`);
34
- this.log(`If browser doesn't open, visit: ${verificationUrl}\n`);
35
- if (process.stdin.isTTY) {
36
- try {
37
- await open(verificationUrl);
38
- }
39
- catch {
40
- // browser open failed silently — URL already printed
41
- }
42
- }
43
- this.log('Waiting for authorization...');
44
- let interval = Math.max((device.interval_seconds || 5) * 1000, 5000);
45
- const deadline = Date.now() + device.expires_in * 1000;
46
- let consecutiveErrors = 0;
47
- // Handle Ctrl+C
48
- const onSignal = () => {
49
- this.log('\nLogin cancelled.');
50
- // eslint-disable-next-line n/no-process-exit -- FIXME if you see this
51
- process.exit(0);
52
- };
53
- process.on('SIGINT', onSignal);
54
- try {
55
- while (Date.now() < deadline) {
56
- await sleep(interval);
57
- let result;
58
- try {
59
- result = await client.post('/auth/token', {
60
- client_id: 'adapty-cli',
61
- device_code: device.device_code,
62
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
63
- });
64
- }
65
- catch (error) {
66
- if (error instanceof ApiError) {
67
- switch (error.errorCode) {
68
- case 'authorization_pending': {
69
- continue;
70
- }
71
- case 'slow_down': {
72
- interval += 5000;
73
- continue;
74
- }
75
- case 'access_denied': {
76
- this.error('Authorization denied.', { exit: 1 });
77
- break;
78
- }
79
- case 'expired_token': {
80
- this.error('Code expired. Run `adapty auth login` again.', { exit: 1 });
81
- break;
82
- }
83
- default:
84
- // unexpected API error — fall through to network error handling
85
- }
86
- }
87
- consecutiveErrors++;
88
- if (consecutiveErrors >= 10) {
89
- this.error('Too many consecutive network errors. Check your connection and try again.', { exit: 1 });
90
- }
91
- if (consecutiveErrors >= 3) {
92
- process.stderr.write(`Warning: ${consecutiveErrors} consecutive network errors (${error instanceof Error ? error.message : 'unknown'})\n`);
93
- }
94
- continue;
95
- }
96
- consecutiveErrors = 0;
97
- if ('access_token' in result) {
98
- await writeConfig({
99
- access_token: result.access_token,
100
- user: result.user,
101
- }, this.config.configDir);
102
- this.log(`\nAuthenticated as ${result.user.email}`);
103
- this.log(`Token saved to ${this.config.configDir}/config.json`);
104
- return;
105
- }
106
- }
107
- this.error('Code expired. Run `adapty auth login` again.', { exit: 1 });
108
- }
109
- finally {
110
- process.removeListener('SIGINT', onSignal);
111
- }
112
- }
113
- /** Keeps the browser on the configured dashboard host, when ADAPTY_APP_URL asks for one. */
114
- verificationUrl(issuedUrl) {
115
- try {
116
- return onAppHost(issuedUrl);
117
- }
118
- catch (error) {
119
- this.error(error instanceof Error ? error.message : String(error), { exit: 2 });
120
- }
121
- }
122
- }
1
+ // Implementation lives in the new cli layer; oclif discovers commands only under src/commands.
2
+ export { default } from '../../cli/commands/auth/login.js';
@@ -1,9 +1 @@
1
- import { Command } from '@oclif/core';
2
- export default class AuthLogout extends Command {
3
- static description: string;
4
- static enableJsonFlag: boolean;
5
- static examples: string[];
6
- run(): Promise<{
7
- status: string;
8
- }>;
9
- }
1
+ export { default } from '../../cli/commands/auth/logout.js';
@@ -1,18 +1,2 @@
1
- import { Command } from '@oclif/core';
2
- import { readConfig, writeConfig } from '../../lib/config.js';
3
- export default class AuthLogout extends Command {
4
- static description = 'Remove stored authentication token';
5
- static enableJsonFlag = true;
6
- static examples = ['<%= config.bin %> auth logout'];
7
- async run() {
8
- await this.parse(AuthLogout);
9
- const config = await readConfig(this.config.configDir);
10
- if (!config.access_token) {
11
- this.log('Not currently authenticated.');
12
- return { status: 'not_authenticated' };
13
- }
14
- await writeConfig({}, this.config.configDir);
15
- this.log('Logged out. Note: token remains valid server-side until expiry.');
16
- return { status: 'logged_out' };
17
- }
18
- }
1
+ // Implementation lives in the new cli layer; oclif discovers commands only under src/commands.
2
+ export { default } from '../../cli/commands/auth/logout.js';
@@ -1,9 +1 @@
1
- import { Command } from '@oclif/core';
2
- export default class AuthRevoke extends Command {
3
- static description: string;
4
- static enableJsonFlag: boolean;
5
- static examples: string[];
6
- run(): Promise<{
7
- status: string;
8
- }>;
9
- }
1
+ export { default } from '../../cli/commands/auth/revoke.js';
@@ -1,21 +1,2 @@
1
- import { Command } from '@oclif/core';
2
- import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
- import { readConfig, writeConfig } from '../../lib/config.js';
4
- export default class AuthRevoke extends Command {
5
- static description = 'Revoke the current authentication token';
6
- static enableJsonFlag = true;
7
- static examples = ['<%= config.bin %> auth revoke'];
8
- async run() {
9
- await this.parse(AuthRevoke);
10
- const config = await readConfig(this.config.configDir);
11
- if (!config.access_token) {
12
- this.log('Not currently authenticated.');
13
- return { status: 'not_authenticated' };
14
- }
15
- const client = await createAuthenticatedClient(this.config);
16
- await client.post('/auth/tokens/revoke', { token: config.access_token });
17
- await writeConfig({}, this.config.configDir);
18
- this.log('Token revoked and logged out.');
19
- return { status: 'revoked' };
20
- }
21
- }
1
+ // Implementation lives in the new cli layer; oclif discovers commands only under src/commands.
2
+ export { default } from '../../cli/commands/auth/revoke.js';
@@ -1,7 +1 @@
1
- import { Command } from '@oclif/core';
2
- export default class AuthStatus extends Command {
3
- static description: string;
4
- static enableJsonFlag: boolean;
5
- static examples: string[];
6
- run(): Promise<Record<string, unknown>>;
7
- }
1
+ export { default } from '../../cli/commands/auth/status/index.js';
@@ -1,26 +1,2 @@
1
- import { Command } from '@oclif/core';
2
- import { readConfig } from '../../lib/config.js';
3
- export default class AuthStatus extends Command {
4
- static description = 'Show current authentication state (local only)';
5
- static enableJsonFlag = true;
6
- static examples = ['<%= config.bin %> auth status'];
7
- async run() {
8
- await this.parse(AuthStatus);
9
- const config = await readConfig(this.config.configDir);
10
- if (!config.access_token || !config.user) {
11
- this.log('Not authenticated. Run `adapty auth login`.');
12
- return { authenticated: false };
13
- }
14
- const masked = config.access_token.slice(0, 8) + '****';
15
- const configPath = `${this.config.configDir}/config.json`;
16
- this.log(`Email: ${config.user.email}`);
17
- this.log(`Token: ${masked}`);
18
- this.log(`Config: ${configPath}`);
19
- return {
20
- authenticated: true,
21
- config_path: configPath,
22
- email: config.user.email,
23
- token_prefix: config.access_token.slice(0, 8),
24
- };
25
- }
26
- }
1
+ // Implementation lives in the new cli layer; oclif discovers commands only under src/commands.
2
+ export { default } from '../../cli/commands/auth/status/index.js';
@@ -1,7 +1 @@
1
- import { Command } from '@oclif/core';
2
- export default class AuthWhoami extends Command {
3
- static description: string;
4
- static enableJsonFlag: boolean;
5
- static examples: string[];
6
- run(): Promise<Record<string, unknown>>;
7
- }
1
+ export { default } from '../../cli/commands/auth/whoami.js';
@@ -1,15 +1,2 @@
1
- import { Command } from '@oclif/core';
2
- import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
- import { printResponse } from '../../lib/output.js';
4
- export default class AuthWhoami extends Command {
5
- static description = 'Show current user info from server (verifies token)';
6
- static enableJsonFlag = true;
7
- static examples = ['<%= config.bin %> auth whoami'];
8
- async run() {
9
- await this.parse(AuthWhoami);
10
- const client = await createAuthenticatedClient(this.config);
11
- const me = await client.get('/me');
12
- printResponse(me, this.log.bind(this));
13
- return me;
14
- }
15
- }
1
+ // Implementation lives in the new cli layer; oclif discovers commands only under src/commands.
2
+ export { default } from '../../cli/commands/auth/whoami.js';
@@ -78,7 +78,8 @@ export type FlowStatus = string;
78
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
  };
84
85
  export type FlowRemoteConfigDTO = {
@@ -152,8 +153,11 @@ export type PlacementAudienceEntryDTO = PlacementFlowAudienceEntryDTO | Placemen
152
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
162
  };
159
163
  export type PlacementDetailDTO = {
@@ -1,4 +1,4 @@
1
- export type AsaAccessSource = 'allowlist' | 'legacy' | 'none' | 'payg';
1
+ export type AsaAccessSource = 'allowlist' | 'legacy' | 'none' | 'payg' | 'trial';
2
2
  export type AsaAppleCredentialsStatus = 'active' | 'expired' | 'invalid' | 'unset';
3
3
  export type AsaKeywordMatchType = 'BROAD' | 'EXACT';
4
4
  export type AsaKeywordStatus = 'ACTIVE' | 'PAUSED';
@@ -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,4 @@
1
+ /** Only the read `apps create` needs; the rest arrives with the access-levels commands. */
2
+ export const accessLevels = (http) => ({
3
+ list: (appId) => http.get(`/apps/${appId}/access-levels`),
4
+ });
@@ -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,5 @@
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';
@@ -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>>;