@opetope/react 0.11.0 → 0.12.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,256 @@
1
1
  # @opetope/react
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 34bdbfb: **An options record is exact by its keys, and the guard that was weaker than no guard is gone (D406).** Exactness is
8
+ checked against the set of keys the argument carries, never against the record the compiler infers from it. The old
9
+ form asked the record to witness itself — `Actual & NoExtraKeys<Actual, Shape> & Shape` — and a record assigned to a
10
+ name first and sharing no key with the shape gave that inference nothing: the guard collapsed and took the weak-type
11
+ check down with it, so the guarded surface admitted what the bare shape refused.
12
+
13
+ ```ts
14
+ // the record a migration carries: assigned first, sharing no key with the options of this node
15
+ const carriedOver = { concurency: 'queue' as const };
16
+
17
+ // before: `own.command`, `own.effect`, `own.event` and both `own.scope` forms accepted this in silence
18
+ // after: refused where it is written, and the message names the key
19
+ command(orders, () => undefined, carriedOver);
20
+ ```
21
+
22
+ - Measured on the auditor's own probe: six misspelled records on six feature nodes, **one refusal before and six
23
+ after**, with no correct declaration in this repository changing.
24
+ - The gate now stands on every authoring record that had one, on `ctx.command`, `ctx.effect`, `ctx.event` and both
25
+ `ctx.scope` forms, which had none, and on the top-level record of `resource.load`/`resource.live` on both
26
+ surfaces. The nested records of the Resource family (`identity`, `apply`, `delivery`, `pagination`) keep the
27
+ runtime refusal of D345 and no type gate: the decision measures why.
28
+ - `queueBy` on a feature used to print `concurrency: { by }`, which is a member of a model command and of nothing
29
+ else, so the author walked from one refusal into the next. It now names the queue a feature has and the surface
30
+ that mints the keyed one, and a negative beside the positive holds that the advice compiles.
31
+ - A stack navigator refuses `persistence` and `defaultScreen` — the words of switch mode — instead of accepting and
32
+ ignoring them. The union of the two option records had no forbidding field, so an assigned record simply matched
33
+ the branch with fewer words and the host persistence contract was never called. `createNavigator` refuses the same
34
+ keys at run time, so a caller without types hears it too.
35
+ - Breaking: `ExactInput` and `NoExtraKeys` are gone from `@opetope/core/internal`; `ExactKeys` is the one gate. A
36
+ record with a key its node does not name stops compiling, as do a feature `queueBy` and a stack navigator carrying
37
+ switch-mode words.
38
+ - Measured: both runtime size rows are byte-identical — 48 067 B for the application graph consumer and 39 500 B for
39
+ the public feature consumer. The navigator's run-time refusal is isolated at **+70 B**: the navigator consumer is
40
+ 1 943 B without it and 2 013 B with it.
41
+ - Measured: the compiler does less work than before, not more. Type-stress instantiations are 11 647 / 85 822 /
42
+ 156 926 over the three slices against a recorded baseline of 11 582 / 90 074 / 165 874 — inferring a key set is
43
+ cheaper than inferring the record, and the decision records which spelling of the gate costs what.
44
+
45
+ - 34bdbfb: **A word of a Resource declaration is its key, not its value — this is a breaking change (D407).** Every option of
46
+ `resource.load` and `resource.live`, and the host `grace` of `openFeature` and `openApplication`, is now read from
47
+ the key the record carries. A record keeps a key whose value is `undefined`, so a record built by a conditional
48
+ spread was until now indistinguishable from one that never wrote the word — and it was accepted in silence.
49
+
50
+ ```ts
51
+ // refused now, and silently a singleton before: the key is written, so the request is written
52
+ ctx.resource.load({ ...shared, request: keyed ? account : undefined });
53
+
54
+ // write the two declarations instead; the half they share is still written once
55
+ keyed ? ctx.resource.load({ ...shared, request: account }) : ctx.resource.load(shared);
56
+ ```
57
+
58
+ What a written `undefined` used to mean, and what it means now:
59
+
60
+ | Written | Was, in silence | Is now |
61
+ | ------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------- |
62
+ | `request: undefined` | a singleton, and the key of every cache entry with it | `… request must be a value or a Readable.` |
63
+ | `enabled: undefined` | a Resource nothing gates | `… enabled must be a Readable.` |
64
+ | `grace: undefined` | the host's window, and accepted beside `lifetime: 'owner'` | the refusal of the word beside an owner, of the number otherwise |
65
+ | `identity: undefined` | no key and no scope: one shared materialization | `… identity must be a plain object record.` |
66
+ | `identity: { key: undefined }` | the request naming itself, or nothing on a singleton | `… identity.key must be a function.`, or the refusal of the word |
67
+ | `identity: { scope: undefined }` | a materialization shared where one was scoped | `… identity.scope must be a Readable.` |
68
+ | `load: undefined` on `live` | no load channel, and `apply.load` refused for it | `… load must be a function.` |
69
+ | `cache: undefined` | no cache | `… cache must be a plain object record.` |
70
+ | `pagination: undefined` | no pagination capability | `… pagination must be a plain object record.` |
71
+ | `delivery: undefined` | the default delivery | `… delivery must be a plain object record.` |
72
+ | `delivery.overflow: undefined` | the answer of a full capacity D351 wrote | `… delivery overflow must be flush-oldest or reject.` |
73
+ | `delivery.pending: undefined` | the field the record owes, reported as missing | `Unknown model resource delivery undefined.` |
74
+ | `apply: { load: undefined }` beside `load` | `apply.load is required beside load.` | `… apply.load must be a function.` |
75
+ | `grace: undefined` on `openFeature` / `openApplication` | the `0` of a host without the word | the refusal of the number |
76
+
77
+ Each refusal is this node's own sentence for a value the word cannot take — the sentence the request of an `event`
78
+ has printed for the same mistake since D395 — followed by the clause that names the remedy the sentence alone
79
+ cannot: `A key written as undefined is a word this declaration wrote: leave the key out where the word is not
80
+ meant.` Where the key is owed rather than optional — `apply.load` beside a load channel, `pending` inside a
81
+ `delivery` — the refusal names the debt and offers no such remedy.
82
+
83
+ `createScenario` of `@opetope/react` forwards the same word, and forwarded it by value: a `grace` written as
84
+ `undefined` there was dropped into the `0` of a host that wrote no window at all. It now travels to
85
+ `openApplication` and is refused there, beside the `conditions` and `imports` of the same record, which already
86
+ asked the key.
87
+
88
+ **Rewrite by building the record, not the value.** Branch on the record (`flag ? f({ ...shared, word }) : f(shared)`),
89
+ or spread a record that carries the key only when it is meant (`...(flag ? { word } : {})`). A conditional value
90
+ (`word: flag ? v : undefined`) is the form that breaks, and it is the form this decision exists to refuse.
91
+
92
+ - aaaa92c: **One word for a command, one for its order (D385–D389).** The primitive is `Command<Input, Output>`: the type was
93
+ the last place the vocabulary still said `Call`, while the hook was already `useCommand`, the record `CommandHook`,
94
+ the outcome `CommandOutcome` and the fixture `command(...)`. `CallError` is `CommandError`, `runCall` is
95
+ `runCommand`, `RunCallOptions` is `RunCommandOptions`, `CallTimeoutError` is `CommandTimeoutError` and
96
+ `CallInputArgs` is `CommandInputArgs`. There are no aliases.
97
+
98
+ - **A body takes one argument, its context (D386).** `ctx.command(({ input, update }) => …)` and
99
+ `own.command(within, ({ input, source, signal }) => …)`. `Input` and `Output` default to `void`, so the
100
+ `_input: void` placeholder is gone from every command that takes none. A typed input is declared once, by
101
+ annotating the destructured context: `({ input, update }: ModelCommandContext<Deposit>)` or
102
+ `({ input, source }: FeatureCommandContext<typeof polling, Deposit>)`. Both types are new public exports. Give a
103
+ model factory a result type — `function createCounter(context: ModelContext): ModelOf<typeof Counter>`, the form
104
+ the examples use: inside an inline arrow handed straight to `openModel(Decl, ctx => …)` or `model(Decl, ctx => …)`
105
+ the expected type is not instantiated yet, so a body that names no input reads as `Command<never, …>` instead of
106
+ taking the `void` default.
107
+ - **`concurrency` replaces `policy`, `queueBy` and `lane` (D387).** One word that also takes the queue node itself:
108
+ `'parallel' | 'queue' | 'latest' | lane | { by } | { by, lane } | { lane, pending: 'latest' }`. A lane is a
109
+ reference commands share, which is why the word accepts it; `'parallel'` beside a lane and a bare `by` without a
110
+ queue are now unwritable rather than refused. `dedupe` stays a sibling option and `'latest'` still refuses it.
111
+ A selection writes the same word with the members it can answer for — the keyed record is not one of them, and
112
+ a feature has no keyed queue either, so both are refused with the reason. The retired `policy`, `queueBy`, `lane`
113
+ and `once` are named by each options record, so they are refused in a written literal and in an assigned record
114
+ alike — on `ctx.command`, on `own.command` and on `ctx.select`, which also refuses `memoize` and `dedupe`.
115
+ - **Declaring and selecting are two words (D388).** `ctx.command(run, options?)` declares;
116
+ `ctx.select(source, 'name' | ['a', 'b'], options?)` selects what a dependency already declares — one name answers
117
+ one command, a list answers a record. `ctx.calls` and `own.calls` are gone, and so is `useCommands`: a record of command consumers
118
+ comes from `useModel(Declaration, select)`, which has an owner and a lifetime. `bind` of `@opetope/runtime` is
119
+ unchanged.
120
+ - **`once` is `memoize` (D389).** The cache keeps its behaviour and loses a name the platform already owns:
121
+ `addEventListener(type, handler, { once: true })` means "then stop", which is not what this option ever did.
122
+
123
+ `@opetope/lint` follows the vocabulary: `opetope/capture-command-cleanup` reads the context off the body's one
124
+ parameter and recognises `concurrency: 'parallel'`, and `opetope/no-command-in-deps` names the selection a record
125
+ comes from.
126
+
127
+ - 34bdbfb: **One word for one thing, and one sentence for one question (D410, D411).** A vocabulary pass over the public API:
128
+ the refusals that asked one question in five sentences now ask it in one, the position a signature calls `source`
129
+ is called `source` everywhere, and eight words that named one behaviour twice are one word apiece. No branch of the
130
+ runtime changes: the same checks run in the same order.
131
+
132
+ **Breaking, word by word.** Every row is a source change with no alias; `opetope/no-retired-vocabulary` reports each
133
+ one and names the replacement.
134
+
135
+ | Before | After |
136
+ | -------------------------------------------------------------- | --------------------------------------------------- |
137
+ | `defineCondition(id, { from, select })` | `defineCondition(id, { select, source })` |
138
+ | `slot(target, value, { when })`, same at `pipe`, `register` | `slot(target, value, { enabled })` |
139
+ | `resource.live({ delivery: { overflow: 'reject' } })` | `resource.live({ delivery: { overflow: 'drop' } })` |
140
+ | `outcome.status === 'ok' \| 'failed' \| 'cancelled'` | `outcome.kind === 'ok' \| 'failed' \| 'cancelled'` |
141
+ | `ResourceActivity` from `@opetope/devtools` | `ResourceNodeActivity` from `@opetope/devtools` |
142
+ | `ResourceDisposer` from `@opetope/core/internal` | `Disposer` from `@opetope/core/internal` |
143
+ | `NavigatorPolicy` from `@opetope/navigation` | `NavigatorCatalogue` |
144
+ | `surface.policy` | `surface.catalogue` |
145
+ | `ApplicationConditionBinding` from `@opetope/runtime/internal` | the same name, from `@opetope/runtime` |
146
+
147
+ `@opetope/lint` renames the rule that read that field: `opetope/when-predicate` is `opetope/enabled-predicate`,
148
+ and its message ids are `enabledIsAsync`, `enabledReturnsBareSource` and `enabledReturnsSource`. A configuration
149
+ that named the old rule has to name the new one; `configs.recommended` and the shipped `oxlintrc.json` fragment
150
+ already do.
151
+
152
+ Two of these are record keys, so a source with `sort-keys` reorders the literal as well as renaming the key:
153
+ `{ from, select }` becomes `{ select, source }`, and `{ Component, when }` becomes `{ Component, enabled }`.
154
+ `{ status: 'ok', value }` becomes `{ kind: 'ok', value }`, which sorts unchanged, while
155
+ `{ reason, status: 'cancelled' }` becomes `{ kind: 'cancelled', reason }`, which does not.
156
+
157
+ **Why each one.** `source` is the word every context already answers the position with, and `from*` stays the
158
+ prefix of a factory that names where a `Readable` comes from (`fromExternal`, `fromMaybe`). `enabled` is the truth
159
+ the runtime reads while a node exists — one word at a contribution and at a Resource — while `when` keeps the other
160
+ mechanism, the declared `Condition`s an application resolves before the node exists; `scope.while(source, open,
161
+ { when })` and an effect's `filter` are untouched. `drop` is what a full capacity does with the value that does not
162
+ fit, at both nodes that admit a producer; `flush-oldest` is still the default of a keyed delivery and still the
163
+ answer for a producer that may lose nothing. `kind` is the discriminant eleven settled answers already carried.
164
+ `NavigatorCatalogue` is named for its job, beside `NavigatorPersistence`, because a policy is data (D291) and this
165
+ is four callbacks a navigator asks.
166
+
167
+ **Not breaking, and worth reading.** Every options record now refuses an unknown field with the sentence spec §3
168
+ promised — `<subject> <node> <record> contains unknown field <key>.` — and says which field it owes with
169
+ `<subject> <node> <record> requires <key>.` Five sentences became one, and the three that never printed the field
170
+ at all (`requires exact data fields`, `own.attach takes open, or open and close, and nothing else`) now print it. A
171
+ node that reads a source says so in two sentences per surface instead of three, and the word of the position is the
172
+ author's. `own.select` lost two of its four overloads: a call that writes the options record has one candidate of
173
+ its arity now, so a fault in that record is reported as the record's own refusal instead of
174
+ `Argument of type '"echo"' is not assignable to parameter of type 'never'`, and the record names the five words a
175
+ selection does not take — `concurrency`, `dedupe`, `lane`, `memoize`, `policy` — each with what to write instead.
176
+
177
+ **Measured.** Runtime size consumers, rebuilt: `application graph consumer` 48 248 B against a ceiling of 49 000
178
+ (48 318 before), `public feature consumer` 39 579 B against 41 000 (39 712 before). Type stress: 11 695 / 87 068 /
179
+ 159 371 instantiations, unchanged to the digit. No ceiling moved.
180
+
181
+ - aaaa92c: **The destructive verb is `reset()` (D373).** `Resource.invalidate()` is now `Resource.reset()`, and the verb of the
182
+ React hook is renamed with it: `useResource(resource)` answers `{ pagination, refresh, reset, retry, state }`. Nothing
183
+ about the operation changes — it clears the whole cache, drops the current data, opens a new epoch and restarts every
184
+ configured channel, exactly as D355 wrote it.
185
+
186
+ The old name promised the opposite of what it did. `invalidate` means «mark stale, keep the data» everywhere else —
187
+ TanStack Query's `invalidateQueries` keeps the cached data until a new answer arrives, and Apollo's `INVALIDATE`
188
+ marks a field stale without removing its value — so an author who came from there wrote it expecting a background
189
+ refresh over the rows still on screen and got an empty state instead.
190
+
191
+ - `resource.reset()` and `useResource(resource).reset.run()` are the current names; there is no alias, and a
192
+ Resource object has no `invalidate` member at all.
193
+ - A host that implements the `Resource` contract itself renames its member too: the runtime recognises a Resource
194
+ field of a model shape by the names of its verbs.
195
+ - `invalidate` now names no operation of a Resource. It is left free for a future operation that would mark data
196
+ stale and keep them — an intent recorded in D373, not a promise of one.
197
+
198
+ ### Patch Changes
199
+
200
+ - Updated dependencies [bb31983]
201
+ - Updated dependencies [bb31983]
202
+ - Updated dependencies [bb31983]
203
+ - Updated dependencies [dae5079]
204
+ - Updated dependencies [dae5079]
205
+ - Updated dependencies [bb31983]
206
+ - Updated dependencies [aaaa92c]
207
+ - Updated dependencies [34bdbfb]
208
+ - Updated dependencies [dae5079]
209
+ - Updated dependencies [34bdbfb]
210
+ - Updated dependencies [bb05f21]
211
+ - Updated dependencies [dae5079]
212
+ - Updated dependencies [34bdbfb]
213
+ - Updated dependencies [aaaa92c]
214
+ - Updated dependencies [34bdbfb]
215
+ - Updated dependencies [aaaa92c]
216
+ - Updated dependencies [34bdbfb]
217
+ - Updated dependencies [bb05f21]
218
+ - Updated dependencies [aaaa92c]
219
+ - Updated dependencies [aaaa92c]
220
+ - Updated dependencies [bb31983]
221
+ - Updated dependencies [34bdbfb]
222
+ - Updated dependencies [dae5079]
223
+ - Updated dependencies [aaaa92c]
224
+ - Updated dependencies [aaaa92c]
225
+ - Updated dependencies [aaaa92c]
226
+ - Updated dependencies [aaaa92c]
227
+ - Updated dependencies [dae5079]
228
+ - Updated dependencies [bb05f21]
229
+ - Updated dependencies [aaaa92c]
230
+ - Updated dependencies [dae5079]
231
+ - Updated dependencies [dae5079]
232
+ - Updated dependencies [aaaa92c]
233
+ - Updated dependencies [aaaa92c]
234
+ - Updated dependencies [34bdbfb]
235
+ - Updated dependencies [bb05f21]
236
+ - Updated dependencies [aaaa92c]
237
+ - Updated dependencies [bb31983]
238
+ - Updated dependencies [34bdbfb]
239
+ - Updated dependencies [dae5079]
240
+ - Updated dependencies [aaaa92c]
241
+ - Updated dependencies [aaaa92c]
242
+ - Updated dependencies [dae5079]
243
+ - Updated dependencies [bb05f21]
244
+ - Updated dependencies [dae5079]
245
+ - Updated dependencies [bb31983]
246
+ - Updated dependencies [bb31983]
247
+ - Updated dependencies [34bdbfb]
248
+ - Updated dependencies [bb05f21]
249
+ - Updated dependencies [34bdbfb]
250
+ - Updated dependencies [34bdbfb]
251
+ - @opetope/runtime@0.12.0
252
+ - @opetope/core@0.12.0
253
+
3
254
  ## 0.11.0
