adapty 0.1.0

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 (71) hide show
  1. package/README.md +110 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/access-levels/create.d.ts +18 -0
  7. package/dist/commands/access-levels/create.js +28 -0
  8. package/dist/commands/access-levels/get.d.ts +19 -0
  9. package/dist/commands/access-levels/get.js +26 -0
  10. package/dist/commands/access-levels/list.d.ts +19 -0
  11. package/dist/commands/access-levels/list.js +20 -0
  12. package/dist/commands/access-levels/update.d.ts +20 -0
  13. package/dist/commands/access-levels/update.js +32 -0
  14. package/dist/commands/apps/create.d.ts +25 -0
  15. package/dist/commands/apps/create.js +47 -0
  16. package/dist/commands/apps/get.d.ts +20 -0
  17. package/dist/commands/apps/get.js +29 -0
  18. package/dist/commands/apps/list.d.ts +18 -0
  19. package/dist/commands/apps/list.js +19 -0
  20. package/dist/commands/apps/update.d.ts +22 -0
  21. package/dist/commands/apps/update.js +42 -0
  22. package/dist/commands/auth/login.d.ts +6 -0
  23. package/dist/commands/auth/login.js +106 -0
  24. package/dist/commands/auth/logout.d.ts +9 -0
  25. package/dist/commands/auth/logout.js +18 -0
  26. package/dist/commands/auth/status.d.ts +7 -0
  27. package/dist/commands/auth/status.js +26 -0
  28. package/dist/commands/auth/whoami.d.ts +16 -0
  29. package/dist/commands/auth/whoami.js +21 -0
  30. package/dist/commands/paywalls/create.d.ts +17 -0
  31. package/dist/commands/paywalls/create.js +31 -0
  32. package/dist/commands/paywalls/get.d.ts +19 -0
  33. package/dist/commands/paywalls/get.js +26 -0
  34. package/dist/commands/paywalls/list.d.ts +18 -0
  35. package/dist/commands/paywalls/list.js +20 -0
  36. package/dist/commands/paywalls/update.d.ts +20 -0
  37. package/dist/commands/paywalls/update.js +37 -0
  38. package/dist/commands/placements/create.d.ts +19 -0
  39. package/dist/commands/placements/create.js +30 -0
  40. package/dist/commands/placements/get.d.ts +20 -0
  41. package/dist/commands/placements/get.js +27 -0
  42. package/dist/commands/placements/list.d.ts +19 -0
  43. package/dist/commands/placements/list.js +20 -0
  44. package/dist/commands/placements/update.d.ts +22 -0
  45. package/dist/commands/placements/update.js +36 -0
  46. package/dist/commands/products/create.d.ts +33 -0
  47. package/dist/commands/products/create.js +59 -0
  48. package/dist/commands/products/get.d.ts +32 -0
  49. package/dist/commands/products/get.js +34 -0
  50. package/dist/commands/products/list.d.ts +30 -0
  51. package/dist/commands/products/list.js +31 -0
  52. package/dist/commands/products/update.d.ts +36 -0
  53. package/dist/commands/products/update.js +65 -0
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +1 -0
  56. package/dist/lib/api-client.d.ts +15 -0
  57. package/dist/lib/api-client.js +75 -0
  58. package/dist/lib/auth.d.ts +1 -0
  59. package/dist/lib/auth.js +8 -0
  60. package/dist/lib/client-from-config.d.ts +4 -0
  61. package/dist/lib/client-from-config.js +15 -0
  62. package/dist/lib/config.d.ts +9 -0
  63. package/dist/lib/config.js +22 -0
  64. package/dist/lib/errors.d.ts +23 -0
  65. package/dist/lib/errors.js +63 -0
  66. package/dist/lib/flags.d.ts +22 -0
  67. package/dist/lib/flags.js +36 -0
  68. package/dist/lib/output.d.ts +6 -0
  69. package/dist/lib/output.js +18 -0
  70. package/oclif.manifest.json +1284 -0
  71. package/package.json +100 -0
