@cmssy/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +696 -0
- package/dist/client.d.cts +77 -0
- package/dist/client.d.ts +77 -0
- package/dist/client.js +690 -0
- package/dist/index.cjs +529 -0
- package/dist/index.d.cts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +503 -0
- package/dist/registry-BoxAyw4_.d.cts +189 -0
- package/dist/registry-BoxAyw4_.d.ts +189 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { F as FieldDefinition, C as CmssyPageData, b as BlockDefinition, c as CmssyLayoutGroup, E as EditorToAppMessage, A as AppToEditorMessage, d as FetchLike, e as CmssyClientConfig, R as RawBlock, f as BlockMap } from './registry-BoxAyw4_.js';
|
|
2
|
+
export { a as BlockMeta, g as BlockRect, B as BlockSchema, h as BoundsMessage, i as ClickMessage, j as FetchLikeResponse, k as FetchPageOptions, l as FieldType, P as PROTOCOL_VERSION, m as ParentReadyMessage, n as PatchMessage, o as RawLayoutBlock, p as ReadyMessage, S as SelectMessage, q as blocksToMeta, r as blocksToSchemas, s as buildBlockMap, t as defineBlock, u as fetchLayouts, v as fetchPage, w as isProtocolCompatible, x as normalizeSlug } from './registry-BoxAyw4_.js';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
|
|
6
|
+
type FieldControl = Omit<FieldDefinition, "type" | "label"> & {
|
|
7
|
+
label?: string;
|
|
8
|
+
};
|
|
9
|
+
declare const fields: {
|
|
10
|
+
singleLine: (opts?: FieldControl) => FieldDefinition;
|
|
11
|
+
multiLine: (opts?: FieldControl) => FieldDefinition;
|
|
12
|
+
richText: (opts?: FieldControl) => FieldDefinition;
|
|
13
|
+
numeric: (opts?: FieldControl) => FieldDefinition;
|
|
14
|
+
date: (opts?: FieldControl) => FieldDefinition;
|
|
15
|
+
media: (opts?: FieldControl) => FieldDefinition;
|
|
16
|
+
link: (opts?: FieldControl) => FieldDefinition;
|
|
17
|
+
select: (opts?: FieldControl) => FieldDefinition;
|
|
18
|
+
multiselect: (opts?: FieldControl) => FieldDefinition;
|
|
19
|
+
boolean: (opts?: FieldControl) => FieldDefinition;
|
|
20
|
+
color: (opts?: FieldControl) => FieldDefinition;
|
|
21
|
+
repeater: (opts?: FieldControl) => FieldDefinition;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
interface CmssyServerPageProps {
|
|
25
|
+
page: CmssyPageData | null;
|
|
26
|
+
blocks: BlockDefinition[];
|
|
27
|
+
locale?: string;
|
|
28
|
+
defaultLocale?: string;
|
|
29
|
+
}
|
|
30
|
+
declare function CmssyServerPage({ page, blocks, locale, defaultLocale, }: CmssyServerPageProps): react_jsx_runtime.JSX.Element | null;
|
|
31
|
+
|
|
32
|
+
interface CmssyServerLayoutProps {
|
|
33
|
+
groups: CmssyLayoutGroup[];
|
|
34
|
+
blocks: BlockDefinition[];
|
|
35
|
+
position: string;
|
|
36
|
+
locale?: string;
|
|
37
|
+
defaultLocale?: string;
|
|
38
|
+
}
|
|
39
|
+
declare function CmssyServerLayout({ groups, blocks, position, locale, defaultLocale, }: CmssyServerLayoutProps): react_jsx_runtime.JSX.Element | null;
|
|
40
|
+
|
|
41
|
+
interface PostTarget {
|
|
42
|
+
postMessage: (message: unknown, targetOrigin: string) => void;
|
|
43
|
+
}
|
|
44
|
+
declare function normalizeOrigin(origin: string): string;
|
|
45
|
+
declare function postToEditor(target: PostTarget, editorOrigin: string, message: AppToEditorMessage): void;
|
|
46
|
+
declare function parseEditorMessage(data: unknown, origin: string, expectedOrigin: string): EditorToAppMessage | null;
|
|
47
|
+
|
|
48
|
+
declare function getBlockContentForLanguage(content: unknown, locale: string, defaultLocale?: string, availableLocales?: string[]): Record<string, unknown>;
|
|
49
|
+
|
|
50
|
+
interface GraphqlRequestOptions {
|
|
51
|
+
fetch?: FetchLike;
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
}
|
|
55
|
+
declare function graphqlRequest<T>(config: CmssyClientConfig, query: string, variables: Record<string, unknown>, options?: GraphqlRequestOptions, label?: string): Promise<T>;
|
|
56
|
+
|
|
57
|
+
interface QueryScopedOptions extends GraphqlRequestOptions {
|
|
58
|
+
workspaceId?: string;
|
|
59
|
+
}
|
|
60
|
+
interface CmssyClient {
|
|
61
|
+
readonly config: CmssyClientConfig;
|
|
62
|
+
query<T = unknown>(document: string, variables?: Record<string, unknown>, options?: GraphqlRequestOptions): Promise<T>;
|
|
63
|
+
queryScoped<T = unknown>(document: string, variables?: Record<string, unknown>, options?: QueryScopedOptions): Promise<T>;
|
|
64
|
+
resolveWorkspaceId(options?: GraphqlRequestOptions): Promise<string>;
|
|
65
|
+
}
|
|
66
|
+
declare function createCmssyClient(config: CmssyClientConfig): CmssyClient;
|
|
67
|
+
|
|
68
|
+
interface CmssySiteConfig {
|
|
69
|
+
id: string;
|
|
70
|
+
workspaceId: string;
|
|
71
|
+
siteName: unknown;
|
|
72
|
+
defaultLanguage: string | null;
|
|
73
|
+
enabledLanguages: string[];
|
|
74
|
+
enabledFeatures: string[];
|
|
75
|
+
notFoundPageId: string | null;
|
|
76
|
+
previewUrl: string | null;
|
|
77
|
+
}
|
|
78
|
+
declare const SITE_CONFIG_QUERY = "query PublicSiteConfig($workspaceSlug: String!) {\n publicSiteConfig(workspaceSlug: $workspaceSlug) {\n id\n workspaceId\n siteName\n defaultLanguage\n enabledLanguages\n enabledFeatures\n notFoundPageId\n previewUrl\n }\n}";
|
|
79
|
+
interface CmssyModelDefinition {
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
slug: string;
|
|
83
|
+
description: string | null;
|
|
84
|
+
icon: string | null;
|
|
85
|
+
color: string | null;
|
|
86
|
+
displayField: string | null;
|
|
87
|
+
recordCount: number | null;
|
|
88
|
+
}
|
|
89
|
+
interface CmssyModelRecord {
|
|
90
|
+
id: string;
|
|
91
|
+
modelId: string;
|
|
92
|
+
data: Record<string, unknown>;
|
|
93
|
+
status: string | null;
|
|
94
|
+
createdAt: string | null;
|
|
95
|
+
updatedAt: string | null;
|
|
96
|
+
}
|
|
97
|
+
interface CmssyRecordList {
|
|
98
|
+
items: CmssyModelRecord[];
|
|
99
|
+
total: number;
|
|
100
|
+
hasMore: boolean;
|
|
101
|
+
}
|
|
102
|
+
declare const MODEL_DEFINITIONS_QUERY = "query PublicModelDefinitions($workspaceId: String!) {\n publicModelDefinitions(workspaceId: $workspaceId) {\n id name slug description icon color displayField recordCount\n }\n}";
|
|
103
|
+
declare const MODEL_RECORDS_QUERY = "query PublicModelRecords($workspaceId: String!, $modelSlug: String!, $filter: JSON, $sort: String, $limit: Int, $offset: Int, $populate: [String!]) {\n publicModelRecords(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, sort: $sort, limit: $limit, offset: $offset, populate: $populate) {\n items { id modelId data status createdAt updatedAt }\n total\n hasMore\n }\n}";
|
|
104
|
+
interface CmssyFormField {
|
|
105
|
+
id: string;
|
|
106
|
+
name: string;
|
|
107
|
+
fieldType: string;
|
|
108
|
+
label: string | null;
|
|
109
|
+
placeholder: string | null;
|
|
110
|
+
helpText: string | null;
|
|
111
|
+
defaultValue: unknown;
|
|
112
|
+
options: unknown;
|
|
113
|
+
validation: unknown;
|
|
114
|
+
width: string | null;
|
|
115
|
+
order: number | null;
|
|
116
|
+
showIf: unknown;
|
|
117
|
+
}
|
|
118
|
+
interface CmssyFormSettings {
|
|
119
|
+
actionType: string | null;
|
|
120
|
+
submitButtonLabel: unknown;
|
|
121
|
+
successMessage: unknown;
|
|
122
|
+
errorMessage: unknown;
|
|
123
|
+
redirectUrl: string | null;
|
|
124
|
+
requireLogin: boolean | null;
|
|
125
|
+
enableCaptcha: boolean | null;
|
|
126
|
+
}
|
|
127
|
+
interface CmssyFormDefinition {
|
|
128
|
+
id: string;
|
|
129
|
+
name: string;
|
|
130
|
+
slug: string | null;
|
|
131
|
+
description: string | null;
|
|
132
|
+
fields: CmssyFormField[];
|
|
133
|
+
settings: CmssyFormSettings | null;
|
|
134
|
+
}
|
|
135
|
+
interface CmssyFormSubmitResponse {
|
|
136
|
+
success: boolean;
|
|
137
|
+
message: string | null;
|
|
138
|
+
submissionId: string | null;
|
|
139
|
+
redirectUrl: string | null;
|
|
140
|
+
accessToken: string | null;
|
|
141
|
+
customer: unknown;
|
|
142
|
+
}
|
|
143
|
+
interface SubmitFormInput {
|
|
144
|
+
data: Record<string, unknown>;
|
|
145
|
+
website?: string;
|
|
146
|
+
}
|
|
147
|
+
declare const FORM_QUERY = "query PublicForm($formId: ID!) {\n publicForm(formId: $formId) {\n id\n name\n slug\n description\n fields {\n id name fieldType label placeholder helpText\n defaultValue options validation width order showIf\n }\n settings {\n actionType submitButtonLabel successMessage errorMessage\n redirectUrl requireLogin enableCaptcha\n }\n }\n}";
|
|
148
|
+
declare const SUBMIT_FORM_MUTATION = "mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {\n submitForm(formId: $formId, input: $input) {\n success message submissionId redirectUrl accessToken customer\n }\n}";
|
|
149
|
+
|
|
150
|
+
interface CmssyBlockProps {
|
|
151
|
+
block: RawBlock;
|
|
152
|
+
locale: string;
|
|
153
|
+
defaultLocale: string;
|
|
154
|
+
blockMap: BlockMap;
|
|
155
|
+
patchedContent?: Record<string, unknown>;
|
|
156
|
+
editable?: boolean;
|
|
157
|
+
layoutPosition?: string;
|
|
158
|
+
}
|
|
159
|
+
declare function CmssyBlock({ block, locale, defaultLocale, blockMap, patchedContent, editable, layoutPosition, }: CmssyBlockProps): react_jsx_runtime.JSX.Element;
|
|
160
|
+
|
|
161
|
+
interface UnknownBlockProps {
|
|
162
|
+
type: string;
|
|
163
|
+
}
|
|
164
|
+
declare function UnknownBlock({ type }: UnknownBlockProps): react_jsx_runtime.JSX.Element;
|
|
165
|
+
|
|
166
|
+
export { AppToEditorMessage, BlockDefinition, BlockMap, CmssyBlock, type CmssyBlockProps, type CmssyClient, CmssyClientConfig, type CmssyFormDefinition, type CmssyFormField, type CmssyFormSettings, type CmssyFormSubmitResponse, CmssyLayoutGroup, type CmssyModelDefinition, type CmssyModelRecord, CmssyPageData, type CmssyRecordList, CmssyServerLayout, type CmssyServerLayoutProps, CmssyServerPage, type CmssyServerPageProps, type CmssySiteConfig, EditorToAppMessage, FORM_QUERY, FetchLike, type FieldControl, FieldDefinition, type GraphqlRequestOptions, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, type PostTarget, type QueryScopedOptions, RawBlock, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SubmitFormInput, UnknownBlock, type UnknownBlockProps, createCmssyClient, fields, getBlockContentForLanguage, graphqlRequest, normalizeOrigin, parseEditorMessage, postToEditor };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
|
|
4
|
+
// src/fields.ts
|
|
5
|
+
function control(type) {
|
|
6
|
+
return (opts = {}) => ({
|
|
7
|
+
type,
|
|
8
|
+
label: opts.label ?? "",
|
|
9
|
+
...opts
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
var fields = {
|
|
13
|
+
singleLine: control("singleLine"),
|
|
14
|
+
multiLine: control("multiLine"),
|
|
15
|
+
richText: control("richText"),
|
|
16
|
+
numeric: control("numeric"),
|
|
17
|
+
date: control("date"),
|
|
18
|
+
media: control("media"),
|
|
19
|
+
link: control("link"),
|
|
20
|
+
select: control("select"),
|
|
21
|
+
multiselect: control("multiselect"),
|
|
22
|
+
boolean: control("boolean"),
|
|
23
|
+
color: control("color"),
|
|
24
|
+
repeater: control("repeater")
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/registry.ts
|
|
28
|
+
function defineBlock(def) {
|
|
29
|
+
return def;
|
|
30
|
+
}
|
|
31
|
+
function buildBlockMap(blocks) {
|
|
32
|
+
const map = /* @__PURE__ */ Object.create(null);
|
|
33
|
+
for (const block of blocks) map[block.type] = block.component;
|
|
34
|
+
return map;
|
|
35
|
+
}
|
|
36
|
+
function blocksToSchemas(blocks) {
|
|
37
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
38
|
+
for (const block of blocks) {
|
|
39
|
+
const schema = {};
|
|
40
|
+
for (const [key, def] of Object.entries(block.props)) {
|
|
41
|
+
schema[key] = { ...def, label: def.label || key };
|
|
42
|
+
}
|
|
43
|
+
out[block.type] = schema;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function blocksToMeta(blocks, defaults = {}) {
|
|
48
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
49
|
+
for (const block of blocks) {
|
|
50
|
+
const category = block.category ?? defaults.category;
|
|
51
|
+
out[block.type] = {
|
|
52
|
+
label: block.label ?? block.type,
|
|
53
|
+
...category ? { category } : {},
|
|
54
|
+
...block.icon ? { icon: block.icon } : {},
|
|
55
|
+
...block.layoutPositions ? { layoutPositions: block.layoutPositions } : {}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/content/get-block-content.ts
|
|
62
|
+
function isPlainObject(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
function looksLikeLocaleKey(key) {
|
|
66
|
+
return /^[a-z]{2}(-[A-Za-z]{2})?$/.test(key);
|
|
67
|
+
}
|
|
68
|
+
function getBlockContentForLanguage(content, locale, defaultLocale = "en", availableLocales) {
|
|
69
|
+
if (!isPlainObject(content)) return {};
|
|
70
|
+
const isLocale = availableLocales ? (key) => availableLocales.includes(key) : looksLikeLocaleKey;
|
|
71
|
+
const localeEntries = Object.entries(content).filter(
|
|
72
|
+
([key, value]) => isLocale(key) && isPlainObject(value)
|
|
73
|
+
);
|
|
74
|
+
if (localeEntries.length === 0) return { ...content };
|
|
75
|
+
const localeMap = Object.fromEntries(localeEntries);
|
|
76
|
+
const nonTranslatable = {};
|
|
77
|
+
for (const [key, value] of Object.entries(content)) {
|
|
78
|
+
if (!(isLocale(key) && isPlainObject(value))) nonTranslatable[key] = value;
|
|
79
|
+
}
|
|
80
|
+
const fallbackKey = Object.keys(localeMap)[0];
|
|
81
|
+
const chosen = localeMap[locale] ?? localeMap[defaultLocale] ?? localeMap[fallbackKey];
|
|
82
|
+
return { ...nonTranslatable, ...chosen };
|
|
83
|
+
}
|
|
84
|
+
var WARN_CAP = 256;
|
|
85
|
+
var warned = /* @__PURE__ */ new Set();
|
|
86
|
+
function UnknownBlock({ type }) {
|
|
87
|
+
if (typeof window !== "undefined" && !warned.has(type)) {
|
|
88
|
+
if (warned.size >= WARN_CAP) warned.clear();
|
|
89
|
+
warned.add(type);
|
|
90
|
+
console.warn(`[cmssy] no component registered for block type "${type}"`);
|
|
91
|
+
}
|
|
92
|
+
return /* @__PURE__ */ jsx("div", { "data-cmssy-unknown-block": type });
|
|
93
|
+
}
|
|
94
|
+
function renderResolvedBlock(block, map, locale, defaultLocale) {
|
|
95
|
+
const Component = map[block.type];
|
|
96
|
+
const content = getBlockContentForLanguage(
|
|
97
|
+
block.content,
|
|
98
|
+
locale,
|
|
99
|
+
defaultLocale
|
|
100
|
+
);
|
|
101
|
+
return /* @__PURE__ */ jsx(
|
|
102
|
+
"div",
|
|
103
|
+
{
|
|
104
|
+
"data-block-id": block.id,
|
|
105
|
+
"data-block-type": block.type,
|
|
106
|
+
style: Component ? void 0 : { display: "none" },
|
|
107
|
+
children: Component ? /* @__PURE__ */ jsx(Component, { content }) : /* @__PURE__ */ jsx(UnknownBlock, { type: block.type })
|
|
108
|
+
},
|
|
109
|
+
block.id
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function CmssyServerPage({
|
|
113
|
+
page,
|
|
114
|
+
blocks,
|
|
115
|
+
locale = "en",
|
|
116
|
+
defaultLocale = "en"
|
|
117
|
+
}) {
|
|
118
|
+
if (!page) return null;
|
|
119
|
+
const map = buildBlockMap(blocks);
|
|
120
|
+
return /* @__PURE__ */ jsx(Fragment, { children: page.blocks.map(
|
|
121
|
+
(block) => renderResolvedBlock(block, map, locale, defaultLocale)
|
|
122
|
+
) });
|
|
123
|
+
}
|
|
124
|
+
function CmssyServerLayout({
|
|
125
|
+
groups,
|
|
126
|
+
blocks,
|
|
127
|
+
position,
|
|
128
|
+
locale = "en",
|
|
129
|
+
defaultLocale = "en"
|
|
130
|
+
}) {
|
|
131
|
+
const group = groups.find((g) => g.position === position);
|
|
132
|
+
const layoutBlocks = group ? group.blocks.filter((b) => b.isActive).slice().sort((a, b) => a.order - b.order) : [];
|
|
133
|
+
if (layoutBlocks.length === 0) return null;
|
|
134
|
+
const map = buildBlockMap(blocks);
|
|
135
|
+
return /* @__PURE__ */ jsx(Fragment, { children: layoutBlocks.map(
|
|
136
|
+
(block) => renderResolvedBlock(block, map, locale, defaultLocale)
|
|
137
|
+
) });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/bridge/protocol.ts
|
|
141
|
+
var PROTOCOL_VERSION = 1;
|
|
142
|
+
function isProtocolCompatible(version) {
|
|
143
|
+
return version === PROTOCOL_VERSION;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/bridge/messages.ts
|
|
147
|
+
function normalizeOrigin(origin) {
|
|
148
|
+
if (origin === "*") return "*";
|
|
149
|
+
try {
|
|
150
|
+
return new URL(origin).origin;
|
|
151
|
+
} catch {
|
|
152
|
+
return origin;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function postToEditor(target, editorOrigin, message) {
|
|
156
|
+
target.postMessage(message, normalizeOrigin(editorOrigin));
|
|
157
|
+
}
|
|
158
|
+
function isObject(value) {
|
|
159
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
160
|
+
}
|
|
161
|
+
function parseEditorMessage(data, origin, expectedOrigin) {
|
|
162
|
+
const expected = normalizeOrigin(expectedOrigin);
|
|
163
|
+
if (expected !== "*" && origin !== expected) return null;
|
|
164
|
+
if (!isObject(data)) return null;
|
|
165
|
+
switch (data.type) {
|
|
166
|
+
case "cmssy:select":
|
|
167
|
+
return typeof data.blockId === "string" && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
168
|
+
type: "cmssy:select",
|
|
169
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
170
|
+
blockId: data.blockId
|
|
171
|
+
} : null;
|
|
172
|
+
case "cmssy:patch":
|
|
173
|
+
return typeof data.blockId === "string" && isObject(data.content) && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
174
|
+
type: "cmssy:patch",
|
|
175
|
+
blockId: data.blockId,
|
|
176
|
+
content: data.content,
|
|
177
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
178
|
+
...typeof data.layoutPosition === "string" ? { layoutPosition: data.layoutPosition } : {}
|
|
179
|
+
} : null;
|
|
180
|
+
case "cmssy:parent-ready":
|
|
181
|
+
return data.protocolVersion === PROTOCOL_VERSION ? { type: "cmssy:parent-ready", protocolVersion: PROTOCOL_VERSION } : null;
|
|
182
|
+
case "cmssy:insert":
|
|
183
|
+
return typeof data.blockId === "string" && typeof data.blockType === "string" && isObject(data.content) && typeof data.index === "number" && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
184
|
+
type: "cmssy:insert",
|
|
185
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
186
|
+
blockId: data.blockId,
|
|
187
|
+
blockType: data.blockType,
|
|
188
|
+
content: data.content,
|
|
189
|
+
index: data.index
|
|
190
|
+
} : null;
|
|
191
|
+
case "cmssy:reorder":
|
|
192
|
+
return Array.isArray(data.blockIds) && data.blockIds.every((id) => typeof id === "string") && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
193
|
+
type: "cmssy:reorder",
|
|
194
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
195
|
+
blockIds: data.blockIds
|
|
196
|
+
} : null;
|
|
197
|
+
case "cmssy:remove":
|
|
198
|
+
return typeof data.blockId === "string" && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
199
|
+
type: "cmssy:remove",
|
|
200
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
201
|
+
blockId: data.blockId
|
|
202
|
+
} : null;
|
|
203
|
+
case "cmssy:drag-over":
|
|
204
|
+
return typeof data.y === "number" && data.protocolVersion === PROTOCOL_VERSION ? {
|
|
205
|
+
type: "cmssy:drag-over",
|
|
206
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
207
|
+
y: data.y
|
|
208
|
+
} : null;
|
|
209
|
+
case "cmssy:drag-end":
|
|
210
|
+
return data.protocolVersion === PROTOCOL_VERSION ? { type: "cmssy:drag-end", protocolVersion: PROTOCOL_VERSION } : null;
|
|
211
|
+
default:
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/content/content-client.ts
|
|
217
|
+
var PUBLIC_PAGE_QUERY = `query PublicPage($workspaceSlug: String!, $slug: String!, $previewSecret: String) {
|
|
218
|
+
publicPage(workspaceSlug: $workspaceSlug, slug: $slug, previewSecret: $previewSecret) {
|
|
219
|
+
id
|
|
220
|
+
blocks { id type content }
|
|
221
|
+
publishedBlocks { id type content }
|
|
222
|
+
}
|
|
223
|
+
}`;
|
|
224
|
+
var PUBLIC_PAGE_LAYOUTS_QUERY = `query PublicPageLayouts($workspaceSlug: String!, $pageSlug: String!, $previewSecret: String) {
|
|
225
|
+
publicPageLayouts(workspaceSlug: $workspaceSlug, pageSlug: $pageSlug, previewSecret: $previewSecret) {
|
|
226
|
+
position
|
|
227
|
+
blocks { id type content order isActive }
|
|
228
|
+
}
|
|
229
|
+
}`;
|
|
230
|
+
function normalizeSlug(path) {
|
|
231
|
+
if (Array.isArray(path)) {
|
|
232
|
+
const joined = path.filter(Boolean).join("/");
|
|
233
|
+
return joined ? `/${joined}` : "/";
|
|
234
|
+
}
|
|
235
|
+
if (!path || path === "/") return "/";
|
|
236
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
237
|
+
}
|
|
238
|
+
async function fetchPage(config, path, options = {}) {
|
|
239
|
+
const slug = normalizeSlug(path);
|
|
240
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
241
|
+
if (typeof doFetch !== "function") {
|
|
242
|
+
throw new Error(
|
|
243
|
+
"cmssy: no fetch implementation available - pass options.fetch"
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
const trimmedSecret = options.previewSecret?.trim();
|
|
247
|
+
const previewSecret = trimmedSecret ? trimmedSecret : null;
|
|
248
|
+
const response = await doFetch(config.apiUrl, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: { "content-type": "application/json" },
|
|
251
|
+
body: JSON.stringify({
|
|
252
|
+
query: PUBLIC_PAGE_QUERY,
|
|
253
|
+
variables: {
|
|
254
|
+
workspaceSlug: config.workspaceSlug,
|
|
255
|
+
slug,
|
|
256
|
+
previewSecret
|
|
257
|
+
}
|
|
258
|
+
}),
|
|
259
|
+
signal: options.signal
|
|
260
|
+
});
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
let detail = "";
|
|
263
|
+
try {
|
|
264
|
+
const body = await response.json();
|
|
265
|
+
if (body.errors && body.errors.length > 0) {
|
|
266
|
+
detail = ` - ${body.errors.map((error) => error.message ?? "GraphQL error").join("; ")}`;
|
|
267
|
+
}
|
|
268
|
+
} catch {
|
|
269
|
+
detail = "";
|
|
270
|
+
}
|
|
271
|
+
throw new Error(`cmssy: page fetch failed (${response.status})${detail}`);
|
|
272
|
+
}
|
|
273
|
+
let json;
|
|
274
|
+
try {
|
|
275
|
+
json = await response.json();
|
|
276
|
+
} catch {
|
|
277
|
+
throw new Error("cmssy: invalid JSON response from the page query");
|
|
278
|
+
}
|
|
279
|
+
if (json.errors && json.errors.length > 0) {
|
|
280
|
+
const message = json.errors.map((error) => error.message ?? "GraphQL error").join("; ");
|
|
281
|
+
throw new Error(`cmssy: page fetch error - ${message}`);
|
|
282
|
+
}
|
|
283
|
+
const page = json.data?.publicPage;
|
|
284
|
+
if (!page) return null;
|
|
285
|
+
const draft = previewSecret !== null;
|
|
286
|
+
const blocks = (draft ? page.blocks : page.publishedBlocks) ?? [];
|
|
287
|
+
return { id: page.id, blocks };
|
|
288
|
+
}
|
|
289
|
+
async function fetchLayouts(config, path, options = {}) {
|
|
290
|
+
const pageSlug = normalizeSlug(path);
|
|
291
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
292
|
+
if (typeof doFetch !== "function") {
|
|
293
|
+
throw new Error(
|
|
294
|
+
"cmssy: no fetch implementation available - pass options.fetch"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const trimmedSecret = options.previewSecret?.trim();
|
|
298
|
+
const previewSecret = trimmedSecret ? trimmedSecret : null;
|
|
299
|
+
const response = await doFetch(config.apiUrl, {
|
|
300
|
+
method: "POST",
|
|
301
|
+
headers: { "content-type": "application/json" },
|
|
302
|
+
body: JSON.stringify({
|
|
303
|
+
query: PUBLIC_PAGE_LAYOUTS_QUERY,
|
|
304
|
+
variables: {
|
|
305
|
+
workspaceSlug: config.workspaceSlug,
|
|
306
|
+
pageSlug,
|
|
307
|
+
previewSecret
|
|
308
|
+
}
|
|
309
|
+
}),
|
|
310
|
+
signal: options.signal
|
|
311
|
+
});
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
throw new Error(`cmssy: layouts fetch failed (${response.status})`);
|
|
314
|
+
}
|
|
315
|
+
let json;
|
|
316
|
+
try {
|
|
317
|
+
json = await response.json();
|
|
318
|
+
} catch {
|
|
319
|
+
throw new Error("cmssy: invalid JSON response from the layouts query");
|
|
320
|
+
}
|
|
321
|
+
if (json.errors && json.errors.length > 0) {
|
|
322
|
+
const message = json.errors.map((error) => error.message ?? "GraphQL error").join("; ");
|
|
323
|
+
throw new Error(`cmssy: layouts fetch error - ${message}`);
|
|
324
|
+
}
|
|
325
|
+
return json.data?.publicPageLayouts ?? [];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/data/graphql-request.ts
|
|
329
|
+
async function graphqlRequest(config, query, variables, options = {}, label = "request") {
|
|
330
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
331
|
+
if (typeof doFetch !== "function") {
|
|
332
|
+
throw new Error(
|
|
333
|
+
"cmssy: no fetch implementation available - pass options.fetch"
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
const response = await doFetch(config.apiUrl, {
|
|
337
|
+
method: "POST",
|
|
338
|
+
headers: { "content-type": "application/json", ...options.headers },
|
|
339
|
+
body: JSON.stringify({ query, variables }),
|
|
340
|
+
signal: options.signal
|
|
341
|
+
});
|
|
342
|
+
if (!response.ok) {
|
|
343
|
+
let detail = "";
|
|
344
|
+
try {
|
|
345
|
+
const body = await response.json();
|
|
346
|
+
if (body.errors && body.errors.length > 0) {
|
|
347
|
+
detail = ` - ${body.errors.map((error) => error.message ?? "GraphQL error").join("; ")}`;
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
detail = "";
|
|
351
|
+
}
|
|
352
|
+
throw new Error(`cmssy: ${label} failed (${response.status})${detail}`);
|
|
353
|
+
}
|
|
354
|
+
let json;
|
|
355
|
+
try {
|
|
356
|
+
json = await response.json();
|
|
357
|
+
} catch {
|
|
358
|
+
throw new Error(`cmssy: invalid JSON response from the ${label}`);
|
|
359
|
+
}
|
|
360
|
+
if (json.errors && json.errors.length > 0) {
|
|
361
|
+
const message = json.errors.map((error) => error.message ?? "GraphQL error").join("; ");
|
|
362
|
+
throw new Error(`cmssy: ${label} error - ${message}`);
|
|
363
|
+
}
|
|
364
|
+
return json.data;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/data/queries.ts
|
|
368
|
+
var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
|
|
369
|
+
publicSiteConfig(workspaceSlug: $workspaceSlug) {
|
|
370
|
+
id
|
|
371
|
+
workspaceId
|
|
372
|
+
siteName
|
|
373
|
+
defaultLanguage
|
|
374
|
+
enabledLanguages
|
|
375
|
+
enabledFeatures
|
|
376
|
+
notFoundPageId
|
|
377
|
+
previewUrl
|
|
378
|
+
}
|
|
379
|
+
}`;
|
|
380
|
+
var MODEL_DEFINITIONS_QUERY = `query PublicModelDefinitions($workspaceId: String!) {
|
|
381
|
+
publicModelDefinitions(workspaceId: $workspaceId) {
|
|
382
|
+
id name slug description icon color displayField recordCount
|
|
383
|
+
}
|
|
384
|
+
}`;
|
|
385
|
+
var MODEL_RECORDS_QUERY = `query PublicModelRecords($workspaceId: String!, $modelSlug: String!, $filter: JSON, $sort: String, $limit: Int, $offset: Int, $populate: [String!]) {
|
|
386
|
+
publicModelRecords(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, sort: $sort, limit: $limit, offset: $offset, populate: $populate) {
|
|
387
|
+
items { id modelId data status createdAt updatedAt }
|
|
388
|
+
total
|
|
389
|
+
hasMore
|
|
390
|
+
}
|
|
391
|
+
}`;
|
|
392
|
+
var FORM_QUERY = `query PublicForm($formId: ID!) {
|
|
393
|
+
publicForm(formId: $formId) {
|
|
394
|
+
id
|
|
395
|
+
name
|
|
396
|
+
slug
|
|
397
|
+
description
|
|
398
|
+
fields {
|
|
399
|
+
id name fieldType label placeholder helpText
|
|
400
|
+
defaultValue options validation width order showIf
|
|
401
|
+
}
|
|
402
|
+
settings {
|
|
403
|
+
actionType submitButtonLabel successMessage errorMessage
|
|
404
|
+
redirectUrl requireLogin enableCaptcha
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}`;
|
|
408
|
+
var SUBMIT_FORM_MUTATION = `mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
|
|
409
|
+
submitForm(formId: $formId, input: $input) {
|
|
410
|
+
success message submissionId redirectUrl accessToken customer
|
|
411
|
+
}
|
|
412
|
+
}`;
|
|
413
|
+
|
|
414
|
+
// src/data/settings-client.ts
|
|
415
|
+
async function fetchSiteConfig(config, options = {}) {
|
|
416
|
+
const data = await graphqlRequest(
|
|
417
|
+
config,
|
|
418
|
+
SITE_CONFIG_QUERY,
|
|
419
|
+
{ workspaceSlug: config.workspaceSlug },
|
|
420
|
+
options,
|
|
421
|
+
"site config query"
|
|
422
|
+
);
|
|
423
|
+
return data.publicSiteConfig ?? null;
|
|
424
|
+
}
|
|
425
|
+
async function resolveWorkspaceId(config, options = {}) {
|
|
426
|
+
const siteConfig = await fetchSiteConfig(config, options);
|
|
427
|
+
if (!siteConfig?.workspaceId) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
`cmssy: could not resolve workspaceId for "${config.workspaceSlug}"`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return siteConfig.workspaceId;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/data/client.ts
|
|
436
|
+
function createCmssyClient(config) {
|
|
437
|
+
let cachedWorkspaceId;
|
|
438
|
+
let inFlight;
|
|
439
|
+
function resolveWorkspaceId2(options) {
|
|
440
|
+
if (cachedWorkspaceId) return Promise.resolve(cachedWorkspaceId);
|
|
441
|
+
if (!inFlight) {
|
|
442
|
+
inFlight = resolveWorkspaceId(config, options).then((id) => {
|
|
443
|
+
cachedWorkspaceId = id;
|
|
444
|
+
return id;
|
|
445
|
+
}).finally(() => {
|
|
446
|
+
inFlight = void 0;
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
return inFlight;
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
config,
|
|
453
|
+
resolveWorkspaceId: resolveWorkspaceId2,
|
|
454
|
+
query(document, variables = {}, options) {
|
|
455
|
+
return graphqlRequest(
|
|
456
|
+
config,
|
|
457
|
+
document,
|
|
458
|
+
variables,
|
|
459
|
+
options,
|
|
460
|
+
"graphql operation"
|
|
461
|
+
);
|
|
462
|
+
},
|
|
463
|
+
async queryScoped(document, variables = {}, options = {}) {
|
|
464
|
+
const { workspaceId: provided, headers, ...rest } = options;
|
|
465
|
+
const workspaceId = provided ?? await resolveWorkspaceId2({ ...rest, headers });
|
|
466
|
+
const hasWorkspaceId = variables.workspaceId !== void 0 && variables.workspaceId !== null;
|
|
467
|
+
const scopedVariables = /\$workspaceId\b/.test(document) && !hasWorkspaceId ? { ...variables, workspaceId } : variables;
|
|
468
|
+
return graphqlRequest(
|
|
469
|
+
config,
|
|
470
|
+
document,
|
|
471
|
+
scopedVariables,
|
|
472
|
+
{ ...rest, headers: { ...headers, "x-workspace-id": workspaceId } },
|
|
473
|
+
"graphql operation"
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function CmssyBlock({
|
|
479
|
+
block,
|
|
480
|
+
locale,
|
|
481
|
+
defaultLocale,
|
|
482
|
+
blockMap,
|
|
483
|
+
patchedContent,
|
|
484
|
+
editable,
|
|
485
|
+
layoutPosition
|
|
486
|
+
}) {
|
|
487
|
+
const Component = Object.hasOwn(blockMap, block.type) ? blockMap[block.type] : void 0;
|
|
488
|
+
const base = getBlockContentForLanguage(block.content, locale, defaultLocale);
|
|
489
|
+
const content = patchedContent ? { ...base, ...patchedContent } : base;
|
|
490
|
+
return /* @__PURE__ */ jsx(
|
|
491
|
+
"div",
|
|
492
|
+
{
|
|
493
|
+
"data-block-id": block.id,
|
|
494
|
+
"data-block-type": block.type,
|
|
495
|
+
"data-layout-position": layoutPosition,
|
|
496
|
+
draggable: editable || void 0,
|
|
497
|
+
style: Component ? void 0 : { display: "none" },
|
|
498
|
+
children: Component ? createElement(Component, { content }) : /* @__PURE__ */ jsx(UnknownBlock, { type: block.type })
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export { CmssyBlock, CmssyServerLayout, CmssyServerPage, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PROTOCOL_VERSION, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, UnknownBlock, blocksToMeta, blocksToSchemas, buildBlockMap, createCmssyClient, defineBlock, fetchLayouts, fetchPage, fields, getBlockContentForLanguage, graphqlRequest, isProtocolCompatible, normalizeOrigin, normalizeSlug, parseEditorMessage, postToEditor };
|