@scalar/api-client-react 1.4.20 → 2.0.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @scalar/api-client-react
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#8760](https://github.com/scalar/scalar/pull/8760): feat: update to use api-client v2 with new hook based api
8
+
3
9
  ## 1.4.20
4
10
 
5
11
  ## 1.4.19
package/README.md CHANGED
@@ -11,34 +11,57 @@
11
11
  npm install @scalar/api-client-react
12
12
  ```
13
13
 
14
+ ## ⚠️ Breaking Changes
15
+
16
+ We have updated the API for the client! Please see the new hook based usage below.
17
+
14
18
  ## Usage
15
19
 
16
- First we need to add the provider, you should add it in the highest place you have a unique spec.
20
+ Call `useApiClient()` from any component. No provider is needed.
17
21
 
18
- ```tsx
19
- import { ApiClientModalProvider } from '@scalar/api-client-react'
22
+ The Vue app is created once and appended to `document.body` where it lives for the lifetime of the
23
+ page it survives client-side navigation without losing state.
20
24
 
25
+ The code is ESM lazy loaded with the dynamic import function to make the smallest possible initial bundle size. We also
26
+ handle de-duplication of documents so as long as the URL is the same you will only get one.
27
+
28
+ ```tsx
29
+ import { useApiClient } from '@scalar/api-client-react'
21
30
  import '@scalar/api-client-react/style.css'
22
- ;<ApiClientModalProvider
23
- configuration={{
24
- url: 'https://registry.scalar.com/@scalar/apis/galaxy?format=json',
25
- }}>
26
- {children}
27
- </ApiClientModalProvider>
31
+
32
+ export const OpenButton = () => {
33
+ const client = useApiClient({
34
+ configuration: {
35
+ url: 'https://registry.scalar.com/@scalar/apis/galaxy?format=json',
36
+ },
37
+ })
38
+
39
+ return (
40
+ <button onClick={() => client?.open({ path: '/planets', method: 'get' })}>
41
+ Open API Client
42
+ </button>
43
+ )
44
+ }
28
45
  ```
29
46
 
30
- Then you can trigger it from anywhere inside of that provider by calling the `useApiClientModal()`
47
+ ### Options
31
48
 
32
- ```tsx
33
- import { useApiClientModal } from '@scalar/api-client-react'
49
+ | Option | Type | Description |
50
+ |---|---|---|
51
+ | `configuration.url` | `string` | URL of an OpenAPI document to load |
52
+ | `configuration.content` | `Record<string, unknown>` | Inline OpenAPI document object |
53
+
54
+ ### Routing to a specific request
34
55
 
35
- const client = useApiClientModal()
56
+ Pass a `RoutePayload` to `client.open()` to navigate directly to a specific endpoint:
57
+
58
+ ```tsx
59
+ const client = useApiClient({
60
+ configuration: { url: '...' },
61
+ })
36
62
 
