@dxos/app-framework 0.8.4-staging.60fe92afc8 → 0.9.1-main.c7dcc2e112

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 (208) hide show
  1. package/dist/lib/browser/{capability-BOPAKKWG.mjs → capability-Z4MRP3CP.mjs} +9 -9
  2. package/dist/lib/browser/{chunk-CO3XLNUX.mjs → chunk-37PG7XJC.mjs} +18 -18
  3. package/dist/lib/browser/chunk-37PG7XJC.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-66IXTIVK.mjs → chunk-3NQLTQCP.mjs} +6 -7
  5. package/dist/lib/browser/chunk-3NQLTQCP.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-KZUDO43J.mjs → chunk-3UTDRCKX.mjs} +31 -23
  7. package/dist/lib/browser/chunk-3UTDRCKX.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-53HJHMS5.mjs +7 -0
  9. package/dist/lib/browser/chunk-53HJHMS5.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-IW44C7UL.mjs → chunk-CMINFVA5.mjs} +1 -1
  11. package/dist/lib/browser/chunk-CMINFVA5.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-Y66ELD4U.mjs → chunk-DFVSOBV2.mjs} +17 -7
  13. package/dist/lib/browser/chunk-DFVSOBV2.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-DC3WRPBV.mjs → chunk-S2ZBYQPI.mjs} +45 -12
  15. package/dist/lib/browser/chunk-S2ZBYQPI.mjs.map +7 -0
  16. package/dist/lib/browser/{chunk-HFLKXMT7.mjs → chunk-SWECIICH.mjs} +57 -41
  17. package/dist/lib/browser/chunk-SWECIICH.mjs.map +7 -0
  18. package/dist/lib/browser/{chunk-5GY3YOEL.mjs → chunk-VF37YTXJ.mjs} +5 -5
  19. package/dist/lib/browser/chunk-VF37YTXJ.mjs.map +7 -0
  20. package/dist/lib/browser/{chunk-XHS5KDNX.mjs → chunk-W4BHZING.mjs} +25 -25
  21. package/dist/lib/browser/chunk-W4BHZING.mjs.map +7 -0
  22. package/dist/lib/browser/{chunk-FO3IYSLV.mjs → chunk-YUVQIOTB.mjs} +29 -37
  23. package/dist/lib/browser/chunk-YUVQIOTB.mjs.map +7 -0
  24. package/dist/lib/browser/cli/index.mjs +9 -9
  25. package/dist/lib/browser/cli/index.mjs.map +2 -2
  26. package/dist/lib/browser/common/activation-events.mjs +7 -7
  27. package/dist/lib/browser/common/capabilities.mjs +7 -7
  28. package/dist/lib/browser/config/index.mjs +8 -0
  29. package/dist/lib/browser/config/index.mjs.map +7 -0
  30. package/dist/lib/browser/core/activation-event.mjs +1 -1
  31. package/dist/lib/browser/core/capability.mjs +1 -1
  32. package/dist/lib/browser/core/plugin-manager.mjs +4 -4
  33. package/dist/lib/browser/core/plugin.mjs +12 -2
  34. package/dist/lib/browser/core/url-loader.mjs +3 -3
  35. package/dist/lib/browser/index.mjs +14 -10
  36. package/dist/lib/browser/index.mjs.map +1 -1
  37. package/dist/lib/browser/meta.json +1 -1
  38. package/dist/lib/browser/{process-manager-capability-GXNUO7AR.mjs → process-manager-capability-RDROCCQD.mjs} +9 -9
  39. package/dist/lib/browser/testing/index.mjs +15 -15
  40. package/dist/lib/browser/testing/index.mjs.map +3 -3
  41. package/dist/lib/browser/testing/react.mjs +11 -11
  42. package/dist/lib/browser/testing/react.mjs.map +3 -3
  43. package/dist/lib/browser/ui/index.mjs +16 -10
  44. package/dist/lib/node-esm/{capability-KZWRTX7R.mjs → capability-34Z6NPVR.mjs} +9 -9
  45. package/dist/lib/node-esm/chunk-2UPUZXGU.mjs +9 -0
  46. package/dist/lib/node-esm/chunk-2UPUZXGU.mjs.map +7 -0
  47. package/dist/lib/node-esm/{chunk-FSVGKTF6.mjs → chunk-A4O42ZO3.mjs} +17 -7
  48. package/dist/lib/node-esm/chunk-A4O42ZO3.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-CTKEZHKF.mjs → chunk-AIDZMJ3U.mjs} +1 -1
  50. package/dist/lib/node-esm/chunk-AIDZMJ3U.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-XOCUANHO.mjs → chunk-BMW6P6QA.mjs} +6 -7
  52. package/dist/lib/node-esm/chunk-BMW6P6QA.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-A5CHGAZE.mjs → chunk-H7T7XXLS.mjs} +57 -41
  54. package/dist/lib/node-esm/chunk-H7T7XXLS.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-NULBHQYS.mjs → chunk-LUK3FCTH.mjs} +18 -18
  56. package/dist/lib/node-esm/chunk-LUK3FCTH.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-XGBUKRCK.mjs → chunk-TEQSMP2O.mjs} +31 -23
  58. package/dist/lib/node-esm/chunk-TEQSMP2O.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-KFZEB6BV.mjs → chunk-TLKYZKPW.mjs} +5 -5
  60. package/dist/lib/node-esm/chunk-TLKYZKPW.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-WK7OIQKI.mjs → chunk-VCPGX7WN.mjs} +29 -37
  62. package/dist/lib/node-esm/chunk-VCPGX7WN.mjs.map +7 -0
  63. package/dist/lib/node-esm/{chunk-XIGPWXCQ.mjs → chunk-WERVO5EK.mjs} +45 -12
  64. package/dist/lib/node-esm/chunk-WERVO5EK.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-SX5NVETP.mjs → chunk-ZRTG4TSS.mjs} +25 -25
  66. package/dist/lib/node-esm/chunk-ZRTG4TSS.mjs.map +7 -0
  67. package/dist/lib/node-esm/cli/index.mjs +9 -9
  68. package/dist/lib/node-esm/cli/index.mjs.map +2 -2
  69. package/dist/lib/node-esm/common/activation-events.mjs +7 -7
  70. package/dist/lib/node-esm/common/capabilities.mjs +7 -7
  71. package/dist/lib/node-esm/config/index.mjs +9 -0
  72. package/dist/lib/node-esm/config/index.mjs.map +7 -0
  73. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  74. package/dist/lib/node-esm/core/capability.mjs +1 -1
  75. package/dist/lib/node-esm/core/plugin-manager.mjs +4 -4
  76. package/dist/lib/node-esm/core/plugin.mjs +12 -2
  77. package/dist/lib/node-esm/core/url-loader.mjs +3 -3
  78. package/dist/lib/node-esm/index.mjs +14 -10
  79. package/dist/lib/node-esm/index.mjs.map +1 -1
  80. package/dist/lib/node-esm/meta.json +1 -1
  81. package/dist/lib/node-esm/{process-manager-capability-TX5TA6YN.mjs → process-manager-capability-6OCXSKUW.mjs} +9 -9
  82. package/dist/lib/node-esm/testing/index.mjs +15 -15
  83. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  84. package/dist/lib/node-esm/testing/react.mjs +11 -11
  85. package/dist/lib/node-esm/testing/react.mjs.map +3 -3
  86. package/dist/lib/node-esm/ui/index.mjs +16 -10
  87. package/dist/plugin/node-esm/index.mjs +406 -71
  88. package/dist/plugin/node-esm/index.mjs.map +4 -4
  89. package/dist/plugin/node-esm/meta.json +1 -1
  90. package/dist/types/src/cli/cli.d.ts +1 -1
  91. package/dist/types/src/common/activation-events.d.ts.map +1 -1
  92. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  93. package/dist/types/src/config/index.d.ts +2 -0
  94. package/dist/types/src/config/index.d.ts.map +1 -0
  95. package/dist/types/src/core/activation-event.d.ts +9 -6
  96. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  97. package/dist/types/src/core/capability.d.ts +6 -2
  98. package/dist/types/src/core/capability.d.ts.map +1 -1
  99. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +6 -11
  100. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -1
  101. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  102. package/dist/types/src/core/plugin-manifest.d.ts +25 -7
  103. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -1
  104. package/dist/types/src/core/plugin.d.ts +81 -91
  105. package/dist/types/src/core/plugin.d.ts.map +1 -1
  106. package/dist/types/src/core/registry.d.ts +17 -57
  107. package/dist/types/src/core/registry.d.ts.map +1 -1
  108. package/dist/types/src/core/url-loader.d.ts +2 -2
  109. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  110. package/dist/types/src/index.d.ts +1 -0
  111. package/dist/types/src/index.d.ts.map +1 -1
  112. package/dist/types/src/plugin-process-manager/meta.d.ts +28 -2
  113. package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -1
  114. package/dist/types/src/testing/harness.d.ts +1 -1
  115. package/dist/types/src/testing/harness.d.ts.map +1 -1
  116. package/dist/types/src/testing/react.d.ts +6 -5
  117. package/dist/types/src/testing/react.d.ts.map +1 -1
  118. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  119. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  120. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts +6 -0
  121. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts.map +1 -1
  122. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +6 -10
  123. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  124. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  125. package/dist/types/src/ui/components/Surface/context.d.ts +6 -2
  126. package/dist/types/src/ui/components/Surface/context.d.ts.map +1 -1
  127. package/dist/types/src/ui/components/Surface/index.d.ts +10 -3
  128. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  129. package/dist/types/src/ui/components/Surface/types.d.ts +50 -70
  130. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  131. package/dist/types/src/ui/hooks/useApp.d.ts +1 -1
  132. package/dist/types/src/ui/hooks/useCapabilities.d.ts +11 -1
  133. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  134. package/dist/types/src/vite-plugin/composer/index.d.ts +11 -4
  135. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -1
  136. package/dist/types/src/vite-plugin/index.d.ts +1 -0
  137. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  138. package/dist/types/src/vite-plugin/load.d.ts +16 -0
  139. package/dist/types/src/vite-plugin/load.d.ts.map +1 -0
  140. package/dist/types/src/vite-plugin/manifest.d.ts +16 -4
  141. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -1
  142. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  143. package/dist/types/tsconfig.tsbuildinfo +1 -1
  144. package/docs/registry-spec.md +331 -0
  145. package/moon.yml +1 -0
  146. package/package.json +32 -27
  147. package/src/cli/cli.ts +2 -2
  148. package/src/common/activation-events.ts +3 -3
  149. package/src/common/capabilities.ts +16 -16
  150. package/src/config/index.ts +5 -0
  151. package/src/core/activation-event.ts +15 -8
  152. package/src/core/capability.ts +15 -4
  153. package/src/core/edge-registry-plugin-provider.ts +41 -42
  154. package/src/core/plugin-manager.test.ts +133 -137
  155. package/src/core/plugin-manager.ts +25 -19
  156. package/src/core/plugin-manifest.test.ts +5 -5
  157. package/src/core/plugin-manifest.ts +13 -10
  158. package/src/core/plugin.ts +58 -114
  159. package/src/core/registry.ts +20 -60
  160. package/src/core/url-loader.test.ts +2 -2
  161. package/src/core/url-loader.ts +23 -19
  162. package/src/index.ts +1 -0
  163. package/src/testing/harness.ts +5 -3
  164. package/src/testing/react.test.tsx +4 -2
  165. package/src/testing/react.tsx +9 -8
  166. package/src/testing/service.ts +2 -2
  167. package/src/testing/withPluginManager.stories.tsx +4 -2
  168. package/src/testing/withPluginManager.tsx +3 -1
  169. package/src/ui/components/PluginManager/PluginManagerProvider.ts +7 -0
  170. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +11 -14
  171. package/src/ui/components/Surface/SurfaceComponent.tsx +84 -74
  172. package/src/ui/components/Surface/context.ts +6 -3
  173. package/src/ui/components/Surface/index.ts +12 -1
  174. package/src/ui/components/Surface/types.test.ts +39 -49
  175. package/src/ui/components/Surface/types.ts +95 -60
  176. package/src/ui/hooks/useApp.test.tsx +1 -1
  177. package/src/ui/hooks/useApp.tsx +2 -2
  178. package/src/ui/hooks/useCapabilities.ts +24 -2
  179. package/src/vite-plugin/composer/index.ts +134 -61
  180. package/src/vite-plugin/index.ts +1 -0
  181. package/src/vite-plugin/load.ts +57 -0
  182. package/src/vite-plugin/manifest.test.ts +3 -3
  183. package/src/vite-plugin/manifest.ts +21 -10
  184. package/src/vite-plugin/packages.ts +0 -1
  185. package/dist/lib/browser/chunk-5GY3YOEL.mjs.map +0 -7
  186. package/dist/lib/browser/chunk-66IXTIVK.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-CO3XLNUX.mjs.map +0 -7
  188. package/dist/lib/browser/chunk-DC3WRPBV.mjs.map +0 -7
  189. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +0 -7
  190. package/dist/lib/browser/chunk-HFLKXMT7.mjs.map +0 -7
  191. package/dist/lib/browser/chunk-IW44C7UL.mjs.map +0 -7
  192. package/dist/lib/browser/chunk-KZUDO43J.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-XHS5KDNX.mjs.map +0 -7
  194. package/dist/lib/browser/chunk-Y66ELD4U.mjs.map +0 -7
  195. package/dist/lib/node-esm/chunk-A5CHGAZE.mjs.map +0 -7
  196. package/dist/lib/node-esm/chunk-CTKEZHKF.mjs.map +0 -7
  197. package/dist/lib/node-esm/chunk-FSVGKTF6.mjs.map +0 -7
  198. package/dist/lib/node-esm/chunk-KFZEB6BV.mjs.map +0 -7
  199. package/dist/lib/node-esm/chunk-NULBHQYS.mjs.map +0 -7
  200. package/dist/lib/node-esm/chunk-SX5NVETP.mjs.map +0 -7
  201. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +0 -7
  202. package/dist/lib/node-esm/chunk-XGBUKRCK.mjs.map +0 -7
  203. package/dist/lib/node-esm/chunk-XIGPWXCQ.mjs.map +0 -7
  204. package/dist/lib/node-esm/chunk-XOCUANHO.mjs.map +0 -7
  205. /package/dist/lib/browser/{capability-BOPAKKWG.mjs.map → capability-Z4MRP3CP.mjs.map} +0 -0
  206. /package/dist/lib/browser/{process-manager-capability-GXNUO7AR.mjs.map → process-manager-capability-RDROCCQD.mjs.map} +0 -0
  207. /package/dist/lib/node-esm/{capability-KZWRTX7R.mjs.map → capability-34Z6NPVR.mjs.map} +0 -0
  208. /package/dist/lib/node-esm/{process-manager-capability-TX5TA6YN.mjs.map → process-manager-capability-6OCXSKUW.mjs.map} +0 -0
