@t4h.framework/orm 0.4.0 → 0.6.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.
|
@@ -6,7 +6,7 @@ description: >-
|
|
|
6
6
|
exists), Ref and RefCollection, query operators, constraint validation, ORM
|
|
7
7
|
activities, and ORMClaim wiring. Use when defining database entities, persisting
|
|
8
8
|
or querying rows in workflows, implementing ORMClaim providers, testing ORM
|
|
9
|
-
|
|
9
|
+
workflows with TestWorkflowEnvironment, or working in packages/orm.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# @t4h.framework/orm
|
|
@@ -41,11 +41,9 @@ Node `>=22`. See [README.md](../../../README.md).
|
|
|
41
41
|
|-------|------|
|
|
42
42
|
| **ORM** | High-level API; applies defaults + constraints before activities |
|
|
43
43
|
| **Ref** / **RefCollection** | Row handles returned by create/find |
|
|
44
|
-
| **Activities** | `
|
|
44
|
+
| **Activities** | Shipped, replay-safe activities the `ORM`/`Ref` APIs drive; delegate to `ORMClaim` |
|
|
45
45
|
| **ORMClaim** | Abstract claim; runtime provides concrete DB implementation |
|
|
46
46
|
|
|
47
|
-
All ORM activities use `new Claim({ orm: ORMClaim })` and delegate in `run()`.
|
|
48
|
-
|
|
49
47
|
---
|
|
50
48
|
|
|
51
49
|
## Defining entities
|
|
@@ -171,7 +169,7 @@ const exists = await orm.exists(User, {
|
|
|
171
169
|
### Ref operations
|
|
172
170
|
|
|
173
171
|
```typescript
|
|
174
|
-
const snapshot = await userRef.getSnapshot() // T | null — entity instance
|
|
172
|
+
const snapshot = await userRef.getSnapshot() // T | null — entity instance
|
|
175
173
|
await userRef.update({ name: 'Jane' }) // partial patch only
|
|
176
174
|
await userRef.remove()
|
|
177
175
|
```
|
|
@@ -260,13 +258,13 @@ abstract class ORMClaim<R extends SerializableRowRef> {
|
|
|
260
258
|
|
|
261
259
|
Activity inputs always include `orm` + `table`; create adds `data[]`, find adds `where` (+ optional `orderBy`/`limit`/`offset`), snapshot/update/remove add `ref` (opaque `Serializable` row handle). Update also includes partial `data`.
|
|
262
260
|
|
|
263
|
-
|
|
261
|
+
The `ORM`/`Ref` calls run inside a workflow (the runtime, or `TestWorkflowEnvironment` in tests, provides the reconciler context).
|
|
264
262
|
|
|
265
263
|
---
|
|
266
264
|
|
|
267
265
|
## Activities
|
|
268
266
|
|
|
269
|
-
|
|
267
|
+
These are the **shipped, replay-safe activities** the `ORM`/`Ref` APIs drive for you (you can also pass them to `History.reconciler` directly). Never define your own:
|
|
270
268
|
|
|
271
269
|
| Activity | Input | Output |
|
|
272
270
|
|----------|-------|--------|
|
|
@@ -283,7 +281,7 @@ Prefer **`ORM` / `Ref` APIs** in workflow code — they apply constraints and re
|
|
|
283
281
|
|
|
284
282
|
## Workflow example
|
|
285
283
|
|
|
286
|
-
|
|
284
|
+
Define entities, instantiate `ORM` with `connectionString` + `entities`, and use it inside a `Workflow`:
|
|
287
285
|
|
|
288
286
|
```typescript
|
|
289
287
|
export const orm = new ORM({ connectionString: '...', entities: [User] })
|
|
@@ -303,13 +301,29 @@ Ensure the runtime provides `ORMClaim`.
|
|
|
303
301
|
|
|
304
302
|
## Testing
|
|
305
303
|
|
|
306
|
-
|
|
304
|
+
Test the **workflow** with `TestWorkflowEnvironment` from
|
|
305
|
+
`@t4h.framework/core/testing`, mocking `ORMClaim` — the single claim the ORM
|
|
306
|
+
activities resolve. Provide the methods your workflow triggers (`create`, `find`,
|
|
307
|
+
`findOne`, `getSnapshot`, `update`, `remove`). See the **framework-workflow-testing**
|
|
308
|
+
skill for the harness API.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
|
|
312
|
+
import { ORMClaim } from '@t4h.framework/orm'
|
|
313
|
+
|
|
314
|
+
const mockCreate = vi.fn().mockImplementation(async () => [{ id: 1 }])
|
|
315
|
+
const mockFindOne = vi.fn().mockImplementation(async () => ({ id: 1, name: 'John' }))
|
|
307
316
|
|
|
308
|
-
|
|
317
|
+
const env = TestWorkflowEnvironment.create({ now: 0 })
|
|
309
318
|
|
|
310
|
-
|
|
319
|
+
await env.execute(workflow, input, {
|
|
320
|
+
claims: [mockClaim(ORMClaim, { create: mockCreate, findOne: mockFindOne })],
|
|
321
|
+
})
|
|
322
|
+
```
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
Constraint/validation errors are applied by the `ORM`/`Ref` layer before the
|
|
325
|
+
activity runs, so you can assert an invalid input rejects without the claim being
|
|
326
|
+
called: `expect(mockCreate).not.toHaveBeenCalled()`.
|
|
313
327
|
|
|
314
328
|
---
|
|
315
329
|
|
|
@@ -320,6 +334,6 @@ Run: `yarn test` in `packages/orm`.
|
|
|
320
334
|
3. Use **`orm` / `Ref` APIs** in workflows — not raw SQL or direct claim calls.
|
|
321
335
|
4. Handle **`ConstraintError`**; validation runs before activities.
|
|
322
336
|
5. **`Ref.update`** is partial; **`getSnapshot`** returns entity instances.
|
|
323
|
-
6. Test with **`
|
|
337
|
+
6. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim(ORMClaim, ...)`**; only use exported APIs.
|
|
324
338
|
|
|
325
|
-
See also: `packages/orm/README.md
|
|
339
|
+
See also: `packages/orm/README.md`; **framework-workflow-testing** for the test harness.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGxD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAUhD,wBAAgB,sBAAsB,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CA2DxE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mockClaim } from '@t4h.framework/core/testing';
|
|
3
|
+
import { ORMClaim } from '../claims/ORMClaim.js';
|
|
4
|
+
export function createInMemoryORMClaim() {
|
|
5
|
+
const tables = new Map();
|
|
6
|
+
const table = (name) => {
|
|
7
|
+
let rows = tables.get(name);
|
|
8
|
+
if (!rows) {
|
|
9
|
+
rows = new Map();
|
|
10
|
+
tables.set(name, rows);
|
|
11
|
+
}
|
|
12
|
+
return rows;
|
|
13
|
+
};
|
|
14
|
+
const claim = {
|
|
15
|
+
find(input) {
|
|
16
|
+
const rows = [...table(input.table).values()];
|
|
17
|
+
return rows
|
|
18
|
+
.filter(row => matchesWhere(row, input.where))
|
|
19
|
+
.slice(input.offset ?? 0, input.limit ? (input.offset ?? 0) + input.limit : undefined)
|
|
20
|
+
.map(row => row.id);
|
|
21
|
+
},
|
|
22
|
+
create(input) {
|
|
23
|
+
const rows = table(input.table);
|
|
24
|
+
return input.data.map(row => {
|
|
25
|
+
const id = String(row.id ?? randomUUID());
|
|
26
|
+
const snapshot = { ...row, id };
|
|
27
|
+
rows.set(id, snapshot);
|
|
28
|
+
return id;
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
getSnapshot(input) {
|
|
32
|
+
return table(input.table).get(input.ref) ?? null;
|
|
33
|
+
},
|
|
34
|
+
update(input) {
|
|
35
|
+
const rows = table(input.table);
|
|
36
|
+
const current = rows.get(input.ref);
|
|
37
|
+
if (!current)
|
|
38
|
+
return;
|
|
39
|
+
rows.set(input.ref, { ...current, ...input.data });
|
|
40
|
+
},
|
|
41
|
+
remove(input) {
|
|
42
|
+
table(input.table).delete(input.ref);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
return mockClaim(ORMClaim, claim);
|
|
46
|
+
}
|
|
47
|
+
function matchesWhere(row, where) {
|
|
48
|
+
if (!where)
|
|
49
|
+
return true;
|
|
50
|
+
return Object.entries(where).every(([key, expected]) => row[key] === expected);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "ORM module for the T4H Framework",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/orm",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"reflect-metadata": "^0.2.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@t4h.framework/core": "^0.
|
|
38
|
+
"@t4h.framework/core": "^0.9.0",
|
|
39
39
|
"typescript": "^5.9.3",
|
|
40
40
|
"vitest": "^4.0.18"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@t4h.framework/core": "^0.
|
|
43
|
+
"@t4h.framework/core": "^0.9.0"
|
|
44
44
|
},
|
|
45
45
|
"packageManager": "yarn@4.12.0",
|
|
46
46
|
"engines": {
|