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,247 @@
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 Library Groups: Endpoints for managing family library card groups
10
+ // ============================================================================
11
+
12
+ /**
13
+ * @see https://yoto.dev/api/getgroups/
14
+ * @typedef {Object} YotoGroupsResponse
15
+ * @property {YotoGroup[]} groups - Array of family library groups
16
+ */
17
+
18
+ /**
19
+ * @see https://yoto.dev/api/getgroups/
20
+ * @typedef {Object} YotoGroup
21
+ * @property {string} id - Group identifier
22
+ * @property {string} name - Group name (e.g., "My Favourites")
23
+ * @property {string} familyId - Associated family ID
24
+ * @property {string} imageId - ID for the group image (can be uploaded family image hash or preset like "fp-cards")
25
+ * @property {string} imageUrl - CDN URL to the group image
26
+ * @property {YotoGroupItem[]} items - Array of content items in the group
27
+ * @property {any[]} cards - Array of card objects
28
+ * @property {string} createdAt - ISO 8601 timestamp when group was created
29
+ * @property {string} lastModifiedAt - ISO 8601 timestamp when group was last updated
30
+ */
31
+
32
+ /**
33
+ * @see https://yoto.dev/api/getgroups/
34
+ * @typedef {Object} YotoGroupItem
35
+ * @property {string} contentId - ID of the card content
36
+ * @property {string} addedAt - ISO 8601 timestamp when item was added to group
37
+ */
38
+
39
+ /**
40
+ * Retrieves all family library groups for the authenticated user's family.
41
+ * Returns an empty array if no groups exist.
42
+ * @see https://yoto.dev/api/getgroups/
43
+ * @param {object} options
44
+ * @param {string} options.accessToken The API token to request with
45
+ * @param {string} [options.userAgent] Optional user agent string
46
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
47
+ * @return {Promise<YotoGroup[]>} Array of family library groups
48
+ * @example
49
+ * import { getGroups } from 'yoto-nodejs-client'
50
+ *
51
+ * const groups = await getGroups({
52
+ * accessToken
53
+ * })
54
+ *
55
+ * console.log('Groups:', groups.length)
56
+ * groups.forEach(group => {
57
+ * console.log(`${group.name}: ${group.items.length} items`)
58
+ * })
59
+ */
60
+ export async function getGroups ({
61
+ accessToken,
62
+ userAgent,
63
+ requestOptions
64
+ }) {
65
+ const requestUrl = new URL('/card/family/library/groups', YOTO_API_URL)
66
+
67
+ const response = await request(requestUrl, mergeRequestOptions({
68
+ method: 'GET',
69
+ headers: defaultAuthHeaders({ accessToken, userAgent })
70
+ }, requestOptions))
71
+
72
+ await handleBadResponse(response)
73
+
74
+ const responseBody = /** @type {YotoGroup[]} */ (await response.body.json())
75
+ return responseBody
76
+ }
77
+
78
+ /**
79
+ * @see https://yoto.dev/api/createagroup/
80
+ * @typedef {Object} YotoCreateGroupRequest
81
+ * @property {string} name - Group name (max 100 characters, UTF-8 supported)
82
+ * @property {string} imageId - Image ID (preset like "fp-cards" or uploaded image hash)
83
+ * @property {YotoGroupItemInput[]} items - Array of content items (can be empty, order preserved)
84
+ */
85
+
86
+ /**
87
+ * @see https://yoto.dev/api/createagroup/
88
+ * @typedef {Object} YotoGroupItemInput
89
+ * @property {string} contentId - ID of the card content to add to group
90
+ */
91
+
92
+ /**
93
+ * Creates a new group in the family library.
94
+ * Max 20 groups per family. ContentIds must be in family's library (invalid ones filtered out).
95
+ * @see https://yoto.dev/api/createagroup/
96
+ * @param {object} options
97
+ * @param {string} options.token The API token to request with
98
+ * @param {YotoCreateGroupRequest} options.group The group data to create
99
+ * @param {string} [options.userAgent] Optional user agent string
100
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
101
+ * @return {Promise<YotoGroup>} The created group with populated cards array
102
+ * @example
103
+ * import { createGroup } from 'yoto-nodejs-client'
104
+ *
105
+ * const group = await createGroup({
106
+ * token: accessToken,
107
+ * group: {
108
+ * name: 'My Favourites',
109
+ * imageId: 'fp-cards',
110
+ * items: [
111
+ * { contentId: '37KwQ' }
112
+ * ]
113
+ * }
114
+ * })
115
+ */
116
+ export async function createGroup ({
117
+ token,
118
+ userAgent,
119
+ group,
120
+ requestOptions
121
+ }) {
122
+ const requestUrl = new URL('/card/family/library/groups', YOTO_API_URL)
123
+
124
+ const response = await request(requestUrl, mergeRequestOptions({
125
+ method: 'POST',
126
+ headers: {
127
+ ...defaultAuthHeaders({ accessToken: token, userAgent }),
128
+ 'Content-Type': 'application/json'
129
+ },
130
+ body: JSON.stringify(group)
131
+ }, requestOptions))
132
+
133
+ await handleBadResponse(response)
134
+
135
+ const responseBody = /** @type {YotoGroup} */ (await response.body.json())
136
+ return responseBody
137
+ }
138
+
139
+ /**
140
+ * Retrieves a specific group by ID.
141
+ * Returns 404 if group doesn't exist or belongs to another family.
142
+ * @see https://yoto.dev/api/getagroup/
143
+ * @param {object} options
144
+ * @param {string} options.accessToken The API token to request with
145
+ * @param {string} options.groupId The group ID to retrieve
146
+ * @param {string} [options.userAgent] Optional user agent string
147
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
148
+ * @return {Promise<YotoGroup>} The requested group with populated cards array
149
+ */
150
+ export async function getGroup ({
151
+ accessToken,
152
+ userAgent,
153
+ groupId,
154
+ requestOptions
155
+ }) {
156
+ const requestUrl = new URL(`/card/family/library/groups/${groupId}`, YOTO_API_URL)
157
+
158
+ const response = await request(requestUrl, mergeRequestOptions({
159
+ method: 'GET',
160
+ headers: defaultAuthHeaders({ accessToken, userAgent })
161
+ }, requestOptions))
162
+
163
+ await handleBadResponse(response, { groupId })
164
+
165
+ const responseBody = /** @type {YotoGroup} */ (await response.body.json())
166
+ return responseBody
167
+ }
168
+
169
+ /**
170
+ * @see https://yoto.dev/api/updateagroup/
171
+ * @typedef {Object} YotoUpdateGroupRequest
172
+ * @property {string} name - Group name (max 100 characters, UTF-8 supported)
173
+ * @property {string} imageId - Image ID (preset like "fp-cards" or uploaded image hash)
174
+ * @property {YotoGroupItemInput[]} items - Array of content items (replaces entire array)
175
+ */
176
+
177
+ /**
178
+ * Updates an existing group.
179
+ * Can only update groups owned by family. Returns 404 if doesn't exist or owned by another family.
180
+ * @see https://yoto.dev/api/updateagroup/
181
+ * @param {object} options
182
+ * @param {string} options.accessToken The API token to request with
183
+ * @param {string} options.groupId The group ID to update
184
+ * @param {YotoUpdateGroupRequest} options.group The updated group data
185
+ * @param {string} [options.userAgent] Optional user agent string
186
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
187
+ * @return {Promise<YotoGroup>} The updated group with populated cards array
188
+ */
189
+ export async function updateGroup ({
190
+ accessToken,
191
+ userAgent,
192
+ groupId,
193
+ group,
194
+ requestOptions
195
+ }) {
196
+ const requestUrl = new URL(`/card/family/library/groups/${groupId}`, YOTO_API_URL)
197
+
198
+ const response = await request(requestUrl, mergeRequestOptions({
199
+ method: 'PUT',
200
+ headers: {
201
+ ...defaultAuthHeaders({ accessToken, userAgent }),
202
+ 'Content-Type': 'application/json'
203
+ },
204
+ body: JSON.stringify(group)
205
+ }, requestOptions))
206
+
207
+ await handleBadResponse(response, { groupId })
208
+
209
+ const responseBody = /** @type {YotoGroup} */ (await response.body.json())
210
+ return responseBody
211
+ }
212
+
213
+ /**
214
+ * @see https://yoto.dev/api/deleteagroup/
215
+ * @typedef {Object} YotoDeleteGroupResponse
216
+ * @property {string} id - The ID of the deleted group
217
+ */
218
+
219
+ /**
220
+ * Deletes a group permanently (hard delete, cannot be recovered).
221
+ * Content remains in family library. Returns 404 if doesn't exist or owned by another family.
222
+ * @see https://yoto.dev/api/deleteagroup/
223
+ * @param {object} options
224
+ * @param {string} options.accessToken The API token to request with
225
+ * @param {string} options.groupId The group ID to delete
226
+ * @param {string} [options.userAgent] Optional user agent string
227
+ * @param {RequestOptions} [options.requestOptions] Additional undici request options
228
+ * @return {Promise<YotoDeleteGroupResponse>} Confirmation with deleted group ID
229
+ */
230
+ export async function deleteGroup ({
231
+ accessToken,
232
+ userAgent,
233
+ groupId,
234
+ requestOptions
235
+ }) {
236
+ const requestUrl = new URL(`/card/family/library/groups/${groupId}`, YOTO_API_URL)
237
+
238
+ const response = await request(requestUrl, mergeRequestOptions({
239
+ method: 'DELETE',
240
+ headers: defaultAuthHeaders({ accessToken, userAgent })
241
+ }, requestOptions))
242
+
243
+ await handleBadResponse(response, { groupId })
244
+
245
+ const responseBody = /** @type {YotoDeleteGroupResponse} */ (await response.body.json())
246
+ return responseBody
247
+ }
@@ -0,0 +1,272 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { getGroups, createGroup, getGroup, updateGroup, deleteGroup } from './family-library-groups.js'
4
+ import { YotoAPIError } from './helpers.js'
5
+ import { loadTestTokens, logResponse } from './test-helpers.js'
6
+
7
+ const { accessToken } = loadTestTokens()
8
+
9
+ test('getGroups', async (t) => {
10
+ await t.test('should fetch family library groups', async () => {
11
+ const response = await getGroups({
12
+ accessToken
13
+ })
14
+
15
+ // Log response for type verification and documentation
16
+ logResponse('GET /card/family/library/groups', response)
17
+
18
+ // Validate response structure matches YotoGroup[]
19
+ assert.ok(response, 'Response should exist')
20
+ assert.ok(Array.isArray(response), 'Response should be an array')
21
+
22
+ // Note: User may not have any groups, so we only validate structure if groups exist
23
+ if (response.length > 0) {
24
+ const group = response[0]
25
+ assert.ok(group, 'Group should exist')
26
+ assert.ok(typeof group.id === 'string', 'Group should have id string')
27
+ assert.ok(typeof group.name === 'string', 'Group should have name string')
28
+ assert.ok(typeof group.familyId === 'string', 'Group should have familyId string')
29
+ assert.ok(typeof group.imageId === 'string', 'Group should have imageId string')
30
+ assert.ok(typeof group.imageUrl === 'string', 'Group should have imageUrl string')
31
+ assert.ok(Array.isArray(group.items), 'Group should have items array')
32
+ assert.ok(Array.isArray(group.cards), 'Group should have cards array')
33
+ assert.ok(typeof group.createdAt === 'string', 'Group should have createdAt string')
34
+ assert.ok(typeof group.lastModifiedAt === 'string', 'Group should have lastModifiedAt string')
35
+
36
+ // Validate items structure if items exist
37
+ if (group.items.length > 0) {
38
+ const item = group.items[0]
39
+ assert.ok(item, 'Item should exist')
40
+ assert.ok(typeof item.contentId === 'string', 'Item should have contentId string')
41
+ assert.ok(typeof item.addedAt === 'string', 'Item should have addedAt string')
42
+ }
43
+ }
44
+ })
45
+
46
+ await t.test('should fail with invalid token', async () => {
47
+ await assert.rejects(
48
+ async () => {
49
+ await getGroups({
50
+ accessToken: 'invalid-token'
51
+ })
52
+ },
53
+ (err) => {
54
+ assert.ok(err instanceof YotoAPIError, 'Should throw YotoAPIError')
55
+ assert.ok(err.statusCode === 401 || err.statusCode === 403, 'Should return 401 or 403 for invalid token')
56
+ assert.ok(err.body, 'Error should have body')
57
+ return true
58
+ }
59
+ )
60
+ })
61
+ })
62
+
63
+ test('CRUD operations - full lifecycle', async (t) => {
64
+ /** @type {string[]} */
65
+ let createdGroupIds = []
66
+ /** @type {string} */
67
+ let group1Id
68
+ /** @type {string} */
69
+ let group2Id
70
+
71
+ // Cleanup function to run after all tests
72
+ t.after(async () => {
73
+ console.log('\n🧹 Cleaning up test groups...')
74
+ for (const groupId of createdGroupIds) {
75
+ try {
76
+ await deleteGroup({ accessToken, groupId })
77
+ console.log(` ✓ Deleted group: ${groupId}`)
78
+ } catch (err) {
79
+ console.log(` ⚠️ Failed to delete group ${groupId}:`, /** @type {Error} */(err).message)
80
+ }
81
+ }
82
+ })
83
+
84
+ await t.test('should create first group', async () => {
85
+ const group = await createGroup({
86
+ token: accessToken,
87
+ group: {
88
+ name: 'Test Group 1',
89
+ imageId: 'fp-cards',
90
+ items: []
91
+ }
92
+ })
93
+
94
+ logResponse('POST /card/family/library/groups (group 1)', group)
95
+
96
+ // Validate response structure
97
+ assert.ok(group, 'Response should exist')
98
+ assert.ok(typeof group.id === 'string', 'Group should have id string')
99
+ assert.strictEqual(group.name, 'Test Group 1', 'Group name should match')
100
+ assert.strictEqual(group.imageId, 'fp-cards', 'Image ID should match')
101
+ assert.ok(Array.isArray(group.items), 'Group should have items array')
102
+ assert.strictEqual(group.items.length, 0, 'Items should be empty')
103
+ assert.ok(Array.isArray(group.cards), 'Group should have cards array')
104
+ assert.ok(typeof group.familyId === 'string', 'Group should have familyId')
105
+ assert.ok(typeof group.createdAt === 'string', 'Group should have createdAt')
106
+ assert.ok(typeof group.lastModifiedAt === 'string', 'Group should have lastModifiedAt')
107
+
108
+ group1Id = group.id
109
+ createdGroupIds.push(group.id)
110
+ })
111
+
112
+ await t.test('should create second group', async () => {
113
+ const group = await createGroup({
114
+ token: accessToken,
115
+ group: {
116
+ name: 'Test Group 2',
117
+ imageId: 'fp-cards',
118
+ items: []
119
+ }
120
+ })
121
+
122
+ logResponse('POST /card/family/library/groups (group 2)', group)
123
+
124
+ // Validate response structure
125
+ assert.ok(group, 'Response should exist')
126
+ assert.ok(typeof group.id === 'string', 'Group should have id string')
127
+ assert.strictEqual(group.name, 'Test Group 2', 'Group name should match')
128
+ assert.notStrictEqual(group.id, group1Id, 'Group IDs should be different')
129
+
130
+ group2Id = group.id
131
+ createdGroupIds.push(group.id)
132
+ })
133
+
134
+ await t.test('should list groups and find both created groups', async () => {
135
+ const groups = await getGroups({
136
+ accessToken
137
+ })
138
+
139
+ logResponse('GET /card/family/library/groups (after creating 2)', groups)
140
+
141
+ assert.ok(Array.isArray(groups), 'Response should be an array')
142
+ assert.ok(groups.length >= 2, 'Should have at least 2 groups')
143
+
144
+ const foundGroup1 = groups.find(g => g.id === group1Id)
145
+ const foundGroup2 = groups.find(g => g.id === group2Id)
146
+
147
+ assert.ok(foundGroup1, 'Should find first created group in list')
148
+ assert.strictEqual(foundGroup1.name, 'Test Group 1', 'First group name should match')
149
+
150
+ assert.ok(foundGroup2, 'Should find second created group in list')
151
+ assert.strictEqual(foundGroup2.name, 'Test Group 2', 'Second group name should match')
152
+ })
153
+
154
+ await t.test('should get first group by ID', async () => {
155
+ const group = await getGroup({
156
+ accessToken,
157
+ groupId: group1Id
158
+ })
159
+
160
+ logResponse(`GET /card/family/library/groups/${group1Id}`, group)
161
+
162
+ assert.ok(group, 'Response should exist')
163
+ assert.strictEqual(group.id, group1Id, 'Group ID should match')
164
+ assert.strictEqual(group.name, 'Test Group 1', 'Group name should match')
165
+ assert.strictEqual(group.imageId, 'fp-cards', 'Image ID should match')
166
+ assert.ok(Array.isArray(group.items), 'Group should have items array')
167
+ assert.ok(Array.isArray(group.cards), 'Group should have cards array')
168
+ })
169
+
170
+ await t.test('should get second group by ID', async () => {
171
+ const group = await getGroup({
172
+ accessToken,
173
+ groupId: group2Id
174
+ })
175
+
176
+ logResponse(`GET /card/family/library/groups/${group2Id}`, group)
177
+
178
+ assert.ok(group, 'Response should exist')
179
+ assert.strictEqual(group.id, group2Id, 'Group ID should match')
180
+ assert.strictEqual(group.name, 'Test Group 2', 'Group name should match')
181
+ })
182
+
183
+ await t.test('should update first group', async () => {
184
+ const updatedGroup = await updateGroup({
185
+ accessToken,
186
+ groupId: group1Id,
187
+ group: {
188
+ name: 'Test Group 1 - Updated',
189
+ imageId: 'fp-cards',
190
+ items: []
191
+ }
192
+ })
193
+
194
+ logResponse(`PUT /card/family/library/groups/${group1Id}`, updatedGroup)
195
+
196
+ assert.ok(updatedGroup, 'Response should exist')
197
+ assert.strictEqual(updatedGroup.id, group1Id, 'Group ID should remain the same')
198
+ assert.strictEqual(updatedGroup.name, 'Test Group 1 - Updated', 'Group name should be updated')
199
+ assert.ok(typeof updatedGroup.lastModifiedAt === 'string', 'Should have lastModifiedAt')
200
+ })
201
+
202
+ await t.test('should get updated group and confirm changes', async () => {
203
+ const group = await getGroup({
204
+ accessToken,
205
+ groupId: group1Id
206
+ })
207
+
208
+ logResponse(`GET /card/family/library/groups/${group1Id} (after update)`, group)
209
+
210
+ assert.strictEqual(group.name, 'Test Group 1 - Updated', 'Updated name should persist')
211
+ })
212
+
213
+ await t.test('should delete first group', async () => {
214
+ const response = await deleteGroup({
215
+ accessToken,
216
+ groupId: group1Id
217
+ })
218
+
219
+ logResponse(`DELETE /card/family/library/groups/${group1Id}`, response)
220
+
221
+ assert.ok(response, 'Response should exist')
222
+ assert.strictEqual(response.id, group1Id, 'Deleted group ID should match')
223
+
224
+ // Remove from cleanup list since we already deleted it
225
+ createdGroupIds = createdGroupIds.filter(id => id !== group1Id)
226
+ })
227
+
228
+ await t.test('should delete second group', async () => {
229
+ const response = await deleteGroup({
230
+ accessToken,
231
+ groupId: group2Id
232
+ })
233
+
234
+ logResponse(`DELETE /card/family/library/groups/${group2Id}`, response)
235
+
236
+ assert.ok(response, 'Response should exist')
237
+ assert.strictEqual(response.id, group2Id, 'Deleted group ID should match')
238
+
239
+ // Remove from cleanup list since we already deleted it
240
+ createdGroupIds = createdGroupIds.filter(id => id !== group2Id)
241
+ })
242
+
243
+ await t.test('should verify groups are deleted from list', async () => {
244
+ const groups = await getGroups({
245
+ accessToken
246
+ })
247
+
248
+ logResponse('GET /card/family/library/groups (after deletion)', groups)
249
+
250
+ const foundGroup1 = groups.find(g => g.id === group1Id)
251
+ const foundGroup2 = groups.find(g => g.id === group2Id)
252
+
253
+ assert.strictEqual(foundGroup1, undefined, 'First group should not be in list')
254
+ assert.strictEqual(foundGroup2, undefined, 'Second group should not be in list')
255
+ })
256
+
257
+ await t.test('should return 404 when getting deleted group', async () => {
258
+ await assert.rejects(
259
+ async () => {
260
+ await getGroup({
261
+ accessToken,
262
+ groupId: group1Id
263
+ })
264
+ },
265
+ (err) => {
266
+ assert.ok(err instanceof YotoAPIError, 'Should throw YotoAPIError')
267
+ assert.strictEqual(err.statusCode, 404, 'Should return 404 for deleted group')
268
+ return true
269
+ }
270
+ )
271
+ })
272
+ })
@@ -0,0 +1,39 @@
1
+ export function getFamilyImages({ 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<YotoFamilyImagesResponse>;
8
+ export function getAFamilyImage({ accessToken, userAgent, imageId, size, requestOptions }: {
9
+ accessToken: string;
10
+ imageId: string;
11
+ size: "640x480" | "320x320";
12
+ userAgent?: string | undefined;
13
+ requestOptions?: ({
14
+ dispatcher?: import("undici").Dispatcher;
15
+ } & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
16
+ }): Promise<YotoFamilyImageResponse>;
17
+ export function uploadAFamilyImage({ accessToken, userAgent, imageData, requestOptions }: {
18
+ accessToken: string;
19
+ imageData: Buffer;
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<YotoUploadFamilyImageResponse>;
25
+ export type YotoFamilyImagesResponse = {
26
+ images: YotoFamilyImage[];
27
+ };
28
+ export type YotoFamilyImage = {
29
+ imageId: string;
30
+ name?: string;
31
+ };
32
+ export type YotoFamilyImageResponse = {
33
+ imageUrl: string;
34
+ };
35
+ export type YotoUploadFamilyImageResponse = {
36
+ imageId: string;
37
+ url: string;
38
+ };
39
+ //# sourceMappingURL=family.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"family.d.ts","sourceRoot":"","sources":["family.js"],"names":[],"mappings":"AAiCA,4EALG;IAAyB,WAAW,EAA3B,MAAM;IACW,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,wBAAwB,CAAC,CAkB5C;AAoBD,2FAPG;IAAyB,WAAW,EAA3B,MAAM;IACU,OAAO,EAAvB,MAAM;IACyB,IAAI,EAAnC,SAAS,GAAG,SAAS;IACJ,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,uBAAuB,CAAC,CAsC3C;AAuCD,0FAlBG;IAAyB,WAAW,EAA3B,MAAM;IACU,SAAS,EAAzB,MAAM;IACW,SAAS;IACD,cAAc;;;CAChD,GAAS,OAAO,CAAC,6BAA6B,CAAC,CAmCjD;;YAvJa,eAAe,EAAE;;;aAMjB,MAAM;WACN,MAAM;;;cAiCN,MAAM;;;aAwDN,MAAM;SACN,MAAM"}