yoto-nodejs-client 0.0.1

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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +736 -0
  3. package/bin/auth.d.ts +3 -0
  4. package/bin/auth.d.ts.map +1 -0
  5. package/bin/auth.js +130 -0
  6. package/bin/content.d.ts +3 -0
  7. package/bin/content.d.ts.map +1 -0
  8. package/bin/content.js +117 -0
  9. package/bin/devices.d.ts +3 -0
  10. package/bin/devices.d.ts.map +1 -0
  11. package/bin/devices.js +239 -0
  12. package/bin/groups.d.ts +3 -0
  13. package/bin/groups.d.ts.map +1 -0
  14. package/bin/groups.js +80 -0
  15. package/bin/icons.d.ts +3 -0
  16. package/bin/icons.d.ts.map +1 -0
  17. package/bin/icons.js +100 -0
  18. package/bin/lib/cli-helpers.d.ts +21 -0
  19. package/bin/lib/cli-helpers.d.ts.map +1 -0
  20. package/bin/lib/cli-helpers.js +140 -0
  21. package/bin/lib/token-helpers.d.ts +14 -0
  22. package/bin/lib/token-helpers.d.ts.map +1 -0
  23. package/bin/lib/token-helpers.js +151 -0
  24. package/bin/refresh-token.d.ts +3 -0
  25. package/bin/refresh-token.d.ts.map +1 -0
  26. package/bin/refresh-token.js +168 -0
  27. package/bin/token-info.d.ts +3 -0
  28. package/bin/token-info.d.ts.map +1 -0
  29. package/bin/token-info.js +351 -0
  30. package/index.d.ts +218 -0
  31. package/index.d.ts.map +1 -0
  32. package/index.js +689 -0
  33. package/lib/api-endpoints/auth.d.ts +56 -0
  34. package/lib/api-endpoints/auth.d.ts.map +1 -0
  35. package/lib/api-endpoints/auth.js +209 -0
  36. package/lib/api-endpoints/auth.test.js +27 -0
  37. package/lib/api-endpoints/constants.d.ts +6 -0
  38. package/lib/api-endpoints/constants.d.ts.map +1 -0
  39. package/lib/api-endpoints/constants.js +31 -0
  40. package/lib/api-endpoints/content.d.ts +275 -0
  41. package/lib/api-endpoints/content.d.ts.map +1 -0
  42. package/lib/api-endpoints/content.js +518 -0
  43. package/lib/api-endpoints/content.test.js +250 -0
  44. package/lib/api-endpoints/devices.d.ts +202 -0
  45. package/lib/api-endpoints/devices.d.ts.map +1 -0
  46. package/lib/api-endpoints/devices.js +404 -0
  47. package/lib/api-endpoints/devices.test.js +483 -0
  48. package/lib/api-endpoints/family-library-groups.d.ts +75 -0
  49. package/lib/api-endpoints/family-library-groups.d.ts.map +1 -0
  50. package/lib/api-endpoints/family-library-groups.js +247 -0
  51. package/lib/api-endpoints/family-library-groups.test.js +272 -0
  52. package/lib/api-endpoints/family.d.ts +39 -0
  53. package/lib/api-endpoints/family.d.ts.map +1 -0
  54. package/lib/api-endpoints/family.js +166 -0
  55. package/lib/api-endpoints/family.test.js +184 -0
  56. package/lib/api-endpoints/helpers.d.ts +29 -0
  57. package/lib/api-endpoints/helpers.d.ts.map +1 -0
  58. package/lib/api-endpoints/helpers.js +104 -0
  59. package/lib/api-endpoints/icons.d.ts +62 -0
  60. package/lib/api-endpoints/icons.d.ts.map +1 -0
  61. package/lib/api-endpoints/icons.js +201 -0
  62. package/lib/api-endpoints/icons.test.js +118 -0
  63. package/lib/api-endpoints/media.d.ts +37 -0
  64. package/lib/api-endpoints/media.d.ts.map +1 -0
  65. package/lib/api-endpoints/media.js +155 -0
  66. package/lib/api-endpoints/test-helpers.d.ts +7 -0
  67. package/lib/api-endpoints/test-helpers.d.ts.map +1 -0
  68. package/lib/api-endpoints/test-helpers.js +64 -0
  69. package/lib/mqtt/client.d.ts +124 -0
  70. package/lib/mqtt/client.d.ts.map +1 -0
  71. package/lib/mqtt/client.js +558 -0
  72. package/lib/mqtt/commands.d.ts +69 -0
  73. package/lib/mqtt/commands.d.ts.map +1 -0
  74. package/lib/mqtt/commands.js +238 -0
  75. package/lib/mqtt/factory.d.ts +12 -0
  76. package/lib/mqtt/factory.d.ts.map +1 -0
  77. package/lib/mqtt/factory.js +107 -0
  78. package/lib/mqtt/index.d.ts +5 -0
  79. package/lib/mqtt/index.d.ts.map +1 -0
  80. package/lib/mqtt/index.js +81 -0
  81. package/lib/mqtt/mqtt.test.js +168 -0
  82. package/lib/mqtt/topics.d.ts +34 -0
  83. package/lib/mqtt/topics.d.ts.map +1 -0
  84. package/lib/mqtt/topics.js +295 -0
  85. package/lib/pkg.cjs +3 -0
  86. package/lib/pkg.d.cts +70 -0
  87. package/lib/pkg.d.cts.map +1 -0
  88. package/lib/token.d.ts +29 -0
  89. package/lib/token.d.ts.map +1 -0
  90. package/lib/token.js +240 -0
  91. package/package.json +91 -0
  92. package/yoto.png +0 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @import { RequestOptions } from './helpers.js'
