@bettercms-ai/sdk 1.12.1 → 1.13.1
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/index.cjs +11 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -5
- package/dist/index.d.ts +41 -5
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -476,6 +476,20 @@ interface DeliveryFormField {
|
|
|
476
476
|
field: string;
|
|
477
477
|
equals: string;
|
|
478
478
|
};
|
|
479
|
+
/**
|
|
480
|
+
* Per-field rules the API enforces on submit. Renderers should mirror them onto the
|
|
481
|
+
* native control (`min`/`max`/`pattern`) so the browser reports them before the round
|
|
482
|
+
* trip — the server check is the one that counts.
|
|
483
|
+
*/
|
|
484
|
+
validation?: DeliveryFormFieldValidation;
|
|
485
|
+
}
|
|
486
|
+
/** @see FormFieldValidation in @bettercms-ai/types — same shape, delivered to the site. */
|
|
487
|
+
interface DeliveryFormFieldValidation {
|
|
488
|
+
emailPolicy?: "any" | "business";
|
|
489
|
+
min?: number;
|
|
490
|
+
max?: number;
|
|
491
|
+
phoneFormat?: "any" | "e164";
|
|
492
|
+
pattern?: string;
|
|
479
493
|
}
|
|
480
494
|
/** The pick-many type, whose value is an array rather than a scalar. */
|
|
481
495
|
declare function isMultiValueField(type: DeliveryFormFieldType): boolean;
|
|
@@ -666,6 +680,7 @@ interface SeoMeta {
|
|
|
666
680
|
|
|
667
681
|
type ManagedLayoutDocument = {
|
|
668
682
|
scope: "global";
|
|
683
|
+
copy?: "draft" | "published";
|
|
669
684
|
revision: number;
|
|
670
685
|
etag: string;
|
|
671
686
|
status: string;
|
|
@@ -673,6 +688,7 @@ type ManagedLayoutDocument = {
|
|
|
673
688
|
data: LayoutDataDocument;
|
|
674
689
|
} | {
|
|
675
690
|
scope: "page";
|
|
691
|
+
copy?: "draft" | "published";
|
|
676
692
|
pageId: string;
|
|
677
693
|
pageSlug: string;
|
|
678
694
|
revision: number;
|
|
@@ -689,6 +705,9 @@ interface ManagedLayoutClient {
|
|
|
689
705
|
interface GetManagedLayoutOptions {
|
|
690
706
|
scope?: "global" | "page";
|
|
691
707
|
pageId?: string;
|
|
708
|
+
/** Which copy to read — default draft. A publish claim verifies ONLY against the
|
|
709
|
+
* published copy; check the response's `copy` echo (FLO-1188). */
|
|
710
|
+
copy?: "draft" | "published";
|
|
692
711
|
/** Required when the management credential is workspace-scoped. */
|
|
693
712
|
projectId?: string;
|
|
694
713
|
}
|
|
@@ -1081,7 +1100,11 @@ interface SeoInput {
|
|
|
1081
1100
|
interface ResolvedSeo {
|
|
1082
1101
|
title: string;
|
|
1083
1102
|
description: string;
|
|
1084
|
-
/**
|
|
1103
|
+
/**
|
|
1104
|
+
* Canonical URL — the page's own `canonical`, else `siteUrl` + `path` when both are given,
|
|
1105
|
+
* else "". Empty means the consumer must omit the tag: a RELATIVE canonical is worse than
|
|
1106
|
+
* none at all.
|
|
1107
|
+
*/
|
|
1085
1108
|
canonical: string;
|
|
1086
1109
|
og: {
|
|
1087
1110
|
title: string;
|
|
@@ -1100,11 +1123,24 @@ interface ResolvedSeo {
|
|
|
1100
1123
|
/** JSON-LD nodes (site schema first, then the page's), already flattened + de-empted. */
|
|
1101
1124
|
jsonLd: Array<Record<string, unknown>>;
|
|
1102
1125
|
}
|
|
1126
|
+
/** Where this page lives, so a canonical can be derived when the page sets none. */
|
|
1127
|
+
interface SeoLocation {
|
|
1128
|
+
/** Absolute site origin, e.g. `https://acme.com`. Trailing slash optional. */
|
|
1129
|
+
siteUrl?: string;
|
|
1130
|
+
/** This page's path, e.g. `/about` or `/`. */
|
|
1131
|
+
path?: string;
|
|
1132
|
+
}
|
|
1103
1133
|
/**
|
|
1104
|
-
* Merge a page's own SEO over the site defaults
|
|
1105
|
-
*
|
|
1134
|
+
* Merge a page's own SEO over the site defaults: page meta wins, then site defaults, then
|
|
1135
|
+
* sensible fallbacks.
|
|
1136
|
+
*
|
|
1137
|
+
* The CANONICAL is the one field that cannot be resolved from content alone, and getting this
|
|
1138
|
+
* wrong is silent. The hosted renderer (`mergeSeoMeta` in the backend) falls back to a canonical
|
|
1139
|
+
* it derives from the request's own origin and path — headless consumers have no request, so
|
|
1140
|
+
* they must pass `location`. Without it this returns "" and the caller ships no
|
|
1141
|
+
* `<link rel="canonical">` and no `og:url`, exactly as it did before `location` existed.
|
|
1106
1142
|
*/
|
|
1107
|
-
declare function resolveSeo(page: SeoInput, defaults?: SiteSeoDefaults): ResolvedSeo;
|
|
1143
|
+
declare function resolveSeo(page: SeoInput, defaults?: SiteSeoDefaults, location?: SeoLocation): ResolvedSeo;
|
|
1108
1144
|
|
|
1109
1145
|
/**
|
|
1110
1146
|
* Public site search — the headless surface for the visitor-facing search box.
|
|
@@ -1326,4 +1362,4 @@ declare function inviteMember(client: BetterCMSAdminClient, workspaceId: string,
|
|
|
1326
1362
|
declare function updateMemberRole(client: BetterCMSAdminClient, workspaceId: string, memberId: string, role: MemberRole): Promise<Member>;
|
|
1327
1363
|
declare function removeMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<void>;
|
|
1328
1364
|
|
|
1329
|
-
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
|
1365
|
+
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
package/dist/index.d.ts
CHANGED
|
@@ -476,6 +476,20 @@ interface DeliveryFormField {
|
|
|
476
476
|
field: string;
|
|
477
477
|
equals: string;
|
|
478
478
|
};
|
|
479
|
+
/**
|
|
480
|
+
* Per-field rules the API enforces on submit. Renderers should mirror them onto the
|
|
481
|
+
* native control (`min`/`max`/`pattern`) so the browser reports them before the round
|
|
482
|
+
* trip — the server check is the one that counts.
|
|
483
|
+
*/
|
|
484
|
+
validation?: DeliveryFormFieldValidation;
|
|
485
|
+
}
|
|
486
|
+
/** @see FormFieldValidation in @bettercms-ai/types — same shape, delivered to the site. */
|
|
487
|
+
interface DeliveryFormFieldValidation {
|
|
488
|
+
emailPolicy?: "any" | "business";
|
|
489
|
+
min?: number;
|
|
490
|
+
max?: number;
|
|
491
|
+
phoneFormat?: "any" | "e164";
|
|
492
|
+
pattern?: string;
|
|
479
493
|
}
|
|
480
494
|
/** The pick-many type, whose value is an array rather than a scalar. */
|
|
481
495
|
declare function isMultiValueField(type: DeliveryFormFieldType): boolean;
|
|
@@ -666,6 +680,7 @@ interface SeoMeta {
|
|
|
666
680
|
|
|
667
681
|
type ManagedLayoutDocument = {
|
|
668
682
|
scope: "global";
|
|
683
|
+
copy?: "draft" | "published";
|
|
669
684
|
revision: number;
|
|
670
685
|
etag: string;
|
|
671
686
|
status: string;
|
|
@@ -673,6 +688,7 @@ type ManagedLayoutDocument = {
|
|
|
673
688
|
data: LayoutDataDocument;
|
|
674
689
|
} | {
|
|
675
690
|
scope: "page";
|
|
691
|
+
copy?: "draft" | "published";
|
|
676
692
|
pageId: string;
|
|
677
693
|
pageSlug: string;
|
|
678
694
|
revision: number;
|
|
@@ -689,6 +705,9 @@ interface ManagedLayoutClient {
|
|
|
689
705
|
interface GetManagedLayoutOptions {
|
|
690
706
|
scope?: "global" | "page";
|
|
691
707
|
pageId?: string;
|
|
708
|
+
/** Which copy to read — default draft. A publish claim verifies ONLY against the
|
|
709
|
+
* published copy; check the response's `copy` echo (FLO-1188). */
|
|
710
|
+
copy?: "draft" | "published";
|
|
692
711
|
/** Required when the management credential is workspace-scoped. */
|
|
693
712
|
projectId?: string;
|
|
694
713
|
}
|
|
@@ -1081,7 +1100,11 @@ interface SeoInput {
|
|
|
1081
1100
|
interface ResolvedSeo {
|
|
1082
1101
|
title: string;
|
|
1083
1102
|
description: string;
|
|
1084
|
-
/**
|
|
1103
|
+
/**
|
|
1104
|
+
* Canonical URL — the page's own `canonical`, else `siteUrl` + `path` when both are given,
|
|
1105
|
+
* else "". Empty means the consumer must omit the tag: a RELATIVE canonical is worse than
|
|
1106
|
+
* none at all.
|
|
1107
|
+
*/
|
|
1085
1108
|
canonical: string;
|
|
1086
1109
|
og: {
|
|
1087
1110
|
title: string;
|
|
@@ -1100,11 +1123,24 @@ interface ResolvedSeo {
|
|
|
1100
1123
|
/** JSON-LD nodes (site schema first, then the page's), already flattened + de-empted. */
|
|
1101
1124
|
jsonLd: Array<Record<string, unknown>>;
|
|
1102
1125
|
}
|
|
1126
|
+
/** Where this page lives, so a canonical can be derived when the page sets none. */
|
|
1127
|
+
interface SeoLocation {
|
|
1128
|
+
/** Absolute site origin, e.g. `https://acme.com`. Trailing slash optional. */
|
|
1129
|
+
siteUrl?: string;
|
|
1130
|
+
/** This page's path, e.g. `/about` or `/`. */
|
|
1131
|
+
path?: string;
|
|
1132
|
+
}
|
|
1103
1133
|
/**
|
|
1104
|
-
* Merge a page's own SEO over the site defaults
|
|
1105
|
-
*
|
|
1134
|
+
* Merge a page's own SEO over the site defaults: page meta wins, then site defaults, then
|
|
1135
|
+
* sensible fallbacks.
|
|
1136
|
+
*
|
|
1137
|
+
* The CANONICAL is the one field that cannot be resolved from content alone, and getting this
|
|
1138
|
+
* wrong is silent. The hosted renderer (`mergeSeoMeta` in the backend) falls back to a canonical
|
|
1139
|
+
* it derives from the request's own origin and path — headless consumers have no request, so
|
|
1140
|
+
* they must pass `location`. Without it this returns "" and the caller ships no
|
|
1141
|
+
* `<link rel="canonical">` and no `og:url`, exactly as it did before `location` existed.
|
|
1106
1142
|
*/
|
|
1107
|
-
declare function resolveSeo(page: SeoInput, defaults?: SiteSeoDefaults): ResolvedSeo;
|
|
1143
|
+
declare function resolveSeo(page: SeoInput, defaults?: SiteSeoDefaults, location?: SeoLocation): ResolvedSeo;
|
|
1108
1144
|
|
|
1109
1145
|
/**
|
|
1110
1146
|
* Public site search — the headless surface for the visitor-facing search box.
|
|
@@ -1326,4 +1362,4 @@ declare function inviteMember(client: BetterCMSAdminClient, workspaceId: string,
|
|
|
1326
1362
|
declare function updateMemberRole(client: BetterCMSAdminClient, workspaceId: string, memberId: string, role: MemberRole): Promise<Member>;
|
|
1327
1363
|
declare function removeMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<void>;
|
|
1328
1364
|
|
|
1329
|
-
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
|
1365
|
+
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
package/dist/index.js
CHANGED
|
@@ -717,6 +717,7 @@ async function getManagedLayout(client, opts) {
|
|
|
717
717
|
const query = new URLSearchParams();
|
|
718
718
|
if (opts?.scope) query.set("scope", opts.scope);
|
|
719
719
|
if (opts?.pageId) query.set("pageId", opts.pageId);
|
|
720
|
+
if (opts?.copy) query.set("preview", opts.copy);
|
|
720
721
|
const result = await client.fetchJSON(
|
|
721
722
|
client.url(`/management/layout${query.size ? `?${query}` : ""}`),
|
|
722
723
|
opts?.projectId ? { headers: { "X-BCMS-Project": opts.projectId } } : void 0
|
|
@@ -1247,12 +1248,20 @@ function decodeEntities(s) {
|
|
|
1247
1248
|
}
|
|
1248
1249
|
|
|
1249
1250
|
// src/seo.ts
|
|
1251
|
+
function derivedCanonical({ siteUrl, path }) {
|
|
1252
|
+
if (!siteUrl || !path) return "";
|
|
1253
|
+
try {
|
|
1254
|
+
return new URL(path, siteUrl.endsWith("/") ? siteUrl : `${siteUrl}/`).toString();
|
|
1255
|
+
} catch {
|
|
1256
|
+
return "";
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1250
1259
|
function jsonLdNodes(schema) {
|
|
1251
1260
|
if (!schema) return [];
|
|
1252
1261
|
const arr = Array.isArray(schema) ? schema : [schema];
|
|
1253
1262
|
return arr.filter((node) => node && Object.keys(node).length > 0);
|
|
1254
1263
|
}
|
|
1255
|
-
function resolveSeo(page, defaults = {}) {
|
|
1264
|
+
function resolveSeo(page, defaults = {}, location = {}) {
|
|
1256
1265
|
const meta = page.metaJson ?? {};
|
|
1257
1266
|
const title = page.metaTitle || page.title || "";
|
|
1258
1267
|
const description = page.metaDescription || defaults.metaDescription || "";
|
|
@@ -1260,7 +1269,7 @@ function resolveSeo(page, defaults = {}) {
|
|
|
1260
1269
|
const ogTitle = meta.og?.title || title;
|
|
1261
1270
|
const ogDescription = meta.og?.description || description;
|
|
1262
1271
|
const twImage = meta.twitter?.image || ogImage;
|
|
1263
|
-
const canonical = meta.canonical ||
|
|
1272
|
+
const canonical = meta.canonical || derivedCanonical(location);
|
|
1264
1273
|
return {
|
|
1265
1274
|
title,
|
|
1266
1275
|
description,
|