@storyblok/live-preview 0.1.7 → 0.3.0-alpha.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/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1233 -114
- package/dist/index.d.mts +1233 -114
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -3
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
//#region src/editable.ts
|
|
4
|
-
function storyblokEditable(
|
|
5
|
-
const editable =
|
|
4
|
+
function storyblokEditable(block) {
|
|
5
|
+
const editable = block?._editable;
|
|
6
6
|
if (!editable) return {};
|
|
7
7
|
if (!editable.startsWith("<!--#storyblok#") || !editable.endsWith("-->")) return {};
|
|
8
8
|
try {
|
|
@@ -160,7 +160,7 @@ async function initializeBridge(bridgeOptions) {
|
|
|
160
160
|
* Multiple listeners can be registered simultaneously. Each call returns
|
|
161
161
|
* a cleanup function that removes the registered listener.
|
|
162
162
|
*
|
|
163
|
-
* @typeParam
|
|
163
|
+
* @typeParam TStory - The schema-aware {@link Story} type to type the payload against.
|
|
164
164
|
*
|
|
165
165
|
* @param callback
|
|
166
166
|
* Callback executed when the Visual Editor sends an `input` event.
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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"}
|
|
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":["import type { BlockContent } from './generated/types/field';\n\ninterface EditableOptions {\n id: string;\n uid: string;\n}\n\nexport default function storyblokEditable(block?: Pick<BlockContent, '_editable'>) {\n const editable = block?._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 { Prettify } from './generated/types/_utils';\nimport type { Story } from './generated/types/story';\n\nimport { loadStoryblokBridge } from './loadStoryblokBridge';\nimport { canUseStoryblokBridge } from './utils/canUseStoryblokBridge';\n\n/**\n * The story payload delivered by the Visual Editor `input` event.\n *\n * The Preview Bridge streams a story whose full runtime shape is not\n * guaranteed to match the CDN API. Only `id`, `uuid`, and `content` are\n * relied upon here — their types are sourced from the supplied {@link Story}\n * generic — while every other field is left as `unknown` rather than\n * over-promising a fully typed CDN story.\n *\n * @typeParam TStory - The schema-aware {@link Story} to source field types from.\n */\nexport type LivePreviewStory<TStory extends Story = Story> = Prettify<\n Pick<TStory, 'id'>\n & Partial<Pick<TStory, 'uuid' | 'content'>>\n & {\n [key: string]: unknown;\n }\n>;\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: LivePreviewStory) => 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 LivePreviewStory);\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 TStory - The schema-aware {@link Story} type to type the payload against.\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<TStory extends Story = Story>(\n callback: (story: LivePreviewStory<TStory>) => void,\n bridgeOptions?: BridgeParams,\n): Promise<() => void> {\n await initializeBridge(bridgeOptions);\n\n const listener = (story: LivePreviewStory<TStory>) => {\n callback(story);\n };\n\n inputListeners.add(listener);\n\n return () => {\n inputListeners.delete(listener);\n };\n}\n"],"mappings":";;;AAOA,SAAwB,kBAAkB,OAAyC;CACjF,MAAM,WAAW,OAAO;AACxB,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;;;;;;AC3Bb,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;;;;;;;;;ACuBlD,MAAM,iCAAiB,IAAI,KAAwC;;;;;AAMnE,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,MAA0B;AAE3C;;AAGF,OAAI,MAAM,WAAW,YAAY,MAAM,WAAW,YAChD,QAAO,SAAS,QAAQ;IAE1B;KACA;AAEJ,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCT,eAAsB,uBACpB,UACA,eACqB;AACrB,OAAM,iBAAiB,cAAc;CAErC,MAAM,YAAY,UAAoC;AACpD,WAAS,MAAM;;AAGjB,gBAAe,IAAI,SAAS;AAE5B,cAAa;AACX,iBAAe,OAAO,SAAS"}
|