@vercel/python-analysis 0.11.1 → 0.13.1

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 (31) hide show
  1. package/dist/index.cjs +245 -4
  2. package/dist/index.d.ts +5 -3
  3. package/dist/index.js +249 -13
  4. package/dist/manifest/pyproject/schema.d.ts +13 -1
  5. package/dist/manifest/pyproject/schema.zod.d.ts +159 -0
  6. package/dist/manifest/pyproject/types.d.ts +19 -0
  7. package/dist/semantic/import-graph.d.ts +69 -0
  8. package/dist/wasm/interfaces/wasi-cli-environment.d.ts +1 -1
  9. package/dist/wasm/interfaces/wasi-cli-exit.d.ts +1 -1
  10. package/dist/wasm/interfaces/wasi-cli-stderr.d.ts +1 -1
  11. package/dist/wasm/interfaces/wasi-cli-stdin.d.ts +1 -1
  12. package/dist/wasm/interfaces/wasi-cli-stdout.d.ts +1 -1
  13. package/dist/wasm/interfaces/wasi-cli-terminal-input.d.ts +1 -1
  14. package/dist/wasm/interfaces/wasi-cli-terminal-output.d.ts +1 -1
  15. package/dist/wasm/interfaces/wasi-cli-terminal-stderr.d.ts +1 -1
  16. package/dist/wasm/interfaces/wasi-cli-terminal-stdin.d.ts +1 -1
  17. package/dist/wasm/interfaces/wasi-cli-terminal-stdout.d.ts +1 -1
  18. package/dist/wasm/interfaces/wasi-clocks-monotonic-clock.d.ts +4 -0
  19. package/dist/wasm/interfaces/wasi-clocks-wall-clock.d.ts +1 -1
  20. package/dist/wasm/interfaces/wasi-filesystem-preopens.d.ts +1 -1
  21. package/dist/wasm/interfaces/wasi-filesystem-types.d.ts +1 -1
  22. package/dist/wasm/interfaces/wasi-io-error.d.ts +1 -1
  23. package/dist/wasm/interfaces/wasi-io-poll.d.ts +2 -1
  24. package/dist/wasm/interfaces/wasi-io-streams.d.ts +1 -1
  25. package/dist/wasm/interfaces/wasi-random-insecure-seed.d.ts +1 -1
  26. package/dist/wasm/vercel_python_analysis.core.wasm +0 -0
  27. package/dist/wasm/vercel_python_analysis.core2.wasm +0 -0
  28. package/dist/wasm/vercel_python_analysis.core3.wasm +0 -0
  29. package/dist/wasm/vercel_python_analysis.d.ts +44 -34
  30. package/dist/wasm/vercel_python_analysis.js +292 -140
  31. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -48,6 +48,9 @@ __export(src_exports, {
48
48
  PyProjectProjectSchema: () => PyProjectProjectSchema,
49
49
  PyProjectTomlSchema: () => PyProjectTomlSchema,
50
50
  PyProjectToolSectionSchema: () => PyProjectToolSectionSchema,
51
+ PyProjectToolVercelFastapiSectionSchema: () => PyProjectToolVercelFastapiSectionSchema,
52
+ PyProjectToolVercelFastapiStaticSectionSchema: () => PyProjectToolVercelFastapiStaticSectionSchema,
53
+ PyProjectToolVercelSectionSchema: () => PyProjectToolVercelSectionSchema,
51
54
  PythonAnalysisError: () => PythonAnalysisError,
52
55
  PythonBuild: () => PythonBuild,
53
56
  PythonConfigKind: () => PythonConfigKind,
@@ -63,11 +66,13 @@ __export(src_exports, {
63
66
  UvConfigWorkspaceSchema: () => UvConfigWorkspaceSchema,
64
67
  UvIndexEntrySchema: () => UvIndexEntrySchema,
65
68
  classifyPackages: () => classifyPackages,
69
+ collectImportClosure: () => collectImportClosure,
66
70
  containsTopLevelCallable: () => containsTopLevelCallable,
67
71
  createMinimalManifest: () => createMinimalManifest,
68
72
  discoverPythonPackage: () => discoverPythonPackage,
69
73
  evaluateMarker: () => evaluateMarker,
70
74
  extendDistRecord: () => extendDistRecord,
75
+ extractImports: () => extractImports,
71
76
  findAppOrHandler: () => findAppOrHandler,
72
77
  getStringConstant: () => getStringConstant,
73
78
  isPrivatePackageSource: () => isPrivatePackageSource,
@@ -223,6 +228,224 @@ async function getStringConstant(source, name) {
223
228
  return mod.getStringConstant(source, name) ?? null;
224
229
  }
225
230
 
231
+ // src/semantic/import-graph.ts
232
+ var import_fs = __toESM(require("fs"), 1);
233
+ var import_path = require("path");
234
+ async function extractImports(source) {
235
+ if (!source.includes("import")) {
236
+ return [];
237
+ }
238
+ const mod = await importWasmModule();
239
+ return mod.extractImports(source);
240
+ }
241
+ var DEFAULT_MAX_FILES = 3e4;
242
+ async function readStatKind(path4) {
243
+ try {
244
+ const stats = await import_fs.default.promises.stat(path4);
245
+ if (stats.isFile())
246
+ return "file";
247
+ if (stats.isDirectory())
248
+ return "dir";
249
+ } catch {
250
+ }
251
+ return null;
252
+ }
253
+ function statKind(path4, cache) {
254
+ const cached = cache.statKinds.get(path4);
255
+ if (cached)
256
+ return cached;
257
+ const pending = readStatKind(path4);
258
+ cache.statKinds.set(path4, pending);
259
+ return pending;
260
+ }
261
+ async function resolveNamePartsUncached(parts, searchDirs, cache) {
262
+ const rel = (0, import_path.join)(...parts);
263
+ for (const root of searchDirs) {
264
+ let target = null;
265
+ let targetKind = null;
266
+ const candidateInit = (0, import_path.join)(root, rel, "__init__.py");
267
+ if (await statKind(candidateInit, cache) === "file") {
268
+ target = candidateInit;
269
+ targetKind = "file";
270
+ } else {
271
+ const candidateModule = (0, import_path.join)(root, `${rel}.py`);
272
+ if (await statKind(candidateModule, cache) === "file") {
273
+ target = candidateModule;
274
+ targetKind = "file";
275
+ } else if (await statKind((0, import_path.join)(root, rel), cache) === "dir") {
276
+ target = (0, import_path.join)(root, rel);
277
+ targetKind = "dir";
278
+ }
279
+ }
280
+ if (target !== null) {
281
+ const parentChain = [];
282
+ let acc = root;
283
+ for (const part of parts.slice(0, -1)) {
284
+ acc = (0, import_path.join)(acc, part);
285
+ const init = (0, import_path.join)(acc, "__init__.py");
286
+ if (await statKind(init, cache) === "file") {
287
+ parentChain.push(init);
288
+ }
289
+ }
290
+ return { target, targetKind, parentChain };
291
+ }
292
+ }
293
+ return null;
294
+ }
295
+ function resolveNameParts(parts, searchDirs, searchKey, cache) {
296
+ if (parts.length === 0)
297
+ return Promise.resolve(null);
298
+ const key = `${searchKey}\0${parts.join(".")}`;
299
+ const cached = cache.names.get(key);
300
+ if (cached)
301
+ return cached;
302
+ const pending = resolveNamePartsUncached(parts, searchDirs, cache);
303
+ cache.names.set(key, pending);
304
+ return pending;
305
+ }
306
+ async function resolveSeedName(parts, searchDirs, searchKey, cache) {
307
+ for (let length = parts.length; length > 0; length--) {
308
+ const resolved = await resolveNameParts(
309
+ parts.slice(0, length),
310
+ searchDirs,
311
+ searchKey,
312
+ cache
313
+ );
314
+ if (resolved)
315
+ return resolved;
316
+ }
317
+ return null;
318
+ }
319
+ function submoduleDir(target, targetKind) {
320
+ if (targetKind === "dir")
321
+ return target;
322
+ if (targetKind === "file" && target.endsWith(`${import_path.sep}__init__.py`)) {
323
+ return (0, import_path.dirname)(target);
324
+ }
325
+ return null;
326
+ }
327
+ async function resolveImport(stmt, importerPath, roots, rootsKey, cache) {
328
+ const resolved = /* @__PURE__ */ new Set();
329
+ let search = roots;
330
+ let searchKey = rootsKey;
331
+ if (stmt.level > 0) {
332
+ let base = (0, import_path.dirname)(importerPath);
333
+ for (let i = 1; i < stmt.level; i++) {
334
+ base = (0, import_path.dirname)(base);
335
+ }
336
+ search = [base];
337
+ searchKey = base;
338
+ }
339
+ const parts = stmt.module ? stmt.module.split(".") : [];
340
+ let target;
341
+ let targetKind;
342
+ if (parts.length > 0) {
343
+ const name = await resolveNameParts(parts, search, searchKey, cache);
344
+ if (!name)
345
+ return [];
346
+ target = name.target;
347
+ targetKind = name.targetKind;
348
+ if (targetKind === "file")
349
+ resolved.add(target);
350
+ for (const init of name.parentChain) {
351
+ resolved.add(init);
352
+ }
353
+ } else if (stmt.level > 0) {
354
+ target = search[0];
355
+ targetKind = "dir";
356
+ } else {
357
+ return [];
358
+ }
359
+ const subDir = submoduleDir(target, targetKind);
360
+ if (subDir) {
361
+ for (const name of stmt.names) {
362
+ if (name === "*")
363
+ continue;
364
+ const sub = await resolveNameParts([name], [subDir], subDir, cache);
365
+ if (sub) {
366
+ if (sub.targetKind === "file")
367
+ resolved.add(sub.target);
368
+ for (const init of sub.parentChain) {
369
+ resolved.add(init);
370
+ }
371
+ }
372
+ }
373
+ }
374
+ return [...resolved];
375
+ }
376
+ function isModuleNameSeed(seed) {
377
+ return !(0, import_path.isAbsolute)(seed) && /^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)*$/.test(seed);
378
+ }
379
+ async function collectImportClosure({
380
+ seeds,
381
+ searchRoots,
382
+ maxFiles = DEFAULT_MAX_FILES
383
+ }) {
384
+ const roots = searchRoots.map((r) => (0, import_path.resolve)(r));
385
+ const rootsKey = roots.join("\0");
386
+ const cache = {
387
+ statKinds: /* @__PURE__ */ new Map(),
388
+ names: /* @__PURE__ */ new Map()
389
+ };
390
+ const visited = /* @__PURE__ */ new Set();
391
+ let frontier = [];
392
+ let truncated = false;
393
+ for (const seed of seeds) {
394
+ if (isModuleNameSeed(seed)) {
395
+ const name = await resolveSeedName(
396
+ seed.split("."),
397
+ roots,
398
+ rootsKey,
399
+ cache
400
+ );
401
+ if (name) {
402
+ if (name.targetKind === "file")
403
+ frontier.push(name.target);
404
+ frontier.push(...name.parentChain);
405
+ }
406
+ } else {
407
+ const seedPath = (0, import_path.resolve)(seed);
408
+ if (await statKind(seedPath, cache) === "file") {
409
+ frontier.push(seedPath);
410
+ }
411
+ }
412
+ }
413
+ while (frontier.length > 0) {
414
+ const batch = [...new Set(frontier)].filter((p) => !visited.has(p));
415
+ frontier = [];
416
+ if (batch.length === 0)
417
+ continue;
418
+ for (const path4 of batch) {
419
+ visited.add(path4);
420
+ }
421
+ if (visited.size > maxFiles) {
422
+ truncated = true;
423
+ break;
424
+ }
425
+ const dependencies = await Promise.all(
426
+ batch.map(async (importerPath) => {
427
+ let source;
428
+ try {
429
+ source = await import_fs.default.promises.readFile(importerPath, "utf8");
430
+ } catch {
431
+ return [];
432
+ }
433
+ const stmts = await extractImports(source);
434
+ const files = await Promise.all(
435
+ stmts.filter((s) => s.isModuleLevel && !s.inTypeChecking).map((s) => resolveImport(s, importerPath, roots, rootsKey, cache))
436
+ );
437
+ return files.flat();
438
+ })
439
+ );
440
+ for (const dep of dependencies.flat()) {
441
+ if (!visited.has(dep)) {
442
+ frontier.push(dep);
443
+ }
444
+ }
445
+ }
446
+ return { files: visited, truncated };
447
+ }
448
+
226
449
  // src/manifest/dist-metadata.ts
227
450
  var import_node_crypto = require("crypto");
228
451
  var import_node_fs = require("fs");
@@ -391,7 +614,7 @@ async function scanDistributions(sitePackagesDir) {
391
614
  return index;
392
615
  }
393
616
  function hashFile(filePath) {
394
- return new Promise((resolve, reject) => {
617
+ return new Promise((resolve2, reject) => {
395
618
  const h = (0, import_node_crypto.createHash)("sha256");
396
619
  let size = 0;
397
620
  const stream = (0, import_node_fs.createReadStream)(filePath);
@@ -401,7 +624,7 @@ function hashFile(filePath) {
401
624
  });
402
625
  stream.on("error", reject);
403
626
  stream.on("end", () => {
404
- resolve({ hash: h.digest("base64url"), size });
627
+ resolve2({ hash: h.digest("base64url"), size });
405
628
  });
406
629
  });
407
630
  }
@@ -1008,8 +1231,18 @@ var dependencyGroupEntrySchema = import_zod4.z.union([
1008
1231
  var pyProjectDependencyGroupsSchema = import_zod4.z.record(
1009
1232
  import_zod4.z.array(dependencyGroupEntrySchema)
1010
1233
  );
1234
+ var pyProjectToolVercelFastapiStaticSectionSchema = import_zod4.z.object({
1235
+ cdn: import_zod4.z.boolean().optional()
1236
+ });
1237
+ var pyProjectToolVercelFastapiSectionSchema = import_zod4.z.object({
1238
+ static: pyProjectToolVercelFastapiStaticSectionSchema.optional()
1239
+ });
1240
+ var pyProjectToolVercelSectionSchema = import_zod4.z.object({
1241
+ fastapi: pyProjectToolVercelFastapiSectionSchema.optional()
1242
+ });
1011
1243
  var pyProjectToolSectionSchema = import_zod4.z.object({
1012
- uv: uvConfigSchema.optional()
1244
+ uv: uvConfigSchema.optional(),
1245
+ vercel: pyProjectToolVercelSectionSchema.optional()
1013
1246
  });
1014
1247
  var pyProjectTomlSchema = import_zod4.z.object({
1015
1248
  project: pyProjectProjectSchema.optional(),
@@ -1027,8 +1260,11 @@ var LicenseObjectSchema = licenseObjectSchema.passthrough();
1027
1260
  var LicenseSchema = licenseSchema;
1028
1261
  var PyProjectProjectSchema = pyProjectProjectSchema.passthrough();
1029
1262
  var PyProjectDependencyGroupsSchema = pyProjectDependencyGroupsSchema;
1263
+ var PyProjectToolVercelSectionSchema = pyProjectToolVercelSectionSchema;
1264
+ var PyProjectToolVercelFastapiSectionSchema = pyProjectToolVercelFastapiSectionSchema;
1265
+ var PyProjectToolVercelFastapiStaticSectionSchema = pyProjectToolVercelFastapiStaticSectionSchema;
1030
1266
  var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
1031
- var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
1267
+ var PyProjectTomlSchema = pyProjectTomlSchema.extend({ tool: PyProjectToolSectionSchema.optional() }).passthrough();
1032
1268
 
1033
1269
  // src/manifest/requirements-txt-parser.ts
1034
1270
  var import_posix2 = require("path/posix");
@@ -2401,6 +2637,9 @@ var HashDigestSchema = hashDigestSchema;
2401
2637
  PyProjectProjectSchema,
2402
2638
  PyProjectTomlSchema,
2403
2639
  PyProjectToolSectionSchema,
2640
+ PyProjectToolVercelFastapiSectionSchema,
2641
+ PyProjectToolVercelFastapiStaticSectionSchema,
2642
+ PyProjectToolVercelSectionSchema,
2404
2643
  PythonAnalysisError,
2405
2644
  PythonBuild,
2406
2645
  PythonConfigKind,
@@ -2416,11 +2655,13 @@ var HashDigestSchema = hashDigestSchema;
2416
2655
  UvConfigWorkspaceSchema,
2417
2656
  UvIndexEntrySchema,
2418
2657
  classifyPackages,
2658
+ collectImportClosure,
2419
2659
  containsTopLevelCallable,
2420
2660
  createMinimalManifest,
2421
2661
  discoverPythonPackage,
2422
2662
  evaluateMarker,
2423
2663
  extendDistRecord,
2664
+ extractImports,
2424
2665
  findAppOrHandler,
2425
2666
  getStringConstant,
2426
2667
  isPrivatePackageSource,
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@
7
7
  * @module @vercel/python-analysis
8
8
  */
9
9
  export { findAppOrHandler, containsTopLevelCallable, getStringConstant, } from './semantic/entrypoints';
10
+ export { collectImportClosure, extractImports } from './semantic/import-graph';
11
+ export type { ImportClosureOptions, ImportClosureResult, ImportStmt, } from './semantic/import-graph';
10
12
  export type { Distribution, DistributionIndex, PackagePath, DirectUrlInfo, } from './manifest/dist-metadata';
11
13
  export { extendDistRecord, scanDistributions } from './manifest/dist-metadata';
12
14
  export type { PythonConfig, PythonConfigs, PythonLockFile, PythonManifest, PythonManifestOrigin, PythonPackage, PythonVersionConfig, } from './manifest/package';
@@ -14,12 +16,12 @@ export { discoverPythonPackage, PythonConfigKind, PythonLockFileKind, PythonMani
14
16
  export { createMinimalManifest, stringifyManifest, type CreateMinimalManifestOptions, } from './manifest/serialize';
15
17
  export type { ClassifyPackagesOptions, PackageClassification, UvLockFile, UvLockPackage, UvLockPackageSource, UvLockWheel, } from './manifest/uv-lock-parser';
16
18
  export { classifyPackages, isPrivatePackageSource, normalizePackageName, parseUvLock, } from './manifest/uv-lock-parser';
17
- export { evaluateMarker, isWheelCompatible, } from './manifest/wheel-compat';
19
+ export { evaluateMarker, isWheelCompatible } from './manifest/wheel-compat';
18
20
  export type { PythonSelectionResult, PythonVersionSelectionResult, } from './manifest/python-selector';
19
21
  export { selectPython, selectPythonVersion } from './manifest/python-selector';
20
22
  export { PythonAnalysisError } from './util/error';
21
- export { LicenseObjectSchema, LicenseSchema, PersonSchema, PyProjectBuildSystemSchema, PyProjectDependencyGroupsSchema, PyProjectProjectSchema, PyProjectToolSectionSchema, PyProjectTomlSchema, ReadmeObjectSchema, ReadmeSchema, } from './manifest/pyproject/schema';
22
- export type { DependencyGroupEntry, DependencyGroupInclude, License, LicenseObject, Person, PyProjectBuildSystem, PyProjectDependencyGroups, PyProjectProject, PyProjectToml, PyProjectToolSection, Readme, ReadmeObject, } from './manifest/pyproject/types';
23
+ export { LicenseObjectSchema, LicenseSchema, PersonSchema, PyProjectBuildSystemSchema, PyProjectDependencyGroupsSchema, PyProjectProjectSchema, PyProjectToolSectionSchema, PyProjectTomlSchema, ReadmeObjectSchema, ReadmeSchema, PyProjectToolVercelFastapiSectionSchema, PyProjectToolVercelFastapiStaticSectionSchema, PyProjectToolVercelSectionSchema, } from './manifest/pyproject/schema';
24
+ export type { DependencyGroupEntry, DependencyGroupInclude, License, LicenseObject, Person, PyProjectBuildSystem, PyProjectDependencyGroups, PyProjectProject, PyProjectToml, PyProjectToolSection, Readme, ReadmeObject, PyProjectToolVercelFastapiSection, PyProjectToolVercelFastapiStaticSection, PyProjectToolVercelSection, } from './manifest/pyproject/types';
23
25
  export { UvConfigSchema, UvConfigWorkspaceSchema, UvIndexEntrySchema, } from './manifest/uv-config/schema';
24
26
  export type { UvConfig, UvConfigWorkspace, UvIndexEntry, } from './manifest/uv-config/types';
25
27
  export { PipfileDependencyDetailSchema, PipfileDependencySchema, PipfileLikeSchema, PipfileLockLikeSchema, PipfileLockMetaSchema, PipfileSourceSchema, } from './manifest/pipfile/schema';
package/dist/index.js CHANGED
@@ -139,11 +139,229 @@ async function getStringConstant(source, name) {
139
139
  return mod.getStringConstant(source, name) ?? null;
140
140
  }
141
141
 
142
+ // src/semantic/import-graph.ts
143
+ import fs from "fs";
144
+ import { isAbsolute, join as join2, dirname as dirname2, resolve, sep } from "path";
145
+ async function extractImports(source) {
146
+ if (!source.includes("import")) {
147
+ return [];
148
+ }
149
+ const mod = await importWasmModule();
150
+ return mod.extractImports(source);
151
+ }
152
+ var DEFAULT_MAX_FILES = 3e4;
153
+ async function readStatKind(path4) {
154
+ try {
155
+ const stats = await fs.promises.stat(path4);
156
+ if (stats.isFile())
157
+ return "file";
158
+ if (stats.isDirectory())
159
+ return "dir";
160
+ } catch {
161
+ }
162
+ return null;
163
+ }
164
+ function statKind(path4, cache) {
165
+ const cached = cache.statKinds.get(path4);
166
+ if (cached)
167
+ return cached;
168
+ const pending = readStatKind(path4);
169
+ cache.statKinds.set(path4, pending);
170
+ return pending;
171
+ }
172
+ async function resolveNamePartsUncached(parts, searchDirs, cache) {
173
+ const rel = join2(...parts);
174
+ for (const root of searchDirs) {
175
+ let target = null;
176
+ let targetKind = null;
177
+ const candidateInit = join2(root, rel, "__init__.py");
178
+ if (await statKind(candidateInit, cache) === "file") {
179
+ target = candidateInit;
180
+ targetKind = "file";
181
+ } else {
182
+ const candidateModule = join2(root, `${rel}.py`);
183
+ if (await statKind(candidateModule, cache) === "file") {
184
+ target = candidateModule;
185
+ targetKind = "file";
186
+ } else if (await statKind(join2(root, rel), cache) === "dir") {
187
+ target = join2(root, rel);
188
+ targetKind = "dir";
189
+ }
190
+ }
191
+ if (target !== null) {
192
+ const parentChain = [];
193
+ let acc = root;
194
+ for (const part of parts.slice(0, -1)) {
195
+ acc = join2(acc, part);
196
+ const init = join2(acc, "__init__.py");
197
+ if (await statKind(init, cache) === "file") {
198
+ parentChain.push(init);
199
+ }
200
+ }
201
+ return { target, targetKind, parentChain };
202
+ }
203
+ }
204
+ return null;
205
+ }
206
+ function resolveNameParts(parts, searchDirs, searchKey, cache) {
207
+ if (parts.length === 0)
208
+ return Promise.resolve(null);
209
+ const key = `${searchKey}\0${parts.join(".")}`;
210
+ const cached = cache.names.get(key);
211
+ if (cached)
212
+ return cached;
213
+ const pending = resolveNamePartsUncached(parts, searchDirs, cache);
214
+ cache.names.set(key, pending);
215
+ return pending;
216
+ }
217
+ async function resolveSeedName(parts, searchDirs, searchKey, cache) {
218
+ for (let length = parts.length; length > 0; length--) {
219
+ const resolved = await resolveNameParts(
220
+ parts.slice(0, length),
221
+ searchDirs,
222
+ searchKey,
223
+ cache
224
+ );
225
+ if (resolved)
226
+ return resolved;
227
+ }
228
+ return null;
229
+ }
230
+ function submoduleDir(target, targetKind) {
231
+ if (targetKind === "dir")
232
+ return target;
233
+ if (targetKind === "file" && target.endsWith(`${sep}__init__.py`)) {
234
+ return dirname2(target);
235
+ }
236
+ return null;
237
+ }
238
+ async function resolveImport(stmt, importerPath, roots, rootsKey, cache) {
239
+ const resolved = /* @__PURE__ */ new Set();
240
+ let search = roots;
241
+ let searchKey = rootsKey;
242
+ if (stmt.level > 0) {
243
+ let base = dirname2(importerPath);
244
+ for (let i = 1; i < stmt.level; i++) {
245
+ base = dirname2(base);
246
+ }
247
+ search = [base];
248
+ searchKey = base;
249
+ }
250
+ const parts = stmt.module ? stmt.module.split(".") : [];
251
+ let target;
252
+ let targetKind;
253
+ if (parts.length > 0) {
254
+ const name = await resolveNameParts(parts, search, searchKey, cache);
255
+ if (!name)
256
+ return [];
257
+ target = name.target;
258
+ targetKind = name.targetKind;
259
+ if (targetKind === "file")
260
+ resolved.add(target);
261
+ for (const init of name.parentChain) {
262
+ resolved.add(init);
263
+ }
264
+ } else if (stmt.level > 0) {
265
+ target = search[0];
266
+ targetKind = "dir";
267
+ } else {
268
+ return [];
269
+ }
270
+ const subDir = submoduleDir(target, targetKind);
271
+ if (subDir) {
272
+ for (const name of stmt.names) {
273
+ if (name === "*")
274
+ continue;
275
+ const sub = await resolveNameParts([name], [subDir], subDir, cache);
276
+ if (sub) {
277
+ if (sub.targetKind === "file")
278
+ resolved.add(sub.target);
279
+ for (const init of sub.parentChain) {
280
+ resolved.add(init);
281
+ }
282
+ }
283
+ }
284
+ }
285
+ return [...resolved];
286
+ }
287
+ function isModuleNameSeed(seed) {
288
+ return !isAbsolute(seed) && /^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)*$/.test(seed);
289
+ }
290
+ async function collectImportClosure({
291
+ seeds,
292
+ searchRoots,
293
+ maxFiles = DEFAULT_MAX_FILES
294
+ }) {
295
+ const roots = searchRoots.map((r) => resolve(r));
296
+ const rootsKey = roots.join("\0");
297
+ const cache = {
298
+ statKinds: /* @__PURE__ */ new Map(),
299
+ names: /* @__PURE__ */ new Map()
300
+ };
301
+ const visited = /* @__PURE__ */ new Set();
302
+ let frontier = [];
303
+ let truncated = false;
304
+ for (const seed of seeds) {
305
+ if (isModuleNameSeed(seed)) {
306
+ const name = await resolveSeedName(
307
+ seed.split("."),
308
+ roots,
309
+ rootsKey,
310
+ cache
311
+ );
312
+ if (name) {
313
+ if (name.targetKind === "file")
314
+ frontier.push(name.target);
315
+ frontier.push(...name.parentChain);
316
+ }
317
+ } else {
318
+ const seedPath = resolve(seed);
319
+ if (await statKind(seedPath, cache) === "file") {
320
+ frontier.push(seedPath);
321
+ }
322
+ }
323
+ }
324
+ while (frontier.length > 0) {
325
+ const batch = [...new Set(frontier)].filter((p) => !visited.has(p));
326
+ frontier = [];
327
+ if (batch.length === 0)
328
+ continue;
329
+ for (const path4 of batch) {
330
+ visited.add(path4);
331
+ }
332
+ if (visited.size > maxFiles) {
333
+ truncated = true;
334
+ break;
335
+ }
336
+ const dependencies = await Promise.all(
337
+ batch.map(async (importerPath) => {
338
+ let source;
339
+ try {
340
+ source = await fs.promises.readFile(importerPath, "utf8");
341
+ } catch {
342
+ return [];
343
+ }
344
+ const stmts = await extractImports(source);
345
+ const files = await Promise.all(
346
+ stmts.filter((s) => s.isModuleLevel && !s.inTypeChecking).map((s) => resolveImport(s, importerPath, roots, rootsKey, cache))
347
+ );
348
+ return files.flat();
349
+ })
350
+ );
351
+ for (const dep of dependencies.flat()) {
352
+ if (!visited.has(dep)) {
353
+ frontier.push(dep);
354
+ }
355
+ }
356
+ }
357
+ return { files: visited, truncated };
358
+ }
359
+
142
360
  // src/manifest/dist-metadata.ts
143
361
  import { createHash } from "crypto";
144
362
  import { createReadStream } from "fs";
145
363
  import { appendFile, readdir, readFile as readFile2 } from "fs/promises";
146
- import { join as join2 } from "path";
364
+ import { join as join3 } from "path";
147
365
 
148
366
  // src/manifest/pep508.ts
149
367
  var EXTRAS_REGEX = /^(.+)\[([^\]]+)\]$/;
@@ -222,7 +440,7 @@ async function parsePep508(depOrDeps) {
222
440
  // src/manifest/dist-metadata.ts
223
441
  async function readDistInfoFile(distInfoDir, filename) {
224
442
  try {
225
- return await readFile2(join2(distInfoDir, filename), "utf-8");
443
+ return await readFile2(join3(distInfoDir, filename), "utf-8");
226
444
  } catch {
227
445
  return void 0;
228
446
  }
@@ -238,7 +456,7 @@ async function scanDistributions(sitePackagesDir) {
238
456
  }
239
457
  const distInfoDirs = entries.filter((e) => e.endsWith(".dist-info"));
240
458
  for (const dirName of distInfoDirs) {
241
- const distInfoPath = join2(sitePackagesDir, dirName);
459
+ const distInfoPath = join3(sitePackagesDir, dirName);
242
460
  const metadataContent = await readDistInfoFile(distInfoPath, "METADATA");
243
461
  if (!metadataContent) {
244
462
  console.debug(`Missing METADATA in ${dirName}`);
@@ -307,7 +525,7 @@ async function scanDistributions(sitePackagesDir) {
307
525
  return index;
308
526
  }
309
527
  function hashFile(filePath) {
310
- return new Promise((resolve, reject) => {
528
+ return new Promise((resolve2, reject) => {
311
529
  const h = createHash("sha256");
312
530
  let size = 0;
313
531
  const stream = createReadStream(filePath);
@@ -317,7 +535,7 @@ function hashFile(filePath) {
317
535
  });
318
536
  stream.on("error", reject);
319
537
  stream.on("end", () => {
320
- resolve({ hash: h.digest("base64url"), size });
538
+ resolve2({ hash: h.digest("base64url"), size });
321
539
  });
322
540
  });
323
541
  }
@@ -339,7 +557,7 @@ async function extendDistRecord(sitePackagesDir, packageName, paths) {
339
557
  `No .dist-info directory found for package "${packageName}" in ${sitePackagesDir}`
340
558
  );
341
559
  }
342
- const recordPath = join2(sitePackagesDir, distInfoDirName, "RECORD");
560
+ const recordPath = join3(sitePackagesDir, distInfoDirName, "RECORD");
343
561
  let existingRecord;
344
562
  try {
345
563
  existingRecord = await readFile2(recordPath, "utf-8");
@@ -354,7 +572,7 @@ async function extendDistRecord(sitePackagesDir, packageName, paths) {
354
572
  const prefix = existingRecord.length > 0 && !existingRecord.endsWith("\n") ? "\n" : "";
355
573
  const lines = [];
356
574
  for (const p of newEntries) {
357
- const fullPath = join2(sitePackagesDir, p);
575
+ const fullPath = join3(sitePackagesDir, p);
358
576
  const { hash, size } = await hashFile(fullPath);
359
577
  lines.push(`${p},sha256=${hash},${size}`);
360
578
  }
@@ -927,8 +1145,18 @@ var dependencyGroupEntrySchema = z4.union([
927
1145
  var pyProjectDependencyGroupsSchema = z4.record(
928
1146
  z4.array(dependencyGroupEntrySchema)
929
1147
  );
1148
+ var pyProjectToolVercelFastapiStaticSectionSchema = z4.object({
1149
+ cdn: z4.boolean().optional()
1150
+ });
1151
+ var pyProjectToolVercelFastapiSectionSchema = z4.object({
1152
+ static: pyProjectToolVercelFastapiStaticSectionSchema.optional()
1153
+ });
1154
+ var pyProjectToolVercelSectionSchema = z4.object({
1155
+ fastapi: pyProjectToolVercelFastapiSectionSchema.optional()
1156
+ });
930
1157
  var pyProjectToolSectionSchema = z4.object({
931
- uv: uvConfigSchema.optional()
1158
+ uv: uvConfigSchema.optional(),
1159
+ vercel: pyProjectToolVercelSectionSchema.optional()
932
1160
  });
933
1161
  var pyProjectTomlSchema = z4.object({
934
1162
  project: pyProjectProjectSchema.optional(),
@@ -946,11 +1174,14 @@ var LicenseObjectSchema = licenseObjectSchema.passthrough();
946
1174
  var LicenseSchema = licenseSchema;
947
1175
  var PyProjectProjectSchema = pyProjectProjectSchema.passthrough();
948
1176
  var PyProjectDependencyGroupsSchema = pyProjectDependencyGroupsSchema;
1177
+ var PyProjectToolVercelSectionSchema = pyProjectToolVercelSectionSchema;
1178
+ var PyProjectToolVercelFastapiSectionSchema = pyProjectToolVercelFastapiSectionSchema;
1179
+ var PyProjectToolVercelFastapiStaticSectionSchema = pyProjectToolVercelFastapiStaticSectionSchema;
949
1180
  var PyProjectToolSectionSchema = pyProjectToolSectionSchema.passthrough();
950
- var PyProjectTomlSchema = pyProjectTomlSchema.passthrough();
1181
+ var PyProjectTomlSchema = pyProjectTomlSchema.extend({ tool: PyProjectToolSectionSchema.optional() }).passthrough();
951
1182
 
952
1183
  // src/manifest/requirements-txt-parser.ts
953
- import { isAbsolute, join as join3, relative } from "path/posix";
1184
+ import { isAbsolute as isAbsolute2, join as join4, relative } from "path/posix";
954
1185
  var PRIMARY_INDEX_NAME = "primary";
955
1186
  var EXTRA_INDEX_PREFIX = "extra-";
956
1187
  var FIND_LINKS_PREFIX = "find-links-";
@@ -1015,11 +1246,11 @@ function wasmEntryToNormalized(entry, editable) {
1015
1246
  function rebasePath(p, workingDir, packageRoot) {
1016
1247
  if (!workingDir || !packageRoot || workingDir === packageRoot)
1017
1248
  return p;
1018
- if (isAbsolute(p))
1249
+ if (isAbsolute2(p))
1019
1250
  return p;
1020
- const abs = join3(workingDir, p);
1251
+ const abs = join4(workingDir, p);
1021
1252
  const rel = relative(packageRoot, abs);
1022
- if (isAbsolute(rel))
1253
+ if (isAbsolute2(rel))
1023
1254
  return rel;
1024
1255
  if (rel.startsWith(".."))
1025
1256
  return rel;
@@ -2319,6 +2550,9 @@ export {
2319
2550
  PyProjectProjectSchema,
2320
2551
  PyProjectTomlSchema,
2321
2552
  PyProjectToolSectionSchema,
2553
+ PyProjectToolVercelFastapiSectionSchema,
2554
+ PyProjectToolVercelFastapiStaticSectionSchema,
2555
+ PyProjectToolVercelSectionSchema,
2322
2556
  PythonAnalysisError,
2323
2557
  PythonBuild,
2324
2558
  PythonConfigKind,
@@ -2334,11 +2568,13 @@ export {
2334
2568
  UvConfigWorkspaceSchema,
2335
2569
  UvIndexEntrySchema,
2336
2570
  classifyPackages,
2571
+ collectImportClosure,
2337
2572
  containsTopLevelCallable,
2338
2573
  createMinimalManifest,
2339
2574
  discoverPythonPackage,
2340
2575
  evaluateMarker,
2341
2576
  extendDistRecord,
2577
+ extractImports,
2342
2578
  findAppOrHandler,
2343
2579
  getStringConstant,
2344
2580
  isPrivatePackageSource,