@vscode/vsce 3.9.3-11 → 3.9.3-12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/out/keytarMigration.js +181 -0
- package/out/legacyCredentials.js +115 -0
- package/out/main.js +1 -5
- package/out/multipart.js +54 -0
- package/out/publish.js +20 -20
- package/out/store.js +14 -5
- package/out/util.js +47 -0
- package/out/windowsKeytar.js +85 -0
- package/package.json +34 -33
package/README.md
CHANGED
|
@@ -17,6 +17,26 @@ Read the [**Documentation**](https://code.visualstudio.com/api/working-with-exte
|
|
|
17
17
|
|
|
18
18
|
In order to save credentials safely, this project uses [`@napi-rs/keyring`](https://www.npmjs.com/package/@napi-rs/keyring), which uses the system Secret Service and falls back to the Linux kernel keyring. Setting the `VSCE_STORE=file` environment variable will revert back to the file credential store. Using the `VSCE_PAT` environment variable will also avoid using the system credential store.
|
|
19
19
|
|
|
20
|
+
### Upgrading saved PATs
|
|
21
|
+
|
|
22
|
+
When a command needs a saved Personal Access Token and the publisher has none in the current native credential store, `vsce` checks for that publisher's old `keytar` credential. If one is found in an interactive terminal, it offers:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
A saved PAT for publisher 'your-publisher' was found in the previous credential store. Copy it to the new store? [y/N]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Enter `Y` to copy just that publisher's PAT and continue without re-entering it. Enter `N` (or press Enter) to continue to the normal PAT prompt without copying anything. Migration is skipped in non-interactive runs such as CI.
|
|
29
|
+
|
|
30
|
+
- **Windows:** reads the requested Windows Credential Manager entry using the built-in Windows PowerShell. No `keytar` installation is needed.
|
|
31
|
+
- **Linux:** requires `secret-tool` (`libsecret-tools` on Debian/Ubuntu) and access to the same desktop Secret Service used previously. You may be prompted to unlock the keyring.
|
|
32
|
+
- **macOS:** existing Keychain entries are already compatible; no copying is necessary.
|
|
33
|
+
|
|
34
|
+
Existing PATs in the new store take precedence; migration does not run while listing publishers or logging out. Each copied PAT is read back before it is used. **Old keytar entries are never modified or deleted**, so older `vsce` versions can still use them. Declining or logging out does not permanently suppress the offer: the old PAT can be offered again when needed, but copying always requires fresh consent. Logout removes only the new entry and does not revoke the PAT.
|
|
35
|
+
|
|
36
|
+
Credential writes are serialized across `vsce` processes, and the destination is checked again after confirmation to avoid overwriting a newer PAT. The non-secret lock lives under `~/.vsce-keytar-migration`; no PATs or consent decisions are stored there. A command waits up to 30 seconds for the lock; a lock abandoned by a crashed process can be recovered after two minutes.
|
|
37
|
+
|
|
38
|
+
If legacy lookup or copying fails, `vsce` displays a warning and continues to the normal PAT prompt. Install the required helper/unlock the keyring and retry, or enter a PAT to save it normally. Migration helpers time out after 30 seconds. Using `VSCE_STORE=file`, `VSCE_PAT`, or `--pat` does not trigger this native-store migration; the existing plaintext-file migration is unchanged.
|
|
39
|
+
|
|
20
40
|
## Usage
|
|
21
41
|
|
|
22
42
|
```console
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LegacyCredentialMigration = void 0;
|
|
37
|
+
const crypto_1 = require("crypto");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os_1 = require("os");
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const proper_lockfile_1 = require("proper-lockfile");
|
|
42
|
+
const legacyCredentials_1 = require("./legacyCredentials");
|
|
43
|
+
const util_1 = require("./util");
|
|
44
|
+
const validation_1 = require("./validation");
|
|
45
|
+
// Decorate the native store so migration policy and write synchronization stay
|
|
46
|
+
// separate from its ordinary credential operations.
|
|
47
|
+
class LegacyCredentialMigration {
|
|
48
|
+
store;
|
|
49
|
+
openStore;
|
|
50
|
+
static wrap(store, openStore, options = {}) {
|
|
51
|
+
const platform = options.platform ?? process.platform;
|
|
52
|
+
return platform === 'win32' || platform === 'linux'
|
|
53
|
+
? new LegacyCredentialMigration(store, openStore, options)
|
|
54
|
+
: store;
|
|
55
|
+
}
|
|
56
|
+
serviceName;
|
|
57
|
+
platform;
|
|
58
|
+
lockPath;
|
|
59
|
+
interactive;
|
|
60
|
+
prompt;
|
|
61
|
+
readCredential;
|
|
62
|
+
constructor(store, openStore, options = {}) {
|
|
63
|
+
this.store = store;
|
|
64
|
+
this.openStore = openStore;
|
|
65
|
+
this.serviceName = options.serviceName ?? 'vscode-vsce';
|
|
66
|
+
this.platform = options.platform ?? process.platform;
|
|
67
|
+
this.lockPath = options.lockPath ?? path.join((0, os_1.homedir)(), '.vsce-keytar-migration', (0, crypto_1.createHash)('sha256').update(`${this.platform}:${this.serviceName}`).digest('hex'));
|
|
68
|
+
// read() otherwise answers "y" in tests and non-interactive processes.
|
|
69
|
+
this.interactive = options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.VSCE_TESTS);
|
|
70
|
+
this.prompt = options.prompt ?? util_1.read;
|
|
71
|
+
this.readCredential = options.readCredential
|
|
72
|
+
?? ((service, name) => (0, legacyCredentials_1.readLegacyCredential)(service, name, { platform: this.platform }));
|
|
73
|
+
}
|
|
74
|
+
get size() {
|
|
75
|
+
return this.store.size;
|
|
76
|
+
}
|
|
77
|
+
get(name) {
|
|
78
|
+
return this.findPublisher(this.store, name);
|
|
79
|
+
}
|
|
80
|
+
async add(publisher) {
|
|
81
|
+
await this.withLock(() => this.store.add({
|
|
82
|
+
name: this.get(publisher.name)?.name ?? publisher.name,
|
|
83
|
+
pat: publisher.pat,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
async delete(name) {
|
|
87
|
+
await this.withLock(() => this.store.delete(this.get(name)?.name ?? name));
|
|
88
|
+
}
|
|
89
|
+
[Symbol.iterator]() {
|
|
90
|
+
return this.store[Symbol.iterator]();
|
|
91
|
+
}
|
|
92
|
+
async tryMigratePublisher(name) {
|
|
93
|
+
(0, validation_1.validatePublisher)(name);
|
|
94
|
+
const existing = this.get(name);
|
|
95
|
+
if (existing || !this.interactive || (this.platform !== 'win32' && this.platform !== 'linux')) {
|
|
96
|
+
return existing;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const legacy = await this.readCredential(this.serviceName, name);
|
|
100
|
+
if (!legacy) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
if (!this.sameAccount(legacy.name, name) || !legacy.pat) {
|
|
104
|
+
throw new legacyCredentials_1.LegacyMigrationError('The legacy credential reader returned an invalid credential.');
|
|
105
|
+
}
|
|
106
|
+
const answer = await this.prompt(`A saved PAT for publisher '${name}' was found in the previous credential store. Copy it to the new store? [y/N] `);
|
|
107
|
+
if (!/^y$/i.test(answer.trim())) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
// Do not hold a cross-process lock while waiting for the user's answer.
|
|
111
|
+
return await this.withLock(() => this.copyAndVerify({ name, pat: legacy.pat }));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof legacyCredentials_1.LegacyMigrationError)) {
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
util_1.log.warn(`${error.message} The previous credential was not changed. `
|
|
118
|
+
+ (this.platform === 'linux' ? 'Legacy lookup requires secret-tool (libsecret-tools on Debian/Ubuntu) and an accessible desktop keyring. ' : '')
|
|
119
|
+
+ 'Enter a PAT to continue, or retry after resolving the credential-store problem.');
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async copyAndVerify(publisher) {
|
|
124
|
+
let verified;
|
|
125
|
+
try {
|
|
126
|
+
const destination = await this.openStore();
|
|
127
|
+
const current = this.findPublisher(destination, publisher.name);
|
|
128
|
+
if (current) {
|
|
129
|
+
this.store = destination;
|
|
130
|
+
return current;
|
|
131
|
+
}
|
|
132
|
+
await destination.add(publisher);
|
|
133
|
+
verified = await this.openStore();
|
|
134
|
+
const saved = this.findPublisher(verified, publisher.name);
|
|
135
|
+
if (saved?.pat !== publisher.pat) {
|
|
136
|
+
throw new legacyCredentials_1.LegacyMigrationError('The copied PAT could not be verified.');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (!(error instanceof Error)) {
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
// Native failures must not include secret values in CLI diagnostics.
|
|
144
|
+
throw new legacyCredentials_1.LegacyMigrationError(`Could not copy and verify the previous PAT for publisher '${publisher.name}'.`);
|
|
145
|
+
}
|
|
146
|
+
this.store = verified;
|
|
147
|
+
util_1.log.info(`Copied the saved PAT for publisher '${publisher.name}'. The previous credential was not changed.`);
|
|
148
|
+
return publisher;
|
|
149
|
+
}
|
|
150
|
+
sameAccount(a, b) {
|
|
151
|
+
return this.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
152
|
+
}
|
|
153
|
+
findPublisher(store, name) {
|
|
154
|
+
return [...store].find(publisher => this.sameAccount(publisher.name, name));
|
|
155
|
+
}
|
|
156
|
+
async withLock(operation) {
|
|
157
|
+
let release;
|
|
158
|
+
try {
|
|
159
|
+
await fs.promises.mkdir(this.lockPath, { recursive: true, mode: 0o700 });
|
|
160
|
+
release = await (0, proper_lockfile_1.lock)(this.lockPath, {
|
|
161
|
+
stale: 120_000,
|
|
162
|
+
update: 5_000,
|
|
163
|
+
retries: { retries: 120, factor: 1, minTimeout: 250, maxTimeout: 250 },
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error instanceof Error && 'code' in error) {
|
|
168
|
+
throw new legacyCredentials_1.LegacyMigrationError('Could not lock the credential store. Another vsce command may still be using it.');
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
return await operation();
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
await release();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.LegacyCredentialMigration = LegacyCredentialMigration;
|
|
181
|
+
//# sourceMappingURL=keytarMigration.js.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LegacyMigrationError = void 0;
|
|
37
|
+
exports.readLegacyCredential = readLegacyCredential;
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const validation_1 = require("./validation");
|
|
41
|
+
const windowsKeytar_1 = require("./windowsKeytar");
|
|
42
|
+
class LegacyMigrationError extends Error {
|
|
43
|
+
}
|
|
44
|
+
exports.LegacyMigrationError = LegacyMigrationError;
|
|
45
|
+
const runCredentialCommand = (file, args, env) => new Promise((resolve, reject) => {
|
|
46
|
+
(0, child_process_1.execFile)(file, args, {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
env,
|
|
49
|
+
windowsHide: true,
|
|
50
|
+
timeout: 30_000,
|
|
51
|
+
maxBuffer: 1024 * 1024,
|
|
52
|
+
}, (error, stdout, stderr) => {
|
|
53
|
+
if (error && (typeof error.code !== 'number' || error.killed)) {
|
|
54
|
+
// Child-process errors can contain stdout/stderr, including PATs.
|
|
55
|
+
const reason = error.code === 'ENOENT' ? 'is not installed'
|
|
56
|
+
: error.killed ? 'timed out or was terminated'
|
|
57
|
+
: 'failed to read the previous credential';
|
|
58
|
+
reject(new LegacyMigrationError(`${path.basename(file)} ${reason}.`));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
resolve({ stdout, stderr, exitCode: typeof error?.code === 'number' ? error.code : 0 });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
function parseWindowsCredential(stdout, name) {
|
|
66
|
+
let value;
|
|
67
|
+
try {
|
|
68
|
+
value = JSON.parse(stdout);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!(error instanceof SyntaxError)) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
throw new LegacyMigrationError('The Windows credential reader returned an invalid response.');
|
|
75
|
+
}
|
|
76
|
+
if (value === null) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
if (!value || typeof value !== 'object'
|
|
80
|
+
|| !('name' in value) || typeof value.name !== 'string' || value.name.toLowerCase() !== name.toLowerCase()
|
|
81
|
+
|| !('pat' in value) || typeof value.pat !== 'string' || !value.pat) {
|
|
82
|
+
throw new LegacyMigrationError('The Windows credential reader returned an invalid credential.');
|
|
83
|
+
}
|
|
84
|
+
return { name, pat: value.pat };
|
|
85
|
+
}
|
|
86
|
+
async function readLegacyCredential(serviceName, publisherName, { platform = process.platform, run = runCredentialCommand } = {}) {
|
|
87
|
+
(0, validation_1.validatePublisher)(publisherName);
|
|
88
|
+
if (platform === 'win32') {
|
|
89
|
+
const powershell = path.win32.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
90
|
+
const { stdout, exitCode } = await run(powershell, [
|
|
91
|
+
'-NoLogo', '-NoProfile', '-NonInteractive', '-Command', windowsKeytar_1.windowsKeytarReadScript,
|
|
92
|
+
], { ...process.env, VSCE_KEYTAR_SERVICE: serviceName, VSCE_KEYTAR_ACCOUNT: publisherName });
|
|
93
|
+
if (exitCode !== 0) {
|
|
94
|
+
throw new LegacyMigrationError('Windows PowerShell could not read the previous credential.');
|
|
95
|
+
}
|
|
96
|
+
return parseWindowsCredential(stdout, publisherName);
|
|
97
|
+
}
|
|
98
|
+
if (platform !== 'linux') {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const { stdout, stderr, exitCode } = await run('secret-tool', [
|
|
102
|
+
'lookup', 'service', serviceName, 'account', publisherName,
|
|
103
|
+
'xdg:schema', 'org.freedesktop.Secret.Generic',
|
|
104
|
+
]);
|
|
105
|
+
// secret-tool exits 1 without output when no item matches; other failures
|
|
106
|
+
// must not masquerade as a missing credential or expose raw diagnostics.
|
|
107
|
+
if (exitCode === 1 && !stdout && !stderr) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (exitCode !== 0 || !stdout) {
|
|
111
|
+
throw new LegacyMigrationError('secret-tool could not read the previous credential.');
|
|
112
|
+
}
|
|
113
|
+
return { name: publisherName, pat: stdout };
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=legacyCredentials.js.map
|
package/out/main.js
CHANGED
|
@@ -32,12 +32,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
const commander_1 = require("commander");
|
|
40
|
-
const leven_1 = __importDefault(require("leven"));
|
|
41
37
|
const package_1 = require("./package");
|
|
42
38
|
const publish_1 = require("./publish");
|
|
43
39
|
const show_1 = require("./show");
|
|
@@ -311,7 +307,7 @@ module.exports = function (argv) {
|
|
|
311
307
|
}
|
|
312
308
|
program.outputHelp(help => {
|
|
313
309
|
const availableCommands = program.commands.map(c => c.name());
|
|
314
|
-
const suggestion =
|
|
310
|
+
const suggestion = (0, util_1.findSimilar)(cmd, availableCommands);
|
|
315
311
|
help = `${help}\n Unknown command '${cmd}'`;
|
|
316
312
|
return suggestion ? `${help}, did you mean '${suggestion}'?\n` : `${help}.\n`;
|
|
317
313
|
});
|
package/out/multipart.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMultipartStream = createMultipartStream;
|
|
4
|
+
exports.runWithStreamError = runWithStreamError;
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
|
+
function escapeHeaderParameter(value) {
|
|
7
|
+
return value.replace(/\r/g, '%0D').replace(/\n/g, '%0A').replace(/"/g, '%22');
|
|
8
|
+
}
|
|
9
|
+
function createMultipartStream(parts, boundary) {
|
|
10
|
+
const lineBreak = '\r\n';
|
|
11
|
+
for (const part of parts) {
|
|
12
|
+
// A part can fail before it is reached. Readable stores the error and its async iterator
|
|
13
|
+
// rethrows it when the part is consumed; this listener just keeps it from being unhandled.
|
|
14
|
+
part.stream.on('error', () => { });
|
|
15
|
+
}
|
|
16
|
+
const destroyParts = () => {
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
part.stream.destroy();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const stream = stream_1.Readable.from((async function* () {
|
|
22
|
+
try {
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
yield `--${boundary}${lineBreak}` +
|
|
25
|
+
`Content-Disposition: attachment; name=${escapeHeaderParameter(part.name)}; filename="${escapeHeaderParameter(part.filename)}"${lineBreak}` +
|
|
26
|
+
`Content-Type: application/octet-stream${lineBreak}${lineBreak}`;
|
|
27
|
+
yield* part.stream;
|
|
28
|
+
yield lineBreak;
|
|
29
|
+
}
|
|
30
|
+
yield `--${boundary}--${lineBreak}`;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
// Parts after a failed or abandoned one are never consumed, so release them here.
|
|
34
|
+
destroyParts();
|
|
35
|
+
}
|
|
36
|
+
})(),
|
|
37
|
+
// Without this the stream emits 'close' once it ends, which makes consumers that treat
|
|
38
|
+
// 'close' as "the body is complete" end the underlying request a second time.
|
|
39
|
+
{ autoDestroy: false });
|
|
40
|
+
// The generator body, and therefore its `finally`, never runs if the stream is destroyed
|
|
41
|
+
// before anything is read from it.
|
|
42
|
+
stream.on('close', destroyParts);
|
|
43
|
+
return stream;
|
|
44
|
+
}
|
|
45
|
+
function runWithStreamError(stream, operation) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
// This listener is deliberately never removed. An error arriving after the operation has
|
|
48
|
+
// settled would otherwise be an unhandled 'error' event, which terminates the process.
|
|
49
|
+
// Settling a promise more than once is a no-op, so the first outcome wins.
|
|
50
|
+
stream.on('error', reject);
|
|
51
|
+
Promise.resolve().then(operation).then(resolve, reject);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=multipart.js.map
|
package/out/publish.js
CHANGED
|
@@ -32,9 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.publish = publish;
|
|
40
37
|
exports.unpublish = unpublish;
|
|
@@ -47,12 +44,12 @@ const store_1 = require("./store");
|
|
|
47
44
|
const util_1 = require("./util");
|
|
48
45
|
const zip_1 = require("./zip");
|
|
49
46
|
const validation_1 = require("./validation");
|
|
50
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
51
47
|
const path_1 = require("path");
|
|
52
48
|
const os_1 = require("os");
|
|
53
49
|
const cockatiel_1 = require("cockatiel");
|
|
54
50
|
const auth_1 = require("./auth");
|
|
55
51
|
const oidc_1 = require("./oidc");
|
|
52
|
+
const multipart_1 = require("./multipart");
|
|
56
53
|
async function withTemporaryPackage(fn) {
|
|
57
54
|
const directory = await fs.promises.mkdtemp((0, path_1.join)((0, os_1.tmpdir)(), 'vsce-'));
|
|
58
55
|
try {
|
|
@@ -143,7 +140,6 @@ async function publish(options = {}) {
|
|
|
143
140
|
async function _publish(packagePath, sigzipPath, manifest, options) {
|
|
144
141
|
const pat = await getPAT(manifest.publisher, options);
|
|
145
142
|
const api = await (0, util_1.getGalleryAPI)(pat);
|
|
146
|
-
const packageStream = fs.createReadStream(packagePath);
|
|
147
143
|
const name = `${manifest.publisher}.${manifest.name}`;
|
|
148
144
|
const description = options.target
|
|
149
145
|
? `${name} (${options.target}) v${manifest.version}`
|
|
@@ -172,11 +168,11 @@ async function _publish(packagePath, sigzipPath, manifest, options) {
|
|
|
172
168
|
}
|
|
173
169
|
}
|
|
174
170
|
if (sigzipPath) {
|
|
175
|
-
await _publishSignedPackage(api,
|
|
171
|
+
await _publishSignedPackage(api, packagePath, sigzipPath, manifest);
|
|
176
172
|
}
|
|
177
173
|
else {
|
|
178
174
|
try {
|
|
179
|
-
await api.updateExtension(undefined,
|
|
175
|
+
await api.updateExtension(undefined, fs.createReadStream(packagePath), manifest.publisher, manifest.name);
|
|
180
176
|
}
|
|
181
177
|
catch (err) {
|
|
182
178
|
if (err.statusCode === 409) {
|
|
@@ -196,10 +192,10 @@ async function _publish(packagePath, sigzipPath, manifest, options) {
|
|
|
196
192
|
}
|
|
197
193
|
else {
|
|
198
194
|
if (sigzipPath) {
|
|
199
|
-
await _publishSignedPackage(api,
|
|
195
|
+
await _publishSignedPackage(api, packagePath, sigzipPath, manifest);
|
|
200
196
|
}
|
|
201
197
|
else {
|
|
202
|
-
await api.createExtension(undefined,
|
|
198
|
+
await api.createExtension(undefined, fs.createReadStream(packagePath));
|
|
203
199
|
}
|
|
204
200
|
}
|
|
205
201
|
}
|
|
@@ -217,23 +213,27 @@ async function _publish(packagePath, sigzipPath, manifest, options) {
|
|
|
217
213
|
util_1.log.info(`Hub URL: ${(0, util_1.getHubUrl)(manifest.publisher, manifest.name)}`);
|
|
218
214
|
util_1.log.done(`Published ${description}.`);
|
|
219
215
|
}
|
|
220
|
-
async function _publishSignedPackage(api,
|
|
216
|
+
async function _publishSignedPackage(api, packagePath, sigzipPath, manifest) {
|
|
221
217
|
const extensionType = 'Visual Studio Code';
|
|
222
|
-
const form = new form_data_1.default();
|
|
223
|
-
const lineBreak = '\r\n';
|
|
224
|
-
form.setBoundary('0f411892-ef48-488f-89d3-4f0546e84723');
|
|
225
|
-
form.append('vsix', packageStream, {
|
|
226
|
-
header: `--${form.getBoundary()}${lineBreak}Content-Disposition: attachment; name=vsix; filename=\"${packageName}\"${lineBreak}Content-Type: application/octet-stream${lineBreak}${lineBreak}`
|
|
227
|
-
});
|
|
228
|
-
form.append('sigzip', sigzipStream, {
|
|
229
|
-
header: `--${form.getBoundary()}${lineBreak}Content-Disposition: attachment; name=sigzip; filename=\"${sigzipName}\"${lineBreak}Content-Type: application/octet-stream${lineBreak}${lineBreak}`
|
|
230
|
-
});
|
|
231
218
|
const publishWithRetry = (0, cockatiel_1.retry)((0, cockatiel_1.handleWhen)(err => err.message.includes('timeout')), {
|
|
232
219
|
maxAttempts: 3,
|
|
233
220
|
backoff: new cockatiel_1.IterableBackoff([5_000, 10_000, 20_000])
|
|
234
221
|
});
|
|
235
222
|
return await publishWithRetry.execute(async () => {
|
|
236
|
-
|
|
223
|
+
const form = (0, multipart_1.createMultipartStream)([
|
|
224
|
+
{ name: 'vsix', filename: (0, path_1.basename)(packagePath), stream: fs.createReadStream(packagePath) },
|
|
225
|
+
{ name: 'sigzip', filename: (0, path_1.basename)(sigzipPath), stream: fs.createReadStream(sigzipPath) },
|
|
226
|
+
], '0f411892-ef48-488f-89d3-4f0546e84723');
|
|
227
|
+
try {
|
|
228
|
+
return await (0, multipart_1.runWithStreamError)(form, () => api.publishExtensionWithPublisherSignature(undefined, form, manifest.publisher, manifest.name, extensionType));
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
// Release the file handles when the request did not consume the whole form, which
|
|
232
|
+
// otherwise keeps the package locked until the process exits.
|
|
233
|
+
if (!form.readableEnded) {
|
|
234
|
+
form.destroy();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
237
|
});
|
|
238
238
|
}
|
|
239
239
|
async function unpublish(options = {}) {
|
package/out/store.js
CHANGED
|
@@ -47,6 +47,7 @@ const util_1 = require("./util");
|
|
|
47
47
|
const validation_1 = require("./validation");
|
|
48
48
|
const package_1 = require("./package");
|
|
49
49
|
const publish_1 = require("./publish");
|
|
50
|
+
const keytarMigration_1 = require("./keytarMigration");
|
|
50
51
|
;
|
|
51
52
|
class FileStore {
|
|
52
53
|
path;
|
|
@@ -105,8 +106,7 @@ class KeytarStore {
|
|
|
105
106
|
keytar;
|
|
106
107
|
serviceName;
|
|
107
108
|
publishers;
|
|
108
|
-
static async open(serviceName = 'vscode-vsce') {
|
|
109
|
-
const keytar = require('@napi-rs/keyring/keytar.js');
|
|
109
|
+
static async open(serviceName = 'vscode-vsce', keytar = require('@napi-rs/keyring/keytar.js')) {
|
|
110
110
|
const creds = await keytar.findCredentials(serviceName);
|
|
111
111
|
return new KeytarStore(keytar, serviceName, creds.map(({ account, password }) => ({ name: account, pat: password })));
|
|
112
112
|
}
|
|
@@ -122,12 +122,14 @@ class KeytarStore {
|
|
|
122
122
|
return this.publishers.filter(p => p.name === name)[0];
|
|
123
123
|
}
|
|
124
124
|
async add(publisher) {
|
|
125
|
-
this.publishers = [...this.publishers.filter(p => p.name !== publisher.name), publisher];
|
|
126
125
|
await this.keytar.setPassword(this.serviceName, publisher.name, publisher.pat);
|
|
126
|
+
this.publishers = [...this.publishers.filter(p => p.name !== publisher.name), publisher];
|
|
127
127
|
}
|
|
128
128
|
async delete(name) {
|
|
129
|
+
if (!await this.keytar.deletePassword(this.serviceName, name)) {
|
|
130
|
+
throw new Error(`Could not remove the saved PAT for publisher '${name}'.`);
|
|
131
|
+
}
|
|
129
132
|
this.publishers = this.publishers.filter(p => p.name !== name);
|
|
130
|
-
await this.keytar.deletePassword(this.serviceName, name);
|
|
131
133
|
}
|
|
132
134
|
[Symbol.iterator]() {
|
|
133
135
|
return this.publishers[Symbol.iterator]();
|
|
@@ -171,6 +173,7 @@ async function openDefaultStore() {
|
|
|
171
173
|
util_1.log.warn(`Failed to open credential store. Falling back to storing secrets clear-text in: ${store.path}`);
|
|
172
174
|
return store;
|
|
173
175
|
}
|
|
176
|
+
keytarStore = keytarMigration_1.LegacyCredentialMigration.wrap(keytarStore, () => KeytarStore.open());
|
|
174
177
|
const fileStore = await FileStore.open();
|
|
175
178
|
// migrate from file store
|
|
176
179
|
if (fileStore.size) {
|
|
@@ -185,7 +188,7 @@ async function openDefaultStore() {
|
|
|
185
188
|
async function getPublisher(publisherName) {
|
|
186
189
|
(0, validation_1.validatePublisher)(publisherName);
|
|
187
190
|
const store = await openDefaultStore();
|
|
188
|
-
let publisher = store.get(publisherName);
|
|
191
|
+
let publisher = store.get(publisherName) ?? await store.tryMigratePublisher?.(publisherName);
|
|
189
192
|
if (publisher) {
|
|
190
193
|
return publisher;
|
|
191
194
|
}
|
|
@@ -205,6 +208,12 @@ async function loginPublisher(publisherName) {
|
|
|
205
208
|
throw new Error('Aborted');
|
|
206
209
|
}
|
|
207
210
|
}
|
|
211
|
+
else {
|
|
212
|
+
publisher = await store.tryMigratePublisher?.(publisherName);
|
|
213
|
+
if (publisher) {
|
|
214
|
+
return publisher;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
208
217
|
const pat = await requestPAT(publisherName);
|
|
209
218
|
publisher = { name: publisherName, pat };
|
|
210
219
|
await store.add(publisher);
|
package/out/util.js
CHANGED
|
@@ -48,6 +48,8 @@ exports.normalize = normalize;
|
|
|
48
48
|
exports.chain = chain;
|
|
49
49
|
exports.flatten = flatten;
|
|
50
50
|
exports.nonnull = nonnull;
|
|
51
|
+
exports.levenshtein = levenshtein;
|
|
52
|
+
exports.findSimilar = findSimilar;
|
|
51
53
|
exports.isCancelledError = isCancelledError;
|
|
52
54
|
exports.sequence = sequence;
|
|
53
55
|
exports.patchOptionsWithManifest = patchOptionsWithManifest;
|
|
@@ -112,6 +114,51 @@ function flatten(arr) {
|
|
|
112
114
|
function nonnull(arg) {
|
|
113
115
|
return !!arg;
|
|
114
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Computes the Levenshtein distance between `a` and `b`, ie. the minimum number of
|
|
119
|
+
* single character insertions, deletions or substitutions needed to turn one into the
|
|
120
|
+
* other. Comparison happens on UTF-16 code units.
|
|
121
|
+
*/
|
|
122
|
+
function levenshtein(a, b) {
|
|
123
|
+
if (a === b) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
// The distance is symmetric, so keep the shorter string in `b` to bound the row size.
|
|
127
|
+
if (b.length > a.length) {
|
|
128
|
+
[a, b] = [b, a];
|
|
129
|
+
}
|
|
130
|
+
// A single row of the edit distance matrix, seeded with the distance between the
|
|
131
|
+
// empty prefix of `a` and every prefix of `b`.
|
|
132
|
+
const row = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
133
|
+
for (let i = 1; i <= a.length; i++) {
|
|
134
|
+
// The value of `row[j - 1]` before this row started being overwritten.
|
|
135
|
+
let diagonal = row[0];
|
|
136
|
+
row[0] = i;
|
|
137
|
+
for (let j = 1; j <= b.length; j++) {
|
|
138
|
+
const above = row[j];
|
|
139
|
+
row[j] = Math.min(row[j - 1] + 1, above + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
140
|
+
diagonal = above;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return row[b.length];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Returns the candidate closest to `target`, or `undefined` when none of them is a
|
|
147
|
+
* plausible correction of it. A candidate qualifies when fewer than 40% of the characters
|
|
148
|
+
* of `target` have to be edited to reach it; ties are broken by iteration order.
|
|
149
|
+
*/
|
|
150
|
+
function findSimilar(target, candidates) {
|
|
151
|
+
let best;
|
|
152
|
+
let bestDistance = Math.ceil(target.length * 0.4);
|
|
153
|
+
for (const candidate of candidates) {
|
|
154
|
+
const distance = levenshtein(candidate, target);
|
|
155
|
+
if (distance < bestDistance) {
|
|
156
|
+
best = candidate;
|
|
157
|
+
bestDistance = distance;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return best;
|
|
161
|
+
}
|
|
115
162
|
const CancelledError = 'Cancelled';
|
|
116
163
|
function isCancelledError(error) {
|
|
117
164
|
return error === CancelledError;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.windowsKeytarReadScript = void 0;
|
|
4
|
+
// Read keytar's UTF-8 credentials without constructing a keyring Entry.withTarget,
|
|
5
|
+
// which overwrites the target credential even when only used to read a secret.
|
|
6
|
+
exports.windowsKeytarReadScript = String.raw `
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
9
|
+
Add-Type -TypeDefinition @'
|
|
10
|
+
using System;
|
|
11
|
+
using System.ComponentModel;
|
|
12
|
+
using System.Runtime.InteropServices;
|
|
13
|
+
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
|
|
14
|
+
using System.Text;
|
|
15
|
+
|
|
16
|
+
public static class VsceKeytarReader {
|
|
17
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
18
|
+
private struct Credential {
|
|
19
|
+
public uint Flags;
|
|
20
|
+
public uint Type;
|
|
21
|
+
public string TargetName;
|
|
22
|
+
public string Comment;
|
|
23
|
+
public FILETIME LastWritten;
|
|
24
|
+
public uint CredentialBlobSize;
|
|
25
|
+
public IntPtr CredentialBlob;
|
|
26
|
+
public uint Persist;
|
|
27
|
+
public uint AttributeCount;
|
|
28
|
+
public IntPtr Attributes;
|
|
29
|
+
public string TargetAlias;
|
|
30
|
+
public string UserName;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public class Publisher {
|
|
34
|
+
public string name;
|
|
35
|
+
public string pat;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
39
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
40
|
+
private static extern bool CredRead(string target, uint type, uint flags, out IntPtr credential);
|
|
41
|
+
|
|
42
|
+
[DllImport("advapi32.dll")]
|
|
43
|
+
private static extern void CredFree(IntPtr credentials);
|
|
44
|
+
|
|
45
|
+
public static Publisher Read(string service, string account) {
|
|
46
|
+
IntPtr pointer;
|
|
47
|
+
if (!CredRead(service + "/" + account, 1, 0, out pointer)) {
|
|
48
|
+
int error = Marshal.GetLastWin32Error();
|
|
49
|
+
if (error == 1168) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
throw new Win32Exception(error);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
var credential = (Credential)Marshal.PtrToStructure(pointer, typeof(Credential));
|
|
57
|
+
if (!String.Equals(credential.UserName, account, StringComparison.OrdinalIgnoreCase)) {
|
|
58
|
+
throw new InvalidOperationException("The credential account does not match.");
|
|
59
|
+
}
|
|
60
|
+
var secret = new byte[checked((int)credential.CredentialBlobSize)];
|
|
61
|
+
try {
|
|
62
|
+
if (secret.Length > 0) {
|
|
63
|
+
Marshal.Copy(credential.CredentialBlob, secret, 0, secret.Length);
|
|
64
|
+
}
|
|
65
|
+
return new Publisher {
|
|
66
|
+
name = credential.UserName,
|
|
67
|
+
pat = new UTF8Encoding(false, true).GetString(secret)
|
|
68
|
+
};
|
|
69
|
+
} finally {
|
|
70
|
+
Array.Clear(secret, 0, secret.Length);
|
|
71
|
+
}
|
|
72
|
+
} finally {
|
|
73
|
+
CredFree(pointer);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
'@
|
|
78
|
+
$credential = [VsceKeytarReader]::Read($env:VSCE_KEYTAR_SERVICE, $env:VSCE_KEYTAR_ACCOUNT)
|
|
79
|
+
if ($null -eq $credential) {
|
|
80
|
+
[Console]::WriteLine('null')
|
|
81
|
+
} else {
|
|
82
|
+
ConvertTo-Json -InputObject $credential -Compress
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
//# sourceMappingURL=windowsKeytar.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vscode/vsce",
|
|
3
|
-
"version": "3.9.3-
|
|
3
|
+
"version": "3.9.3-12",
|
|
4
4
|
"description": "VS Code Extensions Manager",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,50 +39,50 @@
|
|
|
39
39
|
"node": ">= 22"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@azure/identity": "^4.
|
|
42
|
+
"@azure/identity": "^4.13.2",
|
|
43
43
|
"@napi-rs/keyring": "^1.3.0",
|
|
44
|
-
"@secretlint/core": "^10.
|
|
45
|
-
"@secretlint/secretlint-rule-no-dotenv": "^10.
|
|
46
|
-
"@secretlint/secretlint-rule-preset-recommend": "^10.
|
|
47
|
-
"@secretlint/source-creator": "^10.
|
|
48
|
-
"@secretlint/types": "^10.
|
|
49
|
-
"@vscode/vsce-sign": "^2.
|
|
44
|
+
"@secretlint/core": "^10.2.2",
|
|
45
|
+
"@secretlint/secretlint-rule-no-dotenv": "^10.2.2",
|
|
46
|
+
"@secretlint/secretlint-rule-preset-recommend": "^10.2.2",
|
|
47
|
+
"@secretlint/source-creator": "^10.2.2",
|
|
48
|
+
"@secretlint/types": "^10.2.2",
|
|
49
|
+
"@vscode/vsce-sign": "^2.1.0",
|
|
50
50
|
"azure-devops-node-api": "^12.5.0",
|
|
51
|
-
"cockatiel": "^3.1
|
|
51
|
+
"cockatiel": "^3.2.1",
|
|
52
52
|
"commander": "^12.1.0",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"mime": "^1.3.4",
|
|
59
|
-
"minimatch": "^10.2.2",
|
|
53
|
+
"hosted-git-info": "^4.1.0",
|
|
54
|
+
"jsonc-parser": "^3.3.1",
|
|
55
|
+
"marked": "^18.0.11",
|
|
56
|
+
"mime": "^1.6.0",
|
|
57
|
+
"minimatch": "^10.2.6",
|
|
60
58
|
"parse5": "^8.0.1",
|
|
59
|
+
"proper-lockfile": "^4.1.2",
|
|
61
60
|
"read": "^1.0.7",
|
|
62
|
-
"semver": "^7.5
|
|
61
|
+
"semver": "^7.8.5",
|
|
63
62
|
"tinyglobby": "^0.2.17",
|
|
64
|
-
"typed-rest-client": "^1.8.
|
|
63
|
+
"typed-rest-client": "^1.8.11",
|
|
65
64
|
"url-join": "^4.0.1",
|
|
66
65
|
"xml2js": "^0.5.0",
|
|
67
|
-
"yauzl": "^3.
|
|
68
|
-
"yazl": "^2.
|
|
66
|
+
"yauzl": "^3.4.0",
|
|
67
|
+
"yazl": "^2.5.1"
|
|
69
68
|
},
|
|
70
69
|
"devDependencies": {
|
|
71
|
-
"@microsoft/api-extractor": "^7.
|
|
72
|
-
"@types/hosted-git-info": "^3.0.
|
|
73
|
-
"@types/mime": "^1",
|
|
70
|
+
"@microsoft/api-extractor": "^7.59.0",
|
|
71
|
+
"@types/hosted-git-info": "^3.0.5",
|
|
72
|
+
"@types/mime": "^1.3.5",
|
|
74
73
|
"@types/mocha": "^7.0.2",
|
|
75
|
-
"@types/node": "^22.
|
|
74
|
+
"@types/node": "^22.20.1",
|
|
76
75
|
"@types/picomatch": "^4.0.3",
|
|
76
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
77
77
|
"@types/read": "^0.0.28",
|
|
78
|
-
"@types/semver": "^6.
|
|
79
|
-
"@types/url-join": "^4.0.
|
|
80
|
-
"@types/xml2js": "^0.4.
|
|
81
|
-
"@types/yauzl": "^2.
|
|
82
|
-
"@types/yazl": "^2.4.
|
|
83
|
-
"mocha": "^11.
|
|
84
|
-
"ts-node": "^10.9.
|
|
85
|
-
"typescript": "~5.9.
|
|
78
|
+
"@types/semver": "^6.2.7",
|
|
79
|
+
"@types/url-join": "^4.0.3",
|
|
80
|
+
"@types/xml2js": "^0.4.14",
|
|
81
|
+
"@types/yauzl": "^2.10.3",
|
|
82
|
+
"@types/yazl": "^2.4.6",
|
|
83
|
+
"mocha": "^11.8.0",
|
|
84
|
+
"ts-node": "^10.9.2",
|
|
85
|
+
"typescript": "~5.9.3"
|
|
86
86
|
},
|
|
87
87
|
"mocha": {
|
|
88
88
|
"require": [
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
"overrides": {
|
|
95
95
|
"serialize-javascript": "7.x",
|
|
96
|
-
"diff": "^9.0.0"
|
|
96
|
+
"diff": "^9.0.0",
|
|
97
|
+
"@azure/msal-common": "16.14.0"
|
|
97
98
|
}
|
|
98
99
|
}
|