@t4h.framework/orm 0.11.3 → 0.11.5

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.
Files changed (63) hide show
  1. package/.ai/skills/framework-orm/SKILL.md +87 -316
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +21 -266
  4. package/dist/activities/ORMFindOneActivity.d.ts +2 -0
  5. package/dist/activities/ORMFindOneActivity.d.ts.map +1 -1
  6. package/dist/activities/ORMLoadActivity.d.ts.map +1 -1
  7. package/dist/activities/ORMLoadActivity.js +4 -3
  8. package/dist/activities/ORMLoadActivity.js.map +1 -1
  9. package/dist/claims/ORMClaim.js.map +1 -1
  10. package/dist/constraints/assert-constraints.d.ts +1 -1
  11. package/dist/constraints/assert-constraints.d.ts.map +1 -1
  12. package/dist/constraints/assert-constraints.js +1 -1
  13. package/dist/constraints/assert-constraints.js.map +1 -1
  14. package/dist/constraints/assert-property-type-constraint.js.map +1 -1
  15. package/dist/decorators/Entity.d.ts.map +1 -1
  16. package/dist/decorators/Entity.js +1 -1
  17. package/dist/decorators/Entity.js.map +1 -1
  18. package/dist/decorators/Index.d.ts.map +1 -1
  19. package/dist/decorators/Index.js.map +1 -1
  20. package/dist/decorators/ManyToOne.js.map +1 -1
  21. package/dist/decorators/OneToMany.d.ts +1 -1
  22. package/dist/decorators/OneToMany.d.ts.map +1 -1
  23. package/dist/decorators/OneToMany.js.map +1 -1
  24. package/dist/decorators/Property.d.ts.map +1 -1
  25. package/dist/decorators/Property.js.map +1 -1
  26. package/dist/decorators/metadata.d.ts +11 -9
  27. package/dist/decorators/metadata.d.ts.map +1 -1
  28. package/dist/decorators/metadata.js.map +1 -1
  29. package/dist/helpers/Insert.d.ts +4 -3
  30. package/dist/helpers/Insert.d.ts.map +1 -1
  31. package/dist/helpers/Insert.js +22 -15
  32. package/dist/helpers/Insert.js.map +1 -1
  33. package/dist/helpers/Populate.d.ts.map +1 -1
  34. package/dist/helpers/Populate.js +10 -5
  35. package/dist/helpers/Populate.js.map +1 -1
  36. package/dist/helpers/Serialize.d.ts +4 -3
  37. package/dist/helpers/Serialize.d.ts.map +1 -1
  38. package/dist/helpers/Serialize.js +20 -18
  39. package/dist/helpers/Serialize.js.map +1 -1
  40. package/dist/helpers/isPlainObject.js.map +1 -1
  41. package/dist/helpers/refs.d.ts +1 -1
  42. package/dist/helpers/refs.d.ts.map +1 -1
  43. package/dist/helpers/refs.js.map +1 -1
  44. package/dist/models/MapCollection.d.ts +1 -0
  45. package/dist/models/MapCollection.d.ts.map +1 -1
  46. package/dist/models/MapCollection.js +12 -5
  47. package/dist/models/MapCollection.js.map +1 -1
  48. package/dist/models/ORM.d.ts.map +1 -1
  49. package/dist/models/ORM.js +32 -16
  50. package/dist/models/ORM.js.map +1 -1
  51. package/dist/models/Ref.d.ts.map +1 -1
  52. package/dist/models/Ref.js +3 -2
  53. package/dist/models/Ref.js.map +1 -1
  54. package/dist/models/RefCollection.d.ts.map +1 -1
  55. package/dist/models/RefCollection.js +6 -4
  56. package/dist/models/RefCollection.js.map +1 -1
  57. package/dist/typings/FindWhere.d.ts +1 -1
  58. package/dist/typings/FindWhere.d.ts.map +1 -1
  59. package/dist/typings/ORM.d.ts +5 -5
  60. package/dist/typings/ORM.d.ts.map +1 -1
  61. package/dist/typings/Relations.d.ts +2 -2
  62. package/dist/typings/Relations.d.ts.map +1 -1
  63. package/package.json +4 -3
