From 0f2f22c585a6c98be41edb7cec1b6ce12cd51d11 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Thu, 12 Feb 2026 23:42:12 -0800 Subject: [PATCH 01/10] feat: add logs:edge-functions command and historical log support Add edge function log streaming and historical log fetching for both function and edge function logs. The deploy command output now shows CLI hints for accessing logs when functions are deployed. - Add `logs:edge-functions` command with WebSocket streaming and historical mode via `--from`/`--to` - Add `--from`/`--to` date options to `logs:function` for historical log fetching via analytics API - Add `--deploy-id` option to `logs:function` and `logs:edge-functions` to look up functions from a specific deploy - Accept function ID (not just name) in `logs:function` - Show function/edge-function CLI log hints in deploy output - Create shared `log-api.ts` utility for date parsing, API fetching, and log formatting Co-Authored-By: Claude Opus 4.6 --- src/commands/deploy/deploy.ts | 42 +++- src/commands/logs/edge-functions.ts | 100 ++++++++ src/commands/logs/functions.ts | 33 ++- src/commands/logs/index.ts | 41 +++- src/commands/logs/log-api.ts | 74 ++++++ .../commands/logs/edge-functions.test.ts | 228 ++++++++++++++++++ .../commands/logs/functions.test.ts | 104 ++++++++ 7 files changed, 611 insertions(+), 11 deletions(-) create mode 100644 src/commands/logs/edge-functions.ts create mode 100644 src/commands/logs/log-api.ts create mode 100644 tests/integration/commands/logs/edge-functions.test.ts diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index 5b847a8654d..d33b569e61e 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -551,6 +551,8 @@ const runDeploy = async ({ functionLogsUrl: string edgeFunctionLogsUrl: string sourceZipFileName?: string + deployedFunctions: { name: string; id: string }[] + hasEdgeFunctions: boolean }> => { let results let deployId = existingDeployId @@ -662,6 +664,13 @@ const runDeploy = async ({ edgeFunctionLogsUrl += `?scope=deployid:${deployId}` } + const availableFunctions = (results.deploy.available_functions ?? []) as { n?: string; oid?: string }[] + const deployedFunctions = availableFunctions + .filter((fn): fn is { n: string; oid: string } => Boolean(fn.n && fn.oid)) + .map((fn) => ({ name: fn.n, id: fn.oid })) + + const hasEdgeFunctions = Boolean((results.deploy as any).edge_functions_count) + return { siteId: results.deploy.site_id, siteName: results.deploy.name, @@ -672,6 +681,8 @@ const runDeploy = async ({ functionLogsUrl, edgeFunctionLogsUrl, sourceZipFileName: uploadSourceZipResult?.sourceZipFileName, + deployedFunctions, + hasEdgeFunctions, } } @@ -779,6 +790,7 @@ interface JsonData { logs: string function_logs: string edge_function_logs: string + deployed_functions: { name: string; id: string }[] url?: string source_zip_filename?: string } @@ -796,10 +808,18 @@ const printResults = ({ results: Awaited> runBuildCommand: boolean }): void => { - const msgData: Record = { + const buildLogsData: Record = { 'Build logs': terminalLink(results.logsUrl, results.logsUrl, { fallback: false }), + } + + const functionLogsData: Record = { 'Function logs': terminalLink(results.functionLogsUrl, results.functionLogsUrl, { fallback: false }), - 'Edge function Logs': terminalLink(results.edgeFunctionLogsUrl, results.edgeFunctionLogsUrl, { fallback: false }), + 'Function CLI': `netlify logs:function --deploy-id ${results.deployId} `, + } + + const edgeFunctionLogsData: Record = { + 'Edge function logs': terminalLink(results.edgeFunctionLogsUrl, results.edgeFunctionLogsUrl, { fallback: false }), + 'Edge function CLI': `netlify logs:edge-functions --deploy-id ${results.deployId}`, } log('') @@ -816,6 +836,7 @@ const printResults = ({ logs: results.logsUrl, function_logs: results.functionLogsUrl, edge_function_logs: results.edgeFunctionLogsUrl, + deployed_functions: results.deployedFunctions, } if (deployToProduction) { jsonData.url = results.siteUrl @@ -847,7 +868,22 @@ const printResults = ({ }), ) - log(prettyjson.render(msgData)) + log(prettyjson.render(buildLogsData)) + + if (results.deployedFunctions.length > 0) { + log() + log(prettyjson.render(functionLogsData)) + } + + if (results.hasEdgeFunctions) { + log() + log(prettyjson.render(edgeFunctionLogsData)) + } + + if (results.deployedFunctions.length > 0 || results.hasEdgeFunctions) { + log() + log(chalk.dim('Use --from and --to to fetch historical logs (ISO 8601 format)')) + } if (!deployToProduction) { log() diff --git a/src/commands/logs/edge-functions.ts b/src/commands/logs/edge-functions.ts new file mode 100644 index 00000000000..dcc3e88104a --- /dev/null +++ b/src/commands/logs/edge-functions.ts @@ -0,0 +1,100 @@ +import { OptionValues } from 'commander' +import inquirer from 'inquirer' + +import { chalk, log } from '../../utils/command-helpers.js' +import { getWebSocket } from '../../utils/websockets/index.js' +import type BaseCommand from '../base-command.js' + +import { parseDateToMs, fetchHistoricalLogs, printHistoricalLogs, formatLogEntry } from './log-api.js' +import { CLI_LOG_LEVEL_CHOICES_STRING, LOG_LEVELS_LIST } from './log-levels.js' +import { getName } from './build.js' + +export const logsEdgeFunction = async ( + options: OptionValues, + command: BaseCommand, +) => { + let deployId: string | undefined = options.deployId + await command.authenticate() + + const client = command.netlify.api + const { site } = command.netlify + const { id: siteId } = site + + if (!siteId) { + log('You must link a project before attempting to view edge function logs') + return + } + + if (options.level && !options.level.every((level: string) => LOG_LEVELS_LIST.includes(level))) { + log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`) + } + + const levelsToPrint = options.level || LOG_LEVELS_LIST + + if (options.from) { + const fromMs = parseDateToMs(options.from) + const toMs = options.to ? parseDateToMs(options.to) : Date.now() + + const url = `https://analytics.services.netlify.com/v2/sites/${siteId}/edge_function_logs?from=${fromMs.toString()}&to=${toMs.toString()}` + const data = await fetchHistoricalLogs({ url, accessToken: client.accessToken ?? '' }) + printHistoricalLogs(data, levelsToPrint) + return + } + + const userId = command.netlify.globalConfig.get('userId') + + if (!deployId) { + const deploys = await client.listSiteDeploys({ siteId }) + + if (deploys.length === 0) { + log('No deploys found for the project') + return + } + + if (deploys.length === 1) { + deployId = deploys[0].id + } else { + const { result } = await inquirer.prompt({ + name: 'result', + type: 'list', + message: `Select a deploy\n\n${chalk.yellow('*')} indicates a deploy created by you`, + choices: deploys.map((deploy: any) => ({ + name: getName({ deploy, userId }), + value: deploy.id, + })), + }) + + deployId = result + } + } + + const ws = getWebSocket('wss://socketeer.services.netlify.com/edge-function/logs') + + ws.on('open', () => { + ws.send( + JSON.stringify({ + deploy_id: deployId, + site_id: siteId, + access_token: client.accessToken, + since: new Date().toISOString(), + }), + ) + }) + + ws.on('message', (data: string) => { + const logData = JSON.parse(data) + if (!levelsToPrint.includes(logData.level.toLowerCase())) { + return + } + log(formatLogEntry(logData)) + }) + + ws.on('close', () => { + log('Connection closed') + }) + + ws.on('error', (err: any) => { + log('Connection error') + log(err) + }) +} diff --git a/src/commands/logs/functions.ts b/src/commands/logs/functions.ts index 54fa8f54599..22d0932cda4 100644 --- a/src/commands/logs/functions.ts +++ b/src/commands/logs/functions.ts @@ -5,6 +5,7 @@ import { chalk, log } from '../../utils/command-helpers.js' import { getWebSocket } from '../../utils/websockets/index.js' import type BaseCommand from '../base-command.js' +import { parseDateToMs, fetchHistoricalLogs, printHistoricalLogs } from './log-api.js' import { CLI_LOG_LEVEL_CHOICES_STRING, LOG_LEVELS, LOG_LEVELS_LIST } from './log-levels.js' function getLog(logData: { level: string; message: string }) { @@ -28,8 +29,10 @@ function getLog(logData: { level: string; message: string }) { } export const logsFunction = async (functionName: string | undefined, options: OptionValues, command: BaseCommand) => { + await command.authenticate() + const client = command.netlify.api - const { site } = command.netlify + const { site, siteInfo } = command.netlify const { id: siteId } = site if (options.level && !options.level.every((level: string) => LOG_LEVELS_LIST.includes(level))) { @@ -38,17 +41,24 @@ export const logsFunction = async (functionName: string | undefined, options: Op const levelsToPrint = options.level || LOG_LEVELS_LIST - // TODO: Update type once the open api spec is updated https://open-api.netlify.com/#tag/function/operation/searchSiteFunctions - const { functions = [] } = (await client.searchSiteFunctions({ siteId: siteId! })) as any + let functions: any[] + if (options.deployId) { + const deploy = (await client.getSiteDeploy({ siteId: siteId!, deployId: options.deployId })) as any + functions = deploy.available_functions ?? [] + } else { + // TODO: Update type once the open api spec is updated https://open-api.netlify.com/#tag/function/operation/searchSiteFunctions + const result = (await client.searchSiteFunctions({ siteId: siteId! })) as any + functions = result.functions ?? [] + } if (functions.length === 0) { - log(`No functions found for the project`) + log(`No functions found for the ${options.deployId ? 'deploy' : 'project'}`) return } let selectedFunction if (functionName) { - selectedFunction = functions.find((fn: any) => fn.n === functionName) + selectedFunction = functions.find((fn: any) => fn.n === functionName || fn.oid === functionName) } else { const { result } = await inquirer.prompt({ name: 'result', @@ -65,7 +75,18 @@ export const logsFunction = async (functionName: string | undefined, options: Op return } - const { a: accountId, oid: functionId } = selectedFunction + const { a: accountId, n: resolvedFunctionName, oid: functionId } = selectedFunction + + if (options.from) { + const fromMs = parseDateToMs(options.from) + const toMs = options.to ? parseDateToMs(options.to) : Date.now() + const branch = siteInfo.build_settings?.repo_branch ?? 'main' + + const url = `https://analytics.services.netlify.com/v2/sites/${siteId}/branch/${branch}/function_logs/${resolvedFunctionName}?from=${fromMs.toString()}&to=${toMs.toString()}` + const data = await fetchHistoricalLogs({ url, accessToken: client.accessToken ?? '' }) + printHistoricalLogs(data, levelsToPrint) + return + } const ws = getWebSocket('wss://socketeer.services.netlify.com/function/logs') diff --git a/src/commands/logs/index.ts b/src/commands/logs/index.ts index 3c1be8415ac..d0053ed1db6 100644 --- a/src/commands/logs/index.ts +++ b/src/commands/logs/index.ts @@ -22,11 +22,17 @@ export const createLogsFunctionCommand = (program: BaseCommand) => { .addOption( new Option('-l, --level ', `Log levels to stream. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`), ) - .addArgument(new Argument('[functionName]', 'Name of the function to stream logs for')) + .addOption(new Option('--deploy-id ', 'Deploy ID to look up the function from')) + .addOption(new Option('--from ', 'Start date for historical logs (ISO 8601 format)')) + .addOption(new Option('--to ', 'End date for historical logs (ISO 8601 format, defaults to now)')) + .addArgument(new Argument('[functionName]', 'Name or ID of the function to stream logs for')) .addExamples([ 'netlify logs:function', 'netlify logs:function my-function', + 'netlify logs:function my-function --deploy-id ', 'netlify logs:function my-function -l info warn', + 'netlify logs:function my-function --from 2026-01-01T00:00:00Z', + 'netlify logs:function my-function --from 2026-01-01T00:00:00Z --to 2026-01-02T00:00:00Z', ]) .description('Stream netlify function logs to the console') .action(async (functionName: string | undefined, options: OptionValues, command: BaseCommand) => { @@ -35,15 +41,46 @@ export const createLogsFunctionCommand = (program: BaseCommand) => { }) } +export const createLogsEdgeFunctionCommand = (program: BaseCommand) => { + program + .command('logs:edge-functions') + .addOption( + new Option('-l, --level ', `Log levels to stream. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`), + ) + .addOption(new Option('--deploy-id ', 'Deploy ID to stream edge function logs for')) + .addOption(new Option('--from ', 'Start date for historical logs (ISO 8601 format)')) + .addOption(new Option('--to ', 'End date for historical logs (ISO 8601 format, defaults to now)')) + .addExamples([ + 'netlify logs:edge-functions', + 'netlify logs:edge-functions --deploy-id ', + 'netlify logs:edge-functions --from 2026-01-01T00:00:00Z', + 'netlify logs:edge-functions --from 2026-01-01T00:00:00Z --to 2026-01-02T00:00:00Z', + 'netlify logs:edge-functions -l info warn', + ]) + .description('Stream netlify edge function logs to the console') + .action(async (options: OptionValues, command: BaseCommand) => { + const { logsEdgeFunction } = await import('./edge-functions.js') + await logsEdgeFunction(options, command) + }) +} + export const createLogsCommand = (program: BaseCommand) => { createLogsBuildCommand(program) createLogsFunctionCommand(program) + createLogsEdgeFunctionCommand(program) + return program .command('logs') .alias('log') .description('Stream logs from your project') - .addExamples(['netlify logs:deploy', 'netlify logs:function', 'netlify logs:function my-function']) + .addExamples([ + 'netlify logs:deploy', + 'netlify logs:function', + 'netlify logs:function my-function', + 'netlify logs:edge-functions', + 'netlify logs:edge-functions --deploy-id ', + ]) .action((_, command: BaseCommand) => command.help()) } diff --git a/src/commands/logs/log-api.ts b/src/commands/logs/log-api.ts new file mode 100644 index 00000000000..78e1796ce9a --- /dev/null +++ b/src/commands/logs/log-api.ts @@ -0,0 +1,74 @@ +import { chalk, log } from '../../utils/command-helpers.js' +import { LOG_LEVELS } from './log-levels.js' + +export function parseDateToMs(dateString: string): number { + const ms = new Date(dateString).getTime() + if (Number.isNaN(ms)) { + throw new Error(`Invalid date: ${dateString}`) + } + return ms +} + +export async function fetchHistoricalLogs({ + url, + accessToken, +}: { + url: string + accessToken: string +}): Promise { + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + + if (!response.ok) { + const errorData = (await response.json().catch(() => ({}))) as { error?: string } + throw new Error(errorData.error ?? `HTTP ${response.status.toString()}: ${response.statusText}`) + } + + return response.json() +} + +export function formatLogEntry(entry: { timestamp?: string; level?: string; message?: string }): string { + const timestamp = entry.timestamp ? new Date(entry.timestamp).toISOString() : '' + let levelString = entry.level ?? '' + + switch (levelString.toUpperCase()) { + case LOG_LEVELS.INFO: + levelString = chalk.blueBright(levelString) + break + case LOG_LEVELS.WARN: + levelString = chalk.yellowBright(levelString) + break + case LOG_LEVELS.ERROR: + levelString = chalk.redBright(levelString) + break + default: + break + } + + const parts = [timestamp, levelString, entry.message ?? ''].filter(Boolean) + return parts.join(' ') +} + +export function printHistoricalLogs( + data: unknown, + levelsToPrint: string[], +): void { + const entries = Array.isArray(data) ? data : [] + + if (entries.length === 0) { + log('No logs found for the specified time range') + return + } + + for (const entry of entries) { + const level = (entry.level ?? '').toLowerCase() + if (levelsToPrint.length > 0 && !levelsToPrint.includes(level)) { + continue + } + log(formatLogEntry(entry)) + } +} diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts new file mode 100644 index 00000000000..7dae2d793e5 --- /dev/null +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -0,0 +1,228 @@ +import { Mock, afterAll, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' + +import BaseCommand from '../../../../src/commands/base-command.js' +import { createLogsEdgeFunctionCommand } from '../../../../src/commands/logs/index.js' +import { LOG_LEVELS } from '../../../../src/commands/logs/log-levels.js' +import { log } from '../../../../src/utils/command-helpers.js' +import { getWebSocket } from '../../../../src/utils/websockets/index.js' +import { startMockApi } from '../../utils/mock-api-vitest.js' +import { getEnvironmentVariables } from '../../utils/mock-api.js' + +vi.mock('../../../../src/utils/websockets/index.js', () => ({ + getWebSocket: vi.fn(), +})) + +vi.mock('../../../../src/utils/command-helpers.js', async () => { + const actual = await vi.importActual('../../../../src/utils/command-helpers.js') + return { + ...actual, + log: vi.fn(), + } +}) + +vi.mock('inquirer', () => ({ + default: { + prompt: vi.fn().mockResolvedValue({ result: 'deploy-id-1' }), + registerPrompt: vi.fn(), + }, +})) + +const siteInfo = { + admin_url: 'https://app.netlify.com/projects/site-name/overview', + ssl_url: 'https://site-name.netlify.app/', + id: 'site_id', + name: 'site-name', + build_settings: { repo_url: 'https://github.com/owner/repo' }, +} + +const routes = [ + { + path: 'accounts', + response: [{ slug: 'test-account' }], + }, + { + path: 'sites', + response: [], + }, + { path: 'sites/site_id', response: siteInfo }, + { path: 'sites/site_id/service-instances', response: [] }, + { + path: 'user', + response: { name: 'test user', slug: 'test-user', email: 'user@test.com' }, + }, + { + path: 'sites/site_id/deploys', + response: [ + { + id: 'deploy-id-1', + context: 'production', + user_id: 'user-1', + review_id: null, + }, + ], + }, +] + +describe('logs:edge-functions command', () => { + const originalEnv = process.env + + let program: BaseCommand + + afterEach(() => { + vi.clearAllMocks() + process.env = { ...originalEnv } + }) + + beforeEach(() => { + program = new BaseCommand('netlify') + + createLogsEdgeFunctionCommand(program) + }) + + afterAll(() => { + vi.restoreAllMocks() + vi.resetModules() + + process.env = { ...originalEnv } + }) + + test('should setup the edge functions stream correctly', async () => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:edge-functions']) + + expect(spyWebsocket).toHaveBeenCalledOnce() + expect(spyWebsocket).toHaveBeenCalledWith('wss://socketeer.services.netlify.com/edge-function/logs') + expect(spyOn).toHaveBeenCalledTimes(4) + }) + + test('should send the correct payload to the websocket', async () => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:edge-functions']) + + const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') + expect(setupCall).toBeDefined() + + const openCallback = setupCall?.[1] + openCallback?.() + + expect(spySend).toHaveBeenCalledOnce() + const call = spySend.mock.calls[0] + const [message] = call + const body = JSON.parse(message) + + expect(body.deploy_id).toEqual('deploy-id-1') + expect(body.site_id).toEqual('site_id') + expect(body.access_token).toEqual(env.NETLIFY_AUTH_TOKEN) + expect(body.since).toBeDefined() + }) + + test('should use deploy ID from --deploy-id option when provided', async () => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:edge-functions', '--deploy-id', 'my-deploy-id']) + + const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') + const openCallback = setupCall?.[1] + openCallback?.() + + const call = spySend.mock.calls[0] + const body = JSON.parse(call[0]) + + expect(body.deploy_id).toEqual('my-deploy-id') + }) + + test('should print only specified log levels', async () => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + const spyLog = log as unknown as Mock + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:edge-functions', '--level', 'info']) + const messageCallback = spyOn.mock.calls.find((args) => args[0] === 'message') + const messageCallbackFunc = messageCallback?.[1] + + messageCallbackFunc?.(JSON.stringify({ level: LOG_LEVELS.INFO, message: 'Hello World' })) + messageCallbackFunc?.(JSON.stringify({ level: LOG_LEVELS.WARN, message: 'There was a warning' })) + + expect(spyLog).toHaveBeenCalledTimes(1) + }) + + test('should fetch historical logs when --from is specified', async () => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + const mockLogs = [ + { timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Edge function executed' }, + ] + + const originalFetch = global.fetch + const spyFetch = vi.fn().mockImplementation((url: string) => { + if (url.includes('analytics.services.netlify.com')) { + return Promise.resolve({ + ok: true, + json: () => Promise.resolve(mockLogs), + }) + } + return originalFetch(url) + }) + global.fetch = spyFetch + + try { + await program.parseAsync(['', '', 'logs:edge-functions', '--from', '2026-01-01T00:00:00Z']) + + expect(spyWebsocket).not.toHaveBeenCalled() + + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => + args[0].includes('analytics.services.netlify.com'), + ) + expect(analyticsCall).toBeDefined() + expect(analyticsCall![0]).toContain('edge_function_logs') + expect(analyticsCall![0]).toContain('site_id') + } finally { + global.fetch = originalFetch + } + }) +}) diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index f7c740a11c2..6ba82651947 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -201,4 +201,108 @@ describe('logs:function command', () => { expect(spyLog).toHaveBeenCalledTimes(2) }) + + test('should find function by ID', async ({}) => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:function', 'function-id']) + + const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') + const openCallback = setupCall?.[1] + openCallback?.() + + const call = spySend.mock.calls[0] + const body = JSON.parse(call[0]) + + expect(body.function_id).toEqual('function-id') + expect(body.site_id).toEqual('site_id') + }) + + test('should look up function from deploy when --deploy is specified', async ({}) => { + const deployRoutes = [ + ...routes, + { + path: 'sites/site_id/deploys/deploy-123', + response: { + id: 'deploy-123', + available_functions: [ + { n: 'deploy-function', oid: 'deploy-fn-id' }, + ], + }, + }, + ] + const { apiUrl } = await startMockApi({ routes: deployRoutes }) + const spyWebsocket = getWebSocket as unknown as Mock + const spyOn = vi.fn() + const spySend = vi.fn() + spyWebsocket.mockReturnValue({ + on: spyOn, + send: spySend, + }) + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + await program.parseAsync(['', '', 'logs:function', 'deploy-function', '--deploy-id', 'deploy-123']) + + const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') + const openCallback = setupCall?.[1] + openCallback?.() + + const call = spySend.mock.calls[0] + const body = JSON.parse(call[0]) + + expect(body.function_id).toEqual('deploy-fn-id') + expect(body.site_id).toEqual('site_id') + }) + + test('should fetch historical logs when --from is specified', async ({}) => { + const { apiUrl } = await startMockApi({ routes }) + const spyWebsocket = getWebSocket as unknown as Mock + + const env = getEnvironmentVariables({ apiUrl }) + Object.assign(process.env, env) + + const mockLogs = [ + { timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Function executed' }, + ] + + const originalFetch = global.fetch + const spyFetch = vi.fn().mockImplementation((url: string) => { + if (url.includes('analytics.services.netlify.com')) { + return Promise.resolve({ + ok: true, + json: () => Promise.resolve(mockLogs), + }) + } + return originalFetch(url) + }) + global.fetch = spyFetch + + try { + await program.parseAsync(['', '', 'logs:function', 'cool-function', '--from', '2026-01-01T00:00:00Z']) + + expect(spyWebsocket).not.toHaveBeenCalled() + + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => + args[0].includes('analytics.services.netlify.com'), + ) + expect(analyticsCall).toBeDefined() + expect(analyticsCall![0]).toContain('function_logs') + expect(analyticsCall![0]).toContain('cool-function') + expect(analyticsCall![0]).toContain('site_id') + } finally { + global.fetch = originalFetch + } + }) }) From 552a6847d4ae8d6a8802e0ba6079271677b1a784 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 00:09:52 -0800 Subject: [PATCH 02/10] fix: use local edge functions count for deploy output The deploy API response doesn't include an edge_functions_count field. Thread the edgeFunctionsCount from deploySite's local hashing step through to the deploy output so edge function log hints display correctly. Co-Authored-By: Claude Opus 4.6 --- src/commands/deploy/deploy.ts | 2 +- src/utils/deploy/deploy-site.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index d33b569e61e..c6250819699 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -669,7 +669,7 @@ const runDeploy = async ({ .filter((fn): fn is { n: string; oid: string } => Boolean(fn.n && fn.oid)) .map((fn) => ({ name: fn.n, id: fn.oid })) - const hasEdgeFunctions = Boolean((results.deploy as any).edge_functions_count) + const hasEdgeFunctions = (results.edgeFunctionsCount ?? 0) > 0 return { siteId: results.deploy.site_id, diff --git a/src/utils/deploy/deploy-site.ts b/src/utils/deploy/deploy-site.ts index 438bf7387de..6b76c95e45f 100644 --- a/src/utils/deploy/deploy-site.ts +++ b/src/utils/deploy/deploy-site.ts @@ -221,6 +221,7 @@ For more information, visit https://ntl.fyi/cli-native-modules.`) deployId, deploy, uploadList, + edgeFunctionsCount, } return deployManifest } From cd019675df046173521487054f7691b5a57b6c4c Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 07:59:09 -0800 Subject: [PATCH 03/10] Potential fix for code scanning alert no. 72: Incomplete URL substring sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- tests/integration/commands/logs/edge-functions.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts index 7dae2d793e5..4c1c02f96e9 100644 --- a/tests/integration/commands/logs/edge-functions.test.ts +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -200,7 +200,8 @@ describe('logs:edge-functions command', () => { const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { - if (url.includes('analytics.services.netlify.com')) { + const parsedUrl = new URL(url) + if (parsedUrl.hostname === 'analytics.services.netlify.com') { return Promise.resolve({ ok: true, json: () => Promise.resolve(mockLogs), @@ -215,9 +216,10 @@ describe('logs:edge-functions command', () => { expect(spyWebsocket).not.toHaveBeenCalled() - const analyticsCall = spyFetch.mock.calls.find((args: string[]) => - args[0].includes('analytics.services.netlify.com'), - ) + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => { + const parsedUrl = new URL(args[0]) + return parsedUrl.hostname === 'analytics.services.netlify.com' + }) expect(analyticsCall).toBeDefined() expect(analyticsCall![0]).toContain('edge_function_logs') expect(analyticsCall![0]).toContain('site_id') From 5b7edd75c26577369fbbf813fc006d2ee7c55113 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 07:59:32 -0800 Subject: [PATCH 04/10] Potential fix for code scanning alert no. 73: Incomplete URL substring sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- tests/integration/commands/logs/edge-functions.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts index 4c1c02f96e9..65586fcd53f 100644 --- a/tests/integration/commands/logs/edge-functions.test.ts +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -200,7 +200,8 @@ describe('logs:edge-functions command', () => { const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { - const parsedUrl = new URL(url) + const hostname = new URL(url).hostname + if (hostname === 'analytics.services.netlify.com') { if (parsedUrl.hostname === 'analytics.services.netlify.com') { return Promise.resolve({ ok: true, @@ -215,9 +216,10 @@ describe('logs:edge-functions command', () => { await program.parseAsync(['', '', 'logs:edge-functions', '--from', '2026-01-01T00:00:00Z']) expect(spyWebsocket).not.toHaveBeenCalled() - const analyticsCall = spyFetch.mock.calls.find((args: string[]) => { - const parsedUrl = new URL(args[0]) + const hostname = new URL(args[0]).hostname + return hostname === 'analytics.services.netlify.com' + }) return parsedUrl.hostname === 'analytics.services.netlify.com' }) expect(analyticsCall).toBeDefined() From c698a59043370b1e74997719126726ffbddb8653 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 07:59:45 -0800 Subject: [PATCH 05/10] Potential fix for code scanning alert no. 75: Incomplete URL substring sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- tests/integration/commands/logs/functions.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index 6ba82651947..5dd6b94e0a4 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -279,7 +279,8 @@ describe('logs:function command', () => { const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { - if (url.includes('analytics.services.netlify.com')) { + const parsedUrl = new URL(url) + if (parsedUrl.hostname === 'analytics.services.netlify.com') { return Promise.resolve({ ok: true, json: () => Promise.resolve(mockLogs), @@ -294,9 +295,10 @@ describe('logs:function command', () => { expect(spyWebsocket).not.toHaveBeenCalled() - const analyticsCall = spyFetch.mock.calls.find((args: string[]) => - args[0].includes('analytics.services.netlify.com'), - ) + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => { + const parsedUrl = new URL(args[0]) + return parsedUrl.hostname === 'analytics.services.netlify.com' + }) expect(analyticsCall).toBeDefined() expect(analyticsCall![0]).toContain('function_logs') expect(analyticsCall![0]).toContain('cool-function') From 8be5bfa15d5ac22f92923e94437d71208ac61884 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 09:37:54 -0800 Subject: [PATCH 06/10] style: format with prettier Co-Authored-By: Claude Opus 4.6 --- src/commands/logs/edge-functions.ts | 5 +---- src/commands/logs/log-api.ts | 5 +---- tests/integration/commands/logs/edge-functions.test.ts | 4 +--- tests/integration/commands/logs/functions.test.ts | 8 ++------ 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/commands/logs/edge-functions.ts b/src/commands/logs/edge-functions.ts index dcc3e88104a..ef7c0d41058 100644 --- a/src/commands/logs/edge-functions.ts +++ b/src/commands/logs/edge-functions.ts @@ -9,10 +9,7 @@ import { parseDateToMs, fetchHistoricalLogs, printHistoricalLogs, formatLogEntry import { CLI_LOG_LEVEL_CHOICES_STRING, LOG_LEVELS_LIST } from './log-levels.js' import { getName } from './build.js' -export const logsEdgeFunction = async ( - options: OptionValues, - command: BaseCommand, -) => { +export const logsEdgeFunction = async (options: OptionValues, command: BaseCommand) => { let deployId: string | undefined = options.deployId await command.authenticate() diff --git a/src/commands/logs/log-api.ts b/src/commands/logs/log-api.ts index 78e1796ce9a..af40658cc6a 100644 --- a/src/commands/logs/log-api.ts +++ b/src/commands/logs/log-api.ts @@ -53,10 +53,7 @@ export function formatLogEntry(entry: { timestamp?: string; level?: string; mess return parts.join(' ') } -export function printHistoricalLogs( - data: unknown, - levelsToPrint: string[], -): void { +export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void { const entries = Array.isArray(data) ? data : [] if (entries.length === 0) { diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts index 65586fcd53f..5ecf68ff14a 100644 --- a/tests/integration/commands/logs/edge-functions.test.ts +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -194,9 +194,7 @@ describe('logs:edge-functions command', () => { const env = getEnvironmentVariables({ apiUrl }) Object.assign(process.env, env) - const mockLogs = [ - { timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Edge function executed' }, - ] + const mockLogs = [{ timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Edge function executed' }] const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index 5dd6b94e0a4..e15ca331e59 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -235,9 +235,7 @@ describe('logs:function command', () => { path: 'sites/site_id/deploys/deploy-123', response: { id: 'deploy-123', - available_functions: [ - { n: 'deploy-function', oid: 'deploy-fn-id' }, - ], + available_functions: [{ n: 'deploy-function', oid: 'deploy-fn-id' }], }, }, ] @@ -273,9 +271,7 @@ describe('logs:function command', () => { const env = getEnvironmentVariables({ apiUrl }) Object.assign(process.env, env) - const mockLogs = [ - { timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Function executed' }, - ] + const mockLogs = [{ timestamp: '2026-01-15T10:00:00Z', level: 'info', message: 'Function executed' }] const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { From d6ead88b4768719cb517d3cdf52a3e12cfb74af5 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 09:45:54 -0800 Subject: [PATCH 07/10] fix: resolve merge conflict in edge-functions test Co-Authored-By: Claude Opus 4.6 --- .../commands/logs/edge-functions.test.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts index 5ecf68ff14a..3cbcec155e8 100644 --- a/tests/integration/commands/logs/edge-functions.test.ts +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -198,9 +198,7 @@ describe('logs:edge-functions command', () => { const originalFetch = global.fetch const spyFetch = vi.fn().mockImplementation((url: string) => { - const hostname = new URL(url).hostname - if (hostname === 'analytics.services.netlify.com') { - if (parsedUrl.hostname === 'analytics.services.netlify.com') { + if (url.includes('analytics.services.netlify.com')) { return Promise.resolve({ ok: true, json: () => Promise.resolve(mockLogs), @@ -214,12 +212,9 @@ describe('logs:edge-functions command', () => { await program.parseAsync(['', '', 'logs:edge-functions', '--from', '2026-01-01T00:00:00Z']) expect(spyWebsocket).not.toHaveBeenCalled() - const analyticsCall = spyFetch.mock.calls.find((args: string[]) => { - const hostname = new URL(args[0]).hostname - return hostname === 'analytics.services.netlify.com' - }) - return parsedUrl.hostname === 'analytics.services.netlify.com' - }) + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => + args[0].includes('analytics.services.netlify.com'), + ) expect(analyticsCall).toBeDefined() expect(analyticsCall![0]).toContain('edge_function_logs') expect(analyticsCall![0]).toContain('site_id') From efc841a856f11f4decfe7f43bbd749be79ee8fd8 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 09:54:50 -0800 Subject: [PATCH 08/10] fix: resolve lint errors in logs commands and tests Add proper type assertions to eliminate unsafe-any and non-null assertion lint errors. Co-Authored-By: Claude Opus 4.6 --- src/commands/logs/edge-functions.ts | 25 ++++++++++--------- src/commands/logs/log-api.ts | 2 +- .../commands/logs/edge-functions.test.ts | 19 +++++++------- .../commands/logs/functions.test.ts | 6 ++--- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/commands/logs/edge-functions.ts b/src/commands/logs/edge-functions.ts index ef7c0d41058..8dbdc255dad 100644 --- a/src/commands/logs/edge-functions.ts +++ b/src/commands/logs/edge-functions.ts @@ -10,7 +10,7 @@ import { CLI_LOG_LEVEL_CHOICES_STRING, LOG_LEVELS_LIST } from './log-levels.js' import { getName } from './build.js' export const logsEdgeFunction = async (options: OptionValues, command: BaseCommand) => { - let deployId: string | undefined = options.deployId + let deployId = options.deployId as string | undefined await command.authenticate() const client = command.netlify.api @@ -22,15 +22,16 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma return } - if (options.level && !options.level.every((level: string) => LOG_LEVELS_LIST.includes(level))) { - log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`) + const levels = options.level as string[] | undefined + if (levels && !levels.every((level) => LOG_LEVELS_LIST.includes(level))) { + log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING.toString()}`) } - const levelsToPrint = options.level || LOG_LEVELS_LIST + const levelsToPrint: string[] = levels || LOG_LEVELS_LIST if (options.from) { - const fromMs = parseDateToMs(options.from) - const toMs = options.to ? parseDateToMs(options.to) : Date.now() + const fromMs = parseDateToMs(options.from as string) + const toMs = options.to ? parseDateToMs(options.to as string) : Date.now() const url = `https://analytics.services.netlify.com/v2/sites/${siteId}/edge_function_logs?from=${fromMs.toString()}&to=${toMs.toString()}` const data = await fetchHistoricalLogs({ url, accessToken: client.accessToken ?? '' }) @@ -38,7 +39,7 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma return } - const userId = command.netlify.globalConfig.get('userId') + const userId = command.netlify.globalConfig.get('userId') as string if (!deployId) { const deploys = await client.listSiteDeploys({ siteId }) @@ -51,15 +52,15 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma if (deploys.length === 1) { deployId = deploys[0].id } else { - const { result } = await inquirer.prompt({ + const { result } = (await inquirer.prompt({ name: 'result', type: 'list', message: `Select a deploy\n\n${chalk.yellow('*')} indicates a deploy created by you`, - choices: deploys.map((deploy: any) => ({ + choices: deploys.map((deploy) => ({ name: getName({ deploy, userId }), value: deploy.id, })), - }) + })) as { result: string } deployId = result } @@ -79,7 +80,7 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma }) ws.on('message', (data: string) => { - const logData = JSON.parse(data) + const logData = JSON.parse(data) as { level: string; message: string; timestamp?: string } if (!levelsToPrint.includes(logData.level.toLowerCase())) { return } @@ -90,7 +91,7 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma log('Connection closed') }) - ws.on('error', (err: any) => { + ws.on('error', (err: Error) => { log('Connection error') log(err) }) diff --git a/src/commands/logs/log-api.ts b/src/commands/logs/log-api.ts index af40658cc6a..bd8312d2579 100644 --- a/src/commands/logs/log-api.ts +++ b/src/commands/logs/log-api.ts @@ -54,7 +54,7 @@ export function formatLogEntry(entry: { timestamp?: string; level?: string; mess } export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void { - const entries = Array.isArray(data) ? data : [] + const entries = Array.isArray(data) ? (data as { timestamp?: string; level?: string; message?: string }[]) : [] if (entries.length === 0) { log('No logs found for the specified time range') diff --git a/tests/integration/commands/logs/edge-functions.test.ts b/tests/integration/commands/logs/edge-functions.test.ts index 3cbcec155e8..adebf139ac3 100644 --- a/tests/integration/commands/logs/edge-functions.test.ts +++ b/tests/integration/commands/logs/edge-functions.test.ts @@ -124,13 +124,12 @@ describe('logs:edge-functions command', () => { const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') expect(setupCall).toBeDefined() - const openCallback = setupCall?.[1] + const openCallback = setupCall?.[1] as (() => void) | undefined openCallback?.() expect(spySend).toHaveBeenCalledOnce() - const call = spySend.mock.calls[0] - const [message] = call - const body = JSON.parse(message) + const call = spySend.mock.calls[0] as string[] + const body = JSON.parse(call[0]) as Record expect(body.deploy_id).toEqual('deploy-id-1') expect(body.site_id).toEqual('site_id') @@ -154,11 +153,11 @@ describe('logs:edge-functions command', () => { await program.parseAsync(['', '', 'logs:edge-functions', '--deploy-id', 'my-deploy-id']) const setupCall = spyOn.mock.calls.find((args) => args[0] === 'open') - const openCallback = setupCall?.[1] + const openCallback = setupCall?.[1] as (() => void) | undefined openCallback?.() - const call = spySend.mock.calls[0] - const body = JSON.parse(call[0]) + const call = spySend.mock.calls[0] as string[] + const body = JSON.parse(call[0]) as Record expect(body.deploy_id).toEqual('my-deploy-id') }) @@ -179,7 +178,7 @@ describe('logs:edge-functions command', () => { await program.parseAsync(['', '', 'logs:edge-functions', '--level', 'info']) const messageCallback = spyOn.mock.calls.find((args) => args[0] === 'message') - const messageCallbackFunc = messageCallback?.[1] + const messageCallbackFunc = messageCallback?.[1] as ((data: string) => void) | undefined messageCallbackFunc?.(JSON.stringify({ level: LOG_LEVELS.INFO, message: 'Hello World' })) messageCallbackFunc?.(JSON.stringify({ level: LOG_LEVELS.WARN, message: 'There was a warning' })) @@ -216,8 +215,8 @@ describe('logs:edge-functions command', () => { args[0].includes('analytics.services.netlify.com'), ) expect(analyticsCall).toBeDefined() - expect(analyticsCall![0]).toContain('edge_function_logs') - expect(analyticsCall![0]).toContain('site_id') + expect((analyticsCall as string[])[0]).toContain('edge_function_logs') + expect((analyticsCall as string[])[0]).toContain('site_id') } finally { global.fetch = originalFetch } diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index e15ca331e59..d663ec38820 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -296,9 +296,9 @@ describe('logs:function command', () => { return parsedUrl.hostname === 'analytics.services.netlify.com' }) expect(analyticsCall).toBeDefined() - expect(analyticsCall![0]).toContain('function_logs') - expect(analyticsCall![0]).toContain('cool-function') - expect(analyticsCall![0]).toContain('site_id') + expect((analyticsCall as string[])[0]).toContain('function_logs') + expect((analyticsCall as string[])[0]).toContain('cool-function') + expect((analyticsCall as string[])[0]).toContain('site_id') } finally { global.fetch = originalFetch } From 009d87c4f3d9f66ea532f02ccd39b3c9fe2473ba Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 09:56:21 -0800 Subject: [PATCH 09/10] fix: resolve typecheck error in edge-functions error handler Co-Authored-By: Claude Opus 4.6 --- src/commands/logs/edge-functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/logs/edge-functions.ts b/src/commands/logs/edge-functions.ts index 8dbdc255dad..6737118af8d 100644 --- a/src/commands/logs/edge-functions.ts +++ b/src/commands/logs/edge-functions.ts @@ -93,6 +93,6 @@ export const logsEdgeFunction = async (options: OptionValues, command: BaseComma ws.on('error', (err: Error) => { log('Connection error') - log(err) + log(err.message) }) } From ab4e48e629d399d34c68f6335ec5c0a281add638 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Date: Fri, 13 Feb 2026 10:01:00 -0800 Subject: [PATCH 10/10] docs: regenerate command documentation Co-Authored-By: Claude Opus 4.6 --- docs/commands/agents.md | 14 +++++------ docs/commands/api.md | 4 ++-- docs/commands/blobs.md | 18 +++++++------- docs/commands/build.md | 4 ++-- docs/commands/clone.md | 4 ++-- docs/commands/completion.md | 2 +- docs/commands/db.md | 8 +++---- docs/commands/dev.md | 4 ++-- docs/commands/env.md | 24 +++++++++---------- docs/commands/functions.md | 18 +++++++------- docs/commands/init.md | 4 ++-- docs/commands/link.md | 4 ++-- docs/commands/login.md | 2 +- docs/commands/logs.md | 48 +++++++++++++++++++++++++++++++++---- docs/commands/open.md | 8 +++---- docs/commands/recipes.md | 4 ++-- docs/commands/sites.md | 14 +++++------ docs/commands/status.md | 6 ++--- docs/commands/unlink.md | 2 +- docs/commands/watch.md | 2 +- docs/index.md | 1 + 21 files changed, 118 insertions(+), 77 deletions(-) diff --git a/docs/commands/agents.md b/docs/commands/agents.md index 1f6cafdee62..215b3c64aee 100644 --- a/docs/commands/agents.md +++ b/docs/commands/agents.md @@ -21,9 +21,9 @@ netlify agents **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -91,12 +91,12 @@ netlify agents:list **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - output result as JSON - `project` (*string*) - project ID or name (if not in a linked directory) - `status` (*string*) - filter by status (new, running, done, error, cancelled) -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** @@ -123,11 +123,11 @@ netlify agents:show **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - output result as JSON - `project` (*string*) - project ID or name (if not in a linked directory) -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** @@ -153,11 +153,11 @@ netlify agents:stop **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - output result as JSON - `project` (*string*) - project ID or name (if not in a linked directory) -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** diff --git a/docs/commands/api.md b/docs/commands/api.md index a5561037f92..f5b74c676f8 100644 --- a/docs/commands/api.md +++ b/docs/commands/api.md @@ -24,10 +24,10 @@ netlify api **Flags** -- `data` (*string*) - Data to use -- `list` (*boolean*) - List out available API methods - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `data` (*string*) - Data to use +- `list` (*boolean*) - List out available API methods **Examples** diff --git a/docs/commands/blobs.md b/docs/commands/blobs.md index a0a6fb52b4f..a9c6b3d82cb 100644 --- a/docs/commands/blobs.md +++ b/docs/commands/blobs.md @@ -18,9 +18,9 @@ netlify blobs **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -59,10 +59,10 @@ netlify blobs:delete **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `force` (*boolean*) - Bypasses prompts & Force the command to run. - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Bypasses prompts & Force the command to run. --- ## `blobs:get` @@ -82,10 +82,10 @@ netlify blobs:get **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `output` (*string*) - Defines the filesystem path where the blob data should be persisted - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `output` (*string*) - Defines the filesystem path where the blob data should be persisted --- ## `blobs:list` @@ -104,12 +104,12 @@ netlify blobs:list **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `directories` (*boolean*) - Indicates that keys with the '/' character should be treated as directories, returning a list of sub-directories at a given level rather than all the keys inside them - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - Output list contents as JSON - `prefix` (*string*) - A string for filtering down the entries; when specified, only the entries whose key starts with that prefix are returned -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- ## `blobs:set` @@ -130,11 +130,11 @@ netlify blobs:set **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `force` (*boolean*) - Bypasses prompts & Force the command to run. - `input` (*string*) - Defines the filesystem path where the blob data should be read from -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- diff --git a/docs/commands/build.md b/docs/commands/build.md index 2b345292b36..1da787629f2 100644 --- a/docs/commands/build.md +++ b/docs/commands/build.md @@ -17,11 +17,11 @@ netlify build **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `context` (*string*) - Specify a deploy context for environment variables read during the build (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch (default: value of CONTEXT or ”production”) - `dry` (*boolean*) - Dry run: show instructions without running them - `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `offline` (*boolean*) - Disables any features that require network access **Examples** diff --git a/docs/commands/clone.md b/docs/commands/clone.md index d67d38b62b5..c82806c8769 100644 --- a/docs/commands/clone.md +++ b/docs/commands/clone.md @@ -28,11 +28,11 @@ netlify clone **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `id` (*string*) - ID of existing Netlify project to link to - `name` (*string*) - Name of existing Netlify project to link to -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** diff --git a/docs/commands/completion.md b/docs/commands/completion.md index 71e9ee5d967..c460b785bfb 100644 --- a/docs/commands/completion.md +++ b/docs/commands/completion.md @@ -46,9 +46,9 @@ netlify completion:install **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in --- diff --git a/docs/commands/db.md b/docs/commands/db.md index de8ca48e83c..dbf21b4d75f 100644 --- a/docs/commands/db.md +++ b/docs/commands/db.md @@ -19,9 +19,9 @@ netlify db **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -51,12 +51,12 @@ netlify init **Flags** - `assume-no` (*boolean*) - Non-interactive setup. Does not initialize any third-party tools/boilerplate. Ideal for CI environments or AI tools. -- `boilerplate` (*drizzle*) - Type of boilerplate to add to your project. -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `overwrite` (*boolean*) - Overwrites existing files that would be created when setting up boilerplate - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `boilerplate` (*drizzle*) - Type of boilerplate to add to your project. +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `no-boilerplate` (*boolean*) - Don't add any boilerplate to your project. +- `overwrite` (*boolean*) - Overwrites existing files that would be created when setting up boilerplate **Examples** diff --git a/docs/commands/dev.md b/docs/commands/dev.md index cba068156ce..a873f732a89 100644 --- a/docs/commands/dev.md +++ b/docs/commands/dev.md @@ -78,10 +78,10 @@ netlify dev:exec **Flags** -- `context` (*string*) - Specify a deploy context for environment variables (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch (default: dev) -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `context` (*string*) - Specify a deploy context for environment variables (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch (default: dev) +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in **Examples** diff --git a/docs/commands/env.md b/docs/commands/env.md index f21e118241a..3aa7b84789b 100644 --- a/docs/commands/env.md +++ b/docs/commands/env.md @@ -18,9 +18,9 @@ netlify env **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -56,12 +56,12 @@ netlify env:clone **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `force` (*boolean*) - Bypasses prompts & Force the command to run. - `from` (*string*) - Project ID (From) - `to` (*string*) - Project ID (To) -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** @@ -87,11 +87,11 @@ netlify env:get **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `context` (*string*) - Specify a deploy context for environment variables (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - Output environment variables as JSON -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `scope` (*builds | functions | post-processing | runtime | any*) - Specify a scope **Examples** @@ -120,11 +120,11 @@ netlify env:import **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - Output environment variables as JSON - `replace-existing` (*boolean*) - Replace all existing variables instead of merging them with the current ones -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- ## `env:list` @@ -139,13 +139,13 @@ netlify env:list **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `context` (*string*) - Specify a deploy context for environment variables (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch (default: all contexts) - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `json` (*boolean*) - Output environment variables as JSON -- `scope` (*builds | functions | post-processing | runtime | any*) - Specify a scope -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `plain` (*boolean*) - Output environment variables as plaintext +- `scope` (*builds | functions | post-processing | runtime | any*) - Specify a scope **Examples** @@ -214,12 +214,12 @@ netlify env:unset **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `context` (*string*) - Specify a deploy context for environment variables (”production”, ”deploy-preview”, ”branch-deploy”, ”dev”) or `branch:your-branch` where `your-branch` is the name of a branch (default: all contexts) - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `force` (*boolean*) - Bypasses prompts & Force the command to run. - `json` (*boolean*) - Output environment variables as JSON -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** diff --git a/docs/commands/functions.md b/docs/commands/functions.md index 11f5da7b1ba..fc30702f885 100644 --- a/docs/commands/functions.md +++ b/docs/commands/functions.md @@ -19,9 +19,9 @@ netlify functions **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -52,11 +52,11 @@ netlify functions:build **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `functions` (*string*) - Specify a functions directory to build to - `src` (*string*) - Specify the source directory for the functions -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- ## `functions:create` @@ -75,13 +75,13 @@ netlify functions:create **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `language` (*string*) - function language - `name` (*string*) - function name - `offline` (*boolean*) - Disables any features that require network access - `url` (*string*) - pull template from URL -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** @@ -149,11 +149,11 @@ netlify functions:list **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `functions` (*string*) - Specify a functions directory to list - `json` (*boolean*) - Output function data as JSON -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- ## `functions:serve` @@ -168,12 +168,12 @@ netlify functions:serve **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `functions` (*string*) - Specify a functions directory to serve - `offline` (*boolean*) - Disables any features that require network access - `port` (*string*) - Specify a port for the functions server -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in --- diff --git a/docs/commands/init.md b/docs/commands/init.md index 9aa21e7e4fe..7521d8b24c0 100644 --- a/docs/commands/init.md +++ b/docs/commands/init.md @@ -18,12 +18,12 @@ netlify init **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `force` (*boolean*) - Reinitialize CI hooks if the linked project is already configured to use CI - `git-remote-name` (*string*) - Name of Git remote to use. e.g. "origin" - `manual` (*boolean*) - Manually configure a git remote for CI -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in diff --git a/docs/commands/link.md b/docs/commands/link.md index 5d1b233e7b6..f7ad7656510 100644 --- a/docs/commands/link.md +++ b/docs/commands/link.md @@ -18,13 +18,13 @@ netlify link **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `git-remote-name` (*string*) - Name of Git remote to use. e.g. "origin" - `git-remote-url` (*string*) - URL of the repository (or Github `owner/repo`) to link to - `id` (*string*) - ID of project to link to - `name` (*string*) - Name of project to link to -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in **Examples** diff --git a/docs/commands/login.md b/docs/commands/login.md index 2fdd7b4865b..4059fd1cfc1 100644 --- a/docs/commands/login.md +++ b/docs/commands/login.md @@ -19,9 +19,9 @@ netlify login **Flags** -- `new` (*boolean*) - Login to new Netlify account - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `new` (*boolean*) - Login to new Netlify account diff --git a/docs/commands/logs.md b/docs/commands/logs.md index 8c761e85b25..3b3426c2afb 100644 --- a/docs/commands/logs.md +++ b/docs/commands/logs.md @@ -17,13 +17,14 @@ netlify logs **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| | [`logs:deploy`](/commands/logs#logsdeploy) | Stream the logs of deploys currently being built to the console | +| [`logs:edge-functions`](/commands/logs#logsedge-functions) | Stream netlify edge function logs to the console | | [`logs:function`](/commands/logs#logsfunction) | Stream netlify function logs to the console | @@ -33,6 +34,8 @@ netlify logs netlify logs:deploy netlify logs:function netlify logs:function my-function +netlify logs:edge-functions +netlify logs:edge-functions --deploy-id ``` --- @@ -48,9 +51,40 @@ netlify logs:deploy **Flags** +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in + +--- +## `logs:edge-functions` + +Stream netlify edge function logs to the console + +**Usage** + +```bash +netlify logs:edge-functions +``` + +**Flags** + - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `deploy-id` (*string*) - Deploy ID to stream edge function logs for +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `from` (*string*) - Start date for historical logs (ISO 8601 format) +- `level` (*string*) - Log levels to stream. Choices are: trace, debug, info, warn, error, fatal +- `to` (*string*) - End date for historical logs (ISO 8601 format, defaults to now) + +**Examples** + +```bash +netlify logs:edge-functions +netlify logs:edge-functions --deploy-id +netlify logs:edge-functions --from 2026-01-01T00:00:00Z +netlify logs:edge-functions --from 2026-01-01T00:00:00Z --to 2026-01-02T00:00:00Z +netlify logs:edge-functions -l info warn +``` --- ## `logs:function` @@ -65,21 +99,27 @@ netlify logs:function **Arguments** -- functionName - Name of the function to stream logs for +- functionName - Name or ID of the function to stream logs for **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `level` (*string*) - Log levels to stream. Choices are: trace, debug, info, warn, error, fatal - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `deploy-id` (*string*) - Deploy ID to look up the function from +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `from` (*string*) - Start date for historical logs (ISO 8601 format) +- `level` (*string*) - Log levels to stream. Choices are: trace, debug, info, warn, error, fatal +- `to` (*string*) - End date for historical logs (ISO 8601 format, defaults to now) **Examples** ```bash netlify logs:function netlify logs:function my-function +netlify logs:function my-function --deploy-id netlify logs:function my-function -l info warn +netlify logs:function my-function --from 2026-01-01T00:00:00Z +netlify logs:function my-function --from 2026-01-01T00:00:00Z --to 2026-01-02T00:00:00Z ``` --- diff --git a/docs/commands/open.md b/docs/commands/open.md index fc15acdd7a9..1accfa6af95 100644 --- a/docs/commands/open.md +++ b/docs/commands/open.md @@ -18,10 +18,10 @@ netlify open **Flags** - `admin` (*boolean*) - Open Netlify project -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `site` (*boolean*) - Open project - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `site` (*boolean*) - Open project | Subcommand | description | |:--------------------------- |:-----| @@ -51,9 +51,9 @@ netlify open:admin **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in **Examples** @@ -74,9 +74,9 @@ netlify open:site **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in **Examples** diff --git a/docs/commands/recipes.md b/docs/commands/recipes.md index dcd1ed9ced1..29f5ddbd8a7 100644 --- a/docs/commands/recipes.md +++ b/docs/commands/recipes.md @@ -21,9 +21,9 @@ netlify recipes **Flags** -- `name` (*string*) - recipe name to use - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `name` (*string*) - recipe name to use | Subcommand | description | |:--------------------------- |:-----| @@ -50,9 +50,9 @@ netlify recipes:list **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in **Examples** diff --git a/docs/commands/sites.md b/docs/commands/sites.md index d11c84a135c..62e2e4e53b3 100644 --- a/docs/commands/sites.md +++ b/docs/commands/sites.md @@ -19,9 +19,9 @@ netlify sites **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in | Subcommand | description | |:--------------------------- |:-----| @@ -80,11 +80,11 @@ netlify sites:create-template **Flags** - `account-slug` (*string*) - account slug to create the project under +- `debug` (*boolean*) - Print debugging information +- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `name` (*string*) - name of project - `url` (*string*) - template url -- `debug` (*boolean*) - Print debugging information -- `auth` (*string*) - Netlify auth token - can be used to run this command without logging in - `with-ci` (*boolean*) - initialize CI hooks during project creation **Examples** @@ -113,10 +113,10 @@ netlify sites:delete **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `force` (*boolean*) - Delete without prompting (useful for CI). - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `force` (*boolean*) - Delete without prompting (useful for CI). **Examples** @@ -137,10 +137,10 @@ netlify sites:list **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in -- `json` (*boolean*) - Output project data as JSON - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in +- `json` (*boolean*) - Output project data as JSON --- diff --git a/docs/commands/status.md b/docs/commands/status.md index 9529891046a..8a76f13fe28 100644 --- a/docs/commands/status.md +++ b/docs/commands/status.md @@ -18,10 +18,10 @@ netlify status **Flags** -- `json` (*boolean*) - Output status information as JSON -- `verbose` (*boolean*) - Output system info - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `json` (*boolean*) - Output status information as JSON +- `verbose` (*boolean*) - Output system info | Subcommand | description | |:--------------------------- |:-----| @@ -41,9 +41,9 @@ netlify status:hooks **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in --- diff --git a/docs/commands/unlink.md b/docs/commands/unlink.md index 030a0506d15..c44e1f780e9 100644 --- a/docs/commands/unlink.md +++ b/docs/commands/unlink.md @@ -18,9 +18,9 @@ netlify unlink **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in diff --git a/docs/commands/watch.md b/docs/commands/watch.md index ac7062847d5..95a4c45727d 100644 --- a/docs/commands/watch.md +++ b/docs/commands/watch.md @@ -18,9 +18,9 @@ netlify watch **Flags** -- `filter` (*string*) - For monorepos, specify the name of the application to run the command in - `debug` (*boolean*) - Print debugging information - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in +- `filter` (*string*) - For monorepos, specify the name of the application to run the command in **Examples** diff --git a/docs/index.md b/docs/index.md index ee86844d08a..9d981a2c306 100644 --- a/docs/index.md +++ b/docs/index.md @@ -132,6 +132,7 @@ Stream logs from your project | Subcommand | description | |:--------------------------- |:-----| | [`logs:deploy`](/commands/logs#logsdeploy) | Stream the logs of deploys currently being built to the console | +| [`logs:edge-functions`](/commands/logs#logsedge-functions) | Stream netlify edge function logs to the console | | [`logs:function`](/commands/logs#logsfunction) | Stream netlify function logs to the console |