@vltpkg/vsr 0.0.0-27 → 0.0.0-29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/DEPLOY.md +163 -0
  2. package/LICENSE +114 -10
  3. package/config.ts +221 -0
  4. package/dist/README.md +1 -1
  5. package/dist/bin/vsr.js +9 -6
  6. package/dist/index.js +4 -6
  7. package/dist/index.js.map +2 -2
  8. package/drizzle.config.js +40 -0
  9. package/info/COMPARISONS.md +37 -0
  10. package/info/CONFIGURATION.md +143 -0
  11. package/info/CONTRIBUTING.md +32 -0
  12. package/info/DATABASE_SETUP.md +108 -0
  13. package/info/GRANULAR_ACCESS_TOKENS.md +160 -0
  14. package/info/PROJECT_STRUCTURE.md +291 -0
  15. package/info/ROADMAP.md +27 -0
  16. package/info/SUPPORT.md +39 -0
  17. package/info/TESTING.md +301 -0
  18. package/info/USER_SUPPORT.md +31 -0
  19. package/package.json +50 -6
  20. package/scripts/build-assets.js +31 -0
  21. package/scripts/build-bin.js +63 -0
  22. package/src/assets/public/images/bg.png +0 -0
  23. package/src/assets/public/images/clients/logo-bun.png +0 -0
  24. package/src/assets/public/images/clients/logo-deno.png +0 -0
  25. package/src/assets/public/images/clients/logo-npm.png +0 -0
  26. package/src/assets/public/images/clients/logo-pnpm.png +0 -0
  27. package/src/assets/public/images/clients/logo-vlt.png +0 -0
  28. package/src/assets/public/images/clients/logo-yarn.png +0 -0
  29. package/src/assets/public/images/favicon/apple-touch-icon.png +0 -0
  30. package/src/assets/public/images/favicon/favicon-96x96.png +0 -0
  31. package/src/assets/public/images/favicon/favicon.ico +0 -0
  32. package/src/assets/public/images/favicon/favicon.svg +3 -0
  33. package/src/assets/public/images/favicon/site.webmanifest +21 -0
  34. package/src/assets/public/images/favicon/web-app-manifest-192x192.png +0 -0
  35. package/src/assets/public/images/favicon/web-app-manifest-512x512.png +0 -0
  36. package/src/assets/public/styles/styles.css +231 -0
  37. package/src/bin/demo/package.json +6 -0
  38. package/src/bin/demo/vlt.json +1 -0
  39. package/src/bin/vsr.ts +496 -0
  40. package/src/db/client.ts +590 -0
  41. package/src/db/migrations/0000_faulty_ricochet.sql +14 -0
  42. package/src/db/migrations/0000_initial.sql +29 -0
  43. package/src/db/migrations/0001_uuid_validation.sql +35 -0
  44. package/src/db/migrations/0001_wealthy_magdalene.sql +7 -0
  45. package/src/db/migrations/drop.sql +3 -0
  46. package/src/db/migrations/meta/0000_snapshot.json +104 -0
  47. package/src/db/migrations/meta/0001_snapshot.json +155 -0
  48. package/src/db/migrations/meta/_journal.json +20 -0
  49. package/src/db/schema.ts +43 -0
  50. package/src/index.ts +434 -0
  51. package/src/middleware/config.ts +79 -0
  52. package/src/middleware/telemetry.ts +43 -0
  53. package/src/queue/index.ts +97 -0
  54. package/src/routes/access.ts +852 -0
  55. package/src/routes/docs.ts +63 -0
  56. package/src/routes/misc.ts +469 -0
  57. package/src/routes/packages.ts +2823 -0
  58. package/src/routes/ping.ts +39 -0
  59. package/src/routes/search.ts +131 -0
  60. package/src/routes/static.ts +74 -0
  61. package/src/routes/tokens.ts +259 -0
  62. package/src/routes/users.ts +68 -0
  63. package/src/utils/auth.ts +202 -0
  64. package/src/utils/cache.ts +587 -0
  65. package/src/utils/config.ts +50 -0
  66. package/src/utils/database.ts +69 -0
  67. package/src/utils/docs.ts +146 -0
  68. package/src/utils/packages.ts +453 -0
  69. package/src/utils/response.ts +125 -0
  70. package/src/utils/routes.ts +64 -0
  71. package/src/utils/spa.ts +52 -0
  72. package/src/utils/tracing.ts +52 -0
  73. package/src/utils/upstream.ts +172 -0
  74. package/tsconfig.json +16 -0
  75. package/tsconfig.worker.json +3 -0
  76. package/typedoc.mjs +2 -0
  77. package/types.ts +598 -0
  78. package/vitest.config.ts +25 -0
  79. package/vlt.json.example +56 -0
  80. package/wrangler.json +65 -0