@@ -1,362 +1,133 @@
1
1
  ---
2
2
  name: framework-orm
3
3
  description: >-
4
- Guides correct use of @t4h.framework/orm in T4H Framework workflows: entity
5
- decorators (@Entity, @Property, @Index), the ORM model (create, upsert, find, findOne,
6
- exists, delete), Ref and RefCollection, query operators, constraint validation, ORM
7
- activities, and ORMClaim wiring. Use when defining database entities, persisting
8
- or querying rows in workflows, implementing ORMClaim providers, testing ORM
9
- workflows with TestWorkflowEnvironment, or working in packages/orm.
4
+ Guides the use of @t4h.framework/orm in T4H Framework workflows: entity
5
+ decorators (@Entity, @Property, @Index, @ManyToOne, @OneToMany), the ORM
6
+ class (create, upsert, find, findOne, exists, delete), Ref, RefCollection and
7
+ MapCollection, getSnapshot with populate, where operators ($in, $like, $or,
8
+ ...), ConstraintError, and ORMClaim. Use when you define database entities,
9
+ read or write rows in a workflow, model relations between tables, give or
10
+ mock ORMClaim in a test with TestWorkflowEnvironment, or write an ORMClaim
11
+ implementation.
10
12
  ---
11
13
 
12
14
  # @t4h.framework/orm
13
15
 
14
- ORM activities, claim, and models for database operations within workflows. Package path: `framework/packages/orm`. Peer dependency: `@t4h.framework/core`.
16
+ Each `ORM`, `Ref` and `RefCollection` call is an activity recorded in the
17
+ workflow history. The runtime gives the database through `ORMClaim`.
15
18
 
16
- Import from `src/orm.ts` only — do not reference unexported modules:
19
+ ## Define an entity
17
20
 
18
- ```typescript
19
- import {
20
- Entity, Property, Index, ORM, Ref, RefCollection,
21
- ORMClaim, ConstraintError,
22
- ORMCreateActivity, ORMFindActivity, ORMFindOneActivity,
23
- ORMGetSnapshotActivity, ORMUpdateActivity, ORMRemoveActivity,
24
- ORMDeleteActivity,
25
- getEntityMetadata, getEntityTableName, getEntityProperties,
26
- type EntityInsertData, type EntityPartialData, type FindWhere,
27
- type SerializableCreateInput, type SerializableFindInput,
28
- type SerializableGetSnapshotInput, type SerializableUpdateInput,
29
- type SerializableRemoveInput, type SerializableDeleteInput,
30
- } from '@t4h.framework/orm'
31
- ```
32
-
33
- Node `>=22`. See [README.md](../../../README.md).
34
-
35
- ---
36
-
37
- ## Architecture
38
-
39
- `Workflow` → `ORM` / `Ref` methods (`History.bind` on ORM activities) → `ORMClaim` (runtime database adapter).
40
-
41
- | Layer | Role |
42
- |-------|------|
43
- | **ORM** | High-level API; applies defaults + constraints before activities |
44
- | **Ref** / **RefCollection** | Row handles returned by create/find |
45
- | **Activities** | Shipped, replay-safe activities the `ORM`/`Ref` APIs drive; delegate to `ORMClaim` |
46
- | **ORMClaim** | Abstract claim; runtime provides concrete DB implementation |
47
-
48
- ---
49
-
50
- ## Defining entities
51
-
52
- Enable decorator metadata in the project `tsconfig.json`:
53
-
54
- ```json
55
- {
56
- "compilerOptions": {
57
- "experimentalDecorators": true,
58
- "emitDecoratorMetadata": true
59
- }
60
- }
61
- ```
62
-
63
- Import `reflect-metadata` where decorators are evaluated (Property decorator imports it).
64
-
65
- ### @Entity
21
+ 1. Enable `experimentalDecorators` and `emitDecoratorMetadata` in
22
+ `tsconfig.json`.
23
+ 2. Decorate the class with `@Entity({ tableName })` (required option) and each
24
+ column with `@Property`.
25
+ 3. Declare `id` and each property with a `default` as optional. Also set
26
+ `nullable: true` on `id`: the store sets it.
66
27
 