4
255
 
5
256
  ### Minor Changes
@@ -274,7 +525,7 @@
274
525
 
275
526
  **`ResourceHook` (D321).** `useResource(resource)` answers `{ snapshot, refresh, retry }`, and a field of a
276
527
  `useModel` selection whose value is a request answers the same hook and takes the same lease. Nine models of one
277
- application had wrapped `resource.refresh()` in a `Call<void, void>` for nothing but the `inFlight` of a button.
528
+ application had wrapped `resource.refresh()` in a `Command<void, void>` for nothing but the `inFlight` of a button.
278
529
  Recognition asks the same question the type asks: `Resource` is a structural contract (D199), so a host adapter that
279
530
  satisfies it is classified and leased exactly like a materialization the runtime built, which stays the fast path.
280
531
  `read(resource)` still answers the snapshot and leases nothing. One pair of controllers per resource identity keeps
@@ -306,11 +557,11 @@
306
557
 
307
558
  ### Patch Changes
308
559
 
309
- - b964772: Модель подтверждает мутации принадлежащего ей Resource, получает явный исход обновления, выполняет Call из
560
+ - b964772: Модель подтверждает мутации принадлежащего ей Resource, получает явный исход обновления, выполняет Command из
310
561
  effect и сериализует независимые объекты через очередь по ключу. Stream умеет накапливать кадры в границах
