@jwerre/vellum 1.3.0-next.1 → 1.3.0-next.2

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.
@@ -8,6 +8,10 @@ export interface ValidationOptions {
8
8
  silent?: boolean;
9
9
  [key: string]: unknown;
10
10
  }
11
+ export interface CloneOptions {
12
+ keepId?: boolean;
13
+ deep?: boolean;
14
+ }
11
15
  /**
12
16
  * Abstract base class for creating model instances that interact with RESTful APIs.
13
17
  *
@@ -412,6 +416,41 @@ export declare abstract class Model<T extends object> {
412
416
  * }
413
417
  */
414
418
  validate(attributes: Partial<T>, options?: ValidationOptions): string | undefined;
419
+ /**
420
+ * Creates a new instance of the model with identical attributes.
421
+ *
422
+ * This method returns a new model instance that is a clone of the current model,
423
+ * with all attributes copied to the new instance. The clone is a separate object
424
+ * with its own state, so modifications to the clone will not affect the original
425
+ * model and vice versa. By default, the ID is removed so the cloned instance is
426
+ * considered "new" (isNew() returns true).
427
+ *
428
+ * @param {CloneOptions} [options] - Configuration options for cloning
429
+ * @param {boolean} [options.keepId=false] - If true, preserves the ID in the clone
430
+ * @param {boolean} [options.deep=false] - If true, performs a deep clone of nested objects/arrays
431
+ * @returns {this} A new instance of the same model class with cloned attributes
432
+ *
433
+ * @example
434
+ * // Clone a user model (default - no ID)
435
+ * const user = new User({ id: 1, name: 'John', email: 'john@example.com' });
436
+ * const userCopy = user.clone();
437
+ * console.log(userCopy.isNew()); // true
438
+ * console.log(userCopy.get('id')); // undefined
439
+ *
440
+ * @example
441
+ * // Clone with ID preserved
442
+ * const userCopy = user.clone({ keepId: true });
443
+ * console.log(userCopy.isNew()); // false
444
+ * console.log(userCopy.get('id')); // 1
445
+ *
446
+ * @example
447
+ * // Deep clone for nested objects
448
+ * const user = new User({ id: 1, name: 'John', settings: { theme: 'dark' } });
449
+ * const userCopy = user.clone({ deep: true });
450
+ * userCopy.get('settings').theme = 'light';
451
+ * console.log(user.get('settings').theme); // 'dark' (unchanged)
452
+ */
453
+ clone(options?: CloneOptions): this;
415
454
  /**
416
455
  * Performs HTTP synchronization with the server for CRUD operations.
417
456
  *
@@ -46,6 +46,22 @@ import { escapeHTML } from './utils.js';
46
46
  * await user.destroy(); // Deletes user
47
47
  */
48
48
  export class Model {
49
+ /**
50
+ * Internal reactive storage for all model attributes.
51
+ *
52
+ * This private field uses Svelte's $state rune to create a reactive object that
53
+ * holds all the model's attribute data. When attributes are modified, this reactivity
54
+ * ensures that any Svelte components using the model will automatically re-render
55
+ * to reflect the changes.
56
+ *
57
+ * The attributes are initialized as an empty object cast to type T, and are populated
58
+ * during construction by merging default values with provided data. All attribute
59
+ * access and modification should go through the public API methods (get, set, has, etc.)
60
+ * rather than directly accessing this field.
61
+ *
62
+ * @private
63
+ * @type {T}
64
+ */
49
65
  #attributes = $state({});
50
66
  /**
51
67
  * Validation error property that gets set when validation fails.
@@ -422,6 +438,53 @@ export class Model {
422
438
  // Default implementation - no validation
423
439
  return undefined;
424
440
  }
441
+ /**
442
+ * Creates a new instance of the model with identical attributes.
443
+ *
444
+ * This method returns a new model instance that is a clone of the current model,
445
+ * with all attributes copied to the new instance. The clone is a separate object
446
+ * with its own state, so modifications to the clone will not affect the original
447
+ * model and vice versa. By default, the ID is removed so the cloned instance is
448
+ * considered "new" (isNew() returns true).
449
+ *
450
+ * @param {CloneOptions} [options] - Configuration options for cloning
451
+ * @param {boolean} [options.keepId=false] - If true, preserves the ID in the clone
452
+ * @param {boolean} [options.deep=false] - If true, performs a deep clone of nested objects/arrays
453
+ * @returns {this} A new instance of the same model class with cloned attributes
454
+ *
455
+ * @example
456
+ * // Clone a user model (default - no ID)
457
+ * const user = new User({ id: 1, name: 'John', email: 'john@example.com' });
458
+ * const userCopy = user.clone();
459
+ * console.log(userCopy.isNew()); // true
460
+ * console.log(userCopy.get('id')); // undefined
461
+ *
462
+ * @example
463
+ * // Clone with ID preserved
464
+ * const userCopy = user.clone({ keepId: true });
465
+ * console.log(userCopy.isNew()); // false
466
+ * console.log(userCopy.get('id')); // 1
467
+ *
468
+ * @example
469
+ * // Deep clone for nested objects
470
+ * const user = new User({ id: 1, name: 'John', settings: { theme: 'dark' } });
471
+ * const userCopy = user.clone({ deep: true });
472
+ * userCopy.get('settings').theme = 'light';
473
+ * console.log(user.get('settings').theme); // 'dark' (unchanged)
474
+ */
475
+ clone(options) {
476
+ const Constructor = this.constructor;
477
+ let attrs = this.toJSON();
478
+ // Remove ID unless keepId is true
479
+ if (!options?.keepId) {
480
+ delete attrs[this.idAttribute];
481
+ }
482
+ // Perform deep clone if requested
483
+ if (options?.deep) {
484
+ attrs = structuredClone(attrs);
485
+ }
486
+ return new Constructor(attrs);
487
+ }
425
488
  /**
426
489
  * Performs HTTP synchronization with the server for CRUD operations.
427
490
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwerre/vellum",
3
- "version": "1.3.0-next.1",
3
+ "version": "1.3.0-next.2",
4
4
  "description": "Structural state management library for Svelte 5",
5
5
  "repository": {
6
6
  "type": "git",