@phtngyn/cms 0.2.0-beta.2 → 0.2.0-beta.3

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.
@@ -1,5 +1,5 @@
1
1
  import { R as SeoConfiguration } from "../models-ChRxBChy.js";
2
- import { j as StudioBootstrap, t as maxS3SingleCopyBytes, u as makeDefaultSeoOptions } from "../S3MediaObjects-D5Ns4FM6.js";
2
+ import { j as StudioBootstrap, t as maxS3SingleCopyBytes, u as makeDefaultSeoOptions } from "../S3MediaObjects-CR3p1WTg.js";
3
3
  import { validateFormCapabilities } from "./form-capabilities.js";
4
4
  import { compileCmsI18n } from "./i18n.js";
5
5
  import { i as PreviewOrigin } from "../models-C9kF9JlQ.js";
@@ -1,3 +1,3 @@
1
- import { i as resetCmsDevelopment, n as doctorCmsDevelopment, r as loadCmsProjectEnvironment, t as bootstrapCmsDevelopment } from "../dev-lifecycle-CZ7iCzG9.js";
1
+ import { i as resetCmsDevelopment, n as doctorCmsDevelopment, r as loadCmsProjectEnvironment, t as bootstrapCmsDevelopment } from "../dev-lifecycle-D3f9-upa.js";
2
2
 
3
3
  export { bootstrapCmsDevelopment, doctorCmsDevelopment, loadCmsProjectEnvironment, resetCmsDevelopment };
@@ -1,5 +1,5 @@
1
1
  import { d as STUDIO_APP_PATH } from "../HttpPaths-c9nLZ9H2.js";
2
- import { Effect, FileSystem, Path } from "effect";
2
+ import { Config, Effect, FileSystem, Path } from "effect";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { addRouteMiddleware, addServerHandler, addServerTemplate, addVitePlugin } from "@nuxt/kit";
5
5
 
