@frontmcp/utils 1.2.1 → 1.3.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.
@@ -1,28 +1,60 @@
1
1
  /**
2
2
  * Runtime context detection and entry availability matching (Browser version).
3
3
  *
4
- * Returns safe defaults for browser environments.
5
- * The isEntryAvailable matcher works identically to the Node version.
4
+ * Returns safe defaults for browser environments. The matcher and
5
+ * `EntryAvailability` shape track the Node version (issue #417 added
6
+ * `os`/`provider`/`target`/`surface`).
6
7
  */
7
8
  export interface RuntimeContext {
9
+ /** Issue #417: renamed from `platform`. */
10
+ os: string;
11
+ /** @deprecated Use `os`. */
8
12
  platform: string;
9
13
  runtime: string;
10
14
  deployment: string;
15
+ /** Issue #417 — deploy provider. Always `'bare'` in browser. */
16
+ provider: string;
17
+ /** Issue #417 — build target. Always `'browser'` in browser. */
18
+ target: string;
11
19
  env: string;
12
20
  }
21
+ export type Surface = 'mcp' | 'cli' | 'http-trigger' | 'job' | 'agent';
22
+ export interface CallContext {
23
+ surface?: Surface;
24
+ }
13
25
  export interface EntryAvailability {
26
+ /** Issue #417: renamed from `platform`. */
27
+ os?: string[];
28
+ /** @deprecated Use `os`. */
14
29
  platform?: string[];
15
30
  runtime?: string[];
16
31
  deployment?: string[];
32
+ provider?: string[];
33
+ target?: string[];
34
+ surface?: Surface[];
17
35
  env?: string[];
18
36
  }
19
37
  export declare const entryAvailabilitySchema: import("@frontmcp/lazy-zod").ZodObject<{
38
+ os: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
20
39
  platform: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
21
40
  runtime: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
22
41
  deployment: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
42
+ provider: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
43
+ target: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
44
+ surface: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodEnum<{
45
+ cli: "cli";
46
+ mcp: "mcp";
47
+ "http-trigger": "http-trigger";
48
+ job: "job";
49
+ agent: "agent";
50
+ }>>>;
23
51
  env: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
24
52
  }, import("zod/v4/core").$strict>;
25
- export declare function isEntryAvailable(availability: EntryAvailability | undefined, ctx: RuntimeContext): boolean;
53
+ export declare function isEntryAvailable(availability: EntryAvailability | undefined, ctx: RuntimeContext, callCtx?: CallContext): boolean;
54
+ export declare function checkEntryAvailability(availability: EntryAvailability | undefined, ctx: RuntimeContext, callCtx?: CallContext): {
55
+ available: boolean;
56
+ missingAxes: (keyof EntryAvailability)[];
57
+ };
26
58
  export declare function detectRuntimeContext(): RuntimeContext;
27
59
  export declare function getRuntimeContext(): RuntimeContext;
