@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
@@ -1,806 +1,72 @@
1
1
  <!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/app-setup/initial-scaffolding.md`. Do not edit manually. -->
2
2
 
3
- # Initial scaffolding
3
+ ---
4
+ title: Application foundations
5
+ description: Understand and adapt JSKIT's tested minimal and adaptive-shell source patterns.
6
+ ---
4
7
 
5
- In this first chapter, we are going to create the smallest useful JSKIT app, install its dependencies, run it locally, and read the scaffold it gives us. The default scaffold already includes `shell-web`, so the app starts with a real shell, placements, settings routes, and the app-level error host. The goal of this chapter is to explain how to get started with JSKIT and to understand what the generator produced, which files matter, and why the project already has concepts like _surfaces_, a local runtime package, and a server even before we add any real features.
8
+ # Application foundations
6
9
 
7
- Start in a working directory and run:
10
+ JSKIT publishes tested source patterns instead of constructing an application
11
+ through a questionnaire. A foundation is a concrete reference application an
12
+ agent can copy, adapt, compose, or use as evidence.
8
13
 
9
- ```bash
10
- npx @jskit-ai/create-app exampleapp --tenancy-mode none
11
- cd exampleapp
12
- npm install
13
- ```
14
-
15
- The first command creates a new folder called `exampleapp` and fills it with JSKIT's default shell-web app template. The `exampleapp` name is used in a few template replacements, such as the package name and the browser title. The `--tenancy-mode none` flag tells JSKIT to start with the smallest routing model. In this mode, the app is not workspace-aware (more of this later in the guide, when multihoming is introduced). That keeps the first scaffold easier to read because there is no workspace slug handling yet.
16
-
17
- If you are working with an AI agent and want the agent to drive the initial JSKIT setup conversation, use the dedicated seed path:
18
-
19
- ```bash
20
- npx @jskit-ai/create-app exampleapp --template ai-seed
21
- cd exampleapp
22
- ```
23
-
24
- That seed writes only `AGENTS.md`. It is not a runnable app yet. The agent should use that file to ask the Stage 1 platform questions first, make sure the chosen MySQL or Postgres database already exists or can be created with the developer's local admin access, and then promote the same directory into the real scaffold with:
25
-
26
- ```bash
27
- npx @jskit-ai/create-app exampleapp --target . --force --tenancy-mode <mode>
28
- npm install
29
- npx jskit migrations sync
30
- npx jskit ci generate
31
- ```
32
-
33
- After that promotion, the overwritten app `AGENTS.md` stays deliberately small. Use it with the distributed JSKIT agent docs when planning or implementing app changes. The durable app memory lives in `.jskit/APP_BLUEPRINT.md` and should describe product and architecture decisions, not become an implementation task list.
34
-
35
- After creating the real app scaffolding (the default shell-web app, not the seed wrapper), you will need to run `npm install` to install dependencies.
36
-
37
- If you deliberately need the bare scaffold, use `--minimal` or `--template minimal-shell`. That is useful for package-development flows, but it is not the normal starting point for a JSKIT app:
38
-
39
- ```bash
40
- npx @jskit-ai/create-app exampleapp --minimal --tenancy-mode none
41
- ```
42
-
43
- Minimal apps can still install the standard shell later with
44
- `npx --no-install jskit add package shell-web`, as long as the starter files it
45
- claims have not been edited first. After the initial `npm install`, prefer
46
- `npx --no-install jskit` for every local JSKIT command so a missing local CLI
47
- fails clearly.
48
-
49
- When a minimal app's first feature is generated CRUD, do not pre-install the
50
- shell as a workaround. Add the database runtime, create the live disposable
51
- table, run `crud-server-generator scaffold`, and then run
52
- `crud-ui-generator crud`. The server
53
- generator installs the shell/realtime dependency closure in the correct order.
54
- The complete Notes command lane is in [CRUD Generators](/guide/generators/crud-generators#fresh-minimal-notes-app-the-complete-command-order).
55
-
56
- If you already know you want a small non-workspace baseline right after the scaffold, this is the shortest reproducible path:
57
-
58
- ```bash
59
- DB_HOST=127.0.0.1
60
- DB_PORT=3306
61
- DB_NAME=exampleapp
62
- DB_USER=...
63
- DB_PASSWORD=...
64
-
65
- npx @jskit-ai/create-app exampleapp --tenancy-mode none
66
- cd exampleapp
67
- npm install
68
-
69
- npx jskit add package auth-provider-local-core
70
- npx jskit add package auth-web
71
-
72
- npx jskit add package database-runtime-mysql \
73
- --db-host "$DB_HOST" \
74
- --db-port "$DB_PORT" \
75
- --db-name "$DB_NAME" \
76
- --db-user "$DB_USER" \
77
- --db-password "$DB_PASSWORD"
78
-
79
- npx jskit add package users-web
80
- npx jskit add package console-web
81
-
82
- npm run db:migrate
83
- ```
84
-
85
- The default auth install is intentionally local and simple. Do not start a new app by adding Supabase, OAuth, OTP, provider linking, or a users/profile projection unless that complexity is already part of the app you are building.
86
-
87
- If you want the larger workspace-enabled stack with the first assistant already configured, use [Quickstart](/guide/app-setup/quickstart) instead.
88
-
89
- **Try Bash Completion!**
90
-
91
- Once `npm install` has finished, you can enable Bash completion for the JSKIT CLI. If you only want it for the current shell session, run:
14
+ ## The adaptive-shell foundation
92
15
 
93
- ```bash
94
- source <(npx jskit completion bash)
95
- ```
96
-
97
- If you want JSKIT completion to keep working in future Bash sessions as well, run:
98
-
99
- ```bash
100
- npx jskit completion bash --install
101
- ```
102
-
103
- That writes a small loader file into your home directory and updates `~/.bashrc` for you. To activate it in the current shell immediately, run `source ~/.bashrc`.
104
-
105
- Once completion is loaded, you can test it immediately.
106
-
107
- If you type:
16
+ `app/shell-foundation` is the normal browser-product baseline. It includes:
108
17
 
109
- ```bash
110
- npx jskit li
111
- ```
112
-
113
- and press Tab twice, Bash will show completions such as `list`, `list-placements`, and `list-component-tokens`.
18
+ - Fastify server and health endpoint
19
+ - Vue/Vite client bootstrap
20
+ - app-local provider package
21
+ - responsive JSKIT shell
22
+ - surfaces, placements, topology, and settings
23
+ - lint, unit, client, build, and browser configuration
24
+ - managed-preview identity support when the host uses it
114
25
 
115
- If you type:
116
-
117
- ```bash
118
- npx jskit add p
119
- ```
26
+ Use it when navigation and settings are real product requirements. Replace its
27
+ home content and vocabulary; do not preserve the concrete `reading-room` name
28
+ when it is wrong for the product.
120
29
 
121
- and press Tab, JSKIT will complete that subcommand argument to `package`.
30
+ ## The minimal foundation
122
31
 
123
- The `jskit` command is central in the use of JSKIT. The autocompletion will help speeding things up.
32
+ `app/minimal-foundation` keeps the same server/client/package boundaries but
33
+ does not install the full adaptive shell. Use it for deliberately small or
34
+ unusual products, not as a temporary hurdle before installing what was already
35
+ known to be required.
124
36
 
125
- To see the app in the browser with the starter health check working, run the frontend and backend in two terminals:
37
+ ## Safe realization inside an existing repository
126
38
 
127
- ```bash
128
- npm run dev
129
- ```
130
-
131
- ```bash
132
- npm run server
133
- ```
134
-
135
- Then open `http://localhost:5173/` in the browser. The starter screen is intentionally small. That is a good thing. It proves the shell is wired correctly before we start adding packages.
136
-
137
- `npm run server` starts the Fastify server on port `3000`. The default home page already uses the Vite proxy to request `/api/health`, so keep the backend running when you want the starter status to be fully green. A good habit is to treat `npm run dev` as the browser-facing process and `npm run server` as the app runtime behind it.
138
-
139
- If you want a fast sanity check that the backend is alive, open `http://localhost:3000/api/health` or request it from the terminal:
140
-
141
- ```bash
142
- curl http://localhost:3000/api/health
143
- ```
144
-
145
- You should get a small JSON response with `ok: true`.
146
-
147
- ## Reading the scaffold
148
-
149
- A fresh app has more structure than a plain Vue starter because JSKIT is preparing both a web shell and an application runtime from the beginning. The top-level layout looks roughly like this:
39
+ Read the entire selected `PATTERN.md` first. Copy or author files directly in
40
+ the initialized project. Always preserve `.git`; when present, also preserve
41
+ existing project and agent context, including roots such as:
150
42
 
