veryfront 0.1.1095 → 0.1.1099

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/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1095",
3
+ "version": "0.1.1099",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -47,4 +47,10 @@ export declare function serveModule(req: Request, options: ModuleServerOptions):
47
47
  * @returns true if request path starts with /_vf_modules/
48
48
  */
49
49
  export declare function isModuleRequest(req: Request): boolean;
50
+ /**
51
+ * Content type for a dev module response.
52
+ *
53
+ * Exported for testing.
54
+ */
55
+ export declare function getDevModuleContentType(modulePath: string): string;
50
56
  //# sourceMappingURL=module-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA2DtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwDrD,CAAC;AAyEF,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqczF;AAwWD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGrD"}
1
+ {"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA2DtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwDrD,CAAC;AAyEF,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqczF;AAwWD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGrD;AAiBD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAyBlE"}
@@ -786,7 +786,14 @@ function getModuleHeaders(modulePath, options = {}) {
786
786
  : "no-cache",
787
787
  };
788
788
  }
789
- function getDevModuleContentType(modulePath) {
789
+ /** Source extensions the module server compiles to JavaScript before serving. */
790
+ const COMPILED_TO_JS_EXTENSIONS = /\.(?:tsx?|jsx|mdx|md)$/;
791
+ /**
792
+ * Content type for a dev module response.
793
+ *
794
+ * Exported for testing.
795
+ */
796
+ export function getDevModuleContentType(modulePath) {
790
797
  const normalizedPath = modulePath.toLowerCase();
791
798
  if (normalizedPath.endsWith(".map") || normalizedPath.endsWith(".json")) {
792
799
  return "application/json; charset=utf-8";
@@ -794,6 +801,13 @@ function getDevModuleContentType(modulePath) {
794
801
  if (normalizedPath.endsWith(".css")) {
795
802
  return "text/css; charset=utf-8";
796
803
  }
804
+ // The request path carries the *source* extension, but the body we serve is
805
+ // always the compiled JavaScript. Typing the response from the source
806
+ // extension yields `application/typescript`, which browsers refuse to execute
807
+ // as a module under strict MIME checking.
808
+ if (COMPILED_TO_JS_EXTENSIONS.test(normalizedPath)) {
809
+ return "application/javascript; charset=utf-8";
810
+ }
797
811
  const detected = getContentTypeForPath(normalizedPath);
798
812
  if (detected === "application/octet-stream") {
799
813
  return "application/javascript; charset=utf-8";
@@ -1 +1 @@
1
- {"version":3,"file":"esm-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/esm-rewriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAWzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCrE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
1
+ {"version":3,"file":"esm-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/esm-rewriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAkCzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmCrE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CAwEjB"}
@@ -1,7 +1,28 @@
1
1
  import { rendererLogger } from "../../../utils/index.js";
2
2
  import { MODULE_NOT_FOUND } from "../../../errors/index.js";
3
3
  import { generateHash } from "./cache.js";
4
+ import { parseImports } from "../../../transforms/esm/lexer.js";
4
5
  const logger = rendererLogger.component("module-loader");
6
+ /**
7
+ * Specifiers `code` imports statically, as opposed to through `import(...)` or
8
+ * merely mentioning in a string.
9
+ *
10
+ * A lex failure returns every discovered specifier as static, so an unparseable
11
+ * bundle keeps the pre-graceful-degradation behaviour of failing loudly rather
12
+ * than quietly shipping a remote dependency.
13
+ */
14
+ async function staticImportSpecifiers(code) {
15
+ try {
16
+ const imports = await parseImports(code);
17
+ return new Set(imports.filter((imp) => imp.d === -1 && imp.n).map((imp) => imp.n));
18
+ }
19
+ catch (error) {
20
+ logger.debug("Could not lex a fetched module; treating its imports as static", {
21
+ error: error instanceof Error ? error.message : String(error),
22
+ });
23
+ return new Set(code.match(/https:\/\/esm\.sh\/[^"']+/g) ?? []);
24
+ }
25
+ }
5
26
  function escapeRegExp(value) {
6
27
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
28
  }
@@ -53,17 +74,39 @@ export async function fetchEsmModule(url, tmpDir, localAdapter, esmCache) {
53
74
  allEsmUrls.add(match[1]);
54
75
  }
55
76
  const urlArray = Array.from(allEsmUrls);
56
- const cachedPaths = await Promise.all(urlArray.map((esmUrl) => fetchEsmModule(esmUrl, tmpDir, localAdapter, esmCache)));
77
+ const staticUrls = await staticImportSpecifiers(code);
78
+ // Nested pre-fetches of a URL this module only reaches lazily are
79
+ // best-effort: a broken esm.sh build for one package logs a warning and the
80
+ // URL stays in the emitted code for the runtime to resolve at call time. A
81
+ // URL the module imports statically is part of its own import graph and must
82
+ // still resolve here, so the emitted artifact's static dependencies stay
83
+ // local. See `transforms/esm/specifier-resolver.ts` for the same rule on the
84
+ // SSR transform path.
85
+ const settledPaths = await Promise.allSettled(urlArray.map((esmUrl) => fetchEsmModule(esmUrl, tmpDir, localAdapter, esmCache)));
57
86
  if (urlArray.length) {
58
87
  const replacementMap = new Map();
59
88
  for (let i = 0; i < urlArray.length; i++) {
60
89
  const url = urlArray[i];
61
- const cached = cachedPaths[i];
62
- if (url && cached)
63
- replacementMap.set(url, `file://${cached}`);
90
+ const result = settledPaths[i];
91
+ if (!url || !result)
92
+ continue;
93
+ if (result.status === "fulfilled") {
94
+ replacementMap.set(url, `file://${result.value}`);
95
+ continue;
96
+ }
97
+ // A statically imported dependency must be local before this module is
98
+ // handed to the runtime loader, so its failure stays fatal.
99
+ if (staticUrls.has(url))
100
+ throw result.reason;
101
+ logger.warn("Leaving an unfetchable lazy esm.sh module for runtime resolution", {
102
+ url,
103
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
104
+ });
105
+ }
106
+ if (replacementMap.size) {
107
+ const combinedPattern = new RegExp(Array.from(replacementMap.keys()).map(escapeRegExp).join("|"), "g");
108
+ code = code.replace(combinedPattern, (m) => replacementMap.get(m) ?? m);
64
109
  }
65
- const combinedPattern = new RegExp(urlArray.map(escapeRegExp).join("|"), "g");
66
- code = code.replace(combinedPattern, (m) => replacementMap.get(m) ?? m);
67
110
  }
68
111
  const hash = await generateHash(url);
69
112
  const tempFilePath = `${tmpDir}/esm-${hash}.js`;
@@ -147,7 +147,7 @@ export async function loadModule(filePath, config) {
147
147
  const tmpDir = await getModuleCacheDir(config);
148
148
  const localAdapter = await getLocalAdapter();
149
149
  const tempFilePath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
150
- const moduleUrl = `file://${tempFilePath}?t=${Date.now()}`;
150
+ const moduleUrl = `file://${tempFilePath}`;
151
151
  try {
152
152
  return await import(moduleUrl);
153
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AA6CzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
1
+ {"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuDzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CA+DjB"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { join } from "../../../platform/compat/path/index.js";
7
7
  import { rendererLogger } from "../../../utils/index.js";
8
+ import { isCacheWriteRaceError } from "../../../utils/cache-file-ops.js";
8
9
  import { hashCodeHex } from "../../../utils/hash-utils.js";
9
10
  import { getModulePathCache, saveModulePathCache, } from "../../../transforms/mdx/esm-module-loader/cache/index.js";
10
11
  import { buildMdxEsmPathCacheKey } from "../../../transforms/mdx/esm-module-loader/cache-format.js";
@@ -26,19 +27,22 @@ function pruneCreatedDirs() {
26
27
  deleted++;
27
28
  }
28
29
  }
29
- async function ensureDir(adapter, dir) {
30
- if (createdDirs.has(dir))
30
+ async function ensureDir(adapter, dir, force = false) {
31
+ if (!force && createdDirs.has(dir))
31
32
  return;
32
33
  try {
33
34
  await adapter.fs.mkdir(dir, { recursive: true });
34
35
  }
35
- catch (_) {
36
- /* expected: directory might already exist */
37
- }
38
- finally {
39
- createdDirs.add(dir);
40
- pruneCreatedDirs();
36
+ catch (error) {
37
+ // `recursive: true` is a no-op on an existing directory, so a rejection here
38
+ // means the directory may genuinely be absent (EMFILE, EACCES, a racing
39
+ // sweep). Drop the memo so the next attempt retries instead of assuming the
40
+ // directory is present forever after.
41
+ createdDirs.delete(dir);
42
+ throw error;
41
43
  }
44
+ createdDirs.add(dir);
45
+ pruneCreatedDirs();
42
46
  }
43
47
  /** Write a transformed module artifact and register cache pointers. */
44
48
  export async function persistTransformedModule(input) {
@@ -49,17 +53,37 @@ export async function persistTransformedModule(input) {
49
53
  const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, `.${transformedHash}.js`);
50
54
  const tempFilePath = join(input.tmpDir, jsPath);
51
55
  const tempDir = tempFilePath.substring(0, tempFilePath.lastIndexOf("/"));
52
- await ensureDir(input.localAdapter, tempDir);
56
+ await ensureDir(input.localAdapter, tempDir).catch(() => {
57
+ // Fall through to the write, which retries the mkdir on failure.
58
+ });
53
59
  try {
54
60
  await input.localAdapter.fs.writeFile(tempFilePath, input.transformedCode);
55
61
  }
56
62
  catch (error) {
57
- logger.error("Failed to write module:", {
58
- filePath: input.filePath,
59
- tempFilePath,
60
- error: error instanceof Error ? error.message : String(error),
61
- });
62
- throw error;
63
+ // The cache directory can vanish between mkdir and write a manual
64
+ // `rm -rf .cache`, a cache sweep, or a mkdir that never actually landed.
65
+ // Force the directory back into existence and retry once before failing.
66
+ if (!isCacheWriteRaceError(error)) {
67
+ logger.error("Failed to write module:", {
68
+ filePath: input.filePath,
69
+ tempFilePath,
70
+ error: error instanceof Error ? error.message : String(error),
71
+ });
72
+ throw error;
73
+ }
74
+ try {
75
+ await ensureDir(input.localAdapter, tempDir, true);
76
+ await input.localAdapter.fs.writeFile(tempFilePath, input.transformedCode);
77
+ logger.debug("Recreated module cache directory after failed write", { tempDir });
78
+ }
79
+ catch (retryError) {
80
+ logger.error("Failed to write module:", {
81
+ filePath: input.filePath,
82
+ tempFilePath,
83
+ error: retryError instanceof Error ? retryError.message : String(retryError),
84
+ });
85
+ throw retryError;
86
+ }
63
87
  }
64
88
  if (input.contentSourceId) {
65
89
  const normalizedPath = `_vf_modules/${relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js")}`;
@@ -1 +1 @@
1
- {"version":3,"file":"host-tools.d.ts","sourceRoot":"","sources":["../../../src/src/tool/host-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAU,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAElF,KAAK,eAAe,GAAG;IACrB,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAChG,CAAC,gBAAgB,CAAC,CAAC;AAEpB,gCAAgC;AAChC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACpB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,eAAe,CAAC;IAC5C,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;CACzB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAQ7D,qDAAqD;AACrD,MAAM,WAAW,8BAA8B;IAC7C,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACnD;AA4CD,0CAA0C;AAC1C,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,WAAW,EACxB,OAAO,CAAC,EAAE,8BAA8B,GACvC,OAAO,CAAC;AACX,0CAA0C;AAC1C,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,CAAC,EAAE,8BAA8B,GACvC,OAAO,CAAC"}
1
+ {"version":3,"file":"host-tools.d.ts","sourceRoot":"","sources":["../../../src/src/tool/host-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAU,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAElF,KAAK,eAAe,GAAG;IACrB,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAChG,CAAC,gBAAgB,CAAC,CAAC;AAEpB,gCAAgC;AAChC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACpB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,eAAe,CAAC;IAC5C,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;CACzB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAQ7D,qDAAqD;AACrD,MAAM,WAAW,8BAA8B;IAC7C,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACnD;AAuDD,0CAA0C;AAC1C,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,WAAW,EACxB,OAAO,CAAC,EAAE,8BAA8B,GACvC,OAAO,CAAC;AACX,0CAA0C;AAC1C,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,CAAC,EAAE,8BAA8B,GACvC,OAAO,CAAC"}
@@ -16,10 +16,15 @@ function isSchemaLike(value) {
16
16
  // defineSchema wrapper without a brand yet — match on the contract surface.
17
17
  return "_output" in value && typeof value.safeParse === "function";
18
18
  }
19
+ function isParserBackedPrecomputedSchema(input) {
20
+ return (isRecord(input.inputSchema) &&
21
+ typeof input.inputSchema.parse === "function" &&
22
+ isRecord(input.inputSchemaJson));
23
+ }
19
24
  function isHostToolDefinition(value) {
20
25
  return (isRecord(value) &&
21
26
  typeof value.description === "string" &&
22
- isSchemaLike(value.inputSchema) &&
27
+ (isSchemaLike(value.inputSchema) || isParserBackedPrecomputedSchema(value)) &&
23
28
  typeof value.execute === "function");
24
29
  }
25
30
  function defaultToolCallId(toolName) {
@@ -42,22 +47,25 @@ export function createToolsFromHostDefinitions(definitions, options = {}) {
42
47
  continue;
43
48
  const execute = async (input, context) => await definition.execute(input, normalizeExecutionContext(toolName, context, options));
44
49
  try {
45
- tools[toolName] = definition.inputSchemaJson
46
- ? dynamicTool({
50
+ if (definition.inputSchemaJson) {
51
+ tools[toolName] = dynamicTool({
47
52
  id: toolName,
48
53
  description: definition.description,
49
54
  inputSchema: definition.inputSchema,
50
55
  inputSchemaJson: definition.inputSchemaJson,
51
56
  execute,
52
57
  mcp: definition.mcp,
53
- })
54
- : tool({
58
+ });
59
+ }
60
+ else if (isSchemaLike(definition.inputSchema)) {
61
+ tools[toolName] = tool({
55
62
  id: toolName,
56
63
  description: definition.description,
57
64
  inputSchema: definition.inputSchema,
58
65
  execute,
59
66
  mcp: definition.mcp,
60
67
  });
68
+ }
61
69
  }
62
70
  catch (error) {
63
71
  agentLogger.warn("Skipping host tool: schema conversion failed", {
@@ -11,6 +11,9 @@ import { type CacheOptions } from "./http-cache-helpers.js";
11
11
  export type CacheHttpModuleFn = (url: string, options: CacheOptions) => Promise<string | null>;
12
12
  /**
13
13
  * Build a map of specifier replacements by resolving all imports in the code.
14
+ *
15
+ * Resolution failure is fatal for a static import and best-effort for a
16
+ * specifier only ever used in `import(...)`. See {@link isDynamicOnly}.
14
17
  */
15
18
  export declare function buildReplacements(code: string, baseUrl: string | undefined, options: CacheOptions, cacheHttpModule: CacheHttpModuleFn): Promise<Map<string, string>>;
16
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"specifier-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/specifier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EACL,KAAK,YAAY,EAQlB,MAAM,yBAAyB,CAAC;AAEjC,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAgF/F;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiB9B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,MAAM,CAAC,CAKjB"}
1
+ {"version":3,"file":"specifier-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/specifier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EACL,KAAK,YAAY,EAQlB,MAAM,yBAAyB,CAAC;AAEjC,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AA4G/F;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoC9B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,MAAM,CAAC,CAKjB"}
@@ -8,7 +8,9 @@
8
8
  */
9
9
  import { basename } from "../../platform/compat/path/index.js";
10
10
  import { resolveImport } from "../../modules/import-map/resolver.js";
11
+ import { rendererLogger } from "../../utils/index.js";
11
12
  import { parseImports, replaceSpecifiers } from "./lexer.js";
13
+ const logger = rendererLogger.component("specifier-resolver");
12
14
  import { isCanonicalReactEsmUrl, isExternalScheme, isHttpUrl, isInternalBare, isParentHttpModule, isRelative, resolveBareSpecifier, } from "./http-cache-helpers.js";
13
15
  function isLocalMappedSpecifier(specifier) {
14
16
  return specifier.startsWith("/_vf_modules/") ||
@@ -80,20 +82,68 @@ async function resolveSpecifier(specifier, baseUrl, options, cacheHttpModule) {
80
82
  return mapped;
81
83
  return resolveSpecifier(mapped, baseUrl, options, cacheHttpModule);
82
84
  }
85
+ /**
86
+ * Specifiers this module only ever reaches through `import(...)`.
87
+ *
88
+ * The distinction decides what a resolution failure means. A static import is
89
+ * part of the emitted module's own import graph, so the artifact contract holds
90
+ * for it: every static dependency resolves to a local path before the module is
91
+ * handed to the runtime loader, and a failure to do that is fatal, exactly as
92
+ * it was before graceful degradation existed.
93
+ *
94
+ * A dynamic specifier is resolved by the runtime at call time and is routinely
95
+ * guarded by the caller (`platform/adapters/redis/modules.js` only calls
96
+ * `await import("redis")` when the redis adapter is actually used). Pre-fetching
97
+ * it is an optimisation, so failing to pre-fetch it leaves the specifier in
98
+ * place rather than taking down a render that would never have imported it.
99
+ */
100
+ function isDynamicOnly(imports) {
101
+ const dynamic = new Set();
102
+ const staticSpecifiers = new Set();
103
+ for (const imp of imports) {
104
+ if (!imp.n)
105
+ continue;
106
+ (imp.d > -1 ? dynamic : staticSpecifiers).add(imp.n);
107
+ }
108
+ for (const specifier of staticSpecifiers)
109
+ dynamic.delete(specifier);
110
+ return dynamic;
111
+ }
83
112
  /**
84
113
  * Build a map of specifier replacements by resolving all imports in the code.
114
+ *
115
+ * Resolution failure is fatal for a static import and best-effort for a
116
+ * specifier only ever used in `import(...)`. See {@link isDynamicOnly}.
85
117
  */
86
118
  export async function buildReplacements(code, baseUrl, options, cacheHttpModule) {
87
119
  const imports = await parseImports(code);
88
120
  const uniqueSpecifiers = [...new Set(imports.map((imp) => imp.n).filter(Boolean))];
89
- const results = await Promise.all(uniqueSpecifiers.map(async (specifier) => ({
121
+ const dynamicOnly = isDynamicOnly(imports);
122
+ const settled = await Promise.allSettled(uniqueSpecifiers.map(async (specifier) => ({
90
123
  specifier,
91
124
  resolved: await resolveSpecifier(specifier, baseUrl, options, cacheHttpModule),
92
125
  })));
93
126
  const replacements = new Map();
94
- for (const { specifier, resolved } of results) {
95
- if (resolved && resolved !== specifier)
96
- replacements.set(specifier, resolved);
127
+ for (let i = 0; i < settled.length; i++) {
128
+ const outcome = settled[i];
129
+ const specifier = uniqueSpecifiers[i];
130
+ if (!outcome || specifier === undefined)
131
+ continue;
132
+ if (outcome.status === "fulfilled") {
133
+ const { specifier: resolvedFor, resolved } = outcome.value;
134
+ if (resolved && resolved !== resolvedFor)
135
+ replacements.set(resolvedFor, resolved);
136
+ continue;
137
+ }
138
+ // A static import must resolve. Leaving one unresolved would emit a module
139
+ // whose own import graph reaches outside the local cache, which is not what
140
+ // the runtime loader is handed anywhere else.
141
+ if (!dynamicOnly.has(specifier))
142
+ throw outcome.reason;
143
+ logger.warn("Leaving an unresolvable dynamic specifier for runtime resolution", {
144
+ specifier,
145
+ error: outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason),
146
+ });
97
147
  }
98
148
  return replacements;
99
149
  }
@@ -1 +1 @@
1
- {"version":3,"file":"alias-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/alias-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,KAAK;IAEtB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAIzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;IAwCtE,OAAO,CAAC,mBAAmB;CAoB5B;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
1
+ {"version":3,"file":"alias-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/import-rewriter/strategies/alias-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,qBAAa,aAAc,YAAW,qBAAqB;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,KAAK;IAEtB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO;IAIzD,OAAO,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa;IAyCtE,OAAO,CAAC,mBAAmB;CAoB5B;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
@@ -32,8 +32,9 @@ export class AliasStrategy {
32
32
  const relativeFilePath = this.getRelativeFilePath(ctx.filePath, ctx.projectDir);
33
33
  const fileDir = relativeFilePath.substring(0, relativeFilePath.lastIndexOf("/"));
34
34
  const depth = fileDir.split("/").filter(Boolean).length;
35
- let relativePath = depth === 0 ? `./${path}` : `${"../".repeat(depth)}${path}`;
36
- if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css)$/.test(relativePath)) {
35
+ const prefix = depth === 0 ? "./" : "../".repeat(depth);
36
+ let relativePath = normalizeExtension(`${prefix}${path}`);
37
+ if (!/\.(tsx?|jsx?|mjs|cjs|mdx|css|js)$/.test(relativePath)) {
37
38
  relativePath = `${relativePath}.js`;
38
39
  }
39
40
  return { specifier: relativePath };
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1095";
2
+ export declare const VERSION = "0.1.1099";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.1095";
4
+ export const VERSION = "0.1.1099";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1095",
3
+ "version": "0.1.1099",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -328,10 +328,10 @@
328
328
  "@types/react": "19.2.14",
329
329
  "@types/react-dom": "19.2.3",
330
330
  "ws": "8.21.0",
331
- "@veryfront/ext-bundler-esbuild": "0.1.1095",
332
- "@veryfront/ext-content-mdx": "0.1.1095",
333
- "@veryfront/ext-css-tailwind": "0.1.1095",
334
- "@veryfront/ext-parser-babel": "0.1.1095"
331
+ "@veryfront/ext-bundler-esbuild": "0.1.1099",
332
+ "@veryfront/ext-content-mdx": "0.1.1099",
333
+ "@veryfront/ext-css-tailwind": "0.1.1099",
334
+ "@veryfront/ext-parser-babel": "0.1.1099"
335
335
  },
336
336
  "devDependencies": {
337
337
  "@types/node": "20.9.0"