@easy-cms/core 1.32.0 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-J4NASOMO.js → chunk-KUEGUD2X.js} +790 -225
- package/dist/{config-Dr_NZGm6.d.ts → config-BZ4fsHL4.d.ts} +766 -527
- package/dist/{handler-B0m4VOAy.d.ts → handler-CMTJ99sx.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/dist/{plugin-DBHFjM-r.d.ts → plugin-GcNICm6a.d.ts} +1 -1
- package/dist/plugin.d.ts +2 -2
- package/package.json +1 -1
|
@@ -93,6 +93,67 @@ declare const isSignedIn: Access;
|
|
|
93
93
|
*/
|
|
94
94
|
declare const isAdmin: Access;
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Secrets that verify: the current one first, then `previousSecrets` while one is replaced. New
|
|
98
|
+
* values are always signed with the current one.
|
|
99
|
+
*/
|
|
100
|
+
type Secrets = string | readonly string[];
|
|
101
|
+
/** The secrets of a config, the current one first. */
|
|
102
|
+
declare function secretsOf(config: {
|
|
103
|
+
readonly secret: string;
|
|
104
|
+
readonly previousSecrets?: readonly string[] | undefined;
|
|
105
|
+
}): readonly string[];
|
|
106
|
+
/** What a preview token opens: one document of a collection, or one global. */
|
|
107
|
+
type PreviewTarget = {
|
|
108
|
+
readonly collection: string;
|
|
109
|
+
readonly id: string;
|
|
110
|
+
} | {
|
|
111
|
+
readonly global: string;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Why a link was sent: a forgotten password, an invitation to a new account, or (`verify`) to
|
|
115
|
+
* confirm the email of an account made by signing up.
|
|
116
|
+
*/
|
|
117
|
+
type PasswordPurpose = 'reset' | 'invite' | 'verify';
|
|
118
|
+
|
|
119
|
+
declare const API_KEYS = "api-keys";
|
|
120
|
+
/** What a key may do with a collection. `create` on `media` means uploading. */
|
|
121
|
+
declare const API_KEY_OPERATIONS: readonly ["read", "create", "update", "delete", "publish"];
|
|
122
|
+
type ApiKeyOperation = (typeof API_KEY_OPERATIONS)[number];
|
|
123
|
+
/** What a key may do with a global. */
|
|
124
|
+
declare const API_KEY_GLOBAL_OPERATIONS: readonly ["read", "update", "publish"];
|
|
125
|
+
type ApiKeyGlobalOperation = (typeof API_KEY_GLOBAL_OPERATIONS)[number];
|
|
126
|
+
/** Stored in the key's `permissions` field. Anything not listed is not allowed. */
|
|
127
|
+
interface ApiKeyPermissions {
|
|
128
|
+
readonly collections?: Readonly<Record<string, readonly ApiKeyOperation[]>>;
|
|
129
|
+
readonly globals?: Readonly<Record<string, readonly ApiKeyGlobalOperation[]>>;
|
|
130
|
+
/**
|
|
131
|
+
* With `upload.folders`: the media folders (ids) the key may use, with their subfolders. Empty
|
|
132
|
+
* or missing: every folder.
|
|
133
|
+
*/
|
|
134
|
+
readonly folders?: readonly (string | number)[];
|
|
135
|
+
/**
|
|
136
|
+
* The context the key was created in (`onRequest`, e.g. its tenant), kept for good: requests
|
|
137
|
+
* with the key get it as `user.apiKey.permissions.context`. Set by Easy CMS, not by input.
|
|
138
|
+
*/
|
|
139
|
+
readonly context?: RequestContext;
|
|
140
|
+
}
|
|
141
|
+
/** Set on `AuthUser.apiKey` when a request authenticated with an API key. */
|
|
142
|
+
interface ApiKeyContext {
|
|
143
|
+
readonly id: number | string;
|
|
144
|
+
readonly name: string;
|
|
145
|
+
readonly permissions: ApiKeyPermissions;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Whether a request's API key allows an operation. Always true without a key (sessions are
|
|
149
|
+
* governed by access rules alone); a key only narrows what its user may do.
|
|
150
|
+
*/
|
|
151
|
+
declare function keyAllows(user: AuthUser | null, target: {
|
|
152
|
+
collection: string;
|
|
153
|
+
} | {
|
|
154
|
+
global: string;
|
|
155
|
+
}, operation: ApiKeyOperation): boolean;
|
|
156
|
+
|
|
96
157
|
/** More about a log line: `error` for what failed (its stack goes to the log). */
|
|
97
158
|
interface LogDetails {
|
|
98
159
|
readonly error?: unknown;
|
|
@@ -361,66 +422,155 @@ interface BundleHints {
|
|
|
361
422
|
readonly traceInclude: readonly string[];
|
|
362
423
|
}
|
|
363
424
|
|
|
364
|
-
/**
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
readonly
|
|
372
|
-
readonly previousSecrets?: readonly string[] | undefined;
|
|
373
|
-
}): readonly string[];
|
|
374
|
-
/** What a preview token opens: one document of a collection, or one global. */
|
|
375
|
-
type PreviewTarget = {
|
|
376
|
-
readonly collection: string;
|
|
377
|
-
readonly id: string;
|
|
378
|
-
} | {
|
|
379
|
-
readonly global: string;
|
|
380
|
-
};
|
|
381
|
-
/**
|
|
382
|
-
* Why a link was sent: a forgotten password, an invitation to a new account, or (`verify`) to
|
|
383
|
-
* confirm the email of an account made by signing up.
|
|
384
|
-
*/
|
|
385
|
-
type PasswordPurpose = 'reset' | 'invite' | 'verify';
|
|
386
|
-
|
|
387
|
-
declare const API_KEYS = "api-keys";
|
|
388
|
-
/** What a key may do with a collection. `create` on `media` means uploading. */
|
|
389
|
-
declare const API_KEY_OPERATIONS: readonly ["read", "create", "update", "delete", "publish"];
|
|
390
|
-
type ApiKeyOperation = (typeof API_KEY_OPERATIONS)[number];
|
|
391
|
-
/** What a key may do with a global. */
|
|
392
|
-
declare const API_KEY_GLOBAL_OPERATIONS: readonly ["read", "update", "publish"];
|
|
393
|
-
type ApiKeyGlobalOperation = (typeof API_KEY_GLOBAL_OPERATIONS)[number];
|
|
394
|
-
/** Stored in the key's `permissions` field. Anything not listed is not allowed. */
|
|
395
|
-
interface ApiKeyPermissions {
|
|
396
|
-
readonly collections?: Readonly<Record<string, readonly ApiKeyOperation[]>>;
|
|
397
|
-
readonly globals?: Readonly<Record<string, readonly ApiKeyGlobalOperation[]>>;
|
|
425
|
+
/** Audit log settings (`audit`). */
|
|
426
|
+
interface AuditConfig {
|
|
427
|
+
/** Days to keep entries; older ones are deleted. `0`: keep them all. Default 365. */
|
|
428
|
+
readonly keepDays?: number;
|
|
429
|
+
/** Keep the values before and after a change, not only which fields changed. Default `true`. */
|
|
430
|
+
readonly values?: boolean;
|
|
431
|
+
/** Failed logins within an hour that the dashboard warns about. Default 20. */
|
|
432
|
+
readonly failedLogins?: number;
|
|
398
433
|
/**
|
|
399
|
-
*
|
|
400
|
-
*
|
|
434
|
+
* The part of the site an entry belongs to, from the request's context, e.g. its tenant
|
|
435
|
+
* (usually set by a plugin). Users whose role holds in a part only (`scoped`) see that part's
|
|
436
|
+
* entries; others see the chosen part's, or all.
|
|
401
437
|
*/
|
|
402
|
-
readonly
|
|
438
|
+
readonly scope?: ((args: {
|
|
439
|
+
readonly context: RequestContext;
|
|
440
|
+
readonly user: AuthUser | null;
|
|
441
|
+
}) => string | null | undefined) | false;
|
|
442
|
+
}
|
|
443
|
+
/** A field that changed: values before and after, unless they are not kept. */
|
|
444
|
+
interface AuditChange {
|
|
445
|
+
field: string;
|
|
446
|
+
before?: unknown;
|
|
447
|
+
after?: unknown;
|
|
448
|
+
}
|
|
449
|
+
/** One entry (`GET <api>/admin/audit`). */
|
|
450
|
+
interface AuditEntry {
|
|
451
|
+
id: ID;
|
|
452
|
+
/** When, as an ISO date. */
|
|
453
|
+
at: string;
|
|
454
|
+
/** E.g. `create`, `update`, `publish`, `delete`, `login`, `login.failed`, `role.update`. */
|
|
455
|
+
action: string;
|
|
456
|
+
/** A collection slug, `global:<slug>`, or an area such as `auth`, `roles`, `backups`. */
|
|
457
|
+
target: string | null;
|
|
458
|
+
/** The document's id. */
|
|
459
|
+
doc: string | null;
|
|
460
|
+
/** The document's title then (kept after it is deleted). */
|
|
461
|
+
title: string | null;
|
|
462
|
+
actor: {
|
|
463
|
+
id: string | null;
|
|
464
|
+
email: string | null;
|
|
465
|
+
/** `user` (admin or REST), `api-key`, `system` (code), `scheduler`. */
|
|
466
|
+
via: string;
|
|
467
|
+
ip: string | null;
|
|
468
|
+
userAgent: string | null;
|
|
469
|
+
};
|
|
470
|
+
changes: AuditChange[] | null;
|
|
471
|
+
detail: Record<string, unknown> | null;
|
|
472
|
+
}
|
|
473
|
+
/** `GET <api>/admin/audit`: a page of entries, newest first. */
|
|
474
|
+
/** A page of the audit log: the list shape of the rest of the API. */
|
|
475
|
+
type AuditPage = PaginatedDocs<AuditEntry>;
|
|
476
|
+
interface AuditFilter {
|
|
477
|
+
action?: string | null;
|
|
478
|
+
target?: string | null;
|
|
479
|
+
doc?: string | null;
|
|
480
|
+
/** An email, or part of one. */
|
|
481
|
+
actor?: string | null;
|
|
482
|
+
/** ISO dates. */
|
|
483
|
+
from?: string | null;
|
|
484
|
+
to?: string | null;
|
|
485
|
+
/** Only entries of this part of the site (`scope`). */
|
|
486
|
+
scope?: string | null;
|
|
487
|
+
}
|
|
488
|
+
/** `POST <api>/admin/audit/verify`. */
|
|
489
|
+
interface AuditVerification {
|
|
490
|
+
checked: number;
|
|
491
|
+
/** Entries whose signature does not match: changed after they were written. */
|
|
492
|
+
invalid: ID[];
|
|
403
493
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
494
|
+
* Missing ids between entries: deleted entries, or (rarely) inserts that failed. Ids are never
|
|
495
|
+
* reused, so the newest entries deleted show up here once anything is written after them (a
|
|
496
|
+
* check writes an entry itself). The server's log has each check's last id, to compare with.
|
|
406
497
|
*/
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
/** Set on `AuthUser.apiKey` when a request authenticated with an API key. */
|
|
410
|
-
interface ApiKeyContext {
|
|
411
|
-
readonly id: number | string;
|
|
412
|
-
readonly name: string;
|
|
413
|
-
readonly permissions: ApiKeyPermissions;
|
|
498
|
+
gaps: number;
|
|
499
|
+
at: string;
|
|
414
500
|
}
|
|
415
501
|
/**
|
|
416
|
-
*
|
|
417
|
-
*
|
|
502
|
+
* The audit log (`audit` in the config): who did what, when, from where. Entries can't be
|
|
503
|
+
* changed through Easy CMS; each is signed with the secret, so changes in the database show.
|
|
504
|
+
* Available as `cms.audit`.
|
|
418
505
|
*/
|
|
419
|
-
declare
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
506
|
+
declare class AuditLog {
|
|
507
|
+
private readonly cms;
|
|
508
|
+
/** Entries that could not be written since the server started. */
|
|
509
|
+
failures: number;
|
|
510
|
+
constructor(cms: EasyCMS);
|
|
511
|
+
get enabled(): boolean;
|
|
512
|
+
private get settings();
|
|
513
|
+
/**
|
|
514
|
+
* Writes an entry. The user defaults to the request's. Never throws: a failure is logged and
|
|
515
|
+
* counted, and the dashboard says so.
|
|
516
|
+
*/
|
|
517
|
+
record(entry: {
|
|
518
|
+
action: string;
|
|
519
|
+
target?: string | null;
|
|
520
|
+
doc?: ID | null;
|
|
521
|
+
title?: string | null;
|
|
522
|
+
user?: AuthUser | null;
|
|
523
|
+
/** Who it was when there is no user, e.g. the email of a failed login. */
|
|
524
|
+
email?: string | null;
|
|
525
|
+
changes?: AuditChange[] | null;
|
|
526
|
+
detail?: Record<string, unknown> | null;
|
|
527
|
+
/** The call's context, when it is not the request's. */
|
|
528
|
+
context?: RequestContext;
|
|
529
|
+
}): Promise<void>;
|
|
530
|
+
/** Entries, newest first: `page` (from 1) of `limit` (default 50, at most 100). */
|
|
531
|
+
list(options?: AuditFilter & {
|
|
532
|
+
page?: number;
|
|
533
|
+
limit?: number;
|
|
534
|
+
}): Promise<AuditPage>;
|
|
535
|
+
/** The filtered entries as CSV (the newest 10,000). */
|
|
536
|
+
csv(filter: AuditFilter): Promise<string>;
|
|
537
|
+
/** Checks every entry's signature, and that no ids are missing. */
|
|
538
|
+
verify(): Promise<AuditVerification>;
|
|
539
|
+
/** The secret an entry was signed with: the current one, a previous one, or none (changed). */
|
|
540
|
+
private signer;
|
|
541
|
+
/**
|
|
542
|
+
* After `secret` changed: signs again, with the current secret, the entries signed with one of
|
|
543
|
+
* `previousSecrets`, so they still verify once it is removed. Entries no secret verifies were
|
|
544
|
+
* changed: they are left as they are, and reported. Records `audit.resign`.
|
|
545
|
+
*/
|
|
546
|
+
resign(): Promise<{
|
|
547
|
+
checked: number;
|
|
548
|
+
resigned: number;
|
|
549
|
+
invalid: ID[];
|
|
550
|
+
}>;
|
|
551
|
+
/**
|
|
552
|
+
* What the dashboard warns about: many failed logins, sign-ins of break-glass accounts in the
|
|
553
|
+
* last day, entries not written, a failed check.
|
|
554
|
+
*/
|
|
555
|
+
attention(now?: Date): Promise<({
|
|
556
|
+
id: 'failed-logins';
|
|
557
|
+
count: number;
|
|
558
|
+
} | {
|
|
559
|
+
id: 'break-glass';
|
|
560
|
+
count: number;
|
|
561
|
+
emails: string[];
|
|
562
|
+
} | {
|
|
563
|
+
id: 'audit-failures';
|
|
564
|
+
count: number;
|
|
565
|
+
} | {
|
|
566
|
+
id: 'audit-tampered';
|
|
567
|
+
invalid: number;
|
|
568
|
+
at: string;
|
|
569
|
+
})[]>;
|
|
570
|
+
private sign;
|
|
571
|
+
private lastVerification;
|
|
572
|
+
private where;
|
|
573
|
+
}
|
|
424
574
|
|
|
425
575
|
/** The cookie that carries a sign-in through the provider and back. */
|
|
426
576
|
declare const SSO_COOKIE = "ecms-sso";
|
|
@@ -717,338 +867,40 @@ interface EmailAdapter {
|
|
|
717
867
|
* Leave it out to skip the check.
|
|
718
868
|
*/
|
|
719
869
|
readonly apiVersion?: number;
|
|
720
|
-
/** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
|
|
721
|
-
readonly from?: string | undefined;
|
|
722
|
-
send(message: EmailMessage & {
|
|
723
|
-
readonly from: string;
|
|
724
|
-
}): Promise<void>;
|
|
725
|
-
/**
|
|
726
|
-
* The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
|
|
727
|
-
* a password is set, not what it is.
|
|
728
|
-
*/
|
|
729
|
-
describe?(): readonly EmailSetting[];
|
|
730
|
-
/** Checks the connection without sending (Settings → Email); throws with the reason. */
|
|
731
|
-
verify?(): Promise<void>;
|
|
732
|
-
}
|
|
733
|
-
/** One setting an email adapter shows to admins (`describe()`). */
|
|
734
|
-
interface EmailSetting {
|
|
735
|
-
/**
|
|
736
|
-
* `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
|
|
737
|
-
* are translated in the admin; others are shown as they are.
|
|
738
|
-
*/
|
|
739
|
-
readonly key: string;
|
|
740
|
-
/** `null`: not set. */
|
|
741
|
-
readonly value: string | null;
|
|
742
|
-
/** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
|
|
743
|
-
readonly source?: string;
|
|
744
|
-
}
|
|
745
|
-
/** Prints emails to the log instead of sending them: for development and tests. */
|
|
746
|
-
declare function consoleEmail(options?: {
|
|
747
|
-
from?: string;
|
|
748
|
-
log?: (text: string) => void;
|
|
749
|
-
}): EmailAdapter & {
|
|
750
|
-
readonly sent: (EmailMessage & {
|
|
751
|
-
from: string;
|
|
752
|
-
})[];
|
|
753
|
-
};
|
|
754
|
-
|
|
755
|
-
/** A label shown in the admin UI. Either one string or one string per admin locale. */
|
|
756
|
-
type Label = string | {
|
|
757
|
-
readonly [locale: string]: string;
|
|
758
|
-
};
|
|
759
|
-
interface FieldValidateContext {
|
|
760
|
-
/** The whole document being validated, including sibling fields. */
|
|
761
|
-
readonly data: Readonly<Record<string, unknown>>;
|
|
762
|
-
readonly operation: 'create' | 'update';
|
|
763
|
-
}
|
|
764
|
-
/** Return `true` when valid, or an error message to show on the field. */
|
|
765
|
-
type FieldValidate<TValue> = (value: TValue | null | undefined, ctx: FieldValidateContext) => true | string | Promise<true | string>;
|
|
766
|
-
/**
|
|
767
|
-
* A Web Component from an admin module (`admin.modules`), by its tag name, which must start
|
|
768
|
-
* with `ecms-`. `props` (plain JSON) reach the element as its `options` property.
|
|
769
|
-
*/
|
|
770
|
-
type AdminComponent = string | {
|
|
771
|
-
readonly tag: string;
|
|
772
|
-
readonly props?: Readonly<Record<string, unknown>>;
|
|
773
|
-
};
|
|
774
|
-
/** How the admin shows a field, with components from admin modules. */
|
|
775
|
-
interface FieldAdmin {
|
|
776
|
-
/** Help below the field's label. */
|
|
777
|
-
readonly description?: Label;
|
|
778
|
-
/** Its share of a row (`admin.layout`'s `row`). Default: an equal share. */
|
|
779
|
-
readonly width?: '1/4' | '1/3' | '1/2' | '2/3' | '3/4' | 'full';
|
|
780
|
-
/**
|
|
781
|
-
* Shown only when its sibling fields match, e.g. `{ field: 'linkType', equals: 'external' }`.
|
|
782
|
-
* A hidden field isn't required. See `FieldCondition`.
|
|
783
|
-
*/
|
|
784
|
-
readonly condition?: FieldCondition;
|
|
785
|
-
/** Shown instead of the field's input; the admin keeps the label and error messages. */
|
|
786
|
-
readonly component?: AdminComponent;
|
|
787
|
-
/** Shown below the field, e.g. a length meter or a preview. */
|
|
788
|
-
readonly after?: readonly AdminComponent[];
|
|
789
|
-
/** Shows the value in the admin's lists (e.g. a color swatch). Default: the plain value. */
|
|
790
|
-
readonly cell?: AdminComponent;
|
|
791
|
-
/**
|
|
792
|
-
* A column of the list at first (each user can change it): `true`, or per request, e.g.
|
|
793
|
-
* only when the admin shows every tenant.
|
|
794
|
-
*/
|
|
795
|
-
readonly column?: boolean | ((args: AdminFieldArgs) => boolean | Promise<boolean>);
|
|
796
|
-
/** Relationships: offer to create the related document in place. Default true. */
|
|
797
|
-
readonly allowCreate?: boolean;
|
|
798
|
-
/**
|
|
799
|
-
* The value a new document's form starts with, worked out for each user, e.g. from the
|
|
800
|
-
* request's context. The server's own default (`defaultValue`, hooks) still applies.
|
|
801
|
-
*/
|
|
802
|
-
readonly initialValue?: (args: AdminFieldArgs) => unknown;
|
|
803
|
-
/** `sidebar`: shown in the edit page's side panel instead of the main form (top-level fields). */
|
|
804
|
-
readonly position?: 'sidebar';
|
|
805
|
-
/** `false`: the list's filters don't offer it. Default: every field the user may read. */
|
|
806
|
-
readonly filter?: boolean;
|
|
807
|
-
}
|
|
808
|
-
/** What per-request admin options of a field receive. */
|
|
809
|
-
interface AdminFieldArgs {
|
|
810
|
-
readonly user: AuthUser;
|
|
811
|
-
readonly context: RequestContext;
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Field types that packages add (`fieldTypes` in the config), for the types inferred from the
|
|
815
|
-
* config. A package declares its own by augmenting this interface:
|
|
816
|
-
*
|
|
817
|
-
* ```ts
|
|
818
|
-
* declare module '@easy-cms/core' {
|
|
819
|
-
* interface CustomFieldTypes {
|
|
820
|
-
* color: { value: string; options: { readonly presets?: readonly string[] } }
|
|
821
|
-
* }
|
|
822
|
-
* }
|
|
823
|
-
* ```
|
|
824
|
-
*/
|
|
825
|
-
interface CustomFieldTypes {
|
|
826
|
-
}
|
|
827
|
-
/** A field of a type from `CustomFieldTypes`: the common options, plus the type's own. */
|
|
828
|
-
type CustomField = {
|
|
829
|
-
[K in keyof CustomFieldTypes]: BaseField<K & string, CustomFieldTypes[K] extends {
|
|
830
|
-
value: infer V;
|
|
831
|
-
} ? V : unknown> & (CustomFieldTypes[K] extends {
|
|
832
|
-
options: infer O;
|
|
833
|
-
} ? O : unknown);
|
|
834
|
-
}[keyof CustomFieldTypes];
|
|
835
|
-
interface BaseField<TType extends string, TValue> {
|
|
836
|
-
readonly type: TType;
|
|
837
|
-
readonly name: string;
|
|
838
|
-
readonly label?: Label;
|
|
839
|
-
readonly required?: boolean;
|
|
840
|
-
readonly unique?: boolean;
|
|
841
|
-
/**
|
|
842
|
-
* With `unique` (or on a slug field): a sibling field (e.g. `parent` or `tenant`), or several,
|
|
843
|
-
* whose documents only need to differ among themselves, so `/about/team` and `/careers/team`
|
|
844
|
-
* can both be `team`. Single, unlocalized relationship, select, text or number fields.
|
|
845
|
-
*/
|
|
846
|
-
readonly uniqueWithin?: string | readonly string[];
|
|
847
|
-
/**
|
|
848
|
-
* `true` for a database index (filters and sorts on this field). `'trigram'`, on text, textarea
|
|
849
|
-
* and email fields: an index for `like`, which finds any part of the text, in Thai as in other
|
|
850
|
-
* languages. Postgres only (pg_trgm); nothing on SQLite. There, `like` needs at least three
|
|
851
|
-
* characters, and the admin's searches wait for them.
|
|
852
|
-
*/
|
|
853
|
-
readonly index?: boolean | 'trigram';
|
|
854
|
-
readonly defaultValue?: TValue;
|
|
855
|
-
readonly validate?: FieldValidate<TValue>;
|
|
856
|
-
readonly access?: FieldAccess;
|
|
857
|
-
/** Stored but never returned by the API nor accepted as input (e.g. a password hash). */
|
|
858
|
-
readonly hidden?: boolean;
|
|
859
|
-
/**
|
|
860
|
-
* One value per locale (needs `localization` in the config). Not for groups, arrays or
|
|
861
|
-
* hasMany fields: localize the fields inside a group or array instead.
|
|
862
|
-
*/
|
|
863
|
-
readonly localized?: boolean;
|
|
864
|
-
/** How the field looks in the admin: components, position, width, conditions. */
|
|
865
|
-
readonly admin?: FieldAdmin;
|
|
866
|
-
/** Set by Easy CMS on fields of an added type (`fieldTypes`): the type's name, e.g. `color`. */
|
|
867
|
-
readonly customType?: string;
|
|
868
|
-
}
|
|
869
|
-
interface TextField extends BaseField<'text', string> {
|
|
870
|
-
readonly minLength?: number;
|
|
871
|
-
readonly maxLength?: number;
|
|
872
|
-
}
|
|
873
|
-
interface TextareaField extends BaseField<'textarea', string> {
|
|
874
|
-
readonly minLength?: number;
|
|
875
|
-
readonly maxLength?: number;
|
|
876
|
-
}
|
|
877
|
-
interface NumberField extends BaseField<'number', number> {
|
|
878
|
-
readonly min?: number;
|
|
879
|
-
readonly max?: number;
|
|
880
|
-
}
|
|
881
|
-
interface BooleanField extends BaseField<'boolean', boolean> {
|
|
882
|
-
}
|
|
883
|
-
/** Stored and returned as an ISO 8601 string. */
|
|
884
|
-
interface DateField extends BaseField<'date', string> {
|
|
885
|
-
}
|
|
886
|
-
interface EmailField extends BaseField<'email', string> {
|
|
887
|
-
}
|
|
888
|
-
interface JsonField extends BaseField<'json', unknown> {
|
|
889
|
-
}
|
|
890
|
-
type SelectOption = string | {
|
|
891
|
-
readonly label: Label;
|
|
892
|
-
readonly value: string;
|
|
893
|
-
};
|
|
894
|
-
interface SelectField extends BaseField<'select', string | readonly string[]> {
|
|
895
|
-
readonly options: readonly SelectOption[];
|
|
896
|
-
readonly hasMany?: boolean;
|
|
897
|
-
}
|
|
898
|
-
interface SlugField extends BaseField<'slug', string> {
|
|
899
|
-
/** Name of a sibling `text` field to generate the slug from. */
|
|
900
|
-
readonly from?: string;
|
|
901
|
-
}
|
|
902
|
-
/** Tiptap / ProseMirror JSON document. */
|
|
903
|
-
interface RichTextDocument {
|
|
904
|
-
readonly type: 'doc';
|
|
905
|
-
readonly content?: readonly unknown[];
|
|
906
|
-
}
|
|
907
|
-
interface RichTextField extends BaseField<'richText', RichTextDocument> {
|
|
908
|
-
/**
|
|
909
|
-
* Blocks editors place between paragraphs (#328): a card, a callout, a video. Each is a node
|
|
910
|
-
* `{ type: 'block', attrs: { blockType, id, fields } }` in the document.
|
|
911
|
-
*/
|
|
912
|
-
readonly blocks?: readonly RichTextBlock[];
|
|
913
|
-
}
|
|
914
|
-
/** Arguments of a rich text block's `admin.summary` function. */
|
|
915
|
-
interface BlockSummaryArgs {
|
|
916
|
-
readonly fields: Record<string, unknown>;
|
|
917
|
-
readonly user: AuthUser | null;
|
|
918
|
-
readonly cms: EasyCMS;
|
|
919
|
-
}
|
|
920
|
-
/** A block in rich text (`richText.blocks`): the fields of `Block`, and how its card reads. */
|
|
921
|
-
interface RichTextBlock extends Block {
|
|
922
|
-
readonly admin?: {
|
|
923
|
-
/**
|
|
924
|
-
* The text of the block's card in the editor: a function, run on the server, or the tag of a
|
|
925
|
-
* custom element from an admin module, which gets `fields`. Default: the block's label and
|
|
926
|
-
* its first value.
|
|
927
|
-
*/
|
|
928
|
-
readonly summary?: string | ((args: BlockSummaryArgs) => string | Promise<string>);
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
/** References a document in the built-in `media` collection: a file or an image. */
|
|
932
|
-
interface UploadField extends BaseField<'upload', never> {
|
|
933
|
-
/** Several files, e.g. a gallery: the value is a list, in the order editors arrange. */
|
|
934
|
-
readonly hasMany?: boolean;
|
|
935
|
-
/** With `hasMany`: the fewest files. */
|
|
936
|
-
readonly minRows?: number;
|
|
937
|
-
/** With `hasMany`: the most files. */
|
|
938
|
-
readonly maxRows?: number;
|
|
939
|
-
/**
|
|
940
|
-
* The file types allowed, e.g. `['image/*']` or `['image/*', 'application/pdf']`. The admin
|
|
941
|
-
* offers only these, and saving refuses others.
|
|
942
|
-
*/
|
|
943
|
-
readonly mimeTypes?: readonly string[];
|
|
944
|
-
/**
|
|
945
|
-
* With `upload.folders`: the `key` of the media folder this field's files go in, e.g.
|
|
946
|
-
* `'banners'`. Its picker opens there and its uploads land there; the folder is made when it
|
|
947
|
-
* is first needed.
|
|
948
|
-
*/
|
|
949
|
-
readonly folder?: string;
|
|
950
|
-
/** With `folder`: only files in that folder (and its subfolders) may be chosen. */
|
|
951
|
-
readonly folderOnly?: boolean;
|
|
952
|
-
/** Which media documents may be chosen, as for relationships: offered and checked on save. */
|
|
953
|
-
readonly filterOptions?: FilterOptions;
|
|
954
|
-
}
|
|
955
|
-
interface FilterOptionsArgs {
|
|
956
|
-
/** The document being edited; `undefined` while it is being created. */
|
|
957
|
-
readonly id: ID | undefined;
|
|
958
|
-
/** The collection the choices come from: one of the field's `to`. */
|
|
959
|
-
readonly relationTo: string;
|
|
960
|
-
readonly user: AuthUser | null;
|
|
961
|
-
readonly cms: EasyCMS;
|
|
962
|
-
/** The request's context (`onRequest`), `{}` when there is none. */
|
|
963
|
-
readonly context: RequestContext;
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Which documents a relationship may point to: a `where` on the target collection, or `true`
|
|
967
|
-
* for any. Runs on the server: the admin's picker offers only these, and saving checks them.
|
|
968
|
-
*/
|
|
969
|
-
type FilterOptions = (args: FilterOptionsArgs) => Where | true | Promise<Where | true>;
|
|
970
|
-
interface RelationshipField extends BaseField<'relationship', never> {
|
|
971
|
-
/**
|
|
972
|
-
* Slug of the target collection, or several: then each value is `{ relationTo, value }`, the
|
|
973
|
-
* collection and the document's id (#327).
|
|
974
|
-
*/
|
|
975
|
-
readonly to: string | readonly string[];
|
|
976
|
-
readonly hasMany?: boolean;
|
|
977
|
-
/** With `hasMany`: the fewest documents. */
|
|
978
|
-
readonly minRows?: number;
|
|
979
|
-
/** With `hasMany`: the most documents. */
|
|
980
|
-
readonly maxRows?: number;
|
|
981
|
-
readonly filterOptions?: FilterOptions;
|
|
982
|
-
}
|
|
983
|
-
interface ArrayField extends BaseField<'array', never> {
|
|
984
|
-
readonly fields: readonly Field[];
|
|
985
|
-
readonly minRows?: number;
|
|
986
|
-
readonly maxRows?: number;
|
|
987
|
-
}
|
|
988
|
-
interface GroupField extends BaseField<'group', never> {
|
|
989
|
-
readonly fields: readonly Field[];
|
|
990
|
-
}
|
|
991
|
-
/** One kind of block in a `blocks` field. */
|
|
992
|
-
interface Block {
|
|
993
|
-
/** Stored in each row as `blockType`. */
|
|
994
|
-
readonly slug: string;
|
|
995
|
-
readonly labels?: {
|
|
996
|
-
readonly singular?: Label;
|
|
997
|
-
readonly plural?: Label;
|
|
998
|
-
};
|
|
999
|
-
readonly fields: readonly Field[];
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* A list of rows, each one of several block kinds (e.g. hero, text, gallery), for pages that
|
|
1003
|
-
* editors lay out themselves. Rows are `{ id, blockType, ...fields }`, stored as JSON.
|
|
1004
|
-
*/
|
|
1005
|
-
interface BlocksField extends BaseField<'blocks', never> {
|
|
1006
|
-
readonly blocks: readonly Block[];
|
|
1007
|
-
readonly minRows?: number;
|
|
1008
|
-
readonly maxRows?: number;
|
|
1009
|
-
}
|
|
1010
|
-
/** The fields `uniqueWithin` names, as a list. */
|
|
1011
|
-
declare const uniqueWithinOf: (field: Field) => readonly string[];
|
|
1012
|
-
/** Fields with `hasMany`: their value is a list of options, documents or files. */
|
|
1013
|
-
declare const isHasMany: (field: Field) => boolean;
|
|
1014
|
-
/**
|
|
1015
|
-
* Whether a MIME type is one of `patterns` (`image/*` matches every image but SVG, which can
|
|
1016
|
-
* carry scripts and is allowed only when listed by name: `image/svg+xml`).
|
|
1017
|
-
*/
|
|
1018
|
-
declare function mimeAllowedBy(mimeType: string, patterns: readonly string[]): boolean;
|
|
1019
|
-
/** A relationship to several collections: its values are `{ relationTo, value }` (#327). */
|
|
1020
|
-
declare const isPolymorphic: (field: Field) => field is RelationshipField & {
|
|
1021
|
-
readonly to: readonly string[];
|
|
1022
|
-
};
|
|
1023
|
-
/** The collections a relationship or upload field points to. */
|
|
1024
|
-
declare function relationTargets(field: Field): readonly string[];
|
|
1025
|
-
/** A `{ relationTo, value }` of a relationship to several collections, or `undefined`. */
|
|
1026
|
-
declare function polymorphicValue(value: unknown): {
|
|
1027
|
-
relationTo: string;
|
|
1028
|
-
value: unknown;
|
|
1029
|
-
} | undefined;
|
|
1030
|
-
/** Fields whose value is a list of rows: `array` and `blocks`. */
|
|
1031
|
-
declare const hasRows: (field: Field) => field is ArrayField | BlocksField;
|
|
1032
|
-
/** The fields of one row of an `array` or `blocks` field (`undefined` for an unknown block). */
|
|
1033
|
-
declare function rowFields(field: ArrayField | BlocksField, row: unknown): readonly Field[] | undefined;
|
|
1034
|
-
/**
|
|
1035
|
-
* Stores nothing: a component in the edit form (`admin.component`), e.g. a calculator, a preview
|
|
1036
|
-
* or a button. Placed like any field (layout, groups, conditions); not in the database, the API or
|
|
1037
|
-
* the generated types.
|
|
1038
|
-
*/
|
|
1039
|
-
interface UiField extends BaseField<'ui', never> {
|
|
1040
|
-
readonly admin: FieldAdmin & {
|
|
1041
|
-
readonly component: AdminComponent;
|
|
1042
|
-
};
|
|
870
|
+
/** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
|
|
871
|
+
readonly from?: string | undefined;
|
|
872
|
+
send(message: EmailMessage & {
|
|
873
|
+
readonly from: string;
|
|
874
|
+
}): Promise<void>;
|
|
875
|
+
/**
|
|
876
|
+
* The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
|
|
877
|
+
* a password is set, not what it is.
|
|
878
|
+
*/
|
|
879
|
+
describe?(): readonly EmailSetting[];
|
|
880
|
+
/** Checks the connection without sending (Settings → Email); throws with the reason. */
|
|
881
|
+
verify?(): Promise<void>;
|
|
1043
882
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
/**
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
883
|
+
/** One setting an email adapter shows to admins (`describe()`). */
|
|
884
|
+
interface EmailSetting {
|
|
885
|
+
/**
|
|
886
|
+
* `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
|
|
887
|
+
* are translated in the admin; others are shown as they are.
|
|
888
|
+
*/
|
|
889
|
+
readonly key: string;
|
|
890
|
+
/** `null`: not set. */
|
|
891
|
+
readonly value: string | null;
|
|
892
|
+
/** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
|
|
893
|
+
readonly source?: string;
|
|
894
|
+
}
|
|
895
|
+
/** Prints emails to the log instead of sending them: for development and tests. */
|
|
896
|
+
declare function consoleEmail(options?: {
|
|
897
|
+
from?: string;
|
|
898
|
+
log?: (text: string) => void;
|
|
899
|
+
}): EmailAdapter & {
|
|
900
|
+
readonly sent: (EmailMessage & {
|
|
901
|
+
from: string;
|
|
902
|
+
})[];
|
|
903
|
+
};
|
|
1052
904
|
|
|
1053
905
|
type Simplify<T> = {
|
|
1054
906
|
[K in keyof T]: T[K];
|
|
@@ -1473,6 +1325,20 @@ type RichTextBlocks<C extends Config, S extends CollectionSlug<C>, F extends str
|
|
|
1473
1325
|
} : never : never;
|
|
1474
1326
|
|
|
1475
1327
|
type IntegrationCategory = 'social' | 'ai' | 'notifications' | 'automation';
|
|
1328
|
+
/**
|
|
1329
|
+
* A provider's logo: SVG paths, e.g. from simple-icons. Without `fill`, a path takes `color` (the
|
|
1330
|
+
* brand's), or the text color where that wouldn't show on the admin's background.
|
|
1331
|
+
*/
|
|
1332
|
+
interface IntegrationLogo {
|
|
1333
|
+
readonly paths: readonly {
|
|
1334
|
+
readonly d: string;
|
|
1335
|
+
readonly fill?: string;
|
|
1336
|
+
}[];
|
|
1337
|
+
/** Default `0 0 24 24`. */
|
|
1338
|
+
readonly viewBox?: string;
|
|
1339
|
+
/** `#rrggbb`. */
|
|
1340
|
+
readonly color?: string;
|
|
1341
|
+
}
|
|
1476
1342
|
/** A value to fill when connecting (`kind: 'fields'`). */
|
|
1477
1343
|
interface IntegrationField {
|
|
1478
1344
|
readonly name: string;
|
|
@@ -1527,7 +1393,8 @@ interface IntegrationConfig {
|
|
|
1527
1393
|
readonly label: Label;
|
|
1528
1394
|
readonly category: IntegrationCategory;
|
|
1529
1395
|
readonly description?: Label;
|
|
1530
|
-
|
|
1396
|
+
/** Its logo (#363), or the name of an admin icon (`ADMIN_ICONS`). Without one: its first letter. */
|
|
1397
|
+
readonly icon?: string | IntegrationLogo;
|
|
1531
1398
|
/** A link to its guide. */
|
|
1532
1399
|
readonly docs?: string;
|
|
1533
1400
|
/** The plugin it comes from, e.g. `@easy-cms/plugin-notify`. */
|
|
@@ -1551,10 +1418,19 @@ interface IntegrationConfig {
|
|
|
1551
1418
|
redirectUri: string;
|
|
1552
1419
|
}) => Promise<IntegrationOAuthResult> | IntegrationOAuthResult;
|
|
1553
1420
|
};
|
|
1554
|
-
/** Checks the values without posting anything. */
|
|
1421
|
+
/** Checks the values without posting anything; `name` is the connection's. */
|
|
1555
1422
|
readonly test?: (values: Readonly<Record<string, string>>, args: {
|
|
1556
1423
|
cms: EasyCMS;
|
|
1424
|
+
name?: string;
|
|
1557
1425
|
}) => Promise<IntegrationTestResult> | IntegrationTestResult;
|
|
1426
|
+
/**
|
|
1427
|
+
* Asked before the first connection (#357), e.g. that text sent to an AI provider leaves the
|
|
1428
|
+
* site: an admin accepts it, and who did is audit-logged.
|
|
1429
|
+
*/
|
|
1430
|
+
readonly consent?: {
|
|
1431
|
+
readonly text: Label;
|
|
1432
|
+
readonly policy?: string;
|
|
1433
|
+
};
|
|
1558
1434
|
}
|
|
1559
1435
|
/** A connection as plugins read it: its values, secrets decrypted. */
|
|
1560
1436
|
interface IntegrationConnection {
|
|
@@ -1573,6 +1449,12 @@ interface AdminConnection {
|
|
|
1573
1449
|
lastError: string | null;
|
|
1574
1450
|
/** Plain values; secrets as `••••1234`. */
|
|
1575
1451
|
values: Record<string, string>;
|
|
1452
|
+
/** AI providers (#357): requests and tokens this month. */
|
|
1453
|
+
usage?: {
|
|
1454
|
+
requests: number;
|
|
1455
|
+
input: number;
|
|
1456
|
+
output: number;
|
|
1457
|
+
};
|
|
1576
1458
|
}
|
|
1577
1459
|
/** A field as the admin gets it: without its functions. */
|
|
1578
1460
|
type AdminIntegrationField = Omit<IntegrationField, 'lookup'> & {
|
|
@@ -1583,7 +1465,7 @@ interface AdminIntegration {
|
|
|
1583
1465
|
label: Label;
|
|
1584
1466
|
category: IntegrationCategory;
|
|
1585
1467
|
description?: Label;
|
|
1586
|
-
icon?: string;
|
|
1468
|
+
icon?: string | IntegrationLogo;
|
|
1587
1469
|
docs?: string;
|
|
1588
1470
|
plugin?: string;
|
|
1589
1471
|
kind: 'fields' | 'oauth';
|
|
@@ -1593,6 +1475,15 @@ interface AdminIntegration {
|
|
|
1593
1475
|
envFields: string[];
|
|
1594
1476
|
testable: boolean;
|
|
1595
1477
|
connections: AdminConnection[];
|
|
1478
|
+
/** Asked before the first connection; `consented` says who accepted it. */
|
|
1479
|
+
consent?: {
|
|
1480
|
+
text: Label;
|
|
1481
|
+
policy?: string;
|
|
1482
|
+
};
|
|
1483
|
+
consented?: {
|
|
1484
|
+
at: string;
|
|
1485
|
+
by: string;
|
|
1486
|
+
} | null;
|
|
1596
1487
|
}
|
|
1597
1488
|
/** What the code configures: shown, not changed, on the Integrations page. */
|
|
1598
1489
|
interface ConfiguredService {
|
|
@@ -1673,6 +1564,18 @@ declare class Integrations {
|
|
|
1673
1564
|
setEnabled(id: string, enabled: boolean, user: AuthUser): Promise<{
|
|
1674
1565
|
enabled: boolean;
|
|
1675
1566
|
}>;
|
|
1567
|
+
/** Who accepted the integration's `consent`, and when; `null` before anyone did. */
|
|
1568
|
+
private consented;
|
|
1569
|
+
private checkConsent;
|
|
1570
|
+
/** An admin accepts the integration's `consent`; audit-logged. */
|
|
1571
|
+
consent(id: string, user: AuthUser): Promise<{
|
|
1572
|
+
consented: {
|
|
1573
|
+
at: string;
|
|
1574
|
+
by: string;
|
|
1575
|
+
};
|
|
1576
|
+
}>;
|
|
1577
|
+
/** The integration's own row (on or off, consent), made when first needed. */
|
|
1578
|
+
private saveIntegration;
|
|
1676
1579
|
/** Checks a connection's values with the provider; the card shows the result. */
|
|
1677
1580
|
test(id: string, cid: ID | 'env'): Promise<IntegrationTestResult>;
|
|
1678
1581
|
/**
|
|
@@ -1689,9 +1592,9 @@ declare class Integrations {
|
|
|
1689
1592
|
})[];
|
|
1690
1593
|
}>;
|
|
1691
1594
|
/** The provider's sign-in page to send the admin to. */
|
|
1692
|
-
authorize(id: string, user: AuthUser, redirectUri: string, name: string): {
|
|
1595
|
+
authorize(id: string, user: AuthUser, redirectUri: string, name: string): Promise<{
|
|
1693
1596
|
url: string;
|
|
1694
|
-
}
|
|
1597
|
+
}>;
|
|
1695
1598
|
/**
|
|
1696
1599
|
* Back from the provider: one account is connected at once; several wait (encrypted, 10
|
|
1697
1600
|
* minutes) for the admin to choose. Returns where the admin goes next.
|
|
@@ -1750,6 +1653,8 @@ interface RolePermissions {
|
|
|
1750
1653
|
readonly collections?: FieldRules;
|
|
1751
1654
|
readonly globals?: FieldRules;
|
|
1752
1655
|
};
|
|
1656
|
+
/** AI requests a user of the role makes a day (with `ai`); 0: none. Unset: everyone's limit. */
|
|
1657
|
+
readonly aiPerDay?: number;
|
|
1753
1658
|
}
|
|
1754
1659
|
/** One role as Settings → Roles shows it (`GET <api>/admin/roles`). */
|
|
1755
1660
|
interface AdminRole {
|
|
@@ -1799,6 +1704,8 @@ interface AdminRoles {
|
|
|
1799
1704
|
})[];
|
|
1800
1705
|
globals: AdminRoleTarget[];
|
|
1801
1706
|
views: AdminRoleView[];
|
|
1707
|
+
/** Roles can be given a daily AI limit (`ai` in the config). */
|
|
1708
|
+
ai: boolean;
|
|
1802
1709
|
}
|
|
1803
1710
|
/** One change to a role (`GET <api>/admin/roles/:id/history`). */
|
|
1804
1711
|
interface AdminRoleChange {
|
|
@@ -1898,6 +1805,8 @@ declare class Roles {
|
|
|
1898
1805
|
/** Sets one field of every matching document, keeping the rest as stored. */
|
|
1899
1806
|
private setWhere;
|
|
1900
1807
|
private cleanName;
|
|
1808
|
+
/** The role's daily AI limit set in Settings → Roles, if any (`auth.rbac`). */
|
|
1809
|
+
aiLimit(key: string): Promise<number | undefined>;
|
|
1901
1810
|
private permissionsOf;
|
|
1902
1811
|
private load;
|
|
1903
1812
|
private rows;
|
|
@@ -2220,6 +2129,8 @@ declare class EasyCMS<C extends Config = Config> {
|
|
|
2220
2129
|
readonly folders: MediaFolders;
|
|
2221
2130
|
/** Accounts connected to plugins' integrations (`integrations`, Settings → Integrations). */
|
|
2222
2131
|
readonly integrations: Integrations;
|
|
2132
|
+
/** AI for the admin's features (#357): `null` without an AI plugin (`ai` in the config). */
|
|
2133
|
+
readonly ai: Ai | null;
|
|
2223
2134
|
private readonly versions;
|
|
2224
2135
|
private readonly webhooks;
|
|
2225
2136
|
private readonly mailer;
|
|
@@ -2770,155 +2681,472 @@ interface LivePreview<T> {
|
|
|
2770
2681
|
/** From the collection's `preview` function; `null` without one. */
|
|
2771
2682
|
readonly url: string | null;
|
|
2772
2683
|
}
|
|
2773
|
-
|
|
2774
|
-
/**
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
readonly
|
|
2684
|
+
|
|
2685
|
+
/** A label shown in the admin UI. Either one string or one string per admin locale. */
|
|
2686
|
+
type Label = string | {
|
|
2687
|
+
readonly [locale: string]: string;
|
|
2688
|
+
};
|
|
2689
|
+
interface FieldValidateContext {
|
|
2690
|
+
/** The whole document being validated, including sibling fields. */
|
|
2691
|
+
readonly data: Readonly<Record<string, unknown>>;
|
|
2692
|
+
readonly operation: 'create' | 'update';
|
|
2693
|
+
}
|
|
2694
|
+
/** Return `true` when valid, or an error message to show on the field. */
|
|
2695
|
+
type FieldValidate<TValue> = (value: TValue | null | undefined, ctx: FieldValidateContext) => true | string | Promise<true | string>;
|
|
2696
|
+
/**
|
|
2697
|
+
* A Web Component from an admin module (`admin.modules`), by its tag name, which must start
|
|
2698
|
+
* with `ecms-`. `props` (plain JSON) reach the element as its `options` property.
|
|
2699
|
+
*/
|
|
2700
|
+
type AdminComponent = string | {
|
|
2701
|
+
readonly tag: string;
|
|
2702
|
+
readonly props?: Readonly<Record<string, unknown>>;
|
|
2703
|
+
};
|
|
2704
|
+
/** How the admin shows a field, with components from admin modules. */
|
|
2705
|
+
interface FieldAdmin {
|
|
2706
|
+
/** Help below the field's label. */
|
|
2707
|
+
readonly description?: Label;
|
|
2708
|
+
/** Its share of a row (`admin.layout`'s `row`). Default: an equal share. */
|
|
2709
|
+
readonly width?: '1/4' | '1/3' | '1/2' | '2/3' | '3/4' | 'full';
|
|
2710
|
+
/**
|
|
2711
|
+
* Shown only when its sibling fields match, e.g. `{ field: 'linkType', equals: 'external' }`.
|
|
2712
|
+
* A hidden field isn't required. See `FieldCondition`.
|
|
2713
|
+
*/
|
|
2714
|
+
readonly condition?: FieldCondition;
|
|
2715
|
+
/** Shown instead of the field's input; the admin keeps the label and error messages. */
|
|
2716
|
+
readonly component?: AdminComponent;
|
|
2717
|
+
/** Shown below the field, e.g. a length meter or a preview. */
|
|
2718
|
+
readonly after?: readonly AdminComponent[];
|
|
2719
|
+
/** Shows the value in the admin's lists (e.g. a color swatch). Default: the plain value. */
|
|
2720
|
+
readonly cell?: AdminComponent;
|
|
2721
|
+
/**
|
|
2722
|
+
* A column of the list at first (each user can change it): `true`, or per request, e.g.
|
|
2723
|
+
* only when the admin shows every tenant.
|
|
2724
|
+
*/
|
|
2725
|
+
readonly column?: boolean | ((args: AdminFieldArgs) => boolean | Promise<boolean>);
|
|
2726
|
+
/** Relationships: offer to create the related document in place. Default true. */
|
|
2727
|
+
readonly allowCreate?: boolean;
|
|
2728
|
+
/**
|
|
2729
|
+
* The value a new document's form starts with, worked out for each user, e.g. from the
|
|
2730
|
+
* request's context. The server's own default (`defaultValue`, hooks) still applies.
|
|
2731
|
+
*/
|
|
2732
|
+
readonly initialValue?: (args: AdminFieldArgs) => unknown;
|
|
2733
|
+
/** `sidebar`: shown in the edit page's side panel instead of the main form (top-level fields). */
|
|
2734
|
+
readonly position?: 'sidebar';
|
|
2735
|
+
/** `false`: the list's filters don't offer it. Default: every field the user may read. */
|
|
2736
|
+
readonly filter?: boolean;
|
|
2737
|
+
}
|
|
2738
|
+
/** What per-request admin options of a field receive. */
|
|
2739
|
+
interface AdminFieldArgs {
|
|
2740
|
+
readonly user: AuthUser;
|
|
2741
|
+
readonly context: RequestContext;
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Field types that packages add (`fieldTypes` in the config), for the types inferred from the
|
|
2745
|
+
* config. A package declares its own by augmenting this interface:
|
|
2746
|
+
*
|
|
2747
|
+
* ```ts
|
|
2748
|
+
* declare module '@easy-cms/core' {
|
|
2749
|
+
* interface CustomFieldTypes {
|
|
2750
|
+
* color: { value: string; options: { readonly presets?: readonly string[] } }
|
|
2751
|
+
* }
|
|
2752
|
+
* }
|
|
2753
|
+
* ```
|
|
2754
|
+
*/
|
|
2755
|
+
interface CustomFieldTypes {
|
|
2756
|
+
}
|
|
2757
|
+
/** A field of a type from `CustomFieldTypes`: the common options, plus the type's own. */
|
|
2758
|
+
type CustomField = {
|
|
2759
|
+
[K in keyof CustomFieldTypes]: BaseField<K & string, CustomFieldTypes[K] extends {
|
|
2760
|
+
value: infer V;
|
|
2761
|
+
} ? V : unknown> & (CustomFieldTypes[K] extends {
|
|
2762
|
+
options: infer O;
|
|
2763
|
+
} ? O : unknown);
|
|
2764
|
+
}[keyof CustomFieldTypes];
|
|
2765
|
+
interface BaseField<TType extends string, TValue> {
|
|
2766
|
+
readonly type: TType;
|
|
2767
|
+
readonly name: string;
|
|
2768
|
+
readonly label?: Label;
|
|
2769
|
+
readonly required?: boolean;
|
|
2770
|
+
readonly unique?: boolean;
|
|
2771
|
+
/**
|
|
2772
|
+
* With `unique` (or on a slug field): a sibling field (e.g. `parent` or `tenant`), or several,
|
|
2773
|
+
* whose documents only need to differ among themselves, so `/about/team` and `/careers/team`
|
|
2774
|
+
* can both be `team`. Single, unlocalized relationship, select, text or number fields.
|
|
2775
|
+
*/
|
|
2776
|
+
readonly uniqueWithin?: string | readonly string[];
|
|
2777
|
+
/**
|
|
2778
|
+
* `true` for a database index (filters and sorts on this field). `'trigram'`, on text, textarea
|
|
2779
|
+
* and email fields: an index for `like`, which finds any part of the text, in Thai as in other
|
|
2780
|
+
* languages. Postgres only (pg_trgm); nothing on SQLite. There, `like` needs at least three
|
|
2781
|
+
* characters, and the admin's searches wait for them.
|
|
2782
|
+
*/
|
|
2783
|
+
readonly index?: boolean | 'trigram';
|
|
2784
|
+
readonly defaultValue?: TValue;
|
|
2785
|
+
readonly validate?: FieldValidate<TValue>;
|
|
2786
|
+
readonly access?: FieldAccess;
|
|
2787
|
+
/** Stored but never returned by the API nor accepted as input (e.g. a password hash). */
|
|
2788
|
+
readonly hidden?: boolean;
|
|
2789
|
+
/**
|
|
2790
|
+
* One value per locale (needs `localization` in the config). Not for groups, arrays or
|
|
2791
|
+
* hasMany fields: localize the fields inside a group or array instead.
|
|
2792
|
+
*/
|
|
2793
|
+
readonly localized?: boolean;
|
|
2794
|
+
/** How the field looks in the admin: components, position, width, conditions. */
|
|
2795
|
+
readonly admin?: FieldAdmin;
|
|
2796
|
+
/** Set by Easy CMS on fields of an added type (`fieldTypes`): the type's name, e.g. `color`. */
|
|
2797
|
+
readonly customType?: string;
|
|
2798
|
+
}
|
|
2799
|
+
interface TextField extends BaseField<'text', string> {
|
|
2800
|
+
readonly minLength?: number;
|
|
2801
|
+
readonly maxLength?: number;
|
|
2802
|
+
}
|
|
2803
|
+
interface TextareaField extends BaseField<'textarea', string> {
|
|
2804
|
+
readonly minLength?: number;
|
|
2805
|
+
readonly maxLength?: number;
|
|
2806
|
+
}
|
|
2807
|
+
interface NumberField extends BaseField<'number', number> {
|
|
2808
|
+
readonly min?: number;
|
|
2809
|
+
readonly max?: number;
|
|
2810
|
+
}
|
|
2811
|
+
interface BooleanField extends BaseField<'boolean', boolean> {
|
|
2812
|
+
}
|
|
2813
|
+
/** Stored and returned as an ISO 8601 string. */
|
|
2814
|
+
interface DateField extends BaseField<'date', string> {
|
|
2815
|
+
}
|
|
2816
|
+
interface EmailField extends BaseField<'email', string> {
|
|
2817
|
+
}
|
|
2818
|
+
interface JsonField extends BaseField<'json', unknown> {
|
|
2819
|
+
}
|
|
2820
|
+
type SelectOption = string | {
|
|
2821
|
+
readonly label: Label;
|
|
2822
|
+
readonly value: string;
|
|
2823
|
+
};
|
|
2824
|
+
interface SelectField extends BaseField<'select', string | readonly string[]> {
|
|
2825
|
+
readonly options: readonly SelectOption[];
|
|
2826
|
+
readonly hasMany?: boolean;
|
|
2827
|
+
}
|
|
2828
|
+
interface SlugField extends BaseField<'slug', string> {
|
|
2829
|
+
/** Name of a sibling `text` field to generate the slug from. */
|
|
2830
|
+
readonly from?: string;
|
|
2831
|
+
}
|
|
2832
|
+
/** Tiptap / ProseMirror JSON document. */
|
|
2833
|
+
interface RichTextDocument {
|
|
2834
|
+
readonly type: 'doc';
|
|
2835
|
+
readonly content?: readonly unknown[];
|
|
2836
|
+
}
|
|
2837
|
+
interface RichTextField extends BaseField<'richText', RichTextDocument> {
|
|
2838
|
+
/**
|
|
2839
|
+
* Blocks editors place between paragraphs (#328): a card, a callout, a video. Each is a node
|
|
2840
|
+
* `{ type: 'block', attrs: { blockType, id, fields } }` in the document.
|
|
2841
|
+
*/
|
|
2842
|
+
readonly blocks?: readonly RichTextBlock[];
|
|
2843
|
+
}
|
|
2844
|
+
/** Arguments of a rich text block's `admin.summary` function. */
|
|
2845
|
+
interface BlockSummaryArgs {
|
|
2846
|
+
readonly fields: Record<string, unknown>;
|
|
2847
|
+
readonly user: AuthUser | null;
|
|
2848
|
+
readonly cms: EasyCMS;
|
|
2849
|
+
}
|
|
2850
|
+
/** A block in rich text (`richText.blocks`): the fields of `Block`, and how its card reads. */
|
|
2851
|
+
interface RichTextBlock extends Block {
|
|
2852
|
+
readonly admin?: {
|
|
2853
|
+
/**
|
|
2854
|
+
* The text of the block's card in the editor: a function, run on the server, or the tag of a
|
|
2855
|
+
* custom element from an admin module, which gets `fields`. Default: the block's label and
|
|
2856
|
+
* its first value.
|
|
2857
|
+
*/
|
|
2858
|
+
readonly summary?: string | ((args: BlockSummaryArgs) => string | Promise<string>);
|
|
2859
|
+
};
|
|
2860
|
+
}
|
|
2861
|
+
/** References a document in the built-in `media` collection: a file or an image. */
|
|
2862
|
+
interface UploadField extends BaseField<'upload', never> {
|
|
2863
|
+
/** Several files, e.g. a gallery: the value is a list, in the order editors arrange. */
|
|
2864
|
+
readonly hasMany?: boolean;
|
|
2865
|
+
/** With `hasMany`: the fewest files. */
|
|
2866
|
+
readonly minRows?: number;
|
|
2867
|
+
/** With `hasMany`: the most files. */
|
|
2868
|
+
readonly maxRows?: number;
|
|
2869
|
+
/**
|
|
2870
|
+
* The file types allowed, e.g. `['image/*']` or `['image/*', 'application/pdf']`. The admin
|
|
2871
|
+
* offers only these, and saving refuses others.
|
|
2872
|
+
*/
|
|
2873
|
+
readonly mimeTypes?: readonly string[];
|
|
2874
|
+
/**
|
|
2875
|
+
* With `upload.folders`: the `key` of the media folder this field's files go in, e.g.
|
|
2876
|
+
* `'banners'`. Its picker opens there and its uploads land there; the folder is made when it
|
|
2877
|
+
* is first needed.
|
|
2878
|
+
*/
|
|
2879
|
+
readonly folder?: string;
|
|
2880
|
+
/** With `folder`: only files in that folder (and its subfolders) may be chosen. */
|
|
2881
|
+
readonly folderOnly?: boolean;
|
|
2882
|
+
/** Which media documents may be chosen, as for relationships: offered and checked on save. */
|
|
2883
|
+
readonly filterOptions?: FilterOptions;
|
|
2884
|
+
}
|
|
2885
|
+
interface FilterOptionsArgs {
|
|
2886
|
+
/** The document being edited; `undefined` while it is being created. */
|
|
2887
|
+
readonly id: ID | undefined;
|
|
2888
|
+
/** The collection the choices come from: one of the field's `to`. */
|
|
2889
|
+
readonly relationTo: string;
|
|
2890
|
+
readonly user: AuthUser | null;
|
|
2891
|
+
readonly cms: EasyCMS;
|
|
2892
|
+
/** The request's context (`onRequest`), `{}` when there is none. */
|
|
2893
|
+
readonly context: RequestContext;
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2896
|
+
* Which documents a relationship may point to: a `where` on the target collection, or `true`
|
|
2897
|
+
* for any. Runs on the server: the admin's picker offers only these, and saving checks them.
|
|
2898
|
+
*/
|
|
2899
|
+
type FilterOptions = (args: FilterOptionsArgs) => Where | true | Promise<Where | true>;
|
|
2900
|
+
interface RelationshipField extends BaseField<'relationship', never> {
|
|
2782
2901
|
/**
|
|
2783
|
-
*
|
|
2784
|
-
*
|
|
2785
|
-
* entries; others see the chosen part's, or all.
|
|
2902
|
+
* Slug of the target collection, or several: then each value is `{ relationTo, value }`, the
|
|
2903
|
+
* collection and the document's id (#327).
|
|
2786
2904
|
*/
|
|
2787
|
-
readonly
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2905
|
+
readonly to: string | readonly string[];
|
|
2906
|
+
readonly hasMany?: boolean;
|
|
2907
|
+
/** With `hasMany`: the fewest documents. */
|
|
2908
|
+
readonly minRows?: number;
|
|
2909
|
+
/** With `hasMany`: the most documents. */
|
|
2910
|
+
readonly maxRows?: number;
|
|
2911
|
+
readonly filterOptions?: FilterOptions;
|
|
2791
2912
|
}
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
after?: unknown;
|
|
2913
|
+
interface ArrayField extends BaseField<'array', never> {
|
|
2914
|
+
readonly fields: readonly Field[];
|
|
2915
|
+
readonly minRows?: number;
|
|
2916
|
+
readonly maxRows?: number;
|
|
2797
2917
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
/**
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
doc: string | null;
|
|
2809
|
-
/** The document's title then (kept after it is deleted). */
|
|
2810
|
-
title: string | null;
|
|
2811
|
-
actor: {
|
|
2812
|
-
id: string | null;
|
|
2813
|
-
email: string | null;
|
|
2814
|
-
/** `user` (admin or REST), `api-key`, `system` (code), `scheduler`. */
|
|
2815
|
-
via: string;
|
|
2816
|
-
ip: string | null;
|
|
2817
|
-
userAgent: string | null;
|
|
2918
|
+
interface GroupField extends BaseField<'group', never> {
|
|
2919
|
+
readonly fields: readonly Field[];
|
|
2920
|
+
}
|
|
2921
|
+
/** One kind of block in a `blocks` field. */
|
|
2922
|
+
interface Block {
|
|
2923
|
+
/** Stored in each row as `blockType`. */
|
|
2924
|
+
readonly slug: string;
|
|
2925
|
+
readonly labels?: {
|
|
2926
|
+
readonly singular?: Label;
|
|
2927
|
+
readonly plural?: Label;
|
|
2818
2928
|
};
|
|
2819
|
-
|
|
2820
|
-
detail: Record<string, unknown> | null;
|
|
2929
|
+
readonly fields: readonly Field[];
|
|
2821
2930
|
}
|
|
2822
|
-
/**
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
actor?: string | null;
|
|
2831
|
-
/** ISO dates. */
|
|
2832
|
-
from?: string | null;
|
|
2833
|
-
to?: string | null;
|
|
2834
|
-
/** Only entries of this part of the site (`scope`). */
|
|
2835
|
-
scope?: string | null;
|
|
2931
|
+
/**
|
|
2932
|
+
* A list of rows, each one of several block kinds (e.g. hero, text, gallery), for pages that
|
|
2933
|
+
* editors lay out themselves. Rows are `{ id, blockType, ...fields }`, stored as JSON.
|
|
2934
|
+
*/
|
|
2935
|
+
interface BlocksField extends BaseField<'blocks', never> {
|
|
2936
|
+
readonly blocks: readonly Block[];
|
|
2937
|
+
readonly minRows?: number;
|
|
2938
|
+
readonly maxRows?: number;
|
|
2836
2939
|
}
|
|
2837
|
-
/** `
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2940
|
+
/** The fields `uniqueWithin` names, as a list. */
|
|
2941
|
+
declare const uniqueWithinOf: (field: Field) => readonly string[];
|
|
2942
|
+
/** Fields with `hasMany`: their value is a list of options, documents or files. */
|
|
2943
|
+
declare const isHasMany: (field: Field) => boolean;
|
|
2944
|
+
/**
|
|
2945
|
+
* Whether a MIME type is one of `patterns` (`image/*` matches every image but SVG, which can
|
|
2946
|
+
* carry scripts and is allowed only when listed by name: `image/svg+xml`).
|
|
2947
|
+
*/
|
|
2948
|
+
declare function mimeAllowedBy(mimeType: string, patterns: readonly string[]): boolean;
|
|
2949
|
+
/** A relationship to several collections: its values are `{ relationTo, value }` (#327). */
|
|
2950
|
+
declare const isPolymorphic: (field: Field) => field is RelationshipField & {
|
|
2951
|
+
readonly to: readonly string[];
|
|
2952
|
+
};
|
|
2953
|
+
/** The collections a relationship or upload field points to. */
|
|
2954
|
+
declare function relationTargets(field: Field): readonly string[];
|
|
2955
|
+
/** A `{ relationTo, value }` of a relationship to several collections, or `undefined`. */
|
|
2956
|
+
declare function polymorphicValue(value: unknown): {
|
|
2957
|
+
relationTo: string;
|
|
2958
|
+
value: unknown;
|
|
2959
|
+
} | undefined;
|
|
2960
|
+
/** Fields whose value is a list of rows: `array` and `blocks`. */
|
|
2961
|
+
declare const hasRows: (field: Field) => field is ArrayField | BlocksField;
|
|
2962
|
+
/** The fields of one row of an `array` or `blocks` field (`undefined` for an unknown block). */
|
|
2963
|
+
declare function rowFields(field: ArrayField | BlocksField, row: unknown): readonly Field[] | undefined;
|
|
2964
|
+
/**
|
|
2965
|
+
* Stores nothing: a component in the edit form (`admin.component`), e.g. a calculator, a preview
|
|
2966
|
+
* or a button. Placed like any field (layout, groups, conditions); not in the database, the API or
|
|
2967
|
+
* the generated types.
|
|
2968
|
+
*/
|
|
2969
|
+
interface UiField extends BaseField<'ui', never> {
|
|
2970
|
+
readonly admin: FieldAdmin & {
|
|
2971
|
+
readonly component: AdminComponent;
|
|
2972
|
+
};
|
|
2849
2973
|
}
|
|
2974
|
+
type Field = CustomField | TextField | TextareaField | NumberField | BooleanField | DateField | EmailField | JsonField | SelectField | SlugField | RichTextField | UploadField | RelationshipField | ArrayField | GroupField | BlocksField | UiField;
|
|
2975
|
+
type FieldType = Field['type'];
|
|
2850
2976
|
/**
|
|
2851
|
-
* The
|
|
2852
|
-
*
|
|
2853
|
-
* Available as `cms.audit`.
|
|
2977
|
+
* The shortest `like` on a field with `index: 'trigram'`: trigrams are three characters, so a
|
|
2978
|
+
* shorter search can't use the index and would read every row.
|
|
2854
2979
|
*/
|
|
2855
|
-
declare
|
|
2980
|
+
declare const TRIGRAM_LIKE_MIN = 3;
|
|
2981
|
+
declare const FIELD_TYPES: readonly ["text", "textarea", "number", "boolean", "date", "email", "json", "select", "slug", "richText", "upload", "relationship", "array", "group", "blocks", "ui"];
|
|
2982
|
+
|
|
2983
|
+
/** What a provider is asked: the prompt, images as base64, and a JSON schema for the answer. */
|
|
2984
|
+
interface AiProviderRequest {
|
|
2985
|
+
readonly system?: string;
|
|
2986
|
+
readonly prompt: string;
|
|
2987
|
+
readonly images: readonly {
|
|
2988
|
+
readonly data: string;
|
|
2989
|
+
readonly mediaType: string;
|
|
2990
|
+
}[];
|
|
2991
|
+
/** A JSON schema: the answer is JSON of this shape. */
|
|
2992
|
+
readonly json?: Readonly<Record<string, unknown>>;
|
|
2993
|
+
readonly maxTokens: number;
|
|
2994
|
+
readonly signal: AbortSignal;
|
|
2995
|
+
}
|
|
2996
|
+
interface AiProviderResult {
|
|
2997
|
+
readonly text: string;
|
|
2998
|
+
readonly usage: {
|
|
2999
|
+
readonly input: number;
|
|
3000
|
+
readonly output: number;
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
/** A provider's refusal: `status` says whether to retry (429, 5xx) or the key is wrong (401, 403). */
|
|
3004
|
+
declare class AiProviderError extends Error {
|
|
3005
|
+
readonly status: number | undefined;
|
|
3006
|
+
constructor(message: string, status?: number);
|
|
3007
|
+
}
|
|
3008
|
+
/** A provider a plugin adds: its integration (fields, Test) and how it answers. */
|
|
3009
|
+
interface AiProvider {
|
|
3010
|
+
/** The id of the integration whose connections it answers with, e.g. `anthropic`. */
|
|
3011
|
+
readonly integration: string;
|
|
3012
|
+
readonly generate: (values: Readonly<Record<string, string>>, request: AiProviderRequest) => Promise<AiProviderResult>;
|
|
3013
|
+
}
|
|
3014
|
+
/** `ai` in the config, set by the AI plugin. */
|
|
3015
|
+
interface AiConfig {
|
|
3016
|
+
readonly providers: readonly AiProvider[];
|
|
3017
|
+
readonly limits?: {
|
|
3018
|
+
/** Requests a user makes a day (in `timeZone`). Admins can change it. Default 100. */
|
|
3019
|
+
readonly perUserPerDay?: number;
|
|
3020
|
+
/** Per role, e.g. `{ editor: 50 }`; 0 keeps a role from using AI. Settings → Roles wins. */
|
|
3021
|
+
readonly roles?: Readonly<Record<string, number>>;
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
/** A feature that uses AI (`aiFeatures` in the config): admins turn it off or pick its connection. */
|
|
3025
|
+
interface AiFeatureConfig {
|
|
3026
|
+
/** Lowercase letters, digits and `-`, e.g. `alt-text`. */
|
|
3027
|
+
readonly id: string;
|
|
3028
|
+
readonly label: Label;
|
|
3029
|
+
readonly description?: Label;
|
|
3030
|
+
/** On until an admin turns it off. Default true. */
|
|
3031
|
+
readonly default?: boolean;
|
|
3032
|
+
}
|
|
3033
|
+
interface AiImage {
|
|
3034
|
+
/** A media document: its smallest size at least 1024 pixels wide, or the file. */
|
|
3035
|
+
readonly media?: ID;
|
|
3036
|
+
/** Or the image itself, as base64. */
|
|
3037
|
+
readonly data?: string;
|
|
3038
|
+
readonly mediaType?: string;
|
|
3039
|
+
}
|
|
3040
|
+
interface AiRequest {
|
|
3041
|
+
/** The feature asking (`aiFeatures`): it must be on. */
|
|
3042
|
+
readonly feature: string;
|
|
3043
|
+
readonly system?: string;
|
|
3044
|
+
readonly prompt: string;
|
|
3045
|
+
readonly images?: readonly AiImage[];
|
|
3046
|
+
/** A JSON schema: the answer is parsed into `json`. */
|
|
3047
|
+
readonly json?: Readonly<Record<string, unknown>>;
|
|
3048
|
+
/** Default 1024. */
|
|
3049
|
+
readonly maxTokens?: number;
|
|
3050
|
+
}
|
|
3051
|
+
interface AiResult {
|
|
3052
|
+
readonly text: string;
|
|
3053
|
+
readonly json?: unknown;
|
|
3054
|
+
readonly usage: {
|
|
3055
|
+
readonly input: number;
|
|
3056
|
+
readonly output: number;
|
|
3057
|
+
};
|
|
3058
|
+
/** The connection that answered, e.g. `anthropic:Main`. */
|
|
3059
|
+
readonly connection: string;
|
|
3060
|
+
}
|
|
3061
|
+
interface AdminAi {
|
|
3062
|
+
main: string | null;
|
|
3063
|
+
perUserPerDay: number;
|
|
3064
|
+
/** The config's, for "back to the default". */
|
|
3065
|
+
defaultPerUserPerDay: number;
|
|
3066
|
+
connections: {
|
|
3067
|
+
id: string;
|
|
3068
|
+
integration: string;
|
|
3069
|
+
name: string;
|
|
3070
|
+
label: Label;
|
|
3071
|
+
}[];
|
|
3072
|
+
features: {
|
|
3073
|
+
id: string;
|
|
3074
|
+
label: Label;
|
|
3075
|
+
description?: Label;
|
|
3076
|
+
enabled: boolean;
|
|
3077
|
+
connection: string | null;
|
|
3078
|
+
}[];
|
|
3079
|
+
/** This month, in `timeZone`. */
|
|
3080
|
+
usage: {
|
|
3081
|
+
byUser: {
|
|
3082
|
+
user: string;
|
|
3083
|
+
requests: number;
|
|
3084
|
+
input: number;
|
|
3085
|
+
output: number;
|
|
3086
|
+
}[];
|
|
3087
|
+
byFeature: {
|
|
3088
|
+
feature: string;
|
|
3089
|
+
requests: number;
|
|
3090
|
+
input: number;
|
|
3091
|
+
output: number;
|
|
3092
|
+
}[];
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
declare class Ai {
|
|
2856
3096
|
private readonly cms;
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
get
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
detail?: Record<string, unknown> | null;
|
|
2876
|
-
/** The call's context, when it is not the request's. */
|
|
2877
|
-
context?: RequestContext;
|
|
2878
|
-
}): Promise<void>;
|
|
2879
|
-
/** Entries, newest first: `page` (from 1) of `limit` (default 50, at most 100). */
|
|
2880
|
-
list(options?: AuditFilter & {
|
|
2881
|
-
page?: number;
|
|
2882
|
-
limit?: number;
|
|
2883
|
-
}): Promise<AuditPage>;
|
|
2884
|
-
/** The filtered entries as CSV (the newest 10,000). */
|
|
2885
|
-
csv(filter: AuditFilter): Promise<string>;
|
|
2886
|
-
/** Checks every entry's signature, and that no ids are missing. */
|
|
2887
|
-
verify(): Promise<AuditVerification>;
|
|
2888
|
-
/** The secret an entry was signed with: the current one, a previous one, or none (changed). */
|
|
2889
|
-
private signer;
|
|
2890
|
-
/**
|
|
2891
|
-
* After `secret` changed: signs again, with the current secret, the entries signed with one of
|
|
2892
|
-
* `previousSecrets`, so they still verify once it is removed. Entries no secret verifies were
|
|
2893
|
-
* changed: they are left as they are, and reported. Records `audit.resign`.
|
|
2894
|
-
*/
|
|
2895
|
-
resign(): Promise<{
|
|
2896
|
-
checked: number;
|
|
2897
|
-
resigned: number;
|
|
2898
|
-
invalid: ID[];
|
|
3097
|
+
private readonly ai;
|
|
3098
|
+
constructor(cms: EasyCMS, ai: AiConfig);
|
|
3099
|
+
private get config();
|
|
3100
|
+
private get defaultLimit();
|
|
3101
|
+
/** The features of the config. */
|
|
3102
|
+
get features(): readonly AiFeatureConfig[];
|
|
3103
|
+
private settingsRow;
|
|
3104
|
+
private settings;
|
|
3105
|
+
/** Every connection of every provider, in the providers' order. */
|
|
3106
|
+
private connections;
|
|
3107
|
+
/** Whether a feature is on: admins' choice, else its `default`. */
|
|
3108
|
+
enabled(feature: string): Promise<boolean>;
|
|
3109
|
+
/** Requests a user may make today: their role's, else everyone's. */
|
|
3110
|
+
limitFor(user: AuthUser): Promise<number>;
|
|
3111
|
+
/** Requests the user made today (in `timeZone`) and how many are left. */
|
|
3112
|
+
remaining(user: AuthUser, now?: Date): Promise<{
|
|
3113
|
+
used: number;
|
|
3114
|
+
limit: number;
|
|
2899
3115
|
}>;
|
|
2900
3116
|
/**
|
|
2901
|
-
*
|
|
2902
|
-
*
|
|
3117
|
+
* Asks the feature's connection (or the main one). With `user`, the request counts toward their
|
|
3118
|
+
* daily limit; it is refused once they reach it. Usage is recorded, never the prompt.
|
|
2903
3119
|
*/
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
private
|
|
2920
|
-
|
|
2921
|
-
private
|
|
3120
|
+
generate(request: AiRequest, options?: {
|
|
3121
|
+
user?: AuthUser | null;
|
|
3122
|
+
about?: {
|
|
3123
|
+
collection: string;
|
|
3124
|
+
id: ID;
|
|
3125
|
+
};
|
|
3126
|
+
}): Promise<AiResult>;
|
|
3127
|
+
/**
|
|
3128
|
+
* The Test button of a provider's connection: a short prompt; its tokens count on the card,
|
|
3129
|
+
* not toward anyone's limit.
|
|
3130
|
+
*/
|
|
3131
|
+
test(integration: string, values: Readonly<Record<string, string>>, name?: string): Promise<IntegrationTestResult>;
|
|
3132
|
+
private connectionFor;
|
|
3133
|
+
private ask;
|
|
3134
|
+
/** One try, and another after a second when the provider is busy or failing (429, 5xx). */
|
|
3135
|
+
private call;
|
|
3136
|
+
/** An image as base64: given, or read from a media document. */
|
|
3137
|
+
private image;
|
|
3138
|
+
/** Settings → Integrations, AI: the main connection, the features, the limit and this month. */
|
|
3139
|
+
overview(now?: Date): Promise<AdminAi>;
|
|
3140
|
+
/** Tokens and requests of each connection this month, for the cards. */
|
|
3141
|
+
usageByConnection(now?: Date): Promise<Map<string, {
|
|
3142
|
+
requests: number;
|
|
3143
|
+
input: number;
|
|
3144
|
+
output: number;
|
|
3145
|
+
}>>;
|
|
3146
|
+
/** Saves admins' choices; audit-logged. */
|
|
3147
|
+
update(body: unknown, user: AuthUser): Promise<AdminAi>;
|
|
3148
|
+
/** Forgets usage older than 90 days (with the scheduled jobs). */
|
|
3149
|
+
upkeep(now?: Date): Promise<void>;
|
|
2922
3150
|
}
|
|
2923
3151
|
|
|
2924
3152
|
/** What the email functions in `auth.emails` receive. */
|
|
@@ -4189,9 +4417,19 @@ interface Config {
|
|
|
4189
4417
|
* by plugins: a Telegram bot, an AI provider, a Facebook Page. See `IntegrationConfig`.
|
|
4190
4418
|
*/
|
|
4191
4419
|
readonly integrations?: readonly IntegrationConfig[];
|
|
4420
|
+
/**
|
|
4421
|
+
* AI providers and limits (#357), set by `@easy-cms/plugin-ai`: `cms.ai` answers the admin's AI
|
|
4422
|
+
* features with them. See `AiConfig`.
|
|
4423
|
+
*/
|
|
4424
|
+
readonly ai?: AiConfig;
|
|
4425
|
+
/**
|
|
4426
|
+
* Features that use AI, added by plugins and by Easy CMS itself: admins turn each off or pick
|
|
4427
|
+
* the connection it uses on the Integrations page. See `AiFeatureConfig`.
|
|
4428
|
+
*/
|
|
4429
|
+
readonly aiFeatures?: readonly AiFeatureConfig[];
|
|
4192
4430
|
}
|
|
4193
4431
|
/** The config after plugins ran, validation passed and defaults were applied. */
|
|
4194
|
-
interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins' | 'audit' | 'integrations'> {
|
|
4432
|
+
interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins' | 'audit' | 'integrations' | 'aiFeatures'> {
|
|
4195
4433
|
readonly cors: readonly string[] | '*';
|
|
4196
4434
|
/** `null` without localization. */
|
|
4197
4435
|
readonly localization: Required<LocalizationConfig> | null;
|
|
@@ -4205,6 +4443,7 @@ interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes'
|
|
|
4205
4443
|
readonly globals: readonly GlobalConfig[];
|
|
4206
4444
|
readonly endpoints: readonly Endpoint[];
|
|
4207
4445
|
readonly integrations: readonly IntegrationConfig[];
|
|
4446
|
+
readonly aiFeatures: readonly AiFeatureConfig[];
|
|
4208
4447
|
readonly cliCommands: readonly CliCommand[];
|
|
4209
4448
|
readonly events: readonly string[];
|
|
4210
4449
|
readonly jobs: readonly JobConfig[];
|
|
@@ -4220,5 +4459,5 @@ interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes'
|
|
|
4220
4459
|
*/
|
|
4221
4460
|
declare function defineConfig<const TConfig extends Config>(config: TConfig): TConfig;
|
|
4222
4461
|
|
|
4223
|
-
export { ADAPTER_API_VERSION as A, EasyCMS as E,
|
|
4224
|
-
export type {
|
|
4462
|
+
export { ADAPTER_API_VERSION as A, EasyCMS as E, Ai as Q, AiProviderError as Y, AuditLog as a9, ConfigError as aE, EasyCMSError as aV, Auth as ac, FIELD_TYPES as b2, NotFoundError as bU, PLUGIN_API_VERSION as b_, ForbiddenError as bm, PayloadTooLargeError as c3, QueryError as c9, SingleSignOn as cE, TRIGRAM_LIKE_MIN as cL, TooManyRequestsError as cP, UnauthorizedError as cS, ValidationError as cZ, ROLE_OPERATIONS as ca, Roles as cm, SchemaError as cp, WEBHOOK_EVENTS as d1, anyone as d5, consoleEmail as d6, consoleLogger as d7, createEasyCMS as d8, defineConfig as d9, MediaFolders as dA, SSO_COOKIE as dB, codeOfStatus as dD, isJsonLogger as dE, keyAllows as dF, logFormat as dG, secretsOf as dH, defineFieldType as da, definePlugin as db, diskStorage as dc, hasRows as dd, isAdmin as de, isHasMany as df, isPolymorphic as dg, isSignedIn as dh, isStaff as di, isSystemAdmin as dj, jsonLogger as dk, memorySearch as dl, mimeAllowedBy as dm, polymorphicValue as dn, relationTargets as dp, rowFields as dq, silentLogger as dr, uniqueWithinOf as ds, API_KEYS as dt, API_KEY_GLOBAL_OPERATIONS as du, API_KEY_OPERATIONS as dv, DEFAULT_MAX_VERSIONS as dw, DEFAULT_PASSWORD_EMAILS as dx, FIELD_TYPE_BASES as dy, FOLDER_LEVELS as dz, ADMIN_ICONS as j };
|
|
4463
|
+
export type { AiRequest as $, AdminRoleView as B, Config as C, AdminRoles as D, Field as F, GlobalSlug as G, AdminSso as H, ID as I, AdminSwitcher as J, AdminViewAccess as K, LocalizationConfig as L, AfterChangeHook as M, AfterDeleteHook as N, AfterReadHook as O, PaginatedDocs as P, ResolvedConfig as R, AiConfig as S, AiFeatureConfig as T, UncountedDocs as U, AiImage as V, Where as W, AiProvider as X, AiProviderRequest as Z, AiProviderResult as _, CollectionSlug as a, ErrorCode as a$, AiResult as a0, ApiKeyContext as a1, ApiKeyGlobalOperation as a2, ApiKeyOperation as a3, ApiKeyPermissions as a4, ArrayField as a5, AuditChange as a6, AuditConfig as a7, AuditEntry as a8, CollectionHooks as aA, CollectionListAdmin as aB, CollectionSearch as aC, CollectionView as aD, ConfigIssue as aF, ContainerAdmin as aG, CreateInput as aH, CreateMigrationResult as aI, CustomField as aJ, CustomFieldTypes as aK, DashboardWidget as aL, Database as aM, DatabaseAdapter as aN, DatabaseInitArgs as aO, DatabaseTransfer as aP, DateField as aQ, DepthOptions as aR, DirectUpload as aS, DiskStorageOptions as aT, DocumentOf as aU, EmailAdapter as aW, EmailField as aX, EmailMessage as aY, Endpoint as aZ, EndpointRequest as a_, AuditPage as aa, AuditVerification as ab, AuthConfig as ad, AuthProvider as ae, AuthProviderProfile as af, AuthProviderRequest as ag, AuthUser as ah, BackupsConfig as ai, BeforeChangeHook as aj, BeforeDeleteHook as ak, BeforeValidateHook as al, Block as am, BlockSummaryArgs as an, BlocksField as ao, BooleanField as ap, BuiltinApiKeysCollection as aq, BuiltinMediaCollection as ar, BuiltinMediaFoldersCollection as as, BuiltinUsersCollection as at, BundleHints as au, CliCommand as av, CollectionAccess as aw, CollectionAdmin as ax, CollectionCalendarAdmin as ay, CollectionConfig as az, CollectionDocument as b, PasswordEmail as b$, EventListener as b0, EventMessage as b1, FieldAccess as b3, FieldAccessFn as b4, FieldAdmin as b5, FieldRule as b6, FieldType as b7, FieldTypeBase as b8, FieldTypeDefinition as b9, IntegrationLogo as bA, IntegrationOAuthResult as bB, IntegrationTestResult as bC, JobConfig as bD, JsonField as bE, KeysetPage as bF, Label as bG, LayoutNode as bH, LivePreview as bI, LocaleOf as bJ, LogDetails as bK, Logger as bL, LoginArgs as bM, LogsConfig as bN, MediaDocument as bO, MediaSize as bP, MembersConfig as bQ, MembersSignup as bR, MigrationInfo as bS, NavGroup as bT, NumberField as bV, OnRequest as bW, OnRequestArgs as bX, Operation as bY, OutboxRow as bZ, FieldTypeValidateContext as ba, FieldValidate as bb, FieldValidateContext as bc, FieldValue as bd, FieldsInput as be, FieldsValue as bf, FilterOptionsArgs as bg, FindAfterArgs as bh, FindArgs as bi, FindOptions as bj, FolderLevel as bk, FolderPermissions as bl, GlobalAccess as bn, GlobalConfig as bo, GlobalHooks as bp, GlobalInput as bq, GroupField as br, ImageSize as bs, IncrementOptions as bt, InferCollection as bu, InferGlobal as bv, IntegrationCategory as bw, IntegrationConfig as bx, IntegrationConnection as by, IntegrationField as bz, CursorDocs as c, VersionSummary as c$, PasswordEmailArgs as c0, PasswordEmailFn as c1, PasswordLinkOptions as c2, Plugin as c4, PluginInfo as c5, PluginTypes as c6, PreviewTarget as c7, PreviewURL as c8, Session as cA, SidebarPanel as cB, SignupArgs as cC, SignupResult as cD, SlugField as cF, SlugOf as cG, SsoOutcome as cH, SsoProviderRef as cI, StorageAdapter as cJ, StoredFile as cK, TelemetryConfig as cM, TextField as cN, TextareaField as cO, TypedPlugin as cQ, UiField as cR, UpdateInput as cT, UpdateOptions as cU, UploadConfig as cV, UploadField as cW, UploadFromURLConfig as cX, UserIdentity as cY, Version as c_, RawDocument as cb, ReadOptions as cc, RelationshipField as cd, RequestContext as ce, RichTextBlock as cf, RichTextBlocks as cg, RichTextDocument as ch, RichTextField as ci, Role as cj, RoleOperation as ck, RolePermissions as cl, RoutesConfig as cn, ScheduledPublish as co, SchemaMode as cq, SearchAdapter as cr, SearchEntry as cs, SearchHits as ct, SearchOptions as cu, SearchQuery as cv, SearchResults as cw, SearchSetting as cx, SelectField as cy, SelectOption as cz, GlobalDocument as d, VersionsConfig as d0, WebhookConfig as d2, WebhookEvent as d3, WhereOperators as d4, Secrets as dC, FieldError as e, FilterOptions as f, CreateEasyCMSOptions as g, EmailSetting as h, WebhookPayload as i, Access as k, AccessArgs as l, AccessOptions as m, AdminAi as n, AdminBrand as o, AdminCommand as p, AdminComponent as q, AdminConfig as r, AdminFieldArgs as s, AdminIcon as t, AdminLocale as u, AdminPage as v, AdminRole as w, AdminRoleChange as x, AdminRoleField as y, AdminRoleTarget as z };
|