3
+ */
4
+ import { request } from 'undici'
5
+ import { defaultAuthHeaders, handleBadResponse, mergeRequestOptions } from './helpers.js'
6
+ import { YOTO_API_URL } from './constants.js'
7
+
8
+ // ============================================================================
9
+ // Family: Family endpoints for managing family groups and images
10
+ // ============================================================================
11
+
12
+ /**
13
+ * @see https://yoto.dev/api/getfamilyimages/
14
+ * @typedef {Object} YotoFamilyImagesResponse
15
+ * @property {YotoFamilyImage[]} images
16
+ */
17
+
18
+ /**
19
+ * @see https://yoto.dev/api/getfamilyimages/
20
+ * @typedef {Object} YotoFamilyImage
21
+ * @property {string} imageId - The unique identifier for the family image (hash)
22
+ * @property {string} [name] - Optional name of the family image
23
+ */
24
+
25
+ /**
26
+ * Retrieves the list of families associated with the authenticated user.
27
+ * @see https://yoto.dev/api/getfamilyimages/
28
+ * @param {object} options
29
+ * @param {string} options.accessToken The API token to request with
30
+ * @param {string} [options.userAgent] Optional user agent string
31
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
32
+ * @return {Promise<YotoFamilyImagesResponse>} The user's families
33
+ */
34
+ export async function getFamilyImages ({
35
+ accessToken,
36
+ userAgent,
37
+ requestOptions
38
+ }) {
39
+ const requestUrl = new URL('/media/family/images', YOTO_API_URL)
40
+
41
+ const response = await request(requestUrl, mergeRequestOptions({
42
+ method: 'GET',
43
+ headers: defaultAuthHeaders({ accessToken, userAgent })
44
+ }, requestOptions))
45
+
46
+ await handleBadResponse(response)
47
+
48
+ const responseBody = /** @type {YotoFamilyImagesResponse} */ (await response.body.json())
49
+ return responseBody
50
+ }
51
+
52
+ /**
53
+ * @see https://yoto.dev/api/getafamilyimage/
54
+ * @typedef {Object} YotoFamilyImageResponse
55
+ * @property {string} imageUrl - The signed URL to the family image (expires after 7 days)
56
+ */
57
+
58
+ /**
59
+ * Retrieves a signed URL for a specific family image. Returns a 302 redirect with the image URL in the Location header.
60
+ * The signed URL expires after 7 days.
61
+ * @see https://yoto.dev/api/getafamilyimage/
62
+ * @param {object} options
63
+ * @param {string} options.accessToken The API token to request with
64
+ * @param {string} options.imageId The family image ID (hash) to get the image for
65
+ * @param {'640x480' | '320x320'} options.size Image dimensions (supported: '640x480' or '320x320')
66
+ * @param {string} [options.userAgent] Optional user agent string
67
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
68
+ * @return {Promise<YotoFamilyImageResponse>} The signed image URL
69
+ */
70
+ export async function getAFamilyImage ({
71
+ accessToken,
72
+ userAgent,
73
+ imageId,
74
+ size,
75
+ requestOptions
76
+ }) {
77
+ const requestUrl = new URL(`/media/family/images/${imageId}`, YOTO_API_URL)
78
+
79
+ // Map size string to width and height
80
+ const dimensions = size === '640x480' ? { width: 640, height: 480 } : { width: 320, height: 320 }
81
+
82
+ requestUrl.searchParams.set('width', dimensions.width.toString())
83
+ requestUrl.searchParams.set('height', dimensions.height.toString())
84
+
85
+ const response = await request(requestUrl, mergeRequestOptions({
86
+ method: 'GET',
87
+ headers: defaultAuthHeaders({ accessToken, userAgent })
88
+ }, requestOptions))
89
+
90
+ // 302 is expected for successful image requests
91
+ if (response.statusCode !== 302) {
92
+ await handleBadResponse(response, { imageId })
93
+ }
94
+
95
+ // Get the signed URL from the Location header
96
+ const locationHeaders = response.headers['location']
97
+ const imageUrl = Array.isArray(locationHeaders) ? locationHeaders[0] : locationHeaders
98
+
99
+ if (!imageUrl) {
100
+ throw new Error('No Location header found in 302 response')
101
+ }
102
+
103
+ return {
104
+ imageUrl
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @see https://yoto.dev/api/uploadafamilyimage/
110
+ * @typedef {Object} YotoUploadFamilyImageResponse
111
+ * @property {string} imageId - The SHA256 checksum of the uploaded image
112
+ * @property {string} url - URL to the 'get a family image' endpoint (requires width/height params)
113
+ */
114
+
115
+ /**
116
+ * Uploads a family image for use across various features in Yoto.
117
+ * Images are deduplicated using SHA256 checksums.
118
+ *
119
+ * Constraints:
120
+ * - Max size: 8mb
121
+ * - Supported formats: JPEG, GIF, PNG
122
+ * - Limit: 500 images per family
123
+ * - No restrictions on resolution or aspect ratio
124
+ *
125
+ * @see https://yoto.dev/api/uploadafamilyimage/
126
+ * @param {object} options
127
+ * @param {string} options.accessToken The API token to request with
128
+ * @param {Buffer} options.imageData The binary image data (JPEG, GIF, or PNG)
129
+ * @param {string} [options.userAgent] Optional user agent string
130
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
131
+ * @return {Promise<YotoUploadFamilyImageResponse>} The uploaded image details
132
+ * @example
133
+ * import { readFile } from 'fs/promises'
134
+ * import { uploadAFamilyImage } from 'yoto-nodejs-client'
135
+ *
136
+ * const imageData = await readFile('./family-photo.jpg')
137
+ * const result = await uploadAFamilyImage({
138
+ * accessToken,
139
+ * imageData
140
+ * })
141
+ *
142
+ * console.log('Image ID:', result.imageId)
143
+ * console.log('Image URL:', result.url)
144
+ */
145
+ export async function uploadAFamilyImage ({
146
+ accessToken,
147
+ userAgent,
148
+ imageData,
149
+ requestOptions
150
+ }) {
151
+ const requestUrl = new URL('/media/family/images', YOTO_API_URL)
152
+
153
+ const response = await request(requestUrl, mergeRequestOptions({
154
+ method: 'POST',
155
+ headers: {
156
+ ...defaultAuthHeaders({ accessToken, userAgent }),
157
+ 'Content-Type': 'application/octet-stream'
158
+ },
159
+ body: imageData
160
+ }, requestOptions))
161
+
162
+ await handleBadResponse(response)
163
+
164
+ const responseBody = /** @type {YotoUploadFamilyImageResponse} */ (await response.body.json())
165
+ return responseBody
166
+ }
@@ -0,0 +1,184 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { getFamilyImages, getAFamilyImage } from './family.js'
4
+ import { YotoAPIError } from './helpers.js'
5
+ import { loadTestTokens, logResponse } from './test-helpers.js'
6
+
7
+ const { accessToken } = loadTestTokens()
8
+
9
+ test('getFamilyImages', async (t) => {
10
+ await t.test('should fetch user family images', async () => {
11
+ const response = await getFamilyImages({
12
+ accessToken
13
+ })
14
+
15
+ // Log response for type verification and documentation
16
+ logResponse('GET /media/family/images', response)
17
+
18
+ // Validate response structure matches YotoFamilyImagesResponse
19
+ assert.ok(response, 'Response should exist')
20
+ assert.ok(Array.isArray(response.images), 'Response should have images array')
21
+
22
+ // Note: User may not have any family images, so we only validate structure if images exist
23
+ if (response.images.length > 0) {
24
+ const image = response.images[0]
25
+ assert.ok(image, 'Image should exist')
26
+ assert.ok(typeof image.imageId === 'string', 'Image should have imageId string')
27
+ }
28
+ })
29
+
30
+ await t.test('should fail with invalid token', async () => {
31
+ await assert.rejects(
32
+ async () => {
33
+ await getFamilyImages({
34
+ accessToken: 'invalid-token'
35
+ })
36
+ },
37
+ (err) => {
38
+ assert.ok(err instanceof YotoAPIError, 'Should throw YotoAPIError')
39
+ assert.ok(err.statusCode === 401 || err.statusCode === 403, 'Should return 401 or 403 for invalid token')
40
+ assert.ok(err.body, 'Error should have body')
41
+ return true
42
+ }
43
+ )
44
+ })
45
+ })
46
+
47
+ test('getAFamilyImage', async (t) => {
48
+ /** @type {string[]} */
49
+ let testImageIds = []
50
+
51
+ // Get real image IDs to test with
52
+ await t.test('setup - get image IDs from family images', async () => {
53
+ const response = await getFamilyImages({
54
+ accessToken
55
+ })
56
+
57
+ if (response.images.length === 0) {
58
+ console.log('⚠️ User has no family images - getAFamilyImage tests will be skipped')
59
+ return
60
+ }
61
+
62
+ testImageIds = response.images.slice(0, 2).map(image => image.imageId).filter(Boolean)
63
+ assert.ok(testImageIds.length > 0, 'Should have extracted at least one image ID')
64
+ })
65
+
66
+ await t.test('should fetch family image signed URL', async () => {
67
+ if (testImageIds.length === 0) {
68
+ console.log('⚠️ Skipping - no family images available')
69
+ return
70
+ }
71
+
72
+ const imageId = testImageIds[0]
73
+ assert.ok(imageId, 'Image ID should exist')
74
+
75
+ const response = await getAFamilyImage({
76
+ accessToken,
77
+ imageId,
78
+ size: '640x480'
79
+ })
80
+
81
+ // Log response for type verification and documentation
82
+ logResponse('GET /media/family/images/{imageId}', response)
83
+
84
+ // Validate response structure matches YotoFamilyImageResponse
85
+ assert.ok(response, 'Response should exist')
86
+ assert.ok(typeof response.imageUrl === 'string', 'Response should have imageUrl string')
87
+ assert.ok(response.imageUrl.startsWith('http'), 'Image URL should be a valid URL')
88
+ })
89
+
90
+ await t.test('should fetch family image with size 640x480', async () => {
91
+ if (testImageIds.length === 0) {
92
+ console.log('⚠️ Skipping - no family images available')
93
+ return
94
+ }
95
+
96
+ const imageId = testImageIds[0]
97
+ assert.ok(imageId, 'Image ID should exist')
98
+
99
+ const response = await getAFamilyImage({
100
+ accessToken,
101
+ imageId,
102
+ size: '640x480'
103
+ })
104
+
105
+ assert.ok(response, 'Response should exist')
106
+ assert.ok(typeof response.imageUrl === 'string', 'Response should have imageUrl string')
107
+ assert.ok(response.imageUrl.startsWith('http'), 'Image URL should be a valid URL')
108
+ })
109
+
110
+ await t.test('should fetch family image with size 320x320', async () => {
111
+ if (testImageIds.length === 0) {
112
+ console.log('⚠️ Skipping - no family images available')
113
+ return
114
+ }
115
+
116
+ const imageId = testImageIds[0]
117
+ assert.ok(imageId, 'Image ID should exist')
118
+
119
+ const response = await getAFamilyImage({
120
+ accessToken,
121
+ imageId,
122
+ size: '320x320'
123
+ })
124
+
125
+ assert.ok(response, 'Response should exist')
126
+ assert.ok(typeof response.imageUrl === 'string', 'Response should have imageUrl string')
127
+ assert.ok(response.imageUrl.startsWith('http'), 'Image URL should be a valid URL')
128
+ })
129
+
130
+ await t.test('should fail with invalid image ID', async () => {
131
+ await assert.rejects(
132
+ async () => {
133
+ await getAFamilyImage({
134
+ accessToken,
135
+ imageId: 'invalid-image-id-12345',
136
+ size: '640x480'
137
+ })
138
+ },
139
+ (err) => {
140
+ assert.ok(err instanceof YotoAPIError, 'Should throw YotoAPIError')
141
+ assert.ok(err.statusCode === 404 || err.statusCode === 400, 'Should return 404 or 400 for invalid image ID')
142
+ return true
143
+ }
144
+ )
145
+ })
146
+
147
+ await t.test('should fail with invalid token', async () => {
148
+ if (testImageIds.length === 0) {
149
+ console.log('⚠️ Skipping - no family images available')
150
+ return
151
+ }
152
+
153
+ const imageId = testImageIds[0]
154
+ assert.ok(imageId, 'Image ID should exist')
155
+
156
+ await assert.rejects(
157
+ async () => {
158
+ await getAFamilyImage({
159
+ accessToken: 'invalid-token',
160
+ imageId,
161
+ size: '640x480'
162
+ })
163
+ },
164
+ (err) => {
165
+ assert.ok(err instanceof YotoAPIError, 'Should throw YotoAPIError')
166
+ assert.ok(err.statusCode === 401 || err.statusCode === 403, 'Should return 401 or 403 for invalid token')
167
+ assert.ok(err.body, 'Error should have body')
168
+ return true
169
+ }
170
+ )
171
+ })
172
+ })
173
+
174
+ // TODO: Add tests for uploadAFamilyImage
175
+ // - should upload a valid JPEG image
176
+ // - should upload a valid PNG image
177
+ // - should upload a valid GIF image
178
+ // - should return imageId (SHA256 checksum) and url
179
+ // - should deduplicate identical images (same SHA256)
180
+ // - should fail with file larger than 8mb
181
+ // - should fail with unsupported image format
182
+ // - should fail when family has reached 500 image limit
183
+ // - should fail with invalid token
184
+ // - should validate response structure matches YotoUploadFamilyImageResponse
@@ -0,0 +1,29 @@
1
+ export function defaultHeaders(options?: {
2
+ userAgent?: string | undefined;
3
+ requestOptions?: ({
4
+ dispatcher?: Dispatcher;
5
+ } & Omit<Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<Dispatcher.RequestOptions<null>, "method">>) | undefined;
6
+ }): {
7
+ Accept: string;
8
+ 'User-Agent': string;
9
+ };
10
+ export function defaultAuthHeaders({ accessToken: token, userAgent }: {
11
+ accessToken: string;
12
+ userAgent?: string | undefined;
13
+ }): {
14
+ Authorization: string;
15
+ Accept: string;
16
+ 'User-Agent': string;
17
+ };
18
+ export function mergeRequestOptions(baseOptions: RequestOptions, requestOptions?: RequestOptions): object;
19
+ export function handleBadResponse(response: Dispatcher.ResponseData, extra?: any): Promise<void>;
20
+ export class YotoAPIError extends Error {
21
+ constructor(response: Dispatcher.ResponseData, body: string | object, extra?: any);
22
+ statusCode: number;
23
+ body: string | object;
24
+ extra: any;
25
+ }
26
+ export type RequestOptions = NonNullable<Parameters<typeof request>[1]>;
27
+ import type { Dispatcher } from 'undici';
28
+ import type { request } from 'undici';
29
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["helpers.js"],"names":[],"mappings":"AAiBA,yCAHG;IAAyB,SAAS;IACD,cAAc;;;CACjD;;;EAWA;AAOD,sEAHG;IAAwB,WAAW,EAA1B,MAAM;IACS,SAAS;CACnC;;;;EAMA;AAUD,iDAJW,cAAc,mBACd,cAAc,GACZ,MAAM,CAoBlB;AAMD,4CAHY,uBAAuB,UACvB,GAAG,iBAWd;AAED;IAUE,sBAJY,uBAAuB,QACvB,MAAM,GAAG,MAAM,UACf,GAAG,EAUd;IAjBuB,YAAZ,MAAM,CAAgB;IACF,MAArB,MAAM,GAAG,MAAM,CAAU;IACjB,OAAR,GAAG,CAAU;CAgBzB;6BA9FY,WAAW,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;gCARxB,QAAQ;6BACX,QAAQ"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @import { Dispatcher } from 'undici'
3
+ * @import { request } from 'undici'
4
+ */
5
+ import { pkg } from '../pkg.cjs'
6
+ import os from 'node:os'
7
+
8
+ /**
9
+ * Request options derived from undici.request
10
+ * @typedef {NonNullable<Parameters<typeof request>[1]>} RequestOptions
11
+ */
12
+
13
+ /**
14
+ * @param {object} [options]
15
+ * @param {string} [options.userAgent] - Optional user agent string to prepend to library user agent
16
+ * @param {RequestOptions} [options.requestOptions] - Additional undici request options
17
+ */
18
+ export function defaultHeaders (options = {}) {
19
+ const libraryAgent = `${pkg.name}/${pkg.version} (${os.type()})`
20
+ const userAgent = options.userAgent
21
+ ? `${options.userAgent} ${libraryAgent}`
22
+ : libraryAgent
23
+
24
+ return {
25
+ Accept: 'application/json',
26
+ 'User-Agent': userAgent
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @param {object} params
32
+ * @param {string} params.accessToken
33
+ * @param {string} [params.userAgent] - Optional user agent string to prepend to library user agent
34
+ */
35
+ export function defaultAuthHeaders ({ accessToken: token, userAgent }) {
36
+ return {
37
+ ...defaultHeaders({ userAgent }),
38
+ Authorization: `Bearer ${token}`,
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Merge undici request options with defaults
44
+ * Properly merges headers by supplementing rather than overriding
45
+ * Only supports object headers (not arrays)
46
+ * @param {RequestOptions} baseOptions - Base request options with headers
47
+ * @param {RequestOptions} [requestOptions] - Additional request options to merge
48
+ * @returns {object} Merged options
49
+ */
50
+ export function mergeRequestOptions (baseOptions, requestOptions) {
51
+ if (!requestOptions) return baseOptions
52
+
53
+ // Extract headers from both options
54
+ const { headers: baseHeaders, ...baseRest } = baseOptions
55
+ const { headers: requestHeaders, ...requestRest } = requestOptions
56
+
57
+ // Merge headers - only support object headers
58
+ const mergedHeaders = {
59
+ ...(baseHeaders || {}),
60
+ ...(requestHeaders || {})
61
+ }
62
+
63
+ return {
64
+ ...baseRest,
65
+ ...requestRest,
66
+ headers: mergedHeaders
67
+ }
68
+ }
69
+
70
+ /**
71
+ * @param {Dispatcher.ResponseData} response
72
+ * @param {any} [extra]
73
+ */
74
+ export async function handleBadResponse (response, extra) {
75
+ if (response.statusCode > 299) {
76
+ const contentTypeHeaders = response.headers['Content-Type']
77
+ const contentType = Array.isArray(contentTypeHeaders) ? contentTypeHeaders[0] : contentTypeHeaders
78
+ const isJSON = contentType && contentType.match(/json/)
79
+ /** @type { any } */
80
+ const body = isJSON ? await response.body.json() : await response.body.text()
81
+ throw new YotoAPIError(response, body, extra)
82
+ }
83
+ }
84
+
85
+ export class YotoAPIError extends Error {
86
+ /** @type { number } */ statusCode
87
+ /** @type {string | object } */ body
88
+ /** @type {any} */ extra
89
+
90
+ /**
91
+ * @param {Dispatcher.ResponseData} response A undici Response
92
+ * @param {string | object} body response body
93
+ * @param {any} [extra] any extra info to attach to the error
94
+ */
95
+ constructor (response, body, extra) {
96
+ super('Unexpected response status code')
97
+ this.name = this.constructor.name
98
+ Error.captureStackTrace(this, this.constructor)
99
+
100
+ this.statusCode = response.statusCode
101
+ this.body = body
102
+ this.extra = extra
103
+ }
104
+ }
@@ -0,0 +1,62 @@
1
+ export function getPublicIcons({ accessToken, userAgent, requestOptions }: {
2
+ accessToken: string;
3
+ userAgent?: string | undefined;
4
+ requestOptions?: ({
5
+ dispatcher?: import("undici").Dispatcher;
6
+ } & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
7
+ }): Promise<YotoPublicIconsResponse>;
8
+ export function getUserIcons({ accessToken, userAgent, requestOptions }: {
9
+ accessToken: string;
10
+ userAgent?: string | undefined;
11
+ requestOptions?: ({
12
+ dispatcher?: import("undici").Dispatcher;
13
+ } & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
14
+ }): Promise<YotoUserIconsResponse>;
15
+ export function uploadIcon({ accessToken, userAgent, imageData, autoConvert, filename, requestOptions }: {
16
+ accessToken: string;
17
+ imageData: Buffer;
18
+ autoConvert?: boolean | undefined;
19
+ filename?: string | undefined;
20
+ userAgent?: string | undefined;
21
+ requestOptions?: ({
22
+ dispatcher?: import("undici").Dispatcher;
23
+ } & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
24
+ }): Promise<YotoUploadIconResponse>;
25
+ export type YotoPublicIconsResponse = {
26
+ displayIcons: YotoPublicIcon[];
27
+ };
28
+ export type YotoPublicIcon = {
29
+ displayIconId: string;
30
+ mediaId: string;
31
+ userId: string;
32
+ createdAt: string;
33
+ title: string;
34
+ url: string;
35
+ public: boolean;
36
+ new?: boolean;
37
+ publicTags: string[];
38
+ };
39
+ export type YotoUserIconsResponse = {
40
+ displayIcons: YotoUserIcon[];
41
+ };
42
+ export type YotoUserIcon = {
43
+ displayIconId: string;
44
+ mediaId: string;
45
+ userId: string;
46
+ createdAt: string;
47
+ url: string;
48
+ public: boolean;
49
+ };
50
+ export type YotoUploadIconResponse = {
51
+ displayIcon: YotoDisplayIcon;
52
+ };
53
+ export type YotoDisplayIcon = {
54
+ displayIconId: string;
55
+ mediaId: string;
56
+ userId: string;
57
+ url: string | object;
58
+ new?: boolean;
59
+ _id?: string;
60
+ createdAt?: string;
61
+ };
62
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["icons.js"],"names":[],"mappings":"AAmDA,2EAhBG;IAAyB,WAAW,EAA3B,MAAM;IACW,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,uBAAuB,CAAC,CA6B3C;AA4BD,yEALG;IAAyB,WAAW,EAA3B,MAAM;IACW,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,qBAAqB,CAAC,CAkBzC;AA+DD,yGAzBG;IAAyB,WAAW,EAA3B,MAAM;IACU,SAAS,EAAzB,MAAM;IACY,WAAW;IACZ,QAAQ;IACR,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,sBAAsB,CAAC,CA6C1C;;kBA1La,cAAc,EAAE;;;mBAMhB,MAAM;aACN,MAAM;YACN,MAAM;eACN,MAAM;WACN,MAAM;SACN,MAAM;YACN,OAAO;UACP,OAAO;gBACP,MAAM,EAAE;;;kBA4CR,YAAY,EAAE;;;mBAMd,MAAM;aACN,MAAM;YACN,MAAM;eACN,MAAM;SACN,MAAM;YACN,OAAO;;;iBAiCP,eAAe;;;mBAMf,MAAM;aACN,MAAM;YACN,MAAM;SACN,MAAM,GAAG,MAAM;UACf,OAAO;UACP,MAAM;gBACN,MAAM"}