m3api-rest 0.1.0 → 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.
- package/CHANGES.md +61 -0
- package/README.md +170 -34
- package/index.js +774 -74
- package/package.json +3 -3
package/CHANGES.md
CHANGED
|
@@ -5,6 +5,67 @@ 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
|
+
|
|
59
|
+
## v0.1.1 (2026-04-05)
|
|
60
|
+
|
|
61
|
+
- Updated the library for the new network interface of m3api v1.1.0,
|
|
62
|
+
so that it can be used together with that version.
|
|
63
|
+
(This also unlocks many new features in m3api-rest,
|
|
64
|
+
which will be implemented in a subsequent release.)
|
|
65
|
+
- Introduced a new error type, `IncompatibleResponseType`,
|
|
66
|
+
thrown if the server responds with an incompatible response type.
|
|
67
|
+
- Updated dependencies.
|
|
68
|
+
|
|
8
69
|
## v0.1.0 (2026-01-01)
|
|
9
70
|
|
|
10
71
|
Initial release, including:
|
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
|
-
##
|
|
6
|
+
## Usage examples
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 }`,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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 {
|
|
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
|
|
30
|
+
* this may be a string or a JSON-decoded value.
|
|
31
31
|
*
|
|
32
|
-
* @member {
|
|
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 {
|
|
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
|
|
68
|
+
* this may be a string or a JSON-decoded value.
|
|
69
69
|
*
|
|
70
|
-
* @member {
|
|
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 {
|
|
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,61 @@ 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
|
|
108
|
+
* this may be a string or a JSON-decoded value.
|
|
109
109
|
*
|
|
110
|
-
* @member {
|
|
110
|
+
* @member {*}
|
|
111
|
+
*/
|
|
112
|
+
this.body = body;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* An error representing a HTTP response with a content type incompatible with that requested.
|
|
119
|
+
* For example, if the API responded with HTML despite JSON having been requested.
|
|
120
|
+
* Note that, depending on server behavior, this condition may also be represented
|
|
121
|
+
* by a {@link RestApiClientError} with the status 406 Not Acceptable;
|
|
122
|
+
* {@link IncompatibleResponseType} is thrown by m3api-rest if the server responded
|
|
123
|
+
* with a successful status that nevertheless does not match the requested type.
|
|
124
|
+
*/
|
|
125
|
+
export class IncompatibleResponseType extends Error {
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} expectedType The expected MIME type.
|
|
129
|
+
* @param {string} actualType The actual Content-Type response header.
|
|
130
|
+
* @param {*} body The response body received from the API.
|
|
131
|
+
*/
|
|
132
|
+
constructor( expectedType, actualType, body ) {
|
|
133
|
+
super( `Incompatible REST API response type: expected ${ expectedType }, got ${ actualType }` );
|
|
134
|
+
|
|
135
|
+
if ( Error.captureStackTrace ) {
|
|
136
|
+
Error.captureStackTrace( this, IncompatibleResponseType );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.name = 'IncompatibleResponseType';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The MIME type expected by m3api-rest based on the method,
|
|
143
|
+
* e.g. 'application/json' for {@link getJson} and related functions.
|
|
144
|
+
*
|
|
145
|
+
* @member {string}
|
|
146
|
+
*/
|
|
147
|
+
this.expectedType = expectedType;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The actual Content-Type header returned by the server,
|
|
151
|
+
* e.g. 'text/html; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/HTML/2.8.0"'.
|
|
152
|
+
*
|
|
153
|
+
* @member {string}
|
|
154
|
+
*/
|
|
155
|
+
this.actualType = actualType;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The response body.
|
|
159
|
+
* The type of this member will depend on the Content-Type returned by the server
|
|
160
|
+
* and on whether m3api-rest is able to decode that content type or not.
|
|
161
|
+
*
|
|
162
|
+
* @member {*}
|
|
111
163
|
*/
|
|
112
164
|
this.body = body;
|
|
113
165
|
}
|
|
@@ -241,25 +293,97 @@ export class InvalidPathParams extends Error {
|
|
|
241
293
|
}
|
|
242
294
|
|
|
243
295
|
/**
|
|
244
|
-
*
|
|
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
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Determine whether this response contains JSON
|
|
316
|
+
* according to its headers.
|
|
245
317
|
*
|
|
246
318
|
* @private
|
|
247
|
-
* @param {
|
|
319
|
+
* @param {Response} response
|
|
320
|
+
* @return {boolean}
|
|
248
321
|
*/
|
|
249
|
-
function
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
322
|
+
function isResponseJson( response ) {
|
|
323
|
+
const mimeType = getResponseMimeType( response );
|
|
324
|
+
return mimeType === 'application/json' ||
|
|
325
|
+
( mimeType.startsWith( 'application/' ) && mimeType.endsWith( '+json' ) );
|
|
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' ) );
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get a version of the body of the response to put in an error.
|
|
358
|
+
*
|
|
359
|
+
* @private
|
|
360
|
+
* @param {Response} response
|
|
361
|
+
* @return {*}
|
|
362
|
+
*/
|
|
363
|
+
async function getResponseBodyForError( response ) {
|
|
364
|
+
if ( isResponseJson( response ) ) {
|
|
365
|
+
return await response.json();
|
|
366
|
+
} else {
|
|
367
|
+
return await response.text();
|
|
254
368
|
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Check the status code of the response and potentially throw an error based on it.
|
|
373
|
+
*
|
|
374
|
+
* @private
|
|
375
|
+
* @param {Response} response
|
|
376
|
+
*/
|
|
377
|
+
async function checkResponseStatus( response ) {
|
|
378
|
+
const { status } = response;
|
|
255
379
|
if ( status >= 500 ) {
|
|
256
|
-
throw new RestApiServerError( status,
|
|
380
|
+
throw new RestApiServerError( status, await getResponseBodyForError( response ) );
|
|
257
381
|
}
|
|
258
382
|
if ( status >= 400 ) {
|
|
259
|
-
throw new RestApiClientError( status,
|
|
383
|
+
throw new RestApiClientError( status, await getResponseBodyForError( response ) );
|
|
260
384
|
}
|
|
261
|
-
if ( status
|
|
262
|
-
throw new UnexpectedResponseStatus( status,
|
|
385
|
+
if ( status >= 300 ) {
|
|
386
|
+
throw new UnexpectedResponseStatus( status, await getResponseBodyForError( response ) );
|
|
263
387
|
}
|
|
264
388
|
}
|
|
265
389
|
|
|
@@ -268,10 +392,10 @@ const responseStatuses = new WeakMap();
|
|
|
268
392
|
/**
|
|
269
393
|
* Get the HTTP status code for this response.
|
|
270
394
|
*
|
|
271
|
-
* @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
|
|
272
396
|
* ({@link getJson} etc.). Note that it must be exactly the object returned by the function
|
|
273
397
|
* (compared by identity, i.e. `===`), not a serialization of it or anything similar.
|
|
274
|
-
* @return {number} The HTTP status code, i.e. an integer between
|
|
398
|
+
* @return {number} The HTTP status code, i.e. an integer between 200 and 599.
|
|
275
399
|
* (And for a successful response, really only between 200 and 299.)
|
|
276
400
|
*/
|
|
277
401
|
export function getResponseStatus( response ) {
|
|
@@ -286,16 +410,71 @@ export function getResponseStatus( response ) {
|
|
|
286
410
|
* Get the body of the response and check that it’s valid JSON.
|
|
287
411
|
*
|
|
288
412
|
* @private
|
|
289
|
-
* @param {
|
|
290
|
-
* @return {Object|Array}
|
|
291
|
-
*/
|
|
292
|
-
function getResponseJson(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
413
|
+
* @param {Response} response
|
|
414
|
+
* @return {Object|Array|String}
|
|
415
|
+
*/
|
|
416
|
+
async function getResponseJson( response ) {
|
|
417
|
+
if ( isResponseJson( response ) ) {
|
|
418
|
+
const body = await response.json();
|
|
419
|
+
let bodyWithIdentity;
|
|
420
|
+
if ( typeof body === 'object' && body !== null ) {
|
|
421
|
+
bodyWithIdentity = body;
|
|
422
|
+
} else if ( typeof body === 'string' ) {
|
|
423
|
+
bodyWithIdentity = new String( body );
|
|
424
|
+
} else {
|
|
425
|
+
throw new InvalidResponseBody( body );
|
|
426
|
+
}
|
|
427
|
+
responseStatuses.set( bodyWithIdentity, response.status );
|
|
428
|
+
return bodyWithIdentity;
|
|
297
429
|
} else {
|
|
298
|
-
throw new
|
|
430
|
+
throw new IncompatibleResponseType(
|
|
431
|
+
'application/json',
|
|
432
|
+
response.headers.get( 'Content-Type' ),
|
|
433
|
+
await response.text(),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
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
|
+
);
|
|
299
478
|
}
|
|
300
479
|
}
|
|
301
480
|
|
|
@@ -347,8 +526,9 @@ export function path( strings, ...values ) {
|
|
|
347
526
|
* Substitute template expressions in the path,
|
|
348
527
|
* taking values from the given params.
|
|
349
528
|
*
|
|
529
|
+
* @private
|
|
350
530
|
* @param {string} path The path with optional `{param}` expressions.
|
|
351
|
-
* @param {Object|URLSearchParams} params The input params. Not modified.
|
|
531
|
+
* @param {Object|URLSearchParams|FormData} params The input params. Not modified.
|
|
352
532
|
* @return {Object} The potentially modified path and params.
|
|
353
533
|
*/
|
|
354
534
|
function substitutePathParams( path, params ) {
|
|
@@ -372,6 +552,38 @@ function substitutePathParams( path, params ) {
|
|
|
372
552
|
}
|
|
373
553
|
copiedParams.delete( paramName );
|
|
374
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 );
|
|
375
587
|
} else {
|
|
376
588
|
if ( copiedParams === null ) {
|
|
377
589
|
copiedParams = { ...params };
|
|
@@ -394,19 +606,41 @@ function substitutePathParams( path, params ) {
|
|
|
394
606
|
}
|
|
395
607
|
|
|
396
608
|
/**
|
|
397
|
-
*
|
|
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.
|
|
398
624
|
*
|
|
399
|
-
* @
|
|
400
|
-
* @
|
|
625
|
+
* @private
|
|
626
|
+
* @param {Session} session
|
|
627
|
+
* @param {string} path
|
|
628
|
+
* @param {Object} params
|
|
629
|
+
* @param {Options} options
|
|
630
|
+
* @return {Array} url and fetchOptions
|
|
401
631
|
*/
|
|
402
|
-
function
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
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 );
|
|
407
638
|
}
|
|
408
|
-
|
|
409
|
-
|
|
639
|
+
const fetchOptions = {
|
|
640
|
+
method: 'GET',
|
|
641
|
+
headers: session.getRequestHeaders( options ),
|
|
642
|
+
};
|
|
643
|
+
return [ url, fetchOptions ];
|
|
410
644
|
}
|
|
411
645
|
|
|
412
646
|
/**
|
|
@@ -419,53 +653,519 @@ function splitUrlForInternalInterface( url ) {
|
|
|
419
653
|
* @param {Object} [params] Parameters for the request URL.
|
|
420
654
|
* This may include both parameters for the path and query parameters.
|
|
421
655
|
* @param {Options} [options] Request options.
|
|
422
|
-
* @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.
|
|
423
661
|
*/
|
|
424
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 ) {
|
|
425
748
|
( { path, params } = substitutePathParams( path, params ) );
|
|
426
749
|
const restUrl = session.apiUrl.replace( /api\.php$/, 'rest.php' );
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
...session.getRequestHeaders( options ),
|
|
750
|
+
const url = new URL( restUrl + path );
|
|
751
|
+
const fetchOptions = {
|
|
752
|
+
method: '', // trigger a TypeError in fetch() if caller does not override method
|
|
753
|
+
...encodeBody( params ),
|
|
432
754
|
};
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
} ];
|
|
436
779
|
}
|
|
437
780
|
|
|
438
781
|
/**
|
|
439
782
|
* Make a POST request to a REST API endpoint and return the JSON-decoded body.
|
|
440
783
|
*
|
|
441
784
|
* @param {Session} session The m3api session to use for this request.
|
|
442
|
-
* @param {string} path The
|
|
785
|
+
* @param {string} path The resource path, e.g. `/v1/page`.
|
|
443
786
|
* Does not include the domain, script path, or `rest.php` endpoint.
|
|
444
787
|
* Use the {@link path} tag function to build the path.
|
|
445
|
-
* @param {URLSearchParams} params The request body.
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
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.
|
|
449
792
|
* You may also include parameters for the path here.
|
|
450
793
|
* @param {Options} [options] Request options.
|
|
451
|
-
* @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.
|
|
452
799
|
*/
|
|
453
800
|
export async function postForJson( session, path, params, options = {} ) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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 );
|
|
1113
|
+
await checkResponseStatus( response );
|
|
1114
|
+
return await getResponseJson( response );
|
|
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 );
|
|
471
1171
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "m3api-rest",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"node": ">=18.2.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"m3api": "~1.
|
|
29
|
+
"m3api": "~1.1.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"chai": "^6.0.1",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"eslint-config-wikimedia": "^0.31.0",
|
|
37
37
|
"eslint-plugin-chai-friendly": "^1.0.1",
|
|
38
38
|
"jsdoc": "^4.0.3",
|
|
39
|
-
"m3api": "~1.
|
|
39
|
+
"m3api": "~1.1.0",
|
|
40
40
|
"mocha": "^11.1.0",
|
|
41
41
|
"npm-run-all": "^4.1.5"
|
|
42
42
|
},
|