@ikatec/digisac-api-sdk 3.0.0 → 4.0.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/README.md +42 -7
- package/dist/apis/cards/types.d.ts +89 -7
- package/dist/apis/cards/types.d.ts.map +1 -1
- package/dist/apis/pipeline/types.d.ts +67 -3
- package/dist/apis/pipeline/types.d.ts.map +1 -1
- package/dist/core/BaseApiClient.cjs +25 -31
- package/dist/core/BaseApiClient.cjs.map +1 -1
- package/dist/core/BaseApiClient.d.ts +2 -11
- package/dist/core/BaseApiClient.d.ts.map +1 -1
- package/dist/core/BaseApiClient.mjs +27 -31
- package/dist/core/BaseApiClient.mjs.map +1 -1
- package/dist/core/errors.cjs +192 -0
- package/dist/core/errors.cjs.map +1 -0
- package/dist/core/errors.d.ts +117 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.mjs +177 -0
- package/dist/core/errors.mjs.map +1 -0
- package/dist/core/index.cjs +16 -2
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.mjs +3 -2
- package/dist/incommingWebhooks/index.d.ts +3 -3
- package/dist/incommingWebhooks/index.d.ts.map +1 -1
- package/dist/index.cjs +16 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/src/apis/cards/types.ts +104 -7
- package/src/apis/pipeline/types.ts +71 -3
- package/src/core/BaseApiClient.test.ts +221 -7
- package/src/core/BaseApiClient.ts +37 -50
- package/src/core/BaseCrudApi.test-d.ts +48 -0
- package/src/core/BaseCrudApi.test.ts +150 -0
- package/src/core/errors.test-d.ts +14 -0
- package/src/core/errors.test.ts +389 -0
- package/src/core/errors.ts +284 -0
- package/src/core/index.ts +18 -2
- package/src/incommingWebhooks/index.ts +3 -3
package/README.md
CHANGED
|
@@ -165,23 +165,58 @@ const result = await people.getMany({
|
|
|
165
165
|
|
|
166
166
|
## Error Handling
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
Every error thrown by the SDK extends `DigisacError`:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
DigisacError
|
|
172
|
+
├── ApiConnectionError no HTTP response (network failure, DNS, connection refused)
|
|
173
|
+
└── ApiError non-2xx response; plain ApiError for unlisted statuses
|
|
174
|
+
├── BadRequestError 400
|
|
175
|
+
│ └── ValidationError 400 `ValidationError`: field validation failed
|
|
176
|
+
├── AuthenticationError 401
|
|
177
|
+
├── PaymentRequiredError 402: out of credits, plan limits
|
|
178
|
+
├── PermissionDeniedError 403
|
|
179
|
+
├── NotFoundError 404
|
|
180
|
+
├── ConflictError 409
|
|
181
|
+
├── UnprocessableEntityError 422
|
|
182
|
+
├── RateLimitError 429, with `retryAfter`
|
|
183
|
+
└── InternalServerError 5xx
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Errors are classified by HTTP status, plus `ValidationError` for field validation failures. The backend error name is always available as the `errorClass` string.
|
|
169
187
|
|
|
170
188
|
```typescript
|
|
171
|
-
import {
|
|
189
|
+
import {
|
|
190
|
+
ApiConnectionError,
|
|
191
|
+
ApiError,
|
|
192
|
+
NotFoundError,
|
|
193
|
+
ValidationError,
|
|
194
|
+
} from '@ikatec/digisac-api-sdk'
|
|
172
195
|
|
|
173
196
|
try {
|
|
174
197
|
await tickets.create(payload)
|
|
175
198
|
} catch (error) {
|
|
176
|
-
if (error instanceof
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
199
|
+
if (error instanceof ValidationError) {
|
|
200
|
+
for (const { field, message, code } of error.validationErrors) {
|
|
201
|
+
console.log(field, message, code) // 'name', 'Required.', 'required'
|
|
202
|
+
}
|
|
203
|
+
} else if (error instanceof NotFoundError) {
|
|
204
|
+
// ...
|
|
205
|
+
} else if (error instanceof ApiError) {
|
|
206
|
+
console.log(error.status) // HTTP status code
|
|
207
|
+
console.log(error.errorClass) // backend error name, e.g. 'HttpError', 'PaymentRequired'
|
|
208
|
+
console.log(error.message) // backend message
|
|
209
|
+
console.log(error.body) // full parsed error body (or raw text)
|
|
210
|
+
console.log(error.headers) // response headers
|
|
211
|
+
} else if (error instanceof ApiConnectionError) {
|
|
212
|
+
console.log(error.cause) // the original fetch error
|
|
181
213
|
}
|
|
182
214
|
}
|
|
183
215
|
```
|
|
184
216
|
|
|
217
|
+
- `validationErrors` is a flat list of `FieldError` (`{ location, field, path, message, code }`), one entry per failed rule, e.g. `{ location: 'body', field: 'name', path: 'body.name', message: 'Required.', code: 'required' }`. On `ValidationError` it is always an array; on other errors it is `null` when the response has no field errors.
|
|
218
|
+
- `RateLimitError.retryAfter` is the number of seconds to wait, read from `Retry-After` or the backend's `X-RateLimit-Reset`, or `null` when the response does not say.
|
|
219
|
+
|
|
185
220
|
## Incoming Webhooks
|
|
186
221
|
|
|
187
222
|
The SDK ships a fully-typed `WebhookPayload<E>` envelope and a `WebhookEventPayloadMap` that narrows `data` to the correct resource type for every supported event.
|
|
@@ -1,13 +1,71 @@
|
|
|
1
1
|
import type { Contact } from '../contacts/types';
|
|
2
|
-
import type { Pipeline } from '../pipeline/types';
|
|
2
|
+
import type { Pipeline, PipelineStage, PipelineStageReason, PipelineStageStatus } from '../pipeline/types';
|
|
3
3
|
import type { User } from '../users/types';
|
|
4
|
+
type NamedRef = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
/** One entry of the card history — written on create and on every stage/status/owner change. */
|
|
9
|
+
export type CardMovement = {
|
|
10
|
+
id: string;
|
|
11
|
+
cardId: string;
|
|
12
|
+
accountId: string;
|
|
13
|
+
/** The user who made the change (the requester), not the card owner. */
|
|
14
|
+
userId: string;
|
|
15
|
+
pipelineId: string;
|
|
16
|
+
fromPipelineId: string | null;
|
|
17
|
+
toPipelineId: string | null;
|
|
18
|
+
fromPipelineStageId: string | null;
|
|
19
|
+
toPipelineStageId: string | null;
|
|
20
|
+
fromStageStatusId: string | null;
|
|
21
|
+
toStageStatusId: string | null;
|
|
22
|
+
fromOwnerId: string | null;
|
|
23
|
+
toOwnerId: string | null;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
user?: NamedRef | null;
|
|
26
|
+
from_pipeline?: NamedRef | null;
|
|
27
|
+
to_pipeline?: NamedRef | null;
|
|
28
|
+
from_pipeline_stage?: NamedRef | null;
|
|
29
|
+
to_pipeline_stage?: NamedRef | null;
|
|
30
|
+
from_stage_status?: NamedRef | null;
|
|
31
|
+
to_stage_status?: NamedRef | null;
|
|
32
|
+
from_owner?: NamedRef | null;
|
|
33
|
+
to_owner?: NamedRef | null;
|
|
34
|
+
};
|
|
35
|
+
export type CardProduct = {
|
|
36
|
+
id: string;
|
|
37
|
+
cardId: string;
|
|
38
|
+
name: string;
|
|
39
|
+
/** Quantity (the backend column is spelled `ammount`). */
|
|
40
|
+
ammount: number;
|
|
41
|
+
value: number;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
};
|
|
45
|
+
export type CardComment = {
|
|
46
|
+
id: string;
|
|
47
|
+
cardId: string;
|
|
48
|
+
userId: string | null;
|
|
49
|
+
comment: string | null;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
user?: User | null;
|
|
53
|
+
files?: Record<string, unknown>[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A sales funnel card (opportunity).
|
|
57
|
+
*
|
|
58
|
+
* `create` and `updateById` answer the raw entity (with `updatedAt`/`archivedAt`). `getMany` and
|
|
59
|
+
* `getById` go through the backend transformer: no `updatedAt`/`archivedAt`, plus `url`,
|
|
60
|
+
* `lastMessage` and `totalValue`; `getById` also joins the relationships below.
|
|
61
|
+
*/
|
|
4
62
|
export type Card = {
|
|
5
63
|
id: string;
|
|
6
64
|
title: string;
|
|
7
65
|
description: string | null;
|
|
66
|
+
/** Position in the stage — set by the backend to the number of cards already in it. */
|
|
8
67
|
order: number | null;
|
|
9
68
|
isArchived: boolean;
|
|
10
|
-
archivedAt: string | null;
|
|
11
69
|
success: boolean | null;
|
|
12
70
|
finishedAt: string | null;
|
|
13
71
|
organization: string | null;
|
|
@@ -22,38 +80,62 @@ export type Card = {
|
|
|
22
80
|
reasonId: string | null;
|
|
23
81
|
ownerId: string | null;
|
|
24
82
|
createdAt: string;
|
|
25
|
-
|
|
83
|
+
/** Only on create/update answers. */
|
|
84
|
+
updatedAt?: string;
|
|
85
|
+
/** Only on create/update answers. */
|
|
86
|
+
archivedAt?: string | null;
|
|
87
|
+
/** Only on getMany/getById answers (contact avatar URL, or empty string). */
|
|
88
|
+
url?: unknown;
|
|
89
|
+
/** Only on getMany/getById answers (contact last message, or empty string). */
|
|
90
|
+
lastMessage?: unknown;
|
|
91
|
+
/** Only on getMany/getById answers: sum of the products (`ammount * value`). */
|
|
92
|
+
totalValue?: number;
|
|
26
93
|
contact?: Contact | null;
|
|
27
94
|
owner?: User | null;
|
|
28
95
|
pipeline?: Pipeline;
|
|
96
|
+
pipeline_stage?: PipelineStage | null;
|
|
97
|
+
stage_status?: PipelineStageStatus | null;
|
|
98
|
+
stage_reason?: PipelineStageReason | null;
|
|
99
|
+
movements?: CardMovement[];
|
|
100
|
+
products?: CardProduct[];
|
|
101
|
+
comments?: CardComment[];
|
|
29
102
|
};
|
|
30
|
-
export type CardRelationships = 'contact' | 'owner' | 'pipeline';
|
|
103
|
+
export type CardRelationships = 'contact' | 'owner' | 'pipeline' | 'pipeline_stage' | 'stage_status' | 'stage_reason' | 'movements' | 'products' | 'comments';
|
|
31
104
|
export type CreateCardPayload = {
|
|
105
|
+
/** Up to 255 characters. */
|
|
32
106
|
title: string;
|
|
33
107
|
pipelineId: string;
|
|
34
|
-
contactId
|
|
35
|
-
|
|
108
|
+
contactId: string;
|
|
109
|
+
/** Must be a stage of `pipelineId`. */
|
|
110
|
+
pipelineStageId: string;
|
|
36
111
|
description?: string;
|
|
112
|
+
ownerId?: string;
|
|
113
|
+
statusId?: string;
|
|
114
|
+
reasonId?: string;
|
|
37
115
|
organization?: string;
|
|
38
116
|
organizationSegment?: string;
|
|
39
117
|
originChannel?: string;
|
|
40
118
|
originCampaign?: string;
|
|
41
|
-
ownerId?: string;
|
|
42
119
|
};
|
|
43
120
|
export type UpdateCardPayload = {
|
|
44
121
|
title?: string;
|
|
45
122
|
description?: string | null;
|
|
46
123
|
contactId?: string | null;
|
|
124
|
+
/** Moves the card to another pipeline (send the target `pipelineStageId` along). */
|
|
125
|
+
pipelineId?: string;
|
|
47
126
|
pipelineStageId?: string | null;
|
|
48
127
|
statusId?: string | null;
|
|
49
128
|
reasonId?: string | null;
|
|
50
129
|
ownerId?: string | null;
|
|
130
|
+
order?: number;
|
|
51
131
|
organization?: string | null;
|
|
52
132
|
organizationSegment?: string | null;
|
|
53
133
|
originChannel?: string | null;
|
|
54
134
|
originCampaign?: string | null;
|
|
55
135
|
isArchived?: boolean;
|
|
136
|
+
/** Marks the card as won (`true`) or lost (`false`); the backend fills `finishedAt`. */
|
|
56
137
|
success?: boolean | null;
|
|
57
138
|
finishedAt?: string | null;
|
|
58
139
|
};
|
|
140
|
+
export {};
|
|
59
141
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/cards/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/cards/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
import type { Department } from '../departments/types';
|
|
2
|
-
export type
|
|
2
|
+
export type PipelineStageStatus = {
|
|
3
|
+
id: string;
|
|
4
|
+
/** The default "Finalizados" stage carries `STATUS_WON` and `STATUS_LOOSE`. */
|
|
5
|
+
name: string;
|
|
6
|
+
position: number;
|
|
7
|
+
pipelineId: string;
|
|
8
|
+
stageId: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
deletedAt: string | null;
|
|
13
|
+
};
|
|
14
|
+
export type PipelineStageReason = {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
position: number;
|
|
18
|
+
isWon: boolean;
|
|
19
|
+
pipelineId: string;
|
|
20
|
+
stageId: string;
|
|
21
|
+
accountId: string;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
};
|
|
25
|
+
export type PipelineStage = {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
position: number;
|
|
29
|
+
pipelineId: string;
|
|
30
|
+
accountId: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
deletedAt: string | null;
|
|
34
|
+
statuses?: PipelineStageStatus[];
|
|
35
|
+
reasons?: PipelineStageReason[];
|
|
36
|
+
/** Card totals — present on create/update/getById, absent on the listing `include`. */
|
|
37
|
+
totalCards?: number;
|
|
38
|
+
totalCardsValue?: number;
|
|
39
|
+
totalWonCardsValue?: number;
|
|
40
|
+
totalLostCardsValue?: number;
|
|
41
|
+
};
|
|
42
|
+
/** The pipeline row as stored — what the webhook events (`pipeline.created`/`.updated`) carry. */
|
|
43
|
+
export type PipelineRecord = {
|
|
3
44
|
id: string;
|
|
4
45
|
name: string;
|
|
5
46
|
goBack: boolean;
|
|
@@ -9,16 +50,39 @@ export type Pipeline = {
|
|
|
9
50
|
createdAt: string;
|
|
10
51
|
updatedAt: string;
|
|
11
52
|
deletedAt: string | null;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A sales funnel pipeline.
|
|
56
|
+
*
|
|
57
|
+
* `create`, `updateById` and `getById` answer the full entity, always with `stages`. The listing
|
|
58
|
+
* (`getMany`) goes through the backend transformer and only carries `id`, `name`, `goBack` and
|
|
59
|
+
* `archivedAt` (plus the included relationships) — hence the optional fields below.
|
|
60
|
+
*/
|
|
61
|
+
export type Pipeline = {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
goBack: boolean;
|
|
65
|
+
archivedAt: string | null;
|
|
66
|
+
accountId?: string;
|
|
67
|
+
permissionToViewCard?: boolean | null;
|
|
68
|
+
createdAt?: string;
|
|
69
|
+
updatedAt?: string;
|
|
70
|
+
deletedAt?: string | null;
|
|
71
|
+
stages?: PipelineStage[];
|
|
12
72
|
departments?: Department[];
|
|
13
73
|
};
|
|
14
|
-
export type PipelineRelationships = 'departments';
|
|
74
|
+
export type PipelineRelationships = 'departments' | 'stages';
|
|
15
75
|
export type CreatePipelinePayload = {
|
|
76
|
+
/** Up to 100 characters; must be unique in the account. */
|
|
16
77
|
name: string;
|
|
78
|
+
/** `false` = only move forward, `true` = move forward and backward. */
|
|
17
79
|
goBack?: boolean;
|
|
80
|
+
/** When `true`, non-admin users only see the cards they own. */
|
|
18
81
|
permissionToViewCard?: boolean;
|
|
19
82
|
};
|
|
20
83
|
export type UpdatePipelinePayload = {
|
|
21
|
-
|
|
84
|
+
/** Required by the backend on every update (400 `required` otherwise). */
|
|
85
|
+
name: string;
|
|
22
86
|
goBack?: boolean;
|
|
23
87
|
permissionToViewCard?: boolean | null;
|
|
24
88
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/pipeline/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,MAAM,QAAQ,GAAG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/pipeline/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_core_errors = require('./errors.cjs');
|
|
2
3
|
|
|
3
4
|
//#region src/core/BaseApiClient.ts
|
|
4
|
-
var ApiError = class extends Error {
|
|
5
|
-
errorClass;
|
|
6
|
-
status;
|
|
7
|
-
validationErrors;
|
|
8
|
-
constructor(message, errorClass, status, validationErrors = null) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = "ApiError";
|
|
11
|
-
this.errorClass = errorClass;
|
|
12
|
-
this.status = status;
|
|
13
|
-
this.validationErrors = validationErrors;
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
5
|
var BaseApiClient = class {
|
|
17
6
|
baseUrl;
|
|
18
7
|
accessToken = null;
|
|
@@ -26,7 +15,7 @@ var BaseApiClient = class {
|
|
|
26
15
|
}
|
|
27
16
|
async request(method, endpoint, body, headers) {
|
|
28
17
|
const url = `${this.baseUrl}/${endpoint.replace(/^\//, "")}`;
|
|
29
|
-
const
|
|
18
|
+
const init = {
|
|
30
19
|
method,
|
|
31
20
|
headers: {
|
|
32
21
|
"Content-Type": "application/json",
|
|
@@ -34,26 +23,17 @@ var BaseApiClient = class {
|
|
|
34
23
|
...headers
|
|
35
24
|
},
|
|
36
25
|
...body !== void 0 && { body: JSON.stringify(body) }
|
|
37
|
-
}
|
|
26
|
+
};
|
|
27
|
+
let response;
|
|
28
|
+
try {
|
|
29
|
+
response = await fetch(url, init);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new require_core_errors.ApiConnectionError(`Request to ${method} ${url} failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
32
|
+
}
|
|
38
33
|
return this.handleResponse(response);
|
|
39
34
|
}
|
|
40
35
|
async handleResponse(response) {
|
|
41
|
-
if (!response.ok)
|
|
42
|
-
const isJson = response.headers.get("Content-Type")?.includes("application/json") ?? false;
|
|
43
|
-
let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`;
|
|
44
|
-
let errorClass;
|
|
45
|
-
let validationErrors = null;
|
|
46
|
-
if (isJson) try {
|
|
47
|
-
const errorText = await response.text();
|
|
48
|
-
if (errorText) {
|
|
49
|
-
const errorData = JSON.parse(errorText);
|
|
50
|
-
errorMessage = errorData.message || errorData.error || errorMessage;
|
|
51
|
-
errorClass = errorData.error;
|
|
52
|
-
validationErrors = errorData.validationErrors ?? null;
|
|
53
|
-
}
|
|
54
|
-
} catch {}
|
|
55
|
-
throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
|
|
56
|
-
}
|
|
36
|
+
if (!response.ok) throw require_core_errors.ApiError.fromResponse(response.status, response.statusText, response.headers, await this.readErrorBody(response));
|
|
57
37
|
if (response.status === 204) return;
|
|
58
38
|
const text = await response.text();
|
|
59
39
|
if (!text) return;
|
|
@@ -64,6 +44,21 @@ var BaseApiClient = class {
|
|
|
64
44
|
return text;
|
|
65
45
|
}
|
|
66
46
|
}
|
|
47
|
+
/** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */
|
|
48
|
+
async readErrorBody(response) {
|
|
49
|
+
let text;
|
|
50
|
+
try {
|
|
51
|
+
text = await response.text();
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!text) return;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(text);
|
|
58
|
+
} catch {
|
|
59
|
+
return text;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
67
62
|
get(endpoint, headers) {
|
|
68
63
|
return this.request("GET", endpoint, void 0, headers);
|
|
69
64
|
}
|
|
@@ -82,6 +77,5 @@ var BaseApiClient = class {
|
|
|
82
77
|
};
|
|
83
78
|
|
|
84
79
|
//#endregion
|
|
85
|
-
exports.ApiError = ApiError;
|
|
86
80
|
exports.BaseApiClient = BaseApiClient;
|
|
87
81
|
//# sourceMappingURL=BaseApiClient.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\
|
|
1
|
+
{"version":3,"file":"BaseApiClient.cjs","names":["ApiConnectionError","ApiError"],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\nimport { ApiConnectionError, ApiError } from './errors'\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n const init: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n }\n\n let response: Response\n try {\n response = await fetch(url, init)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new ApiConnectionError(`Request to ${method} ${url} failed: ${reason}`, {\n cause: error,\n })\n }\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n throw ApiError.fromResponse(\n response.status,\n response.statusText,\n response.headers,\n await this.readErrorBody(response),\n )\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */\n protected async readErrorBody(response: Response): Promise<unknown> {\n let text: string\n try {\n text = await response.text()\n } catch {\n return undefined\n }\n if (!text) {\n return undefined\n }\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;;AAGA,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAC1D,MAAM,OAAoB;GACxB;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD;EAED,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,MAAM,KAAK,KAAK;WAC1B,OAAO;AAEd,SAAM,IAAIA,uCAAmB,cAAc,OAAO,GAAG,IAAI,WAD1C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACS,EAC5E,OAAO,OACR,CAAC;;AAGJ,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,GACZ,OAAMC,6BAAS,aACb,SAAS,QACT,SAAS,YACT,SAAS,SACT,MAAM,KAAK,cAAc,SAAS,CACnC;AAIH,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;CAKX,MAAgB,cAAc,UAAsC;EAClE,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,MAAM;UACtB;AACN;;AAEF,MAAI,CAAC,KACH;AAEF,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
import type { ApiClient, HttpMethod } from './ApiClient';
|
|
2
|
-
export interface ValidationError {
|
|
3
|
-
path: string;
|
|
4
|
-
message: string;
|
|
5
|
-
code: string;
|
|
6
|
-
}
|
|
7
|
-
export declare class ApiError extends Error {
|
|
8
|
-
readonly errorClass?: string | undefined;
|
|
9
|
-
readonly status?: number | undefined;
|
|
10
|
-
readonly validationErrors: ValidationError[] | null;
|
|
11
|
-
constructor(message: string, errorClass?: string, status?: number, validationErrors?: ValidationError[] | null);
|
|
12
|
-
}
|
|
13
2
|
export declare class BaseApiClient implements ApiClient {
|
|
14
3
|
protected readonly baseUrl: string;
|
|
15
4
|
protected accessToken: string | null;
|
|
@@ -17,6 +6,8 @@ export declare class BaseApiClient implements ApiClient {
|
|
|
17
6
|
setAccessToken(accessToken: string): this;
|
|
18
7
|
request<TResponse = unknown, TBody = unknown>(method: HttpMethod, endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
|
|
19
8
|
protected handleResponse<TResponse>(response: Response): Promise<TResponse>;
|
|
9
|
+
/** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */
|
|
10
|
+
protected readErrorBody(response: Response): Promise<unknown>;
|
|
20
11
|
get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse>;
|
|
21
12
|
post<TResponse = unknown, TBody = unknown>(endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
|
|
22
13
|
put<TResponse = unknown, TBody = unknown>(endpoint: string, body?: TBody, headers?: Record<string, string>): Promise<TResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxD,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE/B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAKjD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAyBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuCjF,sGAAsG;cACtF,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBnE,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
+
import { ApiConnectionError, ApiError } from "./errors.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/core/BaseApiClient.ts
|
|
2
|
-
var ApiError = class extends Error {
|
|
3
|
-
errorClass;
|
|
4
|
-
status;
|
|
5
|
-
validationErrors;
|
|
6
|
-
constructor(message, errorClass, status, validationErrors = null) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = "ApiError";
|
|
9
|
-
this.errorClass = errorClass;
|
|
10
|
-
this.status = status;
|
|
11
|
-
this.validationErrors = validationErrors;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
4
|
var BaseApiClient = class {
|
|
15
5
|
baseUrl;
|
|
16
6
|
accessToken = null;
|
|
@@ -24,7 +14,7 @@ var BaseApiClient = class {
|
|
|
24
14
|
}
|
|
25
15
|
async request(method, endpoint, body, headers) {
|
|
26
16
|
const url = `${this.baseUrl}/${endpoint.replace(/^\//, "")}`;
|
|
27
|
-
const
|
|
17
|
+
const init = {
|
|
28
18
|
method,
|
|
29
19
|
headers: {
|
|
30
20
|
"Content-Type": "application/json",
|
|
@@ -32,26 +22,17 @@ var BaseApiClient = class {
|
|
|
32
22
|
...headers
|
|
33
23
|
},
|
|
34
24
|
...body !== void 0 && { body: JSON.stringify(body) }
|
|
35
|
-
}
|
|
25
|
+
};
|
|
26
|
+
let response;
|
|
27
|
+
try {
|
|
28
|
+
response = await fetch(url, init);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new ApiConnectionError(`Request to ${method} ${url} failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
31
|
+
}
|
|
36
32
|
return this.handleResponse(response);
|
|
37
33
|
}
|
|
38
34
|
async handleResponse(response) {
|
|
39
|
-
if (!response.ok)
|
|
40
|
-
const isJson = response.headers.get("Content-Type")?.includes("application/json") ?? false;
|
|
41
|
-
let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`;
|
|
42
|
-
let errorClass;
|
|
43
|
-
let validationErrors = null;
|
|
44
|
-
if (isJson) try {
|
|
45
|
-
const errorText = await response.text();
|
|
46
|
-
if (errorText) {
|
|
47
|
-
const errorData = JSON.parse(errorText);
|
|
48
|
-
errorMessage = errorData.message || errorData.error || errorMessage;
|
|
49
|
-
errorClass = errorData.error;
|
|
50
|
-
validationErrors = errorData.validationErrors ?? null;
|
|
51
|
-
}
|
|
52
|
-
} catch {}
|
|
53
|
-
throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
|
|
54
|
-
}
|
|
35
|
+
if (!response.ok) throw ApiError.fromResponse(response.status, response.statusText, response.headers, await this.readErrorBody(response));
|
|
55
36
|
if (response.status === 204) return;
|
|
56
37
|
const text = await response.text();
|
|
57
38
|
if (!text) return;
|
|
@@ -62,6 +43,21 @@ var BaseApiClient = class {
|
|
|
62
43
|
return text;
|
|
63
44
|
}
|
|
64
45
|
}
|
|
46
|
+
/** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */
|
|
47
|
+
async readErrorBody(response) {
|
|
48
|
+
let text;
|
|
49
|
+
try {
|
|
50
|
+
text = await response.text();
|
|
51
|
+
} catch {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!text) return;
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(text);
|
|
57
|
+
} catch {
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
65
61
|
get(endpoint, headers) {
|
|
66
62
|
return this.request("GET", endpoint, void 0, headers);
|
|
67
63
|
}
|
|
@@ -80,5 +76,5 @@ var BaseApiClient = class {
|
|
|
80
76
|
};
|
|
81
77
|
|
|
82
78
|
//#endregion
|
|
83
|
-
export {
|
|
79
|
+
export { BaseApiClient };
|
|
84
80
|
//# sourceMappingURL=BaseApiClient.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\
|
|
1
|
+
{"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\nimport { ApiConnectionError, ApiError } from './errors'\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n const init: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n }\n\n let response: Response\n try {\n response = await fetch(url, init)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new ApiConnectionError(`Request to ${method} ${url} failed: ${reason}`, {\n cause: error,\n })\n }\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n throw ApiError.fromResponse(\n response.status,\n response.statusText,\n response.headers,\n await this.readErrorBody(response),\n )\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n /** Reads an error body: parsed JSON when possible, the raw text otherwise, `undefined` when empty. */\n protected async readErrorBody(response: Response): Promise<unknown> {\n let text: string\n try {\n text = await response.text()\n } catch {\n return undefined\n }\n if (!text) {\n return undefined\n }\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;AAGA,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAC1D,MAAM,OAAoB;GACxB;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD;EAED,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,MAAM,KAAK,KAAK;WAC1B,OAAO;AAEd,SAAM,IAAI,mBAAmB,cAAc,OAAO,GAAG,IAAI,WAD1C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACS,EAC5E,OAAO,OACR,CAAC;;AAGJ,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,GACZ,OAAM,SAAS,aACb,SAAS,QACT,SAAS,YACT,SAAS,SACT,MAAM,KAAK,cAAc,SAAS,CACnC;AAIH,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;CAKX,MAAgB,cAAc,UAAsC;EAClE,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,MAAM;UACtB;AACN;;AAEF,MAAI,CAAC,KACH;AAEF,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
|