311
562
  открытия. Контекст данных отделён от ключа, а ёмкость кэша перенесена из retention в самостоятельную опцию cache.
312
563
  Retention теперь задаётся литералом, model resource/stream принимает готовый Readable, а `queueBy` создаёт
313
- приватную keyed queue команды или использует явно переданную общую lane. Effect переносит отмену вложенного Call
564
+ приватную keyed queue команды или использует явно переданную общую lane. Effect переносит отмену вложенного Command
314
565
  через `rethrowIfCancelled`.
315
566
  - Updated dependencies [b964772]
316
567
  - @opetope/core@0.9.3
@@ -330,7 +581,7 @@
330
581
 
331
582
  - d7f346a: A command hook is `{ run, inFlight, outcome }`, and an owned attachment step is `step(key, run, cleanup)`.
332
583
 
333
- `CommandHook.handler` is removed, from `useCommand`, from every key of `useCommands` and from the `Call` fields of a
584
+ `CommandHook.handler` is removed, from `useCommand`, from every key of `useCommands` and from the `Command` fields of a
334
585
  `useModel` selection. It bought one arrow in JSX and nothing the types did not already hold: `onClick={logout.run}`
335
586
  never compiled, because a `MouseEvent` is not a `void` input. A command without input now binds as
336
587
  `onClick={() => void logout.run()}` (D292).