67
28
  ```typescript
68
29
  @Entity({ tableName: 'users' })
69
30
  class User {
70
- @Property({ type: 'string' })
71
- public name!: string
31
+ @Property({ type: 'uuid', nullable: true })
32
+ public id?: string
72
33
 
73
- @Property({ type: 'string' })
34
+ @Property({ type: 'string', index: true })
74
35
  public email!: string
75
36
 
76
37
  @Property({ type: 'boolean', default: true })
77
- public active!: boolean
38
+ public active?: boolean
78
39
  }
79
40
  ```
80
41
 
81
- - `tableName` is optional; defaults to the class name (`User` → `"User"`).
82
- - Use `getEntityTableName(User)` or rely on ORM to resolve the table name.
83
-
84
- ### @Property
85
-
86
- Options: `type?`, `nullable?` (default required), `default?`, `index?` (adds index metadata).
42
+ ## Create the ORM once
87
43
 
88
- Omit `type` to infer from design metadata (`String`→`string`, `Number`→`number`, etc.). Declare domain types (`uuid`) explicitly. Validated types: `string`, `uuid`, `number`, `boolean`, `array`, `date`, `object`. No `type` → skip type validation.
89
-
90
- ### @Index
91
-
92
- Class-level composite indexes:
93
-
94
- ```typescript
95
- @Index({ properties: ['tenantId', 'email'], name: 'accounts_tenant_email_idx' })
96
- class Account { /* ... */ }
97
- ```
98
-
99
- `properties` must be non-empty or the decorator throws. Per-property `@Property({ index: true })` also registers `{ properties: [propertyKey] }`.
100
-
101
- ---
102
-
103
- ## ORM configuration
104
-
105
- `ORM` extends `SerializableClass` — instances can be serialized for workflow persistence. `connectionString` is the `Secret` an `EnvDesign` entry declared `type: Secret` hands the workflow (a string is a `tsc` error); the host reads the value where each query runs.
44
+ Declare `DATABASE_URL: { type: Secret }` with `EnvDesign` in
45
+ `src/constants/environments.ts`, and create the `ORM` at module level, not in
46
+ the workflow function:
106
47
 
