@storyblok/live-preview 0.1.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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/index.cjs +201 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +214 -0
- package/dist/index.d.mts +214 -0
- package/dist/index.mjs +196 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Storyblok GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
<h1 align="center">@storyblok/live-preview</h1>
|
|
6
|
+
<p>
|
|
7
|
+
Lightweight helpers to enable Storyblok Live Preview and Visual Editor
|
|
8
|
+
updates across client-side and SSR frameworks.
|
|
9
|
+
</p>
|
|
10
|
+
<br />
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://npmjs.com/package/@storyblok/live-preview">
|
|
15
|
+
<img src="https://img.shields.io/npm/v/@storyblok/live-preview/latest.svg?style=flat-square&color=8d60ff" alt="Storyblok Live Preview" />
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://npmjs.com/package/@storyblok/live-preview" rel="nofollow">
|
|
18
|
+
<img src="https://img.shields.io/npm/dt/@storyblok/live-preview.svg?style=appveyor&color=8d60ff" alt="npm">
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://storyblok.com/join-discord">
|
|
21
|
+
<img src="https://img.shields.io/discord/700316478792138842?label=Join%20Our%20Discord%20Community&style=appveyor&logo=discord&color=8d60ff">
|
|
22
|
+
</a>
|
|
23
|
+
<a href="https://twitter.com/intent/follow?screen_name=storyblok">
|
|
24
|
+
<img src="https://img.shields.io/badge/Follow-%40storyblok-8d60ff?style=appveyor&logo=twitter" alt="Follow @Storyblok" />
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @storyblok/live-preview
|
|
32
|
+
# or
|
|
33
|
+
yarn add @storyblok/live-preview
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Live preview
|
|
39
|
+
|
|
40
|
+
Covers the most common live preview use cases with minimal setup.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { onStoryblokEditorEvent } from '@storyblok/live-preview';
|
|
44
|
+
|
|
45
|
+
onStoryblokEditorEvent((story) => {
|
|
46
|
+
// update local state
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
### Low-level Preview Bridge access
|
|
50
|
+
For advanced use cases where full control is needed, the Preview Bridge can be accessed directly.
|
|
51
|
+
```ts
|
|
52
|
+
import { loadStoryblokBridge } from '@storyblok/live-preview';
|
|
53
|
+
|
|
54
|
+
const bridge = await loadStoryblokBridge(bridgeOptions);
|
|
55
|
+
|
|
56
|
+
bridge.on(['input', 'change', 'published'], (event) => {
|
|
57
|
+
// custom preview handling
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
## Documentation
|
|
61
|
+
|
|
62
|
+
This package is intentionally minimal.
|
|
63
|
+
More helpers and examples will be added over time as usage expands.
|
|
64
|
+
|
|
65
|
+
## Community
|
|
66
|
+
|
|
67
|
+
For help, best practices, or discussions:
|
|
68
|
+
|
|
69
|
+
* [Storyblok GitHub Discussions](https://github.com/storyblok/monoblok/discussions)
|
|
70
|
+
* [Join the Storyblok Discord](https://storyblok.com/join-discord)
|
|
71
|
+
|
|
72
|
+
## Support
|
|
73
|
+
|
|
74
|
+
For bugs or feature requests, please
|
|
75
|
+
[submit an issue](https://github.com/storyblok/monoblok/issues/new/choose).
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
[MIT](/LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/editable.ts
|
|
4
|
+
function storyblokEditable(blok) {
|
|
5
|
+
const editable = blok?._editable;
|
|
6
|
+
if (!editable) return {};
|
|
7
|
+
if (!editable.startsWith("<!--#storyblok#") || !editable.endsWith("-->")) return {};
|
|
8
|
+
try {
|
|
9
|
+
const json = editable.slice(15, -3);
|
|
10
|
+
const options = JSON.parse(json);
|
|
11
|
+
return {
|
|
12
|
+
"data-blok-c": JSON.stringify(options),
|
|
13
|
+
"data-blok-uid": `${options.id}-${options.uid}`
|
|
14
|
+
};
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/loadStoryblokBridge.ts
|
|
22
|
+
let bridgePromise;
|
|
23
|
+
let storedConfig;
|
|
24
|
+
function configsAreEqual(config1, config2) {
|
|
25
|
+
return JSON.stringify(config1) === JSON.stringify(config2);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get or create a StoryblokBridge instance.
|
|
29
|
+
*⚠️ The bridge is a singleton. Configuration is applied only on first load.
|
|
30
|
+
* @param config Optional configuration for the StoryblokBridge.
|
|
31
|
+
* @returns A promise that resolves to a StoryblokBridge instance.
|
|
32
|
+
*/
|
|
33
|
+
function loadStoryblokBridge(config) {
|
|
34
|
+
if (bridgePromise) {
|
|
35
|
+
if (config && !configsAreEqual(config, storedConfig)) throw new Error("[Storyblok] Preview Bridge already initialized with a different configuration. The bridge can only be created once per page and does not support runtime reconfiguration.");
|
|
36
|
+
return bridgePromise;
|
|
37
|
+
}
|
|
38
|
+
storedConfig = config;
|
|
39
|
+
bridgePromise = import("@storyblok/preview-bridge").then(({ default: StoryblokBridge }) => new StoryblokBridge(config)).catch((error) => {
|
|
40
|
+
bridgePromise = void 0;
|
|
41
|
+
storedConfig = void 0;
|
|
42
|
+
throw error;
|
|
43
|
+
});
|
|
44
|
+
return bridgePromise;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/utils/isBrowser.ts
|
|
49
|
+
function isBrowser() {
|
|
50
|
+
return typeof window !== "undefined";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/utils/isInEditor.ts
|
|
55
|
+
/**
|
|
56
|
+
* Required query parameters that must be present in all Storyblok Visual Editor requests.
|
|
57
|
+
*/
|
|
58
|
+
const REQUIRED_STORYBLOK_PARAMS = [
|
|
59
|
+
"_storyblok",
|
|
60
|
+
"_storyblok_c",
|
|
61
|
+
"_storyblok_tk[space_id]"
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.
|
|
65
|
+
*
|
|
66
|
+
* This function performs a multi-layered validation to ensure the request originates from
|
|
67
|
+
* the Storyblok Visual Editor by checking for required query parameters and optionally
|
|
68
|
+
* validating the space ID.
|
|
69
|
+
*
|
|
70
|
+
* @param url - The URL object to validate.
|
|
71
|
+
* @param options - Optional validation configuration.
|
|
72
|
+
* @param options.spaceId - If provided, validates that the request's space ID matches this value.
|
|
73
|
+
*
|
|
74
|
+
* @returns `true` if the URL contains all required Storyblok Visual Editor parameters
|
|
75
|
+
* and passes optional space ID validation; `false` otherwise.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');
|
|
80
|
+
*
|
|
81
|
+
* // Basic validation
|
|
82
|
+
* if (isInEditor(url)) {
|
|
83
|
+
* console.log('Valid Storyblok editor request');
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* // Validation with space ID check
|
|
87
|
+
* if (isInEditor(url, { spaceId: '789' })) {
|
|
88
|
+
* console.log('Valid request for specific space');
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
function isInEditor(url, options = {}) {
|
|
93
|
+
const params = url.searchParams;
|
|
94
|
+
if (!REQUIRED_STORYBLOK_PARAMS.every((param) => params.has(param))) return false;
|
|
95
|
+
if (options.spaceId && params.get("_storyblok_tk[space_id]") !== options.spaceId) return false;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/utils/canUseStoryblokBridge.ts
|
|
101
|
+
function canUseStoryblokBridge() {
|
|
102
|
+
if (!isBrowser()) return false;
|
|
103
|
+
return isInEditor(new URL(window.location.href));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/onStoryblokEditorEvent.ts
|
|
108
|
+
/**
|
|
109
|
+
* Internal listener registry for Storyblok `input` events.
|
|
110
|
+
* Each listener receives the updated story data from the Visual Editor.
|
|
111
|
+
*/
|
|
112
|
+
const inputListeners = /* @__PURE__ */ new Set();
|
|
113
|
+
/**
|
|
114
|
+
* Tracks whether the Storyblok Preview Bridge event listeners
|
|
115
|
+
* have already been registered.
|
|
116
|
+
*/
|
|
117
|
+
let bridgeInitPromise;
|
|
118
|
+
/**
|
|
119
|
+
* Initializes the Storyblok Preview Bridge and attaches event listeners.
|
|
120
|
+
*
|
|
121
|
+
* This function ensures that the bridge is only initialized once per page.
|
|
122
|
+
*
|
|
123
|
+
* Registered events:
|
|
124
|
+
* - `input` → Dispatches updated story data to all registered listeners.
|
|
125
|
+
* - `change` → Forces a full page reload.
|
|
126
|
+
* - `published` → Forces a full page reload.
|
|
127
|
+
*
|
|
128
|
+
* @param bridgeOptions Optional configuration for the Preview Bridge.
|
|
129
|
+
*/
|
|
130
|
+
async function initializeBridge(bridgeOptions) {
|
|
131
|
+
if (!canUseStoryblokBridge()) return;
|
|
132
|
+
if (bridgeInitPromise) return bridgeInitPromise;
|
|
133
|
+
bridgeInitPromise = (async () => {
|
|
134
|
+
(await loadStoryblokBridge(bridgeOptions)).on([
|
|
135
|
+
"input",
|
|
136
|
+
"change",
|
|
137
|
+
"published"
|
|
138
|
+
], (event) => {
|
|
139
|
+
if (!event) return;
|
|
140
|
+
if (event.action === "input" && event.story) {
|
|
141
|
+
for (const listener of inputListeners) listener(event.story);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (event.action === "change" || event.action === "published") window.location.reload();
|
|
145
|
+
});
|
|
146
|
+
})();
|
|
147
|
+
return bridgeInitPromise;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Registers a callback for Storyblok Visual Editor live preview updates.
|
|
151
|
+
*
|
|
152
|
+
* This utility connects to the Storyblok Preview Bridge and listens
|
|
153
|
+
* for Visual Editor events.
|
|
154
|
+
*
|
|
155
|
+
* Behavior:
|
|
156
|
+
* - **input** → Calls the provided callback with the updated story data.
|
|
157
|
+
* - **change** → Reloads the page.
|
|
158
|
+
* - **published** → Reloads the page.
|
|
159
|
+
*
|
|
160
|
+
* Multiple listeners can be registered simultaneously. Each call returns
|
|
161
|
+
* a cleanup function that removes the registered listener.
|
|
162
|
+
*
|
|
163
|
+
* @typeParam T - The Storyblok component schema type.
|
|
164
|
+
*
|
|
165
|
+
* @param callback
|
|
166
|
+
* Callback executed when the Visual Editor sends an `input` event.
|
|
167
|
+
*
|
|
168
|
+
* @param bridgeOptions
|
|
169
|
+
* Optional configuration for the Storyblok Preview Bridge.
|
|
170
|
+
* This configuration is applied **only during the first initialization**.
|
|
171
|
+
*
|
|
172
|
+
* @returns
|
|
173
|
+
* A cleanup function that removes the registered listener.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const cleanup = await onStoryblokEditorEvent((story) => {
|
|
178
|
+
* console.log('Live updated story:', story)
|
|
179
|
+
* })
|
|
180
|
+
*
|
|
181
|
+
* // later
|
|
182
|
+
* cleanup()
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
async function onStoryblokEditorEvent(callback, bridgeOptions) {
|
|
186
|
+
await initializeBridge(bridgeOptions);
|
|
187
|
+
const listener = (story) => {
|
|
188
|
+
callback(story);
|
|
189
|
+
};
|
|
190
|
+
inputListeners.add(listener);
|
|
191
|
+
return () => {
|
|
192
|
+
inputListeners.delete(listener);
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
exports.isInEditor = isInEditor;
|
|
198
|
+
exports.loadStoryblokBridge = loadStoryblokBridge;
|
|
199
|
+
exports.onStoryblokEditorEvent = onStoryblokEditorEvent;
|
|
200
|
+
exports.storyblokEditable = storyblokEditable;
|
|
201
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/editable.ts","../src/loadStoryblokBridge.ts","../src/utils/isBrowser.ts","../src/utils/isInEditor.ts","../src/utils/canUseStoryblokBridge.ts","../src/onStoryblokEditorEvent.ts"],"sourcesContent":["interface Blok {\n _editable?: string;\n}\n\ninterface EditableOptions {\n id: string;\n uid: string;\n}\n\nexport default function storyblokEditable(blok?: Blok) {\n const editable = blok?._editable;\n if (!editable) {\n return {};\n }\n\n const prefix = '<!--#storyblok#';\n const suffix = '-->';\n\n if (!editable.startsWith(prefix) || !editable.endsWith(suffix)) {\n return {};\n }\n\n try {\n const json = editable.slice(prefix.length, -suffix.length);\n const options = JSON.parse(json) as EditableOptions;\n\n return {\n 'data-blok-c': JSON.stringify(options),\n 'data-blok-uid': `${options.id}-${options.uid}`,\n };\n }\n catch {\n return {};\n }\n}\n","import type StoryblokBridge from '@storyblok/preview-bridge';\nimport type { BridgeParams } from '@storyblok/preview-bridge';\n\nlet bridgePromise: Promise<StoryblokBridge> | undefined;\nlet storedConfig: BridgeParams | undefined;\nfunction configsAreEqual(config1: BridgeParams | undefined, config2: BridgeParams | undefined): boolean {\n return JSON.stringify(config1) === JSON.stringify(config2);\n}\n\n/**\n * Get or create a StoryblokBridge instance.\n *⚠️ The bridge is a singleton. Configuration is applied only on first load.\n * @param config Optional configuration for the StoryblokBridge.\n * @returns A promise that resolves to a StoryblokBridge instance.\n */\nexport function loadStoryblokBridge(config?: BridgeParams) {\n if (bridgePromise) {\n if (config && !configsAreEqual(config, storedConfig)) {\n throw new Error(\n '[Storyblok] Preview Bridge already initialized with a different configuration. '\n + 'The bridge can only be created once per page and does not support runtime reconfiguration.',\n );\n }\n return bridgePromise;\n }\n\n storedConfig = config;\n\n bridgePromise = import('@storyblok/preview-bridge')\n .then(({ default: StoryblokBridge }) => new StoryblokBridge(config))\n .catch((error) => {\n bridgePromise = undefined;\n storedConfig = undefined;\n throw error;\n });\n\n return bridgePromise;\n}\n","export function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n","/**\n * Options for validating Storyblok Visual Editor requests.\n */\ninterface StoryblokValidationOptions {\n /**\n * Optional space ID to validate against the request.\n * If provided, the request must match this space ID to be considered valid.\n */\n spaceId?: string;\n}\n\n/**\n * Required query parameters that must be present in all Storyblok Visual Editor requests.\n */\nconst REQUIRED_STORYBLOK_PARAMS = ['_storyblok', '_storyblok_c', '_storyblok_tk[space_id]'] as const;\n\n/**\n * Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.\n *\n * This function performs a multi-layered validation to ensure the request originates from\n * the Storyblok Visual Editor by checking for required query parameters and optionally\n * validating the space ID.\n *\n * @param url - The URL object to validate.\n * @param options - Optional validation configuration.\n * @param options.spaceId - If provided, validates that the request's space ID matches this value.\n *\n * @returns `true` if the URL contains all required Storyblok Visual Editor parameters\n * and passes optional space ID validation; `false` otherwise.\n *\n * @example\n * ```typescript\n * const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');\n *\n * // Basic validation\n * if (isInEditor(url)) {\n * console.log('Valid Storyblok editor request');\n * }\n *\n * // Validation with space ID check\n * if (isInEditor(url, { spaceId: '789' })) {\n * console.log('Valid request for specific space');\n * }\n * ```\n */\nexport function isInEditor(\n url: URL,\n options: StoryblokValidationOptions = {},\n): boolean {\n const params = url.searchParams;\n\n // Early return: Check all required parameters exist\n const hasRequiredParams = REQUIRED_STORYBLOK_PARAMS.every(param => params.has(param));\n\n if (!hasRequiredParams) {\n return false;\n }\n\n // Optional space ID validation\n if (options.spaceId && params.get('_storyblok_tk[space_id]') !== options.spaceId) {\n return false;\n }\n\n return true;\n}\n","import { isBrowser } from './isBrowser';\nimport { isInEditor } from './isInEditor';\n\nexport function canUseStoryblokBridge(): boolean {\n if (!isBrowser()) {\n return false;\n }\n return isInEditor(new URL(window.location.href));\n}\n","import type { BridgeParams } from '@storyblok/preview-bridge';\nimport type { ISbComponentType, ISbStoryData } from 'storyblok-js-client';\n\nimport { loadStoryblokBridge } from './loadStoryblokBridge';\nimport { canUseStoryblokBridge } from './utils/canUseStoryblokBridge';\n\n/**\n * Internal listener registry for Storyblok `input` events.\n * Each listener receives the updated story data from the Visual Editor.\n */\nconst inputListeners = new Set<(story: ISbStoryData) => void>();\n\n/**\n * Tracks whether the Storyblok Preview Bridge event listeners\n * have already been registered.\n */\nlet bridgeInitPromise: Promise<void> | undefined;\n\n/**\n * Initializes the Storyblok Preview Bridge and attaches event listeners.\n *\n * This function ensures that the bridge is only initialized once per page.\n *\n * Registered events:\n * - `input` → Dispatches updated story data to all registered listeners.\n * - `change` → Forces a full page reload.\n * - `published` → Forces a full page reload.\n *\n * @param bridgeOptions Optional configuration for the Preview Bridge.\n */\nasync function initializeBridge(bridgeOptions?: BridgeParams): Promise<void> {\n if (!canUseStoryblokBridge()) {\n return;\n }\n\n // If initialization already started, reuse it\n if (bridgeInitPromise) {\n return bridgeInitPromise;\n }\n\n bridgeInitPromise = (async () => {\n const bridge = await loadStoryblokBridge(bridgeOptions);\n\n bridge.on(['input', 'change', 'published'], (event) => {\n if (!event) {\n return;\n }\n\n if (event.action === 'input' && event.story) {\n for (const listener of inputListeners) {\n listener(event.story as ISbStoryData);\n }\n return;\n }\n\n if (event.action === 'change' || event.action === 'published') {\n window.location.reload();\n }\n });\n })();\n\n return bridgeInitPromise;\n}\n\n/**\n * Registers a callback for Storyblok Visual Editor live preview updates.\n *\n * This utility connects to the Storyblok Preview Bridge and listens\n * for Visual Editor events.\n *\n * Behavior:\n * - **input** → Calls the provided callback with the updated story data.\n * - **change** → Reloads the page.\n * - **published** → Reloads the page.\n *\n * Multiple listeners can be registered simultaneously. Each call returns\n * a cleanup function that removes the registered listener.\n *\n * @typeParam T - The Storyblok component schema type.\n *\n * @param callback\n * Callback executed when the Visual Editor sends an `input` event.\n *\n * @param bridgeOptions\n * Optional configuration for the Storyblok Preview Bridge.\n * This configuration is applied **only during the first initialization**.\n *\n * @returns\n * A cleanup function that removes the registered listener.\n *\n * @example\n * ```ts\n * const cleanup = await onStoryblokEditorEvent((story) => {\n * console.log('Live updated story:', story)\n * })\n *\n * // later\n * cleanup()\n * ```\n */\nexport async function onStoryblokEditorEvent<\n T extends ISbComponentType<string> = ISbComponentType<string>,\n>(\n callback: (story: ISbStoryData<T>) => void,\n bridgeOptions?: BridgeParams,\n): Promise<() => void> {\n await initializeBridge(bridgeOptions);\n\n const listener = (story: ISbStoryData) => {\n callback(story as ISbStoryData<T>);\n };\n\n inputListeners.add(listener);\n\n return () => {\n inputListeners.delete(listener);\n };\n}\n"],"mappings":";;;AASA,SAAwB,kBAAkB,MAAa;CACrD,MAAM,WAAW,MAAM;AACvB,KAAI,CAAC,SACH,QAAO,EAAE;AAMX,KAAI,CAAC,SAAS,WAHC,kBAGiB,IAAI,CAAC,SAAS,SAF/B,MAE+C,CAC5D,QAAO,EAAE;AAGX,KAAI;EACF,MAAM,OAAO,SAAS,MAAM,IAAe,GAAe;EAC1D,MAAM,UAAU,KAAK,MAAM,KAAK;AAEhC,SAAO;GACL,eAAe,KAAK,UAAU,QAAQ;GACtC,iBAAiB,GAAG,QAAQ,GAAG,GAAG,QAAQ;GAC3C;SAEG;AACJ,SAAO,EAAE;;;;;;AC7Bb,IAAI;AACJ,IAAI;AACJ,SAAS,gBAAgB,SAAmC,SAA4C;AACtG,QAAO,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ;;;;;;;;AAS5D,SAAgB,oBAAoB,QAAuB;AACzD,KAAI,eAAe;AACjB,MAAI,UAAU,CAAC,gBAAgB,QAAQ,aAAa,CAClD,OAAM,IAAI,MACR,4KAED;AAEH,SAAO;;AAGT,gBAAe;AAEf,iBAAgB,OAAO,6BACpB,MAAM,EAAE,SAAS,sBAAsB,IAAI,gBAAgB,OAAO,CAAC,CACnE,OAAO,UAAU;AAChB,kBAAgB;AAChB,iBAAe;AACf,QAAM;GACN;AAEJ,QAAO;;;;;ACpCT,SAAgB,YAAqB;AACnC,QAAO,OAAO,WAAW;;;;;;;;ACa3B,MAAM,4BAA4B;CAAC;CAAc;CAAgB;CAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B3F,SAAgB,WACd,KACA,UAAsC,EAAE,EAC/B;CACT,MAAM,SAAS,IAAI;AAKnB,KAAI,CAFsB,0BAA0B,OAAM,UAAS,OAAO,IAAI,MAAM,CAAC,CAGnF,QAAO;AAIT,KAAI,QAAQ,WAAW,OAAO,IAAI,0BAA0B,KAAK,QAAQ,QACvE,QAAO;AAGT,QAAO;;;;;AC5DT,SAAgB,wBAAiC;AAC/C,KAAI,CAAC,WAAW,CACd,QAAO;AAET,QAAO,WAAW,IAAI,IAAI,OAAO,SAAS,KAAK,CAAC;;;;;;;;;ACGlD,MAAM,iCAAiB,IAAI,KAAoC;;;;;AAM/D,IAAI;;;;;;;;;;;;;AAcJ,eAAe,iBAAiB,eAA6C;AAC3E,KAAI,CAAC,uBAAuB,CAC1B;AAIF,KAAI,kBACF,QAAO;AAGT,sBAAqB,YAAY;AAG/B,GAFe,MAAM,oBAAoB,cAAc,EAEhD,GAAG;GAAC;GAAS;GAAU;GAAY,GAAG,UAAU;AACrD,OAAI,CAAC,MACH;AAGF,OAAI,MAAM,WAAW,WAAW,MAAM,OAAO;AAC3C,SAAK,MAAM,YAAY,eACrB,UAAS,MAAM,MAAsB;AAEvC;;AAGF,OAAI,MAAM,WAAW,YAAY,MAAM,WAAW,YAChD,QAAO,SAAS,QAAQ;IAE1B;KACA;AAEJ,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCT,eAAsB,uBAGpB,UACA,eACqB;AACrB,OAAM,iBAAiB,cAAc;CAErC,MAAM,YAAY,UAAwB;AACxC,WAAS,MAAyB;;AAGpC,gBAAe,IAAI,SAAS;AAE5B,cAAa;AACX,iBAAe,OAAO,SAAS"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import StoryblokBridge, { BridgeParams, BridgeParams as BridgeParams$1 } from "@storyblok/preview-bridge";
|
|
2
|
+
|
|
3
|
+
//#region src/editable.d.ts
|
|
4
|
+
interface Blok {
|
|
5
|
+
_editable?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function storyblokEditable(blok?: Blok): {
|
|
8
|
+
'data-blok-c'?: undefined;
|
|
9
|
+
'data-blok-uid'?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
'data-blok-c': string;
|
|
12
|
+
'data-blok-uid': string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/loadStoryblokBridge.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* Get or create a StoryblokBridge instance.
|
|
18
|
+
*⚠️ The bridge is a singleton. Configuration is applied only on first load.
|
|
19
|
+
* @param config Optional configuration for the StoryblokBridge.
|
|
20
|
+
* @returns A promise that resolves to a StoryblokBridge instance.
|
|
21
|
+
*/
|
|
22
|
+
declare function loadStoryblokBridge(config?: BridgeParams$1): Promise<StoryblokBridge>;
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region ../js-client/dist/index.d.mts
|
|
25
|
+
interface ISbComponentType<T extends string> {
|
|
26
|
+
_uid?: string;
|
|
27
|
+
component?: T;
|
|
28
|
+
_editable?: string;
|
|
29
|
+
}
|
|
30
|
+
interface PreviewToken {
|
|
31
|
+
token: string;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
}
|
|
34
|
+
interface LocalizedPath {
|
|
35
|
+
path: string;
|
|
36
|
+
name: string | null;
|
|
37
|
+
lang: string;
|
|
38
|
+
published: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface ISbStoryData<Content = ISbComponentType<string> & {
|
|
41
|
+
[index: string]: any;
|
|
42
|
+
}> extends ISbMultipleStoriesData {
|
|
43
|
+
alternates: ISbAlternateObject[];
|
|
44
|
+
breadcrumbs?: ISbLinkURLObject[];
|
|
45
|
+
content: Content;
|
|
46
|
+
created_at: string;
|
|
47
|
+
deleted_at?: string;
|
|
48
|
+
default_full_slug?: string | null;
|
|
49
|
+
default_root?: string;
|
|
50
|
+
disable_fe_editor?: boolean;
|
|
51
|
+
favourite_for_user_ids?: number[] | null;
|
|
52
|
+
first_published_at?: string | null;
|
|
53
|
+
full_slug: string;
|
|
54
|
+
group_id: string;
|
|
55
|
+
id: number;
|
|
56
|
+
imported_at?: string;
|
|
57
|
+
is_folder?: boolean;
|
|
58
|
+
is_startpage?: boolean;
|
|
59
|
+
lang: string;
|
|
60
|
+
last_author?: {
|
|
61
|
+
id: number;
|
|
62
|
+
userid: string;
|
|
63
|
+
};
|
|
64
|
+
last_author_id?: number;
|
|
65
|
+
localized_paths?: LocalizedPath[] | null;
|
|
66
|
+
meta_data: any;
|
|
67
|
+
name: string;
|
|
68
|
+
parent?: ISbStoryData;
|
|
69
|
+
parent_id: number | null;
|
|
70
|
+
path?: string;
|
|
71
|
+
pinned?: '1' | boolean;
|
|
72
|
+
position: number;
|
|
73
|
+
preview_token?: PreviewToken;
|
|
74
|
+
published?: boolean;
|
|
75
|
+
published_at: string | null;
|
|
76
|
+
release_id?: number | null;
|
|
77
|
+
scheduled_date?: string | null;
|
|
78
|
+
slug: string;
|
|
79
|
+
sort_by_date: string | null;
|
|
80
|
+
tag_list: string[];
|
|
81
|
+
translated_slugs?: {
|
|
82
|
+
path: string;
|
|
83
|
+
name: string | null;
|
|
84
|
+
lang: ISbStoryData['lang'];
|
|
85
|
+
published: boolean;
|
|
86
|
+
}[] | null;
|
|
87
|
+
unpublished_changes?: boolean;
|
|
88
|
+
updated_at?: string;
|
|
89
|
+
uuid: string;
|
|
90
|
+
}
|
|
91
|
+
interface ISbMultipleStoriesData {
|
|
92
|
+
by_ids?: string;
|
|
93
|
+
by_uuids?: string;
|
|
94
|
+
contain_component?: string;
|
|
95
|
+
excluding_ids?: string;
|
|
96
|
+
filter_query?: any;
|
|
97
|
+
folder_only?: boolean;
|
|
98
|
+
full_slug?: string;
|
|
99
|
+
in_release?: string;
|
|
100
|
+
in_trash?: boolean;
|
|
101
|
+
is_published?: boolean;
|
|
102
|
+
in_workflow_stages?: string;
|
|
103
|
+
page?: number;
|
|
104
|
+
pinned?: '1' | boolean;
|
|
105
|
+
search?: string;
|
|
106
|
+
sort_by?: string;
|
|
107
|
+
starts_with?: string;
|
|
108
|
+
story_only?: boolean;
|
|
109
|
+
text_search?: string;
|
|
110
|
+
with_parent?: number;
|
|
111
|
+
with_slug?: string;
|
|
112
|
+
with_tag?: string;
|
|
113
|
+
}
|
|
114
|
+
interface ISbAlternateObject {
|
|
115
|
+
id: number;
|
|
116
|
+
name: string;
|
|
117
|
+
slug: string;
|
|
118
|
+
published: boolean;
|
|
119
|
+
full_slug: string;
|
|
120
|
+
is_folder: boolean;
|
|
121
|
+
parent_id: number;
|
|
122
|
+
}
|
|
123
|
+
interface ISbLinkURLObject {
|
|
124
|
+
id: number;
|
|
125
|
+
name: string;
|
|
126
|
+
slug: string;
|
|
127
|
+
full_slug: string;
|
|
128
|
+
url: string;
|
|
129
|
+
uuid: string;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/onStoryblokEditorEvent.d.ts
|
|
133
|
+
/**
|
|
134
|
+
* Registers a callback for Storyblok Visual Editor live preview updates.
|
|
135
|
+
*
|
|
136
|
+
* This utility connects to the Storyblok Preview Bridge and listens
|
|
137
|
+
* for Visual Editor events.
|
|
138
|
+
*
|
|
139
|
+
* Behavior:
|
|
140
|
+
* - **input** → Calls the provided callback with the updated story data.
|
|
141
|
+
* - **change** → Reloads the page.
|
|
142
|
+
* - **published** → Reloads the page.
|
|
143
|
+
*
|
|
144
|
+
* Multiple listeners can be registered simultaneously. Each call returns
|
|
145
|
+
* a cleanup function that removes the registered listener.
|
|
146
|
+
*
|
|
147
|
+
* @typeParam T - The Storyblok component schema type.
|
|
148
|
+
*
|
|
149
|
+
* @param callback
|
|
150
|
+
* Callback executed when the Visual Editor sends an `input` event.
|
|
151
|
+
*
|
|
152
|
+
* @param bridgeOptions
|
|
153
|
+
* Optional configuration for the Storyblok Preview Bridge.
|
|
154
|
+
* This configuration is applied **only during the first initialization**.
|
|
155
|
+
*
|
|
156
|
+
* @returns
|
|
157
|
+
* A cleanup function that removes the registered listener.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* const cleanup = await onStoryblokEditorEvent((story) => {
|
|
162
|
+
* console.log('Live updated story:', story)
|
|
163
|
+
* })
|
|
164
|
+
*
|
|
165
|
+
* // later
|
|
166
|
+
* cleanup()
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
declare function onStoryblokEditorEvent<T extends ISbComponentType<string> = ISbComponentType<string>>(callback: (story: ISbStoryData<T>) => void, bridgeOptions?: BridgeParams$1): Promise<() => void>;
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/utils/isInEditor.d.ts
|
|
172
|
+
/**
|
|
173
|
+
* Options for validating Storyblok Visual Editor requests.
|
|
174
|
+
*/
|
|
175
|
+
interface StoryblokValidationOptions {
|
|
176
|
+
/**
|
|
177
|
+
* Optional space ID to validate against the request.
|
|
178
|
+
* If provided, the request must match this space ID to be considered valid.
|
|
179
|
+
*/
|
|
180
|
+
spaceId?: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.
|
|
184
|
+
*
|
|
185
|
+
* This function performs a multi-layered validation to ensure the request originates from
|
|
186
|
+
* the Storyblok Visual Editor by checking for required query parameters and optionally
|
|
187
|
+
* validating the space ID.
|
|
188
|
+
*
|
|
189
|
+
* @param url - The URL object to validate.
|
|
190
|
+
* @param options - Optional validation configuration.
|
|
191
|
+
* @param options.spaceId - If provided, validates that the request's space ID matches this value.
|
|
192
|
+
*
|
|
193
|
+
* @returns `true` if the URL contains all required Storyblok Visual Editor parameters
|
|
194
|
+
* and passes optional space ID validation; `false` otherwise.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');
|
|
199
|
+
*
|
|
200
|
+
* // Basic validation
|
|
201
|
+
* if (isInEditor(url)) {
|
|
202
|
+
* console.log('Valid Storyblok editor request');
|
|
203
|
+
* }
|
|
204
|
+
*
|
|
205
|
+
* // Validation with space ID check
|
|
206
|
+
* if (isInEditor(url, { spaceId: '789' })) {
|
|
207
|
+
* console.log('Valid request for specific space');
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function isInEditor(url: URL, options?: StoryblokValidationOptions): boolean;
|
|
212
|
+
//#endregion
|
|
213
|
+
export { type BridgeParams, type ISbComponentType, type ISbStoryData, isInEditor, loadStoryblokBridge, onStoryblokEditorEvent, storyblokEditable };
|
|
214
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import StoryblokBridge, { BridgeParams, BridgeParams as BridgeParams$1 } from "@storyblok/preview-bridge";
|
|
2
|
+
|
|
3
|
+
//#region src/editable.d.ts
|
|
4
|
+
interface Blok {
|
|
5
|
+
_editable?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function storyblokEditable(blok?: Blok): {
|
|
8
|
+
'data-blok-c'?: undefined;
|
|
9
|
+
'data-blok-uid'?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
'data-blok-c': string;
|
|
12
|
+
'data-blok-uid': string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/loadStoryblokBridge.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* Get or create a StoryblokBridge instance.
|
|
18
|
+
*⚠️ The bridge is a singleton. Configuration is applied only on first load.
|
|
19
|
+
* @param config Optional configuration for the StoryblokBridge.
|
|
20
|
+
* @returns A promise that resolves to a StoryblokBridge instance.
|
|
21
|
+
*/
|
|
22
|
+
declare function loadStoryblokBridge(config?: BridgeParams$1): Promise<StoryblokBridge>;
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region ../js-client/dist/index.d.mts
|
|
25
|
+
interface ISbComponentType<T extends string> {
|
|
26
|
+
_uid?: string;
|
|
27
|
+
component?: T;
|
|
28
|
+
_editable?: string;
|
|
29
|
+
}
|
|
30
|
+
interface PreviewToken {
|
|
31
|
+
token: string;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
}
|
|
34
|
+
interface LocalizedPath {
|
|
35
|
+
path: string;
|
|
36
|
+
name: string | null;
|
|
37
|
+
lang: string;
|
|
38
|
+
published: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface ISbStoryData<Content = ISbComponentType<string> & {
|
|
41
|
+
[index: string]: any;
|
|
42
|
+
}> extends ISbMultipleStoriesData {
|
|
43
|
+
alternates: ISbAlternateObject[];
|
|
44
|
+
breadcrumbs?: ISbLinkURLObject[];
|
|
45
|
+
content: Content;
|
|
46
|
+
created_at: string;
|
|
47
|
+
deleted_at?: string;
|
|
48
|
+
default_full_slug?: string | null;
|
|
49
|
+
default_root?: string;
|
|
50
|
+
disable_fe_editor?: boolean;
|
|
51
|
+
favourite_for_user_ids?: number[] | null;
|
|
52
|
+
first_published_at?: string | null;
|
|
53
|
+
full_slug: string;
|
|
54
|
+
group_id: string;
|
|
55
|
+
id: number;
|
|
56
|
+
imported_at?: string;
|
|
57
|
+
is_folder?: boolean;
|
|
58
|
+
is_startpage?: boolean;
|
|
59
|
+
lang: string;
|
|
60
|
+
last_author?: {
|
|
61
|
+
id: number;
|
|
62
|
+
userid: string;
|
|
63
|
+
};
|
|
64
|
+
last_author_id?: number;
|
|
65
|
+
localized_paths?: LocalizedPath[] | null;
|
|
66
|
+
meta_data: any;
|
|
67
|
+
name: string;
|
|
68
|
+
parent?: ISbStoryData;
|
|
69
|
+
parent_id: number | null;
|
|
70
|
+
path?: string;
|
|
71
|
+
pinned?: '1' | boolean;
|
|
72
|
+
position: number;
|
|
73
|
+
preview_token?: PreviewToken;
|
|
74
|
+
published?: boolean;
|
|
75
|
+
published_at: string | null;
|
|
76
|
+
release_id?: number | null;
|
|
77
|
+
scheduled_date?: string | null;
|
|
78
|
+
slug: string;
|
|
79
|
+
sort_by_date: string | null;
|
|
80
|
+
tag_list: string[];
|
|
81
|
+
translated_slugs?: {
|
|
82
|
+
path: string;
|
|
83
|
+
name: string | null;
|
|
84
|
+
lang: ISbStoryData['lang'];
|
|
85
|
+
published: boolean;
|
|
86
|
+
}[] | null;
|
|
87
|
+
unpublished_changes?: boolean;
|
|
88
|
+
updated_at?: string;
|
|
89
|
+
uuid: string;
|
|
90
|
+
}
|
|
91
|
+
interface ISbMultipleStoriesData {
|
|
92
|
+
by_ids?: string;
|
|
93
|
+
by_uuids?: string;
|
|
94
|
+
contain_component?: string;
|
|
95
|
+
excluding_ids?: string;
|
|
96
|
+
filter_query?: any;
|
|
97
|
+
folder_only?: boolean;
|
|
98
|
+
full_slug?: string;
|
|
99
|
+
in_release?: string;
|
|
100
|
+
in_trash?: boolean;
|
|
101
|
+
is_published?: boolean;
|
|
102
|
+
in_workflow_stages?: string;
|
|
103
|
+
page?: number;
|
|
104
|
+
pinned?: '1' | boolean;
|
|
105
|
+
search?: string;
|
|
106
|
+
sort_by?: string;
|
|
107
|
+
starts_with?: string;
|
|
108
|
+
story_only?: boolean;
|
|
109
|
+
text_search?: string;
|
|
110
|
+
with_parent?: number;
|
|
111
|
+
with_slug?: string;
|
|
112
|
+
with_tag?: string;
|
|
113
|
+
}
|
|
114
|
+
interface ISbAlternateObject {
|
|
115
|
+
id: number;
|
|
116
|
+
name: string;
|
|
117
|
+
slug: string;
|
|
118
|
+
published: boolean;
|
|
119
|
+
full_slug: string;
|
|
120
|
+
is_folder: boolean;
|
|
121
|
+
parent_id: number;
|
|
122
|
+
}
|
|
123
|
+
interface ISbLinkURLObject {
|
|
124
|
+
id: number;
|
|
125
|
+
name: string;
|
|
126
|
+
slug: string;
|
|
127
|
+
full_slug: string;
|
|
128
|
+
url: string;
|
|
129
|
+
uuid: string;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/onStoryblokEditorEvent.d.ts
|
|
133
|
+
/**
|
|
134
|
+
* Registers a callback for Storyblok Visual Editor live preview updates.
|
|
135
|
+
*
|
|
136
|
+
* This utility connects to the Storyblok Preview Bridge and listens
|
|
137
|
+
* for Visual Editor events.
|
|
138
|
+
*
|
|
139
|
+
* Behavior:
|
|
140
|
+
* - **input** → Calls the provided callback with the updated story data.
|
|
141
|
+
* - **change** → Reloads the page.
|
|
142
|
+
* - **published** → Reloads the page.
|
|
143
|
+
*
|
|
144
|
+
* Multiple listeners can be registered simultaneously. Each call returns
|
|
145
|
+
* a cleanup function that removes the registered listener.
|
|
146
|
+
*
|
|
147
|
+
* @typeParam T - The Storyblok component schema type.
|
|
148
|
+
*
|
|
149
|
+
* @param callback
|
|
150
|
+
* Callback executed when the Visual Editor sends an `input` event.
|
|
151
|
+
*
|
|
152
|
+
* @param bridgeOptions
|
|
153
|
+
* Optional configuration for the Storyblok Preview Bridge.
|
|
154
|
+
* This configuration is applied **only during the first initialization**.
|
|
155
|
+
*
|
|
156
|
+
* @returns
|
|
157
|
+
* A cleanup function that removes the registered listener.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* const cleanup = await onStoryblokEditorEvent((story) => {
|
|
162
|
+
* console.log('Live updated story:', story)
|
|
163
|
+
* })
|
|
164
|
+
*
|
|
165
|
+
* // later
|
|
166
|
+
* cleanup()
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
declare function onStoryblokEditorEvent<T extends ISbComponentType<string> = ISbComponentType<string>>(callback: (story: ISbStoryData<T>) => void, bridgeOptions?: BridgeParams$1): Promise<() => void>;
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/utils/isInEditor.d.ts
|
|
172
|
+
/**
|
|
173
|
+
* Options for validating Storyblok Visual Editor requests.
|
|
174
|
+
*/
|
|
175
|
+
interface StoryblokValidationOptions {
|
|
176
|
+
/**
|
|
177
|
+
* Optional space ID to validate against the request.
|
|
178
|
+
* If provided, the request must match this space ID to be considered valid.
|
|
179
|
+
*/
|
|
180
|
+
spaceId?: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.
|
|
184
|
+
*
|
|
185
|
+
* This function performs a multi-layered validation to ensure the request originates from
|
|
186
|
+
* the Storyblok Visual Editor by checking for required query parameters and optionally
|
|
187
|
+
* validating the space ID.
|
|
188
|
+
*
|
|
189
|
+
* @param url - The URL object to validate.
|
|
190
|
+
* @param options - Optional validation configuration.
|
|
191
|
+
* @param options.spaceId - If provided, validates that the request's space ID matches this value.
|
|
192
|
+
*
|
|
193
|
+
* @returns `true` if the URL contains all required Storyblok Visual Editor parameters
|
|
194
|
+
* and passes optional space ID validation; `false` otherwise.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');
|
|
199
|
+
*
|
|
200
|
+
* // Basic validation
|
|
201
|
+
* if (isInEditor(url)) {
|
|
202
|
+
* console.log('Valid Storyblok editor request');
|
|
203
|
+
* }
|
|
204
|
+
*
|
|
205
|
+
* // Validation with space ID check
|
|
206
|
+
* if (isInEditor(url, { spaceId: '789' })) {
|
|
207
|
+
* console.log('Valid request for specific space');
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function isInEditor(url: URL, options?: StoryblokValidationOptions): boolean;
|
|
212
|
+
//#endregion
|
|
213
|
+
export { type BridgeParams, type ISbComponentType, type ISbStoryData, isInEditor, loadStoryblokBridge, onStoryblokEditorEvent, storyblokEditable };
|
|
214
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
//#region src/editable.ts
|
|
2
|
+
function storyblokEditable(blok) {
|
|
3
|
+
const editable = blok?._editable;
|
|
4
|
+
if (!editable) return {};
|
|
5
|
+
if (!editable.startsWith("<!--#storyblok#") || !editable.endsWith("-->")) return {};
|
|
6
|
+
try {
|
|
7
|
+
const json = editable.slice(15, -3);
|
|
8
|
+
const options = JSON.parse(json);
|
|
9
|
+
return {
|
|
10
|
+
"data-blok-c": JSON.stringify(options),
|
|
11
|
+
"data-blok-uid": `${options.id}-${options.uid}`
|
|
12
|
+
};
|
|
13
|
+
} catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/loadStoryblokBridge.ts
|
|
20
|
+
let bridgePromise;
|
|
21
|
+
let storedConfig;
|
|
22
|
+
function configsAreEqual(config1, config2) {
|
|
23
|
+
return JSON.stringify(config1) === JSON.stringify(config2);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get or create a StoryblokBridge instance.
|
|
27
|
+
*⚠️ The bridge is a singleton. Configuration is applied only on first load.
|
|
28
|
+
* @param config Optional configuration for the StoryblokBridge.
|
|
29
|
+
* @returns A promise that resolves to a StoryblokBridge instance.
|
|
30
|
+
*/
|
|
31
|
+
function loadStoryblokBridge(config) {
|
|
32
|
+
if (bridgePromise) {
|
|
33
|
+
if (config && !configsAreEqual(config, storedConfig)) throw new Error("[Storyblok] Preview Bridge already initialized with a different configuration. The bridge can only be created once per page and does not support runtime reconfiguration.");
|
|
34
|
+
return bridgePromise;
|
|
35
|
+
}
|
|
36
|
+
storedConfig = config;
|
|
37
|
+
bridgePromise = import("@storyblok/preview-bridge").then(({ default: StoryblokBridge }) => new StoryblokBridge(config)).catch((error) => {
|
|
38
|
+
bridgePromise = void 0;
|
|
39
|
+
storedConfig = void 0;
|
|
40
|
+
throw error;
|
|
41
|
+
});
|
|
42
|
+
return bridgePromise;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/utils/isBrowser.ts
|
|
47
|
+
function isBrowser() {
|
|
48
|
+
return typeof window !== "undefined";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/utils/isInEditor.ts
|
|
53
|
+
/**
|
|
54
|
+
* Required query parameters that must be present in all Storyblok Visual Editor requests.
|
|
55
|
+
*/
|
|
56
|
+
const REQUIRED_STORYBLOK_PARAMS = [
|
|
57
|
+
"_storyblok",
|
|
58
|
+
"_storyblok_c",
|
|
59
|
+
"_storyblok_tk[space_id]"
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.
|
|
63
|
+
*
|
|
64
|
+
* This function performs a multi-layered validation to ensure the request originates from
|
|
65
|
+
* the Storyblok Visual Editor by checking for required query parameters and optionally
|
|
66
|
+
* validating the space ID.
|
|
67
|
+
*
|
|
68
|
+
* @param url - The URL object to validate.
|
|
69
|
+
* @param options - Optional validation configuration.
|
|
70
|
+
* @param options.spaceId - If provided, validates that the request's space ID matches this value.
|
|
71
|
+
*
|
|
72
|
+
* @returns `true` if the URL contains all required Storyblok Visual Editor parameters
|
|
73
|
+
* and passes optional space ID validation; `false` otherwise.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');
|
|
78
|
+
*
|
|
79
|
+
* // Basic validation
|
|
80
|
+
* if (isInEditor(url)) {
|
|
81
|
+
* console.log('Valid Storyblok editor request');
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* // Validation with space ID check
|
|
85
|
+
* if (isInEditor(url, { spaceId: '789' })) {
|
|
86
|
+
* console.log('Valid request for specific space');
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
function isInEditor(url, options = {}) {
|
|
91
|
+
const params = url.searchParams;
|
|
92
|
+
if (!REQUIRED_STORYBLOK_PARAMS.every((param) => params.has(param))) return false;
|
|
93
|
+
if (options.spaceId && params.get("_storyblok_tk[space_id]") !== options.spaceId) return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/utils/canUseStoryblokBridge.ts
|
|
99
|
+
function canUseStoryblokBridge() {
|
|
100
|
+
if (!isBrowser()) return false;
|
|
101
|
+
return isInEditor(new URL(window.location.href));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/onStoryblokEditorEvent.ts
|
|
106
|
+
/**
|
|
107
|
+
* Internal listener registry for Storyblok `input` events.
|
|
108
|
+
* Each listener receives the updated story data from the Visual Editor.
|
|
109
|
+
*/
|
|
110
|
+
const inputListeners = /* @__PURE__ */ new Set();
|
|
111
|
+
/**
|
|
112
|
+
* Tracks whether the Storyblok Preview Bridge event listeners
|
|
113
|
+
* have already been registered.
|
|
114
|
+
*/
|
|
115
|
+
let bridgeInitPromise;
|
|
116
|
+
/**
|
|
117
|
+
* Initializes the Storyblok Preview Bridge and attaches event listeners.
|
|
118
|
+
*
|
|
119
|
+
* This function ensures that the bridge is only initialized once per page.
|
|
120
|
+
*
|
|
121
|
+
* Registered events:
|
|
122
|
+
* - `input` → Dispatches updated story data to all registered listeners.
|
|
123
|
+
* - `change` → Forces a full page reload.
|
|
124
|
+
* - `published` → Forces a full page reload.
|
|
125
|
+
*
|
|
126
|
+
* @param bridgeOptions Optional configuration for the Preview Bridge.
|
|
127
|
+
*/
|
|
128
|
+
async function initializeBridge(bridgeOptions) {
|
|
129
|
+
if (!canUseStoryblokBridge()) return;
|
|
130
|
+
if (bridgeInitPromise) return bridgeInitPromise;
|
|
131
|
+
bridgeInitPromise = (async () => {
|
|
132
|
+
(await loadStoryblokBridge(bridgeOptions)).on([
|
|
133
|
+
"input",
|
|
134
|
+
"change",
|
|
135
|
+
"published"
|
|
136
|
+
], (event) => {
|
|
137
|
+
if (!event) return;
|
|
138
|
+
if (event.action === "input" && event.story) {
|
|
139
|
+
for (const listener of inputListeners) listener(event.story);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (event.action === "change" || event.action === "published") window.location.reload();
|
|
143
|
+
});
|
|
144
|
+
})();
|
|
145
|
+
return bridgeInitPromise;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Registers a callback for Storyblok Visual Editor live preview updates.
|
|
149
|
+
*
|
|
150
|
+
* This utility connects to the Storyblok Preview Bridge and listens
|
|
151
|
+
* for Visual Editor events.
|
|
152
|
+
*
|
|
153
|
+
* Behavior:
|
|
154
|
+
* - **input** → Calls the provided callback with the updated story data.
|
|
155
|
+
* - **change** → Reloads the page.
|
|
156
|
+
* - **published** → Reloads the page.
|
|
157
|
+
*
|
|
158
|
+
* Multiple listeners can be registered simultaneously. Each call returns
|
|
159
|
+
* a cleanup function that removes the registered listener.
|
|
160
|
+
*
|
|
161
|
+
* @typeParam T - The Storyblok component schema type.
|
|
162
|
+
*
|
|
163
|
+
* @param callback
|
|
164
|
+
* Callback executed when the Visual Editor sends an `input` event.
|
|
165
|
+
*
|
|
166
|
+
* @param bridgeOptions
|
|
167
|
+
* Optional configuration for the Storyblok Preview Bridge.
|
|
168
|
+
* This configuration is applied **only during the first initialization**.
|
|
169
|
+
*
|
|
170
|
+
* @returns
|
|
171
|
+
* A cleanup function that removes the registered listener.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* const cleanup = await onStoryblokEditorEvent((story) => {
|
|
176
|
+
* console.log('Live updated story:', story)
|
|
177
|
+
* })
|
|
178
|
+
*
|
|
179
|
+
* // later
|
|
180
|
+
* cleanup()
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
async function onStoryblokEditorEvent(callback, bridgeOptions) {
|
|
184
|
+
await initializeBridge(bridgeOptions);
|
|
185
|
+
const listener = (story) => {
|
|
186
|
+
callback(story);
|
|
187
|
+
};
|
|
188
|
+
inputListeners.add(listener);
|
|
189
|
+
return () => {
|
|
190
|
+
inputListeners.delete(listener);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
export { isInEditor, loadStoryblokBridge, onStoryblokEditorEvent, storyblokEditable };
|
|
196
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/editable.ts","../src/loadStoryblokBridge.ts","../src/utils/isBrowser.ts","../src/utils/isInEditor.ts","../src/utils/canUseStoryblokBridge.ts","../src/onStoryblokEditorEvent.ts"],"sourcesContent":["interface Blok {\n _editable?: string;\n}\n\ninterface EditableOptions {\n id: string;\n uid: string;\n}\n\nexport default function storyblokEditable(blok?: Blok) {\n const editable = blok?._editable;\n if (!editable) {\n return {};\n }\n\n const prefix = '<!--#storyblok#';\n const suffix = '-->';\n\n if (!editable.startsWith(prefix) || !editable.endsWith(suffix)) {\n return {};\n }\n\n try {\n const json = editable.slice(prefix.length, -suffix.length);\n const options = JSON.parse(json) as EditableOptions;\n\n return {\n 'data-blok-c': JSON.stringify(options),\n 'data-blok-uid': `${options.id}-${options.uid}`,\n };\n }\n catch {\n return {};\n }\n}\n","import type StoryblokBridge from '@storyblok/preview-bridge';\nimport type { BridgeParams } from '@storyblok/preview-bridge';\n\nlet bridgePromise: Promise<StoryblokBridge> | undefined;\nlet storedConfig: BridgeParams | undefined;\nfunction configsAreEqual(config1: BridgeParams | undefined, config2: BridgeParams | undefined): boolean {\n return JSON.stringify(config1) === JSON.stringify(config2);\n}\n\n/**\n * Get or create a StoryblokBridge instance.\n *⚠️ The bridge is a singleton. Configuration is applied only on first load.\n * @param config Optional configuration for the StoryblokBridge.\n * @returns A promise that resolves to a StoryblokBridge instance.\n */\nexport function loadStoryblokBridge(config?: BridgeParams) {\n if (bridgePromise) {\n if (config && !configsAreEqual(config, storedConfig)) {\n throw new Error(\n '[Storyblok] Preview Bridge already initialized with a different configuration. '\n + 'The bridge can only be created once per page and does not support runtime reconfiguration.',\n );\n }\n return bridgePromise;\n }\n\n storedConfig = config;\n\n bridgePromise = import('@storyblok/preview-bridge')\n .then(({ default: StoryblokBridge }) => new StoryblokBridge(config))\n .catch((error) => {\n bridgePromise = undefined;\n storedConfig = undefined;\n throw error;\n });\n\n return bridgePromise;\n}\n","export function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n","/**\n * Options for validating Storyblok Visual Editor requests.\n */\ninterface StoryblokValidationOptions {\n /**\n * Optional space ID to validate against the request.\n * If provided, the request must match this space ID to be considered valid.\n */\n spaceId?: string;\n}\n\n/**\n * Required query parameters that must be present in all Storyblok Visual Editor requests.\n */\nconst REQUIRED_STORYBLOK_PARAMS = ['_storyblok', '_storyblok_c', '_storyblok_tk[space_id]'] as const;\n\n/**\n * Validates whether a given URL is a legitimate request from the Storyblok Visual Editor.\n *\n * This function performs a multi-layered validation to ensure the request originates from\n * the Storyblok Visual Editor by checking for required query parameters and optionally\n * validating the space ID.\n *\n * @param url - The URL object to validate.\n * @param options - Optional validation configuration.\n * @param options.spaceId - If provided, validates that the request's space ID matches this value.\n *\n * @returns `true` if the URL contains all required Storyblok Visual Editor parameters\n * and passes optional space ID validation; `false` otherwise.\n *\n * @example\n * ```typescript\n * const url = new URL('https://example.com/?_storyblok=123&_storyblok_c=456&_storyblok_tk[space_id]=789');\n *\n * // Basic validation\n * if (isInEditor(url)) {\n * console.log('Valid Storyblok editor request');\n * }\n *\n * // Validation with space ID check\n * if (isInEditor(url, { spaceId: '789' })) {\n * console.log('Valid request for specific space');\n * }\n * ```\n */\nexport function isInEditor(\n url: URL,\n options: StoryblokValidationOptions = {},\n): boolean {\n const params = url.searchParams;\n\n // Early return: Check all required parameters exist\n const hasRequiredParams = REQUIRED_STORYBLOK_PARAMS.every(param => params.has(param));\n\n if (!hasRequiredParams) {\n return false;\n }\n\n // Optional space ID validation\n if (options.spaceId && params.get('_storyblok_tk[space_id]') !== options.spaceId) {\n return false;\n }\n\n return true;\n}\n","import { isBrowser } from './isBrowser';\nimport { isInEditor } from './isInEditor';\n\nexport function canUseStoryblokBridge(): boolean {\n if (!isBrowser()) {\n return false;\n }\n return isInEditor(new URL(window.location.href));\n}\n","import type { BridgeParams } from '@storyblok/preview-bridge';\nimport type { ISbComponentType, ISbStoryData } from 'storyblok-js-client';\n\nimport { loadStoryblokBridge } from './loadStoryblokBridge';\nimport { canUseStoryblokBridge } from './utils/canUseStoryblokBridge';\n\n/**\n * Internal listener registry for Storyblok `input` events.\n * Each listener receives the updated story data from the Visual Editor.\n */\nconst inputListeners = new Set<(story: ISbStoryData) => void>();\n\n/**\n * Tracks whether the Storyblok Preview Bridge event listeners\n * have already been registered.\n */\nlet bridgeInitPromise: Promise<void> | undefined;\n\n/**\n * Initializes the Storyblok Preview Bridge and attaches event listeners.\n *\n * This function ensures that the bridge is only initialized once per page.\n *\n * Registered events:\n * - `input` → Dispatches updated story data to all registered listeners.\n * - `change` → Forces a full page reload.\n * - `published` → Forces a full page reload.\n *\n * @param bridgeOptions Optional configuration for the Preview Bridge.\n */\nasync function initializeBridge(bridgeOptions?: BridgeParams): Promise<void> {\n if (!canUseStoryblokBridge()) {\n return;\n }\n\n // If initialization already started, reuse it\n if (bridgeInitPromise) {\n return bridgeInitPromise;\n }\n\n bridgeInitPromise = (async () => {\n const bridge = await loadStoryblokBridge(bridgeOptions);\n\n bridge.on(['input', 'change', 'published'], (event) => {\n if (!event) {\n return;\n }\n\n if (event.action === 'input' && event.story) {\n for (const listener of inputListeners) {\n listener(event.story as ISbStoryData);\n }\n return;\n }\n\n if (event.action === 'change' || event.action === 'published') {\n window.location.reload();\n }\n });\n })();\n\n return bridgeInitPromise;\n}\n\n/**\n * Registers a callback for Storyblok Visual Editor live preview updates.\n *\n * This utility connects to the Storyblok Preview Bridge and listens\n * for Visual Editor events.\n *\n * Behavior:\n * - **input** → Calls the provided callback with the updated story data.\n * - **change** → Reloads the page.\n * - **published** → Reloads the page.\n *\n * Multiple listeners can be registered simultaneously. Each call returns\n * a cleanup function that removes the registered listener.\n *\n * @typeParam T - The Storyblok component schema type.\n *\n * @param callback\n * Callback executed when the Visual Editor sends an `input` event.\n *\n * @param bridgeOptions\n * Optional configuration for the Storyblok Preview Bridge.\n * This configuration is applied **only during the first initialization**.\n *\n * @returns\n * A cleanup function that removes the registered listener.\n *\n * @example\n * ```ts\n * const cleanup = await onStoryblokEditorEvent((story) => {\n * console.log('Live updated story:', story)\n * })\n *\n * // later\n * cleanup()\n * ```\n */\nexport async function onStoryblokEditorEvent<\n T extends ISbComponentType<string> = ISbComponentType<string>,\n>(\n callback: (story: ISbStoryData<T>) => void,\n bridgeOptions?: BridgeParams,\n): Promise<() => void> {\n await initializeBridge(bridgeOptions);\n\n const listener = (story: ISbStoryData) => {\n callback(story as ISbStoryData<T>);\n };\n\n inputListeners.add(listener);\n\n return () => {\n inputListeners.delete(listener);\n };\n}\n"],"mappings":";AASA,SAAwB,kBAAkB,MAAa;CACrD,MAAM,WAAW,MAAM;AACvB,KAAI,CAAC,SACH,QAAO,EAAE;AAMX,KAAI,CAAC,SAAS,WAHC,kBAGiB,IAAI,CAAC,SAAS,SAF/B,MAE+C,CAC5D,QAAO,EAAE;AAGX,KAAI;EACF,MAAM,OAAO,SAAS,MAAM,IAAe,GAAe;EAC1D,MAAM,UAAU,KAAK,MAAM,KAAK;AAEhC,SAAO;GACL,eAAe,KAAK,UAAU,QAAQ;GACtC,iBAAiB,GAAG,QAAQ,GAAG,GAAG,QAAQ;GAC3C;SAEG;AACJ,SAAO,EAAE;;;;;;AC7Bb,IAAI;AACJ,IAAI;AACJ,SAAS,gBAAgB,SAAmC,SAA4C;AACtG,QAAO,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ;;;;;;;;AAS5D,SAAgB,oBAAoB,QAAuB;AACzD,KAAI,eAAe;AACjB,MAAI,UAAU,CAAC,gBAAgB,QAAQ,aAAa,CAClD,OAAM,IAAI,MACR,4KAED;AAEH,SAAO;;AAGT,gBAAe;AAEf,iBAAgB,OAAO,6BACpB,MAAM,EAAE,SAAS,sBAAsB,IAAI,gBAAgB,OAAO,CAAC,CACnE,OAAO,UAAU;AAChB,kBAAgB;AAChB,iBAAe;AACf,QAAM;GACN;AAEJ,QAAO;;;;;ACpCT,SAAgB,YAAqB;AACnC,QAAO,OAAO,WAAW;;;;;;;;ACa3B,MAAM,4BAA4B;CAAC;CAAc;CAAgB;CAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B3F,SAAgB,WACd,KACA,UAAsC,EAAE,EAC/B;CACT,MAAM,SAAS,IAAI;AAKnB,KAAI,CAFsB,0BAA0B,OAAM,UAAS,OAAO,IAAI,MAAM,CAAC,CAGnF,QAAO;AAIT,KAAI,QAAQ,WAAW,OAAO,IAAI,0BAA0B,KAAK,QAAQ,QACvE,QAAO;AAGT,QAAO;;;;;AC5DT,SAAgB,wBAAiC;AAC/C,KAAI,CAAC,WAAW,CACd,QAAO;AAET,QAAO,WAAW,IAAI,IAAI,OAAO,SAAS,KAAK,CAAC;;;;;;;;;ACGlD,MAAM,iCAAiB,IAAI,KAAoC;;;;;AAM/D,IAAI;;;;;;;;;;;;;AAcJ,eAAe,iBAAiB,eAA6C;AAC3E,KAAI,CAAC,uBAAuB,CAC1B;AAIF,KAAI,kBACF,QAAO;AAGT,sBAAqB,YAAY;AAG/B,GAFe,MAAM,oBAAoB,cAAc,EAEhD,GAAG;GAAC;GAAS;GAAU;GAAY,GAAG,UAAU;AACrD,OAAI,CAAC,MACH;AAGF,OAAI,MAAM,WAAW,WAAW,MAAM,OAAO;AAC3C,SAAK,MAAM,YAAY,eACrB,UAAS,MAAM,MAAsB;AAEvC;;AAGF,OAAI,MAAM,WAAW,YAAY,MAAM,WAAW,YAChD,QAAO,SAAS,QAAQ;IAE1B;KACA;AAEJ,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCT,eAAsB,uBAGpB,UACA,eACqB;AACrB,OAAM,iBAAiB,cAAc;CAErC,MAAM,YAAY,UAAwB;AACxC,WAAS,MAAyB;;AAGpC,gBAAe,IAAI,SAAS;AAE5B,cAAa;AACX,iBAAe,OAAO,SAAS"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storyblok/live-preview",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"private": false,
|
|
6
|
+
"description": "Official Live Preview integration for the Storyblok Headless CMS",
|
|
7
|
+
"author": "Storyblok",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/storyblok/monoblok/tree/main/packages/live-preview#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/storyblok/monoblok.git",
|
|
13
|
+
"directory": "packages/live-preview"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/storyblok/monoblok/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"live-preview",
|
|
20
|
+
"storyblok"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/index.cjs",
|
|
30
|
+
"module": "./dist/index.mjs",
|
|
31
|
+
"types": "./dist/index.d.cts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@storyblok/preview-bridge": "^2.1.6"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^24.11.0",
|
|
43
|
+
"bumpp": "^10.4.1",
|
|
44
|
+
"eslint": "^9.39.2",
|
|
45
|
+
"tsdown": "^0.20.3",
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vitest": "^4.0.18",
|
|
48
|
+
"@storyblok/eslint-config": "0.4.2",
|
|
49
|
+
"storyblok-js-client": "7.2.4"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"dev": "tsdown --watch",
|
|
54
|
+
"test": "vitest",
|
|
55
|
+
"lint": "eslint .",
|
|
56
|
+
"lint:fix": "eslint . --fix",
|
|
57
|
+
"typecheck": "tsc --noEmit"
|
|
58
|
+
}
|
|
59
|
+
}
|