@@ -0,0 +1,59 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
+ import { appFlag } from '../../lib/flags.js';
4
+ const VALID_PERIODS = ['weekly', 'monthly', '2_months', '3_months', '6_months', 'yearly', 'lifetime'];
5
+ export default class ProductsCreate extends Command {
6
+ static description = 'Create a product with vendor products per platform';
7
+ static enableJsonFlag = true;
8
+ static examples = [
9
+ '<%= config.bin %> products create --app UUID --name "Monthly" --access-level-id UUID --period monthly --ios-product-id com.example.monthly',
10
+ ];
11
+ static flags = {
12
+ ...appFlag,
13
+ 'access-level-id': Flags.string({ description: 'Access level ID (UUID)', required: true }),
14
+ 'android-base-plan-id': Flags.string({ description: 'Android base plan ID' }),
15
+ 'android-product-id': Flags.string({ description: 'Android product ID' }),
16
+ 'ios-product-id': Flags.string({ description: 'iOS product ID' }),
17
+ name: Flags.string({ description: 'Product name', required: true }),
18
+ period: Flags.string({
19
+ description: 'Subscription period (weekly, monthly, 2_months, 3_months, 6_months, yearly, lifetime)',
20
+ required: true,
21
+ }),
22
+ };
23
+ async run() {
24
+ const { flags } = await this.parse(ProductsCreate);
25
+ if (!VALID_PERIODS.includes(flags.period)) {
26
+ this.error(`Invalid period. Must be one of: ${VALID_PERIODS.join(', ')}`, { exit: 2 });
27
+ }
28
+ if (!flags['ios-product-id'] && !flags['android-product-id']) {
29
+ this.error('At least one of --ios-product-id or --android-product-id is required', { exit: 2 });
30
+ }
31
+ if (flags['android-product-id'] && !flags['android-base-plan-id'] && flags.period !== 'lifetime') {
32
+ this.error('--android-base-plan-id is required with --android-product-id for subscriptions', { exit: 2 });
33
+ }
34
+ const client = await createAuthenticatedClient(this.config);
35
+ const body = {
36
+ access_level_id: flags['access-level-id'],
37
+ name: flags.name,
38
+ period: flags.period,
39
+ };
40
+ if (flags['ios-product-id'])
41
+ body.ios_product_id = flags['ios-product-id'];
42
+ if (flags['android-product-id'])
43
+ body.android_product_id = flags['android-product-id'];
44
+ if (flags['android-base-plan-id'])
45
+ body.android_base_plan_id = flags['android-base-plan-id'];
46
+ const result = await client.post(`/apps/${flags.app}/products`, body);
47
+ this.log('Product created!');
48
+ this.log(`ID: ${result.id}`);
49
+ this.log(`Name: ${result.name}`);
50
+ if (result.vendor_products.ios) {
51
+ this.log(`iOS Product: ${result.vendor_products.ios.product_id}`);
52
+ }
53
+ if (result.vendor_products.android) {
54
+ const bp = result.vendor_products.android.base_plan_id;
55
+ this.log(`Android Product: ${result.vendor_products.android.product_id}${bp ? ` (base plan: ${bp})` : ''}`);
56
+ }
57
+ return result;
58
+ }
59
+ }
@@ -0,0 +1,32 @@
1
+ import { Command } from '@oclif/core';
2
+ interface VendorProducts {
3
+ android?: {
4
+ base_plan_id?: string;
5
+ id: string;
6
+ product_id: string;
7
+ };
8
+ ios?: {
9
+ id: string;
10
+ product_id: string;
11
+ };
12
+ }
13
+ interface ProductDetailResponse {
14
+ access_level_id: string;
15
+ id: string;
16
+ name: string;
17
+ period: string;
18
+ vendor_products: VendorProducts;
19
+ }
20
+ export default class ProductsGet extends Command {
21
+ static args: {
22
+ product_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
23
+ };
24
+ static description: string;
25
+ static enableJsonFlag: boolean;
26
+ static examples: string[];
27
+ static flags: {
28
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
29
+ };
30
+ run(): Promise<ProductDetailResponse>;
31
+ }
32
+ export {};
@@ -0,0 +1,34 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
+ import { appFlag, isValidUuid } from '../../lib/flags.js';
4
+ export default class ProductsGet extends Command {
5
+ static args = {
6
+ product_id: Args.string({ description: 'Product ID (UUID)', required: true }),
7
+ };
8
+ static description = 'Get product details';
9
+ static enableJsonFlag = true;
10
+ static examples = ['<%= config.bin %> products get --app UUID 550e8400-e29b-41d4-a716-446655440000'];
11
+ static flags = {
12
+ ...appFlag,
13
+ };
14
+ async run() {
15
+ const { args, flags } = await this.parse(ProductsGet);
16
+ if (!isValidUuid(args.product_id)) {
17
+ this.error('Invalid product ID format.', { exit: 2 });
18
+ }
19
+ const client = await createAuthenticatedClient(this.config);
20
+ const result = await client.get(`/apps/${flags.app}/products/${args.product_id}`);
21
+ this.log(`ID: ${result.id}`);
22
+ this.log(`Name: ${result.name}`);
23
+ this.log(`Period: ${result.period}`);
24
+ this.log(`Access Level ID: ${result.access_level_id}`);
25
+ if (result.vendor_products.ios) {
26
+ this.log(`iOS Product: ${result.vendor_products.ios.product_id}`);
27
+ }
28
+ if (result.vendor_products.android) {
29
+ const bp = result.vendor_products.android.base_plan_id;
30
+ this.log(`Android Product: ${result.vendor_products.android.product_id}${bp ? ` (base plan: ${bp})` : ''}`);
31
+ }
32
+ return result;
33
+ }
34
+ }
@@ -0,0 +1,30 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type PaginatedResponse } from '../../lib/flags.js';
3
+ interface VendorProducts {
4
+ android?: {
5
+ base_plan_id?: string;
6
+ id: string;
7
+ product_id: string;
8
+ };
9
+ ios?: {
10
+ id: string;
11
+ product_id: string;
12
+ };
13
+ }
14
+ interface ProductItem {
15
+ id: string;
16
+ name: string;
17
+ vendor_products: VendorProducts;
18
+ }
19
+ export default class ProductsList extends Command {
20
+ static description: string;
21
+ static enableJsonFlag: boolean;
22
+ static examples: string[];
23
+ static flags: {
24
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
25
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
26
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
27
+ };
28
+ run(): Promise<PaginatedResponse<ProductItem>>;
29
+ }
30
+ export {};
@@ -0,0 +1,31 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
+ import { appFlag, paginationFlags, paginationParams } from '../../lib/flags.js';
4
+ import { printList } from '../../lib/output.js';
5
+ export default class ProductsList extends Command {
6
+ static description = 'List products for an app';
7
+ static enableJsonFlag = true;
8
+ static examples = ['<%= config.bin %> products list --app 550e8400-...'];
9
+ static flags = {
10
+ ...appFlag,
11
+ ...paginationFlags,
12
+ };
13
+ async run() {
14
+ const { flags } = await this.parse(ProductsList);
15
+ const client = await createAuthenticatedClient(this.config);
16
+ const result = await client.get(`/apps/${flags.app}/products`, paginationParams(flags));
17
+ printList(result.data.map((p) => {
18
+ const row = { ID: p.id, Name: p.name };
19
+ if (p.vendor_products.ios)
20
+ row['iOS Product'] = p.vendor_products.ios.product_id;
21
+ if (p.vendor_products.android) {
22
+ const bp = p.vendor_products.android.base_plan_id;
23
+ row['Android Product'] = bp
24
+ ? `${p.vendor_products.android.product_id} (base plan: ${bp})`
25
+ : p.vendor_products.android.product_id;
26
+ }
27
+ return row;
28
+ }), this.log.bind(this), result.meta.pagination);
29
+ return result;
30
+ }
31
+ }
@@ -0,0 +1,36 @@
1
+ import { Command } from '@oclif/core';
2
+ interface VendorProducts {
3
+ android?: {
4
+ base_plan_id?: string;
5
+ id: string;
6
+ product_id: string;
7
+ };
8
+ ios?: {
9
+ id: string;
10
+ product_id: string;
11
+ };
12
+ }
13
+ interface ProductResponse {
14
+ id: string;
15
+ name: string;
16
+ vendor_products: VendorProducts;
17
+ }
18
+ export default class ProductsUpdate extends Command {
19
+ static args: {
20
+ product_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
21
+ };
22
+ static description: string;
23
+ static enableJsonFlag: boolean;
24
+ static examples: string[];
25
+ static flags: {
26
+ 'access-level-id': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
27
+ 'android-base-plan-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
28
+ 'android-product-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
29
+ 'ios-product-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
30
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
31
+ period: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
32
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
33
+ };
34
+ run(): Promise<ProductResponse>;
35
+ }
36
+ export {};
@@ -0,0 +1,65 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { createAuthenticatedClient } from '../../lib/client-from-config.js';
3
+ import { appFlag, isValidUuid } from '../../lib/flags.js';
4
+ const VALID_PERIODS = ['weekly', 'monthly', '2_months', '3_months', '6_months', 'yearly', 'lifetime'];
5
+ export default class ProductsUpdate extends Command {
6
+ static args = {
7
+ product_id: Args.string({ description: 'Product ID (UUID)', required: true }),
8
+ };
9
+ static description = 'Update a product';
10
+ static enableJsonFlag = true;
11
+ static examples = [
12
+ '<%= config.bin %> products update --app UUID 550e8400-... --name "Monthly" --access-level-id UUID --period monthly --ios-product-id com.example.monthly',
13
+ ];
14
+ static flags = {
15
+ ...appFlag,
16
+ 'access-level-id': Flags.string({ description: 'Access level ID (UUID)', required: true }),
17
+ 'android-base-plan-id': Flags.string({ description: 'Android base plan ID' }),
18
+ 'android-product-id': Flags.string({ description: 'Android product ID' }),
19
+ 'ios-product-id': Flags.string({ description: 'iOS product ID' }),
20
+ name: Flags.string({ description: 'Product name', required: true }),
21
+ period: Flags.string({
22
+ description: 'Subscription period (weekly, monthly, 2_months, 3_months, 6_months, yearly, lifetime)',
23
+ required: true,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { args, flags } = await this.parse(ProductsUpdate);
28
+ if (!isValidUuid(args.product_id)) {
29
+ this.error('Invalid product ID format.', { exit: 2 });
30
+ }
31
+ if (!VALID_PERIODS.includes(flags.period)) {
32
+ this.error(`Invalid period. Must be one of: ${VALID_PERIODS.join(', ')}`, { exit: 2 });
33
+ }
34
+ if (!flags['ios-product-id'] && !flags['android-product-id']) {
35
+ this.error('At least one of --ios-product-id or --android-product-id is required', { exit: 2 });
36
+ }
37
+ if (flags['android-product-id'] && !flags['android-base-plan-id'] && flags.period !== 'lifetime') {
38
+ this.error('--android-base-plan-id is required with --android-product-id for subscriptions', { exit: 2 });
39
+ }
40
+ const client = await createAuthenticatedClient(this.config);
41
+ const body = {
42
+ access_level_id: flags['access-level-id'],
43
+ name: flags.name,
44
+ period: flags.period,
45
+ };
46
+ if (flags['ios-product-id'])
47
+ body.ios_product_id = flags['ios-product-id'];
48
+ if (flags['android-product-id'])
49
+ body.android_product_id = flags['android-product-id'];
50
+ if (flags['android-base-plan-id'])
51
+ body.android_base_plan_id = flags['android-base-plan-id'];
52
+ const result = await client.put(`/apps/${flags.app}/products/${args.product_id}`, body);
53
+ this.log('Product updated!');
54
+ this.log(`ID: ${result.id}`);
55
+ this.log(`Name: ${result.name}`);
56
+ if (result.vendor_products.ios) {
57
+ this.log(`iOS Product: ${result.vendor_products.ios.product_id}`);
58
+ }
59
+ if (result.vendor_products.android) {
60
+ const bp = result.vendor_products.android.base_plan_id;
61
+ this.log(`Android Product: ${result.vendor_products.android.product_id}${bp ? ` (base plan: ${bp})` : ''}`);
62
+ }
63
+ return result;
64
+ }
65
+ }
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,15 @@
1
+ export interface ApiClientOptions {
2
+ baseUrl?: string;
3
+ token?: null | string;
4
+ userAgent?: string;
5
+ }
6
+ export declare class ApiClient {
7
+ private baseUrl;
8
+ private token;
9
+ private userAgent;
10
+ constructor(opts?: ApiClientOptions);
11
+ get<T = unknown>(path: string, params?: Record<string, string>): Promise<T>;
12
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
13
+ put<T = unknown>(path: string, body?: unknown): Promise<T>;
14
+ private request;
15
+ }
@@ -0,0 +1,75 @@
1
+ import { ApiError, NetworkError, parseApiError } from './errors.js';
2
+ const DEFAULT_API_URL = 'https://api.adapty.io/api/v1/developer';
3
+ export class ApiClient {
4
+ baseUrl;
5
+ token;
6
+ userAgent;
7
+ constructor(opts = {}) {
8
+ this.baseUrl = (opts.baseUrl ?? process.env.ADAPTY_API_URL ?? DEFAULT_API_URL).replace(/\/$/, '');
9
+ if (this.baseUrl !== DEFAULT_API_URL) {
10
+ process.stderr.write(`Warning: using non-default API URL: ${this.baseUrl}\n`);
11
+ }
12
+ this.token = opts.token ?? null;
13
+ this.userAgent = opts.userAgent ?? 'adapty-cli';
14
+ }
15
+ async get(path, params) {
16
+ let url = `${this.baseUrl}${path}`;
17
+ if (params) {
18
+ const qs = new URLSearchParams(params);
19
+ url += `?${qs.toString()}`;
20
+ }
21
+ return this.request(url, { method: 'GET' });
22
+ }
23
+ async post(path, body) {
24
+ return this.request(`${this.baseUrl}${path}`, {
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ method: 'POST',
27
+ });
28
+ }
29
+ async put(path, body) {
30
+ return this.request(`${this.baseUrl}${path}`, {
31
+ body: body ? JSON.stringify(body) : undefined,
32
+ method: 'PUT',
33
+ });
34
+ }
35
+ // eslint-disable-next-line no-undef
36
+ async request(url, init) {
37
+ const headers = {
38
+ 'User-Agent': this.userAgent,
39
+ };
40
+ if (init.body) {
41
+ headers['Content-Type'] = 'application/json';
42
+ }
43
+ if (this.token) {
44
+ headers.Authorization = `Bearer ${this.token}`;
45
+ }
46
+ let response;
47
+ try {
48
+ response = await fetch(url, { ...init, headers });
49
+ }
50
+ catch (error) {
51
+ throw new NetworkError(error instanceof Error ? error.message : 'Connection failed');
52
+ }
53
+ if (response.status === 204) {
54
+ return undefined;
55
+ }
56
+ let body;
57
+ try {
58
+ body = await response.json();
59
+ }
60
+ catch {
61
+ if (!response.ok) {
62
+ throw new ApiError(response.status, `http_${response.status}`, {});
63
+ }
64
+ return undefined;
65
+ }
66
+ if (!response.ok) {
67
+ const error = parseApiError(response.status, body);
68
+ if (response.status === 401) {
69
+ error.message = 'Token expired or invalid. Run `adapty auth login`.';
70
+ }
71
+ throw error;
72
+ }
73
+ return body;
74
+ }
75
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveToken(configDir?: string): Promise<null | string>;
@@ -0,0 +1,8 @@
1
+ import { readConfig } from './config.js';
2
+ export async function resolveToken(configDir) {
3
+ const envToken = process.env.ADAPTY_TOKEN;
4
+ if (envToken)
5
+ return envToken;
6
+ const config = await readConfig(configDir);
7
+ return config.access_token ?? null;
8
+ }
@@ -0,0 +1,4 @@
1
+ import type { Config } from '@oclif/core';
2
+ import { ApiClient } from './api-client.js';
3
+ export declare function buildUserAgent(config: Config): string;
4
+ export declare function createAuthenticatedClient(config: Config): Promise<ApiClient>;
@@ -0,0 +1,15 @@
1
+ import { ApiClient } from './api-client.js';
2
+ import { resolveToken } from './auth.js';
3
+ import { AuthRequiredError } from './errors.js';
4
+ export function buildUserAgent(config) {
5
+ return `adapty-cli/${config.version} node/${process.version} ${process.platform}/${process.arch}`;
6
+ }
7
+ export async function createAuthenticatedClient(config) {
8
+ const token = await resolveToken(config.configDir);
9
+ if (!token)
10
+ throw new AuthRequiredError();
11
+ return new ApiClient({
12
+ token,
13
+ userAgent: buildUserAgent(config),
14
+ });
15
+ }
@@ -0,0 +1,9 @@
1
+ export interface AdaptyConfig {
2
+ access_token?: string;
3
+ user?: {
4
+ email: string;
5
+ name: string;
6
+ };
7
+ }
8
+ export declare function readConfig(configDir?: string): Promise<AdaptyConfig>;
9
+ export declare function writeConfig(config: AdaptyConfig, configDir?: string): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ const CONFIG_DIR = join(homedir(), '.config', 'adapty');
5
+ const CONFIG_FILE = 'config.json';
6
+ function configPath(configDir) {
7
+ return join(configDir ?? CONFIG_DIR, CONFIG_FILE);
8
+ }
9
+ export async function readConfig(configDir) {
10
+ try {
11
+ const raw = await readFile(configPath(configDir), 'utf8');
12
+ return JSON.parse(raw);
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ export async function writeConfig(config, configDir) {
19
+ const path = configPath(configDir);
20
+ await mkdir(dirname(path), { recursive: true });
21
+ await writeFile(path, JSON.stringify(config, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
22
+ }
@@ -0,0 +1,23 @@
1
+ export interface ApiErrorBody {
2
+ error?: string;
3
+ error_code?: string;
4
+ errors?: Record<string, string[]>;
5
+ status_code?: number;
6
+ }
7
+ export declare class ApiError extends Error {
8
+ statusCode: number;
9
+ errorCode: string;
10
+ fieldErrors: Record<string, string[]>;
11
+ constructor(statusCode: number, errorCode: string, fieldErrors: Record<string, string[]>);
12
+ toHuman(): string;
13
+ toJSON(): ApiErrorBody;
14
+ }
15
+ export declare class NetworkError extends Error {
16
+ constructor(message: string);
17
+ toHuman(): string;
18
+ toJSON(): ApiErrorBody;
19
+ }
20
+ export declare class AuthRequiredError extends Error {
21
+ constructor();
22
+ }
23
+ export declare function parseApiError(statusCode: number, body: unknown): ApiError;
@@ -0,0 +1,63 @@
1
+ export class ApiError extends Error {
2
+ statusCode;
3
+ errorCode;
4
+ fieldErrors;
5
+ constructor(statusCode, errorCode, fieldErrors) {
6
+ super(errorCode);
7
+ this.statusCode = statusCode;
8
+ this.errorCode = errorCode;
9
+ this.fieldErrors = fieldErrors;
10
+ this.name = 'ApiError';
11
+ }
12
+ toHuman() {
13
+ const lines = [`Error: ${this.errorCode}`];
14
+ const entries = Object.entries(this.fieldErrors);
15
+ if (entries.length > 0) {
16
+ lines.push('Field errors:');
17
+ for (const [field, msgs] of entries) {
18
+ for (const msg of msgs) {
19
+ lines.push(` ${field}: ${msg}`);
20
+ }
21
+ }
22
+ }
23
+ return lines.join('\n');
24
+ }
25
+ toJSON() {
26
+ return {
27
+ error_code: this.errorCode,
28
+ errors: Object.keys(this.fieldErrors).length > 0 ? this.fieldErrors : undefined,
29
+ status_code: this.statusCode,
30
+ };
31
+ }
32
+ }
33
+ export class NetworkError extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = 'NetworkError';
37
+ }
38
+ toHuman() {
39
+ return `Error: Could not reach Adapty API. ${this.message}`;
40
+ }
41
+ toJSON() {
42
+ return { error_code: 'network_error', errors: { connection: [this.message] }, status_code: 0 };
43
+ }
44
+ }
45
+ export class AuthRequiredError extends Error {
46
+ constructor() {
47
+ super('Not authenticated. Run `adapty auth login`.');
48
+ this.name = 'AuthRequiredError';
49
+ }
50
+ }
51
+ export function parseApiError(statusCode, body) {
52
+ if (!body || typeof body !== 'object') {
53
+ return new ApiError(statusCode, `http_${statusCode}`, {});
54
+ }
55
+ const parsed = body;
56
+ if (parsed.error_code) {
57
+ return new ApiError(statusCode, parsed.error_code, parsed.errors ?? {});
58
+ }
59
+ if (parsed.error) {
60
+ return new ApiError(statusCode, parsed.error, {});
61
+ }
62
+ return new ApiError(statusCode, `http_${statusCode}`, {});
63
+ }
@@ -0,0 +1,22 @@
1
+ export declare function isValidUuid(value: string): boolean;
2
+ export declare const appFlag: {
3
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
4
+ };
5
+ export interface PaginatedResponse<T> {
6
+ data: T[];
7
+ meta: {
8
+ pagination: {
9
+ count: number;
10
+ page: number;
11
+ pages: number;
12
+ };
13
+ };
14
+ }
15
+ export declare const paginationFlags: {
16
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
18
+ };
19
+ export declare function paginationParams(flags: {
20
+ page: number;
21
+ 'page-size': number;
22
+ }): Record<string, string>;
@@ -0,0 +1,36 @@
1
+ import { Flags } from '@oclif/core';
2
+ const UUID_REGEX = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/i;
3
+ export function isValidUuid(value) {
4
+ return UUID_REGEX.test(value);
5
+ }
6
+ export const appFlag = {
7
+ app: Flags.string({
8
+ description: 'App ID (UUID)',
9
+ async parse(input) {
10
+ if (!isValidUuid(input)) {
11
+ throw new Error('Invalid app ID format. Run `adapty apps list` to find your app ID.');
12
+ }
13
+ return input;
14
+ },
15
+ required: true,
16
+ }),
17
+ };
18
+ export const paginationFlags = {
19
+ page: Flags.integer({
20
+ default: 1,
21
+ description: 'Page number',
22
+ min: 1,
23
+ }),
24
+ 'page-size': Flags.integer({
25
+ default: 20,
26
+ description: 'Items per page (max 100)',
27
+ max: 100,
28
+ min: 1,
29
+ }),
30
+ };
31
+ export function paginationParams(flags) {
32
+ return {
33
+ 'page[number]': String(flags.page),
34
+ 'page[size]': String(flags['page-size']),
35
+ };
36
+ }
@@ -0,0 +1,6 @@
1
+ export declare function printKeyValue(data: Record<string, unknown>, log: (msg: string) => void): void;
2
+ export declare function printList(items: Record<string, unknown>[], log: (msg: string) => void, pagination?: {
3
+ count: number;
4
+ page: number;
5
+ pages: number;
6
+ }): void;
@@ -0,0 +1,18 @@
1
+ export function printKeyValue(data, log) {
2
+ for (const [key, value] of Object.entries(data)) {
3
+ if (value !== undefined && value !== null) {
4
+ log(`${key}: ${String(value)}`);
5
+ }
6
+ }
7
+ }
8
+ export function printList(items, log, pagination) {
9
+ for (const [i, item] of items.entries()) {
10
+ printKeyValue(item, log);
11
+ if (i < items.length - 1)
12
+ log('---');
13
+ }
14
+ if (pagination) {
15
+ log('');
16
+ log(`Page ${pagination.page} of ${pagination.pages} (${pagination.count} total)`);
17
+ }
18
+ }