@@ -405,17 +656,17 @@ after <attempts> attempts — ` and carrying a non-enumerable `eventually` recor
405
656
  `timeoutMs`, so an expiry does not read like a single failed assertion. A throw that is not an `Error` is rethrown
406
657
  untouched.
407
658
 
408
- `runCall(target, input?, { signal, timeoutMs })` takes a deadline on that one run. `run` is the top-level execution
659
+ `runCommand(target, input?, { signal, timeoutMs })` takes a deadline on that one run. `run` is the top-level execution
409
660
  the consumer owns (D292), so the deadline cancels the way that consumer cancels — by aborting the signal this run
410
- supplied — and rejects with a `CallTimeoutError` naming the call and the deadline. A `signal` stands beside it and
661
+ supplied — and rejects with a `CommandTimeoutError` naming the call and the deadline. A `signal` stands beside it and
411
662
  the first of the two to abort supplies the reason, so a cancelled outcome is never reported as a timeout.
412
663
 
413
- `command(run)` is now published by `@opetope/core/testing` as a `Call` fixture with no binding: the body is
664
+ `command(run)` is now published by `@opetope/core/testing` as a `Command` fixture with no binding: the body is
414
665
  positional and receives the signal of the run, and there is no lane, queue, dedupe or retirement — so no `inFlight`
415
666
  to read, no fence, and two runs of one fixture overlap. A model opened with `openModel` takes it as a dependency,
416
- which removes the second model a test used to open for one Call. `@opetope/react/testing` keeps its own `command`,
667
+ which removes the second model a test used to open for one Command. `@opetope/react/testing` keeps its own `command`,
417
668
  which is this one plus the contribution binding a mount reads, and also re-exports `yieldTurn`, `eventually` and
418
- `CallTimeoutError`.
669
+ `CommandTimeoutError`.
419
670
 
420
671
  `settled` and `waitFor` accept the `ApplicationExecution` an `openApplication` returned. An application contributes
421
672
  the models of every generation it holds right now and a fifth fact of its own: the instances whose retirement has
@@ -425,7 +676,7 @@ after <attempts> attempts — ` and carrying a non-enumerable `eventually` recor
425
676
 
