@astrale-os/shell-react 0.2.5 → 0.2.7

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/index.d.ts CHANGED
@@ -26,8 +26,8 @@ export { SelectionScope, useSelection } from './selection/index.js';
26
26
  export type { Selection, SelectionScopeProps } from './selection/index.js';
27
27
  export { useCloseGuard, useTargetNode, useViewsFor, useWindow, useWindowList, useWindows, ViewHost, ViewStage, } from './window/index.js';
28
28
  export type { CloseGuard, CloseVerdict, ViewDescriptor, ViewHostProps, ViewSource, ViewHostHandle, ViewsForResult, WindowListEntry, WindowState, WindowView, } from './window/index.js';
29
- export { useIntent, useSendIntent } from './intents/index.js';
30
- export type { IntentCtx, IntentPayload, IntentReply, SendIntent } from './intents/index.js';
29
+ export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
30
+ export type { EmitIntent, IntentCtx, IntentPayload, IntentReply, SendIntent, } from './intents/index.js';
31
31
  export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
32
32
  export type { AppHostFallbackState, AppHostProps, AppLogoProps, AppsResult, CurrentSpaceResult, RolesResult, SpaceResult, SpacesResult, } from './application/index.js';
33
33
  export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ export { SelectionScope, useSelection } from './selection/index.js';
25
25
  // ─── window (§10) ────────────────────────────────────────────────────────────
26
26
  export { useCloseGuard, useTargetNode, useViewsFor, useWindow, useWindowList, useWindows, ViewHost, ViewStage, } from './window/index.js';
27
27
  // ─── intents (§11) ───────────────────────────────────────────────────────────
28
- export { useIntent, useSendIntent } from './intents/index.js';
28
+ export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
29
29
  // ─── application — the OS plane (§12) ────────────────────────────────────────
30
30
  export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
31
31
  // ─── error taxonomy (§1.4) — guards + classes, re-exported from kernel-client ─
@@ -0,0 +1,17 @@
1
+ import type { IntentRegistry } from '@astrale-os/shell';
2
+ import type { IntentPayload } from './envelope.js';
3
+ /** Fire-and-forget intent sender returned by {@link useEmitIntent}. */
4
+ export interface EmitIntent {
5
+ <K extends keyof IntentRegistry>(name: K, payload: IntentPayload<K>,
6
+ /** `to`: `'parent'` (reserved) or a child windowId. Default: the parent, if any. */
7
+ opts?: {
8
+ to?: 'parent' | string;
9
+ }): void;
10
+ }
11
+ /**
12
+ * Return a stable sender for intents that do not need a reply. Emitted messages
13
+ * carry no correlation id, so listeners can handle navigation and notifications
14
+ * without retaining a pending request.
15
+ */
16
+ export declare function useEmitIntent(): EmitIntent;
17
+ //# sourceMappingURL=emit-intent.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/emit-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAMlD,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7B,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GAChC,IAAI,CAAA;CACR;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAgB1C"}
@@ -0,0 +1,22 @@
1
+ import { useCallback } from 'react';
2
+ import { useShell } from '../session/shell.hook.js';
3
+ import { intentMessage } from './envelope.js';
4
+ import { resolveIntentTarget } from './resolve-intent-target.js';
5
+ /**
6
+ * Return a stable sender for intents that do not need a reply. Emitted messages
7
+ * carry no correlation id, so listeners can handle navigation and notifications
8
+ * without retaining a pending request.
9
+ */
10
+ export function useEmitIntent() {
11
+ const shell = useShell();
12
+ return useCallback((name, payload, opts) => {
13
+ const target = resolveIntentTarget(shell, opts?.to);
14
+ if (target.kind === 'invalid')
15
+ throw new Error(`useEmitIntent: ${target.reason}`);
16
+ const message = intentMessage(name, payload);
17
+ if (target.kind === 'parent')
18
+ shell.parent.send(message);
19
+ else
20
+ shell.children.send(target.windowId, message);
21
+ }, [shell]);
22
+ }
@@ -1,5 +1,7 @@
1
1
  export { useIntent } from './listen-intent.hook.js';
2
2
  export type { IntentCtx } from './listen-intent.hook.js';
3
+ export { useEmitIntent } from './emit-intent.hook.js';
4
+ export type { EmitIntent } from './emit-intent.hook.js';
3
5
  export { useSendIntent } from './send-intent.hook.js';
4
6
  export type { SendIntent } from './send-intent.hook.js';
5
7
  export type { IntentPayload, IntentReply } from './envelope.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intents/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intents/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -1,3 +1,4 @@
