@getquick/content 0.3.1 → 0.4.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/README.md +87 -0
- package/dist/consent-astro.js +2 -2
- package/dist/consent-client.js +19 -6
- package/dist/consent-server.js +4 -37
- package/dist/consent.js +2 -2
- package/dist/copy-DLto_bWj.js +163 -0
- package/dist/descriptor-B-e7gHi1.d.ts +143 -0
- package/dist/descriptor-x8kHFQ79.js +205 -0
- package/dist/design-client.d.ts +19 -0
- package/dist/design-client.js +519 -0
- package/dist/{dist-D4-CMkrf.js → dist-5jNKLmbn.js} +198 -3
- package/dist/{embeds-p9SyGNZI.js → embeds-CJJemAmE.js} +2 -2
- package/dist/forms-astro.d.ts +50 -0
- package/dist/forms-astro.js +193 -0
- package/dist/forms-server.d.ts +80 -0
- package/dist/forms-server.js +294 -0
- package/dist/forms-testing.d.ts +66 -0
- package/dist/forms-testing.js +334 -0
- package/dist/forms.d.ts +25 -0
- package/dist/forms.js +4 -0
- package/dist/index.d.ts +131 -12
- package/dist/index.js +9 -2
- package/dist/metadata-content.js +1 -1
- package/dist/metadata-testing.js +1 -1
- package/dist/request-guards-BUPr_vA4.js +46 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +281 -26
- package/dist/site-language-BCGjkpWY.js +169178 -0
- package/dist/state-CsMFLUUO.d.ts +66 -0
- package/dist/validate-wRgYUNxN.js +130 -0
- package/dist/{version-ChwS3fUu.js → version-D81MsCZU.js} +1 -1
- package/dist/video-hero-D8AcMR4q.js +69 -0
- package/dist/{wordpress-D_HG2rEt.d.ts → wordpress-BvkTDMA0.d.ts} +306 -3
- package/package.json +21 -1
- package/dist/site-language-BLA2mmOw.js +0 -565
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
//#region frontend/src/forms/descriptor.ts
|
|
2
|
+
/** The Site path the forms handler answers under: `/gq/forms/<formId>`. */
|
|
3
|
+
const FORMS_PATH = "/gq/forms";
|
|
4
|
+
/** The descriptor's wire version; another version doesn't parse. */
|
|
5
|
+
const FORM_DESCRIPTOR_SCHEMA = 1;
|
|
6
|
+
/** The Gravity Forms block a Site's dispatcher routes to the renderer. */
|
|
7
|
+
const GRAVITY_FORMS_BLOCK = "gravityforms/form";
|
|
8
|
+
/** Field types this release renders and submits. */
|
|
9
|
+
const SUPPORTED_FIELD_TYPES = [
|
|
10
|
+
"text",
|
|
11
|
+
"textarea",
|
|
12
|
+
"email",
|
|
13
|
+
"phone",
|
|
14
|
+
"number",
|
|
15
|
+
"website",
|
|
16
|
+
"select",
|
|
17
|
+
"radio",
|
|
18
|
+
"checkbox",
|
|
19
|
+
"consent",
|
|
20
|
+
"name",
|
|
21
|
+
"hidden",
|
|
22
|
+
"html",
|
|
23
|
+
"section"
|
|
24
|
+
];
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
const text = (value) => typeof value === "string" ? value : "";
|
|
29
|
+
const optionalText = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
30
|
+
const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
31
|
+
const INPUT_NAME = /^input_\d+(?:_\d+)?$/;
|
|
32
|
+
const DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
33
|
+
"unavailable",
|
|
34
|
+
"unsupported-captcha",
|
|
35
|
+
"unsupported-file-upload",
|
|
36
|
+
"unsupported-payment",
|
|
37
|
+
"unsupported-multi-page",
|
|
38
|
+
"unsupported-field",
|
|
39
|
+
"unsupported-conditional-logic",
|
|
40
|
+
"requires-login"
|
|
41
|
+
]);
|
|
42
|
+
function parseInput(value) {
|
|
43
|
+
if (!isRecord(value)) return null;
|
|
44
|
+
const id = text(value.id);
|
|
45
|
+
const name = text(value.name);
|
|
46
|
+
if (!/^\d+(?:\.\d+)?$/.test(id) || !INPUT_NAME.test(name)) return null;
|
|
47
|
+
return {
|
|
48
|
+
id,
|
|
49
|
+
name,
|
|
50
|
+
label: text(value.label),
|
|
51
|
+
...typeof value.value === "string" ? { value: value.value } : {},
|
|
52
|
+
...optionalText(value.placeholder) ? { placeholder: text(value.placeholder) } : {},
|
|
53
|
+
...optionalText(value.autocomplete) ? { autocomplete: text(value.autocomplete) } : {}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseField(value) {
|
|
57
|
+
if (!isRecord(value)) return null;
|
|
58
|
+
const id = value.id;
|
|
59
|
+
const type = value.type;
|
|
60
|
+
if (typeof id !== "number" || !Number.isInteger(id) || id < 1) return null;
|
|
61
|
+
if (!SUPPORTED_FIELD_TYPES.includes(type)) return null;
|
|
62
|
+
const inputs = Array.isArray(value.inputs) ? value.inputs.map(parseInput).filter((input) => input !== null) : [];
|
|
63
|
+
const field = {
|
|
64
|
+
id,
|
|
65
|
+
type,
|
|
66
|
+
label: text(value.label),
|
|
67
|
+
description: text(value.description),
|
|
68
|
+
required: value.required === true,
|
|
69
|
+
visibility: value.visibility === "hidden" ? "hidden" : "visible",
|
|
70
|
+
inputs
|
|
71
|
+
};
|
|
72
|
+
if (optionalText(value.placeholder)) field.placeholder = text(value.placeholder);
|
|
73
|
+
const maxLength = finite(value.maxLength);
|
|
74
|
+
if (maxLength !== void 0 && maxLength > 0) field.maxLength = Math.floor(maxLength);
|
|
75
|
+
if (typeof value.defaultValue === "string") field.defaultValue = value.defaultValue;
|
|
76
|
+
if (optionalText(value.cssClass)) field.cssClass = text(value.cssClass);
|
|
77
|
+
if (Array.isArray(value.choices)) field.choices = value.choices.filter(isRecord).map((choice) => ({
|
|
78
|
+
text: text(choice.text),
|
|
79
|
+
value: text(choice.value),
|
|
80
|
+
selected: choice.selected === true
|
|
81
|
+
}));
|
|
82
|
+
if (isRecord(value.consent)) field.consent = {
|
|
83
|
+
checkboxLabel: text(value.consent.checkboxLabel),
|
|
84
|
+
description: text(value.consent.description),
|
|
85
|
+
revisionId: text(value.consent.revisionId)
|
|
86
|
+
};
|
|
87
|
+
if (typeof value.content === "string") field.content = value.content;
|
|
88
|
+
if (isRecord(value.range)) {
|
|
89
|
+
const min = finite(value.range.min);
|
|
90
|
+
const max = finite(value.range.max);
|
|
91
|
+
field.range = {
|
|
92
|
+
...min !== void 0 ? { min } : {},
|
|
93
|
+
...max !== void 0 ? { max } : {}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return field;
|
|
97
|
+
}
|
|
98
|
+
function parseConfirmation(value) {
|
|
99
|
+
if (isRecord(value) && value.type === "redirect" && typeof value.url === "string") try {
|
|
100
|
+
const url = new URL(value.url);
|
|
101
|
+
if (url.protocol === "https:" || url.protocol === "http:") return {
|
|
102
|
+
type: "redirect",
|
|
103
|
+
url: url.href
|
|
104
|
+
};
|
|
105
|
+
} catch {}
|
|
106
|
+
return {
|
|
107
|
+
type: "message",
|
|
108
|
+
message: isRecord(value) ? text(value.message) : ""
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A descriptor as the CMS delivered it, keeping only the public fields above,
|
|
113
|
+
* or null when it isn't one. Fields of a type this release doesn't support
|
|
114
|
+
* are left out; the CMS reports them as diagnostics.
|
|
115
|
+
*/
|
|
116
|
+
function parseFormDescriptor(value) {
|
|
117
|
+
if (!isRecord(value) || value.schema !== 1) return null;
|
|
118
|
+
const id = value.id;
|
|
119
|
+
if (typeof id !== "number" || !Number.isInteger(id) || id < 1) return null;
|
|
120
|
+
const fields = Array.isArray(value.fields) ? value.fields.map(parseField).filter((field) => field !== null) : [];
|
|
121
|
+
const honeypotName = isRecord(value.honeypot) ? text(value.honeypot.name) : "";
|
|
122
|
+
const diagnostics = Array.isArray(value.diagnostics) ? value.diagnostics.filter(isRecord).flatMap((diagnostic) => {
|
|
123
|
+
const code = diagnostic.code;
|
|
124
|
+
if (!DIAGNOSTIC_CODES.has(code)) return [];
|
|
125
|
+
const fieldId = finite(diagnostic.fieldId);
|
|
126
|
+
return [{
|
|
127
|
+
code,
|
|
128
|
+
...fieldId !== void 0 ? { fieldId } : {},
|
|
129
|
+
...optionalText(diagnostic.type) ? { type: text(diagnostic.type) } : {},
|
|
130
|
+
message: text(diagnostic.message)
|
|
131
|
+
}];
|
|
132
|
+
}) : [];
|
|
133
|
+
return {
|
|
134
|
+
schema: 1,
|
|
135
|
+
id,
|
|
136
|
+
available: value.available === true,
|
|
137
|
+
title: text(value.title),
|
|
138
|
+
description: text(value.description),
|
|
139
|
+
submitLabel: text(value.submitLabel),
|
|
140
|
+
fields,
|
|
141
|
+
honeypot: INPUT_NAME.test(honeypotName) ? { name: honeypotName } : null,
|
|
142
|
+
confirmation: parseConfirmation(value.confirmation),
|
|
143
|
+
diagnostics
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The descriptors a publication carries (the CMS's `getquickForms`, as JSON
|
|
148
|
+
* text or data), dropping any that don't parse. Undefined for no value.
|
|
149
|
+
*/
|
|
150
|
+
function parseFormDescriptors(value) {
|
|
151
|
+
let parsed = value;
|
|
152
|
+
if (typeof parsed === "string") try {
|
|
153
|
+
parsed = JSON.parse(parsed);
|
|
154
|
+
} catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
158
|
+
return parsed.map(parseFormDescriptor).filter((descriptor) => descriptor !== null);
|
|
159
|
+
}
|
|
160
|
+
/** The form a `gravityforms/form` block shows, from its attributes (`formId`). */
|
|
161
|
+
function blockFormId(attributes) {
|
|
162
|
+
const raw = attributes?.formId;
|
|
163
|
+
const id = typeof raw === "string" ? Number(raw) : raw;
|
|
164
|
+
return typeof id === "number" && Number.isInteger(id) && id > 0 ? id : null;
|
|
165
|
+
}
|
|
166
|
+
/** A form's descriptor among a publication's, or null. */
|
|
167
|
+
function findForm(forms, formId) {
|
|
168
|
+
return forms?.find((form) => form.id === formId) ?? null;
|
|
169
|
+
}
|
|
170
|
+
/** The path a form posts to on the Site's own origin. */
|
|
171
|
+
const formAction = (formId) => `${FORMS_PATH}/${formId}`;
|
|
172
|
+
/** Everything readiness must fail for: an unavailable form, and each unsupported field or setting. */
|
|
173
|
+
function formDiagnostics(descriptor) {
|
|
174
|
+
const diagnostics = [...descriptor.diagnostics];
|
|
175
|
+
if (!descriptor.available && !diagnostics.some((each) => each.code === "unavailable")) diagnostics.unshift({
|
|
176
|
+
code: "unavailable",
|
|
177
|
+
message: `Form ${descriptor.id} is missing, inactive or in the trash.`
|
|
178
|
+
});
|
|
179
|
+
return diagnostics;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Readiness over the forms a Site's published content references: fails for
|
|
183
|
+
* any diagnostic. `spam` is the Site's declared controls; declaring the
|
|
184
|
+
* honeypot for a form that doesn't enable it is reported as a warning.
|
|
185
|
+
*/
|
|
186
|
+
function formsReadiness(descriptors, options = {}) {
|
|
187
|
+
const forms = [...new Map(descriptors.map((descriptor) => [descriptor.id, descriptor])).values()].map((descriptor) => {
|
|
188
|
+
const diagnostics = formDiagnostics(descriptor);
|
|
189
|
+
return {
|
|
190
|
+
id: descriptor.id,
|
|
191
|
+
title: descriptor.title,
|
|
192
|
+
ok: diagnostics.length === 0,
|
|
193
|
+
diagnostics,
|
|
194
|
+
honeypot: descriptor.honeypot !== null
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
const warnings = options.spam?.includes("honeypot") ? forms.filter((form) => !form.honeypot).map((form) => `Form ${form.id} doesn't enable Gravity Forms' anti-spam honeypot.`) : [];
|
|
198
|
+
return {
|
|
199
|
+
ok: forms.every((form) => form.ok),
|
|
200
|
+
forms,
|
|
201
|
+
warnings
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { blockFormId as a, formDiagnostics as c, parseFormDescriptors as d, SUPPORTED_FIELD_TYPES as i, formsReadiness as l, FORM_DESCRIPTOR_SCHEMA as n, findForm as o, GRAVITY_FORMS_BLOCK as r, formAction as s, FORMS_PATH as t, parseFormDescriptor as u };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region frontend/src/carousel-client.d.ts
|
|
2
|
+
/** Mount each Carousel once. Sites invoke on page load and call cleanup before
|
|
3
|
+
* client navigation replaces the page. DOM APIs are read only on invocation. */
|
|
4
|
+
export declare function mountDesignCarousels(root?: ParentNode): () => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
/** Enhance only decorative media; nested content keeps its native interaction. */
|
|
7
|
+
export declare function mountVideoHero(hero: HTMLElement): () => void;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region frontend/src/navigation-client.d.ts
|
|
10
|
+
/** Mount the adapter's navigation markup once; cleanup restores server state.
|
|
11
|
+
* Native links retain ordinary Tab order (this is navigation, not an ARIA menu).
|
|
12
|
+
* Install navigationStyleSheet alongside the canonical presentation stylesheet. */
|
|
13
|
+
export declare function mountDesignNavigation(root?: ParentNode): () => void;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region frontend/src/cover-video-client.d.ts
|
|
16
|
+
/** Mount decorative native Cover backgrounds in neutral, saved or delivered HTML.
|
|
17
|
+
* No DOM access occurs until invocation. The Site owns mounting and cleanup. */
|
|
18
|
+
export declare function mountDesignCoverVideos(root?: ParentNode): () => void;
|
|
19
|
+
//#endregion
|