@econ-v1/rpc 7.0.51 → 7.0.52
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/dist/client-services-contract.d.ts +25 -0
- package/dist/client-services-contract.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/message-port-client.d.ts +2 -1
- package/dist/message-port-client.d.ts.map +1 -1
- package/dist/system-notice-contract.d.ts +17 -0
- package/dist/system-notice-contract.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/client-services-contract.js +24 -0
- package/src/index.js +4 -0
- package/src/message-port-client.js +99 -18
- package/src/system-notice-contract.js +14 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's own retry edge for a failed kernel startup.
|
|
3
|
+
*
|
|
4
|
+
* Served OUTSIDE the kernel's readiness gates (see `system/pwa/src/kernel-runtime.js`), because
|
|
5
|
+
* it exists for exactly the state in which every gated query is being rejected: normal-services
|
|
6
|
+
* activation failed, so the shell has no registry, no route, and shows its startup-failure card.
|
|
7
|
+
* Re-running the gated reads — which is all the card's Retry used to do — rethrows the cached
|
|
8
|
+
* failure and never re-attempts activation, so the button could not recover anything.
|
|
9
|
+
*
|
|
10
|
+
* The command resolves only once the fresh activation has SETTLED, so its caller can restart the
|
|
11
|
+
* shell immediately afterwards and find the gates either open or honestly failed again.
|
|
12
|
+
*/
|
|
13
|
+
export const CLIENT_SERVICES_RETRY_COMMAND: "client-services.retry.v1";
|
|
14
|
+
/**
|
|
15
|
+
* `"ready"` means normal services are active and a shell restart will now get through. `"failed"`
|
|
16
|
+
* carries the underlying reason where the kernel has one — the same cause the startup card
|
|
17
|
+
* renders — so a repeat failure is legible rather than a silently identical dead screen.
|
|
18
|
+
*/
|
|
19
|
+
export type ClientServicesRetryResult = {
|
|
20
|
+
readonly status: "ready";
|
|
21
|
+
} | {
|
|
22
|
+
readonly cause?: string;
|
|
23
|
+
readonly status: "failed";
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=client-services-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-services-contract.d.ts","sourceRoot":"","sources":["../src/client-services-contract.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,4CAA6C,0BAA0B,CAAC;;;;;;wCAO3D;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;CAAE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { CLIENT_SERVICES_RETRY_COMMAND } from "./client-services-contract.js";
|
|
1
2
|
export { DEVICE_BOOT_STATE_QUERY } from "./device-boot-contract.js";
|
|
2
3
|
export { CLIENT_DEVICE_PENDING_QUERY } from "./client-device-pending-contract.js";
|
|
3
4
|
export { SYNC_CURSORS_QUERY } from "./sync-cursors-contract.js";
|
|
@@ -66,6 +67,7 @@ export type LifecyclePolicyView = import("@econ-v1/ports").LifecyclePolicyView;
|
|
|
66
67
|
export type LifecycleStormView = import("@econ-v1/ports").LifecycleStormView;
|
|
67
68
|
export type LifecycleTransitionView = import("@econ-v1/ports").LifecycleTransitionView;
|
|
68
69
|
export type ShellCacheResetPort = import("@econ-v1/ports").ShellCacheResetPort;
|
|
70
|
+
export type ShellRepairReport = import("@econ-v1/ports").ShellRepairReport;
|
|
69
71
|
export type ShellWorkerControlPort = import("@econ-v1/ports").ShellWorkerControlPort;
|
|
70
72
|
export type ShellVersionPort = import("@econ-v1/ports").ShellVersionPort;
|
|
71
73
|
export type ShellVersionReport = import("@econ-v1/ports").ShellVersionReport;
|
|
@@ -107,6 +109,7 @@ export type L402Enforcement = import("./l402-enforcement-contract.js").L402Enfor
|
|
|
107
109
|
export type L402EnforcementRow = import("./l402-enforcement-contract.js").L402EnforcementRow;
|
|
108
110
|
export type L402EnforcementSource = import("./l402-enforcement-contract.js").L402EnforcementSource;
|
|
109
111
|
export type L402EnforcementSetPayload = import("./l402-enforcement-contract.js").L402EnforcementSetPayload;
|
|
112
|
+
export type ClientServicesRetryResult = import("./client-services-contract.js").ClientServicesRetryResult;
|
|
110
113
|
export type FactoryResetPending = import("./factory-reset-contract.js").FactoryResetPending;
|
|
111
114
|
export type FactoryResetStatus = import("./factory-reset-contract.js").FactoryResetStatus;
|
|
112
115
|
export type FactoryResetStatusRow = import("./factory-reset-contract.js").FactoryResetStatusRow;
|
|
@@ -157,6 +160,7 @@ export type SystemNoticeListener = import("./system-notice-contract.js").SystemN
|
|
|
157
160
|
export type SystemNoticePatch = import("./system-notice-contract.js").SystemNoticePatch;
|
|
158
161
|
export type SystemNoticeSeverity = import("./system-notice-contract.js").SystemNoticeSeverity;
|
|
159
162
|
export type SystemNoticeState = import("./system-notice-contract.js").SystemNoticeState;
|
|
163
|
+
export type SystemNoticeTier = import("./system-notice-contract.js").SystemNoticeTier;
|
|
160
164
|
export type RpcEvent = import("./protocol.js").RpcEvent;
|
|
161
165
|
export type RpcRequest = import("./protocol.js").RpcRequest;
|
|
162
166
|
export type RpcResponse = import("./protocol.js").RpcResponse;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"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"}
|
|
@@ -157,8 +157,9 @@ export type LiveQueryGroup = {
|
|
|
157
157
|
readonly listeners: Set<(diff: import("@econ-v1/domain").QueryDiff<JsonValue>) => void>;
|
|
158
158
|
readonly name: QueryName;
|
|
159
159
|
readonly params: JsonValue;
|
|
160
|
+
retryTimer: ReturnType<typeof globalThis.setTimeout> | undefined;
|
|
160
161
|
readonly sessionId: string | undefined;
|
|
161
|
-
|
|
162
|
+
subscriptionId: string;
|
|
162
163
|
};
|
|
163
164
|
export type RpcResponsivenessObserver = {
|
|
164
165
|
markSlow(id: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-port-client.d.ts","sourceRoot":"","sources":["../src/message-port-client.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"message-port-client.d.ts","sourceRoot":"","sources":["../src/message-port-client.js"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type SystemNoticeSeverity = "error" | "info" | "security" | "success" | "warning";
|
|
2
|
+
export type SystemNoticeTier = "ambient" | "blocking" | "interrupt";
|
|
2
3
|
export type SystemNoticeActionKind = "primary" | "secondary";
|
|
3
4
|
export type SystemNoticeBlockScope = "shell" | "stage";
|
|
4
5
|
export type SystemNoticeState = "busy" | "failed" | "idle" | "resolved";
|
|
@@ -57,6 +58,22 @@ export type SystemNoticeInput = {
|
|
|
57
58
|
severity: SystemNoticeSeverity;
|
|
58
59
|
source: string;
|
|
59
60
|
state?: SystemNoticeState;
|
|
61
|
+
/**
|
|
62
|
+
* How this notice is PRESENTED, as distinct from how severe
|
|
63
|
+
* it is. `"interrupt"` banners briefly and then falls into the notice centre; `"ambient"`
|
|
64
|
+
* never banners at all and reaches the owner through the centre and the topbar issue count;
|
|
65
|
+
* `"blocking"` is owned by `notice-gate.js` and is implied by the `blocking` field.
|
|
66
|
+
*
|
|
67
|
+
* Defaults to `"interrupt"` — deliberately the loud one. A stage-published notice whose author
|
|
68
|
+
* never heard of this field being seen is a recoverable annoyance; being silently swallowed is
|
|
69
|
+
* not. The default is a safety net for out-of-repo publishers, NOT a shortcut for in-repo ones:
|
|
70
|
+
* every publisher in this repository sets it explicitly.
|
|
71
|
+
*
|
|
72
|
+
* Do not infer this from `persistent`. Measured against the real publishers, three of the
|
|
73
|
+
* noisiest standing conditions (`memory-budget-notice.js`, `node-slow-notice.js`,
|
|
74
|
+
* `stage-performance-notice.js`) are published `persistent: false`.
|
|
75
|
+
*/
|
|
76
|
+
tier?: SystemNoticeTier;
|
|
60
77
|
title: string;
|
|
61
78
|
};
|
|
62
79
|
export type SystemNoticePatch = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-notice-contract.d.ts","sourceRoot":"","sources":["../src/system-notice-contract.js"],"names":[],"mappings":"mCAGc,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"system-notice-contract.d.ts","sourceRoot":"","sources":["../src/system-notice-contract.js"],"names":[],"mappings":"mCAGc,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS;+BACrD,SAAS,GAAG,UAAU,GAAG,WAAW;qCACpC,SAAS,GAAG,WAAW;qCACvB,OAAO,GAAG,OAAO;gCACjB,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU;kCACvC,SAAS,GAAG,IAAI;2BAChB,iBAAiB;mCACjB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,IAAI;4CAC1C,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;;;;;;;;SAGpB,MAAM;UAIN,sBAAsB;WACtB,MAAM;cACN,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;iCAE1B;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE;qCAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE;+BACnD;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,sBAAsB,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE;;cAGtI,SAAS,kBAAkB,EAAE;eAC7B,sBAAsB;WACtB,gBAAgB;;;;;;;;;;;;;;cAChB,mBAAmB;iBAWnB,OAAO;QACP,MAAM;aACN,MAAM;gBACN,OAAO;eACP,MAAM;cACN,oBAAoB;YACpB,MAAM;YACN,iBAAiB;;;;;;;;;;;;;;;;WACjB,gBAAgB;WAahB,MAAM;;;cAIN,SAAS,kBAAkB,EAAE,GAAG,SAAS;eACzC,sBAAsB,GAAG,SAAS;WAClC,gBAAgB,GAAG,SAAS;kBAC5B,OAAO,GAAG,SAAS;cACnB,MAAM,GAAG,SAAS;iBAClB,OAAO,GAAG,SAAS;eACnB,MAAM,GAAG,SAAS;eAClB,oBAAoB,GAAG,SAAS;aAChC,MAAM,GAAG,SAAS;YAClB,iBAAiB,GAAG,SAAS;YAC7B,MAAM,GAAG,SAAS;;;aAIlB,MAAM,IAAI;YACV,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI;;;aAIlC,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;YACpB,MAAM,SAAS,YAAY,EAAE;aAC7B,CAAC,KAAK,EAAE,iBAAiB,KAAK,kBAAkB;eAChD,CAAC,QAAQ,EAAE,oBAAoB,KAAK,MAAM,IAAI;;;;;;;;;;0BAC9C,CAAC,QAAQ,EAAE,6BAA6B,KAAK,MAAM,IAAI;YAQvD,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,IAAI"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@econ-v1/rpc",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.52",
|
|
4
4
|
"description": "Typed runtime and contracts for Node client stage-to-kernel RPC",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"author": "Node contributors",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@econ-v1/domain": "7.0.
|
|
60
|
-
"@econ-v1/ports": "7.0.
|
|
59
|
+
"@econ-v1/domain": "7.0.52",
|
|
60
|
+
"@econ-v1/ports": "7.0.52"
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shell's own retry edge for a failed kernel startup.
|
|
5
|
+
*
|
|
6
|
+
* Served OUTSIDE the kernel's readiness gates (see `system/pwa/src/kernel-runtime.js`), because
|
|
7
|
+
* it exists for exactly the state in which every gated query is being rejected: normal-services
|
|
8
|
+
* activation failed, so the shell has no registry, no route, and shows its startup-failure card.
|
|
9
|
+
* Re-running the gated reads — which is all the card's Retry used to do — rethrows the cached
|
|
10
|
+
* failure and never re-attempts activation, so the button could not recover anything.
|
|
11
|
+
*
|
|
12
|
+
* The command resolves only once the fresh activation has SETTLED, so its caller can restart the
|
|
13
|
+
* shell immediately afterwards and find the gates either open or honestly failed again.
|
|
14
|
+
*/
|
|
15
|
+
export const CLIENT_SERVICES_RETRY_COMMAND = "client-services.retry.v1";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `"ready"` means normal services are active and a shell restart will now get through. `"failed"`
|
|
19
|
+
* carries the underlying reason where the kernel has one — the same cause the startup card
|
|
20
|
+
* renders — so a repeat failure is legible rather than a silently identical dead screen.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {{ readonly status: "ready" }
|
|
23
|
+
* | { readonly cause?: string; readonly status: "failed" }} ClientServicesRetryResult
|
|
24
|
+
*/
|
package/src/index.js
CHANGED
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
/** @typedef {import("@econ-v1/ports").LifecycleStormView} LifecycleStormView */
|
|
65
65
|
/** @typedef {import("@econ-v1/ports").LifecycleTransitionView} LifecycleTransitionView */
|
|
66
66
|
/** @typedef {import("@econ-v1/ports").ShellCacheResetPort} ShellCacheResetPort */
|
|
67
|
+
/** @typedef {import("@econ-v1/ports").ShellRepairReport} ShellRepairReport */
|
|
67
68
|
/** @typedef {import("@econ-v1/ports").ShellWorkerControlPort} ShellWorkerControlPort */
|
|
68
69
|
/** @typedef {import("@econ-v1/ports").ShellVersionPort} ShellVersionPort */
|
|
69
70
|
/** @typedef {import("@econ-v1/ports").ShellVersionReport} ShellVersionReport */
|
|
@@ -105,6 +106,7 @@
|
|
|
105
106
|
/** @typedef {import("./l402-enforcement-contract.js").L402EnforcementRow} L402EnforcementRow */
|
|
106
107
|
/** @typedef {import("./l402-enforcement-contract.js").L402EnforcementSource} L402EnforcementSource */
|
|
107
108
|
/** @typedef {import("./l402-enforcement-contract.js").L402EnforcementSetPayload} L402EnforcementSetPayload */
|
|
109
|
+
/** @typedef {import("./client-services-contract.js").ClientServicesRetryResult} ClientServicesRetryResult */
|
|
108
110
|
/** @typedef {import("./factory-reset-contract.js").FactoryResetPending} FactoryResetPending */
|
|
109
111
|
/** @typedef {import("./factory-reset-contract.js").FactoryResetStatus} FactoryResetStatus */
|
|
110
112
|
/** @typedef {import("./factory-reset-contract.js").FactoryResetStatusRow} FactoryResetStatusRow */
|
|
@@ -155,6 +157,7 @@
|
|
|
155
157
|
/** @typedef {import("./system-notice-contract.js").SystemNoticePatch} SystemNoticePatch */
|
|
156
158
|
/** @typedef {import("./system-notice-contract.js").SystemNoticeSeverity} SystemNoticeSeverity */
|
|
157
159
|
/** @typedef {import("./system-notice-contract.js").SystemNoticeState} SystemNoticeState */
|
|
160
|
+
/** @typedef {import("./system-notice-contract.js").SystemNoticeTier} SystemNoticeTier */
|
|
158
161
|
/** @typedef {import("./protocol.js").RpcEvent} RpcEvent */
|
|
159
162
|
/** @typedef {import("./protocol.js").RpcRequest} RpcRequest */
|
|
160
163
|
/** @typedef {import("./protocol.js").RpcResponse} RpcResponse */
|
|
@@ -181,6 +184,7 @@ export {
|
|
|
181
184
|
export { DEFAULT_REQUEST_TIMEOUT_MS, KernelRpcTimeoutError, MessagePortKernelRpcClient } from "./message-port-client.js";
|
|
182
185
|
export { NODE_APP_LIFECYCLE_COMMAND, NODE_APP_MEMORY_CAPTURE_COMMAND, NODE_APP_MEMORY_DETAIL_QUERY, NODE_APP_MEMORY_DOWNLOAD_COMMAND, NODE_APP_MEMORY_LIST_QUERY, NODE_APP_MEMORY_OVERVIEW_QUERY, NODE_APP_MEMORY_STATUS_COMMAND } from "./node-app-memory-contract.js";
|
|
183
186
|
export { APPS_LIFECYCLE_PIN_COMMAND, APPS_LIFECYCLE_QUERY } from "./apps-lifecycle-contract.js";
|
|
187
|
+
export { CLIENT_SERVICES_RETRY_COMMAND } from "./client-services-contract.js";
|
|
184
188
|
export { L402_ENFORCEMENT_QUERY, L402_ENFORCEMENT_ROW_ID, L402_ENFORCEMENT_SET_COMMAND } from "./l402-enforcement-contract.js";
|
|
185
189
|
export {
|
|
186
190
|
FACTORY_RESET_ARM_COMMAND,
|
|
@@ -25,26 +25,32 @@ import {
|
|
|
25
25
|
/** @typedef {import("./transport.js").RpcTransport} RpcTransport */
|
|
26
26
|
/** @typedef {{ readonly reject: (error: unknown) => void, readonly resolve: (value: JsonValue) => void, readonly sessionId: string | undefined, readonly slowBarId: ReturnType<typeof globalThis.setTimeout>, readonly startedAtMs: number, readonly timeoutId: ReturnType<typeof globalThis.setTimeout>, readonly timeoutMs: number }} PendingRequest */
|
|
27
27
|
/** @typedef {{ readonly eventType: KernelEventType, readonly listeners: Set<(event: DomainEvent) => void>, readonly sessionId: string | undefined, readonly subscriptionId: string }} EventGroup */
|
|
28
|
-
/** @typedef {{ readonly errorListeners: Set<(error: { readonly message: string }) => void>, readonly listeners: Set<(diff: import("@econ-v1/domain").QueryDiff<JsonValue>) => void>, readonly name: QueryName, readonly params: JsonValue, readonly sessionId: string | undefined,
|
|
28
|
+
/** @typedef {{ readonly errorListeners: Set<(error: { readonly message: string }) => void>, readonly listeners: Set<(diff: import("@econ-v1/domain").QueryDiff<JsonValue>) => void>, readonly name: QueryName, readonly params: JsonValue, retryTimer: ReturnType<typeof globalThis.setTimeout> | undefined, readonly sessionId: string | undefined, subscriptionId: string }} LiveQueryGroup */
|
|
29
29
|
/** @typedef {{ markSlow(id: string): void, settle(id: string, elapsedMs: number, answered: boolean, timeoutMs: number): void }} RpcResponsivenessObserver */
|
|
30
30
|
/** @typedef {{ readonly monotonicNow?: () => number, readonly now?: () => string, readonly observer?: RpcResponsivenessObserver, readonly requestTimeoutMs?: number }} MessagePortKernelRpcClientOptions */
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* A subscribe request the kernel refused.
|
|
34
|
-
*
|
|
33
|
+
* A subscribe request the kernel refused. An event subscription (no retry path) and the FINAL
|
|
34
|
+
* live-query attempt both drop the group, so those keep the "dropped — nothing will be
|
|
35
|
+
* delivered" wording. A non-final live-query attempt keeps the group and arms a retry instead
|
|
36
|
+
* — saying "dropped" there would be a lie: an operator reading four such warnings for a query
|
|
37
|
+
* that actually recovered on attempt 2 would misdiagnose it. Pass `retryDelayMs` (the delay
|
|
38
|
+
* before the next attempt) only when a retry is actually about to be armed; omit it for the
|
|
39
|
+
* event path and for the final live-query attempt.
|
|
35
40
|
*
|
|
36
41
|
* @param {"event" | "live-query"} kind
|
|
37
42
|
* @param {string} resource
|
|
38
43
|
* @param {string | undefined} sessionId
|
|
39
44
|
* @param {unknown} error
|
|
45
|
+
* @param {number} [retryDelayMs]
|
|
40
46
|
*/
|
|
41
|
-
function warnSubscriptionRejected(kind, resource, sessionId, error) {
|
|
47
|
+
function warnSubscriptionRejected(kind, resource, sessionId, error, retryDelayMs) {
|
|
42
48
|
const reason = error instanceof Error ? error.message : String(error);
|
|
43
49
|
const owner = sessionId === undefined ? "shell" : `stage session ${sessionId}`;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
50
|
+
const outcome = retryDelayMs === undefined
|
|
51
|
+
? "and has been dropped — nothing will be delivered"
|
|
52
|
+
: `and will retry in ${retryDelayMs}ms`;
|
|
53
|
+
console.warn(`[kernel-rpc] ${kind} subscription to "${resource}" was rejected for the ${owner} ${outcome}: ${reason}`);
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/**
|
|
@@ -108,6 +114,11 @@ const MAX_DECLARED_TIMEOUT_MS = 300_000;
|
|
|
108
114
|
// and report the loss as a port timeout instead of the host's clearer error.
|
|
109
115
|
const DECLARED_TIMEOUT_GRACE_MS = 5_000;
|
|
110
116
|
|
|
117
|
+
/** A rejected `live-query.subscribe` used to drop its group permanently — the caller went
|
|
118
|
+
* blind for the life of the client with no way back. These bound a re-subscribe instead:
|
|
119
|
+
* three retries on a doubling delay, then the original give-up behaviour. */
|
|
120
|
+
const LIVE_QUERY_SUBSCRIBE_RETRY_DELAYS_MS = [2_000, 4_000, 8_000];
|
|
121
|
+
|
|
111
122
|
/**
|
|
112
123
|
* The deadline for one request. Only a stage capability invoke may stretch the
|
|
113
124
|
* port's clock, and only by the same `timeout_ms` field the kernel dispatcher
|
|
@@ -372,6 +383,14 @@ export class MessagePortKernelRpcClient {
|
|
|
372
383
|
for (const requestId of [...this.#pending.keys()]) this.#takePending(requestId, false)?.reject(error);
|
|
373
384
|
this.#eventGroupsByKey.clear();
|
|
374
385
|
this.#eventGroupsBySubscription.clear();
|
|
386
|
+
// A pending re-subscribe timer must not outlive the port it would retry on — otherwise a
|
|
387
|
+
// group with a retry in flight keeps its timer (and, through its closure, its listener
|
|
388
|
+
// sets) alive for up to the retry budget's last delay after close(), exactly what
|
|
389
|
+
// #removeSessionSubscriptions and #dropLiveGroup already guard against.
|
|
390
|
+
for (const group of this.#liveGroupsByKey.values()) {
|
|
391
|
+
globalThis.clearTimeout(group.retryTimer);
|
|
392
|
+
group.retryTimer = undefined;
|
|
393
|
+
}
|
|
375
394
|
this.#liveGroupsByKey.clear();
|
|
376
395
|
this.#liveGroupsBySubscription.clear();
|
|
377
396
|
}
|
|
@@ -484,6 +503,10 @@ export class MessagePortKernelRpcClient {
|
|
|
484
503
|
if (group.sessionId === sessionId) {
|
|
485
504
|
this.#liveGroupsByKey.delete(key);
|
|
486
505
|
this.#liveGroupsBySubscription.delete(group.subscriptionId);
|
|
506
|
+
// A session close must cancel any pending re-subscribe timer too — otherwise it fires
|
|
507
|
+
// later against a group nothing references any more.
|
|
508
|
+
globalThis.clearTimeout(group.retryTimer);
|
|
509
|
+
group.retryTimer = undefined;
|
|
487
510
|
}
|
|
488
511
|
}
|
|
489
512
|
}
|
|
@@ -585,22 +608,14 @@ export class MessagePortKernelRpcClient {
|
|
|
585
608
|
let group = this.#liveGroupsByKey.get(key);
|
|
586
609
|
if (group === undefined) {
|
|
587
610
|
const subscriptionId = this.#newSubscriptionId();
|
|
588
|
-
group = { errorListeners: new Set(), listeners: new Set(), name, params, sessionId, subscriptionId };
|
|
611
|
+
group = { errorListeners: new Set(), listeners: new Set(), name, params, retryTimer: undefined, sessionId, subscriptionId };
|
|
589
612
|
this.#liveGroupsByKey.set(key, group);
|
|
590
613
|
this.#liveGroupsBySubscription.set(subscriptionId, group);
|
|
591
614
|
const request = sessionId === undefined
|
|
592
615
|
? /** @type {const} */ ({ name, params, subscriptionId, type: "live-query.subscribe" })
|
|
593
616
|
: /** @type {const} */ ({ name, params, sessionId, subscriptionId, type: "stage.live-query.subscribe" });
|
|
594
617
|
void this.#request(request).catch((error) => {
|
|
595
|
-
|
|
596
|
-
this.#dropLiveGroup(key, rejectedGroup);
|
|
597
|
-
warnSubscriptionRejected("live-query", name, sessionId, error);
|
|
598
|
-
// A rejected (or timed-out) subscribe request never produces a "live-query.failed"
|
|
599
|
-
// event — the kernel has nothing to key it against, since no subscription was ever
|
|
600
|
-
// established. Without this, a caller stuck on #request's 30s timeout would sit in
|
|
601
|
-
// its loading state forever: the group is dropped above, but nobody is told why.
|
|
602
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
603
|
-
for (const listener of [...rejectedGroup.errorListeners]) this.#notify(() => listener({ message }));
|
|
618
|
+
this.#handleLiveSubscribeRejection(key, /** @type {LiveQueryGroup} */ (group), sessionId, name, params, 0, error);
|
|
604
619
|
});
|
|
605
620
|
}
|
|
606
621
|
const storedListener = /** @type {(diff: import("@econ-v1/domain").QueryDiff<JsonValue>) => void} */ (listener);
|
|
@@ -622,6 +637,70 @@ export class MessagePortKernelRpcClient {
|
|
|
622
637
|
};
|
|
623
638
|
}
|
|
624
639
|
|
|
640
|
+
/**
|
|
641
|
+
* Handles a rejected (or timed-out) `live-query.subscribe`/`stage.live-query.subscribe`
|
|
642
|
+
* request. Before this existed, the rejection dropped the group permanently — the caller
|
|
643
|
+
* went blind for the life of the client with nothing to bring it back. This bounds a
|
|
644
|
+
* re-subscribe instead: up to `LIVE_QUERY_SUBSCRIBE_RETRY_DELAYS_MS.length` retries on a
|
|
645
|
+
* doubling delay, each under a FRESH subscription id (the original request may merely have
|
|
646
|
+
* been slow, in which case the kernel established the subscription after we gave up on the
|
|
647
|
+
* reply — reusing that id would let the retry collide with it), then the original give-up
|
|
648
|
+
* behaviour.
|
|
649
|
+
* @param {string} key
|
|
650
|
+
* @param {LiveQueryGroup} group
|
|
651
|
+
* @param {string | undefined} sessionId
|
|
652
|
+
* @param {QueryName} name
|
|
653
|
+
* @param {JsonValue} params
|
|
654
|
+
* @param {number} attempt
|
|
655
|
+
* @param {unknown} error
|
|
656
|
+
* @returns {void}
|
|
657
|
+
*/
|
|
658
|
+
#handleLiveSubscribeRejection(key, group, sessionId, name, params, attempt, error) {
|
|
659
|
+
const delayMs = LIVE_QUERY_SUBSCRIBE_RETRY_DELAYS_MS[attempt];
|
|
660
|
+
// Decided up front so the warning can say which outcome actually applies: a group already
|
|
661
|
+
// replaced or abandoned (unsubscribed) while this request was in flight must not be
|
|
662
|
+
// resurrected, regardless of budget remaining.
|
|
663
|
+
const willRetry = delayMs !== undefined && this.#liveGroupsByKey.get(key) === group && group.listeners.size > 0;
|
|
664
|
+
warnSubscriptionRejected("live-query", name, sessionId, error, willRetry ? delayMs : undefined);
|
|
665
|
+
// A rejected (or timed-out) subscribe request never produces a "live-query.failed" event
|
|
666
|
+
// — the kernel has nothing to key it against, since no subscription was ever established.
|
|
667
|
+
// Told on EVERY attempt, not only the last: a view must be able to draw an honest failure
|
|
668
|
+
// state while a retry is still pending, instead of sitting in its loading state forever.
|
|
669
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
670
|
+
for (const listener of [...group.errorListeners]) this.#notify(() => listener({ message }));
|
|
671
|
+
// The abandoned id is released best-effort either way — fire-and-forget, so a server-side
|
|
672
|
+
// subscription the kernel established after our reply deadline passed cannot leak, whether
|
|
673
|
+
// this attempt is retried or this is the final give-up. Guard against duplicate releases
|
|
674
|
+
// if the group is dropped concurrently (e.g., caller unsubscribes before this rejection settles).
|
|
675
|
+
const staleSubscriptionId = group.subscriptionId;
|
|
676
|
+
if (this.#liveGroupsByKey.get(key) === group) {
|
|
677
|
+
const release = sessionId === undefined
|
|
678
|
+
? /** @type {const} */ ({ subscriptionId: staleSubscriptionId, type: "live-query.unsubscribe" })
|
|
679
|
+
: /** @type {const} */ ({ sessionId, subscriptionId: staleSubscriptionId, type: "stage.live-query.unsubscribe" });
|
|
680
|
+
void this.#request(release).catch(() => undefined);
|
|
681
|
+
}
|
|
682
|
+
if (!willRetry) {
|
|
683
|
+
this.#dropLiveGroup(key, group);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
group.retryTimer = globalThis.setTimeout(() => {
|
|
687
|
+
group.retryTimer = undefined;
|
|
688
|
+
// A subscription dropped (or already replaced by a newer group under this key) while
|
|
689
|
+
// the timer was pending must not be resurrected.
|
|
690
|
+
if (this.#liveGroupsByKey.get(key) !== group || group.listeners.size === 0) return;
|
|
691
|
+
this.#liveGroupsBySubscription.delete(staleSubscriptionId);
|
|
692
|
+
const subscriptionId = this.#newSubscriptionId();
|
|
693
|
+
group.subscriptionId = subscriptionId;
|
|
694
|
+
this.#liveGroupsBySubscription.set(subscriptionId, group);
|
|
695
|
+
const retry = sessionId === undefined
|
|
696
|
+
? /** @type {const} */ ({ name, params, subscriptionId, type: "live-query.subscribe" })
|
|
697
|
+
: /** @type {const} */ ({ name, params, sessionId, subscriptionId, type: "stage.live-query.subscribe" });
|
|
698
|
+
void this.#request(retry).catch((retryError) => {
|
|
699
|
+
this.#handleLiveSubscribeRejection(key, group, sessionId, name, params, attempt + 1, retryError);
|
|
700
|
+
});
|
|
701
|
+
}, delayMs);
|
|
702
|
+
}
|
|
703
|
+
|
|
625
704
|
/**
|
|
626
705
|
* @param {string} key
|
|
627
706
|
* @param {EventGroup} group
|
|
@@ -638,6 +717,8 @@ export class MessagePortKernelRpcClient {
|
|
|
638
717
|
#dropLiveGroup(key, group) {
|
|
639
718
|
if (this.#liveGroupsByKey.get(key) === group) this.#liveGroupsByKey.delete(key);
|
|
640
719
|
this.#liveGroupsBySubscription.delete(group.subscriptionId);
|
|
720
|
+
globalThis.clearTimeout(group.retryTimer);
|
|
721
|
+
group.retryTimer = undefined;
|
|
641
722
|
}
|
|
642
723
|
|
|
643
724
|
/** @returns {string} */
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
export {};
|
|
4
4
|
/** @typedef {"error" | "info" | "security" | "success" | "warning"} SystemNoticeSeverity */
|
|
5
|
+
/** @typedef {"ambient" | "blocking" | "interrupt"} SystemNoticeTier */
|
|
5
6
|
/** @typedef {"primary" | "secondary"} SystemNoticeActionKind */
|
|
6
7
|
/** @typedef {"shell" | "stage"} SystemNoticeBlockScope */
|
|
7
8
|
/** @typedef {"busy" | "failed" | "idle" | "resolved"} SystemNoticeState */
|
|
@@ -46,6 +47,19 @@ export {};
|
|
|
46
47
|
* @property {SystemNoticeSeverity} severity
|
|
47
48
|
* @property {string} source
|
|
48
49
|
* @property {SystemNoticeState} [state]
|
|
50
|
+
* @property {SystemNoticeTier} [tier] How this notice is PRESENTED, as distinct from how severe
|
|
51
|
+
* it is. `"interrupt"` banners briefly and then falls into the notice centre; `"ambient"`
|
|
52
|
+
* never banners at all and reaches the owner through the centre and the topbar issue count;
|
|
53
|
+
* `"blocking"` is owned by `notice-gate.js` and is implied by the `blocking` field.
|
|
54
|
+
*
|
|
55
|
+
* Defaults to `"interrupt"` — deliberately the loud one. A stage-published notice whose author
|
|
56
|
+
* never heard of this field being seen is a recoverable annoyance; being silently swallowed is
|
|
57
|
+
* not. The default is a safety net for out-of-repo publishers, NOT a shortcut for in-repo ones:
|
|
58
|
+
* every publisher in this repository sets it explicitly.
|
|
59
|
+
*
|
|
60
|
+
* Do not infer this from `persistent`. Measured against the real publishers, three of the
|
|
61
|
+
* noisiest standing conditions (`memory-budget-notice.js`, `node-slow-notice.js`,
|
|
62
|
+
* `stage-performance-notice.js`) are published `persistent: false`.
|
|
49
63
|
* @property {string} title
|
|
50
64
|
*/
|
|
51
65
|
/**
|