107
48
  ```typescript
108
49
  import { environments } from '../constants/environments.js'
109
50
 
110
- const orm = new ORM({
111
- connectionString: environments.DATABASE_URL, // Secret
112
- entities: [User, Product],
113
- })
114
- ```
115
-
116
- - `entities`: array of entity **constructors** (decorated classes).
117
- - `config.entities` stores resolved `EntityMetadata` (table name, properties map, indexes).
118
-
119
- Restore from serialized state:
120
-
121
- ```typescript
122
- const orm = await ORM.fromSerializable(serialized, v => v)
123
- ```
124
-
125
- ---
126
-
127
- ## CRUD in workflows
128
-
129
- ORM methods bind activities via `History.bind` — calls inside a workflow are **reconciler-driven** (replay-safe).
130
-
131
- ### Create
132
-
133
- ```typescript
134
- // Single row → Ref<T>
135
- const userRef = await orm.create(User, {
136
- name: 'John',
137
- email: 'john@example.com',
138
- active: true,
139
- })
140
-
141
- // Batch → RefCollection<T>
142
- const usersRef = await orm.create(User, [
143
- { name: 'Alice', email: 'alice@example.com', active: true },
144
- { name: 'Bob', email: 'bob@example.com', active: true },
145
- ])
146
- ```
147
-
148
- Defaults are applied before persistence (`applyDefaultProperties`). Omitted fields with `@Property({ default })` are filled in.
149
-
150
- ### Upsert
151
-
152
- ```typescript
153
- // Single row → Ref<T>; array → RefCollection<T>
154
- const userRef = await orm.upsert(
155
- User,
156
- { email: 'john@example.com', name: 'John', active: true },
157
- { conflictOn: ['email'] },
158
- )
159
- ```
160
-
161
- Rows are validated like `create`. If a row matching `conflictOn` (equality on every listed property) exists, only the passed fields are written; defaults apply on insert only. Every row must have a value for each `conflictOn` property (`ConstraintError` otherwise), and `conflictOn` cannot be empty. The call is a single `ORMUpsertActivity` backed by `ORMClaim.upsert`, which has a default (`find`, then `create` or `update`) — mock `upsert` in tests, or `find`/`create`/`update` if the mock extends `ORMClaim`. A retry after a crash updates instead of duplicating, but it is not atomic across workflows: concurrent writes of the same key may both insert.
162
-
163
- ### Find
164
-
165
- ```typescript
166
- const userRef = await orm.findOne(User, {
167
- where: { email: 'john@example.com' },
168
- orderBy: { name: 'asc' },
169
- }) // Ref<User> | null
170
-
171
- const usersRef = await orm.find(User, {
172
- where: { active: true },
173
- orderBy: { name: 'asc' },
174
- limit: 10,
175
- offset: 0,
176
- }) // RefCollection<User>
177
-
178
- const exists = await orm.exists(User, {
179
- where: { email: 'john@example.com' },
180
- }) // boolean (delegates to findOne)
181
-
182
- const deleted = await orm.delete(User, {
183
- where: { active: false },
184
- }) // number — deletes every match; `{}` empties the table
185
- ```
186
-
187
- `findOne` activity internally calls `claim.find` with `limit: 1`.
188
-
189
- ### Ref operations
190
-
191
- ```typescript
192
- const snapshot = await userRef.getSnapshot() // T | null — entity instance
193
- await userRef.update({ name: 'Jane' }) // partial patch only
194
- await userRef.remove()
195
- ```
196
-
197
- `getSnapshot` returns `null` when the row no longer exists. `update` validates only keys present in the patch.
198
-
199
- **RefCollection:** `toArray()`, `length`, `at(i)`, async `forEach`/`map`/`filter`, `Symbol.iterator`.
200
-
201
- ---
202
-
203
- ## Query operators
204
-
205
- `where` uses `FindWhere<T>` — implicit equality or operator objects:
206
-
207
- ```typescript
208
- await orm.find(User, {
209
- where: {
210
- name: 'John', // $eq (implicit)
211
- active: { $ne: false },
212
- email: { $like: '%@example%' },
213
- age: { $gte: 18, $lt: 65 },
214
- status: { $in: ['active', 'pending'] },
215
- $or: [{ name: 'Alice' }, { name: 'Bob' }],
216
- $and: [{ active: true }, { age: { $gte: 18 } }],
217
- },
51
+ export const orm = new ORM({
52
+ connectionString: environments.DATABASE_URL,
53
+ entities: [User, Post],
218
54
  })
219
55
  ```
220
56
 
221
- Available operators: `$eq`, `$ne`, `$in`, `$nin`, `$not`, `$gt`, `$gte`, `$lt`, `$lte`, `$like`, `$ilike`, `$and`, `$or`.
222
-
223
- ---
224
-
225
- ## Constraint validation
226
-
227
- Validation runs in the ORM library (`ORM.create`, `ORM.upsert` and `Ref.update`), **not** in `ORMClaim` implementations. Failures throw `ConstraintError`.
228
-
229
- ### Create pipeline
230
-
231
- 1. `applyDefaultProperties` — fills `default` for `undefined` fields
232
- 2. `assertPropertyTypeConstraint` — validates declared types
233
- 3. `assertNonNullablePropertyConstraint` — required fields must be present
234
-
235
- ### Update pipeline
236
-
237
- 1. Defaults only for keys present in the patch
238
- 2. Same asserts, scoped to patch keys via `{ keys: Object.keys(data) }`
239
-
240
- ### Handling errors
57
+ ## Read and write
241
58
 
