esoul-sdk 0.22.0 → 0.23.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/CHANGELOG.md +24 -0
- package/api-reference.md +22 -5
- package/dist/react.d.ts +9 -0
- package/dist/react.js +9 -0
- package/dist/server.d.ts +13 -4
- package/dist/server.js +7 -2
- package/dist/types.d.ts +9 -0
- package/docs/03-events-and-state.md +14 -7
- package/docs/05-ui.md +20 -0
- package/llms-full.txt +34 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
Releases before 0.20.0 are recorded in the repository history only.
|
|
4
4
|
|
|
5
|
+
## 0.23.0
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `useAppNav(nodeId, onTarget)` (esoul-sdk/react): the platform opens a place INSIDE your app —
|
|
10
|
+
the tasks pane lands on a conversation or a campaign, not just the app (docs/05-ui.md, "Opened
|
|
11
|
+
from outside").
|
|
12
|
+
- `origin: "server"` on an event definition: only the app's own server code (ops, routes, tasks,
|
|
13
|
+
webhooks) may append it; the browser's append and offline-queue doors and the PAT / SDK / MCP
|
|
14
|
+
dispatch routes refuse it (docs/03-events-and-state.md, "Events only the server emits").
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `readAppState(nodeId)` reads only for the call the platform is running: your app and its own
|
|
19
|
+
type in your workspace, another type only with a `workspaceTools` grant for it (refused with the
|
|
20
|
+
line to add), null for an app in another workspace, own type only from a webhook, and a throw
|
|
21
|
+
outside an op, route, task or webhook. `callWorkspaceTool` refuses a `pluginId`/`nodeId` that is
|
|
22
|
+
not the running call's own. The Forge box applies the same read rule.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `pluginServer.fileProducers` takes a producer with its own key type: `PluginFileProducer<MyKey>`
|
|
27
|
+
with `MyKey` declared as an `interface` no longer fails to typecheck against the map.
|
|
28
|
+
|
|
5
29
|
## 0.22.0
|
|
6
30
|
|
|
7
31
|
### Added
|
package/api-reference.md
CHANGED
|
@@ -1723,6 +1723,7 @@ interface EventDefinition<StateType extends ApplicationIdentifier> {
|
|
|
1723
1723
|
triggerMeta?: TriggerMeta;
|
|
1724
1724
|
sideEffect?: EventSideEffect;
|
|
1725
1725
|
permission?: "user_exclusive";
|
|
1726
|
+
origin?: "server";
|
|
1726
1727
|
conflictPolicy?: "mark" | "silent";
|
|
1727
1728
|
conflictScope?: (eventData: any) => string[] | null | undefined;
|
|
1728
1729
|
merge?: MergeDescription;
|
|
@@ -2633,7 +2634,7 @@ function pluginFiles(_ctx: PluginFilesCtx): Promise<FilesApi>
|
|
|
2633
2634
|
|
|
2634
2635
|
#### `readAppState` — function · src/server.ts
|
|
2635
2636
|
|
|
2636
|
-
Read one app's state folded to head, by nodeId — server truth for an op or a
|
|
2637
|
+
Read one app's state folded to head, by nodeId — server truth for an op, route, task or webhook. It reads for the app instance of the call the platform is running: your own app and other instances of its type in your workspace; another app type only when plugin.json `workspaceTools` grants a tool of that type (the refusal names the line to add). Null when no such app exists — an app in another workspace is null too. A webhook (it runs for no instance) finds only instances of its own type. Outside a running call it throws. HOST-ONLY.
|
|
2637
2638
|
|
|
2638
2639
|
```ts
|
|
2639
2640
|
function readAppState( _nodeId: string, ): Promise<{ nodeId: string; workspaceId: string; applicationType: string; foldedSeq: number; state: Record<string, unknown> } | null>
|
|
@@ -3387,7 +3388,7 @@ interface PluginServerModule {
|
|
|
3387
3388
|
webhooks?: Record<string, PluginWebhookHandler>;
|
|
3388
3389
|
ops?: Record<string, PluginOpHandler>;
|
|
3389
3390
|
routes?: Record<string, PluginRouteHandler>;
|
|
3390
|
-
fileProducers?: Record<string, PluginFileProducer
|
|
3391
|
+
fileProducers?: Record<string, PluginFileProducer<any>>;
|
|
3391
3392
|
}
|
|
3392
3393
|
```
|
|
3393
3394
|
|
|
@@ -3689,11 +3690,11 @@ type WorkspaceGrant = { scope: "folder"; root: string } | { scope: "computer" };
|
|
|
3689
3690
|
```
|
|
3690
3691
|
|
|
3691
3692
|
==============================================================================
|
|
3692
|
-
## `esoul-sdk/react` —
|
|
3693
|
+
## `esoul-sdk/react` — 71 exports
|
|
3693
3694
|
|
|
3694
3695
|
The app's UI: hooks for the viewer, the app's state, realtime, workspace files and tools.
|
|
3695
3696
|
|
|
3696
|
-
### Functions and values (
|
|
3697
|
+
### Functions and values (37)
|
|
3697
3698
|
|
|
3698
3699
|
#### `ConnectAccount` — function · src/react.ts
|
|
3699
3700
|
|
|
@@ -3815,6 +3816,14 @@ True when the current viewer may mutate this workspace.
|
|
|
3815
3816
|
function useAppCanEdit(): boolean
|
|
3816
3817
|
```
|
|
3817
3818
|
|
|
3819
|
+
#### `useAppNav` — function · src/react.ts
|
|
3820
|
+
|
|
3821
|
+
"Open THIS inside the app": the platform's tasks pane, a link chip or a recall hit asks for a place inside this app instance (the keys are the app's own — say which in your tool and task descriptions). `onTarget` runs with the target waiting when the app mounts, and with each one asked for while it is on screen. Open it and select it; an unknown key does nothing.
|
|
3822
|
+
|
|
3823
|
+
```ts
|
|
3824
|
+
function useAppNav(_nodeId: string, _onTarget: (target: AppNavTarget) => void): void
|
|
3825
|
+
```
|
|
3826
|
+
|
|
3818
3827
|
#### `useCredential` — function · src/react.ts
|
|
3819
3828
|
|
|
3820
3829
|
The state of one of your plugin.json `credentials` slots for the person looking at the app.
|
|
@@ -3983,7 +3992,15 @@ The account's computers as a panel: online, the apps each serves, Disconnect.
|
|
|
3983
3992
|
function YourComputers(_props: { className?: string }): any
|
|
3984
3993
|
```
|
|
3985
3994
|
|
|
3986
|
-
### Types (
|
|
3995
|
+
### Types (34)
|
|
3996
|
+
|
|
3997
|
+
#### `AppNavTarget` — type · src/react.ts
|
|
3998
|
+
|
|
3999
|
+
A place inside an app, as named by whoever asks to open it: `{ thread: "…" }`, `{ campaign: "…" }`.
|
|
4000
|
+
|
|
4001
|
+
```ts
|
|
4002
|
+
type AppNavTarget = Record<string, string>;
|
|
4003
|
+
```
|
|
3987
4004
|
|
|
3988
4005
|
#### `DeviceRunStarted` — interface · src/device.ts
|
|
3989
4006
|
|
package/dist/react.d.ts
CHANGED
|
@@ -67,6 +67,15 @@ export interface WorkspaceNav {
|
|
|
67
67
|
* already on.
|
|
68
68
|
*/
|
|
69
69
|
export declare function useWorkspaceNav(): WorkspaceNav;
|
|
70
|
+
/** A place inside an app, as named by whoever asks to open it: `{ thread: "…" }`, `{ campaign: "…" }`. */
|
|
71
|
+
export type AppNavTarget = Record<string, string>;
|
|
72
|
+
/**
|
|
73
|
+
* "Open THIS inside the app": the platform's tasks pane, a link chip or a recall hit asks for a
|
|
74
|
+
* place inside this app instance (the keys are the app's own — say which in your tool and task
|
|
75
|
+
* descriptions). `onTarget` runs with the target waiting when the app mounts, and with each one
|
|
76
|
+
* asked for while it is on screen. Open it and select it; an unknown key does nothing.
|
|
77
|
+
*/
|
|
78
|
+
export declare function useAppNav(_nodeId: string, _onTarget: (target: AppNavTarget) => void): void;
|
|
70
79
|
import type { FileEntry, FileListOptions, FileRef, FileSource } from "./files.js";
|
|
71
80
|
import type { TransferSnapshot } from "./transfers.js";
|
|
72
81
|
import type { LabelPoint, LabelShape } from "./labelme.js";
|
package/dist/react.js
CHANGED
|
@@ -33,6 +33,15 @@ export function useWorkspaceTools(_identity) {
|
|
|
33
33
|
export function useWorkspaceNav() {
|
|
34
34
|
return hostOnly("useWorkspaceNav");
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* "Open THIS inside the app": the platform's tasks pane, a link chip or a recall hit asks for a
|
|
38
|
+
* place inside this app instance (the keys are the app's own — say which in your tool and task
|
|
39
|
+
* descriptions). `onTarget` runs with the target waiting when the app mounts, and with each one
|
|
40
|
+
* asked for while it is on screen. Open it and select it; an unknown key does nothing.
|
|
41
|
+
*/
|
|
42
|
+
export function useAppNav(_nodeId, _onTarget) {
|
|
43
|
+
return hostOnly("useAppNav");
|
|
44
|
+
}
|
|
36
45
|
/** A files call from the UI refused — `kind` is the FileSourceErrorKind when the source said why. */
|
|
37
46
|
export class FilesBrowseError extends Error {
|
|
38
47
|
kind;
|
package/dist/server.d.ts
CHANGED
|
@@ -208,8 +208,12 @@ export interface PluginServerModule {
|
|
|
208
208
|
ops?: Record<string, PluginOpHandler>;
|
|
209
209
|
/** routeName → handler, for names declared in plugin.json `routes`. */
|
|
210
210
|
routes?: Record<string, PluginRouteHandler>;
|
|
211
|
-
/**
|
|
212
|
-
|
|
211
|
+
/**
|
|
212
|
+
* key → producer, for keys declared in plugin.json `fileProducers`: the app's own bytes as a
|
|
213
|
+
* transfer source. Each producer keeps its own key type (`PluginFileProducer<MyKey>`, an
|
|
214
|
+
* interface or a type) — the platform hands `open` the key the transfer item named.
|
|
215
|
+
*/
|
|
216
|
+
fileProducers?: Record<string, PluginFileProducer<any>>;
|
|
213
217
|
}
|
|
214
218
|
export type PluginConnectionCredentials = {
|
|
215
219
|
kind: "oauth2";
|
|
@@ -578,8 +582,13 @@ export interface EmitPluginAppEventArgs {
|
|
|
578
582
|
eventData: Record<string, unknown>;
|
|
579
583
|
}
|
|
580
584
|
/**
|
|
581
|
-
* Read one app's state folded to head, by nodeId — server truth for an op
|
|
582
|
-
*
|
|
585
|
+
* Read one app's state folded to head, by nodeId — server truth for an op, route,
|
|
586
|
+
* task or webhook. It reads for the app instance of the call the platform is
|
|
587
|
+
* running: your own app and other instances of its type in your workspace; another
|
|
588
|
+
* app type only when plugin.json `workspaceTools` grants a tool of that type (the
|
|
589
|
+
* refusal names the line to add). Null when no such app exists — an app in another
|
|
590
|
+
* workspace is null too. A webhook (it runs for no instance) finds only instances
|
|
591
|
+
* of its own type. Outside a running call it throws. HOST-ONLY.
|
|
583
592
|
*/
|
|
584
593
|
export declare function readAppState(_nodeId: string): Promise<{
|
|
585
594
|
nodeId: string;
|
package/dist/server.js
CHANGED
|
@@ -202,8 +202,13 @@ export function removeAppRole(_ctx, _name) {
|
|
|
202
202
|
return hostOnly("removeAppRole");
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
|
-
* Read one app's state folded to head, by nodeId — server truth for an op
|
|
206
|
-
*
|
|
205
|
+
* Read one app's state folded to head, by nodeId — server truth for an op, route,
|
|
206
|
+
* task or webhook. It reads for the app instance of the call the platform is
|
|
207
|
+
* running: your own app and other instances of its type in your workspace; another
|
|
208
|
+
* app type only when plugin.json `workspaceTools` grants a tool of that type (the
|
|
209
|
+
* refusal names the line to add). Null when no such app exists — an app in another
|
|
210
|
+
* workspace is null too. A webhook (it runs for no instance) finds only instances
|
|
211
|
+
* of its own type. Outside a running call it throws. HOST-ONLY.
|
|
207
212
|
*/
|
|
208
213
|
export function readAppState(_nodeId) {
|
|
209
214
|
return hostOnly("readAppState");
|
package/dist/types.d.ts
CHANGED
|
@@ -106,6 +106,15 @@ export interface EventDefinition<StateType extends ApplicationIdentifier> {
|
|
|
106
106
|
triggerMeta?: TriggerMeta;
|
|
107
107
|
sideEffect?: EventSideEffect;
|
|
108
108
|
permission?: "user_exclusive";
|
|
109
|
+
/**
|
|
110
|
+
* WHO MAY APPEND IT. `"server"`: this event records something only the app's own server code
|
|
111
|
+
* knows — a message sent, a sync's result, a model's output, a payment. It is appended by the
|
|
112
|
+
* app's ops, routes, tasks and webhooks (and the platform), never by a client: the browser's
|
|
113
|
+
* append and WAL doors and the PAT/SDK dispatch routes refuse it, so nobody can plant a "sent"
|
|
114
|
+
* that was never sent or a sync that never ran (the platform's mail requirements, KD-120). Absent: any writer
|
|
115
|
+
* with access to the app may append it, as before.
|
|
116
|
+
*/
|
|
117
|
+
origin?: "server";
|
|
109
118
|
conflictPolicy?: "mark" | "silent";
|
|
110
119
|
/** The sub-items (e.g. block ids) a partial write touches — two devices
|
|
111
120
|
* writing different sub-items of one item merge without a conflict. */
|
|
@@ -88,13 +88,20 @@ snapshot baselines and the durability layers. Every shipped plugin and the scaff
|
|
|
88
88
|
|
|
89
89
|
## Events only the server emits
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
An event that records something only your server code knows — a letter sent, a sync's result, a
|
|
92
|
+
model's answer, a payment — says so with `origin: "server"`:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
{ eventName: "mail_sent", type: EventTypes.Client, origin: "server", dataCreator, processor }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Your ops, routes, tasks and webhooks append it as usual (`ctx.emit`, a task's `dispatchEvent`).
|
|
99
|
+
Every door a CLIENT's event enters by refuses it — the browser's appends and offline queue, and the
|
|
100
|
+
PAT / SDK / MCP dispatch routes — so nobody with write access can plant a "sent" that was never
|
|
101
|
+
sent or a sync that never ran. Your UI can still predict it for an optimistic click (run the
|
|
102
|
+
processor locally); it just never dispatches it. `EventTypes` has no server type for this
|
|
103
|
+
(`Client`, `Workflow`, `Workspace` only — an earlier version of this page named `EventTypes.Server`,
|
|
104
|
+
which does not exist); `origin` is the switch. The processor still obeys the three rules.
|
|
98
105
|
|
|
99
106
|
## Durability you get for free
|
|
100
107
|
|
package/docs/05-ui.md
CHANGED
|
@@ -165,6 +165,26 @@ Reload in place, without a skeleton: a screen that flashes every time the assist
|
|
|
165
165
|
reads as broken. Check it in the workbench: open the preview, call one of your write tools from the
|
|
166
166
|
board (or `esoul-forge tool …`), and watch the view change within a few seconds, without a reload.
|
|
167
167
|
|
|
168
|
+
## Opened from outside: a place inside your app
|
|
169
|
+
|
|
170
|
+
The platform's tasks pane (and, as they adopt it, link chips and memory hits) can open a place
|
|
171
|
+
INSIDE your app — a conversation, a campaign, a page — not just the app. It names the place with
|
|
172
|
+
keys you choose; you hear it with `useAppNav`:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { useAppNav } from "esoul-sdk/react";
|
|
176
|
+
|
|
177
|
+
useAppNav(nodeId, (target) => {
|
|
178
|
+
if (target.thread) openThread(target.thread);
|
|
179
|
+
else if (target.campaign) openCampaign(target.campaign);
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`onTarget` runs with the place waiting when the app mounts (declare it after any effect that
|
|
184
|
+
restores a remembered view, so the request wins) and with each one asked for while the app is on
|
|
185
|
+
screen. An unknown key does nothing. Name the keys in your task descriptions so the platform can
|
|
186
|
+
point at them.
|
|
187
|
+
|
|
168
188
|
## Cross-app from the UI
|
|
169
189
|
|
|
170
190
|
```ts
|
package/llms-full.txt
CHANGED
|
@@ -880,13 +880,20 @@ snapshot baselines and the durability layers. Every shipped plugin and the scaff
|
|
|
880
880
|
|
|
881
881
|
## Events only the server emits
|
|
882
882
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
883
|
+
An event that records something only your server code knows — a letter sent, a sync's result, a
|
|
884
|
+
model's answer, a payment — says so with `origin: "server"`:
|
|
885
|
+
|
|
886
|
+
```ts
|
|
887
|
+
{ eventName: "mail_sent", type: EventTypes.Client, origin: "server", dataCreator, processor }
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
Your ops, routes, tasks and webhooks append it as usual (`ctx.emit`, a task's `dispatchEvent`).
|
|
891
|
+
Every door a CLIENT's event enters by refuses it — the browser's appends and offline queue, and the
|
|
892
|
+
PAT / SDK / MCP dispatch routes — so nobody with write access can plant a "sent" that was never
|
|
893
|
+
sent or a sync that never ran. Your UI can still predict it for an optimistic click (run the
|
|
894
|
+
processor locally); it just never dispatches it. `EventTypes` has no server type for this
|
|
895
|
+
(`Client`, `Workflow`, `Workspace` only — an earlier version of this page named `EventTypes.Server`,
|
|
896
|
+
which does not exist); `origin` is the switch. The processor still obeys the three rules.
|
|
890
897
|
|
|
891
898
|
## Durability you get for free
|
|
892
899
|
|
|
@@ -1190,6 +1197,26 @@ Reload in place, without a skeleton: a screen that flashes every time the assist
|
|
|
1190
1197
|
reads as broken. Check it in the workbench: open the preview, call one of your write tools from the
|
|
1191
1198
|
board (or `esoul-forge tool …`), and watch the view change within a few seconds, without a reload.
|
|
1192
1199
|
|
|
1200
|
+
## Opened from outside: a place inside your app
|
|
1201
|
+
|
|
1202
|
+
The platform's tasks pane (and, as they adopt it, link chips and memory hits) can open a place
|
|
1203
|
+
INSIDE your app — a conversation, a campaign, a page — not just the app. It names the place with
|
|
1204
|
+
keys you choose; you hear it with `useAppNav`:
|
|
1205
|
+
|
|
1206
|
+
```tsx
|
|
1207
|
+
import { useAppNav } from "esoul-sdk/react";
|
|
1208
|
+
|
|
1209
|
+
useAppNav(nodeId, (target) => {
|
|
1210
|
+
if (target.thread) openThread(target.thread);
|
|
1211
|
+
else if (target.campaign) openCampaign(target.campaign);
|
|
1212
|
+
});
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
`onTarget` runs with the place waiting when the app mounts (declare it after any effect that
|
|
1216
|
+
restores a remembered view, so the request wins) and with each one asked for while the app is on
|
|
1217
|
+
screen. An unknown key does nothing. Name the keys in your task descriptions so the platform can
|
|
1218
|
+
point at them.
|
|
1219
|
+
|
|
1193
1220
|
## Cross-app from the UI
|
|
1194
1221
|
|
|
1195
1222
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esoul-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Build a full product on ExternalSoul: your own tables with per-person rules, a viewer on every seam, app roles, access levels, realtime with audiences, durable tasks, and bindings to other apps.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|