veryfront 0.1.1284 → 0.1.1285

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 (37) hide show
  1. package/docs/README.md +1 -1
  2. package/docs/config.json +1 -1
  3. package/docs/manifest.json +5 -5
  4. package/esm/deno.js +3 -3
  5. package/esm/extensions/ext-schema-zod/src/adapter.d.ts.map +1 -1
  6. package/esm/extensions/ext-schema-zod/src/adapter.js +16 -4
  7. package/esm/extensions/ext-schema-zod/src/json-schema.d.ts +2 -0
  8. package/esm/extensions/ext-schema-zod/src/json-schema.d.ts.map +1 -1
  9. package/esm/extensions/ext-schema-zod/src/json-schema.js +18 -2
  10. package/esm/extensions/ext-schema-zod/src/object-data-properties.d.ts +4 -0
  11. package/esm/extensions/ext-schema-zod/src/object-data-properties.d.ts.map +1 -0
  12. package/esm/extensions/ext-schema-zod/src/object-data-properties.js +98 -0
  13. package/esm/extensions/ext-schema-zod/src/union-diagnostics.d.ts +4 -0
  14. package/esm/extensions/ext-schema-zod/src/union-diagnostics.d.ts.map +1 -0
  15. package/esm/extensions/ext-schema-zod/src/union-diagnostics.js +63 -0
  16. package/esm/src/agent/service/application-request-credentials.d.ts +3 -0
  17. package/esm/src/agent/service/application-request-credentials.d.ts.map +1 -0
  18. package/esm/src/agent/service/application-request-credentials.js +15 -0
  19. package/esm/src/agent/service/broker-ingress.d.ts.map +1 -1
  20. package/esm/src/agent/service/broker-ingress.js +2 -0
  21. package/esm/src/agent/service/routes.d.ts.map +1 -1
  22. package/esm/src/agent/service/routes.js +2 -13
  23. package/esm/src/discovery/embedded-npm-packages.generated.js +7 -7
  24. package/esm/src/html/hydration-script-builder/hydration-runtime.generated.js +1 -1
  25. package/esm/src/platform/compat/http/pinned-fetch.d.ts +1 -1
  26. package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts.map +1 -1
  27. package/esm/src/routing/api/module-loader/external-import-rewriter.js +10 -7
  28. package/esm/src/routing/api/module-loader/loader-helpers.d.ts.map +1 -1
  29. package/esm/src/routing/api/module-loader/loader-helpers.js +2 -1
  30. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  31. package/esm/src/routing/api/module-loader/loader.js +47 -80
  32. package/esm/src/routing/api/module-loader/path-redaction.d.ts +31 -0
  33. package/esm/src/routing/api/module-loader/path-redaction.d.ts.map +1 -0
  34. package/esm/src/routing/api/module-loader/path-redaction.js +149 -0
  35. package/esm/src/utils/version-constant.d.ts +1 -1
  36. package/esm/src/utils/version-constant.js +1 -1
  37. package/package.json +10 -10
@@ -2,6 +2,7 @@ import * as dntShim from "../../../../_dnt.shims.js";
2
2
  import { computeCodeHash, computeHash, isCompiledBinary, serverLogger } from "../../../utils/index.js";
3
3
  import { readTypeScriptDecoratorOptions } from "../../../extensions/bundler/index.js";
4
4
  import { createHTTPPlugin } from "./esbuild-plugin.js";
5
+ import { isSeparator, redactFreeText, remoteOrigin, replaceMatches, structuralPathLabel, } from "./path-redaction.js";
5
6
  import { rewriteImportMetaLocations } from "./source-capability-analyzer.js";
6
7
  import { restrictedRuntimeModuleReason, validateHTTPImports, validateModuleSpecifierHosts, } from "./http-validator.js";
7
8
  import { loadSecurityConfig } from "./security-config.js";
@@ -34,6 +35,9 @@ const IntrinsicReflectOwnKeys = Reflect.ownKeys;
34
35
  const MapPrototypeDelete = Map.prototype.delete;
35
36
  const MapPrototypeGet = Map.prototype.get;
