@cmssy/core 11.4.2 → 11.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{csp-CRixC_wl.d.cts → csp-0D9pcuDp.d.cts} +1 -1
- package/dist/{csp-DpG1NcSd.d.ts → csp-CpiqfM03.d.ts} +1 -1
- package/dist/index.cjs +52 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +52 -1
- package/dist/internal/locale.cjs +13 -3
- package/dist/internal/locale.d.cts +2 -19
- package/dist/internal/locale.d.ts +2 -19
- package/dist/internal/locale.js +13 -3
- package/dist/internal.cjs +13 -3
- package/dist/internal.d.cts +5 -5
- package/dist/internal.d.ts +5 -5
- package/dist/internal.js +13 -3
- package/dist/{locale-Ox_qVlNy.d.ts → locale-Clouq4s4.d.ts} +18 -2
- package/dist/{locale-C7sA46qu.d.cts → locale-XMylB_kz.d.cts} +18 -2
- package/dist/testing.cjs +163 -14
- package/dist/testing.d.cts +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +163 -14
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CmssyClientConfig, BlockRect, BlockSchema, BlockMeta } from '@cmssy/types';
|
|
2
|
-
import { G as GraphqlRequestOptions } from './locale-
|
|
2
|
+
import { G as GraphqlRequestOptions } from './locale-XMylB_kz.cjs';
|
|
3
3
|
|
|
4
4
|
declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
|
|
5
5
|
declare function isDevelopment(): boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CmssyClientConfig, BlockRect, BlockSchema, BlockMeta } from '@cmssy/types';
|
|
2
|
-
import { G as GraphqlRequestOptions } from './locale-
|
|
2
|
+
import { G as GraphqlRequestOptions } from './locale-Clouq4s4.js';
|
|
3
3
|
|
|
4
4
|
declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
|
|
5
5
|
declare function isDevelopment(): boolean;
|
package/dist/index.cjs
CHANGED
|
@@ -540,6 +540,57 @@ function localizeHref(href, locale) {
|
|
|
540
540
|
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
+
// src/data/site-locales.ts
|
|
544
|
+
var TTL_MS = 6e4;
|
|
545
|
+
var FAILURE_TTL_MS = 5e3;
|
|
546
|
+
var MAX_ENTRIES = 64;
|
|
547
|
+
var cache = /* @__PURE__ */ new Map();
|
|
548
|
+
function localesFromSiteConfig(siteConfig) {
|
|
549
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
550
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
551
|
+
return {
|
|
552
|
+
defaultLocale,
|
|
553
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
async function loadSiteLocales(config, options) {
|
|
557
|
+
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
558
|
+
const cached = cache.get(key);
|
|
559
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
560
|
+
cache.delete(key);
|
|
561
|
+
let value;
|
|
562
|
+
try {
|
|
563
|
+
const data = await graphqlRequest(
|
|
564
|
+
config,
|
|
565
|
+
SITE_CONFIG_QUERY,
|
|
566
|
+
{ workspaceSlug: config.workspaceSlug },
|
|
567
|
+
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
568
|
+
"site config"
|
|
569
|
+
);
|
|
570
|
+
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
571
|
+
} catch {
|
|
572
|
+
value = null;
|
|
573
|
+
}
|
|
574
|
+
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
575
|
+
cache.set(key, {
|
|
576
|
+
value,
|
|
577
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
578
|
+
});
|
|
579
|
+
return value;
|
|
580
|
+
}
|
|
581
|
+
async function resolveCmssyLocale(config, path, options) {
|
|
582
|
+
const siteLocales = await loadSiteLocales(config, options);
|
|
583
|
+
if (!siteLocales) return void 0;
|
|
584
|
+
return splitLocaleFromPath(path, siteLocales).locale;
|
|
585
|
+
}
|
|
586
|
+
function splitLocaleFromPath(path, siteLocales) {
|
|
587
|
+
const first = path?.[0];
|
|
588
|
+
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
589
|
+
return { locale: first, path: path.slice(1) };
|
|
590
|
+
}
|
|
591
|
+
return { locale: siteLocales.defaultLocale, path };
|
|
592
|
+
}
|
|
593
|
+
|
|
543
594
|
// src/locale.ts
|
|
544
595
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
545
596
|
|
|
@@ -823,5 +874,6 @@ exports.localizeHref = localizeHref;
|
|
|
823
874
|
exports.normalizeOrigin = normalizeOrigin;
|
|
824
875
|
exports.parseEditorMessage = parseEditorMessage;
|
|
825
876
|
exports.postToEditor = postToEditor;
|
|
877
|
+
exports.resolveCmssyLocale = resolveCmssyLocale;
|
|
826
878
|
exports.resolveEditorOrigin = resolveEditorOrigin;
|
|
827
879
|
exports.verifyCmssyWebhook = verifyCmssyWebhook;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as CmssyClient, b as CmssyConfig, c as CmssyCspOptions, d as CmssyEnvConfig, e as CmssyTypedDocument, D as DEFAULT_CMSSY_EDITOR_ORIGINS, E as EditorToAppMessage, P as PROTOCOL_VERSION, f as ParentReadyMessage, g as PatchMessage, h as PostTarget, Q as QueryScopedOptions, R as ReadyMessage, S as SelectMessage, i as applyCmssyCsp, j as createCmssyClient, k as defineCmssyConfig, l as isProtocolCompatible, n as normalizeOrigin, p as parseEditorMessage, m as postToEditor, r as resolveEditorOrigin } from './csp-
|
|
2
|
-
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref } from './locale-
|
|
1
|
+
export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as CmssyClient, b as CmssyConfig, c as CmssyCspOptions, d as CmssyEnvConfig, e as CmssyTypedDocument, D as DEFAULT_CMSSY_EDITOR_ORIGINS, E as EditorToAppMessage, P as PROTOCOL_VERSION, f as ParentReadyMessage, g as PatchMessage, h as PostTarget, Q as QueryScopedOptions, R as ReadyMessage, S as SelectMessage, i as applyCmssyCsp, j as createCmssyClient, k as defineCmssyConfig, l as isProtocolCompatible, n as normalizeOrigin, p as parseEditorMessage, m as postToEditor, r as resolveEditorOrigin } from './csp-0D9pcuDp.cjs';
|
|
2
|
+
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref, r as resolveCmssyLocale } from './locale-XMylB_kz.cjs';
|
|
3
3
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, F as FetchLike, a as FetchLikeResponse, b as FetchPageOptions, R as RetryPolicy } from './content-client-CJQ-nafm.cjs';
|
|
4
4
|
import * as _cmssy_types from '@cmssy/types';
|
|
5
5
|
import { FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as CmssyClient, b as CmssyConfig, c as CmssyCspOptions, d as CmssyEnvConfig, e as CmssyTypedDocument, D as DEFAULT_CMSSY_EDITOR_ORIGINS, E as EditorToAppMessage, P as PROTOCOL_VERSION, f as ParentReadyMessage, g as PatchMessage, h as PostTarget, Q as QueryScopedOptions, R as ReadyMessage, S as SelectMessage, i as applyCmssyCsp, j as createCmssyClient, k as defineCmssyConfig, l as isProtocolCompatible, n as normalizeOrigin, p as parseEditorMessage, m as postToEditor, r as resolveEditorOrigin } from './csp-
|
|
2
|
-
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref } from './locale-
|
|
1
|
+
export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as CmssyClient, b as CmssyConfig, c as CmssyCspOptions, d as CmssyEnvConfig, e as CmssyTypedDocument, D as DEFAULT_CMSSY_EDITOR_ORIGINS, E as EditorToAppMessage, P as PROTOCOL_VERSION, f as ParentReadyMessage, g as PatchMessage, h as PostTarget, Q as QueryScopedOptions, R as ReadyMessage, S as SelectMessage, i as applyCmssyCsp, j as createCmssyClient, k as defineCmssyConfig, l as isProtocolCompatible, n as normalizeOrigin, p as parseEditorMessage, m as postToEditor, r as resolveEditorOrigin } from './csp-CpiqfM03.js';
|
|
2
|
+
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref, r as resolveCmssyLocale } from './locale-Clouq4s4.js';
|
|
3
3
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, F as FetchLike, a as FetchLikeResponse, b as FetchPageOptions, R as RetryPolicy } from './content-client-CJQ-nafm.js';
|
|
4
4
|
import * as _cmssy_types from '@cmssy/types';
|
|
5
5
|
import { FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
package/dist/index.js
CHANGED
|
@@ -538,6 +538,57 @@ function localizeHref(href, locale) {
|
|
|
538
538
|
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
// src/data/site-locales.ts
|
|
542
|
+
var TTL_MS = 6e4;
|
|
543
|
+
var FAILURE_TTL_MS = 5e3;
|
|
544
|
+
var MAX_ENTRIES = 64;
|
|
545
|
+
var cache = /* @__PURE__ */ new Map();
|
|
546
|
+
function localesFromSiteConfig(siteConfig) {
|
|
547
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
548
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
549
|
+
return {
|
|
550
|
+
defaultLocale,
|
|
551
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
async function loadSiteLocales(config, options) {
|
|
555
|
+
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
556
|
+
const cached = cache.get(key);
|
|
557
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
558
|
+
cache.delete(key);
|
|
559
|
+
let value;
|
|
560
|
+
try {
|
|
561
|
+
const data = await graphqlRequest(
|
|
562
|
+
config,
|
|
563
|
+
SITE_CONFIG_QUERY,
|
|
564
|
+
{ workspaceSlug: config.workspaceSlug },
|
|
565
|
+
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
566
|
+
"site config"
|
|
567
|
+
);
|
|
568
|
+
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
569
|
+
} catch {
|
|
570
|
+
value = null;
|
|
571
|
+
}
|
|
572
|
+
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
573
|
+
cache.set(key, {
|
|
574
|
+
value,
|
|
575
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
576
|
+
});
|
|
577
|
+
return value;
|
|
578
|
+
}
|
|
579
|
+
async function resolveCmssyLocale(config, path, options) {
|
|
580
|
+
const siteLocales = await loadSiteLocales(config, options);
|
|
581
|
+
if (!siteLocales) return void 0;
|
|
582
|
+
return splitLocaleFromPath(path, siteLocales).locale;
|
|
583
|
+
}
|
|
584
|
+
function splitLocaleFromPath(path, siteLocales) {
|
|
585
|
+
const first = path?.[0];
|
|
586
|
+
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
587
|
+
return { locale: first, path: path.slice(1) };
|
|
588
|
+
}
|
|
589
|
+
return { locale: siteLocales.defaultLocale, path };
|
|
590
|
+
}
|
|
591
|
+
|
|
541
592
|
// src/locale.ts
|
|
542
593
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
543
594
|
|
|
@@ -793,4 +844,4 @@ async function verifyCmssyWebhook(options) {
|
|
|
793
844
|
return parsed;
|
|
794
845
|
}
|
|
795
846
|
|
|
796
|
-
export { CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CmssyRequestError, CmssyWebhookError, DEFAULT_CMSSY_API_URL, DEFAULT_CMSSY_EDITOR_ORIGINS, PROTOCOL_VERSION, applyCmssyCsp, createCmssyClient, defineCmssyConfig, fields, graphqlRequest, isProtocolCompatible, isVerifiedEditUrl, localizeHref, normalizeOrigin, parseEditorMessage, postToEditor, resolveEditorOrigin, verifyCmssyWebhook };
|
|
847
|
+
export { CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CmssyRequestError, CmssyWebhookError, DEFAULT_CMSSY_API_URL, DEFAULT_CMSSY_EDITOR_ORIGINS, PROTOCOL_VERSION, applyCmssyCsp, createCmssyClient, defineCmssyConfig, fields, graphqlRequest, isProtocolCompatible, isVerifiedEditUrl, localizeHref, normalizeOrigin, parseEditorMessage, postToEditor, resolveCmssyLocale, resolveEditorOrigin, verifyCmssyWebhook };
|
package/dist/internal/locale.cjs
CHANGED
|
@@ -166,6 +166,7 @@ var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
|
166
166
|
|
|
167
167
|
// src/data/site-locales.ts
|
|
168
168
|
var TTL_MS = 6e4;
|
|
169
|
+
var FAILURE_TTL_MS = 5e3;
|
|
169
170
|
var MAX_ENTRIES = 64;
|
|
170
171
|
var cache = /* @__PURE__ */ new Map();
|
|
171
172
|
function localesFromSiteConfig(siteConfig) {
|
|
@@ -176,7 +177,7 @@ function localesFromSiteConfig(siteConfig) {
|
|
|
176
177
|
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
177
178
|
};
|
|
178
179
|
}
|
|
179
|
-
async function
|
|
180
|
+
async function loadSiteLocales(config, options) {
|
|
180
181
|
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
181
182
|
const cached = cache.get(key);
|
|
182
183
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
@@ -192,12 +193,21 @@ async function resolveSiteLocales(config, options) {
|
|
|
192
193
|
);
|
|
193
194
|
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
194
195
|
} catch {
|
|
195
|
-
value =
|
|
196
|
+
value = null;
|
|
196
197
|
}
|
|
197
198
|
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
198
|
-
cache.set(key, {
|
|
199
|
+
cache.set(key, {
|
|
200
|
+
value,
|
|
201
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
202
|
+
});
|
|
199
203
|
return value;
|
|
200
204
|
}
|
|
205
|
+
async function resolveSiteLocales(config, options) {
|
|
206
|
+
return await loadSiteLocales(config, options) ?? {
|
|
207
|
+
defaultLocale: "en",
|
|
208
|
+
locales: ["en"]
|
|
209
|
+
};
|
|
210
|
+
}
|
|
201
211
|
function splitLocaleFromPath(path, siteLocales) {
|
|
202
212
|
const first = path?.[0];
|
|
203
213
|
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
@@ -1,28 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, l as localizeHref, d as localizeHtmlLinks, s as splitCmssyLocale } from '../locale-C7sA46qu.cjs';
|
|
3
|
-
import { CmssySiteLocales, CmssyClientConfig } from '@cmssy/types';
|
|
1
|
+
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, d as localesFromSiteConfig, l as localizeHref, e as localizeHtmlLinks, f as resolveSiteLocales, s as splitCmssyLocale, h as splitLocaleFromPath } from '../locale-XMylB_kz.cjs';
|
|
4
2
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
5
3
|
import '../content-client-CJQ-nafm.cjs';
|
|
6
4
|
|
|
7
|
-
/**
|
|
8
|
-
* Maps a workspace site config to its locale set. The single place that
|
|
9
|
-
* decides the default and enabled languages - the router and the SEO helpers
|
|
10
|
-
* must agree, so both go through here.
|
|
11
|
-
*/
|
|
12
|
-
declare function localesFromSiteConfig(siteConfig: {
|
|
13
|
-
defaultLanguage?: string | null;
|
|
14
|
-
enabledLanguages?: string[];
|
|
15
|
-
} | null): CmssySiteLocales;
|
|
16
|
-
declare function resolveSiteLocales(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<CmssySiteLocales>;
|
|
17
|
-
declare function splitLocaleFromPath(path: string[] | undefined, siteLocales: CmssySiteLocales): {
|
|
18
|
-
locale: string;
|
|
19
|
-
path: string[] | undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
5
|
/**
|
|
23
6
|
* Maps a page slug to its path for a locale: the default locale gets no
|
|
24
7
|
* prefix, others get `/${locale}`. The homepage ("/") stays "/" (or "/${locale}").
|
|
25
8
|
*/
|
|
26
9
|
declare function localizedPath(slug: string, locale: string, defaultLocale: string): string;
|
|
27
10
|
|
|
28
|
-
export {
|
|
11
|
+
export { localizedPath };
|
|
@@ -1,28 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, l as localizeHref, d as localizeHtmlLinks, s as splitCmssyLocale } from '../locale-Ox_qVlNy.js';
|
|
3
|
-
import { CmssySiteLocales, CmssyClientConfig } from '@cmssy/types';
|
|
1
|
+
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, d as localesFromSiteConfig, l as localizeHref, e as localizeHtmlLinks, f as resolveSiteLocales, s as splitCmssyLocale, h as splitLocaleFromPath } from '../locale-Clouq4s4.js';
|
|
4
2
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
5
3
|
import '../content-client-CJQ-nafm.js';
|
|
6
4
|
|
|
7
|
-
/**
|
|
8
|
-
* Maps a workspace site config to its locale set. The single place that
|
|
9
|
-
* decides the default and enabled languages - the router and the SEO helpers
|
|
10
|
-
* must agree, so both go through here.
|
|
11
|
-
*/
|
|
12
|
-
declare function localesFromSiteConfig(siteConfig: {
|
|
13
|
-
defaultLanguage?: string | null;
|
|
14
|
-
enabledLanguages?: string[];
|
|
15
|
-
} | null): CmssySiteLocales;
|
|
16
|
-
declare function resolveSiteLocales(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<CmssySiteLocales>;
|
|
17
|
-
declare function splitLocaleFromPath(path: string[] | undefined, siteLocales: CmssySiteLocales): {
|
|
18
|
-
locale: string;
|
|
19
|
-
path: string[] | undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
5
|
/**
|
|
23
6
|
* Maps a page slug to its path for a locale: the default locale gets no
|
|
24
7
|
* prefix, others get `/${locale}`. The homepage ("/") stays "/" (or "/${locale}").
|
|
25
8
|
*/
|
|
26
9
|
declare function localizedPath(slug: string, locale: string, defaultLocale: string): string;
|
|
27
10
|
|
|
28
|
-
export {
|
|
11
|
+
export { localizedPath };
|
package/dist/internal/locale.js
CHANGED
|
@@ -164,6 +164,7 @@ var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
|
164
164
|
|
|
165
165
|
// src/data/site-locales.ts
|
|
166
166
|
var TTL_MS = 6e4;
|
|
167
|
+
var FAILURE_TTL_MS = 5e3;
|
|
167
168
|
var MAX_ENTRIES = 64;
|
|
168
169
|
var cache = /* @__PURE__ */ new Map();
|
|
169
170
|
function localesFromSiteConfig(siteConfig) {
|
|
@@ -174,7 +175,7 @@ function localesFromSiteConfig(siteConfig) {
|
|
|
174
175
|
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
|
-
async function
|
|
178
|
+
async function loadSiteLocales(config, options) {
|
|
178
179
|
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
179
180
|
const cached = cache.get(key);
|
|
180
181
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
@@ -190,12 +191,21 @@ async function resolveSiteLocales(config, options) {
|
|
|
190
191
|
);
|
|
191
192
|
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
192
193
|
} catch {
|
|
193
|
-
value =
|
|
194
|
+
value = null;
|
|
194
195
|
}
|
|
195
196
|
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
196
|
-
cache.set(key, {
|
|
197
|
+
cache.set(key, {
|
|
198
|
+
value,
|
|
199
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
200
|
+
});
|
|
197
201
|
return value;
|
|
198
202
|
}
|
|
203
|
+
async function resolveSiteLocales(config, options) {
|
|
204
|
+
return await loadSiteLocales(config, options) ?? {
|
|
205
|
+
defaultLocale: "en",
|
|
206
|
+
locales: ["en"]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
199
209
|
function splitLocaleFromPath(path, siteLocales) {
|
|
200
210
|
const first = path?.[0];
|
|
201
211
|
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
package/dist/internal.cjs
CHANGED
|
@@ -713,6 +713,7 @@ async function resolveForms(config, blocks, locale, defaultLocale, options) {
|
|
|
713
713
|
|
|
714
714
|
// src/data/site-locales.ts
|
|
715
715
|
var TTL_MS = 6e4;
|
|
716
|
+
var FAILURE_TTL_MS = 5e3;
|
|
716
717
|
var MAX_ENTRIES = 64;
|
|
717
718
|
var cache = /* @__PURE__ */ new Map();
|
|
718
719
|
function localesFromSiteConfig(siteConfig) {
|
|
@@ -723,7 +724,7 @@ function localesFromSiteConfig(siteConfig) {
|
|
|
723
724
|
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
724
725
|
};
|
|
725
726
|
}
|
|
726
|
-
async function
|
|
727
|
+
async function loadSiteLocales(config, options) {
|
|
727
728
|
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
728
729
|
const cached = cache.get(key);
|
|
729
730
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
@@ -739,12 +740,21 @@ async function resolveSiteLocales(config, options) {
|
|
|
739
740
|
);
|
|
740
741
|
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
741
742
|
} catch {
|
|
742
|
-
value =
|
|
743
|
+
value = null;
|
|
743
744
|
}
|
|
744
745
|
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
745
|
-
cache.set(key, {
|
|
746
|
+
cache.set(key, {
|
|
747
|
+
value,
|
|
748
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
749
|
+
});
|
|
746
750
|
return value;
|
|
747
751
|
}
|
|
752
|
+
async function resolveSiteLocales(config, options) {
|
|
753
|
+
return await loadSiteLocales(config, options) ?? {
|
|
754
|
+
defaultLocale: "en",
|
|
755
|
+
locales: ["en"]
|
|
756
|
+
};
|
|
757
|
+
}
|
|
748
758
|
function splitLocaleFromPath(path, siteLocales) {
|
|
749
759
|
const first = path?.[0];
|
|
750
760
|
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
package/dist/internal.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-CJQ-nafm.cjs';
|
|
2
2
|
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig, RawBlock, FieldDefinition } from '@cmssy/types';
|
|
3
3
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
4
|
-
import { G as GraphqlRequestOptions } from './locale-
|
|
5
|
-
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, l as localizeHref,
|
|
6
|
-
import { Q as QueryScopedOptions } from './csp-
|
|
7
|
-
export { o as cmssyCspHeaders, q as isDevelopment, s as resolveInitialTarget, t as toCspOrigin } from './csp-
|
|
8
|
-
export {
|
|
4
|
+
import { G as GraphqlRequestOptions } from './locale-XMylB_kz.cjs';
|
|
5
|
+
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, d as localesFromSiteConfig, l as localizeHref, e as localizeHtmlLinks, f as resolveSiteLocales, s as splitCmssyLocale, h as splitLocaleFromPath } from './locale-XMylB_kz.cjs';
|
|
6
|
+
import { Q as QueryScopedOptions } from './csp-0D9pcuDp.cjs';
|
|
7
|
+
export { o as cmssyCspHeaders, q as isDevelopment, s as resolveInitialTarget, t as toCspOrigin } from './csp-0D9pcuDp.cjs';
|
|
8
|
+
export { localizedPath } from './internal/locale.cjs';
|
|
9
9
|
|
|
10
10
|
declare const SITE_CONFIG_QUERY = "query PublicSiteConfig($workspaceSlug: String!) {\n public {\n siteConfig(workspaceSlug: $workspaceSlug) {\n id\n workspaceId\n siteName\n defaultLanguage\n enabledLanguages\n enabledFeatures\n notFoundPageId\n previewUrl\n branding {\n brandName\n logoUrl\n logoDarkUrl\n faviconUrl\n ogImageUrl\n }\n }\n }\n}";
|
|
11
11
|
declare const MODEL_DEFINITIONS_QUERY = "query PublicModelDefinitions($workspaceId: String!) {\n public {\n model {\n definitions(workspaceId: $workspaceId) {\n id name slug description icon color displayField recordCount\n }\n }\n }\n}";
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-CJQ-nafm.js';
|
|
2
2
|
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig, RawBlock, FieldDefinition } from '@cmssy/types';
|
|
3
3
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
4
|
-
import { G as GraphqlRequestOptions } from './locale-
|
|
5
|
-
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, l as localizeHref,
|
|
6
|
-
import { Q as QueryScopedOptions } from './csp-
|
|
7
|
-
export { o as cmssyCspHeaders, q as isDevelopment, s as resolveInitialTarget, t as toCspOrigin } from './csp-
|
|
8
|
-
export {
|
|
4
|
+
import { G as GraphqlRequestOptions } from './locale-Clouq4s4.js';
|
|
5
|
+
export { C as CMSSY_LOCALE_HEADER, b as buildLocaleSwitchHref, a as localeForPath, c as localeForPathname, d as localesFromSiteConfig, l as localizeHref, e as localizeHtmlLinks, f as resolveSiteLocales, s as splitCmssyLocale, h as splitLocaleFromPath } from './locale-Clouq4s4.js';
|
|
6
|
+
import { Q as QueryScopedOptions } from './csp-CpiqfM03.js';
|
|
7
|
+
export { o as cmssyCspHeaders, q as isDevelopment, s as resolveInitialTarget, t as toCspOrigin } from './csp-CpiqfM03.js';
|
|
8
|
+
export { localizedPath } from './internal/locale.js';
|
|
9
9
|
|
|
10
10
|
declare const SITE_CONFIG_QUERY = "query PublicSiteConfig($workspaceSlug: String!) {\n public {\n siteConfig(workspaceSlug: $workspaceSlug) {\n id\n workspaceId\n siteName\n defaultLanguage\n enabledLanguages\n enabledFeatures\n notFoundPageId\n previewUrl\n branding {\n brandName\n logoUrl\n logoDarkUrl\n faviconUrl\n ogImageUrl\n }\n }\n }\n}";
|
|
11
11
|
declare const MODEL_DEFINITIONS_QUERY = "query PublicModelDefinitions($workspaceId: String!) {\n public {\n model {\n definitions(workspaceId: $workspaceId) {\n id name slug description icon color displayField recordCount\n }\n }\n }\n}";
|
package/dist/internal.js
CHANGED
|
@@ -711,6 +711,7 @@ async function resolveForms(config, blocks, locale, defaultLocale, options) {
|
|
|
711
711
|
|
|
712
712
|
// src/data/site-locales.ts
|
|
713
713
|
var TTL_MS = 6e4;
|
|
714
|
+
var FAILURE_TTL_MS = 5e3;
|
|
714
715
|
var MAX_ENTRIES = 64;
|
|
715
716
|
var cache = /* @__PURE__ */ new Map();
|
|
716
717
|
function localesFromSiteConfig(siteConfig) {
|
|
@@ -721,7 +722,7 @@ function localesFromSiteConfig(siteConfig) {
|
|
|
721
722
|
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
722
723
|
};
|
|
723
724
|
}
|
|
724
|
-
async function
|
|
725
|
+
async function loadSiteLocales(config, options) {
|
|
725
726
|
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
726
727
|
const cached = cache.get(key);
|
|
727
728
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
@@ -737,12 +738,21 @@ async function resolveSiteLocales(config, options) {
|
|
|
737
738
|
);
|
|
738
739
|
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
739
740
|
} catch {
|
|
740
|
-
value =
|
|
741
|
+
value = null;
|
|
741
742
|
}
|
|
742
743
|
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
743
|
-
cache.set(key, {
|
|
744
|
+
cache.set(key, {
|
|
745
|
+
value,
|
|
746
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
747
|
+
});
|
|
744
748
|
return value;
|
|
745
749
|
}
|
|
750
|
+
async function resolveSiteLocales(config, options) {
|
|
751
|
+
return await loadSiteLocales(config, options) ?? {
|
|
752
|
+
defaultLocale: "en",
|
|
753
|
+
locales: ["en"]
|
|
754
|
+
};
|
|
755
|
+
}
|
|
746
756
|
function splitLocaleFromPath(path, siteLocales) {
|
|
747
757
|
const first = path?.[0];
|
|
748
758
|
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { CmssyClientConfig, CmssyLocaleContext, CmssySiteLocales } from '@cmssy/types';
|
|
1
2
|
import { F as FetchLike, R as RetryPolicy } from './content-client-CJQ-nafm.js';
|
|
2
|
-
import { CmssyClientConfig, CmssyLocaleContext } from '@cmssy/types';
|
|
3
3
|
|
|
4
4
|
interface GraphqlRequestOptions {
|
|
5
5
|
fetch?: FetchLike;
|
|
@@ -37,6 +37,22 @@ declare function buildLocaleSwitchHref(target: string, pathname: string, locale:
|
|
|
37
37
|
*/
|
|
38
38
|
declare function localizeHtmlLinks(html: string, locale: CmssyLocaleContext): string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Maps a workspace site config to its locale set. The single place that
|
|
42
|
+
* decides the default and enabled languages - the router and the SEO helpers
|
|
43
|
+
* must agree, so both go through here.
|
|
44
|
+
*/
|
|
45
|
+
declare function localesFromSiteConfig(siteConfig: {
|
|
46
|
+
defaultLanguage?: string | null;
|
|
47
|
+
enabledLanguages?: string[];
|
|
48
|
+
} | null): CmssySiteLocales;
|
|
49
|
+
declare function resolveSiteLocales(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<CmssySiteLocales>;
|
|
50
|
+
declare function resolveCmssyLocale(config: CmssyClientConfig, path: string[] | undefined, options?: GraphqlRequestOptions): Promise<string | undefined>;
|
|
51
|
+
declare function splitLocaleFromPath(path: string[] | undefined, siteLocales: CmssySiteLocales): {
|
|
52
|
+
locale: string;
|
|
53
|
+
path: string[] | undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
40
56
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
41
57
|
declare function localeForPathname(config: CmssyClientConfig, pathname: string): Promise<string>;
|
|
42
58
|
declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | undefined): Promise<{
|
|
@@ -49,4 +65,4 @@ declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | un
|
|
|
49
65
|
*/
|
|
50
66
|
declare function localeForPath(config: CmssyClientConfig, path: string | string[]): Promise<string>;
|
|
51
67
|
|
|
52
|
-
export { CMSSY_LOCALE_HEADER as C, type GraphqlRequestOptions as G, localeForPath as a, buildLocaleSwitchHref as b, localeForPathname as c,
|
|
68
|
+
export { CMSSY_LOCALE_HEADER as C, type GraphqlRequestOptions as G, localeForPath as a, buildLocaleSwitchHref as b, localeForPathname as c, localesFromSiteConfig as d, localizeHtmlLinks as e, resolveSiteLocales as f, graphqlRequest as g, splitLocaleFromPath as h, localizeHref as l, resolveCmssyLocale as r, splitCmssyLocale as s };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { CmssyClientConfig, CmssyLocaleContext, CmssySiteLocales } from '@cmssy/types';
|
|
1
2
|
import { F as FetchLike, R as RetryPolicy } from './content-client-CJQ-nafm.cjs';
|
|
2
|
-
import { CmssyClientConfig, CmssyLocaleContext } from '@cmssy/types';
|
|
3
3
|
|
|
4
4
|
interface GraphqlRequestOptions {
|
|
5
5
|
fetch?: FetchLike;
|
|
@@ -37,6 +37,22 @@ declare function buildLocaleSwitchHref(target: string, pathname: string, locale:
|
|
|
37
37
|
*/
|
|
38
38
|
declare function localizeHtmlLinks(html: string, locale: CmssyLocaleContext): string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Maps a workspace site config to its locale set. The single place that
|
|
42
|
+
* decides the default and enabled languages - the router and the SEO helpers
|
|
43
|
+
* must agree, so both go through here.
|
|
44
|
+
*/
|
|
45
|
+
declare function localesFromSiteConfig(siteConfig: {
|
|
46
|
+
defaultLanguage?: string | null;
|
|
47
|
+
enabledLanguages?: string[];
|
|
48
|
+
} | null): CmssySiteLocales;
|
|
49
|
+
declare function resolveSiteLocales(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<CmssySiteLocales>;
|
|
50
|
+
declare function resolveCmssyLocale(config: CmssyClientConfig, path: string[] | undefined, options?: GraphqlRequestOptions): Promise<string | undefined>;
|
|
51
|
+
declare function splitLocaleFromPath(path: string[] | undefined, siteLocales: CmssySiteLocales): {
|
|
52
|
+
locale: string;
|
|
53
|
+
path: string[] | undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
40
56
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
41
57
|
declare function localeForPathname(config: CmssyClientConfig, pathname: string): Promise<string>;
|
|
42
58
|
declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | undefined): Promise<{
|
|
@@ -49,4 +65,4 @@ declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | un
|
|
|
49
65
|
*/
|
|
50
66
|
declare function localeForPath(config: CmssyClientConfig, path: string | string[]): Promise<string>;
|
|
51
67
|
|
|
52
|
-
export { CMSSY_LOCALE_HEADER as C, type GraphqlRequestOptions as G, localeForPath as a, buildLocaleSwitchHref as b, localeForPathname as c,
|
|
68
|
+
export { CMSSY_LOCALE_HEADER as C, type GraphqlRequestOptions as G, localeForPath as a, buildLocaleSwitchHref as b, localeForPathname as c, localesFromSiteConfig as d, localizeHtmlLinks as e, resolveSiteLocales as f, graphqlRequest as g, splitLocaleFromPath as h, localizeHref as l, resolveCmssyLocale as r, splitCmssyLocale as s };
|
package/dist/testing.cjs
CHANGED
|
@@ -141,6 +141,80 @@ async function graphqlRequest(config, query, variables, options = {}, label = "r
|
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
// src/data/queries.ts
|
|
145
|
+
var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
146
|
+
public {
|
|
147
|
+
siteConfig(workspaceSlug: $workspaceSlug) {
|
|
148
|
+
id
|
|
149
|
+
workspaceId
|
|
150
|
+
siteName
|
|
151
|
+
defaultLanguage
|
|
152
|
+
enabledLanguages
|
|
153
|
+
enabledFeatures
|
|
154
|
+
notFoundPageId
|
|
155
|
+
previewUrl
|
|
156
|
+
branding {
|
|
157
|
+
brandName
|
|
158
|
+
logoUrl
|
|
159
|
+
logoDarkUrl
|
|
160
|
+
faviconUrl
|
|
161
|
+
ogImageUrl
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}`;
|
|
166
|
+
|
|
167
|
+
// src/data/site-locales.ts
|
|
168
|
+
function localesFromSiteConfig(siteConfig) {
|
|
169
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
170
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
171
|
+
return {
|
|
172
|
+
defaultLocale,
|
|
173
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/preflight.ts
|
|
178
|
+
var ALLOWED_FRAME_HOSTS = ["cmssy.io", "www.cmssy.io"];
|
|
179
|
+
function frameAncestorSources(cspHeaderValue) {
|
|
180
|
+
const directive = cspHeaderValue.split(";").map((part) => part.trim()).find((part) => /^frame-ancestors\b/i.test(part));
|
|
181
|
+
if (!directive) return null;
|
|
182
|
+
return directive.split(/\s+/).slice(1).map((source) => source.toLowerCase().replace(/^'|'$/g, ""));
|
|
183
|
+
}
|
|
184
|
+
function allowsCmssyAdmin(source) {
|
|
185
|
+
if (source === "*" || source === "https:") return true;
|
|
186
|
+
const host = source.replace(/^[a-z][a-z0-9+.-]*:\/\//, "").split("/")[0];
|
|
187
|
+
if (!host) return false;
|
|
188
|
+
if (host === "*.cmssy.io") return true;
|
|
189
|
+
return ALLOWED_FRAME_HOSTS.includes(host);
|
|
190
|
+
}
|
|
191
|
+
function checkFrameAncestors(cspHeaderValue) {
|
|
192
|
+
if (!cspHeaderValue || cspHeaderValue.trim().length === 0) {
|
|
193
|
+
return {
|
|
194
|
+
status: "ok",
|
|
195
|
+
message: "no Content-Security-Policy restricts framing"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const sources = frameAncestorSources(cspHeaderValue);
|
|
199
|
+
if (sources === null) {
|
|
200
|
+
return {
|
|
201
|
+
status: "ok",
|
|
202
|
+
message: "the Content-Security-Policy has no frame-ancestors directive"
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (sources.some(allowsCmssyAdmin)) {
|
|
206
|
+
return {
|
|
207
|
+
status: "ok",
|
|
208
|
+
message: "frame-ancestors allows the cmssy editor"
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
status: "fail",
|
|
213
|
+
message: `frame-ancestors (${sources.join(" ") || "'none'"}) blocks the cmssy editor`,
|
|
214
|
+
fix: "add https://cmssy.io https://www.cmssy.io to the frame-ancestors directive (applyCmssyCsp does this for you)"
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
144
218
|
// src/testing/edit-smoke.ts
|
|
145
219
|
var EDITOR_MARKER = /data-cmssy-editor/;
|
|
146
220
|
var SERVER_LAYOUT_BLOCKS = /<header|<footer/;
|
|
@@ -151,7 +225,22 @@ function resolvedContentCounts(body) {
|
|
|
151
225
|
}
|
|
152
226
|
async function html(url) {
|
|
153
227
|
const response = await fetch(url, { redirect: "manual" });
|
|
154
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
status: response.status,
|
|
230
|
+
body: await response.text(),
|
|
231
|
+
headers: response.headers
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
var EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
235
|
+
function withoutEditPrefix(path) {
|
|
236
|
+
if (path === EDIT_PATH_PREFIX) return "/";
|
|
237
|
+
return path.startsWith(`${EDIT_PATH_PREFIX}/`) ? path.slice(EDIT_PATH_PREFIX.length) : path;
|
|
238
|
+
}
|
|
239
|
+
function localeOf(path) {
|
|
240
|
+
return withoutEditPrefix(path).split("/").filter(Boolean)[0];
|
|
241
|
+
}
|
|
242
|
+
function langOf(body) {
|
|
243
|
+
return /<html[^>]*\slang=["']([^"']+)["']/i.exec(body)?.[1];
|
|
155
244
|
}
|
|
156
245
|
var LAYOUT_BLOCKS_QUERY = `query CmssySmokeLayouts($workspaceSlug: String!, $pageSlug: String!) {
|
|
157
246
|
public {
|
|
@@ -180,6 +269,29 @@ async function workspaceHasLayoutBlocks(workspace, pageSlug) {
|
|
|
180
269
|
return null;
|
|
181
270
|
}
|
|
182
271
|
}
|
|
272
|
+
async function derivedLocalizedPath(workspace, path) {
|
|
273
|
+
try {
|
|
274
|
+
const data = await graphqlRequest(
|
|
275
|
+
workspace,
|
|
276
|
+
SITE_CONFIG_QUERY,
|
|
277
|
+
{ workspaceSlug: workspace.workspaceSlug },
|
|
278
|
+
{ public: true, retry: {} },
|
|
279
|
+
"edit smoke: site locales"
|
|
280
|
+
);
|
|
281
|
+
const { defaultLocale, locales } = localesFromSiteConfig(
|
|
282
|
+
data.public?.siteConfig ?? null
|
|
283
|
+
);
|
|
284
|
+
const locale = locales.find((candidate) => candidate !== defaultLocale);
|
|
285
|
+
if (!locale) return { kind: "none" };
|
|
286
|
+
const routed = withoutEditPrefix(path);
|
|
287
|
+
const [first] = routed.split("/").filter(Boolean);
|
|
288
|
+
if (first && locales.includes(first)) return { kind: "none" };
|
|
289
|
+
const suffix = routed === "/" || routed === "" ? "" : routed.replace(/\/+$/, "");
|
|
290
|
+
return { kind: "derived", locale, localizedPath: `/${locale}${suffix}` };
|
|
291
|
+
} catch {
|
|
292
|
+
return { kind: "unavailable" };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
183
295
|
async function checkCmssyEditMode(options) {
|
|
184
296
|
const { baseUrl, secret, path = "/" } = options;
|
|
185
297
|
const failures = [];
|
|
@@ -232,25 +344,62 @@ async function checkCmssyEditMode(options) {
|
|
|
232
344
|
);
|
|
233
345
|
}
|
|
234
346
|
}
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
)
|
|
347
|
+
const query = `cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`;
|
|
348
|
+
const asked = options.localizedPath;
|
|
349
|
+
const derived = !asked && options.workspace ? await derivedLocalizedPath(options.workspace, path) : { kind: "none" };
|
|
350
|
+
if (derived.kind === "unavailable") {
|
|
351
|
+
failures.push(
|
|
352
|
+
`the delivery API would not say which languages ${options.workspace?.workspaceSlug} enables, so the preview's language went unchecked. That is the check three releases shipped a wrong-language preview past - a green run here does not mean it works.`
|
|
242
353
|
);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
354
|
+
}
|
|
355
|
+
const localizedPath = asked ?? (derived.kind === "derived" ? derived.localizedPath : void 0);
|
|
356
|
+
if (localizedPath) {
|
|
357
|
+
const locale = options.localizedLocale ?? (derived.kind === "derived" ? derived.locale : localeOf(localizedPath));
|
|
358
|
+
const localized = await html(url(`${localizedPath}?${query}`));
|
|
359
|
+
if (localized.status !== 200) {
|
|
248
360
|
failures.push(
|
|
249
|
-
`edit ${localizedPath}:
|
|
361
|
+
derived.kind === "derived" ? `edit ${localizedPath}: expected 200, got ${localized.status}. The workspace enables "${locale}", so the language went unchecked - pass localizedPath if this site spells the language some other way.` : `edit ${localizedPath}: expected 200, got ${localized.status}`
|
|
250
362
|
);
|
|
363
|
+
} else {
|
|
364
|
+
if (!EDITOR_MARKER.test(localized.body)) {
|
|
365
|
+
failures.push(`edit ${localizedPath}: no editor in the response`);
|
|
366
|
+
}
|
|
367
|
+
const served = langOf(localized.body);
|
|
368
|
+
if (locale && served !== locale) {
|
|
369
|
+
failures.push(
|
|
370
|
+
`edit ${localizedPath}: the page reports lang="${served ?? "?"}" but the URL asks for "${locale}" - the preview renders in the wrong language`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
if (options.editRoute !== false) {
|
|
374
|
+
await checkDirectEditRoute(url, localizedPath, query, locale, failures);
|
|
375
|
+
}
|
|
251
376
|
}
|
|
252
377
|
}
|
|
378
|
+
if (options.editRoute !== false) {
|
|
379
|
+
await checkDirectEditRoute(url, path, query, void 0, failures);
|
|
380
|
+
}
|
|
253
381
|
return { ok: failures.length === 0, failures };
|
|
254
382
|
}
|
|
383
|
+
async function checkDirectEditRoute(url, routedPath, query, locale, failures) {
|
|
384
|
+
const withoutPrefix = withoutEditPrefix(routedPath);
|
|
385
|
+
const directPath = `${EDIT_PATH_PREFIX}${withoutPrefix === "/" ? "" : withoutPrefix}`;
|
|
386
|
+
if (directPath === routedPath) return;
|
|
387
|
+
const direct = await html(url(`${directPath}?${query}`));
|
|
388
|
+
if (direct.status !== 200 || !EDITOR_MARKER.test(direct.body)) return;
|
|
389
|
+
const directLang = langOf(direct.body);
|
|
390
|
+
if (locale && directLang !== locale) {
|
|
391
|
+
failures.push(
|
|
392
|
+
`edit ${directPath}: reached directly the page reports lang="${directLang ?? "?"}", through the rewrite it reports "${locale}" - the same page renders in two languages depending on how the editor arrives`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
const framing = checkFrameAncestors(
|
|
396
|
+
direct.headers.get("content-security-policy")
|
|
397
|
+
);
|
|
398
|
+
if (framing.status === "fail") {
|
|
399
|
+
failures.push(
|
|
400
|
+
`edit ${directPath}: ${framing.message} - the editor shows a blank panel for a route that renders fine on its own. ${framing.fix ?? ""}`.trim()
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
255
404
|
|
|
256
405
|
exports.checkCmssyEditMode = checkCmssyEditMode;
|
package/dist/testing.d.cts
CHANGED
package/dist/testing.d.ts
CHANGED
package/dist/testing.js
CHANGED
|
@@ -139,6 +139,80 @@ async function graphqlRequest(config, query, variables, options = {}, label = "r
|
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// src/data/queries.ts
|
|
143
|
+
var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
144
|
+
public {
|
|
145
|
+
siteConfig(workspaceSlug: $workspaceSlug) {
|
|
146
|
+
id
|
|
147
|
+
workspaceId
|
|
148
|
+
siteName
|
|
149
|
+
defaultLanguage
|
|
150
|
+
enabledLanguages
|
|
151
|
+
enabledFeatures
|
|
152
|
+
notFoundPageId
|
|
153
|
+
previewUrl
|
|
154
|
+
branding {
|
|
155
|
+
brandName
|
|
156
|
+
logoUrl
|
|
157
|
+
logoDarkUrl
|
|
158
|
+
faviconUrl
|
|
159
|
+
ogImageUrl
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}`;
|
|
164
|
+
|
|
165
|
+
// src/data/site-locales.ts
|
|
166
|
+
function localesFromSiteConfig(siteConfig) {
|
|
167
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
168
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
169
|
+
return {
|
|
170
|
+
defaultLocale,
|
|
171
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/preflight.ts
|
|
176
|
+
var ALLOWED_FRAME_HOSTS = ["cmssy.io", "www.cmssy.io"];
|
|
177
|
+
function frameAncestorSources(cspHeaderValue) {
|
|
178
|
+
const directive = cspHeaderValue.split(";").map((part) => part.trim()).find((part) => /^frame-ancestors\b/i.test(part));
|
|
179
|
+
if (!directive) return null;
|
|
180
|
+
return directive.split(/\s+/).slice(1).map((source) => source.toLowerCase().replace(/^'|'$/g, ""));
|
|
181
|
+
}
|
|
182
|
+
function allowsCmssyAdmin(source) {
|
|
183
|
+
if (source === "*" || source === "https:") return true;
|
|
184
|
+
const host = source.replace(/^[a-z][a-z0-9+.-]*:\/\//, "").split("/")[0];
|
|
185
|
+
if (!host) return false;
|
|
186
|
+
if (host === "*.cmssy.io") return true;
|
|
187
|
+
return ALLOWED_FRAME_HOSTS.includes(host);
|
|
188
|
+
}
|
|
189
|
+
function checkFrameAncestors(cspHeaderValue) {
|
|
190
|
+
if (!cspHeaderValue || cspHeaderValue.trim().length === 0) {
|
|
191
|
+
return {
|
|
192
|
+
status: "ok",
|
|
193
|
+
message: "no Content-Security-Policy restricts framing"
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const sources = frameAncestorSources(cspHeaderValue);
|
|
197
|
+
if (sources === null) {
|
|
198
|
+
return {
|
|
199
|
+
status: "ok",
|
|
200
|
+
message: "the Content-Security-Policy has no frame-ancestors directive"
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (sources.some(allowsCmssyAdmin)) {
|
|
204
|
+
return {
|
|
205
|
+
status: "ok",
|
|
206
|
+
message: "frame-ancestors allows the cmssy editor"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
status: "fail",
|
|
211
|
+
message: `frame-ancestors (${sources.join(" ") || "'none'"}) blocks the cmssy editor`,
|
|
212
|
+
fix: "add https://cmssy.io https://www.cmssy.io to the frame-ancestors directive (applyCmssyCsp does this for you)"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
142
216
|
// src/testing/edit-smoke.ts
|
|
143
217
|
var EDITOR_MARKER = /data-cmssy-editor/;
|
|
144
218
|
var SERVER_LAYOUT_BLOCKS = /<header|<footer/;
|
|
@@ -149,7 +223,22 @@ function resolvedContentCounts(body) {
|
|
|
149
223
|
}
|
|
150
224
|
async function html(url) {
|
|
151
225
|
const response = await fetch(url, { redirect: "manual" });
|
|
152
|
-
return {
|
|
226
|
+
return {
|
|
227
|
+
status: response.status,
|
|
228
|
+
body: await response.text(),
|
|
229
|
+
headers: response.headers
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
var EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
233
|
+
function withoutEditPrefix(path) {
|
|
234
|
+
if (path === EDIT_PATH_PREFIX) return "/";
|
|
235
|
+
return path.startsWith(`${EDIT_PATH_PREFIX}/`) ? path.slice(EDIT_PATH_PREFIX.length) : path;
|
|
236
|
+
}
|
|
237
|
+
function localeOf(path) {
|
|
238
|
+
return withoutEditPrefix(path).split("/").filter(Boolean)[0];
|
|
239
|
+
}
|
|
240
|
+
function langOf(body) {
|
|
241
|
+
return /<html[^>]*\slang=["']([^"']+)["']/i.exec(body)?.[1];
|
|
153
242
|
}
|
|
154
243
|
var LAYOUT_BLOCKS_QUERY = `query CmssySmokeLayouts($workspaceSlug: String!, $pageSlug: String!) {
|
|
155
244
|
public {
|
|
@@ -178,6 +267,29 @@ async function workspaceHasLayoutBlocks(workspace, pageSlug) {
|
|
|
178
267
|
return null;
|
|
179
268
|
}
|
|
180
269
|
}
|
|
270
|
+
async function derivedLocalizedPath(workspace, path) {
|
|
271
|
+
try {
|
|
272
|
+
const data = await graphqlRequest(
|
|
273
|
+
workspace,
|
|
274
|
+
SITE_CONFIG_QUERY,
|
|
275
|
+
{ workspaceSlug: workspace.workspaceSlug },
|
|
276
|
+
{ public: true, retry: {} },
|
|
277
|
+
"edit smoke: site locales"
|
|
278
|
+
);
|
|
279
|
+
const { defaultLocale, locales } = localesFromSiteConfig(
|
|
280
|
+
data.public?.siteConfig ?? null
|
|
281
|
+
);
|
|
282
|
+
const locale = locales.find((candidate) => candidate !== defaultLocale);
|
|
283
|
+
if (!locale) return { kind: "none" };
|
|
284
|
+
const routed = withoutEditPrefix(path);
|
|
285
|
+
const [first] = routed.split("/").filter(Boolean);
|
|
286
|
+
if (first && locales.includes(first)) return { kind: "none" };
|
|
287
|
+
const suffix = routed === "/" || routed === "" ? "" : routed.replace(/\/+$/, "");
|
|
288
|
+
return { kind: "derived", locale, localizedPath: `/${locale}${suffix}` };
|
|
289
|
+
} catch {
|
|
290
|
+
return { kind: "unavailable" };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
181
293
|
async function checkCmssyEditMode(options) {
|
|
182
294
|
const { baseUrl, secret, path = "/" } = options;
|
|
183
295
|
const failures = [];
|
|
@@ -230,25 +342,62 @@ async function checkCmssyEditMode(options) {
|
|
|
230
342
|
);
|
|
231
343
|
}
|
|
232
344
|
}
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
)
|
|
345
|
+
const query = `cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`;
|
|
346
|
+
const asked = options.localizedPath;
|
|
347
|
+
const derived = !asked && options.workspace ? await derivedLocalizedPath(options.workspace, path) : { kind: "none" };
|
|
348
|
+
if (derived.kind === "unavailable") {
|
|
349
|
+
failures.push(
|
|
350
|
+
`the delivery API would not say which languages ${options.workspace?.workspaceSlug} enables, so the preview's language went unchecked. That is the check three releases shipped a wrong-language preview past - a green run here does not mean it works.`
|
|
240
351
|
);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
352
|
+
}
|
|
353
|
+
const localizedPath = asked ?? (derived.kind === "derived" ? derived.localizedPath : void 0);
|
|
354
|
+
if (localizedPath) {
|
|
355
|
+
const locale = options.localizedLocale ?? (derived.kind === "derived" ? derived.locale : localeOf(localizedPath));
|
|
356
|
+
const localized = await html(url(`${localizedPath}?${query}`));
|
|
357
|
+
if (localized.status !== 200) {
|
|
246
358
|
failures.push(
|
|
247
|
-
`edit ${localizedPath}:
|
|
359
|
+
derived.kind === "derived" ? `edit ${localizedPath}: expected 200, got ${localized.status}. The workspace enables "${locale}", so the language went unchecked - pass localizedPath if this site spells the language some other way.` : `edit ${localizedPath}: expected 200, got ${localized.status}`
|
|
248
360
|
);
|
|
361
|
+
} else {
|
|
362
|
+
if (!EDITOR_MARKER.test(localized.body)) {
|
|
363
|
+
failures.push(`edit ${localizedPath}: no editor in the response`);
|
|
364
|
+
}
|
|
365
|
+
const served = langOf(localized.body);
|
|
366
|
+
if (locale && served !== locale) {
|
|
367
|
+
failures.push(
|
|
368
|
+
`edit ${localizedPath}: the page reports lang="${served ?? "?"}" but the URL asks for "${locale}" - the preview renders in the wrong language`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
if (options.editRoute !== false) {
|
|
372
|
+
await checkDirectEditRoute(url, localizedPath, query, locale, failures);
|
|
373
|
+
}
|
|
249
374
|
}
|
|
250
375
|
}
|
|
376
|
+
if (options.editRoute !== false) {
|
|
377
|
+
await checkDirectEditRoute(url, path, query, void 0, failures);
|
|
378
|
+
}
|
|
251
379
|
return { ok: failures.length === 0, failures };
|
|
252
380
|
}
|
|
381
|
+
async function checkDirectEditRoute(url, routedPath, query, locale, failures) {
|
|
382
|
+
const withoutPrefix = withoutEditPrefix(routedPath);
|
|
383
|
+
const directPath = `${EDIT_PATH_PREFIX}${withoutPrefix === "/" ? "" : withoutPrefix}`;
|
|
384
|
+
if (directPath === routedPath) return;
|
|
385
|
+
const direct = await html(url(`${directPath}?${query}`));
|
|
386
|
+
if (direct.status !== 200 || !EDITOR_MARKER.test(direct.body)) return;
|
|
387
|
+
const directLang = langOf(direct.body);
|
|
388
|
+
if (locale && directLang !== locale) {
|
|
389
|
+
failures.push(
|
|
390
|
+
`edit ${directPath}: reached directly the page reports lang="${directLang ?? "?"}", through the rewrite it reports "${locale}" - the same page renders in two languages depending on how the editor arrives`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
const framing = checkFrameAncestors(
|
|
394
|
+
direct.headers.get("content-security-policy")
|
|
395
|
+
);
|
|
396
|
+
if (framing.status === "fail") {
|
|
397
|
+
failures.push(
|
|
398
|
+
`edit ${directPath}: ${framing.message} - the editor shows a blank panel for a route that renders fine on its own. ${framing.fix ?? ""}`.trim()
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
253
402
|
|
|
254
403
|
export { checkCmssyEditMode };
|