@@ -19,12 +19,14 @@ const registerStudioApp = Effect.fn("CmsNuxtModule.registerStudioApp")(function*
19
19
  if (!packaged) {
20
20
  const source = fileURLToPath(import.meta.resolve("@internal/studio/host"));
21
21
  const root = path.dirname(path.dirname(source));
22
- const { build } = yield* Effect.promise(() => import("vite"));
23
- yield* Effect.promise(() => build({
24
- root,
25
- configFile: `${root}/vite.config.ts`
26
- }));
27
22
  host = `${root}/dist/host.js`;
23
+ if (!(yield* Config.Boolean("CMS_REUSE_STUDIO_BUILD").pipe(Config.withDefault(false))) || !(yield* fileSystem.exists(host)) || !(yield* fileSystem.exists(host.replace(/\.js$/u, ".css")))) {
24
+ const { build } = yield* Effect.promise(() => import("vite"));
25
+ yield* Effect.promise(() => build({
26
+ root,
27
+ configFile: `${root}/vite.config.ts`
28
+ }));
29
+ }
28
30
  }
29
31
  nuxt.options.alias["#cms/studio"] = host;
30
32
  nuxt.options.alias["#cms/studio-styles"] = host.replace(/\.js$/u, ".css?url");
@@ -1 +1 @@
1
- {"version":3,"file":"studio-app.js","names":[],"sources":["../../../../src/internal/studio-app.ts"],"sourcesContent":["import type { Nuxt } from '@nuxt/schema'\nimport { fileURLToPath } from 'node:url'\nimport { addRouteMiddleware, addServerHandler, addServerTemplate, addVitePlugin, createResolver } from '@nuxt/kit'\nimport { Effect, FileSystem, Path } from 'effect'\nimport { STUDIO_APP_PATH } from '@internal/protocol/HttpPaths'\n\n/** A separate HTML entry shares Nuxt's Vite graph, but never its document or Vue app. */\nexport const registerStudioApp = Effect.fn('CmsNuxtModule.registerStudioApp')(function* (\n nuxt: Nuxt,\n resolver: ReturnType<typeof createResolver>,\n) {\n addRouteMiddleware({\n name: 'cms-studio-document',\n path: resolver.resolve('./runtime/studio-navigation'),\n global: true,\n })\n const fileSystem = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const packagedHost = resolver.resolve('../studio/host.js')\n const packaged = yield* fileSystem.exists(packagedHost)\n let host = packagedHost\n\n if (!packaged) {\n const source = fileURLToPath(import.meta.resolve('@internal/studio/host'))\n const root = path.dirname(path.dirname(source))\n const { build } = yield* Effect.promise(() => import('vite'))\n yield* Effect.promise(() => build({ root, configFile: `${root}/vite.config.ts` }))\n host = `${root}/dist/host.js`\n }\n\n nuxt.options.alias['#cms/studio'] = host\n nuxt.options.alias['#cms/studio-styles'] = host.replace(/\\.js$/u, '.css?url')\n const entryBase = resolver.resolve('./runtime/studio-entry')\n const entry = `${entryBase}.${(yield* fileSystem.exists(`${entryBase}.ts`)) ? 'ts' : 'js'}`\n const assets = `${nuxt.options.app.baseURL}${nuxt.options.app.buildAssetsDir}`.replace(/\\/+/gu, '/')\n let entryUrl = `${assets}@fs${entry}`\n addVitePlugin(\n {\n name: 'cms-studio-entry',\n apply: 'build',\n buildStart() {\n this.emitFile({ type: 'chunk', id: entry, name: 'cms-studio' })\n },\n generateBundle(_options, bundle) {\n const chunk = Object.values(bundle).find((output) => output.type === 'chunk' && output.name === 'cms-studio')\n\n if (chunk) entryUrl = `${nuxt.options.app.baseURL}${chunk.fileName}`\n },\n },\n { server: false },\n )\n\n const handler = addServerTemplate({\n filename: resolver.resolve('./runtime/studio-shell.virtual.mjs'),\n getContents: () =>\n [\n `import { createStudioAppHandler } from ${JSON.stringify(resolver.resolve('./runtime/studio-app'))}`,\n `export default createStudioAppHandler(${JSON.stringify(entryUrl)}, ${JSON.stringify(nuxt.options.dev ? `${assets}@vite/client` : undefined)})`,\n ].join('\\n'),\n })\n\n addServerHandler({ handler: handler.filename, route: STUDIO_APP_PATH })\n addServerHandler({ handler: handler.filename, route: `${STUDIO_APP_PATH}/**:path` })\n})\n"],"mappings":";;;;;;;AAOA,MAAa,oBAAoB,OAAO,GAAG,iCAAiC,CAAC,CAAC,WAC5E,MACA,UACA;CACA,mBAAmB;EACjB,MAAM;EACN,MAAM,SAAS,QAAQ,6BAA6B;EACpD,QAAQ;CACV,CAAC;CACD,MAAM,aAAa,OAAO,WAAW;CACrC,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,eAAe,SAAS,QAAQ,mBAAmB;CACzD,MAAM,WAAW,OAAO,WAAW,OAAO,YAAY;CACtD,IAAI,OAAO;CAEX,IAAI,CAAC,UAAU;EACb,MAAM,SAAS,cAAc,YAAY,QAAQ,uBAAuB,CAAC;EACzE,MAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,MAAM,CAAC;EAC9C,MAAM,EAAE,UAAU,OAAO,OAAO,cAAc,OAAO,OAAO;EAC5D,OAAO,OAAO,cAAc,MAAM;GAAE;GAAM,YAAY,GAAG,KAAK;EAAiB,CAAC,CAAC;EACjF,OAAO,GAAG,KAAK;CACjB;CAEA,KAAK,QAAQ,MAAM,iBAAiB;CACpC,KAAK,QAAQ,MAAM,wBAAwB,KAAK,QAAQ,UAAU,UAAU;CAC5E,MAAM,YAAY,SAAS,QAAQ,wBAAwB;CAC3D,MAAM,QAAQ,GAAG,UAAU,IAAI,OAAO,WAAW,OAAO,GAAG,UAAU,IAAI,KAAK,OAAO;CACrF,MAAM,SAAS,GAAG,KAAK,QAAQ,IAAI,UAAU,KAAK,QAAQ,IAAI,iBAAiB,QAAQ,SAAS,GAAG;CACnG,IAAI,WAAW,GAAG,OAAO,KAAK;CAC9B,cACE;EACE,MAAM;EACN,OAAO;EACP,aAAa;GACX,KAAK,SAAS;IAAE,MAAM;IAAS,IAAI;IAAO,MAAM;GAAa,CAAC;EAChE;EACA,eAAe,UAAU,QAAQ;GAC/B,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,MAAM,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,YAAY;GAE5G,IAAI,OAAO,WAAW,GAAG,KAAK,QAAQ,IAAI,UAAU,MAAM;EAC5D;CACF,GACA,EAAE,QAAQ,MAAM,CAClB;CAEA,MAAM,UAAU,kBAAkB;EAChC,UAAU,SAAS,QAAQ,oCAAoC;EAC/D,mBACE,CACE,0CAA0C,KAAK,UAAU,SAAS,QAAQ,sBAAsB,CAAC,KACjG,yCAAyC,KAAK,UAAU,QAAQ,EAAE,IAAI,KAAK,UAAU,KAAK,QAAQ,MAAM,GAAG,OAAO,gBAAgB,MAAS,EAAE,EAC/I,CAAC,CAAC,KAAK,IAAI;CACf,CAAC;CAED,iBAAiB;EAAE,SAAS,QAAQ;EAAU,OAAO;CAAgB,CAAC;CACtE,iBAAiB;EAAE,SAAS,QAAQ;EAAU,OAAO,GAAG,gBAAgB;CAAU,CAAC;AACrF,CAAC"}
1
+ {"version":3,"file":"studio-app.js","names":[],"sources":["../../../../src/internal/studio-app.ts"],"sourcesContent":["import type { Nuxt } from '@nuxt/schema'\nimport { fileURLToPath } from 'node:url'\nimport { addRouteMiddleware, addServerHandler, addServerTemplate, addVitePlugin, createResolver } from '@nuxt/kit'\nimport { Config, Effect, FileSystem, Path } from 'effect'\nimport { STUDIO_APP_PATH } from '@internal/protocol/HttpPaths'\n\n/** A separate HTML entry shares Nuxt's Vite graph, but never its document or Vue app. */\nexport const registerStudioApp = Effect.fn('CmsNuxtModule.registerStudioApp')(function* (\n nuxt: Nuxt,\n resolver: ReturnType<typeof createResolver>,\n) {\n addRouteMiddleware({\n name: 'cms-studio-document',\n path: resolver.resolve('./runtime/studio-navigation'),\n global: true,\n })\n const fileSystem = yield* FileSystem.FileSystem\n const path = yield* Path.Path\n const packagedHost = resolver.resolve('../studio/host.js')\n const packaged = yield* fileSystem.exists(packagedHost)\n let host = packagedHost\n\n if (!packaged) {\n const source = fileURLToPath(import.meta.resolve('@internal/studio/host'))\n const root = path.dirname(path.dirname(source))\n host = `${root}/dist/host.js`\n const reuseStudioBuild = yield* Config.Boolean('CMS_REUSE_STUDIO_BUILD').pipe(Config.withDefault(false))\n\n if (\n !reuseStudioBuild ||\n !(yield* fileSystem.exists(host)) ||\n !(yield* fileSystem.exists(host.replace(/\\.js$/u, '.css')))\n ) {\n const { build } = yield* Effect.promise(() => import('vite'))\n yield* Effect.promise(() => build({ root, configFile: `${root}/vite.config.ts` }))\n }\n }\n\n nuxt.options.alias['#cms/studio'] = host\n nuxt.options.alias['#cms/studio-styles'] = host.replace(/\\.js$/u, '.css?url')\n const entryBase = resolver.resolve('./runtime/studio-entry')\n const entry = `${entryBase}.${(yield* fileSystem.exists(`${entryBase}.ts`)) ? 'ts' : 'js'}`\n const assets = `${nuxt.options.app.baseURL}${nuxt.options.app.buildAssetsDir}`.replace(/\\/+/gu, '/')\n let entryUrl = `${assets}@fs${entry}`\n addVitePlugin(\n {\n name: 'cms-studio-entry',\n apply: 'build',\n buildStart() {\n this.emitFile({ type: 'chunk', id: entry, name: 'cms-studio' })\n },\n generateBundle(_options, bundle) {\n const chunk = Object.values(bundle).find((output) => output.type === 'chunk' && output.name === 'cms-studio')\n\n if (chunk) entryUrl = `${nuxt.options.app.baseURL}${chunk.fileName}`\n },\n },\n { server: false },\n )\n\n const handler = addServerTemplate({\n filename: resolver.resolve('./runtime/studio-shell.virtual.mjs'),\n getContents: () =>\n [\n `import { createStudioAppHandler } from ${JSON.stringify(resolver.resolve('./runtime/studio-app'))}`,\n `export default createStudioAppHandler(${JSON.stringify(entryUrl)}, ${JSON.stringify(nuxt.options.dev ? `${assets}@vite/client` : undefined)})`,\n ].join('\\n'),\n })\n\n addServerHandler({ handler: handler.filename, route: STUDIO_APP_PATH })\n addServerHandler({ handler: handler.filename, route: `${STUDIO_APP_PATH}/**:path` })\n})\n"],"mappings":";;;;;;;AAOA,MAAa,oBAAoB,OAAO,GAAG,iCAAiC,CAAC,CAAC,WAC5E,MACA,UACA;CACA,mBAAmB;EACjB,MAAM;EACN,MAAM,SAAS,QAAQ,6BAA6B;EACpD,QAAQ;CACV,CAAC;CACD,MAAM,aAAa,OAAO,WAAW;CACrC,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,eAAe,SAAS,QAAQ,mBAAmB;CACzD,MAAM,WAAW,OAAO,WAAW,OAAO,YAAY;CACtD,IAAI,OAAO;CAEX,IAAI,CAAC,UAAU;EACb,MAAM,SAAS,cAAc,YAAY,QAAQ,uBAAuB,CAAC;EACzE,MAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,MAAM,CAAC;EAC9C,OAAO,GAAG,KAAK;EAGf,IACE,EAAC,OAH6B,OAAO,QAAQ,wBAAwB,CAAC,CAAC,KAAK,OAAO,YAAY,KAAK,CAAC,MAIrG,EAAE,OAAO,WAAW,OAAO,IAAI,MAC/B,EAAE,OAAO,WAAW,OAAO,KAAK,QAAQ,UAAU,MAAM,CAAC,IACzD;GACA,MAAM,EAAE,UAAU,OAAO,OAAO,cAAc,OAAO,OAAO;GAC5D,OAAO,OAAO,cAAc,MAAM;IAAE;IAAM,YAAY,GAAG,KAAK;GAAiB,CAAC,CAAC;EACnF;CACF;CAEA,KAAK,QAAQ,MAAM,iBAAiB;CACpC,KAAK,QAAQ,MAAM,wBAAwB,KAAK,QAAQ,UAAU,UAAU;CAC5E,MAAM,YAAY,SAAS,QAAQ,wBAAwB;CAC3D,MAAM,QAAQ,GAAG,UAAU,IAAI,OAAO,WAAW,OAAO,GAAG,UAAU,IAAI,KAAK,OAAO;CACrF,MAAM,SAAS,GAAG,KAAK,QAAQ,IAAI,UAAU,KAAK,QAAQ,IAAI,iBAAiB,QAAQ,SAAS,GAAG;CACnG,IAAI,WAAW,GAAG,OAAO,KAAK;CAC9B,cACE;EACE,MAAM;EACN,OAAO;EACP,aAAa;GACX,KAAK,SAAS;IAAE,MAAM;IAAS,IAAI;IAAO,MAAM;GAAa,CAAC;EAChE;EACA,eAAe,UAAU,QAAQ;GAC/B,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,MAAM,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,YAAY;GAE5G,IAAI,OAAO,WAAW,GAAG,KAAK,QAAQ,IAAI,UAAU,MAAM;EAC5D;CACF,GACA,EAAE,QAAQ,MAAM,CAClB;CAEA,MAAM,UAAU,kBAAkB;EAChC,UAAU,SAAS,QAAQ,oCAAoC;EAC/D,mBACE,CACE,0CAA0C,KAAK,UAAU,SAAS,QAAQ,sBAAsB,CAAC,KACjG,yCAAyC,KAAK,UAAU,QAAQ,EAAE,IAAI,KAAK,UAAU,KAAK,QAAQ,MAAM,GAAG,OAAO,gBAAgB,MAAS,EAAE,EAC/I,CAAC,CAAC,KAAK,IAAI;CACf,CAAC;CAED,iBAAiB;EAAE,SAAS,QAAQ;EAAU,OAAO;CAAgB,CAAC;CACtE,iBAAiB;EAAE,SAAS,QAAQ;EAAU,OAAO,GAAG,gBAAgB;CAAU,CAAC;AACrF,CAAC"}
@@ -2,7 +2,7 @@ import { a as CMS_FORM_SUBMIT_PATH } from "../../HttpPaths-c9nLZ9H2.js";
2
2
  import { o as FormResult } from "../../Forms-BbkE7QfC.js";
3
3
  import { i as validateFormValues } from "../../Forms-DoB56hfe.js";
4
4
  import { Result, Schema } from "effect";
5
- import { computed, getCurrentInstance, getCurrentScope, onScopeDispose, reactive, readonly, ref, shallowRef, toValue, useId, watch } from "vue";
5
+ import { computed, getCurrentInstance, getCurrentScope, onScopeDispose, readonly, ref, shallowRef, toValue, useId, watch } from "vue";
6
6
 
7
7
  //#region src/runtime/forms/useCmsForm.ts
8
8
  /** Headless, per-instance form state. Definition changes preserve answers for unchanged field types. */
@@ -13,15 +13,15 @@ function useCmsForm(input, options = {}) {
13
13
  const fields = computed(() => steps.value.flatMap((step) => step.fields));
14
14
  const values = ref({});
15
15
  const errors = shallowRef({});
16
- const stepIndex = ref(0);
16
+ const stepIndex = shallowRef(0);
17
17
  const currentStep = computed(() => steps.value[stepIndex.value]);
18
18
  const isFirstStep = computed(() => stepIndex.value === 0);
19
19
  const isLastStep = computed(() => stepIndex.value === steps.value.length - 1);
20
- const submitting = ref(false);
21
- const isSuccess = ref(false);
20
+ const submitting = shallowRef(false);
21
+ const isSuccess = shallowRef(false);
22
22
  const isPreview = computed(() => definition.value.preview);
23
23
  const submitError = shallowRef();
24
- const touched = ref(/* @__PURE__ */ new Set());
24
+ const touched = shallowRef(/* @__PURE__ */ new Set());
25
25
  const controls = /* @__PURE__ */ new Map();
26
26
  let controller;
27
27
  let generation = 0;
@@ -73,37 +73,60 @@ function useCmsForm(input, options = {}) {
73
73
  else delete nextErrors[fieldId];
74
74
  errors.value = nextErrors;
75
75
  };
76
- const state = reactive({
77
- field: current,
78
- value: computed({
79
- get: () => values.value[fieldId] ?? emptyFormValue(current.value),
80
- set: (value) => {
81
- values.value[fieldId] = normalizeFormValue(current.value, value);
82
- if (touched.value.has(fieldId) || errors.value[fieldId]) refreshError();
83
- }
84
- }),
85
- dirty: computed(() => values.value[fieldId] !== emptyFormValue(current.value)),
86
- touched: computed(() => touched.value.has(fieldId)),
87
- valid: computed(() => validateFormValues([current.value], values.value)[fieldId] === void 0),
88
- error: computed(() => errors.value[fieldId]),
89
- attrs: computed(() => {
90
- const fieldError = errors.value[fieldId];
91
- const describedBy = [current.value.help ? helpId : void 0, fieldError ? errorId : void 0].filter((part) => part !== void 0).join(" ");
92
- return {
93
- id: controlId,
94
- name: fieldId,
95
- required: current.value.required === true,
96
- "aria-invalid": fieldError ? "true" : void 0,
97
- "aria-describedby": describedBy || void 0
98
- };
99
- }),
76
+ const value = computed({
77
+ get: () => values.value[fieldId] ?? emptyFormValue(current.value),
78
+ set: (next) => {
79
+ values.value[fieldId] = normalizeFormValue(current.value, next);
80
+ if (touched.value.has(fieldId) || errors.value[fieldId]) refreshError();
81
+ }
82
+ });
83
+ const dirty = computed(() => values.value[fieldId] !== emptyFormValue(current.value));
84
+ const touchedState = computed(() => touched.value.has(fieldId));
85
+ const valid = computed(() => validateFormValues([current.value], values.value)[fieldId] === void 0);
86
+ const error = computed(() => errors.value[fieldId]);
87
+ const attrs = computed(() => {
88
+ const fieldError = errors.value[fieldId];
89
+ const describedBy = [current.value.help ? helpId : void 0, fieldError ? errorId : void 0].filter((part) => part !== void 0).join(" ");
90
+ return {
91
+ id: controlId,
92
+ name: fieldId,
93
+ required: current.value.required === true,
94
+ "aria-invalid": fieldError ? "true" : void 0,
95
+ "aria-describedby": describedBy || void 0
96
+ };
97
+ });
98
+ const state = {
99
+ get field() {
100
+ return current.value;
101
+ },
102
+ get value() {
103
+ return value.value;
104
+ },
105
+ set value(next) {
106
+ value.value = next;
107
+ },
108
+ get dirty() {
109
+ return dirty.value;
110
+ },
111
+ get touched() {
112
+ return touchedState.value;
113
+ },
114
+ get valid() {
115
+ return valid.value;
116
+ },
117
+ get error() {
118
+ return error.value;
119
+ },
120
+ get attrs() {
121
+ return attrs.value;
122
+ },
100
123
  helpAttrs,
101
124
  errorAttrs,
102
125
  touch: () => {
103
126
  touched.value = /* @__PURE__ */ new Set([...touched.value, fieldId]);
104
127
  refreshError();
105
128
  }
106
- });
129
+ };
107
130
  controls.set(fieldId, state);
108
131
  return state;
109
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useCmsForm.js","names":[],"sources":["../../../../../src/runtime/forms/useCmsForm.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, ComputedRef } from 'vue'\nimport type { FormDefinition, FormField, FormStep, FormSubmission, FormValues } from '@internal/protocol/Forms'\nimport { FormResult } from '@internal/protocol/Forms'\nimport { validateFormValues } from '@internal/document/Forms'\nimport { Result, Schema } from 'effect'\nimport {\n computed,\n getCurrentInstance,\n getCurrentScope,\n onScopeDispose,\n reactive,\n readonly,\n ref,\n shallowRef,\n toValue,\n useId,\n watch,\n} from 'vue'\nimport { CMS_FORM_SUBMIT_PATH } from '@internal/protocol/HttpPaths'\n\nexport interface UseCmsFormOptions {\n /** Replace the HTTP transport, retaining validation and submission state. */\n readonly submit?: (submission: FormSubmission, signal: AbortSignal) => Promise<FormResult>\n}\n\nexport interface CmsFormControl {\n readonly field: FormField\n value: FormValues[string]\n readonly dirty: boolean\n readonly touched: boolean\n readonly valid: boolean\n readonly error: string | undefined\n readonly attrs: {\n readonly id: string\n readonly name: string\n readonly required: boolean\n readonly 'aria-invalid': 'true' | undefined\n readonly 'aria-describedby': string | undefined\n }\n readonly helpAttrs: { readonly id: string }\n readonly errorAttrs: { readonly id: string; readonly role: 'alert' }\n readonly touch: () => void\n}\n\nexport interface UseCmsFormReturn {\n readonly fields: ComputedRef<ReadonlyArray<FormField>>\n readonly steps: ComputedRef<ReadonlyArray<FormStep>>\n readonly values: Ref<Record<string, FormValues[string]>>\n readonly errors: Readonly<Ref<Readonly<Record<string, string>>>>\n readonly currentStep: ComputedRef<FormStep | undefined>\n readonly stepIndex: Readonly<Ref<number>>\n readonly isFirstStep: ComputedRef<boolean>\n readonly isLastStep: ComputedRef<boolean>\n readonly submitting: Readonly<Ref<boolean>>\n readonly isSuccess: Readonly<Ref<boolean>>\n readonly isPreview: ComputedRef<boolean>\n readonly submitError: Readonly<Ref<string | undefined>>\n readonly canSubmit: ComputedRef<boolean>\n readonly visibleControls: ComputedRef<ReadonlyArray<CmsFormControl>>\n readonly invalidControls: ComputedRef<ReadonlyArray<CmsFormControl>>\n readonly firstInvalidControlId: ComputedRef<string | undefined>\n readonly control: (field: string | FormField) => CmsFormControl\n readonly next: () => boolean\n readonly previous: () => void\n readonly validate: () => boolean\n readonly submit: () => Promise<boolean>\n readonly reset: () => void\n}\n\n/** Headless, per-instance form state. Definition changes preserve answers for unchanged field types. */\nexport function useCmsForm(input: MaybeRefOrGetter<FormDefinition>, options: UseCmsFormOptions = {}): UseCmsFormReturn {\n const fieldIdPrefix = getCurrentInstance() ? useId() : 'cms-form'\n const definition = computed(() => toValue(input))\n const steps = computed(() => definition.value.steps)\n const fields = computed(() => steps.value.flatMap((step) => step.fields))\n const values = ref<Record<string, FormValues[string]>>({})\n const errors = shallowRef<Record<string, string>>({})\n const stepIndex = ref(0)\n const currentStep = computed(() => steps.value[stepIndex.value])\n const isFirstStep = computed(() => stepIndex.value === 0)\n const isLastStep = computed(() => stepIndex.value === steps.value.length - 1)\n const submitting = ref(false)\n const isSuccess = ref(false)\n const isPreview = computed(() => definition.value.preview)\n const submitError = shallowRef<string>()\n const touched = ref(new Set<string>())\n const controls = new Map<string, CmsFormControl>()\n let controller: AbortController | undefined\n let generation = 0\n let previousFields = new Map<string, string>()\n\n const stop = watch(\n definition,\n (next, before) => {\n generation++\n controller?.abort()\n submitting.value = false\n\n const sameForm =\n before?.documentId === next.documentId &&\n JSON.stringify(before.nodePath) === JSON.stringify(next.nodePath) &&\n before.name === next.name\n\n values.value = Object.fromEntries(\n next.steps\n .flatMap((step) => step.fields)\n .map((field) => [\n field.id,\n sameForm && previousFields.get(field.id) === field.type\n ? (values.value[field.id] ?? null)\n : field.type === 'checkbox'\n ? false\n : field.type === 'number'\n ? null\n : '',\n ]),\n )\n previousFields = new Map(next.steps.flatMap((step) => step.fields).map((field) => [field.id, field.type]))\n stepIndex.value = Math.max(\n 0,\n next.steps.findIndex((step) => step.id === before?.steps[stepIndex.value]?.id),\n )\n errors.value = {}\n touched.value = new Set()\n submitError.value = undefined\n isSuccess.value = false\n },\n { immediate: true, deep: true, flush: 'sync' },\n )\n\n function validate(): boolean {\n touched.value = new Set(fields.value.map((field) => field.id))\n errors.value = validateFormValues(fields.value, values.value)\n\n return Object.keys(errors.value).length === 0\n }\n\n function control(input: string | FormField): CmsFormControl {\n const fieldId = Schema.is(Schema.String)(input) ? input : input.id\n const existing = controls.get(fieldId)\n\n if (existing) return existing\n\n if (!fields.value.some((candidate) => candidate.id === fieldId))\n throw new Error(`Unknown CMS form field ${fieldId}.`)\n\n const current = computed(() => {\n const candidate = fields.value.find((field) => field.id === fieldId)\n\n if (!candidate) throw new Error(`CMS form field ${fieldId} no longer exists.`)\n\n return candidate\n })\n\n const controlId = `${fieldIdPrefix}-${encodeURIComponent(fieldId)}`\n const helpId = `${controlId}-help`\n const errorId = `${controlId}-error`\n const helpAttrs: CmsFormControl['helpAttrs'] = { id: helpId }\n const errorAttrs: CmsFormControl['errorAttrs'] = { id: errorId, role: 'alert' }\n\n const refreshError = () => {\n const issue = validateFormValues([current.value], values.value)[fieldId]\n const nextErrors = { ...errors.value }\n\n if (issue) nextErrors[fieldId] = issue\n else delete nextErrors[fieldId]\n errors.value = nextErrors\n }\n\n const state = reactive({\n field: current,\n value: computed({\n get: () => values.value[fieldId] ?? emptyFormValue(current.value),\n set: (value) => {\n values.value[fieldId] = normalizeFormValue(current.value, value)\n\n if (touched.value.has(fieldId) || errors.value[fieldId]) refreshError()\n },\n }),\n dirty: computed(() => values.value[fieldId] !== emptyFormValue(current.value)),\n touched: computed(() => touched.value.has(fieldId)),\n valid: computed(() => validateFormValues([current.value], values.value)[fieldId] === undefined),\n error: computed(() => errors.value[fieldId]),\n attrs: computed<CmsFormControl['attrs']>(() => {\n const fieldError = errors.value[fieldId]\n\n const describedBy = [current.value.help ? helpId : undefined, fieldError ? errorId : undefined]\n .filter((part) => part !== undefined)\n .join(' ')\n\n return {\n id: controlId,\n name: fieldId,\n required: current.value.required === true,\n 'aria-invalid': fieldError ? 'true' : undefined,\n 'aria-describedby': describedBy || undefined,\n }\n }),\n helpAttrs,\n errorAttrs,\n touch: () => {\n touched.value = new Set([...touched.value, fieldId])\n refreshError()\n },\n })\n\n controls.set(fieldId, state)\n\n return state\n }\n\n function next(): boolean {\n if (submitting.value || isLastStep.value) return false\n const stepFields = currentStep.value?.fields ?? []\n touched.value = new Set([...touched.value, ...stepFields.map((field) => field.id)])\n const issues = validateFormValues(stepFields, values.value)\n errors.value = {\n ...Object.fromEntries(\n Object.entries(errors.value).filter(([id]) => !stepFields.some((field) => field.id === id)),\n ),\n ...issues,\n }\n\n if (Object.keys(issues).length) return false\n stepIndex.value++\n\n return true\n }\n\n function previous(): void {\n if (!submitting.value && !isFirstStep.value) stepIndex.value--\n }\n\n function reset(): void {\n generation++\n controller?.abort()\n submitting.value = false\n values.value = Object.fromEntries(fields.value.map((field) => [field.id, emptyFormValue(field)]))\n errors.value = {}\n touched.value = new Set()\n submitError.value = undefined\n isSuccess.value = false\n stepIndex.value = 0\n }\n\n async function submit(): Promise<boolean> {\n if (submitting.value || isSuccess.value) return false\n submitError.value = undefined\n\n if (!validate()) {\n stepIndex.value = Math.max(\n 0,\n steps.value.findIndex((step) => step.fields.some((field) => errors.value[field.id])),\n )\n\n return false\n }\n\n if (isPreview.value) {\n isSuccess.value = true\n\n return true\n }\n\n const currentGeneration = ++generation\n controller = new AbortController()\n submitting.value = true\n\n try {\n const { documentId, nodePath, revision } = definition.value\n\n const result = await (options.submit ?? send)(\n { documentId, nodePath, revision, values: { ...values.value } },\n controller.signal,\n )\n\n if (generation !== currentGeneration) return false\n errors.value = result.status === 'invalid' ? { ...result.errors } : {}\n isSuccess.value = result.status === 'accepted'\n\n if (result.status === 'invalid')\n stepIndex.value = Math.max(\n 0,\n steps.value.findIndex((step) => step.fields.some((field) => errors.value[field.id])),\n )\n\n if (result.status !== 'accepted' && result.status !== 'invalid')\n submitError.value =\n result.status === 'stale'\n ? 'This form has changed. Reload the page before submitting.'\n : 'Submission failed. Please try again.'\n\n return isSuccess.value\n } catch {\n if (generation === currentGeneration) submitError.value = 'Submission failed. Please try again.'\n\n return false\n } finally {\n if (generation === currentGeneration) submitting.value = false\n }\n }\n\n if (getCurrentScope())\n onScopeDispose(() => {\n generation++\n controller?.abort()\n stop()\n })\n\n const visibleControls = computed(() => currentStep.value?.fields.map((entry) => control(entry)) ?? [])\n const invalidControls = computed(() => fields.value.map((entry) => control(entry)).filter((entry) => !entry.valid))\n const firstInvalidControlId = computed(() => invalidControls.value[0]?.attrs.id)\n const canSubmit = computed(() => !submitting.value && !isSuccess.value && invalidControls.value.length === 0)\n\n return {\n fields,\n steps,\n values,\n errors: readonly(errors),\n currentStep,\n stepIndex: readonly(stepIndex),\n isFirstStep,\n isLastStep,\n submitting: readonly(submitting),\n isSuccess: readonly(isSuccess),\n isPreview,\n submitError: readonly(submitError),\n canSubmit,\n visibleControls,\n invalidControls,\n firstInvalidControlId,\n control,\n next,\n previous,\n validate,\n submit,\n reset,\n }\n}\n\nfunction normalizeFormValue(field: FormField, input: FormValues[string] | undefined): FormValues[string] {\n if (field.type === 'checkbox') return input === true\n\n if (field.type === 'number') {\n if (input === '' || input == null) return null\n const value = Schema.is(Schema.Finite)(input) || Schema.is(Schema.String)(input) ? Number(input) : Number.NaN\n\n if (Number.isFinite(value)) return value\n\n return Schema.is(Schema.String)(input) ? input : null\n }\n\n return Schema.is(Schema.String)(input) ? input : ''\n}\n\nfunction emptyFormValue(field: FormField): FormValues[string] {\n if (field.type === 'checkbox') return false\n\n if (field.type === 'number') return null\n\n return ''\n}\n\nasync function send(submission: FormSubmission, signal: AbortSignal): Promise<FormResult> {\n const response = await fetch(CMS_FORM_SUBMIT_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(submission),\n signal,\n })\n\n if (!response.ok) throw new Error('Submission failed.')\n\n return Result.getOrThrow(Schema.decodeUnknownResult(FormResult)(await response.json()))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useCmsForm.js","names":[],"sources":["../../../../../src/runtime/forms/useCmsForm.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, ComputedRef } from 'vue'\nimport type { FormDefinition, FormField, FormStep, FormSubmission, FormValues } from '@internal/protocol/Forms'\nimport { FormResult } from '@internal/protocol/Forms'\nimport { validateFormValues } from '@internal/document/Forms'\nimport { Result, Schema } from 'effect'\nimport {\n computed,\n getCurrentInstance,\n getCurrentScope,\n onScopeDispose,\n readonly,\n ref,\n shallowRef,\n toValue,\n useId,\n watch,\n} from 'vue'\nimport { CMS_FORM_SUBMIT_PATH } from '@internal/protocol/HttpPaths'\n\nexport interface UseCmsFormOptions {\n /** Replace the HTTP transport, retaining validation and submission state. */\n readonly submit?: (submission: FormSubmission, signal: AbortSignal) => Promise<FormResult>\n}\n\nexport interface CmsFormControl {\n readonly field: FormField\n value: FormValues[string]\n readonly dirty: boolean\n readonly touched: boolean\n readonly valid: boolean\n readonly error: string | undefined\n readonly attrs: {\n readonly id: string\n readonly name: string\n readonly required: boolean\n readonly 'aria-invalid': 'true' | undefined\n readonly 'aria-describedby': string | undefined\n }\n readonly helpAttrs: { readonly id: string }\n readonly errorAttrs: { readonly id: string; readonly role: 'alert' }\n readonly touch: () => void\n}\n\nexport interface UseCmsFormReturn {\n readonly fields: ComputedRef<ReadonlyArray<FormField>>\n readonly steps: ComputedRef<ReadonlyArray<FormStep>>\n readonly values: Ref<Record<string, FormValues[string]>>\n readonly errors: Readonly<Ref<Readonly<Record<string, string>>>>\n readonly currentStep: ComputedRef<FormStep | undefined>\n readonly stepIndex: Readonly<Ref<number>>\n readonly isFirstStep: ComputedRef<boolean>\n readonly isLastStep: ComputedRef<boolean>\n readonly submitting: Readonly<Ref<boolean>>\n readonly isSuccess: Readonly<Ref<boolean>>\n readonly isPreview: ComputedRef<boolean>\n readonly submitError: Readonly<Ref<string | undefined>>\n readonly canSubmit: ComputedRef<boolean>\n readonly visibleControls: ComputedRef<ReadonlyArray<CmsFormControl>>\n readonly invalidControls: ComputedRef<ReadonlyArray<CmsFormControl>>\n readonly firstInvalidControlId: ComputedRef<string | undefined>\n readonly control: (field: string | FormField) => CmsFormControl\n readonly next: () => boolean\n readonly previous: () => void\n readonly validate: () => boolean\n readonly submit: () => Promise<boolean>\n readonly reset: () => void\n}\n\n/** Headless, per-instance form state. Definition changes preserve answers for unchanged field types. */\nexport function useCmsForm(input: MaybeRefOrGetter<FormDefinition>, options: UseCmsFormOptions = {}): UseCmsFormReturn {\n const fieldIdPrefix = getCurrentInstance() ? useId() : 'cms-form'\n const definition = computed(() => toValue(input))\n const steps = computed(() => definition.value.steps)\n const fields = computed(() => steps.value.flatMap((step) => step.fields))\n const values = ref<Record<string, FormValues[string]>>({})\n const errors = shallowRef<Record<string, string>>({})\n const stepIndex = shallowRef(0)\n const currentStep = computed(() => steps.value[stepIndex.value])\n const isFirstStep = computed(() => stepIndex.value === 0)\n const isLastStep = computed(() => stepIndex.value === steps.value.length - 1)\n const submitting = shallowRef(false)\n const isSuccess = shallowRef(false)\n const isPreview = computed(() => definition.value.preview)\n const submitError = shallowRef<string>()\n const touched = shallowRef(new Set<string>())\n const controls = new Map<string, CmsFormControl>()\n let controller: AbortController | undefined\n let generation = 0\n let previousFields = new Map<string, string>()\n\n const stop = watch(\n definition,\n (next, before) => {\n generation++\n controller?.abort()\n submitting.value = false\n\n const sameForm =\n before?.documentId === next.documentId &&\n JSON.stringify(before.nodePath) === JSON.stringify(next.nodePath) &&\n before.name === next.name\n\n values.value = Object.fromEntries(\n next.steps\n .flatMap((step) => step.fields)\n .map((field) => [\n field.id,\n sameForm && previousFields.get(field.id) === field.type\n ? (values.value[field.id] ?? null)\n : field.type === 'checkbox'\n ? false\n : field.type === 'number'\n ? null\n : '',\n ]),\n )\n previousFields = new Map(next.steps.flatMap((step) => step.fields).map((field) => [field.id, field.type]))\n stepIndex.value = Math.max(\n 0,\n next.steps.findIndex((step) => step.id === before?.steps[stepIndex.value]?.id),\n )\n errors.value = {}\n touched.value = new Set()\n submitError.value = undefined\n isSuccess.value = false\n },\n { immediate: true, deep: true, flush: 'sync' },\n )\n\n function validate(): boolean {\n touched.value = new Set(fields.value.map((field) => field.id))\n errors.value = validateFormValues(fields.value, values.value)\n\n return Object.keys(errors.value).length === 0\n }\n\n function control(input: string | FormField): CmsFormControl {\n const fieldId = Schema.is(Schema.String)(input) ? input : input.id\n const existing = controls.get(fieldId)\n\n if (existing) return existing\n\n if (!fields.value.some((candidate) => candidate.id === fieldId))\n throw new Error(`Unknown CMS form field ${fieldId}.`)\n\n const current = computed(() => {\n const candidate = fields.value.find((field) => field.id === fieldId)\n\n if (!candidate) throw new Error(`CMS form field ${fieldId} no longer exists.`)\n\n return candidate\n })\n\n const controlId = `${fieldIdPrefix}-${encodeURIComponent(fieldId)}`\n const helpId = `${controlId}-help`\n const errorId = `${controlId}-error`\n const helpAttrs: CmsFormControl['helpAttrs'] = { id: helpId }\n const errorAttrs: CmsFormControl['errorAttrs'] = { id: errorId, role: 'alert' }\n\n const refreshError = () => {\n const issue = validateFormValues([current.value], values.value)[fieldId]\n const nextErrors = { ...errors.value }\n\n if (issue) nextErrors[fieldId] = issue\n else delete nextErrors[fieldId]\n errors.value = nextErrors\n }\n\n const value = computed({\n get: () => values.value[fieldId] ?? emptyFormValue(current.value),\n set: (next) => {\n values.value[fieldId] = normalizeFormValue(current.value, next)\n\n if (touched.value.has(fieldId) || errors.value[fieldId]) refreshError()\n },\n })\n\n const dirty = computed(() => values.value[fieldId] !== emptyFormValue(current.value))\n\n const touchedState = computed(() => touched.value.has(fieldId))\n\n const valid = computed(() => validateFormValues([current.value], values.value)[fieldId] === undefined)\n\n const error = computed(() => errors.value[fieldId])\n\n const attrs = computed<CmsFormControl['attrs']>(() => {\n const fieldError = errors.value[fieldId]\n\n const describedBy = [current.value.help ? helpId : undefined, fieldError ? errorId : undefined]\n .filter((part) => part !== undefined)\n .join(' ')\n\n return {\n id: controlId,\n name: fieldId,\n required: current.value.required === true,\n 'aria-invalid': fieldError ? 'true' : undefined,\n 'aria-describedby': describedBy || undefined,\n }\n })\n\n const state: CmsFormControl = {\n get field() {\n return current.value\n },\n get value() {\n return value.value\n },\n set value(next) {\n value.value = next\n },\n get dirty() {\n return dirty.value\n },\n get touched() {\n return touchedState.value\n },\n get valid() {\n return valid.value\n },\n get error() {\n return error.value\n },\n get attrs() {\n return attrs.value\n },\n helpAttrs,\n errorAttrs,\n touch: () => {\n touched.value = new Set([...touched.value, fieldId])\n refreshError()\n },\n }\n\n controls.set(fieldId, state)\n\n return state\n }\n\n function next(): boolean {\n if (submitting.value || isLastStep.value) return false\n const stepFields = currentStep.value?.fields ?? []\n touched.value = new Set([...touched.value, ...stepFields.map((field) => field.id)])\n const issues = validateFormValues(stepFields, values.value)\n errors.value = {\n ...Object.fromEntries(\n Object.entries(errors.value).filter(([id]) => !stepFields.some((field) => field.id === id)),\n ),\n ...issues,\n }\n\n if (Object.keys(issues).length) return false\n stepIndex.value++\n\n return true\n }\n\n function previous(): void {\n if (!submitting.value && !isFirstStep.value) stepIndex.value--\n }\n\n function reset(): void {\n generation++\n controller?.abort()\n submitting.value = false\n values.value = Object.fromEntries(fields.value.map((field) => [field.id, emptyFormValue(field)]))\n errors.value = {}\n touched.value = new Set()\n submitError.value = undefined\n isSuccess.value = false\n stepIndex.value = 0\n }\n\n // oxlint-disable-next-line effecttsgo/async-function -- This Vue composable exposes a Promise to component submit handlers.\n async function submit(): Promise<boolean> {\n if (submitting.value || isSuccess.value) return false\n submitError.value = undefined\n\n if (!validate()) {\n stepIndex.value = Math.max(\n 0,\n steps.value.findIndex((step) => step.fields.some((field) => errors.value[field.id])),\n )\n\n return false\n }\n\n if (isPreview.value) {\n isSuccess.value = true\n\n return true\n }\n\n const currentGeneration = ++generation\n controller = new AbortController()\n submitting.value = true\n\n try {\n const { documentId, nodePath, revision } = definition.value\n\n const result = await (options.submit ?? send)(\n { documentId, nodePath, revision, values: { ...values.value } },\n controller.signal,\n )\n\n if (generation !== currentGeneration) return false\n errors.value = result.status === 'invalid' ? { ...result.errors } : {}\n isSuccess.value = result.status === 'accepted'\n\n if (result.status === 'invalid')\n stepIndex.value = Math.max(\n 0,\n steps.value.findIndex((step) => step.fields.some((field) => errors.value[field.id])),\n )\n\n if (result.status !== 'accepted' && result.status !== 'invalid')\n submitError.value =\n result.status === 'stale'\n ? 'This form has changed. Reload the page before submitting.'\n : 'Submission failed. Please try again.'\n\n return isSuccess.value\n } catch {\n if (generation === currentGeneration) submitError.value = 'Submission failed. Please try again.'\n\n return false\n } finally {\n if (generation === currentGeneration) submitting.value = false\n }\n }\n\n if (getCurrentScope())\n onScopeDispose(() => {\n generation++\n controller?.abort()\n stop()\n })\n\n const visibleControls = computed(() => currentStep.value?.fields.map((entry) => control(entry)) ?? [])\n const invalidControls = computed(() => fields.value.map((entry) => control(entry)).filter((entry) => !entry.valid))\n const firstInvalidControlId = computed(() => invalidControls.value[0]?.attrs.id)\n const canSubmit = computed(() => !submitting.value && !isSuccess.value && invalidControls.value.length === 0)\n\n return {\n fields,\n steps,\n values,\n errors: readonly(errors),\n currentStep,\n stepIndex: readonly(stepIndex),\n isFirstStep,\n isLastStep,\n submitting: readonly(submitting),\n isSuccess: readonly(isSuccess),\n isPreview,\n submitError: readonly(submitError),\n canSubmit,\n visibleControls,\n invalidControls,\n firstInvalidControlId,\n control,\n next,\n previous,\n validate,\n submit,\n reset,\n }\n}\n\nfunction normalizeFormValue(field: FormField, input: FormValues[string] | undefined): FormValues[string] {\n if (field.type === 'checkbox') return input === true\n\n if (field.type === 'number') {\n if (input === '' || input == null) return null\n const value = Schema.is(Schema.Finite)(input) || Schema.is(Schema.String)(input) ? Number(input) : Number.NaN\n\n if (Number.isFinite(value)) return value\n\n return Schema.is(Schema.String)(input) ? input : null\n }\n\n return Schema.is(Schema.String)(input) ? input : ''\n}\n\nfunction emptyFormValue(field: FormField): FormValues[string] {\n if (field.type === 'checkbox') return false\n\n if (field.type === 'number') return null\n\n return ''\n}\n\n// oxlint-disable-next-line effecttsgo/async-function -- This browser transport implements the composable's Promise-based submission seam.\nasync function send(submission: FormSubmission, signal: AbortSignal): Promise<FormResult> {\n // oxlint-disable-next-line effecttsgo/global-fetch -- Visitor form submission is a small browser transport that must preserve the caller's AbortSignal.\n const response = await fetch(CMS_FORM_SUBMIT_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(submission),\n signal,\n })\n\n if (!response.ok) throw new Error('Submission failed.')\n\n return Result.getOrThrow(Schema.decodeUnknownResult(FormResult)(await response.json()))\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCmsI18n.js","names":["localePath","resolveLocalePath","switchLocalePath","resolveSwitchLocalePath"],"sources":["../../../../../src/runtime/i18n/useCmsI18n.ts"],"sourcesContent":["import configuration from '#cms/i18n'\nimport { useRoute, useState } from 'nuxt/app'\nimport { computed, type ComputedRef } from 'vue'\nimport type { CmsLocale, CmsMessageKey, CmsMessageParameters } from '../../I18n'\nimport {\n localeFromPath,\n localePath as resolveLocalePath,\n switchLocalePath as resolveSwitchLocalePath,\n translate,\n} from './translator'\n\nexport interface UseCmsI18nReturn {\n readonly locale: ComputedRef<CmsLocale>\n readonly localePath: (path: string, code?: CmsLocale) => string\n readonly switchLocalePath: (code: CmsLocale, destination?: string) => string\n readonly t: <Key extends CmsMessageKey>(\n key: Key,\n ...arguments_: keyof CmsMessageParameters<Key> extends never\n ? [parameters?: undefined]\n : [parameters: CmsMessageParameters<Key>]\n ) => string\n}\n\n/** URL-owned locale is reactive and isolated to the current Nuxt application/request. */\nexport function useCmsI18n(): UseCmsI18nReturn {\n const route = useRoute()\n const documentLocale = useState<string | undefined>('cms-document-locale', () => undefined)\n\n // SAFETY: compileCmsI18n generated CmsLocale from these exact runtime locale keys.\n const locale = computed<CmsLocale>(\n () => (documentLocale.value ?? localeFromPath(configuration, route.path)) as CmsLocale,\n )\n\n const warned = new Set<string>()\n\n function missing(key: string, code: string): void {\n const id = `${code}:${key}`\n\n if (!import.meta.dev || warned.has(id)) return\n warned.add(id)\n // Browser diagnostics should not add an Effect runtime to the website translation bundle.\n // oxlint-disable-next-line effecttsgo/global-console\n console.warn(`[cms i18n] Missing message \"${key}\" for locale \"${code}\".`)\n }\n\n function t<Key extends CmsMessageKey>(\n key: Key,\n ...arguments_: keyof CmsMessageParameters<Key> extends never\n ? [parameters?: undefined]\n : [parameters: CmsMessageParameters<Key>]\n ): string {\n return translate(configuration, locale.value, key, arguments_[0], missing)\n }\n\n function localePath(path: string, code: CmsLocale = locale.value): string {\n return resolveLocalePath(configuration, path, code)\n }\n\n function switchLocalePath(code: CmsLocale, destination?: string): string {\n return destination === undefined\n ? resolveSwitchLocalePath(configuration, route.fullPath, code)\n : resolveLocalePath(configuration, destination, code)\n }\n\n return { locale, localePath, switchLocalePath, t }\n}\n"],"mappings":";;;;;;;AAwBA,SAAgB,aAA+B;CAC7C,MAAM,QAAQ,SAAS;CACvB,MAAM,iBAAiB,SAA6B,6BAA6B,MAAS;CAG1F,MAAM,SAAS,eACN,eAAe,SAAS,eAAe,eAAe,MAAM,IAAI,CACzE;CAEA,MAAM,yBAAS,IAAI,IAAY;CAE/B,SAAS,QAAQ,KAAa,MAAoB;EAChD,MAAM,KAAK,GAAG,KAAK,GAAG;EAEtB,IAAI,CAAC,YAAY,OAAO,OAAO,IAAI,EAAE,GAAG;EACxC,OAAO,IAAI,EAAE;EAGb,QAAQ,KAAK,+BAA+B,IAAI,gBAAgB,KAAK,GAAG;CAC1E;CAEA,SAAS,EACP,KACA,GAAG,YAGK;EACR,OAAO,UAAU,eAAe,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO;CAC3E;CAEA,SAASA,aAAW,MAAc,OAAkB,OAAO,OAAe;EACxE,OAAOC,WAAkB,eAAe,MAAM,IAAI;CACpD;CAEA,SAASC,mBAAiB,MAAiB,aAA8B;EACvE,OAAO,gBAAgB,SACnBC,iBAAwB,eAAe,MAAM,UAAU,IAAI,IAC3DF,WAAkB,eAAe,aAAa,IAAI;CACxD;CAEA,OAAO;EAAE;EAAQ;EAAY;EAAkB;CAAE;AACnD"}
1
+ {"version":3,"file":"useCmsI18n.js","names":["localePath","resolveLocalePath","switchLocalePath","resolveSwitchLocalePath"],"sources":["../../../../../src/runtime/i18n/useCmsI18n.ts"],"sourcesContent":["import configuration from '#cms/i18n'\nimport { useRoute, useState } from 'nuxt/app'\nimport { computed, type ComputedRef } from 'vue'\nimport type { CmsLocale, CmsMessageKey, CmsMessageParameters } from '../../I18n'\nimport {\n localeFromPath,\n localePath as resolveLocalePath,\n switchLocalePath as resolveSwitchLocalePath,\n translate,\n} from './translator'\n\nexport interface UseCmsI18nReturn {\n readonly locale: ComputedRef<CmsLocale>\n readonly localePath: (path: string, code?: CmsLocale) => string\n readonly switchLocalePath: (code: CmsLocale, destination?: string) => string\n readonly t: <Key extends CmsMessageKey>(\n key: Key,\n ...arguments_: keyof CmsMessageParameters<Key> extends never\n ? [parameters?: undefined]\n : [parameters: CmsMessageParameters<Key>]\n ) => string\n}\n\n/** URL-owned locale is reactive and isolated to the current Nuxt application/request. */\nexport function useCmsI18n(): UseCmsI18nReturn {\n const route = useRoute()\n const documentLocale = useState<string | undefined>('cms-document-locale', () => undefined)\n\n // SAFETY: compileCmsI18n generated CmsLocale from these exact runtime locale keys.\n const locale = computed<CmsLocale>(\n () => (documentLocale.value ?? localeFromPath(configuration, route.path)) as CmsLocale,\n )\n\n const warned = new Set<string>()\n\n function missing(key: string, code: string): void {\n const id = `${code}:${key}`\n\n if (!import.meta.dev || warned.has(id)) return\n warned.add(id)\n // oxlint-disable-next-line effecttsgo/global-console -- Browser diagnostics should not add an Effect runtime to the website translation bundle.\n console.warn(`[cms i18n] Missing message \"${key}\" for locale \"${code}\".`)\n }\n\n function t<Key extends CmsMessageKey>(\n key: Key,\n ...arguments_: keyof CmsMessageParameters<Key> extends never\n ? [parameters?: undefined]\n : [parameters: CmsMessageParameters<Key>]\n ): string {\n return translate(configuration, locale.value, key, arguments_[0], missing)\n }\n\n function localePath(path: string, code: CmsLocale = locale.value): string {\n return resolveLocalePath(configuration, path, code)\n }\n\n function switchLocalePath(code: CmsLocale, destination?: string): string {\n return destination === undefined\n ? resolveSwitchLocalePath(configuration, route.fullPath, code)\n : resolveLocalePath(configuration, destination, code)\n }\n\n return { locale, localePath, switchLocalePath, t }\n}\n"],"mappings":";;;;;;;AAwBA,SAAgB,aAA+B;CAC7C,MAAM,QAAQ,SAAS;CACvB,MAAM,iBAAiB,SAA6B,6BAA6B,MAAS;CAG1F,MAAM,SAAS,eACN,eAAe,SAAS,eAAe,eAAe,MAAM,IAAI,CACzE;CAEA,MAAM,yBAAS,IAAI,IAAY;CAE/B,SAAS,QAAQ,KAAa,MAAoB;EAChD,MAAM,KAAK,GAAG,KAAK,GAAG;EAEtB,IAAI,CAAC,YAAY,OAAO,OAAO,IAAI,EAAE,GAAG;EACxC,OAAO,IAAI,EAAE;EAEb,QAAQ,KAAK,+BAA+B,IAAI,gBAAgB,KAAK,GAAG;CAC1E;CAEA,SAAS,EACP,KACA,GAAG,YAGK;EACR,OAAO,UAAU,eAAe,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO;CAC3E;CAEA,SAASA,aAAW,MAAc,OAAkB,OAAO,OAAe;EACxE,OAAOC,WAAkB,eAAe,MAAM,IAAI;CACpD;CAEA,SAASC,mBAAiB,MAAiB,aAA8B;EACvE,OAAO,gBAAgB,SACnBC,iBAAwB,eAAe,MAAM,UAAU,IAAI,IAC3DF,WAAkB,eAAe,aAAa,IAAI;CACxD;CAEA,OAAO;EAAE;EAAQ;EAAY;EAAkB;CAAE;AACnD"}
@@ -1,3 +1,3 @@
1
- import { n as scopedDatabaseUrl, r as toS3MediaObjectStoreOptions, t as installCms } from "../installation-BNuoRDOz.js";
1
+ import { n as scopedDatabaseUrl, r as toS3MediaObjectStoreOptions, t as installCms } from "../installation-fZtNxtoW.js";
2
2
 
3
3
  export { installCms, scopedDatabaseUrl, toS3MediaObjectStoreOptions };
@@ -1,5 +1,5 @@
1
1
  import { resolveCmsInstallation } from "../internal/configuration.js";
2
- import { t as installCms } from "../installation-BNuoRDOz.js";
2
+ import { t as installCms } from "../installation-fZtNxtoW.js";
3
3
  import { diagnosticFromCause, renderDiagnostic } from "../internal/diagnostics.js";
4
4
  import { Effect } from "effect";
5
5
  import { defineNitroPlugin, useNitroApp, useRuntimeConfig } from "nitropack/runtime";
@@ -4,7 +4,7 @@ import { n as readPreviewLocation, r as withPreviewLocation } from "../../locati
4
4
  import { formComponentsKey } from "../delivery/blocks.js";
5
5
  import { documentPreviewStoreKey } from "../delivery/preview.js";
6
6
  import { mountPreviewController } from "./controller.js";
7
- import { t as createStudioPreviewStore } from "../../store-B49J21NA.js";
7
+ import { t as createStudioPreviewStore } from "../../store-CCsjHaZm.js";
8
8
  import { Result, Schema } from "effect";
9
9
  import { inject, watch } from "vue";
10
10
  import { defineNuxtPlugin, navigateTo, onNuxtReady, useNuxtApp, useState } from "nuxt/app";
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.client.js","names":[],"sources":["../../../../../src/runtime/preview/plugin.client.ts"],"sourcesContent":["import { formComponentsKey } from '../delivery/blocks'\nimport { defineNuxtPlugin, navigateTo, onNuxtReady, useNuxtApp, useState } from 'nuxt/app'\nimport { inject, watch } from 'vue'\nimport { PublicPath } from '@internal/protocol/Documents'\nimport { readPreviewLocation, withPreviewLocation } from '@internal/protocol/Preview'\nimport { Result, Schema } from 'effect'\nimport { openSitePreviewConnection } from './connection'\nimport { mountPreviewController } from './controller'\nimport { createStudioPreviewStore, documentPreviewStoreKey } from './store'\n\nexport default defineNuxtPlugin({\n name: 'cms-preview',\n dependsOn: ['cms-blocks'],\n setup: () => {\n const parameters = readPreviewLocation(new URL(window.location.href))\n\n if (!parameters) return\n const route = () => Result.getOrThrow(Schema.decodeResult(PublicPath)(window.location.pathname))\n const forms = inject(formComponentsKey)\n const store = createStudioPreviewStore(route, () => forms?.value ?? [])\n const nuxtApp = useNuxtApp()\n nuxtApp.vueApp.provide(documentPreviewStoreKey, store)\n nuxtApp.hooks.hook('page:finish', store.routeChanged)\n\n const error = useState<string | null>('cms:preview-error', () => null)\n let patchError: string | null = null\n\n const mount = async () => {\n const connection = await openSitePreviewConnection({\n editorOrigin: parameters.editorOrigin,\n route: route(),\n sessionId: parameters.sessionId,\n })\n\n const sendError = () => connection.send({ type: 'preview:error', message: error.value ?? patchError })\n const stopError = watch(error, sendError, { immediate: true })\n\n const stopConnect = connection.subscribe((message) => {\n if (message.type === 'studio:connect') sendError()\n })\n\n const unmount = mountPreviewController({\n bridge: connection,\n onPatchError: (message) => {\n patchError = message\n sendError()\n },\n navigate: async (path) => {\n const target = withPreviewLocation(new URL(path, window.location.origin), parameters)\n await navigateTo(`${target.pathname}${target.search}${target.hash}`)\n },\n root: document,\n store,\n view: window,\n })\n\n window.addEventListener(\n 'pagehide',\n () => {\n stopError()\n stopConnect()\n unmount()\n void connection.close()\n },\n { once: true },\n )\n }\n\n onNuxtReady(mount)\n },\n})\n"],"mappings":";;;;;;;;;;;;AAUA,4BAAe,iBAAiB;CAC9B,MAAM;CACN,WAAW,CAAC,YAAY;CACxB,aAAa;EACX,MAAM,aAAa,oBAAoB,IAAI,IAAI,OAAO,SAAS,IAAI,CAAC;EAEpE,IAAI,CAAC,YAAY;EACjB,MAAM,cAAc,OAAO,WAAW,OAAO,aAAa,UAAU,CAAC,CAAC,OAAO,SAAS,QAAQ,CAAC;EAC/F,MAAM,QAAQ,OAAO,iBAAiB;EACtC,MAAM,QAAQ,yBAAyB,aAAa,OAAO,SAAS,CAAC,CAAC;EACtE,MAAM,UAAU,WAAW;EAC3B,QAAQ,OAAO,QAAQ,yBAAyB,KAAK;EACrD,QAAQ,MAAM,KAAK,eAAe,MAAM,YAAY;EAEpD,MAAM,QAAQ,SAAwB,2BAA2B,IAAI;EACrE,IAAI,aAA4B;EAEhC,MAAM,QAAQ,YAAY;GACxB,MAAM,aAAa,MAAM,0BAA0B;IACjD,cAAc,WAAW;IACzB,OAAO,MAAM;IACb,WAAW,WAAW;GACxB,CAAC;GAED,MAAM,kBAAkB,WAAW,KAAK;IAAE,MAAM;IAAiB,SAAS,MAAM,SAAS;GAAW,CAAC;GACrG,MAAM,YAAY,MAAM,OAAO,WAAW,EAAE,WAAW,KAAK,CAAC;GAE7D,MAAM,cAAc,WAAW,WAAW,YAAY;IACpD,IAAI,QAAQ,SAAS,kBAAkB,UAAU;GACnD,CAAC;GAED,MAAM,UAAU,uBAAuB;IACrC,QAAQ;IACR,eAAe,YAAY;KACzB,aAAa;KACb,UAAU;IACZ;IACA,UAAU,OAAO,SAAS;KACxB,MAAM,SAAS,oBAAoB,IAAI,IAAI,MAAM,OAAO,SAAS,MAAM,GAAG,UAAU;KACpF,MAAM,WAAW,GAAG,OAAO,WAAW,OAAO,SAAS,OAAO,MAAM;IACrE;IACA,MAAM;IACN;IACA,MAAM;GACR,CAAC;GAED,OAAO,iBACL,kBACM;IACJ,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,AAAK,WAAW,MAAM;GACxB,GACA,EAAE,MAAM,KAAK,CACf;EACF;EAEA,YAAY,KAAK;CACnB;AACF,CAAC"}
1
+ {"version":3,"file":"plugin.client.js","names":[],"sources":["../../../../../src/runtime/preview/plugin.client.ts"],"sourcesContent":["import { formComponentsKey } from '../delivery/blocks'\nimport { defineNuxtPlugin, navigateTo, onNuxtReady, useNuxtApp, useState } from 'nuxt/app'\nimport { inject, watch } from 'vue'\nimport { PublicPath } from '@internal/protocol/Documents'\nimport { readPreviewLocation, withPreviewLocation } from '@internal/protocol/Preview'\nimport { Result, Schema } from 'effect'\nimport { openSitePreviewConnection } from './connection'\nimport { mountPreviewController } from './controller'\nimport { createStudioPreviewStore, documentPreviewStoreKey } from './store'\n\nexport default defineNuxtPlugin({\n name: 'cms-preview',\n dependsOn: ['cms-blocks'],\n setup: () => {\n const parameters = readPreviewLocation(new URL(window.location.href))\n\n if (!parameters) return\n const route = () => Result.getOrThrow(Schema.decodeResult(PublicPath)(window.location.pathname))\n const forms = inject(formComponentsKey)\n const store = createStudioPreviewStore(route, () => forms?.value ?? [])\n const nuxtApp = useNuxtApp()\n nuxtApp.vueApp.provide(documentPreviewStoreKey, store)\n nuxtApp.hooks.hook('page:finish', store.routeChanged)\n\n const error = useState<string | null>('cms:preview-error', () => null)\n let patchError: string | null = null\n\n // oxlint-disable-next-line effecttsgo/async-function -- Nuxt's ready hook awaits this Promise-based browser connection setup.\n const mount = async () => {\n const connection = await openSitePreviewConnection({\n editorOrigin: parameters.editorOrigin,\n route: route(),\n sessionId: parameters.sessionId,\n })\n\n const sendError = () => connection.send({ type: 'preview:error', message: error.value ?? patchError })\n const stopError = watch(error, sendError, { immediate: true })\n\n const stopConnect = connection.subscribe((message) => {\n if (message.type === 'studio:connect') sendError()\n })\n\n const unmount = mountPreviewController({\n bridge: connection,\n onPatchError: (message) => {\n patchError = message\n sendError()\n },\n // oxlint-disable-next-line effecttsgo/async-function -- The preview controller accepts Nuxt's Promise-based navigation callback.\n navigate: async (path) => {\n const target = withPreviewLocation(new URL(path, window.location.origin), parameters)\n await navigateTo(`${target.pathname}${target.search}${target.hash}`)\n },\n root: document,\n store,\n view: window,\n })\n\n window.addEventListener(\n 'pagehide',\n () => {\n stopError()\n stopConnect()\n unmount()\n void connection.close()\n },\n { once: true },\n )\n }\n\n onNuxtReady(mount)\n },\n})\n"],"mappings":";;;;;;;;;;;;AAUA,4BAAe,iBAAiB;CAC9B,MAAM;CACN,WAAW,CAAC,YAAY;CACxB,aAAa;EACX,MAAM,aAAa,oBAAoB,IAAI,IAAI,OAAO,SAAS,IAAI,CAAC;EAEpE,IAAI,CAAC,YAAY;EACjB,MAAM,cAAc,OAAO,WAAW,OAAO,aAAa,UAAU,CAAC,CAAC,OAAO,SAAS,QAAQ,CAAC;EAC/F,MAAM,QAAQ,OAAO,iBAAiB;EACtC,MAAM,QAAQ,yBAAyB,aAAa,OAAO,SAAS,CAAC,CAAC;EACtE,MAAM,UAAU,WAAW;EAC3B,QAAQ,OAAO,QAAQ,yBAAyB,KAAK;EACrD,QAAQ,MAAM,KAAK,eAAe,MAAM,YAAY;EAEpD,MAAM,QAAQ,SAAwB,2BAA2B,IAAI;EACrE,IAAI,aAA4B;EAGhC,MAAM,QAAQ,YAAY;GACxB,MAAM,aAAa,MAAM,0BAA0B;IACjD,cAAc,WAAW;IACzB,OAAO,MAAM;IACb,WAAW,WAAW;GACxB,CAAC;GAED,MAAM,kBAAkB,WAAW,KAAK;IAAE,MAAM;IAAiB,SAAS,MAAM,SAAS;GAAW,CAAC;GACrG,MAAM,YAAY,MAAM,OAAO,WAAW,EAAE,WAAW,KAAK,CAAC;GAE7D,MAAM,cAAc,WAAW,WAAW,YAAY;IACpD,IAAI,QAAQ,SAAS,kBAAkB,UAAU;GACnD,CAAC;GAED,MAAM,UAAU,uBAAuB;IACrC,QAAQ;IACR,eAAe,YAAY;KACzB,aAAa;KACb,UAAU;IACZ;IAEA,UAAU,OAAO,SAAS;KACxB,MAAM,SAAS,oBAAoB,IAAI,IAAI,MAAM,OAAO,SAAS,MAAM,GAAG,UAAU;KACpF,MAAM,WAAW,GAAG,OAAO,WAAW,OAAO,SAAS,OAAO,MAAM;IACrE;IACA,MAAM;IACN;IACA,MAAM;GACR,CAAC;GAED,OAAO,iBACL,kBACM;IACJ,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,AAAK,WAAW,MAAM;GACxB,GACA,EAAE,MAAM,KAAK,CACf;EACF;EAEA,YAAY,KAAK;CACnB;AACF,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import { documentPreviewStoreKey } from "../delivery/preview.js";
2
- import { t as createStudioPreviewStore } from "../../store-B49J21NA.js";
2
+ import { t as createStudioPreviewStore } from "../../store-CCsjHaZm.js";
3
3
 
4
4
  export { createStudioPreviewStore, documentPreviewStoreKey };
@@ -6,7 +6,7 @@ import { prepareFormDocument } from "./runtime/forms/document.js";
6
6
  import { CMS_PREVIEW_INLINE_HOST_ATTRIBUTE, CMS_PREVIEW_MARKER_TAG, CMS_PREVIEW_NODE_ATTRIBUTE, CMS_PREVIEW_TARGET_ATTRIBUTE, CMS_PREVIEW_WRAPPER_ATTRIBUTE } from "./runtime/preview/protocol.js";
7
7
  import { toPreviewRenderDocument } from "./runtime/preview/comark-document.js";
8
8
  import { Predicate, Result, Schema } from "effect";
9
- import { shallowReactive } from "vue";
9
+ import { shallowRef } from "vue";
10
10
  import { createComarkContext } from "comark";
11
11
 
12
12
  //#region ../document/src/PreviewProjection.ts
@@ -139,11 +139,11 @@ function hasInlineImplicitDefaultContent(nodes) {
139
139
  //#region src/runtime/preview/store.ts
140
140
  function createStudioPreviewStore(route, forms = () => []) {
141
141
  const context = createComarkContext();
142
- const patches = shallowReactive(/* @__PURE__ */ new Map());
142
+ const patches = shallowRef(/* @__PURE__ */ new Map());
143
143
  const registrations = /* @__PURE__ */ new Map();
144
144
  const listeners = /* @__PURE__ */ new Set();
145
145
  const manifest = () => ({
146
- documents: renderedDocuments(registrations.values(), patches),
146
+ documents: renderedDocuments(registrations.values(), patches.value),
147
147
  route: route()
148
148
  });
149
149
  const notify = () => {
@@ -158,7 +158,7 @@ function createStudioPreviewStore(route, forms = () => []) {
158
158
  ...document,
159
159
  body: preview.body
160
160
  }, forms(), true);
161
- patches.set(document.id, patched);
161
+ patches.value = new Map(patches.value).set(document.id, patched);
162
162
  const renderBody = toPreviewRenderDocument(patched.body);
163
163
  context.get(document.id, renderBody).set(renderBody);
164
164
  notify();
@@ -177,7 +177,7 @@ function createStudioPreviewStore(route, forms = () => []) {
177
177
  };
178
178
  },
179
179
  resolve(document) {
180
- return patches.get(document.id) ?? document;
180
+ return patches.value.get(document.id) ?? document;
181
181
  },
182
182
  routeChanged: notify,
183
183
  subscribe(listener) {
@@ -223,4 +223,4 @@ function createPreviewDocument(body) {
223
223
 
224
224
  //#endregion
225
225
  export { createStudioPreviewStore as t };
226
- //# sourceMappingURL=store-B49J21NA.js.map
226
+ //# sourceMappingURL=store-CCsjHaZm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-CCsjHaZm.js","names":["CmsMarkdownDocumentSchema"],"sources":["../../../../document/src/PreviewProjection.ts","../../../src/runtime/preview/store.ts"],"sourcesContent":["import type { CmsMarkdownDocument, MarkdownNode } from '@internal/protocol/Documents'\nimport type { EditorNodeId } from '@internal/protocol/Documents'\nimport { CmsMarkdownDocument as CmsMarkdownDocumentSchema } from '@internal/protocol/Documents'\nimport { Predicate, Result, Schema } from 'effect'\nimport { mutableJson, mutableJsonObject } from './internal/comark/document-adapter'\nimport { editorMetadata } from './internal/editor/metadata'\n\nexport interface PreviewProjectionMarkers {\n readonly inlineHostAttribute: string\n readonly markerTag: string\n readonly nodeAttribute: string\n readonly targetAttribute: string\n readonly wrapperAttribute: string\n}\n\nexport interface PreviewProjection {\n readonly body: CmsMarkdownDocument\n readonly fallbacks: Readonly<Record<string, string>>\n}\n\nexport class PreviewProjectionInvalid extends Schema.TaggedError<PreviewProjectionInvalid>()(\n 'PreviewProjection.Invalid',\n {\n message: Schema.NonEmptyString,\n cause: Schema.Defect(),\n },\n) {}\n\nconst NATIVE_BLOCK_TAGS = new Set([\n 'blockquote',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'hr',\n 'img',\n 'li',\n 'math',\n 'ol',\n 'p',\n 'pre',\n 'table',\n 'tbody',\n 'td',\n 'th',\n 'thead',\n 'tr',\n 'ul',\n])\n\nconst INLINE_TAGS = new Set(['a', 'br', 'code', 'del', 'em', 'img', 'strong', 'u'])\n\nexport function projectPreviewDocument(\n body: CmsMarkdownDocument,\n markers: PreviewProjectionMarkers,\n): Result.Result<PreviewProjection, PreviewProjectionInvalid> {\n return Result.try({\n try: () => projectPreviewDocumentUnsafe(body, markers),\n catch: (cause) =>\n new PreviewProjectionInvalid({\n cause,\n message: cause instanceof Error && cause.message ? cause.message : 'The preview projection is invalid.',\n }),\n })\n}\n\nfunction projectPreviewDocumentUnsafe(body: CmsMarkdownDocument, markers: PreviewProjectionMarkers): PreviewProjection {\n const fallbacks: Record<string, string> = {}\n let identifiedTargets = 0\n\n const cloneNodes = (nodes: ReadonlyArray<MarkdownNode>, componentId?: EditorNodeId): ReadonlyArray<Schema.Json> =>\n nodes.map((node) => {\n if (Predicate.isString(node) || node[0] === null) return mutableJson(node)\n const [tag, originalAttributes, ...originalChildren] = node\n const isSlot = tag === 'template'\n const isNative = NATIVE_BLOCK_TAGS.has(tag)\n const attributes = mutableJsonObject(originalAttributes)\n const metadata = editorMetadata(attributes)\n const id = metadata?.nodeId\n\n if (id) {\n identifiedTargets++\n\n if (componentId) fallbacks[id] = componentId\n\n if (!isSlot && !isNative) {\n attributes[markers.nodeAttribute] = id\n attributes[markers.targetAttribute] = 'component'\n }\n }\n\n const nextComponentId = !isNative && !isSlot && id ? id : componentId\n const children = [...cloneNodes(originalChildren, nextComponentId)]\n\n if (id && metadata?.implicitSlotId && !isNative && !isSlot && hasImplicitDefaultContent(originalChildren)) {\n const slotId = metadata.implicitSlotId\n fallbacks[slotId] = id\n const markerChildren = hasInlineImplicitDefaultContent(originalChildren) ? children.splice(0) : []\n\n const markerAttributes = {\n [markers.nodeAttribute]: slotId,\n [markers.targetAttribute]: 'slot',\n [markers.wrapperAttribute]: '',\n }\n\n if (markerChildren.length) markerAttributes[markers.inlineHostAttribute] = ''\n children.unshift([markers.markerTag, markerAttributes, ...markerChildren])\n }\n\n if (isSlot && id) {\n children.unshift([\n markers.markerTag,\n {\n [markers.nodeAttribute]: id,\n [markers.targetAttribute]: 'slot',\n [markers.wrapperAttribute]: '',\n },\n ])\n }\n\n const cloned: Schema.Json = [tag, attributes, ...children]\n\n if (id && isNative) return previewWrapper(id, 'native', cloned, markers)\n\n if (id && !isNative && !isSlot) return previewWrapper(id, 'component', cloned, markers)\n\n return cloned\n })\n\n const nodes = cloneNodes(body.nodes)\n\n if (body.nodes.length && !identifiedTargets)\n throw new Error('Studio preview requires an identified authoring document.')\n\n return {\n body: CmsMarkdownDocumentSchema.make({\n frontmatter: mutableJsonObject(body.frontmatter),\n meta: mutableJsonObject(body.meta),\n nodes,\n }),\n fallbacks,\n }\n}\n\nfunction previewWrapper(\n nodeId: EditorNodeId,\n kind: 'component' | 'native' | 'slot',\n child: Schema.Json,\n markers: PreviewProjectionMarkers,\n): Schema.Json {\n return [\n markers.markerTag,\n {\n [markers.nodeAttribute]: nodeId,\n [markers.targetAttribute]: kind,\n [markers.wrapperAttribute]: '',\n },\n child,\n ]\n}\n\nfunction hasImplicitDefaultContent(nodes: ReadonlyArray<MarkdownNode>): boolean {\n const hasExplicitDefault = nodes.some(\n (node) => Array.isArray(node) && node[0] === 'template' && node[1].name === 'default',\n )\n\n return (\n !hasExplicitDefault && nodes.some((node) => (Predicate.isString(node) ? Boolean(node) : node[0] !== 'template'))\n )\n}\n\nfunction hasInlineImplicitDefaultContent(nodes: ReadonlyArray<MarkdownNode>): boolean {\n return (\n nodes.length > 0 &&\n nodes.every(\n (node) => Predicate.isString(node) || (Array.isArray(node) && node[0] !== null && INLINE_TAGS.has(node[0])),\n )\n )\n}\n","import { prepareFormDocument } from '../forms/document'\nimport type { ComponentDefinition } from '@internal/protocol/Configuration'\nimport type { RenderedCompiledDocument, RenderedDocumentManifest } from '@internal/protocol/Preview'\nimport { projectPreviewDocument } from '@internal/document/PreviewProjection'\nimport { CollectionName } from '@internal/protocol/Configuration'\nimport { DocumentId, PublicPath } from '@internal/protocol/Documents'\nimport { Result } from 'effect'\nimport { createComarkContext } from 'comark'\nimport { shallowRef } from 'vue'\nimport type { DeliveryDocument as PreviewCompiledDocument } from '../delivery/types'\nimport type { DocumentPreviewStore, PreviewDocumentRole } from '../delivery/preview'\nimport { toPreviewRenderDocument } from './comark-document'\nimport {\n CMS_PREVIEW_INLINE_HOST_ATTRIBUTE,\n CMS_PREVIEW_MARKER_TAG,\n CMS_PREVIEW_NODE_ATTRIBUTE,\n CMS_PREVIEW_TARGET_ATTRIBUTE,\n CMS_PREVIEW_WRAPPER_ATTRIBUTE,\n} from './protocol'\n\nexport { documentPreviewStoreKey, type DocumentPreviewStore, type PreviewDocumentRole } from '../delivery/preview'\n\nexport type { DeliveryDocument as PreviewCompiledDocument } from '../delivery/types'\n\nexport interface StudioPreviewStore extends DocumentPreviewStore {\n readonly manifest: () => RenderedDocumentManifest\n readonly patch: (document: PreviewCompiledDocument) => Promise<Readonly<Record<string, string>>>\n readonly routeChanged: () => void\n readonly subscribe: (listener: (manifest: RenderedDocumentManifest) => void) => () => void\n}\n\nexport function createStudioPreviewStore(\n route: () => RenderedDocumentManifest['route'],\n forms: () => ReadonlyArray<ComponentDefinition> = () => [],\n): StudioPreviewStore {\n const context = createComarkContext()\n const patches = shallowRef(new Map<string, PreviewCompiledDocument>())\n\n const registrations = new Map<\n symbol,\n { readonly document: PreviewCompiledDocument; readonly role: PreviewDocumentRole }\n >()\n\n const listeners = new Set<(manifest: RenderedDocumentManifest) => void>()\n\n const manifest = (): RenderedDocumentManifest => ({\n documents: renderedDocuments(registrations.values(), patches.value),\n route: route(),\n })\n\n const notify = (): void => {\n const value = manifest()\n\n for (const listener of listeners) listener(value)\n }\n\n return {\n manifest,\n // oxlint-disable-next-line effecttsgo/async-function -- The Promise contract turns synchronous projection failures into patch rejections for the preview controller.\n async patch(document) {\n const preview = createPreviewDocument(document.body)\n const patched = prepareFormDocument({ ...document, body: preview.body }, forms(), true)\n patches.value = new Map(patches.value).set(document.id, patched)\n const renderBody = toPreviewRenderDocument(patched.body)\n context.get(document.id, renderBody).set(renderBody)\n notify()\n\n return preview.fallbacks\n },\n register(document, role) {\n const registration = Symbol(document.id)\n registrations.set(registration, { document, role })\n notify()\n\n return () => {\n registrations.delete(registration)\n notify()\n }\n },\n resolve(document) {\n return patches.value.get(document.id) ?? document\n },\n routeChanged: notify,\n subscribe(listener) {\n listeners.add(listener)\n listener(manifest())\n\n return () => listeners.delete(listener)\n },\n }\n}\n\nfunction renderedDocuments(\n registrations: Iterable<{ readonly document: PreviewCompiledDocument; readonly role: PreviewDocumentRole }>,\n patches: ReadonlyMap<string, PreviewCompiledDocument>,\n): ReadonlyArray<RenderedCompiledDocument> {\n const priority = { embedded: 1, primary: 4, related: 2, summary: 3 } satisfies Record<PreviewDocumentRole, number>\n const documents = new Map<string, RenderedCompiledDocument>()\n\n for (const registration of registrations) {\n const document = patches.get(registration.document.id) ?? registration.document\n const current = documents.get(document.id)\n\n if (current && priority[current.role] >= priority[registration.role]) continue\n documents.set(document.id, {\n collection: CollectionName.make(document.collection),\n documentId: DocumentId.make(document.id),\n path: PublicPath.make(document.path),\n role: registration.role,\n title: document.title,\n })\n }\n\n return [...documents.values()].sort(\n (left, right) => priority[right.role] - priority[left.role] || left.title.localeCompare(right.title),\n )\n}\n\nfunction createPreviewDocument(body: PreviewCompiledDocument['body']) {\n const projection = projectPreviewDocument(body, {\n inlineHostAttribute: CMS_PREVIEW_INLINE_HOST_ATTRIBUTE,\n markerTag: CMS_PREVIEW_MARKER_TAG,\n nodeAttribute: CMS_PREVIEW_NODE_ATTRIBUTE,\n targetAttribute: CMS_PREVIEW_TARGET_ATTRIBUTE,\n wrapperAttribute: CMS_PREVIEW_WRAPPER_ATTRIBUTE,\n })\n\n if (Result.isFailure(projection)) throw projection.failure\n\n return projection.success\n}\n"],"mappings":"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"}