@antelopejs/interface-database-decorators 0.1.12 → 0.2.1
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 +11 -9
- package/dist/antelope.test.js +1 -1
- package/dist/common.d.ts +48 -2
- package/dist/common.js +51 -5
- package/dist/common.js.map +1 -1
- package/dist/database.js +1 -2
- package/dist/database.js.map +1 -1
- package/dist/internal/bound-schema.d.ts +8 -0
- package/dist/internal/bound-schema.js +14 -0
- package/dist/internal/bound-schema.js.map +1 -0
- package/dist/model.d.ts +21 -8
- package/dist/model.js +10 -7
- package/dist/model.js.map +1 -1
- package/dist/modifiers/autodate.d.ts +2 -2
- package/dist/modifiers/autodate.js.map +1 -1
- package/dist/modifiers/common.d.ts +37 -20
- package/dist/modifiers/common.js +77 -60
- package/dist/modifiers/common.js.map +1 -1
- package/dist/modifiers/encryption.js +26 -14
- package/dist/modifiers/encryption.js.map +1 -1
- package/dist/modifiers/hash.js +4 -1
- package/dist/modifiers/hash.js.map +1 -1
- package/dist/modifiers/internal/kinds.d.ts +14 -1
- package/dist/modifiers/internal/kinds.js +31 -0
- package/dist/modifiers/internal/kinds.js.map +1 -1
- package/dist/modifiers/internal/records.d.ts +40 -7
- package/dist/modifiers/internal/records.js +69 -14
- package/dist/modifiers/internal/records.js.map +1 -1
- package/dist/modifiers/localization.js +9 -2
- package/dist/modifiers/localization.js.map +1 -1
- package/dist/relation.d.ts +5 -0
- package/dist/relation.js +11 -0
- package/dist/relation.js.map +1 -1
- package/dist/schema.js +5 -6
- package/dist/schema.js.map +1 -1
- package/dist/table.d.ts +3 -2
- package/dist/table.js +27 -12
- package/dist/table.js.map +1 -1
- package/dist/tests/cross_instance.test.js +5 -2
- package/dist/tests/cross_instance.test.js.map +1 -1
- package/dist/tests/database.test.js +42 -34
- package/dist/tests/database.test.js.map +1 -1
- package/dist/tests/helpers/other-instance.js.map +1 -1
- package/dist/tests/integration.test.js +15 -4
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/metadata_inheritance.test.d.ts +1 -0
- package/dist/tests/metadata_inheritance.test.js +212 -0
- package/dist/tests/metadata_inheritance.test.js.map +1 -0
- package/dist/tests/modifiers/encryption_date_marker.test.d.ts +1 -0
- package/dist/tests/modifiers/encryption_date_marker.test.js +78 -0
- package/dist/tests/modifiers/encryption_date_marker.test.js.map +1 -0
- package/dist/tests/modifiers/inherited_names.test.d.ts +1 -0
- package/dist/tests/modifiers/inherited_names.test.js +96 -0
- package/dist/tests/modifiers/inherited_names.test.js.map +1 -0
- package/dist/tests/own_key_lookups.test.d.ts +1 -0
- package/dist/tests/own_key_lookups.test.js +49 -0
- package/dist/tests/own_key_lookups.test.js.map +1 -0
- package/dist/tests/row_forms.test.d.ts +1 -0
- package/dist/tests/row_forms.test.js +59 -0
- package/dist/tests/row_forms.test.js.map +1 -0
- package/dist/tests/stored_metadata.test.d.ts +1 -0
- package/dist/tests/stored_metadata.test.js +91 -0
- package/dist/tests/stored_metadata.test.js.map +1 -0
- package/dist/tests/subclass_schema_binding.test.d.ts +1 -0
- package/dist/tests/subclass_schema_binding.test.js +115 -0
- package/dist/tests/subclass_schema_binding.test.js.map +1 -0
- package/dist/tests/table.test.js +16 -0
- package/dist/tests/table.test.js.map +1 -1
- package/docs/1.introduction.md +24 -20
- package/docs/2.table-definitions.md +63 -41
- package/docs/3.table-modifiers.md +113 -104
- package/docs/4.data-models.md +63 -52
- package/docs/5.parameter-decoration.md +28 -24
- package/docs/tsconfig.json +3 -0
- package/package.json +10 -9
- package/skills/database-decorators-interface/SKILL.md +3 -2
package/docs/4.data-models.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Data models
|
|
3
|
+
description: Reference for BasicDataModel, its CRUD methods and return values, the data conversions, validation, and GetModel.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
A data model reads and writes one table. It converts between stored records and instances of the table class, and has methods for the common CRUD operations.
|
|
4
8
|
|
|
5
|
-
## BasicDataModel
|
|
9
|
+
## `BasicDataModel`
|
|
6
10
|
|
|
7
|
-
The `BasicDataModel` function creates a model class for a
|
|
11
|
+
The `BasicDataModel` function creates a model class for a table. The class wraps the AQL table with methods that apply the modifier transformations.
|
|
8
12
|
|
|
9
|
-
```typescript
|
|
13
|
+
```typescript fragment
|
|
10
14
|
import { BasicDataModel } from "@antelopejs/interface-database-decorators";
|
|
11
15
|
import { User } from "./user.table";
|
|
12
16
|
|
|
@@ -15,27 +19,28 @@ const UserModel = BasicDataModel(User, "users");
|
|
|
15
19
|
|
|
16
20
|
### Parameters
|
|
17
21
|
|
|
18
|
-
| Parameter | Type | Description
|
|
19
|
-
| ----------- | ------------------ |
|
|
20
|
-
| `dataType` | `Constructible<T>` | The
|
|
21
|
-
| `tableName` | `string` | The table name in the database
|
|
22
|
+
| Parameter | Type | Description |
|
|
23
|
+
| ----------- | ------------------ | ----------------------------------------------------------------------- |
|
|
24
|
+
| `dataType` | `Constructible<T>` | The table class. |
|
|
25
|
+
| `tableName` | `string` | The table name in the database. Optional when `@RegisterTable` sets it. |
|
|
22
26
|
|
|
23
|
-
When the table class has
|
|
27
|
+
When the table class has `@RegisterTable`, you can omit `tableName`. Without either, `BasicDataModel` throws `tableName must be provided or set via @RegisterTable`.
|
|
24
28
|
|
|
25
|
-
## Instantiate a
|
|
29
|
+
## Instantiate a model
|
|
26
30
|
|
|
27
|
-
A model instance
|
|
31
|
+
A model instance needs a `SchemaInstance` from the database interface:
|
|
28
32
|
|
|
29
|
-
```typescript
|
|
33
|
+
```typescript fragment
|
|
30
34
|
import { Schema } from "@antelopejs/interface-database";
|
|
31
35
|
|
|
32
|
-
const schema = Schema.get("myapp")
|
|
36
|
+
const schema = Schema.get("myapp");
|
|
37
|
+
if (!schema) throw new Error("Schema myapp is not registered");
|
|
33
38
|
const db = schema.instance();
|
|
34
39
|
|
|
35
40
|
const userModel = new UserModel(db);
|
|
36
41
|
```
|
|
37
42
|
|
|
38
|
-
##
|
|
43
|
+
## Read and write rows
|
|
39
44
|
|
|
40
45
|
For a model created with `BasicDataModel(User)`, the return values are:
|
|
41
46
|
|
|
@@ -47,11 +52,11 @@ For a model created with `BasicDataModel(User)`, the return values are:
|
|
|
47
52
|
| `update(...)` | `Promise<number>` | Number of modified documents. |
|
|
48
53
|
| `delete(id)` | `Promise<number>` | Number of deleted documents. |
|
|
49
54
|
|
|
50
|
-
Use `model.table` to compose AQL
|
|
55
|
+
Use `model.table` to compose an AQL query before you run it. The static `fromDatabase()` conversion returns `undefined` when the record is `null` or `undefined`, as `table.get` gives for a missing ID. The static `toDatabase()` returns a `StoredRow<T>`, the record to store, not a saved record or a write result.
|
|
51
56
|
|
|
52
57
|
### Read
|
|
53
58
|
|
|
54
|
-
```typescript
|
|
59
|
+
```typescript fragment
|
|
55
60
|
// Get by primary key
|
|
56
61
|
const user = await userModel.get("user-123");
|
|
57
62
|
|
|
@@ -64,7 +69,7 @@ const allUsers = await userModel.getAll();
|
|
|
64
69
|
|
|
65
70
|
### Create
|
|
66
71
|
|
|
67
|
-
```typescript
|
|
72
|
+
```typescript fragment
|
|
68
73
|
// Insert a single document
|
|
69
74
|
const ids = await userModel.insert({
|
|
70
75
|
email: "alice@example.com",
|
|
@@ -80,7 +85,7 @@ const batchIds = await userModel.insert([
|
|
|
80
85
|
|
|
81
86
|
### Update
|
|
82
87
|
|
|
83
|
-
```typescript
|
|
88
|
+
```typescript fragment
|
|
84
89
|
// Update by ID
|
|
85
90
|
await userModel.update("user-123", { name: "Updated Name" });
|
|
86
91
|
|
|
@@ -88,17 +93,17 @@ await userModel.update("user-123", { name: "Updated Name" });
|
|
|
88
93
|
await userModel.update({ _id: "user-123", name: "Updated Name" });
|
|
89
94
|
```
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
When you call `update` without a separate ID argument, the object must include the primary key. Its type requires `_id`, and a call without it throws `Missing primary key in object update.`
|
|
92
97
|
|
|
93
98
|
### Delete
|
|
94
99
|
|
|
95
|
-
```typescript
|
|
100
|
+
```typescript fragment
|
|
96
101
|
await userModel.delete("user-123");
|
|
97
102
|
```
|
|
98
103
|
|
|
99
|
-
## Data
|
|
104
|
+
## Data conversion
|
|
100
105
|
|
|
101
|
-
|
|
106
|
+
A model class has static methods that convert between the forms of a row:
|
|
102
107
|
|
|
103
108
|
| Starting value | Conversion | Result | Persists data? |
|
|
104
109
|
| ----------------------- | --------------- | ------------------------------------------------- | -------------- |
|
|
@@ -106,15 +111,15 @@ Data models include static methods for converting between different data represe
|
|
|
106
111
|
| Raw database record | `fromDatabase` | Table instance with read modifier events applied | No |
|
|
107
112
|
| Table instance | `toDatabase` | Plain storage representation | No |
|
|
108
113
|
|
|
109
|
-
Use `insert` or `update` to
|
|
114
|
+
Use `insert` or `update` to write. A conversion alone saves nothing. Reads through `get`, `getBy`, and `getAll` apply `fromDatabase` for you. A custom query through `model.table` returns AQL results, so apply conversion explicitly when you need model instances.
|
|
110
115
|
|
|
111
|
-
{width="480"}
|
|
112
117
|
|
|
113
118
|
### `fromPlainData(obj)`
|
|
114
119
|
|
|
115
|
-
|
|
120
|
+
Converts a plain JavaScript object into an instance of the table class. Each key is assigned, so modifiers such as encryption and hashing transform the values during this conversion. An instance of the table class is returned as it is.
|
|
116
121
|
|
|
117
|
-
```typescript
|
|
122
|
+
```typescript fragment
|
|
118
123
|
const user = UserModel.fromPlainData({
|
|
119
124
|
_id: "user-123",
|
|
120
125
|
email: "user@example.com",
|
|
@@ -122,56 +127,62 @@ const user = UserModel.fromPlainData({
|
|
|
122
127
|
});
|
|
123
128
|
```
|
|
124
129
|
|
|
130
|
+
The object is a `PlainData<T>`. Every field is optional and may be partial, and a field under a container modifier may hold its value per key, as a localized field holds one text per locale. `toPlainData` gives the reverse form, a `PlainRow<T>`.
|
|
131
|
+
|
|
125
132
|
### `fromDatabase(obj)`
|
|
126
133
|
|
|
127
|
-
|
|
134
|
+
Converts a stored record into an instance of the table class. It copies the record, sets the class prototype on the copy, and triggers the `fromDatabase` modifier event. It returns `undefined` when the record is `null` or `undefined`, as `table.get` gives for a missing ID. An empty object still gives an instance.
|
|
128
135
|
|
|
129
|
-
```typescript
|
|
136
|
+
```typescript fragment
|
|
130
137
|
const user = UserModel.fromDatabase(rawDbRecord);
|
|
131
138
|
```
|
|
132
139
|
|
|
133
140
|
### `toDatabase(obj)`
|
|
134
141
|
|
|
135
|
-
|
|
142
|
+
Converts an instance of the table class, or plain data, into the record to store, and triggers the `toDatabase` modifier event. The result is a `StoredRow<T>`. It holds the fields that are set, has no prototype, and keeps its modified fields locked in `_internal` instead of at the top level.
|
|
136
143
|
|
|
137
|
-
```typescript
|
|
144
|
+
```typescript fragment
|
|
138
145
|
const dbReady = UserModel.toDatabase(userInstance);
|
|
139
146
|
```
|
|
140
147
|
|
|
141
148
|
## Validation
|
|
142
149
|
|
|
143
|
-
`Model.validate(obj)` runs every io-ts codec
|
|
150
|
+
`Model.validate(obj)` runs every io-ts codec that `@Field` attached against the matching property of `obj`. It skips string-token fields and properties without `@Field`. A failure's `message` is the first message the codec reports, or `invalid <field>` when the codec reports none, as io-ts's built-in codecs do.
|
|
144
151
|
|
|
145
|
-
```typescript
|
|
152
|
+
```typescript fragment
|
|
146
153
|
const result = UserModel.validate({ name: "Alice", age: 30 });
|
|
147
154
|
if (!result.ok) {
|
|
148
|
-
console.error(result.errors); // [{ field: "age", message: "
|
|
155
|
+
console.error(result.errors); // [{ field: "age", message: "invalid age" }]
|
|
149
156
|
}
|
|
150
157
|
```
|
|
151
158
|
|
|
152
|
-
Pass `{ validate: true }` to `insert` or `update` to
|
|
159
|
+
Pass `{ validate: true }` to `insert` or `update` to validate before the write. On failure the call throws an `Error` with the message `validation failed: <field> (<message>), ...` and an `errors` property that holds the details per field. On success the write goes on with the object unchanged.
|
|
153
160
|
|
|
154
|
-
```typescript
|
|
161
|
+
```typescript fragment
|
|
155
162
|
await userModel.insert({ name: "Alice", age: 30 }, { validate: true });
|
|
156
163
|
await userModel.update("user-123", { name: "Bob" }, { validate: true });
|
|
157
164
|
```
|
|
158
165
|
|
|
159
|
-
`insert` validates every
|
|
166
|
+
`insert` validates every codec field of each row. `update` validates only the fields present on the patch object, so you can leave out the fields you do not change. For the same patch check outside `update`, use `Model.validate(obj, { partial: true })`.
|
|
160
167
|
|
|
161
|
-
## Extend BasicDataModel
|
|
168
|
+
## Extend `BasicDataModel`
|
|
162
169
|
|
|
163
|
-
Extend the generated model class to add custom query methods
|
|
170
|
+
Extend the generated model class to add custom query methods.
|
|
164
171
|
|
|
165
|
-
The `register` method below checks for an existing email to
|
|
172
|
+
The `register` method below checks for an existing email to report a duplicate early. It does not guarantee uniqueness under concurrent writes. Enforce that constraint in the database with a unique index where the database module supports it, and handle the conflict. `findActive` converts each row with `fromDatabase`, which returns `undefined` only when the record is `null` or `undefined`, and the filter drops a missing row.
|
|
166
173
|
|
|
167
|
-
```typescript
|
|
174
|
+
```typescript fragment
|
|
168
175
|
const UserModelBase = BasicDataModel(User, "users");
|
|
169
176
|
|
|
170
177
|
class CustomUserModel extends UserModelBase {
|
|
171
178
|
async findActive() {
|
|
172
179
|
return this.table
|
|
173
180
|
.filter((u) => u.key("status").eq("active"))
|
|
174
|
-
.then((rows) =>
|
|
181
|
+
.then((rows) =>
|
|
182
|
+
rows
|
|
183
|
+
.map((row) => CustomUserModel.fromDatabase(row))
|
|
184
|
+
.filter((user) => user !== undefined),
|
|
185
|
+
);
|
|
175
186
|
}
|
|
176
187
|
|
|
177
188
|
async register(email: string, name: string) {
|
|
@@ -184,24 +195,24 @@ class CustomUserModel extends UserModelBase {
|
|
|
184
195
|
}
|
|
185
196
|
```
|
|
186
197
|
|
|
187
|
-
## GetModel
|
|
198
|
+
## `GetModel`
|
|
188
199
|
|
|
189
|
-
The `GetModel` function
|
|
200
|
+
The `GetModel` function returns the cached model instance for a model class and an instance ID, and creates it on the first call:
|
|
190
201
|
|
|
191
|
-
```typescript
|
|
202
|
+
```typescript fragment
|
|
192
203
|
import { GetModel } from "@antelopejs/interface-database-decorators";
|
|
193
204
|
|
|
194
205
|
const userModel = GetModel(UserModel, "instance-id");
|
|
195
206
|
```
|
|
196
207
|
|
|
197
|
-
|
|
208
|
+
The cache key is the class and the instance ID, so repeated calls with the same arguments return the same instance. Without an instance ID, the model uses the default instance. A model built by `BasicDataModel` uses the schema that `RegisterSchema` bound its table class to, so it stays on the declaring module's database even when another module calls `GetModel`. An unbound table class whose schema name has no schema throws `<Class> is not bound to a schema`. A custom model class without a `tableClass` gets its schema from its `schemaName` through `Schema.get`.
|
|
198
209
|
|
|
199
|
-
## Modifier
|
|
210
|
+
## Modifier integration
|
|
200
211
|
|
|
201
|
-
Data models
|
|
212
|
+
Data models apply every modifier transformation:
|
|
202
213
|
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
214
|
+
- On insert, the model converts the data with `fromPlainData`, fires the `insert` event, and converts the result with `toDatabase` before the write.
|
|
215
|
+
- On update, the model runs the same conversions with the `update` event. It leaves out an empty `_internal`, so a patch keeps what the row stores there.
|
|
216
|
+
- On read, the model converts the results of `get`, `getBy`, and `getAll` with `fromDatabase`.
|
|
206
217
|
|
|
207
|
-
|
|
218
|
+
As a result, encrypted fields are encrypted before storage and decrypted on read, hashed fields are hashed on write, and localized fields follow their locale settings.
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Parameter decoration
|
|
3
|
+
description: Inject a data model into an API controller method or property with the Model decorator, with a fixed or a per-request instance ID.
|
|
4
|
+
mode: how-to
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
The
|
|
7
|
+
The `Model` decorator injects a data model instance into an API controller method. It registers a parameter provider of `@antelopejs/interface-api` through `SetParameterProvider`.
|
|
4
8
|
|
|
5
|
-
## The Model
|
|
9
|
+
## The `Model` decorator
|
|
6
10
|
|
|
7
|
-
The `Model` decorator injects
|
|
11
|
+
The `Model` decorator injects the cached model instance that `GetModel` returns. It works as a parameter decorator and as a property decorator.
|
|
8
12
|
|
|
9
|
-
###
|
|
13
|
+
### Pass a fixed instance
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
To bind the model to one schema instance, pass a fixed instance ID:
|
|
12
16
|
|
|
13
|
-
```typescript
|
|
17
|
+
```typescript fragment
|
|
14
18
|
import { Controller, Get } from "@antelopejs/interface-api";
|
|
15
19
|
import { Model } from "@antelopejs/interface-database-decorators";
|
|
16
20
|
import { UserModel } from "./user.model";
|
|
@@ -25,13 +29,13 @@ class UsersController extends Controller("/users") {
|
|
|
25
29
|
}
|
|
26
30
|
```
|
|
27
31
|
|
|
28
|
-
###
|
|
32
|
+
### Pick the instance per request
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
To pick the instance ID for each request, pass a callback. It receives the request context and returns an instance ID, or `undefined` for the default instance.
|
|
31
35
|
|
|
32
|
-
Authorize access to the requested tenant before
|
|
36
|
+
Authorize access to the requested tenant before you read or write through the injected model. The callback only selects an instance, and a URL parameter alone does not prove that the caller belongs to that tenant.
|
|
33
37
|
|
|
34
|
-
```typescript
|
|
38
|
+
```typescript fragment
|
|
35
39
|
import { Controller, Get, Parameter } from "@antelopejs/interface-api";
|
|
36
40
|
import { Model } from "@antelopejs/interface-database-decorators";
|
|
37
41
|
import { UserModel } from "./user.model";
|
|
@@ -40,7 +44,7 @@ class MultitenantController extends Controller("/tenants") {
|
|
|
40
44
|
@Get("/:tenantId/users")
|
|
41
45
|
async getTenantUsers(
|
|
42
46
|
@Parameter("tenantId") tenantId: string,
|
|
43
|
-
@Model(UserModel, (ctx) => `tenant-${ctx.
|
|
47
|
+
@Model(UserModel, (ctx) => `tenant-${ctx.routeParameters.tenantId}`)
|
|
44
48
|
userModel: InstanceType<typeof UserModel>,
|
|
45
49
|
) {
|
|
46
50
|
return await userModel.getAll();
|
|
@@ -48,11 +52,11 @@ class MultitenantController extends Controller("/tenants") {
|
|
|
48
52
|
}
|
|
49
53
|
```
|
|
50
54
|
|
|
51
|
-
## Property
|
|
55
|
+
## Property decoration
|
|
52
56
|
|
|
53
|
-
The `Model` decorator also works as a property decorator. The
|
|
57
|
+
The `Model` decorator also works as a property decorator. The property gets its model when the controller handles a request. Outside a request handler, the property is unset, although the `!` declaration types it as always present:
|
|
54
58
|
|
|
55
|
-
```typescript
|
|
59
|
+
```typescript fragment
|
|
56
60
|
import { Controller, Get, Parameter } from "@antelopejs/interface-api";
|
|
57
61
|
import { Model } from "@antelopejs/interface-database-decorators";
|
|
58
62
|
import { UserModel } from "./models";
|
|
@@ -73,11 +77,11 @@ class UsersController extends Controller("/users") {
|
|
|
73
77
|
}
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
## Model
|
|
80
|
+
## Model caching
|
|
77
81
|
|
|
78
|
-
The `Model` decorator
|
|
82
|
+
The `Model` decorator calls `GetModel`, which caches model instances by class and instance ID. Decorators that name the same model class and instance ID share one instance:
|
|
79
83
|
|
|
80
|
-
```typescript
|
|
84
|
+
```typescript fragment
|
|
81
85
|
import { GetModel } from "@antelopejs/interface-database-decorators";
|
|
82
86
|
|
|
83
87
|
// These return the same instance
|
|
@@ -88,13 +92,13 @@ const model2 = GetModel(UserModel, "main");
|
|
|
88
92
|
const model3 = GetModel(UserModel, "other");
|
|
89
93
|
```
|
|
90
94
|
|
|
91
|
-
## Combine with API
|
|
95
|
+
## Combine with API decorators
|
|
92
96
|
|
|
93
|
-
Model
|
|
97
|
+
`@Model` combines with the other API decorators in a CRUD controller.
|
|
94
98
|
|
|
95
|
-
This example
|
|
99
|
+
This example shows injection, not a complete public API policy. Before you expose writes, reject malformed or non-object JSON, allow only permitted fields, validate their values, and authorize the operation. `JSON.parse` and `@Model` do none of these checks, and string-token `@Field` declarations do not validate input either.
|
|
96
100
|
|
|
97
|
-
```typescript
|
|
101
|
+
```typescript fragment
|
|
98
102
|
import {
|
|
99
103
|
Controller,
|
|
100
104
|
Get,
|
|
@@ -150,9 +154,9 @@ class UsersController extends Controller("/users") {
|
|
|
150
154
|
}
|
|
151
155
|
```
|
|
152
156
|
|
|
153
|
-
## Custom
|
|
157
|
+
## Custom parameter providers
|
|
154
158
|
|
|
155
|
-
For
|
|
159
|
+
For other injection logic, write your own decorator that registers a parameter provider:
|
|
156
160
|
|
|
157
161
|
```typescript
|
|
158
162
|
import { SetParameterProvider } from "@antelopejs/interface-api";
|
package/docs/tsconfig.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-database-decorators",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "AntelopeJS decorators for database tables and typed data models, with encrypted, hashed and localized fields",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -131,9 +131,7 @@
|
|
|
131
131
|
"prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
|
|
132
132
|
"checks": "antelopejs-checks",
|
|
133
133
|
"size": "antelopejs-check-size",
|
|
134
|
-
"ratchet": "antelopejs-ratchet",
|
|
135
134
|
"sync": "antelopejs-sync",
|
|
136
|
-
"ratchet:update": "antelopejs-ratchet --update",
|
|
137
135
|
"sync:check": "antelopejs-sync --check",
|
|
138
136
|
"typecheck": "tsc --noEmit",
|
|
139
137
|
"suppressions": "antelopejs-suppressions",
|
|
@@ -144,11 +142,11 @@
|
|
|
144
142
|
"reflect-metadata": "^0.2.2"
|
|
145
143
|
},
|
|
146
144
|
"devDependencies": {
|
|
147
|
-
"@antelopejs/core": "^1.
|
|
145
|
+
"@antelopejs/core": "^1.20.0",
|
|
148
146
|
"@antelopejs/interface-api": "^0.0.15",
|
|
149
|
-
"@antelopejs/interface-core": "^0.
|
|
150
|
-
"@antelopejs/interface-database": "^0.
|
|
151
|
-
"@antelopejs/tooling-configs": "^0.
|
|
147
|
+
"@antelopejs/interface-core": "^0.4.0",
|
|
148
|
+
"@antelopejs/interface-database": "^0.3.0",
|
|
149
|
+
"@antelopejs/tooling-configs": "^0.3.1",
|
|
152
150
|
"@types/chai": "^4.3.20",
|
|
153
151
|
"@types/mocha": "^10.0.10",
|
|
154
152
|
"@types/node": "^22.19.15",
|
|
@@ -172,12 +170,15 @@
|
|
|
172
170
|
"@antelopejs/interface-core": ">=0.2.4 <1.0.0",
|
|
173
171
|
"@antelopejs/interface-database": ">=0.1.11 <1.0.0"
|
|
174
172
|
},
|
|
175
|
-
"packageManager": "pnpm@10.
|
|
173
|
+
"packageManager": "pnpm@10.33.0",
|
|
176
174
|
"antelopeJs": {
|
|
177
175
|
"standalone": true,
|
|
178
176
|
"test": "src/antelope.test.ts",
|
|
179
177
|
"skills": [
|
|
180
178
|
"./skills"
|
|
181
|
-
]
|
|
179
|
+
],
|
|
180
|
+
"docs": {
|
|
181
|
+
"standard": true
|
|
182
|
+
}
|
|
182
183
|
}
|
|
183
184
|
}
|
|
@@ -134,7 +134,8 @@ class Chef extends Table.with(
|
|
|
134
134
|
- `Model.validate` (and `{ validate: true }` on insert/update) only checks fields whose `@Field` type is an
|
|
135
135
|
io-ts-style codec with `.decode`; plain string field tokens are skipped. Update validates with
|
|
136
136
|
`{ partial: true }` semantics.
|
|
137
|
-
- `update(obj)` without an explicit id
|
|
137
|
+
- `update(obj)` without an explicit id takes an object typed with the primary key `_id`, and asserts at run time that it carries it.
|
|
138
|
+
- A row has three forms. `fromPlainData` reads a `PlainData<T>` (every field optional, a localized field possibly one text per locale), `toPlainData` gives a `PlainRow<T>`, and `toDatabase` gives a `StoredRow<T>`, its modified fields locked in `_internal`. `fromDatabase` takes the row the database answered, unchecked.
|
|
138
139
|
- Indexes are scoped to the schema instance. Add `@Index({ crossInstance: true })` only for indexes also queried
|
|
139
140
|
across all instances (`CROSS_INSTANCE`); it never changes results, only speed. On a group, one member setting it
|
|
140
141
|
applies it to the whole group.
|
|
@@ -143,7 +144,7 @@ class Chef extends Table.with(
|
|
|
143
144
|
to the whole group.
|
|
144
145
|
- `@Fixture` data is only inserted by `RegisterSchema` when the table is empty (count === 0).
|
|
145
146
|
- `GetModel` caches by model class + instanceId; the same pair always returns the same instance.
|
|
146
|
-
- `RegisterSchema` binds each registered table class to the `Schema` it builds; call it in the module that declares the tables. `User.table()`, `User.table(instanceId)` and `User.table(db)` then return the class's AQL table, and `GetModel` builds `BasicDataModel` models on that same schema, even when another module (bound to another database) calls them. An unbound class
|
|
147
|
+
- `RegisterSchema` binds each registered table class to the `Schema` it builds; call it in the module that declares the tables. `User.table()`, `User.table(instanceId)` and `User.table(db)` then return the class's AQL table, and `GetModel` builds `BasicDataModel` models on that same schema, even when another module (bound to another database) calls them. An unbound class whose schema name has a schema uses that schema; one whose schema name has none throws `<Class> is not bound to a schema`.
|
|
147
148
|
- Serialization of modified instances to plain JSON goes through `toPlainData` (auto-attached as `toJSON`).
|
|
148
149
|
|
|
149
150
|
Deeper reference: this package's `docs/` chapters — Introduction, Table Definitions, Table Modifiers,
|