@agntcms/next 0.3.1 → 0.3.4
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 → assets-B3oNeLdj.d.cts} +1 -1
- package/dist/{assets-P8OCigDG.d.ts → assets-DHumg-X7.d.ts} +1 -1
- package/dist/client.cjs +2831 -4747
- package/dist/client.d.cts +11 -153
- package/dist/client.d.ts +11 -153
- package/dist/client.mjs +4750 -6675
- package/dist/config.cjs +25 -121
- package/dist/config.d.cts +6 -59
- package/dist/config.d.ts +6 -59
- package/dist/config.mjs +25 -114
- package/dist/{defineSection-Kr0pWqMY.d.ts → defineSection-ByG5uwiR.d.cts} +5 -24
- package/dist/{defineSection-9qQ5ulAH.d.cts → defineSection-ChkZCQyQ.d.ts} +5 -24
- package/dist/{rateLimit-CXptRM_K.d.ts → getContent-DAgAn095.d.ts} +3 -132
- package/dist/{rateLimit-CiROGTLE.d.cts → getContent-yK-sARoc.d.cts} +3 -132
- package/dist/handlers.cjs +19 -382
- package/dist/handlers.d.cts +4 -73
- package/dist/handlers.d.ts +4 -73
- package/dist/handlers.mjs +19 -377
- package/dist/index.cjs +1 -109
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.mjs +1 -103
- package/dist/{form-BqY0H1V5.d.cts → page-DXF0_SrY.d.cts} +3 -293
- package/dist/{form-BqY0H1V5.d.ts → page-DXF0_SrY.d.ts} +3 -293
- package/dist/server.cjs +15 -635
- package/dist/server.d.cts +8 -75
- package/dist/server.d.ts +8 -75
- package/dist/server.mjs +11 -618
- package/package.json +1 -1
- package/dist/defineForm-Bp9vzW56.d.ts +0 -71
- package/dist/defineForm-CJ8KZC93.d.cts +0 -71
- package/dist/registry-CraTTwT7.d.cts +0 -29
- package/dist/registry-DMujGqt0.d.ts +0 -29
package/dist/index.cjs
CHANGED
|
@@ -22,21 +22,15 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BooleanField: () => BooleanField,
|
|
24
24
|
ButtonField: () => ButtonField,
|
|
25
|
-
FormOverridesField: () => FormOverridesField,
|
|
26
|
-
HoneypotCollisionError: () => HoneypotCollisionError,
|
|
27
25
|
ImageField: () => ImageField,
|
|
28
|
-
InvalidFormFieldError: () => InvalidFormFieldError,
|
|
29
|
-
InvalidFormNameError: () => InvalidFormNameError,
|
|
30
26
|
LinkField: () => LinkField,
|
|
31
27
|
ListField: () => ListField,
|
|
32
28
|
NumberField: () => NumberField,
|
|
33
29
|
ReferenceField: () => ReferenceField,
|
|
34
30
|
RichTextField: () => RichTextField,
|
|
35
31
|
SelectField: () => SelectField,
|
|
36
|
-
SubmissionsNotReadableError: () => SubmissionsNotReadableError,
|
|
37
32
|
TextField: () => TextField,
|
|
38
33
|
VideoField: () => VideoField,
|
|
39
|
-
defineForm: () => defineForm,
|
|
40
34
|
defineSection: () => defineSection,
|
|
41
35
|
hasUniqueSectionIds: () => hasUniqueSectionIds,
|
|
42
36
|
hrefOf: () => hrefOf,
|
|
@@ -197,37 +191,6 @@ function normalizeLinkValue(raw) {
|
|
|
197
191
|
return { type: "internal", slug: "", label: "" };
|
|
198
192
|
}
|
|
199
193
|
|
|
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
194
|
// src/sections/defineSection.ts
|
|
232
195
|
function builtInDefault(fieldName, descriptor) {
|
|
233
196
|
switch (descriptor.kind) {
|
|
@@ -255,8 +218,6 @@ function builtInDefault(fieldName, descriptor) {
|
|
|
255
218
|
return descriptor.options[0]?.value ?? "";
|
|
256
219
|
case "list":
|
|
257
220
|
return [];
|
|
258
|
-
case "formOverrides":
|
|
259
|
-
return {};
|
|
260
221
|
default: {
|
|
261
222
|
const _exhaustive = descriptor;
|
|
262
223
|
void _exhaustive;
|
|
@@ -274,91 +235,22 @@ function defineSection(input) {
|
|
|
274
235
|
...input.category !== void 0 ? { category: input.category } : {},
|
|
275
236
|
schema: input.schema,
|
|
276
237
|
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 } : {}
|
|
238
|
+
defaults
|
|
282
239
|
};
|
|
283
240
|
}
|
|
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
241
|
// Annotate the CommonJS export names for ESM import in node:
|
|
344
242
|
0 && (module.exports = {
|
|
345
243
|
BooleanField,
|
|
346
244
|
ButtonField,
|
|
347
|
-
FormOverridesField,
|
|
348
|
-
HoneypotCollisionError,
|
|
349
245
|
ImageField,
|
|
350
|
-
InvalidFormFieldError,
|
|
351
|
-
InvalidFormNameError,
|
|
352
246
|
LinkField,
|
|
353
247
|
ListField,
|
|
354
248
|
NumberField,
|
|
355
249
|
ReferenceField,
|
|
356
250
|
RichTextField,
|
|
357
251
|
SelectField,
|
|
358
|
-
SubmissionsNotReadableError,
|
|
359
252
|
TextField,
|
|
360
253
|
VideoField,
|
|
361
|
-
defineForm,
|
|
362
254
|
defineSection,
|
|
363
255
|
hasUniqueSectionIds,
|
|
364
256
|
hrefOf,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { P as Page,
|
|
2
|
-
export {
|
|
1
|
+
import { P as Page, e as LinkValue } from './page-DXF0_SrY.cjs';
|
|
2
|
+
export { g as BooleanField, B as ButtonField, f as ButtonValue, F as FieldDescriptor, l as FieldKind, k as FieldValueFor, I as ImageField, a as ImageValue, L as LinkField, i as ListField, m as ListItem, N as NumberField, n as PageSeo, j as PageSummary, c as ReferenceField, d as ReferenceValue, R as RichTextField, o as Section, S as SectionSchema, h as SelectField, p as SelectOption, T as TextField, V as VideoField, b as VideoValue } from './page-DXF0_SrY.cjs';
|
|
3
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-
|
|
5
|
-
export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-CJ8KZC93.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-ByG5uwiR.cjs';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Returns `true` when every `section.id` in the page appears exactly once.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { P as Page,
|
|
2
|
-
export {
|
|
1
|
+
import { P as Page, e as LinkValue } from './page-DXF0_SrY.js';
|
|
2
|
+
export { g as BooleanField, B as ButtonField, f as ButtonValue, F as FieldDescriptor, l as FieldKind, k as FieldValueFor, I as ImageField, a as ImageValue, L as LinkField, i as ListField, m as ListItem, N as NumberField, n as PageSeo, j as PageSummary, c as ReferenceField, d as ReferenceValue, R as RichTextField, o as Section, S as SectionSchema, h as SelectField, p as SelectOption, T as TextField, V as VideoField, b as VideoValue } from './page-DXF0_SrY.js';
|
|
3
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-
|
|
5
|
-
export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-Bp9vzW56.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-ChkZCQyQ.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Returns `true` when every `section.id` in the page appears exactly once.
|
package/dist/index.mjs
CHANGED
|
@@ -145,37 +145,6 @@ function normalizeLinkValue(raw) {
|
|
|
145
145
|
return { type: "internal", slug: "", label: "" };
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
// src/domain/form.ts
|
|
149
|
-
var FORM_FORBIDDEN_KINDS = /* @__PURE__ */ new Set([
|
|
150
|
-
"image",
|
|
151
|
-
"video",
|
|
152
|
-
"reference",
|
|
153
|
-
"list",
|
|
154
|
-
// `formOverrides` is a section-only descriptor (it overrides another
|
|
155
|
-
// form schema instance). Putting it inside a form would mean a form's
|
|
156
|
-
// payload could carry overrides for itself or another form — a
|
|
157
|
-
// recursive shape with no ergonomic editor UI. Section-only by design.
|
|
158
|
-
"formOverrides",
|
|
159
|
-
// `button` is a section-only descriptor: a styled CTA with an
|
|
160
|
-
// optional link. Public-form payloads collect user input — a button
|
|
161
|
-
// value is authored content, not a submitted answer. Section-only
|
|
162
|
-
// by design (mirrors `formOverrides`).
|
|
163
|
-
"button"
|
|
164
|
-
]);
|
|
165
|
-
var SubmissionsNotReadableError = class extends Error {
|
|
166
|
-
constructor(message = "submission adapter does not support reading") {
|
|
167
|
-
super(message);
|
|
168
|
-
this.name = "SubmissionsNotReadableError";
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// src/domain/formOverrides.ts
|
|
173
|
-
var FormOverridesField = (formName, opts) => ({
|
|
174
|
-
kind: "formOverrides",
|
|
175
|
-
formName,
|
|
176
|
-
...opts?.default !== void 0 ? { default: opts.default } : {}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
148
|
// src/sections/defineSection.ts
|
|
180
149
|
function builtInDefault(fieldName, descriptor) {
|
|
181
150
|
switch (descriptor.kind) {
|
|
@@ -203,8 +172,6 @@ function builtInDefault(fieldName, descriptor) {
|
|
|
203
172
|
return descriptor.options[0]?.value ?? "";
|
|
204
173
|
case "list":
|
|
205
174
|
return [];
|
|
206
|
-
case "formOverrides":
|
|
207
|
-
return {};
|
|
208
175
|
default: {
|
|
209
176
|
const _exhaustive = descriptor;
|
|
210
177
|
void _exhaustive;
|
|
@@ -222,90 +189,21 @@ function defineSection(input) {
|
|
|
222
189
|
...input.category !== void 0 ? { category: input.category } : {},
|
|
223
190
|
schema: input.schema,
|
|
224
191
|
component: input.component,
|
|
225
|
-
defaults
|
|
226
|
-
// Conditional spread mirrors the `category` pattern — required so
|
|
227
|
-
// `exactOptionalPropertyTypes` does not see `layouts: undefined` being
|
|
228
|
-
// assigned to an optional-only property.
|
|
229
|
-
...input.layouts !== void 0 ? { layouts: input.layouts } : {}
|
|
192
|
+
defaults
|
|
230
193
|
};
|
|
231
194
|
}
|
|
232
|
-
|
|
233
|
-
// src/forms/defineForm.ts
|
|
234
|
-
var InvalidFormFieldError = class extends Error {
|
|
235
|
-
formName;
|
|
236
|
-
fieldName;
|
|
237
|
-
fieldKind;
|
|
238
|
-
constructor(formName, fieldName, fieldKind) {
|
|
239
|
-
super(
|
|
240
|
-
`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.`
|
|
241
|
-
);
|
|
242
|
-
this.name = "InvalidFormFieldError";
|
|
243
|
-
this.formName = formName;
|
|
244
|
-
this.fieldName = fieldName;
|
|
245
|
-
this.fieldKind = fieldKind;
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
var HoneypotCollisionError = class extends Error {
|
|
249
|
-
formName;
|
|
250
|
-
fieldName;
|
|
251
|
-
reason;
|
|
252
|
-
constructor(formName, fieldName, reason = "collision") {
|
|
253
|
-
super(
|
|
254
|
-
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.`
|
|
255
|
-
);
|
|
256
|
-
this.name = "HoneypotCollisionError";
|
|
257
|
-
this.formName = formName;
|
|
258
|
-
this.fieldName = fieldName;
|
|
259
|
-
this.reason = reason;
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
var InvalidFormNameError = class extends Error {
|
|
263
|
-
constructor(name) {
|
|
264
|
-
super(
|
|
265
|
-
`Invalid form name ${JSON.stringify(name)}. Use letters, digits, hyphen, and underscore only (no path separators).`
|
|
266
|
-
);
|
|
267
|
-
this.name = "InvalidFormNameError";
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
var FORM_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
271
|
-
function defineForm(input) {
|
|
272
|
-
if (typeof input.name !== "string" || !FORM_NAME_PATTERN.test(input.name)) {
|
|
273
|
-
throw new InvalidFormNameError(input.name);
|
|
274
|
-
}
|
|
275
|
-
for (const [fieldName, descriptor] of Object.entries(input.schema)) {
|
|
276
|
-
const kind = descriptor.kind;
|
|
277
|
-
if (FORM_FORBIDDEN_KINDS.has(kind)) {
|
|
278
|
-
throw new InvalidFormFieldError(input.name, fieldName, kind);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (input.honeypot !== void 0) {
|
|
282
|
-
if (typeof input.honeypot !== "string" || input.honeypot === "") {
|
|
283
|
-
throw new HoneypotCollisionError(input.name, input.honeypot, "empty");
|
|
284
|
-
}
|
|
285
|
-
if (Object.prototype.hasOwnProperty.call(input.schema, input.honeypot)) {
|
|
286
|
-
throw new HoneypotCollisionError(input.name, input.honeypot, "collision");
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return input.honeypot !== void 0 ? { name: input.name, schema: input.schema, honeypot: input.honeypot } : { name: input.name, schema: input.schema };
|
|
290
|
-
}
|
|
291
195
|
export {
|
|
292
196
|
BooleanField,
|
|
293
197
|
ButtonField,
|
|
294
|
-
FormOverridesField,
|
|
295
|
-
HoneypotCollisionError,
|
|
296
198
|
ImageField,
|
|
297
|
-
InvalidFormFieldError,
|
|
298
|
-
InvalidFormNameError,
|
|
299
199
|
LinkField,
|
|
300
200
|
ListField,
|
|
301
201
|
NumberField,
|
|
302
202
|
ReferenceField,
|
|
303
203
|
RichTextField,
|
|
304
204
|
SelectField,
|
|
305
|
-
SubmissionsNotReadableError,
|
|
306
205
|
TextField,
|
|
307
206
|
VideoField,
|
|
308
|
-
defineForm,
|
|
309
207
|
defineSection,
|
|
310
208
|
hasUniqueSectionIds,
|
|
311
209
|
hrefOf,
|
|
@@ -1,103 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-field override record. Every property is optional; an absent property
|
|
3
|
-
* means "fall back to the form schema's behaviour for this field":
|
|
4
|
-
*
|
|
5
|
-
* - `label` — visible label. Defaults to `titleCase(fieldName)`.
|
|
6
|
-
* Field NAME (the payload key) is NEVER renamable.
|
|
7
|
-
* - `placeholder` — input placeholder text.
|
|
8
|
-
* - `helpText` — small text rendered below the input.
|
|
9
|
-
* - `hidden` — when `true`, the field is not rendered. If `default`
|
|
10
|
-
* is also set, the default value is included in the
|
|
11
|
-
* submitted payload exactly as if the user had typed it.
|
|
12
|
-
* If no default, the field is omitted entirely (the
|
|
13
|
-
* submit handler will reject if required).
|
|
14
|
-
* - `order` — sort key. Lower values render first. Fields without
|
|
15
|
-
* `order` render last in their original schema order
|
|
16
|
-
* (stable). Ties broken by schema order.
|
|
17
|
-
* - `default` — initial value seeded into the input. Stored as
|
|
18
|
-
* `unknown` because the override record is keyed by
|
|
19
|
-
* raw field name with no compile-time link back to
|
|
20
|
-
* the form schema. The `<Form>` component narrows
|
|
21
|
-
* per descriptor kind at render time and falls back
|
|
22
|
-
* to the schema default (with a one-shot console
|
|
23
|
-
* warning) when the stored shape doesn't match.
|
|
24
|
-
*
|
|
25
|
-
* Both the interface and the property values are `readonly` — overrides
|
|
26
|
-
* are content (snapshot-versioned, JSON-serialisable) and the runtime
|
|
27
|
-
* never mutates them after read.
|
|
28
|
-
*/
|
|
29
|
-
interface FormFieldOverride {
|
|
30
|
-
readonly label?: string;
|
|
31
|
-
readonly placeholder?: string;
|
|
32
|
-
readonly helpText?: string;
|
|
33
|
-
readonly hidden?: boolean;
|
|
34
|
-
readonly order?: number;
|
|
35
|
-
/**
|
|
36
|
-
* Initial value seeded into the field. Type is `unknown` because there
|
|
37
|
-
* is no compile-time link from the keyed field name back to the form
|
|
38
|
-
* schema. See file header for narrowing/fallback policy.
|
|
39
|
-
*/
|
|
40
|
-
readonly default?: unknown;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Map from field name to override record. Keys are the form schema's
|
|
44
|
-
* raw field names (the payload keys). Fields not present in the map use
|
|
45
|
-
* schema defaults.
|
|
46
|
-
*
|
|
47
|
-
* Stored as a plain `Record` rather than `Readonly<Record<...>>` so it
|
|
48
|
-
* round-trips through `JSON.parse` cleanly (parsed objects are plain
|
|
49
|
-
* mutable objects). The runtime treats it as read-only by convention.
|
|
50
|
-
*/
|
|
51
|
-
type FormFieldOverrides = Record<string, FormFieldOverride>;
|
|
52
|
-
/**
|
|
53
|
-
* A field descriptor a section author uses to expose per-section-instance
|
|
54
|
-
* form overrides as content. The `formName` references a registered
|
|
55
|
-
* `FormDefinition` by name (NOT a function reference, so the value is
|
|
56
|
-
* RSC-serialisable and can travel through the section data record like
|
|
57
|
-
* any other field).
|
|
58
|
-
*
|
|
59
|
-
* NOT allowed inside a form schema (forbidden via `FORM_FORBIDDEN_KINDS`
|
|
60
|
-
* in `domain/form.ts`). Forms cannot contain a form-overrides field —
|
|
61
|
-
* that would be a recursive content shape with no ergonomic editor UI.
|
|
62
|
-
*
|
|
63
|
-
* The descriptor's runtime value type is `FormFieldOverrides` (see
|
|
64
|
-
* `FieldValueFor` in `domain/schema.ts`).
|
|
65
|
-
*/
|
|
66
|
-
interface FormOverridesFieldDescriptor {
|
|
67
|
-
readonly kind: 'formOverrides';
|
|
68
|
-
/**
|
|
69
|
-
* Name of the form whose fields this descriptor overrides. Must match
|
|
70
|
-
* a form registered in `defineConfig({ forms: [...] })`. The runtime
|
|
71
|
-
* does NOT validate this at section-definition time (the form
|
|
72
|
-
* registry isn't visible to `defineSection`); the editor widget shows
|
|
73
|
-
* a "form not found" message instead of crashing.
|
|
74
|
-
*/
|
|
75
|
-
readonly formName: string;
|
|
76
|
-
/**
|
|
77
|
-
* Default override map for a freshly inserted section. Defaults to
|
|
78
|
-
* `{}` (i.e. all fields fall back to schema defaults) when omitted —
|
|
79
|
-
* the conditional spread in `FormOverridesField()` keeps the property
|
|
80
|
-
* absent under `exactOptionalPropertyTypes`.
|
|
81
|
-
*/
|
|
82
|
-
readonly default?: FormFieldOverrides;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Factory for a `FormOverridesFieldDescriptor`.
|
|
86
|
-
*
|
|
87
|
-
* `formName` matches the `name` of the `FormDefinition` produced by
|
|
88
|
-
* `defineForm()`. The factory itself does NOT validate that the name
|
|
89
|
-
* resolves to a registered form — registry lookup happens lazily in
|
|
90
|
-
* the editor widget, with a graceful "form not found" message.
|
|
91
|
-
*
|
|
92
|
-
* Why a factory and not a singleton: every section author binds this
|
|
93
|
-
* descriptor to a specific form name, so there is no useful no-args
|
|
94
|
-
* shape. `LinkField` is a singleton because every link descriptor is
|
|
95
|
-
* structurally identical; `FormOverridesField('contact')` is not.
|
|
96
|
-
*/
|
|
97
|
-
declare const FormOverridesField: (formName: string, opts?: {
|
|
98
|
-
readonly default?: FormFieldOverrides;
|
|
99
|
-
}) => FormOverridesFieldDescriptor;
|
|
100
|
-
|
|
101
1
|
/**
|
|
102
2
|
* A section schema: a record mapping field names to built-in field
|
|
103
3
|
* descriptors. This is the COMPILE-TIME description authors pass to
|
|
@@ -147,16 +47,12 @@ interface ReferenceValue {
|
|
|
147
47
|
* select -> string (one of the descriptor's option values)
|
|
148
48
|
* list -> Array<ListItem<S>> (each item: derived data shape of S
|
|
149
49
|
* plus opaque `_id`)
|
|
150
|
-
* formOverrides -> FormFieldOverrides
|
|
151
|
-
* (record of per-field overrides for
|
|
152
|
-
* a section instance pointing at a
|
|
153
|
-
* named form; see domain/formOverrides.ts)
|
|
154
50
|
*
|
|
155
51
|
* The `list` case recurses through the schema. The recursion has to live
|
|
156
52
|
* on this side of the descriptor/value seam because `ListField<S>`'s
|
|
157
53
|
* value depends on `FieldValueFor` applied to every member of `S`.
|
|
158
54
|
*/
|
|
159
|
-
type FieldValueFor<F extends FieldDescriptor> = F extends TextField ? string : F extends RichTextField ? string : F extends ImageField ? ImageValue : F extends VideoField ? VideoValue : F extends ReferenceField ? ReferenceValue : F extends LinkField ? LinkValue : F extends ButtonField ? ButtonValue : F extends NumberField ? number : F extends BooleanField ? boolean : F extends SelectField ? string : F extends ListField<infer S> ? ReadonlyArray<ListItem<S>> :
|
|
55
|
+
type FieldValueFor<F extends FieldDescriptor> = F extends TextField ? string : F extends RichTextField ? string : F extends ImageField ? ImageValue : F extends VideoField ? VideoValue : F extends ReferenceField ? ReferenceValue : F extends LinkField ? LinkValue : F extends ButtonField ? ButtonValue : F extends NumberField ? number : F extends BooleanField ? boolean : F extends SelectField ? string : F extends ListField<infer S> ? ReadonlyArray<ListItem<S>> : never;
|
|
160
56
|
/**
|
|
161
57
|
* The runtime shape of a single item in a `ListField<S>` value.
|
|
162
58
|
*
|
|
@@ -281,15 +177,8 @@ type ListItemDefault<S extends SectionSchema> = {
|
|
|
281
177
|
* This is the v1 vocabulary for describing a section's schema. It is closed
|
|
282
178
|
* on purpose: consistency of the editing UI depends on the runtime knowing
|
|
283
179
|
* every possible field type ahead of time.
|
|
284
|
-
*
|
|
285
|
-
* `FormOverridesFieldDescriptor` is section-only: it is a member of this
|
|
286
|
-
* union (so a section schema can carry it) but is explicitly listed in
|
|
287
|
-
* `FORM_FORBIDDEN_KINDS` (see `domain/form.ts`), so a form schema cannot
|
|
288
|
-
* carry one. The descriptor lives in `domain/formOverrides.ts` to keep
|
|
289
|
-
* the override-shape types (`FormFieldOverride`, `FormFieldOverrides`) in
|
|
290
|
-
* the same file as the descriptor that produces them.
|
|
291
180
|
*/
|
|
292
|
-
type FieldDescriptor = TextField | RichTextField | ImageField | VideoField | ReferenceField | LinkField | ButtonField | NumberField | BooleanField | SelectField | ListField
|
|
181
|
+
type FieldDescriptor = TextField | RichTextField | ImageField | VideoField | ReferenceField | LinkField | ButtonField | NumberField | BooleanField | SelectField | ListField;
|
|
293
182
|
/** String literal union of every descriptor's `kind` — useful for maps and tables. */
|
|
294
183
|
type FieldKind = FieldDescriptor['kind'];
|
|
295
184
|
/** Plain inline text (single-line or small multi-line without formatting). */
|
|
@@ -571,183 +460,4 @@ interface Page {
|
|
|
571
460
|
*/
|
|
572
461
|
type PageSummary = Omit<Page, 'sections'>;
|
|
573
462
|
|
|
574
|
-
|
|
575
|
-
* Closed union of field descriptors allowed in a form schema in v1.
|
|
576
|
-
*
|
|
577
|
-
* Why this is a strict subset of `FieldDescriptor`:
|
|
578
|
-
* - `image` → file upload is deferred (§6.5, §12).
|
|
579
|
-
* - `video` → forms collect text/numbers/etc. from end users; a
|
|
580
|
-
* public submission carrying an embed URL is a semantic
|
|
581
|
-
* outlier (videos are authoring content, not user input).
|
|
582
|
-
* - `reference` → semantically odd for user input — references are an
|
|
583
|
-
* authoring construct, not a public-site concern.
|
|
584
|
-
* - `list` → payload validation becomes recursive; YAGNI for the
|
|
585
|
-
* newsletter/contact use cases v1 targets.
|
|
586
|
-
*
|
|
587
|
-
* Keeping this set closed at the type level makes the restriction visible
|
|
588
|
-
* to authors at compile time. The runtime check in `defineForm()` provides
|
|
589
|
-
* the same guarantee for callers that erase types via `as`.
|
|
590
|
-
*/
|
|
591
|
-
type FormFieldDescriptor = TextField | RichTextField | NumberField | BooleanField | SelectField | LinkField;
|
|
592
|
-
/**
|
|
593
|
-
* A form schema: a record mapping field names to allowed form-field descriptors.
|
|
594
|
-
*
|
|
595
|
-
* Defined as a plain `Record` (no `Readonly<>`) for the same `keyof S`
|
|
596
|
-
* preservation reason described in `domain/schema.ts`'s `SectionSchema`.
|
|
597
|
-
*/
|
|
598
|
-
type FormSchema = Record<string, FormFieldDescriptor>;
|
|
599
|
-
/**
|
|
600
|
-
* Registration record produced by `defineForm`. The runtime registry
|
|
601
|
-
* (`forms/registry.ts`) stores these keyed by `name`, mirroring the
|
|
602
|
-
* section registry.
|
|
603
|
-
*
|
|
604
|
-
* `honeypot` is the OPTIONAL name of an additional hidden field the
|
|
605
|
-
* frontend renders out-of-schema (e.g. `<input name="website" style="display:none">`).
|
|
606
|
-
* If a non-empty value arrives in a submit payload at that key, the server
|
|
607
|
-
* suppresses the submission silently. On the wire the response is
|
|
608
|
-
* indistinguishable from a successful no-op (200 OK with `{ ok: true,
|
|
609
|
-
* stored: false }`) — by design, so a bot cannot tell from the response
|
|
610
|
-
* whether it was filtered. The `suppressed: 'honeypot'` marker is INTERNAL
|
|
611
|
-
* to the runtime's `SubmitFormResult` discriminant (see
|
|
612
|
-
* `runtime/submitForm.ts`); the submit route handler strips it before
|
|
613
|
-
* returning. The honeypot field name MUST NOT collide with any real schema
|
|
614
|
-
* field name — `defineForm()` enforces this.
|
|
615
|
-
*/
|
|
616
|
-
interface FormDefinition<S extends FormSchema = FormSchema> {
|
|
617
|
-
/** Unique name. Used as the URL-safe identifier in submit/list/read endpoints. */
|
|
618
|
-
readonly name: string;
|
|
619
|
-
/** Field descriptors. */
|
|
620
|
-
readonly schema: S;
|
|
621
|
-
/** Optional honeypot field name (out-of-schema). See type doc. */
|
|
622
|
-
readonly honeypot?: string;
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* Erased/heterogeneous form definition for use in lists (e.g.
|
|
626
|
-
* `defineConfig({ forms: [Contact, Newsletter] })`). Mirrors the
|
|
627
|
-
* `AnySectionDefinition` pattern in `sections/defineSection.ts`.
|
|
628
|
-
*/
|
|
629
|
-
interface AnyFormDefinition {
|
|
630
|
-
readonly name: string;
|
|
631
|
-
readonly schema: FormSchema;
|
|
632
|
-
readonly honeypot?: string;
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Minimal lookup interface for a registry of form definitions.
|
|
636
|
-
*
|
|
637
|
-
* Lives in `domain/` because the runtime (`runtime/submitForm.ts`) needs
|
|
638
|
-
* to depend on "something that can look up a form by name" without having
|
|
639
|
-
* to import from the `forms/` sibling. This keeps the dependency graph
|
|
640
|
-
* `runtime → domain, storage` (ARCHITECTURE.md §8) intact.
|
|
641
|
-
*
|
|
642
|
-
* The concrete registry built by `forms/registry.ts` (`FormRegistry`)
|
|
643
|
-
* structurally satisfies this interface — no wrapping needed at the call
|
|
644
|
-
* site. `definitions` is included so handlers can enumerate registered
|
|
645
|
-
* forms (for `forms/list`).
|
|
646
|
-
*/
|
|
647
|
-
interface FormLookup {
|
|
648
|
-
readonly definitions: ReadonlyArray<AnyFormDefinition>;
|
|
649
|
-
get(name: string): AnyFormDefinition | undefined;
|
|
650
|
-
has(name: string): boolean;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* A single form submission stored by a `SubmissionStorageAdapter`.
|
|
654
|
-
*
|
|
655
|
-
* Shape is intentionally flat:
|
|
656
|
-
* - `formName` selects the form schema this submission belongs to.
|
|
657
|
-
* - `payload` is the validated record of field values, opaque to the
|
|
658
|
-
* storage layer (storage doesn't know schemas; the runtime validates
|
|
659
|
-
* before calling the adapter).
|
|
660
|
-
* - `submittedAt` is an ISO-8601 string. We use a string rather than a
|
|
661
|
-
* `Date` because submissions cross JSON boundaries (route handler →
|
|
662
|
-
* adapter → potentially webhook → potentially admin UI) and
|
|
663
|
-
* `Date.toISOString()` round-tripping has known edge cases.
|
|
664
|
-
* - `id` is an opaque, lex-sortable identifier. The FS adapter uses it
|
|
665
|
-
* as a tie-breaker when two submissions share a `submittedAt`
|
|
666
|
-
* millisecond.
|
|
667
|
-
*
|
|
668
|
-
* Privacy note: the IP address is NOT stored by default (ARCHITECTURE.md
|
|
669
|
-
* §6.5 / §11). Adding it would be a config-flag change.
|
|
670
|
-
*/
|
|
671
|
-
interface Submission {
|
|
672
|
-
readonly formName: string;
|
|
673
|
-
/**
|
|
674
|
-
* Validated payload. Each value type matches the runtime type of its
|
|
675
|
-
* declared `FormFieldDescriptor` (see `formPayloadValueFor`). Stored
|
|
676
|
-
* as `unknown` here so the storage layer stays schema-agnostic.
|
|
677
|
-
*/
|
|
678
|
-
readonly payload: Readonly<Record<string, unknown>>;
|
|
679
|
-
/** ISO-8601 timestamp of when the submit handler accepted the payload. */
|
|
680
|
-
readonly submittedAt: string;
|
|
681
|
-
/** Lex-sortable opaque identifier (ULID-like). */
|
|
682
|
-
readonly id: string;
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Lightweight summary of a stored submission, returned by
|
|
686
|
-
* `SubmissionStorageAdapter.list`. The full payload is fetched on demand
|
|
687
|
-
* via `read` so the AdminModal list view stays cheap even when a form
|
|
688
|
-
* accumulates thousands of entries.
|
|
689
|
-
*/
|
|
690
|
-
interface SubmissionSummary {
|
|
691
|
-
readonly id: string;
|
|
692
|
-
readonly submittedAt: string;
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Public adapter-info descriptor surfaced through the runtime so handlers
|
|
696
|
-
* (notably the frozen `forms/list` route) can tell the AdminModal whether
|
|
697
|
-
* submissions are locally readable. Two shapes:
|
|
698
|
-
* - `{ kind: 'fs' }` — local FS submissions; readable.
|
|
699
|
-
* - `{ kind: 'webhook', host }` — webhook adapter; not locally readable.
|
|
700
|
-
*
|
|
701
|
-
* The host is the `URL.host` portion of the configured webhook (no path,
|
|
702
|
-
* no query, no credentials) — surfaced so the AdminModal can render a
|
|
703
|
-
* "POSTed to <host>" hint without leaking secrets.
|
|
704
|
-
*/
|
|
705
|
-
type SubmissionAdapterInfo = {
|
|
706
|
-
readonly kind: 'fs';
|
|
707
|
-
} | {
|
|
708
|
-
readonly kind: 'webhook';
|
|
709
|
-
readonly host: string;
|
|
710
|
-
};
|
|
711
|
-
/**
|
|
712
|
-
* Storage adapter for form submissions (ARCHITECTURE.md §5, §6.5).
|
|
713
|
-
*
|
|
714
|
-
* Implementations:
|
|
715
|
-
* - FS: writes `<root>/<formName>/<submittedAt>-<id>.json`. Default.
|
|
716
|
-
* - Webhook: POSTs each submission to a configured URL; does NOT write
|
|
717
|
-
* to disk. `list`/`read` throw if called (no local copy).
|
|
718
|
-
*
|
|
719
|
-
* `store` is the only mandatory write path. `list`/`read` are required
|
|
720
|
-
* to satisfy AdminModal's "Submissions" tab (Dispatch 2). A webhook-only
|
|
721
|
-
* setup signals "no local copies" by throwing a typed error so the route
|
|
722
|
-
* handler can return a clean 405.
|
|
723
|
-
*
|
|
724
|
-
* `info` is a small introspection field. Frozen route handlers wire the
|
|
725
|
-
* adapter into `createFormsListHandler` so the AdminModal knows whether
|
|
726
|
-
* to attempt `/forms/read` (FS) or render a "remote" hint (webhook).
|
|
727
|
-
* Without it the handler would default to `kind: 'fs'` and the modal
|
|
728
|
-
* would issue 501-returning fetches against a webhook adapter.
|
|
729
|
-
*
|
|
730
|
-
* Implementations MUST NOT throw for "form has no submissions"; `list`
|
|
731
|
-
* MUST return an empty array in that case. `read(formName, id)` MUST
|
|
732
|
-
* return `null` for an unknown id (mirrors `ContentStorageAdapter.readPage`).
|
|
733
|
-
*/
|
|
734
|
-
interface SubmissionStorageAdapter {
|
|
735
|
-
/** Static descriptor of this adapter. See `SubmissionAdapterInfo`. */
|
|
736
|
-
readonly info: SubmissionAdapterInfo;
|
|
737
|
-
/** Persist a submission. Returns void; the runtime stamps id+submittedAt. */
|
|
738
|
-
store(submission: Submission): Promise<void>;
|
|
739
|
-
/** List submissions for a form, newest first. Returns [] if none. */
|
|
740
|
-
list(formName: string): Promise<ReadonlyArray<SubmissionSummary>>;
|
|
741
|
-
/** Read a single submission. Returns `null` if the id is unknown. */
|
|
742
|
-
read(formName: string, id: string): Promise<Submission | null>;
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Thrown by adapters that have no local read surface (e.g. webhook-only).
|
|
746
|
-
* The route handler maps this to HTTP 501 / 405 so the AdminModal can
|
|
747
|
-
* surface a helpful message.
|
|
748
|
-
*/
|
|
749
|
-
declare class SubmissionsNotReadableError extends Error {
|
|
750
|
-
constructor(message?: string);
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
export { type AnyFormDefinition as A, ButtonField as B, type PageSeo as C, type Section as D, type SelectOption as E, type FormLookup as F, ImageField as I, LinkField as L, NumberField as N, type Page as P, RichTextField as R, type SectionSchema as S, TextField as T, VideoField as V, type FormSchema as a, type FormDefinition as b, type FieldDescriptor as c, type ImageValue as d, type VideoValue as e, ReferenceField as f, type ReferenceValue as g, type LinkValue as h, type ButtonValue as i, BooleanField as j, SelectField as k, ListField as l, type FormOverridesFieldDescriptor as m, type FormFieldOverrides as n, type SubmissionStorageAdapter as o, type FormFieldDescriptor as p, type FormFieldOverride as q, FormOverridesField as r, type Submission as s, type SubmissionAdapterInfo as t, type SubmissionSummary as u, SubmissionsNotReadableError as v, type PageSummary as w, type FieldValueFor as x, type FieldKind as y, type ListItem as z };
|
|
463
|
+
export { ButtonField as B, type FieldDescriptor as F, ImageField as I, LinkField as L, NumberField as N, type Page as P, RichTextField as R, type SectionSchema as S, TextField as T, VideoField as V, type ImageValue as a, type VideoValue as b, ReferenceField as c, type ReferenceValue as d, type LinkValue as e, type ButtonValue as f, BooleanField as g, SelectField as h, ListField as i, type PageSummary as j, type FieldValueFor as k, type FieldKind as l, type ListItem as m, type PageSeo as n, type Section as o, type SelectOption as p };
|