@astrale-os/shell-react 0.2.9 → 0.2.10-beta.3
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/application/apps/app-host.d.ts +4 -15
- package/dist/application/apps/app-host.d.ts.map +1 -1
- package/dist/application/apps/app-host.js +1 -1
- package/dist/application/apps/apps.hook.d.ts +7 -4
- package/dist/application/apps/apps.hook.d.ts.map +1 -1
- package/dist/application/apps/apps.hook.js +10 -2
- package/dist/application/apps/domains.hook.d.ts +1 -1
- package/dist/application/apps/domains.hook.d.ts.map +1 -1
- package/dist/application/apps/domains.hook.js +1 -1
- package/dist/application/apps/open-app.hook.d.ts +2 -3
- package/dist/application/apps/open-app.hook.d.ts.map +1 -1
- package/dist/application/index.d.ts +2 -4
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +2 -3
- package/dist/application/spaces/space.hook.d.ts +2 -2
- package/dist/application/spaces/space.hook.d.ts.map +1 -1
- package/dist/application/spaces/space.hook.js +2 -2
- package/dist/application/spaces/spaces.hook.d.ts +2 -3
- package/dist/application/spaces/spaces.hook.d.ts.map +1 -1
- package/dist/application/spaces/spaces.hook.js +10 -3
- package/dist/auth/act-as.context.d.ts +15 -32
- package/dist/auth/act-as.context.d.ts.map +1 -1
- package/dist/auth/act-as.context.js +22 -66
- package/dist/auth/auth.hook.d.ts +1 -0
- package/dist/auth/auth.hook.d.ts.map +1 -1
- package/dist/auth/auth.hook.js +2 -22
- package/dist/auth/can.hook.d.ts +10 -38
- package/dist/auth/can.hook.d.ts.map +1 -1
- package/dist/auth/can.hook.js +35 -110
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/self.hook.d.ts +2 -2
- package/dist/auth/self.hook.d.ts.map +1 -1
- package/dist/auth/self.hook.js +0 -7
- package/dist/auth/session-kernel.d.ts +2 -3
- package/dist/auth/session-kernel.d.ts.map +1 -1
- package/dist/auth/session-kernel.js +4 -13
- package/dist/components/astrale.d.ts +14 -0
- package/dist/components/astrale.d.ts.map +1 -0
- package/dist/components/astrale.js +12 -0
- package/dist/components/boundaries.d.ts +47 -0
- package/dist/components/boundaries.d.ts.map +1 -0
- package/dist/components/boundaries.js +55 -0
- package/dist/components/can.d.ts +11 -0
- package/dist/components/can.d.ts.map +1 -0
- package/dist/components/can.js +5 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +3 -0
- package/dist/graph/call/call.hook.d.ts +8 -18
- package/dist/graph/call/call.hook.d.ts.map +1 -1
- package/dist/graph/call/call.hook.js +7 -13
- package/dist/graph/call/stream.hook.d.ts +12 -20
- package/dist/graph/call/stream.hook.d.ts.map +1 -1
- package/dist/graph/call/stream.hook.js +55 -111
- package/dist/graph/graph.context.d.ts +4 -3
- package/dist/graph/graph.context.d.ts.map +1 -1
- package/dist/graph/graph.context.js +2 -8
- package/dist/graph/guards.error.d.ts +7 -8
- package/dist/graph/guards.error.d.ts.map +1 -1
- package/dist/graph/guards.error.js +11 -7
- package/dist/graph/index.d.ts +8 -16
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +3 -7
- package/dist/graph/memory/read/envelope.d.ts +22 -54
- package/dist/graph/memory/read/envelope.d.ts.map +1 -1
- package/dist/graph/memory/read/envelope.js +26 -20
- package/dist/graph/memory/read/idle.d.ts +1 -15
- package/dist/graph/memory/read/idle.d.ts.map +1 -1
- package/dist/graph/memory/read/idle.js +4 -13
- package/dist/graph/query-definition.hook.d.ts +11 -0
- package/dist/graph/query-definition.hook.d.ts.map +1 -0
- package/dist/graph/query-definition.hook.js +205 -0
- package/dist/graph/resource.d.ts +34 -0
- package/dist/graph/resource.d.ts.map +1 -0
- package/dist/graph/resource.js +244 -0
- package/dist/graph/store.d.ts +25 -0
- package/dist/graph/store.d.ts.map +1 -0
- package/dist/graph/store.js +69 -0
- package/dist/host/actions.hook.d.ts +34 -0
- package/dist/host/actions.hook.d.ts.map +1 -0
- package/dist/host/actions.hook.js +125 -0
- package/dist/host/components.d.ts +15 -0
- package/dist/host/components.d.ts.map +1 -0
- package/dist/host/components.js +14 -0
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +3 -0
- package/dist/index.d.ts +26 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -31
- package/dist/intents/envelope.d.ts +7 -19
- package/dist/intents/envelope.d.ts.map +1 -1
- package/dist/intents/envelope.js +2 -3
- package/dist/intents/send-intent.hook.d.ts +5 -7
- package/dist/intents/send-intent.hook.d.ts.map +1 -1
- package/dist/intents/send-intent.hook.js +17 -75
- package/dist/navigation/adapters/adapter.d.ts +5 -0
- package/dist/navigation/adapters/adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/memory.adapter.js +4 -0
- package/dist/navigation/adapters/url.adapter.d.ts.map +1 -1
- package/dist/navigation/adapters/url.adapter.js +57 -10
- package/dist/navigation/codec.d.ts +60 -0
- package/dist/navigation/codec.d.ts.map +1 -0
- package/dist/navigation/codec.js +121 -0
- package/dist/navigation/history.d.ts +11 -9
- package/dist/navigation/history.d.ts.map +1 -1
- package/dist/navigation/history.js +6 -8
- package/dist/navigation/hooks/ancestors.hook.d.ts +5 -7
- package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/ancestors.hook.js +7 -51
- package/dist/navigation/hooks/location.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/location.hook.js +4 -6
- package/dist/navigation/hooks/scope.hook.d.ts.map +1 -1
- package/dist/navigation/hooks/scope.hook.js +4 -6
- package/dist/navigation/index.d.ts +4 -2
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +2 -1
- package/dist/navigation/link.d.ts +12 -0
- package/dist/navigation/link.d.ts.map +1 -1
- package/dist/navigation/link.js +13 -2
- package/dist/navigation/location.d.ts +13 -40
- package/dist/navigation/location.d.ts.map +1 -1
- package/dist/navigation/location.js +14 -76
- package/dist/navigation/scope.context.d.ts +17 -26
- package/dist/navigation/scope.context.d.ts.map +1 -1
- package/dist/navigation/scope.context.js +140 -108
- package/dist/navigation/snapshot.d.ts +14 -19
- package/dist/navigation/snapshot.d.ts.map +1 -1
- package/dist/navigation/snapshot.js +48 -44
- package/dist/schema/action.hook.d.ts +44 -0
- package/dist/schema/action.hook.d.ts.map +1 -0
- package/dist/schema/action.hook.js +72 -0
- package/dist/schema/domain.hook.d.ts +12 -7
- package/dist/schema/domain.hook.d.ts.map +1 -1
- package/dist/schema/domain.hook.js +101 -4
- package/dist/schema/index.d.ts +9 -8
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +5 -7
- package/dist/schema/kernel.hook.d.ts +4 -9
- package/dist/schema/kernel.hook.d.ts.map +1 -1
- package/dist/schema/kernel.hook.js +4 -6
- package/dist/schema/mutation.hook.d.ts +9 -0
- package/dist/schema/mutation.hook.d.ts.map +1 -0
- package/dist/schema/mutation.hook.js +41 -0
- package/dist/schema/object.hook.d.ts +16 -0
- package/dist/schema/object.hook.d.ts.map +1 -0
- package/dist/schema/object.hook.js +46 -0
- package/dist/schema/relation.hook.d.ts +46 -0
- package/dist/schema/relation.hook.d.ts.map +1 -0
- package/dist/schema/relation.hook.js +136 -0
- package/dist/selection/live.d.ts +9 -17
- package/dist/selection/live.d.ts.map +1 -1
- package/dist/selection/live.js +65 -112
- package/dist/selection/scope.context.js +5 -4
- package/dist/session/boot/boot.d.ts +1 -13
- package/dist/session/boot/boot.d.ts.map +1 -1
- package/dist/session/boot/boot.js +9 -27
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/kernel-instance.hook.d.ts +1 -4
- package/dist/session/kernel-instance.hook.d.ts.map +1 -1
- package/dist/session/kernel-instance.hook.js +1 -5
- package/dist/session/provider.d.ts +9 -23
- package/dist/session/provider.d.ts.map +1 -1
- package/dist/session/provider.js +29 -75
- package/dist/session/session.context.d.ts +5 -0
- package/dist/session/session.context.d.ts.map +1 -1
- package/dist/window/adopt.d.ts +0 -1
- package/dist/window/adopt.d.ts.map +1 -1
- package/dist/window/hooks/target-node.hook.d.ts +10 -6
- package/dist/window/hooks/target-node.hook.d.ts.map +1 -1
- package/dist/window/hooks/target-node.hook.js +14 -72
- package/dist/window/hooks/views-for.hook.d.ts +4 -3
- package/dist/window/hooks/views-for.hook.d.ts.map +1 -1
- package/dist/window/hooks/views-for.hook.js +6 -22
- package/dist/window/hooks/windows.hook.d.ts.map +1 -1
- package/dist/window/hooks/windows.hook.js +3 -4
- package/dist/window/host.d.ts +9 -11
- package/dist/window/host.d.ts.map +1 -1
- package/dist/window/host.js +26 -62
- package/dist/window/hub.d.ts +2 -0
- package/dist/window/hub.d.ts.map +1 -1
- package/dist/window/hub.js +33 -0
- package/dist/window/index.d.ts +2 -2
- package/dist/window/index.d.ts.map +1 -1
- package/dist/window/index.js +2 -2
- package/dist/window/lifecycle/mount.d.ts +5 -44
- package/dist/window/lifecycle/mount.d.ts.map +1 -1
- package/dist/window/lifecycle/mount.js +14 -120
- package/dist/window/lifecycle/states.d.ts +0 -5
- package/dist/window/lifecycle/states.d.ts.map +1 -1
- package/package.json +20 -8
- package/src/application/apps/app-host.tsx +11 -15
- package/src/application/apps/apps.hook.ts +13 -6
- package/src/application/apps/domains.hook.ts +1 -1
- package/src/application/apps/open-app.hook.ts +3 -4
- package/src/application/index.ts +4 -6
- package/src/application/spaces/space.hook.ts +5 -6
- package/src/application/spaces/spaces.hook.ts +14 -9
- package/src/auth/act-as.context.tsx +37 -91
- package/src/auth/auth.hook.ts +2 -30
- package/src/auth/can.hook.ts +44 -135
- package/src/auth/index.ts +1 -0
- package/src/auth/self.hook.ts +2 -9
- package/src/auth/session-kernel.ts +6 -16
- package/src/components/astrale.tsx +40 -0
- package/src/components/boundaries.tsx +113 -0
- package/src/components/can.tsx +16 -0
- package/src/components/index.ts +13 -0
- package/src/graph/call/call.hook.ts +22 -27
- package/src/graph/call/stream.hook.ts +66 -143
- package/src/graph/graph.context.ts +6 -11
- package/src/graph/guards.error.ts +17 -55
- package/src/graph/index.ts +23 -17
- package/src/graph/memory/read/envelope.ts +40 -65
- package/src/graph/memory/read/idle.ts +5 -18
- package/src/graph/query-definition.hook.ts +270 -0
- package/src/graph/resource.ts +358 -0
- package/src/graph/store.ts +97 -0
- package/src/host/actions.hook.ts +165 -0
- package/src/host/components.tsx +29 -0
- package/src/host/index.ts +17 -0
- package/src/index.ts +131 -124
- package/src/intents/envelope.ts +7 -23
- package/src/intents/send-intent.hook.ts +24 -79
- package/src/navigation/adapters/adapter.ts +3 -0
- package/src/navigation/adapters/memory.adapter.ts +4 -0
- package/src/navigation/adapters/url.adapter.ts +48 -8
- package/src/navigation/codec.ts +190 -0
- package/src/navigation/history.ts +20 -11
- package/src/navigation/hooks/ancestors.hook.ts +10 -60
- package/src/navigation/hooks/location.hook.ts +4 -6
- package/src/navigation/hooks/scope.hook.ts +4 -6
- package/src/navigation/index.ts +18 -2
- package/src/navigation/link.tsx +27 -2
- package/src/navigation/location.ts +26 -93
- package/src/navigation/scope.context.tsx +151 -136
- package/src/navigation/snapshot.ts +69 -50
- package/src/schema/action.hook.ts +146 -0
- package/src/schema/domain.hook.ts +127 -11
- package/src/schema/index.ts +9 -30
- package/src/schema/kernel.hook.ts +6 -13
- package/src/schema/mutation.hook.ts +63 -0
- package/src/schema/object.hook.ts +85 -0
- package/src/schema/relation.hook.ts +253 -0
- package/src/selection/live.ts +75 -125
- package/src/selection/scope.context.tsx +4 -4
- package/src/session/boot/boot.ts +8 -33
- package/src/session/index.ts +1 -0
- package/src/session/kernel-instance.hook.ts +2 -11
- package/src/session/provider.tsx +34 -103
- package/src/session/session.context.ts +6 -0
- package/src/window/adopt.ts +0 -1
- package/src/window/hooks/target-node.hook.ts +20 -85
- package/src/window/hooks/views-for.hook.ts +12 -25
- package/src/window/hooks/windows.hook.ts +3 -4
- package/src/window/host.tsx +35 -77
- package/src/window/hub.ts +33 -0
- package/src/window/index.ts +3 -3
- package/src/window/lifecycle/mount.ts +15 -160
- package/src/window/lifecycle/states.ts +1 -5
- package/dist/application/roles.hook.d.ts +0 -7
- package/dist/application/roles.hook.d.ts.map +0 -1
- package/dist/application/roles.hook.js +0 -23
- package/dist/graph/memory/base.d.ts +0 -21
- package/dist/graph/memory/base.d.ts.map +0 -1
- package/dist/graph/memory/base.js +0 -33
- package/dist/graph/memory/read/read.d.ts +0 -9
- package/dist/graph/memory/read/read.d.ts.map +0 -1
- package/dist/graph/memory/read/read.js +0 -87
- package/dist/graph/memory/scope.d.ts +0 -7
- package/dist/graph/memory/scope.d.ts.map +0 -1
- package/dist/graph/memory/scope.js +0 -25
- package/dist/graph/mutate/move.hook.d.ts +0 -7
- package/dist/graph/mutate/move.hook.d.ts.map +0 -1
- package/dist/graph/mutate/move.hook.js +0 -49
- package/dist/graph/mutate/mutate.hook.d.ts +0 -17
- package/dist/graph/mutate/mutate.hook.d.ts.map +0 -1
- package/dist/graph/mutate/mutate.hook.js +0 -45
- package/dist/graph/query/children.hook.d.ts +0 -29
- package/dist/graph/query/children.hook.d.ts.map +0 -1
- package/dist/graph/query/children.hook.js +0 -64
- package/dist/graph/query/graph.hook.d.ts +0 -9
- package/dist/graph/query/graph.hook.d.ts.map +0 -1
- package/dist/graph/query/graph.hook.js +0 -5
- package/dist/graph/query/neighbors.hook.d.ts +0 -7
- package/dist/graph/query/neighbors.hook.d.ts.map +0 -1
- package/dist/graph/query/neighbors.hook.js +0 -104
- package/dist/graph/query/node.hook.d.ts +0 -14
- package/dist/graph/query/node.hook.d.ts.map +0 -1
- package/dist/graph/query/node.hook.js +0 -43
- package/dist/graph/query/query.hook.d.ts +0 -14
- package/dist/graph/query/query.hook.d.ts.map +0 -1
- package/dist/graph/query/query.hook.js +0 -23
- package/dist/schema/bind-read.d.ts +0 -37
- package/dist/schema/bind-read.d.ts.map +0 -1
- package/dist/schema/bind-read.js +0 -205
- package/dist/schema/binding.d.ts +0 -21
- package/dist/schema/binding.d.ts.map +0 -1
- package/dist/schema/binding.js +0 -99
- package/dist/schema/function.hook.d.ts +0 -9
- package/dist/schema/function.hook.d.ts.map +0 -1
- package/dist/schema/function.hook.js +0 -40
- package/dist/schema/method.hook.d.ts +0 -14
- package/dist/schema/method.hook.d.ts.map +0 -1
- package/dist/schema/method.hook.js +0 -49
- package/dist/schema/tag.d.ts +0 -11
- package/dist/schema/tag.d.ts.map +0 -1
- package/dist/schema/tag.js +0 -11
- package/dist/schema/types.d.ts +0 -73
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js +0 -1
- package/dist/schema/write.d.ts +0 -16
- package/dist/schema/write.d.ts.map +0 -1
- package/dist/schema/write.js +0 -56
- package/src/application/roles.hook.ts +0 -41
- package/src/graph/memory/base.ts +0 -50
- package/src/graph/memory/read/read.ts +0 -103
- package/src/graph/memory/scope.ts +0 -29
- package/src/graph/mutate/move.hook.ts +0 -70
- package/src/graph/mutate/mutate.hook.ts +0 -69
- package/src/graph/query/children.hook.ts +0 -136
- package/src/graph/query/graph.hook.ts +0 -13
- package/src/graph/query/neighbors.hook.ts +0 -159
- package/src/graph/query/node.hook.ts +0 -83
- package/src/graph/query/query.hook.ts +0 -42
- package/src/schema/bind-read.ts +0 -319
- package/src/schema/binding.ts +0 -146
- package/src/schema/function.hook.ts +0 -68
- package/src/schema/method.hook.ts +0 -103
- package/src/schema/tag.ts +0 -19
- package/src/schema/types.ts +0 -150
- package/src/schema/write.ts +0 -96
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ApplicationNode, SpaceNode } from '@astrale-os/shell'
|
|
1
|
+
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import type { ApplicationNode, NodeTarget, SpaceNode } from '@astrale-os/shell'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
5
5
|
/**
|
|
6
6
|
* useApps(space?) (API §12.2) — the apps installed in a space (`installed_in`
|
|
7
7
|
* in-edges). No arg targets the CURRENT space (shared store, so a switch anywhere
|
|
@@ -26,7 +26,7 @@ export interface AppsResult extends ReadState {
|
|
|
26
26
|
readonly apps: readonly ApplicationNode[]
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export function useApps(space?: SpaceNode |
|
|
29
|
+
export function useApps(space?: SpaceNode | NodeTarget | null): AppsResult {
|
|
30
30
|
const shell = useShell()
|
|
31
31
|
const available = isOsPlaneAvailable(shell)
|
|
32
32
|
|
|
@@ -76,6 +76,13 @@ function hasId(x: unknown): x is { readonly id: string } {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/** An `@id` ref for a bound node, else the normalized ref of a raw subject. */
|
|
79
|
-
export function toRef(x:
|
|
80
|
-
return hasId(x) ?
|
|
79
|
+
export function toRef(x: NodeTarget | { readonly id: string }): NodeTarget {
|
|
80
|
+
return hasId(x) ? x.id : x
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Stable dependency identity for one Shell node target. */
|
|
84
|
+
export function targetKey(x: NodeTarget | { readonly id: string }): string {
|
|
85
|
+
if (hasId(x)) return `id:${x.id}`
|
|
86
|
+
if (typeof x === 'string') return `text:${x}`
|
|
87
|
+
return `path:${Path.from(x as PathLike).raw}`
|
|
81
88
|
}
|
|
@@ -14,7 +14,7 @@ export interface DomainsResult extends ReadState {
|
|
|
14
14
|
readonly domains: readonly DomainSearchResult[]
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function
|
|
17
|
+
export function useInstalledDomains(): DomainsResult {
|
|
18
18
|
const shell = useShell()
|
|
19
19
|
const available = isOsPlaneAvailable(shell)
|
|
20
20
|
const result = useAsyncResource<readonly DomainSearchResult[]>(
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* app-open protocol lives in the shell package). Resolves to the session; disposal
|
|
4
4
|
* is the caller's (AppHost disposes it with the window).
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
7
|
-
import type { ApplicationNode, AppSession } from '@astrale-os/shell'
|
|
6
|
+
import type { ApplicationNode, AppSession, NodeTarget } from '@astrale-os/shell'
|
|
8
7
|
|
|
9
8
|
import type { Action } from '../../graph/action.js'
|
|
10
9
|
|
|
@@ -12,7 +11,7 @@ import { useAction } from '../../graph/action.js'
|
|
|
12
11
|
import { useShell } from '../../session/shell.hook.js'
|
|
13
12
|
import { toRef } from './apps.hook.js'
|
|
14
13
|
|
|
15
|
-
export function useOpenApp(): Action<[ApplicationNode |
|
|
14
|
+
export function useOpenApp(): Action<[ApplicationNode | NodeTarget], AppSession> {
|
|
16
15
|
const shell = useShell()
|
|
17
|
-
return useAction((app: ApplicationNode |
|
|
16
|
+
return useAction((app: ApplicationNode | NodeTarget) => shell.apps.open(toRef(app)))
|
|
18
17
|
}
|
package/src/application/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// application — API §12.2. The OS-plane hooks over the shell package's
|
|
2
2
|
// `shell.user/spaces/apps` namespaces + the window module: useUser, useSpaces,
|
|
3
|
-
// useCurrentSpace, useSpace, useApps,
|
|
3
|
+
// useCurrentSpace, useSpace, useApps, useInstalledDomains, useOpenApp,
|
|
4
4
|
// AppHost, AppLogo.
|
|
5
5
|
export { useUser } from './user/user.hook.js'
|
|
6
6
|
export { useSpaces } from './spaces/spaces.hook.js'
|
|
@@ -11,22 +11,20 @@ export { useSpace } from './spaces/space.hook.js'
|
|
|
11
11
|
export type { SpaceResult } from './spaces/space.hook.js'
|
|
12
12
|
export { useApps } from './apps/apps.hook.js'
|
|
13
13
|
export type { AppsResult } from './apps/apps.hook.js'
|
|
14
|
-
export {
|
|
14
|
+
export { useInstalledDomains } from './apps/domains.hook.js'
|
|
15
15
|
export type { DomainsResult } from './apps/domains.hook.js'
|
|
16
16
|
export { useOpenApp } from './apps/open-app.hook.js'
|
|
17
17
|
export { AppHost } from './apps/app-host.js'
|
|
18
18
|
export type { AppHostFallbackState, AppHostProps } from './apps/app-host.js'
|
|
19
19
|
export { AppLogo } from './apps/app-logo.js'
|
|
20
20
|
export type { AppLogoProps } from './apps/app-logo.js'
|
|
21
|
-
export { useRoles } from './roles.hook.js'
|
|
22
|
-
export type { RolesResult } from './roles.hook.js'
|
|
23
|
-
|
|
24
21
|
// The OS node/session types (from the shell package, single import surface).
|
|
25
22
|
export type {
|
|
26
23
|
ApplicationNode,
|
|
27
24
|
AppSession,
|
|
28
25
|
DomainSearchResult,
|
|
29
|
-
|
|
26
|
+
NodeTarget,
|
|
27
|
+
ResolvedView,
|
|
30
28
|
SpaceNode,
|
|
31
29
|
UserNode,
|
|
32
30
|
} from '@astrale-os/shell'
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { SpaceNode } from '@astrale-os/shell'
|
|
1
|
+
import type { NodeTarget, SpaceNode } from '@astrale-os/shell'
|
|
3
2
|
|
|
4
|
-
import { normalizeRef } from '@astrale-os/kernel-client'
|
|
5
3
|
/**
|
|
6
4
|
* useSpace(ref) (API §12.2) — a single space by ref; `null`/`undefined` (or an
|
|
7
5
|
* unavailable OS plane) is idle (§1.3). One-shot today (§12 flag).
|
|
@@ -13,17 +11,18 @@ import type { CurrentSpaceResult } from './current-space.hook.js'
|
|
|
13
11
|
import { useAsyncResource } from '../../graph/async.js'
|
|
14
12
|
import { isIdleInput } from '../../graph/memory/read/idle.js'
|
|
15
13
|
import { useShell } from '../../session/shell.hook.js'
|
|
14
|
+
import { targetKey } from '../apps/apps.hook.js'
|
|
16
15
|
import { isOsPlaneAvailable } from '../user/user.store.js'
|
|
17
16
|
|
|
18
17
|
export type SpaceResult = CurrentSpaceResult
|
|
19
18
|
|
|
20
|
-
export function useSpace(ref:
|
|
19
|
+
export function useSpace(ref: NodeTarget | null | undefined): SpaceResult {
|
|
21
20
|
const shell = useShell()
|
|
22
21
|
const idle = isIdleInput(ref) || !isOsPlaneAvailable(shell)
|
|
23
22
|
// The inline guard (not the stored `idle`) narrows `ref` to non-null below.
|
|
24
|
-
const key = idle ? null :
|
|
23
|
+
const key = idle ? null : targetKey(ref as NodeTarget)
|
|
25
24
|
const res = useAsyncResource<SpaceNode | null>(
|
|
26
|
-
idle ? null : () => shell.spaces.get(ref as
|
|
25
|
+
idle ? null : () => shell.spaces.get(ref as NodeTarget),
|
|
27
26
|
null,
|
|
28
27
|
[shell, key],
|
|
29
28
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { SpaceNode } from '@astrale-os/shell'
|
|
1
|
+
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
2
|
+
import type { NodeTarget, SpaceNode } from '@astrale-os/shell'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
5
5
|
/**
|
|
6
6
|
* useSpaces (API §12.2) — the spaces list, the selected space, and a `switch`
|
|
7
7
|
* Action that repoints the `selected_space` edge. Reads the SAME per-Shell
|
|
@@ -28,7 +28,7 @@ export interface SpacesResult extends ReadState {
|
|
|
28
28
|
/** The `selected_space` target — optimistically ahead during a `switch`. */
|
|
29
29
|
readonly selected: SpaceNode | null
|
|
30
30
|
/** `user::switchSpace` with a manual optimistic overlay (§1.6). */
|
|
31
|
-
readonly switch: Action<[
|
|
31
|
+
readonly switch: Action<[NodeTarget], void>
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function useSpaces(): SpacesResult {
|
|
@@ -52,7 +52,7 @@ export function useSpaces(): SpacesResult {
|
|
|
52
52
|
|
|
53
53
|
const spaces = spacesRes.data
|
|
54
54
|
|
|
55
|
-
const switchAction = useAction(async (ref:
|
|
55
|
+
const switchAction = useAction(async (ref: NodeTarget): Promise<void> => {
|
|
56
56
|
// Optimistic override on the SHARED store, so useCurrentSpace / useApps ahead
|
|
57
57
|
// of the round-trip too (§12.2). Only overlay a known target (a path ref that
|
|
58
58
|
// matches nothing in the list simply skips the preview).
|
|
@@ -94,7 +94,7 @@ export function useSpaces(): SpacesResult {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/** The space in `spaces` denoted by `ref`, or `null` (path refs don't match by id). */
|
|
97
|
-
function findSpace(spaces: readonly SpaceNode[], ref:
|
|
97
|
+
function findSpace(spaces: readonly SpaceNode[], ref: NodeTarget): SpaceNode | null {
|
|
98
98
|
const id = refIdOf(ref)
|
|
99
99
|
if (id === null) return null
|
|
100
100
|
return spaces.find((s) => s.id === id) ?? null
|
|
@@ -106,8 +106,13 @@ function hasId(x: unknown): x is { readonly id: string } {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/** The graph id a subject denotes, or `null` when it is a path (no id to match). */
|
|
109
|
-
function refIdOf(x:
|
|
109
|
+
function refIdOf(x: NodeTarget | { readonly id: string }): string | null {
|
|
110
110
|
if (hasId(x)) return x.id
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
if (typeof x === 'string' && !x.startsWith('@') && !x.startsWith('/')) return x
|
|
112
|
+
try {
|
|
113
|
+
const path = Path.from(x as PathLike)
|
|
114
|
+
return path.ast.anchor.kind === 'id' && path.ast.steps.length === 0 ? path.ast.anchor.id : null
|
|
115
|
+
} catch {
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
113
118
|
}
|
|
@@ -1,124 +1,70 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Identity } from '@astrale-os/kernel-client/auth'
|
|
2
|
+
import type { KernelClient } from '@astrale-os/shell'
|
|
3
3
|
import type { ReactElement, ReactNode } from 'react'
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* The acting-principal binding (API §4). `IdentityContext` seats the effective
|
|
7
|
-
* principal a subtree dispatches as — its bound kernel view + whoami + memory
|
|
8
|
-
* scope; `useIdentityBinding` resolves it (an `<ActAs>` override, else the root
|
|
9
|
-
* off ShellContext). `<ActAs>` rebinds a subtree to another principal with a
|
|
10
|
-
* DISTINCT memory scope so results never leak across principals (§5.4).
|
|
11
|
-
*/
|
|
12
5
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
|
13
6
|
|
|
14
|
-
import type { SelfNode } from '../session/session.context.js'
|
|
15
|
-
|
|
16
7
|
import { ShellContext } from '../session/session.context.js'
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
export type SessionView =
|
|
20
|
-
|
|
21
|
-
/** A whoami-shaped identity/graph node (kernel-client does not yet re-export `Node` — G8). */
|
|
22
|
-
export type NodeValue = SelfNode
|
|
23
|
-
|
|
24
|
-
/** The root scope key — the memory's own default scope (`createMemory` default). */
|
|
25
|
-
export const ROOT_SCOPE = 'default'
|
|
9
|
+
export type NodeValue = Identity
|
|
10
|
+
export type SessionView = KernelClient
|
|
26
11
|
|
|
27
|
-
/**
|
|
28
|
-
* The principal a subtree acts as (API §4). `scope` is the memory isolation key
|
|
29
|
-
* (§5.4): the graph plane scopes its memory by `memory.forScope(scope)` so an
|
|
30
|
-
* <ActAs> subtree never shares cached results with another principal.
|
|
31
|
-
*/
|
|
32
12
|
export interface IdentityBinding {
|
|
33
|
-
readonly kernel:
|
|
34
|
-
readonly self:
|
|
35
|
-
readonly scope: string
|
|
13
|
+
readonly kernel: KernelClient
|
|
14
|
+
readonly self: Identity
|
|
36
15
|
}
|
|
16
|
+
|
|
37
17
|
export const IdentityContext = createContext<IdentityBinding | null>(null)
|
|
38
18
|
IdentityContext.displayName = 'AstraleIdentityContext'
|
|
39
19
|
|
|
40
|
-
/**
|
|
41
|
-
* The principal this subtree dispatches as. An <ActAs> value wins; otherwise the
|
|
42
|
-
* root binding off ShellContext. Throws outside a provider, or when the bound
|
|
43
|
-
* kernel resolved no identity (a bare prebuilt Shell with no auth surface).
|
|
44
|
-
*/
|
|
45
20
|
export function useIdentityBinding(): IdentityBinding {
|
|
46
21
|
const override = useContext(IdentityContext)
|
|
47
22
|
if (override !== null) return override
|
|
48
|
-
|
|
49
23
|
const runtime = useContext(ShellContext)
|
|
50
|
-
if (runtime === null)
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (runtime.self === null) {
|
|
54
|
-
throw new Error(
|
|
55
|
-
'identity hooks require an identity: this shell exposed no auth surface, so whoami ' +
|
|
56
|
-
'resolved nothing at boot',
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
// The root binding reads stable fields (self, shell.kernel), so a fresh object
|
|
60
|
-
// per render is inert — consumers key off `kernel`/`self`, both stable.
|
|
61
|
-
return { kernel: runtime.shell.kernel, self: runtime.self, scope: ROOT_SCOPE }
|
|
24
|
+
if (runtime === null) throw new Error('identity hooks must be used within a <ShellProvider>')
|
|
25
|
+
if (runtime.self === null) throw new Error('the active Shell has no authenticated identity')
|
|
26
|
+
return { kernel: runtime.shell.kernel, self: runtime.self }
|
|
62
27
|
}
|
|
63
28
|
|
|
64
29
|
export interface ActAsProps {
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
30
|
+
/**
|
|
31
|
+
* A separately authenticated Kernel capability. Raw credentials are deliberately not accepted;
|
|
32
|
+
* construct the delegated Client Session at the trust boundary that owns credential resolution.
|
|
33
|
+
*/
|
|
34
|
+
readonly client: KernelClient
|
|
35
|
+
readonly fallback?: ReactNode
|
|
36
|
+
readonly children: ReactNode
|
|
68
37
|
}
|
|
69
38
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const parent = useIdentityBinding()
|
|
78
|
-
// Key the rebind on a STABLE credential identity (like `configIdentity` does),
|
|
79
|
-
// so an inline object credential (`credential={{ … }}`) does not re-mint the
|
|
80
|
-
// session view — and re-run whoami, unmounting the subtree — every parent
|
|
81
|
-
// render. A thunk still keys by reference: pass a stable thunk to avoid churn.
|
|
82
|
-
const credKey = credentialIdentity(credential)
|
|
83
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
-
const kernel = useMemo(() => parent.kernel.as(credential), [parent.kernel, credKey])
|
|
39
|
+
/** Bind a subtree to an explicitly supplied authenticated Kernel capability. */
|
|
40
|
+
export function ActAs({ client, fallback, children }: ActAsProps): ReactElement | null {
|
|
41
|
+
const [state, setState] = useState<
|
|
42
|
+
| { readonly status: 'pending' }
|
|
43
|
+
| { readonly status: 'ready'; readonly self: Identity }
|
|
44
|
+
| { readonly status: 'failed'; readonly error: unknown }
|
|
45
|
+
>({ status: 'pending' })
|
|
85
46
|
|
|
86
|
-
const [self, setSelf] = useState<SelfNode | null>(null)
|
|
87
47
|
useEffect(() => {
|
|
88
|
-
let
|
|
89
|
-
|
|
90
|
-
void
|
|
91
|
-
(
|
|
92
|
-
if (
|
|
48
|
+
let active = true
|
|
49
|
+
setState({ status: 'pending' })
|
|
50
|
+
void client.auth.whoami().then(
|
|
51
|
+
(self) => {
|
|
52
|
+
if (active) setState({ status: 'ready', self })
|
|
93
53
|
},
|
|
94
|
-
() => {
|
|
95
|
-
|
|
54
|
+
(error: unknown) => {
|
|
55
|
+
if (active) setState({ status: 'failed', error })
|
|
96
56
|
},
|
|
97
57
|
)
|
|
98
58
|
return () => {
|
|
99
|
-
|
|
59
|
+
active = false
|
|
100
60
|
}
|
|
101
|
-
}, [
|
|
61
|
+
}, [client])
|
|
102
62
|
|
|
103
63
|
const value = useMemo<IdentityBinding | null>(
|
|
104
|
-
() => (
|
|
105
|
-
[
|
|
64
|
+
() => (state.status === 'ready' ? { kernel: client, self: state.self } : null),
|
|
65
|
+
[client, state],
|
|
106
66
|
)
|
|
107
|
-
|
|
108
|
-
if (value === null) return null
|
|
67
|
+
if (state.status === 'failed') throw state.error
|
|
68
|
+
if (value === null) return fallback === undefined ? null : <>{fallback}</>
|
|
109
69
|
return <IdentityContext.Provider value={value}>{children}</IdentityContext.Provider>
|
|
110
70
|
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* A stable identity for a credential: the thunk by reference, a string as itself,
|
|
114
|
-
* and an object by a serialized form — so an equal inline object does not churn
|
|
115
|
-
* the memo (worst case a serialize failure falls back to reference identity).
|
|
116
|
-
*/
|
|
117
|
-
function credentialIdentity(credential: CredentialInput | (() => CredentialInput)): unknown {
|
|
118
|
-
if (typeof credential === 'function' || typeof credential === 'string') return credential
|
|
119
|
-
try {
|
|
120
|
-
return `cred:${JSON.stringify(credential)}`
|
|
121
|
-
} catch {
|
|
122
|
-
return credential
|
|
123
|
-
}
|
|
124
|
-
}
|
package/src/auth/auth.hook.ts
CHANGED
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import type { AuthApi } from '@astrale-os/kernel-client/auth'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* useAuth (API §4) — the imperative AuthApi of the effective principal
|
|
5
|
-
* (whoami/credential/mint/delegate/check/grant/revoke/bind/unbind). Grant and
|
|
6
|
-
* revoke additionally nudge the principal's useCan ledger on success, delivering
|
|
7
|
-
* the Tier-0 local re-check (SPEC §11.3). DEVIATION: API §4 describes a pure
|
|
8
|
-
* passthrough; the wrap is the only way to surface a local grant/revoke today,
|
|
9
|
-
* since permissions dispatch as identity methods rather than graph patches.
|
|
10
|
-
* Cross-client liveness still awaits the watch tiers (G7).
|
|
11
|
-
*/
|
|
12
|
-
import { useMemo } from 'react'
|
|
13
|
-
|
|
14
|
-
import type { SessionView } from './act-as.context.js'
|
|
15
|
-
|
|
16
3
|
import { useIdentityBinding } from './act-as.context.js'
|
|
17
|
-
import { ledgerFor } from './can.hook.js'
|
|
18
4
|
|
|
5
|
+
/** The exact Client Auth capability for the effective subtree principal. */
|
|
19
6
|
export function useAuth(): AuthApi {
|
|
20
|
-
|
|
21
|
-
return useMemo(() => withLedgerNudge(kernel), [kernel])
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Wrap grant/revoke so a successful local change re-checks the principal's cells. */
|
|
25
|
-
function withLedgerNudge(kernel: SessionView): AuthApi {
|
|
26
|
-
const auth = kernel.auth
|
|
27
|
-
const ledger = ledgerFor(kernel)
|
|
28
|
-
const invalidate = (): void => ledger.invalidateAll()
|
|
29
|
-
|
|
30
|
-
const grant = ((arg: never) =>
|
|
31
|
-
Promise.resolve(auth.grant(arg)).then(invalidate)) as AuthApi['grant']
|
|
32
|
-
const revoke = ((arg: never) =>
|
|
33
|
-
Promise.resolve(auth.revoke(arg)).then(invalidate)) as AuthApi['revoke']
|
|
34
|
-
|
|
35
|
-
return { ...auth, grant, revoke }
|
|
7
|
+
return useIdentityBinding().kernel.auth
|
|
36
8
|
}
|
package/src/auth/can.hook.ts
CHANGED
|
@@ -1,149 +1,58 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { PathLike
|
|
1
|
+
import type { NodeId } from '@astrale-os/sdk/graph/node'
|
|
2
|
+
import type { PathLike } from '@astrale-os/sdk/graph/path'
|
|
3
|
+
import type { schema } from '@astrale-os/sdk/schema'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
* useCan (API §4) — deduped standing-grant permission checks. `auth.check`-backed:
|
|
7
|
-
* `undefined` while the first check is in flight (or `on` is idle), then a boolean.
|
|
8
|
-
* Deduped per (principal, perms, nodeId) across the whole tree via the principal's
|
|
9
|
-
* check ledger (folded in here as the dedup substrate). Standing-grant semantics:
|
|
10
|
-
* the dispatched call remains the gatekeeper — this is a UI affordance, not the
|
|
11
|
-
* enforcement point (SPEC §6.2). A local grant/revoke invalidates the ledger.
|
|
12
|
-
*/
|
|
13
|
-
import { useCallback, useMemo, useSyncExternalStore } from 'react'
|
|
5
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
6
|
+
import { useEffect, useState } from 'react'
|
|
14
7
|
|
|
15
|
-
import type { NodeValue, SessionView } from './act-as.context.js'
|
|
16
|
-
|
|
17
|
-
import { isIdleInput } from '../graph/memory/read/idle.js'
|
|
18
8
|
import { useIdentityBinding } from './act-as.context.js'
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** A node value carries the id/class/path triple (mirrors the memory's `isNodeValue`). */
|
|
23
|
-
export function isNodeValue(x: unknown): x is NodeValue {
|
|
24
|
-
return typeof x === 'object' && x !== null && 'id' in x && 'class' in x && 'path' in x
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Normalize a `useCan` subject to a dispatchable `Ref` plus a stable dedup key. */
|
|
28
|
-
export function toRef(on: NodeValue | PathLike): { ref: Ref; key: string } {
|
|
29
|
-
if (isNodeValue(on)) return { ref: on.path as Ref, key: `@${on.id}` }
|
|
30
|
-
return { ref: on, key: rawOf(on) }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ── check ledger (the dedup substrate) ───────────────────────────────────────
|
|
10
|
+
export type PolicyObject = NodeId | { readonly id: NodeId }
|
|
34
11
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getSnapshot = (): boolean | undefined => this.value
|
|
54
|
-
|
|
55
|
-
/** Run the check once when the value is unknown and nothing is in flight. */
|
|
56
|
-
ensure(): void {
|
|
57
|
-
if (this.value !== undefined || this.inFlight) return
|
|
58
|
-
this.run()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Force a re-check (local grant/revoke on this principal). */
|
|
62
|
-
invalidate(): void {
|
|
63
|
-
if (this.listeners.size === 0) {
|
|
64
|
-
this.value = undefined // recompute lazily on the next subscribe
|
|
12
|
+
/**
|
|
13
|
+
* Evaluate one declared Kernel policy for one exact graph object. This is a UI affordance; the
|
|
14
|
+
* invoked callable remains the authorization enforcement point.
|
|
15
|
+
*/
|
|
16
|
+
export function useCan(
|
|
17
|
+
policy: PathLike | schema.PolicyRef | null | undefined,
|
|
18
|
+
object: PolicyObject | null | undefined,
|
|
19
|
+
): boolean | undefined {
|
|
20
|
+
const { kernel } = useIdentityBinding()
|
|
21
|
+
const objectId = objectIdOf(object)
|
|
22
|
+
const policyTarget = policyPath(policy)
|
|
23
|
+
const [decision, setDecision] = useState<boolean | undefined>(undefined)
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (policyTarget === null || objectId === null) {
|
|
27
|
+
setDecision(undefined)
|
|
65
28
|
return
|
|
66
29
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// A failed check leaves the value unknown (undefined) — never a false grant.
|
|
80
|
-
this.value = undefined
|
|
81
|
-
},
|
|
82
|
-
)
|
|
83
|
-
.finally(() => {
|
|
84
|
-
this.inFlight = false
|
|
85
|
-
for (const l of this.listeners) l()
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Per-principal cell registry — dedup across the whole tree for one kernel handle. */
|
|
91
|
-
export class CheckLedger {
|
|
92
|
-
private readonly cells = new Map<string, CheckCell>()
|
|
93
|
-
|
|
94
|
-
constructor(private readonly auth: AuthApi) {}
|
|
95
|
-
|
|
96
|
-
cell(key: string, perms: Perms, on: Ref): CheckCell {
|
|
97
|
-
let cell = this.cells.get(key)
|
|
98
|
-
if (cell === undefined) {
|
|
99
|
-
cell = new CheckCell(this.auth, perms, on)
|
|
100
|
-
this.cells.set(key, cell)
|
|
30
|
+
let active = true
|
|
31
|
+
setDecision(undefined)
|
|
32
|
+
void kernel.auth.can({ policy: policyTarget, object: objectId }).then(
|
|
33
|
+
(allowed) => {
|
|
34
|
+
if (active) setDecision(allowed)
|
|
35
|
+
},
|
|
36
|
+
() => {
|
|
37
|
+
if (active) setDecision(undefined)
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
return () => {
|
|
41
|
+
active = false
|
|
101
42
|
}
|
|
102
|
-
|
|
103
|
-
}
|
|
43
|
+
}, [kernel, policyTarget, objectId])
|
|
104
44
|
|
|
105
|
-
|
|
106
|
-
invalidateAll(): void {
|
|
107
|
-
for (const cell of this.cells.values()) cell.invalidate()
|
|
108
|
-
}
|
|
45
|
+
return decision
|
|
109
46
|
}
|
|
110
47
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
let ledger = ledgers.get(kernel)
|
|
117
|
-
if (ledger === undefined) {
|
|
118
|
-
ledger = new CheckLedger(kernel.auth)
|
|
119
|
-
ledgers.set(kernel, ledger)
|
|
120
|
-
}
|
|
121
|
-
return ledger
|
|
48
|
+
function policyPath(policy: PathLike | schema.PolicyRef | null | undefined) {
|
|
49
|
+
if (policy === null || policy === undefined) return null
|
|
50
|
+
return policy instanceof Path || typeof policy === 'string'
|
|
51
|
+
? Path.from(policy).raw
|
|
52
|
+
: Path.project(policy).raw
|
|
122
53
|
}
|
|
123
54
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
perms: Perms,
|
|
128
|
-
on: NodeValue | PathLike | null | undefined,
|
|
129
|
-
): boolean | undefined {
|
|
130
|
-
const binding = useIdentityBinding()
|
|
131
|
-
const ref = isIdleInput(on) ? null : toRef(on)
|
|
132
|
-
|
|
133
|
-
// The shared cell for (principal, perms, nodeId): identical to every other
|
|
134
|
-
// useCan reading the same triple, so one check serves the whole tree.
|
|
135
|
-
const cell = useMemo(
|
|
136
|
-
() =>
|
|
137
|
-
ref === null ? null : ledgerFor(binding.kernel).cell(`${perms}\0${ref.key}`, perms, ref.ref),
|
|
138
|
-
// ref is rebuilt per render; its `key` string is the stable identity.
|
|
139
|
-
[binding.kernel, perms, ref?.key],
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
const subscribe = useCallback(
|
|
143
|
-
(onChange: () => void) => (cell === null ? NOOP_UNSUB : cell.subscribe(onChange)),
|
|
144
|
-
[cell],
|
|
145
|
-
)
|
|
146
|
-
const getSnapshot = useCallback((): boolean | undefined => cell?.getSnapshot(), [cell])
|
|
147
|
-
|
|
148
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
|
55
|
+
function objectIdOf(value: PolicyObject | null | undefined): NodeId | null {
|
|
56
|
+
if (value === null || value === undefined) return null
|
|
57
|
+
return typeof value === 'object' ? value.id : value
|
|
149
58
|
}
|
package/src/auth/index.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { ActAs } from './act-as.context.js'
|
|
|
3
3
|
export type { ActAsProps } from './act-as.context.js'
|
|
4
4
|
export { useAuth } from './auth.hook.js'
|
|
5
5
|
export { useCan } from './can.hook.js'
|
|
6
|
+
export type { PolicyObject } from './can.hook.js'
|
|
6
7
|
export { useSelf } from './self.hook.js'
|
package/src/auth/self.hook.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Identity } from '@astrale-os/kernel-client/auth'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* useSelf (API §4) — the caller's identity node, resolved at boot and read
|
|
5
|
-
* synchronously (never suspends). An <ActAs> subtree returns the acted-as
|
|
6
|
-
* identity. FLAG: today this is the boot-resolved whoami snapshot; it becomes a
|
|
7
|
-
* live memory shape once the graph plane subscribes the self node — no signature
|
|
8
|
-
* change.
|
|
9
|
-
*/
|
|
10
3
|
import { useIdentityBinding } from './act-as.context.js'
|
|
11
4
|
|
|
12
|
-
export function useSelf():
|
|
5
|
+
export function useSelf(): Identity {
|
|
13
6
|
return useIdentityBinding().self
|
|
14
7
|
}
|
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
* useSessionKernel (API §4) — the dispatch-floor resolver. Inside an `<ActAs>` it
|
|
3
|
-
* is the acted-as principal's bound session view; otherwise the root
|
|
4
|
-
* `shell.kernel`. Every dispatch floor (schema bindings, useCall/useStream, useMove,
|
|
5
|
-
* typed method dispatch) routes through this so a call runs as the subtree's
|
|
6
|
-
* principal — not the root — and its scoped memory.
|
|
7
|
-
*/
|
|
8
|
-
import { useContext } from 'react'
|
|
1
|
+
import type { KernelClient } from '@astrale-os/shell'
|
|
9
2
|
|
|
10
|
-
import
|
|
3
|
+
import { useContext } from 'react'
|
|
11
4
|
|
|
12
5
|
import { ShellContext } from '../session/session.context.js'
|
|
13
6
|
import { IdentityContext } from './act-as.context.js'
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const binding = useContext(IdentityContext)
|
|
8
|
+
export function useSessionKernel(): KernelClient {
|
|
9
|
+
const identity = useContext(IdentityContext)
|
|
18
10
|
const runtime = useContext(ShellContext)
|
|
19
|
-
if (
|
|
20
|
-
if (runtime === null)
|
|
21
|
-
throw new Error('session hooks must be used within a <ShellProvider>')
|
|
22
|
-
}
|
|
11
|
+
if (identity !== null) return identity.kernel
|
|
12
|
+
if (runtime === null) throw new Error('session hooks must be used within a <ShellProvider>')
|
|
23
13
|
return runtime.shell.kernel
|
|
24
14
|
}
|