37
- return (
38
- <button onClick={() => client?.open({ path: '/auth/token', method: 'get' })}>
39
- Click me to open the Api Client
40
- </button>
41
- )
63
+ // Open to a specific request
64
+ client?.open({ path: '/auth/token', method: 'post' })
42
65
  ```
43
66
 
44
67
  Check out the playground for a working example.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export type { OpenClientPayload } from './ApiClientModalProvider';
2
- export { ApiClientModalProvider, useApiClientModal } from './ApiClientModalProvider';
1
+ export type { RoutePayload } from '@scalar/api-client/v2/features/modal';
2
+ export type { ApiClientConfigurationReact, UseApiClientModalProps } from './use-api-client';
3
+ export { useApiClient } from './use-api-client';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AAExE,YAAY,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -1,115 +1,136 @@
1
1
  "use client";
2
- import { createContext, useContext, useEffect, useRef, useSyncExternalStore } from "react";
3
- import { jsx, jsxs } from "react/jsx-runtime";
4
- //#region src/client-store.ts
5
- globalThis.__VUE_OPTIONS_API__ = true;
6
- globalThis.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true;
7
- globalThis.__VUE_PROD_DEVTOOLS__ = false;
8
- /** Client state */
9
- var state = {
10
- createClient: null,
11
- clientDict: {}
12
- };
13
- /** Set of listener functions to be called when state changes */
14
- var listeners = /* @__PURE__ */ new Set();
15
- /** Subscribe to state changes */
16
- var subscribe = (listener) => {
17
- listeners.add(listener);
18
- return () => listeners.delete(listener);
19
- };
20
- /** Get the current state at this moment in time */
21
- var getSnapshot = () => state;
22
- /** Trigger all listeners */
23
- var emit = () => listeners.forEach((listener) => listener());
24
- /** Set the create client state */
25
- var setCreateClient = (clientFactory) => {
26
- state = {
27
- ...state,
28
- createClient: clientFactory
29
- };
30
- emit();
31
- };
32
- /** Add a client to the client dict */
33
- var addClient = (url, client) => {
34
- state = {
35
- ...state,
36
- clientDict: {
37
- ...state.clientDict,
38
- [url]: client
39
- }
40
- };
41
- emit();
42
- };
43
- /** Remove a client from the client dict */
44
- var removeClient = (url) => {
45
- const { [url]: _, ...clientDict } = state.clientDict;
46
- state = {
47
- ...state,
48
- clientDict
2
+ import { useEffect, useState } from "react";
3
+ //#region src/lazy-load.ts
4
+ /**
5
+ * Creates a lazy singleton getter: the factory runs at most once, caches the resulting promise,
6
+ * and clears the cache on failure so the next call can retry.
7
+ *
8
+ * Optional args are forwarded to the factory on the first (cache-miss) call only.
9
+ * Subsequent calls return the cached promise regardless of the args passed.
10
+ */
11
+ var makeLazySingleton = (factory) => {
12
+ let cached;
13
+ return (...args) => {
14
+ cached ??= factory(...args).catch((error) => {
15
+ cached = void 0;
16
+ throw error;
17
+ });
18
+ return cached;
49
19
  };
50
- emit();
51
- };
52
- var clientStore = {
53
- getSnapshot,
54
- subscribe,
55
- setCreateClient,
56
- addClient,
57
- removeClient
58
20
  };
21
+ /** Lazy load the client modal creator */
22
+ var getClientModalCreator = makeLazySingleton(() => import("@scalar/api-client/v2/features/modal").then(({ createApiClientModal }) => createApiClientModal));
23
+ /** Module-scoped singleton workspace store (lazy-loaded on first use). */
24
+ var getWorkspaceStoreSingleton = makeLazySingleton(() => import("@scalar/workspace-store/client").then(({ createWorkspaceStore }) => createWorkspaceStore()));
25
+ /** Module-scoped singleton workspace event bus (lazy-loaded on first use). */
26
+ var getWorkspaceEventBusSingleton = makeLazySingleton(() => import("@scalar/workspace-store/events").then(({ createWorkspaceEventBus }) => createWorkspaceEventBus()));
27
+ /**
28
+ * Lazily creates the singleton Vue app, mounts it as the last child of document.body,
29
+ * and returns the controller. Subsequent calls return the same promise.
30
+ *
31
+ * Only modal-level options (e.g. `proxyUrl`) are accepted here. Document-specific fields
32
+ * (`url`, `content`) must be registered via `workspaceStore.addDocument` after the client
33
+ * is ready — they are not part of the modal constructor.
34
+ */
35
+ var getOrCreateApiClient = makeLazySingleton(async (options = {}) => {
36
+ const el = document.createElement("div");
37
+ el.className = "scalar-app";
38
+ document.body.appendChild(el);
39
+ try {
40
+ const [createModal, workspaceStore, eventBus] = await Promise.all([
41
+ getClientModalCreator(),
42
+ getWorkspaceStoreSingleton(),
43
+ getWorkspaceEventBusSingleton()
44
+ ]);
45
+ return {
46
+ apiClient: createModal({
47
+ el,
48
+ eventBus,
49
+ workspaceStore,
50
+ options
51
+ }),
52
+ workspaceStore
53
+ };
54
+ } catch (error) {
55
+ el.remove();
56
+ throw error;
57
+ }
58
+ });
59
59
  //#endregion
60
- //#region src/ApiClientModalProvider.tsx
61
- var ApiClientModalContext = createContext(null);
62
- /** Ensures we only load createClient once */
63
- var isLoading = false;
64
- /** Hack: this is strictly to prevent creation of extra clients as the store lags a bit */
65
- var clientDict = {};
60
+ //#region src/use-api-client.ts
61
+ globalThis.__VUE_OPTIONS_API__ = true;
62
+ globalThis.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true;
63
+ globalThis.__VUE_PROD_DEVTOOLS__ = false;
64
+ /** Tracks which documents are/have been loaded so we dont duplicate */
65
+ var documentDict = {};
66
66
  /**
67
- * Api Client Modal React
67
+ * Returns the singleton Api Client
68
+ *
69
+ * On first call the Vue app is lazily created and appended to document.body where it
70
+ * lives for the lifetime of the page — it is never unmounted, so it survives client-side
71
+ * navigation without losing state.
68
72
  *
69
- * Provider which mounts the Scalar Api Client Modal vue app.
70
- * Rebuilt to support multiple instances when using a unique spec.url
73
+ * Subsequent calls from any component share the same instance of the client but can use the same
74
+ * or different documents
71
75
  */
72
- var ApiClientModalProvider = ({ children, initialRequest, configuration = {} }) => {
73
- const key = configuration.spec?.url || "default";
74
- const el = useRef(null);
75
- const state = useSyncExternalStore(clientStore.subscribe, clientStore.getSnapshot, clientStore.getSnapshot);
76
+ var useApiClient = ({ configuration } = {}) => {
77
+ const [client, setClient] = useState(void 0);
78
+ const [workspaceStore, setWorkspaceStore] = useState(void 0);
79
+ const [documentSlug, setDocumentSlug] = useState("");
80
+ /** Small wrapper to set the documentSlug */
81
+ const open = (payload) => client?.open({
82
+ documentSlug,
83
+ ...payload
84
+ });
76
85
  useEffect(() => {
77
- const loadApiClientJs = async () => {
78
- isLoading = true;
79
- const { createApiClientModal } = await import("@scalar/api-client/layouts/Modal");
80
- clientStore.setCreateClient(createApiClientModal);
86
+ let cancelled = false;
87
+ const { url, content, ...modalOptions } = configuration ?? {};
88
+ getOrCreateApiClient(modalOptions)?.then((_client) => {
89
+ if (cancelled || !_client) return;
90
+ const slug = url || content?.info?.title || "";
91
+ setClient(_client.apiClient);
92
+ setWorkspaceStore(_client.workspaceStore);
93
+ setDocumentSlug(slug);
94
+ if (slug && !documentDict[slug]) {
95
+ documentDict[slug] = true;
96
+ _client.workspaceStore.addDocument(content ? {
97
+ name: slug,
98
+ document: content
99
+ } : {
100
+ name: slug,
101
+ url: url ?? ""
102
+ });
103
+ }
104
+ });
105
+ return () => {
106
+ cancelled = true;
81
107
  };
82
- if (!isLoading) loadApiClientJs();
83
108
  }, []);
84
109
  useEffect(() => {
85
- if (!el.current || !state.createClient || clientDict[key]) return () => null;
86
- const { client: _client } = state.createClient({
87
- el: el.current,
88
- configuration
110
+ if (!client || !configuration || !workspaceStore) return;
111
+ const slug = configuration.url || configuration.content?.info?.title || "default";
112
+ setDocumentSlug(slug);
113
+ if (documentDict[slug]) return;
114
+ documentDict[slug] = true;
115
+ workspaceStore.addDocument(configuration.content ? {
116
+ name: slug,
117
+ document: configuration.content
118
+ } : {
119
+ name: slug,
120
+ url: configuration.url ?? ""
89
121
  });
90
- const updateConfig = async () => {
91
- await _client.updateConfig(configuration);
92
- if (initialRequest) _client.route(initialRequest);
93
- };
94
- clientStore.addClient(key, _client);
95
- clientDict[key] = _client;
96
- updateConfig();
97
- return () => {
98
- _client.app.unmount();
99
- clientStore.removeClient(key);
100
- delete clientDict[key];
101
- };
102
- }, [el.current, state.createClient]);
103
- return /* @__PURE__ */ jsxs(ApiClientModalContext.Provider, {
104
- value: state.clientDict[key] ?? null,
105
- children: [/* @__PURE__ */ jsx("div", {
106
- className: "scalar-app",
107
- ref: el
108
- }), children]
109
- });
122
+ }, [
123
+ client,
124
+ configuration?.url,
125
+ configuration?.content,
126
+ workspaceStore
127
+ ]);
128
+ return client ? {
129
+ ...client,
130
+ open
131
+ } : void 0;
110
132
  };
111
- var useApiClientModal = () => useContext(ApiClientModalContext);
112
133
  //#endregion
113
- export { ApiClientModalProvider, useApiClientModal };
134
+ export { useApiClient };
114
135
 
115
136
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/client-store.ts","../src/ApiClientModalProvider.tsx"],"sourcesContent":["import type { ApiClient, createApiClientModal } from '@scalar/api-client/layouts/Modal'\n\n// These are required for the vue bundler version\nglobalThis.__VUE_OPTIONS_API__ = true\nglobalThis.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true\nglobalThis.__VUE_PROD_DEVTOOLS__ = false\n\n/** Client state */\nlet state = {\n createClient: null as typeof createApiClientModal | null,\n clientDict: {} as Record<string, ApiClient>,\n}\n\n/** Set of listener functions to be called when state changes */\nconst listeners = new Set<() => void>()\n\n/** Subscribe to state changes */\nconst subscribe = (listener: () => void) => {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\n/** Get the current state at this moment in time */\nconst getSnapshot = () => state\n\n/** Trigger all listeners */\nconst emit = () => listeners.forEach((listener) => listener())\n\n/** Set the create client state */\nconst setCreateClient = (clientFactory: typeof createApiClientModal) => {\n state = { ...state, createClient: clientFactory }\n emit()\n}\n\n/** Add a client to the client dict */\nconst addClient = (url: string, client: ApiClient) => {\n state = { ...state, clientDict: { ...state.clientDict, [url]: client } }\n emit()\n}\n\n/** Remove a client from the client dict */\nconst removeClient = (url: string) => {\n const { [url]: _, ...clientDict } = state.clientDict\n state = { ...state, clientDict }\n emit()\n}\n\nexport const clientStore = {\n getSnapshot,\n subscribe,\n setCreateClient,\n addClient,\n removeClient,\n}\n","'use client'\n\nimport type { ApiClient } from '@scalar/api-client/layouts/Modal'\nimport type { OpenClientPayload } from '@scalar/api-client/libs'\nimport type { ApiClientConfiguration } from '@scalar/types/api-reference'\nimport type { PropsWithChildren } from 'react'\nimport { createContext, useContext, useEffect, useRef, useSyncExternalStore } from 'react'\n\nexport type { OpenClientPayload }\n\nimport { clientStore } from './client-store'\n\nimport './style.css'\n\nconst ApiClientModalContext = createContext<ApiClient | null>(null)\n\ntype Props = PropsWithChildren<{\n /** Choose a request to initially route to */\n initialRequest?: OpenClientPayload\n /** Configuration for the Api Client */\n configuration?: Partial<ApiClientConfiguration>\n}>\n\n/** Ensures we only load createClient once */\nlet isLoading = false\n\n/** Hack: this is strictly to prevent creation of extra clients as the store lags a bit */\nconst clientDict: Record<string, ApiClient> = {}\n\n/**\n * Api Client Modal React\n *\n * Provider which mounts the Scalar Api Client Modal vue app.\n * Rebuilt to support multiple instances when using a unique spec.url\n */\nexport const ApiClientModalProvider = ({ children, initialRequest, configuration = {} }: Props) => {\n const key = configuration.spec?.url || 'default'\n const el = useRef<HTMLDivElement | null>(null)\n\n const state = useSyncExternalStore(clientStore.subscribe, clientStore.getSnapshot, clientStore.getSnapshot)\n\n // Lazyload the js to create the client, but we only wanna call this once\n useEffect(() => {\n const loadApiClientJs = async () => {\n isLoading = true\n const { createApiClientModal } = await import('@scalar/api-client/layouts/Modal')\n clientStore.setCreateClient(createApiClientModal)\n }\n if (!isLoading) {\n void loadApiClientJs()\n }\n }, [])\n\n useEffect(() => {\n if (!el.current || !state.createClient || clientDict[key]) {\n return () => null\n }\n\n // Check for cached client first\n const { client: _client } = state.createClient({\n el: el.current,\n configuration,\n })\n\n const updateConfig = async () => {\n await _client.updateConfig(configuration!)\n if (initialRequest) {\n _client.route(initialRequest)\n }\n }\n\n // Add the client to the store and dict\n clientStore.addClient(key, _client)\n clientDict[key] = _client\n\n // We update the config as we are using the sync version\n void updateConfig()\n\n // Ensure we unmount the vue app on unmount\n return () => {\n _client.app.unmount()\n clientStore.removeClient(key)\n delete clientDict[key]\n }\n }, [el.current, state.createClient])\n\n return (\n <ApiClientModalContext.Provider value={state.clientDict[key] ?? null}>\n <div\n className=\"scalar-app\"\n ref={el}\n />\n {children}\n </ApiClientModalContext.Provider>\n )\n}\n\nexport const useApiClientModal = (): ApiClient | null => useContext(ApiClientModalContext)\n"],"mappings":";;;;AAGA,WAAW,sBAAsB;AACjC,WAAW,0CAA0C;AACrD,WAAW,wBAAwB;;AAGnC,IAAI,QAAQ;CACV,cAAc;CACd,YAAY,EAAA;CACb;;AAGD,IAAM,4BAAY,IAAI,KAAiB;;AAGvC,IAAM,aAAa,aAAyB;AAC1C,WAAU,IAAI,SAAS;AACvB,cAAa,UAAU,OAAO,SAAS;;;AAIzC,IAAM,oBAAoB;;AAG1B,IAAM,aAAa,UAAU,SAAS,aAAa,UAAU,CAAC;;AAG9D,IAAM,mBAAmB,kBAA+C;AACtE,SAAQ;EAAE,GAAG;EAAO,cAAc;EAAe;AACjD,OAAM;;;AAIR,IAAM,aAAa,KAAa,WAAsB;AACpD,SAAQ;EAAE,GAAG;EAAO,YAAY;GAAE,GAAG,MAAM;IAAa,MAAM;;EAAU;AACxE,OAAM;;;AAIR,IAAM,gBAAgB,QAAgB;CACpC,MAAM,GAAG,MAAM,GAAG,GAAG,eAAe,MAAM;AAC1C,SAAQ;EAAE,GAAG;EAAO;EAAY;AAChC,OAAM;;AAGR,IAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACD;;;ACvCD,IAAM,wBAAwB,cAAgC,KAAK;;AAUnE,IAAI,YAAY;;AAGhB,IAAM,aAAwC,EAAE;;;;;;;AAQhD,IAAa,0BAA0B,EAAE,UAAU,gBAAgB,gBAAgB,EAAE,OAAc;CACjG,MAAM,MAAM,cAAc,MAAM,OAAO;CACvC,MAAM,KAAK,OAA8B,KAAK;CAE9C,MAAM,QAAQ,qBAAqB,YAAY,WAAW,YAAY,aAAa,YAAY,YAAY;AAG3G,iBAAgB;EACd,MAAM,kBAAkB,YAAY;AAClC,eAAY;GACZ,MAAM,EAAE,yBAAyB,MAAM,OAAO;AAC9C,eAAY,gBAAgB,qBAAqB;;AAEnD,MAAI,CAAC,UACE,kBAAiB;IAEvB,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,CAAC,GAAG,WAAW,CAAC,MAAM,gBAAgB,WAAW,KACnD,cAAa;EAIf,MAAM,EAAE,QAAQ,YAAY,MAAM,aAAa;GAC7C,IAAI,GAAG;GACP;GACD,CAAC;EAEF,MAAM,eAAe,YAAY;AAC/B,SAAM,QAAQ,aAAa,cAAe;AAC1C,OAAI,eACF,SAAQ,MAAM,eAAe;;AAKjC,cAAY,UAAU,KAAK,QAAQ;AACnC,aAAW,OAAO;AAGb,gBAAc;AAGnB,eAAa;AACX,WAAQ,IAAI,SAAS;AACrB,eAAY,aAAa,IAAI;AAC7B,UAAO,WAAW;;IAEnB,CAAC,GAAG,SAAS,MAAM,aAAa,CAAC;AAEpC,QACE,qBAAC,sBAAsB,UAAvB;EAAgC,OAAO,MAAM,WAAW,QAAQ;YAAhE,CACE,oBAAC,OAAD;GACE,WAAU;GACV,KAAK;GACL,CAAA,EACD,SAAA;;;AAKP,IAAa,0BAA4C,WAAW,sBAAsB"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/lazy-load.ts","../src/use-api-client.ts"],"sourcesContent":["import type { ApiClientConfiguration } from '@scalar/types/api-reference'\n\n/**\n * Creates a lazy singleton getter: the factory runs at most once, caches the resulting promise,\n * and clears the cache on failure so the next call can retry.\n *\n * Optional args are forwarded to the factory on the first (cache-miss) call only.\n * Subsequent calls return the cached promise regardless of the args passed.\n */\nconst makeLazySingleton = <T, Args extends unknown[] = []>(\n factory: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let cached: Promise<T> | undefined\n return (...args: Args) => {\n cached ??= factory(...args).catch((error) => {\n cached = undefined\n throw error\n })\n return cached\n }\n}\n\n/** Lazy load the client modal creator */\nexport const getClientModalCreator = makeLazySingleton(() =>\n import('@scalar/api-client/v2/features/modal').then(({ createApiClientModal }) => createApiClientModal),\n)\n\n/** Module-scoped singleton workspace store (lazy-loaded on first use). */\nexport const getWorkspaceStoreSingleton = makeLazySingleton(() =>\n import('@scalar/workspace-store/client').then(({ createWorkspaceStore }) => createWorkspaceStore()),\n)\n\n/** Module-scoped singleton workspace event bus (lazy-loaded on first use). */\nexport const getWorkspaceEventBusSingleton = makeLazySingleton(() =>\n import('@scalar/workspace-store/events').then(({ createWorkspaceEventBus }) => createWorkspaceEventBus()),\n)\n\n/**\n * Lazily creates the singleton Vue app, mounts it as the last child of document.body,\n * and returns the controller. Subsequent calls return the same promise.\n *\n * Only modal-level options (e.g. `proxyUrl`) are accepted here. Document-specific fields\n * (`url`, `content`) must be registered via `workspaceStore.addDocument` after the client\n * is ready they are not part of the modal constructor.\n */\nexport const getOrCreateApiClient = makeLazySingleton(async (options: Partial<ApiClientConfiguration> = {}) => {\n const el = document.createElement('div')\n el.className = 'scalar-app'\n document.body.appendChild(el)\n\n try {\n const [createModal, workspaceStore, eventBus] = await Promise.all([\n getClientModalCreator(),\n getWorkspaceStoreSingleton(),\n getWorkspaceEventBusSingleton(),\n ])\n\n const apiClient = createModal({\n el,\n eventBus,\n workspaceStore,\n options,\n // TODO: map plugins from configuration when available\n // plugins: mapConfigPlugins(options),\n })\n\n return { apiClient, workspaceStore }\n } catch (error) {\n el.remove()\n throw error\n }\n})\n","'use client'\n\nimport type { ApiClientModal, RoutePayload } from '@scalar/api-client/v2/features/modal'\nimport type { ApiClientConfiguration } from '@scalar/types/api-reference'\nimport { useEffect, useState } from 'react'\n\nimport './style.css'\n\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { getOrCreateApiClient } from './lazy-load'\n\nglobalThis.__VUE_OPTIONS_API__ = true\nglobalThis.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true\nglobalThis.__VUE_PROD_DEVTOOLS__ = false\n\n/** We don't really need all of the content types so we just accept an object instead */\nexport type ApiClientConfigurationReact = Partial<\n Omit<ApiClientConfiguration, 'content' | 'url'> & { content?: Record<string, unknown>; url?: string }\n>\n\nexport type UseApiClientModalProps = {\n /** Configuration for the Api Client (url or inline content) */\n configuration?: ApiClientConfigurationReact\n}\n\n/** Tracks which documents are/have been loaded so we dont duplicate */\nconst documentDict: Record<string, true> = {}\n\n/**\n * Returns the singleton Api Client\n *\n * On first call the Vue app is lazily created and appended to document.body where it\n * lives for the lifetime of the page it is never unmounted, so it survives client-side\n * navigation without losing state.\n *\n * Subsequent calls from any component share the same instance of the client but can use the same\n * or different documents\n */\nexport const useApiClient = ({\n configuration,\n}: UseApiClientModalProps = {}):\n | (Omit<ApiClientModal, 'open'> & { open: (payload: RoutePayload) => void })\n | undefined => {\n const [client, setClient] = useState<ApiClientModal | undefined>(undefined)\n const [workspaceStore, setWorkspaceStore] = useState<WorkspaceStore | undefined>(undefined)\n const [documentSlug, setDocumentSlug] = useState('')\n\n /** Small wrapper to set the documentSlug */\n const open = (payload: RoutePayload) => client?.open({ documentSlug, ...payload })\n\n useEffect(() => {\n let cancelled = false\n\n // Strip document-specific fields before passing to the modal constructor.\n // `url` and `content` are registered separately via workspaceStore.addDocument.\n const { url, content, ...modalOptions } = configuration ?? {}\n\n void getOrCreateApiClient(modalOptions)?.then((_client) => {\n if (cancelled || !_client) {\n return\n }\n\n // Compute the slug here so we can batch all three state updates into one render,\n // preventing a render where `client` is set but `documentSlug` is still ''.\n const slug = url || (content as { info?: { title?: string } })?.info?.title || ''\n\n setClient(_client.apiClient)\n setWorkspaceStore(_client.workspaceStore)\n setDocumentSlug(slug)\n\n if (slug && !documentDict[slug]) {\n documentDict[slug] = true\n void _client.workspaceStore.addDocument(\n content ? { name: slug, document: content } : { name: slug, url: url ?? '' },\n )\n }\n })\n\n return () => {\n cancelled = true\n }\n\n // Only run once per mount\n }, [])\n\n // When url or content changes after the client is already mounted, register the new document\n useEffect(() => {\n if (!client || !configuration || !workspaceStore) {\n return\n }\n\n const slug = configuration.url || (configuration.content as { info?: { title?: string } })?.info?.title || 'default'\n setDocumentSlug(slug)\n\n if (documentDict[slug]) {\n return\n }\n documentDict[slug] = true\n\n void workspaceStore.addDocument(\n configuration.content\n ? { name: slug, document: configuration.content }\n : { name: slug, url: configuration.url ?? '' },\n )\n }, [client, configuration?.url, configuration?.content, workspaceStore])\n\n return client\n ? {\n ...client,\n open,\n }\n : undefined\n}\n"],"mappings":";;;;;;;;;;AASA,IAAM,qBACJ,YACoC;CACpC,IAAI;AACJ,SAAQ,GAAG,SAAe;AACxB,aAAW,QAAQ,GAAG,KAAK,CAAC,OAAO,UAAU;AAC3C,YAAS,KAAA;AACT,SAAM;IACN;AACF,SAAO;;;;AAKX,IAAa,wBAAwB,wBACnC,OAAO,wCAAwC,MAAM,EAAE,2BAA2B,qBAAqB,CACxG;;AAGD,IAAa,6BAA6B,wBACxC,OAAO,kCAAkC,MAAM,EAAE,2BAA2B,sBAAsB,CAAC,CACpG;;AAGD,IAAa,gCAAgC,wBAC3C,OAAO,kCAAkC,MAAM,EAAE,8BAA8B,yBAAyB,CAAC,CAC1G;;;;;;;;;AAUD,IAAa,uBAAuB,kBAAkB,OAAO,UAA2C,EAAE,KAAK;CAC7G,MAAM,KAAK,SAAS,cAAc,MAAM;AACxC,IAAG,YAAY;AACf,UAAS,KAAK,YAAY,GAAG;AAE7B,KAAI;EACF,MAAM,CAAC,aAAa,gBAAgB,YAAY,MAAM,QAAQ,IAAI;GAChE,uBAAuB;GACvB,4BAA4B;GAC5B,+BAAA;GACD,CAAC;AAWF,SAAO;GAAE,WATS,YAAY;IAC5B;IACA;IACA;IACA;IAGD,CAAC;GAEkB;GAAgB;UAC7B,OAAO;AACd,KAAG,QAAQ;AACX,QAAM;;EAER;;;AC3DF,WAAW,sBAAsB;AACjC,WAAW,0CAA0C;AACrD,WAAW,wBAAwB;;AAanC,IAAM,eAAqC,EAAE;;;;;;;;;;;AAY7C,IAAa,gBAAgB,EAC3B,kBAC0B,EAAE,KAEb;CACf,MAAM,CAAC,QAAQ,aAAa,SAAqC,KAAA,EAAU;CAC3E,MAAM,CAAC,gBAAgB,qBAAqB,SAAqC,KAAA,EAAU;CAC3F,MAAM,CAAC,cAAc,mBAAmB,SAAS,GAAG;;CAGpD,MAAM,QAAQ,YAA0B,QAAQ,KAAK;EAAE;EAAc,GAAG;EAAS,CAAC;AAElF,iBAAgB;EACd,IAAI,YAAY;EAIhB,MAAM,EAAE,KAAK,SAAS,GAAG,iBAAiB,iBAAiB,EAAE;AAExD,uBAAqB,aAAa,EAAE,MAAM,YAAY;AACzD,OAAI,aAAa,CAAC,QAChB;GAKF,MAAM,OAAO,OAAQ,SAA2C,MAAM,SAAS;AAE/E,aAAU,QAAQ,UAAU;AAC5B,qBAAkB,QAAQ,eAAe;AACzC,mBAAgB,KAAK;AAErB,OAAI,QAAQ,CAAC,aAAa,OAAO;AAC/B,iBAAa,QAAQ;AAChB,YAAQ,eAAe,YAC1B,UAAU;KAAE,MAAM;KAAM,UAAU;KAAS,GAAG;KAAE,MAAM;KAAM,KAAK,OAAO;KAAI,CAC7E;;IAEH;AAEF,eAAa;AACX,eAAY;;IAIb,EAAE,CAAC;AAGN,iBAAgB;AACd,MAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,eAChC;EAGF,MAAM,OAAO,cAAc,OAAQ,cAAc,SAA2C,MAAM,SAAS;AAC3G,kBAAgB,KAAK;AAErB,MAAI,aAAa,MACf;AAEF,eAAa,QAAQ;AAEhB,iBAAe,YAClB,cAAc,UACV;GAAE,MAAM;GAAM,UAAU,cAAc;GAAS,GAC/C;GAAE,MAAM;GAAM,KAAK,cAAc,OAAO;GAAI,CACjD;IACA;EAAC;EAAQ,eAAe;EAAK,eAAe;EAAS;EAAe,CAAC;AAExE,QAAO,SACH;EACE,GAAG;EACH;EACD,GACD,KAAA"}
@@ -0,0 +1,87 @@
1
+ /** Lazy load the client modal creator */
2
+ export declare const getClientModalCreator: () => Promise<({ el, eventBus, mountOnInitialize, plugins, workspaceStore, options, }: {
3
+ el: HTMLElement | null;
4
+ mountOnInitialize?: boolean;
5
+ eventBus?: import("@scalar/workspace-store/events").WorkspaceEventBus;
6
+ workspaceStore: import("@scalar/workspace-store/client").WorkspaceStore;
7
+ plugins?: import("@scalar/oas-utils/helpers").ClientPlugin[];
8
+ options?: import("vue").MaybeRefOrGetter<Partial<Pick<import("@scalar/types/api-reference").ApiReferenceConfigurationRaw, "authentication" | "baseServerURL" | "hideClientButton" | "servers" | "hiddenClients">>>;
9
+ }) => import("@scalar/api-client/v2/features/modal").ApiClientModal>;
10
+ /** Module-scoped singleton workspace store (lazy-loaded on first use). */
11
+ export declare const getWorkspaceStoreSingleton: () => Promise<import("@scalar/workspace-store/client").WorkspaceStore>;
12
+ /** Module-scoped singleton workspace event bus (lazy-loaded on first use). */
13
+ export declare const getWorkspaceEventBusSingleton: () => Promise<import("@scalar/workspace-store/events").WorkspaceEventBus>;
14
+ /**
15
+ * Lazily creates the singleton Vue app, mounts it as the last child of document.body,
16
+ * and returns the controller. Subsequent calls return the same promise.
17
+ *
18
+ * Only modal-level options (e.g. `proxyUrl`) are accepted here. Document-specific fields
19
+ * (`url`, `content`) must be registered via `workspaceStore.addDocument` after the client
20
+ * is ready — they are not part of the modal constructor.
21
+ */
22
+ export declare const getOrCreateApiClient: (options?: Partial<{
23
+ hideClientButton: boolean;
24
+ showSidebar: boolean;
25
+ showDeveloperTools: "never" | "always" | "localhost";
26
+ showToolbar: "never" | "always" | "localhost";
27
+ operationTitleSource: "summary" | "path";
28
+ theme: "default" | "alternate" | "moon" | "purple" | "solarized" | "bluePlanet" | "deepSpace" | "saturn" | "kepler" | "elysiajs" | "fastify" | "mars" | "laserwave" | "none";
29
+ persistAuth: boolean;
30
+ telemetry: boolean;
31
+ externalUrls: {
32
+ dashboardUrl: string;
33
+ registryUrl: string;
34
+ proxyUrl: string;
35
+ apiBaseUrl: string;
36
+ };
37
+ authentication?: any;
38
+ baseServerURL?: string | undefined;
39
+ proxyUrl?: string | undefined;
40
+ oauth2RedirectUri?: string | undefined;
41
+ searchHotKey?: "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | undefined;
42
+ servers?: any[] | undefined;
43
+ _integration?: "elysiajs" | "fastify" | "adonisjs" | "astro" | "docusaurus" | "dotnet" | "express" | "fastapi" | "go" | "hono" | "html" | "laravel" | "litestar" | "nestjs" | "nextjs" | "nitro" | "nuxt" | "platformatic" | "react" | "rust" | "svelte" | "vue" | null | undefined;
44
+ onRequestSent?: import("zod/v4/core").$InferOuterFunctionType<import("zod").ZodTuple<readonly [import("zod").ZodString], null>, import("zod").ZodVoid> | undefined;
45
+ plugins?: import("zod/v4/core").$InferOuterFunctionType<import("zod").ZodTuple<readonly [], null>, import("zod").ZodObject<{
46
+ name: import("zod").ZodString;
47
+ views: import("zod").ZodOptional<import("zod").ZodObject<{
48
+ "request.section": import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
49
+ title: import("zod").ZodOptional<import("zod").ZodString>;
50
+ component: import("zod").ZodUnknown;
51
+ props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
52
+ }, import("zod/v4/core").$strip>>>;
53
+ "response.section": import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
54
+ title: import("zod").ZodOptional<import("zod").ZodString>;
55
+ component: import("zod").ZodUnknown;
56
+ props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
57
+ }, import("zod/v4/core").$strip>>>;
58
+ }, import("zod/v4/core").$strip>>;
59
+ hooks: import("zod").ZodOptional<import("zod").ZodObject<{
60
+ onBeforeRequest: import("zod").ZodOptional<import("zod").ZodFunction<import("zod").ZodTuple<readonly [import("zod").ZodObject<{
61
+ request: import("zod").ZodAny;
62
+ }, import("zod/v4/core").$strip>], null>, import("zod/v4/core").$ZodFunctionOut>>;
63
+ onResponseReceived: import("zod").ZodOptional<import("zod").ZodFunction<import("zod").ZodTuple<readonly [import("zod").ZodObject<{
64
+ response: import("zod").ZodCustom<Response, Response>;
65
+ operation: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>;
66
+ }, import("zod/v4/core").$strip>], null>, import("zod/v4/core").$ZodFunctionOut>>;
67
+ }, import("zod/v4/core").$strip>>;
68
+ }, import("zod/v4/core").$strip>>[] | undefined;
69
+ default?: boolean | undefined;
70
+ url?: string | undefined;
71
+ content?: string | Record<string, any> | import("zod/v4/core").$InferOuterFunctionType<import("zod").ZodTuple<readonly [], null>, import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>> | null | undefined;
72
+ title?: string | undefined;
73
+ slug?: string | undefined;
74
+ spec?: {
75
+ url?: string | undefined;
76
+ content?: string | Record<string, any> | import("zod/v4/core").$InferOuterFunctionType<import("zod").ZodTuple<readonly [], null>, import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>> | null | undefined;
77
+ } | undefined;
78
+ agent?: {
79
+ key?: string | undefined;
80
+ disabled?: boolean | undefined;
81
+ hideAddApi?: boolean | undefined;
82
+ } | undefined;
83
+ }> | undefined) => Promise<{
84
+ apiClient: import("@scalar/api-client/v2/features/modal").ApiClientModal;
85
+ workspaceStore: import("@scalar/workspace-store/client").WorkspaceStore;
86
+ }>;
87
+ //# sourceMappingURL=lazy-load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-load.d.ts","sourceRoot":"","sources":["../src/lazy-load.ts"],"names":[],"mappings":"AAsBA,yCAAyC;AACzC,eAAO,MAAM,qBAAqB;;;;;;;oEAEjC,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,wEAEtC,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,2EAEzC,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0B/B,CAAA"}
package/dist/style.css CHANGED
@@ -4727,12 +4727,18 @@ input[data-v-c1a50a6e]::placeholder {
4727
4727
  .scalar-app .min-h-20 {
4728
4728
  min-height: 80px;
4729
4729
  }
4730
+ .scalar-app .min-h-\[4rem\] {
4731
+ min-height: 4rem;
4732
+ }
4730
4733
  .scalar-app .min-h-\[64px\] {
4731
4734
  min-height: 64px;
4732
4735
  }
4733
4736
  .scalar-app .min-h-\[65px\] {
4734
4737
  min-height: 65px;
4735
4738
  }
4739
+ .scalar-app .min-h-\[300px\] {
4740
+ min-height: 300px;
4741
+ }
4736
4742
  .scalar-app .min-h-\[calc\(1rem\*4\)\] {
4737
4743
  min-height: 4rem;
4738
4744
  }
@@ -4757,6 +4763,9 @@ input[data-v-c1a50a6e]::placeholder {
4757
4763
  .scalar-app .w-1\/2 {
4758
4764
  width: 50%;
4759
4765
  }
4766
+ .scalar-app .w-2 {
4767
+ width: 8px;
4768
+ }
4760
4769
  .scalar-app .w-2\.5 {
4761
4770
  width: 10px;
4762
4771
  }
@@ -5342,7 +5351,7 @@ input[data-v-c1a50a6e]::placeholder {
5342
5351
  .scalar-app .border-\(--scalar-color-alert\) {
5343
5352
  border-color: var(--scalar-color-alert);
5344
5353
  }
5345
- .scalar-app .border-border {
5354
+ .scalar-app .border-\[var\(--scalar-border-color\)\], .scalar-app .border-border {
5346
5355
  border-color: var(--scalar-border-color);
5347
5356
  }
5348
5357
  .scalar-app .border-c-1 {
@@ -5379,6 +5388,18 @@ input[data-v-c1a50a6e]::placeholder {
5379
5388
  .scalar-app .bg-\(--scalar-background-alert\) {
5380
5389
  background-color: var(--scalar-background-alert);
5381
5390
  }
5391
+ .scalar-app .bg-\[var\(--scalar-background-1\)\] {
5392
+ background-color: var(--scalar-background-1);
5393
+ }
5394
+ .scalar-app .bg-\[var\(--scalar-background-2\)\] {
5395
+ background-color: var(--scalar-background-2);
5396
+ }
5397
+ .scalar-app .bg-\[var\(--scalar-background-3\)\] {
5398
+ background-color: var(--scalar-background-3);
5399
+ }
5400
+ .scalar-app .bg-\[var\(--scalar-color-green\)\] {
5401
+ background-color: var(--scalar-color-green);
5402
+ }
5382
5403
  .scalar-app .bg-b-1 {
5383
5404
  background-color: var(--scalar-background-1);
5384
5405
  }
@@ -6036,6 +6057,10 @@ input[data-v-c1a50a6e]::placeholder {
6036
6057
  --tw-shadow: 0 -8px 0 8px var(--tw-shadow-color, var(--scalar-background-1)), 0 0 8px 8px var(--tw-shadow-color, var(--scalar-background-1));
6037
6058
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
6038
6059
  }
6060
+ .scalar-app .shadow-\[var\(--scalar-shadow-1\)\] {
6061
+ --tw-shadow: var(--scalar-shadow-1);
6062
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
6063
+ }
6039
6064
  .scalar-app .shadow-border {
6040
6065
  --tw-shadow: inset 0 0 0 var(--tw-shadow-color, var(--scalar-border-width)) var(--scalar-border-color);
6041
6066
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -6096,6 +6121,11 @@ input[data-v-c1a50a6e]::placeholder {
6096
6121
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
6097
6122
  transition-duration: var(--tw-duration, var(--default-transition-duration));
6098
6123
  }
6124
+ .scalar-app .transition-all {
6125
+ transition-property: all;
6126
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
6127
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
6128
+ }
6099
6129
  .scalar-app .transition-colors {
6100
6130
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
6101
6131
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -6479,6 +6509,10 @@ input[data-v-c1a50a6e]::placeholder {
6479
6509
  border-color: inherit;
6480
6510
  }
6481
6511
 
6512
+ .scalar-app .hover\:bg-\[var\(--scalar-background-3\)\]:hover {
6513
+ background-color: var(--scalar-background-3);
6514
+ }
6515
+
6482
6516
  .scalar-app .hover\:bg-b-2:hover, .scalar-app .hover\:bg-b-2\/40:hover {
6483
6517
  background-color: var(--scalar-background-2);
6484
6518
  }
@@ -9030,5 +9064,8 @@ to {
9030
9064
  justify-content: flex-end;
9031
9065
  gap: 1rem;
9032
9066
  }
9067
+ .document-scripts-editors__container[data-v-8c8fa790] {
9068
+ min-height: 300px;
9069
+ }
9033
9070
  /*$vite$:1*/
9034
9071
  /*$vite$:1*/
@@ -0,0 +1,26 @@
1
+ import type { ApiClientModal, RoutePayload } from '@scalar/api-client/v2/features/modal';
2
+ import type { ApiClientConfiguration } from '@scalar/types/api-reference';
3
+ import './style.css';
4
+ /** We don't really need all of the content types so we just accept an object instead */
5
+ export type ApiClientConfigurationReact = Partial<Omit<ApiClientConfiguration, 'content' | 'url'> & {
6
+ content?: Record<string, unknown>;
7
+ url?: string;
8
+ }>;
9
+ export type UseApiClientModalProps = {
10
+ /** Configuration for the Api Client (url or inline content) */
11
+ configuration?: ApiClientConfigurationReact;
12
+ };
13
+ /**
14
+ * Returns the singleton Api Client
15
+ *
16
+ * On first call the Vue app is lazily created and appended to document.body where it
17
+ * lives for the lifetime of the page — it is never unmounted, so it survives client-side
18
+ * navigation without losing state.
19
+ *
20
+ * Subsequent calls from any component share the same instance of the client but can use the same
21
+ * or different documents
22
+ */
23
+ export declare const useApiClient: ({ configuration, }?: UseApiClientModalProps) => (Omit<ApiClientModal, "open"> & {
24
+ open: (payload: RoutePayload) => void;
25
+ }) | undefined;
26
+ //# sourceMappingURL=use-api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-api-client.d.ts","sourceRoot":"","sources":["../src/use-api-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AAGzE,OAAO,aAAa,CAAA;AAUpB,wFAAwF;AACxF,MAAM,MAAM,2BAA2B,GAAG,OAAO,CAC/C,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,KAAK,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CACtG,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,2BAA2B,CAAA;CAC5C,CAAA;AAKD;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,qBAE1B,sBAA2B,KAC1B,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAA;CAAE,CAAC,GAC1E,SAsEH,CAAA"}
@@ -0,0 +1,2 @@
1
+ import '@testing-library/jest-dom';
2
+ //# sourceMappingURL=vitest.setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.setup.d.ts","sourceRoot":"","sources":["../src/vitest.setup.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAA"}
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "testing",
20
20
  "react"
21
21
  ],
22
- "version": "1.4.20",
22
+ "version": "2.0.0",
23
23
  "engines": {
24
24
  "node": ">=22"
25
25
  },
@@ -39,17 +39,23 @@
39
39
  "CHANGELOG.md"
40
40
  ],
41
41
  "dependencies": {
42
- "@scalar/api-client": "2.41.0",
43
- "@scalar/types": "0.7.6"
42
+ "@scalar/workspace-store": "0.44.0",
43
+ "@scalar/api-client": "2.42.0",
44
+ "@scalar/types": "0.8.0"
44
45
  },
45
46
  "devDependencies": {
47
+ "@testing-library/dom": "^10.4.1",
48
+ "@testing-library/jest-dom": "^6.9.1",
49
+ "@testing-library/react": "^16.3.2",
46
50
  "@types/react": "^19.2.7",
47
51
  "@types/react-dom": "^19.2.3",
48
52
  "@vitejs/plugin-react": "6.0.1",
53
+ "jsdom": "^29.0.1",
49
54
  "react": "^19.2.3",
50
55
  "react-dom": "^19.2.3",
51
56
  "rollup-preserve-directives": "^1.1.1",
52
- "vite": "8.0.0"
57
+ "vite": "8.0.0",
58
+ "vitest": "^4.1.2"
53
59
  },
54
60
  "peerDependencies": {
55
61
  "react": "^18.0.0 || ^19.0.0"
@@ -57,6 +63,7 @@
57
63
  "scripts": {
58
64
  "build": "vite build && vue-tsc -p tsconfig.build.json",
59
65
  "dev": "vite ./playground -c ./vite.config.ts",
66
+ "test": "vitest --run",
60
67
  "types:check": "vue-tsc --noEmit"
61
68
  }
62
69
  }
@@ -1,21 +0,0 @@
1
- import type { ApiClient } from '@scalar/api-client/layouts/Modal';
2
- import type { OpenClientPayload } from '@scalar/api-client/libs';
3
- import type { ApiClientConfiguration } from '@scalar/types/api-reference';
4
- import type { PropsWithChildren } from 'react';
5
- export type { OpenClientPayload };
6
- import './style.css';
7
- type Props = PropsWithChildren<{
8
- /** Choose a request to initially route to */
9
- initialRequest?: OpenClientPayload;
10
- /** Configuration for the Api Client */
11
- configuration?: Partial<ApiClientConfiguration>;
12
- }>;
13
- /**
14
- * Api Client Modal React
15
- *
16
- * Provider which mounts the Scalar Api Client Modal vue app.
17
- * Rebuilt to support multiple instances when using a unique spec.url
18
- */
19
- export declare const ApiClientModalProvider: ({ children, initialRequest, configuration }: Props) => import("react/jsx-runtime").JSX.Element;
20
- export declare const useApiClientModal: () => ApiClient | null;
21
- //# sourceMappingURL=ApiClientModalProvider.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ApiClientModalProvider.d.ts","sourceRoot":"","sources":["../src/ApiClientModalProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAG9C,YAAY,EAAE,iBAAiB,EAAE,CAAA;AAIjC,OAAO,aAAa,CAAA;AAIpB,KAAK,KAAK,GAAG,iBAAiB,CAAC;IAC7B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,iBAAiB,CAAA;IAClC,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;CAChD,CAAC,CAAA;AAQF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,6CAAkD,KAAK,4CA4D7F,CAAA;AAED,eAAO,MAAM,iBAAiB,QAAO,SAAS,GAAG,IAAyC,CAAA"}
@@ -1,12 +0,0 @@
1
- import type { ApiClient, createApiClientModal } from '@scalar/api-client/layouts/Modal';
2
- export declare const clientStore: {
3
- getSnapshot: () => {
4
- createClient: typeof createApiClientModal | null;
5
- clientDict: Record<string, ApiClient>;
6
- };
7
- subscribe: (listener: () => void) => () => boolean;
8
- setCreateClient: (clientFactory: typeof createApiClientModal) => void;
9
- addClient: (url: string, client: ApiClient) => void;
10
- removeClient: (url: string) => void;
11
- };
12
- //# sourceMappingURL=client-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-store.d.ts","sourceRoot":"","sources":["../src/client-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AA+CvF,eAAO,MAAM,WAAW;;sBAtCA,OAAO,oBAAoB,GAAG,IAAI;oBACtC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;;0BAOhB,MAAM,IAAI;qCAYC,OAAO,oBAAoB;qBAM3C,MAAM,UAAU,SAAS;wBAMtB,MAAM;CAYhC,CAAA"}