@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,13 @@
1
+ <template>
2
+ <v-app>
3
+ <v-main>
4
+ <v-container class="py-10 py-md-14">
5
+ <v-row justify="center">
6
+ <v-col cols="12" sm="11" md="10" lg="8" xl="7">
7
+ <RouterView />
8
+ </v-col>
9
+ </v-row>
10
+ </v-container>
11
+ </v-main>
12
+ </v-app>
13
+ </template>
@@ -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,48 @@
1
+ <template>
2
+ <section class="home-start-screen d-flex flex-column ga-4">
3
+ <header>
4
+ <p class="text-overline text-medium-emphasis mb-1">Home</p>
5
+ <h1 class="home-start-screen__title">Home base</h1>
6
+ <p class="text-body-2 text-medium-emphasis mb-0">
7
+ The app runtime is online and ready for the first real workflow.
8
+ </p>
9
+ </header>
10
+
11
+ <v-sheet rounded="lg" border class="home-start-screen__panel">
12
+ <h2 class="text-h6 mb-2">No activity yet</h2>
13
+ <p class="text-body-2 text-medium-emphasis mb-0">
14
+ Recent work, saved records, and next actions will appear here once this app has data.
15
+ </p>
16
+ </v-sheet>
17
+ </section>
18
+ </template>
19
+
20
+ <style scoped>
21
+ .home-start-screen {
22
+ --home-start-title-size: 2rem;
23
+ --home-start-panel-padding: 1rem;
24
+ }
25
+
26
+ .home-start-screen {
27
+ margin-inline: auto;
28
+ max-width: 48rem;
29
+ }
30
+
31
+ .home-start-screen__title {
32
+ font-size: var(--home-start-title-size);
33
+ font-weight: 700;
34
+ letter-spacing: 0;
35
+ line-height: 1.08;
36
+ margin: 0 0 0.45rem;
37
+ }
38
+
39
+ .home-start-screen__panel {
40
+ padding: var(--home-start-panel-padding);
41
+ }
42
+
43
+ @media (max-width: 640px) {
44
+ .home-start-screen {
45
+ --home-start-title-size: 1.5rem;
46
+ }
47
+ }
48
+ </style>
@@ -0,0 +1,13 @@
1
+ <route lang="json">
2
+ {
3
+ "meta": {
4
+ "jskit": {
5
+ "surface": "home"
6
+ }
7
+ }
8
+ }
9
+ </route>
10
+
11
+ <template>
12
+ <RouterView />
13
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup>
2
+ const title = "Not Found";
3
+ const message = "The page you requested does not exist.";
4
+ </script>
5
+
6
+ <template>
7
+ <v-container class="fill-height d-flex align-center justify-center">
8
+ <v-card class="pa-6 text-center" max-width="560" rounded="lg">
9
+ <v-card-title class="text-h4">{{ title }}</v-card-title>
10
+ <v-card-text class="text-medium-emphasis">{{ message }}</v-card-text>
11
+ </v-card>
12
+ </v-container>
13
+ </template>
@@ -0,0 +1,7 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ describe("reading-room client smoke", () => {
4
+ it("runs vitest in reading-room", () => {
5
+ expect(true).toBe(true);
6
+ });
7
+ });
@@ -0,0 +1,23 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ const VIEWPORTS = Object.freeze([
4
+ Object.freeze({ name: "compact", width: 390, height: 844 }),
5
+ Object.freeze({ name: "medium", width: 768, height: 1024 }),
6
+ Object.freeze({ name: "expanded", width: 1280, height: 900 })
7
+ ]);
8
+
9
+ test.describe("application responsive smoke", () => {
10
+ for (const viewport of VIEWPORTS) {
11
+ test(`${viewport.name} home route renders without horizontal overflow`, async ({ page }) => {
12
+ await page.setViewportSize(viewport);
13
+ await page.goto("/home");
14
+ await expect(page.getByRole("heading", { name: "Home base" })).toBeVisible();
15
+
16
+ const dimensions = await page.evaluate(() => ({
17
+ clientWidth: document.documentElement.clientWidth,
18
+ scrollWidth: document.documentElement.scrollWidth
19
+ }));
20
+ expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth + 1);
21
+ });
22
+ }
23
+ });
@@ -0,0 +1,16 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { createServer } from "../../server.js";
4
+
5
+ test("GET /api/health returns built-in health response", async () => {
6
+ const app = await createServer();
7
+ const response = await app.inject({
8
+ method: "GET",
9
+ url: "/api/health"
10
+ });
11
+
12
+ assert.equal(response.statusCode, 200);
13
+ assert.equal(response.json().ok, true);
14
+
15
+ await app.close();
16
+ });
@@ -0,0 +1,81 @@
1
+ import { fileURLToPath, URL } from "node:url";
2
+ import { defineConfig } from "vite";
3
+ import vue from "@vitejs/plugin-vue";
4
+ import vuetify from "vite-plugin-vuetify";
5
+ import VueRouter from "vue-router/vite";
6
+ import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
7
+
8
+ const configuredDevPort = Number.parseInt(String(process.env.VITE_DEV_PORT || "").trim(), 10);
9
+ const devPort = Number.isInteger(configuredDevPort) && configuredDevPort > 0 ? configuredDevPort : 5173;
10
+ const apiProxyTarget = String(process.env.VITE_API_PROXY_TARGET || "").trim() || "http://localhost:3000";
11
+ const clientEntry = (() => {
12
+ const normalized = String(process.env.VITE_CLIENT_ENTRY || "").trim();
13
+ if (!normalized) {
14
+ return "/src/main.js";
15
+ }
16
+ if (normalized.startsWith("/")) {
17
+ return normalized;
18
+ }
19
+ if (normalized.startsWith("src/")) {
20
+ return `/${normalized}`;
21
+ }
22
+ return `/src/${normalized}`;
23
+ })();
24
+
25
+ export default defineConfig({
26
+ resolve: {
27
+ preserveSymlinks: true,
28
+ alias: {
29
+ "@": fileURLToPath(new URL("./src", import.meta.url))
30
+ }
31
+ },
32
+ plugins: [
33
+ createJskitClientBootstrapPlugin({
34
+ proxyTarget: apiProxyTarget
35
+ }),
36
+ VueRouter({
37
+ routesFolder: "src/pages",
38
+ // Generated on the first Vite dev/build scan and intentionally gitignored.
39
+ dts: "src/typed-router.d.ts",
40
+ nestedChildren: false
41
+ }),
42
+ vue(),
43
+ vuetify({
44
+ autoImport: true
45
+ }),
46
+ {
47
+ name: "jskit-client-entry",
48
+ transformIndexHtml(source) {
49
+ return String(source || "")
50
+ .replace(/\/src\/%VITE_CLIENT_ENTRY%/g, clientEntry)
51
+ .replace(/\/src\/main\.js/g, clientEntry);
52
+ }
53
+ }
54
+ ],
55
+ test: {
56
+ include: ["tests/client/**/*.vitest.js"]
57
+ },
58
+ optimizeDeps: {
59
+ entries: [
60
+ "index.html",
61
+ "src/**/*.{js,ts,vue}"
62
+ ]
63
+ },
64
+ server: {
65
+ port: devPort,
66
+ warmup: {
67
+ clientFiles: [
68
+ "src/main.{js,ts}",
69
+ "src/router/**/*.{js,ts}",
70
+ "src/pages/**/*.{js,ts,vue}",
71
+ "src/components/**/*.{js,ts,vue}"
72
+ ]
73
+ },
74
+ proxy: {
75
+ "/api": {
76
+ target: apiProxyTarget,
77
+ changeOrigin: true
78
+ }
79
+ }
80
+ }
81
+ });
@@ -5,44 +5,47 @@ Use when:
5
5
  - adding a non-CRUD route page
