@rage-against-the-pixel/app-store-connect-api 3.6.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 RageAgainstThePixel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # app-store-connect-api
2
+
3
+ A TypeScript package for communicating with Apple [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi)
4
+
5
+ - Automatically generated API client using the latest [OpenAPI specification](https://developer.apple.com/sample-code/app-store-connect/app-store-connect-openapi-specification.zip) from Apple.
6
+ - Fully typed models and methods for every endpoint.
7
+ - Designed for use in a Node.js server environment.
8
+
9
+ ## Installation
10
+
11
+ ```sh
12
+ npm install @rage-against-the-pixel/app-store-connect-api
13
+ ```
14
+
15
+ ## Authentication
16
+
17
+ To authenticate with the API you will need to [create a API keys for App Store Connect API](https://appstoreconnect.apple.com/access/api).
18
+
19
+ Download and save the private key `.p8` file to a save, secure location.
20
+ The contents of this file is your `privateKey`.
21
+ The `privateKeyId` and `issuerId` are both listed on the same page where you create your private key.
22
+
23
+ ## Example
24
+
25
+ ```ts
26
+ import { AppStoreConnectClient, AppStoreConnectOptions } from 'app-store-connect-api';
27
+
28
+ async function main() {
29
+ const options: AppStoreConnectOptions = {
30
+ issuerId: '<ISSUER_ID>',
31
+ privateKeyId: '<PRIVATE_KEY_ID>',
32
+ privateKey: '<PRIVATE_KEY>',
33
+ };
34
+ const client = new AppStoreConnectClient(options);
35
+ const { data: response, error } = await client.api.appsGetCollection({
36
+ query: {
37
+ limit: 10
38
+ }
39
+ });
40
+ if (error) {
41
+ console.error('Error fetching apps:', error);
42
+ } else {
43
+ const apps = response.data.map(app => ({
44
+ id: app.id,
45
+ name: app.attributes.name,
46
+ bundleId: app.attributes.bundleId
47
+ }));
48
+ console.log('Apps:', apps);
49
+ }
50
+ }
51
+
52
+ main();
53
+ ```
@@ -0,0 +1,4 @@
1
+ import type { Client, Config } from './types';
2
+ export declare const createClient: (config?: Config) => Client;
3
+ export type { Client, Config, Options, RequestOptionsBase, RequestResult, } from './types';
4
+ export { createConfig, formDataBodySerializer, jsonBodySerializer, type QuerySerializerOptions, urlSearchParamsBodySerializer, } from './utils';
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.urlSearchParamsBodySerializer = exports.jsonBodySerializer = exports.formDataBodySerializer = exports.createConfig = exports.createClient = void 0;
4
+ const utils_1 = require("./utils");
5
+ const createClient = (config = {}) => {
6
+ let _config = (0, utils_1.mergeConfigs)((0, utils_1.createConfig)(), config);
7
+ const getConfig = () => ({ ..._config });
8
+ const setConfig = (config) => {
9
+ _config = (0, utils_1.mergeConfigs)(_config, config);
10
+ return getConfig();
11
+ };
12
+ const interceptors = (0, utils_1.createInterceptors)();
13
+ // @ts-expect-error
14
+ const request = async (options) => {
15
+ // @ts-expect-error
16
+ const opts = {
17
+ ..._config,
18
+ ...options,
19
+ headers: (0, utils_1.mergeHeaders)(_config.headers, options.headers),
20
+ };
21
+ if (opts.body && opts.bodySerializer) {
22
+ opts.body = opts.bodySerializer(opts.body);
23
+ }
24
+ // remove Content-Type header if body is empty to avoid sending invalid requests
25
+ if (!opts.body) {
26
+ opts.headers.delete('Content-Type');
27
+ }
28
+ const url = (0, utils_1.getUrl)({
29
+ baseUrl: opts.baseUrl ?? '',
30
+ path: opts.path,
31
+ query: opts.query,
32
+ querySerializer: typeof opts.querySerializer === 'function'
33
+ ? opts.querySerializer
34
+ : (0, utils_1.createQuerySerializer)(opts.querySerializer),
35
+ url: opts.url,
36
+ });
37
+ const requestInit = {
38
+ redirect: 'follow',
39
+ ...opts,
40
+ };
41
+ let request = new Request(url, requestInit);
42
+ for (const fn of interceptors.request._fns) {
43
+ request = await fn(request, opts);
44
+ }
45
+ const _fetch = opts.fetch;
46
+ let response = await _fetch(request);
47
+ for (const fn of interceptors.response._fns) {
48
+ response = await fn(response, request, opts);
49
+ }
50
+ const result = {
51
+ request,
52
+ response,
53
+ };
54
+ if (response.ok) {
55
+ if (response.status === 204 ||
56
+ response.headers.get('Content-Length') === '0') {
57
+ return {
58
+ data: {},
59
+ ...result,
60
+ };
61
+ }
62
+ if (opts.parseAs === 'stream') {
63
+ return {
64
+ data: response.body,
65
+ ...result,
66
+ };
67
+ }
68
+ const parseAs = (opts.parseAs === 'auto'
69
+ ? (0, utils_1.getParseAs)(response.headers.get('Content-Type'))
70
+ : opts.parseAs) ?? 'json';
71
+ let data = await response[parseAs]();
72
+ if (parseAs === 'json' && opts.responseTransformer) {
73
+ data = await opts.responseTransformer(data);
74
+ }
75
+ return {
76
+ data,
77
+ ...result,
78
+ };
79
+ }
80
+ let error = await response.text();
81
+ try {
82
+ error = JSON.parse(error);
83
+ }
84
+ catch {
85
+ // noop
86
+ }
87
+ let finalError = error;
88
+ for (const fn of interceptors.error._fns) {
89
+ finalError = (await fn(error, response, request, opts));
90
+ }
91
+ finalError = finalError || {};
92
+ if (opts.throwOnError) {
93
+ throw finalError;
94
+ }
95
+ return {
96
+ error: finalError,
97
+ ...result,
98
+ };
99
+ };
100
+ return {
101
+ connect: (options) => request({ ...options, method: 'CONNECT' }),
102
+ delete: (options) => request({ ...options, method: 'DELETE' }),
103
+ get: (options) => request({ ...options, method: 'GET' }),
104
+ getConfig,
105
+ head: (options) => request({ ...options, method: 'HEAD' }),
106
+ interceptors,
107
+ options: (options) => request({ ...options, method: 'OPTIONS' }),
108
+ patch: (options) => request({ ...options, method: 'PATCH' }),
109
+ post: (options) => request({ ...options, method: 'POST' }),
110
+ put: (options) => request({ ...options, method: 'PUT' }),
111
+ request,
112
+ setConfig,
113
+ trace: (options) => request({ ...options, method: 'TRACE' }),
114
+ };
115
+ };
116
+ exports.createClient = createClient;
117
+ var utils_2 = require("./utils");
118
+ Object.defineProperty(exports, "createConfig", { enumerable: true, get: function () { return utils_2.createConfig; } });
119
+ Object.defineProperty(exports, "formDataBodySerializer", { enumerable: true, get: function () { return utils_2.formDataBodySerializer; } });
120
+ Object.defineProperty(exports, "jsonBodySerializer", { enumerable: true, get: function () { return utils_2.jsonBodySerializer; } });
121
+ Object.defineProperty(exports, "urlSearchParamsBodySerializer", { enumerable: true, get: function () { return utils_2.urlSearchParamsBodySerializer; } });
@@ -0,0 +1,121 @@
1
+ import type { BodySerializer, Middleware, QuerySerializer, QuerySerializerOptions } from './utils';
2
+ type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>;
3
+ export interface Config<ThrowOnError extends boolean = boolean> extends Omit<RequestInit, 'body' | 'headers' | 'method'> {
4
+ /**
5
+ * Base URL for all requests made by this client.
6
+ * @default ''
7
+ */
8
+ baseUrl?: string;
9
+ /**
10
+ * Any body that you want to add to your request.
11
+ *
12
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
13
+ */
14
+ body?: RequestInit['body'] | Record<string, unknown> | Array<Record<string, unknown>> | Array<unknown> | number;
15
+ /**
16
+ * A function for serializing request body parameter. By default,
17
+ * {@link JSON.stringify()} will be used.
18
+ */
19
+ bodySerializer?: BodySerializer;
20
+ /**
21
+ * Fetch API implementation. You can use this option to provide a custom
22
+ * fetch instance.
23
+ * @default globalThis.fetch
24
+ */
25
+ fetch?: (request: Request) => ReturnType<typeof fetch>;
26
+ /**
27
+ * An object containing any HTTP headers that you want to pre-populate your
28
+ * `Headers` object with.
29
+ *
30
+ * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
31
+ */
32
+ headers?: RequestInit['headers'] | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>;
33
+ /**
34
+ * The request method.
35
+ *
36
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
37
+ */
38
+ method?: 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE';
39
+ /**
40
+ * Return the response data parsed in a specified format. By default, `auto`
41
+ * will infer the appropriate method from the `Content-Type` response header.
42
+ * You can override this behavior with any of the {@link Body} methods.
43
+ * Select `stream` if you don't want to parse response data at all.
44
+ * @default 'auto'
45
+ */
46
+ parseAs?: Exclude<keyof Body, 'body' | 'bodyUsed'> | 'auto' | 'stream';
47
+ /**
48
+ * A function for serializing request query parameters. By default, arrays
49
+ * will be exploded in form style, objects will be exploded in deepObject
50
+ * style, and reserved characters are percent-encoded.
51
+ *
52
+ * {@link https://swagger.io/docs/specification/serialization/#query View examples}
53
+ */
54
+ querySerializer?: QuerySerializer | QuerySerializerOptions;
55
+ /**
56
+ * A function for transforming response data before it's returned to the
57
+ * caller function. This is an ideal place to post-process server data,
58
+ * e.g. convert date ISO strings into native Date objects.
59
+ */
60
+ responseTransformer?: (data: unknown) => Promise<unknown>;
61
+ /**
62
+ * Throw an error instead of returning it in the response?
63
+ * @default false
64
+ */
65
+ throwOnError?: ThrowOnError;
66
+ }
67
+ export interface RequestOptionsBase<ThrowOnError extends boolean> extends Config<ThrowOnError> {
68
+ path?: Record<string, unknown>;
69
+ query?: Record<string, unknown>;
70
+ url: string;
71
+ }
72
+ export type RequestResult<Data = unknown, TError = unknown, ThrowOnError extends boolean = boolean> = ThrowOnError extends true ? Promise<{
73
+ data: Data;
74
+ request: Request;
75
+ response: Response;
76
+ }> : Promise<({
77
+ data: Data;
78
+ error: undefined;
79
+ } | {
80
+ data: undefined;
81
+ error: TError;
82
+ }) & {
83
+ request: Request;
84
+ response: Response;
85
+ }>;
86
+ type MethodFn = <Data = unknown, TError = unknown, ThrowOnError extends boolean = false>(options: Omit<RequestOptionsBase<ThrowOnError>, 'method'>) => RequestResult<Data, TError, ThrowOnError>;
87
+ type RequestFn = <Data = unknown, TError = unknown, ThrowOnError extends boolean = false>(options: Omit<RequestOptionsBase<ThrowOnError>, 'method'> & Pick<Required<RequestOptionsBase<ThrowOnError>>, 'method'>) => RequestResult<Data, TError, ThrowOnError>;
88
+ export interface Client<Req = Request, Res = Response, Err = unknown, Options = RequestOptions> {
89
+ connect: MethodFn;
90
+ delete: MethodFn;
91
+ get: MethodFn;
92
+ getConfig: () => Config;
93
+ head: MethodFn;
94
+ interceptors: Middleware<Req, Res, Err, Options>;
95
+ options: MethodFn;
96
+ patch: MethodFn;
97
+ post: MethodFn;
98
+ put: MethodFn;
99
+ request: RequestFn;
100
+ setConfig: (config: Config) => Config;
101
+ trace: MethodFn;
102
+ }
103
+ export type RequestOptions = RequestOptionsBase<false> & Config<false> & {
104
+ headers: Headers;
105
+ };
106
+ type OptionsBase<ThrowOnError extends boolean> = Omit<RequestOptionsBase<ThrowOnError>, 'url'> & {
107
+ /**
108
+ * You can provide a client instance returned by `createClient()` instead of
109
+ * individual options. This might be also useful if you want to implement a
110
+ * custom client.
111
+ */
112
+ client?: Client;
113
+ };
114
+ export type Options<T = unknown, ThrowOnError extends boolean = boolean> = T extends {
115
+ body?: any;
116
+ } ? T extends {
117
+ headers?: any;
118
+ } ? OmitKeys<OptionsBase<ThrowOnError>, 'body' | 'headers'> & T : OmitKeys<OptionsBase<ThrowOnError>, 'body'> & T & Pick<OptionsBase<ThrowOnError>, 'headers'> : T extends {
119
+ headers?: any;
120
+ } ? OmitKeys<OptionsBase<ThrowOnError>, 'headers'> & T & Pick<OptionsBase<ThrowOnError>, 'body'> : OptionsBase<ThrowOnError> & T;
121
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,63 @@
1
+ import type { Config } from './types';
2
+ type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
3
+ type ObjectStyle = 'form' | 'deepObject';
4
+ export type QuerySerializer = (query: Record<string, unknown>) => string;
5
+ export type BodySerializer = (body: any) => any;
6
+ interface SerializerOptions<T> {
7
+ /**
8
+ * @default true
9
+ */
10
+ explode: boolean;
11
+ style: T;
12
+ }
13
+ export interface QuerySerializerOptions {
14
+ allowReserved?: boolean;
15
+ array?: SerializerOptions<ArrayStyle>;
16
+ object?: SerializerOptions<ObjectStyle>;
17
+ }
18
+ export declare const createQuerySerializer: <T = unknown>({ allowReserved, array, object, }?: QuerySerializerOptions) => (queryParams: T) => string;
19
+ /**
20
+ * Infers parseAs value from provided Content-Type header.
21
+ */
22
+ export declare const getParseAs: (content: string | null) => Exclude<Config["parseAs"], "auto" | "stream">;
23
+ export declare const getUrl: ({ baseUrl, path, query, querySerializer, url: _url, }: {
24
+ baseUrl: string;
25
+ path?: Record<string, unknown>;
26
+ query?: Record<string, unknown>;
27
+ querySerializer: QuerySerializer;
28
+ url: string;
29
+ }) => string;
30
+ export declare const mergeConfigs: (a: Config, b: Config) => Config;
31
+ export declare const mergeHeaders: (...headers: Array<Required<Config>["headers"] | undefined>) => Headers;
32
+ type ErrInterceptor<Err, Res, Req, Options> = (error: Err, response: Res, request: Req, options: Options) => Err | Promise<Err>;
33
+ type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>;
34
+ type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>;
35
+ declare class Interceptors<Interceptor> {
36
+ _fns: Interceptor[];
37
+ constructor();
38
+ clear(): void;
39
+ exists(fn: Interceptor): boolean;
40
+ eject(fn: Interceptor): void;
41
+ use(fn: Interceptor): void;
42
+ }
43
+ export interface Middleware<Req, Res, Err, Options> {
44
+ error: Pick<Interceptors<ErrInterceptor<Err, Res, Req, Options>>, 'eject' | 'use'>;
45
+ request: Pick<Interceptors<ReqInterceptor<Req, Options>>, 'eject' | 'use'>;
46
+ response: Pick<Interceptors<ResInterceptor<Res, Req, Options>>, 'eject' | 'use'>;
47
+ }
48
+ export declare const createInterceptors: <Req, Res, Err, Options>() => {
49
+ error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>;
50
+ request: Interceptors<ReqInterceptor<Req, Options>>;
51
+ response: Interceptors<ResInterceptor<Res, Req, Options>>;
52
+ };
53
+ export declare const formDataBodySerializer: {
54
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T) => FormData;
55
+ };
56
+ export declare const jsonBodySerializer: {
57
+ bodySerializer: <T>(body: T) => string;
58
+ };
59
+ export declare const urlSearchParamsBodySerializer: {
60
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T) => URLSearchParams;
61
+ };
62
+ export declare const createConfig: (override?: Config) => Config;
63
+ export {};