@dxos/app-framework 0.9.0 → 0.9.1-staging.ee54ba693a

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 (224) hide show
  1. package/dist/lib/browser/{capability-BOPAKKWG.mjs → capability-S44TE2H7.mjs} +9 -9
  2. package/dist/lib/browser/{chunk-66IXTIVK.mjs → chunk-3NQLTQCP.mjs} +6 -7
  3. package/dist/lib/browser/chunk-3NQLTQCP.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-53HJHMS5.mjs +7 -0
  5. package/dist/lib/browser/chunk-53HJHMS5.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-Y66ELD4U.mjs → chunk-KRJ4KK2W.mjs} +36 -7
  7. package/dist/lib/browser/chunk-KRJ4KK2W.mjs.map +7 -0
  8. package/dist/lib/browser/{chunk-KZUDO43J.mjs → chunk-LUH4Y5F2.mjs} +31 -23
  9. package/dist/lib/browser/chunk-LUH4Y5F2.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-CO3XLNUX.mjs → chunk-NKVRSMFN.mjs} +18 -18
  11. package/dist/lib/browser/chunk-NKVRSMFN.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-DC3WRPBV.mjs → chunk-OO53M5UK.mjs} +45 -12
  13. package/dist/lib/browser/chunk-OO53M5UK.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-IW44C7UL.mjs → chunk-OPTS3ZSN.mjs} +1 -1
  15. package/dist/lib/browser/chunk-OPTS3ZSN.mjs.map +7 -0
  16. package/dist/lib/browser/{chunk-HFLKXMT7.mjs → chunk-U5MESPM5.mjs} +61 -45
  17. package/dist/lib/browser/chunk-U5MESPM5.mjs.map +7 -0
  18. package/dist/lib/browser/{chunk-XHS5KDNX.mjs → chunk-UGYCLOXE.mjs} +25 -25
  19. package/dist/lib/browser/chunk-UGYCLOXE.mjs.map +7 -0
  20. package/dist/lib/browser/{chunk-5GY3YOEL.mjs → chunk-VF37YTXJ.mjs} +5 -5
  21. package/dist/lib/browser/chunk-VF37YTXJ.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-QK4BMKMN.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 +18 -10
  44. package/dist/lib/node-esm/{capability-KZWRTX7R.mjs → capability-3YRF77LV.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-XIGPWXCQ.mjs → chunk-4SE7SX7N.mjs} +45 -12
  48. package/dist/lib/node-esm/chunk-4SE7SX7N.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-XOCUANHO.mjs → chunk-BMW6P6QA.mjs} +6 -7
  50. package/dist/lib/node-esm/chunk-BMW6P6QA.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-A5CHGAZE.mjs → chunk-DRT75WGD.mjs} +61 -45
  52. package/dist/lib/node-esm/chunk-DRT75WGD.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-XGBUKRCK.mjs → chunk-FKMVORZN.mjs} +31 -23
  54. package/dist/lib/node-esm/chunk-FKMVORZN.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-CTKEZHKF.mjs → chunk-GLH4LGHZ.mjs} +1 -1
  56. package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-SX5NVETP.mjs → chunk-IL7O7IRX.mjs} +25 -25
  58. package/dist/lib/node-esm/chunk-IL7O7IRX.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-FSVGKTF6.mjs → chunk-ND2HETHD.mjs} +36 -7
  60. package/dist/lib/node-esm/chunk-ND2HETHD.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-KFZEB6BV.mjs → chunk-TLKYZKPW.mjs} +5 -5
  62. package/dist/lib/node-esm/chunk-TLKYZKPW.mjs.map +7 -0
  63. package/dist/lib/node-esm/{chunk-WK7OIQKI.mjs → chunk-VCPGX7WN.mjs} +29 -37
  64. package/dist/lib/node-esm/chunk-VCPGX7WN.mjs.map +7 -0
  65. package/dist/lib/node-esm/{chunk-NULBHQYS.mjs → chunk-XSZMX53L.mjs} +18 -18
  66. package/dist/lib/node-esm/chunk-XSZMX53L.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-5S7WZ2JR.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 +18 -10
  87. package/dist/plugin/node-esm/index.mjs +414 -81
  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 +2 -2
  93. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  94. package/dist/types/src/config/index.d.ts +2 -0
  95. package/dist/types/src/config/index.d.ts.map +1 -0
  96. package/dist/types/src/core/activation-event.d.ts +9 -6
  97. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  98. package/dist/types/src/core/capability.d.ts +9 -3
  99. package/dist/types/src/core/capability.d.ts.map +1 -1
  100. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +6 -11
  101. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -1
  102. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  103. package/dist/types/src/core/plugin-manifest.d.ts +25 -7
  104. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -1
  105. package/dist/types/src/core/plugin.d.ts +89 -91
  106. package/dist/types/src/core/plugin.d.ts.map +1 -1
  107. package/dist/types/src/core/registry.d.ts +17 -57
  108. package/dist/types/src/core/registry.d.ts.map +1 -1
  109. package/dist/types/src/core/url-loader.d.ts +2 -2
  110. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  111. package/dist/types/src/index.d.ts +1 -0
  112. package/dist/types/src/index.d.ts.map +1 -1
  113. package/dist/types/src/plugin-process-manager/history/capability.d.ts +1 -3
  114. package/dist/types/src/plugin-process-manager/history/capability.d.ts.map +1 -1
  115. package/dist/types/src/plugin-process-manager/meta.d.ts +32 -2
  116. package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -1
  117. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +1 -4
  118. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
  119. package/dist/types/src/testing/harness.d.ts +1 -1
  120. package/dist/types/src/testing/harness.d.ts.map +1 -1
  121. package/dist/types/src/testing/react.d.ts +6 -5
  122. package/dist/types/src/testing/react.d.ts.map +1 -1
  123. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  124. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  125. package/dist/types/src/ui/components/App/App.stories.d.ts +2 -2
  126. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  127. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts +6 -0
  128. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts.map +1 -1
  129. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +6 -10
  130. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  131. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  132. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts +1 -1
  133. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  134. package/dist/types/src/ui/components/Surface/context.d.ts +6 -2
  135. package/dist/types/src/ui/components/Surface/context.d.ts.map +1 -1
  136. package/dist/types/src/ui/components/Surface/index.d.ts +11 -4
  137. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  138. package/dist/types/src/ui/components/Surface/types.d.ts +52 -72
  139. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  140. package/dist/types/src/ui/hooks/useApp.d.ts +1 -1
  141. package/dist/types/src/ui/hooks/useCapabilities.d.ts +17 -1
  142. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  143. package/dist/types/src/vite-plugin/composer/index.d.ts +11 -4
  144. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -1
  145. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -1
  146. package/dist/types/src/vite-plugin/index.d.ts +1 -0
  147. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  148. package/dist/types/src/vite-plugin/load.d.ts +16 -0
  149. package/dist/types/src/vite-plugin/load.d.ts.map +1 -0
  150. package/dist/types/src/vite-plugin/manifest.d.ts +16 -4
  151. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -1
  152. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  153. package/dist/types/tsconfig.tsbuildinfo +1 -1
  154. package/docs/registry-spec.md +331 -0
  155. package/moon.yml +1 -0
  156. package/package.json +50 -39
  157. package/src/cli/cli.ts +2 -2
  158. package/src/common/activation-events.ts +3 -3
  159. package/src/common/capabilities.ts +18 -18
  160. package/src/config/index.ts +5 -0
  161. package/src/core/activation-event.ts +15 -8
  162. package/src/core/capability.ts +18 -6
  163. package/src/core/edge-registry-plugin-provider.ts +41 -42
  164. package/src/core/plugin-manager.test.ts +133 -137
  165. package/src/core/plugin-manager.ts +25 -19
  166. package/src/core/plugin-manifest.test.ts +5 -5
  167. package/src/core/plugin-manifest.ts +13 -10
  168. package/src/core/plugin.ts +58 -114
  169. package/src/core/registry.ts +20 -60
  170. package/src/core/url-loader.test.ts +2 -2
  171. package/src/core/url-loader.ts +23 -19
  172. package/src/index.ts +1 -0
  173. package/src/testing/harness.ts +5 -3
  174. package/src/testing/react.test.tsx +4 -2
  175. package/src/testing/react.tsx +10 -9
  176. package/src/testing/service.ts +2 -2
  177. package/src/testing/withPluginManager.stories.tsx +4 -2
  178. package/src/testing/withPluginManager.tsx +3 -1
  179. package/src/ui/components/App/App.stories.tsx +2 -2
  180. package/src/ui/components/PluginManager/PluginManagerProvider.ts +7 -0
  181. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +22 -22
  182. package/src/ui/components/Surface/SurfaceComponent.tsx +90 -79
  183. package/src/ui/components/Surface/SurfaceProfilerContext.tsx +1 -1
  184. package/src/ui/components/Surface/context.ts +6 -4
  185. package/src/ui/components/Surface/index.ts +14 -3
  186. package/src/ui/components/Surface/types.test.ts +43 -51
  187. package/src/ui/components/Surface/types.ts +99 -64
  188. package/src/ui/hooks/useApp.test.tsx +1 -1
  189. package/src/ui/hooks/useApp.tsx +2 -2
  190. package/src/ui/hooks/useCapabilities.ts +49 -2
  191. package/src/vite-plugin/boot-loader/BootLoader.solid-stories.tsx +11 -11
  192. package/src/vite-plugin/boot-loader/loader.ts +3 -3
  193. package/src/vite-plugin/composer/index.ts +134 -61
  194. package/src/vite-plugin/import-map/index.ts +23 -7
  195. package/src/vite-plugin/index.ts +1 -0
  196. package/src/vite-plugin/load.ts +57 -0
  197. package/src/vite-plugin/manifest.test.ts +3 -3
  198. package/src/vite-plugin/manifest.ts +21 -10
  199. package/src/vite-plugin/packages.ts +0 -2
  200. package/tsconfig.json +5 -2
  201. package/dist/lib/browser/chunk-5GY3YOEL.mjs.map +0 -7
  202. package/dist/lib/browser/chunk-66IXTIVK.mjs.map +0 -7
  203. package/dist/lib/browser/chunk-CO3XLNUX.mjs.map +0 -7
  204. package/dist/lib/browser/chunk-DC3WRPBV.mjs.map +0 -7
  205. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +0 -7
  206. package/dist/lib/browser/chunk-HFLKXMT7.mjs.map +0 -7
  207. package/dist/lib/browser/chunk-IW44C7UL.mjs.map +0 -7
  208. package/dist/lib/browser/chunk-KZUDO43J.mjs.map +0 -7
  209. package/dist/lib/browser/chunk-XHS5KDNX.mjs.map +0 -7
  210. package/dist/lib/browser/chunk-Y66ELD4U.mjs.map +0 -7
  211. package/dist/lib/node-esm/chunk-A5CHGAZE.mjs.map +0 -7
  212. package/dist/lib/node-esm/chunk-CTKEZHKF.mjs.map +0 -7
  213. package/dist/lib/node-esm/chunk-FSVGKTF6.mjs.map +0 -7
  214. package/dist/lib/node-esm/chunk-KFZEB6BV.mjs.map +0 -7
  215. package/dist/lib/node-esm/chunk-NULBHQYS.mjs.map +0 -7
  216. package/dist/lib/node-esm/chunk-SX5NVETP.mjs.map +0 -7
  217. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +0 -7
  218. package/dist/lib/node-esm/chunk-XGBUKRCK.mjs.map +0 -7
  219. package/dist/lib/node-esm/chunk-XIGPWXCQ.mjs.map +0 -7
  220. package/dist/lib/node-esm/chunk-XOCUANHO.mjs.map +0 -7
  221. /package/dist/lib/browser/{capability-BOPAKKWG.mjs.map → capability-S44TE2H7.mjs.map} +0 -0
  222. /package/dist/lib/browser/{process-manager-capability-GXNUO7AR.mjs.map → process-manager-capability-QK4BMKMN.mjs.map} +0 -0
  223. /package/dist/lib/node-esm/{capability-KZWRTX7R.mjs.map → capability-3YRF77LV.mjs.map} +0 -0
  224. /package/dist/lib/node-esm/{process-manager-capability-TX5TA6YN.mjs.map → process-manager-capability-5S7WZ2JR.mjs.map} +0 -0