6
6
  - adding a placeholder page or screen stub
7
7
  - adding a menu-linked page
8
- - deciding whether a page and its link should be generated or hand-written
8
+ - deciding how a page and its link should fit the current application
9
9
 
10
10
  Check first:
11
11
 
12
12
  - the app blueprint route family and chosen surface
13
- - `jskit show ui-generator --details`
14
- - `jskit list-placements`
13
+ - the page-and-placement source pattern
14
+ - the current placement declarations and topology
15
15
  - the nearest existing routed host under `src/pages`
16
16
 
17
17
  Rules:
18
18
 
19
- - Default to `jskit generate ui-generator page ...` for a new app-owned non-CRUD route page.
20
- - Let the generator create both the page file and the matching `src/placement.js` entry, then adapt the generated output if needed.
21
- - Choose the generated page's product navigation role deliberately. Use `--navigation-role primary` for main destinations, `secondary` for lower-priority shell links, and `detail`, `workflow`, or `none` when the page should not appear in navigation.
22
- - If the page link belongs in a non-default semantic slot, discover the public placement first with `jskit list-placements` and pass `--link-placement`.
23
- - If you need the concrete outlet inventory, use `jskit list-placements --concrete`; do not target concrete outlets by default.
24
- - If the page sits under an existing routed host, check whether `ui-generator page` can infer the correct `page.section-nav` owner before writing a custom link by hand.
25
- - If you do not use `ui-generator page`, state exactly why the generator does not fit before editing code.
19
+ - Start from the package-owned page-and-placement pattern for a new app-owned
20
+ non-CRUD route, then adapt the page and placement as ordinary source.
21
+ - Create both the page file and matching `src/placement.js` entry when the page
22
+ belongs in navigation.
23
+ - Choose the page's product navigation role deliberately: primary for main
24
+ destinations, secondary for lower-priority shell links, and detail,
25
+ workflow, or no placement when the page should not appear in navigation.
26
+ - Inspect the installed packages' public placement declarations before using
27
+ a semantic slot. Target concrete outlets only for deliberate infrastructure.
28
+ - If the page sits under an existing routed host, reuse its `page.section-nav`
29
+ ownership rather than creating a parallel navigation structure.
26
30
  - For a small placeholder route inside an existing route family, keep the change scoped unless the durable route or surface plan in the app blueprint changed.
