@zimic/interceptor
Version:
Next-gen TypeScript-first HTTP intercepting and mocking
354 lines (344 loc) • 14.1 kB
JavaScript
;
var chunkLWUFSWRA_js = require('./chunk-LWUFSWRA.js');
var chunkWCQVDF3K_js = require('./chunk-WCQVDF3K.js');
var yargs = require('yargs');
var helpers = require('yargs/helpers');
var fs = require('fs');
var path = require('path');
var color3 = require('picocolors');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var yargs__default = /*#__PURE__*/_interopDefault(yargs);
var fs__default = /*#__PURE__*/_interopDefault(fs);
var path__default = /*#__PURE__*/_interopDefault(path);
var color3__default = /*#__PURE__*/_interopDefault(color3);
// package.json
var version = "1.0.1";
// src/cli/browser/shared/constants.ts
var SERVICE_WORKER_FILE_NAME = "mockServiceWorker.js";
// src/cli/browser/init.ts
var MSW_ROOT_PATH = path__default.default.join(chunkWCQVDF3K_js.__require.resolve("msw"), "..", "..", "..");
var MOCK_SERVICE_WORKER_PATH = path__default.default.join(MSW_ROOT_PATH, "lib", SERVICE_WORKER_FILE_NAME);
async function initializeBrowserServiceWorker({ publicDirectory }) {
await fs__default.default.promises.mkdir(publicDirectory, { recursive: true });
const destinationPath = path__default.default.join(publicDirectory, SERVICE_WORKER_FILE_NAME);
await fs__default.default.promises.copyFile(MOCK_SERVICE_WORKER_PATH, destinationPath);
chunkLWUFSWRA_js.logger.info(`Service worker script saved to ${color3__default.default.magenta(destinationPath)}.`);
}
chunkWCQVDF3K_js.__name(initializeBrowserServiceWorker, "initializeBrowserServiceWorker");
var init_default = initializeBrowserServiceWorker;
// src/utils/processes.ts
var PROCESS_EXIT_EVENTS = Object.freeze([
"beforeExit",
"uncaughtExceptionMonitor",
"SIGINT",
"SIGTERM",
"SIGHUP",
"SIGBREAK"
]);
var PROCESS_EXIT_CODE_BY_EXIT_EVENT = {
beforeExit: void 0,
uncaughtExceptionMonitor: void 0,
SIGINT: 130,
SIGTERM: 143,
SIGHUP: 129,
SIGBREAK: 131
};
var importExeca = chunkLWUFSWRA_js.createCachedDynamicImport_default(() => import('execa'));
var CommandError = class _CommandError extends Error {
static {
chunkWCQVDF3K_js.__name(this, "CommandError");
}
static DEFAULT_EXIT_CODE = 1;
command;
exitCode;
signal;
constructor(executable, options) {
const message = _CommandError.createMessage(executable, options);
super(message);
this.name = "CommandError";
this.command = options.command ?? [executable];
this.exitCode = this.getExitCode(options);
this.signal = options.signal;
}
getExitCode(options) {
const existingExitCode = options.exitCode;
const exitCodeInferredFromSignal = options.signal === void 0 ? void 0 : PROCESS_EXIT_CODE_BY_EXIT_EVENT[options.signal];
return existingExitCode ?? exitCodeInferredFromSignal ?? _CommandError.DEFAULT_EXIT_CODE;
}
static createMessage(command, options) {
const suffix = options.originalMessage ? `: ${options.originalMessage}` : "";
if (options.exitCode === void 0 && options.signal === void 0) {
return `Command '${command}' failed${suffix}`;
}
const prefix = `Command '${command}' exited `;
const infix = options.exitCode === void 0 ? `after signal ${options.signal}` : `with code ${options.exitCode}`;
return `${prefix}${infix}${suffix}`;
}
};
async function runCommand(commandEntry, commandArguments) {
const { execa: $, ExecaError } = await importExeca();
try {
await $(commandEntry, commandArguments, { stdio: "inherit" });
} catch (error) {
if (!(error instanceof ExecaError)) {
throw error;
}
const commandError = new CommandError(commandEntry, {
command: [commandEntry, ...commandArguments],
exitCode: error.exitCode,
signal: error.signal,
originalMessage: error.originalMessage
});
throw commandError;
}
}
chunkWCQVDF3K_js.__name(runCommand, "runCommand");
async function startInterceptorServer({
hostname,
port,
logUnhandledRequests,
tokensDirectory,
ephemeral,
onReady
}) {
const server = chunkLWUFSWRA_js.createInterceptorServer({
hostname,
port,
logUnhandledRequests,
tokensDirectory
});
async function handleExitEvent(exitEvent) {
await server.stop();
for (const { exitEvent: exitEvent2, exitHandler } of exitHandlerGroups) {
process.off(exitEvent2, exitHandler);
}
const exitCode = exitEvent ? PROCESS_EXIT_CODE_BY_EXIT_EVENT[exitEvent] : void 0;
if (exitCode !== void 0) {
process.exit(exitCode);
}
}
chunkWCQVDF3K_js.__name(handleExitEvent, "handleExitEvent");
const exitHandlerGroups = PROCESS_EXIT_EVENTS.map((exitEvent) => ({
exitEvent,
exitHandler: handleExitEvent.bind(null, exitEvent)
}));
for (const { exitEvent, exitHandler } of exitHandlerGroups) {
process.on(exitEvent, exitHandler);
}
await server.start();
chunkLWUFSWRA_js.logger.info(
`${ephemeral ? "Ephemeral s" : "S"}erver is running on ${color3__default.default.yellow(`${server.hostname}:${server.port}`)}`
);
const isDangerouslyUnprotected = !tokensDirectory && process.env.NODE_ENV === "production";
if (isDangerouslyUnprotected) {
chunkLWUFSWRA_js.logger.warn(
[
`Attention: this interceptor server is ${color3__default.default.bold(color3__default.default.red("unprotected"))}. Do not expose it publicly without authentication.`,
"",
"Learn more: https://zimic.dev/docs/interceptor/guides/http/remote-interceptors#interceptor-server-authentication"
].join("\n")
);
}
if (onReady) {
try {
await runCommand(onReady.command, onReady.arguments);
} catch (error) {
console.error(error);
if (!(error instanceof CommandError)) {
throw error;
}
process.exit(error.exitCode);
}
}
if (ephemeral) {
await handleExitEvent(void 0);
process.exit(0);
}
}
chunkWCQVDF3K_js.__name(startInterceptorServer, "startInterceptorServer");
var start_default = startInterceptorServer;
async function createInterceptorServerToken({
tokenName,
tokensDirectory
}) {
const token = await chunkLWUFSWRA_js.createInterceptorToken({ name: tokenName, tokensDirectory });
chunkLWUFSWRA_js.logger.info(
[
`${color3__default.default.green(color3__default.default.bold("\u2714"))} Token${tokenName ? ` ${color3__default.default.green(tokenName)}` : ""} created:`,
"",
color3__default.default.yellow(token.value),
"",
"Store this token securely. It cannot be retrieved later.",
"",
`To enable authentication in your interceptor server, use the ${color3__default.default.cyan("--tokens-dir")} option. Only remote interceptors bearing a valid token will be allowed to connect.`,
"",
`${color3__default.default.dim("$")} zimic-interceptor server start ${color3__default.default.cyan("--tokens-dir")} ${color3__default.default.magenta(tokensDirectory)}`,
"",
"Learn more: https://zimic.dev/docs/interceptor/guides/http/remote-interceptors#interceptor-server-authentication"
].join("\n")
);
}
chunkWCQVDF3K_js.__name(createInterceptorServerToken, "createInterceptorServerToken");
// src/cli/server/token/list.ts
async function listInterceptorServerTokens({ tokensDirectory }) {
const tokens = await chunkLWUFSWRA_js.listInterceptorTokens({ tokensDirectory });
chunkLWUFSWRA_js.logger.raw.table(
[
{ title: "ID", property: "id" },
{ title: "NAME", property: "name" },
{ title: "CREATED AT", property: "createdAt" }
],
tokens.map((token) => ({
id: token.id,
name: token.name ?? "",
createdAt: token.createdAt.toISOString()
}))
);
}
chunkWCQVDF3K_js.__name(listInterceptorServerTokens, "listInterceptorServerTokens");
async function removeInterceptorServerToken({ tokenId, tokensDirectory }) {
const token = await chunkLWUFSWRA_js.readInterceptorTokenFromFile(tokenId, { tokensDirectory });
if (!token) {
chunkLWUFSWRA_js.logger.error(`${color3__default.default.red(color3__default.default.bold("\u2718"))} Token ${color3__default.default.red(tokenId)} not found.`);
process.exit(1);
}
await chunkLWUFSWRA_js.removeInterceptorToken(token.id, { tokensDirectory });
chunkLWUFSWRA_js.logger.info(`${color3__default.default.green(color3__default.default.bold("\u2714"))} Token ${color3__default.default.green(token.name ?? token.id)} removed.`);
}
chunkWCQVDF3K_js.__name(removeInterceptorServerToken, "removeInterceptorServerToken");
// src/cli/cli.ts
async function runCLI() {
await yargs__default.default(helpers.hideBin(process.argv)).scriptName("zimic-interceptor").version(version).showHelpOnFail(false).strict().command(
"browser",
"Manage your browser mock configuration.",
(yargs2) => yargs2.demandCommand().command(
"init <publicDirectory>",
"Initialize the browser service worker configuration.",
(yargs3) => yargs3.positional("publicDirectory", {
type: "string",
description: "The path to the public directory of your application.",
demandOption: true
}),
async (cliArguments) => {
await init_default({
publicDirectory: cliArguments.publicDirectory
});
}
)
).command(
"server",
"Manage interceptor servers.",
(yargs2) => yargs2.demandCommand().command(
"start [-- onReady]",
"Start an interceptor server.",
(yargs3) => yargs3.positional("onReady", {
description: "A command to run when the server is ready to accept connections.",
type: "string"
}).option("hostname", {
type: "string",
description: "The hostname to start the server on.",
alias: "h",
default: "localhost"
}).option("port", {
type: "number",
description: "The port to start the server on.",
alias: "p"
}).option("ephemeral", {
type: "boolean",
description: "Whether the server should stop automatically after the on-ready command finishes. If no on-ready command is provided and ephemeral is true, the server will stop immediately after starting.",
alias: "e",
default: false
}).option("log-unhandled-requests", {
type: "boolean",
description: "Whether to log a warning when no interceptors were found for the base URL of a request. If an interceptor was matched, the logging behavior for that base URL is configured in the interceptor itself.",
alias: "l"
}).option("tokens-dir", {
type: "string",
description: "The directory where the authorized interceptor authentication tokens are saved. If provided, only remote interceptors bearing a valid token will be accepted. This option is essential if you are exposing your interceptor server publicly. For local development and testing, though, `--tokens-dir` is optional.",
alias: "t"
}),
async (cliArguments) => {
const onReadyCommand = cliArguments._.at(2)?.toString();
const onReadyCommandArguments = cliArguments._.slice(3).map((argument) => argument.toString());
await start_default({
hostname: cliArguments.hostname,
port: cliArguments.port,
ephemeral: cliArguments.ephemeral,
logUnhandledRequests: cliArguments.logUnhandledRequests,
tokensDirectory: cliArguments.tokensDir,
onReady: onReadyCommand ? {
command: onReadyCommand.toString(),
arguments: onReadyCommandArguments
} : void 0
});
}
).command(
"token",
"Manage remote interceptor authentication tokens.",
(yargs3) => yargs3.demandCommand().command(
"create",
"Create an interceptor token.",
(yargs4) => yargs4.option("name", {
type: "string",
description: "The name of the token to create.",
alias: "n"
}).option("tokens-dir", {
type: "string",
description: "The directory where the created interceptor token will be saved.",
alias: "t",
default: chunkLWUFSWRA_js.DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY
}),
async (cliArguments) => {
await createInterceptorServerToken({
tokenName: cliArguments.name,
tokensDirectory: cliArguments.tokensDir
});
}
).command(
["ls", "list"],
"List the authorized interceptor tokens.",
(yargs4) => yargs4.option("tokens-dir", {
type: "string",
description: "The directory where the interceptor tokens are saved.",
alias: "t",
default: chunkLWUFSWRA_js.DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY
}),
async (cliArguments) => {
await listInterceptorServerTokens({
tokensDirectory: cliArguments.tokensDir
});
}
).command(
["rm <tokenId>", "remove <tokenId>"],
"Remove (invalidate) an interceptor token. Existing connections will not be affected, so restarting the server is recommended to disconnect all interceptors.",
(yargs4) => yargs4.positional("tokenId", {
type: "string",
description: "The identifier of the token to remove.",
demandOption: true
}).option("tokens-dir", {
type: "string",
description: "The directory where the interceptor tokens are saved.",
alias: "t",
default: chunkLWUFSWRA_js.DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY
}),
async (cliArguments) => {
await removeInterceptorServerToken({
tokenId: cliArguments.tokenId,
tokensDirectory: cliArguments.tokensDir
});
}
)
)
).parse();
}
chunkWCQVDF3K_js.__name(runCLI, "runCLI");
var cli_default = runCLI;
// src/cli/index.ts
void cli_default();
/* istanbul ignore if -- @preserve
* This is a safeguard if the error is not an ExecaError. It is not expected to run. */
/* istanbul ignore if -- @preserve
* A CommandError is always expected here. */
//# sourceMappingURL=cli.js.map
//# sourceMappingURL=cli.js.map