242
59
  ```typescript
243
- import { ConstraintError, ORM } from '@t4h.framework/orm'
60
+ const userRef = await orm.create(User, { email: input.email }) // Ref<User>
61
+ const found = await orm.findOne(User, { where: { email: input.email } }) // Ref<User> | null
62
+ const active = await orm.find(User, { where: { active: true }, limit: 10 }) // RefCollection<User>
244
63
 
245
- try {
246
- await orm.create(User, { email: 'bad' } as EntityInsertData<User>)
247
- } catch (error) {
248
- if (error instanceof ConstraintError) {
249
- // e.g. 'Property "name" is required'
250
- // e.g. 'Property "name" must be of type string'
251
- }
252
- }
64
+ const user = await userRef.getSnapshot() // User | null
65
+ await userRef.update({ active: false }) // partial patch
66
+ const snapshots = await active.getSnapshots() // one activity for all refs
253
67
  ```
254
68
 
255
- Constraint checks run **before** activities are invoked — failed creates/updates never reach `ORMClaim`.
69
+ ## Relations
256
70
 
257
- ---
258
-
259
- ## ORMClaim (runtime dependency)
260
-
261
- Runtime must register a concrete implementation:
71
+ The `@ManyToOne` side owns the column and is a `Ref<T>`. The `@OneToMany` side
72
+ is a lazy `MapCollection<T>`. Read related rows in the same activity with
73
+ `populate`:
262
74
 
263
75
  ```typescript
264
- { provide: ORMClaim, value: new MyORMClaimImpl() }
265
- ```
266
-
267
- Contract:
268
-
269
- ```typescript
270
- abstract class ORMClaim<R extends SerializableRowRef> {
271
- abstract find(input: SerializableFindInput): R[] | Promise<R[]>
272
- abstract create(input: SerializableCreateInput): R[] | Promise<R[]>
273
- abstract getSnapshot(input: SerializableGetSnapshotInput<R>): SerializedRow | null | Promise<SerializedRow | null>
274
- abstract update(input: SerializableUpdateInput<R>): void | Promise<void>
275
- abstract remove(input: SerializableRemoveInput<R>): void | Promise<void>
276
- upsert(input: SerializableUpsertInput): Promise<R[]> // default: find, then create/update; override for a native upsert
277
- abstract delete(input: SerializableDeleteInput): number | Promise<number>
278
- }
279
- ```
280
-
281
- Activity inputs always include `orm` + `table`; create adds `data[]`, find and delete add `where` (find also takes optional `orderBy`/`limit`/`offset`), snapshot/update/remove add `ref` (opaque `Serializable` row handle). Update also includes partial `data`. Upsert adds `conflictOn` and `data: { insert, update }[]` — `insert` is the full row with defaults, `update` only the caller's fields; return refs in input order.
282
-
283
- The `ORM`/`Ref` calls run inside a workflow (the runtime, or `TestWorkflowEnvironment` in tests, provides the reconciler context).
284
-
285
- ---
286
-
287
- ## Activities
288
-
289
- 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:
290
-
291
- | Activity | Input | Output |
292
- |----------|-------|--------|
293
- | `ORMCreateActivity` | `SerializableCreateInput` | `SerializableRowRef[]` |
294
- | `ORMFindActivity` | `SerializableFindInput` | `SerializableRowRef[]` |
295
- | `ORMFindOneActivity` | `SerializableFindInput` | `SerializableRowRef \| null` |
296
- | `ORMGetSnapshotActivity` | `SerializableGetSnapshotInput` | `SerializedRow \| null` |
297
- | `ORMUpdateActivity` | `SerializableUpdateInput` | `void` |
298
- | `ORMRemoveActivity` | `SerializableRemoveInput` | `void` |
299
- | `ORMDeleteActivity` | `SerializableDeleteInput` | `number` |
300
-
301
- Prefer **`ORM` / `Ref` APIs** in workflow code — they apply constraints and return typed handles. Use activities directly only for advanced/testing scenarios.
302
-
303
- ---
304
-
305
- ## Workflow example
306
-
307
- Define entities, instantiate `ORM` with `connectionString` + `entities`, and use it inside a `Workflow`:
308
-
309
- ```typescript
310
- export const orm = new ORM({ connectionString: environments.DATABASE_URL, entities: [User] })
311
-
312
- export const ormWorkflow = new Workflow({ id: 'orm' }, async () => {
313
- await orm.create(User, { name: 'John', email: 'john@example.com', age: 20 })
314
- const user = await orm.findOne(User, { where: { email: 'john@example.com' } })
315
- await user?.update({ name: 'John Updated' })
316
- await user?.remove()
317
- return { user }
318
- })
319
- ```
320
-
321
- Ensure the runtime provides `ORMClaim`.
322
-
323
- ---
324
-
325
- ## Testing
326
-
327
- Test the **workflow** with `TestWorkflowEnvironment` from
328
- `@t4h.framework/core/testing`, mocking `ORMClaim` — the single claim the ORM
329
- activities resolve. Provide the methods your workflow triggers (`create`, `upsert`, `find`,
330
- `findOne`, `getSnapshot`, `update`, `remove`). See the **framework-workflow-testing**
331
- skill for the harness API.
76
+ @OneToMany(() => Post, post => post.author)
77
+ public posts!: MapCollection<Post>
78
+
79
+ @ManyToOne(() => User)
80
+ public author!: Ref<User>
81
+
82
+ const user = await userRef.getSnapshot({ populate: ['posts.comments'] })
83
+ const titles = await user?.posts.map(post => post.title)
84
+ ```
85
+
86
+ ## Traps
87
+
88
+ - `getSnapshot()` returns `null` when the row no longer exists. A populated
89
+ `@ManyToOne` can also be `null`, also when the relation is not nullable.
90
+ - A snapshot is a copy. Write through the `Ref` (`ref.update`), and use
91
+ `ref.load('author')` to get the `Ref` of a related row.
92
+ - `MapCollection` is not a promise. Call a method (`toArray()`, `map()`,
93
+ `count()`); do not `await user.posts`.
94
+ - `refs.map(ref => ref.getSnapshot())` runs one activity per ref. Use
95
+ `refs.getSnapshots()` or `populate`.
96
+ - A populate is the output of one activity and goes into the history. Keep it
97
+ narrow on large relations; filter with `where` and `limit`.
98
+ - `orm.delete(User, { where: {} })` deletes every row of the table.
99
+ - `upsert` needs a non-empty `conflictOn` and a value for each listed property.
100
+ It is not atomic across workflows: two workflows can both insert.
101
+ - A bad row throws `ConstraintError` before an activity runs.
102
+ - Do not construct or extend the `ORM*Activity` classes. The lint rule
103
+ `@t4h.framework/core/no-framework-activity-in-app` rejects it. Use `ORM` and
104
+ `Ref`.
105
+ - To write a relation, give a `Ref` (or the data of a new row in `create`).
106
+ You cannot write the `@OneToMany` side of an existing row.
107
+
108
+ ## Test
109
+
110
+ Give `ORMClaim` with `mockClaim` and mock only what the workflow calls.
111
+ `ORMClaim` has no `findOne` (it calls `find` with `limit: 1`), and
112
+ `getSnapshot` on a `Ref` calls `getSnapshots`:
332
113
 
333
114
  ```typescript
