Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions kits/workplace/app/src/lib/workspace.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
// The workspace this page is open in, the way the console decides it.
//
// A kit is launched per workspace and the gateway scopes every call by two headers the console
// adds from its own record of the active workspace (localStorage, one key per org). This app is
// served on the console's origin, so it reads the same record and adds the same headers to its
// own calls; without them the gateway answers unscoped, and on an instance where the kit has been
// adds from its record of the active workspace (a cookie on this origin, and one localStorage key
// per organization). This app is served on the console's origin, so it reads the same record and
// adds the same headers to its own calls; without them the gateway answers unscoped, and on an instance where the kit has been
// launched in two workspaces the app picked the wrong recruiter (2026-09-30). The shared
// transport (reifyui/harness) takes no headers yet, so they are added where every call goes
// through: fetch, for this origin's API path only.
const PREFIX = 'hr.workspace.current.';
const API = '/api/harness/';

const COOKIE = 'hr_workspace';

/** The workspace this page is open in, as the console says it.
*
* A hosted console writes it as a cookie on this origin when it opens a kit (its own API proxy
* scopes by that cookie too), so the cookie is the answer wherever it exists. The per-org record
* in localStorage is the fallback for a console that keeps only that: a browser signed in to
* several organizations holds one record per organization, and the last one written is not the
* one this page is open in (a console with eight organizations opened the workplace and the app
* asked about another organization's workspace, so it said the workplace was not launched,
* 2026-10-01). */
export function currentWorkspace() {
try {
const m = document.cookie.match(new RegExp('(?:^|;\\s*)' + COOKIE + '=([^;]*)'));
if (m) {
const [id, def] = decodeURIComponent(m[1]).split('|');
if (id) return { id: String(id), isDefault: def === '1' };
}
} catch { /* no document, or the cookie is unreadable here */ }
try {
const keys = Object.keys(localStorage).filter((k) => k.startsWith(PREFIX));
// One org per browser on a self-hosted instance; on a hosted one the console keeps a key per
// org it has shown, and the newest write wins there too (a single key in practice).
for (const k of keys.reverse()) {
const raw = JSON.parse(localStorage.getItem(k) || 'null');
if (raw && raw.id) return { id: String(raw.id), isDefault: !!raw.def };
Expand Down
37 changes: 37 additions & 0 deletions kits/workplace/app/src/lib/workspace.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// The workspace this page is open in: the console's cookie decides; the per-organization records
// are the fallback; nothing known is null. A browser signed in to several organizations holds a
// record per organization, and the last one written was not the one the page was open in.
import test from 'node:test';
import assert from 'node:assert/strict';

function world({ cookie = '', records = {} } = {}) {
globalThis.document = { cookie };
const store = { ...records };
globalThis.localStorage = { getItem: (k) => (k in store ? store[k] : null) };
Object.defineProperty(globalThis.localStorage, 'keys', { value: () => Object.keys(store) });
globalThis.Object.keys = ((orig) => (o) => (o === globalThis.localStorage ? Object.getOwnPropertyNames(store) : orig(o)))(Object.keys);
}
const { currentWorkspace, workspaceHeaders } = await import('./workspace.js');

test('the cookie names the workspace even when other organizations left records', () => {
world({ cookie: 'hr_auth=secret; hr_workspace=org.epsilla__hr_default%7C1',
records: { 'hr.workspace.current.org.a': JSON.stringify({ id: 'org.a__hr_default', def: true }),
'hr.workspace.current.org.epsilla': JSON.stringify({ id: 'org.epsilla__hr_default', def: true }),
'hr.workspace.current.org.z': JSON.stringify({ id: 'org.z__hr_default', def: true }) } });
assert.deepEqual(currentWorkspace(), { id: 'org.epsilla__hr_default', isDefault: true });
assert.deepEqual(workspaceHeaders(), { 'x-harness-workspace': 'org.epsilla__hr_default', 'x-harness-workspace-default': '1' });
});

test('a named, non-default workspace in the cookie carries no default flag', () => {
world({ cookie: 'hr_workspace=org.epsilla__hrws_7' });
assert.deepEqual(currentWorkspace(), { id: 'org.epsilla__hrws_7', isDefault: false });
assert.deepEqual(workspaceHeaders(), { 'x-harness-workspace': 'org.epsilla__hrws_7' });
});

test('without a cookie the record is the answer, and nothing known is null', () => {
world({ records: { 'hr.workspace.current.org.one': JSON.stringify({ id: 'org.one__hr_default', def: true }) } });
assert.deepEqual(currentWorkspace(), { id: 'org.one__hr_default', isDefault: true });
world();
assert.equal(currentWorkspace(), null);
assert.deepEqual(workspaceHeaders(), {});
});
Loading