@@ -59,7 +59,7 @@ export type PreloadOptions = Options & {
59
59
  *
60
60
  * `url` is the URL of the plugin manifest (`plugin.json`), not the entry module.
61
61
  */
62
- export type RemotePluginEntry = {
62
+ export type RemotePluginView = {
63
63
  id: string;
64
64
  url: string;
65
65
  /** Installed version string, e.g. `v1.0.0`. Populated after install from a community catalog entry. */
@@ -71,22 +71,26 @@ const defaultStorage = (): Storage => ({
71
71
  set: (key, value) => localStorage.setItem(key, value),
72
72
  });
73
73
 
74
- const getPersistedRemotePlugins = (storage: Storage, key: string): RemotePluginEntry[] => {
74
+ const getPersistedRemotePlugins = (storage: Storage, key: string): RemotePluginView[] => {
75
75
  try {
76
76
  const parsed: unknown = JSON.parse(storage.get(key) ?? '[]');
77
77
  if (!Array.isArray(parsed)) {
78
78
  return [];
79
79
  }
80
80
  return parsed.filter(
81
- (entry): entry is RemotePluginEntry =>
82
- typeof entry === 'object' && entry !== null && typeof entry.id === 'string' && typeof entry.url === 'string',
81
+ (entry): entry is RemotePluginView =>
82
+ typeof entry === 'object' &&
83
+ entry !== null &&
84
+ typeof entry.id === 'string' &&
85
+ typeof entry.url === 'string' &&
86
+ (entry.version === undefined || typeof entry.version === 'string'),
83
87
  );
84
88
  } catch {
85
89
  return [];
86
90
  }
87
91
  };
88
92
 
89
- const persistRemotePlugin = (storage: Storage, key: string, entry: RemotePluginEntry): void => {
93
+ const persistRemotePlugin = (storage: Storage, key: string, entry: RemotePluginView): void => {
90
94
  try {
91
95
  const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== entry.id);
92
96
  entries.push(entry);
@@ -132,7 +136,7 @@ export const isLocalUrl = (locator: string): boolean => {
132
136
  * Useful for UI code that needs to know which loaded plugins were installed from a URL
133
137
  * (e.g. to surface a tag on remote or localhost-hosted plugins).
134
138
  */
135
- export const getRemoteEntries = (options: Options = {}): readonly RemotePluginEntry[] => {
139
+ export const getRemoteEntries = (options: Options = {}): readonly RemotePluginView[] => {
136
140
  const storage = options.storage ?? defaultStorage();
137
141
  const key = options.key ?? DEFAULT_KEY;
138
142
  return getPersistedRemotePlugins(storage, key);
@@ -196,14 +200,14 @@ const loadStylesheets = (
196
200
  }
197
201
  const cssUrls = manifest.assetUrls.filter((url) => url.endsWith('.css'));
198
202
  for (const url of cssUrls) {
199
- const resolved = yield* cache.resolve(manifest.id, url);
200
- if (document.querySelector(`link[data-dxos-plugin-id="${manifest.id}"][href="${resolved}"]`)) {
203
+ const resolved = yield* cache.resolve(manifest.key, url);
204
+ if (document.querySelector(`link[data-dxos-plugin-id="${manifest.key}"][href="${resolved}"]`)) {
201
205
  continue;
202
206
  }
203
207
  const link = document.createElement('link');
204
208
  link.rel = 'stylesheet';
205
209
  link.href = resolved;
206
- link.dataset.dxosPluginId = manifest.id;
210
+ link.dataset.dxosPluginId = manifest.key;
207
211
  document.head.appendChild(link);
208
212
  }
209
213
  });
@@ -233,30 +237,30 @@ const loadFromManifest = (
233
237
  // plugin's host is offline, dropping the plugin from the runtime.
234
238
  const cachedUrls =
235
239
  manifest.assetUrls.indexOf(manifestUrl) === -1 ? [manifestUrl, ...manifest.assetUrls] : manifest.assetUrls;
236
- yield* cache.cache(manifest.id, cachedUrls).pipe(wrapCacheError);
240
+ yield* cache.cache(manifest.key, cachedUrls).pipe(wrapCacheError);
237
241
  }
238
242
  const entryUrl = manifest.dev
239
243
  ? manifest.entryUrl
240
- : yield* cache.resolve(manifest.id, manifest.entryUrl).pipe(wrapCacheError);
244
+ : yield* cache.resolve(manifest.key, manifest.entryUrl).pipe(wrapCacheError);
241
245
  const mod = yield* Effect.tryPromise({
242
246
  try: () => import(/* @vite-ignore */ entryUrl),
243
247
  catch: (cause) =>
244
248
  new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'import-failed' }, cause }),
245
249
  });
246
250
  const plugin = normalizePluginExport(mod);
247
- if (!plugin.meta.id || !plugin.meta.name) {
251
+ if (!plugin.meta.profile.key || !plugin.meta.profile.name) {
248
252
  return yield* Effect.fail(
249
253
  new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'meta-missing' } }),
250
254
  );
251
255
  }
252
- if (plugin.meta.id !== manifest.id) {
256
+ if (plugin.meta.profile.key !== manifest.key) {
253
257
  return yield* Effect.fail(
254
258
  new RemotePluginLoadError({
255
259
  context: {
256
260
  locator: manifestUrl,
257
261
  reason: 'meta-mismatch',
258
- metaId: plugin.meta.id,
259
- manifestId: manifest.id,
262
+ metaId: plugin.meta.profile.key,
263
+ manifestId: manifest.key,
260
264
  },
261
265
  }),
262
266
  );
@@ -337,7 +341,7 @@ export const make = (builtinPlugins: Plugin.Plugin[], options: Options = {}) =>
337
341
 
338
342
  return (locator: string): Effect.Effect<{ plugin: Plugin.Plugin; dev?: boolean }, RemotePluginLoadError> =>
339
343
  Effect.gen(function* () {
340
- const builtin = builtinPlugins.find((plugin) => plugin.meta.id === locator);
344
+ const builtin = builtinPlugins.find((plugin) => plugin.meta.profile.key === locator);
341
345
  if (builtin) {
342
346
  return { plugin: builtin };
343
347
  }
@@ -351,13 +355,13 @@ export const make = (builtinPlugins: Plugin.Plugin[], options: Options = {}) =>
351
355
  // overlap (dev plugin takes the id slot for the session, original is
352
356
  // restored on uninstall or page reload).
353
357
  if (!manifest.dev) {
354
- const duplicate = builtinPlugins.find((existing) => existing.meta.id === plugin.meta.id);
358
+ const duplicate = builtinPlugins.find((existing) => existing.meta.profile.key === plugin.meta.profile.key);
355
359
  if (duplicate) {
356
360
  return yield* Effect.fail(
357
- new RemotePluginLoadError({ context: { locator, reason: 'duplicate-id', id: plugin.meta.id } }),
361
+ new RemotePluginLoadError({ context: { locator, reason: 'duplicate-id', id: plugin.meta.profile.key } }),
358
362
  );
359
363
  }
360
- persistRemotePlugin(storage, key, { id: plugin.meta.id, url: locator });
364
+ persistRemotePlugin(storage, key, { id: plugin.meta.profile.key, url: locator });
361
365
  }
362
366
  return { plugin, dev: manifest.dev };
363
367
  });
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './common';
6
+ export * from './config';
6
7
  export * from './context';
7
8
  export * from './core';
8
9
  export * from './plugin-process-manager';
@@ -17,7 +17,7 @@ import { ActivationEvent, type Capability, type CapabilityManager, type Plugin,
17
17
 
18
18
  export type TestAppOptions = {
19
19
  /**
20
- * Plugins to register. Plugins whose `meta.tags` includes `'system'` are treated as core
20
+ * Plugins to register. Plugins whose `meta.profile.tags` includes `'system'` are treated as core
21
21
  * (force-enabled). For test convenience, plugins without a `'system'` tag are enabled by
22
22
  * default unless `enabled` is provided.
23
23
  */
@@ -97,7 +97,9 @@ const DEFAULT_TIMEOUT_MS = 5_000;
97
97
  export const createTestApp = async (opts: TestAppOptions): Promise<TestHarness> => {
98
98
  const {
99
99
  plugins,
100
- enabled = plugins.filter(({ meta }) => !meta.tags?.includes('system')).map((plugin) => plugin.meta.id),
100
+ enabled = plugins
101
+ .filter(({ meta }) => !meta.profile.tags?.includes('system'))
102
+ .map((plugin) => plugin.meta.profile.key),
101
103
  setupEvents = [],
102
104
  autoStart = true,
103
105
  registerFrameworkCapabilities = true,
@@ -105,7 +107,7 @@ export const createTestApp = async (opts: TestAppOptions): Promise<TestHarness>
105
107
 
106
108
  const pluginLoader = (id: string) =>
107
109
  Effect.sync(() => {
108
- const plugin = plugins.find((plugin) => plugin.meta.id === id);
110
+ const plugin = plugins.find((plugin) => plugin.meta.profile.key === id);
109
111
  invariant(plugin, `Plugin not found: ${id}`);
110
112
  return { plugin };
111
113
  });
@@ -16,6 +16,8 @@ import { render, renderSurface } from './react';
16
16
 
17
17
  const testMeta = Plugin.makeMeta({ key: DXN.make('org.dxos.plugin.test.reactHarness'), name: 'ReactHarnessTest' });
18
18
 
19
+ const GreetingRole = Surface.makeType<{ message: string }>('org.dxos.test.role.greeting');
20
+
19
21
  const TestPlugin = Plugin.define(testMeta).pipe(
20
22
  Plugin.addModule({
21
23
  id: 'surfaces',
@@ -25,7 +27,7 @@ const TestPlugin = Plugin.define(testMeta).pipe(
25
27
  Capability.contributes(Capabilities.ReactSurface, [
26
28
  Surface.create<{ message: string }>({
27
29
  id: 'greeting',
28
- role: 'greeting',
30
+ filter: Surface.makeFilter(GreetingRole),
29
31
  component: ({ data }) => <span data-testid='greeting'>hello {data.message}</span>,
30
32
  }),
31
33
  ]),
@@ -43,7 +45,7 @@ describe('testing/react', () => {
43
45
 
44
46
  test('renderSurface mounts a surface registered by a plugin', async ({ expect }) => {
45
47
  await using harness = await createTestApp({ plugins: [TestPlugin()] });
46
- const view = renderSurface(harness, { role: 'greeting', data: { message: 'plugins' } });
48
+ const view = renderSurface(harness, { type: GreetingRole, data: { message: 'plugins' } });
47
49
  const node = await view.findByTestId('greeting');
48
50
  expect(node.textContent).toBe('hello plugins');
49
51
  });
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { RegistryContext } from '@effect-atom/atom-react';
6
- import { render as rtlRender, type RenderOptions, type RenderResult } from '@testing-library/react';
6
+ import { type RenderOptions, type RenderResult, render as rtlRender } from '@testing-library/react';
7
7
  import React, { type FC, Fragment, type PropsWithChildren, type ReactNode } from 'react';
8
8
 
9
9
  import { ContextProtocolProvider } from '@dxos/web-context-react';
@@ -13,6 +13,7 @@ import { PluginManagerContext } from '../context';
13
13
  import { topologicalSort } from '../helpers';
14
14
  import { PluginManagerProvider } from '../ui/components/PluginManager/PluginManagerProvider';
15
15
  import { SurfaceComponent } from '../ui/components/Surface/SurfaceComponent';
16
+ import { type RoleToken } from '../ui/components/Surface/types';
16
17
  import { type TestHarness } from './harness';
17
18
 
18
19
  export type HarnessRenderOptions = Omit<RenderOptions, 'wrapper'> & {
@@ -34,26 +35,26 @@ export const render = (harness: TestHarness, ui: ReactNode, options?: HarnessRen
34
35
  return rtlRender(<>{ui}</>, { ...rest, wrapper: Wrapper });
35
36
  };
36
37
 
37
- export type RenderSurfaceProps = {
38
- role: string;
39
- data?: unknown;
38
+ export type RenderSurfaceProps<TToken extends RoleToken<any>> = {
39
+ type: TToken;
40
+ data?: TToken extends RoleToken<infer D> ? D : never;
40
41
  limit?: number;
41
42
  fallback?: FC<{ error: Error; data?: any }>;
42
43
  placeholder?: ReactNode;
43
44
  };
44
45
 
45
46
  /**
46
- * Renders a `Surface` with the given role/data inside the harness provider tree.
47
+ * Renders a `Surface` with the given role token/data inside the harness provider tree.
47
48
  */
48
- export const renderSurface = (
49
+ export const renderSurface = <TToken extends RoleToken<any>>(
49
50
  harness: TestHarness,
50
- props: RenderSurfaceProps,
51
+ props: RenderSurfaceProps<TToken>,
51
52
  options?: HarnessRenderOptions,
52
53
  ): RenderResult => {
53
- const { role, data, limit, fallback, placeholder } = props;
54
+ const { type, data, limit, fallback, placeholder } = props;
54
55
  return render(
55
56
  harness,
56
- <SurfaceComponent role={role} data={data as any} limit={limit} fallback={fallback} placeholder={placeholder} />,
57
+ <SurfaceComponent type={type} data={data as any} limit={limit} fallback={fallback} placeholder={placeholder} />,
57
58
  options,
58
59
  );
59
60
  };
@@ -22,7 +22,7 @@ export const fromPlugins = (plugins: Plugin.Plugin[]) =>
22
22
 
23
23
  const pluginLoader = (id: string) =>
24
24
  Effect.sync(() => {
25
- const plugin = plugins.find((plugin) => plugin.meta.id === id);
25
+ const plugin = plugins.find((plugin) => plugin.meta.profile.key === id);
26
26
  invariant(plugin, `Plugin not found: ${id}`);
27
27
  return { plugin };
28
28
  });
@@ -30,7 +30,7 @@ export const fromPlugins = (plugins: Plugin.Plugin[]) =>
30
30
  const manager = PluginManager.make({
31
31
  pluginLoader,
32
32
  plugins,
33
- enabled: plugins.map((plugin) => plugin.meta.id),
33
+ enabled: plugins.map((plugin) => plugin.meta.profile.key),
34
34
  });
35
35
 
36
36
  manager.capabilities.contribute({
@@ -12,12 +12,14 @@ import { Capability } from '../core';
12
12
  import { Surface } from '../ui';
13
13
  import { withPluginManager } from './withPluginManager';
14
14
 
15
+ const MainRole = Surface.makeType<Record<string, never>>('org.dxos.test.role.main');
16
+
15
17
  const DefaultStory = () => {
16
18
  console.log('Render');
17
19
  return (
18
20
  <div>
19
21
  <div>Hello</div>
20
- <Surface.Surface role='main' />
22
+ <Surface.Surface type={MainRole} />
21
23
  </div>
22
24
  );
23
25
  };
@@ -33,7 +35,7 @@ const meta = {
33
35
  Capabilities.ReactSurface,
34
36
  Surface.create({
35
37
  id: 'test',
36
- role: 'main',
38
+ filter: Surface.makeFilter(MainRole),
37
39
  component: ({ role }) => <span>{JSON.stringify({ role })}</span>,
38
40
  }),
39
41
  ),
@@ -26,7 +26,9 @@ export const setupPluginManager = ({
26
26
  }: UseAppOptions & Pick<WithPluginManagerOptions, 'capabilities'> = {}) => {
27
27
  // Auto-enable every non-system plugin so stories don't have to spell out
28
28
  // enablement. System-tagged plugins are force-enabled by the manager.
29
- const enabled = plugins.filter(({ meta }) => !meta.tags?.includes('system')).map(({ meta }) => meta.id);
29
+ const enabled = plugins
30
+ .filter(({ meta }) => !meta.profile.tags?.includes('system'))
31
+ .map(({ meta }) => meta.profile.key);
30
32
  const pluginManager = PluginManager.make({
31
33
  pluginLoader: () => raise(new Error('Not implemented')),
32
34
  plugins: [StoryPlugin, ...plugins],
@@ -50,9 +50,9 @@ const TestPlugin = Plugin.define<{ error?: boolean }>(
50
50
  Plugin.make,
51
51
  );
52
52
 
53
- type DefaultStoryProps = { plugins?: Plugin.Plugin[] };
53
+ type StoryArgs = { plugins?: Plugin.Plugin[] };
54
54
 
55
- const DefaultStory = ({ plugins }: DefaultStoryProps) => {
55
+ const DefaultStory = ({ plugins }: StoryArgs) => {
56
56
  const App = useApp({ plugins });
57
57
 
58
58
  return <App />;
@@ -16,6 +16,13 @@ const PluginManagerContext = createContext<PluginManager.PluginManager | undefin
16
16
  export const usePluginManager = (): PluginManager.PluginManager =>
17
17
  useContext(PluginManagerContext) ?? raise(new Error('Missing PluginManagerContext'));
18
18
 
19
+ /**
20
+ * Get the plugin manager if present, or `undefined` when rendered outside a {@link PluginManagerProvider}
21
+ * (e.g. a standalone component story). Lets components treat plugin capabilities as a progressive
22
+ * enhancement rather than a hard requirement.
23
+ */
24
+ export const useOptionalPluginManager = (): PluginManager.PluginManager | undefined => useContext(PluginManagerContext);
25
+
19
26
  /**
20
27
  * Context provider for a plugin manager.
21
28
  */
@@ -3,10 +3,11 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
6
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
7
7
 
8
8
  import { random } from '@dxos/random';
9
- import { List, ListItem, Panel, Toolbar } from '@dxos/react-ui';
9
+ import { Panel, Toolbar } from '@dxos/react-ui';
10
+ import { Listbox } from '@dxos/react-ui-list';
10
11
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
12
  import { type ColorStyles, getHashStyles, mx } from '@dxos/ui-theme';
12
13
 
@@ -14,23 +15,22 @@ import { Capabilities } from '../../../common';
14
15
  import { withPluginManager } from '../../../testing';
15
16
  import { usePluginManager } from '../PluginManager';
16
17
  import { SurfaceComponent, useSurfaces } from './SurfaceComponent';
17
- import { create } from './types';
18
+ import { create, makeFilter, makeType } from './types';
19
+
20
+ const ItemRole = makeType<{ id: string }>('org.dxos.test.role.item');
18
21
 
19
22
  type TestComponentProps = {
20
23
  id: string;
21
24
  styles: ColorStyles;
22
25
  };
23
26
 
24
- const TestComponent = forwardRef<HTMLDivElement, TestComponentProps>(({ styles, id }, forwardedRef) => {
27
+ const TestComponent = ({ styles, id }: TestComponentProps) => {
25
28
  return (
26
- <div
27
- className={mx('flex justify-center items-center border rounded-sm', styles.surface, styles.border)}
28
- ref={forwardedRef}
29
- >
29
+ <div className={mx('flex justify-center items-center border rounded-sm', styles.surface, styles.border)}>
30
30
  <span className={mx('dx-tag font-mono text-lg', styles.text)}>{id}</span>
31
31
  </div>
32
32
  );
33
- });
33
+ };
34
34
 
35
35
  const DefaultStory = () => {
36
36
  const manager = usePluginManager();
@@ -46,9 +46,8 @@ const DefaultStory = () => {
46
46
  interface: Capabilities.ReactSurface,
47
47
  implementation: create({
48
48
  id,
49
- role: 'item',
50
- filter: (data): data is any => (data as any)?.id === id,
51
- component: ({ ref }) => <TestComponent id={id} styles={styles} ref={ref} />,
49
+ filter: makeFilter(ItemRole, (data) => data.id === id),
50
+ component: () => <TestComponent id={id} styles={styles} />,
52
51
  }),
53
52
  });
54
53
 
@@ -65,8 +64,7 @@ const DefaultStory = () => {
65
64
  interface: Capabilities.ReactSurface,
66
65
  implementation: create({
67
66
  id: 'error',
68
- role: 'item',
69
- filter: (data): data is any => (data as any)?.id === 'error',
67
+ filter: makeFilter(ItemRole, (data) => data.id === 'error'),
70
68
  component: () => {
71
69
  const [count, setCount] = useState(3);
72
70
  useEffect(() => {
@@ -113,15 +111,17 @@ const DefaultStory = () => {
113
111
  </Toolbar.Root>
114
112
  </Panel.Toolbar>
115
113
  <Panel.Content className='grid grid-cols-2 h-full gap-4 overflow-hidden'>
116
- <SurfaceComponent role='item' data={selected ? { id: selected } : undefined} limit={1} ref={ref} />
114
+ <SurfaceComponent type={ItemRole} data={selected ? { id: selected } : undefined} limit={1} ref={ref} />
117
115
  <div className='overflow-y-auto h-full'>
118
- <List>
119
- {surfaces.map((surface) => (
120
- <ListItem.Root key={surface.id} id={surface.id}>
121
- <ListItem.Heading classNames='flex items-center'>{surface.id}</ListItem.Heading>
122
- </ListItem.Root>
123
- ))}
124
- </List>
116
+ <Listbox.Root>
117
+ <Listbox.Content aria-label='Surfaces'>
118
+ {surfaces.map((surface) => (
119
+ <Listbox.Item key={surface.id} id={surface.id}>
120
+ <Listbox.ItemLabel classNames='flex items-center'>{surface.id}</Listbox.ItemLabel>
121
+ </Listbox.Item>
122
+ ))}
123
+ </Listbox.Content>
124
+ </Listbox.Root>
125
125
  </div>
126
126
  </Panel.Content>
127
127
  </Panel.Root>