m3api-rest 0.1.1 → 0.2.0

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 (4) hide show
  1. package/CHANGES.md +51 -0
  2. package/README.md +170 -34
  3. package/index.js +674 -51
  4. package/package.json +1 -1
package/CHANGES.md CHANGED
@@ -5,6 +5,57 @@ This file records the changes in each m3api-rest release.
5
5
  The annotated tag (and GitLab release) for each version also lists the changes,
6
6
  but this file may sometimes contain later improvements (e.g. typo fixes).
7
7
 
8
+ ## v0.2.0 (2026-04-05)
9
+
10
+ - Major functionality update:
11
+ beyond `getJson()` and `postForJson()`,
12
+ m3api-rest now supports the following:
13
+ - request methods:
14
+ - GET
15
+ - POST
16
+ - PUT
17
+ - DELETE
18
+ - PATCH
19
+ - request body content types:
20
+ - `application/json`
21
+ - `application/x-www-form-urlencoded`
22
+ - `multipart/form-data`
23
+ - response body content types:
24
+ - `application/json`
25
+ - `text/plain`
26
+ - `text/html`
27
+
28
+ Yielding the following full list of request functions:
29
+ - `getJson()`
30
+ - `getText()`
31
+ - `getHtml()`
32
+ - `postForJson()`
33
+ - `postForText()`
34
+ - `postForHtml()`
35
+ - `putForJson()`
36
+ - `putForText()`
37
+ - `putForHtml()`
38
+ - `deleteForJson()`
39
+ - `deleteForText()`
40
+ - `deleteForHtml()`
41
+ - `patchForJson()`
42
+ - `patchForText()`
43
+ - `patchForHtml()`
44
+ - BREAKING CHANGE:
45
+ `getJson()` and `postForJson()` can now return `String` instances
46
+ in addition to objects and arrays,
47
+ if the server returns JSON-encoded strings.
48
+ (This is unlikely for MediaWiki core’s REST API endpoints,
49
+ but common in the Wikibase REST API.)
50
+ - BREAKING CHANGE:
51
+ The `body` property of the `RestApiServerError`,
52
+ `RestApiClientError` and `UnexpectedResponseStatus` classes
53
+ is now documented with the type `*` (“any”) rather than `string|Object`.
54
+ The previous type was incorrect – if the server response is JSON,
55
+ m3api-rest just decodes it for the error without checking if it’s an object or not.
56
+ (You almost certainly don’t need to care about this change,
57
+ but it’s technically breaking.)
58
+
8
59
  ## v0.1.1 (2026-04-05)
9
60
 
10
61
  - Updated the library for the new network interface of m3api v1.1.0,
package/README.md CHANGED
@@ -3,22 +3,14 @@
3
3
  m3api-rest is an extension package for [m3api],
4
4
  allowing you to interact with the MediaWiki REST API.
5
5
 
6
- ## Request functions
6
+ ## Usage examples
7
7
 
8
- m3api-rest includes functions to make GET and POST requests that return JSON responses.
9
- Other request methods and response content types will be added in future versions.
10
- All functions start with the HTTP method name (in lowercase);
11
- for GET functions, this is directly followed by the response type (e.g. `getJson`),
12
- while other functions have the word “for” in between (e.g. `postForJson`)
13
- to clarify that this is the response content type, not the request content type.
14
- (The content type of the request body is specified by the type of the body passed into the function;
15
- currently `postForJson` only supports `URLSearchParams` bodies,
16
- but in future versions the same function will support multiple body types.)
8
+ Before digging into all the available features in m3api-rest,
9
+ let’s start with some examples for commonly used functions.
17
10
 
18
11
  ### `getJson`
19
12
 
20
13
  Make a GET request for some JSON data.
21
- Usage examples:
22
14
 
23
15
  ```js
24
16
  import Session from 'm3api';
@@ -46,7 +38,23 @@ See also below for details on the different ways to specify parameters.
46
38
  ### `postForJson`
47
39
 
48
40
  Make a POST request that will return some JSON data.
49
- Usage example:
41
+
42
+ ```js
43
+ const session = new Session( 'test.wikipedia.org', {}, {
44
+ userAgent: 'm3api-rest-README-example',
45
+ accessToken: ...,
46
+ } );
47
+
48
+ const title = 'Test page',
49
+ source = 'Test page contents',
50
+ comment = 'test edit';
51
+ const response = await postForJson( session, '/v1/page', {
52
+ title,
53
+ source,
54
+ comment,
55
+ } );
56
+ console.log( `Created new page with page ID ${ response.id }` );
57
+ ```
50
58
 
51
59
  ```js
52
60
  const wikitext = '== ==\nThis is <span id=id>bad</span> <span id=id>wikitext</span>.';