151
43
  ```text
152
- exampleapp/
153
- .jskit/
154
- config/
155
- packages/main/
156
- server/
157
- src/
158
- tests/
159
- package.json
160
- server.js
161
- vite.config.mjs
162
- ```
163
-
164
- The first file most people should read is `package.json`. It is the command center for the app. It tells you how to run the frontend (`npm run dev`), the backend (`npm run server`), the test suite, and the build. It also shows the most important dependencies that make the starter shell work: Vue, Vite, Fastify, the JSKIT kernel, and the HTTP runtime.
165
-
166
- The most important parts look like this:
167
-
168
- ```json
169
- {
170
- "engines": {
171
- "node": "26.x"
172
- },
173
- "scripts": {
174
- "server": "node ./bin/server.js",
175
- "server:all": "node ./bin/server.js",
176
- "server:home": "SERVER_SURFACE=home node ./bin/server.js",
177
- "dev": "vite",
178
- "dev:all": "vite",
179
- "dev:home": "VITE_SURFACE=home vite",
180
- "build": "vite build",
181
- "build:all": "vite build",
182
- "build:home": "VITE_SURFACE=home vite build",
183
- "preview": "vite preview",
184
- "lint": "eslint .",
185
- "test": "node --test",
186
- "test:client": "vitest run tests/client",
187
- "test:e2e": "playwright test tests/e2e",
188
- "verify": "jskit app verify && npm run --if-present verify:app",
189
- "release": "jskit app release",
190
- "jskit:update": "jskit app update-packages"
191
- },
192
- "dependencies": {
193
- "@local/main": "file:packages/main",
194
- "@fastify/static": "^9.1.3",
195
- "@jskit-ai/kernel": "0.1.148",
196
- "@tanstack/vue-query": "^5.101.0",
197
- "@jskit-ai/http-runtime": "0.1.146",
198
- "@jskit-ai/shell-web": "0.1.152",
199
- "fastify": "^5.8.5",
200
- "json-rest-schema": "^1.0.17",
201
- "pinia": "^3.0.4",
202
- "vue": "^3.5.38",
203
- "vue-router": "^5.1.0",
204
- "vuetify": "^4.1.2"
205
- },
206
- "devDependencies": {
207
- "@jskit-ai/agent-docs": "0.1.119",
208
- "@jskit-ai/config-eslint": "0.1.145",
209
- "@jskit-ai/jskit-cli": "0.2.179",
210
- "@playwright/test": "1.61.1",
211
- "@vitejs/plugin-vue": "^6.0.7",
212
- "eslint": "^10.8.0",
213
- "vite": "^8.2.1",
214
- "vitest": "^4.1.9"
215
- }
216
- }
217
- ```
218
-
219
- Published JSKIT libraries and tooling support Node.js 22 from 22.13.0 onward, Node.js 24, and Node.js 26. Newly generated applications deliberately require Node 26: their app-level `engines` contract, `.nvmrc`, and generated verification workflow all name that runtime. The app-level contract is the runtime boundary for the app and its installed JSKIT runtime packages, while independently consumed JSKIT CLI and tooling packages retain the wider supported range. The dependency on `@local/main` points at `file:packages/main`, which means your app already contains its own local JSKIT package. The maintenance scripts are also useful to notice early, because they show an important ownership boundary in JSKIT.
220
-
221
- `verify`, `jskit:update`, and `release` are intentionally thin wrappers. They stay in `package.json` because they are convenient app-local shortcuts, but the real implementation lives in `jskit app ...`, not in copied scaffold scripts.
222
-
223
- That matters because JSKIT maintenance policy changes over time. If the scaffold copied a large shell script into every app, existing apps would freeze the old behavior forever. By delegating to `jskit app verify`, `jskit app update-packages`, and `jskit app release`, the app keeps the nice `npm run` shortcuts while the maintained behavior stays in the installed CLI package.
224
-
225
- The Playwright scaffold follows the same rule. `playwright.config.mjs` delegates to `@jskit-ai/jskit-cli/test/playwright`, and the starter browser specs delegate their shared responsive checks to published JSKIT helpers. The generated files stay small while later JSKIT package updates can change local server startup, managed `PLAYWRIGHT_BASE_URL` handling, and `VIBE64_PLAYWRIGHT_STORAGE_STATE` support without copying that logic into each new app.
226
-
227
- The scaffold also configures `src/typed-router.d.ts` as generated output. It is intentionally absent from a fresh scaffold and ignored by Git; the Vue Router Vite plugin writes it during the first `npm run dev` or `npm run build` route scan.
228
-
229
- `jskit app verify` is worth noticing specifically. Linting, tests, and builds check your source code and runtime behavior. The JSKIT part of that flow runs `doctor`, which checks the installed package graph, capabilities, migrations, generated CI, surfaces, placements, and other JSKIT-specific health rules.
230
-
231
- After `npm install`, run `npx jskit ci generate` to write `.github/workflows/jskit-verify.yml` from installed package `ci` contracts. The baseline runs checkout, Node 26 setup, `npm ci`, and `npm run verify`. Packages can add job environment values, service containers, and explicit `before-verify` steps. For example, the database runtime adds migrations before verification. Its MySQL driver adds a MariaDB service with synthetic CI-only credentials and `DB_CLIENT=mysql2`; its Postgres driver adds the equivalent Postgres service and `DB_CLIENT=pg`.
232
-
233
- `npx jskit ci generate` replaces that one workflow in full. Do not edit it. Put application-specific CI in separate workflow files, and use `npx jskit ci generate --check` in validation lanes.
234
-
235
- The surface-specific script names are also worth noticing early, even in this tiny app. `dev:home`, `server:home`, and `build:home` are the first concrete places where surface selection shows up in the scaffold. They work by setting `VITE_SURFACE=home` on the client side and `SERVER_SURFACE=home` on the server side. In this first chapter, where `home` is the only surface, those variants behave almost the same as the default commands. Later, once more surfaces exist, those scripts become the simplest way to run or build just one surface at a time.
236
-
237
- ### App surfaces in JSKIT
238
-
239
- A surface is JSKIT's name for a named slice of the application. They are a very important concept in JSKIT, since a surface can be built -- and deployed -- separately from each other. This is useful if for example you want the end-user interface _not_ to contain _any_ of the symbols/strings of the admin interface.
240
-
241
- Surfaces are defined in a very important file in JSKIT: `config/public.js`. This is the app's shared public configuration, used both by client and server. It's called "public" because it _will_ be read by the browser, and therefore it _will_ be available to the world. It defines the current tenancy mode, the default surface, and the list of surface definitions. In this first scaffold there is only one surface, `home`, which is the starter surface
242
-
243
- Even though we are using `--tenancy-mode none`, it will still be possible to add more surfaces. Every app starts with a single `home` surface, and later packages will expand that topology.
244
-
245
- Here is the part of `config/public.js` that sets that up:
246
-
247
- ```js
248
- import { surfaceAccessPolicies } from "./surfaceAccessPolicies.js";
249
-
250
- export const config = {};
251
- config.tenancyMode = "none";
252
-
253
- config.surfaceModeAll = "all";
254
- config.surfaceDefaultId = "home";
255
- config.webRootAllowed = "no";
256
- config.surfaceAccessPolicies = surfaceAccessPolicies;
257
- config.surfaceDefinitions = {};
258
- config.surfaceDefinitions.home = {
259
- id: "home",
260
- label: "Home",
261
- pagesRoot: "home",
262
- enabled: true,
263
- requiresAuth: false,
264
- requiresWorkspace: false,
265
- accessPolicyId: "public",
266
- origin: ""
267
- };
268
- ```
269
-
270
- Right next to that file is `config/surfaceAccessPolicies.js`. This is where the access rules for surfaces live. In the initial shell, `home` uses the `public` policy. You do not need to change these policies now, but you do need to know where they come from, because later packages will extend them.
271
-
272
- The starter policies are small enough to read in one glance:
273
-
274
- ```js
275
- export const surfaceAccessPolicies = {};
276
-
277
- surfaceAccessPolicies.public = {};
278
- ```
279
-
280
- That tells you one thing immediately: `home` is open. More specific policies only appear when later packages add them.
281
-
282
- ### The client side
283
-
284
- #### Client bootstrap
285
-
286
- The `src/` directory is the frontend application. `src/main.js` is the real boot file. It creates the Vue app, sets up the router, enables Vuetify, and builds a JSKIT surface runtime from `config/public.js`. That one file is worth reading carefully because it shows the main client-side contract of a JSKIT app: scaffold config is turned into a running client shell, with the surface runtime, router, installed client modules, and app boot all wired together.
287
-
288
- The important part looks like this:
289
-
290
- ```js
291
- import { createApp } from "vue";
292
- import { createPinia } from "pinia";
293
- import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
294
- import { createRouter, createWebHistory } from "vue-router/auto";
295
- import { routes } from "vue-router/auto-routes";
296
- import "vuetify/styles";
297
- import { createVuetify } from "vuetify";
298
- import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
299
- import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
300
- import {
301
- shouldRetryTransientQueryFailure,
302
- transientQueryRetryDelay
303
- } from "@jskit-ai/kernel/shared/support";
304
- import {
305
- bootstrapClientShellApp,
306
- createShellRouter
307
- } from "@jskit-ai/kernel/client";
308
- import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
309
- import App from "./App.vue";
310
- import NotFoundView from "./views/NotFound.vue";
311
- import { config } from "../config/public.js";
312
-
313
- const surfaceRuntime = createSurfaceRuntime({
314
- allMode: config.surfaceModeAll,
315
- surfaces: config.surfaceDefinitions,
316
- defaultSurfaceId: config.surfaceDefaultId
317
- });
318
-
319
- const surfaceMode = surfaceRuntime.normalizeSurfaceMode(import.meta.env.VITE_SURFACE);
320
- const { router, fallbackRoute } = createShellRouter({
321
- createRouter,
322
- history: createWebHistory(),
323
- routes,
324
- surfaceRuntime,
325
- surfaceMode,
326
- notFoundComponent: NotFoundView,
327
- guard: {
328
- surfaceDefinitions: config.surfaceDefinitions,
329
- defaultSurfaceId: config.surfaceDefaultId,
330
- webRootAllowed: config.webRootAllowed
331
- }
332
- });
333
-
334
- const vuetify = createVuetify({
335
- theme: {
336
- defaultTheme: "light"
337
- },
338
- icons: {
339
- defaultSet: "mdi",
340
- aliases: mdiAliases,
341
- sets: { mdi }
342
- }
343
- });
344
- const pinia = createPinia();
345
- const queryClient = new QueryClient({
346
- defaultOptions: {
347
- queries: {
348
- refetchOnWindowFocus: false,
349
- refetchOnReconnect: true,
350
- retry: shouldRetryTransientQueryFailure,
351
- retryDelay: transientQueryRetryDelay
352
- }
353
- }
354
- });
355
-
356
- void bootstrapClientShellApp({
357
- createApp,
358
- rootComponent: App,
359
- appConfig: config,
360
- appPlugins: [
361
- pinia,
362
- [VueQueryPlugin, { queryClient }],
363
- vuetify
364
- ],
365
- pinia,
366
- queryClient,
367
- router,
368
- bootClientModules: bootInstalledClientModules,
369
- surfaceRuntime,
370
- surfaceMode,
371
- env: import.meta.env,
372
- fallbackRoute
373
- }).catch((error) => {
374
- console.error("Failed to bootstrap client app.", error);
375
- });
376
- ```
377
-
378
- The flow is simple once you read it in order: config in, runtime in memory, router built from that runtime, app-owned Vue plugins created once, app bootstrapped. Pinia, Vue Query, the router, and Vuetify are owned by the base app bootstrap so runtime packages share the same instances instead of carrying independent copies. Passing the same `queryClient` into `bootstrapClientShellApp(...)` also gives `shell-web` the QueryClient it observes for automatic request recovery when transport failures exhaust their normal retries.
379
-
380
- <DocsInDepth title="In depth" preview-height="15rem">
381
-
382
- `createSurfaceRuntime(...)` turns the static surface config into a small runtime registry that both the router and later client modules can query. In the starter app, the input data is basically this:
383
-
384
- ```js
385
- config.surfaceDefinitions = {
386
- home: {
387
- pagesRoot: "home", // this surface lives under src/pages/home
388
- requiresAuth: false,
389
- accessPolicyId: "public"
390
- }
391
- };
392
-
393
- config.surfaceDefaultId = "home"; // default surface
394
- config.surfaceModeAll = "all"; // unrestricted surface mode
395
- ```
396
-
397
- That means the `home` surface itself lives under `/home`. Because `home` is also the default surface and the starter scaffold sets `webRootAllowed = "no"`, visiting `/` redirects to `/home`.
398
-
399
- That gives the client a normalized surface runtime with answers to questions such as:
400
-
401
- ```js
402
- surfaceRuntime.DEFAULT_SURFACE_ID; // "home"
403
- surfaceRuntime.listEnabledSurfaceIds(); // ["home"]
404
- surfaceRuntime.resolveSurfaceFromPathname("/home"); // "home"
405
- ```
406
-
407
- `surfaceMode` is not another surface definition. It is the current viewing mode for the app. In a starter app, `VITE_SURFACE` is usually unset, so `surfaceMode` becomes `"all"`, meaning "do not restrict the router to one specific surface". Later, when you run surface-specific profiles, the same runtime can narrow the active routes to just one surface. In the scaffold scripts, `npm run dev:home` is simply setting `VITE_SURFACE=home`, while `npm run dev` and `npm run dev:all` leave the client in unrestricted `"all"` mode.
408
-
409
- `createShellRouter(...)` uses that `surfaceRuntime` object to assemble the actual router. Concretely, it does this:
410
-
411
- 1. Takes the generated Vue routes.
412
- 2. Adds a fallback not-found route.
413
- 3. Filters those routes down to the ones that belong to the current surface mode.
414
- 4. Calls Vue Router's real `createRouter(...)` with that filtered route list.
415
- 5. Installs a `beforeEach` guard for surface-aware redirects and access behavior.
416
-
417
- So this is not a separate routing system. It is a thin JSKIT wrapper around normal Vue Router setup, using the surface runtime to decide which routes should be active and which guard behavior should be applied.
418
-
419
- In the starter app, with only `home`, this is almost boring. It mostly means:
420
-
421
- - build the router
422
- - add not-found
423
- - enforce the `/` to `/home` redirect behavior
424
-
425
- `createPinia()` is the standard shared-state layer for the client app. JSKIT installs it from day 0 so later packages can expose Vue-facing stores without each app having to bolt Pinia on afterward.
426
-
427
- The default scaffold already has the `shell-web` client stores available, such as `useShellLayoutStore()` and the shell error presentation store behind `ShellErrorHost`. Pinia is also there from day 0 so later packages, such as `auth-web`, can add their own stores without changing the app bootstrap.
428
-
429
- `createVuetify(...)` is the ordinary UI plugin setup. There is nothing especially JSKIT-specific there; it configures theme settings and icon aliases before the router is mounted. Vuetify components are auto-imported by `vite-plugin-vuetify`, so the scaffold does not register the full Vuetify component namespace in the client bundle.
430
-
431
- `bootInstalledClientModules` is the extension seam, meaning "this is the point where later-installed JSKIT packages get to join client startup". The confusing part is that it is not a normal file in your app. In `src/main.js` you import it from:
432
-
433
- ```js
434
- import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
435
- ```
436
-
437
- That `virtual:` prefix is a Vite convention. It means there is no real file on disk with that name. Instead, JSKIT's Vite plugin generates that module for you during development and build.
438
-
439
- The startup path looks like this:
440
-
441
- 1. `src/main.js` imports `virtual:jskit-client-bootstrap`.
442
- 2. The JSKIT Vite plugin sees that import and generates a module on the fly.
443
- 3. That generated module imports the `.../client` entrypoint of each installed JSKIT package that has client-side behavior.
444
- 4. It wraps those imported modules into one function called `bootInstalledClientModules(...)`.
445
- 5. `bootstrapClientShellApp(...)` calls that function before the router is installed and before the app is mounted.
446
- 6. `bootClientModules(...)` then starts client providers and registers any client routes contributed by those installed packages.
447
-
448
- The generated module is roughly like this:
449
-
450
- ```js
451
- import * as authClient from "@jskit-ai/auth-web/client";
452
- import * as usersClient from "@jskit-ai/users-web/client";
453
- import { bootClientModules } from "@jskit-ai/kernel/client/moduleBootstrap";
454
-
455
- const installedClientModules = [
456
- { packageId: "@jskit-ai/auth-web", module: authClient },
457
- { packageId: "@jskit-ai/users-web", module: usersClient }
458
- ];
459
-
460
- async function bootInstalledClientModules(context = {}) {
461
- return bootClientModules({
462
- ...context,
463
- clientModules: installedClientModules
464
- });
465
- }
466
- ```
467
-
468
- That is why Vite is involved. The browser cannot inspect the installed npm graph and turn it into bundler-visible imports at runtime. Vite needs a normal import graph up front. The plugin creates that graph for the app.
469
-
470
- If this were plain Vue without that plugin, you would have to maintain the list yourself:
471
-
472
- ```js
473
- import { bootClientModules } from "@jskit-ai/kernel/client/moduleBootstrap";
474
- import * as authClient from "@jskit-ai/auth-web/client";
475
- import * as usersClient from "@jskit-ai/users-web/client";
476
-
477
- await bootClientModules({
478
- app,
479
- router,
480
- surfaceRuntime,
481
- surfaceMode,
482
- clientModules: [
483
- { packageId: "@jskit-ai/auth-web", module: authClient },
484
- { packageId: "@jskit-ai/users-web", module: usersClient }
485
- ]
486
- });
487
- ```
488
-
489
- JSKIT automates that step so adding or removing packages does not require hand-editing `src/main.js`.
490
-
491
- In a brand-new shell app, there are no extra installed client modules yet, so the generated function is effectively empty. Later, when you install packages with client providers or extra UI routes, this same hook is what starts those providers and registers those routes.
492
-
493
- `bootstrapClientShellApp(...)` is the final assembly step. It creates the Vue app, installs plugins such as Pinia and Vuetify, passes the Pinia instance explicitly into client-module boot, stores the client app config, runs `bootInstalledClientModules(...)`, attaches the fallback route if needed, installs the router, waits for the router to be ready, and only then mounts the app. That order matters because installed packages need a chance to extend the app before the first render happens.
494
-
495
- </DocsInDepth>
496
-
497
- #### The main package (client side)
498
-
499
- One more client-side piece is worth seeing before looking at page files: the starter app already has its own client provider. The app-local package declares it in `packages/main/package.json` like this:
500
-
501
- ```json
502
- {
503
- "jskit": {
504
- "runtime": {
505
- "client": {
506
- "providers": [
507
- {
508
- "entrypoint": "src/client/providers/MainClientProvider.js",
509
- "export": "MainClientProvider"
510
- }
511
- ]
512
- }
513
- }
514
- }
515
- }
516
- ```
517
-
518
- That declaration is one of the things `bootClientModules(...)` uses. On the client, the lifecycle is:
519
-
520
- 1. collect the installed client modules
521
- 2. resolve the provider classes they declare
522
- 3. create the client runtime application container
523
- 4. run each provider's `register()` method
524
- 5. run each provider's `boot()` method, if it has one
525
-
526
- So yes: client providers use the same `register()`/`boot()` lifecycle pattern as server providers. In the scaffold, the app-local client provider starts like this:
527
-
528
- ```js
529
- const mainClientComponents = [];
530
-
531
- function registerMainClientComponent(token, resolveComponent) {
532
- mainClientComponents.push({ token, resolveComponent });
533
- }
534
-
535
- class MainClientProvider {
536
- static id = "local.main.client";
537
-
538
- register(app) {
539
- for (const { token, resolveComponent } of mainClientComponents) {
540
- app.singleton(token, resolveComponent);
541
- }
542
- }
543
- }
544
-
545
- export {
546
- MainClientProvider,
547
- registerMainClientComponent
548
- };
549
- ```
550
-
551
- The important idea is that this provider is not rendering UI directly. It is registering token-addressable client components into the application container. In the default scaffold, the list starts with shell link components that the placement runtime can use for menus and tabs. Later package installs and generators can extend this file by adding imports and `registerMainClientComponent(...)` calls for more app-owned client components. In other words, this file is the app's local registration seam.
552
-
553
- ```js
554
- import MenuLinkItem from "/src/components/menus/MenuLinkItem.vue";
555
-
556
- registerMainClientComponent("local.main.ui.menu-link-item", () => MenuLinkItem);
557
- ```
558
-
559
- Then `MainClientProvider.register(app)` publishes those into the client container with `app.singleton(...)`. Later packages and placement runtime code can ask for those components by token instead of importing app files directly.
560
-
561
- This code is intentionally small. `registerMainClientComponent(...)` is a private app-local registration hook, not a public validation API, so the scaffold keeps it minimal and lets obvious mistakes fail honestly when the provider is used.
562
-
563
- `MainClientProvider` does not define a `boot()` method yet, so the boot phase is effectively empty for this provider right now. But the lifecycle still supports it. If you later add `boot()`, JSKIT will run it after all client providers have finished `register()`.
564
-
565
- Inside `src/pages/` you will find both route owners and actual page components. The easy file to notice is `src/pages/home/index.vue`, because that is the page with visible content. The easy file to miss is `src/pages/home.vue`. That wrapper file contains route metadata that attaches the page tree to a JSKIT surface. When you later add more pages, that surface information is one of the things JSKIT uses to decide where a page belongs.
566
-
567
- The wrapper file is tiny, but it is doing an important job:
568
-
569
- ```vue
570
- <route lang="json">
571
- {
572
- "meta": {
573
- "jskit": {
574
- "surface": "home"
575
- }
576
- }
577
- }
578
- </route>
579
-
580
- <template>
581
- <RouterView />
582
- </template>
583
- ```
584
-
585
- This is why `src/pages/home/index.vue` becomes part of the `home` surface instead of just being "some route".
586
-
587
- **Routing: How JSKIT Reads Route Metadata**
588
-
589
- The `<route lang="json">` block is not something `shell-web` or `auth-web` parses directly.
590
-
591
- During dev/build, the file-based Vue Router plugin scans `src/pages/` and turns that block into ordinary Vue Router route metadata. Later, when navigation happens, JSKIT reads that metadata from the matched route records.
592
-
593
- That is why a block like this can affect behavior without any extra code in the page component itself:
594
-
595
- - `meta.jskit.surface` tells JSKIT which surface the page tree belongs to
596
- - `meta.guard.policy` can later tell JSKIT whether the route should be treated as `public` or `authenticated`
597
-
598
- So the order is:
599
-
600
- 1. the router plugin reads the `<route>` block
601
- 2. the generated route record gets a normal `meta` object
602
- 3. JSKIT reads that `meta` during routing
603
-
604
- `src/App.vue` is deliberately small. It is the outer Vuetify app shell, the top-level `RouterView`, and `ShellErrorHost`. That is another pattern you should get used to in JSKIT: the app root stays thin, and most behavior is pushed toward packages, page files, and runtime providers.
605
-
606
- **Container: App Methods**
607
-
608
- The `app` object in `register(app)` and `boot(app)` is JSKIT's application container.
609
-
610
- Defining values:
611
-
612
- - `app.singleton(token, factory)`: register one lazily created shared value for the whole app.
613
- - `app.bind(token, factory)`: register a factory that creates a fresh value every time the token is resolved.
614
- - `app.scoped(token, factory)`: register one value per child scope.
615
- - `app.instance(token, value)`: register an already-created value directly.
616
- - `app.tag(token, tagName)`: add a token to a named group.
617
-
618
- Typical examples look like this:
619
-
620
- ```js
621
- app.instance("appConfig", appConfig);
622
- app.singleton("local.main.ui.menu-link-item", () => TabLinkItem);
623
- app.bind("feature.clock", () => new Clock());
624
- app.tag("auth.login.component", "auth.ui");
625
- ```
626
-
627
- That means:
628
-
629
- - `instance(...)` is good for something you already created, such as loaded config.
630
- - `singleton(...)` is good for one shared runtime object or component resolver.
631
- - `bind(...)` is for "make a fresh one each time someone asks".
632
- - `tag(...)` lets you group related tokens so another part of the app can ask for the whole set later.
633
-
634
- Using values:
635
-
636
- - `app.make(token)`: resolve a token from the container.
637
- - `app.has(token)`: check whether a token is registered.
638
- - `app.createScope(scopeId)`: create a child scope.
639
- - `app.resolveTag(tagName)`: resolve all tokens in that group.
640
-
641
- Then later code can consume those registrations like this:
642
-
643
- ```js
644
- const authGuardRuntime = app.make("runtime.auth-guard.client");
645
-
646
- if (app.has("jskit.client.vue.app")) {
647
- const vueApp = app.make("jskit.client.vue.app");
648
- }
649
-
650
- const authUi = app.resolveTag("auth.ui");
651
- const requestScope = app.createScope("request:123");
652
- ```
653
-
654
- A scope is a real child container, not just a label. You can put request-local values into it and then resolve them from the scope itself:
655
-
656
- ```js
657
- app.singleton("logger", () => createLogger());
658
-
659
- const requestScope = app.createScope("request:123");
660
- requestScope.instance("request.id", "request:123");
661
-
662
- const logger = requestScope.make("logger"); // inherited from the parent app container
663
- const requestId = requestScope.make("request.id"); // local to this scope
664
- ```
665
-
666
- Here:
667
-
668
- - `make(...)` gets the thing behind a token.
669
- - `has(...)` lets you check before assuming a token exists.
670
- - `resolveTag(...)` gets every token in a named group.
671
- - `createScope(...)` gives you a child container when you need scoped values rather than app-wide ones, and that child container can then use `make(...)`, `has(...)`, `instance(...)`, and the other container methods too.
672
-
673
- ### The server side
674
-
675
- The backend entry point is `server.js`, with `bin/server.js` acting as the small executable wrapper used by the npm scripts. `server.js` starts Fastify, registers a built-in `/api/health` route, loads the provider runtime, and decides how to serve the frontend. In development, you normally visit the Vite dev server on port `5173`. In a built app, this same server can also serve the compiled frontend.
676
-
677
- The core of that startup path looks like this:
678
-
679
- ```js
680
- async function createServer() {
681
- const app = Fastify({ logger: true });
682
-
683
- app.get("/api/health", async () => {
684
- return {
685
- ok: true,
686
- app: "exampleapp"
687
- };
688
- });
689
-
690
- const runtimeEnv = resolveRuntimeEnv();
691
- const appRoot = path.resolve(process.cwd());
692
- const runtime = await tryCreateProviderRuntimeFromApp({
693
- appRoot,
694
- profile: resolveRuntimeProfileFromSurface({
695
- surfaceRuntime,
696
- serverSurface: runtimeEnv.SERVER_SURFACE
697
- }),
698
- env: runtimeEnv,
699
- logger: app.log,
700
- fastify: app
701
- });
702
-
703
- registerSurfaceRequestConstraint({
704
- fastify: app,
705
- surfaceRuntime,
706
- serverSurface: runtimeEnv.SERVER_SURFACE,
707
- globalUiPaths: resolveGlobalUiPaths(runtime?.globalUiPaths || [])
708
- });
709
-
710
- return app;
711
- }
712
- ```
713
-
714
- The health route is built in, but the more important idea is that the server is already prepared to validate HTTP input with Fastify's normal JSON Schema path, load the JSKIT provider runtime from the app itself, and constrain requests by surface.
715
-
716
- You will also notice `config/server.js`. In the base app it is intentionally almost empty. It is there to reserve a clear place for server-side configuration as backend features are added, without pretending the starter app already has feature-specific server configuration.
717
-
718
- The small `server/lib/` directory exists to keep that server boot code tidy. `runtimeEnv.js` reads environment variables such as port and host. `surfaceRuntime.js` builds the same surface runtime that the client uses, so the server and browser agree on what surfaces exist. In the scaffold scripts, `npm run server:home` is simply setting `SERVER_SURFACE=home`, while `npm run server` and `npm run server:all` leave the server unrestricted.
719
-
720
- #### The main package (server side)
721
-
722
- The most unusual part of the scaffold, if you are new to JSKIT, is `packages/main/`. This is the app-local runtime package. It is not there by accident, and it is not just a convenience folder. JSKIT treats your app itself as a local package with package metadata, client provider hooks, and server provider hooks. That is why the folder contains `package.json` and a small `src/` tree of its own.
723
-
724
- You already saw the client-side provider in the client bootstrap path. The server side uses the same model: `package.json.jskit` tells JSKIT which provider class belongs to the local package, and the runtime calls `register()` and then `boot()`.
725
-
726
- The server part of that metadata looks like this:
727
-
728
- ```json
729
- {
730
- "name": "@local/main",
731
- "version": "0.1.0",
732
- "jskit": {
733
- "kind": "runtime",
734
- "runtime": {
735
- "server": {
736
- "providerEntrypoint": "src/server/MainServiceProvider.js",
737
- "providers": [
738
- {
739
- "entrypoint": "src/server/MainServiceProvider.js",
740
- "export": "MainServiceProvider"
741
- }
742
- ]
743
- }
744
- },
745
- "metadata": {
746
- "server": {
747
- "routes": []
748
- }
749
- }
750
- }
751
- }
44
+ .git/
45
+ .agents/
46
+ .codex/
752
47
  ```
