Skip to content

Commit 23c7f92

Browse files
authored
Merge pull request #32841 from storybookjs/version-patch-from-9.1.15
Release: Patch 9.1.16
2 parents d0d17d9 + 230f1b8 commit 23c7f92

File tree

21 files changed

+576
-78
lines changed

21 files changed

+576
-78
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 9.1.16
2+
3+
- CLI: Fix Nextjs project creation in empty directories - [#32828](https://github.com/storybookjs/storybook/pull/32828), thanks @yannbf!
4+
- Core: Add `experimental_devServer` preset - [#32862](https://github.com/storybookjs/storybook/pull/32862), thanks @yannbf!
5+
- Telemetry: Fix preview-first-load event - [#32859](https://github.com/storybookjs/storybook/pull/32859), thanks @shilman!
6+
17
## 9.1.15
28

39
- Core: Add `preview-first-load` telemetry - [#32770](https://github.com/storybookjs/storybook/pull/32770), thanks @shilman!

code/core/src/core-server/dev-server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ export async function storybookDevServer(options: Options) {
5050

5151
getMiddleware(options.configDir)(app);
5252

53+
// Apply experimental_devServer preset to allow addons/frameworks to extend the dev server with middlewares, etc.
54+
await options.presets.apply('experimental_devServer', app);
55+
5356
const { port, host, initialPath } = options;
5457
invariant(port, 'expected options to have a port');
5558
const proto = options.https ? 'https' : 'http';
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2+
3+
import { makePayload } from './preview-initialized-channel';
4+
5+
describe('makePayload', () => {
6+
beforeEach(() => {
7+
vi.useFakeTimers();
8+
vi.setSystemTime(new Date('2024-01-01T00:00:00Z'));
9+
});
10+
afterEach(() => {
11+
vi.useRealTimers();
12+
});
13+
14+
it('new user init session', () => {
15+
const userAgent = 'Mozilla/5.0';
16+
const sessionId = 'session-123';
17+
const lastInit = {
18+
timestamp: Date.now() - 3000,
19+
body: {
20+
sessionId,
21+
payload: { newUser: true },
22+
},
23+
};
24+
25+
expect(makePayload(userAgent, lastInit as any, sessionId)).toMatchInlineSnapshot(`
26+
{
27+
"isNewUser": true,
28+
"timeSinceInit": 3000,
29+
"userAgent": "Mozilla/5.0",
30+
}
31+
`);
32+
});
33+
34+
it('existing user init session', () => {
35+
const userAgent = 'Mozilla/5.0';
36+
const sessionId = 'session-123';
37+
const lastInit = {
38+
timestamp: Date.now() - 3000,
39+
body: {
40+
sessionId,
41+
payload: {},
42+
},
43+
};
44+
45+
expect(makePayload(userAgent, lastInit as any, sessionId)).toMatchInlineSnapshot(`
46+
{
47+
"isNewUser": false,
48+
"timeSinceInit": 3000,
49+
"userAgent": "Mozilla/5.0",
50+
}
51+
`);
52+
});
53+
54+
it('no init session', () => {
55+
const userAgent = 'Mozilla/5.0';
56+
const sessionId = 'session-123';
57+
const lastInit = undefined;
58+
59+
expect(makePayload(userAgent, lastInit, sessionId)).toMatchInlineSnapshot(`
60+
{
61+
"isNewUser": false,
62+
"timeSinceInit": undefined,
63+
"userAgent": "Mozilla/5.0",
64+
}
65+
`);
66+
});
67+
68+
it('init session with different sessionId', () => {
69+
const userAgent = 'Mozilla/5.0';
70+
const sessionId = 'session-123';
71+
const lastInit = {
72+
timestamp: Date.now() - 3000,
73+
body: {
74+
sessionId: 'session-456',
75+
},
76+
};
77+
78+
expect(makePayload(userAgent, lastInit as any, sessionId)).toMatchInlineSnapshot(`
79+
{
80+
"isNewUser": false,
81+
"timeSinceInit": undefined,
82+
"userAgent": "Mozilla/5.0",
83+
}
84+
`);
85+
});
86+
});

code/core/src/core-server/server-channel/preview-initialized-channel.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
import type { Channel } from 'storybook/internal/channels';
22
import { PREVIEW_INITIALIZED } from 'storybook/internal/core-events';
3-
import { telemetry } from 'storybook/internal/telemetry';
3+
import { type InitPayload, telemetry } from 'storybook/internal/telemetry';
44
import type { CoreConfig, Options } from 'storybook/internal/types';
55

6-
import { getLastEvents } from '../../telemetry/event-cache';
6+
import { type CacheEntry, getLastEvents } from '../../telemetry/event-cache';
77
import { getSessionId } from '../../telemetry/session-id';
88

9+
export const makePayload = (
10+
userAgent: string,
11+
lastInit: CacheEntry | undefined,
12+
sessionId: string
13+
) => {
14+
let timeSinceInit: number | undefined;
15+
const payload = {
16+
userAgent,
17+
isNewUser: false,
18+
timeSinceInit,
19+
};
20+
21+
if (sessionId && lastInit?.body?.sessionId === sessionId) {
22+
payload.timeSinceInit = Date.now() - lastInit.timestamp;
23+
payload.isNewUser = !!(lastInit.body.payload as InitPayload).newUser;
24+
}
25+
return payload;
26+
};
27+
928
export function initPreviewInitializedChannel(
1029
channel: Channel,
1130
options: Options,
@@ -19,9 +38,8 @@ export function initPreviewInitializedChannel(
1938
const lastInit = lastEvents.init;
2039
const lastPreviewFirstLoad = lastEvents['preview-first-load'];
2140
if (!lastPreviewFirstLoad) {
22-
const isInitSession = lastInit?.body.sessionId === sessionId;
23-
const timeSinceInit = lastInit ? Date.now() - lastInit.body.timestamp : undefined;
24-
telemetry('preview-first-load', { timeSinceInit, isInitSession, userAgent });
41+
const payload = makePayload(userAgent, lastInit, sessionId);
42+
telemetry('preview-first-load', payload);
2543
}
2644
} catch (e) {
2745
// do nothing

code/core/src/telemetry/event-cache.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cache } from 'storybook/internal/common';
22

3-
import type { EventType } from './types';
3+
import type { EventType, TelemetryEvent } from './types';
44

55
interface UpgradeSummary {
66
timestamp: number;
@@ -9,9 +9,14 @@ interface UpgradeSummary {
99
sessionId?: string;
1010
}
1111

12+
export interface CacheEntry {
13+
timestamp: number;
14+
body: TelemetryEvent;
15+
}
16+
1217
let operation: Promise<any> = Promise.resolve();
1318

14-
const setHelper = async (eventType: EventType, body: any) => {
19+
const setHelper = async (eventType: EventType, body: TelemetryEvent) => {
1520
const lastEvents = (await cache.get('lastEvents')) || {};
1621
lastEvents[eventType] = { body, timestamp: Date.now() };
1722
await cache.set('lastEvents', lastEvents);
@@ -23,16 +28,16 @@ export const set = async (eventType: EventType, body: any) => {
2328
return operation;
2429
};
2530

26-
export const get = async (eventType: EventType) => {
31+
export const get = async (eventType: EventType): Promise<CacheEntry | undefined> => {
2732
const lastEvents = await getLastEvents();
2833
return lastEvents[eventType];
2934
};
3035

31-
export const getLastEvents = async () => {
36+
export const getLastEvents = async (): Promise<Record<EventType, CacheEntry>> => {
3237
return (await cache.get('lastEvents')) || {};
3338
};
3439

35-
const upgradeFields = (event: any): UpgradeSummary => {
40+
const upgradeFields = (event: CacheEntry): UpgradeSummary => {
3641
const { body, timestamp } = event;
3742
return {
3843
timestamp,

code/core/src/telemetry/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ export const telemetry = async (
4343
telemetryData.metadata = await getStorybookMetadata(options?.configDir);
4444
}
4545
} catch (error: any) {
46-
telemetryData.payload.metadataErrorMessage = sanitizeError(error).message;
46+
payload.metadataErrorMessage = sanitizeError(error).message;
4747

4848
if (options?.enableCrashReports) {
49-
telemetryData.payload.metadataError = sanitizeError(error);
49+
payload.metadataError = sanitizeError(error);
5050
}
5151
} finally {
52-
const { error } = telemetryData.payload;
52+
const { error } = payload;
5353
// make sure to anonymise possible paths from error messages
5454

5555
// make sure to anonymise possible paths from error messages
5656
if (error) {
57-
telemetryData.payload.error = sanitizeError(error);
57+
payload.error = sanitizeError(error);
5858
}
5959

60-
if (!telemetryData.payload.error || options?.enableCrashReports) {
60+
if (!payload.error || options?.enableCrashReports) {
6161
if (process.env?.STORYBOOK_TELEMETRY_DEBUG) {
6262
logger.info('\n[telemetry]');
6363
logger.info(JSON.stringify(telemetryData, null, 2));

code/core/src/telemetry/types.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export type EventType =
3333
| 'onboarding-survey'
3434
| 'mocking'
3535
| 'preview-first-load';
36-
3736
export interface Dependency {
3837
version: string | undefined;
3938
versionSpecifier?: string;
@@ -89,6 +88,10 @@ export interface Payload {
8988
[key: string]: any;
9089
}
9190

91+
export interface Context {
92+
[key: string]: any;
93+
}
94+
9295
export interface Options {
9396
retryDelay: number;
9497
immediate: boolean;
@@ -103,3 +106,17 @@ export interface TelemetryData {
103106
payload: Payload;
104107
metadata?: StorybookMetadata;
105108
}
109+
110+
export interface TelemetryEvent extends TelemetryData {
111+
eventId: string;
112+
sessionId: string;
113+
context: Context;
114+
}
115+
116+
export interface InitPayload {
117+
projectType: string;
118+
features: { dev: boolean; docs: boolean; test: boolean; onboarding: boolean };
119+
newUser: boolean;
120+
versionSpecifier: string | undefined;
121+
cliIntegration: string | undefined;
122+
}

code/core/src/types/modules/core-common.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ export type Middleware<T extends IncomingMessage = IncomingMessage> = (
223223
next: (err?: string | Error) => Promise<void> | void
224224
) => Promise<void> | void;
225225

226-
interface ServerApp<T extends IncomingMessage = IncomingMessage> {
226+
export interface ServerApp<T extends IncomingMessage = IncomingMessage> {
227227
server: NetServer;
228228

229229
use(pattern: RegExp | string, ...handlers: Middleware<T>[]): this;
@@ -476,6 +476,8 @@ export interface StorybookConfigRaw {
476476

477477
experimental_indexers?: Indexer[];
478478

479+
experimental_devServer?: ServerApp;
480+
479481
docs?: DocsOptions;
480482

481483
previewHead?: string;

code/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,5 +281,6 @@
281281
"Dependency Upgrades"
282282
]
283283
]
284-
}
284+
},
285+
"deferredNextVersion": "9.1.16"
285286
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1-
```tsx filename=".storybook/vitest.setup.ts" renderer="react" language="ts"
1+
```ts filename=".storybook/vitest.setup.ts" renderer="react" language="ts"
22
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
33
import { setProjectAnnotations } from '@storybook/your-framework';
44
import * as previewAnnotations from './preview';
55

66
const annotations = setProjectAnnotations([previewAnnotations]);
77
```
88

9-
```tsx filename=".storybook/vitest.setup.ts" renderer="svelte" language="ts"
9+
```ts filename=".storybook/vitest.setup.ts" renderer="svelte" language="ts"
1010
// Replace your-framework with the framework you are using, e.g. sveltekit or svelte-vite
1111
import { setProjectAnnotations } from '@storybook/your-framework';
1212
import * as previewAnnotations from './preview';
1313

1414
const annotations = setProjectAnnotations([previewAnnotations]);
1515
```
1616

17-
```tsx filename=".storybook/vitest.setup.ts" renderer="vue" language="ts"
17+
```ts filename=".storybook/vitest.setup.ts" renderer="vue" language="ts"
1818
import { setProjectAnnotations } from '@storybook/vue3-vite';
1919
import * as previewAnnotations from './preview';
2020

2121
const annotations = setProjectAnnotations([previewAnnotations]);
2222
```
23+
24+
```ts filename=".storybook/vitest.setup.ts" renderer="web-components" language="ts"
25+
import { setProjectAnnotations } from '@storybook/web-components-vite';
26+
import * as previewAnnotations from './preview';
27+
28+
const annotations = setProjectAnnotations([previewAnnotations]);
29+
```

0 commit comments

Comments
 (0)