@byline/core 0.9.3

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.
Files changed (283) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +17 -0
  3. package/dist/@types/admin-types.d.ts +275 -0
  4. package/dist/@types/admin-types.d.ts.map +1 -0
  5. package/dist/@types/admin-types.js +18 -0
  6. package/dist/@types/admin-types.js.map +1 -0
  7. package/dist/@types/collection-types.d.ts +816 -0
  8. package/dist/@types/collection-types.d.ts.map +1 -0
  9. package/dist/@types/collection-types.js +217 -0
  10. package/dist/@types/collection-types.js.map +1 -0
  11. package/dist/@types/db-types.d.ts +463 -0
  12. package/dist/@types/db-types.d.ts.map +1 -0
  13. package/dist/@types/db-types.js +2 -0
  14. package/dist/@types/db-types.js.map +1 -0
  15. package/dist/@types/field-data-types.d.ts +147 -0
  16. package/dist/@types/field-data-types.d.ts.map +1 -0
  17. package/dist/@types/field-data-types.js +38 -0
  18. package/dist/@types/field-data-types.js.map +1 -0
  19. package/dist/@types/field-types.d.ts +579 -0
  20. package/dist/@types/field-types.d.ts.map +1 -0
  21. package/dist/@types/field-types.js +32 -0
  22. package/dist/@types/field-types.js.map +1 -0
  23. package/dist/@types/index.d.ts +18 -0
  24. package/dist/@types/index.d.ts.map +1 -0
  25. package/dist/@types/index.js +18 -0
  26. package/dist/@types/index.js.map +1 -0
  27. package/dist/@types/populate-types.d.ts +54 -0
  28. package/dist/@types/populate-types.d.ts.map +1 -0
  29. package/dist/@types/populate-types.js +9 -0
  30. package/dist/@types/populate-types.js.map +1 -0
  31. package/dist/@types/query-predicate.d.ts +74 -0
  32. package/dist/@types/query-predicate.d.ts.map +1 -0
  33. package/dist/@types/query-predicate.js +9 -0
  34. package/dist/@types/query-predicate.js.map +1 -0
  35. package/dist/@types/site-config.d.ts +212 -0
  36. package/dist/@types/site-config.d.ts.map +1 -0
  37. package/dist/@types/site-config.js +9 -0
  38. package/dist/@types/site-config.js.map +1 -0
  39. package/dist/@types/storage-types.d.ts +86 -0
  40. package/dist/@types/storage-types.d.ts.map +1 -0
  41. package/dist/@types/storage-types.js +9 -0
  42. package/dist/@types/storage-types.js.map +1 -0
  43. package/dist/@types/store-types.d.ts +134 -0
  44. package/dist/@types/store-types.d.ts.map +1 -0
  45. package/dist/@types/store-types.js +24 -0
  46. package/dist/@types/store-types.js.map +1 -0
  47. package/dist/@types/type-utils.d.ts +17 -0
  48. package/dist/@types/type-utils.d.ts.map +1 -0
  49. package/dist/@types/type-utils.js +9 -0
  50. package/dist/@types/type-utils.js.map +1 -0
  51. package/dist/auth/apply-before-read.d.ts +36 -0
  52. package/dist/auth/apply-before-read.d.ts.map +1 -0
  53. package/dist/auth/apply-before-read.js +68 -0
  54. package/dist/auth/apply-before-read.js.map +1 -0
  55. package/dist/auth/apply-before-read.test.node.d.ts +9 -0
  56. package/dist/auth/apply-before-read.test.node.d.ts.map +1 -0
  57. package/dist/auth/apply-before-read.test.node.js +144 -0
  58. package/dist/auth/apply-before-read.test.node.js.map +1 -0
  59. package/dist/auth/assert-actor-can-perform.d.ts +39 -0
  60. package/dist/auth/assert-actor-can-perform.d.ts.map +1 -0
  61. package/dist/auth/assert-actor-can-perform.js +64 -0
  62. package/dist/auth/assert-actor-can-perform.js.map +1 -0
  63. package/dist/auth/assert-actor-can-perform.test.node.d.ts +9 -0
  64. package/dist/auth/assert-actor-can-perform.test.node.d.ts.map +1 -0
  65. package/dist/auth/assert-actor-can-perform.test.node.js +119 -0
  66. package/dist/auth/assert-actor-can-perform.test.node.js.map +1 -0
  67. package/dist/auth/index.d.ts +11 -0
  68. package/dist/auth/index.d.ts.map +1 -0
  69. package/dist/auth/index.js +11 -0
  70. package/dist/auth/index.js.map +1 -0
  71. package/dist/auth/register-collection-abilities.d.ts +40 -0
  72. package/dist/auth/register-collection-abilities.d.ts.map +1 -0
  73. package/dist/auth/register-collection-abilities.js +87 -0
  74. package/dist/auth/register-collection-abilities.js.map +1 -0
  75. package/dist/auth/register-collection-abilities.test.node.d.ts +9 -0
  76. package/dist/auth/register-collection-abilities.test.node.d.ts.map +1 -0
  77. package/dist/auth/register-collection-abilities.test.node.js +124 -0
  78. package/dist/auth/register-collection-abilities.test.node.js.map +1 -0
  79. package/dist/config/config.d.ts +10 -0
  80. package/dist/config/config.d.ts.map +1 -0
  81. package/dist/config/config.js +108 -0
  82. package/dist/config/config.js.map +1 -0
  83. package/dist/config/routes.d.ts +16 -0
  84. package/dist/config/routes.d.ts.map +1 -0
  85. package/dist/config/routes.js +26 -0
  86. package/dist/config/routes.js.map +1 -0
  87. package/dist/config/validate-admin-configs.d.ts +33 -0
  88. package/dist/config/validate-admin-configs.d.ts.map +1 -0
  89. package/dist/config/validate-admin-configs.js +250 -0
  90. package/dist/config/validate-admin-configs.js.map +1 -0
  91. package/dist/config/validate-admin-configs.test.node.d.ts +9 -0
  92. package/dist/config/validate-admin-configs.test.node.d.ts.map +1 -0
  93. package/dist/config/validate-admin-configs.test.node.js +224 -0
  94. package/dist/config/validate-admin-configs.test.node.js.map +1 -0
  95. package/dist/config/validate-collections.d.ts +33 -0
  96. package/dist/config/validate-collections.d.ts.map +1 -0
  97. package/dist/config/validate-collections.js +70 -0
  98. package/dist/config/validate-collections.js.map +1 -0
  99. package/dist/config/validate-collections.test.node.d.ts +9 -0
  100. package/dist/config/validate-collections.test.node.d.ts.map +1 -0
  101. package/dist/config/validate-collections.test.node.js +149 -0
  102. package/dist/config/validate-collections.test.node.js.map +1 -0
  103. package/dist/core.d.ts +89 -0
  104. package/dist/core.d.ts.map +1 -0
  105. package/dist/core.js +99 -0
  106. package/dist/core.js.map +1 -0
  107. package/dist/defaults/default-values.d.ts +13 -0
  108. package/dist/defaults/default-values.d.ts.map +1 -0
  109. package/dist/defaults/default-values.js +60 -0
  110. package/dist/defaults/default-values.js.map +1 -0
  111. package/dist/index.d.ts +20 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +36 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/lib/errors.d.ts +98 -0
  116. package/dist/lib/errors.d.ts.map +1 -0
  117. package/dist/lib/errors.js +134 -0
  118. package/dist/lib/errors.js.map +1 -0
  119. package/dist/lib/logger.d.ts +62 -0
  120. package/dist/lib/logger.d.ts.map +1 -0
  121. package/dist/lib/logger.js +120 -0
  122. package/dist/lib/logger.js.map +1 -0
  123. package/dist/lib/registry.d.ts +65 -0
  124. package/dist/lib/registry.d.ts.map +1 -0
  125. package/dist/lib/registry.js +133 -0
  126. package/dist/lib/registry.js.map +1 -0
  127. package/dist/logger/index.d.ts +3 -0
  128. package/dist/logger/index.d.ts.map +1 -0
  129. package/dist/logger/index.js +3 -0
  130. package/dist/logger/index.js.map +1 -0
  131. package/dist/patches/apply-patches.d.ts +21 -0
  132. package/dist/patches/apply-patches.d.ts.map +1 -0
  133. package/dist/patches/apply-patches.js +357 -0
  134. package/dist/patches/apply-patches.js.map +1 -0
  135. package/dist/patches/index.d.ts +3 -0
  136. package/dist/patches/index.d.ts.map +1 -0
  137. package/dist/patches/index.js +4 -0
  138. package/dist/patches/index.js.map +1 -0
  139. package/dist/patches/patch-types.d.ts +82 -0
  140. package/dist/patches/patch-types.d.ts.map +1 -0
  141. package/dist/patches/patch-types.js +3 -0
  142. package/dist/patches/patch-types.js.map +1 -0
  143. package/dist/patches/patch.test.node.d.ts +2 -0
  144. package/dist/patches/patch.test.node.d.ts.map +1 -0
  145. package/dist/patches/patch.test.node.js +193 -0
  146. package/dist/patches/patch.test.node.js.map +1 -0
  147. package/dist/query/parse-where.d.ts +100 -0
  148. package/dist/query/parse-where.d.ts.map +1 -0
  149. package/dist/query/parse-where.js +352 -0
  150. package/dist/query/parse-where.js.map +1 -0
  151. package/dist/query/parse-where.test.node.d.ts +9 -0
  152. package/dist/query/parse-where.test.node.d.ts.map +1 -0
  153. package/dist/query/parse-where.test.node.js +581 -0
  154. package/dist/query/parse-where.test.node.js.map +1 -0
  155. package/dist/schemas/zod/builder.d.ts +466 -0
  156. package/dist/schemas/zod/builder.d.ts.map +1 -0
  157. package/dist/schemas/zod/builder.js +276 -0
  158. package/dist/schemas/zod/builder.js.map +1 -0
  159. package/dist/schemas/zod/cache.d.ts +14 -0
  160. package/dist/schemas/zod/cache.d.ts.map +1 -0
  161. package/dist/schemas/zod/cache.js +40 -0
  162. package/dist/schemas/zod/cache.js.map +1 -0
  163. package/dist/schemas/zod/index.d.ts +4 -0
  164. package/dist/schemas/zod/index.d.ts.map +1 -0
  165. package/dist/schemas/zod/index.js +4 -0
  166. package/dist/schemas/zod/index.js.map +1 -0
  167. package/dist/schemas/zod/types.d.ts +13 -0
  168. package/dist/schemas/zod/types.d.ts.map +1 -0
  169. package/dist/schemas/zod/types.js +2 -0
  170. package/dist/schemas/zod/types.js.map +1 -0
  171. package/dist/services/collection-bootstrap.d.ts +46 -0
  172. package/dist/services/collection-bootstrap.d.ts.map +1 -0
  173. package/dist/services/collection-bootstrap.js +108 -0
  174. package/dist/services/collection-bootstrap.js.map +1 -0
  175. package/dist/services/collection-bootstrap.test.node.d.ts +9 -0
  176. package/dist/services/collection-bootstrap.test.node.d.ts.map +1 -0
  177. package/dist/services/collection-bootstrap.test.node.js +208 -0
  178. package/dist/services/collection-bootstrap.test.node.js.map +1 -0
  179. package/dist/services/document-lifecycle.d.ts +245 -0
  180. package/dist/services/document-lifecycle.d.ts.map +1 -0
  181. package/dist/services/document-lifecycle.js +481 -0
  182. package/dist/services/document-lifecycle.js.map +1 -0
  183. package/dist/services/document-lifecycle.test.node.d.ts +9 -0
  184. package/dist/services/document-lifecycle.test.node.d.ts.map +1 -0
  185. package/dist/services/document-lifecycle.test.node.js +781 -0
  186. package/dist/services/document-lifecycle.test.node.js.map +1 -0
  187. package/dist/services/document-read.d.ts +26 -0
  188. package/dist/services/document-read.d.ts.map +1 -0
  189. package/dist/services/document-read.js +60 -0
  190. package/dist/services/document-read.js.map +1 -0
  191. package/dist/services/field-upload.d.ts +100 -0
  192. package/dist/services/field-upload.d.ts.map +1 -0
  193. package/dist/services/field-upload.js +328 -0
  194. package/dist/services/field-upload.js.map +1 -0
  195. package/dist/services/field-upload.test.node.d.ts +9 -0
  196. package/dist/services/field-upload.test.node.d.ts.map +1 -0
  197. package/dist/services/field-upload.test.node.js +337 -0
  198. package/dist/services/field-upload.test.node.js.map +1 -0
  199. package/dist/services/index.d.ts +10 -0
  200. package/dist/services/index.d.ts.map +1 -0
  201. package/dist/services/index.js +11 -0
  202. package/dist/services/index.js.map +1 -0
  203. package/dist/services/populate.d.ts +299 -0
  204. package/dist/services/populate.d.ts.map +1 -0
  205. package/dist/services/populate.js +484 -0
  206. package/dist/services/populate.js.map +1 -0
  207. package/dist/services/populate.test.node.d.ts +9 -0
  208. package/dist/services/populate.test.node.d.ts.map +1 -0
  209. package/dist/services/populate.test.node.js +910 -0
  210. package/dist/services/populate.test.node.js.map +1 -0
  211. package/dist/services/relation-projection.d.ts +52 -0
  212. package/dist/services/relation-projection.d.ts.map +1 -0
  213. package/dist/services/relation-projection.js +81 -0
  214. package/dist/services/relation-projection.js.map +1 -0
  215. package/dist/services/richtext-populate.d.ts +87 -0
  216. package/dist/services/richtext-populate.d.ts.map +1 -0
  217. package/dist/services/richtext-populate.js +189 -0
  218. package/dist/services/richtext-populate.js.map +1 -0
  219. package/dist/services/richtext-populate.test.node.d.ts +9 -0
  220. package/dist/services/richtext-populate.test.node.d.ts.map +1 -0
  221. package/dist/services/richtext-populate.test.node.js +197 -0
  222. package/dist/services/richtext-populate.test.node.js.map +1 -0
  223. package/dist/storage/collection-fingerprint.d.ts +21 -0
  224. package/dist/storage/collection-fingerprint.d.ts.map +1 -0
  225. package/dist/storage/collection-fingerprint.js +172 -0
  226. package/dist/storage/collection-fingerprint.js.map +1 -0
  227. package/dist/storage/collection-fingerprint.test.node.d.ts +9 -0
  228. package/dist/storage/collection-fingerprint.test.node.d.ts.map +1 -0
  229. package/dist/storage/collection-fingerprint.test.node.js +256 -0
  230. package/dist/storage/collection-fingerprint.test.node.js.map +1 -0
  231. package/dist/storage/field-store-map.d.ts +59 -0
  232. package/dist/storage/field-store-map.d.ts.map +1 -0
  233. package/dist/storage/field-store-map.js +75 -0
  234. package/dist/storage/field-store-map.js.map +1 -0
  235. package/dist/storage/field-store-map.test.node.d.ts +9 -0
  236. package/dist/storage/field-store-map.test.node.d.ts.map +1 -0
  237. package/dist/storage/field-store-map.test.node.js +117 -0
  238. package/dist/storage/field-store-map.test.node.js.map +1 -0
  239. package/dist/storage/index.d.ts +10 -0
  240. package/dist/storage/index.d.ts.map +1 -0
  241. package/dist/storage/index.js +10 -0
  242. package/dist/storage/index.js.map +1 -0
  243. package/dist/utils/normalise-dates.d.ts +15 -0
  244. package/dist/utils/normalise-dates.d.ts.map +1 -0
  245. package/dist/utils/normalise-dates.js +22 -0
  246. package/dist/utils/normalise-dates.js.map +1 -0
  247. package/dist/utils/slugify.d.ts +56 -0
  248. package/dist/utils/slugify.d.ts.map +1 -0
  249. package/dist/utils/slugify.js +91 -0
  250. package/dist/utils/slugify.js.map +1 -0
  251. package/dist/utils/slugify.test.node.d.ts +9 -0
  252. package/dist/utils/slugify.test.node.d.ts.map +1 -0
  253. package/dist/utils/slugify.test.node.js +86 -0
  254. package/dist/utils/slugify.test.node.js.map +1 -0
  255. package/dist/utils/storage-utils.d.ts +36 -0
  256. package/dist/utils/storage-utils.d.ts.map +1 -0
  257. package/dist/utils/storage-utils.js +38 -0
  258. package/dist/utils/storage-utils.js.map +1 -0
  259. package/dist/utils/utils.general.d.ts +64 -0
  260. package/dist/utils/utils.general.d.ts.map +1 -0
  261. package/dist/utils/utils.general.js +219 -0
  262. package/dist/utils/utils.general.js.map +1 -0
  263. package/dist/validation/index.d.ts +9 -0
  264. package/dist/validation/index.d.ts.map +1 -0
  265. package/dist/validation/index.js +9 -0
  266. package/dist/validation/index.js.map +1 -0
  267. package/dist/validation/shared.d.ts +36 -0
  268. package/dist/validation/shared.d.ts.map +1 -0
  269. package/dist/validation/shared.js +42 -0
  270. package/dist/validation/shared.js.map +1 -0
  271. package/dist/workflow/index.d.ts +2 -0
  272. package/dist/workflow/index.d.ts.map +1 -0
  273. package/dist/workflow/index.js +3 -0
  274. package/dist/workflow/index.js.map +1 -0
  275. package/dist/workflow/workflow.d.ts +40 -0
  276. package/dist/workflow/workflow.d.ts.map +1 -0
  277. package/dist/workflow/workflow.js +96 -0
  278. package/dist/workflow/workflow.js.map +1 -0
  279. package/dist/workflow/workflow.test.node.d.ts +2 -0
  280. package/dist/workflow/workflow.test.node.d.ts.map +1 -0
  281. package/dist/workflow/workflow.test.node.js +198 -0
  282. package/dist/workflow/workflow.test.node.js.map +1 -0
  283. package/package.json +88 -0
