@jskit-ai/agent-docs 0.1.279 → 0.1.281
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/guide/agent/app-extras/assistant-conversation.md +5 -1
- package/guide/agent/app-extras/assistant.md +123 -0
- package/guide/agent/app-extras/realtime.md +20 -10
- package/guide/agent/app-setup/authentication.md +54 -4
- package/guide/agent/app-setup/database-layer.md +108 -35
- package/guide/agent/app-setup/existing-application-migration.md +7 -1
- package/guide/agent/app-setup/material-3-migration.md +38 -9
- package/guide/agent/app-setup/multi-homing.md +14 -4
- package/guide/agent/app-setup/upgrading-jskit.md +105 -0
- package/guide/agent/app-setup/users.md +9 -1
- package/guide/agent/framework/client-features.md +35 -1
- package/guide/agent/framework/material-3.md +111 -71
- package/guide/agent/framework/sanity-checks.md +181 -0
- package/guide/agent/framework/translation.md +4 -0
- package/guide/agent/framework/typed-features.md +222 -120
- package/guide/agent/framework/ui-operations.md +3 -1
- package/guide/agent/index.md +3 -0
- package/package.json +2 -2
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/PATTERN.md +6 -2
- package/patterns/minimal-foundation/example/AGENTS.md +22 -5
- package/patterns/minimal-foundation/example/package.json +6 -6
- package/patterns/minimal-foundation/example/playwright.config.mjs +3 -30
- package/patterns/minimal-foundation/example/server.js +7 -1
- package/patterns/minimal-foundation/example/src/main.js +3 -2
- package/patterns/minimal-foundation/example/tests/e2e/startup.spec.ts +13 -0
- package/patterns/minimal-foundation/example/vite.config.mjs +3 -1
- package/patterns/shell-foundation/PATTERN.md +4 -1
- package/patterns/shell-foundation/example/AGENTS.md +22 -5
- package/patterns/shell-foundation/example/package.json +7 -7
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +3 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +3 -30
- package/patterns/shell-foundation/example/server.js +7 -1
- package/patterns/shell-foundation/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/main.js +3 -2
- package/patterns/shell-foundation/example/src/pages/home.vue +1 -1
- package/patterns/shell-foundation/example/src/views/NotFound.vue +1 -1
- package/patterns/shell-foundation/example/tests/e2e/startup.spec.ts +13 -0
- package/patterns/shell-foundation/example/vite.config.mjs +3 -1
- package/reference/autogen/PATTERN_INDEX.md +37 -37
- package/reference/autogen/packages/agent-docs.md +4 -0
- package/reference/autogen/packages/architecture-inspector.md +1 -0
- package/reference/autogen/packages/assistant-runtime.md +28 -3
- package/reference/autogen/packages/auth-provider-local-core.md +2 -1
- package/reference/autogen/packages/auth-web.md +10 -0
- package/reference/autogen/packages/connectors-catalog.md +8 -0
- package/reference/autogen/packages/crud-core.md +3 -0
- package/reference/autogen/packages/database-runtime.md +33 -0
- package/reference/autogen/packages/http-web.md +0 -4
- package/reference/autogen/packages/json-rest-api-core.md +17 -4
- package/reference/autogen/packages/kernel.md +37 -2
- package/reference/autogen/packages/material-web.md +2 -1
- package/reference/autogen/packages/oidc-issuer-core.md +0 -1
- package/reference/autogen/packages/realtime.md +8 -5
- package/reference/autogen/packages/shell-web.md +88 -3
- package/reference/autogen/packages/users-web.md +5 -2
- package/reference/autogen/packages/workspaces-core.md +3 -1
- package/reference/autogen/tooling/jskit-catalog.md +122 -5
- package/reference/autogen/tooling/testUtils.md +15 -1
- package/skills/jskit/SKILL.md +17 -5
- package/skills/jskit/references/client-features.md +35 -1
- package/skills/jskit/references/existing-application-migration.md +7 -1
- package/skills/jskit/references/heuristics/INDEX.md +3 -1
- package/skills/jskit/references/heuristics/row-policies.md +3 -3
- package/skills/jskit/references/heuristics/typed-features.md +7 -7
- package/skills/jskit/references/heuristics/ui-testing.md +27 -21
- package/skills/jskit/references/material-3.md +111 -71
- package/skills/jskit/references/multi-homing.md +14 -4
- package/skills/jskit/references/pattern-index.md +37 -37
- package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +6 -2
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +22 -5
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +6 -6
- package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +3 -30
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +7 -1
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +3 -2
- package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/e2e/startup.spec.ts +13 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/vite.config.mjs +3 -1
- package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +4 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +22 -5
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +7 -7
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +3 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +3 -30
- package/skills/jskit/references/patterns/app/shell-foundation/example/server.js +7 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +3 -2
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home.vue +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/e2e/startup.spec.ts +13 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/vite.config.mjs +3 -1
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +18 -5
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/components/AppBrand.vue +17 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/login.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/reset-password.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/signout.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/LoginView.vue +6 -1
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/ResetPasswordView.vue +6 -1
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/SignOutView.vue +6 -1
- package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/aws-storage-queries/example/formats/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console.vue +1 -1
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
- package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +34 -24
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +3 -2
- package/skills/jskit/references/patterns/database/mysql-application/example/tests/e2e/databases.js +25 -0
- package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +12 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +3 -2
- package/skills/jskit/references/patterns/database/postgres-application/example/tests/e2e/databases.js +25 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/rewards/google-rewarded/example/googlePublisherTag.js +5 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +11 -6
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home.vue +1 -1
- package/skills/jskit/references/patterns/shell/application-shell/example/src/views/NotFound.vue +1 -1
- package/skills/jskit/references/patterns/ui/page-and-placement/PATTERN.md +6 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +9 -4
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placementTopology.js +5 -3
- package/skills/jskit/references/patterns/users/account-settings/PATTERN.md +24 -3
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +1 -1
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +2 -1
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +3 -2
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/[section].vue +23 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account.vue +4 -3
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +3 -3
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/root.vue +1 -1
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/root.vue +1 -1
- package/skills/jskit/references/sanity-checks.md +181 -0
- package/skills/jskit/references/translation.md +4 -0
- package/skills/jskit/references/typed-features.md +222 -120
- package/skills/jskit/references/ui-operations.md +3 -1
- package/skills/jskit/references/patterns/database/mysql-application/example/tests/browser/database.js +0 -68
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"build:home": "VITE_SURFACE=home vite build",
|
|
25
25
|
"preview": "vite preview",
|
|
26
26
|
"lint": "eslint .",
|
|
27
|
-
"test": "
|
|
27
|
+
"test": "jskit test",
|
|
28
28
|
"test:client": "vitest run tests/client",
|
|
29
29
|
"test:e2e": "playwright test tests/e2e",
|
|
30
30
|
"jskit:update": "npx --yes @jskit-ai/jskit-catalog@latest update",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@local/main": "0.1.0",
|
|
36
36
|
"@fastify/static": "^10.1.3",
|
|
37
|
-
"@jskit-ai/kernel": "0.2.
|
|
38
|
-
"@jskit-ai/material-web": "0.1.
|
|
37
|
+
"@jskit-ai/kernel": "0.2.12",
|
|
38
|
+
"@jskit-ai/material-web": "0.1.3",
|
|
39
39
|
"@tanstack/vue-query": "^5.101.0",
|
|
40
40
|
"fastify": "^5.12.7",
|
|
41
41
|
"pino": "^10.3.1",
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"vue": "^3.5.44",
|
|
45
45
|
"vue-router": "^5.3.1",
|
|
46
46
|
"vuetify": "^4.3.0",
|
|
47
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
47
|
+
"@jskit-ai/http-runtime": "0.2.12",
|
|
48
48
|
"@fastify/fast-json-stringify-compiler": "^5.1.0",
|
|
49
49
|
"@vue/devtools-api": "^8.2.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@jskit-ai/config-eslint": "0.1.
|
|
53
|
-
"@jskit-ai/jskit-catalog": "0.2.
|
|
52
|
+
"@jskit-ai/config-eslint": "0.1.246",
|
|
53
|
+
"@jskit-ai/jskit-catalog": "0.2.33",
|
|
54
54
|
"@playwright/test": "1.61.1",
|
|
55
55
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
56
56
|
"eslint": "^10.8.0",
|
|
@@ -1,32 +1,5 @@
|
|
|
1
1
|
import { defineConfig } from "@playwright/test";
|
|
2
|
+
import { browserTestConfig } from "@jskit-ai/jskit-catalog/browser-tests";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
.replace(/\/+$/u, "");
|
|
6
|
-
const port = String(process.env.PLAYWRIGHT_PORT || "").trim() || "4173";
|
|
7
|
-
const baseURL = managedBaseUrl || `http://127.0.0.1:${port}`;
|
|
8
|
-
const storageState = String(process.env.PLAYWRIGHT_STORAGE_STATE || "").trim();
|
|
9
|
-
|
|
10
|
-
export default defineConfig({
|
|
11
|
-
testDir: "./tests/e2e",
|
|
12
|
-
timeout: 60_000,
|
|
13
|
-
expect: {
|
|
14
|
-
timeout: 10_000
|
|
15
|
-
},
|
|
16
|
-
use: {
|
|
17
|
-
baseURL,
|
|
18
|
-
headless: true,
|
|
19
|
-
...(storageState ? { storageState } : {})
|
|
20
|
-
},
|
|
21
|
-
...(managedBaseUrl ? {} : {
|
|
22
|
-
webServer: {
|
|
23
|
-
command: "npm run develop",
|
|
24
|
-
env: {
|
|
25
|
-
PORT: port
|
|
26
|
-
},
|
|
27
|
-
url: `${baseURL}/api/health`,
|
|
28
|
-
reuseExistingServer: true,
|
|
29
|
-
timeout: 180_000
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
});
|
|
4
|
+
// Each Playwright worker gets its own server on the production build; with PLAYWRIGHT_BASE_URL, the managed target.
|
|
5
|
+
export default defineConfig(browserTestConfig({ testDir: "./tests/e2e" }));
|
|
@@ -23,6 +23,10 @@ import { surfaceRuntime } from "./server/lib/surfaceRuntime.js";
|
|
|
23
23
|
/** @typedef {import("@jskit-ai/kernel/server/types").RuntimeEnv} RuntimeEnv */
|
|
24
24
|
|
|
25
25
|
const SPA_INDEX_FILE = "index.html";
|
|
26
|
+
// Vite names each built file under assets/ after its content (`index-BxY3kz9a.js`), so a browser may keep one for good:
|
|
27
|
+
// a new build references new names. index.html and unhashed files are revalidated on every load.
|
|
28
|
+
const HASHED_ASSET = /^assets\/(?:.+\/)?[^/]+-[\w-]{8}\.[a-z0-9]+$/u;
|
|
29
|
+
const HASHED_ASSET_CACHE = Object.freeze({ maxAge: 365 * 24 * 60 * 60 * 1000, immutable: true });
|
|
26
30
|
const STATIC_GLOBAL_UI_PATHS = Object.freeze([
|
|
27
31
|
"/assets",
|
|
28
32
|
"/favicon.svg",
|
|
@@ -157,7 +161,9 @@ async function createServer({ runtimeEnv }) {
|
|
|
157
161
|
if (hasFileExtension(pathname)) {
|
|
158
162
|
const staticFilePath = toStaticFilePath(pathname);
|
|
159
163
|
if (hasWebBuild && staticFilePath && canServeStaticFile(distRoot, staticFilePath)) {
|
|
160
|
-
return
|
|
164
|
+
return HASHED_ASSET.test(staticFilePath)
|
|
165
|
+
? reply.sendFile(staticFilePath, HASHED_ASSET_CACHE)
|
|
166
|
+
: reply.sendFile(staticFilePath);
|
|
161
167
|
}
|
|
162
168
|
return reply.code(404).send({
|
|
163
169
|
message: `Route ${method}:${pathname} not found`,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
// Vuetify's styles first, so its stylesheet comes before any component chunk's.
|
|
2
|
+
import "vuetify/styles";
|
|
3
|
+
import "@jskit-ai/material-web/client/material.css";
|
|
1
4
|
import { createApp } from "vue";
|
|
2
5
|
import { createPinia } from "pinia";
|
|
3
6
|
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
|
|
4
7
|
import { createRouter, createWebHistory } from "vue-router/auto";
|
|
5
8
|
import { routes } from "vue-router/auto-routes";
|
|
6
|
-
import "vuetify/styles";
|
|
7
|
-
import "@jskit-ai/material-web/client/material.css";
|
|
8
9
|
import { createVuetify } from "vuetify";
|
|
9
10
|
import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
|
|
10
11
|
import App from "./App.vue";
|
|
@@ -33,3 +33,16 @@ for (const width of [390, 768, 1280]) {
|
|
|
33
33
|
await expect(page.locator("#startup-shell")).toHaveCount(0);
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
test("built assets are kept for good and the page itself is revalidated", async ({ page }) => {
|
|
38
|
+
test.skip(Boolean(process.env.PLAYWRIGHT_BASE_URL), "A managed target's server and proxy set its own caching.");
|
|
39
|
+
const assets: string[] = [];
|
|
40
|
+
page.on("response", response => {
|
|
41
|
+
if (new URL(response.url()).pathname.startsWith("/assets/")) assets.push(response.headers()["cache-control"] ?? "");
|
|
42
|
+
});
|
|
43
|
+
const document = await page.goto("/home");
|
|
44
|
+
await page.waitForLoadState("load");
|
|
45
|
+
expect(document?.headers()["cache-control"] ?? "").not.toContain("immutable");
|
|
46
|
+
expect(assets.length).toBeGreaterThan(0);
|
|
47
|
+
expect(new Set(assets)).toEqual(new Set(["public, max-age=31536000, immutable"]));
|
|
48
|
+
});
|
|
@@ -4,6 +4,7 @@ import vue from "@vitejs/plugin-vue";
|
|
|
4
4
|
import vuetify from "vite-plugin-vuetify";
|
|
5
5
|
import VueRouter from "vue-router/vite";
|
|
6
6
|
import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
|
|
7
|
+
import { testConcurrency } from "@jskit-ai/jskit-catalog/test-budget";
|
|
7
8
|
|
|
8
9
|
const configuredDevPort = Number.parseInt(String(process.env.VITE_DEV_PORT || "").trim(), 10);
|
|
9
10
|
const devPort = Number.isInteger(configuredDevPort) && configuredDevPort > 0 ? configuredDevPort : 5173;
|
|
@@ -52,7 +53,8 @@ export default defineConfig({
|
|
|
52
53
|
}
|
|
53
54
|
],
|
|
54
55
|
test: {
|
|
55
|
-
include: ["tests/client/**/*.vitest.js"]
|
|
56
|
+
include: ["tests/client/**/*.vitest.js"],
|
|
57
|
+
maxWorkers: testConcurrency()
|
|
56
58
|
},
|
|
57
59
|
optimizeDeps: {
|
|
58
60
|
include: ["vuetify/components/**", "vuetify/directives/**"],
|
|
@@ -118,7 +118,10 @@ Read these together before adapting the shell:
|
|
|
118
118
|
|
|
119
119
|
## Variation points
|
|
120
120
|
|
|
121
|
-
Rename the example
|
|
121
|
+
Rename the example (its name is spelt in `package.json`, `app.json` and
|
|
122
|
+
`index.html`'s `<title>`, and, once the app adds sign-in, in the
|
|
123
|
+
`auth/auth-surface` pattern's `src/components/AppBrand.vue`; rename them
|
|
124
|
+
together), replace the home content, add or remove settings routes,
|
|
122
125
|
and change placements through their public contracts. Use package-owned
|
|
123
126
|
patterns for auth, users, databases, CRUD, realtime, and other capabilities.
|
|
124
127
|
Do not preinstall capabilities solely because they appear in another app.
|
|
@@ -29,11 +29,28 @@ Do not install an unrelated runtime package only to read documentation.
|
|
|
29
29
|
Before adding or changing browser-test setup, read the browser verification
|
|
30
30
|
guide at
|
|
31
31
|
`https://mobily-enterprises.github.io/jskit-ai/guide/framework/ui-operations#browser-verification`.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
`playwright.config.mjs` is `browserTestConfig()` from
|
|
33
|
+
`@jskit-ai/jskit-catalog/browser-tests`: every Playwright worker gets its own
|
|
34
|
+
server on the production build, its own port and, through the config's
|
|
35
|
+
`prepare` module, its own copy of the browser database, restored from a schema
|
|
36
|
+
snapshot and seeded. A local run never starts the development server; with
|
|
37
|
+
`PLAYWRIGHT_BASE_URL`, the tests use that managed target as its host serves
|
|
38
|
+
it. Keep `BROWSER_TEST_DB_NAME` distinct from disposable `TEST_DB_NAME`. The build and
|
|
39
|
+
the snapshot are reused while their sources are unchanged; do not rebuild the
|
|
40
|
+
database or production frontend for each focused browser check. Require
|
|
41
|
+
repeat-start regression proof.
|
|
42
|
+
|
|
43
|
+
Tests run in parallel within the machine's test budget: run node's test
|
|
44
|
+
runner through `jskit test` (`npm test`), with `--databases` once the app has a
|
|
45
|
+
database so each file running at once gets its own copy of `TEST_DB_NAME`,
|
|
46
|
+
Playwright through `browserTestConfig()`, and take Vitest's `maxWorkers` from
|
|
47
|
+
`testConcurrency()` of
|
|
48
|
+
`@jskit-ai/jskit-catalog/test-budget`; never one file at a time or a fixed
|
|
49
|
+
count. Isolate files that collide (their own port, temporary directory and
|
|
50
|
+
database) instead of serialising them; a file that truly must run alone says
|
|
51
|
+
why on its first line, `// jskit-test: alone - <reason>`. Plain `jskit check`
|
|
52
|
+
fails anything else
|
|
53
|
+
(`https://mobily-enterprises.github.io/jskit-ai/guide/framework/typed-features#test-files-run-in-parallel`).
|
|
37
54
|
|
|
38
55
|
Keep verification output small. Run the relevant cases together, save complete
|
|
39
56
|
stdout/stderr to local artifacts, and preserve the exit status. Report counts,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"build:home": "VITE_SURFACE=home vite build",
|
|
25
25
|
"preview": "vite preview",
|
|
26
26
|
"lint": "eslint .",
|
|
27
|
-
"test": "
|
|
27
|
+
"test": "jskit test",
|
|
28
28
|
"test:client": "vitest run tests/client",
|
|
29
29
|
"test:e2e": "playwright test tests/e2e",
|
|
30
30
|
"jskit:update": "npx --yes @jskit-ai/jskit-catalog@latest update",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@local/main": "0.1.0",
|
|
36
36
|
"@fastify/static": "^10.1.3",
|
|
37
|
-
"@jskit-ai/kernel": "0.2.
|
|
38
|
-
"@jskit-ai/material-web": "0.1.
|
|
37
|
+
"@jskit-ai/kernel": "0.2.12",
|
|
38
|
+
"@jskit-ai/material-web": "0.1.3",
|
|
39
39
|
"@tanstack/vue-query": "^5.101.0",
|
|
40
40
|
"fastify": "^5.12.7",
|
|
41
41
|
"pino": "^10.3.1",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"vue": "^3.5.44",
|
|
45
45
|
"vue-router": "^5.3.1",
|
|
46
46
|
"vuetify": "^4.3.0",
|
|
47
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
48
|
-
"@jskit-ai/shell-web": "0.2.
|
|
47
|
+
"@jskit-ai/http-runtime": "0.2.12",
|
|
48
|
+
"@jskit-ai/shell-web": "0.2.15",
|
|
49
49
|
"@fastify/fast-json-stringify-compiler": "^5.1.0",
|
|
50
50
|
"@vue/devtools-api": "^8.2.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@jskit-ai/config-eslint": "0.1.
|
|
54
|
-
"@jskit-ai/jskit-catalog": "0.2.
|
|
53
|
+
"@jskit-ai/config-eslint": "0.1.246",
|
|
54
|
+
"@jskit-ai/jskit-catalog": "0.2.33",
|
|
55
55
|
"@playwright/test": "1.61.1",
|
|
56
56
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
57
57
|
"eslint": "^10.8.0",
|
package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
2
2
|
import MenuLinkItem from "/src/components/menus/MenuLinkItem.vue";
|
|
3
|
+
import SectionTabLinkItem from "/src/components/menus/SectionTabLinkItem.vue";
|
|
3
4
|
import SurfaceAwareMenuLinkItem from "/src/components/menus/SurfaceAwareMenuLinkItem.vue";
|
|
4
5
|
import TabLinkItem from "/src/components/menus/TabLinkItem.vue";
|
|
5
6
|
|
|
@@ -13,6 +14,8 @@ const MainClientProvider = defineProvider({
|
|
|
13
14
|
components.register("local.main.ui.menu-link-item", MenuLinkItem);
|
|
14
15
|
components.register("local.main.ui.surface-aware-menu-link-item", SurfaceAwareMenuLinkItem);
|
|
15
16
|
components.register("local.main.ui.tab-link-item", TabLinkItem);
|
|
17
|
+
// A page's section tabs (page.section-nav): Material 3 primary tabs, not navigation bar destinations.
|
|
18
|
+
components.register("local.main.ui.section-tab-link-item", SectionTabLinkItem);
|
|
16
19
|
}
|
|
17
20
|
});
|
|
18
21
|
|
|
@@ -1,32 +1,5 @@
|
|
|
1
1
|
import { defineConfig } from "@playwright/test";
|
|
2
|
+
import { browserTestConfig } from "@jskit-ai/jskit-catalog/browser-tests";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
.replace(/\/+$/u, "");
|
|
6
|
-
const port = String(process.env.PLAYWRIGHT_PORT || "").trim() || "4173";
|
|
7
|
-
const baseURL = managedBaseUrl || `http://127.0.0.1:${port}`;
|
|
8
|
-
const storageState = String(process.env.PLAYWRIGHT_STORAGE_STATE || "").trim();
|
|
9
|
-
|
|
10
|
-
export default defineConfig({
|
|
11
|
-
testDir: "./tests/e2e",
|
|
12
|
-
timeout: 60_000,
|
|
13
|
-
expect: {
|
|
14
|
-
timeout: 10_000
|
|
15
|
-
},
|
|
16
|
-
use: {
|
|
17
|
-
baseURL,
|
|
18
|
-
headless: true,
|
|
19
|
-
...(storageState ? { storageState } : {})
|
|
20
|
-
},
|
|
21
|
-
...(managedBaseUrl ? {} : {
|
|
22
|
-
webServer: {
|
|
23
|
-
command: "npm run develop",
|
|
24
|
-
env: {
|
|
25
|
-
PORT: port
|
|
26
|
-
},
|
|
27
|
-
url: `${baseURL}/api/health`,
|
|
28
|
-
reuseExistingServer: true,
|
|
29
|
-
timeout: 180_000
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
});
|
|
4
|
+
// Each Playwright worker gets its own server on the production build; with PLAYWRIGHT_BASE_URL, the managed target.
|
|
5
|
+
export default defineConfig(browserTestConfig({ testDir: "./tests/e2e" }));
|
|
@@ -23,6 +23,10 @@ import { surfaceRuntime } from "./server/lib/surfaceRuntime.js";
|
|
|
23
23
|
/** @typedef {import("@jskit-ai/kernel/server/types").RuntimeEnv} RuntimeEnv */
|
|
24
24
|
|
|
25
25
|
const SPA_INDEX_FILE = "index.html";
|
|
26
|
+
// Vite names each built file under assets/ after its content (`index-BxY3kz9a.js`), so a browser may keep one for good:
|
|
27
|
+
// a new build references new names. index.html and unhashed files are revalidated on every load.
|
|
28
|
+
const HASHED_ASSET = /^assets\/(?:.+\/)?[^/]+-[\w-]{8}\.[a-z0-9]+$/u;
|
|
29
|
+
const HASHED_ASSET_CACHE = Object.freeze({ maxAge: 365 * 24 * 60 * 60 * 1000, immutable: true });
|
|
26
30
|
const STATIC_GLOBAL_UI_PATHS = Object.freeze([
|
|
27
31
|
"/assets",
|
|
28
32
|
"/favicon.svg",
|
|
@@ -157,7 +161,9 @@ async function createServer({ runtimeEnv }) {
|
|
|
157
161
|
if (hasFileExtension(pathname)) {
|
|
158
162
|
const staticFilePath = toStaticFilePath(pathname);
|
|
159
163
|
if (hasWebBuild && staticFilePath && canServeStaticFile(distRoot, staticFilePath)) {
|
|
160
|
-
return
|
|
164
|
+
return HASHED_ASSET.test(staticFilePath)
|
|
165
|
+
? reply.sendFile(staticFilePath, HASHED_ASSET_CACHE)
|
|
166
|
+
: reply.sendFile(staticFilePath);
|
|
161
167
|
}
|
|
162
168
|
return reply.code(404).send({
|
|
163
169
|
message: `Route ${method}:${pathname} not found`,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import ShellSectionTabLinkItem from "@jskit-ai/shell-web/client/components/ShellSectionTabLinkItem";
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
label: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: ""
|
|
8
|
+
},
|
|
9
|
+
to: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: ""
|
|
12
|
+
},
|
|
13
|
+
icon: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: ""
|
|
16
|
+
},
|
|
17
|
+
surface: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: ""
|
|
20
|
+
},
|
|
21
|
+
scopedSuffix: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: "/"
|
|
24
|
+
},
|
|
25
|
+
unscopedSuffix: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: "/"
|
|
28
|
+
},
|
|
29
|
+
disabled: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
exact: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<ShellSectionTabLinkItem v-bind="props" />
|
|
42
|
+
</template>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
// Vuetify's styles first, so its stylesheet comes before any component chunk's.
|
|
2
|
+
import "vuetify/styles";
|
|
3
|
+
import "@jskit-ai/material-web/client/material.css";
|
|
1
4
|
import { createApp } from "vue";
|
|
2
5
|
import { createPinia } from "pinia";
|
|
3
6
|
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
|
|
4
7
|
import { createRouter, createWebHistory } from "vue-router/auto";
|
|
5
8
|
import { routes } from "vue-router/auto-routes";
|
|
6
|
-
import "vuetify/styles";
|
|
7
|
-
import "@jskit-ai/material-web/client/material.css";
|
|
8
9
|
import { createVuetify } from "vuetify";
|
|
9
10
|
import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
|
|
10
11
|
import App from "./App.vue";
|
|
@@ -6,7 +6,7 @@ const message = "The page you requested does not exist.";
|
|
|
6
6
|
|
|
7
7
|
<template>
|
|
8
8
|
<!-- The shell's top app bar carries the title, and its navigation leads back into the app. -->
|
|
9
|
-
<ShellLayout title="Not Found"
|
|
9
|
+
<ShellLayout title="Not Found">
|
|
10
10
|
<section class="d-flex flex-column align-center text-center on-surface-variant py-16">
|
|
11
11
|
<v-icon icon="$searchOff" size="48" />
|
|
12
12
|
<p class="text-body-large mt-4 mb-0">{{ message }}</p>
|
|
@@ -33,3 +33,16 @@ for (const width of [390, 768, 1280]) {
|
|
|
33
33
|
await expect(page.locator("#startup-shell")).toHaveCount(0);
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
test("built assets are kept for good and the page itself is revalidated", async ({ page }) => {
|
|
38
|
+
test.skip(Boolean(process.env.PLAYWRIGHT_BASE_URL), "A managed target's server and proxy set its own caching.");
|
|
39
|
+
const assets: string[] = [];
|
|
40
|
+
page.on("response", response => {
|
|
41
|
+
if (new URL(response.url()).pathname.startsWith("/assets/")) assets.push(response.headers()["cache-control"] ?? "");
|
|
42
|
+
});
|
|
43
|
+
const document = await page.goto("/home");
|
|
44
|
+
await page.waitForLoadState("load");
|
|
45
|
+
expect(document?.headers()["cache-control"] ?? "").not.toContain("immutable");
|
|
46
|
+
expect(assets.length).toBeGreaterThan(0);
|
|
47
|
+
expect(new Set(assets)).toEqual(new Set(["public, max-age=31536000, immutable"]));
|
|
48
|
+
});
|
|
@@ -4,6 +4,7 @@ import vue from "@vitejs/plugin-vue";
|
|
|
4
4
|
import vuetify from "vite-plugin-vuetify";
|
|
5
5
|
import VueRouter from "vue-router/vite";
|
|
6
6
|
import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
|
|
7
|
+
import { testConcurrency } from "@jskit-ai/jskit-catalog/test-budget";
|
|
7
8
|
|
|
8
9
|
const configuredDevPort = Number.parseInt(String(process.env.VITE_DEV_PORT || "").trim(), 10);
|
|
9
10
|
const devPort = Number.isInteger(configuredDevPort) && configuredDevPort > 0 ? configuredDevPort : 5173;
|
|
@@ -52,7 +53,8 @@ export default defineConfig({
|
|
|
52
53
|
}
|
|
53
54
|
],
|
|
54
55
|
test: {
|
|
55
|
-
include: ["tests/client/**/*.vitest.js"]
|
|
56
|
+
include: ["tests/client/**/*.vitest.js"],
|
|
57
|
+
maxWorkers: testConcurrency()
|
|
56
58
|
},
|
|
57
59
|
optimizeDeps: {
|
|
58
60
|
include: ["vuetify/components/**", "vuetify/directives/**"],
|