@verdaccio/local-storage 14.0.0-next-9.30 → 14.0.0-next-9.32
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/build/fs.js +0 -9
- package/build/fs.js.map +1 -1
- package/build/fs.mjs +2 -10
- package/build/fs.mjs.map +1 -1
- package/build/local-fs.d.ts +5 -0
- package/build/local-fs.js +29 -8
- package/build/local-fs.js.map +1 -1
- package/build/local-fs.mjs +30 -9
- package/build/local-fs.mjs.map +1 -1
- package/package.json +6 -6
package/build/fs.js
CHANGED
|
@@ -16,17 +16,8 @@ var openPromise = node_fs_promises.open;
|
|
|
16
16
|
var readFilePromise = async (path) => {
|
|
17
17
|
return await readFile(path, "utf8");
|
|
18
18
|
};
|
|
19
|
-
function fstatPromise(fd) {
|
|
20
|
-
return new Promise((resolve, reject) => {
|
|
21
|
-
node_fs.default.fstat(fd, function(err, stats) {
|
|
22
|
-
if (err) return reject(err);
|
|
23
|
-
return resolve(stats);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
19
|
//#endregion
|
|
28
20
|
exports.accessPromise = accessPromise;
|
|
29
|
-
exports.fstatPromise = fstatPromise;
|
|
30
21
|
exports.mkdirPromise = mkdirPromise;
|
|
31
22
|
exports.openPromise = openPromise;
|
|
32
23
|
exports.readFilePromise = readFilePromise;
|
package/build/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.js","names":[],"sources":["../src/fs.ts"],"sourcesContent":["import fsCallback from 'node:fs';\nimport * as fsP from 'node:fs/promises';\n\nconst readFile = fsP.readFile;\nconst mkdirPromise = fsP.mkdir;\nconst accessPromise = fsP.access;\nconst writeFilePromise = fsP.writeFile;\nconst readdirPromise = fsP.readdir;\nconst statPromise = fsP.stat;\nconst unlinkPromise = fsP.unlink;\nconst rmdirPromise = fsP.rmdir;\nconst renamePromise = fsP.rename;\nconst openPromise = fsP.open;\n\nconst readFilePromise = async (path) => {\n return await readFile(path, 'utf8');\n};\n\nfunction fstatPromise(fd: number): Promise<fsCallback.Stats> {\n return new Promise((resolve, reject) => {\n fsCallback.fstat(fd, function (err, stats) {\n if (err) {\n return reject(err);\n }\n return resolve(stats);\n });\n });\n}\n\nexport {\n readFilePromise,\n renamePromise,\n mkdirPromise,\n writeFilePromise,\n readdirPromise,\n statPromise,\n accessPromise,\n unlinkPromise,\n rmdirPromise,\n openPromise,\n fstatPromise,\n};\n"],"mappings":";;;;;;AAGA,IAAM,WAAW,iBAAI;AACrB,IAAM,eAAe,iBAAI;AACzB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,mBAAmB,iBAAI;AAE7B,IAAM,cAAc,iBAAI;AACxB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,eAAe,iBAAI;AACzB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,cAAc,iBAAI;AAExB,IAAM,kBAAkB,OAAO,SAAS;CACtC,OAAO,MAAM,SAAS,MAAM,MAAM;AACpC
|
|
1
|
+
{"version":3,"file":"fs.js","names":[],"sources":["../src/fs.ts"],"sourcesContent":["import fsCallback from 'node:fs';\nimport * as fsP from 'node:fs/promises';\n\nconst readFile = fsP.readFile;\nconst mkdirPromise = fsP.mkdir;\nconst accessPromise = fsP.access;\nconst writeFilePromise = fsP.writeFile;\nconst readdirPromise = fsP.readdir;\nconst statPromise = fsP.stat;\nconst unlinkPromise = fsP.unlink;\nconst rmdirPromise = fsP.rmdir;\nconst renamePromise = fsP.rename;\nconst openPromise = fsP.open;\n\nconst readFilePromise = async (path) => {\n return await readFile(path, 'utf8');\n};\n\nfunction fstatPromise(fd: number): Promise<fsCallback.Stats> {\n return new Promise((resolve, reject) => {\n fsCallback.fstat(fd, function (err, stats) {\n if (err) {\n return reject(err);\n }\n return resolve(stats);\n });\n });\n}\n\nexport {\n readFilePromise,\n renamePromise,\n mkdirPromise,\n writeFilePromise,\n readdirPromise,\n statPromise,\n accessPromise,\n unlinkPromise,\n rmdirPromise,\n openPromise,\n fstatPromise,\n};\n"],"mappings":";;;;;;AAGA,IAAM,WAAW,iBAAI;AACrB,IAAM,eAAe,iBAAI;AACzB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,mBAAmB,iBAAI;AAE7B,IAAM,cAAc,iBAAI;AACxB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,eAAe,iBAAI;AACzB,IAAM,gBAAgB,iBAAI;AAC1B,IAAM,cAAc,iBAAI;AAExB,IAAM,kBAAkB,OAAO,SAAS;CACtC,OAAO,MAAM,SAAS,MAAM,MAAM;AACpC"}
|
package/build/fs.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "node:fs";
|
|
2
2
|
import * as fsP from "node:fs/promises";
|
|
3
3
|
//#region src/fs.ts
|
|
4
4
|
var readFile = fsP.readFile;
|
|
@@ -13,15 +13,7 @@ var openPromise = fsP.open;
|
|
|
13
13
|
var readFilePromise = async (path) => {
|
|
14
14
|
return await readFile(path, "utf8");
|
|
15
15
|
};
|
|
16
|
-
function fstatPromise(fd) {
|
|
17
|
-
return new Promise((resolve, reject) => {
|
|
18
|
-
fsCallback.fstat(fd, function(err, stats) {
|
|
19
|
-
if (err) return reject(err);
|
|
20
|
-
return resolve(stats);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
16
|
//#endregion
|
|
25
|
-
export { accessPromise,
|
|
17
|
+
export { accessPromise, mkdirPromise, openPromise, readFilePromise, renamePromise, rmdirPromise, statPromise, unlinkPromise, writeFilePromise };
|
|
26
18
|
|
|
27
19
|
//# sourceMappingURL=fs.mjs.map
|
package/build/fs.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.mjs","names":[],"sources":["../src/fs.ts"],"sourcesContent":["import fsCallback from 'node:fs';\nimport * as fsP from 'node:fs/promises';\n\nconst readFile = fsP.readFile;\nconst mkdirPromise = fsP.mkdir;\nconst accessPromise = fsP.access;\nconst writeFilePromise = fsP.writeFile;\nconst readdirPromise = fsP.readdir;\nconst statPromise = fsP.stat;\nconst unlinkPromise = fsP.unlink;\nconst rmdirPromise = fsP.rmdir;\nconst renamePromise = fsP.rename;\nconst openPromise = fsP.open;\n\nconst readFilePromise = async (path) => {\n return await readFile(path, 'utf8');\n};\n\nfunction fstatPromise(fd: number): Promise<fsCallback.Stats> {\n return new Promise((resolve, reject) => {\n fsCallback.fstat(fd, function (err, stats) {\n if (err) {\n return reject(err);\n }\n return resolve(stats);\n });\n });\n}\n\nexport {\n readFilePromise,\n renamePromise,\n mkdirPromise,\n writeFilePromise,\n readdirPromise,\n statPromise,\n accessPromise,\n unlinkPromise,\n rmdirPromise,\n openPromise,\n fstatPromise,\n};\n"],"mappings":";;;AAGA,IAAM,WAAW,IAAI;AACrB,IAAM,eAAe,IAAI;AACzB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,mBAAmB,IAAI;AAE7B,IAAM,cAAc,IAAI;AACxB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,eAAe,IAAI;AACzB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,cAAc,IAAI;AAExB,IAAM,kBAAkB,OAAO,SAAS;CACtC,OAAO,MAAM,SAAS,MAAM,MAAM;AACpC
|
|
1
|
+
{"version":3,"file":"fs.mjs","names":[],"sources":["../src/fs.ts"],"sourcesContent":["import fsCallback from 'node:fs';\nimport * as fsP from 'node:fs/promises';\n\nconst readFile = fsP.readFile;\nconst mkdirPromise = fsP.mkdir;\nconst accessPromise = fsP.access;\nconst writeFilePromise = fsP.writeFile;\nconst readdirPromise = fsP.readdir;\nconst statPromise = fsP.stat;\nconst unlinkPromise = fsP.unlink;\nconst rmdirPromise = fsP.rmdir;\nconst renamePromise = fsP.rename;\nconst openPromise = fsP.open;\n\nconst readFilePromise = async (path) => {\n return await readFile(path, 'utf8');\n};\n\nfunction fstatPromise(fd: number): Promise<fsCallback.Stats> {\n return new Promise((resolve, reject) => {\n fsCallback.fstat(fd, function (err, stats) {\n if (err) {\n return reject(err);\n }\n return resolve(stats);\n });\n });\n}\n\nexport {\n readFilePromise,\n renamePromise,\n mkdirPromise,\n writeFilePromise,\n readdirPromise,\n statPromise,\n accessPromise,\n unlinkPromise,\n rmdirPromise,\n openPromise,\n fstatPromise,\n};\n"],"mappings":";;;AAGA,IAAM,WAAW,IAAI;AACrB,IAAM,eAAe,IAAI;AACzB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,mBAAmB,IAAI;AAE7B,IAAM,cAAc,IAAI;AACxB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,eAAe,IAAI;AACzB,IAAM,gBAAgB,IAAI;AAC1B,IAAM,cAAc,IAAI;AAExB,IAAM,kBAAkB,OAAO,SAAS;CACtC,OAAO,MAAM,SAAS,MAAM,MAAM;AACpC"}
|
package/build/local-fs.d.ts
CHANGED
|
@@ -52,6 +52,11 @@ export default class LocalFS implements ILocalFSPackageManager {
|
|
|
52
52
|
savePackage(name: string, value: Manifest): Promise<void>;
|
|
53
53
|
readPackage(name: string): Promise<Manifest>;
|
|
54
54
|
hasTarball(fileName: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Best-effort removal of a temporary file. Error and abort can both try to
|
|
57
|
+
* clean the same file from event listeners, so a missing file is fine and
|
|
58
|
+
* nothing may throw — a rejection here becomes an uncaught exception.
|
|
59
|
+
*/
|
|
55
60
|
private removeTempFile;
|
|
56
61
|
/**
|
|
57
62
|
* Write a tarball into the storage
|
package/build/local-fs.js
CHANGED
|
@@ -168,10 +168,22 @@ var LocalFS = class {
|
|
|
168
168
|
}).catch(() => resolve(false));
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Best-effort removal of a temporary file. Error and abort can both try to
|
|
173
|
+
* clean the same file from event listeners, so a missing file is fine and
|
|
174
|
+
* nothing may throw — a rejection here becomes an uncaught exception.
|
|
175
|
+
*/
|
|
171
176
|
async removeTempFile(temporalName) {
|
|
172
177
|
debug$1("remove temporal file %o", temporalName);
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
try {
|
|
179
|
+
await require_fs.unlinkPromise(temporalName);
|
|
180
|
+
debug$1("removed temporal file %o", temporalName);
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (err?.code !== "ENOENT") this.logger.warn({
|
|
183
|
+
temporalName,
|
|
184
|
+
err: err.message
|
|
185
|
+
}, "unable to remove temporal file @{temporalName}: @{err}");
|
|
186
|
+
}
|
|
175
187
|
}
|
|
176
188
|
/**
|
|
177
189
|
* Write a tarball into the storage
|
|
@@ -186,17 +198,19 @@ var LocalFS = class {
|
|
|
186
198
|
const temporalName = node_path.default.join(this.path, (0, sanitize_filename.default)(`${fileName}.tmp-${String(Math.random()).replace(/^0\./, "")}`));
|
|
187
199
|
debug$1("write a temporal name %o", temporalName);
|
|
188
200
|
let opened = false;
|
|
201
|
+
let failed = false;
|
|
189
202
|
const writeStream = node_fs.default.createWriteStream(temporalName);
|
|
190
203
|
writeStream.on("open", () => {
|
|
191
204
|
opened = true;
|
|
192
205
|
});
|
|
193
206
|
writeStream.on("error", async (err) => {
|
|
207
|
+
failed = true;
|
|
194
208
|
if (opened) {
|
|
195
209
|
this.logger.error({
|
|
196
210
|
err,
|
|
197
211
|
fileName
|
|
198
212
|
}, "error on open write tarball for @{fileName}: @{err.message}");
|
|
199
|
-
writeStream.
|
|
213
|
+
writeStream.once("close", async () => {
|
|
200
214
|
await this.removeTempFile(temporalName);
|
|
201
215
|
});
|
|
202
216
|
} else {
|
|
@@ -207,7 +221,8 @@ var LocalFS = class {
|
|
|
207
221
|
await this.removeTempFile(temporalName);
|
|
208
222
|
}
|
|
209
223
|
});
|
|
210
|
-
writeStream.
|
|
224
|
+
writeStream.once("close", async () => {
|
|
225
|
+
if (failed) return;
|
|
211
226
|
try {
|
|
212
227
|
await renameTmp(temporalName, pathName);
|
|
213
228
|
} catch (err) {
|
|
@@ -219,6 +234,7 @@ var LocalFS = class {
|
|
|
219
234
|
}
|
|
220
235
|
});
|
|
221
236
|
signal?.addEventListener("abort", async () => {
|
|
237
|
+
failed = true;
|
|
222
238
|
if (opened) writeStream.once("close", async () => {
|
|
223
239
|
await this.removeTempFile(temporalName);
|
|
224
240
|
});
|
|
@@ -236,12 +252,17 @@ var LocalFS = class {
|
|
|
236
252
|
const pathName = this._getStorage(tarballName);
|
|
237
253
|
debug$1("read a tarball %o", pathName);
|
|
238
254
|
const readStream = (0, node_stream.addAbortSignal)(signal, node_fs.default.createReadStream(pathName));
|
|
239
|
-
readStream.on("open",
|
|
255
|
+
readStream.on("open", function(fileDescriptorId) {
|
|
240
256
|
debug$1("file descriptor id %o", fileDescriptorId);
|
|
241
257
|
if (fileDescriptorId) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
let size;
|
|
259
|
+
try {
|
|
260
|
+
size = node_fs.default.fstatSync(fileDescriptorId).size;
|
|
261
|
+
} catch {}
|
|
262
|
+
if (size !== void 0) {
|
|
263
|
+
debug$1("file size %o", size);
|
|
264
|
+
readStream.emit("content-length", size);
|
|
265
|
+
}
|
|
245
266
|
}
|
|
246
267
|
});
|
|
247
268
|
readStream.on("error", (error) => {
|
package/build/local-fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-fs.js","names":[],"sources":["../src/local-fs.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable, Writable } from 'node:stream';\nimport { addAbortSignal } from 'node:stream';\nimport sanitzers from 'sanitize-filename';\n\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport { errorUtils } from '@verdaccio/core';\nimport { readFileNext, unlockFileNext } from '@verdaccio/file-locking';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport {\n accessPromise,\n fstatPromise,\n mkdirPromise,\n openPromise,\n readFilePromise,\n renamePromise,\n rmdirPromise,\n statPromise,\n unlinkPromise,\n writeFilePromise,\n} from './fs';\n\nexport const fileExist = 'EEXISTS';\nexport const noSuchFile = 'ENOENT';\nexport const resourceNotAvailable = 'EAGAIN';\nexport const packageJSONFileName = 'package.json';\n\nexport type ILocalFSPackageManager = pluginUtils.StorageHandler & { path: string };\n\nconst debug = buildDebug('verdaccio:plugin:local-storage:local-fs');\n\nexport const fSError = function (message: string, code = 409): VerdaccioError {\n const err: VerdaccioError = errorUtils.getCode(code, message);\n // FIXME: we should return http-status codes here instead, future improvement\n // @ts-ignore\n err.code = message;\n\n return err;\n};\n\nconst tempFile = function (str): string {\n return `${str}.tmp${String(Math.random()).slice(2)}`;\n};\n\nexport async function renameTmp(src: string, dst: string): Promise<void> {\n debug('rename %s to %s', src, dst);\n try {\n await renamePromise(src, dst);\n } catch (err: any) {\n debug('error rename %s error %s', src, err?.message);\n await unlinkPromise(src);\n }\n}\n\nexport default class LocalFS implements ILocalFSPackageManager {\n public path: string;\n public logger: Logger;\n\n public constructor(path: string, logger: Logger) {\n this.path = path;\n this.logger = logger;\n }\n\n /**\n * This function allows to update the package\n * This function handle the update package logic, for this plugin\n * we need to lock/unlock handlers for thread-safely and then apply\n * the `handleUpdate` and return the result.\n *\n * The lock could fail on several steps so we need to ensure the\n * file does not get locked if the whole process fails.\n *\n Algorithm:\n 1. lock package.json for writing\n 2. read package.json\n 3. apply external update package handler\n 4. return manifest (write is being hanlded into the core)\n 5. rename package.json.tmp package.json\n * @param {*} packageName\n * The update package handler could be different based\n * on the action and handled into the core.\n * @param {*} handleUpdate\n */\n public async updatePackage(\n packageName: string,\n handleUpdate: (manifest: Manifest) => Promise<Manifest>\n ): Promise<Manifest> {\n // this plugin lock files on write, we handle all possible scenarios\n let locked = false;\n let manifestUpdated: Manifest;\n try {\n const manifest = await this._lockAndReadJSON(packageJSONFileName);\n locked = true;\n manifestUpdated = await handleUpdate(manifest);\n if (locked) {\n debug('unlock %s', packageJSONFileName);\n await this._unlockJSON(packageJSONFileName);\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n } else {\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n }\n } catch (err: any) {\n // we ensure lock the file\n this.logger.error(\n { err, packageName },\n 'error on update package @{packageName}: @{err.message}'\n );\n if (locked) {\n try {\n await this._unlockJSON(packageJSONFileName);\n // after unlock bubble up error.\n throw err;\n } catch {\n // unlock could fail, we bubble up error\n throw errorUtils.getInternalError('resource temporarily unavailable');\n }\n } else {\n if (err.code === resourceNotAvailable) {\n throw errorUtils.getInternalError('resource temporarily unavailable');\n } else if (err.code === noSuchFile) {\n throw errorUtils.getNotFound();\n } else {\n throw err;\n }\n }\n }\n }\n\n public async deletePackage(packageName: string): Promise<void> {\n debug('delete a file/package %o', packageName);\n\n return await unlinkPromise(this._getStorage(packageName));\n }\n\n public async removePackage(): Promise<void> {\n debug('remove a package folder %o', this.path);\n\n await rmdirPromise(this._getStorage('.'));\n }\n\n /**\n * Verify if the package exists already.\n * @param name package name\n * @returns\n */\n public async hasPackage(): Promise<boolean> {\n const pathName: string = this._getStorage(packageJSONFileName);\n try {\n const stat = await statPromise(pathName);\n return stat.isFile();\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('dir: %o does not exist %s', pathName, err?.code);\n return false;\n } else {\n this.logger.error({ err }, 'error on verify a package exist: @{err.message}');\n throw errorUtils.getInternalError('error on verify a package exist');\n }\n }\n }\n\n /**\n * Create a package in the local storage, if package already exist fails.\n * @param name package name\n * @param manifest package manifest\n */\n public async createPackage(name: string, manifest: Manifest): Promise<void> {\n debug('create a new package %o', name);\n const pathPackage = this._getStorage(packageJSONFileName);\n try {\n // https://nodejs.org/dist/latest-v17.x/docs/api/fs.html#file-system-flags\n // 'wx': Like 'w' but fails if the path exists\n await openPromise(pathPackage, 'wx');\n } catch (err: any) {\n // cannot override a pacakge that already exist\n if (err.code === 'EEXIST') {\n debug('file %o cannot be created, it already exists: %o', name);\n throw fSError(fileExist);\n }\n }\n // Create a new file and it´s folder if does not exist previously\n await this.writeFile(pathPackage, this._convertToString(manifest));\n }\n\n public async savePackage(name: string, value: Manifest): Promise<void> {\n debug('save a package %o', name);\n\n await this.writeFile(this._getStorage(packageJSONFileName), this._convertToString(value));\n }\n\n public async readPackage(name: string): Promise<Manifest> {\n debug('read a package %o', name);\n try {\n const res = await this._readStorageFile(this._getStorage(packageJSONFileName));\n const data: any = JSON.parse(res.toString('utf8'));\n\n debug('read storage file %o has succeeded', name);\n return data;\n } catch (err: any) {\n if (err.code !== noSuchFile) {\n debug('parse error');\n this.logger.error({ err, name }, 'error on parse @{name}: @{err.message}');\n }\n throw err;\n }\n }\n\n public async hasTarball(fileName: string): Promise<boolean> {\n const pathName: string = this._getStorage(fileName);\n return new Promise((resolve) => {\n accessPromise(pathName)\n .then(() => {\n resolve(true);\n })\n .catch(() => resolve(false));\n });\n }\n\n // remove the temporary file\n private async removeTempFile(temporalName): Promise<void> {\n debug('remove temporal file %o', temporalName);\n await unlinkPromise(temporalName);\n debug('removed temporal file %o', temporalName);\n }\n\n /**\n * Write a tarball into the storage\n * @param fileName package name\n * @param param1\n * @returns\n */\n public async writeTarball(fileName: string, { signal }): Promise<Writable> {\n debug('write a tarball %o', fileName);\n const pathName: string = this._getStorage(fileName);\n await this.checkCreateFolder(pathName);\n // create a temporary file to avoid conflicts or prev corruption files\n const temporalName = path.join(\n this.path,\n sanitzers(`${fileName}.tmp-${String(Math.random()).replace(/^0\\./, '')}`)\n );\n\n debug('write a temporal name %o', temporalName);\n let opened = false;\n const writeStream = fs.createWriteStream(temporalName);\n\n writeStream.on('open', () => {\n opened = true;\n });\n\n writeStream.on('error', async (err) => {\n if (opened) {\n this.logger.error(\n { err, fileName },\n 'error on open write tarball for @{fileName}: @{err.message}'\n );\n // TODO: maybe add .once\n writeStream.on('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n this.logger.error(\n { err, fileName },\n 'error on writing tarball for @{fileName}: @{err.message}'\n );\n await this.removeTempFile(temporalName);\n }\n });\n\n // the 'close' event is emitted when the stream and any of its\n // underlying resources (a file descriptor, for example) have been closed.\n // TODO: maybe add .once\n writeStream.on('close', async () => {\n try {\n await renameTmp(temporalName, pathName);\n } catch (err) {\n this.logger.error(\n { err, temporalName, pathName },\n 'error on rename temporal file @{temporalName} to @{pathName}: @{err.message},' +\n 'please report this bug'\n );\n }\n });\n\n // if upload is aborted, we clean up the temporal file\n signal?.addEventListener(\n 'abort',\n async () => {\n if (opened) {\n // close always happens, even if error\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n await this.removeTempFile(temporalName);\n }\n },\n { once: true }\n );\n\n return writeStream;\n }\n\n /**\n * Read a tarball from the storage\n * @param tarballName tarball name eg: foo-1.0.0.tgz\n * @param options {signal} abort signal\n * @returns Readable stream\n */\n public async readTarball(tarballName: string, { signal }): Promise<Readable> {\n const pathName: string = this._getStorage(tarballName);\n debug('read a tarball %o', pathName);\n const readStream = addAbortSignal(signal, fs.createReadStream(pathName));\n readStream.on('open', async function (fileDescriptorId: number) {\n // if abort, the descriptor is null\n debug('file descriptor id %o', fileDescriptorId);\n if (fileDescriptorId) {\n const stats = await fstatPromise(fileDescriptorId);\n debug('file size %o', stats.size);\n readStream.emit('content-length', stats.size);\n }\n });\n readStream.on('error', (error) => {\n debug('no tarball found %o for %s message %s', pathName, tarballName, error.message);\n });\n return readStream;\n }\n\n private async _readStorageFile(name: string): Promise<any> {\n debug('reading the file: %o', name);\n try {\n return await readFilePromise(name);\n } catch (err: any) {\n debug('error reading the file: %o with error %o', name, err.message);\n throw err;\n }\n }\n\n private _convertToString(value: Manifest): string {\n return JSON.stringify(value);\n }\n\n public _getStorage(fileName = ''): string {\n const storagePath: string = path.join(this.path, sanitzers(fileName));\n debug('get storage %s', storagePath);\n return storagePath;\n }\n\n private async writeTempFileAndRename(dest: string, fileContent: string): Promise<any> {\n const tempFilePath = tempFile(dest);\n try {\n // write file on temp location\n await this.checkCreateFolder(tempFilePath);\n await writeFilePromise(tempFilePath, fileContent);\n debug('creating a new file:: %o', dest);\n // rename tmp file to original\n await renameTmp(tempFilePath, dest);\n } catch (err: any) {\n debug('error on write the file: %o', dest);\n throw err;\n }\n }\n\n private async writeFile(destiny: string, fileContent: string): Promise<void> {\n try {\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } catch (err: any) {\n if (err && err.code === noSuchFile) {\n await this.checkCreateFolder(destiny);\n // we try again create the temp file\n debug('writing a temp file %s', destiny);\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } else {\n this.logger.error({ err }, 'error on write file: @{err.message}');\n throw err;\n }\n }\n }\n\n private async checkCreateFolder(pathName: string): Promise<void> {\n // Check if folder exists and create it if not\n const dir = path.dirname(pathName);\n try {\n await accessPromise(dir);\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('folder does not exist %o', dir);\n await mkdirPromise(dir, { recursive: true });\n debug('folder %o created', dir);\n } else {\n debug('error on check folder %o', dir);\n throw err;\n }\n }\n }\n\n private async _lockAndReadJSON(name: string): Promise<Manifest> {\n const fileName: string = this._getStorage(name);\n debug('lock and read a file %o', fileName);\n try {\n const data = await readFileNext<Manifest>(fileName, {\n lock: true,\n parse: true,\n });\n return data;\n } catch (err) {\n this.logger.error({ err }, 'error on lock file: @{err.message}');\n debug('error on lock and read json for file: %o', name);\n\n throw err;\n }\n }\n\n private async _unlockJSON(name: string): Promise<void> {\n await unlockFileNext(this._getStorage(name));\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-fs.js","names":[],"sources":["../src/local-fs.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable, Writable } from 'node:stream';\nimport { addAbortSignal } from 'node:stream';\nimport sanitzers from 'sanitize-filename';\n\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport { errorUtils } from '@verdaccio/core';\nimport { readFileNext, unlockFileNext } from '@verdaccio/file-locking';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport {\n accessPromise,\n mkdirPromise,\n openPromise,\n readFilePromise,\n renamePromise,\n rmdirPromise,\n statPromise,\n unlinkPromise,\n writeFilePromise,\n} from './fs';\n\nexport const fileExist = 'EEXISTS';\nexport const noSuchFile = 'ENOENT';\nexport const resourceNotAvailable = 'EAGAIN';\nexport const packageJSONFileName = 'package.json';\n\nexport type ILocalFSPackageManager = pluginUtils.StorageHandler & { path: string };\n\nconst debug = buildDebug('verdaccio:plugin:local-storage:local-fs');\n\nexport const fSError = function (message: string, code = 409): VerdaccioError {\n const err: VerdaccioError = errorUtils.getCode(code, message);\n // FIXME: we should return http-status codes here instead, future improvement\n // @ts-ignore\n err.code = message;\n\n return err;\n};\n\nconst tempFile = function (str): string {\n return `${str}.tmp${String(Math.random()).slice(2)}`;\n};\n\nexport async function renameTmp(src: string, dst: string): Promise<void> {\n debug('rename %s to %s', src, dst);\n try {\n await renamePromise(src, dst);\n } catch (err: any) {\n debug('error rename %s error %s', src, err?.message);\n await unlinkPromise(src);\n }\n}\n\nexport default class LocalFS implements ILocalFSPackageManager {\n public path: string;\n public logger: Logger;\n\n public constructor(path: string, logger: Logger) {\n this.path = path;\n this.logger = logger;\n }\n\n /**\n * This function allows to update the package\n * This function handle the update package logic, for this plugin\n * we need to lock/unlock handlers for thread-safely and then apply\n * the `handleUpdate` and return the result.\n *\n * The lock could fail on several steps so we need to ensure the\n * file does not get locked if the whole process fails.\n *\n Algorithm:\n 1. lock package.json for writing\n 2. read package.json\n 3. apply external update package handler\n 4. return manifest (write is being hanlded into the core)\n 5. rename package.json.tmp package.json\n * @param {*} packageName\n * The update package handler could be different based\n * on the action and handled into the core.\n * @param {*} handleUpdate\n */\n public async updatePackage(\n packageName: string,\n handleUpdate: (manifest: Manifest) => Promise<Manifest>\n ): Promise<Manifest> {\n // this plugin lock files on write, we handle all possible scenarios\n let locked = false;\n let manifestUpdated: Manifest;\n try {\n const manifest = await this._lockAndReadJSON(packageJSONFileName);\n locked = true;\n manifestUpdated = await handleUpdate(manifest);\n if (locked) {\n debug('unlock %s', packageJSONFileName);\n await this._unlockJSON(packageJSONFileName);\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n } else {\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n }\n } catch (err: any) {\n // we ensure lock the file\n this.logger.error(\n { err, packageName },\n 'error on update package @{packageName}: @{err.message}'\n );\n if (locked) {\n try {\n await this._unlockJSON(packageJSONFileName);\n // after unlock bubble up error.\n throw err;\n } catch {\n // unlock could fail, we bubble up error\n throw errorUtils.getInternalError('resource temporarily unavailable');\n }\n } else {\n if (err.code === resourceNotAvailable) {\n throw errorUtils.getInternalError('resource temporarily unavailable');\n } else if (err.code === noSuchFile) {\n throw errorUtils.getNotFound();\n } else {\n throw err;\n }\n }\n }\n }\n\n public async deletePackage(packageName: string): Promise<void> {\n debug('delete a file/package %o', packageName);\n\n return await unlinkPromise(this._getStorage(packageName));\n }\n\n public async removePackage(): Promise<void> {\n debug('remove a package folder %o', this.path);\n\n await rmdirPromise(this._getStorage('.'));\n }\n\n /**\n * Verify if the package exists already.\n * @param name package name\n * @returns\n */\n public async hasPackage(): Promise<boolean> {\n const pathName: string = this._getStorage(packageJSONFileName);\n try {\n const stat = await statPromise(pathName);\n return stat.isFile();\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('dir: %o does not exist %s', pathName, err?.code);\n return false;\n } else {\n this.logger.error({ err }, 'error on verify a package exist: @{err.message}');\n throw errorUtils.getInternalError('error on verify a package exist');\n }\n }\n }\n\n /**\n * Create a package in the local storage, if package already exist fails.\n * @param name package name\n * @param manifest package manifest\n */\n public async createPackage(name: string, manifest: Manifest): Promise<void> {\n debug('create a new package %o', name);\n const pathPackage = this._getStorage(packageJSONFileName);\n try {\n // https://nodejs.org/dist/latest-v17.x/docs/api/fs.html#file-system-flags\n // 'wx': Like 'w' but fails if the path exists\n await openPromise(pathPackage, 'wx');\n } catch (err: any) {\n // cannot override a pacakge that already exist\n if (err.code === 'EEXIST') {\n debug('file %o cannot be created, it already exists: %o', name);\n throw fSError(fileExist);\n }\n }\n // Create a new file and it´s folder if does not exist previously\n await this.writeFile(pathPackage, this._convertToString(manifest));\n }\n\n public async savePackage(name: string, value: Manifest): Promise<void> {\n debug('save a package %o', name);\n\n await this.writeFile(this._getStorage(packageJSONFileName), this._convertToString(value));\n }\n\n public async readPackage(name: string): Promise<Manifest> {\n debug('read a package %o', name);\n try {\n const res = await this._readStorageFile(this._getStorage(packageJSONFileName));\n const data: any = JSON.parse(res.toString('utf8'));\n\n debug('read storage file %o has succeeded', name);\n return data;\n } catch (err: any) {\n if (err.code !== noSuchFile) {\n debug('parse error');\n this.logger.error({ err, name }, 'error on parse @{name}: @{err.message}');\n }\n throw err;\n }\n }\n\n public async hasTarball(fileName: string): Promise<boolean> {\n const pathName: string = this._getStorage(fileName);\n return new Promise((resolve) => {\n accessPromise(pathName)\n .then(() => {\n resolve(true);\n })\n .catch(() => resolve(false));\n });\n }\n\n /**\n * Best-effort removal of a temporary file. Error and abort can both try to\n * clean the same file from event listeners, so a missing file is fine and\n * nothing may throw — a rejection here becomes an uncaught exception.\n */\n private async removeTempFile(temporalName): Promise<void> {\n debug('remove temporal file %o', temporalName);\n try {\n await unlinkPromise(temporalName);\n debug('removed temporal file %o', temporalName);\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n this.logger.warn(\n { temporalName, err: err.message },\n 'unable to remove temporal file @{temporalName}: @{err}'\n );\n }\n }\n }\n\n /**\n * Write a tarball into the storage\n * @param fileName package name\n * @param param1\n * @returns\n */\n public async writeTarball(fileName: string, { signal }): Promise<Writable> {\n debug('write a tarball %o', fileName);\n const pathName: string = this._getStorage(fileName);\n await this.checkCreateFolder(pathName);\n // create a temporary file to avoid conflicts or prev corruption files\n const temporalName = path.join(\n this.path,\n sanitzers(`${fileName}.tmp-${String(Math.random()).replace(/^0\\./, '')}`)\n );\n\n debug('write a temporal name %o', temporalName);\n let opened = false;\n // an errored or aborted download must never be renamed into place: that\n // would publish a truncated tarball under the final name\n let failed = false;\n const writeStream = fs.createWriteStream(temporalName);\n\n writeStream.on('open', () => {\n opened = true;\n });\n\n writeStream.on('error', async (err) => {\n failed = true;\n if (opened) {\n this.logger.error(\n { err, fileName },\n 'error on open write tarball for @{fileName}: @{err.message}'\n );\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n this.logger.error(\n { err, fileName },\n 'error on writing tarball for @{fileName}: @{err.message}'\n );\n await this.removeTempFile(temporalName);\n }\n });\n\n // the 'close' event is emitted when the stream and any of its\n // underlying resources (a file descriptor, for example) have been closed.\n writeStream.once('close', async () => {\n if (failed) {\n // the error/abort listeners own the cleanup of the temporal file\n return;\n }\n try {\n await renameTmp(temporalName, pathName);\n } catch (err) {\n this.logger.error(\n { err, temporalName, pathName },\n 'error on rename temporal file @{temporalName} to @{pathName}: @{err.message},' +\n 'please report this bug'\n );\n }\n });\n\n // if upload is aborted, we clean up the temporal file\n signal?.addEventListener(\n 'abort',\n async () => {\n failed = true;\n if (opened) {\n // close always happens, even if error\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n await this.removeTempFile(temporalName);\n }\n },\n { once: true }\n );\n\n return writeStream;\n }\n\n /**\n * Read a tarball from the storage\n * @param tarballName tarball name eg: foo-1.0.0.tgz\n * @param options {signal} abort signal\n * @returns Readable stream\n */\n public async readTarball(tarballName: string, { signal }): Promise<Readable> {\n const pathName: string = this._getStorage(tarballName);\n debug('read a tarball %o', pathName);\n const readStream = addAbortSignal(signal, fs.createReadStream(pathName));\n readStream.on('open', function (fileDescriptorId: number) {\n // if abort, the descriptor is null\n debug('file descriptor id %o', fileDescriptorId);\n if (fileDescriptorId) {\n // sync on purpose: the size must be emitted before the first data\n // chunk flushes the response headers (an async fstat races it)\n let size: number | undefined;\n try {\n size = fs.fstatSync(fileDescriptorId).size;\n } catch {\n // the stream 'error' event surfaces any real problem\n }\n if (size !== undefined) {\n debug('file size %o', size);\n readStream.emit('content-length', size);\n }\n }\n });\n readStream.on('error', (error) => {\n debug('no tarball found %o for %s message %s', pathName, tarballName, error.message);\n });\n return readStream;\n }\n\n private async _readStorageFile(name: string): Promise<any> {\n debug('reading the file: %o', name);\n try {\n return await readFilePromise(name);\n } catch (err: any) {\n debug('error reading the file: %o with error %o', name, err.message);\n throw err;\n }\n }\n\n private _convertToString(value: Manifest): string {\n return JSON.stringify(value);\n }\n\n public _getStorage(fileName = ''): string {\n const storagePath: string = path.join(this.path, sanitzers(fileName));\n debug('get storage %s', storagePath);\n return storagePath;\n }\n\n private async writeTempFileAndRename(dest: string, fileContent: string): Promise<any> {\n const tempFilePath = tempFile(dest);\n try {\n // write file on temp location\n await this.checkCreateFolder(tempFilePath);\n await writeFilePromise(tempFilePath, fileContent);\n debug('creating a new file:: %o', dest);\n // rename tmp file to original\n await renameTmp(tempFilePath, dest);\n } catch (err: any) {\n debug('error on write the file: %o', dest);\n throw err;\n }\n }\n\n private async writeFile(destiny: string, fileContent: string): Promise<void> {\n try {\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } catch (err: any) {\n if (err && err.code === noSuchFile) {\n await this.checkCreateFolder(destiny);\n // we try again create the temp file\n debug('writing a temp file %s', destiny);\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } else {\n this.logger.error({ err }, 'error on write file: @{err.message}');\n throw err;\n }\n }\n }\n\n private async checkCreateFolder(pathName: string): Promise<void> {\n // Check if folder exists and create it if not\n const dir = path.dirname(pathName);\n try {\n await accessPromise(dir);\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('folder does not exist %o', dir);\n await mkdirPromise(dir, { recursive: true });\n debug('folder %o created', dir);\n } else {\n debug('error on check folder %o', dir);\n throw err;\n }\n }\n }\n\n private async _lockAndReadJSON(name: string): Promise<Manifest> {\n const fileName: string = this._getStorage(name);\n debug('lock and read a file %o', fileName);\n try {\n const data = await readFileNext<Manifest>(fileName, {\n lock: true,\n parse: true,\n });\n return data;\n } catch (err) {\n this.logger.error({ err }, 'error on lock file: @{err.message}');\n debug('error on lock and read json for file: %o', name);\n\n throw err;\n }\n }\n\n private async _unlockJSON(name: string): Promise<void> {\n await unlockFileNext(this._getStorage(name));\n }\n}\n"],"mappings":"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"}
|
package/build/local-fs.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { accessPromise,
|
|
1
|
+
import { accessPromise, mkdirPromise, openPromise, readFilePromise, renamePromise, rmdirPromise, statPromise, unlinkPromise, writeFilePromise } from "./fs.mjs";
|
|
2
2
|
import buildDebug from "debug";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { errorUtils } from "@verdaccio/core";
|
|
@@ -163,10 +163,22 @@ var LocalFS = class {
|
|
|
163
163
|
}).catch(() => resolve(false));
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Best-effort removal of a temporary file. Error and abort can both try to
|
|
168
|
+
* clean the same file from event listeners, so a missing file is fine and
|
|
169
|
+
* nothing may throw — a rejection here becomes an uncaught exception.
|
|
170
|
+
*/
|
|
166
171
|
async removeTempFile(temporalName) {
|
|
167
172
|
debug("remove temporal file %o", temporalName);
|
|
168
|
-
|
|
169
|
-
|
|
173
|
+
try {
|
|
174
|
+
await unlinkPromise(temporalName);
|
|
175
|
+
debug("removed temporal file %o", temporalName);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
if (err?.code !== "ENOENT") this.logger.warn({
|
|
178
|
+
temporalName,
|
|
179
|
+
err: err.message
|
|
180
|
+
}, "unable to remove temporal file @{temporalName}: @{err}");
|
|
181
|
+
}
|
|
170
182
|
}
|
|
171
183
|
/**
|
|
172
184
|
* Write a tarball into the storage
|
|
@@ -181,17 +193,19 @@ var LocalFS = class {
|
|
|
181
193
|
const temporalName = path.join(this.path, sanitzers(`${fileName}.tmp-${String(Math.random()).replace(/^0\./, "")}`));
|
|
182
194
|
debug("write a temporal name %o", temporalName);
|
|
183
195
|
let opened = false;
|
|
196
|
+
let failed = false;
|
|
184
197
|
const writeStream = fsCallback.createWriteStream(temporalName);
|
|
185
198
|
writeStream.on("open", () => {
|
|
186
199
|
opened = true;
|
|
187
200
|
});
|
|
188
201
|
writeStream.on("error", async (err) => {
|
|
202
|
+
failed = true;
|
|
189
203
|
if (opened) {
|
|
190
204
|
this.logger.error({
|
|
191
205
|
err,
|
|
192
206
|
fileName
|
|
193
207
|
}, "error on open write tarball for @{fileName}: @{err.message}");
|
|
194
|
-
writeStream.
|
|
208
|
+
writeStream.once("close", async () => {
|
|
195
209
|
await this.removeTempFile(temporalName);
|
|
196
210
|
});
|
|
197
211
|
} else {
|
|
@@ -202,7 +216,8 @@ var LocalFS = class {
|
|
|
202
216
|
await this.removeTempFile(temporalName);
|
|
203
217
|
}
|
|
204
218
|
});
|
|
205
|
-
writeStream.
|
|
219
|
+
writeStream.once("close", async () => {
|
|
220
|
+
if (failed) return;
|
|
206
221
|
try {
|
|
207
222
|
await renameTmp(temporalName, pathName);
|
|
208
223
|
} catch (err) {
|
|
@@ -214,6 +229,7 @@ var LocalFS = class {
|
|
|
214
229
|
}
|
|
215
230
|
});
|
|
216
231
|
signal?.addEventListener("abort", async () => {
|
|
232
|
+
failed = true;
|
|
217
233
|
if (opened) writeStream.once("close", async () => {
|
|
218
234
|
await this.removeTempFile(temporalName);
|
|
219
235
|
});
|
|
@@ -231,12 +247,17 @@ var LocalFS = class {
|
|
|
231
247
|
const pathName = this._getStorage(tarballName);
|
|
232
248
|
debug("read a tarball %o", pathName);
|
|
233
249
|
const readStream = addAbortSignal(signal, fsCallback.createReadStream(pathName));
|
|
234
|
-
readStream.on("open",
|
|
250
|
+
readStream.on("open", function(fileDescriptorId) {
|
|
235
251
|
debug("file descriptor id %o", fileDescriptorId);
|
|
236
252
|
if (fileDescriptorId) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
let size;
|
|
254
|
+
try {
|
|
255
|
+
size = fsCallback.fstatSync(fileDescriptorId).size;
|
|
256
|
+
} catch {}
|
|
257
|
+
if (size !== void 0) {
|
|
258
|
+
debug("file size %o", size);
|
|
259
|
+
readStream.emit("content-length", size);
|
|
260
|
+
}
|
|
240
261
|
}
|
|
241
262
|
});
|
|
242
263
|
readStream.on("error", (error) => {
|
package/build/local-fs.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-fs.mjs","names":[],"sources":["../src/local-fs.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable, Writable } from 'node:stream';\nimport { addAbortSignal } from 'node:stream';\nimport sanitzers from 'sanitize-filename';\n\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport { errorUtils } from '@verdaccio/core';\nimport { readFileNext, unlockFileNext } from '@verdaccio/file-locking';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport {\n accessPromise,\n fstatPromise,\n mkdirPromise,\n openPromise,\n readFilePromise,\n renamePromise,\n rmdirPromise,\n statPromise,\n unlinkPromise,\n writeFilePromise,\n} from './fs';\n\nexport const fileExist = 'EEXISTS';\nexport const noSuchFile = 'ENOENT';\nexport const resourceNotAvailable = 'EAGAIN';\nexport const packageJSONFileName = 'package.json';\n\nexport type ILocalFSPackageManager = pluginUtils.StorageHandler & { path: string };\n\nconst debug = buildDebug('verdaccio:plugin:local-storage:local-fs');\n\nexport const fSError = function (message: string, code = 409): VerdaccioError {\n const err: VerdaccioError = errorUtils.getCode(code, message);\n // FIXME: we should return http-status codes here instead, future improvement\n // @ts-ignore\n err.code = message;\n\n return err;\n};\n\nconst tempFile = function (str): string {\n return `${str}.tmp${String(Math.random()).slice(2)}`;\n};\n\nexport async function renameTmp(src: string, dst: string): Promise<void> {\n debug('rename %s to %s', src, dst);\n try {\n await renamePromise(src, dst);\n } catch (err: any) {\n debug('error rename %s error %s', src, err?.message);\n await unlinkPromise(src);\n }\n}\n\nexport default class LocalFS implements ILocalFSPackageManager {\n public path: string;\n public logger: Logger;\n\n public constructor(path: string, logger: Logger) {\n this.path = path;\n this.logger = logger;\n }\n\n /**\n * This function allows to update the package\n * This function handle the update package logic, for this plugin\n * we need to lock/unlock handlers for thread-safely and then apply\n * the `handleUpdate` and return the result.\n *\n * The lock could fail on several steps so we need to ensure the\n * file does not get locked if the whole process fails.\n *\n Algorithm:\n 1. lock package.json for writing\n 2. read package.json\n 3. apply external update package handler\n 4. return manifest (write is being hanlded into the core)\n 5. rename package.json.tmp package.json\n * @param {*} packageName\n * The update package handler could be different based\n * on the action and handled into the core.\n * @param {*} handleUpdate\n */\n public async updatePackage(\n packageName: string,\n handleUpdate: (manifest: Manifest) => Promise<Manifest>\n ): Promise<Manifest> {\n // this plugin lock files on write, we handle all possible scenarios\n let locked = false;\n let manifestUpdated: Manifest;\n try {\n const manifest = await this._lockAndReadJSON(packageJSONFileName);\n locked = true;\n manifestUpdated = await handleUpdate(manifest);\n if (locked) {\n debug('unlock %s', packageJSONFileName);\n await this._unlockJSON(packageJSONFileName);\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n } else {\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n }\n } catch (err: any) {\n // we ensure lock the file\n this.logger.error(\n { err, packageName },\n 'error on update package @{packageName}: @{err.message}'\n );\n if (locked) {\n try {\n await this._unlockJSON(packageJSONFileName);\n // after unlock bubble up error.\n throw err;\n } catch {\n // unlock could fail, we bubble up error\n throw errorUtils.getInternalError('resource temporarily unavailable');\n }\n } else {\n if (err.code === resourceNotAvailable) {\n throw errorUtils.getInternalError('resource temporarily unavailable');\n } else if (err.code === noSuchFile) {\n throw errorUtils.getNotFound();\n } else {\n throw err;\n }\n }\n }\n }\n\n public async deletePackage(packageName: string): Promise<void> {\n debug('delete a file/package %o', packageName);\n\n return await unlinkPromise(this._getStorage(packageName));\n }\n\n public async removePackage(): Promise<void> {\n debug('remove a package folder %o', this.path);\n\n await rmdirPromise(this._getStorage('.'));\n }\n\n /**\n * Verify if the package exists already.\n * @param name package name\n * @returns\n */\n public async hasPackage(): Promise<boolean> {\n const pathName: string = this._getStorage(packageJSONFileName);\n try {\n const stat = await statPromise(pathName);\n return stat.isFile();\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('dir: %o does not exist %s', pathName, err?.code);\n return false;\n } else {\n this.logger.error({ err }, 'error on verify a package exist: @{err.message}');\n throw errorUtils.getInternalError('error on verify a package exist');\n }\n }\n }\n\n /**\n * Create a package in the local storage, if package already exist fails.\n * @param name package name\n * @param manifest package manifest\n */\n public async createPackage(name: string, manifest: Manifest): Promise<void> {\n debug('create a new package %o', name);\n const pathPackage = this._getStorage(packageJSONFileName);\n try {\n // https://nodejs.org/dist/latest-v17.x/docs/api/fs.html#file-system-flags\n // 'wx': Like 'w' but fails if the path exists\n await openPromise(pathPackage, 'wx');\n } catch (err: any) {\n // cannot override a pacakge that already exist\n if (err.code === 'EEXIST') {\n debug('file %o cannot be created, it already exists: %o', name);\n throw fSError(fileExist);\n }\n }\n // Create a new file and it´s folder if does not exist previously\n await this.writeFile(pathPackage, this._convertToString(manifest));\n }\n\n public async savePackage(name: string, value: Manifest): Promise<void> {\n debug('save a package %o', name);\n\n await this.writeFile(this._getStorage(packageJSONFileName), this._convertToString(value));\n }\n\n public async readPackage(name: string): Promise<Manifest> {\n debug('read a package %o', name);\n try {\n const res = await this._readStorageFile(this._getStorage(packageJSONFileName));\n const data: any = JSON.parse(res.toString('utf8'));\n\n debug('read storage file %o has succeeded', name);\n return data;\n } catch (err: any) {\n if (err.code !== noSuchFile) {\n debug('parse error');\n this.logger.error({ err, name }, 'error on parse @{name}: @{err.message}');\n }\n throw err;\n }\n }\n\n public async hasTarball(fileName: string): Promise<boolean> {\n const pathName: string = this._getStorage(fileName);\n return new Promise((resolve) => {\n accessPromise(pathName)\n .then(() => {\n resolve(true);\n })\n .catch(() => resolve(false));\n });\n }\n\n // remove the temporary file\n private async removeTempFile(temporalName): Promise<void> {\n debug('remove temporal file %o', temporalName);\n await unlinkPromise(temporalName);\n debug('removed temporal file %o', temporalName);\n }\n\n /**\n * Write a tarball into the storage\n * @param fileName package name\n * @param param1\n * @returns\n */\n public async writeTarball(fileName: string, { signal }): Promise<Writable> {\n debug('write a tarball %o', fileName);\n const pathName: string = this._getStorage(fileName);\n await this.checkCreateFolder(pathName);\n // create a temporary file to avoid conflicts or prev corruption files\n const temporalName = path.join(\n this.path,\n sanitzers(`${fileName}.tmp-${String(Math.random()).replace(/^0\\./, '')}`)\n );\n\n debug('write a temporal name %o', temporalName);\n let opened = false;\n const writeStream = fs.createWriteStream(temporalName);\n\n writeStream.on('open', () => {\n opened = true;\n });\n\n writeStream.on('error', async (err) => {\n if (opened) {\n this.logger.error(\n { err, fileName },\n 'error on open write tarball for @{fileName}: @{err.message}'\n );\n // TODO: maybe add .once\n writeStream.on('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n this.logger.error(\n { err, fileName },\n 'error on writing tarball for @{fileName}: @{err.message}'\n );\n await this.removeTempFile(temporalName);\n }\n });\n\n // the 'close' event is emitted when the stream and any of its\n // underlying resources (a file descriptor, for example) have been closed.\n // TODO: maybe add .once\n writeStream.on('close', async () => {\n try {\n await renameTmp(temporalName, pathName);\n } catch (err) {\n this.logger.error(\n { err, temporalName, pathName },\n 'error on rename temporal file @{temporalName} to @{pathName}: @{err.message},' +\n 'please report this bug'\n );\n }\n });\n\n // if upload is aborted, we clean up the temporal file\n signal?.addEventListener(\n 'abort',\n async () => {\n if (opened) {\n // close always happens, even if error\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n await this.removeTempFile(temporalName);\n }\n },\n { once: true }\n );\n\n return writeStream;\n }\n\n /**\n * Read a tarball from the storage\n * @param tarballName tarball name eg: foo-1.0.0.tgz\n * @param options {signal} abort signal\n * @returns Readable stream\n */\n public async readTarball(tarballName: string, { signal }): Promise<Readable> {\n const pathName: string = this._getStorage(tarballName);\n debug('read a tarball %o', pathName);\n const readStream = addAbortSignal(signal, fs.createReadStream(pathName));\n readStream.on('open', async function (fileDescriptorId: number) {\n // if abort, the descriptor is null\n debug('file descriptor id %o', fileDescriptorId);\n if (fileDescriptorId) {\n const stats = await fstatPromise(fileDescriptorId);\n debug('file size %o', stats.size);\n readStream.emit('content-length', stats.size);\n }\n });\n readStream.on('error', (error) => {\n debug('no tarball found %o for %s message %s', pathName, tarballName, error.message);\n });\n return readStream;\n }\n\n private async _readStorageFile(name: string): Promise<any> {\n debug('reading the file: %o', name);\n try {\n return await readFilePromise(name);\n } catch (err: any) {\n debug('error reading the file: %o with error %o', name, err.message);\n throw err;\n }\n }\n\n private _convertToString(value: Manifest): string {\n return JSON.stringify(value);\n }\n\n public _getStorage(fileName = ''): string {\n const storagePath: string = path.join(this.path, sanitzers(fileName));\n debug('get storage %s', storagePath);\n return storagePath;\n }\n\n private async writeTempFileAndRename(dest: string, fileContent: string): Promise<any> {\n const tempFilePath = tempFile(dest);\n try {\n // write file on temp location\n await this.checkCreateFolder(tempFilePath);\n await writeFilePromise(tempFilePath, fileContent);\n debug('creating a new file:: %o', dest);\n // rename tmp file to original\n await renameTmp(tempFilePath, dest);\n } catch (err: any) {\n debug('error on write the file: %o', dest);\n throw err;\n }\n }\n\n private async writeFile(destiny: string, fileContent: string): Promise<void> {\n try {\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } catch (err: any) {\n if (err && err.code === noSuchFile) {\n await this.checkCreateFolder(destiny);\n // we try again create the temp file\n debug('writing a temp file %s', destiny);\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } else {\n this.logger.error({ err }, 'error on write file: @{err.message}');\n throw err;\n }\n }\n }\n\n private async checkCreateFolder(pathName: string): Promise<void> {\n // Check if folder exists and create it if not\n const dir = path.dirname(pathName);\n try {\n await accessPromise(dir);\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('folder does not exist %o', dir);\n await mkdirPromise(dir, { recursive: true });\n debug('folder %o created', dir);\n } else {\n debug('error on check folder %o', dir);\n throw err;\n }\n }\n }\n\n private async _lockAndReadJSON(name: string): Promise<Manifest> {\n const fileName: string = this._getStorage(name);\n debug('lock and read a file %o', fileName);\n try {\n const data = await readFileNext<Manifest>(fileName, {\n lock: true,\n parse: true,\n });\n return data;\n } catch (err) {\n this.logger.error({ err }, 'error on lock file: @{err.message}');\n debug('error on lock and read json for file: %o', name);\n\n throw err;\n }\n }\n\n private async _unlockJSON(name: string): Promise<void> {\n await unlockFileNext(this._getStorage(name));\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-fs.mjs","names":[],"sources":["../src/local-fs.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable, Writable } from 'node:stream';\nimport { addAbortSignal } from 'node:stream';\nimport sanitzers from 'sanitize-filename';\n\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport { errorUtils } from '@verdaccio/core';\nimport { readFileNext, unlockFileNext } from '@verdaccio/file-locking';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport {\n accessPromise,\n mkdirPromise,\n openPromise,\n readFilePromise,\n renamePromise,\n rmdirPromise,\n statPromise,\n unlinkPromise,\n writeFilePromise,\n} from './fs';\n\nexport const fileExist = 'EEXISTS';\nexport const noSuchFile = 'ENOENT';\nexport const resourceNotAvailable = 'EAGAIN';\nexport const packageJSONFileName = 'package.json';\n\nexport type ILocalFSPackageManager = pluginUtils.StorageHandler & { path: string };\n\nconst debug = buildDebug('verdaccio:plugin:local-storage:local-fs');\n\nexport const fSError = function (message: string, code = 409): VerdaccioError {\n const err: VerdaccioError = errorUtils.getCode(code, message);\n // FIXME: we should return http-status codes here instead, future improvement\n // @ts-ignore\n err.code = message;\n\n return err;\n};\n\nconst tempFile = function (str): string {\n return `${str}.tmp${String(Math.random()).slice(2)}`;\n};\n\nexport async function renameTmp(src: string, dst: string): Promise<void> {\n debug('rename %s to %s', src, dst);\n try {\n await renamePromise(src, dst);\n } catch (err: any) {\n debug('error rename %s error %s', src, err?.message);\n await unlinkPromise(src);\n }\n}\n\nexport default class LocalFS implements ILocalFSPackageManager {\n public path: string;\n public logger: Logger;\n\n public constructor(path: string, logger: Logger) {\n this.path = path;\n this.logger = logger;\n }\n\n /**\n * This function allows to update the package\n * This function handle the update package logic, for this plugin\n * we need to lock/unlock handlers for thread-safely and then apply\n * the `handleUpdate` and return the result.\n *\n * The lock could fail on several steps so we need to ensure the\n * file does not get locked if the whole process fails.\n *\n Algorithm:\n 1. lock package.json for writing\n 2. read package.json\n 3. apply external update package handler\n 4. return manifest (write is being hanlded into the core)\n 5. rename package.json.tmp package.json\n * @param {*} packageName\n * The update package handler could be different based\n * on the action and handled into the core.\n * @param {*} handleUpdate\n */\n public async updatePackage(\n packageName: string,\n handleUpdate: (manifest: Manifest) => Promise<Manifest>\n ): Promise<Manifest> {\n // this plugin lock files on write, we handle all possible scenarios\n let locked = false;\n let manifestUpdated: Manifest;\n try {\n const manifest = await this._lockAndReadJSON(packageJSONFileName);\n locked = true;\n manifestUpdated = await handleUpdate(manifest);\n if (locked) {\n debug('unlock %s', packageJSONFileName);\n await this._unlockJSON(packageJSONFileName);\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n } else {\n this.logger.debug({ packageName }, 'the package @{packageName} has been updated');\n return manifestUpdated;\n }\n } catch (err: any) {\n // we ensure lock the file\n this.logger.error(\n { err, packageName },\n 'error on update package @{packageName}: @{err.message}'\n );\n if (locked) {\n try {\n await this._unlockJSON(packageJSONFileName);\n // after unlock bubble up error.\n throw err;\n } catch {\n // unlock could fail, we bubble up error\n throw errorUtils.getInternalError('resource temporarily unavailable');\n }\n } else {\n if (err.code === resourceNotAvailable) {\n throw errorUtils.getInternalError('resource temporarily unavailable');\n } else if (err.code === noSuchFile) {\n throw errorUtils.getNotFound();\n } else {\n throw err;\n }\n }\n }\n }\n\n public async deletePackage(packageName: string): Promise<void> {\n debug('delete a file/package %o', packageName);\n\n return await unlinkPromise(this._getStorage(packageName));\n }\n\n public async removePackage(): Promise<void> {\n debug('remove a package folder %o', this.path);\n\n await rmdirPromise(this._getStorage('.'));\n }\n\n /**\n * Verify if the package exists already.\n * @param name package name\n * @returns\n */\n public async hasPackage(): Promise<boolean> {\n const pathName: string = this._getStorage(packageJSONFileName);\n try {\n const stat = await statPromise(pathName);\n return stat.isFile();\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('dir: %o does not exist %s', pathName, err?.code);\n return false;\n } else {\n this.logger.error({ err }, 'error on verify a package exist: @{err.message}');\n throw errorUtils.getInternalError('error on verify a package exist');\n }\n }\n }\n\n /**\n * Create a package in the local storage, if package already exist fails.\n * @param name package name\n * @param manifest package manifest\n */\n public async createPackage(name: string, manifest: Manifest): Promise<void> {\n debug('create a new package %o', name);\n const pathPackage = this._getStorage(packageJSONFileName);\n try {\n // https://nodejs.org/dist/latest-v17.x/docs/api/fs.html#file-system-flags\n // 'wx': Like 'w' but fails if the path exists\n await openPromise(pathPackage, 'wx');\n } catch (err: any) {\n // cannot override a pacakge that already exist\n if (err.code === 'EEXIST') {\n debug('file %o cannot be created, it already exists: %o', name);\n throw fSError(fileExist);\n }\n }\n // Create a new file and it´s folder if does not exist previously\n await this.writeFile(pathPackage, this._convertToString(manifest));\n }\n\n public async savePackage(name: string, value: Manifest): Promise<void> {\n debug('save a package %o', name);\n\n await this.writeFile(this._getStorage(packageJSONFileName), this._convertToString(value));\n }\n\n public async readPackage(name: string): Promise<Manifest> {\n debug('read a package %o', name);\n try {\n const res = await this._readStorageFile(this._getStorage(packageJSONFileName));\n const data: any = JSON.parse(res.toString('utf8'));\n\n debug('read storage file %o has succeeded', name);\n return data;\n } catch (err: any) {\n if (err.code !== noSuchFile) {\n debug('parse error');\n this.logger.error({ err, name }, 'error on parse @{name}: @{err.message}');\n }\n throw err;\n }\n }\n\n public async hasTarball(fileName: string): Promise<boolean> {\n const pathName: string = this._getStorage(fileName);\n return new Promise((resolve) => {\n accessPromise(pathName)\n .then(() => {\n resolve(true);\n })\n .catch(() => resolve(false));\n });\n }\n\n /**\n * Best-effort removal of a temporary file. Error and abort can both try to\n * clean the same file from event listeners, so a missing file is fine and\n * nothing may throw — a rejection here becomes an uncaught exception.\n */\n private async removeTempFile(temporalName): Promise<void> {\n debug('remove temporal file %o', temporalName);\n try {\n await unlinkPromise(temporalName);\n debug('removed temporal file %o', temporalName);\n } catch (err: any) {\n if (err?.code !== 'ENOENT') {\n this.logger.warn(\n { temporalName, err: err.message },\n 'unable to remove temporal file @{temporalName}: @{err}'\n );\n }\n }\n }\n\n /**\n * Write a tarball into the storage\n * @param fileName package name\n * @param param1\n * @returns\n */\n public async writeTarball(fileName: string, { signal }): Promise<Writable> {\n debug('write a tarball %o', fileName);\n const pathName: string = this._getStorage(fileName);\n await this.checkCreateFolder(pathName);\n // create a temporary file to avoid conflicts or prev corruption files\n const temporalName = path.join(\n this.path,\n sanitzers(`${fileName}.tmp-${String(Math.random()).replace(/^0\\./, '')}`)\n );\n\n debug('write a temporal name %o', temporalName);\n let opened = false;\n // an errored or aborted download must never be renamed into place: that\n // would publish a truncated tarball under the final name\n let failed = false;\n const writeStream = fs.createWriteStream(temporalName);\n\n writeStream.on('open', () => {\n opened = true;\n });\n\n writeStream.on('error', async (err) => {\n failed = true;\n if (opened) {\n this.logger.error(\n { err, fileName },\n 'error on open write tarball for @{fileName}: @{err.message}'\n );\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n this.logger.error(\n { err, fileName },\n 'error on writing tarball for @{fileName}: @{err.message}'\n );\n await this.removeTempFile(temporalName);\n }\n });\n\n // the 'close' event is emitted when the stream and any of its\n // underlying resources (a file descriptor, for example) have been closed.\n writeStream.once('close', async () => {\n if (failed) {\n // the error/abort listeners own the cleanup of the temporal file\n return;\n }\n try {\n await renameTmp(temporalName, pathName);\n } catch (err) {\n this.logger.error(\n { err, temporalName, pathName },\n 'error on rename temporal file @{temporalName} to @{pathName}: @{err.message},' +\n 'please report this bug'\n );\n }\n });\n\n // if upload is aborted, we clean up the temporal file\n signal?.addEventListener(\n 'abort',\n async () => {\n failed = true;\n if (opened) {\n // close always happens, even if error\n writeStream.once('close', async () => {\n await this.removeTempFile(temporalName);\n });\n } else {\n await this.removeTempFile(temporalName);\n }\n },\n { once: true }\n );\n\n return writeStream;\n }\n\n /**\n * Read a tarball from the storage\n * @param tarballName tarball name eg: foo-1.0.0.tgz\n * @param options {signal} abort signal\n * @returns Readable stream\n */\n public async readTarball(tarballName: string, { signal }): Promise<Readable> {\n const pathName: string = this._getStorage(tarballName);\n debug('read a tarball %o', pathName);\n const readStream = addAbortSignal(signal, fs.createReadStream(pathName));\n readStream.on('open', function (fileDescriptorId: number) {\n // if abort, the descriptor is null\n debug('file descriptor id %o', fileDescriptorId);\n if (fileDescriptorId) {\n // sync on purpose: the size must be emitted before the first data\n // chunk flushes the response headers (an async fstat races it)\n let size: number | undefined;\n try {\n size = fs.fstatSync(fileDescriptorId).size;\n } catch {\n // the stream 'error' event surfaces any real problem\n }\n if (size !== undefined) {\n debug('file size %o', size);\n readStream.emit('content-length', size);\n }\n }\n });\n readStream.on('error', (error) => {\n debug('no tarball found %o for %s message %s', pathName, tarballName, error.message);\n });\n return readStream;\n }\n\n private async _readStorageFile(name: string): Promise<any> {\n debug('reading the file: %o', name);\n try {\n return await readFilePromise(name);\n } catch (err: any) {\n debug('error reading the file: %o with error %o', name, err.message);\n throw err;\n }\n }\n\n private _convertToString(value: Manifest): string {\n return JSON.stringify(value);\n }\n\n public _getStorage(fileName = ''): string {\n const storagePath: string = path.join(this.path, sanitzers(fileName));\n debug('get storage %s', storagePath);\n return storagePath;\n }\n\n private async writeTempFileAndRename(dest: string, fileContent: string): Promise<any> {\n const tempFilePath = tempFile(dest);\n try {\n // write file on temp location\n await this.checkCreateFolder(tempFilePath);\n await writeFilePromise(tempFilePath, fileContent);\n debug('creating a new file:: %o', dest);\n // rename tmp file to original\n await renameTmp(tempFilePath, dest);\n } catch (err: any) {\n debug('error on write the file: %o', dest);\n throw err;\n }\n }\n\n private async writeFile(destiny: string, fileContent: string): Promise<void> {\n try {\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } catch (err: any) {\n if (err && err.code === noSuchFile) {\n await this.checkCreateFolder(destiny);\n // we try again create the temp file\n debug('writing a temp file %s', destiny);\n await this.writeTempFileAndRename(destiny, fileContent);\n debug('write file success %s', destiny);\n } else {\n this.logger.error({ err }, 'error on write file: @{err.message}');\n throw err;\n }\n }\n }\n\n private async checkCreateFolder(pathName: string): Promise<void> {\n // Check if folder exists and create it if not\n const dir = path.dirname(pathName);\n try {\n await accessPromise(dir);\n } catch (err: any) {\n if (err.code === noSuchFile) {\n debug('folder does not exist %o', dir);\n await mkdirPromise(dir, { recursive: true });\n debug('folder %o created', dir);\n } else {\n debug('error on check folder %o', dir);\n throw err;\n }\n }\n }\n\n private async _lockAndReadJSON(name: string): Promise<Manifest> {\n const fileName: string = this._getStorage(name);\n debug('lock and read a file %o', fileName);\n try {\n const data = await readFileNext<Manifest>(fileName, {\n lock: true,\n parse: true,\n });\n return data;\n } catch (err) {\n this.logger.error({ err }, 'error on lock file: @{err.message}');\n debug('error on lock and read json for file: %o', name);\n\n throw err;\n }\n }\n\n private async _unlockJSON(name: string): Promise<void> {\n await unlockFileNext(this._getStorage(name));\n }\n}\n"],"mappings":";;;;;;;;;AAwBA,IAAa,YAAY;AACzB,IAAa,aAAa;AAC1B,IAAa,uBAAuB;AACpC,IAAa,sBAAsB;AAInC,IAAM,QAAQ,WAAW,yCAAyC;AAElE,IAAa,UAAU,SAAU,SAAiB,OAAO,KAAqB;CAC5E,MAAM,MAAsB,WAAW,QAAQ,MAAM,OAAO;CAG5D,IAAI,OAAO;CAEX,OAAO;AACT;AAEA,IAAM,WAAW,SAAU,KAAa;CACtC,OAAO,GAAG,IAAI,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACnD;AAEA,eAAsB,UAAU,KAAa,KAA4B;CACvE,MAAM,mBAAmB,KAAK,GAAG;CACjC,IAAI;EACF,MAAM,cAAc,KAAK,GAAG;CAC9B,SAAS,KAAU;EACjB,MAAM,4BAA4B,KAAK,KAAK,OAAO;EACnD,MAAM,cAAc,GAAG;CACzB;AACF;AAEA,IAAqB,UAArB,MAA+D;CAC7D;CACA;CAEA,YAAmB,MAAc,QAAgB;EAC/C,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAa,cACX,aACA,cACmB;EAEnB,IAAI,SAAS;EACb,IAAI;EACJ,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,iBAAiB,mBAAmB;GAChE,SAAS;GACT,kBAAkB,MAAM,aAAa,QAAQ;GAC7C,IAAI,QAAQ;IACV,MAAM,aAAa,mBAAmB;IACtC,MAAM,KAAK,YAAY,mBAAmB;IAC1C,KAAK,OAAO,MAAM,EAAE,YAAY,GAAG,6CAA6C;IAChF,OAAO;GACT,OAAO;IACL,KAAK,OAAO,MAAM,EAAE,YAAY,GAAG,6CAA6C;IAChF,OAAO;GACT;EACF,SAAS,KAAU;GAEjB,KAAK,OAAO,MACV;IAAE;IAAK;GAAY,GACnB,wDACF;GACA,IAAI,QACF,IAAI;IACF,MAAM,KAAK,YAAY,mBAAmB;IAE1C,MAAM;GACR,QAAQ;IAEN,MAAM,WAAW,iBAAiB,kCAAkC;GACtE;QAEA,IAAI,IAAI,SAAA,UACN,MAAM,WAAW,iBAAiB,kCAAkC;QAC/D,IAAI,IAAI,SAAA,UACb,MAAM,WAAW,YAAY;QAE7B,MAAM;EAGZ;CACF;CAEA,MAAa,cAAc,aAAoC;EAC7D,MAAM,4BAA4B,WAAW;EAE7C,OAAO,MAAM,cAAc,KAAK,YAAY,WAAW,CAAC;CAC1D;CAEA,MAAa,gBAA+B;EAC1C,MAAM,8BAA8B,KAAK,IAAI;EAE7C,MAAM,aAAa,KAAK,YAAY,GAAG,CAAC;CAC1C;;;;;;CAOA,MAAa,aAA+B;EAC1C,MAAM,WAAmB,KAAK,YAAY,mBAAmB;EAC7D,IAAI;GAEF,QAAO,MADY,YAAY,QAAQ,EAAA,CAC3B,OAAO;EACrB,SAAS,KAAU;GACjB,IAAI,IAAI,SAAA,UAAqB;IAC3B,MAAM,6BAA6B,UAAU,KAAK,IAAI;IACtD,OAAO;GACT,OAAO;IACL,KAAK,OAAO,MAAM,EAAE,IAAI,GAAG,iDAAiD;IAC5E,MAAM,WAAW,iBAAiB,iCAAiC;GACrE;EACF;CACF;;;;;;CAOA,MAAa,cAAc,MAAc,UAAmC;EAC1E,MAAM,2BAA2B,IAAI;EACrC,MAAM,cAAc,KAAK,YAAY,mBAAmB;EACxD,IAAI;GAGF,MAAM,YAAY,aAAa,IAAI;EACrC,SAAS,KAAU;GAEjB,IAAI,IAAI,SAAS,UAAU;IACzB,MAAM,oDAAoD,IAAI;IAC9D,MAAM,QAAQ,SAAS;GACzB;EACF;EAEA,MAAM,KAAK,UAAU,aAAa,KAAK,iBAAiB,QAAQ,CAAC;CACnE;CAEA,MAAa,YAAY,MAAc,OAAgC;EACrE,MAAM,qBAAqB,IAAI;EAE/B,MAAM,KAAK,UAAU,KAAK,YAAY,mBAAmB,GAAG,KAAK,iBAAiB,KAAK,CAAC;CAC1F;CAEA,MAAa,YAAY,MAAiC;EACxD,MAAM,qBAAqB,IAAI;EAC/B,IAAI;GACF,MAAM,MAAM,MAAM,KAAK,iBAAiB,KAAK,YAAY,mBAAmB,CAAC;GAC7E,MAAM,OAAY,KAAK,MAAM,IAAI,SAAS,MAAM,CAAC;GAEjD,MAAM,sCAAsC,IAAI;GAChD,OAAO;EACT,SAAS,KAAU;GACjB,IAAI,IAAI,SAAA,UAAqB;IAC3B,MAAM,aAAa;IACnB,KAAK,OAAO,MAAM;KAAE;KAAK;IAAK,GAAG,wCAAwC;GAC3E;GACA,MAAM;EACR;CACF;CAEA,MAAa,WAAW,UAAoC;EAC1D,MAAM,WAAmB,KAAK,YAAY,QAAQ;EAClD,OAAO,IAAI,SAAS,YAAY;GAC9B,cAAc,QAAQ,CAAC,CACpB,WAAW;IACV,QAAQ,IAAI;GACd,CAAC,CAAC,CACD,YAAY,QAAQ,KAAK,CAAC;EAC/B,CAAC;CACH;;;;;;CAOA,MAAc,eAAe,cAA6B;EACxD,MAAM,2BAA2B,YAAY;EAC7C,IAAI;GACF,MAAM,cAAc,YAAY;GAChC,MAAM,4BAA4B,YAAY;EAChD,SAAS,KAAU;GACjB,IAAI,KAAK,SAAS,UAChB,KAAK,OAAO,KACV;IAAE;IAAc,KAAK,IAAI;GAAQ,GACjC,wDACF;EAEJ;CACF;;;;;;;CAQA,MAAa,aAAa,UAAkB,EAAE,UAA6B;EACzE,MAAM,sBAAsB,QAAQ;EACpC,MAAM,WAAmB,KAAK,YAAY,QAAQ;EAClD,MAAM,KAAK,kBAAkB,QAAQ;EAErC,MAAM,eAAe,KAAK,KACxB,KAAK,MACL,UAAU,GAAG,SAAS,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,GAAG,CAC1E;EAEA,MAAM,4BAA4B,YAAY;EAC9C,IAAI,SAAS;EAGb,IAAI,SAAS;EACb,MAAM,cAAc,WAAG,kBAAkB,YAAY;EAErD,YAAY,GAAG,cAAc;GAC3B,SAAS;EACX,CAAC;EAED,YAAY,GAAG,SAAS,OAAO,QAAQ;GACrC,SAAS;GACT,IAAI,QAAQ;IACV,KAAK,OAAO,MACV;KAAE;KAAK;IAAS,GAChB,6DACF;IACA,YAAY,KAAK,SAAS,YAAY;KACpC,MAAM,KAAK,eAAe,YAAY;IACxC,CAAC;GACH,OAAO;IACL,KAAK,OAAO,MACV;KAAE;KAAK;IAAS,GAChB,0DACF;IACA,MAAM,KAAK,eAAe,YAAY;GACxC;EACF,CAAC;EAID,YAAY,KAAK,SAAS,YAAY;GACpC,IAAI,QAEF;GAEF,IAAI;IACF,MAAM,UAAU,cAAc,QAAQ;GACxC,SAAS,KAAK;IACZ,KAAK,OAAO,MACV;KAAE;KAAK;KAAc;IAAS,GAC9B,qGAEF;GACF;EACF,CAAC;EAGD,QAAQ,iBACN,SACA,YAAY;GACV,SAAS;GACT,IAAI,QAEF,YAAY,KAAK,SAAS,YAAY;IACpC,MAAM,KAAK,eAAe,YAAY;GACxC,CAAC;QAED,MAAM,KAAK,eAAe,YAAY;EAE1C,GACA,EAAE,MAAM,KAAK,CACf;EAEA,OAAO;CACT;;;;;;;CAQA,MAAa,YAAY,aAAqB,EAAE,UAA6B;EAC3E,MAAM,WAAmB,KAAK,YAAY,WAAW;EACrD,MAAM,qBAAqB,QAAQ;EACnC,MAAM,aAAa,eAAe,QAAQ,WAAG,iBAAiB,QAAQ,CAAC;EACvE,WAAW,GAAG,QAAQ,SAAU,kBAA0B;GAExD,MAAM,yBAAyB,gBAAgB;GAC/C,IAAI,kBAAkB;IAGpB,IAAI;IACJ,IAAI;KACF,OAAO,WAAG,UAAU,gBAAgB,CAAC,CAAC;IACxC,QAAQ,CAER;IACA,IAAI,SAAS,KAAA,GAAW;KACtB,MAAM,gBAAgB,IAAI;KAC1B,WAAW,KAAK,kBAAkB,IAAI;IACxC;GACF;EACF,CAAC;EACD,WAAW,GAAG,UAAU,UAAU;GAChC,MAAM,yCAAyC,UAAU,aAAa,MAAM,OAAO;EACrF,CAAC;EACD,OAAO;CACT;CAEA,MAAc,iBAAiB,MAA4B;EACzD,MAAM,wBAAwB,IAAI;EAClC,IAAI;GACF,OAAO,MAAM,gBAAgB,IAAI;EACnC,SAAS,KAAU;GACjB,MAAM,4CAA4C,MAAM,IAAI,OAAO;GACnE,MAAM;EACR;CACF;CAEA,iBAAyB,OAAyB;EAChD,OAAO,KAAK,UAAU,KAAK;CAC7B;CAEA,YAAmB,WAAW,IAAY;EACxC,MAAM,cAAsB,KAAK,KAAK,KAAK,MAAM,UAAU,QAAQ,CAAC;EACpE,MAAM,kBAAkB,WAAW;EACnC,OAAO;CACT;CAEA,MAAc,uBAAuB,MAAc,aAAmC;EACpF,MAAM,eAAe,SAAS,IAAI;EAClC,IAAI;GAEF,MAAM,KAAK,kBAAkB,YAAY;GACzC,MAAM,iBAAiB,cAAc,WAAW;GAChD,MAAM,4BAA4B,IAAI;GAEtC,MAAM,UAAU,cAAc,IAAI;EACpC,SAAS,KAAU;GACjB,MAAM,+BAA+B,IAAI;GACzC,MAAM;EACR;CACF;CAEA,MAAc,UAAU,SAAiB,aAAoC;EAC3E,IAAI;GACF,MAAM,KAAK,uBAAuB,SAAS,WAAW;GACtD,MAAM,yBAAyB,OAAO;EACxC,SAAS,KAAU;GACjB,IAAI,OAAO,IAAI,SAAA,UAAqB;IAClC,MAAM,KAAK,kBAAkB,OAAO;IAEpC,MAAM,0BAA0B,OAAO;IACvC,MAAM,KAAK,uBAAuB,SAAS,WAAW;IACtD,MAAM,yBAAyB,OAAO;GACxC,OAAO;IACL,KAAK,OAAO,MAAM,EAAE,IAAI,GAAG,qCAAqC;IAChE,MAAM;GACR;EACF;CACF;CAEA,MAAc,kBAAkB,UAAiC;EAE/D,MAAM,MAAM,KAAK,QAAQ,QAAQ;EACjC,IAAI;GACF,MAAM,cAAc,GAAG;EACzB,SAAS,KAAU;GACjB,IAAI,IAAI,SAAA,UAAqB;IAC3B,MAAM,4BAA4B,GAAG;IACrC,MAAM,aAAa,KAAK,EAAE,WAAW,KAAK,CAAC;IAC3C,MAAM,qBAAqB,GAAG;GAChC,OAAO;IACL,MAAM,4BAA4B,GAAG;IACrC,MAAM;GACR;EACF;CACF;CAEA,MAAc,iBAAiB,MAAiC;EAC9D,MAAM,WAAmB,KAAK,YAAY,IAAI;EAC9C,MAAM,2BAA2B,QAAQ;EACzC,IAAI;GAKF,OAAO,MAJY,aAAuB,UAAU;IAClD,MAAM;IACN,OAAO;GACT,CAAC;EAEH,SAAS,KAAK;GACZ,KAAK,OAAO,MAAM,EAAE,IAAI,GAAG,oCAAoC;GAC/D,MAAM,4CAA4C,IAAI;GAEtD,MAAM;EACR;CACF;CAEA,MAAc,YAAY,MAA6B;EACrD,MAAM,eAAe,KAAK,YAAY,IAAI,CAAC;CAC7C;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/local-storage",
|
|
3
|
-
"version": "14.0.0-next-9.
|
|
3
|
+
"version": "14.0.0-next-9.32",
|
|
4
4
|
"description": "Verdaccio Local Storage Plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"private",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=24"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@verdaccio/core": "9.0.0-next-9.
|
|
39
|
+
"@verdaccio/core": "9.0.0-next-9.32",
|
|
40
40
|
"@verdaccio/file-locking": "14.0.0-next-9.4",
|
|
41
41
|
"debug": "4.4.3",
|
|
42
42
|
"globby": "11.1.0",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/lodash-es": "4.17.12",
|
|
49
|
-
"@verdaccio/logger": "9.0.0-next-9.
|
|
50
|
-
"@verdaccio/types": "14.0.0-next-9.
|
|
49
|
+
"@verdaccio/logger": "9.0.0-next-9.32",
|
|
50
|
+
"@verdaccio/types": "14.0.0-next-9.15",
|
|
51
51
|
"rimraf": "6.1.3",
|
|
52
|
-
"vite": "8.2.
|
|
53
|
-
"vitest": "4.1.
|
|
52
|
+
"vite": "8.2.2",
|
|
53
|
+
"vitest": "4.1.11"
|
|
54
54
|
},
|
|
55
55
|
"funding": {
|
|
56
56
|
"type": "opencollective",
|