@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/lib/App.mjs +1 -2
  3. package/dist/lib/Hooks.mjs +1 -2
  4. package/dist/lib/chunk-App.mjs +11 -0
  5. package/dist/lib/chunk-App.mjs.map +1 -0
  6. package/dist/lib/chunk-Cli.mjs +8 -0
  7. package/dist/lib/chunk-Cli.mjs.map +1 -0
  8. package/dist/lib/chunk-Devtools.mjs +112 -0
  9. package/dist/lib/chunk-Devtools.mjs.map +1 -0
  10. package/dist/lib/chunk-Hooks2.mjs +35 -0
  11. package/dist/lib/chunk-Hooks2.mjs.map +1 -0
  12. package/dist/lib/chunk-PluginManagerProvider.mjs +12 -0
  13. package/dist/lib/chunk-PluginManagerProvider.mjs.map +1 -0
  14. package/dist/lib/chunk-ProcessManagerPlugin.mjs +3 -3
  15. package/dist/lib/chunk-ProcessManagerPlugin.mjs.map +1 -1
  16. package/dist/lib/chunk-Role.mjs +19 -0
  17. package/dist/lib/chunk-Role.mjs.map +1 -0
  18. package/dist/lib/chunk-SharedPackages.mjs +11 -0
  19. package/dist/lib/chunk-SharedPackages.mjs.map +1 -0
  20. package/dist/lib/chunk-Surface.mjs.map +1 -1
  21. package/dist/lib/chunk-Surface2.mjs +37 -0
  22. package/dist/lib/chunk-Surface2.mjs.map +1 -0
  23. package/dist/lib/chunk-SurfaceManager.mjs +3 -3
  24. package/dist/lib/chunk-SurfaceManager.mjs.map +1 -1
  25. package/dist/lib/chunk-activation-event.mjs +86 -0
  26. package/dist/lib/chunk-activation-event.mjs.map +1 -0
  27. package/dist/lib/chunk-activation-events.mjs +66 -0
  28. package/dist/lib/chunk-activation-events.mjs.map +1 -0
  29. package/dist/lib/chunk-capabilities.mjs +202 -0
  30. package/dist/lib/chunk-capabilities.mjs.map +1 -0
  31. package/dist/lib/chunk-capability-manager.mjs +203 -0
  32. package/dist/lib/chunk-capability-manager.mjs.map +1 -0
  33. package/dist/lib/chunk-capability2.mjs +2 -2
  34. package/dist/lib/chunk-capability2.mjs.map +1 -1
  35. package/dist/lib/chunk-history-tracker.mjs +1 -1
  36. package/dist/lib/chunk-hooks.mjs +5 -5
  37. package/dist/lib/chunk-hooks.mjs.map +1 -1
  38. package/dist/lib/chunk-plugin-asset-cache.mjs +28 -0
  39. package/dist/lib/chunk-plugin-asset-cache.mjs.map +1 -0
  40. package/dist/lib/chunk-plugin-manager.mjs +68 -35
  41. package/dist/lib/chunk-plugin-manager.mjs.map +1 -1
  42. package/dist/lib/chunk-plugin-manager2.mjs +22 -0
  43. package/dist/lib/chunk-plugin-manager2.mjs.map +1 -0
  44. package/dist/lib/chunk-plugin-manifest.mjs +126 -0
  45. package/dist/lib/chunk-plugin-manifest.mjs.map +1 -0
  46. package/dist/lib/chunk-plugin.mjs +354 -0
  47. package/dist/lib/chunk-plugin.mjs.map +1 -0
  48. package/dist/lib/chunk-process-manager-capability.mjs +11 -10
  49. package/dist/lib/chunk-process-manager-capability.mjs.map +1 -1
  50. package/dist/lib/chunk-registry.mjs.map +1 -1
  51. package/dist/lib/chunk-undo-mapping.mjs +47 -0
  52. package/dist/lib/{plugin-process-manager/history/undo-mapping.mjs.map → chunk-undo-mapping.mjs.map} +1 -1
  53. package/dist/lib/chunk-undo-registry.mjs +22 -0
  54. package/dist/lib/{plugin-process-manager/history/undo-registry.mjs.map → chunk-undo-registry.mjs.map} +1 -1
  55. package/dist/lib/chunk-url-loader.mjs +345 -0
  56. package/dist/lib/chunk-url-loader.mjs.map +1 -0
  57. package/dist/lib/cli.mjs +3 -3
  58. package/dist/lib/cli.mjs.map +1 -1
  59. package/dist/lib/common/Role.mjs +1 -19
  60. package/dist/lib/common/activation-events.mjs +1 -57
  61. package/dist/lib/common/capabilities.mjs +1 -202
  62. package/dist/lib/config.mjs +1 -2
  63. package/dist/lib/core/activation-event.mjs +1 -86
  64. package/dist/lib/core/capability-manager.mjs +1 -203
  65. package/dist/lib/core/capability.mjs +1 -2
  66. package/dist/lib/core/plugin-asset-cache.mjs +1 -28
  67. package/dist/lib/core/plugin-manager.mjs +2 -19
  68. package/dist/lib/core/plugin-manifest.mjs +1 -126
  69. package/dist/lib/core/plugin.mjs +1 -354
  70. package/dist/lib/core/registry.mjs +1 -2
  71. package/dist/lib/core/url-loader.mjs +1 -345
  72. package/dist/lib/index.mjs +19 -19
  73. package/dist/lib/ns/App.mjs +2 -11
  74. package/dist/lib/ns/Cli.mjs +2 -8
  75. package/dist/lib/ns/Devtools.mjs +1 -112
  76. package/dist/lib/ns/Hooks.mjs +2 -35
  77. package/dist/lib/ns/PluginManagerProvider.mjs +2 -12
  78. package/dist/lib/ns/SharedPackages.mjs +2 -11
  79. package/dist/lib/ns/Surface.mjs +2 -35
  80. package/dist/lib/ns/Translations.mjs +1 -2
  81. package/dist/lib/plugin-cli.mjs +2 -4
  82. package/dist/lib/plugin-cli.mjs.map +1 -1
  83. package/dist/lib/plugin-process-manager/ProcessManagerPlugin.mjs +1 -2
  84. package/dist/lib/plugin-process-manager/history/history-tracker.mjs +1 -2
  85. package/dist/lib/plugin-process-manager/history/undo-mapping.mjs +1 -47
  86. package/dist/lib/plugin-process-manager/history/undo-registry.mjs +1 -22
  87. package/dist/lib/testing/react.mjs +1 -1
  88. package/dist/lib/testing/react.mjs.map +1 -1
  89. package/dist/lib/testing.mjs +5 -5
  90. package/dist/lib/testing.mjs.map +1 -1
  91. package/dist/lib/vite-plugin.mjs +3 -3
  92. package/dist/lib/vite-plugin.mjs.map +1 -1
  93. package/dist/types/src/common/activation-events.d.ts +8 -0
  94. package/dist/types/src/common/activation-events.d.ts.map +1 -1
  95. package/dist/types/src/core/plugin-manager/manager-state.d.ts.map +1 -1
  96. package/dist/types/src/core/plugin-manager/module-loader.d.ts +0 -10
  97. package/dist/types/src/core/plugin-manager/module-loader.d.ts.map +1 -1
  98. package/dist/types/src/core/plugin-manager/plugin-manager.d.ts.map +1 -1
  99. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
  100. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  101. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  102. package/dist/types/tsconfig.tsbuildinfo +1 -1
  103. package/package.json +32 -32
  104. package/src/common/activation-events.ts +9 -0
  105. package/src/core/plugin-manager/manager-state.ts +14 -12
  106. package/src/core/plugin-manager/module-loader.ts +43 -7
  107. package/src/core/plugin-manager/plugin-manager.ts +2 -1
  108. package/src/plugin-process-manager/process-manager-capability.test.ts +66 -1
  109. package/src/plugin-process-manager/process-manager-capability.ts +22 -2
  110. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +9 -7
  111. package/src/ui/hooks/useApp.tsx +2 -1
  112. package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +19 -1
  113. package/dist/lib/common/Role.mjs.map +0 -1
  114. package/dist/lib/common/activation-events.mjs.map +0 -1
  115. package/dist/lib/common/capabilities.mjs.map +0 -1
  116. package/dist/lib/core/activation-event.mjs.map +0 -1
  117. package/dist/lib/core/capability-manager.mjs.map +0 -1
  118. package/dist/lib/core/plugin-asset-cache.mjs.map +0 -1
  119. package/dist/lib/core/plugin-manager.mjs.map +0 -1
  120. package/dist/lib/core/plugin-manifest.mjs.map +0 -1
  121. package/dist/lib/core/plugin.mjs.map +0 -1
  122. package/dist/lib/core/url-loader.mjs.map +0 -1
  123. package/dist/lib/ns/App.mjs.map +0 -1
  124. package/dist/lib/ns/Cli.mjs.map +0 -1
  125. package/dist/lib/ns/Devtools.mjs.map +0 -1
  126. package/dist/lib/ns/Hooks.mjs.map +0 -1
  127. package/dist/lib/ns/PluginManagerProvider.mjs.map +0 -1
  128. package/dist/lib/ns/SharedPackages.mjs.map +0 -1
  129. package/dist/lib/ns/Surface.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/core/plugin.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as Pipeable from 'effect/Pipeable';\nimport * as Schema from 'effect/Schema';\nimport type * as Scope from 'effect/Scope';\nimport type * as Types from 'effect/Types';\n\nimport { BaseError } from '@dxos/errors';\nimport { invariant } from '@dxos/invariant';\nimport { DXN } from '@dxos/keys';\nimport { Config2, PluginProfileSchema, PluginReleaseSchema } from '@dxos/protocols';\n\nimport * as ActivationEvent from './activation-event.ts';\nimport * as Capability from './capability.ts';\nimport type * as PluginManager from './plugin-manager/index.ts';\n\n//\n// Plugin Service Layer\n//\n\n/**\n * Effect Context.Tag for accessing PluginManager via the Effect layer system.\n * This allows lifecycle operations to access the plugin manager without having it passed as an argument.\n */\nexport class Service extends Context.Service<Service, PluginManager.PluginManager>()(\n '@dxos/app-framework/PluginManager',\n) {}\n\n//\n// Lifecycle Functions\n//\n\n/**\n * Activates plugins based on the activation event.\n * Accesses the PluginManager via the Effect layer system.\n * @param event The activation event.\n * @returns Whether the activation was successful.\n */\nexport const activate = (event: ActivationEvent.ActivationEvent): Effect.Effect<boolean, Error, Service> =>\n Effect.flatMap(Service, (manager) => manager.activate(event));\n\n/**\n * Re-activates the modules that were activated by the event.\n * Accesses the PluginManager via the Effect layer system.\n * @param event The activation event.\n * @returns Whether the reset was successful.\n */\nexport const reset = (event: ActivationEvent.ActivationEvent): Effect.Effect<boolean, Error, Service> =>\n Effect.flatMap(Service, (manager) => manager.reset(event));\n\n/**\n * Shuts down the plugin manager, deactivating all active modules and clearing lifecycle state.\n * Accesses the PluginManager via the Effect layer system.\n */\nexport const shutdown = (): Effect.Effect<boolean, Error, Service> =>\n Effect.flatMap(Service, (manager) => manager.shutdown());\n\n/**\n * Computes a module ID from plugin ID and export name.\n */\nconst computeModuleId = (pluginId: string, moduleName: string): string => {\n return `${pluginId}.module.${moduleName}`;\n};\n\n/**\n * Identifier denoting a PluginModule.\n */\nexport const PluginModuleTypeId: unique symbol = Symbol.for('@dxos/app-framework/PluginModule');\nexport type PluginModuleTypeId = typeof PluginModuleTypeId;\n\n/**\n * Type guard to check if a value is a PluginModule.\n */\nexport const isPluginModule = (value: unknown): value is PluginModule => {\n return typeof value === 'object' && value !== null && PluginModuleTypeId in value;\n};\n\n/**\n * Normalized activation specification of a module — which wave it belongs to and what it\n * exchanges with the capability graph.\n *\n * Every module names a wave: `activatesOn` defaults to {@link ActivationEvent.Idle} when the\n * authoring record omits it. Within a wave, modules are ordered topologically (providers of\n * `requires` activate first) and a module may pull a provider whose own wave has already fired.\n * Because a fired event stays fired, a module gated on an event that has already passed remains\n * eligible in every later round — which is what lets a consumer activate once a provider from\n * some other wave finally lands.\n */\nexport type ActivationSpec = {\n readonly activatesOn: ActivationEvent.Events;\n readonly requires: readonly Capability.AnyTag[];\n readonly provides: readonly Capability.AnyTag[];\n};\n\n/**\n * What a module's activate may produce at runtime: raw capability entries (see\n * {@link Capability.ModuleReturn}) or typed {@link Capability.Contribution}s.\n */\nexport type ModuleActivateResult = Capability.ModuleReturn | readonly Capability.AnyContribution[];\n\n/**\n * A unit of containment of modular functionality that can be provided to an application.\n * Activation of a module is async allowing for code to split and loaded lazily.\n *\n * Ordering between modules is expressed through capability dependencies, not registration\n * order: a module declares the capabilities it `provides` and `requires`, and the plugin\n * manager activates providers before consumers (topological order). Runtime-event modules\n * (`activatesOn`) activate only when their event fires. See\n * `plugin-client/src/ClientPlugin.ts` for a worked example.\n *\n * @idiom org.dxos.app-framework.moduleActivationOrdering\n * applies: sequencing one plugin module's activation before or after another\n * instead-of: assuming module registration order controls activation order\n * uses: {@link ActivationSpec}\n */\nexport interface PluginModule {\n readonly [PluginModuleTypeId]: PluginModuleTypeId;\n /**\n * Unique id of the module.\n */\n id: string;\n\n /**\n * Normalized activation specification — how and when the module activates.\n */\n readonly activation: ActivationSpec;\n\n /**\n * Called when the module is activated.\n * Declared `requires` are provided as Effect services; CapabilityManager and PluginManager\n * are ambient (Capability.Service, Plugin.Service).\n * @param props Optional props passed to the module.\n * @returns The capabilities of the module.\n */\n activate: (\n props?: any,\n ) => Effect.Effect<ModuleActivateResult, Error, Capability.Service | Service | Scope.Scope | never>;\n}\n\n/**\n * Structural authoring shape for typed modules. A module without `requires` is a root,\n * triggered by `activatesOn` (implicitly Idle). A module with `requires` is a chain\n * member: it activates once every declared capability has been contributed — by whichever\n * event's chain produced the providers, not necessarily during startup — additionally gated\n * on `activatesOn` when declared. Consumers of event-gated providers stay pending until the\n * event fires and the provider contributes; capability cycles fail at runtime\n * (DependencyCycleError). Channel enforcement happens in {@link ValidateModuleOptions}.\n */\nexport type TypedModuleOptions = {\n readonly id?: string;\n readonly activatesOn?: ActivationEvent.Events;\n readonly requires?: readonly Capability.AnyTag[];\n readonly provides?: readonly Capability.AnyTag[];\n readonly activate: (props?: any) => Effect.Effect<any, any, any>;\n};\n\ntype OptRequires<Opts> = Opts extends { requires: infer R extends readonly Capability.AnyTag[] } ? R : readonly [];\ntype OptProvides<Opts> = Opts extends { provides: infer P extends readonly Capability.AnyTag[] } ? P : readonly [];\n\n/**\n * Validates typed module options: evaluates to `true` for a valid module, otherwise to an\n * unconstructible branded type naming the violation. Checks that activate's environment only\n * uses declared `requires`, that its error channel extends `Error`, and that its return\n * exactly covers the declared `provides`. Options with no capability declarations are a\n * dependency-mode root and validate as `true` (the runtime normalizer classifies them).\n *\n * Applied on {@link addModule}'s return type (not intersected with the parameter): inference\n * of `Opts` from an options object containing inline generator activates fails when the\n * parameter is an intersection with a conditional over `Opts`, but succeeds for a naked\n * `Opts` parameter. An invalid module surfaces as a non-callable branded return, which the\n * `.pipe(...)` site rejects with the brand's message.\n */\nexport type ValidateModuleOptions<Opts extends TypedModuleOptions> = Opts extends\n | { provides: readonly Capability.AnyTag[] }\n | { requires: readonly Capability.AnyTag[] }\n | { activatesOn: ActivationEvent.Events; requires: readonly Capability.AnyTag[] }\n ? Opts['activate'] extends (props?: any) => Effect.Effect<infer A, infer E, infer R>\n ? [R] extends [Capability.Requirements<OptRequires<Opts>>]\n ? [E] extends [Error]\n ? [A] extends [Capability.ProvidesReturn<OptProvides<Opts>>]\n ? [Capability.ProvidedIds<OptProvides<Opts>>] extends [Capability.CoveredBy<A>]\n ? true\n : {\n readonly 'activate return misses declared capabilities': Exclude<\n Capability.ProvidedIds<OptProvides<Opts>>,\n Capability.CoveredBy<A>\n >;\n }\n : {\n readonly 'activate return includes undeclared capabilities': Exclude<\n Capability.CoveredBy<A>,\n Capability.ProvidedIds<OptProvides<Opts>>\n >;\n }\n : { readonly 'activate error channel must extend Error': E }\n : { readonly 'activate requires undeclared capabilities': Exclude<R, Capability.Requirements<OptRequires<Opts>>> }\n : { readonly 'activate must be an effect-returning function': Opts['activate'] }\n : true;\n\n/**\n * Erased module authoring record stored on the builder. Type enforcement happens at the\n * {@link addModule} overloads; the builder itself is intentionally heterogeneous.\n */\nexport type ModuleEntry = {\n readonly id?: string;\n readonly activatesOn?: ActivationEvent.Events;\n readonly requires?: readonly Capability.AnyTag[];\n readonly provides?: readonly Capability.AnyTag[];\n readonly activate: (props?: any) => Effect.Effect<any, Error, any>;\n};\n\nclass PluginModuleImpl implements PluginModule {\n readonly [PluginModuleTypeId]: PluginModuleTypeId = PluginModuleTypeId;\n readonly id: string;\n readonly activation: ActivationSpec;\n readonly activate: PluginModule['activate'];\n\n constructor(options: { id: string; activation: ActivationSpec; activate: PluginModule['activate'] }) {\n this.id = options.id;\n this.activation = options.activation;\n this.activate = options.activate;\n }\n}\n\n/**\n * Runtime plugin metadata, derived from the `@dxos/protocols` registry schemas (protocols owns the\n * config + registry schemas; the runtime projection lives here).\n *\n * - {@link Profile}: version-independent identity + display + provenance — the wire `PluginProfileSchema`\n * (which carries `key`, the display fields, `dependsOn`, `spec`) plus `author` (resolved at runtime\n * from the publisher's handle/DID for registry plugins, or self-declared for bundled).\n * - {@link Release}: a specific hosted release — the wire `PluginReleaseSchema` (version, moduleUrl, dependencies).\n * - {@link Meta}: a resolved single-release view — `{ profile, release? }`, both nested. `release` is\n * unset for bundled plugins (compiled in) and set for registry plugins.\n */\nexport const Profile = Schema.Struct({\n ...PluginProfileSchema.fields,\n author: Schema.optional(Schema.String),\n});\nexport type Profile = Schema.Schema.Type<typeof Profile>;\n\nexport const Release = PluginReleaseSchema;\nexport type Release = Schema.Schema.Type<typeof Release>;\n\nexport const Meta = Schema.Struct({\n profile: Profile,\n release: Schema.optional(Release),\n});\nexport type Meta = Schema.Schema.Type<typeof Meta>;\n\n/** Options for {@link makeMeta}: the {@link Profile} content fields plus the canonical DXN `key`. */\nexport type MakeMetaOptions = {\n key: DXN.DXN;\n name: string;\n description?: string;\n author?: string;\n homePage?: string;\n source?: string;\n tags?: readonly string[];\n dependsOn?: readonly string[];\n spec?: string;\n icon?: Config2.Icon;\n screenshots?: ReadonlyArray<Config2.Screenshot>;\n};\n\n/** Constructs a bundled-plugin {@link Meta} from a canonical DXN. `profile.key` is the bare NSID derived from `key`. */\nexport const makeMeta = (options: MakeMetaOptions): Meta => {\n const { key, ...rest } = options;\n return { profile: { ...rest, key: DXN.getName(key) } };\n};\n\n/** Returns the plugin's canonical DXN URI, constructed from `meta.profile.key` and `meta.release?.version`. */\nexport const getURI = (meta: Meta): DXN.DXN => DXN.make(meta.profile.key, meta.release?.version);\n\n/**\n * Derives a runtime {@link Meta} from a loaded `dx.config.ts` (the `@dxos/protocols` `Config2.Config`).\n * Maps the flat authoring config to the nested profile; bundled plugins have no `release`.\n */\nexport const getMetaFromConfig = ({ plugin }: Config2.Config): Meta => {\n const { key, ...rest } = plugin;\n return makeMeta({ key: DXN.make(key), ...rest });\n};\n\n/**\n * Identifier denoting a Plugin.\n */\nexport const PluginTypeId: unique symbol = Symbol.for('@dxos/app-framework/Plugin');\nexport type PluginTypeId = typeof PluginTypeId;\n\n/**\n * Type guard to check if a value is a Plugin.\n */\nexport const isPlugin = (value: unknown): value is Plugin => {\n return typeof value === 'object' && value !== null && PluginTypeId in value;\n};\n\n/**\n * A collection of modules that are be enabled/disabled as a unit.\n * Plugins provide things such as components, state, actions, etc. to the application.\n */\n// TODO(burdon): Convert to ECHO schema.\nexport interface Plugin {\n readonly [PluginTypeId]: PluginTypeId;\n readonly meta: Readonly<Meta>;\n readonly modules: ReadonlyArray<PluginModule>;\n}\n\n/**\n * Internal implementation of Plugin.\n * @internal\n */\nclass PluginImpl implements Plugin {\n readonly [PluginTypeId]: PluginTypeId = PluginTypeId;\n\n constructor(\n private readonly _meta: Meta,\n private readonly _modules: PluginModule[],\n ) {}\n\n get meta(): Readonly<Meta> {\n return this._meta;\n }\n\n get modules(): ReadonlyArray<PluginModule> {\n return this._modules;\n }\n}\n\n/**\n * Builder interface for creating plugins incrementally.\n */\nexport interface PluginBuilder<T = void> extends Pipeable.Pipeable {\n readonly meta: Meta;\n readonly modules: ReadonlyArray<ModuleEntry | ((options: T) => ModuleEntry)>;\n addModule(moduleOptions: ModuleEntry | ((options: T) => ModuleEntry)): PluginBuilder<T>;\n}\n\n/**\n * Builder implementation for creating plugins incrementally.\n */\nclass PluginBuilderImpl<T = void> implements PluginBuilder<T> {\n readonly meta: Meta;\n private readonly _modules: Array<ModuleEntry | ((options: T) => ModuleEntry)> = [];\n\n constructor(meta: Meta) {\n this.meta = meta;\n }\n\n get modules(): ReadonlyArray<ModuleEntry | ((options: T) => ModuleEntry)> {\n return this._modules;\n }\n\n addModule(moduleOptions: ModuleEntry | ((options: T) => ModuleEntry)): PluginBuilder<T> {\n this._modules.push(moduleOptions);\n return this;\n }\n\n pipe() {\n // eslint-disable-next-line prefer-rest-params\n return Pipeable.pipeArguments(this, arguments);\n }\n}\n\n/**\n * Creates a new PluginBuilder to start building a plugin.\n */\nexport const define = <T = void>(meta: Meta): PluginBuilder<T> => new PluginBuilderImpl<T>(meta);\n\n/**\n * Adds a module to a plugin builder. Supports both pipeline and direct call styles.\n *\n * Accepts either:\n * - A spec-carrying module ({@link Capability.lazyModule} / {@link Capability.inlineModule}):\n * requires/provides/activatesOn/props all come from the module's own spec (declared where\n * it is authored), so the call needs only an optional id override.\n * - An inline authoring record (or a function receiving plugin options): declares\n * `provides`/`requires` (dependency mode) or `activatesOn` + `requires`/`provides`\n * (runtime-event mode); the activate effect's environment is constrained to the declared\n * requires and its return must cover the declared provides.\n */\nexport function addModule<T = void>(\n module: Capability.Module<void> | undefined,\n options?: { id?: string },\n): (builder: PluginBuilder<T>) => PluginBuilder<T>;\n/** Spec-carrying module whose props are mapped from the plugin options. */\nexport function addModule<Options, T extends Options = Options>(\n module: Capability.Module<Options>,\n options?: { id?: string },\n): (builder: PluginBuilder<T>) => PluginBuilder<T>;\nexport function addModule<T, const Opts extends Types.NoExcessProperties<TypedModuleOptions, Opts>>(\n moduleOptions: Opts | ((options: T) => Opts),\n): true extends ValidateModuleOptions<Opts>\n ? (builder: PluginBuilder<T>) => PluginBuilder<T>\n : ValidateModuleOptions<Opts>;\n/** Direct-call variant of the typed record overload above (see {@link PluginBuilder.addModule}). */\nexport function addModule<T>(\n builder: PluginBuilder<T>,\n moduleOptions: TypedModuleOptions | ((options: T) => TypedModuleOptions),\n): PluginBuilder<T>;\nexport function addModule<T>(\n moduleOrOptionsOrBuilder:\n | Capability.Module<any>\n | ModuleEntry\n | ((options: T) => ModuleEntry)\n | PluginBuilder<T>\n | undefined,\n moduleOptions?: ModuleEntry | ((options: T) => ModuleEntry) | { id?: string },\n): ((builder: PluginBuilder<T>) => PluginBuilder<T>) | PluginBuilder<T> {\n // Headless capability barrels stub excluded modules as `undefined`, letting one canonical\n // plugin entry serve every environment.\n if (moduleOrOptionsOrBuilder === undefined) {\n return (builder: PluginBuilder<T>) => builder;\n }\n // Spec-carrying module: a tagged function whose requires/provides/activatesOn come from its\n // own spec; only an optional id override is supplied at the call site.\n if (typeof moduleOrOptionsOrBuilder === 'function' && Capability.ModuleTag in moduleOrOptionsOrBuilder) {\n const module = moduleOrOptionsOrBuilder as Capability.Module<any>;\n const { id } = (moduleOptions as { id?: string } | undefined) ?? {};\n return (builder: PluginBuilder<T>) =>\n builder.addModule((pluginOptions: T) => ({\n id: id ?? Capability.getModuleTag(module),\n activatesOn: module.activatesOn,\n requires: module.requires,\n provides: module.provides,\n activate: () => module(pluginOptions),\n }));\n }\n // Direct-call style: addModule(builder, moduleOptions).\n if (moduleOptions !== undefined) {\n return (moduleOrOptionsOrBuilder as PluginBuilder<T>).addModule(\n moduleOptions as ModuleEntry | ((options: T) => ModuleEntry),\n );\n }\n // Pipeline style: addModule(moduleOptions) returns a function.\n const moduleOpts = moduleOrOptionsOrBuilder as ModuleEntry | ((options: T) => ModuleEntry);\n return (builder: PluginBuilder<T>) => builder.addModule(moduleOpts);\n}\n\n/**\n * A plugin factory. The options parameter is optional exactly when `T` has no required members\n * (`{} extends T` — every field is optional, or `T` is `void`), so `SomePlugin()` is accepted\n * without an empty `{}`; a plugin whose options include a required field still demands them.\n */\nexport type PluginFactory<T = void> = ({} extends T ? (options?: T) => Plugin : (options: T) => Plugin) & {\n meta: Meta;\n};\n\n/**\n * Normalizes an authoring record to an {@link ActivationSpec} — the single boundary where the\n * optional authoring shape becomes the concrete runtime shape.\n *\n * Omitting `activatesOn` means the module belongs to the IDLE wave: a module that must run at\n * boot states `activatesOn: ActivationEvents.Startup` explicitly. Inverted from a startup default\n * so that forgetting the annotation costs post-paint responsiveness rather than time-to-\n * interactive — startup was the dumping ground, and its cost becomes the transitive demand\n * closure of the boot path, enforced by the capability graph instead of by remembering to\n * annotate. Un-annotated modules stay pullable as providers because the baseline wave is\n * `Startup u Idle`; see `ActivationScheduler.#isBaselineWave`, which moves with this default.\n */\nconst normalizeActivation = (options: ModuleEntry): ActivationSpec => ({\n activatesOn: options.activatesOn ?? ActivationEvent.Idle,\n requires: options.requires ?? [],\n provides: options.provides ?? [],\n});\n\n/**\n * Resolves a module from either a module entry or a function that returns one.\n */\nconst resolveModule = (\n meta: Meta,\n module: ModuleEntry | ((options: any) => ModuleEntry),\n options?: any,\n): PluginModuleImpl => {\n const moduleOptions = typeof module === 'function' ? module(options) : module;\n const pluginName = meta.profile.key;\n const id = Option.fromNullishOr(moduleOptions.id).pipe(\n Option.match({\n onNone: () => {\n const exportName = Capability.getModuleTag(moduleOptions.activate);\n invariant(exportName, `Plugin module missing name. Plugin: ${meta.profile.key}`);\n return computeModuleId(pluginName, exportName);\n },\n onSome: (id) => computeModuleId(pluginName, id),\n }),\n );\n return new PluginModuleImpl({\n id,\n activation: normalizeActivation(moduleOptions),\n // Erasure boundary: the authoring layer constrained the effect's environment to the\n // declared requires; the manager supplies exactly those services plus the ambient ones.\n activate: moduleOptions.activate as PluginModule['activate'],\n });\n};\n\n/**\n * Creates a Plugin from a builder.\n * Supports both pipeline and direct call styles.\n * Always returns a factory function (options: T) => Plugin.\n * When T is void, the function takes no arguments: () => Plugin.\n */\nexport function make<T>(builder: PluginBuilder<T>): PluginFactory<T>;\n// The impl return names the required-parameter form; the public overload above narrows it to the\n// optional-parameter form when `T` has no required members (the conditional can't be reduced for a\n// generic `T` inside the body, so it is stated on the overload instead).\nexport function make<T>(builder: PluginBuilder<T>): ((options: T) => Plugin) & { meta: Meta } {\n const meta = builder.meta;\n // `dependsOn` entries and `key` are both bare NSIDs, so compare directly.\n invariant(\n !meta.profile.dependsOn?.includes(meta.profile.key),\n `Plugin ${meta.profile.key} declares itself as a dependency.`,\n );\n\n const factory = (options?: T) => {\n // Default to `{}` so an omitted all-optional options object (see {@link PluginFactory}) is\n // safe for option-reading module callbacks; ignored by `void` and record modules.\n const resolved = (options ?? {}) as T;\n const modules = builder.modules.map((module) => resolveModule(meta, module, resolved));\n // The manager keys modules by id, so a second module with the same id would be dropped silently.\n const ids = new Set<string>();\n for (const module of modules) {\n invariant(!ids.has(module.id), `Duplicate module id ${module.id} in plugin ${meta.profile.key}.`);\n ids.add(module.id);\n }\n return new PluginImpl(meta, modules);\n };\n\n return Object.assign(factory, { meta });\n}\n\n//\n// Lazy plugin loading\n//\n\n/**\n * Symbol used to tag lazy plugin stubs with their loader closure.\n * Hidden from enumeration so plugin manager iteration / serialization paths\n * don't trip over it.\n */\nconst LazyTag: unique symbol = Symbol.for('@dxos/app-framework/Plugin/Lazy');\n\n/**\n * Async loader for a lazy plugin's real implementation.\n * The default export of the loaded module must be a `PluginFactory<T>` —\n * i.e. the same shape `Plugin.make` produces.\n */\nexport type LazyLoader<T = void> = () => Promise<{ default: PluginFactory<T> }>;\n\n/** Internal: payload carried on a lazy stub. */\ntype LazyPayload = { loader: LazyLoader<any>; options: unknown };\n\n/**\n * Defines a lazy plugin whose body is loaded on first enable.\n *\n * The returned factory produces a stub `Plugin` that exposes `meta`\n * synchronously (so callers can read `Plugin.meta.profile.key` for free) but defers\n * loading the real plugin's modules until the manager calls\n * `Plugin.resolveLazy`. This lets the plugin's main entry point ship as a\n * tiny meta-only chunk — the heavy capabilities, schema, React surfaces,\n * etc. live behind the dynamic `import()` and become a separate Rollup\n * chunk that is only fetched when the plugin is enabled.\n *\n * @example\n * ```ts\n * // plugin-markdown/src/index.ts\n * import * as Plugin from '@dxos/app-framework/Plugin';\n * import { meta } from './meta';\n *\n * export const MarkdownPlugin = Plugin.lazy(meta, () => import('./MarkdownPlugin'));\n *\n * // plugin-markdown/src/MarkdownPlugin.tsx\n * export const MarkdownPlugin = Plugin.define(meta).pipe(...heavy modules..., Plugin.make);\n * export default MarkdownPlugin;\n * ```\n */\nexport function lazy<T = void>(meta: Meta, loader: LazyLoader<T>): PluginFactory<T>;\n// Impl names the required-parameter form; the overload narrows to optional when `T` has no\n// required members (see {@link make} / {@link PluginFactory}).\nexport function lazy<T = void>(meta: Meta, loader: LazyLoader<T>): ((options: T) => Plugin) & { meta: Meta } {\n const factory = (options?: T): Plugin => {\n const stub = new PluginImpl(meta, []);\n Object.defineProperty(stub, LazyTag, {\n value: { loader, options } satisfies LazyPayload,\n enumerable: false,\n });\n return stub;\n };\n return Object.assign(factory, { meta });\n}\n\n/**\n * Type guard for lazy plugin stubs produced by {@link lazy}.\n */\nexport const isLazy = (plugin: Plugin): boolean => LazyTag in plugin;\n\n/**\n * Tagged error for failures during lazy plugin resolution. `context.id` is\n * the lazy plugin's `meta.profile.key`; `context.reason` discriminates the failure\n * mode (`'load-failed' | 'missing-default' | 'invalid-plugin' |\n * 'meta-mismatch'`) so callers can route on it.\n */\nexport class LazyPluginError extends BaseError.extend('LazyPluginError', 'Failed to resolve lazy plugin') {}\n\n/**\n * Tagged error for plugin-level dependency resolution failures.\n *\n * `context.id` is the plugin id the manager was acting on. `context.reason`\n * discriminates the failure mode:\n * - `'missing'` — declared dep is neither registered nor in the catalog.\n * `context.missing` lists offending ids.\n * - `'install-failed'` — dep was found in the catalog but `add()` failed.\n * `cause` carries the original error.\n * - `'cycle'` — closure walk detected a cycle. `context.path` is the cycle path.\n * - `'core-dependent'` — cascade-disable would have to disable a core plugin.\n * `context.coreDependent` is the blocking id.\n */\nexport class PluginDependencyError extends BaseError.extend(\n 'PluginDependencyError',\n 'Plugin dependency resolution failed',\n) {}\n\n/**\n * Resolves a lazy plugin stub to its real plugin.\n * Returns the plugin unchanged if it is not lazy. Failures surface as\n * {@link LazyPluginError} with `context.reason` indicating the failure mode\n * and (for loader failures) `cause` set to the original error.\n */\nexport const resolveLazy = (plugin: Plugin): Effect.Effect<Plugin, LazyPluginError> =>\n Effect.gen(function* () {\n if (!isLazy(plugin)) {\n return plugin;\n }\n const id = plugin.meta.profile.key;\n const { loader, options } = (plugin as unknown as { [LazyTag]: LazyPayload })[LazyTag];\n const mod = yield* Effect.tryPromise({\n try: loader,\n catch: (error) => new LazyPluginError({ context: { id, reason: 'load-failed' }, cause: error }),\n });\n if (!mod || typeof mod.default !== 'function') {\n return yield* Effect.fail(new LazyPluginError({ context: { id, reason: 'missing-default' } }));\n }\n const result = mod.default(options);\n if (!isPlugin(result)) {\n return yield* Effect.fail(new LazyPluginError({ context: { id, reason: 'invalid-plugin' } }));\n }\n if (result.meta.profile.key !== id) {\n return yield* Effect.fail(\n new LazyPluginError({ context: { id, reason: 'meta-mismatch', returnedId: result.meta.profile.key } }),\n );\n }\n return result;\n });\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"url-loader.mjs","names":[],"sources":["../../../src/core/url-loader.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport { BaseError } from '@dxos/errors';\nimport { log } from '@dxos/log';\n\nimport * as PluginAssetCache from './plugin-asset-cache.ts';\nimport * as PluginManifest from './plugin-manifest.ts';\nimport * as Plugin from './plugin.ts';\n\nconst DEFAULT_KEY = 'org.dxos.composer.remote-plugins';\n\n/**\n * Tagged error for any failure during remote plugin loading. Construction sites\n * set `context.locator` and `context.reason` (one of `'invalid-locator' |\n * 'manifest-error' | 'cache-error' | 'import-failed' | 'meta-missing' |\n * 'meta-mismatch' | 'duplicate-id'`) so handlers can route on the failure mode.\n */\nexport class RemotePluginLoadError extends BaseError.extend('RemotePluginLoadError', 'Failed to load remote plugin') {}\n\n/**\n * Abstraction over key-value storage (defaults to localStorage).\n */\nexport type Storage = {\n get(key: string): string | null;\n set(key: string, value: string): void;\n};\n\n/**\n * Options for configuring the URL loader.\n */\nexport type Options = {\n storage?: Storage;\n key?: string;\n /**\n * Per-platform offline asset cache. Defaults to a no-op cache (no offline support).\n */\n cache?: PluginAssetCache.Cache;\n};\n\n/**\n * Options for the preload entry point. Adds an optional progress hook so hosts\n * can drive a boot-loader counter (`Loading plugins (3/12)…`) as remote plugin\n * manifests resolve. Resolution order is *not* deterministic — each entry races\n * its own network fetch — so callers should treat `loaded` as a count, not an\n * index. Failures are still swallowed; `loaded` advances on both success and\n * recoverable failure so the counter always reaches `total`.\n */\nexport type PreloadOptions = Options & {\n onPluginLoaded?: (loaded: number, total: number) => void;\n};\n\n/**\n * Persisted record of a remote plugin that has been loaded previously.\n *\n * `url` is the URL of the plugin manifest (`plugin.json`), not the entry module.\n */\nexport type RemotePluginView = {\n id: string;\n url: string;\n /** Installed version string, e.g. `v1.0.0`. Populated after install from a community catalog entry. */\n version?: string;\n};\n\nconst defaultStorage = (): Storage => ({\n get: (key) => localStorage.getItem(key),\n set: (key, value) => localStorage.setItem(key, value),\n});\n\nconst getPersistedRemotePlugins = (storage: Storage, key: string): RemotePluginView[] => {\n try {\n const parsed: unknown = JSON.parse(storage.get(key) ?? '[]');\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter(\n (entry): entry is RemotePluginView =>\n typeof entry === 'object' &&\n entry !== null &&\n typeof entry.id === 'string' &&\n typeof entry.url === 'string' &&\n (entry.version === undefined || typeof entry.version === 'string'),\n );\n } catch {\n return [];\n }\n};\n\nconst persistRemotePlugin = (storage: Storage, key: string, entry: RemotePluginView): void => {\n try {\n const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== entry.id);\n entries.push(entry);\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to persist remote plugin entry', { entry, error });\n }\n};\n\nconst removePersistedRemotePlugin = (storage: Storage, key: string, pluginId: string): void => {\n try {\n const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== pluginId);\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to remove remote plugin entry', { pluginId, error });\n }\n};\n\nconst isUrl = (locator: string): boolean => {\n try {\n const url = new URL(locator);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Returns true when the URL's hostname is the local host (localhost, 127.0.0.1, or ::1).\n */\nexport const isLocalUrl = (locator: string): boolean => {\n try {\n const hostname = new URL(locator).hostname.toLowerCase();\n // WHATWG URL returns '::1' without brackets in Node; some browsers return '[::1]'.\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';\n } catch {\n return false;\n }\n};\n\n/**\n * Returns the list of remote plugin entries previously persisted by {@link make}.\n * Useful for UI code that needs to know which loaded plugins were installed from a URL\n * (e.g. to surface a tag on remote or localhost-hosted plugins).\n */\nexport const getRemoteEntries = (options: Options = {}): readonly RemotePluginView[] => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n return getPersistedRemotePlugins(storage, key);\n};\n\n/**\n * Replaces the persisted remote plugin entries wholesale. Entries are read during {@link preload},\n * so a replacement takes effect on the next reload.\n */\nexport const setRemoteEntries = (entries: readonly RemotePluginView[], options: Options = {}): void => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n try {\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to replace remote plugin entries', { error });\n }\n};\n\n/**\n * Updates the persisted installed version for an already-stored remote plugin entry.\n * Does nothing if the plugin has not been persisted yet.\n */\nexport const setInstalledVersion = (id: string, version: string, options: Options = {}): void => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const entries = getPersistedRemotePlugins(storage, key);\n const index = entries.findIndex((entry) => entry.id === id);\n if (index === -1) {\n return;\n }\n entries[index] = { ...entries[index], version };\n try {\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to update installed version for remote plugin', { id, version, error });\n }\n};\n\n/**\n * Returns the installed version string for the given plugin id, or `undefined` if\n * the plugin was installed without version tracking (legacy install) or is not found.\n */\nexport const getInstalledVersion = (id: string, options: Options = {}): string | undefined => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n return getPersistedRemotePlugins(storage, key).find((entry) => entry.id === id)?.version;\n};\n\nconst normalizePluginExport = (mod: Record<string, unknown>): Plugin.Plugin => {\n const exported = mod.default;\n if (Plugin.isPlugin(exported)) {\n return exported;\n }\n if (typeof exported === 'function') {\n const result = (exported as () => unknown)();\n if (Plugin.isPlugin(result)) {\n return result;\n }\n }\n throw new Error('Remote module default export is not a Plugin or a zero-arg plugin factory.');\n};\n\n/**\n * Loads stylesheets declared in the manifest by appending `<link rel=\"stylesheet\">` elements to the host document.\n * Each link is tagged with `data-dxos-plugin-id` so `uninstall` can clean them up.\n */\nconst loadStylesheets = (\n manifest: PluginManifest.ResolvedManifest,\n cache: PluginAssetCache.Cache,\n): Effect.Effect<void, PluginAssetCache.PluginAssetCacheError> =>\n Effect.gen(function* () {\n if (typeof document === 'undefined') {\n return;\n }\n const cssUrls = manifest.assetUrls.filter((url) => url.endsWith('.css'));\n for (const url of cssUrls) {\n const resolved = yield* cache.resolve(manifest.key, url);\n if (document.querySelector(`link[data-dxos-plugin-id=\"${manifest.key}\"][href=\"${resolved}\"]`)) {\n continue;\n }\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = resolved;\n link.dataset.dxosPluginId = manifest.key;\n document.head.appendChild(link);\n }\n });\n\nconst loadFromManifest = (\n manifestUrl: string,\n cache: PluginAssetCache.Cache,\n): Effect.Effect<{ plugin: Plugin.Plugin; manifest: PluginManifest.ResolvedManifest }, RemotePluginLoadError> =>\n Effect.gen(function* () {\n log.info('loading remote plugin', { manifestUrl });\n const manifest = yield* PluginManifest.fetchManifest(manifestUrl).pipe(\n Effect.mapError(\n (cause) => new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'manifest-error' }, cause }),\n ),\n );\n const wrapCacheError = Effect.mapError(\n (cause: PluginAssetCache.PluginAssetCacheError) =>\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'cache-error' }, cause }),\n );\n // Dev-mode manifests (served by `vite dev` via composerPlugin) skip the offline\n // cache: chunks resolve on demand through the dev server, the asset list isn't\n // enumerable up-front, and CSS arrives via runtime <style> injection rather than\n // static <link> tags.\n if (!manifest.dev) {\n // Cache the manifest URL alongside its declared assets. Without it, `preload` on a\n // subsequent reload would fetch the manifest from the network — and fail when the\n // plugin's host is offline, dropping the plugin from the runtime.\n const cachedUrls =\n manifest.assetUrls.indexOf(manifestUrl) === -1 ? [manifestUrl, ...manifest.assetUrls] : manifest.assetUrls;\n yield* cache.cache(manifest.key, cachedUrls).pipe(wrapCacheError);\n }\n const entryUrl = manifest.dev\n ? manifest.entryUrl\n : yield* cache.resolve(manifest.key, manifest.entryUrl).pipe(wrapCacheError);\n const mod = yield* Effect.tryPromise({\n try: () => import(/* @vite-ignore */ entryUrl),\n catch: (cause) =>\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'import-failed' }, cause }),\n });\n const plugin = normalizePluginExport(mod);\n if (!plugin.meta.profile.key || !plugin.meta.profile.name) {\n return yield* Effect.fail(\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'meta-missing' } }),\n );\n }\n if (plugin.meta.profile.key !== manifest.key) {\n return yield* Effect.fail(\n new RemotePluginLoadError({\n context: {\n locator: manifestUrl,\n reason: 'meta-mismatch',\n metaId: plugin.meta.profile.key,\n manifestId: manifest.key,\n },\n }),\n );\n }\n // Append stylesheets only after the entry imports cleanly and meta validation\n // passes. If we did this earlier and the import or meta checks failed, the\n // `<link>` tags would leak into the host DOM with no plugin to own their\n // teardown — `uninstall` only runs for plugins the manager actually accepted.\n // In dev mode, Vite injects styles at runtime via the module graph, so static\n // <link> tags would duplicate (or 404, since CSS chunks aren't pre-built).\n if (!manifest.dev) {\n yield* loadStylesheets(manifest, cache).pipe(wrapCacheError);\n }\n return { plugin, manifest };\n });\n\n/**\n * Preloads previously persisted remote plugins from storage. Per-entry failures\n * are logged and swallowed — the returned effect always succeeds with whichever\n * plugins loaded cleanly, so a single bad entry can't block the host's startup.\n */\nexport const preload = (options: PreloadOptions = {}): Effect.Effect<Plugin.Plugin[], never> =>\n Effect.gen(function* () {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n const onPluginLoaded = options.onPluginLoaded;\n\n const entries = getPersistedRemotePlugins(storage, key);\n if (entries.length === 0) {\n return [];\n }\n log.info('preloading remote plugins', { count: entries.length });\n const total = entries.length;\n let loaded = 0;\n const results = yield* Effect.all(\n entries.map((entry) =>\n loadFromManifest(entry.url, cache).pipe(\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to preload remote plugin', { entry, error }))),\n Effect.option,\n // Tick the progress hook on both success and recoverable failure so\n // the counter monotonically reaches `total`. The host-supplied\n // callback is best-effort: any synchronous throw flows through\n // `Effect.try` and gets logged + ignored so a buggy hook can't\n // derail the preload.\n Effect.tap(() =>\n Effect.sync(() => {\n loaded += 1;\n }).pipe(\n Effect.andThen(Effect.try(() => onPluginLoaded?.(loaded, total))),\n Effect.tapError((error) => Effect.sync(() => log.warn('onPluginLoaded threw', { loaded, total, error }))),\n Effect.ignore,\n ),\n ),\n ),\n ),\n { concurrency: 'unbounded' },\n );\n return results.flatMap((result) =>\n Option.match(result, {\n onNone: () => [],\n onSome: ({ plugin }) => [plugin],\n }),\n );\n });\n\n/**\n * Creates a plugin loader that resolves built-in plugins by ID or loads remote plugins from URLs.\n *\n * Remote URLs must point at a plugin manifest (`plugin.json`). The loader fetches the manifest,\n * eagerly persists every declared asset via the configured `PluginAssetCache`, then dynamic-imports\n * the entry module.\n */\nexport const make = (builtinPlugins: Plugin.Plugin[], options: Options = {}) => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n\n return (locator: string): Effect.Effect<{ plugin: Plugin.Plugin; dev?: boolean }, RemotePluginLoadError> =>\n Effect.gen(function* () {\n const builtin = builtinPlugins.find((plugin) => plugin.meta.profile.key === locator);\n if (builtin) {\n return { plugin: builtin };\n }\n if (!isUrl(locator)) {\n return yield* Effect.fail(new RemotePluginLoadError({ context: { locator, reason: 'invalid-locator' } }));\n }\n const { plugin, manifest } = yield* loadFromManifest(locator, cache);\n // Production manifests can't collide with a builtin id — that would make\n // the registered plugin permanently inaccessible. Dev manifests are\n // allowed to collide because the manager shadow primitive handles the\n // overlap (dev plugin takes the id slot for the session, original is\n // restored on uninstall or page reload).\n if (!manifest.dev) {\n const duplicate = builtinPlugins.find((existing) => existing.meta.profile.key === plugin.meta.profile.key);\n if (duplicate) {\n return yield* Effect.fail(\n new RemotePluginLoadError({ context: { locator, reason: 'duplicate-id', id: plugin.meta.profile.key } }),\n );\n }\n persistRemotePlugin(storage, key, { id: plugin.meta.profile.key, url: locator });\n }\n return { plugin, dev: manifest.dev };\n });\n};\n\n/**\n * Removes a previously installed remote plugin: drops the persisted entry, evicts cached\n * assets, and removes any stylesheet `<link>` tags that {@link loadFromManifest} appended.\n *\n * Cache eviction failures are logged and swallowed — the persisted entry has already been\n * dropped so the user-visible state is consistent regardless of whether the platform\n * cache cooperated.\n */\nexport const uninstall = (pluginId: string, options: Options = {}): Effect.Effect<void, never> =>\n Effect.gen(function* () {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n\n removePersistedRemotePlugin(storage, key, pluginId);\n if (typeof document !== 'undefined') {\n document.querySelectorAll(`link[data-dxos-plugin-id=\"${pluginId}\"]`).forEach((node) => node.remove());\n }\n yield* cache.evict(pluginId).pipe(\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to evict plugin assets', { pluginId, error }))),\n Effect.ignore,\n );\n });\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"App.mjs","names":[],"sources":["../../../src/App.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport { App as Root, bootLoader } from './ui/components/App/index.ts';\nexport type { AppProps as RootProps } from './ui/components/App/index.ts';\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Cli.mjs","names":[],"sources":["../../../src/Cli.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport * from './cli/index.ts';\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Devtools.mjs","names":[],"sources":["../../../src/Devtools.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Result from 'effect/Result';\nimport * as Schema from 'effect/Schema';\nimport * as SchemaIssue from 'effect/SchemaIssue';\n\nimport * as Operation from '@dxos/compute/Operation';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\nimport { DXN } from '@dxos/keys';\n\nimport * as Capabilities from './common/capabilities.ts';\nimport { type PluginManager } from './core/index.ts';\n\n/** Summary of one registered plugin. */\nexport type PluginInfo = {\n id: string;\n name?: string;\n description?: string;\n /** Core plugins cannot be disabled. */\n core: boolean;\n enabled: boolean;\n /** At least one of the plugin's modules has activated. */\n active: boolean;\n moduleIds: string[];\n};\n\n/** One top-level field of an operation's input or output. */\nexport type OperationField = {\n name: string;\n optional: boolean;\n /** Rendered type, truncated — enough to choose a value, not a substitute for the schema. */\n type: string;\n};\n\n/** Summary of one operation, enumerated without loading its handler. */\nexport type OperationInfo = {\n key: string;\n name?: string;\n description?: string;\n /** Derived from the contributing module id; absent when the operation came from outside a plugin. */\n pluginId?: string;\n moduleId?: string;\n /** Top-level input fields. Absent when the input is not a struct (e.g. `Schema.Void`). */\n input?: OperationField[];\n /** Top-level output fields. Absent when the output is not a struct. */\n output?: OperationField[];\n};\n\n/**\n * `globalThis.composer` — the app-layer console namespace, the counterpart to `__DXOS__`/`dxos`\n * (client and ECHO). Populated as the app boots, so every member is optional; the declaration buys\n * type-checking at call sites, not a presence guarantee.\n *\n * An interface rather than a type alias so apps can declaration-merge their own debug hooks onto it\n * — a second `declare global { var composer }` elsewhere would collide with this one instead.\n */\nexport interface ComposerDevtools {\n manager?: PluginManager.PluginManager;\n plugins?: () => PluginInfo[];\n operations?: (pluginId?: string) => OperationInfo[];\n invoke?: (key: string, input?: unknown, options?: Operation.InvokeOptions) => Promise<unknown>;\n [key: string]: unknown;\n}\n\ndeclare global {\n // eslint-disable-next-line no-var\n var composer: ComposerDevtools | undefined;\n}\n\n/**\n * Module ids are `org.dxos.plugin.<slug>.module.<name>`; the plugin id is everything before\n * `.module.`. Returns undefined for ids that do not follow the convention.\n */\nconst pluginIdOf = (moduleId: string): string | undefined => {\n const index = moduleId.indexOf('.module.');\n return index === -1 ? undefined : moduleId.slice(0, index);\n};\n\n/** Rendered types carry the field's description annotation, which can be a paragraph. */\nconst MAX_TYPE_LENGTH = 60;\n\n/**\n * Top-level fields of a struct schema, so `operations()` says what an operation takes rather than\n * only what it is called. Non-struct schemas (`Schema.Void`, unions) have no field list.\n */\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();\n\nconst fieldsOf = (schema: Schema.Top | undefined): OperationField[] | undefined => {\n const ast = schema?.ast;\n if (!ast || !SchemaAST.isObjects(ast)) {\n return undefined;\n }\n return SchemaAST.getPropertySignatures(ast).map((property) => {\n const type = String(property.type);\n return {\n name: String(property.name),\n optional: SchemaAST.isOptional(property.type),\n type: type.length > MAX_TYPE_LENGTH ? `${type.slice(0, MAX_TYPE_LENGTH)}…` : type,\n };\n });\n};\n\n/**\n * Attaches the plugin/operation console API to `globalThis.composer`.\n *\n * Lives at the framework level rather than in a devtools plugin so it is present in any app and\n * does not depend on which plugins happen to be enabled — inspecting a broken plugin set is\n * exactly when this is wanted.\n */\nexport const setup = (manager: PluginManager.PluginManager): void => {\n const listOperations = (pluginId?: string): OperationInfo[] => {\n const byModule = manager.registry.get(manager.capabilities.atomByModule(Capabilities.OperationHandler));\n return Object.entries(byModule).flatMap(([moduleId, sets]) => {\n const owner = pluginIdOf(moduleId);\n if (pluginId && owner !== pluginId) {\n return [];\n }\n // `definitions()` is the enumerate-without-loading path; `getHandlers()` would pull every\n // lazily-imported handler module just to list them.\n return sets.flatMap((set) =>\n set.definitions().map((definition) => ({\n key: definition.meta.key,\n name: definition.meta.name,\n description: definition.meta.description,\n pluginId: owner,\n moduleId,\n input: fieldsOf(definition.input),\n output: fieldsOf(definition.output),\n })),\n );\n });\n };\n\n // Definitions carry DXN-form keys, so accept either that or the bare NSID — same normalization\n // the handler sets apply, so whatever `operations()` prints can be pasted straight into `invoke`.\n // `isDXN` is only a `dxn:` prefix check, so a hand-typed `dxn:` would throw out of `getName` and\n // abort the whole lookup; fall back to the raw key and let it miss as \"Unknown operation\".\n const normalizeKey = (key: string): string => {\n if (!DXN.isDXN(key)) {\n return key;\n }\n try {\n return DXN.getName(key);\n } catch {\n return key;\n }\n };\n\n const findDefinition = (key: string): Operation.Definition.Any | undefined => {\n const wanted = normalizeKey(key);\n for (const set of manager.capabilities.getAll(Capabilities.OperationHandler)) {\n const definition = set.definitions().find((candidate) => normalizeKey(candidate.meta.key) === wanted);\n if (definition) {\n return definition;\n }\n }\n return undefined;\n };\n\n const composer: ComposerDevtools = (globalThis.composer ??= {});\n composer.manager = manager;\n\n composer.plugins = () => {\n const core = new Set(manager.getCore());\n const enabled = new Set(manager.getEnabled());\n const active = new Set(manager.getActive());\n return manager.getPlugins().map((plugin) => {\n const id = plugin.meta.profile.key;\n return {\n id,\n name: plugin.meta.profile.name,\n description: plugin.meta.profile.description,\n core: core.has(id),\n enabled: enabled.has(id),\n active: plugin.modules.some((module) => active.has(module.id)),\n moduleIds: plugin.modules.map((module) => module.id),\n };\n });\n };\n\n composer.operations = listOperations;\n\n composer.invoke = async (key, input, options) => {\n const definition = findDefinition(key);\n if (!definition) {\n throw new Error(`Unknown operation: ${key} (try composer.operations())`);\n }\n\n // `invokePromise` does not validate its input: a payload missing a required field comes back\n // as `{ data: undefined }` with no error, and the operation quietly does nothing. Validating\n // here turns that silent no-op into the error the caller expected — the whole point of an\n // operation carrying a schema.\n // Validated against the TYPE side: callers pass decoded values, so decoding the codec would\n // report a materialized `Ref` as malformed at a path no caller can address.\n const validation = Schema.decodeUnknownResult(\n Schema.make<Schema.Codec<unknown, unknown>>(SchemaAST.toType(definition.input.ast)),\n {\n errors: 'all',\n },\n )(input);\n if (Result.isFailure(validation)) {\n // The flattened formatter over the tree one: the tree renders the whole schema before reaching\n // the offending field, burying `id: is missing` under a paragraph of unrelated shape.\n const issues = formatIssue(validation.failure.issue)\n .issues.map((issue) => `${issue.path?.join('.') || '(root)'}: ${issue.message}`)\n .join('; ');\n throw new Error(`Invalid input for ${key} — ${issues}`);\n }\n\n const invoker = manager.capabilities.get(Capabilities.OperationInvoker);\n // Forwarded as given: a database-backed operation resolves its space from `options.spaceId`,\n // which a caller on the debug port has no other way to supply.\n const { data, error } = await invoker.invokePromise(definition, input as never, options);\n if (error) {\n throw error;\n }\n return data;\n };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Hooks.mjs","names":[],"sources":["../../../src/Hooks.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport * from './ui/hooks/index.ts';\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PluginManagerProvider.mjs","names":[],"sources":["../../../src/PluginManagerProvider.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport {\n PluginManagerProvider as Root,\n useOptionalPluginManager,\n usePluginManager,\n} from './ui/components/PluginManager/index.ts';\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"SharedPackages.mjs","names":[],"sources":["../../../src/SharedPackages.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport * from './vite-plugin/packages.ts';\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Surface.mjs","names":[],"sources":["../../../src/Surface.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nexport * from './ui/components/Surface/Surface.ts';\nexport {\n SurfaceManager as Manager,\n SurfaceManagerProvider as ManagerProvider,\n useSurfaceManager,\n} from './ui/components/Surface/index.ts';\nexport type {\n SurfaceProfilerEntry as ProfilerEntry,\n SurfaceProfilerStats as ProfilerStats,\n} from './ui/components/Surface/index.ts';\n"],"mappings":""}