753
48
 
754
- This is the moment where the scaffold stops looking like "just a Vue app". The app is declaring itself as a runtime package that JSKIT can discover, load, and mutate safely.
755
-
756
- For the server side, the main file to remember is `packages/main/src/server/MainServiceProvider.js`. It stays intentionally flat and small. That is the point: `packages/main` is the app-local composition package, not the default home for new backend feature trees.
757
-
758
- The server-side provider starts like this:
759
-
760
- ```js
761
- import { loadAppConfig } from "./loadAppConfig.js";
762
-
763
- class MainServiceProvider {
764
- static id = "local.main";
765
-
766
- async register(app) {
767
- const appConfig = await loadAppConfig({
768
- moduleUrl: import.meta.url
769
- });
770
- app.instance("appConfig", appConfig);
771
- }
772
-
773
- boot() {}
774
- }
775
-
776
- export { MainServiceProvider };
777
- ```
778
-
779
- It is deliberately small because it is only for app-local glue: loading config, wiring tiny app-specific behavior, and bootstrapping shared runtime concerns. When a backend capability becomes substantial, do not grow `packages/main` into a mini service tree. Generate a dedicated package instead:
780
-
781
- ```bash
782
- npx jskit generate feature-server-generator scaffold booking-engine
783
- ```
784
-
785
- That keeps the ownership boundary clear: `packages/main` stays composition-only, while real server features get their own provider, service, and optional repository seams. The client side uses the same provider lifecycle; you already saw the matching pattern earlier in the client boot path.
786
-
787
- The root `package.json`, its exact JSKIT dependency versions, and
788
- `package-lock.json` define the installed graph. JSKIT reads each installed
789
- package's `package.json.jskit` metadata directly. The local `@local/main`
790
- package participates through its normal `file:packages/main` dependency.
791
-
792
- ### Other files and options
793
-
794
- The remaining files are easier to understand once you know the core pieces above. `vite.config.mjs` configures the frontend build and the `/api` proxy used during development. `index.html` is the HTML shell Vite uses to mount Vue. `tests/` contains basic smoke tests so the app has a verification path from day one. The `scripts/` directory is intentionally small because JSKIT maintenance helpers such as `verify`, `jskit:update`, and `release` are package-owned CLI commands rather than copied app scripts.
49
+ Inspect every collision. A direct pattern copy must never need `--force` and
50
+ must never work through a temporary directory. Rename the packaged `gitignore`
51
+ asset to `.gitignore` when using it.
795
52
 