@@ -2,16 +2,16 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { DXN } from '@dxos/keys';
5
6
  import { compositeKey } from '@dxos/util';
6
7
 
7
8
  /**
8
- * A unique string identifier representing an event.
9
- * This is expected to be a URI, where initial parts are often the id of the plugin whose package defines the event.
9
+ * A unique DXN identifier representing an event.
10
10
  *
11
- * @example org.dxos.plugin.example.event.ready
11
+ * @example dxn:org.dxos.plugin.example.event.ready
12
12
  */
13
13
  export type ActivationEvent = {
14
- id: string;
14
+ id: DXN.DXN;
15
15
  specifier?: string;
16
16
  };
17
17
 
@@ -24,11 +24,18 @@ export type Events =
24
24
  | { type: 'all-of'; events: ActivationEvent[] };
25
25
 
26
26
  /**
27
- * Helper to define an activation event.
27
+ * Helper to define an activation event from an NSID.
28
+ * Static NSID strings are validated at compile time via {@link DXN.Name}.
28
29
  */
29
- export const make = (id: string, specifier?: string) => {
30
- return { id, specifier } as ActivationEvent;
31
- };
30
+ export const make: {
31
+ <T extends string>(
32
+ nsid: [DXN.Name<T>] extends [never] ? `Invalid NSID "${T}": final segment must be camelCase (no hyphens)` : T,
33
+ specifier?: string,
34
+ ): ActivationEvent;
35
+ } = (nsid: string, specifier?: string): ActivationEvent => ({
36
+ id: DXN.make(nsid),
37
+ specifier,
38
+ });
32
39
 
