@dyrected/knowledge 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/index.js ADDED
@@ -0,0 +1,2776 @@
1
+ // src/generated/recipes.ts
2
+ var recipes = [
3
+ {
4
+ "id": "auto-slug",
5
+ "title": "Generate a slug from a title",
6
+ "description": "Generate stable URL slugs on the server while showing editors the value live in the Admin UI.",
7
+ "category": "data-lifecycle",
8
+ "intents": [
9
+ "make the URL follow the title",
10
+ "automatically generate a slug",
11
+ "create friendly URLs from titles",
12
+ "keep a slug synchronized with a title"
13
+ ],
14
+ "concepts": [
15
+ "beforeChange",
16
+ "admin.hooks.onChange",
17
+ "promoted",
18
+ "unique"
19
+ ],
20
+ "requires": [],
21
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const toSlug = (value: unknown) =>\n String(value ?? "")\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, "-")\n .replace(/(^-|-$)/g, "");\n\nexport const Posts = defineCollection({\n slug: "posts",\n hooks: {\n beforeChange: [\n ({ data, operation }) => {\n if (operation === "create" || data.title !== undefined) {\n return { ...data, slug: toSlug(data.title) };\n }\n return data;\n },\n ],\n },\n fields: [\n { name: "title", type: "text", label: "Title", required: true },\n {\n name: "slug",\n type: "text",\n label: "Slug",\n required: true,\n unique: true,\n promoted: true,\n admin: {\n hooks: {\n onChange: ({ siblingData }) => toSlug(siblingData.title),\n },\n },\n },\n ],\n});',
22
+ "docsPath": "/docs/recipes/auto-slug"
23
+ },
24
+ {
25
+ "id": "conditional-admin-field",
26
+ "title": "Show an Admin field only when it is relevant",
27
+ "description": "Use a serializable Admin condition to reveal a field from the editor's current form values.",
28
+ "category": "admin-experience",
29
+ "intents": [
30
+ "show a field conditionally",
31
+ "hide irrelevant form fields",
32
+ "show discount only with a coupon",
33
+ "make the admin form react to another field"
34
+ ],
35
+ "concepts": [
36
+ "admin.condition",
37
+ "Jexl",
38
+ "conditional fields"
39
+ ],
40
+ "requires": [],
41
+ "source": `import { defineCollection } from "@dyrected/core";
42
+
43
+ export const Orders = defineCollection({
44
+ slug: "orders",
45
+ fields: [
46
+ { name: "couponCode", type: "text", label: "Coupon code" },
47
+ {
48
+ name: "discountPercent",
49
+ type: "number",
50
+ label: "Discount percentage",
51
+ admin: { condition: "couponCode != null && couponCode != ''" },
52
+ },
53
+ ],
54
+ });`,
55
+ "docsPath": "/docs/recipes/conditional-admin-field"
56
+ },
57
+ {
58
+ "id": "cross-field-validation",
59
+ "title": "Validate related fields before saving",
60
+ "description": "Reject invalid combinations of field values before they reach the database.",
61
+ "category": "data-lifecycle",
62
+ "intents": [
63
+ "validate fields before saving",
64
+ "make sure an end date is after the start date",
65
+ "reject invalid form submissions",
66
+ "validate multiple fields together"
67
+ ],
68
+ "concepts": [
69
+ "beforeChange",
70
+ "validation",
71
+ "throw to abort"
72
+ ],
73
+ "requires": [],
74
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Events = defineCollection({\n slug: "events",\n hooks: {\n beforeChange: [\n ({ data, doc }) => {\n const startsAt = data.startsAt ?? doc?.startsAt;\n const endsAt = data.endsAt ?? doc?.endsAt;\n\n const start = startsAt ? new Date(startsAt) : undefined;\n const end = endsAt ? new Date(endsAt) : undefined;\n\n if (start && Number.isNaN(start.getTime())) {\n throw new Error("The event start time must be a valid date.");\n }\n if (end && Number.isNaN(end.getTime())) {\n throw new Error("The event end time must be a valid date.");\n }\n if (start && end && end <= start) {\n throw new Error("The event end time must be after its start time.");\n }\n\n return data;\n },\n ],\n },\n fields: [\n { name: "title", type: "text", label: "Title", required: true },\n {\n name: "startsAt",\n type: "datetime",\n label: "Starts at",\n required: true,\n },\n {\n name: "endsAt",\n type: "datetime",\n label: "Ends at",\n required: true,\n },\n ],\n});',
75
+ "docsPath": "/docs/recipes/cross-field-validation"
76
+ },
77
+ {
78
+ "id": "dependent-dropdown",
79
+ "title": "Update a dropdown from another field",
80
+ "description": "Change available Admin UI options immediately when an editor changes a related field.",
81
+ "category": "admin-experience",
82
+ "intents": [
83
+ "make one dropdown depend on another",
84
+ "show states based on the selected country",
85
+ "create a cascading dropdown",
86
+ "update select options while editing"
87
+ ],
88
+ "concepts": [
89
+ "admin.hooks.options",
90
+ "select",
91
+ "siblingData"
92
+ ],
93
+ "requires": [],
94
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Locations = defineCollection({\n slug: "locations",\n fields: [\n {\n name: "country",\n type: "select",\n label: "Country",\n required: true,\n options: [\n { label: "Nigeria", value: "ng" },\n { label: "United States", value: "us" },\n ],\n },\n {\n name: "region",\n type: "select",\n label: "State or region",\n required: true,\n options: [],\n admin: {\n hooks: {\n options: ({ siblingData }) => {\n if (siblingData.country === "ng") return ["Lagos", "Abuja", "Oyo"];\n if (siblingData.country === "us")\n return ["California", "New York", "Texas"];\n return [];\n },\n },\n },\n },\n ],\n});',
95
+ "docsPath": "/docs/recipes/dependent-dropdown"
96
+ },
97
+ {
98
+ "id": "editorial-publishing-workflow",
99
+ "title": "Add draft, review, and publishing states",
100
+ "description": "Attach Dyrected's standard editorial workflow and its capability-aware transitions to a collection.",
101
+ "category": "workflows",
102
+ "intents": [
103
+ "add draft and publish states",
104
+ "require review before publishing",
105
+ "create an editorial workflow",
106
+ "let editors submit content for approval"
107
+ ],
108
+ "concepts": [
109
+ "publishingWorkflow",
110
+ "workflow transitions",
111
+ "capabilities"
112
+ ],
113
+ "requires": [],
114
+ "source": 'import { defineCollection, publishingWorkflow } from "@dyrected/core";\n\nexport const Posts = defineCollection({\n slug: "posts",\n workflow: publishingWorkflow(),\n fields: [\n { name: "title", type: "text", label: "Title", required: true },\n { name: "body", type: "richText", label: "Body", required: true },\n ],\n});',
115
+ "docsPath": "/docs/recipes/editorial-publishing-workflow"
116
+ },
117
+ {
118
+ "id": "owner-scoped-access",
119
+ "title": "Limit documents to their owner",
120
+ "description": "Return a where constraint from access control so authenticated users only read their own records.",
121
+ "category": "access-control",
122
+ "intents": [
123
+ "users should only see their own records",
124
+ "add row level access",
125
+ "scope documents by owner",
126
+ "prevent users reading another user's data"
127
+ ],
128
+ "concepts": [
129
+ "access.read",
130
+ "where",
131
+ "row-level access"
132
+ ],
133
+ "requires": [],
134
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Projects = defineCollection({\n slug: "projects",\n access: {\n read: ({ user }) => (user ? { owner: { equals: user.sub } } : false),\n create: ({ user }) => Boolean(user),\n update: ({ user }) => (user ? { owner: { equals: user.sub } } : false),\n delete: ({ user }) => (user ? { owner: { equals: user.sub } } : false),\n },\n hooks: {\n beforeChange: [\n ({ data, operation, user }) => {\n if (operation !== "create") return data;\n if (!user) throw new Error("Authentication is required to create a project.");\n return { ...data, owner: user.sub };\n },\n ],\n },\n fields: [\n { name: "name", type: "text", label: "Project name", required: true },\n {\n name: "owner",\n type: "relationship",\n label: "Owner",\n relationTo: "users",\n required: true,\n admin: { readOnly: true },\n },\n ],\n});',
135
+ "docsPath": "/docs/recipes/owner-scoped-access"
136
+ },
137
+ {
138
+ "id": "page-builder-blocks",
139
+ "title": "Build flexible pages from reusable blocks",
140
+ "description": "Define labeled hero, rich-text, and call-to-action blocks for an editor-controlled page layout.",
141
+ "category": "content-modeling",
142
+ "intents": [
143
+ "build a page builder",
144
+ "let editors arrange page sections",
145
+ "create reusable content blocks",
146
+ "model flexible landing pages"
147
+ ],
148
+ "concepts": [
149
+ "blocks",
150
+ "Block",
151
+ "content modeling"
152
+ ],
153
+ "requires": [],
154
+ "source": 'import { defineCollection } from "@dyrected/core";\nimport type { Block } from "@dyrected/core";\n\nexport const HeroBlock = {\n slug: "hero",\n labels: { singular: "Hero", plural: "Heroes" },\n fields: [\n { name: "heading", type: "text", label: "Heading", required: true },\n { name: "body", type: "textarea", label: "Body" },\n ],\n} satisfies Block;\n\nexport const CallToActionBlock = {\n slug: "callToAction",\n labels: { singular: "Call to action", plural: "Calls to action" },\n fields: [\n { name: "label", type: "text", label: "Link label", required: true },\n { name: "url", type: "url", label: "URL", required: true },\n ],\n} satisfies Block;\n\nexport const Pages = defineCollection({\n slug: "pages",\n fields: [\n { name: "title", type: "text", label: "Title", required: true },\n {\n name: "layout",\n type: "blocks",\n label: "Page layout",\n blocks: [HeroBlock, CallToActionBlock],\n },\n ],\n});',
155
+ "docsPath": "/docs/recipes/page-builder-blocks"
156
+ },
157
+ {
158
+ "id": "relationship-and-reverse-join",
159
+ "title": "Model a relationship and its reverse lookup",
160
+ "description": "Store an author relationship on posts and expose the author's posts through a virtual join field.",
161
+ "category": "content-modeling",
162
+ "intents": [
163
+ "connect posts to authors",
164
+ "show every post written by a user",
165
+ "create a reverse relationship",
166
+ "model one-to-many content"
167
+ ],
168
+ "concepts": [
169
+ "relationship",
170
+ "join",
171
+ "relationTo",
172
+ "depth"
173
+ ],
174
+ "requires": [],
175
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Users = defineCollection({\n slug: "users",\n auth: true,\n fields: [\n { name: "name", type: "text", label: "Name", required: true },\n {\n name: "posts",\n type: "join",\n label: "Posts",\n collection: "posts",\n on: "author",\n limit: 20,\n },\n ],\n});\n\nexport const Posts = defineCollection({\n slug: "posts",\n fields: [\n { name: "title", type: "text", label: "Title", required: true },\n {\n name: "author",\n type: "relationship",\n label: "Author",\n relationTo: "users",\n required: true,\n },\n ],\n});',
176
+ "docsPath": "/docs/recipes/relationship-and-reverse-join"
177
+ },
178
+ {
179
+ "id": "role-based-access",
180
+ "title": "Restrict content operations by user role",
181
+ "description": "Allow public reads, editor writes, and administrator deletion with collection access rules.",
182
+ "category": "access-control",
183
+ "intents": [
184
+ "only editors can update content",
185
+ "restrict deletion to admins",
186
+ "make content publicly readable",
187
+ "add role based access"
188
+ ],
189
+ "concepts": [
190
+ "access",
191
+ "AuthenticatedUser",
192
+ "roles"
193
+ ],
194
+ "requires": [],
195
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Articles = defineCollection({\n slug: "articles",\n access: {\n read: () => true,\n create: ({ user }) => user?.roles?.some((role) => role === "editor" || role === "admin") ?? false,\n update: ({ user }) => user?.roles?.some((role) => role === "editor" || role === "admin") ?? false,\n delete: ({ user }) => user?.roles?.includes("admin") ?? false,\n },\n fields: [{ name: "title", type: "text", label: "Title", required: true }],\n});',
196
+ "docsPath": "/docs/recipes/role-based-access"
197
+ },
198
+ {
199
+ "id": "safe-field-rename",
200
+ "title": "Rename a field without orphaning existing data",
201
+ "description": "Use renameTo and a safe default while documents migrate lazily to a new field name.",
202
+ "category": "data-lifecycle",
203
+ "intents": [
204
+ "rename a field safely",
205
+ "change a field name without losing data",
206
+ "migrate an existing schema",
207
+ "keep old documents working after a rename"
208
+ ],
209
+ "concepts": [
210
+ "renameTo",
211
+ "defaultValue",
212
+ "schema evolution"
213
+ ],
214
+ "requires": [],
215
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Customers = defineCollection({\n slug: "customers",\n fields: [\n {\n name: "fullName",\n type: "text",\n label: "Full name",\n renameTo: "name",\n defaultValue: "",\n required: true,\n },\n ],\n});',
216
+ "docsPath": "/docs/recipes/safe-field-rename"
217
+ },
218
+ {
219
+ "id": "upload-collection",
220
+ "title": "Create a media upload collection",
221
+ "description": "Enable file uploads and capture accessible metadata in a dedicated media collection.",
222
+ "category": "integrations",
223
+ "intents": [
224
+ "let editors upload images",
225
+ "create a media library",
226
+ "store uploaded files",
227
+ "add image uploads to my project"
228
+ ],
229
+ "concepts": [
230
+ "upload",
231
+ "StorageAdapter",
232
+ "media"
233
+ ],
234
+ "requires": [],
235
+ "source": 'import { defineCollection } from "@dyrected/core";\n\nexport const Media = defineCollection({\n slug: "media",\n upload: {\n allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],\n maxFileSize: 10 * 1024 * 1024,\n },\n fields: [\n { name: "alt", type: "text", label: "Alternative text", required: true },\n { name: "caption", type: "textarea", label: "Caption" },\n ],\n});',
236
+ "docsPath": "/docs/recipes/upload-collection"
237
+ }
238
+ ];
239
+
240
+ // src/generated/references.ts
241
+ var references = [
242
+ {
243
+ "id": "@dyrected/core:AdminConfig",
244
+ "name": "AdminConfig",
245
+ "kind": "interface",
246
+ "category": "configuration",
247
+ "sourcePackage": "@dyrected/core",
248
+ "description": "Branding and metadata options for the Dyrected Admin UI.",
249
+ "signature": "export interface AdminConfig {\n /** Custom component slots around the built-in dashboard. */\n components?: AdminDashboardComponentSlots;\n branding?: {\n /** Full logo image shown in the expanded sidebar. URL or imported image asset. */\n logo?: string;\n /** Compact logo mark used in the collapsed sidebar state. */\n logoMark?: string;\n /** Text alternative or addition to the logo image. */\n logoText?: string;\n /**\n * Primary accent colour as any CSS colour value.\n * @example '#6366f1'\n * @example 'hsl(240 50% 60%)'\n */\n primaryColor?: string;\n /** Browser tab favicon URL. */\n favicon?: string;\n /** Font family for body and UI text. Must be loaded separately. */\n fontSans?: string;\n /** Font family for headings. Must be loaded separately. */\n fontSerif?: string;\n };\n meta?: {\n /**\n * String appended to every Admin page's `<title>`.\n * @default '- Dyrected'\n */\n titleSuffix?: string;\n };\n}",
250
+ "members": [
251
+ {
252
+ "name": "components",
253
+ "signature": "components?: AdminDashboardComponentSlots",
254
+ "description": "Custom component slots around the built-in dashboard."
255
+ },
256
+ {
257
+ "name": "branding",
258
+ "signature": "branding?: {\n /** Full logo image shown in the expanded sidebar. URL or imported image asset. */\n logo?: string;\n /** Compact logo mark used in the collapsed sidebar state. */\n logoMark?: string;\n /** Text alternative or addition to the logo image. */\n logoText?: string;\n /**\n * Primary accent colour as any CSS colour value.\n * @example '#6366f1'\n * @example 'hsl(240 50% 60%)'\n */\n primaryColor?: string;\n /** Browser tab favicon URL. */\n favicon?: string;\n /** Font family for body and UI text. Must be loaded separately. */\n fontSans?: string;\n /** Font family for headings. Must be loaded separately. */\n fontSerif?: string;\n }",
259
+ "description": ""
260
+ },
261
+ {
262
+ "name": "meta",
263
+ "signature": "meta?: {\n /**\n * String appended to every Admin page's `<title>`.\n * @default '- Dyrected'\n */\n titleSuffix?: string;\n }",
264
+ "description": ""
265
+ }
266
+ ]
267
+ },
268
+ {
269
+ "id": "@dyrected/core:AuthDocFields",
270
+ "name": "AuthDocFields",
271
+ "kind": "type",
272
+ "category": "fields",
273
+ "sourcePackage": "@dyrected/core",
274
+ "description": "Fields automatically injected into collections with `auth: true`.\n`password` is part of the schema but is stripped from API read responses.",
275
+ "signature": "export type AuthDocFields = {\n email: string;\n password?: string;\n roles?: string[];\n};",
276
+ "members": []
277
+ },
278
+ {
279
+ "id": "@dyrected/core:AuthenticatedUser",
280
+ "name": "AuthenticatedUser",
281
+ "kind": "interface",
282
+ "category": "hooks",
283
+ "sourcePackage": "@dyrected/core",
284
+ "description": "Base shape of an authenticated user as decoded from the JWT.\n\nThe actual shape will include every field on your auth collection \u2014 this\ninterface only guarantees the properties that Dyrected always stamps on the\ntoken. Extend it in your own codebase for stronger typing:",
285
+ "signature": "export interface AuthenticatedUser {\n /** The user's document ID in the database. */\n sub: string;\n /** The user's email address. */\n email?: string;\n /** Slug of the collection this user was authenticated against. */\n collection: string;\n /** Array of role strings, if your auth collection has a `roles` field. */\n roles?: string[];\n /** Any additional fields from the auth collection document. */\n [key: string]: unknown;\n}",
286
+ "members": [
287
+ {
288
+ "name": "sub",
289
+ "signature": "sub: string",
290
+ "description": "The user's document ID in the database."
291
+ },
292
+ {
293
+ "name": "email",
294
+ "signature": "email?: string",
295
+ "description": "The user's email address."
296
+ },
297
+ {
298
+ "name": "collection",
299
+ "signature": "collection: string",
300
+ "description": "Slug of the collection this user was authenticated against."
301
+ },
302
+ {
303
+ "name": "roles",
304
+ "signature": "roles?: string[]",
305
+ "description": "Array of role strings, if your auth collection has a `roles` field."
306
+ }
307
+ ]
308
+ },
309
+ {
310
+ "id": "@dyrected/core:availableWorkflowTransitions",
311
+ "name": "availableWorkflowTransitions",
312
+ "kind": "function",
313
+ "category": "workflows",
314
+ "sourcePackage": "@dyrected/core",
315
+ "description": "",
316
+ "signature": "export function availableWorkflowTransitions(\n workflow: WorkflowConfig,\n state: string,\n user?: AuthenticatedUser,\n): WorkflowTransition[]",
317
+ "members": []
318
+ },
319
+ {
320
+ "id": "@dyrected/core:canViewWorkflowDraft",
321
+ "name": "canViewWorkflowDraft",
322
+ "kind": "function",
323
+ "category": "workflows",
324
+ "sourcePackage": "@dyrected/core",
325
+ "description": "",
326
+ "signature": "export function canViewWorkflowDraft(workflow: WorkflowConfig, user?: AuthenticatedUser): boolean",
327
+ "members": []
328
+ },
329
+ {
330
+ "id": "@dyrected/core:CollectionAfterChangeHook",
331
+ "name": "CollectionAfterChangeHook",
332
+ "kind": "type",
333
+ "category": "hooks",
334
+ "sourcePackage": "@dyrected/core",
335
+ "description": "Runs **after** a document is created or updated in the database.\n\n**Isolation**: errors thrown inside this hook are caught by the framework,\nlogged to the console, and then discarded. The HTTP response returns the\nsaved document as if nothing went wrong \u2014 because from the database's\nperspective, nothing did. This means a transient email failure or webhook\ntimeout will never turn a successful write into a 500 for the caller.\n\nThe return value is ignored \u2014 this hook is for side-effects only (emails,\nwebhooks, cache revalidation, search index updates, etc.).\n\n**Awaiting vs fire-and-forget**: `await`ing inside this hook is fine for\nfast, reliable calls. For slow or unreliable external services prefer\nfire-and-forget with your own `.catch()` so the response doesn't block:\n\n```ts\n// \u2713 fast & reliable \u2014 safe to await\nafterChange: [async ({ doc }) => {\n await revalidatePath(`/posts/${doc.slug}`)\n}]\n\n// \u2713 slow or unreliable \u2014 fire-and-forget\nafterChange: [({ doc }) => {\n sendEmail({ to: doc.email, ... }).catch(console.error)\n}]\n```",
336
+ "signature": "export type CollectionAfterChangeHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The document as it was written to the database. */\n doc: TDoc;\n /**\n * Snapshot of the document before the write. Only present on `'update'`\n * operations; `undefined` on `'create'`.\n */\n previousDoc?: TDoc;\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /** Whether this was a new document or an update. */\n operation: \"create\" | \"update\";\n /**\n * Database adapter with full read/write access. The DB write for this\n * operation has already committed \u2014 safe for side-effect writes.\n */\n db: DatabaseAdapter;\n}) => void | Promise<void>;",
337
+ "members": []
338
+ },
339
+ {
340
+ "id": "@dyrected/core:CollectionAfterDeleteHook",
341
+ "name": "CollectionAfterDeleteHook",
342
+ "kind": "type",
343
+ "category": "hooks",
344
+ "sourcePackage": "@dyrected/core",
345
+ "description": "",
346
+ "signature": "export type CollectionAfterDeleteHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The ID of the deleted document. */\n id: string;\n /** The document as it was just before deletion. */\n doc: TDoc;\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter with full read/write access. The deletion has already\n * committed \u2014 safe for cascade deletes or cleanup writes.\n */\n db: DatabaseAdapter;\n}) => void | Promise<void>;",
347
+ "members": []
348
+ },
349
+ {
350
+ "id": "@dyrected/core:CollectionAfterReadHook",
351
+ "name": "CollectionAfterReadHook",
352
+ "kind": "type",
353
+ "category": "hooks",
354
+ "sourcePackage": "@dyrected/core",
355
+ "description": "Runs after a document (or list of documents) is fetched from the database,\nbefore the response is sent to the client.\n\nReturn a modified document to send instead. Useful for adding computed\nvirtual fields or transforming the shape of the response.",
356
+ "signature": "export type CollectionAfterReadHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The document as fetched from the database (with defaults applied). */\n doc: TDoc;\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange`/`afterDelete` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => TDoc | Promise<TDoc>;",
357
+ "members": []
358
+ },
359
+ {
360
+ "id": "@dyrected/core:CollectionAfterTransitionHook",
361
+ "name": "CollectionAfterTransitionHook",
362
+ "kind": "type",
363
+ "category": "hooks",
364
+ "sourcePackage": "@dyrected/core",
365
+ "description": "",
366
+ "signature": "export type CollectionAfterTransitionHook<\n TDoc extends object = Record<string, unknown>,\n> = (\n args: WorkflowTransitionContext<TDoc> & { event: LifecycleEvent },\n) => void | Promise<void>;",
367
+ "members": []
368
+ },
369
+ {
370
+ "id": "@dyrected/core:CollectionBeforeChangeHook",
371
+ "name": "CollectionBeforeChangeHook",
372
+ "kind": "type",
373
+ "category": "hooks",
374
+ "sourcePackage": "@dyrected/core",
375
+ "description": "Runs **before** a document is created or updated in the database.\n\nReturn a modified data object to write instead of the original. This is the\nright place for data transformation, normalisation, slug generation, and\nvalidation (throw to abort the write).",
376
+ "signature": "export type CollectionBeforeChangeHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The incoming data payload being written. */\n data: Partial<TDoc>;\n /**\n * The existing document before this update. Only present on `'update'`\n * operations; `undefined` on `'create'`.\n */\n doc?: TDoc;\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /** Whether this is a new document or an update to an existing one. */\n operation: \"create\" | \"update\";\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange`/`afterDelete` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => Partial<TDoc> | void | Promise<Partial<TDoc> | void>;",
377
+ "members": []
378
+ },
379
+ {
380
+ "id": "@dyrected/core:CollectionBeforeDeleteHook",
381
+ "name": "CollectionBeforeDeleteHook",
382
+ "kind": "type",
383
+ "category": "hooks",
384
+ "sourcePackage": "@dyrected/core",
385
+ "description": "Runs **before** a document is deleted from the database.\n\nThrow an error to cancel the deletion \u2014 the document will not be removed\nand the API will return a `500` with your error message.",
386
+ "signature": "export type CollectionBeforeDeleteHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The ID of the document about to be deleted. */\n id: string;\n /** The full document about to be deleted. */\n doc: TDoc;\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterDelete` for post-deletion writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => void | Promise<void>;",
387
+ "members": []
388
+ },
389
+ {
390
+ "id": "@dyrected/core:CollectionBeforeReadHook",
391
+ "name": "CollectionBeforeReadHook",
392
+ "kind": "type",
393
+ "category": "hooks",
394
+ "sourcePackage": "@dyrected/core",
395
+ "description": "Runs before Dyrected queries the database for a list or single-document fetch.\n\nReturn a new `where` query object to override or extend the current filter.\nReturn `undefined` (or nothing) to leave the query unchanged.",
396
+ "signature": "export type CollectionBeforeReadHook = (args: {\n /** The HTTP request context. */\n req: HookRequestContext;\n /** The current `where` query filter. Modify and return to override. */\n query?: Record<string, unknown>;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange`/`afterDelete` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;",
397
+ "members": []
398
+ },
399
+ {
400
+ "id": "@dyrected/core:CollectionBeforeTransitionHook",
401
+ "name": "CollectionBeforeTransitionHook",
402
+ "kind": "type",
403
+ "category": "hooks",
404
+ "sourcePackage": "@dyrected/core",
405
+ "description": "",
406
+ "signature": "export type CollectionBeforeTransitionHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: WorkflowTransitionContext<TDoc>) => void | Promise<void>;",
407
+ "members": []
408
+ },
409
+ {
410
+ "id": "@dyrected/core:CollectionConfig",
411
+ "name": "CollectionConfig",
412
+ "kind": "interface",
413
+ "category": "configuration",
414
+ "sourcePackage": "@dyrected/core",
415
+ "description": "Defines a Dyrected collection \u2014 a named set of documents with a shared schema.\n\nPass your document's TypeScript type as the generic parameter `TDoc` to get\nfully typed hooks and access functions:\n\n```ts\ninterface Post {\n id: string\n title: string\n slug: string\n status: 'draft' | 'published'\n publishedAt?: string\n}\n\nexport const Posts = defineCollection<Post>({\n slug: 'posts',\n hooks: {\n beforeChange: [({ data, operation }) => {\n // `data` is typed as Partial<Post>\n if (operation === 'create') return { ...data, status: 'draft' }\n return data\n }],\n afterChange: [({ doc, previousDoc }) => {\n // `doc` and `previousDoc` are typed as Post\n if (doc.status !== previousDoc?.status) notifySubscribers(doc)\n }],\n },\n fields: [...],\n})\n```",
416
+ "signature": "export interface CollectionConfig<\n TDoc extends object = Record<string, unknown>,\n> {\n /**\n * Unique identifier for this collection.\n * Used as the URL segment (`/api/collections/:slug`) and the database table/collection name.\n * Use kebab-case, e.g. `'blog-posts'`.\n */\n slug: string;\n\n /**\n * Restricts this collection to a specific site in a multi-tenant deployment.\n * When set, only requests bearing a matching `X-Site-Id` header can access it.\n */\n siteId?: string;\n\n /**\n * If `true`, this collection is shared across all sites in a multi-tenant\n * deployment and accessible regardless of the `X-Site-Id` header.\n */\n shared?: boolean;\n\n /** Human-readable names for documents in this collection, shown in the Admin UI. */\n labels?: {\n singular: string;\n plural: string;\n };\n\n /**\n * If `true`, this collection is an **auth collection** \u2014 it gains\n * `POST /api/collections/:slug/login` and `POST /api/collections/:slug/logout`\n * endpoints, and documents are expected to have a `password` field.\n */\n auth?: boolean;\n\n /**\n * If `true` (or a config object), this collection supports **file uploads**.\n * Documents gain file-related fields (`url`, `filename`, `mimeType`, etc.)\n * and the create endpoint accepts `multipart/form-data`.\n */\n upload?: boolean | UploadConfig;\n\n /** Field definitions that make up the document schema for this collection. */\n fields: Field[];\n\n /**\n * If `true`, Dyrected automatically adds `createdAt` and `updatedAt`\n * timestamp fields to every document. Defaults to `true`.\n */\n timestamps?: boolean;\n\n /**\n * Initial documents to seed into this collection the first time it is\n * fetched and found to be empty (e.g. for demo data or defaults).\n */\n initialData?: Partial<TDoc>[];\n\n /**\n * If `true`, every create, update, and delete operation on this collection\n * is logged to the `__audit` collection with before/after snapshots and the\n * acting user's identity.\n */\n audit?: boolean;\n\n /**\n * Optional state-machine workflow for this collection. Workflow-enabled\n * entries keep an editable working revision and an independent public\n * snapshot, so editing published content never changes the live response.\n */\n workflow?: WorkflowConfig<TDoc>;\n\n /**\n * Collection-level access control.\n *\n * Each key is an operation; the value is a function (or Jexl string) that\n * returns `true` to allow or `false` to deny. Returning a `where`-style\n * object grants access only to matching documents.\n *\n * @example\n * access: {\n * read: () => true, // public read\n * create: ({ user }) => !!user, // logged-in users only\n * update: ({ user }) => user?.roles?.includes('editor') ?? false,\n * delete: ({ user }) => user?.roles?.includes('admin') ?? false,\n * }\n */\n access?: {\n read?: AccessFunction<TDoc> | string;\n create?: AccessFunction<TDoc> | string;\n update?: AccessFunction<TDoc> | string;\n delete?: AccessFunction<TDoc> | string;\n };\n\n /**\n * Collection-level lifecycle hooks.\n *\n * Hooks run in the order they appear in the array. The return value of each\n * hook is passed as the input to the next. Throwing inside any hook aborts\n * the operation and returns a `500` error.\n *\n * See the [Hooks reference](/docs/concepts/hooks) for the full lifecycle diagram.\n */\n hooks?: {\n /**\n * Runs before the database is queried. Return a modified `where` object\n * to override the query filter.\n */\n beforeRead?: CollectionBeforeReadHook[];\n\n /**\n * Runs after documents are fetched. Return a modified doc to change what\n * the client receives. Runs on every document in a list response.\n */\n afterRead?: CollectionAfterReadHook<TDoc>[];\n\n /**\n * Runs before create or update. Return modified data to change what is\n * written to the database. Throw to abort the write entirely.\n */\n beforeChange?: CollectionBeforeChangeHook<TDoc>[];\n\n /**\n * Runs after create or update is committed. For side-effects only \u2014\n * webhooks, cache busting, notifications. Return value is ignored.\n *\n * Errors are **isolated**: caught, logged, and discarded so a failing\n * side-effect never turns a successful write into an HTTP 500.\n * See {@link CollectionAfterChangeHook} for the await-vs-fire-and-forget guidance.\n */\n afterChange?: CollectionAfterChangeHook<TDoc>[];\n\n /**\n * Runs before a document is deleted. Throw to cancel the deletion.\n */\n beforeDelete?: CollectionBeforeDeleteHook<TDoc>[];\n\n /**\n * Runs after a document has been deleted. For cleanup side-effects only.\n *\n * Errors are **isolated**: caught, logged, and discarded \u2014 the deletion is\n * already committed and will not be undone.\n */\n afterDelete?: CollectionAfterDeleteHook<TDoc>[];\n };\n\n /** Admin UI configuration for this collection. */\n admin?: {\n /**\n * Lucide icon displayed beside this collection in the Admin sidebar.\n * Uses Lucide component names, e.g. `'Newspaper'` or `'ShoppingBag'`.\n */\n icon?: AdminIconName;\n\n /** Custom component slots for this collection's list view. */\n components?: CollectionListComponentSlots;\n\n /**\n * The field name used as the document's display title in the Admin list\n * view and breadcrumbs. Defaults to `'title'` if the field exists.\n */\n useAsTitle?: string;\n\n /**\n * Field names to show as columns in the Admin list view.\n * Defaults to a sensible set of the first few non-structural fields.\n */\n defaultColumns?: string[];\n\n /**\n * Groups this collection under a named section in the Admin sidebar.\n * Collections with the same `group` are visually grouped together.\n */\n group?: string;\n\n /** If `true`, this collection is not shown in the Admin UI sidebar. */\n hidden?: boolean;\n\n /** If `false`, disables the filter UI entirely for this collection. Defaults to `true`. */\n filterable?: boolean;\n\n /**\n * URL to open in the Live Preview pane when editing a document.\n * Pass a function to derive the URL from the document's fields.\n *\n * @example\n * previewUrl: (doc) => `https://mysite.com/blog/${doc.slug}`\n */\n previewUrl?:\n | string\n | ((doc: TDoc, opts: { locale?: string }) => string | null);\n\n /**\n * How the Live Preview pane communicates with the frontend.\n * - `'postMessage'` (default) \u2014 sends a `postMessage` with the current doc data.\n * - `'token'` \u2014 passes a short-lived preview token as a query parameter.\n */\n previewMode?: \"postMessage\" | \"token\";\n\n /**\n * Frontend URL pattern for this collection, used by `url` fields to\n * resolve internal links. Use `{fieldName}` placeholders.\n *\n * @example\n * urlPattern: '/blog/{slug}' // \u2192 /blog/my-post\n * urlPattern: '/{slug}' // \u2192 /about\n */\n urlPattern?: string;\n };\n}",
417
+ "members": [
418
+ {
419
+ "name": "slug",
420
+ "signature": "slug: string",
421
+ "description": "Unique identifier for this collection.\nUsed as the URL segment (`/api/collections/:slug`) and the database table/collection name.\nUse kebab-case, e.g. `'blog-posts'`."
422
+ },
423
+ {
424
+ "name": "siteId",
425
+ "signature": "siteId?: string",
426
+ "description": "Restricts this collection to a specific site in a multi-tenant deployment.\nWhen set, only requests bearing a matching `X-Site-Id` header can access it."
427
+ },
428
+ {
429
+ "name": "shared",
430
+ "signature": "shared?: boolean",
431
+ "description": "If `true`, this collection is shared across all sites in a multi-tenant\ndeployment and accessible regardless of the `X-Site-Id` header."
432
+ },
433
+ {
434
+ "name": "labels",
435
+ "signature": "labels?: {\n singular: string;\n plural: string;\n }",
436
+ "description": "Human-readable names for documents in this collection, shown in the Admin UI."
437
+ },
438
+ {
439
+ "name": "auth",
440
+ "signature": "auth?: boolean",
441
+ "description": "If `true`, this collection is an **auth collection** \u2014 it gains\n`POST /api/collections/:slug/login` and `POST /api/collections/:slug/logout`\nendpoints, and documents are expected to have a `password` field."
442
+ },
443
+ {
444
+ "name": "upload",
445
+ "signature": "upload?: boolean | UploadConfig",
446
+ "description": "If `true` (or a config object), this collection supports **file uploads**.\nDocuments gain file-related fields (`url`, `filename`, `mimeType`, etc.)\nand the create endpoint accepts `multipart/form-data`."
447
+ },
448
+ {
449
+ "name": "fields",
450
+ "signature": "fields: Field[]",
451
+ "description": "Field definitions that make up the document schema for this collection."
452
+ },
453
+ {
454
+ "name": "timestamps",
455
+ "signature": "timestamps?: boolean",
456
+ "description": "If `true`, Dyrected automatically adds `createdAt` and `updatedAt`\ntimestamp fields to every document. Defaults to `true`."
457
+ },
458
+ {
459
+ "name": "initialData",
460
+ "signature": "initialData?: Partial<TDoc>[]",
461
+ "description": "Initial documents to seed into this collection the first time it is\nfetched and found to be empty (e.g. for demo data or defaults)."
462
+ },
463
+ {
464
+ "name": "audit",
465
+ "signature": "audit?: boolean",
466
+ "description": "If `true`, every create, update, and delete operation on this collection\nis logged to the `__audit` collection with before/after snapshots and the\nacting user's identity."
467
+ },
468
+ {
469
+ "name": "workflow",
470
+ "signature": "workflow?: WorkflowConfig<TDoc>",
471
+ "description": "Optional state-machine workflow for this collection. Workflow-enabled\nentries keep an editable working revision and an independent public\nsnapshot, so editing published content never changes the live response."
472
+ },
473
+ {
474
+ "name": "access",
475
+ "signature": "access?: {\n read?: AccessFunction<TDoc> | string;\n create?: AccessFunction<TDoc> | string;\n update?: AccessFunction<TDoc> | string;\n delete?: AccessFunction<TDoc> | string;\n }",
476
+ "description": "Collection-level access control.\n\nEach key is an operation; the value is a function (or Jexl string) that\nreturns `true` to allow or `false` to deny. Returning a `where`-style\nobject grants access only to matching documents."
477
+ },
478
+ {
479
+ "name": "hooks",
480
+ "signature": "hooks?: {\n /**\n * Runs before the database is queried. Return a modified `where` object\n * to override the query filter.\n */\n beforeRead?: CollectionBeforeReadHook[];\n\n /**\n * Runs after documents are fetched. Return a modified doc to change what\n * the client receives. Runs on every document in a list response.\n */\n afterRead?: CollectionAfterReadHook<TDoc>[];\n\n /**\n * Runs before create or update. Return modified data to change what is\n * written to the database. Throw to abort the write entirely.\n */\n beforeChange?: CollectionBeforeChangeHook<TDoc>[];\n\n /**\n * Runs after create or update is committed. For side-effects only \u2014\n * webhooks, cache busting, notifications. Return value is ignored.\n *\n * Errors are **isolated**: caught, logged, and discarded so a failing\n * side-effect never turns a successful write into an HTTP 500.\n * See {@link CollectionAfterChangeHook} for the await-vs-fire-and-forget guidance.\n */\n afterChange?: CollectionAfterChangeHook<TDoc>[];\n\n /**\n * Runs before a document is deleted. Throw to cancel the deletion.\n */\n beforeDelete?: CollectionBeforeDeleteHook<TDoc>[];\n\n /**\n * Runs after a document has been deleted. For cleanup side-effects only.\n *\n * Errors are **isolated**: caught, logged, and discarded \u2014 the deletion is\n * already committed and will not be undone.\n */\n afterDelete?: CollectionAfterDeleteHook<TDoc>[];\n }",
481
+ "description": "Collection-level lifecycle hooks.\n\nHooks run in the order they appear in the array. The return value of each\nhook is passed as the input to the next. Throwing inside any hook aborts\nthe operation and returns a `500` error.\n\nSee the [Hooks reference](/docs/concepts/hooks) for the full lifecycle diagram."
482
+ },
483
+ {
484
+ "name": "admin",
485
+ "signature": "admin?: {\n /**\n * Lucide icon displayed beside this collection in the Admin sidebar.\n * Uses Lucide component names, e.g. `'Newspaper'` or `'ShoppingBag'`.\n */\n icon?: AdminIconName;\n\n /** Custom component slots for this collection's list view. */\n components?: CollectionListComponentSlots;\n\n /**\n * The field name used as the document's display title in the Admin list\n * view and breadcrumbs. Defaults to `'title'` if the field exists.\n */\n useAsTitle?: string;\n\n /**\n * Field names to show as columns in the Admin list view.\n * Defaults to a sensible set of the first few non-structural fields.\n */\n defaultColumns?: string[];\n\n /**\n * Groups this collection under a named section in the Admin sidebar.\n * Collections with the same `group` are visually grouped together.\n */\n group?: string;\n\n /** If `true`, this collection is not shown in the Admin UI sidebar. */\n hidden?: boolean;\n\n /** If `false`, disables the filter UI entirely for this collection. Defaults to `true`. */\n filterable?: boolean;\n\n /**\n * URL to open in the Live Preview pane when editing a document.\n * Pass a function to derive the URL from the document's fields.\n *\n * @example\n * previewUrl: (doc) => `https://mysite.com/blog/${doc.slug}`\n */\n previewUrl?:\n | string\n | ((doc: TDoc, opts: { locale?: string }) => string | null);\n\n /**\n * How the Live Preview pane communicates with the frontend.\n * - `'postMessage'` (default) \u2014 sends a `postMessage` with the current doc data.\n * - `'token'` \u2014 passes a short-lived preview token as a query parameter.\n */\n previewMode?: \"postMessage\" | \"token\";\n\n /**\n * Frontend URL pattern for this collection, used by `url` fields to\n * resolve internal links. Use `{fieldName}` placeholders.\n *\n * @example\n * urlPattern: '/blog/{slug}' // \u2192 /blog/my-post\n * urlPattern: '/{slug}' // \u2192 /about\n */\n urlPattern?: string;\n }",
486
+ "description": "Admin UI configuration for this collection."
487
+ }
488
+ ]
489
+ },
490
+ {
491
+ "id": "@dyrected/core:createLifecycleEvent",
492
+ "name": "createLifecycleEvent",
493
+ "kind": "function",
494
+ "category": "workflows",
495
+ "sourcePackage": "@dyrected/core",
496
+ "description": "",
497
+ "signature": "export function createLifecycleEvent(args: {\n name: LifecycleEventName;\n collection: string;\n documentId: string;\n actorId?: string;\n payload: Record<string, unknown>;\n}): LifecycleEvent",
498
+ "members": []
499
+ },
500
+ {
501
+ "id": "@dyrected/core:createWorkflowDocument",
502
+ "name": "createWorkflowDocument",
503
+ "kind": "function",
504
+ "category": "workflows",
505
+ "sourcePackage": "@dyrected/core",
506
+ "description": "",
507
+ "signature": "export async function createWorkflowDocument(args: {\n config: DyrectedConfig;\n collection: CollectionConfig;\n data: Record<string, unknown>;\n user?: AuthenticatedUser;\n}): Promise<{ doc: BaseDocument; event: LifecycleEvent }>",
508
+ "members": []
509
+ },
510
+ {
511
+ "id": "@dyrected/core:DatabaseAdapter",
512
+ "name": "DatabaseAdapter",
513
+ "kind": "interface",
514
+ "category": "adapters",
515
+ "sourcePackage": "@dyrected/core",
516
+ "description": "The interface every database adapter must implement.\n\nDyrected ships adapters for PostgreSQL, MySQL, SQLite, and MongoDB.\nImplement this interface to connect any other database.",
517
+ "signature": "export interface DatabaseAdapter {\n /** Find a paginated list of documents in a collection. */\n find(args: {\n collection: string;\n where?: Record<string, unknown>;\n limit?: number;\n page?: number;\n sort?: string;\n }): Promise<PaginatedResult>;\n\n /** Find a single document by its ID. Returns `null` if not found. */\n findOne(args: {\n collection: string;\n id: string;\n }): Promise<BaseDocument | null>;\n\n /** Insert a new document and return it with its generated `id`. */\n create(args: {\n collection: string;\n data: Record<string, unknown>;\n }): Promise<BaseDocument>;\n\n /** Update a document by ID and return the updated document. */\n update(args: {\n collection: string;\n id: string;\n data: Record<string, unknown>;\n }): Promise<BaseDocument>;\n\n /** Delete a document by ID. Return value is intentionally untyped \u2014 callers do not use it. */\n delete(args: { collection: string; id: string }): Promise<unknown>;\n\n /** Fetch the singleton document for a global. Returns an empty object if not yet initialised. */\n getGlobal(args: { slug: string }): Promise<Record<string, unknown>>;\n\n /** Create or replace the singleton document for a global. */\n updateGlobal(args: {\n slug: string;\n data: Record<string, unknown>;\n }): Promise<Record<string, unknown>>;\n\n /**\n * Sync the database schema with the current collection and global configs.\n * Called on startup to create tables/collections that don't exist yet.\n * Not all adapters implement this (e.g. MongoDB is schema-less).\n */\n sync?(\n collections: CollectionConfig[],\n globals: GlobalConfig[],\n ): Promise<void>;\n\n /**\n * Execute a raw SQL query or database command.\n * Optional \u2014 not all adapters support raw access.\n */\n execute?(query: string, params?: unknown[]): Promise<unknown>;\n\n /**\n * Run all adapter operations in `callback` as one atomic transaction.\n * Shipped adapters implement this; workflow transitions require it.\n */\n transaction?<T>(callback: (db: DatabaseAdapter) => Promise<T>): Promise<T>;\n}",
518
+ "members": [
519
+ {
520
+ "name": "find",
521
+ "signature": "find(args: {\n collection: string;\n where?: Record<string, unknown>;\n limit?: number;\n page?: number;\n sort?: string;\n }): Promise<PaginatedResult>",
522
+ "description": "Find a paginated list of documents in a collection."
523
+ },
524
+ {
525
+ "name": "findOne",
526
+ "signature": "findOne(args: {\n collection: string;\n id: string;\n }): Promise<BaseDocument | null>",
527
+ "description": "Find a single document by its ID. Returns `null` if not found."
528
+ },
529
+ {
530
+ "name": "create",
531
+ "signature": "create(args: {\n collection: string;\n data: Record<string, unknown>;\n }): Promise<BaseDocument>",
532
+ "description": "Insert a new document and return it with its generated `id`."
533
+ },
534
+ {
535
+ "name": "update",
536
+ "signature": "update(args: {\n collection: string;\n id: string;\n data: Record<string, unknown>;\n }): Promise<BaseDocument>",
537
+ "description": "Update a document by ID and return the updated document."
538
+ },
539
+ {
540
+ "name": "delete",
541
+ "signature": "delete(args: { collection: string; id: string }): Promise<unknown>",
542
+ "description": "Delete a document by ID. Return value is intentionally untyped \u2014 callers do not use it."
543
+ },
544
+ {
545
+ "name": "getGlobal",
546
+ "signature": "getGlobal(args: { slug: string }): Promise<Record<string, unknown>>",
547
+ "description": "Fetch the singleton document for a global. Returns an empty object if not yet initialised."
548
+ },
549
+ {
550
+ "name": "updateGlobal",
551
+ "signature": "updateGlobal(args: {\n slug: string;\n data: Record<string, unknown>;\n }): Promise<Record<string, unknown>>",
552
+ "description": "Create or replace the singleton document for a global."
553
+ },
554
+ {
555
+ "name": "sync",
556
+ "signature": "sync(collections: CollectionConfig[], globals: GlobalConfig[]): Promise<void>",
557
+ "description": "Sync the database schema with the current collection and global configs.\nCalled on startup to create tables/collections that don't exist yet.\nNot all adapters implement this (e.g. MongoDB is schema-less)."
558
+ },
559
+ {
560
+ "name": "execute",
561
+ "signature": "execute(query: string, params?: unknown[]): Promise<unknown>",
562
+ "description": "Execute a raw SQL query or database command.\nOptional \u2014 not all adapters support raw access."
563
+ },
564
+ {
565
+ "name": "transaction",
566
+ "signature": "transaction<T>(callback: (db: DatabaseAdapter) => Promise<T>): Promise<T>",
567
+ "description": "Run all adapter operations in `callback` as one atomic transaction.\nShipped adapters implement this; workflow transitions require it."
568
+ }
569
+ ]
570
+ },
571
+ {
572
+ "id": "@dyrected/core:dispatchLifecycleEvent",
573
+ "name": "dispatchLifecycleEvent",
574
+ "kind": "function",
575
+ "category": "workflows",
576
+ "sourcePackage": "@dyrected/core",
577
+ "description": "",
578
+ "signature": "export async function dispatchLifecycleEvent(config: DyrectedConfig, event: LifecycleEvent): Promise<void>",
579
+ "members": []
580
+ },
581
+ {
582
+ "id": "@dyrected/core:dispatchPendingLifecycleEvents",
583
+ "name": "dispatchPendingLifecycleEvents",
584
+ "kind": "function",
585
+ "category": "workflows",
586
+ "sourcePackage": "@dyrected/core",
587
+ "description": "",
588
+ "signature": "export async function dispatchPendingLifecycleEvents(config: DyrectedConfig, limit = 50): Promise<number>",
589
+ "members": []
590
+ },
591
+ {
592
+ "id": "@dyrected/core:DyrectedConfig",
593
+ "name": "DyrectedConfig",
594
+ "kind": "interface",
595
+ "category": "configuration",
596
+ "sourcePackage": "@dyrected/core",
597
+ "description": "The root configuration object passed to `createDyrectedApp`.\n\nThis is the single source of truth for your entire Dyrected instance \u2014\ncollections, globals, database adapter, storage, email, and more.",
598
+ "signature": "export interface DyrectedConfig {\n /** Collection definitions. Each collection maps to a database table/collection. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n collections: CollectionConfig<any>[];\n\n /** Global (singleton) definitions. Each global maps to a single document. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n globals: GlobalConfig<any>[];\n\n /**\n * The database adapter. Required for all data operations.\n * @see {@link DatabaseAdapter}\n */\n db?: DatabaseAdapter;\n\n /**\n * The storage adapter for file uploads.\n * Required when any collection has `upload: true`.\n * @see {@link StorageAdapter}\n */\n storage?: StorageAdapter;\n\n /**\n * The image processing service. Required when any upload collection\n * defines `imageSizes`.\n * @see {@link ImageService}\n */\n image?: ImageService;\n\n /** Admin UI branding and metadata. */\n admin?: AdminConfig;\n\n /**\n * Email transport configuration. Required for welcome emails, password\n * resets, and invite links.\n *\n * @example\n * email: {\n * from: 'no-reply@myapp.com',\n * send: async ({ to, subject, html }) => {\n * await resend.emails.send({ from, to, subject, html })\n * },\n * }\n */\n email?: {\n /** The `From` address for all outbound emails. */\n from: string;\n /** The send function. Wire in any email provider (Resend, SendGrid, SES, etc.). */\n send: (args: {\n to: string;\n subject: string;\n html: string;\n }) => Promise<void>;\n /** Override the default email templates. */\n templates?: {\n welcome?: (args: { email: string }) => { subject?: string; html: string };\n invite?: (args: { token: string; invitedByEmail?: string }) => {\n subject?: string;\n html: string;\n };\n resetPassword?: (args: { token: string; url?: string }) => {\n subject?: string;\n html: string;\n };\n passwordChanged?: (args: { email: string }) => {\n subject?: string;\n html: string;\n };\n };\n };\n\n /**\n * Redis connection URL. Required for distributed caching of dynamic option\n * resolvers and other server-side caches in multi-instance deployments.\n *\n * @example\n * redis: { url: process.env.REDIS_URL }\n */\n redis?: {\n url: string;\n };\n\n /** Durable lifecycle-event delivery configuration. */\n events?: {\n handlers: LifecycleEventHandler[];\n /** Maximum delivery attempts before an event remains failed. Defaults to 8. */\n maxAttempts?: number;\n /** Initial exponential-backoff delay in milliseconds. Defaults to 1000. */\n retryDelayMs?: number;\n };\n\n /**\n * Cross-Origin Resource Sharing (CORS) configuration.\n * List all origins that are allowed to call the Dyrected API.\n *\n * @example\n * cors: { origins: ['https://myapp.com', 'https://www.myapp.com'] }\n */\n cors?: {\n origins: string[];\n };\n\n /**\n * Callback to dynamically fetch additional collections and globals for a\n * given site ID at request time. Used in multi-tenant deployments where each\n * site has its own schema stored in the database.\n *\n * @param siteId The `X-Site-Id` header value from the incoming request.\n * @returns Extra collections and globals to merge into the config for this request.\n *\n * @example\n * onSchemaFetch: async (siteId) => {\n * const site = await db.findOne({ collection: 'sites', id: siteId })\n * return buildSchemaFromSiteConfig(site)\n * }\n */\n onSchemaFetch?: (\n siteId: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ) => Promise<{\n collections?: CollectionConfig<any>[];\n globals?: GlobalConfig<any>[];\n }>;\n}",
599
+ "members": [
600
+ {
601
+ "name": "collections",
602
+ "signature": "collections: CollectionConfig<any>[]",
603
+ "description": "Collection definitions. Each collection maps to a database table/collection."
604
+ },
605
+ {
606
+ "name": "globals",
607
+ "signature": "globals: GlobalConfig<any>[]",
608
+ "description": "Global (singleton) definitions. Each global maps to a single document."
609
+ },
610
+ {
611
+ "name": "db",
612
+ "signature": "db?: DatabaseAdapter",
613
+ "description": "The database adapter. Required for all data operations."
614
+ },
615
+ {
616
+ "name": "storage",
617
+ "signature": "storage?: StorageAdapter",
618
+ "description": "The storage adapter for file uploads.\nRequired when any collection has `upload: true`."
619
+ },
620
+ {
621
+ "name": "image",
622
+ "signature": "image?: ImageService",
623
+ "description": "The image processing service. Required when any upload collection\ndefines `imageSizes`."
624
+ },
625
+ {
626
+ "name": "admin",
627
+ "signature": "admin?: AdminConfig",
628
+ "description": "Admin UI branding and metadata."
629
+ },
630
+ {
631
+ "name": "email",
632
+ "signature": "email?: {\n /** The `From` address for all outbound emails. */\n from: string;\n /** The send function. Wire in any email provider (Resend, SendGrid, SES, etc.). */\n send: (args: {\n to: string;\n subject: string;\n html: string;\n }) => Promise<void>;\n /** Override the default email templates. */\n templates?: {\n welcome?: (args: { email: string }) => { subject?: string; html: string };\n invite?: (args: { token: string; invitedByEmail?: string }) => {\n subject?: string;\n html: string;\n };\n resetPassword?: (args: { token: string; url?: string }) => {\n subject?: string;\n html: string;\n };\n passwordChanged?: (args: { email: string }) => {\n subject?: string;\n html: string;\n };\n };\n }",
633
+ "description": "Email transport configuration. Required for welcome emails, password\nresets, and invite links."
634
+ },
635
+ {
636
+ "name": "redis",
637
+ "signature": "redis?: {\n url: string;\n }",
638
+ "description": "Redis connection URL. Required for distributed caching of dynamic option\nresolvers and other server-side caches in multi-instance deployments."
639
+ },
640
+ {
641
+ "name": "events",
642
+ "signature": "events?: {\n handlers: LifecycleEventHandler[];\n /** Maximum delivery attempts before an event remains failed. Defaults to 8. */\n maxAttempts?: number;\n /** Initial exponential-backoff delay in milliseconds. Defaults to 1000. */\n retryDelayMs?: number;\n }",
643
+ "description": "Durable lifecycle-event delivery configuration."
644
+ },
645
+ {
646
+ "name": "cors",
647
+ "signature": "cors?: {\n origins: string[];\n }",
648
+ "description": "Cross-Origin Resource Sharing (CORS) configuration.\nList all origins that are allowed to call the Dyrected API."
649
+ },
650
+ {
651
+ "name": "onSchemaFetch",
652
+ "signature": "onSchemaFetch?: (\n siteId: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ) => Promise<{\n collections?: CollectionConfig<any>[];\n globals?: GlobalConfig<any>[];\n }>",
653
+ "description": "Callback to dynamically fetch additional collections and globals for a\ngiven site ID at request time. Used in multi-tenant deployments where each\nsite has its own schema stored in the database."
654
+ }
655
+ ]
656
+ },
657
+ {
658
+ "id": "@dyrected/core:Field",
659
+ "name": "Field",
660
+ "kind": "type",
661
+ "category": "fields",
662
+ "sourcePackage": "@dyrected/core",
663
+ "description": "Defines a single field on a collection or global.\n\n## Typed `value` in hook callbacks\n\nThree hook callbacks automatically receive a `value` typed to the field's\nown value type \u2014 no manual annotations needed:\n\n| Hook | When it runs |\n|------|-------------|\n| `hooks.beforeChange` | Server \u2014 before the value is written to the DB |\n| `hooks.afterRead` | Server \u2014 after the value is read, before the API response |\n| `admin.hooks.onChange` | Browser \u2014 whenever a sibling field changes in the Admin UI |\n\nType mapping by `type` property:\n- `text / textarea / email / url / icon / date / select / radio` \u2192 `string`\n- `number` \u2192 `number`\n- `boolean` \u2192 `boolean`\n- `multiSelect` \u2192 `string[]`\n- `relationship / image` \u2192 `string | string[]`\n- `richText / json` \u2192 `Record<string, unknown>`\n- `object / array / blocks` \u2192 `unknown`\n\n```ts\n{\n name: 'slug', type: 'text',\n hooks: {\n beforeChange: [({ value }) => value.toLowerCase()], // value: string \u2713\n afterRead: [({ value }) => value.trim()], // value: string \u2713\n },\n admin: {\n hooks: {\n onChange: ({ value, siblingData }) => // value: string \u2713\n (siblingData.title as string ?? '').toLowerCase().replace(/\\s+/g, '-'),\n },\n },\n}\n```\n\n**Important**: write `type` as a plain string literal \u2014 do **not** use `as const`.\nTypeScript's `const` generic inference already preserves literal types, and\nadding `as const` to the discriminant property prevents the contextual type\nfrom flowing into the hook callbacks.\n\n```ts\n// \u2713 correct\n{ name: 'slug', type: 'text', hooks: { beforeChange: [({ value }) => value.toLowerCase()] } }\n\n// \u2717 breaks value typing\n{ name: 'slug', type: 'text' as const, hooks: { beforeChange: [({ value }) => value.toLowerCase()] } }\n```",
664
+ "signature": 'export type Field = FieldBase &\n (\n | ({\n type:\n | "text"\n | "textarea"\n | "email"\n | "url"\n | "icon"\n | "date"\n | "datetime"\n | "time"\n | "select"\n | "radio";\n } & FieldHooks<string> &\n FieldAdminHooks<string>)\n | ({ type: "number" } & FieldHooks<number> & FieldAdminHooks<number>)\n | ({ type: "boolean" } & FieldHooks<boolean> & FieldAdminHooks<boolean>)\n | ({ type: "multiSelect" } & FieldHooks<string[]> &\n FieldAdminHooks<string[]>)\n | ({ type: "relationship" | "image" } & FieldHooks<string | string[]> &\n FieldAdminHooks<string | string[]>)\n | ({ type: "richText" | "json" } & FieldHooks<Record<string, unknown>> &\n FieldAdminHooks<Record<string, unknown>>)\n | ({\n type: "object" | "array" | "blocks" | "join" | "row";\n } & FieldHooks<unknown> &\n FieldAdminHooks<unknown>)\n );',
665
+ "members": []
666
+ },
667
+ {
668
+ "id": "@dyrected/core:FieldAfterReadHook",
669
+ "name": "FieldAfterReadHook",
670
+ "kind": "type",
671
+ "category": "hooks",
672
+ "sourcePackage": "@dyrected/core",
673
+ "description": "A hook that runs **after a field value is read** from the database, before\nthe response is sent to the client.\n\nReturn the transformed value to return to the client. Use this for masking,\nformatting, or adding computed properties.",
674
+ "signature": "export type FieldAfterReadHook<\n TValue = unknown,\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The raw field value as stored in the database. */\n value: TValue;\n /** The full document being returned (with defaults applied). */\n doc: TDoc;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange`/`afterDelete` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => TValue | undefined | Promise<TValue | undefined>;",
675
+ "members": []
676
+ },
677
+ {
678
+ "id": "@dyrected/core:FieldBeforeChangeHook",
679
+ "name": "FieldBeforeChangeHook",
680
+ "kind": "type",
681
+ "category": "hooks",
682
+ "sourcePackage": "@dyrected/core",
683
+ "description": "A hook that runs **before a field value is saved** to the database.\n\nReturn the transformed value to persist. Return `undefined` to leave the\nvalue unchanged (same as returning the original `value`).\n\nField `beforeChange` hooks run recursively inside `array`, `object`, and\n`blocks` fields \u2014 every nested item is processed automatically.",
684
+ "signature": "export type FieldBeforeChangeHook<\n TValue = unknown,\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n /** The current value of this field (after any previous hooks in the chain). */\n value: TValue;\n /** The full document as it existed before this update. `undefined` on create. */\n originalDoc?: TDoc;\n /** The full incoming data payload being written. */\n data: Partial<TDoc>;\n /** The authenticated user, or `undefined` for unauthenticated requests. */\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange`/`afterDelete` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => TValue | undefined | Promise<TValue | undefined>;",
685
+ "members": []
686
+ },
687
+ {
688
+ "id": "@dyrected/core:FieldHook",
689
+ "name": "FieldHook",
690
+ "kind": "type",
691
+ "category": "hooks",
692
+ "sourcePackage": "@dyrected/core",
693
+ "description": "",
694
+ "signature": "export type FieldHook<\n TDoc extends object = Record<string, unknown>,\n TValue = unknown,\n> = FieldBeforeChangeHook<TValue, TDoc>;",
695
+ "members": []
696
+ },
697
+ {
698
+ "id": "@dyrected/core:FieldType",
699
+ "name": "FieldType",
700
+ "kind": "type",
701
+ "category": "fields",
702
+ "sourcePackage": "@dyrected/core",
703
+ "description": "Every field type supported by Dyrected.\n\n- Text group: `text`, `textarea`, `richText`, `email`, `url`, `icon`\n- Number/Bool: `number`, `boolean`\n- Date: `date`, `datetime`, `time`\n- Selection: `select`, `multiSelect`, `radio`\n- Relationship: `relationship`, `join`\n- Structural: `array`, `object`, `blocks`, `json`\n- Layout: `row`\n- Media: `image`",
704
+ "signature": 'export type FieldType =\n | "text"\n | "textarea"\n | "richText"\n | "number"\n | "boolean"\n | "date"\n | "datetime"\n | "time"\n | "select"\n | "multiSelect"\n | "radio"\n | "relationship"\n | "array"\n | "object"\n | "json"\n | "blocks"\n | "image"\n | "email"\n | "url"\n | "icon"\n | "join"\n | "row";',
705
+ "members": []
706
+ },
707
+ {
708
+ "id": "@dyrected/core:FileData",
709
+ "name": "FileData",
710
+ "kind": "interface",
711
+ "category": "adapters",
712
+ "sourcePackage": "@dyrected/core",
713
+ "description": "Metadata returned after a file is uploaded and stored.\nStored on the document in upload collections.",
714
+ "signature": "export interface FileData {\n filename: string;\n filesize?: number;\n mimeType: string;\n /** Public URL of the stored file. */\n url: string;\n width?: number;\n height?: number;\n focalPoint?: { x: number; y: number };\n /** Base64-encoded BlurHash string for progressive image loading. */\n blurhash?: string;\n /** `'upload'` for server-stored files; `'external'` for provider-managed files. */\n type?: \"upload\" | \"external\";\n provider?: string;\n provider_metadata?: unknown;\n [key: string]: unknown;\n}",
715
+ "members": [
716
+ {
717
+ "name": "filename",
718
+ "signature": "filename: string",
719
+ "description": ""
720
+ },
721
+ {
722
+ "name": "filesize",
723
+ "signature": "filesize?: number",
724
+ "description": ""
725
+ },
726
+ {
727
+ "name": "mimeType",
728
+ "signature": "mimeType: string",
729
+ "description": ""
730
+ },
731
+ {
732
+ "name": "url",
733
+ "signature": "url: string",
734
+ "description": "Public URL of the stored file."
735
+ },
736
+ {
737
+ "name": "width",
738
+ "signature": "width?: number",
739
+ "description": ""
740
+ },
741
+ {
742
+ "name": "height",
743
+ "signature": "height?: number",
744
+ "description": ""
745
+ },
746
+ {
747
+ "name": "focalPoint",
748
+ "signature": "focalPoint?: { x: number; y: number }",
749
+ "description": ""
750
+ },
751
+ {
752
+ "name": "blurhash",
753
+ "signature": "blurhash?: string",
754
+ "description": "Base64-encoded BlurHash string for progressive image loading."
755
+ },
756
+ {
757
+ "name": "type",
758
+ "signature": 'type?: "upload" | "external"',
759
+ "description": "`'upload'` for server-stored files; `'external'` for provider-managed files."
760
+ },
761
+ {
762
+ "name": "provider",
763
+ "signature": "provider?: string",
764
+ "description": ""
765
+ },
766
+ {
767
+ "name": "provider_metadata",
768
+ "signature": "provider_metadata?: unknown",
769
+ "description": ""
770
+ }
771
+ ]
772
+ },
773
+ {
774
+ "id": "@dyrected/core:GlobalAfterChangeHook",
775
+ "name": "GlobalAfterChangeHook",
776
+ "kind": "type",
777
+ "category": "hooks",
778
+ "sourcePackage": "@dyrected/core",
779
+ "description": "",
780
+ "signature": 'export type GlobalAfterChangeHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n doc: TDoc;\n previousDoc?: TDoc;\n req: HookRequestContext;\n user?: AuthenticatedUser;\n operation: "update";\n /**\n * Database adapter with full read/write access. The DB write for this\n * operation has already committed \u2014 safe for side-effect writes.\n */\n db: DatabaseAdapter;\n}) => void | Promise<void>;',
781
+ "members": []
782
+ },
783
+ {
784
+ "id": "@dyrected/core:GlobalAfterReadHook",
785
+ "name": "GlobalAfterReadHook",
786
+ "kind": "type",
787
+ "category": "hooks",
788
+ "sourcePackage": "@dyrected/core",
789
+ "description": "Runs after the global document is fetched, before the response is sent.",
790
+ "signature": "export type GlobalAfterReadHook<TDoc extends object = Record<string, unknown>> =\n (args: {\n doc: TDoc;\n req: HookRequestContext;\n user?: AuthenticatedUser;\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n }) => TDoc | Promise<TDoc>;",
791
+ "members": []
792
+ },
793
+ {
794
+ "id": "@dyrected/core:GlobalBeforeChangeHook",
795
+ "name": "GlobalBeforeChangeHook",
796
+ "kind": "type",
797
+ "category": "hooks",
798
+ "sourcePackage": "@dyrected/core",
799
+ "description": "Runs before the global document is updated.\nOperation is always `'update'` (globals cannot be created or deleted).",
800
+ "signature": 'export type GlobalBeforeChangeHook<\n TDoc extends object = Record<string, unknown>,\n> = (args: {\n data: Partial<TDoc>;\n doc?: TDoc;\n req: HookRequestContext;\n user?: AuthenticatedUser;\n operation: "update";\n /**\n * Database adapter for cross-collection reads. Write operations (create,\n * update, delete) will throw \u2014 use `afterChange` for writes.\n */\n db: ReadonlyDatabaseAdapter;\n}) => Partial<TDoc> | void | Promise<Partial<TDoc> | void>;',
801
+ "members": []
802
+ },
803
+ {
804
+ "id": "@dyrected/core:GlobalBeforeReadHook",
805
+ "name": "GlobalBeforeReadHook",
806
+ "kind": "type",
807
+ "category": "hooks",
808
+ "sourcePackage": "@dyrected/core",
809
+ "description": "",
810
+ "signature": "export type GlobalBeforeReadHook = CollectionBeforeReadHook;",
811
+ "members": []
812
+ },
813
+ {
814
+ "id": "@dyrected/core:GlobalConfig",
815
+ "name": "GlobalConfig",
816
+ "kind": "interface",
817
+ "category": "configuration",
818
+ "sourcePackage": "@dyrected/core",
819
+ "description": "Defines a Dyrected global \u2014 a singleton document without pagination or IDs.\n\nGlobals are ideal for site-wide settings, feature flags, or any data where\nthere is always exactly one record (e.g. `site-settings`, `navigation`, `theme`).\n\nPass your document's TypeScript type as the generic parameter `TDoc` to get\nfully typed hooks:\n\n```ts\ninterface SiteSettings {\n siteName: string\n tagline: string\n maintenanceMode: boolean\n}\n\nexport const Settings = defineGlobal<SiteSettings>({\n slug: 'site-settings',\n hooks: {\n afterChange: [({ doc }) => {\n // `doc` is typed as SiteSettings\n if (doc.maintenanceMode) alertOnCall()\n }],\n },\n fields: [...],\n})\n```",
820
+ "signature": "export interface GlobalConfig<TDoc extends object = Record<string, unknown>> {\n /**\n * Unique identifier for this global.\n * Used as the URL segment (`/api/globals/:slug`) and the storage key.\n */\n slug: string;\n\n /** Restricts this global to a specific site in a multi-tenant deployment. */\n siteId?: string;\n\n /**\n * If `true`, this global is shared across all sites in a multi-tenant\n * deployment.\n */\n shared?: boolean;\n\n /** Human-readable label shown in the Admin UI sidebar. */\n label?: string;\n\n /** Field definitions for this global's document schema. */\n fields: Field[];\n\n /** Access control for reading and updating this global. */\n access?: {\n read?: AccessFunction<TDoc>;\n update?: AccessFunction<TDoc>;\n };\n\n /**\n * Global-level lifecycle hooks.\n * Globals support `beforeRead`, `afterRead`, `beforeChange`, and `afterChange`.\n * There are no delete hooks since globals cannot be deleted.\n */\n hooks?: {\n beforeRead?: GlobalBeforeReadHook[];\n afterRead?: GlobalAfterReadHook<TDoc>[];\n beforeChange?: GlobalBeforeChangeHook<TDoc>[];\n afterChange?: GlobalAfterChangeHook<TDoc>[];\n };\n\n /** Admin UI configuration for this global. */\n admin?: {\n /**\n * Lucide icon displayed beside this global in the Admin sidebar.\n * Uses Lucide component names, e.g. `'Settings2'` or `'Palette'`.\n */\n icon?: AdminIconName;\n /** Groups this global under a named section in the Admin sidebar. */\n group?: string;\n /** If `true`, this global is not shown in the Admin UI sidebar. */\n hidden?: boolean;\n };\n\n /**\n * Initial data to seed this global with the first time it is fetched and\n * found to be empty.\n */\n initialData?: Partial<TDoc>;\n}",
821
+ "members": [
822
+ {
823
+ "name": "slug",
824
+ "signature": "slug: string",
825
+ "description": "Unique identifier for this global.\nUsed as the URL segment (`/api/globals/:slug`) and the storage key."
826
+ },
827
+ {
828
+ "name": "siteId",
829
+ "signature": "siteId?: string",
830
+ "description": "Restricts this global to a specific site in a multi-tenant deployment."
831
+ },
832
+ {
833
+ "name": "shared",
834
+ "signature": "shared?: boolean",
835
+ "description": "If `true`, this global is shared across all sites in a multi-tenant\ndeployment."
836
+ },
837
+ {
838
+ "name": "label",
839
+ "signature": "label?: string",
840
+ "description": "Human-readable label shown in the Admin UI sidebar."
841
+ },
842
+ {
843
+ "name": "fields",
844
+ "signature": "fields: Field[]",
845
+ "description": "Field definitions for this global's document schema."
846
+ },
847
+ {
848
+ "name": "access",
849
+ "signature": "access?: {\n read?: AccessFunction<TDoc>;\n update?: AccessFunction<TDoc>;\n }",
850
+ "description": "Access control for reading and updating this global."
851
+ },
852
+ {
853
+ "name": "hooks",
854
+ "signature": "hooks?: {\n beforeRead?: GlobalBeforeReadHook[];\n afterRead?: GlobalAfterReadHook<TDoc>[];\n beforeChange?: GlobalBeforeChangeHook<TDoc>[];\n afterChange?: GlobalAfterChangeHook<TDoc>[];\n }",
855
+ "description": "Global-level lifecycle hooks.\nGlobals support `beforeRead`, `afterRead`, `beforeChange`, and `afterChange`.\nThere are no delete hooks since globals cannot be deleted."
856
+ },
857
+ {
858
+ "name": "admin",
859
+ "signature": "admin?: {\n /**\n * Lucide icon displayed beside this global in the Admin sidebar.\n * Uses Lucide component names, e.g. `'Settings2'` or `'Palette'`.\n */\n icon?: AdminIconName;\n /** Groups this global under a named section in the Admin sidebar. */\n group?: string;\n /** If `true`, this global is not shown in the Admin UI sidebar. */\n hidden?: boolean;\n }",
860
+ "description": "Admin UI configuration for this global."
861
+ },
862
+ {
863
+ "name": "initialData",
864
+ "signature": "initialData?: Partial<TDoc>",
865
+ "description": "Initial data to seed this global with the first time it is fetched and\nfound to be empty."
866
+ }
867
+ ]
868
+ },
869
+ {
870
+ "id": "@dyrected/core:HookFunction",
871
+ "name": "HookFunction",
872
+ "kind": "type",
873
+ "category": "hooks",
874
+ "sourcePackage": "@dyrected/core",
875
+ "description": "",
876
+ "signature": 'export type HookFunction<TDoc extends object = Record<string, unknown>> =\n (args: {\n data?: Partial<TDoc>;\n doc?: TDoc;\n user?: AuthenticatedUser;\n req?: HookRequestContext;\n operation?: "create" | "update" | "delete";\n db?: DatabaseAdapter;\n [key: string]: unknown;\n }) => unknown | Promise<unknown>;',
877
+ "members": []
878
+ },
879
+ {
880
+ "id": "@dyrected/core:HookRequestContext",
881
+ "name": "HookRequestContext",
882
+ "kind": "interface",
883
+ "category": "hooks",
884
+ "sourcePackage": "@dyrected/core",
885
+ "description": "Minimum HTTP request context passed to every server-side hook and resolver.\n\nThe full Web Standard `Request` is available as `raw` when you need it, but\nmost hooks only need `query` (URL search parameters).",
886
+ "signature": "export interface HookRequestContext {\n /** Parsed URL query-string parameters, e.g. `{ page: '2', search: 'hello' }`. */\n query: Record<string, string>;\n /** Incoming HTTP headers, lowercased. */\n headers: Record<string, string>;\n /** The raw Web Standard `Request` object. Useful for streaming or advanced header inspection. */\n raw?: Request;\n}",
887
+ "members": [
888
+ {
889
+ "name": "query",
890
+ "signature": "query: Record<string, string>",
891
+ "description": "Parsed URL query-string parameters, e.g. `{ page: '2', search: 'hello' }`."
892
+ },
893
+ {
894
+ "name": "headers",
895
+ "signature": "headers: Record<string, string>",
896
+ "description": "Incoming HTTP headers, lowercased."
897
+ },
898
+ {
899
+ "name": "raw",
900
+ "signature": "raw?: Request",
901
+ "description": "The raw Web Standard `Request` object. Useful for streaming or advanced header inspection."
902
+ }
903
+ ]
904
+ },
905
+ {
906
+ "id": "@dyrected/core:ImageService",
907
+ "name": "ImageService",
908
+ "kind": "interface",
909
+ "category": "adapters",
910
+ "sourcePackage": "@dyrected/core",
911
+ "description": "Processes uploaded images \u2014 generates metadata (dimensions, BlurHash) and\nproduces resized variants defined in `UploadConfig.imageSizes`.",
912
+ "signature": 'export interface ImageService {\n process(args: {\n buffer: Uint8Array;\n mimeType: string;\n config?: CollectionConfig["upload"];\n focalPoint?: { x: number; y: number };\n }): Promise<{\n metadata: {\n width?: number;\n height?: number;\n /** Base64-encoded BlurHash for progressive loading. */\n blurhash?: string;\n };\n /** Generated image sizes keyed by their `name`. */\n sizes?: Record<\n string,\n { buffer: Uint8Array; width: number; height: number; filename: string }\n >;\n }>;\n}',
913
+ "members": [
914
+ {
915
+ "name": "process",
916
+ "signature": 'process(args: {\n buffer: Uint8Array;\n mimeType: string;\n config?: CollectionConfig["upload"];\n focalPoint?: { x: number; y: number };\n }): Promise<{\n metadata: {\n width?: number;\n height?: number;\n /** Base64-encoded BlurHash for progressive loading. */\n blurhash?: string;\n };\n /** Generated image sizes keyed by their `name`. */\n sizes?: Record<\n string,\n { buffer: Uint8Array; width: number; height: number; filename: string }\n >;\n }>',
917
+ "description": ""
918
+ }
919
+ ]
920
+ },
921
+ {
922
+ "id": "@dyrected/core:InferDocShape",
923
+ "name": "InferDocShape",
924
+ "kind": "type",
925
+ "category": "fields",
926
+ "sourcePackage": "@dyrected/core",
927
+ "description": "",
928
+ "signature": "export type InferDocShape<Fields extends readonly Field[]> =\n Fields extends readonly []\n ? Record<never, never>\n : Fields extends readonly [\n infer Head extends Field,\n ...infer Tail extends readonly Field[],\n ]\n ? InferFieldEntry<Head> & InferDocShape<Tail>\n : Record<string, unknown>;",
929
+ "members": []
930
+ },
931
+ {
932
+ "id": "@dyrected/core:initializeWorkflowDocument",
933
+ "name": "initializeWorkflowDocument",
934
+ "kind": "function",
935
+ "category": "workflows",
936
+ "sourcePackage": "@dyrected/core",
937
+ "description": "",
938
+ "signature": "export function initializeWorkflowDocument(data: Record<string, unknown>, workflow: WorkflowConfig)",
939
+ "members": []
940
+ },
941
+ {
942
+ "id": "@dyrected/core:LIFECYCLE_EVENTS_COLLECTION",
943
+ "name": "LIFECYCLE_EVENTS_COLLECTION",
944
+ "kind": "constant",
945
+ "category": "workflows",
946
+ "sourcePackage": "@dyrected/core",
947
+ "description": "",
948
+ "signature": 'export const LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";',
949
+ "members": []
950
+ },
951
+ {
952
+ "id": "@dyrected/core:LifecycleEvent",
953
+ "name": "LifecycleEvent",
954
+ "kind": "interface",
955
+ "category": "workflows",
956
+ "sourcePackage": "@dyrected/core",
957
+ "description": "",
958
+ "signature": 'export interface LifecycleEvent<TPayload = Record<string, unknown>> {\n id: string;\n name: LifecycleEventName;\n collection: string;\n documentId: string;\n occurredAt: string;\n actorId?: string;\n payload: TPayload;\n attempts: number;\n status: "pending" | "processing" | "delivered" | "failed";\n nextAttemptAt?: string;\n deliveredAt?: string;\n lastError?: string;\n}',
959
+ "members": [
960
+ {
961
+ "name": "id",
962
+ "signature": "id: string",
963
+ "description": ""
964
+ },
965
+ {
966
+ "name": "name",
967
+ "signature": "name: LifecycleEventName",
968
+ "description": ""
969
+ },
970
+ {
971
+ "name": "collection",
972
+ "signature": "collection: string",
973
+ "description": ""
974
+ },
975
+ {
976
+ "name": "documentId",
977
+ "signature": "documentId: string",
978
+ "description": ""
979
+ },
980
+ {
981
+ "name": "occurredAt",
982
+ "signature": "occurredAt: string",
983
+ "description": ""
984
+ },
985
+ {
986
+ "name": "actorId",
987
+ "signature": "actorId?: string",
988
+ "description": ""
989
+ },
990
+ {
991
+ "name": "payload",
992
+ "signature": "payload: TPayload",
993
+ "description": ""
994
+ },
995
+ {
996
+ "name": "attempts",
997
+ "signature": "attempts: number",
998
+ "description": ""
999
+ },
1000
+ {
1001
+ "name": "status",
1002
+ "signature": 'status: "pending" | "processing" | "delivered" | "failed"',
1003
+ "description": ""
1004
+ },
1005
+ {
1006
+ "name": "nextAttemptAt",
1007
+ "signature": "nextAttemptAt?: string",
1008
+ "description": ""
1009
+ },
1010
+ {
1011
+ "name": "deliveredAt",
1012
+ "signature": "deliveredAt?: string",
1013
+ "description": ""
1014
+ },
1015
+ {
1016
+ "name": "lastError",
1017
+ "signature": "lastError?: string",
1018
+ "description": ""
1019
+ }
1020
+ ]
1021
+ },
1022
+ {
1023
+ "id": "@dyrected/core:LifecycleEventHandler",
1024
+ "name": "LifecycleEventHandler",
1025
+ "kind": "type",
1026
+ "category": "workflows",
1027
+ "sourcePackage": "@dyrected/core",
1028
+ "description": "",
1029
+ "signature": "export type LifecycleEventHandler = (\n event: LifecycleEvent,\n) => void | Promise<void>;",
1030
+ "members": []
1031
+ },
1032
+ {
1033
+ "id": "@dyrected/core:LifecycleEventName",
1034
+ "name": "LifecycleEventName",
1035
+ "kind": "type",
1036
+ "category": "workflows",
1037
+ "sourcePackage": "@dyrected/core",
1038
+ "description": "",
1039
+ "signature": "export type LifecycleEventName = (typeof LIFECYCLE_EVENT_NAMES)[number];",
1040
+ "members": []
1041
+ },
1042
+ {
1043
+ "id": "@dyrected/core:materializeWorkflowDocument",
1044
+ "name": "materializeWorkflowDocument",
1045
+ "kind": "function",
1046
+ "category": "workflows",
1047
+ "sourcePackage": "@dyrected/core",
1048
+ "description": "",
1049
+ "signature": "export function materializeWorkflowDocument(\n doc: BaseDocument,\n workflow: WorkflowConfig,\n user?: AuthenticatedUser,\n): BaseDocument | null",
1050
+ "members": []
1051
+ },
1052
+ {
1053
+ "id": "@dyrected/core:PaginatedResult",
1054
+ "name": "PaginatedResult",
1055
+ "kind": "interface",
1056
+ "category": "adapters",
1057
+ "sourcePackage": "@dyrected/core",
1058
+ "description": "The envelope returned by collection list endpoints (`GET /api/collections/:slug`).",
1059
+ "signature": "export interface PaginatedResult<T = Record<string, any>> {\n /** The documents on the current page. */\n docs: T[];\n /** Total number of documents matching the query (across all pages). */\n total: number;\n /** Maximum number of documents per page as requested. */\n limit: number;\n /** The current page number (1-indexed). */\n page: number;\n /** Total number of pages given the current `limit`. */\n totalPages: number;\n /** Whether a next page exists. */\n hasNextPage: boolean;\n /** Whether a previous page exists. */\n hasPrevPage: boolean;\n}",
1060
+ "members": [
1061
+ {
1062
+ "name": "docs",
1063
+ "signature": "docs: T[]",
1064
+ "description": "The documents on the current page."
1065
+ },
1066
+ {
1067
+ "name": "total",
1068
+ "signature": "total: number",
1069
+ "description": "Total number of documents matching the query (across all pages)."
1070
+ },
1071
+ {
1072
+ "name": "limit",
1073
+ "signature": "limit: number",
1074
+ "description": "Maximum number of documents per page as requested."
1075
+ },
1076
+ {
1077
+ "name": "page",
1078
+ "signature": "page: number",
1079
+ "description": "The current page number (1-indexed)."
1080
+ },
1081
+ {
1082
+ "name": "totalPages",
1083
+ "signature": "totalPages: number",
1084
+ "description": "Total number of pages given the current `limit`."
1085
+ },
1086
+ {
1087
+ "name": "hasNextPage",
1088
+ "signature": "hasNextPage: boolean",
1089
+ "description": "Whether a next page exists."
1090
+ },
1091
+ {
1092
+ "name": "hasPrevPage",
1093
+ "signature": "hasPrevPage: boolean",
1094
+ "description": "Whether a previous page exists."
1095
+ }
1096
+ ]
1097
+ },
1098
+ {
1099
+ "id": "@dyrected/core:publishingWorkflow",
1100
+ "name": "publishingWorkflow",
1101
+ "kind": "function",
1102
+ "category": "workflows",
1103
+ "sourcePackage": "@dyrected/core",
1104
+ "description": "",
1105
+ "signature": "export function publishingWorkflow(): WorkflowConfig",
1106
+ "members": []
1107
+ },
1108
+ {
1109
+ "id": "@dyrected/core:ReadonlyDatabaseAdapter",
1110
+ "name": "ReadonlyDatabaseAdapter",
1111
+ "kind": "type",
1112
+ "category": "adapters",
1113
+ "sourcePackage": "@dyrected/core",
1114
+ "description": "",
1115
+ "signature": 'export type ReadonlyDatabaseAdapter = Pick<\n DatabaseAdapter,\n "find" | "findOne" | "getGlobal"\n>;',
1116
+ "members": []
1117
+ },
1118
+ {
1119
+ "id": "@dyrected/core:saveWorkflowDraft",
1120
+ "name": "saveWorkflowDraft",
1121
+ "kind": "function",
1122
+ "category": "workflows",
1123
+ "sourcePackage": "@dyrected/core",
1124
+ "description": "",
1125
+ "signature": "export async function saveWorkflowDraft(args: {\n config: DyrectedConfig;\n collection: CollectionConfig;\n id: string;\n originalDoc: BaseDocument;\n data: Record<string, unknown>;\n user?: AuthenticatedUser;\n}): Promise<{ doc: BaseDocument; event: LifecycleEvent }>",
1126
+ "members": []
1127
+ },
1128
+ {
1129
+ "id": "@dyrected/core:StorageAdapter",
1130
+ "name": "StorageAdapter",
1131
+ "kind": "interface",
1132
+ "category": "adapters",
1133
+ "sourcePackage": "@dyrected/core",
1134
+ "description": "The interface every storage adapter must implement.\n\nDyrected ships adapters for local disk, S3, Cloudflare R2, Cloudinary, and\nBackblaze B2. Implement this interface to use any other storage provider.",
1135
+ "signature": "export interface StorageAdapter {\n /**\n * Upload a file and return its metadata (URL, dimensions, etc.).\n * The `prefix` is a path prefix used for multi-tenant setups.\n */\n upload(args: {\n filename: string;\n buffer: Uint8Array;\n mimeType: string;\n prefix?: string;\n }): Promise<FileData>;\n\n /** Delete a file by its stored filename. */\n delete(args: { filename: string }): Promise<void>;\n\n /** Return the public URL for a stored file. */\n getURL(args: { filename: string }): string;\n\n /**\n * Retrieve the file's raw bytes and MIME type for serving via the API.\n * Only needed by adapters that serve files through the Dyrected API\n * (e.g. `LocalStorage`). Cloud adapters return `null` here and rely on\n * direct CDN URLs instead.\n */\n resolve?(args: {\n filename: string;\n }): Promise<{ buffer: Uint8Array; mimeType: string } | null>;\n}",
1136
+ "members": [
1137
+ {
1138
+ "name": "upload",
1139
+ "signature": "upload(args: {\n filename: string;\n buffer: Uint8Array;\n mimeType: string;\n prefix?: string;\n }): Promise<FileData>",
1140
+ "description": "Upload a file and return its metadata (URL, dimensions, etc.).\nThe `prefix` is a path prefix used for multi-tenant setups."
1141
+ },
1142
+ {
1143
+ "name": "delete",
1144
+ "signature": "delete(args: { filename: string }): Promise<void>",
1145
+ "description": "Delete a file by its stored filename."
1146
+ },
1147
+ {
1148
+ "name": "getURL",
1149
+ "signature": "getURL(args: { filename: string }): string",
1150
+ "description": "Return the public URL for a stored file."
1151
+ },
1152
+ {
1153
+ "name": "resolve",
1154
+ "signature": "resolve(args: {\n filename: string;\n }): Promise<{ buffer: Uint8Array; mimeType: string } | null>",
1155
+ "description": "Retrieve the file's raw bytes and MIME type for serving via the API.\nOnly needed by adapters that serve files through the Dyrected API\n(e.g. `LocalStorage`). Cloud adapters return `null` here and rely on\ndirect CDN URLs instead."
1156
+ }
1157
+ ]
1158
+ },
1159
+ {
1160
+ "id": "@dyrected/core:SystemDocFields",
1161
+ "name": "SystemDocFields",
1162
+ "kind": "type",
1163
+ "category": "fields",
1164
+ "sourcePackage": "@dyrected/core",
1165
+ "description": "Audit fields automatically injected into every collection document at runtime.\nThey are always present in API responses but hidden in the Admin UI by default.",
1166
+ "signature": "export type SystemDocFields = {\n createdAt?: string;\n updatedAt?: string;\n /** ID of the user who created the document. */\n createdBy?: string;\n /** ID of the user who last updated the document. */\n updatedBy?: string;\n};",
1167
+ "members": []
1168
+ },
1169
+ {
1170
+ "id": "@dyrected/core:transitionWorkflow",
1171
+ "name": "transitionWorkflow",
1172
+ "kind": "function",
1173
+ "category": "workflows",
1174
+ "sourcePackage": "@dyrected/core",
1175
+ "description": "",
1176
+ "signature": "export async function transitionWorkflow(args: {\n config: DyrectedConfig;\n collection: CollectionConfig;\n id: string;\n transitionName: string;\n expectedRevision?: number;\n comment?: string;\n user?: AuthenticatedUser;\n req: HookRequestContext;\n}): Promise<BaseDocument>",
1177
+ "members": []
1178
+ },
1179
+ {
1180
+ "id": "@dyrected/core:UploadConfig",
1181
+ "name": "UploadConfig",
1182
+ "kind": "interface",
1183
+ "category": "configuration",
1184
+ "sourcePackage": "@dyrected/core",
1185
+ "description": "Upload configuration for collections that store files.\nSet `upload: true` on the collection to use all defaults, or pass this\nobject to customise allowed types, size limits, and image processing.",
1186
+ "signature": "export interface UploadConfig {\n /**\n * Allowed MIME types. Requests with other MIME types are rejected with `400`.\n * @example ['image/jpeg', 'image/png', 'image/webp', 'application/pdf']\n */\n allowedMimeTypes?: string[];\n\n /**\n * Maximum file size in **bytes**.\n * @example 10 * 1024 * 1024 // 10 MB\n */\n maxFileSize?: number;\n\n /**\n * Local filesystem path where files are stored.\n * Only used by the `LocalStorage` adapter.\n */\n staticDir?: string;\n\n /**\n * Public URL prefix prepended to filenames when generating download URLs.\n * Only used by the `LocalStorage` adapter.\n * @example '/uploads'\n */\n staticURL?: string;\n\n /**\n * The `imageSizes` entry name to use as the thumbnail in the Admin media grid.\n * @example 'thumbnail'\n */\n adminThumbnail?: string;\n\n /**\n * Additional image sizes to generate when an image is uploaded.\n * Requires an `ImageService` to be configured (e.g. `@dyrected/image-sharp`).\n *\n * @example\n * imageSizes: [\n * { name: 'thumbnail', width: 300, height: 300, fit: 'cover' },\n * { name: 'card', width: 800 },\n * ]\n */\n imageSizes?: {\n /** Identifier used to access this size, e.g. `doc.sizes.thumbnail`. */\n name: string;\n /** Target width in pixels. */\n width?: number;\n /** Target height in pixels. */\n height?: number;\n /** sharp crop strategy (`'entropy'`, `'attention'`, etc.). */\n crop?: string;\n /**\n * sharp fit strategy.\n * @see https://sharp.pixelplumbing.com/api-resize#parameters\n */\n fit?: string;\n /**\n * If `true`, images smaller than the target size are not upscaled.\n * @default true\n */\n withoutEnlargement?: boolean;\n /** Additional sharp format options forwarded to the output pipeline. */\n formatOptions?: Record<string, unknown>;\n }[];\n}",
1187
+ "members": [
1188
+ {
1189
+ "name": "allowedMimeTypes",
1190
+ "signature": "allowedMimeTypes?: string[]",
1191
+ "description": "Allowed MIME types. Requests with other MIME types are rejected with `400`."
1192
+ },
1193
+ {
1194
+ "name": "maxFileSize",
1195
+ "signature": "maxFileSize?: number",
1196
+ "description": "Maximum file size in **bytes**."
1197
+ },
1198
+ {
1199
+ "name": "staticDir",
1200
+ "signature": "staticDir?: string",
1201
+ "description": "Local filesystem path where files are stored.\nOnly used by the `LocalStorage` adapter."
1202
+ },
1203
+ {
1204
+ "name": "staticURL",
1205
+ "signature": "staticURL?: string",
1206
+ "description": "Public URL prefix prepended to filenames when generating download URLs.\nOnly used by the `LocalStorage` adapter."
1207
+ },
1208
+ {
1209
+ "name": "adminThumbnail",
1210
+ "signature": "adminThumbnail?: string",
1211
+ "description": "The `imageSizes` entry name to use as the thumbnail in the Admin media grid."
1212
+ },
1213
+ {
1214
+ "name": "imageSizes",
1215
+ "signature": "imageSizes?: {\n /** Identifier used to access this size, e.g. `doc.sizes.thumbnail`. */\n name: string;\n /** Target width in pixels. */\n width?: number;\n /** Target height in pixels. */\n height?: number;\n /** sharp crop strategy (`'entropy'`, `'attention'`, etc.). */\n crop?: string;\n /**\n * sharp fit strategy.\n * @see https://sharp.pixelplumbing.com/api-resize#parameters\n */\n fit?: string;\n /**\n * If `true`, images smaller than the target size are not upscaled.\n * @default true\n */\n withoutEnlargement?: boolean;\n /** Additional sharp format options forwarded to the output pipeline. */\n formatOptions?: Record<string, unknown>;\n }[]",
1216
+ "description": "Additional image sizes to generate when an image is uploaded.\nRequires an `ImageService` to be configured (e.g. `@dyrected/image-sharp`)."
1217
+ }
1218
+ ]
1219
+ },
1220
+ {
1221
+ "id": "@dyrected/core:UploadDocFields",
1222
+ "name": "UploadDocFields",
1223
+ "kind": "type",
1224
+ "category": "fields",
1225
+ "sourcePackage": "@dyrected/core",
1226
+ "description": "Fields automatically added to upload/media collection documents.\nThese mirror the `FileData` interface returned by storage adapters.",
1227
+ "signature": "export type UploadDocFields = {\n filename: string;\n filesize?: number;\n mimeType: string;\n /** Public URL of the stored file. */\n url: string;\n width?: number;\n height?: number;\n focalPoint?: { x: number; y: number };\n /** Base64 BlurHash for progressive image loading. */\n blurhash?: string;\n sizes?: Record<\n string,\n { filename?: string; url?: string; width?: number; height?: number }\n >;\n};",
1228
+ "members": []
1229
+ },
1230
+ {
1231
+ "id": "@dyrected/core:WORKFLOW_HISTORY_COLLECTION",
1232
+ "name": "WORKFLOW_HISTORY_COLLECTION",
1233
+ "kind": "constant",
1234
+ "category": "workflows",
1235
+ "sourcePackage": "@dyrected/core",
1236
+ "description": "",
1237
+ "signature": 'export const WORKFLOW_HISTORY_COLLECTION = "__workflow_history";',
1238
+ "members": []
1239
+ },
1240
+ {
1241
+ "id": "@dyrected/core:workflowCapabilities",
1242
+ "name": "workflowCapabilities",
1243
+ "kind": "function",
1244
+ "category": "workflows",
1245
+ "sourcePackage": "@dyrected/core",
1246
+ "description": "",
1247
+ "signature": "export function workflowCapabilities(workflow: WorkflowConfig, user?: AuthenticatedUser): Set<string>",
1248
+ "members": []
1249
+ },
1250
+ {
1251
+ "id": "@dyrected/core:WorkflowConfig",
1252
+ "name": "WorkflowConfig",
1253
+ "kind": "interface",
1254
+ "category": "workflows",
1255
+ "sourcePackage": "@dyrected/core",
1256
+ "description": "",
1257
+ "signature": "export interface WorkflowConfig<TDoc extends object = Record<string, unknown>> {\n initialState: string;\n /** State used for a new working revision created from published content. */\n draftState?: string;\n states: WorkflowState[];\n transitions: WorkflowTransition[];\n /** Maps values in `user.roles` to workflow capabilities. */\n roles?: WorkflowRole[];\n hooks?: {\n beforeTransition?: CollectionBeforeTransitionHook<TDoc>[];\n afterTransition?: CollectionAfterTransitionHook<TDoc>[];\n };\n}",
1258
+ "members": [
1259
+ {
1260
+ "name": "initialState",
1261
+ "signature": "initialState: string",
1262
+ "description": ""
1263
+ },
1264
+ {
1265
+ "name": "draftState",
1266
+ "signature": "draftState?: string",
1267
+ "description": "State used for a new working revision created from published content."
1268
+ },
1269
+ {
1270
+ "name": "states",
1271
+ "signature": "states: WorkflowState[]",
1272
+ "description": ""
1273
+ },
1274
+ {
1275
+ "name": "transitions",
1276
+ "signature": "transitions: WorkflowTransition[]",
1277
+ "description": ""
1278
+ },
1279
+ {
1280
+ "name": "roles",
1281
+ "signature": "roles?: WorkflowRole[]",
1282
+ "description": "Maps values in `user.roles` to workflow capabilities."
1283
+ },
1284
+ {
1285
+ "name": "hooks",
1286
+ "signature": "hooks?: {\n beforeTransition?: CollectionBeforeTransitionHook<TDoc>[];\n afterTransition?: CollectionAfterTransitionHook<TDoc>[];\n }",
1287
+ "description": ""
1288
+ }
1289
+ ]
1290
+ },
1291
+ {
1292
+ "id": "@dyrected/core:WorkflowMetadata",
1293
+ "name": "WorkflowMetadata",
1294
+ "kind": "interface",
1295
+ "category": "workflows",
1296
+ "sourcePackage": "@dyrected/core",
1297
+ "description": "",
1298
+ "signature": "export interface WorkflowMetadata {\n state: string;\n revision: number;\n publishedRevision?: number;\n publishedAt?: string;\n publishedBy?: string;\n /** Transitions currently allowed for the requesting user. Response-only. */\n availableTransitions?: string[];\n}",
1299
+ "members": [
1300
+ {
1301
+ "name": "state",
1302
+ "signature": "state: string",
1303
+ "description": ""
1304
+ },
1305
+ {
1306
+ "name": "revision",
1307
+ "signature": "revision: number",
1308
+ "description": ""
1309
+ },
1310
+ {
1311
+ "name": "publishedRevision",
1312
+ "signature": "publishedRevision?: number",
1313
+ "description": ""
1314
+ },
1315
+ {
1316
+ "name": "publishedAt",
1317
+ "signature": "publishedAt?: string",
1318
+ "description": ""
1319
+ },
1320
+ {
1321
+ "name": "publishedBy",
1322
+ "signature": "publishedBy?: string",
1323
+ "description": ""
1324
+ },
1325
+ {
1326
+ "name": "availableTransitions",
1327
+ "signature": "availableTransitions?: string[]",
1328
+ "description": "Transitions currently allowed for the requesting user. Response-only."
1329
+ }
1330
+ ]
1331
+ },
1332
+ {
1333
+ "id": "@dyrected/core:WorkflowRole",
1334
+ "name": "WorkflowRole",
1335
+ "kind": "interface",
1336
+ "category": "workflows",
1337
+ "sourcePackage": "@dyrected/core",
1338
+ "description": "",
1339
+ "signature": "export interface WorkflowRole {\n /** Existing user role value, for example `editor` or `publisher`. */\n role: string;\n capabilities: string[];\n}",
1340
+ "members": [
1341
+ {
1342
+ "name": "role",
1343
+ "signature": "role: string",
1344
+ "description": "Existing user role value, for example `editor` or `publisher`."
1345
+ },
1346
+ {
1347
+ "name": "capabilities",
1348
+ "signature": "capabilities: string[]",
1349
+ "description": ""
1350
+ }
1351
+ ]
1352
+ },
1353
+ {
1354
+ "id": "@dyrected/core:WorkflowState",
1355
+ "name": "WorkflowState",
1356
+ "kind": "interface",
1357
+ "category": "workflows",
1358
+ "sourcePackage": "@dyrected/core",
1359
+ "description": "",
1360
+ "signature": 'export interface WorkflowState {\n /** Stable machine-readable state key. */\n name: string;\n /** Label rendered in the Admin UI. */\n label: string;\n /** Marks the state whose revision is visible to public readers. */\n published?: boolean;\n /** Optional visual tone used by the Admin UI. */\n color?: "neutral" | "warning" | "success" | "danger" | "info";\n}',
1361
+ "members": [
1362
+ {
1363
+ "name": "name",
1364
+ "signature": "name: string",
1365
+ "description": "Stable machine-readable state key."
1366
+ },
1367
+ {
1368
+ "name": "label",
1369
+ "signature": "label: string",
1370
+ "description": "Label rendered in the Admin UI."
1371
+ },
1372
+ {
1373
+ "name": "published",
1374
+ "signature": "published?: boolean",
1375
+ "description": "Marks the state whose revision is visible to public readers."
1376
+ },
1377
+ {
1378
+ "name": "color",
1379
+ "signature": 'color?: "neutral" | "warning" | "success" | "danger" | "info"',
1380
+ "description": "Optional visual tone used by the Admin UI."
1381
+ }
1382
+ ]
1383
+ },
1384
+ {
1385
+ "id": "@dyrected/core:WorkflowTransition",
1386
+ "name": "WorkflowTransition",
1387
+ "kind": "interface",
1388
+ "category": "workflows",
1389
+ "sourcePackage": "@dyrected/core",
1390
+ "description": "",
1391
+ "signature": "export interface WorkflowTransition {\n /** Stable transition key used by the REST and SDK APIs. */\n name: string;\n label: string;\n from: string | string[];\n to: string;\n /** Every listed capability is required. */\n requiredCapabilities?: string[];\n /** Require a non-empty comment when performing the transition. */\n requireComment?: boolean;\n /** Remove the public snapshot after this transition commits. */\n unpublish?: boolean;\n}",
1392
+ "members": [
1393
+ {
1394
+ "name": "name",
1395
+ "signature": "name: string",
1396
+ "description": "Stable transition key used by the REST and SDK APIs."
1397
+ },
1398
+ {
1399
+ "name": "label",
1400
+ "signature": "label: string",
1401
+ "description": ""
1402
+ },
1403
+ {
1404
+ "name": "from",
1405
+ "signature": "from: string | string[]",
1406
+ "description": ""
1407
+ },
1408
+ {
1409
+ "name": "to",
1410
+ "signature": "to: string",
1411
+ "description": ""
1412
+ },
1413
+ {
1414
+ "name": "requiredCapabilities",
1415
+ "signature": "requiredCapabilities?: string[]",
1416
+ "description": "Every listed capability is required."
1417
+ },
1418
+ {
1419
+ "name": "requireComment",
1420
+ "signature": "requireComment?: boolean",
1421
+ "description": "Require a non-empty comment when performing the transition."
1422
+ },
1423
+ {
1424
+ "name": "unpublish",
1425
+ "signature": "unpublish?: boolean",
1426
+ "description": "Remove the public snapshot after this transition commits."
1427
+ }
1428
+ ]
1429
+ },
1430
+ {
1431
+ "id": "@dyrected/core:WorkflowTransitionContext",
1432
+ "name": "WorkflowTransitionContext",
1433
+ "kind": "interface",
1434
+ "category": "workflows",
1435
+ "sourcePackage": "@dyrected/core",
1436
+ "description": "",
1437
+ "signature": "export interface WorkflowTransitionContext<\n TDoc extends object = Record<string, unknown>,\n> {\n transition: WorkflowTransition;\n from: string;\n to: string;\n doc: TDoc;\n user?: AuthenticatedUser;\n comment?: string;\n req: HookRequestContext;\n db: DatabaseAdapter;\n}",
1438
+ "members": [
1439
+ {
1440
+ "name": "transition",
1441
+ "signature": "transition: WorkflowTransition",
1442
+ "description": ""
1443
+ },
1444
+ {
1445
+ "name": "from",
1446
+ "signature": "from: string",
1447
+ "description": ""
1448
+ },
1449
+ {
1450
+ "name": "to",
1451
+ "signature": "to: string",
1452
+ "description": ""
1453
+ },
1454
+ {
1455
+ "name": "doc",
1456
+ "signature": "doc: TDoc",
1457
+ "description": ""
1458
+ },
1459
+ {
1460
+ "name": "user",
1461
+ "signature": "user?: AuthenticatedUser",
1462
+ "description": ""
1463
+ },
1464
+ {
1465
+ "name": "comment",
1466
+ "signature": "comment?: string",
1467
+ "description": ""
1468
+ },
1469
+ {
1470
+ "name": "req",
1471
+ "signature": "req: HookRequestContext",
1472
+ "description": ""
1473
+ },
1474
+ {
1475
+ "name": "db",
1476
+ "signature": "db: DatabaseAdapter",
1477
+ "description": ""
1478
+ }
1479
+ ]
1480
+ },
1481
+ {
1482
+ "id": "@dyrected/sdk:BaseSchema",
1483
+ "name": "BaseSchema",
1484
+ "kind": "interface",
1485
+ "category": "sdk",
1486
+ "sourcePackage": "@dyrected/sdk",
1487
+ "description": "",
1488
+ "signature": "export interface BaseSchema {\n collections: Record<string, any>;\n globals: Record<string, any>;\n}",
1489
+ "members": [
1490
+ {
1491
+ "name": "collections",
1492
+ "signature": "collections: Record<string, any>",
1493
+ "description": ""
1494
+ },
1495
+ {
1496
+ "name": "globals",
1497
+ "signature": "globals: Record<string, any>",
1498
+ "description": ""
1499
+ }
1500
+ ]
1501
+ },
1502
+ {
1503
+ "id": "@dyrected/sdk:createClient",
1504
+ "name": "createClient",
1505
+ "kind": "function",
1506
+ "category": "sdk",
1507
+ "sourcePackage": "@dyrected/sdk",
1508
+ "description": "",
1509
+ "signature": "export function createClient<\n TSchema extends { collections: any; globals: any } = any,\n>(config: DyrectedClientConfig): DyrectedClient<TSchema>",
1510
+ "members": []
1511
+ },
1512
+ {
1513
+ "id": "@dyrected/sdk:DyrectedClient",
1514
+ "name": "DyrectedClient",
1515
+ "kind": "class",
1516
+ "category": "sdk",
1517
+ "sourcePackage": "@dyrected/sdk",
1518
+ "description": "",
1519
+ "signature": "export class DyrectedClient<TSchema extends BaseSchema = any> {\n}",
1520
+ "members": [
1521
+ {
1522
+ "name": "setToken",
1523
+ "signature": "setToken(token: string): void",
1524
+ "description": "Update the Authorization header with a Bearer token.\nCall this after a successful login."
1525
+ },
1526
+ {
1527
+ "name": "clearToken",
1528
+ "signature": "clearToken(): void",
1529
+ "description": "Remove the Authorization header.\nCall this after logout."
1530
+ },
1531
+ {
1532
+ "name": "getAuthHeaders",
1533
+ "signature": "getAuthHeaders(): Record<string, string>",
1534
+ "description": "Returns the headers needed to authenticate raw `fetch()` calls made outside\nthe SDK client (e.g. streaming endpoints, dynamic options). Includes the\nAuthorization bearer token (if set), x-api-key, and x-site-id."
1535
+ },
1536
+ {
1537
+ "name": "getBaseUrl",
1538
+ "signature": "getBaseUrl()",
1539
+ "description": ""
1540
+ },
1541
+ {
1542
+ "name": "getSchemas",
1543
+ "signature": "getSchemas(): Promise<{ collections: any[]; globals: any[] }>",
1544
+ "description": ""
1545
+ },
1546
+ {
1547
+ "name": "getPreference",
1548
+ "signature": "getPreference<T = unknown>(key: string): Promise<{ key: string; value: T | null }>",
1549
+ "description": ""
1550
+ },
1551
+ {
1552
+ "name": "setPreference",
1553
+ "signature": "setPreference<T = unknown>(key: string, value: T): Promise<{ key: string; value: T }>",
1554
+ "description": ""
1555
+ },
1556
+ {
1557
+ "name": "getPreviewData",
1558
+ "signature": "getPreviewData(token: string): Promise<any>",
1559
+ "description": 'Fetch draft data for a specific preview token.\nUsed in "token" preview mode.'
1560
+ },
1561
+ {
1562
+ "name": "find",
1563
+ "signature": 'find<K extends keyof TSchema["collections"]>(collection: K & string, args: QueryArgs = {}): Promise<PaginatedResult<TSchema["collections"][K]>>',
1564
+ "description": ""
1565
+ },
1566
+ {
1567
+ "name": "collection",
1568
+ "signature": 'collection<K extends keyof TSchema["collections"]>(slug: K & string)',
1569
+ "description": "Returns a fluent query builder for a collection."
1570
+ },
1571
+ {
1572
+ "name": "global",
1573
+ "signature": 'global<K extends keyof TSchema["globals"]>(slug: K & string)',
1574
+ "description": "Access a global by its slug with a fluent builder."
1575
+ },
1576
+ {
1577
+ "name": "findOne",
1578
+ "signature": "findOne<T = any>(collection: string, id: string, args: { depth?: number; initialData?: T } = {}): Promise<T>",
1579
+ "description": ""
1580
+ },
1581
+ {
1582
+ "name": "create",
1583
+ "signature": "create<T = any>(collection: string, data: any): Promise<T>",
1584
+ "description": ""
1585
+ },
1586
+ {
1587
+ "name": "update",
1588
+ "signature": "update<T = any>(collection: string, id: string, data: any): Promise<T>",
1589
+ "description": ""
1590
+ },
1591
+ {
1592
+ "name": "delete",
1593
+ "signature": "delete(collection: string, id: string): Promise<{ message: string }>",
1594
+ "description": ""
1595
+ },
1596
+ {
1597
+ "name": "transition",
1598
+ "signature": "transition<T = WorkflowDocument>(collection: string, id: string, transitionName: string, opts: TransitionOptions = {}): Promise<T>",
1599
+ "description": "Perform a workflow transition on a document.\n\nSends `POST /api/collections/:collection/:id/transitions/:transition`.\nRequires the client to have a valid bearer token set via `setToken()`."
1600
+ },
1601
+ {
1602
+ "name": "workflowHistory",
1603
+ "signature": "workflowHistory(collection: string, id: string, args: { limit?: number } = {}): Promise<PaginatedResult<WorkflowHistoryEntry>>",
1604
+ "description": "Fetch the workflow history for a document.\n\nSends `GET /api/collections/:collection/:id/workflow-history`."
1605
+ },
1606
+ {
1607
+ "name": "deleteMany",
1608
+ "signature": "deleteMany(collection: string, ids: string[]): Promise<{ message: string }>",
1609
+ "description": ""
1610
+ },
1611
+ {
1612
+ "name": "getGlobal",
1613
+ "signature": "getGlobal<T = any>(slug: string, args: { depth?: number; initialData?: T } = {}): Promise<T>",
1614
+ "description": ""
1615
+ },
1616
+ {
1617
+ "name": "updateGlobal",
1618
+ "signature": "updateGlobal<T = any>(slug: string, data: any): Promise<T>",
1619
+ "description": ""
1620
+ },
1621
+ {
1622
+ "name": "listMedia",
1623
+ "signature": 'listMedia(args: QueryArgs = {}, collection: string = "media"): Promise<PaginatedResult<Media>>',
1624
+ "description": ""
1625
+ },
1626
+ {
1627
+ "name": "uploadMedia",
1628
+ "signature": 'uploadMedia(file: File, collection: string = "media"): Promise<Media>',
1629
+ "description": ""
1630
+ },
1631
+ {
1632
+ "name": "deleteMedia",
1633
+ "signature": 'deleteMedia(id: string, collection: string = "media"): Promise<{ message: string }>',
1634
+ "description": ""
1635
+ }
1636
+ ]
1637
+ },
1638
+ {
1639
+ "id": "@dyrected/sdk:DyrectedClientConfig",
1640
+ "name": "DyrectedClientConfig",
1641
+ "kind": "interface",
1642
+ "category": "sdk",
1643
+ "sourcePackage": "@dyrected/sdk",
1644
+ "description": "",
1645
+ "signature": "export interface DyrectedClientConfig {\n baseUrl: string;\n apiKey?: string;\n siteId?: string;\n headers?: Record<string, string>;\n fetch?: typeof fetch;\n /** Default depth for relationship population. Applied to every request unless overridden per-call. */\n defaultDepth?: number;\n}",
1646
+ "members": [
1647
+ {
1648
+ "name": "baseUrl",
1649
+ "signature": "baseUrl: string",
1650
+ "description": ""
1651
+ },
1652
+ {
1653
+ "name": "apiKey",
1654
+ "signature": "apiKey?: string",
1655
+ "description": ""
1656
+ },
1657
+ {
1658
+ "name": "siteId",
1659
+ "signature": "siteId?: string",
1660
+ "description": ""
1661
+ },
1662
+ {
1663
+ "name": "headers",
1664
+ "signature": "headers?: Record<string, string>",
1665
+ "description": ""
1666
+ },
1667
+ {
1668
+ "name": "fetch",
1669
+ "signature": "fetch?: typeof fetch",
1670
+ "description": ""
1671
+ },
1672
+ {
1673
+ "name": "defaultDepth",
1674
+ "signature": "defaultDepth?: number",
1675
+ "description": "Default depth for relationship population. Applied to every request unless overridden per-call."
1676
+ }
1677
+ ]
1678
+ },
1679
+ {
1680
+ "id": "@dyrected/sdk:DyrectedError",
1681
+ "name": "DyrectedError",
1682
+ "kind": "class",
1683
+ "category": "sdk",
1684
+ "sourcePackage": "@dyrected/sdk",
1685
+ "description": "Structured error thrown by the SDK when the server returns a non-2xx response.",
1686
+ "signature": "export class DyrectedError extends Error {\n}",
1687
+ "members": [
1688
+ {
1689
+ "name": "statusCode",
1690
+ "signature": "readonly statusCode: number",
1691
+ "description": ""
1692
+ },
1693
+ {
1694
+ "name": "errors",
1695
+ "signature": "readonly errors: { field?: string; message: string }[]",
1696
+ "description": ""
1697
+ }
1698
+ ]
1699
+ },
1700
+ {
1701
+ "id": "@dyrected/sdk:InferSchema",
1702
+ "name": "InferSchema",
1703
+ "kind": "type",
1704
+ "category": "sdk",
1705
+ "sourcePackage": "@dyrected/sdk",
1706
+ "description": "Derives a typed `TSchema` from your exported collection and global config constants.\n\nPass it to `createClient<Schema>()` so every `find`, `findOne`, `create`,\n`update`, `global().get()` call returns the inferred document shape \u2014 no\nmanual interfaces required.",
1707
+ "signature": "export type InferSchema<\n TCollections extends Record<string, CollectionConfig<any>>,\n TGlobals extends Record<string, GlobalConfig<any>> = Record<never, never>,\n> = {\n collections: { [K in keyof TCollections]: ExtractDoc<TCollections[K]> };\n globals: { [K in keyof TGlobals]: ExtractDoc<TGlobals[K]> };\n};",
1708
+ "members": []
1709
+ },
1710
+ {
1711
+ "id": "@dyrected/sdk:TransitionOptions",
1712
+ "name": "TransitionOptions",
1713
+ "kind": "interface",
1714
+ "category": "sdk",
1715
+ "sourcePackage": "@dyrected/sdk",
1716
+ "description": "Options accepted by `client.transition()`.",
1717
+ "signature": "export interface TransitionOptions {\n /**\n * The revision number currently shown to the user. When provided, the server\n * rejects the transition if the document has changed since it was loaded,\n * preventing lost-update races.\n */\n expectedRevision?: number;\n /** Required for transitions that have `requireComment: true` (e.g. `reject`). */\n comment?: string;\n}",
1718
+ "members": [
1719
+ {
1720
+ "name": "expectedRevision",
1721
+ "signature": "expectedRevision?: number",
1722
+ "description": "The revision number currently shown to the user. When provided, the server\nrejects the transition if the document has changed since it was loaded,\npreventing lost-update races."
1723
+ },
1724
+ {
1725
+ "name": "comment",
1726
+ "signature": "comment?: string",
1727
+ "description": "Required for transitions that have `requireComment: true` (e.g. `reject`)."
1728
+ }
1729
+ ]
1730
+ },
1731
+ {
1732
+ "id": "@dyrected/sdk:WorkflowDocument",
1733
+ "name": "WorkflowDocument",
1734
+ "kind": "interface",
1735
+ "category": "sdk",
1736
+ "sourcePackage": "@dyrected/sdk",
1737
+ "description": "Shape of a document returned from a workflow-enabled collection.",
1738
+ "signature": "export interface WorkflowDocument {\n id: string;\n _workflow: WorkflowMetadata;\n [key: string]: unknown;\n}",
1739
+ "members": [
1740
+ {
1741
+ "name": "id",
1742
+ "signature": "id: string",
1743
+ "description": ""
1744
+ },
1745
+ {
1746
+ "name": "_workflow",
1747
+ "signature": "_workflow: WorkflowMetadata",
1748
+ "description": ""
1749
+ }
1750
+ ]
1751
+ },
1752
+ {
1753
+ "id": "@dyrected/sdk:WorkflowHistoryEntry",
1754
+ "name": "WorkflowHistoryEntry",
1755
+ "kind": "interface",
1756
+ "category": "sdk",
1757
+ "sourcePackage": "@dyrected/sdk",
1758
+ "description": "A single workflow history entry returned by `client.workflowHistory()`.",
1759
+ "signature": "export interface WorkflowHistoryEntry {\n id: string;\n collection: string;\n documentId: string;\n transition: string;\n from: string;\n to: string;\n revision: number;\n comment: string | null;\n actorId: string | null;\n createdAt: string;\n}",
1760
+ "members": [
1761
+ {
1762
+ "name": "id",
1763
+ "signature": "id: string",
1764
+ "description": ""
1765
+ },
1766
+ {
1767
+ "name": "collection",
1768
+ "signature": "collection: string",
1769
+ "description": ""
1770
+ },
1771
+ {
1772
+ "name": "documentId",
1773
+ "signature": "documentId: string",
1774
+ "description": ""
1775
+ },
1776
+ {
1777
+ "name": "transition",
1778
+ "signature": "transition: string",
1779
+ "description": ""
1780
+ },
1781
+ {
1782
+ "name": "from",
1783
+ "signature": "from: string",
1784
+ "description": ""
1785
+ },
1786
+ {
1787
+ "name": "to",
1788
+ "signature": "to: string",
1789
+ "description": ""
1790
+ },
1791
+ {
1792
+ "name": "revision",
1793
+ "signature": "revision: number",
1794
+ "description": ""
1795
+ },
1796
+ {
1797
+ "name": "comment",
1798
+ "signature": "comment: string | null",
1799
+ "description": ""
1800
+ },
1801
+ {
1802
+ "name": "actorId",
1803
+ "signature": "actorId: string | null",
1804
+ "description": ""
1805
+ },
1806
+ {
1807
+ "name": "createdAt",
1808
+ "signature": "createdAt: string",
1809
+ "description": ""
1810
+ }
1811
+ ]
1812
+ }
1813
+ ];
1814
+ var endpoints = [
1815
+ {
1816
+ "id": "GET /api/collections/media",
1817
+ "method": "GET",
1818
+ "path": "/api/collections/media",
1819
+ "summary": "Find Media",
1820
+ "tags": [
1821
+ "Collections"
1822
+ ],
1823
+ "authenticated": true,
1824
+ "parameters": [
1825
+ {
1826
+ "name": "limit",
1827
+ "in": "query",
1828
+ "required": false
1829
+ },
1830
+ {
1831
+ "name": "page",
1832
+ "in": "query",
1833
+ "required": false
1834
+ },
1835
+ {
1836
+ "name": "where",
1837
+ "in": "query",
1838
+ "required": false,
1839
+ "description": "JSON filter"
1840
+ },
1841
+ {
1842
+ "name": "sort",
1843
+ "in": "query",
1844
+ "required": false,
1845
+ "description": "Sort field (e.g. -createdAt)"
1846
+ }
1847
+ ],
1848
+ "responses": [
1849
+ "200"
1850
+ ]
1851
+ },
1852
+ {
1853
+ "id": "POST /api/collections/media",
1854
+ "method": "POST",
1855
+ "path": "/api/collections/media",
1856
+ "summary": "Create Media item",
1857
+ "tags": [
1858
+ "Collections"
1859
+ ],
1860
+ "authenticated": true,
1861
+ "parameters": [],
1862
+ "responses": [
1863
+ "201"
1864
+ ]
1865
+ },
1866
+ {
1867
+ "id": "DELETE /api/collections/media/{id}",
1868
+ "method": "DELETE",
1869
+ "path": "/api/collections/media/{id}",
1870
+ "summary": "Delete Media item",
1871
+ "tags": [
1872
+ "Collections"
1873
+ ],
1874
+ "authenticated": true,
1875
+ "parameters": [
1876
+ {
1877
+ "name": "id",
1878
+ "in": "path",
1879
+ "required": true
1880
+ }
1881
+ ],
1882
+ "responses": [
1883
+ "204"
1884
+ ]
1885
+ },
1886
+ {
1887
+ "id": "GET /api/collections/media/{id}",
1888
+ "method": "GET",
1889
+ "path": "/api/collections/media/{id}",
1890
+ "summary": "Get a single Media item",
1891
+ "tags": [
1892
+ "Collections"
1893
+ ],
1894
+ "authenticated": true,
1895
+ "parameters": [
1896
+ {
1897
+ "name": "id",
1898
+ "in": "path",
1899
+ "required": true
1900
+ }
1901
+ ],
1902
+ "responses": [
1903
+ "200"
1904
+ ]
1905
+ },
1906
+ {
1907
+ "id": "PATCH /api/collections/media/{id}",
1908
+ "method": "PATCH",
1909
+ "path": "/api/collections/media/{id}",
1910
+ "summary": "Update Media item",
1911
+ "tags": [
1912
+ "Collections"
1913
+ ],
1914
+ "authenticated": true,
1915
+ "parameters": [
1916
+ {
1917
+ "name": "id",
1918
+ "in": "path",
1919
+ "required": true
1920
+ }
1921
+ ],
1922
+ "responses": [
1923
+ "200"
1924
+ ]
1925
+ },
1926
+ {
1927
+ "id": "DELETE /api/collections/media/delete-many",
1928
+ "method": "DELETE",
1929
+ "path": "/api/collections/media/delete-many",
1930
+ "summary": "Delete multiple Media",
1931
+ "tags": [
1932
+ "Collections"
1933
+ ],
1934
+ "authenticated": true,
1935
+ "parameters": [],
1936
+ "responses": [
1937
+ "200"
1938
+ ]
1939
+ },
1940
+ {
1941
+ "id": "GET /api/collections/media/media",
1942
+ "method": "GET",
1943
+ "path": "/api/collections/media/media",
1944
+ "summary": "List Media",
1945
+ "tags": [
1946
+ "Media"
1947
+ ],
1948
+ "authenticated": true,
1949
+ "parameters": [],
1950
+ "responses": [
1951
+ "200"
1952
+ ]
1953
+ },
1954
+ {
1955
+ "id": "POST /api/collections/media/media",
1956
+ "method": "POST",
1957
+ "path": "/api/collections/media/media",
1958
+ "summary": "Upload Media item",
1959
+ "tags": [
1960
+ "Media"
1961
+ ],
1962
+ "authenticated": true,
1963
+ "parameters": [],
1964
+ "responses": [
1965
+ "201"
1966
+ ]
1967
+ },
1968
+ {
1969
+ "id": "GET /api/collections/media/media/{filename}",
1970
+ "method": "GET",
1971
+ "path": "/api/collections/media/media/{filename}",
1972
+ "summary": "Serve Media item bytes",
1973
+ "tags": [
1974
+ "Media"
1975
+ ],
1976
+ "authenticated": false,
1977
+ "parameters": [
1978
+ {
1979
+ "name": "filename",
1980
+ "in": "path",
1981
+ "required": true
1982
+ }
1983
+ ],
1984
+ "responses": [
1985
+ "200",
1986
+ "404"
1987
+ ]
1988
+ },
1989
+ {
1990
+ "id": "POST /api/collections/media/seed",
1991
+ "method": "POST",
1992
+ "path": "/api/collections/media/seed",
1993
+ "summary": "Seed initial Media",
1994
+ "tags": [
1995
+ "Collections"
1996
+ ],
1997
+ "authenticated": true,
1998
+ "parameters": [],
1999
+ "responses": [
2000
+ "200"
2001
+ ]
2002
+ },
2003
+ {
2004
+ "id": "GET /api/collections/posts",
2005
+ "method": "GET",
2006
+ "path": "/api/collections/posts",
2007
+ "summary": "Find Posts",
2008
+ "tags": [
2009
+ "Collections"
2010
+ ],
2011
+ "authenticated": true,
2012
+ "parameters": [
2013
+ {
2014
+ "name": "limit",
2015
+ "in": "query",
2016
+ "required": false
2017
+ },
2018
+ {
2019
+ "name": "page",
2020
+ "in": "query",
2021
+ "required": false
2022
+ },
2023
+ {
2024
+ "name": "where",
2025
+ "in": "query",
2026
+ "required": false,
2027
+ "description": "JSON filter"
2028
+ },
2029
+ {
2030
+ "name": "sort",
2031
+ "in": "query",
2032
+ "required": false,
2033
+ "description": "Sort field (e.g. -createdAt)"
2034
+ }
2035
+ ],
2036
+ "responses": [
2037
+ "200"
2038
+ ]
2039
+ },
2040
+ {
2041
+ "id": "POST /api/collections/posts",
2042
+ "method": "POST",
2043
+ "path": "/api/collections/posts",
2044
+ "summary": "Create Post",
2045
+ "tags": [
2046
+ "Collections"
2047
+ ],
2048
+ "authenticated": true,
2049
+ "parameters": [],
2050
+ "responses": [
2051
+ "201"
2052
+ ]
2053
+ },
2054
+ {
2055
+ "id": "DELETE /api/collections/posts/{id}",
2056
+ "method": "DELETE",
2057
+ "path": "/api/collections/posts/{id}",
2058
+ "summary": "Delete Post",
2059
+ "tags": [
2060
+ "Collections"
2061
+ ],
2062
+ "authenticated": true,
2063
+ "parameters": [
2064
+ {
2065
+ "name": "id",
2066
+ "in": "path",
2067
+ "required": true
2068
+ }
2069
+ ],
2070
+ "responses": [
2071
+ "204"
2072
+ ]
2073
+ },
2074
+ {
2075
+ "id": "GET /api/collections/posts/{id}",
2076
+ "method": "GET",
2077
+ "path": "/api/collections/posts/{id}",
2078
+ "summary": "Get a single Post",
2079
+ "tags": [
2080
+ "Collections"
2081
+ ],
2082
+ "authenticated": true,
2083
+ "parameters": [
2084
+ {
2085
+ "name": "id",
2086
+ "in": "path",
2087
+ "required": true
2088
+ }
2089
+ ],
2090
+ "responses": [
2091
+ "200"
2092
+ ]
2093
+ },
2094
+ {
2095
+ "id": "PATCH /api/collections/posts/{id}",
2096
+ "method": "PATCH",
2097
+ "path": "/api/collections/posts/{id}",
2098
+ "summary": "Update Post",
2099
+ "tags": [
2100
+ "Collections"
2101
+ ],
2102
+ "authenticated": true,
2103
+ "parameters": [
2104
+ {
2105
+ "name": "id",
2106
+ "in": "path",
2107
+ "required": true
2108
+ }
2109
+ ],
2110
+ "responses": [
2111
+ "200"
2112
+ ]
2113
+ },
2114
+ {
2115
+ "id": "POST /api/collections/posts/{id}/transitions/{transition}",
2116
+ "method": "POST",
2117
+ "path": "/api/collections/posts/{id}/transitions/{transition}",
2118
+ "summary": "Transition Post workflow",
2119
+ "tags": [
2120
+ "Workflows"
2121
+ ],
2122
+ "authenticated": true,
2123
+ "parameters": [
2124
+ {
2125
+ "name": "id",
2126
+ "in": "path",
2127
+ "required": true
2128
+ },
2129
+ {
2130
+ "name": "transition",
2131
+ "in": "path",
2132
+ "required": true
2133
+ }
2134
+ ],
2135
+ "responses": [
2136
+ "200",
2137
+ "400",
2138
+ "403",
2139
+ "409"
2140
+ ]
2141
+ },
2142
+ {
2143
+ "id": "GET /api/collections/posts/{id}/workflow-history",
2144
+ "method": "GET",
2145
+ "path": "/api/collections/posts/{id}/workflow-history",
2146
+ "summary": "Get Post workflow history",
2147
+ "tags": [
2148
+ "Workflows"
2149
+ ],
2150
+ "authenticated": true,
2151
+ "parameters": [
2152
+ {
2153
+ "name": "id",
2154
+ "in": "path",
2155
+ "required": true
2156
+ },
2157
+ {
2158
+ "name": "limit",
2159
+ "in": "query",
2160
+ "required": false
2161
+ }
2162
+ ],
2163
+ "responses": [
2164
+ "200",
2165
+ "403"
2166
+ ]
2167
+ },
2168
+ {
2169
+ "id": "DELETE /api/collections/posts/delete-many",
2170
+ "method": "DELETE",
2171
+ "path": "/api/collections/posts/delete-many",
2172
+ "summary": "Delete multiple Posts",
2173
+ "tags": [
2174
+ "Collections"
2175
+ ],
2176
+ "authenticated": true,
2177
+ "parameters": [],
2178
+ "responses": [
2179
+ "200"
2180
+ ]
2181
+ },
2182
+ {
2183
+ "id": "POST /api/collections/posts/seed",
2184
+ "method": "POST",
2185
+ "path": "/api/collections/posts/seed",
2186
+ "summary": "Seed initial Posts",
2187
+ "tags": [
2188
+ "Collections"
2189
+ ],
2190
+ "authenticated": true,
2191
+ "parameters": [],
2192
+ "responses": [
2193
+ "200"
2194
+ ]
2195
+ },
2196
+ {
2197
+ "id": "GET /api/collections/users",
2198
+ "method": "GET",
2199
+ "path": "/api/collections/users",
2200
+ "summary": "Find Users",
2201
+ "tags": [
2202
+ "Collections"
2203
+ ],
2204
+ "authenticated": true,
2205
+ "parameters": [
2206
+ {
2207
+ "name": "limit",
2208
+ "in": "query",
2209
+ "required": false
2210
+ },
2211
+ {
2212
+ "name": "page",
2213
+ "in": "query",
2214
+ "required": false
2215
+ },
2216
+ {
2217
+ "name": "where",
2218
+ "in": "query",
2219
+ "required": false,
2220
+ "description": "JSON filter"
2221
+ },
2222
+ {
2223
+ "name": "sort",
2224
+ "in": "query",
2225
+ "required": false,
2226
+ "description": "Sort field (e.g. -createdAt)"
2227
+ }
2228
+ ],
2229
+ "responses": [
2230
+ "200"
2231
+ ]
2232
+ },
2233
+ {
2234
+ "id": "POST /api/collections/users",
2235
+ "method": "POST",
2236
+ "path": "/api/collections/users",
2237
+ "summary": "Create User",
2238
+ "tags": [
2239
+ "Collections"
2240
+ ],
2241
+ "authenticated": true,
2242
+ "parameters": [],
2243
+ "responses": [
2244
+ "201"
2245
+ ]
2246
+ },
2247
+ {
2248
+ "id": "DELETE /api/collections/users/{id}",
2249
+ "method": "DELETE",
2250
+ "path": "/api/collections/users/{id}",
2251
+ "summary": "Delete User",
2252
+ "tags": [
2253
+ "Collections"
2254
+ ],
2255
+ "authenticated": true,
2256
+ "parameters": [
2257
+ {
2258
+ "name": "id",
2259
+ "in": "path",
2260
+ "required": true
2261
+ }
2262
+ ],
2263
+ "responses": [
2264
+ "204"
2265
+ ]
2266
+ },
2267
+ {
2268
+ "id": "GET /api/collections/users/{id}",
2269
+ "method": "GET",
2270
+ "path": "/api/collections/users/{id}",
2271
+ "summary": "Get a single User",
2272
+ "tags": [
2273
+ "Collections"
2274
+ ],
2275
+ "authenticated": true,
2276
+ "parameters": [
2277
+ {
2278
+ "name": "id",
2279
+ "in": "path",
2280
+ "required": true
2281
+ }
2282
+ ],
2283
+ "responses": [
2284
+ "200"
2285
+ ]
2286
+ },
2287
+ {
2288
+ "id": "PATCH /api/collections/users/{id}",
2289
+ "method": "PATCH",
2290
+ "path": "/api/collections/users/{id}",
2291
+ "summary": "Update User",
2292
+ "tags": [
2293
+ "Collections"
2294
+ ],
2295
+ "authenticated": true,
2296
+ "parameters": [
2297
+ {
2298
+ "name": "id",
2299
+ "in": "path",
2300
+ "required": true
2301
+ }
2302
+ ],
2303
+ "responses": [
2304
+ "200"
2305
+ ]
2306
+ },
2307
+ {
2308
+ "id": "POST /api/collections/users/{id}/change-password",
2309
+ "method": "POST",
2310
+ "path": "/api/collections/users/{id}/change-password",
2311
+ "summary": "Change a User password",
2312
+ "tags": [
2313
+ "Authentication"
2314
+ ],
2315
+ "authenticated": true,
2316
+ "parameters": [
2317
+ {
2318
+ "name": "id",
2319
+ "in": "path",
2320
+ "required": true
2321
+ }
2322
+ ],
2323
+ "responses": [
2324
+ "200"
2325
+ ]
2326
+ },
2327
+ {
2328
+ "id": "POST /api/collections/users/accept-invite",
2329
+ "method": "POST",
2330
+ "path": "/api/collections/users/accept-invite",
2331
+ "summary": "Accept an invitation",
2332
+ "tags": [
2333
+ "Authentication"
2334
+ ],
2335
+ "authenticated": false,
2336
+ "parameters": [],
2337
+ "responses": [
2338
+ "200",
2339
+ "400"
2340
+ ]
2341
+ },
2342
+ {
2343
+ "id": "DELETE /api/collections/users/delete-many",
2344
+ "method": "DELETE",
2345
+ "path": "/api/collections/users/delete-many",
2346
+ "summary": "Delete multiple Users",
2347
+ "tags": [
2348
+ "Collections"
2349
+ ],
2350
+ "authenticated": true,
2351
+ "parameters": [],
2352
+ "responses": [
2353
+ "200"
2354
+ ]
2355
+ },
2356
+ {
2357
+ "id": "POST /api/collections/users/first-user",
2358
+ "method": "POST",
2359
+ "path": "/api/collections/users/first-user",
2360
+ "summary": "Register the first User",
2361
+ "tags": [
2362
+ "Authentication"
2363
+ ],
2364
+ "authenticated": false,
2365
+ "parameters": [],
2366
+ "responses": [
2367
+ "200",
2368
+ "400"
2369
+ ]
2370
+ },
2371
+ {
2372
+ "id": "POST /api/collections/users/forgot-password",
2373
+ "method": "POST",
2374
+ "path": "/api/collections/users/forgot-password",
2375
+ "summary": "Request a password reset",
2376
+ "tags": [
2377
+ "Authentication"
2378
+ ],
2379
+ "authenticated": false,
2380
+ "parameters": [],
2381
+ "responses": [
2382
+ "200",
2383
+ "400"
2384
+ ]
2385
+ },
2386
+ {
2387
+ "id": "GET /api/collections/users/init",
2388
+ "method": "GET",
2389
+ "path": "/api/collections/users/init",
2390
+ "summary": "Get Users initialization state",
2391
+ "tags": [
2392
+ "Authentication"
2393
+ ],
2394
+ "authenticated": false,
2395
+ "parameters": [],
2396
+ "responses": [
2397
+ "200"
2398
+ ]
2399
+ },
2400
+ {
2401
+ "id": "POST /api/collections/users/invite",
2402
+ "method": "POST",
2403
+ "path": "/api/collections/users/invite",
2404
+ "summary": "Invite a User",
2405
+ "tags": [
2406
+ "Authentication"
2407
+ ],
2408
+ "authenticated": true,
2409
+ "parameters": [],
2410
+ "responses": [
2411
+ "200"
2412
+ ]
2413
+ },
2414
+ {
2415
+ "id": "POST /api/collections/users/login",
2416
+ "method": "POST",
2417
+ "path": "/api/collections/users/login",
2418
+ "summary": "Log in to Users",
2419
+ "tags": [
2420
+ "Authentication"
2421
+ ],
2422
+ "authenticated": false,
2423
+ "parameters": [],
2424
+ "responses": [
2425
+ "200",
2426
+ "401"
2427
+ ]
2428
+ },
2429
+ {
2430
+ "id": "POST /api/collections/users/logout",
2431
+ "method": "POST",
2432
+ "path": "/api/collections/users/logout",
2433
+ "summary": "Log out of Users",
2434
+ "tags": [
2435
+ "Authentication"
2436
+ ],
2437
+ "authenticated": true,
2438
+ "parameters": [],
2439
+ "responses": [
2440
+ "200"
2441
+ ]
2442
+ },
2443
+ {
2444
+ "id": "GET /api/collections/users/me",
2445
+ "method": "GET",
2446
+ "path": "/api/collections/users/me",
2447
+ "summary": "Get the current User",
2448
+ "tags": [
2449
+ "Authentication"
2450
+ ],
2451
+ "authenticated": true,
2452
+ "parameters": [],
2453
+ "responses": [
2454
+ "200"
2455
+ ]
2456
+ },
2457
+ {
2458
+ "id": "POST /api/collections/users/refresh-token",
2459
+ "method": "POST",
2460
+ "path": "/api/collections/users/refresh-token",
2461
+ "summary": "Refresh an authentication token",
2462
+ "tags": [
2463
+ "Authentication"
2464
+ ],
2465
+ "authenticated": true,
2466
+ "parameters": [],
2467
+ "responses": [
2468
+ "200"
2469
+ ]
2470
+ },
2471
+ {
2472
+ "id": "POST /api/collections/users/reset-password",
2473
+ "method": "POST",
2474
+ "path": "/api/collections/users/reset-password",
2475
+ "summary": "Reset a password",
2476
+ "tags": [
2477
+ "Authentication"
2478
+ ],
2479
+ "authenticated": false,
2480
+ "parameters": [],
2481
+ "responses": [
2482
+ "200",
2483
+ "400"
2484
+ ]
2485
+ },
2486
+ {
2487
+ "id": "POST /api/collections/users/seed",
2488
+ "method": "POST",
2489
+ "path": "/api/collections/users/seed",
2490
+ "summary": "Seed initial Users",
2491
+ "tags": [
2492
+ "Collections"
2493
+ ],
2494
+ "authenticated": true,
2495
+ "parameters": [],
2496
+ "responses": [
2497
+ "200"
2498
+ ]
2499
+ },
2500
+ {
2501
+ "id": "GET /api/docs",
2502
+ "method": "GET",
2503
+ "path": "/api/docs",
2504
+ "summary": "Open interactive API documentation",
2505
+ "tags": [
2506
+ "System"
2507
+ ],
2508
+ "authenticated": false,
2509
+ "parameters": [],
2510
+ "responses": [
2511
+ "200"
2512
+ ]
2513
+ },
2514
+ {
2515
+ "id": "GET /api/dyrected/options/{collection}/{field}",
2516
+ "method": "GET",
2517
+ "path": "/api/dyrected/options/{collection}/{field}",
2518
+ "summary": "Resolve dynamic field options",
2519
+ "tags": [
2520
+ "System"
2521
+ ],
2522
+ "authenticated": true,
2523
+ "parameters": [
2524
+ {
2525
+ "name": "collection",
2526
+ "in": "path",
2527
+ "required": true
2528
+ },
2529
+ {
2530
+ "name": "field",
2531
+ "in": "path",
2532
+ "required": true
2533
+ }
2534
+ ],
2535
+ "responses": [
2536
+ "200"
2537
+ ]
2538
+ },
2539
+ {
2540
+ "id": "GET /api/globals/settings",
2541
+ "method": "GET",
2542
+ "path": "/api/globals/settings",
2543
+ "summary": "Get Site settings",
2544
+ "tags": [
2545
+ "Globals"
2546
+ ],
2547
+ "authenticated": true,
2548
+ "parameters": [],
2549
+ "responses": [
2550
+ "200"
2551
+ ]
2552
+ },
2553
+ {
2554
+ "id": "PATCH /api/globals/settings",
2555
+ "method": "PATCH",
2556
+ "path": "/api/globals/settings",
2557
+ "summary": "Update Site settings",
2558
+ "tags": [
2559
+ "Globals"
2560
+ ],
2561
+ "authenticated": true,
2562
+ "parameters": [],
2563
+ "responses": [
2564
+ "200"
2565
+ ]
2566
+ },
2567
+ {
2568
+ "id": "POST /api/globals/settings/seed",
2569
+ "method": "POST",
2570
+ "path": "/api/globals/settings/seed",
2571
+ "summary": "Seed Site settings",
2572
+ "tags": [
2573
+ "Globals"
2574
+ ],
2575
+ "authenticated": true,
2576
+ "parameters": [],
2577
+ "responses": [
2578
+ "200"
2579
+ ]
2580
+ },
2581
+ {
2582
+ "id": "GET /api/media/{filename}",
2583
+ "method": "GET",
2584
+ "path": "/api/media/{filename}",
2585
+ "summary": "Serve a stored file",
2586
+ "tags": [
2587
+ "Media"
2588
+ ],
2589
+ "authenticated": false,
2590
+ "parameters": [
2591
+ {
2592
+ "name": "filename",
2593
+ "in": "path",
2594
+ "required": true
2595
+ }
2596
+ ],
2597
+ "responses": [
2598
+ "200",
2599
+ "404"
2600
+ ]
2601
+ },
2602
+ {
2603
+ "id": "GET /api/openapi.json",
2604
+ "method": "GET",
2605
+ "path": "/api/openapi.json",
2606
+ "summary": "Get the OpenAPI specification",
2607
+ "tags": [
2608
+ "System"
2609
+ ],
2610
+ "authenticated": false,
2611
+ "parameters": [],
2612
+ "responses": [
2613
+ "200"
2614
+ ]
2615
+ },
2616
+ {
2617
+ "id": "GET /api/preferences/{key}",
2618
+ "method": "GET",
2619
+ "path": "/api/preferences/{key}",
2620
+ "summary": "Get an authenticated user preference",
2621
+ "tags": [
2622
+ "Preferences"
2623
+ ],
2624
+ "authenticated": true,
2625
+ "parameters": [
2626
+ {
2627
+ "name": "key",
2628
+ "in": "path",
2629
+ "required": true
2630
+ }
2631
+ ],
2632
+ "responses": [
2633
+ "200"
2634
+ ]
2635
+ },
2636
+ {
2637
+ "id": "PUT /api/preferences/{key}",
2638
+ "method": "PUT",
2639
+ "path": "/api/preferences/{key}",
2640
+ "summary": "Set an authenticated user preference",
2641
+ "tags": [
2642
+ "Preferences"
2643
+ ],
2644
+ "authenticated": true,
2645
+ "parameters": [
2646
+ {
2647
+ "name": "key",
2648
+ "in": "path",
2649
+ "required": true
2650
+ }
2651
+ ],
2652
+ "responses": [
2653
+ "200"
2654
+ ]
2655
+ },
2656
+ {
2657
+ "id": "GET /api/preview-data",
2658
+ "method": "GET",
2659
+ "path": "/api/preview-data",
2660
+ "summary": "Resolve preview data from a token",
2661
+ "tags": [
2662
+ "Preview"
2663
+ ],
2664
+ "authenticated": false,
2665
+ "parameters": [
2666
+ {
2667
+ "name": "token",
2668
+ "in": "query",
2669
+ "required": true
2670
+ }
2671
+ ],
2672
+ "responses": [
2673
+ "200"
2674
+ ]
2675
+ },
2676
+ {
2677
+ "id": "POST /api/preview-token",
2678
+ "method": "POST",
2679
+ "path": "/api/preview-token",
2680
+ "summary": "Create a preview token",
2681
+ "tags": [
2682
+ "Preview"
2683
+ ],
2684
+ "authenticated": true,
2685
+ "parameters": [],
2686
+ "responses": [
2687
+ "200"
2688
+ ]
2689
+ },
2690
+ {
2691
+ "id": "GET /api/schemas",
2692
+ "method": "GET",
2693
+ "path": "/api/schemas",
2694
+ "summary": "Get the serialized Dyrected schema",
2695
+ "tags": [
2696
+ "System"
2697
+ ],
2698
+ "authenticated": false,
2699
+ "parameters": [],
2700
+ "responses": [
2701
+ "200"
2702
+ ]
2703
+ }
2704
+ ];
2705
+
2706
+ // src/search.ts
2707
+ var ignoredWords = /* @__PURE__ */ new Set([
2708
+ "a",
2709
+ "an",
2710
+ "and",
2711
+ "for",
2712
+ "from",
2713
+ "in",
2714
+ "of",
2715
+ "the",
2716
+ "to",
2717
+ "with"
2718
+ ]);
2719
+ function tokens(value) {
2720
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(" ").filter((token) => token.length > 1 && !ignoredWords.has(token));
2721
+ }
2722
+ function findRecipesByIntent(query, catalogue, options = {}) {
2723
+ const queryTokens = new Set(tokens(query));
2724
+ if (queryTokens.size === 0) return [];
2725
+ const minimumScore = options.minimumScore ?? 1;
2726
+ const matches = catalogue.map((recipe) => {
2727
+ const intentText = recipe.intents.join(" ").toLowerCase();
2728
+ const titleText = `${recipe.title} ${recipe.description}`.toLowerCase();
2729
+ const conceptText = recipe.concepts.join(" ").toLowerCase();
2730
+ let score = intentText.includes(query.toLowerCase()) ? 20 : 0;
2731
+ for (const token of queryTokens) {
2732
+ if (intentText.includes(token)) score += 5;
2733
+ if (titleText.includes(token)) score += 3;
2734
+ if (conceptText.includes(token)) score += 2;
2735
+ }
2736
+ return { recipe, score };
2737
+ }).filter((match) => match.score >= minimumScore).sort(
2738
+ (left, right) => right.score - left.score || left.recipe.title.localeCompare(right.recipe.title)
2739
+ );
2740
+ return matches.slice(0, options.limit ?? 5);
2741
+ }
2742
+
2743
+ // src/generated/ai.ts
2744
+ var AI_RULES = '# Dyrected AI Rules\n\nThis file combines authored operating rules with facts generated by `@dyrected/knowledge`.\n\n## Before writing code\n\n1. Check `package.json` and the workspace root to confirm `@dyrected/core` is installed.\n2. If it is not installed, use `npx @dyrected/cli init` and verify lint, types, and build before adding collections.\n3. If it is installed, inspect its installed version, public exports, and the existing `dyrected.config.ts`.\n4. When documentation and installed types disagree, follow the installed package.\n5. Detect the project\'s framework, database adapter, storage adapter, package manager, and deployment target before changing setup.\n\n## Hard constraints\n\n- Every named field must have an explicit human-readable `label`.\n- Never invent fields, hooks, configuration properties, SDK methods, adapter methods, or routes.\n- Never use `client.collections`; use `client.collection(\'slug\')`.\n- Never define `email` or `password` on an `auth: true` collection.\n- Never add custom authentication middleware around Dyrected Admin routes.\n- Never remove or directly rename a persisted field. Use `renameTo`, and give new fields on existing schemas a safe `defaultValue`.\n- Use server hooks for correctness. Browser Admin hooks are an optional feedback layer, not the only implementation.\n- Use Jexl strings for `admin.condition` when the schema must synchronize with Dyrected Cloud.\n- Prefer the smallest required access permissions and enforce them in server configuration.\n\n## Setup and verification\n\n- New project: initialize, configure environment variables, database, storage and Admin route, then run lint, type checking and build.\n- Existing project: read the UI and current content model before proposing collections or globals. Preserve existing content and routes.\n- Existing Dyrected project: read the config first and make schema changes in small, verifiable batches.\n- Self-hosted and Cloud projects can have different schema synchronization and serialization constraints. Confirm the target before advising commands.\n\n## Modeling and migration guidance\n\n- Use `defineGlobal` for one shared settings document and `defineCollection` for repeatable entries.\n- Use `relationship` for the stored owning reference and `join` for a virtual reverse lookup.\n- Use `depth: 0` on lightweight lists; increase depth only when the view needs hydrated relations.\n- Use field hooks for one value and collection hooks for cross-field validation or derived data.\n- Use `workflow: publishingWorkflow()` for capability-controlled draft, review, and publication.\n- Treat collection slugs, field names, block slugs, and public URL patterns as persisted contracts.\n\n### Rename a field safely\n\n```ts\n{\n name: "fullName",\n type: "text",\n label: "Full name",\n renameTo: "name",\n defaultValue: "",\n}\n```\n\nKeep `renameTo` until production documents have been migrated and verified.\n\n## Security boundaries\n\n- Admin visibility and conditions are presentation, not authorization or validation.\n- Set ownership and other trusted fields in server hooks; do not trust client-submitted owner or role values.\n- Keep API keys and storage credentials out of browser bundles.\n- Grant destructive operations and workflow capabilities independently.\n- Use `expectedRevision` when workflow transitions can race with another editor.\n\n## Uploads and dynamic options\n\nUse `allowedMimeTypes` and `maxFileSize` for upload collections, consume returned URLs, and validate untrusted file contents. Use server option resolvers for database/secret-backed choices and Admin option hooks only for browser-safe dependent choices.\n\n## Supported field types\n\n<!-- GENERATED:FIELD_TYPES:START -->\n`text`, `textarea`, `richText`, `number`, `boolean`, `date`, `datetime`, `time`, `select`, `multiSelect`, `radio`, `relationship`, `array`, `object`, `json`, `blocks`, `image`, `email`, `url`, `icon`, `join`, `row`\n<!-- GENERATED:FIELD_TYPES:END -->\n\n## Compiled recipes\n\n<!-- GENERATED:RECIPES:START -->\n- [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug) \u2014 Generate stable URL slugs on the server while showing editors the value live in the Admin UI.\n- [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field) \u2014 Use a serializable Admin condition to reveal a field from the editor\'s current form values.\n- [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation) \u2014 Reject invalid combinations of field values before they reach the database.\n- [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown) \u2014 Change available Admin UI options immediately when an editor changes a related field.\n- [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow) \u2014 Attach Dyrected\'s standard editorial workflow and its capability-aware transitions to a collection.\n- [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access) \u2014 Return a where constraint from access control so authenticated users only read their own records.\n- [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks) \u2014 Define labeled hero, rich-text, and call-to-action blocks for an editor-controlled page layout.\n- [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join) \u2014 Store an author relationship on posts and expose the author\'s posts through a virtual join field.\n- [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access) \u2014 Allow public reads, editor writes, and administrator deletion with collection access rules.\n- [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename) \u2014 Use renameTo and a safe default while documents migrate lazily to a new field name.\n- [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection) \u2014 Enable file uploads and capture accessible metadata in a dedicated media collection.\n<!-- GENERATED:RECIPES:END -->\n\n## Intent-to-pattern index\n\n<!-- GENERATED:INTENTS:START -->\n- \u201Cmake the URL follow the title\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Cautomatically generate a slug\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Ccreate friendly URLs from titles\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Ckeep a slug synchronized with a title\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Cshow a field conditionally\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Chide irrelevant form fields\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cshow discount only with a coupon\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cmake the admin form react to another field\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cvalidate fields before saving\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cmake sure an end date is after the start date\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Creject invalid form submissions\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cvalidate multiple fields together\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cmake one dropdown depend on another\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cshow states based on the selected country\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Ccreate a cascading dropdown\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cupdate select options while editing\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cadd draft and publish states\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Crequire review before publishing\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Ccreate an editorial workflow\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Clet editors submit content for approval\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Cusers should only see their own records\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cadd row level access\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cscope documents by owner\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cprevent users reading another user\'s data\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cbuild a page builder\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Clet editors arrange page sections\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Ccreate reusable content blocks\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Cmodel flexible landing pages\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Cconnect posts to authors\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Cshow every post written by a user\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Ccreate a reverse relationship\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Cmodel one-to-many content\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Conly editors can update content\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Crestrict deletion to admins\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Cmake content publicly readable\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Cadd role based access\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Crename a field safely\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Cchange a field name without losing data\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Cmigrate an existing schema\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Ckeep old documents working after a rename\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Clet editors upload images\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Ccreate a media library\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Cstore uploaded files\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Cadd image uploads to my project\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n<!-- GENERATED:INTENTS:END -->\n\n## Canonical references\n\n<!-- GENERATED:REFERENCES:START -->\n- [Configuration](https://docs.dyrected.com/docs/reference/configuration)\n- [Fields and hooks](https://docs.dyrected.com/docs/reference/fields)\n- [Database adapters](https://docs.dyrected.com/docs/adapters/databases)\n- [Storage adapters](https://docs.dyrected.com/docs/adapters/storage)\n- [SDK](https://docs.dyrected.com/docs/reference/sdk)\n- [Workflows](https://docs.dyrected.com/docs/reference/generated-workflows)\n- [REST and OpenAPI](https://docs.dyrected.com/docs/reference/rest-api)\n<!-- GENERATED:REFERENCES:END -->\n\n## Completion checks\n\nRun the repository\'s lint, type-check, tests and build. Confirm generated files are current, configuration fields remain labeled, schema migrations preserve stored data, and authentication/access behavior is enforced server-side.\n';
2745
+ var SKILL = '---\nname: dyrected\ndescription: Work correctly with Dyrected in new and existing projects using installation checks, schema safety rules, and compiled implementation recipes.\n---\n\n# Dyrected\n\nDyrected is a declarative, schema-driven headless CMS configured primarily through `dyrected.config.ts`.\n\n## Step 0 \u2014 determine the project state\n\nBefore changing code, inspect the nearest `package.json` and the workspace root.\n\n### Dyrected is not installed\n\nUse the CLI:\n\n```bash\nnpx @dyrected/cli init\n```\n\nDetect the framework, package manager, database requirements, storage requirements and deployment target. Let the CLI scaffold configuration, environment variables, Admin integration and AI rules. Verify lint, types and build before modeling content.\n\nFor an existing website, first inventory editable content and distinguish repeatable entries from singleton settings. Preserve the existing interface, content, URLs and behavior. Do not invent a new content model merely because it is convenient.\n\n### Dyrected is installed\n\nRead `dyrected.config.ts`, the installed `@dyrected/core` version and its public exports. Inspect the configured database, storage, email, collections, globals, authentication and workflows before proposing changes. Installed types take precedence over remembered APIs or newer documentation.\n\n## Operational rules\n\n1. Use public package imports such as `@dyrected/core` and `@dyrected/sdk`; do not reach into another workspace package\'s source directory.\n2. Every named field must define an explicit `label`.\n3. Never invent field types, hook names, configuration keys, adapter methods, SDK methods or REST routes.\n4. Use `client.collection(\'slug\')`, never `client.collections`.\n5. Do not wrap Dyrected Admin routes in custom auth/session middleware.\n6. Do not define `email` or `password` fields on `auth: true` collections; Dyrected injects them.\n7. Do not delete or directly rename persisted fields. Use `renameTo`; add a safe `defaultValue` when introducing fields to existing schemas.\n8. Use server hooks for data correctness. Admin hooks improve live editor feedback but API writes bypass them.\n9. Use serializable Jexl conditions for Cloud-compatible schemas.\n10. Enforce permissions in server access configuration, not only by hiding Admin controls.\n\n## Core imports\n\nImport public APIs from package entry points:\n\n```ts\nimport { defineCollection, defineConfig, defineGlobal } from "@dyrected/core";\nimport { createClient, type InferSchema } from "@dyrected/sdk";\n```\n\nNever import from a monorepo source path such as `packages/core/src`. Verify the installed package exports when documentation and local types disagree.\n\n## Schema and deployment safety\n\n- Read the existing schema before editing it.\n- Make related changes in small batches and verify each batch.\n- Confirm whether the project is Cloud or self-hosted before giving schema synchronization instructions.\n- MongoDB is schema-less; relational adapters may require synchronization for promoted fields.\n- Use `relationship` for the owning stored reference and `join` for a virtual reverse lookup.\n- Use `depth: 0` for lightweight lists and increase depth only when related documents are required.\n- Use hooks for derived values, validation, side effects and revalidation.\n- Use `workflow: publishingWorkflow()` when the requirement is draft, review and publication rather than inventing status logic.\n- Use `defineGlobal` for singleton settings and `defineCollection` for repeatable entries.\n\n### Rename a field safely\n\nThe current `name` is the new key and `renameTo` is the previous stored key:\n\n```ts\n{\n name: "fullName",\n type: "text",\n label: "Full name",\n renameTo: "name",\n defaultValue: "",\n}\n```\n\nKeep the fallback until production documents are migrated and verified. For relational adapters, test promoted or unique changes in staging before synchronization.\n\n### Zero-state behavior\n\nUse `initialData` only when deliberate seed/fallback behavior is desired:\n\n```ts\nconst { docs } = await client.collection("posts").find({ initialData: [] });\nconst settings = await client\n .global("site-settings")\n .get({ initialData: { siteName: "My site" } });\n```\n\nDo not convert authentication, validation, or network failures into empty successful states.\n\n## Relationships and depth\n\n`relationship` is the stored owning reference. `join` is a virtual reverse lookup.\n\n```ts\n{ name: "author", type: "relationship", label: "Author", relationTo: "users" }\n{\n name: "posts",\n type: "join",\n label: "Posts",\n collection: "posts",\n on: "author",\n limit: 20,\n}\n```\n\nUse `depth: 0` for lightweight lists and increase depth only when related values are needed. Bound joins and account for their query cost.\n\n## Auth and access\n\n`auth: true` injects authentication fields and endpoints. Do not redefine `email` or `password`. Treat roles as trusted only when clients cannot assign them to themselves.\n\n```ts\nexport const Users = defineCollection({\n slug: "users",\n auth: true,\n fields: [\n { name: "name", type: "text", label: "Name" },\n {\n name: "role",\n type: "select",\n label: "Role",\n options: ["member", "editor", "admin"],\n },\n ],\n});\n```\n\nGrant read, create, update, delete, and workflow capabilities independently. UI visibility is not authorization.\n\n## Uploads\n\n```ts\nexport const Media = defineCollection({\n slug: "media",\n upload: {\n allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],\n maxFileSize: 5_000_000,\n },\n fields: [\n { name: "alt", type: "text", label: "Alternative text", required: true },\n ],\n});\n```\n\nConsume the returned URL, keep provider credentials server-side, and validate untrusted file contents in addition to MIME metadata.\n\n## Dynamic and conditional fields\n\n- Static `options`: fixed choices.\n- Server `options` resolver: database access, secrets, user filtering, or caching.\n- `admin.hooks.options`: instant browser-only dependent choices.\n- `admin.condition`: presentation only; use Jexl strings for Cloud synchronization.\n\nServer validation is still required when a dependent choice or condition is part of the data contract.\n\n## Custom Admin components\n\nReference custom inputs and slots with registered string keys in serializable configuration. Register the actual framework component in the Admin integration. Keep validation and access in the server field definition.\n\n## Supported field types\n\n<!-- GENERATED:FIELD_TYPES:START -->\n`text`, `textarea`, `richText`, `number`, `boolean`, `date`, `datetime`, `time`, `select`, `multiSelect`, `radio`, `relationship`, `array`, `object`, `json`, `blocks`, `image`, `email`, `url`, `icon`, `join`, `row`\n<!-- GENERATED:FIELD_TYPES:END -->\n\n## Compiled recipes\n\nThese recipes are compiled and behavior-tested. Select them from the user\'s desired outcome; do not require the user to know Dyrected terminology.\n\n<!-- GENERATED:RECIPES:START -->\n- [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug) \u2014 Generate stable URL slugs on the server while showing editors the value live in the Admin UI.\n- [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field) \u2014 Use a serializable Admin condition to reveal a field from the editor\'s current form values.\n- [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation) \u2014 Reject invalid combinations of field values before they reach the database.\n- [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown) \u2014 Change available Admin UI options immediately when an editor changes a related field.\n- [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow) \u2014 Attach Dyrected\'s standard editorial workflow and its capability-aware transitions to a collection.\n- [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access) \u2014 Return a where constraint from access control so authenticated users only read their own records.\n- [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks) \u2014 Define labeled hero, rich-text, and call-to-action blocks for an editor-controlled page layout.\n- [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join) \u2014 Store an author relationship on posts and expose the author\'s posts through a virtual join field.\n- [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access) \u2014 Allow public reads, editor writes, and administrator deletion with collection access rules.\n- [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename) \u2014 Use renameTo and a safe default while documents migrate lazily to a new field name.\n- [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection) \u2014 Enable file uploads and capture accessible metadata in a dedicated media collection.\n<!-- GENERATED:RECIPES:END -->\n\n## Intent-to-pattern index\n\n<!-- GENERATED:INTENTS:START -->\n- \u201Cmake the URL follow the title\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Cautomatically generate a slug\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Ccreate friendly URLs from titles\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Ckeep a slug synchronized with a title\u201D \u2192 [Generate a slug from a title](https://docs.dyrected.com/docs/recipes/auto-slug)\n- \u201Cshow a field conditionally\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Chide irrelevant form fields\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cshow discount only with a coupon\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cmake the admin form react to another field\u201D \u2192 [Show an Admin field only when it is relevant](https://docs.dyrected.com/docs/recipes/conditional-admin-field)\n- \u201Cvalidate fields before saving\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cmake sure an end date is after the start date\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Creject invalid form submissions\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cvalidate multiple fields together\u201D \u2192 [Validate related fields before saving](https://docs.dyrected.com/docs/recipes/cross-field-validation)\n- \u201Cmake one dropdown depend on another\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cshow states based on the selected country\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Ccreate a cascading dropdown\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cupdate select options while editing\u201D \u2192 [Update a dropdown from another field](https://docs.dyrected.com/docs/recipes/dependent-dropdown)\n- \u201Cadd draft and publish states\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Crequire review before publishing\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Ccreate an editorial workflow\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Clet editors submit content for approval\u201D \u2192 [Add draft, review, and publishing states](https://docs.dyrected.com/docs/recipes/editorial-publishing-workflow)\n- \u201Cusers should only see their own records\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cadd row level access\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cscope documents by owner\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cprevent users reading another user\'s data\u201D \u2192 [Limit documents to their owner](https://docs.dyrected.com/docs/recipes/owner-scoped-access)\n- \u201Cbuild a page builder\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Clet editors arrange page sections\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Ccreate reusable content blocks\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Cmodel flexible landing pages\u201D \u2192 [Build flexible pages from reusable blocks](https://docs.dyrected.com/docs/recipes/page-builder-blocks)\n- \u201Cconnect posts to authors\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Cshow every post written by a user\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Ccreate a reverse relationship\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Cmodel one-to-many content\u201D \u2192 [Model a relationship and its reverse lookup](https://docs.dyrected.com/docs/recipes/relationship-and-reverse-join)\n- \u201Conly editors can update content\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Crestrict deletion to admins\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Cmake content publicly readable\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Cadd role based access\u201D \u2192 [Restrict content operations by user role](https://docs.dyrected.com/docs/recipes/role-based-access)\n- \u201Crename a field safely\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Cchange a field name without losing data\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Cmigrate an existing schema\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Ckeep old documents working after a rename\u201D \u2192 [Rename a field without orphaning existing data](https://docs.dyrected.com/docs/recipes/safe-field-rename)\n- \u201Clet editors upload images\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Ccreate a media library\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Cstore uploaded files\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n- \u201Cadd image uploads to my project\u201D \u2192 [Create a media upload collection](https://docs.dyrected.com/docs/recipes/upload-collection)\n<!-- GENERATED:INTENTS:END -->\n\n## Generated contract map\n\n<!-- GENERATED:REFERENCES:START -->\n- [Configuration](https://docs.dyrected.com/docs/reference/configuration)\n- [Fields and hooks](https://docs.dyrected.com/docs/reference/fields)\n- [Database adapters](https://docs.dyrected.com/docs/adapters/databases)\n- [Storage adapters](https://docs.dyrected.com/docs/adapters/storage)\n- [SDK](https://docs.dyrected.com/docs/reference/sdk)\n- [Workflows](https://docs.dyrected.com/docs/reference/generated-workflows)\n- [REST and OpenAPI](https://docs.dyrected.com/docs/reference/rest-api)\n<!-- GENERATED:REFERENCES:END -->\n\n## Work sequence\n\n1. Inspect installation, versions, framework and existing configuration.\n2. Translate the plain-language outcome into the matching recipe or documented contract.\n3. Implement no more than three related collections or globals in one batch.\n4. Keep every field labeled and preserve stored data during schema evolution.\n5. Run lint, types, focused tests and build; fix failures before expanding scope.\n6. Explain decisions in the user\'s language. Do not ask them to choose between technical CMS concepts when the intent determines the correct pattern.\n\n## Troubleshooting\n\n- Missing export: inspect the installed package version; do not substitute an internal source import.\n- Admin route failure: remove custom auth wrappers and verify the framework integration generated by the CLI.\n- Empty frontend: provide an intentional zero-state or SDK `initialData` fallback.\n- Relationship payload too large: lower query depth.\n- Cloud condition missing: replace callback conditions with Jexl strings.\n- Existing records fail after a schema change: restore the old key through `renameTo` and add a compatible default.\n\n## Completion checklist\n\n- Installed package APIs were verified.\n- Existing configuration and content were inspected.\n- Named fields have labels.\n- Access and auth are server-enforced.\n- Migrations preserve existing data.\n- Generated knowledge is current.\n- Lint, type checking, tests and build pass.\n';
2746
+ function buildAiRules() {
2747
+ return AI_RULES;
2748
+ }
2749
+
2750
+ // src/index.ts
2751
+ function findRecipesByIntent2(query, options) {
2752
+ return findRecipesByIntent(query, recipes, options);
2753
+ }
2754
+ function getRecipe(id) {
2755
+ return recipes.find((recipe) => recipe.id === id);
2756
+ }
2757
+ function getReference(id) {
2758
+ return references.find((reference) => reference.id === id);
2759
+ }
2760
+ function getEndpoint(method, path) {
2761
+ return endpoints.find(
2762
+ (endpoint) => endpoint.method === method.toUpperCase() && endpoint.path === path
2763
+ );
2764
+ }
2765
+ export {
2766
+ AI_RULES,
2767
+ SKILL,
2768
+ buildAiRules,
2769
+ endpoints,
2770
+ findRecipesByIntent2 as findRecipesByIntent,
2771
+ getEndpoint,
2772
+ getRecipe,
2773
+ getReference,
2774
+ recipes,
2775
+ references
2776
+ };