27
- - Generated live pages must be usable screens, not instructional scaffolds. Do not ship copy such as "replace this", "use this area", or "this page is ready".
28
- - Prefer a page header plus a direct `v-sheet` working region. Do not wrap every generated page in a generic `v-card`.
31
+ - Live pages must be usable screens, not instructional scaffolds. Do not ship copy such as "replace this", "use this area", or "this page is ready".
32
+ - Prefer a page header plus a direct `v-sheet` working region. Do not wrap every page in a generic `v-card`.
29
33
  - If the screen is not implemented yet, use a product-shaped empty state with one clear next action or status, not developer instructions.
30
- - Primary navigation links belong in semantic placements such as `shell.primary-nav` or `page.section-nav`; do not place every generated route into one drawer by default.
34
+ - Primary navigation links belong in semantic placements such as `shell.primary-nav` or `page.section-nav`; do not place every route into one drawer by default.
31
35
  - Compact layouts must be checked first: no horizontal overflow, no unreachable primary action, and tap targets should be at least 48 px.
32
36
 
33
- Generated UI contract:
37
+ UI contract:
34
38
 
35
39
  - App-facing screens are phone-first and task-first; admin/console screens may be denser but still need responsive controls.
36
40
  - Navigation uses semantic placements by default. Raw `host:position` outlets are advanced infrastructure.
37
41
  - Page architecture is header plus direct work region, normally `v-sheet`; do not use generic page-level `v-card` shells.
38
42
  - Empty/loading/error states are product-shaped and resource-named.
39
43
  - Detail and workflow routes are not primary navigation by default.
40
- - Generated UI must have compact, medium, and expanded browser checks when it changes user-facing behavior.
44
+ - Changed UI must have compact, medium, and expanded browser checks when it changes user-facing behavior.
41
45
 
42
46
  Avoid:
43
47
 
44
- - hand-writing both `src/pages/...` and `src/placement.js` for a normal non-CRUD page before checking `ui-generator`
48
+ - inventing a page and placement shape without checking the existing pattern and neighboring routes
45
49
  - treating a small page stub as permission to rewrite marketing copy, route architecture, or app blueprint scope
46
- - claiming that no generator exists without checking the actual `jskit` generator inventory first
47
50
  - adding cards inside cards or repeating the page title inside a card title
48
51
  - treating Vuetify component defaults as the product architecture
@@ -45,8 +45,8 @@ Placement topology:
45
45
  - Every variant needs an `outlet: "host:position"`.
46
46
  - Variant `renderers` maps semantic `kind` values to component tokens, for example `renderers: { link: "local.main.ui.surface-aware-menu-link-item" }`.
47
47
  - Renderer choice for semantic `kind: "link"` placements belongs in topology, not in each placement entry.