28
60
  export declare function resetRuntimeContext(): void;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Build-target detection (issue #417).
3
+ *
4
+ * The CLI's `build` command knows the target name (`cli`, `node`,
5
+ * `vercel`, `lambda`, `cloudflare`, `browser`, `sdk`, `mcpb`,
6
+ * `distributed`) but doesn't tell the runtime today. This helper exposes
7
+ * it so `@Tool({ availableWhen: { target: ['cli'] } })` can express
8
+ * "this tool is only valid in the compiled CLI bundle."
9
+ *
10
+ * Resolution order:
11
+ *
12
+ * 1. `globalThis.FRONTMCP_BUILD_TARGET` — preferred. Build adapters
13
+ * inline `const FRONTMCP_BUILD_TARGET = '<target>';` into the
14
+ * emitted entry shim before any user code runs, so this constant
15
+ * is correct from the very first line of imports.
16
+ * 2. `process.env.FRONTMCP_BUILD_TARGET` — set by the build adapter
17
+ * via the runtime env when inlining isn't viable (e.g. SEA).
18
+ * 3. `'unknown'` — dev mode, tests, or any context the build command
19
+ * hasn't touched.
20
+ *
21
+ * The supported values match `frontmcp build --target <name>` plus
22
+ * `'unknown'` for the dev path.
23
+ */
24
+ export type BuildTarget = 'unknown' | 'node' | 'distributed' | 'cli' | 'vercel' | 'lambda' | 'cloudflare' | 'browser' | 'sdk' | 'mcpb';
25
+ export declare function getBuildTarget(): BuildTarget;
26
+ /** Test helper — reset the cache between specs. */
27
+ export declare function resetBuildTargetCacheForTesting(): void;
package/env/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export { getEnv, getCwd, isProduction, isDevelopment, getEnvFlag, isDebug, setEnv, isEdgeRuntime, isServerless, supportsAnsi, } from '#env';
2
- export { getRuntimeContext, resetRuntimeContext, detectRuntimeContext, isEntryAvailable, entryAvailabilitySchema, } from '#runtime-context';
3
- export type { RuntimeContext, EntryAvailability } from '#runtime-context';
2
+ export { getRuntimeContext, resetRuntimeContext, detectRuntimeContext, isEntryAvailable, checkEntryAvailability, entryAvailabilitySchema, } from '#runtime-context';
3
+ export type { RuntimeContext, EntryAvailability, Surface, CallContext } from '#runtime-context';
4
+ export { detectProvider, resetProviderCacheForTesting } from './provider';
5
+ export type { DeployProvider } from './provider';
6
+ export { getBuildTarget, resetBuildTargetCacheForTesting } from './build-target';
7
+ export type { BuildTarget } from './build-target';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Deploy-provider detection (issue #417).
3
+ *
4
+ * `isServerless()` collapses every serverless env to a single boolean;
5
+ * this returns the discriminated identity so `@Tool({ availableWhen: {
6
+ * provider: ['vercel'] } })` can express provider-specific rules.
7
+ *
8
+ * Order matters — first match wins. A user can override detection by
9
+ * setting `FRONTMCP_PROVIDER=<name>` explicitly (used by tests and by
10
+ * Docker images that don't expose a discriminating env var).
11
+ */
12
+ export type DeployProvider = 'bare' | 'docker' | 'vercel' | 'lambda' | 'cloudflare' | 'netlify' | 'azure' | 'gcp' | 'fly' | 'render' | 'railway';
13
+ /**
14
+ * Detect the current deploy provider. Cached per process.
15
+ *
16
+ * Detection rules (first match wins):
17
+ *
18
+ * 1. `FRONTMCP_PROVIDER` env var (override; trust whatever the operator set).
19
+ * 2. `VERCEL` env var → `'vercel'`.
20
+ * 3. `AWS_LAMBDA_FUNCTION_NAME` → `'lambda'`.
21
+ * 4. `CF_PAGES` → `'cloudflare'`.
22
+ * 5. `NETLIFY` → `'netlify'`.
23
+ * 6. `AZURE_FUNCTIONS_ENVIRONMENT` → `'azure'`.
24
+ * 7. `K_SERVICE` (Cloud Run / Knative) → `'gcp'`.
25
+ * 8. `FLY_APP_NAME` → `'fly'`.
26
+ * 9. `RENDER` → `'render'`.
27
+ * 10. `RAILWAY_ENVIRONMENT` → `'railway'`.
28
+ * 11. `/.dockerenv` exists → `'docker'`.
29
+ * 12. Fallback: `'bare'`.
30
+ */
31
+ export declare function detectProvider(): DeployProvider;
32
+ /** Test helper — reset the cache between specs. */
33
+ export declare function resetProviderCacheForTesting(): void;
@@ -2,25 +2,48 @@
2
2
  * Runtime context detection and entry availability matching (Node.js version).
3
3
  *
4
4
  * Provides:
5
- * - `RuntimeContext` — snapshot of the current platform, runtime, deployment, and env
5
+ * - `RuntimeContext` — snapshot of the current platform, runtime, deployment, env, provider, target
6
6
  * - `EntryAvailability` — declarative constraint for when an entry is available
7
7
  * - `getRuntimeContext()` — lazy singleton returning the detected context
8
8
  * - `isEntryAvailable()` — pure matcher: does the constraint match the context?
9
+ * - `checkEntryAvailability()` — structured matcher returning `{available, missingAxes}`
10
+ *
11
+ * Issue #417 adds:
12
+ * - `os` as the new name for `platform` (alias preserved for back-compat)
13
+ * - `provider` (vercel / lambda / cloudflare / netlify / docker / bare / …)
14
+ * - `target` (the build output produced by `frontmcp build --target <x>`)
15
+ * - `surface` (per-call axis: mcp / cli / agent / job / http-trigger)
9
16
  */
10
17
  /**
11
- * Snapshot of the current runtime environment.
12
- * Detected once and cached for the lifetime of the process.
18
+ * Snapshot of the current runtime environment. Detected once and cached
19
+ * for the lifetime of the process.
13
20
  */
14
21
  export interface RuntimeContext {
15
- /** OS platform: 'darwin', 'linux', 'win32', 'freebsd', etc. */
22
+ /** OS platform: 'darwin', 'linux', 'win32', etc. (issue #417: renamed from platform) */
23
+ os: string;
24
+ /** @deprecated Use `os` instead. Legacy alias preserved for back-compat. */
16
25
  platform: string;
17
26
  /** JavaScript runtime: 'node', 'bun', 'deno', 'edge', 'browser' */
18
27
  runtime: string;
19
28
  /** Deployment mode: 'distributed', 'serverless', 'standalone', or 'browser' */
20
29
  deployment: string;
30
+ /** Deploy provider (issue #417): 'vercel', 'lambda', 'cloudflare', 'docker', 'bare', … */
31
+ provider: string;
32
+ /** Build target produced by `frontmcp build` (issue #417). `'unknown'` in dev. */
33
+ target: string;
21
34
  /** NODE_ENV value: 'production', 'development', 'test', etc. */
22
35
  env: string;
23
36
  }
37
+ /**
38
+ * Per-call surface — `surface` is the only axis that varies per request.
39
+ * Set by transport adapters / agent dispatchers / job runners on the
40
+ * outgoing `ctx` so registry filtering can fork on call origin.
41
+ */
42
+ export type Surface = 'mcp' | 'cli' | 'http-trigger' | 'job' | 'agent';
43
+ /** Call-context wrapper threaded through `findTool` for per-call filtering. */
44
+ export interface CallContext {
45
+ surface?: Surface;
46
+ }
24
47
  /**
25
48
  * Declarative constraint for entry availability.
26
49
  *
@@ -33,21 +56,34 @@ export interface RuntimeContext {
33
56
  *
34
57
  * @example macOS only
35
58
  * ```typescript
36
- * { platform: ['darwin'] }
59
+ * { os: ['darwin'] }
60
+ * ```
61
+ *
62
+ * @example CLI binary on Linux only (issue #417)
63
+ * ```typescript
64
+ * { target: ['cli'], os: ['linux'] }
37
65
  * ```
38
66
  *
39
- * @example Node.js or Bun, production only
67
+ * @example MCP-only — block CLI / agent / job invocations (issue #417)
40
68
  * ```typescript
41
- * { runtime: ['node', 'bun'], env: ['production'] }
69
+ * { surface: ['mcp'] }
42
70
  * ```
43
71
  */
44
72
  export interface EntryAvailability {
45
- /** OS platform constraint: 'darwin', 'linux', 'win32', etc. */
73
+ /** OS constraint (issue #417: renamed from `platform`). */
74
+ os?: string[];
75
+ /** @deprecated Use `os` instead. Legacy alias preserved for back-compat. */
46
76
  platform?: string[];
47
77
  /** Runtime constraint: 'node', 'browser', 'edge', 'bun', 'deno' */
48
78
  runtime?: string[];
49
79
  /** Deployment constraint: 'distributed', 'serverless', 'standalone', 'browser' */
50
80
  deployment?: string[];
81
+ /** Deploy-provider constraint (issue #417). */
82
+ provider?: string[];
83
+ /** Build-target constraint (issue #417). */
84
+ target?: string[];
85
+ /** Surface constraint (issue #417): 'mcp', 'cli', 'agent', 'job', 'http-trigger'. */
86
+ surface?: Surface[];
51
87
  /** Environment constraint: 'production', 'development', 'test', etc. */
52
88
  env?: string[];
53
89
  }
@@ -55,19 +91,43 @@ export interface EntryAvailability {
55
91
  * Zod schema for validating EntryAvailability in metadata.
56
92
  */
57
93
  export declare const entryAvailabilitySchema: import("@frontmcp/lazy-zod").ZodObject<{
94
+ os: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
58
95
  platform: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
59
96
  runtime: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
60
97
  deployment: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
98
+ provider: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
99
+ target: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
100
+ surface: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodEnum<{
101
+ cli: "cli";
102
+ mcp: "mcp";
103
+ "http-trigger": "http-trigger";
104
+ job: "job";
105
+ agent: "agent";
106
+ }>>>;
61
107
  env: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodArray<import("@frontmcp/lazy-zod").ZodString>>;
62
108
  }, import("zod/v4/core").$strict>;
63
109
  /**
64
110
  * Check if an entry is available in the given runtime context.
65
111
  *
112
+ * Surface is checked separately because it's a per-call axis, not a
113
+ * process-global one. Pass it via `callCtx.surface` to enforce
114
+ * `availableWhen.surface` at call time.
115
+ *
66
116
  * @param availability - The constraint (undefined = always available)
67
117
  * @param ctx - The current runtime context
118
+ * @param callCtx - Optional per-call context (surface, …)
68
119
  * @returns true if the entry should be available
69
120
  */
70
- export declare function isEntryAvailable(availability: EntryAvailability | undefined, ctx: RuntimeContext): boolean;
121
+ export declare function isEntryAvailable(availability: EntryAvailability | undefined, ctx: RuntimeContext, callCtx?: CallContext): boolean;
122
+ /**
123
+ * Structured variant of `isEntryAvailable` — returns the list of axes
124
+ * that failed so flow-level call sites can build a `missingAxes`-shaped
125
+ * error response (issue #417).
126
+ */
127
+ export declare function checkEntryAvailability(availability: EntryAvailability | undefined, ctx: RuntimeContext, callCtx?: CallContext): {
128
+ available: boolean;
129
+ missingAxes: (keyof EntryAvailability)[];
130
+ };
71
131
  /**
72
132
  * Detect the current runtime context from the environment.
73
133
  */
package/esm/index.mjs CHANGED
@@ -70,8 +70,8 @@ async function readFile(p, encoding = "utf8") {
70
70
  return fsp.readFile(p, encoding);
71
71
  }
72
72
  function readFileSync(p, encoding = "utf8") {
73
- const fs = getFs();
74
- return fs.readFileSync(p, encoding);
73
+ const fs2 = getFs();
74
+ return fs2.readFileSync(p, encoding);
75
75
  }
76
76
  async function readFileBuffer(p) {
77
77
  const fsp = getFsp();
@@ -143,6 +143,10 @@ async function readdir(p) {
143
143
  const fsp = getFsp();
144
144
  return fsp.readdir(p);
145
145
  }
146
+ function readdirSync(p) {
147
+ const fs2 = getFs();
148
+ return fs2.readdirSync(p);
149
+ }
146
150
  async function rm(p, options) {
147
151
  const fsp = getFsp();
148
152
  await fsp.rm(p, options);
@@ -562,9 +566,9 @@ function createSecretData(options) {
562
566
  }
563
567
  async function getOrCreateSecret(options) {
564
568
  const cacheKey = resolveSecretPath(options);
565
- const cached2 = secretCache.get(cacheKey);
566
- if (cached2) {
567
- return cached2;
569
+ const cached4 = secretCache.get(cacheKey);
570
+ if (cached4) {
571
+ return cached4;
568
572
  }
569
573
  const existing = generationPromises.get(cacheKey);
570
574
  if (existing) {
@@ -801,8 +805,8 @@ var init_key_persistence = __esm({
801
805
  */
802
806
  async get(kid) {
803
807
  if (this.enableCache) {
804
- const cached2 = this.cache.get(kid);
805
- if (cached2) return cached2;
808
+ const cached4 = this.cache.get(kid);
809
+ if (cached4) return cached4;
806
810
  }
807
811
  const raw = await this.storage.get(kid);
808
812
  if (!raw) return null;
@@ -4185,6 +4189,7 @@ function joinPath(...parts) {
4185
4189
 
4186
4190
  // libs/utils/src/path/node-path.ts
4187
4191
  import { resolve, join, basename, dirname, extname, isAbsolute } from "path";
4192
+ import { pathToFileURL } from "url";
4188
4193
 
4189
4194
  // libs/utils/src/content/content.ts
4190
4195
  function sanitizeToJson(value) {
@@ -4483,23 +4488,114 @@ import {
4483
4488
  // libs/utils/src/env/runtime-context.ts
4484
4489
  import { z as z3 } from "@frontmcp/lazy-zod";
4485
4490
  import { isEdgeRuntime, isServerless } from "#env";
4491
+
4492
+ // libs/utils/src/env/build-target.ts
4493
+ var cached;
4494
+ function getBuildTarget() {
4495
+ if (cached) return cached;
4496
+ cached = resolveBuildTarget();
4497
+ return cached;
4498
+ }
4499
+ function resolveBuildTarget() {
4500
+ const globalAny = globalThis;
4501
+ const fromGlobal = globalAny.FRONTMCP_BUILD_TARGET;
4502
+ if (typeof fromGlobal === "string" && isKnownTarget(fromGlobal)) return fromGlobal;
4503
+ const fromEnv = typeof process !== "undefined" ? process.env?.["FRONTMCP_BUILD_TARGET"] : void 0;
4504
+ if (typeof fromEnv === "string" && isKnownTarget(fromEnv)) return fromEnv;
4505
+ return "unknown";
4506
+ }
4507
+ function isKnownTarget(value) {
4508
+ return value === "unknown" || value === "node" || value === "distributed" || value === "cli" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "browser" || value === "sdk" || value === "mcpb";
4509
+ }
4510
+
4511
+ // libs/utils/src/env/provider.ts
4512
+ import * as fs from "node:fs";
4513
+ var cached2;
4514
+ function detectProvider() {
4515
+ if (cached2) return cached2;
4516
+ cached2 = detectProviderUncached();
4517
+ return cached2;
4518
+ }
4519
+ function detectProviderUncached() {
4520
+ const explicit = process.env["FRONTMCP_PROVIDER"];
4521
+ if (explicit && isKnownProvider(explicit)) return explicit;
4522
+ if (process.env["VERCEL"]) return "vercel";
4523
+ if (process.env["AWS_LAMBDA_FUNCTION_NAME"]) return "lambda";
4524
+ if (process.env["CF_PAGES"]) return "cloudflare";
4525
+ if (process.env["NETLIFY"]) return "netlify";
4526
+ if (process.env["AZURE_FUNCTIONS_ENVIRONMENT"]) return "azure";
4527
+ if (process.env["K_SERVICE"]) return "gcp";
4528
+ if (process.env["FLY_APP_NAME"]) return "fly";
4529
+ if (process.env["RENDER"]) return "render";
4530
+ if (process.env["RAILWAY_ENVIRONMENT"]) return "railway";
4531
+ try {
4532
+ if (fs.existsSync("/.dockerenv")) return "docker";
4533
+ } catch {
4534
+ }
4535
+ return "bare";
4536
+ }
4537
+ function isKnownProvider(value) {
4538
+ return value === "bare" || value === "docker" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "netlify" || value === "azure" || value === "gcp" || value === "fly" || value === "render" || value === "railway";
4539
+ }
4540
+
4541
+ // libs/utils/src/env/runtime-context.ts
4486
4542
  var entryAvailabilitySchema = z3.object({
4543
+ os: z3.array(z3.string().min(1)).optional(),
4487
4544
  platform: z3.array(z3.string().min(1)).optional(),
4488
4545
  runtime: z3.array(z3.string().min(1)).optional(),
4489
4546
  deployment: z3.array(z3.string().min(1)).optional(),
4547
+ provider: z3.array(z3.string().min(1)).optional(),
4548
+ target: z3.array(z3.string().min(1)).optional(),
4549
+ surface: z3.array(z3.enum(["mcp", "cli", "http-trigger", "job", "agent"])).optional(),
4490
4550
  env: z3.array(z3.string().min(1)).optional()
4491
4551
  }).strict();
4492
- function isEntryAvailable(availability, ctx) {
4552
+ var PROCESS_AXES = [
4553
+ // `os` and `platform` both compare against the same RuntimeContext value
4554
+ // — keeping `platform` as a deprecated alias lets pre-#417 metadata
4555
+ // continue to match without any code change.
4556
+ { key: "os", ctxKey: "os" },
4557
+ { key: "platform", ctxKey: "platform" },
4558
+ { key: "runtime", ctxKey: "runtime" },
4559
+ { key: "deployment", ctxKey: "deployment" },
4560
+ { key: "provider", ctxKey: "provider" },
4561
+ { key: "target", ctxKey: "target" },
4562
+ { key: "env", ctxKey: "env" }
4563
+ ];
4564
+ function isEntryAvailable(availability, ctx, callCtx) {
4493
4565
  if (!availability) return true;
4494
- const fields = ["platform", "runtime", "deployment", "env"];
4495
- for (const field of fields) {
4496
- const allowed = availability[field];
4566
+ for (const { key, ctxKey } of PROCESS_AXES) {
4567
+ const allowed = availability[key];
4497
4568
  if (allowed === void 0) continue;
4498
4569
  if (allowed.length === 0) return false;
4499
- if (!allowed.includes(ctx[field])) return false;
4570
+ if (!allowed.includes(ctx[ctxKey])) return false;
4571
+ }
4572
+ if (availability.surface !== void 0) {
4573
+ if (availability.surface.length === 0) return false;
4574
+ if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4575
+ return false;
4576
+ }
4500
4577
  }
4501
4578
  return true;
4502
4579
  }
4580
+ function checkEntryAvailability(availability, ctx, callCtx) {
4581
+ if (!availability) return { available: true, missingAxes: [] };
4582
+ const missing = [];
4583
+ for (const { key, ctxKey } of PROCESS_AXES) {
4584
+ const allowed = availability[key];
4585
+ if (allowed === void 0) continue;
4586
+ if (allowed.length === 0 || !allowed.includes(ctx[ctxKey])) {
4587
+ missing.push(key);
4588
+ }
4589
+ }
4590
+ if (availability.surface !== void 0) {
4591
+ if (availability.surface.length === 0) {
4592
+ missing.push("surface");
4593
+ } else if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4594
+ missing.push("surface");
4595
+ }
4596
+ }
4597
+ return { available: missing.length === 0, missingAxes: missing };
4598
+ }
4503
4599
  function detectRuntime() {
4504
4600
  if (typeof globalThis !== "undefined" && "Bun" in globalThis) return "bun";
4505
4601
  if (typeof globalThis !== "undefined" && "Deno" in globalThis) return "deno";
@@ -4516,22 +4612,26 @@ function detectDeployment(runtime) {
4516
4612
  }
4517
4613
  function detectRuntimeContext() {
4518
4614
  const runtime = detectRuntime();
4615
+ const os = process.platform;
4519
4616
  return {
4520
- platform: process.platform,
4617
+ os,
4618
+ platform: os,
4521
4619
  runtime,
4522
4620
  deployment: detectDeployment(runtime),
4621
+ provider: detectProvider(),
4622
+ target: getBuildTarget(),
4523
4623
  env: process.env["NODE_ENV"] || "development"
4524
4624
  };
4525
4625
  }
4526
- var cached;
4626
+ var cached3;
4527
4627
  function getRuntimeContext() {
4528
- if (!cached) {
4529
- cached = detectRuntimeContext();
4628
+ if (!cached3) {
4629
+ cached3 = detectRuntimeContext();
4530
4630
  }
4531
- return cached;
4631
+ return cached3;
4532
4632
  }
4533
4633
  function resetRuntimeContext() {
4534
- cached = void 0;
4634
+ cached3 = void 0;
4535
4635
  }
4536
4636
 
4537
4637
  // libs/utils/src/storage/factory.ts
@@ -5372,8 +5472,8 @@ function loadMachineIdSync() {
5372
5472
  if (!isDevPersistenceEnabled()) return null;
5373
5473
  const machineIdPath = resolveMachineIdPath();
5374
5474
  try {
5375
- const fs = __require("fs");
5376
- const content = fs.readFileSync(machineIdPath, "utf8").trim();
5475
+ const fs2 = __require("fs");
5476
+ const content = fs2.readFileSync(machineIdPath, "utf8").trim();
5377
5477
  if (/^[0-9a-f-]{32,36}$/i.test(content)) return content;
5378
5478
  console.warn(`[MachineId] Invalid format at ${machineIdPath}, will regenerate`);
5379
5479
  return null;
@@ -5472,6 +5572,7 @@ export {
5472
5572
  buildPrefix,
5473
5573
  buildSetCookie,
5474
5574
  bytesToHex,
5575
+ checkEntryAvailability,
5475
5576
  clearCachedSecret,
5476
5577
  collapseChar,
5477
5578
  collapseWhitespace,
@@ -5573,6 +5674,7 @@ export {
5573
5674
  parseUriTemplate,
5574
5675
  join as pathJoin,
5575
5676
  resolve as pathResolve,
5677
+ pathToFileURL,
5576
5678
  pemToPublicJwk2 as pemToPublicJwk,
5577
5679
  processPlatformToolCalls,
5578
5680
  randomBytes,
@@ -5582,6 +5684,7 @@ export {
5582
5684
  readFileSync,
5583
5685
  readJSON,
5584
5686
  readdir,
5687
+ readdirSync,
5585
5688
  realpath,
5586
5689
  rename,
5587
5690
  resetRuntimeContext,
package/fs/fs.d.ts CHANGED
@@ -228,6 +228,21 @@ export declare function cp(src: string, dest: string, options?: {
228
228
  * const files = await readdir('/path/to/dir');
229
229
  */
230
230
  export declare function readdir(p: string): Promise<string[]>;
231
+ /**
232
+ * List directory contents synchronously.
233
+ *
234
+ * **Node.js only** - throws an error if called in browser.
235
+ *
236
+ * Use this only when async operations are not possible (e.g., per-scrape
237
+ * synchronous metrics collectors). Prefer the async `readdir` in most cases.
238
+ *
239
+ * @param p - Path to directory
240
+ * @returns Array of file/directory names
241
+ *
242
+ * @example
243
+ * const files = readdirSync('/proc/self/fd');
244
+ */
245
+ export declare function readdirSync(p: string): string[];
231
246
  /**
232
247
  * Remove a file or directory recursively.
233
248
  *
@@ -296,4 +311,5 @@ export declare function access(p: string, mode?: number): Promise<void>;
296
311
  */
297
312
  export declare function runCmd(cmd: string, args: string[], opts?: {
298
313
  cwd?: string;
314
+ env?: NodeJS.ProcessEnv;
299
315
  }): Promise<void>;
package/fs/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { readFile, readFileSync, readFileBuffer, writeFile, mkdir, rename, unlink, stat, copyFile, cp, readdir, realpath, rm, mkdtemp, symlink, access, fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd, } from './fs';
1
+ export { readFile, readFileSync, readFileBuffer, writeFile, mkdir, rename, unlink, stat, copyFile, cp, readdir, readdirSync, realpath, rm, mkdtemp, symlink, access, fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd, } from './fs';
package/index.d.ts CHANGED
@@ -9,18 +9,18 @@ export { splitWords, toCase, sepFor, shortHash, ensureMaxLen, idFromString } fro
9
9
  export type { NameCase } from './naming';
10
10
  export { isValidMcpUri, extractUriScheme, isValidMcpUriTemplate, parseUriTemplate, matchUriTemplate, expandUriTemplate, extractTemplateParams, isUriTemplate, } from './uri';
11
11
  export type { ParsedUriTemplate } from './uri';
12
- export { trimSlashes, joinPath, pathResolve, pathJoin, basename, dirname, extname, isAbsolute } from './path';
12
+ export { trimSlashes, joinPath, pathResolve, pathJoin, basename, dirname, extname, isAbsolute, pathToFileURL, } from './path';
13
13
  export { sanitizeToJson, inferMimeType, findNonFiniteNumber } from './content';
14
14
  export { validateBaseUrl, buildSetCookie, parseCookies, getCookie, isLocalhost, isSecureRequest } from './http';
15
15
  export type { CookieOptions, SecureDetectionRequest } from './http';
16
- export { readFile, readFileSync, readFileBuffer, writeFile, mkdir, rename, unlink, stat, copyFile, cp, readdir, realpath, rm, mkdtemp, symlink, access, fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd, } from './fs';
16
+ export { readFile, readFileSync, readFileBuffer, writeFile, mkdir, rename, unlink, stat, copyFile, cp, readdir, readdirSync, realpath, rm, mkdtemp, symlink, access, fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd, } from './fs';
17
17
  export { escapeHtml, escapeHtmlAttr, escapeJsString, escapeScriptClose, safeJsonForScript } from './escape';
18
18
  export { safeStringify } from './serialization';
19
19
  export { getCrypto, rsaVerify, rsaVerifySync, pemToPublicJwk, generateRsaKeyPair, rsaSignBase64Url, type RsaJwk, type RsaKeyPair, jwtAlgToNodeAlg, isRsaPssAlg, randomUUID, randomBytes, sha256, sha256Hex, sha256Base64url, hmacSha256, hkdfSha256, encryptAesGcm, decryptAesGcm, timingSafeEqual, bytesToHex, base64urlEncode, base64urlDecode, base64Encode, base64Decode, isNode, isBrowser, assertNode, EncryptedBlobError, encryptValue, decryptValue, tryDecryptValue, serializeBlob, deserializeBlob, tryDeserializeBlob, isValidEncryptedBlob, encryptAndSerialize, deserializeAndDecrypt, tryDeserializeAndDecrypt, generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, generatePkcePair, isValidCodeVerifier, isValidCodeChallenge, MIN_CODE_VERIFIER_LENGTH, MAX_CODE_VERIFIER_LENGTH, DEFAULT_CODE_VERIFIER_LENGTH, PkceError, type PkcePair, type SecretData, type SecretPersistenceOptions, type SecretValidationResult, secretDataSchema, validateSecretData, parseSecretData, isSecretPersistenceEnabled, resolveSecretPath, loadSecret, saveSecret, deleteSecret, generateSecret, createSecretData, getOrCreateSecret, clearCachedSecret, isSecretCached, type SignedData, type HmacSigningConfig, signData, verifyData, isSignedData, verifyOrParseData, type BaseKeyData, type SecretKeyData, type AsymmetricKeyData, type AnyKeyData, type KeyPersistenceOptions, type CreateKeyPersistenceOptions, type CreateSecretOptions, type CreateAsymmetricOptions, type KeyValidationResult, asymmetricAlgSchema, secretKeyDataSchema, asymmetricKeyDataSchema, anyKeyDataSchema, validateKeyData, parseKeyData, isSecretKeyData, isAsymmetricKeyData, KeyPersistence, createKeyPersistence, createKeyPersistenceWithStorage, } from './crypto';
20
20
  export type { CryptoProvider, EncBlob, EncryptedBlob } from './crypto';
21
21
  export { AsyncLocalStorage } from './async-context';
22
22
  export { EventEmitter } from './event-emitter';
23
- export { getEnv, getCwd, isProduction, isDevelopment, getEnvFlag, isDebug, setEnv, isEdgeRuntime, isServerless, supportsAnsi, getRuntimeContext, resetRuntimeContext, detectRuntimeContext, isEntryAvailable, entryAvailabilitySchema, } from './env';
23
+ export { getEnv, getCwd, isProduction, isDevelopment, getEnvFlag, isDebug, setEnv, isEdgeRuntime, isServerless, supportsAnsi, getRuntimeContext, resetRuntimeContext, detectRuntimeContext, isEntryAvailable, checkEntryAvailability, entryAvailabilitySchema, } from './env';
24
24
  export type { RuntimeContext, EntryAvailability } from './env';
25
25
  export { analyzePattern, isPatternSafe, createSafeRegExp, safeTest, safeMatch, safeReplace, safeExec, isInputLengthSafe, DEFAULT_MAX_INPUT_LENGTH, REDOS_THRESHOLDS, trimLeading, trimTrailing, trimBoth, trimChars, extractBracedParams, expandTemplate, hasTemplatePlaceholders, collapseChar, collapseWhitespace, type SafeRegexOptions, type PatternAnalysisResult, } from './regex';
26
26
  export { createStorage, createMemoryStorage, getDetectedStorageType, NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, VercelKvStorageAdapter, UpstashStorageAdapter, FileSystemStorageAdapter, type FileSystemAdapterOptions, TypedStorage, type TypedStorageOptions, type TypedSetOptions, type TypedSetEntry, EncryptedTypedStorage, type EncryptedTypedStorageOptions, type EncryptedSetOptions, type EncryptedSetEntry, type EncryptionKey, type StoredEncryptedBlob, type ClientKeyBinding, globToRegex, matchesPattern, validatePattern, escapeGlob, MAX_TTL_SECONDS, validateTTL, validateOptionalTTL, ttlToExpiresAt, expiresAtToTTL, isExpired, normalizeTTL, } from './storage';
package/index.js CHANGED
@@ -75,8 +75,8 @@ async function readFile(p, encoding = "utf8") {
75
75
  return fsp.readFile(p, encoding);
76
76
  }
77
77
  function readFileSync(p, encoding = "utf8") {
78
- const fs = getFs();
79
- return fs.readFileSync(p, encoding);
78
+ const fs2 = getFs();
79
+ return fs2.readFileSync(p, encoding);
80
80
  }
81
81
  async function readFileBuffer(p) {
82
82
  const fsp = getFsp();
@@ -148,6 +148,10 @@ async function readdir(p) {
148
148
  const fsp = getFsp();
149
149
  return fsp.readdir(p);
150
150
  }
151
+ function readdirSync(p) {
152
+ const fs2 = getFs();
153
+ return fs2.readdirSync(p);
154
+ }
151
155
  async function rm(p, options) {
152
156
  const fsp = getFsp();
153
157
  await fsp.rm(p, options);
@@ -566,9 +570,9 @@ function createSecretData(options) {
566
570
  }
567
571
  async function getOrCreateSecret(options) {
568
572
  const cacheKey = resolveSecretPath(options);
569
- const cached2 = secretCache.get(cacheKey);
570
- if (cached2) {
571
- return cached2;
573
+ const cached4 = secretCache.get(cacheKey);
574
+ if (cached4) {
575
+ return cached4;
572
576
  }
573
577
  const existing = generationPromises.get(cacheKey);
574
578
  if (existing) {
@@ -806,8 +810,8 @@ var init_key_persistence = __esm({
806
810
  */
807
811
  async get(kid) {
808
812
  if (this.enableCache) {
809
- const cached2 = this.cache.get(kid);
810
- if (cached2) return cached2;
813
+ const cached4 = this.cache.get(kid);
814
+ if (cached4) return cached4;
811
815
  }
812
816
  const raw = await this.storage.get(kid);
813
817
  if (!raw) return null;
@@ -3834,6 +3838,7 @@ __export(index_exports, {
3834
3838
  buildPrefix: () => buildPrefix,
3835
3839
  buildSetCookie: () => buildSetCookie,
3836
3840
  bytesToHex: () => bytesToHex,
3841
+ checkEntryAvailability: () => checkEntryAvailability,
3837
3842
  clearCachedSecret: () => clearCachedSecret,
3838
3843
  collapseChar: () => collapseChar,
3839
3844
  collapseWhitespace: () => collapseWhitespace,
@@ -3935,6 +3940,7 @@ __export(index_exports, {
3935
3940
  parseUriTemplate: () => parseUriTemplate,
3936
3941
  pathJoin: () => import_path.join,
3937
3942
  pathResolve: () => import_path.resolve,
3943
+ pathToFileURL: () => import_url.pathToFileURL,
3938
3944
  pemToPublicJwk: () => pemToPublicJwk2,
3939
3945
  processPlatformToolCalls: () => processPlatformToolCalls,
3940
3946
  randomBytes: () => randomBytes,
@@ -3944,6 +3950,7 @@ __export(index_exports, {
3944
3950
  readFileSync: () => readFileSync,
3945
3951
  readJSON: () => readJSON,
3946
3952
  readdir: () => readdir,
3953
+ readdirSync: () => readdirSync,
3947
3954
  realpath: () => realpath,
3948
3955
  rename: () => rename,
3949
3956
  resetRuntimeContext: () => resetRuntimeContext,
@@ -4405,6 +4412,7 @@ function joinPath(...parts) {
4405
4412
 
4406
4413
  // libs/utils/src/path/node-path.ts
4407
4414
  var import_path = require("path");
4415
+ var import_url = require("url");
4408
4416
 
4409
4417
  // libs/utils/src/content/content.ts
4410
4418
  function sanitizeToJson(value) {
@@ -4692,23 +4700,114 @@ var import_env3 = require("#env");
4692
4700
  // libs/utils/src/env/runtime-context.ts
4693
4701
  var import_lazy_zod3 = require("@frontmcp/lazy-zod");
4694
4702
  var import_env2 = require("#env");
4703
+
4704
+ // libs/utils/src/env/build-target.ts
4705
+ var cached;
4706
+ function getBuildTarget() {
4707
+ if (cached) return cached;
4708
+ cached = resolveBuildTarget();
4709
+ return cached;
4710
+ }
4711
+ function resolveBuildTarget() {
4712
+ const globalAny = globalThis;
4713
+ const fromGlobal = globalAny.FRONTMCP_BUILD_TARGET;
4714
+ if (typeof fromGlobal === "string" && isKnownTarget(fromGlobal)) return fromGlobal;
4715
+ const fromEnv = typeof process !== "undefined" ? process.env?.["FRONTMCP_BUILD_TARGET"] : void 0;
4716
+ if (typeof fromEnv === "string" && isKnownTarget(fromEnv)) return fromEnv;
4717
+ return "unknown";
4718
+ }
4719
+ function isKnownTarget(value) {
4720
+ return value === "unknown" || value === "node" || value === "distributed" || value === "cli" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "browser" || value === "sdk" || value === "mcpb";
4721
+ }
4722
+
4723
+ // libs/utils/src/env/provider.ts
4724
+ var fs = __toESM(require("node:fs"));
4725
+ var cached2;
4726
+ function detectProvider() {
4727
+ if (cached2) return cached2;
4728
+ cached2 = detectProviderUncached();
4729
+ return cached2;
4730
+ }
4731
+ function detectProviderUncached() {
4732
+ const explicit = process.env["FRONTMCP_PROVIDER"];
4733
+ if (explicit && isKnownProvider(explicit)) return explicit;
4734
+ if (process.env["VERCEL"]) return "vercel";
4735
+ if (process.env["AWS_LAMBDA_FUNCTION_NAME"]) return "lambda";
4736
+ if (process.env["CF_PAGES"]) return "cloudflare";
4737
+ if (process.env["NETLIFY"]) return "netlify";
4738
+ if (process.env["AZURE_FUNCTIONS_ENVIRONMENT"]) return "azure";
4739
+ if (process.env["K_SERVICE"]) return "gcp";
4740
+ if (process.env["FLY_APP_NAME"]) return "fly";
4741
+ if (process.env["RENDER"]) return "render";
4742
+ if (process.env["RAILWAY_ENVIRONMENT"]) return "railway";
4743
+ try {
4744
+ if (fs.existsSync("/.dockerenv")) return "docker";
4745
+ } catch {
4746
+ }
4747
+ return "bare";
4748
+ }
4749
+ function isKnownProvider(value) {
4750
+ return value === "bare" || value === "docker" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "netlify" || value === "azure" || value === "gcp" || value === "fly" || value === "render" || value === "railway";
4751
+ }
4752
+
4753
+ // libs/utils/src/env/runtime-context.ts
4695
4754
  var entryAvailabilitySchema = import_lazy_zod3.z.object({
4755
+ os: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4696
4756
  platform: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4697
4757
  runtime: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4698
4758
  deployment: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4759
+ provider: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4760
+ target: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
4761
+ surface: import_lazy_zod3.z.array(import_lazy_zod3.z.enum(["mcp", "cli", "http-trigger", "job", "agent"])).optional(),
4699
4762
  env: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional()
4700
4763
  }).strict();
4701
- function isEntryAvailable(availability, ctx) {
4764
+ var PROCESS_AXES = [
4765
+ // `os` and `platform` both compare against the same RuntimeContext value
4766
+ // — keeping `platform` as a deprecated alias lets pre-#417 metadata
4767
+ // continue to match without any code change.
4768
+ { key: "os", ctxKey: "os" },
4769
+ { key: "platform", ctxKey: "platform" },
4770
+ { key: "runtime", ctxKey: "runtime" },
4771
+ { key: "deployment", ctxKey: "deployment" },
4772
+ { key: "provider", ctxKey: "provider" },
4773
+ { key: "target", ctxKey: "target" },
4774
+ { key: "env", ctxKey: "env" }
4775
+ ];
4776
+ function isEntryAvailable(availability, ctx, callCtx) {
4702
4777
  if (!availability) return true;
4703
- const fields = ["platform", "runtime", "deployment", "env"];
4704
- for (const field of fields) {
4705
- const allowed = availability[field];
4778
+ for (const { key, ctxKey } of PROCESS_AXES) {
4779
+ const allowed = availability[key];
4706
4780
  if (allowed === void 0) continue;
4707
4781
  if (allowed.length === 0) return false;
4708
- if (!allowed.includes(ctx[field])) return false;
4782
+ if (!allowed.includes(ctx[ctxKey])) return false;
4783
+ }
4784
+ if (availability.surface !== void 0) {
4785
+ if (availability.surface.length === 0) return false;
4786
+ if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4787
+ return false;
4788
+ }
4709
4789
  }
4710
4790
  return true;
4711
4791
  }
4792
+ function checkEntryAvailability(availability, ctx, callCtx) {
4793
+ if (!availability) return { available: true, missingAxes: [] };
4794
+ const missing = [];
4795
+ for (const { key, ctxKey } of PROCESS_AXES) {
4796
+ const allowed = availability[key];
4797
+ if (allowed === void 0) continue;
4798
+ if (allowed.length === 0 || !allowed.includes(ctx[ctxKey])) {
4799
+ missing.push(key);
4800
+ }
4801
+ }
4802
+ if (availability.surface !== void 0) {
4803
+ if (availability.surface.length === 0) {
4804
+ missing.push("surface");
4805
+ } else if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4806
+ missing.push("surface");
4807
+ }
4808
+ }
4809
+ return { available: missing.length === 0, missingAxes: missing };
4810
+ }
4712
4811
  function detectRuntime() {
4713
4812
  if (typeof globalThis !== "undefined" && "Bun" in globalThis) return "bun";
4714
4813
  if (typeof globalThis !== "undefined" && "Deno" in globalThis) return "deno";
@@ -4725,22 +4824,26 @@ function detectDeployment(runtime) {
4725
4824
  }
4726
4825
  function detectRuntimeContext() {
4727
4826
  const runtime = detectRuntime();
4827
+ const os = process.platform;
4728
4828
  return {
4729
- platform: process.platform,
4829
+ os,
4830
+ platform: os,
4730
4831
  runtime,
4731
4832
  deployment: detectDeployment(runtime),
4833
+ provider: detectProvider(),
4834
+ target: getBuildTarget(),
4732
4835
  env: process.env["NODE_ENV"] || "development"
4733
4836
  };
4734
4837
  }
4735
- var cached;
4838
+ var cached3;
4736
4839
  function getRuntimeContext() {
4737
- if (!cached) {
4738
- cached = detectRuntimeContext();
4840
+ if (!cached3) {
4841
+ cached3 = detectRuntimeContext();
4739
4842
  }
4740
- return cached;
4843
+ return cached3;
4741
4844
  }
4742
4845
  function resetRuntimeContext() {
4743
- cached = void 0;
4846
+ cached3 = void 0;
4744
4847
  }
4745
4848
 
4746
4849
  // libs/utils/src/storage/factory.ts
@@ -5581,8 +5684,8 @@ function loadMachineIdSync() {
5581
5684
  if (!isDevPersistenceEnabled()) return null;
5582
5685
  const machineIdPath = resolveMachineIdPath();
5583
5686
  try {
5584
- const fs = require("fs");
5585
- const content = fs.readFileSync(machineIdPath, "utf8").trim();
5687
+ const fs2 = require("fs");
5688
+ const content = fs2.readFileSync(machineIdPath, "utf8").trim();
5586
5689
  if (/^[0-9a-f-]{32,36}$/i.test(content)) return content;
5587
5690
  console.warn(`[MachineId] Invalid format at ${machineIdPath}, will regenerate`);
5588
5691
  return null;
@@ -5682,6 +5785,7 @@ function setMachineIdOverride(id) {
5682
5785
  buildPrefix,
5683
5786
  buildSetCookie,
5684
5787
  bytesToHex,
5788
+ checkEntryAvailability,
5685
5789
  clearCachedSecret,
5686
5790
  collapseChar,
5687
5791
  collapseWhitespace,
@@ -5783,6 +5887,7 @@ function setMachineIdOverride(id) {
5783
5887
  parseUriTemplate,
5784
5888
  pathJoin,
5785
5889
  pathResolve,
5890
+ pathToFileURL,
5786
5891
  pemToPublicJwk,
5787
5892
  processPlatformToolCalls,
5788
5893
  randomBytes,
@@ -5792,6 +5897,7 @@ function setMachineIdOverride(id) {
5792
5897
  readFileSync,
5793
5898
  readJSON,
5794
5899
  readdir,
5900
+ readdirSync,
5795
5901
  realpath,
5796
5902
  rename,
5797
5903
  resetRuntimeContext,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/utils",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Shared utility functions for FrontMCP - string manipulation, URI handling, path utilities, and more",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  "peerDependencies": {
35
35
  "@vercel/kv": "^2.0.0 || ^3.0.0",
36
36
  "ioredis": "^5.0.0",
37
- "@frontmcp/lazy-zod": "1.2.1"
37
+ "@frontmcp/lazy-zod": "1.3.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@vercel/kv": {
@@ -4,3 +4,4 @@ export declare function basename(p: string, ext?: string): string;
4
4
  export declare function dirname(_p: string): string;
5
5
  export declare function extname(p: string): string;
6
6
  export declare function isAbsolute(p: string): boolean;
7
+ export declare function pathToFileURL(_p: string): URL;
package/path/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { trimSlashes, joinPath } from './path';
2
- export { resolve as pathResolve, join as pathJoin, basename, dirname, extname, isAbsolute } from '#path';
2
+ export { resolve as pathResolve, join as pathJoin, basename, dirname, extname, isAbsolute, pathToFileURL } from '#path';
@@ -1 +1,2 @@
1
1
  export { resolve, join, basename, dirname, extname, isAbsolute } from 'path';
2
+ export { pathToFileURL } from 'url';