@easy-cms/core 0.13.1 → 0.14.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/index.d.ts CHANGED
@@ -169,8 +169,29 @@ interface Database {
169
169
  * do that itself (SQLite). Others (Postgres) leave it out; use their own tools.
170
170
  */
171
171
  readonly backup?: ((file: string) => Promise<void>) | undefined;
172
+ /** Raw table access for copying a whole database to another (`easy-cms copy`). */
173
+ readonly transfer?: DatabaseTransfer | undefined;
172
174
  destroy(): Promise<void>;
173
175
  }
176
+ /**
177
+ * Reads and writes stored rows as they are, ids and hidden fields included, bypassing validation
178
+ * and hooks. Only for moving a database: `copyDatabase()`.
179
+ */
180
+ interface DatabaseTransfer {
181
+ /**
182
+ * Identifies the tables and columns the config produces, the same for every database kind.
183
+ * Two databases can be copied into each other when their hashes match.
184
+ */
185
+ readonly schemaHash: string;
186
+ /** Every table, in an order that is safe to write. */
187
+ tables(): string[];
188
+ count(table: string): Promise<number>;
189
+ /** Rows in a stable order (by primary key), `offset` and `limit` for batches. */
190
+ read(table: string, offset: number, limit: number): Promise<Record<string, unknown>[]>;
191
+ write(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
192
+ /** After all writes, e.g. moves id sequences past the copied ids so new documents fit. */
193
+ finish(): Promise<void>;
194
+ }
174
195
  /** What `sqlite()` / `postgres()` return and what `config.db` holds. */
175
196
  interface DatabaseAdapter {
176
197
  readonly name: string;
@@ -1278,6 +1299,24 @@ declare const MEDIA = "media";
1278
1299
  /** Collections Easy CMS uses internally. Not exposed over REST or in the admin UI. */
1279
1300
  declare const INTERNAL_COLLECTIONS: ReadonlySet<string>;
1280
1301
 
1302
+ interface CopyProgress {
1303
+ readonly table: string;
1304
+ readonly rows: number;
1305
+ }
1306
+ interface CopyResult {
1307
+ readonly tables: number;
1308
+ readonly rows: number;
1309
+ }
1310
+ /**
1311
+ * Copies every stored row from `source` into `target` (another kind of database is fine, e.g.
1312
+ * SQLite to Postgres), keeping ids, so relationships, versions and users stay as they are.
1313
+ * Both must come from the same collections and fields, and the target must be empty.
1314
+ * Uploaded files are not copied: they stay in the storage they are in.
1315
+ */
1316
+ declare function copyDatabase(source: Database, target: Database, options?: {
1317
+ onTable?: (progress: CopyProgress) => void;
1318
+ }): Promise<CopyResult>;
1319
+
1281
1320
  interface ConfigIssue {
1282
1321
  /** Where the problem is, e.g. `collections.posts.fields.slug.from`. */
1283
1322
  readonly path: string;
@@ -1577,4 +1616,4 @@ declare const BUILTIN_COLLECTIONS: readonly ["users", "media"];
1577
1616
  /** Checks a config and returns every problem found. An empty array means it is valid. */
1578
1617
  declare function validateConfig(config: Config): ConfigIssue[];
1579
1618
 
1580
- export { ADMIN_ICONS, type Access, type AccessArgs, type AccessOptions, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminSchema, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ArrayField, Auth, type AuthConfig, type AuthUser, BUILTIN_COLLECTIONS, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CollectionAccess, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_TOKEN_EXPIRATION, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DateField, type DepthOptions, EXTENSIONS, EasyCMS, EasyCMSError, type EmailField, type Endpoint, type EndpointRequest, FIELD_TYPES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldType, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, 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, PayloadTooLargeError, type Plugin, type PreviewTarget, type PreviewURL, QueryError, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoutesConfig, SESSION_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, UnauthorizedError, type UpdateInput, type UploadConfig, type UploadField, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, consoleLogger, createEasyCMS, createRestHandler, defineConfig, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isLoggedIn, loadConfig, localStorage, mergeForUpdate, mimeAllowed, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword };
1619
+ export { ADMIN_ICONS, type Access, type AccessArgs, type AccessOptions, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminSchema, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ArrayField, Auth, type AuthConfig, type AuthUser, BUILTIN_COLLECTIONS, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CollectionAccess, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CopyProgress, type CopyResult, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_TOKEN_EXPIRATION, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DatabaseTransfer, type DateField, type DepthOptions, EXTENSIONS, EasyCMS, EasyCMSError, type EmailField, type Endpoint, type EndpointRequest, FIELD_TYPES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldType, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, 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, PayloadTooLargeError, type Plugin, type PreviewTarget, type PreviewURL, QueryError, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoutesConfig, SESSION_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, UnauthorizedError, type UpdateInput, type UploadConfig, type UploadField, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, consoleLogger, copyDatabase, createEasyCMS, createRestHandler, defineConfig, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isLoggedIn, loadConfig, localStorage, mergeForUpdate, mimeAllowed, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword };
package/dist/index.js CHANGED
@@ -750,6 +750,44 @@ function defineConfig(config) {
750
750
  return config;
751
751
  }
752
752
 
753
+ // src/copy.ts
754
+ var BATCH = 200;
755
+ async function copyDatabase(source, target, options = {}) {
756
+ const from = source.transfer;
757
+ const to = target.transfer;
758
+ if (!from || !to) throw new EasyCMSError("This database adapter cannot copy databases", 400);
759
+ if (from.schemaHash !== to.schemaHash) {
760
+ throw new EasyCMSError(
761
+ "The two configs describe different collections or fields. Copy between configs with the same collections (import one config into the other and change only `db`).",
762
+ 400
763
+ );
764
+ }
765
+ const tables = from.tables();
766
+ for (const table of tables) {
767
+ if (await to.count(table) > 0) {
768
+ throw new EasyCMSError(
769
+ `The target database is not empty ("${table}" has rows). Copy into a new, empty database.`,
770
+ 400
771
+ );
772
+ }
773
+ }
774
+ let total = 0;
775
+ for (const table of tables) {
776
+ let copied = 0;
777
+ for (; ; ) {
778
+ const rows = await from.read(table, copied, BATCH);
779
+ if (rows.length === 0) break;
780
+ await to.write(table, rows);
781
+ copied += rows.length;
782
+ if (rows.length < BATCH) break;
783
+ }
784
+ total += copied;
785
+ options.onTable?.({ table, rows: copied });
786
+ }
787
+ await to.finish();
788
+ return { tables: tables.length, rows: total };
789
+ }
790
+
753
791
  // src/document.ts
754
792
  import { randomUUID } from "crypto";
755
793
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -4814,6 +4852,7 @@ export {
4814
4852
  applyDefaults,
4815
4853
  applyPlugins,
4816
4854
  consoleLogger,
4855
+ copyDatabase,
4817
4856
  createEasyCMS,
4818
4857
  createRestHandler,
4819
4858
  defineConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easy-cms/core",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Embedded, code-first headless CMS for Nuxt and Next.js",
5
5
  "license": "MIT",
6
6
  "type": "module",