@easy-cms/core 1.15.0 → 1.15.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.
@@ -35,7 +35,7 @@ import {
35
35
  warnDeprecated,
36
36
  withCharset,
37
37
  withPrivacy
38
- } from "./chunk-RTKWMVAC.js";
38
+ } from "./chunk-SKGKGKUN.js";
39
39
 
40
40
  // src/access.ts
41
41
  var isSystemAdmin = (user) => user?.role === "admin" && !user.apiKey && user.scoped !== true;
@@ -2314,7 +2314,10 @@ var ADAPTER_API_VERSION = 1;
2314
2314
 
2315
2315
  // src/document.ts
2316
2316
  import { randomUUID } from "crypto";
2317
- var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2317
+ function isEmail(value) {
2318
+ const at = value.indexOf("@");
2319
+ return at > 0 && !value.includes("@", at + 1) && !/\s/.test(value) && value.slice(at + 2, -1).includes(".");
2320
+ }
2318
2321
  var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2319
2322
  function itemsOf(field, n) {
2320
2323
  const unit = field.type === "upload" ? "file" : field.type === "relationship" ? "document" : "row";
@@ -2522,8 +2525,7 @@ async function normalizeValue(field, raw, path, options, fail, errors, reference
2522
2525
  return raw;
2523
2526
  }
2524
2527
  case "email": {
2525
- if (typeof raw !== "string" || !EMAIL_PATTERN.test(raw))
2526
- return fail("must be a valid email address");
2528
+ if (typeof raw !== "string" || !isEmail(raw)) return fail("must be a valid email address");
2527
2529
  return raw.trim().toLowerCase();
2528
2530
  }
2529
2531
  case "number": {
@@ -3735,6 +3737,7 @@ async function populate(load, collections, fields, docs, depth) {
3735
3737
  }
3736
3738
  function collectSlots(fields, data, bySlug, slots) {
3737
3739
  for (const field of fields) {
3740
+ if (field.name === "__proto__") continue;
3738
3741
  const value = data[field.name];
3739
3742
  if (value === null || value === void 0) continue;
3740
3743
  if (field.type === "group") {
@@ -4440,7 +4443,10 @@ for (const [net, prefix] of [
4440
4443
  ["::1", 128],
4441
4444
  ["64:ff9b:1::", 48],
4442
4445
  ["100::", 64],
4446
+ // Teredo and 6to4 tunnel to an IPv4 address the IPv6 one carries: any of them, private too.
4447
+ ["2001::", 32],
4443
4448
  ["2001:db8::", 32],
4449
+ ["2002::", 16],
4444
4450
  ["fc00::", 7],
4445
4451
  ["fe80::", 10],
4446
4452
  ["ff00::", 8]
@@ -6406,6 +6412,15 @@ function validateFields2(fields, path, reserved, collectionSlugs, add) {
6406
6412
  add(`${path}[${i}].name`, `must be a valid identifier (got ${JSON.stringify(name)})`);
6407
6413
  return;
6408
6414
  }
6415
+ if (name === "__proto__") {
6416
+ add(`${path}[${i}].name`, '"__proto__" is reserved by JavaScript');
6417
+ return;
6418
+ }
6419
+ if (name === "constructor" || name === "prototype")
6420
+ warnDeprecated(
6421
+ "EASY_CMS_DEP006",
6422
+ `A field named "${name}" (${path}.${name}) works through 1.x; rename it before 2.0.`
6423
+ );
6409
6424
  if (reserved.has(name)) {
6410
6425
  add(`${path}.${name}`, `"${name}" is reserved`, "Easy CMS adds this field automatically");
6411
6426
  }
@@ -7030,7 +7045,7 @@ function cacheableResponse(request, body, status, headers, times, tags) {
7030
7045
  }
7031
7046
 
7032
7047
  // src/version.ts
7033
- var VERSION = true ? "1.15.0" : "0.0.0-dev";
7048
+ var VERSION = true ? "1.15.1" : "0.0.0-dev";
7034
7049
 
7035
7050
  // src/telemetry.ts
7036
7051
  var DB_CALLS = /* @__PURE__ */ new Set([
@@ -11183,7 +11198,6 @@ async function deleteFailedDeliveries(cms, kind) {
11183
11198
  }
11184
11199
 
11185
11200
  // src/rest/admin-email.ts
11186
- var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
11187
11201
  var LIMIT = 5;
11188
11202
  var WINDOW = 10 * 6e4;
11189
11203
  var sentAt = /* @__PURE__ */ new WeakMap();
@@ -11232,7 +11246,7 @@ async function sendTestEmail(cms, user, to, locale) {
11232
11246
  const adapter = cms.config.email;
11233
11247
  if (!adapter) return { ok: false, error: "Email is not set up (`email` in the config)" };
11234
11248
  const address = typeof to === "string" && to.trim() ? to.trim() : user.email;
11235
- if (!EMAIL.test(address))
11249
+ if (!isEmail(address))
11236
11250
  throw new ValidationError("email", [{ field: "to", message: "must be an email address" }]);
11237
11251
  const times = sentAt.get(cms) ?? /* @__PURE__ */ new Map();
11238
11252
  sentAt.set(cms, times);
@@ -13354,13 +13368,14 @@ function errorResponse(cms, error, headers, production) {
13354
13368
  }
13355
13369
  return new Response(JSON.stringify({ errors }), { status: error.status, headers });
13356
13370
  }
13357
- const message2 = error instanceof Error ? error.message : String(error);
13358
- cms.logger.error(`REST request failed: ${message2}`, { error });
13359
- const body = {
13360
- errors: [
13361
- { message: production ? "Internal Server Error" : message2, code: "INTERNAL_SERVER_ERROR" }
13362
- ]
13363
- };
13371
+ cms.logger.error(
13372
+ `REST request failed: ${error instanceof Error ? error.message : String(error)}`,
13373
+ {
13374
+ error
13375
+ }
13376
+ );
13377
+ const message2 = !production && error instanceof Error ? error.message : "Internal Server Error";
13378
+ const body = { errors: [{ message: message2, code: "INTERNAL_SERVER_ERROR" }] };
13364
13379
  return new Response(JSON.stringify(body), { status: 500, headers });
13365
13380
  }
13366
13381
 
@@ -13496,6 +13511,7 @@ export {
13496
13511
  SSO_COOKIE,
13497
13512
  Auth,
13498
13513
  ADAPTER_API_VERSION,
13514
+ isEmail,
13499
13515
  slugify,
13500
13516
  applyDefaults,
13501
13517
  fillMissing,
@@ -166,6 +166,25 @@ function zipType(data) {
166
166
  if (contains(data, "ppt/presentation.xml")) return OFFICE[2];
167
167
  return "application/zip";
168
168
  }
169
+ function isSvg(text) {
170
+ let rest = text;
171
+ if (rest.slice(0, 5).toLowerCase() === "<?xml") {
172
+ const end = rest.indexOf(">");
173
+ if (end < 0) return false;
174
+ rest = rest.slice(end + 1).trimStart();
175
+ }
176
+ while (rest.startsWith("<!--")) {
177
+ const end = rest.indexOf("-->", 4);
178
+ if (end < 0) return false;
179
+ rest = rest.slice(end + 3).trimStart();
180
+ }
181
+ if (/^<!DOCTYPE svg/i.test(rest)) {
182
+ const end = rest.indexOf(">");
183
+ if (end < 0) return false;
184
+ rest = rest.slice(end + 1).trimStart();
185
+ }
186
+ return /^<svg[\s>]/i.test(rest);
187
+ }
169
188
  function sniffMimeType(data, name = "") {
170
189
  if (startsWith(data, [137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
171
190
  if (startsWith(data, [255, 216, 255])) return "image/jpeg";
@@ -195,8 +214,7 @@ function sniffMimeType(data, name = "") {
195
214
  return void 0;
196
215
  }
197
216
  const trimmed = text.replace(/^/, "").trimStart();
198
- if (/^(<\?xml[^>]*>\s*)?(<!--[\s\S]*?-->\s*)*(<!DOCTYPE svg[^>]*>\s*)?<svg[\s>]/i.test(trimmed))
199
- return "image/svg+xml";
217
+ if (isSvg(trimmed)) return "image/svg+xml";
200
218
  return /\.csv$/i.test(name) ? "text/csv" : "text/plain";
201
219
  }
202
220
  function mimeAllowed(type, allowed) {
@@ -45,6 +45,11 @@ declare function writeBackupFile<C extends Config>(cms: EasyCMS<C>, file: string
45
45
  type Localization = Required<LocalizationConfig>;
46
46
 
47
47
  type Data = Record<string, unknown>;
48
+ /**
49
+ * `name@host.tld`: no whitespace, one `@`, and a dot inside the part after it. String operations,
50
+ * not a regex: `/^[^\s@]+@[^\s@]+\.[^\s@]+$/` takes time quadratic in the length.
51
+ */
52
+ declare function isEmail(value: string): boolean;
48
53
  /**
49
54
  * Turns text into a URL slug. Keeps letters and digits of any script,
50
55
  * so Thai titles stay readable: "สวัสดี World!" → "สวัสดี-world".
@@ -193,5 +198,5 @@ declare function createRootEndpointHandler<C extends Config>(instance: EasyCMS<C
193
198
  /** A cookie of the request; the `__Host-` one (set over HTTPS) first. */
194
199
  declare function readCookie(request: Request, name: string): string | undefined;
195
200
 
196
- export { CONFIG_FILE_NAMES as C, DEFAULT_ADMIN_PATH as D, readCookie as E, validateFields as F, writeBackupFile as G, 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, decryptBackup as p, fillMissing as q, resolveConfig as r, sharedEasyCMS as s, findConfigFile as t, forwardedClientIp as u, generateSlugs as v, importConfig as w, mergeForUpdate as x, parseId as y, platformClientIp as z };
201
+ export { CONFIG_FILE_NAMES as C, DEFAULT_ADMIN_PATH as D, platformClientIp as E, readCookie as F, validateFields as G, writeBackupFile as H, 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, decryptBackup as p, fillMissing as q, resolveConfig as r, sharedEasyCMS as s, findConfigFile as t, forwardedClientIp as u, generateSlugs as v, importConfig as w, isEmail as x, mergeForUpdate as y, parseId as z };
197
202
  export type { AdminBackup as A, BackupState as B, LoadConfigOptions as L, RestHandler as R, AdminBackups as a, ApiHandlerOptions as b, RestHandlerOptions as c };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { P as PaginatedDocs, I as ID, E as EmailSetting } from './config-BLxN8qcy.js';
2
2
  export { A as ADAPTER_API_VERSION, a as ADMIN_ICONS, b as Access, c as AccessArgs, d as AccessOptions, e as AdminBrand, f as AdminCommand, g as AdminComponent, h as AdminConfig, i as AdminFieldArgs, j as AdminIcon, k as AdminLocale, l as AdminPage, m as AdminRole, n as AdminRoleChange, o as AdminRoleField, p as AdminRoleTarget, q as AdminRoleView, r as AdminRoles, s as AdminSso, t as AdminSwitcher, u as AdminViewAccess, v as AfterChangeHook, w as AfterDeleteHook, x as AfterReadHook, y as ApiKeyContext, z as ApiKeyGlobalOperation, B as ApiKeyOperation, C as ApiKeyPermissions, D as ArrayField, F as AuditChange, G as AuditConfig, H as AuditEntry, J as AuditLog, K as AuditPage, L as AuditVerification, M as Auth, N as AuthConfig, O as AuthProvider, Q as AuthProviderProfile, R as AuthProviderRequest, S as AuthUser, T as BackupsConfig, U as BeforeChangeHook, V as BeforeDeleteHook, W as BeforeValidateHook, X as Block, Y as BlocksField, Z as BooleanField, _ as BuiltinApiKeysCollection, $ as BuiltinMediaCollection, a0 as BuiltinMediaFoldersCollection, a1 as BuiltinUsersCollection, a2 as BundleHints, a3 as CliCommand, a4 as CollectionAccess, a5 as CollectionAdmin, a6 as CollectionConfig, a7 as CollectionDocument, a8 as CollectionHooks, a9 as CollectionListAdmin, aa as CollectionSearch, ab as CollectionSlug, ac as Config, ad as ConfigError, ae as ConfigIssue, af as ContainerAdmin, ag as CreateEasyCMSOptions, ah as CreateInput, ai as CreateMigrationResult, aj as CursorDocs, ak as CustomField, al as CustomFieldTypes, am as DashboardWidget, an as Database, ao as DatabaseAdapter, ap as DatabaseInitArgs, aq as DatabaseTransfer, ar as DateField, as as DepthOptions, at as DirectUpload, au as DiskStorageOptions, av as DocumentOf, aw as EasyCMS, ax as EasyCMSError, ay as EmailAdapter, az as EmailField, aA as EmailMessage, aB as Endpoint, aC as EndpointRequest, aD as ErrorCode, aE as FIELD_TYPES, aF as Field, aG as FieldAccess, aH as FieldAccessFn, aI as FieldAdmin, aJ as FieldError, aK as FieldRule, aL as FieldType, aM as FieldTypeBase, aN as FieldTypeDefinition, aO as FieldTypeValidateContext, aP as FieldValidate, aQ as FieldValidateContext, aR as FieldValue, aS as FieldsInput, aT as FieldsValue, aU as FilterOptions, aV as FilterOptionsArgs, aW as FindAfterArgs, aX as FindArgs, aY as FindOptions, aZ as FolderLevel, a_ as FolderPermissions, a$ as ForbiddenError, b0 as GlobalAccess, b1 as GlobalConfig, b2 as GlobalDocument, b3 as GlobalHooks, b4 as GlobalInput, b5 as GlobalSlug, b6 as GroupField, b7 as ImageSize, b8 as IncrementOptions, b9 as InferCollection, ba as InferGlobal, bb as JobConfig, bc as JsonField, bd as KeysetPage, be as Label, bf as LayoutNode, bg as LivePreview, bh as LocaleOf, bi as LocalizationConfig, bj as LogDetails, bk as Logger, bl as LoginArgs, bm as LogsConfig, bn as MediaDocument, bo as MediaSize, bp as MembersConfig, bq as MembersSignup, br as MigrationInfo, bs as NavGroup, bt as NotFoundError, bu as NumberField, bv as OnRequest, bw as OnRequestArgs, bx as Operation, by as OutboxRow, bz as PLUGIN_API_VERSION, bA as PasswordEmail, bB as PasswordEmailArgs, bC as PasswordEmailFn, bD as PasswordLinkOptions, bE as PayloadTooLargeError, bF as Plugin, bG as PluginInfo, bH as PluginTypes, bI as PreviewTarget, bJ as PreviewURL, bK as QueryError, bL as ROLE_OPERATIONS, bM as RawDocument, bN as ReadOptions, bO as RelationshipField, bP as RequestContext, bQ as ResolvedConfig, bR as RichTextDocument, bS as RichTextField, bT as Role, bU as RoleOperation, bV as RolePermissions, bW as Roles, bX as RoutesConfig, bY as ScheduledPublish, bZ as SchemaError, b_ as SchemaMode, b$ as SearchAdapter, c0 as SearchEntry, c1 as SearchHits, c2 as SearchOptions, c3 as SearchQuery, c4 as SearchResults, c5 as SearchSetting, c6 as SelectField, c7 as SelectOption, c8 as Session, c9 as SidebarPanel, ca as SignupArgs, cb as SignupResult, cc as SingleSignOn, cd as SlugField, ce as SlugOf, cf as SsoOutcome, cg as SsoProviderRef, ch as StorageAdapter, ci as StoredFile, cj as TelemetryConfig, ck as TextField, cl as TextareaField, cm as TooManyRequestsError, cn as TypedPlugin, co as UnauthorizedError, cp as UncountedDocs, cq as UpdateInput, cr as UpdateOptions, cs as UploadConfig, ct as UploadField, cu as UploadFromURLConfig, cv as UserIdentity, cw as ValidationError, cx as Version, cy as VersionSummary, cz as VersionsConfig, cA as WEBHOOK_EVENTS, cB as WebhookConfig, cC as WebhookEvent, cD as WebhookPayload, cE as Where, cF as WhereOperators, cG as anyone, cH as consoleEmail, cI as consoleLogger, cJ as createEasyCMS, cK as defineConfig, cL as defineFieldType, cM as definePlugin, cN as diskStorage, cO as hasRows, cP as isAdmin, cQ as isHasMany, cR as isSignedIn, cS as isStaff, cT as isSystemAdmin, cU as jsonLogger, cV as memorySearch, cW as mimeAllowedBy, cX as rowFields, cY as silentLogger, cZ as uniqueWithinOf } from './config-BLxN8qcy.js';
3
3
  export { A as ADMIN_API_VERSION, a as AdminCollection, b as AdminComponentRef, c as AdminElementProps, d as AdminField, e as AdminGlobal, f as AdminLayoutNode, g as AdminNavGroup, h as AdminNavItem, i as AdminNavNode, j as AdminPageRef, k as AdminPageRoute, l as AdminSchema, m as AdminWidgetRef, S as SetFieldDetail, w as warnDeprecated } from './plugin-JIb70PKm.js';
4
- export { A as AdminBackup, a as AdminBackups, b as ApiHandlerOptions, B as BackupState, L as LoadConfigOptions, R as RestHandler, c as RestHandlerOptions, d as createApiHandler, e as createRestHandler, l as loadConfig, r as resolveConfig, s as sharedEasyCMS, f as slugify } from './handler-BjlBwaiO.js';
4
+ export { A as AdminBackup, a as AdminBackups, b as ApiHandlerOptions, B as BackupState, L as LoadConfigOptions, R as RestHandler, c as RestHandlerOptions, d as createApiHandler, e as createRestHandler, l as loadConfig, r as resolveConfig, s as sharedEasyCMS, f as slugify } from './handler-Ccc-CwBq.js';
5
5
  export { FieldCondition, matchesCondition } from './conditions.js';
6
6
 
7
7
  type DeliveryKind = 'webhook' | 'email';
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  sharedEasyCMS,
27
27
  silentLogger,
28
28
  slugify
29
- } from "./chunk-63UEVLN6.js";
29
+ } from "./chunk-Q3QLUBFK.js";
30
30
  import {
31
31
  matchesCondition
32
32
  } from "./chunk-U266LV4M.js";
@@ -51,7 +51,7 @@ import {
51
51
  defineFieldType,
52
52
  definePlugin,
53
53
  warnDeprecated
54
- } from "./chunk-RTKWMVAC.js";
54
+ } from "./chunk-SKGKGKUN.js";
55
55
  export {
56
56
  ADAPTER_API_VERSION,
57
57
  ADMIN_API_VERSION,
@@ -1,6 +1,6 @@
1
1
  import { Config, Database, EasyCMS, NavGroup, CollectionConfig, ID, RawDocument, Field, ResolvedConfig, ConfigIssue } from './index.js';
2
2
  export { c_ as API_KEYS, c$ as API_KEY_GLOBAL_OPERATIONS, d0 as API_KEY_OPERATIONS, d1 as DEFAULT_MAX_VERSIONS, d2 as DEFAULT_PASSWORD_EMAILS, d3 as FIELD_TYPE_BASES, d4 as FOLDER_LEVELS, d5 as MediaFolders, d6 as SSO_COOKIE, d7 as codeOfStatus, cO as hasRows, cQ as isHasMany, d8 as isJsonLogger, d9 as keyAllows, da as logFormat, cW as mimeAllowedBy, cX as rowFields, cZ as uniqueWithinOf } from './config-BLxN8qcy.js';
3
- export { C as CONFIG_FILE_NAMES, g as CSRF_COOKIE, h as CSRF_HEADER, D as DEFAULT_ADMIN_PATH, i as DEFAULT_API_PATH, j as DEFAULT_MAX_FILE_SIZE, k as DEFAULT_TOKEN_EXPIRATION, m as applyDefaults, n as applyPlugins, o as createRootEndpointHandler, p as decryptBackup, q as fillMissing, t as findConfigFile, u as forwardedClientIp, v as generateSlugs, w as importConfig, x as mergeForUpdate, y as parseId, z as platformClientIp, E as readCookie, F as validateFields, G as writeBackupFile } from './handler-BjlBwaiO.js';
3
+ export { C as CONFIG_FILE_NAMES, g as CSRF_COOKIE, h as CSRF_HEADER, D as DEFAULT_ADMIN_PATH, i as DEFAULT_API_PATH, j as DEFAULT_MAX_FILE_SIZE, k as DEFAULT_TOKEN_EXPIRATION, m as applyDefaults, n as applyPlugins, o as createRootEndpointHandler, p as decryptBackup, q as fillMissing, t as findConfigFile, u as forwardedClientIp, v as generateSlugs, w as importConfig, x as isEmail, y as mergeForUpdate, z as parseId, E as platformClientIp, F as readCookie, G as validateFields, H as writeBackupFile } from './handler-Ccc-CwBq.js';
4
4
  export { conditionIssues } from './conditions.js';
5
5
 
6
6
  /** The session cookie the REST API sets at login. */
package/dist/internal.js CHANGED
@@ -41,6 +41,7 @@ import {
41
41
  healthResponse,
42
42
  honeypotName,
43
43
  importConfig,
44
+ isEmail,
44
45
  isJsonLogger,
45
46
  keyAllows,
46
47
  logFormat,
@@ -56,7 +57,7 @@ import {
56
57
  verifyPassword,
57
58
  verifyTurnstile,
58
59
  writeBackupFile
59
- } from "./chunk-63UEVLN6.js";
60
+ } from "./chunk-Q3QLUBFK.js";
60
61
  import {
61
62
  conditionIssues
62
63
  } from "./chunk-U266LV4M.js";
@@ -74,7 +75,7 @@ import {
74
75
  rowFields,
75
76
  sniffMimeType,
76
77
  uniqueWithinOf
77
- } from "./chunk-RTKWMVAC.js";
78
+ } from "./chunk-SKGKGKUN.js";
78
79
 
79
80
  // src/typegen.ts
80
81
  var pascal = (name) => name.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
@@ -293,6 +294,7 @@ export {
293
294
  honeypotName,
294
295
  imageDimensions,
295
296
  importConfig,
297
+ isEmail,
296
298
  isHasMany,
297
299
  isJsonLogger,
298
300
  keyAllows,
package/dist/plugin.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  defineFieldType,
17
17
  definePlugin,
18
18
  warnDeprecated
19
- } from "./chunk-RTKWMVAC.js";
19
+ } from "./chunk-SKGKGKUN.js";
20
20
  export {
21
21
  ADMIN_API_VERSION,
22
22
  ADMIN_ICONS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easy-cms/core",
3
- "version": "1.15.0",
3
+ "version": "1.15.1",
4
4
  "description": "Embedded, code-first headless CMS for Nuxt and Next.js",
5
5
  "license": "MIT",
6
6
  "type": "module",