netlify-cli 27.12.0 → 27.13.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.
Files changed (41) hide show
  1. package/dist/commands/agents/agents-stop.js +2 -2
  2. package/dist/commands/base-command.js +7 -3
  3. package/dist/commands/deploy/deploy.js +51 -94
  4. package/dist/commands/sites/sites-create.js +3 -2
  5. package/dist/lib/spinner.d.ts +2 -2
  6. package/dist/lib/spinner.js +1 -1
  7. package/dist/utils/deploy/deploy-site.d.ts +32 -12
  8. package/dist/utils/deploy/deploy-site.js +18 -40
  9. package/dist/utils/deploy/drop-api.d.ts +7 -1
  10. package/dist/utils/deploy/drop-api.js +3 -4
  11. package/dist/utils/deploy/file.d.ts +4 -2
  12. package/dist/utils/deploy/hash-config.d.ts +5 -6
  13. package/dist/utils/deploy/hash-config.js +1 -4
  14. package/dist/utils/deploy/hash-files.d.ts +9 -8
  15. package/dist/utils/deploy/hash-fns.d.ts +45 -10
  16. package/dist/utils/deploy/hasher-segments.d.ts +10 -6
  17. package/dist/utils/deploy/hasher-segments.js +0 -1
  18. package/dist/utils/deploy/process-files.d.ts +1 -1
  19. package/dist/utils/deploy/process-files.js +1 -0
  20. package/dist/utils/deploy/types.d.ts +17 -0
  21. package/dist/utils/deploy/types.js +2 -0
  22. package/dist/utils/deploy/upload-files.d.ts +8 -3
  23. package/dist/utils/deploy/upload-files.js +4 -3
  24. package/dist/utils/deploy/upload-source-zip.js +4 -7
  25. package/dist/utils/deploy/util.d.ts +6 -3
  26. package/dist/utils/deploy/util.js +4 -16
  27. package/dist/utils/team.d.ts +1 -0
  28. package/dist/utils/team.js +10 -5
  29. package/dist/utils/telemetry/report-error.js +2 -3
  30. package/dist/utils/telemetry/telemetry.js +9 -5
  31. package/dist/utils/telemetry/utils.d.ts +1 -0
  32. package/dist/utils/telemetry/utils.js +4 -0
  33. package/functions-templates/javascript/scheduled-function/package.json +1 -1
  34. package/functions-templates/typescript/database/package-lock.json +8 -8
  35. package/functions-templates/typescript/database/package.json +1 -1
  36. package/functions-templates/typescript/database-drizzle/package-lock.json +8 -8
  37. package/functions-templates/typescript/database-drizzle/package.json +1 -1
  38. package/functions-templates/typescript/hello-world/package-lock.json +15 -17
  39. package/functions-templates/typescript/hello-world/package.json +1 -1
  40. package/functions-templates/typescript/scheduled-function/package.json +1 -1
  41. package/package.json +4 -4
@@ -1,8 +1,10 @@
1
1
  import walker from 'folder-walker';
2
2
  export type OriginalFile = walker.Entry;
3
- export type File = walker.Entry & {
3
+ export type HashedFile = OriginalFile & {
4
4
  hash: string;
5
- assetType: string;
5
+ };
6
+ export type File = HashedFile & {
7
+ assetType: 'file';
6
8
  normalizedPath: string;
7
9
  };
8
10
  //# sourceMappingURL=file.d.ts.map
@@ -1,10 +1,9 @@
1
+ import type { DeployConfig } from './types.js';
2
+ import type { InlineUploadFile } from './upload-files.js';
1
3
  export declare const hashConfig: ({ config }: {
2
- config: any;
3
- }) => {
4
- assetType: string;
5
- body: string;
4
+ config: DeployConfig | undefined;
5
+ }) => InlineUploadFile & {
6
6
  hash: string;
7
- normalizedPath: string;
8
7
  };
9
- export declare const serializeToml: (object: any) => string;
8
+ export declare const serializeToml: (object: DeployConfig) => string;
10
9
  //# sourceMappingURL=hash-config.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import tomlify from 'tomlify-j0.4';
3
- // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message
4
3
  export const hashConfig = ({ config }) => {
5
4
  if (!config)
6
5
  throw new Error('Missing config option');
@@ -13,14 +12,12 @@ export const hashConfig = ({ config }) => {
13
12
  normalizedPath: 'netlify.toml',
14
13
  };
15
14
  };
