@jskit-ai/agent-docs 0.1.292 → 0.1.293

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/guide/agent/app-setup/authentication.md +19 -8
  2. package/guide/agent/app-setup/multi-homing.md +19 -0
  3. package/guide/agent/app-setup/upgrading-jskit.md +4 -0
  4. package/guide/agent/framework/client-features.md +26 -1
  5. package/guide/agent/framework/typed-features.md +33 -2
  6. package/package.json +1 -1
  7. package/patterns/feature-package/example/booking-engine/package.json +2 -2
  8. package/patterns/minimal-foundation/example/AGENTS.md +5 -1
  9. package/patterns/minimal-foundation/example/package.json +5 -5
  10. package/patterns/shell-foundation/example/AGENTS.md +5 -1
  11. package/patterns/shell-foundation/example/package.json +6 -6
  12. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  13. package/reference/autogen/PATTERN_INDEX.md +38 -38
  14. package/reference/autogen/packages/kernel.md +7 -1
  15. package/reference/autogen/packages/workspaces-core.md +8 -2
  16. package/reference/autogen/packages/workspaces-web.md +1 -0
  17. package/reference/autogen/tooling/config-eslint.md +18 -1
  18. package/reference/autogen/tooling/jskit-catalog.md +8 -0
  19. package/skills/jskit/references/client-features.md +26 -1
  20. package/skills/jskit/references/multi-homing.md +19 -0
  21. package/skills/jskit/references/pattern-index.md +38 -38
  22. package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +5 -1
  23. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +5 -5
  24. package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +5 -1
  25. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +6 -6
  26. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  27. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +3 -3
  28. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  29. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  30. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  31. package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/package.json +3 -3
  32. package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +3 -1
  33. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +2 -2
  34. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +2 -2
  35. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  36. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +2 -2
  37. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +7 -0
  38. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +3 -3
  39. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +6 -6
  40. package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +9 -0
  41. package/skills/jskit/references/typed-features.md +33 -2
@@ -161,8 +161,15 @@ The login page shows either refusal, translated, with the wait.
161
161
 
162
162
  Local auth confirms the email address of each account it registers by
163
163
  password, in every environment, unless `AUTH_LOCAL_EMAIL_CONFIRMATION=false`.
164
- Registering keeps the registration waiting, with no account, profile or
165
- session, and answers `requiresEmailConfirmation: true`; the sign-in page then
164
+ Two registrations whose email is already proven sign in at once instead: one
165
+ through an invitation that a feature's invitation context resolver (such as
166
+ workspace invites) resolves, still pending, for the registering email (letter
167
+ case aside) and before its `expiresAt`, since its link reached that address;
168
+ and the first account a production server's setup code admits (see below). An
169
+ invitation passed through as given, with no resolver installed, or whose
170
+ context names no email, proves nothing. Any other registration keeps waiting,
171
+ with no account, profile or session, and answers
172
+ `requiresEmailConfirmation: true`; the sign-in page then
166
173
  shows "Confirm your email", with a button to send a new link and a field for
167
174
  the link or its code. Confirming takes the link's code and the password the
