UNPKG

13.5 kBSource Map (JSON)View Raw
1{"version":3,"sources":["../../telemetry/storage.ts"],"names":["TELEMETRY_KEY_ENABLED","TELEMETRY_KEY_NOTIFY_DATE","TELEMETRY_KEY_ID","TELEMETRY_KEY_SALT","Telemetry","constructor","distDir","conf","sessionId","rawProjectId","NEXT_TELEMETRY_DISABLED","NEXT_TELEMETRY_DEBUG","queue","notify","isDisabled","get","set","Date","now","toString","console","log","chalk","magenta","bold","cyan","setEnabled","_enabled","enabled","oneWayHash","payload","hash","update","salt","digest","record","_events","_this","wrapper","submitRecord","prom","then","value","isFulfilled","isRejected","catch","reason","res","delete","add","flush","Promise","all","events","Array","isArray","length","resolve","forEach","eventName","error","JSON","stringify","context","anonymousId","projectId","meta","map","fields","process","env","storageDirectory","getStorageDirectory","Conf","projectName","cwd","_","Set","val","generated","isEnabled","isLikelyEphemeral","ciEnvironment","isCI","path","join","undefined"],"mappings":"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","sourcesContent":["import chalk from 'chalk'\nimport Conf from 'next/dist/compiled/conf'\nimport { BinaryLike, createHash, randomBytes } from 'crypto'\nimport isDockerFunction from 'next/dist/compiled/is-docker'\nimport path from 'path'\n\nimport { getAnonymousMeta } from './anonymous-meta'\nimport * as ciEnvironment from './ci-info'\nimport { _postPayload } from './post-payload'\nimport { getRawProjectId } from './project-id'\n\n// This is the key that stores whether or not telemetry is enabled or disabled.\nconst TELEMETRY_KEY_ENABLED = 'telemetry.enabled'\n\n// This is the key that specifies when the user was informed about anonymous\n// telemetry collection.\nconst TELEMETRY_KEY_NOTIFY_DATE = 'telemetry.notifiedAt'\n\n// This is a quasi-persistent identifier used to dedupe recurring events. It's\n// generated from random data and completely anonymous.\nconst TELEMETRY_KEY_ID = `telemetry.anonymousId`\n\n// This is the cryptographic salt that is included within every hashed value.\n// This salt value is never sent to us, ensuring privacy and the one-way nature\n// of the hash (prevents dictionary lookups of pre-computed hashes).\n// See the `oneWayHash` function.\nconst TELEMETRY_KEY_SALT = `telemetry.salt`\n\ntype TelemetryEvent = { eventName: string; payload: object }\ntype EventContext = {\n anonymousId: string\n projectId: string\n sessionId: string\n}\ntype EventMeta = { [key: string]: unknown }\ntype EventBatchShape = {\n eventName: string\n fields: object\n}\n\ntype RecordObject = {\n isFulfilled: boolean\n isRejected: boolean\n value?: any\n reason?: any\n}\n\nexport class Telemetry {\n private conf: Conf<any> | null\n private sessionId: string\n private rawProjectId: string\n private NEXT_TELEMETRY_DISABLED: any\n private NEXT_TELEMETRY_DEBUG: any\n\n private queue: Set<Promise<RecordObject>>\n\n constructor({ distDir }: { distDir: string }) {\n // Read in the constructor so that .env can be loaded before reading\n const { NEXT_TELEMETRY_DISABLED, NEXT_TELEMETRY_DEBUG } = process.env\n this.NEXT_TELEMETRY_DISABLED = NEXT_TELEMETRY_DISABLED\n this.NEXT_TELEMETRY_DEBUG = NEXT_TELEMETRY_DEBUG\n const storageDirectory = getStorageDirectory(distDir)\n\n try {\n // `conf` incorrectly throws a permission error during initialization\n // instead of waiting for first use. We need to handle it, otherwise the\n // process may crash.\n this.conf = new Conf({ projectName: 'nextjs', cwd: storageDirectory })\n } catch (_) {\n this.conf = null\n }\n this.sessionId = randomBytes(32).toString('hex')\n this.rawProjectId = getRawProjectId()\n\n this.queue = new Set()\n\n this.notify()\n }\n\n private notify = () => {\n if (this.isDisabled || !this.conf) {\n return\n }\n\n // The end-user has already been notified about our telemetry integration. We\n // don't need to constantly annoy them about it.\n // We will re-inform users about the telemetry if significant changes are\n // ever made.\n if (this.conf.get(TELEMETRY_KEY_NOTIFY_DATE, '')) {\n return\n }\n this.conf.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now().toString())\n\n console.log(\n `${chalk.magenta.bold(\n 'Attention'\n )}: Next.js now collects completely anonymous telemetry regarding usage.`\n )\n console.log(\n `This information is used to shape Next.js' roadmap and prioritize features.`\n )\n console.log(\n `You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:`\n )\n console.log(chalk.cyan('https://nextjs.org/telemetry'))\n console.log()\n }\n\n get anonymousId(): string {\n const val = this.conf && this.conf.get(TELEMETRY_KEY_ID)\n if (val) {\n return val\n }\n\n const generated = randomBytes(32).toString('hex')\n this.conf && this.conf.set(TELEMETRY_KEY_ID, generated)\n return generated\n }\n\n get salt(): string {\n const val = this.conf && this.conf.get(TELEMETRY_KEY_SALT)\n if (val) {\n return val\n }\n\n const generated = randomBytes(16).toString('hex')\n this.conf && this.conf.set(TELEMETRY_KEY_SALT, generated)\n return generated\n }\n\n private get isDisabled(): boolean {\n if (!!this.NEXT_TELEMETRY_DISABLED || !this.conf) {\n return true\n }\n return this.conf.get(TELEMETRY_KEY_ENABLED, true) === false\n }\n\n setEnabled = (_enabled: boolean) => {\n const enabled = !!_enabled\n this.conf && this.conf.set(TELEMETRY_KEY_ENABLED, enabled)\n }\n\n get isEnabled(): boolean {\n return !!this.conf && this.conf.get(TELEMETRY_KEY_ENABLED, true) !== false\n }\n\n oneWayHash = (payload: BinaryLike): string => {\n const hash = createHash('sha256')\n\n // Always prepend the payload value with salt. This ensures the hash is truly\n // one-way.\n hash.update(this.salt)\n\n // Update is an append operation, not a replacement. The salt from the prior\n // update is still present!\n hash.update(payload)\n return hash.digest('hex')\n }\n\n private get projectId(): string {\n return this.oneWayHash(this.rawProjectId)\n }\n\n record = (\n _events: TelemetryEvent | TelemetryEvent[]\n ): Promise<RecordObject> => {\n const _this = this\n // pseudo try-catch\n async function wrapper() {\n return await _this.submitRecord(_events)\n }\n\n const prom = wrapper()\n .then((value) => ({\n isFulfilled: true,\n isRejected: false,\n value,\n }))\n .catch((reason) => ({\n isFulfilled: false,\n isRejected: true,\n reason,\n }))\n // Acts as `Promise#finally` because `catch` transforms the error\n .then((res) => {\n // Clean up the event to prevent unbounded `Set` growth\n this.queue.delete(prom)\n return res\n })\n\n // Track this `Promise` so we can flush pending events\n this.queue.add(prom)\n\n return prom\n }\n\n flush = async () => Promise.all(this.queue).catch(() => null)\n\n private submitRecord = (\n _events: TelemetryEvent | TelemetryEvent[]\n ): Promise<any> => {\n let events: TelemetryEvent[]\n if (Array.isArray(_events)) {\n events = _events\n } else {\n events = [_events]\n }\n\n if (events.length < 1) {\n return Promise.resolve()\n }\n\n if (this.NEXT_TELEMETRY_DEBUG) {\n // Print to standard error to simplify selecting the output\n events.forEach(({ eventName, payload }) =>\n console.error(\n `[telemetry] ` + JSON.stringify({ eventName, payload }, null, 2)\n )\n )\n // Do not send the telemetry data if debugging. Users may use this feature\n // to preview what data would be sent.\n return Promise.resolve()\n }\n\n // Skip recording telemetry if the feature is disabled\n if (this.isDisabled) {\n return Promise.resolve()\n }\n\n const context: EventContext = {\n anonymousId: this.anonymousId,\n projectId: this.projectId,\n sessionId: this.sessionId,\n }\n const meta: EventMeta = getAnonymousMeta()\n return _postPayload(`https://telemetry.nextjs.org/api/v1/record`, {\n context,\n meta,\n events: events.map(({ eventName, payload }) => ({\n eventName,\n fields: payload,\n })) as Array<EventBatchShape>,\n })\n }\n}\n\nfunction getStorageDirectory(distDir: string): string | undefined {\n const isLikelyEphemeral = ciEnvironment.isCI || isDockerFunction()\n\n if (isLikelyEphemeral) {\n return path.join(distDir, 'cache')\n }\n\n return undefined\n}\n"]}
\No newline at end of file