@relyio/sdk 0.4.0 → 0.6.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/{chunk-43VPZT2G.js → chunk-7ABNXVWL.js} +464 -200
  4. package/dist/{chunk-HAQELDZN.cjs → chunk-DY2XT7OF.cjs} +2 -2
  5. package/dist/{chunk-XS32U2UP.cjs → chunk-GCMQUM6N.cjs} +466 -199
  6. package/dist/{chunk-XY5KFEFY.js → chunk-XJVY2CLY.js} +2 -2
  7. package/dist/client/index.cjs +3 -3
  8. package/dist/client/index.d.cts +5 -4
  9. package/dist/client/index.d.ts +5 -4
  10. package/dist/client/index.js +1 -1
  11. package/dist/define-content-type-CDB3kyeA.d.cts +1173 -0
  12. package/dist/define-content-type-CDB3kyeA.d.ts +1173 -0
  13. package/dist/{define-schema-DzYXk4HL.d.ts → define-schema-1kr2HIzN.d.cts} +25 -347
  14. package/dist/{define-schema-2xbHgtK-.d.cts → define-schema-ChvAzY8H.d.ts} +25 -347
  15. package/dist/{index-BzOFJJKS.d.ts → index-Df1VsfJR.d.cts} +334 -72
  16. package/dist/{index-BvO6HDtY.d.cts → index-XqoGUaAY.d.ts} +334 -72
  17. package/dist/{index-DFehx5sI.d.cts → index-d1Kz1e19.d.cts} +191 -29
  18. package/dist/{index-FDak5ofk.d.ts → index-yPbhZc6q.d.ts} +191 -29
  19. package/dist/index.cjs +161 -68
  20. package/dist/index.d.cts +302 -8
  21. package/dist/index.d.ts +302 -8
  22. package/dist/index.js +90 -3
  23. package/dist/schema/index.cjs +35 -35
  24. package/dist/schema/index.d.cts +11 -7
  25. package/dist/schema/index.d.ts +11 -7
  26. package/dist/schema/index.js +1 -1
  27. package/dist/types/index.d.cts +2 -2
  28. package/dist/types/index.d.ts +2 -2
  29. package/package.json +3 -3
  30. package/dist/define-content-type-CBfnTWSS.d.cts +0 -716
  31. package/dist/define-content-type-CBfnTWSS.d.ts +0 -716