@@ -0,0 +1,816 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { RequestContext } from '@byline/auth';
9
+ import type { ReadContext } from './db-types.js';
10
+ import type { FieldSetData, FieldSetDataAllLocales, StoredFileValue } from './field-data-types.js';
11
+ import type { Block, DefaultValue, Field, FileField, ImageField } from './field-types.js';
12
+ import type { QueryPredicate } from './query-predicate.js';
13
+ import type { IStorageProvider } from './storage-types.js';
14
+ import type { Prettify } from './type-utils.js';
15
+ /** Output format for Sharp-generated image variants. */
16
+ export type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif';
17
+ /**
18
+ * Resize fit strategy passed to Sharp.
19
+ * Mirrors Sharp's `fit` option.
20
+ */
21
+ export type ImageFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
22
+ /**
23
+ * A named image size variant to generate after upload via Sharp.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * { name: 'thumbnail', width: 200, height: 200, fit: 'cover' }
28
+ * { name: 'desktop', width: 1920, fit: 'inside', format: 'webp', quality: 85 }
29
+ * ```
30
+ */
31
+ export interface ImageSize {
32
+ /** A unique name for this variant (e.g. `'thumbnail'`, `'desktop'`). */
33
+ name: string;
34
+ /** Target width in pixels. Omit to scale proportionally from height. */
35
+ width?: number;
36
+ /** Target height in pixels. Omit to scale proportionally from width. */
37
+ height?: number;
38
+ /** Resize fit strategy. Defaults to `'cover'`. */
39
+ fit?: ImageFit;
40
+ /** Output format override. Defaults to the original image format. */
41
+ format?: ImageFormat;
42
+ /** Quality (1–100). Relevant for jpeg, webp, and avif output. */
43
+ quality?: number;
44
+ }
45
+ /**
46
+ * Configuration block declared on an `image` or `file` field. Hangs the
47
+ * upload contract — accepted MIME types, size limit, generated variants,
48
+ * storage routing, and server-side hooks — directly off the field that
49
+ * receives the file. A collection with at least one image/file field
50
+ * carrying an `upload` block is upload-capable; the auto-mounted route at
51
+ * `POST /admin/api/<collection-path>/upload` accepts a `field` selector
52
+ * to pick the target field.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * export const Media: CollectionDefinition = {
57
+ * path: 'media',
58
+ * fields: [
59
+ * {
60
+ * name: 'image',
61
+ * label: 'Image',
62
+ * type: 'image',
63
+ * upload: {
64
+ * mimeTypes: ['image/*'],
65
+ * maxFileSize: 10 * 1024 * 1024, // 10 MB
66
+ * sizes: [
67
+ * { name: 'thumbnail', width: 300, height: 300, fit: 'cover' },
68
+ * { name: 'mobile', width: 768, fit: 'inside' },
69
+ * { name: 'desktop', width: 1920, fit: 'inside', format: 'webp', quality: 85 },
70
+ * ],
71
+ * },
72
+ * },
73
+ * // ...
74
+ * ],
75
+ * }
76
+ * ```
77
+ */
78
+ export interface UploadConfig {
79
+ /**
80
+ * Allowed MIME types. Supports wildcards (e.g. `'image/*'`).
81
+ * Omit to allow all types.
82
+ */
83
+ mimeTypes?: string[];
84
+ /** Maximum file size in bytes. Omit for no limit. */
85
+ maxFileSize?: number;
86
+ /**
87
+ * Named image variants to generate via Sharp after the original is
88
+ * stored. Only applied to MIME types that match `image/*`.
89
+ * Omit to skip image processing (e.g. for a video or PDF field).
90
+ */
91
+ sizes?: ImageSize[];
92
+ /**
93
+ * Storage provider for this field.
94
+ *
95
+ * When set, this takes precedence over the site-wide `ServerConfig.storage`
96
+ * default. Use this to route different fields to different backends —
97
+ * for example, keep avatars on local disk while sending editorial
98
+ * images to S3, or target separate S3 buckets per field.
99
+ *
100
+ * Falls back to `ServerConfig.storage` when omitted.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * // Dedicated S3 bucket just for this field:
105
+ * upload: {
106
+ * mimeTypes: ['image/*'],
107
+ * storage: s3StorageProvider({ bucket: 'my-photos', region: 'eu-west-1', ... }),
108
+ * }
109
+ * ```
110
+ */
111
+ storage?: IStorageProvider;
112
+ /**
113
+ * Server-side lifecycle hooks for this field's upload pipeline.
114
+ *
115
+ * `beforeStore` fires after MIME / size validation passes and before
116
+ * the storage provider is asked to write the file — the bytes have
117
+ * already crossed the network and live on the server (an in-memory
118
+ * Buffer or /tmp file, depending on the storage adapter), but
119
+ * permanent storage hasn't been touched yet. It can rename or reject
120
+ * the upload.
121
+ *
122
+ * `afterStore` fires after the original file *and* all image variants
123
+ * have been written to the storage provider, before the document
124
+ * version is created.
125
+ *
126
+ * @see UploadHooks
127
+ */
128
+ hooks?: UploadHooks;
129
+ }
130
+ /**
131
+ * The three status names that every workflow must contain.
132
+ *
133
+ * Storage, versioning, and API logic depend on these statuses being present.
134
+ * `defineWorkflow()` enforces their existence and ordering automatically:
135
+ *
136
+ * `[draft, ...customStatuses, published, archived]`
137
+ */
138
+ export declare const WORKFLOW_STATUS_DRAFT: "draft";
139
+ export declare const WORKFLOW_STATUS_PUBLISHED: "published";
140
+ export declare const WORKFLOW_STATUS_ARCHIVED: "archived";
141
+ export declare const REQUIRED_WORKFLOW_STATUSES: readonly ["draft", "published", "archived"];
142
+ export type RequiredWorkflowStatusName = (typeof REQUIRED_WORKFLOW_STATUSES)[number];
143
+ /**
144
+ * A single status in a sequential workflow.
145
+ *
146
+ * `name` is the value stored in the database (e.g. `'draft'`, `'needs_review'`).
147
+ * `label` is an optional human-readable display label for the status indicator (defaults to `name`).
148
+ * `verb` is an optional action label shown on the transition button (e.g. "Publish"). Defaults to `label` then `name`.
149
+ */
150
+ export interface WorkflowStatus {
151
+ name: string;
152
+ label?: string;
153
+ verb?: string;
154
+ }
155
+ /**
156
+ * Configurable sequential workflow for a collection.
157
+ *
158
+ * The `statuses` array defines the ordered progression. The first entry is
159
+ * used as the default status for new documents unless `defaultStatus` is
160
+ * explicitly set.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * defineWorkflow({
165
+ * draft: { label: 'Draft', verb: 'Revert to Draft' },
166
+ * published: { label: 'Published', verb: 'Publish' },
167
+ * archived: { label: 'Archived', verb: 'Archive' },
168
+ * customStatuses: [
169
+ * { name: 'needs_review', label: 'Needs Review', verb: 'Request Review' },
170
+ * ],
171
+ * })
172
+ * ```
173
+ */
174
+ export interface WorkflowConfig {
175
+ statuses: WorkflowStatus[];
176
+ /** Override the default status for new documents (defaults to the first entry). */
177
+ defaultStatus?: string;
178
+ }
179
+ /**
180
+ * Optional label/verb overrides for one of the three required workflow statuses.
181
+ *
182
+ * The `name` is fixed (`'draft'`, `'published'`, or `'archived'`) and injected
183
+ * automatically by `defineWorkflow()`.
184
+ */
185
+ export interface RequiredStatusConfig {
186
+ label?: string;
187
+ verb?: string;
188
+ }
189
+ /**
190
+ * Input accepted by `defineWorkflow()`.
191
+ *
192
+ * The three required statuses — `draft`, `published`, `archived` — are always
193
+ * present in the resulting workflow. Their position is enforced:
194
+ *
195
+ * `[draft, ...customStatuses, published, archived]`
196
+ *
197
+ * Each required status accepts an optional `{ label, verb }` override.
198
+ * Any additional statuses are placed between `draft` and `published` via
199
+ * `customStatuses`, in the order specified.
200
+ */
201
+ export interface DefineWorkflowInput {
202
+ /** Customize the `draft` status (always first). Defaults to `{ label: 'Draft' }`. */
203
+ draft?: RequiredStatusConfig;
204
+ /** Customize the `published` status (always second-to-last). Defaults to `{ label: 'Published' }`. */
205
+ published?: RequiredStatusConfig;
206
+ /** Customize the `archived` status (always last). Defaults to `{ label: 'Archived' }`. */
207
+ archived?: RequiredStatusConfig;
208
+ /** Additional statuses placed between `draft` and `published`, in order. */
209
+ customStatuses?: WorkflowStatus[];
210
+ /**
211
+ * Override the default status for new documents (defaults to `'draft'`).
212
+ *
213
+ * Setting this to `'published'` is the supported "publish-on-save" pattern:
214
+ * the full draft → published → archived lifecycle stays available, but new
215
+ * versions land directly as `published` so trusted editors can skip the
216
+ * draft step. For collections that have no editorial lifecycle at all
217
+ * (categories, tags, taxonomies), use `SINGLE_STATUS_WORKFLOW` instead —
218
+ * that also strips the workflow controls from the admin UI.
219
+ */
220
+ defaultStatus?: string;
221
+ }
222
+ /**
223
+ * The built-in default workflow used when a collection does not define its own.
224
+ */
225
+ export declare const DEFAULT_WORKFLOW: WorkflowConfig;
226
+ /**
227
+ * Single-status workflow for collections that have no editorial lifecycle —
228
+ * typically lookup or reference data such as categories, tags, taxonomies,
229
+ * and facets where a draft → review → publish flow adds friction without
230
+ * value.
231
+ *
232
+ * Effects of using this workflow:
233
+ * - Every save lands as `'published'` immediately, so public clients
234
+ * reading via `readMode: 'published'` see new rows right away.
235
+ * - The form renderer hides workflow controls and shows only Save / Close.
236
+ * - The list view's status filter is hidden.
237
+ * - `changeDocumentStatus()` and `unpublishDocument()` reject server-side
238
+ * because there is no other status to transition to.
239
+ *
240
+ * For editorial collections that should keep the draft/published/archived
241
+ * lifecycle but skip the draft step on save, prefer
242
+ * `defineWorkflow({ ..., defaultStatus: 'published' })` instead.
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * import { SINGLE_STATUS_WORKFLOW } from '@byline/core'
247
+ *
248
+ * export const DocsCategories: CollectionDefinition = {
249
+ * path: 'docs-categories',
250
+ * workflow: SINGLE_STATUS_WORKFLOW,
251
+ * // ...
252
+ * }
253
+ * ```
254
+ */
255
+ export declare const SINGLE_STATUS_WORKFLOW: WorkflowConfig;
256
+ /**
257
+ * Type-safe factory for creating a `WorkflowConfig`.
258
+ *
259
+ * Guarantees that the three required statuses (`draft`, `published`, `archived`)
260
+ * are always present and correctly ordered:
261
+ *
262
+ * `[draft, ...customStatuses, published, archived]`
263
+ *
264
+ * Custom statuses are inserted between `draft` and `published`. If a custom
265
+ * status uses a reserved name (`draft`, `published`, or `archived`) this
266
+ * function throws at initialization time.
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * // Minimal — uses default labels for all three required statuses:
271
+ * defineWorkflow({})
272
+ *
273
+ * // With custom labels/verbs on the required statuses:
274
+ * defineWorkflow({
275
+ * draft: { label: 'Draft', verb: 'Revert to Draft' },
276
+ * published: { label: 'Published', verb: 'Publish' },
277
+ * archived: { label: 'Archived', verb: 'Archive' },
278
+ * })
279
+ *
280
+ * // With additional statuses between draft and published:
281
+ * defineWorkflow({
282
+ * draft: { label: 'Draft', verb: 'Revert to Draft' },
283
+ * published: { label: 'Published', verb: 'Publish' },
284
+ * archived: { label: 'Archived', verb: 'Archive' },
285
+ * customStatuses: [
286
+ * { name: 'needs_review', label: 'Needs Review', verb: 'Request Review' },
287
+ * ],
288
+ * })
289
+ *
290
+ * // Publish-on-save: keep the full lifecycle, but new versions land
291
+ * // directly as `published` instead of `draft`.
292
+ * defineWorkflow({
293
+ * defaultStatus: 'published',
294
+ * })
295
+ * ```
296
+ *
297
+ * For collections that have no editorial lifecycle at all (categories,
298
+ * tags, taxonomies), use `SINGLE_STATUS_WORKFLOW` instead.
299
+ */
300
+ export declare function defineWorkflow(input?: DefineWorkflowInput): WorkflowConfig;
301
+ /**
302
+ * Context passed to `beforeCreate` hooks.
303
+ *
304
+ * The hook can mutate `data` before it is persisted.
305
+ */
306
+ export interface BeforeCreateContext {
307
+ data: Record<string, any>;
308
+ collectionPath: string;
309
+ }
310
+ /**
311
+ * Context passed to `afterCreate` hooks.
312
+ *
313
+ * Includes the `documentId` and `documentVersionId` returned by storage
314
+ * so the hook can reference the persisted document.
315
+ */
316
+ export interface AfterCreateContext {
317
+ data: Record<string, any>;
318
+ collectionPath: string;
319
+ documentId: string;
320
+ documentVersionId: string;
321
+ }
322
+ /**
323
+ * Context passed to `beforeUpdate` hooks — both PUT and patch flows.
324
+ *
325
+ * `data` is the next version (mutable). `originalData` is the previous
326
+ * version as reconstructed from storage.
327
+ */
328
+ export interface BeforeUpdateContext {
329
+ data: Record<string, any>;
330
+ originalData: Record<string, any>;
331
+ collectionPath: string;
332
+ }
333
+ /**
334
+ * Context passed to `afterUpdate` hooks.
335
+ *
336
+ * Includes the `documentId` and `documentVersionId` of the newly created
337
+ * version so the hook can reference the persisted document.
338
+ */
339
+ export interface AfterUpdateContext {
340
+ data: Record<string, any>;
341
+ originalData: Record<string, any>;
342
+ collectionPath: string;
343
+ documentId: string;
344
+ documentVersionId: string;
345
+ }
346
+ /**
347
+ * Context passed to `beforeStatusChange` / `afterStatusChange` hooks.
348
+ */
349
+ export interface StatusChangeContext {
350
+ documentId: string;
351
+ documentVersionId: string;
352
+ collectionPath: string;
353
+ previousStatus: string;
354
+ nextStatus: string;
355
+ }
356
+ /**
357
+ * Context passed to `beforeUnpublish` hooks.
358
+ */
359
+ export interface BeforeUnpublishContext {
360
+ documentId: string;
361
+ collectionPath: string;
362
+ }
363
+ /**
364
+ * Context passed to `afterUnpublish` hooks.
365
+ *
366
+ * `archivedCount` indicates how many published versions were archived.
367
+ */
368
+ export interface AfterUnpublishContext {
369
+ documentId: string;
370
+ collectionPath: string;
371
+ archivedCount: number;
372
+ }
373
+ /**
374
+ * Context passed to `beforeDelete` / `afterDelete` hooks (future).
375
+ */
376
+ export interface DeleteContext {
377
+ documentId: string;
378
+ collectionPath: string;
379
+ }
380
+ /**
381
+ * Context passed to `beforeStore` hooks (configured on
382
+ * `field.upload.hooks`).
383
+ *
384
+ * Fires after MIME-type and file-size validation succeed and before
385
+ * the storage provider is asked to write the file. By the time this
386
+ * hook runs the bytes have already crossed the network and live on
387
+ * the server — an in-memory `Buffer` in our current adapters, or a
388
+ * /tmp file with a future streaming adapter — but permanent storage
389
+ * has not yet been touched.
390
+ *
391
+ * The hook can:
392
+ *
393
+ * - Rename the file by returning a string or `{ filename }`. The
394
+ * override is threaded into `storage.upload(...)`, so generated
395
+ * image variants automatically inherit the new prefix.
396
+ * - Reject the upload by returning `{ error }`. Surfaces as
397
+ * `ERR_VALIDATION` with the supplied message; no file is written,
398
+ * no variants are generated, no document is created, no later
399
+ * hook in the chain runs.
400
+ * - Keep defaults by returning `void` / `undefined`.
401
+ *
402
+ * When configured as an array, hooks fold: each function receives the
403
+ * filename returned by the previous function (or the original sanitised
404
+ * filename if the previous returned `void`).
405
+ */
406
+ export interface BeforeStoreContext {
407
+ /** Name of the image/file field receiving this upload. */
408
+ fieldName: string;
409
+ /** The full field definition. Carries `field.upload` for hooks that want to introspect their own config. */
410
+ field: ImageField | FileField;
411
+ /** Sanitised default filename. Hooks may override. */
412
+ filename: string;
413
+ mimeType: string;
414
+ fileSize: number;
415
+ /**
416
+ * Other form values posted alongside the file. Use these to derive
417
+ * filenames from document context (e.g. `fields.publicationId`,
418
+ * `fields.serialNumber`). Always strings — multipart form values
419
+ * arrive untyped.
420
+ */
421
+ fields: Record<string, string>;
422
+ collectionPath: string;
423
+ /** Authenticated request context. `actor.id`, `actor.tenantId`, etc. for prefixing. */
424
+ requestContext: RequestContext;
425
+ }
426
+ /**
427
+ * Result returned by a `beforeStore` hook.
428
+ *
429
+ * - `string` → override filename (shorthand).
430
+ * - `{ filename }` → override filename (object form).
431
+ * - `{ error }` → reject the upload; surfaces as
432
+ * `ERR_VALIDATION`. Short-circuits the chain.
433
+ * - `void` / undefined → keep current defaults.
434
+ */
435
+ export type BeforeStoreResult = string | {
436
+ filename?: string;
437
+ error?: undefined;
438
+ } | {
439
+ error: string;
440
+ filename?: undefined;
441
+ } | void;
442
+ /**
443
+ * A `beforeStore` hook function. Async-capable.
444
+ */
445
+ export type BeforeStoreHookFn = (ctx: BeforeStoreContext) => BeforeStoreResult | Promise<BeforeStoreResult>;
446
+ /**
447
+ * Context passed to `afterStore` hooks (configured on
448
+ * `field.upload.hooks`).
449
+ *
450
+ * Fires after the original file and every generated image variant
451
+ * have been written to the storage provider, and before the document
452
+ * version is created. Suitable for CDN cache warmup, audit logging,
453
+ * or async post-processing kicks. Failures are logged but do not
454
+ * roll back the storage write — consistent with `afterCreate` etc.,
455
+ * which run outside the storage transaction.
456
+ */
457
+ export interface AfterStoreContext {
458
+ /** Name of the image/file field that received this upload. */
459
+ fieldName: string;
460
+ field: ImageField | FileField;
461
+ /**
462
+ * The persisted file value, including the `variants` array with
463
+ * `storagePath`, `storageUrl`, `width`, `height`, and `format` for
464
+ * each generated derivative.
465
+ */
466
+ storedFile: StoredFileValue;
467
+ fields: Record<string, string>;
468
+ collectionPath: string;
469
+ requestContext: RequestContext;
470
+ }
471
+ /** An `afterStore` hook function. Async-capable. */
472
+ export type AfterStoreHookFn = (ctx: AfterStoreContext) => void | Promise<void>;
473
+ /**
474
+ * Server-side hooks declared on an upload-capable field's `upload`
475
+ * block. Each hook accepts a single function or an ordered array;
476
+ * `beforeStore` chains fold filename overrides through the array,
477
+ * `afterStore` chains run sequentially with errors logged.
478
+ */
479
+ export interface UploadHooks {
480
+ beforeStore?: BeforeStoreHookFn | BeforeStoreHookFn[];
481
+ afterStore?: AfterStoreHookFn | AfterStoreHookFn[];
482
+ }
483
+ /**
484
+ * Context passed to `afterRead` hooks.
485
+ *
486
+ * Fires once per materialised document on every read path that runs through
487
+ * `@byline/client` or `populateDocuments`:
488
+ * - `find`, `findOne`, `findById`, `findByPath` on `CollectionHandle`
489
+ * (once per returned source document)
490
+ * - Each populated relation target across every depth level
491
+ *
492
+ * The hook receives the **raw storage shape** (`{document_version_id,
493
+ * document_id, path, status, created_at, updated_at, fields, …}`), not
494
+ * the camelCase `ClientDocument` — afterRead runs *before* the client's
495
+ * response shaping pass so mutations to `fields` propagate cleanly.
496
+ * Mutations persist in place; there is no return value.
497
+ *
498
+ * Fires **after** populate on the source document, so hooks can observe
499
+ * (and mutate) the fully populated tree.
500
+ *
501
+ * Recursion safety: `readContext` is the same request-scoped context used
502
+ * by populate. A hook that performs its own reads should thread this
503
+ * context back in via `client.collection(...).findById(id, { _readContext:
504
+ * readContext })` so the visited set and read budget are preserved —
505
+ * essential to foreclose the A→B→A loop (see
506
+ * `docs/analysis/RELATIONSHIPS-ANALYSIS.md` § "Special consideration:
507
+ * recursive-read safety").
508
+ */
509
+ export interface AfterReadContext {
510
+ /** The raw reconstructed document. Mutate in place — changes persist. */
511
+ doc: Record<string, any>;
512
+ collectionPath: string;
513
+ /** Thread this into any nested reads the hook performs. */
514
+ readContext: ReadContext;
515
+ }
516
+ /**
517
+ * Context passed to `beforeRead` hooks.
518
+ *
519
+ * Fires once per `IDocumentQueries.findDocuments` call (and once per populate
520
+ * batch, per target collection) **before** any DB work. The hook returns a
521
+ * `QueryPredicate` that the query layer compiles into the same `EXISTS` /
522
+ * `LEFT JOIN LATERAL` SQL machinery the client's existing `where` parser
523
+ * emits, then ANDs onto whatever the caller passed in `where`.
524
+ *
525
+ * Returning `undefined` (or simply `void`) means "no scoping" — typically
526
+ * the superuser / unconditional-read branch. Use a sentinel predicate that
527
+ * yields no rows (e.g. `{ id: '__none__' }`) when the actor cannot read
528
+ * anything; do not throw, because callers expect empty list results rather
529
+ * than collapsed endpoints.
530
+ *
531
+ * The hook receives:
532
+ * - `requestContext` — the authenticated request, including `actor`. The
533
+ * actor is the primary input to most predicates.
534
+ * - `readContext` — the same per-request context threaded through
535
+ * populate and `afterRead`. Carries a hook-result cache so async
536
+ * predicates don't re-run across populate fanout.
537
+ * - `collectionPath` — the collection being queried (useful when the
538
+ * same hook function is reused across collections).
539
+ *
540
+ * See `docs/analysis/AUTHN-AUTHZ-ANALYSIS.md` (Phase 7) for the strategic
541
+ * rationale and `docs/analysis/ACCESS-CONTROL-RECIPES.md` for worked
542
+ * examples.
543
+ */
544
+ export interface BeforeReadContext {
545
+ collectionPath: string;
546
+ requestContext: RequestContext;
547
+ readContext: ReadContext;
548
+ }
549
+ /**
550
+ * A `beforeRead` hook function. Returns a `QueryPredicate` to scope the
551
+ * query, or `void`/`undefined` to apply no scoping. May be async — actors
552
+ * needing tenant lookups or role-metadata fetches commonly are.
553
+ */
554
+ export type BeforeReadHookFn = (ctx: BeforeReadContext) => QueryPredicate | void | Promise<QueryPredicate | void>;
555
+ /**
556
+ * Slot type for `beforeRead`.
557
+ *
558
+ * Distinct from the generic `CollectionHookSlot` because `beforeRead`
559
+ * returns a value (a predicate). When multiple hook functions are
560
+ * configured, their predicates are combined with implicit AND in
561
+ * declaration order; functions that return `void` are skipped.
562
+ */
563
+ export type BeforeReadHookSlot = BeforeReadHookFn | BeforeReadHookFn[];
564
+ /**
565
+ * A single collection-hook function signature, parameterised by context type.
566
+ */
567
+ export type CollectionHookFn<Ctx> = (ctx: Ctx) => void | Promise<void>;
568
+ /**
569
+ * A hook slot: accepts a single function **or** an array of functions.
570
+ *
571
+ * When an array is provided the functions are executed sequentially in order.
572
+ */
573
+ export type CollectionHookSlot<Ctx> = CollectionHookFn<Ctx> | CollectionHookFn<Ctx>[];
574
+ /** Normalise a collection-hook slot (single function or array) into a flat array. */
575
+ export declare function normalizeCollectionHook<Ctx>(hook: CollectionHookSlot<Ctx> | undefined): CollectionHookFn<Ctx>[];
576
+ /**
577
+ * Lifecycle hooks for a collection.
578
+ *
579
+ * Each hook receives a typed context object. `before*` hooks can mutate the
580
+ * data before it is persisted; `after*` hooks receive the final data after
581
+ * persistence together with identifiers of what was created/updated.
582
+ *
583
+ * Hooks run **outside** the storage transaction — they cannot participate in
584
+ * the atomic write. They are suitable for logging, cache invalidation,
585
+ * webhooks, and similar side-effects.
586
+ *
587
+ * Each hook accepts a single function or an **array** of functions that are
588
+ * executed sequentially in order. Hooks are optional — if omitted, the
589
+ * framework skips the step.
590
+ */
591
+ export interface CollectionHooks {
592
+ /** Runs before a new document is created. Can mutate `data`. */
593
+ beforeCreate?: CollectionHookSlot<BeforeCreateContext>;
594
+ /** Runs after a new document is created. */
595
+ afterCreate?: CollectionHookSlot<AfterCreateContext>;
596
+ /** Runs before an existing document is updated (PUT or patch). Can mutate `data`. */
597
+ beforeUpdate?: CollectionHookSlot<BeforeUpdateContext>;
598
+ /** Runs after an existing document is updated. */
599
+ afterUpdate?: CollectionHookSlot<AfterUpdateContext>;
600
+ /** Runs before a document's workflow status is changed. */
601
+ beforeStatusChange?: CollectionHookSlot<StatusChangeContext>;
602
+ /** Runs after a document's workflow status has been changed. */
603
+ afterStatusChange?: CollectionHookSlot<StatusChangeContext>;
604
+ /** Runs before a published document is unpublished (archived). */
605
+ beforeUnpublish?: CollectionHookSlot<BeforeUnpublishContext>;
606
+ /** Runs after a published document has been unpublished. */
607
+ afterUnpublish?: CollectionHookSlot<AfterUnpublishContext>;
608
+ /** Runs before a document is deleted. */
609
+ beforeDelete?: CollectionHookSlot<DeleteContext>;
610
+ /** Runs after a document is deleted. */
611
+ afterDelete?: CollectionHookSlot<DeleteContext>;
612
+ /**
613
+ * Runs once per `findDocuments` call (and once per populate batch, per
614
+ * target collection), **before** any DB work. Returns a `QueryPredicate`
615
+ * that the query layer ANDs onto the caller's `where` to enforce
616
+ * read-side row scoping (multi-tenant, owner-only-drafts, soft-delete
617
+ * hide, etc). Returning `void` applies no scoping. Multiple functions
618
+ * combine with implicit AND. See
619
+ * `docs/analysis/ACCESS-CONTROL-RECIPES.md`.
620
+ */
621
+ beforeRead?: BeforeReadHookSlot;
622
+ /**
623
+ * Runs once per materialised document on every read path that flows
624
+ * through `@byline/client` or `populateDocuments`. Can mutate
625
+ * `ctx.doc.fields` in place — mutations propagate back through the
626
+ * response. Fires after populate on the source document, so hooks see
627
+ * the fully populated tree. Hooks that perform their own reads should
628
+ * thread `ctx.readContext` through to preserve the visited set and
629
+ * read budget (A→B→A safety).
630
+ */
631
+ afterRead?: CollectionHookSlot<AfterReadContext>;
632
+ }
633
+ export interface CollectionDefinition {
634
+ labels: {
635
+ singular: string;
636
+ plural: string;
637
+ };
638
+ path: string;
639
+ fields: Field[];
640
+ /** Sequential workflow configuration. Falls back to DEFAULT_WORKFLOW if omitted. */
641
+ workflow?: WorkflowConfig;
642
+ /** Lifecycle hooks for server-side document operations. */
643
+ hooks?: CollectionHooks;
644
+ /**
645
+ * Configures which text fields are searched when the admin list view's
646
+ * search box is used. Only `store_text` fields are supported for now.
647
+ * Falls back to `{ fields: ['title'] }` when omitted.
648
+ */
649
+ search?: {
650
+ fields: string[];
651
+ };
652
+ /**
653
+ * The field that represents this document's identity — used anywhere a
654
+ * single-line label for the document is needed: form headings, relation
655
+ * widget summaries, populate's default projection, future `afterRead`
656
+ * hooks, logs, etc.
657
+ *
658
+ * Lives on the schema (not admin config) so server-side consumers like
659
+ * `populateDocuments` and the client API can read it without taking a
660
+ * dependency on UI concerns. Analogous to Django's `Model.__str__`.
661
+ */
662
+ useAsTitle?: string;
663
+ /**
664
+ * Names the field whose value initialises this collection's
665
+ * `documentVersions.path` column. The value is slugified (in the
666
+ * default content locale) using the installation slugifier and stored
667
+ * as system metadata — `path` itself is a reserved name and cannot be
668
+ * declared as a field.
669
+ *
670
+ * `path` is sticky after creation: subsequent updates do not
671
+ * re-derive. Users edit it via the system path widget; collections
672
+ * without `useAsPath` receive a UUID `path` instead.
673
+ */
674
+ useAsPath?: string;
675
+ /***
676
+ * When `true`, the rich text editor's link plugin surfaces relation targets
677
+ * from this collection as linkable options. Requires the collection to have
678
+ * a `useAsTitle` field, which is used to label the options in the editor.
679
+ */
680
+ linksInEditor?: boolean;
681
+ /**
682
+ * When `true`, the admin landing page displays a per-status document count
683
+ * inside the collection card. Requires a database round-trip per collection
684
+ * on every landing-page load, so opt in deliberately.
685
+ */
686
+ showStats?: boolean;
687
+ /**
688
+ * Optional explicit version pin. When omitted, the startup bootstrap
689
+ * auto-increments the collection's stored version any time the schema
690
+ * fingerprint changes. When set, the value is used verbatim as long as it
691
+ * is >= the currently-stored version; pinning backwards throws at startup.
692
+ *
693
+ * The stamped version is written onto every `documentVersions` row so that
694
+ * a document can later be resolved against the schema shape it was
695
+ * authored under.
696
+ */
697
+ version?: number;
698
+ }
699
+ /**
700
+ * Type-safe factory for creating a CollectionDefinition.
701
+ * Returns the definition as-is but provides type checking.
702
+ */
703
+ export declare function defineCollection<const C extends CollectionDefinition>(definition: C & CollectionDefinition): C;
704
+ export type CollectionFieldData<C extends CollectionDefinition> = FieldSetData<C['fields']>;
705
+ export type CollectionFieldDataAllLocales<C extends CollectionDefinition> = FieldSetDataAllLocales<C['fields']>;
706
+ /**
707
+ * Type-safe factory for creating a Block. Returns the definition as-is,
708
+ * but locks in literal types for `blockType`, field names, and select
709
+ * option values — so `BlockFieldData<typeof MyBlock>` and the
710
+ * `_type: B['blockType']` discriminant resolve precisely. Replaces the
711
+ * `as const satisfies Block` pattern.
712
+ */
713
+ export declare function defineBlock<const B extends Block>(definition: B & Block): B;
714
+ /**
715
+ * Field-only data shape inferred from a block schema. The counterpart
716
+ * to `CollectionFieldData<C>` — use this when you want just the
717
+ * editable fields (e.g. for forms or block-internal helpers).
718
+ */
719
+ export type BlockFieldData<B extends Block> = FieldSetData<B['fields']>;
720
+ export type BlockFieldDataAllLocales<B extends Block> = FieldSetDataAllLocales<B['fields']>;
721
+ /**
722
+ * Full block instance shape as it appears inside a document tree:
723
+ * the field data plus the synthetic `_id` / `_type` discriminants
724
+ * written by the storage layer. Use this as the prop type for block
725
+ * renderers — `_type` lets a `switch (block._type)` exhaustively narrow
726
+ * across a `BlocksUnion<typeof MyBlocks>`.
727
+ */
728
+ export type BlockData<B extends Block> = Prettify<{
729
+ _id: string;
730
+ _type: B['blockType'];
731
+ } & BlockFieldData<B>>;
732
+ /**
733
+ * Discriminated union of `BlockData<B>` over a tuple of block
734
+ * definitions. Drives exhaustive switching in `RenderBlocks`-style
735
+ * renderers.
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * const Blocks = [PhotoBlock, RichTextBlock] as const
740
+ * type AnyBlock = BlocksUnion<typeof Blocks>
741
+ * // → BlockData<typeof PhotoBlock> | BlockData<typeof RichTextBlock>
742
+ * ```
743
+ */
744
+ export type BlocksUnion<Bs extends readonly Block[]> = Bs[number] extends infer B ? B extends Block ? BlockData<B> : never : never;
745
+ export type CollectionData<C extends CollectionDefinition> = Prettify<{
746
+ document_id: string;
747
+ document_version_id: string;
748
+ status: string;
749
+ created_at: Date;
750
+ updated_at: Date;
751
+ } & CollectionFieldData<C>>;
752
+ export type CollectionDataAllLocales<C extends CollectionDefinition> = Prettify<{
753
+ document_id: string;
754
+ document_version_id: string;
755
+ status: string;
756
+ created_at: Date;
757
+ updated_at: Date;
758
+ } & CollectionFieldDataAllLocales<C>>;
759
+ /**
760
+ * A field definition with all function-valued properties stripped.
761
+ *
762
+ * Safe for JSON serialization — use for API responses, SSR loader return
763
+ * values, RSC props, mobile clients, and CLI introspection. The following
764
+ * are omitted:
765
+ * - `validate` — client UI concern, cannot cross a network boundary
766
+ * - `hooks` — field-level hooks are always functions
767
+ * - `defaultValue` — only literal (non-function) defaults are preserved
768
+ *
769
+ * Nested `fields` (composite / array / blocks) are recursively serialized.
770
+ */
771
+ export type SerializableField = Omit<Field, 'validate' | 'hooks' | 'defaultValue' | 'fields' | 'blocks'> & {
772
+ /** Only literal defaults are serializable; function defaults are dropped. */
773
+ defaultValue?: Exclude<DefaultValue, (...args: any[]) => any>;
774
+ /** Recursively serializable child fields (group / array). */
775
+ fields?: SerializableField[];
776
+ /** Recursively serializable blocks (blocks field). */
777
+ blocks?: SerializableBlock[];
778
+ };
779
+ /**
780
+ * A block definition with all function-valued properties stripped.
781
+ */
782
+ export type SerializableBlock = Omit<Block, 'validate' | 'hooks' | 'fields'> & {
783
+ fields: SerializableField[];
784
+ };
785
+ /**
786
+ * A collection definition with all function-valued properties stripped.
787
+ *
788
+ * Safe for JSON serialization — use for API schema endpoints, SSR loaders,
789
+ * RSC components, mobile clients, and any context where the full definition
790
+ * (with live functions) cannot be transmitted.
791
+ *
792
+ * - Collection-level `hooks` are entirely omitted (all entries are functions).
793
+ * - Field `validate`, `hooks`, and function `defaultValue` are stripped.
794
+ *
795
+ * On the receiving end, resolve the full `CollectionDefinition` from the
796
+ * local config store via `getCollectionDefinition(path)` to regain access
797
+ * to validators, hooks, and computed defaults.
798
+ */
799
+ export type SerializableCollectionDefinition = Omit<CollectionDefinition, 'hooks' | 'fields'> & {
800
+ fields: SerializableField[];
801
+ };
802
+ /**
803
+ * Strips all function-valued properties from a `CollectionDefinition`,
804
+ * producing a version safe for JSON serialization.
805
+ *
806
+ * @example
807
+ * ```ts
808
+ * // In an API route:
809
+ * return Response.json(toSerializableCollection(collectionDef))
810
+ *
811
+ * // In an SSR loader:
812
+ * return { schema: toSerializableCollection(collectionDef), document: data }
813
+ * ```
814
+ */
815
+ export declare function toSerializableCollection(def: CollectionDefinition): SerializableCollectionDefinition;
816
+ //# sourceMappingURL=collection-types.d.ts.map