@vltpkg/vsr 0.2.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/.editorconfig +13 -0
- package/.prettierrc +7 -0
- package/CONTRIBUTING.md +228 -0
- package/LICENSE.md +110 -0
- package/README.md +373 -0
- package/bin/vsr.ts +29 -0
- package/config.ts +124 -0
- package/debug-npm.js +19 -0
- package/drizzle.config.js +33 -0
- package/package.json +80 -0
- package/pnpm-workspace.yaml +5 -0
- package/src/api.ts +2246 -0
- package/src/assets/public/images/bg.png +0 -0
- package/src/assets/public/images/clients/logo-bun.png +0 -0
- package/src/assets/public/images/clients/logo-deno.png +0 -0
- package/src/assets/public/images/clients/logo-npm.png +0 -0
- package/src/assets/public/images/clients/logo-pnpm.png +0 -0
- package/src/assets/public/images/clients/logo-vlt.png +0 -0
- package/src/assets/public/images/clients/logo-yarn.png +0 -0
- package/src/assets/public/images/favicon/apple-touch-icon.png +0 -0
- package/src/assets/public/images/favicon/favicon-96x96.png +0 -0
- package/src/assets/public/images/favicon/favicon.ico +0 -0
- package/src/assets/public/images/favicon/favicon.svg +3 -0
- package/src/assets/public/images/favicon/site.webmanifest +21 -0
- package/src/assets/public/images/favicon/web-app-manifest-192x192.png +0 -0
- package/src/assets/public/images/favicon/web-app-manifest-512x512.png +0 -0
- package/src/assets/public/styles/styles.css +219 -0
- package/src/db/client.ts +544 -0
- package/src/db/migrations/0000_faulty_ricochet.sql +14 -0
- package/src/db/migrations/0000_initial.sql +29 -0
- package/src/db/migrations/0001_uuid_validation.sql +35 -0
- package/src/db/migrations/0001_wealthy_magdalene.sql +7 -0
- package/src/db/migrations/drop.sql +3 -0
- package/src/db/migrations/meta/0000_snapshot.json +104 -0
- package/src/db/migrations/meta/0001_snapshot.json +155 -0
- package/src/db/migrations/meta/_journal.json +20 -0
- package/src/db/schema.ts +41 -0
- package/src/index.ts +709 -0
- package/src/routes/access.ts +263 -0
- package/src/routes/auth.ts +93 -0
- package/src/routes/index.ts +135 -0
- package/src/routes/packages.ts +924 -0
- package/src/routes/search.ts +50 -0
- package/src/routes/static.ts +53 -0
- package/src/routes/tokens.ts +102 -0
- package/src/routes/users.ts +14 -0
- package/src/utils/auth.ts +145 -0
- package/src/utils/cache.ts +466 -0
- package/src/utils/database.ts +44 -0
- package/src/utils/packages.ts +337 -0
- package/src/utils/response.ts +100 -0
- package/src/utils/routes.ts +47 -0
- package/src/utils/spa.ts +14 -0
- package/src/utils/tracing.ts +63 -0
- package/src/utils/upstream.ts +131 -0
- package/test/README.md +91 -0
- package/test/access.test.js +760 -0
- package/test/cloudflare-waituntil.test.js +141 -0
- package/test/db.test.js +447 -0
- package/test/dist-tag.test.js +415 -0
- package/test/e2e.test.js +904 -0
- package/test/hono-context.test.js +250 -0
- package/test/integrity-validation.test.js +183 -0
- package/test/json-response.test.js +76 -0
- package/test/manifest-slimming.test.js +449 -0
- package/test/packument-consistency.test.js +351 -0
- package/test/packument-version-range.test.js +144 -0
- package/test/performance.test.js +162 -0
- package/test/route-with-waituntil.test.js +298 -0
- package/test/run-tests.js +151 -0
- package/test/setup-cache-tests.js +190 -0
- package/test/setup.js +64 -0
- package/test/stale-while-revalidate.test.js +273 -0
- package/test/static-assets.test.js +85 -0
- package/test/upstream-routing.test.js +86 -0
- package/test/utils/test-helpers.js +84 -0
- package/test/waituntil-correct.test.js +208 -0
- package/test/waituntil-demo.test.js +138 -0
- package/test/waituntil-readme.md +113 -0
- package/tsconfig.json +37 -0
- package/types.ts +446 -0
- package/vitest.config.js +95 -0
- package/wrangler.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# **vlt** serverless registry (`vsr`)
|
|
2
|
+
|
|
3
|
+
`vsr` aims to be a minimal "npm-compatible" registry which replicates the core features found in `registry.npmjs.org` as well as adding net-new capabilities.
|
|
4
|
+
|
|
5
|
+
<img src="https://github.com/user-attachments/assets/e76c6f8a-a078-4787-963c-8ec95a879731" alt="vsr api screenshot" />
|
|
6
|
+
|
|
7
|
+
**Table of Contents:**
|
|
8
|
+
|
|
9
|
+
- [Quick Starts](#quick-starts)
|
|
10
|
+
- [Requirements](#requirements)
|
|
11
|
+
- [API](#api)
|
|
12
|
+
- [Compatibility](#compatibility)
|
|
13
|
+
- [Comparisons](#comparisons)
|
|
14
|
+
- [Roadmap](#roadmap)
|
|
15
|
+
- [License](#license)
|
|
16
|
+
- [Testing](#testing)
|
|
17
|
+
|
|
18
|
+
### Quick Starts
|
|
19
|
+
|
|
20
|
+
#### Local
|
|
21
|
+
|
|
22
|
+
You can quickly get started by installing/executing `vsr` with the following command:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
vlx -y @vltpkg/vsr
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
#### Production
|
|
29
|
+
|
|
30
|
+
You can deploy `vsr` to [Cloudflare](https://www.cloudflare.com/) in under 5 minutes, for free, with a single click (coming soon).
|
|
31
|
+
|
|
32
|
+
<img src="https://github.com/user-attachments/assets/528deda2-4c20-44c9-b057-f07c2e2e3c71" alt="Deply to Cloudflare Workers" width="200" />
|
|
33
|
+
|
|
34
|
+
<!-- [](https://deploy.workers.cloudflare.com/?url=https://github.com/vltpkg/vsr) -->
|
|
35
|
+
|
|
36
|
+
Alternatively, you can deploy to production using [`wrangler`](https://www.npmjs.com/package/wrangler) after following the **Development** quick start steps.
|
|
37
|
+
|
|
38
|
+
#### Development
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# clone the repo
|
|
42
|
+
git clone https://github.com/vltpkg/vsr.git
|
|
43
|
+
|
|
44
|
+
# navigate to the repository directory
|
|
45
|
+
cd ./vsr
|
|
46
|
+
|
|
47
|
+
# install the project's dependencies
|
|
48
|
+
vlt install
|
|
49
|
+
|
|
50
|
+
# run the development script
|
|
51
|
+
vlr dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For detailed information on development workflow, testing, and contributing to the project, please see our [CONTRIBUTING.md](CONTRIBUTING.md) guide.
|
|
55
|
+
|
|
56
|
+
### Requirements
|
|
57
|
+
|
|
58
|
+
#### Production
|
|
59
|
+
|
|
60
|
+
- [Cloudflare (free account at minimum)](https://www.cloudflare.com/en-ca/plans/developer-platform/)
|
|
61
|
+
- Workers (free: 100k requests /day)
|
|
62
|
+
- D1 Database (free: 100k writes, 5M reads /day & 5GB Storage /mo)
|
|
63
|
+
- R2 Bucket (free: 1M writes, 10M reads & 10GB /mo)
|
|
64
|
+
|
|
65
|
+
> Note: all usage numbers & pricing documented is as of **October 24th, 2024**. Plans & metering is subject to change at Cloudflare's discretion.
|
|
66
|
+
|
|
67
|
+
#### Development
|
|
68
|
+
|
|
69
|
+
- `git`
|
|
70
|
+
- `node`
|
|
71
|
+
- `vlt`
|
|
72
|
+
|
|
73
|
+
### Granular Access Tokens
|
|
74
|
+
|
|
75
|
+
All tokens are considered "granular access tokens" (GATs). Token entries in the database consist of 3 parts:
|
|
76
|
+
|
|
77
|
+
- `token` the unique token value
|
|
78
|
+
- `uuid` associative value representing a single user/scope
|
|
79
|
+
- `scope` value representing the granular access/privileges
|
|
80
|
+
|
|
81
|
+
#### `scope` as a JSON `Object`
|
|
82
|
+
|
|
83
|
+
A `scope` contains an array of privileges that define both the type(s) of & access value(s) for a token.
|
|
84
|
+
|
|
85
|
+
> [!NOTE]
|
|
86
|
+
> Tokens can be associated with multiple "types" of access
|
|
87
|
+
|
|
88
|
+
- `type(s)`:
|
|
89
|
+
- `pkg:read` read associated packages
|
|
90
|
+
- `pkg:read+write` write associated packages (requires read access)
|
|
91
|
+
- `user:read` read associated user
|
|
92
|
+
- `user:read+write` write associated user (requires read access)
|
|
93
|
+
- `value(s)`:
|
|
94
|
+
- `*` an ANY selector for `user:` or `pkg:` access types
|
|
95
|
+
- `~<user>` user selector for the `user:` access type
|
|
96
|
+
- `@<scope>/<pkg>` package specific selector for the `pkg:` access type
|
|
97
|
+
- `@<scope>/*` glob scope selector for `pkg:` access types
|
|
98
|
+
|
|
99
|
+
> [!NOTE]
|
|
100
|
+
> - user/org/team management via `@<scope>` is not supported at the moment
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
|
|
104
|
+
<summary><h4>Granular Access Examples</h4></summary>
|
|
105
|
+
|
|
106
|
+
##### End-user/Subscriber Persona
|
|
107
|
+
|
|
108
|
+
- specific package read access
|
|
109
|
+
- individual user read+write access
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
[
|
|
113
|
+
{
|
|
114
|
+
"values": ["@organization/package-name"],
|
|
115
|
+
"types": {
|
|
116
|
+
"pkg": {
|
|
117
|
+
"read": true,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"values": ["~johnsmith"],
|
|
123
|
+
"types": {
|
|
124
|
+
"user": {
|
|
125
|
+
"read": true,
|
|
126
|
+
"write": true,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
##### Team Member/Maintainer Persona
|
|
134
|
+
|
|
135
|
+
- scoped package read+write access
|
|
136
|
+
- individual user read+write access
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
[
|
|
140
|
+
{
|
|
141
|
+
"values": ["@organization/*"],
|
|
142
|
+
"types": {
|
|
143
|
+
"pkg": {
|
|
144
|
+
"read": true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"values": ["~johnsmith"],
|
|
150
|
+
"types": {
|
|
151
|
+
"user": {
|
|
152
|
+
"read": true,
|
|
153
|
+
"write": true,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
##### Package Publish CI Persona
|
|
161
|
+
|
|
162
|
+
- organization scoped packages read+write access
|
|
163
|
+
- individual user read+write access
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
[
|
|
167
|
+
{
|
|
168
|
+
"values": ["@organization/package-name"],
|
|
169
|
+
"types": {
|
|
170
|
+
"pkg": {
|
|
171
|
+
"read": true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"values": ["~johnsmith"],
|
|
177
|
+
"types": {
|
|
178
|
+
"user": {
|
|
179
|
+
"read": true,
|
|
180
|
+
"write": true,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
##### Organization Admin Persona
|
|
188
|
+
|
|
189
|
+
- organization scoped package read+write access
|
|
190
|
+
- organization users read+write access
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
[
|
|
194
|
+
{
|
|
195
|
+
"values": ["@company/*"],
|
|
196
|
+
"types": {
|
|
197
|
+
"pkg": {
|
|
198
|
+
"read": true,
|
|
199
|
+
"write": true
|
|
200
|
+
},
|
|
201
|
+
"user": {
|
|
202
|
+
"read": true,
|
|
203
|
+
"write": true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
##### Registry Owner/Admin Persona
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
[
|
|
214
|
+
{
|
|
215
|
+
"values": ["*"],
|
|
216
|
+
"types": {
|
|
217
|
+
"pkg": {
|
|
218
|
+
"read": true,
|
|
219
|
+
"write": true
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"user": {
|
|
223
|
+
"read": true,
|
|
224
|
+
"write": true
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
</details>
|
|
233
|
+
|
|
234
|
+
### API
|
|
235
|
+
|
|
236
|
+
We have rich, interactive API docs out-of-the-box with the help of our friends [Scalar](https://scalar.com/). The docs can be found at the registry root when running `vsr` locally (ex. run `vlx -y @vltpkg/vsr` &/or check out this repo & run `vlr dev`)
|
|
237
|
+
|
|
238
|
+
Notable API features include:
|
|
239
|
+
- Complete npm-compatible registry API
|
|
240
|
+
- Semver range resolution for package version requests
|
|
241
|
+
- Support for URL-encoded complex semver ranges (e.g., `%3E%3D1.0.0%20%3C2.0.0` for `>=1.0.0 <2.0.0`)
|
|
242
|
+
- Background refresh of stale package data
|
|
243
|
+
- Minimal JSON responses for faster installs
|
|
244
|
+
|
|
245
|
+
### `npm` Client Compatibility
|
|
246
|
+
|
|
247
|
+
The following commands should work out-of-the-box with `npm` & any other `npm` "compatible" clients although their specific commands & arguments may vary (ex. `vlt`, `yarn`, `pnpm` & `bun`)
|
|
248
|
+
|
|
249
|
+
#### Configuration
|
|
250
|
+
|
|
251
|
+
##### For `vlt`:
|
|
252
|
+
|
|
253
|
+
...
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
##### For `npm`, `pnpm`, `yarn` & `bun`:
|
|
257
|
+
|
|
258
|
+
To use `vsr` as your registry you must either pass a registry config through a client-specific flag (ex. `--registry=...` for `npm`) or define client-specific configuration which stores the reference to your registry (ex. `.npmrc` for `npm`). Access to the registry & packages is private by default although an `"admin"` user is created during setup locally (for development purposes) with a default auth token of `"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"`.
|
|
259
|
+
|
|
260
|
+
```ini
|
|
261
|
+
; .npmrc
|
|
262
|
+
registry=http://localhost:1337
|
|
263
|
+
//localhost:1337/:_authToken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
<h4>Supported `npm` Client Commands</h4>
|
|
267
|
+
|
|
268
|
+
| Support | Commannd |
|
|
269
|
+
| :--: | :-- |
|
|
270
|
+
| ✅ | `access` |
|
|
271
|
+
| ✅ | `access list packages` |
|
|
272
|
+
| ✅ | `access get status` |
|
|
273
|
+
| ✅ | `access set status` |
|
|
274
|
+
| 🕤 | `access set mfa` |
|
|
275
|
+
| ✅ | `access grant` |
|
|
276
|
+
| ✅ | `access revoke` |
|
|
277
|
+
| 🕤 | `adduser` - `PUT /-/org/@<org>/<user>`: Adds/updates a user (requires admin privileges) |
|
|
278
|
+
| ⏳ | `audit` |
|
|
279
|
+
| ✅ | `bugs` |
|
|
280
|
+
| ✅ | `dist-tag add` |
|
|
281
|
+
| ✅ | `dist-tag rm` |
|
|
282
|
+
| ✅ | `dist-tag ls` |
|
|
283
|
+
| ✅ | `deprecate` |
|
|
284
|
+
| ✅ | `docs` |
|
|
285
|
+
| ✅ | `exec` |
|
|
286
|
+
| ✅ | `install` |
|
|
287
|
+
| ⏳ | `login` |
|
|
288
|
+
| ⏳ | `logout` |
|
|
289
|
+
| 🕤 | `org` |
|
|
290
|
+
| ✅ | `outdated` |
|
|
291
|
+
| 🕤 | `owner add` |
|
|
292
|
+
| 🕤 | `owner rm` |
|
|
293
|
+
| 🕤 | `owner ls` |
|
|
294
|
+
| ✅ | `ping` |
|
|
295
|
+
| 🕤 | `profile enable-2fa` |
|
|
296
|
+
| 🕤 | `profile disable-2fa` |
|
|
297
|
+
| ✅ | `profile get` |
|
|
298
|
+
| 🕤 | `profile set` - `PUT /-/npm/v1/user`: Updates a user (requires auth) |
|
|
299
|
+
| ✅ | `publish` |
|
|
300
|
+
| ✅ | `repo` |
|
|
301
|
+
| ✅ | `search` |
|
|
302
|
+
| 🕤 | `team` |
|
|
303
|
+
| ✅ | `view` |
|
|
304
|
+
| ✅ | `whoami` |
|
|
305
|
+
|
|
306
|
+
### Registry Comparisons
|
|
307
|
+
|
|
308
|
+
| Feature | **vsr** | **npm** | **GitHub** | **Verdaccio** | **JSR** | **jFrog** | **Sonatype** | **Cloudsmith** | **Buildkite** | **Bit** |
|
|
309
|
+
| -- | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
|
310
|
+
| License | `FSL-1.1-MIT` | `Closed Source` | `Closed Source` | `MIT` | `MIT` | `Closed Source` | `Closed Source` | `Closed Source` | `Closed Source` | `Closed Source` |
|
|
311
|
+
| Authored Language | `JavaScript` | `JavaScript` | `Ruby`/`Go` | `TypeScript` | `Rust` | `-` | `-` | `-` | `-` | `-` |
|
|
312
|
+
| Publishing | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
313
|
+
| Installation | ✅ | ✅ | ✅ | ✅ | ✴️ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
314
|
+
| Search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
315
|
+
| Scoped Packages | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
316
|
+
| Unscoped Packages | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
317
|
+
| Proxying Upstream Sources | ✅ | ❌ | ✴️ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
318
|
+
| Hosted Instance | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
319
|
+
| Hosted Instance Cost | `$` | `-` | `$$$$` | `-` | `-` | `$$$$` | `$$$$` | `$$$$` | `$$$` | `$$$` |
|
|
320
|
+
| Self-Hosted Instance | ✅ | ❌ | ✴️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
321
|
+
| Self-Hosted Instance Cost | 🆓 | `-` | `$$$$$` | `$` | `$` | `$$$$$` | `$$$$$` | `-` | `-` | `-` |
|
|
322
|
+
| Hosted Public Packages | ⏳ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
323
|
+
| Hosted Private Packages | 🕤 | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
324
|
+
| Hosted Private Package Cost | `-` | `$$` | 🆓 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🆓 |
|
|
325
|
+
| Granular Access/Permissions | ✅ | ✴️ | ❌ | ✅ | ❌ | ✴️ | ✴️ | ✴️ | ✴️ | ❌ |
|
|
326
|
+
| Manifest Validation | ✅ | ✴️ | ❌ | ❌ | ✴️ | ✴️ | ✴️ | ❌ | ❌ | ❌ |
|
|
327
|
+
| Audit | 🕤 | ✴️ | ❌ | ✴️ | ✴️ | ✴️ | ✴️ | ✴️ | ❌ | ❌ |
|
|
328
|
+
| Events/Hooks | 🕤 | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
329
|
+
| Plugins | 🕤 | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
330
|
+
| Multi-Cloud | 🕤 | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
331
|
+
| Documentation Generation | 🕤 | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✴️ |
|
|
332
|
+
| Unpackaged Files/ESM Imports | 🕤 | ❌ | ❌ | ❌ | ✴️ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
333
|
+
| Variant Support | 🕤 | ❌ | ❌ | ❌ | ✴️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
334
|
+
|
|
335
|
+
#### Legend:
|
|
336
|
+
- ✅ implemented
|
|
337
|
+
- ✴️ supported with caveats
|
|
338
|
+
- ⏳ in-progress
|
|
339
|
+
- 🕤 planned
|
|
340
|
+
- ❌ unsupported
|
|
341
|
+
- `$` expense estimation (0-5)
|
|
342
|
+
|
|
343
|
+
### Roadmap
|
|
344
|
+
|
|
345
|
+
#### v1.0.0
|
|
346
|
+
|
|
347
|
+
| Status | Feature |
|
|
348
|
+
| :--: | :-- |
|
|
349
|
+
| ⏳ | web: user login (ex. `npm login` / `--auth-type=web`) |
|
|
350
|
+
| ⏳ | web: user account management (`hosted`) |
|
|
351
|
+
| ⏳ | web: user registration (`hosted`) |
|
|
352
|
+
| ⏳ | web: admin user management (`hosted`) |
|
|
353
|
+
| ⏳ | web: package pages |
|
|
354
|
+
| ⏳ | web: search |
|
|
355
|
+
| ⏳ | api: rate-limiting |
|
|
356
|
+
|
|
357
|
+
#### v1.x
|
|
358
|
+
|
|
359
|
+
| Status | Feature |
|
|
360
|
+
| :--: | :-- |
|
|
361
|
+
| 🕤 | api: package insights (powered by socket) |
|
|
362
|
+
| 🕤 | api: audit (powered by socket) |
|
|
363
|
+
| 🕤 | mfa access provisioning |
|
|
364
|
+
| 🕤 | orgs |
|
|
365
|
+
| 🕤 | teams |
|
|
366
|
+
| 🕤 | staging |
|
|
367
|
+
| 🕤 | events/hooks |
|
|
368
|
+
| 🕤 | plugins/middleware |
|
|
369
|
+
| 🕤 | variants/distributions |
|
|
370
|
+
|
|
371
|
+
### License
|
|
372
|
+
|
|
373
|
+
This project is licensed under the [Functional Source License](https://fsl.software) ([**FSL-1.1-MIT**](LICENSE.md)).
|
package/bin/vsr.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node --no-warnings --experimental-strip-types
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { execSync } from 'node:child_process'
|
|
5
|
+
import { PathScurry } from 'path-scurry'
|
|
6
|
+
import { PackageJson } from '@vltpkg/package-json'
|
|
7
|
+
import { createServer } from '@vltpkg/server'
|
|
8
|
+
import { SecurityArchive } from '@vltpkg/security-archive'
|
|
9
|
+
import { DAEMON_PORT } from '../config.ts'
|
|
10
|
+
import packageJson from '../package.json' with { type: 'json' }
|
|
11
|
+
|
|
12
|
+
const __filename: string = fileURLToPath(import.meta.url)
|
|
13
|
+
const __dirname: string = path.dirname(__filename)
|
|
14
|
+
const cwd: string = path.resolve(__dirname, '../')
|
|
15
|
+
|
|
16
|
+
const server = createServer({
|
|
17
|
+
scurry: new PathScurry(cwd),
|
|
18
|
+
projectRoot: cwd,
|
|
19
|
+
packageJson: new PackageJson(),
|
|
20
|
+
securityArchive: new SecurityArchive(),
|
|
21
|
+
port: DAEMON_PORT
|
|
22
|
+
} as any)
|
|
23
|
+
|
|
24
|
+
await server.start({
|
|
25
|
+
port: DAEMON_PORT
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
console.log(`Listening on ${server.address()}`)
|
|
29
|
+
execSync(packageJson.scripts['serve:dev'], { cwd, stdio: 'inherit' })
|
package/config.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// get openapi schema
|
|
2
|
+
import { API } from './src/api.ts'
|
|
3
|
+
import wranglerJson from './wrangler.json' with { type: 'json' }
|
|
4
|
+
import packageJson from './package.json' with { type: 'json' }
|
|
5
|
+
import type { OriginConfig, CookieOptions, ApiDocsConfig } from './types.ts'
|
|
6
|
+
|
|
7
|
+
export const DEV_CONFIG = wranglerJson.dev
|
|
8
|
+
|
|
9
|
+
export const DAEMON_PORT: number = 3000
|
|
10
|
+
|
|
11
|
+
export const VERSION: string = packageJson.version
|
|
12
|
+
|
|
13
|
+
// how to handle packages requests
|
|
14
|
+
export const ORIGIN_CONFIG: OriginConfig = {
|
|
15
|
+
default: 'local',
|
|
16
|
+
upstreams: {
|
|
17
|
+
local: {
|
|
18
|
+
type: 'local',
|
|
19
|
+
url: 'http://localhost:8787',
|
|
20
|
+
allowPublish: true
|
|
21
|
+
},
|
|
22
|
+
vsr: {
|
|
23
|
+
type: 'vsr',
|
|
24
|
+
url: 'https://vsr.io'
|
|
25
|
+
},
|
|
26
|
+
npm: {
|
|
27
|
+
type: 'npm',
|
|
28
|
+
url: 'https://registry.npmjs.org'
|
|
29
|
+
},
|
|
30
|
+
jsr: {
|
|
31
|
+
type: 'jsr',
|
|
32
|
+
url: 'https://jsr.io'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Reserved route prefixes that cannot be used as upstream names
|
|
38
|
+
export const RESERVED_ROUTES: string[] = [
|
|
39
|
+
'-',
|
|
40
|
+
'user',
|
|
41
|
+
'docs',
|
|
42
|
+
'search',
|
|
43
|
+
'tokens',
|
|
44
|
+
'auth',
|
|
45
|
+
'ping',
|
|
46
|
+
'package',
|
|
47
|
+
'v1',
|
|
48
|
+
'api',
|
|
49
|
+
'admin',
|
|
50
|
+
'*' // Reserved for hash-based routes
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
// Backward compatibility - maintain old PROXY behavior
|
|
54
|
+
export const PROXY: boolean = Object.keys(ORIGIN_CONFIG.upstreams).length > 1
|
|
55
|
+
export const PROXY_URL: string | undefined = ORIGIN_CONFIG.upstreams[ORIGIN_CONFIG.default]?.url
|
|
56
|
+
|
|
57
|
+
// exposes a publically accessible docs endpoint
|
|
58
|
+
export const EXPOSE_DOCS: boolean = true
|
|
59
|
+
|
|
60
|
+
// the domain the registry is hosted on
|
|
61
|
+
export const DOMAIN: string = `http://localhost:${DEV_CONFIG.port}`
|
|
62
|
+
export const REDIRECT_URI: string = `${DOMAIN}/-/auth/callback`
|
|
63
|
+
|
|
64
|
+
// the time in seconds to cache the registry
|
|
65
|
+
export const REQUEST_TIMEOUT: number = 60 * 1000
|
|
66
|
+
|
|
67
|
+
// cookie options
|
|
68
|
+
export const COOKIE_OPTIONS: CookieOptions = {
|
|
69
|
+
path: '/',
|
|
70
|
+
httpOnly: true,
|
|
71
|
+
secure: true,
|
|
72
|
+
sameSite: 'strict',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// the docs configuration for the API reference
|
|
76
|
+
export const API_DOCS: ApiDocsConfig = {
|
|
77
|
+
metaData: {
|
|
78
|
+
title: 'vlt serverless registry'
|
|
79
|
+
},
|
|
80
|
+
hideModels: false,
|
|
81
|
+
hideDownloadButton: false,
|
|
82
|
+
darkMode: false,
|
|
83
|
+
favicon: '/public/images/favicon/favicon.svg',
|
|
84
|
+
defaultHttpClient: {
|
|
85
|
+
targetKey: 'curl',
|
|
86
|
+
clientKey: 'fetch',
|
|
87
|
+
},
|
|
88
|
+
authentication: {
|
|
89
|
+
http: {
|
|
90
|
+
bearer: {
|
|
91
|
+
token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
|
92
|
+
},
|
|
93
|
+
basic: {
|
|
94
|
+
username: 'user',
|
|
95
|
+
password: 'pass'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
hiddenClients: {
|
|
100
|
+
python: true,
|
|
101
|
+
c: true,
|
|
102
|
+
go: true,
|
|
103
|
+
java: true,
|
|
104
|
+
ruby: true,
|
|
105
|
+
shell: ['httpie', 'wget', 'fetch'],
|
|
106
|
+
clojure: true,
|
|
107
|
+
csharp: true,
|
|
108
|
+
kotlin: true,
|
|
109
|
+
objc: true,
|
|
110
|
+
swift: true,
|
|
111
|
+
r: true,
|
|
112
|
+
powershell: false,
|
|
113
|
+
ocaml: true,
|
|
114
|
+
curl: false,
|
|
115
|
+
http: true,
|
|
116
|
+
php: true,
|
|
117
|
+
node: ['request', 'unirest'],
|
|
118
|
+
javascript: ['xhr', 'jquery']
|
|
119
|
+
},
|
|
120
|
+
spec: {
|
|
121
|
+
content: API
|
|
122
|
+
},
|
|
123
|
+
customCss: `@import '${DOMAIN}/public/styles/styles.css';`
|
|
124
|
+
}
|
package/debug-npm.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
console.log('Starting wrangler dev in background...');
|
|
5
|
+
const child = execSync('npx wrangler dev --port 1337 &', { stdio: 'pipe' });
|
|
6
|
+
|
|
7
|
+
// Wait for server to start
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
console.log('Testing npm proxy...');
|
|
10
|
+
try {
|
|
11
|
+
const result = execSync('curl -v http://localhost:1337/npm/sleepover', { encoding: 'utf8' });
|
|
12
|
+
console.log('Result:', result);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.log('Error:', error.stdout);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Kill wrangler
|
|
18
|
+
execSync('pkill -f wrangler');
|
|
19
|
+
}, 5000);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { existsSync, readdirSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
// Find the actual SQLite database file in the miniflare-D1DatabaseObject directory
|
|
6
|
+
let dbPath = './local.db' // Fallback
|
|
7
|
+
const miniflareDir = join('./local-store', 'v3', 'd1', 'miniflare-D1DatabaseObject')
|
|
8
|
+
|
|
9
|
+
if (existsSync(miniflareDir)) {
|
|
10
|
+
// Look for the most recently modified SQLite file
|
|
11
|
+
const files = readdirSync(miniflareDir)
|
|
12
|
+
.filter(file => file.endsWith('.sqlite'))
|
|
13
|
+
.map(file => {
|
|
14
|
+
const fullPath = join(miniflareDir, file);
|
|
15
|
+
return { file, fullPath };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (files.length > 0) {
|
|
19
|
+
// Just use the first file if there's only one
|
|
20
|
+
dbPath = files[0].fullPath;
|
|
21
|
+
console.log('Using D1 database at:', dbPath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// For Drizzle Kit
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
schema: './src/db/schema.ts',
|
|
28
|
+
out: './src/db/migrations',
|
|
29
|
+
dialect: 'sqlite',
|
|
30
|
+
dbCredentials: {
|
|
31
|
+
url: dbPath
|
|
32
|
+
}
|
|
33
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vltpkg/vsr",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"license": "FSL-1.1-MIT",
|
|
5
|
+
"author": "vlt technology inc. <support@vlt.sh> (http://vlt.sh)",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/vltpkg/vltpkg.git",
|
|
10
|
+
"directory": "src/vsr"
|
|
11
|
+
},
|
|
12
|
+
"bin": "./bin/vsr.ts",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"db:push": "drizzle-kit push",
|
|
15
|
+
"db:generate": "drizzle-kit generate",
|
|
16
|
+
"db:migrate": "drizzle-kit migrate",
|
|
17
|
+
"db:studio": "drizzle-kit studio --port 4985",
|
|
18
|
+
"db:setup": "wrangler d1 execute vsr-local-database --file=src/db/migrations/0000_initial.sql --local --persist-to=local-store --no-remote",
|
|
19
|
+
"db:drop": "wrangler d1 execute vsr-local-database --file=src/db/migrations/drop.sql --local --persist-to=local-store --no-remote && rm -rf local-store && rm -rf .wrangler",
|
|
20
|
+
"serve:build": "npm run build:assets && wrangler dev ./dist/index.js --local --persist-to=local-store --no-remote",
|
|
21
|
+
"serve:dev": "pnpm run build:assets && wrangler dev --local --persist-to=local-store --no-remote",
|
|
22
|
+
"serve:death": "echo \"Killing wrangler dev processes...\" && (pkill -f 'wrangler.*dev' || true) && sleep 1 && (pids=$(lsof -ti :1337 2>/dev/null; lsof -ti :3000 2>/dev/null) && [ -n \"$pids\" ] && echo \"Force killing remaining processes: $pids\" && kill -9 $pids || echo \"No remaining processes found\") && echo \"Done.\"",
|
|
23
|
+
"build:assets": "pnpm install && cp -r ./node_modules/@vltpkg/gui/dist ./src/assets/public",
|
|
24
|
+
"build": "pnpm run build:assets && wrangler deploy --dry-run --outdir dist",
|
|
25
|
+
"deploy": "pnpm run build && wrangler deploy",
|
|
26
|
+
"lint": "prettier .",
|
|
27
|
+
"test": "pnpm run test:cleanup && pnpm run test:setup && pnpm run test:run",
|
|
28
|
+
"test:run": "vitest run",
|
|
29
|
+
"test:setup": "pnpm run db:setup",
|
|
30
|
+
"test:cleanup": "pkill -f wrangler || true",
|
|
31
|
+
"pretest:run": "wrangler dev --local --persist-to=local-store --no-remote",
|
|
32
|
+
"posttest:run": "pkill -f wrangler || true",
|
|
33
|
+
"test:cache:clean": "rm -f test/cache-refresh.test.js test/background-refresh.test.js"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@cloudflare/workers-types": "^4.20240320.0",
|
|
37
|
+
"@hono-rate-limiter/cloudflare": "^0.2.1",
|
|
38
|
+
"@libsql/client": "^0.15.1",
|
|
39
|
+
"@scalar/hono-api-reference": "^0.5.158",
|
|
40
|
+
"@twind/core": "^1.1.3",
|
|
41
|
+
"@twind/preset-autoprefix": "^1.0.7",
|
|
42
|
+
"@twind/preset-tailwind": "^1.1.4",
|
|
43
|
+
"@vltpkg/gui": "^0.0.0-13",
|
|
44
|
+
"@vltpkg/server": "^0.0.0-13",
|
|
45
|
+
"@workos-inc/node": "^7.48.0",
|
|
46
|
+
"autoprefixer": "^10.4.21",
|
|
47
|
+
"better-sqlite3": "^11.9.1",
|
|
48
|
+
"crypto": "npm:webcrypto-core@^1.8.1",
|
|
49
|
+
"drizzle-kit": "^0.30.5",
|
|
50
|
+
"drizzle-orm": "^0.41.0",
|
|
51
|
+
"get-npm-tarball-url": "^2.1.0",
|
|
52
|
+
"hono": "^4.5.5",
|
|
53
|
+
"hono-rate-limiter": "^0.4.0",
|
|
54
|
+
"htmx.org": "^1.9.12",
|
|
55
|
+
"hyperscript.org": "^0.9.14",
|
|
56
|
+
"js-yaml": "^4.1.0",
|
|
57
|
+
"libnpmpack": "^7.0.4",
|
|
58
|
+
"npm-registry-fetch": "^17.1.0",
|
|
59
|
+
"open": "^10.1.0",
|
|
60
|
+
"prettier": "^3.3.3",
|
|
61
|
+
"schemes": "^1.4.0",
|
|
62
|
+
"semver": "^7.6.3",
|
|
63
|
+
"ssri": "^10.0.6",
|
|
64
|
+
"streaming-tarball": "^1.0.3",
|
|
65
|
+
"tailwindcss": "^4.1.4",
|
|
66
|
+
"tsx": "^4.19.3",
|
|
67
|
+
"undici": "^7.5.0",
|
|
68
|
+
"uuid": "^10.0.0",
|
|
69
|
+
"validate-npm-package-name": "5.0.0",
|
|
70
|
+
"vitest": "^1.3.1"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@vltpkg/package-json": "0.0.0-15",
|
|
74
|
+
"@vltpkg/security-archive": "0.0.0-15",
|
|
75
|
+
"path-scurry": "^2.0.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"wrangler": "^4.20.3"
|
|
79
|
+
}
|
|
80
|
+
}
|