1
1
  // intents — API §11 (useIntent, useSendIntent). See SHELL_REACT_API.md.
2
2
  export { useIntent } from './listen-intent.hook.js';
3
+ export { useEmitIntent } from './emit-intent.hook.js';
3
4
  export { useSendIntent } from './send-intent.hook.js';
@@ -0,0 +1,12 @@
1
+ import type { Shell } from '@astrale-os/shell';
2
+ export type IntentTarget = {
3
+ readonly kind: 'parent';
4
+ } | {
5
+ readonly kind: 'child';
6
+ readonly windowId: string;
7
+ } | {
8
+ readonly kind: 'invalid';
9
+ readonly reason: string;
10
+ };
11
+ export declare function resolveIntentTarget(shell: Shell, to: string | undefined): IntentTarget;
12
+ //# sourceMappingURL=resolve-intent-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-intent-target.d.ts","sourceRoot":"","sources":["../../src/intents/resolve-intent-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAYtF"}
@@ -0,0 +1,13 @@
1
+ export function resolveIntentTarget(shell, to) {
2
+ if (to === undefined) {
3
+ return shell.parent !== null
4
+ ? { kind: 'parent' }
5
+ : { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' };
6
+ }
7
+ if (to === 'parent') {
8
+ return shell.parent !== null
9
+ ? { kind: 'parent' }
10
+ : { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" };
11
+ }
12
+ return { kind: 'child', windowId: to };
13
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"send-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/send-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,EAAS,MAAM,mBAAmB,CAAA;AAW7E,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK/D,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7B,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;CAClC;AAqBD,wBAAgB,aAAa,IAAI,UAAU,CAiE1C"}
1
+ {"version":3,"file":"send-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/send-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAWtE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAM/D,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7B,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;CAClC;AAED,wBAAgB,aAAa,IAAI,UAAU,CAiF1C"}
@@ -8,19 +8,7 @@
8
8
  import { useCallback, useEffect, useRef } from 'react';
9
9
  import { useShell } from '../session/shell.hook.js';
10
10
  import { INTENT_REPLY, NOOP, intentMessage } from './envelope.js';
11
- function resolveTarget(shell, to) {
12
- if (to === undefined) {
13
- return shell.parent !== null
14
- ? { kind: 'parent' }
15
- : { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' };
16
- }
17
- if (to === 'parent') {
18
- return shell.parent !== null
19
- ? { kind: 'parent' }
20
- : { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" };
21
- }
22
- return { kind: 'child', windowId: to };
23
- }
11
+ import { resolveIntentTarget } from './resolve-intent-target.js';
24
12
  export function useSendIntent() {
25
13
  const shell = useShell();
26
14
  // Unmount settlers for in-flight sends, so a pending reply resolves on teardown.
@@ -34,14 +22,14 @@ export function useSendIntent() {
34
22
  };
35
23
  }, []);
36
24
  return useCallback((name, payload, opts) => {
37
- const target = resolveTarget(shell, opts?.to);
25
+ const target = resolveIntentTarget(shell, opts?.to);
38
26
  if (target.kind === 'invalid') {
39
27
  return Promise.reject(new Error(`useSendIntent: ${target.reason}`));
40
28
  }
41
29
  const correlationId = crypto.randomUUID();
42
30
  const message = intentMessage(name, payload, correlationId);
43
31
  const pending = pendingRef.current;
44
- return new Promise((resolve) => {
32
+ return new Promise((resolve, reject) => {
45
33
  let done = false;
46
34
  let offReply = NOOP;
47
35
  let offClose = NOOP;
@@ -55,12 +43,25 @@ export function useSendIntent() {
55
43
  pending.delete(onUnmount);
56
44
  resolve(value);
57
45
  }
46
+ function fail(error) {
47
+ if (done)
48
+ return;
49
+ done = true;
50
+ offReply();
51
+ offClose();
52
+ pending.delete(onUnmount);
53
+ reject(error);
54
+ }
58
55
  const onReply = (m) => {
59
56
  if (m.envelope.name !== INTENT_REPLY)
60
57
  return;
61
58
  const reply = m.envelope.payload;
62
59
  if (reply.correlationId !== correlationId)
63
60
  return;
61
+ if (reply.error) {
62
+ fail(new Error(reply.error.message));
63
+ return;
64
+ }
64
65
  settle(reply.result);
65
66
  };
66
67
  pending.add(onUnmount);
@@ -1 +1 @@
1
- {"version":3,"file":"views-for.hook.d.ts","sourceRoot":"","sources":["../../../src/window/hooks/views-for.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAW/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAKjE,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CAC1C;AAiBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,CAqBpF"}
1
+ {"version":3,"file":"views-for.hook.d.ts","sourceRoot":"","sources":["../../../src/window/hooks/views-for.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAW/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAKjE,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CAC1C;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,CAqBpF"}
@@ -1,4 +1,3 @@
1
- import { K } from '@astrale-os/kernel-core';
2
1
  /**
3
2
  * useViewsFor (API §10) — "what Views can render this node", as mountable
4
3
  * `ViewDescriptor`s for the open-with menu. Backed one-shot (`live:false`) by the
@@ -7,14 +6,12 @@ import { K } from '@astrale-os/kernel-core';
7
6
  */
8
7
  import { useMemo } from 'react';
9
8
  import { useAsyncResource } from '../../graph/async.js';
10
- import { useKernelClient } from '../../session/kernel-client.hook.js';
11
- /** The `View.resolve` wire address — the qualified static-method slug from the schema. */
12
- const VIEW_RESOLVE_METHOD = K.View.resolve.path.method.raw;
9
+ import { useShell } from '../../session/shell.hook.js';
13
10
  const NO_VIEWS = Object.freeze([]);
14
11
  export function useViewsFor(node) {
15
- const kernel = useKernelClient();
12
+ const shell = useShell();
16
13
  const target = resolvePath(node);
17
- const res = useAsyncResource(target === null ? null : () => resolveViews(kernel, target), NO_VIEWS, [kernel, target]);
14
+ const res = useAsyncResource(target === null ? null : () => resolveViews(shell, target), NO_VIEWS, [shell, target]);
18
15
  return useMemo(() => ({
19
16
  views: res.data,
20
17
  pending: res.pending,
@@ -25,9 +22,9 @@ export function useViewsFor(node) {
25
22
  }), [res.data, res.pending, res.error, res.refetch]);
26
23
  }
27
24
  /** Dispatch `View.resolve` and project its rows to mountable `ViewDescriptor`s. */
28
- async function resolveViews(kernel, node) {
29
- const rows = (await kernel.call(VIEW_RESOLVE_METHOD, { node }));
30
- if (rows === null || rows === undefined || rows.length === 0)
25
+ async function resolveViews(shell, node) {
26
+ const rows = await shell.views.resolve(node);
27
+ if (rows.length === 0)
31
28
  return NO_VIEWS;
32
29
  return rows.map((r) => ({ id: r.id, path: r.path, url: r.url, handshake: r.handshake }));
33
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/shell-react",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Astrale shell-react — the React face of the shell (provider, hooks, view components)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -44,7 +44,7 @@
44
44
  "react-dom": "^19.2.0",
45
45
  "typescript": "~6.0.1-rc",
46
46
  "vitest": "^3.2.4",
47
- "@astrale-os/shell": "0.3.4"
47
+ "@astrale-os/shell": "0.3.5"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@astrale-os/shell": ">=0.2.0 <1.0.0",
package/src/index.ts CHANGED
@@ -117,8 +117,14 @@ export type {
117
117
  } from './window/index.js'
118
118
 
119
119
  // ─── intents (§11) ───────────────────────────────────────────────────────────
120
- export { useIntent, useSendIntent } from './intents/index.js'
121
- export type { IntentCtx, IntentPayload, IntentReply, SendIntent } from './intents/index.js'
120
+ export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js'
121
+ export type {
122
+ EmitIntent,
123
+ IntentCtx,
124
+ IntentPayload,
125
+ IntentReply,
126
+ SendIntent,
127
+ } from './intents/index.js'
122
128
 
123
129
  // ─── application — the OS plane (§12) ────────────────────────────────────────
124
130
  export {
@@ -0,0 +1,42 @@
1
+ import type { IntentRegistry } from '@astrale-os/shell'
2
+
3
+ import { useCallback } from 'react'
4
+
5
+ import type { IntentPayload } from './envelope.js'
6
+
7
+ import { useShell } from '../session/shell.hook.js'
8
+ import { intentMessage } from './envelope.js'
9
+ import { resolveIntentTarget } from './resolve-intent-target.js'
10
+
11
+ /** Fire-and-forget intent sender returned by {@link useEmitIntent}. */
12
+ export interface EmitIntent {
13
+ <K extends keyof IntentRegistry>(
14
+ name: K,
15
+ payload: IntentPayload<K>,
16
+ /** `to`: `'parent'` (reserved) or a child windowId. Default: the parent, if any. */
17
+ opts?: { to?: 'parent' | string },
18
+ ): void
19
+ }
20
+
21
+ /**
22
+ * Return a stable sender for intents that do not need a reply. Emitted messages
23
+ * carry no correlation id, so listeners can handle navigation and notifications
24
+ * without retaining a pending request.
25
+ */
26
+ export function useEmitIntent(): EmitIntent {
27
+ const shell = useShell()
28
+ return useCallback(
29
+ <K extends keyof IntentRegistry>(
30
+ name: K,
31
+ payload: IntentPayload<K>,
32
+ opts?: { to?: 'parent' | string },
33
+ ): void => {
34
+ const target = resolveIntentTarget(shell, opts?.to)
35
+ if (target.kind === 'invalid') throw new Error(`useEmitIntent: ${target.reason}`)
36
+ const message = intentMessage(name, payload)
37
+ if (target.kind === 'parent') shell.parent!.send(message)
38
+ else shell.children.send(target.windowId, message)
39
+ },
40
+ [shell],
41
+ )
42
+ }
@@ -1,6 +1,8 @@
1
1
  // intents — API §11 (useIntent, useSendIntent). See SHELL_REACT_API.md.
2
2
  export { useIntent } from './listen-intent.hook.js'
3
3
  export type { IntentCtx } from './listen-intent.hook.js'
4
+ export { useEmitIntent } from './emit-intent.hook.js'
5
+ export type { EmitIntent } from './emit-intent.hook.js'
4
6
  export { useSendIntent } from './send-intent.hook.js'
5
7
  export type { SendIntent } from './send-intent.hook.js'
6
8
  export type { IntentPayload, IntentReply } from './envelope.js'
@@ -0,0 +1,20 @@
1
+ import type { Shell } from '@astrale-os/shell'
2
+
3
+ export type IntentTarget =
4
+ | { readonly kind: 'parent' }
5
+ | { readonly kind: 'child'; readonly windowId: string }
6
+ | { readonly kind: 'invalid'; readonly reason: string }
7
+
8
+ export function resolveIntentTarget(shell: Shell, to: string | undefined): IntentTarget {
9
+ if (to === undefined) {
10
+ return shell.parent !== null
11
+ ? { kind: 'parent' }
12
+ : { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' }
13
+ }
14
+ if (to === 'parent') {
15
+ return shell.parent !== null
16
+ ? { kind: 'parent' }
17
+ : { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" }
18
+ }
19
+ return { kind: 'child', windowId: to }
20
+ }
@@ -1,4 +1,4 @@
1
- import type { IntentMessage, IntentRegistry, Shell } from '@astrale-os/shell'
1
+ import type { IntentMessage, IntentRegistry } from '@astrale-os/shell'
2
2
 
3
3
  /**
4
4
  * useSendIntent (API §11) — a stable typed sender. Each call attaches a
@@ -13,6 +13,7 @@ import type { IntentPayload, IntentReply } from './envelope.js'
13
13
 
14
14
  import { useShell } from '../session/shell.hook.js'
15
15
  import { INTENT_REPLY, NOOP, intentMessage } from './envelope.js'
16
+ import { resolveIntentTarget } from './resolve-intent-target.js'
16
17
 
17
18
  /** The typed sender returned by {@link useSendIntent}. */
18
19
  export interface SendIntent {
@@ -24,25 +25,6 @@ export interface SendIntent {
24
25
  ): Promise<IntentReply<K> | void>
25
26
  }
26
27
 
27
- type Target =
28
- | { readonly kind: 'parent' }
29
- | { readonly kind: 'child'; readonly windowId: string }
30
- | { readonly kind: 'invalid'; readonly reason: string }
31
-
32
- function resolveTarget(shell: Shell, to: string | undefined): Target {
33
- if (to === undefined) {
34
- return shell.parent !== null
35
- ? { kind: 'parent' }
36
- : { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' }
37
- }
38
- if (to === 'parent') {
39
- return shell.parent !== null
40
- ? { kind: 'parent' }
41
- : { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" }
42
- }
43
- return { kind: 'child', windowId: to }
44
- }
45
-
46
28
  export function useSendIntent(): SendIntent {
47
29
  const shell = useShell()
48
30
  // Unmount settlers for in-flight sends, so a pending reply resolves on teardown.
@@ -61,7 +43,7 @@ export function useSendIntent(): SendIntent {
61
43
  payload: IntentPayload<K>,
62
44
  opts?: { to?: 'parent' | string },
63
45
  ): Promise<IntentReply<K> | void> => {
64
- const target = resolveTarget(shell, opts?.to)
46
+ const target = resolveIntentTarget(shell, opts?.to)
65
47
  if (target.kind === 'invalid') {
66
48
  return Promise.reject(new Error(`useSendIntent: ${target.reason}`))
67
49
  }
@@ -69,7 +51,7 @@ export function useSendIntent(): SendIntent {
69
51
  const message = intentMessage(name, payload, correlationId)
70
52
  const pending = pendingRef.current
71
53
 
72
- return new Promise<IntentReply<K> | void>((resolve) => {
54
+ return new Promise<IntentReply<K> | void>((resolve, reject) => {
73
55
  let done = false
74
56
  let offReply = NOOP
75
57
  let offClose = NOOP
@@ -82,10 +64,26 @@ export function useSendIntent(): SendIntent {
82
64
  pending.delete(onUnmount)
83
65
  resolve(value)
84
66
  }
67
+ function fail(error: Error): void {
68
+ if (done) return
69
+ done = true
70
+ offReply()
71
+ offClose()
72
+ pending.delete(onUnmount)
73
+ reject(error)
74
+ }
85
75
  const onReply = (m: IntentMessage): void => {
86
76
  if (m.envelope.name !== INTENT_REPLY) return
87
- const reply = m.envelope.payload as { correlationId: string; result: unknown }
77
+ const reply = m.envelope.payload as {
78
+ correlationId: string
79
+ result?: unknown
80
+ error?: { message: string }
81
+ }
88
82
  if (reply.correlationId !== correlationId) return
83
+ if (reply.error) {
84
+ fail(new Error(reply.error.message))
85
+ return
86
+ }
89
87
  settle(reply.result as IntentReply<K>)
90
88
  }
91
89
 
@@ -1,6 +1,6 @@
1
1
  import type { PathLike } from '@astrale-os/kernel-client/graph'
2
+ import type { Shell } from '@astrale-os/shell'
2
3
 
3
- import { K } from '@astrale-os/kernel-core'
4
4
  /**
5
5
  * useViewsFor (API §10) — "what Views can render this node", as mountable
6
6
  * `ViewDescriptor`s for the open-with menu. Backed one-shot (`live:false`) by the
@@ -13,35 +13,22 @@ import type { ReadState } from '../../graph/memory/read/envelope.js'
13
13
  import type { Node, ViewDescriptor } from '../lifecycle/mount.js'
14
14
 
15
15
  import { useAsyncResource } from '../../graph/async.js'
16
- import { useKernelClient } from '../../session/kernel-client.hook.js'
16
+ import { useShell } from '../../session/shell.hook.js'
17
17
 
18
18
  export interface ViewsForResult extends ReadState {
19
19
  readonly views: readonly ViewDescriptor[]
20
20
  }
21
21
 
22
- /** The `View.resolve` wire address — the qualified static-method slug from the schema. */
23
- const VIEW_RESOLVE_METHOD = K.View.resolve.path.method.raw
24
-
25
- /** The kernel `View.resolve` row shape (id/path/url/name?/handshake?/origin). */
26
- interface ViewOut {
27
- readonly id: string
28
- readonly path: string
29
- readonly url: string
30
- readonly name?: string
31
- readonly handshake?: 'shell' | 'none'
32
- readonly origin: 'self' | 'class'
33
- }
34
-
35
22
  const NO_VIEWS: readonly ViewDescriptor[] = Object.freeze([])
36
23
 
37
24
  export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForResult {
38
- const kernel = useKernelClient()
25
+ const shell = useShell()
39
26
  const target = resolvePath(node)
40
27
 
41
28
  const res = useAsyncResource<readonly ViewDescriptor[]>(
42
- target === null ? null : () => resolveViews(kernel, target),
29
+ target === null ? null : () => resolveViews(shell, target),
43
30
  NO_VIEWS,
44
- [kernel, target],
31
+ [shell, target],
45
32
  )
46
33
 
47
34
  return useMemo<ViewsForResult>(
@@ -58,12 +45,9 @@ export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForR
58
45
  }
59
46
 
60
47
  /** Dispatch `View.resolve` and project its rows to mountable `ViewDescriptor`s. */
61
- async function resolveViews(
62
- kernel: { call(method: string, params: unknown): Promise<unknown> },
63
- node: string,
64
- ): Promise<readonly ViewDescriptor[]> {
65
- const rows = (await kernel.call(VIEW_RESOLVE_METHOD, { node })) as ViewOut[] | null | undefined
66
- if (rows === null || rows === undefined || rows.length === 0) return NO_VIEWS
48
+ async function resolveViews(shell: Shell, node: string): Promise<readonly ViewDescriptor[]> {
49
+ const rows = await shell.views.resolve(node)
50
+ if (rows.length === 0) return NO_VIEWS
67
51
  return rows.map((r) => ({ id: r.id, path: r.path, url: r.url, handshake: r.handshake }))
68
52
  }
69
53