48
- - `default: true` marks the fallback semantic placement that page generators use when no nearer host applies.
49
- - Package topology is discovered too, but app topology with the same `id` and `owner` overrides package topology in CLI discovery.
48
+ - `default: true` marks the fallback semantic placement that app-owned pages use when no nearer host applies.
49
+ - Package topology is discovered too, but app topology with the same `id` and `owner` overrides package topology.
50
50
 
51
51
  Runtime behavior:
52
52
 
@@ -62,21 +62,23 @@ Runtime behavior:
62
62
  - On medium and expanded layouts, closing the standard drawer produces an 80px Material navigation rail by default. Expanded and collapsed icons share one centreline. Use `railWidth` to configure an intentionally denser rail, or `desktopDrawerClosedMode="hidden"` only when another discoverable navigation surface exists.
63
63
  - The shell link renderers own rail centring, selected-state treatment, and opaque theme-aware tooltips. Applications should keep supplying the same semantic placement records rather than creating rail-only links or tooltip CSS.
64
64
 
65
- CLI and generators:
66
-
67
- - `jskit list-placements` shows semantic placements by default.
68
- - `jskit list-placements --concrete` shows concrete `ShellOutlet` recipients.
69
- - `jskit list-placements --all` shows both.
70
- - `jskit list-placements --json` returns structured semantic and concrete placement data.
71
- - `ui-generator page`, CRUD UI list generation, and assistant page generation target semantic placements.
72
- - `--link-placement` for generated pages is a semantic placement id, not a concrete outlet id.
73
- - If a generated page is under a parent host with a mapped `ShellOutlet`, the generator infers the semantic placement and owner from topology.
74
- - `ui-generator add-subpages` upgrades a page into a routed child-page host and appends a `page.section-nav` topology entry for the generated concrete outlet.
75
- - `ui-generator outlet` injects a plain concrete `ShellOutlet` and appends the semantic topology mapping in the same command.
76
- - `ui-generator page` and `crud-ui-generator crud` accept `--navigation-role` so detail, workflow, and utility routes do not accidentally become primary navigation.
65
+ Authoring and inspection:
66
+
67
+ - Read package placement contributions and the app's `src/placement.js` and
68
+ `src/placementTopology.js` directly; they are the complete current state.
69
+ - Source patterns for pages, CRUD UI, and assistant UI target semantic
70
+ placements rather than concrete outlets.
71
+ - A page under a parent host with a mapped `ShellOutlet` should use the
72
+ topology's semantic placement and owner.
73
+ - Upgrading a page into a routed child-page host includes a
74
+ `page.section-nav` topology entry for its concrete outlet in the same change.
75
+ - Adding a concrete `ShellOutlet` includes its semantic topology mapping in the
76
+ same change.
77
+ - Detail, workflow, and utility routes should not accidentally become primary
78
+ navigation entries.
77
79
  - `page.supporting-content` is the default semantic place for supporting detail/content overlays. The default shell maps it to a closed compact bottom sheet and a closed medium/expanded side panel.
78
80
  - When adding a public concrete outlet by hand, add its semantic topology mapping in the same change.
79
- - If `jskit list-placements` reports unmapped concrete outlets, either add semantic topology for them or keep those outlets private/internal.
81
+ - Keep every public concrete outlet mapped by semantic topology; otherwise keep it private/internal.
80
82
 
81
83
  Rules:
82
84
 
@@ -9,10 +9,9 @@ Use when:
9
9
 
10
10
  Read first:
11
11
 
12
- - `packages/agent-docs/site/guide/generators/row-policies.md`
13
12
  - the owning CRUD provider and shared resource
14
- - the generated CRUD repository and service templates
15
- - `packages/crud-server-generator/test/crudService.test.js`
13
+ - the package-owned CRUD server pattern
14
+ - `packages/crud-core/test/defineCrudJsonApiFeature.test.js`
16
15
 
17
16
  Default JSKIT pattern:
18
17
 
@@ -60,7 +59,7 @@ Avoid:
60
59
 
61
60
  - filtering `document.data` in `service.js`
62
61
  - accepting visible ids or hierarchy roots from client query parameters
63
- - installing `RowPolicyPlugin` in generated applications
64
- - adding a default policy file to every generated CRUD
62
+ - installing `RowPolicyPlugin` in applications
63
+ - adding a default policy file to every CRUD
65
64
  - returning a Knex builder or promise from a synchronous grouped contribution
66
65
  - swallowing a policy error and retrying without mandatory visibility