@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.
- package/guide/agent/app-extras/assistant.md +29 -605
- package/guide/agent/app-extras/mobile-capacitor.md +29 -362
- package/guide/agent/app-extras/realtime.md +29 -277
- package/guide/agent/app-setup/a-more-interesting-shell.md +44 -815
- package/guide/agent/app-setup/authentication.md +43 -1073
- package/guide/agent/app-setup/console.md +26 -298
- package/guide/agent/app-setup/database-layer.md +110 -790
- package/guide/agent/app-setup/existing-application-migration.md +273 -0
- package/guide/agent/app-setup/initial-scaffolding.md +50 -784
- package/guide/agent/app-setup/multi-homing.md +39 -712
- package/guide/agent/app-setup/quickstart.md +43 -179
- package/guide/agent/app-setup/users.md +34 -353
- package/guide/agent/index.md +17 -23
- package/package.json +2 -2
- package/patterns/INDEX.md +7 -7
- package/patterns/child-cruds.md +3 -3
- package/patterns/client-requests.md +6 -6
- package/patterns/crud-authoring.md +94 -0
- package/patterns/crud-links.md +1 -1
- package/patterns/feature-package/PATTERN.md +108 -0
- package/patterns/feature-package/example/booking-engine/package.json +48 -0
- package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
- package/patterns/feature-package/example/booking-engine/src/server/actions.js +26 -0
- package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
- package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
- package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
- package/patterns/feature-package/example/variations/customKnexRepository.js +34 -0
- package/patterns/feature-package/example/variations/orchestratorService.js +23 -0
- package/patterns/filters.md +8 -8
- package/patterns/live-actions.md +5 -18
- package/patterns/minimal-foundation/PATTERN.md +98 -0
- package/patterns/minimal-foundation/example/.nvmrc +1 -0
- package/patterns/minimal-foundation/example/AGENTS.md +17 -0
- package/patterns/minimal-foundation/example/Procfile +2 -0
- package/patterns/minimal-foundation/example/app.json +14 -0
- package/patterns/minimal-foundation/example/bin/develop.js +71 -0
- package/patterns/minimal-foundation/example/bin/server.js +8 -0
- package/patterns/minimal-foundation/example/config/public.js +40 -0
- package/patterns/minimal-foundation/example/config/server.js +1 -0
- package/patterns/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/minimal-foundation/example/eslint.config.mjs +19 -0
- package/patterns/minimal-foundation/example/favicon.svg +7 -0
- package/patterns/minimal-foundation/example/gitignore +9 -0
- package/patterns/minimal-foundation/example/index.html +13 -0
- package/patterns/minimal-foundation/example/jsconfig.json +8 -0
- package/patterns/minimal-foundation/example/package.json +57 -0
- package/patterns/minimal-foundation/example/packages/main/package.json +42 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/minimal-foundation/example/playwright.config.mjs +31 -0
- package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/minimal-foundation/example/server.js +195 -0
- package/patterns/minimal-foundation/example/src/App.vue +13 -0
- package/patterns/minimal-foundation/example/src/main.js +85 -0
- package/patterns/minimal-foundation/example/src/pages/home/index.vue +48 -0
- package/patterns/minimal-foundation/example/src/pages/home.vue +13 -0
- package/patterns/minimal-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/minimal-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/minimal-foundation/example/vite.config.mjs +81 -0
- package/patterns/page-scaffolding.md +20 -17
- package/patterns/placements.md +17 -15
- package/patterns/row-policies.md +4 -5
- package/patterns/server-search.md +3 -3
- package/patterns/shell-foundation/PATTERN.md +104 -0
- package/patterns/shell-foundation/example/.nvmrc +1 -0
- package/patterns/shell-foundation/example/AGENTS.md +17 -0
- package/patterns/shell-foundation/example/Procfile +2 -0
- package/patterns/shell-foundation/example/app.json +14 -0
- package/patterns/shell-foundation/example/bin/develop.js +71 -0
- package/patterns/shell-foundation/example/bin/server.js +8 -0
- package/patterns/shell-foundation/example/config/public.js +40 -0
- package/patterns/shell-foundation/example/config/server.js +1 -0
- package/patterns/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/shell-foundation/example/eslint.config.mjs +19 -0
- package/patterns/shell-foundation/example/favicon.svg +7 -0
- package/patterns/shell-foundation/example/gitignore +9 -0
- package/patterns/shell-foundation/example/index.html +13 -0
- package/patterns/shell-foundation/example/jsconfig.json +8 -0
- package/patterns/shell-foundation/example/package.json +59 -0
- package/patterns/shell-foundation/example/packages/main/package.json +56 -0
- package/patterns/shell-foundation/example/packages/main/src/client/index.js +9 -0
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +31 -0
- package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/shell-foundation/example/server.js +195 -0
- package/patterns/shell-foundation/example/src/App.vue +11 -0
- package/patterns/shell-foundation/example/src/components/ShellLayout.vue +12 -0
- package/patterns/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
- package/patterns/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/error.js +19 -0
- package/patterns/shell-foundation/example/src/main.js +85 -0
- package/patterns/shell-foundation/example/src/pages/home/index.vue +116 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +40 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
- package/patterns/shell-foundation/example/src/pages/home/settings.vue +109 -0
- package/patterns/shell-foundation/example/src/pages/home.vue +20 -0
- package/patterns/shell-foundation/example/src/placement.js +56 -0
- package/patterns/shell-foundation/example/src/placementTopology.js +149 -0
- package/patterns/shell-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
- package/patterns/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/shell-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/shell-foundation/example/vite.config.mjs +81 -0
- package/patterns/ui-contract.md +56 -0
- package/patterns/ui-testing.md +10 -12
- package/reference/autogen/KERNEL_MAP.md +29 -107
- package/reference/autogen/PATTERN_INDEX.md +230 -0
- package/reference/autogen/README.md +4 -8
- package/reference/autogen/packages/agent-docs.md +259 -0
- package/reference/autogen/packages/assistant-core.md +3 -3
- package/reference/autogen/packages/assistant-runtime.md +32 -17
- package/reference/autogen/packages/auth-core.md +31 -33
- package/reference/autogen/packages/auth-provider-local-core.md +4 -12
- package/reference/autogen/packages/auth-provider-local-db-core.md +4 -4
- package/reference/autogen/packages/auth-provider-supabase-core.md +14 -18
- package/reference/autogen/packages/auth-web.md +42 -22
- package/reference/autogen/packages/console-core.md +8 -25
- package/reference/autogen/packages/console-web.md +5 -5
- package/reference/autogen/packages/crud-core.md +61 -17
- package/reference/autogen/packages/database-runtime-mysql.md +12 -2
- package/reference/autogen/packages/database-runtime-postgres.md +12 -2
- package/reference/autogen/packages/database-runtime.md +26 -25
- package/reference/autogen/packages/google-rewarded-core.md +19 -104
- package/reference/autogen/packages/http-runtime.md +4 -8
- package/reference/autogen/packages/http-web.md +32 -0
- package/reference/autogen/packages/json-rest-api-core.md +4 -6
- package/reference/autogen/packages/kernel.md +109 -390
- package/reference/autogen/packages/mobile-capacitor.md +2 -13
- package/reference/autogen/packages/realtime.md +29 -26
- package/reference/autogen/packages/resource-crud-core.md +6 -0
- package/reference/autogen/packages/shell-web.md +69 -54
- package/reference/autogen/packages/storage-runtime.md +3 -3
- package/reference/autogen/packages/uploads-image-web.md +0 -1
- package/reference/autogen/packages/uploads-runtime.md +3 -3
- package/reference/autogen/packages/users-core.md +45 -90
- package/reference/autogen/packages/users-web.md +5 -7
- package/reference/autogen/packages/workspaces-core.md +53 -74
- package/reference/autogen/packages/workspaces-web.md +15 -16
- package/reference/autogen/tooling/jskit-catalog.md +34 -0
- package/reference/autogen/tooling/testUtils.md +4 -4
- package/skills/jskit/SKILL.md +38 -31
- package/skills/jskit/references/app-operations.md +68 -53
- package/skills/jskit/references/crud-operations.md +58 -106
- package/skills/jskit/references/material-3.md +1 -1
- package/skills/jskit/references/ui-operations.md +41 -44
- package/templates/app/AGENTS.md +7 -3
- package/guide/agent/app-setup/upgrade-beta-1-to-final.md +0 -252
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +0 -325
- package/guide/agent/generators/advanced-cruds.md +0 -1935
- package/guide/agent/generators/crud-generators.md +0 -948
- package/guide/agent/generators/intro.md +0 -65
- package/guide/agent/generators/row-policies.md +0 -537
- package/guide/agent/generators/ui-generators.md +0 -690
- package/patterns/crud-scaffolding.md +0 -198
- package/patterns/generated-ui-contract-tracking.md +0 -66
- package/reference/autogen/packages/assistant.md +0 -68
- package/reference/autogen/packages/crud-server-generator.md +0 -215
- package/reference/autogen/packages/crud-ui-generator.md +0 -192
- package/reference/autogen/packages/feature-server-generator.md +0 -65
- package/reference/autogen/packages/ui-generator.md +0 -127
- package/reference/autogen/tooling/create-app.md +0 -317
- package/reference/autogen/tooling/jskit-cli.md +0 -933
- package/reference/autogen/tooling/test-support.md +0 -27
|
@@ -2,205 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
title: Quickstart
|
|
5
|
-
description:
|
|
5
|
+
description: Create a real JSKIT app through an agent conversation, source patterns, and framework APIs.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Quickstart
|
|
9
9
|
|
|
10
|
-
This
|
|
10
|
+
This is the normal AI-first path to a JSKIT application.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- personal workspaces
|
|
15
|
-
- users
|
|
16
|
-
- console
|
|
17
|
-
- MySQL
|
|
18
|
-
- local auth with no external auth service
|
|
19
|
-
- one `admin` assistant configured from `console`
|
|
20
|
-
|
|
21
|
-
It also shows the first page-extension moves most apps need:
|
|
22
|
-
|
|
23
|
-
- add two workspace settings pages
|
|
24
|
-
- make one of them the default landing page
|
|
25
|
-
- add a page to the admin cog
|
|
26
|
-
- add a normal left-menu page
|
|
27
|
-
- inspect placement destinations and understand why some links are inferred automatically
|
|
28
|
-
|
|
29
|
-
## Step 1: Create the app
|
|
30
|
-
|
|
31
|
-
Set these values first:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
OPENAI_API_KEY=...
|
|
35
|
-
DB_HOST=127.0.0.1
|
|
36
|
-
DB_PORT=3306
|
|
37
|
-
DB_NAME=testapp
|
|
38
|
-
DB_USER=...
|
|
39
|
-
DB_PASSWORD=...
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Before continuing, make sure the MySQL database already exists and that the chosen `DB_USER` / `DB_PASSWORD` can connect to it. If the database does not exist yet, create it first or use a local MySQL account with enough privileges to create it before the runtime install step.
|
|
43
|
-
|
|
44
|
-
Then run the exact sequence below:
|
|
12
|
+
## Initialize the project conversation
|
|
45
13
|
|
|
46
14
|
```bash
|
|
47
|
-
|
|
48
|
-
cd
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
npx jskit add package auth-provider-local-core
|
|
52
|
-
npx jskit add package auth-web
|
|
53
|
-
|
|
54
|
-
npx jskit add package database-runtime-mysql \
|
|
55
|
-
--db-host "$DB_HOST" \
|
|
56
|
-
--db-port "$DB_PORT" \
|
|
57
|
-
--db-name "$DB_NAME" \
|
|
58
|
-
--db-user "$DB_USER" \
|
|
59
|
-
--db-password "$DB_PASSWORD"
|
|
60
|
-
|
|
61
|
-
npx jskit add package users-web
|
|
62
|
-
npx jskit add package console-web
|
|
63
|
-
npx jskit add package workspaces-core
|
|
64
|
-
npx jskit add package workspaces-web
|
|
65
|
-
|
|
66
|
-
npx jskit generate assistant setup \
|
|
67
|
-
--surface admin \
|
|
68
|
-
--settings-surface console \
|
|
69
|
-
--config-scope global \
|
|
70
|
-
--ai-provider openai \
|
|
71
|
-
--ai-api-key "$OPENAI_API_KEY"
|
|
72
|
-
|
|
73
|
-
npx jskit generate assistant page \
|
|
74
|
-
w/[workspaceSlug]/admin/assistant/index.vue \
|
|
75
|
-
--name "Assistant"
|
|
76
|
-
|
|
77
|
-
npx jskit generate assistant settings-page \
|
|
78
|
-
console/settings/admin-assistant/index.vue \
|
|
79
|
-
--surface admin \
|
|
80
|
-
--name "Admin Assistant"
|
|
81
|
-
|
|
82
|
-
npm run db:migrate
|
|
15
|
+
mkdir exampleapp
|
|
16
|
+
cd exampleapp
|
|
17
|
+
git init -b main
|
|
83
18
|
```
|
|
84
19
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
20
|
+
Ask your agent to read the installed JSKIT skill, then describe the product in
|
|
21
|
+
normal language. The opening conversation should make the product direction
|
|
22
|
+
and any material technology choice clear. Explicitly choose JSKIT and any
|
|
23
|
+
database integration you actually need. JSKIT does not ask the same questions
|
|
24
|
+
again through a framework questionnaire.
|
|
88
25
|
|
|
89
|
-
|
|
90
|
-
- an `admin` assistant at `/w/[workspaceSlug]/admin/assistant`
|
|
91
|
-
- an assistant settings page at `/console/settings/admin-assistant`
|
|
26
|
+
## Select an application foundation
|
|
92
27
|
|
|
93
|
-
|
|
28
|
+
The installed JSKIT skill and generated pattern index expose two initial
|
|
29
|
+
foundations:
|
|
94
30
|
|
|
95
|
-
|
|
31
|
+
- `app/shell-foundation` for the normal responsive application shell
|
|
32
|
+
- `app/minimal-foundation` for an intentionally smaller foundation
|
|
96
33
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
npx jskit generate ui-generator page \
|
|
103
|
-
w/[workspaceSlug]/admin/workspace/settings/branding/index.vue \
|
|
104
|
-
--name "Branding"
|
|
105
|
-
```
|
|
34
|
+
The agent reads `PATTERN.md` and its complete `example/` tree. It copies or
|
|
35
|
+
adapts the pattern into this existing repository, preserving `.git` and any
|
|
36
|
+
existing project/agent context. It resolves real file collisions and renames
|
|
37
|
+
the concrete `reading-room` example to match the product.
|
|
106
38
|
|
|
107
|
-
|
|
39
|
+
No `create-app` command, seed wrapper, force-overwrite flow, template
|
|
40
|
+
interpolation, or operation receipt is involved.
|
|
108
41
|
|
|
109
|
-
|
|
110
|
-
- `src/pages/w/[workspaceSlug]/admin/workspace/settings/branding/index.vue`
|
|
42
|
+
## Plan the first capability closure
|
|
111
43
|
|
|
112
|
-
|
|
44
|
+
Choose only what the first useful version needs. For example, an account-based
|
|
45
|
+
database product may need a local auth provider, auth UI, users, and one
|
|
46
|
+
database runtime. A public single-user tool may need none of those.
|
|
113
47
|
|
|
114
|
-
|
|
48
|
+
Inspect package details and patterns before modifying the package graph. Apply
|
|
49
|
+
one coherent dependency plan, then run `npm install` once. Environment values
|
|
50
|
+
come from the selected technology contract. They are not product questions and
|
|
51
|
+
must not be copied into committed source.
|
|
115
52
|
|
|
116
|
-
|
|
53
|
+
## Build the first product operation
|
|
117
54
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
and replace its contents with:
|
|
125
|
-
|
|
126
|
-
```vue
|
|
127
|
-
<script setup>
|
|
128
|
-
import { redirectToChild } from "@jskit-ai/kernel/client/pageRedirects";
|
|
129
|
-
|
|
130
|
-
definePage({
|
|
131
|
-
redirect: redirectToChild("billing")
|
|
132
|
-
});
|
|
133
|
-
</script>
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
That makes `/w/[workspaceSlug]/admin/workspace/settings` land on `/w/[workspaceSlug]/admin/workspace/settings/billing`.
|
|
137
|
-
|
|
138
|
-
Use this explicit redirect pattern instead of trying to infer the default child from placement order or “the first generated page”.
|
|
139
|
-
|
|
140
|
-
## Step 4: Add a page to the admin cog
|
|
141
|
-
|
|
142
|
-
First list the available placement destinations:
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npx jskit list-placements
|
|
146
|
-
```
|
|
55
|
+
Use the narrow relevant pattern and public APIs. For database CRUD, author an
|
|
56
|
+
immutable migration and a normal shared resource contract; use framework APIs
|
|
57
|
+
for standard mechanics and app code for product-specific behavior. Do not
|
|
58
|
+
invoke a source generator or reconstruct one as prompt options.
|
|
147
59
|
|
|
148
|
-
|
|
60
|
+
## Verify
|
|
149
61
|
|
|
150
|
-
|
|
62
|
+
Run focused tests throughout. Before handoff, run:
|
|
151
63
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
--link-placement admin.tools-menu
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
`--link-placement` is necessary here because this is just a normal `admin` page. It is not a child page under a local route host that already owns a nested outlet.
|
|
160
|
-
|
|
161
|
-
## Step 5: Add a normal left-menu page
|
|
162
|
-
|
|
163
|
-
Generate a normal `admin` page without an explicit placement:
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
npx jskit generate ui-generator page \
|
|
167
|
-
w/[workspaceSlug]/admin/reports/index.vue \
|
|
168
|
-
--name "Reports"
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Because this page is not under a more specific local host, JSKIT falls back to the app's default shell menu outlet. In practice, that means a normal left-menu entry.
|
|
172
|
-
|
|
173
|
-
## Step 6: Understand the placement "magic"
|
|
174
|
-
|
|
175
|
-
The workspace settings pages in Step 2 auto-linked into the settings menu for two reasons:
|
|
176
|
-
|
|
177
|
-
1. The parent host already exposes a concrete outlet:
|
|
178
|
-
|
|
179
|
-
```vue
|
|
180
|
-
<ShellOutlet target="admin-settings:primary-menu" />
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
2. Your generated pages live under that host route:
|
|
184
|
-
|
|
185
|
-
```text
|
|
186
|
-
w/[workspaceSlug]/admin/workspace/settings/...
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
So JSKIT can infer both:
|
|
190
|
-
|
|
191
|
-
- the semantic placement target: `page.section-nav`
|
|
192
|
-
- the placement owner: `admin-settings`
|
|
193
|
-
|
|
194
|
-
The renderer comes from `src/placementTopology.js`, where `page.section-nav` maps to the concrete `admin-settings:primary-menu` outlet for each layout class.
|
|
195
|
-
|
|
196
|
-
That is why the simple settings-page commands do not need `--link-placement`.
|
|
197
|
-
|
|
198
|
-
The admin cog example is different. `w/[workspaceSlug]/admin/catalogue/index.vue` is just a normal admin page, so there is no local nested host to infer. That is why you must pass `--link-placement admin.tools-menu` there.
|
|
199
|
-
|
|
200
|
-
If you want a little more context than the raw destination list, this is also useful:
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
npx jskit show @jskit-ai/workspaces-web --details
|
|
204
|
-
```
|
|
64
|
+
- lint and server/client tests
|
|
65
|
+
- the production build
|
|
66
|
+
- a fresh disposable database rebuild when persistence changed
|
|
67
|
+
- focused Playwright at compact, medium, and expanded sizes for UI work
|
|
205
68
|
|
|
206
|
-
|
|
69
|
+
The current source, dependency graph, migrations, tests, and behavior are the
|
|
70
|
+
evidence. A receipt saying that a tool ran is not evidence.
|
|
@@ -2,372 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
# Users
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
`users-web` sounds like a UI package, but it is actually the point where several layers arrive together:
|
|
10
|
-
|
|
11
|
-
- the persistent users/account data model from `users-core`
|
|
12
|
-
- the account surface and account settings UI
|
|
13
|
-
- the switch from provider-only auth profiles to users-backed auth profile projection
|
|
14
|
-
|
|
15
|
-
This is also the first chapter where the difference between "JSKIT wrote migration files into the app" and "Knex applied those files to the database" becomes important in practice.
|
|
16
|
-
|
|
17
|
-
## Installing `users-web`
|
|
18
|
-
|
|
19
|
-
From inside `exampleapp`, run:
|
|
5
|
+
Authentication proves an identity. `@jskit-ai/users-core` gives that identity a
|
|
6
|
+
persistent application profile and settings model; `@jskit-ai/users-web`
|
|
7
|
+
provides self-service account UI.
|
|
20
8
|
|
|
21
9
|
```bash
|
|
22
|
-
|
|
10
|
+
npm install @jskit-ai/users-web
|
|
23
11
|
npm run db:migrate
|
|
24
12
|
```
|
|
25
13
|
|
|
26
|
-
The
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- `users-core` arrives as a dependency and adds the persistent users/account server layer and schema migrations
|
|
30
|
-
|
|
31
|
-
`jskit add` installs those runtime packages and their dependencies. `npm run db:migrate` is the separate step that makes the new tables real in MySQL.
|
|
32
|
-
|
|
33
|
-
In the normal install flow, JSKIT materializes the managed `users-core` migration files while the package install is being applied. Then `npm run db:migrate` is what actually runs those files against MySQL.
|
|
34
|
-
|
|
35
|
-
**Important: Run The Migrations Before Testing Login**
|
|
36
|
-
|
|
37
|
-
This is the first chapter where the migration step is not just "nice to have."
|
|
38
|
-
|
|
39
|
-
`users-core` writes:
|
|
40
|
-
|
|
41
|
-
- the provider-neutral `auth.profile.projector` service binding through its runtime provider
|
|
42
|
-
- `config.auth.profileMode = "users"` into `config/server.js`
|
|
43
|
-
- real users/account schema migrations into `migrations/`
|
|
44
|
-
|
|
45
|
-
That means the app is expected to project authenticated identities into 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
|
-
|
|
47
|
-
So the correct flow is:
|
|
48
|
-
|
|
49
|
-
1. add `users-web`
|
|
50
|
-
2. run `npx jskit migrations sync --check`
|
|
51
|
-
3. run `npm run db:migrate`
|
|
52
|
-
4. only then start the app and sign in
|
|
53
|
-
|
|
54
|
-
`jskit add package users-web` already synchronizes package migration files. The distinction still matters:
|
|
55
|
-
|
|
56
|
-
- `jskit migrations sync` writes missing immutable package migration files in `migrations/`
|
|
57
|
-
- `npm run db:migrate` actually applies pending migrations to MySQL
|
|
58
|
-
|
|
59
|
-
## What `users-web` adds
|
|
60
|
-
|
|
61
|
-
This chapter is the real transition from "authentication exists" to "the app knows about users."
|
|
62
|
-
|
|
63
|
-
### Authentication becomes users-backed
|
|
64
|
-
|
|
65
|
-
In the database chapter, JSKIT still treated the signed-in person as an auth-provider identity. After installing `users-web`, JSKIT expects to synchronize authenticated users into real JSKIT tables.
|
|
66
|
-
|
|
67
|
-
That is the biggest architectural change in this chapter.
|
|
68
|
-
|
|
69
|
-
- the selected auth provider still owns the auth identity and session
|
|
70
|
-
- JSKIT owns a persistent users/account data model in MySQL
|
|
71
|
-
|
|
72
|
-
So after this chapter, a signed-in user is not only "someone the auth provider knows about." They are also a persistent JSKIT-side user with settings and profile state in the app database.
|
|
73
|
-
|
|
74
|
-
### The app gets an authenticated account surface
|
|
75
|
-
|
|
76
|
-
The app has an authenticated surface at `/account`.
|
|
77
|
-
|
|
78
|
-
This is where the starter account settings UI lives. It already has real sections for:
|
|
79
|
-
|
|
80
|
-
- profile
|
|
81
|
-
- preferences
|
|
82
|
-
- notifications
|
|
83
|
-
|
|
84
|
-
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.
|
|
85
|
-
|
|
86
|
-
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.
|
|
87
|
-
|
|
88
|
-
### The shell changes for signed-in users
|
|
89
|
-
|
|
90
|
-
Once a user is signed in, the shell becomes noticeably richer.
|
|
91
|
-
|
|
92
|
-
- the profile menu gets a `Settings` entry that leads to `/account`
|
|
93
|
-
- the home surface gets a small users tools widget in `shell.status`
|
|
94
|
-
- the auth bootstrap payload includes persistent user settings instead of only the fallback mirror data
|
|
95
|
-
|
|
96
|
-
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.
|
|
97
|
-
|
|
98
|
-
## What to look at in the browser
|
|
99
|
-
|
|
100
|
-
Start both processes again:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npm run dev
|
|
104
|
-
npm run server
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Then sign in through `http://localhost:5173/auth/login`.
|
|
108
|
-
|
|
109
|
-
After a successful sign-in, check these concrete differences compared with the previous chapter:
|
|
110
|
-
|
|
111
|
-
- the profile menu contains `Settings`
|
|
112
|
-
- `shell.status` includes the users tools widget
|
|
113
|
-
- `/account` exists and is authenticated
|
|
114
|
-
|
|
115
|
-
This is the first chapter where the app starts to feel like it has a real user model behind it.
|
|
116
|
-
|
|
117
|
-
## What `users-web` adds to the app
|
|
118
|
-
|
|
119
|
-
The most interesting files are spread across config, migrations, routing, and the app-owned account UI.
|
|
120
|
-
|
|
121
|
-
### `users-core` projects auth identities into app users
|
|
122
|
-
|
|
123
|
-
The most important server-side change is the provider-neutral projector binding from `users-core`:
|
|
124
|
-
|
|
125
|
-
```js
|
|
126
|
-
if (!app.has("auth.profile.projector")) {
|
|
127
|
-
app.singleton("auth.profile.projector", (scope) => scope.make("users.profile.sync.service"));
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
That one binding explains the deepest change in the chapter.
|
|
132
|
-
|
|
133
|
-
Before this chapter, auth could authenticate a user without creating a persistent app-owned user row. After this chapter, auth providers can call `auth.profile.projector.syncIdentityProfile(...)` and get back a persistent users-backed profile.
|
|
134
|
-
|
|
135
|
-
`users-core` also selects users-backed Supabase profile projection:
|
|
136
|
-
|
|
137
|
-
```js
|
|
138
|
-
config.auth ||= {};
|
|
139
|
-
config.auth.profileMode = "users";
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The local provider uses the token directly. The Supabase provider also understands `config.auth.profileMode = "users"` and resolves the same users-backed sync service. In both cases, this only works because `users-core` installs the required repositories, services, and tables.
|
|
143
|
-
|
|
144
|
-
### `migrations/` stops being mostly empty
|
|
145
|
-
|
|
146
|
-
After `users-web`, the app gets real schema files such as:
|
|
147
|
-
|
|
148
|
-
```text
|
|
149
|
-
migrations/
|
|
150
|
-
2026..._users-core-generic-initial-schema.cjs
|
|
151
|
-
2026..._users-core-profile-username-schema.cjs
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
These files are the first real database schema in the guide.
|
|
155
|
-
|
|
156
|
-
The important initial migration creates:
|
|
157
|
-
|
|
158
|
-
- `users`
|
|
159
|
-
- `user_settings`
|
|
160
|
-
|
|
161
|
-
That is why this chapter needs `npm run db:migrate` in a much more serious way than the previous one did.
|
|
162
|
-
|
|
163
|
-
### `config/public.js` gains one new authenticated surface
|
|
164
|
-
|
|
165
|
-
After the install, `config/public.js` grows one important surface definition:
|
|
166
|
-
|
|
167
|
-
```js
|
|
168
|
-
config.surfaceDefinitions.account = {
|
|
169
|
-
id: "account",
|
|
170
|
-
label: "Account",
|
|
171
|
-
pagesRoot: "account",
|
|
172
|
-
enabled: true,
|
|
173
|
-
requiresAuth: true,
|
|
174
|
-
requiresWorkspace: false,
|
|
175
|
-
origin: ""
|
|
176
|
-
};
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
This chapter keeps the split simple:
|
|
180
|
-
|
|
181
|
-
- `account` is the normal authenticated user area
|
|
182
|
-
- operator surfaces such as `console` are introduced later, by packages that actually own them
|
|
183
|
-
|
|
184
|
-
### `src/placement.js` grows account entries
|
|
185
|
-
|
|
186
|
-
The placement registry also becomes more interesting:
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
addPlacement({
|
|
190
|
-
id: "users.profile.menu.settings",
|
|
191
|
-
target: "auth.profile-menu",
|
|
192
|
-
kind: "link",
|
|
193
|
-
surfaces: ["*"],
|
|
194
|
-
order: 500,
|
|
195
|
-
props: {
|
|
196
|
-
label: "Settings",
|
|
197
|
-
to: "/account"
|
|
198
|
-
},
|
|
199
|
-
when: ({ auth }) => Boolean(auth?.authenticated)
|
|
200
|
-
});
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
This chapter is the first one where one package install adds meaningful authenticated shell entries and a real account surface.
|
|
204
|
-
|
|
205
|
-
### `src/pages/account/index.vue` is a real authenticated route
|
|
206
|
-
|
|
207
|
-
The account route itself is very small:
|
|
208
|
-
|
|
209
|
-
```vue
|
|
210
|
-
<route lang="json">
|
|
211
|
-
{
|
|
212
|
-
"meta": {
|
|
213
|
-
"guard": {
|
|
214
|
-
"policy": "authenticated"
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
</route>
|
|
219
|
-
|
|
220
|
-
<template>
|
|
221
|
-
<AccountSettingsClientElement />
|
|
222
|
-
</template>
|
|
223
|
-
|
|
224
|
-
<script setup>
|
|
225
|
-
import AccountSettingsClientElement from "@jskit-ai/users-web/client/components/AccountSettingsClientElement";
|
|
226
|
-
</script>
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
That is a very JSKIT-style file.
|
|
230
|
-
|
|
231
|
-
- the route policy is app-owned
|
|
232
|
-
- the page wrapper is app-owned
|
|
233
|
-
- the heavy UI is delegated to a package-owned reusable client element
|
|
234
|
-
|
|
235
|
-
So the route is simple, but it is already a real authenticated account screen rather than a placeholder card.
|
|
236
|
-
|
|
237
|
-
### The account screen itself is scaffolded app-owned UI
|
|
238
|
-
|
|
239
|
-
The account page is backed by:
|
|
240
|
-
|
|
241
|
-
```text
|
|
242
|
-
src/components/account/settings/
|
|
243
|
-
AccountSettingsProfileSection.vue
|
|
244
|
-
AccountSettingsPreferencesSection.vue
|
|
245
|
-
AccountSettingsNotificationsSection.vue
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
Those three section components stay app-owned so you can reshape the actual UI freely.
|
|
249
|
-
|
|
250
|
-
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.
|
|
251
|
-
|
|
252
|
-
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.
|
|
253
|
-
|
|
254
|
-
That is worth noticing because this is a higher level of scaffolding:
|
|
255
|
-
|
|
256
|
-
- earlier chapters mostly introduced shells and routes
|
|
257
|
-
- this chapter introduces app-owned leaf section UI while the generic section host stays in the package
|
|
258
|
-
|
|
259
|
-
## Under the hood
|
|
260
|
-
|
|
261
|
-
### Why auth uses the users layer
|
|
262
|
-
|
|
263
|
-
In the previous chapter, auth did not have a users-backed projector. The core logic in `registerUsersCore()` looks like this:
|
|
264
|
-
|
|
265
|
-
```js
|
|
266
|
-
app.singleton("users.profile.sync.service", (scope) => {
|
|
267
|
-
return createAuthProfileSyncService({
|
|
268
|
-
userProfilesRepository: scope.make("internal.repository.user-profiles"),
|
|
269
|
-
userSettingsRepository: scope.make("internal.repository.user-settings"),
|
|
270
|
-
lifecycleContributors: resolveProfileSyncLifecycleContributors(scope)
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
if (!app.has("auth.profile.projector")) {
|
|
275
|
-
app.singleton("auth.profile.projector", (scope) => scope.make("users.profile.sync.service"));
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
The important part is concrete.
|
|
280
|
-
|
|
281
|
-
After `users-web`:
|
|
282
|
-
|
|
283
|
-
- `users-core` supplies `users.profile.sync.service`
|
|
284
|
-
- `users-core` aliases that service as `auth.profile.projector`
|
|
285
|
-
- the migrations supply the required tables
|
|
286
|
-
|
|
287
|
-
So auth has everything it needs to stop returning only provider-owned profile data and start using the persistent users-backed profile.
|
|
288
|
-
|
|
289
|
-
That is the true point of this chapter. The app is not just authenticated. It has a real users layer.
|
|
290
|
-
|
|
291
|
-
### `users-core` also owns the profile-sync lifecycle registry
|
|
292
|
-
|
|
293
|
-
There is one more seam worth noticing here because later packages depend on it.
|
|
294
|
-
|
|
295
|
-
The important thing to understand is that the public extension API is:
|
|
296
|
-
|
|
297
|
-
```js
|
|
298
|
-
registerProfileSyncLifecycleContributor(...)
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
That is the function another server package uses when it wants to run logic after JSKIT has created or synchronized a user record.
|
|
302
|
-
|
|
303
|
-
If you were writing another server package and wanted to run some logic every time a JSKIT user is synchronized, you would register a contributor during package boot:
|
|
304
|
-
|
|
305
|
-
```js
|
|
306
|
-
import { registerProfileSyncLifecycleContributor } from "@jskit-ai/users-core/server/profileSyncLifecycleContributorRegistry";
|
|
307
|
-
|
|
308
|
-
function registerExampleCore(app) {
|
|
309
|
-
registerProfileSyncLifecycleContributor(app, "example.core.profileSyncLogger", () => {
|
|
310
|
-
return {
|
|
311
|
-
contributorId: "example.core.profileSyncLogger",
|
|
312
|
-
order: 0,
|
|
313
|
-
async afterIdentityProfileSynced({ profile, created } = {}) {
|
|
314
|
-
if (!profile) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (created) {
|
|
319
|
-
console.log("Created JSKIT user:", profile.id, profile.email);
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
console.log("Synchronized existing JSKIT user:", profile.id, profile.email);
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
That example is deliberately simple, but it shows the real usage pattern:
|
|
331
|
-
|
|
332
|
-
- import `registerProfileSyncLifecycleContributor(...)`
|
|
333
|
-
- call it from your package's server registration code
|
|
334
|
-
- implement `afterIdentityProfileSynced(...)`
|
|
335
|
-
- use `created` to tell "brand-new user" apart from "existing user synchronized again"
|
|
336
|
-
|
|
337
|
-
In a real package, the same seam is useful for things like:
|
|
338
|
-
|
|
339
|
-
- provisioning related rows when a user is created
|
|
340
|
-
- seeding package-owned settings
|
|
341
|
-
- writing audit events
|
|
342
|
-
- attaching app-owned resources to the new user
|
|
343
|
-
|
|
344
|
-
This runs on the server, inside the same overall sync flow. So if your contributor throws, the sync fails too. That is intentional: the seam is for real lifecycle work, not best-effort UI decoration.
|
|
14
|
+
The installed graph supplies the required users runtime, storage, uploads,
|
|
15
|
+
database, HTTP, and auth integration. The application must already have one
|
|
16
|
+
database driver and one auth provider selected.
|
|
345
17
|
|
|
346
|
-
|
|
18
|
+
Package-owned user migrations are discovered directly from the installed
|
|
19
|
+
graph. `npm run db:migrate` is the application-owned Knex command that applies
|
|
20
|
+
them.
|
|
347
21
|
|
|
348
|
-
|
|
349
|
-
- auth still only calls one service: `users.profile.sync.service`
|
|
350
|
-
- other packages extend the post-sync lifecycle by registering contributors
|
|
22
|
+
## Self-service accounts
|
|
351
23
|
|
|
352
|
-
|
|
24
|
+
Use the `users/account-settings` pattern for profile, preference, and
|
|
25
|
+
notification sections. Decide which fields are editable and which operations
|
|
26
|
+
require reauthentication.
|
|
353
27
|
|
|
354
|
-
|
|
355
|
-
|
|
28
|
+
Use the framework account settings elements and command composables rather than
|
|
29
|
+
new page-local request clients. Cached query data must hydrate editable fields
|
|
30
|
+
immediately; clearing a value must send the clear operation rather than omit the
|
|
31
|
+
field.
|
|
356
32
|
|
|
357
|
-
|
|
33
|
+
Loading uses layout-stable skeletons. Mutation errors use a toast, not an
|
|
34
|
+
in-page banner that moves the form.
|
|
358
35
|
|
|
359
|
-
|
|
36
|
+
## User administration
|
|
360
37
|
|
|
361
|
-
|
|
362
|
-
-
|
|
363
|
-
|
|
38
|
+
Use `users/user-administration-server` only when the product needs global or
|
|
39
|
+
workspace-scoped administration beyond self-service settings. It demonstrates
|
|
40
|
+
an app-owned feature package built with `defineCrudJsonApiFeature()` while
|
|
41
|
+
keeping profile persistence in `users-core`.
|
|
364
42
|
|
|
365
|
-
|
|
43
|
+
Do not expose password hashes, provider credentials, or session data through a
|
|
44
|
+
user resource. Apply permission and workspace scope before repository access so
|
|
45
|
+
record existence is not leaked.
|
|
366
46
|
|
|
367
|
-
|
|
47
|
+
## Verification
|
|
368
48
|
|
|
369
|
-
|
|
370
|
-
-
|
|
371
|
-
|
|
49
|
+
Test profile creation from an authenticated identity, read/update/clear flows,
|
|
50
|
+
warm-cache navigation, uploads when enabled, forbidden administration, missing
|
|
51
|
+
records, pagination, and workspace isolation where relevant.
|
|
372
52
|
|
|
373
|
-
|
|
53
|
+
Do not copy users-core tables or services into the application. Do not add
|
|
54
|
+
field questionnaires, generated-file markers, migration copies, or receipts.
|