diff --git a/cypress.config.ts b/cypress.config.ts
index f5e67ba7..afd236f8 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -1,5 +1,5 @@
import { defineConfig } from 'cypress';
-import { svelte } from '@sveltejs/vite-plugin-svelte'
+import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
e2e: {
@@ -12,8 +12,8 @@ export default defineConfig({
bundler: 'vite',
viteConfig: () => {
return {
- plugins: [svelte()]
- }
+ plugins: [svelte()],
+ };
},
},
},
diff --git a/cypress/component/page.cy.ts b/cypress/component/page.cy.ts
index e68d1b24..75b588b2 100644
--- a/cypress/component/page.cy.ts
+++ b/cypress/component/page.cy.ts
@@ -1,7 +1,7 @@
/**
Cypress is not working well with Svelte 5, so for now no StoryblokComponent tests are available.
@todo Fix Cypress to work with Svelte 5
-*/
+ */
describe('Basic Test', () => {
it('can find an element', () => {
// Create a basic element
diff --git a/package.json b/package.json
index 0b82a2a3..7839f623 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
"cypress": "^13.17.0",
"eslint": "^9.18.0",
"eslint-plugin-svelte": "^2.36.0",
+ "jsdom": "^26.0.0",
"publint": "^0.2.0",
"simple-git-hooks": "^2.11.1",
"start-server-and-test": "^2.0.10",
@@ -88,7 +89,11 @@
"@commitlint/config-conventional"
],
"rules": {
- "body-max-line-length": [2, "always", 200]
+ "body-max-line-length": [
+ 2,
+ "always",
+ 200
+ ]
}
},
"release": {
diff --git a/playground/sveltekit/src/lib/Page.svelte b/playground/sveltekit/src/lib/Page.svelte
index d77b0e80..c3b09bb8 100644
--- a/playground/sveltekit/src/lib/Page.svelte
+++ b/playground/sveltekit/src/lib/Page.svelte
@@ -1,13 +1,19 @@
-
-
- {#each blok.body as item}
-
-
-
- {/each}
-
+{#key blok}
+
+ {#each blok.body as bodyBlok}
+
+ {/each}
+
{@html resolvedRichText}
+
+{/key}
diff --git a/playground/sveltekit/src/routes/+layout.svelte b/playground/sveltekit/src/routes/+layout.svelte
index e52c8ac2..da879e26 100644
--- a/playground/sveltekit/src/routes/+layout.svelte
+++ b/playground/sveltekit/src/routes/+layout.svelte
@@ -1,8 +1,7 @@
-{@render children()}
+{@render children?.()}
diff --git a/playground/sveltekit/src/routes/+layout.ts b/playground/sveltekit/src/routes/+layout.ts
index d700ca05..0a19e0f4 100644
--- a/playground/sveltekit/src/routes/+layout.ts
+++ b/playground/sveltekit/src/routes/+layout.ts
@@ -1,5 +1,5 @@
import type { LayoutLoad } from './$types';
-import { storyblokInit, apiPlugin, useStoryblokApi } from "@storyblok/svelte";
+import { storyblokInit, apiPlugin, getStoryblokApi } from "@storyblok/svelte";
import Teaser from '$lib/Teaser.svelte';
import Page from '$lib/Page.svelte';
import Feature from '$lib/Feature.svelte';
@@ -26,6 +26,6 @@ export const load: LayoutLoad = () => {
});
return {
- storyblokApi: useStoryblokApi()
+ storyblokApi: getStoryblokApi(),
};
};
diff --git a/playground/sveltekit/src/routes/[...slug]/+page.svelte b/playground/sveltekit/src/routes/[...slug]/+page.svelte
index 86b3b6c6..13114081 100644
--- a/playground/sveltekit/src/routes/[...slug]/+page.svelte
+++ b/playground/sveltekit/src/routes/[...slug]/+page.svelte
@@ -3,7 +3,7 @@
import type { PageData } from './$types';
import { onMount } from 'svelte';
- let { data }: { data: PageData } = $props();
+ let { data = $bindable() }: { data: PageData } = $props();
let story = $state(data.story);
let loaded = $state(false);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 525a7716..31507703 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,7 +29,7 @@ importers:
version: 12.1.2(rollup@4.30.1)(tslib@2.8.1)(typescript@5.7.3)
'@storyblok/eslint-config':
specifier: ^0.3.0
- version: 0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))
+ version: 0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))
'@sveltejs/adapter-auto':
specifier: ^3.3.1
version: 3.3.1(@sveltejs/kit@2.15.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.17.4)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0)))(svelte@5.17.4)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0)))
@@ -51,6 +51,9 @@ importers:
eslint-plugin-svelte:
specifier: ^2.36.0
version: 2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)
+ jsdom:
+ specifier: ^26.0.0
+ version: 26.0.0
publint:
specifier: ^0.2.0
version: 0.2.12
@@ -77,7 +80,7 @@ importers:
version: 4.5.0(@types/node@22.10.6)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0))
vitest:
specifier: ^2.1.8
- version: 2.1.8(@types/node@22.10.6)
+ version: 2.1.8(@types/node@22.10.6)(jsdom@26.0.0)
playground/sveltekit:
dependencies:
@@ -165,6 +168,9 @@ packages:
'@antfu/utils@0.7.10':
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+ '@asamuzakjp/css-color@2.8.2':
+ resolution: {integrity: sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==}
+
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
@@ -267,6 +273,34 @@ packages:
resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==}
engines: {node: '>=v18'}
+ '@csstools/color-helpers@5.0.1':
+ resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.1':
+ resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-color-parser@3.0.7':
+ resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4':
+ resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-tokenizer@3.0.3':
+ resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
+ engines: {node: '>=18'}
+
'@cypress/request@3.0.7':
resolution: {integrity: sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==}
engines: {node: '>= 6'}
@@ -1099,6 +1133,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.3:
+ resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+ engines: {node: '>= 14'}
+
aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@@ -1429,6 +1467,10 @@ packages:
engines: {node: '>=4'}
hasBin: true
+ cssstyle@4.2.1:
+ resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==}
+ engines: {node: '>=18'}
+
cypress@13.17.0:
resolution: {integrity: sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==}
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
@@ -1442,6 +1484,10 @@ packages:
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
engines: {node: '>=0.10'}
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
@@ -1465,6 +1511,9 @@ packages:
supports-color:
optional: true
+ decimal.js@10.4.3:
+ resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+
decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
@@ -2057,10 +2106,22 @@ packages:
hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
http-signature@1.4.0:
resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==}
engines: {node: '>=0.10'}
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
human-signals@1.1.1:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
engines: {node: '>=8.12.0'}
@@ -2069,6 +2130,10 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -2153,6 +2218,9 @@ packages:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
@@ -2201,6 +2269,15 @@ packages:
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
engines: {node: '>=12.0.0'}
+ jsdom@26.0.0:
+ resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -2355,6 +2432,10 @@ packages:
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+ lru-cache@11.0.2:
+ resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==}
+ engines: {node: 20 || >=22}
+
lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@@ -2594,6 +2675,9 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+ nwsapi@2.2.16:
+ resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
+
object-inspect@1.13.3:
resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
engines: {node: '>= 0.4'}
@@ -2663,6 +2747,9 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
+ parse5@7.2.1:
+ resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
+
pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
@@ -2879,6 +2966,9 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -2895,6 +2985,10 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scslre@0.3.0:
resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
engines: {node: ^14.0.0 || >=16.0.0}
@@ -3089,6 +3183,9 @@ packages:
resolution: {integrity: sha512-ne4IhhVBwzpUByjo1ocxQnqRoWsRilc9Ry1j+0uPWhHmg4jS/nnlSwYYfx7Ium8okCZ4hYM89rg0B5G0hQzk+g==}
engines: {node: '>=18'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
synckit@0.6.2:
resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==}
engines: {node: '>=12.20'}
@@ -3159,6 +3256,10 @@ packages:
resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==}
engines: {node: '>=16'}
+ tr46@5.0.0:
+ resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+ engines: {node: '>=18'}
+
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -3399,11 +3500,31 @@ packages:
peerDependencies:
eslint: '>=6.0.0'
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
wait-on@8.0.2:
resolution: {integrity: sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==}
engines: {node: '>=12.0.0'}
hasBin: true
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.1.0:
+ resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==}
+ engines: {node: '>=18'}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -3429,10 +3550,29 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
@@ -3485,7 +3625,7 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
- '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))':
+ '@antfu/eslint-config@3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))':
dependencies:
'@antfu/install-pkg': 0.4.1
'@clack/prompts': 0.7.0
@@ -3494,7 +3634,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.13.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/eslint-plugin': 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/parser': 8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
- '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))
+ '@vitest/eslint-plugin': 1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))
eslint: 9.18.0(jiti@2.4.2)
eslint-config-flat-gitignore: 0.3.0(eslint@9.18.0(jiti@2.4.2))
eslint-flat-config-utils: 0.4.0
@@ -3542,6 +3682,14 @@ snapshots:
'@antfu/utils@0.7.10': {}
+ '@asamuzakjp/css-color@2.8.2':
+ dependencies:
+ '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+ lru-cache: 11.0.2
+
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
@@ -3685,6 +3833,26 @@ snapshots:
'@types/conventional-commits-parser': 5.0.1
chalk: 5.4.1
+ '@csstools/color-helpers@5.0.1': {}
+
+ '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/color-helpers': 5.0.1
+ '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-tokenizer@3.0.3': {}
+
'@cypress/request@3.0.7':
dependencies:
aws-sign2: 0.7.0
@@ -4138,9 +4306,9 @@ snapshots:
'@sideway/pinpoint@2.0.0': {}
- '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))':
+ '@storyblok/eslint-config@0.3.0(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))':
dependencies:
- '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))
+ '@antfu/eslint-config': 3.6.2(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@2.4.2)))(eslint-plugin-svelte@2.46.1(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4))(eslint@9.18.0(jiti@2.4.2))(svelte@5.17.4)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))
eslint: 9.18.0(jiti@2.4.2)
eslint-plugin-format: 0.1.3(eslint@9.18.0(jiti@2.4.2))
transitivePeerDependencies:
@@ -4357,13 +4525,13 @@ snapshots:
'@typescript-eslint/types': 8.20.0
eslint-visitor-keys: 4.2.0
- '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6))':
+ '@vitest/eslint-plugin@1.1.25(@typescript-eslint/utils@8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0))':
dependencies:
'@typescript-eslint/utils': 8.20.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3)
eslint: 9.18.0(jiti@2.4.2)
optionalDependencies:
typescript: 5.7.3
- vitest: 2.1.8(@types/node@22.10.6)
+ vitest: 2.1.8(@types/node@22.10.6)(jsdom@26.0.0)
'@vitest/expect@2.1.8':
dependencies:
@@ -4482,6 +4650,8 @@ snapshots:
acorn@8.14.0: {}
+ agent-base@7.1.3: {}
+
aggregate-error@3.1.0:
dependencies:
clean-stack: 2.2.0
@@ -4780,6 +4950,11 @@ snapshots:
cssesc@3.0.0: {}
+ cssstyle@4.2.1:
+ dependencies:
+ '@asamuzakjp/css-color': 2.8.2
+ rrweb-cssom: 0.8.0
+
cypress@13.17.0:
dependencies:
'@cypress/request': 3.0.7
@@ -4832,6 +5007,11 @@ snapshots:
dependencies:
assert-plus: 1.0.0
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.1.0
+
dayjs@1.11.13: {}
de-indent@1.0.2: {}
@@ -4848,6 +5028,8 @@ snapshots:
optionalDependencies:
supports-color: 8.1.1
+ decimal.js@10.4.3: {}
+
decode-named-character-reference@1.0.2:
dependencies:
character-entities: 2.0.2
@@ -5583,16 +5765,38 @@ snapshots:
hosted-git-info@2.8.9: {}
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.0(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
http-signature@1.4.0:
dependencies:
assert-plus: 1.0.0
jsprim: 2.0.2
sshpk: 1.18.0
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.4.0(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
human-signals@1.1.1: {}
human-signals@2.1.0: {}
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
ieee754@1.2.1: {}
ignore-walk@5.0.1:
@@ -5654,6 +5858,8 @@ snapshots:
is-path-inside@3.0.3: {}
+ is-potential-custom-element-name@1.0.1: {}
+
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.6
@@ -5694,6 +5900,34 @@ snapshots:
jsdoc-type-pratt-parser@4.1.0: {}
+ jsdom@26.0.0:
+ dependencies:
+ cssstyle: 4.2.1
+ data-urls: 5.0.0
+ decimal.js: 10.4.3
+ form-data: 4.0.1
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.16
+ parse5: 7.2.1
+ rrweb-cssom: 0.8.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 5.1.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.1.0
+ ws: 8.18.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
jsesc@0.5.0: {}
jsesc@3.1.0: {}
@@ -5833,6 +6067,8 @@ snapshots:
dependencies:
tslib: 2.8.1
+ lru-cache@11.0.2: {}
+
lru-cache@6.0.0:
dependencies:
yallist: 4.0.0
@@ -6235,6 +6471,8 @@ snapshots:
dependencies:
boolbase: 1.0.0
+ nwsapi@2.2.16: {}
+
object-inspect@1.13.3: {}
once@1.4.0:
@@ -6306,6 +6544,10 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse5@7.2.1:
+ dependencies:
+ entities: 4.5.0
+
pascal-case@3.1.2:
dependencies:
no-case: 3.0.4
@@ -6500,6 +6742,8 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.30.1
fsevents: 2.3.3
+ rrweb-cssom@0.8.0: {}
+
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -6516,6 +6760,10 @@ snapshots:
safer-buffer@2.1.2: {}
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scslre@0.3.0:
dependencies:
'@eslint-community/regexpp': 4.12.1
@@ -6738,6 +6986,8 @@ snapshots:
magic-string: 0.30.17
zimmerframe: 1.1.2
+ symbol-tree@3.2.4: {}
+
synckit@0.6.2:
dependencies:
tslib: 2.8.1
@@ -6792,6 +7042,10 @@ snapshots:
dependencies:
tldts: 6.1.71
+ tr46@5.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tree-kill@1.2.2: {}
ts-api-utils@2.0.0(typescript@5.7.3):
@@ -6949,7 +7203,7 @@ snapshots:
optionalDependencies:
vite: 6.0.7(@types/node@22.10.6)(jiti@2.4.2)(yaml@2.7.0)
- vitest@2.1.8(@types/node@22.10.6):
+ vitest@2.1.8(@types/node@22.10.6)(jsdom@26.0.0):
dependencies:
'@vitest/expect': 2.1.8
'@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.6))
@@ -6973,6 +7227,7 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.10.6
+ jsdom: 26.0.0
transitivePeerDependencies:
- less
- lightningcss
@@ -6999,6 +7254,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
wait-on@8.0.2(debug@4.4.0):
dependencies:
axios: 1.7.9(debug@4.4.0)
@@ -7009,6 +7268,19 @@ snapshots:
transitivePeerDependencies:
- debug
+ webidl-conversions@7.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.1.0:
+ dependencies:
+ tr46: 5.0.0
+ webidl-conversions: 7.0.0
+
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -7034,8 +7306,14 @@ snapshots:
wrappy@1.0.2: {}
+ ws@8.18.0: {}
+
xml-name-validator@4.0.0: {}
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
y18n@5.0.8: {}
yallist@4.0.0: {}
diff --git a/src/demo.spec.ts b/src/demo.spec.ts
deleted file mode 100644
index aa934263..00000000
--- a/src/demo.spec.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { describe, expect, it } from 'vitest';
-
-describe('sum test', () => {
- it('adds 1 + 2 to equal 3', () => {
- expect(1 + 2).toBe(3);
- });
-});
diff --git a/src/lib/StoryblokComponent.svelte b/src/lib/StoryblokComponent.svelte
index a2ac1407..153b2ac2 100644
--- a/src/lib/StoryblokComponent.svelte
+++ b/src/lib/StoryblokComponent.svelte
@@ -1,19 +1,16 @@
-
+{#if component}
+
+{/if}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 65053fcd..fd97ad74 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1,79 +1,5 @@
-import {
- storyblokEditable as sbEdit,
- storyblokInit as sbInit,
-} from '@storyblok/js';
-
-import type {
- SbBlokData,
- SbSvelteComponentsMap,
- SbSvelteSDKOptions,
- StoryblokClient,
-} from './types';
-
+export * from './storyblok';
export { default as StoryblokComponent } from './StoryblokComponent.svelte';
-
-export const storyblokEditable = (node: HTMLElement, value: SbBlokData) => {
- const updateDom = (value: SbBlokData) => {
- const options = sbEdit(value);
- if (options['data-blok-c']) {
- node.setAttribute('data-blok-c', options['data-blok-c']);
- node.setAttribute('data-blok-uid', options['data-blok-uid']);
- node.classList.add('storyblok__outline');
- }
- };
-
- updateDom(value); // when is mounted
-
- return {
- update(newValue: SbBlokData) {
- // when value changes
- updateDom(newValue);
- },
- };
-};
-
-let storyblokApiInstance: StoryblokClient;
-export const useStoryblokApi = (): StoryblokClient => {
- if (!storyblokApiInstance) {
- // eslint-disable-next-line no-console
- console.log(
- `You can't use getStoryblokApi if you're not loading apiPlugin.`,
- );
- }
- return storyblokApiInstance;
-};
-
-export { useStoryblokApi as getStoryblokApi };
-
-let componentsMap: SbSvelteComponentsMap | CallableFunction = {};
-
-export const storyblokInit = (options: SbSvelteSDKOptions) => {
- const { storyblokApi } = sbInit(options);
- storyblokApiInstance = storyblokApi ?? storyblokApiInstance;
- componentsMap = options.components || {};
-};
-
-export const getComponent = (componentName: string) => {
- let component = null;
- component
- = typeof componentsMap === 'function'
- ? componentsMap()[componentName]
- : componentsMap[componentName];
-
- if (!component) {
- console.error(`You didn't load the ${componentName} component. Please load it in storyblokInit. For example:
-storyblokInit({
- accessToken: "",
- components: {
- "teaser": Teaser
- }
-})
-`);
- }
-
- return component;
-};
-
export * from './types';
export {
apiPlugin,
diff --git a/src/lib/storyblok.ts b/src/lib/storyblok.ts
new file mode 100644
index 00000000..6ef1bc39
--- /dev/null
+++ b/src/lib/storyblok.ts
@@ -0,0 +1,63 @@
+import {
+ storyblokEditable as sbEdit,
+} from '@storyblok/js';
+
+import type {
+ SbBlokData,
+} from './types';
+
+/**
+ * Creates options object for Storyblok editable elements
+ */
+const createEditableOptions = (value: SbBlokData) => {
+ const options = sbEdit(value);
+ return options['data-blok-c']
+ ? {
+ 'data-blok-c': options['data-blok-c'],
+ 'data-blok-uid': options['data-blok-uid'],
+ 'className': 'storyblok__outline',
+ }
+ : null;
+};
+
+/**
+ * Applies Storyblok editable attributes to a DOM node
+ */
+const applyEditableOptions = (node: HTMLElement, options: ReturnType) => {
+ if (!options) {
+ return;
+ }
+
+ node.setAttribute('data-blok-c', options['data-blok-c']);
+ node.setAttribute('data-blok-uid', options['data-blok-uid']);
+ node.classList.add(options.className);
+};
+
+/**
+ * Svelte action to make a component editable in Storyblok
+ */
+export const storyblokEditable = (node: HTMLElement, value: SbBlokData) => {
+ const updateDom = (value: SbBlokData) => {
+ const options = createEditableOptions(value);
+ if (options) {
+ applyEditableOptions(node, options);
+ }
+ };
+
+ updateDom(value);
+
+ return {
+ update: updateDom,
+ };
+};
+
+export {
+ getComponent,
+ getStoryblokApi,
+ storyblokInit,
+} from './storyblokStore';
+
+/**
+ * @deprecated Use getStoryblokApi() instead. This will be removed in a next major version.
+ */
+export { getStoryblokApi as useStoryblokApi } from './storyblokStore';
diff --git a/src/lib/storyblokStore.ts b/src/lib/storyblokStore.ts
new file mode 100644
index 00000000..982a4a81
--- /dev/null
+++ b/src/lib/storyblokStore.ts
@@ -0,0 +1,72 @@
+import { storyblokInit as sbInit } from '@storyblok/js';
+import type {
+ SbSvelteComponentsMap,
+ SbSvelteSDKOptions,
+ StoryblokClient,
+} from './types';
+
+/**
+ * Creates a singleton store for managing Storyblok state
+ */
+const createStoryblokStore = () => {
+ let api: StoryblokClient | null = null;
+ let components: SbSvelteComponentsMap | CallableFunction = {};
+
+ /**
+ * Retrieves the Storyblok API instance
+ */
+ const getApi = (): StoryblokClient => {
+ if (!api) {
+ console.error('Storyblok API not initialized. Make sure to load apiPlugin.');
+ }
+ return api!;
+ };
+
+ /**
+ * Initializes the Storyblok SDK with provided options
+ */
+ const init = (options: SbSvelteSDKOptions) => {
+ const { storyblokApi } = sbInit(options);
+ api = storyblokApi ?? api;
+ components = options.components ?? {};
+ };
+
+ /**
+ * Retrieves a component from the registered components map
+ */
+ const getComponent = (componentName: string) => {
+ const resolvedComponent = typeof components === 'function'
+ ? components()[componentName]
+ : components[componentName];
+
+ if (!resolvedComponent) {
+ console.error(
+ `Component "${componentName}" not found. Please register it in storyblokInit:\n`
+ + `storyblokInit({
+ accessToken: "",
+ components: {
+ "${componentName}": YourComponent
+ }
+ })`,
+ );
+ }
+
+ return resolvedComponent;
+ };
+
+ return {
+ init,
+ getApi,
+ getComponent,
+ };
+};
+
+// Create singleton instance
+const storyblokStore = createStoryblokStore();
+
+// Export store methods
+export const {
+ init: storyblokInit,
+ getApi: getStoryblokApi,
+ getComponent,
+} = storyblokStore;
diff --git a/src/tests/storyblok.init.spec.ts b/src/tests/storyblok.init.spec.ts
new file mode 100644
index 00000000..4bee33bd
--- /dev/null
+++ b/src/tests/storyblok.init.spec.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it, vi } from 'vitest';
+import { getStoryblokApi } from '../lib/storyblokStore';
+
+// We simulate an uninitialized API by returning null
+vi.mock('@storyblok/js', () => ({
+ storyblokInit: vi.fn(() => ({
+ storyblokApi: null,
+ })),
+}));
+
+describe('storyblokStore initialization', () => {
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
+
+ it('should log error if API is not initialized', () => {
+ // API is not initialized
+ getStoryblokApi();
+ expect(consoleError).toHaveBeenCalledWith(
+ 'Storyblok API not initialized. Make sure to load apiPlugin.',
+ );
+ });
+});
diff --git a/src/tests/storyblok.spec.ts b/src/tests/storyblok.spec.ts
new file mode 100644
index 00000000..0000a0a2
--- /dev/null
+++ b/src/tests/storyblok.spec.ts
@@ -0,0 +1,60 @@
+import { describe, expect, it, vi } from 'vitest';
+import { storyblokEditable } from '../lib/storyblok';
+import type { SbBlokData } from '../lib/types';
+
+// Mock @storyblok/js
+vi.mock('@storyblok/js', () => ({
+ storyblokEditable: (blok: SbBlokData) => {
+ if (blok._editable) {
+ return {
+ 'data-blok-c': '1234',
+ 'data-blok-uid': '123-456',
+ 'class': 'storyblok__outline',
+ };
+ }
+ return {};
+ },
+}));
+
+describe('storyblok utilities', () => {
+ describe('storyblokEditable', () => {
+ it('should apply editable attributes to DOM node', () => {
+ // Create a test DOM node
+ const node = document.createElement('div');
+
+ // Mock Storyblok blok data
+ const mockBlok: SbBlokData = {
+ _uid: '123-456',
+ component: 'test-component',
+ _editable: '',
+ };
+
+ // Apply storyblokEditable action
+ storyblokEditable(node, mockBlok);
+
+ // Assert that the correct attributes were applied
+ expect(node.hasAttribute('data-blok-c')).toBe(true);
+ expect(node.hasAttribute('data-blok-uid')).toBe(true);
+ expect(node.classList.contains('storyblok__outline')).toBe(true);
+ });
+
+ it('should not apply attributes when _editable is not present', () => {
+ // Create a test DOM node
+ const node = document.createElement('div');
+
+ // Mock Storyblok blok data without _editable
+ const mockBlok: SbBlokData = {
+ _uid: '123-456',
+ component: 'test-component',
+ };
+
+ // Apply storyblokEditable action
+ storyblokEditable(node, mockBlok);
+
+ // Assert that no attributes were applied
+ expect(node.getAttribute('data-blok-c')).toBe(null);
+ expect(node.getAttribute('data-blok-uid')).toBe(null);
+ expect(node.classList.contains('storyblok__outline')).toBe(false);
+ });
+ });
+});
diff --git a/src/tests/storyblokStore.spec.ts b/src/tests/storyblokStore.spec.ts
new file mode 100644
index 00000000..91f1a3ea
--- /dev/null
+++ b/src/tests/storyblokStore.spec.ts
@@ -0,0 +1,102 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { getComponent, getStoryblokApi, storyblokInit } from '../lib/storyblokStore';
+import type { SbSvelteSDKOptions } from '../lib/types';
+
+// Mock @storyblok/js
+vi.mock('@storyblok/js', () => ({
+ storyblokInit: vi.fn(() => ({
+ storyblokApi: { test: 'api' },
+ })),
+}));
+
+describe('storyblokStore', () => {
+ // Mock console.error to prevent noise in tests
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
+
+ beforeEach(() => {
+ // Clear console.error mock calls between tests
+ consoleError.mockClear();
+ });
+
+ describe('storyblokInit', () => {
+ it('should initialize with components', () => {
+ const TestComponent = class {};
+ const options: SbSvelteSDKOptions = {
+ accessToken: 'test-token',
+ components: {
+ 'test-component': TestComponent,
+ },
+ };
+
+ storyblokInit(options);
+
+ // Verify component is registered by trying to retrieve it
+ const component = getComponent('test-component');
+ expect(component).toBe(TestComponent);
+ });
+
+ it('should initialize with function components', () => {
+ const TestComponent = class {};
+ const options: SbSvelteSDKOptions = {
+ accessToken: 'test-token',
+ components: () => ({
+ 'test-component': TestComponent,
+ }),
+ };
+
+ storyblokInit(options);
+
+ // Verify component is registered by trying to retrieve it
+ const component = getComponent('test-component');
+ expect(component).toBe(TestComponent);
+ });
+
+ it('should initialize without components', () => {
+ const options: SbSvelteSDKOptions = {
+ accessToken: 'test-token',
+ };
+
+ storyblokInit(options);
+
+ // Trying to get a non-existent component should trigger error
+ getComponent('non-existent');
+ expect(consoleError).toHaveBeenCalled();
+ });
+ });
+
+ describe('getComponent', () => {
+ it('should return registered component', () => {
+ const TestComponent = class {};
+ storyblokInit({
+ accessToken: 'test-token',
+ components: {
+ 'test-component': TestComponent,
+ },
+ });
+
+ const component = getComponent('test-component');
+ expect(component).toBe(TestComponent);
+ });
+
+ it('should return undefined and log error for non-existent component', () => {
+ const component = getComponent('non-existent');
+
+ expect(component).toBeUndefined();
+ expect(consoleError).toHaveBeenCalledWith(
+ expect.stringContaining('Component "non-existent" not found'),
+ );
+ });
+ });
+
+ describe('getStoryblokApi', () => {
+ it('should return initialized API', () => {
+ storyblokInit({
+ accessToken: 'test-token',
+ });
+
+ // API is initialized
+ const api = getStoryblokApi();
+ expect(api).toEqual({ test: 'api' });
+ });
+ });
+});
diff --git a/vite.config.ts b/vite.config.ts
index 755809d3..174019ad 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -6,6 +6,7 @@ export default defineConfig({
plugins: [dts({ insertTypesEntry: true }), sveltekit()] as Plugin[],
test: {
- include: ['./src/__tests__/**/*', './src/**/*.spec.ts'],
+ environment: 'jsdom',
+ include: ['./src/tests/**/*', './src/**/*.spec.ts'],
},
});