@relyio/sdk 0.13.0 → 0.15.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 (36) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/dist/{chunk-DEDIJKHY.cjs → chunk-3BHDGUNS.cjs} +1554 -3250
  3. package/dist/{chunk-3NPPMGB6.js → chunk-5UMY6CS4.js} +2 -2
  4. package/dist/{chunk-EW23SZPX.js → chunk-PCFTJLEP.js} +540 -262
  5. package/dist/{chunk-6NGJXLOR.js → chunk-PXA5ONIU.js} +1553 -3240
  6. package/dist/{chunk-Z6GLWSSD.cjs → chunk-RSHMA4KT.cjs} +2 -2
  7. package/dist/{chunk-FOSM4LMM.cjs → chunk-X4KSS7PP.cjs} +539 -262
  8. package/dist/client/index.cjs +3 -3
  9. package/dist/client/index.d.cts +3 -3
  10. package/dist/client/index.d.ts +3 -3
  11. package/dist/client/index.js +1 -1
  12. package/dist/{include-paths-Bs5FhNw2.d.cts → define-content-type-B2gQZkTy.d.cts} +3697 -2585
  13. package/dist/{include-paths-Cb7iPCW2.d.ts → define-content-type-Ds5rCAYl.d.ts} +3697 -2585
  14. package/dist/{define-schema-sWJ8Zn7C.d.cts → define-schema-Cm_5y3ei.d.cts} +238 -356
  15. package/dist/{define-schema-UHViXKiv.d.ts → define-schema-CxpIYFFR.d.ts} +238 -356
  16. package/dist/index-DQ7eTUPQ.d.ts +3090 -0
  17. package/dist/{index--_u1nPHv.d.cts → index-DY64WjZM.d.cts} +2 -2
  18. package/dist/{index-8DozAWsA.d.ts → index-Db3_tmYf.d.ts} +2 -2
  19. package/dist/index-F0UuHpRj.d.cts +3090 -0
  20. package/dist/index.cjs +90 -130
  21. package/dist/index.d.cts +16 -23
  22. package/dist/index.d.ts +16 -23
  23. package/dist/index.js +4 -4
  24. package/dist/rich-text/index.cjs +11 -11
  25. package/dist/rich-text/index.d.cts +2 -2
  26. package/dist/rich-text/index.d.ts +2 -2
  27. package/dist/rich-text/index.js +1 -1
  28. package/dist/schema/index.cjs +33 -37
  29. package/dist/schema/index.d.cts +33 -18
  30. package/dist/schema/index.d.ts +33 -18
  31. package/dist/schema/index.js +1 -1
  32. package/dist/types/index.d.cts +1 -1
  33. package/dist/types/index.d.ts +1 -1
  34. package/package.json +2 -2
  35. package/dist/index-BT0L8xJj.d.cts +0 -3717
  36. package/dist/index-CPjJ25R4.d.ts +0 -3717
@@ -1,4 +1,4 @@
1
- import { ao as ContentTypeDefinition, av as SingletonContentTypeDefinition, aB as SettingsGroupDefinition, be as WorkflowDefinition, bs as ViewDefinition, aG as AssetTypeDefinition, aq as InferContentType } from './include-paths-Bs5FhNw2.cjs';
1
+ import { aL as ContentTypeDefinition, aR as SingletonContentTypeDefinition, aW as SettingsGroupDefinition, aC as LocaleDefinition, bw as RoleDefinition, bP as WorkflowDefinition, c5 as ViewDefinition, a$ as AssetTypeDefinition, aN as InferContentType, bz as KeyedRoles, c8 as KeyedViews, ce as CheckedLocales, cf as CheckedRoles, cg as CheckedWorkflow, ch as CheckedSection, ci as KeysDeclared, cj as LocaleCodesOf, ck as StagesDeclared, bA as KeyedRolesAsList, c9 as KeyedViewsAsList } from './define-content-type-B2gQZkTy.cjs';
2
2
 
3
3
  /**
4
4
  * Query definition matching @relyio/schema QueryDefinitionSchema
@@ -16,61 +16,78 @@ import { ao as ContentTypeDefinition, av as SingletonContentTypeDefinition, aB a
16
16
  /** One value a query clause compares with. */
17
17
  type QueryScalar = string | number | boolean;
18
18
  /**
19
- * The operators `where` and `filters` compare a field with — `{ price: { gte: 10 } }`.
19
+ * A window back or forward from the moment of the read — `{ amount: 7, unit: 'days' }` — which
20
+ * `$inTheLast` and `$inTheNext` take, as a list's `where` takes them.
21
+ */
22
+ type QueryRelativeWindow = {
23
+ amount: number;
24
+ unit: 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years';
25
+ };
26
+ /**
27
+ * The operators `where` and `filters` compare a field with — `{ price: { $gte: 10 } }` — the
28
+ * `$`-prefixed operators a list's `where` takes, meaning what they mean there (REL-2439).
20
29
  *
21
30
  * The service's one vocabulary for `where`, `filters` and `having`, which deploy refuses any other
22
- * key against (REL-1479). There were four copies, and this one's `where` had no `eq` while `filters`
23
- * took six operators, each a `${…}` string. A value may be a parameter expression: a list operator
24
- * takes a list or a parameter that supplies one, a flag a boolean or a parameter.
31
+ * key against (REL-1479). The bare form (`{ price: { gte: 10 } }`) is gone: deploy refuses it,
32
+ * saying what to write. A value may be a parameter expression: a list operator takes a list or a
33
+ * parameter that supplies one, a flag a boolean or a parameter. A parameter only ever fills a value;
34
+ * which operators a condition asks is written here, never supplied.
25
35
  * `@relyio/codegen-typescript`'s `sdk-definition-config.types.ts` fails to compile when an operator,
26
36
  * or what it takes, differs from the schema's.
27
37
  */