426
677
  Migration: `flush()` (a `setImmediate` or `setTimeout(0)` helper) → `yieldTurn()`; a local generic
427
678
  `waitFor<Value>(read, predicate)` → the library `waitFor`, which returns the value; a local `eventually` → the
428
- library one, whose expiry carries the last assertion failure; `AbortSignal.timeout(500)` in `runCall` →
679
+ library one, whose expiry carries the last assertion failure; `AbortSignal.timeout(500)` in `runCommand` →
429
680
  `{ timeoutMs: 500 }`; `command` from `@opetope/react/testing` in a test that renders nothing →
430
681
  `command` from `@opetope/core/testing`; and a hand-written poll loop for an application → `settled(application)`.
431
682
 
@@ -453,9 +704,9 @@ auth.getTokens()) === null)` — belongs in `eventually`, which awaits its block
453
704
 
454
705
  - 0318472: Give every package a testing entry, and open a model without a feature.
455
706
 
456
- `@opetope/core/testing` publishes `testReadable` (the state cell a model receives as `ctx.state`), `runCall` — moved
707
+ `@opetope/core/testing` publishes `testReadable` (the state cell a model receives as `ctx.state`), `runCommand` — moved
457
708
  here from `@opetope/react/testing`, because it reaches the existing invoker and knows about neither a renderer nor a
458
- feature — and `CallInputArgs`, so a test can wrap `runCall` without naming `./internal` (D285).
709
+ feature — and `CommandInputArgs`, so a test can wrap `runCommand` without naming `./internal` (D285).
459
710
 
460
711
  `@opetope/runtime/testing` adds `openModel(Decl, dependencies?, create, { reporter })`, which opens one model with
461
712
  the same kernel, lanes and retirement the feature path builds and none of what a feature adds (D286), and
@@ -526,9 +777,9 @@ auth.getTokens()) === null)` — belongs in `eventually`, which awaits its block
526
777
 
