chrome-webstore-upload 3.1.0 → 3.1.2
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/index.d.ts +26 -0
- package/index.js +26 -52
- package/package.json +29 -7
- package/readme.md +11 -15
package/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ReadStream } from 'node:fs';
|
|
2
|
+
import { type JsonObject } from 'type-fest';
|
|
3
|
+
export declare const refreshTokenURI = "https://www.googleapis.com/oauth2/v4/token";
|
|
4
|
+
export type APIClientOptions = {
|
|
5
|
+
extensionId: string;
|
|
6
|
+
clientId: string;
|
|
7
|
+
refreshToken: string;
|
|
8
|
+
clientSecret: string | undefined;
|
|
9
|
+
};
|
|
10
|
+
declare class APIClient {
|
|
11
|
+
extensionId: string;
|
|
12
|
+
clientId: string;
|
|
13
|
+
refreshToken: string;
|
|
14
|
+
clientSecret: string | undefined;
|
|
15
|
+
constructor(options: APIClientOptions);
|
|
16
|
+
uploadExisting(readStream: ReadStream | ReadableStream, token?: Promise<string>): Promise<JsonObject>;
|
|
17
|
+
publish(target?: string, token?: Promise<string>, deployPercentage?: number | undefined): Promise<JsonObject>;
|
|
18
|
+
get(projection?: string, token?: Promise<string>): Promise<JsonObject>;
|
|
19
|
+
fetchToken(): Promise<string>;
|
|
20
|
+
_headers(token: string): {
|
|
21
|
+
Authorization: string;
|
|
22
|
+
'x-goog-api-version': string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export default function chromeWebstoreUpload(options: APIClientOptions): APIClient;
|
|
26
|
+
export {};
|
package/index.js
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
// API documentation:
|
|
2
2
|
// https://developer.chrome.com/docs/webstore/api
|
|
3
3
|
// https://developer.chrome.com/docs/webstore/using-api
|
|
4
|
-
|
|
5
4
|
const rootURI = 'https://www.googleapis.com';
|
|
6
5
|
export const refreshTokenURI = 'https://www.googleapis.com/oauth2/v4/token';
|
|
7
|
-
const uploadExistingURI = id =>
|
|
8
|
-
`${rootURI}/upload/chromewebstore/v1.1/items/${id}`;
|
|
9
|
-
|
|
6
|
+
const uploadExistingURI = (id) => `${rootURI}/upload/chromewebstore/v1.1/items/${id}`;
|
|
10
7
|
const publishURI = ({ extensionId, target = 'default', deployPercentage }) => {
|
|
11
8
|
const url = new URL(`${rootURI}/chromewebstore/v1.1/items/${extensionId}/publish`);
|
|
12
9
|
url.searchParams.set('publishTarget', target);
|
|
13
|
-
if (deployPercentage) {
|
|
14
|
-
url.searchParams.set('deployPercentage', deployPercentage);
|
|
10
|
+
if (deployPercentage !== undefined) {
|
|
11
|
+
url.searchParams.set('deployPercentage', String(deployPercentage));
|
|
15
12
|
}
|
|
16
|
-
|
|
17
13
|
return url.href;
|
|
18
14
|
};
|
|
19
|
-
|
|
20
|
-
const getURI = (id, projection) =>
|
|
21
|
-
`${rootURI}/chromewebstore/v1.1/items/${id}?projection=${projection}`;
|
|
22
|
-
|
|
15
|
+
const getURI = (id, projection) => `${rootURI}/chromewebstore/v1.1/items/${id}?projection=${projection}`;
|
|
23
16
|
const requiredFields = ['extensionId', 'clientId', 'refreshToken'];
|
|
24
|
-
|
|
25
17
|
function throwIfNotOk(request, response) {
|
|
26
18
|
if (!request.ok) {
|
|
27
19
|
const error = new Error(request.statusText ?? 'Unknown error');
|
|
@@ -29,89 +21,76 @@ function throwIfNotOk(request, response) {
|
|
|
29
21
|
throw error;
|
|
30
22
|
}
|
|
31
23
|
}
|
|
32
|
-
|
|
33
24
|
class APIClient {
|
|
25
|
+
extensionId;
|
|
26
|
+
clientId;
|
|
27
|
+
refreshToken;
|
|
28
|
+
clientSecret;
|
|
34
29
|
constructor(options) {
|
|
35
30
|
if (typeof fetch !== 'function') {
|
|
36
|
-
throw new TypeError('`chrome-webstore-upload` requires Node.js 18.
|
|
31
|
+
throw new TypeError('`chrome-webstore-upload` requires Node.js 18.17 or newer because it relies on the global `fetch` function.');
|
|
32
|
+
}
|
|
33
|
+
if (typeof options !== 'object') {
|
|
34
|
+
throw new TypeError('The options object is required');
|
|
37
35
|
}
|
|
38
|
-
|
|
39
36
|
for (const field of requiredFields) {
|
|
40
37
|
if (!options[field]) {
|
|
41
38
|
throw new Error(`Option "${field}" is required`);
|
|
42
39
|
}
|
|
43
|
-
|
|
44
|
-
this[field] = options[field];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if ('clientSecret' in options) {
|
|
48
|
-
this.clientSecret = options.clientSecret;
|
|
49
40
|
}
|
|
41
|
+
this.extensionId = options.extensionId;
|
|
42
|
+
this.clientId = options.clientId;
|
|
43
|
+
this.refreshToken = options.refreshToken;
|
|
44
|
+
this.clientSecret = options.clientSecret;
|
|
50
45
|
}
|
|
51
|
-
|
|
52
46
|
async uploadExisting(readStream, token = this.fetchToken()) {
|
|
53
47
|
if (!readStream) {
|
|
54
48
|
throw new Error('Read stream missing');
|
|
55
49
|
}
|
|
56
|
-
|
|
57
50
|
const { extensionId } = this;
|
|
58
|
-
|
|
59
51
|
const request = await fetch(uploadExistingURI(extensionId), {
|
|
60
52
|
method: 'PUT',
|
|
61
53
|
headers: this._headers(await token),
|
|
54
|
+
// @ts-expect-error Node extension? 🤷♂️ Required https://github.com/nodejs/node/issues/46221
|
|
62
55
|
duplex: 'half',
|
|
56
|
+
// Until they figure it out, this seems to work. Alternatively use https://stackoverflow.com/a/76780381/288906
|
|
63
57
|
body: readStream,
|
|
64
58
|
});
|
|
65
|
-
|
|
66
59
|
const response = await request.json();
|
|
67
|
-
|
|
68
60
|
throwIfNotOk(request, response);
|
|
69
|
-
|
|
70
61
|
return response;
|
|
71
62
|
}
|
|
72
|
-
|
|
73
|
-
async publish(target = 'default', token = this.fetchToken(), deployPercentage) {
|
|
63
|
+
async publish(target = 'default', token = this.fetchToken(), deployPercentage = undefined) {
|
|
74
64
|
const { extensionId } = this;
|
|
75
|
-
|
|
76
65
|
const request = await fetch(publishURI({ extensionId, target, deployPercentage }), {
|
|
77
66
|
method: 'POST',
|
|
78
67
|
headers: this._headers(await token),
|
|
79
68
|
});
|
|
80
|
-
|
|
81
69
|
const response = await request.json();
|
|
82
|
-
|
|
83
70
|
throwIfNotOk(request, response);
|
|
84
|
-
|
|
85
71
|
return response;
|
|
86
72
|
}
|
|
87
|
-
|
|
88
73
|
async get(projection = 'DRAFT', token = this.fetchToken()) {
|
|
89
74
|
const { extensionId } = this;
|
|
90
|
-
|
|
91
75
|
const request = await fetch(getURI(extensionId, projection), {
|
|
92
76
|
method: 'GET',
|
|
93
77
|
headers: this._headers(await token),
|
|
94
78
|
});
|
|
95
|
-
|
|
96
79
|
const response = await request.json();
|
|
97
|
-
|
|
98
80
|
throwIfNotOk(request, response);
|
|
99
|
-
|
|
100
81
|
return response;
|
|
101
82
|
}
|
|
102
|
-
|
|
103
83
|
async fetchToken() {
|
|
104
84
|
const { clientId, clientSecret, refreshToken } = this;
|
|
105
85
|
const json = {
|
|
106
86
|
client_id: clientId,
|
|
107
87
|
refresh_token: refreshToken,
|
|
108
88
|
grant_type: 'refresh_token',
|
|
89
|
+
client_secret: clientSecret,
|
|
109
90
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
json.client_secret = clientSecret;
|
|
91
|
+
if (!clientSecret) {
|
|
92
|
+
delete json.client_secret;
|
|
113
93
|
}
|
|
114
|
-
|
|
115
94
|
const request = await fetch(refreshTokenURI, {
|
|
116
95
|
method: 'POST',
|
|
117
96
|
body: JSON.stringify(json),
|
|
@@ -119,14 +98,10 @@ class APIClient {
|
|
|
119
98
|
'Content-Type': 'application/json',
|
|
120
99
|
},
|
|
121
100
|
});
|
|
122
|
-
|
|
123
|
-
await throwIfNotOk(request);
|
|
124
|
-
|
|
125
101
|
const response = await request.json();
|
|
126
|
-
|
|
127
|
-
return response
|
|
102
|
+
throwIfNotOk(request, response);
|
|
103
|
+
return response['access_token'];
|
|
128
104
|
}
|
|
129
|
-
|
|
130
105
|
_headers(token) {
|
|
131
106
|
return {
|
|
132
107
|
Authorization: `Bearer ${token}`,
|
|
@@ -134,7 +109,6 @@ class APIClient {
|
|
|
134
109
|
};
|
|
135
110
|
}
|
|
136
111
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return new APIClient(...args);
|
|
112
|
+
export default function chromeWebstoreUpload(options) {
|
|
113
|
+
return new APIClient(options);
|
|
140
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-webstore-upload",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Upload Chrome Extensions to the Chrome Web Store",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chrome",
|
|
@@ -21,16 +21,21 @@
|
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
23
|
"exports": "./index.js",
|
|
24
|
+
"types": "./index.d.ts",
|
|
24
25
|
"files": [
|
|
25
|
-
"index.js"
|
|
26
|
+
"index.js",
|
|
27
|
+
"index.d.ts"
|
|
26
28
|
],
|
|
27
29
|
"scripts": {
|
|
28
|
-
"test": "xo && vitest run",
|
|
29
|
-
"upload": "npm run bundle && npm run test:upload",
|
|
30
30
|
"prebundle": "dot-json test/extension/manifest.json version $(utc-version)",
|
|
31
31
|
"bundle": "web-ext build --filename live-test.zip --overwrite-dest",
|
|
32
32
|
"postbundle": "git restore test/extension/manifest.json",
|
|
33
|
-
"
|
|
33
|
+
"build": "tsc",
|
|
34
|
+
"watch": "tsc -w",
|
|
35
|
+
"prepack": "npm run build",
|
|
36
|
+
"test": "xo && vitest run && tsc",
|
|
37
|
+
"test:upload": "eval $(cat .env) node test/live-test.js",
|
|
38
|
+
"upload": "npm run bundle && npm run test:upload"
|
|
34
39
|
},
|
|
35
40
|
"xo": {
|
|
36
41
|
"rules": {
|
|
@@ -40,15 +45,32 @@
|
|
|
40
45
|
"always"
|
|
41
46
|
]
|
|
42
47
|
},
|
|
48
|
+
"overrides": [
|
|
49
|
+
{
|
|
50
|
+
"files": [
|
|
51
|
+
"**/*.ts"
|
|
52
|
+
],
|
|
53
|
+
"rules": {
|
|
54
|
+
"@typescript-eslint/naming-convention": "off",
|
|
55
|
+
"@typescript-eslint/object-curly-spacing": [
|
|
56
|
+
"error",
|
|
57
|
+
"always"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
],
|
|
43
62
|
"space": 4
|
|
44
63
|
},
|
|
45
64
|
"devDependencies": {
|
|
65
|
+
"@sindresorhus/tsconfig": "^5.0.0",
|
|
46
66
|
"dot-json": "^1.3.0",
|
|
47
67
|
"fetch-mock": "^9.11.0",
|
|
48
68
|
"node-fetch": "^2.7.0",
|
|
69
|
+
"type-fest": "^4.9.0",
|
|
70
|
+
"typescript": "^5.3.3",
|
|
49
71
|
"utc-version": "^2.0.2",
|
|
50
|
-
"vitest": "^1.0
|
|
51
|
-
"xo": "^0.
|
|
72
|
+
"vitest": "^1.6.0",
|
|
73
|
+
"xo": "^0.58.0"
|
|
52
74
|
},
|
|
53
75
|
"engines": {
|
|
54
76
|
"node": ">=18"
|
package/readme.md
CHANGED
|
@@ -38,10 +38,9 @@ import fs from 'fs';
|
|
|
38
38
|
|
|
39
39
|
const myZipFile = fs.createReadStream('./mypackage.zip');
|
|
40
40
|
const token = 'xxxx'; // optional. One will be fetched if not provided
|
|
41
|
-
store.uploadExisting(myZipFile, token)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
});
|
|
41
|
+
const response = await store.uploadExisting(myZipFile, token);
|
|
42
|
+
// response is a Resource Representation
|
|
43
|
+
// https://developer.chrome.com/webstore/webstore_api/items#resource
|
|
45
44
|
```
|
|
46
45
|
|
|
47
46
|
### Publish extension
|
|
@@ -50,10 +49,9 @@ store.uploadExisting(myZipFile, token).then(res => {
|
|
|
50
49
|
const target = 'default'; // optional. Can also be 'trustedTesters'
|
|
51
50
|
const token = 'xxxx'; // optional. One will be fetched if not provided
|
|
52
51
|
const deployPercentage = 25; // optional. Will default to 100%.
|
|
53
|
-
store.publish(target, token, deployPercentage)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
52
|
+
const response = await store.publish(target, token, deployPercentage);
|
|
53
|
+
// response is documented here:
|
|
54
|
+
// https://developer.chrome.com/webstore/webstore_api/items#publish
|
|
57
55
|
```
|
|
58
56
|
|
|
59
57
|
### Get a Chrome Web Store item
|
|
@@ -61,18 +59,16 @@ store.publish(target, token, deployPercentage).then(res => {
|
|
|
61
59
|
```javascript
|
|
62
60
|
const projection = "DRAFT"; // optional. Can also be 'PUBLISHED' but only "DRAFT" is supported at this time.
|
|
63
61
|
const token = "xxxx"; // optional. One will be fetched if not provided
|
|
64
|
-
store.get(projection, token)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
62
|
+
const response = await store.get(projection, token);
|
|
63
|
+
// response is documented here:
|
|
64
|
+
// https://developer.chrome.com/docs/webstore/webstore_api/items#get
|
|
68
65
|
```
|
|
69
66
|
|
|
70
67
|
### Fetch token
|
|
71
68
|
|
|
72
69
|
```javascript
|
|
73
|
-
store.fetchToken()
|
|
74
|
-
|
|
75
|
-
});
|
|
70
|
+
const token = store.fetchToken();
|
|
71
|
+
// token is astring
|
|
76
72
|
```
|
|
77
73
|
|
|
78
74
|
## Tips
|