@cloudflare/sandbox 0.6.10 → 0.7.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.
@@ -10,6 +10,45 @@ function getEnvString(env, key) {
10
10
  const value = env?.[key];
11
11
  return typeof value === "string" ? value : void 0;
12
12
  }
13
+ /**
14
+ * Filter environment variables object to only include string values.
15
+ * Skips undefined, null, and non-string values.
16
+ *
17
+ * Use this when you only need the defined values (e.g., for per-command env
18
+ * where undefined means "don't override").
19
+ *
20
+ * @param envVars - Object that may contain undefined values
21
+ * @returns Clean object with only string values
22
+ */
23
+ function filterEnvVars(envVars) {
24
+ const filtered = {};
25
+ for (const [key, value] of Object.entries(envVars)) if (value != null && typeof value === "string") filtered[key] = value;
26
+ return filtered;
27
+ }
28
+ /**
29
+ * Partition environment variables into values to set and keys to unset.
30
+ *
31
+ * - String values → toSet (will be exported)
32
+ * - undefined/null → toUnset (will be unset)
33
+ *
34
+ * This enables idiomatic JS patterns where undefined means "remove":
35
+ * ```typescript
36
+ * await sandbox.setEnvVars({
37
+ * API_KEY: 'new-key', // will be set
38
+ * OLD_VAR: undefined, // will be unset
39
+ * });
40
+ * ```
41
+ */
42
+ function partitionEnvVars(envVars) {
43
+ const toSet = {};
44
+ const toUnset = [];
45
+ for (const [key, value] of Object.entries(envVars)) if (value != null && typeof value === "string") toSet[key] = value;
46
+ else toUnset.push(key);
47
+ return {
48
+ toSet,
49
+ toUnset
50
+ };
51
+ }
13
52
 
14
53
  //#endregion
15
54
  //#region ../shared/dist/git.js
@@ -670,5 +709,5 @@ function generateRequestId() {
670
709
  }
671
710
 
672
711
  //#endregion
