@jskit-ai/agent-docs 0.1.27 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISTR_AGENT.md +1 -0
- package/guide/agent/app-extras/assistant.md +2 -2
- package/guide/agent/app-extras/mobile-capacitor.md +10 -3
- package/guide/agent/app-extras/realtime.md +11 -11
- package/guide/agent/app-setup/a-more-interesting-shell.md +52 -44
- package/guide/agent/app-setup/authentication.md +40 -22
- package/guide/agent/app-setup/console.md +5 -5
- package/guide/agent/app-setup/database-layer.md +15 -15
- package/guide/agent/app-setup/initial-scaffolding.md +37 -5
- package/guide/agent/app-setup/multi-homing.md +28 -28
- package/guide/agent/app-setup/users.md +22 -22
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +13 -6
- package/guide/agent/generators/advanced-cruds.md +111 -68
- package/guide/agent/generators/crud-generators.md +5 -5
- package/guide/agent/generators/ui-generators.md +15 -13
- package/guide/agent/index.md +1 -1
- package/package.json +1 -1
- package/patterns/INDEX.md +4 -1
- package/patterns/client-requests.md +23 -6
- package/patterns/crud-repository-mapping.md +1 -1
- package/patterns/crud-scaffolding.md +10 -1
- package/patterns/filters.md +28 -10
- package/patterns/generated-ui-contract-tracking.md +62 -0
- package/patterns/page-scaffolding.md +17 -0
- package/patterns/placements.md +60 -0
- package/patterns/surfaces.md +5 -1
- package/patterns/ui-testing.md +3 -0
- package/reference/autogen/KERNEL_MAP.md +30 -1
- package/reference/autogen/packages/assistant.md +1 -1
- package/reference/autogen/packages/crud-ui-generator.md +19 -8
- package/reference/autogen/packages/kernel.md +38 -6
- package/reference/autogen/packages/realtime.md +1 -0
- package/reference/autogen/packages/shell-web.md +35 -3
- package/reference/autogen/packages/ui-generator.md +26 -5
- package/reference/autogen/packages/users-core.md +2 -0
- package/reference/autogen/packages/users-web.md +92 -3
- package/reference/autogen/packages/workspaces-web.md +3 -1
- package/reference/autogen/tooling/jskit-cli.md +24 -8
- package/templates/app/AGENTS.md +1 -0
- package/workflow/bootstrap.md +1 -0
|
@@ -25,7 +25,7 @@ npm run db:migrate
|
|
|
25
25
|
- `console-core` owns the console schema, owner check, bootstrap flag, and API routes
|
|
26
26
|
- `console-web` owns the console surface scaffold, settings shell, and menu placement
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
The console is a real vertical slice. The users packages do not own it.
|
|
29
29
|
|
|
30
30
|
## What the console is for
|
|
31
31
|
|
|
@@ -70,7 +70,7 @@ Then sign in.
|
|
|
70
70
|
|
|
71
71
|
Once you are authenticated, two things matter:
|
|
72
72
|
|
|
73
|
-
- the app
|
|
73
|
+
- the app knows who you are as a persistent JSKIT user
|
|
74
74
|
- the console bootstrap logic can determine whether you are the console owner
|
|
75
75
|
|
|
76
76
|
Open:
|
|
@@ -92,7 +92,7 @@ This is the most important idea in the chapter.
|
|
|
92
92
|
|
|
93
93
|
The account surface only requires authentication. The console surface requires a specific access flag: `console_owner`.
|
|
94
94
|
|
|
95
|
-
That means the guide
|
|
95
|
+
That means the guide has two different kinds of authenticated routes:
|
|
96
96
|
|
|
97
97
|
- normal authenticated routes such as `/account`
|
|
98
98
|
- privileged authenticated routes such as `/console`
|
|
@@ -163,7 +163,7 @@ This is the first clear example in the guide of a surface guarded by a named fla
|
|
|
163
163
|
|
|
164
164
|
### The starter console routes are app-owned
|
|
165
165
|
|
|
166
|
-
After the previous chapter, the app
|
|
166
|
+
After the previous chapter, the app has:
|
|
167
167
|
|
|
168
168
|
```text
|
|
169
169
|
src/pages/console.vue
|
|
@@ -308,7 +308,7 @@ That stricter access model is the key idea to keep:
|
|
|
308
308
|
- `/account` is for any signed-in user
|
|
309
309
|
- `/console` is only for the console owner
|
|
310
310
|
|
|
311
|
-
So the app
|
|
311
|
+
So the app has both:
|
|
312
312
|
|
|
313
313
|
- a normal authenticated user area
|
|
314
314
|
- a privileged operator area with its own server-side ownership check
|
|
@@ -49,11 +49,11 @@ Installing `database-runtime-mysql` gives the app three important things right a
|
|
|
49
49
|
|
|
50
50
|
### A real database runtime
|
|
51
51
|
|
|
52
|
-
The server can
|
|
52
|
+
The server can build a real Knex client from environment variables. That gives later packages a standard way to ask for a database connection instead of each package inventing its own wiring.
|
|
53
53
|
|
|
54
54
|
### A migration workflow
|
|
55
55
|
|
|
56
|
-
The app
|
|
56
|
+
The app gets three database scripts in `package.json`:
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
@@ -73,7 +73,7 @@ If you run the status command immediately after this chapter:
|
|
|
73
73
|
npm run db:migrate:status
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
you should still see that there are no completed migrations and no pending migration files yet. The runtime and the Knex wiring exist
|
|
76
|
+
you should still see that there are no completed migrations and no pending migration files yet. The runtime and the Knex wiring exist at this point, but no package has added real schema files until the next chapter.
|
|
77
77
|
|
|
78
78
|
### A place for future schema files
|
|
79
79
|
|
|
@@ -88,7 +88,7 @@ That is the key idea of this chapter:
|
|
|
88
88
|
|
|
89
89
|
This chapter is the right place to make one distinction very explicit.
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
The app has two different migration-related layers:
|
|
92
92
|
|
|
93
93
|
- JSKIT-managed migration files on disk
|
|
94
94
|
- Knex actually applying those files to the database
|
|
@@ -171,7 +171,7 @@ import {
|
|
|
171
171
|
} from "@jskit-ai/database-runtime/shared";
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
This is worth calling out
|
|
174
|
+
This is worth calling out here because the database layer is not only "Knex plus migrations".
|
|
175
175
|
|
|
176
176
|
It also gives your later repositories and services a standard persistence toolbox so every package does not have to solve the same problems differently. The main point is consistency:
|
|
177
177
|
|
|
@@ -561,7 +561,7 @@ The three new scripts are also worth reading carefully:
|
|
|
561
561
|
|
|
562
562
|
They are not special JSKIT commands. They are ordinary project scripts, which makes them easy to run in any environment.
|
|
563
563
|
|
|
564
|
-
### `.env`
|
|
564
|
+
### `.env` owns the database connection settings
|
|
565
565
|
|
|
566
566
|
The package install also writes the database settings into `.env`:
|
|
567
567
|
|
|
@@ -583,7 +583,7 @@ This matters because the generic runtime is written to support more than one dri
|
|
|
583
583
|
|
|
584
584
|
### `knexfile.js` is for migration commands, not normal page code
|
|
585
585
|
|
|
586
|
-
The migration scripts in `package.json` work because the app
|
|
586
|
+
The migration scripts in `package.json` work because the app has a top-level `knexfile.js`:
|
|
587
587
|
|
|
588
588
|
```js
|
|
589
589
|
import path from "node:path";
|
|
@@ -628,7 +628,7 @@ It is not the main runtime API that your app code imports during a request. It i
|
|
|
628
628
|
npm run db:migrate
|
|
629
629
|
```
|
|
630
630
|
|
|
631
|
-
So there are really two separate database entry points
|
|
631
|
+
So there are really two separate database entry points:
|
|
632
632
|
|
|
633
633
|
- `knexfile.js` for migration commands
|
|
634
634
|
- the JSKIT server provider runtime for application code
|
|
@@ -695,7 +695,7 @@ At first glance it can feel strange that the database layer is installed but the
|
|
|
695
695
|
|
|
696
696
|
The reason is simple:
|
|
697
697
|
|
|
698
|
-
- the runtime is
|
|
698
|
+
- the runtime is available
|
|
699
699
|
- but almost no installed package is using it yet
|
|
700
700
|
|
|
701
701
|
Right now:
|
|
@@ -739,7 +739,7 @@ So the auth layer keeps behaving the same way it did before:
|
|
|
739
739
|
- JSKIT still mirrors just enough profile data locally
|
|
740
740
|
- that JSKIT-side mirror is still not persistent yet
|
|
741
741
|
|
|
742
|
-
The database runtime is
|
|
742
|
+
The database runtime is ready, but the users layer that will actually use it has not been installed yet.
|
|
743
743
|
|
|
744
744
|
### Why the empty `migrations/` directory is important
|
|
745
745
|
|
|
@@ -747,7 +747,7 @@ The new `migrations/` directory can look almost silly at first because it only c
|
|
|
747
747
|
|
|
748
748
|
It means:
|
|
749
749
|
|
|
750
|
-
- the app
|
|
750
|
+
- the app has a migration system
|
|
751
751
|
- the app does **not** yet have a schema of its own
|
|
752
752
|
|
|
753
753
|
That is exactly the right state at this stage of the guide.
|
|
@@ -758,9 +758,9 @@ The database runtime chapter should give the app a database foundation first. Th
|
|
|
758
758
|
|
|
759
759
|
This chapter did not make the app feel dramatically different in the browser, but it changed the server foundation in an important way.
|
|
760
760
|
|
|
761
|
-
- the app
|
|
762
|
-
- the app
|
|
763
|
-
- the app
|
|
761
|
+
- the app has a real JSKIT database runtime
|
|
762
|
+
- the app has a standard Knex migration workflow
|
|
763
|
+
- the app has a place for future schema files
|
|
764
764
|
|
|
765
765
|
But just as importantly, this chapter also defined what has **not** changed yet:
|
|
766
766
|
|
|
@@ -771,5 +771,5 @@ But just as importantly, this chapter also defined what has **not** changed yet:
|
|
|
771
771
|
So the right mental model at the end of this chapter is:
|
|
772
772
|
|
|
773
773
|
- Supabase already handles real authentication
|
|
774
|
-
- MySQL is
|
|
774
|
+
- MySQL is wired up and ready
|
|
775
775
|
- the persistent JSKIT-side user model arrives in the next chapter
|
|
@@ -14,7 +14,7 @@ npm install
|
|
|
14
14
|
|
|
15
15
|
The first command creates a new folder called `exampleapp` and fills it with JSKIT's base shell 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
16
|
|
|
17
|
-
If you are working with an AI agent and want the agent to drive the initial JSKIT setup conversation,
|
|
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
18
|
|
|
19
19
|
```bash
|
|
20
20
|
npx @jskit-ai/create-app exampleapp --template ai-seed
|
|
@@ -166,23 +166,35 @@ The most important parts look like this:
|
|
|
166
166
|
"build": "vite build",
|
|
167
167
|
"build:all": "vite build",
|
|
168
168
|
"build:home": "VITE_SURFACE=home vite build",
|
|
169
|
+
"preview": "vite preview",
|
|
170
|
+
"lint": "eslint .",
|
|
169
171
|
"test": "node --test",
|
|
170
172
|
"test:client": "vitest run tests/client",
|
|
173
|
+
"test:e2e": "playwright test tests/e2e",
|
|
171
174
|
"verify": "jskit app verify && npm run --if-present verify:app",
|
|
172
175
|
"release": "jskit app release",
|
|
173
176
|
"jskit:update": "jskit app update-packages"
|
|
174
177
|
},
|
|
175
178
|
"dependencies": {
|
|
176
179
|
"@local/main": "file:packages/main",
|
|
180
|
+
"@fastify/static": "^9.1.1",
|
|
177
181
|
"@jskit-ai/kernel": "0.x",
|
|
182
|
+
"@tanstack/vue-query": "^5.90.5",
|
|
178
183
|
"@jskit-ai/http-runtime": "0.x",
|
|
179
184
|
"fastify": "^5.7.4",
|
|
185
|
+
"json-rest-schema": "^1.0.13",
|
|
180
186
|
"pinia": "^3.0.4",
|
|
181
187
|
"vue": "^3.5.13",
|
|
188
|
+
"vue-router": "^5.0.4",
|
|
182
189
|
"vuetify": "^4.0.0"
|
|
183
190
|
},
|
|
184
191
|
"devDependencies": {
|
|
192
|
+
"@jskit-ai/agent-docs": "0.x",
|
|
193
|
+
"@jskit-ai/config-eslint": "0.x",
|
|
185
194
|
"@jskit-ai/jskit-cli": "0.x",
|
|
195
|
+
"@playwright/test": "^1.57.0",
|
|
196
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
197
|
+
"eslint": "^9.39.1",
|
|
186
198
|
"vite": "^6.1.0",
|
|
187
199
|
"vitest": "^4.0.18"
|
|
188
200
|
}
|
|
@@ -191,7 +203,7 @@ The most important parts look like this:
|
|
|
191
203
|
|
|
192
204
|
There are two details worth noticing immediately. 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.
|
|
193
205
|
|
|
194
|
-
`verify`, `jskit:update`, `devlinks`, and `release` are intentionally thin wrappers. They stay in `package.json` because they are convenient app-local shortcuts, but the real implementation
|
|
206
|
+
`verify`, `jskit:update`, `devlinks`, 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.
|
|
195
207
|
|
|
196
208
|
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`, `jskit app link-local-packages`, and `jskit app release`, the app keeps the nice `npm run` shortcuts while the maintained behavior stays in the installed CLI package.
|
|
197
209
|
|
|
@@ -257,6 +269,7 @@ The important part looks like this:
|
|
|
257
269
|
```js
|
|
258
270
|
import { createApp } from "vue";
|
|
259
271
|
import { createPinia } from "pinia";
|
|
272
|
+
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
|
|
260
273
|
import { createRouter, createWebHistory } from "vue-router/auto";
|
|
261
274
|
import { routes } from "vue-router/auto-routes";
|
|
262
275
|
import "vuetify/styles";
|
|
@@ -265,6 +278,10 @@ import * as components from "vuetify/components";
|
|
|
265
278
|
import * as directives from "vuetify/directives";
|
|
266
279
|
import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
|
|
267
280
|
import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
|
|
281
|
+
import {
|
|
282
|
+
shouldRetryTransientQueryFailure,
|
|
283
|
+
transientQueryRetryDelay
|
|
284
|
+
} from "@jskit-ai/kernel/shared/support";
|
|
268
285
|
import {
|
|
269
286
|
bootstrapClientShellApp,
|
|
270
287
|
createShellRouter
|
|
@@ -308,13 +325,28 @@ const vuetify = createVuetify({
|
|
|
308
325
|
}
|
|
309
326
|
});
|
|
310
327
|
const pinia = createPinia();
|
|
328
|
+
const queryClient = new QueryClient({
|
|
329
|
+
defaultOptions: {
|
|
330
|
+
queries: {
|
|
331
|
+
refetchOnWindowFocus: false,
|
|
332
|
+
refetchOnReconnect: true,
|
|
333
|
+
retry: shouldRetryTransientQueryFailure,
|
|
334
|
+
retryDelay: transientQueryRetryDelay
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
});
|
|
311
338
|
|
|
312
339
|
void bootstrapClientShellApp({
|
|
313
340
|
createApp,
|
|
314
341
|
rootComponent: App,
|
|
315
342
|
appConfig: config,
|
|
316
|
-
appPlugins: [
|
|
343
|
+
appPlugins: [
|
|
344
|
+
pinia,
|
|
345
|
+
[VueQueryPlugin, { queryClient }],
|
|
346
|
+
vuetify
|
|
347
|
+
],
|
|
317
348
|
pinia,
|
|
349
|
+
queryClient,
|
|
318
350
|
router,
|
|
319
351
|
bootClientModules: bootInstalledClientModules,
|
|
320
352
|
surfaceRuntime,
|
|
@@ -326,7 +358,7 @@ void bootstrapClientShellApp({
|
|
|
326
358
|
});
|
|
327
359
|
```
|
|
328
360
|
|
|
329
|
-
The flow is simple once you read it in order: config in, runtime in memory, router built from that runtime,
|
|
361
|
+
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.
|
|
330
362
|
|
|
331
363
|
<DocsInDepth title="In depth" preview-height="15rem">
|
|
332
364
|
|
|
@@ -766,7 +798,7 @@ That is why you saw `@jskit-ai/kernel` and `@jskit-ai/http-runtime` earlier in `
|
|
|
766
798
|
|
|
767
799
|
### Other files and options
|
|
768
800
|
|
|
769
|
-
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
|
|
801
|
+
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`, `devlinks`, and `release` are package-owned CLI commands rather than copied app scripts.
|
|
770
802
|
|
|
771
803
|
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. `--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.
|
|
772
804
|
|
|
@@ -63,7 +63,7 @@ npx jskit show @jskit-ai/workspaces-web --details
|
|
|
63
63
|
|
|
64
64
|
That output makes the package feel much less mysterious, because it shows the exact workspace shell contributions, settings outlets, client tokens, app-owned file writes, and capability requirements before you mutate the app.
|
|
65
65
|
|
|
66
|
-
## What
|
|
66
|
+
## What workspaces add
|
|
67
67
|
|
|
68
68
|
This chapter is where the app stops being a collection of global surfaces and starts supporting workspace-dependent ones.
|
|
69
69
|
|
|
@@ -107,13 +107,13 @@ That is why this chapter feels larger than the previous ones. It is not just add
|
|
|
107
107
|
|
|
108
108
|
### The shell and account surface gain workspace-aware controls
|
|
109
109
|
|
|
110
|
-
The placement registry
|
|
110
|
+
The placement registry gets a workspace selector in `shell.identity`, pending-invites and workspace tools in `shell.status`, and an `Invites` section in the existing `/account` settings screen through the account-settings extension seam that `users-web` exposes. The default shell topology maps identity/status placements to the visible shell chrome.
|
|
111
111
|
|
|
112
112
|
That means the shell itself starts adapting to workspace context.
|
|
113
113
|
|
|
114
|
-
- on any authenticated surface, the shell can
|
|
115
|
-
- signed-in users can
|
|
116
|
-
- on admin workspace surfaces, the shell can
|
|
114
|
+
- on any authenticated surface, the shell can expose a workspace selector
|
|
115
|
+
- signed-in users can see a pending-invites cue without `users-web` owning that workspace feature
|
|
116
|
+
- on admin workspace surfaces, the shell can expose workspace-specific tools and settings
|
|
117
117
|
|
|
118
118
|
This is the first time the guide shows the shell reacting not just to authentication state, but to workspace state as well.
|
|
119
119
|
|
|
@@ -139,7 +139,7 @@ This is also important to notice:
|
|
|
139
139
|
|
|
140
140
|
The new workspace surfaces are added on top of the existing app, not instead of it.
|
|
141
141
|
|
|
142
|
-
That is exactly what multi-homing should feel like. The app
|
|
142
|
+
That is exactly what multi-homing should feel like. The app has both:
|
|
143
143
|
|
|
144
144
|
- global surfaces
|
|
145
145
|
- workspace-scoped surfaces
|
|
@@ -153,7 +153,7 @@ npm run dev
|
|
|
153
153
|
npm run server
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
After you sign in, the app
|
|
156
|
+
After you sign in, the app has the routing structure needed for workspace-aware paths such as:
|
|
157
157
|
|
|
158
158
|
```text
|
|
159
159
|
/w/your-personal-slug
|
|
@@ -166,15 +166,15 @@ At this stage of the guide, the starter workspace pages are still intentionally
|
|
|
166
166
|
|
|
167
167
|
The most important new visible ideas are:
|
|
168
168
|
|
|
169
|
-
- a workspace slug
|
|
169
|
+
- a workspace slug appears in the route
|
|
170
170
|
- the shell can expose workspace selection and workspace tools
|
|
171
171
|
- workspace-dependent surfaces can show a dedicated unavailable-state card when the requested workspace cannot be resolved
|
|
172
172
|
|
|
173
|
-
First, even the
|
|
173
|
+
First, even the global `/home` surface reacts to workspace state. The selector and invites cue come from semantic placement entries and workspace bootstrap context, not from the `home` page itself.
|
|
174
174
|
|
|
175
|
-
Then open your personal workspace route. The page itself is deliberately light. `src/pages/w/[workspaceSlug]/index.vue` mostly exists to prove that the `app` surface is
|
|
175
|
+
Then open your personal workspace route. The page itself is deliberately light. `src/pages/w/[workspaceSlug]/index.vue` mostly exists to prove that the `app` surface is real and ready to host later modules.
|
|
176
176
|
|
|
177
|
-
Next open the admin surface for the same workspace. This is the matching pattern on the admin side: `src/pages/w/[workspaceSlug]/admin.vue` is the shell wrapper, `src/pages/w/[workspaceSlug]/admin/index.vue` is the starter page, and the
|
|
177
|
+
Next open the admin surface for the same workspace. This is the matching pattern on the admin side: `src/pages/w/[workspaceSlug]/admin.vue` is the shell wrapper, `src/pages/w/[workspaceSlug]/admin/index.vue` is the starter page, and the workspace tools button is coming from placements rather than being hand-built into that page file.
|
|
178
178
|
|
|
179
179
|
Finally open the nested workspace settings route. This is useful to inspect because it shows the container pattern most clearly. `src/pages/w/[workspaceSlug]/admin/workspace/settings.vue` owns the section frame and the child outlet, while the actual settings sub-pages can keep arriving later under that shell.
|
|
180
180
|
|
|
@@ -231,7 +231,7 @@ config.workspaceInvitations = {
|
|
|
231
231
|
};
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
Those lines are the public contract that tells both client and server
|
|
234
|
+
Those lines are the public contract that tells both client and server that this app is workspace-aware.
|
|
235
235
|
|
|
236
236
|
### `config/surfaceAccessPolicies.js` gains `workspace_member`
|
|
237
237
|
|
|
@@ -252,7 +252,7 @@ That is the core idea of multi-homing in JSKIT:
|
|
|
252
252
|
- the server resolves that workspace
|
|
253
253
|
- access depends on whether the current user belongs to it
|
|
254
254
|
|
|
255
|
-
### The migrations
|
|
255
|
+
### The migrations include workspace schema
|
|
256
256
|
|
|
257
257
|
After `workspaces-core`, the migration directory grows again with files such as:
|
|
258
258
|
|
|
@@ -269,7 +269,7 @@ That is why the chapter needs another `npm run db:migrate`. Without those tables
|
|
|
269
269
|
|
|
270
270
|
### The route tree gains workspace-dependent pages
|
|
271
271
|
|
|
272
|
-
The app
|
|
272
|
+
The app gets:
|
|
273
273
|
|
|
274
274
|
```text
|
|
275
275
|
src/pages/w/[workspaceSlug].vue
|
|
@@ -583,13 +583,13 @@ addPlacement({
|
|
|
583
583
|
});
|
|
584
584
|
```
|
|
585
585
|
|
|
586
|
-
That one block explains a lot of the
|
|
586
|
+
That one block explains a lot of the workspace shell behavior.
|
|
587
587
|
|
|
588
|
-
- the authenticated profile menu can
|
|
589
|
-
-
|
|
590
|
-
-
|
|
591
|
-
- the admin surface gets workspace tools
|
|
592
|
-
- the admin workspace settings menu is
|
|
588
|
+
- the authenticated profile menu can switch into workspace surfaces
|
|
589
|
+
- `shell.identity` carries the workspace selector
|
|
590
|
+
- `shell.status` can show a pending-invites cue
|
|
591
|
+
- the admin surface gets workspace tools through `shell.status`
|
|
592
|
+
- the admin workspace settings menu is another nested placement host with both `Settings` and `Members`
|
|
593
593
|
|
|
594
594
|
If you want to add your own app-owned page into that top cog menu, first ask JSKIT which semantic placements exist:
|
|
595
595
|
|
|
@@ -597,7 +597,7 @@ If you want to add your own app-owned page into that top cog menu, first ask JSK
|
|
|
597
597
|
npx jskit list-placements
|
|
598
598
|
```
|
|
599
599
|
|
|
600
|
-
In a workspace-enabled app, that list
|
|
600
|
+
In a workspace-enabled app, that list includes:
|
|
601
601
|
|
|
602
602
|
```text
|
|
603
603
|
- admin.tools-menu: Admin surface tools menu actions.
|
|
@@ -620,7 +620,7 @@ That command creates `src/pages/w/[workspaceSlug]/admin/catalogue/index.vue` and
|
|
|
620
620
|
|
|
621
621
|
You also do **not** need a renderer flag here. `admin.tools-menu` defines its link renderer in topology, so JSKIT resolves that when it renders the placement entry.
|
|
622
622
|
|
|
623
|
-
So the placement system from the shell chapter is
|
|
623
|
+
So the placement system from the shell chapter is doing the same job with a richer routing and tenancy context.
|
|
624
624
|
|
|
625
625
|
### The local client provider gets one more app-owned token
|
|
626
626
|
|
|
@@ -666,7 +666,7 @@ registerProfileSyncLifecycleContributor(app, "workspaces.core.profileSyncLifecyc
|
|
|
666
666
|
|
|
667
667
|
That means the workspace package does not need to patch auth directly to learn that a user was added. It listens through the `users-core` lifecycle registry instead.
|
|
668
668
|
|
|
669
|
-
For this chapter's `tenancyMode = "personal"` setup, that contributor
|
|
669
|
+
For this chapter's `tenancyMode = "personal"` setup, that contributor does real work. When an authenticated JSKIT user is synchronized from auth, `workspaces-core` ensures that user's personal workspace exists.
|
|
670
670
|
|
|
671
671
|
That detail matters for the retrofit path described earlier in this chapter. If the app started on `none`, then later switched to `personal`, the first sign-in after that switch still needs to backfill the personal workspace for the already-existing user record. The lifecycle contributor handles that because the workspace provision step is idempotent.
|
|
672
672
|
|
|
@@ -712,9 +712,9 @@ Before:
|
|
|
712
712
|
After:
|
|
713
713
|
|
|
714
714
|
- global surfaces still exist
|
|
715
|
-
- workspace-scoped surfaces
|
|
715
|
+
- workspace-scoped surfaces exist too
|
|
716
716
|
- the shell can navigate between workspaces
|
|
717
|
-
- access to some surfaces
|
|
717
|
+
- access to some surfaces depends on workspace membership
|
|
718
718
|
|
|
719
719
|
That is why multi-homing deserves its own chapter. It is not just another feature package. It is the moment the app becomes tenancy-aware.
|
|
720
720
|
|
|
@@ -722,11 +722,11 @@ That is why multi-homing deserves its own chapter. It is not just another featur
|
|
|
722
722
|
|
|
723
723
|
This chapter is the real routing and tenancy pivot in the guide.
|
|
724
724
|
|
|
725
|
-
- the app
|
|
725
|
+
- the app uses `tenancyMode = "personal"`
|
|
726
726
|
- `workspaces-core` added the persistent schema and server runtime for workspaces
|
|
727
727
|
- `workspaces-web` added the first workspace-scoped surfaces, shell controls, and the workspace-owned account invites extension
|
|
728
728
|
|
|
729
|
-
At the end of this chapter, the app
|
|
729
|
+
At the end of this chapter, the app has both:
|
|
730
730
|
|
|
731
731
|
- global surfaces such as `home`, `auth`, `account`, and `console`
|
|
732
732
|
- workspace-scoped surfaces such as `/w/[workspaceSlug]` and `/w/[workspaceSlug]/admin`
|
|
@@ -736,7 +736,7 @@ That is the most important mental shift to keep:
|
|
|
736
736
|
- earlier chapters added features inside one global app shell
|
|
737
737
|
- this chapter changed the topology of the app itself
|
|
738
738
|
|
|
739
|
-
From here on, later modules
|
|
739
|
+
From here on, later modules can add features inside either:
|
|
740
740
|
|
|
741
741
|
- the global surfaces
|
|
742
742
|
- the workspace-specific surfaces
|
|
@@ -42,7 +42,7 @@ This is the first chapter where the migration step is not just "nice to have."
|
|
|
42
42
|
- `AUTH_PROFILE_MODE=users` into `.env`
|
|
43
43
|
- real users/account schema migrations into `migrations/`
|
|
44
44
|
|
|
45
|
-
That means the app is
|
|
45
|
+
That means the app is expected to use the persistent users-backed profile sync service. If you skip `npm run db:migrate`, the code and routes are installed, but the required tables are still missing.
|
|
46
46
|
|
|
47
47
|
So the correct flow is:
|
|
48
48
|
|
|
@@ -57,24 +57,24 @@ Most of the time, step 3 is not needed because `jskit add package users-web` alr
|
|
|
57
57
|
- `jskit migrations changed` writes or refreshes JSKIT-managed migration files in `migrations/`
|
|
58
58
|
- `npm run db:migrate` actually applies pending migrations to MySQL
|
|
59
59
|
|
|
60
|
-
## What
|
|
60
|
+
## What `users-web` adds
|
|
61
61
|
|
|
62
62
|
This chapter is the real transition from "authentication exists" to "the app knows about users."
|
|
63
63
|
|
|
64
64
|
### Authentication becomes users-backed
|
|
65
65
|
|
|
66
|
-
In the database chapter, JSKIT
|
|
66
|
+
In the database chapter, JSKIT used the standalone in-memory profile mirror. After installing `users-web`, JSKIT expects to synchronize authenticated users into real JSKIT tables.
|
|
67
67
|
|
|
68
68
|
That is the biggest architectural change in this chapter.
|
|
69
69
|
|
|
70
70
|
- Supabase still owns the real auth identity and session
|
|
71
|
-
- JSKIT
|
|
71
|
+
- JSKIT owns a persistent users/account data model in MySQL
|
|
72
72
|
|
|
73
|
-
So after this chapter, a signed-in user is
|
|
73
|
+
So after this chapter, a signed-in user is not only "someone Supabase knows about." They are also a persistent JSKIT-side user with settings and profile state in the app database.
|
|
74
74
|
|
|
75
75
|
### The app gets an authenticated account surface
|
|
76
76
|
|
|
77
|
-
The app
|
|
77
|
+
The app has an authenticated surface at `/account`.
|
|
78
78
|
|
|
79
79
|
This is where the starter account settings UI lives. It already has real sections for:
|
|
80
80
|
|
|
@@ -84,17 +84,17 @@ This is where the starter account settings UI lives. It already has real section
|
|
|
84
84
|
|
|
85
85
|
Later chapters can extend this account screen with more sections. For example, the multi-homing chapter adds workspace invitation UI through `workspaces-web`, not through `users-web` itself.
|
|
86
86
|
|
|
87
|
-
The important point is that this is
|
|
87
|
+
The important point is that this is a real account route, not a placeholder. It is the first app-owned screen that assumes there is a persistent user model behind it.
|
|
88
88
|
|
|
89
89
|
### The shell changes for signed-in users
|
|
90
90
|
|
|
91
91
|
Once a user is signed in, the shell becomes noticeably richer.
|
|
92
92
|
|
|
93
93
|
- the profile menu gets a `Settings` entry that leads to `/account`
|
|
94
|
-
- the home surface gets a small users tools widget in
|
|
95
|
-
- the auth bootstrap payload
|
|
94
|
+
- the home surface gets a small users tools widget in `shell.status`
|
|
95
|
+
- the auth bootstrap payload includes persistent user settings instead of only the fallback mirror data
|
|
96
96
|
|
|
97
|
-
So this chapter is also the first one where logging in changes more than just "guest vs signed in." It
|
|
97
|
+
So this chapter is also the first one where logging in changes more than just "guest vs signed in." It changes what persistent user-facing surfaces the app can expose.
|
|
98
98
|
|
|
99
99
|
## What to look at in the browser
|
|
100
100
|
|
|
@@ -107,17 +107,17 @@ npm run server
|
|
|
107
107
|
|
|
108
108
|
Then sign in through `http://localhost:5173/auth/login`.
|
|
109
109
|
|
|
110
|
-
After a successful sign-in,
|
|
110
|
+
After a successful sign-in, check these concrete differences compared with the previous chapter:
|
|
111
111
|
|
|
112
|
-
- the profile menu
|
|
113
|
-
-
|
|
114
|
-
- `/account`
|
|
112
|
+
- the profile menu contains `Settings`
|
|
113
|
+
- `shell.status` includes the users tools widget
|
|
114
|
+
- `/account` exists and is authenticated
|
|
115
115
|
|
|
116
116
|
This is the first chapter where the app starts to feel like it has a real user model behind it.
|
|
117
117
|
|
|
118
118
|
## What `users-web` adds to the app
|
|
119
119
|
|
|
120
|
-
The most interesting files
|
|
120
|
+
The most interesting files are spread across config, migrations, routing, and the app-owned account UI.
|
|
121
121
|
|
|
122
122
|
### `.env` flips auth into users mode
|
|
123
123
|
|
|
@@ -239,18 +239,18 @@ src/components/account/settings/
|
|
|
239
239
|
|
|
240
240
|
Those three section components stay app-owned so you can reshape the actual UI freely.
|
|
241
241
|
|
|
242
|
-
The host itself
|
|
242
|
+
The host itself lives in `users-web` and resolves every account section through the semantic `settings.sections` placement with owner `account-settings`, including the default `profile`, `preferences`, and `notifications` entries.
|
|
243
243
|
|
|
244
244
|
So the screen follows the same rule as the rest of JSKIT UI: sections are added by placement rather than being hardcoded into an app-owned host component.
|
|
245
245
|
|
|
246
|
-
That is worth noticing because
|
|
246
|
+
That is worth noticing because this is a higher level of scaffolding:
|
|
247
247
|
|
|
248
248
|
- earlier chapters mostly introduced shells and routes
|
|
249
249
|
- this chapter introduces app-owned leaf section UI while the generic section host stays in the package
|
|
250
250
|
|
|
251
251
|
## Under the hood
|
|
252
252
|
|
|
253
|
-
### Why auth
|
|
253
|
+
### Why auth uses the users layer
|
|
254
254
|
|
|
255
255
|
In the previous chapter, auth still defaulted to the standalone profile sync fallback. The core logic in `AuthSupabaseServiceProvider` looks like this:
|
|
256
256
|
|
|
@@ -268,7 +268,7 @@ if (authProfileMode === AUTH_PROFILE_MODE_USERS) {
|
|
|
268
268
|
}
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
-
The important part is
|
|
271
|
+
The important part is concrete.
|
|
272
272
|
|
|
273
273
|
After `users-web`:
|
|
274
274
|
|
|
@@ -276,9 +276,9 @@ After `users-web`:
|
|
|
276
276
|
- `users-core` supplies the users-backed sync service
|
|
277
277
|
- the migrations supply the required tables
|
|
278
278
|
|
|
279
|
-
So auth
|
|
279
|
+
So auth has everything it needs to stop using the fallback mirror and start using the persistent users-backed one.
|
|
280
280
|
|
|
281
|
-
That is the true point of this chapter. The app is
|
|
281
|
+
That is the true point of this chapter. The app is not just authenticated. It has a real users layer.
|
|
282
282
|
|
|
283
283
|
### `users-core` also owns the profile-sync lifecycle registry
|
|
284
284
|
|
|
@@ -361,7 +361,7 @@ This chapter is where the app stopped treating signed-in people as only Supabase
|
|
|
361
361
|
|
|
362
362
|
That is why this chapter feels bigger than a normal page install. It changes both the browser experience and the server-side meaning of "a signed-in user."
|
|
363
363
|
|
|
364
|
-
At the end of this chapter, the app
|
|
364
|
+
At the end of this chapter, the app has:
|
|
365
365
|
|
|
366
366
|
- real JSKIT-side `users` and `user_settings` tables
|
|
367
367
|
- a real authenticated `/account` surface
|
|
@@ -71,14 +71,15 @@ In the current scaffold, those scripts are intentionally thin:
|
|
|
71
71
|
|
|
72
72
|
That is a deliberate design choice.
|
|
73
73
|
|
|
74
|
-
The app keeps the handy `npm run` names, but the real maintenance policy
|
|
74
|
+
The app keeps the handy `npm run` names, but the real maintenance policy lives in the installed CLI package instead of copied shell scripts inside the app. That means if JSKIT later changes how package updates, local linking, or baseline verification should work, apps can pick up the new behavior by updating `@jskit-ai/jskit-cli` instead of hand-editing frozen scaffold files.
|
|
75
75
|
|
|
76
76
|
This gives you a clean ownership split:
|
|
77
77
|
|
|
78
78
|
- app-owned scripts still describe how *this app* runs, builds, and tests
|
|
79
|
+
- `npm run devlinks` re-applies local checkout links after `npm install` when you are testing an app against this monorepo
|
|
79
80
|
- JSKIT-owned wrapper scripts delegate framework maintenance to `jskit app ...`
|
|
80
81
|
|
|
81
|
-
That split is worth keeping in mind through the rest of the guide. When you see `npm run verify`,
|
|
82
|
+
That split is worth keeping in mind through the rest of the guide. When you see `npm run verify`, read it as "run the app's JSKIT baseline verification policy, then any app-specific extra verification hook".
|
|
82
83
|
|
|
83
84
|
The starter scaffold also includes `.github/workflows/verify.yml`. That workflow is intentionally conservative: it runs `npm run verify` and does not assume that every app can stand up full browser, auth, seed-data, and database verification inside hosted CI.
|
|
84
85
|
|
|
@@ -505,7 +506,13 @@ npx jskit add package shell-web
|
|
|
505
506
|
npm install
|
|
506
507
|
```
|
|
507
508
|
|
|
508
|
-
`jskit add` changes the app. `npm install` downloads the dependencies that the changed app
|
|
509
|
+
`jskit add` changes the app. `npm install` downloads the dependencies that the changed app requires.
|
|
510
|
+
|
|
511
|
+
If the app is being tested against a local JSKIT checkout with linked packages, run the local-link wrapper again after every `npm install`:
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
npm run devlinks
|
|
515
|
+
```
|
|
509
516
|
|
|
510
517
|
### `add bundle`
|
|
511
518
|
|
|
@@ -700,7 +707,7 @@ That is a different job from:
|
|
|
700
707
|
|
|
701
708
|
Those commands can all pass while JSKIT-managed state is still inconsistent. `doctor` is the command that checks that JSKIT's own view of the app still makes sense.
|
|
702
709
|
|
|
703
|
-
That is exactly why the starter scaffold
|
|
710
|
+
That is exactly why the starter scaffold routes `npm run verify` through `jskit app verify`.
|
|
704
711
|
|
|
705
712
|
It belongs there because JSKIT apps are not only source trees. They also have:
|
|
706
713
|
|
|
@@ -732,7 +739,7 @@ Good times to run it manually include:
|
|
|
732
739
|
|
|
733
740
|
One important nuance: `doctor` is checking for broken JSKIT ownership and visibility, not trying to stop you from editing app-owned files. For example, a managed file that still exists but whose contents changed is normally fine. The problem is when JSKIT expects a managed file to exist and it is gone, or when the installed package state no longer resolves cleanly.
|
|
734
741
|
|
|
735
|
-
There is one intentional exception
|
|
742
|
+
There is one intentional exception for user-facing UI work.
|
|
736
743
|
|
|
737
744
|
If the current git working tree has changed UI files under the app's normal UI paths, `doctor` expects a matching `.jskit/verification/ui.json` receipt. The intended way to create that receipt is:
|
|
738
745
|
|
|
@@ -962,7 +969,7 @@ Good fits include:
|
|
|
962
969
|
- a custom integration package that talks to one external API
|
|
963
970
|
- app-specific auth or policy behavior
|
|
964
971
|
- a local runtime package that owns both client and server behavior for one feature area
|
|
965
|
-
- a package that starts small
|
|
972
|
+
- a package that starts small but may later grow providers, shared helpers, and routes
|
|
966
973
|
|
|
967
974
|
Use a generator when the problem is already understood well enough that JSKIT can scaffold the feature shape for you.
|
|
968
975
|
|