@storybook/react-native 10.3.0-next.6 → 10.3.1
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.
- package/dist/index-6iAzVvXp.d.ts +35 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +57 -6
- package/dist/metro/withStorybook.d.ts +4 -13
- package/dist/metro/withStorybook.js +41 -15
- package/dist/node.d.ts +18 -1
- package/dist/node.js +13 -10
- package/dist/repack/withStorybook.d.ts +2 -13
- package/dist/repack/withStorybook.js +44 -16
- package/package.json +17 -17
- package/readme.md +13 -1
- package/scripts/generate.js +24 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for configuring WebSockets used for syncing storybook instances or sending events to storybook.
|
|
3
|
+
*/
|
|
4
|
+
interface WebsocketsOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The port WebSocket server will listen on. Defaults to 7007.
|
|
7
|
+
*/
|
|
8
|
+
port?: number;
|
|
9
|
+
/**
|
|
10
|
+
* The host WebSocket server will bind to. Defaults to 'localhost'.
|
|
11
|
+
*/
|
|
12
|
+
host?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to use WSS/HTTPS for the channel server.
|
|
15
|
+
*/
|
|
16
|
+
secured?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* TLS private key used when `secured` is true.
|
|
19
|
+
*/
|
|
20
|
+
key?: string | Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* TLS certificate used when `secured` is true.
|
|
23
|
+
*/
|
|
24
|
+
cert?: string | Buffer;
|
|
25
|
+
/**
|
|
26
|
+
* Optional certificate authority chain used when `secured` is true.
|
|
27
|
+
*/
|
|
28
|
+
ca?: string | Buffer | Array<string | Buffer>;
|
|
29
|
+
/**
|
|
30
|
+
* Optional TLS passphrase used when `secured` is true.
|
|
31
|
+
*/
|
|
32
|
+
passphrase?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type { WebsocketsOptions as W };
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ declare class View {
|
|
|
78
78
|
}>;
|
|
79
79
|
_getHost: (params?: Partial<Params>) => any;
|
|
80
80
|
__getPort: (params?: Partial<Params>) => any;
|
|
81
|
+
_isSecureConnection: (params?: Partial<Params>) => any;
|
|
81
82
|
_getServerChannel: (params?: Partial<Params>) => Channel;
|
|
82
83
|
createPreparedStoryMapping: () => Promise<void>;
|
|
83
84
|
getStorybookUI: (params?: Partial<Params>) => () => react_jsx_runtime.JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -471,6 +471,7 @@ var BUILDING_BLOCK_recomputeInvalidatedAtoms = (store2) => {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
if (hasChangedDeps) {
|
|
474
|
+
invalidatedAtoms.set(a, aState.n);
|
|
474
475
|
readAtomState(store2, a);
|
|
475
476
|
mountDependencies(store2, a);
|
|
476
477
|
}
|
|
@@ -494,9 +495,19 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
|
|
|
494
495
|
const mountDependencies = buildingBlocks[17];
|
|
495
496
|
const setAtomStateValueOrPromise = buildingBlocks[20];
|
|
496
497
|
const registerAbortHandler = buildingBlocks[26];
|
|
498
|
+
const storeEpochHolder = buildingBlocks[28];
|
|
497
499
|
const atomState = ensureAtomState(store2, atom2);
|
|
500
|
+
const storeEpochNumber = storeEpochHolder[0];
|
|
498
501
|
if (isAtomStateInitialized(atomState)) {
|
|
499
|
-
if (
|
|
502
|
+
if (
|
|
503
|
+
// If the atom is mounted, we can use cached atom state,
|
|
504
|
+
// because it should have been updated by dependencies.
|
|
505
|
+
// We can't use the cache if the atom is invalidated.
|
|
506
|
+
mountedMap.has(atom2) && invalidatedAtoms.get(atom2) !== atomState.n || // If atom is not mounted, we can use cached atom state,
|
|
507
|
+
// only if store hasn't been mutated.
|
|
508
|
+
atomState.m === storeEpochNumber
|
|
509
|
+
) {
|
|
510
|
+
atomState.m = storeEpochNumber;
|
|
500
511
|
return atomState;
|
|
501
512
|
}
|
|
502
513
|
let hasChangedDeps = false;
|
|
@@ -507,6 +518,7 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
|
|
|
507
518
|
}
|
|
508
519
|
}
|
|
509
520
|
if (!hasChangedDeps) {
|
|
521
|
+
atomState.m = storeEpochNumber;
|
|
510
522
|
return atomState;
|
|
511
523
|
}
|
|
512
524
|
}
|
|
@@ -597,6 +609,7 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
|
|
|
597
609
|
}
|
|
598
610
|
};
|
|
599
611
|
const prevEpochNumber = atomState.n;
|
|
612
|
+
const prevInvalidated = invalidatedAtoms.get(atom2) === prevEpochNumber;
|
|
600
613
|
try {
|
|
601
614
|
if ((import_meta.env ? import_meta.env.MODE : void 0) !== "production") {
|
|
602
615
|
storeMutationSet.delete(store2);
|
|
@@ -619,15 +632,17 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
|
|
|
619
632
|
pruneDependencies();
|
|
620
633
|
}
|
|
621
634
|
(_a = storeHooks.r) == null ? void 0 : _a.call(storeHooks, atom2);
|
|
635
|
+
atomState.m = storeEpochNumber;
|
|
622
636
|
return atomState;
|
|
623
637
|
} catch (error) {
|
|
624
638
|
delete atomState.v;
|
|
625
639
|
atomState.e = error;
|
|
626
640
|
++atomState.n;
|
|
641
|
+
atomState.m = storeEpochNumber;
|
|
627
642
|
return atomState;
|
|
628
643
|
} finally {
|
|
629
644
|
isSync = false;
|
|
630
|
-
if (
|
|
645
|
+
if (atomState.n !== prevEpochNumber && prevInvalidated) {
|
|
631
646
|
invalidatedAtoms.set(atom2, atomState.n);
|
|
632
647
|
changedAtoms.add(atom2);
|
|
633
648
|
(_b = storeHooks.c) == null ? void 0 : _b.call(storeHooks, atom2);
|
|
@@ -665,6 +680,7 @@ var BUILDING_BLOCK_writeAtomState = (store2, atom2, ...args) => {
|
|
|
665
680
|
const writeAtomState = buildingBlocks[16];
|
|
666
681
|
const mountDependencies = buildingBlocks[17];
|
|
667
682
|
const setAtomStateValueOrPromise = buildingBlocks[20];
|
|
683
|
+
const storeEpochHolder = buildingBlocks[28];
|
|
668
684
|
let isSync = true;
|
|
669
685
|
const getter = (a) => returnAtomValue(readAtomState(store2, a));
|
|
670
686
|
const setter = (a, ...args2) => {
|
|
@@ -683,6 +699,7 @@ var BUILDING_BLOCK_writeAtomState = (store2, atom2, ...args) => {
|
|
|
683
699
|
setAtomStateValueOrPromise(store2, a, v);
|
|
684
700
|
mountDependencies(store2, a);
|
|
685
701
|
if (prevEpochNumber !== aState.n) {
|
|
702
|
+
++storeEpochHolder[0];
|
|
686
703
|
changedAtoms.add(a);
|
|
687
704
|
invalidateDependents(store2, a);
|
|
688
705
|
(_a = storeHooks.c) == null ? void 0 : _a.call(storeHooks, a);
|
|
@@ -867,14 +884,18 @@ var BUILDING_BLOCK_storeGet = (store2, atom2) => {
|
|
|
867
884
|
};
|
|
868
885
|
var BUILDING_BLOCK_storeSet = (store2, atom2, ...args) => {
|
|
869
886
|
const buildingBlocks = getInternalBuildingBlocks(store2);
|
|
887
|
+
const changedAtoms = buildingBlocks[3];
|
|
870
888
|
const flushCallbacks = buildingBlocks[12];
|
|
871
889
|
const recomputeInvalidatedAtoms = buildingBlocks[13];
|
|
872
890
|
const writeAtomState = buildingBlocks[16];
|
|
891
|
+
const prevChangedAtomsSize = changedAtoms.size;
|
|
873
892
|
try {
|
|
874
893
|
return writeAtomState(store2, atom2, ...args);
|
|
875
894
|
} finally {
|
|
876
|
-
|
|
877
|
-
|
|
895
|
+
if (changedAtoms.size !== prevChangedAtomsSize) {
|
|
896
|
+
recomputeInvalidatedAtoms(store2);
|
|
897
|
+
flushCallbacks(store2);
|
|
898
|
+
}
|
|
878
899
|
}
|
|
879
900
|
};
|
|
880
901
|
var BUILDING_BLOCK_storeSub = (store2, atom2, listener) => {
|
|
@@ -983,7 +1004,9 @@ function buildStore(...buildArgs) {
|
|
|
983
1004
|
/* @__PURE__ */ new WeakMap(),
|
|
984
1005
|
// abortHandlersMap
|
|
985
1006
|
BUILDING_BLOCK_registerAbortHandler,
|
|
986
|
-
BUILDING_BLOCK_abortPromise
|
|
1007
|
+
BUILDING_BLOCK_abortPromise,
|
|
1008
|
+
// store epoch
|
|
1009
|
+
[0]
|
|
987
1010
|
].map((fn, i) => buildArgs[i] || fn);
|
|
988
1011
|
buildingBlockMap.set(store2, Object.freeze(buildingBlocks));
|
|
989
1012
|
return store2;
|
|
@@ -1155,6 +1178,28 @@ function useAtomValue(atom2, options) {
|
|
|
1155
1178
|
}
|
|
1156
1179
|
}
|
|
1157
1180
|
if (typeof delay === "number") {
|
|
1181
|
+
console.warn(`[DEPRECATED] delay option is deprecated and will be removed in v3.
|
|
1182
|
+
|
|
1183
|
+
Migration guide:
|
|
1184
|
+
|
|
1185
|
+
Create a custom hook like the following.
|
|
1186
|
+
|
|
1187
|
+
function useAtomValueWithDelay<Value>(
|
|
1188
|
+
atom: Atom<Value>,
|
|
1189
|
+
options: { delay: number },
|
|
1190
|
+
): Value {
|
|
1191
|
+
const { delay } = options
|
|
1192
|
+
const store = useStore(options)
|
|
1193
|
+
const [value, setValue] = useState(() => store.get(atom))
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
const unsub = store.sub(atom, () => {
|
|
1196
|
+
setTimeout(() => setValue(store.get(atom)), delay)
|
|
1197
|
+
})
|
|
1198
|
+
return unsub
|
|
1199
|
+
}, [store, atom, delay])
|
|
1200
|
+
return value
|
|
1201
|
+
}
|
|
1202
|
+
`);
|
|
1158
1203
|
setTimeout(rerender, delay);
|
|
1159
1204
|
return;
|
|
1160
1205
|
}
|
|
@@ -1389,11 +1434,17 @@ var View3 = class {
|
|
|
1389
1434
|
}
|
|
1390
1435
|
return 7007;
|
|
1391
1436
|
};
|
|
1437
|
+
_isSecureConnection = (params = {}) => {
|
|
1438
|
+
if (typeof params.secured === "boolean") {
|
|
1439
|
+
return params.secured;
|
|
1440
|
+
}
|
|
1441
|
+
return globalThis.STORYBOOK_WEBSOCKET?.secured ?? false;
|
|
1442
|
+
};
|
|
1392
1443
|
_getServerChannel = (params = {}) => {
|
|
1393
1444
|
const host = this._getHost(params);
|
|
1394
1445
|
const port = `:${this.__getPort(params)}`;
|
|
1395
1446
|
const query = params.query || "";
|
|
1396
|
-
const websocketType = params
|
|
1447
|
+
const websocketType = this._isSecureConnection(params) ? "wss" : "ws";
|
|
1397
1448
|
const url = `${websocketType}://${host}${port}/${query}`;
|
|
1398
1449
|
const channel = new import_channels.Channel({
|
|
1399
1450
|
async: true,
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import { MetroConfig } from 'metro-config';
|
|
2
|
+
import { W as WebsocketsOptions } from '../index-6iAzVvXp.js';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
-
* Options for configuring WebSockets used for syncing storybook instances or sending events to storybook.
|
|
5
|
-
*/
|
|
6
|
-
interface WebsocketsOptions {
|
|
7
|
-
/**
|
|
8
|
-
* The port WebSocket server will listen on. Defaults to 7007.
|
|
9
|
-
*/
|
|
10
|
-
port?: number;
|
|
11
|
-
/**
|
|
12
|
-
* The host WebSocket server will bind to. Defaults to 'localhost'.
|
|
13
|
-
*/
|
|
14
|
-
host?: string;
|
|
15
|
-
}
|
|
16
4
|
/**
|
|
17
5
|
* Options for configuring Storybook with React Native.
|
|
18
6
|
*/
|
|
@@ -64,6 +52,9 @@ interface WithStorybookOptions {
|
|
|
64
52
|
* When provided, creates a WebSocket server for real-time communication.
|
|
65
53
|
* @param options.websockets.port - The port WebSocket server will listen on. Defaults to 7007.
|
|
66
54
|
* @param options.websockets.host - The host WebSocket server will bind to. Defaults to 'localhost'.
|
|
55
|
+
* @param options.websockets.secured - Whether to use WSS/HTTPS for the channel server.
|
|
56
|
+
* @param options.websockets.key - TLS private key used when `secured` is true.
|
|
57
|
+
* @param options.websockets.cert - TLS certificate used when `secured` is true.
|
|
67
58
|
* @param options.useJs - Whether to use JavaScript files for Storybook configuration instead of TypeScript.
|
|
68
59
|
* When true, generates storybook.requires.js instead of storybook.requires.ts.
|
|
69
60
|
* Defaults to false.
|
|
@@ -201,7 +201,8 @@ var require_generate = __commonJS({
|
|
|
201
201
|
useJs = false,
|
|
202
202
|
docTools = true,
|
|
203
203
|
host = void 0,
|
|
204
|
-
port =
|
|
204
|
+
port = void 0,
|
|
205
|
+
secured = false
|
|
205
206
|
}) {
|
|
206
207
|
const channelHost = host === "auto" ? getLocalIPAddress() : host;
|
|
207
208
|
const storybookRequiresLocation = path3.resolve(
|
|
@@ -284,11 +285,22 @@ var require_generate = __commonJS({
|
|
|
284
285
|
const annotations = `[
|
|
285
286
|
${enhancers.join(",\n ")}
|
|
286
287
|
]`;
|
|
288
|
+
const hasWebsocketConfig = host !== void 0 || port !== void 0 || secured;
|
|
289
|
+
const websocketAssignmentLines = [];
|
|
290
|
+
if (channelHost) {
|
|
291
|
+
websocketAssignmentLines.push(`host: '${channelHost}',`);
|
|
292
|
+
}
|
|
293
|
+
if (hasWebsocketConfig) {
|
|
294
|
+
websocketAssignmentLines.push(`port: ${port ?? 7007},`);
|
|
295
|
+
websocketAssignmentLines.push(`secured: ${Boolean(secured)},`);
|
|
296
|
+
}
|
|
287
297
|
const globalTypes = `
|
|
288
298
|
declare global {
|
|
289
299
|
var view: View;
|
|
290
300
|
var STORIES: typeof normalizedStories;
|
|
291
|
-
var STORYBOOK_WEBSOCKET:
|
|
301
|
+
var STORYBOOK_WEBSOCKET:
|
|
302
|
+
| { host?: string; port?: number; secured?: boolean }
|
|
303
|
+
| undefined;
|
|
292
304
|
var FEATURES: Features;
|
|
293
305
|
}
|
|
294
306
|
`;
|
|
@@ -306,7 +318,9 @@ ${useJs ? "" : globalTypes}
|
|
|
306
318
|
const annotations = ${annotations};
|
|
307
319
|
|
|
308
320
|
globalThis.STORIES = normalizedStories;
|
|
309
|
-
${
|
|
321
|
+
${hasWebsocketConfig ? `globalThis.STORYBOOK_WEBSOCKET = {
|
|
322
|
+
${websocketAssignmentLines.join("\n ")}
|
|
323
|
+
};` : ""}
|
|
310
324
|
|
|
311
325
|
module?.hot?.accept?.();
|
|
312
326
|
${featuresAssignment ? `
|
|
@@ -574,6 +588,7 @@ var import_telemetry = require("storybook/internal/telemetry");
|
|
|
574
588
|
// src/metro/channelServer.ts
|
|
575
589
|
var import_ws2 = require("ws");
|
|
576
590
|
var import_node_http = require("http");
|
|
591
|
+
var import_node_https = require("https");
|
|
577
592
|
init_buildIndex();
|
|
578
593
|
|
|
579
594
|
// src/metro/mcpServer.ts
|
|
@@ -632,11 +647,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
632
647
|
{ McpServer },
|
|
633
648
|
{ ValibotJsonSchemaAdapter },
|
|
634
649
|
{ HttpTransport },
|
|
635
|
-
{
|
|
636
|
-
addListAllDocumentationTool,
|
|
637
|
-
addGetDocumentationTool,
|
|
638
|
-
addGetComponentStoryDocumentationTool
|
|
639
|
-
},
|
|
650
|
+
{ addListAllDocumentationTool, addGetDocumentationTool, addGetStoryDocumentationTool },
|
|
640
651
|
{ storyInstructions: storyInstructions2 },
|
|
641
652
|
{ buildIndex: buildIndex2 },
|
|
642
653
|
valibot,
|
|
@@ -675,7 +686,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
675
686
|
).withContext();
|
|
676
687
|
addListAllDocumentationTool(server);
|
|
677
688
|
addGetDocumentationTool(server);
|
|
678
|
-
|
|
689
|
+
addGetStoryDocumentationTool(server);
|
|
679
690
|
server.tool(
|
|
680
691
|
{
|
|
681
692
|
name: "get-storybook-story-instructions",
|
|
@@ -951,14 +962,20 @@ function createChannelServer({
|
|
|
951
962
|
host = void 0,
|
|
952
963
|
configPath,
|
|
953
964
|
experimental_mcp = false,
|
|
954
|
-
websockets = true
|
|
965
|
+
websockets = true,
|
|
966
|
+
secured = false,
|
|
967
|
+
ssl
|
|
955
968
|
}) {
|
|
956
|
-
|
|
969
|
+
if (secured && (!ssl?.key || !ssl?.cert)) {
|
|
970
|
+
throw new Error("[Storybook] Secure channel server requires both `ssl.key` and `ssl.cert`.");
|
|
971
|
+
}
|
|
972
|
+
const httpServer = secured ? (0, import_node_https.createServer)(ssl) : (0, import_node_http.createServer)();
|
|
957
973
|
const wss = websockets ? new import_ws2.WebSocketServer({ server: httpServer }) : null;
|
|
958
974
|
const mcpServer = experimental_mcp ? createMcpHandler(configPath, wss ?? void 0) : null;
|
|
959
975
|
const selectStorySyncEndpoint = wss ? createSelectStorySyncEndpoint(wss) : null;
|
|
960
976
|
httpServer.on("request", async (req, res) => {
|
|
961
|
-
const
|
|
977
|
+
const protocol = "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
|
|
978
|
+
const requestUrl = new URL(req.url ?? "/", `${protocol}://${req.headers.host ?? "localhost"}`);
|
|
962
979
|
if (req.method === "OPTIONS") {
|
|
963
980
|
res.writeHead(204);
|
|
964
981
|
res.end();
|
|
@@ -1059,7 +1076,7 @@ function createChannelServer({
|
|
|
1059
1076
|
}
|
|
1060
1077
|
});
|
|
1061
1078
|
httpServer.listen(port, host, () => {
|
|
1062
|
-
const protocol = wss ? "WebSocket" : "HTTP";
|
|
1079
|
+
const protocol = wss ? secured ? "WSS" : "WebSocket" : secured ? "HTTPS" : "HTTP";
|
|
1063
1080
|
console.log(`${protocol} server listening on ${host ?? "localhost"}:${port}`);
|
|
1064
1081
|
});
|
|
1065
1082
|
mcpServer?.preInit();
|
|
@@ -1125,12 +1142,20 @@ function withStorybook(config, options = {
|
|
|
1125
1142
|
if (websockets || experimental_mcp) {
|
|
1126
1143
|
const port = websockets === "auto" ? 7007 : websockets?.port ?? 7007;
|
|
1127
1144
|
const host = websockets === "auto" ? "auto" : websockets?.host;
|
|
1145
|
+
const secured = Boolean(websockets && websockets !== "auto" && websockets.secured);
|
|
1128
1146
|
createChannelServer({
|
|
1129
1147
|
port,
|
|
1130
1148
|
host: host === "auto" ? void 0 : host,
|
|
1131
1149
|
configPath,
|
|
1132
1150
|
experimental_mcp,
|
|
1133
|
-
websockets: Boolean(websockets)
|
|
1151
|
+
websockets: Boolean(websockets),
|
|
1152
|
+
secured,
|
|
1153
|
+
ssl: websockets && websockets !== "auto" ? {
|
|
1154
|
+
key: websockets.key,
|
|
1155
|
+
cert: websockets.cert,
|
|
1156
|
+
ca: websockets.ca,
|
|
1157
|
+
passphrase: websockets.passphrase
|
|
1158
|
+
} : void 0
|
|
1134
1159
|
});
|
|
1135
1160
|
if (websockets) {
|
|
1136
1161
|
(0, import_generate.generate)({
|
|
@@ -1138,7 +1163,8 @@ function withStorybook(config, options = {
|
|
|
1138
1163
|
useJs,
|
|
1139
1164
|
docTools,
|
|
1140
1165
|
host,
|
|
1141
|
-
port
|
|
1166
|
+
port,
|
|
1167
|
+
secured
|
|
1142
1168
|
});
|
|
1143
1169
|
} else {
|
|
1144
1170
|
(0, import_generate.generate)({
|
package/dist/node.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { WebSocketServer } from 'ws';
|
|
2
2
|
import { StoryIndex } from 'storybook/internal/types';
|
|
3
3
|
|
|
4
|
+
interface ChannelServerSecureOptions {
|
|
5
|
+
ca?: string | Buffer | Array<string | Buffer>;
|
|
6
|
+
cert?: string | Buffer | Array<string | Buffer>;
|
|
7
|
+
key?: string | Buffer | Array<string | Buffer>;
|
|
8
|
+
passphrase?: string;
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
11
|
* Options for creating a channel server.
|
|
6
12
|
*/
|
|
@@ -27,6 +33,15 @@ interface ChannelServerOptions {
|
|
|
27
33
|
* When false, starts only the HTTP server endpoints.
|
|
28
34
|
*/
|
|
29
35
|
websockets?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether to use HTTPS/WSS for the channel server.
|
|
38
|
+
* When true, valid TLS credentials must be provided via `ssl`.
|
|
39
|
+
*/
|
|
40
|
+
secured?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* TLS credentials used when `secured` is true.
|
|
43
|
+
*/
|
|
44
|
+
ssl?: ChannelServerSecureOptions;
|
|
30
45
|
}
|
|
31
46
|
/**
|
|
32
47
|
* Creates a channel server for syncing storybook instances and sending events.
|
|
@@ -43,9 +58,11 @@ interface ChannelServerOptions {
|
|
|
43
58
|
* @param options.configPath - The path to the Storybook config folder.
|
|
44
59
|
* @param options.experimental_mcp - Whether to enable MCP server support.
|
|
45
60
|
* @param options.websockets - Whether to enable WebSocket server support.
|
|
61
|
+
* @param options.secured - Whether to use HTTPS/WSS for the channel server.
|
|
62
|
+
* @param options.ssl - TLS credentials used when `secured` is true.
|
|
46
63
|
* @returns The created WebSocketServer instance, or null when websockets are disabled.
|
|
47
64
|
*/
|
|
48
|
-
declare function createChannelServer({ port, host, configPath, experimental_mcp, websockets, }: ChannelServerOptions): WebSocketServer | null;
|
|
65
|
+
declare function createChannelServer({ port, host, configPath, experimental_mcp, websockets, secured, ssl, }: ChannelServerOptions): WebSocketServer | null;
|
|
49
66
|
|
|
50
67
|
declare function buildIndex({ configPath }: {
|
|
51
68
|
configPath: string;
|
package/dist/node.js
CHANGED
|
@@ -353,6 +353,7 @@ module.exports = __toCommonJS(node_exports);
|
|
|
353
353
|
// src/metro/channelServer.ts
|
|
354
354
|
var import_ws2 = require("ws");
|
|
355
355
|
var import_node_http = require("http");
|
|
356
|
+
var import_node_https = require("https");
|
|
356
357
|
init_buildIndex();
|
|
357
358
|
|
|
358
359
|
// src/metro/mcpServer.ts
|
|
@@ -411,11 +412,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
411
412
|
{ McpServer },
|
|
412
413
|
{ ValibotJsonSchemaAdapter },
|
|
413
414
|
{ HttpTransport },
|
|
414
|
-
{
|
|
415
|
-
addListAllDocumentationTool,
|
|
416
|
-
addGetDocumentationTool,
|
|
417
|
-
addGetComponentStoryDocumentationTool
|
|
418
|
-
},
|
|
415
|
+
{ addListAllDocumentationTool, addGetDocumentationTool, addGetStoryDocumentationTool },
|
|
419
416
|
{ storyInstructions: storyInstructions2 },
|
|
420
417
|
{ buildIndex: buildIndex2 },
|
|
421
418
|
valibot,
|
|
@@ -454,7 +451,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
454
451
|
).withContext();
|
|
455
452
|
addListAllDocumentationTool(server);
|
|
456
453
|
addGetDocumentationTool(server);
|
|
457
|
-
|
|
454
|
+
addGetStoryDocumentationTool(server);
|
|
458
455
|
server.tool(
|
|
459
456
|
{
|
|
460
457
|
name: "get-storybook-story-instructions",
|
|
@@ -730,14 +727,20 @@ function createChannelServer({
|
|
|
730
727
|
host = void 0,
|
|
731
728
|
configPath,
|
|
732
729
|
experimental_mcp = false,
|
|
733
|
-
websockets = true
|
|
730
|
+
websockets = true,
|
|
731
|
+
secured = false,
|
|
732
|
+
ssl
|
|
734
733
|
}) {
|
|
735
|
-
|
|
734
|
+
if (secured && (!ssl?.key || !ssl?.cert)) {
|
|
735
|
+
throw new Error("[Storybook] Secure channel server requires both `ssl.key` and `ssl.cert`.");
|
|
736
|
+
}
|
|
737
|
+
const httpServer = secured ? (0, import_node_https.createServer)(ssl) : (0, import_node_http.createServer)();
|
|
736
738
|
const wss = websockets ? new import_ws2.WebSocketServer({ server: httpServer }) : null;
|
|
737
739
|
const mcpServer = experimental_mcp ? createMcpHandler(configPath, wss ?? void 0) : null;
|
|
738
740
|
const selectStorySyncEndpoint = wss ? createSelectStorySyncEndpoint(wss) : null;
|
|
739
741
|
httpServer.on("request", async (req, res) => {
|
|
740
|
-
const
|
|
742
|
+
const protocol = "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
|
|
743
|
+
const requestUrl = new URL(req.url ?? "/", `${protocol}://${req.headers.host ?? "localhost"}`);
|
|
741
744
|
if (req.method === "OPTIONS") {
|
|
742
745
|
res.writeHead(204);
|
|
743
746
|
res.end();
|
|
@@ -838,7 +841,7 @@ function createChannelServer({
|
|
|
838
841
|
}
|
|
839
842
|
});
|
|
840
843
|
httpServer.listen(port, host, () => {
|
|
841
|
-
const protocol = wss ? "WebSocket" : "HTTP";
|
|
844
|
+
const protocol = wss ? secured ? "WSS" : "WebSocket" : secured ? "HTTPS" : "HTTP";
|
|
842
845
|
console.log(`${protocol} server listening on ${host ?? "localhost"}:${port}`);
|
|
843
846
|
});
|
|
844
847
|
mcpServer?.preInit();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { W as WebsocketsOptions } from '../index-6iAzVvXp.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Minimal compiler types for webpack/rspack compatibility.
|
|
3
5
|
* We define these inline to avoid requiring @rspack/core or webpack as dependencies.
|
|
@@ -21,19 +23,6 @@ interface Compiler {
|
|
|
21
23
|
};
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Options for configuring WebSockets used for syncing storybook instances or sending events to storybook.
|
|
26
|
-
*/
|
|
27
|
-
interface WebsocketsOptions {
|
|
28
|
-
/**
|
|
29
|
-
* The port WebSocket server will listen on. Defaults to 7007.
|
|
30
|
-
*/
|
|
31
|
-
port?: number;
|
|
32
|
-
/**
|
|
33
|
-
* The host WebSocket server will bind to. Defaults to 'localhost'.
|
|
34
|
-
*/
|
|
35
|
-
host?: string;
|
|
36
|
-
}
|
|
37
26
|
/**
|
|
38
27
|
* Options for configuring the Storybook Repack plugin.
|
|
39
28
|
*/
|
|
@@ -201,7 +201,8 @@ var require_generate = __commonJS({
|
|
|
201
201
|
useJs = false,
|
|
202
202
|
docTools = true,
|
|
203
203
|
host = void 0,
|
|
204
|
-
port =
|
|
204
|
+
port = void 0,
|
|
205
|
+
secured = false
|
|
205
206
|
}) {
|
|
206
207
|
const channelHost = host === "auto" ? getLocalIPAddress() : host;
|
|
207
208
|
const storybookRequiresLocation = path3.resolve(
|
|
@@ -284,11 +285,22 @@ var require_generate = __commonJS({
|
|
|
284
285
|
const annotations = `[
|
|
285
286
|
${enhancers.join(",\n ")}
|
|
286
287
|
]`;
|
|
288
|
+
const hasWebsocketConfig = host !== void 0 || port !== void 0 || secured;
|
|
289
|
+
const websocketAssignmentLines = [];
|
|
290
|
+
if (channelHost) {
|
|
291
|
+
websocketAssignmentLines.push(`host: '${channelHost}',`);
|
|
292
|
+
}
|
|
293
|
+
if (hasWebsocketConfig) {
|
|
294
|
+
websocketAssignmentLines.push(`port: ${port ?? 7007},`);
|
|
295
|
+
websocketAssignmentLines.push(`secured: ${Boolean(secured)},`);
|
|
296
|
+
}
|
|
287
297
|
const globalTypes = `
|
|
288
298
|
declare global {
|
|
289
299
|
var view: View;
|
|
290
300
|
var STORIES: typeof normalizedStories;
|
|
291
|
-
var STORYBOOK_WEBSOCKET:
|
|
301
|
+
var STORYBOOK_WEBSOCKET:
|
|
302
|
+
| { host?: string; port?: number; secured?: boolean }
|
|
303
|
+
| undefined;
|
|
292
304
|
var FEATURES: Features;
|
|
293
305
|
}
|
|
294
306
|
`;
|
|
@@ -306,7 +318,9 @@ ${useJs ? "" : globalTypes}
|
|
|
306
318
|
const annotations = ${annotations};
|
|
307
319
|
|
|
308
320
|
globalThis.STORIES = normalizedStories;
|
|
309
|
-
${
|
|
321
|
+
${hasWebsocketConfig ? `globalThis.STORYBOOK_WEBSOCKET = {
|
|
322
|
+
${websocketAssignmentLines.join("\n ")}
|
|
323
|
+
};` : ""}
|
|
310
324
|
|
|
311
325
|
module?.hot?.accept?.();
|
|
312
326
|
${featuresAssignment ? `
|
|
@@ -572,6 +586,7 @@ var import_generate = __toESM(require_generate());
|
|
|
572
586
|
// src/metro/channelServer.ts
|
|
573
587
|
var import_ws2 = require("ws");
|
|
574
588
|
var import_node_http = require("http");
|
|
589
|
+
var import_node_https = require("https");
|
|
575
590
|
init_buildIndex();
|
|
576
591
|
|
|
577
592
|
// src/metro/mcpServer.ts
|
|
@@ -630,11 +645,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
630
645
|
{ McpServer },
|
|
631
646
|
{ ValibotJsonSchemaAdapter },
|
|
632
647
|
{ HttpTransport },
|
|
633
|
-
{
|
|
634
|
-
addListAllDocumentationTool,
|
|
635
|
-
addGetDocumentationTool,
|
|
636
|
-
addGetComponentStoryDocumentationTool
|
|
637
|
-
},
|
|
648
|
+
{ addListAllDocumentationTool, addGetDocumentationTool, addGetStoryDocumentationTool },
|
|
638
649
|
{ storyInstructions: storyInstructions2 },
|
|
639
650
|
{ buildIndex: buildIndex2 },
|
|
640
651
|
valibot,
|
|
@@ -673,7 +684,7 @@ function createMcpHandler(configPath, wss) {
|
|
|
673
684
|
).withContext();
|
|
674
685
|
addListAllDocumentationTool(server);
|
|
675
686
|
addGetDocumentationTool(server);
|
|
676
|
-
|
|
687
|
+
addGetStoryDocumentationTool(server);
|
|
677
688
|
server.tool(
|
|
678
689
|
{
|
|
679
690
|
name: "get-storybook-story-instructions",
|
|
@@ -949,14 +960,20 @@ function createChannelServer({
|
|
|
949
960
|
host = void 0,
|
|
950
961
|
configPath,
|
|
951
962
|
experimental_mcp = false,
|
|
952
|
-
websockets = true
|
|
963
|
+
websockets = true,
|
|
964
|
+
secured = false,
|
|
965
|
+
ssl
|
|
953
966
|
}) {
|
|
954
|
-
|
|
967
|
+
if (secured && (!ssl?.key || !ssl?.cert)) {
|
|
968
|
+
throw new Error("[Storybook] Secure channel server requires both `ssl.key` and `ssl.cert`.");
|
|
969
|
+
}
|
|
970
|
+
const httpServer = secured ? (0, import_node_https.createServer)(ssl) : (0, import_node_http.createServer)();
|
|
955
971
|
const wss = websockets ? new import_ws2.WebSocketServer({ server: httpServer }) : null;
|
|
956
972
|
const mcpServer = experimental_mcp ? createMcpHandler(configPath, wss ?? void 0) : null;
|
|
957
973
|
const selectStorySyncEndpoint = wss ? createSelectStorySyncEndpoint(wss) : null;
|
|
958
974
|
httpServer.on("request", async (req, res) => {
|
|
959
|
-
const
|
|
975
|
+
const protocol = "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
|
|
976
|
+
const requestUrl = new URL(req.url ?? "/", `${protocol}://${req.headers.host ?? "localhost"}`);
|
|
960
977
|
if (req.method === "OPTIONS") {
|
|
961
978
|
res.writeHead(204);
|
|
962
979
|
res.end();
|
|
@@ -1057,7 +1074,7 @@ function createChannelServer({
|
|
|
1057
1074
|
}
|
|
1058
1075
|
});
|
|
1059
1076
|
httpServer.listen(port, host, () => {
|
|
1060
|
-
const protocol = wss ? "WebSocket" : "HTTP";
|
|
1077
|
+
const protocol = wss ? secured ? "WSS" : "WebSocket" : secured ? "HTTPS" : "HTTP";
|
|
1061
1078
|
console.log(`${protocol} server listening on ${host ?? "localhost"}:${port}`);
|
|
1062
1079
|
});
|
|
1063
1080
|
mcpServer?.preInit();
|
|
@@ -1109,6 +1126,7 @@ var StorybookPlugin = class {
|
|
|
1109
1126
|
}) {
|
|
1110
1127
|
const port = websockets === "auto" ? 7007 : websockets?.port ?? 7007;
|
|
1111
1128
|
const host = websockets === "auto" ? "auto" : websockets?.host;
|
|
1129
|
+
const secured = Boolean(websockets && websockets !== "auto" && websockets.secured);
|
|
1112
1130
|
if ((websockets || experimental_mcp) && !this.serverStarted) {
|
|
1113
1131
|
this.serverStarted = true;
|
|
1114
1132
|
createChannelServer({
|
|
@@ -1116,7 +1134,14 @@ var StorybookPlugin = class {
|
|
|
1116
1134
|
host: host === "auto" ? void 0 : host,
|
|
1117
1135
|
configPath,
|
|
1118
1136
|
experimental_mcp,
|
|
1119
|
-
websockets: Boolean(websockets)
|
|
1137
|
+
websockets: Boolean(websockets),
|
|
1138
|
+
secured,
|
|
1139
|
+
ssl: websockets && websockets !== "auto" ? {
|
|
1140
|
+
key: websockets.key,
|
|
1141
|
+
cert: websockets.cert,
|
|
1142
|
+
ca: websockets.ca,
|
|
1143
|
+
passphrase: websockets.passphrase
|
|
1144
|
+
} : void 0
|
|
1120
1145
|
});
|
|
1121
1146
|
}
|
|
1122
1147
|
compiler.hooks.beforeCompile.tapPromise("StorybookPlugin", async () => {
|
|
@@ -1126,7 +1151,7 @@ var StorybookPlugin = class {
|
|
|
1126
1151
|
configPath,
|
|
1127
1152
|
useJs,
|
|
1128
1153
|
docTools,
|
|
1129
|
-
...websockets ? { host, port } : {}
|
|
1154
|
+
...websockets ? { host, port, secured } : {}
|
|
1130
1155
|
});
|
|
1131
1156
|
console.log("[StorybookPlugin] Generated storybook.requires");
|
|
1132
1157
|
});
|
|
@@ -1141,7 +1166,10 @@ var StorybookPlugin = class {
|
|
|
1141
1166
|
* and replace the config folder index with a stub component.
|
|
1142
1167
|
*/
|
|
1143
1168
|
applyDisabled(compiler, configPath) {
|
|
1144
|
-
const stubPath =
|
|
1169
|
+
const stubPath = path2.resolve(
|
|
1170
|
+
__dirname,
|
|
1171
|
+
__dirname.includes(`${path2.sep}src${path2.sep}`) ? "../stub.tsx" : "../stub.js"
|
|
1172
|
+
);
|
|
1145
1173
|
const normalizedConfigPath = path2.resolve(configPath);
|
|
1146
1174
|
new compiler.webpack.NormalModuleReplacementPlugin(/./, (resource) => {
|
|
1147
1175
|
const request = resource.request;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native",
|
|
3
|
-
"version": "10.3.
|
|
3
|
+
"version": "10.3.1",
|
|
4
4
|
"description": "A better way to develop React Native Components for your app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -43,36 +43,36 @@
|
|
|
43
43
|
"metro/**/*"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@storybook/mcp": "^0.
|
|
47
|
-
"@storybook/react": "10.3.
|
|
48
|
-
"@storybook/react-native-theming": "^10.3.
|
|
49
|
-
"@storybook/react-native-ui": "^10.3.
|
|
50
|
-
"@storybook/react-native-ui-common": "^10.3.
|
|
46
|
+
"@storybook/mcp": "^0.6.1",
|
|
47
|
+
"@storybook/react": "^10.3.1",
|
|
48
|
+
"@storybook/react-native-theming": "^10.3.1",
|
|
49
|
+
"@storybook/react-native-ui": "^10.3.1",
|
|
50
|
+
"@storybook/react-native-ui-common": "^10.3.1",
|
|
51
51
|
"@tmcp/adapter-valibot": "^0.1.4",
|
|
52
|
-
"@tmcp/transport-http": "^0.8.
|
|
52
|
+
"@tmcp/transport-http": "^0.8.5",
|
|
53
53
|
"commander": "^14.0.2",
|
|
54
|
-
"dedent": "^1.7.
|
|
54
|
+
"dedent": "^1.7.2",
|
|
55
55
|
"deepmerge": "^4.3.1",
|
|
56
56
|
"esbuild-register": "^3.6.0",
|
|
57
57
|
"glob": "^13.0.0",
|
|
58
58
|
"react-native-url-polyfill": "^3.0.0",
|
|
59
59
|
"setimmediate": "^1.0.5",
|
|
60
|
-
"tmcp": "^1.
|
|
61
|
-
"valibot": "^1.
|
|
62
|
-
"ws": "^8.
|
|
60
|
+
"tmcp": "^1.19.3",
|
|
61
|
+
"valibot": "^1.3.1",
|
|
62
|
+
"ws": "^8.20.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/jest": "^29.4.3",
|
|
66
66
|
"@types/react": "~19.2.14",
|
|
67
67
|
"babel-jest": "^29.7.0",
|
|
68
|
-
"babel-preset-expo": "^
|
|
68
|
+
"babel-preset-expo": "^55.0.13",
|
|
69
69
|
"jest": "^29.7.0",
|
|
70
|
-
"jest-expo": "~55.0.
|
|
71
|
-
"jotai": "^2.
|
|
70
|
+
"jest-expo": "~55.0.11",
|
|
71
|
+
"jotai": "^2.19.0",
|
|
72
72
|
"react": "19.2.0",
|
|
73
|
-
"react-native": "0.83.
|
|
74
|
-
"storybook": "10.3.
|
|
75
|
-
"test-renderer": "^0.
|
|
73
|
+
"react-native": "0.83.4",
|
|
74
|
+
"storybook": "^10.3.1",
|
|
75
|
+
"test-renderer": "^0.15.0",
|
|
76
76
|
"tsup": "^8.5.0",
|
|
77
77
|
"typescript": "~5.9.3"
|
|
78
78
|
},
|
package/readme.md
CHANGED
|
@@ -461,7 +461,7 @@ You can enable MCP with or without websockets:
|
|
|
461
461
|
|
|
462
462
|
### websockets
|
|
463
463
|
|
|
464
|
-
Type: `'auto' | { host
|
|
464
|
+
Type: `'auto' | { host?: string, port?: number, secured?: boolean, key?: string | Buffer, cert?: string | Buffer, ca?: string | Buffer | Array<string | Buffer>, passphrase?: string }`, default: `undefined`
|
|
465
465
|
|
|
466
466
|
If specified, create a WebSocket server on startup. This allows you to sync up multiple devices to show the same story and [arg](https://storybook.js.org/docs/writing-stories/args) values connected to the story in the UI.
|
|
467
467
|
|
|
@@ -479,6 +479,18 @@ Type: `number`, default: `7007`
|
|
|
479
479
|
|
|
480
480
|
The port on which to run the WebSocket, if specified.
|
|
481
481
|
|
|
482
|
+
### websockets.secured
|
|
483
|
+
|
|
484
|
+
Type: `boolean`, default: `false`
|
|
485
|
+
|
|
486
|
+
When `true`, the channel server starts on `https` and upgrades WebSocket clients over `wss`.
|
|
487
|
+
|
|
488
|
+
### websockets.key / websockets.cert
|
|
489
|
+
|
|
490
|
+
Type: `string | Buffer`, default: `undefined`
|
|
491
|
+
|
|
492
|
+
TLS private key and certificate used when `secured` is `true`.
|
|
493
|
+
|
|
482
494
|
## getStorybookUI options
|
|
483
495
|
|
|
484
496
|
You can pass these parameters to getStorybookUI call in your storybook entry point:
|
package/scripts/generate.js
CHANGED
|
@@ -55,7 +55,8 @@ async function generate({
|
|
|
55
55
|
useJs = false,
|
|
56
56
|
docTools = true,
|
|
57
57
|
host = undefined,
|
|
58
|
-
port =
|
|
58
|
+
port = undefined,
|
|
59
|
+
secured = false,
|
|
59
60
|
}) {
|
|
60
61
|
// here we want to get the ip address and pass it to rn storybook so that devices can connect over lan easily
|
|
61
62
|
const channelHost = host === 'auto' ? getLocalIPAddress() : host;
|
|
@@ -165,11 +166,25 @@ async function generate({
|
|
|
165
166
|
${enhancers.join(',\n ')}
|
|
166
167
|
]`;
|
|
167
168
|
|
|
169
|
+
const hasWebsocketConfig = host !== undefined || port !== undefined || secured;
|
|
170
|
+
const websocketAssignmentLines = [];
|
|
171
|
+
|
|
172
|
+
if (channelHost) {
|
|
173
|
+
websocketAssignmentLines.push(`host: '${channelHost}',`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (hasWebsocketConfig) {
|
|
177
|
+
websocketAssignmentLines.push(`port: ${port ?? 7007},`);
|
|
178
|
+
websocketAssignmentLines.push(`secured: ${Boolean(secured)},`);
|
|
179
|
+
}
|
|
180
|
+
|
|
168
181
|
const globalTypes = `
|
|
169
182
|
declare global {
|
|
170
183
|
var view: View;
|
|
171
184
|
var STORIES: typeof normalizedStories;
|
|
172
|
-
var STORYBOOK_WEBSOCKET:
|
|
185
|
+
var STORYBOOK_WEBSOCKET:
|
|
186
|
+
| { host?: string; port?: number; secured?: boolean }
|
|
187
|
+
| undefined;
|
|
173
188
|
var FEATURES: Features;
|
|
174
189
|
}
|
|
175
190
|
`;
|
|
@@ -188,7 +203,13 @@ ${useJs ? '' : globalTypes}
|
|
|
188
203
|
const annotations = ${annotations};
|
|
189
204
|
|
|
190
205
|
globalThis.STORIES = normalizedStories;
|
|
191
|
-
${
|
|
206
|
+
${
|
|
207
|
+
hasWebsocketConfig
|
|
208
|
+
? `globalThis.STORYBOOK_WEBSOCKET = {
|
|
209
|
+
${websocketAssignmentLines.join('\n ')}
|
|
210
|
+
};`
|
|
211
|
+
: ''
|
|
212
|
+
}
|
|
192
213
|
|
|
193
214
|
module?.hot?.accept?.();
|
|
194
215
|
${featuresAssignment ? `\n${featuresAssignment}\n` : ''}
|