673
- export { isExecResult as a, shellEscape as c, TraceContext as d, Execution as f, getEnvString as g, extractRepoName as h, isWSStreamChunk as i, createLogger as l, GitLogger as m, isWSError as n, isProcess as o, ResultImpl as p, isWSResponse as r, isProcessStatus as s, generateRequestId as t, createNoOpLogger as u };
674
- //# sourceMappingURL=dist-c_xYW5i_.js.map
712
+ export { getEnvString as _, isExecResult as a, shellEscape as c, TraceContext as d, Execution as f, filterEnvVars as g, extractRepoName as h, isWSStreamChunk as i, createLogger as l, GitLogger as m, isWSError as n, isProcess as o, ResultImpl as p, isWSResponse as r, isProcessStatus as s, generateRequestId as t, createNoOpLogger as u, partitionEnvVars as v };
713
+ //# sourceMappingURL=dist-BpbdH8ry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dist-BpbdH8ry.js","names":["LogLevelEnum","LogLevelEnum"],"sources":["../../shared/dist/env.js","../../shared/dist/git.js","../../shared/dist/interpreter-types.js","../../shared/dist/logger/types.js","../../shared/dist/logger/logger.js","../../shared/dist/logger/trace-context.js","../../shared/dist/logger/index.js","../../shared/dist/shell-escape.js","../../shared/dist/types.js","../../shared/dist/ws-types.js"],"sourcesContent":["/**\n * Safely extract a string value from an environment object\n *\n * @param env - Environment object with dynamic keys\n * @param key - The environment variable key to access\n * @returns The string value if present and is a string, undefined otherwise\n */\nexport function getEnvString(env, key) {\n const value = env?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n/**\n * Filter environment variables object to only include string values.\n * Skips undefined, null, and non-string values.\n *\n * Use this when you only need the defined values (e.g., for per-command env\n * where undefined means \"don't override\").\n *\n * @param envVars - Object that may contain undefined values\n * @returns Clean object with only string values\n */\nexport function filterEnvVars(envVars) {\n const filtered = {};\n for (const [key, value] of Object.entries(envVars)) {\n if (value != null && typeof value === 'string') {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n/**\n * Partition environment variables into values to set and keys to unset.\n *\n * - String values → toSet (will be exported)\n * - undefined/null → toUnset (will be unset)\n *\n * This enables idiomatic JS patterns where undefined means \"remove\":\n * ```typescript\n * await sandbox.setEnvVars({\n * API_KEY: 'new-key', // will be set\n * OLD_VAR: undefined, // will be unset\n * });\n * ```\n */\nexport function partitionEnvVars(envVars) {\n const toSet = {};\n const toUnset = [];\n for (const [key, value] of Object.entries(envVars)) {\n if (value != null && typeof value === 'string') {\n toSet[key] = value;\n }\n else {\n toUnset.push(key);\n }\n }\n return { toSet, toUnset };\n}\n","/**\n * Fallback repository name used when URL parsing fails\n */\nexport const FALLBACK_REPO_NAME = 'repository';\n/**\n * Extract repository name from a Git URL\n *\n * Supports multiple URL formats:\n * - HTTPS: https://github.com/user/repo.git → repo\n * - HTTPS without .git: https://github.com/user/repo → repo\n * - SSH: git@github.com:user/repo.git → repo\n * - GitLab/others: https://gitlab.com/org/project.git → project\n *\n * @param repoUrl - Git repository URL (HTTPS or SSH format)\n * @returns Repository name extracted from URL, or 'repository' as fallback\n */\nexport function extractRepoName(repoUrl) {\n // Try parsing as standard URL (https://, http://)\n try {\n const url = new URL(repoUrl);\n const pathParts = url.pathname.split('/');\n const lastPart = pathParts[pathParts.length - 1];\n if (lastPart) {\n return lastPart.replace(/\\.git$/, '');\n }\n }\n catch {\n // Not a standard URL, try SSH format\n }\n // For SSH URLs (git@github.com:user/repo.git), split by : and / to get last segment\n // Only process if the URL contains path delimiters\n if (repoUrl.includes(':') || repoUrl.includes('/')) {\n const segments = repoUrl.split(/[:/]/).filter(Boolean);\n const lastSegment = segments[segments.length - 1];\n if (lastSegment) {\n return lastSegment.replace(/\\.git$/, '');\n }\n }\n return FALLBACK_REPO_NAME;\n}\n/**\n * Redact credentials from URLs for secure logging\n *\n * Replaces any credentials (username:password, tokens, etc.) embedded\n * in URLs with ****** to prevent sensitive data exposure in logs.\n * Works with URLs embedded in text (e.g., \"Error: https://token@github.com/repo.git failed\")\n *\n * @param text - String that may contain URLs with credentials\n * @returns String with credentials redacted from any URLs\n */\nexport function redactCredentials(text) {\n // Scan for http(s):// URLs and redact any credentials found\n let result = text;\n let pos = 0;\n while (pos < result.length) {\n const httpPos = result.indexOf('http://', pos);\n const httpsPos = result.indexOf('https://', pos);\n let protocolPos = -1;\n let protocolLen = 0;\n if (httpPos === -1 && httpsPos === -1)\n break;\n if (httpPos !== -1 && (httpsPos === -1 || httpPos < httpsPos)) {\n protocolPos = httpPos;\n protocolLen = 7; // 'http://'.length\n }\n else {\n protocolPos = httpsPos;\n protocolLen = 8; // 'https://'.length\n }\n // Look for @ after the protocol\n const searchStart = protocolPos + protocolLen;\n const atPos = result.indexOf('@', searchStart);\n // Find where the URL ends (whitespace, quotes, or structural delimiters)\n let urlEnd = searchStart;\n while (urlEnd < result.length) {\n const char = result[urlEnd];\n if (/[\\s\"'`<>,;{}[\\]]/.test(char))\n break;\n urlEnd++;\n }\n if (atPos !== -1 && atPos < urlEnd) {\n result = `${result.substring(0, searchStart)}******${result.substring(atPos)}`;\n pos = searchStart + 6; // Move past '******'\n }\n else {\n pos = protocolPos + protocolLen;\n }\n }\n return result;\n}\n/**\n * Sanitize data by redacting credentials from any strings\n * Recursively processes objects and arrays to ensure credentials are never leaked\n */\nexport function sanitizeGitData(data) {\n // Handle primitives\n if (typeof data === 'string') {\n return redactCredentials(data);\n }\n if (data === null || data === undefined) {\n return data;\n }\n // Handle arrays\n if (Array.isArray(data)) {\n return data.map((item) => sanitizeGitData(item));\n }\n // Handle objects - recursively sanitize all fields\n if (typeof data === 'object') {\n const result = {};\n for (const [key, value] of Object.entries(data)) {\n result[key] = sanitizeGitData(value);\n }\n return result;\n }\n return data;\n}\n/**\n * Logger wrapper that automatically sanitizes git credentials\n */\nexport class GitLogger {\n baseLogger;\n constructor(baseLogger) {\n this.baseLogger = baseLogger;\n }\n sanitizeContext(context) {\n return context\n ? sanitizeGitData(context)\n : context;\n }\n sanitizeError(error) {\n if (!error)\n return error;\n // Create a new error with sanitized message and stack\n const sanitized = new Error(redactCredentials(error.message));\n sanitized.name = error.name;\n if (error.stack) {\n sanitized.stack = redactCredentials(error.stack);\n }\n // Preserve other enumerable properties\n const sanitizedRecord = sanitized;\n const errorRecord = error;\n for (const key of Object.keys(error)) {\n if (key !== 'message' && key !== 'stack' && key !== 'name') {\n sanitizedRecord[key] = sanitizeGitData(errorRecord[key]);\n }\n }\n return sanitized;\n }\n debug(message, context) {\n this.baseLogger.debug(message, this.sanitizeContext(context));\n }\n info(message, context) {\n this.baseLogger.info(message, this.sanitizeContext(context));\n }\n warn(message, context) {\n this.baseLogger.warn(message, this.sanitizeContext(context));\n }\n error(message, error, context) {\n this.baseLogger.error(message, this.sanitizeError(error), this.sanitizeContext(context));\n }\n child(context) {\n const sanitized = sanitizeGitData(context);\n const childLogger = this.baseLogger.child(sanitized);\n return new GitLogger(childLogger);\n }\n}\n","// Execution Result Container\nexport class Execution {\n code;\n context;\n /**\n * All results from the execution\n */\n results = [];\n /**\n * Accumulated stdout and stderr\n */\n logs = {\n stdout: [],\n stderr: []\n };\n /**\n * Execution error if any\n */\n error;\n /**\n * Execution count (for interpreter)\n */\n executionCount;\n constructor(code, context) {\n this.code = code;\n this.context = context;\n }\n /**\n * Convert to a plain object for serialization\n */\n toJSON() {\n return {\n code: this.code,\n logs: this.logs,\n error: this.error,\n executionCount: this.executionCount,\n results: this.results.map((result) => ({\n text: result.text,\n html: result.html,\n png: result.png,\n jpeg: result.jpeg,\n svg: result.svg,\n latex: result.latex,\n markdown: result.markdown,\n javascript: result.javascript,\n json: result.json,\n chart: result.chart,\n data: result.data\n }))\n };\n }\n}\n// Implementation of Result\nexport class ResultImpl {\n raw;\n constructor(raw) {\n this.raw = raw;\n }\n get text() {\n return this.raw.text || this.raw.data?.['text/plain'];\n }\n get html() {\n return this.raw.html || this.raw.data?.['text/html'];\n }\n get png() {\n return this.raw.png || this.raw.data?.['image/png'];\n }\n get jpeg() {\n return this.raw.jpeg || this.raw.data?.['image/jpeg'];\n }\n get svg() {\n return this.raw.svg || this.raw.data?.['image/svg+xml'];\n }\n get latex() {\n return this.raw.latex || this.raw.data?.['text/latex'];\n }\n get markdown() {\n return this.raw.markdown || this.raw.data?.['text/markdown'];\n }\n get javascript() {\n return this.raw.javascript || this.raw.data?.['application/javascript'];\n }\n get json() {\n return this.raw.json || this.raw.data?.['application/json'];\n }\n get chart() {\n return this.raw.chart;\n }\n get data() {\n return this.raw.data;\n }\n formats() {\n const formats = [];\n if (this.text)\n formats.push('text');\n if (this.html)\n formats.push('html');\n if (this.png)\n formats.push('png');\n if (this.jpeg)\n formats.push('jpeg');\n if (this.svg)\n formats.push('svg');\n if (this.latex)\n formats.push('latex');\n if (this.markdown)\n formats.push('markdown');\n if (this.javascript)\n formats.push('javascript');\n if (this.json)\n formats.push('json');\n if (this.chart)\n formats.push('chart');\n return formats;\n }\n}\n","/**\n * Logger types for Cloudflare Sandbox SDK\n *\n * Provides structured, trace-aware logging across Worker, Durable Object, and Container.\n */\n/**\n * Log levels (from most to least verbose)\n */\nexport var LogLevel;\n(function (LogLevel) {\n LogLevel[LogLevel[\"DEBUG\"] = 0] = \"DEBUG\";\n LogLevel[LogLevel[\"INFO\"] = 1] = \"INFO\";\n LogLevel[LogLevel[\"WARN\"] = 2] = \"WARN\";\n LogLevel[LogLevel[\"ERROR\"] = 3] = \"ERROR\";\n})(LogLevel || (LogLevel = {}));\n","/**\n * Logger implementation\n */\nimport { LogLevel as LogLevelEnum } from './types.js';\n/**\n * ANSI color codes for terminal output\n */\nconst COLORS = {\n reset: '\\x1b[0m',\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n dim: '\\x1b[2m' // Dim\n};\n/**\n * CloudflareLogger implements structured logging with support for\n * both JSON output (production) and pretty printing (development).\n */\nexport class CloudflareLogger {\n baseContext;\n minLevel;\n pretty;\n /**\n * Create a new CloudflareLogger\n *\n * @param baseContext Base context included in all log entries\n * @param minLevel Minimum log level to output (default: INFO)\n * @param pretty Enable pretty printing for human-readable output (default: false)\n */\n constructor(baseContext, minLevel = LogLevelEnum.INFO, pretty = false) {\n this.baseContext = baseContext;\n this.minLevel = minLevel;\n this.pretty = pretty;\n }\n /**\n * Log debug-level message\n */\n debug(message, context) {\n if (this.shouldLog(LogLevelEnum.DEBUG)) {\n const logData = this.buildLogData('debug', message, context);\n this.output(console.log, logData);\n }\n }\n /**\n * Log info-level message\n */\n info(message, context) {\n if (this.shouldLog(LogLevelEnum.INFO)) {\n const logData = this.buildLogData('info', message, context);\n this.output(console.log, logData);\n }\n }\n /**\n * Log warning-level message\n */\n warn(message, context) {\n if (this.shouldLog(LogLevelEnum.WARN)) {\n const logData = this.buildLogData('warn', message, context);\n this.output(console.warn, logData);\n }\n }\n /**\n * Log error-level message\n */\n error(message, error, context) {\n if (this.shouldLog(LogLevelEnum.ERROR)) {\n const logData = this.buildLogData('error', message, context, error);\n this.output(console.error, logData);\n }\n }\n /**\n * Create a child logger with additional context\n */\n child(context) {\n return new CloudflareLogger({ ...this.baseContext, ...context }, this.minLevel, this.pretty);\n }\n /**\n * Check if a log level should be output\n */\n shouldLog(level) {\n return level >= this.minLevel;\n }\n /**\n * Build log data object\n */\n buildLogData(level, message, context, error) {\n const logData = {\n level,\n msg: message,\n ...this.baseContext,\n ...context,\n timestamp: new Date().toISOString()\n };\n // Add error details if provided\n if (error) {\n logData.error = {\n message: error.message,\n stack: error.stack,\n name: error.name\n };\n }\n return logData;\n }\n /**\n * Output log data to console (pretty or JSON)\n */\n output(consoleFn, data) {\n if (this.pretty) {\n this.outputPretty(consoleFn, data);\n }\n else {\n this.outputJson(consoleFn, data);\n }\n }\n /**\n * Output as JSON (production)\n */\n outputJson(consoleFn, data) {\n consoleFn(JSON.stringify(data));\n }\n /**\n * Output as pretty-printed, colored text (development)\n *\n * Format: LEVEL [component] message (trace: tr_...) {context}\n * Example: INFO [sandbox-do] Command started (trace: tr_7f3a9b2c) {commandId: \"cmd-123\"}\n */\n outputPretty(consoleFn, data) {\n const { level, msg, timestamp, traceId, component, sandboxId, sessionId, processId, commandId, operation, duration, error, ...rest } = data;\n // Build the main log line\n const levelStr = String(level || 'INFO').toUpperCase();\n const levelColor = this.getLevelColor(levelStr);\n const componentBadge = component ? `[${component}]` : '';\n const traceIdShort = traceId ? String(traceId).substring(0, 12) : '';\n // Start with level and component\n let logLine = `${levelColor}${levelStr.padEnd(5)}${COLORS.reset} ${componentBadge} ${msg}`;\n // Add trace ID if present\n if (traceIdShort) {\n logLine += ` ${COLORS.dim}(trace: ${traceIdShort})${COLORS.reset}`;\n }\n // Collect important context fields\n const contextFields = [];\n if (operation)\n contextFields.push(`operation: ${operation}`);\n if (commandId)\n contextFields.push(`commandId: ${String(commandId).substring(0, 12)}`);\n if (sandboxId)\n contextFields.push(`sandboxId: ${sandboxId}`);\n if (sessionId)\n contextFields.push(`sessionId: ${String(sessionId).substring(0, 12)}`);\n if (processId)\n contextFields.push(`processId: ${processId}`);\n if (duration !== undefined)\n contextFields.push(`duration: ${duration}ms`);\n // Add important context inline\n if (contextFields.length > 0) {\n logLine += ` ${COLORS.dim}{${contextFields.join(', ')}}${COLORS.reset}`;\n }\n // Output main log line\n consoleFn(logLine);\n // Output error details on separate lines if present\n if (error && typeof error === 'object') {\n const errorObj = error;\n if (errorObj.message) {\n consoleFn(` ${COLORS.error}Error: ${errorObj.message}${COLORS.reset}`);\n }\n if (errorObj.stack) {\n consoleFn(` ${COLORS.dim}${errorObj.stack}${COLORS.reset}`);\n }\n }\n // Output additional context if present\n if (Object.keys(rest).length > 0) {\n consoleFn(` ${COLORS.dim}${JSON.stringify(rest, null, 2)}${COLORS.reset}`);\n }\n }\n /**\n * Get ANSI color code for log level\n */\n getLevelColor(level) {\n const levelLower = level.toLowerCase();\n switch (levelLower) {\n case 'debug':\n return COLORS.debug;\n case 'info':\n return COLORS.info;\n case 'warn':\n return COLORS.warn;\n case 'error':\n return COLORS.error;\n default:\n return COLORS.reset;\n }\n }\n}\n","/**\n * Trace context utilities for request correlation\n *\n * Trace IDs enable correlating logs across distributed components:\n * Worker → Durable Object → Container → back\n *\n * The trace ID is propagated via the X-Trace-Id HTTP header.\n */\n/**\n * Utility for managing trace context across distributed components\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: Keep as class for namespace grouping and discoverability\nexport class TraceContext {\n /**\n * HTTP header name for trace ID propagation\n */\n static TRACE_HEADER = 'X-Trace-Id';\n /**\n * Generate a new trace ID\n *\n * Format: \"tr_\" + 16 random hex characters\n * Example: \"tr_7f3a9b2c4e5d6f1a\"\n *\n * @returns Newly generated trace ID\n */\n static generate() {\n // Use crypto.randomUUID() for randomness, extract 16 hex chars\n const randomHex = crypto.randomUUID().replace(/-/g, '').substring(0, 16);\n return `tr_${randomHex}`;\n }\n /**\n * Extract trace ID from HTTP request headers\n *\n * @param headers Request headers\n * @returns Trace ID if present, null otherwise\n */\n static fromHeaders(headers) {\n return headers.get(TraceContext.TRACE_HEADER);\n }\n /**\n * Create headers object with trace ID for outgoing requests\n *\n * @param traceId Trace ID to include\n * @returns Headers object with X-Trace-Id set\n */\n static toHeaders(traceId) {\n return { [TraceContext.TRACE_HEADER]: traceId };\n }\n /**\n * Get the header name used for trace ID propagation\n *\n * @returns Header name (\"X-Trace-Id\")\n */\n static getHeaderName() {\n return TraceContext.TRACE_HEADER;\n }\n}\n","/**\n * Logger module\n *\n * Provides structured, trace-aware logging with:\n * - Explicit logger passing via constructor injection\n * - Pretty printing for local development\n * - JSON output for production\n * - Environment auto-detection\n * - Log level configuration\n *\n * Usage:\n *\n * ```typescript\n * // Create a logger at entry point\n * const logger = createLogger({ component: 'sandbox-do', traceId: 'tr_abc123' });\n *\n * // Pass to classes via constructor\n * const service = new MyService(logger);\n *\n * // Create child loggers for additional context\n * const execLogger = logger.child({ operation: 'exec', commandId: 'cmd-456' });\n * execLogger.info('Operation started');\n * ```\n */\nimport { CloudflareLogger } from './logger.js';\nimport { TraceContext } from './trace-context.js';\nimport { LogLevel as LogLevelEnum } from './types.js';\nexport { CloudflareLogger } from './logger.js';\nexport { TraceContext } from './trace-context.js';\nexport { LogLevel as LogLevelEnum } from './types.js';\n/**\n * Create a no-op logger for testing\n *\n * Returns a logger that implements the Logger interface but does nothing.\n * Useful for tests that don't need actual logging output.\n *\n * @returns No-op logger instance\n *\n * @example\n * ```typescript\n * // In tests\n * const client = new HttpClient({\n * baseUrl: 'http://test.com',\n * logger: createNoOpLogger() // Optional - tests can enable real logging if needed\n * });\n * ```\n */\nexport function createNoOpLogger() {\n return {\n debug: () => { },\n info: () => { },\n warn: () => { },\n error: () => { },\n child: () => createNoOpLogger()\n };\n}\n/**\n * Create a new logger instance\n *\n * @param context Base context for the logger. Must include 'component'.\n * TraceId will be auto-generated if not provided.\n * @returns New logger instance\n *\n * @example\n * ```typescript\n * // In Durable Object\n * const logger = createLogger({\n * component: 'sandbox-do',\n * traceId: TraceContext.fromHeaders(request.headers) || TraceContext.generate(),\n * sandboxId: this.id\n * });\n *\n * // In Container\n * const logger = createLogger({\n * component: 'container',\n * traceId: TraceContext.fromHeaders(request.headers)!,\n * sessionId: this.id\n * });\n * ```\n */\nexport function createLogger(context) {\n const minLevel = getLogLevelFromEnv();\n const pretty = isPrettyPrintEnabled();\n const baseContext = {\n ...context,\n traceId: context.traceId || TraceContext.generate(),\n component: context.component\n };\n return new CloudflareLogger(baseContext, minLevel, pretty);\n}\n/**\n * Get log level from environment variable\n *\n * Checks SANDBOX_LOG_LEVEL env var, falls back to default based on environment.\n * Default: 'debug' for development, 'info' for production\n */\nfunction getLogLevelFromEnv() {\n const envLevel = getEnvVar('SANDBOX_LOG_LEVEL') || 'info';\n switch (envLevel.toLowerCase()) {\n case 'debug':\n return LogLevelEnum.DEBUG;\n case 'info':\n return LogLevelEnum.INFO;\n case 'warn':\n return LogLevelEnum.WARN;\n case 'error':\n return LogLevelEnum.ERROR;\n default:\n // Invalid level, fall back to info\n return LogLevelEnum.INFO;\n }\n}\n/**\n * Check if pretty printing should be enabled\n *\n * Checks SANDBOX_LOG_FORMAT env var, falls back to auto-detection:\n * - Local development: pretty (colored, human-readable)\n * - Production: json (structured)\n */\nfunction isPrettyPrintEnabled() {\n // Check explicit SANDBOX_LOG_FORMAT env var\n const format = getEnvVar('SANDBOX_LOG_FORMAT');\n if (format) {\n return format.toLowerCase() === 'pretty';\n }\n return false;\n}\n/**\n * Get environment variable value\n *\n * Supports both Node.js (process.env) and Bun (Bun.env)\n */\nfunction getEnvVar(name) {\n // Try process.env first (Node.js / Bun)\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n // Try Bun.env (Bun runtime)\n if (typeof Bun !== 'undefined') {\n const bunEnv = Bun.env;\n if (bunEnv) {\n return bunEnv[name];\n }\n }\n return undefined;\n}\n","/**\n * Escapes a string for safe use in shell commands using POSIX single-quote escaping.\n * Prevents command injection by wrapping the string in single quotes and escaping\n * any single quotes within the string.\n */\nexport function shellEscape(str) {\n return `'${str.replace(/'/g, \"'\\\\''\")}'`;\n}\n","/**\n * Check if a process status indicates the process has terminated\n */\nexport function isTerminalStatus(status) {\n return (status === 'completed' ||\n status === 'failed' ||\n status === 'killed' ||\n status === 'error');\n}\n// Type guards for runtime validation\nexport function isExecResult(value) {\n return (value &&\n typeof value.success === 'boolean' &&\n typeof value.exitCode === 'number' &&\n typeof value.stdout === 'string' &&\n typeof value.stderr === 'string');\n}\nexport function isProcess(value) {\n return (value &&\n typeof value.id === 'string' &&\n typeof value.command === 'string' &&\n typeof value.status === 'string');\n}\nexport function isProcessStatus(value) {\n return [\n 'starting',\n 'running',\n 'completed',\n 'failed',\n 'killed',\n 'error'\n ].includes(value);\n}\n// Re-export interpreter types for convenience\nexport { Execution, ResultImpl } from './interpreter-types';\n","/**\n * WebSocket transport protocol types\n *\n * Enables multiplexing HTTP-like requests over a single WebSocket connection.\n * This reduces sub-request count when running inside Workers/Durable Objects.\n *\n * Protocol:\n * - Client sends WSRequest messages\n * - Server responds with WSResponse messages (matched by id)\n * - For streaming endpoints, server sends multiple WSStreamChunk messages\n * followed by a final WSResponse\n */\n/**\n * Type guard for WSRequest\n *\n * Note: Only validates the discriminator field (type === 'request').\n * Does not validate other required fields (id, method, path).\n * Use for routing messages; trust TypeScript for field validation.\n */\nexport function isWSRequest(msg) {\n return (typeof msg === 'object' &&\n msg !== null &&\n 'type' in msg &&\n msg.type === 'request');\n}\n/**\n * Type guard for WSResponse\n *\n * Note: Only validates the discriminator field (type === 'response').\n */\nexport function isWSResponse(msg) {\n return (typeof msg === 'object' &&\n msg !== null &&\n 'type' in msg &&\n msg.type === 'response');\n}\n/**\n * Type guard for WSStreamChunk\n *\n * Note: Only validates the discriminator field (type === 'stream').\n */\nexport function isWSStreamChunk(msg) {\n return (typeof msg === 'object' &&\n msg !== null &&\n 'type' in msg &&\n msg.type === 'stream');\n}\n/**\n * Type guard for WSError\n *\n * Note: Only validates the discriminator field (type === 'error').\n */\nexport function isWSError(msg) {\n return (typeof msg === 'object' &&\n msg !== null &&\n 'type' in msg &&\n msg.type === 'error');\n}\n/**\n * Generate a unique request ID\n */\nexport function generateRequestId() {\n return `ws_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as ProcessInfoResult, A as RequestConfig, B as FileChunk, C as WriteFileRequest, D as ContainerStub, E as BaseApiResponse, F as BucketProvider, G as ListFilesOptions, H as FileStreamEvent, I as ExecEvent, J as PortCloseResult, K as LogEvent, L as ExecOptions, M as SessionRequest, N as BaseExecOptions, O as ErrorResponse, P as BucketCredentials, Q as ProcessCleanupResult, R as ExecResult, S as ReadFileRequest, T as ExecuteResponse, U as GitCheckoutResult, V as FileMetadata, W as ISandbox, X as PortListResult, Y as PortExposeResult, Z as Process, _ as GitCheckoutRequest, _t as ExecutionResult, a as CreateSessionRequest, at as ProcessStatus, b as FileOperationRequest, c as DeleteSessionResponse, ct as StreamOptions, d as ProcessClient, dt as isExecResult, et as ProcessKillResult, f as ExposePortRequest, ft as isProcess, g as InterpreterClient, gt as Execution, h as ExecutionCallbacks, ht as CreateContextOptions, i as CommandsResponse, it as ProcessStartResult, j as ResponseHandler, k as HttpClientOptions, l as PingResponse, lt as WaitForLogResult, m as UnexposePortRequest, mt as CodeContext, n as getSandbox, nt as ProcessLogsResult, o as CreateSessionResponse, ot as SandboxOptions, p as PortClient, pt as isProcessStatus, q as MountBucketOptions, r as SandboxClient, rt as ProcessOptions, s as DeleteSessionRequest, st as SessionOptions, t as Sandbox, tt as ProcessListResult, u as UtilityClient, ut as WaitForPortOptions, v as GitClient, vt as RunCodeOptions, w as CommandClient, x as MkdirRequest, y as FileClient, z as ExecutionSession } from "./sandbox-DAb6o08K.js";
1
+ import { $ as ProcessKillResult, A as ResponseHandler, B as FileMetadata, C as CommandClient, D as ErrorResponse, E as ContainerStub, F as ExecEvent, G as LogEvent, H as GitCheckoutResult, I as ExecOptions, J as PortExposeResult, K as MountBucketOptions, L as ExecResult, M as BaseExecOptions, N as BucketCredentials, O as HttpClientOptions, P as BucketProvider, Q as ProcessInfoResult, R as ExecutionSession, S as WriteFileRequest, T as BaseApiResponse, U as ISandbox, V as FileStreamEvent, W as ListFilesOptions, X as Process, Y as PortListResult, Z as ProcessCleanupResult, _ as GitClient, _t as RunCodeOptions, a as CreateSessionRequest, at as SandboxOptions, b as MkdirRequest, c as DeleteSessionResponse, ct as WaitForLogResult, d as ProcessClient, dt as isProcess, et as ProcessListResult, f as PortClient, ft as isProcessStatus, g as GitCheckoutRequest, gt as ExecutionResult, h as InterpreterClient, ht as Execution, i as CommandsResponse, it as ProcessStatus, j as SessionRequest, k as RequestConfig, l as PingResponse, lt as WaitForPortOptions, m as ExecutionCallbacks, mt as CreateContextOptions, n as getSandbox, nt as ProcessOptions, o as CreateSessionResponse, ot as SessionOptions, p as UnexposePortRequest, pt as CodeContext, q as PortCloseResult, r as SandboxClient, rt as ProcessStartResult, s as DeleteSessionRequest, st as StreamOptions, t as Sandbox, tt as ProcessLogsResult, u as UtilityClient, ut as isExecResult, v as FileClient, w as ExecuteResponse, x as ReadFileRequest, y as FileOperationRequest, z as FileChunk } from "./sandbox-CEsJ1edi.js";
2
2
  import { a as OperationType, i as ErrorResponse$1, n as ProcessExitedBeforeReadyContext, o as ErrorCode, r as ProcessReadyTimeoutContext } from "./contexts-CdrlvHWK.js";