28
38
  type WhereOperator = {
29
39
  /** Equal to */
30
- eq?: QueryScalar;
40
+ $eq?: QueryScalar;
31
41
  /** Not equal to */
32
- ne?: QueryScalar;
42
+ $ne?: QueryScalar;
33
43
  /** Greater than */
34
- gt?: QueryScalar;
44
+ $gt?: QueryScalar;
35
45
  /** Greater than or equal to */
36
- gte?: QueryScalar;
46
+ $gte?: QueryScalar;
37
47
  /** Less than */
38
- lt?: QueryScalar;
48
+ $lt?: QueryScalar;
39
49
  /** Less than or equal to */
40
- lte?: QueryScalar;
50
+ $lte?: QueryScalar;
41
51
  /** One of the values given */
42
- in?: QueryScalar[] | string;
52
+ $in?: QueryScalar[] | string;
43
53
  /** None of the values given */
44
- nin?: QueryScalar[] | string;
54
+ $nin?: QueryScalar[] | string;
45
55
  /** Contains the text, ignoring case */
46
- contains?: QueryScalar;
47
- /** Starts with the text, ignoring case */
48
- startsWith?: QueryScalar;
49
- /** Ends with the text, ignoring case */
50
- endsWith?: QueryScalar;
56
+ $contains?: QueryScalar;
57
+ /** Starts with the text, matching case */
58
+ $startsWith?: QueryScalar;
59
+ /** Ends with the text, matching case */
60
+ $endsWith?: QueryScalar;
51
61
  /** The field holds a value (true) or does not (false) */
52
- exists?: boolean | string;
53
- /** The field holds no value (true) or does (false) */
54
- notExists?: boolean | string;
62
+ $exists?: boolean | string;
63
+ /** The value is empty (true) or not (false) */
64
+ $isNull?: boolean | string;
65
+ /** A date within the span back from the moment of the read */
66
+ $inTheLast?: QueryRelativeWindow;
67
+ /** A date within the span forward from the moment of the read */
68
+ $inTheNext?: QueryRelativeWindow;
55
69
  };
56
70
  /**
57
71
  * The operators `having` compares a metric with: the same as `where`'s, each with a literal, since
58
- * `having` reads the values the aggregate computed and substitutes no parameter.
72
+ * `having` reads the values the aggregate computed and substitutes no parameter. A metric holds no
73
+ * date of an entry's, so neither relative window is taken.
59
74
  */
60
75
  type HavingOperator = {
61
- eq?: QueryScalar;
62
- ne?: QueryScalar;
63
- gt?: QueryScalar;
64
- gte?: QueryScalar;
65
- lt?: QueryScalar;
66
- lte?: QueryScalar;
67
- in?: QueryScalar[];
68
- nin?: QueryScalar[];
69
- contains?: QueryScalar;
70
- startsWith?: QueryScalar;
71
- endsWith?: QueryScalar;
72
- exists?: boolean;
73
- notExists?: boolean;
76
+ $eq?: QueryScalar;
77
+ $ne?: QueryScalar;
78
+ $gt?: QueryScalar;
79
+ $gte?: QueryScalar;
80
+ $lt?: QueryScalar;
81
+ $lte?: QueryScalar;
82
+ $in?: QueryScalar[];
83
+ $nin?: QueryScalar[];
84
+ $contains?: QueryScalar;
85
+ $startsWith?: QueryScalar;
86
+ $endsWith?: QueryScalar;
87
+ $exists?: boolean;
88
+ $isNull?: boolean;
89
+ $inTheLast?: never;
90
+ $inTheNext?: never;
74
91
  };
75
92
  type WhereValue = string | number | boolean | (string | number | boolean)[] | WhereOperator;
76
93
  type WhereCondition = Record<string, WhereValue>;
@@ -284,12 +301,26 @@ interface QueryConfig {
284
301
  /** Whether the response is the record itself or a list. Defaults to `many` */
285
302
  cardinality?: QueryCardinality;
286
303
  }
304
+ /** A value a query parameter takes, as the query string carries it to the query. */
305
+ type QueryParameterValue = QueryScalar | readonly QueryScalar[];
306
+ /** What a query whose parameters no type states accepts: any name, any value. */
307
+ type AnyQueryParameters = {
308
+ readonly [name: string]: QueryParameterValue | undefined;
309
+ };
310
+ /** Type only: where a keyed definition keeps the parameters it declares (`InferQueryParams`). */
311
+ declare const parametersAs: unique symbol;
287
312
  /**
288
313
  * Query definition
314
+ *
315
+ * `TParameters` is what the query accepts, read from the definition as written (REL-2421); a
316
+ * definition typed by nothing more than this interface accepts any.
289
317
  */