@@ -0,0 +1,1173 @@
1
+ /**
2
+ * Field type definitions matching @relyio/schema package
3
+ */
4
+ /**
5
+ * AI configuration. A single prompt — `instructions` — describes what the
6
+ * element is and how AI should produce its value. Attaches to fields, content
7
+ * types, components, fieldsets, and (extended) asset types.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * fields.richText('body', {
12
+ * ai: {
13
+ * instructions: `
14
+ * Write in a conversational tone.
15
+ * Use short paragraphs (2-3 sentences).
16
+ * Target grade 8 reading level.
17
+ * `
18
+ * }
19
+ * })
20
+ * ```
21
+ */
22
+ interface AIConfig {
23
+ /**
24
+ * AI brief for this element: what it should contain and how to produce it.
25
+ *
26
+ * Used by the Studio authoring agent and — on asset types with
27
+ * `ai.autoGenerate` — by the automatic post-upload run.
28
+ *
29
+ * The fallback chain for AI guidance is:
30
+ * 1. ai.instructions (most specific)
31
+ * 2. ui.description (general field description)
32
+ * 3. Field displayName + type (inferred from semantics)
33
+ */
34
+ instructions?: string;
35
+ }
36
+ /**
37
+ * Field-level AI config. `inputs` names the fields this instruction reads.
38
+ * Unknown ids and cycles are refused when the schema is parsed.
39
+ */
40
+ interface FieldAIConfig extends AIConfig {
41
+ /** Field ids this instruction reads. Never inferred from instruction text. */
42
+ inputs?: string[];
43
+ }
44
+ /**
45
+ * Asset-type AI configuration. Extends {@link AIConfig} with `autoGenerate`,
46
+ * the single switch that turns on automatic field generation after upload.
47
+ */
48
+ interface AssetAIConfig extends AIConfig {
49
+ /**
50
+ * Master switch for automatic, post-upload field generation. When `true`, the
51
+ * derived analysis agent fills every field that declares `ai.instructions`,
52
+ * applied as a review proposal. Only meaningful for image asset types in v1.
53
+ */
54
+ autoGenerate?: boolean;
55
+ }
56
+ interface BaseFieldConfig {
57
+ /** Whether the field is required */
58
+ required?: boolean;
59
+ /** Whether the field value must be unique */
60
+ unique?: boolean;
61
+ /** Default value for the field */
62
+ defaultValue?: unknown;
63
+ }
64
+ interface FieldUIConfig {
65
+ /** Display name shown in the UI */
66
+ displayName: string;
67
+ /** Field description/help text */
68
+ description?: string;
69
+ /** Widget-specific options */
70
+ options?: Record<string, unknown>;
71
+ }
72
+ interface RuleCondition {
73
+ field: string;
74
+ operator: 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'greater_than' | 'less_than' | 'is_empty' | 'is_not_empty';
75
+ value?: unknown;
76
+ }
77
+ interface RuleConditions {
78
+ mode: 'all' | 'any' | 'none';
79
+ conditions: RuleCondition[];
80
+ }
81
+ interface VisibilityRule {
82
+ type: 'visibility';
83
+ action: 'show' | 'hide';
84
+ conditions: RuleConditions;
85
+ }
86
+ interface RequirementRule {
87
+ type: 'requirement';
88
+ action: 'require' | 'optional';
89
+ conditions: RuleConditions;
90
+ }
91
+ interface ValidationRule {
92
+ type: 'validation';
93
+ action: 'validate' | 'skip';
94
+ message?: string;
95
+ conditions: RuleConditions;
96
+ }
97
+ interface ReadOnlyRule {
98
+ type: 'readOnly';
99
+ action: 'enable' | 'disable';
100
+ conditions: RuleConditions;
101
+ }
102
+ type FieldRule = VisibilityRule | RequirementRule | ValidationRule | ReadOnlyRule;
103
+ type LocalizationConfig = boolean | string[] | {
104
+ enabled: boolean;
105
+ requiredLocales?: string[];
106
+ fallback?: boolean;
107
+ };
108
+ interface FieldDefinition<TId extends string = string, TType extends string = string, TConfig = unknown> {
109
+ /** Unique field identifier - preserves literal type for type inference */
110
+ readonly id: TId;
111
+ /** Field type */
112
+ readonly type: TType;
113
+ /** Type-specific configuration */
114
+ config?: TConfig;
115
+ /** UI display configuration */
116
+ ui?: FieldUIConfig;
117
+ /** AI configuration: generation/review guidance for this field */
118
+ ai?: FieldAIConfig;
119
+ /** Dynamic field rules */
120
+ rules?: FieldRule[];
121
+ /** Localization settings */
122
+ localization?: LocalizationConfig;
123
+ /** Fallback value when field is empty */
124
+ fallback?: unknown;
125
+ /** Whether this field is deprecated */
126
+ deprecated?: boolean;
127
+ }
128
+ /**
129
+ * Any field definition - used in arrays where we need to preserve specific field types
130
+ */
131
+ type AnyFieldDefinition = FieldDefinition<string, string, unknown>;
132
+ interface ShortTextConfig extends BaseFieldConfig {
133
+ /** Maximum character length */
134
+ maxLength?: number;
135
+ /** Regex pattern for validation */
136
+ pattern?: string;
137
+ }
138
+ interface LongTextConfig extends BaseFieldConfig {
139
+ minLength?: number;
140
+ maxLength?: number;
141
+ }
142
+ interface RichTextConfig extends BaseFieldConfig {
143
+ /** Maximum character length for the rich text content */
144
+ maxLength?: number;
145
+ /** Allowed formatting options (e.g., bold, italic, underline) */
146
+ allowedFormats?: string[];
147
+ /** Allowed embed types (e.g., image, video, code) */
148
+ embedTypes?: string[];
149
+ }
150
+ interface SlugConfig extends BaseFieldConfig {
151
+ /** Source field to generate slug from */
152
+ sourceField?: string;
153
+ /** Maximum character length */
154
+ maxLength?: number;
155
+ }
156
+ interface UrlConfig extends BaseFieldConfig {
157
+ /** Allowed URL protocols */
158
+ allowedProtocols?: ('http' | 'https' | 'ftp' | 'ftps')[];
159
+ /** Maximum character length */
160
+ maxLength?: number;
161
+ }
162
+ interface EmailConfig extends BaseFieldConfig {
163
+ /** Maximum character length */
164
+ maxLength?: number;
165
+ /** Whitelist of allowed email domains */
166
+ domainWhitelist?: string[];
167
+ }
168
+ interface IntegerConfig extends BaseFieldConfig {
169
+ min?: number;
170
+ max?: number;
171
+ }
172
+ interface DecimalConfig extends BaseFieldConfig {
173
+ min?: number;
174
+ max?: number;
175
+ decimalPlaces?: number;
176
+ roundingMode?: 'HALF_UP' | 'FLOOR' | 'CEILING' | 'ROUND' | 'TRUNCATE';
177
+ }
178
+ interface BooleanConfig extends BaseFieldConfig {
179
+ trueLabel?: string;
180
+ falseLabel?: string;
181
+ }
182
+ /**
183
+ * `TValue` is the union of the option values and `TMulti` whether the field holds a list of them.
184
+ * `fields.enum()` infers both from the declaration, so a read of the field is typed by its options
185
+ * (`INV-SS-TYPES-INFERRED`); the defaults describe any enum field.
186
+ */
187
+ interface EnumConfig<TValue extends string = string, TMulti extends boolean = boolean> extends BaseFieldConfig {
188
+ options: Array<{
189
+ value: TValue;
190
+ label?: string;
191
+ }>;
192
+ multi?: TMulti;
193
+ }
194
+ interface DateConfig extends BaseFieldConfig {
195
+ minDate?: string;
196
+ maxDate?: string;
197
+ format?: string;
198
+ }
199
+ interface DateTimeConfig extends BaseFieldConfig {
200
+ minDateTime?: string;
201
+ maxDateTime?: string;
202
+ format?: string;
203
+ timezone?: boolean;
204
+ }
205
+ /**
206
+ * `TTo` is the union of the target type ids and `TMulti` whether the field holds a list.
207
+ * `fields.reference()` infers both, so a read of the field is typed by what it points at
208
+ * (`INV-SS-TYPES-INFERRED`); the defaults describe any reference field.
209
+ */
210
+ interface ReferenceConfig<TTo extends string = string, TMulti extends boolean = boolean> extends BaseFieldConfig {
211
+ /**
212
+ * Target entry type id(s) — content type or asset type ids. Pass a single id
213
+ * for one target or an array for several. All ids must belong to the same
214
+ * family (all content types or all asset types).
215
+ */
216
+ to: TTo | TTo[];
217
+ /** Allow multiple references (the field value is a list). */
218
+ multi?: TMulti;
219
+ /** Reference strength for publish/unpublish behavior. */
220
+ referenceStrength?: 'strong' | 'weak';
221
+ /** Behavior when a referenced entry is deleted. */
222
+ onTargetDelete?: 'block' | 'nullify';
223
+ }
224
+ interface TagsConfig extends BaseFieldConfig {
225
+ allowCustom?: boolean;
226
+ predefinedTags?: string[];
227
+ maxTags?: number;
228
+ }
229
+ /**
230
+ * `TSubfields` is the declared subfields, which `fields.list()` infers so a read types each item by
231
+ * them (`INV-SS-TYPES-INFERRED`); the default describes any list field.
232
+ */
233
+ interface ListConfig<TSubfields extends readonly AnyFieldDefinition[] = FieldDefinition[]> extends BaseFieldConfig {
234
+ dragAndDrop?: boolean;
235
+ minItems?: number;
236
+ maxItems?: number;
237
+ /** Subfields for list items */
238
+ subfields?: TSubfields;
239
+ }
240
+ type FieldType = 'shortText' | 'longText' | 'richText' | 'slug' | 'url' | 'email' | 'integer' | 'decimal' | 'boolean' | 'enum' | 'date' | 'dateTime' | 'reference' | 'tags' | 'list';
241
+ type ComponentFieldType = `component(${string})`;
242
+ type AllFieldTypes = FieldType | ComponentFieldType;
243
+
244
+ /**
245
+ * Workflow definition for content approval pipelines
246
+ *
247
+ * Workflows define approval stages that content must pass through before
248
+ * publication. They can be configured globally and per content type.
249
+ *
250
+ * @example
251
+ * // Simple workflow with one approval stage
252
+ * const workflow = defineWorkflow({
253
+ * enabled: true,
254
+ * stages: [
255
+ * defineWorkflowStage('review', {
256
+ * displayName: 'Editorial Review',
257
+ * allowedRoles: ['editor', 'admin']
258
+ * })
259
+ * ]
260
+ * })
261
+ *
262
+ * @example
263
+ * // Multi-stage workflow
264
+ * const workflow = defineWorkflow({
265
+ * enabled: true,
266
+ * stages: [
267
+ * defineWorkflowStage('editorial-review', {
268
+ * displayName: 'Editorial Review',
269
+ * allowedRoles: ['editor']
270
+ * }),
271
+ * defineWorkflowStage('legal-approval', {
272
+ * displayName: 'Legal Approval',
273
+ * requiredApprovals: 1,
274
+ * allowedRoles: ['legal-reviewer']
275
+ * }),
276
+ * defineWorkflowStage('final-approval', {
277
+ * displayName: 'Final Approval',
278
+ * requiredApprovals: 2,
279
+ * allowedRoles: ['publisher', 'admin']
280
+ * })
281
+ * ]
282
+ * })
283
+ */
284
+ /**
285
+ * Escalation configuration when a workflow stage times out
286
+ */
287
+ interface WorkflowEscalation {
288
+ /** Roles to notify when stage times out */
289
+ notifyRoles?: string[];
290
+ /** Roles to escalate to if still pending after another timeout period */
291
+ escalateToRoles?: string[];
292
+ }
293
+ /**
294
+ * Auto-assignment configuration for workflow stages
295
+ */
296
+ interface WorkflowAutoAssign {
297
+ /** Roles whose users should be notified when content enters this stage */
298
+ roles: string[];
299
+ }
300
+ /**
301
+ * Workflow stage configuration
302
+ */
303
+ interface WorkflowStageConfig {
304
+ /** Human-readable name shown in Studio */
305
+ displayName: string;
306
+ /** Optional description for reviewers */
307
+ description?: string;
308
+ /** Number of approvals needed to pass this stage (default: 1) */
309
+ requiredApprovals?: number;
310
+ /** Roles that can approve this stage */
311
+ allowedRoles?: string[];
312
+ /** Auto-assignment configuration */
313
+ autoAssign?: WorkflowAutoAssign;
314
+ /** Days before escalation triggers (optional) */
315
+ timeoutDays?: number;
316
+ /** Escalation configuration when stage times out */
317
+ escalation?: WorkflowEscalation;
318
+ }
319
+ /**
320
+ * Complete workflow stage definition
321
+ */
322
+ interface WorkflowStageDefinition {
323
+ readonly _tag: 'workflowStage';
324
+ readonly id: string;
325
+ readonly displayName: string;
326
+ readonly description?: string;
327
+ readonly requiredApprovals: number;
328
+ readonly allowedRoles?: string[];
329
+ readonly autoAssign?: WorkflowAutoAssign;
330
+ readonly timeoutDays?: number;
331
+ readonly escalation?: WorkflowEscalation;
332
+ }
333
+ /**
334
+ * Workflow configuration
335
+ */
336
+ interface WorkflowConfig {
337
+ /** Enable/disable workflow for the branch (default: false) */
338
+ enabled?: boolean;
339
+ /** Array of approval stage definitions */
340
+ stages?: WorkflowStageDefinition[];
341
+ }
342
+ /**
343
+ * Complete workflow definition
344
+ */
345
+ interface WorkflowDefinition {
346
+ readonly _tag: 'workflow';
347
+ readonly enabled: boolean;
348
+ readonly stages: WorkflowStageDefinition[];
349
+ }
350
+ /**
351
+ * Per-content-type workflow configuration
352
+ */
353
+ interface ContentTypeWorkflowConfig {
354
+ /** Enable workflow for this content type (inherits from global if not set) */
355
+ enabled?: boolean;
356
+ /** Which global stages apply to this content type (subset of global stages) */
357
+ stages?: string[];
358
+ /** Publish automatically after all stages are approved */
359
+ autoPublish?: boolean;
360
+ /** Require all locales to be approved before publishing */
361
+ requireAllLocales?: boolean;
362
+ }
363
+ /**
364
+ * Define a workflow stage
365
+ *
366
+ * @param id - Unique identifier for the stage (kebab-case or camelCase)
367
+ * @param config - Stage configuration
368
+ *
369
+ * @example
370
+ * const editorialReview = defineWorkflowStage('editorial-review', {
371
+ * displayName: 'Editorial Review',
372
+ * description: 'Content quality and style check',
373
+ * requiredApprovals: 1,
374
+ * allowedRoles: ['editor', 'senior-editor']
375
+ * })
376
+ *
377
+ * @example
378
+ * // Stage with auto-assignment and timeout
379
+ * const legalApproval = defineWorkflowStage('legal-approval', {
380
+ * displayName: 'Legal Approval',
381
+ * allowedRoles: ['legal-reviewer'],
382
+ * autoAssign: { roles: ['legal-reviewer'] },
383
+ * timeoutDays: 3,
384
+ * escalation: {
385
+ * notifyRoles: ['legal-reviewer'],
386
+ * escalateToRoles: ['admin']
387
+ * }
388
+ * })
389
+ */
390
+ declare function defineWorkflowStage(id: string, config: WorkflowStageConfig): WorkflowStageDefinition;
391
+ /**
392
+ * Define a workflow configuration
393
+ *
394
+ * @param config - Workflow configuration with enabled flag and stages
395
+ *
396
+ * @example
397
+ * // Simple workflow
398
+ * const workflow = defineWorkflow({
399
+ * enabled: true,
400
+ * stages: [
401
+ * defineWorkflowStage('review', {
402
+ * displayName: 'Review',
403
+ * allowedRoles: ['editor']
404
+ * })
405
+ * ]
406
+ * })
407
+ *
408
+ * @example
409
+ * // Disabled workflow (direct publish)
410
+ * const workflow = defineWorkflow({
411
+ * enabled: false
412
+ * })
413
+ */
414
+ declare function defineWorkflow(config: WorkflowConfig): WorkflowDefinition;
415
+ /**
416
+ * Define content-type specific workflow configuration
417
+ *
418
+ * @param config - Content type workflow configuration
419
+ *
420
+ * @example
421
+ * // Use specific stages from global workflow
422
+ * const blogPostWorkflow = defineContentTypeWorkflow({
423
+ * enabled: true,
424
+ * stages: ['editorial-review', 'final-approval'], // Skip legal
425
+ * autoPublish: false
426
+ * })
427
+ *
428
+ * @example
429
+ * // Disable workflow for this content type
430
+ * const quickUpdateWorkflow = defineContentTypeWorkflow({
431
+ * enabled: false
432
+ * })
433
+ *
434
+ * @example
435
+ * // Require all locales to be approved
436
+ * const marketingPageWorkflow = defineContentTypeWorkflow({
437
+ * enabled: true,
438
+ * stages: ['translation-review', 'regional-approval'],
439
+ * requireAllLocales: true
440
+ * })
441
+ */
442
+ declare function defineContentTypeWorkflow(config: ContentTypeWorkflowConfig): ContentTypeWorkflowConfig;
443
+ /**
444
+ * Check if a value is a workflow stage definition
445
+ */
446
+ declare function isWorkflowStageDefinition(value: unknown): value is WorkflowStageDefinition;
447
+ /**
448
+ * Check if a value is a workflow definition
449
+ */
450
+ declare function isWorkflowDefinition(value: unknown): value is WorkflowDefinition;
451
+ /**
452
+ * Get all stage IDs from a workflow definition
453
+ */
454
+ declare function getWorkflowStageIds(workflow: WorkflowDefinition): string[];
455
+ /**
456
+ * Find a stage by ID in a workflow definition
457
+ */
458
+ declare function findWorkflowStage(workflow: WorkflowDefinition, stageId: string): WorkflowStageDefinition | undefined;
459
+ /**
460
+ * Get the next stage after a given stage ID (null if last stage)
461
+ */
462
+ declare function getNextWorkflowStage(workflow: WorkflowDefinition, currentStageId: string): WorkflowStageDefinition | null;
463
+ /**
464
+ * Get the first stage in the workflow (null if no stages)
465
+ */
466
+ declare function getFirstWorkflowStage(workflow: WorkflowDefinition): WorkflowStageDefinition | null;
467
+ /**
468
+ * Check if a stage is the final stage in the workflow
469
+ */
470
+ declare function isFinalWorkflowStage(workflow: WorkflowDefinition, stageId: string): boolean;
471
+
472
+ /**
473
+ * Singleton content type definition matching @relyio/schema
474
+ *
475
+ * In RelyIO, singletons are just content types with a special flag.
476
+ * They share the same structure as regular content types.
477
+ *
478
+ * Uses advanced TypeScript generics to preserve literal field IDs through
479
+ * the type system, enabling full autocomplete in SDK methods.
480
+ */
481
+
482
+ /**
483
+ * Type alias for a fieldset with any fields.
484
+ */
485
+ type AnyFieldset$2 = Fieldset<any>;
486
+ /**
487
+ * Singleton content type definition - preserves field types for inference
488
+ */
489
+ interface SingletonContentTypeDefinition<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[], TFieldsets extends readonly AnyFieldset$2[] = readonly AnyFieldset$2[]> {
490
+ readonly _tag: 'singletonContentType';
491
+ readonly name: string;
492
+ readonly displayName: string;
493
+ readonly displayNamePlural?: string;
494
+ readonly description?: string;
495
+ /** AI configuration: generation guidance + entry intelligence */
496
+ readonly ai?: AIConfig;
497
+ /** Field ID to use as entry display title */
498
+ readonly displayField?: string;
499
+ readonly hierarchical: false;
500
+ readonly accessTags?: string[];
501
+ readonly fields?: TFields;
502
+ readonly fieldsets?: TFieldsets;
503
+ }
504
+ /**
505
+ * Define a singleton content type
506
+ *
507
+ * Singletons are single-instance content types like homepage, site settings, etc.
508
+ * They use the same field structure as regular content types.
509
+ *
510
+ * The generic type parameters preserve the literal field IDs through the type
511
+ * system, enabling autocomplete in SDK methods.
512
+ *
513
+ * @example
514
+ * const homepage = defineSingletonContentType('homepage', {
515
+ * displayName: 'Homepage',
516
+ * fields: [
517
+ * fields.shortText('heroTitle', { config: { required: true } }),
518
+ * fields.image('heroImage'),
519
+ * ]
520
+ * })
521
+ */
522
+ declare function defineSingletonContentType<TFields extends readonly AnyFieldDefinition[], _TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: ContentTypeConfigWithFields<TFields>): SingletonContentTypeDefinition<TFields, readonly []>;
523
+ declare function defineSingletonContentType<_TFields extends readonly AnyFieldDefinition[], TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: ContentTypeConfigWithFieldsets<TFieldsets>): SingletonContentTypeDefinition<readonly [], TFieldsets>;
524
+ declare const defineSingleton: typeof defineSingletonContentType;
525
+ /**
526
+ * Singleton metadata.
527
+ *
528
+ * The per-locale publication half is inherited from `LocalePublicationFields` rather than absent.
529
+ * `apps/services/api` reports `localeStatus` / `localeHasDraft` / `draftLocales` on singleton reads
530
+ * exactly as it does on entry reads (`INV-LM-PUBLICATION-IS-REPORTED-PER-LOCALE`); this type simply
531
+ * never said so, which told a customer that a singleton has no publication state to read.
532
+ */
533
+ interface SingletonMetadata extends LocalePublicationFields {
534
+ /** Singleton key identifier */
535
+ key: string;
536
+ /** Singleton type identifier */
537
+ singletonType: string;
538
+ /** Content version */
539
+ version: number;
540
+ /** Content locale */
541
+ locale: string;
542
+ /** Creation timestamp */
543
+ createdAt: Date;
544
+ /** Last update timestamp */
545
+ updatedAt: Date;
546
+ }
547
+ /**
548
+ * Type alias for any singleton content type definition.
549
+ */
550
+ type AnySingletonContentTypeDefinition = SingletonContentTypeDefinition<any, any>;
551
+ /**
552
+ * Infer the TypeScript type from a singleton definition: every field typed as a read returns it
553
+ * (`schema/infer-fields.ts`), plus `_metadata`. `S` is the schema, as for `InferContentType`.
554
+ */
555
+ type InferSingletonContentType<T extends AnySingletonContentTypeDefinition, S extends FieldSchemaContext | undefined = undefined> = FieldValues<DeclaredFields<T>, S> & {
556
+ _metadata: SingletonMetadata;
557
+ };
558
+ type InferSingleton<T extends AnySingletonContentTypeDefinition, S extends FieldSchemaContext | undefined = undefined> = InferSingletonContentType<T, S>;
559
+
560
+ /**
561
+ * Asset type definition matching @relyio/schema AssetTypeSchema
562
+ *
563
+ * Asset types are first-class entries (entryType: 'asset') that pair a binary
564
+ * pipeline family (declared via exactly one kind key: `image`, `video`, or
565
+ * `file`) with user-defined metadata fields that reuse the standard field
566
+ * system — including fieldsets, workflow, access tags, and AI config.
567
+ *
568
+ * The kind config blocks are strongly typed below (mirroring the zod-inferred
569
+ * `@relyio/schema` types), so authoring asset types in TypeScript gets full
570
+ * autocomplete. The authoritative validation still lives in @relyio/schema's
571
+ * `AssetTypesValidator` and runs on deploy.
572
+ */
573
+
574
+ /** Resize fit modes supported by the Cloudflare Images binding. */
575
+ type ImageFitMode = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
576
+ /** Output formats the Cloudflare Images binding can produce (`heic` is input-only). */
577
+ type ImageOutputFormat = 'avif' | 'webp' | 'jpeg' | 'png';
578
+ /** Shared image transform props (CF Images vocabulary). */
579
+ interface ImageTransform {
580
+ width?: number;
581
+ height?: number;
582
+ fit?: ImageFitMode;
583
+ format?: ImageOutputFormat;
584
+ quality?: number;
585
+ }
586
+ /** Upload gate for the `image` kind (rejects bad files at the door). */
587
+ interface ImageUploadConfig {
588
+ allowedMimeTypes?: string[];
589
+ maxFileSize?: number;
590
+ minWidth?: number;
591
+ maxWidth?: number;
592
+ minHeight?: number;
593
+ maxHeight?: number;
594
+ /** `width:height`, e.g. `16:9`. */
595
+ aspectRatio?: string;
596
+ }
597
+ /** Upload gate for the `video` kind. */
598
+ interface VideoUploadConfig {
599
+ allowedMimeTypes?: string[];
600
+ maxFileSize?: number;
601
+ maxDuration?: number;
602
+ minWidth?: number;
603
+ maxWidth?: number;
604
+ minHeight?: number;
605
+ maxHeight?: number;
606
+ }
607
+ /** Upload gate for the `file` kind (MIME + size only in v1). */
608
+ interface FileUploadConfig {
609
+ allowedMimeTypes?: string[];
610
+ maxFileSize?: number;
611
+ }
612
+ /** Delivery rendition preset (max 20 per asset type), applied on the fly by the gateway. */
613
+ interface VariantDefinition extends ImageTransform {
614
+ /** Variant id; must start with a letter (codegen emits the `*Variant` union from these). */
615
+ id: string;
616
+ }
617
+ /**
618
+ * Ingest normalization of the canonical stored original (image v1 only).
619
+ * `true` expands to the platform default on deploy.
620
+ */
621
+ type ImageSourceConfig = boolean | (ImageTransform & {
622
+ stripMetadata?: boolean;
623
+ });
624
+ /** EXIF retention policy for the `image` kind (plan 15). */
625
+ interface ImageExifConfig {
626
+ /** Retain GPS in `extracted.exif` (default strips it for privacy). */
627
+ keepGps?: boolean;
628
+ }
629
+ /** Image kind config (`upload`, `source`, `variants`, `localization`, `exif`). */
630
+ interface ImageKindConfig {
631
+ upload?: ImageUploadConfig;
632
+ source?: ImageSourceConfig;
633
+ /** Max 20 variants per asset type. */
634
+ variants?: VariantDefinition[];
635
+ localization?: LocalizationConfig;
636
+ exif?: ImageExifConfig;
637
+ }
638
+ /** Poster/thumbnail strategy for the `video` kind. */
639
+ interface VideoPosterConfig {
640
+ /** 0.0–1.0 → Stream thumbnailTimestampPct. */
641
+ defaultTimestampPct?: number;
642
+ /** Enable an animated preview (gif/webp). */
643
+ animated?: boolean;
644
+ }
645
+ /** Subtitle track strategy for the `video` kind. */
646
+ interface VideoCaptionsConfig {
647
+ enabled?: boolean;
648
+ requiredLocales?: string[];
649
+ /** Stream AI transcription when no track is uploaded. */
650
+ autoGenerate?: boolean;
651
+ }
652
+ /** Downloadable-MP4 toggle for the `video` kind. */
653
+ interface VideoDownloadConfig {
654
+ enabled?: boolean;
655
+ }
656
+ /** Stream watermark profile for the `video` kind. */
657
+ interface VideoWatermarkConfig {
658
+ enabled?: boolean;
659
+ /** Account-level Stream watermark profile uid. */
660
+ profileId?: string;
661
+ }
662
+ /**
663
+ * Video kind config (`upload`, `poster`, `captions`, `download`, `watermark`,
664
+ * `localization`). Stream auto-generates the ABR ladder — there is intentionally
665
+ * NO `variants` and NO `encoding` block.
666
+ */
667
+ interface VideoKindConfig {
668
+ upload?: VideoUploadConfig;
669
+ poster?: VideoPosterConfig;
670
+ captions?: VideoCaptionsConfig;
671
+ download?: VideoDownloadConfig;
672
+ watermark?: VideoWatermarkConfig;
673
+ localization?: LocalizationConfig;
674
+ }
675
+ /** Malware scan config for the `file` kind (plan 19). */
676
+ interface FileScanConfig {
677
+ enabled?: boolean;
678
+ blockUntilClean?: boolean;
679
+ }
680
+ /** File kind config (`upload`, `localization`, `scan`, `extractText`). */
681
+ interface FileKindConfig {
682
+ upload?: FileUploadConfig;
683
+ localization?: LocalizationConfig;
684
+ /** Malware scan (plan 19). */
685
+ scan?: FileScanConfig;
686
+ /** Extract searchable text + metadata for PDFs (plan 15). */
687
+ extractText?: boolean;
688
+ }
689
+ interface AssetTypeBaseConfig {
690
+ /** Display name shown in the UI */
691
+ displayName: string;
692
+ /** Plural display name (e.g., "Product Photos" for "Product Photo") */
693
+ displayNamePlural?: string;
694
+ /** Description of the asset type */
695
+ description?: string;
696
+ /**
697
+ * AI configuration. `ai.instructions` is an optional global brief; per-field
698
+ * `ai.instructions` describe individual fields. `ai.autoGenerate` (image
699
+ * kind, v1) is the master switch: when true, the derived analysis agent runs
700
+ * after upload and fills every field that declares `ai.instructions`, applied
701
+ * as a review proposal.
702
+ */
703
+ ai?: AssetAIConfig;
704
+ /** Field ID to use as the asset display title */
705
+ displayField?: string;
706
+ /** Access tags for role-based access control */
707
+ accessTags?: string[];
708
+ /** Workflow configuration for this asset type */
709
+ workflow?: ContentTypeWorkflowConfig;
710
+ /** Image pipeline config — mutually exclusive with `video`/`file` */
711
+ image?: ImageKindConfig;
712
+ /** Video pipeline config — mutually exclusive with `image`/`file` */
713
+ video?: VideoKindConfig;
714
+ /** File pipeline config — mutually exclusive with `image`/`video` */
715
+ file?: FileKindConfig;
716
+ }
717
+ /**
718
+ * Type alias for a fieldset with any fields.
719
+ */
720
+ type AnyFieldset$1 = Fieldset<any>;
721
+ /** Asset type configuration using direct fields. */
722
+ interface AssetTypeConfigWithFields<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[]> extends AssetTypeBaseConfig {
723
+ /** Direct field definitions - preserves literal types */
724
+ fields: TFields;
725
+ /** Cannot have fieldsets when using fields */
726
+ fieldsets?: never;
727
+ }
728
+ /** Asset type configuration using fieldsets. */
729
+ interface AssetTypeConfigWithFieldsets<TFieldsets extends readonly AnyFieldset$1[] = readonly AnyFieldset$1[]> extends AssetTypeBaseConfig {
730
+ /** Field organization via fieldsets - preserves literal types */
731
+ fieldsets: TFieldsets;
732
+ /** Cannot have direct fields when using fieldsets */
733
+ fields?: never;
734
+ }
735
+ /** Asset type configuration (either fields OR fieldsets). */
736
+ type AssetTypeConfig<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[], TFieldsets extends readonly AnyFieldset$1[] = readonly AnyFieldset$1[]> = AssetTypeConfigWithFields<TFields> | AssetTypeConfigWithFieldsets<TFieldsets>;
737
+ /** Asset type definition - preserves field types for inference. */
738
+ interface AssetTypeDefinition<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[], TFieldsets extends readonly AnyFieldset$1[] = readonly AnyFieldset$1[]> {
739
+ readonly _tag: 'assetType';
740
+ readonly name: string;
741
+ readonly displayName: string;
742
+ readonly displayNamePlural?: string;
743
+ readonly description?: string;
744
+ readonly ai?: AssetAIConfig;
745
+ readonly displayField?: string;
746
+ readonly accessTags?: string[];
747
+ readonly workflow?: ContentTypeWorkflowConfig;
748
+ readonly image?: ImageKindConfig;
749
+ readonly video?: VideoKindConfig;
750
+ readonly file?: FileKindConfig;
751
+ readonly fields?: TFields;
752
+ readonly fieldsets?: TFieldsets;
753
+ }
754
+ /**
755
+ * Define an asset type.
756
+ *
757
+ * Exactly one kind key (`image`, `video`, or `file`) must be supplied. The
758
+ * generic type parameters preserve literal field IDs for inference, mirroring
759
+ * `defineContentType`.
760
+ *
761
+ * @example
762
+ * const productPhoto = defineAssetType('productPhoto', {
763
+ * displayName: 'Product Photo',
764
+ * image: {
765
+ * upload: { allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'], maxFileSize: 10485760 },
766
+ * variants: [{ id: 'thumb', width: 200, height: 200, fit: 'cover', format: 'webp' }],
767
+ * },
768
+ * fields: [
769
+ * fields.shortText('altText', { ui: { displayName: 'Alt Text' } }),
770
+ * ],
771
+ * })
772
+ */
773
+ declare function defineAssetType<TFields extends readonly AnyFieldDefinition[], _TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: AssetTypeConfigWithFields<TFields>): AssetTypeDefinition<TFields, readonly []>;
774
+ declare function defineAssetType<_TFields extends readonly AnyFieldDefinition[], TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: AssetTypeConfigWithFieldsets<TFieldsets>): AssetTypeDefinition<readonly [], TFieldsets>;
775
+ /**
776
+ * Type alias for any asset type definition.
777
+ */
778
+ type AnyAssetTypeDefinition = AssetTypeDefinition<any, any>;
779
+ /**
780
+ * Infer the TypeScript type from an asset type definition.
781
+ *
782
+ * Mirrors {@link InferContentType}: produces the (optional) metadata field shape
783
+ * plus `_metadata`, so it is assignable to `BaseContent` and can parametrize
784
+ * `AssetClient<T>`. The runtime `file`/`urls` blocks are layered on by the
785
+ * client's `AssetEntryResponse<T>` return type, not here, to keep the schema
786
+ * layer free of any dependency on the client layer.
787
+ */
788
+ type InferAssetType<T extends AnyAssetTypeDefinition, S extends FieldSchemaContext | undefined = undefined> = FieldValues<DeclaredFields<T>, S> & {
789
+ _metadata: EntryMetadata;
790
+ };
791
+
792
+ /**
793
+ * What a read returns for each field, derived from the field's declaration (`INV-SS-TYPES-INFERRED`).
794
+ *
795
+ * Every `Infer*` in this package — content types, singletons, asset types, settings groups — types
796
+ * its fields here and nowhere else, so an entry, a referenced entry, a component and a list item are
797
+ * typed by one table. The types describe **what a read returns**, which is not always what a write
798
+ * accepts:
799
+ *
800
+ * - `richText` is the stored HTML string; `date` is `YYYY-MM-DD` and `dateTime` an ISO 8601 string —
801
+ * the client does not revive dates.
802
+ * - A `reference` is the target's id until `include` names it, and then the target entry itself.
803
+ * The type cannot see `include`, so it is both: `string | <entry>`. A single reference whose target
804
+ * was deleted with `onTargetDelete: 'nullify'` is `null`.
805
+ * - A localized field is the value for the one locale the read composed, like any other field; the
806
+ * other locales of a `locales=` read sit under `_metadata.localizations`, not here.
807
+ * - Every field is optional. The wire omits a field with no value — a draft, a `fields` projection, a
808
+ * locale with nothing to fall back to, an entry older than a field — so `required` does not make
809
+ * one present.
810
+ */
811
+
812
+ /**
813
+ * The parts of a schema a field's value type reads: the types a reference may expand to and the
814
+ * components a component field names. Structural, so every `defineSchema()` result fits.
815
+ *
816
+ * Without one in view — `InferContentType<typeof post>` with no second argument — a reference
817
+ * expands to an {@link UnresolvedEntry} and a component to an {@link UnresolvedComponent}: the field
818
+ * is still typed, but what it names is not.
819
+ */
820
+ interface FieldSchemaContext {
821
+ readonly contentTypes: object;
822
+ readonly components: object;
823
+ readonly singletonContentTypes: object;
824
+ readonly assetTypes?: object;
825
+ }
826
+ /** A schema in view, or `undefined` when none is. */
827
+ type Context = FieldSchemaContext | undefined;
828
+ /** An expanded reference whose target type is not in view: an entry, with fields nothing declared. */
829
+ interface UnresolvedEntry {
830
+ _metadata: EntryMetadata;
831
+ [field: string]: unknown;
832
+ }
833
+ /** A component whose definition is not in view: an object, with fields nothing declared. */
834
+ type UnresolvedComponent = Record<string, unknown>;
835
+ /**
836
+ * One row per field type the schema declares. `OverEveryFieldType` makes the table exhaustive by
837
+ * construction: a field type added to `FieldType` without a row here fails to compile, rather than
838
+ * reading as `unknown`.
839
+ */
840
+ type OverEveryFieldType<T extends {
841
+ [K in FieldType]: unknown;
842
+ }> = T;
843
+ type FieldValueByType<C, S extends Context> = OverEveryFieldType<{
844
+ shortText: string;
845
+ longText: string;
846
+ /** The stored HTML. */
847
+ richText: string;
848
+ email: string;
849
+ url: string;
850
+ slug: string;
851
+ integer: number;
852
+ decimal: number;
853
+ boolean: boolean;
854
+ enum: EnumValue<C>;
855
+ /** `YYYY-MM-DD`. */
856
+ date: string;
857
+ /** ISO 8601. */
858
+ dateTime: string;
859
+ reference: ReferenceValue<C, S>;
860
+ tags: string[];
861
+ list: ListValue<C, S>;
862
+ }>;
863
+ /**
864
+ * The value a read returns for field `F`.
865
+ *
866
+ * The last arm is the one `unknown` in this module, and it is deliberate: a field whose `type` is not
867
+ * a declared literal — a definition built from a plain `string` rather than through `fields.*` — has
868
+ * no row to read.
869
+ */
870
+ type FieldValue<F extends AnyFieldDefinition, S extends Context = undefined> = F['type'] extends `component(${infer Id})` ? ComponentValue<Id, S> : F['type'] extends FieldType ? FieldValueByType<NonNullable<F['config']>, S>[F['type']] : unknown;
871
+ /** The fields `F` (a union of field definitions) as an object, each optional — see the module note. */
872
+ type FieldValues<F extends AnyFieldDefinition, S extends Context = undefined> = {
873
+ [K in F['id']]?: FieldValue<Extract<F, {
874
+ readonly id: K;
875
+ }>, S>;
876
+ };
877
+ /**
878
+ * Every field a definition declares, directly or through its fieldsets, as one union — the one walk
879
+ * over `fields` / `fieldsets` every `Infer*` shares.
880
+ */
881
+ type DeclaredFields<T extends {
882
+ readonly fields?: unknown;
883
+ readonly fieldsets?: unknown;
884
+ }> = Extract<(NonNullable<T['fields']> extends readonly (infer F)[] ? F : never) | (NonNullable<T['fieldsets']> extends readonly (infer FS)[] ? FS extends {
885
+ readonly fields: readonly (infer F)[];
886
+ } ? F : never : never), AnyFieldDefinition>;
887
+ /** `multi` as declared: `true`, `false`, or `boolean` when the declaration does not say. */
888
+ type Multiplicity<C> = C extends {
889
+ readonly multi?: infer M;
890
+ } ? M : false;
891
+ /** One `V`, a list of them, or — when `multi` is not a literal — either. */
892
+ type OneOrMany<C, V> = [Multiplicity<C>] extends [true] ? V[] : [Multiplicity<C>] extends [false | undefined] ? V : V | V[];
893
+ type EnumValue<C> = OneOrMany<C, C extends {
894
+ readonly options: ReadonlyArray<{
895
+ readonly value: infer V;
896
+ }>;
897
+ } ? V : string>;
898
+ type ReferenceTarget<C> = C extends {
899
+ readonly to: infer T;
900
+ } ? T extends readonly (infer E)[] ? E : T : string;
901
+ /** `R[K]` where `R` declares `K`. A `Record<string, never>` default declares nothing. */
902
+ type Declared<R, K> = K extends keyof R ? R[K] : never;
903
+ type ContentEntryOf<D, S extends Context> = D extends ContentTypeDefinition ? InferContentType<D, S> : never;
904
+ type SingletonEntryOf<D, S extends Context> = D extends SingletonContentTypeDefinition ? InferSingletonContentType<D, S> : never;
905
+ type AssetEntryOf<D, S extends Context> = D extends AssetTypeDefinition ? InferAssetType<D, S> : never;
906
+ type OrUnresolved<T> = [T] extends [never] ? UnresolvedEntry : T;
907
+ /** What `include` expands a reference to `Target` into: that type's entry, typed by the schema. */
908
+ type ExpandedEntry<Target, S extends Context> = S extends FieldSchemaContext ? OrUnresolved<ContentEntryOf<Declared<S['contentTypes'], Target>, S> | SingletonEntryOf<Declared<S['singletonContentTypes'], Target>, S> | AssetEntryOf<Declared<NonNullable<S['assetTypes']>, Target>, S>> : UnresolvedEntry;
909
+ type ReferenceValue<C, S extends Context> = [Multiplicity<C>] extends [true] ? Array<string | ExpandedEntry<ReferenceTarget<C>, S>> : [Multiplicity<C>] extends [false | undefined] ? string | null | ExpandedEntry<ReferenceTarget<C>, S> : string | null | Array<string | ExpandedEntry<ReferenceTarget<C>, S>> | ExpandedEntry<ReferenceTarget<C>, S>;
910
+ type ComponentFields<D, S extends Context> = [D] extends [never] ? UnresolvedComponent : D extends {
911
+ readonly fields: readonly (infer F)[];
912
+ } ? FieldValues<Extract<F, AnyFieldDefinition>, S> : UnresolvedComponent;
913
+ type ComponentValue<Id extends string, S extends Context> = S extends FieldSchemaContext ? ComponentFields<Declared<S['components'], Id>, S> : UnresolvedComponent;
914
+ /**
915
+ * A list with subfields is a list of objects typed by them; a list without is a list of strings,
916
+ * which is what the validation document enforces for it.
917
+ */
918
+ type ListValue<C, S extends Context> = C extends {
919
+ readonly subfields?: infer Sub;
920
+ } ? Sub extends readonly (infer F)[] ? [F] extends [never] ? string[] : Array<FieldValues<Extract<F, AnyFieldDefinition>, S>> : string[] : string[];
921
+
922
+ /**
923
+ * Content type definition matching @relyio/schema ContentTypeSchema
924
+ *
925
+ * Uses advanced TypeScript generics to preserve each field's declaration through the type system,
926
+ * so `InferContentType` types every field by what a read returns and `where()` autocompletes.
927
+ */
928
+
929
+ /**
930
+ * Fieldset for organizing related fields
931
+ * Generic over fields to preserve literal field IDs
932
+ */
933
+ interface Fieldset<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[]> {
934
+ /** Unique fieldset identifier */
935
+ readonly id: string;
936
+ /** Display name shown in the UI */
937
+ readonly displayName: string;
938
+ /** Optional description */
939
+ readonly description?: string;
940
+ /** AI configuration for content generation and review */
941
+ readonly ai?: AIConfig;
942
+ /** Fields within this fieldset - preserves literal types */
943
+ readonly fields: TFields;
944
+ }
945
+ /**
946
+ * Type alias for a fieldset with any fields.
947
+ * Used in generic constraints where we need to accept any fieldset type.
948
+ */
949
+ type AnyFieldset = Fieldset<any>;
950
+ /**
951
+ * Create a fieldset for organizing fields
952
+ * Generic to preserve the specific field types for type inference
953
+ *
954
+ * @example
955
+ * const seoFieldset = fieldset('seo', {
956
+ * displayName: 'SEO Settings',
957
+ * fields: [
958
+ * fields.shortText('metaTitle', { config: { maxLength: 60 } }),
959
+ * fields.longText('metaDescription', { config: { maxLength: 160 } }),
960
+ * ]
961
+ * })
962
+ */
963
+ declare function fieldset<TFields extends readonly AnyFieldDefinition[]>(id: string, config: {
964
+ displayName: string;
965
+ description?: string;
966
+ ai?: AIConfig;
967
+ fields: TFields;
968
+ }): Fieldset<TFields>;
969
+ /**
970
+ * Content type configuration using direct fields
971
+ * Generic to preserve literal field IDs for type inference
972
+ */
973
+ interface ContentTypeConfigWithFields<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[]> {
974
+ /** Display name shown in the UI */
975
+ displayName: string;
976
+ /** Plural display name shown in the UI (e.g., "Products" for "Product") */
977
+ displayNamePlural?: string;
978
+ /** Description of the content type */
979
+ description?: string;
980
+ /** AI configuration: generation/review guidance */
981
+ ai?: AIConfig;
982
+ /**
983
+ * Field ID to use as entry display title in lists, references, and editor header.
984
+ * If not set, falls back to: title → name → slug → first shortText field → entryUid
985
+ */
986
+ displayField?: string;
987
+ /** Whether this content type supports hierarchical (parent-child) relationships */
988
+ hierarchical?: boolean;
989
+ /** Access tags for role-based access control */
990
+ accessTags?: string[];
991
+ /** Workflow configuration for this content type */
992
+ workflow?: ContentTypeWorkflowConfig;
993
+ /** Direct field definitions - preserves literal types */
994
+ fields: TFields;
995
+ /** Cannot have fieldsets when using fields */
996
+ fieldsets?: never;
997
+ }
998
+ /**
999
+ * Content type configuration using fieldsets
1000
+ * Generic to preserve fieldset field types
1001
+ */
1002
+ interface ContentTypeConfigWithFieldsets<TFieldsets extends readonly AnyFieldset[] = readonly AnyFieldset[]> {
1003
+ /** Display name shown in the UI */
1004
+ displayName: string;
1005
+ /** Plural display name shown in the UI (e.g., "Products" for "Product") */
1006
+ displayNamePlural?: string;
1007
+ /** Description of the content type */
1008
+ description?: string;
1009
+ /** AI configuration: generation/review guidance */
1010
+ ai?: AIConfig;
1011
+ /**
1012
+ * Field ID to use as entry display title in lists, references, and editor header.
1013
+ * If not set, falls back to: title → name → slug → first shortText field → entryUid
1014
+ */
1015
+ displayField?: string;
1016
+ /** Access tags for role-based access control */
1017
+ accessTags?: string[];
1018
+ /** Whether this content type supports hierarchical (parent-child) relationships */
1019
+ hierarchical?: boolean;
1020
+ /** Workflow configuration for this content type */
1021
+ workflow?: ContentTypeWorkflowConfig;
1022
+ /** Field organization via fieldsets - preserves literal types */
1023
+ fieldsets: TFieldsets;
1024
+ /** Cannot have direct fields when using fieldsets */
1025
+ fields?: never;
1026
+ }
1027
+ /**
1028
+ * Content type configuration (either fields OR fieldsets, not both)
1029
+ */
1030
+ type ContentTypeConfig<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[], TFieldsets extends readonly AnyFieldset[] = readonly AnyFieldset[]> = ContentTypeConfigWithFields<TFields> | ContentTypeConfigWithFieldsets<TFieldsets>;
1031
+ /**
1032
+ * Content type definition - preserves field types for inference
1033
+ */
1034
+ interface ContentTypeDefinition<TFields extends readonly AnyFieldDefinition[] = readonly AnyFieldDefinition[], TFieldsets extends readonly AnyFieldset[] = readonly AnyFieldset[]> {
1035
+ readonly _tag: 'contentType';
1036
+ readonly name: string;
1037
+ readonly displayName: string;
1038
+ readonly displayNamePlural?: string;
1039
+ readonly description?: string;
1040
+ /** AI configuration: generation/review guidance */
1041
+ readonly ai?: AIConfig;
1042
+ /** Field ID to use as entry display title */
1043
+ readonly displayField?: string;
1044
+ readonly hierarchical: boolean;
1045
+ readonly accessTags?: string[];
1046
+ readonly workflow?: ContentTypeWorkflowConfig;
1047
+ readonly fields?: TFields;
1048
+ readonly fieldsets?: TFieldsets;
1049
+ }
1050
+ /**
1051
+ * Define a content type with type-safe fields
1052
+ *
1053
+ * The generic type parameters preserve the literal field IDs through the type
1054
+ * system, enabling autocomplete in SDK methods like where(), select(), etc.
1055
+ *
1056
+ * @example
1057
+ * // Using direct fields
1058
+ * const product = defineContentType('product', {
1059
+ * displayName: 'Product',
1060
+ * fields: [
1061
+ * fields.shortText('title', { config: { required: true } }),
1062
+ * fields.currency('price', { config: { currencyCode: 'USD' } }),
1063
+ * ]
1064
+ * })
1065
+ *
1066
+ * @example
1067
+ * // Using fieldsets for organization
1068
+ * const product = defineContentType('product', {
1069
+ * displayName: 'Product',
1070
+ * fieldsets: [
1071
+ * fieldset('basic', {
1072
+ * displayName: 'Basic Info',
1073
+ * fields: [
1074
+ * fields.shortText('title', { config: { required: true } }),
1075
+ * ]
1076
+ * }),
1077
+ * fieldset('pricing', {
1078
+ * displayName: 'Pricing',
1079
+ * fields: [
1080
+ * fields.currency('price', { config: { currencyCode: 'USD' } }),
1081
+ * ]
1082
+ * }),
1083
+ * ]
1084
+ * })
1085
+ */
1086
+ declare function defineContentType<TFields extends readonly AnyFieldDefinition[], _TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: ContentTypeConfigWithFields<TFields>): ContentTypeDefinition<TFields, readonly []>;
1087
+ declare function defineContentType<_TFields extends readonly AnyFieldDefinition[], TFieldsets extends readonly Fieldset<AnyFieldDefinition[]>[]>(name: string, config: ContentTypeConfigWithFieldsets<TFieldsets>): ContentTypeDefinition<readonly [], TFieldsets>;
1088
+ /**
1089
+ * Type alias for any content type definition.
1090
+ */
1091
+ type AnyContentTypeDefinition = ContentTypeDefinition<any, any>;
1092
+ /**
1093
+ * The per-locale publication half of an entry's metadata, declared once for this package.
1094
+ *
1095
+ * This is a **mirror** of `LocalePublicationFacts` in `@relyio/content-db`, the return type of the
1096
+ * one derivation that produces these values server-side
1097
+ * (`INV-LM-PUBLICATION-IS-REPORTED-PER-LOCALE`). It is declared again here rather than imported
1098
+ * because this package is published to npm with exactly one runtime dependency and is built for
1099
+ * browsers; taking a dependency on the content database's document layer to reach three field names
1100
+ * is not a trade this SDK can make.
1101
+ *
1102
+ * Every field is **optional**, and that is deliberate rather than lax: a published client is always
1103
+ * older than the service it talks to, so it must read a body that predates a field it knows about.
1104
+ * Server-side the same three fields are required — `EntryMetadataResponse` in `apps/services/api`
1105
+ * declares them so and `implements LocalePublicationFacts` proves it.
1106
+ */
1107
+ interface LocalePublicationFields {
1108
+ /** Publication state of `locale`; absent when that locale has no content of its own. */
1109
+ localeStatus?: 'draft' | 'published' | 'archived';
1110
+ /** Whether `locale` has unpublished changes of its own. */
1111
+ localeHasDraft?: boolean;
1112
+ /** Every locale with unpublished changes, including shared-field drafts. */
1113
+ draftLocales?: string[];
1114
+ }
1115
+ /**
1116
+ * Entry metadata that's present on all content entries.
1117
+ *
1118
+ * The per-locale publication half is inherited from `LocalePublicationFields` rather than restated,
1119
+ * so this package holds one copy of it and not one per file.
1120
+ */
1121
+ interface EntryMetadata extends LocalePublicationFields {
1122
+ /** Unique entry identifier */
1123
+ id: string;
1124
+ /** Content type id (API field name is `type`) */
1125
+ type: string;
1126
+ /** Entry type (content, asset, singleton, settings) */
1127
+ entryType: string;
1128
+ /**
1129
+ * Container lifecycle only (`active` | `archived` | `proposed`).
1130
+ * Publication is `localeStatus` (`INV-LM-STATUS-IS-TWO-FIELDS`).
1131
+ */
1132
+ status: 'active' | 'archived' | 'proposed';
1133
+ /** Per-locale version number */
1134
+ version: number;
1135
+ /** Parent ID for hierarchical content */
1136
+ parentId?: string | null;
1137
+ /** Content locale this payload is reported in */
1138
+ locale: string;
1139
+ /** Whether the shared (non-localized) fields have an unpublished overlay. */
1140
+ hasDraft?: boolean;
1141
+ /** Creation timestamp */
1142
+ createdAt: Date;
1143
+ /** Last update timestamp */
1144
+ updatedAt: Date;
1145
+ }
1146
+ /**
1147
+ * Infer the TypeScript type of an entry from a content type definition: every field it declares,
1148
+ * directly or through fieldsets, typed as a read returns it (`INV-SS-TYPES-INFERRED`), plus
1149
+ * `_metadata`. Each field is optional — `schema/infer-fields.ts` says why, and what each field type
1150
+ * reads as.
1151
+ *
1152
+ * `S` is the schema the type belongs to. With it, a reference expanded by `include` is typed as the
1153
+ * entry it points at and a component field by the component's fields; without it, those two are
1154
+ * typed as an entry or an object whose fields are not known. A client typed by a schema passes it.
1155
+ *
1156
+ * @example
1157
+ * const product = defineContentType('product', {
1158
+ * displayName: 'Product',
1159
+ * fields: [
1160
+ * fields.shortText('title'),
1161
+ * fields.decimal('price'),
1162
+ * fields.enum('status', { config: { options: [{ value: 'draft' }, { value: 'live' }] } }),
1163
+ * ]
1164
+ * })
1165
+ *
1166
+ * type Product = InferContentType<typeof product>
1167
+ * // { title?: string; price?: number; status?: 'draft' | 'live'; _metadata: EntryMetadata }
1168
+ */
1169
+ type InferContentType<T extends AnyContentTypeDefinition, S extends FieldSchemaContext | undefined = undefined> = FieldValues<DeclaredFields<T>, S> & {
1170
+ _metadata: EntryMetadata;
1171
+ };
1172
+
1173
+ export { type InferSingletonContentType as $, type AnyFieldDefinition as A, type BooleanConfig as B, type ComponentFieldType as C, type DecimalConfig as D, type EmailConfig as E, type FieldUIConfig as F, defineContentType as G, fieldset as H, type IntegerConfig as I, type Fieldset as J, type ContentTypeConfig as K, type LocalizationConfig as L, type ContentTypeConfigWithFields as M, type ContentTypeConfigWithFieldsets as N, type ContentTypeDefinition as O, type EntryMetadata as P, type InferContentType as Q, type RichTextConfig as R, type ShortTextConfig as S, type TagsConfig as T, type UrlConfig as U, type ValidationRule as V, type LocalePublicationFields as W, defineSingletonContentType as X, defineSingleton as Y, type SingletonContentTypeDefinition as Z, type SingletonMetadata as _, type FieldAIConfig as a, type InferSingleton as a0, defineAssetType as a1, type AssetTypeConfig as a2, type AssetTypeConfigWithFields as a3, type AssetTypeConfigWithFieldsets as a4, type AssetTypeDefinition as a5, type InferAssetType as a6, type ImageKindConfig as a7, type VideoKindConfig as a8, type FileKindConfig as a9, type WorkflowStageConfig as aA, type WorkflowStageDefinition as aB, type WorkflowConfig as aC, type WorkflowDefinition as aD, type ContentTypeWorkflowConfig as aE, type ImageTransform as aa, type ImageFitMode as ab, type ImageOutputFormat as ac, type ImageUploadConfig as ad, type ImageSourceConfig as ae, type ImageExifConfig as af, type VariantDefinition as ag, type VideoUploadConfig as ah, type VideoPosterConfig as ai, type VideoCaptionsConfig as aj, type VideoDownloadConfig as ak, type VideoWatermarkConfig as al, type FileUploadConfig as am, type FileScanConfig as an, defineWorkflow as ao, defineWorkflowStage as ap, defineContentTypeWorkflow as aq, isWorkflowDefinition as ar, isWorkflowStageDefinition as as, getWorkflowStageIds as at, findWorkflowStage as au, getNextWorkflowStage as av, getFirstWorkflowStage as aw, isFinalWorkflowStage as ax, type WorkflowEscalation as ay, type WorkflowAutoAssign as az, type FieldRule as b, type FieldDefinition as c, type LongTextConfig as d, type SlugConfig as e, type EnumConfig as f, type DateConfig as g, type DateTimeConfig as h, type ReferenceConfig as i, type ListConfig as j, type AIConfig as k, type AllFieldTypes as l, type AssetAIConfig as m, type BaseFieldConfig as n, type FieldType as o, type ReadOnlyRule as p, type RequirementRule as q, type RuleCondition as r, type RuleConditions as s, type VisibilityRule as t, type FieldSchemaContext as u, type FieldValue as v, type FieldValues as w, type DeclaredFields as x, type UnresolvedEntry as y, type UnresolvedComponent as z };