3
3
 
4
4
  //#region ../shared/dist/request-types.d.ts
@@ -15,7 +15,7 @@ interface ExecuteRequest {
15
15
  sessionId?: string;
16
16
  background?: boolean;
17
17
  timeoutMs?: number;
18
- env?: Record<string, string>;
18
+ env?: Record<string, string | undefined>;
19
19
  cwd?: string;
20
20
  }
21
21
  /**
@@ -27,11 +27,18 @@ interface StartProcessRequest {
27
27
  sessionId?: string;
28
28
  processId?: string;
29
29
  timeoutMs?: number;
30
- env?: Record<string, string>;
30
+ env?: Record<string, string | undefined>;
31
31
  cwd?: string;
32
32
  encoding?: string;
33
33
  autoCleanup?: boolean;
34
34
  }
35
+ /**
36
+ * Request to expose a port
37
+ */
38
+ interface ExposePortRequest {
39
+ port: number;
40
+ name?: string;
41
+ }
35
42
  //#endregion
36
43
  //#region src/errors/classes.d.ts
37
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["ExecuteRequest","Record","StartProcessRequest","ReadFileRequest","WriteFileRequest","DeleteFileRequest","RenameFileRequest","MoveFileRequest","MkdirRequest","FileExistsRequest","ExposePortRequest","GitCheckoutRequest","ListFilesRequest","SessionCreateRequest","SessionDeleteRequest"],"sources":["../../shared/dist/request-types.d.ts","../src/errors/classes.ts","../src/file-stream.ts","../src/interpreter.ts","../src/request-handler.ts","../src/sse-parser.ts","../src/storage-mount/errors.ts"],"sourcesContent":["/**\n * Request types for API calls to the container\n * Single source of truth for the contract between SDK clients and container handlers\n */\n/**\n * Request to execute a command\n */\nexport interface ExecuteRequest {\n command: string;\n sessionId?: string;\n background?: boolean;\n timeoutMs?: number;\n env?: Record<string, string>;\n cwd?: string;\n}\n/**\n * Request to start a background process\n * Uses flat structure consistent with other endpoints\n */\nexport interface StartProcessRequest {\n command: string;\n sessionId?: string;\n processId?: string;\n timeoutMs?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n}\n/**\n * Request to read a file\n */\nexport interface ReadFileRequest {\n path: string;\n encoding?: string;\n sessionId?: string;\n}\n/**\n * Request to write a file\n */\nexport interface WriteFileRequest {\n path: string;\n content: string;\n encoding?: string;\n sessionId?: string;\n}\n/**\n * Request to delete a file\n */\nexport interface DeleteFileRequest {\n path: string;\n sessionId?: string;\n}\n/**\n * Request to rename a file\n */\nexport interface RenameFileRequest {\n oldPath: string;\n newPath: string;\n sessionId?: string;\n}\n/**\n * Request to move a file\n */\nexport interface MoveFileRequest {\n sourcePath: string;\n destinationPath: string;\n sessionId?: string;\n}\n/**\n * Request to create a directory\n */\nexport interface MkdirRequest {\n path: string;\n recursive?: boolean;\n sessionId?: string;\n}\n/**\n * Request to check if a file or directory exists\n */\nexport interface FileExistsRequest {\n path: string;\n sessionId?: string;\n}\n/**\n * Request to expose a port\n */\nexport interface ExposePortRequest {\n port: number;\n name?: string;\n}\n/**\n * Request to clone a Git repository\n */\nexport interface GitCheckoutRequest {\n repoUrl: string;\n branch?: string;\n targetDir?: string;\n sessionId?: string;\n /** Clone depth for shallow clones (e.g., 1 for latest commit only) */\n depth?: number;\n}\n/**\n * Request to list files in a directory\n */\nexport interface ListFilesRequest {\n path: string;\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n };\n sessionId?: string;\n}\n/**\n * Request to create a session\n */\nexport interface SessionCreateRequest {\n id?: string;\n name?: string;\n env?: Record<string, string>;\n cwd?: string;\n}\n/**\n * Request to delete a session\n */\nexport interface SessionDeleteRequest {\n sessionId: string;\n}\n//# sourceMappingURL=request-types.d.ts.map"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":["ExecuteRequest","Record","StartProcessRequest","ReadFileRequest","WriteFileRequest","DeleteFileRequest","RenameFileRequest","MoveFileRequest","MkdirRequest","FileExistsRequest","ExposePortRequest","GitCheckoutRequest","ListFilesRequest","SessionCreateRequest","SessionDeleteRequest"],"sources":["../../shared/dist/request-types.d.ts","../src/errors/classes.ts","../src/file-stream.ts","../src/interpreter.ts","../src/request-handler.ts","../src/sse-parser.ts","../src/storage-mount/errors.ts"],"sourcesContent":["/**\n * Request types for API calls to the container\n * Single source of truth for the contract between SDK clients and container handlers\n */\n/**\n * Request to execute a command\n */\nexport interface ExecuteRequest {\n command: string;\n sessionId?: string;\n background?: boolean;\n timeoutMs?: number;\n env?: Record<string, string | undefined>;\n cwd?: string;\n}\n/**\n * Request to start a background process\n * Uses flat structure consistent with other endpoints\n */\nexport interface StartProcessRequest {\n command: string;\n sessionId?: string;\n processId?: string;\n timeoutMs?: number;\n env?: Record<string, string | undefined>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n}\n/**\n * Request to read a file\n */\nexport interface ReadFileRequest {\n path: string;\n encoding?: string;\n sessionId?: string;\n}\n/**\n * Request to write a file\n */\nexport interface WriteFileRequest {\n path: string;\n content: string;\n encoding?: string;\n sessionId?: string;\n}\n/**\n * Request to delete a file\n */\nexport interface DeleteFileRequest {\n path: string;\n sessionId?: string;\n}\n/**\n * Request to rename a file\n */\nexport interface RenameFileRequest {\n oldPath: string;\n newPath: string;\n sessionId?: string;\n}\n/**\n * Request to move a file\n */\nexport interface MoveFileRequest {\n sourcePath: string;\n destinationPath: string;\n sessionId?: string;\n}\n/**\n * Request to create a directory\n */\nexport interface MkdirRequest {\n path: string;\n recursive?: boolean;\n sessionId?: string;\n}\n/**\n * Request to check if a file or directory exists\n */\nexport interface FileExistsRequest {\n path: string;\n sessionId?: string;\n}\n/**\n * Request to expose a port\n */\nexport interface ExposePortRequest {\n port: number;\n name?: string;\n}\n/**\n * Request to clone a Git repository\n */\nexport interface GitCheckoutRequest {\n repoUrl: string;\n branch?: string;\n targetDir?: string;\n sessionId?: string;\n /** Clone depth for shallow clones (e.g., 1 for latest commit only) */\n depth?: number;\n}\n/**\n * Request to list files in a directory\n */\nexport interface ListFilesRequest {\n path: string;\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n };\n sessionId?: string;\n}\n/**\n * Request to create a session\n */\nexport interface SessionCreateRequest {\n id?: string;\n name?: string;\n env?: Record<string, string | undefined>;\n cwd?: string;\n}\n/**\n * Request to delete a session\n */\nexport interface SessionDeleteRequest {\n sessionId: string;\n}\n//# sourceMappingURL=request-types.d.ts.map"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as isExecResult, c as shellEscape, d as TraceContext, f as Execution, g as getEnvString, h as extractRepoName, i as isWSStreamChunk, l as createLogger, m as GitLogger, n as isWSError, o as isProcess, p as ResultImpl, r as isWSResponse, s as isProcessStatus, t as generateRequestId, u as createNoOpLogger } from "./dist-c_xYW5i_.js";
1
+ import { _ as getEnvString, a as isExecResult, c as shellEscape, d as TraceContext, f as Execution, g as filterEnvVars, h as extractRepoName, i as isWSStreamChunk, l as createLogger, m as GitLogger, n as isWSError, o as isProcess, p as ResultImpl, r as isWSResponse, s as isProcessStatus, t as generateRequestId, u as createNoOpLogger, v as partitionEnvVars } from "./dist-BpbdH8ry.js";
2
2
  import { t as ErrorCode } from "./errors-BCXUmJUn.js";
