@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/README.md
CHANGED
|
@@ -19,12 +19,12 @@ No GitHub access is needed to read those installed guides.
|
|
|
19
19
|
|
|
20
20
|
```tsx
|
|
21
21
|
import { defineModel } from '@opetope/core';
|
|
22
|
-
import type {
|
|
22
|
+
import type { Command, Readable } from '@opetope/core';
|
|
23
23
|
import { defineSlot, requiresModels, Slot, useModel } from '@opetope/react';
|
|
24
24
|
|
|
25
25
|
const CounterModel = defineModel<{
|
|
26
26
|
readonly count: Readable<number>;
|
|
27
|
-
readonly increment:
|
|
27
|
+
readonly increment: Command<void, void>;
|
|
28
28
|
}>('example.counter.model');
|
|
29
29
|
const CounterSlot = defineSlot<{ readonly label: string }>('example.counter.slot');
|
|
30
30
|
|
|
@@ -39,7 +39,7 @@ const CounterButton = requiresModels([CounterModel])(({ label }: { readonly labe
|
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<button disabled={inFlight} onClick={() => void run()}>
|
|
42
|
-
{outcome?.
|
|
42
|
+
{outcome?.kind === 'failed' ? 'Retry' : `${label}: ${count}`}
|
|
43
43
|
</button>
|
|
44
44
|
);
|
|
45
45
|
});
|
|
@@ -57,10 +57,10 @@ rejection (D116, D292).
|
|
|
57
57
|
`outcome` is the last settled non-cancelled outcome of this consumer: `ok` takes the place of a previous `failed`
|
|
58
58
|
and a `failed` the place of a previous `ok`, a `cancelled` outcome moves nothing, and starting a run clears nothing,
|
|
59
59
|
so a failure stays readable while the retry is in flight. Before the first settle it is `undefined`. Read it through
|
|
60
|
-
its discriminant — `outcome?.
|
|
60
|
+
its discriminant — `outcome?.kind === 'failed' ? outcome.error : null`.
|
|
61
61
|
|
|
62
62
|
A command without input binds to an event through an arrow: `onClick={() => void logout.run()}`. The arrow is what
|
|
63
|
-
keeps the React event out of the
|
|
63
|
+
keeps the React event out of the Command and the promise from hanging; `onClick={logout.run}` does not compile, because
|
|
64
64
|
a `MouseEvent` is not a `void` input. Use `run(input, options?)` for data, an outcome, callbacks or a per-run signal.
|
|
65
65
|
Where a lint config bans an inline arrow prop (`react-perf/jsx-no-new-function-as-prop`, `react/jsx-no-bind`), hoist
|
|
66
66
|
it with `useCallback(() => void logout.run(), [logout.run])`: the dependency is `run`, never the hook object (D290).
|
|
@@ -70,39 +70,25 @@ changes as `inFlight` or `outcome` changes, so an effect or a memo depends on `r
|
|
|
70
70
|
`opetope/no-command-in-deps` holds this for the author (D290). A `run` captured from a replaced or unmounted
|
|
71
71
|
consumer is fenced; it does not start work on the replacement.
|
|
72
72
|
|
|
73
|
-
The hook schedules nothing. Every `run` reaches the
|
|
74
|
-
happens to it (D203). For an absolute value setter the model declares `
|
|
75
|
-
newest input then replaces the waiting one there (D185). What the consumer keeps
|
|
76
|
-
input signal is refused as `cancelled` without reaching the
|
|
77
|
-
way, `inFlight` is true while any run this consumer started is unsettled,
|
|
78
|
-
and signal.
|
|
73
|
+
The hook schedules nothing. Every `run` reaches the command, and the order the command was declared with decides
|
|
74
|
+
what happens to it (D203). For an absolute value setter the model declares `concurrency: 'latest'` in
|
|
75
|
+
`context.command`, and the newest input then replaces the waiting one there (D185, D387). What the consumer keeps
|
|
76
|
+
is its own: an already-aborted input signal is refused as `cancelled` without reaching the command, a run of an
|
|
77
|
+
unmounted consumer is refused the same way, `inFlight` is true while any run this consumer started is unsettled,
|
|
78
|
+
and every run carries its own callbacks and signal.
|
|
79
79
|
|
|
80
80
|
To share equivalent pending or running work, the model declares `dedupe: true` or `dedupe: input => key`
|
|
81
|
-
on `context.
|
|
82
|
-
be combined with `latest` (D277). These are
|
|
81
|
+
on `context.command`. Sharing keeps the first input and each caller's independent cancellation; neither form may
|
|
82
|
+
be combined with `latest` (D277). These are Command options, not hook options.
|
|
83
83
|
|
|
84
|
-
For several commands
|
|
84
|
+
For several commands a component writes `useCommand` per command, or one `useModel` selection, which already
|
|
85
|
+
answers ready hooks. `useCommands` is gone: a record over the one `CommandHook` was sugar that saved hook calls,
|
|
86
|
+
not a second mechanism, and the set that is really wanted is a selection in the model, where `ctx.select` gives it
|
|
87
|
+
an owner and a lifetime (D388).
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
void save.run(input);
|
|
90
|
-
const saving = save.inFlight;
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Import `useCommands` from `@opetope/react`. Each selected field returns the same `CommandHook` as `useCommand`,
|
|
94
|
-
with its own input/output types and its own `inFlight` and `outcome`. Keys are local UI names; commands may come from
|
|
95
|
-
different granted models. There is no second argument: each call carries its own policy. Its own case is a key set
|
|
96
|
-
the component does not know in advance — hooks are not called in a loop; a fixed set is `useCommand` per word or one
|
|
97
|
-
`useModel` selection. It is proven by the packaged consumer of `ci:pack` and is a candidate to leave this entry
|
|
98
|
-
until a second application is measured (D301).
|
|
99
|
-
|
|
100
|
-
An inline object needs no `useMemo`. Reordering keys or updating a sibling preserves an unchanged field's object
|
|
101
|
-
and `.run`. Adding or removing keys does not change the number of React hooks. Removing a key or replacing its
|
|
102
|
-
command starts a fresh local status for that field; abandoned renders leave the
|
|
103
|
-
committed selection intact. Two aliases of one Call have independent local statuses, just like two `useCommand`
|
|
104
|
-
consumers; they use the same Call policy and mount command record (D197, D203). A group introduces no shared busy state, queue or transaction; controls that overwrite one value still
|
|
105
|
-
need one semantic command. Only own enumerable properties are selected, including symbol keys.
|
|
89
|
+
Two aliases of one Command have independent local statuses, just like two `useCommand` consumers; they use the same
|
|
90
|
+
Command concurrency and mount command record (D197, D203). A selection introduces no shared busy state, queue or
|
|
91
|
+
transaction; controls that overwrite one value still need one semantic command.
|
|
106
92
|
|
|
107
93
|
## Selecting data and commands
|
|
108
94
|
|
|
@@ -110,10 +96,22 @@ need one semantic command. Only own enumerable properties are selected, includin
|
|
|
110
96
|
|
|
111
97
|
A model selection can use a named interface without an index signature (D217). Its result is a flat data record;
|
|
112
98
|
arrays, functions, constructors and built-in collection/date/promise objects are not selection records.
|
|
113
|
-
`read(source)` returns its snapshot; `read(source, project)` returns a projection. Authentic
|
|
114
|
-
fields become the same `CommandHook` as `
|
|
99
|
+
`read(source)` returns its snapshot; `read(source, project)` returns a projection. Authentic Commands selected as
|
|
100
|
+
record fields become the same `CommandHook` as `useCommand`, including independent alias statuses and stable `.run`.
|
|
115
101
|
A single selected command needs no nested hooks:
|
|
116
102
|
|
|
103
|
+
<!--example
|
|
104
|
+
import type { Command } from '@opetope/core';
|
|
105
|
+
import { defineModel } from '@opetope/core';
|
|
106
|
+
import { useModel } from '@opetope/react';
|
|
107
|
+
|
|
108
|
+
const AuthModel = defineModel<{ readonly logout: Command<void, void> }>('example.auth.model');
|
|
109
|
+
|
|
110
|
+
const LogOutButton = () => {
|
|
111
|
+
/* … */
|
|
112
|
+
};
|
|
113
|
+
-->
|
|
114
|
+
|
|
117
115
|
```tsx
|
|
118
116
|
const { logout } = useModel(AuthModel, auth => ({ logout: auth.logout }));
|
|
119
117
|
return (
|
|
@@ -136,13 +134,26 @@ values — that field answers a `ResourceHook`, and one lease per Resource ident
|
|
|
136
134
|
keys name one Resource (D321, D359) — and nothing else: a
|
|
137
135
|
`read(resource.state)` leases nothing, and neither does the one-argument form. Combining hooks does not promise fewer
|
|
138
136
|
source subscriptions or faster renders; a raw `useModel` import now also includes the selection implementation.
|
|
139
|
-
There is no additional
|
|
137
|
+
There is no additional Command scheduler.
|
|
140
138
|
|
|
141
139
|
## Application host
|
|
142
140
|
|
|
143
141
|
The host opens a feature graph with `openApplication`. Ready features publish their UI contributions atomically;
|
|
144
142
|
the ordinary `Slot` mounts them into consumer-owned targets:
|
|
145
143
|
|
|
144
|
+
<!--example
|
|
145
|
+
import { defineSlot, Slot } from '@opetope/react';
|
|
146
|
+
|
|
147
|
+
const exampleFooterSlot = defineSlot('example.footer');
|
|
148
|
+
const exampleSettingsSlot = defineSlot('example.settings');
|
|
149
|
+
|
|
150
|
+
const ExampleShell = () => (
|
|
151
|
+
<>
|
|
152
|
+
/* … */
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
-->
|
|
156
|
+
|
|
146
157
|
```tsx
|
|
147
158
|
<Slot target={exampleFooterSlot} />
|
|
148
159
|
<Slot target={exampleSettingsSlot} />
|
|
@@ -152,6 +163,26 @@ The application owns feature lifetimes; each feature's UI mounts through its con
|
|
|
152
163
|
|
|
153
164
|
## Feature demand boundary
|
|
154
165
|
|
|
166
|
+
<!--example
|
|
167
|
+
import type { ReactNode } from 'react';
|
|
168
|
+
import type { Resource } from '@opetope/core';
|
|
169
|
+
import { defineSlot } from '@opetope/react';
|
|
170
|
+
import type { FeatureDemandSource } from '@opetope/react/integration';
|
|
171
|
+
|
|
172
|
+
type Tasks = Readonly<{ id: string }>;
|
|
173
|
+
type ConfirmActionProps = Readonly<{ itemId: string; onConfirmed: () => void }>;
|
|
174
|
+
|
|
175
|
+
declare const host: FeatureDemandSource<Readonly<{ exports: Readonly<{ tasks: Resource<Tasks, 'none'> }> }>>;
|
|
176
|
+
declare const itemId: string;
|
|
177
|
+
declare const onConfirmed: () => void;
|
|
178
|
+
declare const children: ReactNode;
|
|
179
|
+
declare const Spinner: () => null;
|
|
180
|
+
declare const Failure: (props: Readonly<{ error: unknown; onRetry: () => void }>) => null;
|
|
181
|
+
declare const TasksResourceLease: (props: Readonly<{ children: ReactNode; resource: Resource<Tasks, 'none'> }>) => null;
|
|
182
|
+
|
|
183
|
+
const confirmActionContentSlot = defineSlot<ConfirmActionProps>('example.confirmAction.content');
|
|
184
|
+
-->
|
|
185
|
+
|
|
155
186
|
```tsx
|
|
156
187
|
import { Slot } from '@opetope/react';
|
|
157
188
|
import { FeatureBoundary, useFeatureRetry } from '@opetope/react/integration';
|
|
@@ -211,21 +242,21 @@ frame of the instance that gave the contribution. The order and the stable key c
|
|
|
211
242
|
React introduces neither a second comparator nor a second lifetime registry. The packaged consumer of `ci:pack`
|
|
212
243
|
exercises that caching and routes one contribution through it (D301).
|
|
213
244
|
|
|
214
|
-
`useResource(resource)` answers `{
|
|
245
|
+
`useResource(resource)` answers `{ pagination, refresh, reset, retry, state }`: the `ResourceState` through
|
|
215
246
|
`useSyncExternalStore`, the lease `resource.acquire()` answers taken in a commit effect for as long as the component
|
|
216
247
|
is mounted, and the three verbs as ordinary `CommandHook` consumers (D359). An interrupted render therefore opens
|
|
217
248
|
nothing, and when the reference changes the state follows the new Resource while the effect cleanup releases the
|
|
218
249
|
previous lease. The number of React hooks does not depend on the Resource: a declaration without `pagination`
|
|
219
250
|
subscribes to a constant source and keeps a `loadNext` that answers `skipped`, while a declaration that carried the
|
|
220
251
|
capability answers `pagination` as `{ loadNext, state }` (D356). A field of a `useModel` selection whose value is a
|
|
221
|
-
Resource answers the same hook and takes the same lease, so a model declares no `
|
|
252
|
+
Resource answers the same hook and takes the same lease, so a model declares no `Command<void, void>` around
|
|
222
253
|
`resource.refresh()` to give a button its `inFlight`. `useReadable(resource)` is a compile error, because a Resource
|
|
223
254
|
is not a `Readable`; `useReadable(resource.state)` and a `read(resource.state)` in a selector stay passive and lease
|
|
224
255
|
nothing — observing and holding are independent questions, so a selector that reads the state and a `useResource`
|
|
225
256
|
beside it subscribe twice (D349). The compile-checked example of specification §2.13 shows both sides of that
|
|
226
257
|
(D301, D321).
|
|
227
258
|
|
|
228
|
-
`refresh.run()`, `retry.run()` and `
|
|
259
|
+
`refresh.run()`, `retry.run()` and `reset.run()` reach the Resource, which is owned by the model that declared
|
|
229
260
|
it: `inFlight` is true until the outcome of the operation settles, `outcome` carries that `ResourceOperationOutcome`
|
|
230
261
|
as the `value` of an `ok` status, and `run(undefined, { signal })` cancels the wait of this consumer and not the work
|
|
231
262
|
the model owns. Two consumers of one Resource keep separate statuses, and `run` keeps its identity while the Resource
|
|
@@ -251,6 +282,14 @@ Every mount is an error boundary of its own contribution (D256). A render or com
|
|
|
251
282
|
reported to the feature that published the contribution, and leaves the other mounts of the target untouched.
|
|
252
283
|
`ContributionBoundary` states once, above every slot, what a failed mount shows:
|
|
253
284
|
|
|
285
|
+
<!--example
|
|
286
|
+
import { defineSlot, Slot } from '@opetope/react';
|
|
287
|
+
|
|
288
|
+
declare const FailedContribution: (props: Readonly<{ error: unknown; onRetry: () => void }>) => null;
|
|
289
|
+
|
|
290
|
+
const applicationSurface = defineSlot('example.surface');
|
|
291
|
+
-->
|
|
292
|
+
|
|
254
293
|
```tsx
|
|
255
294
|
import { ContributionBoundary } from '@opetope/react/integration';
|
|
256
295
|
|
|
@@ -276,7 +315,7 @@ dependency. `scenario.mount(target, { props })` uses the published Slot contribu
|
|
|
276
315
|
while targets without props omit it, exactly as with `Slot` (D217). Fixture commands do not bypass authority.
|
|
277
316
|
|
|
278
317
|
As with `openApplication`, omit `conditions` when no enabled feature requires a host-bound condition. Conditions
|
|
279
|
-
computed through `
|
|
318
|
+
computed through `source`/`select` bind automatically; external conditions still require their sources (D271).
|
|
280
319
|
|
|
281
320
|
The synchronous constructor exposes `ready`, so a test can inspect a pending lazy body before readiness.
|
|
282
321
|
`waitFor(snapshot => predicate, { label, timeoutMs, pollIntervalMs })` wakes on inspection changes and also polls
|
|
@@ -300,12 +339,13 @@ The inspection schemas are `opetope.devtools-graph/5` and `opetope.devtools-fram
|
|
|
300
339
|
a frame without `activity` preserves the previous activity; a full snapshot/reset without it clears that observation
|
|
301
340
|
(D216). Activity-bearing frames replace the previous activity in full.
|
|
302
341
|
Use matching runtime/devtools versions: previous graph/frame and activity revisions are rejected; weak-edge presence requires graph `/5` and frame `/4`. Activity identifies the execution,
|
|
303
|
-
actual feature generation, physical
|
|
342
|
+
actual feature generation, physical calls, exact current lane blockers, and for every registered Resource its
|
|
304
343
|
`kind`, `lifetime`, `epoch`, `state`, `activity` with its `operation`, `leases`, `subscribers`, `pagination` and the
|
|
305
344
|
`epoch` of each physical load — there is no `attempt` and no numeric generation, because a Resource runs one logical
|
|
306
345
|
attempt and recovery belongs to the transport (D358). Host demand and UI models are unknown. `freshness`
|
|
307
346
|
and `truncated` distinguish a complete live view from a partial or detached one. A closed session is stale;
|
|
308
|
-
`closed: true`
|
|
347
|
+
`closed: true` says that the close of the application finished, whether its physical drain succeeded or refused, and
|
|
348
|
+
the snapshot of a closed application names no feature and no Resource (D427). No control authority or product payload is added.
|
|
309
349
|
Activity output is bounded by record capacity. Snapshot collection still visits registered owners, executors and
|
|
310
350
|
resources, so capacity does not bound traversal cost. Collection stops once truncation is proven;
|
|
311
351
|
idle executors may still require traversal to establish completeness. Normal call dispatch allocates no diagnostic record with
|
|
@@ -318,14 +358,14 @@ observation disabled. Graph frames remain bounded by the existing ring capacity.
|
|
|
318
358
|
(D285):
|
|
319
359
|
|
|
320
360
|
- `renderSlot(target, { contribution, models, props })` mounts the published target or one fixture contribution;
|
|
321
|
-
- `command(run)` produces an authentic `
|
|
322
|
-
- `
|
|
361
|
+
- `command(run)` produces an authentic `Command` for a model fixture, plus the contribution binding a mount reads;
|
|
362
|
+
- `runCommand(target, input?, options?)` executes an existing authentic `Command` without mounting React (D261);
|
|
323
363
|
- `testReadable(initial)`, `testResourceEpoch()`, `openModel`, `settled`, `waitFor`, `yieldTurn` and `eventually`
|
|
324
364
|
come from the entries below, for the part of a test that has no UI in it — `testResourceEpoch()` is what a
|
|
325
365
|
component fixture writing a `ResourceState` by hand puts in its `epoch` (D362).
|
|
326
366
|
|
|
327
367
|
A test that renders nothing should import `@opetope/runtime/testing` directly: `renderSlot` and the binding half of
|
|
328
|
-
`command` are all that needs React here. The
|
|
368
|
+
`command` are all that needs React here. The Command a fixture mints is the `command` of `@opetope/core/testing`
|
|
329
369
|
(D300), and this word is the one name of the re-export chain that does not pass through: the React `command`
|
|
330
370
|
shadows it with the stronger one.
|
|
331
371
|
|
|
@@ -334,21 +374,21 @@ application as contributions (D85). It is not a test spelling of `<Slot>` — it
|
|
|
334
374
|
`reporter`, because a component test has no feature to report to — and the packaged consumer of `ci:pack` mounts it
|
|
335
375
|
from the real archive (D301).
|
|
336
376
|
|
|
337
|
-
A test that previously mounted `useCommand` only to invoke a
|
|
338
|
-
|
|
377
|
+
A test that previously mounted `useCommand` only to invoke a Command can use `runCommand` directly. The result is the
|
|
378
|
+
Command's output, while a UI test still checks the `CommandOutcome` returned by the mounted consumer:
|
|
339
379
|
|
|
340
380
|
```ts
|
|
341
381
|
import assert from 'node:assert/strict';
|
|
342
382
|
import { defineModel } from '@opetope/core';
|
|
343
|
-
import type {
|
|
383
|
+
import type { Command, ModelCommandContext } from '@opetope/core';
|
|
344
384
|
import { defineFeature, openFeature } from '@opetope/runtime';
|
|
345
|
-
import {
|
|
385
|
+
import { runCommand } from '@opetope/react/testing';
|
|
346
386
|
|
|
347
|
-
const Arithmetic = defineModel<{ readonly double:
|
|
387
|
+
const Arithmetic = defineModel<{ readonly double: Command<number, number> }>('example.testing.model');
|
|
348
388
|
const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
349
389
|
own: ({ model }) => ({
|
|
350
390
|
arithmetic: model(Arithmetic, context => ({
|
|
351
|
-
double: context.
|
|
391
|
+
double: context.command(({ input }: ModelCommandContext<number>) => input * 2),
|
|
352
392
|
})),
|
|
353
393
|
}),
|
|
354
394
|
exports: ({ own }) => ({ double: own.arithmetic.double }),
|
|
@@ -356,35 +396,35 @@ const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
|
356
396
|
const instance = openFeature(arithmetic, { imports: {}, reporter: () => undefined });
|
|
357
397
|
try {
|
|
358
398
|
const ready = await instance.ready;
|
|
359
|
-
const result = await
|
|
399
|
+
const result = await runCommand(ready.exports.double, 3);
|
|
360
400
|
assert.equal(result, 6);
|
|
361
401
|
} finally {
|
|
362
402
|
await instance.close();
|
|
363
403
|
}
|
|
364
404
|
```
|
|
365
405
|
|
|
366
|
-
`
|
|
367
|
-
UI status or outcome wrapper, scheduling policy, model creation or cleanup lifetime. The existing
|
|
406
|
+
`runCommand` returns `Promise<Output>` and preserves the Command's original errors and cancellation rejections. It adds no
|
|
407
|
+
UI status or outcome wrapper, scheduling policy, model creation or cleanup lifetime. The existing Command retains its
|
|
368
408
|
owner, lane and retirement rules; the test closes the instance it opened, including after failed readiness.
|
|
369
|
-
A void
|
|
370
|
-
`
|
|
371
|
-
`
|
|
409
|
+
A void Command can use `runCommand(target)`. Options always occupy the third argument:
|
|
410
|
+
`runCommand(target, input, { signal })`, or `runCommand(voidTarget, undefined, { signal })`.
|
|
411
|
+
`RunCommandOptions` contains only the optional `signal`; a pre-aborted signal rejects before the body runs.
|
|
372
412
|
|
|
373
|
-
`run`, `
|
|
413
|
+
`run`, `runCommand` and nested `invoke` use the same no-input rule (D273): `void` and `undefined` allow omission;
|
|
374
414
|
other inputs, including `T | undefined`, require an argument. `never` cannot be invoked without input.
|
|
375
415
|
Generic helpers may forward the target and its explicit input without losing their types.
|
|
376
416
|
|
|
377
417
|
## Word map and entries
|
|
378
418
|
|
|
379
|
-
| Entry | What it holds
|
|
380
|
-
| ---------------------------- |
|
|
381
|
-
| `@opetope/react` | `useModel`, `useCommand`, `
|
|
382
|
-
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` for the integration layer; types `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease`
|
|
383
|
-
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` and fixture/scenario types, plus everything `@opetope/runtime/testing` and `@opetope/core/testing` publish; tests only
|
|
419
|
+
| Entry | What it holds |
|
|
420
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
421
|
+
| `@opetope/react` | `useModel`, `useCommand`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; types `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`, `PaginatedResourceHook`, `ResourcePaginationHook` |
|
|
422
|
+
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` for the integration layer; types `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
|
|
423
|
+
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` and fixture/scenario types, plus everything `@opetope/runtime/testing` and `@opetope/core/testing` publish; tests only |
|
|
384
424
|
|
|
385
|
-
`
|
|
425
|
+
`Command<Input, Output>` is imported from `@opetope/core`; UI `CommandOutcome<Output>` is imported from `@opetope/react`. The former `Command` synonym and the `CommandOutcome` re-export from `@opetope/react/integration` are removed (D270).
|
|
386
426
|
|
|
387
|
-
`Model` is a typed key for a record of `Readable`, `
|
|
427
|
+
`Model` is a typed key for a record of `Readable`, `Command` and readable factories that UI components consume.
|
|
388
428
|
The constructors of the integration entry are not re-exported from the safe entry and cannot come back into
|
|
389
429
|
`ui/models/data/contracts` through a local barrel of a feature.
|
|
390
430
|
|
|
@@ -394,7 +434,7 @@ The constructors of the integration entry are not re-exported from the safe entr
|
|
|
394
434
|
- one committed contribution owns its model frame until unmount or retire;
|
|
395
435
|
- an abandoned concurrent render holds no frame;
|
|
396
436
|
- `useCommand` does not subscribe to the invoker: the observable state of a call lies in a `Readable` of the model;
|
|
397
|
-
- the `cancelled` outcome does not move `outcome`, and it is only the `
|
|
437
|
+
- the `cancelled` outcome does not move `outcome`, and it is only the `CommandError` codes `cancelled`
|
|
398
438
|
and `closed`; a call to a weak port with no provider (`unavailable`) and a rejected publication
|
|
399
439
|
(`publication-rejected`) arrive as the `failed` outcome and settle in `outcome` (D138, D292);
|
|
400
440
|
- a contribution that fails to render or to commit is contained by its own mount and reported to its feature;
|