@easy-cms/core 0.35.0 → 0.36.1

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 (3) hide show
  1. package/dist/index.d.ts +528 -357
  2. package/dist/index.js +912 -318
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -56,328 +56,6 @@ declare const anyone: Access;
56
56
  declare const isLoggedIn: Access;
57
57
  declare const isAdmin: Access;
58
58
 
59
- /** What the email functions in `auth.emails` receive. */
60
- interface PasswordEmailArgs {
61
- readonly user: AuthUser;
62
- /** The page that sets the password; absent for `passwordChanged`. */
63
- readonly url?: string;
64
- /** The admin language for the text: `en` or `th`. */
65
- readonly locale: string;
66
- /** When the link stops working. */
67
- readonly expiresAt?: Date;
68
- /**
69
- * Invitations when passwords are off (`auth.password: false`): the providers to sign in with,
70
- * e.g. `['Google']`; `url` is then the login page.
71
- */
72
- readonly sso?: readonly string[];
73
- }
74
- /** An email's content: a subject, and text and/or HTML. */
75
- interface PasswordEmail {
76
- readonly subject: string;
77
- readonly text: string;
78
- readonly html?: string;
79
- }
80
- type PasswordEmailFn = (args: PasswordEmailArgs) => PasswordEmail | Promise<PasswordEmail>;
81
- /** The default emails, in English and Thai. */
82
- declare const DEFAULT_PASSWORD_EMAILS: {
83
- resetPassword: PasswordEmailFn;
84
- invite: PasswordEmailFn;
85
- passwordChanged: PasswordEmailFn;
86
- };
87
-
88
- /**
89
- * Signing in to the admin with an outside account (`auth.providers`): Google, Microsoft, GitHub
90
- * or any OpenID Connect provider. `@easy-cms/auth-oauth` makes these; Easy CMS runs the rest
91
- * (the redirect, matching users, sessions).
92
- */
93
- /** Values bound to one sign-in, checked when the browser comes back. */
94
- interface AuthProviderRequest {
95
- /** Where the provider sends the browser back: `<serverURL><api>/auth/<id>/callback`. */
96
- readonly redirectUri: string;
97
- readonly state: string;
98
- /** For PKCE: the provider gets its S256 challenge. */
99
- readonly codeVerifier: string;
100
- /** For OpenID Connect: must come back in the ID token. */
101
- readonly nonce: string;
102
- }
103
- /** Who signed in, as the provider says. */
104
- interface AuthProviderProfile {
105
- /** The provider's stable id for the account (`sub`). */
106
- readonly subject: string;
107
- readonly email: string | null;
108
- /** The provider vouches for the email. Only verified emails are matched to users. */
109
- readonly emailVerified: boolean;
110
- readonly name?: string | undefined;
111
- }
112
- interface AuthProvider {
113
- /** In URLs and stored identities, e.g. `google`: lowercase letters, digits and `-`. */
114
- readonly id: string;
115
- /** On the sign-in button, e.g. `Google`. */
116
- readonly name: string;
117
- /** The button's icon in the admin. Default `key`. */
118
- readonly icon?: 'google' | 'microsoft' | 'github' | 'key';
119
- /** The provider's page to send the browser to. */
120
- authorizationURL(request: AuthProviderRequest): Promise<URL>;
121
- /**
122
- * After the browser comes back to `redirectUri` (`url`, with its query): checks the response
123
- * and exchanges the code. Throws when anything does not check out.
124
- */
125
- callback(request: AuthProviderRequest & {
126
- readonly url: URL;
127
- }): Promise<AuthProviderProfile>;
128
- /** For Settings → SSO: what it connects to, never secrets. */
129
- describe?(): Readonly<Record<string, string>>;
130
- }
131
-
132
- interface Logger {
133
- info(message: string): void;
134
- warn(message: string): void;
135
- error(message: string): void;
136
- }
137
- declare const consoleLogger: Logger;
138
- declare const silentLogger: Logger;
139
-
140
- /** A document as stored: relationships and uploads are ids, never populated. */
141
- type RawDocument = Record<string, unknown> & {
142
- id: ID;
143
- };
144
- interface PaginatedDocs<T> {
145
- docs: T[];
146
- totalDocs: number;
147
- limit: number;
148
- page: number;
149
- totalPages: number;
150
- hasNextPage: boolean;
151
- hasPrevPage: boolean;
152
- }
153
- /**
154
- * How the adapter treats the database schema on startup.
155
- * - `push`: apply schema changes directly (development).
156
- * - `verify`: fail if migrations are pending or the config changed without a migration (production).
157
- * - `skip`: do nothing (used by the migration CLI).
158
- */
159
- type SchemaMode = 'push' | 'verify' | 'skip';
160
- interface DatabaseInitArgs {
161
- readonly config: ResolvedConfig;
162
- /** Project root; relative paths (database file, migrations) resolve from here. */
163
- readonly cwd: string;
164
- readonly schema: SchemaMode;
165
- readonly logger: Logger;
166
- /** Whether the process can prompt the user (the CLI in a terminal). */
167
- readonly interactive?: boolean;
168
- }
169
- interface FindArgs {
170
- readonly collection: string;
171
- readonly where?: Where | undefined;
172
- /** Field paths; prefix with `-` for descending. */
173
- readonly sort: readonly string[];
174
- /** `0` returns every matching document. */
175
- readonly limit: number;
176
- readonly page: number;
177
- }
178
- interface MigrationInfo {
179
- readonly name: string;
180
- readonly applied: boolean;
181
- }
182
- interface CreateMigrationResult {
183
- readonly name: string;
184
- readonly file: string;
185
- readonly statements: readonly string[];
186
- }
187
- /** A connected database bound to one config. Returned by `DatabaseAdapter.init`. */
188
- interface Database {
189
- find(args: FindArgs): Promise<PaginatedDocs<RawDocument>>;
190
- findById(args: {
191
- collection: string;
192
- id: ID;
193
- }): Promise<RawDocument | null>;
194
- /** Documents of one collection by id, in any order. Used to populate relationships. */
195
- findByIds(args: {
196
- collection: string;
197
- ids: readonly ID[];
198
- }): Promise<RawDocument[]>;
199
- count(args: {
200
- collection: string;
201
- where?: Where | undefined;
202
- }): Promise<number>;
203
- /** `data` holds every field, `createdAt` and `updatedAt`. Runs in one transaction. */
204
- create(args: {
205
- collection: string;
206
- data: Record<string, unknown>;
207
- }): Promise<RawDocument>;
208
- /** `data` holds the whole document after merging. Runs in one transaction. */
209
- update(args: {
210
- collection: string;
211
- id: ID;
212
- data: Record<string, unknown>;
213
- }): Promise<RawDocument>;
214
- delete(args: {
215
- collection: string;
216
- id: ID;
217
- }): Promise<void>;
218
- findGlobal(args: {
219
- slug: string;
220
- }): Promise<Record<string, unknown> | null>;
221
- updateGlobal(args: {
222
- slug: string;
223
- data: Record<string, unknown>;
224
- }): Promise<Record<string, unknown>>;
225
- /** Writes a migration file for the difference between the config and the latest migration. `null` when nothing changed. */
226
- createMigration(args: {
227
- name: string;
228
- }): Promise<CreateMigrationResult | null>;
229
- /** Applies pending migrations in order. Returns the names applied. */
230
- migrate(): Promise<string[]>;
231
- migrationStatus(): Promise<MigrationInfo[]>;
232
- /**
233
- * Copies the database to `file` (an absolute path) while it is in use, when the database can
234
- * do that itself (SQLite). Others (Postgres) leave it out; use their own tools.
235
- */
236
- readonly backup?: ((file: string) => Promise<void>) | undefined;
237
- /** Raw table access for copying a whole database to another (`easy-cms copy`). */
238
- readonly transfer?: DatabaseTransfer | undefined;
239
- /** The prefix of its tables (`tablePrefix`), so a copy (a backup) can use the same. */
240
- readonly tablePrefix?: string | undefined;
241
- destroy(): Promise<void>;
242
- }
243
- /**
244
- * Reads and writes stored rows as they are, ids and hidden fields included, bypassing validation
245
- * and hooks. Only for moving a database: `copyDatabase()`.
246
- */
247
- interface DatabaseTransfer {
248
- /**
249
- * Identifies the tables and columns the config produces, the same for every database kind.
250
- * Two databases can be copied into each other when their hashes match.
251
- */
252
- readonly schemaHash: string;
253
- /** Every table, in an order that is safe to write. */
254
- tables(): string[];
255
- count(table: string): Promise<number>;
256
- /** Rows in a stable order (by primary key), `offset` and `limit` for batches. */
257
- read(table: string, offset: number, limit: number): Promise<Record<string, unknown>[]>;
258
- write(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
259
- /** After all writes, e.g. moves id sequences past the copied ids so new documents fit. */
260
- finish(): Promise<void>;
261
- }
262
- /** What `sqlite()` / `postgres()` return and what `config.db` holds. */
263
- interface DatabaseAdapter {
264
- readonly name: string;
265
- init(args: DatabaseInitArgs): Promise<Database>;
266
- /** Hints for bundlers (Nitro, Next.js output tracing) about files static analysis cannot find. */
267
- readonly bundle?: BundleHints;
268
- }
269
- interface BundleHints {
270
- /** Absolute paths of files to ship with the server build, e.g. native binaries loaded dynamically. */
271
- readonly traceInclude: readonly string[];
272
- }
273
-
274
- /** An email to send. Addresses may be `name <address>`. */
275
- interface EmailMessage {
276
- readonly to: string | readonly string[];
277
- readonly cc?: string | readonly string[];
278
- readonly bcc?: string | readonly string[];
279
- /** Default: the adapter's `from`. */
280
- readonly from?: string;
281
- readonly replyTo?: string;
282
- readonly subject: string;
283
- readonly html?: string;
284
- readonly text?: string;
285
- }
286
- /**
287
- * Sends email for Easy CMS and its plugins (form notifications, and more later). Set one in
288
- * the config as `email`, e.g. `smtp()` from `@easy-cms/email-smtp`, or `consoleEmail()` in
289
- * development. Throw to report a failure: the message is retried later.
290
- */
291
- interface EmailAdapter {
292
- /** What sends the email, e.g. `smtp`; shown to admins on the dashboard (System). */
293
- readonly name?: string;
294
- /** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
295
- readonly from?: string | undefined;
296
- send(message: EmailMessage & {
297
- readonly from: string;
298
- }): Promise<void>;
299
- /**
300
- * The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
301
- * a password is set, not what it is.
302
- */
303
- describe?(): readonly EmailSetting[];
304
- /** Checks the connection without sending (Settings → Email); throws with the reason. */
305
- verify?(): Promise<void>;
306
- }
307
- /** One setting an email adapter shows to admins (`describe()`). */
308
- interface EmailSetting {
309
- /**
310
- * `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
311
- * are translated in the admin; others are shown as they are.
312
- */
313
- readonly key: string;
314
- /** `null`: not set. */
315
- readonly value: string | null;
316
- /** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
317
- readonly source?: string;
318
- }
319
- /** Prints emails to the log instead of sending them: for development and tests. */
320
- declare function consoleEmail(options?: {
321
- from?: string;
322
- log?: (text: string) => void;
323
- }): EmailAdapter & {
324
- readonly sent: (EmailMessage & {
325
- from: string;
326
- })[];
327
- };
328
-
329
- interface ConfigIssue {
330
- /** Where the problem is, e.g. `collections.posts.fields.slug.from`. */
331
- readonly path: string;
332
- readonly message: string;
333
- /** How to fix it. */
334
- readonly hint?: string;
335
- }
336
- declare class ConfigError extends Error {
337
- readonly issues: readonly ConfigIssue[];
338
- constructor(issues: readonly ConfigIssue[]);
339
- static format(issues: readonly ConfigIssue[]): string;
340
- }
341
- /** Base class for errors that map to an HTTP status in the REST API. */
342
- declare class EasyCMSError extends Error {
343
- readonly status: number;
344
- constructor(message: string, status: number);
345
- }
346
- interface FieldError {
347
- /** Dotted path, e.g. `links.0.url`. */
348
- readonly field: string;
349
- readonly message: string;
350
- }
351
- declare class ValidationError extends EasyCMSError {
352
- readonly errors: readonly FieldError[];
353
- constructor(collection: string, errors: readonly FieldError[]);
354
- }
355
- declare class NotFoundError extends EasyCMSError {
356
- constructor(collection: string, id: unknown);
357
- }
358
- /** A bad `where`, `sort` or unknown collection/global. */
359
- declare class QueryError extends EasyCMSError {
360
- constructor(message: string);
361
- }
362
- /** The database schema does not match the config (pending or missing migrations). */
363
- declare class SchemaError extends EasyCMSError {
364
- constructor(message: string);
365
- }
366
- /** Not logged in, or the session is invalid. */
367
- declare class UnauthorizedError extends EasyCMSError {
368
- constructor(message?: string);
369
- }
370
- /** Logged in, but not allowed to do this. */
371
- declare class ForbiddenError extends EasyCMSError {
372
- constructor(message?: string);
373
- }
374
- declare class TooManyRequestsError extends EasyCMSError {
375
- constructor(message: string);
376
- }
377
- declare class PayloadTooLargeError extends EasyCMSError {
378
- constructor(message: string);
379
- }
380
-
381
59
  /** What a preview token opens: one document of a collection, or one global. */
382
60
  type PreviewTarget = {
383
61
  readonly collection: string;
@@ -553,42 +231,239 @@ declare class Auth {
553
231
  ip?: string | undefined;
554
232
  }): Promise<void>;
555
233
  /**
556
- * Emails a user a link to set their password: an invitation if they have none yet, else a
557
- * reset link. For admins (the REST API checks the role). Returns which one was sent.
234
+ * Emails a user a link to set their password: an invitation if they have none yet, else a
235
+ * reset link. For admins (the REST API checks the role). Returns which one was sent.
236
+ */
237
+ sendPasswordLink(userId: ID, options?: PasswordLinkOptions): Promise<PasswordPurpose>;
238
+ /** The user and purpose of a valid, unused password link, or `null`. */
239
+ checkPasswordToken(token: string): Promise<{
240
+ user: AuthUser;
241
+ purpose: PasswordPurpose;
242
+ } | null>;
243
+ /**
244
+ * Sets the password from a link, ends every session of the account and starts one here.
245
+ * Throws `ValidationError` when the link expired or was used, or the password is too short.
246
+ */
247
+ resetPassword(args: {
248
+ token: string;
249
+ password: string;
250
+ ip?: string | undefined;
251
+ locale?: string | undefined;
252
+ }): Promise<Session & {
253
+ purpose: PasswordPurpose;
254
+ }>;
255
+ private passwordTokenUser;
256
+ private mailPasswordLink;
257
+ private localeOf;
258
+ private findByEmail;
259
+ /** The owner of an API key, limited by the key; `null` when unknown, expired or disabled. */
260
+ private verifyApiKey;
261
+ private startSession;
262
+ private findSession;
263
+ private toAuthUser;
264
+ private deleteExpiredSessions;
265
+ private windowStart;
266
+ private checkRateLimit;
267
+ private recordFailure;
268
+ private clearFailures;
269
+ }
270
+
271
+ interface Logger {
272
+ info(message: string): void;
273
+ warn(message: string): void;
274
+ error(message: string): void;
275
+ }
276
+ declare const consoleLogger: Logger;
277
+ declare const silentLogger: Logger;
278
+
279
+ /** A document as stored: relationships and uploads are ids, never populated. */
280
+ type RawDocument = Record<string, unknown> & {
281
+ id: ID;
282
+ };
283
+ interface PaginatedDocs<T> {
284
+ docs: T[];
285
+ totalDocs: number;
286
+ limit: number;
287
+ page: number;
288
+ totalPages: number;
289
+ hasNextPage: boolean;
290
+ hasPrevPage: boolean;
291
+ }
292
+ /**
293
+ * How the adapter treats the database schema on startup.
294
+ * - `push`: apply schema changes directly (development).
295
+ * - `verify`: fail if migrations are pending or the config changed without a migration (production).
296
+ * - `skip`: do nothing (used by the migration CLI).
297
+ */
298
+ type SchemaMode = 'push' | 'verify' | 'skip';
299
+ interface DatabaseInitArgs {
300
+ readonly config: ResolvedConfig;
301
+ /** Project root; relative paths (database file, migrations) resolve from here. */
302
+ readonly cwd: string;
303
+ readonly schema: SchemaMode;
304
+ readonly logger: Logger;
305
+ /** Whether the process can prompt the user (the CLI in a terminal). */
306
+ readonly interactive?: boolean;
307
+ }
308
+ interface FindArgs {
309
+ readonly collection: string;
310
+ readonly where?: Where | undefined;
311
+ /** Field paths; prefix with `-` for descending. */
312
+ readonly sort: readonly string[];
313
+ /** `0` returns every matching document. */
314
+ readonly limit: number;
315
+ readonly page: number;
316
+ }
317
+ interface MigrationInfo {
318
+ readonly name: string;
319
+ readonly applied: boolean;
320
+ }
321
+ interface CreateMigrationResult {
322
+ readonly name: string;
323
+ readonly file: string;
324
+ readonly statements: readonly string[];
325
+ }
326
+ /** A connected database bound to one config. Returned by `DatabaseAdapter.init`. */
327
+ interface Database {
328
+ find(args: FindArgs): Promise<PaginatedDocs<RawDocument>>;
329
+ findById(args: {
330
+ collection: string;
331
+ id: ID;
332
+ }): Promise<RawDocument | null>;
333
+ /** Documents of one collection by id, in any order. Used to populate relationships. */
334
+ findByIds(args: {
335
+ collection: string;
336
+ ids: readonly ID[];
337
+ }): Promise<RawDocument[]>;
338
+ count(args: {
339
+ collection: string;
340
+ where?: Where | undefined;
341
+ }): Promise<number>;
342
+ /** `data` holds every field, `createdAt` and `updatedAt`. Runs in one transaction. */
343
+ create(args: {
344
+ collection: string;
345
+ data: Record<string, unknown>;
346
+ }): Promise<RawDocument>;
347
+ /** `data` holds the whole document after merging. Runs in one transaction. */
348
+ update(args: {
349
+ collection: string;
350
+ id: ID;
351
+ data: Record<string, unknown>;
352
+ }): Promise<RawDocument>;
353
+ delete(args: {
354
+ collection: string;
355
+ id: ID;
356
+ }): Promise<void>;
357
+ findGlobal(args: {
358
+ slug: string;
359
+ }): Promise<Record<string, unknown> | null>;
360
+ updateGlobal(args: {
361
+ slug: string;
362
+ data: Record<string, unknown>;
363
+ }): Promise<Record<string, unknown>>;
364
+ /** Writes a migration file for the difference between the config and the latest migration. `null` when nothing changed. */
365
+ createMigration(args: {
366
+ name: string;
367
+ }): Promise<CreateMigrationResult | null>;
368
+ /** Applies pending migrations in order. Returns the names applied. */
369
+ migrate(): Promise<string[]>;
370
+ migrationStatus(): Promise<MigrationInfo[]>;
371
+ /**
372
+ * Copies the database to `file` (an absolute path) while it is in use, when the database can
373
+ * do that itself (SQLite). Others (Postgres) leave it out; use their own tools.
374
+ */
375
+ readonly backup?: ((file: string) => Promise<void>) | undefined;
376
+ /** Raw table access for copying a whole database to another (`easy-cms copy`). */
377
+ readonly transfer?: DatabaseTransfer | undefined;
378
+ /** The prefix of its tables (`tablePrefix`), so a copy (a backup) can use the same. */
379
+ readonly tablePrefix?: string | undefined;
380
+ destroy(): Promise<void>;
381
+ }
382
+ /**
383
+ * Reads and writes stored rows as they are, ids and hidden fields included, bypassing validation
384
+ * and hooks. Only for moving a database: `copyDatabase()`.
385
+ */
386
+ interface DatabaseTransfer {
387
+ /**
388
+ * Identifies the tables and columns the config produces, the same for every database kind.
389
+ * Two databases can be copied into each other when their hashes match.
390
+ */
391
+ readonly schemaHash: string;
392
+ /** Every table, in an order that is safe to write. */
393
+ tables(): string[];
394
+ count(table: string): Promise<number>;
395
+ /** Rows in a stable order (by primary key), `offset` and `limit` for batches. */
396
+ read(table: string, offset: number, limit: number): Promise<Record<string, unknown>[]>;
397
+ write(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
398
+ /** After all writes, e.g. moves id sequences past the copied ids so new documents fit. */
399
+ finish(): Promise<void>;
400
+ }
401
+ /** What `sqlite()` / `postgres()` return and what `config.db` holds. */
402
+ interface DatabaseAdapter {
403
+ readonly name: string;
404
+ init(args: DatabaseInitArgs): Promise<Database>;
405
+ /** Hints for bundlers (Nitro, Next.js output tracing) about files static analysis cannot find. */
406
+ readonly bundle?: BundleHints;
407
+ }
408
+ interface BundleHints {
409
+ /** Absolute paths of files to ship with the server build, e.g. native binaries loaded dynamically. */
410
+ readonly traceInclude: readonly string[];
411
+ }
412
+
413
+ /** An email to send. Addresses may be `name <address>`. */
414
+ interface EmailMessage {
415
+ readonly to: string | readonly string[];
416
+ readonly cc?: string | readonly string[];
417
+ readonly bcc?: string | readonly string[];
418
+ /** Default: the adapter's `from`. */
419
+ readonly from?: string;
420
+ readonly replyTo?: string;
421
+ readonly subject: string;
422
+ readonly html?: string;
423
+ readonly text?: string;
424
+ }
425
+ /**
426
+ * Sends email for Easy CMS and its plugins (form notifications, and more later). Set one in
427
+ * the config as `email`, e.g. `smtp()` from `@easy-cms/email-smtp`, or `consoleEmail()` in
428
+ * development. Throw to report a failure: the message is retried later.
429
+ */
430
+ interface EmailAdapter {
431
+ /** What sends the email, e.g. `smtp`; shown to admins on the dashboard (System). */
432
+ readonly name?: string;
433
+ /** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
434
+ readonly from?: string | undefined;
435
+ send(message: EmailMessage & {
436
+ readonly from: string;
437
+ }): Promise<void>;
438
+ /**
439
+ * The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
440
+ * a password is set, not what it is.
558
441
  */
559
- sendPasswordLink(userId: ID, options?: PasswordLinkOptions): Promise<PasswordPurpose>;
560
- /** The user and purpose of a valid, unused password link, or `null`. */
561
- checkPasswordToken(token: string): Promise<{
562
- user: AuthUser;
563
- purpose: PasswordPurpose;
564
- } | null>;
442
+ describe?(): readonly EmailSetting[];
443
+ /** Checks the connection without sending (Settings → Email); throws with the reason. */
444
+ verify?(): Promise<void>;
445
+ }
446
+ /** One setting an email adapter shows to admins (`describe()`). */
447
+ interface EmailSetting {
565
448
  /**
566
- * Sets the password from a link, ends every session of the account and starts one here.
567
- * Throws `ValidationError` when the link expired or was used, or the password is too short.
449
+ * `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
450
+ * are translated in the admin; others are shown as they are.
568
451
  */
569
- resetPassword(args: {
570
- token: string;
571
- password: string;
572
- ip?: string | undefined;
573
- locale?: string | undefined;
574
- }): Promise<Session & {
575
- purpose: PasswordPurpose;
576
- }>;
577
- private passwordTokenUser;
578
- private mailPasswordLink;
579
- private localeOf;
580
- private findByEmail;
581
- /** The owner of an API key, limited by the key; `null` when unknown, expired or disabled. */
582
- private verifyApiKey;
583
- private startSession;
584
- private findSession;
585
- private toAuthUser;
586
- private deleteExpiredSessions;
587
- private windowStart;
588
- private checkRateLimit;
589
- private recordFailure;
590
- private clearFailures;
452
+ readonly key: string;
453
+ /** `null`: not set. */
454
+ readonly value: string | null;
455
+ /** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
456
+ readonly source?: string;
591
457
  }
458
+ /** Prints emails to the log instead of sending them: for development and tests. */
459
+ declare function consoleEmail(options?: {
460
+ from?: string;
461
+ log?: (text: string) => void;
462
+ }): EmailAdapter & {
463
+ readonly sent: (EmailMessage & {
464
+ from: string;
465
+ })[];
466
+ };
592
467
 
593
468
  type Simplify<T> = {
594
469
  [K in keyof T]: T[K];
@@ -979,11 +854,25 @@ declare class Roles {
979
854
  private readonly config;
980
855
  private readonly db;
981
856
  private readonly log?;
857
+ /** Writes to the audit log (`audit`). */
858
+ private readonly audit?;
982
859
  readonly enabled: boolean;
983
860
  private readonly versions;
984
861
  private cache;
985
862
  private synced;
986
- constructor(config: ResolvedConfig, db: Database, log?: ((message: string) => void) | undefined);
863
+ constructor(config: ResolvedConfig, db: Database, log?: ((message: string) => void) | undefined,
864
+ /** Writes to the audit log (`audit`). */
865
+ audit?: ((entry: {
866
+ action: string;
867
+ target: string;
868
+ doc: ID;
869
+ title: string;
870
+ changes?: {
871
+ field: string;
872
+ before?: unknown;
873
+ after?: unknown;
874
+ }[];
875
+ }) => Promise<void>) | undefined);
987
876
  /**
988
877
  * Whether the user's role allows an operation: `true`, `false`, or for their own account
989
878
  * (users without read or update on `users`) a constraint to it.
@@ -1220,6 +1109,8 @@ declare class EasyCMS<C extends Config = Config> {
1220
1109
  readonly cwd: string;
1221
1110
  /** Roles and their permissions (`auth.rbac`, Settings → Roles). */
1222
1111
  readonly roles: Roles;
1112
+ /** Who did what (`audit`, Settings → Audit log). */
1113
+ readonly audit: AuditLog;
1223
1114
  private readonly versions;
1224
1115
  private readonly webhooks;
1225
1116
  private readonly mailer;
@@ -1480,6 +1371,11 @@ declare class EasyCMS<C extends Config = Config> {
1480
1371
  private withDrafts;
1481
1372
  /** Sends webhook events for a change; `before` is the stored status before it, if any. */
1482
1373
  private emit;
1374
+ /**
1375
+ * Writes the audit log entry for a change (`audit`): publishing, unpublishing and restoring
1376
+ * are told apart from other saves.
1377
+ */
1378
+ private audited;
1483
1379
  /** Records a version after a save, when the collection or global keeps versions. */
1484
1380
  private saveVersion;
1485
1381
  private hookArgs;
@@ -1703,6 +1599,257 @@ type Field = CustomField | TextField | TextareaField | NumberField | BooleanFiel
1703
1599
  type FieldType = Field['type'];
1704
1600
  declare const FIELD_TYPES: readonly ["text", "textarea", "number", "boolean", "date", "email", "json", "select", "slug", "richText", "upload", "relationship", "array", "group", "blocks"];
1705
1601
 
1602
+ /** Audit log settings (`audit`). */
1603
+ interface AuditConfig {
1604
+ /** Days to keep entries; older ones are deleted. `0`: keep them all. Default 365. */
1605
+ readonly keep?: number;
1606
+ /** Keep the values before and after a change, not only which fields changed. Default `true`. */
1607
+ readonly values?: boolean;
1608
+ /** Failed logins within an hour that the dashboard warns about. Default 20. */
1609
+ readonly failedLogins?: number;
1610
+ }
1611
+ /** A field that changed: values before and after, unless they are not kept. */
1612
+ interface AuditChange {
1613
+ field: string;
1614
+ before?: unknown;
1615
+ after?: unknown;
1616
+ }
1617
+ /** One entry (`GET <api>/admin/audit`). */
1618
+ interface AuditEntry {
1619
+ id: ID;
1620
+ /** When, as an ISO date. */
1621
+ at: string;
1622
+ /** E.g. `create`, `update`, `publish`, `delete`, `login`, `login.failed`, `role.update`. */
1623
+ action: string;
1624
+ /** A collection slug, `global:<slug>`, or an area such as `auth`, `roles`, `backups`. */
1625
+ target: string | null;
1626
+ /** The document's id. */
1627
+ doc: string | null;
1628
+ /** The document's title then (kept after it is deleted). */
1629
+ title: string | null;
1630
+ actor: {
1631
+ id: string | null;
1632
+ email: string | null;
1633
+ /** `user` (admin or REST), `api-key`, `system` (code), `scheduler`. */
1634
+ via: string;
1635
+ ip: string | null;
1636
+ userAgent: string | null;
1637
+ };
1638
+ changes: AuditChange[] | null;
1639
+ detail: Record<string, unknown> | null;
1640
+ }
1641
+ /** `GET <api>/admin/audit`: a page of entries, newest first. */
1642
+ interface AuditPage {
1643
+ docs: AuditEntry[];
1644
+ page: number;
1645
+ totalPages: number;
1646
+ totalDocs: number;
1647
+ }
1648
+ interface AuditFilter {
1649
+ action?: string | null;
1650
+ target?: string | null;
1651
+ doc?: string | null;
1652
+ /** An email, or part of one. */
1653
+ actor?: string | null;
1654
+ /** ISO dates. */
1655
+ from?: string | null;
1656
+ to?: string | null;
1657
+ }
1658
+ /** `POST <api>/admin/audit/verify`. */
1659
+ interface AuditVerification {
1660
+ checked: number;
1661
+ /** Entries whose signature does not match: changed after they were written. */
1662
+ invalid: ID[];
1663
+ /** Missing ids between entries: deleted entries, or (rarely) inserts that failed. */
1664
+ gaps: number;
1665
+ at: string;
1666
+ }
1667
+ /**
1668
+ * The audit log (`audit` in the config): who did what, when, from where. Entries can't be
1669
+ * changed through Easy CMS; each is signed with the secret, so changes in the database show.
1670
+ * Available as `cms.audit`.
1671
+ */
1672
+ declare class AuditLog {
1673
+ private readonly cms;
1674
+ /** Entries that could not be written since the server started. */
1675
+ failures: number;
1676
+ constructor(cms: EasyCMS);
1677
+ get enabled(): boolean;
1678
+ private get settings();
1679
+ /**
1680
+ * Writes an entry. The user defaults to the request's. Never throws: a failure is logged and
1681
+ * counted, and the dashboard says so.
1682
+ */
1683
+ record(entry: {
1684
+ action: string;
1685
+ target?: string | null;
1686
+ doc?: ID | null;
1687
+ title?: string | null;
1688
+ user?: AuthUser | null;
1689
+ /** Who it was when there is no user, e.g. the email of a failed login. */
1690
+ email?: string | null;
1691
+ changes?: AuditChange[] | null;
1692
+ detail?: Record<string, unknown> | null;
1693
+ }): Promise<void>;
1694
+ /**
1695
+ * A change to a document or global: what changed between `before` and `after`. Internal
1696
+ * collections (sessions, versions…) are not logged.
1697
+ */
1698
+ content(config: CollectionConfig | GlobalConfig, global: boolean, action: string, after: Record<string, unknown> | undefined, before: Record<string, unknown> | undefined, user: AuthUser | null): Promise<void>;
1699
+ /** Fields whose stored value changed. Hidden ones never (the password only as "changed"). */
1700
+ diff(fields: readonly Field[], before: Record<string, unknown>, after: Record<string, unknown>): AuditChange[];
1701
+ list(filter: AuditFilter, page?: number): Promise<AuditPage>;
1702
+ /** The filtered entries as CSV (the newest 10,000). */
1703
+ csv(filter: AuditFilter): Promise<string>;
1704
+ /** Checks every entry's signature, and that no ids are missing. */
1705
+ verify(): Promise<AuditVerification>;
1706
+ /**
1707
+ * Daily upkeep from `runJobs`: deletes entries older than `keep` days, then verifies the log.
1708
+ * Runs when the last check is more than a day old.
1709
+ */
1710
+ upkeep(now?: Date): Promise<void>;
1711
+ /** What the dashboard warns about: many failed logins, entries not written, a failed check. */
1712
+ attention(now?: Date): Promise<({
1713
+ id: 'failed-logins';
1714
+ count: number;
1715
+ } | {
1716
+ id: 'audit-failures';
1717
+ count: number;
1718
+ } | {
1719
+ id: 'audit-tampered';
1720
+ invalid: number;
1721
+ at: string;
1722
+ })[]>;
1723
+ private sign;
1724
+ private lastVerification;
1725
+ private where;
1726
+ }
1727
+
1728
+ /** What the email functions in `auth.emails` receive. */
1729
+ interface PasswordEmailArgs {
1730
+ readonly user: AuthUser;
1731
+ /** The page that sets the password; absent for `passwordChanged`. */
1732
+ readonly url?: string;
1733
+ /** The admin language for the text: `en` or `th`. */
1734
+ readonly locale: string;
1735
+ /** When the link stops working. */
1736
+ readonly expiresAt?: Date;
1737
+ /**
1738
+ * Invitations when passwords are off (`auth.password: false`): the providers to sign in with,
1739
+ * e.g. `['Google']`; `url` is then the login page.
1740
+ */
1741
+ readonly sso?: readonly string[];
1742
+ }
1743
+ /** An email's content: a subject, and text and/or HTML. */
1744
+ interface PasswordEmail {
1745
+ readonly subject: string;
1746
+ readonly text: string;
1747
+ readonly html?: string;
1748
+ }
1749
+ type PasswordEmailFn = (args: PasswordEmailArgs) => PasswordEmail | Promise<PasswordEmail>;
1750
+ /** The default emails, in English and Thai. */
1751
+ declare const DEFAULT_PASSWORD_EMAILS: {
1752
+ resetPassword: PasswordEmailFn;
1753
+ invite: PasswordEmailFn;
1754
+ passwordChanged: PasswordEmailFn;
1755
+ };
1756
+
1757
+ /**
1758
+ * Signing in to the admin with an outside account (`auth.providers`): Google, Microsoft, GitHub
1759
+ * or any OpenID Connect provider. `@easy-cms/auth-oauth` makes these; Easy CMS runs the rest
1760
+ * (the redirect, matching users, sessions).
1761
+ */
1762
+ /** Values bound to one sign-in, checked when the browser comes back. */
1763
+ interface AuthProviderRequest {
1764
+ /** Where the provider sends the browser back: `<serverURL><api>/auth/<id>/callback`. */
1765
+ readonly redirectUri: string;
1766
+ readonly state: string;
1767
+ /** For PKCE: the provider gets its S256 challenge. */
1768
+ readonly codeVerifier: string;
1769
+ /** For OpenID Connect: must come back in the ID token. */
1770
+ readonly nonce: string;
1771
+ }
1772
+ /** Who signed in, as the provider says. */
1773
+ interface AuthProviderProfile {
1774
+ /** The provider's stable id for the account (`sub`). */
1775
+ readonly subject: string;
1776
+ readonly email: string | null;
1777
+ /** The provider vouches for the email. Only verified emails are matched to users. */
1778
+ readonly emailVerified: boolean;
1779
+ readonly name?: string | undefined;
1780
+ }
1781
+ interface AuthProvider {
1782
+ /** In URLs and stored identities, e.g. `google`: lowercase letters, digits and `-`. */
1783
+ readonly id: string;
1784
+ /** On the sign-in button, e.g. `Google`. */
1785
+ readonly name: string;
1786
+ /** The button's icon in the admin. Default `key`. */
1787
+ readonly icon?: 'google' | 'microsoft' | 'github' | 'key';
1788
+ /** The provider's page to send the browser to. */
1789
+ authorizationURL(request: AuthProviderRequest): Promise<URL>;
1790
+ /**
1791
+ * After the browser comes back to `redirectUri` (`url`, with its query): checks the response
1792
+ * and exchanges the code. Throws when anything does not check out.
1793
+ */
1794
+ callback(request: AuthProviderRequest & {
1795
+ readonly url: URL;
1796
+ }): Promise<AuthProviderProfile>;
1797
+ /** For Settings → SSO: what it connects to, never secrets. */
1798
+ describe?(): Readonly<Record<string, string>>;
1799
+ }
1800
+
1801
+ interface ConfigIssue {
1802
+ /** Where the problem is, e.g. `collections.posts.fields.slug.from`. */
1803
+ readonly path: string;
1804
+ readonly message: string;
1805
+ /** How to fix it. */
1806
+ readonly hint?: string;
1807
+ }
1808
+ declare class ConfigError extends Error {
1809
+ readonly issues: readonly ConfigIssue[];
1810
+ constructor(issues: readonly ConfigIssue[]);
1811
+ static format(issues: readonly ConfigIssue[]): string;
1812
+ }
1813
+ /** Base class for errors that map to an HTTP status in the REST API. */
1814
+ declare class EasyCMSError extends Error {
1815
+ readonly status: number;
1816
+ constructor(message: string, status: number);
1817
+ }
1818
+ interface FieldError {
1819
+ /** Dotted path, e.g. `links.0.url`. */
1820
+ readonly field: string;
1821
+ readonly message: string;
1822
+ }
1823
+ declare class ValidationError extends EasyCMSError {
1824
+ readonly errors: readonly FieldError[];
1825
+ constructor(collection: string, errors: readonly FieldError[]);
1826
+ }
1827
+ declare class NotFoundError extends EasyCMSError {
1828
+ constructor(collection: string, id: unknown);
1829
+ }
1830
+ /** A bad `where`, `sort` or unknown collection/global. */
1831
+ declare class QueryError extends EasyCMSError {
1832
+ constructor(message: string);
1833
+ }
1834
+ /** The database schema does not match the config (pending or missing migrations). */
1835
+ declare class SchemaError extends EasyCMSError {
1836
+ constructor(message: string);
1837
+ }
1838
+ /** Not logged in, or the session is invalid. */
1839
+ declare class UnauthorizedError extends EasyCMSError {
1840
+ constructor(message?: string);
1841
+ }
1842
+ /** Logged in, but not allowed to do this. */
1843
+ declare class ForbiddenError extends EasyCMSError {
1844
+ constructor(message?: string);
1845
+ }
1846
+ declare class TooManyRequestsError extends EasyCMSError {
1847
+ constructor(message: string);
1848
+ }
1849
+ declare class PayloadTooLargeError extends EasyCMSError {
1850
+ constructor(message: string);
1851
+ }
1852
+
1706
1853
  /** Built-in types a field type can be stored as. */
1707
1854
  declare const FIELD_TYPE_BASES: readonly ["text", "textarea", "email", "number", "boolean", "date", "json"];
1708
1855
  type FieldTypeBase = (typeof FIELD_TYPE_BASES)[number];
@@ -2273,10 +2420,15 @@ interface Config {
2273
2420
  * `every`, admins can still back up by hand.
2274
2421
  */
2275
2422
  readonly backups?: BackupsConfig;
2423
+ /**
2424
+ * The audit log (Settings → Audit log): who changed what and when, sign-ins, and admin
2425
+ * actions, kept for `keep` days. `true` for the defaults.
2426
+ */
2427
+ readonly audit?: boolean | AuditConfig;
2276
2428
  readonly plugins?: readonly Plugin[];
2277
2429
  }
2278
2430
  /** The config after plugins ran, validation passed and defaults were applied. */
2279
- interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins'> {
2431
+ interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins' | 'audit'> {
2280
2432
  readonly cors: readonly string[] | '*';
2281
2433
  /** `null` without localization. */
2282
2434
  readonly localization: Required<LocalizationConfig> | null;
@@ -2284,6 +2436,8 @@ interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes'
2284
2436
  readonly admin: Required<AdminConfig>;
2285
2437
  readonly upload: Required<Omit<UploadConfig, 'storage' | 'fromURL'>> & Pick<UploadConfig, 'storage' | 'fromURL'>;
2286
2438
  readonly auth: Required<AuthConfig>;
2439
+ /** `false` without an audit log. */
2440
+ readonly audit: Required<AuditConfig> | false;
2287
2441
  readonly collections: readonly CollectionConfig[];
2288
2442
  readonly globals: readonly GlobalConfig[];
2289
2443
  readonly endpoints: readonly Endpoint[];
@@ -2734,6 +2888,7 @@ interface AdminSchema {
2734
2888
  email: boolean;
2735
2889
  roles: boolean;
2736
2890
  sso: boolean;
2891
+ audit: boolean;
2737
2892
  };
2738
2893
  /** Roles from the admin are on (`auth.rbac`): documents have owners. */
2739
2894
  rbac: boolean;
@@ -2776,6 +2931,22 @@ type AdminAttention =
2776
2931
  id: 'backups';
2777
2932
  failed: boolean;
2778
2933
  lastDone: string | null;
2934
+ }
2935
+ /** Many failed logins in the last hour (`audit.failedLogins`): someone may be guessing. */
2936
+ | {
2937
+ id: 'failed-logins';
2938
+ count: number;
2939
+ }
2940
+ /** Audit log entries that could not be written since the server started. */
2941
+ | {
2942
+ id: 'audit-failures';
2943
+ count: number;
2944
+ }
2945
+ /** The last check of the audit log found entries changed in the database. */
2946
+ | {
2947
+ id: 'audit-tampered';
2948
+ invalid: number;
2949
+ at: string;
2779
2950
  };
2780
2951
  /** What the dashboard shows admins (`GET <api>/admin/status`). */
2781
2952
  interface AdminStatus {
@@ -2837,4 +3008,4 @@ declare function validateConfig(config: Config): ConfigIssue[];
2837
3008
  /** The version of `@easy-cms/core`, set when the package is built. */
2838
3009
  declare const VERSION: string;
2839
3010
 
2840
- export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBackup, type AdminBackups, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminDeliveries, type AdminDelivery, type AdminEmail, type AdminEmailDelivery, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminRole, type AdminRoleChange, type AdminRoleField, type AdminRoleTarget, type AdminRoleView, type AdminRoles, type AdminSchema, type AdminSso, type AdminStatus, type AdminViewAccess, type AdminWebhookDelivery, type AdminWidgetRef, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ApiKeyContext, type ApiKeyGlobalOperation, type ApiKeyOperation, type ApiKeyPermissions, type ArrayField, Auth, type AuthConfig, type AuthProvider, type AuthProviderProfile, type AuthProviderRequest, type AuthUser, BUILTIN_COLLECTIONS, type BackupState, type BackupsConfig, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinApiKeysCollection, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CliCommand, type CollectionAccess, type CollectionAdmin, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionListAdmin, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CopyProgress, type CopyResult, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, type CustomField, type CustomFieldTypes, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_PASSWORD_EMAILS, DEFAULT_TOKEN_EXPIRATION, type DashboardWidget, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DatabaseTransfer, type DateField, type DeliveryKind, type DeliveryState, type DepthOptions, type DocumentOf, EXTENSIONS, EasyCMS, EasyCMSError, type EmailAdapter, type EmailCheck, type EmailField, type EmailMessage, type EmailSetting, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldRule, type FieldType, type FieldTypeBase, type FieldTypeDefinition, type FieldTypeValidateContext, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, type FilterOptions, type FilterOptionsArgs, type FindArgs, type FindOptions, ForbiddenError, type GlobalAccess, type GlobalConfig, type GlobalDocument, type GlobalHooks, type GlobalInput, type GlobalSlug, type GroupField, type ID, INTERNAL_COLLECTIONS, type ImageSize, type InferCollection, type InferGlobal, type JsonField, type Label, type LivePreview, type LoadConfigOptions, type LocalStorageOptions, type LocalizationConfig, type Logger, type LoginArgs, MAX_DEPTH, MEDIA, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, type MediaDocument, type MediaSize, type MigrationInfo, NotFoundError, type NumberField, type Operation, type PaginatedDocs, type PasswordEmail, type PasswordEmailArgs, type PasswordEmailFn, type PasswordLinkOptions, PayloadTooLargeError, type Plugin, type PluginInfo, type PluginTypes, type PreviewTarget, type PreviewURL, QueryError, ROLE_OPERATIONS, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoleOperation, type RolePermissions, Roles, type RoutesConfig, SESSION_COOKIE, SSO_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type SsoOutcome, type SsoProviderRef, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, type UserIdentity, VERSION, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, configSignature, consoleEmail, consoleLogger, copyDatabase, createEasyCMS, createRestHandler, createRootEndpointHandler, defineConfig, defineFieldType, definePlugin, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isHasMany, isLoggedIn, keyAllows, loadConfig, localStorage, mergeForUpdate, mimeAllowed, mimeAllowedBy, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword, writeBackupFile };
3011
+ export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBackup, type AdminBackups, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminDeliveries, type AdminDelivery, type AdminEmail, type AdminEmailDelivery, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminRole, type AdminRoleChange, type AdminRoleField, type AdminRoleTarget, type AdminRoleView, type AdminRoles, type AdminSchema, type AdminSso, type AdminStatus, type AdminViewAccess, type AdminWebhookDelivery, type AdminWidgetRef, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ApiKeyContext, type ApiKeyGlobalOperation, type ApiKeyOperation, type ApiKeyPermissions, type ArrayField, type AuditChange, type AuditConfig, type AuditEntry, AuditLog, type AuditPage, type AuditVerification, Auth, type AuthConfig, type AuthProvider, type AuthProviderProfile, type AuthProviderRequest, type AuthUser, BUILTIN_COLLECTIONS, type BackupState, type BackupsConfig, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinApiKeysCollection, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CliCommand, type CollectionAccess, type CollectionAdmin, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionListAdmin, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CopyProgress, type CopyResult, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, type CustomField, type CustomFieldTypes, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_PASSWORD_EMAILS, DEFAULT_TOKEN_EXPIRATION, type DashboardWidget, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DatabaseTransfer, type DateField, type DeliveryKind, type DeliveryState, type DepthOptions, type DocumentOf, EXTENSIONS, EasyCMS, EasyCMSError, type EmailAdapter, type EmailCheck, type EmailField, type EmailMessage, type EmailSetting, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldRule, type FieldType, type FieldTypeBase, type FieldTypeDefinition, type FieldTypeValidateContext, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, type FilterOptions, type FilterOptionsArgs, type FindArgs, type FindOptions, ForbiddenError, type GlobalAccess, type GlobalConfig, type GlobalDocument, type GlobalHooks, type GlobalInput, type GlobalSlug, type GroupField, type ID, INTERNAL_COLLECTIONS, type ImageSize, type InferCollection, type InferGlobal, type JsonField, type Label, type LivePreview, type LoadConfigOptions, type LocalStorageOptions, type LocalizationConfig, type Logger, type LoginArgs, MAX_DEPTH, MEDIA, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, type MediaDocument, type MediaSize, type MigrationInfo, NotFoundError, type NumberField, type Operation, type PaginatedDocs, type PasswordEmail, type PasswordEmailArgs, type PasswordEmailFn, type PasswordLinkOptions, PayloadTooLargeError, type Plugin, type PluginInfo, type PluginTypes, type PreviewTarget, type PreviewURL, QueryError, ROLE_OPERATIONS, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoleOperation, type RolePermissions, Roles, type RoutesConfig, SESSION_COOKIE, SSO_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type SsoOutcome, type SsoProviderRef, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, type UserIdentity, VERSION, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, configSignature, consoleEmail, consoleLogger, copyDatabase, createEasyCMS, createRestHandler, createRootEndpointHandler, defineConfig, defineFieldType, definePlugin, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isHasMany, isLoggedIn, keyAllows, loadConfig, localStorage, mergeForUpdate, mimeAllowed, mimeAllowedBy, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword, writeBackupFile };