@shopvirge/shopvirge-client 0.2.2

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 ADDED
@@ -0,0 +1,106 @@
1
+ # @shopvirge/shopvirge-client
2
+
3
+ Auto-generated TypeScript API client for the [ShopVirge](https://api.shopvirge.com) API, built from the OpenAPI spec using [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @shopvirge/shopvirge-client
9
+ # or
10
+ yarn add @shopvirge/shopvirge-client
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Configure the API URL
16
+
17
+ Before making any API calls, set the `BASE` URL on the exported `OpenAPI` config object:
18
+
19
+ ```ts
20
+ import { OpenAPI } from '@shopvirge/shopvirge-client';
21
+
22
+ OpenAPI.BASE = 'https://api.shopvirge.com';
23
+ ```
24
+
25
+ ### Authentication
26
+
27
+ You can configure authentication by setting `TOKEN` or `HEADERS`:
28
+
29
+ ```ts
30
+ import { OpenAPI } from '@shopvirge/shopvirge-client';
31
+
32
+ // Bearer token
33
+ OpenAPI.TOKEN = 'your-access-token';
34
+
35
+ // Or use custom headers
36
+ OpenAPI.HEADERS = {
37
+ Authorization: 'Bearer your-access-token',
38
+ };
39
+ ```
40
+
41
+ ### Making API calls
42
+
43
+ Once configured, use the generated service functions to interact with the API:
44
+
45
+ ```ts
46
+ import { OpenAPI, ShopsService } from '@shopvirge/shopvirge-client';
47
+
48
+ OpenAPI.BASE = 'https://api.shopvirge.com';
49
+
50
+ const shops = await ShopsService.getShops();
51
+ ```
52
+
53
+ ### Full configuration reference
54
+
55
+ All properties on the `OpenAPI` config object can be overridden:
56
+
57
+ | Property | Type | Default | Description |
58
+ | ------------------ | -------------------------------------- | ----------- | -------------------------------- |
59
+ | `BASE` | `string` | `''` | Base URL for all API requests |
60
+ | `TOKEN` | `string \| Resolver` | `undefined` | Bearer token or async resolver |
61
+ | `HEADERS` | `Record<string, string> \| Resolver` | `undefined` | Custom headers or async resolver |
62
+ | `CREDENTIALS` | `'include' \| 'omit' \| 'same-origin'` | `'include'` | Fetch credentials mode |
63
+ | `WITH_CREDENTIALS` | `boolean` | `false` | XHR withCredentials flag |
64
+ | `ENCODE_PATH` | `(path: string) => string` | `undefined` | Custom path encoder |
65
+ | `USERNAME` | `string \| Resolver` | `undefined` | Basic auth username |
66
+ | `PASSWORD` | `string \| Resolver` | `undefined` | Basic auth password |
67
+
68
+ ### Request/Response interceptors
69
+
70
+ You can add interceptors to modify requests or responses:
71
+
72
+ ```ts
73
+ import { OpenAPI } from '@shopvirge/shopvirge-client';
74
+
75
+ OpenAPI.interceptors.request.use((request) => {
76
+ console.log('Request:', request);
77
+ return request;
78
+ });
79
+
80
+ OpenAPI.interceptors.response.use((response) => {
81
+ console.log('Response:', response);
82
+ return response;
83
+ });
84
+ ```
85
+
86
+ ## Development
87
+
88
+ ### Regenerate the client
89
+
90
+ From the `packages/shopvirge-client/` directory:
91
+
92
+ ```bash
93
+ # From a local openapi.json file
94
+ yarn openapi-ts
95
+
96
+ # From the live API
97
+ yarn openapi-ts-live
98
+ ```
99
+
100
+ ### Sync package version
101
+
102
+ After regenerating, sync the package version from the OpenAPI spec:
103
+
104
+ ```bash
105
+ yarn sync-version
106
+ ```
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@shopvirge/shopvirge-client",
3
+ "version": "0.2.2",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "type": "module",
10
+ "scripts": {
11
+ "test": "jest --passWithNoTests",
12
+ "build": "tsup src/index.ts --format esm --dts --tsconfig tsconfig.json",
13
+ "tsc": "tsc --noEmit",
14
+ "lint": "eslint \"src/**/*.ts*\"",
15
+ "dev": "yarn build -- --watch",
16
+ "openapi-ts": "openapi-ts -i src/api/openapi.json -o src/api/generated",
17
+ "openapi-ts-live": "openapi-ts -i https://api.shopvirge.com/openapi.json -o src/api/generated",
18
+ "sync-version": "node -e \"const pkg=require('./package.json');const api=require('./src/api/openapi.json');pkg.version=api.info.version;require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,4)+'\\n');\""
19
+ },
20
+ "dependencies": {
21
+ "@graphql-typed-document-node/core": "3.2.0",
22
+ "moment": "2.29.4",
23
+ "use-query-params": "2.2.1"
24
+ },
25
+ "peerDependencies": {
26
+ "graphql-request": "5.2.0",
27
+ "next": "^13.4.1",
28
+ "next-query-params": "4.2.2",
29
+ "react": "^18.2.0",
30
+ "react-query": "3.39.3",
31
+ "stripe": "^13.7.0",
32
+ "typescript": "^4.9.0"
33
+ },
34
+ "devDependencies": {
35
+ "@hey-api/openapi-ts": "^0.46.3",
36
+ "@testing-library/jest-dom": "^5.16.1",
37
+ "@testing-library/react": "^14.0.0",
38
+ "@testing-library/react-hooks": "^8.0.1",
39
+ "@testing-library/user-event": "^14.4.3",
40
+ "@types/jest": "^27.4.0",
41
+ "esbuild": "^0.14.10",
42
+ "esbuild-jest": "^0.5.0",
43
+ "jest": "^27.4.7",
44
+ "jest-watch-typeahead": "^1.0.0",
45
+ "react": "^18.2.0",
46
+ "react-dom": "^18.2.0"
47
+ }
48
+ }
@@ -0,0 +1,25 @@
1
+ import type { ApiRequestOptions } from './ApiRequestOptions';
2
+ import type { ApiResult } from './ApiResult';
3
+
4
+ export class ApiError extends Error {
5
+ public readonly url: string;
6
+ public readonly status: number;
7
+ public readonly statusText: string;
8
+ public readonly body: unknown;
9
+ public readonly request: ApiRequestOptions;
10
+
11
+ constructor(
12
+ request: ApiRequestOptions,
13
+ response: ApiResult,
14
+ message: string,
15
+ ) {
16
+ super(message);
17
+
18
+ this.name = 'ApiError';
19
+ this.url = response.url;
20
+ this.status = response.status;
21
+ this.statusText = response.statusText;
22
+ this.body = response.body;
23
+ this.request = request;
24
+ }
25
+ }
@@ -0,0 +1,20 @@
1
+ export type ApiRequestOptions = {
2
+ readonly method:
3
+ | 'GET'
4
+ | 'PUT'
5
+ | 'POST'
6
+ | 'DELETE'
7
+ | 'OPTIONS'
8
+ | 'HEAD'
9
+ | 'PATCH';
10
+ readonly url: string;
11
+ readonly path?: Record<string, unknown>;
12
+ readonly cookies?: Record<string, unknown>;
13
+ readonly headers?: Record<string, unknown>;
14
+ readonly query?: Record<string, unknown>;
15
+ readonly formData?: Record<string, unknown>;
16
+ readonly body?: any;
17
+ readonly mediaType?: string;
18
+ readonly responseHeader?: string;
19
+ readonly errors?: Record<number | string, string>;
20
+ };
@@ -0,0 +1,7 @@
1
+ export type ApiResult<TData = any> = {
2
+ readonly body: TData;
3
+ readonly ok: boolean;
4
+ readonly status: number;
5
+ readonly statusText: string;
6
+ readonly url: string;
7
+ };
@@ -0,0 +1,130 @@
1
+ export class CancelError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'CancelError';
5
+ }
6
+
7
+ public get isCancelled(): boolean {
8
+ return true;
9
+ }
10
+ }
11
+
12
+ export interface OnCancel {
13
+ readonly isResolved: boolean;
14
+ readonly isRejected: boolean;
15
+ readonly isCancelled: boolean;
16
+
17
+ (cancelHandler: () => void): void;
18
+ }
19
+
20
+ export class CancelablePromise<T> implements Promise<T> {
21
+ private _isResolved: boolean;
22
+ private _isRejected: boolean;
23
+ private _isCancelled: boolean;
24
+ readonly cancelHandlers: (() => void)[];
25
+ readonly promise: Promise<T>;
26
+ private _resolve?: (value: T | PromiseLike<T>) => void;
27
+ private _reject?: (reason?: unknown) => void;
28
+
29
+ constructor(
30
+ executor: (
31
+ resolve: (value: T | PromiseLike<T>) => void,
32
+ reject: (reason?: unknown) => void,
33
+ onCancel: OnCancel,
34
+ ) => void,
35
+ ) {
36
+ this._isResolved = false;
37
+ this._isRejected = false;
38
+ this._isCancelled = false;
39
+ this.cancelHandlers = [];
40
+ this.promise = new Promise<T>((resolve, reject) => {
41
+ this._resolve = resolve;
42
+ this._reject = reject;
43
+
44
+ const onResolve = (value: T | PromiseLike<T>): void => {
45
+ if (this._isResolved || this._isRejected || this._isCancelled) {
46
+ return;
47
+ }
48
+ this._isResolved = true;
49
+ if (this._resolve) this._resolve(value);
50
+ };
51
+
52
+ const onReject = (reason?: unknown): void => {
53
+ if (this._isResolved || this._isRejected || this._isCancelled) {
54
+ return;
55
+ }
56
+ this._isRejected = true;
57
+ if (this._reject) this._reject(reason);
58
+ };
59
+
60
+ const onCancel = (cancelHandler: () => void): void => {
61
+ if (this._isResolved || this._isRejected || this._isCancelled) {
62
+ return;
63
+ }
64
+ this.cancelHandlers.push(cancelHandler);
65
+ };
66
+
67
+ Object.defineProperty(onCancel, 'isResolved', {
68
+ get: (): boolean => this._isResolved,
69
+ });
70
+
71
+ Object.defineProperty(onCancel, 'isRejected', {
72
+ get: (): boolean => this._isRejected,
73
+ });
74
+
75
+ Object.defineProperty(onCancel, 'isCancelled', {
76
+ get: (): boolean => this._isCancelled,
77
+ });
78
+
79
+ return executor(onResolve, onReject, onCancel as OnCancel);
80
+ });
81
+ }
82
+
83
+ get [Symbol.toStringTag]() {
84
+ return 'Cancellable Promise';
85
+ }
86
+
87
+ public then<TResult1 = T, TResult2 = never>(
88
+ onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
89
+ onRejected?:
90
+ | ((reason: unknown) => TResult2 | PromiseLike<TResult2>)
91
+ | null,
92
+ ): Promise<TResult1 | TResult2> {
93
+ return this.promise.then(onFulfilled, onRejected);
94
+ }
95
+
96
+ public catch<TResult = never>(
97
+ onRejected?:
98
+ | ((reason: unknown) => TResult | PromiseLike<TResult>)
99
+ | null,
100
+ ): Promise<T | TResult> {
101
+ return this.promise.catch(onRejected);
102
+ }
103
+
104
+ public finally(onFinally?: (() => void) | null): Promise<T> {
105
+ return this.promise.finally(onFinally);
106
+ }
107
+
108
+ public cancel(): void {
109
+ if (this._isResolved || this._isRejected || this._isCancelled) {
110
+ return;
111
+ }
112
+ this._isCancelled = true;
113
+ if (this.cancelHandlers.length) {
114
+ try {
115
+ for (const cancelHandler of this.cancelHandlers) {
116
+ cancelHandler();
117
+ }
118
+ } catch (error) {
119
+ console.warn('Cancellation threw an error', error);
120
+ return;
121
+ }
122
+ }
123
+ this.cancelHandlers.length = 0;
124
+ if (this._reject) this._reject(new CancelError('Request aborted'));
125
+ }
126
+
127
+ public get isCancelled(): boolean {
128
+ return this._isCancelled;
129
+ }
130
+ }
@@ -0,0 +1,59 @@
1
+ import type { ApiRequestOptions } from './ApiRequestOptions';
2
+
3
+ type Headers = Record<string, string>;
4
+ type Middleware<T> = (value: T) => T | Promise<T>;
5
+ type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
6
+
7
+ export class Interceptors<T> {
8
+ _fns: Middleware<T>[];
9
+
10
+ constructor() {
11
+ this._fns = [];
12
+ }
13
+
14
+ eject(fn: Middleware<T>): void {
15
+ const index = this._fns.indexOf(fn);
16
+ if (index !== -1) {
17
+ this._fns = [
18
+ ...this._fns.slice(0, index),
19
+ ...this._fns.slice(index + 1),
20
+ ];
21
+ }
22
+ }
23
+
24
+ use(fn: Middleware<T>): void {
25
+ this._fns = [...this._fns, fn];
26
+ }
27
+ }
28
+
29
+ export type OpenAPIConfig = {
30
+ BASE: string;
31
+ CREDENTIALS: 'include' | 'omit' | 'same-origin';
32
+ ENCODE_PATH?: ((path: string) => string) | undefined;
33
+ HEADERS?: Headers | Resolver<Headers> | undefined;
34
+ PASSWORD?: string | Resolver<string> | undefined;
35
+ TOKEN?: string | Resolver<string> | undefined;
36
+ USERNAME?: string | Resolver<string> | undefined;
37
+ VERSION: string;
38
+ WITH_CREDENTIALS: boolean;
39
+ interceptors: {
40
+ request: Interceptors<RequestInit>;
41
+ response: Interceptors<Response>;
42
+ };
43
+ };
44
+
45
+ export const OpenAPI: OpenAPIConfig = {
46
+ BASE: '',
47
+ CREDENTIALS: 'include',
48
+ ENCODE_PATH: undefined,
49
+ HEADERS: undefined,
50
+ PASSWORD: undefined,
51
+ TOKEN: undefined,
52
+ USERNAME: undefined,
53
+ VERSION: '0.2.2',
54
+ WITH_CREDENTIALS: false,
55
+ interceptors: {
56
+ request: new Interceptors(),
57
+ response: new Interceptors(),
58
+ },
59
+ };