@@ -0,0 +1,202 @@
1
+ import { packageSpec } from './packages.ts'
2
+ import type {
3
+ HonoContext,
4
+ TokenScope,
5
+ TokenAccess,
6
+ AuthUser,
7
+ DatabaseOperations,
8
+ } from '../../types.ts'
9
+
10
+ // Helper function to get typed database from context
11
+ function getDb(c: HonoContext): DatabaseOperations {
12
+ return c.get('db')
13
+ }
14
+
15
+ export function getTokenFromHeader(c: HonoContext): string | null {
16
+ const auth = c.req.header('Authorization')
17
+ if (auth?.startsWith('Bearer ')) {
18
+ return auth.substring(7).trim()
19
+ }
20
+ return null
21
+ }
22
+
23
+ export function parseTokenAccess({
24
+ scope,
25
+ pkg,
26
+ uuid,
27
+ }: {
28
+ scope: TokenScope[]
29
+ pkg?: string
30
+ uuid: string
31
+ }): TokenAccess {
32
+ const read = ['get']
33
+ const write = ['put', 'post', 'delete']
34
+ const temp: TokenAccess = {
35
+ anyUser: false,
36
+ specificUser: false,
37
+ anyPackage: false,
38
+ specificPackage: false,
39
+ readAccess: false,
40
+ writeAccess: false,
41
+ methods: [],
42
+ }
43
+
44
+ // TODO: add for multiple package access/aliases in scopes
45
+ const alternates: Record<string, string> = {}
46
+
47
+ scope.map(s => {
48
+ if (s.types.pkg) {
49
+ if (s.values.includes('*')) {
50
+ temp.anyPackage = true
51
+ }
52
+ if (
53
+ pkg &&
54
+ (s.values.includes(pkg) ||
55
+ (alternates[pkg] && s.values.includes(alternates[pkg])))
56
+ ) {
57
+ temp.specificPackage = true
58
+ }
59
+ if (
60
+ (temp.anyPackage || temp.specificPackage) &&
61
+ s.types.pkg.read
62
+ ) {
63
+ temp.readAccess = true
64
+ }
65
+ if (
66
+ (temp.anyPackage || temp.specificPackage) &&
67
+ s.types.pkg.write
68
+ ) {
69
+ temp.writeAccess = true
70
+ }
71
+ }
72
+ if (s.types.user) {
73
+ if (s.values.includes('*')) {
74
+ temp.anyUser = true
75
+ }
76
+ if (s.values.includes(`~${uuid}`)) {
77
+ temp.specificUser = true
78
+ }
79
+ if ((temp.anyUser || temp.specificUser) && s.types.user.read) {
80
+ temp.readAccess = true
81
+ }
82
+ if ((temp.anyUser || temp.specificUser) && s.types.user.write) {
83
+ temp.writeAccess = true
84
+ }
85
+ }
86
+ })
87
+
88
+ temp.methods = (temp.readAccess ? read : []).concat(
89
+ temp.writeAccess ? write : [],
90
+ )
91
+ return temp
92
+ }
93
+
94
+ export function isUserRoute(path: string): boolean {
95
+ const routes = [
96
+ 'ping',
97
+ 'whoami',
98
+ 'vlt/tokens',
99
+ 'npm/v1/user',
100
+ 'npm/v1/tokens',
101
+ 'org/',
102
+ ]
103
+ return !!routes.filter(r => path.startsWith(`/-/${r}`)).length
104
+ }
105
+
106
+ export async function getUserFromToken({
107
+ c,
108
+ token,
109
+ }: {
110
+ c: HonoContext
111
+ token: string
112
+ }): Promise<AuthUser> {
113
+ const result = await getDb(c).getToken(token)
114
+ if (!result) return { uuid: null, scope: null, token }
115
+
116
+ // Handle the case when scope is already an object (for tests)
117
+ let scope = result.scope
118
+ if (typeof scope === 'string') {
119
+ try {
120
+ scope = JSON.parse(scope) as TokenScope[]
121
+ } catch (_e) {
122
+ // Log error to monitoring system instead of console
123
+ return { uuid: null, scope: null, token }
124
+ }
125
+ }
126
+
127
+ return {
128
+ uuid: result.uuid,
129
+ scope,
130
+ token,
131
+ }
132
+ }
133
+
134
+ export async function getAuthedUser({
135
+ c,
136
+ token,
137
+ }: {
138
+ c: HonoContext
139
+ token?: string | null
140
+ }): Promise<AuthUser | null> {
141
+ const authToken = token || getTokenFromHeader(c)
142
+ if (!authToken) {
143
+ return null
144
+ }
145
+ return await getUserFromToken({ c, token: authToken })
146
+ }
147
+
148
+ export async function verifyToken(
149
+ token: string,
150
+ c: HonoContext,
151
+ ): Promise<boolean> {
152
+ const method = c.req.method ? c.req.method.toLowerCase() : ''
153
+
154
+ if (!token) {
155
+ return false
156
+ }
157
+
158
+ const { uuid, scope } = await getUserFromToken({ c, token })
159
+
160
+ if (!uuid || !scope?.length) {
161
+ return false
162
+ } else {
163
+ const { path } = c.req
164
+ const { pkg } = packageSpec(c)
165
+ const routeType =
166
+ isUserRoute(path) ? 'user'
167
+ : pkg ? 'pkg'
168
+ : null
169
+
170
+ // determine access
171
+ const parseParams: {
172
+ scope: TokenScope[]
173
+ uuid: string
174
+ pkg?: string
175
+ } = { scope, uuid }
176
+ if (pkg) {
177
+ parseParams.pkg = pkg
178
+ }
179
+ const {
180
+ anyUser,
181
+ specificUser,
182
+ anyPackage,
183
+ specificPackage,
184
+ methods,
185
+ } = parseTokenAccess(parseParams)
186
+
187
+ const methodAllowed = methods.includes(method)
188
+
189
+ // if the route is a user route
190
+ if (routeType === 'user') {
191
+ return methodAllowed && (anyUser || specificUser)
192
+ }
193
+
194
+ // handle package routes
195
+ if (routeType === 'pkg') {
196
+ return methodAllowed && (anyPackage || specificPackage)
197
+ }
198
+
199
+ // fallback to false (should be unreachable code path)
200
+ return false
201
+ }
202
+ }