16
- // @ts-expect-error TS(7006) FIXME: Parameter 'object' implicitly has an 'any' type.
17
15
  export const serializeToml = function (object) {
18
16
  return tomlify.toToml(object, { space: 2, replace: replaceTomlValue });
19
17
  };
20
18
  // `tomlify-j0.4` serializes integers as floats, e.g. `200.0`.
21
19
  // This is a problem with `redirects[*].status`.
22
- // @ts-expect-error TS(7006) FIXME: Parameter 'key' implicitly has an 'any' type.
23
- const replaceTomlValue = function (key, value) {
20
+ const replaceTomlValue = function (_key, value) {
24
21
  return Number.isInteger(value) ? String(value) : false;
25
22
  };
26
23
  //# sourceMappingURL=hash-config.js.map
@@ -1,15 +1,16 @@
1
- import { $TSFixMe } from '../../commands/types.js';
1
+ import type { File } from './file.js';
2
+ import type { StatusCallback } from './status-cb.js';
2
3
  declare const hashFiles: ({ assetType, concurrentHash, directories, filter, hashAlgorithm, normalizer, statusCb, }: {
3
- assetType?: string | undefined;
4
- concurrentHash: $TSFixMe;
5
- directories: $TSFixMe;
6
- filter: $TSFixMe;
4
+ assetType?: 'file' | undefined;
5
+ concurrentHash: number;
6
+ directories: string[];
7
+ filter: ((filename: string) => boolean) | undefined;
7
8
  hashAlgorithm?: string | undefined;
8
- normalizer?: $TSFixMe;
9
- statusCb: $TSFixMe;
9
+ normalizer?: (file: File) => File;
10
+ statusCb: StatusCallback;
10
11
  }) => Promise<{
11
12
  files: Record<string, string>;
12
- filesShaMap: Record<string, $TSFixMe[]>;
13
+ filesShaMap: Record<string, File[]>;
13
14
  }>;
14
15
  export default hashFiles;
15
16
  //# sourceMappingURL=hash-files.d.ts.map
@@ -1,9 +1,44 @@
1
- import BaseCommand from '../../commands/base-command.js';
2
- import { $TSFixMe } from '../../commands/types.js';
3
- import type { ServerUploadFile } from './upload-files.js';
1
+ import { type Config as FunctionsConfig, type FunctionResult, type TrafficRules } from '@netlify/zip-it-and-ship-it';
2
+ import type BaseCommand from '../../commands/base-command.js';
3
+ import type { StatusCallback } from './status-cb.js';
4
+ import type { FunctionUploadFile, ServerUploadFile } from './upload-files.js';
5
+ type HashedFunctionFile = FunctionUploadFile & {
6
+ hash: string;
7
+ relname: string;
8
+ };
9
+ declare const trafficRulesConfig: (trafficRules?: TrafficRules) => {
10
+ action: {
11
+ type: string;
12
+ config: {
13
+ rate_limit_config: {
14
+ algorithm: string;
15
+ window_size: number;
16
+ window_limit: number;
17
+ };
18
+ aggregate: {
19
+ keys: {
20
+ type: string;
21
+ }[];
22
+ };
23
+ to: string | undefined;
24
+ };
25
+ };
26
+ } | undefined;
27
+ interface FunctionConfigPayload {
28
+ display_name: FunctionResult['displayName'];
29
+ excluded_routes: FunctionResult['excludedRoutes'];
30
+ generator: FunctionResult['generator'];
31
+ memory: FunctionResult['memory'];
32
+ region: FunctionResult['region'];
33
+ routes: FunctionResult['routes'];
34
+ build_data: FunctionResult['buildData'];
35
+ priority: FunctionResult['priority'];
36
+ traffic_rules: ReturnType<typeof trafficRulesConfig>;
37
+ vcpu: FunctionResult['vcpu'];
38
+ }
4
39
  declare const hashFns: (command: BaseCommand, directories: string[], { concurrentHash, functionsConfig, hashAlgorithm, manifestPath, packagePath, rootDir, serverEnabled, serverManifestPath, skipFunctionsCache, statusCb, tmpDir, }: {
5
40
  concurrentHash?: number;
6
- functionsConfig?: $TSFixMe;
41
+ functionsConfig?: FunctionsConfig | undefined;
7
42
  hashAlgorithm?: string | undefined;
8
43
  manifestPath?: string | undefined;
9
44
  packagePath?: string | undefined;
@@ -11,18 +46,18 @@ declare const hashFns: (command: BaseCommand, directories: string[], { concurren
11
46
  serverEnabled?: boolean | undefined;
12
47
  serverManifestPath?: string | undefined;
13
48
  skipFunctionsCache?: boolean | undefined;
14
- statusCb: $TSFixMe;
15
- tmpDir: $TSFixMe;
49
+ statusCb: StatusCallback;
50
+ tmpDir?: string | undefined;
16
51
  }) => Promise<{
17
52
  functionSchedules?: {
18
53
  name: string;
19
54
  cron: string;
20
55
  }[] | undefined;
21
56
  functions: Record<string, string>;
22
- functionsWithNativeModules: $TSFixMe[];
23
- shaMap?: Record<string, $TSFixMe> | undefined;
24
- fnShaMap?: Record<string, $TSFixMe[]> | undefined;
25
- fnConfig?: Record<string, $TSFixMe> | undefined;
57
+ functionsWithNativeModules: FunctionResult[];
58
+ shaMap?: Record<string, never> | undefined;
59
+ fnShaMap?: Record<string, HashedFunctionFile[]> | undefined;
60
+ fnConfig?: Record<string, FunctionConfigPayload> | undefined;
26
61
  server?: {
27
62
  sha: string;
28
63
  region?: string;
@@ -1,15 +1,19 @@
1
1
  import { Transform, Writable } from 'node:stream';
2
- import type { File, OriginalFile } from './file.js';
2
+ import type { File } from './file.js';
3
3
  import type { StatusCallback } from './status-cb.js';
4
4
  export declare const hasherCtor: ({ concurrentHash, hashAlgorithm }: {
5
- concurrentHash: any;
6
- hashAlgorithm: any;
5
+ concurrentHash?: number;
6
+ hashAlgorithm: string;
7
7
  }) => Transform;
8
8
  export declare const fileNormalizerCtor: ({ assetType, normalizer: normalizeFunction, }: {
9
- assetType: string;
10
- normalizer?: (file: OriginalFile) => File;
9
+ assetType: 'file';
10
+ normalizer?: (file: File) => File;
11
11
  }) => Transform;
12
- export declare const manifestCollectorCtor: (filesObj: Record<string, unknown>, shaMap: Record<string, unknown[]>, { statusCb }: {
12
+ export declare const manifestCollectorCtor: <T extends {
13
+ hash: string;
14
+ normalizedPath: string;
15
+ relname: string;
16
+ }>(filesObj: Record<string, string>, shaMap: Record<string, T[]>, { statusCb }: {
13
17
  statusCb: StatusCallback;
14
18
  }) => Writable;
15
19
  export declare const fileFilterCtor: () => Transform;
@@ -11,7 +11,6 @@ const hashFile = async (filePath, algorithm) => {
11
11
  };
12
12
  // a parallel transform stream segment ctor that hashes fileObj's created by folder-walker
13
13
  // TODO: use promises instead of callbacks
14
- // @ts-expect-error TS(7031) FIXME: Binding element 'concurrentHash' implicitly has an... Remove this comment to see the full error message
15
14
  export const hasherCtor = ({ concurrentHash, hashAlgorithm }) => {
16
15
  if (!concurrentHash)
17
16
  throw new Error('Missing required opts');
@@ -7,7 +7,7 @@ export declare const deployFileNormalizer: (workingDir: string, file: File) => {
7
7
  stat: import("fs").Stats;
8
8
  type: "file" | "directory";
9
9
  hash: string;
10
- assetType: string;
10
+ assetType: 'file';
11
11
  normalizedPath: string;
12
12
  };
13
13
  export declare const getDeployConfigPathIfExists: (workingDir: string) => Promise<string | undefined>;
@@ -39,6 +39,7 @@ const getDirectoryIfExists = async (directoryPath) => {
39
39
  catch {
40
40
  // no-op
41
41
  }
42
+ return undefined;
42
43
  };
43
44
  export const isEdgeFunctionFile = (filePath) => filePath.startsWith(`${PUBLIC_URL_PATH}/`);
44
45
  //# sourceMappingURL=process-files.js.map
@@ -0,0 +1,17 @@
1
+ import type { NetlifyConfig } from '@netlify/build';
2
+ import type { MinimalHeader } from '@netlify/headers-parser';
3
+ /**
4
+ * The parts of the resolved configuration a deploy reads and uploads. Satisfied both by the CLI's cached config and
5
+ * by the `NetlifyConfig` that `@netlify/build` hands to the deploy handler.
6
+ */
7
+ export interface DeployConfig {
8
+ build: {
9
+ base: string;
10
+ publish?: string;
11
+ };
12
+ functions?: NetlifyConfig['functions'];
13
+ functionsDirectory?: string;
14
+ headers?: NetlifyConfig['headers'] | MinimalHeader[];
15
+ redirects?: unknown[];
16
+ }
17
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -1,15 +1,20 @@
1
- import fs from 'fs';
2
1
  import type { NetlifyAPI } from '@netlify/api';
3
2
  import type { StatusCallback } from './status-cb.js';
4
3
  export type UploadApi = Pick<NetlifyAPI, 'uploadDeployFile' | 'uploadDeployFunction' | 'uploadDeployEdgeFunction' | 'uploadDeployServer'>;
5
4
  interface UploadFileBase {
6
5
  filepath: string;
7
6
  normalizedPath: string;
8
- body?: fs.ReadStream;
7
+ body?: undefined;
9
8
  }
10
9
  export interface StaticUploadFile extends UploadFileBase {
11
10
  assetType: 'file';
12
11
  }
12
+ export interface InlineUploadFile {
13
+ assetType: 'file';
14
+ body: string;
15
+ filepath?: undefined;
16
+ normalizedPath: string;
17
+ }
13
18
  export interface FunctionUploadFile extends UploadFileBase {
14
19
  assetType: 'function';
15
20
  runtime?: string;
@@ -24,7 +29,7 @@ export interface ServerUploadFile extends UploadFileBase {
24
29
  assetType: 'server';
25
30
  hash: string;
26
31
  }
27
- export type UploadFile = StaticUploadFile | FunctionUploadFile | EdgeFunctionUploadFile | ServerUploadFile;
32
+ export type UploadFile = StaticUploadFile | InlineUploadFile | FunctionUploadFile | EdgeFunctionUploadFile | ServerUploadFile;
28
33
  interface UploadFilesOptions {
29
34
  concurrentUpload: number;
30
35
  maxRetry: number;
@@ -23,7 +23,8 @@ const uploadFiles = async (api, deployId, uploadList, { concurrentUpload, maxRet
23
23
  });
24
24
  const uploadFile = async (fileObj, index) => {
25
25
  const { body, filepath, normalizedPath } = fileObj;
26
- const readStreamCtor = () => body ?? fs.createReadStream(filepath);
26
+ // FIXME(@netlify/api): octet-stream `body` is typed as `ReadStream` only, but an inline string body works too
27
+ const readStreamCtor = () => (body ?? fs.createReadStream(filepath));
27
28
  statusCb({
28
29
  type: 'upload',
29
30
  msg: `(${index}/${uploadList.length}) Uploading ${normalizedPath}...`,
@@ -131,12 +132,12 @@ const retryUpload = (uploadFn, maxRetry) => new Promise((resolve, reject) => {
131
132
  // Do something when backoff starts, e.g. show to the
132
133
  // user the delay before next reconnection attempt.
133
134
  });
134
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
135
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises -- FIXME: the listener's promise is discarded
135
136
  fibonacciBackoff.on('ready', tryUpload);
136
137
  fibonacciBackoff.on('fail', () => {
137
138
  reject(lastError);
138
139
  });
139
- tryUpload();
140
+ void tryUpload();
140
141
  });
141
142
  export default uploadFiles;
142
143
  //# sourceMappingURL=upload-files.js.map
@@ -27,6 +27,7 @@ const DEFAULT_IGNORE_PATTERNS = [
27
27
  '.tmp*',
28
28
  '.temp*',
29
29
  ];
30
+ const isExecaError = (error) => error instanceof Error && 'command' in error;
30
31
  const createSourceZip = async ({ sourceDir, filename, statusCb, }) => {
31
32
  // Check for Windows - this feature is not supported on Windows
32
33
  if (platform() === 'win32') {
@@ -54,13 +55,9 @@ const createSourceZip = async ({ sourceDir, filename, statusCb, }) => {
54
55
  stdio: ['ignore', 'pipe', 'pipe'],
55
56
  });
56
57
  }
57
- catch (_baseErr) {
58
- let message = 'zip command failed';
59
- if (_baseErr instanceof Error && 'command' in _baseErr) {
60
- const baseErr = _baseErr;
61
- message = `${baseErr.shortMessage}\n\n${baseErr.all ?? ''}`;
62
- }
63
- throw new Error(message, { cause: _baseErr });
58
+ catch (error) {
59
+ const message = isExecaError(error) ? `${error.shortMessage}\n\n${error.all ?? ''}` : 'zip command failed';
60
+ throw new Error(message, { cause: error });
64
61
  }
65
62
  return zipPath;
66
63
  };
@@ -1,6 +1,9 @@
1
+ import type { NetlifyAPI } from '@netlify/api';
1
2
  export declare const pluralize: (amount: number, noun: string) => string;
3
+ export type Deploy = Awaited<ReturnType<NetlifyAPI['getSiteDeploy']>>;
4
+ export type ReadyDeploy = Deploy & Required<Pick<Deploy, 'id' | 'site_id' | 'name' | 'admin_url' | 'url' | 'ssl_url' | 'deploy_url' | 'deploy_ssl_url'>>;
2
5
  export declare const normalizePath: (relname: string) => string;
3
- export declare const waitForDiff: (api: any, deployId: any, siteId: any, timeout: any) => Promise<undefined>;
4
- export declare const waitForDeploy: (api: any, deployId: any, siteId: any, timeout: any) => Promise<undefined>;
5
- export declare const getUploadList: (required: any, shaMap: any) => any;
6
+ export declare const waitForDiff: (api: Pick<NetlifyAPI, 'getSiteDeploy'>, deployId: string, siteId: string, timeout: number) => Promise<Deploy>;
7
+ export declare const waitForDeploy: (api: Pick<NetlifyAPI, 'getSiteDeploy'>, deployId: string, siteId: string, timeout: number) => Promise<ReadyDeploy>;
8
+ export declare const getUploadList: <T>(required: string[] | undefined, shaMap: Record<string, T[]> | undefined) => T[];
6
9
  //# sourceMappingURL=util.d.ts.map
@@ -10,17 +10,13 @@ export const normalizePath = (relname) => {
10
10
  return relname.split(sep).join('/');
11
11
  };
12
12
  // poll an async deployId until its done diffing
13
- // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type.
14
13
  export const waitForDiff = async (api, deployId, siteId, timeout) => {
15
- // capture ready deploy during poll
16
- let deploy;
17
14
  const loadDeploy = async () => {
18
15
  const siteDeploy = await api.getSiteDeploy({ siteId, deployId });
19
16
  switch (siteDeploy.state) {
20
17
  // https://github.com/netlify/bitballoon/blob/master/app/models/deploy.rb#L21-L33
21
18
  case 'error': {
22
19
  const deployError = new Error(siteDeploy.error_message || `Deploy ${deployId} had an error`);
23
- // @ts-expect-error TS(2339) FIXME: Property 'deploy' does not exist on type 'Error'.
24
20
  deployError.deploy = siteDeploy;
25
21
  throw deployError;
26
22
  }
@@ -28,8 +24,7 @@ export const waitForDiff = async (api, deployId, siteId, timeout) => {
28
24
  case 'uploading':
29
25
  case 'uploaded':
30
26
  case 'ready': {
31
- deploy = siteDeploy;
32
- return true;
27
+ return pWaitFor.resolveWith(siteDeploy);
33
28
  }
34
29
  case 'preparing':
35
30
  default: {
@@ -37,7 +32,7 @@ export const waitForDiff = async (api, deployId, siteId, timeout) => {
37
32
  }
38
33
  }
39
34
  };
40
- await pWaitFor(loadDeploy, {
35
+ const deploy = await pWaitFor(loadDeploy, {
41
36
  interval: DEPLOY_POLL,
42
37
  timeout: {
43
38
  milliseconds: timeout,
@@ -47,23 +42,18 @@ export const waitForDiff = async (api, deployId, siteId, timeout) => {
47
42
  return deploy;
48
43
  };
49
44
  // Poll a deployId until its ready
50
- // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type.
51
45
  export const waitForDeploy = async (api, deployId, siteId, timeout) => {
52
- // capture ready deploy during poll
53
- let deploy;
54
46
  const loadDeploy = async () => {
55
47
  const siteDeploy = await api.getSiteDeploy({ siteId, deployId });
56
48
  switch (siteDeploy.state) {
57
49
  // https://github.com/netlify/bitballoon/blob/master/app/models/deploy.rb#L21-L33
58
50
  case 'error': {
59
51
  const deployError = new Error(siteDeploy.error_message || `Deploy ${deployId} had an error`);
60
- // @ts-expect-error TS(2339) FIXME: Property 'deploy' does not exist on type 'Error'.
61
52
  deployError.deploy = siteDeploy;
62
53
  throw deployError;
63
54
  }
64
55
  case 'ready': {
65
- deploy = siteDeploy;
66
- return true;
56
+ return pWaitFor.resolveWith(siteDeploy);
67
57
  }
68
58
  case 'preparing':
69
59
  case 'prepared':
@@ -74,7 +64,7 @@ export const waitForDeploy = async (api, deployId, siteId, timeout) => {
74
64
  }
75
65
  }
76
66
  };
77
- await pWaitFor(loadDeploy, {
67
+ const deploy = await pWaitFor(loadDeploy, {
78
68
  interval: DEPLOY_POLL,
79
69
  timeout: {
80
70
  milliseconds: timeout,
@@ -84,11 +74,9 @@ export const waitForDeploy = async (api, deployId, siteId, timeout) => {
84
74
  return deploy;
85
75
  };
86
76
  // Transform the fileShaMap and fnShaMap into a generic shaMap that file-uploader.js can use
87
- // @ts-expect-error TS(7006) FIXME: Parameter 'required' implicitly has an 'any' type.
88
77
  export const getUploadList = (required, shaMap) => {
89
78
  if (!required || !shaMap)
90
79
  return [];
91
- // @ts-expect-error TS(7006) FIXME: Parameter 'sha' implicitly has an 'any' type.
92
80
  return required.flatMap((sha) => shaMap[sha]);
93
81
  };
94
82
  //# sourceMappingURL=util.js.map
@@ -1,4 +1,5 @@
1
1
  import type { MinimalAccount } from './types.js';
2
+ export declare const requireTeams: (accounts: MinimalAccount[] | undefined) => MinimalAccount[];
2
3
  export declare const resolveTeam: (accounts: MinimalAccount[]) => MinimalAccount | undefined;
3
4
  export declare const resolveTeamForNonInteractive: (accounts: MinimalAccount[], commandExample: string) => MinimalAccount;
4
5
  //# sourceMappingURL=team.d.ts.map
@@ -1,4 +1,11 @@
1
1
  import { logAndThrowError } from './command-helpers.js';
2
+ export const requireTeams = (accounts) => {
3
+ const teams = accounts ?? [];
4
+ if (teams.length === 0) {
5
+ return logAndThrowError('No teams available. Please ensure you have access to at least one team.');
6
+ }
7
+ return teams;
8
+ };
2
9
  export const resolveTeam = (accounts) => {
3
10
  if (accounts.length === 1) {
4
11
  return accounts[0];
@@ -6,14 +13,12 @@ export const resolveTeam = (accounts) => {
6
13
  return accounts.find((acc) => acc.default);
7
14
  };
8
15
  export const resolveTeamForNonInteractive = (accounts, commandExample) => {
9
- if (accounts.length === 0) {
10
- return logAndThrowError('No teams available. Please ensure you have access to at least one team.');
11
- }
12
- const team = resolveTeam(accounts);
16
+ const teams = requireTeams(accounts);
17
+ const team = resolveTeam(teams);
13
18
  if (team) {
14
19
  return team;
15
20
  }
16
- const availableTeams = accounts.map((t) => t.slug).join(', ');
21
+ const availableTeams = teams.map((t) => t.slug).join(', ');
17
22
  return logAndThrowError(`Multiple teams available. Please specify which team to use.\n` +
18
23
  `Available teams: ${availableTeams}\n\n` +
19
24
  `Example: ${commandExample}\n\n` +
@@ -3,9 +3,8 @@ import { dirname, join } from 'path';
3
3
  import process, { version as nodejsVersion } from 'process';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { getGlobalConfigStore } from '@netlify/dev-utils';
6
- import { isCI } from 'ci-info';
7
6
  import execa from '../execa.js';
8
- import { cliVersion } from './utils.js';
7
+ import { cliVersion, isTelemetryDisabledByEnv } from './utils.js';
9
8
  const dirPath = dirname(fileURLToPath(import.meta.url));
10
9
  let currentCommand;
11
10
  export const setCommandForErrorReporting = (command) => {
@@ -21,7 +20,7 @@ export const setCommandForErrorReporting = (command) => {
21
20
  */
22
21
  // @ts-expect-error TS(7006) FIXME: Parameter 'error' implicitly has an 'any' type.
23
22
  export const reportError = async function (error, config = {}) {
24
- if (isCI) {
23
+ if (isTelemetryDisabledByEnv()) {
25
24
  return;
26
25
  }
27
26
  // convert a NotifiableError to an error class
@@ -2,10 +2,10 @@ import { dirname, join } from 'path';
2
2
  import process, { version as nodejsVersion } from 'process';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { getGlobalConfigStore } from '@netlify/dev-utils';
5
- import { isCI } from 'ci-info';
5
+ import { id as ciVendor, isPR } from 'ci-info';
6
6
  import { getDrivingAgent } from '../agent-detection.js';
7
7
  import execa from '../execa.js';
8
- import { isTelemetryDisabled, cliVersion } from './utils.js';
8
+ import { isTelemetryDisabled, isTelemetryDisabledByEnv, cliVersion } from './utils.js';
9
9
  import isValidEventName from './validation.js';
10
10
  const dirPath = dirname(fileURLToPath(import.meta.url));
11
11
  function send(type, payload) {
@@ -49,11 +49,15 @@ const getAgentProperties = () => {
49
49
  agent_other_value: agent?.otherValue,
50
50
  };
51
51
  };
52
+ const getCIProperties = () => ({
53
+ ci_vendor: ciVendor ?? undefined,
54
+ ci_is_pr: isPR ?? undefined,
55
+ });
52
56
  /**
53
57
  * Tracks a custom event with the provided payload
54
58
  */
55
59
  export async function track(eventName, payload = {}) {
56
- if (isCI) {
60
+ if (isTelemetryDisabledByEnv()) {
57
61
  return;
58
62
  }
59
63
  const globalConfig = await getGlobalConfigStore();
@@ -78,12 +82,12 @@ export async function track(eventName, payload = {}) {
78
82
  anonymousId: cliId,
79
83
  duration,
80
84
  status,
81
- properties: { ...properties, nodejsVersion, cliVersion, ...getAgentProperties() },
85
+ properties: { ...properties, nodejsVersion, cliVersion, ...getAgentProperties(), ...getCIProperties() },
82
86
  };
83
87
  return send('track', defaultData);
84
88
  }
85
89
  export async function identify(payload) {
86
- if (isCI) {
90
+ if (isTelemetryDisabledByEnv()) {
87
91
  return;
88
92
  }
89
93
  const globalConfig = await getGlobalConfigStore();
@@ -1,3 +1,4 @@
1
1
  export declare const cliVersion: string;
2
2
  export declare const isTelemetryDisabled: (config: any) => any;
3
+ export declare const isTelemetryDisabledByEnv: () => boolean;
3
4
  //# sourceMappingURL=utils.d.ts.map
@@ -1,7 +1,11 @@
1
+ import process from 'process';
1
2
  import getCLIPackageJson from '../get-cli-package-json.js';
2
3
  export const { version: cliVersion } = await getCLIPackageJson();
3
4
  // @ts-expect-error TS(7006) FIXME: Parameter 'config' implicitly has an 'any' type.
4
5
  export const isTelemetryDisabled = function (config) {
5
6
  return config.get('telemetryDisabled');
6
7
  };
8
+ const isEnabledFlag = (value) => value !== undefined && !['', '0', 'false'].includes(value);
9
+ // An env var opt-out matters in CI, where the global config written by `--telemetry-disable` rarely persists.
10
+ export const isTelemetryDisabledByEnv = () => isEnabledFlag(process.env.NETLIFY_TELEMETRY_DISABLED) || isEnabledFlag(process.env.DO_NOT_TRACK);
7
11
  //# sourceMappingURL=utils.js.map
@@ -15,6 +15,6 @@
15
15
  "test": "echo \"Error: no test specified\" && exit 1"
16
16
  },
17
17
  "dependencies": {
18
- "@netlify/functions": "^6.0.0"
18
+ "@netlify/functions": "^6.0.2"
19
19
  }
20
20
  }
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@netlify/database": "latest",
13
- "@netlify/functions": "^6.0.0",
13
+ "@netlify/functions": "^6.0.2",
14
14
  "@types/node": "^22.0.0",
15
15
  "typescript": "^4.5.5"
16
16
  }
@@ -41,12 +41,12 @@
41
41
  }
42
42
  },
43
43
  "node_modules/@netlify/functions": {
44
- "version": "6.0.0",
45
- "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-6.0.0.tgz",
46
- "integrity": "sha512-Pee+FoFnAtdejTkl62a7mZxj6Wx4eqVNuYyDc5T4XPJN9GaAWaUf4cZQFlf2m4Fqq0HoghjtcPKo+BZviVPPKA==",
44
+ "version": "6.0.2",
45
+ "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-6.0.2.tgz",
46
+ "integrity": "sha512-JqbPgAOt2GAwzCEitdEWAHOn6AkZ0Rna401wdAyuDIBiDv9M03b3Cu5mGH7/eHOmIXfZIcN1pG35flHjfu6asQ==",
47
47
  "license": "MIT",
48
48
  "dependencies": {
49
- "@netlify/types": "3.0.0"
49
+ "@netlify/types": "3.2.0"
50
50
  },
51
51
  "engines": {
52
52
  "node": ">=22.12.0"
@@ -62,9 +62,9 @@
62
62
  }
63
63
  },
64
64
  "node_modules/@netlify/types": {
65
- "version": "3.0.0",
66
- "resolved": "https://registry.npmjs.org/@netlify/types/-/types-3.0.0.tgz",
67
- "integrity": "sha512-zRCidsQgI4CZWrZB/kmwdPf0QbSNljakfNSLizFvrsSA3UcUrTWSBNMVKmZnob92/8ESeUN0ewXLOlffJU+ATQ==",
65
+ "version": "3.2.0",
66
+ "resolved": "https://registry.npmjs.org/@netlify/types/-/types-3.2.0.tgz",
67
+ "integrity": "sha512-PVvY1EEN9i2Er0LQoiyNG7i/BnwnttLuZqIkBnhL2Jrcj3P5N/NcGkv1i20MJ2ngNGSPQcT7tagDmF3V6BsdWg==",
68
68
  "license": "MIT",
69
69
  "engines": {
70
70
  "node": ">=22.12.0"
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@netlify/database": "latest",
19
- "@netlify/functions": "^6.0.0",
19
+ "@netlify/functions": "^6.0.2",
20
20
  "@types/node": "^22.0.0",
21
21
  "typescript": "^4.5.5"
22
22
  }
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@netlify/database": "latest",
13
- "@netlify/functions": "^6.0.0",
13
+ "@netlify/functions": "^6.0.2",
14
14
  "@types/node": "^22.0.0",
15
15
  "drizzle-orm": "beta",
16
16
  "typescript": "^4.5.5"
@@ -42,12 +42,12 @@
42
42
  }
43
43
  },
44
44
  "node_modules/@netlify/functions": {
45
- "version": "6.0.0",
46
- "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-6.0.0.tgz",
47
- "integrity": "sha512-Pee+FoFnAtdejTkl62a7mZxj6Wx4eqVNuYyDc5T4XPJN9GaAWaUf4cZQFlf2m4Fqq0HoghjtcPKo+BZviVPPKA==",
45
+ "version": "6.0.2",
46
+ "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-6.0.2.tgz",
47
+ "integrity": "sha512-JqbPgAOt2GAwzCEitdEWAHOn6AkZ0Rna401wdAyuDIBiDv9M03b3Cu5mGH7/eHOmIXfZIcN1pG35flHjfu6asQ==",
48
48
  "license": "MIT",
49
49
  "dependencies": {
50
- "@netlify/types": "3.0.0"
50
+ "@netlify/types": "3.2.0"
51
51
  },
52
52
  "engines": {
53
53
  "node": ">=22.12.0"
@@ -63,9 +63,9 @@
63
63
  }
64
64
  },
65
65
  "node_modules/@netlify/types": {
66
- "version": "3.0.0",
67
- "resolved": "https://registry.npmjs.org/@netlify/types/-/types-3.0.0.tgz",
68
- "integrity": "sha512-zRCidsQgI4CZWrZB/kmwdPf0QbSNljakfNSLizFvrsSA3UcUrTWSBNMVKmZnob92/8ESeUN0ewXLOlffJU+ATQ==",
66
+ "version": "3.2.0",
67
+ "resolved": "https://registry.npmjs.org/@netlify/types/-/types-3.2.0.tgz",
68
+ "integrity": "sha512-PVvY1EEN9i2Er0LQoiyNG7i/BnwnttLuZqIkBnhL2Jrcj3P5N/NcGkv1i20MJ2ngNGSPQcT7tagDmF3V6BsdWg==",
69
69
  "license": "MIT",
70
70
  "engines": {
71
71
  "node": ">=22.12.0"
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@netlify/database": "latest",
20
- "@netlify/functions": "^6.0.0",
20
+ "@netlify/functions": "^6.0.2",
21
21
  "@types/node": "^22.0.0",
22
22
  "drizzle-orm": "beta",
23
23
  "typescript": "^4.5.5"