36
37
  const MapPrototypeSet = Map.prototype.set;
38
+ // Redaction runs after project code has evaluated in this realm, so it must
39
+ // not dispatch through prototypes that code could have replaced.
40
+ const RegExpPrototypeExec = RegExp.prototype.exec;
37
41
  const trustedRequestContextAccessor = currentRequestContext;
38
42
  /**
39
43
  * A specifier the HTTP plugin fetches rather than a path. URL schemes are
@@ -67,7 +71,7 @@ export function loadHandlerModule(options) {
67
71
  return extractAPIRouteHandlers(module);
68
72
  }
69
73
  catch (error) {
70
- const errorMsg = redactAbsolutePaths(error instanceof Error ? error.message : String(error), projectDir);
74
+ const errorMsg = redactFreeText(error instanceof Error ? error.message : String(error));
71
75
  // The reason, not the error: its stack names host-absolute paths.
72
76
  logger.error(`Failed to load API handler ${projectModuleLabel(modulePath, projectDir)}:`, { reason: errorMsg });
73
77
  throw toError(createError({
@@ -105,7 +109,7 @@ export function prepareHandlerModule(options) {
105
109
  });
106
110
  }
107
111
  catch (error) {
108
- const errorMsg = redactAbsolutePaths(error instanceof Error ? error.message : String(error), projectDir);
112
+ const errorMsg = redactFreeText(error instanceof Error ? error.message : String(error));
109
113
  logger.error(`Failed to prepare isolated API handler ${projectModuleLabel(modulePath, projectDir)}:`, { reason: errorMsg });
110
114
  throw toError(createError({
111
115
  type: "api",
@@ -161,13 +165,13 @@ async function loadModule(args) {
161
165
  if (!isSpecifierResolutionError(error))
162
166
  throw error;
163
167
  logger.debug("Direct import could not resolve a specifier, bundling instead", {
164
- modulePath,
165
- error: error instanceof Error ? error.message : String(error),
168
+ modulePath: projectModuleLabel(modulePath, projectDir),
169
+ error: redactFreeText(error instanceof Error ? error.message : String(error)),
166
170
  });
167
171
  return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config, decoratorOptions, allowHostTypeScriptConfigReads);
168
172
  }
169
173
  }
170
- logger.debug(`File not local, using adapter-based loading: ${modulePath}`);
174
+ logger.debug(`File not local, using adapter-based loading: ${projectModuleLabel(modulePath, projectDir)}`);
171
175
  return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config, undefined, allowHostTypeScriptConfigReads);
172
176
  }
173
177
  /** @internal Exported for the runtime-selection regression test. */
@@ -305,7 +309,7 @@ function loadTSModuleDirect(modulePath, revision) {
305
309
  const url = modulePath.startsWith("file://")
306
310
  ? `${modulePath}${cacheBuster}`
307
311
  : `file://${modulePath}${cacheBuster}`;
308
- logger.debug(`Direct import (Deno): ${url}`);
312
+ logger.debug(`Direct import (Deno): ${pathHelper.basename(modulePath)}`);
309
313
  return import(url);
310
314
  }
311
315
  /** Directly import a JavaScript route, keyed by revision so an edit is not served from the module cache. */
