@jskit-ai/agent-docs 0.1.26 → 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 +9 -20
- package/guide/agent/app-extras/mobile-capacitor.md +381 -0
- package/guide/agent/app-extras/realtime.md +13 -12
- package/guide/agent/app-setup/a-more-interesting-shell.md +102 -87
- package/guide/agent/app-setup/authentication.md +50 -31
- package/guide/agent/app-setup/console.md +8 -8
- 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 +50 -47
- package/guide/agent/app-setup/quickstart.md +11 -14
- package/guide/agent/app-setup/users.md +24 -24
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +18 -10
- 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 +44 -36
- package/guide/agent/index.md +3 -2
- 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 +20 -2
- package/patterns/placements.md +67 -1
- package/patterns/surfaces.md +5 -1
- package/patterns/ui-testing.md +3 -0
- package/reference/autogen/KERNEL_MAP.md +46 -1
- package/reference/autogen/README.md +3 -0
- package/reference/autogen/packages/assistant.md +2 -1
- package/reference/autogen/packages/auth-web.md +20 -1
- package/reference/autogen/packages/crud-ui-generator.md +20 -8
- package/reference/autogen/packages/google-rewarded-core.md +222 -0
- package/reference/autogen/packages/google-rewarded-web.md +71 -0
- package/reference/autogen/packages/kernel.md +94 -10
- package/reference/autogen/packages/mobile-capacitor.md +76 -0
- package/reference/autogen/packages/realtime.md +1 -0
- package/reference/autogen/packages/shell-web.md +48 -3
- package/reference/autogen/packages/ui-generator.md +34 -2
- package/reference/autogen/packages/users-core.md +2 -0
- package/reference/autogen/packages/users-web.md +92 -4
- package/reference/autogen/packages/workspaces-web.md +3 -2
- package/reference/autogen/tooling/jskit-cli.md +114 -9
- package/templates/app/AGENTS.md +1 -0
- package/workflow/bootstrap.md +1 -0
|
@@ -83,7 +83,7 @@ npm run server
|
|
|
83
83
|
|
|
84
84
|
Then open `http://localhost:5173/auth/login` in the browser.
|
|
85
85
|
|
|
86
|
-
The login page is
|
|
86
|
+
The login page is real, and it already contains several different auth modes behind the same card.
|
|
87
87
|
|
|
88
88
|
- **Sign in** is the normal email-and-password flow.
|
|
89
89
|
- **Register** creates a new Supabase auth user.
|
|
@@ -91,7 +91,7 @@ The login page is now real, and it already contains several different auth modes
|
|
|
91
91
|
- **Use one-time code** switches to an email OTP login flow.
|
|
92
92
|
- **Remember this account on this device** stores a small local hint in browser storage so the next visit can greet the last-used account and let the user keep that email preselected.
|
|
93
93
|
|
|
94
|
-
If you go back to `http://localhost:5173/home`, the shell also has a small auth widget in the
|
|
94
|
+
If you go back to `http://localhost:5173/home`, the shell also has a small auth widget in the status area. When you are signed out it shows a guest state and a menu entry that leads to `/auth/login`. After you sign in, the same placement changes to a sign-out menu.
|
|
95
95
|
|
|
96
96
|
## Reading the screen carefully
|
|
97
97
|
|
|
@@ -165,7 +165,7 @@ JSKIT asks Supabase to send OTP login emails only for existing users. In other w
|
|
|
165
165
|
|
|
166
166
|
That endpoint asks Supabase to send a password reset email. In this chapter's scaffold, this mode is only the **request** step.
|
|
167
167
|
|
|
168
|
-
The backend already supports the later recovery endpoints too, but the chapter's simple app does **not** yet scaffold a dedicated app-owned `reset-password` page. So at this stage, the guide should be read as: the screen can request recovery emails
|
|
168
|
+
The backend already supports the later recovery endpoints too, but the chapter's simple app does **not** yet scaffold a dedicated app-owned `reset-password` page. So at this stage, the guide should be read as: the screen can request recovery emails, while the full browser-side password-reset completion UI is still something you would add explicitly in the app.
|
|
169
169
|
|
|
170
170
|
### Remembered account behavior
|
|
171
171
|
|
|
@@ -184,7 +184,7 @@ On the next visit, the card can show a `Welcome back, ...` panel and a `Use anot
|
|
|
184
184
|
|
|
185
185
|
The screen is also ready to show OAuth provider buttons, but only if providers are configured.
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
For this chapter, `config.server.js` keeps this empty:
|
|
188
188
|
|
|
189
189
|
```js
|
|
190
190
|
config.auth = {
|
|
@@ -319,10 +319,10 @@ The placement entry is just a normal shell link:
|
|
|
319
319
|
```js
|
|
320
320
|
addPlacement({
|
|
321
321
|
id: "ui-generator.page.home.reports.link",
|
|
322
|
-
target: "shell
|
|
322
|
+
target: "shell.primary-nav",
|
|
323
|
+
kind: "link",
|
|
323
324
|
surfaces: ["home"],
|
|
324
325
|
order: 155,
|
|
325
|
-
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
326
326
|
props: {
|
|
327
327
|
label: "Reports",
|
|
328
328
|
surface: "home",
|
|
@@ -332,13 +332,22 @@ addPlacement({
|
|
|
332
332
|
});
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
And the page file itself is a normal
|
|
335
|
+
And the page file itself is a normal generated empty-state screen:
|
|
336
336
|
|
|
337
337
|
```vue
|
|
338
338
|
<template>
|
|
339
|
-
<section class="
|
|
340
|
-
<
|
|
341
|
-
|
|
339
|
+
<section class="generated-page-screen d-flex flex-column ga-4">
|
|
340
|
+
<header>
|
|
341
|
+
<p class="text-overline text-medium-emphasis mb-1">Screen</p>
|
|
342
|
+
<h1 class="generated-page-screen__title">Reports</h1>
|
|
343
|
+
</header>
|
|
344
|
+
|
|
345
|
+
<v-sheet rounded="lg" border class="generated-page-screen__empty-state">
|
|
346
|
+
<h2 class="text-h6 mb-2">No Reports activity yet</h2>
|
|
347
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
348
|
+
Activity and actions for this screen will appear here.
|
|
349
|
+
</p>
|
|
350
|
+
</v-sheet>
|
|
342
351
|
</section>
|
|
343
352
|
</template>
|
|
344
353
|
```
|
|
@@ -364,9 +373,18 @@ To make the route require login, add a route guard block to `src/pages/home/repo
|
|
|
364
373
|
</route>
|
|
365
374
|
|
|
366
375
|
<template>
|
|
367
|
-
<section class="
|
|
368
|
-
<
|
|
369
|
-
|
|
376
|
+
<section class="generated-page-screen d-flex flex-column ga-4">
|
|
377
|
+
<header>
|
|
378
|
+
<p class="text-overline text-medium-emphasis mb-1">Screen</p>
|
|
379
|
+
<h1 class="generated-page-screen__title">Reports</h1>
|
|
380
|
+
</header>
|
|
381
|
+
|
|
382
|
+
<v-sheet rounded="lg" border class="generated-page-screen__empty-state">
|
|
383
|
+
<h2 class="text-h6 mb-2">No Reports activity yet</h2>
|
|
384
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
385
|
+
Activity and actions for this screen will appear here.
|
|
386
|
+
</p>
|
|
387
|
+
</v-sheet>
|
|
370
388
|
</section>
|
|
371
389
|
</template>
|
|
372
390
|
```
|
|
@@ -383,17 +401,17 @@ So after login, JSKIT can send the user back to the page they originally asked f
|
|
|
383
401
|
|
|
384
402
|
### Hide the menu entry when signed out
|
|
385
403
|
|
|
386
|
-
The route is
|
|
404
|
+
The route is protected, but the drawer link is still visible. That is expected. Route protection and shell visibility are separate concerns.
|
|
387
405
|
|
|
388
406
|
To hide the `Reports` menu entry until the user is logged in, update the placement entry in `src/placement.js`:
|
|
389
407
|
|
|
390
408
|
```js
|
|
391
409
|
addPlacement({
|
|
392
410
|
id: "ui-generator.page.home.reports.link",
|
|
393
|
-
target: "shell
|
|
411
|
+
target: "shell.primary-nav",
|
|
412
|
+
kind: "link",
|
|
394
413
|
surfaces: ["home"],
|
|
395
414
|
order: 155,
|
|
396
|
-
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
397
415
|
props: {
|
|
398
416
|
label: "Reports",
|
|
399
417
|
surface: "home",
|
|
@@ -407,7 +425,7 @@ addPlacement({
|
|
|
407
425
|
|
|
408
426
|
The only new part is the `when(...)` line. That predicate is evaluated by the shell placement runtime using the auth context that `auth-web` injects from `/api/session`.
|
|
409
427
|
|
|
410
|
-
So the behavior
|
|
428
|
+
So the behavior becomes:
|
|
411
429
|
|
|
412
430
|
- signed out:
|
|
413
431
|
- the `Reports` drawer entry disappears
|
|
@@ -440,7 +458,7 @@ const auth = useAuthStore();
|
|
|
440
458
|
</v-alert>
|
|
441
459
|
|
|
442
460
|
<h1 class="text-h5 mb-2">Home</h1>
|
|
443
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
461
|
+
<p class="text-body-2 text-medium-emphasis">Your account session is active on this device.</p>
|
|
444
462
|
</section>
|
|
445
463
|
</template>
|
|
446
464
|
```
|
|
@@ -501,7 +519,7 @@ At this point the guide has shown three distinct layers of client state:
|
|
|
501
519
|
- `shell-web` adds shell-facing stores such as `useShellLayoutStore()`
|
|
502
520
|
- `auth-web` adds `useAuthStore()` for authentication state
|
|
503
521
|
|
|
504
|
-
That progression is intentional. Packages keep their operational runtimes internally, but the app-facing shared state they surface to Vue code is
|
|
522
|
+
That progression is intentional. Packages keep their operational runtimes internally, but the app-facing shared state they surface to Vue code is store-based.
|
|
505
523
|
|
|
506
524
|
## What `auth-base` adds to the app
|
|
507
525
|
|
|
@@ -528,7 +546,7 @@ Three things are worth noticing immediately.
|
|
|
528
546
|
|
|
529
547
|
- `auth-provider-supabase-core` is the provider-specific runtime.
|
|
530
548
|
- `auth-web` is the part that adds the web routes and the default auth UI.
|
|
531
|
-
- there is
|
|
549
|
+
- there is an `auth` surface-specific dev/build script family, just as `home` already had.
|
|
532
550
|
|
|
533
551
|
The provider command also writes a new `.env` file:
|
|
534
552
|
|
|
@@ -541,7 +559,7 @@ APP_PUBLIC_URL=http://localhost:5173
|
|
|
541
559
|
|
|
542
560
|
This is the bridge between the scaffold and your real Supabase project. `APP_PUBLIC_URL` matters because auth emails and callback flows need to know which browser URL they should return to.
|
|
543
561
|
|
|
544
|
-
Public routing config changes too. `config/public.js`
|
|
562
|
+
Public routing config changes too. `config/public.js` has a second surface:
|
|
545
563
|
|
|
546
564
|
```js
|
|
547
565
|
config.surfaceDefinitions.auth = {
|
|
@@ -568,7 +586,7 @@ config.auth = {
|
|
|
568
586
|
};
|
|
569
587
|
```
|
|
570
588
|
|
|
571
|
-
That small block explains a lot of the default login screen. The stock UI is ready for OAuth providers such as Google, but
|
|
589
|
+
That small block explains a lot of the default login screen. The stock UI is ready for OAuth providers such as Google, but this chapter keeps the provider list empty, so the page only shows the email/password and one-time-code flows. Later, if you enable a provider in Supabase and list it here, the same login screen can expose that button too.
|
|
572
590
|
|
|
573
591
|
The auth routes themselves are app-owned wrappers around the module-supplied default views. `src/pages/auth/login.vue` looks like this:
|
|
574
592
|
|
|
@@ -606,7 +624,8 @@ Authentication also becomes visible in the shell through `src/placement.js`:
|
|
|
606
624
|
```js
|
|
607
625
|
addPlacement({
|
|
608
626
|
id: "auth.profile.widget",
|
|
609
|
-
target: "shell
|
|
627
|
+
target: "shell.status",
|
|
628
|
+
kind: "component",
|
|
610
629
|
surfaces: ["*"],
|
|
611
630
|
order: 1000,
|
|
612
631
|
componentToken: "auth.web.profile.widget"
|
|
@@ -614,10 +633,10 @@ addPlacement({
|
|
|
614
633
|
|
|
615
634
|
addPlacement({
|
|
616
635
|
id: "auth.profile.menu.sign-in",
|
|
617
|
-
target: "auth
|
|
636
|
+
target: "auth.profile-menu",
|
|
637
|
+
kind: "link",
|
|
618
638
|
surfaces: ["*"],
|
|
619
639
|
order: 200,
|
|
620
|
-
componentToken: "auth.web.profile.menu.link-item",
|
|
621
640
|
props: {
|
|
622
641
|
label: "Sign in",
|
|
623
642
|
to: "/auth/login"
|
|
@@ -627,10 +646,10 @@ addPlacement({
|
|
|
627
646
|
|
|
628
647
|
addPlacement({
|
|
629
648
|
id: "auth.profile.menu.sign-out",
|
|
630
|
-
target: "auth
|
|
649
|
+
target: "auth.profile-menu",
|
|
650
|
+
kind: "link",
|
|
631
651
|
surfaces: ["*"],
|
|
632
652
|
order: 1000,
|
|
633
|
-
componentToken: "auth.web.profile.menu.link-item",
|
|
634
653
|
props: {
|
|
635
654
|
label: "Sign out",
|
|
636
655
|
to: "/auth/signout"
|
|
@@ -639,7 +658,7 @@ addPlacement({
|
|
|
639
658
|
});
|
|
640
659
|
```
|
|
641
660
|
|
|
642
|
-
This is the shell placement system from the previous chapter doing real work again. `auth-web` does not hard-code a permanent
|
|
661
|
+
This is the shell placement system from the previous chapter doing real work again. `auth-web` does not hard-code a permanent auth button into `ShellLayout.vue`. Instead, it contributes the profile widget into the semantic `shell.status` placement and contributes sign-in/sign-out links into the semantic `auth.profile-menu` placement. Topology maps those semantic targets to concrete outlets and renderers.
|
|
643
662
|
|
|
644
663
|
So the auth story in this chapter is spread across clear responsibilities:
|
|
645
664
|
|
|
@@ -701,7 +720,7 @@ onBeforeUnmount(() => {
|
|
|
701
720
|
</v-alert>
|
|
702
721
|
|
|
703
722
|
<h1 class="text-h5 mb-2">Home</h1>
|
|
704
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
723
|
+
<p class="text-body-2 text-medium-emphasis">Your account session is active on this device.</p>
|
|
705
724
|
</section>
|
|
706
725
|
</template>
|
|
707
726
|
```
|
|
@@ -916,7 +935,7 @@ It is also why the shell widget can react cleanly to auth state without storing
|
|
|
916
935
|
|
|
917
936
|
### Authenticated Playwright testing with the dev auth bypass
|
|
918
937
|
|
|
919
|
-
JSKIT
|
|
938
|
+
JSKIT ships a development-only auth bootstrap path specifically so authenticated UI can be verified in Playwright without depending on a real live login flow through Supabase.
|
|
920
939
|
|
|
921
940
|
This is the standard path the agent should use for authenticated browser tests:
|
|
922
941
|
|
|
@@ -1082,6 +1101,6 @@ That is why the confirmation screen in the app should be understood as a **sessi
|
|
|
1082
1101
|
|
|
1083
1102
|
## Summary
|
|
1084
1103
|
|
|
1085
|
-
After this chapter, the app can really authenticate against Supabase. It has a public `auth` surface, a stock login page, a sign-out route, and a shell widget that reflects auth state. The provider-specific values
|
|
1104
|
+
After this chapter, the app can really authenticate against Supabase. It has a public `auth` surface, a stock login page, a sign-out route, and a shell widget that reflects auth state. The provider-specific values live in `.env`, and the web auth layer is wired into the same placement and surface system introduced earlier in the guide.
|
|
1086
1105
|
|
|
1087
1106
|
Just as importantly, the app is still deliberately incomplete. Authentication exists, but the database-backed user model does not. That separation is useful, because the next layer of the guide can explain users and persistence without having to also explain the first auth setup at the same time.
|
|
@@ -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
|
|
@@ -179,7 +179,7 @@ This follows the same route-owner pattern the guide has already shown on `home`
|
|
|
179
179
|
- `src/pages/console/settings.vue` is the nested settings shell
|
|
180
180
|
- `src/pages/console/settings/index.vue` is the initial developer-owned stub
|
|
181
181
|
|
|
182
|
-
That last file is intentionally empty, because later modules are expected to add real console settings sections
|
|
182
|
+
That last file is intentionally empty, because later modules are expected to add real console settings sections through `page.section-nav` with owner `console-settings`.
|
|
183
183
|
|
|
184
184
|
### `src/placement.js` wires the first console menu entry
|
|
185
185
|
|
|
@@ -188,10 +188,10 @@ The starter placement block is:
|
|
|
188
188
|
```js
|
|
189
189
|
addPlacement({
|
|
190
190
|
id: "console.web.menu.settings",
|
|
191
|
-
target: "shell
|
|
191
|
+
target: "shell.primary-nav",
|
|
192
|
+
kind: "link",
|
|
192
193
|
surfaces: ["console"],
|
|
193
194
|
order: 100,
|
|
194
|
-
componentToken: "local.main.ui.menu-link-item",
|
|
195
195
|
props: {
|
|
196
196
|
label: "Settings",
|
|
197
197
|
to: "/console/settings",
|
|
@@ -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
|
|