almostnode 0.1.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.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +731 -0
  3. package/dist/__sw__.js +394 -0
  4. package/dist/ai-chatbot-demo-entry.d.ts +6 -0
  5. package/dist/ai-chatbot-demo-entry.d.ts.map +1 -0
  6. package/dist/ai-chatbot-demo.d.ts +42 -0
  7. package/dist/ai-chatbot-demo.d.ts.map +1 -0
  8. package/dist/assets/runtime-worker-D9x_Ddwz.js +60543 -0
  9. package/dist/assets/runtime-worker-D9x_Ddwz.js.map +1 -0
  10. package/dist/convex-app-demo-entry.d.ts +6 -0
  11. package/dist/convex-app-demo-entry.d.ts.map +1 -0
  12. package/dist/convex-app-demo.d.ts +68 -0
  13. package/dist/convex-app-demo.d.ts.map +1 -0
  14. package/dist/cors-proxy.d.ts +46 -0
  15. package/dist/cors-proxy.d.ts.map +1 -0
  16. package/dist/create-runtime.d.ts +42 -0
  17. package/dist/create-runtime.d.ts.map +1 -0
  18. package/dist/demo.d.ts +6 -0
  19. package/dist/demo.d.ts.map +1 -0
  20. package/dist/dev-server.d.ts +97 -0
  21. package/dist/dev-server.d.ts.map +1 -0
  22. package/dist/frameworks/next-dev-server.d.ts +202 -0
  23. package/dist/frameworks/next-dev-server.d.ts.map +1 -0
  24. package/dist/frameworks/vite-dev-server.d.ts +85 -0
  25. package/dist/frameworks/vite-dev-server.d.ts.map +1 -0
  26. package/dist/index.cjs +14965 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.ts +71 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.mjs +14867 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/next-demo.d.ts +49 -0
  33. package/dist/next-demo.d.ts.map +1 -0
  34. package/dist/npm/index.d.ts +71 -0
  35. package/dist/npm/index.d.ts.map +1 -0
  36. package/dist/npm/registry.d.ts +66 -0
  37. package/dist/npm/registry.d.ts.map +1 -0
  38. package/dist/npm/resolver.d.ts +52 -0
  39. package/dist/npm/resolver.d.ts.map +1 -0
  40. package/dist/npm/tarball.d.ts +29 -0
  41. package/dist/npm/tarball.d.ts.map +1 -0
  42. package/dist/runtime-interface.d.ts +90 -0
  43. package/dist/runtime-interface.d.ts.map +1 -0
  44. package/dist/runtime.d.ts +103 -0
  45. package/dist/runtime.d.ts.map +1 -0
  46. package/dist/sandbox-helpers.d.ts +43 -0
  47. package/dist/sandbox-helpers.d.ts.map +1 -0
  48. package/dist/sandbox-runtime.d.ts +65 -0
  49. package/dist/sandbox-runtime.d.ts.map +1 -0
  50. package/dist/server-bridge.d.ts +89 -0
  51. package/dist/server-bridge.d.ts.map +1 -0
  52. package/dist/shims/assert.d.ts +51 -0
  53. package/dist/shims/assert.d.ts.map +1 -0
  54. package/dist/shims/async_hooks.d.ts +37 -0
  55. package/dist/shims/async_hooks.d.ts.map +1 -0
  56. package/dist/shims/buffer.d.ts +20 -0
  57. package/dist/shims/buffer.d.ts.map +1 -0
  58. package/dist/shims/child_process-browser.d.ts +92 -0
  59. package/dist/shims/child_process-browser.d.ts.map +1 -0
  60. package/dist/shims/child_process.d.ts +93 -0
  61. package/dist/shims/child_process.d.ts.map +1 -0
  62. package/dist/shims/chokidar.d.ts +55 -0
  63. package/dist/shims/chokidar.d.ts.map +1 -0
  64. package/dist/shims/cluster.d.ts +52 -0
  65. package/dist/shims/cluster.d.ts.map +1 -0
  66. package/dist/shims/crypto.d.ts +122 -0
  67. package/dist/shims/crypto.d.ts.map +1 -0
  68. package/dist/shims/dgram.d.ts +34 -0
  69. package/dist/shims/dgram.d.ts.map +1 -0
  70. package/dist/shims/diagnostics_channel.d.ts +80 -0
  71. package/dist/shims/diagnostics_channel.d.ts.map +1 -0
  72. package/dist/shims/dns.d.ts +87 -0
  73. package/dist/shims/dns.d.ts.map +1 -0
  74. package/dist/shims/domain.d.ts +25 -0
  75. package/dist/shims/domain.d.ts.map +1 -0
  76. package/dist/shims/esbuild.d.ts +105 -0
  77. package/dist/shims/esbuild.d.ts.map +1 -0
  78. package/dist/shims/events.d.ts +37 -0
  79. package/dist/shims/events.d.ts.map +1 -0
  80. package/dist/shims/fs.d.ts +115 -0
  81. package/dist/shims/fs.d.ts.map +1 -0
  82. package/dist/shims/fsevents.d.ts +67 -0
  83. package/dist/shims/fsevents.d.ts.map +1 -0
  84. package/dist/shims/http.d.ts +217 -0
  85. package/dist/shims/http.d.ts.map +1 -0
  86. package/dist/shims/http2.d.ts +81 -0
  87. package/dist/shims/http2.d.ts.map +1 -0
  88. package/dist/shims/https.d.ts +36 -0
  89. package/dist/shims/https.d.ts.map +1 -0
  90. package/dist/shims/inspector.d.ts +25 -0
  91. package/dist/shims/inspector.d.ts.map +1 -0
  92. package/dist/shims/module.d.ts +22 -0
  93. package/dist/shims/module.d.ts.map +1 -0
  94. package/dist/shims/net.d.ts +100 -0
  95. package/dist/shims/net.d.ts.map +1 -0
  96. package/dist/shims/os.d.ts +159 -0
  97. package/dist/shims/os.d.ts.map +1 -0
  98. package/dist/shims/path.d.ts +72 -0
  99. package/dist/shims/path.d.ts.map +1 -0
  100. package/dist/shims/perf_hooks.d.ts +50 -0
  101. package/dist/shims/perf_hooks.d.ts.map +1 -0
  102. package/dist/shims/process.d.ts +93 -0
  103. package/dist/shims/process.d.ts.map +1 -0
  104. package/dist/shims/querystring.d.ts +23 -0
  105. package/dist/shims/querystring.d.ts.map +1 -0
  106. package/dist/shims/readdirp.d.ts +52 -0
  107. package/dist/shims/readdirp.d.ts.map +1 -0
  108. package/dist/shims/readline.d.ts +62 -0
  109. package/dist/shims/readline.d.ts.map +1 -0
  110. package/dist/shims/rollup.d.ts +34 -0
  111. package/dist/shims/rollup.d.ts.map +1 -0
  112. package/dist/shims/sentry.d.ts +163 -0
  113. package/dist/shims/sentry.d.ts.map +1 -0
  114. package/dist/shims/stream.d.ts +181 -0
  115. package/dist/shims/stream.d.ts.map +1 -0
  116. package/dist/shims/tls.d.ts +53 -0
  117. package/dist/shims/tls.d.ts.map +1 -0
  118. package/dist/shims/tty.d.ts +30 -0
  119. package/dist/shims/tty.d.ts.map +1 -0
  120. package/dist/shims/url.d.ts +64 -0
  121. package/dist/shims/url.d.ts.map +1 -0
  122. package/dist/shims/util.d.ts +106 -0
  123. package/dist/shims/util.d.ts.map +1 -0
  124. package/dist/shims/v8.d.ts +73 -0
  125. package/dist/shims/v8.d.ts.map +1 -0
  126. package/dist/shims/vfs-adapter.d.ts +126 -0
  127. package/dist/shims/vfs-adapter.d.ts.map +1 -0
  128. package/dist/shims/vm.d.ts +45 -0
  129. package/dist/shims/vm.d.ts.map +1 -0
  130. package/dist/shims/worker_threads.d.ts +66 -0
  131. package/dist/shims/worker_threads.d.ts.map +1 -0
  132. package/dist/shims/ws.d.ts +66 -0
  133. package/dist/shims/ws.d.ts.map +1 -0
  134. package/dist/shims/zlib.d.ts +161 -0
  135. package/dist/shims/zlib.d.ts.map +1 -0
  136. package/dist/transform.d.ts +24 -0
  137. package/dist/transform.d.ts.map +1 -0
  138. package/dist/virtual-fs.d.ts +226 -0
  139. package/dist/virtual-fs.d.ts.map +1 -0
  140. package/dist/vite-demo.d.ts +35 -0
  141. package/dist/vite-demo.d.ts.map +1 -0
  142. package/dist/vite-sw.js +132 -0
  143. package/dist/worker/runtime-worker.d.ts +8 -0
  144. package/dist/worker/runtime-worker.d.ts.map +1 -0
  145. package/dist/worker-runtime.d.ts +50 -0
  146. package/dist/worker-runtime.d.ts.map +1 -0
  147. package/package.json +85 -0
  148. package/src/ai-chatbot-demo-entry.ts +244 -0
  149. package/src/ai-chatbot-demo.ts +509 -0
  150. package/src/convex-app-demo-entry.ts +1107 -0
  151. package/src/convex-app-demo.ts +1316 -0
  152. package/src/cors-proxy.ts +81 -0
  153. package/src/create-runtime.ts +147 -0
  154. package/src/demo.ts +304 -0
  155. package/src/dev-server.ts +274 -0
  156. package/src/frameworks/next-dev-server.ts +2224 -0
  157. package/src/frameworks/vite-dev-server.ts +702 -0
  158. package/src/index.ts +101 -0
  159. package/src/next-demo.ts +1784 -0
  160. package/src/npm/index.ts +347 -0
  161. package/src/npm/registry.ts +152 -0
  162. package/src/npm/resolver.ts +385 -0
  163. package/src/npm/tarball.ts +209 -0
  164. package/src/runtime-interface.ts +103 -0
  165. package/src/runtime.ts +1046 -0
  166. package/src/sandbox-helpers.ts +173 -0
  167. package/src/sandbox-runtime.ts +252 -0
  168. package/src/server-bridge.ts +426 -0
  169. package/src/shims/assert.ts +664 -0
  170. package/src/shims/async_hooks.ts +86 -0
  171. package/src/shims/buffer.ts +75 -0
  172. package/src/shims/child_process-browser.ts +217 -0
  173. package/src/shims/child_process.ts +463 -0
  174. package/src/shims/chokidar.ts +313 -0
  175. package/src/shims/cluster.ts +67 -0
  176. package/src/shims/crypto.ts +830 -0
  177. package/src/shims/dgram.ts +47 -0
  178. package/src/shims/diagnostics_channel.ts +196 -0
  179. package/src/shims/dns.ts +172 -0
  180. package/src/shims/domain.ts +58 -0
  181. package/src/shims/esbuild.ts +805 -0
  182. package/src/shims/events.ts +195 -0
  183. package/src/shims/fs.ts +803 -0
  184. package/src/shims/fsevents.ts +63 -0
  185. package/src/shims/http.ts +904 -0
  186. package/src/shims/http2.ts +96 -0
  187. package/src/shims/https.ts +86 -0
  188. package/src/shims/inspector.ts +30 -0
  189. package/src/shims/module.ts +82 -0
  190. package/src/shims/net.ts +359 -0
  191. package/src/shims/os.ts +195 -0
  192. package/src/shims/path.ts +199 -0
  193. package/src/shims/perf_hooks.ts +92 -0
  194. package/src/shims/process.ts +346 -0
  195. package/src/shims/querystring.ts +97 -0
  196. package/src/shims/readdirp.ts +228 -0
  197. package/src/shims/readline.ts +110 -0
  198. package/src/shims/rollup.ts +80 -0
  199. package/src/shims/sentry.ts +133 -0
  200. package/src/shims/stream.ts +1126 -0
  201. package/src/shims/tls.ts +95 -0
  202. package/src/shims/tty.ts +64 -0
  203. package/src/shims/url.ts +171 -0
  204. package/src/shims/util.ts +312 -0
  205. package/src/shims/v8.ts +113 -0
  206. package/src/shims/vfs-adapter.ts +402 -0
  207. package/src/shims/vm.ts +83 -0
  208. package/src/shims/worker_threads.ts +111 -0
  209. package/src/shims/ws.ts +382 -0
  210. package/src/shims/zlib.ts +289 -0
  211. package/src/transform.ts +313 -0
  212. package/src/types/external.d.ts +67 -0
  213. package/src/virtual-fs.ts +903 -0
  214. package/src/vite-demo.ts +577 -0
  215. package/src/worker/runtime-worker.ts +128 -0
  216. package/src/worker-runtime.ts +145 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * npm Package Manager
