@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 +268 -17
- package/README.md +107 -67
- package/README.ru.md +53 -66
- package/dist/command-hook-controller.d.ts +4 -4
- package/dist/command-hook.d.ts +3 -3
- package/dist/command.d.ts +8 -8
- package/dist/contribution-frame-CCKnOxZR.js +2 -0
- package/dist/contribution-frame-CCKnOxZR.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-binding.d.ts +4 -4
- package/dist/model-hook.d.ts +2 -2
- package/dist/resource-command.d.ts +11 -4
- package/dist/scenario-slot.d.ts +6 -3
- package/dist/scenario-types.d.ts +2 -2
- package/dist/scenario.d.ts +1 -2
- package/dist/{commands-hook.d.ts → selection-hooks.d.ts} +4 -11
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-DkNmjChZ.js +0 -2
- package/dist/contribution-frame-DkNmjChZ.js.map +0 -1
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 `
|
|
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, получает явный исход обновления, выполняет
|
|
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 переносит отмену вложенного
|
|
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 `
|
|
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
|
-
`
|
|
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 `
|
|
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 `
|
|
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
|
|
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
|
-
`
|
|
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 `
|
|
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`), `
|
|
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 `
|
|
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
|
|
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
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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
|
|