@gitbook/react-openapi 1.0.4 → 1.0.5

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.
@@ -1 +1,10 @@
1
- export declare function useSyncedTabsGlobalState<T>(): readonly [Map<string, T>, (updater: (tabs: Map<string, T>) => Map<string, T>) => void];
1
+ type Key = string | number;
2
+ type TabState = {
3
+ tabKey: Key | null;
4
+ };
5
+ type TabActions = {
6
+ setTabKey: (tab: Key | null) => void;
7
+ };
8
+ type TabStore = TabState & TabActions;
9
+ export declare const getOrCreateTabStoreByKey: (storeKey: string, initialKey?: Key) => import("zustand").StoreApi<TabStore>;
10
+ export {};
@@ -1,16 +1,20 @@
1
1
  'use client';
2
- import { create } from 'zustand';
3
- var useSyncedTabsStore = create()(function (set) { return ({
4
- tabs: new Map(),
5
- setTabs: function (updater) {
6
- return set(function (state) { return ({
7
- tabs: updater(new Map(state.tabs)), // Ensure a new Map is created for reactivity
8
- }); });
9
- },
10
- }); });
11
- // Selector for better performance - only re-renders when tabs change
12
- export function useSyncedTabsGlobalState() {
13
- var tabs = useSyncedTabsStore(function (state) { return state.tabs; });
14
- var setTabs = useSyncedTabsStore(function (state) { return state.setTabs; });
15
- return [tabs, setTabs];
16
- }
2
+ import { createStore } from 'zustand';
3
+ var createTabStore = function (initialTab) {
4
+ return createStore()(function (set) { return ({
5
+ tabKey: initialTab !== null && initialTab !== void 0 ? initialTab : null,
6
+ setTabKey: function (tabKey) {
7
+ set(function () { return ({ tabKey: tabKey }); });
8
+ },
9
+ }); });
10
+ };
11
+ var defaultTabStores = new Map();
12
+ var createTabStoreFactory = function (stores) {
13
+ return function (storeKey, initialKey) {
14
+ if (!stores.has(storeKey)) {
15
+ stores.set(storeKey, createTabStore(initialKey));
16
+ }
17
+ return stores.get(storeKey);
18
+ };
19
+ };
20
+ export var getOrCreateTabStoreByKey = createTabStoreFactory(defaultTabStores);
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "default": "./dist/index.js"
9
9
  }
10
10
  },
11
- "version": "1.0.4",
11
+ "version": "1.0.5",
12
12
  "sideEffects": false,