334
115
  import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
335
116
  import { ORMClaim } from '@t4h.framework/orm'
336
117
 
337
- const mockCreate = vi.fn().mockImplementation(async () => [{ id: 1 }])
338
- const mockFindOne = vi.fn().mockImplementation(async () => ({ id: 1, name: 'John' }))
118
+ const create = vi.fn(async () => [{ id: 1 }])
119
+ const getSnapshots = vi.fn(async () => [{ id: 1, email: 'john@example.com' }])
339
120
 
340
- const env = TestWorkflowEnvironment.create({ now: 0 })
341
-
342
- await env.execute(workflow, input, {
343
- claims: [mockClaim(ORMClaim, { create: mockCreate, findOne: mockFindOne })],
121
+ await TestWorkflowEnvironment.create().execute(onboard, input, {
122
+ claims: [mockClaim(ORMClaim, { create, getSnapshots })],
344
123
  })
345
124
  ```
346
125
 
347
- Constraint/validation errors are applied by the `ORM`/`Ref` layer before the
348
- activity runs, so you can assert an invalid input rejects without the claim being
349
- called: `expect(mockCreate).not.toHaveBeenCalled()`.
350
-
351
- ---
352
-
353
- ## Checklist for agents
354
-
355
- 1. **`@Entity` + `@Property`** with `experimentalDecorators` + `emitDecoratorMetadata`.
356
- 2. Register entities in **`ORM({ entities: [...] })`**; runtime provides **`ORMClaim`**.
357
- 3. Use **`orm` / `Ref` APIs** in workflows — not raw SQL or direct claim calls.
358
- 4. Handle **`ConstraintError`**; validation runs before activities.
359
- 5. **`Ref.update`** is partial; **`getSnapshot`** returns entity instances.
360
- 6. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim(ORMClaim, ...)`**; only use exported APIs.
126
+ ## Reference
361
127
 
362
- See also: `packages/orm/README.md`; **framework-workflow-testing** for the test harness.
128
+ - Overview, runtime and tests: https://tech4humans-brasil.github.io/framework/orm/
129
+ - Entities and validation: https://tech4humans-brasil.github.io/framework/orm/entities
130
+ - Queries, upsert, delete, operators: https://tech4humans-brasil.github.io/framework/orm/queries
131
+ - `Ref` and `RefCollection`: https://tech4humans-brasil.github.io/framework/orm/records
132
+ - Relations, populate, `MapCollection`: https://tech4humans-brasil.github.io/framework/orm/relations
133
+ - Types: `node_modules/@t4h.framework/orm/dist/orm.d.ts`
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @t4h.framework/orm
2
2
 
3
+ ## 0.11.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The orm README is short and points to the docs site. The docs and the skill now match the source: the entity examples declare `id` as optional, and a test mocks `find` and `getSnapshots` of `ORMClaim`, not `findOne`.
8
+
9
+ ## 0.11.4
10
+
11
+ ### Patch Changes
12
+
13
+ - [#241](https://github.com/tech4humans-brasil/framework/pull/241) [`f6b8454`](https://github.com/tech4humans-brasil/framework/commit/f6b845484d22e7c8e7240fe2e113f1cdea7c8ce3) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds seven rules to `@t4h.framework/core/eslint` and turns them on in `configs.recommended`:
14
+ - `no-io-in-workflow` reports `fetch` and calls to network, file, database and mail modules in a workflow and in the `constructor`, `toInput` and `toOutput` of an activity. It also reports `zlib` and `stream` calls there, because they work on the content in memory on each replay.
15
+ - `no-binary-subclass` reports a class that extends `Binary`.
16
+ - `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
17
+ - `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
18
+ - `require-workflow-id` reports a `Workflow` with no `id`.
19
+ - `no-secret-to-string` reports a `Secret` that becomes a string.
20
+ - `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
21
+
22
+ The package skills now name these rules in place of the text that the rules enforce. The docs and skills give each `Workflow` an `id`, read the HMAC secret from `EnvDesign`, and do not document a custom HTTP activity.
23
+
24
+ - [#243](https://github.com/tech4humans-brasil/framework/pull/243) [`0ffd663`](https://github.com/tech4humans-brasil/framework/commit/0ffd663ce8369edd7b54206cb3216568ad1de471) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The README examples read each value from `environments`, an `EnvDesign` in `src/constants/environments.ts`, and pass each credential as a `Secret`. They no longer use `process.env`, the raw `env` from the core or a literal in a `Secret` field. The jwks README describes the real context fields (`signature`, `rawPayload`) and says that a token that `jwt.verify` rejects throws.
25
+
26
+ - [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
27
+
28
+ - [#226](https://github.com/tech4humans-brasil/framework/pull/226) [`e11c2ee`](https://github.com/tech4humans-brasil/framework/commit/e11c2ee0d959dff4ea1c8861ec036f9f299b743c) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - `RefCollection.filter` waits for an async predicate. Before, it kept every ref, because a pending promise is truthy. A missing relation, plan row or loaded row now throws a clear error. The `mappedBy` callback of `OneToMany` returns `Ref<unknown>` in place of `Ref<any>`.
29
+
3
30
  ## 0.11.3
4
31
 
5
32
  ### Patch Changes