@@ -409,11 +413,11 @@ function markDirectGraphVisit(visited, filePath) {
409
413
  * absolute one, which would expose the machine's directory layout.
410
414
  */
411
415
  function projectModuleLabel(filePath, projectDir) {
412
- if (!pathHelper.isAbsolute(filePath))
416
+ const absolute = isSeparator(filePath[0]) ||
417
+ (filePath[1] === ":" && isSeparator(filePath[2]));
418
+ if (!absolute)
413
419
  return filePath;
414
- return isWithinDirectory(projectDir, filePath)
415
- ? pathHelper.relative(projectDir, filePath)
416
- : pathHelper.basename(filePath);
420
+ return structuralPathLabel(filePath, pathHelper.resolve(projectDir));
417
421
  }
418
422
  async function inspectDirectGraphModule(options) {
419
423
  const { filePath, routeModulePath, projectRoot, fs, allowedHosts, importMap, pending } = options;
@@ -974,7 +978,7 @@ function createImportMapPlugin(projectDir, sourceSnapshot, routeModulePath, allo
974
978
  if (!resolvedPath && args.namespace === "import-map" && args.path.startsWith(".")) {
975
979
  const modulePath = modulePathOfSpecifier(args.path);
976
980
  const absolutePath = resolveContainedLocalModule(projectDir, args.importer, modulePath);
977
- logger.debug(`[API] Import map relative resolve: ${args.path} (from ${args.importer}) -> ${absolutePath}`);
981
+ logger.debug(`[API] Import map relative resolve: ${args.path} (from ${projectModuleLabel(args.importer, projectDir)}) -> ${projectModuleLabel(absolutePath, projectDir)}`);
978
982
  return {
979
983
  path: absolutePath,
980
984
  namespace: "import-map",
@@ -1014,10 +1018,10 @@ function createImportMapPlugin(projectDir, sourceSnapshot, routeModulePath, allo
1014
1018
  ? resolveContainedLocalModule(projectDir, args.importer || projectDir, resolvedPath)
1015
1019
  : pathHelper.resolve(projectDir, mappedModulePath);
1016
1020
  if (!isWithinDirectory(pathHelper.resolve(projectDir), absolutePath)) {
1017
- logger.error(`[API] Import map entry escapes project directory: ${args.path} -> ${absolutePath}`);
1021
+ logger.error(`[API] Import map entry escapes project directory: ${args.path} -> ${projectModuleLabel(absolutePath, projectDir)}`);
1018
1022
  return { errors: [{ text: `Import map path escapes project: ${args.path}` }] };
1019
1023
  }
1020
- logger.debug(`Import map resolved: ${args.path} -> ${absolutePath}`);
1024
+ logger.debug(`Import map resolved: ${args.path} -> ${projectModuleLabel(absolutePath, projectDir)}`);
1021
1025
  return {
1022
1026
  path: absolutePath,
1023
1027
  namespace: "import-map",
@@ -1068,7 +1072,7 @@ function createNamespaceOnLoadHandler(options) {
1068
1072
  };
1069
1073
  }
1070
1074
  catch (error) {
1071
- const msg = redactAbsolutePaths(error instanceof Error ? error.message : String(error), projectDir);
1075
+ const msg = redactFreeText(error instanceof Error ? error.message : String(error));
1072
1076
  logger.error(`Failed to load ${errorLabel}: ${projectModuleLabel(args.path, projectDir)}`, { reason: msg });
1073
1077
  return { errors: [{ text: `Failed to load: ${msg}` }] };
1074
1078
  }
@@ -1236,7 +1240,7 @@ function createProjectAliasPlugin(sourceSnapshot, projectDir, routeModulePath, a
1236
1240
  const aliasedPath = args.path.slice(2);
1237
1241
  const absolutePath = pathHelper.resolve(projectRoot, modulePathOfSpecifier(aliasedPath));
1238
1242
  if (!isWithinDirectory(projectRoot, absolutePath)) {
1239
- logger.error(`[API] Project alias escapes project: ${args.path} -> ${absolutePath}`);
1243
+ logger.error(`[API] Project alias escapes project: ${args.path} -> ${projectModuleLabel(absolutePath, projectRoot)}`);
1240
1244
  return { errors: [{ text: `Project alias escapes project: ${args.path}` }] };
1241
1245
  }
1242
1246
  return {
@@ -1270,12 +1274,12 @@ function createAdapterResolvePlugin(sourceSnapshot, projectDir, routeModulePath,
1270
1274
  const modulePath = modulePathOfSpecifier(args.path);
1271
1275
  const absolutePath = pathHelper.resolve(baseDir, modulePath);
1272
1276
  if (!isWithinDirectory(pathHelper.resolve(projectDir), absolutePath)) {
1273
- logger.error(`[API] Adapter resolve path escapes project: ${args.path} -> ${absolutePath}`);
1277
+ logger.error(`[API] Adapter resolve path escapes project: ${args.path} -> ${projectModuleLabel(absolutePath, projectDir)}`);
1274
1278
  return {
1275
1279
  errors: [{ text: `Relative import escapes project: ${args.path}` }],
1276
1280
  };
1277
1281
  }
1278
- logger.debug(`[API] Adapter resolve: ${args.path} (from ${args.importer || "stdin"}) -> ${absolutePath}`);
1282
+ logger.debug(`[API] Adapter resolve: ${args.path} (from ${args.importer ? projectModuleLabel(args.importer, projectDir) : "stdin"}) -> ${projectModuleLabel(absolutePath, projectDir)}`);
1279
1283
  return {
1280
1284
  path: absolutePath,
1281
1285
  namespace: "vf-adapter",
@@ -1311,11 +1315,12 @@ function createAdapterResolvePlugin(sourceSnapshot, projectDir, routeModulePath,
1311
1315
  * are admitted only through the project's own canonical `node_modules` root;
1312
1316
  * an unrelated path is never trusted merely because one segment has that name.
1313
1317
  */
1314
- function projectBoundaryError(path) {
1315
- logger.error(`[API] Resolved import escapes project: ${path}`);
1318
+ function projectBoundaryError(path, projectDir) {
1319
+ const label = projectModuleLabel(path, projectDir);
1320
+ logger.error(`[API] Resolved import escapes project: ${label}`);
1316
1321
  return {
1317
1322
  errors: [{
1318
- text: `Import escapes the project directory: ${path}. ` +
1323
+ text: `Import escapes the project directory: ${label}. ` +
1319
1324
  `API routes may only import project files and project-owned dependencies.`,
1320
1325
  }],
1321
1326
  };
@@ -1354,7 +1359,7 @@ function createProjectBoundaryPlugin(sourceSnapshot, projectDir, routeModulePath
1354
1359
  }
1355
1360
  catch (error) {
1356
1361
  if (error instanceof ProjectBoundaryViolationError) {
1357
- return projectBoundaryError(args.path);
1362
+ return projectBoundaryError(args.path, projectDir);
1358
1363
  }
1359
1364
  const message = error instanceof Error ? error.message : String(error);
1360
1365
  return {
@@ -1366,61 +1371,19 @@ function createProjectBoundaryPlugin(sourceSnapshot, projectDir, routeModulePath
1366
1371
  };
1367
1372
  }
1368
1373
  /**
1369
- * `text` with every absolute filesystem path replaced by its project label:
1370
- * project-relative inside the project, the file name outside it. A URL's path
1371
- * is not a filesystem path and is left alone.
1374
+ * A build location by its label. The location is held structurally, so a
1375
+ * project file keeps its project-relative path; a remote module keeps only its
1376
+ * origin.
1372
1377
  */
1373
- function redactAbsolutePaths(text, projectDir) {
1374
- // `file:` URLs are paths. Then the project root, so a root containing spaces
1375
- // is caught whole, and only where a path starts: a web URL may contain it.
1376
- const root = pathHelper.resolve(projectDir).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1377
- return text
1378
- .replace(/(?<![\w-])file:[^\s'"`(),;]+/gi, (url) => fileUrlLabel(url, projectDir))
1379
- .replace(new RegExp(`(?<![\\w.:/\\\\])${root}[\\\\/]`, "g"), "").replace(/(?<![\w.:/\\])(?:[A-Za-z]:[\\/]|\\\\|\/)[^\s'"`(),;]+/g,
1380
- // A path another platform calls absolute (a drive or UNC path on POSIX)
1381
- // keeps only its file name.
1382
- (path) => pathHelper.isAbsolute(path)
1383
- ? projectModuleLabel(path, projectDir)
1384
- : path.split(/[\\/]/).pop() ?? "");
1385
- }
1386
- /**
1387
- * A `file:` URL by its project label, whatever its authority: a UNC path
1388
- * converts to `file://server/share/...`, which must not leak either.
1389
- */
1390
- function fileUrlLabel(url, projectDir) {
1391
- let parsed;
1392
- try {
1393
- parsed = new URL(url);
1394
- }
1395
- catch {
1396
- return "file";
1397
- }
1398
- // Extra safety: a query or fragment can carry a credential. fromFileUrl and
1399
- // the pathname fallback already ignore both.
1400
- parsed.search = "";
1401
- parsed.hash = "";
1402
- try {
1403
- const path = pathHelper.fromFileUrl(parsed);
1404
- if (pathHelper.isAbsolute(path))
1405
- return projectModuleLabel(path, projectDir);
1406
- }
1407
- catch {
1408
- // Not a path this platform can convert; fall back to the file name.
1409
- }
1410
- return parsed.pathname.split("/").pop() ?? "";
1411
- }
1412
- /** A remote module by its origin and path, without credentials or query. */
1413
1378
  function buildLocationLabel(file, projectDir, workingDir) {
1414
- if (/^https?:\/\//i.test(file)) {
1415
- try {
1416
- const url = new URL(file);
1417
- return `${url.origin}${url.pathname}`;
1418
- }
1419
- catch {
1420
- return "remote module";
1421
- }
1422
- }
1423
- return projectModuleLabel(pathHelper.resolve(workingDir, file), projectDir);
1379
+ if (testPattern(/^https?:\/\//i, file))
1380
+ return remoteOrigin(file);
1381
+ const absolute = isSeparator(file[0]) || (file[1] === ":" && isSeparator(file[2]));
1382
+ return structuralPathLabel(absolute ? file : pathHelper.resolve(workingDir, file), projectDir);
1383
+ }
1384
+ function testPattern(pattern, text) {
1385
+ pattern.lastIndex = 0;
1386
+ return IntrinsicReflectApply(RegExpPrototypeExec, pattern, [text]) !== null;
1424
1387
  }
1425
1388
  /**
1426
1389
  * A failed build, reported by its first error with a project-relative
@@ -1433,15 +1396,18 @@ function buildLocationLabel(file, projectDir, workingDir) {
1433
1396
  export function buildFailure(errors, projectDir, workingDir) {
1434
1397
  const first = errors[0];
1435
1398
  // Drop an esbuild namespace such as `http-url:`, but not a URL's own scheme.
1436
- const file = first?.location?.file?.replace(/^[a-z][a-z-]+:(?!\/\/)/, "");
1399
+ const location = first?.location?.file;
1400
+ const file = location === undefined
1401
+ ? undefined
1402
+ : replaceMatches(location, /^[a-z][a-z-]+:(?!\/\/)/g, () => "");
1437
1403
  const where = file
1438
- ? `${buildLocationLabel(file, projectDir, workingDir)}:` +
1404
+ ? `${buildLocationLabel(file, pathHelper.resolve(projectDir), workingDir)}:` +
1439
1405
  // esbuild columns are zero-based; editors count from one.
1440
1406
  `${first?.location?.line}:${(first?.location?.column ?? 0) + 1}: `
1441
1407
  : "";
1442
1408
  return toError(createError({
1443
1409
  type: "api",
1444
- message: `[API] handler build failed: ${where}${redactAbsolutePaths(first?.text || "unknown error", projectDir)}`,
1410
+ message: `[API] handler build failed: ${where}${redactFreeText(first?.text || "unknown error")}`,
1445
1411
  }));
1446
1412
  }
1447
1413
  async function loadAndTranspileModule(modulePath, projectDir, adapter, fs, config, decoratorOptions, allowHostTypeScriptConfigReads = false) {
@@ -1599,7 +1565,7 @@ function buildTranspiledModuleSource(modulePath, projectDir, adapter, config, re
1599
1565
  });
1600
1566
  if (result.errors?.length)
1601
1567
  throw buildFailure(result.errors, projectDir, workingDir);
1602
- logger.debug(`built handler ${resolvedPath}`);
1568
+ logger.debug(`built handler ${projectModuleLabel(resolvedPath, projectDir)}`);
1603
1569
  const js = result.outputFiles?.[0]?.text ?? "export {}";
1604
1570
  logger.debug(`transpiled size ${js.length} bytes`);
1605
1571
  return await rewriteExternalImports(js, projectDir, sourceSnapshot, userDeps);
@@ -1746,8 +1712,9 @@ async function importModuleFromCode(code, fs) {
1746
1712
  return await import(`file://${tempFile}?v=${Date.now()}`);
1747
1713
  }
1748
1714
  catch (e) {
1749
- const errorMessage = e instanceof Error && e.stack ? e.stack : String(e);
1750
- logger.error(`dynamic import failed ${tempFile}: ${errorMessage}`);
1715
+ // The message, not the stack: its frames name host-absolute paths.
1716
+ const errorMessage = e instanceof Error ? e.message : String(e);
1717
+ logger.error(`dynamic import failed: ${redactFreeText(errorMessage)}`);
1751
1718
  throw e;
1752
1719
  }
1753
1720
  finally {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Keep host paths out of API module loader errors and logs.
3
+ *
4
+ * Redaction can run after project code has evaluated in this realm, so it uses
5
+ * intrinsics captured at module load, never the live string, RegExp or URL
6
+ * prototypes that code could have replaced.
7
+ *
8
+ * @module routing/api/module-loader/path-redaction
9
+ */
10
+ /** Every match of global `pattern` in `text` replaced, in one linear pass. */
11
+ export declare function replaceMatches(text: string, pattern: RegExp, replace: (match: RegExpExecArray) => string): string;
12
+ export declare function isSeparator(char: string | undefined): boolean;
13
+ /** The text after the last `/` or `\`. */
14
+ export declare function lastPathSegment(path: string): string;
15
+ /**
16
+ * A path the code holds structurally (a module or location path, not text
17
+ * scanned out of a message): project-relative inside the resolved
18
+ * `projectDir`, else its file name. A `..` segment is never trusted inside.
19
+ */
20
+ export declare function structuralPathLabel(path: string, projectDir: string): string;
21
+ /** An `http(s)` URL by scheme and host only: a path can carry a token. */
22
+ export declare function remoteOrigin(url: string): string;
23
+ /**
24
+ * `text` with every absolute path and `file:` URL cut to its file name and
25
+ * every `http(s)` URL cut to its origin. A path is taken through to the next
26
+ * whitespace or quote, so a directory name is never left behind; an outside
27
+ * path that itself contains whitespace is the accepted exception, since text
28
+ * gives no way to tell where it ends. Never throws.
29
+ */
30
+ export declare function redactFreeText(text: string): string;
31
+ //# sourceMappingURL=path-redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-redaction.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/path-redaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,8EAA8E;AAC9E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,GAC1C,MAAM,CAgBR;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE7D;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAiCD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAI5E;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYhD;AAmCD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUnD"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Keep host paths out of API module loader errors and logs.
3
+ *
4
+ * Redaction can run after project code has evaluated in this realm, so it uses
5
+ * intrinsics captured at module load, never the live string, RegExp or URL
6
+ * prototypes that code could have replaced.
7
+ *
8
+ * @module routing/api/module-loader/path-redaction
9
+ */
10
+ const IntrinsicReflectApply = Reflect.apply;
11
+ const IntrinsicDecodeURIComponent = decodeURIComponent;
12
+ const RegExpPrototypeExec = RegExp.prototype.exec;
13
+ const StringPrototypeSlice = String.prototype.slice;
14
+ const REDACTION_FAILED = "<redacted path>";
15
+ function slice(text, start, end = text.length) {
16
+ return IntrinsicReflectApply(StringPrototypeSlice, text, [start, end]);
17
+ }
18
+ /** Every match of global `pattern` in `text` replaced, in one linear pass. */
19
+ export function replaceMatches(text, pattern, replace) {
20
+ let result = "";
21
+ let last = 0;
22
+ pattern.lastIndex = 0;
23
+ for (let match = IntrinsicReflectApply(RegExpPrototypeExec, pattern, [text]); match !== null; match = IntrinsicReflectApply(RegExpPrototypeExec, pattern, [text])) {
24
+ result += slice(text, last, match.index) + replace(match);
25
+ last = match.index + match[0].length;
26
+ if (match[0].length === 0)
27
+ pattern.lastIndex++;
28
+ }
29
+ return result + slice(text, last);
30
+ }
31
+ export function isSeparator(char) {
32
+ return char === "/" || char === "\\";
33
+ }
34
+ /** The text after the last `/` or `\`. */
35
+ export function lastPathSegment(path) {
36
+ let start = 0;
37
+ for (let index = 0; index < path.length; index++) {
38
+ if (isSeparator(path[index]))
39
+ start = index + 1;
40
+ }
41
+ return slice(path, start);
42
+ }
43
+ /** `projectDir` without trailing separators, so `/app/` and `/app` agree. */
44
+ function trimmedRoot(projectDir) {
45
+ let end = projectDir.length;
46
+ while (end > 1 && isSeparator(projectDir[end - 1]))
47
+ end--;
48
+ return slice(projectDir, 0, end);
49
+ }
50
+ /** Whether `path` starts with `root` followed by a separator, either kind. */
51
+ function startsWithRoot(path, root) {
52
+ for (let index = 0; index < root.length; index++) {
53
+ const same = path[index] === root[index] ||
54
+ (isSeparator(path[index]) && isSeparator(root[index]));
55
+ if (!same)
56
+ return false;
57
+ }
58
+ return isSeparator(path[root.length]);
59
+ }
60
+ /** Whether `path` climbs with a `..` segment, so its prefix proves nothing. */
61
+ function hasParentSegment(path) {
62
+ let segmentStart = 0;
63
+ for (let index = 0; index <= path.length; index++) {
64
+ if (index === path.length || isSeparator(path[index])) {
65
+ if (index - segmentStart === 2 && path[segmentStart] === "." && path[segmentStart + 1] === ".")
66
+ return true;
67
+ segmentStart = index + 1;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+ /**
73
+ * A path the code holds structurally (a module or location path, not text
74
+ * scanned out of a message): project-relative inside the resolved
75
+ * `projectDir`, else its file name. A `..` segment is never trusted inside.
76
+ */
77
+ export function structuralPathLabel(path, projectDir) {
78
+ const root = trimmedRoot(projectDir);
79
+ if (!hasParentSegment(path) && startsWithRoot(path, root))
80
+ return slice(path, root.length + 1);
81
+ return lastPathSegment(path);
82
+ }
83
+ /** An `http(s)` URL by scheme and host only: a path can carry a token. */
84
+ export function remoteOrigin(url) {
85
+ const schemeEnd = url[4] === ":" ? 7 : 8;
86
+ let hostEnd = schemeEnd;
87
+ while (hostEnd < url.length && !isSeparator(url[hostEnd]) && url[hostEnd] !== "?" &&
88
+ url[hostEnd] !== "#")
89
+ hostEnd++;
90
+ let hostStart = schemeEnd;
91
+ for (let index = schemeEnd; index < hostEnd; index++) {
92
+ if (url[index] === "@")
93
+ hostStart = index + 1;
94
+ }
95
+ return (schemeEnd === 7 ? "http://" : "https://") + slice(url, hostStart, hostEnd);
96
+ }
97
+ /** A `file:` URL by its file name, without its query or fragment. */
98
+ function fileUrlName(url) {
99
+ let end = url.length;
100
+ for (let index = 0; index < url.length; index++) {
101
+ if (url[index] === "?" || url[index] === "#") {
102
+ end = index;
103
+ break;
104
+ }
105
+ }
106
+ let path = slice(url, 5, end);
107
+ if (path[0] === "/" && path[1] === "/") {
108
+ // The authority is a host, never a file name.
109
+ let start = 2;
110
+ while (start < path.length && path[start] !== "/")
111
+ start++;
112
+ path = slice(path, start);
113
+ }
114
+ let name = lastPathSegment(path);
115
+ try {
116
+ name = IntrinsicDecodeURIComponent(name);
117
+ }
118
+ catch {
119
+ // A malformed escape stays encoded.
120
+ }
121
+ // A decoded `%2F` or `%5C` is a separator too.
122
+ return lastPathSegment(name);
123
+ }
124
+ // One pass, three kinds of token, each running to the next whitespace or quote:
125
+ // a `file:` URL, an `http(s)` URL, and an absolute path. A path may follow `:`
126
+ // or a sentence's `.` (`error:/home/a.ts`, `end./home/a.ts`), but not a word,
127
+ // another path character, or a relative `./` or `../`.
128
+ const FREE_TEXT_PATTERN = /(?<![\w-])(file:[^\s'"`]+)|(https?:\/\/[^\s'"`]+)|(?<![\w/\\])(?<!(?<![\w])\.)(?:[A-Za-z]:[\\/]|\\\\|\/)[^\s'"`]+/gi;
129
+ /**
130
+ * `text` with every absolute path and `file:` URL cut to its file name and
131
+ * every `http(s)` URL cut to its origin. A path is taken through to the next
132
+ * whitespace or quote, so a directory name is never left behind; an outside
133
+ * path that itself contains whitespace is the accepted exception, since text
134
+ * gives no way to tell where it ends. Never throws.
135
+ */
136
+ export function redactFreeText(text) {
137
+ try {
138
+ return replaceMatches(text, FREE_TEXT_PATTERN, (match) => {
139
+ if (match[1] !== undefined)
140
+ return fileUrlName(match[1]);
141
+ if (match[2] !== undefined)
142
+ return remoteOrigin(match[2]);
143
+ return lastPathSegment(match[0]);
144
+ });
145
+ }
146
+ catch {
147
+ return REDACTION_FAILED;
148
+ }
149
+ }
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1284";
2
+ export declare const VERSION = "0.1.1285";
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.1284";
4
+ export const VERSION = "0.1.1285";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1284",
3
+ "version": "0.1.1285",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -504,17 +504,17 @@
504
504
  "dependencies": {
505
505
  "ajv": "8.18.0",
506
506
  "ajv-formats": "3.0.1",
507
- "zod": "4.3.6",
507
+ "zod": "4.6.7",
508
508
  "@deno/shim-deno": "0.18.0",
509
509
  "@deno/shim-crypto": "0.3.1",
510
510
  "@types/react": "19.2.14",
511
511
  "@types/react-dom": "19.2.3",
512
- "@veryfront/ext-bundler-esbuild": "0.1.1284",
513
- "@veryfront/ext-css-tailwind": "0.1.1284",
514
- "@veryfront/ext-dev-ui-react": "0.1.1284",
515
- "@veryfront/ext-node-websocket-ws": "0.1.1284",
516
- "@veryfront/ext-parser-babel": "0.1.1284",
517
- "@veryfront/ext-yaml": "0.1.1284"
512
+ "@veryfront/ext-bundler-esbuild": "0.1.1285",
513
+ "@veryfront/ext-css-tailwind": "0.1.1285",
514
+ "@veryfront/ext-dev-ui-react": "0.1.1285",
515
+ "@veryfront/ext-node-websocket-ws": "0.1.1285",
516
+ "@veryfront/ext-parser-babel": "0.1.1285",
517
+ "@veryfront/ext-yaml": "0.1.1285"
518
518
  },
519
519
  "devDependencies": {
520
520
  "@types/node": "20.9.0"
@@ -527,7 +527,7 @@
527
527
  "@huggingface/transformers": "^4.3.0",
528
528
  "react": "^19.2.4",
529
529
  "react-dom": "^19.2.4",
530
- "@veryfront/ext-content-mdx": "0.1.1284"
530
+ "@veryfront/ext-content-mdx": "0.1.1285"
531
531
  },
532
532
  "peerDependenciesMeta": {
533
533
  "@huggingface/transformers": {
@@ -556,5 +556,5 @@
556
556
  "NOTICE",
557
557
  "README.md"
558
558
  ],
559
- "gitHead": "ee34c936d96f979a54dc2f32def75772677f4cfd"
559
+ "gitHead": "2bac963100515224f14213021beb42e128b201ea"
560
560
  }