Update README for the new version and add tests status badge.
All checks were successful
/ test (push) Successful in 19s

This commit is contained in:
Madeorsk 2025-03-30 12:51:04 +02:00
parent 7e86e6fe86
commit 8b1a1dabcf
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F

233
README.md
View file

@ -19,42 +19,64 @@
</p>
<p align="center">
<img alt="Version 3.3.0" src="https://img.shields.io/badge/version-3.3.0-blue" />
<a href="https://www.npmjs.com/package/@sharkitek/core" target="_blank">
<img alt="Latest release" src="https://code.zeptotech.net/Sharkitek/Core/badges/release.svg" />
</a>
<img alt="Tests status" src="https://code.zeptotech.net/Sharkitek/Core/badges/workflows/test.yaml/badge.svg" />
</p>
## Introduction
Sharkitek is a Javascript / TypeScript library designed to ease development of client-side models.
Sharkitek is a lightweight Javascript / TypeScript library designed to ease development of models.
With Sharkitek, you define the architecture of your models by specifying their properties and their types.
Then, you can use the defined methods like `serialize`, `deserialize`, `patch` or `serializeDiff`.
Then, you can use the defined methods like `serialize`, `parse`, `patch` or `serializeDiff`.
```typescript
class Example extends s.model({
id: s.property.numeric(),
name: s.property.string(),
})
class Example
{
static model = defineModel({
Class: Example,
properties: {
id: s.property.numeric(),
name: s.property.string(),
},
identifier: "id",
});
id: number;
name: string;
}
```
## Examples
## Quick start
### Simple model definition
**Note**: by convention, we define our models in a `model` static variable in the model's class. It is a good way to keep your model declaration near the actual class, and its usage will be more natural.
### Model definition
```typescript
/**
* A person.
*/
class Person extends s.model({
id: s.property.numeric(),
name: s.property.string(),
firstName: s.property.string(),
email: s.property.string(),
createdAt: s.property.date(),
active: s.property.boolean(),
}, "id")
class Person
{
static model = defineModel({
Class: Person,
properties: {
id: s.property.numeric(),
name: s.property.string(),
email: s.property.string(),
createdAt: s.property.date(),
active: s.property.boolean(),
},
identifier: "id",
});
id: number;
name: string;
email: string;
createdAt: Date;
active: boolean = true;
}
```
@ -63,22 +85,28 @@ class Person extends s.model({
/**
* An article.
*/
class Article extends s.model({
id: s.property.numeric(),
title: s.property.string(),
authors: s.property.array(s.property.model(Author)),
text: s.property.string(),
evaluation: s.property.decimal(),
tags: s.property.array(
s.property.object({
name: s.property.string(),
})
),
}, "id")
class Article
{
static model = defineModel({
Class: Article,
properties: {
id: s.property.numeric(),
title: s.property.string(),
authors: s.property.array(s.property.model(Person)),
text: s.property.string(),
evaluation: s.property.decimal(),
tags: s.property.array(
s.property.object({
name: s.property.string(),
})
),
},
identifier: "id",
});
id: number;
title: string;
authors: Author[] = [];
authors: Person[] = [];
text: string;
evaluation: number;
tags: {
@ -87,6 +115,84 @@ class Article extends s.model({
}
```
```typescript
/**
* A model with composite keys.
*/
class CompositeKeys
{
static model = defineModel({
Class: CompositeKeys,
properties: {
id1: s.property.numeric(),
id2: s.property.string(),
},
identifier: ["id1", "id2"],
});
id1: number;
id2: string;
}
```
### Model functions
#### Serialization
```typescript
const instance = new Person();
instance.id = 1;
instance.createdAt = new Date();
instance.name = "John Doe";
instance.email = "john@doe.test";
instance.active = true;
const serialized = Person.model.model(instance).serialize();
console.log(serialized); // { id: 1, createdAt: "YYYY-MM-DDTHH:mm:ss.sssZ", name: "John Doe", email: "john@doe.test", active: true }
```
#### Deserialization
```typescript
const instance = Person.model.parse({
id: 1,
createdAt: "2011-10-05T14:48:00.000Z",
name: "John Doe",
email: "john@doe.test",
active: true,
});
console.log(instance instanceof Person); // true
console.log(instance.createdAt instanceof Date); // true
```
#### Patch
```typescript
const instance = Person.model.parse({
id: 1,
createdAt: "2011-10-05T14:48:00.000Z",
name: "John Doe",
email: "john@doe.test",
active: true,
});
instance.name = "Johnny";
// Patch serialized only changed properties and the identifier.
console.log(Person.model.model(instance).patch()); // { id: 1, name: "Johnny" }
// If you run it one more time, already patched properties will not be included again.
console.log(Person.model.model(instance).patch()); // { id: 1 }
```
#### Identifier
```typescript
const instance = new CompositeKeys();
instance.id1 = 5;
instance.id2 = "foo";
const instanceIdentifier = CompositeKeys.model.model(instance).getIdentifier();
console.log(instanceIdentifier); // [5, "foo"]
```
## API
### Types
@ -95,7 +201,7 @@ Types are defined by a class extending `Type`.
Sharkitek defines some basic types by default, in these classes:
- `BoolType`: boolean value in the model, boolean value in the serialized object.
- `BooleanType`: boolean value in the model, boolean value in the serialized object.
- `StringType`: string in the model, string in the serialized object.
- `NumericType`: number in the model, number in the serialized object.
- `DecimalType`: number in the model, formatted string in the serialized object.
@ -107,18 +213,22 @@ Sharkitek defines some basic types by default, in these classes:
When you are defining a property of a Sharkitek model, you must provide its type by instantiating one of these classes.
```typescript
class Example extends s.model({
foo: s.property.define(new StringType()),
})
class Example
{
static model = defineModel({
Class: Example,
properties: {
foo: s.property.define(new StringType()),
},
});
foo: string;
}
```
To ease the use of these classes and reduce read complexity,
properties of each type are easily definable with a function for each type.
To ease the use of these classes and reduce read complexity, properties of each type are easily definable with a function for each type.
- `BoolType` => `s.property.boolean`
- `BooleanType` => `s.property.boolean`
- `StringType` => `s.property.string`
- `NumericType` => `s.property.numeric`
- `DecimalType` => `s.property.decimal`
@ -127,21 +237,32 @@ properties of each type are easily definable with a function for each type.
- `ObjectType` => `s.property.object`
- `ModelType` => `s.property.model`
Type implementers should provide a corresponding function for each defined type. They can even provide
multiple functions or constants with predefined parameters.
(For example, we could define `s.property.stringArray()` which would be similar to `s.property.array(s.property.string())`.)
Type implementers should provide a corresponding function for each defined type. They can even provide multiple functions or constants with predefined parameters. For example, we could define `s.property.stringArray()` which would be similar to `s.property.array(s.property.string())`.
```typescript
class Example extends s.model({
foo: s.property.string(),
})
class Example
{
static model = defineModel({
Class: Example,
properties: {
foo: s.property.string(),
},
});
foo: string;
}
```
### Models
#### `model(instance)`
Get a model class (which has all the sharkitek models' functions) from a model instance.
```typescript
const model = definedModel.model(modelInstance);
```
#### `serialize()`
Serialize the model.
@ -149,17 +270,17 @@ Serialize the model.
Example:
```typescript
const serializedObject = model.serialize();
const serializedObject = definedModel.model(modelInstance).serialize();
```
#### `deserialize(serializedObject)`
#### `parse(serializedObject)`
Deserialize the model.
Example:
```typescript
const model = (new TestModel()).deserialize({
const modelInstance = definedModel.parse({
id: 5,
title: "Hello World!",
users: [
@ -178,7 +299,7 @@ Serialize the difference between current model state and original one.
Example:
```typescript
const model = (new TestModel()).deserialize({
const modelInstance = definedModel.parse({
id: 5,
title: "Hello World!",
users: [
@ -189,9 +310,9 @@ const model = (new TestModel()).deserialize({
],
});
model.title = "A new title for a new world";
modelInstance.title = "A new title for a new world";
const result = model.serializeDiff();
const result = definedModel.model(modelInstance).serializeDiff();
// if `id` is defined as the model identifier:
// result = { id: 5, title: "A new title for a new world" }
// if `id` is not defined as the model identifier:
@ -205,7 +326,7 @@ Set current properties values as original values.
Example:
```typescript
const model = (new TestModel()).deserialize({
const modelInstance = definedModel.parse({
id: 5,
title: "Hello World!",
users: [
@ -216,11 +337,11 @@ const model = (new TestModel()).deserialize({
],
});
model.title = "A new title for a new world";
modelInstance.title = "A new title for a new world";
model.resetDiff();
definedModel.model(modelInstance).resetDiff();
const result = model.serializeDiff();
const result = definedModel.model(modelInstance).serializeDiff();
// if `id` is defined as the model identifier:
// result = { id: 5 }
// if `id` is not defined as the model identifier:
@ -233,7 +354,7 @@ Get difference between original values and current ones, then reset it.
Similar to call `serializeDiff()` then `resetDiff()`.
```typescript
const model = (new TestModel()).deserialize({
const modelInstance = definedModel.parse({
id: 5,
title: "Hello World!",
users: [
@ -244,9 +365,9 @@ const model = (new TestModel()).deserialize({
],
});
model.title = "A new title for a new world";
modelInstance.title = "A new title for a new world";
const result = model.patch();
const result = definedModel.model(modelInstance).patch();
// if `id` is defined as the model identifier:
// result = { id: 5, title: "A new title for a new world" }
// if `id` is not defined as the model identifier: