diff --git a/CHANGELOG.md b/CHANGELOG.md index 67dd4be250e6..a2b083c7f424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 9.1.15 + +- Core: Add `preview-first-load` telemetry - [#32770](https://github.com/storybookjs/storybook/pull/32770), thanks @shilman! +- Dependencies: Update `vite-plugin-storybook-nextjs` - [#32821](https://github.com/storybookjs/storybook/pull/32821), thanks @ndelangen! + ## 9.1.14 - NextJS: Add NextJS 16 support - [#32791](https://github.com/storybookjs/storybook/pull/32791), thanks @yannbf and @ndelangen! diff --git a/code/core/src/core-events/index.ts b/code/core/src/core-events/index.ts index d0ff9cca7d54..d9d326ba5bfb 100644 --- a/code/core/src/core-events/index.ts +++ b/code/core/src/core-events/index.ts @@ -58,6 +58,8 @@ enum events { // A global was just updated GLOBALS_UPDATED = 'globalsUpdated', REGISTER_SUBSCRIPTION = 'registerSubscription', + // Preview initialized for first-load-event + PREVIEW_INITIALIZED = 'previewInitialized', // Tell the manager that the user pressed a key in the preview PREVIEW_KEYDOWN = 'previewKeydown', // Tell the preview that the builder is in progress @@ -111,6 +113,7 @@ export const { PLAY_FUNCTION_THREW_EXCEPTION, UNHANDLED_ERRORS_WHILE_PLAYING, PRELOAD_ENTRIES, + PREVIEW_INITIALIZED, PREVIEW_BUILDER_PROGRESS, PREVIEW_KEYDOWN, REGISTER_SUBSCRIPTION, diff --git a/code/core/src/core-server/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts index d3a402e3fc64..9b4d36f882a6 100644 --- a/code/core/src/core-server/presets/common-preset.ts +++ b/code/core/src/core-server/presets/common-preset.ts @@ -29,6 +29,7 @@ import { dedent } from 'ts-dedent'; import { initCreateNewStoryChannel } from '../server-channel/create-new-story-channel'; import { initFileSearchChannel } from '../server-channel/file-search-channel'; +import { initPreviewInitializedChannel } from '../server-channel/preview-initialized-channel'; import { defaultStaticDirs } from '../utils/constants'; import { initializeSaveStory } from '../utils/save-story/save-story'; import { parseStaticDir } from '../utils/server-statics'; @@ -257,6 +258,7 @@ export const experimental_serverChannel = async ( initFileSearchChannel(channel, options, coreOptions); initCreateNewStoryChannel(channel, options, coreOptions); + initPreviewInitializedChannel(channel, options, coreOptions); return channel; }; diff --git a/code/core/src/core-server/server-channel/preview-initialized-channel.ts b/code/core/src/core-server/server-channel/preview-initialized-channel.ts new file mode 100644 index 000000000000..1a0ad5e595da --- /dev/null +++ b/code/core/src/core-server/server-channel/preview-initialized-channel.ts @@ -0,0 +1,31 @@ +import type { Channel } from 'storybook/internal/channels'; +import { PREVIEW_INITIALIZED } from 'storybook/internal/core-events'; +import { telemetry } from 'storybook/internal/telemetry'; +import type { CoreConfig, Options } from 'storybook/internal/types'; + +import { getLastEvents } from '../../telemetry/event-cache'; +import { getSessionId } from '../../telemetry/session-id'; + +export function initPreviewInitializedChannel( + channel: Channel, + options: Options, + _coreConfig: CoreConfig +) { + channel.on(PREVIEW_INITIALIZED, async ({ userAgent }) => { + if (!options.disableTelemetry) { + try { + const sessionId = await getSessionId(); + const lastEvents = await getLastEvents(); + const lastInit = lastEvents.init; + const lastPreviewFirstLoad = lastEvents['preview-first-load']; + if (!lastPreviewFirstLoad) { + const isInitSession = lastInit?.body.sessionId === sessionId; + const timeSinceInit = lastInit ? Date.now() - lastInit.body.timestamp : undefined; + telemetry('preview-first-load', { timeSinceInit, isInitSession, userAgent }); + } + } catch (e) { + // do nothing + } + } + }); +} diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index 2f5d81f6cfc9..e54590b07c87 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -559,6 +559,7 @@ export default { 'PLAY_FUNCTION_THREW_EXCEPTION', 'PRELOAD_ENTRIES', 'PREVIEW_BUILDER_PROGRESS', + 'PREVIEW_INITIALIZED', 'PREVIEW_KEYDOWN', 'REGISTER_SUBSCRIPTION', 'REQUEST_WHATS_NEW_DATA', @@ -618,6 +619,7 @@ export default { 'PLAY_FUNCTION_THREW_EXCEPTION', 'PRELOAD_ENTRIES', 'PREVIEW_BUILDER_PROGRESS', + 'PREVIEW_INITIALIZED', 'PREVIEW_KEYDOWN', 'REGISTER_SUBSCRIPTION', 'REQUEST_WHATS_NEW_DATA', diff --git a/code/core/src/preview-api/modules/preview-web/Preview.tsx b/code/core/src/preview-api/modules/preview-web/Preview.tsx index f7ac363d1205..60b1892c17e8 100644 --- a/code/core/src/preview-api/modules/preview-web/Preview.tsx +++ b/code/core/src/preview-api/modules/preview-web/Preview.tsx @@ -9,6 +9,7 @@ import { FORCE_REMOUNT, FORCE_RE_RENDER, GLOBALS_UPDATED, + PREVIEW_INITIALIZED, RESET_STORY_ARGS, type RequestData, type ResponseData, @@ -128,6 +129,9 @@ export class Preview { const projectAnnotations = await this.getProjectAnnotationsOrRenderError(); await this.runBeforeAllHook(projectAnnotations); await this.initializeWithProjectAnnotations(projectAnnotations); + // eslint-disable-next-line compat/compat + const userAgent = globalThis?.navigator?.userAgent; + await this.channel.emit(PREVIEW_INITIALIZED, { userAgent }); } catch (err) { this.rejectStoreInitializationPromise(err as Error); } diff --git a/code/core/src/telemetry/event-cache.ts b/code/core/src/telemetry/event-cache.ts index c40d7254aefc..0f25b1c14579 100644 --- a/code/core/src/telemetry/event-cache.ts +++ b/code/core/src/telemetry/event-cache.ts @@ -24,8 +24,12 @@ export const set = async (eventType: EventType, body: any) => { }; export const get = async (eventType: EventType) => { - const lastEvents = await cache.get('lastEvents'); - return lastEvents?.[eventType]; + const lastEvents = await getLastEvents(); + return lastEvents[eventType]; +}; + +export const getLastEvents = async () => { + return (await cache.get('lastEvents')) || {}; }; const upgradeFields = (event: any): UpgradeSummary => { diff --git a/code/core/src/telemetry/telemetry.ts b/code/core/src/telemetry/telemetry.ts index 9796a315db13..53a57fcb09cf 100644 --- a/code/core/src/telemetry/telemetry.ts +++ b/code/core/src/telemetry/telemetry.ts @@ -97,17 +97,13 @@ export async function sendTelemetry( try { request = prepareRequest(data, context, options); tasks.push(request); - if (options.immediate) { - await Promise.all(tasks); - } else { - await request; - } const sessionId = await getSessionId(); const eventId = nanoid(); const body = { ...rest, eventType, eventId, sessionId, metadata, payload, context }; - await saveToCache(eventType, body); + const waitFor = options.immediate ? tasks : [request]; + await Promise.all([...waitFor, saveToCache(eventType, body)]); } catch (err) { // } finally { diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index d1827d07219e..89b23b58a111 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -31,7 +31,8 @@ export type EventType = | 'test-run' | 'addon-onboarding' | 'onboarding-survey' - | 'mocking'; + | 'mocking' + | 'preview-first-load'; export interface Dependency { version: string | undefined; diff --git a/code/lib/create-storybook/src/scaffold-new-project.ts b/code/lib/create-storybook/src/scaffold-new-project.ts index eb35622428bb..e9bc69cf3653 100644 --- a/code/lib/create-storybook/src/scaffold-new-project.ts +++ b/code/lib/create-storybook/src/scaffold-new-project.ts @@ -45,10 +45,10 @@ const SUPPORTED_PROJECTS: Record = { language: 'TS', }, createScript: { - npm: 'npm create next-app . -- --turbopack --typescript --use-npm --eslint --tailwind --no-app --import-alias="@/*" --src-dir', + npm: 'npm create next-app . -- --turbopack --typescript --use-npm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler', // yarn doesn't support version ranges, so we have to use npx - yarn: 'npx create-next-app . --turbopack --typescript --use-yarn --eslint --tailwind --no-app --import-alias="@/*" --src-dir', - pnpm: 'pnpm create next-app . --turbopack --typescript --use-pnpm --eslint --tailwind --no-app --import-alias="@/*" --src-dir', + yarn: 'npx create-next-app . --turbopack --typescript --use-yarn --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler', + pnpm: 'pnpm create next-app . --turbopack --typescript --use-pnpm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler', }, }, 'vue-vite-ts': { diff --git a/code/package.json b/code/package.json index df5b50e821b0..f21cdf576171 100644 --- a/code/package.json +++ b/code/package.json @@ -281,5 +281,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "9.1.15" } diff --git a/docs/versions/latest.json b/docs/versions/latest.json index 1b1318d07234..a97d3b11b5a2 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"9.1.14","info":{"plain":"- Addon-Vitest: Support Vitest 4 - [#32819](https://github.com/storybookjs/storybook/pull/32819), thanks @yannbf!\n- CSF: Fix `play-fn` tag for methods - [#32695](https://github.com/storybookjs/storybook/pull/32695), thanks @shilman!"}} +{"version":"9.1.15","info":{"plain":"- Core: Add `preview-first-load` telemetry - [#32770](https://github.com/storybookjs/storybook/pull/32770), thanks @shilman!\n- Dependencies: Update `vite-plugin-storybook-nextjs` - [#32821](https://github.com/storybookjs/storybook/pull/32821), thanks @ndelangen!"}}