@adobe/data-ai 0.10.2 → 0.10.6

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.
@@ -195,7 +195,7 @@ export const createTodo = (
195
195
  return { entities: new Map(state.entities).set(id, { name, complete: false, order }) };
196
196
  };
197
197
 
198
- export const cases = Conformance.cases(createTodo,
198
+ export const cases = /*@__PURE__*/ Conformance.cases(createTodo,
199
199
  { name: "adds the first todo",
200
200
  before: {}, // empty delta — the default State.create() (empty entities)
201
201
  args: { name: "a", analytics: AnalyticsService.createFake() },
@@ -217,7 +217,10 @@ export const cases = Conformance.cases(createTodo,
217
217
  injected service, etc. into it, never as a third positional. A transition that takes
218
218
  **no** args omits `args` from each case entirely (the shared `Case` type makes
219
219
  `args` optional exactly then). **Guard no-ops by returning an empty patch `{}`**
220
- (or the unchanged slice), never throw.
220
+ (or the unchanged slice), never throw for an ordinary "nothing to do here"
221
+ condition. Reserve `throw` for a genuine precondition violation the case wants
222
+ to name (see "A case that expects a throw" below) — the rare exception, not the
223
+ default guard style.
221
224
  - **A composer merges sub-patches explicitly.** A transition built from smaller
222
225
  ones spreads them — `return { ...s, ...sub(s) }` — so each sub-patch's writes
223
226
  layer in; a transition that merely **delegates** to one sub-transition returns
@@ -227,6 +230,22 @@ export const cases = Conformance.cases(createTodo,
227
230
  any barrel member) in a top-level `cases` literal dead-locks the import cycle.
228
231
  Import the concrete helper directly (`import { create } from "./create.js"`) or
229
232
  inline full-`State` literals.
233
+ - **Always write the call as `/*@__PURE__*/ Conformance.cases(fn, …)`** (same for
234
+ `Conformance.derivations`) — the annotation is not optional style, it is what
235
+ makes the co-location safe. `@adobe/data-testing`'s `sideEffects: false` only
236
+ lets a bundler drop an unused *import* of the package; `Conformance.cases(fn,
237
+ …)` is still an ordinary function call happening in *this* file, and an
238
+ unannotated call is conservatively kept even when the `cases` export it
239
+ initializes is never read — because the transition function `fn` is legitimately
240
+ used elsewhere, this module is very much reachable, so the unused `cases` const
241
+ is the only thing tree-shaking has to remove, and it can't without the hint.
242
+ Verified against this repo's actual Vite/Rollup production build: a transition
243
+ whose `cases` call lacked the annotation shipped the full case-fixture data
244
+ (and the builder's inlined logic) into the bundle; adding `/*@__PURE__*/`
245
+ removed it completely while leaving the transition function itself intact. A
246
+ linter/codemod catching a bare `Conformance.cases(`/`Conformance.derivations(`
247
+ call (missing the leading comment) in a `data/state/*.ts` file would catch this
248
+ by construction — worth adding if these calls keep getting hand-authored.
230
249
  - **`Conformance.cases(fn, ...cases)`** (the builder above) derives each case's
231
250
  types from `fn`'s own signature — pass the transition function and cases can't drift
232
251
  from what it accepts. **`before` is a delta over `State.create()`** — list only
@@ -295,6 +314,32 @@ export const cases = Conformance.cases(createTodo,
295
314
  and isn't a `(state,args)=>state` transform, so `runSpec` skips it: **keep its own
296
315
  sibling `*.test.ts`** rather than deleting it and losing coverage.
297
316
 
317
+ ### A case that expects a throw
318
+
319
+ A case is normally `{ name, before, args?, after, effects? }`. When a transition
320
+ must reject its input outright — not the "guard no-op, return `{}`" case above,
321
+ but a genuine precondition violation the spec wants to name — declare `throws`
322
+ instead of `after`/`effects`:
323
+
324
+ ```ts
325
+ export const cases = Conformance.cases(withdrawFunds,
326
+ { name: "rejects a withdrawal over the balance",
327
+ before: { balance: 10 },
328
+ args: { amount: 11 },
329
+ throws: "insufficient funds" }, // substring the thrown Error.message must contain
330
+ );
331
+ ```
332
+
333
+ `throws: true` accepts any thrown error; a string narrows to one whose message
334
+ **contains** it. A case can declare `after`/`effects` **or** `throws`, never
335
+ both — the shared `Case` type makes them mutually exclusive, and there is
336
+ nothing to compare against `after` once a call never returns a patch. The same
337
+ case runs against the pure transform (`runSpec`) **and** the paired ecs
338
+ transaction/action (`runFeature`/`runTransactions`/`runActions`), so a `throws`
339
+ case proves both sides reject the same input the same way. Reach for this only
340
+ when throwing is the transition's actual contract — most invalid input should
341
+ still no-op per the rule above.
342
+
298
343
  ## Injected services and side effects
299
344
 
300
345
  A transform that needs an outside capability injects it with **`Pick<Services,
@@ -165,7 +165,11 @@ It discovers every file exporting `cases`, enforces the two-exports rule, and
165
165
  dispatches on case shape (transition → state + effects, derivation →
166
166
  `fn(input) ≡ value`), seeding each case's `before`/`input` as a delta over
167
167
  `State.create()`. On the ECS side it resolves the reference args a case's `args`
168
- schema marks; the pure side reads them plain.
168
+ schema marks; the pure side reads them plain. A transition case may declare
169
+ `throws` instead of `after`/`effects` to assert the call rejects its input —
170
+ see `../../data/state.md` ("A case that expects a throw"). `runSpec`,
171
+ `runTransactions`, and `runActions` all honor it identically, so the same case
172
+ proves the pure transform and its paired ecs op fail the same way.
169
173
 
170
174
  ## The exception — a per-surface `userId`, via the lower-level runners
171
175
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adobe-data-ai",
3
- "version": "0.10.2",
3
+ "version": "0.10.6",
4
4
  "description": "Architecture skills for @adobe/data — data-oriented modelling, archetype iteration, hot-path performance, and related conventions.",
5
5
  "author": {
6
6
  "name": "Adobe"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data-ai",
3
- "version": "0.10.2",
3
+ "version": "0.10.6",
4
4
  "description": "Cross-agent architecture skills for @adobe/data — installable as a Claude Code plugin or copied into any Agent-Skills-compatible agent (Cursor, Codex).",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,12 +29,12 @@
29
29
  "skills"
30
30
  ],
31
31
  "peerDependencies": {
32
- "@adobe/data": "0.10.2"
32
+ "@adobe/data": "0.10.6"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^5.8.3",
36
36
  "vitest": "^1.6.0",
37
- "@adobe/data": "0.10.2"
37
+ "@adobe/data": "0.10.6"
38
38
  },
39
39
  "license": "MIT",
40
40
  "scripts": {