@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.
- package/dist/lib/browser/{capability-BOPAKKWG.mjs → capability-S44TE2H7.mjs} +9 -9
- package/dist/lib/browser/{chunk-66IXTIVK.mjs → chunk-3NQLTQCP.mjs} +6 -7
- package/dist/lib/browser/chunk-3NQLTQCP.mjs.map +7 -0
- package/dist/lib/browser/chunk-53HJHMS5.mjs +7 -0
- package/dist/lib/browser/chunk-53HJHMS5.mjs.map +7 -0
- package/dist/lib/browser/{chunk-Y66ELD4U.mjs → chunk-KRJ4KK2W.mjs} +36 -7
- package/dist/lib/browser/chunk-KRJ4KK2W.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KZUDO43J.mjs → chunk-LUH4Y5F2.mjs} +31 -23
- package/dist/lib/browser/chunk-LUH4Y5F2.mjs.map +7 -0
- package/dist/lib/browser/{chunk-CO3XLNUX.mjs → chunk-NKVRSMFN.mjs} +18 -18
- package/dist/lib/browser/chunk-NKVRSMFN.mjs.map +7 -0
- package/dist/lib/browser/{chunk-DC3WRPBV.mjs → chunk-OO53M5UK.mjs} +45 -12
- package/dist/lib/browser/chunk-OO53M5UK.mjs.map +7 -0
- package/dist/lib/browser/{chunk-IW44C7UL.mjs → chunk-OPTS3ZSN.mjs} +1 -1
- package/dist/lib/browser/chunk-OPTS3ZSN.mjs.map +7 -0
- package/dist/lib/browser/{chunk-HFLKXMT7.mjs → chunk-U5MESPM5.mjs} +61 -45
- package/dist/lib/browser/chunk-U5MESPM5.mjs.map +7 -0
- package/dist/lib/browser/{chunk-XHS5KDNX.mjs → chunk-UGYCLOXE.mjs} +25 -25
- package/dist/lib/browser/chunk-UGYCLOXE.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5GY3YOEL.mjs → chunk-VF37YTXJ.mjs} +5 -5
- package/dist/lib/browser/chunk-VF37YTXJ.mjs.map +7 -0
- package/dist/lib/browser/{chunk-FO3IYSLV.mjs → chunk-YUVQIOTB.mjs} +29 -37
- package/dist/lib/browser/chunk-YUVQIOTB.mjs.map +7 -0
- package/dist/lib/browser/cli/index.mjs +9 -9
- package/dist/lib/browser/cli/index.mjs.map +2 -2
- package/dist/lib/browser/common/activation-events.mjs +7 -7
- package/dist/lib/browser/common/capabilities.mjs +7 -7
- package/dist/lib/browser/config/index.mjs +8 -0
- package/dist/lib/browser/config/index.mjs.map +7 -0
- package/dist/lib/browser/core/activation-event.mjs +1 -1
- package/dist/lib/browser/core/capability.mjs +1 -1
- package/dist/lib/browser/core/plugin-manager.mjs +4 -4
- package/dist/lib/browser/core/plugin.mjs +12 -2
- package/dist/lib/browser/core/url-loader.mjs +3 -3
- package/dist/lib/browser/index.mjs +14 -10
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{process-manager-capability-GXNUO7AR.mjs → process-manager-capability-QK4BMKMN.mjs} +9 -9
- package/dist/lib/browser/testing/index.mjs +15 -15
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/testing/react.mjs +11 -11
- package/dist/lib/browser/testing/react.mjs.map +3 -3
- package/dist/lib/browser/ui/index.mjs +18 -10
- package/dist/lib/node-esm/{capability-KZWRTX7R.mjs → capability-3YRF77LV.mjs} +9 -9
- package/dist/lib/node-esm/chunk-2UPUZXGU.mjs +9 -0
- package/dist/lib/node-esm/chunk-2UPUZXGU.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-XIGPWXCQ.mjs → chunk-4SE7SX7N.mjs} +45 -12
- package/dist/lib/node-esm/chunk-4SE7SX7N.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-XOCUANHO.mjs → chunk-BMW6P6QA.mjs} +6 -7
- package/dist/lib/node-esm/chunk-BMW6P6QA.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-A5CHGAZE.mjs → chunk-DRT75WGD.mjs} +61 -45
- package/dist/lib/node-esm/chunk-DRT75WGD.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-XGBUKRCK.mjs → chunk-FKMVORZN.mjs} +31 -23
- package/dist/lib/node-esm/chunk-FKMVORZN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-CTKEZHKF.mjs → chunk-GLH4LGHZ.mjs} +1 -1
- package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-SX5NVETP.mjs → chunk-IL7O7IRX.mjs} +25 -25
- package/dist/lib/node-esm/chunk-IL7O7IRX.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-FSVGKTF6.mjs → chunk-ND2HETHD.mjs} +36 -7
- package/dist/lib/node-esm/chunk-ND2HETHD.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-KFZEB6BV.mjs → chunk-TLKYZKPW.mjs} +5 -5
- package/dist/lib/node-esm/chunk-TLKYZKPW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-WK7OIQKI.mjs → chunk-VCPGX7WN.mjs} +29 -37
- package/dist/lib/node-esm/chunk-VCPGX7WN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-NULBHQYS.mjs → chunk-XSZMX53L.mjs} +18 -18
- package/dist/lib/node-esm/chunk-XSZMX53L.mjs.map +7 -0
- package/dist/lib/node-esm/cli/index.mjs +9 -9
- package/dist/lib/node-esm/cli/index.mjs.map +2 -2
- package/dist/lib/node-esm/common/activation-events.mjs +7 -7
- package/dist/lib/node-esm/common/capabilities.mjs +7 -7
- package/dist/lib/node-esm/config/index.mjs +9 -0
- package/dist/lib/node-esm/config/index.mjs.map +7 -0
- package/dist/lib/node-esm/core/activation-event.mjs +1 -1
- package/dist/lib/node-esm/core/capability.mjs +1 -1
- package/dist/lib/node-esm/core/plugin-manager.mjs +4 -4
- package/dist/lib/node-esm/core/plugin.mjs +12 -2
- package/dist/lib/node-esm/core/url-loader.mjs +3 -3
- package/dist/lib/node-esm/index.mjs +14 -10
- package/dist/lib/node-esm/index.mjs.map +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{process-manager-capability-TX5TA6YN.mjs → process-manager-capability-5S7WZ2JR.mjs} +9 -9
- package/dist/lib/node-esm/testing/index.mjs +15 -15
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/testing/react.mjs +11 -11
- package/dist/lib/node-esm/testing/react.mjs.map +3 -3
- package/dist/lib/node-esm/ui/index.mjs +18 -10
- package/dist/plugin/node-esm/index.mjs +414 -81
- package/dist/plugin/node-esm/index.mjs.map +4 -4
- package/dist/plugin/node-esm/meta.json +1 -1
- package/dist/types/src/cli/cli.d.ts +1 -1
- package/dist/types/src/common/activation-events.d.ts.map +1 -1
- package/dist/types/src/common/capabilities.d.ts +2 -2
- package/dist/types/src/common/capabilities.d.ts.map +1 -1
- package/dist/types/src/config/index.d.ts +2 -0
- package/dist/types/src/config/index.d.ts.map +1 -0
- package/dist/types/src/core/activation-event.d.ts +9 -6
- package/dist/types/src/core/activation-event.d.ts.map +1 -1
- package/dist/types/src/core/capability.d.ts +9 -3
- package/dist/types/src/core/capability.d.ts.map +1 -1
- package/dist/types/src/core/edge-registry-plugin-provider.d.ts +6 -11
- package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manifest.d.ts +25 -7
- package/dist/types/src/core/plugin-manifest.d.ts.map +1 -1
- package/dist/types/src/core/plugin.d.ts +89 -91
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/core/registry.d.ts +17 -57
- package/dist/types/src/core/registry.d.ts.map +1 -1
- package/dist/types/src/core/url-loader.d.ts +2 -2
- package/dist/types/src/core/url-loader.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/history/capability.d.ts +1 -3
- package/dist/types/src/plugin-process-manager/history/capability.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/meta.d.ts +32 -2
- package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +1 -4
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
- package/dist/types/src/testing/harness.d.ts +1 -1
- package/dist/types/src/testing/harness.d.ts.map +1 -1
- package/dist/types/src/testing/react.d.ts +6 -5
- package/dist/types/src/testing/react.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/App.stories.d.ts +2 -2
- package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts +6 -0
- package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +6 -10
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/context.d.ts +6 -2
- package/dist/types/src/ui/components/Surface/context.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/index.d.ts +11 -4
- package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/types.d.ts +52 -72
- package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.d.ts +1 -1
- package/dist/types/src/ui/hooks/useCapabilities.d.ts +17 -1
- package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/composer/index.d.ts +11 -4
- package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/index.d.ts +1 -0
- package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/load.d.ts +16 -0
- package/dist/types/src/vite-plugin/load.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/manifest.d.ts +16 -4
- package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/docs/registry-spec.md +331 -0
- package/moon.yml +1 -0
- package/package.json +50 -39
- package/src/cli/cli.ts +2 -2
- package/src/common/activation-events.ts +3 -3
- package/src/common/capabilities.ts +18 -18
- package/src/config/index.ts +5 -0
- package/src/core/activation-event.ts +15 -8
- package/src/core/capability.ts +18 -6
- package/src/core/edge-registry-plugin-provider.ts +41 -42
- package/src/core/plugin-manager.test.ts +133 -137
- package/src/core/plugin-manager.ts +25 -19
- package/src/core/plugin-manifest.test.ts +5 -5
- package/src/core/plugin-manifest.ts +13 -10
- package/src/core/plugin.ts +58 -114
- package/src/core/registry.ts +20 -60
- package/src/core/url-loader.test.ts +2 -2
- package/src/core/url-loader.ts +23 -19
- package/src/index.ts +1 -0
- package/src/testing/harness.ts +5 -3
- package/src/testing/react.test.tsx +4 -2
- package/src/testing/react.tsx +10 -9
- package/src/testing/service.ts +2 -2
- package/src/testing/withPluginManager.stories.tsx +4 -2
- package/src/testing/withPluginManager.tsx +3 -1
- package/src/ui/components/App/App.stories.tsx +2 -2
- package/src/ui/components/PluginManager/PluginManagerProvider.ts +7 -0
- package/src/ui/components/Surface/SurfaceComponent.stories.tsx +22 -22
- package/src/ui/components/Surface/SurfaceComponent.tsx +90 -79
- package/src/ui/components/Surface/SurfaceProfilerContext.tsx +1 -1
- package/src/ui/components/Surface/context.ts +6 -4
- package/src/ui/components/Surface/index.ts +14 -3
- package/src/ui/components/Surface/types.test.ts +43 -51
- package/src/ui/components/Surface/types.ts +99 -64
- package/src/ui/hooks/useApp.test.tsx +1 -1
- package/src/ui/hooks/useApp.tsx +2 -2
- package/src/ui/hooks/useCapabilities.ts +49 -2
- package/src/vite-plugin/boot-loader/BootLoader.solid-stories.tsx +11 -11
- package/src/vite-plugin/boot-loader/loader.ts +3 -3
- package/src/vite-plugin/composer/index.ts +134 -61
- package/src/vite-plugin/import-map/index.ts +23 -7
- package/src/vite-plugin/index.ts +1 -0
- package/src/vite-plugin/load.ts +57 -0
- package/src/vite-plugin/manifest.test.ts +3 -3
- package/src/vite-plugin/manifest.ts +21 -10
- package/src/vite-plugin/packages.ts +0 -2
- package/tsconfig.json +5 -2
- package/dist/lib/browser/chunk-5GY3YOEL.mjs.map +0 -7
- package/dist/lib/browser/chunk-66IXTIVK.mjs.map +0 -7
- package/dist/lib/browser/chunk-CO3XLNUX.mjs.map +0 -7
- package/dist/lib/browser/chunk-DC3WRPBV.mjs.map +0 -7
- package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +0 -7
- package/dist/lib/browser/chunk-HFLKXMT7.mjs.map +0 -7
- package/dist/lib/browser/chunk-IW44C7UL.mjs.map +0 -7
- package/dist/lib/browser/chunk-KZUDO43J.mjs.map +0 -7
- package/dist/lib/browser/chunk-XHS5KDNX.mjs.map +0 -7
- package/dist/lib/browser/chunk-Y66ELD4U.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-A5CHGAZE.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CTKEZHKF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FSVGKTF6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-KFZEB6BV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NULBHQYS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SX5NVETP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XGBUKRCK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XIGPWXCQ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XOCUANHO.mjs.map +0 -7
- /package/dist/lib/browser/{capability-BOPAKKWG.mjs.map → capability-S44TE2H7.mjs.map} +0 -0
- /package/dist/lib/browser/{process-manager-capability-GXNUO7AR.mjs.map → process-manager-capability-QK4BMKMN.mjs.map} +0 -0
- /package/dist/lib/node-esm/{capability-KZWRTX7R.mjs.map → capability-3YRF77LV.mjs.map} +0 -0
- /package/dist/lib/node-esm/{process-manager-capability-TX5TA6YN.mjs.map → process-manager-capability-5S7WZ2JR.mjs.map} +0 -0
package/src/core/url-loader.ts
CHANGED
|
@@ -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
|
|
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):
|
|
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
|
|
82
|
-
typeof entry === 'object' &&
|
|
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:
|
|
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
|
|
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.
|
|
200
|
-
if (document.querySelector(`link[data-dxos-plugin-id="${manifest.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
259
|
-
manifestId: manifest.
|
|
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.
|
|
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.
|
|
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.
|
|
361
|
+
new RemotePluginLoadError({ context: { locator, reason: 'duplicate-id', id: plugin.meta.profile.key } }),
|
|
358
362
|
);
|
|
359
363
|
}
|
|
360
|
-
persistRemotePlugin(storage, key, { id: plugin.meta.
|
|
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
package/src/testing/harness.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
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, {
|
|
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
|
});
|
package/src/testing/react.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
-
import {
|
|
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
|
-
|
|
39
|
-
data?:
|
|
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 {
|
|
54
|
+
const { type, data, limit, fallback, placeholder } = props;
|
|
54
55
|
return render(
|
|
55
56
|
harness,
|
|
56
|
-
<SurfaceComponent
|
|
57
|
+
<SurfaceComponent type={type} data={data as any} limit={limit} fallback={fallback} placeholder={placeholder} />,
|
|
57
58
|
options,
|
|
58
59
|
);
|
|
59
60
|
};
|
package/src/testing/service.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
53
|
+
type StoryArgs = { plugins?: Plugin.Plugin[] };
|
|
54
54
|
|
|
55
|
-
const DefaultStory = ({ plugins }:
|
|
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, {
|
|
6
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { random } from '@dxos/random';
|
|
9
|
-
import {
|
|
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 =
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
|
114
|
+
<SurfaceComponent type={ItemRole} data={selected ? { id: selected } : undefined} limit={1} ref={ref} />
|
|
117
115
|
<div className='overflow-y-auto h-full'>
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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>
|