13
13
  "dependencies": {
14
14
  "@gitbook/openapi-parser": "workspace:*",
@@ -4,6 +4,7 @@ import clsx from 'clsx';
4
4
  import { useRef, useState } from 'react';
5
5
  import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
6
6
  import { useDisclosureState } from 'react-stately';
7
+ import { Section, SectionBody, SectionHeader, SectionHeaderContent } from './StaticSection';
7
8
 
8
9
  interface InteractiveSectionTab {
9
10
  key: string;
@@ -63,7 +64,7 @@ export function InteractiveSection(props: {
63
64
  const { isFocusVisible, focusProps } = useFocusRing();
64
65
 
65
66
  return (
66
- <div
67
+ <Section
67
68
  id={id}
68
69
  className={clsx(
69
70
  'openapi-section',
@@ -73,20 +74,15 @@ export function InteractiveSection(props: {
73
74
  )}
74
75
  >
75
76
  {header ? (
76
- <div
77
+ <SectionHeader
77
78
  onClick={() => {
78
79
  if (toggeable) {
79
80
  state.toggle();
80
81
  }
81
82
  }}
82
- className={clsx('openapi-section-header', `${className}-header`)}
83
+ className={className}
83
84
  >
84
- <div
85
- className={clsx(
86
- 'openapi-section-header-content',
87
- `${className}-header-content`
88
- )}
89
- >
85
+ <SectionHeaderContent className={className}>
90
86
  {(children || selectedTab?.body) && toggeable ? (
91
87
  <button
92
88
  {...mergeProps(buttonProps, focusProps)}
@@ -102,7 +98,7 @@ export function InteractiveSection(props: {
102
98
  </button>
103
99
  ) : null}
104
100
  {header}
105
- </div>
101
+ </SectionHeaderContent>
106
102
  <div
107
103
  className={clsx(
108
104
  'openapi-section-header-controls',
@@ -133,23 +129,19 @@ export function InteractiveSection(props: {
133
129
  </select>
134
130
  ) : null}
135
131
  </div>
136
- </div>
132
+ </SectionHeader>
137
133
  ) : null}
138
134
  {(!toggeable || state.isExpanded) && (children || selectedTab?.body) ? (
139
- <div
140
- ref={panelRef}
141
- {...panelProps}
142
- className={clsx('openapi-section-body', `${className}-body`)}
143
- >
135
+ <SectionBody ref={panelRef} {...panelProps} className={className}>
144
136
  {children}
145
137
  {selectedTab?.body}
146
- </div>
138
+ </SectionBody>
147
139
  ) : null}
148
140
  {overlay ? (
149
141
  <div className={clsx('openapi-section-overlay', `${className}-overlay`)}>
150
142
  {overlay}
151
143
  </div>
152
144
  ) : null}
153
- </div>
145
+ </Section>
154
146
  );
155
147
  }
@@ -1,5 +1,5 @@
1
- import { InteractiveSection } from './InteractiveSection';
2
1
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
2
+ import { StaticSection } from './StaticSection';
3
3
  import { type CodeSampleInput, codeSampleGenerators } from './code-samples';
4
4
  import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
5
5
  import { stringifyOpenAPI } from './stringifyOpenAPI';
@@ -117,9 +117,9 @@ export function OpenAPICodeSample(props: {
117
117
 
118
118
  return (
119
119
  <OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
120
- <InteractiveSection header={<OpenAPITabsList />} className="openapi-codesample">
120
+ <StaticSection header={<OpenAPITabsList />} className="openapi-codesample">
121
121
  <OpenAPITabsPanels />
122
- </InteractiveSection>
122
+ </StaticSection>
123
123
  </OpenAPITabs>
124
124
  );
125
125
  }
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import type React from 'react';
2
3
  import { useRef } from 'react';
3
4
  import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
@@ -1,3 +1,14 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useRef, useState } from 'react';
4
+ import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
5
+ import {
6
+ type DisclosureGroupProps,
7
+ type DisclosureGroupState,
8
+ useDisclosureGroupState,
9
+ useDisclosureState,
10
+ } from 'react-stately';
11
+
1
12
  interface Props {
2
13
  groups: TDisclosureGroup[];
3
14
  icon?: React.ReactNode;
@@ -13,15 +24,6 @@ type TDisclosureGroup = {
13
24
  }[];
14
25
  };
15
26
 
16
- import { createContext, useContext, useRef, useState } from 'react';
17
- import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
18
- import {
19
- type DisclosureGroupProps,
20
- type DisclosureGroupState,
21
- useDisclosureGroupState,
22
- useDisclosureState,
23
- } from 'react-stately';
24
-
25
27
  const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(null);
26
28
 
27
29
  /**
@@ -1,6 +1,6 @@
1
1
  import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
- import { InteractiveSection } from './InteractiveSection';
3
2
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
3
+ import { StaticSection } from './StaticSection';
4
4
  import { generateSchemaExample } from './generateSchemaExample';
5
5
  import { json2xml } from './json2xml';
6
6
  import { stringifyOpenAPI } from './stringifyOpenAPI';
@@ -84,9 +84,9 @@ export function OpenAPIResponseExample(props: {
84
84
 
85
85
  return (
86
86
  <OpenAPITabs stateKey={createStateKey('response-example')} items={tabs}>
87
- <InteractiveSection header={<OpenAPITabsList />} className="openapi-response-example">
87
+ <StaticSection header={<OpenAPITabsList />} className="openapi-response-example">
88
88
  <OpenAPITabsPanels />
89
- </InteractiveSection>
89
+ </StaticSection>
90
90
  </OpenAPITabs>
91
91
  );
92
92
  }
@@ -134,12 +134,9 @@ function OpenAPIResponse(props: {
134
134
 
135
135
  return (
136
136
  <OpenAPITabs stateKey={createStateKey('response-media-types')} items={tabs}>
137
- <InteractiveSection
138
- header={<OpenAPITabsList />}
139
- className="openapi-response-media-types"
140
- >
137
+ <StaticSection header={<OpenAPITabsList />} className="openapi-response-media-types">
141
138
  <OpenAPITabsPanels />
142
- </InteractiveSection>
139
+ </StaticSection>
143
140
  </OpenAPITabs>
144
141
  );
145
142
  }
@@ -173,23 +170,19 @@ function OpenAPIResponseMediaType(props: {
173
170
  key: example.key,
174
171
  label: example.example.summary || example.key,
175
172
  body: (
176
- <OpenAPIExample
177
- example={firstExample.example}
178
- context={props.context}
179
- syntax={syntax}
180
- />
173
+ <OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
181
174
  ),
182
175
  };
183
176
  });
184
177
 
185
178
  return (
186
179
  <OpenAPITabs stateKey={createStateKey('response-media-type-examples')} items={tabs}>
187
- <InteractiveSection
180
+ <StaticSection
188
181
  header={<OpenAPITabsList />}
189
182
  className="openapi-response-media-type-examples"
190
183
  >
191
184
  <OpenAPITabsPanels />
192
- </InteractiveSection>
185
+ </StaticSection>
193
186
  </OpenAPITabs>
194
187
  );
195
188
  }
@@ -1,8 +1,8 @@
1
1
  import type { OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
2
- import { InteractiveSection } from './InteractiveSection';
3
2
  import { Markdown } from './Markdown';
4
3
  import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
5
4
  import { OpenAPIResponse } from './OpenAPIResponse';
5
+ import { StaticSection } from './StaticSection';
6
6
  import type { OpenAPIClientContext } from './types';
7
7
 
8
8
  /**
@@ -15,7 +15,7 @@ export function OpenAPIResponses(props: {
15
15
  const { responses, context } = props;
16
16
 
17
17
  return (
18
- <InteractiveSection header="Responses" className="openapi-responses">
18
+ <StaticSection header="Responses" className="openapi-responses">
19
19
  <OpenAPIDisclosureGroup
20
20
  allowsMultipleExpanded
21
21
  icon={context.icons.chevronRight}
@@ -58,6 +58,6 @@ export function OpenAPIResponses(props: {
58
58
  }
59
59
  )}
60
60
  />
61
- </InteractiveSection>
61
+ </StaticSection>
62
62
  );
63
63
  }
@@ -119,9 +119,9 @@ export function OpenAPISchemaProperties(props: {
119
119
 
120
120
  return (
121
121
  <div id={id} className="openapi-schema-properties">
122
- {properties.map((property) => (
122
+ {properties.map((property, index) => (
123
123
  <OpenAPISchemaProperty
124
- key={property.propertyName}
124
+ key={index}
125
125
  circularRefs={circularRefs}
126
126
  {...property}
127
127
  context={context}
@@ -1,12 +1,10 @@
1
- 'use client';
2
-
3
1
  import type { OpenAPI } from '@gitbook/openapi-parser';
4
2
 
5
- import { InteractiveSection } from './InteractiveSection';
6
3
  import { OpenAPIRequestBody } from './OpenAPIRequestBody';
7
4
  import { OpenAPIResponses } from './OpenAPIResponses';
8
5
  import { OpenAPISchemaProperties } from './OpenAPISchema';
9
6
  import { OpenAPISecurities } from './OpenAPISecurities';
7
+ import { StaticSection } from './StaticSection';
10
8
  import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
11
9
  import { parameterToProperty } from './utils';
12
10
 
@@ -32,7 +30,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
32
30
 
33
31
  {parameterGroups.map((group) => {
34
32
  return (
35
- <InteractiveSection
33
+ <StaticSection
36
34
  key={group.key}
37
35
  className="openapi-parameters"
38
36
  header={group.label}
@@ -41,7 +39,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
41
39
  properties={group.parameters.map(parameterToProperty)}
42
40
  context={context}
43
41
  />
44
- </InteractiveSection>
42
+ </StaticSection>
45
43
  );
46
44
  })}
47
45
 
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { createContext, useContext, useEffect, useMemo, useState } from 'react';
3
+ import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { type Key, Tab, TabList, TabPanel, Tabs, type TabsProps } from 'react-aria-components';
5
- import { useIntersectionObserver } from 'usehooks-ts';
5
+ import { useEventCallback } from 'usehooks-ts';
6
6
  import { Markdown } from './Markdown';
7
- import { useSyncedTabsGlobalState } from './useSyncedTabsGlobalState';
7
+ import { getOrCreateTabStoreByKey } from './useSyncedTabsGlobalState';
8
8
 
9
9
  export type TabItem = {
10
10
  key: Key;
@@ -15,7 +15,7 @@ export type TabItem = {
15
15
 
16
16
  type OpenAPITabsContextData = {
17
17
  items: TabItem[];
18
- selectedTab: TabItem;
18
+ selectedTab: TabItem | null;
19
19
  };
20
20
 
21
21
  const OpenAPITabsContext = createContext<OpenAPITabsContextData | null>(null);
@@ -35,68 +35,54 @@ export function OpenAPITabs(
35
35
  props: React.PropsWithChildren<TabsProps & { items: TabItem[]; stateKey?: string }>
36
36
  ) {
37
37
  const { children, items, stateKey } = props;
38
- const [ref, isIntersectionVisible] = useIntersectionObserver({
39
- threshold: 0.1,
40
- rootMargin: '200px',
41
- });
42
- const isVisible = stateKey ? isIntersectionVisible : true;
43
- const defaultTab = items[0] as TabItem;
44
- const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState<TabItem>();
45
- const [selectedTabKey, setSelectedTabKey] = useState(() => {
46
- if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
47
- const tabFromState = syncedTabs.get(stateKey);
48
- return tabFromState?.key ?? items[0]?.key;
38
+ const [tabKey, setTabKey] = useState<Key | null>(() => {
39
+ if (stateKey && typeof window !== 'undefined') {
40
+ const store = getOrCreateTabStoreByKey(stateKey);
41
+ const tabKey = store.getState().tabKey;
42
+ if (tabKey) {
43
+ return tabKey;
44
+ }
49
45
  }
50
- return items[0]?.key;
46
+ return items[0]?.key ?? null;
51
47
  });
52
- const [selectedTab, setSelectedTab] = useState<TabItem>(defaultTab);
53
-
54
- const handleSelectionChange = (key: Key) => {
55
- setSelectedTabKey(key);
56
- if (stateKey) {
57
- const tab = items.find((item) => item.key === key);
58
-
59
- if (!tab) {
60
- return;
61
- }
62
-
63
- setSyncedTabs((state) => {
64
- const newState = new Map(state);
65
- newState.set(stateKey, tab);
66
- return newState;
67
- });
48
+ const selectTab = useEventCallback((key: Key | null) => {
49
+ if (!key || key === tabKey) {
50
+ return;
68
51
  }
69
- };
70
-
52
+ const tab = items.find((item) => item.key === key);
53
+ if (!tab) {
54
+ return;
55
+ }
56
+ setTabKey(key);
57
+ });
58
+ const selectedTab = items.find((item) => item.key === tabKey) ?? items[0] ?? null;
59
+ const cancelDeferRef = useRef<(() => void) | null>(null);
71
60
  useEffect(() => {
72
- if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
73
- const tabFromState = syncedTabs.get(stateKey);
74
-
75
- if (!items.some((item) => item.key === tabFromState?.key)) {
76
- return setSelectedTab(defaultTab);
77
- }
78
-
79
- if (tabFromState && tabFromState?.key !== selectedTab?.key) {
80
- const tabFromItems = items.find((item) => item.key === tabFromState.key);
81
-
82
- if (!tabFromItems) {
83
- return;
84
- }
85
-
86
- setSelectedTab(tabFromItems);
87
- }
61
+ if (!stateKey) {
62
+ return undefined;
88
63
  }
89
- }, [isVisible, stateKey, syncedTabs, selectedTabKey]);
90
-
64
+ const store = getOrCreateTabStoreByKey(stateKey);
65
+ return store.subscribe((state) => {
66
+ cancelDeferRef.current?.();
67
+ cancelDeferRef.current = defer(() => selectTab(state.tabKey));
68
+ });
69
+ }, [stateKey, selectTab]);
70
+ useEffect(() => {
71
+ return () => cancelDeferRef.current?.();
72
+ }, []);
91
73
  const contextValue = useMemo(() => ({ items, selectedTab }), [items, selectedTab]);
92
-
93
74
  return (
94
75
  <OpenAPITabsContext.Provider value={contextValue}>
95
76
  <Tabs
96
- ref={ref}
97
77
  className="openapi-tabs"
98
- onSelectionChange={handleSelectionChange}
99
- selectedKey={selectedTab?.key}
78
+ onSelectionChange={(tabKey) => {
79
+ selectTab(tabKey);
80
+ if (stateKey) {
81
+ const store = getOrCreateTabStoreByKey(stateKey);
82
+ store.setState({ tabKey });
83
+ }
84
+ }}
85
+ selectedKey={tabKey}
100
86
  >
101
87
  {children}
102
88
  </Tabs>
@@ -104,6 +90,11 @@ export function OpenAPITabs(
104
90
  );
105
91
  }
106
92
 
93
+ const defer = (fn: () => void) => {
94
+ const id = setTimeout(fn, 0);
95
+ return () => clearTimeout(id);
96
+ };
97
+
107
98
  /**
108
99
  * The OpenAPI Tabs list component.
109
100
  * This component should be used as a child of the OpenAPITabs component.
@@ -116,14 +107,14 @@ export function OpenAPITabsList() {
116
107
  <TabList className="openapi-tabs-list">
117
108
  {items.map((tab) => (
118
109
  <Tab
110
+ key={tab.key}
111
+ id={tab.key}
119
112
  style={({ isFocusVisible }) => ({
120
113
  outline: isFocusVisible
121
114
  ? '2px solid rgb(var(--primary-color-500)/0.4)'
122
115
  : 'none',
123
116
  })}
124
117
  className="openapi-tabs-tab"
125
- key={`Tab-${tab.key}`}
126
- id={tab.key}
127
118
  >
128
119
  {tab.label}
129
120
  </Tab>
@@ -144,12 +135,10 @@ export function OpenAPITabsPanels() {
144
135
  return null;
145
136
  }
146
137
 
138
+ const key = selectedTab.key.toString();
139
+
147
140
  return (
148
- <TabPanel
149
- key={`TabPanel-${selectedTab.key}`}
150
- id={selectedTab.key.toString()}
151
- className="openapi-tabs-panel"
152
- >
141
+ <TabPanel key={key} id={key} className="openapi-tabs-panel">
153
142
  {selectedTab.body}
154
143
  {selectedTab.description ? (
155
144
  <Markdown source={selectedTab.description} className="openapi-tabs-footer" />
@@ -0,0 +1,59 @@
1
+ import clsx from 'clsx';
2
+ import { type ComponentPropsWithoutRef, forwardRef } from 'react';
3
+
4
+ export function Section(props: ComponentPropsWithoutRef<'div'>) {
5
+ return <div {...props} className={clsx('openapi-section', props.className)} />;
6
+ }
7
+
8
+ export function SectionHeader(props: ComponentPropsWithoutRef<'div'>) {
9
+ return (
10
+ <div
11
+ {...props}
12
+ className={clsx(
13
+ 'openapi-section-header',
14
+ props.className && `${props.className}-header`
15
+ )}
16
+ />
17
+ );
18
+ }
19
+
20
+ export function SectionHeaderContent(props: ComponentPropsWithoutRef<'div'>) {
21
+ return (
22
+ <div
23
+ {...props}
24
+ className={clsx(
25
+ 'openapi-section-header-content',
26
+ props.className && `${props.className}-header-content`
27
+ )}
28
+ />
29
+ );
30
+ }
31
+
32
+ export const SectionBody = forwardRef(function SectionBody(
33
+ props: ComponentPropsWithoutRef<'div'>,
34
+ ref: React.ForwardedRef<HTMLDivElement>
35
+ ) {
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ {...props}
40
+ className={clsx('openapi-section-body', props.className && `${props.className}-body`)}
41
+ />
42
+ );
43
+ });
44
+
45
+ export function StaticSection(props: {
46
+ className: string;
47
+ header: React.ReactNode;
48
+ children: React.ReactNode;
49
+ }) {
50
+ const { className, header, children } = props;
51
+ return (
52
+ <Section className={className}>
53
+ <SectionHeader className={className}>
54
+ <SectionHeaderContent className={className}>{header}</SectionHeaderContent>
55
+ </SectionHeader>
56
+ <SectionBody className={className}>{children}</SectionBody>
57
+ </Section>
58
+ );
59
+ }
@@ -1,23 +1,35 @@
1
1
  'use client';
2
2
 
3
- import { create } from 'zustand';
4
-
5
- interface SyncedTabsState<T> {
6
- tabs: Map<string, T>;
7
- setTabs: (updater: (tabs: Map<string, T>) => Map<string, T>) => void;
8
- }
9
-
10
- const useSyncedTabsStore = create<SyncedTabsState<any>>()((set) => ({
11
- tabs: new Map<string, any>(),
12
- setTabs: (updater) =>
13
- set((state) => ({
14
- tabs: updater(new Map(state.tabs)), // Ensure a new Map is created for reactivity
15
- })),
16
- }));
17
-
18
- // Selector for better performance - only re-renders when tabs change
19
- export function useSyncedTabsGlobalState<T>() {
20
- const tabs = useSyncedTabsStore((state) => state.tabs as Map<string, T>);
21
- const setTabs = useSyncedTabsStore((state) => state.setTabs as SyncedTabsState<T>['setTabs']);
22
- return [tabs, setTabs] as const;
23
- }
3
+ import { createStore } from 'zustand';
4
+
5
+ type Key = string | number;
6
+
7
+ type TabState = {
8
+ tabKey: Key | null;
9
+ };
10
+
11
+ type TabActions = { setTabKey: (tab: Key | null) => void };
12
+
13
+ type TabStore = TabState & TabActions;
14
+
15
+ const createTabStore = (initialTab?: Key) => {
16
+ return createStore<TabStore>()((set) => ({
17
+ tabKey: initialTab ?? null,
18
+ setTabKey: (tabKey) => {
19
+ set(() => ({ tabKey }));
20
+ },
21
+ }));
22
+ };
23
+
24
+ const defaultTabStores = new Map<string, ReturnType<typeof createTabStore>>();
25
+
26
+ const createTabStoreFactory = (stores: typeof defaultTabStores) => {
27
+ return (storeKey: string, initialKey?: Key) => {
28
+ if (!stores.has(storeKey)) {
29
+ stores.set(storeKey, createTabStore(initialKey));
30
+ }
31
+ return stores.get(storeKey)!;
32
+ };
33
+ };
34
+
35
+ export const getOrCreateTabStoreByKey = createTabStoreFactory(defaultTabStores);