@venturewild/workspace 0.6.29 → 0.6.31
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/package.json +1 -1
- package/server/src/index.mjs +24 -5
- package/server/src/preview-proxy.mjs +26 -0
package/package.json
CHANGED
package/server/src/index.mjs
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
proxyWsUpgrade,
|
|
66
66
|
reconcilePreviews,
|
|
67
67
|
previewUrlFor,
|
|
68
|
+
pickPreviewSlug,
|
|
68
69
|
PREVIEW_ENDED_PAGE,
|
|
69
70
|
} from './preview-proxy.mjs';
|
|
70
71
|
import { createPreviewRails } from './preview-rails.mjs';
|
|
@@ -1107,6 +1108,10 @@ export async function createServer(overrides = {}) {
|
|
|
1107
1108
|
// relay strips any client-forged X-Forwarded-* so the host can't be spoofed
|
|
1108
1109
|
// from off-box. Populated by the dev-port watcher (Part B).
|
|
1109
1110
|
const localPreviews = overrides.localPreviews || new LocalPreviewRegistry();
|
|
1111
|
+
// Most-recently-viewed workspace id (set by the auth middleware from
|
|
1112
|
+
// X-Workspace-Id) — the shared-preview watcher tags previews with the workspace
|
|
1113
|
+
// the user is actively collaborating in, so teammates can discover them.
|
|
1114
|
+
let lastActiveWorkspaceId = null;
|
|
1110
1115
|
app.use('*', async (c, next) => {
|
|
1111
1116
|
const token = previewTokenFromHeaders((n) => c.req.header(n));
|
|
1112
1117
|
if (!token) return next();
|
|
@@ -1180,7 +1185,13 @@ export async function createServer(overrides = {}) {
|
|
|
1180
1185
|
c.set('session', session);
|
|
1181
1186
|
// Resolve the active workspace for this request (lobby M1). Per-tab via the
|
|
1182
1187
|
// X-Workspace-Id header; absent/unknown → the boot default.
|
|
1183
|
-
|
|
1188
|
+
const xwid = c.req.header('x-workspace-id');
|
|
1189
|
+
const activeWs = resolveWorkspace(xwid);
|
|
1190
|
+
c.set('workspace', activeWs);
|
|
1191
|
+
// Track the most-recently-viewed workspace so the shared-preview watcher can
|
|
1192
|
+
// tag previews with the workspace the user is actually collaborating in
|
|
1193
|
+
// (not the boot-default launch folder). (tk-5d09bf04-5.)
|
|
1194
|
+
if (xwid && activeWs?.id) lastActiveWorkspaceId = activeWs.id;
|
|
1184
1195
|
// Block the API for denied (non-localhost, unauthenticated) requests, but
|
|
1185
1196
|
// let static assets + the public endpoints through so the SPA can still
|
|
1186
1197
|
// load and prompt for sign-in. (Concern C1.)
|
|
@@ -3704,10 +3715,18 @@ export async function createServer(overrides = {}) {
|
|
|
3704
3715
|
// shared slug for member discovery (the picker covers the multi-server edge).
|
|
3705
3716
|
// Skipped under the test runner; tests drive reconcilePreviews directly.
|
|
3706
3717
|
let previewTimer = null;
|
|
3707
|
-
|
|
3718
|
+
// Tag previews with the workspace the user is actively collaborating in (active
|
|
3719
|
+
// → sole shared workspace → boot-default), so a teammate's Live view discovers
|
|
3720
|
+
// them. Tagging only with the boot-default (a local launch folder) meant
|
|
3721
|
+
// cross-member discovery never fired. (tk-5d09bf04-5.)
|
|
3722
|
+
function previewSlug() {
|
|
3708
3723
|
try {
|
|
3709
|
-
|
|
3710
|
-
|
|
3724
|
+
return pickPreviewSlug({
|
|
3725
|
+
activeId: lastActiveWorkspaceId,
|
|
3726
|
+
bootDefaultId: config.workspaceId,
|
|
3727
|
+
resolve: (id) => getWorkspace(id, registryEnv),
|
|
3728
|
+
all: () => listWorkspaces(registryEnv),
|
|
3729
|
+
});
|
|
3711
3730
|
} catch {
|
|
3712
3731
|
return null;
|
|
3713
3732
|
}
|
|
@@ -3721,7 +3740,7 @@ export async function createServer(overrides = {}) {
|
|
|
3721
3740
|
detect: () => detectPreviewPorts(),
|
|
3722
3741
|
registry: localPreviews,
|
|
3723
3742
|
rails: previewRails,
|
|
3724
|
-
slug:
|
|
3743
|
+
slug: previewSlug(),
|
|
3725
3744
|
initiator: config.account?.email || config.account?.slug || null,
|
|
3726
3745
|
selfPort: config.port,
|
|
3727
3746
|
});
|
|
@@ -204,6 +204,32 @@ export function previewUrlFor(shareBaseUrl, token) {
|
|
|
204
204
|
return `https://${parts.join('.')}`;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
// Choose which shared-workspace slug to TAG a published preview with, so
|
|
208
|
+
// collaborators can discover it (the membership-gated `/api/preview/list` filters
|
|
209
|
+
// by this tag). A raw dev port can't be perfectly attributed to a workspace, so:
|
|
210
|
+
// 1. the workspace the user is actively in (last X-Workspace-Id) — strongest;
|
|
211
|
+
// 2. else the SOLE shared workspace, if the install has exactly one — the
|
|
212
|
+
// unambiguous common case (a small team in one shared workspace);
|
|
213
|
+
// 3. else the boot-default workspace (original behaviour; usually local → null).
|
|
214
|
+
// `resolve(id)` → a registry entry (with `kind`/`shared`) or null; `all()` → all
|
|
215
|
+
// entries. Pure (deps injected) so it's unit-testable without a live registry.
|
|
216
|
+
//
|
|
217
|
+
// WHY this exists: tagging with the boot-default alone (the install's launch
|
|
218
|
+
// folder, almost always a LOCAL workspace) meant cross-member discovery never
|
|
219
|
+
// fired — a teammate's preview never appeared in your picker. (tk-5d09bf04-5.)
|
|
220
|
+
export function pickPreviewSlug({ activeId, bootDefaultId, resolve, all }) {
|
|
221
|
+
const sharedSlugOf = (id) => {
|
|
222
|
+
if (!id) return null;
|
|
223
|
+
const e = resolve(id);
|
|
224
|
+
return e && e.kind === 'shared' && e.shared && e.shared.slug ? e.shared.slug : null;
|
|
225
|
+
};
|
|
226
|
+
const active = sharedSlugOf(activeId);
|
|
227
|
+
if (active) return active;
|
|
228
|
+
const shared = (all() || []).filter((w) => w && w.kind === 'shared' && w.shared && w.shared.slug);
|
|
229
|
+
if (shared.length === 1) return shared[0].shared.slug;
|
|
230
|
+
return sharedSlugOf(bootDefaultId);
|
|
231
|
+
}
|
|
232
|
+
|
|
207
233
|
// ---------------------------------------------------------------------------
|
|
208
234
|
// Dev-port reconcile (Part B) — one detection pass, kept pure for unit tests.
|
|
209
235
|
// ---------------------------------------------------------------------------
|