@agntcms/next 0.2.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/assets-Cyt9upqW.d.cts +290 -0
- package/dist/assets-P8OCigDG.d.ts +290 -0
- package/dist/client.cjs +13244 -0
- package/dist/client.d.cts +806 -0
- package/dist/client.d.ts +806 -0
- package/dist/client.mjs +13234 -0
- package/dist/config.cjs +240 -0
- package/dist/config.d.cts +112 -0
- package/dist/config.d.ts +112 -0
- package/dist/config.mjs +194 -0
- package/dist/defineForm-Bp9vzW56.d.ts +71 -0
- package/dist/defineForm-CJ8KZC93.d.cts +71 -0
- package/dist/defineSection-9qQ5ulAH.d.cts +243 -0
- package/dist/defineSection-Kr0pWqMY.d.ts +243 -0
- package/dist/form-BqY0H1V5.d.cts +753 -0
- package/dist/form-BqY0H1V5.d.ts +753 -0
- package/dist/global-CV23g5Bn.d.cts +15 -0
- package/dist/global-CV23g5Bn.d.ts +15 -0
- package/dist/handlers.cjs +2525 -0
- package/dist/handlers.d.cts +330 -0
- package/dist/handlers.d.ts +330 -0
- package/dist/handlers.mjs +2473 -0
- package/dist/index.cjs +372 -0
- package/dist/index.d.cts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.mjs +319 -0
- package/dist/rateLimit-CXptRM_K.d.ts +391 -0
- package/dist/rateLimit-CiROGTLE.d.cts +391 -0
- package/dist/registry-CraTTwT7.d.cts +29 -0
- package/dist/registry-DMujGqt0.d.ts +29 -0
- package/dist/server.cjs +1970 -0
- package/dist/server.d.cts +153 -0
- package/dist/server.d.ts +153 -0
- package/dist/server.mjs +1889 -0
- package/package.json +62 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
BooleanField: () => BooleanField,
|
|
24
|
+
ButtonField: () => ButtonField,
|
|
25
|
+
FormOverridesField: () => FormOverridesField,
|
|
26
|
+
HoneypotCollisionError: () => HoneypotCollisionError,
|
|
27
|
+
ImageField: () => ImageField,
|
|
28
|
+
InvalidFormFieldError: () => InvalidFormFieldError,
|
|
29
|
+
InvalidFormNameError: () => InvalidFormNameError,
|
|
30
|
+
LinkField: () => LinkField,
|
|
31
|
+
ListField: () => ListField,
|
|
32
|
+
NumberField: () => NumberField,
|
|
33
|
+
ReferenceField: () => ReferenceField,
|
|
34
|
+
RichTextField: () => RichTextField,
|
|
35
|
+
SelectField: () => SelectField,
|
|
36
|
+
SubmissionsNotReadableError: () => SubmissionsNotReadableError,
|
|
37
|
+
TextField: () => TextField,
|
|
38
|
+
VideoField: () => VideoField,
|
|
39
|
+
defineForm: () => defineForm,
|
|
40
|
+
defineSection: () => defineSection,
|
|
41
|
+
hasUniqueSectionIds: () => hasUniqueSectionIds,
|
|
42
|
+
hrefOf: () => hrefOf,
|
|
43
|
+
isExternalLink: () => isExternalLink,
|
|
44
|
+
linkAnchorAttrs: () => linkAnchorAttrs,
|
|
45
|
+
normalizeLinkValue: () => normalizeLinkValue,
|
|
46
|
+
validateEmail: () => validateEmail,
|
|
47
|
+
validateExternalUrl: () => validateExternalUrl,
|
|
48
|
+
validateInternalSlug: () => validateInternalSlug,
|
|
49
|
+
validatePhone: () => validatePhone
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(src_exports);
|
|
52
|
+
|
|
53
|
+
// src/domain/fields.ts
|
|
54
|
+
var TextField = { kind: "text" };
|
|
55
|
+
var RichTextField = { kind: "richText" };
|
|
56
|
+
var ImageField = { kind: "image" };
|
|
57
|
+
var VideoField = { kind: "video" };
|
|
58
|
+
var ReferenceField = { kind: "reference" };
|
|
59
|
+
var LinkField = { kind: "link" };
|
|
60
|
+
var NumberField = { kind: "number" };
|
|
61
|
+
var BooleanField = { kind: "boolean" };
|
|
62
|
+
var SelectField = (options, opts) => ({
|
|
63
|
+
kind: "select",
|
|
64
|
+
options,
|
|
65
|
+
...opts?.default !== void 0 ? { default: opts.default } : {}
|
|
66
|
+
});
|
|
67
|
+
var ButtonField = (variants, opts) => ({
|
|
68
|
+
kind: "button",
|
|
69
|
+
variants,
|
|
70
|
+
...opts?.default !== void 0 ? { default: opts.default } : {}
|
|
71
|
+
});
|
|
72
|
+
var ListField = (itemSchema, opts) => ({
|
|
73
|
+
kind: "list",
|
|
74
|
+
itemSchema,
|
|
75
|
+
...opts?.min !== void 0 ? { min: opts.min } : {},
|
|
76
|
+
...opts?.max !== void 0 ? { max: opts.max } : {},
|
|
77
|
+
...opts?.default !== void 0 ? { default: opts.default } : {}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// src/domain/invariants.ts
|
|
81
|
+
function hasUniqueSectionIds(page) {
|
|
82
|
+
const seen = /* @__PURE__ */ new Set();
|
|
83
|
+
for (const section of page.sections) {
|
|
84
|
+
if (seen.has(section.id)) return false;
|
|
85
|
+
seen.add(section.id);
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/domain/linkValidation.ts
|
|
91
|
+
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-/]*[a-z0-9])?$/;
|
|
92
|
+
var validateInternalSlug = (slug) => {
|
|
93
|
+
if (slug === "") return null;
|
|
94
|
+
if (/^https?:\/\//i.test(slug)) return "slug must not include a protocol";
|
|
95
|
+
if (slug.startsWith("//")) return "slug must not start with //";
|
|
96
|
+
if (slug.startsWith("/")) return "slug must not start with /";
|
|
97
|
+
if (slug.endsWith("/")) return "slug must not end with /";
|
|
98
|
+
if (slug.includes("//")) return "slug must not contain //";
|
|
99
|
+
if (slug.includes("..")) return "slug must not contain ..";
|
|
100
|
+
if (!SLUG_PATTERN.test(slug)) {
|
|
101
|
+
return "slug must use lowercase letters, digits, hyphens, and slashes only";
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
var validateExternalUrl = (url) => {
|
|
106
|
+
if (url === "") return null;
|
|
107
|
+
if (typeof url !== "string") return "URL must be a string";
|
|
108
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
109
|
+
return "External link must start with http:// or https://";
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const parsed = new globalThis.URL(url);
|
|
113
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
114
|
+
return "External link must use http or https";
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
return "External link is not a valid URL";
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
122
|
+
var validateEmail = (email) => {
|
|
123
|
+
if (email === "") return null;
|
|
124
|
+
if (typeof email !== "string") return "Invalid email address";
|
|
125
|
+
if (!EMAIL_PATTERN.test(email)) return "Invalid email address";
|
|
126
|
+
return null;
|
|
127
|
+
};
|
|
128
|
+
var validatePhone = (phone) => {
|
|
129
|
+
if (phone === "") return null;
|
|
130
|
+
if (typeof phone !== "string") return "Phone number is too short";
|
|
131
|
+
const digits = phone.replace(/[^\d+]/g, "").replace(/\+/g, "");
|
|
132
|
+
if (digits.length < 7) return "Phone number is too short";
|
|
133
|
+
return null;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/domain/link.ts
|
|
137
|
+
function hrefOf(link) {
|
|
138
|
+
if (link.type === "external") return link.url;
|
|
139
|
+
if (link.type === "email") {
|
|
140
|
+
if (link.email === "") return "";
|
|
141
|
+
return "mailto:" + link.email;
|
|
142
|
+
}
|
|
143
|
+
if (link.type === "phone") {
|
|
144
|
+
if (link.phone === "") return "";
|
|
145
|
+
return "tel:" + link.phone.replace(/[^\d+]/g, "");
|
|
146
|
+
}
|
|
147
|
+
const slug = link.slug.startsWith("/") ? link.slug.slice(1) : link.slug;
|
|
148
|
+
if (slug === "" || slug === "home") return "/";
|
|
149
|
+
return "/" + slug;
|
|
150
|
+
}
|
|
151
|
+
function isExternalLink(link) {
|
|
152
|
+
return link.type === "external";
|
|
153
|
+
}
|
|
154
|
+
function linkAnchorAttrs(link) {
|
|
155
|
+
const href = hrefOf(link);
|
|
156
|
+
if (link.type === "external") {
|
|
157
|
+
return { href, target: "_blank", rel: "noreferrer" };
|
|
158
|
+
}
|
|
159
|
+
return { href };
|
|
160
|
+
}
|
|
161
|
+
function normalizeLinkValue(raw) {
|
|
162
|
+
if (raw === null || typeof raw !== "object") {
|
|
163
|
+
return { type: "internal", slug: "", label: "" };
|
|
164
|
+
}
|
|
165
|
+
const obj = raw;
|
|
166
|
+
const label = typeof obj["label"] === "string" ? obj["label"] : "";
|
|
167
|
+
if (obj["type"] === "internal") {
|
|
168
|
+
const slug = typeof obj["slug"] === "string" ? obj["slug"] : "";
|
|
169
|
+
return { type: "internal", slug, label };
|
|
170
|
+
}
|
|
171
|
+
if (obj["type"] === "external") {
|
|
172
|
+
const url = typeof obj["url"] === "string" ? obj["url"] : "";
|
|
173
|
+
return { type: "external", url, label };
|
|
174
|
+
}
|
|
175
|
+
if (obj["type"] === "email") {
|
|
176
|
+
const email = typeof obj["email"] === "string" ? obj["email"] : "";
|
|
177
|
+
return { type: "email", email, label };
|
|
178
|
+
}
|
|
179
|
+
if (obj["type"] === "phone") {
|
|
180
|
+
const phone = typeof obj["phone"] === "string" ? obj["phone"] : "";
|
|
181
|
+
return { type: "phone", phone, label };
|
|
182
|
+
}
|
|
183
|
+
if (typeof obj["href"] === "string") {
|
|
184
|
+
const href = obj["href"];
|
|
185
|
+
if (/^mailto:/i.test(href)) {
|
|
186
|
+
return { type: "email", email: href.slice("mailto:".length), label };
|
|
187
|
+
}
|
|
188
|
+
if (/^tel:/i.test(href)) {
|
|
189
|
+
return { type: "phone", phone: href.slice("tel:".length), label };
|
|
190
|
+
}
|
|
191
|
+
if (/^https?:\/\//i.test(href)) {
|
|
192
|
+
return { type: "external", url: href, label };
|
|
193
|
+
}
|
|
194
|
+
const slug = href.startsWith("/") ? href.slice(1) : href;
|
|
195
|
+
return { type: "internal", slug, label };
|
|
196
|
+
}
|
|
197
|
+
return { type: "internal", slug: "", label: "" };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/domain/form.ts
|
|
201
|
+
var FORM_FORBIDDEN_KINDS = /* @__PURE__ */ new Set([
|
|
202
|
+
"image",
|
|
203
|
+
"video",
|
|
204
|
+
"reference",
|
|
205
|
+
"list",
|
|
206
|
+
// `formOverrides` is a section-only descriptor (it overrides another
|
|
207
|
+
// form schema instance). Putting it inside a form would mean a form's
|
|
208
|
+
// payload could carry overrides for itself or another form — a
|
|
209
|
+
// recursive shape with no ergonomic editor UI. Section-only by design.
|
|
210
|
+
"formOverrides",
|
|
211
|
+
// `button` is a section-only descriptor: a styled CTA with an
|
|
212
|
+
// optional link. Public-form payloads collect user input — a button
|
|
213
|
+
// value is authored content, not a submitted answer. Section-only
|
|
214
|
+
// by design (mirrors `formOverrides`).
|
|
215
|
+
"button"
|
|
216
|
+
]);
|
|
217
|
+
var SubmissionsNotReadableError = class extends Error {
|
|
218
|
+
constructor(message = "submission adapter does not support reading") {
|
|
219
|
+
super(message);
|
|
220
|
+
this.name = "SubmissionsNotReadableError";
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/domain/formOverrides.ts
|
|
225
|
+
var FormOverridesField = (formName, opts) => ({
|
|
226
|
+
kind: "formOverrides",
|
|
227
|
+
formName,
|
|
228
|
+
...opts?.default !== void 0 ? { default: opts.default } : {}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// src/sections/defineSection.ts
|
|
232
|
+
function builtInDefault(fieldName, descriptor) {
|
|
233
|
+
switch (descriptor.kind) {
|
|
234
|
+
case "image":
|
|
235
|
+
return { filename: "placeholder.png", alt: "Placeholder image" };
|
|
236
|
+
case "video":
|
|
237
|
+
return { url: "" };
|
|
238
|
+
case "richText":
|
|
239
|
+
return "Start writing here...";
|
|
240
|
+
case "text":
|
|
241
|
+
return fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/([A-Z])/g, " $1");
|
|
242
|
+
case "reference":
|
|
243
|
+
return { slug: "" };
|
|
244
|
+
case "link":
|
|
245
|
+
return { type: "internal", slug: "", label: "Learn more" };
|
|
246
|
+
case "button": {
|
|
247
|
+
const firstVariant = descriptor.variants[0]?.value ?? "";
|
|
248
|
+
return { label: "Get started", variant: firstVariant };
|
|
249
|
+
}
|
|
250
|
+
case "number":
|
|
251
|
+
return 0;
|
|
252
|
+
case "boolean":
|
|
253
|
+
return false;
|
|
254
|
+
case "select":
|
|
255
|
+
return descriptor.options[0]?.value ?? "";
|
|
256
|
+
case "list":
|
|
257
|
+
return [];
|
|
258
|
+
case "formOverrides":
|
|
259
|
+
return {};
|
|
260
|
+
default: {
|
|
261
|
+
const _exhaustive = descriptor;
|
|
262
|
+
void _exhaustive;
|
|
263
|
+
return "";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function defineSection(input) {
|
|
268
|
+
const defaults = {};
|
|
269
|
+
for (const [key, descriptor] of Object.entries(input.schema)) {
|
|
270
|
+
defaults[key] = descriptor.default ?? builtInDefault(key, descriptor);
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
name: input.name,
|
|
274
|
+
...input.category !== void 0 ? { category: input.category } : {},
|
|
275
|
+
schema: input.schema,
|
|
276
|
+
component: input.component,
|
|
277
|
+
defaults,
|
|
278
|
+
// Conditional spread mirrors the `category` pattern — required so
|
|
279
|
+
// `exactOptionalPropertyTypes` does not see `layouts: undefined` being
|
|
280
|
+
// assigned to an optional-only property.
|
|
281
|
+
...input.layouts !== void 0 ? { layouts: input.layouts } : {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/forms/defineForm.ts
|
|
286
|
+
var InvalidFormFieldError = class extends Error {
|
|
287
|
+
formName;
|
|
288
|
+
fieldName;
|
|
289
|
+
fieldKind;
|
|
290
|
+
constructor(formName, fieldName, fieldKind) {
|
|
291
|
+
super(
|
|
292
|
+
`Form "${formName}": field "${fieldName}" uses kind "${fieldKind}", which is not allowed in a form schema in v1. Allowed kinds: text, richText, number, boolean, select, link. Forbidden kinds: image, reference, list, formOverrides. See ARCHITECTURE.md \xA76.5.`
|
|
293
|
+
);
|
|
294
|
+
this.name = "InvalidFormFieldError";
|
|
295
|
+
this.formName = formName;
|
|
296
|
+
this.fieldName = fieldName;
|
|
297
|
+
this.fieldKind = fieldKind;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var HoneypotCollisionError = class extends Error {
|
|
301
|
+
formName;
|
|
302
|
+
fieldName;
|
|
303
|
+
reason;
|
|
304
|
+
constructor(formName, fieldName, reason = "collision") {
|
|
305
|
+
super(
|
|
306
|
+
reason === "empty" ? `Form "${formName}": honeypot name must be a non-empty string.` : `Form "${formName}": honeypot name "${fieldName}" collides with a real field. Choose a honeypot name that does not appear in the schema.`
|
|
307
|
+
);
|
|
308
|
+
this.name = "HoneypotCollisionError";
|
|
309
|
+
this.formName = formName;
|
|
310
|
+
this.fieldName = fieldName;
|
|
311
|
+
this.reason = reason;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
var InvalidFormNameError = class extends Error {
|
|
315
|
+
constructor(name) {
|
|
316
|
+
super(
|
|
317
|
+
`Invalid form name ${JSON.stringify(name)}. Use letters, digits, hyphen, and underscore only (no path separators).`
|
|
318
|
+
);
|
|
319
|
+
this.name = "InvalidFormNameError";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var FORM_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
323
|
+
function defineForm(input) {
|
|
324
|
+
if (typeof input.name !== "string" || !FORM_NAME_PATTERN.test(input.name)) {
|
|
325
|
+
throw new InvalidFormNameError(input.name);
|
|
326
|
+
}
|
|
327
|
+
for (const [fieldName, descriptor] of Object.entries(input.schema)) {
|
|
328
|
+
const kind = descriptor.kind;
|
|
329
|
+
if (FORM_FORBIDDEN_KINDS.has(kind)) {
|
|
330
|
+
throw new InvalidFormFieldError(input.name, fieldName, kind);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (input.honeypot !== void 0) {
|
|
334
|
+
if (typeof input.honeypot !== "string" || input.honeypot === "") {
|
|
335
|
+
throw new HoneypotCollisionError(input.name, input.honeypot, "empty");
|
|
336
|
+
}
|
|
337
|
+
if (Object.prototype.hasOwnProperty.call(input.schema, input.honeypot)) {
|
|
338
|
+
throw new HoneypotCollisionError(input.name, input.honeypot, "collision");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return input.honeypot !== void 0 ? { name: input.name, schema: input.schema, honeypot: input.honeypot } : { name: input.name, schema: input.schema };
|
|
342
|
+
}
|
|
343
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
344
|
+
0 && (module.exports = {
|
|
345
|
+
BooleanField,
|
|
346
|
+
ButtonField,
|
|
347
|
+
FormOverridesField,
|
|
348
|
+
HoneypotCollisionError,
|
|
349
|
+
ImageField,
|
|
350
|
+
InvalidFormFieldError,
|
|
351
|
+
InvalidFormNameError,
|
|
352
|
+
LinkField,
|
|
353
|
+
ListField,
|
|
354
|
+
NumberField,
|
|
355
|
+
ReferenceField,
|
|
356
|
+
RichTextField,
|
|
357
|
+
SelectField,
|
|
358
|
+
SubmissionsNotReadableError,
|
|
359
|
+
TextField,
|
|
360
|
+
VideoField,
|
|
361
|
+
defineForm,
|
|
362
|
+
defineSection,
|
|
363
|
+
hasUniqueSectionIds,
|
|
364
|
+
hrefOf,
|
|
365
|
+
isExternalLink,
|
|
366
|
+
linkAnchorAttrs,
|
|
367
|
+
normalizeLinkValue,
|
|
368
|
+
validateEmail,
|
|
369
|
+
validateExternalUrl,
|
|
370
|
+
validateInternalSlug,
|
|
371
|
+
validatePhone
|
|
372
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { P as Page, h as LinkValue } from './form-BqY0H1V5.cjs';
|
|
2
|
+
export { A as AnyFormDefinition, j as BooleanField, B as ButtonField, i as ButtonValue, c as FieldDescriptor, y as FieldKind, x as FieldValueFor, b as FormDefinition, p as FormFieldDescriptor, q as FormFieldOverride, n as FormFieldOverrides, r as FormOverridesField, m as FormOverridesFieldDescriptor, a as FormSchema, I as ImageField, d as ImageValue, L as LinkField, l as ListField, z as ListItem, N as NumberField, C as PageSeo, w as PageSummary, f as ReferenceField, g as ReferenceValue, R as RichTextField, D as Section, S as SectionSchema, k as SelectField, E as SelectOption, s as Submission, t as SubmissionAdapterInfo, o as SubmissionStorageAdapter, u as SubmissionSummary, v as SubmissionsNotReadableError, T as TextField, V as VideoField, e as VideoValue } from './form-BqY0H1V5.cjs';
|
|
3
|
+
export { G as Global } from './global-CV23g5Bn.cjs';
|
|
4
|
+
export { A as AnySectionDefinition, D as DataOf, a as DefineSectionInput, E as EditableSlot, F as FieldDataType, S as SectionComponent, b as SectionDefinition, c as SlotItem, d as defineSection } from './defineSection-9qQ5ulAH.cjs';
|
|
5
|
+
export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-CJ8KZC93.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns `true` when every `section.id` in the page appears exactly once.
|
|
9
|
+
* Empty `sections` arrays are trivially unique.
|
|
10
|
+
*/
|
|
11
|
+
declare function hasUniqueSectionIds(page: Page): boolean;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validate the slug stored on an internal `LinkValue`.
|
|
15
|
+
*
|
|
16
|
+
* Returns `null` when valid (including the empty string, which the
|
|
17
|
+
* editor renders as "not yet selected"), an error message otherwise.
|
|
18
|
+
*/
|
|
19
|
+
declare const validateInternalSlug: (slug: string) => string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Validate the URL stored on an external `LinkValue`.
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` when valid (including the empty string, which the
|
|
24
|
+
* editor renders as "not yet entered"), an error message otherwise.
|
|
25
|
+
*
|
|
26
|
+
* Only absolute http(s) URLs pass. Site-relative paths belong to the
|
|
27
|
+
* internal branch — if the author types `/about`, they should switch
|
|
28
|
+
* the segmented control to "Internal" and enter `about` instead.
|
|
29
|
+
*/
|
|
30
|
+
declare const validateExternalUrl: (url: string) => string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Validate the email stored on an `email` `LinkValue`.
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` when valid (including the empty string, which the
|
|
35
|
+
* editor renders as "not yet entered"), an error message otherwise.
|
|
36
|
+
*/
|
|
37
|
+
declare const validateEmail: (email: string) => string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Validate the phone string stored on a `phone` `LinkValue`.
|
|
40
|
+
*
|
|
41
|
+
* Returns `null` when valid (including the empty string, which the
|
|
42
|
+
* editor renders as "not yet entered"). Non-empty input must contain
|
|
43
|
+
* at least seven digits after stripping every character that is
|
|
44
|
+
* neither a digit nor a leading `+`. Country-code structure is not
|
|
45
|
+
* validated — it's overkill for v1 and would reject authored strings
|
|
46
|
+
* that real-world dialler apps handle fine.
|
|
47
|
+
*/
|
|
48
|
+
declare const validatePhone: (phone: string) => string | null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a `LinkValue` to the href string a section component should
|
|
52
|
+
* render in an `<a>` element.
|
|
53
|
+
*
|
|
54
|
+
* Internal slugs route through the catch-all (`app/[[...slug]]/page.tsx`)
|
|
55
|
+
* so they always live under the site root:
|
|
56
|
+
* - empty slug or `'home'` → `'/'`
|
|
57
|
+
* - `'about'` → `'/about'`
|
|
58
|
+
* - `'/blog'` → `'/blog'` (defensive: strip leading `/`)
|
|
59
|
+
* - `'blog/welcome'` → `'/blog/welcome'`
|
|
60
|
+
*
|
|
61
|
+
* External links return their URL verbatim; the validator already
|
|
62
|
+
* enforces the http(s) protocol allow-list, so we don't re-check here.
|
|
63
|
+
*
|
|
64
|
+
* Email links return `mailto:<email>` when the email is non-empty, or
|
|
65
|
+
* `''` when empty — the empty href lets a section component skip the
|
|
66
|
+
* anchor entirely when an author has not yet filled the field.
|
|
67
|
+
*
|
|
68
|
+
* Phone links return `tel:<digits>` with non-`[\d+]` characters
|
|
69
|
+
* stripped from the URI (so parens, spaces, and hyphens that authors
|
|
70
|
+
* type for legibility are removed before emitting `tel:`). The
|
|
71
|
+
* original formatted string stays in `link.phone` for display
|
|
72
|
+
* purposes. Empty phone → `''`, same rationale as email.
|
|
73
|
+
*/
|
|
74
|
+
declare function hrefOf(link: LinkValue): string;
|
|
75
|
+
/**
|
|
76
|
+
* True iff the link points outside the current site.
|
|
77
|
+
*
|
|
78
|
+
* Only the `external` branch returns true. `email` and `phone` fire
|
|
79
|
+
* native handlers (mail client, dialler) — they do not navigate to a
|
|
80
|
+
* different site, so they are NOT considered "external" for the
|
|
81
|
+
* purpose of `target='_blank'` / `rel='noreferrer'`. Use
|
|
82
|
+
* `linkAnchorAttrs` to pick the right anchor attributes from a
|
|
83
|
+
* `LinkValue`.
|
|
84
|
+
*/
|
|
85
|
+
declare function isExternalLink(link: LinkValue): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Canonical helper for translating a `LinkValue` into anchor
|
|
88
|
+
* attributes. Returns the href every link should carry, plus the
|
|
89
|
+
* `target='_blank'` / `rel='noreferrer'` pair only for the `external`
|
|
90
|
+
* branch.
|
|
91
|
+
*
|
|
92
|
+
* Why a single helper instead of letting consumers compose `hrefOf` +
|
|
93
|
+
* `isExternalLink` themselves: the contract for "open in a new tab"
|
|
94
|
+
* has gotten more nuanced as the discriminated union grew. Email and
|
|
95
|
+
* phone hand off to native OS handlers and would behave incorrectly
|
|
96
|
+
* with `target='_blank'` (Safari opens a blank tab that lingers after
|
|
97
|
+
* the mailto handler resolves). Centralising the decision here means
|
|
98
|
+
* marketing-site primitives, custom CTA buttons, navigation menus,
|
|
99
|
+
* and any future link-rendering surface make the same choice.
|
|
100
|
+
*
|
|
101
|
+
* Section authors should prefer:
|
|
102
|
+
* const attrs = linkAnchorAttrs(link)
|
|
103
|
+
* <a {...attrs}>{link.label}</a>
|
|
104
|
+
* over manually computing `target` / `rel`.
|
|
105
|
+
*/
|
|
106
|
+
declare function linkAnchorAttrs(link: LinkValue): {
|
|
107
|
+
href: string;
|
|
108
|
+
target?: '_blank';
|
|
109
|
+
rel?: 'noreferrer';
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Project an arbitrary value into the canonical `LinkValue` shape.
|
|
113
|
+
*
|
|
114
|
+
* Three input cases:
|
|
115
|
+
* 1. Old-shape `{ href, label, external? }`: infer `type` from the
|
|
116
|
+
* `href` protocol — `mailto:` → email, `tel:` → phone, `http(s):`
|
|
117
|
+
* → external, otherwise internal. For the internal branch we
|
|
118
|
+
* strip the leading `/` so the stored slug matches the page-id
|
|
119
|
+
* model (no leading slash, see `hrefOf`).
|
|
120
|
+
* 2. New-shape `{ type: 'internal' | 'external' | 'email' | 'phone',
|
|
121
|
+
* … }`: returned as-is, narrowed.
|
|
122
|
+
* 3. Anything else (null, primitives, garbage objects): collapse to
|
|
123
|
+
* a blank internal link so the renderer can still emit something
|
|
124
|
+
* and the editor can re-author from a known-good baseline.
|
|
125
|
+
*
|
|
126
|
+
* The function is intentionally lenient: it never throws. A throwing
|
|
127
|
+
* normaliser would push error-handling onto every section component
|
|
128
|
+
* for the rare case of legacy data, defeating the point of the
|
|
129
|
+
* defensive narrowing.
|
|
130
|
+
*/
|
|
131
|
+
declare function normalizeLinkValue(raw: unknown): LinkValue;
|
|
132
|
+
|
|
133
|
+
export { LinkValue, Page, hasUniqueSectionIds, hrefOf, isExternalLink, linkAnchorAttrs, normalizeLinkValue, validateEmail, validateExternalUrl, validateInternalSlug, validatePhone };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { P as Page, h as LinkValue } from './form-BqY0H1V5.js';
|
|
2
|
+
export { A as AnyFormDefinition, j as BooleanField, B as ButtonField, i as ButtonValue, c as FieldDescriptor, y as FieldKind, x as FieldValueFor, b as FormDefinition, p as FormFieldDescriptor, q as FormFieldOverride, n as FormFieldOverrides, r as FormOverridesField, m as FormOverridesFieldDescriptor, a as FormSchema, I as ImageField, d as ImageValue, L as LinkField, l as ListField, z as ListItem, N as NumberField, C as PageSeo, w as PageSummary, f as ReferenceField, g as ReferenceValue, R as RichTextField, D as Section, S as SectionSchema, k as SelectField, E as SelectOption, s as Submission, t as SubmissionAdapterInfo, o as SubmissionStorageAdapter, u as SubmissionSummary, v as SubmissionsNotReadableError, T as TextField, V as VideoField, e as VideoValue } from './form-BqY0H1V5.js';
|
|
3
|
+
export { G as Global } from './global-CV23g5Bn.js';
|
|
4
|
+
export { A as AnySectionDefinition, D as DataOf, a as DefineSectionInput, E as EditableSlot, F as FieldDataType, S as SectionComponent, b as SectionDefinition, c as SlotItem, d as defineSection } from './defineSection-Kr0pWqMY.js';
|
|
5
|
+
export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-Bp9vzW56.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns `true` when every `section.id` in the page appears exactly once.
|
|
9
|
+
* Empty `sections` arrays are trivially unique.
|
|
10
|
+
*/
|
|
11
|
+
declare function hasUniqueSectionIds(page: Page): boolean;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validate the slug stored on an internal `LinkValue`.
|
|
15
|
+
*
|
|
16
|
+
* Returns `null` when valid (including the empty string, which the
|
|
17
|
+
* editor renders as "not yet selected"), an error message otherwise.
|
|
18
|
+
*/
|
|
19
|
+
declare const validateInternalSlug: (slug: string) => string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Validate the URL stored on an external `LinkValue`.
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` when valid (including the empty string, which the
|
|
24
|
+
* editor renders as "not yet entered"), an error message otherwise.
|
|
25
|
+
*
|
|
26
|
+
* Only absolute http(s) URLs pass. Site-relative paths belong to the
|
|
27
|
+
* internal branch — if the author types `/about`, they should switch
|
|
28
|
+
* the segmented control to "Internal" and enter `about` instead.
|
|
29
|
+
*/
|
|
30
|
+
declare const validateExternalUrl: (url: string) => string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Validate the email stored on an `email` `LinkValue`.
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` when valid (including the empty string, which the
|
|
35
|
+
* editor renders as "not yet entered"), an error message otherwise.
|
|
36
|
+
*/
|
|
37
|
+
declare const validateEmail: (email: string) => string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Validate the phone string stored on a `phone` `LinkValue`.
|
|
40
|
+
*
|
|
41
|
+
* Returns `null` when valid (including the empty string, which the
|
|
42
|
+
* editor renders as "not yet entered"). Non-empty input must contain
|
|
43
|
+
* at least seven digits after stripping every character that is
|
|
44
|
+
* neither a digit nor a leading `+`. Country-code structure is not
|
|
45
|
+
* validated — it's overkill for v1 and would reject authored strings
|
|
46
|
+
* that real-world dialler apps handle fine.
|
|
47
|
+
*/
|
|
48
|
+
declare const validatePhone: (phone: string) => string | null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a `LinkValue` to the href string a section component should
|
|
52
|
+
* render in an `<a>` element.
|
|
53
|
+
*
|
|
54
|
+
* Internal slugs route through the catch-all (`app/[[...slug]]/page.tsx`)
|
|
55
|
+
* so they always live under the site root:
|
|
56
|
+
* - empty slug or `'home'` → `'/'`
|
|
57
|
+
* - `'about'` → `'/about'`
|
|
58
|
+
* - `'/blog'` → `'/blog'` (defensive: strip leading `/`)
|
|
59
|
+
* - `'blog/welcome'` → `'/blog/welcome'`
|
|
60
|
+
*
|
|
61
|
+
* External links return their URL verbatim; the validator already
|
|
62
|
+
* enforces the http(s) protocol allow-list, so we don't re-check here.
|
|
63
|
+
*
|
|
64
|
+
* Email links return `mailto:<email>` when the email is non-empty, or
|
|
65
|
+
* `''` when empty — the empty href lets a section component skip the
|
|
66
|
+
* anchor entirely when an author has not yet filled the field.
|
|
67
|
+
*
|
|
68
|
+
* Phone links return `tel:<digits>` with non-`[\d+]` characters
|
|
69
|
+
* stripped from the URI (so parens, spaces, and hyphens that authors
|
|
70
|
+
* type for legibility are removed before emitting `tel:`). The
|
|
71
|
+
* original formatted string stays in `link.phone` for display
|
|
72
|
+
* purposes. Empty phone → `''`, same rationale as email.
|
|
73
|
+
*/
|
|
74
|
+
declare function hrefOf(link: LinkValue): string;
|
|
75
|
+
/**
|
|
76
|
+
* True iff the link points outside the current site.
|
|
77
|
+
*
|
|
78
|
+
* Only the `external` branch returns true. `email` and `phone` fire
|
|
79
|
+
* native handlers (mail client, dialler) — they do not navigate to a
|
|
80
|
+
* different site, so they are NOT considered "external" for the
|
|
81
|
+
* purpose of `target='_blank'` / `rel='noreferrer'`. Use
|
|
82
|
+
* `linkAnchorAttrs` to pick the right anchor attributes from a
|
|
83
|
+
* `LinkValue`.
|
|
84
|
+
*/
|
|
85
|
+
declare function isExternalLink(link: LinkValue): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Canonical helper for translating a `LinkValue` into anchor
|
|
88
|
+
* attributes. Returns the href every link should carry, plus the
|
|
89
|
+
* `target='_blank'` / `rel='noreferrer'` pair only for the `external`
|
|
90
|
+
* branch.
|
|
91
|
+
*
|
|
92
|
+
* Why a single helper instead of letting consumers compose `hrefOf` +
|
|
93
|
+
* `isExternalLink` themselves: the contract for "open in a new tab"
|
|
94
|
+
* has gotten more nuanced as the discriminated union grew. Email and
|
|
95
|
+
* phone hand off to native OS handlers and would behave incorrectly
|
|
96
|
+
* with `target='_blank'` (Safari opens a blank tab that lingers after
|
|
97
|
+
* the mailto handler resolves). Centralising the decision here means
|
|
98
|
+
* marketing-site primitives, custom CTA buttons, navigation menus,
|
|
99
|
+
* and any future link-rendering surface make the same choice.
|
|
100
|
+
*
|
|
101
|
+
* Section authors should prefer:
|
|
102
|
+
* const attrs = linkAnchorAttrs(link)
|
|
103
|
+
* <a {...attrs}>{link.label}</a>
|
|
104
|
+
* over manually computing `target` / `rel`.
|
|
105
|
+
*/
|
|
106
|
+
declare function linkAnchorAttrs(link: LinkValue): {
|
|
107
|
+
href: string;
|
|
108
|
+
target?: '_blank';
|
|
109
|
+
rel?: 'noreferrer';
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Project an arbitrary value into the canonical `LinkValue` shape.
|
|
113
|
+
*
|
|
114
|
+
* Three input cases:
|
|
115
|
+
* 1. Old-shape `{ href, label, external? }`: infer `type` from the
|
|
116
|
+
* `href` protocol — `mailto:` → email, `tel:` → phone, `http(s):`
|
|
117
|
+
* → external, otherwise internal. For the internal branch we
|
|
118
|
+
* strip the leading `/` so the stored slug matches the page-id
|
|
119
|
+
* model (no leading slash, see `hrefOf`).
|
|
120
|
+
* 2. New-shape `{ type: 'internal' | 'external' | 'email' | 'phone',
|
|
121
|
+
* … }`: returned as-is, narrowed.
|
|
122
|
+
* 3. Anything else (null, primitives, garbage objects): collapse to
|
|
123
|
+
* a blank internal link so the renderer can still emit something
|
|
124
|
+
* and the editor can re-author from a known-good baseline.
|
|
125
|
+
*
|
|
126
|
+
* The function is intentionally lenient: it never throws. A throwing
|
|
127
|
+
* normaliser would push error-handling onto every section component
|
|
128
|
+
* for the rare case of legacy data, defeating the point of the
|
|
129
|
+
* defensive narrowing.
|
|
130
|
+
*/
|
|
131
|
+
declare function normalizeLinkValue(raw: unknown): LinkValue;
|
|
132
|
+
|
|
133
|
+
export { LinkValue, Page, hasUniqueSectionIds, hrefOf, isExternalLink, linkAnchorAttrs, normalizeLinkValue, validateEmail, validateExternalUrl, validateInternalSlug, validatePhone };
|