@dxos/app-framework 0.14.0 → 0.16.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 +73 -0
- package/dist/lib/App.mjs +1 -2
- package/dist/lib/Hooks.mjs +1 -2
- package/dist/lib/chunk-App.mjs +11 -0
- package/dist/lib/chunk-App.mjs.map +1 -0
- package/dist/lib/chunk-Cli.mjs +8 -0
- package/dist/lib/chunk-Cli.mjs.map +1 -0
- package/dist/lib/chunk-Devtools.mjs +112 -0
- package/dist/lib/chunk-Devtools.mjs.map +1 -0
- package/dist/lib/chunk-Hooks2.mjs +35 -0
- package/dist/lib/chunk-Hooks2.mjs.map +1 -0
- package/dist/lib/chunk-PluginManagerProvider.mjs +12 -0
- package/dist/lib/chunk-PluginManagerProvider.mjs.map +1 -0
- package/dist/lib/chunk-ProcessManagerPlugin.mjs +3 -3
- package/dist/lib/chunk-ProcessManagerPlugin.mjs.map +1 -1
- package/dist/lib/chunk-Role.mjs +19 -0
- package/dist/lib/chunk-Role.mjs.map +1 -0
- package/dist/lib/chunk-SharedPackages.mjs +11 -0
- package/dist/lib/chunk-SharedPackages.mjs.map +1 -0
- package/dist/lib/chunk-Surface.mjs.map +1 -1
- package/dist/lib/chunk-Surface2.mjs +37 -0
- package/dist/lib/chunk-Surface2.mjs.map +1 -0
- package/dist/lib/chunk-SurfaceManager.mjs +3 -3
- package/dist/lib/chunk-SurfaceManager.mjs.map +1 -1
- package/dist/lib/chunk-activation-event.mjs +86 -0
- package/dist/lib/chunk-activation-event.mjs.map +1 -0
- package/dist/lib/chunk-activation-events.mjs +66 -0
- package/dist/lib/chunk-activation-events.mjs.map +1 -0
- package/dist/lib/chunk-capabilities.mjs +202 -0
- package/dist/lib/chunk-capabilities.mjs.map +1 -0
- package/dist/lib/chunk-capability-manager.mjs +203 -0
- package/dist/lib/chunk-capability-manager.mjs.map +1 -0
- package/dist/lib/chunk-capability2.mjs +2 -2
- package/dist/lib/chunk-capability2.mjs.map +1 -1
- package/dist/lib/chunk-history-tracker.mjs +1 -1
- package/dist/lib/chunk-hooks.mjs +5 -5
- package/dist/lib/chunk-hooks.mjs.map +1 -1
- package/dist/lib/chunk-plugin-asset-cache.mjs +28 -0
- package/dist/lib/chunk-plugin-asset-cache.mjs.map +1 -0
- package/dist/lib/chunk-plugin-manager.mjs +68 -35
- package/dist/lib/chunk-plugin-manager.mjs.map +1 -1
- package/dist/lib/chunk-plugin-manager2.mjs +22 -0
- package/dist/lib/chunk-plugin-manager2.mjs.map +1 -0
- package/dist/lib/chunk-plugin-manifest.mjs +126 -0
- package/dist/lib/chunk-plugin-manifest.mjs.map +1 -0
- package/dist/lib/chunk-plugin.mjs +354 -0
- package/dist/lib/chunk-plugin.mjs.map +1 -0
- package/dist/lib/chunk-process-manager-capability.mjs +11 -10
- package/dist/lib/chunk-process-manager-capability.mjs.map +1 -1
- package/dist/lib/chunk-registry.mjs.map +1 -1
- package/dist/lib/chunk-undo-mapping.mjs +47 -0
- package/dist/lib/{plugin-process-manager/history/undo-mapping.mjs.map → chunk-undo-mapping.mjs.map} +1 -1
- package/dist/lib/chunk-undo-registry.mjs +22 -0
- package/dist/lib/{plugin-process-manager/history/undo-registry.mjs.map → chunk-undo-registry.mjs.map} +1 -1
- package/dist/lib/chunk-url-loader.mjs +345 -0
- package/dist/lib/chunk-url-loader.mjs.map +1 -0
- package/dist/lib/cli.mjs +3 -3
- package/dist/lib/cli.mjs.map +1 -1
- package/dist/lib/common/Role.mjs +1 -19
- package/dist/lib/common/activation-events.mjs +1 -57
- package/dist/lib/common/capabilities.mjs +1 -202
- package/dist/lib/config.mjs +1 -2
- package/dist/lib/core/activation-event.mjs +1 -86
- package/dist/lib/core/capability-manager.mjs +1 -203
- package/dist/lib/core/capability.mjs +1 -2
- package/dist/lib/core/plugin-asset-cache.mjs +1 -28
- package/dist/lib/core/plugin-manager.mjs +2 -19
- package/dist/lib/core/plugin-manifest.mjs +1 -126
- package/dist/lib/core/plugin.mjs +1 -354
- package/dist/lib/core/registry.mjs +1 -2
- package/dist/lib/core/url-loader.mjs +1 -345
- package/dist/lib/index.mjs +19 -19
- package/dist/lib/ns/App.mjs +2 -11
- package/dist/lib/ns/Cli.mjs +2 -8
- package/dist/lib/ns/Devtools.mjs +1 -112
- package/dist/lib/ns/Hooks.mjs +2 -35
- package/dist/lib/ns/PluginManagerProvider.mjs +2 -12
- package/dist/lib/ns/SharedPackages.mjs +2 -11
- package/dist/lib/ns/Surface.mjs +2 -35
- package/dist/lib/ns/Translations.mjs +1 -2
- package/dist/lib/plugin-cli.mjs +2 -4
- package/dist/lib/plugin-cli.mjs.map +1 -1
- package/dist/lib/plugin-process-manager/ProcessManagerPlugin.mjs +1 -2
- package/dist/lib/plugin-process-manager/history/history-tracker.mjs +1 -2
- package/dist/lib/plugin-process-manager/history/undo-mapping.mjs +1 -47
- package/dist/lib/plugin-process-manager/history/undo-registry.mjs +1 -22
- package/dist/lib/testing/react.mjs +1 -1
- package/dist/lib/testing/react.mjs.map +1 -1
- package/dist/lib/testing.mjs +5 -5
- package/dist/lib/testing.mjs.map +1 -1
- package/dist/lib/vite-plugin.mjs +3 -3
- package/dist/lib/vite-plugin.mjs.map +1 -1
- package/dist/types/src/common/activation-events.d.ts +8 -0
- package/dist/types/src/common/activation-events.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager/manager-state.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager/module-loader.d.ts +0 -10
- package/dist/types/src/core/plugin-manager/module-loader.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager/plugin-manager.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -32
- package/src/common/activation-events.ts +9 -0
- package/src/core/plugin-manager/manager-state.ts +14 -12
- package/src/core/plugin-manager/module-loader.ts +43 -7
- package/src/core/plugin-manager/plugin-manager.ts +2 -1
- package/src/plugin-process-manager/process-manager-capability.test.ts +66 -1
- package/src/plugin-process-manager/process-manager-capability.ts +22 -2
- package/src/ui/components/Surface/SurfaceComponent.stories.tsx +9 -7
- package/src/ui/hooks/useApp.tsx +2 -1
- package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +19 -1
- package/dist/lib/common/Role.mjs.map +0 -1
- package/dist/lib/common/activation-events.mjs.map +0 -1
- package/dist/lib/common/capabilities.mjs.map +0 -1
- package/dist/lib/core/activation-event.mjs.map +0 -1
- package/dist/lib/core/capability-manager.mjs.map +0 -1
- package/dist/lib/core/plugin-asset-cache.mjs.map +0 -1
- package/dist/lib/core/plugin-manager.mjs.map +0 -1
- package/dist/lib/core/plugin-manifest.mjs.map +0 -1
- package/dist/lib/core/plugin.mjs.map +0 -1
- package/dist/lib/core/url-loader.mjs.map +0 -1
- package/dist/lib/ns/App.mjs.map +0 -1
- package/dist/lib/ns/Cli.mjs.map +0 -1
- package/dist/lib/ns/Devtools.mjs.map +0 -1
- package/dist/lib/ns/Hooks.mjs.map +0 -1
- package/dist/lib/ns/PluginManagerProvider.mjs.map +0 -1
- package/dist/lib/ns/SharedPackages.mjs.map +0 -1
- package/dist/lib/ns/Surface.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as Service, u as contribute, x as makeModule } from "./chunk-capability.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { c as Service$1 } from "./chunk-plugin.mjs";
|
|
3
|
+
import { _ as ServiceResolver$1, a as LayerSpec$1, c as OperationHandlers, d as ProcessManager$1, f as ProcessManagerRuntime, g as RemoteTraceMonitor$1, l as OperationInvoker, s as OperationHandler, t as AtomRegistry, v as TraceSink } from "./chunk-capabilities.mjs";
|
|
4
4
|
import { r as layerIdb } from "./chunk-ProcessManagerPlugin.mjs";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Exit from "effect/Exit";
|
|
@@ -16,7 +16,7 @@ import * as Stream from "effect/Stream";
|
|
|
16
16
|
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
17
17
|
import * as Registry from "effect/reactivity/AtomRegistry";
|
|
18
18
|
import * as Tracer from "effect/Tracer";
|
|
19
|
-
import { LayerStack, ProcessManager, ProcessOperationInvoker, RemoteProcessManager, RemoteTraceMonitor, UnifiedProcessManager } from "@dxos/compute-runtime";
|
|
19
|
+
import { LayerStack, ProcessManager, ProcessOperationInvoker, RemoteOperationInvoker, RemoteProcessManager, RemoteTraceMonitor, UnifiedProcessManager } from "@dxos/compute-runtime";
|
|
20
20
|
import * as LayerSpec from "@dxos/compute/LayerSpec";
|
|
21
21
|
import * as Process from "@dxos/compute/Process";
|
|
22
22
|
import * as Trace from "@dxos/compute/Trace";
|
|
@@ -47,7 +47,7 @@ var makeDynamicTraceSink = (getFactories, resolver) => {
|
|
|
47
47
|
log.warn("trace sink factory failed", { err }, {
|
|
48
48
|
"~LogMeta": "~LogMeta",
|
|
49
49
|
F: __dxlog_file,
|
|
50
|
-
L:
|
|
50
|
+
L: 87,
|
|
51
51
|
S: void 0
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -107,7 +107,7 @@ var process_manager_capability_default = makeModule(Effect.fnUntraced(function*
|
|
|
107
107
|
log.info("setup process manager", { traceSinks: traceSinkContributions.get().length }, {
|
|
108
108
|
"~LogMeta": "~LogMeta",
|
|
109
109
|
F: __dxlog_file,
|
|
110
|
-
L:
|
|
110
|
+
L: 169,
|
|
111
111
|
S: this
|
|
112
112
|
});
|
|
113
113
|
let processManagerHolder;
|
|
@@ -124,7 +124,7 @@ var process_manager_capability_default = makeModule(Effect.fnUntraced(function*
|
|
|
124
124
|
invariant(processManagerHolder, "ProcessManagerService accessed before the process-manager runtime was initialised", {
|
|
125
125
|
"~LogMeta": "~LogMeta",
|
|
126
126
|
F: __dxlog_file,
|
|
127
|
-
L:
|
|
127
|
+
L: 199,
|
|
128
128
|
S: this,
|
|
129
129
|
A: ["processManagerHolder", "'ProcessManagerService accessed before the process-manager runtime was initialised'"]
|
|
130
130
|
});
|
|
@@ -132,9 +132,10 @@ var process_manager_capability_default = makeModule(Effect.fnUntraced(function*
|
|
|
132
132
|
}))));
|
|
133
133
|
const fallbacks = Effect.runSync(Effect.gen(function* () {
|
|
134
134
|
const remoteProcessManager = yield* RemoteProcessManager.Service;
|
|
135
|
+
const remoteOperationInvoker = yield* RemoteOperationInvoker.Service;
|
|
135
136
|
const remoteTraceMonitor = yield* RemoteTraceMonitor.Service;
|
|
136
|
-
return Context.make(RemoteProcessManager.Service, remoteProcessManager).pipe(Context.add(RemoteTraceMonitor.Service, remoteTraceMonitor));
|
|
137
|
-
}).pipe(Effect.provide(Layer.mergeAll(RemoteProcessManager.layerNoop, remoteTraceMonitors.length > 0 ? Layer.succeed(RemoteTraceMonitor.Service, remoteTraceMonitors[0]) : RemoteTraceMonitor.layerNoop)), Effect.provideService(Registry.AtomRegistry, atomRegistry)));
|
|
137
|
+
return Context.make(RemoteProcessManager.Service, remoteProcessManager).pipe(Context.add(RemoteOperationInvoker.Service, remoteOperationInvoker), Context.add(RemoteTraceMonitor.Service, remoteTraceMonitor));
|
|
138
|
+
}).pipe(Effect.provide(Layer.mergeAll(RemoteProcessManager.layerNoop, RemoteOperationInvoker.layerNoop, remoteTraceMonitors.length > 0 ? Layer.succeed(RemoteTraceMonitor.Service, remoteTraceMonitors[0]) : RemoteTraceMonitor.layerNoop)), Effect.provideService(Registry.AtomRegistry, atomRegistry)));
|
|
138
139
|
const layerStack = new LayerStack.LayerStack({
|
|
139
140
|
layers: [
|
|
140
141
|
ambientLayerSpec,
|
|
@@ -160,7 +161,7 @@ var process_manager_capability_default = makeModule(Effect.fnUntraced(function*
|
|
|
160
161
|
}, {
|
|
161
162
|
"~LogMeta": "~LogMeta",
|
|
162
163
|
F: __dxlog_file,
|
|
163
|
-
L:
|
|
164
|
+
L: 253,
|
|
164
165
|
S: this
|
|
165
166
|
});
|
|
166
167
|
}
|
|
@@ -177,7 +178,7 @@ var process_manager_capability_default = makeModule(Effect.fnUntraced(function*
|
|
|
177
178
|
yield* Effect.addFinalizer(() => Scope.close(stackScope, Exit.void));
|
|
178
179
|
const unifiedProcessManager = fromStack(serviceResolver, stackScope, atomRegistry);
|
|
179
180
|
const unifiedProcessManagerLayer = Layer.succeed(Process.ManagerService, unifiedProcessManager);
|
|
180
|
-
const operationInvokerLayer = ProcessOperationInvoker.layer.pipe(Layer.provide(Layer.mergeAll(unifiedProcessManagerLayer, baseLayer, Layer.succeed(Database.Origin, "user"))));
|
|
181
|
+
const operationInvokerLayer = ProcessOperationInvoker.layer.pipe(Layer.provide(Layer.mergeAll(unifiedProcessManagerLayer, baseLayer, Layer.succeed(Database.Origin, "user"), Layer.succeed(RemoteOperationInvoker.Service, { invoke: (ctx, deployedId, input, options) => serviceResolver.resolve(RemoteOperationInvoker.Service, {}).pipe(Scope.provide(stackScope), Effect.orDie, Effect.flatMap((remote) => remote.invoke(ctx, deployedId, input, options))) }))));
|
|
181
182
|
const runtimeLayer = Layer.mergeAll(baseLayer, processManagerLayer, operationInvokerLayer, unifiedProcessManagerLayer);
|
|
182
183
|
const managedRuntime = ManagedRuntime.make(runtimeLayer);
|
|
183
184
|
yield* Effect.addFinalizer(() => Effect.promise(() => managedRuntime.dispose()).pipe(Effect.andThen(layerStack.destroy())));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-process-manager-capability.mjs","names":[],"sources":["../../src/plugin-process-manager/process-manager-capability.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Layer from 'effect/Layer';\nimport * as ManagedRuntime from 'effect/ManagedRuntime';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Registry from 'effect/reactivity/AtomRegistry';\nimport * as Scope from 'effect/Scope';\nimport * as Stream from 'effect/Stream';\nimport * as Tracer from 'effect/Tracer';\n\nimport {\n LayerStack,\n ProcessManager,\n ProcessOperationInvoker,\n RemoteProcessManager,\n RemoteTraceMonitor,\n UnifiedProcessManager,\n} from '@dxos/compute-runtime';\nimport * as LayerSpec from '@dxos/compute/LayerSpec';\nimport * as OperationHandlerSet from '@dxos/compute/OperationHandlerSet';\nimport * as Process from '@dxos/compute/Process';\nimport * as ServiceResolver from '@dxos/compute/ServiceResolver';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Database } from '@dxos/echo';\nimport * as OtelTracer from '@dxos/effect/OtelTracer';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n// Explicit import so the emitted `.d.ts` references the package via its public\n// alias instead of a relative `node_modules` path (TS2883).\nimport { OperationInvoker } from '@dxos/operation';\n\nimport { Capabilities } from '../common/index.ts';\nimport { Capability, Plugin } from '../core/index.ts';\nimport { layerIdb } from './idb-key-value-store.ts';\n\n//\n// Capability Module\n//\n// Hosts the {@link ProcessManager} runtime for the plugin system.\n//\n// Workflow:\n// 1. Requires {@link Capabilities.LayerSpec} and {@link Capabilities.OperationHandler}\n// contributions from dependency-mode modules.\n// 2. Collects all contributed {@link LayerSpec.LayerSpec}s and builds a\n// {@link LayerStack} whose {@link ServiceResolver} drives process-scoped\n// service resolution; specs contributed later are added to the live stack.\n// 3. Wires a reactive {@link OperationHandlerSet} that tracks\n// {@link Capabilities.OperationHandler} contributions and invalidates its\n// cached merge when new handlers register.\n// 4. Composes the fixed runtime requirements (capability/plugin managers,\n// service resolver, operation invoker, process manager) into a single\n// {@link Layer} and builds a {@link ManagedRuntime} from it.\n// 5. Exposes a disposable-less wrapper as {@link Capabilities.ProcessManagerRuntime}\n// (the plugin system manages its lifecycle).\n//\n\n/**\n * Trace sink over the LIVE contribution list, so a sink contributed after the runtime was built (an\n * on-demand module, like plugin-progress's adapter) still observes writes. Instances are cached per\n * factory, since a sink may hold state across writes and must not be rebuilt underneath itself.\n */\nexport const makeDynamicTraceSink = (\n getFactories: () => readonly Capabilities.TraceSinkFactory[],\n resolver: ServiceResolver.ServiceResolver,\n): Trace.Sink => {\n const instances = new Map<Capabilities.TraceSinkFactory, Trace.Sink>();\n const resolve = (): Trace.Sink[] => {\n const sinks: Trace.Sink[] = [];\n for (const factory of getFactories()) {\n const existing = instances.get(factory);\n if (existing) {\n sinks.push(existing);\n continue;\n }\n try {\n const sink = factory({ resolver });\n instances.set(factory, sink);\n sinks.push(sink);\n } catch (err) {\n // One factory that cannot build must not cost the sinks behind it their messages.\n log.warn('trace sink factory failed', { err });\n }\n }\n return sinks;\n };\n\n // `mergeSinks` per write, for its guarantee that one throwing sink cannot break the chain.\n return { write: (message) => Trace.mergeSinks(resolve()).write(message) };\n};\n\n/**\n * The application's one {@link Process.ManagerService}, as built by the stack. Every built-in and plugin\n * consumer sees the same manager, so a host's remote manager (e.g. EDGE) reaches the app's invoker too.\n */\nconst ProcessManagerSpec = LayerSpec.make(\n {\n affinity: 'application',\n requires: [\n ProcessManager.ProcessManagerService,\n RemoteProcessManager.Service,\n RemoteTraceMonitor.Service,\n Registry.AtomRegistry,\n ],\n provides: [Process.ManagerService],\n },\n () => UnifiedProcessManager.layer,\n);\n\n/**\n * A {@link Process.Manager} that is the stack's {@link Process.ManagerService}, resolved on first use: building it\n * resolves the host's remote manager, which may need services (the client) that are not ready at activation.\n * The process tree reads empty until then.\n */\nexport const fromStack = (\n resolver: ServiceResolver.ServiceResolver,\n scope: Scope.Scope,\n registry: Registry.AtomRegistry,\n): Process.Manager => {\n const resolvedAtom = Atom.make<Process.Manager | undefined>(undefined).pipe(Atom.keepAlive);\n const manager: Effect.Effect<Process.Manager> = Effect.suspend(() => {\n const resolved = registry.get(resolvedAtom);\n return resolved !== undefined\n ? Effect.succeed(resolved)\n : resolver.resolve(Process.ManagerService, {}).pipe(\n Scope.provide(scope),\n Effect.tap((stackManager) => Effect.sync(() => registry.set(resolvedAtom, stackManager))),\n // The framework's own spec provides it, so a failure here is a broken stack, not a missing plugin.\n Effect.orDie,\n );\n });\n\n return {\n processTree: Effect.flatMap(manager, (stackManager) => stackManager.processTree),\n processTreeAtom: Atom.make((get) => {\n const stackManager = get(resolvedAtom);\n return stackManager === undefined ? [] : get(stackManager.processTreeAtom);\n }),\n list: (filter) => Effect.flatMap(manager, (stackManager) => stackManager.list(filter)),\n subscribeToTraceMessages: (filter) =>\n Stream.unwrap(Effect.map(manager, (stackManager) => stackManager.subscribeToTraceMessages(filter))),\n spawn: (definition, options) => Effect.flatMap(manager, (stackManager) => stackManager.spawn(definition, options)),\n handles: (options) => Effect.flatMap(manager, (stackManager) => stackManager.handles(options)),\n attach: (pid, options) => Effect.flatMap(manager, (stackManager) => stackManager.attach(pid, options)),\n };\n};\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const capabilityManager = yield* Capability.Service;\n const pluginManager = yield* Plugin.Service;\n const atomRegistry = yield* Capabilities.AtomRegistry;\n\n const layerSpecContributions = yield* Capabilities.LayerSpec;\n const traceSinkContributions = yield* Capabilities.TraceSink;\n const operationHandlerContributions = yield* Capabilities.OperationHandler;\n const remoteTraceMonitorContributions = yield* Capabilities.RemoteTraceMonitor;\n // Startup soft-ordering makes same-pass providers visible here; specs contributed later (a\n // plugin enabled after boot) join the live stack through the subscription below.\n const layerSpecs = layerSpecContributions.get();\n // Optional swarm-backed remote trace source (DX-1125); first contribution wins, else empty.\n const remoteTraceMonitors = remoteTraceMonitorContributions.get();\n\n log.info('setup process manager', { traceSinks: traceSinkContributions.get().length });\n\n // Forward reference to `ProcessManager.ProcessManagerService`. The runtime\n // that owns the manager depends transitively on `ServiceResolver` (which is\n // built from the `LayerStack` below), so we cannot materialise it before\n // the stack exists. Instead we publish the manager into this holder as\n // soon as the runtime is built, and the ambient layer reads it lazily via\n // `Layer.effect` — slice init only runs once a process actually triggers\n // service resolution, by which point the holder is populated.\n let processManagerHolder: ProcessManager.Manager | undefined;\n\n // Expose the foundational app-framework services through the LayerStack so\n // that operations declaring `services: [Capability.Service]` (and friends)\n // can resolve them via the ServiceResolver. Without this, only consumers\n // sitting on the same ManagedRuntime layer graph can see them — process\n // executions go through ServiceResolver.resolveAll and would fail.\n const ambientLayerSpec = LayerSpec.make(\n {\n affinity: 'application',\n requires: [],\n provides: [Capability.Service, Plugin.Service, Registry.AtomRegistry, ProcessManager.ProcessManagerService],\n },\n () =>\n Layer.mergeAll(\n Layer.succeed(Capability.Service, capabilityManager),\n Layer.succeed(Plugin.Service, pluginManager),\n Layer.succeed(Registry.AtomRegistry, atomRegistry),\n Layer.effect(\n ProcessManager.ProcessManagerService,\n Effect.sync(() => {\n invariant(\n processManagerHolder,\n 'ProcessManagerService accessed before the process-manager runtime was initialised',\n );\n return processManagerHolder;\n }),\n ),\n ),\n );\n\n // Fallbacks for a host with no remote runtime; a plugin spec providing either tag (e.g. EDGE's) wins.\n const fallbacks = Effect.runSync(\n Effect.gen(function* () {\n const remoteProcessManager = yield* RemoteProcessManager.Service;\n const remoteTraceMonitor = yield* RemoteTraceMonitor.Service;\n return Context.make(RemoteProcessManager.Service, remoteProcessManager).pipe(\n Context.add(RemoteTraceMonitor.Service, remoteTraceMonitor),\n );\n }).pipe(\n Effect.provide(\n Layer.mergeAll(\n RemoteProcessManager.layerNoop,\n // Remote ephemeral trace (DX-1125): the first contributed swarm-backed monitor, else no-op.\n remoteTraceMonitors.length > 0\n ? Layer.succeed(RemoteTraceMonitor.Service, remoteTraceMonitors[0])\n : RemoteTraceMonitor.layerNoop,\n ),\n ),\n Effect.provideService(Registry.AtomRegistry, atomRegistry),\n ),\n );\n\n const layerStack = new LayerStack.LayerStack({\n layers: [ambientLayerSpec, ProcessManagerSpec, ...layerSpecs],\n services: fallbacks,\n });\n const serviceResolver = layerStack.getServiceResolver();\n\n // The stack extends built slices in place, so admitting a late spec costs no live service.\n const rejectedLayerSpecs = new WeakSet<LayerSpec.LayerSpec>();\n const admitLayerSpecs = (specs: readonly LayerSpec.LayerSpec[]) => {\n const candidates = specs.filter((spec) => !rejectedLayerSpecs.has(spec));\n try {\n layerStack.addLayers(candidates);\n } catch {\n // Admitted one at a time so a single bad spec does not keep the others out.\n for (const spec of candidates) {\n try {\n layerStack.addLayers([spec]);\n } catch (err) {\n rejectedLayerSpecs.add(spec);\n log.error('LayerSpec rejected', { provides: spec.provides.map((tag) => tag.key), err });\n }\n }\n }\n };\n const cancelLayerSpecWatch = atomRegistry.subscribe(layerSpecContributions.atom, admitLayerSpecs);\n yield* Effect.addFinalizer(() => Effect.sync(cancelLayerSpecWatch));\n // Covers a contribution landing between the snapshot above and the subscription.\n admitLayerSpecs(layerSpecContributions.get());\n\n // Handler sets register eagerly at startup (keyed sets defer only handler BODIES), so the\n // reactive view over contributions is complete at boot — no demand pull on a miss.\n const handlerSet = OperationHandlerSet.reactive(atomRegistry, operationHandlerContributions.atom);\n\n const mergedTraceSink = makeDynamicTraceSink(() => traceSinkContributions.get(), serviceResolver);\n\n // Base services required by ProcessManager and the operation invoker.\n // Sensible defaults are provided here; plugins that want alternative\n // implementations (e.g. persistent KV store) can contribute their own LayerSpec entries\n // against the ServiceResolver.\n const baseLayer = Layer.mergeAll(\n Layer.succeed(Capability.Service, capabilityManager),\n Layer.succeed(Plugin.Service, pluginManager),\n Layer.succeed(Registry.AtomRegistry, atomRegistry),\n Layer.succeed(ServiceResolver.ServiceResolver, serviceResolver),\n OperationHandlerSet.provide(handlerSet),\n layerIdb,\n Layer.succeed(Trace.TraceSink, mergedTraceSink),\n // Over the OTel global provider, a proxy that no-ops until one is registered, so this is\n // installed whether or not observability exists.\n Layer.succeed(Tracer.Tracer, OtelTracer.makeGlobal('@dxos/app-framework/process-manager')),\n );\n\n const processManagerLayer = ProcessManager.layer({ runtimeName: Trace.CommonRuntimeName.local }).pipe(\n Layer.provide(baseLayer),\n );\n // Holds the stack's application slice that the manager resolves from, for the module's lifetime.\n const stackScope = yield* Scope.make();\n yield* Effect.addFinalizer(() => Scope.close(stackScope, Exit.void));\n const unifiedProcessManager = fromStack(serviceResolver, stackScope, atomRegistry);\n const unifiedProcessManagerLayer = Layer.succeed(Process.ManagerService, unifiedProcessManager);\n const operationInvokerLayer = ProcessOperationInvoker.layer.pipe(\n // Operations invoked through the app's own invoker are the person's actions, from a menu, dialog or shortcut.\n Layer.provide(Layer.mergeAll(unifiedProcessManagerLayer, baseLayer, Layer.succeed(Database.Origin, 'user'))),\n );\n\n const runtimeLayer = Layer.mergeAll(\n baseLayer,\n processManagerLayer,\n operationInvokerLayer,\n unifiedProcessManagerLayer,\n );\n\n const managedRuntime = ManagedRuntime.make(runtimeLayer as Layer.Layer<any, any, never>);\n\n // The module scope closes on deactivation/shutdown: dispose the runtime, then tear\n // down the stack's keep-alive slices.\n yield* Effect.addFinalizer(() =>\n Effect.promise(() => managedRuntime.dispose()).pipe(Effect.andThen(layerStack.destroy())),\n );\n\n const processManagerRuntime: Capabilities.ProcessManagerRuntime = {\n runPromise: (effect, options) => managedRuntime.runPromise(effect as Effect.Effect<any, any, any>, options),\n runPromiseExit: (effect, options) =>\n managedRuntime.runPromiseExit(effect as Effect.Effect<any, any, any>, options),\n runFork: (effect, options) => managedRuntime.runFork(effect as Effect.Effect<any, any, any>, options),\n runSync: (effect) => managedRuntime.runSync(effect as Effect.Effect<any, any, any>),\n };\n\n // Publish the manager into the ambient-layer holder so that\n // `ProcessManager.ProcessManagerService` becomes resolvable through the\n // LayerStack alongside the other framework-supplied services.\n processManagerHolder = managedRuntime.runSync(\n Effect.flatMap(ProcessManager.ProcessManagerService, Effect.succeed) as Effect.Effect<\n ProcessManager.Manager,\n never,\n never\n >,\n );\n\n // Resolved in the background rather than on first spawn, so a view that only reads the tree still fills in;\n // after the holder above, which the stack's manager is built over.\n managedRuntime.runFork(unifiedProcessManager.processTree);\n\n // Eagerly extract the operation invoker built by ProcessOperationInvoker.layer, via its own tag so the\n // contributed value carries the full OperationInvoker interface (`invocations`, `pendingFollowups`,\n // `awaitFollowups`, `_invokeCore`) that HistoryTracker requires.\n const operationInvoker: OperationInvoker.OperationInvoker = managedRuntime.runSync(\n Effect.flatMap(ProcessOperationInvoker.Service, Effect.succeed) as Effect.Effect<\n ProcessOperationInvoker.ProcessOperationInvoker,\n never,\n never\n >,\n );\n\n return [\n Capability.contribute(Capabilities.ProcessManagerRuntime, processManagerRuntime),\n Capability.contribute(Capabilities.ServiceResolver, serviceResolver),\n Capability.contribute(Capabilities.ProcessManager, unifiedProcessManager),\n Capability.contribute(Capabilities.OperationInvoker, operationInvoker),\n Capability.contribute(Capabilities.OperationHandlers, handlerSet),\n ];\n }),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-process-manager-capability.mjs","names":["Capability.makeModule","Capability.Service","Plugin.Service","Capabilities.AtomRegistry","Capabilities.LayerSpec","Capabilities.TraceSink","Capabilities.OperationHandler","Capabilities.RemoteTraceMonitor","Capability.contribute","Capabilities.ProcessManagerRuntime","Capabilities.ServiceResolver","Capabilities.ProcessManager","Capabilities.OperationInvoker","Capabilities.OperationHandlers"],"sources":["../../src/plugin-process-manager/process-manager-capability.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Layer from 'effect/Layer';\nimport * as ManagedRuntime from 'effect/ManagedRuntime';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Registry from 'effect/reactivity/AtomRegistry';\nimport * as Scope from 'effect/Scope';\nimport * as Stream from 'effect/Stream';\nimport * as Tracer from 'effect/Tracer';\n\nimport {\n LayerStack,\n ProcessManager,\n ProcessOperationInvoker,\n RemoteOperationInvoker,\n RemoteProcessManager,\n RemoteTraceMonitor,\n UnifiedProcessManager,\n} from '@dxos/compute-runtime';\nimport * as LayerSpec from '@dxos/compute/LayerSpec';\nimport * as OperationHandlerSet from '@dxos/compute/OperationHandlerSet';\nimport * as Process from '@dxos/compute/Process';\nimport * as ServiceResolver from '@dxos/compute/ServiceResolver';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Database } from '@dxos/echo';\nimport * as OtelTracer from '@dxos/effect/OtelTracer';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n// Explicit import so the emitted `.d.ts` references the package via its public\n// alias instead of a relative `node_modules` path (TS2883).\nimport { OperationInvoker } from '@dxos/operation';\n\nimport { Capabilities } from '../common/index.ts';\nimport { Capability, Plugin } from '../core/index.ts';\nimport { layerIdb } from './idb-key-value-store.ts';\n\n//\n// Capability Module\n//\n// Hosts the {@link ProcessManager} runtime for the plugin system.\n//\n// Workflow:\n// 1. Requires {@link Capabilities.LayerSpec} and {@link Capabilities.OperationHandler}\n// contributions from dependency-mode modules.\n// 2. Collects all contributed {@link LayerSpec.LayerSpec}s and builds a\n// {@link LayerStack} whose {@link ServiceResolver} drives process-scoped\n// service resolution; specs contributed later are added to the live stack.\n// 3. Wires a reactive {@link OperationHandlerSet} that tracks\n// {@link Capabilities.OperationHandler} contributions and invalidates its\n// cached merge when new handlers register.\n// 4. Composes the fixed runtime requirements (capability/plugin managers,\n// service resolver, operation invoker, process manager) into a single\n// {@link Layer} and builds a {@link ManagedRuntime} from it.\n// 5. Exposes a disposable-less wrapper as {@link Capabilities.ProcessManagerRuntime}\n// (the plugin system manages its lifecycle).\n//\n\n/**\n * Trace sink over the LIVE contribution list, so a sink contributed after the runtime was built (an\n * on-demand module, like plugin-progress's adapter) still observes writes. Instances are cached per\n * factory, since a sink may hold state across writes and must not be rebuilt underneath itself.\n */\nexport const makeDynamicTraceSink = (\n getFactories: () => readonly Capabilities.TraceSinkFactory[],\n resolver: ServiceResolver.ServiceResolver,\n): Trace.Sink => {\n const instances = new Map<Capabilities.TraceSinkFactory, Trace.Sink>();\n const resolve = (): Trace.Sink[] => {\n const sinks: Trace.Sink[] = [];\n for (const factory of getFactories()) {\n const existing = instances.get(factory);\n if (existing) {\n sinks.push(existing);\n continue;\n }\n try {\n const sink = factory({ resolver });\n instances.set(factory, sink);\n sinks.push(sink);\n } catch (err) {\n // One factory that cannot build must not cost the sinks behind it their messages.\n log.warn('trace sink factory failed', { err });\n }\n }\n return sinks;\n };\n\n // `mergeSinks` per write, for its guarantee that one throwing sink cannot break the chain.\n return { write: (message) => Trace.mergeSinks(resolve()).write(message) };\n};\n\n/**\n * The application's one {@link Process.ManagerService}, as built by the stack. Every built-in and plugin\n * consumer sees the same manager, so a host's remote manager (e.g. EDGE) reaches the app's invoker too.\n */\nconst ProcessManagerSpec = LayerSpec.make(\n {\n affinity: 'application',\n requires: [\n ProcessManager.ProcessManagerService,\n RemoteProcessManager.Service,\n RemoteTraceMonitor.Service,\n Registry.AtomRegistry,\n ],\n provides: [Process.ManagerService],\n },\n () => UnifiedProcessManager.layer,\n);\n\n/**\n * A {@link Process.Manager} that is the stack's {@link Process.ManagerService}, resolved on first use: building it\n * resolves the host's remote manager, which may need services (the client) that are not ready at activation.\n * The process tree reads empty until then.\n */\nexport const fromStack = (\n resolver: ServiceResolver.ServiceResolver,\n scope: Scope.Scope,\n registry: Registry.AtomRegistry,\n): Process.Manager => {\n const resolvedAtom = Atom.make<Process.Manager | undefined>(undefined).pipe(Atom.keepAlive);\n const manager: Effect.Effect<Process.Manager> = Effect.suspend(() => {\n const resolved = registry.get(resolvedAtom);\n return resolved !== undefined\n ? Effect.succeed(resolved)\n : resolver.resolve(Process.ManagerService, {}).pipe(\n Scope.provide(scope),\n Effect.tap((stackManager) => Effect.sync(() => registry.set(resolvedAtom, stackManager))),\n // The framework's own spec provides it, so a failure here is a broken stack, not a missing plugin.\n Effect.orDie,\n );\n });\n\n return {\n processTree: Effect.flatMap(manager, (stackManager) => stackManager.processTree),\n processTreeAtom: Atom.make((get) => {\n const stackManager = get(resolvedAtom);\n return stackManager === undefined ? [] : get(stackManager.processTreeAtom);\n }),\n list: (filter) => Effect.flatMap(manager, (stackManager) => stackManager.list(filter)),\n subscribeToTraceMessages: (filter) =>\n Stream.unwrap(Effect.map(manager, (stackManager) => stackManager.subscribeToTraceMessages(filter))),\n spawn: (definition, options) => Effect.flatMap(manager, (stackManager) => stackManager.spawn(definition, options)),\n handles: (options) => Effect.flatMap(manager, (stackManager) => stackManager.handles(options)),\n attach: (pid, options) => Effect.flatMap(manager, (stackManager) => stackManager.attach(pid, options)),\n };\n};\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const capabilityManager = yield* Capability.Service;\n const pluginManager = yield* Plugin.Service;\n const atomRegistry = yield* Capabilities.AtomRegistry;\n\n const layerSpecContributions = yield* Capabilities.LayerSpec;\n const traceSinkContributions = yield* Capabilities.TraceSink;\n const operationHandlerContributions = yield* Capabilities.OperationHandler;\n const remoteTraceMonitorContributions = yield* Capabilities.RemoteTraceMonitor;\n // Startup soft-ordering makes same-pass providers visible here; specs contributed later (a\n // plugin enabled after boot) join the live stack through the subscription below.\n const layerSpecs = layerSpecContributions.get();\n // Optional swarm-backed remote trace source (DX-1125); first contribution wins, else empty.\n const remoteTraceMonitors = remoteTraceMonitorContributions.get();\n\n log.info('setup process manager', { traceSinks: traceSinkContributions.get().length });\n\n // Forward reference to `ProcessManager.ProcessManagerService`. The runtime\n // that owns the manager depends transitively on `ServiceResolver` (which is\n // built from the `LayerStack` below), so we cannot materialise it before\n // the stack exists. Instead we publish the manager into this holder as\n // soon as the runtime is built, and the ambient layer reads it lazily via\n // `Layer.effect` — slice init only runs once a process actually triggers\n // service resolution, by which point the holder is populated.\n let processManagerHolder: ProcessManager.Manager | undefined;\n\n // Expose the foundational app-framework services through the LayerStack so\n // that operations declaring `services: [Capability.Service]` (and friends)\n // can resolve them via the ServiceResolver. Without this, only consumers\n // sitting on the same ManagedRuntime layer graph can see them — process\n // executions go through ServiceResolver.resolveAll and would fail.\n const ambientLayerSpec = LayerSpec.make(\n {\n affinity: 'application',\n requires: [],\n provides: [Capability.Service, Plugin.Service, Registry.AtomRegistry, ProcessManager.ProcessManagerService],\n },\n () =>\n Layer.mergeAll(\n Layer.succeed(Capability.Service, capabilityManager),\n Layer.succeed(Plugin.Service, pluginManager),\n Layer.succeed(Registry.AtomRegistry, atomRegistry),\n Layer.effect(\n ProcessManager.ProcessManagerService,\n Effect.sync(() => {\n invariant(\n processManagerHolder,\n 'ProcessManagerService accessed before the process-manager runtime was initialised',\n );\n return processManagerHolder;\n }),\n ),\n ),\n );\n\n // Fallbacks for a host with no remote runtime; a plugin spec providing any of these tags (e.g. EDGE's) wins.\n const fallbacks = Effect.runSync(\n Effect.gen(function* () {\n const remoteProcessManager = yield* RemoteProcessManager.Service;\n const remoteOperationInvoker = yield* RemoteOperationInvoker.Service;\n const remoteTraceMonitor = yield* RemoteTraceMonitor.Service;\n return Context.make(RemoteProcessManager.Service, remoteProcessManager).pipe(\n Context.add(RemoteOperationInvoker.Service, remoteOperationInvoker),\n Context.add(RemoteTraceMonitor.Service, remoteTraceMonitor),\n );\n }).pipe(\n Effect.provide(\n Layer.mergeAll(\n RemoteProcessManager.layerNoop,\n RemoteOperationInvoker.layerNoop,\n // Remote ephemeral trace (DX-1125): the first contributed swarm-backed monitor, else no-op.\n remoteTraceMonitors.length > 0\n ? Layer.succeed(RemoteTraceMonitor.Service, remoteTraceMonitors[0])\n : RemoteTraceMonitor.layerNoop,\n ),\n ),\n Effect.provideService(Registry.AtomRegistry, atomRegistry),\n ),\n );\n\n const layerStack = new LayerStack.LayerStack({\n layers: [ambientLayerSpec, ProcessManagerSpec, ...layerSpecs],\n services: fallbacks,\n });\n const serviceResolver = layerStack.getServiceResolver();\n\n // The stack extends built slices in place, so admitting a late spec costs no live service.\n const rejectedLayerSpecs = new WeakSet<LayerSpec.LayerSpec>();\n const admitLayerSpecs = (specs: readonly LayerSpec.LayerSpec[]) => {\n const candidates = specs.filter((spec) => !rejectedLayerSpecs.has(spec));\n try {\n layerStack.addLayers(candidates);\n } catch {\n // Admitted one at a time so a single bad spec does not keep the others out.\n for (const spec of candidates) {\n try {\n layerStack.addLayers([spec]);\n } catch (err) {\n rejectedLayerSpecs.add(spec);\n log.error('LayerSpec rejected', { provides: spec.provides.map((tag) => tag.key), err });\n }\n }\n }\n };\n const cancelLayerSpecWatch = atomRegistry.subscribe(layerSpecContributions.atom, admitLayerSpecs);\n yield* Effect.addFinalizer(() => Effect.sync(cancelLayerSpecWatch));\n // Covers a contribution landing between the snapshot above and the subscription.\n admitLayerSpecs(layerSpecContributions.get());\n\n // Handler sets register eagerly at startup (keyed sets defer only handler BODIES), so the\n // reactive view over contributions is complete at boot — no demand pull on a miss.\n const handlerSet = OperationHandlerSet.reactive(atomRegistry, operationHandlerContributions.atom);\n\n const mergedTraceSink = makeDynamicTraceSink(() => traceSinkContributions.get(), serviceResolver);\n\n // Base services required by ProcessManager and the operation invoker.\n // Sensible defaults are provided here; plugins that want alternative\n // implementations (e.g. persistent KV store) can contribute their own LayerSpec entries\n // against the ServiceResolver.\n const baseLayer = Layer.mergeAll(\n Layer.succeed(Capability.Service, capabilityManager),\n Layer.succeed(Plugin.Service, pluginManager),\n Layer.succeed(Registry.AtomRegistry, atomRegistry),\n Layer.succeed(ServiceResolver.ServiceResolver, serviceResolver),\n OperationHandlerSet.provide(handlerSet),\n layerIdb,\n Layer.succeed(Trace.TraceSink, mergedTraceSink),\n // Over the OTel global provider, a proxy that no-ops until one is registered, so this is\n // installed whether or not observability exists.\n Layer.succeed(Tracer.Tracer, OtelTracer.makeGlobal('@dxos/app-framework/process-manager')),\n );\n\n const processManagerLayer = ProcessManager.layer({ runtimeName: Trace.CommonRuntimeName.local }).pipe(\n Layer.provide(baseLayer),\n );\n // Holds the stack's application slice that the manager resolves from, for the module's lifetime.\n const stackScope = yield* Scope.make();\n yield* Effect.addFinalizer(() => Scope.close(stackScope, Exit.void));\n const unifiedProcessManager = fromStack(serviceResolver, stackScope, atomRegistry);\n const unifiedProcessManagerLayer = Layer.succeed(Process.ManagerService, unifiedProcessManager);\n // The stack's remote invoker, resolved per call for the same reason as the manager: it may need the client.\n const remoteOperationInvoker: RemoteOperationInvoker.Invoker = {\n invoke: (ctx, deployedId, input, options) =>\n serviceResolver.resolve(RemoteOperationInvoker.Service, {}).pipe(\n Scope.provide(stackScope),\n Effect.orDie,\n Effect.flatMap((remote) => remote.invoke(ctx, deployedId, input, options)),\n ),\n };\n const operationInvokerLayer = ProcessOperationInvoker.layer.pipe(\n // Operations invoked through the app's own invoker are the person's actions, from a menu, dialog or shortcut.\n Layer.provide(\n Layer.mergeAll(\n unifiedProcessManagerLayer,\n baseLayer,\n Layer.succeed(Database.Origin, 'user'),\n Layer.succeed(RemoteOperationInvoker.Service, remoteOperationInvoker),\n ),\n ),\n );\n\n const runtimeLayer = Layer.mergeAll(\n baseLayer,\n processManagerLayer,\n operationInvokerLayer,\n unifiedProcessManagerLayer,\n );\n\n const managedRuntime = ManagedRuntime.make(runtimeLayer as Layer.Layer<any, any, never>);\n\n // The module scope closes on deactivation/shutdown: dispose the runtime, then tear\n // down the stack's keep-alive slices.\n yield* Effect.addFinalizer(() =>\n Effect.promise(() => managedRuntime.dispose()).pipe(Effect.andThen(layerStack.destroy())),\n );\n\n const processManagerRuntime: Capabilities.ProcessManagerRuntime = {\n runPromise: (effect, options) => managedRuntime.runPromise(effect as Effect.Effect<any, any, any>, options),\n runPromiseExit: (effect, options) =>\n managedRuntime.runPromiseExit(effect as Effect.Effect<any, any, any>, options),\n runFork: (effect, options) => managedRuntime.runFork(effect as Effect.Effect<any, any, any>, options),\n runSync: (effect) => managedRuntime.runSync(effect as Effect.Effect<any, any, any>),\n };\n\n // Publish the manager into the ambient-layer holder so that\n // `ProcessManager.ProcessManagerService` becomes resolvable through the\n // LayerStack alongside the other framework-supplied services.\n processManagerHolder = managedRuntime.runSync(\n Effect.flatMap(ProcessManager.ProcessManagerService, Effect.succeed) as Effect.Effect<\n ProcessManager.Manager,\n never,\n never\n >,\n );\n\n // Resolved in the background rather than on first spawn, so a view that only reads the tree still fills in;\n // after the holder above, which the stack's manager is built over.\n managedRuntime.runFork(unifiedProcessManager.processTree);\n\n // Eagerly extract the operation invoker built by ProcessOperationInvoker.layer, via its own tag so the\n // contributed value carries the full OperationInvoker interface (`invocations`, `pendingFollowups`,\n // `awaitFollowups`, `_invokeCore`) that HistoryTracker requires.\n const operationInvoker: OperationInvoker.OperationInvoker = managedRuntime.runSync(\n Effect.flatMap(ProcessOperationInvoker.Service, Effect.succeed) as Effect.Effect<\n ProcessOperationInvoker.ProcessOperationInvoker,\n never,\n never\n >,\n );\n\n return [\n Capability.contribute(Capabilities.ProcessManagerRuntime, processManagerRuntime),\n Capability.contribute(Capabilities.ServiceResolver, serviceResolver),\n Capability.contribute(Capabilities.ProcessManager, unifiedProcessManager),\n Capability.contribute(Capabilities.OperationInvoker, operationInvoker),\n Capability.contribute(Capabilities.OperationHandlers, handlerSet),\n ];\n }),\n);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-registry.mjs","names":[],"sources":["../../src/core/plugin-manager/errors.ts","../../src/core/edge-registry-plugin-provider.ts","../../src/core/registry.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError } from '@dxos/errors';\n\n/** Plugin manager operation failed. The underlying failure, where there is one, is the `cause`. */\nexport class PluginManagerError extends BaseError.extend('PluginManagerError', 'Plugin manager operation failed.') {}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Context } from '@dxos/context';\nimport { type EdgeHttpClient } from '@dxos/edge-client';\nimport { BaseError } from '@dxos/errors';\nimport { type PluginView } from '@dxos/protocols';\n\nimport { PluginManagerError } from './plugin-manager/errors.ts';\nimport type * as Plugin from './plugin.ts';\nimport type * as Registry from './registry.ts';\n\n/**\n * Maps a wire-format `PluginView` (from `@dxos/protocols`) to a runtime `Plugin.Meta`: the nested\n * `profile` (the view's profile plus the resolved provenance `author`) and the latest `release`.\n *\n * This is the one translation seam between the nested wire view and the resolved runtime manifest.\n * `profile.key` is the composer plugin id — a valid NSID — so `DXN.make(profile.key, release.version)`\n * reconstructs the canonical plugin DXN.\n */\nconst toRegistryPlugin = (entry: PluginView): Plugin.Meta | null => {\n if (!entry.releases?.length) {\n return null;\n }\n const latestRelease = entry.releases.find((release) => release.version === entry.latestVersion) ?? entry.releases[0];\n return {\n profile: {\n ...entry.profile,\n // Provenance: the verified publisher is the author (handle preferred, DID fallback).\n author: entry.handle ?? entry.did,\n },\n release: latestRelease,\n };\n};\n\n/**\n * Implements `Registry.PluginProvider` against the Edge `/registry` HTTP endpoints.\n *\n * Lives in app-framework (rather than the more obvious home of `@dxos/edge-client`)\n * because edge-client doesn't depend on app-framework; siting the implementation\n * here keeps the dependency arrow `app-framework → edge-client` and avoids a\n * cycle. The class needs only the public `EdgeHttpClient` type, so a one-way\n * type-import is enough.\n *\n * `listVersions` is served directly from the `releases` array inlined on each\n * entry — no separate endpoint is needed.\n */\n/** The plugin registry behind EDGE did not answer, or answered with something unusable. */\nexport class RegistryError extends BaseError.extend('RegistryError', 'Plugin registry request failed.') {}\n\nexport type EdgeRegistryPluginProviderOptions = {\n /**\n * `public` (default) lists the curator-verified AT Protocol catalog; `private` lists the plugins the\n * client's identity published privately, so it needs a client with an identity set.\n */\n catalog?: 'public' | 'private';\n};\n\n/** The slice of {@link EdgeHttpClient} the provider calls. */\nexport type RegistryHttpClient = Pick<EdgeHttpClient, 'getRegistryPlugins' | 'getPrivateRegistryPlugins'>;\n\nexport class EdgeRegistryPluginProvider implements Registry.PluginProvider {\n // Cached on first load so getPlugin/listVersions can resolve without re-fetching.\n #cachedPlugins: readonly Plugin.Meta[] = [];\n #cachedEntries: readonly PluginView[] = [];\n readonly #catalog: 'public' | 'private';\n /** Bumped per {@link listPlugins}, so a slower earlier response cannot overwrite the cache a later one wrote. */\n #request = 0;\n\n constructor(\n private readonly _client: RegistryHttpClient,\n { catalog = 'public' }: EdgeRegistryPluginProviderOptions = {},\n ) {\n this.#catalog = catalog;\n }\n\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], RegistryError> {\n return Effect.suspend(() => {\n const request = ++this.#request;\n return Effect.tryPromise({\n try: () =>\n this.#catalog === 'private'\n ? this._client.getPrivateRegistryPlugins(Context.default())\n : this._client.getRegistryPlugins(Context.default()),\n catch: RegistryError.wrap(),\n }).pipe(\n Effect.map((body) => {\n const plugins = body.plugins.map(toRegistryPlugin).filter((entry): entry is Plugin.Meta => entry !== null);\n if (request === this.#request) {\n this.#cachedEntries = body.plugins;\n this.#cachedPlugins = plugins;\n }\n return plugins;\n }),\n );\n });\n }\n\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error> {\n const entry = this.#cachedEntries.find((candidate) => candidate.profile.key === id);\n if (!entry) {\n return Effect.fail(new PluginManagerError({ message: `Plugin not found in catalog: ${id}` }));\n }\n // Releases are already `PluginRelease`-shaped on the wire view; serve them directly.\n return Effect.succeed(entry.releases);\n }\n\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error> {\n const plugin = this.#cachedPlugins.find((candidate) => candidate.profile.key === id);\n if (!plugin) {\n return Effect.fail(new PluginManagerError({ message: `Plugin not found in catalog: ${id}` }));\n }\n if (version && version !== plugin.release?.version) {\n return Effect.fail(\n new PluginManagerError({\n message: `Version ${version} not available for ${id}; only ${plugin.release?.version} is cached`,\n }),\n );\n }\n return Effect.succeed(plugin);\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Atom from 'effect/reactivity/Atom';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\n\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\n\nimport { PluginManagerError } from './plugin-manager/errors.ts';\nimport type * as Plugin from './plugin.ts';\n\n/**\n * A registry catalog entry is a {@link Plugin.Meta} (profile + the latest release), the same\n * runtime type bundled plugins use. {@link PluginProvider} implementations (e.g.\n * {@link EdgeRegistryPluginProvider}) map their wire-format entries (`PluginView`) to this shape.\n * A single installable version is a {@link Plugin.Release}.\n */\n\n/**\n * Abstraction over the plugin registry catalog backend.\n * Implementations may call Edge, a local cache, or a stub.\n *\n * The interface itself is transport-agnostic; concrete implementations live\n * alongside it in this package (see {@link EdgeRegistryPluginProvider}) so we\n * avoid `app-framework ↔ edge-client` cycles, but they only need to satisfy\n * this shape — callers can substitute their own.\n */\nexport interface PluginProvider {\n /**\n * Returns all registry plugins (latest version of each).\n */\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], Error>;\n\n /**\n * Returns all known versions of a plugin, identified by its composer plugin id (NSID).\n * Ordered newest-first. Must return at least one entry.\n */\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error>;\n\n /**\n * Returns a single plugin entry for the given id.\n * If `version` is omitted, returns the latest.\n * Fails if the plugin or version is not found.\n */\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error>;\n}\n\n/**\n * Atomic snapshot of the registry catalog as observed by the host.\n * Exposed via {@link Manager.plugins} so consumers can subscribe reactively\n * (loading state, list contents, last error).\n */\nexport type PluginsState = {\n entries: readonly Plugin.Meta[];\n loading: boolean;\n error: Error | null;\n};\n\n/**\n * Default no-op provider used when no `PluginProvider` is supplied at construction\n * time (e.g. tests, stories, environments without an Edge connection).\n *\n * `listPlugins` returns an empty list (so `Manager.plugins` settles to a stable\n * empty state). `listVersions` and `getPlugin` fail with an explicit error so\n * callers know they need to configure a real provider.\n */\nconst NULL_PROVIDER: PluginProvider = {\n listPlugins: () => Effect.succeed([] as readonly Plugin.Meta[]),\n listVersions: () => Effect.fail(new PluginManagerError({ message: 'No plugin registry provider configured' })),\n getPlugin: () => Effect.fail(new PluginManagerError({ message: 'No plugin registry provider configured' })),\n};\n\n/** Concatenates provider lists, keeping the first entry of each plugin key. */\nconst mergeByKey = (lists: readonly (readonly Plugin.Meta[])[]): Plugin.Meta[] => {\n const byKey = new Map<string, Plugin.Meta>();\n for (const entry of lists.flat()) {\n if (!byKey.has(entry.profile.key)) {\n byKey.set(entry.profile.key, entry);\n }\n }\n return [...byKey.values()];\n};\n\n/**\n * Owns the cached registry catalog state and forwards `listVersions` / `getPlugin`\n * calls to its {@link PluginProvider}s. Lives on the `PluginManager`\n * (as `manager.pluginRegistry`) so consumers can reach it through the manager\n * without a separate capability lookup.\n *\n * The catalog is the union of every provider's plugins: the one given at construction (the public\n * registry) and any added later with {@link addProvider}, such as the signed-in user's private\n * plugins, which cannot be listed until an identity exists. A key listed by more than one provider\n * resolves to the earliest.\n *\n * On construction, and again on {@link addProvider} or {@link refresh}, the manager lists every\n * provider and writes the merged result into the {@link plugins} atom. Subscribers see\n * `loading: true` during the fetch and `loading: false` once it settles.\n */\nexport class Manager {\n readonly plugins: Atom.Writable<PluginsState>;\n readonly #providers: PluginProvider[];\n readonly #atomRegistry: AtomRegistry.AtomRegistry;\n /** Bumped per {@link refresh}, so a slower earlier reload cannot overwrite a later one. */\n #generation = 0;\n\n constructor(provider: PluginProvider | undefined, atomRegistry: AtomRegistry.AtomRegistry) {\n this.#providers = provider ? [provider] : [];\n this.#atomRegistry = atomRegistry;\n this.plugins = Atom.make<PluginsState>({ entries: [], loading: provider !== undefined, error: null }).pipe(\n Atom.keepAlive,\n );\n if (provider !== undefined) {\n this.refresh();\n }\n }\n\n /** Adds a provider whose plugins join the catalog, and reloads it. */\n addProvider(provider: PluginProvider): void {\n this.#providers.push(provider);\n this.refresh();\n }\n\n /** Removes a provider added with {@link addProvider}, dropping its plugins from the catalog. */\n removeProvider(provider: PluginProvider): void {\n const index = this.#providers.indexOf(provider);\n if (index === -1) {\n return;\n }\n this.#providers.splice(index, 1);\n this.refresh();\n }\n\n /**\n * Reloads the catalog from every provider, in the background. A provider that fails contributes\n * nothing and its error is surfaced on the atom, so one unreachable backend does not hide the others.\n */\n refresh(): void {\n const generation = ++this.#generation;\n const current = this.#atomRegistry.get(this.plugins);\n this.#atomRegistry.set(this.plugins, { ...current, loading: true });\n void EffectEx.runAndForwardErrors(\n Effect.forEach(this.#providers, (provider) => Effect.result(provider.listPlugins())).pipe(\n Effect.map((results) => {\n if (generation !== this.#generation) {\n return;\n }\n const lists: (readonly Plugin.Meta[])[] = [];\n let error: Error | null = null;\n for (const result of results) {\n if (result._tag === 'Failure') {\n log.catch(result.failure);\n error ??= result.failure;\n } else {\n lists.push(result.success);\n }\n }\n this.#atomRegistry.set(this.plugins, { entries: mergeByKey(lists), loading: false, error });\n }),\n ),\n );\n }\n\n /** Lists every provider's plugins, merged as the catalog is. */\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], Error> {\n return Effect.forEach(this.#providers, (provider) => provider.listPlugins()).pipe(Effect.map(mergeByKey));\n }\n\n /** Versions of `id` from the first provider that has it. */\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error> {\n return this.#firstOf((provider) => provider.listVersions(id));\n }\n\n /** The entry for `id` from the first provider that has it. */\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error> {\n return this.#firstOf((provider) => provider.getPlugin(id, version));\n }\n\n #firstOf<A>(lookup: (provider: PluginProvider) => Effect.Effect<A, Error>): Effect.Effect<A, Error> {\n const [first, ...rest] = this.#providers.length > 0 ? this.#providers : [NULL_PROVIDER];\n return rest.reduce((effect, provider) => Effect.catch(effect, () => lookup(provider)), lookup(first));\n }\n}\n\nexport {\n EdgeRegistryPluginProvider as EdgePluginProvider,\n type EdgeRegistryPluginProviderOptions as EdgePluginProviderOptions,\n} from './edge-registry-plugin-provider.ts';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-registry.mjs","names":["__dxlog_file"],"sources":["../../src/core/plugin-manager/errors.ts","../../src/core/edge-registry-plugin-provider.ts","../../src/core/registry.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError } from '@dxos/errors';\n\n/** Plugin manager operation failed. The underlying failure, where there is one, is the `cause`. */\nexport class PluginManagerError extends BaseError.extend('PluginManagerError', 'Plugin manager operation failed.') {}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Context } from '@dxos/context';\nimport { type EdgeHttpClient } from '@dxos/edge-client';\nimport { BaseError } from '@dxos/errors';\nimport { type PluginView } from '@dxos/protocols';\n\nimport { PluginManagerError } from './plugin-manager/errors.ts';\nimport type * as Plugin from './plugin.ts';\nimport type * as Registry from './registry.ts';\n\n/**\n * Maps a wire-format `PluginView` (from `@dxos/protocols`) to a runtime `Plugin.Meta`: the nested\n * `profile` (the view's profile plus the resolved provenance `author`) and the latest `release`.\n *\n * This is the one translation seam between the nested wire view and the resolved runtime manifest.\n * `profile.key` is the composer plugin id — a valid NSID — so `DXN.make(profile.key, release.version)`\n * reconstructs the canonical plugin DXN.\n */\nconst toRegistryPlugin = (entry: PluginView): Plugin.Meta | null => {\n if (!entry.releases?.length) {\n return null;\n }\n const latestRelease = entry.releases.find((release) => release.version === entry.latestVersion) ?? entry.releases[0];\n return {\n profile: {\n ...entry.profile,\n // Provenance: the verified publisher is the author (handle preferred, DID fallback).\n author: entry.handle ?? entry.did,\n },\n release: latestRelease,\n };\n};\n\n/**\n * Implements `Registry.PluginProvider` against the Edge `/registry` HTTP endpoints.\n *\n * Lives in app-framework (rather than the more obvious home of `@dxos/edge-client`)\n * because edge-client doesn't depend on app-framework; siting the implementation\n * here keeps the dependency arrow `app-framework → edge-client` and avoids a\n * cycle. The class needs only the public `EdgeHttpClient` type, so a one-way\n * type-import is enough.\n *\n * `listVersions` is served directly from the `releases` array inlined on each\n * entry — no separate endpoint is needed.\n */\n/** The plugin registry behind EDGE did not answer, or answered with something unusable. */\nexport class RegistryError extends BaseError.extend('RegistryError', 'Plugin registry request failed.') {}\n\nexport type EdgeRegistryPluginProviderOptions = {\n /**\n * `public` (default) lists the curator-verified AT Protocol catalog; `private` lists the plugins the\n * client's identity published privately, so it needs a client with an identity set.\n */\n catalog?: 'public' | 'private';\n};\n\n/** The slice of {@link EdgeHttpClient} the provider calls. */\nexport type RegistryHttpClient = Pick<EdgeHttpClient, 'getRegistryPlugins' | 'getPrivateRegistryPlugins'>;\n\nexport class EdgeRegistryPluginProvider implements Registry.PluginProvider {\n // Cached on first load so getPlugin/listVersions can resolve without re-fetching.\n #cachedPlugins: readonly Plugin.Meta[] = [];\n #cachedEntries: readonly PluginView[] = [];\n readonly #catalog: 'public' | 'private';\n /** Bumped per {@link listPlugins}, so a slower earlier response cannot overwrite the cache a later one wrote. */\n #request = 0;\n\n constructor(\n private readonly _client: RegistryHttpClient,\n { catalog = 'public' }: EdgeRegistryPluginProviderOptions = {},\n ) {\n this.#catalog = catalog;\n }\n\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], RegistryError> {\n return Effect.suspend(() => {\n const request = ++this.#request;\n return Effect.tryPromise({\n try: () =>\n this.#catalog === 'private'\n ? this._client.getPrivateRegistryPlugins(Context.default())\n : this._client.getRegistryPlugins(Context.default()),\n catch: RegistryError.wrap(),\n }).pipe(\n Effect.map((body) => {\n const plugins = body.plugins.map(toRegistryPlugin).filter((entry): entry is Plugin.Meta => entry !== null);\n if (request === this.#request) {\n this.#cachedEntries = body.plugins;\n this.#cachedPlugins = plugins;\n }\n return plugins;\n }),\n );\n });\n }\n\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error> {\n const entry = this.#cachedEntries.find((candidate) => candidate.profile.key === id);\n if (!entry) {\n return Effect.fail(new PluginManagerError({ message: `Plugin not found in catalog: ${id}` }));\n }\n // Releases are already `PluginRelease`-shaped on the wire view; serve them directly.\n return Effect.succeed(entry.releases);\n }\n\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error> {\n const plugin = this.#cachedPlugins.find((candidate) => candidate.profile.key === id);\n if (!plugin) {\n return Effect.fail(new PluginManagerError({ message: `Plugin not found in catalog: ${id}` }));\n }\n if (version && version !== plugin.release?.version) {\n return Effect.fail(\n new PluginManagerError({\n message: `Version ${version} not available for ${id}; only ${plugin.release?.version} is cached`,\n }),\n );\n }\n return Effect.succeed(plugin);\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Atom from 'effect/reactivity/Atom';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\n\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\n\nimport { PluginManagerError } from './plugin-manager/errors.ts';\nimport type * as Plugin from './plugin.ts';\n\n/**\n * A registry catalog entry is a {@link Plugin.Meta} (profile + the latest release), the same\n * runtime type bundled plugins use. {@link PluginProvider} implementations (e.g.\n * {@link EdgeRegistryPluginProvider}) map their wire-format entries (`PluginView`) to this shape.\n * A single installable version is a {@link Plugin.Release}.\n */\n\n/**\n * Abstraction over the plugin registry catalog backend.\n * Implementations may call Edge, a local cache, or a stub.\n *\n * The interface itself is transport-agnostic; concrete implementations live\n * alongside it in this package (see {@link EdgeRegistryPluginProvider}) so we\n * avoid `app-framework ↔ edge-client` cycles, but they only need to satisfy\n * this shape — callers can substitute their own.\n */\nexport interface PluginProvider {\n /**\n * Returns all registry plugins (latest version of each).\n */\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], Error>;\n\n /**\n * Returns all known versions of a plugin, identified by its composer plugin id (NSID).\n * Ordered newest-first. Must return at least one entry.\n */\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error>;\n\n /**\n * Returns a single plugin entry for the given id.\n * If `version` is omitted, returns the latest.\n * Fails if the plugin or version is not found.\n */\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error>;\n}\n\n/**\n * Atomic snapshot of the registry catalog as observed by the host.\n * Exposed via {@link Manager.plugins} so consumers can subscribe reactively\n * (loading state, list contents, last error).\n */\nexport type PluginsState = {\n entries: readonly Plugin.Meta[];\n loading: boolean;\n error: Error | null;\n};\n\n/**\n * Default no-op provider used when no `PluginProvider` is supplied at construction\n * time (e.g. tests, stories, environments without an Edge connection).\n *\n * `listPlugins` returns an empty list (so `Manager.plugins` settles to a stable\n * empty state). `listVersions` and `getPlugin` fail with an explicit error so\n * callers know they need to configure a real provider.\n */\nconst NULL_PROVIDER: PluginProvider = {\n listPlugins: () => Effect.succeed([] as readonly Plugin.Meta[]),\n listVersions: () => Effect.fail(new PluginManagerError({ message: 'No plugin registry provider configured' })),\n getPlugin: () => Effect.fail(new PluginManagerError({ message: 'No plugin registry provider configured' })),\n};\n\n/** Concatenates provider lists, keeping the first entry of each plugin key. */\nconst mergeByKey = (lists: readonly (readonly Plugin.Meta[])[]): Plugin.Meta[] => {\n const byKey = new Map<string, Plugin.Meta>();\n for (const entry of lists.flat()) {\n if (!byKey.has(entry.profile.key)) {\n byKey.set(entry.profile.key, entry);\n }\n }\n return [...byKey.values()];\n};\n\n/**\n * Owns the cached registry catalog state and forwards `listVersions` / `getPlugin`\n * calls to its {@link PluginProvider}s. Lives on the `PluginManager`\n * (as `manager.pluginRegistry`) so consumers can reach it through the manager\n * without a separate capability lookup.\n *\n * The catalog is the union of every provider's plugins: the one given at construction (the public\n * registry) and any added later with {@link addProvider}, such as the signed-in user's private\n * plugins, which cannot be listed until an identity exists. A key listed by more than one provider\n * resolves to the earliest.\n *\n * On construction, and again on {@link addProvider} or {@link refresh}, the manager lists every\n * provider and writes the merged result into the {@link plugins} atom. Subscribers see\n * `loading: true` during the fetch and `loading: false` once it settles.\n */\nexport class Manager {\n readonly plugins: Atom.Writable<PluginsState>;\n readonly #providers: PluginProvider[];\n readonly #atomRegistry: AtomRegistry.AtomRegistry;\n /** Bumped per {@link refresh}, so a slower earlier reload cannot overwrite a later one. */\n #generation = 0;\n\n constructor(provider: PluginProvider | undefined, atomRegistry: AtomRegistry.AtomRegistry) {\n this.#providers = provider ? [provider] : [];\n this.#atomRegistry = atomRegistry;\n this.plugins = Atom.make<PluginsState>({ entries: [], loading: provider !== undefined, error: null }).pipe(\n Atom.keepAlive,\n );\n if (provider !== undefined) {\n this.refresh();\n }\n }\n\n /** Adds a provider whose plugins join the catalog, and reloads it. */\n addProvider(provider: PluginProvider): void {\n this.#providers.push(provider);\n this.refresh();\n }\n\n /** Removes a provider added with {@link addProvider}, dropping its plugins from the catalog. */\n removeProvider(provider: PluginProvider): void {\n const index = this.#providers.indexOf(provider);\n if (index === -1) {\n return;\n }\n this.#providers.splice(index, 1);\n this.refresh();\n }\n\n /**\n * Reloads the catalog from every provider, in the background. A provider that fails contributes\n * nothing and its error is surfaced on the atom, so one unreachable backend does not hide the others.\n */\n refresh(): void {\n const generation = ++this.#generation;\n const current = this.#atomRegistry.get(this.plugins);\n this.#atomRegistry.set(this.plugins, { ...current, loading: true });\n void EffectEx.runAndForwardErrors(\n Effect.forEach(this.#providers, (provider) => Effect.result(provider.listPlugins())).pipe(\n Effect.map((results) => {\n if (generation !== this.#generation) {\n return;\n }\n const lists: (readonly Plugin.Meta[])[] = [];\n let error: Error | null = null;\n for (const result of results) {\n if (result._tag === 'Failure') {\n log.catch(result.failure);\n error ??= result.failure;\n } else {\n lists.push(result.success);\n }\n }\n this.#atomRegistry.set(this.plugins, { entries: mergeByKey(lists), loading: false, error });\n }),\n ),\n );\n }\n\n /** Lists every provider's plugins, merged as the catalog is. */\n listPlugins(): Effect.Effect<readonly Plugin.Meta[], Error> {\n return Effect.forEach(this.#providers, (provider) => provider.listPlugins()).pipe(Effect.map(mergeByKey));\n }\n\n /** Versions of `id` from the first provider that has it. */\n listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error> {\n return this.#firstOf((provider) => provider.listVersions(id));\n }\n\n /** The entry for `id` from the first provider that has it. */\n getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error> {\n return this.#firstOf((provider) => provider.getPlugin(id, version));\n }\n\n #firstOf<A>(lookup: (provider: PluginProvider) => Effect.Effect<A, Error>): Effect.Effect<A, Error> {\n const [first, ...rest] = this.#providers.length > 0 ? this.#providers : [NULL_PROVIDER];\n return rest.reduce((effect, provider) => Effect.catch(effect, () => lookup(provider)), lookup(first));\n }\n}\n\nexport {\n EdgeRegistryPluginProvider as EdgePluginProvider,\n type EdgeRegistryPluginProviderOptions as EdgePluginProviderOptions,\n} from './edge-registry-plugin-provider.ts';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
//#region src/plugin-process-manager/history/undo-mapping.ts
|
|
3
|
+
var undo_mapping_exports = /* @__PURE__ */ __exportAll({
|
|
4
|
+
make: () => make,
|
|
5
|
+
resolveMessage: () => resolveMessage
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Creates a type-safe undo mapping.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // Static message
|
|
13
|
+
* const mapping = UndoMapping.make({
|
|
14
|
+
* operation: DeleteThread,
|
|
15
|
+
* inverse: RestoreThread,
|
|
16
|
+
* deriveContext: (input, output) => ({
|
|
17
|
+
* thread: output.thread,
|
|
18
|
+
* anchor: output.anchor,
|
|
19
|
+
* }),
|
|
20
|
+
* message: ['thread deleted label', { ns: '@dxos/plugin-thread' }],
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Dynamic message based on input/output
|
|
24
|
+
* const mapping = UndoMapping.make({
|
|
25
|
+
* operation: RemoveObjects,
|
|
26
|
+
* inverse: RestoreObjects,
|
|
27
|
+
* deriveContext: (_input, output) => output,
|
|
28
|
+
* message: (input, _output) =>
|
|
29
|
+
* input.objects.length === 1
|
|
30
|
+
* ? ['object deleted label', { ns: getTypename(input.objects[0]) }]
|
|
31
|
+
* : ['objects deleted label', { ns: '@dxos/plugin-space' }],
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
var make = (props) => props;
|
|
36
|
+
/**
|
|
37
|
+
* Resolves a message provider to a Label.
|
|
38
|
+
*/
|
|
39
|
+
var resolveMessage = (message, input, output) => {
|
|
40
|
+
if (message === void 0) return;
|
|
41
|
+
if (typeof message === "function") return message(input, output);
|
|
42
|
+
return message;
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { resolveMessage as n, undo_mapping_exports as r, make as t };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=chunk-undo-mapping.mjs.map
|
package/dist/lib/{plugin-process-manager/history/undo-mapping.mjs.map → chunk-undo-mapping.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"undo-mapping.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"chunk-undo-mapping.mjs","names":[],"sources":["../../src/plugin-process-manager/history/undo-mapping.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Operation from '@dxos/compute/Operation';\n\n/**\n * Label type for translatable text (canonical definition in @dxos/app-toolkit).\n */\ntype Label = string | [string, { ns: string | readonly string[]; count?: number; defaultValue?: string }];\n\n/**\n * Extract the input type from an OperationDefinition.\n */\nexport type InputOf<T> = T extends Operation.Definition<infer I, any> ? I : never;\n\n/**\n * Extract the output type from an OperationDefinition.\n */\nexport type OutputOf<T> = T extends Operation.Definition<any, infer O> ? O : never;\n\n/**\n * Message provider for undo toast.\n * Can be a static Label or a function that derives the message from input/output.\n */\nexport type MessageProvider<Op extends Operation.Definition<any, any>> =\n | Label\n | ((input: InputOf<Op>, output: OutputOf<Op>) => Label);\n\n/**\n * Undo mapping that links a forward operation to its inverse.\n * Type parameters ensure deriveContext has correctly typed arguments.\n *\n * @template Op - The forward operation definition type.\n * @template Inv - The inverse operation definition type.\n */\nexport interface UndoMapping<\n Op extends Operation.Definition<any, any> = Operation.Definition<any, any>,\n Inv extends Operation.Definition<any, any> = Operation.Definition<any, any>,\n> {\n /** The forward operation. */\n readonly operation: Op;\n\n /** The inverse operation to invoke for undo. */\n readonly inverse: Inv;\n\n /**\n * Derives the input for the inverse operation from the forward operation's input and output.\n * @param input - The input that was passed to the forward operation.\n * @param output - The output that was returned by the forward operation.\n * @returns The input to pass to the inverse operation, or undefined to indicate the operation is not undoable.\n */\n readonly deriveContext: (input: InputOf<Op>, output: OutputOf<Op>) => InputOf<Inv> | undefined;\n\n /**\n * Optional message to show in the undo toast.\n * Can be a static Label or a function that derives the message from input/output.\n */\n readonly message?: MessageProvider<Op>;\n}\n\n/**\n * Props for creating an UndoMapping.\n */\nexport interface UndoMappingProps<\n Op extends Operation.Definition<any, any>,\n Inv extends Operation.Definition<any, any>,\n> {\n /** The forward operation. */\n operation: Op;\n\n /** The inverse operation to invoke for undo. */\n inverse: Inv;\n\n /**\n * Derives the input for the inverse operation from the forward operation's input and output.\n * Return undefined to indicate the operation is not undoable.\n */\n deriveContext: (input: InputOf<Op>, output: OutputOf<Op>) => InputOf<Inv> | undefined;\n\n /**\n * Optional message to show in the undo toast.\n * Can be a static Label or a function that derives the message from input/output.\n */\n message?: MessageProvider<Op>;\n}\n\n/**\n * Creates a type-safe undo mapping.\n *\n * @example\n * ```ts\n * // Static message\n * const mapping = UndoMapping.make({\n * operation: DeleteThread,\n * inverse: RestoreThread,\n * deriveContext: (input, output) => ({\n * thread: output.thread,\n * anchor: output.anchor,\n * }),\n * message: ['thread deleted label', { ns: '@dxos/plugin-thread' }],\n * });\n *\n * // Dynamic message based on input/output\n * const mapping = UndoMapping.make({\n * operation: RemoveObjects,\n * inverse: RestoreObjects,\n * deriveContext: (_input, output) => output,\n * message: (input, _output) =>\n * input.objects.length === 1\n * ? ['object deleted label', { ns: getTypename(input.objects[0]) }]\n * : ['objects deleted label', { ns: '@dxos/plugin-space' }],\n * });\n * ```\n */\nexport const make = <Op extends Operation.Definition<any, any>, Inv extends Operation.Definition<any, any>>(\n props: UndoMappingProps<Op, Inv>,\n): UndoMapping<Op, Inv> => props;\n\n/**\n * Resolves a message provider to a Label.\n */\nexport const resolveMessage = <Op extends Operation.Definition<any, any>>(\n message: MessageProvider<Op> | undefined,\n input: InputOf<Op>,\n output: OutputOf<Op>,\n): Label | undefined => {\n if (message === undefined) {\n return undefined;\n }\n if (typeof message === 'function') {\n return message(input, output);\n }\n return message;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,IAAa,QACX,UACyB;;;;AAK3B,IAAa,kBACX,SACA,OACA,WACsB;CACtB,IAAI,YAAY,KAAA,GACd;CAEF,IAAI,OAAO,YAAY,YACrB,OAAO,QAAQ,OAAO,MAAM;CAE9B,OAAO;AACT"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
//#region src/plugin-process-manager/history/undo-registry.ts
|
|
3
|
+
var undo_registry_exports = /* @__PURE__ */ __exportAll({ make: () => make });
|
|
4
|
+
/**
|
|
5
|
+
* Creates an UndoRegistry that looks up inverse operations.
|
|
6
|
+
*/
|
|
7
|
+
var make = (getMappings) => {
|
|
8
|
+
const lookup = (operation) => {
|
|
9
|
+
const mapping = getMappings().find((m) => m.operation.meta.key === operation.meta.key);
|
|
10
|
+
if (!mapping) return;
|
|
11
|
+
return {
|
|
12
|
+
inverse: mapping.inverse,
|
|
13
|
+
deriveContext: mapping.deriveContext,
|
|
14
|
+
message: mapping.message
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
return { lookup };
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { undo_registry_exports as n, make as t };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=chunk-undo-registry.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"undo-registry.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"chunk-undo-registry.mjs","names":[],"sources":["../../src/plugin-process-manager/history/undo-registry.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Operation from '@dxos/compute/Operation';\n\nimport type * as UndoMapping from './undo-mapping.ts';\n\n//\n// Public Interface\n//\n\n/**\n * Lookup result from UndoRegistry.\n */\nexport type UndoMappingResult = {\n inverse: Operation.Definition<any, any>;\n /** Returns undefined to indicate the operation is not undoable. */\n deriveContext: (input: any, output: any) => any | undefined;\n /** Message provider - may be a static Label or a function. */\n message?: UndoMapping.MessageProvider<Operation.Definition<any, any>>;\n};\n\n/**\n * UndoRegistry interface - looks up inverse operations.\n */\nexport interface UndoRegistry {\n lookup: (operation: Operation.Definition<any, any>) => UndoMappingResult | undefined;\n}\n\n//\n// Factory\n//\n\n/**\n * Creates an UndoRegistry that looks up inverse operations.\n */\nexport const make = (getMappings: () => UndoMapping.UndoMapping[]): UndoRegistry => {\n const lookup = (operation: Operation.Definition<any, any>): UndoMappingResult | undefined => {\n const mappings = getMappings();\n const mapping = mappings.find((m) => m.operation.meta.key === operation.meta.key);\n if (!mapping) {\n return undefined;\n }\n\n return {\n inverse: mapping.inverse,\n deriveContext: mapping.deriveContext,\n message: mapping.message,\n };\n };\n\n return { lookup };\n};\n"],"mappings":";;;;;;AAqCA,IAAa,QAAQ,gBAA+D;CAClF,MAAM,UAAU,cAA6E;EAE3F,MAAM,UADW,YACD,CAAA,CAAS,MAAM,MAAM,EAAE,UAAU,KAAK,QAAQ,UAAU,KAAK,GAAG;EAChF,IAAI,CAAC,SACH;EAGF,OAAO;GACL,SAAS,QAAQ;GACjB,eAAe,QAAQ;GACvB,SAAS,QAAQ;EACnB;CACF;CAEA,OAAO,EAAE,OAAO;AAClB"}
|