@spotlightjs/spotlight 4.8.0 → 4.9.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.
- package/dist/_virtual/_sentry-release-injection-file.js +2 -2
- package/dist/sentry-config.js +2 -2
- package/dist/server/cli/help.d.ts +2 -1
- package/dist/server/cli/help.js +41 -18
- package/dist/server/cli/help.js.map +1 -1
- package/dist/server/cli/mcp.d.ts +8 -2
- package/dist/server/cli/mcp.js +27 -3
- package/dist/server/cli/mcp.js.map +1 -1
- package/dist/server/cli/run.d.ts +8 -2
- package/dist/server/cli/run.js +34 -6
- package/dist/server/cli/run.js.map +1 -1
- package/dist/server/cli/server.d.ts +8 -2
- package/dist/server/cli/server.js +27 -3
- package/dist/server/cli/server.js.map +1 -1
- package/dist/server/cli/tail.d.ts +8 -2
- package/dist/server/cli/tail.js +28 -3
- package/dist/server/cli/tail.js.map +1 -1
- package/dist/server/cli.d.ts +3 -2
- package/dist/server/cli.js +18 -16
- package/dist/server/cli.js.map +1 -1
- package/dist/server/formatters/schema.d.ts +382 -382
- package/dist/server/mcp/mcp.js +2 -2
- package/dist/server/parser/types.d.ts +47 -1
- package/dist/server/types/cli.d.ts +11 -0
- package/dist/ui/assets/index.js +58 -58
- package/dist/ui/assets/index.js.map +1 -1
- package/dist/ui/assets/instrumentation.js +2 -2
- package/dist/ui/assets/main.css +1 -1
- package/dist/ui/assets/main.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tail.js","sources":["../../../src/server/cli/tail.ts"],"sourcesContent":["import type { ServerType } from \"@hono/node-server\";\nimport { captureException } from \"@sentry/core\";\nimport { metrics } from \"@sentry/node\";\nimport { EventSource } from \"eventsource\";\nimport { SENTRY_CONTENT_TYPE } from \"../constants.ts\";\nimport {\n type FormatterRegistry,\n type FormatterType,\n applyFormatter,\n humanFormatters,\n jsonFormatters,\n logfmtFormatters,\n mdFormatters,\n} from \"../formatters/index.ts\";\nimport { logger } from \"../logger.ts\";\nimport { setupSpotlight } from \"../main.ts\";\nimport type { ParsedEnvelope } from \"../parser/index.ts\";\nimport type { CLIHandlerOptions } from \"../types/cli.ts\";\nimport { getSpotlightURL } from \"../utils/extras.ts\";\nimport { getBuffer } from \"../utils/index.ts\";\n\nexport type OnItemCallback = (\n type: string,\n item: ParsedEnvelope[\"envelope\"][1][number],\n envelopeHeader: ParsedEnvelope[\"envelope\"][0],\n) => boolean;\nexport const NAME_TO_TYPE_MAPPING: Record<string, string[]> = Object.freeze({\n traces: [\"transaction\", \"span\"],\n // profiles: [\"profile\"],\n logs: [\"log\"],\n attachments: [\"attachment\"],\n errors: [\"event\"],\n // sessions: [\"session\"],\n // replays: [\"replay_video\"],\n // client_report\n});\n\nconst SUPPORTED_ENVELOPE_TYPES = new Set(Object.values(NAME_TO_TYPE_MAPPING).flat());\nexport const EVERYTHING_MAGIC_WORDS = new Set([\"everything\", \"all\", \"*\"]);\nexport const SUPPORTED_TAIL_ARGS = new Set([...Object.keys(NAME_TO_TYPE_MAPPING), ...EVERYTHING_MAGIC_WORDS]);\n\nconst FORMATTERS: Record<FormatterType, FormatterRegistry> = {\n md: mdFormatters,\n logfmt: logfmtFormatters,\n json: jsonFormatters,\n human: humanFormatters,\n};\n\nconst connectUpstream = async (port: number) =>\n new Promise<EventSource>((resolve, reject) => {\n const client = new EventSource(getSpotlightURL(port));\n client.onerror = reject;\n client.onopen = () => resolve(client);\n });\n\nexport
|
|
1
|
+
{"version":3,"file":"tail.js","sources":["../../../src/server/cli/tail.ts"],"sourcesContent":["import type { ServerType } from \"@hono/node-server\";\nimport { captureException } from \"@sentry/core\";\nimport { metrics } from \"@sentry/node\";\nimport { EventSource } from \"eventsource\";\nimport { SENTRY_CONTENT_TYPE } from \"../constants.ts\";\nimport {\n type FormatterRegistry,\n type FormatterType,\n applyFormatter,\n humanFormatters,\n jsonFormatters,\n logfmtFormatters,\n mdFormatters,\n} from \"../formatters/index.ts\";\nimport { logger } from \"../logger.ts\";\nimport { setupSpotlight } from \"../main.ts\";\nimport type { ParsedEnvelope } from \"../parser/index.ts\";\nimport type { CLIHandlerOptions, Command, CommandMeta } from \"../types/cli.ts\";\nimport { getSpotlightURL } from \"../utils/extras.ts\";\nimport { getBuffer } from \"../utils/index.ts\";\n\nexport type OnItemCallback = (\n type: string,\n item: ParsedEnvelope[\"envelope\"][1][number],\n envelopeHeader: ParsedEnvelope[\"envelope\"][0],\n) => boolean;\nexport const NAME_TO_TYPE_MAPPING: Record<string, string[]> = Object.freeze({\n traces: [\"transaction\", \"span\"],\n // profiles: [\"profile\"],\n logs: [\"log\"],\n attachments: [\"attachment\"],\n errors: [\"event\"],\n // sessions: [\"session\"],\n // replays: [\"replay_video\"],\n // client_report\n});\n\nconst SUPPORTED_ENVELOPE_TYPES = new Set(Object.values(NAME_TO_TYPE_MAPPING).flat());\nexport const EVERYTHING_MAGIC_WORDS = new Set([\"everything\", \"all\", \"*\"]);\nexport const SUPPORTED_TAIL_ARGS = new Set([...Object.keys(NAME_TO_TYPE_MAPPING), ...EVERYTHING_MAGIC_WORDS]);\n\nexport const meta: CommandMeta = {\n name: \"tail\",\n short: `Tail Sentry events (types: ${Object.keys(NAME_TO_TYPE_MAPPING).join(\", \")})`,\n usage: \"spotlight tail [types...] [options]\",\n long: `Stream Sentry events to your terminal in real-time.\n\nAvailable event types:\n ${Object.keys(NAME_TO_TYPE_MAPPING).join(\", \")}\n\nMagic words (to tail everything):\n ${[...EVERYTHING_MAGIC_WORDS].join(\", \")}\n\nUse -f/--format to change output format (human, logfmt, json, md).\nConnects to existing sidecar if running, otherwise starts a new one.`,\n examples: [\n \"spotlight tail # Tail all event types (human format)\",\n \"spotlight tail errors # Tail only errors\",\n \"spotlight tail errors logs # Tail errors and logs\",\n \"spotlight tail --format json # Use JSON output format\",\n \"spotlight tail traces -f logfmt # Tail traces with logfmt format\",\n ],\n};\n\nconst FORMATTERS: Record<FormatterType, FormatterRegistry> = {\n md: mdFormatters,\n logfmt: logfmtFormatters,\n json: jsonFormatters,\n human: humanFormatters,\n};\n\nconst connectUpstream = async (port: number) =>\n new Promise<EventSource>((resolve, reject) => {\n const client = new EventSource(getSpotlightURL(port));\n client.onerror = reject;\n client.onopen = () => resolve(client);\n });\n\nexport async function handler(\n { port, cmdArgs, basePath, filesToServe, format = \"logfmt\", allowedOrigins }: CLIHandlerOptions,\n onItem?: OnItemCallback,\n): Promise<ServerType | undefined> {\n const eventTypes = cmdArgs.length > 0 ? cmdArgs.map(arg => arg.toLowerCase()) : [\"everything\"];\n for (const eventType of eventTypes) {\n if (!SUPPORTED_TAIL_ARGS.has(eventType)) {\n logger.error(`Error: Unsupported argument \"${eventType}\".`);\n logger.error(`Supported arguments are: ${[...SUPPORTED_TAIL_ARGS].join(\", \")}`);\n process.exit(1);\n }\n }\n\n // Track which event types users tail\n for (const eventType of eventTypes) {\n metrics.count(\"cli.tail.event_type\", 1, { attributes: { type: eventType } });\n }\n\n const formatter = FORMATTERS[format];\n const types = eventTypes.some(type => EVERYTHING_MAGIC_WORDS.has(type))\n ? SUPPORTED_ENVELOPE_TYPES\n : new Set([...eventTypes.flatMap(type => NAME_TO_TYPE_MAPPING[type] || [])]);\n const onEnvelope: (envelope: ParsedEnvelope[\"envelope\"]) => void = envelope => {\n const [envelopeHeader, items] = envelope;\n const formatted: string[] = [];\n\n for (const item of items) {\n const [{ type }, payload] = item;\n\n if (!type || !types.has(type)) {\n // Skip if not a type we're interested in\n continue;\n }\n\n // Check if this event type is supported by the formatter\n if (!(type in formatter)) {\n continue;\n }\n\n if (onItem && !onItem(type, item, envelopeHeader)) {\n continue;\n }\n\n formatted.push(...applyFormatter(formatter, type as keyof FormatterRegistry, payload, envelopeHeader));\n }\n\n if (formatted.length > 0) {\n console.log(formatted.join(\"\\n\"));\n }\n };\n\n // try to connect to an already existing server first\n try {\n const client = await connectUpstream(port);\n client.addEventListener(SENTRY_CONTENT_TYPE, event => onEnvelope!(JSON.parse(event.data)));\n // Early return - don't start our own server if we can connect to an upstream one\n return undefined;\n } catch (err) {\n // if we fail, fine then we'll start our own\n if (err instanceof Error && !err.message?.includes(port.toString())) {\n captureException(err);\n logger.error(\"Error when trying to connect to upstream sidecar:\");\n logger.error(err);\n process.exit(1);\n }\n }\n\n const serverInstance = await setupSpotlight({ port, filesToServe, basePath, isStandalone: true, allowedOrigins });\n\n // Subscribe the onEnvelope callback to the message buffer\n // This ensures it gets called whenever any envelope is added to the buffer\n getBuffer().subscribe(container => {\n const parsedEnvelope = container.getParsedEnvelope();\n if (parsedEnvelope) {\n onEnvelope(parsedEnvelope.envelope);\n }\n });\n\n return serverInstance;\n}\n\nexport default { meta, handler } satisfies Command;\n"],"names":["mdFormatters","logfmtFormatters","jsonFormatters","humanFormatters"],"mappings":"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"}
|
package/dist/server/cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormatterType } from './formatters/types.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { CLIHandlerOptions, Command } from './types/cli.ts';
|
|
3
3
|
export type CLIArgs = {
|
|
4
4
|
port: number;
|
|
5
5
|
debug: boolean;
|
|
@@ -11,7 +11,8 @@ export type CLIArgs = {
|
|
|
11
11
|
allowedOrigins: string[];
|
|
12
12
|
};
|
|
13
13
|
export declare function parseCLIArgs(): CLIArgs;
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const COMMANDS: Command[];
|
|
15
|
+
export declare const CLI_CMD_MAP: Map<string | undefined, import('./types/cli.ts').CLIHandler>;
|
|
15
16
|
export declare function main({ basePath, filesToServe, }?: {
|
|
16
17
|
basePath?: CLIHandlerOptions["basePath"];
|
|
17
18
|
filesToServe?: CLIHandlerOptions["filesToServe"];
|
package/dist/server/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
!function() {
|
|
3
3
|
try {
|
|
4
4
|
var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}, n = new e.Error().stack;
|
|
5
|
-
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "
|
|
5
|
+
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "df0f70e6-e8df-4b3a-bb62-3651a132707e", e._sentryDebugIdIdentifier = "sentry-dbid-df0f70e6-e8df-4b3a-bb62-3651a132707e");
|
|
6
6
|
} catch (e2) {
|
|
7
7
|
}
|
|
8
8
|
}();
|
|
@@ -68,9 +68,10 @@ function parseCLIArgs() {
|
|
|
68
68
|
tokens: true
|
|
69
69
|
});
|
|
70
70
|
let cmdArgs = void 0;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const firstPositional = preParse.tokens.find((token) => token.kind === "positional");
|
|
72
|
+
const isRunCommand = firstPositional?.value === "run";
|
|
73
|
+
if (isRunCommand && firstPositional) {
|
|
74
|
+
const cutOff = preParse.tokens.find((token) => token.index > firstPositional.index && token.kind === "positional");
|
|
74
75
|
cmdArgs = cutOff ? args.splice(cutOff.index) : [];
|
|
75
76
|
}
|
|
76
77
|
const { values, positionals } = parseArgs({
|
|
@@ -78,7 +79,7 @@ function parseCLIArgs() {
|
|
|
78
79
|
...PARSE_ARGS_CONFIG
|
|
79
80
|
});
|
|
80
81
|
const portInput = positionals.length === 1 && /^\d{1,5}$/.test(positionals[0]) ? positionals.shift() : values.port;
|
|
81
|
-
const port = portInput != null ? Number(portInput) :
|
|
82
|
+
const port = portInput != null ? Number(portInput) : isRunCommand ? MAGIC_PORT_FOR_DYNAMIC_ASSIGNMENT : DEFAULT_PORT;
|
|
82
83
|
if (Number.isNaN(port)) {
|
|
83
84
|
console.error(`Error: Invalid port number '${portInput}'`);
|
|
84
85
|
console.error("Port must be a valid number between 1 and 65535");
|
|
@@ -112,23 +113,21 @@ function parseCLIArgs() {
|
|
|
112
113
|
};
|
|
113
114
|
return result;
|
|
114
115
|
}
|
|
115
|
-
const
|
|
116
|
+
const COMMANDS = [run, tail, mcp, server];
|
|
117
|
+
const CLI_CMD_MAP = new Map([
|
|
116
118
|
["help", showHelp],
|
|
117
|
-
[
|
|
118
|
-
[
|
|
119
|
-
["run", run],
|
|
120
|
-
["server", server],
|
|
121
|
-
[void 0, server]
|
|
119
|
+
...COMMANDS.map((cmd) => [cmd.meta.name, cmd.handler]),
|
|
120
|
+
[void 0, server.handler]
|
|
122
121
|
]);
|
|
123
122
|
async function main({
|
|
124
123
|
basePath,
|
|
125
124
|
filesToServe
|
|
126
125
|
} = {}) {
|
|
127
|
-
|
|
126
|
+
const { cmd, cmdArgs, help, port, debug, format, allowedOrigins, open } = parseCLIArgs();
|
|
128
127
|
if (debug || process.env.SPOTLIGHT_DEBUG) {
|
|
129
128
|
enableDebugLogging(true);
|
|
130
129
|
}
|
|
131
|
-
const spotlightVersion = "4.
|
|
130
|
+
const spotlightVersion = "4.9.0";
|
|
132
131
|
logger.info(`Spotlight by Sentry - v${spotlightVersion}`);
|
|
133
132
|
metrics.count("cli.invocation", 1, {
|
|
134
133
|
attributes: {
|
|
@@ -137,15 +136,18 @@ async function main({
|
|
|
137
136
|
debug: String(debug)
|
|
138
137
|
}
|
|
139
138
|
});
|
|
140
|
-
if (help) {
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
if (help || cmd === "help") {
|
|
140
|
+
const targetCmd = cmd === "help" ? cmdArgs[0] : cmd;
|
|
141
|
+
return showHelp({
|
|
142
|
+
cmdArgs: targetCmd ? [targetCmd] : []
|
|
143
|
+
});
|
|
143
144
|
}
|
|
144
145
|
const handler = CLI_CMD_MAP.get(cmd) || showHelp;
|
|
145
146
|
return await handler({ cmd, cmdArgs, port, help, debug, format, basePath, filesToServe, allowedOrigins, open });
|
|
146
147
|
}
|
|
147
148
|
export {
|
|
148
149
|
CLI_CMD_MAP,
|
|
150
|
+
COMMANDS,
|
|
149
151
|
main,
|
|
150
152
|
parseCLIArgs
|
|
151
153
|
};
|
package/dist/server/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../../src/server/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport { metrics } from \"@sentry/node\";\nimport showHelp from \"./cli/help.ts\";\nimport mcp from \"./cli/mcp.ts\";\nimport run from \"./cli/run.ts\";\nimport server from \"./cli/server.ts\";\nimport tail from \"./cli/tail.ts\";\nimport { DEFAULT_PORT } from \"./constants.ts\";\nimport { AVAILABLE_FORMATTERS } from \"./formatters/types.ts\";\nimport type { FormatterType } from \"./formatters/types.ts\";\nimport { enableDebugLogging, logger } from \"./logger.ts\";\nimport type {
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../../src/server/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport { metrics } from \"@sentry/node\";\nimport showHelp from \"./cli/help.ts\";\nimport mcp from \"./cli/mcp.ts\";\nimport run from \"./cli/run.ts\";\nimport server from \"./cli/server.ts\";\nimport tail from \"./cli/tail.ts\";\nimport { DEFAULT_PORT } from \"./constants.ts\";\nimport { AVAILABLE_FORMATTERS } from \"./formatters/types.ts\";\nimport type { FormatterType } from \"./formatters/types.ts\";\nimport { enableDebugLogging, logger } from \"./logger.ts\";\nimport type { CLIHandlerOptions, Command } from \"./types/cli.ts\";\n\n// When port is set to `0` for a server to listen on,\n// it tells the OS to find a random available port.\n// This can then be retrieved after the server starts.\nconst MAGIC_PORT_FOR_DYNAMIC_ASSIGNMENT = 0;\nconst PARSE_ARGS_CONFIG = {\n options: {\n port: {\n type: \"string\",\n short: \"p\",\n default: undefined,\n },\n debug: {\n type: \"boolean\",\n short: \"d\",\n default: false,\n },\n format: {\n type: \"string\",\n short: \"f\",\n default: \"human\",\n },\n \"allowed-origin\": {\n type: \"string\",\n short: \"A\",\n multiple: true,\n default: [] as string[],\n },\n // Deprecated -- use the positional `mcp` argument instead\n \"stdio-mcp\": {\n type: \"boolean\",\n default: false,\n },\n open: {\n type: \"boolean\",\n short: \"o\",\n default: false,\n },\n help: {\n type: \"boolean\",\n short: \"h\",\n default: false,\n },\n },\n allowPositionals: true,\n strict: true,\n} as const;\n\nexport type CLIArgs = {\n port: number;\n debug: boolean;\n help: boolean;\n open: boolean;\n format: FormatterType;\n cmd: string | undefined;\n cmdArgs: string[];\n allowedOrigins: string[];\n};\n\nexport function parseCLIArgs(): CLIArgs {\n const args = Array.from(process.argv).slice(2);\n const preParse = parseArgs({\n ...PARSE_ARGS_CONFIG,\n strict: false,\n tokens: true,\n });\n let cmdArgs: string[] | undefined = undefined;\n // Only apply special handling for \"run\" if it's the actual command (first positional),\n // not when it appears as an argument to another command like \"spotlight help run\"\n const firstPositional = preParse.tokens.find(token => token.kind === \"positional\");\n const isRunCommand = firstPositional?.value === \"run\";\n if (isRunCommand && firstPositional) {\n const cutOff = preParse.tokens.find(token => token.index > firstPositional.index && token.kind === \"positional\");\n cmdArgs = cutOff ? args.splice(cutOff.index) : [];\n }\n const { values, positionals } = parseArgs({\n args,\n ...PARSE_ARGS_CONFIG,\n });\n\n // Handle legacy positional argument for port (backwards compatibility)\n const portInput = positionals.length === 1 && /^\\d{1,5}$/.test(positionals[0]) ? positionals.shift() : values.port;\n\n const port = portInput != null ? Number(portInput) : isRunCommand ? MAGIC_PORT_FOR_DYNAMIC_ASSIGNMENT : DEFAULT_PORT;\n if (Number.isNaN(port)) {\n // Validate port number\n console.error(`Error: Invalid port number '${portInput}'`);\n console.error(\"Port must be a valid number between 1 and 65535\");\n process.exit(1);\n }\n\n if (port < 0 || port > 65535) {\n console.error(`Error: Port ${port} is out of valid range (1-65535 or 0 for automatic assignment)`);\n process.exit(1);\n }\n\n if (values[\"stdio-mcp\"]) {\n positionals.unshift(\"mcp\");\n console.warn(\"Warning: --stdio-mcp is deprecated. Please use the positional argument 'mcp' instead.\");\n }\n\n const format = values.format as string;\n if (!AVAILABLE_FORMATTERS.includes(format as FormatterType)) {\n console.error(`Error: Invalid format '${format}'`);\n console.error(`Valid formats are: ${AVAILABLE_FORMATTERS.join(\", \")}`);\n process.exit(1);\n }\n\n // Parse allowed origins - supports both repeatable flags and comma-separated values\n const allowedOriginInput = values[\"allowed-origin\"] as string[];\n const allowedOrigins = allowedOriginInput.flatMap(origin => origin.split(\",\").map(o => o.trim())).filter(Boolean);\n\n const result: CLIArgs = {\n debug: values.debug as boolean,\n help: values.help as boolean,\n open: values.open as boolean,\n format: format as FormatterType,\n port,\n cmd: positionals[0],\n cmdArgs: cmdArgs ?? positionals.slice(1),\n allowedOrigins,\n };\n\n return result;\n}\n// Command registry - each command provides its own metadata\nexport const COMMANDS: Command[] = [run, tail, mcp, server];\n\n// Build command map from registry for fast lookup\nexport const CLI_CMD_MAP = new Map<string | undefined, Command[\"handler\"]>([\n [\"help\", showHelp],\n ...COMMANDS.map(cmd => [cmd.meta.name, cmd.handler] as [string, Command[\"handler\"]]),\n [undefined, server.handler],\n]);\n\nexport async function main({\n basePath,\n filesToServe,\n}: { basePath?: CLIHandlerOptions[\"basePath\"]; filesToServe?: CLIHandlerOptions[\"filesToServe\"] } = {}) {\n const { cmd, cmdArgs, help, port, debug, format, allowedOrigins, open } = parseCLIArgs();\n if (debug || process.env.SPOTLIGHT_DEBUG) {\n enableDebugLogging(true);\n }\n const spotlightVersion = process.env.npm_package_version || \"{unknown}\";\n logger.info(`Spotlight by Sentry - v${spotlightVersion}`);\n\n metrics.count(\"cli.invocation\", 1, {\n attributes: {\n command: cmd || \"server\",\n format,\n debug: String(debug),\n },\n });\n\n // Handle help: `spotlight --help`, `spotlight help`, `spotlight help <cmd>`, or `spotlight <cmd> --help`\n if (help || cmd === \"help\") {\n // If `spotlight <cmd> --help`, show help for that specific command\n // If `spotlight help <cmd>`, cmdArgs[0] contains the command name\n const targetCmd = cmd === \"help\" ? cmdArgs[0] : cmd;\n return showHelp({\n cmd: \"help\",\n cmdArgs: targetCmd ? [targetCmd] : [],\n port,\n help: true,\n debug,\n format,\n basePath,\n filesToServe,\n allowedOrigins,\n open,\n });\n }\n\n const handler = CLI_CMD_MAP.get(cmd) || showHelp;\n\n return await handler({ cmd, cmdArgs, port, help, debug, format, basePath, filesToServe, allowedOrigins, open });\n}\n"],"names":[],"mappings":"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"}
|