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.
- package/LICENSE +21 -0
- package/README.md +736 -0
- package/bin/auth.d.ts +3 -0
- package/bin/auth.d.ts.map +1 -0
- package/bin/auth.js +130 -0
- package/bin/content.d.ts +3 -0
- package/bin/content.d.ts.map +1 -0
- package/bin/content.js +117 -0
- package/bin/devices.d.ts +3 -0
- package/bin/devices.d.ts.map +1 -0
- package/bin/devices.js +239 -0
- package/bin/groups.d.ts +3 -0
- package/bin/groups.d.ts.map +1 -0
- package/bin/groups.js +80 -0
- package/bin/icons.d.ts +3 -0
- package/bin/icons.d.ts.map +1 -0
- package/bin/icons.js +100 -0
- package/bin/lib/cli-helpers.d.ts +21 -0
- package/bin/lib/cli-helpers.d.ts.map +1 -0
- package/bin/lib/cli-helpers.js +140 -0
- package/bin/lib/token-helpers.d.ts +14 -0
- package/bin/lib/token-helpers.d.ts.map +1 -0
- package/bin/lib/token-helpers.js +151 -0
- package/bin/refresh-token.d.ts +3 -0
- package/bin/refresh-token.d.ts.map +1 -0
- package/bin/refresh-token.js +168 -0
- package/bin/token-info.d.ts +3 -0
- package/bin/token-info.d.ts.map +1 -0
- package/bin/token-info.js +351 -0
- package/index.d.ts +218 -0
- package/index.d.ts.map +1 -0
- package/index.js +689 -0
- package/lib/api-endpoints/auth.d.ts +56 -0
- package/lib/api-endpoints/auth.d.ts.map +1 -0
- package/lib/api-endpoints/auth.js +209 -0
- package/lib/api-endpoints/auth.test.js +27 -0
- package/lib/api-endpoints/constants.d.ts +6 -0
- package/lib/api-endpoints/constants.d.ts.map +1 -0
- package/lib/api-endpoints/constants.js +31 -0
- package/lib/api-endpoints/content.d.ts +275 -0
- package/lib/api-endpoints/content.d.ts.map +1 -0
- package/lib/api-endpoints/content.js +518 -0
- package/lib/api-endpoints/content.test.js +250 -0
- package/lib/api-endpoints/devices.d.ts +202 -0
- package/lib/api-endpoints/devices.d.ts.map +1 -0
- package/lib/api-endpoints/devices.js +404 -0
- package/lib/api-endpoints/devices.test.js +483 -0
- package/lib/api-endpoints/family-library-groups.d.ts +75 -0
- package/lib/api-endpoints/family-library-groups.d.ts.map +1 -0
- package/lib/api-endpoints/family-library-groups.js +247 -0
- package/lib/api-endpoints/family-library-groups.test.js +272 -0
- package/lib/api-endpoints/family.d.ts +39 -0
- package/lib/api-endpoints/family.d.ts.map +1 -0
- package/lib/api-endpoints/family.js +166 -0
- package/lib/api-endpoints/family.test.js +184 -0
- package/lib/api-endpoints/helpers.d.ts +29 -0
- package/lib/api-endpoints/helpers.d.ts.map +1 -0
- package/lib/api-endpoints/helpers.js +104 -0
- package/lib/api-endpoints/icons.d.ts +62 -0
- package/lib/api-endpoints/icons.d.ts.map +1 -0
- package/lib/api-endpoints/icons.js +201 -0
- package/lib/api-endpoints/icons.test.js +118 -0
- package/lib/api-endpoints/media.d.ts +37 -0
- package/lib/api-endpoints/media.d.ts.map +1 -0
- package/lib/api-endpoints/media.js +155 -0
- package/lib/api-endpoints/test-helpers.d.ts +7 -0
- package/lib/api-endpoints/test-helpers.d.ts.map +1 -0
- package/lib/api-endpoints/test-helpers.js +64 -0
- package/lib/mqtt/client.d.ts +124 -0
- package/lib/mqtt/client.d.ts.map +1 -0
- package/lib/mqtt/client.js +558 -0
- package/lib/mqtt/commands.d.ts +69 -0
- package/lib/mqtt/commands.d.ts.map +1 -0
- package/lib/mqtt/commands.js +238 -0
- package/lib/mqtt/factory.d.ts +12 -0
- package/lib/mqtt/factory.d.ts.map +1 -0
- package/lib/mqtt/factory.js +107 -0
- package/lib/mqtt/index.d.ts +5 -0
- package/lib/mqtt/index.d.ts.map +1 -0
- package/lib/mqtt/index.js +81 -0
- package/lib/mqtt/mqtt.test.js +168 -0
- package/lib/mqtt/topics.d.ts +34 -0
- package/lib/mqtt/topics.d.ts.map +1 -0
- package/lib/mqtt/topics.js +295 -0
- package/lib/pkg.cjs +3 -0
- package/lib/pkg.d.cts +70 -0
- package/lib/pkg.d.cts.map +1 -0
- package/lib/token.d.ts +29 -0
- package/lib/token.d.ts.map +1 -0
- package/lib/token.js +240 -0
- package/package.json +91 -0
- package/yoto.png +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import {ArgscloptsParseArgsOptionsConfig} from 'argsclopts'
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { printHelpText } from 'argsclopts'
|
|
8
|
+
import { parseArgs } from 'node:util'
|
|
9
|
+
import { YotoClient } from '../index.js'
|
|
10
|
+
import { pkg } from '../lib/pkg.cjs'
|
|
11
|
+
import { DEFAULT_CLIENT_ID } from '../lib/api-endpoints/constants.js'
|
|
12
|
+
import { decodeJwt, formatTimestamp, checkExpiration, saveTokensToEnv } from './lib/token-helpers.js'
|
|
13
|
+
import {
|
|
14
|
+
getCommonOptions,
|
|
15
|
+
loadEnvFile,
|
|
16
|
+
printHeader
|
|
17
|
+
} from './lib/cli-helpers.js'
|
|
18
|
+
|
|
19
|
+
/** @type {ArgscloptsParseArgsOptionsConfig} */
|
|
20
|
+
const options = {
|
|
21
|
+
...getCommonOptions(),
|
|
22
|
+
'access-token': {
|
|
23
|
+
type: 'string',
|
|
24
|
+
short: 'a',
|
|
25
|
+
help: 'Access token to inspect (or set YOTO_ACCESS_TOKEN env var)'
|
|
26
|
+
},
|
|
27
|
+
'refresh-token': {
|
|
28
|
+
type: 'string',
|
|
29
|
+
short: 'r',
|
|
30
|
+
help: 'Refresh token to inspect (or set YOTO_REFRESH_TOKEN env var)'
|
|
31
|
+
},
|
|
32
|
+
introspect: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
short: 'i',
|
|
35
|
+
help: 'Attempt to introspect token with authorization server'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const args = parseArgs({ options, strict: false })
|
|
40
|
+
|
|
41
|
+
if (args.values['help']) {
|
|
42
|
+
await printHelpText({
|
|
43
|
+
options,
|
|
44
|
+
name: 'yoto-token-info',
|
|
45
|
+
version: pkg.version,
|
|
46
|
+
exampleFn: ({ name }) => ` Display information about Yoto OAuth tokens\n\n Examples:\n ${name}\n ${name} --access-token eyJhbGc...\n ${name} --env-file .env.local\n`
|
|
47
|
+
})
|
|
48
|
+
process.exit(0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Load .env file if specified or use default
|
|
52
|
+
const envFile = loadEnvFile(args.values['env-file'] ? String(args.values['env-file']) : undefined)
|
|
53
|
+
const outputFile = envFile // Use same file for output
|
|
54
|
+
|
|
55
|
+
// Get tokens from args or env
|
|
56
|
+
const accessToken = String(args.values['access-token'] || process.env['YOTO_ACCESS_TOKEN'] || '')
|
|
57
|
+
const refreshToken = String(args.values['refresh-token'] || process.env['YOTO_REFRESH_TOKEN'] || '')
|
|
58
|
+
const clientId = String(args.values['client-id'] || process.env['YOTO_CLIENT_ID'] || DEFAULT_CLIENT_ID)
|
|
59
|
+
const shouldIntrospect = Boolean(args.values['introspect'])
|
|
60
|
+
|
|
61
|
+
if (!accessToken || !refreshToken) {
|
|
62
|
+
console.error('❌ Both access token and refresh token are required')
|
|
63
|
+
console.error('Provide tokens via:')
|
|
64
|
+
console.error(' - Command line flags: --access-token and --refresh-token')
|
|
65
|
+
console.error(' - Environment variables: YOTO_ACCESS_TOKEN and YOTO_REFRESH_TOKEN')
|
|
66
|
+
console.error(' - .env file (loaded automatically or specify with --env-file)')
|
|
67
|
+
console.error('\n💡 Tip: Run yoto-auth to get both tokens')
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Introspect token with authorization server
|
|
73
|
+
* @param {string} token
|
|
74
|
+
* @param {string} clientId
|
|
75
|
+
* @returns {Promise<any>}
|
|
76
|
+
*/
|
|
77
|
+
async function introspectToken (token, clientId) {
|
|
78
|
+
const introspectionUrl = 'https://login.yotoplay.com/oauth/introspect'
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(introspectionUrl, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: {
|
|
84
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
85
|
+
},
|
|
86
|
+
body: new URLSearchParams({
|
|
87
|
+
token,
|
|
88
|
+
client_id: clientId
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (response.status === 404) {
|
|
93
|
+
return { error: 'not_supported', message: 'Token introspection endpoint not found (404)' }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (response.status === 401) {
|
|
97
|
+
return {
|
|
98
|
+
error: 'authentication_required',
|
|
99
|
+
message: 'Endpoint requires client authentication. This is typical for introspection endpoints - they require confidential clients with credentials (client secret or private key). Public clients cannot use this endpoint.'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const errorText = await response.text()
|
|
105
|
+
return { error: 'request_failed', message: `Request failed: ${response.status} ${errorText}` }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return await response.json()
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const error = /** @type {any} */ (err)
|
|
111
|
+
return { error: 'network_error', message: error.message }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Display token information
|
|
117
|
+
* @param {string} tokenType
|
|
118
|
+
* @param {string} token
|
|
119
|
+
* @param {string} clientId
|
|
120
|
+
* @param {boolean} shouldIntrospect
|
|
121
|
+
*/
|
|
122
|
+
async function displayTokenInfo (tokenType, token, clientId, shouldIntrospect) {
|
|
123
|
+
console.log(`\n${'='.repeat(60)}`)
|
|
124
|
+
console.log(`${tokenType.toUpperCase()} TOKEN`)
|
|
125
|
+
console.log('='.repeat(60))
|
|
126
|
+
|
|
127
|
+
// Check if token is a JWT (has three parts separated by dots)
|
|
128
|
+
const parts = token.split('.')
|
|
129
|
+
if (parts.length !== 3) {
|
|
130
|
+
// Not a JWT - probably an opaque token (like refresh tokens)
|
|
131
|
+
console.log('\n🔤 Token Type: Opaque Token (not a JWT)')
|
|
132
|
+
console.log('\n📝 Information:')
|
|
133
|
+
console.log(' This token is not a JWT and cannot be decoded.')
|
|
134
|
+
console.log(' It is likely an opaque refresh token that can only')
|
|
135
|
+
console.log(' be validated by the authorization server.')
|
|
136
|
+
console.log('\n🔤 Token Preview:')
|
|
137
|
+
if (token.length > 40) {
|
|
138
|
+
console.log(` ${token.substring(0, 20)}...${token.substring(token.length - 20)}`)
|
|
139
|
+
} else {
|
|
140
|
+
console.log(` ${token}`)
|
|
141
|
+
}
|
|
142
|
+
console.log(` Length: ${token.length} characters`)
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const decoded = decodeJwt(token)
|
|
148
|
+
|
|
149
|
+
console.log('\n📋 Token Type: JWT')
|
|
150
|
+
|
|
151
|
+
// Expiration
|
|
152
|
+
if (decoded.exp) {
|
|
153
|
+
console.log('\n⏰ Expiration:')
|
|
154
|
+
console.log(` Expires: ${formatTimestamp(decoded.exp)}`)
|
|
155
|
+
const expCheck = checkExpiration(decoded.exp)
|
|
156
|
+
const statusIcon = expCheck.expired ? '❌' : '✅'
|
|
157
|
+
console.log(` Status: ${statusIcon} ${expCheck.message}`)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Issued at
|
|
161
|
+
if (decoded.iat) {
|
|
162
|
+
console.log(` Issued: ${formatTimestamp(decoded.iat)}`)
|
|
163
|
+
const age = Math.floor(Date.now() / 1000) - decoded.iat
|
|
164
|
+
const ageMinutes = Math.floor(age / 60)
|
|
165
|
+
const ageHours = Math.floor(ageMinutes / 60)
|
|
166
|
+
if (ageHours > 0) {
|
|
167
|
+
console.log(` Age: ${ageHours} hour(s) ${ageMinutes % 60} minute(s)`)
|
|
168
|
+
} else {
|
|
169
|
+
console.log(` Age: ${ageMinutes} minute(s)`)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Subject and audience
|
|
174
|
+
console.log('\n👤 Claims:')
|
|
175
|
+
if (decoded.sub) console.log(` Subject: ${decoded.sub}`)
|
|
176
|
+
if (decoded.aud) {
|
|
177
|
+
if (Array.isArray(decoded.aud)) {
|
|
178
|
+
console.log(' Audience:')
|
|
179
|
+
decoded.aud.forEach((/** @type {any} */ aud) => console.log(` - ${aud}`))
|
|
180
|
+
} else {
|
|
181
|
+
console.log(` Audience: ${decoded.aud}`)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (decoded.iss) console.log(` Issuer: ${decoded.iss}`)
|
|
185
|
+
|
|
186
|
+
// Client ID verification
|
|
187
|
+
if (decoded.azp || decoded.client_id) {
|
|
188
|
+
const tokenClientId = decoded.azp || decoded.client_id
|
|
189
|
+
console.log(` Client ID: ${tokenClientId}`)
|
|
190
|
+
if (clientId) {
|
|
191
|
+
if (tokenClientId === clientId) {
|
|
192
|
+
console.log(' ✅ Matches provided client ID')
|
|
193
|
+
} else {
|
|
194
|
+
console.log(` ⚠️ Does NOT match provided client ID (${clientId})`)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Scopes
|
|
200
|
+
if (decoded.scope) {
|
|
201
|
+
console.log('\n🔐 Scopes:')
|
|
202
|
+
const scopes = typeof decoded.scope === 'string' ? decoded.scope.split(' ') : decoded.scope
|
|
203
|
+
if (Array.isArray(scopes)) {
|
|
204
|
+
scopes.forEach(scope => console.log(` - ${scope}`))
|
|
205
|
+
} else {
|
|
206
|
+
console.log(` ${scopes}`)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Permissions
|
|
211
|
+
if (decoded.permissions && Array.isArray(decoded.permissions)) {
|
|
212
|
+
console.log('\n🔑 Permissions:')
|
|
213
|
+
decoded.permissions.forEach((/** @type {any} */ perm) => console.log(` - ${perm}`))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Other claims
|
|
217
|
+
const standardClaims = new Set(['exp', 'iat', 'nbf', 'sub', 'aud', 'iss', 'azp', 'client_id', 'scope', 'permissions', 'alg', 'typ', 'kid'])
|
|
218
|
+
const otherClaims = Object.keys(decoded).filter(key => !standardClaims.has(key))
|
|
219
|
+
|
|
220
|
+
if (otherClaims.length > 0) {
|
|
221
|
+
console.log('\n📦 Other Claims:')
|
|
222
|
+
otherClaims.forEach(key => {
|
|
223
|
+
const value = decoded[key]
|
|
224
|
+
if (typeof value === 'object') {
|
|
225
|
+
console.log(` ${key}: ${JSON.stringify(value)}`)
|
|
226
|
+
} else {
|
|
227
|
+
console.log(` ${key}: ${value}`)
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Raw token (first/last 20 chars)
|
|
233
|
+
console.log('\n🔤 Token Preview:')
|
|
234
|
+
if (token.length > 40) {
|
|
235
|
+
console.log(` ${token.substring(0, 20)}...${token.substring(token.length - 20)}`)
|
|
236
|
+
} else {
|
|
237
|
+
console.log(` ${token}`)
|
|
238
|
+
}
|
|
239
|
+
console.log(` Length: ${token.length} characters`)
|
|
240
|
+
} catch (err) {
|
|
241
|
+
const error = /** @type {any} */ (err)
|
|
242
|
+
console.error(`\n❌ Error decoding ${tokenType} token:`)
|
|
243
|
+
console.error(` ${error.message}`)
|
|
244
|
+
console.error('\n💡 Tip: If this is a refresh token, it may be an opaque token')
|
|
245
|
+
console.error(' that cannot be decoded. This is normal for some OAuth2 implementations.')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Introspection
|
|
249
|
+
if (shouldIntrospect) {
|
|
250
|
+
console.log('\n🔍 Token Introspection:')
|
|
251
|
+
console.log(' Querying authorization server...')
|
|
252
|
+
|
|
253
|
+
const introspectionResult = await introspectToken(token, clientId)
|
|
254
|
+
|
|
255
|
+
if (introspectionResult.error) {
|
|
256
|
+
if (introspectionResult.error === 'authentication_required') {
|
|
257
|
+
console.log(` ⚠️ ${introspectionResult.message}`)
|
|
258
|
+
} else {
|
|
259
|
+
console.log(` ❌ ${introspectionResult.message}`)
|
|
260
|
+
}
|
|
261
|
+
} else if (introspectionResult.active === false) {
|
|
262
|
+
console.log(' ❌ Token is INACTIVE')
|
|
263
|
+
} else if (introspectionResult.active === true) {
|
|
264
|
+
console.log(' ✅ Token is ACTIVE')
|
|
265
|
+
if (introspectionResult.scope) {
|
|
266
|
+
console.log(` Scope: ${introspectionResult.scope}`)
|
|
267
|
+
}
|
|
268
|
+
if (introspectionResult.client_id) {
|
|
269
|
+
console.log(` Client ID: ${introspectionResult.client_id}`)
|
|
270
|
+
}
|
|
271
|
+
if (introspectionResult.username) {
|
|
272
|
+
console.log(` Username: ${introspectionResult.username}`)
|
|
273
|
+
}
|
|
274
|
+
if (introspectionResult.exp) {
|
|
275
|
+
console.log(` Expires: ${formatTimestamp(introspectionResult.exp)}`)
|
|
276
|
+
}
|
|
277
|
+
if (introspectionResult.iat) {
|
|
278
|
+
console.log(` Issued: ${formatTimestamp(introspectionResult.iat)}`)
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
console.log(' ⚠️ Unexpected response:')
|
|
282
|
+
console.log(` ${JSON.stringify(introspectionResult, null, 2)}`)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
printHeader('Yoto Token Information')
|
|
288
|
+
|
|
289
|
+
// Create YotoClient to show token status
|
|
290
|
+
let client
|
|
291
|
+
try {
|
|
292
|
+
console.log('\n📊 Creating YotoClient instance...')
|
|
293
|
+
client = new YotoClient({
|
|
294
|
+
clientId,
|
|
295
|
+
refreshToken,
|
|
296
|
+
accessToken,
|
|
297
|
+
onTokenRefresh: async (tokens) => {
|
|
298
|
+
// Save tokens if they refresh during inspection
|
|
299
|
+
console.log('\n⚠️ Token was refreshed during inspection! Saving...')
|
|
300
|
+
await saveTokensToEnv(outputFile, {
|
|
301
|
+
access_token: tokens.accessToken,
|
|
302
|
+
refresh_token: tokens.refreshToken,
|
|
303
|
+
token_type: 'Bearer',
|
|
304
|
+
expires_in: tokens.expiresAt - Math.floor(Date.now() / 1000)
|
|
305
|
+
}, tokens.clientId)
|
|
306
|
+
console.log(`✅ Updated tokens saved to ${outputFile}`)
|
|
307
|
+
},
|
|
308
|
+
onRefreshStart: () => {
|
|
309
|
+
console.log('\n🔄 Token refresh triggered during inspection...')
|
|
310
|
+
},
|
|
311
|
+
onRefreshError: (error) => {
|
|
312
|
+
console.warn('\n⚠️ Token refresh failed:', error.message)
|
|
313
|
+
},
|
|
314
|
+
onInvalid: (error) => {
|
|
315
|
+
console.error('\n❌ Refresh token is invalid:', error.message)
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
console.log('✅ YotoClient created successfully')
|
|
320
|
+
console.log('\n🔍 Token Status from RefreshableToken:')
|
|
321
|
+
console.log(` Valid: ${client.token.isValid() ? '✅ Yes' : '❌ No'}`)
|
|
322
|
+
console.log(` Expires At: ${formatTimestamp(client.token.getExpiresAt())}`)
|
|
323
|
+
|
|
324
|
+
const timeRemaining = client.token.getTimeRemaining()
|
|
325
|
+
if (timeRemaining > 0) {
|
|
326
|
+
const minutes = Math.floor(timeRemaining / 60)
|
|
327
|
+
const hours = Math.floor(minutes / 60)
|
|
328
|
+
if (hours > 0) {
|
|
329
|
+
console.log(` Time Remaining: ${hours} hour(s) ${minutes % 60} minute(s)`)
|
|
330
|
+
} else {
|
|
331
|
+
console.log(` Time Remaining: ${minutes} minute(s)`)
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
console.log(` Time Remaining: Expired ${Math.abs(timeRemaining)} seconds ago`)
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
const error = /** @type {Error} */ (err)
|
|
338
|
+
console.log(`⚠️ Could not create YotoClient: ${error.message}`)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Display access token info
|
|
342
|
+
if (accessToken) {
|
|
343
|
+
await displayTokenInfo('access', accessToken, clientId, shouldIntrospect)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Display refresh token info
|
|
347
|
+
await displayTokenInfo('refresh', refreshToken, clientId, shouldIntrospect)
|
|
348
|
+
|
|
349
|
+
console.log('\n' + '='.repeat(60))
|
|
350
|
+
console.log('✨ Token inspection complete')
|
|
351
|
+
console.log('='.repeat(60) + '\n')
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
export class YotoClient {
|
|
2
|
+
static getAuthorizeUrl(params: {
|
|
3
|
+
clientId: string;
|
|
4
|
+
redirectUri: string;
|
|
5
|
+
responseType: "code" | "token" | "id_token" | "code token" | "code id_token" | "token id_token" | "code token id_token";
|
|
6
|
+
state: string;
|
|
7
|
+
audience?: string | undefined;
|
|
8
|
+
scope?: string | undefined;
|
|
9
|
+
nonce?: string | undefined;
|
|
10
|
+
prompt?: "none" | "login" | "consent" | "select_account" | undefined;
|
|
11
|
+
maxAge?: number | undefined;
|
|
12
|
+
codeChallenge?: string | undefined;
|
|
13
|
+
codeChallengeMethod?: "S256" | "plain" | undefined;
|
|
14
|
+
}): string;
|
|
15
|
+
static exchangeToken(params: {
|
|
16
|
+
grantType: "authorization_code" | "refresh_token" | "client_credentials" | "urn:ietf:params:oauth:grant-type:device_code";
|
|
17
|
+
code?: string | undefined;
|
|
18
|
+
redirectUri?: string | undefined;
|
|
19
|
+
refreshToken?: string | undefined;
|
|
20
|
+
clientId?: string | undefined;
|
|
21
|
+
clientSecret?: string | undefined;
|
|
22
|
+
scope?: string | undefined;
|
|
23
|
+
codeVerifier?: string | undefined;
|
|
24
|
+
deviceCode?: string | undefined;
|
|
25
|
+
audience?: string | undefined;
|
|
26
|
+
}): Promise<Auth.YotoTokenResponse>;
|
|
27
|
+
static requestDeviceCode(params: {
|
|
28
|
+
clientId: string;
|
|
29
|
+
scope?: string | undefined;
|
|
30
|
+
audience?: string | undefined;
|
|
31
|
+
}): Promise<Auth.YotoDeviceCodeResponse>;
|
|
32
|
+
constructor({ clientId, refreshToken, accessToken, onTokenRefresh, bufferSeconds, onRefreshStart, onRefreshError, onInvalid, userAgent, defaultRequestOptions }: YotoClientConstructorOptions);
|
|
33
|
+
get token(): RefreshableToken;
|
|
34
|
+
getContent({ cardId, timezone, signingType, playable, requestOptions }: {
|
|
35
|
+
cardId: string;
|
|
36
|
+
timezone?: string | undefined;
|
|
37
|
+
signingType?: "full" | "pre" | undefined;
|
|
38
|
+
playable?: boolean | undefined;
|
|
39
|
+
requestOptions?: ({
|
|
40
|
+
dispatcher?: import("undici").Dispatcher;
|
|
41
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
42
|
+
}): Promise<Content.YotoContentResponse>;
|
|
43
|
+
getUserMyoContent({ showDeleted, requestOptions }?: {
|
|
44
|
+
showDeleted?: boolean | undefined;
|
|
45
|
+
requestOptions?: ({
|
|
46
|
+
dispatcher?: import("undici").Dispatcher;
|
|
47
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
48
|
+
}): Promise<Content.YotoMyoContentResponse>;
|
|
49
|
+
createOrUpdateContent({ content, requestOptions }: {
|
|
50
|
+
content: Content.YotoCreateOrUpdateContentRequest;
|
|
51
|
+
requestOptions?: ({
|
|
52
|
+
dispatcher?: import("undici").Dispatcher;
|
|
53
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
54
|
+
}): Promise<Content.YotoCreateOrUpdateContentResponse>;
|
|
55
|
+
deleteContent({ cardId, requestOptions }: {
|
|
56
|
+
cardId: string;
|
|
57
|
+
requestOptions?: ({
|
|
58
|
+
dispatcher?: import("undici").Dispatcher;
|
|
59
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
60
|
+
}): Promise<Content.YotoDeleteContentResponse>;
|
|
61
|
+
getDevices({ requestOptions }?: {
|
|
62
|
+
requestOptions?: ({
|
|
63
|
+
dispatcher?: import("undici").Dispatcher;
|
|
64
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
65
|
+
}): Promise<Devices.YotoDevicesResponse>;
|
|
66
|
+
getDeviceStatus({ deviceId, requestOptions }: {
|
|
67
|
+
deviceId: string;
|
|
68
|
+
requestOptions?: ({
|
|
69
|
+
dispatcher?: import("undici").Dispatcher;
|
|
70
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
71
|
+
}): Promise<Devices.YotoDeviceStatusResponse>;
|
|
72
|
+
getDeviceConfig({ deviceId, requestOptions }: {
|
|
73
|
+
deviceId: string;
|
|
74
|
+
requestOptions?: ({
|
|
75
|
+
dispatcher?: import("undici").Dispatcher;
|
|
76
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
77
|
+
}): Promise<Devices.YotoDeviceConfigResponse>;
|
|
78
|
+
updateDeviceConfig({ deviceId, configUpdate, requestOptions }: {
|
|
79
|
+
deviceId: string;
|
|
80
|
+
configUpdate: Devices.YotoUpdateDeviceConfigRequest;
|
|
81
|
+
requestOptions?: ({
|
|
82
|
+
dispatcher?: import("undici").Dispatcher;
|
|
83
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
84
|
+
}): Promise<Devices.YotoUpdateDeviceConfigResponse>;
|
|
85
|
+
updateDeviceShortcuts({ deviceId, shortcutsUpdate, requestOptions }: {
|
|
86
|
+
deviceId: string;
|
|
87
|
+
shortcutsUpdate: Devices.YotoUpdateShortcutsRequest;
|
|
88
|
+
requestOptions?: ({
|
|
89
|
+
dispatcher?: import("undici").Dispatcher;
|
|
90
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
91
|
+
}): Promise<Devices.YotoUpdateShortcutsResponse>;
|
|
92
|
+
sendDeviceCommand({ deviceId, command, requestOptions }: {
|
|
93
|
+
deviceId: string;
|
|
94
|
+
command: Devices.YotoDeviceCommand;
|
|
95
|
+
requestOptions?: ({
|
|
96
|
+
dispatcher?: import("undici").Dispatcher;
|
|
97
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
98
|
+
}): Promise<Devices.YotoDeviceCommandResponse>;
|
|
99
|
+
getGroups({ requestOptions }?: {
|
|
100
|
+
requestOptions?: ({
|
|
101
|
+
dispatcher?: import("undici").Dispatcher;
|
|
102
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
103
|
+
}): Promise<FamilyLibraryGroups.YotoGroup[]>;
|
|
104
|
+
createGroup({ group, requestOptions }: {
|
|
105
|
+
group: FamilyLibraryGroups.YotoCreateGroupRequest;
|
|
106
|
+
requestOptions?: ({
|
|
107
|
+
dispatcher?: import("undici").Dispatcher;
|
|
108
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
109
|
+
}): Promise<FamilyLibraryGroups.YotoGroup>;
|
|
110
|
+
getGroup({ groupId, requestOptions }: {
|
|
111
|
+
groupId: string;
|
|
112
|
+
requestOptions?: ({
|
|
113
|
+
dispatcher?: import("undici").Dispatcher;
|
|
114
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
115
|
+
}): Promise<FamilyLibraryGroups.YotoGroup>;
|
|
116
|
+
updateGroup({ groupId, group, requestOptions }: {
|
|
117
|
+
groupId: string;
|
|
118
|
+
group: FamilyLibraryGroups.YotoUpdateGroupRequest;
|
|
119
|
+
requestOptions?: ({
|
|
120
|
+
dispatcher?: import("undici").Dispatcher;
|
|
121
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
122
|
+
}): Promise<FamilyLibraryGroups.YotoGroup>;
|
|
123
|
+
deleteGroup({ groupId, requestOptions }: {
|
|
124
|
+
groupId: string;
|
|
125
|
+
requestOptions?: ({
|
|
126
|
+
dispatcher?: import("undici").Dispatcher;
|
|
127
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
128
|
+
}): Promise<FamilyLibraryGroups.YotoDeleteGroupResponse>;
|
|
129
|
+
getFamilyImages({ requestOptions }?: {
|
|
130
|
+
requestOptions?: ({
|
|
131
|
+
dispatcher?: import("undici").Dispatcher;
|
|
132
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
133
|
+
}): Promise<Family.YotoFamilyImagesResponse>;
|
|
134
|
+
getAFamilyImage({ imageId, size, requestOptions }: {
|
|
135
|
+
imageId: string;
|
|
136
|
+
size: "640x480" | "320x320";
|
|
137
|
+
requestOptions?: ({
|
|
138
|
+
dispatcher?: import("undici").Dispatcher;
|
|
139
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
140
|
+
}): Promise<Family.YotoFamilyImageResponse>;
|
|
141
|
+
uploadAFamilyImage({ imageData, requestOptions }: {
|
|
142
|
+
imageData: Buffer;
|
|
143
|
+
requestOptions?: ({
|
|
144
|
+
dispatcher?: import("undici").Dispatcher;
|
|
145
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
146
|
+
}): Promise<Family.YotoUploadFamilyImageResponse>;
|
|
147
|
+
getPublicIcons({ requestOptions }?: {
|
|
148
|
+
requestOptions?: ({
|
|
149
|
+
dispatcher?: import("undici").Dispatcher;
|
|
150
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
151
|
+
}): Promise<Icons.YotoPublicIconsResponse>;
|
|
152
|
+
getUserIcons({ requestOptions }?: {
|
|
153
|
+
requestOptions?: ({
|
|
154
|
+
dispatcher?: import("undici").Dispatcher;
|
|
155
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
156
|
+
}): Promise<Icons.YotoUserIconsResponse>;
|
|
157
|
+
uploadIcon({ imageData, autoConvert, filename, requestOptions }: {
|
|
158
|
+
imageData: Buffer;
|
|
159
|
+
autoConvert?: boolean | undefined;
|
|
160
|
+
filename?: string | undefined;
|
|
161
|
+
requestOptions?: ({
|
|
162
|
+
dispatcher?: import("undici").Dispatcher;
|
|
163
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
164
|
+
}): Promise<Icons.YotoUploadIconResponse>;
|
|
165
|
+
getAudioUploadUrl({ sha256, filename, requestOptions }: {
|
|
166
|
+
sha256: string;
|
|
167
|
+
filename?: string | undefined;
|
|
168
|
+
requestOptions?: ({
|
|
169
|
+
dispatcher?: import("undici").Dispatcher;
|
|
170
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
171
|
+
}): Promise<Media.YotoAudioUploadUrlResponse>;
|
|
172
|
+
uploadCoverImage({ imageData, imageUrl, autoConvert, coverType, filename, requestOptions }: {
|
|
173
|
+
imageData?: Buffer<ArrayBufferLike> | undefined;
|
|
174
|
+
imageUrl?: string | undefined;
|
|
175
|
+
autoConvert?: boolean | undefined;
|
|
176
|
+
coverType?: Media.YotoCoverType | undefined;
|
|
177
|
+
filename?: string | undefined;
|
|
178
|
+
requestOptions?: ({
|
|
179
|
+
dispatcher?: import("undici").Dispatcher;
|
|
180
|
+
} & Omit<import("undici").Dispatcher.RequestOptions<unknown>, "origin" | "path" | "method"> & Partial<Pick<import("undici").Dispatcher.RequestOptions<null>, "method">>) | undefined;
|
|
181
|
+
}): Promise<Media.YotoUploadCoverImageResponse>;
|
|
182
|
+
createMqttClient({ deviceId, options }: {
|
|
183
|
+
deviceId: string;
|
|
184
|
+
options?: {
|
|
185
|
+
autoSubscribe?: boolean | undefined;
|
|
186
|
+
} | undefined;
|
|
187
|
+
}): Promise<YotoMqttClient>;
|
|
188
|
+
#private;
|
|
189
|
+
}
|
|
190
|
+
export type RefreshSuccessEvent = {
|
|
191
|
+
clientId: string;
|
|
192
|
+
accessToken: string;
|
|
193
|
+
refreshToken: string;
|
|
194
|
+
expiresAt: number;
|
|
195
|
+
};
|
|
196
|
+
export type YotoClientConstructorOptions = {
|
|
197
|
+
clientId: string;
|
|
198
|
+
refreshToken: string;
|
|
199
|
+
accessToken: string;
|
|
200
|
+
onTokenRefresh: (event: RefreshSuccessEvent) => void | Promise<void>;
|
|
201
|
+
bufferSeconds?: number;
|
|
202
|
+
onRefreshStart?: () => void | Promise<void>;
|
|
203
|
+
onRefreshError?: (error: Error) => void | Promise<void>;
|
|
204
|
+
onInvalid?: (error: Error) => void | Promise<void>;
|
|
205
|
+
userAgent?: string;
|
|
206
|
+
defaultRequestOptions?: RequestOptions;
|
|
207
|
+
};
|
|
208
|
+
import { RefreshableToken } from './lib/token.js';
|
|
209
|
+
import * as Content from './lib/api-endpoints/content.js';
|
|
210
|
+
import * as Devices from './lib/api-endpoints/devices.js';
|
|
211
|
+
import * as FamilyLibraryGroups from './lib/api-endpoints/family-library-groups.js';
|
|
212
|
+
import * as Family from './lib/api-endpoints/family.js';
|
|
213
|
+
import * as Icons from './lib/api-endpoints/icons.js';
|
|
214
|
+
import * as Media from './lib/api-endpoints/media.js';
|
|
215
|
+
import type { YotoMqttClient } from './lib/mqtt/client.js';
|
|
216
|
+
import * as Auth from './lib/api-endpoints/auth.js';
|
|
217
|
+
import type { RequestOptions } from './lib/api-endpoints/helpers.js';
|
|
218
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"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"}
|