796
- The `create-app` command also accepts a few other flags that are useful without changing the basic meaning of this chapter's setup. `--title <text>` lets you replace the browser title and other template text with a friendlier app name. `--target <path>` lets you choose a different output directory instead of the default `./exampleapp`. `--tenancy-mode <mode>` can seed `none`, `personal`, or `workspaces`; for this chapter we intentionally use `none` so the first scaffold stays small and non-workspace. `--minimal` selects the bare `minimal-shell` template instead of the default shell-web app template. `--force` allows writing into a non-empty target directory when you know that is what you want. `--dry-run` prints the planned file writes without touching the filesystem, which is useful when you want to inspect what the generator would do. `-h` or `--help` prints the command help.
53
+ ## Ownership after copying
797
54
 
798
- **Template: Use The Default Shell Unless You Need Minimal**
55
+ All copied files are ordinary application source. The pattern package does not
56
+ own future edits. There is no generated-file header, pattern receipt, template
57
+ hash, completion record, or replay journal. Framework packages continue to own
58
+ their public runtime APIs; the application owns its product source.
799
59
 
800
- The default app already includes `shell-web`. Start there for normal JSKIT apps because it gives you placement-aware navigation, settings routes, and shell-level error presentation immediately.
60
+ ## Loading and errors
801
61
 
802
- Use `--minimal` only when you deliberately need the bare scaffold. If you later add `shell-web` to a minimal app, do it before editing files such as `src/App.vue`, `src/pages/home.vue`, and `src/pages/home/index.vue`, because the package only claims files that still match the untouched minimal baseline exactly.
62
+ The adaptive example demonstrates structure-matching skeletons for visible
63
+ loading. Do not replace them with generic spinners. Keep failures local when a
64
+ resource cannot render; use shared toast/snackbar feedback for transient user
65
+ commands so errors do not push the working page down.
803
66
 
804
- ## Summary
67
+ ## Verification
805
68
 
806
- At the end of this first step, you should have more than a generated folder. You should have a mental map. `src/` is the web app, `server.js` is the runtime server, `config/` defines surfaces and shared behavior, `packages/main/` is your app's own local JSKIT package, and the npm package graph supplies JSKIT runtime metadata. That is the foundation the next chapters will build on.
69
+ Pattern contract tests keep both examples concrete, parseable, npm-published,
70
+ free from interpolation tokens, and aligned with their public dependencies.
71
+ After adapting a foundation, verify the actual application rather than
72
+ comparing it byte-for-byte with the example.