3
+ * Orchestrates package installation into the virtual file system
4
+ */
5
+
6
+ import { VirtualFS } from '../virtual-fs';
7
+ import { Registry, RegistryOptions } from './registry';
8
+ import {
9
+ resolveDependencies,
10
+ resolveFromPackageJson,
11
+ ResolvedPackage,
12
+ ResolveOptions,
13
+ } from './resolver';
14
+ import { downloadAndExtract, extractTarball } from './tarball';
15
+ import * as path from '../shims/path';
16
+ import { initTransformer, transformPackage, isTransformerReady } from '../transform';
17
+
18
+ export interface InstallOptions {
19
+ registry?: string;
20
+ save?: boolean;
21
+ saveDev?: boolean;
22
+ includeDev?: boolean;
23
+ includeOptional?: boolean;
24
+ onProgress?: (message: string) => void;
25
+ /** Transform ESM packages to CJS after install (default: true) */
26
+ transform?: boolean;
27
+ }
28
+
29
+ export interface InstallResult {
30
+ installed: Map<string, ResolvedPackage>;
31
+ added: string[];
32
+ }
33
+
34
+ /**
35
+ * npm Package Manager for VirtualFS
36
+ */
37
+ export class PackageManager {
38
+ private vfs: VirtualFS;
39
+ private registry: Registry;
40
+ private cwd: string;
41
+
42
+ constructor(vfs: VirtualFS, options: { cwd?: string } & RegistryOptions = {}) {
43
+ this.vfs = vfs;
44
+ this.registry = new Registry(options);
45
+ this.cwd = options.cwd || '/';
46
+ }
47
+
48
+ /**
49
+ * Install a package and its dependencies
50
+ */
51
+ async install(
52
+ packageSpec: string,
53
+ options: InstallOptions = {}
54
+ ): Promise<InstallResult> {
55
+ const { onProgress } = options;
56
+
57
+ // Parse package spec (name@version)
58
+ const { name, version } = parsePackageSpec(packageSpec);
59
+
60
+ onProgress?.(`Resolving ${name}@${version || 'latest'}...`);
61
+
62
+ // Resolve dependencies
63
+ const resolved = await resolveDependencies(name, version || 'latest', {
64
+ registry: this.registry,
65
+ includeDev: options.includeDev,
66
+ includeOptional: options.includeOptional,
67
+ onProgress,
68
+ });
69
+
70
+ // Install all resolved packages
71
+ const added = await this.installResolved(resolved, options);
72
+
73
+ // Update package.json if save option is set
74
+ if (options.save || options.saveDev) {
75
+ const pkgToAdd = resolved.get(name);
76
+ if (pkgToAdd) {
77
+ await this.updatePackageJson(
78
+ name,
79
+ `^${pkgToAdd.version}`,
80
+ options.saveDev || false
81
+ );
82
+ }
83
+ }
84
+
85
+ onProgress?.(`Installed ${resolved.size} packages`);
86
+
87
+ return { installed: resolved, added };
88
+ }
89
+
90
+ /**
91
+ * Install all dependencies from package.json
92
+ */
93
+ async installFromPackageJson(options: InstallOptions = {}): Promise<InstallResult> {
94
+ const { onProgress } = options;
95
+
96
+ const pkgJsonPath = path.join(this.cwd, 'package.json');
97
+
98
+ if (!this.vfs.existsSync(pkgJsonPath)) {
99
+ throw new Error('No package.json found');
100
+ }
101
+
102
+ const pkgJson = JSON.parse(this.vfs.readFileSync(pkgJsonPath, 'utf8'));
103
+
104
+ onProgress?.('Resolving dependencies...');
105
+
106
+ // Resolve all dependencies
107
+ const resolved = await resolveFromPackageJson(pkgJson, {
108
+ registry: this.registry,
109
+ includeDev: options.includeDev,
110
+ includeOptional: options.includeOptional,
111
+ onProgress,
112
+ });
113
+
114
+ // Install all resolved packages
115
+ const added = await this.installResolved(resolved, options);
116
+
117
+ onProgress?.(`Installed ${resolved.size} packages`);
118
+
119
+ return { installed: resolved, added };
120
+ }
121
+
122
+ /**
123
+ * Install resolved packages to node_modules
124
+ */
125
+ private async installResolved(
126
+ resolved: Map<string, ResolvedPackage>,
127
+ options: InstallOptions
128
+ ): Promise<string[]> {
129
+ const { onProgress } = options;
130
+ const added: string[] = [];
131
+
132
+ // Ensure node_modules exists
133
+ const nodeModulesPath = path.join(this.cwd, 'node_modules');
134
+ this.vfs.mkdirSync(nodeModulesPath, { recursive: true });
135
+
136
+ // Filter packages that need to be installed
137
+ const toInstall: Array<{ name: string; pkg: ResolvedPackage; pkgPath: string }> = [];
138
+
139
+ for (const [name, pkg] of resolved) {
140
+ const pkgPath = path.join(nodeModulesPath, name);
141
+
142
+ // Skip if already installed with same version
143
+ const existingPkgJson = path.join(pkgPath, 'package.json');
144
+ if (this.vfs.existsSync(existingPkgJson)) {
145
+ try {
146
+ const existing = JSON.parse(
147
+ this.vfs.readFileSync(existingPkgJson, 'utf8')
148
+ );
149
+ if (existing.version === pkg.version) {
150
+ onProgress?.(`Skipping ${name}@${pkg.version} (already installed)`);
151
+ continue;
152
+ }
153
+ } catch {
154
+ // Continue with installation if package.json is invalid
155
+ }
156
+ }
157
+
158
+ toInstall.push({ name, pkg, pkgPath });
159
+ }
160
+
161
+ // Initialize transformer if transform option is enabled (default: true)
162
+ const shouldTransform = options.transform !== false;
163
+ if (shouldTransform && !isTransformerReady()) {
164
+ onProgress?.('Initializing ESM transformer...');
165
+ await initTransformer();
166
+ }
167
+
168
+ // Install packages in parallel (limit concurrency to avoid overwhelming the browser)
169
+ const CONCURRENCY = 6;
170
+ onProgress?.(`Installing ${toInstall.length} packages...`);
171
+
172
+ for (let i = 0; i < toInstall.length; i += CONCURRENCY) {
173
+ const batch = toInstall.slice(i, i + CONCURRENCY);
174
+
175
+ await Promise.all(
176
+ batch.map(async ({ name, pkg, pkgPath }) => {
177
+ onProgress?.(` Downloading ${name}@${pkg.version}...`);
178
+
179
+ // Download and extract tarball
180
+ await downloadAndExtract(pkg.tarballUrl, this.vfs, pkgPath, {
181
+ stripComponents: 1, // Strip "package/" prefix
182
+ });
183
+
184
+ // Transform ESM to CJS
185
+ if (shouldTransform) {
186
+ try {
187
+ const count = await transformPackage(this.vfs, pkgPath, onProgress);
188
+ if (count > 0) {
189
+ onProgress?.(` Transformed ${count} files in ${name}`);
190
+ }
191
+ } catch (transformError) {
192
+ onProgress?.(` Warning: Transform failed for ${name}: ${transformError}`);
193
+ }
194
+ }
195
+
196
+ added.push(name);
197
+ })
198
+ );
199
+ }
200
+
201
+ // Create .package-lock.json for tracking
202
+ await this.writeLockfile(resolved);
203
+
204
+ return added;
205
+ }
206
+
207
+ /**
208
+ * Write lockfile with resolved versions
209
+ */
210
+ private async writeLockfile(
211
+ resolved: Map<string, ResolvedPackage>
212
+ ): Promise<void> {
213
+ const lockfile: Record<string, { version: string; resolved: string }> = {};
214
+
215
+ for (const [name, pkg] of resolved) {
216
+ lockfile[name] = {
217
+ version: pkg.version,
218
+ resolved: pkg.tarballUrl,
219
+ };
220
+ }
221
+
222
+ const lockfilePath = path.join(this.cwd, 'node_modules', '.package-lock.json');
223
+ this.vfs.writeFileSync(lockfilePath, JSON.stringify(lockfile, null, 2));
224
+ }
225
+
226
+ /**
227
+ * Update package.json with new dependency
228
+ */
229
+ private async updatePackageJson(
230
+ packageName: string,
231
+ version: string,
232
+ isDev: boolean
233
+ ): Promise<void> {
234
+ const pkgJsonPath = path.join(this.cwd, 'package.json');
235
+
236
+ let pkgJson: Record<string, unknown> = {};
237
+
238
+ if (this.vfs.existsSync(pkgJsonPath)) {
239
+ pkgJson = JSON.parse(this.vfs.readFileSync(pkgJsonPath, 'utf8'));
240
+ }
241
+
242
+ const field = isDev ? 'devDependencies' : 'dependencies';
243
+
244
+ if (!pkgJson[field]) {
245
+ pkgJson[field] = {};
246
+ }
247
+
248
+ (pkgJson[field] as Record<string, string>)[packageName] = version;
249
+
250
+ this.vfs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
251
+ }
252
+
253
+ /**
254
+ * List installed packages
255
+ */
256
+ list(): Record<string, string> {
257
+ const nodeModulesPath = path.join(this.cwd, 'node_modules');
258
+
259
+ if (!this.vfs.existsSync(nodeModulesPath)) {
260
+ return {};
261
+ }
262
+
263
+ const packages: Record<string, string> = {};
264
+ const entries = this.vfs.readdirSync(nodeModulesPath);
265
+
266
+ for (const entry of entries) {
267
+ // Skip hidden files and non-package entries
268
+ if (entry.startsWith('.')) continue;
269
+
270
+ // Handle scoped packages (@org/pkg)
271
+ if (entry.startsWith('@')) {
272
+ const scopePath = path.join(nodeModulesPath, entry);
273
+ const scopedPkgs = this.vfs.readdirSync(scopePath);
274
+
275
+ for (const scopedPkg of scopedPkgs) {
276
+ const pkgJsonPath = path.join(scopePath, scopedPkg, 'package.json');
277
+ if (this.vfs.existsSync(pkgJsonPath)) {
278
+ const pkgJson = JSON.parse(this.vfs.readFileSync(pkgJsonPath, 'utf8'));
279
+ packages[`${entry}/${scopedPkg}`] = pkgJson.version;
280
+ }
281
+ }
282
+ } else {
283
+ const pkgJsonPath = path.join(nodeModulesPath, entry, 'package.json');
284
+ if (this.vfs.existsSync(pkgJsonPath)) {
285
+ const pkgJson = JSON.parse(this.vfs.readFileSync(pkgJsonPath, 'utf8'));
286
+ packages[entry] = pkgJson.version;
287
+ }
288
+ }
289
+ }
290
+
291
+ return packages;
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Parse a package specifier into name and version
297
+ * Examples: "express", "express@4.18.2", "@types/node@18"
298
+ */
299
+ function parsePackageSpec(spec: string): { name: string; version?: string } {
300
+ // Handle scoped packages
301
+ if (spec.startsWith('@')) {
302
+ const slashIndex = spec.indexOf('/');
303
+ if (slashIndex === -1) {
304
+ throw new Error(`Invalid package spec: ${spec}`);
305
+ }
306
+
307
+ const afterSlash = spec.slice(slashIndex + 1);
308
+ const atIndex = afterSlash.indexOf('@');
309
+
310
+ if (atIndex === -1) {
311
+ return { name: spec };
312
+ }
313
+
314
+ return {
315
+ name: spec.slice(0, slashIndex + 1 + atIndex),
316
+ version: afterSlash.slice(atIndex + 1),
317
+ };
318
+ }
319
+
320
+ // Regular packages
321
+ const atIndex = spec.indexOf('@');
322
+ if (atIndex === -1) {
323
+ return { name: spec };
324
+ }
325
+
326
+ return {
327
+ name: spec.slice(0, atIndex),
328
+ version: spec.slice(atIndex + 1),
329
+ };
330
+ }
331
+
332
+ // Convenience function for quick installs
333
+ export async function install(
334
+ packageSpec: string,
335
+ vfs: VirtualFS,
336
+ options?: InstallOptions
337
+ ): Promise<InstallResult> {
338
+ const pm = new PackageManager(vfs);
339
+ return pm.install(packageSpec, options);
340
+ }
341
+
342
+ // Re-export types and modules
343
+ export { Registry } from './registry';
344
+ export type { RegistryOptions, PackageVersion, PackageManifest } from './registry';
345
+ export type { ResolvedPackage, ResolveOptions } from './resolver';
346
+ export type { ExtractOptions } from './tarball';
347
+ export { parsePackageSpec };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * npm Registry Client
3
+ * Fetches package metadata from npm registry
4
+ */
5
+
6
+ export interface PackageVersion {
7
+ name: string;
8
+ version: string;
9
+ dependencies?: Record<string, string>;
10
+ devDependencies?: Record<string, string>;
11
+ peerDependencies?: Record<string, string>;
12
+ optionalDependencies?: Record<string, string>;
13
+ dist: {
14
+ tarball: string;
15
+ shasum: string;
16
+ integrity?: string;
17
+ };
18
+ main?: string;
19
+ module?: string;
20
+ exports?: Record<string, unknown>;
21
+ bin?: Record<string, string> | string;
22
+ }
23
+
24
+ export interface PackageManifest {
25
+ name: string;
26
+ 'dist-tags': {
27
+ latest: string;
28
+ [tag: string]: string;
29
+ };
30
+ versions: Record<string, PackageVersion>;
31
+ time?: Record<string, string>;
32
+ }
33
+
34
+ export interface RegistryOptions {
35
+ registry?: string;
36
+ cache?: Map<string, PackageManifest>;
37
+ }
38
+
39
+ const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
40
+
41
+ export class Registry {
42
+ private registryUrl: string;
43
+ private cache: Map<string, PackageManifest>;
44
+
45
+ constructor(options: RegistryOptions = {}) {
46
+ this.registryUrl = options.registry || DEFAULT_REGISTRY;
47
+ this.cache = options.cache || new Map();
48
+ }
49
+
50
+ /**
51
+ * Fetch package manifest (all versions metadata)
52
+ */
53
+ async getPackageManifest(packageName: string): Promise<PackageManifest> {
54
+ // Check cache first
55
+ if (this.cache.has(packageName)) {
56
+ return this.cache.get(packageName)!;
57
+ }
58
+
59
+ const url = `${this.registryUrl}/${encodePackageName(packageName)}`;
60
+
61
+ const response = await fetch(url, {
62
+ headers: {
63
+ Accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8',
64
+ },
65
+ });
66
+
67
+ if (!response.ok) {
68
+ if (response.status === 404) {
69
+ throw new Error(`Package not found: ${packageName}`);
70
+ }
71
+ throw new Error(`Failed to fetch package ${packageName}: ${response.status}`);
72
+ }
73
+
74
+ const manifest = (await response.json()) as PackageManifest;
75
+
76
+ // Cache the result
77
+ this.cache.set(packageName, manifest);
78
+
79
+ return manifest;
80
+ }
81
+
82
+ /**
83
+ * Get specific version metadata
84
+ */
85
+ async getPackageVersion(
86
+ packageName: string,
87
+ version: string
88
+ ): Promise<PackageVersion> {
89
+ const manifest = await this.getPackageManifest(packageName);
90
+
91
+ // Handle dist-tags (like "latest", "next", etc.)
92
+ if (manifest['dist-tags'][version]) {
93
+ version = manifest['dist-tags'][version];
94
+ }
95
+
96
+ const versionData = manifest.versions[version];
97
+ if (!versionData) {
98
+ throw new Error(`Version ${version} not found for package ${packageName}`);
99
+ }
100
+
101
+ return versionData;
102
+ }
103
+
104
+ /**
105
+ * Get latest version number
106
+ */
107
+ async getLatestVersion(packageName: string): Promise<string> {
108
+ const manifest = await this.getPackageManifest(packageName);
109
+ return manifest['dist-tags'].latest;
110
+ }
111
+
112
+ /**
113
+ * Get all available versions
114
+ */
115
+ async getVersions(packageName: string): Promise<string[]> {
116
+ const manifest = await this.getPackageManifest(packageName);
117
+ return Object.keys(manifest.versions);
118
+ }
119
+
120
+ /**
121
+ * Download tarball as ArrayBuffer
122
+ */
123
+ async downloadTarball(tarballUrl: string): Promise<ArrayBuffer> {
124
+ const response = await fetch(tarballUrl);
125
+
126
+ if (!response.ok) {
127
+ throw new Error(`Failed to download tarball: ${response.status}`);
128
+ }
129
+
130
+ return response.arrayBuffer();
131
+ }
132
+
133
+ /**
134
+ * Clear the cache
135
+ */
136
+ clearCache(): void {
137
+ this.cache.clear();
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Encode scoped package names for URL
143
+ * @scoped/package -> @scoped%2fpackage
144
+ */
145
+ function encodePackageName(name: string): string {
146
+ return name.replace('/', '%2f');
147
+ }
148
+
149
+ // Default registry instance
150
+ export const registry = new Registry();
151
+
152
+ export default Registry;