@astrale-os/shell-react 0.1.0
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/LICENSE +202 -0
- package/dist/application/apps/app-host.d.ts +31 -0
- package/dist/application/apps/app-host.d.ts.map +1 -0
- package/dist/application/apps/app-host.js +59 -0
- package/dist/application/apps/app-logo.d.ts +19 -0
- package/dist/application/apps/app-logo.d.ts.map +1 -0
- package/dist/application/apps/app-logo.js +31 -0
- package/dist/application/apps/apps.hook.d.ts +12 -0
- package/dist/application/apps/apps.hook.d.ts.map +1 -0
- package/dist/application/apps/apps.hook.js +51 -0
- package/dist/application/apps/open-app.hook.d.ts +10 -0
- package/dist/application/apps/open-app.hook.d.ts.map +1 -0
- package/dist/application/apps/open-app.hook.js +7 -0
- package/dist/application/index.d.ts +18 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +12 -0
- package/dist/application/roles.hook.d.ts +7 -0
- package/dist/application/roles.hook.d.ts.map +1 -0
- package/dist/application/roles.hook.js +23 -0
- package/dist/application/spaces/current-space.hook.d.ts +7 -0
- package/dist/application/spaces/current-space.hook.d.ts.map +1 -0
- package/dist/application/spaces/current-space.hook.js +27 -0
- package/dist/application/spaces/current-space.store.d.ts +32 -0
- package/dist/application/spaces/current-space.store.d.ts.map +1 -0
- package/dist/application/spaces/current-space.store.js +79 -0
- package/dist/application/spaces/space.hook.d.ts +5 -0
- package/dist/application/spaces/space.hook.d.ts.map +1 -0
- package/dist/application/spaces/space.hook.js +25 -0
- package/dist/application/spaces/spaces.hook.d.ts +14 -0
- package/dist/application/spaces/spaces.hook.d.ts.map +1 -0
- package/dist/application/spaces/spaces.hook.js +82 -0
- package/dist/application/user/user.hook.d.ts +10 -0
- package/dist/application/user/user.hook.d.ts.map +1 -0
- package/dist/application/user/user.hook.js +19 -0
- package/dist/application/user/user.store.d.ts +35 -0
- package/dist/application/user/user.store.d.ts.map +1 -0
- package/dist/application/user/user.store.js +51 -0
- package/dist/auth/act-as.context.d.ts +40 -0
- package/dist/auth/act-as.context.d.ts.map +1 -0
- package/dist/auth/act-as.context.js +84 -0
- package/dist/auth/auth.hook.d.ts +3 -0
- package/dist/auth/auth.hook.d.ts.map +1 -0
- package/dist/auth/auth.hook.js +25 -0
- package/dist/auth/can.hook.d.ts +40 -0
- package/dist/auth/can.hook.d.ts.map +1 -0
- package/dist/auth/can.hook.js +119 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/self.hook.d.ts +3 -0
- package/dist/auth/self.hook.d.ts.map +1 -0
- package/dist/auth/self.hook.js +11 -0
- package/dist/auth/session-kernel.d.ts +4 -0
- package/dist/auth/session-kernel.d.ts.map +1 -0
- package/dist/auth/session-kernel.js +21 -0
- package/dist/graph/action.d.ts +22 -0
- package/dist/graph/action.d.ts.map +1 -0
- package/dist/graph/action.js +85 -0
- package/dist/graph/async.d.ts +11 -0
- package/dist/graph/async.d.ts.map +1 -0
- package/dist/graph/async.js +60 -0
- package/dist/graph/call/call.hook.d.ts +21 -0
- package/dist/graph/call/call.hook.d.ts.map +1 -0
- package/dist/graph/call/call.hook.js +23 -0
- package/dist/graph/call/stream.hook.d.ts +28 -0
- package/dist/graph/call/stream.hook.d.ts.map +1 -0
- package/dist/graph/call/stream.hook.js +137 -0
- package/dist/graph/graph.context.d.ts +4 -0
- package/dist/graph/graph.context.d.ts.map +1 -0
- package/dist/graph/graph.context.js +10 -0
- package/dist/graph/guards.error.d.ts +9 -0
- package/dist/graph/guards.error.d.ts.map +1 -0
- package/dist/graph/guards.error.js +7 -0
- package/dist/graph/index.d.ts +20 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/memory/base.d.ts +21 -0
- package/dist/graph/memory/base.d.ts.map +1 -0
- package/dist/graph/memory/base.js +33 -0
- package/dist/graph/memory/read/envelope.d.ts +71 -0
- package/dist/graph/memory/read/envelope.d.ts.map +1 -0
- package/dist/graph/memory/read/envelope.js +29 -0
- package/dist/graph/memory/read/idle.d.ts +18 -0
- package/dist/graph/memory/read/idle.d.ts.map +1 -0
- package/dist/graph/memory/read/idle.js +16 -0
- package/dist/graph/memory/read/read.d.ts +9 -0
- package/dist/graph/memory/read/read.d.ts.map +1 -0
- package/dist/graph/memory/read/read.js +87 -0
- package/dist/graph/memory/scope.d.ts +7 -0
- package/dist/graph/memory/scope.d.ts.map +1 -0
- package/dist/graph/memory/scope.js +25 -0
- package/dist/graph/mutate/move.hook.d.ts +7 -0
- package/dist/graph/mutate/move.hook.d.ts.map +1 -0
- package/dist/graph/mutate/move.hook.js +49 -0
- package/dist/graph/mutate/mutate.hook.d.ts +17 -0
- package/dist/graph/mutate/mutate.hook.d.ts.map +1 -0
- package/dist/graph/mutate/mutate.hook.js +45 -0
- package/dist/graph/query/children.hook.d.ts +29 -0
- package/dist/graph/query/children.hook.d.ts.map +1 -0
- package/dist/graph/query/children.hook.js +64 -0
- package/dist/graph/query/graph.hook.d.ts +9 -0
- package/dist/graph/query/graph.hook.d.ts.map +1 -0
- package/dist/graph/query/graph.hook.js +5 -0
- package/dist/graph/query/neighbors.hook.d.ts +7 -0
- package/dist/graph/query/neighbors.hook.d.ts.map +1 -0
- package/dist/graph/query/neighbors.hook.js +79 -0
- package/dist/graph/query/node.hook.d.ts +14 -0
- package/dist/graph/query/node.hook.d.ts.map +1 -0
- package/dist/graph/query/node.hook.js +43 -0
- package/dist/graph/query/query.hook.d.ts +14 -0
- package/dist/graph/query/query.hook.d.ts.map +1 -0
- package/dist/graph/query/query.hook.js +23 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/intents/envelope.d.ts +33 -0
- package/dist/intents/envelope.d.ts.map +1 -0
- package/dist/intents/envelope.js +19 -0
- package/dist/intents/index.d.ts +6 -0
- package/dist/intents/index.d.ts.map +1 -0
- package/dist/intents/index.js +3 -0
- package/dist/intents/listen-intent.hook.d.ts +16 -0
- package/dist/intents/listen-intent.hook.d.ts.map +1 -0
- package/dist/intents/listen-intent.hook.js +75 -0
- package/dist/intents/reply.d.ts +9 -0
- package/dist/intents/reply.d.ts.map +1 -0
- package/dist/intents/reply.js +14 -0
- package/dist/intents/send-intent.hook.d.ts +12 -0
- package/dist/intents/send-intent.hook.d.ts.map +1 -0
- package/dist/intents/send-intent.hook.js +86 -0
- package/dist/navigation/adapters/adapter.d.ts +15 -0
- package/dist/navigation/adapters/adapter.d.ts.map +1 -0
- package/dist/navigation/adapters/adapter.js +1 -0
- package/dist/navigation/adapters/memory.adapter.d.ts +9 -0
- package/dist/navigation/adapters/memory.adapter.d.ts.map +1 -0
- package/dist/navigation/adapters/memory.adapter.js +15 -0
- package/dist/navigation/adapters/url.adapter.d.ts +6 -0
- package/dist/navigation/adapters/url.adapter.d.ts.map +1 -0
- package/dist/navigation/adapters/url.adapter.js +59 -0
- package/dist/navigation/history.d.ts +63 -0
- package/dist/navigation/history.d.ts.map +1 -0
- package/dist/navigation/history.js +144 -0
- package/dist/navigation/hooks/ancestors.hook.d.ts +13 -0
- package/dist/navigation/hooks/ancestors.hook.d.ts.map +1 -0
- package/dist/navigation/hooks/ancestors.hook.js +56 -0
- package/dist/navigation/hooks/location.hook.d.ts +4 -0
- package/dist/navigation/hooks/location.hook.d.ts.map +1 -0
- package/dist/navigation/hooks/location.hook.js +17 -0
- package/dist/navigation/hooks/navigate.hook.d.ts +12 -0
- package/dist/navigation/hooks/navigate.hook.d.ts.map +1 -0
- package/dist/navigation/hooks/navigate.hook.js +33 -0
- package/dist/navigation/hooks/scope.hook.d.ts +12 -0
- package/dist/navigation/hooks/scope.hook.d.ts.map +1 -0
- package/dist/navigation/hooks/scope.hook.js +21 -0
- package/dist/navigation/index.d.ts +16 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +10 -0
- package/dist/navigation/link.d.ts +11 -0
- package/dist/navigation/link.d.ts.map +1 -0
- package/dist/navigation/link.js +62 -0
- package/dist/navigation/location.d.ts +48 -0
- package/dist/navigation/location.d.ts.map +1 -0
- package/dist/navigation/location.js +88 -0
- package/dist/navigation/scope.context.d.ts +64 -0
- package/dist/navigation/scope.context.d.ts.map +1 -0
- package/dist/navigation/scope.context.js +178 -0
- package/dist/navigation/snapshot.d.ts +22 -0
- package/dist/navigation/snapshot.d.ts.map +1 -0
- package/dist/navigation/snapshot.js +65 -0
- package/dist/schema/bind-read.d.ts +37 -0
- package/dist/schema/bind-read.d.ts.map +1 -0
- package/dist/schema/bind-read.js +205 -0
- package/dist/schema/binding.d.ts +21 -0
- package/dist/schema/binding.d.ts.map +1 -0
- package/dist/schema/binding.js +55 -0
- package/dist/schema/domain.hook.d.ts +10 -0
- package/dist/schema/domain.hook.d.ts.map +1 -0
- package/dist/schema/domain.hook.js +7 -0
- package/dist/schema/function.hook.d.ts +9 -0
- package/dist/schema/function.hook.d.ts.map +1 -0
- package/dist/schema/function.hook.js +40 -0
- package/dist/schema/index.d.ts +10 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +8 -0
- package/dist/schema/kernel.hook.d.ts +10 -0
- package/dist/schema/kernel.hook.d.ts.map +1 -0
- package/dist/schema/kernel.hook.js +8 -0
- package/dist/schema/method.hook.d.ts +14 -0
- package/dist/schema/method.hook.d.ts.map +1 -0
- package/dist/schema/method.hook.js +49 -0
- package/dist/schema/tag.d.ts +11 -0
- package/dist/schema/tag.d.ts.map +1 -0
- package/dist/schema/tag.js +11 -0
- package/dist/schema/types.d.ts +73 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +1 -0
- package/dist/schema/write.d.ts +15 -0
- package/dist/schema/write.d.ts.map +1 -0
- package/dist/schema/write.js +45 -0
- package/dist/selection/index.d.ts +4 -0
- package/dist/selection/index.d.ts.map +1 -0
- package/dist/selection/index.js +4 -0
- package/dist/selection/live.d.ts +21 -0
- package/dist/selection/live.d.ts.map +1 -0
- package/dist/selection/live.js +116 -0
- package/dist/selection/scope.context.d.ts +26 -0
- package/dist/selection/scope.context.d.ts.map +1 -0
- package/dist/selection/scope.context.js +100 -0
- package/dist/selection/selection.hook.d.ts +4 -0
- package/dist/selection/selection.hook.d.ts.map +1 -0
- package/dist/selection/selection.hook.js +14 -0
- package/dist/session/boot/boot.d.ts +19 -0
- package/dist/session/boot/boot.d.ts.map +1 -0
- package/dist/session/boot/boot.js +45 -0
- package/dist/session/boot/ownership.d.ts +18 -0
- package/dist/session/boot/ownership.d.ts.map +1 -0
- package/dist/session/boot/ownership.js +39 -0
- package/dist/session/boot/status.d.ts +24 -0
- package/dist/session/boot/status.d.ts.map +1 -0
- package/dist/session/boot/status.js +11 -0
- package/dist/session/connection/connection.hook.d.ts +3 -0
- package/dist/session/connection/connection.hook.d.ts.map +1 -0
- package/dist/session/connection/connection.hook.js +12 -0
- package/dist/session/connection/connection.store.d.ts +24 -0
- package/dist/session/connection/connection.store.d.ts.map +1 -0
- package/dist/session/connection/connection.store.js +31 -0
- package/dist/session/index.d.ts +8 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +6 -0
- package/dist/session/kernel-client.hook.d.ts +9 -0
- package/dist/session/kernel-client.hook.d.ts.map +1 -0
- package/dist/session/kernel-client.hook.js +5 -0
- package/dist/session/kernel-instance.hook.d.ts +9 -0
- package/dist/session/kernel-instance.hook.d.ts.map +1 -0
- package/dist/session/kernel-instance.hook.js +10 -0
- package/dist/session/provider.d.ts +31 -0
- package/dist/session/provider.d.ts.map +1 -0
- package/dist/session/provider.js +165 -0
- package/dist/session/session.context.d.ts +18 -0
- package/dist/session/session.context.d.ts.map +1 -0
- package/dist/session/session.context.js +8 -0
- package/dist/session/shell.hook.d.ts +4 -0
- package/dist/session/shell.hook.d.ts.map +1 -0
- package/dist/session/shell.hook.js +15 -0
- package/dist/window/adopt.d.ts +27 -0
- package/dist/window/adopt.d.ts.map +1 -0
- package/dist/window/adopt.js +27 -0
- package/dist/window/hooks/close-guard.hook.d.ts +6 -0
- package/dist/window/hooks/close-guard.hook.d.ts.map +1 -0
- package/dist/window/hooks/close-guard.hook.js +90 -0
- package/dist/window/hooks/target-node.hook.d.ts +7 -0
- package/dist/window/hooks/target-node.hook.d.ts.map +1 -0
- package/dist/window/hooks/target-node.hook.js +77 -0
- package/dist/window/hooks/views-for.hook.d.ts +8 -0
- package/dist/window/hooks/views-for.hook.d.ts.map +1 -0
- package/dist/window/hooks/views-for.hook.js +51 -0
- package/dist/window/hooks/window.hook.d.ts +4 -0
- package/dist/window/hooks/window.hook.d.ts.map +1 -0
- package/dist/window/hooks/window.hook.js +14 -0
- package/dist/window/hooks/windows.hook.d.ts +15 -0
- package/dist/window/hooks/windows.hook.d.ts.map +1 -0
- package/dist/window/hooks/windows.hook.js +25 -0
- package/dist/window/host.d.ts +31 -0
- package/dist/window/host.d.ts.map +1 -0
- package/dist/window/host.js +270 -0
- package/dist/window/hub.d.ts +41 -0
- package/dist/window/hub.d.ts.map +1 -0
- package/dist/window/hub.js +100 -0
- package/dist/window/index.d.ts +15 -0
- package/dist/window/index.d.ts.map +1 -0
- package/dist/window/index.js +10 -0
- package/dist/window/lifecycle/mount.d.ts +47 -0
- package/dist/window/lifecycle/mount.d.ts.map +1 -0
- package/dist/window/lifecycle/mount.js +92 -0
- package/dist/window/lifecycle/states.d.ts +33 -0
- package/dist/window/lifecycle/states.d.ts.map +1 -0
- package/dist/window/lifecycle/states.js +7 -0
- package/dist/window/placement.d.ts +33 -0
- package/dist/window/placement.d.ts.map +1 -0
- package/dist/window/placement.js +57 -0
- package/dist/window/stage.d.ts +3 -0
- package/dist/window/stage.d.ts.map +1 -0
- package/dist/window/stage.js +41 -0
- package/package.json +59 -0
- package/src/application/apps/app-host.tsx +113 -0
- package/src/application/apps/app-logo.tsx +56 -0
- package/src/application/apps/apps.hook.ts +81 -0
- package/src/application/apps/open-app.hook.ts +18 -0
- package/src/application/index.ts +22 -0
- package/src/application/roles.hook.ts +41 -0
- package/src/application/spaces/current-space.hook.ts +43 -0
- package/src/application/spaces/current-space.store.ts +110 -0
- package/src/application/spaces/space.hook.ts +41 -0
- package/src/application/spaces/spaces.hook.ts +113 -0
- package/src/application/user/user.hook.ts +27 -0
- package/src/application/user/user.store.ts +85 -0
- package/src/auth/act-as.context.tsx +124 -0
- package/src/auth/auth.hook.ts +36 -0
- package/src/auth/can.hook.ts +149 -0
- package/src/auth/index.ts +6 -0
- package/src/auth/self.hook.ts +14 -0
- package/src/auth/session-kernel.ts +24 -0
- package/src/graph/action.ts +127 -0
- package/src/graph/async.ts +87 -0
- package/src/graph/call/call.hook.ts +42 -0
- package/src/graph/call/stream.hook.ts +200 -0
- package/src/graph/graph.context.ts +14 -0
- package/src/graph/guards.error.ts +58 -0
- package/src/graph/index.ts +20 -0
- package/src/graph/memory/base.ts +50 -0
- package/src/graph/memory/read/envelope.ts +98 -0
- package/src/graph/memory/read/idle.ts +24 -0
- package/src/graph/memory/read/read.ts +103 -0
- package/src/graph/memory/scope.ts +29 -0
- package/src/graph/mutate/move.hook.ts +70 -0
- package/src/graph/mutate/mutate.hook.ts +69 -0
- package/src/graph/query/children.hook.ts +136 -0
- package/src/graph/query/graph.hook.ts +13 -0
- package/src/graph/query/neighbors.hook.ts +124 -0
- package/src/graph/query/node.hook.ts +83 -0
- package/src/graph/query/query.hook.ts +42 -0
- package/src/index.ts +202 -0
- package/src/intents/envelope.ts +52 -0
- package/src/intents/index.ts +6 -0
- package/src/intents/listen-intent.hook.ts +92 -0
- package/src/intents/reply.ts +24 -0
- package/src/intents/send-intent.hook.ts +111 -0
- package/src/navigation/adapters/adapter.ts +13 -0
- package/src/navigation/adapters/memory.adapter.ts +22 -0
- package/src/navigation/adapters/url.adapter.ts +61 -0
- package/src/navigation/history.ts +170 -0
- package/src/navigation/hooks/ancestors.hook.ts +71 -0
- package/src/navigation/hooks/location.hook.ts +21 -0
- package/src/navigation/hooks/navigate.hook.ts +43 -0
- package/src/navigation/hooks/scope.hook.ts +34 -0
- package/src/navigation/index.ts +17 -0
- package/src/navigation/link.tsx +110 -0
- package/src/navigation/location.ts +122 -0
- package/src/navigation/scope.context.tsx +267 -0
- package/src/navigation/snapshot.ts +79 -0
- package/src/schema/bind-read.ts +319 -0
- package/src/schema/binding.ts +91 -0
- package/src/schema/domain.hook.ts +16 -0
- package/src/schema/function.hook.ts +68 -0
- package/src/schema/index.ts +25 -0
- package/src/schema/kernel.hook.ts +17 -0
- package/src/schema/method.hook.ts +103 -0
- package/src/schema/tag.ts +19 -0
- package/src/schema/types.ts +150 -0
- package/src/schema/write.ts +80 -0
- package/src/selection/index.ts +5 -0
- package/src/selection/live.ts +142 -0
- package/src/selection/scope.context.tsx +151 -0
- package/src/selection/selection.hook.ts +18 -0
- package/src/session/boot/boot.ts +57 -0
- package/src/session/boot/ownership.ts +54 -0
- package/src/session/boot/status.ts +32 -0
- package/src/session/connection/connection.hook.ts +16 -0
- package/src/session/connection/connection.store.ts +58 -0
- package/src/session/index.ts +8 -0
- package/src/session/kernel-client.hook.ts +13 -0
- package/src/session/kernel-instance.hook.ts +22 -0
- package/src/session/provider.tsx +205 -0
- package/src/session/session.context.ts +27 -0
- package/src/session/shell.hook.ts +19 -0
- package/src/window/adopt.ts +57 -0
- package/src/window/hooks/close-guard.hook.ts +100 -0
- package/src/window/hooks/target-node.hook.ts +94 -0
- package/src/window/hooks/views-for.hook.ts +82 -0
- package/src/window/hooks/window.hook.ts +18 -0
- package/src/window/hooks/windows.hook.ts +40 -0
- package/src/window/host.tsx +347 -0
- package/src/window/hub.ts +156 -0
- package/src/window/index.ts +17 -0
- package/src/window/lifecycle/mount.ts +134 -0
- package/src/window/lifecycle/states.ts +23 -0
- package/src/window/placement.ts +84 -0
- package/src/window/stage.tsx +46 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error taxonomy (API §1.4) — re-exported from kernel-client so shell-react
|
|
3
|
+
* consumers narrow dispatch failures through the single import surface. The
|
|
4
|
+
* dispatched kernel call stays the source of truth; these guards drive UI
|
|
5
|
+
* affordances (retry, request-access, toast). Names match kernel-client exactly.
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
AuthBatchError,
|
|
9
|
+
AuthenticationError,
|
|
10
|
+
ClientSessionError,
|
|
11
|
+
ConnectionError,
|
|
12
|
+
DisconnectedError,
|
|
13
|
+
InvalidTransportError,
|
|
14
|
+
InvariantViolationError,
|
|
15
|
+
KernelError,
|
|
16
|
+
NodeUnavailableError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
PermissionDeniedError,
|
|
19
|
+
TimeoutError,
|
|
20
|
+
UnsupportedTransportError,
|
|
21
|
+
ValidationError,
|
|
22
|
+
WebOSError,
|
|
23
|
+
classifyKernelError,
|
|
24
|
+
isAuthBatchError,
|
|
25
|
+
isAuthenticationError,
|
|
26
|
+
isClientSessionError,
|
|
27
|
+
isDelegationMintError,
|
|
28
|
+
isDelegationPolicyError,
|
|
29
|
+
isDelegationRefusedError,
|
|
30
|
+
isHopLimitExceededError,
|
|
31
|
+
isIdentityError,
|
|
32
|
+
isInternalKernelError,
|
|
33
|
+
isInvalidRequestError,
|
|
34
|
+
isInvalidTransportError,
|
|
35
|
+
isInvariantViolationError,
|
|
36
|
+
isKernelError,
|
|
37
|
+
isNodeUnavailableError,
|
|
38
|
+
isNotFoundError,
|
|
39
|
+
isParseError,
|
|
40
|
+
isPermissionDeniedError,
|
|
41
|
+
isPersistenceLoadFailedError,
|
|
42
|
+
isPoolClosedError,
|
|
43
|
+
isRedirectPolicyError,
|
|
44
|
+
isRedirectRefusedError,
|
|
45
|
+
isRegistryWriteFailedError,
|
|
46
|
+
isSchemaOwnershipConflictError,
|
|
47
|
+
isTransportError,
|
|
48
|
+
isUnsupportedTransportError,
|
|
49
|
+
isValidationError,
|
|
50
|
+
isWebOSError,
|
|
51
|
+
} from '@astrale-os/kernel-client'
|
|
52
|
+
export type {
|
|
53
|
+
AuthReason,
|
|
54
|
+
ClientErrorDetails,
|
|
55
|
+
IdentityError,
|
|
56
|
+
KernelErrorKind,
|
|
57
|
+
TransportError,
|
|
58
|
+
} from '@astrale-os/kernel-client'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// graph — the graph plane (API §5 + §7). Merged read hooks, writes, dispatch.
|
|
2
|
+
export { useNode } from './query/node.hook'
|
|
3
|
+
export type { NodeResult } from './query/node.hook'
|
|
4
|
+
export { useChildren } from './query/children.hook'
|
|
5
|
+
export type { ChildrenOptions, ChildrenResult } from './query/children.hook'
|
|
6
|
+
export { useQuery } from './query/query.hook'
|
|
7
|
+
export type { QueryResult } from './query/query.hook'
|
|
8
|
+
export { useGraph } from './query/graph.hook'
|
|
9
|
+
export { useMutate } from './mutate/mutate.hook'
|
|
10
|
+
export type { UseMutateOptions } from './mutate/mutate.hook'
|
|
11
|
+
export { useMove } from './mutate/move.hook'
|
|
12
|
+
export type { MoveAction } from './mutate/move.hook'
|
|
13
|
+
export { useCall } from './call/call.hook'
|
|
14
|
+
export type { CallOptions } from './call/call.hook'
|
|
15
|
+
export { useStream } from './call/stream.hook'
|
|
16
|
+
export type { StreamHandle, StreamOptions } from './call/stream.hook'
|
|
17
|
+
export type { Action } from './action'
|
|
18
|
+
export type { ReadState, ReadOptions, Windowed, Node } from './memory/read/envelope'
|
|
19
|
+
export type { GraphMemory } from './graph.context'
|
|
20
|
+
export type { ShapeInput } from '@astrale-os/kernel-client/store'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { GraphApi } from '@astrale-os/kernel-client/graph'
|
|
2
|
+
import type { GraphMemory, MemoryEvent } from '@astrale-os/kernel-client/store'
|
|
3
|
+
/**
|
|
4
|
+
* The base memory (API §5.4). One `GraphMemory` per live Shell — the partial
|
|
5
|
+
* client copy of the graph every graph/schema/nav hook reads through, so they all
|
|
6
|
+
* share one cache, one dedupe, one optimistic overlay. Derived from `shell.kernel`
|
|
7
|
+
* and memoized on the Shell object. Its `onError` fans out through a per-Shell
|
|
8
|
+
* sink the provider registers, so warm-refresh failures and rolled-back mutations
|
|
9
|
+
* reach the provider's `onError` channel (§2).
|
|
10
|
+
*/
|
|
11
|
+
import type { Shell } from '@astrale-os/shell'
|
|
12
|
+
|
|
13
|
+
import { createMemory } from '@astrale-os/kernel-client/store'
|
|
14
|
+
|
|
15
|
+
/** One memory per Shell reference — shared by every hook under that provider. */
|
|
16
|
+
const memoryByShell = new WeakMap<Shell, GraphMemory>()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per-Shell error sink (API §2/§8). The provider registers a bridge here; the
|
|
20
|
+
* memory emits refresh/mutation/divergence events through it, read lazily at
|
|
21
|
+
* emit time so registration order doesn't matter. Cleared on provider unmount.
|
|
22
|
+
*/
|
|
23
|
+
const errorSinks = new WeakMap<Shell, (event: MemoryEvent) => void>()
|
|
24
|
+
|
|
25
|
+
/** Register (or clear) the provider's memory-event bridge for a Shell. */
|
|
26
|
+
export function setMemoryErrorSink(
|
|
27
|
+
shell: Shell,
|
|
28
|
+
sink: ((event: MemoryEvent) => void) | null,
|
|
29
|
+
): void {
|
|
30
|
+
if (sink === null) errorSinks.delete(shell)
|
|
31
|
+
else errorSinks.set(shell, sink)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The base (default-scope) memory for a Shell — the SAME instance every graph/
|
|
36
|
+
* schema/nav hook reads through `useMemory`, so the provider can seat it in
|
|
37
|
+
* `GraphContext` (selection's liveness source) without minting a rival copy.
|
|
38
|
+
* Its `onError` fans out through the per-Shell sink so warm-refresh failures and
|
|
39
|
+
* rolled-back mutations reach `ShellProvider`'s `onError` channel (§2).
|
|
40
|
+
*/
|
|
41
|
+
export function baseMemoryFor(shell: Shell): GraphMemory {
|
|
42
|
+
let memory = memoryByShell.get(shell)
|
|
43
|
+
if (memory === undefined) {
|
|
44
|
+
memory = createMemory(shell.kernel as GraphApi, {
|
|
45
|
+
onError: (event) => errorSinks.get(shell)?.(event),
|
|
46
|
+
})
|
|
47
|
+
memoryByShell.set(shell, memory)
|
|
48
|
+
}
|
|
49
|
+
return memory
|
|
50
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read envelope (API §1.1, §1.5) — the shape every read hook's result
|
|
3
|
+
* extends (`ReadState` + `Windowed`), the `ReadOptions` accepted by every read,
|
|
4
|
+
* and the assembly of the unified `RawRead` projection over a memory shape
|
|
5
|
+
* (empty/idle/keep-previous builders). Dependency-light so every context can
|
|
6
|
+
* consume it without a cycle.
|
|
7
|
+
*/
|
|
8
|
+
import type { LiveResult } from '@astrale-os/kernel-client/store'
|
|
9
|
+
|
|
10
|
+
// ── Read envelope (§1.1) ─────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/** Base of every read hook's result — stale-while-revalidate + liveness + seq. */
|
|
13
|
+
export interface ReadState {
|
|
14
|
+
/** A (re)validation is in flight; data shown alongside is last-good. */
|
|
15
|
+
readonly pending: boolean
|
|
16
|
+
/** Last refresh failure; last-good data is retained. Cleared on next success. */
|
|
17
|
+
readonly error: unknown
|
|
18
|
+
/** A watch subscription is active for this shape (Tier ≥ 1), or Tier-0 only. */
|
|
19
|
+
readonly live: boolean
|
|
20
|
+
/** Journal position this result reflects (FRONTEND spec §6.1). 0 until known. */
|
|
21
|
+
readonly seq: number
|
|
22
|
+
/** Manual revalidate. Rarely needed; kept for tooling and non-live reads. */
|
|
23
|
+
refetch(): Promise<void>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Windowed reads add a cursor-fold `more`. */
|
|
27
|
+
export interface Windowed {
|
|
28
|
+
readonly more: {
|
|
29
|
+
readonly has: boolean
|
|
30
|
+
load(): Promise<void>
|
|
31
|
+
readonly pending: boolean
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Options accepted by every read hook (§1.5). */
|
|
36
|
+
export interface ReadOptions {
|
|
37
|
+
/** default true; false = one-shot snapshot. */
|
|
38
|
+
live?: boolean
|
|
39
|
+
/** default true; false → pending-without-data on cold. */
|
|
40
|
+
suspend?: boolean
|
|
41
|
+
/** default false; show previous shape's data while a new one confirms. */
|
|
42
|
+
keepPrevious?: boolean
|
|
43
|
+
/** Visibility permission on the underlying AST — a kernel permission bitmask. */
|
|
44
|
+
with?: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A graph node — derived from the memory result so no direct kernel-core import is needed. */
|
|
48
|
+
export type Node = NonNullable<LiveResult['node']>
|
|
49
|
+
|
|
50
|
+
/** Read options plus the windowing lever `useChildren`/`useQuery` add. */
|
|
51
|
+
export type CoreReadOptions = ReadOptions & { window?: 'append' | 'paged' }
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The unified read projection: every data arm the memory materializes, over
|
|
55
|
+
* the shell-react read envelope (`ReadState` + `Windowed`). Each hook narrows
|
|
56
|
+
* to the arms it exposes. The store's `LiveResult` is a structural superset, so
|
|
57
|
+
* the warm path returns it directly (stable ref).
|
|
58
|
+
*/
|
|
59
|
+
export interface RawRead extends ReadState, Windowed {
|
|
60
|
+
readonly nodes: readonly Node[]
|
|
61
|
+
readonly roots: readonly Node[]
|
|
62
|
+
readonly first: Node | null
|
|
63
|
+
readonly children: readonly Node[]
|
|
64
|
+
gathered(alias: string): { readonly nodes: readonly Node[]; readonly first: Node | null }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const EMPTY: readonly Node[] = []
|
|
68
|
+
|
|
69
|
+
const idleGathered = (): { readonly nodes: readonly Node[]; readonly first: Node | null } => ({
|
|
70
|
+
nodes: EMPTY,
|
|
71
|
+
first: null,
|
|
72
|
+
})
|
|
73
|
+
const idleMore: Windowed['more'] = { has: false, pending: false, load: () => Promise.resolve() }
|
|
74
|
+
|
|
75
|
+
export function emptyRead(pending: boolean): RawRead {
|
|
76
|
+
return {
|
|
77
|
+
nodes: EMPTY,
|
|
78
|
+
roots: EMPTY,
|
|
79
|
+
first: null,
|
|
80
|
+
children: EMPTY,
|
|
81
|
+
gathered: idleGathered,
|
|
82
|
+
pending,
|
|
83
|
+
error: undefined,
|
|
84
|
+
live: false,
|
|
85
|
+
seq: 0,
|
|
86
|
+
more: idleMore,
|
|
87
|
+
refetch: () => Promise.resolve(),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The result for a null/undefined subject (§1.3) — settled, empty, no I/O. */
|
|
91
|
+
export const IDLE_READ = emptyRead(false)
|
|
92
|
+
/** Cold shape with `suspend:false` (§1.4) — empty data, pending true. */
|
|
93
|
+
export const EMPTY_PENDING = emptyRead(true)
|
|
94
|
+
|
|
95
|
+
/** Previous shape's data while a new one confirms (§1.5 `keepPrevious`). */
|
|
96
|
+
export function withPending(prev: LiveResult): RawRead {
|
|
97
|
+
return { ...prev, pending: true }
|
|
98
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The idle-input rule (API §1.3). A read hook's `null | undefined` subject is
|
|
3
|
+
* inert: `isIdleInput` is the one gate every read branches on, and
|
|
4
|
+
* `idleReadState` is the settled, not-live, seq-0 envelope it returns.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReadState } from './envelope'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The chaining rule: every read hook accepts `null | undefined` for its subject
|
|
10
|
+
* and returns an *idle* result (empty data, no I/O). This predicate is the one
|
|
11
|
+
* gate — read hooks branch on it before touching the memory.
|
|
12
|
+
*/
|
|
13
|
+
export function isIdleInput(subject: unknown): subject is null | undefined {
|
|
14
|
+
return subject === null || subject === undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The idle `ReadState` a read hook returns for a `null`/`undefined` subject:
|
|
19
|
+
* settled, not live, seq 0, `refetch` a resolved no-op.
|
|
20
|
+
*/
|
|
21
|
+
export function idleReadState(): ReadState {
|
|
22
|
+
return { pending: false, error: undefined, live: false, seq: 0, refetch: idleRefetch }
|
|
23
|
+
}
|
|
24
|
+
const idleRefetch = (): Promise<void> => Promise.resolve()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LiveResult,
|
|
3
|
+
ReadOptions as StoreReadOptions,
|
|
4
|
+
ShapeInput,
|
|
5
|
+
} from '@astrale-os/kernel-client/store'
|
|
6
|
+
|
|
7
|
+
import { canonicalizeAST, shapeInputToAST, stableStringify } from '@astrale-os/kernel-client/store'
|
|
8
|
+
/**
|
|
9
|
+
* The shared read substrate (API §1, §5). One `useSyncExternalStore` binding over
|
|
10
|
+
* a memory shape — the substrate every graph read hook narrows. Handles the four
|
|
11
|
+
* cross-cutting concerns once: idle inputs (§1.3), suspense-first on a cold shape
|
|
12
|
+
* (§1.4), stale-while-revalidate + `keepPrevious` (§1.5), and referential
|
|
13
|
+
* stability (§1.7 — an unchanged shape returns the store's own stable snapshot).
|
|
14
|
+
*/
|
|
15
|
+
import { useCallback, useRef, useSyncExternalStore } from 'react'
|
|
16
|
+
|
|
17
|
+
import type { CoreReadOptions, RawRead } from './envelope'
|
|
18
|
+
|
|
19
|
+
import { useMemory } from '../scope'
|
|
20
|
+
import { EMPTY_PENDING, IDLE_READ, withPending } from './envelope'
|
|
21
|
+
import { isIdleInput } from './idle'
|
|
22
|
+
|
|
23
|
+
const NOOP_UNSUB = (): void => {}
|
|
24
|
+
const IDLE_TOKEN = ' idle'
|
|
25
|
+
|
|
26
|
+
/** Project shell-react read options onto the store's — `with`/`window`/`live` key the shape. */
|
|
27
|
+
function storeReadOptions(opts?: CoreReadOptions): StoreReadOptions | undefined {
|
|
28
|
+
if (opts === undefined) return undefined
|
|
29
|
+
const out: StoreReadOptions = {}
|
|
30
|
+
if (opts.live !== undefined) out.live = opts.live
|
|
31
|
+
if (opts.window !== undefined) out.window = opts.window
|
|
32
|
+
// `with` is a PermName at the store/kernel (READ lit-up today); the public
|
|
33
|
+
// `PermMask` typing reconciles with the grant epoch (API §14 G7).
|
|
34
|
+
if (opts.with !== undefined) out.with = opts.with as unknown as StoreReadOptions['with']
|
|
35
|
+
return out
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A stable per-render key for a shape input + its keying options, so the
|
|
40
|
+
* subscription/snapshot closures only re-bind when the LOGICAL read changes
|
|
41
|
+
* (not when an inline builder closure is re-created each render).
|
|
42
|
+
*/
|
|
43
|
+
function shapeToken(shape: ShapeInput, opts?: CoreReadOptions): string {
|
|
44
|
+
const canon = stableStringify(canonicalizeAST(shapeInputToAST(shape)))
|
|
45
|
+
return `${canon}#w=${String(opts?.with ?? '')}#win=${opts?.window ?? ''}#live=${
|
|
46
|
+
opts?.live === false ? '0' : '1'
|
|
47
|
+
}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to a memory shape and project it. `null`/`undefined` shape → idle.
|
|
52
|
+
* Cold + `suspend` (default) throws the cold-shape promise; `suspend:false`
|
|
53
|
+
* returns empty-pending; `keepPrevious` returns the last data while confirming.
|
|
54
|
+
*/
|
|
55
|
+
export function useRead(shape: ShapeInput | null | undefined, opts?: CoreReadOptions): RawRead {
|
|
56
|
+
const memory = useMemory()
|
|
57
|
+
const idle = isIdleInput(shape)
|
|
58
|
+
|
|
59
|
+
const shapeRef = useRef<ShapeInput | null | undefined>(shape)
|
|
60
|
+
shapeRef.current = shape
|
|
61
|
+
const optsRef = useRef<CoreReadOptions | undefined>(opts)
|
|
62
|
+
optsRef.current = opts
|
|
63
|
+
|
|
64
|
+
const token = idle ? IDLE_TOKEN : shapeToken(shape as ShapeInput, opts)
|
|
65
|
+
|
|
66
|
+
const subscribe = useCallback(
|
|
67
|
+
(onStoreChange: () => void) => {
|
|
68
|
+
const s = shapeRef.current
|
|
69
|
+
if (token === IDLE_TOKEN || s === null || s === undefined) return NOOP_UNSUB
|
|
70
|
+
return memory.subscribe(s, onStoreChange, storeReadOptions(optsRef.current))
|
|
71
|
+
},
|
|
72
|
+
[memory, token],
|
|
73
|
+
)
|
|
74
|
+
const getSnapshot = useCallback((): LiveResult | null => {
|
|
75
|
+
const s = shapeRef.current
|
|
76
|
+
if (token === IDLE_TOKEN || s === null || s === undefined) return null
|
|
77
|
+
return memory.getSnapshot(s, storeReadOptions(optsRef.current))
|
|
78
|
+
}, [memory, token])
|
|
79
|
+
|
|
80
|
+
const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
|
81
|
+
const prevRef = useRef<LiveResult | null>(null)
|
|
82
|
+
|
|
83
|
+
if (idle) return IDLE_READ
|
|
84
|
+
|
|
85
|
+
const warm = memory.peek(shape as ShapeInput, storeReadOptions(opts)) !== null
|
|
86
|
+
if (warm && snap !== null) {
|
|
87
|
+
prevRef.current = snap
|
|
88
|
+
return snap
|
|
89
|
+
}
|
|
90
|
+
if (opts?.keepPrevious === true && prevRef.current !== null) {
|
|
91
|
+
return withPending(prevRef.current)
|
|
92
|
+
}
|
|
93
|
+
if (opts?.suspend !== false) {
|
|
94
|
+
// Cold FAILURE (§1.4): the shape fetched and rejected with no data to show —
|
|
95
|
+
// throw the error to the nearest boundary. Re-throwing the promise here would
|
|
96
|
+
// re-arm a fresh fetch every retry (the entry's inflight was cleared on
|
|
97
|
+
// failure), looping render→fetch→reject forever. This breaks that loop.
|
|
98
|
+
if (snap !== null && snap.error !== undefined) throw snap.error
|
|
99
|
+
// Cold shape: suspend on the first-confirmed-data promise (§1.4).
|
|
100
|
+
throw memory.promise(shape as ShapeInput, storeReadOptions(opts))
|
|
101
|
+
}
|
|
102
|
+
return EMPTY_PENDING
|
|
103
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { GraphApi } from '@astrale-os/kernel-client/graph'
|
|
2
|
+
import type { GraphMemory } from '@astrale-os/kernel-client/store'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Memory scope resolution (API §5.4). `useMemory` returns the memory for this
|
|
6
|
+
* subtree: the base memory derived from the provider's Shell, re-scoped (and
|
|
7
|
+
* re-api'd) to the `<ActAs>` principal via `forScope` when an identity binding is
|
|
8
|
+
* present — so a principal never shares cached results with another.
|
|
9
|
+
*/
|
|
10
|
+
import { useContext, useMemo } from 'react'
|
|
11
|
+
|
|
12
|
+
import { IdentityContext } from '../../auth/act-as.context'
|
|
13
|
+
import { ShellContext } from '../../session/session.context'
|
|
14
|
+
import { baseMemoryFor } from './base'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The memory for this subtree. Throws outside a `<ShellProvider>`. Above any
|
|
18
|
+
* `<ActAs>` it is the base memory; inside one it is scoped by the binding.
|
|
19
|
+
*/
|
|
20
|
+
export function useMemory(): GraphMemory {
|
|
21
|
+
const runtime = useContext(ShellContext)
|
|
22
|
+
const binding = useContext(IdentityContext)
|
|
23
|
+
const shell = runtime?.shell ?? null
|
|
24
|
+
return useMemo(() => {
|
|
25
|
+
if (shell === null) throw new Error('graph hooks must be used within a <ShellProvider>')
|
|
26
|
+
const base = baseMemoryFor(shell)
|
|
27
|
+
return binding === null ? base : base.forScope(binding.scope, binding.kernel as GraphApi)
|
|
28
|
+
}, [shell, binding])
|
|
29
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { NodeRefInput } from '@astrale-os/kernel-client'
|
|
2
|
+
import type { PathLike } from '@astrale-os/kernel-client/graph'
|
|
3
|
+
import type { GraphMemory } from '@astrale-os/kernel-client/store'
|
|
4
|
+
|
|
5
|
+
import { rawOf } from '@astrale-os/kernel-client/graph'
|
|
6
|
+
/**
|
|
7
|
+
* useMove (API §5, the G1 seam) — dispatch the sealed `node::move` method (the one
|
|
8
|
+
* tree op no patch arm can express). PESSIMISTIC today (honest `pending`); affected
|
|
9
|
+
* container shapes revalidate on settlement. Turns optimistic when the `nodes.move`
|
|
10
|
+
* patch arm lands, with zero consumer change.
|
|
11
|
+
*/
|
|
12
|
+
import { useCallback } from 'react'
|
|
13
|
+
|
|
14
|
+
import type { Action } from '../action'
|
|
15
|
+
import type { Node } from '../memory/read/envelope'
|
|
16
|
+
|
|
17
|
+
import { useSessionKernel } from '../../auth/session-kernel'
|
|
18
|
+
import { useAction } from '../action'
|
|
19
|
+
import { useMemory } from '../memory/scope'
|
|
20
|
+
|
|
21
|
+
export type MoveAction = Action<[node: Node | NodeRefInput, to: PathLike], void>
|
|
22
|
+
|
|
23
|
+
export function useMove(): MoveAction {
|
|
24
|
+
const kernel = useSessionKernel()
|
|
25
|
+
const memory = useMemory()
|
|
26
|
+
const run = useCallback(
|
|
27
|
+
async (node: Node | NodeRefInput, to: PathLike): Promise<void> => {
|
|
28
|
+
const method = isNode(node) ? `@${node.id}::move` : `${rawOf(node)}::move`
|
|
29
|
+
await kernel.call(method, { to: rawOf(to) })
|
|
30
|
+
await reconcileMove(memory, node, to)
|
|
31
|
+
},
|
|
32
|
+
[kernel, memory],
|
|
33
|
+
)
|
|
34
|
+
return useAction(run)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isNode(x: Node | NodeRefInput): x is Node {
|
|
38
|
+
return typeof x === 'object' && x !== null && 'id' in x && 'class' in x && 'path' in x
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Root-first parent of a raw absolute path, or `null` at the root. */
|
|
42
|
+
function parentPathOf(raw: string): string | null {
|
|
43
|
+
const trimmed = raw.replace(/\/+$/, '')
|
|
44
|
+
const i = trimmed.lastIndexOf('/')
|
|
45
|
+
if (i < 0) return null
|
|
46
|
+
return i === 0 ? '/' : trimmed.slice(0, i)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Best-effort settlement reconciliation (Tier 0): refetch the source and
|
|
51
|
+
* destination container's default `children` shapes when they are observed. A
|
|
52
|
+
* custom-ordered/filtered children shape is keyed distinctly and is not reached
|
|
53
|
+
* here — consumers get honest `pending` and reconcile on G1.
|
|
54
|
+
*/
|
|
55
|
+
async function reconcileMove(
|
|
56
|
+
memory: GraphMemory,
|
|
57
|
+
node: Node | NodeRefInput,
|
|
58
|
+
to: PathLike,
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
const path = isNode(node) ? rawOf(node.path) : rawOf(node)
|
|
61
|
+
const containers = new Set<string>([rawOf(to)])
|
|
62
|
+
const old = parentPathOf(path)
|
|
63
|
+
if (old !== null) containers.add(old)
|
|
64
|
+
await Promise.allSettled(
|
|
65
|
+
[...containers].map((c) => {
|
|
66
|
+
const snap = memory.peek((q) => q.from(c).children())
|
|
67
|
+
return snap !== null ? snap.refetch() : Promise.resolve()
|
|
68
|
+
}),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PatchBuilder } from '@astrale-os/kernel-client/graph'
|
|
2
|
+
import type { GraphMemory } from '@astrale-os/kernel-client/store'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* useMutate (API 5/1.6) - build a graph-scoped Action. The callback receives
|
|
6
|
+
* this subtree's GraphMemory, so callers do not need a separate useGraph() just
|
|
7
|
+
* to author the mutation. Patch-shaped writes are optimistic via their own
|
|
8
|
+
* overlay; pass a function `optimistic` to preview an opaque dispatch effect.
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useRef } from 'react'
|
|
11
|
+
|
|
12
|
+
import type { Action } from '../action'
|
|
13
|
+
|
|
14
|
+
import { useAction } from '../action'
|
|
15
|
+
import { useMemory } from '../memory/scope'
|
|
16
|
+
|
|
17
|
+
export interface UseMutateOptions<A extends unknown[], R> {
|
|
18
|
+
/**
|
|
19
|
+
* `'auto'` (default) / `false`: the write's own overlay governs optimism - the
|
|
20
|
+
* callback's graph writes apply the authored patch. A function opens a
|
|
21
|
+
* MANUAL overlay decoupled from what the callback dispatches.
|
|
22
|
+
*/
|
|
23
|
+
optimistic?: 'auto' | false | ((overlay: PatchBuilder, ...args: A) => void)
|
|
24
|
+
/** default 'auto'. false = keep the manual overlay on failure (caller reconciles). */
|
|
25
|
+
rollback?: 'auto' | false
|
|
26
|
+
onCommit?: (result: R) => void
|
|
27
|
+
onError?: (error: unknown) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useMutate<A extends unknown[], R>(
|
|
31
|
+
fn: (graph: GraphMemory, ...args: A) => Promise<R>,
|
|
32
|
+
opts?: UseMutateOptions<A, R>,
|
|
33
|
+
): Action<A, R> {
|
|
34
|
+
const memory = useMemory()
|
|
35
|
+
const fnRef = useRef(fn)
|
|
36
|
+
fnRef.current = fn
|
|
37
|
+
const optsRef = useRef(opts)
|
|
38
|
+
optsRef.current = opts
|
|
39
|
+
|
|
40
|
+
const run = useCallback(
|
|
41
|
+
async (...args: A): Promise<R> => {
|
|
42
|
+
const o = optsRef.current
|
|
43
|
+
const optimistic = o?.optimistic
|
|
44
|
+
if (typeof optimistic === 'function') {
|
|
45
|
+
const overlay = memory.overlay((m) => optimistic(m, ...args))
|
|
46
|
+
try {
|
|
47
|
+
const result = await fnRef.current(memory, ...args)
|
|
48
|
+
overlay.commit()
|
|
49
|
+
o?.onCommit?.(result)
|
|
50
|
+
return result
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (o?.rollback !== false) overlay.rollback()
|
|
53
|
+
o?.onError?.(error)
|
|
54
|
+
throw error
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const result = await fnRef.current(memory, ...args)
|
|
59
|
+
o?.onCommit?.(result)
|
|
60
|
+
return result
|
|
61
|
+
} catch (error) {
|
|
62
|
+
o?.onError?.(error)
|
|
63
|
+
throw error
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[memory],
|
|
67
|
+
)
|
|
68
|
+
return useAction(run)
|
|
69
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { AnyBoundNode, TypeNames } from '@astrale-os/kernel-client'
|
|
2
|
+
import type {
|
|
3
|
+
GatherBuilder,
|
|
4
|
+
OrderInput,
|
|
5
|
+
PathLike,
|
|
6
|
+
QuerySource,
|
|
7
|
+
WalkOpts,
|
|
8
|
+
} from '@astrale-os/kernel-client/graph'
|
|
9
|
+
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
10
|
+
|
|
11
|
+
import { rawOf } from '@astrale-os/kernel-client/graph'
|
|
12
|
+
/**
|
|
13
|
+
* useChildren (API §5 + §6) — ONE public symbol, overloaded: pass `opts.type`
|
|
14
|
+
* (over a `BoundNode` parent) for the typed children, or a bare parent for the
|
|
15
|
+
* untyped children. Both ride the SAME windowed memory shape. The untyped floor
|
|
16
|
+
* is a windowed, live read of a node's direct children; the typed twin binds them.
|
|
17
|
+
*/
|
|
18
|
+
import { useMemo } from 'react'
|
|
19
|
+
|
|
20
|
+
import type { TypedChildrenResult } from '../../schema/types'
|
|
21
|
+
import type { ReadState, Node, ReadOptions, Windowed } from '../memory/read/envelope'
|
|
22
|
+
|
|
23
|
+
import { useChildren as useChildrenTyped } from '../../schema/bind-read'
|
|
24
|
+
import { useRead } from '../memory/read/read'
|
|
25
|
+
|
|
26
|
+
export interface ChildrenOptions extends ReadOptions {
|
|
27
|
+
/** Restrict to these classes (dual-path descent filter). */
|
|
28
|
+
classes?: PathLike[]
|
|
29
|
+
/** Server-ordered by a node prop / `id`. */
|
|
30
|
+
order?: OrderInput
|
|
31
|
+
/** Page size; `more.load()` extends. */
|
|
32
|
+
limit?: number
|
|
33
|
+
/** `append` (default, infinite) vs `paged` (replace). */
|
|
34
|
+
window?: 'append' | 'paged'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ChildrenResult extends ReadState, Windowed {
|
|
38
|
+
/** Ordered, all loaded pages, parent excluded. */
|
|
39
|
+
readonly children: readonly Node[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A node's direct children — windowed, live; `null` parent is idle (§1.3). */
|
|
43
|
+
function useChildrenUntyped(
|
|
44
|
+
parent: Node | PathLike | null | undefined,
|
|
45
|
+
opts?: ChildrenOptions,
|
|
46
|
+
): ChildrenResult {
|
|
47
|
+
const shape = isIdle(parent) ? null : childrenShape(parent, opts)
|
|
48
|
+
const r = useRead(shape, opts)
|
|
49
|
+
return useMemo(
|
|
50
|
+
() => ({
|
|
51
|
+
pending: r.pending,
|
|
52
|
+
error: r.error,
|
|
53
|
+
live: r.live,
|
|
54
|
+
seq: r.seq,
|
|
55
|
+
refetch: r.refetch,
|
|
56
|
+
children: r.children,
|
|
57
|
+
more: r.more,
|
|
58
|
+
}),
|
|
59
|
+
[r],
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Typed: a bound node's children of `opts.type`. */
|
|
64
|
+
export function useChildren<S extends Schema, T extends TypeNames<S>>(
|
|
65
|
+
parent: AnyBoundNode<S> | null | undefined,
|
|
66
|
+
opts: ReadOptions & {
|
|
67
|
+
type: T
|
|
68
|
+
order?: OrderInput
|
|
69
|
+
limit?: number
|
|
70
|
+
window?: 'append' | 'paged'
|
|
71
|
+
},
|
|
72
|
+
): TypedChildrenResult<S, T>
|
|
73
|
+
/** Untyped: a node's direct children — windowed, live; `null` parent is idle (§1.3). */
|
|
74
|
+
export function useChildren(
|
|
75
|
+
parent: Node | PathLike | null | undefined,
|
|
76
|
+
opts?: ChildrenOptions,
|
|
77
|
+
): ChildrenResult
|
|
78
|
+
export function useChildren(
|
|
79
|
+
parent: unknown,
|
|
80
|
+
opts?: unknown,
|
|
81
|
+
): ReadState & Windowed & { readonly children: readonly unknown[] } {
|
|
82
|
+
if (hasType(opts)) {
|
|
83
|
+
return useChildrenTyped(
|
|
84
|
+
parent as AnyBoundNode<Schema> | null | undefined,
|
|
85
|
+
opts as ReadOptions & { type: string },
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
return useChildrenUntyped(
|
|
89
|
+
parent as Node | PathLike | null | undefined,
|
|
90
|
+
opts as ChildrenOptions | undefined,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hasType(opts: unknown): opts is { type: string } {
|
|
95
|
+
return (
|
|
96
|
+
typeof opts === 'object' &&
|
|
97
|
+
opts !== null &&
|
|
98
|
+
'type' in opts &&
|
|
99
|
+
(opts as { type?: unknown }).type !== undefined
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── internals ────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
function isIdle(x: unknown): x is null | undefined {
|
|
106
|
+
return x === null || x === undefined
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isNode(x: Node | PathLike): x is Node {
|
|
110
|
+
return typeof x === 'object' && x !== null && 'id' in x && 'class' in x && 'path' in x
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A parent ref anchored on the node id (stable across rename) or the raw path. */
|
|
114
|
+
function refOf(parent: Node | PathLike): PathLike {
|
|
115
|
+
return isNode(parent) ? `@${parent.id}` : parent
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function walkOptsOf(opts?: ChildrenOptions): WalkOpts | undefined {
|
|
119
|
+
if (opts === undefined) return undefined
|
|
120
|
+
const { classes, order, limit } = opts
|
|
121
|
+
if (classes === undefined && order === undefined && limit === undefined) return undefined
|
|
122
|
+
return {
|
|
123
|
+
...(classes !== undefined ? { classes: classes.map((c) => rawOf(c)) } : {}),
|
|
124
|
+
...(order !== undefined ? { order } : {}),
|
|
125
|
+
...(limit !== undefined ? { limit } : {}),
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function childrenShape(
|
|
130
|
+
parent: Node | PathLike,
|
|
131
|
+
opts?: ChildrenOptions,
|
|
132
|
+
): (q: QuerySource) => GatherBuilder {
|
|
133
|
+
const ref = refOf(parent)
|
|
134
|
+
const walk = walkOptsOf(opts)
|
|
135
|
+
return (q) => q.from(ref).children(walk)
|
|
136
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useGraph (API §5) — the memory handle for this subtree (the raw floor plus the
|
|
3
|
+
* memory's optimistic-auto write shorthands). The same scoped memory every
|
|
4
|
+
* graph/schema/nav hook reads.
|
|
5
|
+
*/
|
|
6
|
+
import type { GraphMemory } from '@astrale-os/kernel-client/store'
|
|
7
|
+
|
|
8
|
+
import { useMemory } from '../memory/scope'
|
|
9
|
+
|
|
10
|
+
/** The memory for this subtree — the raw floor plus optimistic-auto shorthands (§5). */
|
|
11
|
+
export function useGraph(): GraphMemory {
|
|
12
|
+
return useMemory()
|
|
13
|
+
}
|