3
3
  import { Container, getContainer, switchPort } from "@cloudflare/containers";
4
4
 
@@ -2273,21 +2273,29 @@ async function proxyToSandbox(request, env) {
2273
2273
  }
2274
2274
  }
2275
2275
  function extractSandboxRoute(url) {
2276
- const subdomainMatch = url.hostname.match(/^(\d{4,5})-([^.-][^.]*?[^.-]|[^.-])-([a-z0-9_-]{16})\.(.+)$/);
2277
- if (!subdomainMatch) return null;
2278
- const portStr = subdomainMatch[1];
2279
- const sandboxId = subdomainMatch[2];
2280
- const token = subdomainMatch[3];
2281
- subdomainMatch[4];
2276
+ const dotIndex = url.hostname.indexOf(".");
2277
+ if (dotIndex === -1) return null;
2278
+ const subdomain = url.hostname.slice(0, dotIndex);
2279
+ url.hostname.slice(dotIndex + 1);
2280
+ const firstHyphen = subdomain.indexOf("-");
2281
+ if (firstHyphen === -1) return null;
2282
+ const portStr = subdomain.slice(0, firstHyphen);
2283
+ if (!/^\d{4,5}$/.test(portStr)) return null;
2282
2284
  const port = parseInt(portStr, 10);
2283
2285
  if (!validatePort(port)) return null;
2286
+ const rest = subdomain.slice(firstHyphen + 1);
2287
+ const lastHyphen = rest.lastIndexOf("-");
2288
+ if (lastHyphen === -1) return null;
2289
+ const sandboxId = rest.slice(0, lastHyphen);
2290
+ const token = rest.slice(lastHyphen + 1);
2291
+ if (!/^[a-z0-9_]+$/.test(token) || token.length === 0 || token.length > 63) return null;
2292
+ if (sandboxId.length === 0 || sandboxId.length > 63) return null;
2284
2293
  let sanitizedSandboxId;
2285
2294
  try {
2286
2295
  sanitizedSandboxId = sanitizeSandboxId(sandboxId);
2287
- } catch (error) {
2296
+ } catch {
2288
2297
  return null;
2289
2298
  }
2290
- if (sandboxId.length > 63) return null;
2291
2299
  return {
2292
2300
  port,
2293
2301
  sandboxId: sanitizedSandboxId,
@@ -2509,6 +2517,30 @@ function resolveS3fsOptions(provider, userOptions) {
2509
2517
  return Array.from(flagMap.values());
2510
2518
  }
2511
2519
 
2520
+ //#endregion
2521
+ //#region src/storage-mount/validation.ts
2522
+ function validatePrefix(prefix) {
2523
+ if (!prefix.startsWith("/")) throw new InvalidMountConfigError(`Prefix must start with '/': "${prefix}"`);
2524
+ }
2525
+ function validateBucketName(bucket, mountPath) {
2526
+ if (bucket.includes(":")) {
2527
+ const [bucketName, prefixPart] = bucket.split(":");
2528
+ throw new InvalidMountConfigError(`Bucket name cannot contain ':'. To mount a prefix, use the 'prefix' option:\n mountBucket('${bucketName}', '${mountPath}', { ...options, prefix: '${prefixPart}' })`);
2529
+ }
2530
+ if (!/^[a-z0-9]([a-z0-9.-]{0,61}[a-z0-9])?$/.test(bucket)) throw new InvalidMountConfigError(`Invalid bucket name: "${bucket}". Bucket names must be 3-63 characters, lowercase alphanumeric, dots, or hyphens, and cannot start/end with dots or hyphens.`);
2531
+ }
2532
+ /**
2533
+ * Builds the s3fs source string from bucket name and optional prefix.
2534
+ * Format: "bucket" or "bucket:/prefix/" for subdirectory mounts.
2535
+ *
2536
+ * @param bucket - The bucket name
2537
+ * @param prefix - Optional prefix/subdirectory path
2538
+ * @returns The s3fs source string
2539
+ */
2540
+ function buildS3fsSource(bucket, prefix) {
2541
+ return prefix ? `${bucket}:${prefix}` : bucket;
2542
+ }
2543
+
2512
2544
  //#endregion
2513
2545
  //#region src/version.ts
2514
2546
  /**
@@ -2516,7 +2548,7 @@ function resolveS3fsOptions(provider, userOptions) {
2516
2548
  * This file is auto-updated by .github/changeset-version.ts during releases
2517
2549
  * DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
2518
2550
  */
2519
- const SDK_VERSION = "0.6.10";
2551
+ const SDK_VERSION = "0.7.0";
2520
2552
 
2521
2553
  //#endregion
2522
2554
  //#region src/sandbox.ts
@@ -2631,18 +2663,25 @@ var Sandbox = class extends Container {
2631
2663
  async setKeepAlive(keepAlive) {
2632
2664
  this.keepAliveEnabled = keepAlive;
2633
2665
  await this.ctx.storage.put("keepAliveEnabled", keepAlive);
2634
- if (keepAlive) this.logger.info("KeepAlive mode enabled - container will stay alive until explicitly destroyed");
2635
- else this.logger.info("KeepAlive mode disabled - container will timeout normally");
2636
2666
  }
2637
2667
  async setEnvVars(envVars) {
2668
+ const { toSet, toUnset } = partitionEnvVars(envVars);
2669
+ for (const key of toUnset) delete this.envVars[key];
2638
2670
  this.envVars = {
2639
2671
  ...this.envVars,
2640
- ...envVars
2672
+ ...toSet
2641
2673
  };
2642
- if (this.defaultSession) for (const [key, value] of Object.entries(envVars)) {
2643
- const exportCommand = `export ${key}='${value.replace(/'/g, "'\\''")}'`;
2644
- const result = await this.client.commands.execute(exportCommand, this.defaultSession);
2645
- if (result.exitCode !== 0) throw new Error(`Failed to set ${key}: ${result.stderr || "Unknown error"}`);
2674
+ if (this.defaultSession) {
2675
+ for (const key of toUnset) {
2676
+ const unsetCommand = `unset ${key}`;
2677
+ const result = await this.client.commands.execute(unsetCommand, this.defaultSession);
2678
+ if (result.exitCode !== 0) throw new Error(`Failed to unset ${key}: ${result.stderr || "Unknown error"}`);
2679
+ }
2680
+ for (const [key, value] of Object.entries(toSet)) {
2681
+ const exportCommand = `export ${key}=${shellEscape(value)}`;
2682
+ const result = await this.client.commands.execute(exportCommand, this.defaultSession);
2683
+ if (result.exitCode !== 0) throw new Error(`Failed to set ${key}: ${result.stderr || "Unknown error"}`);
2684
+ }
2646
2685
  }
2647
2686
  }
2648
2687
  /**
@@ -2693,13 +2732,21 @@ var Sandbox = class extends Container {
2693
2732
  }
2694
2733
  async mountBucket(bucket, mountPath, options) {
2695
2734
  this.logger.info(`Mounting bucket ${bucket} to ${mountPath}`);
2696
- this.validateMountOptions(bucket, mountPath, options);
2735
+ const prefix = options.prefix || void 0;
2736
+ this.validateMountOptions(bucket, mountPath, {
2737
+ ...options,
2738
+ prefix
2739
+ });
2740
+ const s3fsSource = buildS3fsSource(bucket, prefix);
2697
2741
  const provider = options.provider || detectProviderFromUrl(options.endpoint);
2698
- this.logger.debug(`Detected provider: ${provider || "unknown"}`, { explicitProvider: options.provider });
2742
+ this.logger.debug(`Detected provider: ${provider || "unknown"}`, {
2743
+ explicitProvider: options.provider,
2744
+ prefix
2745
+ });
2699
2746
  const credentials = detectCredentials(options, this.envVars);
2700
2747
  const passwordFilePath = this.generatePasswordFilePath();
2701
2748
  this.activeMounts.set(mountPath, {
2702
- bucket,
2749
+ bucket: s3fsSource,
2703
2750
  mountPath,
2704
2751
  endpoint: options.endpoint,
2705
2752
  provider,
@@ -2709,9 +2756,9 @@ var Sandbox = class extends Container {
2709
2756
  try {
2710
2757
  await this.createPasswordFile(passwordFilePath, bucket, credentials);
2711
2758
  await this.exec(`mkdir -p ${shellEscape(mountPath)}`);
2712
- await this.executeS3FSMount(bucket, mountPath, options, provider, passwordFilePath);
2759
+ await this.executeS3FSMount(s3fsSource, mountPath, options, provider, passwordFilePath);
2713
2760
  this.activeMounts.set(mountPath, {
2714
- bucket,
2761
+ bucket: s3fsSource,
2715
2762
  mountPath,
2716
2763
  endpoint: options.endpoint,
2717
2764
  provider,
@@ -2754,9 +2801,10 @@ var Sandbox = class extends Container {
2754
2801
  } catch (error) {
2755
2802
  throw new InvalidMountConfigError(`Invalid endpoint URL: "${options.endpoint}". Must be a valid HTTP(S) URL.`);
2756
2803
  }
2757
- if (!/^[a-z0-9]([a-z0-9.-]{0,61}[a-z0-9])?$/.test(bucket)) throw new InvalidMountConfigError(`Invalid bucket name: "${bucket}". Bucket names must be 3-63 characters, lowercase alphanumeric, dots, or hyphens, and cannot start/end with dots or hyphens.`);
2804
+ validateBucketName(bucket, mountPath);
2758
2805
  if (!mountPath.startsWith("/")) throw new InvalidMountConfigError(`Mount path must be absolute (start with /): "${mountPath}"`);
2759
2806
  if (this.activeMounts.has(mountPath)) throw new InvalidMountConfigError(`Mount path "${mountPath}" is already in use by bucket "${this.activeMounts.get(mountPath)?.bucket}". Unmount the existing bucket first or use a different mount path.`);
2807
+ if (options.prefix !== void 0) validatePrefix(options.prefix);
2760
2808
  }
2761
2809
  /**
2762
2810
  * Generate unique password file path for s3fs credentials
@@ -3391,7 +3439,7 @@ var Sandbox = class extends Container {
3391
3439
  const requestOptions = {
3392
3440
  ...options?.processId !== void 0 && { processId: options.processId },
3393
3441
  ...options?.timeout !== void 0 && { timeoutMs: options.timeout },
3394
- ...options?.env !== void 0 && { env: options.env },
3442
+ ...options?.env !== void 0 && { env: filterEnvVars(options.env) },
3395
3443
  ...options?.cwd !== void 0 && { cwd: options.cwd },
3396
3444
  ...options?.encoding !== void 0 && { encoding: options.encoding },
3397
3445
  ...options?.autoCleanup !== void 0 && { autoCleanup: options.autoCleanup }
@@ -3559,6 +3607,30 @@ var Sandbox = class extends Container {
3559
3607
  const session = sessionId ?? await this.ensureDefaultSession();
3560
3608
  return this.client.files.exists(path, session);
3561
3609
  }
3610
+ /**
3611
+ * Expose a port and get a preview URL for accessing services running in the sandbox
3612
+ *
3613
+ * @param port - Port number to expose (1024-65535)
3614
+ * @param options - Configuration options
3615
+ * @param options.hostname - Your Worker's domain name (required for preview URL construction)
3616
+ * @param options.name - Optional friendly name for the port
3617
+ * @param options.token - Optional custom token for the preview URL (1-16 characters: lowercase letters, numbers, hyphens, underscores)
3618
+ * If not provided, a random 16-character token will be generated automatically
3619
+ * @returns Preview URL information including the full URL, port number, and optional name
3620
+ *
3621
+ * @example
3622
+ * // With auto-generated token
3623
+ * const { url } = await sandbox.exposePort(8080, { hostname: 'example.com' });
3624
+ * // url: https://8080-sandbox-id-abc123random4567.example.com
3625
+ *
3626
+ * @example
3627
+ * // With custom token for stable URLs across deployments
3628
+ * const { url } = await sandbox.exposePort(8080, {
3629
+ * hostname: 'example.com',
3630
+ * token: 'my-token-v1'
3631
+ * });
3632
+ * // url: https://8080-sandbox-id-my-token-v1.example.com
3633
+ */
3562
3634
  async exposePort(port, options) {
3563
3635
  if (options.hostname.endsWith(".workers.dev")) throw new CustomDomainRequiredError({
3564
3636
  code: ErrorCode.CUSTOM_DOMAIN_REQUIRED,
@@ -3567,11 +3639,17 @@ var Sandbox = class extends Container {
3567
3639
  httpStatus: 400,
3568
3640
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3569
3641
  });
3570
- const sessionId = await this.ensureDefaultSession();
3571
- await this.client.ports.exposePort(port, sessionId, options?.name);
3572
3642
  if (!this.sandboxName) throw new Error("Sandbox name not available. Ensure sandbox is accessed through getSandbox()");
3573
- const token = this.generatePortToken();
3643
+ let token;
3644
+ if (options.token !== void 0) {
3645
+ this.validateCustomToken(options.token);
3646
+ token = options.token;
3647
+ } else token = this.generatePortToken();
3574
3648
  const tokens = await this.ctx.storage.get("portTokens") || {};
3649
+ const existingPort = Object.entries(tokens).find(([p, t]) => t === token && p !== port.toString());
3650
+ if (existingPort) throw new SecurityError(`Token '${token}' is already in use by port ${existingPort[0]}. Please use a different token.`);
3651
+ const sessionId = await this.ensureDefaultSession();
3652
+ await this.client.ports.exposePort(port, sessionId, options?.name);
3575
3653
  tokens[port.toString()] = token;
3576
3654
  await this.ctx.storage.put("portTokens", tokens);
3577
3655
  return {
@@ -3621,12 +3699,21 @@ var Sandbox = class extends Container {
3621
3699
  this.logger.error("Port is exposed but has no token - bug detected", void 0, { port });
3622
3700
  return false;
3623
3701
  }
3624
- return storedToken === token;
3702
+ if (storedToken.length !== token.length) return false;
3703
+ const encoder = new TextEncoder();
3704
+ const a = encoder.encode(storedToken);
3705
+ const b = encoder.encode(token);
3706
+ return crypto.subtle.timingSafeEqual(a, b);
3707
+ }
3708
+ validateCustomToken(token) {
3709
+ if (token.length === 0) throw new SecurityError(`Custom token cannot be empty.`);
3710
+ if (token.length > 16) throw new SecurityError(`Custom token too long. Maximum 16 characters allowed. Received: ${token.length} characters.`);
3711
+ if (!/^[a-z0-9_]+$/.test(token)) throw new SecurityError(`Custom token must contain only lowercase letters (a-z), numbers (0-9), and underscores (_). Invalid token provided.`);
3625
3712
  }
3626
3713
  generatePortToken() {
3627
3714
  const array = new Uint8Array(12);
3628
3715
  crypto.getRandomValues(array);
3629
- return btoa(String.fromCharCode(...array)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "").toLowerCase();
3716
+ return btoa(String.fromCharCode(...array)).replace(/\+/g, "_").replace(/\//g, "_").replace(/=/g, "").toLowerCase();
3630
3717
  }
3631
3718
  constructPreviewUrl(port, sandboxId, hostname, token) {
3632
3719
  if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`);
@@ -3659,11 +3746,11 @@ var Sandbox = class extends Container {
3659
3746
  */
3660
3747
  async createSession(options) {
3661
3748
  const sessionId = options?.id || `session-${Date.now()}`;
3662
- const mergedEnv = {
3749
+ const filteredEnv = filterEnvVars({
3663
3750
  ...this.envVars,
3664
3751
  ...options?.env ?? {}
3665
- };
3666
- const envPayload = Object.keys(mergedEnv).length > 0 ? mergedEnv : void 0;
3752
+ });
3753
+ const envPayload = Object.keys(filteredEnv).length > 0 ? filteredEnv : void 0;
3667
3754
  await this.client.utils.createSession({
3668
3755
  id: sessionId,
3669
3756
  ...envPayload && { env: envPayload },
@@ -3743,9 +3830,15 @@ var Sandbox = class extends Container {
3743
3830
  sessionId
3744
3831
  }),
3745
3832
  setEnvVars: async (envVars) => {
3833
+ const { toSet, toUnset } = partitionEnvVars(envVars);
3746
3834
  try {
3747
- for (const [key, value] of Object.entries(envVars)) {
3748
- const exportCommand = `export ${key}='${value.replace(/'/g, "'\\''")}'`;
3835
+ for (const key of toUnset) {
3836
+ const unsetCommand = `unset ${key}`;
3837
+ const result = await this.client.commands.execute(unsetCommand, sessionId);
3838
+ if (result.exitCode !== 0) throw new Error(`Failed to unset ${key}: ${result.stderr || "Unknown error"}`);
3839
+ }
3840
+ for (const [key, value] of Object.entries(toSet)) {
3841
+ const exportCommand = `export ${key}=${shellEscape(value)}`;
3749
3842
  const result = await this.client.commands.execute(exportCommand, sessionId);
3750
3843
  if (result.exitCode !== 0) throw new Error(`Failed to set ${key}: ${result.stderr || "Unknown error"}`);
3751
3844
  }