@easy-cms/core 0.48.0 → 0.60.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.
@@ -0,0 +1,188 @@
1
+ import { I as ID, ab as Config, au as EasyCMS, bd as LocalizationConfig, aD as Field, aH as FieldError, aS as FilterOptions, af as CreateEasyCMSOptions, bI as ResolvedConfig } from './config-jzFGsNL6.js';
2
+
3
+ type BackupState = 'pending' | 'running' | 'done' | 'failed';
4
+ /** One backup as the admin shows it (`GET <api>/admin/backups`). */
5
+ interface AdminBackup {
6
+ id: ID;
7
+ state: BackupState;
8
+ trigger: 'manual' | 'scheduled';
9
+ /** The file's name, e.g. `my-site-2026-10-04-0300.db.gz`. */
10
+ filename: string | null;
11
+ /** Bytes, compressed. */
12
+ size: number | null;
13
+ startedAt: string | null;
14
+ finishedAt: string | null;
15
+ error: string | null;
16
+ /** Who started it (manual backups). */
17
+ author: string | null;
18
+ downloadedBy: string | null;
19
+ downloadedAt: string | null;
20
+ }
21
+ /** Settings → Backups (`GET <api>/admin/backups`). */
22
+ interface AdminBackups {
23
+ settings: {
24
+ frequency: 'daily' | 'weekly' | null;
25
+ at: string;
26
+ keep: number;
27
+ /** The storage adapter's name, e.g. `local` or `s3`. */
28
+ storage: string;
29
+ /** The folder, for local storage. */
30
+ dir: string | null;
31
+ database: string;
32
+ /** Why backups can't be made with this setup, if they can't. */
33
+ unavailable: string | null;
34
+ };
35
+ backups: AdminBackup[];
36
+ }
37
+ /**
38
+ * Writes a consistent copy of the database to `file` as SQLite, while the CMS keeps running:
39
+ * SQLite copies itself; other databases are copied into a new SQLite file (`backups.sqlite`).
40
+ */
41
+ declare function writeBackupFile<C extends Config>(cms: EasyCMS<C>, file: string): Promise<void>;
42
+
43
+ type Localization = Required<LocalizationConfig>;
44
+
45
+ type Data = Record<string, unknown>;
46
+ /**
47
+ * Turns text into a URL slug. Keeps letters and digits of any script,
48
+ * so Thai titles stay readable: "สวัสดี World!" → "สวัสดี-world".
49
+ */
50
+ declare function slugify(text: string): string;
51
+ /** Fills in `defaultValue` for fields that are missing, including inside groups and array rows. */
52
+ declare function applyDefaults(fields: readonly Field[], data: Data, localization?: Localization | null): Data;
53
+ /** Gives every field a value (`null`, `[]` or a nested object) so documents always have the same shape. */
54
+ declare function fillMissing(fields: readonly Field[], data: Data): Data;
55
+ /** Fills empty `slug` fields from their `from` field. Does not check uniqueness. */
56
+ declare function generateSlugs(fields: readonly Field[], data: Data, localization?: Localization | null): Data;
57
+ /**
58
+ * Merges an update into the stored document. Top-level fields and group
59
+ * fields merge key by key; arrays and hasMany values are replaced.
60
+ */
61
+ declare function mergeForUpdate(fields: readonly Field[], existing: Data, patch: Data): Data;
62
+ /** Parses an id from user input. Integer ids may arrive as strings from URLs. */
63
+ declare function parseId(value: unknown): ID | undefined;
64
+ /** A reference to another document found while validating, checked against the database afterwards. */
65
+ interface Reference {
66
+ readonly field: string;
67
+ readonly collection: string;
68
+ readonly id: ID;
69
+ /** The relationship's `filterOptions`, checked when saving. */
70
+ readonly filterOptions?: FilterOptions;
71
+ /** The upload's `mimeTypes`, checked when saving. */
72
+ readonly mimeTypes?: readonly string[];
73
+ /** The upload's `folder` with `folderOnly`: the file must be in it, checked when saving. */
74
+ readonly folderOnly?: string;
75
+ }
76
+ interface ValidateOptions {
77
+ readonly operation: 'create' | 'update';
78
+ /** Drafts may be incomplete: skip `required`, but still check types (FR-DRF-02). */
79
+ readonly skipRequired?: boolean;
80
+ /** The whole top-level document, passed to custom `validate` functions. */
81
+ readonly root: Data;
82
+ /** Localized fields hold `{ [locale]: value }`; each value is validated. */
83
+ readonly localization?: Localization | null;
84
+ /**
85
+ * The locale being written: `required` and custom `validate` apply to its values, and its
86
+ * errors use the plain field path. Default: the default locale.
87
+ */
88
+ readonly locale?: string;
89
+ }
90
+ interface ValidateResult {
91
+ /** Only known fields, with values normalized (ids parsed, dates as ISO strings, row ids filled). */
92
+ readonly data: Data;
93
+ readonly errors: FieldError[];
94
+ readonly references: Reference[];
95
+ }
96
+ /** Checks and normalizes data against fields. Unknown keys are dropped. */
97
+ declare function validateFields(fields: readonly Field[], input: Data, options: ValidateOptions, prefix?: string): Promise<ValidateResult>;
98
+
99
+ /**
100
+ * The Easy CMS instance for this server, made on first use and kept across hot reloads.
101
+ *
102
+ * Configs are compared by structure, not identity: frameworks bundle the config into each server
103
+ * layer, so the same config can arrive as several objects. A change to fields or options (in
104
+ * development) closes the old instance and makes a new one; a change to a hook's code alone needs
105
+ * a restart. A failed start is not kept: the next call tries again.
106
+ */
107
+ declare function sharedEasyCMS<const C extends Config>(config: C, options?: CreateEasyCMSOptions): Promise<EasyCMS<C>>;
108
+ interface ApiHandlerOptions {
109
+ /** Where the API is mounted. Default: `routes.api` of the config. */
110
+ readonly basePath?: string;
111
+ /**
112
+ * The client's IP (login rate limiting, the audit log), from the framework's request object.
113
+ * Takes precedence over `trustProxy`.
114
+ */
115
+ readonly getClientIp?: (request: Request) => string | undefined;
116
+ /**
117
+ * Behind a proxy that adds the client's address to `X-Forwarded-For`: the last address in it
118
+ * (the one the proxy added; earlier ones come from the client and can say anything). Vercel
119
+ * and Netlify are recognized without it.
120
+ */
121
+ readonly trustProxy?: boolean;
122
+ }
123
+ /**
124
+ * The client's address from `X-Forwarded-For` as the nearest proxy wrote it: its last entry.
125
+ * Earlier entries were sent by the client, so they can't be trusted.
126
+ */
127
+ declare function forwardedClientIp(value: string | null | undefined): string | undefined;
128
+ /** The client's address on hosting platforms that set it themselves: Vercel, Netlify. */
129
+ declare function platformClientIp(headers: Headers): string | undefined;
130
+ /**
131
+ * The REST API as a `(Request) => Response` handler for a framework's route, on the shared
132
+ * instance (`sharedEasyCMS`). Mount it at `routes.api` (default `/api/cms`), for every method.
133
+ */
134
+ declare function createApiHandler(config: Config, options?: ApiHandlerOptions): (request: Request) => Promise<Response>;
135
+
136
+ declare const CONFIG_FILE_NAMES: readonly ["easy-cms.config.ts", "easy-cms.config.mts", "easy-cms.config.js", "easy-cms.config.mjs"];
137
+ interface LoadConfigOptions {
138
+ /** Directory to search. Default `process.cwd()`. */
139
+ readonly cwd?: string;
140
+ /** Explicit config file path, relative to `cwd`. */
141
+ readonly configFile?: string;
142
+ }
143
+ /** Finds the config file in `cwd`, returning its absolute path or `undefined`. */
144
+ declare function findConfigFile(cwd?: string): string | undefined;
145
+ /**
146
+ * Imports a config file without resolving or validating it.
147
+ * Build tools use this to read hints (e.g. `db.bundle`) when env vars may be missing.
148
+ */
149
+ declare function importConfig(file: string): Promise<Config | undefined>;
150
+ /** Loads `easy-cms.config.ts` (or `.mts`/`.js`/`.mjs`) and resolves it. */
151
+ declare function loadConfig(options?: LoadConfigOptions): Promise<ResolvedConfig>;
152
+
153
+ declare const DEFAULT_ADMIN_PATH = "/admin";
154
+ declare const DEFAULT_API_PATH = "/api/cms";
155
+ declare const DEFAULT_MAX_FILE_SIZE: number;
156
+ declare const DEFAULT_TOKEN_EXPIRATION: number;
157
+ /**
158
+ * Runs the config's plugins in order, without validating the result. Build tools use it to
159
+ * read what plugins add (e.g. `admin.modules`) when the config is not complete yet.
160
+ */
161
+ declare function applyPlugins(input: Config): Promise<Config>;
162
+ /**
163
+ * Runs plugins in order, adds the built-in collections, validates the result
164
+ * and fills in defaults. Throws `ConfigError`. Resolving twice is a no-op.
165
+ */
166
+ declare function resolveConfig(input: Config | ResolvedConfig): Promise<ResolvedConfig>;
167
+
168
+ declare const CSRF_COOKIE = "ecms-csrf";
169
+ declare const CSRF_HEADER = "x-csrf-token";
170
+ interface RestHandlerOptions {
171
+ /** Path the handler is mounted at. Default: `routes.api` from the config (`/api/cms`). */
172
+ readonly basePath?: string;
173
+ /** Client IP, used with the email to rate-limit logins. Adapters provide it. */
174
+ readonly getClientIp?: (request: Request) => string | undefined;
175
+ }
176
+ type RestHandler = (request: Request) => Promise<Response>;
177
+ /** Creates the REST API as a Web-standard `(Request) => Response` handler. */
178
+ declare function createRestHandler<C extends Config>(instance: EasyCMS<C>, options?: RestHandlerOptions): RestHandler;
179
+ /**
180
+ * Serves the endpoints marked `root: true` (e.g. `/robots.txt`), with the same auth, CSRF and
181
+ * errors as the REST API. Resolves to `undefined` when no root endpoint has the request's path,
182
+ * so a server can go on to its other routes. Nuxt and Next.js apps own their root and don't
183
+ * use it; the standalone server does.
184
+ */
185
+ declare function createRootEndpointHandler<C extends Config>(instance: EasyCMS<C>, options?: Pick<RestHandlerOptions, 'getClientIp'>): (request: Request) => Promise<Response | undefined>;
186
+ declare function readCookie(request: Request, name: string): string | undefined;
187
+
188
+ export { type AdminBackup as A, type BackupState as B, CONFIG_FILE_NAMES as C, DEFAULT_ADMIN_PATH as D, validateFields as E, writeBackupFile as F, type LoadConfigOptions as L, type RestHandler as R, type AdminBackups as a, type ApiHandlerOptions as b, type RestHandlerOptions as c, createApiHandler as d, createRestHandler as e, slugify as f, CSRF_COOKIE as g, CSRF_HEADER as h, DEFAULT_API_PATH as i, DEFAULT_MAX_FILE_SIZE as j, DEFAULT_TOKEN_EXPIRATION as k, loadConfig as l, applyDefaults as m, applyPlugins as n, createRootEndpointHandler as o, fillMissing as p, findConfigFile as q, resolveConfig as r, sharedEasyCMS as s, forwardedClientIp as t, generateSlugs as u, importConfig as v, mergeForUpdate as w, parseId as x, platformClientIp as y, readCookie as z };