33
40
  /**
34
41
  * Helper to create an activation event key.
@@ -7,6 +7,8 @@ import * as Context from 'effect/Context';
7
7
  import * as Effect from 'effect/Effect';
8
8
  import * as Layer from 'effect/Layer';
9
9
 
10
+ import type { DXN } from '@dxos/keys';
11
+
10
12
  import type * as CapabilityManager from './capability-manager';
11
13
  import type * as Plugin from './plugin';
12
14
 
@@ -100,8 +102,13 @@ export namespace InterfaceDef {
100
102
 
101
103
  /**
102
104
  * Helper to define the interface of a capability.
105
+ * Static NSID strings are validated at compile time via {@link DXN.Name}.
103
106
  */
104
- export const make = <T>(identifier: string) => {
107
+ export const make: {
108
+ <T, S extends string = string>(
109
+ identifier: [DXN.Name<S>] extends [never] ? `Invalid NSID "${S}": final segment must be camelCase (no hyphens)` : S,
110
+ ): InterfaceDef<T>;
111
+ } = <T>(identifier: string): InterfaceDef<T> => {
105
112
  return { identifier } as InterfaceDef<T>;
106
113
  };
107
114
 
@@ -179,8 +186,8 @@ export type LazyCapability<Props = void, Capabilities extends ModuleReturn = Mod
179
186
  export const lazy = <T = void, R extends ModuleReturn = ModuleReturn>(
180
187
  name: string,
181
188
  c: LoadCapability<T, R> | LoadCapabilities<T, R>,
182
- ): LazyCapability<T, R> => {
183
- const lazyFn: LazyCapability<T, R> = (props: T) =>
189
+ ): LazyCapability<T> => {
190
+ const lazyFn = (props: T) =>
184
191
  Effect.gen(function* () {
185
192
  const { default: getCapability } = yield* Effect.promise(() => c());
186
193
  const result = yield* getCapability(props);
@@ -188,7 +195,11 @@ export const lazy = <T = void, R extends ModuleReturn = ModuleReturn>(
188
195
  return normalized as NormalizeReturn<R>;
189
196
  });
190
197
 
191
- return Object.assign(lazyFn, { [ModuleTag]: name });
198
+ // Props (T) are preserved so callers pass correctly-typed options, but the contributed
199
+ // Capabilities type is widened to the opaque base. The concrete capability type often traces to a
200
+ // module-internal source path that TypeScript cannot name in declaration files (TS2883); the base
201
+ // type is portable. The contributed type is checked at Capability.contributes regardless.
202
+ return Object.assign(lazyFn, { [ModuleTag]: name }) as LazyCapability<T>;
192
203
  };
193
204
 
194
205
  /**
@@ -6,31 +6,33 @@ import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Context } from '@dxos/context';
8
8
  import { type EdgeHttpClient } from '@dxos/edge-client';
9
- import { type PluginEntry } from '@dxos/protocols';
9
+ import { type PluginView } from '@dxos/protocols';
10
10
 
11
+ import type * as Plugin from './plugin';
11
12
  import * as Registry from './registry';
12
13
 
13
14
  /**
14
- * Maps a wire-format `PluginEntry` (from `@dxos/protocols`) to a
15
- * `Registry.Plugin` (the app-framework domain type).
15
+ * Maps a wire-format `PluginView` (from `@dxos/protocols`) to a runtime `Plugin.Meta`: the nested
16
+ * `profile` (the view's profile plus the resolved provenance `author`) and the latest `release`.
16
17
  *
17
- * This is the only translation seam between the two independently-defined type
18
- * hierarchies fields are mapped explicitly, with no shared type between them.
18
+ * This is the one translation seam between the nested wire view and the resolved runtime manifest.
19
+ * `profile.key` is the composer plugin id a valid NSID so `DXN.make(profile.key, release.version)`
20
+ * reconstructs the canonical plugin DXN.
19
21
  */
20
- const toRegistryPlugin = (entry: PluginEntry): Registry.Plugin => ({
21
- id: entry.meta.id,
22
- name: entry.meta.name,
23
- description: entry.meta.description,
24
- homePage: entry.meta.homePage,
25
- source: entry.meta.source,
26
- screenshots: entry.meta.screenshots ? [...entry.meta.screenshots] : undefined,
27
- tags: entry.meta.tags ? [...entry.meta.tags] : undefined,
28
- icon: entry.meta.icon,
29
- iconHue: entry.meta.iconHue,
30
- moduleUrl: entry.moduleUrl,
31
- repo: entry.repo,
32
- version: entry.releaseTag,
33
- });
22
+ const toRegistryPlugin = (entry: PluginView): Plugin.Meta | null => {
23
+ if (!entry.releases?.length) {
24
+ return null;
25
+ }
26
+ const latestRelease = entry.releases.find((release) => release.version === entry.latestVersion) ?? entry.releases[0];
27
+ return {
28
+ profile: {
29
+ ...entry.profile,
30
+ // Provenance: the verified publisher is the author (handle preferred, DID fallback).
31
+ author: entry.handle ?? entry.did,
32
+ },
33
+ release: latestRelease,
34
+ };
35
+ };
34
36
 
35
37
  /**
36
38
  * Implements `Registry.PluginProvider` against the Edge `/registry` HTTP endpoints.
@@ -41,51 +43,48 @@ const toRegistryPlugin = (entry: PluginEntry): Registry.Plugin => ({
41
43
  * cycle. The class needs only the public `EdgeHttpClient` type, so a one-way
42
44
  * type-import is enough.
43
45
  *
44
- * `listVersions` is currently a stub: it returns the single latest version derived
45
- * from the cached plugin list, so the host's version picker has something to render.
46
- * The wire contract for the real endpoint already exists — see
47
- * `GetPluginVersionsResponseBodySchema` in `@dxos/protocols/edge/registry` and
48
- * {@link EdgeHttpClient.getRegistryPluginVersions} — so once Edge ships
49
- * `GET /registry/plugins/:repo/versions`, swap this stub for a call to
50
- * `this._client.getRegistryPluginVersions(...)` and map each entry through a
51
- * `toRegistryPluginVersion` helper (mirror of {@link toRegistryPlugin}).
46
+ * `listVersions` is served directly from the `releases` array inlined on each
47
+ * entry no separate endpoint is needed.
52
48
  */
53
49
  export class EdgeRegistryPluginProvider implements Registry.PluginProvider {
54
50
  // Cached on first load so getPlugin/listVersions can resolve without re-fetching.
55
- #cachedPlugins: readonly Registry.Plugin[] = [];
51
+ #cachedPlugins: readonly Plugin.Meta[] = [];
52
+ #cachedEntries: readonly PluginView[] = [];
56
53
 
57
54
  constructor(private readonly _client: EdgeHttpClient) {}
58
55
 
59
- listPlugins(): Effect.Effect<readonly Registry.Plugin[], Error> {
56
+ listPlugins(): Effect.Effect<readonly Plugin.Meta[], Error> {
60
57
  return Effect.tryPromise({
61
58
  try: () => this._client.getRegistryPlugins(Context.default()),
62
59
  catch: (error) => (error instanceof Error ? error : new Error(String(error))),
63
60
  }).pipe(
64
61
  Effect.map((body) => {
65
- const plugins = body.plugins.filter((entry) => entry.health === 'ok').map(toRegistryPlugin);
62
+ this.#cachedEntries = body.plugins;
63
+ const plugins = body.plugins.map(toRegistryPlugin).filter((entry): entry is Plugin.Meta => entry !== null);
66
64
  this.#cachedPlugins = plugins;
67
65
  return plugins;
68
66
  }),
69
67
  );
70
68
  }
71
69
 
72
- listVersions(repo: string): Effect.Effect<readonly Registry.PluginVersion[], Error> {
73
- // Stub: return only the currently-known version until Edge implements the versions endpoint.
74
- const plugin = this.#cachedPlugins.find((candidate) => candidate.repo === repo);
75
- if (!plugin) {
76
- return Effect.fail(new Error(`Plugin not found in catalog: ${repo}`));
70
+ listVersions(id: string): Effect.Effect<readonly Plugin.Release[], Error> {
71
+ const entry = this.#cachedEntries.find((candidate) => candidate.profile.key === id);
72
+ if (!entry) {
73
+ return Effect.fail(new Error(`Plugin not found in catalog: ${id}`));
77
74
  }
78
- const version: Registry.PluginVersion = { tag: plugin.version, moduleUrl: plugin.moduleUrl };
79
- return Effect.succeed([version]);
75
+ // Releases are already `PluginRelease`-shaped on the wire view; serve them directly.
76
+ return Effect.succeed(entry.releases);
80
77
  }
81
78
 
82
- getPlugin(repo: string, version?: string): Effect.Effect<Registry.Plugin, Error> {
83
- const plugin = this.#cachedPlugins.find((p) => p.repo === repo);
79
+ getPlugin(id: string, version?: string): Effect.Effect<Plugin.Meta, Error> {
80
+ const plugin = this.#cachedPlugins.find((candidate) => candidate.profile.key === id);
84
81
  if (!plugin) {
85
- return Effect.fail(new Error(`Plugin not found in catalog: ${repo}`));
82
+ return Effect.fail(new Error(`Plugin not found in catalog: ${id}`));
86
83
  }
87
- if (version && version !== plugin.version) {
88
- return Effect.fail(new Error(`Version ${version} not available for ${repo}; only ${plugin.version} is cached`));
84
+ if (version && version !== plugin.release?.version) {
85
+ return Effect.fail(
86
+ new Error(`Version ${version} not available for ${id}; only ${plugin.release?.version} is cached`),
87
+ );
89
88
  }
90
89
  return Effect.succeed(plugin);
91
90
  }