@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
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type { StoreQueryResponse } from '@astrale-os/kernel-client/store'
|
|
2
|
+
import type {
|
|
3
|
+
BoundInstanceOf,
|
|
4
|
+
Definition,
|
|
5
|
+
EdgeEndpointAcceptedDefinitionsOf,
|
|
6
|
+
} from '@astrale-os/sdk/domain'
|
|
7
|
+
import type { LinkedCountOf } from '@astrale-os/sdk/query'
|
|
8
|
+
|
|
9
|
+
import { NodeId } from '@astrale-os/sdk/graph/node'
|
|
10
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
11
|
+
import { Query } from '@astrale-os/sdk/query'
|
|
12
|
+
import { useCallback, useMemo } from 'react'
|
|
13
|
+
|
|
14
|
+
import type { GraphResource } from '../graph/resource.js'
|
|
15
|
+
|
|
16
|
+
import { useStoreResource } from '../graph/resource.js'
|
|
17
|
+
import { bindingFor } from './domain.hook.js'
|
|
18
|
+
|
|
19
|
+
type DefinitionCoordinates = Pick<
|
|
20
|
+
Definition<'class' | 'interface'>,
|
|
21
|
+
'kind' | 'origin' | 'name' | 'key' | 'schemaRef' | 'definition'
|
|
22
|
+
>
|
|
23
|
+
type NodeDefinition = DefinitionCoordinates & { readonly family: 'node' }
|
|
24
|
+
type EdgeDefinition = DefinitionCoordinates & { readonly family: 'edge' }
|
|
25
|
+
type EndpointRole = 'source' | 'target'
|
|
26
|
+
type RelationCardinality = '1' | '0..1' | '0..*' | '1..*'
|
|
27
|
+
type RuntimeEndpoint = {
|
|
28
|
+
readonly accepts: readonly {
|
|
29
|
+
readonly origin: string
|
|
30
|
+
readonly kind: 'class' | 'interface'
|
|
31
|
+
readonly name: string
|
|
32
|
+
}[]
|
|
33
|
+
readonly outgoing?: RelationCardinality
|
|
34
|
+
readonly incoming?: RelationCardinality
|
|
35
|
+
}
|
|
36
|
+
type Opposite<Role extends EndpointRole> = Role extends 'source' ? 'target' : 'source'
|
|
37
|
+
type Accepted<
|
|
38
|
+
Definition extends EdgeDefinition,
|
|
39
|
+
Role extends EndpointRole,
|
|
40
|
+
> = EdgeEndpointAcceptedDefinitionsOf<Definition, Opposite<Role>>
|
|
41
|
+
type StartingDefinition<
|
|
42
|
+
Relation extends EdgeDefinition,
|
|
43
|
+
Role extends EndpointRole,
|
|
44
|
+
> = EdgeEndpointAcceptedDefinitionsOf<Relation, Role> & NodeDefinition
|
|
45
|
+
type RelatedDefinition<Relation extends EdgeDefinition, Role extends EndpointRole> = Accepted<
|
|
46
|
+
Relation,
|
|
47
|
+
Role
|
|
48
|
+
> &
|
|
49
|
+
NodeDefinition
|
|
50
|
+
type EffectiveCardinality<
|
|
51
|
+
Relation extends EdgeDefinition,
|
|
52
|
+
Role extends EndpointRole,
|
|
53
|
+
To extends RelatedDefinition<Relation, Role>,
|
|
54
|
+
> = LinkedCountOf<Relation, Role, To>
|
|
55
|
+
|
|
56
|
+
export interface RelationUnavailable {
|
|
57
|
+
readonly kind: 'required-hidden-or-invalid'
|
|
58
|
+
readonly cardinality: '1' | '1..*'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class RelationUnavailableError extends Error {
|
|
62
|
+
readonly code = 'SHELL_REACT_RELATION_UNAVAILABLE' as const
|
|
63
|
+
constructor(readonly unavailable: RelationUnavailable) {
|
|
64
|
+
super(
|
|
65
|
+
`Required relation (${unavailable.cardinality}) is hidden or violates the installed schema.`,
|
|
66
|
+
)
|
|
67
|
+
this.name = 'RelationUnavailableError'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type RelationResource<T, C extends string> = C extends '1' | '0..1'
|
|
72
|
+
? GraphResource<T | null> & {
|
|
73
|
+
readonly cardinality: C
|
|
74
|
+
readonly unavailable?: RelationUnavailable
|
|
75
|
+
require(): T
|
|
76
|
+
}
|
|
77
|
+
: GraphResource<readonly T[]> & {
|
|
78
|
+
readonly cardinality: C
|
|
79
|
+
readonly unavailable?: RelationUnavailable
|
|
80
|
+
require(): readonly T[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface RelationOptions<Role extends EndpointRole, To> {
|
|
84
|
+
readonly from: Role
|
|
85
|
+
/** Exact rich opposite-endpoint definition used to admit and brand returned nodes. */
|
|
86
|
+
readonly to: To
|
|
87
|
+
readonly page?: { readonly size: number }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Traverse one exact rich Edge definition with endpoint and cardinality inference. */
|
|
91
|
+
export function useRelation<
|
|
92
|
+
Definition extends EdgeDefinition,
|
|
93
|
+
Role extends EndpointRole,
|
|
94
|
+
To extends RelatedDefinition<Definition, Role>,
|
|
95
|
+
>(
|
|
96
|
+
definition: Definition,
|
|
97
|
+
source: BoundInstanceOf<StartingDefinition<Definition, Role>> | null | undefined,
|
|
98
|
+
options: RelationOptions<Role, To>,
|
|
99
|
+
): RelationResource<BoundInstanceOf<To>, EffectiveCardinality<Definition, Role, To>> {
|
|
100
|
+
const binding = bindingFor(definition)
|
|
101
|
+
if (source !== null && source !== undefined) {
|
|
102
|
+
admitSource(binding, definition, options.from, source)
|
|
103
|
+
}
|
|
104
|
+
admitRelatedDefinition(binding, definition, options.from, options.to)
|
|
105
|
+
const path = source === null || source === undefined ? null : Path.id(NodeId(source.id))
|
|
106
|
+
const query = useMemo(() => {
|
|
107
|
+
if (path === null) return null
|
|
108
|
+
const root = Query.from({ kind: 'node', paths: [path], binding: 'relation_source' })
|
|
109
|
+
const admitted = root.filter({ satisfies: source!.$.definition.schemaRef })
|
|
110
|
+
const expanded = admitted.expand({
|
|
111
|
+
via: [definition.schemaRef],
|
|
112
|
+
direction: options.from === 'source' ? 'outgoing' : 'incoming',
|
|
113
|
+
})
|
|
114
|
+
return expanded
|
|
115
|
+
.filter({ satisfies: options.to.schemaRef })
|
|
116
|
+
.select({ kind: 'graph', binding: expanded.node })
|
|
117
|
+
}, [definition, options.from, options.to, path?.raw, source])
|
|
118
|
+
const cardinality: RelationCardinality = effectiveCardinality(
|
|
119
|
+
binding,
|
|
120
|
+
definition,
|
|
121
|
+
options.from,
|
|
122
|
+
options.to,
|
|
123
|
+
)
|
|
124
|
+
const project = useCallback(
|
|
125
|
+
(response: StoreQueryResponse<NonNullable<typeof query>>) => {
|
|
126
|
+
const result = response.result
|
|
127
|
+
if (result.kind !== 'graph' || result.selection.kind !== 'node') {
|
|
128
|
+
return cardinality === '0..1'
|
|
129
|
+
? null
|
|
130
|
+
: cardinality === '0..*'
|
|
131
|
+
? Object.freeze([])
|
|
132
|
+
: undefined
|
|
133
|
+
}
|
|
134
|
+
const byId = new Map(result.graph.nodes.map((node) => [node.id, node]))
|
|
135
|
+
const values = result.selection.ids.flatMap((id) => {
|
|
136
|
+
const node = byId.get(id)
|
|
137
|
+
if (node === undefined) return []
|
|
138
|
+
const bind = binding.$.bind as unknown as (
|
|
139
|
+
expected: To,
|
|
140
|
+
value: typeof node,
|
|
141
|
+
) => BoundInstanceOf<To>
|
|
142
|
+
return [bind(options.to, node)]
|
|
143
|
+
})
|
|
144
|
+
if (cardinality === '0..1') return values[0] ?? null
|
|
145
|
+
if (cardinality === '1') return values[0]
|
|
146
|
+
if (cardinality === '1..*' && values.length === 0) return undefined
|
|
147
|
+
return Object.freeze(values)
|
|
148
|
+
},
|
|
149
|
+
[binding, cardinality, options.to, query],
|
|
150
|
+
)
|
|
151
|
+
const resource = useStoreResource(query, options.page?.size ?? 50, project)
|
|
152
|
+
const unavailable =
|
|
153
|
+
(cardinality === '1' &&
|
|
154
|
+
resource.data === undefined &&
|
|
155
|
+
resource.state !== 'idle' &&
|
|
156
|
+
resource.state !== 'loading') ||
|
|
157
|
+
(cardinality === '1..*' &&
|
|
158
|
+
resource.data === undefined &&
|
|
159
|
+
resource.state !== 'idle' &&
|
|
160
|
+
resource.state !== 'loading')
|
|
161
|
+
? Object.freeze({ kind: 'required-hidden-or-invalid' as const, cardinality })
|
|
162
|
+
: undefined
|
|
163
|
+
const require = () => {
|
|
164
|
+
if (unavailable !== undefined) throw new RelationUnavailableError(unavailable)
|
|
165
|
+
if (resource.data === undefined || resource.data === null) {
|
|
166
|
+
throw new Error('Relation resource is not settled with an available value.')
|
|
167
|
+
}
|
|
168
|
+
return resource.data
|
|
169
|
+
}
|
|
170
|
+
return Object.freeze({
|
|
171
|
+
...resource,
|
|
172
|
+
cardinality,
|
|
173
|
+
...(unavailable === undefined ? {} : { unavailable }),
|
|
174
|
+
require,
|
|
175
|
+
}) as RelationResource<BoundInstanceOf<To>, EffectiveCardinality<Definition, Role, To>>
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function admitSource<Relation extends EdgeDefinition, Role extends EndpointRole>(
|
|
179
|
+
binding: ReturnType<typeof bindingFor>,
|
|
180
|
+
relation: Relation,
|
|
181
|
+
from: Role,
|
|
182
|
+
source: BoundInstanceOf<StartingDefinition<Relation, Role>>,
|
|
183
|
+
): void {
|
|
184
|
+
const accepted = edgeEndpoints(relation)[from].accepts
|
|
185
|
+
const matches = accepted.some((ref) => {
|
|
186
|
+
const candidate = binding.$.inspect(ref) as Definition<'class' | 'interface'>
|
|
187
|
+
return source.$.definition.satisfies(candidate)
|
|
188
|
+
})
|
|
189
|
+
if (!matches) throw new TypeError('Relation source does not satisfy the selected endpoint.')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function endpointCardinality(definition: EdgeDefinition, side: EndpointRole): RelationCardinality {
|
|
193
|
+
const endpoint = edgeEndpoints(definition)[side]
|
|
194
|
+
const cardinality = side === 'source' ? endpoint.outgoing : endpoint.incoming
|
|
195
|
+
if (cardinality === undefined) {
|
|
196
|
+
throw new TypeError(`Relation ${side} endpoint does not expose its traversal cardinality.`)
|
|
197
|
+
}
|
|
198
|
+
return cardinality
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function effectiveCardinality<
|
|
202
|
+
Relation extends EdgeDefinition,
|
|
203
|
+
Role extends EndpointRole,
|
|
204
|
+
To extends RelatedDefinition<Relation, Role>,
|
|
205
|
+
>(
|
|
206
|
+
binding: ReturnType<typeof bindingFor>,
|
|
207
|
+
relation: Relation,
|
|
208
|
+
from: Role,
|
|
209
|
+
to: To,
|
|
210
|
+
): EffectiveCardinality<Relation, Role, To> {
|
|
211
|
+
const base = endpointCardinality(relation, from)
|
|
212
|
+
if (base !== '1' && base !== '1..*') {
|
|
213
|
+
return base as EffectiveCardinality<Relation, Role, To>
|
|
214
|
+
}
|
|
215
|
+
const opposite: EndpointRole = from === 'source' ? 'target' : 'source'
|
|
216
|
+
const coversEndpoint = edgeEndpoints(relation)[opposite].accepts.every((ref) => {
|
|
217
|
+
const candidate = binding.$.inspect(ref) as Definition<'class' | 'interface'>
|
|
218
|
+
return candidate.satisfies(to)
|
|
219
|
+
})
|
|
220
|
+
return (coversEndpoint ? base : base === '1' ? '0..1' : '0..*') as EffectiveCardinality<
|
|
221
|
+
Relation,
|
|
222
|
+
Role,
|
|
223
|
+
To
|
|
224
|
+
>
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function admitRelatedDefinition<
|
|
228
|
+
Relation extends EdgeDefinition,
|
|
229
|
+
Role extends EndpointRole,
|
|
230
|
+
To extends RelatedDefinition<Relation, Role>,
|
|
231
|
+
>(binding: ReturnType<typeof bindingFor>, relation: Relation, from: Role, to: To): void {
|
|
232
|
+
const opposite: EndpointRole = from === 'source' ? 'target' : 'source'
|
|
233
|
+
const accepted = edgeEndpoints(relation)[opposite].accepts
|
|
234
|
+
const matches = accepted.some((ref) => {
|
|
235
|
+
const candidate = binding.$.inspect(ref) as Definition<'class' | 'interface'>
|
|
236
|
+
return to.satisfies(candidate)
|
|
237
|
+
})
|
|
238
|
+
if (!matches)
|
|
239
|
+
throw new TypeError('Relation target definition is not admitted by the opposite endpoint.')
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function edgeEndpoints(relation: EdgeDefinition): Readonly<Record<EndpointRole, RuntimeEndpoint>> {
|
|
243
|
+
const declaration = relation.definition as unknown as {
|
|
244
|
+
readonly family?: unknown
|
|
245
|
+
readonly endpoints?: Partial<Record<EndpointRole, RuntimeEndpoint>>
|
|
246
|
+
}
|
|
247
|
+
const source = declaration.endpoints?.source
|
|
248
|
+
const target = declaration.endpoints?.target
|
|
249
|
+
if (declaration.family !== 'edge' || source === undefined || target === undefined) {
|
|
250
|
+
throw new TypeError('Relation definition does not expose admitted Edge endpoints.')
|
|
251
|
+
}
|
|
252
|
+
return Object.freeze({ source, target })
|
|
253
|
+
}
|
package/src/selection/live.ts
CHANGED
|
@@ -1,142 +1,92 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Node, NodeId } from '@astrale-os/sdk/graph/node'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* the node types the selection speaks. Each id is subscribed as an independent
|
|
6
|
-
* point-read shape; an id drops ONLY once its shape has SETTLED to absence (the
|
|
7
|
-
* fixed latch) — a cold shape whose point-read has not landed is kept alive with
|
|
8
|
-
* no node, so a selection made from a children row is never self-cleared before
|
|
9
|
-
* its fetch resolves. Degrades to no nodes (ids kept) when no memory is in scope.
|
|
10
|
-
*/
|
|
11
|
-
import { useCallback, useContext, useRef, useSyncExternalStore } from 'react'
|
|
3
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
12
5
|
|
|
13
|
-
import {
|
|
6
|
+
import { useSessionKernel } from '../auth/session-kernel.js'
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
export type
|
|
17
|
-
export type NodeId = SelectionNode['id']
|
|
8
|
+
export type SelectionNode = Node
|
|
9
|
+
export type { NodeId }
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
const EMPTY_NODES: readonly SelectionNode[] = []
|
|
21
|
-
const NOOP = (): void => {}
|
|
22
|
-
|
|
23
|
-
/** Point-read shape addressing a node by its graph id. */
|
|
24
|
-
function nodeShape(id: NodeId): ShapeInput {
|
|
25
|
-
return `@${id}`
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** A point-read snapshot — the arms selection reads (widened to tolerate fakes). */
|
|
29
|
-
type NodeSnapshot = { readonly node: SelectionNode | null; live?: boolean; pending?: boolean }
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* A null-node id is a CONFIRMED absence (drop it) only when the shape settled
|
|
33
|
-
* there: it was seen warm and is now gone (deletion), or a live subscription has
|
|
34
|
-
* finished fetching and found nothing (mask/never-existed). A never-warm, not-
|
|
35
|
-
* yet-fetched (cold/pending) shape is NOT absence — the id stays alive, no node.
|
|
36
|
-
*/
|
|
37
|
-
function isConfirmedAbsent(snap: NodeSnapshot, id: NodeId, everWarm: ReadonlySet<NodeId>): boolean {
|
|
38
|
-
if (everWarm.has(id)) return true
|
|
39
|
-
return snap.live === true && snap.pending === false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The in-scope graph memory, structurally narrowed. Reads the base memory the
|
|
44
|
-
* provider seats in `GraphContext`, defensively so selection also works under a
|
|
45
|
-
* bare/fake context (tests) or before one is mounted.
|
|
46
|
-
*/
|
|
47
|
-
export function useMemory(): GraphMemory | null {
|
|
48
|
-
const ctx = useContext(GraphContext) as unknown as GraphMemory | null
|
|
49
|
-
return ctx !== null &&
|
|
50
|
-
typeof ctx.subscribe === 'function' &&
|
|
51
|
-
typeof ctx.getSnapshot === 'function'
|
|
52
|
-
? ctx
|
|
53
|
-
: null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface LiveSnapshot {
|
|
11
|
+
export interface ResolvedSelection {
|
|
57
12
|
readonly aliveIds: readonly NodeId[]
|
|
58
13
|
readonly nodes: readonly SelectionNode[]
|
|
14
|
+
readonly pending: boolean
|
|
15
|
+
readonly error: unknown
|
|
16
|
+
refetch(): Promise<void>
|
|
59
17
|
}
|
|
60
18
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
aliveIds: readonly NodeId[]
|
|
64
|
-
nodes: readonly SelectionNode[]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// yet), so compare the two lists independently.
|
|
68
|
-
if (prev.aliveIds.length !== aliveIds.length || prev.nodes.length !== nodes.length) return false
|
|
69
|
-
for (let i = 0; i < aliveIds.length; i += 1) if (prev.aliveIds[i] !== aliveIds[i]) return false
|
|
70
|
-
for (let i = 0; i < nodes.length; i += 1) if (prev.nodes[i] !== nodes[i]) return false
|
|
71
|
-
return true
|
|
19
|
+
interface State {
|
|
20
|
+
readonly key: string
|
|
21
|
+
readonly aliveIds: readonly NodeId[]
|
|
22
|
+
readonly nodes: readonly SelectionNode[]
|
|
23
|
+
readonly pending: boolean
|
|
24
|
+
readonly error: unknown
|
|
72
25
|
}
|
|
73
26
|
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
27
|
+
/** Resolve selected identities through the authenticated GraphApi; no fake live subscription. */
|
|
28
|
+
export function useResolvedNodes(orderedIds: readonly NodeId[]): ResolvedSelection {
|
|
29
|
+
const graph = useSessionKernel().graph
|
|
30
|
+
const key = orderedIds.join('\u0000')
|
|
31
|
+
const idsRef = useRef(orderedIds)
|
|
32
|
+
idsRef.current = orderedIds
|
|
33
|
+
const generation = useRef(0)
|
|
34
|
+
const alive = useRef(true)
|
|
35
|
+
const [state, setState] = useState<State>({
|
|
36
|
+
key,
|
|
37
|
+
aliveIds: orderedIds,
|
|
38
|
+
nodes: [],
|
|
39
|
+
pending: orderedIds.length > 0,
|
|
40
|
+
error: undefined,
|
|
41
|
+
})
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
alive.current = true
|
|
44
|
+
return () => {
|
|
45
|
+
alive.current = false
|
|
46
|
+
}
|
|
47
|
+
}, [])
|
|
90
48
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
} else {
|
|
99
|
-
const seen = everWarm.current
|
|
100
|
-
const a: NodeId[] = []
|
|
101
|
-
const n: SelectionNode[] = []
|
|
102
|
-
for (const id of orderedIds) {
|
|
103
|
-
const snap = memory.getSnapshot(nodeShape(id), LIVE_OPTS) as NodeSnapshot
|
|
104
|
-
const node = snap.node
|
|
105
|
-
if (node !== null) {
|
|
106
|
-
seen.add(id)
|
|
107
|
-
a.push(id)
|
|
108
|
-
n.push(node)
|
|
109
|
-
} else if (!isConfirmedAbsent(snap, id, seen)) {
|
|
110
|
-
// Cold / not-yet-fetched: keep the id alive, no node yet — never a
|
|
111
|
-
// premature drop (the critical self-clear this guards against).
|
|
112
|
-
a.push(id)
|
|
113
|
-
}
|
|
114
|
-
// else: settled absence (deleted/masked) → drops from ids and nodes.
|
|
49
|
+
const load = useCallback(async (): Promise<void> => {
|
|
50
|
+
const ids = idsRef.current
|
|
51
|
+
const currentKey = ids.join('\u0000')
|
|
52
|
+
const id = ++generation.current
|
|
53
|
+
if (ids.length === 0) {
|
|
54
|
+
if (alive.current) {
|
|
55
|
+
setState({ key: currentKey, aliveIds: [], nodes: [], pending: false, error: undefined })
|
|
115
56
|
}
|
|
116
|
-
|
|
117
|
-
nodes = n
|
|
57
|
+
return
|
|
118
58
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
59
|
+
setState({ key: currentKey, aliveIds: ids, nodes: [], pending: true, error: undefined })
|
|
60
|
+
try {
|
|
61
|
+
const values = await Promise.all(ids.map((nodeId) => graph.get(Path.id(nodeId))))
|
|
62
|
+
if (!alive.current || id !== generation.current) return
|
|
63
|
+
const aliveIds: NodeId[] = []
|
|
64
|
+
const nodes: Node[] = []
|
|
65
|
+
values.forEach((node, index) => {
|
|
66
|
+
if (node === null) return
|
|
67
|
+
aliveIds.push(ids[index]!)
|
|
68
|
+
nodes.push(node)
|
|
69
|
+
})
|
|
70
|
+
setState({
|
|
71
|
+
key: currentKey,
|
|
72
|
+
aliveIds: Object.freeze(aliveIds),
|
|
73
|
+
nodes: Object.freeze(nodes),
|
|
74
|
+
pending: false,
|
|
75
|
+
error: undefined,
|
|
76
|
+
})
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (alive.current && id === generation.current) {
|
|
79
|
+
setState({ key: currentKey, aliveIds: ids, nodes: [], pending: false, error })
|
|
80
|
+
}
|
|
122
81
|
}
|
|
123
|
-
|
|
124
|
-
cache.current = { memory, snap }
|
|
125
|
-
return snap
|
|
126
|
-
}, [memory, idsKey])
|
|
82
|
+
}, [graph])
|
|
127
83
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const unsubs = orderedIds.map((id) =>
|
|
132
|
-
memory.subscribe(nodeShape(id), onStoreChange, LIVE_OPTS),
|
|
133
|
-
)
|
|
134
|
-
return () => {
|
|
135
|
-
for (const unsub of unsubs) unsub()
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
[memory, idsKey],
|
|
139
|
-
)
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
void load()
|
|
86
|
+
}, [key, load])
|
|
140
87
|
|
|
141
|
-
|
|
88
|
+
if (state.key !== key) {
|
|
89
|
+
return { aliveIds: orderedIds, nodes: [], pending: true, error: undefined, refetch: load }
|
|
90
|
+
}
|
|
91
|
+
return { ...state, refetch: load }
|
|
142
92
|
}
|
|
@@ -10,7 +10,7 @@ import { createContext, useCallback, useEffect, useMemo, useReducer, useRef } fr
|
|
|
10
10
|
|
|
11
11
|
import type { NodeId, SelectionNode } from './live.js'
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { useResolvedNodes } from './live.js'
|
|
14
14
|
|
|
15
15
|
/** The value `useSelection()` returns (API §9). */
|
|
16
16
|
export interface Selection {
|
|
@@ -109,14 +109,14 @@ export function SelectionScope(props: SelectionScopeProps): ReactElement {
|
|
|
109
109
|
const { initial, onChange, children } = props
|
|
110
110
|
const [state, dispatch] = useReducer(reducer, initial, initState)
|
|
111
111
|
|
|
112
|
-
const
|
|
113
|
-
const { aliveIds, nodes } = useLiveNodes(state.ids, memory)
|
|
112
|
+
const { aliveIds, nodes, pending, error } = useResolvedNodes(state.ids)
|
|
114
113
|
|
|
115
114
|
// Re-canonicalize stored state when liveness drops a node (or its anchor).
|
|
116
115
|
useEffect(() => {
|
|
117
116
|
const anchorDead = state.anchor !== null && !aliveIds.includes(state.anchor)
|
|
117
|
+
if (pending || error !== undefined) return
|
|
118
118
|
if (!sameIds(state.ids, aliveIds) || anchorDead) dispatch({ t: 'prune', alive: aliveIds })
|
|
119
|
-
}, [aliveIds, state.ids, state.anchor])
|
|
119
|
+
}, [aliveIds, pending, error, state.ids, state.anchor])
|
|
120
120
|
|
|
121
121
|
// Report changes (explicit ops AND liveness drops), skipping the initial mount.
|
|
122
122
|
const onChangeRef = useRef(onChange)
|
package/src/session/boot/boot.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The boot machine inputs (API §2): config identity (what triggers a reboot) and
|
|
3
|
-
* whoami resolution (`createShell → init → whoami → OS-user preresolve`). Kept
|
|
4
|
-
* pure of React so the provider's boot effect composes them.
|
|
5
|
-
*/
|
|
6
1
|
import type { Shell, ShellConfig } from '@astrale-os/shell'
|
|
7
2
|
|
|
8
3
|
import type { SelfNode } from '../session.context.js'
|
|
@@ -13,45 +8,25 @@ export function isShellConfig(config: ShellConfig | Shell): config is ShellConfi
|
|
|
13
8
|
|
|
14
9
|
let prebuiltCounter = 0
|
|
15
10
|
const prebuiltIds = new WeakMap<object, string>()
|
|
16
|
-
|
|
11
|
+
|
|
17
12
|
function prebuiltId(shell: object): string {
|
|
18
13
|
let id = prebuiltIds.get(shell)
|
|
19
14
|
if (id === undefined) {
|
|
20
|
-
|
|
21
|
-
id = `prebuilt#${prebuiltCounter}`
|
|
15
|
+
id = `prebuilt#${++prebuiltCounter}`
|
|
22
16
|
prebuiltIds.set(shell, id)
|
|
23
17
|
}
|
|
24
18
|
return id
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
/**
|
|
28
|
-
* Identity of a config — primitive fields only, so re-passing an inline `fetch`,
|
|
29
|
-
* `adapter`, `getCredential`, or `parentWindow` each render does not reboot;
|
|
30
|
-
* changing the kernel/credential does. A principal switch driven by a NEW
|
|
31
|
-
* `getCredential` closure (rather than a new `credential` string) is therefore
|
|
32
|
-
* NOT observed here — remount the provider (React `key`) to rebind it.
|
|
33
|
-
*/
|
|
21
|
+
/** Stable ownership key. Capability objects intentionally use reference identity. */
|
|
34
22
|
export function configIdentity(config: ShellConfig | Shell): string {
|
|
35
23
|
if (!isShellConfig(config)) return prebuiltId(config)
|
|
36
|
-
if (config.mode === '
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
config.kernelUrl,
|
|
40
|
-
config.credential ?? '',
|
|
41
|
-
config.mintIdentity ?? '',
|
|
42
|
-
config.windowId ?? '',
|
|
43
|
-
].join('|')
|
|
44
|
-
}
|
|
45
|
-
return `sandboxed|${config.initTimeoutMs ?? ''}`
|
|
24
|
+
if (config.mode === 'sandboxed') return prebuiltId(config)
|
|
25
|
+
if (config.client !== undefined) return `standalone-client|${prebuiltId(config.client)}`
|
|
26
|
+
return `standalone-session|${prebuiltId(config.session)}`
|
|
46
27
|
}
|
|
47
28
|
|
|
48
|
-
/** Resolve whoami when the bound kernel exposes an auth surface; else null. */
|
|
49
29
|
export async function resolveSelf(shell: Shell): Promise<SelfNode | null> {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const auth = kernel.auth
|
|
54
|
-
const whoami = auth?.whoami
|
|
55
|
-
if (typeof whoami !== 'function') return null
|
|
56
|
-
return whoami.call(auth)
|
|
30
|
+
const auth = shell.kernel.auth
|
|
31
|
+
return typeof auth.whoami === 'function' ? auth.whoami() : null
|
|
57
32
|
}
|
package/src/session/index.ts
CHANGED
|
@@ -6,3 +6,4 @@ export { useKernelClient } from './kernel-client.hook.js'
|
|
|
6
6
|
export { useKernelInstance } from './kernel-instance.hook.js'
|
|
7
7
|
export type { KernelInstance } from './kernel-instance.hook.js'
|
|
8
8
|
export { useConnection } from './connection/connection.hook.js'
|
|
9
|
+
export type { ConnectionStatus, ConnectionValue } from './connection/connection.store.js'
|
|
@@ -2,21 +2,12 @@ import { useMemo } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { useKernelClient } from './kernel-client.hook.js'
|
|
4
4
|
|
|
5
|
+
/** Public, non-secret identity of the connected source Kernel. */
|
|
5
6
|
export interface KernelInstance {
|
|
6
|
-
/** Kernel envelope endpoint this shell is bound to. */
|
|
7
7
|
readonly url: string
|
|
8
|
-
/** Current kernel credential. Never cache; read per request. */
|
|
9
|
-
token(): string
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
/** Low-level access for direct kernel-envelope calls and token exchange code. */
|
|
13
10
|
export function useKernelInstance(): KernelInstance {
|
|
14
11
|
const kernel = useKernelClient()
|
|
15
|
-
return useMemo
|
|
16
|
-
() => ({
|
|
17
|
-
url: kernel.url,
|
|
18
|
-
token: () => kernel.authToken(),
|
|
19
|
-
}),
|
|
20
|
-
[kernel],
|
|
21
|
-
)
|
|
12
|
+
return useMemo(() => Object.freeze({ url: kernel.url }), [kernel])
|
|
22
13
|
}
|