@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.
@@ -0,0 +1,240 @@
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/_config.ts
21
+ var config_exports = {};
22
+ __export(config_exports, {
23
+ BooleanField: () => BooleanField,
24
+ ButtonField: () => ButtonField,
25
+ DuplicateFormNameError: () => DuplicateFormNameError,
26
+ FormOverridesField: () => FormOverridesField,
27
+ HoneypotCollisionError: () => HoneypotCollisionError,
28
+ ImageField: () => ImageField,
29
+ InvalidFormFieldError: () => InvalidFormFieldError,
30
+ InvalidFormNameError: () => InvalidFormNameError,
31
+ LinkField: () => LinkField,
32
+ ListField: () => ListField,
33
+ NumberField: () => NumberField,
34
+ ReferenceField: () => ReferenceField,
35
+ RichTextField: () => RichTextField,
36
+ SelectField: () => SelectField,
37
+ SubmissionsNotReadableError: () => SubmissionsNotReadableError,
38
+ TextField: () => TextField,
39
+ VideoField: () => VideoField,
40
+ defineConfig: () => defineConfig,
41
+ defineForm: () => defineForm,
42
+ withagntcms: () => withagntcms
43
+ });
44
+ module.exports = __toCommonJS(config_exports);
45
+
46
+ // src/config/defaults-registry.ts
47
+ var SLOT = /* @__PURE__ */ Symbol.for("@agntcms/next/default-adapter-factories");
48
+ function holder() {
49
+ return globalThis;
50
+ }
51
+ function getDefaultAdapterFactories(requested) {
52
+ const f = holder()[SLOT];
53
+ if (f === void 0) {
54
+ throw new Error(
55
+ `[agntcms] defineConfig: cannot fill the default ${requested} adapter because the server-only adapter factories were never registered. Import \`@agntcms/next/server\` (or \`@agntcms/next/handlers\`) before evaluating \`agntcms/config.ts\`, or pass an explicit adapter to \`defineConfig({ contentAdapter, assetAdapter, submissionAdapter })\`.`
56
+ );
57
+ }
58
+ return f;
59
+ }
60
+
61
+ // src/config/defineConfig.ts
62
+ function defineConfig(config) {
63
+ if (config.sections.length === 0) {
64
+ console.warn(
65
+ "[agntcms] defineConfig: `sections` array is empty. No sections will be available for rendering."
66
+ );
67
+ }
68
+ const contentAdapter = config.contentAdapter ?? getDefaultAdapterFactories("content").content();
69
+ const assetAdapter = config.assetAdapter ?? getDefaultAdapterFactories("asset").asset();
70
+ const submissionAdapter = config.submissionAdapter ?? getDefaultAdapterFactories("submission").submission();
71
+ return {
72
+ sections: config.sections,
73
+ contentAdapter,
74
+ assetAdapter,
75
+ templates: config.templates ?? [],
76
+ forms: config.forms ?? [],
77
+ submissionAdapter,
78
+ submissionRateLimit: config.submissionRateLimit ?? { perMinute: 5 }
79
+ };
80
+ }
81
+
82
+ // src/config/withagntcms.ts
83
+ function withagntcms(nextConfig) {
84
+ return {
85
+ ...nextConfig,
86
+ output: "standalone"
87
+ };
88
+ }
89
+
90
+ // src/domain/form.ts
91
+ var FORM_FORBIDDEN_KINDS = /* @__PURE__ */ new Set([
92
+ "image",
93
+ "video",
94
+ "reference",
95
+ "list",
96
+ // `formOverrides` is a section-only descriptor (it overrides another
97
+ // form schema instance). Putting it inside a form would mean a form's
98
+ // payload could carry overrides for itself or another form — a
99
+ // recursive shape with no ergonomic editor UI. Section-only by design.
100
+ "formOverrides",
101
+ // `button` is a section-only descriptor: a styled CTA with an
102
+ // optional link. Public-form payloads collect user input — a button
103
+ // value is authored content, not a submitted answer. Section-only
104
+ // by design (mirrors `formOverrides`).
105
+ "button"
106
+ ]);
107
+ var SubmissionsNotReadableError = class extends Error {
108
+ constructor(message = "submission adapter does not support reading") {
109
+ super(message);
110
+ this.name = "SubmissionsNotReadableError";
111
+ }
112
+ };
113
+
114
+ // src/forms/defineForm.ts
115
+ var InvalidFormFieldError = class extends Error {
116
+ formName;
117
+ fieldName;
118
+ fieldKind;
119
+ constructor(formName, fieldName, fieldKind) {
120
+ super(
121
+ `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.`
122
+ );
123
+ this.name = "InvalidFormFieldError";
124
+ this.formName = formName;
125
+ this.fieldName = fieldName;
126
+ this.fieldKind = fieldKind;
127
+ }
128
+ };
129
+ var HoneypotCollisionError = class extends Error {
130
+ formName;
131
+ fieldName;
132
+ reason;
133
+ constructor(formName, fieldName, reason = "collision") {
134
+ super(
135
+ 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.`
136
+ );
137
+ this.name = "HoneypotCollisionError";
138
+ this.formName = formName;
139
+ this.fieldName = fieldName;
140
+ this.reason = reason;
141
+ }
142
+ };
143
+ var InvalidFormNameError = class extends Error {
144
+ constructor(name) {
145
+ super(
146
+ `Invalid form name ${JSON.stringify(name)}. Use letters, digits, hyphen, and underscore only (no path separators).`
147
+ );
148
+ this.name = "InvalidFormNameError";
149
+ }
150
+ };
151
+ var FORM_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
152
+ function defineForm(input) {
153
+ if (typeof input.name !== "string" || !FORM_NAME_PATTERN.test(input.name)) {
154
+ throw new InvalidFormNameError(input.name);
155
+ }
156
+ for (const [fieldName, descriptor] of Object.entries(input.schema)) {
157
+ const kind = descriptor.kind;
158
+ if (FORM_FORBIDDEN_KINDS.has(kind)) {
159
+ throw new InvalidFormFieldError(input.name, fieldName, kind);
160
+ }
161
+ }
162
+ if (input.honeypot !== void 0) {
163
+ if (typeof input.honeypot !== "string" || input.honeypot === "") {
164
+ throw new HoneypotCollisionError(input.name, input.honeypot, "empty");
165
+ }
166
+ if (Object.prototype.hasOwnProperty.call(input.schema, input.honeypot)) {
167
+ throw new HoneypotCollisionError(input.name, input.honeypot, "collision");
168
+ }
169
+ }
170
+ return input.honeypot !== void 0 ? { name: input.name, schema: input.schema, honeypot: input.honeypot } : { name: input.name, schema: input.schema };
171
+ }
172
+
173
+ // src/forms/registry.ts
174
+ var DuplicateFormNameError = class extends Error {
175
+ formName;
176
+ constructor(name) {
177
+ super(
178
+ `Form name "${name}" is registered more than once. Form names must be unique within a config.`
179
+ );
180
+ this.name = "DuplicateFormNameError";
181
+ this.formName = name;
182
+ }
183
+ };
184
+
185
+ // src/domain/fields.ts
186
+ var TextField = { kind: "text" };
187
+ var RichTextField = { kind: "richText" };
188
+ var ImageField = { kind: "image" };
189
+ var VideoField = { kind: "video" };
190
+ var ReferenceField = { kind: "reference" };
191
+ var LinkField = { kind: "link" };
192
+ var NumberField = { kind: "number" };
193
+ var BooleanField = { kind: "boolean" };
194
+ var SelectField = (options, opts) => ({
195
+ kind: "select",
196
+ options,
197
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
198
+ });
199
+ var ButtonField = (variants, opts) => ({
200
+ kind: "button",
201
+ variants,
202
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
203
+ });
204
+ var ListField = (itemSchema, opts) => ({
205
+ kind: "list",
206
+ itemSchema,
207
+ ...opts?.min !== void 0 ? { min: opts.min } : {},
208
+ ...opts?.max !== void 0 ? { max: opts.max } : {},
209
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
210
+ });
211
+
212
+ // src/domain/formOverrides.ts
213
+ var FormOverridesField = (formName, opts) => ({
214
+ kind: "formOverrides",
215
+ formName,
216
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
217
+ });
218
+ // Annotate the CommonJS export names for ESM import in node:
219
+ 0 && (module.exports = {
220
+ BooleanField,
221
+ ButtonField,
222
+ DuplicateFormNameError,
223
+ FormOverridesField,
224
+ HoneypotCollisionError,
225
+ ImageField,
226
+ InvalidFormFieldError,
227
+ InvalidFormNameError,
228
+ LinkField,
229
+ ListField,
230
+ NumberField,
231
+ ReferenceField,
232
+ RichTextField,
233
+ SelectField,
234
+ SubmissionsNotReadableError,
235
+ TextField,
236
+ VideoField,
237
+ defineConfig,
238
+ defineForm,
239
+ withagntcms
240
+ });
@@ -0,0 +1,112 @@
1
+ import { A as AnyFormDefinition, o as SubmissionStorageAdapter } from './form-BqY0H1V5.cjs';
2
+ export { j as BooleanField, B as ButtonField, b as FormDefinition, p as FormFieldDescriptor, q as FormFieldOverride, n as FormFieldOverrides, r as FormOverridesField, m as FormOverridesFieldDescriptor, a as FormSchema, I as ImageField, L as LinkField, l as ListField, N as NumberField, f as ReferenceField, R as RichTextField, k as SelectField, s as Submission, t as SubmissionAdapterInfo, u as SubmissionSummary, v as SubmissionsNotReadableError, T as TextField, V as VideoField } from './form-BqY0H1V5.cjs';
3
+ import { A as AnySectionDefinition } from './defineSection-9qQ5ulAH.cjs';
4
+ import { C as ContentStorageAdapter, A as AssetStorageAdapter } from './assets-Cyt9upqW.cjs';
5
+ export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-CJ8KZC93.cjs';
6
+ export { D as DuplicateFormNameError } from './registry-CraTTwT7.cjs';
7
+ import './global-CV23g5Bn.cjs';
8
+
9
+ /**
10
+ * A named preset that pre-fills sections when creating a new page.
11
+ *
12
+ * Templates are a config-level concept only — after creation, the page is a
13
+ * normal page with no template constraints. The `sectionTypes` array
14
+ * references section names from the registered `sections` list; unknown
15
+ * names are silently skipped at list time.
16
+ */
17
+ interface PageTemplate {
18
+ readonly name: string;
19
+ readonly description?: string;
20
+ /** Ordered list of section type names to pre-fill. */
21
+ readonly sectionTypes: readonly string[];
22
+ }
23
+ /**
24
+ * Submission rate-limit configuration knob (ARCHITECTURE.md §6.5).
25
+ *
26
+ * Single field on purpose — multiplexing perWindow/windowMs/strategy into
27
+ * a struct here would invite YAGNI bikeshedding. The window is fixed at
28
+ * 60 seconds in v1; only the per-window count is user-tunable.
29
+ */
30
+ interface SubmissionRateLimit {
31
+ /** Max submissions per minute per (IP, form) pair. Default: 5. */
32
+ readonly perMinute: number;
33
+ }
34
+ /**
35
+ * User-facing configuration shape passed to `defineConfig`.
36
+ *
37
+ * `sections` is required and must contain at least one definition.
38
+ * Adapters are optional — when omitted, the default FS adapters are used
39
+ * with the canonical template paths.
40
+ */
41
+ interface AgentCmsConfig {
42
+ /** Registered section definitions. Order does not matter. */
43
+ readonly sections: readonly AnySectionDefinition[];
44
+ /** Content storage adapter. Defaults to FS adapter with standard paths. */
45
+ readonly contentAdapter?: ContentStorageAdapter;
46
+ /** Asset storage adapter. Defaults to FS adapter with standard paths. */
47
+ readonly assetAdapter?: AssetStorageAdapter;
48
+ /** Page creation templates. Optional — defaults to [] (blank pages only). */
49
+ readonly templates?: readonly PageTemplate[];
50
+ /**
51
+ * Registered form definitions (ARCHITECTURE.md §6.5). Optional — when
52
+ * omitted, no forms are registered and the submit handler returns 404
53
+ * for every form name.
54
+ */
55
+ readonly forms?: readonly AnyFormDefinition[];
56
+ /**
57
+ * Submission storage adapter. Defaults to the FS adapter rooted at
58
+ * `<projectRoot>/content/submissions`. Use `createWebhookSubmissionAdapter`
59
+ * (or a custom implementation) to send submissions to an external service.
60
+ */
61
+ readonly submissionAdapter?: SubmissionStorageAdapter;
62
+ /**
63
+ * Rate-limit knob for the submit endpoint. Defaults to `{ perMinute: 5 }`.
64
+ * The window is always 60 seconds in v1 — only the count is user-tunable.
65
+ */
66
+ readonly submissionRateLimit?: SubmissionRateLimit;
67
+ }
68
+ /**
69
+ * Fully-resolved configuration with all defaults filled in.
70
+ * Downstream code (runtime factory, handlers) can depend on every
71
+ * field being present.
72
+ */
73
+ interface ResolvedConfig {
74
+ readonly sections: readonly AnySectionDefinition[];
75
+ readonly contentAdapter: ContentStorageAdapter;
76
+ readonly assetAdapter: AssetStorageAdapter;
77
+ /** Page creation templates. Always present (defaults to []). */
78
+ readonly templates: readonly PageTemplate[];
79
+ /** Registered forms. Always present (defaults to []). */
80
+ readonly forms: readonly AnyFormDefinition[];
81
+ /** Submission storage adapter. Always present. */
82
+ readonly submissionAdapter: SubmissionStorageAdapter;
83
+ /** Resolved rate-limit knob. Always present. */
84
+ readonly submissionRateLimit: SubmissionRateLimit;
85
+ }
86
+ /**
87
+ * Resolve a user config into a fully-populated config with defaults.
88
+ *
89
+ * Default adapters:
90
+ * - content: `createFsContentAdapter({ contentRoot: process.cwd() + '/content' })`
91
+ * - asset: `createFsAssetAdapter({ assetsRoot: process.cwd() + '/public/assets', publicUrlBase: '/assets' })`
92
+ *
93
+ * Validates that `sections` is non-empty (warns in dev, does not throw)
94
+ * so that a misconfigured template gets a visible signal rather than
95
+ * silent emptiness at render time.
96
+ */
97
+ declare function defineConfig(config: AgentCmsConfig): ResolvedConfig;
98
+
99
+ /**
100
+ * Wrap a Next.js configuration object for agntcms.
101
+ *
102
+ * Sets `output: 'standalone'` so the production Docker build
103
+ * (which copies `.next/standalone/`) works out of the box.
104
+ *
105
+ * In future versions this may also configure webpack aliases,
106
+ * image optimization domains, or framework-level middleware.
107
+ */
108
+ declare function withagntcms<T extends Record<string, unknown>>(nextConfig: T): T & {
109
+ output: 'standalone';
110
+ };
111
+
112
+ export { type AgentCmsConfig, AnyFormDefinition, type PageTemplate, type ResolvedConfig, type SubmissionRateLimit, SubmissionStorageAdapter, defineConfig, withagntcms };
@@ -0,0 +1,112 @@
1
+ import { A as AnyFormDefinition, o as SubmissionStorageAdapter } from './form-BqY0H1V5.js';
2
+ export { j as BooleanField, B as ButtonField, b as FormDefinition, p as FormFieldDescriptor, q as FormFieldOverride, n as FormFieldOverrides, r as FormOverridesField, m as FormOverridesFieldDescriptor, a as FormSchema, I as ImageField, L as LinkField, l as ListField, N as NumberField, f as ReferenceField, R as RichTextField, k as SelectField, s as Submission, t as SubmissionAdapterInfo, u as SubmissionSummary, v as SubmissionsNotReadableError, T as TextField, V as VideoField } from './form-BqY0H1V5.js';
3
+ import { A as AnySectionDefinition } from './defineSection-Kr0pWqMY.js';
4
+ import { C as ContentStorageAdapter, A as AssetStorageAdapter } from './assets-P8OCigDG.js';
5
+ export { D as DefineFormInput, H as HoneypotCollisionError, I as InvalidFormFieldError, a as InvalidFormNameError, d as defineForm } from './defineForm-Bp9vzW56.js';
6
+ export { D as DuplicateFormNameError } from './registry-DMujGqt0.js';
7
+ import './global-CV23g5Bn.js';
8
+
9
+ /**
10
+ * A named preset that pre-fills sections when creating a new page.
11
+ *
12
+ * Templates are a config-level concept only — after creation, the page is a
13
+ * normal page with no template constraints. The `sectionTypes` array
14
+ * references section names from the registered `sections` list; unknown
15
+ * names are silently skipped at list time.
16
+ */
17
+ interface PageTemplate {
18
+ readonly name: string;
19
+ readonly description?: string;
20
+ /** Ordered list of section type names to pre-fill. */
21
+ readonly sectionTypes: readonly string[];
22
+ }
23
+ /**
24
+ * Submission rate-limit configuration knob (ARCHITECTURE.md §6.5).
25
+ *
26
+ * Single field on purpose — multiplexing perWindow/windowMs/strategy into
27
+ * a struct here would invite YAGNI bikeshedding. The window is fixed at
28
+ * 60 seconds in v1; only the per-window count is user-tunable.
29
+ */
30
+ interface SubmissionRateLimit {
31
+ /** Max submissions per minute per (IP, form) pair. Default: 5. */
32
+ readonly perMinute: number;
33
+ }
34
+ /**
35
+ * User-facing configuration shape passed to `defineConfig`.
36
+ *
37
+ * `sections` is required and must contain at least one definition.
38
+ * Adapters are optional — when omitted, the default FS adapters are used
39
+ * with the canonical template paths.
40
+ */
41
+ interface AgentCmsConfig {
42
+ /** Registered section definitions. Order does not matter. */
43
+ readonly sections: readonly AnySectionDefinition[];
44
+ /** Content storage adapter. Defaults to FS adapter with standard paths. */
45
+ readonly contentAdapter?: ContentStorageAdapter;
46
+ /** Asset storage adapter. Defaults to FS adapter with standard paths. */
47
+ readonly assetAdapter?: AssetStorageAdapter;
48
+ /** Page creation templates. Optional — defaults to [] (blank pages only). */
49
+ readonly templates?: readonly PageTemplate[];
50
+ /**
51
+ * Registered form definitions (ARCHITECTURE.md §6.5). Optional — when
52
+ * omitted, no forms are registered and the submit handler returns 404
53
+ * for every form name.
54
+ */
55
+ readonly forms?: readonly AnyFormDefinition[];
56
+ /**
57
+ * Submission storage adapter. Defaults to the FS adapter rooted at
58
+ * `<projectRoot>/content/submissions`. Use `createWebhookSubmissionAdapter`
59
+ * (or a custom implementation) to send submissions to an external service.
60
+ */
61
+ readonly submissionAdapter?: SubmissionStorageAdapter;
62
+ /**
63
+ * Rate-limit knob for the submit endpoint. Defaults to `{ perMinute: 5 }`.
64
+ * The window is always 60 seconds in v1 — only the count is user-tunable.
65
+ */
66
+ readonly submissionRateLimit?: SubmissionRateLimit;
67
+ }
68
+ /**
69
+ * Fully-resolved configuration with all defaults filled in.
70
+ * Downstream code (runtime factory, handlers) can depend on every
71
+ * field being present.
72
+ */
73
+ interface ResolvedConfig {
74
+ readonly sections: readonly AnySectionDefinition[];
75
+ readonly contentAdapter: ContentStorageAdapter;
76
+ readonly assetAdapter: AssetStorageAdapter;
77
+ /** Page creation templates. Always present (defaults to []). */
78
+ readonly templates: readonly PageTemplate[];
79
+ /** Registered forms. Always present (defaults to []). */
80
+ readonly forms: readonly AnyFormDefinition[];
81
+ /** Submission storage adapter. Always present. */
82
+ readonly submissionAdapter: SubmissionStorageAdapter;
83
+ /** Resolved rate-limit knob. Always present. */
84
+ readonly submissionRateLimit: SubmissionRateLimit;
85
+ }
86
+ /**
87
+ * Resolve a user config into a fully-populated config with defaults.
88
+ *
89
+ * Default adapters:
90
+ * - content: `createFsContentAdapter({ contentRoot: process.cwd() + '/content' })`
91
+ * - asset: `createFsAssetAdapter({ assetsRoot: process.cwd() + '/public/assets', publicUrlBase: '/assets' })`
92
+ *
93
+ * Validates that `sections` is non-empty (warns in dev, does not throw)
94
+ * so that a misconfigured template gets a visible signal rather than
95
+ * silent emptiness at render time.
96
+ */
97
+ declare function defineConfig(config: AgentCmsConfig): ResolvedConfig;
98
+
99
+ /**
100
+ * Wrap a Next.js configuration object for agntcms.
101
+ *
102
+ * Sets `output: 'standalone'` so the production Docker build
103
+ * (which copies `.next/standalone/`) works out of the box.
104
+ *
105
+ * In future versions this may also configure webpack aliases,
106
+ * image optimization domains, or framework-level middleware.
107
+ */
108
+ declare function withagntcms<T extends Record<string, unknown>>(nextConfig: T): T & {
109
+ output: 'standalone';
110
+ };
111
+
112
+ export { type AgentCmsConfig, AnyFormDefinition, type PageTemplate, type ResolvedConfig, type SubmissionRateLimit, SubmissionStorageAdapter, defineConfig, withagntcms };
@@ -0,0 +1,194 @@
1
+ // src/config/defaults-registry.ts
2
+ var SLOT = /* @__PURE__ */ Symbol.for("@agntcms/next/default-adapter-factories");
3
+ function holder() {
4
+ return globalThis;
5
+ }
6
+ function getDefaultAdapterFactories(requested) {
7
+ const f = holder()[SLOT];
8
+ if (f === void 0) {
9
+ throw new Error(
10
+ `[agntcms] defineConfig: cannot fill the default ${requested} adapter because the server-only adapter factories were never registered. Import \`@agntcms/next/server\` (or \`@agntcms/next/handlers\`) before evaluating \`agntcms/config.ts\`, or pass an explicit adapter to \`defineConfig({ contentAdapter, assetAdapter, submissionAdapter })\`.`
11
+ );
12
+ }
13
+ return f;
14
+ }
15
+
16
+ // src/config/defineConfig.ts
17
+ function defineConfig(config) {
18
+ if (config.sections.length === 0) {
19
+ console.warn(
20
+ "[agntcms] defineConfig: `sections` array is empty. No sections will be available for rendering."
21
+ );
22
+ }
23
+ const contentAdapter = config.contentAdapter ?? getDefaultAdapterFactories("content").content();
24
+ const assetAdapter = config.assetAdapter ?? getDefaultAdapterFactories("asset").asset();
25
+ const submissionAdapter = config.submissionAdapter ?? getDefaultAdapterFactories("submission").submission();
26
+ return {
27
+ sections: config.sections,
28
+ contentAdapter,
29
+ assetAdapter,
30
+ templates: config.templates ?? [],
31
+ forms: config.forms ?? [],
32
+ submissionAdapter,
33
+ submissionRateLimit: config.submissionRateLimit ?? { perMinute: 5 }
34
+ };
35
+ }
36
+
37
+ // src/config/withagntcms.ts
38
+ function withagntcms(nextConfig) {
39
+ return {
40
+ ...nextConfig,
41
+ output: "standalone"
42
+ };
43
+ }
44
+
45
+ // src/domain/form.ts
46
+ var FORM_FORBIDDEN_KINDS = /* @__PURE__ */ new Set([
47
+ "image",
48
+ "video",
49
+ "reference",
50
+ "list",
51
+ // `formOverrides` is a section-only descriptor (it overrides another
52
+ // form schema instance). Putting it inside a form would mean a form's
53
+ // payload could carry overrides for itself or another form — a
54
+ // recursive shape with no ergonomic editor UI. Section-only by design.
55
+ "formOverrides",
56
+ // `button` is a section-only descriptor: a styled CTA with an
57
+ // optional link. Public-form payloads collect user input — a button
58
+ // value is authored content, not a submitted answer. Section-only
59
+ // by design (mirrors `formOverrides`).
60
+ "button"
61
+ ]);
62
+ var SubmissionsNotReadableError = class extends Error {
63
+ constructor(message = "submission adapter does not support reading") {
64
+ super(message);
65
+ this.name = "SubmissionsNotReadableError";
66
+ }
67
+ };
68
+
69
+ // src/forms/defineForm.ts
70
+ var InvalidFormFieldError = class extends Error {
71
+ formName;
72
+ fieldName;
73
+ fieldKind;
74
+ constructor(formName, fieldName, fieldKind) {
75
+ super(
76
+ `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.`
77
+ );
78
+ this.name = "InvalidFormFieldError";
79
+ this.formName = formName;
80
+ this.fieldName = fieldName;
81
+ this.fieldKind = fieldKind;
82
+ }
83
+ };
84
+ var HoneypotCollisionError = class extends Error {
85
+ formName;
86
+ fieldName;
87
+ reason;
88
+ constructor(formName, fieldName, reason = "collision") {
89
+ super(
90
+ 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.`
91
+ );
92
+ this.name = "HoneypotCollisionError";
93
+ this.formName = formName;
94
+ this.fieldName = fieldName;
95
+ this.reason = reason;
96
+ }
97
+ };
98
+ var InvalidFormNameError = class extends Error {
99
+ constructor(name) {
100
+ super(
101
+ `Invalid form name ${JSON.stringify(name)}. Use letters, digits, hyphen, and underscore only (no path separators).`
102
+ );
103
+ this.name = "InvalidFormNameError";
104
+ }
105
+ };
106
+ var FORM_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
107
+ function defineForm(input) {
108
+ if (typeof input.name !== "string" || !FORM_NAME_PATTERN.test(input.name)) {
109
+ throw new InvalidFormNameError(input.name);
110
+ }
111
+ for (const [fieldName, descriptor] of Object.entries(input.schema)) {
112
+ const kind = descriptor.kind;
113
+ if (FORM_FORBIDDEN_KINDS.has(kind)) {
114
+ throw new InvalidFormFieldError(input.name, fieldName, kind);
115
+ }
116
+ }
117
+ if (input.honeypot !== void 0) {
118
+ if (typeof input.honeypot !== "string" || input.honeypot === "") {
119
+ throw new HoneypotCollisionError(input.name, input.honeypot, "empty");
120
+ }
121
+ if (Object.prototype.hasOwnProperty.call(input.schema, input.honeypot)) {
122
+ throw new HoneypotCollisionError(input.name, input.honeypot, "collision");
123
+ }
124
+ }
125
+ return input.honeypot !== void 0 ? { name: input.name, schema: input.schema, honeypot: input.honeypot } : { name: input.name, schema: input.schema };
126
+ }
127
+
128
+ // src/forms/registry.ts
129
+ var DuplicateFormNameError = class extends Error {
130
+ formName;
131
+ constructor(name) {
132
+ super(
133
+ `Form name "${name}" is registered more than once. Form names must be unique within a config.`
134
+ );
135
+ this.name = "DuplicateFormNameError";
136
+ this.formName = name;
137
+ }
138
+ };
139
+
140
+ // src/domain/fields.ts
141
+ var TextField = { kind: "text" };
142
+ var RichTextField = { kind: "richText" };
143
+ var ImageField = { kind: "image" };
144
+ var VideoField = { kind: "video" };
145
+ var ReferenceField = { kind: "reference" };
146
+ var LinkField = { kind: "link" };
147
+ var NumberField = { kind: "number" };
148
+ var BooleanField = { kind: "boolean" };
149
+ var SelectField = (options, opts) => ({
150
+ kind: "select",
151
+ options,
152
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
153
+ });
154
+ var ButtonField = (variants, opts) => ({
155
+ kind: "button",
156
+ variants,
157
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
158
+ });
159
+ var ListField = (itemSchema, opts) => ({
160
+ kind: "list",
161
+ itemSchema,
162
+ ...opts?.min !== void 0 ? { min: opts.min } : {},
163
+ ...opts?.max !== void 0 ? { max: opts.max } : {},
164
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
165
+ });
166
+
167
+ // src/domain/formOverrides.ts
168
+ var FormOverridesField = (formName, opts) => ({
169
+ kind: "formOverrides",
170
+ formName,
171
+ ...opts?.default !== void 0 ? { default: opts.default } : {}
172
+ });
173
+ export {
174
+ BooleanField,
175
+ ButtonField,
176
+ DuplicateFormNameError,
177
+ FormOverridesField,
178
+ HoneypotCollisionError,
179
+ ImageField,
180
+ InvalidFormFieldError,
181
+ InvalidFormNameError,
182
+ LinkField,
183
+ ListField,
184
+ NumberField,
185
+ ReferenceField,
186
+ RichTextField,
187
+ SelectField,
188
+ SubmissionsNotReadableError,
189
+ TextField,
190
+ VideoField,
191
+ defineConfig,
192
+ defineForm,
193
+ withagntcms
194
+ };