App Graph
import * as AppGraph from '@dxos/app-graph/AppGraph';import * as AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';import * as AppGraphNode from '@dxos/app-graph/AppGraphNode';import * as AppNode from '@dxos/app-toolkit/AppNode';App Graph is a graph of everything a person can open or do in a PLEX app. In Composer, the navigation tree, toolbars, context menus and the command palette all read it, so a plugin that adds to App Graph shows up in all of them.
App Graph comes from the App Toolkit, not the framework: it is how a workspace app such as Composer organizes what plugins add. A PLEX app can run without it.
How App Graph is shaped
Section titled “How App Graph is shaped”A node has an id, a type, data and properties. Its data is usually the thing it stands for, such as a space or an ECHO object; its properties carry the label, icon and display hints. The graph starts from a root node.
Nodes connect by relations. child makes the navigation tree: a space’s children are its sections and objects.
action connects a node to the things a person can do to it; an action node’s data is a function that returns an
Effect. Other relations, such as companion, attach panels to a node.
A node’s identity is its path: the ids from the root down to it, joined like a URL. GraphPath from
@dxos/app-toolkit builds the paths of the common nodes, such as a space or an object in it, and navigation
operations take paths. In Composer the address bar shows the open node’s path.
Plugins add nodes through builder extensions. An extension has a matcher and a connector: the matcher picks the
nodes it applies to, and the connector returns the nodes to connect to them. For the usual section that lists an
object type, TypeSection.createTypeSectionExtension builds the extensions for you, with an address for each object.
Stash uses it:
import * as import AppNodeMatcher
AppNodeMatcher from '@dxos/app-toolkit/AppNodeMatcher';import * as import GraphPath
GraphPath from '@dxos/app-toolkit/GraphPath';import * as import TypeSection
TypeSection from '@dxos/app-toolkit/TypeSection';4 collapsed lines
import * as import Operation
Operation from '@dxos/compute/Operation';import { import Obj
Obj, import Ref
Ref } from '@dxos/echo';
import { const StashNamespace: { key: "com.example.plugin.stash"; make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>; makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;}
StashNamespace } from '../namespace.ts';import { class StashedLink
StashedLink } from '../types/StashedLink.ts';2 collapsed lines
import { const MarkRead: Operation.Definition<{ readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, void, Service>
MarkRead } from '../types/StashedLinkOperation.ts';
/** A "Stashed links" section in each space's Content group, once it has one. */const const section: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
A "Stashed links" section in each space's Content group, once it has one.
section = import TypeSection
TypeSection.const createTypeSectionExtension: <typeof StashedLink>(type: typeof StashedLink, options: { position?: Position; match?: (node: Node) => Option<Space>; groupSegment?: string; createObject?: (space: Space) => Effect.Effect<any, any, any>; urlKey: string; sectionUrlKey?: string; dropInto?: ((object: StashedLink & OfKind<EntityKind.Object>) => Container) | undefined; sort?: ((a: StashedLink & OfKind<EntityKind.Object>, b: StashedLink & OfKind<EntityKind.Object>) => number) | undefined; showEmpty?: boolean;}) => Effect.Effect<...>
createTypeSectionExtension(class StashedLink
StashedLink, { match?: ((node: Node) => Option<Space>) | undefined
Override the default
AppNodeMatcher.whenSpace
match function.
Use when the section should live under a group node rather than directly under a space.
The match must still return Option<Space> so the connector can query the space db.
match: import AppNodeMatcher
AppNodeMatcher.const whenNavTreeGroup: (groupType: string) => (node: Node) => Option<Space>
Match navtree section-group nodes of a specific type and return the
Space
stored in node.properties.space. Group nodes carry the space in properties
(not node.data) so child connectors can access it without an extra graph lookup.
whenNavTreeGroup(import GraphPath
GraphPath.const GroupTypes: { readonly ai: "org.dxos.navtree.group.ai"; readonly content: "org.dxos.navtree.group.content"; readonly communications: "org.dxos.navtree.group.communications"; readonly crm: "org.dxos.navtree.group.crm"; readonly system: "org.dxos.navtree.group.system"; readonly debug: "org.dxos.navtree.group.debug";}
Navtree section-group node type values. Co-located with
GroupSegments
so plugins can
match (via AppNodeMatcher.whenNavTreeGroup) and create group nodes without depending on
plugin-navtree.
GroupTypes.content: "org.dxos.navtree.group.content"
content), groupSegment?: string | undefined
Group segment the section nests under (e.g. GraphPath.GroupSegments.ai), when match places it
beneath a navtree group rather than directly under the space. Included in the forward-resolution
urlPath so section objects at root/<space>/<groupSegment>/<typename>/<id> resolve
deterministically. Omit for a space-direct section (root/<space>/<typename>/<id>).
groupSegment: import GraphPath
GraphPath.const GroupSegments: { readonly ai: "ai"; readonly content: "content"; readonly communications: "communications"; readonly crm: "crm"; readonly system: "system"; readonly debug: "debug";}
Navtree section-group node id values, which double as path segments: a section (collections,
database, etc.) is nested under its group node in the graph, so its canonical path includes the
group segment. Kept in app-toolkit so any plugin can reference them without depending on
plugin-navtree.
GroupSegments.content: "content"
content, // The link's address in Composer's URL: `/w/<space>/stash/<id>`. urlKey: string
Registered URL prefix key for this section's connector (e.g. doc, mail). Keys are global, so
it is declared here rather than derived — see @dxos/app-graph's path-resolution.ts for how
registered keys resolve and serialize URLs.
urlKey: 'stash',});
Stash’s App Graph module in the inspector shows the branches that lead to what it added:
How it behaves
Section titled “How it behaves”- App Graph expands lazily. The framework runs a node’s extensions only when something asks for that node’s connections, such as the person opening a branch in the tree. A space with ten thousand objects costs nothing until it is opened.
- A connector reads reactive state through
get. When that state changes, such as a query gaining a result, the connector reruns and the node’s connections update. - Nodes nothing is showing can be dropped again. A plugin that must keep a node loaded while it is off screen, such as
the one a panel is pinned to, contributes it through
AppCapabilities.AppGraphRetention. - Actions reach every menu from one place. An action node carries a disposition that says where it appears:
toolbarfor an object’s toolbar,list-itemfor its context menu in the tree. The command palette lists every action of the open node, so an action needs no command of its own.
import * as import Effect
Effect from 'effect/Effect';2 collapsed lines
import * as import Capability
Capability from '@dxos/app-framework/Capability';import * as import AppGraphBuilder
AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';4 collapsed lines
import * as import AppCapabilities
AppCapabilities from '@dxos/app-toolkit/AppCapabilities';import * as import AppNodeMatcher
AppNodeMatcher from '@dxos/app-toolkit/AppNodeMatcher';import * as import GraphPath
GraphPath from '@dxos/app-toolkit/GraphPath';import * as import TypeSection
TypeSection from '@dxos/app-toolkit/TypeSection';import * as import Operation
Operation from '@dxos/compute/Operation';import { import Obj
Obj, import Ref
Ref } from '@dxos/echo';
import { const StashNamespace: { key: "com.example.plugin.stash"; make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>; makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;}
StashNamespace } from '../namespace.ts';import { class StashedLink
StashedLink } from '../types/StashedLink.ts';import { const MarkRead: Operation.Definition<{ readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, void, Service>
MarkRead } from '../types/StashedLinkOperation.ts';9 collapsed lines
/** A "Stashed links" section in each space's Content group, once it has one. */const const section: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
A "Stashed links" section in each space's Content group, once it has one.
section = import TypeSection
TypeSection.const createTypeSectionExtension: <typeof StashedLink>(type: typeof StashedLink, options: { position?: Position; match?: (node: Node) => Option<Space>; groupSegment?: string; createObject?: (space: Space) => Effect.Effect<any, any, any>; urlKey: string; sectionUrlKey?: string; dropInto?: ((object: StashedLink & OfKind<EntityKind.Object>) => Container) | undefined; sort?: ((a: StashedLink & OfKind<EntityKind.Object>, b: StashedLink & OfKind<EntityKind.Object>) => number) | undefined; showEmpty?: boolean;}) => Effect.Effect<...>
createTypeSectionExtension(class StashedLink
StashedLink, { match?: ((node: Node) => Option<Space>) | undefined
Override the default
AppNodeMatcher.whenSpace
match function.
Use when the section should live under a group node rather than directly under a space.
The match must still return Option<Space> so the connector can query the space db.
match: import AppNodeMatcher
AppNodeMatcher.const whenNavTreeGroup: (groupType: string) => (node: Node) => Option<Space>
Match navtree section-group nodes of a specific type and return the
Space
stored in node.properties.space. Group nodes carry the space in properties
(not node.data) so child connectors can access it without an extra graph lookup.
whenNavTreeGroup(import GraphPath
GraphPath.const GroupTypes: { readonly ai: "org.dxos.navtree.group.ai"; readonly content: "org.dxos.navtree.group.content"; readonly communications: "org.dxos.navtree.group.communications"; readonly crm: "org.dxos.navtree.group.crm"; readonly system: "org.dxos.navtree.group.system"; readonly debug: "org.dxos.navtree.group.debug";}
Navtree section-group node type values. Co-located with
GroupSegments
so plugins can
match (via AppNodeMatcher.whenNavTreeGroup) and create group nodes without depending on
plugin-navtree.
GroupTypes.content: "org.dxos.navtree.group.content"
content), groupSegment?: string | undefined
Group segment the section nests under (e.g. GraphPath.GroupSegments.ai), when match places it
beneath a navtree group rather than directly under the space. Included in the forward-resolution
urlPath so section objects at root/<space>/<groupSegment>/<typename>/<id> resolve
deterministically. Omit for a space-direct section (root/<space>/<typename>/<id>).
groupSegment: import GraphPath
GraphPath.const GroupSegments: { readonly ai: "ai"; readonly content: "content"; readonly communications: "communications"; readonly crm: "crm"; readonly system: "system"; readonly debug: "debug";}
Navtree section-group node id values, which double as path segments: a section (collections,
database, etc.) is nested under its group node in the graph, so its canonical path includes the
group segment. Kept in app-toolkit so any plugin can reference them without depending on
plugin-navtree.
GroupSegments.content: "content"
content, // The link's address in Composer's URL: `/w/<space>/stash/<id>`. urlKey: string
Registered URL prefix key for this section's connector (e.g. doc, mail). Keys are global, so
it is declared here rather than derived — see @dxos/app-graph's path-resolution.ts for how
registered keys resolve and serialize URLs.
urlKey: 'stash',});
/** An action on every link, on its toolbar and its menu in the tree. */const const stashedLinkActions: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
An action on every link, on its toolbar and its menu in the tree.
stashedLinkActions = import AppGraphBuilder
AppGraphBuilder.const createTypeExtension: <typeof StashedLink, never, "stashedLinkActions">(options: AppGraphBuilder.CreateTypeExtensionOptions<typeof StashedLink, never, "stashedLinkActions">) => Effect.Effect<Readonly<{ id: string; position?: Position; relation?: RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
Create an extension that matches nodes by schema type.
The entity type is inferred from the schema type and works for both object and relation schemas.
Returns an Effect to allow callbacks to access services via dependency injection.
createTypeExtension({ id: "stashedLinkActions"
id: 'stashedLinkActions', type: typeof StashedLink
type: class StashedLink
StashedLink, actions?: ((object: StashedLink & OfKind<EntityKind.Object>, get: AtomContext) => Effect.Effect<Omit<NodeArg<ActionData<any>, Record<string, any>>, "type">[], never, never>) | undefined
actions: (link: StashedLink & OfKind<EntityKind.Object>
link) => import Effect
Effect.const succeed: <{ id: string; data: () => Effect.Effect<void, Operation.NoHandlerError, Operation.Service>; properties: { label: (string | { ns: "com.example.plugin.stash"; })[]; icon: string; disposition: string[]; };}[]>(value: { id: string; data: () => Effect.Effect<void, Operation.NoHandlerError, Operation.Service>; properties: { label: (string | { ns: "com.example.plugin.stash"; })[]; icon: string; disposition: string[]; };}[]) => Effect.Effect<{ id: string; data: () => Effect.Effect<void, Operation.NoHandlerError, Operation.Service>; properties: { label: (string | { ns: "com.example.plugin.stash"; })[]; icon: string; disposition: string[]; };}[], never, never>
Creates an Effect that always succeeds with a given value.
When to use
Use when an effect should complete successfully with a specific value without any errors
or external dependencies.
Example (Creating a successful effect)
import { Effect } from "effect"
// Creating an effect that represents a successful scenario//// ┌─── Effect<number, never, never>// ▼const success = Effect.succeed(42)Effect.runSync(success) // => 42
succeed([ { id: string
id: 'markRead', data: () => Effect.Effect<void, Operation.NoHandlerError, Operation.Service>
data: () => import Operation
Operation.const invoke: <{ readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, void>(op: Operation.Definition<{ readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, void, any>, input: { readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, options?: Operation.InvokeOptions | undefined) => Effect.Effect<void, Operation.NoHandlerError, Operation.Service>
Invoke an operation as an Effect.
Yields the Operation.Service internally.
invoke( const MarkRead: Operation.Definition<{ readonly link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>;}, void, Service>
MarkRead, { link: Ref<StashedLink & OfKind<EntityKind.Object> & Obj.Unknown>
link: import Ref
Ref.const make: <StashedLink & OfKind<EntityKind.Object>>(object: StashedLink & OfKind<EntityKind.Object>) => Ref<StashedLink & OfKind<EntityKind.Object>>
Create a reference to an entity.
make(link: StashedLink & OfKind<EntityKind.Object>
link) }, { InvokeOptions.spaceId?: SpaceId | undefined
Space ID to provide database context for the handler.
spaceId: import Obj
Obj.const getDatabase: (entity: Unknown | Snapshot) => Database | undefined
Get the database the object belongs to.
Accepts both reactive objects and snapshots.
getDatabase(link: StashedLink & OfKind<EntityKind.Object>
link)?.Database.spaceId: SpaceId | undefined
spaceId }, ), properties: { label: (string | { ns: "com.example.plugin.stash"; })[]; icon: string; disposition: string[];}
properties: { label: (string | { ns: "com.example.plugin.stash";})[]
label: ['mark-read.label', { ns: "com.example.plugin.stash"
ns: const StashNamespace: { key: "com.example.plugin.stash"; make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>; makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;}
StashNamespace.key: "com.example.plugin.stash"
key }], icon: string
icon: 'ph--check-circle--regular', disposition: string[]
disposition: ['toolbar', 'list-item'], }, }, ]),});
Write a graph builder extension
Section titled “Write a graph builder extension”AppGraphBuilder.createExtension takes an id, a matcher and a connector. The matcher returns Option.some(value) for
the nodes it applies to; AppNodeMatcher has ready-made ones such as whenSpace. The connector returns the nodes to
connect. Read reactive state with get so the connector reruns when it changes.
import * as import Effect
Effect from 'effect/Effect';import * as import AppGraphBuilder
AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';import * as import AppNode
AppNode from '@dxos/app-toolkit/AppNode';import * as import AppNodeMatcher
AppNodeMatcher from '@dxos/app-toolkit/AppNodeMatcher';import { import Filter
Filter } from '@dxos/echo';
// One node per note, as a child of each space node.const const notes: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
notes = import AppGraphBuilder
AppGraphBuilder.const createExtension: <Space, never, "notes">(options: AppGraphBuilder.CreateExtensionOptions<Space, never, "notes">) => Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
Create a graph builder extension with simplified API.
Returns an Effect to allow callbacks to access services via dependency injection.
createExtension({ id: "notes"
id: 'notes', match: (node: AppGraphNode.Node, get: AtomContext) => Option<Space>
match: import AppNodeMatcher
AppNodeMatcher.const whenSpace: (node: AppGraphNode.Node) => Option<Space>
Match space nodes and return the
Space
payload — saves callers from
unwrapping node.data themselves and adds an isSpace runtime guard.
whenSpace, connector?: ((matched: Space, get: AtomContext) => Effect.Effect<AppGraphNode.NodeArg<any, any>[], never, never>) | undefined
connector: (space: Space
space, get: AtomContext
get) => { const const objects: (Note & OfKind<EntityKind.Object>)[]
objects = get: AtomContext<(Note & OfKind<EntityKind.Object>)[]>(atom: Atom<(Note & OfKind<EntityKind.Object>)[]>) => (Note & OfKind<EntityKind.Object>)[]
get(space: Space
space.Space.db: EchoDatabase
Echo database.
db.Database.query: QueryFn<Filter.Filter<Note & OfKind<EntityKind.Object>>>(filter: Filter.Filter<Note & OfKind<EntityKind.Object>>) => QueryResult<Note & OfKind<EntityKind.Object>> (+1 overload)
query(import Filter
Filter.const type: <typeof Note>(type: typeof Note, props?: Filter.Props<Note & OfKind<EntityKind.Object>> | undefined) => Filter.Filter<Note & OfKind<EntityKind.Object>> (+4 overloads)
type(class Note
Note)).QueryResult<Note & OfKind<EntityKind.Object>>.atom: Atom<(Note & OfKind<EntityKind.Object>)[]>
Self-updating atom. Updates automatically when query results change.
Memoized per QueryResult instance — repeated accesses on the same instance return the same
Atom. Safe only when the QueryResult is itself held stable across re-renders (e.g. behind a
useMemo). It must NOT be used in graph-builder connectors/actions or other atom computes,
where db.query(...) is called fresh on each re-evaluation: every run constructs a new
QueryResult and so a new atom + subscription, leaking the previous ones. Use the memoized
atom
family there instead.
atom); return import Effect
Effect.const succeed: <{ id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };}[]>(value: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from ...
Creates an Effect that always succeeds with a given value.
When to use
Use when an effect should complete successfully with a specific value without any errors
or external dependencies.
Example (Creating a successful effect)
import { Effect } from "effect"
// Creating an effect that represents a successful scenario//// ┌─── Effect<number, never, never>// ▼const success = Effect.succeed(42)Effect.runSync(success) // => 42
succeed( const objects: (Note & OfKind<EntityKind.Object>)[]
objects .Array<Note & OfKind<EntityKind.Object>>.map<{ id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null>(callbackfn: (value: Note & OfKind<...>, index: number, array: (Note & OfKind<...>)[]) => { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope ...
Calls a defined callback function on each element of an array, and returns an array that contains the results.
map((object: Note & OfKind<EntityKind.Object>
object) => import AppNode
AppNode.const makeObject: ({ get, db, object, disposition, draggable, droppable, navigable, dropInto, canDrop: canDropOverride, blockInstruction, }: { get: AtomContext; db: Database; object: Obj.Unknown; disposition?: string | string[]; draggable?: boolean; droppable?: boolean; navigable?: boolean; dropInto?: Container; canDrop?: (source: TreeData) => boolean; blockInstruction?: (source: TreeData, instruction: AppNode.Instruction) => boolean;}) => { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null
Builds an app-graph node for an ECHO object. Uses the local object ID as the graph node ID.
makeObject({ get: AtomContext
Atom context from the enclosing connector — registers reactive subscriptions so property changes re-run the connector.
get, db: Database
db: space: Space
space.Space.db: EchoDatabase
Echo database.
db, object: Obj.Unknown
object })) .Array<{ id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: Instruction) => boolean) | undefined; canDrop: typeof CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition ...<{ id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };}>(predicate: (value: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown ... (+1 overload)
Returns the elements of an array that meet the condition specified in a callback function.
filter((node: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null
node): node: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null
node is type NonNullable<T> = T & {}
Exclude null and undefined from T
NonNullable<typeof node: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null
node> => node: { id: string; type: string; cacheable: string[]; data: Obj.Unknown; properties: { role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { managesAutofocus?: boolean; role?: "branch" | undefined; acceptPersistenceClass?: Set<string> | undefined; acceptPersistenceKey?: Set<string> | undefined; moveScope?: string | undefined; isLink?: ((child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean) | undefined; onMoveIn?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; onLink?: ((child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void) | undefined; dropInto?: Container | undefined; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; canDrop: typeof AppNode.CAN_DROP_OBJECT | undefined; } | { role: "branch"; acceptPersistenceClass: Set<string>; acceptPersistenceKey: Set<string>; moveScope: string | undefined; canDrop: (source: TreeData) => boolean; isLink: (child: AppGraphNode.Node<Obj.Unknown>, from?: AppGraphNode.Node) => boolean; onMoveIn: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; onLink: (child: AppGraphNode.Node<Obj.Unknown>, index?: number) => void; dropInto: Container; onRearrange: (nextOrder: unknown[]) => void; onMoveOut: (child: AppGraphNode.Node<Obj.Unknown>, destination: AppGraphNode.Node) => void; listOf: Container; label: string | [string, Record<string, any>]; icon: string; iconHue: string | undefined; disposition: string | string[] | undefined; testId: string; persistenceClass: string; persistenceKey: import("@dxos/keys").SpaceId; selectable: boolean; draggable: boolean | undefined; droppable: boolean | undefined; blockInstruction: ((source: TreeData, instruction: AppNode.Instruction) => boolean) | undefined; };} | null
node !== null), ); },});
Attach actions to nodes
Section titled “Attach actions to nodes”AppGraphBuilder.createTypeExtension matches nodes whose data is an object of one type. Its actions callback returns
action nodes. AppGraphNode.makeAction makes one; disposition decides where it shows. AppNode.makeToolbarAction
is a shortcut for the object toolbar.
import * as import Effect
Effect from 'effect/Effect';import * as import AppGraphBuilder
AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';import * as import AppGraphNode
AppGraphNode from '@dxos/app-graph/AppGraphNode';import { import Obj
Obj } from '@dxos/echo';
// An action on every node whose data is a Note.const const pin: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
pin = import AppGraphBuilder
AppGraphBuilder.const createTypeExtension: <typeof Note, never, "pin">(options: AppGraphBuilder.CreateTypeExtensionOptions<typeof Note, never, "pin">) => Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
Create an extension that matches nodes by schema type.
The entity type is inferred from the schema type and works for both object and relation schemas.
Returns an Effect to allow callbacks to access services via dependency injection.
createTypeExtension({ id: "pin"
id: 'pin', type: typeof Note
type: class Note
Note, actions?: ((object: Note & OfKind<EntityKind.Object>, get: AtomContext) => Effect.Effect<Omit<AppGraphNode.NodeArg<AppGraphNode.ActionData<any>, Record<string, any>>, "type">[], never, never>) | undefined
actions: (note: Note & OfKind<EntityKind.Object>
note) => import Effect
Effect.const succeed: <AppGraphNode.NodeArg<AppGraphNode.ActionData<never>, Record<string, any>>[]>(value: AppGraphNode.NodeArg<AppGraphNode.ActionData<never>, Record<string, any>>[]) => Effect.Effect<AppGraphNode.NodeArg<AppGraphNode.ActionData<never>, Record<string, any>>[], never, never>
Creates an Effect that always succeeds with a given value.
When to use
Use when an effect should complete successfully with a specific value without any errors
or external dependencies.
Example (Creating a successful effect)
import { Effect } from "effect"
// Creating an effect that represents a successful scenario//// ┌─── Effect<number, never, never>// ▼const success = Effect.succeed(42)Effect.runSync(success) // => 42
succeed([ import AppGraphNode
AppGraphNode.const makeAction: <never>(arg: Omit<AppGraphNode.NodeArg<AppGraphNode.ActionData<never>, Record<string, any>>, "type" | "nodes" | "edges">) => AppGraphNode.NodeArg<AppGraphNode.ActionData<never>, Record<string, any>>
Create an action node. Automatically sets type: ActionType.
makeAction({ id: string
Globally unique ID.
id: 'pin', data?: AppGraphNode.ActionData<never> | undefined
Data the node represents.
data: () => import Effect
Effect.const sync: <Note & OfKind<EntityKind.Object>>(thunk: LazyArg<Note & OfKind<EntityKind.Object>>) => Effect.Effect<Note & OfKind<EntityKind.Object>, never, never>
Creates an Effect that represents a synchronous side-effectful computation.
When to use
Use when you need to wrap a synchronous side-effectful operation that is not
expected to throw.
Details
The provided function is evaluated lazily when the effect runs.
Gotchas
The function must not throw. If it throws, the thrown value is treated as a
defect, not as a typed failure. Use try when throwing is expected.
Example (Capturing synchronous logging in an Effect)
import { Effect } from "effect"const output: Array<unknown> = []
const log = (message: string) => Effect.sync(() => { void output.push(message) // side effect })
// ┌─── Effect<void, never, never>// ▼const program = log("Hello, World!")Effect.runSync(program)output // => ["Hello, World!"]
sync(() => import Obj
Obj.const update: <Note & OfKind<EntityKind.Object>>(obj: Note & OfKind<EntityKind.Object>, callback: ChangeCallback<Note & OfKind<EntityKind.Object>>) => Note & OfKind<EntityKind.Object>
Perform mutations on an echo object within a controlled context.
All mutations within the callback are batched and trigger a single notification
when the callback completes. Direct mutations outside of Obj.update will throw
an error for echo objects.
This function also works with nested objects within echo objects (e.g., Template structs)
that are reactive at runtime.
update(note: Note & OfKind<EntityKind.Object>
note, (note: { title?: string | undefined; pinned?: boolean | undefined; id: EntityId; "~@dxos/echo/Kind": EntityKind.Object;}
note) => (note: { title?: string | undefined; pinned?: boolean | undefined; id: EntityId; "~@dxos/echo/Kind": EntityKind.Object;}
note.pinned?: boolean | undefined
pinned = !note: { title?: string | undefined; pinned?: boolean | undefined; id: EntityId; "~@dxos/echo/Kind": EntityKind.Object;}
note.pinned?: boolean | undefined
pinned)), ), properties?: Readonly<Record<string, any>> | undefined
Properties of the node relevant to displaying the node.
properties: { label: string
label: 'Pin', icon: string
icon: 'ph--push-pin--regular', disposition: string
disposition: 'list-item', }, }), ]),});
Both builders return Effects. Run them in the module body and contribute the result through
AppCapabilities.AppGraphBuilder.
import * as import Effect
Effect from 'effect/Effect';import * as import Capability
Capability from '@dxos/app-framework/Capability';import * as import AppCapabilities
AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
export default import Capability
Capability.const makeModule: <void, Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.appGraphBuilder", "multi">>, never, never>(fn: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.appGraphBuilder", "multi">>, never, Scope>) => (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.appGraphBuilder", "multi">>, never, Scope>
Helper to define a capability module with explicit typing.
Wraps the default export function to provide better type inference and make the pattern explicit.
This helper provides explicit typing for the module activation function,
making it clear that the function should:
- Access declared
requires via yield* (or the ambient Capability.Service/Plugin.Service)
- Return an array of typed
Contribution
s (see
contribute
/
contributeAll
)
makeModule( import Effect
Effect.const fnUntraced: <Effect.Effect<[Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[]], never, never>, Capability.Contribution<...>, []>(body: (this: unassigned) => Generator<...>) => () => Effect.Effect<...> (+41 overloads)
fnUntraced(function* () { const const extensions: [Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[]]
extensions = yield* import Effect
Effect.const all: <readonly [Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>, Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>], { ...;}>(arg: readonly [...], options?: { ...;} | undefined) => Effect.Effect<...>
Combines an iterable or record of effects into one effect whose success shape
follows the input.
When to use
Use to run a known collection of effects and collect results in the same
tuple, iterable, or record shape.
Details
Tuple and iterable inputs collect results in order. Record inputs collect
results under the same keys. By default, the combined effect fails on the
first failure; with concurrent execution, effects that have already started
may be interrupted, while effects not yet started are skipped.
Options:
Use concurrency to control sequential or concurrent execution. Use
mode: "result" to run every effect and collect each success or failure as a
Result in the same output shape. Use discard: true to ignore successful
values and return void.
Example (Collecting tuple results in order)
import { Effect } from "effect"
const tupleOfEffects = [ Effect.succeed(42), Effect.succeed("Hello")] as const
// ┌─── Effect<[number, string], never, never>// ▼const resultsAsTuple = Effect.all(tupleOfEffects)
await Effect.runPromise(resultsAsTuple) // => [42, "Hello"]
Example (Collecting iterable results in order)
import { Effect } from "effect"
const iterableOfEffects: Iterable<Effect.Effect<number>> = [1, 2, 3].map( Effect.succeed)
// ┌─── Effect<number[], never, never>// ▼const resultsAsArray = Effect.all(iterableOfEffects)
await Effect.runPromise(resultsAsArray) // => [1, 2, 3]
Example (Collecting struct results by key)
import { Effect } from "effect"
const structOfEffects = { a: Effect.succeed(42), b: Effect.succeed("Hello")}
// ┌─── Effect<{ a: number; b: string; }, never, never>// ▼const resultsAsStruct = Effect.all(structOfEffects)
await Effect.runPromise(resultsAsStruct) // => { a: 42, b: "Hello" }
Example (Collecting record results by key)
import { Effect } from "effect"
const recordOfEffects: Record<string, Effect.Effect<number>> = { key1: Effect.succeed(1), key2: Effect.succeed(2)}
// ┌─── Effect<{ [x: string]: number; }, never, never>// ▼const resultsAsRecord = Effect.all(recordOfEffects)
await Effect.runPromise(resultsAsRecord) // => { key1: 1, key2: 2 }
Example (Stopping on the first failure)
import { Effect, Exit } from "effect"const output: Array<unknown> = []const record = (value: unknown) => Effect.sync(() => { output.push(value) })
const program = Effect.all([ Effect.succeed("Task1").pipe(Effect.tap(record)), Effect.fail("Task2: Oh no!").pipe(Effect.tap(record)), // Won't execute due to earlier failure Effect.succeed("Task3").pipe(Effect.tap(record))])
const outcome = await Effect.runPromiseExit(program)const observation = [output, outcome] // => [["Task1"], Exit.fail("Task2: Oh no!")]
all([const notes: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
notes, const pin: Effect.Effect<Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], never, never>
pin]); return import Capability
Capability.const contribute: <Capability.MultiTag<AppGraphBuilder.BuilderExtensions, "org.dxos.app-framework.capability.appGraphBuilder">>(capability: Capability.MultiTag<AppGraphBuilder.BuilderExtensions, "org.dxos.app-framework.capability.appGraphBuilder">, implementation: AppGraphBuilder.BuilderExtensions) => Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.appGraphBuilder", "multi">> (+1 overload)
contribute( import AppCapabilities
AppCapabilities.const AppGraphBuilder: Capability.MultiTag<AppGraphBuilder.BuilderExtensions, "org.dxos.app-framework.capability.appGraphBuilder">
AppGraphBuilder, const extensions: [Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[]]
extensions.Array<Readonly<{ id: string; position?: Position; relation?: RelationInput | undefined; meta?: UrlBinding | undefined; connector?: Connector<...> | undefined; }>[]>.flat<[Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[], Readonly<{ id: string; position?: Position; relation?: AppGraphNode.RelationInput | undefined; meta?: AppGraphBuilder.UrlBinding | undefined; connector?: Connector<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>, AppGraphNode.NodeArg<any, Record<string, any>>> | undefined;}>[]], 1>(this: [...], depth?: 1 | undefined): Readonly<...>[]
Returns a new array with all sub-array elements concatenated into it recursively up to the
specified depth.
flat(), ); }),);
Work with AppNode and GraphPath
Section titled “Work with AppNode and GraphPath”AppNode builds the common kinds of node: makeObject for an ECHO object, makeSection for a heading in the tree,
makeCompanion and makeDeckCompanion for panels. GraphPath builds and parses node paths, which navigation
operations take.
import * as import GraphPath
GraphPath from '@dxos/app-toolkit/GraphPath';
export const const pathOfNote: (note: Note, spaceId: string) => string
pathOfNote = (note: Note
note: class Note
Note, spaceId: string
spaceId: string) => import GraphPath
GraphPath.const getSpacePath: (spaceId: string, ...segments: string[]) => string
Canonical qualified path to a space node.
Optional additional segments are appended (e.g. a section name for a well-known child node).
getSpacePath(spaceId: string
spaceId, note: Note
note.OfKind<EntityKind.Object>.id: string
id);
Expand and read the graph from a component
Section titled “Expand and read the graph from a component”useAppGraph returns the graph. Expand a node before reading its connections, because the graph builds them lazily.
graph.connections(id, relation) is the same data as an atom, for a component that should update as the graph changes.
import { function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
Accepts a function that contains imperative, possibly effectful code.
useEffect, function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)
Returns a stateful value, and a function to update it.
useState } from 'react';import * as import AppGraph
AppGraph from '@dxos/app-graph/AppGraph';import * as import AppGraphNode
AppGraphNode from '@dxos/app-graph/AppGraphNode';import * as import Hooks
Hooks from '@dxos/app-toolkit/Hooks';import * as import EffectEx
EffectEx from '@dxos/effect/EffectEx';
export const const ChildCount: ({ id }: { id: string;}) => React.JSX.Element
ChildCount = ({ id: string
id }: { id: string
id: string }) => { const { const graph: AppGraph.ExpandableGraph
graph } = import Hooks
Hooks.const useAppGraph: () => AppCapabilities.AppGraph
Hook to get the current app graph.
useAppGraph(); const [const count: number
count, const setCount: React.Dispatch<React.SetStateAction<number>>
setCount] = useState<number>(initialState: number | (() => number)): [number, React.Dispatch<React.SetStateAction<number>>] (+1 overload)
Returns a stateful value, and a function to update it.
useState(0); function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
Accepts a function that contains imperative, possibly effectful code.
useEffect(() => { // Children are built lazily: expand the node, then read its connections. void import EffectEx
EffectEx.runPromise<void, never>(effect: Effect.Effect<void, never, never>, options?: { signal?: AbortSignal;}): Promise<void>export runPromise
Runs the embedded effect asynchronously and throws any failures and defects as errors.
Inserts effect spans as stack frames.
The error will have stack frames of where the effect was run (if stack trace limit allows).
Removes effect runtime internal stack frames.
To be used in place of Effect.runPromise.
runPromise( import AppGraph
AppGraph.const expand: <AppGraph.ExpandableGraph>(graph: AppGraph.ExpandableGraph, id: string, relation: AppGraphNode.RelationInput) => Effect.Effect<void>
Expand a node in the graph, off the paint-critical path.
Yields to the main thread before running
expandSync
, so a caller reacting to input does not
block the frame. Interrupting the effect cancels a still-pending expansion, which makes rescheduling
(e.g. superseding a hover with the next one) a matter of interrupting the previous fiber.
expand(const graph: AppGraph.ExpandableGraph
graph, id: string
id, import AppGraphNode
AppGraphNode.const child: Readonly<{ kind: string; direction: AppGraphNode.RelationDirection;}>
The relation a node's children hang off; the graph's structure.
child), ).Promise<void>.then<void, never>(onfulfilled?: ((value: void) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
Attaches callbacks for the resolution and/or rejection of the Promise.
then(() => const setCount: (value: React.SetStateAction<number>) => void
setCount(import AppGraph
AppGraph.const getConnections: (graph: AppGraph.BaseGraph, id: string, relation: AppGraphNode.RelationInput) => AppGraphNode.Node[]
Get all nodes connected to the node with the given id by the given relation from the graph's registry.
getConnections(const graph: AppGraph.ExpandableGraph
graph, id: string
id, import AppGraphNode
AppGraphNode.const child: Readonly<{ kind: string; direction: AppGraphNode.RelationDirection;}>
The relation a node's children hang off; the graph's structure.
child).Array<Readonly<{ id: string; type: string; cacheable?: string[]; properties: Readonly<Record<string, any>>; data: any; }>>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length), ); }, [const graph: AppGraph.ExpandableGraph
graph, id: string
id]); return <JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span>{const count: number
count}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span>;};
Limits
Section titled “Limits”- An extension’s id and a node’s id must be camelCase. A hyphenated id is dropped without an error.
- A connector must be cheap and must not write. It runs again on every change to the state it reads.
- Paths are stable only while the ids along them are. Moving an object to another space changes its path.
Related
Section titled “Related”| Page | What it covers |
|---|---|
| Extend the App Graph | Sections, items and actions end to end. |
| Operation | The operations that actions invoke. |