ovsx 1.1.1 → 1.3.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 (132) hide show
  1. package/README.md +73 -1
  2. package/lib/check-license.d.ts.map +1 -1
  3. package/lib/check-license.js +3 -2
  4. package/lib/check-license.js.map +1 -1
  5. package/lib/get.js +6 -6
  6. package/lib/get.js.map +1 -1
  7. package/lib/http.d.ts +32 -0
  8. package/lib/http.d.ts.map +1 -0
  9. package/lib/http.js +170 -0
  10. package/lib/http.js.map +1 -0
  11. package/lib/index.d.ts +4 -0
  12. package/lib/index.d.ts.map +1 -1
  13. package/lib/index.js +4 -0
  14. package/lib/index.js.map +1 -1
  15. package/lib/list-options.d.ts +24 -0
  16. package/lib/list-options.d.ts.map +1 -0
  17. package/lib/list-options.js +15 -0
  18. package/lib/list-options.js.map +1 -0
  19. package/lib/list.d.ts +18 -0
  20. package/lib/list.d.ts.map +1 -0
  21. package/lib/list.js +46 -0
  22. package/lib/list.js.map +1 -0
  23. package/lib/main.js +111 -32
  24. package/lib/main.js.map +1 -1
  25. package/lib/oidc.d.ts +22 -0
  26. package/lib/oidc.d.ts.map +1 -0
  27. package/lib/oidc.js +59 -0
  28. package/lib/oidc.js.map +1 -0
  29. package/lib/publish-options.d.ts +8 -1
  30. package/lib/publish-options.d.ts.map +1 -1
  31. package/lib/publish.d.ts.map +1 -1
  32. package/lib/publish.js +150 -13
  33. package/lib/publish.js.map +1 -1
  34. package/lib/registry-options.d.ts +8 -0
  35. package/lib/registry-options.d.ts.map +1 -1
  36. package/lib/registry.d.ts +161 -5
  37. package/lib/registry.d.ts.map +1 -1
  38. package/lib/registry.js +259 -100
  39. package/lib/registry.js.map +1 -1
  40. package/lib/search-options.d.ts +48 -0
  41. package/lib/search-options.d.ts.map +1 -0
  42. package/lib/search-options.js +15 -0
  43. package/lib/search-options.js.map +1 -0
  44. package/lib/search.d.ts +22 -0
  45. package/lib/search.d.ts.map +1 -0
  46. package/lib/search.js +86 -0
  47. package/lib/search.js.map +1 -0
  48. package/lib/show-options.d.ts +33 -0
  49. package/lib/show-options.d.ts.map +1 -0
  50. package/lib/show-options.js +15 -0
  51. package/lib/show-options.js.map +1 -0
  52. package/lib/show.d.ts +18 -0
  53. package/lib/show.d.ts.map +1 -0
  54. package/lib/show.js +289 -0
  55. package/lib/show.js.map +1 -0
  56. package/lib/table.d.ts +27 -0
  57. package/lib/table.d.ts.map +1 -0
  58. package/lib/table.js +56 -0
  59. package/lib/table.js.map +1 -0
  60. package/lib/trusted-publishing-options.d.ts +30 -0
  61. package/lib/trusted-publishing-options.d.ts.map +1 -0
  62. package/lib/trusted-publishing-options.js +15 -0
  63. package/lib/trusted-publishing-options.js.map +1 -0
  64. package/lib/trusted-publishing.d.ts +35 -0
  65. package/lib/trusted-publishing.d.ts.map +1 -0
  66. package/lib/trusted-publishing.js +112 -0
  67. package/lib/trusted-publishing.js.map +1 -0
  68. package/lib/unpublish-options.d.ts +34 -0
  69. package/lib/unpublish-options.d.ts.map +1 -0
  70. package/lib/unpublish-options.js +15 -0
  71. package/lib/unpublish-options.js.map +1 -0
  72. package/lib/unpublish.d.ts +22 -0
  73. package/lib/unpublish.d.ts.map +1 -0
  74. package/lib/unpublish.js +137 -0
  75. package/lib/unpublish.js.map +1 -0
  76. package/lib/util.d.ts +51 -9
  77. package/lib/util.d.ts.map +1 -1
  78. package/lib/util.js +130 -67
  79. package/lib/util.js.map +1 -1
  80. package/lib/verify-options.d.ts +24 -0
  81. package/lib/verify-options.d.ts.map +1 -0
  82. package/lib/verify-options.js +15 -0
  83. package/lib/verify-options.js.map +1 -0
  84. package/lib/verify-signature-options.d.ts +32 -0
  85. package/lib/verify-signature-options.d.ts.map +1 -0
  86. package/lib/verify-signature-options.js +15 -0
  87. package/lib/verify-signature-options.js.map +1 -0
  88. package/lib/verify-signature.d.ts +28 -0
  89. package/lib/verify-signature.d.ts.map +1 -0
  90. package/lib/verify-signature.js +105 -0
  91. package/lib/verify-signature.js.map +1 -0
  92. package/lib/verify.d.ts +21 -0
  93. package/lib/verify.d.ts.map +1 -0
  94. package/lib/verify.js +97 -0
  95. package/lib/verify.js.map +1 -0
  96. package/lib/zip.d.ts +10 -0
  97. package/lib/zip.d.ts.map +1 -1
  98. package/lib/zip.js +33 -10
  99. package/lib/zip.js.map +1 -1
  100. package/package.json +21 -14
  101. package/src/check-license.ts +4 -3
  102. package/src/get.ts +7 -7
  103. package/src/http.ts +176 -0
  104. package/src/index.ts +4 -0
  105. package/src/list-options.ts +25 -0
  106. package/src/list.ts +48 -0
  107. package/src/main.ts +125 -34
  108. package/src/oidc.ts +65 -0
  109. package/src/publish-options.ts +8 -1
  110. package/src/publish.ts +208 -14
  111. package/src/registry-options.ts +8 -0
  112. package/src/registry.ts +379 -98
  113. package/src/search-options.ts +49 -0
  114. package/src/search.ts +95 -0
  115. package/src/show-options.ts +34 -0
  116. package/src/show.ts +328 -0
  117. package/src/table.ts +53 -0
  118. package/src/trusted-publishing-options.ts +30 -0
  119. package/src/trusted-publishing.ts +148 -0
  120. package/src/unpublish-options.ts +35 -0
  121. package/src/unpublish.ts +160 -0
  122. package/src/util.ts +138 -70
  123. package/src/verify-options.ts +25 -0
  124. package/src/verify-signature-options.ts +32 -0
  125. package/src/verify-signature.ts +124 -0
  126. package/src/verify.ts +107 -0
  127. package/src/zip.ts +33 -13
  128. package/lib/version.d.ts +0 -2
  129. package/lib/version.d.ts.map +0 -1
  130. package/lib/version.js +0 -5
  131. package/lib/version.js.map +0 -1
  132. package/src/version.ts +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/lib/zip.d.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  import { Manifest } from './util';
