@toaster-builder/site-runtime 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.
Files changed (70) hide show
  1. package/README.md +38 -0
  2. package/dist/module.d.mts +7 -0
  3. package/dist/module.json +12 -0
  4. package/dist/module.mjs +21 -0
  5. package/dist/runtime/assets/editor/add-block.svg +5 -0
  6. package/dist/runtime/assets/editor/ai-assistant.svg +5 -0
  7. package/dist/runtime/assets/editor/block-settings-center.svg +3 -0
  8. package/dist/runtime/assets/editor/block-settings-path.svg +3 -0
  9. package/dist/runtime/assets/editor/copy-block.svg +10 -0
  10. package/dist/runtime/assets/editor/delete-block.svg +4 -0
  11. package/dist/runtime/assets/editor/hide-block.svg +6 -0
  12. package/dist/runtime/assets/editor/move-block-down.svg +3 -0
  13. package/dist/runtime/assets/editor/move-block-up.svg +3 -0
  14. package/dist/runtime/assets/editor/show-block.svg +4 -0
  15. package/dist/runtime/components/BlockEditorFrame.d.vue.ts +16 -0
  16. package/dist/runtime/components/BlockEditorFrame.vue +418 -0
  17. package/dist/runtime/components/BlockEditorFrame.vue.d.ts +16 -0
  18. package/dist/runtime/components/BlockPlacementProvider.d.vue.ts +16 -0
  19. package/dist/runtime/components/BlockPlacementProvider.vue +12 -0
  20. package/dist/runtime/components/BlockPlacementProvider.vue.d.ts +16 -0
  21. package/dist/runtime/components/PageRegionRenderer.d.vue.ts +16 -0
  22. package/dist/runtime/components/PageRegionRenderer.vue +50 -0
  23. package/dist/runtime/components/PageRegionRenderer.vue.d.ts +16 -0
  24. package/dist/runtime/components/PageRenderer.d.vue.ts +3 -0
  25. package/dist/runtime/components/PageRenderer.vue +65 -0
  26. package/dist/runtime/components/PageRenderer.vue.d.ts +3 -0
  27. package/dist/runtime/composables/useBuilder.d.ts +15 -0
  28. package/dist/runtime/composables/useBuilder.js +27 -0
  29. package/dist/runtime/composables/useBuilderEventService.d.ts +3 -0
  30. package/dist/runtime/composables/useBuilderEventService.js +14 -0
  31. package/dist/runtime/composables/useBuilderState.d.ts +8 -0
  32. package/dist/runtime/composables/useBuilderState.js +21 -0
  33. package/dist/runtime/composables/usePageRegistry.d.ts +6 -0
  34. package/dist/runtime/composables/usePageRegistry.js +76 -0
  35. package/dist/runtime/composables/usePageSettings.d.ts +21 -0
  36. package/dist/runtime/composables/usePageSettings.js +90 -0
  37. package/dist/runtime/events/builder-event.d.ts +9 -0
  38. package/dist/runtime/events/builder-event.js +14 -0
  39. package/dist/runtime/events/builder-form-preview-token-event.d.ts +7 -0
  40. package/dist/runtime/events/builder-form-preview-token-event.js +19 -0
  41. package/dist/runtime/events/builder-page-state-event.d.ts +9 -0
  42. package/dist/runtime/events/builder-page-state-event.js +62 -0
  43. package/dist/runtime/events/builder-ready-ping-event.d.ts +6 -0
  44. package/dist/runtime/events/builder-ready-ping-event.js +10 -0
  45. package/dist/runtime/events/site-options-updated-event.d.ts +8 -0
  46. package/dist/runtime/events/site-options-updated-event.js +18 -0
  47. package/dist/runtime/plugin.d.ts +10 -0
  48. package/dist/runtime/plugin.js +10 -0
  49. package/dist/runtime/server/tsconfig.json +3 -0
  50. package/dist/runtime/services/builder-event-service.d.ts +37 -0
  51. package/dist/runtime/services/builder-event-service.js +104 -0
  52. package/dist/runtime/types/assets.d.ts +5 -0
  53. package/dist/runtime/types/page.d.ts +37 -0
  54. package/dist/runtime/types/page.js +9 -0
  55. package/dist/runtime/utils/block-options.d.ts +2 -0
  56. package/dist/runtime/utils/block-options.js +9 -0
  57. package/dist/runtime/utils/block-placement.d.ts +2 -0
  58. package/dist/runtime/utils/block-placement.js +1 -0
  59. package/dist/runtime/utils/common.d.ts +2 -0
  60. package/dist/runtime/utils/common.js +6 -0
  61. package/dist/runtime/utils/css-variables.d.ts +7 -0
  62. package/dist/runtime/utils/css-variables.js +35 -0
  63. package/dist/runtime/utils/fonts.d.ts +8 -0
  64. package/dist/runtime/utils/fonts.js +80 -0
  65. package/dist/runtime/utils/page-config.d.ts +3 -0
  66. package/dist/runtime/utils/page-config.js +114 -0
  67. package/dist/runtime/utils/page-options.d.ts +3 -0
  68. package/dist/runtime/utils/page-options.js +26 -0
  69. package/dist/types.d.mts +3 -0
  70. package/package.json +65 -0