@@ -56,12 +64,112 @@ const lints = await postForJson( session, '/v1/transform/wikitext/to/lint', new
56
64
  console.log( lints ); // [ { type: 'empty-heading', ... }, { type: 'duplicate-ids', ... } ]
57
65
  ```
58
66
 
59
- (Note that some endpoints, including `POST /v1/page`, cannot be used yet,
60
- because m3api-rest can currently only send `application/x-www-form-urlencoded` request bodies
61
- but those endpoints require `application/json`.
62
- This will be fixed in a future version.)
67
+ ### `putForJson`
68
+
69
+ Make a PUT request that will return some JSON data.
70
+
71
+ ```js
72
+ // session should still have an accessToken, like in the previous example
73
+ const title = 'Test page';
74
+ const page = await getJson( session, path`/v1/page/${ title }` );
75
+ const updatedPage = await putForJson( session, path`/v1/page/${ title }`, {
76
+ source: page.source.replace( /privledge/g, 'privilege' ), // example typo fix
77
+ comment: 'test edit',
78
+ content_model: page.content_model,
79
+ latest: page.latest,
80
+ } );
81
+ console.log( `Edited page with revision ID ${ updatedPage.latest.id }` );
82
+ ```
83
+
84
+ ### `getHtml`
85
+
86
+ Make a GET request that will return HTML.
87
+ The HTML is returned as a `String`;
88
+ if you want to turn it into a DOM, you will need to parse it yourself,
89
+ e.g. using a [`DOMParser`][] in the browser or [jsdom][] on Node.js.
90
+
91
+ ```js
92
+ const title = 'Douglas Adams';
93
+ const html = await getHtml( session, path`/v1/page/${ title }/html` );
94
+ const dom = new DOMParser().parseFromString( html, 'text/html' );
95
+ const rudimentaryLeadParagraph = dom.querySelector( 'p:not( .mw-empty-elt )' );
96
+ ```
97
+
98
+ ### `postForText`, `postForHtml`
99
+
100
+ Make GET requests that will return text or HTML, respectively.
101
+ As with `getHtml`, the result is returned as a `String`.
102
+ You can use these functions to convert between wikitext and HTML, for instance.
103
+
104
+ ```js
105
+ const wikitext = "''Hello, world!''";
106
+ const html = await postForHtml( session, '/v1/transform/wikitext/to/html', {
107
+ wikitext,
108
+ } );
109
+ console.log( html.valueOf() );
110
+ // <!DOCTYPE html>...<i ...>Hello, world!</i>
111
+ const wikitext2 = await postForText( session, '/v1/transform/html/to/wikitext', {
112
+ html,
113
+ } );
114
+ console.log( wikitext2.valueOf() );
115
+ // ''Hello, world!''
116
+ ```
117
+
118
+ ## Request functions
63
119
 
64
- ## Specifying parameters
120
+ m3api-rest supports the following:
121
+
122
+ - request methods:
123
+ - GET
124
+ - POST
125
+ - PUT
126
+ - DELETE
127
+ - PATCH
128
+ - request body content types:
129
+ - `application/json`
130
+ - `application/x-www-form-urlencoded`
131
+ - `multipart/form-data`
132
+ - response body content types:
133
+ - `application/json`
134
+ - `text/plain`
135
+ - `text/html`
136
+
137
+ ### Request methods and response body content types
138
+
139
+ Each combination of request method and response body content type has a separate function,
140
+ whose name begins with the HTTP method name (in lowercase).
141
+ For GET functions, this is directly followed by the response type (e.g. `getJson`),
142
+ while other functions have the word “for” in between (e.g. `postForJson`)
143
+ to clarify that this is the response body content type, not the request body content type.
144
+ The response body content types are “json”, “text”, or “html”,
145
+ and included in the method name with an initial uppercase letter
146
+ (e.g. `getJson`, `postForText`, `postForHtml`).
147
+ Overall, the following functions are available:
148
+
149
+ - `getJson`
150
+ - `getText`
151
+ - `getHtml`
152
+ - `postForJson`
153
+ - `postForText`
154
+ - `postForHtml`
155
+ - `putForJson`
156
+ - `putForText`
157
+ - `putForHtml`
158
+ - `deleteForJson`
159
+ - `deleteForText`
160
+ - `deleteForHtml`
161
+ - `patchForJson`
162
+ - `patchForText`
163
+ - `patchForHtml`
164
+
165
+ ### Request body content types
166
+
167
+ The request body type for non-GET request functions is determined
168
+ by the type of the object passed into the function,
169
+ i.e. the same function (e.g. `postForJson`) can send request bodies encoded in different ways.
170
+ For details, see the section on “body parameters” below.
171
+
172
+ ### Specifying parameters
65
173
 
66
174
  REST API endpoints have different kinds of parameters.
67
175
  Many endpoints take parameters in the path, written like `/v1/page/{title}` (`title` is a parameter);
@@ -69,7 +177,7 @@ many endpoints take query parameters after the path, like `/v1/search/page?q=sea
69
177
  and non-GET endpoints (POST etc.) take body parameters in several encodings.
70
178
  m3api-rest supports several ways to specify these parameters.
71
179
 
72
- ### Path parameters
180
+ #### Path parameters
73
181
 
74
182
  Path parameters can be specified using a tagged template literal (string template)
75
183
  using the `path` function,
@@ -98,7 +206,7 @@ const page = await getJson( session, '/v1/page/{title}', {
98
206
  // makes a request to /v1/page/AC%2FDC?redirect=true
99
207
  ```
100
208
 
101
- ### Query parameters
209
+ #### Query parameters
102
210
 
103
211
  The GET request functions, e.g. `getJson`, take an object with query parameters after the path:
104
212
 
@@ -119,31 +227,57 @@ you should pass them into the `path` as an object:
119
227
 
120
228
  ```js
121
229
  const params = { fakeQueryParam: 'abc' };
122
- await postForJson( path`/v0/fake/endpoint?${ params }`, new URLSearchParams( {
230
+ await postForJson( path`/v0/fake/endpoint?${ params }`, {
123
231
  fakeBodyParam: 'xyz',
124
- } ) );
125
- // makes a request to /v0/fake/endpoint?fakeQueryParam=abc with fakeBodyParam=xyz in the body
232
+ } );
233
+ // makes a request to /v0/fake/endpoint?fakeQueryParam=abc with {"fakeBodyParam":"xyz"} in the body
126
234
  ```
127
235
 
128
236
  This is also possible for GET requests, but you should probably prefer passing the query parameters separately there.
129
237
 
130
- ### Body parameters
238
+ #### Body parameters
131
239
 
132
- The non-GET request functions, e.g. `postForJson`, take a value for the body after the path.
133
- The type of the value encodes the content type with which the body will be sent.
134
- Currently, the only supported type is `URLSearchParams`,
135
- which will send the body as `application/x-www-form-urlencoded`;
136
- support for other body types
137
- (`FormData` for `multipart/form-data`, plain object for `application/json`)
138
- will be added in a future version.
240
+ The non-GET request functions, e.g. `postForJson`, take a value for the request body after the path.
241
+ The type of the value encodes the content type with which the request body will be sent:
242
+ a plain object is sent as `application/json`;
243
+ a `URLSearchParams` instance is sent as `application/x-www-form-urlencoded`;
244
+ and a `FormData` instance is sent as `multipart/form-data`.
139
245
 
140
246
  ```js
141
- const lints = await postForJson( session, '/v1/transform/wikitext/to/lint', new URLSearchParams( {
142
- wikitext: 'some wikitext',
143
- } ) );
144
- /// makes a request to /v1/transform/wikitext/to/lint with wikitext=some%20wikitext
247
+ const wikitext = "''Hello, world!''";
248
+
249
+ // send as application/json
250
+ const html = await postForHtml(
251
+ session,
252
+ '/v1/transform/wikitext/to/html',
253
+ {
254
+ wikitext,
255
+ },
256
+ );
257
+
258
+ // send as application/x-www-form-urlencoded
259
+ const html = await postForHtml(
260
+ session,
261
+ '/v1/transform/wikitext/to/html',
262
+ new URLSearchParams( {
263
+ wikitext,
264
+ } ),
265
+ );
266
+
267
+ // send as multipart/form-data
268
+ const formData = new FormData();
269
+ formData.set( 'wikitext', wikitext );
270
+ const html = await postForHtml(
271
+ session,
272
+ '/v1/transform/wikitext/to/html',
273
+ formData,
274
+ );
145
275
  ```
146
276
 
277
+ Note that not all REST API endpoints accept all request body content types.
278
+ Generally speaking, JSON is your safest bet,
279
+ but you should consult the API endpoint’s documentation to see what request bodies it accepts.
280
+
147
281
  As mentioned above, you can also specify path parameters here.
148
282
 
149
283
  ## License
@@ -153,4 +287,6 @@ By contributing to this software,
153
287
  you agree to publish your contribution under the same license.
154
288
 
155
289
  [m3api]: https://www.npmjs.com/package/m3api
290
+ [`DOMParser`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
291
+ [jsdom]: https://www.npmjs.com/package/jsdom
156
292
  [ISC License]: https://spdx.org/licenses/ISC.html
package/index.js CHANGED
@@ -5,7 +5,7 @@ export class RestApiServerError extends Error {
5
5
 
6
6
  /**
7
7
  * @param {number} status The invalid status code received from the API.
8
- * @param {string|Object} body The response body received from the API.
8
+ * @param {*} body The response body received from the API.
9
9
  */
10
10
  constructor( status, body ) {
11
11
  super( `REST API server error: ${ status }\n\n${ JSON.stringify( body ) }` );
@@ -27,9 +27,9 @@ export class RestApiServerError extends Error {
27
27
  * The body of the response.
28
28
  *
29
29
  * Depending on the response’s content type,
30
- * this may be a string or a JSON-decoded object.
30
+ * this may be a string or a JSON-decoded value.
31
31
  *
32
- * @member {string|Object}
32
+ * @member {*}
33
33
  */
34
34
  this.body = body;
35
35
  }
@@ -43,7 +43,7 @@ export class RestApiClientError extends Error {
43
43
 
44
44
  /**
45
45
  * @param {number} status The invalid status code received from the API.
46
- * @param {string|Object} body The response body received from the API.
46
+ * @param {*} body The response body received from the API.
47
47
  */
48
48
  constructor( status, body ) {
49
49
  super( `REST API client error: ${ status }\n\n${ JSON.stringify( body ) }` );
@@ -65,9 +65,9 @@ export class RestApiClientError extends Error {
65
65
  * The body of the response.
66
66
  *
67
67
  * Depending on the response’s content type,
68
- * this may be a string or a JSON-decoded object.
68
+ * this may be a string or a JSON-decoded value.
69
69
  *
70
- * @member {string|Object}
70
+ * @member {*}
71
71
  */
72
72
  this.body = body;
73
73
  }
@@ -83,7 +83,7 @@ export class UnexpectedResponseStatus extends Error {
83
83
 
84
84
  /**
85
85
  * @param {number} status The unexpected status code received from the API.
86
- * @param {string|Object} body The response body received from the API.
86
+ * @param {*} body The response body received from the API.
87
87
  */
88
88
  constructor( status, body ) {
89
89
  super( `Unexpected REST API response status: ${ status }\n\n${ JSON.stringify( body ) }` );
@@ -105,9 +105,9 @@ export class UnexpectedResponseStatus extends Error {
105
105
  * The body of the response.
106
106
  *
107
107
  * Depending on the response’s content type,
108
- * this may be a string or a JSON-decoded object.
108
+ * this may be a string or a JSON-decoded value.
109
109
  *
110
- * @member {string|Object}
110
+ * @member {*}
111
111
  */
112
112
  this.body = body;
113
113
  }
@@ -292,6 +292,25 @@ export class InvalidPathParams extends Error {
292
292
 
293
293
  }
294
294
 
295
+ /**
296
+ * Get the MIME type (e.g. application/json) of the given response.
297
+ *
298
+ * @private
299
+ * @param {Response} response
300
+ * @return {string} The mime type part of the Content-Type response header,
301
+ * not including parameters like ;charset=utf-8
302
+ */
303
+ function getResponseMimeType( response ) {
304
+ const contentType = response.headers.get( 'Content-Type' );
305
+ const [ mimeType ] = contentType.split( ';', 1 );
306
+ // this accepts some technically invalid Content-Type headers
307
+ // (we don’t check if the part before ; is a valid media-type,
308
+ // i.e. two tokens separated by a slash,
309
+ // cf. https://httpwg.org/specs/rfc9110.html#media.type)
310
+ // because there’s no reason to reject them
311
+ return mimeType;
312
+ }
313
+
295
314
  /**
296
315
  * Determine whether this response contains JSON
297
316
  * according to its headers.
@@ -301,14 +320,37 @@ export class InvalidPathParams extends Error {
301
320
  * @return {boolean}
302
321
  */
303
322
  function isResponseJson( response ) {
304
- const contentType = response.headers.get( 'Content-Type' );
305
- const [ mimeType ] = contentType.split( ';', 1 ); // split off ;charset=utf-8 and suchlike
323
+ const mimeType = getResponseMimeType( response );
306
324
  return mimeType === 'application/json' ||
307
325
  ( mimeType.startsWith( 'application/' ) && mimeType.endsWith( '+json' ) );
308
- // this accepts some technically invalid Content-Type headers
309
- // (we don’t check if the part before +json is a valid subtype / token,
310
- // cf. https://httpwg.org/specs/rfc9110.html#media.type)
311
- // because there’s no reason to reject them
326
+ }
327
+
328
+ /**
329
+ * Determine whether this response contains text
330
+ * (“plain text”, but in practice often wikitext) according to its headers.
331
+ *
332
+ * @private
333
+ * @param {Response} response
334
+ * @return {boolean}
335
+ */
336
+ function isResponseText( response ) {
337
+ const mimeType = getResponseMimeType( response );
338
+ return mimeType === 'text/plain' ||
339
+ ( mimeType.startsWith( 'text/' ) && mimeType.endsWith( '+plain' ) );
340
+ }
341
+
342
+ /**
343
+ * Determine whether this response contains HTML
344
+ * according to its headers.
345
+ *
346
+ * @private
347
+ * @param {Response} response
348
+ * @return {boolean}
349
+ */
350
+ function isResponseHtml( response ) {
351
+ const mimeType = getResponseMimeType( response );
352
+ return mimeType === 'text/html' ||
353
+ ( mimeType.startsWith( 'text/' ) && mimeType.endsWith( '+html' ) );
312
354
  }
313
355
 
314
356
  /**
@@ -316,7 +358,7 @@ function isResponseJson( response ) {
316
358
  *
317
359
  * @private
318
360
  * @param {Response} response
319
- * @return {string|Object}
361
+ * @return {*}
320
362
  */
321
363
  async function getResponseBodyForError( response ) {
322
364
  if ( isResponseJson( response ) ) {
@@ -350,7 +392,7 @@ const responseStatuses = new WeakMap();
350
392
  /**
351
393
  * Get the HTTP status code for this response.
352
394
  *
353
- * @param {Object|Array} response A response object returned by one of the request functions
395
+ * @param {Object|Array|String} response A response object returned by one of the request functions
354
396
  * ({@link getJson} etc.). Note that it must be exactly the object returned by the function
355
397
  * (compared by identity, i.e. `===`), not a serialization of it or anything similar.
356
398
  * @return {number} The HTTP status code, i.e. an integer between 200 and 599.
@@ -369,17 +411,21 @@ export function getResponseStatus( response ) {
369
411
  *
370
412
  * @private
371
413
  * @param {Response} response
372
- * @return {Object|Array}
414
+ * @return {Object|Array|String}
373
415
  */
374
416
  async function getResponseJson( response ) {
375
417
  if ( isResponseJson( response ) ) {
376
418
  const body = await response.json();
419
+ let bodyWithIdentity;
377
420
  if ( typeof body === 'object' && body !== null ) {
378
- responseStatuses.set( body, response.status );
379
- return body;
421
+ bodyWithIdentity = body;
422
+ } else if ( typeof body === 'string' ) {
423
+ bodyWithIdentity = new String( body );
380
424
  } else {
381
425
  throw new InvalidResponseBody( body );
382
426
  }
427
+ responseStatuses.set( bodyWithIdentity, response.status );
428
+ return bodyWithIdentity;
383
429
  } else {
384
430
  throw new IncompatibleResponseType(
385
431
  'application/json',
@@ -389,6 +435,49 @@ async function getResponseJson( response ) {
389
435
  }
390
436
  }
391
437
 
438
+ /**
439
+ * Get the body of the response and check that it’s valid text
440
+ * (“plain text”, but in practice often wikitext.)
441
+ *
442
+ * @private
443
+ * @param {Response} response
444
+ * @return {String}
445
+ */
446
+ async function getResponseText( response ) {
447
+ if ( isResponseText( response ) ) {
448
+ const text = new String( await response.text() );
449
+ responseStatuses.set( text, response.status );
450
+ return text;
451
+ } else {
452
+ throw new IncompatibleResponseType(
453
+ 'text/plain',
454
+ response.headers.get( 'Content-Type' ),
455
+ await getResponseBodyForError( response ),
456
+ );
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Get the body of the response and check that it’s valid HTML.
462
+ *
463
+ * @private
464
+ * @param {Response} response
465
+ * @return {String}
466
+ */
467
+ async function getResponseHtml( response ) {
468
+ if ( isResponseHtml( response ) ) {
469
+ const html = new String( await response.text() );
470
+ responseStatuses.set( html, response.status );
471
+ return html;
472
+ } else {
473
+ throw new IncompatibleResponseType(
474
+ 'text/html',
475
+ response.headers.get( 'Content-Type' ),
476
+ await getResponseBodyForError( response ),
477
+ );
478
+ }
479
+ }
480
+
392
481
  /**
393
482
  * Encode a path for a REST API endpoint.
394
483
  *
@@ -439,7 +528,7 @@ export function path( strings, ...values ) {
439
528
  *
440
529
  * @private
441
530
  * @param {string} path The path with optional `{param}` expressions.
442
- * @param {Object|URLSearchParams} params The input params. Not modified.
531
+ * @param {Object|URLSearchParams|FormData} params The input params. Not modified.
443
532
  * @return {Object} The potentially modified path and params.
444
533
  */
445
534
  function substitutePathParams( path, params ) {
@@ -463,6 +552,38 @@ function substitutePathParams( path, params ) {
463
552
  }
464
553
  copiedParams.delete( paramName );
465
554
  return encodeURIComponent( values[ 0 ] );
555
+ } else if ( params instanceof FormData ) {
556
+ if ( copiedParams === null ) {
557
+ copiedParams = new FormData();
558
+ for ( const [ name, value ] of params.entries() ) {
559
+ copiedParams.append( name, value );
560
+ }
561
+ }
562
+ const values = copiedParams.getAll( paramName );
563
+ if ( values.length !== 1 ) {
564
+ throw new InvalidPathParams(
565
+ values.length === 0 ?
566
+ `Unspecified path param ${ match }` :
567
+ `Ambiguous path param ${ match }`,
568
+ path,
569
+ paramName,
570
+ params,
571
+ );
572
+ }
573
+ copiedParams.delete( paramName );
574
+ const value = values[ 0 ];
575
+ if ( value instanceof Blob ) {
576
+ // in practice, value will almost always be a File here,
577
+ // as FormData will turn any Blob into a File (except on old Node versions),
578
+ // hence the unspecific “Blob or File” in the message
579
+ throw new InvalidPathParams(
580
+ `Path param ${ match } cannot be a Blob or File`,
581
+ path,
582
+ paramName,
583
+ params,
584
+ );
585
+ }
586
+ return encodeURIComponent( value );
466
587
  } else {
467
588
  if ( copiedParams === null ) {
468
589
  copiedParams = { ...params };
@@ -484,6 +605,44 @@ function substitutePathParams( path, params ) {
484
605
  return { path: substitutedPath, params: copiedParams || params };
485
606
  }
486
607
 
608
+ /**
609
+ * Convenience function to add a request header to the given `fetch()` options.
610
+ *
611
+ * @private
612
+ * @param {RequestInit} fetchOptions
613
+ * @param {string} name
614
+ * @param {string} value
615
+ */
616
+ function addHeaderToOptions( fetchOptions, name, value ) {
617
+ fetchOptions.headers = new Headers( fetchOptions.headers );
618
+ fetchOptions.headers.set( name, value );
619
+ }
620
+
621
+ /**
622
+ * Prepare a GET request for the given parameters.
623
+ * Encodes the params into the URL and sets up request headers.
624
+ *
625
+ * @private
626
+ * @param {Session} session
627
+ * @param {string} path
628
+ * @param {Object} params
629
+ * @param {Options} options
630
+ * @return {Array} url and fetchOptions
631
+ */
632
+ function prepareGetRequest( session, path, params, options ) {
633
+ ( { path, params } = substitutePathParams( path, params ) );
634
+ const restUrl = session.apiUrl.replace( /api\.php$/, 'rest.php' );
635
+ const url = new URL( restUrl + path );
636
+ for ( const [ name, value ] of Object.entries( params || {} ) ) {
637
+ url.searchParams.append( name, value );
638
+ }
639
+ const fetchOptions = {
640
+ method: 'GET',
641
+ headers: session.getRequestHeaders( options ),
642
+ };
643
+ return [ url, fetchOptions ];
644
+ }
645
+
487
646
  /**
488
647
  * Make a GET request to a REST API endpoint and return the JSON-decoded body.
489
648
  *
@@ -494,25 +653,129 @@ function substitutePathParams( path, params ) {
494
653
  * @param {Object} [params] Parameters for the request URL.
495
654
  * This may include both parameters for the path and query parameters.
496
655
  * @param {Options} [options] Request options.
497
- * @return {Object|Array} The body of the API response, JSON-decoded.
656
+ * @return {Object|Array|String} The body of the API response, JSON-decoded.
657
+ * If the API returned a string, then for technical reasons it will be returned
658
+ * as a `String` instance, not a primitive string value;
659
+ * you can mostly use it interchangeably with an ordinary string,
660
+ * or turn it into one by calling its `.valueOf()` method.
498
661
  */
499
662
  export async function getJson( session, path, params, options = {} ) {
663
+ const [ url, fetchOptions ] = prepareGetRequest( session, path, params, options );
664
+ addHeaderToOptions( fetchOptions, 'accept', 'application/json' );
665
+ const response = await session.fetch( url, fetchOptions );
666
+ await checkResponseStatus( response );
667
+ return await getResponseJson( response );
668
+ }
669
+
670
+ /**
671
+ * Make a GET request to a REST API endpoint and return the text body.
672
+ *
673
+ * @param {Session} session The m3api session to use for this request.
674
+ * @param {string} path The resource path.
675
+ * Does not include the domain, script path, or `rest.php` endpoint.
676
+ * Use the {@link path} tag function to build the path.
677
+ * @param {Object} [params] Parameters for the request URL.
678
+ * This may include both parameters for the path and query parameters.
679
+ * @param {Options} [options] Request options.
680
+ * @return {String} The body of the API response.
681
+ * For technical reasons, this is a `String` instance, not a primitive string value;
682
+ * you can mostly use it interchangeably with an ordinary string,
683
+ * or turn it into one by calling its `.valueOf()` method.
684
+ */
685
+ export async function getText( session, path, params, options = {} ) {
686
+ const [ url, fetchOptions ] = prepareGetRequest( session, path, params, options );
687
+ addHeaderToOptions( fetchOptions, 'accept', 'text/plain' );
688
+ const response = await session.fetch( url, fetchOptions );
689
+ await checkResponseStatus( response );
690
+ return await getResponseText( response );
691
+ }
692
+
693
+ /**
694
+ * Make a GET request to a REST API endpoint and return the HTML body.
695
+ *
696
+ * @param {Session} session The m3api session to use for this request.
697
+ * @param {string} path The resource path, e.g. `/v1/page/{title}/html`.
698
+ * Does not include the domain, script path, or `rest.php` endpoint.
699
+ * Use the {@link path} tag function to build the path.
700
+ * @param {Object} [params] Parameters for the request URL.
701
+ * This may include both parameters for the path and query parameters.
702
+ * @param {Options} [options] Request options.
703
+ * @return {String} The body of the API response.
704
+ * For technical reasons, this is a `String` instance, not a primitive string value;
705
+ * you can mostly use it interchangeably with an ordinary string,
706
+ * or turn it into one by calling its `.valueOf()` method.
707
+ */
708
+ export async function getHtml( session, path, params, options = {} ) {
709
+ const [ url, fetchOptions ] = prepareGetRequest( session, path, params, options );
710
+ addHeaderToOptions( fetchOptions, 'accept', 'text/html' );
711
+ const response = await session.fetch( url, fetchOptions );
712
+ await checkResponseStatus( response );
713
+ return await getResponseHtml( response );
714
+ }
715
+
716
+ /**
717
+ * Encode a body for the request to the server.
718
+ *
719
+ * @param {Object|URLSearchParams|FormData} body
720
+ * @return {RequestInit} To be mixed into the `fetch()` options.
721
+ */
722
+ function encodeBody( body ) {
723
+ if ( body instanceof URLSearchParams || body instanceof FormData ) {
724
+ return { body };
725
+ } else {
726
+ return {
727
+ body: JSON.stringify( body ),
728
+ headers: {
729
+ 'Content-Type': 'application/json',
730
+ },
731
+ };
732
+ }
733
+ }
734
+
735
+ /**
736
+ * Prepare a non-GET request for the given parameters.
737
+ * Encodes the params into the URL and body and sets up request headers.
738
+ * The caller must set the specific method afterwards.
739
+ *
740
+ * @private
741
+ * @param {Session} session
742
+ * @param {string} path
743
+ * @param {Object} params
744
+ * @param {Options} options
745
+ * @return {Array} url and fetchOptions
746
+ */
747
+ function prepareRequestWithBody( session, path, params, options ) {
500
748
  ( { path, params } = substitutePathParams( path, params ) );
501
749
  const restUrl = session.apiUrl.replace( /api\.php$/, 'rest.php' );
502
750
  const url = new URL( restUrl + path );
503
- for ( const [ name, value ] of Object.entries( params || {} ) ) {
504
- url.searchParams.append( name, value );
505
- }
506
- const headers = {
507
- accept: 'application/json',
508
- ...session.getRequestHeaders( options ),
751
+ const fetchOptions = {
752
+ method: '', // trigger a TypeError in fetch() if caller does not override method
753
+ ...encodeBody( params ),
509
754
  };
510
- const response = await session.fetch( url, {
511
- method: 'GET',
512
- headers,
513
- } );
514
- await checkResponseStatus( response );
515
- return await getResponseJson( response );
755
+ fetchOptions.headers = new Headers( fetchOptions.headers );
756
+ for ( const [ name, value ] of Object.entries( session.getRequestHeaders( options ) ) ) {
757
+ fetchOptions.headers.set( name, value );
758
+ }
759
+ return [ url, fetchOptions ];
760
+ }
761
+
762
+ /**
763
+ * Prepare a POST request for the given parameters.
764
+ * Encodes the params into the URL and body and sets up request headers.
765
+ *
766
+ * @private
767
+ * @param {Session} session
768
+ * @param {string} path
769
+ * @param {Object} params
770
+ * @param {Options} options
771
+ * @return {Array} url and fetchOptions
772
+ */
773
+ function preparePostRequest( session, path, params, options ) {
774
+ const [ url, fetchOptions ] = prepareRequestWithBody( session, path, params, options );
775
+ return [ url, {
776
+ ...fetchOptions,
777
+ method: 'POST',
778
+ } ];
516
779
  }
517
780
 
518
781
  /**
@@ -522,27 +785,387 @@ export async function getJson( session, path, params, options = {} ) {
522
785
  * @param {string} path The resource path, e.g. `/v1/page`.
523
786
  * Does not include the domain, script path, or `rest.php` endpoint.
524
787
  * Use the {@link path} tag function to build the path.
525
- * @param {URLSearchParams} params The request body.
526
- * Will be sent using the `application/x-www-form-urlencoded` content type.
527
- * (Future versions of this library will support additional request body content types,
528
- * but that requires changes to m3api first.)
788
+ * @param {Object|URLSearchParams|FormData} params The request body.
789
+ * An Object will be sent using the `application/json` content type;
790
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
791
+ * FormData will be sent using the `multipart/form-data` content type.
529
792
  * You may also include parameters for the path here.
530
793
  * @param {Options} [options] Request options.
531
- * @return {Object|Array} The body of the API response, JSON-decoded.
794
+ * @return {Object|Array|String} The body of the API response, JSON-decoded.
795
+ * If the API returned a string, then for technical reasons it will be returned
796
+ * as a `String` instance, not a primitive string value;
797
+ * you can mostly use it interchangeably with an ordinary string,
798
+ * or turn it into one by calling its `.valueOf()` method.
532
799
  */
533
800
  export async function postForJson( session, path, params, options = {} ) {
534
- ( { path, params } = substitutePathParams( path, params ) );
535
- const restUrl = session.apiUrl.replace( /api\.php$/, 'rest.php' );
536
- const url = new URL( restUrl + path );
537
- const headers = {
538
- // accept: 'application/json', // skip this for now due to T412610
539
- ...session.getRequestHeaders( options ),
540
- };
541
- const response = await session.fetch( url, {
542
- method: 'POST',
543
- headers,
544
- body: params,
545
- } );
801
+ const [ url, fetchOptions ] = preparePostRequest( session, path, params, options );
802
+ // skip this for now due to T412610
803
+ // addHeaderToOptions( fetchOptions, 'accept', 'application/json' );
804
+ const response = await session.fetch( url, fetchOptions );
805
+ await checkResponseStatus( response );
806
+ return await getResponseJson( response );
807
+ }
808
+
809
+ /**
810
+ * Make a POST request to a REST API endpoint and return the text body.
811
+ *
812
+ * @param {Session} session The m3api session to use for this request.
813
+ * @param {string} path The resource path, e.g. `/v1/transform/html/to/wikitext`.
814
+ * Does not include the domain, script path, or `rest.php` endpoint.
815
+ * Use the {@link path} tag function to build the path.
816
+ * @param {Object|URLSearchParams|FormData} params The request body.
817
+ * An Object will be sent using the `application/json` content type;
818
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
819
+ * FormData will be sent using the `multipart/form-data` content type.
820
+ * You may also include parameters for the path here.
821
+ * @param {Options} [options] Request options.
822
+ * @return {String} The body of the API response.
823
+ * For technical reasons, this is a `String` instance, not a primitive string value;
824
+ * you can mostly use it interchangeably with an ordinary string,
825
+ * or turn it into one by calling its `.valueOf()` method.
826
+ */
827
+ export async function postForText( session, path, params, options = {} ) {
828
+ const [ url, fetchOptions ] = preparePostRequest( session, path, params, options );
829
+ addHeaderToOptions( fetchOptions, 'accept', 'text/plain' );
830
+ const response = await session.fetch( url, fetchOptions );
831
+ await checkResponseStatus( response );
832
+ return await getResponseText( response );
833
+ }
834
+
835
+ /**
836
+ * Make a POST request to a REST API endpoint and return the HTML body.
837
+ *
838
+ * @param {Session} session The m3api session to use for this request.
839
+ * @param {string} path The resource path, e.g. `/v1/transform/wikitext/to/html`.
840
+ * Does not include the domain, script path, or `rest.php` endpoint.
841
+ * Use the {@link path} tag function to build the path.
842
+ * @param {Object|URLSearchParams|FormData} params The request body.
843
+ * An Object will be sent using the `application/json` content type;
844
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
845
+ * FormData will be sent using the `multipart/form-data` content type.
846
+ * You may also include parameters for the path here.
847
+ * @param {Options} [options] Request options.
848
+ * @return {String} The body of the API response.
849
+ * For technical reasons, this is a `String` instance, not a primitive string value;
850
+ * you can mostly use it interchangeably with an ordinary string,
851
+ * or turn it into one by calling its `.valueOf()` method.
852
+ */
853
+ export async function postForHtml( session, path, params, options = {} ) {
854
+ const [ url, fetchOptions ] = preparePostRequest( session, path, params, options );
855
+ addHeaderToOptions( fetchOptions, 'accept', 'text/html' );
856
+ const response = await session.fetch( url, fetchOptions );
857
+ await checkResponseStatus( response );
858
+ return await getResponseHtml( response );
859
+ }
860
+
861
+ /**
862
+ * Prepare a PUT request for the given parameters.
863
+ * Encodes the params into the URL and body and sets up request headers.
864
+ *
865
+ * @private
866
+ * @param {Session} session
867
+ * @param {string} path
868
+ * @param {Object} params
869
+ * @param {Options} options
870
+ * @return {Array} url and fetchOptions
871
+ */
872
+ function preparePutRequest( session, path, params, options ) {
873
+ const [ url, fetchOptions ] = prepareRequestWithBody( session, path, params, options );
874
+ return [ url, {
875
+ ...fetchOptions,
876
+ method: 'PUT',
877
+ } ];
878
+ }
879
+
880
+ /**
881
+ * Make a PUT request to a REST API endpoint and return the JSON-decoded body.
882
+ *
883
+ * @param {Session} session The m3api session to use for this request.
884
+ * @param {string} path The resource path, e.g. `/v1/page/{title}`.
885
+ * Does not include the domain, script path, or `rest.php` endpoint.
886
+ * Use the {@link path} tag function to build the path.
887
+ * @param {Object|URLSearchParams|FormData} params The request body.
888
+ * An Object will be sent using the `application/json` content type;
889
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
890
+ * FormData will be sent using the `multipart/form-data` content type.
891
+ * You may also include parameters for the path here.
892
+ * Note that all known PUT endpoints only accept JSON bodies,
893
+ * so URLSearchParams or FormData params are unlikely to be useful.
894
+ * @param {Options} [options] Request options.
895
+ * @return {Object|Array|String} The body of the API response, JSON-decoded.
896
+ * If the API returned a string, then for technical reasons it will be returned
897
+ * as a `String` instance, not a primitive string value;
898
+ * you can mostly use it interchangeably with an ordinary string,
899
+ * or turn it into one by calling its `.valueOf()` method.
900
+ */
901
+ export async function putForJson( session, path, params, options = {} ) {
902
+ const [ url, fetchOptions ] = preparePutRequest( session, path, params, options );
903
+ addHeaderToOptions( fetchOptions, 'accept', 'application/json' );
904
+ const response = await session.fetch( url, fetchOptions );
905
+ await checkResponseStatus( response );
906
+ return await getResponseJson( response );
907
+ }
908
+
909
+ /**
910
+ * Make a PUT request to a REST API endpoint and return the text body.
911
+ *
912
+ * @param {Session} session The m3api session to use for this request.
913
+ * @param {string} path The resource path.
914
+ * Does not include the domain, script path, or `rest.php` endpoint.
915
+ * Use the {@link path} tag function to build the path.
916
+ * @param {Object|URLSearchParams|FormData} params The request body.
917
+ * An Object will be sent using the `application/json` content type;
918
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
919
+ * FormData will be sent using the `multipart/form-data` content type.
920
+ * You may also include parameters for the path here.
921
+ * Note that all known PUT endpoints only accept JSON bodies,
922
+ * so URLSearchParams or FormData params are unlikely to be useful.
923
+ * @param {Options} [options] Request options.
924
+ * @return {String} The body of the API response.
925
+ * For technical reasons, this is a `String` instance, not a primitive string value;
926
+ * you can mostly use it interchangeably with an ordinary string,
927
+ * or turn it into one by calling its `.valueOf()` method.
928
+ */
929
+ export async function putForText( session, path, params, options = {} ) {
930
+ const [ url, fetchOptions ] = preparePutRequest( session, path, params, options );
931
+ addHeaderToOptions( fetchOptions, 'accept', 'text/plain' );
932
+ const response = await session.fetch( url, fetchOptions );
933
+ await checkResponseStatus( response );
934
+ return await getResponseText( response );
935
+ }
936
+
937
+ /**
938
+ * Make a PUT request to a REST API endpoint and return the HTML body.
939
+ *
940
+ * @param {Session} session The m3api session to use for this request.
941
+ * @param {string} path The resource path.
942
+ * Does not include the domain, script path, or `rest.php` endpoint.
943
+ * Use the {@link path} tag function to build the path.
944
+ * @param {Object|URLSearchParams|FormData} params The request body.
945
+ * An Object will be sent using the `application/json` content type;
946
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
947
+ * FormData will be sent using the `multipart/form-data` content type.
948
+ * You may also include parameters for the path here.
949
+ * Note that all known PUT endpoints only accept JSON bodies,
950
+ * so URLSearchParams or FormData params are unlikely to be useful.
951
+ * @param {Options} [options] Request options.
952
+ * @return {String} The body of the API response.
953
+ * For technical reasons, this is a `String` instance, not a primitive string value;
954
+ * you can mostly use it interchangeably with an ordinary string,
955
+ * or turn it into one by calling its `.valueOf()` method.
956
+ */
957
+ export async function putForHtml( session, path, params, options = {} ) {
958
+ const [ url, fetchOptions ] = preparePutRequest( session, path, params, options );
959
+ addHeaderToOptions( fetchOptions, 'accept', 'text/html' );
960
+ const response = await session.fetch( url, fetchOptions );
961
+ await checkResponseStatus( response );
962
+ return await getResponseHtml( response );
963
+ }
964
+
965
+ /**
966
+ * Prepare a DELETE request for the given parameters.
967
+ * Encodes the params into the URL and body and sets up request headers.
968
+ *
969
+ * @private
970
+ * @param {Session} session
971
+ * @param {string} path
972
+ * @param {Object} params
973
+ * @param {Options} options
974
+ * @return {Array} url and fetchOptions
975
+ */
976
+ function prepareDeleteRequest( session, path, params, options ) {
977
+ const [ url, fetchOptions ] = prepareRequestWithBody( session, path, params, options );
978
+ return [ url, {
979
+ ...fetchOptions,
980
+ method: 'DELETE',
981
+ } ];
982
+ }
983
+
984
+ /**
985
+ * Make a DELETE request to a REST API endpoint and return the JSON-decoded body.
986
+ *
987
+ * @param {Session} session The m3api session to use for this request.
988
+ * @param {string} path The resource path, e.g. `/v1/page/{title}`.
989
+ * Does not include the domain, script path, or `rest.php` endpoint.
990
+ * Use the {@link path} tag function to build the path.
991
+ * @param {Object|URLSearchParams|FormData} params The request body.
992
+ * An Object will be sent using the `application/json` content type;
993
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
994
+ * FormData will be sent using the `multipart/form-data` content type.
995
+ * You may also include parameters for the path here.
996
+ * Note that all known DELETE endpoints only accept JSON bodies,
997
+ * so URLSearchParams or FormData params are unlikely to be useful.
998
+ * @param {Options} [options] Request options.
999
+ * @return {Object|Array|String} The body of the API response, JSON-decoded.
1000
+ * If the API returned a string, then for technical reasons it will be returned
1001
+ * as a `String` instance, not a primitive string value;
1002
+ * you can mostly use it interchangeably with an ordinary string,
1003
+ * or turn it into one by calling its `.valueOf()` method.
1004
+ */
1005
+ export async function deleteForJson( session, path, params, options = {} ) {
1006
+ const [ url, fetchOptions ] = prepareDeleteRequest( session, path, params, options );
1007
+ addHeaderToOptions( fetchOptions, 'accept', 'application/json' );
1008
+ const response = await session.fetch( url, fetchOptions );
1009
+ await checkResponseStatus( response );
1010
+ return await getResponseJson( response );
1011
+ }
1012
+
1013
+ /**
1014
+ * Make a DELETE request to a REST API endpoint and return the text body.
1015
+ *
1016
+ * @param {Session} session The m3api session to use for this request.
1017
+ * @param {string} path The resource path.
1018
+ * Does not include the domain, script path, or `rest.php` endpoint.
1019
+ * Use the {@link path} tag function to build the path.
1020
+ * @param {Object|URLSearchParams|FormData} params The request body.
1021
+ * An Object will be sent using the `application/json` content type;
1022
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
1023
+ * FormData will be sent using the `multipart/form-data` content type.
1024
+ * You may also include parameters for the path here.
1025
+ * Note that all known DELETE endpoints only accept JSON bodies,
1026
+ * so URLSearchParams or FormData params are unlikely to be useful.
1027
+ * @param {Options} [options] Request options.
1028
+ * @return {String} The body of the API response.
1029
+ * For technical reasons, this is a `String` instance, not a primitive string value;
1030
+ * you can mostly use it interchangeably with an ordinary string,
1031
+ * or turn it into one by calling its `.valueOf()` method.
1032
+ */
1033
+ export async function deleteForText( session, path, params, options = {} ) {
1034
+ const [ url, fetchOptions ] = prepareDeleteRequest( session, path, params, options );
1035
+ addHeaderToOptions( fetchOptions, 'accept', 'text/plain' );
1036
+ const response = await session.fetch( url, fetchOptions );
1037
+ await checkResponseStatus( response );
1038
+ return await getResponseText( response );
1039
+ }
1040
+
1041
+ /**
1042
+ * Make a DELETE request to a REST API endpoint and return the HTML body.
1043
+ *
1044
+ * @param {Session} session The m3api session to use for this request.
1045
+ * @param {string} path The resource path.
1046
+ * Does not include the domain, script path, or `rest.php` endpoint.
1047
+ * Use the {@link path} tag function to build the path.
1048
+ * @param {Object|URLSearchParams|FormData} params The request body.
1049
+ * An Object will be sent using the `application/json` content type;
1050
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
1051
+ * FormData will be sent using the `multipart/form-data` content type.
1052
+ * You may also include parameters for the path here.
1053
+ * Note that all known DELETE endpoints only accept JSON bodies,
1054
+ * so URLSearchParams or FormData params are unlikely to be useful.
1055
+ * @param {Options} [options] Request options.
1056
+ * @return {String} The body of the API response.
1057
+ * For technical reasons, this is a `String` instance, not a primitive string value;
1058
+ * you can mostly use it interchangeably with an ordinary string,
1059
+ * or turn it into one by calling its `.valueOf()` method.
1060
+ */
1061
+ export async function deleteForHtml( session, path, params, options = {} ) {
1062
+ const [ url, fetchOptions ] = prepareDeleteRequest( session, path, params, options );
1063
+ addHeaderToOptions( fetchOptions, 'accept', 'text/html' );
1064
+ const response = await session.fetch( url, fetchOptions );
1065
+ await checkResponseStatus( response );
1066
+ return await getResponseHtml( response );
1067
+ }
1068
+
1069
+ /**
1070
+ * Prepare a PATCH request for the given parameters.
1071
+ * Encodes the params into the URL and body and sets up request headers.
1072
+ *
1073
+ * @private
1074
+ * @param {Session} session
1075
+ * @param {string} path
1076
+ * @param {Object} params
1077
+ * @param {Options} options
1078
+ * @return {Array} url and fetchOptions
1079
+ */
1080
+ function preparePatchRequest( session, path, params, options ) {
1081
+ const [ url, fetchOptions ] = prepareRequestWithBody( session, path, params, options );
1082
+ return [ url, {
1083
+ ...fetchOptions,
1084
+ method: 'PATCH',
1085
+ } ];
1086
+ }
1087
+
1088
+ /**
1089
+ * Make a PATCH request to a REST API endpoint and return the JSON-decoded body.
1090
+ *
1091
+ * @param {Session} session The m3api session to use for this request.
1092
+ * @param {string} path The resource path, e.g. `/v1/page/{title}`.
1093
+ * Does not include the domain, script path, or `rest.php` endpoint.
1094
+ * Use the {@link path} tag function to build the path.
1095
+ * @param {Object|URLSearchParams|FormData} params The request body.
1096
+ * An Object will be sent using the `application/json` content type;
1097
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
1098
+ * FormData will be sent using the `multipart/form-data` content type.
1099
+ * You may also include parameters for the path here.
1100
+ * Note that all known PATCH endpoints only accept JSON bodies,
1101
+ * so URLSearchParams or FormData params are unlikely to be useful.
1102
+ * @param {Options} [options] Request options.
1103
+ * @return {Object|Array|String} The body of the API response, JSON-decoded.
1104
+ * If the API returned a string, then for technical reasons it will be returned
1105
+ * as a `String` instance, not a primitive string value;
1106
+ * you can mostly use it interchangeably with an ordinary string,
1107
+ * or turn it into one by calling its `.valueOf()` method.
1108
+ */
1109
+ export async function patchForJson( session, path, params, options = {} ) {
1110
+ const [ url, fetchOptions ] = preparePatchRequest( session, path, params, options );
1111
+ addHeaderToOptions( fetchOptions, 'accept', 'application/json' );
1112
+ const response = await session.fetch( url, fetchOptions );
546
1113
  await checkResponseStatus( response );
547
1114
  return await getResponseJson( response );
548
1115
  }
1116
+
1117
+ /**
1118
+ * Make a PATCH request to a REST API endpoint and return the text body.
1119
+ *
1120
+ * @param {Session} session The m3api session to use for this request.
1121
+ * @param {string} path The resource path.
1122
+ * Does not include the domain, script path, or `rest.php` endpoint.
1123
+ * Use the {@link path} tag function to build the path.
1124
+ * @param {Object|URLSearchParams|FormData} params The request body.
1125
+ * An Object will be sent using the `application/json` content type;
1126
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
1127
+ * FormData will be sent using the `multipart/form-data` content type.
1128
+ * You may also include parameters for the path here.
1129
+ * Note that all known PATCH endpoints only accept JSON bodies,
1130
+ * so URLSearchParams or FormData params are unlikely to be useful.
1131
+ * @param {Options} [options] Request options.
1132
+ * @return {String} The body of the API response.
1133
+ * For technical reasons, this is a `String` instance, not a primitive string value;
1134
+ * you can mostly use it interchangeably with an ordinary string,
1135
+ * or turn it into one by calling its `.valueOf()` method.
1136
+ */
1137
+ export async function patchForText( session, path, params, options = {} ) {
1138
+ const [ url, fetchOptions ] = preparePatchRequest( session, path, params, options );
1139
+ addHeaderToOptions( fetchOptions, 'accept', 'text/plain' );
1140
+ const response = await session.fetch( url, fetchOptions );
1141
+ await checkResponseStatus( response );
1142
+ return await getResponseText( response );
1143
+ }
1144
+
1145
+ /**
1146
+ * Make a PATCH request to a REST API endpoint and return the HTML body.
1147
+ *
1148
+ * @param {Session} session The m3api session to use for this request.
1149
+ * @param {string} path The resource path.
1150
+ * Does not include the domain, script path, or `rest.php` endpoint.
1151
+ * Use the {@link path} tag function to build the path.
1152
+ * @param {Object|URLSearchParams|FormData} params The request body.
1153
+ * An Object will be sent using the `application/json` content type;
1154
+ * URLSearchParams will be sent using the `application/x-www-form-urlencoded` content type;
1155
+ * FormData will be sent using the `multipart/form-data` content type.
1156
+ * You may also include parameters for the path here.
1157
+ * Note that all known PATCH endpoints only accept JSON bodies,
1158
+ * so URLSearchParams or FormData params are unlikely to be useful.
1159
+ * @param {Options} [options] Request options.
1160
+ * @return {String} The body of the API response.
1161
+ * For technical reasons, this is a `String` instance, not a primitive string value;
1162
+ * you can mostly use it interchangeably with an ordinary string,
1163
+ * or turn it into one by calling its `.valueOf()` method.
1164
+ */
1165
+ export async function patchForHtml( session, path, params, options = {} ) {
1166
+ const [ url, fetchOptions ] = preparePatchRequest( session, path, params, options );
1167
+ addHeaderToOptions( fetchOptions, 'accept', 'text/html' );
1168
+ const response = await session.fetch( url, fetchOptions );
1169
+ await checkResponseStatus( response );
1170
+ return await getResponseHtml( response );
1171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "m3api-rest",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "m3api extension package to interact with the MediaWiki REST API.",
5
5
  "main": "index.js",
6
6
  "type": "module",