168
175
  registration was made with (`POST /api/register/confirm` with
@@ -174,7 +181,9 @@ wrong password creates nothing and keeps the registration and its link, with
174
181
  status 400 and `auth.local.register.confirmationPasswordInvalid`. Signing in
175
182
  with the right password while the registration waits is refused with status
176
183
  403 and `auth.login.emailUnconfirmed`, and the sign-in page shows how to
177
- confirm; a wrong password is an ordinary failed sign-in.
184
+ confirm; a wrong password is an ordinary failed sign-in. The refusal holds
185
+ even after `AUTH_LOCAL_EMAIL_CONFIRMATION=false`, which offers no
186
+ confirming: the person registers again.
178
187
 
179
188
  A link's code is random, kept only as a hash, works once and expires after a
180
189
  day. Registering the same email again replaces the waiting registration whole
@@ -210,9 +219,10 @@ Confirmation guards self-registration. An app whose accounts are only invited,
210
219
  or created through the first account, sets `AUTH_LOCAL_SELF_REGISTRATION=false`
211
220
  and needs no confirmation sender: an invitation counts when a feature's
212
221
  invitation context resolver (such as workspace invites) resolves it for that
213
- email, and the first account is admitted by its setup code. In production
214
- without a sender, those registrations sign in at once; with one, they confirm
215
- as usual. An app that refused the register routes itself, with a 404 hook,
222
+ email, and the first account is admitted by its setup code. Those
223
+ registrations sign in at once, with or without a sender, except an invitation
224
+ whose resolver names no email, which confirms when a sender is installed. An
225
+ app that refused the register routes itself, with a 404 hook,
216
226
  can drop the hook for this setting. The sign-in page then shows its Register
217
227
  tab only to an invited person (an `invitationToken` in its address) and, while
218
228
  the app has no account, to the first one: local auth reports
@@ -289,8 +299,9 @@ field (capability `features.firstAccountSetupCode`), and the register body
289
299
  takes `setupCode`; letter case, spaces and dashes do not matter. A missing code
290
300
  is refused with 403 `auth.local.setup.codeRequired`, a wrong one with 403
291
301
  `auth.local.setup.codeInvalid`. An OAuth sign-in cannot create that first
292
- account. The registration then follows the ordinary rules, email confirmation
293
- included, and confirming it signs the owner in. The code admits registrations
302
+ account. The registration then follows the ordinary rules, except that it
303
+ needs no email confirmation: only the operator can read the code, so it signs
304
+ the owner in at once. The code admits registrations
294
305
  until an account exists; then it stops working and its file is deleted, and
295
306
  registration needs no code again. Restarting before then makes a new code and
296
307
  the old one stops working: the latest log line and the file always agree, and
@@ -336,6 +336,7 @@ authorization and role checks. Do not pass arbitrary HTTP input as options.
336
336
  | `workspaces.services.members.sendInvite(workspace, actor, prepared, options)` | Refuses to run inside a transaction: it delivers after the preparation's transaction commits. Reloads the committed pending invitation and workspace display data, validates expiry and token correspondence, and sends that invitation without replacement. |
337
337
  | `workspaces.services.members.createInvite(workspace, actor, payload, options)` | Owns preparation and commit, then calls `sendInvite`, and answers with `sendInvite`'s result as JSON:API data (`returnJsonApiData`, read it from `.value`), as the invite action sends it. Refuses to run inside a transaction. |
338
338
  | `workspaces.invite-acceptance-participant` (optional capability) | One application function receiving `({ invite, user }, options)` inside the acceptance transaction. Without it, acceptance activates the membership only. |
339
+ | `workspaces.workspace-created-participant` (optional capability) | One application function receiving `({ workspace, owner }, options)` inside a new workspace's creation transaction. Without it, creation writes the workspace, its owner's membership and its settings only. |
339
340
 
340
341
  `sendInvite` accepts `{ createdInviteId, inviteTokenPreview }` from preparation or
341
342
  only `{ createdInviteId }` for a later attempt. The latter uses JSKIT's existing
@@ -471,6 +472,24 @@ Replacement can create a new pending invitation after acceptance while retaining
471
472
  the accepted invitation. Keep callbacks short: invitation changes in one workspace
472
473
  serialize. Deadlocks/serialization failures propagate without replaying callbacks.
473
474
 
475
+ ### Workspace creation participation
476
+
477
+ Provide the optional `workspaces.workspace-created-participant` capability the
478
+ same way, from a small application feature, to write application records for
479
+ every new workspace in the same transaction. Both creation paths call
480
+ `fn({ workspace, owner }, txOptions)`: the `workspace.workspaces.create` action
481
+ (`createWorkspaceForAuthenticatedUser`) and a personal workspace's provisioning
482
+ (`ensurePersonalWorkspaceForUser`, which joins its caller's transaction, such as
483
+ a first sign-in's profile sync). It runs after the workspace, its owner's
484
+ membership and its settings are written, so it can read them with `txOptions`.
485
+ `workspace` and `owner` are shallow frozen. Pass `txOptions` to every write and
486
+ let failures propagate: a failure rolls back the whole creation, workspace,
487
+ membership, settings and the participant's own writes, and the caller receives
488
+ the failure itself (an `AppError` answers as that refusal). Keep external effects
489
+ out of it. An existing personal workspace is not created again and does not call
490
+ it, and a create that loses its slug to the same owner's identical create answers
491
+ that workspace without calling it a second time.
492
+
474
493
  ### Compatibility and verification
475
494
 
476
495
  Since `@jskit-ai/workspaces-core@0.1.198`, transactional `createInvite` callers
@@ -178,6 +178,10 @@ The check fails when:
178
178
  - installed package versions or root/workspace declarations disagree with the lockfile.
179
179
  - a package script runs tests outside the machine's test budget (`test/*`, see
180
180
  [Test files run in parallel](/guide/framework/typed-features#test-files-run-in-parallel)).
181
+ - an application package's manifest (`jskit.capabilities`) misses a capability
182
+ its features provide or require, or provides one none of them does
183
+ (`manifest/capabilities`, see
184
+ [Package manifest](/guide/framework/typed-features#package-manifest)).
181
185
 
182
186
  Shared client owners such as the kernel, HTTP runtime, shell, auth web runtime,
183
187
  realtime runtime, and assistant runtime use exact peer dependencies where they
@@ -389,6 +389,29 @@ const emit = defineEmits(["close"]);
389
389
  emit("closed");
390
390
  ```
391
391
 
392
+ ### Selection fields
393
+
394
+ **A select, autocomplete or combobox whose options load from the server, or
395
+ whose value is a record id, is `SelectField` from
396
+ `@jskit-ai/http-web/client/components/SelectField`, with `loading` bound while
397
+ the options load.** A raw `v-select`, `v-autocomplete` or `v-combobox` takes
398
+ fixed options only: a list written out in the template, a top-level `const`
399
+ holding one (as written, frozen, or `computed()` over one written out), or an
400
+ import from a `shared` module. The model's name decides nothing.
401
+
402
+ Why: Vuetify titles a value missing from `items` with the value itself, so a
403
+ stored id such as `53` shows until the options arrive; `SelectField` hides it
404
+ while `loading` is true.
405
+
406
+ ```vue
407
+ <!-- good -->
408
+ <SelectField v-model="form.serviceId" :items="services.items" :loading="services.loading" />
409
+ <v-select v-model="form.size" :items="SIZE_OPTIONS" />
410
+
411
+ <!-- bad: a stored id shows while the services load -->
412
+ <v-select v-model="form.serviceId" :items="services.items" />
413
+ ```
414
+
392
415
  ### Presenting scripts
393
416
 
394
417
  **A component's `<script>` presents: props, emits, element refs, display
@@ -563,6 +586,7 @@ client-only rules below. The server's action and error rules do not apply.
563
586
  | `jskit/no-frozen-vue-state` | `Object.freeze` over refs, computeds or reactive state | [Fixed result shape](#fixed-result-shape) |
564
587
  | `jskit/released-listeners` | a listener, interval, observer or subscription the file starts and never releases | [Released listeners](#released-listeners) |
565
588
  | `jskit/no-module-state` | a module-level `let`, `ref()` or `reactive()` | [Module state](#module-state) |
589
+ | `jskit/selection-field` | a `v-select`, `v-autocomplete` or `v-combobox` whose items are not fixed, and a `SelectField` whose items are not fixed that binds no `loading` | [Selection fields](#selection-fields) |
566
590
  | `vue/require-prop-types`, `vue/require-default-prop`, `vue/require-valid-default-prop`, `vue/no-required-prop-with-default` | a prop without a type, optional without a default, or both | [Collaborator props](#collaborator-props) |
567
591
  | `vue/require-explicit-emits` | an emitted event the component does not declare | [Declared emits](#declared-emits) |
568
592
 
@@ -583,10 +607,11 @@ Simplicity, effect order and presenting scripts are checked by reading.
583
607
  Client exceptions use the
584
608
  [typed features exceptions](/guide/framework/typed-features#exceptions): a
585
609
  comment on the line before the finding, naming the rule or the finding, with a
586
- reason prefix. Client code adds three placements:
610
+ reason prefix. Client code adds four placements:
587
611
 
588
612
  | Placement | The exception |
589
613
  | --- | --- |
590
614
  | `// readable-check: allow module-state - shared resource: <what the page shares>` | module state every instance on the page shares on purpose |
591
615
  | `<!-- readable-check: allow optional-call - optional operation: <what lacks it> -->` above an element | that element's bindings call an optional operation |
616
+ | `<!-- readable-check: allow selection-field - data: <where the options come from> -->` above an element | a selection whose options are in memory and whose value is no record id |
592
617
  | `// readable-check: allow method-probe - input validation: <the shapes>` above a function | the probes of a function whose one job is normalizing an external value of several shapes |
@@ -81,6 +81,31 @@ setup: ({ resources }) => ({ invoices: { list: (options) => resources.invoices.q
81
81
  actions: ({ invoices }) => [{ id: "invoices.list", execute: () => invoices.list() }]
82
82
  ```
83
83
 
84
+ ### Package manifest
85
+
86
+ **A package's manifest, `jskit.capabilities` in its `package.json`, lists
87
+ exactly what its features provide, and every capability they require beyond
88
+ the kernel's own (`runtime.actions`, `runtime.events`, `runtime.http`,
89
+ `runtime.bootstrap`) and those the package provides itself.** Optional
90
+ capabilities are not required. A requirement no feature has stays only when
91
+ the package needs it at install time, such as the database its migrations
92
+ need.
93
+
94
+ Why: start-up resolves every package's requirements from the manifests, and
95
+ refuses a manifest that disagrees with the package's features. Plain
96
+ `jskit check` loads each application package's features (the server providers
97
+ `jskit.runtime.server.providers` declares) and reports the drift before that
98
+ (`manifest/capabilities`), each capability to add or remove; it lists the
99
+ requirements no feature has for review.
100
+
101
+ ```jsonc
102
+ // good: BooksFeature provides app.books and requires crud.contracts and runtime.actions
103
+ "capabilities": { "provides": ["app.books"], "requires": ["crud.contracts"] }
104
+
105
+ // bad: app.books missing from provides, crud.contracts from requires
106
+ "capabilities": { "provides": ["app.book"], "requires": [] }
107
+ ```
108
+
84
109
  ### Capability types
85
110
 
86
111
  **Each provided token has one `@typedef` in its package's `src/server/types.js`,
@@ -696,6 +721,11 @@ way (is this user an operator) is a member of its owner's capability.**
696
721
  meaning of 0 and false before replacing a copy.
697
722
  - A helper two packages of an application use lives in its shared package,
698
723
  never inside one of them.
724
+ - A component's closure over its own state is not a helper: a function of at
725
+ most two statements in `<script setup>` whose every outside name is a
726
+ binding `<script setup>` declares (not an import or a global). Two pages
727
+ that reload their own resources the same way keep their own `reload()`;
728
+ sharing it would take a module that receives each page's state.
699
729
 
700
730
  Why: the kernel's helpers fix one meaning of empty, and a copy brings its own.
701
731
 
@@ -1175,8 +1205,9 @@ Why: a comment that narrates history is wrong the day after it is written.
1175
1205
 
1176
1206
  `npm run lint` runs ESLint with `@jskit-ai/config-eslint`, and `npm run
1177
1207
  verify` runs `jskit check` (the package graph, navigation, [data
1178
- access](/guide/framework/data-access#exceptions) and [test
1179
- runs](#test-run-rules)), lint, the tests and the build. The foundations'
1208
+ access](/guide/framework/data-access#exceptions), [test
1209
+ runs](#test-run-rules) and [package manifests](#package-manifest)), lint, the
1210
+ tests and the build. The foundations'
1180
1211
  `eslint.config.mjs` already holds a new
1181
1212
  application's code to every check:
1182
1213
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.292",
3
+ "version": "0.1.293",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -5,8 +5,8 @@
5
5
  "private": true,
6
6
  "type": "module",
7
7
  "dependencies": {
8
- "@jskit-ai/kernel": "0.2.17",
9
- "json-rest-schema": "^1.0.17"
8
+ "@jskit-ai/kernel": "0.2.18",
9
+ "json-rest-schema": "^1.0.20"
10
10
  },
11
11
  "exports": {
12
12
  "./server/actions": "./src/server/bookingEngineActions.js",
@@ -74,7 +74,11 @@ Selection fields never show a stored id as text. A select, autocomplete or
74
74
  combobox whose options load from the server, or whose value is a record id,
75
75
  uses `SelectField` from `@jskit-ai/http-web/client/components/SelectField` with
76
76
  `loading` while the options load, not a raw `v-select`, `v-autocomplete` or
77
- `v-combobox`.
77
+ `v-combobox`. The lint rule `jskit/selection-field` reports a raw one whose
78
+ items are not fixed (a list written out, a top-level `const` holding one, or an
79
+ import from a `shared` module) and a `SelectField` over loaded items without
80
+ `loading`; a justified case says why in
81
+ `<!-- readable-check: allow selection-field - data: <reason> -->` above it.
78
82
 
79
83
  Copied pattern source is ordinary application source. Do not add generator
80
84
  provenance, receipts, completion ledgers, or tooling-operation history. Keep
@@ -34,23 +34,23 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.17",
37
+ "@jskit-ai/kernel": "0.2.18",
38
38
  "@jskit-ai/material-web": "0.1.5",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
42
- "json-rest-schema": "^1.0.17",
42
+ "json-rest-schema": "^1.0.20",
43
43
  "pinia": "^4.0.3",
44
44
  "vue": "^3.5.44",
45
45
  "vue-router": "^5.3.1",
46
46
  "vuetify": "^4.3.0",
47
- "@jskit-ai/http-runtime": "0.2.17",
47
+ "@jskit-ai/http-runtime": "0.2.18",
48
48
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
49
49
  "@vue/devtools-api": "^8.2.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@jskit-ai/config-eslint": "0.1.248",
53
- "@jskit-ai/jskit-catalog": "0.2.44",
52
+ "@jskit-ai/config-eslint": "0.1.249",
53
+ "@jskit-ai/jskit-catalog": "0.2.45",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -74,7 +74,11 @@ Selection fields never show a stored id as text. A select, autocomplete or
74
74
  combobox whose options load from the server, or whose value is a record id,
75
75
  uses `SelectField` from `@jskit-ai/http-web/client/components/SelectField` with
76
76
  `loading` while the options load, not a raw `v-select`, `v-autocomplete` or
77
- `v-combobox`.
77
+ `v-combobox`. The lint rule `jskit/selection-field` reports a raw one whose
78
+ items are not fixed (a list written out, a top-level `const` holding one, or an
79
+ import from a `shared` module) and a `SelectField` over loaded items without
80
+ `loading`; a justified case says why in
81
+ `<!-- readable-check: allow selection-field - data: <reason> -->` above it.
78
82
 
79
83
  Copied pattern source is ordinary application source. Do not add generator
80
84
  provenance, receipts, completion ledgers, or tooling-operation history. Keep
@@ -34,24 +34,24 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.17",
37
+ "@jskit-ai/kernel": "0.2.18",
38
38
  "@jskit-ai/material-web": "0.1.5",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
42
- "json-rest-schema": "^1.0.17",
42
+ "json-rest-schema": "^1.0.20",
43
43
  "pinia": "^4.0.3",
44
44
  "vue": "^3.5.44",
45
45
  "vue-router": "^5.3.1",
46
46
  "vuetify": "^4.3.0",
47
- "@jskit-ai/http-runtime": "0.2.17",
48
- "@jskit-ai/shell-web": "0.2.21",
47
+ "@jskit-ai/http-runtime": "0.2.18",
48
+ "@jskit-ai/shell-web": "0.2.22",
49
49
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
50
50
  "@vue/devtools-api": "^8.2.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@jskit-ai/config-eslint": "0.1.248",
54
- "@jskit-ai/jskit-catalog": "0.2.44",
53
+ "@jskit-ai/config-eslint": "0.1.249",
54
+ "@jskit-ai/jskit-catalog": "0.2.45",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "eslint": "^10.8.0",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "description": "App-local runtime composition and lightweight glue.",
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.2.17"
12
+ "@jskit-ai/kernel": "0.2.18"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",