@@ -0,0 +1,50 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import BlockEditorFrame from "#toaster-site-runtime/components/BlockEditorFrame.vue";
4
+ import BlockPlacementProvider from "#toaster-site-runtime/components/BlockPlacementProvider.vue";
5
+ const props = defineProps({
6
+ builderReady: { type: Boolean, required: true },
7
+ isPlacementEnabled: { type: Function, required: true },
8
+ pagePlacements: { type: Function, required: true },
9
+ placementKey: { type: Function, required: true },
10
+ region: { type: null, required: true },
11
+ resolveBlock: { type: Function, required: true },
12
+ resolveBlockOptions: { type: Function, required: true }
13
+ });
14
+ const placements = computed(() => props.pagePlacements(props.region));
15
+ </script>
16
+
17
+ <template>
18
+ <component :is="region">
19
+ <template
20
+ v-for="(placement, index) in placements"
21
+ :key="placementKey(region, placement)"
22
+ >
23
+ <DevOnly>
24
+ <BlockEditorFrame
25
+ v-if="isPlacementEnabled(placement) || builderReady"
26
+ :block-component="resolveBlock(placement.id)"
27
+ :block-id="placement.id"
28
+ :block-index="index"
29
+ :builder-ready="builderReady"
30
+ :enabled="isPlacementEnabled(placement)"
31
+ :options="resolveBlockOptions(placement)"
32
+ :placement-id="placement.placementId"
33
+ :placement-count="placements.length"
34
+ :region="region"
35
+ />
36
+ <template #fallback>
37
+ <BlockPlacementProvider
38
+ v-if="isPlacementEnabled(placement)"
39
+ :placement-id="placement.placementId"
40
+ >
41
+ <component
42
+ :is="resolveBlock(placement.id)"
43
+ :options="resolveBlockOptions(placement)"
44
+ />
45
+ </BlockPlacementProvider>
46
+ </template>
47
+ </DevOnly>
48
+ </template>
49
+ </component>
50
+ </template>
@@ -0,0 +1,16 @@
1
+ import { type Component } from 'vue';
2
+ import type { BlockOptions, BlockPlacement, PageRegion } from '#toaster-site-runtime/types/page';
3
+ type __VLS_Props = {
4
+ builderReady: boolean;
5
+ isPlacementEnabled: (placement: BlockPlacement) => boolean;
6
+ pagePlacements: (region: PageRegion) => BlockPlacement[];
7
+ placementKey: (region: PageRegion, placement: BlockPlacement) => string;
8
+ region: PageRegion;
9
+ resolveBlock: (id: string) => Component;
10
+ resolveBlockOptions: (placement: BlockPlacement & {
11
+ options?: BlockOptions;
12
+ }) => BlockOptions;
13
+ };
14
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,65 @@
1
+ <script setup>
2
+ import { onBeforeUnmount } from "vue";
3
+ import { useRoute, useSeoMeta } from "#imports";
4
+ import PageRegionRenderer from "#toaster-site-runtime/components/PageRegionRenderer.vue";
5
+ import { useBuilderEventService } from "#toaster-site-runtime/composables/useBuilderEventService";
6
+ import { useBuilderState } from "#toaster-site-runtime/composables/useBuilderState";
7
+ import { usePageRegistry } from "#toaster-site-runtime/composables/usePageRegistry";
8
+ import { usePageSettings } from "#toaster-site-runtime/composables/usePageSettings";
9
+ import { normalizePagePath } from "#toaster-site-runtime/utils/page-options";
10
+ import { pageRegions } from "#toaster-site-runtime/types/page";
11
+ const route = useRoute();
12
+ const currentPath = () => normalizePagePath(route.path);
13
+ const { blockDefaultOptionsRegistry, blockRegistry } = usePageRegistry();
14
+ const {
15
+ builderReady,
16
+ formPreviewToken,
17
+ pageRuntimeState,
18
+ siteOptions
19
+ } = useBuilderState();
20
+ const {
21
+ getDescription,
22
+ getTitle,
23
+ isPlacementEnabled,
24
+ pagePlacements,
25
+ placementKey,
26
+ resolveBlock,
27
+ resolveBlockOptions
28
+ } = usePageSettings({
29
+ blockDefaultOptionsRegistry,
30
+ blockRegistry,
31
+ currentPath,
32
+ pageRuntimeState,
33
+ siteOptions
34
+ });
35
+ if (import.meta.dev) {
36
+ const builderEvents = useBuilderEventService();
37
+ const clearPageContext = builderEvents.setPageContext({
38
+ currentPath,
39
+ formPreviewToken,
40
+ isKnownBlock: (blockId) => blockRegistry.has(blockId),
41
+ pageRuntimeState
42
+ });
43
+ onBeforeUnmount(clearPageContext);
44
+ }
45
+ useSeoMeta({
46
+ title: getTitle,
47
+ description: getDescription
48
+ });
49
+ </script>
50
+
51
+ <template>
52
+ <div class="site-shell">
53
+ <PageRegionRenderer
54
+ v-for="region in pageRegions"
55
+ :key="region"
56
+ :builder-ready="builderReady"
57
+ :is-placement-enabled="isPlacementEnabled"
58
+ :page-placements="pagePlacements"
59
+ :placement-key="placementKey"
60
+ :region="region"
61
+ :resolve-block="resolveBlock"
62
+ :resolve-block-options="resolveBlockOptions"
63
+ />
64
+ </div>
65
+ </template>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import type { BlockOptions } from '#toaster-site-runtime/types/page';
2
+ interface SiteControl {
3
+ id: string;
4
+ type: string;
5
+ }
6
+ interface UseBuilderOptions {
7
+ siteControls: readonly SiteControl[];
8
+ siteOptions: BlockOptions;
9
+ }
10
+ export declare function useBuilder(options: UseBuilderOptions): {
11
+ siteCssVariables: import("vue").ComputedRef<Record<string, string>>;
12
+ siteFontFacesCss: import("vue").ComputedRef<string>;
13
+ siteFontStyleSheets: import("vue").ComputedRef<string[]>;
14
+ };
15
+ export {};
@@ -0,0 +1,27 @@
1
+ import { computed, onBeforeUnmount, onMounted } from "vue";
2
+ import { BuilderEventService } from "#toaster-site-runtime/services/builder-event-service";
3
+ import { collectCssVariables } from "#toaster-site-runtime/utils/css-variables";
4
+ import { collectFontFacesCss, collectFontStyleSheets } from "#toaster-site-runtime/utils/fonts";
5
+ import { provideBuilderEventService } from "./useBuilderEventService.js";
6
+ import { useBuilderState } from "./useBuilderState.js";
7
+ export function useBuilder(options) {
8
+ const { builderOrigin, builderReady, siteOptions } = useBuilderState(options.siteOptions);
9
+ if (import.meta.dev) {
10
+ const builderEvents = new BuilderEventService({
11
+ builderOrigin,
12
+ builderReady,
13
+ siteOptions
14
+ });
15
+ provideBuilderEventService(builderEvents);
16
+ onMounted(() => window.addEventListener("message", builderEvents.handleMessage));
17
+ onBeforeUnmount(() => window.removeEventListener("message", builderEvents.handleMessage));
18
+ }
19
+ const siteCssVariables = computed(() => collectCssVariables(options.siteControls, siteOptions.value));
20
+ const siteFontStyleSheets = computed(() => collectFontStyleSheets(options.siteControls, siteOptions.value));
21
+ const siteFontFacesCss = computed(() => collectFontFacesCss(options.siteControls, siteOptions.value));
22
+ return {
23
+ siteCssVariables,
24
+ siteFontFacesCss,
25
+ siteFontStyleSheets
26
+ };
27
+ }
@@ -0,0 +1,3 @@
1
+ import type { BuilderEventService } from '#toaster-site-runtime/services/builder-event-service';
2
+ export declare function provideBuilderEventService(service: BuilderEventService): void;
3
+ export declare function useBuilderEventService(): BuilderEventService;
@@ -0,0 +1,14 @@
1
+ import { inject, provide } from "vue";
2
+ const builderEventServiceKey = Symbol(
3
+ "toaster-builder-event-service"
4
+ );
5
+ export function provideBuilderEventService(service) {
6
+ provide(builderEventServiceKey, service);
7
+ }
8
+ export function useBuilderEventService() {
9
+ const service = inject(builderEventServiceKey);
10
+ if (!service) {
11
+ throw new Error("BuilderEventService was not provided by the application root.");
12
+ }
13
+ return service;
14
+ }
@@ -0,0 +1,8 @@
1
+ import type { BlockOptions, PageRuntimeState } from '#toaster-site-runtime/types/page';
2
+ export declare function useBuilderState(initialSiteOptions?: BlockOptions): {
3
+ builderOrigin: import("vue").Ref<string | null, string | null>;
4
+ builderReady: import("vue").Ref<boolean, boolean>;
5
+ formPreviewToken: import("vue").Ref<string | null, string | null>;
6
+ pageRuntimeState: import("vue").Ref<PageRuntimeState | null, PageRuntimeState | null>;
7
+ siteOptions: import("vue").Ref<BlockOptions, BlockOptions>;
8
+ };
@@ -0,0 +1,21 @@
1
+ import { ref } from "vue";
2
+ import { useState } from "#imports";
3
+ export function useBuilderState(initialSiteOptions = {}) {
4
+ const siteOptions = useState("toaster-site-options", () => ({
5
+ ...initialSiteOptions
6
+ }));
7
+ const builderOrigin = import.meta.dev ? useState("toaster-builder-origin", () => null) : ref(null);
8
+ const builderReady = import.meta.dev ? useState("toaster-builder-ready", () => false) : ref(false);
9
+ const formPreviewToken = useState("toaster-form-preview-token", () => null);
10
+ const pageRuntimeState = useState(
11
+ "toaster-page-runtime-state",
12
+ () => null
13
+ );
14
+ return {
15
+ builderOrigin,
16
+ builderReady,
17
+ formPreviewToken,
18
+ pageRuntimeState,
19
+ siteOptions
20
+ };
21
+ }
@@ -0,0 +1,6 @@
1
+ import type { Component } from 'vue';
2
+ import { type BlockOptions } from '#toaster-site-runtime/types/page';
3
+ export declare function usePageRegistry(): {
4
+ blockDefaultOptionsRegistry: Map<string, BlockOptions>;
5
+ blockRegistry: Map<string, Component>;
6
+ };
@@ -0,0 +1,76 @@
1
+ import { isBlockId } from "#toaster-site-runtime/types/page";
2
+ import { isRecord } from "#toaster-site-runtime/utils/common";
3
+ const blockModules = import.meta.glob("/blocks/*/*/*.vue", {
4
+ eager: true,
5
+ import: "default"
6
+ });
7
+ const blockConfigModules = import.meta.glob("/blocks/*/*/config.json", {
8
+ eager: true,
9
+ import: "default"
10
+ });
11
+ const blockSchemaModules = import.meta.glob("/blocks/*/*/schema.json", {
12
+ eager: true,
13
+ import: "default"
14
+ });
15
+ function blockIdFromSource(source) {
16
+ const pathParts = source.split("/");
17
+ const blockName = pathParts.at(-3);
18
+ const variantName = pathParts.at(-2);
19
+ return blockName && variantName ? `${blockName}/${variantName}` : null;
20
+ }
21
+ export function usePageRegistry() {
22
+ const blockRegistry = /* @__PURE__ */ new Map();
23
+ const blockDefaultOptionsRegistry = /* @__PURE__ */ new Map();
24
+ const blockSchemaRegistry = /* @__PURE__ */ new Set();
25
+ for (const [source, component] of Object.entries(blockModules)) {
26
+ const pathParts = source.split("/");
27
+ const blockName = pathParts.at(-3);
28
+ const fileName = pathParts.at(-1)?.replace(/\.vue$/, "");
29
+ const blockId = blockIdFromSource(source);
30
+ if (!blockId || !isBlockId(blockId) || blockName !== fileName) {
31
+ throw new Error(`Block path must follow blocks/<Block>/<variant>/<Block>.vue: ${source}`);
32
+ }
33
+ if (blockRegistry.has(blockId)) {
34
+ throw new Error(`Duplicate Block id "${blockId}"`);
35
+ }
36
+ blockRegistry.set(blockId, component);
37
+ }
38
+ for (const [source, value] of Object.entries(blockConfigModules)) {
39
+ const blockId = blockIdFromSource(source);
40
+ if (!blockId || !isBlockId(blockId) || !blockRegistry.has(blockId)) {
41
+ throw new Error(`Block config has no matching component: ${source}`);
42
+ }
43
+ if (!isRecord(value) || value.version !== 1 || !isRecord(value.options)) {
44
+ throw new Error(`Invalid Block config in ${source}`);
45
+ }
46
+ if (blockDefaultOptionsRegistry.has(blockId)) {
47
+ throw new Error(`Duplicate config for Block "${blockId}"`);
48
+ }
49
+ blockDefaultOptionsRegistry.set(blockId, value.options);
50
+ }
51
+ for (const [source, value] of Object.entries(blockSchemaModules)) {
52
+ const blockId = blockIdFromSource(source);
53
+ if (!blockId || !isBlockId(blockId) || !blockRegistry.has(blockId)) {
54
+ throw new Error(`Block schema has no matching component: ${source}`);
55
+ }
56
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.tabs) || !Array.isArray(value.controls)) {
57
+ throw new Error(`Invalid Block schema in ${source}`);
58
+ }
59
+ if (blockSchemaRegistry.has(blockId)) {
60
+ throw new Error(`Duplicate schema for Block "${blockId}"`);
61
+ }
62
+ blockSchemaRegistry.add(blockId);
63
+ }
64
+ for (const blockId of blockRegistry.keys()) {
65
+ if (!blockDefaultOptionsRegistry.has(blockId)) {
66
+ throw new Error(`Missing config for Block "${blockId}"`);
67
+ }
68
+ if (!blockSchemaRegistry.has(blockId)) {
69
+ throw new Error(`Missing schema for Block "${blockId}"`);
70
+ }
71
+ }
72
+ return {
73
+ blockDefaultOptionsRegistry,
74
+ blockRegistry
75
+ };
76
+ }
@@ -0,0 +1,21 @@
1
+ import { type Component, type Ref } from 'vue';
2
+ import { type BlockOptions, type BlockPlacement, type PageRegion, type PageRuntimeState } from '#toaster-site-runtime/types/page';
3
+ interface UsePageSettingsOptions {
4
+ blockDefaultOptionsRegistry: ReadonlyMap<string, BlockOptions>;
5
+ blockRegistry: ReadonlyMap<string, Component>;
6
+ currentPath: () => string;
7
+ pageRuntimeState: Ref<PageRuntimeState | null>;
8
+ siteOptions: Ref<BlockOptions>;
9
+ }
10
+ export declare function usePageSettings(options: UsePageSettingsOptions): {
11
+ getDescription: () => string | undefined;
12
+ getTitle: () => string | undefined;
13
+ isPlacementEnabled: (placement: BlockPlacement) => boolean;
14
+ pagePlacements: (region: PageRegion) => BlockPlacement[];
15
+ placementKey: (region: PageRegion, placement: BlockPlacement) => string;
16
+ resolveBlock: (id: string) => Component;
17
+ resolveBlockOptions: (placement: BlockPlacement & {
18
+ options?: BlockOptions;
19
+ }) => BlockOptions;
20
+ };
21
+ export {};
@@ -0,0 +1,90 @@
1
+ import { computed, watch } from "vue";
2
+ import { createError } from "#imports";
3
+ import {
4
+ pageRegions
5
+ } from "#toaster-site-runtime/types/page";
6
+ import { getBlockElementId } from "#toaster-site-runtime/utils/block-options";
7
+ import { getBlockPlacementOptions, getPageConfig } from "#toaster-site-runtime/utils/page-config";
8
+ export function usePageSettings(options) {
9
+ watch(options.currentPath, () => {
10
+ options.pageRuntimeState.value = null;
11
+ });
12
+ const pageConfig = computed(() => {
13
+ const currentPath = options.currentPath();
14
+ const persistedConfig = getPageConfig(currentPath);
15
+ const config = options.pageRuntimeState.value?.page.options.path === currentPath ? options.pageRuntimeState.value.page : persistedConfig;
16
+ if (!config) {
17
+ throw createError({
18
+ statusCode: 404,
19
+ statusMessage: `Page config not found for path "${currentPath}"`
20
+ });
21
+ }
22
+ const elementIds = /* @__PURE__ */ new Map();
23
+ for (const region of pageRegions) {
24
+ for (const placement of config.blocks[region]) {
25
+ const blockId = placement.id;
26
+ if (!options.blockRegistry.has(blockId)) {
27
+ throw createError({
28
+ statusCode: 500,
29
+ statusMessage: `Unknown Block "${blockId}" used by page "${config.options.path}"`
30
+ });
31
+ }
32
+ const elementId = getBlockElementId(resolveBlockOptions(placement), blockId);
33
+ const duplicateBlock = elementIds.get(elementId);
34
+ if (duplicateBlock !== void 0) {
35
+ throw createError({
36
+ statusCode: 500,
37
+ statusMessage: `Duplicate element-id "${elementId}" in Blocks "${duplicateBlock}" and "${placement.id}"`
38
+ });
39
+ }
40
+ elementIds.set(elementId, blockId);
41
+ }
42
+ }
43
+ return config;
44
+ });
45
+ function resolveBlockOptions(placement) {
46
+ if (placement.options) return placement.options;
47
+ return {
48
+ ...options.blockDefaultOptionsRegistry.get(placement.id),
49
+ ...getBlockPlacementOptions(placement)
50
+ };
51
+ }
52
+ function resolveBlock(id) {
53
+ const component = options.blockRegistry.get(id);
54
+ if (!component) {
55
+ throw createError({
56
+ statusCode: 500,
57
+ statusMessage: `Unknown Block "${id}" used by page "${pageConfig.value.options.path}"`
58
+ });
59
+ }
60
+ return component;
61
+ }
62
+ function pagePlacements(region) {
63
+ return pageConfig.value.blocks[region];
64
+ }
65
+ function placementKey(region, placement) {
66
+ return `${region}:${placement.placementId}`;
67
+ }
68
+ function isPlacementEnabled(placement) {
69
+ return placement.enable;
70
+ }
71
+ function getSiteStringOption(optionId) {
72
+ const value = options.siteOptions.value[optionId];
73
+ return typeof value === "string" ? value : void 0;
74
+ }
75
+ function getTitle() {
76
+ return pageConfig.value.options.seoTitle ?? getSiteStringOption("name");
77
+ }
78
+ function getDescription() {
79
+ return pageConfig.value.options.seoDescription ?? getSiteStringOption("description");
80
+ }
81
+ return {
82
+ getDescription,
83
+ getTitle,
84
+ isPlacementEnabled,
85
+ pagePlacements,
86
+ placementKey,
87
+ resolveBlock,
88
+ resolveBlockOptions
89
+ };
90
+ }
@@ -0,0 +1,9 @@
1
+ export declare abstract class BuilderEvent {
2
+ readonly origin: string;
3
+ protected constructor(origin: string);
4
+ }
5
+ export declare abstract class BuilderMutationEvent extends BuilderEvent {
6
+ readonly mutationId: string;
7
+ protected constructor(origin: string, mutationId: string);
8
+ }
9
+ export declare function parseMutationId(payload: Record<string, unknown>): string | null;
@@ -0,0 +1,14 @@
1
+ export class BuilderEvent {
2
+ constructor(origin) {
3
+ this.origin = origin;
4
+ }
5
+ }
6
+ export class BuilderMutationEvent extends BuilderEvent {
7
+ constructor(origin, mutationId) {
8
+ super(origin);
9
+ this.mutationId = mutationId;
10
+ }
11
+ }
12
+ export function parseMutationId(payload) {
13
+ return typeof payload.mutationId === "string" && payload.mutationId.trim() !== "" ? payload.mutationId : null;
14
+ }
@@ -0,0 +1,7 @@
1
+ import { BuilderMutationEvent } from './builder-event.js';
2
+ export declare class BuilderFormPreviewTokenEvent extends BuilderMutationEvent {
3
+ readonly token: string;
4
+ static readonly event = "builder-form-preview-token";
5
+ private constructor();
6
+ static fromPayload(payload: Record<string, unknown>, origin: string): BuilderFormPreviewTokenEvent | null;
7
+ }
@@ -0,0 +1,19 @@
1
+ import { BuilderMutationEvent, parseMutationId } from "./builder-event.js";
2
+ export class BuilderFormPreviewTokenEvent extends BuilderMutationEvent {
3
+ constructor(token, mutationId, origin) {
4
+ super(origin, mutationId);
5
+ this.token = token;
6
+ }
7
+ static event = "builder-form-preview-token";
8
+ static fromPayload(payload, origin) {
9
+ const mutationId = parseMutationId(payload);
10
+ if (typeof payload.token !== "string" || payload.token === "" || mutationId === null) {
11
+ return null;
12
+ }
13
+ return new BuilderFormPreviewTokenEvent(
14
+ payload.token,
15
+ mutationId,
16
+ origin
17
+ );
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ import { type PageRuntimeState } from '#toaster-site-runtime/types/page';
2
+ import { BuilderMutationEvent } from './builder-event.js';
3
+ export declare class BuilderPageStateEvent extends BuilderMutationEvent {
4
+ readonly state: PageRuntimeState;
5
+ static readonly event = "builder-page-state";
6
+ private constructor();
7
+ static fromPayload(payload: Record<string, unknown>, origin: string, isKnownBlock: (blockId: string) => boolean): BuilderPageStateEvent | null;
8
+ private static parseState;
9
+ }
@@ -0,0 +1,62 @@
1
+ import {
2
+ isBlockId,
3
+ isPlacementId,
4
+ pageRegions
5
+ } from "#toaster-site-runtime/types/page";
6
+ import { isRecord } from "#toaster-site-runtime/utils/common";
7
+ import { parsePageOptions } from "#toaster-site-runtime/utils/page-options";
8
+ import { BuilderMutationEvent, parseMutationId } from "./builder-event.js";
9
+ export class BuilderPageStateEvent extends BuilderMutationEvent {
10
+ constructor(state, mutationId, origin) {
11
+ super(origin, mutationId);
12
+ this.state = state;
13
+ }
14
+ static event = "builder-page-state";
15
+ static fromPayload(payload, origin, isKnownBlock) {
16
+ const state = this.parseState(payload.state, isKnownBlock);
17
+ const mutationId = parseMutationId(payload);
18
+ if (!state || mutationId === null) return null;
19
+ return new BuilderPageStateEvent(
20
+ state,
21
+ mutationId,
22
+ origin
23
+ );
24
+ }
25
+ static parseState(value, isKnownBlock) {
26
+ if (!isRecord(value) || typeof value.revision !== "string" || !/^[a-f0-9]{64}$/.test(value.revision) || !isRecord(value.page) || value.page.version !== 1 || !isRecord(value.page.blocks)) return null;
27
+ let pageOptions;
28
+ try {
29
+ pageOptions = parsePageOptions(value.page.options, "builder-page-state");
30
+ } catch {
31
+ return null;
32
+ }
33
+ const placementIds = /* @__PURE__ */ new Set();
34
+ const blocks = {
35
+ header: [],
36
+ main: [],
37
+ footer: []
38
+ };
39
+ for (const region of pageRegions) {
40
+ const placements = value.page.blocks[region];
41
+ if (!Array.isArray(placements)) return null;
42
+ for (const placement of placements) {
43
+ if (!isRecord(placement) || typeof placement.id !== "string" || !isBlockId(placement.id) || !isKnownBlock(placement.id) || typeof placement.enable !== "boolean" || typeof placement.placementId !== "string" || !isPlacementId(placement.placementId) || placementIds.has(placement.placementId) || !isRecord(placement.options)) return null;
44
+ placementIds.add(placement.placementId);
45
+ blocks[region].push({
46
+ id: placement.id,
47
+ enable: placement.enable,
48
+ placementId: placement.placementId,
49
+ options: { ...placement.options }
50
+ });
51
+ }
52
+ }
53
+ return {
54
+ revision: value.revision,
55
+ page: {
56
+ version: 1,
57
+ options: pageOptions,
58
+ blocks
59
+ }
60
+ };
61
+ }
62
+ }
@@ -0,0 +1,6 @@
1
+ import { BuilderEvent } from './builder-event.js';
2
+ export declare class BuilderReadyPingEvent extends BuilderEvent {
3
+ static readonly event = "builder-ready-ping";
4
+ private constructor();
5
+ static fromOrigin(origin: string): BuilderReadyPingEvent;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { BuilderEvent } from "./builder-event.js";
2
+ export class BuilderReadyPingEvent extends BuilderEvent {
3
+ static event = "builder-ready-ping";
4
+ constructor(origin) {
5
+ super(origin);
6
+ }
7
+ static fromOrigin(origin) {
8
+ return new BuilderReadyPingEvent(origin);
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ import type { BlockOptions } from '#toaster-site-runtime/types/page';
2
+ import { BuilderMutationEvent } from './builder-event.js';
3
+ export declare class SiteOptionsUpdatedEvent extends BuilderMutationEvent {
4
+ readonly options: BlockOptions;
5
+ static readonly event = "site-options-updated";
6
+ private constructor();
7
+ static fromPayload(payload: Record<string, unknown>, origin: string): SiteOptionsUpdatedEvent | null;
8
+ }
@@ -0,0 +1,18 @@
1
+ import { isRecord } from "#toaster-site-runtime/utils/common";
2
+ import { BuilderMutationEvent, parseMutationId } from "./builder-event.js";
3
+ export class SiteOptionsUpdatedEvent extends BuilderMutationEvent {
4
+ constructor(options, mutationId, origin) {
5
+ super(origin, mutationId);
6
+ this.options = options;
7
+ }
8
+ static event = "site-options-updated";
9
+ static fromPayload(payload, origin) {
10
+ const mutationId = parseMutationId(payload);
11
+ if (!isRecord(payload.options) || mutationId === null) return null;
12
+ return new SiteOptionsUpdatedEvent(
13
+ { ...payload.options },
14
+ mutationId,
15
+ origin
16
+ );
17
+ }
18
+ }
@@ -0,0 +1,10 @@
1
+ declare const _default: import("#app").Plugin<{
2
+ toasterSiteRuntime: {
3
+ protocolVersion: number;
4
+ };
5
+ }> & import("#app").ObjectPlugin<{
6
+ toasterSiteRuntime: {
7
+ protocolVersion: number;
8
+ };
9
+ }>;
10
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import { defineNuxtPlugin } from "#app";
2
+ export default defineNuxtPlugin((_nuxtApp) => {
3
+ return {
4
+ provide: {
5
+ toasterSiteRuntime: {
6
+ protocolVersion: 1
7
+ }
8
+ }
9
+ };
10
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../.nuxt/tsconfig.server.json",
3
+ }