290
- interface QueryDefinition {
318
+ interface QueryDefinition<TParameters extends object = AnyQueryParameters> {
319
+ /** Type only: never present at runtime. */
320
+ readonly [parametersAs]?: TParameters;
291
321
  readonly _tag: 'query';
292
- readonly name: string;
322
+ /** Written from its key by `defineSchema` */
323
+ readonly name?: string;
293
324
  readonly type?: QueryType;
294
325
  readonly endpoint: string;
295
326
  readonly description?: string;
@@ -305,10 +336,23 @@ interface QueryDefinition {
305
336
  readonly cardinality?: QueryCardinality;
306
337
  }
307
338
  /**
308
- * Define a predefined query
339
+ * Define a predefined query (REL-1969): it takes no name, because its key under `defineSchema`'s
340
+ * `queries` is its id. Its `where` is the one a list takes, its operators `$`-prefixed (REL-2439).
341
+ *
342
+ * @example
343
+ * defineSchema({
344
+ * contentTypes: { product },
345
+ * queries: {
346
+ * featuredProducts: defineQuery({
347
+ * endpoint: '/api/featured-products',
348
+ * fetch: { contentType: 'product', where: { featured: true }, limit: 10 },
349
+ * }),
350
+ * },
351
+ * })
309
352
  *
310
353
  * @example
311
- * const featuredProducts = defineQuery('featuredProducts', {
354
+ * // queries: { featuredProducts }
355
+ * const featuredProducts = defineQuery({
312
356
  * endpoint: '/api/featured-products',
313
357
  * description: 'Get featured products for homepage',
314
358
  * fetch: {
@@ -329,7 +373,8 @@ interface QueryDefinition {
329
373
  * })
330
374
  *
331
375
  * @example
332
- * const searchProducts = defineQuery('searchProducts', {
376
+ * // queries: { searchProducts }
377
+ * const searchProducts = defineQuery({
333
378
  * endpoint: '/api/search',
334
379
  * description: 'Search products with semantic understanding',
335
380
  * search: {
@@ -340,14 +385,15 @@ interface QueryDefinition {
340
385
  * limit: 20,
341
386
  * },
342
387
  * filters: {
343
- * 'price.base': { gte: '${minPrice?0}', lte: '${maxPrice?}' },
388
+ * 'price.base': { $gte: '${minPrice?0}', $lte: '${maxPrice?}' },
344
389
  * category: '${categoryId?}',
345
390
  * },
346
391
  * cache: 'public 60s',
347
392
  * })
348
393
  *
349
394
  * @example
350
- * const productStats = defineQuery('productStats', {
395
+ * // queries: { productStats }
396
+ * const productStats = defineQuery({
351
397
  * endpoint: '/api/products/stats',
352
398
  * description: 'Get product statistics by category',
353
399
  * aggregate: {
@@ -365,299 +411,105 @@ interface QueryDefinition {
365
411
  * cache: 'public 300s',
366
412
  * })
367
413
  */
368
- declare function defineQuery(name: string, config: QueryConfig): QueryDefinition;
414
+ declare function defineQuery<const TConfig extends QueryConfig>(config: TConfig): QueryDefinition<DeclaredParameters<TConfig>>;
369
415
  /**
370
416
  * Infer basic response type from a query definition
371
417
  * Note: For accurate types, use the codegen tool
372
418
  */
373
419
  type InferQueryResponse<_T extends QueryDefinition> = unknown[];
374
420
  /**
375
- * Infer parameters from a query definition
376
- * Note: For accurate types, use the codegen tool
377
- */
378
- type InferQueryParams<_T extends QueryDefinition> = Record<string, unknown>;
379
-
380
- /**
381
- * Locale definition for multi-language support
382
- *
383
- * Locales define the languages and regions available for content
384
- * within your RelyIO project. Each locale can have:
385
- * - A fallback chain for missing content
386
- * - RTL text direction support
387
- * - Active/inactive status for gradual rollouts
388
- *
389
- * @example
390
- * import { defineLocale, defineSchema } from '@relyio/sdk/schema'
391
- *
392
- * const enUS = defineLocale('en-US', {
393
- * displayName: 'English (US)',
394
- * default: true,
395
- * })
396
- *
397
- * const esES = defineLocale('es-ES', {
398
- * displayName: 'Spanish (Spain)',
399
- * fallback: ['en-US'],
400
- * })
401
- *
402
- * const arSA = defineLocale('ar-SA', {
403
- * displayName: 'Arabic (Saudi Arabia)',
404
- * direction: 'rtl',
405
- * fallback: ['en-US'],
406
- * })
407
- *
408
- * export const schema = defineSchema({
409
- * locales: [enUS, esES, arSA],
410
- * contentTypes: { ... },
411
- * })
412
- */
413
- /**
414
- * Text direction for RTL language support
415
- */
416
- type TextDirection = 'ltr' | 'rtl';
417
- /**
418
- * Options for defining a locale
419
- */
420
- interface LocaleOptions {
421
- /** Human-readable name shown in the Studio UI */
422
- displayName: string;
423
- /**
424
- * Marks the default locale (exactly one required per schema)
425
- * The default locale serves as the ultimate fallback for all other locales
426
- */
427
- default?: boolean;
428
- /**
429
- * Ordered fallback chain when content is missing in this locale
430
- * Locales are tried in order until content is found
431
- *
432
- * @example
433
- * // Spanish Mexico falls back to Spain Spanish, then US English
434
- * fallback: ['es-ES', 'en-US']
435
- */
436
- fallback?: string[];
437
- /**
438
- * Text direction for RTL languages like Arabic, Hebrew
439
- * @default 'ltr'
440
- */
441
- direction?: TextDirection;
442
- /**
443
- * Enable/disable locale without removing translations
444
- * Useful for:
445
- * - Testing new locales before enabling them
446
- * - Temporarily disabling locales for compliance review
447
- * - Gradual market rollouts
448
- *
449
- * Note: Inactive locales can still serve as fallback targets
450
- * @default true
451
- */
452
- active?: boolean;
453
- }
454
- /**
455
- * Locale definition with type tag for SDK type inference
456
- */
457
- interface LocaleDefinition {
458
- readonly _tag: 'locale';
459
- readonly code: string;
460
- readonly displayName: string;
461
- readonly default?: boolean;
462
- readonly fallback?: readonly string[];
463
- readonly direction?: TextDirection;
464
- readonly active?: boolean;
465
- }
466
- /**
467
- * Define a locale for multi-language support
468
- *
469
- * Creates a locale definition that can be included in your schema.
470
- * Locales control:
471
- * - What languages/regions are available for content
472
- * - Fallback behavior when content is missing
473
- * - Text direction for RTL languages
474
- *
475
- * @param code BCP 47 locale code (e.g., 'en-US', 'fr-FR', 'ar-SA')
476
- * @param options Locale configuration
477
- * @returns Locale definition for use in defineSchema()
478
- *
479
- * @example
480
- * // Basic locale
481
- * const enUS = defineLocale('en-US', {
482
- * displayName: 'English (US)',
483
- * default: true,
484
- * })
485
- *
486
- * @example
487
- * // Locale with fallback chain
488
- * const esMX = defineLocale('es-MX', {
489
- * displayName: 'Spanish (Mexico)',
490
- * fallback: ['es-ES', 'en-US'],
491
- * })
492
- *
493
- * @example
494
- * // RTL language
495
- * const arSA = defineLocale('ar-SA', {
496
- * displayName: 'Arabic (Saudi Arabia)',
497
- * direction: 'rtl',
498
- * fallback: ['en-US'],
499
- * })
500
- *
501
- * @example
502
- * // Inactive locale for testing
503
- * const jaJP = defineLocale('ja-JP', {
504
- * displayName: 'Japanese',
505
- * fallback: ['en-US'],
506
- * active: false, // Hidden from Studio UI, but translations preserved
507
- * })
508
- */
509
- declare function defineLocale(code: string, options: LocaleOptions): LocaleDefinition;
510
- /**
511
- * Type guard to check if a value is a LocaleDefinition
512
- */
513
- declare function isLocaleDefinition(value: unknown): value is LocaleDefinition;
514
- /**
515
- * Extract locale codes from an array of locale definitions
516
- */
517
- declare function getLocaleCodes(locales: readonly LocaleDefinition[]): string[];
518
- /**
519
- * Find the default locale from an array of locale definitions
520
- */
521
- declare function findDefaultLocale(locales: readonly LocaleDefinition[]): LocaleDefinition | undefined;
522
- /**
523
- * Get active locales (excludes inactive ones)
524
- */
525
- declare function getActiveLocales(locales: readonly LocaleDefinition[]): LocaleDefinition[];
526
- /**
527
- * Build the complete fallback chain for a locale, resolving transitive fallbacks
528
- *
529
- * @param localeCode The locale to build the fallback chain for
530
- * @param locales All defined locales
531
- * @param maxDepth Maximum depth to prevent infinite loops (default: 10)
532
- * @returns Ordered array of locale codes to try as fallbacks
533
- *
534
- * @example
535
- * // Given: es-MX → es-ES → en-US (default)
536
- * buildFallbackChain('es-MX', locales)
537
- * // Returns: ['es-ES', 'en-US']
538
- */
539
- declare function buildFallbackChain(localeCode: string, locales: readonly LocaleDefinition[], maxDepth?: number): string[];
540
-
541
- /**
542
- * Role definition for fine-grained access control
543
- *
544
- * Roles define what users can do within a project. They can:
545
- * - Inherit from built-in roles (admin, editor, viewer)
546
- * - Compose from other roles using 'includes'
547
- * - Define granular permissions by access tags
548
- * - Set field visibility (hidden/readonly)
549
- */
550
- /**
551
- * Built-in roles that are always available
552
- */
553
- declare const BUILT_IN_ROLES: readonly ["admin", "editor", "viewer"];
554
- type BuiltInRole = (typeof BUILT_IN_ROLES)[number];
555
- /**
556
- * All available content actions
557
- */
558
- declare const ROLE_ACTIONS: readonly ["create", "read", "update", "delete", "publish", "unpublish", "duplicate", "comment", "approveElevated"];
559
- type RoleAction = (typeof ROLE_ACTIONS)[number];
560
- /**
561
- * Default actions for each built-in role
562
- */
563
- declare const BUILT_IN_ROLE_ACTIONS: Record<BuiltInRole, readonly RoleAction[]>;
564
- /**
565
- * Resource permission rule - defines what actions are allowed for specific tags
566
- */
567
- interface ResourcePermission {
568
- /** Access tags this permission applies to */
569
- tags: string[];
570
- /** Actions allowed for resources with these tags */
571
- actions: RoleAction[];
572
- }
573
- /**
574
- * Field visibility permissions
575
- */
576
- interface FieldVisibility {
577
- /** Fields that are hidden from this role (cannot see) */
578
- hidden?: string[];
579
- /** Fields that are readonly for this role (can see but not edit) */
580
- readonly?: string[];
581
- }
582
- /**
583
- * Role configuration
584
- */
585
- interface RoleConfig {
586
- /** Human-readable name shown in Studio */
587
- displayName: string;
588
- /** Optional description for administrators */
589
- description?: string;
590
- /** Inherit from a built-in role (admin, editor, viewer) */
591
- baseRole?: BuiltInRole;
592
- /** Compose from other roles (built-in or custom) */
593
- includes?: string[];
594
- /** Resource permission rules */
595
- permissions?: ResourcePermission[];
596
- /** Field visibility settings */
597
- fields?: FieldVisibility;
598
- }
599
- /**
600
- * Complete role definition
601
- */
602
- interface RoleDefinition {
603
- readonly _tag: 'role';
604
- readonly id: string;
605
- readonly displayName: string;
606
- readonly description?: string;
607
- readonly baseRole?: BuiltInRole;
608
- readonly includes?: string[];
609
- readonly permissions?: ResourcePermission[];
610
- readonly fields?: FieldVisibility;
421
+ * The parameters a query accepts, by name (REL-2421): what `client.query(name, { params })` takes.
422
+ *
423
+ * Read from the keyed definition as written, as the API's `declareQueryParameters` reads the
424
+ * deployed one: each `{name}` segment of the endpoint (required — the route cannot match without
425
+ * it), each `${name}` in the definition's values (required) or `${name?…}` (optional), and each
426
+ * entry of `parameters`, whose `type` types the value and whose `required` decides. A name the
427
+ * query does not declare is not a parameter, so it does not compile.
428
+ */
429
+ type InferQueryParams<T extends QueryDefinition<object>> = T extends QueryDefinition<infer P> ? P : AnyQueryParameters;
430
+ /** Every string value in `T`, at any depth — values only: a key is a field path, never a parameter. */
431
+ type StringsIn<T> = T extends string ? T : T extends readonly (infer Item)[] ? StringsIn<Item> : T extends object ? {
432
+ [K in keyof T]-?: StringsIn<T[K]>;
433
+ }[keyof T] : never;
434
+ /** The `${…}` expressions in one string, each as its name and whether it is optional. */
435
+ type ExpressionsIn<S extends string> = S extends `${string}\${${infer Body}}${infer Rest}` ? (Body extends `${infer Name}?${string}` ? {
436
+ readonly name: Name;
437
+ readonly optional: true;
438
+ } : {
439
+ readonly name: Body;
440
+ readonly optional: false;
441
+ }) | ExpressionsIn<Rest> : never;
442
+ /** The `{name}` segments of an endpoint. */
443
+ type EndpointNames<E> = E extends `${string}{${infer Name}}${infer Rest}` ? Name | EndpointNames<Rest> : never;
444
+ /** The expressions of a definition: its values but `parameters` and `description`, as the API scans. */
445
+ type Expressions<C> = ExpressionsIn<StringsIn<Omit<C, 'parameters' | 'description'>>>;
446
+ /** The `parameters` entries a definition declares. */
447
+ type Declared<C> = C extends {
448
+ readonly parameters: readonly (infer Entry)[];
449
+ } ? Entry : never;
450
+ /** What a declared `type` makes a value. */
451
+ type ValueOfType<T> = T extends 'string' ? string : T extends 'number' | 'integer' ? number : T extends 'boolean' ? boolean : T extends 'array' ? readonly QueryScalar[] : T extends 'object' ? never : QueryParameterValue;
452
+ /** What a definition declares, read once: its endpoint's segments, its expressions, its entries. */
453
+ type Declaration<C> = {
454
+ readonly segments: EndpointNames<C extends {
455
+ readonly endpoint: infer E;
456
+ } ? E : never>;
457
+ readonly expressions: Expressions<C>;
458
+ readonly entries: Declared<C>;
459
+ };
460
+ /** What any declaration is. */
461
+ interface AnyDeclaration {
462
+ readonly segments: string;
463
+ readonly expressions: {
464
+ readonly name: string;
465
+ readonly optional: boolean;
466
+ };
467
+ readonly entries: unknown;
611
468
  }
612
- /**
613
- * Define a custom role
614
- *
615
- * @example
616
- * // Inherit from built-in role
617
- * const blogEditor = defineRole('blog-editor', {
618
- * displayName: 'Blog Editor',
619
- * baseRole: 'editor',
620
- * permissions: [
621
- * { tags: ['blog'], actions: ['create', 'read', 'update', 'publish'] }
622
- * ]
623
- * })
624
- *
625
- * @example
626
- * // Compose from other roles
627
- * const seniorEditor = defineRole('senior-editor', {
628
- * displayName: 'Senior Editor',
629
- * includes: ['blog-editor', 'news-editor'],
630
- * permissions: [
631
- * { tags: ['featured'], actions: ['publish', 'unpublish'] }
632
- * ]
633
- * })
634
- *
635
- * @example
636
- * // Full permissions control
637
- * const moderator = defineRole('moderator', {
638
- * displayName: 'Content Moderator',
639
- * permissions: [
640
- * { tags: ['comments'], actions: ['read', 'delete'] }
641
- * ],
642
- * fields: {
643
- * hidden: ['internalNotes'],
644
- * readonly: ['authorId']
645
- * }
646
- * })
647
- */
648
- declare function defineRole(id: string, config: RoleConfig): RoleDefinition;
649
- /**
650
- * Check if a value is a role definition
651
- */
652
- declare function isRoleDefinition(value: unknown): value is RoleDefinition;
653
- /**
654
- * Check if a role ID is a built-in role
655
- */
656
- declare function isBuiltInRole(roleId: string): roleId is BuiltInRole;
657
- /**
658
- * Get the default actions for a built-in role
659
- */
660
- declare function getBuiltInRoleActions(roleId: BuiltInRole): readonly RoleAction[];
469
+ /** Every name a declaration holds. */
470
+ type NamesOf<D extends AnyDeclaration> = D['segments'] | NameOf<D['expressions']> | NameOf<D['entries']>;
471
+ /**
472
+ * The names a union of expressions or entries holds. Distributive on purpose: none is `never`,
473
+ * which a bare `extends` would match and answer the constraint, `string`, for.
474
+ */
475
+ type NameOf<E> = E extends {
476
+ readonly name: infer N extends string;
477
+ } ? N : never;
478
+ /** Whether `Name` is used bare (`${name}`) anywhere — which makes it required. */
479
+ type UsedBare<D extends AnyDeclaration, Name> = [
480
+ Extract<D['expressions'], {
481
+ readonly name: Name;
482
+ readonly optional: false;
483
+ }>
484
+ ] extends [never] ? false : true;
485
+ /** The `parameters` entry naming `Name`, or `never` where none does. */
486
+ type EntryFor<D extends AnyDeclaration, Name> = Extract<D['entries'], {
487
+ readonly name: Name;
488
+ }>;
489
+ /**
490
+ * Whether `Name` is required: an endpoint segment always; else its entry's `required`; else an entry
491
+ * that says nothing about it is required unless some use says it is optional and none is bare; else
492
+ * any bare use. No entry is asked about first: `never` matches every shape.
493
+ */
494
+ type IsRequired<D extends AnyDeclaration, Name> = [Name] extends [D['segments']] ? true : [EntryFor<D, Name>] extends [never] ? UsedBare<D, Name> : [EntryFor<D, Name>] extends [{
495
+ readonly required: infer R extends boolean;
496
+ }] ? R : [Extract<D['expressions'], {
497
+ readonly name: Name;
498
+ }>] extends [never] ? true : UsedBare<D, Name>;
499
+ /** The value `Name` takes: its entry's declared `type`, else any value a parameter takes. */
500
+ type ParameterValue<D extends AnyDeclaration, Name> = [EntryFor<D, Name>] extends [never] ? QueryParameterValue : [EntryFor<D, Name>] extends [{
501
+ readonly type: infer T;
502
+ }] ? ValueOfType<T> : QueryParameterValue;
503
+ type Simplify<T> = {
504
+ [K in keyof T]: T[K];
505
+ } & {};
506
+ /** The parameters a keyed definition declares: required ones required, the rest optional. */
507
+ type DeclaredParameters<C> = ParametersOf<Declaration<C>>;
508
+ type ParametersOf<D extends AnyDeclaration> = Simplify<{
509
+ readonly [Name in NamesOf<D> as IsRequired<D, Name> extends true ? Name : never]: ParameterValue<D, Name>;
510
+ } & {
511
+ readonly [Name in NamesOf<D> as IsRequired<D, Name> extends true ? never : Name]?: ParameterValue<D, Name>;
512
+ }>;
661
513
 
662
514
  /**
663
515
  * Schema definition matching @relyio/schema SchemaDefinitionSchema
@@ -674,7 +526,7 @@ declare function getBuiltInRoleActions(roleId: BuiltInRole): readonly RoleAction
674
526
  * - singletonContentTypes: Required record of singleton definitions (can be empty {})
675
527
  * - queries: Required record of query definitions (can be empty {})
676
528
  * - settingsGroups: Optional record of settings groups
677
- * - roles: Optional array of custom role definitions for access control
529
+ * - roles: Optional record of custom roles, each keyed by its id
678
530
  * - workflow: Optional workflow configuration with approval stages
679
531
  *
680
532
  * Note: `version` is auto-generated by the backend, not user-defined!
@@ -692,12 +544,12 @@ interface SchemaConfig {
692
544
  settingsGroups?: Record<string, SettingsGroupDefinition>;
693
545
  /** Asset type definitions (optional) */
694
546
  assetTypes?: Record<string, AssetTypeDefinition>;
695
- /** Custom role definitions for fine-grained access control (optional) */
696
- roles?: RoleDefinition[];
547
+ /** Custom roles for fine-grained access control, each under its id (optional) */
548
+ roles?: KeyedRoles;
697
549
  /** Global workflow configuration with approval stages (optional) */
698
550
  workflow?: WorkflowDefinition;
699
- /** View definitions for pre-configured entry list views (optional) */
700
- views?: ViewDefinition[];
551
+ /** Pre-configured entry list views, each under its id (optional) */
552
+ views?: KeyedViews;
701
553
  }
702
554
  /**
703
555
  * Complete schema definition - Generic version for type inference
@@ -717,7 +569,7 @@ interface SchemaDefinition<TContentTypes extends Record<string, ContentTypeDefin
717
569
  /**
718
570
  * Typed schema config for type inference
719
571
  */
720
- interface TypedSchemaConfig<TContentTypes extends Record<string, ContentTypeDefinition>, TSingletons extends Record<string, SingletonContentTypeDefinition>, TQueries extends Record<string, QueryDefinition>, TSettings extends Record<string, SettingsGroupDefinition>, TLocales extends readonly LocaleDefinition[] = readonly LocaleDefinition[], TRoles extends readonly RoleDefinition[] = readonly RoleDefinition[], TWorkflow extends WorkflowDefinition | undefined = WorkflowDefinition | undefined, TViews extends readonly ViewDefinition[] = readonly ViewDefinition[], TAssets extends Record<string, AssetTypeDefinition> = Record<string, AssetTypeDefinition>> {
572
+ interface TypedSchemaConfig<TContentTypes extends Record<string, ContentTypeDefinition>, TSingletons extends Record<string, SingletonContentTypeDefinition>, TQueries extends Record<string, QueryDefinition>, TSettings extends Record<string, SettingsGroupDefinition>, TLocales extends readonly LocaleDefinition[] = readonly LocaleDefinition[], TRoles extends KeyedRoles = KeyedRoles, TWorkflow extends WorkflowDefinition | undefined = WorkflowDefinition | undefined, TViews extends KeyedViews = KeyedViews, TAssets extends Record<string, AssetTypeDefinition> = Record<string, AssetTypeDefinition>> {
721
573
  locales?: TLocales;
722
574
  contentTypes: TContentTypes;
723
575
  singletonContentTypes?: TSingletons;
@@ -736,30 +588,26 @@ interface TypedSchemaConfig<TContentTypes extends Record<string, ContentTypeDefi
736
588
  * 2. Used for type inference in the SDK client
737
589
  *
738
590
  * @example
739
- * import { defineSchema, defineContentType, defineLocale, fields } from '@relyio/sdk/schema'
591
+ * import { defineSchema, defineContentType, defineLocale, fields, fieldset } from '@relyio/sdk/schema'
740
592
  *
741
593
  * const enUS = defineLocale('en-US', { displayName: 'English (US)', default: true })
742
594
  * const esES = defineLocale('es-ES', { displayName: 'Spanish', fallback: ['en-US'] })
743
595
  *
744
- * const product = defineContentType('product', {
745
- * displayName: 'Product',
746
- * fields: [
747
- * fields.shortText('title', { config: { required: true }, localization: true }),
748
- * fields.decimal('price', { config: { min: 0, decimalPlaces: 2 } }),
596
+ * const product = defineContentType({
597
+ * fields: {
598
+ * title: fields.shortText({ required: true, localized: true }),
599
+ * price: fields.decimal({ min: 0, decimalPlaces: 2 }),
749
600
  * // One value of a fieldset: `{ _type: 'seo', _key, metaTitle?, metaDescription? }`
750
- * fields.object('seo', {
751
- * config: {
752
- * of: {
753
- * id: 'seo',
754
- * displayName: 'SEO Metadata',
755
- * fields: [
756
- * fields.shortText('metaTitle', { config: { maxLength: 60 } }),
757
- * fields.longText('metaDescription', { config: { maxLength: 160 } }),
758
- * ],
601
+ * seo: fields.object({
602
+ * of: fieldset('seo', {
603
+ * displayName: 'SEO metadata',
604
+ * fields: {
605
+ * metaTitle: fields.shortText({ maxLength: 60 }),
606
+ * metaDescription: fields.longText({ maxLength: 160 }),
759
607
  * },
760
- * },
608
+ * }),
761
609
  * }),
762
- * ]
610
+ * },
763
611
  * })
764
612
  *
765
613
  * export const schema = defineSchema({
@@ -767,7 +615,38 @@ interface TypedSchemaConfig<TContentTypes extends Record<string, ContentTypeDefi
767
615
  * contentTypes: { product },
768
616
  * })
769
617
  */
770
- declare function defineSchema<TContentTypes extends Record<string, ContentTypeDefinition>, TSingletons extends Record<string, SingletonContentTypeDefinition> = Record<string, never>, TQueries extends Record<string, QueryDefinition> = Record<string, never>, TSettings extends Record<string, SettingsGroupDefinition> = Record<string, never>, TLocales extends readonly LocaleDefinition[] = readonly LocaleDefinition[], TRoles extends readonly RoleDefinition[] = readonly RoleDefinition[], TWorkflow extends WorkflowDefinition | undefined = WorkflowDefinition | undefined, TViews extends readonly ViewDefinition[] = readonly ViewDefinition[], TAssets extends Record<string, AssetTypeDefinition> = Record<string, never>>(config: TypedSchemaConfig<TContentTypes, TSingletons, TQueries, TSettings, TLocales, TRoles, TWorkflow, TViews, TAssets>): SchemaDefinition<TContentTypes, TSingletons, TQueries, TSettings, TLocales, TRoles, TWorkflow, TViews, TAssets>;
618
+ declare function defineSchema<TContentTypes extends Record<string, ContentTypeDefinition>, TSingletons extends Record<string, SingletonContentTypeDefinition> = Record<string, never>, TQueries extends Record<string, QueryDefinition> = Record<string, never>, TSettings extends Record<string, SettingsGroupDefinition> = Record<string, never>, TLocales extends readonly LocaleDefinition[] = readonly never[], TRoles extends KeyedRoles = Record<never, never>, TWorkflow extends WorkflowDefinition | undefined = undefined, TViews extends KeyedViews = Record<never, never>, TAssets extends Record<string, AssetTypeDefinition> = Record<string, never>>(config: TypedSchemaConfig<TContentTypes, TSingletons, TQueries, TSettings, TLocales, TRoles, TWorkflow, TViews, TAssets> & NamesChecked<TContentTypes, TSingletons, TSettings, TAssets, TLocales, TRoles, TWorkflow>): SchemaDefinition<TContentTypes, TSingletons, TQueries, TSettings, TLocales, RolesAsList<TRoles>, TWorkflow, ViewsAsList<TViews>, TAssets>;
619
+ /**
620
+ * What `defineSchema` holds a schema's names to (REL-2416): each section of types against what the
621
+ * schema declares — a reference's `to` and rich text's `links.to` against its content, singleton and
622
+ * asset type keys (a reference may point at a singleton: `@relyio/schema`'s
623
+ * `ResolvedReferenceTarget`), and a Field's `requiredLocales` against its locale codes; its locales'
624
+ * `fallback` against their codes; the roles its workflow's stages name, and its roles' `includes`,
625
+ * against the built-in roles and its role keys; and a type's stages against its workflow's stage
626
+ * keys. Types only; `./checked-names.ts` says how.
627
+ */
628
+ type NamesChecked<TContentTypes, TSingletons, TSettings, TAssets, TLocales, TRoles, TWorkflow> = {
629
+ readonly locales?: CheckedLocales<TLocales>;
630
+ readonly roles?: CheckedRoles<TRoles>;
631
+ readonly workflow?: CheckedWorkflow<TWorkflow, TRoles>;
632
+ readonly contentTypes: CheckedSection<TContentTypes, DeclaredBy<TContentTypes, TSingletons, TAssets, TLocales, TWorkflow>>;
633
+ readonly singletonContentTypes?: CheckedSection<TSingletons, DeclaredBy<TContentTypes, TSingletons, TAssets, TLocales, TWorkflow>>;
634
+ readonly settingsGroups?: CheckedSection<TSettings, DeclaredBy<TContentTypes, TSingletons, TAssets, TLocales, TWorkflow>>;
635
+ readonly assetTypes?: CheckedSection<TAssets, DeclaredBy<TContentTypes, TSingletons, TAssets, TLocales, TWorkflow>>;
636
+ };
637
+ /** The names a schema declares, for its sections' names to be checked against. */
638
+ interface DeclaredBy<TContentTypes, TSingletons, TAssets, TLocales, TWorkflow> {
639
+ readonly targets: KeysDeclared<TContentTypes> | KeysDeclared<TSingletons> | KeysDeclared<TAssets>;
640
+ readonly locales: LocaleCodesOf<TLocales>;
641
+ readonly stages: StagesDeclared<TWorkflow>;
642
+ }
643
+ /**
644
+ * The roles `defineSchema` returns for the keyed record it was given: the list it compiles to, each
645
+ * role carrying its key as a literal `id` (REL-2414).
646
+ */
647
+ type RolesAsList<R extends KeyedRoles> = KeyedRolesAsList<R>;
648
+ /** The views `defineSchema` returns for the ones it was given, as {@link RolesAsList} says of roles. */
649
+ type ViewsAsList<V extends KeyedViews> = KeyedViewsAsList<V>;
771
650
  /**
772
651
  * Type alias for any schema definition.
773
652
  * Uses `any` intentionally for type inference utilities that need to accept any schema.
@@ -802,7 +681,7 @@ type SchemaQueryNames<T extends AnySchemaDefinition> = keyof T['queries'] & stri
802
681
  *
803
682
  * `settingsGroups`, `assetTypes` and `locales` are optional members of `SchemaDefinition`, so
804
683
  * indexing one yields `X | undefined`, which never extends the record — every typed client's
805
- * `settings()`, `asset()` and `withLocale()` took `never`. The three read through `NonNullable`.
684
+ * `settings()`, `asset()` and `with({ locale })` took `never`. The three read through `NonNullable`.
806
685
  */
807
686
  type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = NonNullable<T['settingsGroups']> extends Record<string, SettingsGroupDefinition> ? keyof NonNullable<T['settingsGroups']> & string : never;
808
687
  /**
@@ -810,8 +689,11 @@ type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = NonNullable<T['se
810
689
  */
811
690
  type SchemaAssetTypeNames<T extends AnySchemaDefinition> = NonNullable<T['assetTypes']> extends Record<string, AssetTypeDefinition> ? keyof NonNullable<T['assetTypes']> & string : never;
812
691
  /**
813
- * Get locale codes from a schema
692
+ * Get locale codes from a schema: the codes its locales declare, or `string` for a schema that
693
+ * declares none. `defineSchema` now types an absent `locales` as an empty list, so that a Field's
694
+ * `requiredLocales` is checked against no locale (REL-2416); a read's locale keeps what it took
695
+ * before — any code — until the read options check it (REL-2417).
814
696
  */
815
- type SchemaLocaleCodes<T extends AnySchemaDefinition> = NonNullable<T['locales']> extends readonly LocaleDefinition[] ? NonNullable<T['locales']>[number]['code'] : never;
697
+ type SchemaLocaleCodes<T extends AnySchemaDefinition> = NonNullable<T['locales']> extends readonly LocaleDefinition[] ? [NonNullable<T['locales']>[number]] extends [never] ? string : NonNullable<T['locales']>[number]['code'] : never;
816
698
 
817
- export { type CachePolicyObject as $, type RoleDefinition as A, BUILT_IN_ROLES as B, defineQuery as C, type QueryDefinition as D, type QueryType as E, type FieldVisibility as F, type FullTextSearch as G, type FetchBlock as H, type AggregateBlock as I, type SearchBlock as J, type WhereValue as K, type LocaleOptions as L, type WhereOperator as M, type HavingOperator as N, type QueryScalar as O, type SortCondition as P, type QueryConfig as Q, ROLE_ACTIONS as R, type SchemaConfig as S, type TextDirection as T, type SortDirection as U, type IncludeDefinition as V, type WhereCondition as W, type IncludeRelationship as X, type SelectMapping as Y, type FilterDefinition as Z, type CachePolicy as _, type SchemaDefinition as a, type IncludeSubQuery as a0, type IncludeSubQueries as a1, type Parameterizable as a2, type QueryParameter as a3, type QueryParameterType as a4, type QueryCardinality as a5, type InferQueryResponse as a6, type InferQueryParams as a7, type FilterOperator as a8, type SchemaContentTypeNames as b, type SchemaSingletonNames as c, defineSchema as d, type SchemaQueryNames as e, type SchemaLocaleCodes as f, type SchemaSettingsGroupNames as g, type SchemaAssetTypeNames as h, type SchemaEntry as i, type SchemaEntryOf as j, defineLocale as k, isLocaleDefinition as l, getLocaleCodes as m, findDefaultLocale as n, getActiveLocales as o, buildFallbackChain as p, type LocaleDefinition as q, defineRole as r, isRoleDefinition as s, isBuiltInRole as t, getBuiltInRoleActions as u, BUILT_IN_ROLE_ACTIONS as v, type BuiltInRole as w, type RoleAction as x, type ResourcePermission as y, type RoleConfig as z };
699
+ export { type AggregateBlock as A, type QueryParameter as B, type CachePolicy as C, type QueryParameterType as D, type QueryCardinality as E, type FullTextSearch as F, type InferQueryResponse as G, type HavingOperator as H, type IncludeDefinition as I, type InferQueryParams as J, type QueryParameterValue as K, type AnyQueryParameters as L, type DeclaredParameters as M, type Parameterizable as P, type QueryConfig as Q, type RolesAsList as R, type SchemaConfig as S, type ViewsAsList as V, type WhereCondition as W, type SchemaDefinition as a, type SchemaContentTypeNames as b, type SchemaSingletonNames as c, defineSchema as d, type SchemaQueryNames as e, type SchemaLocaleCodes as f, type SchemaSettingsGroupNames as g, type SchemaAssetTypeNames as h, type SchemaEntry as i, type SchemaEntryOf as j, defineQuery as k, type QueryDefinition as l, type QueryType as m, type FetchBlock as n, type SearchBlock as o, type WhereValue as p, type WhereOperator as q, type QueryScalar as r, type SortCondition as s, type SortDirection as t, type IncludeRelationship as u, type SelectMapping as v, type FilterDefinition as w, type CachePolicyObject as x, type IncludeSubQuery as y, type IncludeSubQueries as z };