527
778
  Keep derive({ from, select }) for explicit sources and preserve the existing reactive scheduling and lifetime behavior. Migrate framework consumers and add a compile-checked model example in the English and Russian guides.
528
779
 
529
- - 538cdfe: Replace singleFlight with dedupe on custom Calls. Use dedupe: true to share one pending or running execution per Call and generation, or a key function to share equivalent inputs. Retain the first input and independent caller cancellation. Remove the former name without a compatibility alias.
780
+ - 538cdfe: Replace singleFlight with dedupe on custom Calls. Use dedupe: true to share one pending or running execution per Command and generation, or a key function to share equivalent inputs. Retain the first input and independent caller cancellation. Remove the former name without a compatibility alias.
530
781
 
531
- Reject both forms of dedupe with policy: latest in types and at runtime. Different keys keep one FIFO under queue and run independently under parallel. Preserve once caching before deduplication and keep scheduling options on Call declarations rather than React hooks. Add a compile-checked model example and update the English and Russian guides.
782
+ Reject both forms of dedupe with policy: latest in types and at runtime. Different keys keep one FIFO under queue and run independently under parallel. Preserve once caching before deduplication and keep scheduling options on Command declarations rather than React hooks. Add a compile-checked model example and update the English and Russian guides.
532
783
 
