@converse/skeletor 4.0.1 → 4.0.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.
package/CHANGES.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.2 (2026-11-05)
4
+
5
+ - Subclasses of `Model` and `Collection` now type-check for consumers with `strict: true` and
6
+ `skipLibCheck: false`. (#67)
7
+ - A subclass that declares `computed` still satisfies `T extends Model` (TS2344).
8
+ - `Collection#model` and `Collection#comparator` are no longer accessors, so subclasses can
9
+ override them as instance properties (`model = Contact`) as well as accessors (TS2610).
10
+ - `model` accepts model classes whose constructors take required or narrower attributes and
11
+ options (TS2416).
12
+ - `EventHandlersMap` no longer conflicts with its own index signature (TS2411).
13
+
14
+ `Collection` still falls back to `Model` when `model` is unset. The protected `_model` and
15
+ `_comparator` fields are gone; use `model` and `comparator`.
16
+
3
17
  ## 4.0.1 (2026-10-27)
4
18
 
5
19
  - The declarations for `Model` and `Collection` no longer contain `#private` members, so mixins
@@ -309,6 +309,22 @@ declare class PersistentStorage {
309
309
  getItemName(id: string | number): string;
310
310
  }
311
311
 
312
+ /**
313
+ * `model` and `comparator` are declared on an interface merged with the class
314
+ * rather than on the class itself, so that subclasses may override them either
315
+ * as instance properties (`model = Contact`) or as accessors (`get model()`).
316
+ * TypeScript rejects the one form overriding the other (TS2610, TS2611) only
317
+ * when the base member is declared on a class.
318
+ */
319
+ interface Collection<T extends Model = Model> {
320
+ /**
321
+ * The model class to instantiate when raw attributes are added. Defaults to
322
+ * `Model` when unset. Typed loosely so that model classes whose constructors
323
+ * take required or narrower attributes and options fit.
324
+ */
325
+ model: new (attributes?: any, options?: any) => T;
326
+ comparator: Comparator<T>;
327
+ }
312
328
  /**
313
329
  * @public
314
330
  * If models tend to represent a single row of data, a Collection is
@@ -321,20 +337,16 @@ declare class PersistentStorage {
321
337
  declare class Collection<T extends Model = Model> extends EventEmitterObject {
322
338
  [key: symbol]: () => CollectionIterator<T>;
323
339
  _storage?: PersistentStorage;
324
- _comparator?: Comparator<T>;
325
340
  _url: string;
326
341
  hydrated?: Promise<void>;
327
342
  models: T[];
328
343
  protected _byId: Record<string, T>;
329
- protected _model?: new (attributes?: Partial<ModelAttributes>, options?: ModelOptions) => T;
330
344
  /**
331
345
  * Create a new **Collection**, perhaps to contain a specific type of `model`.
332
346
  * If a `comparator` is specified, the Collection will maintain
333
347
  * its models in sort order, as they're added and removed.
334
348
  */
335
349
  constructor(models?: T[] | ModelAttributes[] | T | ModelAttributes, options?: CollectionOptions<T>);
336
- get comparator(): Comparator<T>;
337
- set comparator(c: Comparator<T>);
338
350
  /**
339
351
  * The canonical storage accessor. Set to a `PersistentStorage` instance
340
352
  * to enable local persistence for this collection.
@@ -358,12 +370,6 @@ declare class Collection<T extends Model = Model> extends EventEmitterObject {
358
370
  * record. A collection-level write would be redundant with that.
359
371
  */
360
372
  get autoSync(): boolean;
361
- /**
362
- * The default model for a collection is just a **Model**.
363
- * This should be overridden in most cases.
364
- */
365
- get model(): new (attributes?: Partial<ModelAttributes>, options?: ModelOptions) => T | Model;
366
- set model(model: new (attributes?: Partial<ModelAttributes>, options?: ModelOptions) => T);
367
373
  get length(): number;
368
374
  get url(): string;
369
375
  set url(url: string);
@@ -630,10 +636,7 @@ type EventListenerMap = Record<string, ListeningType>;
630
636
  /**
631
637
  * @public
632
638
  */
633
- interface EventHandlersMap {
634
- all?: EventHandler[];
635
- [name: string]: EventHandler[];
636
- }
639
+ type EventHandlersMap = Record<string, EventHandler[]>;
637
640
  /**
638
641
  * @public
639
642
  */
@@ -692,7 +695,7 @@ type Comparator<T extends Model = Model> = string | boolean | ((a: T, b: T) => n
692
695
  */
693
696
  type ComputedProperty<M> = {
694
697
  deps: string[];
695
- fn: (model: M) => any;
698
+ fn(model: M): any;
696
699
  };
697
700
  /**
698
701
  * @public
@@ -719,7 +722,7 @@ type FetchOrCreateOptions = Options & {
719
722
  * @public
720
723
  */
721
724
  type CollectionOptions<T extends Model = Model> = Options & {
722
- model?: new (attributes?: Partial<ModelAttributes>, options?: ModelOptions) => T;
725
+ model?: new (attributes?: any, options?: any) => T;
723
726
  comparator?: Comparator<T>;
724
727
  previousModels?: Model[];
725
728
  };
@@ -11677,6 +11677,7 @@ async function hydrate(collection) {
11677
11677
  * belonging to this particular author, and so on. Collections maintain
11678
11678
  * indexes of their models, both in order, and for lookup by `id`.
11679
11679
  */
11680
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- see the interface above
11680
11681
  class Collection extends EventEmitterObject {
11681
11682
  /**
11682
11683
  * Create a new **Collection**, perhaps to contain a specific type of `model`.
@@ -11694,7 +11695,7 @@ class Collection extends EventEmitterObject {
11694
11695
  options = options || {};
11695
11696
  this.preinitialize.apply(this, arguments);
11696
11697
  if (options.model)
11697
- this._model = options.model;
11698
+ this.model = options.model;
11698
11699
  if (options.comparator !== undefined)
11699
11700
  this.comparator = options.comparator;
11700
11701
  this._reset();
@@ -11709,12 +11710,6 @@ class Collection extends EventEmitterObject {
11709
11710
  this.hydrated = getStorage(this) ? hydrate(this) : Promise.resolve();
11710
11711
  }
11711
11712
  }
11712
- get comparator() {
11713
- return this._comparator;
11714
- }
11715
- set comparator(c) {
11716
- this._comparator = c;
11717
- }
11718
11713
  /**
11719
11714
  * The canonical storage accessor. Set to a `PersistentStorage` instance
11720
11715
  * to enable local persistence for this collection.
@@ -11750,16 +11745,6 @@ class Collection extends EventEmitterObject {
11750
11745
  get autoSync() {
11751
11746
  return false;
11752
11747
  }
11753
- /**
11754
- * The default model for a collection is just a **Model**.
11755
- * This should be overridden in most cases.
11756
- */
11757
- get model() {
11758
- return this._model ?? Model;
11759
- }
11760
- set model(model) {
11761
- this._model = model;
11762
- }
11763
11748
  get length() {
11764
11749
  return this.models.length;
11765
11750
  }
@@ -12263,7 +12248,7 @@ class Collection extends EventEmitterObject {
12263
12248
  * Define how to uniquely identify models in the collection.
12264
12249
  */
12265
12250
  modelId(attrs) {
12266
- return attrs[this.model.prototype?.idAttribute || 'id'];
12251
+ return attrs[(this.model ?? Model).prototype?.idAttribute || 'id'];
12267
12252
  }
12268
12253
  /** Get an iterator of all models in this collection. */
12269
12254
  values() {
@@ -12286,7 +12271,7 @@ class Collection extends EventEmitterObject {
12286
12271
  this._byId = {};
12287
12272
  }
12288
12273
  createModel(attrs, options) {
12289
- const Klass = this.model;
12274
+ const Klass = this.model ?? Model;
12290
12275
  return new Klass(attrs, options);
12291
12276
  }
12292
12277
  /**