@phtngyn/cms 0.2.0-beta.2 → 0.2.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +6 -6
- package/src/Content.js +41 -18
- package/src/Content.js.map +1 -1
- package/src/{S3MediaObjects-D5Ns4FM6.js → S3MediaObjects-CR3p1WTg.js} +8 -2
- package/src/S3MediaObjects-CR3p1WTg.js.map +1 -0
- package/src/cli.js +2 -2
- package/src/connection-D5S9Iph-.js.map +1 -1
- package/src/{dev-lifecycle-CZ7iCzG9.js → dev-lifecycle-D3f9-upa.js} +2 -2
- package/src/dev-lifecycle-D3f9-upa.js.map +1 -0
- package/src/{installation-BNuoRDOz.js → installation-fZtNxtoW.js} +27 -12
- package/src/installation-fZtNxtoW.js.map +1 -0
- package/src/internal/configuration.js +1 -1
- package/src/internal/dev-lifecycle.js +1 -1
- package/src/internal/studio-app.js +8 -6
- package/src/internal/studio-app.js.map +1 -1
- package/src/runtime/delivery/document-page.js +1 -5
- package/src/runtime/delivery/document-page.js.map +1 -1
- package/src/runtime/delivery/plugin.js +34 -5
- package/src/runtime/delivery/plugin.js.map +1 -1
- package/src/runtime/delivery/useCmsDocumentSeo.d.ts +1 -0
- package/src/runtime/delivery/useCmsDocumentSeo.js +5 -4
- package/src/runtime/delivery/useCmsDocumentSeo.js.map +1 -1
- package/src/runtime/forms/useCmsForm.js +53 -30
- package/src/runtime/forms/useCmsForm.js.map +1 -1
- package/src/runtime/i18n/useCmsI18n.js.map +1 -1
- package/src/runtime/installation.js +1 -1
- package/src/runtime/plugin.js +1 -1
- package/src/runtime/preview/plugin.client.js +1 -1
- package/src/runtime/preview/plugin.client.js.map +1 -1
- package/src/runtime/preview/store.js +1 -1
- package/src/{store-B49J21NA.js → store-CCsjHaZm.js} +6 -6
- package/src/store-CCsjHaZm.js.map +1 -0
- package/studio/host.js +2562 -2556
- package/src/S3MediaObjects-D5Ns4FM6.js.map +0 -1
- package/src/dev-lifecycle-CZ7iCzG9.js.map +0 -1
- package/src/installation-BNuoRDOz.js.map +0 -1
- package/src/store-B49J21NA.js.map +0 -1
|
@@ -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-
|
|
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-
|
|
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
|
|
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"}
|
|
@@ -13,11 +13,7 @@ function resolveDocumentPage(request) {
|
|
|
13
13
|
}
|
|
14
14
|
return page ? h(DocumentRenderer_default, { value: page }) : void 0;
|
|
15
15
|
}
|
|
16
|
-
return request.ready.then(() =>
|
|
17
|
-
const failure = request.error() ?? (!request.page() ? request.notFound() : void 0);
|
|
18
|
-
if (failure && !request.renderError) throw failure;
|
|
19
|
-
return render;
|
|
20
|
-
});
|
|
16
|
+
return request.ready.then(() => render);
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-page.js","names":["DocumentRenderer"],"sources":["../../../../../src/runtime/delivery/document-page.ts"],"sourcesContent":["import type { VNode } from 'vue'\nimport type { DeliveryDocument } from './types'\nimport { h } from 'vue'\nimport DocumentRenderer from './DocumentRenderer'\n\nexport interface DocumentPageRequest {\n readonly error: () => Error | undefined\n readonly notFound: () => Error\n readonly page: () => DeliveryDocument | null\n readonly renderError?: ((error: Error) => () => VNode) | undefined\n readonly ready: Promise<unknown>\n}\n\nexport function resolveDocumentPage(request: DocumentPageRequest): Promise<() => VNode | undefined> {\n function render(): VNode | undefined {\n const error = request.error()\n const page = request.page()\n const failure = error ?? (!page ? request.notFound() : undefined)\n\n if (failure) {\n if (request.renderError) return request.renderError(failure)()\n throw failure\n }\n\n return page ? h(DocumentRenderer, { value: page }) : undefined\n }\n\n
|
|
1
|
+
{"version":3,"file":"document-page.js","names":["DocumentRenderer"],"sources":["../../../../../src/runtime/delivery/document-page.ts"],"sourcesContent":["import type { VNode } from 'vue'\nimport type { DeliveryDocument } from './types'\nimport { h } from 'vue'\nimport DocumentRenderer from './DocumentRenderer'\n\nexport interface DocumentPageRequest {\n readonly error: () => Error | undefined\n readonly notFound: () => Error\n readonly page: () => DeliveryDocument | null\n readonly renderError?: ((error: Error) => () => VNode) | undefined\n readonly ready: Promise<unknown>\n}\n\nexport function resolveDocumentPage(request: DocumentPageRequest): Promise<() => VNode | undefined> {\n function render(): VNode | undefined {\n const error = request.error()\n const page = request.page()\n const failure = error ?? (!page ? request.notFound() : undefined)\n\n if (failure) {\n if (request.renderError) return request.renderError(failure)()\n throw failure\n }\n\n return page ? h(DocumentRenderer, { value: page }) : undefined\n }\n\n // Always resolve setup with a renderer. Throwing delivery failures from render\n // lets Nuxt set the HTTP status without making Vue report a missing renderer.\n return request.ready.then(() => render)\n}\n"],"mappings":";;;;AAaA,SAAgB,oBAAoB,SAAgE;CAClG,SAAS,SAA4B;EACnC,MAAM,QAAQ,QAAQ,MAAM;EAC5B,MAAM,OAAO,QAAQ,KAAK;EAC1B,MAAM,UAAU,UAAU,CAAC,OAAO,QAAQ,SAAS,IAAI;EAEvD,IAAI,SAAS;GACX,IAAI,QAAQ,aAAa,OAAO,QAAQ,YAAY,OAAO,CAAC,CAAC;GAC7D,MAAM;EACR;EAEA,OAAO,OAAO,EAAEA,0BAAkB,EAAE,OAAO,KAAK,CAAC,IAAI;CACvD;CAIA,OAAO,QAAQ,MAAM,WAAW,MAAM;AACxC"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { ownsCmsLocale } from "../../internal/i18n.js";
|
|
2
2
|
import { n as readPreviewLocation, t as previewRedirect } from "../../location-CPX7oj0o.js";
|
|
3
3
|
import { usePreviewDelivery } from "./context.js";
|
|
4
|
-
import { installCmsDocumentSeoAdapters } from "./useCmsDocumentSeo.js";
|
|
5
4
|
import { localeFromPath } from "../i18n/translator.js";
|
|
5
|
+
import { installCmsDocumentSeoAdapters } from "./useCmsDocumentSeo.js";
|
|
6
6
|
import { computed, watch } from "vue";
|
|
7
|
-
import { addRouteMiddleware, defineNuxtPlugin, useHead, useRequestURL, useResponseHeader, useRoute,
|
|
7
|
+
import { addRouteMiddleware, defineNuxtPlugin, useHead, useRequestURL, useResponseHeader, useRoute, useState } from "nuxt/app";
|
|
8
8
|
import configuration from "#cms/i18n";
|
|
9
9
|
import { updateSiteConfig, useRobotsRule, useSchemaOrg } from "#imports";
|
|
10
10
|
|
|
11
11
|
//#region src/runtime/delivery/plugin.ts
|
|
12
12
|
var plugin_default = defineNuxtPlugin(() => {
|
|
13
13
|
const route = useRoute();
|
|
14
|
+
const url = useRequestURL();
|
|
14
15
|
const documentLocale = useState("cms-document-locale", () => void 0);
|
|
15
16
|
if (configuration.defaultLocale) {
|
|
16
17
|
const ownsRoute = computed(() => ownsCmsLocale(configuration, route.meta.cmsDocument === true));
|
|
@@ -38,13 +39,42 @@ var plugin_default = defineNuxtPlugin(() => {
|
|
|
38
39
|
const robots = useRobotsRule(input);
|
|
39
40
|
if (import.meta.server) watch(input, (value) => {
|
|
40
41
|
robots.value = value;
|
|
41
|
-
}, {
|
|
42
|
+
}, {
|
|
43
|
+
flush: "sync",
|
|
44
|
+
immediate: true
|
|
45
|
+
});
|
|
42
46
|
},
|
|
43
47
|
schema: (input) => {
|
|
44
48
|
useSchemaOrg(input);
|
|
45
49
|
},
|
|
46
50
|
seoMeta: (input) => {
|
|
47
|
-
|
|
51
|
+
useHead(() => {
|
|
52
|
+
const meta = [];
|
|
53
|
+
const addName = (name, content) => {
|
|
54
|
+
if (content) meta.push({
|
|
55
|
+
content,
|
|
56
|
+
name
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const addProperty = (property, content) => {
|
|
60
|
+
if (content) meta.push({
|
|
61
|
+
content,
|
|
62
|
+
property
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
addName("description", input.description());
|
|
66
|
+
addName("robots", input.robots());
|
|
67
|
+
addProperty("og:title", input.ogTitle());
|
|
68
|
+
addProperty("og:description", input.ogDescription());
|
|
69
|
+
addProperty("og:image", input.ogImage());
|
|
70
|
+
addProperty("og:image:alt", input.ogImageAlt());
|
|
71
|
+
addProperty("og:locale", input.ogLocale());
|
|
72
|
+
addProperty("og:url", input.ogUrl() ?? (input.robots()?.startsWith("noindex") ? url.href : void 0));
|
|
73
|
+
return {
|
|
74
|
+
meta,
|
|
75
|
+
title: input.title()
|
|
76
|
+
};
|
|
77
|
+
});
|
|
48
78
|
},
|
|
49
79
|
locale: (input) => {
|
|
50
80
|
watch(input, (value) => documentLocale.value = value, {
|
|
@@ -53,7 +83,6 @@ var plugin_default = defineNuxtPlugin(() => {
|
|
|
53
83
|
});
|
|
54
84
|
}
|
|
55
85
|
});
|
|
56
|
-
const url = useRequestURL();
|
|
57
86
|
const parameters = readPreviewLocation(url);
|
|
58
87
|
if (!usePreviewDelivery().value || !parameters) return;
|
|
59
88
|
if (import.meta.server) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../../src/runtime/delivery/plugin.ts"],"sourcesContent":["import configuration from '#cms/i18n'\nimport {\n addRouteMiddleware,\n defineNuxtPlugin,\n useHead,\n useRequestURL,\n useResponseHeader,\n useRoute,\n
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../../../../src/runtime/delivery/plugin.ts"],"sourcesContent":["import configuration from '#cms/i18n'\nimport {\n addRouteMiddleware,\n defineNuxtPlugin,\n useHead,\n useRequestURL,\n useResponseHeader,\n useRoute,\n useState,\n} from 'nuxt/app'\nimport { updateSiteConfig, useRobotsRule, useSchemaOrg } from '#imports'\nimport { previewRedirect, readPreviewLocation } from '@internal/protocol/Preview'\nimport { computed, watch } from 'vue'\nimport { usePreviewDelivery } from './context'\nimport { installCmsDocumentSeoAdapters } from './useCmsDocumentSeo'\nimport { ownsCmsLocale } from '../../internal/i18n'\nimport { localeFromPath } from '../i18n/translator'\n\nexport default defineNuxtPlugin(() => {\n const route = useRoute()\n const url = useRequestURL()\n const documentLocale = useState<string | undefined>('cms-document-locale', () => undefined)\n\n if (configuration.defaultLocale) {\n const ownsRoute = computed(() => ownsCmsLocale(configuration, route.meta.cmsDocument === true))\n const locale = computed(() => documentLocale.value ?? localeFromPath(configuration, route.path))\n\n watch(\n () => route.fullPath,\n () => {\n documentLocale.value = undefined\n },\n { flush: 'sync' },\n )\n useHead({ htmlAttrs: { lang: computed(() => (ownsRoute.value ? locale.value : undefined)) } })\n updateSiteConfig({\n _context: '@phtngyn/cms',\n _priority: 0,\n currentLocale: computed(() => (ownsRoute.value ? locale.value : undefined)),\n defaultLocale: computed(() => (ownsRoute.value ? configuration.defaultLocale : undefined)),\n locales: computed(() =>\n ownsRoute.value\n ? Object.entries(configuration.locales).map(([code, value]) => ({ code, name: value.name }))\n : undefined,\n ),\n })\n }\n\n installCmsDocumentSeoAdapters({\n head: (input) => {\n useHead(input)\n },\n robots: (input) => {\n const robots = useRobotsRule(input)\n\n if (import.meta.server) {\n watch(\n input,\n (value) => {\n robots.value = value\n },\n { flush: 'sync', immediate: true },\n )\n }\n },\n schema: (input) => {\n useSchemaOrg(input)\n },\n seoMeta: (input) => {\n useHead(() => {\n const meta: Array<{ content: string; name: string } | { content: string; property: string }> = []\n\n const addName = (name: string, content: string | undefined) => {\n if (content) meta.push({ content, name })\n }\n\n const addProperty = (property: string, content: string | undefined) => {\n if (content) meta.push({ content, property })\n }\n\n addName('description', input.description())\n addName('robots', input.robots())\n addProperty('og:title', input.ogTitle())\n addProperty('og:description', input.ogDescription())\n addProperty('og:image', input.ogImage())\n addProperty('og:image:alt', input.ogImageAlt())\n addProperty('og:locale', input.ogLocale())\n addProperty('og:url', input.ogUrl() ?? (input.robots()?.startsWith('noindex') ? url.href : undefined))\n\n return { meta, title: input.title() }\n })\n },\n locale: (input) => {\n watch(input, (value) => (documentLocale.value = value), { immediate: true, flush: 'sync' })\n },\n })\n\n const parameters = readPreviewLocation(url)\n const preview = usePreviewDelivery()\n\n if (!preview.value || !parameters) return\n\n if (import.meta.server) {\n useResponseHeader('cache-control').value = 'private, no-store'\n useResponseHeader('x-frame-options').value = 'SAMEORIGIN'\n }\n\n addRouteMiddleware(\n 'cms-preview',\n (to) => {\n return previewRedirect(new URL(to.fullPath, url.origin), parameters)\n },\n { global: true },\n )\n})\n"],"mappings":";;;;;;;;;;;AAkBA,qBAAe,uBAAuB;CACpC,MAAM,QAAQ,SAAS;CACvB,MAAM,MAAM,cAAc;CAC1B,MAAM,iBAAiB,SAA6B,6BAA6B,MAAS;CAE1F,IAAI,cAAc,eAAe;EAC/B,MAAM,YAAY,eAAe,cAAc,eAAe,MAAM,KAAK,gBAAgB,IAAI,CAAC;EAC9F,MAAM,SAAS,eAAe,eAAe,SAAS,eAAe,eAAe,MAAM,IAAI,CAAC;EAE/F,YACQ,MAAM,gBACN;GACJ,eAAe,QAAQ;EACzB,GACA,EAAE,OAAO,OAAO,CAClB;EACA,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAgB,UAAU,QAAQ,OAAO,QAAQ,MAAU,EAAE,EAAE,CAAC;EAC7F,iBAAiB;GACf,UAAU;GACV,WAAW;GACX,eAAe,eAAgB,UAAU,QAAQ,OAAO,QAAQ,MAAU;GAC1E,eAAe,eAAgB,UAAU,QAAQ,cAAc,gBAAgB,MAAU;GACzF,SAAS,eACP,UAAU,QACN,OAAO,QAAQ,cAAc,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY;IAAE;IAAM,MAAM,MAAM;GAAK,EAAE,IACzF,MACN;EACF,CAAC;CACH;CAEA,8BAA8B;EAC5B,OAAO,UAAU;GACf,QAAQ,KAAK;EACf;EACA,SAAS,UAAU;GACjB,MAAM,SAAS,cAAc,KAAK;GAElC,IAAI,YAAY,QACd,MACE,QACC,UAAU;IACT,OAAO,QAAQ;GACjB,GACA;IAAE,OAAO;IAAQ,WAAW;GAAK,CACnC;EAEJ;EACA,SAAS,UAAU;GACjB,aAAa,KAAK;EACpB;EACA,UAAU,UAAU;GAClB,cAAc;IACZ,MAAM,OAAyF,CAAC;IAEhG,MAAM,WAAW,MAAc,YAAgC;KAC7D,IAAI,SAAS,KAAK,KAAK;MAAE;MAAS;KAAK,CAAC;IAC1C;IAEA,MAAM,eAAe,UAAkB,YAAgC;KACrE,IAAI,SAAS,KAAK,KAAK;MAAE;MAAS;KAAS,CAAC;IAC9C;IAEA,QAAQ,eAAe,MAAM,YAAY,CAAC;IAC1C,QAAQ,UAAU,MAAM,OAAO,CAAC;IAChC,YAAY,YAAY,MAAM,QAAQ,CAAC;IACvC,YAAY,kBAAkB,MAAM,cAAc,CAAC;IACnD,YAAY,YAAY,MAAM,QAAQ,CAAC;IACvC,YAAY,gBAAgB,MAAM,WAAW,CAAC;IAC9C,YAAY,aAAa,MAAM,SAAS,CAAC;IACzC,YAAY,UAAU,MAAM,MAAM,MAAM,MAAM,OAAO,CAAC,EAAE,WAAW,SAAS,IAAI,IAAI,OAAO,OAAU;IAErG,OAAO;KAAE;KAAM,OAAO,MAAM,MAAM;IAAE;GACtC,CAAC;EACH;EACA,SAAS,UAAU;GACjB,MAAM,QAAQ,UAAW,eAAe,QAAQ,OAAQ;IAAE,WAAW;IAAM,OAAO;GAAO,CAAC;EAC5F;CACF,CAAC;CAED,MAAM,aAAa,oBAAoB,GAAG;CAG1C,IAAI,CAFY,mBAEL,CAAC,CAAC,SAAS,CAAC,YAAY;CAEnC,IAAI,YAAY,QAAQ;EACtB,kBAAkB,eAAe,CAAC,CAAC,QAAQ;EAC3C,kBAAkB,iBAAiB,CAAC,CAAC,QAAQ;CAC/C;CAEA,mBACE,gBACC,OAAO;EACN,OAAO,gBAAgB,IAAI,IAAI,GAAG,UAAU,IAAI,MAAM,GAAG,UAAU;CACrE,GACA,EAAE,QAAQ,KAAK,CACjB;AACF,CAAC"}
|
|
@@ -11,6 +11,7 @@ export interface CmsSeoMetadata {
|
|
|
11
11
|
readonly ogImage: ReactiveMetadataValue;
|
|
12
12
|
readonly ogImageAlt: ReactiveMetadataValue;
|
|
13
13
|
readonly ogLocale: ReactiveMetadataValue;
|
|
14
|
+
readonly ogUrl: ReactiveMetadataValue;
|
|
14
15
|
readonly robots: ReactiveMetadataValue;
|
|
15
16
|
}
|
|
16
17
|
interface CmsIdentitySchemaNode {
|
|
@@ -14,11 +14,12 @@ function useCmsDocumentSeo(document) {
|
|
|
14
14
|
title: () => seo.value?.title,
|
|
15
15
|
description: () => seo.value?.description,
|
|
16
16
|
ogTitle: () => seo.value?.socialTitle,
|
|
17
|
-
ogDescription: () => seo.value?.socialDescription,
|
|
17
|
+
ogDescription: () => seo.value?.socialDescription || (current.value?.draft ? seo.value?.title : void 0),
|
|
18
18
|
ogImage: () => seo.value?.socialImage?.url,
|
|
19
19
|
ogImageAlt: () => seo.value?.socialImage?.alt,
|
|
20
20
|
ogLocale: () => current.value?.locale,
|
|
21
|
-
|
|
21
|
+
ogUrl: () => seo.value?.canonicalUrl,
|
|
22
|
+
robots: () => seo.value && !current.value?.draft ? `${seo.value.index ? "index" : "noindex"}, ${seo.value.follow ? "follow" : "nofollow"}` : "noindex, nofollow"
|
|
22
23
|
});
|
|
23
24
|
adapters.locale(computed(() => current.value?.locale));
|
|
24
25
|
adapters.head(() => ({
|
|
@@ -27,8 +28,8 @@ function useCmsDocumentSeo(document) {
|
|
|
27
28
|
titleTemplate: null
|
|
28
29
|
}));
|
|
29
30
|
adapters.robots(computed(() => ({
|
|
30
|
-
follow: seo.value?.follow ??
|
|
31
|
-
index: seo.value?.index ??
|
|
31
|
+
follow: current.value?.draft ? false : seo.value?.follow ?? false,
|
|
32
|
+
index: current.value?.draft ? false : seo.value?.index ?? false
|
|
32
33
|
})));
|
|
33
34
|
adapters.schema(computed(() => seo.value ? schemaNodes(seo.value, current.value?.locale) : []));
|
|
34
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCmsDocumentSeo.js","names":[],"sources":["../../../../../src/runtime/delivery/useCmsDocumentSeo.ts"],"sourcesContent":["import type { ResolvedSeo, SiteIdentity } from '@internal/protocol/Seo'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\nimport type { DeliveryDocument } from './types'\nimport { computed, toValue } from 'vue'\n\ntype ReactiveMetadataValue = () => string | undefined\n\nexport interface CmsSeoMetadata {\n readonly title: ReactiveMetadataValue\n readonly description: ReactiveMetadataValue\n readonly ogTitle: ReactiveMetadataValue\n readonly ogDescription: ReactiveMetadataValue\n readonly ogImage: ReactiveMetadataValue\n readonly ogImageAlt: ReactiveMetadataValue\n readonly ogLocale: ReactiveMetadataValue\n readonly robots: ReactiveMetadataValue\n}\n\ninterface CmsIdentitySchemaNode {\n readonly '@type': SiteIdentity['type']\n readonly name: string\n readonly url?: string\n readonly image?: string\n readonly logo?: string\n readonly telephone?: string\n readonly address?: NonNullable<Extract<SiteIdentity, { readonly type: 'LocalBusiness' }>['address']>\n}\n\ninterface CmsPageSchemaNode {\n readonly '@type': 'Article' | 'WebPage'\n readonly name: string\n readonly description?: string\n readonly image?: string\n readonly url?: string\n readonly headline?: string\n readonly datePublished?: string\n readonly dateModified?: string\n readonly inLanguage?: string\n}\n\nexport type CmsSeoSchemaNode = CmsIdentitySchemaNode | CmsPageSchemaNode\n\ntype EffectiveSeo = Omit<ResolvedSeo, 'canonicalUrl'> & {\n readonly canonicalUrl?: ResolvedSeo['canonicalUrl']\n}\n\nexport interface CmsDocumentSeoAdapters {\n readonly head: (\n input: () => {\n htmlAttrs?: { lang: string }\n link: Array<{ href: string; rel: 'canonical' } | { href: string; hreflang: string; rel: 'alternate' }>\n titleTemplate: null\n },\n ) => void\n readonly robots: (input: ComputedRef<{ readonly follow: boolean; readonly index: boolean }>) => void\n readonly schema: (input: ComputedRef<ReadonlyArray<CmsSeoSchemaNode>>) => void\n readonly seoMeta: (input: CmsSeoMetadata) => void\n readonly locale: (input: ComputedRef<string | undefined>) => void\n}\n\nlet installedAdapters: CmsDocumentSeoAdapters | undefined\n\n/** Installs Nuxt's composables once before document page setup. */\nexport function installCmsDocumentSeoAdapters(adapters: CmsDocumentSeoAdapters): void {\n installedAdapters = adapters\n}\n\nexport function useCmsDocumentSeo(document: MaybeRefOrGetter<DeliveryDocument | null>): void {\n const adapters = installedCmsDocumentSeoAdapters()\n const current = computed(() => toValue(document))\n const seo = computed(() => resolvedSeo(current.value))\n\n adapters.seoMeta({\n title: () => seo.value?.title,\n description: () => seo.value?.description,\n ogTitle: () => seo.value?.socialTitle,\n ogDescription: () => seo.value?.socialDescription,\n ogImage: () => seo.value?.socialImage?.url,\n ogImageAlt: () => seo.value?.socialImage?.alt,\n ogLocale: () => current.value?.locale,\n robots: () =>\n seo.value ? `${seo.value.index ? 'index' : 'noindex'}, ${seo.value.follow ? 'follow' : 'nofollow'}
|
|
1
|
+
{"version":3,"file":"useCmsDocumentSeo.js","names":[],"sources":["../../../../../src/runtime/delivery/useCmsDocumentSeo.ts"],"sourcesContent":["import type { ResolvedSeo, SiteIdentity } from '@internal/protocol/Seo'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\nimport type { DeliveryDocument } from './types'\nimport { computed, toValue } from 'vue'\n\ntype ReactiveMetadataValue = () => string | undefined\n\nexport interface CmsSeoMetadata {\n readonly title: ReactiveMetadataValue\n readonly description: ReactiveMetadataValue\n readonly ogTitle: ReactiveMetadataValue\n readonly ogDescription: ReactiveMetadataValue\n readonly ogImage: ReactiveMetadataValue\n readonly ogImageAlt: ReactiveMetadataValue\n readonly ogLocale: ReactiveMetadataValue\n readonly ogUrl: ReactiveMetadataValue\n readonly robots: ReactiveMetadataValue\n}\n\ninterface CmsIdentitySchemaNode {\n readonly '@type': SiteIdentity['type']\n readonly name: string\n readonly url?: string\n readonly image?: string\n readonly logo?: string\n readonly telephone?: string\n readonly address?: NonNullable<Extract<SiteIdentity, { readonly type: 'LocalBusiness' }>['address']>\n}\n\ninterface CmsPageSchemaNode {\n readonly '@type': 'Article' | 'WebPage'\n readonly name: string\n readonly description?: string\n readonly image?: string\n readonly url?: string\n readonly headline?: string\n readonly datePublished?: string\n readonly dateModified?: string\n readonly inLanguage?: string\n}\n\nexport type CmsSeoSchemaNode = CmsIdentitySchemaNode | CmsPageSchemaNode\n\ntype EffectiveSeo = Omit<ResolvedSeo, 'canonicalUrl'> & {\n readonly canonicalUrl?: ResolvedSeo['canonicalUrl']\n}\n\nexport interface CmsDocumentSeoAdapters {\n readonly head: (\n input: () => {\n htmlAttrs?: { lang: string }\n link: Array<{ href: string; rel: 'canonical' } | { href: string; hreflang: string; rel: 'alternate' }>\n titleTemplate: null\n },\n ) => void\n readonly robots: (input: ComputedRef<{ readonly follow: boolean; readonly index: boolean }>) => void\n readonly schema: (input: ComputedRef<ReadonlyArray<CmsSeoSchemaNode>>) => void\n readonly seoMeta: (input: CmsSeoMetadata) => void\n readonly locale: (input: ComputedRef<string | undefined>) => void\n}\n\nlet installedAdapters: CmsDocumentSeoAdapters | undefined\n\n/** Installs Nuxt's composables once before document page setup. */\nexport function installCmsDocumentSeoAdapters(adapters: CmsDocumentSeoAdapters): void {\n installedAdapters = adapters\n}\n\nexport function useCmsDocumentSeo(document: MaybeRefOrGetter<DeliveryDocument | null>): void {\n const adapters = installedCmsDocumentSeoAdapters()\n const current = computed(() => toValue(document))\n const seo = computed(() => resolvedSeo(current.value))\n\n adapters.seoMeta({\n title: () => seo.value?.title,\n description: () => seo.value?.description,\n ogTitle: () => seo.value?.socialTitle,\n ogDescription: () => seo.value?.socialDescription || (current.value?.draft ? seo.value?.title : undefined),\n ogImage: () => seo.value?.socialImage?.url,\n ogImageAlt: () => seo.value?.socialImage?.alt,\n ogLocale: () => current.value?.locale,\n ogUrl: () => seo.value?.canonicalUrl,\n robots: () =>\n seo.value && !current.value?.draft\n ? `${seo.value.index ? 'index' : 'noindex'}, ${seo.value.follow ? 'follow' : 'nofollow'}`\n : 'noindex, nofollow',\n })\n\n adapters.locale(computed(() => current.value?.locale))\n adapters.head(() => ({\n ...(current.value?.locale ? { htmlAttrs: { lang: current.value.locale } } : undefined),\n link: links(current.value, seo.value?.canonicalUrl),\n titleTemplate: null,\n }))\n adapters.robots(\n computed(() => ({\n follow: current.value?.draft ? false : (seo.value?.follow ?? false),\n index: current.value?.draft ? false : (seo.value?.index ?? false),\n })),\n )\n adapters.schema(computed(() => (seo.value ? schemaNodes(seo.value, current.value?.locale) : [])))\n}\n\nfunction links(document: DeliveryDocument | null, canonicalUrl?: string) {\n const canonical = canonicalUrl ? [{ href: canonicalUrl, rel: 'canonical' as const }] : []\n\n if (!canonicalUrl || !document?.alternatives) return canonical\n\n return [\n ...canonical,\n ...document.alternatives.map((alternative) => ({\n href: new URL(alternative.path, canonicalUrl).href,\n hreflang: alternative.locale,\n rel: 'alternate' as const,\n })),\n ]\n}\n\nfunction installedCmsDocumentSeoAdapters(): CmsDocumentSeoAdapters {\n if (!installedAdapters) throw new Error('The CMS document SEO adapter has not been installed.')\n\n return installedAdapters\n}\n\nfunction resolvedSeo(document: DeliveryDocument | null): EffectiveSeo | undefined {\n if (!document) return undefined\n\n if (document.resolvedSeo) return document.resolvedSeo\n\n return {\n description: document.description,\n follow: true,\n index: true,\n socialDescription: document.description,\n socialTitle: document.title,\n structuredData: { type: 'WebPage' },\n title: document.title,\n }\n}\n\nfunction schemaNodes(seo: EffectiveSeo, locale?: string): ReadonlyArray<CmsSeoSchemaNode> {\n const identity = seo.identity ? [identityNode(seo.identity)] : []\n\n if (!seo.structuredData) return identity\n\n const common = {\n name: seo.title,\n ...(seo.description ? { description: seo.description } : undefined),\n ...(seo.socialImage ? { image: seo.socialImage.url } : undefined),\n ...(seo.canonicalUrl ? { url: seo.canonicalUrl } : undefined),\n ...(locale ? { inLanguage: locale } : undefined),\n }\n\n if (seo.structuredData.type === 'Article') {\n return [\n ...identity,\n {\n '@type': 'Article',\n ...common,\n headline: seo.structuredData.headline ?? seo.title,\n ...(seo.structuredData.datePublished ? { datePublished: seo.structuredData.datePublished } : undefined),\n ...(seo.structuredData.dateModified ? { dateModified: seo.structuredData.dateModified } : undefined),\n },\n ]\n }\n\n return [...identity, { '@type': 'WebPage', ...common }]\n}\n\nfunction identityNode(identity: SiteIdentity): CmsIdentitySchemaNode {\n const common = {\n '@type': identity.type,\n name: identity.name,\n ...(identity.url ? { url: identity.url } : undefined),\n }\n\n switch (identity.type) {\n case 'Person':\n return { ...common, ...(identity.image ? { image: identity.image.url } : undefined) }\n case 'Organization':\n return { ...common, ...(identity.logo ? { logo: identity.logo.url } : undefined) }\n case 'LocalBusiness':\n return {\n ...common,\n ...(identity.address ? { address: identity.address } : undefined),\n ...(identity.image ? { image: identity.image.url } : undefined),\n ...(identity.logo ? { logo: identity.logo.url } : undefined),\n ...(identity.telephone ? { telephone: identity.telephone } : undefined),\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -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,
|
|
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 =
|
|
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 =
|
|
21
|
-
const isSuccess =
|
|
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 =
|
|
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
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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-
|
|
1
|
+
import { n as scopedDatabaseUrl, r as toS3MediaObjectStoreOptions, t as installCms } from "../installation-fZtNxtoW.js";
|
|
2
2
|
|
|
3
3
|
export { installCms, scopedDatabaseUrl, toS3MediaObjectStoreOptions };
|
package/src/runtime/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveCmsInstallation } from "../internal/configuration.js";
|
|
2
|
-
import { t as installCms } from "../installation-
|
|
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-
|
|
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;
|
|
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-
|
|
2
|
+
import { t as createStudioPreviewStore } from "../../store-CCsjHaZm.js";
|
|
3
3
|
|
|
4
4
|
export { createStudioPreviewStore, documentPreviewStoreKey };
|