533
784
  - Updated dependencies [538cdfe]
534
785
  - Updated dependencies [538cdfe]
@@ -566,14 +817,14 @@ auth.getTokens()) === null)` — belongs in `eventually`, which awaits its block
566
817
  runtime nodes or adding ownership relations. Preserve diagnostic group names in the tooltip and inspector.
567
818
 
568
819
  - a6a5f73: Remove the redundant `Command` type synonym and the `CommandOutcome` re-export from
569
- `@opetope/react/integration`. Import `Call` from `@opetope/core` and the UI `CommandOutcome`
820
+ `@opetope/react/integration`. Import `Command` from `@opetope/core` and the UI `CommandOutcome`
570
821
  from `@opetope/react`. Alpha releases keep one current API without compatibility aliases.
571
822
 
572
823
  ### Patch Changes
573
824
 
574
825
  - a6a5f73: Allow nested `invoke(target)` without an input placeholder for Calls accepting `void` or `undefined`, including
575
826
  model, feature and attachment execution contexts. Keep required inputs inferred from the target, preserve generic
576
- forwarding, and share the no-input rule with React `run` and testing `runCall`. Calls accepting `never` cannot be
827
+ forwarding, and share the no-input rule with React `run` and testing `runCommand`. Calls accepting `never` cannot be
577
828
  invoked without input.
578
829
  - a6a5f73: Allow omitting `conditions` from `openApplication` and `createScenario` when no enabled feature requires a host-bound condition. Missing external bindings and explicitly invalid condition records still fail validation.
579
830
  - Updated dependencies [a6a5f73]
@@ -587,7 +838,7 @@ auth.getTokens()) === null)` — belongs in `eventually`, which awaits its block
587
838
 
588
839
  ### Minor Changes
589
840
 
590
- - 410f2f0: Run authentic Calls in headless model tests with `runCall` from `@opetope/react/testing`, preserving their existing policies, cancellation, errors and owner lifetime.
841
+ - 410f2f0: Run authentic Calls in headless model tests with `runCommand` from `@opetope/react/testing`, preserving their existing policies, cancellation, errors and owner lifetime.
591
842
 
592
843
  Add the recommended `opetope/no-internal-imports` rule for static imports, re-exports, literal dynamic imports, unshadowed `require` and TypeScript import types. `configs.internalImports` uses this rule for JavaScript and TypeScript, including tests, without replacing the host's `no-restricted-imports`. The redundant `internalImportPattern` export is removed; allow trusted implementation files through the config or an explicit rule override.
593
844