@jwerre/vellum 1.1.0 → 1.2.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/README.md +97 -48
- package/dist/Collection.svelte.d.ts +45 -0
- package/dist/Collection.svelte.js +48 -0
- package/dist/Model.svelte.js +1 -1
- package/dist/config.svelte.d.ts +3 -0
- package/dist/config.svelte.js +9 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -178,12 +178,10 @@ Vellum works seamlessly with Svelte 5 components.
|
|
|
178
178
|
|
|
179
179
|
There is a working example of Vellum in the `routes` directory. To run it, clone the repository, install dependencies, and run the development server:
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
npm
|
|
185
|
-
npm run dev
|
|
186
|
-
```
|
|
181
|
+
git clone https://github.com/jwerre/vellum.git
|
|
182
|
+
cd vellum
|
|
183
|
+
npm install
|
|
184
|
+
npm run dev
|
|
187
185
|
|
|
188
186
|
## API Documentation
|
|
189
187
|
|
|
@@ -198,58 +196,63 @@ npm run dev
|
|
|
198
196
|
- [Model](#model)
|
|
199
197
|
- [Parameters](#parameters-1)
|
|
200
198
|
- [Examples](#examples-1)
|
|
201
|
-
- [validationError](#validationerror)
|
|
202
199
|
- [idAttribute](#idattribute)
|
|
200
|
+
- [Examples](#examples-2)
|
|
201
|
+
- [validationError](#validationerror)
|
|
202
|
+
- [defaults](#defaults)
|
|
203
|
+
- [Examples](#examples-3)
|
|
203
204
|
- [get](#get)
|
|
204
205
|
- [Parameters](#parameters-2)
|
|
205
|
-
- [Examples](#examples-
|
|
206
|
+
- [Examples](#examples-4)
|
|
206
207
|
- [has](#has)
|
|
207
208
|
- [Parameters](#parameters-3)
|
|
208
|
-
- [Examples](#examples-
|
|
209
|
+
- [Examples](#examples-5)
|
|
209
210
|
- [unset](#unset)
|
|
210
211
|
- [Parameters](#parameters-4)
|
|
211
|
-
- [Examples](#examples-
|
|
212
|
+
- [Examples](#examples-6)
|
|
212
213
|
- [clear](#clear)
|
|
213
|
-
- [Examples](#examples-
|
|
214
|
+
- [Examples](#examples-7)
|
|
214
215
|
- [escape](#escape)
|
|
215
216
|
- [Parameters](#parameters-5)
|
|
216
|
-
- [Examples](#examples-
|
|
217
|
+
- [Examples](#examples-8)
|
|
217
218
|
- [isNew](#isnew)
|
|
218
219
|
- [isValid](#isvalid)
|
|
219
220
|
- [Parameters](#parameters-6)
|
|
220
|
-
- [Examples](#examples-
|
|
221
|
+
- [Examples](#examples-9)
|
|
221
222
|
- [validate](#validate)
|
|
222
223
|
- [Parameters](#parameters-7)
|
|
223
|
-
- [Examples](#examples-
|
|
224
|
+
- [Examples](#examples-10)
|
|
224
225
|
- [sync](#sync)
|
|
225
226
|
- [Parameters](#parameters-8)
|
|
226
|
-
- [Examples](#examples-
|
|
227
|
+
- [Examples](#examples-11)
|
|
227
228
|
- [fetch](#fetch)
|
|
228
|
-
- [Examples](#examples-
|
|
229
|
+
- [Examples](#examples-12)
|
|
229
230
|
- [save](#save)
|
|
230
231
|
- [Parameters](#parameters-9)
|
|
231
|
-
- [Examples](#examples-
|
|
232
|
+
- [Examples](#examples-13)
|
|
232
233
|
- [destroy](#destroy)
|
|
233
|
-
- [Examples](#examples-
|
|
234
|
+
- [Examples](#examples-14)
|
|
234
235
|
- [toJSON](#tojson)
|
|
235
|
-
- [Examples](#examples-
|
|
236
|
+
- [Examples](#examples-15)
|
|
237
|
+
- [validationError](#validationerror-1)
|
|
236
238
|
- [Collection](#collection)
|
|
237
239
|
- [Parameters](#parameters-10)
|
|
238
|
-
- [Examples](#examples-
|
|
240
|
+
- [Examples](#examples-16)
|
|
239
241
|
- [items](#items)
|
|
240
242
|
- [length](#length)
|
|
241
243
|
- [add](#add)
|
|
242
244
|
- [Parameters](#parameters-11)
|
|
243
|
-
- [Examples](#examples-
|
|
245
|
+
- [Examples](#examples-17)
|
|
246
|
+
- [sort](#sort)
|
|
244
247
|
- [reset](#reset)
|
|
245
248
|
- [Parameters](#parameters-12)
|
|
246
|
-
- [Examples](#examples-
|
|
249
|
+
- [Examples](#examples-18)
|
|
247
250
|
- [find](#find)
|
|
248
251
|
- [Parameters](#parameters-13)
|
|
249
|
-
- [Examples](#examples-
|
|
252
|
+
- [Examples](#examples-19)
|
|
250
253
|
- [fetch](#fetch-1)
|
|
251
254
|
- [Parameters](#parameters-14)
|
|
252
|
-
- [Examples](#examples-
|
|
255
|
+
- [Examples](#examples-20)
|
|
253
256
|
|
|
254
257
|
### vellumConfig
|
|
255
258
|
|
|
@@ -269,6 +272,7 @@ with existing headers rather than replaced entirely.
|
|
|
269
272
|
- `config` **Partial\<VellumConfig>** Partial configuration object with properties to update
|
|
270
273
|
- `config.origin` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** New origin URL to set
|
|
271
274
|
- `config.headers` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>?** Headers to merge with existing headers
|
|
275
|
+
- `config.idAttribute` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** The default unique identifier attribute for models (optional, default `"id"`)
|
|
272
276
|
|
|
273
277
|
#### Examples
|
|
274
278
|
|
|
@@ -312,7 +316,6 @@ Key features:
|
|
|
312
316
|
#### Parameters
|
|
313
317
|
|
|
314
318
|
- `data` (optional, default `{}`)
|
|
315
|
-
- `options` (optional, default `{}`)
|
|
316
319
|
|
|
317
320
|
#### Examples
|
|
318
321
|
|
|
@@ -326,9 +329,12 @@ interface UserAttributes {
|
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
class User extends Model<UserAttributes> {
|
|
329
|
-
endpoint()
|
|
332
|
+
endpoint() {
|
|
330
333
|
return '/users';
|
|
331
334
|
}
|
|
335
|
+
defaults() {
|
|
336
|
+
return { name: '', createdAt: new Date() };
|
|
337
|
+
}
|
|
332
338
|
}
|
|
333
339
|
|
|
334
340
|
// Create and use a model instance
|
|
@@ -336,40 +342,73 @@ const user = new User({ name: 'John Doe', email: 'john@example.com' });
|
|
|
336
342
|
await user.save(); // Creates new user on server
|
|
337
343
|
user.set('name', 'Jane Doe');
|
|
338
344
|
await user.save(); // Updates existing user
|
|
345
|
+
await user.destroy(); // Deletes user
|
|
339
346
|
```
|
|
340
347
|
|
|
341
|
-
|
|
342
|
-
// Using custom ID attribute (e.g., MongoDB _id)
|
|
343
|
-
interface MongoUserAttributes {
|
|
344
|
-
_id?: string;
|
|
345
|
-
username: string;
|
|
346
|
-
profile: {
|
|
347
|
-
firstName: string;
|
|
348
|
-
lastName: string;
|
|
349
|
-
};
|
|
350
|
-
}
|
|
348
|
+
#### idAttribute
|
|
351
349
|
|
|
352
|
-
|
|
353
|
-
constructor(data?: Partial<MongoUserAttributes>) {
|
|
354
|
-
super(data, { idAttribute: '_id' });
|
|
355
|
-
}
|
|
350
|
+
The name of the attribute that serves as the unique identifier for this model instance.
|
|
356
351
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
352
|
+
This private field stores the attribute name that will be used to identify the model's
|
|
353
|
+
primary key when performing operations like determining if the model is new, constructing
|
|
354
|
+
URLs for API requests, and managing model identity. The default value is 'id', but it
|
|
355
|
+
can be customized through the ModelOptions parameter in the constructor.
|
|
356
|
+
|
|
357
|
+
Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)
|
|
358
|
+
|
|
359
|
+
##### Examples
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
// Default behavior uses 'id' as the identifier
|
|
363
|
+
const user = new User({ id: 1, name: 'John' });
|
|
364
|
+
|
|
365
|
+
// Custom ID attribute can be specified in constructor options
|
|
366
|
+
class User extends Model<UserSchema> {
|
|
367
|
+
idAttribute = '_id
|
|
368
|
+
endpoint(): string {
|
|
369
|
+
return '/users';
|
|
370
|
+
}
|
|
360
371
|
}
|
|
372
|
+
const user = new User({ _id: '507f1f77bcf86cd799439011', name: 'John' });
|
|
361
373
|
```
|
|
362
374
|
|
|
363
375
|
#### validationError
|
|
364
376
|
|
|
365
|
-
|
|
366
|
-
This property contains the error returned by the validate method.
|
|
377
|
+
Gets the latest validation error.
|
|
367
378
|
|
|
368
|
-
####
|
|
379
|
+
#### defaults
|
|
380
|
+
|
|
381
|
+
Provides default attribute values for new model instances.
|
|
369
382
|
|
|
370
|
-
|
|
383
|
+
This method is called during model construction to establish initial attribute
|
|
384
|
+
values before applying any user-provided data. Subclasses can override this
|
|
385
|
+
method to define default values for their specific attributes, ensuring that
|
|
386
|
+
models always have sensible initial state.
|
|
371
387
|
|
|
372
|
-
|
|
388
|
+
The defaults are applied first, then any data passed to the constructor will
|
|
389
|
+
override these default values. This allows for flexible model initialization
|
|
390
|
+
where some attributes have fallback values while others can be explicitly set.
|
|
391
|
+
|
|
392
|
+
##### Examples
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// Override in a User model subclass
|
|
396
|
+
protected defaults(): Partial<UserAttributes> {
|
|
397
|
+
return {
|
|
398
|
+
role: 'user',
|
|
399
|
+
isActive: true,
|
|
400
|
+
createdAt: new Date()
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
```javascript
|
|
406
|
+
// Creating a model with defaults
|
|
407
|
+
const user = new User({ name: 'John' });
|
|
408
|
+
// Resulting attributes: { role: 'user', isActive: true, createdAt: Date, name: 'John' }
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Returns **Partial\<T>** A partial object containing default attribute values
|
|
373
412
|
|
|
374
413
|
#### get
|
|
375
414
|
|
|
@@ -766,6 +805,11 @@ const jsonString = JSON.stringify(user.toJSON());
|
|
|
766
805
|
|
|
767
806
|
Returns **T** A plain object containing all of the model's attributes
|
|
768
807
|
|
|
808
|
+
### validationError
|
|
809
|
+
|
|
810
|
+
Validation error property that gets set when validation fails.
|
|
811
|
+
This property contains the error returned by the validate method.
|
|
812
|
+
|
|
769
813
|
### Collection
|
|
770
814
|
|
|
771
815
|
Abstract base class for managing collections of Model instances.
|
|
@@ -820,6 +864,11 @@ collection.add(existingUser);
|
|
|
820
864
|
|
|
821
865
|
Returns **any** The model instance that was added to the collection
|
|
822
866
|
|
|
867
|
+
#### sort
|
|
868
|
+
|
|
869
|
+
Sorts the collection using the comparator if one is defined.
|
|
870
|
+
Called automatically when items are added to the collection.
|
|
871
|
+
|
|
823
872
|
#### reset
|
|
824
873
|
|
|
825
874
|
Resets the collection with new data, replacing all existing items.
|
|
@@ -33,6 +33,46 @@ export declare abstract class Collection<M extends Model<T>, T extends object> {
|
|
|
33
33
|
};
|
|
34
34
|
/** Returns the API endpoint URL for this collection */
|
|
35
35
|
abstract endpoint(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Optional comparator for sorting the collection.
|
|
38
|
+
*
|
|
39
|
+
* By default, there is no comparator for a collection. If you define a comparator,
|
|
40
|
+
* it will be used to sort the collection any time a model is added.
|
|
41
|
+
*
|
|
42
|
+
* A comparator can be defined in three ways:
|
|
43
|
+
*
|
|
44
|
+
* 1. **sortBy** - Pass a function that takes a single model argument and returns
|
|
45
|
+
* a numeric or string value by which the model should be ordered relative to others.
|
|
46
|
+
*
|
|
47
|
+
* 2. **sort** - Pass a comparator function that takes two model arguments and returns
|
|
48
|
+
* -1 if the first model should come before the second, 0 if they are of the same
|
|
49
|
+
* rank, and 1 if the first model should come after.
|
|
50
|
+
*
|
|
51
|
+
* 3. **attribute name** - Pass a string indicating the attribute to sort by.
|
|
52
|
+
*
|
|
53
|
+
* Note: The implementation depends on the arity of your comparator function to
|
|
54
|
+
* determine between sortBy (1 argument) and sort (2 arguments) styles, so be
|
|
55
|
+
* careful if your comparator function is bound.
|
|
56
|
+
*
|
|
57
|
+
* @returns A comparator function, string attribute name, or undefined for no sorting
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Sort by attribute name
|
|
61
|
+
* comparator = () => 'createdAt';
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Sort using sortBy (single argument)
|
|
65
|
+
* comparator = () => (model: UserModel) => model.get('age');
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Sort using sort comparator (two arguments)
|
|
69
|
+
* comparator = () => (a: UserModel, b: UserModel) => {
|
|
70
|
+
* if (a.get('priority') < b.get('priority')) return -1;
|
|
71
|
+
* if (a.get('priority') > b.get('priority')) return 1;
|
|
72
|
+
* return 0;
|
|
73
|
+
* };
|
|
74
|
+
*/
|
|
75
|
+
comparator?(): string | ((model: M) => string | number) | ((a: M, b: M) => number) | undefined;
|
|
36
76
|
/**
|
|
37
77
|
* Creates a new Collection instance.
|
|
38
78
|
*
|
|
@@ -66,6 +106,11 @@ export declare abstract class Collection<M extends Model<T>, T extends object> {
|
|
|
66
106
|
* collection.add(existingUser);
|
|
67
107
|
*/
|
|
68
108
|
add(data: T | M): M;
|
|
109
|
+
/**
|
|
110
|
+
* Sorts the collection using the comparator if one is defined.
|
|
111
|
+
* Called automatically when items are added to the collection.
|
|
112
|
+
*/
|
|
113
|
+
private sort;
|
|
69
114
|
/**
|
|
70
115
|
* Resets the collection with new data, replacing all existing items.
|
|
71
116
|
*
|
|
@@ -65,8 +65,55 @@ export class Collection {
|
|
|
65
65
|
add(data) {
|
|
66
66
|
const instance = data instanceof Model ? data : new this.model(data);
|
|
67
67
|
this.items.push(instance);
|
|
68
|
+
this.sort();
|
|
68
69
|
return instance;
|
|
69
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Sorts the collection using the comparator if one is defined.
|
|
73
|
+
* Called automatically when items are added to the collection.
|
|
74
|
+
*/
|
|
75
|
+
sort() {
|
|
76
|
+
if (!this.comparator) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const comparator = this.comparator();
|
|
80
|
+
if (!comparator) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// String attribute name
|
|
84
|
+
if (typeof comparator === 'string') {
|
|
85
|
+
const attr = comparator;
|
|
86
|
+
this.items.sort((a, b) => {
|
|
87
|
+
const aVal = a.get(attr);
|
|
88
|
+
const bVal = b.get(attr);
|
|
89
|
+
if (aVal < bVal)
|
|
90
|
+
return -1;
|
|
91
|
+
if (aVal > bVal)
|
|
92
|
+
return 1;
|
|
93
|
+
return 0;
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Function comparator - check arity
|
|
98
|
+
if (comparator.length === 1) {
|
|
99
|
+
// sortBy function (single argument)
|
|
100
|
+
const sortByFn = comparator;
|
|
101
|
+
this.items.sort((a, b) => {
|
|
102
|
+
const aVal = sortByFn(a);
|
|
103
|
+
const bVal = sortByFn(b);
|
|
104
|
+
if (aVal < bVal)
|
|
105
|
+
return -1;
|
|
106
|
+
if (aVal > bVal)
|
|
107
|
+
return 1;
|
|
108
|
+
return 0;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// sort function (two arguments)
|
|
113
|
+
const sortFn = comparator;
|
|
114
|
+
this.items.sort(sortFn);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
70
117
|
/**
|
|
71
118
|
* Resets the collection with new data, replacing all existing items.
|
|
72
119
|
*
|
|
@@ -81,6 +128,7 @@ export class Collection {
|
|
|
81
128
|
*/
|
|
82
129
|
reset(data) {
|
|
83
130
|
this.items = data.map((attrs) => new this.model(attrs));
|
|
131
|
+
this.sort();
|
|
84
132
|
}
|
|
85
133
|
/**
|
|
86
134
|
* Finds the first item in the collection that matches the given query.
|
package/dist/Model.svelte.js
CHANGED
package/dist/config.svelte.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface VellumConfig {
|
|
2
2
|
origin: string;
|
|
3
3
|
headers: Record<string, string>;
|
|
4
|
+
idAttribute: string;
|
|
4
5
|
}
|
|
5
6
|
/**
|
|
6
7
|
* Global reactive state for Vellum configuration. Uses Svelte's $state rune to
|
|
@@ -8,6 +9,7 @@ export interface VellumConfig {
|
|
|
8
9
|
*
|
|
9
10
|
* @default origin - Empty string (must be configured before use)
|
|
10
11
|
* @default headers - Contains 'Content-Type': 'application/json'
|
|
12
|
+
* @default idAttribute - The default unique identifier attribute for models
|
|
11
13
|
*/
|
|
12
14
|
export declare const vellumConfig: VellumConfig;
|
|
13
15
|
/**
|
|
@@ -20,6 +22,7 @@ export declare const vellumConfig: VellumConfig;
|
|
|
20
22
|
* @param {Partial<VellumConfig>} config - Partial configuration object with properties to update
|
|
21
23
|
* @param {string} [config.origin] - New origin URL to set
|
|
22
24
|
* @param {Record<string, string>} [config.headers] - Headers to merge with existing headers
|
|
25
|
+
* @param {string} [config.idAttribute="id"] - The default unique identifier attribute for models
|
|
23
26
|
*
|
|
24
27
|
* @example
|
|
25
28
|
* // Set the API origin
|
package/dist/config.svelte.js
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @default origin - Empty string (must be configured before use)
|
|
6
6
|
* @default headers - Contains 'Content-Type': 'application/json'
|
|
7
|
+
* @default idAttribute - The default unique identifier attribute for models
|
|
7
8
|
*/
|
|
8
9
|
export const vellumConfig = $state({
|
|
9
10
|
origin: '',
|
|
10
11
|
headers: {
|
|
11
12
|
'Content-Type': 'application/json'
|
|
12
|
-
}
|
|
13
|
+
},
|
|
14
|
+
idAttribute: 'id'
|
|
13
15
|
});
|
|
14
16
|
/**
|
|
15
17
|
* Helper function to update global Vellum configuration
|
|
@@ -21,6 +23,7 @@ export const vellumConfig = $state({
|
|
|
21
23
|
* @param {Partial<VellumConfig>} config - Partial configuration object with properties to update
|
|
22
24
|
* @param {string} [config.origin] - New origin URL to set
|
|
23
25
|
* @param {Record<string, string>} [config.headers] - Headers to merge with existing headers
|
|
26
|
+
* @param {string} [config.idAttribute="id"] - The default unique identifier attribute for models
|
|
24
27
|
*
|
|
25
28
|
* @example
|
|
26
29
|
* // Set the API origin
|
|
@@ -41,8 +44,12 @@ export const vellumConfig = $state({
|
|
|
41
44
|
* });
|
|
42
45
|
*/
|
|
43
46
|
export const configureVellum = (config) => {
|
|
44
|
-
if (config.origin)
|
|
47
|
+
if (config.origin?.length) {
|
|
45
48
|
vellumConfig.origin = config.origin;
|
|
49
|
+
}
|
|
50
|
+
if (config.idAttribute?.length) {
|
|
51
|
+
vellumConfig.idAttribute = config.idAttribute;
|
|
52
|
+
}
|
|
46
53
|
if (config.headers) {
|
|
47
54
|
vellumConfig.headers = { ...vellumConfig.headers, ...config.headers };
|
|
48
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jwerre/vellum",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Structural state management library for Svelte 5",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"types:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
33
33
|
"check": "npm audit --audit-level=moderate --omit=dev && npm run format && npm run lint && npm run spell && npm run types && npm test && npm run build && npm run release:dry",
|
|
34
34
|
"dev": "vite dev",
|
|
35
|
-
"docs": "npm run build && documentation readme dist/index.js --section=\"API Documentation\"",
|
|
35
|
+
"docs": "npm run build && documentation readme dist/index.js --section=\"API Documentation\" && prettier --write README.md",
|
|
36
36
|
"format": "prettier --check .",
|
|
37
37
|
"format:write": "prettier --write .",
|
|
38
38
|
"lint": "prettier --check . && eslint .",
|