@bagelink/sdk 0.0.1276 → 0.0.1288
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/bin/index.ts +0 -2
- package/bin/utils.ts +0 -66
- package/dist/index.cjs +42 -7
- package/dist/index.d.cts +3 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +42 -8
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/utils.ts +65 -0
package/bin/index.ts
CHANGED
package/bin/utils.ts
CHANGED
|
@@ -71,69 +71,3 @@ export async function handleAuthCode(_withAuth: boolean, _bagelinkDir: string) {
|
|
|
71
71
|
await formatAndWriteCode(authPath, authCode)
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
export function formatAPIErrorMessage(error: any) {
|
|
76
|
-
// Handle case where error or response is undefined
|
|
77
|
-
if (!error || !error.response) {
|
|
78
|
-
return 'Network error occurred. Please check your connection.'
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const { status, data } = error.response
|
|
82
|
-
|
|
83
|
-
// Handle validation errors (422)
|
|
84
|
-
if (data?.detail && Array.isArray(data.detail)) {
|
|
85
|
-
return data.detail
|
|
86
|
-
.map((err: any) => {
|
|
87
|
-
// Handle nested field paths properly
|
|
88
|
-
const fieldPath = err.loc.slice(1).join('.')
|
|
89
|
-
const field = fieldPath
|
|
90
|
-
.split('.')
|
|
91
|
-
.map((part: string) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
92
|
-
.join(' ')
|
|
93
|
-
|
|
94
|
-
let message = err.msg
|
|
95
|
-
// Common validation messages
|
|
96
|
-
.replace(/^(?:field|value|string|none) required$/i, 'is required')
|
|
97
|
-
.replace(/^value is not a valid/i, 'must be a valid')
|
|
98
|
-
.replace(/^ensure this value/i, 'this value must')
|
|
99
|
-
.replace(/^str type expected$/i, 'must be text')
|
|
100
|
-
.replace(/^value could not be parsed to/i, 'must be a')
|
|
101
|
-
.replace(/^ensure this value has at least/i, 'must have at least')
|
|
102
|
-
.replace(/^ensure this value has at most/i, 'must have at most')
|
|
103
|
-
.replace(/^invalid datetime format/i, 'must be a valid date/time')
|
|
104
|
-
.replace(/^value is not a valid email address/i, 'must be a valid email address')
|
|
105
|
-
.replace(/^value is not a valid integer/i, 'must be a whole number')
|
|
106
|
-
.replace(/^value is not a valid float/i, 'must be a valid number')
|
|
107
|
-
|
|
108
|
-
return `${field} ${message.toLowerCase()}`
|
|
109
|
-
})
|
|
110
|
-
.join('\n')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Handle specific HTTP status codes
|
|
114
|
-
switch (status) {
|
|
115
|
-
case 401:
|
|
116
|
-
return 'Authentication required. Please log in.'
|
|
117
|
-
case 403:
|
|
118
|
-
return 'You do not have permission to perform this action.'
|
|
119
|
-
case 404:
|
|
120
|
-
return 'The requested resource was not found.'
|
|
121
|
-
case 429:
|
|
122
|
-
return 'Too many requests. Please try again later.'
|
|
123
|
-
case 500:
|
|
124
|
-
return 'An internal server error occurred. Please try again later.'
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Handle other errors with detail
|
|
128
|
-
if (data?.detail && typeof data.detail === 'string') {
|
|
129
|
-
return data.detail
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Handle errors with a message field
|
|
133
|
-
if (data?.message) {
|
|
134
|
-
return data.message
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Generic error fallback
|
|
138
|
-
return `An error occurred (Status ${status}): ${error.message || 'Unknown error'}`
|
|
139
|
-
}
|
package/dist/index.cjs
CHANGED
|
@@ -49,7 +49,7 @@ function schemaToType(schema) {
|
|
|
49
49
|
return "undefined";
|
|
50
50
|
case "null":
|
|
51
51
|
return "null";
|
|
52
|
-
case
|
|
52
|
+
case void 0:
|
|
53
53
|
return "any";
|
|
54
54
|
default:
|
|
55
55
|
console.log("Unknown type", schema.type);
|
|
@@ -61,7 +61,7 @@ function isOptional(schema) {
|
|
|
61
61
|
const splitType = type.split(/\s+\|\s+/);
|
|
62
62
|
const includesNull = splitType.includes("null");
|
|
63
63
|
const includesUndefined = splitType.includes("undefined");
|
|
64
|
-
return includesNull || includesUndefined || schema.default !==
|
|
64
|
+
return includesNull || includesUndefined || schema.default !== void 0;
|
|
65
65
|
}
|
|
66
66
|
function cleanOptionals(str) {
|
|
67
67
|
return str.split(" | ").filter((t) => t !== "null" && t !== "undefined").join(" | ");
|
|
@@ -343,7 +343,7 @@ function generateFunctions(paths, baseUrl) {
|
|
|
343
343
|
}, body);
|
|
344
344
|
}
|
|
345
345
|
for (const [parent, object] of Object.entries(body)) {
|
|
346
|
-
tsString += `export const ${parent} = ${JSON.stringify(object,
|
|
346
|
+
tsString += `export const ${parent} = ${JSON.stringify(object, void 0, 2)};
|
|
347
347
|
`;
|
|
348
348
|
}
|
|
349
349
|
Object.entries(functionsInventory).forEach(([key, value]) => {
|
|
@@ -394,6 +394,40 @@ const index = async (openApiUrl, baseUrl) => {
|
|
|
394
394
|
}
|
|
395
395
|
};
|
|
396
396
|
|
|
397
|
+
function formatAPIErrorMessage(error) {
|
|
398
|
+
if (!error || !error.response) {
|
|
399
|
+
return "Network error occurred. Please check your connection.";
|
|
400
|
+
}
|
|
401
|
+
const { status, data } = error.response;
|
|
402
|
+
if (data?.detail && Array.isArray(data.detail)) {
|
|
403
|
+
return data.detail.map((err) => {
|
|
404
|
+
const fieldPath = err.loc.slice(1).join(".");
|
|
405
|
+
const field = fieldPath.split(".").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
406
|
+
let message = err.msg.replace(/^(?:field|value|string|none) required$/i, "is required").replace(/^value is not a valid/i, "must be a valid").replace(/^ensure this value/i, "this value must").replace(/^str type expected$/i, "must be text").replace(/^value could not be parsed to/i, "must be a").replace(/^ensure this value has at least/i, "must have at least").replace(/^ensure this value has at most/i, "must have at most").replace(/^invalid datetime format/i, "must be a valid date/time").replace(/^value is not a valid email address/i, "must be a valid email address").replace(/^value is not a valid integer/i, "must be a whole number").replace(/^value is not a valid float/i, "must be a valid number");
|
|
407
|
+
return `${field} ${message.toLowerCase()}`;
|
|
408
|
+
}).join("\n");
|
|
409
|
+
}
|
|
410
|
+
switch (status) {
|
|
411
|
+
case 401:
|
|
412
|
+
return "Authentication required. Please log in.";
|
|
413
|
+
case 403:
|
|
414
|
+
return "You do not have permission to perform this action.";
|
|
415
|
+
case 404:
|
|
416
|
+
return "The requested resource was not found.";
|
|
417
|
+
case 429:
|
|
418
|
+
return "Too many requests. Please try again later.";
|
|
419
|
+
case 500:
|
|
420
|
+
return "An internal server error occurred. Please try again later.";
|
|
421
|
+
}
|
|
422
|
+
if (data?.detail && typeof data.detail === "string") {
|
|
423
|
+
return data.detail;
|
|
424
|
+
}
|
|
425
|
+
if (data?.message) {
|
|
426
|
+
return data.message;
|
|
427
|
+
}
|
|
428
|
+
return `An error occurred (Status ${status}): ${error.message || "Unknown error"}`;
|
|
429
|
+
}
|
|
430
|
+
|
|
397
431
|
const axios = axios__default.create({
|
|
398
432
|
withCredentials: true
|
|
399
433
|
});
|
|
@@ -426,7 +460,7 @@ class DataRequest {
|
|
|
426
460
|
} catch (err) {
|
|
427
461
|
console.log(err);
|
|
428
462
|
this.bagel.onError?.(err);
|
|
429
|
-
return
|
|
463
|
+
return void 0;
|
|
430
464
|
}
|
|
431
465
|
}
|
|
432
466
|
item(id) {
|
|
@@ -463,7 +497,7 @@ class BagelAuth {
|
|
|
463
497
|
this.bagel = bagel;
|
|
464
498
|
this.bagel = bagel;
|
|
465
499
|
}
|
|
466
|
-
user =
|
|
500
|
+
user = void 0;
|
|
467
501
|
async validateUser() {
|
|
468
502
|
try {
|
|
469
503
|
const { data: usr } = await axios.get("/users/me", {
|
|
@@ -511,7 +545,7 @@ class BagelAuth {
|
|
|
511
545
|
this.bagel.onError?.(err);
|
|
512
546
|
console.log(err);
|
|
513
547
|
}
|
|
514
|
-
this.user =
|
|
548
|
+
this.user = void 0;
|
|
515
549
|
}
|
|
516
550
|
async acceptInvite(token, user) {
|
|
517
551
|
await axios.post(`/auth/accept-invite/${token}`, user);
|
|
@@ -541,7 +575,7 @@ class Bagel {
|
|
|
541
575
|
axios.defaults.baseURL = this.host;
|
|
542
576
|
this.onError = onError;
|
|
543
577
|
}
|
|
544
|
-
read_table =
|
|
578
|
+
read_table = void 0;
|
|
545
579
|
data(table) {
|
|
546
580
|
return new DataRequest(table, this);
|
|
547
581
|
}
|
|
@@ -625,4 +659,5 @@ class Bagel {
|
|
|
625
659
|
}
|
|
626
660
|
|
|
627
661
|
exports.Bagel = Bagel;
|
|
662
|
+
exports.formatAPIErrorMessage = formatAPIErrorMessage;
|
|
628
663
|
exports.openAPI = index;
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,8 @@ interface OpenAPIResponse {
|
|
|
4
4
|
}
|
|
5
5
|
declare const _default: (openApiUrl: string, baseUrl: string) => Promise<OpenAPIResponse>;
|
|
6
6
|
|
|
7
|
+
declare function formatAPIErrorMessage(error: any): any;
|
|
8
|
+
|
|
7
9
|
type Tables = '';
|
|
8
10
|
type TableToTypeMapping = Record<Tables, any>;
|
|
9
11
|
|
|
@@ -106,4 +108,4 @@ declare class Bagel {
|
|
|
106
108
|
uploadFile<T = any>(file: File, options?: UploadOptions): Promise<T>;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
export { Bagel, type TableToTypeMapping, type Tables, type User, _default as openAPI };
|
|
111
|
+
export { Bagel, type TableToTypeMapping, type Tables, type User, formatAPIErrorMessage, _default as openAPI };
|
package/dist/index.d.mts
CHANGED
|
@@ -4,6 +4,8 @@ interface OpenAPIResponse {
|
|
|
4
4
|
}
|
|
5
5
|
declare const _default: (openApiUrl: string, baseUrl: string) => Promise<OpenAPIResponse>;
|
|
6
6
|
|
|
7
|
+
declare function formatAPIErrorMessage(error: any): any;
|
|
8
|
+
|
|
7
9
|
type Tables = '';
|
|
8
10
|
type TableToTypeMapping = Record<Tables, any>;
|
|
9
11
|
|
|
@@ -106,4 +108,4 @@ declare class Bagel {
|
|
|
106
108
|
uploadFile<T = any>(file: File, options?: UploadOptions): Promise<T>;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
export { Bagel, type TableToTypeMapping, type Tables, type User, _default as openAPI };
|
|
111
|
+
export { Bagel, type TableToTypeMapping, type Tables, type User, formatAPIErrorMessage, _default as openAPI };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ interface OpenAPIResponse {
|
|
|
4
4
|
}
|
|
5
5
|
declare const _default: (openApiUrl: string, baseUrl: string) => Promise<OpenAPIResponse>;
|
|
6
6
|
|
|
7
|
+
declare function formatAPIErrorMessage(error: any): any;
|
|
8
|
+
|
|
7
9
|
type Tables = '';
|
|
8
10
|
type TableToTypeMapping = Record<Tables, any>;
|
|
9
11
|
|
|
@@ -106,4 +108,4 @@ declare class Bagel {
|
|
|
106
108
|
uploadFile<T = any>(file: File, options?: UploadOptions): Promise<T>;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
export { Bagel, type TableToTypeMapping, type Tables, type User, _default as openAPI };
|
|
111
|
+
export { Bagel, type TableToTypeMapping, type Tables, type User, formatAPIErrorMessage, _default as openAPI };
|
package/dist/index.mjs
CHANGED
|
@@ -43,7 +43,7 @@ function schemaToType(schema) {
|
|
|
43
43
|
return "undefined";
|
|
44
44
|
case "null":
|
|
45
45
|
return "null";
|
|
46
|
-
case
|
|
46
|
+
case void 0:
|
|
47
47
|
return "any";
|
|
48
48
|
default:
|
|
49
49
|
console.log("Unknown type", schema.type);
|
|
@@ -55,7 +55,7 @@ function isOptional(schema) {
|
|
|
55
55
|
const splitType = type.split(/\s+\|\s+/);
|
|
56
56
|
const includesNull = splitType.includes("null");
|
|
57
57
|
const includesUndefined = splitType.includes("undefined");
|
|
58
|
-
return includesNull || includesUndefined || schema.default !==
|
|
58
|
+
return includesNull || includesUndefined || schema.default !== void 0;
|
|
59
59
|
}
|
|
60
60
|
function cleanOptionals(str) {
|
|
61
61
|
return str.split(" | ").filter((t) => t !== "null" && t !== "undefined").join(" | ");
|
|
@@ -337,7 +337,7 @@ function generateFunctions(paths, baseUrl) {
|
|
|
337
337
|
}, body);
|
|
338
338
|
}
|
|
339
339
|
for (const [parent, object] of Object.entries(body)) {
|
|
340
|
-
tsString += `export const ${parent} = ${JSON.stringify(object,
|
|
340
|
+
tsString += `export const ${parent} = ${JSON.stringify(object, void 0, 2)};
|
|
341
341
|
`;
|
|
342
342
|
}
|
|
343
343
|
Object.entries(functionsInventory).forEach(([key, value]) => {
|
|
@@ -388,6 +388,40 @@ const index = async (openApiUrl, baseUrl) => {
|
|
|
388
388
|
}
|
|
389
389
|
};
|
|
390
390
|
|
|
391
|
+
function formatAPIErrorMessage(error) {
|
|
392
|
+
if (!error || !error.response) {
|
|
393
|
+
return "Network error occurred. Please check your connection.";
|
|
394
|
+
}
|
|
395
|
+
const { status, data } = error.response;
|
|
396
|
+
if (data?.detail && Array.isArray(data.detail)) {
|
|
397
|
+
return data.detail.map((err) => {
|
|
398
|
+
const fieldPath = err.loc.slice(1).join(".");
|
|
399
|
+
const field = fieldPath.split(".").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
400
|
+
let message = err.msg.replace(/^(?:field|value|string|none) required$/i, "is required").replace(/^value is not a valid/i, "must be a valid").replace(/^ensure this value/i, "this value must").replace(/^str type expected$/i, "must be text").replace(/^value could not be parsed to/i, "must be a").replace(/^ensure this value has at least/i, "must have at least").replace(/^ensure this value has at most/i, "must have at most").replace(/^invalid datetime format/i, "must be a valid date/time").replace(/^value is not a valid email address/i, "must be a valid email address").replace(/^value is not a valid integer/i, "must be a whole number").replace(/^value is not a valid float/i, "must be a valid number");
|
|
401
|
+
return `${field} ${message.toLowerCase()}`;
|
|
402
|
+
}).join("\n");
|
|
403
|
+
}
|
|
404
|
+
switch (status) {
|
|
405
|
+
case 401:
|
|
406
|
+
return "Authentication required. Please log in.";
|
|
407
|
+
case 403:
|
|
408
|
+
return "You do not have permission to perform this action.";
|
|
409
|
+
case 404:
|
|
410
|
+
return "The requested resource was not found.";
|
|
411
|
+
case 429:
|
|
412
|
+
return "Too many requests. Please try again later.";
|
|
413
|
+
case 500:
|
|
414
|
+
return "An internal server error occurred. Please try again later.";
|
|
415
|
+
}
|
|
416
|
+
if (data?.detail && typeof data.detail === "string") {
|
|
417
|
+
return data.detail;
|
|
418
|
+
}
|
|
419
|
+
if (data?.message) {
|
|
420
|
+
return data.message;
|
|
421
|
+
}
|
|
422
|
+
return `An error occurred (Status ${status}): ${error.message || "Unknown error"}`;
|
|
423
|
+
}
|
|
424
|
+
|
|
391
425
|
const axios = axios$1.create({
|
|
392
426
|
withCredentials: true
|
|
393
427
|
});
|
|
@@ -420,7 +454,7 @@ class DataRequest {
|
|
|
420
454
|
} catch (err) {
|
|
421
455
|
console.log(err);
|
|
422
456
|
this.bagel.onError?.(err);
|
|
423
|
-
return
|
|
457
|
+
return void 0;
|
|
424
458
|
}
|
|
425
459
|
}
|
|
426
460
|
item(id) {
|
|
@@ -457,7 +491,7 @@ class BagelAuth {
|
|
|
457
491
|
this.bagel = bagel;
|
|
458
492
|
this.bagel = bagel;
|
|
459
493
|
}
|
|
460
|
-
user =
|
|
494
|
+
user = void 0;
|
|
461
495
|
async validateUser() {
|
|
462
496
|
try {
|
|
463
497
|
const { data: usr } = await axios.get("/users/me", {
|
|
@@ -505,7 +539,7 @@ class BagelAuth {
|
|
|
505
539
|
this.bagel.onError?.(err);
|
|
506
540
|
console.log(err);
|
|
507
541
|
}
|
|
508
|
-
this.user =
|
|
542
|
+
this.user = void 0;
|
|
509
543
|
}
|
|
510
544
|
async acceptInvite(token, user) {
|
|
511
545
|
await axios.post(`/auth/accept-invite/${token}`, user);
|
|
@@ -535,7 +569,7 @@ class Bagel {
|
|
|
535
569
|
axios.defaults.baseURL = this.host;
|
|
536
570
|
this.onError = onError;
|
|
537
571
|
}
|
|
538
|
-
read_table =
|
|
572
|
+
read_table = void 0;
|
|
539
573
|
data(table) {
|
|
540
574
|
return new DataRequest(table, this);
|
|
541
575
|
}
|
|
@@ -618,4 +652,4 @@ class Bagel {
|
|
|
618
652
|
}
|
|
619
653
|
}
|
|
620
654
|
|
|
621
|
-
export { Bagel, index as openAPI };
|
|
655
|
+
export { Bagel, formatAPIErrorMessage, index as openAPI };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bagelink/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.1288",
|
|
5
5
|
"description": "Bagel core sdk packages",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Neveh Allon",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@apidevtools/swagger-parser": "^10.1.1",
|
|
57
57
|
"handlebars": "^4.7.8",
|
|
58
|
-
"openapi-zod-client": "^1.18.
|
|
58
|
+
"openapi-zod-client": "^1.18.3",
|
|
59
59
|
"openapi3-ts": "^3.1.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export function formatAPIErrorMessage(error: any) {
|
|
2
|
+
// Handle case where error or response is undefined
|
|
3
|
+
if (!error || !error.response) {
|
|
4
|
+
return 'Network error occurred. Please check your connection.'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { status, data } = error.response
|
|
8
|
+
|
|
9
|
+
// Handle validation errors (422)
|
|
10
|
+
if (data?.detail && Array.isArray(data.detail)) {
|
|
11
|
+
return data.detail
|
|
12
|
+
.map((err: any) => {
|
|
13
|
+
// Handle nested field paths properly
|
|
14
|
+
const fieldPath = err.loc.slice(1).join('.')
|
|
15
|
+
const field = fieldPath
|
|
16
|
+
.split('.')
|
|
17
|
+
.map((part: string) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
18
|
+
.join(' ')
|
|
19
|
+
|
|
20
|
+
let message = err.msg
|
|
21
|
+
// Common validation messages
|
|
22
|
+
.replace(/^(?:field|value|string|none) required$/i, 'is required')
|
|
23
|
+
.replace(/^value is not a valid/i, 'must be a valid')
|
|
24
|
+
.replace(/^ensure this value/i, 'this value must')
|
|
25
|
+
.replace(/^str type expected$/i, 'must be text')
|
|
26
|
+
.replace(/^value could not be parsed to/i, 'must be a')
|
|
27
|
+
.replace(/^ensure this value has at least/i, 'must have at least')
|
|
28
|
+
.replace(/^ensure this value has at most/i, 'must have at most')
|
|
29
|
+
.replace(/^invalid datetime format/i, 'must be a valid date/time')
|
|
30
|
+
.replace(/^value is not a valid email address/i, 'must be a valid email address')
|
|
31
|
+
.replace(/^value is not a valid integer/i, 'must be a whole number')
|
|
32
|
+
.replace(/^value is not a valid float/i, 'must be a valid number')
|
|
33
|
+
|
|
34
|
+
return `${field} ${message.toLowerCase()}`
|
|
35
|
+
})
|
|
36
|
+
.join('\n')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Handle specific HTTP status codes
|
|
40
|
+
switch (status) {
|
|
41
|
+
case 401:
|
|
42
|
+
return 'Authentication required. Please log in.'
|
|
43
|
+
case 403:
|
|
44
|
+
return 'You do not have permission to perform this action.'
|
|
45
|
+
case 404:
|
|
46
|
+
return 'The requested resource was not found.'
|
|
47
|
+
case 429:
|
|
48
|
+
return 'Too many requests. Please try again later.'
|
|
49
|
+
case 500:
|
|
50
|
+
return 'An internal server error occurred. Please try again later.'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle other errors with detail
|
|
54
|
+
if (data?.detail && typeof data.detail === 'string') {
|
|
55
|
+
return data.detail
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Handle errors with a message field
|
|
59
|
+
if (data?.message) {
|
|
60
|
+
return data.message
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Generic error fallback
|
|
64
|
+
return `An error occurred (Status ${status}): ${error.message || 'Unknown error'}`
|
|
65
|
+
}
|