@3sln/trove 0.0.2
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/README.md +1227 -0
- package/package.json +75 -0
- package/packages/core/src/collections/index.js +249 -0
- package/packages/core/src/errors.js +186 -0
- package/packages/core/src/identity/discovery.js +210 -0
- package/packages/core/src/identity/index.js +188 -0
- package/packages/core/src/identity/jwt.js +199 -0
- package/packages/core/src/index.js +104 -0
- package/packages/core/src/indexers/contribution.js +115 -0
- package/packages/core/src/indexers/registry.js +162 -0
- package/packages/core/src/indexing.js +340 -0
- package/packages/core/src/issues.js +150 -0
- package/packages/core/src/kv.js +0 -0
- package/packages/core/src/links.js +141 -0
- package/packages/core/src/metadata/cursor.js +73 -0
- package/packages/core/src/metadata/interface.js +244 -0
- package/packages/core/src/metadata/memory.js +270 -0
- package/packages/core/src/metadata/sqlite.js +412 -0
- package/packages/core/src/notifications/index.js +139 -0
- package/packages/core/src/notifications/webpush.js +217 -0
- package/packages/core/src/plugins/contributions.js +177 -0
- package/packages/core/src/plugins/identity.js +98 -0
- package/packages/core/src/plugins/index.js +225 -0
- package/packages/core/src/plugins/indexers.js +142 -0
- package/packages/core/src/plugins/installStore.js +134 -0
- package/packages/core/src/plugins/package.js +102 -0
- package/packages/core/src/plugins/packageStore.js +61 -0
- package/packages/core/src/plugins/runtime.js +101 -0
- package/packages/core/src/plugins/sql.js +52 -0
- package/packages/core/src/retry.js +74 -0
- package/packages/core/src/scan.js +302 -0
- package/packages/core/src/search/embeddings.js +128 -0
- package/packages/core/src/search/index.js +200 -0
- package/packages/core/src/search/keywordStore.js +107 -0
- package/packages/core/src/search/sqliteStores.js +455 -0
- package/packages/core/src/search/tagMatch.js +59 -0
- package/packages/core/src/search/transformer.js +195 -0
- package/packages/core/src/search/vectorStore.js +274 -0
- package/packages/core/src/search/vectorize.js +249 -0
- package/packages/core/src/sidecar/document.js +213 -0
- package/packages/core/src/sidecar/index.js +174 -0
- package/packages/core/src/sidecar/manager.js +239 -0
- package/packages/core/src/sidecar/store.js +46 -0
- package/packages/core/src/signedUrls.js +170 -0
- package/packages/core/src/sqlite-d1.js +162 -0
- package/packages/core/src/sqlite-driver.js +42 -0
- package/packages/core/src/sqlite.js +162 -0
- package/packages/core/src/storage/filesystem.js +283 -0
- package/packages/core/src/storage/interface.js +222 -0
- package/packages/core/src/storage/memory.js +113 -0
- package/packages/core/src/storage/prefixed.js +75 -0
- package/packages/core/src/storage/s3.js +316 -0
- package/packages/core/src/storage/s3sigv4.js +185 -0
- package/packages/core/src/tasks.js +228 -0
- package/packages/core/src/uploads.js +386 -0
- package/packages/core/src/util.js +125 -0
- package/packages/core/src/vfs.js +666 -0
- package/packages/plugin-sdk/src/browser.js +316 -0
- package/packages/plugin-sdk/src/index.js +32 -0
- package/packages/plugin-sdk/src/protocol.js +59 -0
- package/packages/plugin-sdk/src/rpc.js +95 -0
- package/packages/server/src/adapters/bun.js +78 -0
- package/packages/server/src/adapters/node.js +115 -0
- package/packages/server/src/adapters/staticAssets.js +123 -0
- package/packages/server/src/adapters/webDist.js +70 -0
- package/packages/server/src/adapters/worker-tasks.js +206 -0
- package/packages/server/src/adapters/worker.js +159 -0
- package/packages/server/src/cachePolicy.js +34 -0
- package/packages/server/src/engine/README.md +88 -0
- package/packages/server/src/engine/actions/scanCollection.js +114 -0
- package/packages/server/src/engine/index.js +95 -0
- package/packages/server/src/engine/lazy.js +25 -0
- package/packages/server/src/engine/providers/access.js +363 -0
- package/packages/server/src/engine/providers/core.js +405 -0
- package/packages/server/src/engine/providers/scan.js +67 -0
- package/packages/server/src/index.js +698 -0
- package/packages/server/src/manifest.js +98 -0
- package/packages/server/src/mcp/auth.js +40 -0
- package/packages/server/src/mcp/index.js +213 -0
- package/packages/server/src/mcp/protocol.js +181 -0
- package/packages/server/src/mcp/tools.js +351 -0
- package/packages/server/src/router.js +229 -0
- package/packages/server/src/routes.js +1066 -0
- package/packages/server/src/scope.js +43 -0
- package/packages/web/dist/assets/chunk-4xqbzebh.js +5 -0
- package/packages/web/dist/assets/chunk-4xqbzebh.js.map +9 -0
- package/packages/web/dist/assets/chunk-h05bxfbs.js +5 -0
- package/packages/web/dist/assets/chunk-h05bxfbs.js.map +10 -0
- package/packages/web/dist/assets/main-4cxs7prw.js +356 -0
- package/packages/web/dist/assets/main-4cxs7prw.js.map +103 -0
- package/packages/web/dist/assets/styles-kcx1x337.css +1 -0
- package/packages/web/dist/icon.svg +11 -0
- package/packages/web/dist/index.html +16 -0
- package/packages/web/dist/sql-wasm.wasm +0 -0
- package/packages/web/dist/sw.js +186 -0
- package/packages/web/src/bl/actions.js +410 -0
- package/packages/web/src/bl/activity.js +306 -0
- package/packages/web/src/bl/commands.js +274 -0
- package/packages/web/src/bl/fileType.js +49 -0
- package/packages/web/src/bl/index.js +70 -0
- package/packages/web/src/bl/links.js +54 -0
- package/packages/web/src/bl/offline.js +268 -0
- package/packages/web/src/bl/openers.js +71 -0
- package/packages/web/src/bl/pluginInstall.js +59 -0
- package/packages/web/src/bl/services.js +143 -0
- package/packages/web/src/bl/social.js +234 -0
- package/packages/web/src/bl/tagQuery.js +44 -0
- package/packages/web/src/main.js +10 -0
- package/packages/web/src/platform/api.js +529 -0
- package/packages/web/src/platform/commands.js +89 -0
- package/packages/web/src/platform/context.js +77 -0
- package/packages/web/src/platform/contributions.js +156 -0
- package/packages/web/src/platform/index.js +150 -0
- package/packages/web/src/platform/keybindings.js +199 -0
- package/packages/web/src/platform/mediaUrls.js +137 -0
- package/packages/web/src/platform/navigation.js +131 -0
- package/packages/web/src/platform/notifications.js +50 -0
- package/packages/web/src/platform/overlay.js +81 -0
- package/packages/web/src/platform/pluginClientDb.js +132 -0
- package/packages/web/src/platform/pluginDock.js +141 -0
- package/packages/web/src/platform/pluginFrames.js +194 -0
- package/packages/web/src/platform/pluginHost.js +648 -0
- package/packages/web/src/platform/pluginMedia.js +62 -0
- package/packages/web/src/platform/pluginModules.js +90 -0
- package/packages/web/src/platform/pluginNet.js +71 -0
- package/packages/web/src/platform/pluginPackage.js +247 -0
- package/packages/web/src/platform/pluginRpc.js +377 -0
- package/packages/web/src/platform/pluginSigning.js +168 -0
- package/packages/web/src/platform/pluginStore.js +67 -0
- package/packages/web/src/platform/settings.js +101 -0
- package/packages/web/src/platform/spatialNav.js +286 -0
- package/packages/web/src/platform/viewport.js +123 -0
- package/packages/web/src/platform/voice.js +133 -0
- package/packages/web/src/platform/voiceSearch.js +155 -0
- package/packages/web/src/platform/whenclause.js +162 -0
- package/packages/web/src/platform/workbench.js +156 -0
- package/packages/web/src/runtime.js +73 -0
- package/packages/web/src/styles.css +1382 -0
- package/packages/web/src/ui/components/activityBar.js +35 -0
- package/packages/web/src/ui/components/activityPanel.js +132 -0
- package/packages/web/src/ui/components/commandPalette.js +154 -0
- package/packages/web/src/ui/components/editorArea.js +75 -0
- package/packages/web/src/ui/components/launcher.js +392 -0
- package/packages/web/src/ui/components/openers/index.js +212 -0
- package/packages/web/src/ui/components/openers/markdown.js +222 -0
- package/packages/web/src/ui/components/overlays.js +255 -0
- package/packages/web/src/ui/components/phoneChrome.js +188 -0
- package/packages/web/src/ui/components/pluginReview.js +151 -0
- package/packages/web/src/ui/components/pluginsView.js +120 -0
- package/packages/web/src/ui/components/settingsView.js +258 -0
- package/packages/web/src/ui/components/social.js +290 -0
- package/packages/web/src/ui/components/statusBar.js +198 -0
- package/packages/web/src/ui/components/views/grid.js +115 -0
- package/packages/web/src/ui/components/views/index.js +155 -0
- package/packages/web/src/ui/components/views/list.js +50 -0
- package/packages/web/src/ui/components/views/parts.js +58 -0
- package/packages/web/src/ui/compositions/workbench.js +125 -0
- package/packages/web/src/ui/format.js +33 -0
- package/packages/web/src/ui/icon.js +81 -0
- package/packages/web/src/ui/media.js +114 -0
- package/packages/web/src/ui/sanitize.js +86 -0
- package/packages/web/src/workbench.js +205 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// FrameManager — the sandbox seam: everything about creating, handshaking with, and
|
|
2
|
+
// destroying a plugin's iframe.
|
|
3
|
+
//
|
|
4
|
+
// A plugin's code runs inside a sandboxed iframe on an OPAQUE origin (sandbox
|
|
5
|
+
// allow-scripts, no allow-same-origin): it can't touch the host DOM, can't read
|
|
6
|
+
// cookies/storage, and can't even fetch its own package files. The host injects our
|
|
7
|
+
// browser SDK + the plugin's entry into the frame's srcdoc; everything else flows over
|
|
8
|
+
// a single transferred MessagePort, capability-gated by what the user granted.
|
|
9
|
+
//
|
|
10
|
+
// Every frame — the plugin's primary (background) instance and each viewer/panel —
|
|
11
|
+
// comes from `spawn()`, so they all share the same wiring and capabilities.
|
|
12
|
+
|
|
13
|
+
import { RpcChannel } from '@3sln/trove/plugin-sdk/rpc.js';
|
|
14
|
+
import SDK_SOURCE from '@3sln/trove/plugin-sdk/browser.js' with { type: 'text' };
|
|
15
|
+
import { PROTOCOL_VERSION, isCompatible } from '@3sln/trove/plugin-sdk/protocol.js';
|
|
16
|
+
import { buildModuleGraph, isModuleEntry } from './pluginModules.js';
|
|
17
|
+
|
|
18
|
+
const HANDSHAKE_TIMEOUT_MS = 15000;
|
|
19
|
+
|
|
20
|
+
export class FrameManager {
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} deps
|
|
23
|
+
* @param {import('./pluginMedia.js').MediaController} deps.media
|
|
24
|
+
* @param {import('./pluginDock.js').FrameDock} deps.dock
|
|
25
|
+
*/
|
|
26
|
+
constructor({ media, dock } = {}) {
|
|
27
|
+
this.media = media;
|
|
28
|
+
this.dock = dock;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create ONE sandboxed iframe wired with `record`'s granted capabilities, run the
|
|
33
|
+
* handshake, and resolve to a live frame `{ role, iframe, channel, record }`.
|
|
34
|
+
* @param {object} record the plugin runtime record
|
|
35
|
+
* @param {'primary'|'viewer'} role
|
|
36
|
+
* @param {{onCall:Function, onEvent:Function, online:boolean}} wiring
|
|
37
|
+
*/
|
|
38
|
+
async spawn(record, role, wiring) {
|
|
39
|
+
const iframe = document.createElement('iframe');
|
|
40
|
+
iframe.style.cssText = HIDDEN_STYLE;
|
|
41
|
+
// Opaque-origin sandbox: scripts only, no same-origin, no top navigation.
|
|
42
|
+
//
|
|
43
|
+
// Deliberately NO `allow-popups`. The CSP below blocks fetch/XHR/WebSocket/beacon
|
|
44
|
+
// so that all network access is brokered by the host against the plugin's declared
|
|
45
|
+
// endpoints — but no CSP directive covers `window.open`, so a popup is a hole
|
|
46
|
+
// straight through that broker: one real click and a plugin granted `files` but not
|
|
47
|
+
// `network` can navigate a new context to `https://attacker/?d=<file bytes>`.
|
|
48
|
+
// A plugin that genuinely needs to send the user somewhere asks the host to do it.
|
|
49
|
+
iframe.setAttribute('sandbox', 'allow-scripts allow-forms');
|
|
50
|
+
iframe.setAttribute('referrerpolicy', 'no-referrer');
|
|
51
|
+
// A frame can boot at a different ENTRY MODULE of the plugin's one module tree —
|
|
52
|
+
// that's all an opener is. Same package, same shared code, different entry.
|
|
53
|
+
const entry = wiring?.entry;
|
|
54
|
+
if (entry) {
|
|
55
|
+
record._entrySrcdoc ||= {};
|
|
56
|
+
record._entrySrcdoc[entry] ||= await buildSrcdoc({ ...record.manifest, entry }, record.files);
|
|
57
|
+
iframe.srcdoc = record._entrySrcdoc[entry];
|
|
58
|
+
} else {
|
|
59
|
+
record._srcdoc ||= await buildSrcdoc(record.manifest, record.files);
|
|
60
|
+
iframe.srcdoc = record._srcdoc;
|
|
61
|
+
}
|
|
62
|
+
const frame = { role, iframe, channel: null, record, place: null, dock: null, mediaActions: null };
|
|
63
|
+
document.body.appendChild(iframe);
|
|
64
|
+
try {
|
|
65
|
+
await this.#handshake(record, frame, wiring);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
// Never leave a hung/failed frame's iframe (or its channel) behind.
|
|
68
|
+
try { frame.channel?.dispose(); } catch { /* ignore */ }
|
|
69
|
+
iframe.remove();
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
return frame;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Resolves once the frame's plugin calls activate(); rejects (and fully cleans up
|
|
76
|
+
// its global message listener + timer) on boot error, iframe error, or timeout.
|
|
77
|
+
#handshake(record, frame, { onCall, onEvent, online }) {
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const { iframe } = frame;
|
|
80
|
+
const manifest = record.manifest;
|
|
81
|
+
let settled = false;
|
|
82
|
+
let timer = 0;
|
|
83
|
+
const cleanup = () => { window.removeEventListener('message', onReady); if (timer) clearTimeout(timer); };
|
|
84
|
+
const fail = (msg) => { if (settled) return; settled = true; cleanup(); reject(new Error(msg)); };
|
|
85
|
+
const onReady = (e) => {
|
|
86
|
+
if (e.source !== iframe.contentWindow) return;
|
|
87
|
+
if (e.data?.__trove === 'boot-error') return fail(e.data.error || 'Plugin failed to load its modules');
|
|
88
|
+
if (e.data?.__trove !== 'ready') return;
|
|
89
|
+
// A plugin built against an incompatible protocol MAJOR can't be talked to —
|
|
90
|
+
// fail loudly here rather than letting calls misbehave in obscure ways.
|
|
91
|
+
if (!isCompatible(e.data.protocolVersion)) {
|
|
92
|
+
return fail(`Plugin speaks protocol ${e.data.protocolVersion}, this host speaks ${PROTOCOL_VERSION}`);
|
|
93
|
+
}
|
|
94
|
+
// Ready received — the transferred port takes over; stop listening globally.
|
|
95
|
+
window.removeEventListener('message', onReady);
|
|
96
|
+
const channel = new MessageChannel();
|
|
97
|
+
frame.channel = new RpcChannel(channel.port1, {
|
|
98
|
+
onCall: (m, p) => onCall(record, m, p, frame),
|
|
99
|
+
onEvent: (m, p) => onEvent(record, m, p, frame),
|
|
100
|
+
});
|
|
101
|
+
// activate() success/failure settles this promise (and clears the timer).
|
|
102
|
+
frame.resolveActivated = (f) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); resolve(f); };
|
|
103
|
+
frame.rejectActivated = (err) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); reject(err); };
|
|
104
|
+
// Opaque origin → target '*'; the transferred port is the real capability.
|
|
105
|
+
iframe.contentWindow.postMessage(
|
|
106
|
+
{ __trove: 'init', manifest, capabilities: record.grants, storage: record.storage, online, role: frame.role, protocolVersion: PROTOCOL_VERSION },
|
|
107
|
+
'*',
|
|
108
|
+
[channel.port2],
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
timer = setTimeout(() => fail('Plugin handshake timed out'), HANDSHAKE_TIMEOUT_MS);
|
|
112
|
+
window.addEventListener('message', onReady);
|
|
113
|
+
iframe.addEventListener('error', () => fail('Failed to load plugin iframe'));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Tear a frame down for good: stop tracking it, drop the dock/media session if it
|
|
119
|
+
* held them, close its RPC channel, and remove the iframe from the DOM.
|
|
120
|
+
*/
|
|
121
|
+
destroy(frame) {
|
|
122
|
+
if (!frame) return;
|
|
123
|
+
this.dock?.releaseFrame(frame);
|
|
124
|
+
this.media?.releaseFrame(frame);
|
|
125
|
+
try { frame.channel?.emit('deactivate'); } catch { /* ignore */ }
|
|
126
|
+
try { frame.channel?.dispose(); } catch { /* ignore */ }
|
|
127
|
+
frame.iframe.remove();
|
|
128
|
+
frame.record?.frames?.delete(frame);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const HIDDEN_STYLE = 'position:fixed;left:0;top:0;width:0;height:0;border:0;visibility:hidden;';
|
|
133
|
+
|
|
134
|
+
// The frame's CSP. `connect-src 'none'` means it cannot make ANY direct network
|
|
135
|
+
// request (fetch/XHR/WebSocket/beacon) — all web access is brokered by the host and
|
|
136
|
+
// confined to the plugin's declared endpoints. img/media/font are limited to
|
|
137
|
+
// in-frame blob:/data: so remote loads can't be an exfiltration side-channel.
|
|
138
|
+
// `blob:` in script-src is only for the plugin's own package modules (below), which
|
|
139
|
+
// are same-origin blobs — no external code can load.
|
|
140
|
+
const CSP = [
|
|
141
|
+
"default-src 'none'",
|
|
142
|
+
"script-src 'unsafe-inline' 'unsafe-eval' blob:",
|
|
143
|
+
"style-src 'unsafe-inline'",
|
|
144
|
+
'img-src blob: data:',
|
|
145
|
+
'media-src blob: data:',
|
|
146
|
+
"font-src 'none'",
|
|
147
|
+
"connect-src 'none'",
|
|
148
|
+
"base-uri 'none'",
|
|
149
|
+
"form-action 'none'",
|
|
150
|
+
].join('; ');
|
|
151
|
+
|
|
152
|
+
const HEAD = `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${CSP}"></head><body>`;
|
|
153
|
+
|
|
154
|
+
/** Build the iframe document — classic single-file, or ESM module mode (src/). */
|
|
155
|
+
export async function buildSrcdoc(manifest, files) {
|
|
156
|
+
if (isModuleEntry(manifest)) return moduleBootstrapHtml(await buildModuleGraph({ manifest, files }));
|
|
157
|
+
const entryJs = new TextDecoder().decode(files.get(manifest.entry) ?? new Uint8Array());
|
|
158
|
+
return `${HEAD}
|
|
159
|
+
<script>${SDK_SOURCE}<\/script>
|
|
160
|
+
<script>${entryJs}<\/script>
|
|
161
|
+
</body></html>`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Escape a value for safe inlining inside <script> (so `</script>` and JS line
|
|
165
|
+
// separators in package code can't break out of the tag).
|
|
166
|
+
function safeJson(value) {
|
|
167
|
+
return JSON.stringify(value).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ESM module mode: ship every src/ module as a blob: URL and wire them with an
|
|
171
|
+
// import map keyed by canonical `trove:/<path>` specifiers (the specifiers in the
|
|
172
|
+
// code were rewritten to match). `trove` resolves to a shim over the injected SDK.
|
|
173
|
+
function moduleBootstrapHtml(graph) {
|
|
174
|
+
const entryKey = safeJson('trove:/' + graph.entry);
|
|
175
|
+
return `${HEAD}
|
|
176
|
+
<script>${SDK_SOURCE}<\/script>
|
|
177
|
+
<script id="__trove_src" type="application/json">${safeJson(graph.modules)}<\/script>
|
|
178
|
+
<script>
|
|
179
|
+
(function () {
|
|
180
|
+
var src = JSON.parse(document.getElementById('__trove_src').textContent);
|
|
181
|
+
var imports = {};
|
|
182
|
+
for (var p in src) imports['trove:/' + p] = URL.createObjectURL(new Blob([src[p] + '\\n//# sourceURL=trove:/' + p], { type: 'text/javascript' }));
|
|
183
|
+
imports['trove'] = URL.createObjectURL(new Blob(['export const activate = globalThis.trove.activate;\\nexport default globalThis.trove;'], { type: 'text/javascript' }));
|
|
184
|
+
var im = document.createElement('script');
|
|
185
|
+
im.type = 'importmap';
|
|
186
|
+
im.textContent = JSON.stringify({ imports: imports });
|
|
187
|
+
document.head.appendChild(im);
|
|
188
|
+
import(${entryKey}).catch(function (e) {
|
|
189
|
+
try { parent.postMessage({ __trove: 'boot-error', error: String((e && e.message) || e) }, '*'); } catch (_) {}
|
|
190
|
+
});
|
|
191
|
+
})();
|
|
192
|
+
<\/script>
|
|
193
|
+
</body></html>`;
|
|
194
|
+
}
|