@jayxuz/verdaccio-offline-storage 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/README.zh-CN.md +138 -0
- package/build/OfflinePackageStorage.d.ts +21 -0
- package/build/OfflinePackageStorage.d.ts.map +1 -0
- package/build/OfflinePackageStorage.js +109 -0
- package/build/OfflinePackageStorage.js.map +1 -0
- package/build/OfflineStoragePlugin.d.ts +32 -0
- package/build/OfflineStoragePlugin.d.ts.map +1 -0
- package/build/OfflineStoragePlugin.js +86 -0
- package/build/OfflineStoragePlugin.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +20 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +16 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/package.json +45 -0
- package/src-backup/OfflinePackageStorage.js +122 -0
- package/src-backup/OfflineStoragePlugin.js +102 -0
- package/src-backup/plugin.js +4 -0
- package/tsconfig.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @jayxuz/verdaccio-offline-storage
|
|
2
|
+
|
|
3
|
+
English | [中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
A Verdaccio storage plugin that treats local package cache as first class citizen for offline environments.
|
|
6
|
+
|
|
7
|
+
> **Fork of [verdaccio-offline-storage](https://github.com/g3ngar/verdaccio-offline-storage)** with improvements for Verdaccio 6.x compatibility and enhanced functionality.
|
|
8
|
+
|
|
9
|
+
## What's Different from the Original?
|
|
10
|
+
|
|
11
|
+
This is an improved fork of the original `verdaccio-offline-storage` plugin. Key improvements include:
|
|
12
|
+
|
|
13
|
+
### Verdaccio 6.x Compatibility
|
|
14
|
+
- **TypeScript Rewrite**: Fully rewritten in TypeScript for better type safety and maintainability
|
|
15
|
+
- **Updated Dependencies**: Compatible with `@verdaccio/local-storage` 13.x and `@verdaccio/core` 8.x
|
|
16
|
+
- **Dual Package Support**: Automatically detects and uses either `@verdaccio/local-storage-legacy` (Verdaccio 6.x) or `@verdaccio/local-storage` (newer versions)
|
|
17
|
+
|
|
18
|
+
### Enhanced Version Handling
|
|
19
|
+
- **Semver Validation**: Uses proper semver validation when extracting versions from tarball filenames
|
|
20
|
+
- **Prerelease Handling**: Prefers stable versions over prereleases when setting `dist-tags.latest`
|
|
21
|
+
- **Robust Sorting**: Uses `semver.compare()` instead of simple string comparison for accurate version ordering
|
|
22
|
+
|
|
23
|
+
### Improved Error Handling
|
|
24
|
+
- **Async/Await**: Uses modern async/await patterns with Promises instead of callbacks
|
|
25
|
+
- **Graceful Degradation**: Better error handling that doesn't break the entire request on partial failures
|
|
26
|
+
- **Detailed Logging**: More informative debug and trace logs for troubleshooting
|
|
27
|
+
|
|
28
|
+
### Code Quality
|
|
29
|
+
- **Modern JavaScript**: ES2020+ features, async/await, optional chaining
|
|
30
|
+
- **Type Definitions**: Full TypeScript type definitions included
|
|
31
|
+
- **Null Safety**: Proper null checks for `data.versions`, `packageAccess.proxy`, etc.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **Offline-First**: Makes Verdaccio's package cache work properly when going offline
|
|
36
|
+
- **No Lockfile Required**: All dependencies resolve correctly if they were cached when online
|
|
37
|
+
- **Transparent**: Works with existing `local-storage` cache without modifications
|
|
38
|
+
- **Selective Offline Mode**: Can be enabled globally or per-package based on proxy configuration
|
|
39
|
+
- **Web UI Integration**: Lists all locally available packages in Verdaccio's web interface
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @jayxuz/verdaccio-offline-storage
|
|
45
|
+
# or
|
|
46
|
+
yarn add @jayxuz/verdaccio-offline-storage
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
Edit your Verdaccio `config.yaml`:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
# Storage path (same as default local-storage)
|
|
55
|
+
storage: /path/to/storage
|
|
56
|
+
|
|
57
|
+
# Use this plugin instead of default storage
|
|
58
|
+
store:
|
|
59
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
60
|
+
# Optional: force offline mode for ALL packages
|
|
61
|
+
offline: true
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Offline Mode Options
|
|
65
|
+
|
|
66
|
+
**Option 1: Selective Offline (Default)**
|
|
67
|
+
|
|
68
|
+
Without `offline: true`, packages are resolved in offline mode only when they have no `proxy` defined:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
packages:
|
|
72
|
+
'@my-scope/*':
|
|
73
|
+
access: $all
|
|
74
|
+
publish: $authenticated
|
|
75
|
+
# No proxy = offline mode
|
|
76
|
+
|
|
77
|
+
'**':
|
|
78
|
+
access: $all
|
|
79
|
+
publish: $authenticated
|
|
80
|
+
proxy: npmjs # Has proxy = online mode
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Option 2: Global Offline**
|
|
84
|
+
|
|
85
|
+
With `offline: true`, ALL packages are resolved in offline mode regardless of proxy settings:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
store:
|
|
89
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
90
|
+
offline: true
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## How It Works
|
|
94
|
+
|
|
95
|
+
1. When a package is requested, the plugin scans the storage directory for `.tgz` files
|
|
96
|
+
2. It filters the package metadata to only include versions that have local tarballs
|
|
97
|
+
3. It updates `dist-tags.latest` to the highest locally available stable version
|
|
98
|
+
4. The modified metadata is returned to the client
|
|
99
|
+
|
|
100
|
+
This means:
|
|
101
|
+
- `npm install package@latest` installs the latest **locally available** version
|
|
102
|
+
- Version ranges like `^1.0.0` resolve to locally available versions
|
|
103
|
+
- No network errors when upstream registry is unreachable
|
|
104
|
+
|
|
105
|
+
## Requirements
|
|
106
|
+
|
|
107
|
+
- Node.js >= 18.0.0
|
|
108
|
+
- Verdaccio >= 5.0.0 (tested with 6.x)
|
|
109
|
+
|
|
110
|
+
## Migration from Original Plugin
|
|
111
|
+
|
|
112
|
+
If you're migrating from `verdaccio-offline-storage`:
|
|
113
|
+
|
|
114
|
+
1. Install this package: `npm install @jayxuz/verdaccio-offline-storage`
|
|
115
|
+
2. Update your `config.yaml`:
|
|
116
|
+
```yaml
|
|
117
|
+
store:
|
|
118
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
119
|
+
# your existing options
|
|
120
|
+
```
|
|
121
|
+
3. Restart Verdaccio
|
|
122
|
+
|
|
123
|
+
Your existing storage data is fully compatible - no migration needed.
|
|
124
|
+
|
|
125
|
+
## Related Plugins
|
|
126
|
+
|
|
127
|
+
This plugin works well with:
|
|
128
|
+
|
|
129
|
+
- **verdaccio-ingest-middleware**: Download packages from upstream for offline caching
|
|
130
|
+
- **verdaccio-metadata-healer**: Automatically repair missing package metadata
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
|
135
|
+
|
|
136
|
+
## Credits
|
|
137
|
+
|
|
138
|
+
Original plugin by [g3ngar](https://github.com/g3ngar/verdaccio-offline-storage)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @jayxuz/verdaccio-offline-storage
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 中文
|
|
4
|
+
|
|
5
|
+
一个将本地包缓存作为一等公民的 Verdaccio 存储插件,专为离线环境设计。
|
|
6
|
+
|
|
7
|
+
> **[verdaccio-offline-storage](https://github.com/g3ngar/verdaccio-offline-storage) 的改进分支**,增强了 Verdaccio 6.x 兼容性和功能。
|
|
8
|
+
|
|
9
|
+
## 与原版的区别
|
|
10
|
+
|
|
11
|
+
这是原版 `verdaccio-offline-storage` 插件的改进分支。主要改进包括:
|
|
12
|
+
|
|
13
|
+
### Verdaccio 6.x 兼容性
|
|
14
|
+
- **TypeScript 重写**:完全使用 TypeScript 重写,提供更好的类型安全性和可维护性
|
|
15
|
+
- **更新依赖**:兼容 `@verdaccio/local-storage` 13.x 和 `@verdaccio/core` 8.x
|
|
16
|
+
- **双包支持**:自动检测并使用 `@verdaccio/local-storage-legacy`(Verdaccio 6.x)或 `@verdaccio/local-storage`(更新版本)
|
|
17
|
+
|
|
18
|
+
### 增强的版本处理
|
|
19
|
+
- **Semver 验证**:从 tarball 文件名提取版本时使用正确的 semver 验证
|
|
20
|
+
- **预发布版本处理**:设置 `dist-tags.latest` 时优先选择稳定版本而非预发布版本
|
|
21
|
+
- **健壮的排序**:使用 `semver.compare()` 而非简单字符串比较,确保版本排序准确
|
|
22
|
+
|
|
23
|
+
### 改进的错误处理
|
|
24
|
+
- **Async/Await**:使用现代 async/await 模式替代回调
|
|
25
|
+
- **优雅降级**:更好的错误处理,部分失败不会导致整个请求失败
|
|
26
|
+
- **详细日志**:更丰富的 debug 和 trace 日志,便于故障排查
|
|
27
|
+
|
|
28
|
+
### 代码质量
|
|
29
|
+
- **现代 JavaScript**:ES2020+ 特性、async/await、可选链
|
|
30
|
+
- **类型定义**:包含完整的 TypeScript 类型定义
|
|
31
|
+
- **空值安全**:对 `data.versions`、`packageAccess.proxy` 等进行正确的空值检查
|
|
32
|
+
|
|
33
|
+
## 功能特性
|
|
34
|
+
|
|
35
|
+
- **离线优先**:使 Verdaccio 的包缓存在离线时正常工作
|
|
36
|
+
- **无需锁文件**:如果在线时已缓存,所有依赖都能正确解析
|
|
37
|
+
- **透明**:与现有的 `local-storage` 缓存兼容,无需修改
|
|
38
|
+
- **选择性离线模式**:可以全局启用或根据 proxy 配置按包启用
|
|
39
|
+
- **Web 界面集成**:在 Verdaccio 的 Web 界面中列出所有本地可用的包
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @jayxuz/verdaccio-offline-storage
|
|
45
|
+
# 或
|
|
46
|
+
yarn add @jayxuz/verdaccio-offline-storage
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 配置
|
|
50
|
+
|
|
51
|
+
编辑 Verdaccio 的 `config.yaml`:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
# 存储路径(与默认 local-storage 相同)
|
|
55
|
+
storage: /path/to/storage
|
|
56
|
+
|
|
57
|
+
# 使用此插件替代默认存储
|
|
58
|
+
store:
|
|
59
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
60
|
+
# 可选:强制所有包使用离线模式
|
|
61
|
+
offline: true
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 离线模式选项
|
|
65
|
+
|
|
66
|
+
**选项 1:选择性离线(默认)**
|
|
67
|
+
|
|
68
|
+
不设置 `offline: true` 时,只有未定义 `proxy` 的包才会以离线模式解析:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
packages:
|
|
72
|
+
'@my-scope/*':
|
|
73
|
+
access: $all
|
|
74
|
+
publish: $authenticated
|
|
75
|
+
# 无 proxy = 离线模式
|
|
76
|
+
|
|
77
|
+
'**':
|
|
78
|
+
access: $all
|
|
79
|
+
publish: $authenticated
|
|
80
|
+
proxy: npmjs # 有 proxy = 在线模式
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**选项 2:全局离线**
|
|
84
|
+
|
|
85
|
+
设置 `offline: true` 时,所有包都以离线模式解析,忽略 proxy 设置:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
store:
|
|
89
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
90
|
+
offline: true
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 工作原理
|
|
94
|
+
|
|
95
|
+
1. 当请求包时,插件扫描存储目录中的 `.tgz` 文件
|
|
96
|
+
2. 过滤包元数据,只包含有本地 tarball 的版本
|
|
97
|
+
3. 将 `dist-tags.latest` 更新为本地可用的最高稳定版本
|
|
98
|
+
4. 将修改后的元数据返回给客户端
|
|
99
|
+
|
|
100
|
+
这意味着:
|
|
101
|
+
- `npm install package@latest` 安装最新的**本地可用**版本
|
|
102
|
+
- 版本范围如 `^1.0.0` 解析为本地可用的版本
|
|
103
|
+
- 上游仓库不可达时不会出现网络错误
|
|
104
|
+
|
|
105
|
+
## 系统要求
|
|
106
|
+
|
|
107
|
+
- Node.js >= 18.0.0
|
|
108
|
+
- Verdaccio >= 5.0.0(已在 6.x 测试)
|
|
109
|
+
|
|
110
|
+
## 从原版插件迁移
|
|
111
|
+
|
|
112
|
+
如果你正在从 `verdaccio-offline-storage` 迁移:
|
|
113
|
+
|
|
114
|
+
1. 安装此包:`npm install @jayxuz/verdaccio-offline-storage`
|
|
115
|
+
2. 更新 `config.yaml`:
|
|
116
|
+
```yaml
|
|
117
|
+
store:
|
|
118
|
+
'@jayxuz/verdaccio-offline-storage':
|
|
119
|
+
# 你现有的选项
|
|
120
|
+
```
|
|
121
|
+
3. 重启 Verdaccio
|
|
122
|
+
|
|
123
|
+
你现有的存储数据完全兼容 - 无需迁移。
|
|
124
|
+
|
|
125
|
+
## 相关插件
|
|
126
|
+
|
|
127
|
+
本插件可与以下插件配合使用:
|
|
128
|
+
|
|
129
|
+
- **verdaccio-ingest-middleware**:从上游下载包用于离线缓存
|
|
130
|
+
- **verdaccio-metadata-healer**:自动修复缺失的包元数据
|
|
131
|
+
|
|
132
|
+
## 许可证
|
|
133
|
+
|
|
134
|
+
MIT
|
|
135
|
+
|
|
136
|
+
## 致谢
|
|
137
|
+
|
|
138
|
+
原版插件作者 [g3ngar](https://github.com/g3ngar/verdaccio-offline-storage)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Logger, Callback } from '@verdaccio/types';
|
|
2
|
+
import { OfflineStorageConfig } from './types';
|
|
3
|
+
declare let LocalFS: any;
|
|
4
|
+
/**
|
|
5
|
+
* IPackageStorage implementation for offline mode
|
|
6
|
+
*
|
|
7
|
+
* This works just like the IPackageStorage used by the local-storage plugin but modifying
|
|
8
|
+
* the packages definition files so only the locally available versions appear in the definition.
|
|
9
|
+
* This does NOT modify the original package.json file stored in the local-storage cache.
|
|
10
|
+
*/
|
|
11
|
+
export declare class OfflinePackageStorage extends LocalFS {
|
|
12
|
+
private config;
|
|
13
|
+
protected logger: Logger;
|
|
14
|
+
constructor(path: string, logger: Logger, config: OfflineStorageConfig);
|
|
15
|
+
/**
|
|
16
|
+
* Computes a package's definition that only lists the locally available versions.
|
|
17
|
+
*/
|
|
18
|
+
readPackage(name: string, cb: Callback): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=OfflinePackageStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflinePackageStorage.d.ts","sourceRoot":"","sources":["../src/OfflinePackageStorage.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAY,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI/C,QAAA,IAAI,OAAO,EAAE,GAAG,CAAC;AAejB;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,OAAO;IAChD,OAAO,CAAC,MAAM,CAAuB;IACrC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEb,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB;IAMtE;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,IAAI;CAqG9C"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OfflinePackageStorage = void 0;
|
|
7
|
+
const promises_1 = require("fs/promises");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const semver_1 = __importDefault(require("semver"));
|
|
10
|
+
// Import LocalFS from @verdaccio/local-storage-legacy
|
|
11
|
+
// Verdaccio 6.x uses local-storage-legacy package
|
|
12
|
+
let LocalFS;
|
|
13
|
+
try {
|
|
14
|
+
// Try @verdaccio/local-storage-legacy first (used by Verdaccio 6.x)
|
|
15
|
+
LocalFS = require('@verdaccio/local-storage-legacy/lib/local-fs').default;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
try {
|
|
19
|
+
// Fallback to @verdaccio/local-storage (newer versions)
|
|
20
|
+
LocalFS = require('@verdaccio/local-storage').LocalDriver ||
|
|
21
|
+
require('@verdaccio/local-storage/lib/local-fs').default;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.error('[verdaccio-offline-storage] Failed to load LocalFS:', e);
|
|
25
|
+
throw new Error('Could not load LocalFS from @verdaccio/local-storage-legacy or @verdaccio/local-storage');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* IPackageStorage implementation for offline mode
|
|
30
|
+
*
|
|
31
|
+
* This works just like the IPackageStorage used by the local-storage plugin but modifying
|
|
32
|
+
* the packages definition files so only the locally available versions appear in the definition.
|
|
33
|
+
* This does NOT modify the original package.json file stored in the local-storage cache.
|
|
34
|
+
*/
|
|
35
|
+
class OfflinePackageStorage extends LocalFS {
|
|
36
|
+
constructor(path, logger, config) {
|
|
37
|
+
super(path, logger);
|
|
38
|
+
this.config = config;
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Computes a package's definition that only lists the locally available versions.
|
|
43
|
+
*/
|
|
44
|
+
readPackage(name, cb) {
|
|
45
|
+
const packageAccess = this.config.getMatchedPackagesSpec
|
|
46
|
+
? this.config.getMatchedPackagesSpec(name)
|
|
47
|
+
: { proxy: [] };
|
|
48
|
+
// It's offline if set explicitly in the config or if no proxy is defined for the package
|
|
49
|
+
const offline = this.config.offline || !packageAccess?.proxy || packageAccess.proxy.length === 0;
|
|
50
|
+
if (!offline) {
|
|
51
|
+
this.logger.debug({ packageName: name }, '[verdaccio-offline-storage/readPackage] Resolving package @{packageName} in online mode');
|
|
52
|
+
super.readPackage(name, cb);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.logger.debug({ packageName: name }, '[verdaccio-offline-storage/readPackage] Resolving package @{packageName} in offline mode');
|
|
56
|
+
super.readPackage(name, async (err, data) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
cb(err, data);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
this.logger.debug({ packageName: name }, '[verdaccio-offline-storage/readPackage] Discovering local versions for package: @{packageName}');
|
|
63
|
+
const items = await (0, promises_1.readdir)(this.path);
|
|
64
|
+
const localVersions = items
|
|
65
|
+
.filter((item) => item.endsWith('.tgz'))
|
|
66
|
+
.map((item) => {
|
|
67
|
+
// Extract version from filename: package-name-1.0.0.tgz -> 1.0.0
|
|
68
|
+
const baseName = (0, path_1.basename)(name);
|
|
69
|
+
return item.substring(baseName.length + 1, item.length - 4);
|
|
70
|
+
})
|
|
71
|
+
.filter((v) => semver_1.default.valid(v));
|
|
72
|
+
this.logger.debug({ packageName: name, count: localVersions.length }, '[verdaccio-offline-storage/readPackage] Discovered @{count} local versions for package: @{packageName}');
|
|
73
|
+
const allVersions = Object.keys(data.versions || {});
|
|
74
|
+
const originalVersionCount = allVersions.length;
|
|
75
|
+
// Remove versions that are not locally available
|
|
76
|
+
for (const version of allVersions) {
|
|
77
|
+
if (!localVersions.includes(version)) {
|
|
78
|
+
delete data.versions[version];
|
|
79
|
+
this.logger.trace({ packageName: name, version }, '[verdaccio-offline-storage/readPackage] Removed @{packageName}@@{version}');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const removedCount = originalVersionCount - Object.keys(data.versions).length;
|
|
83
|
+
this.logger.debug({ packageName: name, count: removedCount }, '[verdaccio-offline-storage/readPackage] Removed @{count} unavailable versions for package: @{packageName}');
|
|
84
|
+
// Update dist-tags.latest to the highest locally available version
|
|
85
|
+
const availableVersions = Object.keys(data.versions);
|
|
86
|
+
if (availableVersions.length > 0) {
|
|
87
|
+
const sortedVersions = availableVersions
|
|
88
|
+
.filter((v) => semver_1.default.valid(v))
|
|
89
|
+
.sort((a, b) => semver_1.default.compare(b, a));
|
|
90
|
+
// Prefer stable versions over prereleases
|
|
91
|
+
const stableVersions = sortedVersions.filter((v) => !semver_1.default.prerelease(v));
|
|
92
|
+
const latestVersion = stableVersions.length > 0 ? stableVersions[0] : sortedVersions[0];
|
|
93
|
+
if (latestVersion) {
|
|
94
|
+
data['dist-tags'] = data['dist-tags'] || {};
|
|
95
|
+
data['dist-tags'].latest = latestVersion;
|
|
96
|
+
this.logger.debug({ packageName: name, latest: latestVersion }, '[verdaccio-offline-storage/readPackage] Set latest version to @{latest} for package: @{packageName}');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
cb(null, data);
|
|
100
|
+
}
|
|
101
|
+
catch (readErr) {
|
|
102
|
+
this.logger.error({ err: readErr.message, packageName: name }, '[verdaccio-offline-storage/readPackage] Error discovering package "@{packageName}" files: @{err}');
|
|
103
|
+
cb(readErr, data);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.OfflinePackageStorage = OfflinePackageStorage;
|
|
109
|
+
//# sourceMappingURL=OfflinePackageStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflinePackageStorage.js","sourceRoot":"","sources":["../src/OfflinePackageStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Logger, Callback } from '@verdaccio/types';
|
|
2
|
+
import { pluginUtils } from '@verdaccio/core';
|
|
3
|
+
import { OfflineStorageConfig, PluginOptions } from './types';
|
|
4
|
+
declare let LocalDatabase: any;
|
|
5
|
+
/**
|
|
6
|
+
* Verdaccio storage plugin that provides only the locally available versions of
|
|
7
|
+
* packages cached in a local-storage storage.
|
|
8
|
+
*
|
|
9
|
+
* This is just like local-storage but modifying on the fly the available packages list
|
|
10
|
+
* and the packages definitions without altering the original files in the local-storage storage.
|
|
11
|
+
*
|
|
12
|
+
* @see https://verdaccio.org/docs/en/plugin-storage
|
|
13
|
+
*/
|
|
14
|
+
export default class OfflineStoragePlugin extends LocalDatabase {
|
|
15
|
+
protected config: OfflineStorageConfig;
|
|
16
|
+
protected logger: Logger;
|
|
17
|
+
protected data: {
|
|
18
|
+
list: string[];
|
|
19
|
+
};
|
|
20
|
+
constructor(config: OfflineStorageConfig, options: PluginOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Retrieves all the locally available packages names.
|
|
23
|
+
* Packages with no cached versions (only package.json file in the directory) are ignored.
|
|
24
|
+
*/
|
|
25
|
+
get(callback: Callback): void;
|
|
26
|
+
/**
|
|
27
|
+
* Returns the IPackageStorage used internally for packages I/O operations.
|
|
28
|
+
*/
|
|
29
|
+
getPackageStorage(packageName: string): pluginUtils.StorageHandler;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=OfflineStoragePlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineStoragePlugin.d.ts","sourceRoot":"","sources":["../src/OfflineStoragePlugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI9D,QAAA,IAAI,aAAa,EAAE,GAAG,CAAC;AAgBvB;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,aAAa;IAC7D,SAAS,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACvC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;gBAEvB,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,aAAa;IAmBhE;;;OAGG;IACH,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA+C7B;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAAC,cAAc;CAInE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path_1 = require("path");
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
5
|
+
const OfflinePackageStorage_1 = require("./OfflinePackageStorage");
|
|
6
|
+
// Import LocalDatabase - try multiple package names for compatibility
|
|
7
|
+
let LocalDatabase;
|
|
8
|
+
try {
|
|
9
|
+
// Try @verdaccio/local-storage-legacy first (used by Verdaccio 6.x)
|
|
10
|
+
LocalDatabase = require('@verdaccio/local-storage-legacy').LocalDatabase ||
|
|
11
|
+
require('@verdaccio/local-storage-legacy').default;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
try {
|
|
15
|
+
// Fallback to @verdaccio/local-storage (newer versions)
|
|
16
|
+
LocalDatabase = require('@verdaccio/local-storage').LocalDatabase ||
|
|
17
|
+
require('@verdaccio/local-storage').default;
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.error('[verdaccio-offline-storage] Failed to load LocalDatabase:', e);
|
|
21
|
+
throw new Error('Could not load LocalDatabase from @verdaccio/local-storage-legacy or @verdaccio/local-storage');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Verdaccio storage plugin that provides only the locally available versions of
|
|
26
|
+
* packages cached in a local-storage storage.
|
|
27
|
+
*
|
|
28
|
+
* This is just like local-storage but modifying on the fly the available packages list
|
|
29
|
+
* and the packages definitions without altering the original files in the local-storage storage.
|
|
30
|
+
*
|
|
31
|
+
* @see https://verdaccio.org/docs/en/plugin-storage
|
|
32
|
+
*/
|
|
33
|
+
class OfflineStoragePlugin extends LocalDatabase {
|
|
34
|
+
constructor(config, options) {
|
|
35
|
+
super(config, options.logger);
|
|
36
|
+
this.config = config;
|
|
37
|
+
this.logger = options.logger;
|
|
38
|
+
this.data = { list: [] };
|
|
39
|
+
if (config.offline) {
|
|
40
|
+
this.logger.warn({}, '[verdaccio-offline-storage] Offline mode set explicitly in config. All packages will be resolved in offline mode.');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.logger.warn({}, '[verdaccio-offline-storage] Offline mode NOT set explicitly in config. Only packages with no `proxy` will be resolved in offline mode.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves all the locally available packages names.
|
|
48
|
+
* Packages with no cached versions (only package.json file in the directory) are ignored.
|
|
49
|
+
*/
|
|
50
|
+
get(callback) {
|
|
51
|
+
const packages = [];
|
|
52
|
+
this.search((item, cb) => {
|
|
53
|
+
this.logger.debug({ packageName: item.name }, '[verdaccio-offline-storage/get] Discovering local versions for package: @{packageName}');
|
|
54
|
+
(0, promises_1.readdir)(item.path)
|
|
55
|
+
.then((items) => {
|
|
56
|
+
const hasTgz = items.some((file) => file.endsWith('.tgz'));
|
|
57
|
+
if (hasTgz) {
|
|
58
|
+
packages.push(item.name);
|
|
59
|
+
this.logger.trace({ packageName: item.name }, '[verdaccio-offline-storage/get] Found locally available package: "@{packageName}"');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.logger.trace({ packageName: item.name }, '[verdaccio-offline-storage/get] No locally available version found for package: "@{packageName}"');
|
|
63
|
+
}
|
|
64
|
+
cb();
|
|
65
|
+
})
|
|
66
|
+
.catch((err) => {
|
|
67
|
+
this.logger.trace({ err: err.message, packageName: item.name }, '[verdaccio-offline-storage/get] Error discovering package "@{packageName}\" files: @{err}');
|
|
68
|
+
cb(err);
|
|
69
|
+
});
|
|
70
|
+
}, () => {
|
|
71
|
+
this.data.list = packages;
|
|
72
|
+
callback(null, packages);
|
|
73
|
+
this.logger.debug({ totalItems: packages.length }, '[verdaccio-offline-storage/get] Full list of packages (@{totalItems}) has been fetched');
|
|
74
|
+
}, (name) => !name.startsWith('.') // Ignore .verdaccio-db.json etc.
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Returns the IPackageStorage used internally for packages I/O operations.
|
|
79
|
+
*/
|
|
80
|
+
getPackageStorage(packageName) {
|
|
81
|
+
const storagePath = (0, path_1.join)(this.config.storage, packageName);
|
|
82
|
+
return new OfflinePackageStorage_1.OfflinePackageStorage(storagePath, this.logger, this.config);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.default = OfflineStoragePlugin;
|
|
86
|
+
//# sourceMappingURL=OfflineStoragePlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineStoragePlugin.js","sourceRoot":"","sources":["../src/OfflineStoragePlugin.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verdaccio-offline-storage
|
|
3
|
+
*
|
|
4
|
+
* A Verdaccio storage plugin that treats local package cache as first class citizen.
|
|
5
|
+
* Provides only locally available versions of packages for offline environments.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/g3ngar/verdaccio-offline-storage
|
|
8
|
+
*/
|
|
9
|
+
import OfflineStoragePlugin from './OfflineStoragePlugin';
|
|
10
|
+
import { OfflinePackageStorage } from './OfflinePackageStorage';
|
|
11
|
+
import { OfflineStorageConfig, PluginOptions } from './types';
|
|
12
|
+
export default OfflineStoragePlugin;
|
|
13
|
+
export { OfflineStoragePlugin, OfflinePackageStorage, OfflineStorageConfig, PluginOptions };
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,oBAAoB,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE9D,eAAe,oBAAoB,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* verdaccio-offline-storage
|
|
4
|
+
*
|
|
5
|
+
* A Verdaccio storage plugin that treats local package cache as first class citizen.
|
|
6
|
+
* Provides only locally available versions of packages for offline environments.
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/g3ngar/verdaccio-offline-storage
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.OfflinePackageStorage = exports.OfflineStoragePlugin = void 0;
|
|
15
|
+
const OfflineStoragePlugin_1 = __importDefault(require("./OfflineStoragePlugin"));
|
|
16
|
+
exports.OfflineStoragePlugin = OfflineStoragePlugin_1.default;
|
|
17
|
+
const OfflinePackageStorage_1 = require("./OfflinePackageStorage");
|
|
18
|
+
Object.defineProperty(exports, "OfflinePackageStorage", { enumerable: true, get: function () { return OfflinePackageStorage_1.OfflinePackageStorage; } });
|
|
19
|
+
exports.default = OfflineStoragePlugin_1.default;
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;AAEH,kFAA0D;AAKjD,+BALF,8BAAoB,CAKE;AAJ7B,mEAAgE;AAIjC,sGAJtB,6CAAqB,OAIsB;AADpD,kBAAe,8BAAoB,CAAC"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Config, Logger } from '@verdaccio/types';
|
|
2
|
+
/**
|
|
3
|
+
* Offline storage plugin configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface OfflineStorageConfig extends Config {
|
|
6
|
+
/** Force offline mode for all packages */
|
|
7
|
+
offline?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Plugin options passed by Verdaccio
|
|
11
|
+
*/
|
|
12
|
+
export interface PluginOptions {
|
|
13
|
+
config: Config;
|
|
14
|
+
logger: Logger;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,MAAM;IAClD,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jayxuz/verdaccio-offline-storage",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Verdaccio storage plugin that treats local package cache as first class citizen for offline environments",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"clean": "rm -rf build",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"test": "jest"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"verdaccio",
|
|
15
|
+
"plugin",
|
|
16
|
+
"storage",
|
|
17
|
+
"offline",
|
|
18
|
+
"local",
|
|
19
|
+
"cache",
|
|
20
|
+
"npm",
|
|
21
|
+
"registry"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@verdaccio/local-storage": "13.0.0-next-8.28",
|
|
25
|
+
"semver": "^7.5.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@verdaccio/types": "13.0.0-next-8.10",
|
|
29
|
+
"@verdaccio/core": "8.0.0-next-8.28",
|
|
30
|
+
"@types/jsonwebtoken": "^9.0.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"@types/semver": "^7.5.0",
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"verdaccio": ">=5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": "https://github.com/g3ngar/verdaccio-offline-storage",
|
|
43
|
+
"author": "g3ngar <gengar@nauta.cu>",
|
|
44
|
+
"homepage": "https://github.com/g3ngar/verdaccio-offline-storage"
|
|
45
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readdir } from 'fs';
|
|
2
|
+
import { basename } from 'path';
|
|
3
|
+
import cmp from 'semver-compare';
|
|
4
|
+
import LocalFS from '@verdaccio/local-storage/lib/local-fs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `IPackageStorage` used internally for packages I/O operations.
|
|
8
|
+
*
|
|
9
|
+
* This works just like the `IPackageStorage` used by the local-storage plugin but modifying
|
|
10
|
+
* the packages definition files (the local-storage `package.json` files) so only the locally
|
|
11
|
+
* available versions appears in the definition. This does **NOT** modifies the original
|
|
12
|
+
* `package.json` file stored in the local-storage cache, meaning that all the modifications are
|
|
13
|
+
* done on the fly, on demand and in memory.
|
|
14
|
+
*
|
|
15
|
+
* @see https://verdaccio.org/docs/en/plugin-storage#api
|
|
16
|
+
* @see https://github.com/verdaccio/monorepo/tree/master/plugins/local-storage
|
|
17
|
+
*/
|
|
18
|
+
export default class OfflinePackageStorage extends LocalFS {
|
|
19
|
+
constructor(path, logger, config) {
|
|
20
|
+
super(path, logger);
|
|
21
|
+
this.config = config;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Computes a package's definition that only lists the locally available versions.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} name Package name.
|
|
27
|
+
* @param cb Callback to invoke with the computed definition.
|
|
28
|
+
*/
|
|
29
|
+
readPackage(name, cb) {
|
|
30
|
+
const packageAccess = this.config.getMatchedPackagesSpec(name);
|
|
31
|
+
// It's offline if set explicitly in the config or if no proxy is defined for the package
|
|
32
|
+
const offline = this.config.offline || !packageAccess.proxy || !packageAccess.proxy.length;
|
|
33
|
+
if (!offline) {
|
|
34
|
+
this.logger.trace(
|
|
35
|
+
{
|
|
36
|
+
packageName: name,
|
|
37
|
+
},
|
|
38
|
+
'[verdaccio-offline-storage/readPackage] Resolving package @{packageName} in online mode'
|
|
39
|
+
);
|
|
40
|
+
super.readPackage(name, cb);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.logger.trace(
|
|
44
|
+
{
|
|
45
|
+
packageName: name,
|
|
46
|
+
},
|
|
47
|
+
'[verdaccio-offline-storage/readPackage] Resolving package @{packageName} in offline mode'
|
|
48
|
+
);
|
|
49
|
+
super.readPackage(name, (err, data) => {
|
|
50
|
+
if (err) {
|
|
51
|
+
cb(err);
|
|
52
|
+
} else {
|
|
53
|
+
this.logger.trace(
|
|
54
|
+
{
|
|
55
|
+
packageName: name,
|
|
56
|
+
},
|
|
57
|
+
'[verdaccio-offline-storage/readPackage] Discovering local versions for package: @{packageName}'
|
|
58
|
+
);
|
|
59
|
+
readdir(this.path, (err, items) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
this.logger.trace(
|
|
62
|
+
{
|
|
63
|
+
err,
|
|
64
|
+
packageName: name,
|
|
65
|
+
},
|
|
66
|
+
'[verdaccio-offline-storage/readPackage/readdir] Error discovering package "@{packageName}" files: @{err}'
|
|
67
|
+
);
|
|
68
|
+
cb(err);
|
|
69
|
+
} else {
|
|
70
|
+
const localVersions = items
|
|
71
|
+
.filter(item => item.endsWith('.tgz'))
|
|
72
|
+
.map(item => item.substring(basename(name).length + 1, item.length - 4));
|
|
73
|
+
this.logger.trace(
|
|
74
|
+
{
|
|
75
|
+
packageName: name,
|
|
76
|
+
count: localVersions.length,
|
|
77
|
+
},
|
|
78
|
+
'[verdaccio-offline-storage/readPackage/readdir] Discovered @{count} items for package: @{packageName}'
|
|
79
|
+
);
|
|
80
|
+
const allVersions = Object.keys(data.versions);
|
|
81
|
+
const originalVersionCount = allVersions.length;
|
|
82
|
+
this.logger.trace(
|
|
83
|
+
{
|
|
84
|
+
packageName: name,
|
|
85
|
+
count: originalVersionCount,
|
|
86
|
+
},
|
|
87
|
+
'[verdaccio-offline-storage/readPackage/readdir] Analyzing @{count} declared versions for package: @{packageName}'
|
|
88
|
+
);
|
|
89
|
+
for (const version of allVersions) {
|
|
90
|
+
if (!localVersions.includes(version)) {
|
|
91
|
+
delete data.versions[version];
|
|
92
|
+
this.logger.trace(
|
|
93
|
+
{
|
|
94
|
+
packageName: name,
|
|
95
|
+
version,
|
|
96
|
+
},
|
|
97
|
+
'[verdaccio-offline-storage/readPackage/readdir] Removed @{packageName}@@{version}'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.logger.trace(
|
|
102
|
+
{
|
|
103
|
+
packageName: name,
|
|
104
|
+
count: originalVersionCount - Object.keys(data.versions).length,
|
|
105
|
+
},
|
|
106
|
+
'[verdaccio-offline-storage/readPackage/readdir] Removed @{count} versions for package: @{packageName}'
|
|
107
|
+
);
|
|
108
|
+
data['dist-tags'].latest = Object.keys(data.versions).sort((a, b) => cmp(b, a))[0];
|
|
109
|
+
this.logger.trace(
|
|
110
|
+
{
|
|
111
|
+
packageName: name,
|
|
112
|
+
latest: data['dist-tags'].latest,
|
|
113
|
+
},
|
|
114
|
+
'[verdaccio-offline-storage/readPackage/readdir] Set latest version to @{latest} for package: @{packageName}'
|
|
115
|
+
);
|
|
116
|
+
cb(null, data);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { readdir } from 'fs';
|
|
3
|
+
import LocalDatabase from '@verdaccio/local-storage';
|
|
4
|
+
import OfflinePackageStorage from './OfflinePackageStorage';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Verdaccio storage plugin (`IPluginStorage`) that provides only the locally available versions of
|
|
8
|
+
* packages cached in a local-storage storage.
|
|
9
|
+
*
|
|
10
|
+
* Basically, this is just like local-storage but modifying on the fly the available packages list
|
|
11
|
+
* and the packages definitions without altering the original files in the local-storage storage.
|
|
12
|
+
*
|
|
13
|
+
* @see https://verdaccio.org/docs/en/plugin-storage
|
|
14
|
+
* @see https://github.com/verdaccio/monorepo/tree/master/plugins/local-storage
|
|
15
|
+
*/
|
|
16
|
+
export default class OfflineStoragePlugin extends LocalDatabase {
|
|
17
|
+
constructor(config, options) {
|
|
18
|
+
super(config, options.logger);
|
|
19
|
+
// eslint-disable-next-line no-console
|
|
20
|
+
if (config.offline) {
|
|
21
|
+
options.logger.warn({}, 'Offline mode set explicitly in config. All packages will be resolved in offline mode.');
|
|
22
|
+
} else {
|
|
23
|
+
options.logger.warn(
|
|
24
|
+
{},
|
|
25
|
+
'Offline mode NOT set explicitly in config. Only packages with no `proxy` will be resolved in offline mode.'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves all the locally available packages names. Packages with no cached versions (only
|
|
32
|
+
* `package.json` file in the directory) are ignored.
|
|
33
|
+
*
|
|
34
|
+
* @param callback: The callback to invoke with the found packages names.
|
|
35
|
+
* @see https://verdaccio.org/docs/en/plugin-storage#api
|
|
36
|
+
*/
|
|
37
|
+
get(callback) {
|
|
38
|
+
const packages = [];
|
|
39
|
+
this.search(
|
|
40
|
+
(item, cb) => {
|
|
41
|
+
this.logger.debug(
|
|
42
|
+
{
|
|
43
|
+
packageName: item.name,
|
|
44
|
+
},
|
|
45
|
+
'[verdaccio-offline-storage/get/search] discovering local versions for package: @{packageName}'
|
|
46
|
+
);
|
|
47
|
+
readdir(item.path, (err, items) => {
|
|
48
|
+
if (err) {
|
|
49
|
+
this.logger.trace(
|
|
50
|
+
{
|
|
51
|
+
err,
|
|
52
|
+
packageName: item.name,
|
|
53
|
+
},
|
|
54
|
+
'[verdaccio-offline-storage/get/search/readdir] error discovering package "@{packageName}" files: @{err}'
|
|
55
|
+
);
|
|
56
|
+
cb(err);
|
|
57
|
+
} else {
|
|
58
|
+
if (items.find(item => item.endsWith('.tgz'))) {
|
|
59
|
+
packages.push(item.name);
|
|
60
|
+
this.logger.trace(
|
|
61
|
+
{
|
|
62
|
+
packageName: item.name,
|
|
63
|
+
},
|
|
64
|
+
'[verdaccio-offline-storage/get/search/readdir] found locally available package: "@{packageName}"'
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
this.logger.trace(
|
|
68
|
+
{
|
|
69
|
+
packageName: item.name,
|
|
70
|
+
},
|
|
71
|
+
'[verdaccio-offline-storage/get/search/readdir] no locally available version found for package: "@{packageName}"'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
cb();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
() => {
|
|
79
|
+
this.data.list = packages;
|
|
80
|
+
callback(null, packages);
|
|
81
|
+
this.logger.trace(
|
|
82
|
+
{
|
|
83
|
+
totalItems: packages.length,
|
|
84
|
+
},
|
|
85
|
+
'verdaccio-offline-storage: [get] full list of packages (@{totalItems}) has been fetched'
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
name => !name.startsWith('.') // so `.{sinopia|verdaccio}-db.json` gets ignored
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns the `IPackageStorage` used internally for packages I/O operations.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} packageName Package name.
|
|
96
|
+
* @return {OfflinePackageStorage}
|
|
97
|
+
* @see https://verdaccio.org/docs/en/plugin-storage#api
|
|
98
|
+
*/
|
|
99
|
+
getPackageStorage(packageName) {
|
|
100
|
+
return new OfflinePackageStorage(join(this.config.storage, packageName), this.logger, this.config);
|
|
101
|
+
}
|
|
102
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"outDir": "./build",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"moduleResolution": "node"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "build"]
|
|
20
|
+
}
|