@jskit-ai/workspaces-web 0.1.139-oldline.2 → 0.1.140
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 +247 -9
- package/patterns/workspace-surfaces/PATTERN.md +63 -0
- package/src/client/account-settings/accountSettingsInvitesRuntime.js +2 -2
- package/src/client/account-settings/useAccountSettingsInvitesSectionRuntime.js +3 -3
- package/src/client/components/MembersAdminClientElement.vue +1 -1
- package/src/client/components/WorkspaceInviteLanding.vue +4 -4
- package/src/client/components/WorkspaceMembersClientElement.vue +6 -6
- package/src/client/components/{UsersWorkspaceMembersMenuItem.vue → WorkspaceMembersMenuItem.vue} +2 -2
- package/src/client/components/{UsersWorkspacePermissionMenuItem.vue → WorkspacePermissionMenuItem.vue} +3 -3
- package/src/client/components/{UsersProfileSurfaceSwitchMenuItem.vue → WorkspaceProfileSurfaceSwitchMenuItem.vue} +1 -1
- package/src/client/components/{UsersWorkspaceSelector.vue → WorkspaceSelector.vue} +1 -1
- package/src/client/components/{UsersWorkspaceSettingsMenuItem.vue → WorkspaceSettingsMenuItem.vue} +2 -2
- package/src/client/components/WorkspacesClientElement.vue +13 -13
- package/src/client/composables/useWorkspaceRouteContext.js +1 -1
- package/src/client/index.js +0 -5
- package/src/client/lib/surfaceAccessPolicy.js +4 -1
- package/src/client/providers/WorkspacesWebClientProvider.js +54 -50
- package/src/client/runtime/bootstrapPlacementRuntime.js +18 -32
- package/src/client/runtime/bootstrapPlacementRuntimeHelpers.js +4 -4
- package/test/bootstrapPlacementRuntime.test.js +40 -125
- package/test/packageMetadataOwnership.test.js +19 -0
- package/test/settingsPlacementContract.test.js +87 -389
- package/test/{usersWorkspaceSelectorContract.test.js → workspaceSelectorContract.test.js} +3 -3
- package/package.descriptor.mjs +0 -492
- package/src/client/lib/httpClient.js +0 -10
- package/src/client/lib/permissions.js +0 -27
- package/test/packageDescriptorOwnership.test.js +0 -16
- /package/{templates → patterns/workspace-surfaces/example}/packages/main/src/client/components/AccountPendingInvitesCue.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/packages/main/src/client/components/AccountSettingsInvitesSection.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/components/WorkspaceNotFoundCard.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/composables/useWorkspaceNotFoundState.js +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/pages/admin/members/index.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/pages/admin/workspace/settings/index.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/pages/admin/workspace/settings.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/pages/invite/[token].vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/surfaces/admin/index.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/surfaces/admin/root.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/surfaces/app/index.vue +0 -0
- /package/{templates → patterns/workspace-surfaces/example}/src/surfaces/app/root.vue +0 -0
- /package/src/client/components/{UsersWorkspaceToolsWidget.vue → WorkspaceToolsWidget.vue} +0 -0
|
@@ -1,35 +1,48 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
import WorkspaceProfileSurfaceSwitchMenuItem from "../components/WorkspaceProfileSurfaceSwitchMenuItem.vue";
|
|
2
|
+
import WorkspaceSelector from "../components/WorkspaceSelector.vue";
|
|
3
|
+
import WorkspaceToolsWidget from "../components/WorkspaceToolsWidget.vue";
|
|
4
|
+
import WorkspaceSettingsMenuItem from "../components/WorkspaceSettingsMenuItem.vue";
|
|
5
|
+
import WorkspaceMembersMenuItem from "../components/WorkspaceMembersMenuItem.vue";
|
|
6
6
|
import MembersAdminClientElement from "../components/MembersAdminClientElement.vue";
|
|
7
|
-
import { registerBootstrapPayloadHandler } from "@jskit-ai/shell-web/client/bootstrap";
|
|
8
7
|
import { createBootstrapPlacementRuntime } from "../runtime/bootstrapPlacementRuntime.js";
|
|
9
8
|
import {
|
|
10
9
|
WORKSPACES_WEB_SCOPE_SUPPORT_INJECTION_KEY,
|
|
11
10
|
createWorkspaceScopeSupport
|
|
12
11
|
} from "../support/workspaceScopeSupport.js";
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const WorkspacesWebClientProvider = defineProvider({
|
|
14
|
+
id: "workspaces.web.client",
|
|
15
|
+
requires: {
|
|
16
|
+
components: "client.components",
|
|
17
|
+
logger: "client.logger",
|
|
18
|
+
router: "client.router",
|
|
19
|
+
shell: "client.shell",
|
|
20
|
+
vueApp: "client.vue"
|
|
21
|
+
},
|
|
22
|
+
optional: {
|
|
23
|
+
realtime: "client.realtime"
|
|
24
|
+
},
|
|
25
|
+
provides: {
|
|
26
|
+
workspaces: "client.workspaces"
|
|
27
|
+
},
|
|
28
|
+
setup({ components, logger, realtime, router, shell, vueApp }) {
|
|
29
|
+
components.register("workspaces.web.profile.menu.surface-switch-item", WorkspaceProfileSurfaceSwitchMenuItem);
|
|
30
|
+
components.register("workspaces.web.workspace.selector", WorkspaceSelector);
|
|
31
|
+
components.register("workspaces.web.workspace.tools.widget", WorkspaceToolsWidget);
|
|
32
|
+
components.register("workspaces.web.workspace-settings.menu-item", WorkspaceSettingsMenuItem);
|
|
33
|
+
components.register("workspaces.web.workspace-members.menu-item", WorkspaceMembersMenuItem);
|
|
34
|
+
components.register("workspaces.web.members-admin.element", MembersAdminClientElement);
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
app.singleton("workspaces.web.members-admin.element", () => MembersAdminClientElement);
|
|
29
|
-
app.singleton("workspaces.web.bootstrap-placement.runtime", (scope) => createBootstrapPlacementRuntime({ app: scope }));
|
|
30
|
-
registerBootstrapPayloadHandler(app, "workspaces.web.bootstrap.handler", (scope) => {
|
|
31
|
-
const runtime = scope.make("workspaces.web.bootstrap-placement.runtime");
|
|
32
|
-
return Object.freeze({
|
|
36
|
+
const runtime = createBootstrapPlacementRuntime({
|
|
37
|
+
bootstrapRuntime: shell.bootstrap,
|
|
38
|
+
placementRuntime: shell.placement,
|
|
39
|
+
realtime,
|
|
40
|
+
router,
|
|
41
|
+
vueApp,
|
|
42
|
+
logger
|
|
43
|
+
});
|
|
44
|
+
shell.bootstrapHandlers.register(
|
|
45
|
+
Object.freeze({
|
|
33
46
|
handlerId: "workspaces.web.bootstrap",
|
|
34
47
|
order: 100,
|
|
35
48
|
resolveBootstrapRequest(input = {}) {
|
|
@@ -41,46 +54,37 @@ class WorkspacesWebClientProvider {
|
|
|
41
54
|
handleBootstrapError(input = {}) {
|
|
42
55
|
return runtime.handleBootstrapError(input);
|
|
43
56
|
}
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const runtime =
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
return {
|
|
60
|
+
workspaces: Object.freeze({
|
|
61
|
+
bootstrap: runtime,
|
|
62
|
+
scopeSupport: createWorkspaceScopeSupport()
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
async boot({ vueApp }, { outputs }) {
|
|
67
|
+
const runtime = outputs.workspaces.bootstrap;
|
|
55
68
|
if (runtime && typeof runtime.initialize === "function") {
|
|
56
69
|
await runtime.initialize();
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
if (!app.has("jskit.client.vue.app")) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const vueApp = app.make("jskit.client.vue.app");
|
|
64
72
|
if (!vueApp || typeof vueApp.provide !== "function") {
|
|
65
73
|
return;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
vueApp.provide(
|
|
69
77
|
WORKSPACES_WEB_SCOPE_SUPPORT_INJECTION_KEY,
|
|
70
|
-
|
|
78
|
+
outputs.workspaces.scopeSupport
|
|
71
79
|
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!app || typeof app.make !== "function") {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const runtime = app.make("workspaces.web.bootstrap-placement.runtime");
|
|
80
|
+
},
|
|
81
|
+
shutdown(_dependencies, { outputs }) {
|
|
82
|
+
const runtime = outputs.workspaces.bootstrap;
|
|
80
83
|
if (runtime && typeof runtime.shutdown === "function") {
|
|
81
84
|
runtime.shutdown();
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
|
-
}
|
|
87
|
+
});
|
|
85
88
|
|
|
86
89
|
export { WorkspacesWebClientProvider };
|
|
90
|
+
import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
findWorkspaceBySlug,
|
|
3
3
|
normalizeWorkspaceList
|
|
4
4
|
} from "../lib/bootstrap.js";
|
|
5
|
-
import { normalizePermissionList } from "
|
|
5
|
+
import { normalizePermissionList } from "@jskit-ai/http-web/client/lib/permissions";
|
|
6
6
|
import {
|
|
7
7
|
persistBootstrapThemePreference,
|
|
8
8
|
resolveBootstrapThemeName,
|
|
@@ -28,21 +28,24 @@ import {
|
|
|
28
28
|
} from "./bootstrapPlacementRuntimeHelpers.js";
|
|
29
29
|
import { resolveErrorStatusCode } from "../support/runtimeNormalization.js";
|
|
30
30
|
|
|
31
|
-
function createBootstrapPlacementRuntime({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
function createBootstrapPlacementRuntime({
|
|
32
|
+
bootstrapRuntime,
|
|
33
|
+
placementRuntime,
|
|
34
|
+
realtime = null,
|
|
35
|
+
router = null,
|
|
36
|
+
vueApp = null,
|
|
37
|
+
logger = null
|
|
38
|
+
} = {}) {
|
|
39
|
+
if (!placementRuntime || typeof placementRuntime.getContext !== "function") {
|
|
36
40
|
throw new Error("createBootstrapPlacementRuntime requires shell-web placement runtime.");
|
|
37
41
|
}
|
|
42
|
+
if (!bootstrapRuntime || typeof bootstrapRuntime.refresh !== "function") {
|
|
43
|
+
throw new Error("createBootstrapPlacementRuntime requires shell-web bootstrap runtime.");
|
|
44
|
+
}
|
|
38
45
|
|
|
39
|
-
const runtimeLogger =
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
let vuetifyThemeController = resolveVuetifyThemeController(
|
|
43
|
-
app.has("jskit.client.vue.app") ? app.make("jskit.client.vue.app") : null
|
|
44
|
-
);
|
|
45
|
-
const socket = app.has("runtime.realtime.client.socket") ? app.make("runtime.realtime.client.socket") : null;
|
|
46
|
+
const runtimeLogger = createProviderLogger(logger);
|
|
47
|
+
let vuetifyThemeController = resolveVuetifyThemeController(vueApp);
|
|
48
|
+
const socket = realtime?.socket || null;
|
|
46
49
|
const cleanup = [];
|
|
47
50
|
let shutdownRequested = false;
|
|
48
51
|
let lastRouteWorkspaceSlug = resolveRouteState(placementRuntime, router).workspaceSlug;
|
|
@@ -181,11 +184,7 @@ function createBootstrapPlacementRuntime({ app, logger = null } = {}) {
|
|
|
181
184
|
if (vuetifyThemeController) {
|
|
182
185
|
return vuetifyThemeController;
|
|
183
186
|
}
|
|
184
|
-
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
vuetifyThemeController = resolveVuetifyThemeController(app.make("jskit.client.vue.app"));
|
|
187
|
+
vuetifyThemeController = resolveVuetifyThemeController(vueApp);
|
|
189
188
|
return vuetifyThemeController;
|
|
190
189
|
}
|
|
191
190
|
|
|
@@ -386,25 +385,12 @@ function createBootstrapPlacementRuntime({ app, logger = null } = {}) {
|
|
|
386
385
|
return null;
|
|
387
386
|
}
|
|
388
387
|
|
|
389
|
-
function resolveBootstrapRuntime() {
|
|
390
|
-
if (!app.has("runtime.web-bootstrap.client")) {
|
|
391
|
-
throw new Error("createBootstrapPlacementRuntime requires shell-web bootstrap runtime.");
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const bootstrapRuntime = app.make("runtime.web-bootstrap.client");
|
|
395
|
-
if (!bootstrapRuntime || typeof bootstrapRuntime.refresh !== "function") {
|
|
396
|
-
throw new Error("createBootstrapPlacementRuntime requires runtime.web-bootstrap.client.refresh().");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return bootstrapRuntime;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
388
|
function refresh(reason = "manual") {
|
|
403
389
|
if (shutdownRequested) {
|
|
404
390
|
return Promise.resolve(null);
|
|
405
391
|
}
|
|
406
392
|
|
|
407
|
-
return
|
|
393
|
+
return bootstrapRuntime.refresh(reason);
|
|
408
394
|
}
|
|
409
395
|
|
|
410
396
|
function enforceCurrentWorkspaceStatus() {
|
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
} from "@jskit-ai/shell-web/client/placement";
|
|
8
8
|
import { parseWorkspacePathname } from "@jskit-ai/workspaces-core/shared/support/workspacePathModel";
|
|
9
9
|
import { extractWorkspaceSlugFromSurfacePathname } from "../lib/workspaceSurfacePaths.js";
|
|
10
|
-
import {
|
|
10
|
+
import { httpWebClient } from "@jskit-ai/http-web/client/lib/httpClient";
|
|
11
11
|
import { buildBootstrapApiPath } from "../lib/bootstrap.js";
|
|
12
12
|
import {
|
|
13
13
|
normalizeWorkspaceBootstrapStatusValue
|
|
14
14
|
} from "../support/runtimeNormalization.js";
|
|
15
15
|
import { WORKSPACE_BOOTSTRAP_STATUSES } from "./bootstrapPlacementRuntimeConstants.js";
|
|
16
16
|
|
|
17
|
-
function createProviderLogger(
|
|
18
|
-
return createSharedProviderLogger(
|
|
17
|
+
function createProviderLogger(logger) {
|
|
18
|
+
return createSharedProviderLogger(logger);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function resolveRouteState(placementRuntime, router) {
|
|
@@ -127,7 +127,7 @@ function countPendingInvites(entries = []) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
async function fetchBootstrapPayload(workspaceSlug = "") {
|
|
130
|
-
return
|
|
130
|
+
return httpWebClient.request(buildBootstrapApiPath(workspaceSlug), {
|
|
131
131
|
method: "GET"
|
|
132
132
|
});
|
|
133
133
|
}
|
|
@@ -183,20 +183,20 @@ function createBootstrapRuntimeStub() {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
186
|
+
function createTestRuntime({
|
|
187
|
+
placementRuntime,
|
|
188
|
+
router,
|
|
189
|
+
bootstrapRuntime = createBootstrapRuntimeStub(),
|
|
190
|
+
socket = null,
|
|
191
|
+
vueApp = null
|
|
192
|
+
} = {}) {
|
|
193
|
+
return createBootstrapPlacementRuntime({
|
|
194
|
+
placementRuntime,
|
|
195
|
+
bootstrapRuntime,
|
|
196
|
+
realtime: socket ? { socket } : null,
|
|
197
|
+
router,
|
|
198
|
+
vueApp
|
|
199
|
+
});
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
function createVuetifyThemeController(initial = "light") {
|
|
@@ -265,13 +265,7 @@ function createErrorWithStatus(status, message = "") {
|
|
|
265
265
|
test("bootstrap placement runtime contributes workspace slug to shared bootstrap request and writes payload into placement context", async () => {
|
|
266
266
|
const placementRuntime = createPlacementRuntimeStub();
|
|
267
267
|
const router = createRouterStub("/w/acme/dashboard");
|
|
268
|
-
const runtime =
|
|
269
|
-
app: createAppStub({
|
|
270
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
271
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
272
|
-
["jskit.client.router"]: router
|
|
273
|
-
})
|
|
274
|
-
});
|
|
268
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
275
269
|
|
|
276
270
|
await runtime.initialize();
|
|
277
271
|
const request = runtime.resolveBootstrapRequest();
|
|
@@ -329,13 +323,7 @@ test("bootstrap placement runtime resolves workspace slug from pathname when sur
|
|
|
329
323
|
const placementRuntime = createPlacementRuntimeStub();
|
|
330
324
|
placementRuntime.setContext({}, { replace: true, source: "test.clear" });
|
|
331
325
|
const router = createRouterStub("/w/acme/admin");
|
|
332
|
-
const runtime =
|
|
333
|
-
app: createAppStub({
|
|
334
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
335
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
336
|
-
["jskit.client.router"]: router
|
|
337
|
-
})
|
|
338
|
-
});
|
|
326
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
339
327
|
|
|
340
328
|
await runtime.initialize();
|
|
341
329
|
const request = runtime.resolveBootstrapRequest();
|
|
@@ -378,13 +366,7 @@ test("bootstrap placement runtime does not mutate placement auth context", async
|
|
|
378
366
|
{ source: "test.seed" }
|
|
379
367
|
);
|
|
380
368
|
const router = createRouterStub("/w/acme/dashboard");
|
|
381
|
-
const runtime =
|
|
382
|
-
app: createAppStub({
|
|
383
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
384
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
385
|
-
["jskit.client.router"]: router
|
|
386
|
-
})
|
|
387
|
-
});
|
|
369
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
388
370
|
|
|
389
371
|
await runtime.initialize();
|
|
390
372
|
await runtime.applyBootstrapPayload({
|
|
@@ -418,14 +400,7 @@ test("bootstrap placement runtime delegates route and realtime refreshes to the
|
|
|
418
400
|
const router = createRouterStub("/w/acme/dashboard");
|
|
419
401
|
const socket = createSocketStub();
|
|
420
402
|
const bootstrapRuntime = createBootstrapRuntimeStub();
|
|
421
|
-
const runtime =
|
|
422
|
-
app: createAppStub({
|
|
423
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
424
|
-
["runtime.web-bootstrap.client"]: bootstrapRuntime,
|
|
425
|
-
["jskit.client.router"]: router,
|
|
426
|
-
["runtime.realtime.client.socket"]: socket
|
|
427
|
-
})
|
|
428
|
-
});
|
|
403
|
+
const runtime = createTestRuntime({ placementRuntime, bootstrapRuntime, router, socket });
|
|
429
404
|
|
|
430
405
|
await runtime.initialize();
|
|
431
406
|
assert.deepEqual(bootstrapRuntime.calls, []);
|
|
@@ -451,13 +426,10 @@ test("bootstrap placement runtime applies theme changes from bootstrap payloads"
|
|
|
451
426
|
const placementRuntime = createPlacementRuntimeStub();
|
|
452
427
|
const router = createRouterStub("/w/acme/dashboard");
|
|
453
428
|
const themeController = createVuetifyThemeController("light");
|
|
454
|
-
const runtime =
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
["jskit.client.router"]: router,
|
|
459
|
-
["jskit.client.vue.app"]: createVueAppWithThemeController(themeController)
|
|
460
|
-
})
|
|
429
|
+
const runtime = createTestRuntime({
|
|
430
|
+
placementRuntime,
|
|
431
|
+
router,
|
|
432
|
+
vueApp: createVueAppWithThemeController(themeController)
|
|
461
433
|
});
|
|
462
434
|
|
|
463
435
|
await runtime.initialize();
|
|
@@ -499,13 +471,10 @@ test("bootstrap placement runtime applies workspace palette and clears it when l
|
|
|
499
471
|
const placementRuntime = createPlacementRuntimeStub();
|
|
500
472
|
const router = createRouterStub("/w/acme/dashboard");
|
|
501
473
|
const themeController = createVuetifyThemeController("light");
|
|
502
|
-
const runtime =
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
["jskit.client.router"]: router,
|
|
507
|
-
["jskit.client.vue.app"]: createVueAppWithThemeController(themeController)
|
|
508
|
-
})
|
|
474
|
+
const runtime = createTestRuntime({
|
|
475
|
+
placementRuntime,
|
|
476
|
+
router,
|
|
477
|
+
vueApp: createVueAppWithThemeController(themeController)
|
|
509
478
|
});
|
|
510
479
|
|
|
511
480
|
await runtime.initialize();
|
|
@@ -575,12 +544,9 @@ test("bootstrap placement runtime marks workspace slug as not_found and clears w
|
|
|
575
544
|
},
|
|
576
545
|
{ source: "test.seed" }
|
|
577
546
|
);
|
|
578
|
-
const runtime =
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
582
|
-
["jskit.client.router"]: createRouterStub("/w/acme/dashboard")
|
|
583
|
-
})
|
|
547
|
+
const runtime = createTestRuntime({
|
|
548
|
+
placementRuntime,
|
|
549
|
+
router: createRouterStub("/w/acme/dashboard")
|
|
584
550
|
});
|
|
585
551
|
|
|
586
552
|
await runtime.initialize();
|
|
@@ -606,13 +572,7 @@ test("bootstrap placement runtime tracks status per workspace slug across route
|
|
|
606
572
|
const placementRuntime = createPlacementRuntimeStub();
|
|
607
573
|
const router = createRouterStub("/w/acme/dashboard");
|
|
608
574
|
const bootstrapRuntime = createBootstrapRuntimeStub();
|
|
609
|
-
const runtime =
|
|
610
|
-
app: createAppStub({
|
|
611
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
612
|
-
["runtime.web-bootstrap.client"]: bootstrapRuntime,
|
|
613
|
-
["jskit.client.router"]: router
|
|
614
|
-
})
|
|
615
|
-
});
|
|
575
|
+
const runtime = createTestRuntime({ placementRuntime, bootstrapRuntime, router });
|
|
616
576
|
|
|
617
577
|
await runtime.initialize();
|
|
618
578
|
await runtime.applyBootstrapPayload({
|
|
@@ -651,13 +611,7 @@ test("bootstrap placement runtime tracks status per workspace slug across route
|
|
|
651
611
|
test("bootstrap placement runtime uses requestedWorkspace status and keeps global workspace list on inaccessible slug", async () => {
|
|
652
612
|
const placementRuntime = createPlacementRuntimeStub();
|
|
653
613
|
const router = createRouterStub("/w/tonymobily");
|
|
654
|
-
const runtime =
|
|
655
|
-
app: createAppStub({
|
|
656
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
657
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
658
|
-
["jskit.client.router"]: router
|
|
659
|
-
})
|
|
660
|
-
});
|
|
614
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
661
615
|
|
|
662
616
|
await runtime.initialize();
|
|
663
617
|
await runtime.applyBootstrapPayload({
|
|
@@ -695,13 +649,7 @@ test("bootstrap placement runtime uses requestedWorkspace status and keeps globa
|
|
|
695
649
|
test("bootstrap placement runtime uses requestedWorkspace=not_found without forcing forbidden fallback", async () => {
|
|
696
650
|
const placementRuntime = createPlacementRuntimeStub();
|
|
697
651
|
const router = createRouterStub("/w/missing");
|
|
698
|
-
const runtime =
|
|
699
|
-
app: createAppStub({
|
|
700
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
701
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
702
|
-
["jskit.client.router"]: router
|
|
703
|
-
})
|
|
704
|
-
});
|
|
652
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
705
653
|
|
|
706
654
|
await runtime.initialize();
|
|
707
655
|
await runtime.applyBootstrapPayload({
|
|
@@ -745,13 +693,7 @@ test("bootstrap placement runtime guard wrapper preserves delegated deny outcome
|
|
|
745
693
|
});
|
|
746
694
|
globalThis[SHELL_GUARD_EVALUATOR_KEY] = () => delegatedOutcome;
|
|
747
695
|
|
|
748
|
-
const runtime =
|
|
749
|
-
app: createAppStub({
|
|
750
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
751
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
752
|
-
["jskit.client.router"]: router
|
|
753
|
-
})
|
|
754
|
-
});
|
|
696
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
755
697
|
|
|
756
698
|
await runtime.initialize();
|
|
757
699
|
await runtime.applyBootstrapPayload({
|
|
@@ -789,13 +731,7 @@ test("bootstrap placement runtime guard wrapper preserves delegated deny outcome
|
|
|
789
731
|
test("bootstrap placement runtime guard wrapper blocks forbidden workspace routes and redirects nested workspace paths", async () => {
|
|
790
732
|
const placementRuntime = createPlacementRuntimeStub();
|
|
791
733
|
const router = createRouterStub("/w/acme/admin/workspace/settings?tab=general");
|
|
792
|
-
const runtime =
|
|
793
|
-
app: createAppStub({
|
|
794
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
795
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
796
|
-
["jskit.client.router"]: router
|
|
797
|
-
})
|
|
798
|
-
});
|
|
734
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
799
735
|
|
|
800
736
|
await runtime.initialize();
|
|
801
737
|
await runtime.handleBootstrapError({
|
|
@@ -831,13 +767,7 @@ test("bootstrap placement runtime guard wrapper blocks forbidden workspace route
|
|
|
831
767
|
test("bootstrap placement runtime guard wrapper redirects nested not_found routes to workspace surface root", async () => {
|
|
832
768
|
const placementRuntime = createPlacementRuntimeStub();
|
|
833
769
|
const router = createRouterStub("/w/acme/projects");
|
|
834
|
-
const runtime =
|
|
835
|
-
app: createAppStub({
|
|
836
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
837
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
838
|
-
["jskit.client.router"]: router
|
|
839
|
-
})
|
|
840
|
-
});
|
|
770
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
841
771
|
|
|
842
772
|
await runtime.initialize();
|
|
843
773
|
await runtime.handleBootstrapError({
|
|
@@ -891,13 +821,7 @@ test("bootstrap placement runtime guard wrapper redirects nested not_found route
|
|
|
891
821
|
test("bootstrap placement runtime redirects admin nested route to admin root when workspace is not_found", async () => {
|
|
892
822
|
const placementRuntime = createPlacementRuntimeStub();
|
|
893
823
|
const router = createRouterStub("/w/acme/admin/workspace/settings");
|
|
894
|
-
const runtime =
|
|
895
|
-
app: createAppStub({
|
|
896
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
897
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
898
|
-
["jskit.client.router"]: router
|
|
899
|
-
})
|
|
900
|
-
});
|
|
824
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
901
825
|
|
|
902
826
|
await runtime.initialize();
|
|
903
827
|
await runtime.handleBootstrapError({
|
|
@@ -950,13 +874,7 @@ test("bootstrap placement runtime enforces surface access policies after bootstr
|
|
|
950
874
|
}
|
|
951
875
|
});
|
|
952
876
|
const router = createRouterStub("/ops");
|
|
953
|
-
const runtime =
|
|
954
|
-
app: createAppStub({
|
|
955
|
-
["runtime.web-placement.client"]: placementRuntime,
|
|
956
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
957
|
-
["jskit.client.router"]: router
|
|
958
|
-
})
|
|
959
|
-
});
|
|
877
|
+
const runtime = createTestRuntime({ placementRuntime, router });
|
|
960
878
|
|
|
961
879
|
await runtime.initialize();
|
|
962
880
|
await runtime.applyBootstrapPayload({
|
|
@@ -976,12 +894,9 @@ test("bootstrap placement runtime enforces surface access policies after bootstr
|
|
|
976
894
|
|
|
977
895
|
test("bootstrap placement runtime handles unauthenticated errors and marks workspace status", async () => {
|
|
978
896
|
const placementRuntime = createPlacementRuntimeStub();
|
|
979
|
-
const runtime =
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
["runtime.web-bootstrap.client"]: createBootstrapRuntimeStub(),
|
|
983
|
-
["jskit.client.router"]: createRouterStub("/w/acme/dashboard")
|
|
984
|
-
})
|
|
897
|
+
const runtime = createTestRuntime({
|
|
898
|
+
placementRuntime,
|
|
899
|
+
router: createRouterStub("/w/acme/dashboard")
|
|
985
900
|
});
|
|
986
901
|
|
|
987
902
|
await runtime.initialize();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
7
|
+
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
|
|
10
|
+
test("workspaces-web publishes editable workspace surfaces as an agent-readable pattern", async () => {
|
|
11
|
+
assert.equal(packageJson.jskit?.mutations, undefined);
|
|
12
|
+
const pattern = await readFile(path.join(packageRoot, "patterns", "workspace-surfaces", "PATTERN.md"), "utf8");
|
|
13
|
+
|
|
14
|
+
assert.match(pattern, /## Product decisions/);
|
|
15
|
+
assert.match(pattern, /## Invariants/);
|
|
16
|
+
assert.match(pattern, /Loading uses skeletons/);
|
|
17
|
+
assert.match(pattern, /mutation failures use toasts/);
|
|
18
|
+
assert.match(pattern, /## Example files/);
|
|
19
|
+
});
|