dsh-plugin-shop 0.8.7 → 0.9.0-beta.2
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/lib/client.js +1561 -762
- package/lib/index.js +210 -34
- package/lib/typert.host.js +12 -12
- package/lib/typert.remote-client.js +11 -11
- package/lib/types/client/ShopDetailAction.d.ts +46 -0
- package/lib/types/client/ShopGlyph.d.ts +12 -0
- package/lib/types/client/ShopPage.d.ts +27 -0
- package/lib/types/client/ShopTab.d.ts +27 -10
- package/lib/types/client/flow-registry.d.ts +89 -0
- package/lib/types/client/index.d.ts +52 -7
- package/lib/types/client/listeners.d.ts +27 -0
- package/lib/types/client/locales.d.ts +17 -5
- package/lib/types/client/page-store.d.ts +255 -0
- package/lib/types/client/present.d.ts +35 -13
- package/lib/types/client/restart-monitor.d.ts +11 -0
- package/lib/types/client/useFlows.d.ts +34 -26
- package/lib/types/client/useUpdateSelf.d.ts +19 -16
- package/lib/types/host/harness.d.ts +26 -2
- package/lib/types/host/index.d.ts +39 -6
- package/lib/types/host/manager-runner.d.ts +24 -0
- package/lib/types/host/plugin-manager.d.ts +5 -0
- package/package.json +1 -1
- package/lib/types/client/ShopSection.d.ts +0 -10
- package/lib/types/client/useInstall.d.ts +0 -31
- package/lib/types/client/useUninstall.d.ts +0 -28
|
@@ -36,9 +36,9 @@ export declare function installPhaseKey(phase: 'downloading' | 'installing'): Sh
|
|
|
36
36
|
/** Why the shop cannot restart dsh, as a locale key.
|
|
37
37
|
*
|
|
38
38
|
* A sibling of `activationNoticeKey` and exhaustive for the same reason: the
|
|
39
|
-
* switch has no default, so a
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* switch has no default, so a reason added to `RestartBlockedReason` is a type
|
|
40
|
+
* error here rather than a card that silently keeps a neighbour's copy. That
|
|
41
|
+
* is the whole point of the type — the single boolean this replaced
|
|
42
42
|
* had ONE string for three causes, and it named systemd, so a Windows reader
|
|
43
43
|
* was told to restart a service they do not run and to set an override that
|
|
44
44
|
* could not have helped. */
|
|
@@ -146,10 +146,12 @@ export declare function refusesInstall(blockers: readonly {
|
|
|
146
146
|
kind: BlockerKind;
|
|
147
147
|
}[]): boolean;
|
|
148
148
|
/**
|
|
149
|
-
* The refused
|
|
150
|
-
* the package declared, in the order dsh reported them. A
|
|
151
|
-
*
|
|
152
|
-
* it
|
|
149
|
+
* The refused requirements as the refusal's copy names them: each one and the
|
|
150
|
+
* range the package declared, in the order dsh reported them. A peer is named
|
|
151
|
+
* by its package. `engines.dsh` is a manifest path, not a name anyone asked
|
|
152
|
+
* for, so it reads as `dsh` and its range, the way the sentence says "it
|
|
153
|
+
* requires dsh >=0.3.0". A blank range — which dsh refuses — is quoted, or the
|
|
154
|
+
* sentence would name the requirement and nothing it asked for.
|
|
153
155
|
*/
|
|
154
156
|
export declare function refusedPeersText(refused: Readonly<Record<string, string>>): string;
|
|
155
157
|
/**
|
|
@@ -233,8 +235,9 @@ export interface InstallStatusShape {
|
|
|
233
235
|
/** The install view state machine (§7.2). `phase` distinguishes the download
|
|
234
236
|
* that runs ahead of the per-profile mutex from the install that holds it.
|
|
235
237
|
* It rides the `running` kind rather than adding a kind of its own: every
|
|
236
|
-
* `view.kind === 'running'` site —
|
|
237
|
-
* them — must keep treating a downloading install
|
|
238
|
+
* `view.kind === 'running'` site — the flow registry's poll collection
|
|
239
|
+
* (flow-registry.ts) among them — must keep treating a downloading install
|
|
240
|
+
* as live. */
|
|
238
241
|
export type InstallView = {
|
|
239
242
|
kind: 'idle';
|
|
240
243
|
} | {
|
|
@@ -328,12 +331,23 @@ export declare function restartMonitorVerdict(input: {
|
|
|
328
331
|
* a host older than the field, the one that matters most.
|
|
329
332
|
*/
|
|
330
333
|
export declare function answeredByAnotherProcess(previous: ShopVersionResult, next: ShopVersionResult): boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Whether an update of the shop has landed and waits for a restart: the
|
|
336
|
+
* profile holds a version other than the one running. A host older than the
|
|
337
|
+
* field sends none, and that reads as waiting: a client newer than its host
|
|
338
|
+
* exists only because the disk holds a newer shop than the process running
|
|
339
|
+
* (design §7.3, 2026-09-27 amendment). The tab's restart offer and the
|
|
340
|
+
* hand-over after a hot swap (design 2026-10-16-shop-sidebar-page §6) read
|
|
341
|
+
* it alike.
|
|
342
|
+
*/
|
|
343
|
+
export declare function updateAwaitsRestart(version: ShopVersionResult): boolean;
|
|
331
344
|
/**
|
|
332
345
|
* Whether dsh restarting under the page settles this receipt: a change that
|
|
333
|
-
* landed and was waiting for exactly that. The
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* running settles by
|
|
346
|
+
* landed and was waiting for exactly that. The page store clears these
|
|
347
|
+
* (page-store.ts, `noteRestarted`) once the answers come from another
|
|
348
|
+
* process, and nothing else — a reload is still owed by a page nobody
|
|
349
|
+
* reloaded, a failure still happened, and a flow still running settles by
|
|
350
|
+
* its own poll.
|
|
337
351
|
*/
|
|
338
352
|
export declare function settledByRestart(view: InstallView): boolean;
|
|
339
353
|
/** How long the check-update button reports "up to date" after a re-check
|
|
@@ -456,6 +470,14 @@ export declare function npmPageUrl(entry: CatalogEntry): string | null;
|
|
|
456
470
|
* This mirrors the registry's identity verbatim; the two must not drift.
|
|
457
471
|
*/
|
|
458
472
|
export declare function entryKey(entry: CatalogEntry): string;
|
|
473
|
+
/**
|
|
474
|
+
* Whether the shelf lists `entry`: a name the catalog's `notAShop` clears,
|
|
475
|
+
* or one whose name and repository `isShopLike` leaves alone. One rule for
|
|
476
|
+
* the shelf and for the Plugins page's "View in Plugin shop" (design
|
|
477
|
+
* 2026-10-16-shop-sidebar-page §7), so that button never sends a reader to a
|
|
478
|
+
* shelf that hides the plugin.
|
|
479
|
+
*/
|
|
480
|
+
export declare function listedOnShelf(entry: CatalogEntry, cleared: ReadonlySet<string>): boolean;
|
|
459
481
|
/**
|
|
460
482
|
* One entry's star count from the sidecar, or undefined when it has none.
|
|
461
483
|
*
|
|
@@ -72,6 +72,17 @@ export type RestartRequestOutcome = {
|
|
|
72
72
|
} | {
|
|
73
73
|
kind: 'unreachable';
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Whether a shop instance on this page has registered its restart path:
|
|
77
|
+
* every `apply` since 0.8.4 does so before anything can ask for a restart.
|
|
78
|
+
* Read before this instance's own `apply` registers one, a store here names
|
|
79
|
+
* a predecessor — a 0.8.x shop when no page store sits beside it
|
|
80
|
+
* (page-store.ts `readPredecessor`; design 2026-10-16-shop-sidebar-page §6).
|
|
81
|
+
* The store outlives a carrier's withdrawal: a predecessor's teardown
|
|
82
|
+
* withdraws its carrier and leaves the store, and the store is what names it.
|
|
83
|
+
* Only a read: unlike `monitorStore`, it never creates the store.
|
|
84
|
+
*/
|
|
85
|
+
export declare function hasRestartMonitorStore(): boolean;
|
|
75
86
|
/** The page's restart, or null when it has committed none. The same object
|
|
76
87
|
* until the state changes, as `useSyncExternalStore` requires. */
|
|
77
88
|
export declare function readRestartMonitor(): RestartMonitorState | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* React's view of a keyed registry of install-shaped flows (flow-registry.ts):
|
|
3
|
+
* start, poll to terminal, reset.
|
|
3
4
|
*
|
|
4
5
|
* Install and uninstall are the same machine. Both fold a
|
|
5
6
|
* `ShopInstallStatusResult` through `reduceInstall` into an `InstallView`,
|
|
@@ -12,16 +13,22 @@
|
|
|
12
13
|
* install refusal is a `rejected` view carrying the gate's code; an uninstall
|
|
13
14
|
* refusal is a `failed` view carrying the host's published detail, because
|
|
14
15
|
* `ShopUninstallResult`'s failure variant has no code to populate and those
|
|
15
|
-
* codes belong to the install gate. That difference is the
|
|
16
|
-
* and it is the whole of it.
|
|
16
|
+
* codes belong to the install gate. That difference is the registry's
|
|
17
|
+
* `begin`, and it is the whole of it.
|
|
17
18
|
*
|
|
18
19
|
* The two used to be hand-synced copies — 71 of 84 lines identical. The cost
|
|
19
20
|
* was not theoretical: a single review round had to make the same edit twice
|
|
20
21
|
* (the settle signature, and its call), and the `pending` set below was added
|
|
21
22
|
* to one of them and not the other before this module existed.
|
|
23
|
+
*
|
|
24
|
+
* Since 2026-10-16 the registry is its own module, framework-free, and the
|
|
25
|
+
* polling belongs to it: a component's effect stopped the moment the shop left
|
|
26
|
+
* the screen (design 2026-10-16-shop-sidebar-page §5). This module only
|
|
27
|
+
* renders one, through `useRegistryFlows`.
|
|
22
28
|
*/
|
|
23
|
-
import type {
|
|
24
|
-
import { type
|
|
29
|
+
import type { InstallArgs } from '../host/index.ts';
|
|
30
|
+
import { type FlowRegistry } from './flow-registry.ts';
|
|
31
|
+
import type { InstallView } from './present.ts';
|
|
25
32
|
/** One identity's flow, as the tab hands it to a panel. */
|
|
26
33
|
export interface KeyedFlow<TArgs> {
|
|
27
34
|
view: InstallView;
|
|
@@ -39,16 +46,13 @@ export interface UseKeyedFlows<TArgs> {
|
|
|
39
46
|
* moves. The accessor's identity still tracks `views`; the result's does
|
|
40
47
|
* not, and the result is what a card should be given. */
|
|
41
48
|
flowFor: (key: string) => KeyedFlow<TArgs>;
|
|
42
|
-
/** Return one identity to `idle` without building a flow for it. Stable
|
|
43
|
-
* across renders (unlike `flowFor`, whose identity tracks `views`), so one
|
|
44
|
-
* registry's settle callback can supersede the other's receipt for the same
|
|
45
|
-
* key without taking a dependency on that registry's output. */
|
|
46
|
-
resetFlow: (key: string) => void;
|
|
47
49
|
/** Return every identity whose view `predicate` selects to `idle`, and
|
|
48
|
-
* nothing else. Stable across renders
|
|
50
|
+
* nothing else. Stable across renders, unlike `flowFor`; a call selecting
|
|
49
51
|
* nothing leaves the registry — and so every flow and `pending` — exactly
|
|
50
|
-
* as it was. The
|
|
51
|
-
*
|
|
52
|
+
* as it was. The receipts dsh restarting under the page settled are
|
|
53
|
+
* cleared the same way at the store, through each registry's own
|
|
54
|
+
* `resetWhere` (page-store.ts `noteRestarted`; design §8, 2026-10-06
|
|
55
|
+
* amendment). */
|
|
52
56
|
resetWhere: (predicate: (view: InstallView) => boolean) => void;
|
|
53
57
|
/**
|
|
54
58
|
* The identities whose flow is not `idle` — asked by the shelf, which has
|
|
@@ -65,16 +69,20 @@ export interface UseKeyedFlows<TArgs> {
|
|
|
65
69
|
*/
|
|
66
70
|
pending: ReadonlySet<string>;
|
|
67
71
|
}
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*/
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
}
|
|
72
|
+
/** One entry's install flow, as the tab hands it to a panel: the page
|
|
73
|
+
* store's install registry (page-store.ts), begun through flow-registry.ts's
|
|
74
|
+
* `beginInstall`. */
|
|
75
|
+
export type InstallFlow = KeyedFlow<InstallArgs>;
|
|
76
|
+
/** One entry's uninstall flow, as the tab hands it to a panel: the page
|
|
77
|
+
* store's uninstall registry, begun through `beginUninstall`. */
|
|
78
|
+
export type UninstallFlow = KeyedFlow<{
|
|
79
|
+
name: string;
|
|
80
|
+
}>;
|
|
81
|
+
/** The uninstall flows as the tab hands them to the shadowed rows. */
|
|
82
|
+
export type UseUninstallFlows = UseKeyedFlows<{
|
|
83
|
+
name: string;
|
|
84
|
+
}>;
|
|
85
|
+
/** The keyed-flow face over any registry: one of the page store's, which
|
|
86
|
+
* outlives the component, or one of the store a tab builds for itself when
|
|
87
|
+
* none is handed over (ShopTab). */
|
|
88
|
+
export declare function useRegistryFlows<TArgs>(registry: FlowRegistry<TArgs>): UseKeyedFlows<TArgs>;
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
import {
|
|
1
|
+
/** The shop's own update as the tab renders it: the face over the registry
|
|
2
|
+
* that holds it, a page store's `self` (page-store.ts). It shares the install
|
|
3
|
+
* view machine and the registry's polling; its start mapping is
|
|
4
|
+
* flow-registry.ts's `beginUpdate`, which shares the uninstall's refusal
|
|
5
|
+
* rule — a business refusal (a non-semver version) lands in the `failed`
|
|
6
|
+
* view with the host's detail, never in the install `rejected` codes. */
|
|
7
|
+
import type { FlowRegistry } from './flow-registry.ts';
|
|
8
|
+
import type { InstallView } from './present.ts';
|
|
8
9
|
export interface UseUpdateSelfResult {
|
|
9
10
|
view: InstallView;
|
|
10
11
|
start: (args: {
|
|
11
12
|
version: string;
|
|
12
13
|
}) => Promise<void>;
|
|
13
14
|
/** Return the view to `idle` when `predicate` selects it; stable across
|
|
14
|
-
* renders. The
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* renders. The failed panel's Dismiss uses it, so the version row decides
|
|
16
|
+
* again; an update waiting for a restart is settled at the store instead,
|
|
17
|
+
* when dsh restarts under the page (page-store.ts `noteRestarted`; design
|
|
18
|
+
* §8, 2026-10-06 amendment). */
|
|
17
19
|
resetWhere: (predicate: (view: InstallView) => boolean) => void;
|
|
18
20
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
/** The one key the self-update registry holds: the shop has one update. */
|
|
22
|
+
export declare const SELF_UPDATE_KEY = "self";
|
|
23
|
+
/** The self-update face over a registry: the keyed-flow face
|
|
24
|
+
* (`useRegistryFlows`) at its one key, so the subscription and the flow's
|
|
25
|
+
* identity are that hook's and nothing here can drift from it. */
|
|
26
|
+
export declare function useRegistrySelf(registry: FlowRegistry<{
|
|
22
27
|
version: string;
|
|
23
|
-
})
|
|
24
|
-
installId: string;
|
|
25
|
-
}) => Promise<ShopInstallStatusResult>): UseUpdateSelfResult;
|
|
28
|
+
}>): UseUpdateSelfResult;
|
|
@@ -28,13 +28,19 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { type ProfileTemplates } from './compatibility.ts';
|
|
30
30
|
/** One package's harness peers as the running dsh judges them: app-boot's
|
|
31
|
-
* `evaluatePluginCompatibility` result, the shape 0.1.7 returns
|
|
31
|
+
* `evaluatePluginCompatibility` result, in the shape 0.1.7 returns, whichever
|
|
32
|
+
* name the running dsh gives its list (`peerCheckOf`). */
|
|
32
33
|
export interface PeerIssue {
|
|
33
34
|
name: string;
|
|
34
35
|
version: string;
|
|
35
36
|
/** The dsh version the check judged against — app-boot's own. */
|
|
36
37
|
runtimeVersion: string;
|
|
37
|
-
/**
|
|
38
|
+
/** What the package asked of dsh and the running one does not meet, each
|
|
39
|
+
* with the range the package declared. A key is a dsh peer's package name,
|
|
40
|
+
* or the literal `engines.dsh`. dsh names this list `requirements` from
|
|
41
|
+
* 0.2.1 and `peers` before it (design 2026-10-19-dsh-021-readiness, section
|
|
42
|
+
* 4); `refusedRequirements` reads either into this field, so every consumer
|
|
43
|
+
* keeps reading the one name. */
|
|
38
44
|
peers: Record<string, string>;
|
|
39
45
|
/** Whether the profile holds dsh's exact-version exemption for this
|
|
40
46
|
* package on this dsh, in which case dsh accepts the install anyway. */
|
|
@@ -84,6 +90,24 @@ export interface RunningHarness {
|
|
|
84
90
|
* not be read — no verdict either way. */
|
|
85
91
|
peerCheck: PeerCheck | null;
|
|
86
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* The list a refusal names, read off one answer of dsh's compatibility check
|
|
95
|
+
* or of its plugin manager, under whichever name the dsh that gave the answer
|
|
96
|
+
* uses: `requirements` from 0.2.1, `peers` from 0.1.7 to 0.2.0. app-boot's
|
|
97
|
+
* `evaluatePluginCompatibility` and dsh-plugin-manager's `IncompatiblePlugin`
|
|
98
|
+
* were renamed together (design 2026-10-19-dsh-021-readiness, section 4, read
|
|
99
|
+
* off 0.2.1-rc.2's own types and code on 2026-10-19). A key is a dsh peer's
|
|
100
|
+
* package name or the literal `engines.dsh`; a value is the range the package
|
|
101
|
+
* declared.
|
|
102
|
+
*
|
|
103
|
+
* `requirements` is read first, and only when it holds an object: an answer
|
|
104
|
+
* whose `requirements` is anything else is not in the newer shape, so its
|
|
105
|
+
* `peers` is tried. Only the entries whose range is a string are kept, as
|
|
106
|
+
* `incompatibleList` always kept them, so one malformed range costs its own
|
|
107
|
+
* entry and not the rest of the list. An answer naming neither reads as an
|
|
108
|
+
* empty list, never as a throw.
|
|
109
|
+
*/
|
|
110
|
+
export declare function refusedRequirements(answer: Readonly<Record<string, unknown>>): Record<string, string>;
|
|
87
111
|
/**
|
|
88
112
|
* The harness this process runs, identified from `script` — the gateway's
|
|
89
113
|
* `restartScript`, `process.argv[1]` in production — or nothing, never a
|
|
@@ -211,7 +211,7 @@ export type ShopRestartResult = RestartOutcome;
|
|
|
211
211
|
* restart was impossible after it had become possible. Static here, dynamic
|
|
212
212
|
* there; the split is the whole reason this is a separate predicate rather
|
|
213
213
|
* than a cache of `restart()`'s answer. */
|
|
214
|
-
export type RestartBlockedReason = 'desktop' | 'windows' | 'systemd' | 'port-zero';
|
|
214
|
+
export type RestartBlockedReason = 'desktop' | 'from-default-profile' | 'windows' | 'systemd' | 'port-zero';
|
|
215
215
|
/** `shop/version` result (§7.3): the RUNNING shop version (from the shipped
|
|
216
216
|
* package.json, not the manifest's range), the npm latest when the check
|
|
217
217
|
* could answer (`null` = no answer — advisory, never an error), and the
|
|
@@ -618,14 +618,32 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
618
618
|
*
|
|
619
619
|
* The order is load-bearing for the copy a reader sees. The desktop
|
|
620
620
|
* profile first: dsh itself refuses to launch it, so no platform,
|
|
621
|
-
* supervisor or port could make a restart possible there
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
621
|
+
* supervisor or port could make a restart possible there, and the remedy
|
|
622
|
+
* is the app. Then `from-default-profile` and Windows, both ahead of
|
|
623
|
+
* systemd, because neither has an override and reporting the systemd one
|
|
624
|
+
* there sends the reader to set `allowRestart: true`, which this gate would
|
|
625
|
+
* still refuse. And `from-default-profile` ahead of Windows, because every
|
|
626
|
+
* remedy after `desktop` is "restart dsh manually", which a reader launched
|
|
627
|
+
* with the flag would do with the flag, and on 0.2.1 that recreates the
|
|
628
|
+
* profile: its copy is the one that says "without that flag", and a
|
|
629
|
+
* Windows, systemd or `--port 0` reader needs it as much as any other.
|
|
625
630
|
*
|
|
626
631
|
* - `desktop`: dsh's CLI refuses `--profile desktop` ("managed exclusively
|
|
627
632
|
* by the Electron application"), so the relaunch would exit at once and
|
|
628
633
|
* leave nothing on the port; the app restarts it.
|
|
634
|
+
* - `from-default-profile`: from dsh 0.2.1, `--from-default-profile <name>`
|
|
635
|
+
* replaces an existing custom profile with a fresh copy of the shipped
|
|
636
|
+
* template on every launch, and the relaunch re-runs this argv verbatim,
|
|
637
|
+
* so it would delete every plugin in the profile, this shop included.
|
|
638
|
+
* Measured 2026-10-19 in throwaway homes: 0.2.1-rc.2 booted a relaunched
|
|
639
|
+
* profile that had held the shop with no dependencies, while 0.2.0-rc.2
|
|
640
|
+
* refused to boot an existing profile at all, so there the restarted dsh
|
|
641
|
+
* failed to come back. Both spellings are the flag:
|
|
642
|
+
* `--from-default-profile <name>` and `--from-default-profile=<name>`. It
|
|
643
|
+
* is left in the argv rather than stripped: the restarted process would no
|
|
644
|
+
* longer run the command the reader chose, and their next launch with the
|
|
645
|
+
* flag recreates the profile anyway. No override (design
|
|
646
|
+
* 2026-10-19-dsh-021-readiness §3).
|
|
629
647
|
* - `windows`: the handoff helper is a POSIX shell one-liner (restart.ts)
|
|
630
648
|
* and there is no `sh` on Windows. That spawn fails ASYNCHRONOUSLY, so
|
|
631
649
|
* committing would answer `ok: true`, exit this process, and leave nothing
|
|
@@ -633,7 +651,7 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
633
651
|
* - `systemd`: under a unit the two-phase handoff kills itself, because the
|
|
634
652
|
* main process exiting also kills the unit's cgroup and takes the detached
|
|
635
653
|
* helper with it; the service never comes back. Overridable, and the only
|
|
636
|
-
* one
|
|
654
|
+
* one that is.
|
|
637
655
|
* - `port-zero`: the OS hands the NEW process a fresh port the browser
|
|
638
656
|
* cannot know, so a restart would strand the client on a dead origin. */
|
|
639
657
|
private staticRestartBlock;
|
|
@@ -668,6 +686,15 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
668
686
|
* passing request reaches the executor.
|
|
669
687
|
*/
|
|
670
688
|
install(args: InstallArgs): Promise<ShopInstallResult>;
|
|
689
|
+
/**
|
|
690
|
+
* One install of `name` through dsh's CLI, `dsh plugin --profile <p> add
|
|
691
|
+
* <spec>` (§7.2): the path every install takes when dsh offers no plugin
|
|
692
|
+
* manager, and the one that continues an update the manager refuses in
|
|
693
|
+
* place (design 2026-10-19-dsh-021-readiness §2). `alreadyImported` is
|
|
694
|
+
* whether this process may already hold the package's module, which rules
|
|
695
|
+
* out a hot mount. The caller tracks the record and records any pin, once.
|
|
696
|
+
*/
|
|
697
|
+
private startCliInstall;
|
|
671
698
|
/**
|
|
672
699
|
* What would stop the profile from starting now that `name`'s files are on
|
|
673
700
|
* disk, or null: an entry id another installed package already declares,
|
|
@@ -790,5 +817,11 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
790
817
|
updateStart(args: {
|
|
791
818
|
version: string;
|
|
792
819
|
}): Promise<ShopUpdateResult>;
|
|
820
|
+
/** The shop's own update through dsh's CLI: the path when dsh offers no
|
|
821
|
+
* plugin manager, and the one that continues a self-update the manager
|
|
822
|
+
* refuses in place (design 2026-10-19-dsh-021-readiness §2). It passes no
|
|
823
|
+
* `afterDone`, so it reports a restart: a host half cannot swap itself live
|
|
824
|
+
* (§8). The caller tracks the record. */
|
|
825
|
+
private startCliSelfUpdate;
|
|
793
826
|
}
|
|
794
827
|
export default ShopGateway;
|
|
@@ -49,3 +49,27 @@ export interface ManagerOperationOptions {
|
|
|
49
49
|
* can throw, and a null-prototype object has no conversion to a string. */
|
|
50
50
|
export declare const messageOf: (error: unknown) => string;
|
|
51
51
|
export declare function startManagerOperation(options: ManagerOperationOptions): RunningInstall;
|
|
52
|
+
/**
|
|
53
|
+
* `first`, continued by the record `next` starts when `first` ends refused,
|
|
54
|
+
* as one record under `first`'s id: design 2026-10-19-dsh-021-readiness,
|
|
55
|
+
* section 2. dsh-plugin-manager 0.2.1-rc.2 refuses an update in place as
|
|
56
|
+
* `already-installed`, and the gateway continues such an update through
|
|
57
|
+
* dsh's CLI. One record, so the client keeps polling the id it was given,
|
|
58
|
+
* and the restart gate and eviction, which read every tracked record, see
|
|
59
|
+
* the command that runs.
|
|
60
|
+
*
|
|
61
|
+
* While `first` runs, this is `first`. Whether it continues is decided once,
|
|
62
|
+
* when `first` ends: the first `status()` read or `first.finished` settling
|
|
63
|
+
* that sees `first` terminal asks `refused`, and that answer is kept. So a
|
|
64
|
+
* caller may set its flag while reading the answer that ends `first`, and a
|
|
65
|
+
* flag flipped after that changes neither `status()` nor `finished`, which
|
|
66
|
+
* answer from that one decision. Not refused, this stays `first`. Refused,
|
|
67
|
+
* `next` runs, exactly once, at that same read or finish, so no read between
|
|
68
|
+
* the refusal and the next command reports the refusal. From then on the
|
|
69
|
+
* record is `next`'s, its state, detail and activation, with `first`'s log,
|
|
70
|
+
* then `bridge`, then `next`'s log. That log joins two bounded logs and the
|
|
71
|
+
* bridge, so a continued record can carry about twice `MAX_LOG_LINES` and
|
|
72
|
+
* `MAX_LOG_BYTES`, where one record of `startManagerOperation` carries at most
|
|
73
|
+
* one line over them.
|
|
74
|
+
*/
|
|
75
|
+
export declare function continueAfter(first: RunningInstall, refused: () => boolean, next: () => RunningInstall, bridge: string): RunningInstall;
|
|
@@ -24,6 +24,11 @@ export interface PluginManagerLike {
|
|
|
24
24
|
* All or nothing: half a service would take an install and fail its
|
|
25
25
|
* uninstall. */
|
|
26
26
|
export declare function asPluginManager(service: unknown): PluginManagerLike | null;
|
|
27
|
+
/** One entry of dsh's `incompatible` list. `peers` is what the package
|
|
28
|
+
* declared and the running dsh does not meet, read under whichever name that
|
|
29
|
+
* dsh gives it, `requirements` from 0.2.1 and `peers` before
|
|
30
|
+
* (`refusedRequirements`): a key is a dsh peer's package name or
|
|
31
|
+
* `engines.dsh`. */
|
|
27
32
|
export interface ManagerIncompatible {
|
|
28
33
|
name: string;
|
|
29
34
|
version: string;
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shop's Settings section (design §5.1, amended 2026-10-15): its own
|
|
3
|
-
* entry in the Settings nav, directly after Built-in plugins, where it used
|
|
4
|
-
* to be a tab inside that section. The page carries the title and the one
|
|
5
|
-
* intro line the built-in Models and Built-in plugins pages carry, above the
|
|
6
|
-
* shop body, which is `ShopTab` unchanged.
|
|
7
|
-
*/
|
|
8
|
-
import type { ReactNode } from 'react';
|
|
9
|
-
import { type ShopTabProps } from './ShopTab.tsx';
|
|
10
|
-
export declare function ShopSection(props: ShopTabProps): ReactNode;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/** Install driving hooks: the shared poll loop, and the tab's keyed registry. */
|
|
2
|
-
import { type Dispatch, type SetStateAction } from 'react';
|
|
3
|
-
import { type InstallView } from './present.ts';
|
|
4
|
-
import { type KeyedFlow, type UseKeyedFlows } from './useFlows.ts';
|
|
5
|
-
import type { InstallArgs, ShopInstallResult, ShopInstallStatusResult } from '../host/index.ts';
|
|
6
|
-
/** The single-view poll loop: while the view is `running`, poll once per
|
|
7
|
-
* second and fold each status through the reducer. A poll failure is
|
|
8
|
-
* transient — the host retains the record, so the next tick finds it. The
|
|
9
|
-
* rejection handler must be present: an unhandled rejection here would
|
|
10
|
-
* escape the poll loop.
|
|
11
|
-
*
|
|
12
|
-
* Kept for the self-update (`useUpdateSelf`), which drives ONE flow with no
|
|
13
|
-
* identity to key it by. Everything per-plugin goes through `useKeyedFlows`,
|
|
14
|
-
* whose own loop polls every running identity from one interval. */
|
|
15
|
-
export declare function usePollStatus(view: InstallView, setView: Dispatch<SetStateAction<InstallView>>, installStatus: (args: {
|
|
16
|
-
installId: string;
|
|
17
|
-
}) => Promise<ShopInstallStatusResult>): void;
|
|
18
|
-
/** One entry's install flow, as the tab hands it to a panel. */
|
|
19
|
-
export type InstallFlow = KeyedFlow<InstallArgs>;
|
|
20
|
-
export type UseInstallFlows = UseKeyedFlows<InstallArgs>;
|
|
21
|
-
/**
|
|
22
|
-
* Install flows owned by the tab and keyed by install identity. Keeping the
|
|
23
|
-
* state above individual cards preserves a running operation when filtering
|
|
24
|
-
* unmounts its card, and makes the shelf and Outdated panels agree.
|
|
25
|
-
*
|
|
26
|
-
* The registry itself is `useKeyedFlows`; this supplies the one thing that is
|
|
27
|
-
* install-specific — what the starting RPC's answer means.
|
|
28
|
-
*/
|
|
29
|
-
export declare function useInstallFlows(install: (args: InstallArgs) => Promise<ShopInstallResult>, installStatus: (args: {
|
|
30
|
-
installId: string;
|
|
31
|
-
}) => Promise<ShopInstallStatusResult>, onSettled?: (key: string, outcome: 'done' | 'failed') => void): UseInstallFlows;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/** Uninstall driving hook: the tab's keyed registry, one start mapping. */
|
|
2
|
-
import type { ShopInstallStatusResult, ShopUninstallResult } from '../host/index.ts';
|
|
3
|
-
import { type KeyedFlow, type UseKeyedFlows } from './useFlows.ts';
|
|
4
|
-
/** One entry's uninstall flow, as the tab hands it to a panel. */
|
|
5
|
-
export type UninstallFlow = KeyedFlow<{
|
|
6
|
-
name: string;
|
|
7
|
-
}>;
|
|
8
|
-
export type UseUninstallFlows = UseKeyedFlows<{
|
|
9
|
-
name: string;
|
|
10
|
-
}>;
|
|
11
|
-
/**
|
|
12
|
-
* Uninstall flows owned by the tab and keyed by install identity. Lifting the
|
|
13
|
-
* state above individual cards is what lets a completed uninstall's outcome
|
|
14
|
-
* survive the installed-projection refresh that follows it — the row backing
|
|
15
|
-
* the card disappears, but the flow keyed by identity does not.
|
|
16
|
-
*
|
|
17
|
-
* The registry itself is `useKeyedFlows`; this supplies the one thing that is
|
|
18
|
-
* uninstall-specific — what the starting RPC's answer means. An uninstall
|
|
19
|
-
* business failure (not in the catalog / not installed) lands in the `failed`
|
|
20
|
-
* view with the host's published detail, never in the install `rejected`
|
|
21
|
-
* codes: those belong to the install gate, and `ShopUninstallResult`'s
|
|
22
|
-
* failure variant has no `code` field to populate.
|
|
23
|
-
*/
|
|
24
|
-
export declare function useUninstallFlows(uninstall: (args: {
|
|
25
|
-
name: string;
|
|
26
|
-
}) => Promise<ShopUninstallResult>, installStatus: (args: {
|
|
27
|
-
installId: string;
|
|
28
|
-
}) => Promise<ShopInstallStatusResult>, onSettled?: (key: string, outcome: 'done' | 'failed') => void): UseUninstallFlows;
|