2
+ export declare function readZip(packagePath: string, filter: (name: string) => boolean): Promise<Map<string, Buffer>>;
3
+ /**
4
+ * Computes the SHA256 digest of every non-directory entry of a zip file, keyed by its exact
5
+ * (case-preserving) name - unlike {@link readZip}, which lowercases names for its
6
+ * case-insensitive-filter callers. Used where entry names and digests are compared against another
7
+ * source of truth, such as a signature manifest that records the original names, without also
8
+ * having to hold every entry's full content in memory at once alongside it - each entry's content
9
+ * is streamed straight into a hash and discarded, one entry at a time.
10
+ */
11
+ export declare function hashAllZipEntries(packagePath: string): Promise<Map<string, string>>;
2
12
  export declare function readVSIXPackage(packagePath: string): Promise<Manifest>;
3
13
  //# sourceMappingURL=zip.d.ts.map
package/lib/zip.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AA8BlC,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ5E"}
1
+ {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,wBAAsB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBlH;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkBzF;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ5E"}
package/lib/zip.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readZip = readZip;
4
+ exports.hashAllZipEntries = hashAllZipEntries;
3
5
  exports.readVSIXPackage = readVSIXPackage;
4
6
  /********************************************************************************
5
7
  * Copyright (c) 2024 Precies. Software and others
@@ -10,15 +12,10 @@ exports.readVSIXPackage = readVSIXPackage;
10
12
  *
11
13
  * SPDX-License-Identifier: EPL-2.0
12
14
  ********************************************************************************/
15
+ const crypto = require("crypto");
13
16
  const yauzl = require("yauzl-promise");
14
- async function bufferStream(stream) {
15
- return await new Promise((resolve, reject) => {
16
- const buffers = [];
17
- stream.on('data', buffer => buffers.push(buffer));
18
- stream.once('error', reject);
19
- stream.once('end', () => resolve(Buffer.concat(buffers)));
20
- });
21
- }
17
+ const consumers_1 = require("stream/consumers");
18
+ const promises_1 = require("stream/promises");
22
19
  async function readZip(packagePath, filter) {
23
20
  const result = new Map();
24
21
  const zipfile = await yauzl.open(packagePath);
@@ -27,9 +24,35 @@ async function readZip(packagePath, filter) {
27
24
  const name = entry.filename.toLowerCase();
28
25
  if (filter(name)) {
29
26
  const stream = await zipfile.openReadStream(entry);
30
- const buffer = await bufferStream(stream);
31
- result.set(name, buffer);
27
+ result.set(name, await (0, consumers_1.buffer)(stream));
28
+ }
29
+ }
30
+ }
31
+ finally {
32
+ await zipfile.close();
33
+ }
34
+ return result;
35
+ }
36
+ /**
37
+ * Computes the SHA256 digest of every non-directory entry of a zip file, keyed by its exact
38
+ * (case-preserving) name - unlike {@link readZip}, which lowercases names for its
39
+ * case-insensitive-filter callers. Used where entry names and digests are compared against another
40
+ * source of truth, such as a signature manifest that records the original names, without also
41
+ * having to hold every entry's full content in memory at once alongside it - each entry's content
42
+ * is streamed straight into a hash and discarded, one entry at a time.
43
+ */
44
+ async function hashAllZipEntries(packagePath) {
45
+ const result = new Map();
46
+ const zipfile = await yauzl.open(packagePath);
47
+ try {
48
+ for await (const entry of zipfile) {
49
+ if (entry.filename.endsWith('/')) {
50
+ continue;
32
51
  }
52
+ const stream = await zipfile.openReadStream(entry);
53
+ const hash = crypto.createHash('sha256');
54
+ await (0, promises_1.pipeline)(stream, hash);
55
+ result.set(entry.filename, hash.digest('base64'));
33
56
  }
34
57
  }
35
58
  finally {
package/lib/zip.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":";;AAyCA,0CAQC;AAjDD;;;;;;;;kFAQkF;AAClF,uCAAuC;AAIvC,KAAK,UAAU,YAAY,CAAC,MAAgB;IAC3C,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,WAAmB,EAAE,MAAiC;IAC5E,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzF,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":";;AAeA,0BAgBC;AAUD,8CAkBC;AAED,0CAQC;AArED;;;;;;;;kFAQkF;AAClF,iCAAiC;AACjC,uCAAuC;AACvC,gDAA0C;AAC1C,8CAA2C;AAGpC,KAAK,UAAU,OAAO,CAAC,WAAmB,EAAE,MAAiC;IACnF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAA,kBAAM,EAAC,MAAM,CAAC,CAAC,CAAC;YACxC,CAAC;QACF,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,SAAS;YACV,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,IAAA,mBAAQ,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnD,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzF,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa,CAAC;AAC7D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ovsx",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Command line interface for Eclipse Open VSX",
5
5
  "keywords": [
6
6
  "cli",
@@ -21,6 +21,14 @@
21
21
  "name": "Miro Spönemann",
22
22
  "email": "miro.spoenemann@typefox.io",
23
23
  "url": "https://www.typefox.io"
24
+ },
25
+ {
26
+ "name": "Thomas Neidhart",
27
+ "email": "thomas.neidhart@eclipse-foundation.org"
28
+ },
29
+ {
30
+ "name": "Jordi Gómez Hidalgo",
31
+ "email": "jordi.gomez@eclipse-foundation.org"
24
32
  }
25
33
  ],
26
34
  "files": [
@@ -31,44 +39,43 @@
31
39
  "types": "lib/index",
32
40
  "bin": "bin/ovsx",
33
41
  "engines": {
34
- "node": ">= 20"
42
+ "node": ">=22.0.0"
35
43
  },
36
44
  "dependencies": {
37
45
  "@inquirer/prompts": "^7.10.1",
38
- "@vscode/vsce": "^3.7.1",
39
- "commander": "^6.2.1",
46
+ "@vscode/vsce": "^4.0.0",
47
+ "commander": "^14.0.3",
40
48
  "cross-keychain": "^1.1.0",
41
- "follow-redirects": "^1.16.0",
42
49
  "is-ci": "^2.0.0",
43
- "leven": "^3.1.0",
44
50
  "semver": "^7.6.0",
45
- "tmp": "^0.2.3",
46
51
  "yauzl-promise": "^4.0.0"
47
52
  },
48
53
  "devDependencies": {
49
54
  "@eslint/eslintrc": "^3.2.0",
50
55
  "@eslint/js": "^9.15.0",
51
56
  "@stylistic/eslint-plugin": "^2.11.0",
52
- "@types/follow-redirects": "^1.13.1",
53
57
  "@types/is-ci": "^2.0.0",
54
- "@types/node": "^20.14.8",
58
+ "@types/node": "^22.0.0",
55
59
  "@types/semver": "^7.5.8",
56
- "@types/tmp": "^0.2.2",
57
60
  "@types/yauzl-promise": "^4",
61
+ "@types/yazl": "^3.3.1",
58
62
  "@typescript-eslint/eslint-plugin": "^8.15.0",
59
63
  "@typescript-eslint/parser": "^8.15.0",
60
64
  "eslint": "^9.15.0",
61
65
  "limiter": "^2.1.0",
62
66
  "rimraf": "^6.0.1",
63
- "typescript": "^5.6.3"
67
+ "typescript": "^5.6.3",
68
+ "vite": "^8.3.2",
69
+ "vitest": "^5.0.3",
70
+ "yazl": "^3.3.1"
64
71
  },
65
72
  "scripts": {
66
73
  "clean": "rimraf lib",
67
- "prebuild": "node -p \"'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
68
74
  "build": "tsc -p ./tsconfig.json && yarn run lint",
69
75
  "watch": "tsc -w -p ./tsconfig.json",
70
- "lint": "eslint -c ./configs/eslintrc.mjs src",
71
- "prepare": "yarn run clean && yarn run prebuild && yarn run build",
76
+ "test": "vitest run",
77
+ "lint": "eslint -c ./configs/eslintrc.mjs src test",
78
+ "prepare": "yarn run clean && yarn run build",
72
79
  "publish:next": "yarn npm publish --tag next",
73
80
  "publish:latest": "yarn npm publish --tag latest",
74
81
  "load-extensions": "node scripts/load-test-extensions.js"
@@ -9,10 +9,11 @@
9
9
  ********************************************************************************/
10
10
 
11
11
  import * as fs from 'fs';
12
+ import * as path from 'path';
12
13
  import * as isCI from 'is-ci';
13
14
  import { input, select } from '@inquirer/prompts';
14
15
  import {
15
- readManifest, writeManifest, Manifest, writeFile, validateManifest, promisify
16
+ readManifest, writeManifest, Manifest, validateManifest
16
17
  } from './util';
17
18
 
18
19
  async function addLicense(packagePath: string, manifest: Manifest): Promise<void> {
@@ -87,14 +88,14 @@ async function useMITLicense(manifest: Manifest, packagePath?: string) {
87
88
  manifest.license = 'MIT';
88
89
  await writeManifest(manifest, packagePath);
89
90
  const license = MIT_LICENSE_TEXT.replace('<YEAR> <COPYRIGHT HOLDER>', copyright);
90
- await writeFile('LICENSE', license, packagePath);
91
+ await fs.promises.writeFile(path.join(packagePath ?? process.cwd(), 'LICENSE'), license, 'utf-8');
91
92
  console.log('LICENSE file has been written. Please commit it to the source repository.');
92
93
  }
93
94
 
94
95
  const LICENSE_FILE_NAMES = ['license.md', 'license', 'license.txt', 'licence.md', 'licence', 'licence.txt'];
95
96
 
96
97
  async function hasLicenseFile(packagePath?: string): Promise<boolean> {
97
- const fileNames = await promisify(fs.readdir)(packagePath ?? '.');
98
+ const fileNames = await fs.promises.readdir(packagePath ?? '.');
98
99
  for (const fileName of fileNames) {
99
100
  for (const licFileName of LICENSE_FILE_NAMES) {
100
101
  if (fileName.toLowerCase() === licFileName) {
package/src/get.ts CHANGED
@@ -12,7 +12,7 @@ import * as fs from 'fs';
12
12
  import * as path from 'path';
13
13
  import * as semver from 'semver';
14
14
  import { Registry, Extension } from "./registry";
15
- import { promisify, matchExtensionId, optionalStat, makeDirs, addEnvOptions, rejectError } from './util';
15
+ import { matchExtensionId, addEnvOptions, rejectError } from './util';
16
16
  import { GetOptions } from './get-options';
17
17
 
18
18
  /**
@@ -23,7 +23,7 @@ export async function getExtension(options: GetOptions): Promise<void> {
23
23
  const registry = new Registry(options);
24
24
  const match = matchExtensionId(options.extensionId);
25
25
  if (!match) {
26
- throw new Error('The extension identifier must have the form `namespace.extension`.');
26
+ throw new Error('The extension identifier must have the form `namespace.extension` or `namespace/extension`.');
27
27
  }
28
28
 
29
29
  const extension = await registry.getMetadata(match[1], match[2], options.target);
@@ -70,15 +70,15 @@ async function printMetadata(registry: Registry, extension: Extension, output?:
70
70
  return;
71
71
  }
72
72
  let filePath: string | undefined;
73
- const stats = await optionalStat(output);
73
+ const stats = fs.statSync(output, { throwIfNoEntry: false });
74
74
  if (stats?.isDirectory() || !stats && output.endsWith(path.sep)) {
75
75
  const fileName = `${extension.namespace}.${extension.name}-${extension.version}.json`;
76
76
  filePath = path.resolve(process.cwd(), output, fileName);
77
77
  } else {
78
78
  filePath = path.resolve(process.cwd(), output);
79
79
  }
80
- await makeDirs(path.dirname(filePath));
81
- await promisify(fs.writeFile)(filePath, metadata);
80
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
81
+ await fs.promises.writeFile(filePath, metadata);
82
82
  }
83
83
 
84
84
  async function download(registry: Registry, extension: Extension, output?: string): Promise<void> {
@@ -90,7 +90,7 @@ async function download(registry: Registry, extension: Extension, output?: strin
90
90
  const fileName = decodeURIComponent(downloadUrl.substring(fileNameIndex + 1));
91
91
  let filePath: string | undefined;
92
92
  if (output) {
93
- const stats = await optionalStat(output);
93
+ const stats = fs.statSync(output, { throwIfNoEntry: false });
94
94
  if (stats?.isDirectory() || !stats && output.endsWith(path.sep)) {
95
95
  filePath = path.resolve(process.cwd(), output, fileName);
96
96
  } else {
@@ -99,7 +99,7 @@ async function download(registry: Registry, extension: Extension, output?: strin
99
99
  } else {
100
100
  filePath = path.resolve(process.cwd(), fileName);
101
101
  }
102
- await makeDirs(path.dirname(filePath));
102
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
103
103
  const target = extension.targetPlatform !== 'universal' ? '@' + extension.targetPlatform : '';
104
104
  console.log(`Downloading ${extension.namespace}.${extension.name}-${extension.version}${target} to ${filePath}`);
105
105
  await registry.download(filePath, new URL(downloadUrl));
package/src/http.ts ADDED
@@ -0,0 +1,176 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import * as http from 'http';
15
+ import * as https from 'https';
16
+ import * as fs from 'fs';
17
+ import { redactUrl, statusError, withStatus } from './util';
18
+
19
+ // Hop limit the fetch standard uses.
20
+ const MAX_REDIRECTS = 20;
21
+
22
+ // Dropped on a redirect to another origin; X-OpenVSX-Token carries the PAT just like Authorization.
23
+ const CREDENTIAL_HEADERS = /^(?:authorization|cookie|proxy-authorization|x-openvsx-token)$/i;
24
+
25
+ export type RequestBody = () => string | Buffer | Uint8Array | fs.ReadStream;
26
+
27
+ /**
28
+ * Sends a request and follows redirects to `onResponse`'s final response, reporting any failure
29
+ * to `fail`. A GET follows every redirect; a request with a body follows only 307/308, sending
30
+ * the body again, because 301/302/303 turn it into a GET without one. Credentials are dropped
31
+ * once a redirect leaves the origin. Only the headers in `options` are sent, so a caller that
32
+ * passes none carries no credentials.
33
+ */
34
+ export function send(
35
+ url: URL,
36
+ options: http.RequestOptions,
37
+ body: RequestBody | undefined,
38
+ onResponse: (response: http.IncomingMessage) => void,
39
+ fail: (err: Error) => void,
40
+ hops = 0
41
+ ): void {
42
+ const protocol = url.protocol === 'https:' ? https : http;
43
+ const request = protocol.request(url, options, response => {
44
+ const status = response.statusCode ?? 0;
45
+ const location = response.headers.location;
46
+ if (status < 300 || status > 399 || !location) {
47
+ onResponse(response);
48
+ return;
49
+ }
50
+ response.resume();
51
+
52
+ let next: URL;
53
+ try {
54
+ next = new URL(location, url);
55
+ } catch (err) {
56
+ fail(err as Error);
57
+ return;
58
+ }
59
+ if (hops >= MAX_REDIRECTS) {
60
+ fail(new Error(`The request to ${redactUrl(url)} was redirected more than ${MAX_REDIRECTS} times.`));
61
+ } else if (next.protocol !== 'https:' && next.protocol !== 'http:') {
62
+ fail(new Error(`The request to ${redactUrl(url)} was redirected to unsupported URL ${redactUrl(next)}.`));
63
+ } else if (body && status !== 307 && status !== 308) {
64
+ fail(withStatus(new Error(`The ${options.method} request to ${redactUrl(url)} was redirected to ${redactUrl(next)} `
65
+ + `with status ${status}, which drops the request body. Use the redirect target as the registry URL.`), status));
66
+ } else {
67
+ // The server may answer before reading the body: abandon this hop so its stream
68
+ // releases the file and a late reset on its socket can't fail the next hop.
69
+ request.removeListener('error', fail);
70
+ request.on('error', () => { /* abandoned */ });
71
+ if (content instanceof fs.ReadStream) {
72
+ content.unpipe(request);
73
+ content.destroy();
74
+ }
75
+ request.destroy();
76
+ const headers = next.origin === url.origin ? options.headers : withoutCredentials(options.headers as http.OutgoingHttpHeaders);
77
+ send(next, { ...options, headers }, body, onResponse, fail, hops + 1);
78
+ }
79
+ });
80
+ request.on('error', fail);
81
+ failOnTimeout(request, url, options.timeout, fail);
82
+
83
+ const content = body?.();
84
+ if (content instanceof fs.ReadStream) {
85
+ content.on('error', (err: Error) => {
86
+ request.destroy();
87
+ fail(err);
88
+ });
89
+ request.on('error', () => content.close());
90
+ content.on('open', () => content.pipe(request));
91
+ } else {
92
+ request.end(content);
93
+ }
94
+ }
95
+
96
+ /** Sends a GET with only the given headers and resolves with the parsed JSON response. */
97
+ export function getJson<T>(url: URL, headers: http.OutgoingHttpHeaders, timeout: number): Promise<T> {
98
+ return new Promise((resolve, reject) => {
99
+ send(url, { method: 'GET', headers, timeout }, undefined, jsonResponse<T>(resolve, reject), reject);
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Reads a response as JSON. A non-2xx status rejects with the `message` or `error` of a JSON
105
+ * error body when there is one, and with the status otherwise. An HTML page rejects with an
106
+ * Error rather than its markup, since it usually means the URL points at a website, not an API.
107
+ */
108
+ export function jsonResponse<T>(resolve: (value: T) => void, reject: (reason: any) => void): (res: http.IncomingMessage) => void {
109
+ return response => {
110
+ response.setEncoding('utf-8');
111
+ let json = '';
112
+ // A connection lost after the headers have arrived ends the response without 'end' ever
113
+ // firing, so without this the promise is never settled and the command waits on a body
114
+ // that is not coming. The configured timeout would eventually rescue it, but rejecting
115
+ // here reports what actually happened instead of thirty seconds of silence.
116
+ response.on('error', reject);
117
+ response.on('data', chunk => json += chunk);
118
+ response.on('end', () => {
119
+ if (response.statusCode !== undefined && (response.statusCode < 200 || response.statusCode > 299)) {
120
+ if (json.trimStart().startsWith('{')) {
121
+ try {
122
+ const parsed = JSON.parse(json) as { message?: string; error?: string };
123
+ const message = parsed.message || parsed.error;
124
+ if (message) {
125
+ // keep the status: the message alone cannot say whether retrying is worth it
126
+ reject(withStatus(new Error(message), response.statusCode));
127
+ return;
128
+ }
129
+ } catch (err) {
130
+ // Ignore the error and reject with response status
131
+ }
132
+ }
133
+ reject(statusError(response));
134
+ } else if (isHtml(response, json)) {
135
+ reject(withStatus(new Error(`The server responded with status ${response.statusCode} and an HTML page instead of JSON.`),
136
+ response.statusCode));
137
+ } else {
138
+ try {
139
+ resolve(JSON.parse(json));
140
+ } catch (err) {
141
+ reject(err);
142
+ }
143
+ }
144
+ });
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Node's `timeout` option only raises an event - the request stays open, which is why a server
150
+ * that accepts a connection and then says nothing used to hold a command open indefinitely.
151
+ * Destroying the request with an error routes through the error handling each caller already
152
+ * has, so a stalled request rejects the way any other failure does. With a timeout of zero the
153
+ * event never fires and this does nothing.
154
+ */
155
+ function failOnTimeout(request: http.ClientRequest, url: URL, timeout: number | undefined, fail: (err: Error) => void): void {
156
+ request.on('timeout', () => {
157
+ // Reported before the request is torn down, rather than by destroying it with the error.
158
+ // Destroying raises errors of its own - ECONNRESET on the response, a premature close
159
+ // from pipeline - and node guarantees no order between those and the request's own. In
160
+ // practice the request's arrives first, so this is belt and braces rather than the thing
161
+ // that stops the wrong error being reported; that is the caller claiming the failure
162
+ // before it does any asynchronous cleanup.
163
+ fail(new Error(`No response from ${redactUrl(url)} for ${timeout} ms.`));
164
+ request.destroy();
165
+ });
166
+ }
167
+
168
+ // The body check covers servers that send HTML without a text/html Content-Type.
169
+ function isHtml(response: http.IncomingMessage, body: string): boolean {
170
+ const contentType = response.headers['content-type']?.split(';')[0].trim().toLowerCase();
171
+ return contentType === 'text/html' || /^\s*<(?:!doctype\s+html|html)[\s>]/i.test(body);
172
+ }
173
+
174
+ function withoutCredentials(headers: http.OutgoingHttpHeaders = {}): http.OutgoingHttpHeaders {
175
+ return Object.fromEntries(Object.entries(headers).filter(([name]) => !CREDENTIAL_HEADERS.test(name)));
176
+ }
package/src/index.ts CHANGED
@@ -19,6 +19,10 @@ export * from './publish';
19
19
  export * from './publish-options';
20
20
  export * from './registry';
21
21
  export * from './registry-options';
22
+ export * from './trusted-publishing';
23
+ export * from './trusted-publishing-options';
24
+ export * from './unpublish';
25
+ export * from './unpublish-options';
22
26
  export * from './verify-pat';
23
27
  export * from './verify-pat-options';
24
28
  export { isLicenseOk } from './check-license';
@@ -0,0 +1,25 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { RegistryOptions } from './registry-options';
15
+
16
+ export interface ListOptions extends RegistryOptions {
17
+ /**
18
+ * Namespace whose extensions should be listed.
19
+ */
20
+ namespace: string;
21
+ /**
22
+ * Print the raw namespace metadata as JSON instead of a list.
23
+ */
24
+ json?: boolean;
25
+ }
package/src/list.ts ADDED
@@ -0,0 +1,48 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { ListOptions } from './list-options';
15
+ import { Registry } from './registry';
16
+ import { formatCount } from './table';
17
+ import { addEnvOptions } from './util';
18
+
19
+ /**
20
+ * Lists the extensions published in a namespace.
21
+ */
22
+ export async function list(options: ListOptions): Promise<void> {
23
+ addEnvOptions(options);
24
+ const registry = new Registry(options);
25
+ const namespace = await registry.getNamespace(options.namespace);
26
+ if (namespace.error) {
27
+ throw new Error(namespace.error);
28
+ }
29
+
30
+ if (options.json) {
31
+ console.log(JSON.stringify(namespace, null, 4));
32
+ return;
33
+ }
34
+
35
+ // Sorted here rather than relying on the response's key order, so the output is stable and
36
+ // scriptable regardless of how the registry happens to serialise the map.
37
+ const names = Object.keys(namespace.extensions ?? {}).sort((a, b) => a.localeCompare(b));
38
+ const verified = namespace.verified ? ' (verified)' : '';
39
+ console.log(`${namespace.name}${verified} - ${formatCount(names.length, 'extension')}`);
40
+ if (names.length === 0) {
41
+ return;
42
+ }
43
+
44
+ console.log();
45
+ for (const name of names) {
46
+ console.log(` ${namespace.name}.${name}`);
47
+ }
48
+ }