@taskcluster/client-web 88.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/README.md ADDED
@@ -0,0 +1,373 @@
1
+ # Taskcluster Client for Web
2
+
3
+ [![Download](https://img.shields.io/badge/yarn-taskcluster--client--web-brightgreen)](https://yarnpkg.com/en/package/@taskcluster/client-web)
4
+ [![License](https://img.shields.io/badge/license-MPL%202.0-orange.svg)](http://mozilla.org/MPL/2.0)
5
+
6
+ **A Taskcluster client library for the browser.**
7
+
8
+ This library differs from
9
+ [@taskcluster/client](https://yarnpkg.com/en/package/@taskcluster/client) by
10
+ providing a version that is compatible with the browser out of the box and does
11
+ not require a build step to use.
12
+
13
+ ## Installation
14
+
15
+ You can install this package using Yarn or npm:
16
+
17
+ ```bash
18
+ yarn add @taskcluster/client-web
19
+ ```
20
+ ```bash
21
+ npm install --save @taskcluster/client-web
22
+ ```
23
+
24
+ ## Usage
25
+ ### Import
26
+
27
+ After installing this package, you can then import functionality as desired. Your specific
28
+ build process and installation method will determine how you can import this functionality.
29
+ The following importing standards are supported:
30
+
31
+ **ES imports**
32
+
33
+ ```js
34
+ import * as taskcluster from '@taskcluster/client-web';
35
+ import { Queue } from '@taskcluster/client-web';
36
+ ```
37
+
38
+ **CommonJS require**
39
+
40
+ ```js
41
+ const taskcluster = require('@taskcluster/client-web');
42
+ const { Queue } = require('@taskcluster/client-web');
43
+ ```
44
+
45
+ **AMD/UMD require**
46
+
47
+ ```js
48
+ require(['@taskcluster/client-web'], (taskcluster) => {
49
+ // ...
50
+ });
51
+
52
+ require(['@taskcluster/client-web'], ({ Queue }) => {
53
+ // ...
54
+ });
55
+ ```
56
+
57
+ ### Setup
58
+
59
+ To invoke an API endpoint, instantiate a taskcluster client class.
60
+ In the following example we instantiate an instance of the `Queue` client
61
+ class.
62
+
63
+ _Note: while these examples use ES imports, your actual usage will depend on
64
+ what your build process or installation method support._
65
+
66
+ ```js
67
+ import { Queue } from '@taskcluster/client-web';
68
+
69
+ const taskId = '...';
70
+
71
+ // Instantiate the Queue Client class
72
+ const queue = new Queue({
73
+ rootUrl: 'https://taskcluster.net',
74
+ timeout: 30 * 1000, // timeout for _each_ individual http request
75
+ credentials: {
76
+ clientId: '...',
77
+ accessToken: '...',
78
+ // Certificate must also be provided if using temporary credentials,
79
+ // this can be either a JSON object or a JSON string.
80
+ certificate: {...} // Only applicable for temporary credentials
81
+ }
82
+ });
83
+ ```
84
+
85
+ You must configure the `rootUrl` when creating an instance of the client. The
86
+ credentials can also be provided in options. If no credentials are provided,
87
+ requests will be made without authentication.
88
+
89
+ If you need to create a client similar to a existing client, but with some
90
+ options changed, use `client.use(options)`:
91
+
92
+ ```js
93
+ queue
94
+ .use({ authorizedScopes: [/* ... */] })
95
+ .createTask(/* ... */)
96
+ .then(/* ... */);
97
+ ```
98
+
99
+ This replaces any given options with new values.
100
+
101
+ #### Authorized Scopes
102
+
103
+ If you wish to perform requests on behalf of a third-party that has a smaller set of
104
+ scopes than you do, you can specify which scopes your request should be allowed
105
+ to use with `authorizedScopes`.
106
+
107
+ ```js
108
+ import { Queue } from '@taskcluster/client-web';
109
+
110
+ // Create a Queue Client class can only define tasks for a specific workerType
111
+ const queue = new Queue({
112
+ rootUrl,
113
+ // Credentials that can define tasks for any provisioner and workerType.
114
+ credentials: {
115
+ clientId: '...',
116
+ accessToken: '...'
117
+ },
118
+ // Restricting this instance of the Queue client to only one scope
119
+ authorizedScopes: ['queue:post:create-task/my-provisioner/my-worker-type']
120
+ });
121
+
122
+ // This request will only be successful if the task posted is aimed at
123
+ // "my-worker-type" under "my-provisioner".
124
+ queue
125
+ .createTask(taskId, taskDefinition)
126
+ .then(result => {
127
+ // ...
128
+ });
129
+ ```
130
+
131
+
132
+
133
+ ### Calling API Methods
134
+
135
+ API endpoints are available as async methods on the client object created
136
+ above. The calling conventions are given in the Taskcluster reference
137
+ documentation.
138
+
139
+ ```js
140
+ // Create task using the queue client
141
+ queue
142
+ .createTask(taskId, payload)
143
+ .then((result) => {
144
+ // status is a task status structure
145
+ console.log(result.status);
146
+ });
147
+ ```
148
+
149
+ The `payload` parameter is always a JavaScript object as documented by the reference
150
+ documentation.
151
+
152
+ Some API end-points may take a query string. This is indicated in the signature
153
+ as `[options]`. These options are always _optional_, commonly used for
154
+ continuation tokens when paging a list.
155
+
156
+ ### Generating URLs
157
+
158
+ You can build a URL for any request, but this feature is mostly useful for
159
+ requests that do not require any authentication. If you need authentication,
160
+ take a look at the section on building signed URLs, which is possible for all
161
+ `GET` requests. To construct a URL for a request use the `buildUrl` method, as
162
+ illustrated in the following example:
163
+
164
+ ```js
165
+ import { Queue } from '@taskcluster/client-web';
166
+
167
+ // Create queue instance
168
+ const queue = new Queue({ rootUrl });
169
+
170
+ // Build url to get a specific task
171
+ const url = queue.buildUrl(
172
+ queue.getTask, // Method to build url for.
173
+ taskId // First parameter for the method, in this case taskId
174
+ );
175
+ ```
176
+
177
+ Please note that the `payload` parameter cannot be encoded in URLs and must be
178
+ sent when using a constructed URLs. This should not a problem as most methods
179
+ that accept a `payload` also require authentication.
180
+
181
+ It's possible to build signed URLs for `GET` requests. A signed URL
182
+ contains a query string parameter called `bewit`. This parameter holds
183
+ expiration time, signature, and scope restrictions if applied. The signature
184
+ covers the following parameters:
185
+
186
+ * Expiration time,
187
+ * URL and query string
188
+ * Scope restrictions, if applied
189
+
190
+ These signed URLs are convenient if you want to grant someone access to a
191
+ specific resource without proxying the request or sharing your credentials.
192
+ It's fairly safe to provide someone with a signed URL for a
193
+ specific artifact that is protected by a scope, for example:
194
+
195
+ ```js
196
+ import { Queue } from '@taskcluster/client-web';
197
+
198
+ // Create queue instance
199
+ const queue = new Queue({ rootUrl, credentials });
200
+
201
+ // Build signed url
202
+ queue
203
+ .buildSignedUrl(
204
+ queue.getArtifactFromRun, // method to build signed url for.
205
+ taskId, // Task ID parameter
206
+ runId, // Run ID parameter
207
+ artifactName, // Artifact name parameter
208
+ { expiration: 60 * 10 } // Expiration time in seconds
209
+ )
210
+ .then(signedUrl => { /* ... });
211
+ ```
212
+
213
+ **NOTE**: This method returns a promise, unlike in [@taskcluster/client](https://yarnpkg.com/en/package/@taskcluster/client).
214
+ If you are not using a credentials agent, but have passed `credentials` to the client constructor, you can use the synchronous `buildSignedUrlSync` instead.
215
+
216
+ Please note that the `payload` parameter cannot be encoded in the signed URL
217
+ and must be sent as request payload. This should work fine, just remember that
218
+ it's only possible to make signed URLs for `GET` requests, which in most cases
219
+ don't accept a payload.
220
+
221
+ Also please consider using a relatively limited expiration time, as it's not
222
+ possible to retract a signed url without revoking your credentials.
223
+ For more technical details on signed urls, see _bewit_ URLs in
224
+ [hawk](https://github.com/mozilla/hawk).
225
+
226
+ ### Generating Temporary Credentials
227
+
228
+ If you have non-temporary Taskcluster credentials you can generate a set of
229
+ temporary credentials as follows. Notice that the credentials cannot last more
230
+ than 31 days, and you can only revoke them by revoking the credentials that were
231
+ used to issue them, which can take up to one hour.
232
+
233
+ ```js
234
+ import { createTemporaryCredentials } from '@taskcluster/client-web';
235
+
236
+ const credentials = createTemporaryCredentials({
237
+ // Name of temporary credential (optional)
238
+ clientId: '...',
239
+ // Validity of temporary credentials starts here
240
+ start: new Date(),
241
+ // Expiration of temporary credentials
242
+ expiry: new Date(new Date().getTime() + 5 * 60 * 1000),
243
+ // Scopes to grant the temporary credentials
244
+ scopes: ['ScopeA', 'ScopeB', /* ... */],
245
+ credentials: { // Non-temporary taskcluster credentials
246
+ clientId: '...',
247
+ accessToken: '...'
248
+ }
249
+ });
250
+ ```
251
+
252
+ You cannot use temporary credentials to issue new temporary credentials. You
253
+ must have `auth:create-client:<name>` to create a named temporary credential,
254
+ but unnamed temporary credentials can be created regardless of your scopes.
255
+ ### Handling Timestamps
256
+
257
+ Many Taskcluster APIs require ISO 8601 timestamp offsets into the future
258
+ as way of providing expiration, deadlines, etc. These can be easily created
259
+ using `new Date().toJSON()`, however, it can be rather error prone and tedious
260
+ to offset `Date` objects into the future. Therefore this library comes with two
261
+ utility functions for this purpose.
262
+
263
+ ```js
264
+ import { fromNow, fromNowJSON } from '@taskcluster/client-web';
265
+
266
+ const dateObject = fromNow('2 days 3 hours 1 minute');
267
+ const dateString = fromNowJSON('2 days 3 hours 1 minute');
268
+
269
+ (dateObject.toJSON() === dateString)
270
+ // dateObject = now() + 2 days 2 hours and 1 minute
271
+ (new Date().getTime() < dateObject.getTime())
272
+ ```
273
+
274
+ By default it will offset the datetime into the future. If the offset strings
275
+ are minus-prefixed (`-`), the date object will be offset into the past. This is
276
+ useful in some corner cases.
277
+
278
+ ```js
279
+ import { fromNow } from '@taskcluster/client-web';
280
+
281
+ const dateObject = fromNow('- 1 year 2 months 3 weeks 5 seconds');
282
+
283
+ // dateObject = now() - 1 year, 2 months, 3 weeks and 5 seconds
284
+ (new Date().getTime() > dateObject.getTime())
285
+ ```
286
+
287
+ The offset string is ignorant of whitespace and case-insensitive. It may also
288
+ optionally be plus-prefixed `+`, if not minus-prefixed. Any `+` prefix will be
289
+ ignored. However, entries in the offset string must be given in order from
290
+ highest to lowest, e.g. `2 years 1 day`. Additionally, various shorthands may be
291
+ employed, as illustrated below.
292
+
293
+ ```
294
+ years, year, yr, y
295
+ months, month, mo
296
+ weeks, week, wk, w
297
+ days, day, d
298
+ hours, hour, hr, h
299
+ minutes, minute, min
300
+ seconds, second, sec, s
301
+ ```
302
+
303
+ The `fromNow` function may also be given a date to be relative to as a second
304
+ argument. This is useful if offsetting the task expiration relative to the the task
305
+ deadline or doing something similar.
306
+
307
+ ```js
308
+ import { fromNow } from '@taskcluster/client-web';
309
+
310
+ const dateObject1 = fromNow('2 days 3 hours');
311
+ // dateObject1 = now() + 2 days and 3 hours
312
+
313
+ const dateObject2 = fromNow('1 year', dateObject1);
314
+ // dateObject2 = now() + 1 year, 2 days and 3 hours
315
+ ```
316
+ ### Generating SlugIDs
317
+ In Node.js you can rely on the `slugid` module to generate slug IDs, but in the browser we
318
+ expose the preferred slug ID generation function as `slugid()`.
319
+
320
+ ```js
321
+ import { slugid } from '@taskcluster/client-web';
322
+
323
+ // Generate new taskId
324
+ const taskId = slugid();
325
+ ```
326
+
327
+ The generates _nice_ random slug IDs.
328
+ ### Inspecting Credentials
329
+
330
+ Your users may find the options for Taskcluster credentials overwhelming. You
331
+ can help by interpreting the credentials for them.
332
+
333
+ The `credentialInformation(credentials, options)` function returns a Promise
334
+ with information about the given credentials:
335
+
336
+ ```js
337
+ {
338
+ clientId: '..', // name of the credential
339
+ type: '..', // type of credential, e.g., "temporary"
340
+ active: '..', // active (valid, not disabled, etc.)
341
+ start: '..', // validity start time (if applicable)
342
+ expiry: '..', // validity end time (if applicable)
343
+ scopes: ['...'] // associated scopes (if available)
344
+ }
345
+ ```
346
+
347
+ The resulting information should *only* be used for presentation purposes, and
348
+ never for access control. This function may fail unexpectedly with invalid
349
+ credentials and performs no cryptographic checks. It is acceptable to use the
350
+ scopes result to determine whether to display UI elements associated with a
351
+ particular scope, as long as the underlying API performs more reliable
352
+ authorization checks.
353
+
354
+ ### Credential Agents
355
+
356
+ This is common server-side when using
357
+ [@taskcluster/client](https://yarnpkg.com/en/package/@taskcluster/client), but
358
+ for web applications the credentials are usually acquired through some
359
+ user-login process. For such cases, the client uses a `credentialAgent` to get
360
+ Taskcluster credentials corresponding to the logged-in user. Agents can be
361
+ shared between multiple clients, and are inherited via `.use`.
362
+
363
+ Any object with an async `getCredentials()` method that returns Taskcluster
364
+ credentials is suitable as a credential agent. The method will be called for
365
+ every Client method call, so it should perform some local caching.
366
+
367
+ ## Compatibility
368
+
369
+ This library is co-versioned with Taskcluster itself.
370
+ That is, a client with version x.y.z contains API methods corresponding to Taskcluster version x.y.z.
371
+ Taskcluster is careful to maintain API compatibility, and guarantees it within a major version.
372
+ That means that any client with version x.* will work against any Taskcluster services at version x.*, and is very likely to work for many other major versions of the Taskcluster services.
373
+ Any incompatibilities are noted in the [Changelog](https://github.com/taskcluster/taskcluster/blob/main/CHANGELOG.md).
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@taskcluster/client-web",
3
+ "version": "88.0.1",
4
+ "main": "src/index.js",
5
+ "author": "Eli Perelman <eli@eliperelman.com>",
6
+ "license": "MPL-2.0",
7
+ "repository": "https://github.com/taskcluster/taskcluster/tree/main/clients/client-web",
8
+ "files": [
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "lint": "eslint src/*.js test/*.js",
13
+ "test": "NODE_OPTIONS=--openssl-legacy-provider karma start --single-run"
14
+ },
15
+ "devDependencies": {
16
+ "@babel/core": "^7.28.0",
17
+ "chai": "^4.3.10",
18
+ "chai-as-promised": "^8.0.1",
19
+ "crypto-js": "^4.2.0",
20
+ "dotenv": "^16.5.0",
21
+ "eslint": "^9.31.0",
22
+ "karma": "^6.4.4",
23
+ "karma-cli": "^2.0.0",
24
+ "karma-coverage": "^2.2.1",
25
+ "karma-firefox-launcher": "^2.1.3",
26
+ "karma-mocha": "^2.0.1",
27
+ "karma-mocha-reporter": "^2.2.5",
28
+ "karma-sourcemap-loader": "^0.4.0",
29
+ "karma-webpack": "^4.0.2",
30
+ "query-string": "^7.0.0",
31
+ "webpack": "^4.47.0",
32
+ "webpack-cli": "^4.0.0"
33
+ },
34
+ "dependencies": {
35
+ "crypto-js": "^4.2.0",
36
+ "hawk": "^9.0.2",
37
+ "query-string": "^7.0.0",
38
+ "taskcluster-lib-urls": "^13.0.0"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }