@jskit-ai/agent-docs 0.1.131 → 0.1.133

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.
Files changed (171) hide show
  1. package/guide/agent/app-extras/assistant.md +29 -605
  2. package/guide/agent/app-extras/mobile-capacitor.md +29 -362
  3. package/guide/agent/app-extras/realtime.md +29 -277
  4. package/guide/agent/app-setup/a-more-interesting-shell.md +44 -815
  5. package/guide/agent/app-setup/authentication.md +43 -1073
  6. package/guide/agent/app-setup/console.md +26 -298
  7. package/guide/agent/app-setup/database-layer.md +110 -790
  8. package/guide/agent/app-setup/existing-application-migration.md +273 -0
  9. package/guide/agent/app-setup/initial-scaffolding.md +50 -784
  10. package/guide/agent/app-setup/multi-homing.md +39 -712
  11. package/guide/agent/app-setup/quickstart.md +43 -179
  12. package/guide/agent/app-setup/users.md +34 -353
  13. package/guide/agent/index.md +17 -23
  14. package/package.json +2 -2
  15. package/patterns/INDEX.md +7 -7
  16. package/patterns/child-cruds.md +3 -3
  17. package/patterns/client-requests.md +6 -6
  18. package/patterns/crud-authoring.md +94 -0
  19. package/patterns/crud-links.md +1 -1
  20. package/patterns/feature-package/PATTERN.md +108 -0
  21. package/patterns/feature-package/example/booking-engine/package.json +48 -0
  22. package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
  23. package/patterns/feature-package/example/booking-engine/src/server/actions.js +26 -0
  24. package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
  25. package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
  26. package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
  27. package/patterns/feature-package/example/variations/customKnexRepository.js +34 -0
  28. package/patterns/feature-package/example/variations/orchestratorService.js +23 -0
  29. package/patterns/filters.md +8 -8
  30. package/patterns/live-actions.md +5 -18
  31. package/patterns/minimal-foundation/PATTERN.md +98 -0
  32. package/patterns/minimal-foundation/example/.nvmrc +1 -0
  33. package/patterns/minimal-foundation/example/AGENTS.md +17 -0
  34. package/patterns/minimal-foundation/example/Procfile +2 -0
  35. package/patterns/minimal-foundation/example/app.json +14 -0
  36. package/patterns/minimal-foundation/example/bin/develop.js +71 -0
  37. package/patterns/minimal-foundation/example/bin/server.js +8 -0
  38. package/patterns/minimal-foundation/example/config/public.js +40 -0
  39. package/patterns/minimal-foundation/example/config/server.js +1 -0
  40. package/patterns/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
  41. package/patterns/minimal-foundation/example/eslint.config.mjs +19 -0
  42. package/patterns/minimal-foundation/example/favicon.svg +7 -0
  43. package/patterns/minimal-foundation/example/gitignore +9 -0
  44. package/patterns/minimal-foundation/example/index.html +13 -0
  45. package/patterns/minimal-foundation/example/jsconfig.json +8 -0
  46. package/patterns/minimal-foundation/example/package.json +57 -0
  47. package/patterns/minimal-foundation/example/packages/main/package.json +42 -0
  48. package/patterns/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
  49. package/patterns/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
  50. package/patterns/minimal-foundation/example/playwright.config.mjs +31 -0
  51. package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
  52. package/patterns/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
  53. package/patterns/minimal-foundation/example/server.js +195 -0
  54. package/patterns/minimal-foundation/example/src/App.vue +13 -0
  55. package/patterns/minimal-foundation/example/src/main.js +85 -0
  56. package/patterns/minimal-foundation/example/src/pages/home/index.vue +48 -0
  57. package/patterns/minimal-foundation/example/src/pages/home.vue +13 -0
  58. package/patterns/minimal-foundation/example/src/views/NotFound.vue +13 -0
  59. package/patterns/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
  60. package/patterns/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
  61. package/patterns/minimal-foundation/example/tests/server/smoke.test.js +16 -0
  62. package/patterns/minimal-foundation/example/vite.config.mjs +81 -0
  63. package/patterns/page-scaffolding.md +20 -17
  64. package/patterns/placements.md +17 -15
  65. package/patterns/row-policies.md +4 -5
  66. package/patterns/server-search.md +3 -3
  67. package/patterns/shell-foundation/PATTERN.md +104 -0
  68. package/patterns/shell-foundation/example/.nvmrc +1 -0
  69. package/patterns/shell-foundation/example/AGENTS.md +17 -0
  70. package/patterns/shell-foundation/example/Procfile +2 -0
  71. package/patterns/shell-foundation/example/app.json +14 -0
  72. package/patterns/shell-foundation/example/bin/develop.js +71 -0
  73. package/patterns/shell-foundation/example/bin/server.js +8 -0
  74. package/patterns/shell-foundation/example/config/public.js +40 -0
  75. package/patterns/shell-foundation/example/config/server.js +1 -0
  76. package/patterns/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
  77. package/patterns/shell-foundation/example/eslint.config.mjs +19 -0
  78. package/patterns/shell-foundation/example/favicon.svg +7 -0
  79. package/patterns/shell-foundation/example/gitignore +9 -0
  80. package/patterns/shell-foundation/example/index.html +13 -0
  81. package/patterns/shell-foundation/example/jsconfig.json +8 -0
  82. package/patterns/shell-foundation/example/package.json +59 -0
  83. package/patterns/shell-foundation/example/packages/main/package.json +56 -0
  84. package/patterns/shell-foundation/example/packages/main/src/client/index.js +9 -0
  85. package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
  86. package/patterns/shell-foundation/example/packages/main/src/shared/index.js +10 -0
  87. package/patterns/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
  88. package/patterns/shell-foundation/example/playwright.config.mjs +31 -0
  89. package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
  90. package/patterns/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
  91. package/patterns/shell-foundation/example/server.js +195 -0
  92. package/patterns/shell-foundation/example/src/App.vue +11 -0
  93. package/patterns/shell-foundation/example/src/components/ShellLayout.vue +12 -0
  94. package/patterns/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
  95. package/patterns/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
  96. package/patterns/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
  97. package/patterns/shell-foundation/example/src/error.js +19 -0
  98. package/patterns/shell-foundation/example/src/main.js +85 -0
  99. package/patterns/shell-foundation/example/src/pages/home/index.vue +116 -0
  100. package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +40 -0
  101. package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
  102. package/patterns/shell-foundation/example/src/pages/home/settings.vue +109 -0
  103. package/patterns/shell-foundation/example/src/pages/home.vue +20 -0
  104. package/patterns/shell-foundation/example/src/placement.js +56 -0
  105. package/patterns/shell-foundation/example/src/placementTopology.js +149 -0
  106. package/patterns/shell-foundation/example/src/views/NotFound.vue +13 -0
  107. package/patterns/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
  108. package/patterns/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
  109. package/patterns/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
  110. package/patterns/shell-foundation/example/tests/server/smoke.test.js +16 -0
  111. package/patterns/shell-foundation/example/vite.config.mjs +81 -0
  112. package/patterns/ui-contract.md +56 -0
  113. package/patterns/ui-testing.md +10 -12
  114. package/reference/autogen/KERNEL_MAP.md +29 -107
  115. package/reference/autogen/PATTERN_INDEX.md +230 -0
  116. package/reference/autogen/README.md +4 -8
  117. package/reference/autogen/packages/agent-docs.md +259 -0
  118. package/reference/autogen/packages/assistant-core.md +3 -3
  119. package/reference/autogen/packages/assistant-runtime.md +32 -17
  120. package/reference/autogen/packages/auth-core.md +31 -33
  121. package/reference/autogen/packages/auth-provider-local-core.md +4 -12
  122. package/reference/autogen/packages/auth-provider-local-db-core.md +4 -4
  123. package/reference/autogen/packages/auth-provider-supabase-core.md +14 -18
  124. package/reference/autogen/packages/auth-web.md +42 -22
  125. package/reference/autogen/packages/console-core.md +8 -25
  126. package/reference/autogen/packages/console-web.md +5 -5
  127. package/reference/autogen/packages/crud-core.md +61 -17
  128. package/reference/autogen/packages/database-runtime-mysql.md +12 -2
  129. package/reference/autogen/packages/database-runtime-postgres.md +12 -2
  130. package/reference/autogen/packages/database-runtime.md +26 -25
  131. package/reference/autogen/packages/google-rewarded-core.md +19 -104
  132. package/reference/autogen/packages/http-runtime.md +4 -8
  133. package/reference/autogen/packages/http-web.md +32 -0
  134. package/reference/autogen/packages/json-rest-api-core.md +4 -6
  135. package/reference/autogen/packages/kernel.md +109 -390
  136. package/reference/autogen/packages/mobile-capacitor.md +2 -13
  137. package/reference/autogen/packages/realtime.md +29 -26
  138. package/reference/autogen/packages/resource-crud-core.md +6 -0
  139. package/reference/autogen/packages/shell-web.md +69 -54
  140. package/reference/autogen/packages/storage-runtime.md +3 -3
  141. package/reference/autogen/packages/uploads-image-web.md +0 -1
  142. package/reference/autogen/packages/uploads-runtime.md +3 -3
  143. package/reference/autogen/packages/users-core.md +45 -90
  144. package/reference/autogen/packages/users-web.md +5 -7
  145. package/reference/autogen/packages/workspaces-core.md +53 -74
  146. package/reference/autogen/packages/workspaces-web.md +15 -16
  147. package/reference/autogen/tooling/jskit-catalog.md +34 -0
  148. package/reference/autogen/tooling/testUtils.md +4 -4
  149. package/skills/jskit/SKILL.md +38 -31
  150. package/skills/jskit/references/app-operations.md +68 -53
  151. package/skills/jskit/references/crud-operations.md +58 -106
  152. package/skills/jskit/references/material-3.md +1 -1
  153. package/skills/jskit/references/ui-operations.md +41 -44
  154. package/templates/app/AGENTS.md +7 -3
  155. package/guide/agent/app-setup/upgrade-beta-1-to-final.md +0 -252
  156. package/guide/agent/app-setup/working-with-the-jskit-cli.md +0 -325
  157. package/guide/agent/generators/advanced-cruds.md +0 -1935
  158. package/guide/agent/generators/crud-generators.md +0 -948
  159. package/guide/agent/generators/intro.md +0 -65
  160. package/guide/agent/generators/row-policies.md +0 -537
  161. package/guide/agent/generators/ui-generators.md +0 -690
  162. package/patterns/crud-scaffolding.md +0 -198
  163. package/patterns/generated-ui-contract-tracking.md +0 -66
  164. package/reference/autogen/packages/assistant.md +0 -68
  165. package/reference/autogen/packages/crud-server-generator.md +0 -215
  166. package/reference/autogen/packages/crud-ui-generator.md +0 -192
  167. package/reference/autogen/packages/feature-server-generator.md +0 -65
  168. package/reference/autogen/packages/ui-generator.md +0 -127
  169. package/reference/autogen/tooling/create-app.md +0 -317
  170. package/reference/autogen/tooling/jskit-cli.md +0 -933
  171. package/reference/autogen/tooling/test-support.md +0 -27
@@ -0,0 +1,195 @@
1
+ import Fastify from "fastify";
2
+ import fastifyStatic from "@fastify/static";
3
+ import { resolveRuntimeEnv } from "./server/lib/runtimeEnv.js";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import {
7
+ createInstalledRuntime,
8
+ registerSurfaceRequestConstraint,
9
+ resolveRuntimeProfileFromSurface
10
+ } from "@jskit-ai/kernel/server/platform";
11
+ import { loadAppConfigFromAppRoot } from "@jskit-ai/kernel/server/support";
12
+ import { matchesPathPrefix, normalizePathname } from "@jskit-ai/kernel/shared/surface/paths";
13
+ import { surfaceRuntime } from "./server/lib/surfaceRuntime.js";
14
+
15
+ const SPA_INDEX_FILE = "index.html";
16
+ const API_BASE_PATH = "/api";
17
+ const STATIC_GLOBAL_UI_PATHS = Object.freeze([
18
+ "/assets",
19
+ "/favicon.svg",
20
+ "/favicon.ico",
21
+ "/robots.txt",
22
+ "/manifest.webmanifest"
23
+ ]);
24
+
25
+ function toRequestPathname(urlValue) {
26
+ const rawUrl = String(urlValue || "").trim() || "/";
27
+ try {
28
+ return normalizePathname(new URL(rawUrl, "http://localhost").pathname || "/");
29
+ } catch {
30
+ return normalizePathname(rawUrl.split("?")[0] || "/");
31
+ }
32
+ }
33
+
34
+ function isApiPath(pathname) {
35
+ return matchesPathPrefix(pathname, API_BASE_PATH);
36
+ }
37
+
38
+ function hasFileExtension(pathname) {
39
+ return path.extname(normalizePathname(pathname)) !== "";
40
+ }
41
+
42
+ function resolveGlobalUiPaths(runtimeGlobalUiPaths = []) {
43
+ const paths = new Set(Array.isArray(runtimeGlobalUiPaths) ? runtimeGlobalUiPaths : []);
44
+ for (const staticPath of STATIC_GLOBAL_UI_PATHS) {
45
+ paths.add(staticPath);
46
+ }
47
+ return [...paths];
48
+ }
49
+
50
+ function resolveStaticFilePath(pathname) {
51
+ const normalizedPathname = normalizePathname(pathname);
52
+
53
+ const relativePath = normalizedPathname.replace(/^\/+/, "");
54
+ if (!relativePath || relativePath.endsWith("/")) {
55
+ return "";
56
+ }
57
+
58
+ const normalizedRelativePath = path.posix.normalize(relativePath);
59
+ if (
60
+ !normalizedRelativePath ||
61
+ normalizedRelativePath === "." ||
62
+ normalizedRelativePath === ".." ||
63
+ normalizedRelativePath.startsWith("../") ||
64
+ normalizedRelativePath.includes("/../")
65
+ ) {
66
+ return "";
67
+ }
68
+
69
+ return normalizedRelativePath;
70
+ }
71
+
72
+ function canServeStaticFile(distRoot, relativePath) {
73
+ if (!distRoot || !relativePath) {
74
+ return false;
75
+ }
76
+
77
+ const normalizedDistRoot = path.resolve(distRoot);
78
+ const resolvedPath = path.resolve(normalizedDistRoot, relativePath);
79
+ if (!(resolvedPath === normalizedDistRoot || resolvedPath.startsWith(`${normalizedDistRoot}${path.sep}`))) {
80
+ return false;
81
+ }
82
+
83
+ return existsSync(resolvedPath);
84
+ }
85
+
86
+ async function createServer({ runtimeEnv = resolveRuntimeEnv() } = {}) {
87
+ const app = Fastify({
88
+ logger: true,
89
+ ajv: {
90
+ customOptions: {
91
+ allowUnionTypes: true
92
+ }
93
+ }
94
+ });
95
+
96
+ app.get("/api/health", async () => {
97
+ return {
98
+ ok: true,
99
+ app: "reading-room"
100
+ };
101
+ });
102
+ const appRoot = path.resolve(process.cwd());
103
+ const distRoot = path.resolve(appRoot, "dist");
104
+ const hasWebBuild = existsSync(path.resolve(distRoot, SPA_INDEX_FILE));
105
+ const spaDocument = hasWebBuild ? readFileSync(path.resolve(distRoot, SPA_INDEX_FILE), "utf8") : "";
106
+ const runtime = await createInstalledRuntime({
107
+ appRoot,
108
+ profile: resolveRuntimeProfileFromSurface({
109
+ surfaceRuntime,
110
+ serverSurface: runtimeEnv.SERVER_SURFACE
111
+ }),
112
+ config: await loadAppConfigFromAppRoot({ appRoot }),
113
+ env: runtimeEnv,
114
+ logger: app.log,
115
+ fastify: app
116
+ });
117
+
118
+ registerSurfaceRequestConstraint({
119
+ fastify: app,
120
+ surfaceRuntime,
121
+ serverSurface: runtimeEnv.SERVER_SURFACE,
122
+ globalUiPaths: resolveGlobalUiPaths(runtime?.globalUiPaths || [])
123
+ });
124
+
125
+ if (hasWebBuild) {
126
+ await app.register(fastifyStatic, {
127
+ root: distRoot,
128
+ index: false,
129
+ serve: false
130
+ });
131
+ } else {
132
+ app.log.warn("Frontend build not found (dist/index.html). Page routes will return 404 until `npm run build`.");
133
+ }
134
+
135
+ app.setNotFoundHandler(async (request, reply) => {
136
+ const pathname = toRequestPathname(request?.url);
137
+ const method = String(request?.method || "GET")
138
+ .trim()
139
+ .toUpperCase();
140
+ if (isApiPath(pathname) || (method !== "GET" && method !== "HEAD")) {
141
+ return reply.code(404).send({
142
+ message: `Route ${method}:${pathname} not found`,
143
+ error: "Not Found",
144
+ statusCode: 404
145
+ });
146
+ }
147
+ if (hasFileExtension(pathname)) {
148
+ const staticFilePath = resolveStaticFilePath(pathname);
149
+ if (hasWebBuild && staticFilePath && canServeStaticFile(distRoot, staticFilePath)) {
150
+ return reply.sendFile(staticFilePath);
151
+ }
152
+ return reply.code(404).send({
153
+ message: `Route ${method}:${pathname} not found`,
154
+ error: "Not Found",
155
+ statusCode: 404
156
+ });
157
+ }
158
+ if (!hasWebBuild) {
159
+ return reply.code(404).send({
160
+ error: "Frontend build is not available. Run `npm run build`."
161
+ });
162
+ }
163
+ return reply.type("text/html; charset=utf-8").send(spaDocument);
164
+ });
165
+
166
+ app.log.info(
167
+ {
168
+ surface: surfaceRuntime.normalizeSurfaceMode(runtimeEnv.SERVER_SURFACE),
169
+ providerPackages: runtime.providerPackageOrder,
170
+ packageOrder: runtime.packageOrder,
171
+ capabilities: runtime.diagnostics.capabilityIds
172
+ },
173
+ "Started JSKIT installed runtime."
174
+ );
175
+
176
+ return app;
177
+ }
178
+
179
+ async function startServer(options = {}) {
180
+ const runtimeEnv = resolveRuntimeEnv();
181
+ const configuredPort = options?.port === undefined ? runtimeEnv.PORT : Number(options.port);
182
+ const port = Number.isInteger(configuredPort) && configuredPort >= 0 ? configuredPort : runtimeEnv.PORT;
183
+ const host = String(options?.host || "").trim() || runtimeEnv.HOST;
184
+ const app = await createServer({
185
+ runtimeEnv: {
186
+ ...runtimeEnv,
187
+ HOST: host,
188
+ PORT: port
189
+ }
190
+ });
191
+ await app.listen({ port, host });
192
+ return app;
193
+ }
194
+
195
+ export { createServer, startServer };
@@ -0,0 +1,11 @@
1
+ <script setup>
2
+ import { RouterView } from "vue-router";
3
+ import ShellErrorHost from "@jskit-ai/shell-web/client/components/ShellErrorHost";
4
+ </script>
5
+
6
+ <template>
7
+ <v-app>
8
+ <RouterView />
9
+ <ShellErrorHost />
10
+ </v-app>
11
+ </template>
@@ -0,0 +1,12 @@
1
+ <script>
2
+ import { h } from "vue";
3
+ import PackageShellLayout from "@jskit-ai/shell-web/client/components/ShellLayout";
4
+
5
+ export default {
6
+ name: "ShellLayout",
7
+ inheritAttrs: false,
8
+ setup(_, { attrs, slots }) {
9
+ return () => h(PackageShellLayout, attrs, slots);
10
+ }
11
+ };
12
+ </script>
@@ -0,0 +1,30 @@
1
+ <script setup>
2
+ import ShellMenuLinkItem from "@jskit-ai/shell-web/client/components/ShellMenuLinkItem";
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
+ disabled: {
18
+ type: Boolean,
19
+ default: false
20
+ },
21
+ exact: {
22
+ type: Boolean,
23
+ default: false
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <template>
29
+ <ShellMenuLinkItem v-bind="props" />
30
+ </template>
@@ -0,0 +1,42 @@
1
+ <script setup>
2
+ import ShellSurfaceAwareMenuLinkItem from "@jskit-ai/shell-web/client/components/ShellSurfaceAwareMenuLinkItem";
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
+ <ShellSurfaceAwareMenuLinkItem v-bind="props" />
42
+ </template>
@@ -0,0 +1,42 @@
1
+ <script setup>
2
+ import ShellTabLinkItem from "@jskit-ai/shell-web/client/components/ShellTabLinkItem";
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
+ <ShellTabLinkItem v-bind="props" />
42
+ </template>
@@ -0,0 +1,19 @@
1
+ import { createDefaultErrorPolicy } from "@jskit-ai/shell-web/client/error";
2
+
3
+ /**
4
+ * App-owned error handling contract.
5
+ * - intent chooses default presentation: resource-load, action-feedback, app-recoverable, blocking.
6
+ * - policy(event, ctx): decide channel + presenter + message.
7
+ * - defaultPresenterId: used when policy does not set presenterId.
8
+ * - presenters: optional custom presenters registered at boot.
9
+ */
10
+ export default Object.freeze({
11
+ defaultPresenterId: "material.snackbar",
12
+ policy: createDefaultErrorPolicy({
13
+ resourceLoadChannel: "silent",
14
+ actionFeedbackChannel: "snackbar",
15
+ appRecoverableChannel: "banner",
16
+ blockingChannel: "dialog"
17
+ }),
18
+ presenters: []
19
+ });
@@ -0,0 +1,85 @@
1
+ import { createApp } from "vue";
2
+ import { createPinia } from "pinia";
3
+ import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
4
+ import { createRouter, createWebHistory } from "vue-router/auto";
5
+ import { routes } from "vue-router/auto-routes";
6
+ import "vuetify/styles";
7
+ import { createVuetify } from "vuetify";
8
+ import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
9
+ import App from "./App.vue";
10
+ import NotFoundView from "./views/NotFound.vue";
11
+ import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
12
+ import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
13
+ import {
14
+ shouldRetryTransientQueryFailure,
15
+ transientQueryRetryDelay
16
+ } from "@jskit-ai/kernel/shared/support";
17
+ import {
18
+ bootstrapClientShellApp,
19
+ createShellRouter
20
+ } from "@jskit-ai/kernel/client";
21
+ import { config } from "../config/public.js";
22
+
23
+ const surfaceRuntime = createSurfaceRuntime({
24
+ allMode: config.surfaceModeAll,
25
+ surfaces: config.surfaceDefinitions,
26
+ defaultSurfaceId: config.surfaceDefaultId
27
+ });
28
+
29
+ const surfaceMode = surfaceRuntime.normalizeSurfaceMode(import.meta.env.VITE_SURFACE);
30
+ const { router, fallbackRoute } = createShellRouter({
31
+ createRouter,
32
+ history: createWebHistory(),
33
+ routes,
34
+ surfaceRuntime,
35
+ surfaceMode,
36
+ notFoundComponent: NotFoundView,
37
+ guard: {
38
+ surfaceDefinitions: config.surfaceDefinitions,
39
+ defaultSurfaceId: config.surfaceDefaultId,
40
+ webRootAllowed: config.webRootAllowed
41
+ }
42
+ });
43
+
44
+ const vuetify = createVuetify({
45
+ theme: {
46
+ defaultTheme: "light"
47
+ },
48
+ icons: {
49
+ defaultSet: "mdi",
50
+ aliases: mdiAliases,
51
+ sets: { mdi }
52
+ }
53
+ });
54
+ const pinia = createPinia();
55
+ const queryClient = new QueryClient({
56
+ defaultOptions: {
57
+ queries: {
58
+ refetchOnWindowFocus: false,
59
+ refetchOnReconnect: true,
60
+ retry: shouldRetryTransientQueryFailure,
61
+ retryDelay: transientQueryRetryDelay
62
+ }
63
+ }
64
+ });
65
+
66
+ void bootstrapClientShellApp({
67
+ createApp,
68
+ rootComponent: App,
69
+ appConfig: config,
70
+ appPlugins: [
71
+ pinia,
72
+ [VueQueryPlugin, { queryClient }],
73
+ vuetify
74
+ ],
75
+ pinia,
76
+ queryClient,
77
+ router,
78
+ bootClientModules: bootInstalledClientModules,
79
+ surfaceRuntime,
80
+ surfaceMode,
81
+ env: import.meta.env,
82
+ fallbackRoute
83
+ }).catch((error) => {
84
+ console.error("Failed to bootstrap client app.", error);
85
+ });
@@ -0,0 +1,116 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { useQuery } from "@tanstack/vue-query";
4
+
5
+ const healthQuery = useQuery({
6
+ queryKey: ["shell-web", "health"],
7
+ queryFn: async () => {
8
+ const response = await fetch("/api/health");
9
+ if (!response.ok) {
10
+ throw new Error("Health request failed.");
11
+ }
12
+ return response.json();
13
+ },
14
+ refetchOnWindowFocus: false
15
+ });
16
+
17
+ const health = computed(() => {
18
+ if (healthQuery.error.value) {
19
+ return "unreachable";
20
+ }
21
+
22
+ return healthQuery.data.value?.ok ? "ok" : "unhealthy";
23
+ });
24
+
25
+ const healthPending = computed(
26
+ () => healthQuery.isPending.value || healthQuery.isFetching.value
27
+ );
28
+ </script>
29
+
30
+ <template>
31
+ <section class="home-surface-screen d-flex flex-column ga-4">
32
+ <header class="home-surface-screen__header">
33
+ <div>
34
+ <p class="text-overline text-medium-emphasis mb-1">Home</p>
35
+ <h1 class="home-surface-screen__title">Ready</h1>
36
+ <p class="text-body-2 text-medium-emphasis mb-0">
37
+ Core services are available.
38
+ </p>
39
+ </div>
40
+ <v-btn color="primary" variant="flat" to="/home/settings/general">Settings</v-btn>
41
+ </header>
42
+
43
+ <v-sheet rounded="lg" border class="home-surface-screen__panel">
44
+ <div class="home-surface-screen__status">
45
+ <span class="text-caption text-medium-emphasis">Service health</span>
46
+ <v-skeleton-loader
47
+ v-if="healthPending"
48
+ aria-label="Loading service health"
49
+ class="home-surface-screen__health-skeleton"
50
+ type="text"
51
+ />
52
+ <strong v-else>{{ health }}</strong>
53
+ </div>
54
+ <v-divider vertical class="d-none d-sm-block" />
55
+ <div class="home-surface-screen__status">
56
+ <span class="text-caption text-medium-emphasis">Route</span>
57
+ <strong>/home</strong>
58
+ </div>
59
+ </v-sheet>
60
+ </section>
61
+ </template>
62
+
63
+ <style scoped>
64
+ .home-surface-screen {
65
+ --home-surface-title-size: 2rem;
66
+ --home-surface-panel-padding: 1rem;
67
+ }
68
+
69
+ .home-surface-screen__header {
70
+ align-items: flex-start;
71
+ display: flex;
72
+ gap: 1rem;
73
+ justify-content: space-between;
74
+ }
75
+
76
+ .home-surface-screen__title {
77
+ font-size: var(--home-surface-title-size);
78
+ font-weight: 700;
79
+ letter-spacing: 0;
80
+ line-height: 1.1;
81
+ margin: 0 0 0.4rem;
82
+ }
83
+
84
+ .home-surface-screen__panel {
85
+ align-items: stretch;
86
+ display: flex;
87
+ flex-wrap: wrap;
88
+ gap: 1rem;
89
+ padding: var(--home-surface-panel-padding);
90
+ }
91
+
92
+ .home-surface-screen__status {
93
+ display: grid;
94
+ gap: 0.15rem;
95
+ min-width: 9rem;
96
+ }
97
+
98
+ .home-surface-screen__health-skeleton {
99
+ max-width: 5rem;
100
+ }
101
+
102
+ @media (max-width: 640px) {
103
+ .home-surface-screen {
104
+ --home-surface-title-size: 1.5rem;
105
+ }
106
+
107
+ .home-surface-screen__header {
108
+ flex-direction: column;
109
+ }
110
+
111
+ .home-surface-screen__header :deep(.v-btn) {
112
+ min-height: 48px;
113
+ width: 100%;
114
+ }
115
+ }
116
+ </style>
@@ -0,0 +1,40 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { useShellLayoutState } from "@jskit-ai/shell-web/client/composables/useShellLayoutState";
4
+
5
+ const { drawerDefaultOpen, setDrawerDefaultOpen } = useShellLayoutState();
6
+
7
+ const drawerDefaultOpenModel = computed({
8
+ get() {
9
+ return drawerDefaultOpen.value;
10
+ },
11
+ set(value) {
12
+ setDrawerDefaultOpen(Boolean(value));
13
+ }
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <section class="settings-general-screen d-flex flex-column ga-4">
19
+ <div>
20
+ <h2 class="text-h6 mb-2">Navigation</h2>
21
+ <p class="text-body-2 text-medium-emphasis mb-0">
22
+ On wider screens, collapsed navigation remains available as a rail. Phone layouts close the drawer and keep primary navigation in the bottom bar.
23
+ </p>
24
+ </div>
25
+
26
+ <v-switch
27
+ v-model="drawerDefaultOpenModel"
28
+ color="primary"
29
+ inset
30
+ hide-details="auto"
31
+ label="Start with expanded navigation on wider screens"
32
+ />
33
+ </section>
34
+ </template>
35
+
36
+ <style scoped>
37
+ .settings-general-screen :deep(.v-switch) {
38
+ min-height: 48px;
39
+ }
40
+ </style>
@@ -0,0 +1,7 @@
1
+ <script setup>
2
+ import { redirectToChild } from "@jskit-ai/kernel/client/pageRedirects";
3
+
4
+ definePage({
5
+ redirect: redirectToChild("general")
6
+ });
7
+ </script>